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