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