1 # SPDX-License-Identifier: GPL-2.0+
2 # Copyright (c) 2016 Google, Inc
3 # Written by Simon Glass <sjg@chromium.org>
5 # To run a single test, change to this directory, and:
7 # python -m unittest func_test.TestFunctional.testHelp
9 from optparse import OptionParser
29 # Contents of test files, corresponding to different entry types
31 U_BOOT_IMG_DATA = 'img'
32 U_BOOT_SPL_DATA = '56780123456789abcde'
36 U_BOOT_DTB_DATA = 'udtb'
37 U_BOOT_SPL_DTB_DATA = 'spldtb'
38 X86_START16_DATA = 'start16'
39 X86_START16_SPL_DATA = 'start16spl'
40 U_BOOT_NODTB_DATA = 'nodtb with microcode pointer somewhere in here'
41 U_BOOT_SPL_NODTB_DATA = 'splnodtb with microcode pointer somewhere in here'
50 class TestFunctional(unittest.TestCase):
51 """Functional tests for binman
53 Most of these use a sample .dts file to build an image and then check
54 that it looks correct. The sample files are in the test/ subdirectory
57 For each entry type a very small test file is created using fixed
58 string contents. This makes it easy to test that things look right, and
61 In some cases a 'real' file must be used - these are also supplied in
69 # Handle the case where argv[0] is 'python'
70 self._binman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
71 self._binman_pathname = os.path.join(self._binman_dir, 'binman')
73 # Create a temporary directory for input files
74 self._indir = tempfile.mkdtemp(prefix='binmant.')
76 # Create some test files
77 TestFunctional._MakeInputFile('u-boot.bin', U_BOOT_DATA)
78 TestFunctional._MakeInputFile('u-boot.img', U_BOOT_IMG_DATA)
79 TestFunctional._MakeInputFile('spl/u-boot-spl.bin', U_BOOT_SPL_DATA)
80 TestFunctional._MakeInputFile('blobfile', BLOB_DATA)
81 TestFunctional._MakeInputFile('me.bin', ME_DATA)
82 TestFunctional._MakeInputFile('vga.bin', VGA_DATA)
83 TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA)
84 TestFunctional._MakeInputFile('spl/u-boot-spl.dtb', U_BOOT_SPL_DTB_DATA)
85 TestFunctional._MakeInputFile('u-boot-x86-16bit.bin', X86_START16_DATA)
86 TestFunctional._MakeInputFile('spl/u-boot-x86-16bit-spl.bin',
88 TestFunctional._MakeInputFile('u-boot-nodtb.bin', U_BOOT_NODTB_DATA)
89 TestFunctional._MakeInputFile('spl/u-boot-spl-nodtb.bin',
90 U_BOOT_SPL_NODTB_DATA)
91 TestFunctional._MakeInputFile('fsp.bin', FSP_DATA)
92 TestFunctional._MakeInputFile('cmc.bin', CMC_DATA)
93 TestFunctional._MakeInputFile('vbt.bin', VBT_DATA)
94 TestFunctional._MakeInputFile('mrc.bin', MRC_DATA)
95 self._output_setup = False
97 # ELF file with a '_dt_ucode_base_size' symbol
98 with open(self.TestFile('u_boot_ucode_ptr')) as fd:
99 TestFunctional._MakeInputFile('u-boot', fd.read())
101 # Intel flash descriptor file
102 with open(self.TestFile('descriptor.bin')) as fd:
103 TestFunctional._MakeInputFile('descriptor.bin', fd.read())
106 def tearDownClass(self):
107 """Remove the temporary input directory and its contents"""
109 shutil.rmtree(self._indir)
113 # Enable this to turn on debugging output
114 # tout.Init(tout.DEBUG)
115 command.test_result = None
118 """Remove the temporary output directory"""
119 tools._FinaliseForTest()
121 def _RunBinman(self, *args, **kwargs):
122 """Run binman using the command line
125 Arguments to pass, as a list of strings
126 kwargs: Arguments to pass to Command.RunPipe()
128 result = command.RunPipe([[self._binman_pathname] + list(args)],
129 capture=True, capture_stderr=True, raise_on_error=False)
130 if result.return_code and kwargs.get('raise_on_error', True):
131 raise Exception("Error running '%s': %s" % (' '.join(args),
132 result.stdout + result.stderr))
135 def _DoBinman(self, *args):
136 """Run binman using directly (in the same process)
139 Arguments to pass, as a list of strings
141 Return value (0 for success)
146 (options, args) = cmdline.ParseArgs(args)
147 options.pager = 'binman-invalid-pager'
148 options.build_dir = self._indir
150 # For testing, you can force an increase in verbosity here
151 # options.verbosity = tout.DEBUG
152 return control.Binman(options, args)
154 def _DoTestFile(self, fname, debug=False, map=False, update_dtb=False,
156 """Run binman with a given test file
159 fname: Device-tree source filename to use (e.g. 05_simple.dts)
160 debug: True to enable debugging output
161 map: True to output map files for the images
162 update_dtb: Update the offset and size of each entry in the device
163 tree before packing it into the image
165 args = ['-p', '-I', self._indir, '-d', self.TestFile(fname)]
173 for arg, value in entry_args.iteritems():
174 args.append('-a%s=%s' % (arg, value))
175 return self._DoBinman(*args)
177 def _SetupDtb(self, fname, outfile='u-boot.dtb'):
178 """Set up a new test device-tree file
180 The given file is compiled and set up as the device tree to be used
184 fname: Filename of .dts file to read
185 outfile: Output filename for compiled device-tree binary
188 Contents of device-tree binary
190 if not self._output_setup:
191 tools.PrepareOutputDir(self._indir, True)
192 self._output_setup = True
193 dtb = fdt_util.EnsureCompiled(self.TestFile(fname))
194 with open(dtb) as fd:
196 TestFunctional._MakeInputFile(outfile, data)
199 def _DoReadFileDtb(self, fname, use_real_dtb=False, map=False,
200 update_dtb=False, entry_args=None):
201 """Run binman and return the resulting image
203 This runs binman with a given test file and then reads the resulting
204 output file. It is a shortcut function since most tests need to do
207 Raises an assertion failure if binman returns a non-zero exit code.
210 fname: Device-tree source filename to use (e.g. 05_simple.dts)
211 use_real_dtb: True to use the test file as the contents of
212 the u-boot-dtb entry. Normally this is not needed and the
213 test contents (the U_BOOT_DTB_DATA string) can be used.
214 But in some test we need the real contents.
215 map: True to output map files for the images
216 update_dtb: Update the offset and size of each entry in the device
217 tree before packing it into the image
221 Resulting image contents
223 Map data showing contents of image (or None if none)
224 Output device tree binary filename ('u-boot.dtb' path)
227 # Use the compiled test file as the u-boot-dtb input
229 dtb_data = self._SetupDtb(fname)
232 retcode = self._DoTestFile(fname, map=map, update_dtb=update_dtb,
233 entry_args=entry_args)
234 self.assertEqual(0, retcode)
235 out_dtb_fname = control.GetFdtPath('u-boot.dtb')
237 # Find the (only) image, read it and return its contents
238 image = control.images['image']
239 image_fname = tools.GetOutputFilename('image.bin')
240 self.assertTrue(os.path.exists(image_fname))
242 map_fname = tools.GetOutputFilename('image.map')
243 with open(map_fname) as fd:
247 with open(image_fname) as fd:
248 return fd.read(), dtb_data, map_data, out_dtb_fname
250 # Put the test file back
252 TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA)
254 def _DoReadFile(self, fname, use_real_dtb=False):
255 """Helper function which discards the device-tree binary
258 fname: Device-tree source filename to use (e.g. 05_simple.dts)
259 use_real_dtb: True to use the test file as the contents of
260 the u-boot-dtb entry. Normally this is not needed and the
261 test contents (the U_BOOT_DTB_DATA string) can be used.
262 But in some test we need the real contents.
265 Resulting image contents
267 return self._DoReadFileDtb(fname, use_real_dtb)[0]
270 def _MakeInputFile(self, fname, contents):
271 """Create a new test input file, creating directories as needed
274 fname: Filename to create
275 contents: File contents to write in to the file
277 Full pathname of file created
279 pathname = os.path.join(self._indir, fname)
280 dirname = os.path.dirname(pathname)
281 if dirname and not os.path.exists(dirname):
283 with open(pathname, 'wb') as fd:
288 def TestFile(self, fname):
289 return os.path.join(self._binman_dir, 'test', fname)
291 def AssertInList(self, grep_list, target):
292 """Assert that at least one of a list of things is in a target
295 grep_list: List of strings to check
296 target: Target string
298 for grep in grep_list:
301 self.fail("Error: '%' not found in '%s'" % (grep_list, target))
303 def CheckNoGaps(self, entries):
304 """Check that all entries fit together without gaps
307 entries: List of entries to check
310 for entry in entries.values():
311 self.assertEqual(offset, entry.offset)
314 def GetFdtLen(self, dtb):
315 """Get the totalsize field from a device-tree binary
318 dtb: Device-tree binary contents
321 Total size of device-tree binary, from the header
323 return struct.unpack('>L', dtb[4:8])[0]
325 def _GetPropTree(self, dtb_data, node_names):
326 def AddNode(node, path):
328 path += '/' + node.name
329 for subnode in node.subnodes:
330 for prop in subnode.props.values():
331 if prop.name in node_names:
332 prop_path = path + '/' + subnode.name + ':' + prop.name
333 tree[prop_path[len('/binman/'):]] = fdt_util.fdt32_to_cpu(
335 AddNode(subnode, path)
338 dtb = fdt.Fdt(dtb_data)
340 AddNode(dtb.GetRoot(), '')
344 """Test a basic run with valid args"""
345 result = self._RunBinman('-h')
347 def testFullHelp(self):
348 """Test that the full help is displayed with -H"""
349 result = self._RunBinman('-H')
350 help_file = os.path.join(self._binman_dir, 'README')
351 # Remove possible extraneous strings
352 extra = '::::::::::::::\n' + help_file + '\n::::::::::::::\n'
353 gothelp = result.stdout.replace(extra, '')
354 self.assertEqual(len(gothelp), os.path.getsize(help_file))
355 self.assertEqual(0, len(result.stderr))
356 self.assertEqual(0, result.return_code)
358 def testFullHelpInternal(self):
359 """Test that the full help is displayed with -H"""
361 command.test_result = command.CommandResult()
362 result = self._DoBinman('-H')
363 help_file = os.path.join(self._binman_dir, 'README')
365 command.test_result = None
368 """Test that the basic help is displayed with -h"""
369 result = self._RunBinman('-h')
370 self.assertTrue(len(result.stdout) > 200)
371 self.assertEqual(0, len(result.stderr))
372 self.assertEqual(0, result.return_code)
375 """Test that we can run it with a specific board"""
376 self._SetupDtb('05_simple.dts', 'sandbox/u-boot.dtb')
377 TestFunctional._MakeInputFile('sandbox/u-boot.bin', U_BOOT_DATA)
378 result = self._DoBinman('-b', 'sandbox')
379 self.assertEqual(0, result)
381 def testNeedBoard(self):
382 """Test that we get an error when no board ius supplied"""
383 with self.assertRaises(ValueError) as e:
384 result = self._DoBinman()
385 self.assertIn("Must provide a board to process (use -b <board>)",
388 def testMissingDt(self):
389 """Test that an invalid device-tree file generates an error"""
390 with self.assertRaises(Exception) as e:
391 self._RunBinman('-d', 'missing_file')
392 # We get one error from libfdt, and a different one from fdtget.
393 self.AssertInList(["Couldn't open blob from 'missing_file'",
394 'No such file or directory'], str(e.exception))
396 def testBrokenDt(self):
397 """Test that an invalid device-tree source file generates an error
399 Since this is a source file it should be compiled and the error
400 will come from the device-tree compiler (dtc).
402 with self.assertRaises(Exception) as e:
403 self._RunBinman('-d', self.TestFile('01_invalid.dts'))
404 self.assertIn("FATAL ERROR: Unable to parse input tree",
407 def testMissingNode(self):
408 """Test that a device tree without a 'binman' node generates an error"""
409 with self.assertRaises(Exception) as e:
410 self._DoBinman('-d', self.TestFile('02_missing_node.dts'))
411 self.assertIn("does not have a 'binman' node", str(e.exception))
414 """Test that an empty binman node works OK (i.e. does nothing)"""
415 result = self._RunBinman('-d', self.TestFile('03_empty.dts'))
416 self.assertEqual(0, len(result.stderr))
417 self.assertEqual(0, result.return_code)
419 def testInvalidEntry(self):
420 """Test that an invalid entry is flagged"""
421 with self.assertRaises(Exception) as e:
422 result = self._RunBinman('-d',
423 self.TestFile('04_invalid_entry.dts'))
424 self.assertIn("Unknown entry type 'not-a-valid-type' in node "
425 "'/binman/not-a-valid-type'", str(e.exception))
427 def testSimple(self):
428 """Test a simple binman with a single file"""
429 data = self._DoReadFile('05_simple.dts')
430 self.assertEqual(U_BOOT_DATA, data)
432 def testSimpleDebug(self):
433 """Test a simple binman run with debugging enabled"""
434 data = self._DoTestFile('05_simple.dts', debug=True)
437 """Test that we can handle creating two images
439 This also tests image padding.
441 retcode = self._DoTestFile('06_dual_image.dts')
442 self.assertEqual(0, retcode)
444 image = control.images['image1']
445 self.assertEqual(len(U_BOOT_DATA), image._size)
446 fname = tools.GetOutputFilename('image1.bin')
447 self.assertTrue(os.path.exists(fname))
448 with open(fname) as fd:
450 self.assertEqual(U_BOOT_DATA, data)
452 image = control.images['image2']
453 self.assertEqual(3 + len(U_BOOT_DATA) + 5, image._size)
454 fname = tools.GetOutputFilename('image2.bin')
455 self.assertTrue(os.path.exists(fname))
456 with open(fname) as fd:
458 self.assertEqual(U_BOOT_DATA, data[3:7])
459 self.assertEqual(chr(0) * 3, data[:3])
460 self.assertEqual(chr(0) * 5, data[7:])
462 def testBadAlign(self):
463 """Test that an invalid alignment value is detected"""
464 with self.assertRaises(ValueError) as e:
465 self._DoTestFile('07_bad_align.dts')
466 self.assertIn("Node '/binman/u-boot': Alignment 23 must be a power "
467 "of two", str(e.exception))
469 def testPackSimple(self):
470 """Test that packing works as expected"""
471 retcode = self._DoTestFile('08_pack.dts')
472 self.assertEqual(0, retcode)
473 self.assertIn('image', control.images)
474 image = control.images['image']
475 entries = image.GetEntries()
476 self.assertEqual(5, len(entries))
479 self.assertIn('u-boot', entries)
480 entry = entries['u-boot']
481 self.assertEqual(0, entry.offset)
482 self.assertEqual(len(U_BOOT_DATA), entry.size)
484 # Second u-boot, aligned to 16-byte boundary
485 self.assertIn('u-boot-align', entries)
486 entry = entries['u-boot-align']
487 self.assertEqual(16, entry.offset)
488 self.assertEqual(len(U_BOOT_DATA), entry.size)
490 # Third u-boot, size 23 bytes
491 self.assertIn('u-boot-size', entries)
492 entry = entries['u-boot-size']
493 self.assertEqual(20, entry.offset)
494 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
495 self.assertEqual(23, entry.size)
497 # Fourth u-boot, placed immediate after the above
498 self.assertIn('u-boot-next', entries)
499 entry = entries['u-boot-next']
500 self.assertEqual(43, entry.offset)
501 self.assertEqual(len(U_BOOT_DATA), entry.size)
503 # Fifth u-boot, placed at a fixed offset
504 self.assertIn('u-boot-fixed', entries)
505 entry = entries['u-boot-fixed']
506 self.assertEqual(61, entry.offset)
507 self.assertEqual(len(U_BOOT_DATA), entry.size)
509 self.assertEqual(65, image._size)
511 def testPackExtra(self):
512 """Test that extra packing feature works as expected"""
513 retcode = self._DoTestFile('09_pack_extra.dts')
515 self.assertEqual(0, retcode)
516 self.assertIn('image', control.images)
517 image = control.images['image']
518 entries = image.GetEntries()
519 self.assertEqual(5, len(entries))
521 # First u-boot with padding before and after
522 self.assertIn('u-boot', entries)
523 entry = entries['u-boot']
524 self.assertEqual(0, entry.offset)
525 self.assertEqual(3, entry.pad_before)
526 self.assertEqual(3 + 5 + len(U_BOOT_DATA), entry.size)
528 # Second u-boot has an aligned size, but it has no effect
529 self.assertIn('u-boot-align-size-nop', entries)
530 entry = entries['u-boot-align-size-nop']
531 self.assertEqual(12, entry.offset)
532 self.assertEqual(4, entry.size)
534 # Third u-boot has an aligned size too
535 self.assertIn('u-boot-align-size', entries)
536 entry = entries['u-boot-align-size']
537 self.assertEqual(16, entry.offset)
538 self.assertEqual(32, entry.size)
540 # Fourth u-boot has an aligned end
541 self.assertIn('u-boot-align-end', entries)
542 entry = entries['u-boot-align-end']
543 self.assertEqual(48, entry.offset)
544 self.assertEqual(16, entry.size)
546 # Fifth u-boot immediately afterwards
547 self.assertIn('u-boot-align-both', entries)
548 entry = entries['u-boot-align-both']
549 self.assertEqual(64, entry.offset)
550 self.assertEqual(64, entry.size)
552 self.CheckNoGaps(entries)
553 self.assertEqual(128, image._size)
555 def testPackAlignPowerOf2(self):
556 """Test that invalid entry alignment is detected"""
557 with self.assertRaises(ValueError) as e:
558 self._DoTestFile('10_pack_align_power2.dts')
559 self.assertIn("Node '/binman/u-boot': Alignment 5 must be a power "
560 "of two", str(e.exception))
562 def testPackAlignSizePowerOf2(self):
563 """Test that invalid entry size alignment is detected"""
564 with self.assertRaises(ValueError) as e:
565 self._DoTestFile('11_pack_align_size_power2.dts')
566 self.assertIn("Node '/binman/u-boot': Alignment size 55 must be a "
567 "power of two", str(e.exception))
569 def testPackInvalidAlign(self):
570 """Test detection of an offset that does not match its alignment"""
571 with self.assertRaises(ValueError) as e:
572 self._DoTestFile('12_pack_inv_align.dts')
573 self.assertIn("Node '/binman/u-boot': Offset 0x5 (5) does not match "
574 "align 0x4 (4)", str(e.exception))
576 def testPackInvalidSizeAlign(self):
577 """Test that invalid entry size alignment is detected"""
578 with self.assertRaises(ValueError) as e:
579 self._DoTestFile('13_pack_inv_size_align.dts')
580 self.assertIn("Node '/binman/u-boot': Size 0x5 (5) does not match "
581 "align-size 0x4 (4)", str(e.exception))
583 def testPackOverlap(self):
584 """Test that overlapping regions are detected"""
585 with self.assertRaises(ValueError) as e:
586 self._DoTestFile('14_pack_overlap.dts')
587 self.assertIn("Node '/binman/u-boot-align': Offset 0x3 (3) overlaps "
588 "with previous entry '/binman/u-boot' ending at 0x4 (4)",
591 def testPackEntryOverflow(self):
592 """Test that entries that overflow their size are detected"""
593 with self.assertRaises(ValueError) as e:
594 self._DoTestFile('15_pack_overflow.dts')
595 self.assertIn("Node '/binman/u-boot': Entry contents size is 0x4 (4) "
596 "but entry size is 0x3 (3)", str(e.exception))
598 def testPackImageOverflow(self):
599 """Test that entries which overflow the image size are detected"""
600 with self.assertRaises(ValueError) as e:
601 self._DoTestFile('16_pack_image_overflow.dts')
602 self.assertIn("Section '/binman': contents size 0x4 (4) exceeds section "
603 "size 0x3 (3)", str(e.exception))
605 def testPackImageSize(self):
606 """Test that the image size can be set"""
607 retcode = self._DoTestFile('17_pack_image_size.dts')
608 self.assertEqual(0, retcode)
609 self.assertIn('image', control.images)
610 image = control.images['image']
611 self.assertEqual(7, image._size)
613 def testPackImageSizeAlign(self):
614 """Test that image size alignemnt works as expected"""
615 retcode = self._DoTestFile('18_pack_image_align.dts')
616 self.assertEqual(0, retcode)
617 self.assertIn('image', control.images)
618 image = control.images['image']
619 self.assertEqual(16, image._size)
621 def testPackInvalidImageAlign(self):
622 """Test that invalid image alignment is detected"""
623 with self.assertRaises(ValueError) as e:
624 self._DoTestFile('19_pack_inv_image_align.dts')
625 self.assertIn("Section '/binman': Size 0x7 (7) does not match "
626 "align-size 0x8 (8)", str(e.exception))
628 def testPackAlignPowerOf2(self):
629 """Test that invalid image alignment is detected"""
630 with self.assertRaises(ValueError) as e:
631 self._DoTestFile('20_pack_inv_image_align_power2.dts')
632 self.assertIn("Section '/binman': Alignment size 131 must be a power of "
633 "two", str(e.exception))
635 def testImagePadByte(self):
636 """Test that the image pad byte can be specified"""
637 with open(self.TestFile('bss_data')) as fd:
638 TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
639 data = self._DoReadFile('21_image_pad.dts')
640 self.assertEqual(U_BOOT_SPL_DATA + (chr(0xff) * 1) + U_BOOT_DATA, data)
642 def testImageName(self):
643 """Test that image files can be named"""
644 retcode = self._DoTestFile('22_image_name.dts')
645 self.assertEqual(0, retcode)
646 image = control.images['image1']
647 fname = tools.GetOutputFilename('test-name')
648 self.assertTrue(os.path.exists(fname))
650 image = control.images['image2']
651 fname = tools.GetOutputFilename('test-name.xx')
652 self.assertTrue(os.path.exists(fname))
654 def testBlobFilename(self):
655 """Test that generic blobs can be provided by filename"""
656 data = self._DoReadFile('23_blob.dts')
657 self.assertEqual(BLOB_DATA, data)
659 def testPackSorted(self):
660 """Test that entries can be sorted"""
661 data = self._DoReadFile('24_sorted.dts')
662 self.assertEqual(chr(0) * 1 + U_BOOT_SPL_DATA + chr(0) * 2 +
665 def testPackZeroOffset(self):
666 """Test that an entry at offset 0 is not given a new offset"""
667 with self.assertRaises(ValueError) as e:
668 self._DoTestFile('25_pack_zero_size.dts')
669 self.assertIn("Node '/binman/u-boot-spl': Offset 0x0 (0) overlaps "
670 "with previous entry '/binman/u-boot' ending at 0x4 (4)",
673 def testPackUbootDtb(self):
674 """Test that a device tree can be added to U-Boot"""
675 data = self._DoReadFile('26_pack_u_boot_dtb.dts')
676 self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA, data)
678 def testPackX86RomNoSize(self):
679 """Test that the end-at-4gb property requires a size property"""
680 with self.assertRaises(ValueError) as e:
681 self._DoTestFile('27_pack_4gb_no_size.dts')
682 self.assertIn("Section '/binman': Section size must be provided when "
683 "using end-at-4gb", str(e.exception))
685 def testPackX86RomOutside(self):
686 """Test that the end-at-4gb property checks for offset boundaries"""
687 with self.assertRaises(ValueError) as e:
688 self._DoTestFile('28_pack_4gb_outside.dts')
689 self.assertIn("Node '/binman/u-boot': Offset 0x0 (0) is outside "
690 "the section starting at 0xffffffe0 (4294967264)",
693 def testPackX86Rom(self):
694 """Test that a basic x86 ROM can be created"""
695 data = self._DoReadFile('29_x86-rom.dts')
696 self.assertEqual(U_BOOT_DATA + chr(0) * 7 + U_BOOT_SPL_DATA +
699 def testPackX86RomMeNoDesc(self):
700 """Test that an invalid Intel descriptor entry is detected"""
701 TestFunctional._MakeInputFile('descriptor.bin', '')
702 with self.assertRaises(ValueError) as e:
703 self._DoTestFile('31_x86-rom-me.dts')
704 self.assertIn("Node '/binman/intel-descriptor': Cannot find FD "
705 "signature", str(e.exception))
707 def testPackX86RomBadDesc(self):
708 """Test that the Intel requires a descriptor entry"""
709 with self.assertRaises(ValueError) as e:
710 self._DoTestFile('30_x86-rom-me-no-desc.dts')
711 self.assertIn("Node '/binman/intel-me': No offset set with "
712 "offset-unset: should another entry provide this correct "
713 "offset?", str(e.exception))
715 def testPackX86RomMe(self):
716 """Test that an x86 ROM with an ME region can be created"""
717 data = self._DoReadFile('31_x86-rom-me.dts')
718 self.assertEqual(ME_DATA, data[0x1000:0x1000 + len(ME_DATA)])
720 def testPackVga(self):
721 """Test that an image with a VGA binary can be created"""
722 data = self._DoReadFile('32_intel-vga.dts')
723 self.assertEqual(VGA_DATA, data[:len(VGA_DATA)])
725 def testPackStart16(self):
726 """Test that an image with an x86 start16 region can be created"""
727 data = self._DoReadFile('33_x86-start16.dts')
728 self.assertEqual(X86_START16_DATA, data[:len(X86_START16_DATA)])
730 def _RunMicrocodeTest(self, dts_fname, nodtb_data, ucode_second=False):
731 """Handle running a test for insertion of microcode
734 dts_fname: Name of test .dts file
735 nodtb_data: Data that we expect in the first section
736 ucode_second: True if the microsecond entry is second instead of
741 Contents of first region (U-Boot or SPL)
742 Offset and size components of microcode pointer, as inserted
743 in the above (two 4-byte words)
745 data = self._DoReadFile(dts_fname, True)
747 # Now check the device tree has no microcode
749 ucode_content = data[len(nodtb_data):]
750 ucode_pos = len(nodtb_data)
751 dtb_with_ucode = ucode_content[16:]
752 fdt_len = self.GetFdtLen(dtb_with_ucode)
754 dtb_with_ucode = data[len(nodtb_data):]
755 fdt_len = self.GetFdtLen(dtb_with_ucode)
756 ucode_content = dtb_with_ucode[fdt_len:]
757 ucode_pos = len(nodtb_data) + fdt_len
758 fname = tools.GetOutputFilename('test.dtb')
759 with open(fname, 'wb') as fd:
760 fd.write(dtb_with_ucode)
761 dtb = fdt.FdtScan(fname)
762 ucode = dtb.GetNode('/microcode')
763 self.assertTrue(ucode)
764 for node in ucode.subnodes:
765 self.assertFalse(node.props.get('data'))
767 # Check that the microcode appears immediately after the Fdt
768 # This matches the concatenation of the data properties in
769 # the /microcode/update@xxx nodes in 34_x86_ucode.dts.
770 ucode_data = struct.pack('>4L', 0x12345678, 0x12345679, 0xabcd0000,
772 self.assertEqual(ucode_data, ucode_content[:len(ucode_data)])
774 # Check that the microcode pointer was inserted. It should match the
775 # expected offset and size
776 pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
778 u_boot = data[:len(nodtb_data)]
779 return u_boot, pos_and_size
781 def testPackUbootMicrocode(self):
782 """Test that x86 microcode can be handled correctly
784 We expect to see the following in the image, in order:
785 u-boot-nodtb.bin with a microcode pointer inserted at the correct
787 u-boot.dtb with the microcode removed
790 first, pos_and_size = self._RunMicrocodeTest('34_x86_ucode.dts',
792 self.assertEqual('nodtb with microcode' + pos_and_size +
793 ' somewhere in here', first)
795 def _RunPackUbootSingleMicrocode(self):
796 """Test that x86 microcode can be handled correctly
798 We expect to see the following in the image, in order:
799 u-boot-nodtb.bin with a microcode pointer inserted at the correct
801 u-boot.dtb with the microcode
802 an empty microcode region
804 # We need the libfdt library to run this test since only that allows
805 # finding the offset of a property. This is required by
806 # Entry_u_boot_dtb_with_ucode.ObtainContents().
807 data = self._DoReadFile('35_x86_single_ucode.dts', True)
809 second = data[len(U_BOOT_NODTB_DATA):]
811 fdt_len = self.GetFdtLen(second)
812 third = second[fdt_len:]
813 second = second[:fdt_len]
815 ucode_data = struct.pack('>2L', 0x12345678, 0x12345679)
816 self.assertIn(ucode_data, second)
817 ucode_pos = second.find(ucode_data) + len(U_BOOT_NODTB_DATA)
819 # Check that the microcode pointer was inserted. It should match the
820 # expected offset and size
821 pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
823 first = data[:len(U_BOOT_NODTB_DATA)]
824 self.assertEqual('nodtb with microcode' + pos_and_size +
825 ' somewhere in here', first)
827 def testPackUbootSingleMicrocode(self):
828 """Test that x86 microcode can be handled correctly with fdt_normal.
830 self._RunPackUbootSingleMicrocode()
832 def testUBootImg(self):
833 """Test that u-boot.img can be put in a file"""
834 data = self._DoReadFile('36_u_boot_img.dts')
835 self.assertEqual(U_BOOT_IMG_DATA, data)
837 def testNoMicrocode(self):
838 """Test that a missing microcode region is detected"""
839 with self.assertRaises(ValueError) as e:
840 self._DoReadFile('37_x86_no_ucode.dts', True)
841 self.assertIn("Node '/binman/u-boot-dtb-with-ucode': No /microcode "
842 "node found in ", str(e.exception))
844 def testMicrocodeWithoutNode(self):
845 """Test that a missing u-boot-dtb-with-ucode node is detected"""
846 with self.assertRaises(ValueError) as e:
847 self._DoReadFile('38_x86_ucode_missing_node.dts', True)
848 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
849 "microcode region u-boot-dtb-with-ucode", str(e.exception))
851 def testMicrocodeWithoutNode2(self):
852 """Test that a missing u-boot-ucode node is detected"""
853 with self.assertRaises(ValueError) as e:
854 self._DoReadFile('39_x86_ucode_missing_node2.dts', True)
855 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
856 "microcode region u-boot-ucode", str(e.exception))
858 def testMicrocodeWithoutPtrInElf(self):
859 """Test that a U-Boot binary without the microcode symbol is detected"""
860 # ELF file without a '_dt_ucode_base_size' symbol
862 with open(self.TestFile('u_boot_no_ucode_ptr')) as fd:
863 TestFunctional._MakeInputFile('u-boot', fd.read())
865 with self.assertRaises(ValueError) as e:
866 self._RunPackUbootSingleMicrocode()
867 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot locate "
868 "_dt_ucode_base_size symbol in u-boot", str(e.exception))
871 # Put the original file back
872 with open(self.TestFile('u_boot_ucode_ptr')) as fd:
873 TestFunctional._MakeInputFile('u-boot', fd.read())
875 def testMicrocodeNotInImage(self):
876 """Test that microcode must be placed within the image"""
877 with self.assertRaises(ValueError) as e:
878 self._DoReadFile('40_x86_ucode_not_in_image.dts', True)
879 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Microcode "
880 "pointer _dt_ucode_base_size at fffffe14 is outside the "
881 "section ranging from 00000000 to 0000002e", str(e.exception))
883 def testWithoutMicrocode(self):
884 """Test that we can cope with an image without microcode (e.g. qemu)"""
885 with open(self.TestFile('u_boot_no_ucode_ptr')) as fd:
886 TestFunctional._MakeInputFile('u-boot', fd.read())
887 data, dtb, _, _ = self._DoReadFileDtb('44_x86_optional_ucode.dts', True)
889 # Now check the device tree has no microcode
890 self.assertEqual(U_BOOT_NODTB_DATA, data[:len(U_BOOT_NODTB_DATA)])
891 second = data[len(U_BOOT_NODTB_DATA):]
893 fdt_len = self.GetFdtLen(second)
894 self.assertEqual(dtb, second[:fdt_len])
896 used_len = len(U_BOOT_NODTB_DATA) + fdt_len
897 third = data[used_len:]
898 self.assertEqual(chr(0) * (0x200 - used_len), third)
900 def testUnknownPosSize(self):
901 """Test that microcode must be placed within the image"""
902 with self.assertRaises(ValueError) as e:
903 self._DoReadFile('41_unknown_pos_size.dts', True)
904 self.assertIn("Section '/binman': Unable to set offset/size for unknown "
905 "entry 'invalid-entry'", str(e.exception))
907 def testPackFsp(self):
908 """Test that an image with a FSP binary can be created"""
909 data = self._DoReadFile('42_intel-fsp.dts')
910 self.assertEqual(FSP_DATA, data[:len(FSP_DATA)])
912 def testPackCmc(self):
913 """Test that an image with a CMC binary can be created"""
914 data = self._DoReadFile('43_intel-cmc.dts')
915 self.assertEqual(CMC_DATA, data[:len(CMC_DATA)])
917 def testPackVbt(self):
918 """Test that an image with a VBT binary can be created"""
919 data = self._DoReadFile('46_intel-vbt.dts')
920 self.assertEqual(VBT_DATA, data[:len(VBT_DATA)])
922 def testSplBssPad(self):
923 """Test that we can pad SPL's BSS with zeros"""
924 # ELF file with a '__bss_size' symbol
925 with open(self.TestFile('bss_data')) as fd:
926 TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
927 data = self._DoReadFile('47_spl_bss_pad.dts')
928 self.assertEqual(U_BOOT_SPL_DATA + (chr(0) * 10) + U_BOOT_DATA, data)
930 with open(self.TestFile('u_boot_ucode_ptr')) as fd:
931 TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
932 with self.assertRaises(ValueError) as e:
933 data = self._DoReadFile('47_spl_bss_pad.dts')
934 self.assertIn('Expected __bss_size symbol in spl/u-boot-spl',
937 def testPackStart16Spl(self):
938 """Test that an image with an x86 start16 region can be created"""
939 data = self._DoReadFile('48_x86-start16-spl.dts')
940 self.assertEqual(X86_START16_SPL_DATA, data[:len(X86_START16_SPL_DATA)])
942 def _PackUbootSplMicrocode(self, dts, ucode_second=False):
943 """Helper function for microcode tests
945 We expect to see the following in the image, in order:
946 u-boot-spl-nodtb.bin with a microcode pointer inserted at the
948 u-boot.dtb with the microcode removed
952 dts: Device tree file to use for test
953 ucode_second: True if the microsecond entry is second instead of
956 # ELF file with a '_dt_ucode_base_size' symbol
957 with open(self.TestFile('u_boot_ucode_ptr')) as fd:
958 TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
959 first, pos_and_size = self._RunMicrocodeTest(dts, U_BOOT_SPL_NODTB_DATA,
960 ucode_second=ucode_second)
961 self.assertEqual('splnodtb with microc' + pos_and_size +
962 'ter somewhere in here', first)
964 def testPackUbootSplMicrocode(self):
965 """Test that x86 microcode can be handled correctly in SPL"""
966 self._PackUbootSplMicrocode('49_x86_ucode_spl.dts')
968 def testPackUbootSplMicrocodeReorder(self):
969 """Test that order doesn't matter for microcode entries
971 This is the same as testPackUbootSplMicrocode but when we process the
972 u-boot-ucode entry we have not yet seen the u-boot-dtb-with-ucode
973 entry, so we reply on binman to try later.
975 self._PackUbootSplMicrocode('58_x86_ucode_spl_needs_retry.dts',
978 def testPackMrc(self):
979 """Test that an image with an MRC binary can be created"""
980 data = self._DoReadFile('50_intel_mrc.dts')
981 self.assertEqual(MRC_DATA, data[:len(MRC_DATA)])
983 def testSplDtb(self):
984 """Test that an image with spl/u-boot-spl.dtb can be created"""
985 data = self._DoReadFile('51_u_boot_spl_dtb.dts')
986 self.assertEqual(U_BOOT_SPL_DTB_DATA, data[:len(U_BOOT_SPL_DTB_DATA)])
988 def testSplNoDtb(self):
989 """Test that an image with spl/u-boot-spl-nodtb.bin can be created"""
990 data = self._DoReadFile('52_u_boot_spl_nodtb.dts')
991 self.assertEqual(U_BOOT_SPL_NODTB_DATA, data[:len(U_BOOT_SPL_NODTB_DATA)])
993 def testSymbols(self):
994 """Test binman can assign symbols embedded in U-Boot"""
995 elf_fname = self.TestFile('u_boot_binman_syms')
996 syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
997 addr = elf.GetSymbolAddress(elf_fname, '__image_copy_start')
998 self.assertEqual(syms['_binman_u_boot_spl_prop_offset'].address, addr)
1000 with open(self.TestFile('u_boot_binman_syms')) as fd:
1001 TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
1002 data = self._DoReadFile('53_symbols.dts')
1003 sym_values = struct.pack('<LQL', 0x24 + 0, 0x24 + 24, 0x24 + 20)
1004 expected = (sym_values + U_BOOT_SPL_DATA[16:] + chr(0xff) +
1006 sym_values + U_BOOT_SPL_DATA[16:])
1007 self.assertEqual(expected, data)
1009 def testPackUnitAddress(self):
1010 """Test that we support multiple binaries with the same name"""
1011 data = self._DoReadFile('54_unit_address.dts')
1012 self.assertEqual(U_BOOT_DATA + U_BOOT_DATA, data)
1014 def testSections(self):
1015 """Basic test of sections"""
1016 data = self._DoReadFile('55_sections.dts')
1017 expected = (U_BOOT_DATA + '!' * 12 + U_BOOT_DATA + 'a' * 12 +
1018 U_BOOT_DATA + '&' * 4)
1019 self.assertEqual(expected, data)
1022 """Tests outputting a map of the images"""
1023 _, _, map_data, _ = self._DoReadFileDtb('55_sections.dts', map=True)
1024 self.assertEqual(''' Offset Size Name
1025 00000000 00000028 main-section
1026 00000000 00000010 section@0
1027 00000000 00000004 u-boot
1028 00000010 00000010 section@1
1029 00000000 00000004 u-boot
1030 00000020 00000004 section@2
1031 00000000 00000004 u-boot
1034 def testNamePrefix(self):
1035 """Tests that name prefixes are used"""
1036 _, _, map_data, _ = self._DoReadFileDtb('56_name_prefix.dts', map=True)
1037 self.assertEqual(''' Offset Size Name
1038 00000000 00000028 main-section
1039 00000000 00000010 section@0
1040 00000000 00000004 ro-u-boot
1041 00000010 00000010 section@1
1042 00000000 00000004 rw-u-boot
1045 def testUnknownContents(self):
1046 """Test that obtaining the contents works as expected"""
1047 with self.assertRaises(ValueError) as e:
1048 self._DoReadFile('57_unknown_contents.dts', True)
1049 self.assertIn("Section '/binman': Internal error: Could not complete "
1050 "processing of contents: remaining [<_testing.Entry__testing ",
1053 def testBadChangeSize(self):
1054 """Test that trying to change the size of an entry fails"""
1055 with self.assertRaises(ValueError) as e:
1056 self._DoReadFile('59_change_size.dts', True)
1057 self.assertIn("Node '/binman/_testing': Cannot update entry size from "
1058 '2 to 1', str(e.exception))
1060 def testUpdateFdt(self):
1061 """Test that we can update the device tree with offset/size info"""
1062 _, _, _, out_dtb_fname = self._DoReadFileDtb('60_fdt_update.dts',
1064 props = self._GetPropTree(out_dtb_fname, ['offset', 'size',
1066 with open('/tmp/x.dtb', 'wb') as outf:
1067 with open(out_dtb_fname) as inf:
1068 outf.write(inf.read())
1072 '_testing:offset': 32,
1074 '_testing:image-pos': 32,
1075 'section@0/u-boot:offset': 0,
1076 'section@0/u-boot:size': len(U_BOOT_DATA),
1077 'section@0/u-boot:image-pos': 0,
1078 'section@0:offset': 0,
1079 'section@0:size': 16,
1080 'section@0:image-pos': 0,
1082 'section@1/u-boot:offset': 0,
1083 'section@1/u-boot:size': len(U_BOOT_DATA),
1084 'section@1/u-boot:image-pos': 16,
1085 'section@1:offset': 16,
1086 'section@1:size': 16,
1087 'section@1:image-pos': 16,
1091 def testUpdateFdtBad(self):
1092 """Test that we detect when ProcessFdt never completes"""
1093 with self.assertRaises(ValueError) as e:
1094 self._DoReadFileDtb('61_fdt_update_bad.dts', update_dtb=True)
1095 self.assertIn('Could not complete processing of Fdt: remaining '
1096 '[<_testing.Entry__testing', str(e.exception))
1098 def testEntryArgs(self):
1099 """Test passing arguments to entries from the command line"""
1101 'test-str-arg': 'test1',
1102 'test-int-arg': '456',
1104 self._DoReadFileDtb('62_entry_args.dts', entry_args=entry_args)
1105 self.assertIn('image', control.images)
1106 entry = control.images['image'].GetEntries()['_testing']
1107 self.assertEqual('test0', entry.test_str_fdt)
1108 self.assertEqual('test1', entry.test_str_arg)
1109 self.assertEqual(123, entry.test_int_fdt)
1110 self.assertEqual(456, entry.test_int_arg)
1112 def testEntryArgsMissing(self):
1113 """Test missing arguments and properties"""
1115 'test-int-arg': '456',
1117 self._DoReadFileDtb('63_entry_args_missing.dts', entry_args=entry_args)
1118 entry = control.images['image'].GetEntries()['_testing']
1119 self.assertEqual('test0', entry.test_str_fdt)
1120 self.assertEqual(None, entry.test_str_arg)
1121 self.assertEqual(None, entry.test_int_fdt)
1122 self.assertEqual(456, entry.test_int_arg)
1124 def testEntryArgsRequired(self):
1125 """Test missing arguments and properties"""
1127 'test-int-arg': '456',
1129 with self.assertRaises(ValueError) as e:
1130 self._DoReadFileDtb('64_entry_args_required.dts')
1131 self.assertIn("Node '/binman/_testing': Missing required "
1132 'properties/entry args: test-str-arg, test-int-fdt, test-int-arg',
1135 def testEntryArgsInvalidFormat(self):
1136 """Test that an invalid entry-argument format is detected"""
1137 args = ['-d', self.TestFile('64_entry_args_required.dts'), '-ano-value']
1138 with self.assertRaises(ValueError) as e:
1139 self._DoBinman(*args)
1140 self.assertIn("Invalid entry arguemnt 'no-value'", str(e.exception))
1142 def testEntryArgsInvalidInteger(self):
1143 """Test that an invalid entry-argument integer is detected"""
1145 'test-int-arg': 'abc',
1147 with self.assertRaises(ValueError) as e:
1148 self._DoReadFileDtb('62_entry_args.dts', entry_args=entry_args)
1149 self.assertIn("Node '/binman/_testing': Cannot convert entry arg "
1150 "'test-int-arg' (value 'abc') to integer",
1153 def testEntryArgsInvalidDatatype(self):
1154 """Test that an invalid entry-argument datatype is detected
1156 This test could be written in entry_test.py except that it needs
1157 access to control.entry_args, which seems more than that module should
1161 'test-bad-datatype-arg': '12',
1163 with self.assertRaises(ValueError) as e:
1164 self._DoReadFileDtb('65_entry_args_unknown_datatype.dts',
1165 entry_args=entry_args)
1166 self.assertIn('GetArg() internal error: Unknown data type ',
1170 """Test for a text entry type"""
1172 'test-id': TEXT_DATA,
1173 'test-id2': TEXT_DATA2,
1174 'test-id3': TEXT_DATA3,
1176 data, _, _, _ = self._DoReadFileDtb('66_text.dts',
1177 entry_args=entry_args)
1178 expected = (TEXT_DATA + chr(0) * (8 - len(TEXT_DATA)) + TEXT_DATA2 +
1179 TEXT_DATA3 + 'some text')
1180 self.assertEqual(expected, data)
1182 def testEntryDocs(self):
1183 """Test for creation of entry documentation"""
1184 with test_util.capture_sys_output() as (stdout, stderr):
1185 control.WriteEntryDocs(binman.GetEntryModules())
1186 self.assertTrue(len(stdout.getvalue()) > 0)
1188 def testEntryDocsMissing(self):
1189 """Test handling of missing entry documentation"""
1190 with self.assertRaises(ValueError) as e:
1191 with test_util.capture_sys_output() as (stdout, stderr):
1192 control.WriteEntryDocs(binman.GetEntryModules(), 'u_boot')
1193 self.assertIn('Documentation is missing for modules: u_boot',
1197 """Basic test of generation of a flashrom fmap"""
1198 data = self._DoReadFile('67_fmap.dts')
1199 fhdr, fentries = fmap_util.DecodeFmap(data[32:])
1200 expected = U_BOOT_DATA + '!' * 12 + U_BOOT_DATA + 'a' * 12
1201 self.assertEqual(expected, data[:32])
1202 self.assertEqual('__FMAP__', fhdr.signature)
1203 self.assertEqual(1, fhdr.ver_major)
1204 self.assertEqual(0, fhdr.ver_minor)
1205 self.assertEqual(0, fhdr.base)
1206 self.assertEqual(16 + 16 +
1207 fmap_util.FMAP_HEADER_LEN +
1208 fmap_util.FMAP_AREA_LEN * 3, fhdr.image_size)
1209 self.assertEqual('FMAP', fhdr.name)
1210 self.assertEqual(3, fhdr.nareas)
1211 for fentry in fentries:
1212 self.assertEqual(0, fentry.flags)
1214 self.assertEqual(0, fentries[0].offset)
1215 self.assertEqual(4, fentries[0].size)
1216 self.assertEqual('RO_U_BOOT', fentries[0].name)
1218 self.assertEqual(16, fentries[1].offset)
1219 self.assertEqual(4, fentries[1].size)
1220 self.assertEqual('RW_U_BOOT', fentries[1].name)
1222 self.assertEqual(32, fentries[2].offset)
1223 self.assertEqual(fmap_util.FMAP_HEADER_LEN +
1224 fmap_util.FMAP_AREA_LEN * 3, fentries[2].size)
1225 self.assertEqual('FMAP', fentries[2].name)
1228 if __name__ == "__main__":