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