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