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