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