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