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