]> git.sur5r.net Git - u-boot/blob - tools/binman/ftest.py
Merge git://git.denx.de/u-boot-dm
[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):
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         """
155         args = ['-p', '-I', self._indir, '-d', self.TestFile(fname)]
156         if debug:
157             args.append('-D')
158         return self._DoBinman(*args)
159
160     def _SetupDtb(self, fname, outfile='u-boot.dtb'):
161         """Set up a new test device-tree file
162
163         The given file is compiled and set up as the device tree to be used
164         for ths test.
165
166         Args:
167             fname: Filename of .dts file to read
168             outfile: Output filename for compiled device tree binary
169
170         Returns:
171             Contents of device tree binary
172         """
173         if not self._output_setup:
174             tools.PrepareOutputDir(self._indir, True)
175             self._output_setup = True
176         dtb = fdt_util.EnsureCompiled(self.TestFile(fname))
177         with open(dtb) as fd:
178             data = fd.read()
179             TestFunctional._MakeInputFile(outfile, data)
180             return data
181
182     def _DoReadFileDtb(self, fname, use_real_dtb=False):
183         """Run binman and return the resulting image
184
185         This runs binman with a given test file and then reads the resulting
186         output file. It is a shortcut function since most tests need to do
187         these steps.
188
189         Raises an assertion failure if binman returns a non-zero exit code.
190
191         Args:
192             fname: Device tree source filename to use (e.g. 05_simple.dts)
193             use_real_dtb: True to use the test file as the contents of
194                 the u-boot-dtb entry. Normally this is not needed and the
195                 test contents (the U_BOOT_DTB_DATA string) can be used.
196                 But in some test we need the real contents.
197
198         Returns:
199             Tuple:
200                 Resulting image contents
201                 Device tree contents
202         """
203         dtb_data = None
204         # Use the compiled test file as the u-boot-dtb input
205         if use_real_dtb:
206             dtb_data = self._SetupDtb(fname)
207
208         try:
209             retcode = self._DoTestFile(fname)
210             self.assertEqual(0, retcode)
211
212             # Find the (only) image, read it and return its contents
213             image = control.images['image']
214             fname = tools.GetOutputFilename('image.bin')
215             self.assertTrue(os.path.exists(fname))
216             with open(fname) as fd:
217                 return fd.read(), dtb_data
218         finally:
219             # Put the test file back
220             if use_real_dtb:
221                 TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA)
222
223     def _DoReadFile(self, fname, use_real_dtb=False):
224         """Helper function which discards the device-tree binary"""
225         return self._DoReadFileDtb(fname, use_real_dtb)[0]
226
227     @classmethod
228     def _MakeInputFile(self, fname, contents):
229         """Create a new test input file, creating directories as needed
230
231         Args:
232             fname: Filenaem to create
233             contents: File contents to write in to the file
234         Returns:
235             Full pathname of file created
236         """
237         pathname = os.path.join(self._indir, fname)
238         dirname = os.path.dirname(pathname)
239         if dirname and not os.path.exists(dirname):
240             os.makedirs(dirname)
241         with open(pathname, 'wb') as fd:
242             fd.write(contents)
243         return pathname
244
245     @classmethod
246     def TestFile(self, fname):
247         return os.path.join(self._binman_dir, 'test', fname)
248
249     def AssertInList(self, grep_list, target):
250         """Assert that at least one of a list of things is in a target
251
252         Args:
253             grep_list: List of strings to check
254             target: Target string
255         """
256         for grep in grep_list:
257             if grep in target:
258                 return
259         self.fail("Error: '%' not found in '%s'" % (grep_list, target))
260
261     def CheckNoGaps(self, entries):
262         """Check that all entries fit together without gaps
263
264         Args:
265             entries: List of entries to check
266         """
267         pos = 0
268         for entry in entries.values():
269             self.assertEqual(pos, entry.pos)
270             pos += entry.size
271
272     def GetFdtLen(self, dtb):
273         """Get the totalsize field from a device tree binary
274
275         Args:
276             dtb: Device tree binary contents
277
278         Returns:
279             Total size of device tree binary, from the header
280         """
281         return struct.unpack('>L', dtb[4:8])[0]
282
283     def testRun(self):
284         """Test a basic run with valid args"""
285         result = self._RunBinman('-h')
286
287     def testFullHelp(self):
288         """Test that the full help is displayed with -H"""
289         result = self._RunBinman('-H')
290         help_file = os.path.join(self._binman_dir, 'README')
291         # Remove possible extraneous strings
292         extra = '::::::::::::::\n' + help_file + '\n::::::::::::::\n'
293         gothelp = result.stdout.replace(extra, '')
294         self.assertEqual(len(gothelp), os.path.getsize(help_file))
295         self.assertEqual(0, len(result.stderr))
296         self.assertEqual(0, result.return_code)
297
298     def testFullHelpInternal(self):
299         """Test that the full help is displayed with -H"""
300         try:
301             command.test_result = command.CommandResult()
302             result = self._DoBinman('-H')
303             help_file = os.path.join(self._binman_dir, 'README')
304         finally:
305             command.test_result = None
306
307     def testHelp(self):
308         """Test that the basic help is displayed with -h"""
309         result = self._RunBinman('-h')
310         self.assertTrue(len(result.stdout) > 200)
311         self.assertEqual(0, len(result.stderr))
312         self.assertEqual(0, result.return_code)
313
314     def testBoard(self):
315         """Test that we can run it with a specific board"""
316         self._SetupDtb('05_simple.dts', 'sandbox/u-boot.dtb')
317         TestFunctional._MakeInputFile('sandbox/u-boot.bin', U_BOOT_DATA)
318         result = self._DoBinman('-b', 'sandbox')
319         self.assertEqual(0, result)
320
321     def testNeedBoard(self):
322         """Test that we get an error when no board ius supplied"""
323         with self.assertRaises(ValueError) as e:
324             result = self._DoBinman()
325         self.assertIn("Must provide a board to process (use -b <board>)",
326                 str(e.exception))
327
328     def testMissingDt(self):
329         """Test that an invalid device tree file generates an error"""
330         with self.assertRaises(Exception) as e:
331             self._RunBinman('-d', 'missing_file')
332         # We get one error from libfdt, and a different one from fdtget.
333         self.AssertInList(["Couldn't open blob from 'missing_file'",
334                            'No such file or directory'], str(e.exception))
335
336     def testBrokenDt(self):
337         """Test that an invalid device tree source file generates an error
338
339         Since this is a source file it should be compiled and the error
340         will come from the device-tree compiler (dtc).
341         """
342         with self.assertRaises(Exception) as e:
343             self._RunBinman('-d', self.TestFile('01_invalid.dts'))
344         self.assertIn("FATAL ERROR: Unable to parse input tree",
345                 str(e.exception))
346
347     def testMissingNode(self):
348         """Test that a device tree without a 'binman' node generates an error"""
349         with self.assertRaises(Exception) as e:
350             self._DoBinman('-d', self.TestFile('02_missing_node.dts'))
351         self.assertIn("does not have a 'binman' node", str(e.exception))
352
353     def testEmpty(self):
354         """Test that an empty binman node works OK (i.e. does nothing)"""
355         result = self._RunBinman('-d', self.TestFile('03_empty.dts'))
356         self.assertEqual(0, len(result.stderr))
357         self.assertEqual(0, result.return_code)
358
359     def testInvalidEntry(self):
360         """Test that an invalid entry is flagged"""
361         with self.assertRaises(Exception) as e:
362             result = self._RunBinman('-d',
363                                      self.TestFile('04_invalid_entry.dts'))
364         #print e.exception
365         self.assertIn("Unknown entry type 'not-a-valid-type' in node "
366                 "'/binman/not-a-valid-type'", str(e.exception))
367
368     def testSimple(self):
369         """Test a simple binman with a single file"""
370         data = self._DoReadFile('05_simple.dts')
371         self.assertEqual(U_BOOT_DATA, data)
372
373     def testSimpleDebug(self):
374         """Test a simple binman run with debugging enabled"""
375         data = self._DoTestFile('05_simple.dts', debug=True)
376
377     def testDual(self):
378         """Test that we can handle creating two images
379
380         This also tests image padding.
381         """
382         retcode = self._DoTestFile('06_dual_image.dts')
383         self.assertEqual(0, retcode)
384
385         image = control.images['image1']
386         self.assertEqual(len(U_BOOT_DATA), image._size)
387         fname = tools.GetOutputFilename('image1.bin')
388         self.assertTrue(os.path.exists(fname))
389         with open(fname) as fd:
390             data = fd.read()
391             self.assertEqual(U_BOOT_DATA, data)
392
393         image = control.images['image2']
394         self.assertEqual(3 + len(U_BOOT_DATA) + 5, image._size)
395         fname = tools.GetOutputFilename('image2.bin')
396         self.assertTrue(os.path.exists(fname))
397         with open(fname) as fd:
398             data = fd.read()
399             self.assertEqual(U_BOOT_DATA, data[3:7])
400             self.assertEqual(chr(0) * 3, data[:3])
401             self.assertEqual(chr(0) * 5, data[7:])
402
403     def testBadAlign(self):
404         """Test that an invalid alignment value is detected"""
405         with self.assertRaises(ValueError) as e:
406             self._DoTestFile('07_bad_align.dts')
407         self.assertIn("Node '/binman/u-boot': Alignment 23 must be a power "
408                       "of two", str(e.exception))
409
410     def testPackSimple(self):
411         """Test that packing works as expected"""
412         retcode = self._DoTestFile('08_pack.dts')
413         self.assertEqual(0, retcode)
414         self.assertIn('image', control.images)
415         image = control.images['image']
416         entries = image._entries
417         self.assertEqual(5, len(entries))
418
419         # First u-boot
420         self.assertIn('u-boot', entries)
421         entry = entries['u-boot']
422         self.assertEqual(0, entry.pos)
423         self.assertEqual(len(U_BOOT_DATA), entry.size)
424
425         # Second u-boot, aligned to 16-byte boundary
426         self.assertIn('u-boot-align', entries)
427         entry = entries['u-boot-align']
428         self.assertEqual(16, entry.pos)
429         self.assertEqual(len(U_BOOT_DATA), entry.size)
430
431         # Third u-boot, size 23 bytes
432         self.assertIn('u-boot-size', entries)
433         entry = entries['u-boot-size']
434         self.assertEqual(20, entry.pos)
435         self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
436         self.assertEqual(23, entry.size)
437
438         # Fourth u-boot, placed immediate after the above
439         self.assertIn('u-boot-next', entries)
440         entry = entries['u-boot-next']
441         self.assertEqual(43, entry.pos)
442         self.assertEqual(len(U_BOOT_DATA), entry.size)
443
444         # Fifth u-boot, placed at a fixed position
445         self.assertIn('u-boot-fixed', entries)
446         entry = entries['u-boot-fixed']
447         self.assertEqual(61, entry.pos)
448         self.assertEqual(len(U_BOOT_DATA), entry.size)
449
450         self.assertEqual(65, image._size)
451
452     def testPackExtra(self):
453         """Test that extra packing feature works as expected"""
454         retcode = self._DoTestFile('09_pack_extra.dts')
455
456         self.assertEqual(0, retcode)
457         self.assertIn('image', control.images)
458         image = control.images['image']
459         entries = image._entries
460         self.assertEqual(5, len(entries))
461
462         # First u-boot with padding before and after
463         self.assertIn('u-boot', entries)
464         entry = entries['u-boot']
465         self.assertEqual(0, entry.pos)
466         self.assertEqual(3, entry.pad_before)
467         self.assertEqual(3 + 5 + len(U_BOOT_DATA), entry.size)
468
469         # Second u-boot has an aligned size, but it has no effect
470         self.assertIn('u-boot-align-size-nop', entries)
471         entry = entries['u-boot-align-size-nop']
472         self.assertEqual(12, entry.pos)
473         self.assertEqual(4, entry.size)
474
475         # Third u-boot has an aligned size too
476         self.assertIn('u-boot-align-size', entries)
477         entry = entries['u-boot-align-size']
478         self.assertEqual(16, entry.pos)
479         self.assertEqual(32, entry.size)
480
481         # Fourth u-boot has an aligned end
482         self.assertIn('u-boot-align-end', entries)
483         entry = entries['u-boot-align-end']
484         self.assertEqual(48, entry.pos)
485         self.assertEqual(16, entry.size)
486
487         # Fifth u-boot immediately afterwards
488         self.assertIn('u-boot-align-both', entries)
489         entry = entries['u-boot-align-both']
490         self.assertEqual(64, entry.pos)
491         self.assertEqual(64, entry.size)
492
493         self.CheckNoGaps(entries)
494         self.assertEqual(128, image._size)
495
496     def testPackAlignPowerOf2(self):
497         """Test that invalid entry alignment is detected"""
498         with self.assertRaises(ValueError) as e:
499             self._DoTestFile('10_pack_align_power2.dts')
500         self.assertIn("Node '/binman/u-boot': Alignment 5 must be a power "
501                       "of two", str(e.exception))
502
503     def testPackAlignSizePowerOf2(self):
504         """Test that invalid entry size alignment is detected"""
505         with self.assertRaises(ValueError) as e:
506             self._DoTestFile('11_pack_align_size_power2.dts')
507         self.assertIn("Node '/binman/u-boot': Alignment size 55 must be a "
508                       "power of two", str(e.exception))
509
510     def testPackInvalidAlign(self):
511         """Test detection of an position that does not match its alignment"""
512         with self.assertRaises(ValueError) as e:
513             self._DoTestFile('12_pack_inv_align.dts')
514         self.assertIn("Node '/binman/u-boot': Position 0x5 (5) does not match "
515                       "align 0x4 (4)", str(e.exception))
516
517     def testPackInvalidSizeAlign(self):
518         """Test that invalid entry size alignment is detected"""
519         with self.assertRaises(ValueError) as e:
520             self._DoTestFile('13_pack_inv_size_align.dts')
521         self.assertIn("Node '/binman/u-boot': Size 0x5 (5) does not match "
522                       "align-size 0x4 (4)", str(e.exception))
523
524     def testPackOverlap(self):
525         """Test that overlapping regions are detected"""
526         with self.assertRaises(ValueError) as e:
527             self._DoTestFile('14_pack_overlap.dts')
528         self.assertIn("Node '/binman/u-boot-align': Position 0x3 (3) overlaps "
529                       "with previous entry '/binman/u-boot' ending at 0x4 (4)",
530                       str(e.exception))
531
532     def testPackEntryOverflow(self):
533         """Test that entries that overflow their size are detected"""
534         with self.assertRaises(ValueError) as e:
535             self._DoTestFile('15_pack_overflow.dts')
536         self.assertIn("Node '/binman/u-boot': Entry contents size is 0x4 (4) "
537                       "but entry size is 0x3 (3)", str(e.exception))
538
539     def testPackImageOverflow(self):
540         """Test that entries which overflow the image size are detected"""
541         with self.assertRaises(ValueError) as e:
542             self._DoTestFile('16_pack_image_overflow.dts')
543         self.assertIn("Image '/binman': contents size 0x4 (4) exceeds image "
544                       "size 0x3 (3)", str(e.exception))
545
546     def testPackImageSize(self):
547         """Test that the image size can be set"""
548         retcode = self._DoTestFile('17_pack_image_size.dts')
549         self.assertEqual(0, retcode)
550         self.assertIn('image', control.images)
551         image = control.images['image']
552         self.assertEqual(7, image._size)
553
554     def testPackImageSizeAlign(self):
555         """Test that image size alignemnt works as expected"""
556         retcode = self._DoTestFile('18_pack_image_align.dts')
557         self.assertEqual(0, retcode)
558         self.assertIn('image', control.images)
559         image = control.images['image']
560         self.assertEqual(16, image._size)
561
562     def testPackInvalidImageAlign(self):
563         """Test that invalid image alignment is detected"""
564         with self.assertRaises(ValueError) as e:
565             self._DoTestFile('19_pack_inv_image_align.dts')
566         self.assertIn("Image '/binman': Size 0x7 (7) does not match "
567                       "align-size 0x8 (8)", str(e.exception))
568
569     def testPackAlignPowerOf2(self):
570         """Test that invalid image alignment is detected"""
571         with self.assertRaises(ValueError) as e:
572             self._DoTestFile('20_pack_inv_image_align_power2.dts')
573         self.assertIn("Image '/binman': Alignment size 131 must be a power of "
574                       "two", str(e.exception))
575
576     def testImagePadByte(self):
577         """Test that the image pad byte can be specified"""
578         with open(self.TestFile('bss_data')) as fd:
579             TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
580         data = self._DoReadFile('21_image_pad.dts')
581         self.assertEqual(U_BOOT_SPL_DATA + (chr(0xff) * 1) + U_BOOT_DATA, data)
582
583     def testImageName(self):
584         """Test that image files can be named"""
585         retcode = self._DoTestFile('22_image_name.dts')
586         self.assertEqual(0, retcode)
587         image = control.images['image1']
588         fname = tools.GetOutputFilename('test-name')
589         self.assertTrue(os.path.exists(fname))
590
591         image = control.images['image2']
592         fname = tools.GetOutputFilename('test-name.xx')
593         self.assertTrue(os.path.exists(fname))
594
595     def testBlobFilename(self):
596         """Test that generic blobs can be provided by filename"""
597         data = self._DoReadFile('23_blob.dts')
598         self.assertEqual(BLOB_DATA, data)
599
600     def testPackSorted(self):
601         """Test that entries can be sorted"""
602         data = self._DoReadFile('24_sorted.dts')
603         self.assertEqual(chr(0) * 1 + U_BOOT_SPL_DATA + chr(0) * 2 +
604                          U_BOOT_DATA, data)
605
606     def testPackZeroPosition(self):
607         """Test that an entry at position 0 is not given a new position"""
608         with self.assertRaises(ValueError) as e:
609             self._DoTestFile('25_pack_zero_size.dts')
610         self.assertIn("Node '/binman/u-boot-spl': Position 0x0 (0) overlaps "
611                       "with previous entry '/binman/u-boot' ending at 0x4 (4)",
612                       str(e.exception))
613
614     def testPackUbootDtb(self):
615         """Test that a device tree can be added to U-Boot"""
616         data = self._DoReadFile('26_pack_u_boot_dtb.dts')
617         self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA, data)
618
619     def testPackX86RomNoSize(self):
620         """Test that the end-at-4gb property requires a size property"""
621         with self.assertRaises(ValueError) as e:
622             self._DoTestFile('27_pack_4gb_no_size.dts')
623         self.assertIn("Image '/binman': Image size must be provided when "
624                       "using end-at-4gb", str(e.exception))
625
626     def testPackX86RomOutside(self):
627         """Test that the end-at-4gb property checks for position boundaries"""
628         with self.assertRaises(ValueError) as e:
629             self._DoTestFile('28_pack_4gb_outside.dts')
630         self.assertIn("Node '/binman/u-boot': Position 0x0 (0) is outside "
631                       "the image starting at 0xffffffe0 (4294967264)",
632                       str(e.exception))
633
634     def testPackX86Rom(self):
635         """Test that a basic x86 ROM can be created"""
636         data = self._DoReadFile('29_x86-rom.dts')
637         self.assertEqual(U_BOOT_DATA + chr(0) * 7 + U_BOOT_SPL_DATA +
638                          chr(0) * 2, data)
639
640     def testPackX86RomMeNoDesc(self):
641         """Test that an invalid Intel descriptor entry is detected"""
642         TestFunctional._MakeInputFile('descriptor.bin', '')
643         with self.assertRaises(ValueError) as e:
644             self._DoTestFile('31_x86-rom-me.dts')
645         self.assertIn("Node '/binman/intel-descriptor': Cannot find FD "
646                       "signature", str(e.exception))
647
648     def testPackX86RomBadDesc(self):
649         """Test that the Intel requires a descriptor entry"""
650         with self.assertRaises(ValueError) as e:
651             self._DoTestFile('30_x86-rom-me-no-desc.dts')
652         self.assertIn("Node '/binman/intel-me': No position set with "
653                       "pos-unset: should another entry provide this correct "
654                       "position?", str(e.exception))
655
656     def testPackX86RomMe(self):
657         """Test that an x86 ROM with an ME region can be created"""
658         data = self._DoReadFile('31_x86-rom-me.dts')
659         self.assertEqual(ME_DATA, data[0x1000:0x1000 + len(ME_DATA)])
660
661     def testPackVga(self):
662         """Test that an image with a VGA binary can be created"""
663         data = self._DoReadFile('32_intel-vga.dts')
664         self.assertEqual(VGA_DATA, data[:len(VGA_DATA)])
665
666     def testPackStart16(self):
667         """Test that an image with an x86 start16 region can be created"""
668         data = self._DoReadFile('33_x86-start16.dts')
669         self.assertEqual(X86_START16_DATA, data[:len(X86_START16_DATA)])
670
671     def _RunMicrocodeTest(self, dts_fname, nodtb_data):
672         data = self._DoReadFile(dts_fname, True)
673
674         # Now check the device tree has no microcode
675         second = data[len(nodtb_data):]
676         fname = tools.GetOutputFilename('test.dtb')
677         with open(fname, 'wb') as fd:
678             fd.write(second)
679         dtb = fdt.FdtScan(fname)
680         ucode = dtb.GetNode('/microcode')
681         self.assertTrue(ucode)
682         for node in ucode.subnodes:
683             self.assertFalse(node.props.get('data'))
684
685         fdt_len = self.GetFdtLen(second)
686         third = second[fdt_len:]
687
688         # Check that the microcode appears immediately after the Fdt
689         # This matches the concatenation of the data properties in
690         # the /microcode/update@xxx nodes in 34_x86_ucode.dts.
691         ucode_data = struct.pack('>4L', 0x12345678, 0x12345679, 0xabcd0000,
692                                  0x78235609)
693         self.assertEqual(ucode_data, third[:len(ucode_data)])
694         ucode_pos = len(nodtb_data) + fdt_len
695
696         # Check that the microcode pointer was inserted. It should match the
697         # expected position and size
698         pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
699                                    len(ucode_data))
700         first = data[:len(nodtb_data)]
701         return first, pos_and_size
702
703     def testPackUbootMicrocode(self):
704         """Test that x86 microcode can be handled correctly
705
706         We expect to see the following in the image, in order:
707             u-boot-nodtb.bin with a microcode pointer inserted at the correct
708                 place
709             u-boot.dtb with the microcode removed
710             the microcode
711         """
712         first, pos_and_size = self._RunMicrocodeTest('34_x86_ucode.dts',
713                                                      U_BOOT_NODTB_DATA)
714         self.assertEqual('nodtb with microcode' + pos_and_size +
715                          ' somewhere in here', first)
716
717     def _RunPackUbootSingleMicrocode(self):
718         """Test that x86 microcode can be handled correctly
719
720         We expect to see the following in the image, in order:
721             u-boot-nodtb.bin with a microcode pointer inserted at the correct
722                 place
723             u-boot.dtb with the microcode
724             an empty microcode region
725         """
726         # We need the libfdt library to run this test since only that allows
727         # finding the offset of a property. This is required by
728         # Entry_u_boot_dtb_with_ucode.ObtainContents().
729         data = self._DoReadFile('35_x86_single_ucode.dts', True)
730
731         second = data[len(U_BOOT_NODTB_DATA):]
732
733         fdt_len = self.GetFdtLen(second)
734         third = second[fdt_len:]
735         second = second[:fdt_len]
736
737         ucode_data = struct.pack('>2L', 0x12345678, 0x12345679)
738         self.assertIn(ucode_data, second)
739         ucode_pos = second.find(ucode_data) + len(U_BOOT_NODTB_DATA)
740
741         # Check that the microcode pointer was inserted. It should match the
742         # expected position and size
743         pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
744                                    len(ucode_data))
745         first = data[:len(U_BOOT_NODTB_DATA)]
746         self.assertEqual('nodtb with microcode' + pos_and_size +
747                          ' somewhere in here', first)
748
749     def testPackUbootSingleMicrocode(self):
750         """Test that x86 microcode can be handled correctly with fdt_normal.
751         """
752         self._RunPackUbootSingleMicrocode()
753
754     def testUBootImg(self):
755         """Test that u-boot.img can be put in a file"""
756         data = self._DoReadFile('36_u_boot_img.dts')
757         self.assertEqual(U_BOOT_IMG_DATA, data)
758
759     def testNoMicrocode(self):
760         """Test that a missing microcode region is detected"""
761         with self.assertRaises(ValueError) as e:
762             self._DoReadFile('37_x86_no_ucode.dts', True)
763         self.assertIn("Node '/binman/u-boot-dtb-with-ucode': No /microcode "
764                       "node found in ", str(e.exception))
765
766     def testMicrocodeWithoutNode(self):
767         """Test that a missing u-boot-dtb-with-ucode node is detected"""
768         with self.assertRaises(ValueError) as e:
769             self._DoReadFile('38_x86_ucode_missing_node.dts', True)
770         self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
771                 "microcode region u-boot-dtb-with-ucode", str(e.exception))
772
773     def testMicrocodeWithoutNode2(self):
774         """Test that a missing u-boot-ucode node is detected"""
775         with self.assertRaises(ValueError) as e:
776             self._DoReadFile('39_x86_ucode_missing_node2.dts', True)
777         self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
778             "microcode region u-boot-ucode", str(e.exception))
779
780     def testMicrocodeWithoutPtrInElf(self):
781         """Test that a U-Boot binary without the microcode symbol is detected"""
782         # ELF file without a '_dt_ucode_base_size' symbol
783         try:
784             with open(self.TestFile('u_boot_no_ucode_ptr')) as fd:
785                 TestFunctional._MakeInputFile('u-boot', fd.read())
786
787             with self.assertRaises(ValueError) as e:
788                 self._RunPackUbootSingleMicrocode()
789             self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot locate "
790                     "_dt_ucode_base_size symbol in u-boot", str(e.exception))
791
792         finally:
793             # Put the original file back
794             with open(self.TestFile('u_boot_ucode_ptr')) as fd:
795                 TestFunctional._MakeInputFile('u-boot', fd.read())
796
797     def testMicrocodeNotInImage(self):
798         """Test that microcode must be placed within the image"""
799         with self.assertRaises(ValueError) as e:
800             self._DoReadFile('40_x86_ucode_not_in_image.dts', True)
801         self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Microcode "
802                 "pointer _dt_ucode_base_size at fffffe14 is outside the "
803                 "image ranging from 00000000 to 0000002e", str(e.exception))
804
805     def testWithoutMicrocode(self):
806         """Test that we can cope with an image without microcode (e.g. qemu)"""
807         with open(self.TestFile('u_boot_no_ucode_ptr')) as fd:
808             TestFunctional._MakeInputFile('u-boot', fd.read())
809         data, dtb = self._DoReadFileDtb('44_x86_optional_ucode.dts', True)
810
811         # Now check the device tree has no microcode
812         self.assertEqual(U_BOOT_NODTB_DATA, data[:len(U_BOOT_NODTB_DATA)])
813         second = data[len(U_BOOT_NODTB_DATA):]
814
815         fdt_len = self.GetFdtLen(second)
816         self.assertEqual(dtb, second[:fdt_len])
817
818         used_len = len(U_BOOT_NODTB_DATA) + fdt_len
819         third = data[used_len:]
820         self.assertEqual(chr(0) * (0x200 - used_len), third)
821
822     def testUnknownPosSize(self):
823         """Test that microcode must be placed within the image"""
824         with self.assertRaises(ValueError) as e:
825             self._DoReadFile('41_unknown_pos_size.dts', True)
826         self.assertIn("Image '/binman': Unable to set pos/size for unknown "
827                 "entry 'invalid-entry'", str(e.exception))
828
829     def testPackFsp(self):
830         """Test that an image with a FSP binary can be created"""
831         data = self._DoReadFile('42_intel-fsp.dts')
832         self.assertEqual(FSP_DATA, data[:len(FSP_DATA)])
833
834     def testPackCmc(self):
835         """Test that an image with a CMC binary can be created"""
836         data = self._DoReadFile('43_intel-cmc.dts')
837         self.assertEqual(CMC_DATA, data[:len(CMC_DATA)])
838
839     def testPackVbt(self):
840         """Test that an image with a VBT binary can be created"""
841         data = self._DoReadFile('46_intel-vbt.dts')
842         self.assertEqual(VBT_DATA, data[:len(VBT_DATA)])
843
844     def testSplBssPad(self):
845         """Test that we can pad SPL's BSS with zeros"""
846         # ELF file with a '__bss_size' symbol
847         with open(self.TestFile('bss_data')) as fd:
848             TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
849         data = self._DoReadFile('47_spl_bss_pad.dts')
850         self.assertEqual(U_BOOT_SPL_DATA + (chr(0) * 10) + U_BOOT_DATA, data)
851
852         with open(self.TestFile('u_boot_ucode_ptr')) as fd:
853             TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
854         with self.assertRaises(ValueError) as e:
855             data = self._DoReadFile('47_spl_bss_pad.dts')
856         self.assertIn('Expected __bss_size symbol in spl/u-boot-spl',
857                       str(e.exception))
858
859     def testPackStart16Spl(self):
860         """Test that an image with an x86 start16 region can be created"""
861         data = self._DoReadFile('48_x86-start16-spl.dts')
862         self.assertEqual(X86_START16_SPL_DATA, data[:len(X86_START16_SPL_DATA)])
863
864     def testPackUbootSplMicrocode(self):
865         """Test that x86 microcode can be handled correctly in SPL
866
867         We expect to see the following in the image, in order:
868             u-boot-spl-nodtb.bin with a microcode pointer inserted at the
869                 correct place
870             u-boot.dtb with the microcode removed
871             the microcode
872         """
873         # ELF file with a '_dt_ucode_base_size' symbol
874         with open(self.TestFile('u_boot_ucode_ptr')) as fd:
875             TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
876         first, pos_and_size = self._RunMicrocodeTest('49_x86_ucode_spl.dts',
877                                                      U_BOOT_SPL_NODTB_DATA)
878         self.assertEqual('splnodtb with microc' + pos_and_size +
879                          'ter somewhere in here', first)
880
881     def testPackMrc(self):
882         """Test that an image with an MRC binary can be created"""
883         data = self._DoReadFile('50_intel_mrc.dts')
884         self.assertEqual(MRC_DATA, data[:len(MRC_DATA)])
885
886     def testSplDtb(self):
887         """Test that an image with spl/u-boot-spl.dtb can be created"""
888         data = self._DoReadFile('51_u_boot_spl_dtb.dts')
889         self.assertEqual(U_BOOT_SPL_DTB_DATA, data[:len(U_BOOT_SPL_DTB_DATA)])
890
891     def testSplNoDtb(self):
892         """Test that an image with spl/u-boot-spl-nodtb.bin can be created"""
893         data = self._DoReadFile('52_u_boot_spl_nodtb.dts')
894         self.assertEqual(U_BOOT_SPL_NODTB_DATA, data[:len(U_BOOT_SPL_NODTB_DATA)])
895
896     def testSymbols(self):
897         """Test binman can assign symbols embedded in U-Boot"""
898         elf_fname = self.TestFile('u_boot_binman_syms')
899         syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
900         addr = elf.GetSymbolAddress(elf_fname, '__image_copy_start')
901         self.assertEqual(syms['_binman_u_boot_spl_prop_pos'].address, addr)
902
903         with open(self.TestFile('u_boot_binman_syms')) as fd:
904             TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
905         data = self._DoReadFile('53_symbols.dts')
906         sym_values = struct.pack('<LQL', 0x24 + 0, 0x24 + 24, 0x24 + 20)
907         expected = (sym_values + U_BOOT_SPL_DATA[16:] + chr(0xff) +
908                     U_BOOT_DATA +
909                     sym_values + U_BOOT_SPL_DATA[16:])
910         self.assertEqual(expected, data)
911
912
913 if __name__ == "__main__":
914     unittest.main()