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