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