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