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