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
10 from optparse import OptionParser
31 # Contents of test files, corresponding to different entry types
33 U_BOOT_IMG_DATA = 'img'
34 U_BOOT_SPL_DATA = '56780123456789abcde'
35 U_BOOT_TPL_DATA = 'tpl'
39 U_BOOT_DTB_DATA = 'udtb'
40 U_BOOT_SPL_DTB_DATA = 'spldtb'
41 U_BOOT_TPL_DTB_DATA = 'tpldtb'
42 X86_START16_DATA = 'start16'
43 X86_START16_SPL_DATA = 'start16spl'
44 X86_START16_TPL_DATA = 'start16tpl'
45 U_BOOT_NODTB_DATA = 'nodtb with microcode pointer somewhere in here'
46 U_BOOT_SPL_NODTB_DATA = 'splnodtb with microcode pointer somewhere in here'
54 CROS_EC_RW_DATA = 'ecrw'
58 FILES_DATA = ("sorry I'm late\nOh, don't bother apologising, I'm " +
59 "sorry you're alive\n")
60 COMPRESS_DATA = 'data to compress'
63 class TestFunctional(unittest.TestCase):
64 """Functional tests for binman
66 Most of these use a sample .dts file to build an image and then check
67 that it looks correct. The sample files are in the test/ subdirectory
70 For each entry type a very small test file is created using fixed
71 string contents. This makes it easy to test that things look right, and
74 In some cases a 'real' file must be used - these are also supplied in
82 # Handle the case where argv[0] is 'python'
83 self._binman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
84 self._binman_pathname = os.path.join(self._binman_dir, 'binman')
86 # Create a temporary directory for input files
87 self._indir = tempfile.mkdtemp(prefix='binmant.')
89 # Create some test files
90 TestFunctional._MakeInputFile('u-boot.bin', U_BOOT_DATA)
91 TestFunctional._MakeInputFile('u-boot.img', U_BOOT_IMG_DATA)
92 TestFunctional._MakeInputFile('spl/u-boot-spl.bin', U_BOOT_SPL_DATA)
93 TestFunctional._MakeInputFile('tpl/u-boot-tpl.bin', U_BOOT_TPL_DATA)
94 TestFunctional._MakeInputFile('blobfile', BLOB_DATA)
95 TestFunctional._MakeInputFile('me.bin', ME_DATA)
96 TestFunctional._MakeInputFile('vga.bin', VGA_DATA)
98 TestFunctional._MakeInputFile('u-boot-x86-16bit.bin', X86_START16_DATA)
99 TestFunctional._MakeInputFile('spl/u-boot-x86-16bit-spl.bin',
100 X86_START16_SPL_DATA)
101 TestFunctional._MakeInputFile('tpl/u-boot-x86-16bit-tpl.bin',
102 X86_START16_TPL_DATA)
103 TestFunctional._MakeInputFile('u-boot-nodtb.bin', U_BOOT_NODTB_DATA)
104 TestFunctional._MakeInputFile('spl/u-boot-spl-nodtb.bin',
105 U_BOOT_SPL_NODTB_DATA)
106 TestFunctional._MakeInputFile('fsp.bin', FSP_DATA)
107 TestFunctional._MakeInputFile('cmc.bin', CMC_DATA)
108 TestFunctional._MakeInputFile('vbt.bin', VBT_DATA)
109 TestFunctional._MakeInputFile('mrc.bin', MRC_DATA)
110 TestFunctional._MakeInputFile('ecrw.bin', CROS_EC_RW_DATA)
111 TestFunctional._MakeInputDir('devkeys')
112 TestFunctional._MakeInputFile('bmpblk.bin', BMPBLK_DATA)
113 self._output_setup = False
115 # ELF file with a '_dt_ucode_base_size' symbol
116 with open(self.TestFile('u_boot_ucode_ptr')) as fd:
117 TestFunctional._MakeInputFile('u-boot', fd.read())
119 # Intel flash descriptor file
120 with open(self.TestFile('descriptor.bin')) as fd:
121 TestFunctional._MakeInputFile('descriptor.bin', fd.read())
123 shutil.copytree(self.TestFile('files'),
124 os.path.join(self._indir, 'files'))
126 TestFunctional._MakeInputFile('compress', COMPRESS_DATA)
129 def tearDownClass(self):
130 """Remove the temporary input directory and its contents"""
132 shutil.rmtree(self._indir)
136 # Enable this to turn on debugging output
137 # tout.Init(tout.DEBUG)
138 command.test_result = None
141 """Remove the temporary output directory"""
142 tools._FinaliseForTest()
145 def _ResetDtbs(self):
146 TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA)
147 TestFunctional._MakeInputFile('spl/u-boot-spl.dtb', U_BOOT_SPL_DTB_DATA)
148 TestFunctional._MakeInputFile('tpl/u-boot-tpl.dtb', U_BOOT_TPL_DTB_DATA)
150 def _RunBinman(self, *args, **kwargs):
151 """Run binman using the command line
154 Arguments to pass, as a list of strings
155 kwargs: Arguments to pass to Command.RunPipe()
157 result = command.RunPipe([[self._binman_pathname] + list(args)],
158 capture=True, capture_stderr=True, raise_on_error=False)
159 if result.return_code and kwargs.get('raise_on_error', True):
160 raise Exception("Error running '%s': %s" % (' '.join(args),
161 result.stdout + result.stderr))
164 def _DoBinman(self, *args):
165 """Run binman using directly (in the same process)
168 Arguments to pass, as a list of strings
170 Return value (0 for success)
175 (options, args) = cmdline.ParseArgs(args)
176 options.pager = 'binman-invalid-pager'
177 options.build_dir = self._indir
179 # For testing, you can force an increase in verbosity here
180 # options.verbosity = tout.DEBUG
181 return control.Binman(options, args)
183 def _DoTestFile(self, fname, debug=False, map=False, update_dtb=False,
184 entry_args=None, images=None, use_real_dtb=False):
185 """Run binman with a given test file
188 fname: Device-tree source filename to use (e.g. 05_simple.dts)
189 debug: True to enable debugging output
190 map: True to output map files for the images
191 update_dtb: Update the offset and size of each entry in the device
192 tree before packing it into the image
193 entry_args: Dict of entry args to supply to binman
195 value: value of that arg
196 images: List of image names to build
198 args = ['-p', '-I', self._indir, '-d', self.TestFile(fname)]
206 args.append('--fake-dtb')
208 for arg, value in entry_args.iteritems():
209 args.append('-a%s=%s' % (arg, value))
212 args += ['-i', image]
213 return self._DoBinman(*args)
215 def _SetupDtb(self, fname, outfile='u-boot.dtb'):
216 """Set up a new test device-tree file
218 The given file is compiled and set up as the device tree to be used
222 fname: Filename of .dts file to read
223 outfile: Output filename for compiled device-tree binary
226 Contents of device-tree binary
228 if not self._output_setup:
229 tools.PrepareOutputDir(self._indir, True)
230 self._output_setup = True
231 dtb = fdt_util.EnsureCompiled(self.TestFile(fname))
232 with open(dtb) as fd:
234 TestFunctional._MakeInputFile(outfile, data)
237 def _GetDtbContentsForSplTpl(self, dtb_data, name):
238 """Create a version of the main DTB for SPL or SPL
240 For testing we don't actually have different versions of the DTB. With
241 U-Boot we normally run fdtgrep to remove unwanted nodes, but for tests
242 we don't normally have any unwanted nodes.
244 We still want the DTBs for SPL and TPL to be different though, since
245 otherwise it is confusing to know which one we are looking at. So add
246 an 'spl' or 'tpl' property to the top-level node.
248 dtb = fdt.Fdt.FromData(dtb_data)
250 dtb.GetNode('/binman').AddZeroProp(name)
251 dtb.Sync(auto_resize=True)
253 return dtb.GetContents()
255 def _DoReadFileDtb(self, fname, use_real_dtb=False, map=False,
256 update_dtb=False, entry_args=None, reset_dtbs=True):
257 """Run binman and return the resulting image
259 This runs binman with a given test file and then reads the resulting
260 output file. It is a shortcut function since most tests need to do
263 Raises an assertion failure if binman returns a non-zero exit code.
266 fname: Device-tree source filename to use (e.g. 05_simple.dts)
267 use_real_dtb: True to use the test file as the contents of
268 the u-boot-dtb entry. Normally this is not needed and the
269 test contents (the U_BOOT_DTB_DATA string) can be used.
270 But in some test we need the real contents.
271 map: True to output map files for the images
272 update_dtb: Update the offset and size of each entry in the device
273 tree before packing it into the image
277 Resulting image contents
279 Map data showing contents of image (or None if none)
280 Output device tree binary filename ('u-boot.dtb' path)
283 # Use the compiled test file as the u-boot-dtb input
285 dtb_data = self._SetupDtb(fname)
286 infile = os.path.join(self._indir, 'u-boot.dtb')
288 # For testing purposes, make a copy of the DT for SPL and TPL. Add
289 # a node indicating which it is, so aid verification.
290 for name in ['spl', 'tpl']:
291 dtb_fname = '%s/u-boot-%s.dtb' % (name, name)
292 outfile = os.path.join(self._indir, dtb_fname)
293 TestFunctional._MakeInputFile(dtb_fname,
294 self._GetDtbContentsForSplTpl(dtb_data, name))
297 retcode = self._DoTestFile(fname, map=map, update_dtb=update_dtb,
298 entry_args=entry_args, use_real_dtb=use_real_dtb)
299 self.assertEqual(0, retcode)
300 out_dtb_fname = tools.GetOutputFilename('u-boot.dtb.out')
302 # Find the (only) image, read it and return its contents
303 image = control.images['image']
304 image_fname = tools.GetOutputFilename('image.bin')
305 self.assertTrue(os.path.exists(image_fname))
307 map_fname = tools.GetOutputFilename('image.map')
308 with open(map_fname) as fd:
312 with open(image_fname) as fd:
313 return fd.read(), dtb_data, map_data, out_dtb_fname
315 # Put the test file back
316 if reset_dtbs and use_real_dtb:
319 def _DoReadFile(self, fname, use_real_dtb=False):
320 """Helper function which discards the device-tree binary
323 fname: Device-tree source filename to use (e.g. 05_simple.dts)
324 use_real_dtb: True to use the test file as the contents of
325 the u-boot-dtb entry. Normally this is not needed and the
326 test contents (the U_BOOT_DTB_DATA string) can be used.
327 But in some test we need the real contents.
330 Resulting image contents
332 return self._DoReadFileDtb(fname, use_real_dtb)[0]
335 def _MakeInputFile(self, fname, contents):
336 """Create a new test input file, creating directories as needed
339 fname: Filename to create
340 contents: File contents to write in to the file
342 Full pathname of file created
344 pathname = os.path.join(self._indir, fname)
345 dirname = os.path.dirname(pathname)
346 if dirname and not os.path.exists(dirname):
348 with open(pathname, 'wb') as fd:
353 def _MakeInputDir(self, dirname):
354 """Create a new test input directory, creating directories as needed
357 dirname: Directory name to create
360 Full pathname of directory created
362 pathname = os.path.join(self._indir, dirname)
363 if not os.path.exists(pathname):
364 os.makedirs(pathname)
368 def TestFile(self, fname):
369 return os.path.join(self._binman_dir, 'test', fname)
371 def AssertInList(self, grep_list, target):
372 """Assert that at least one of a list of things is in a target
375 grep_list: List of strings to check
376 target: Target string
378 for grep in grep_list:
381 self.fail("Error: '%' not found in '%s'" % (grep_list, target))
383 def CheckNoGaps(self, entries):
384 """Check that all entries fit together without gaps
387 entries: List of entries to check
390 for entry in entries.values():
391 self.assertEqual(offset, entry.offset)
394 def GetFdtLen(self, dtb):
395 """Get the totalsize field from a device-tree binary
398 dtb: Device-tree binary contents
401 Total size of device-tree binary, from the header
403 return struct.unpack('>L', dtb[4:8])[0]
405 def _GetPropTree(self, dtb, prop_names):
406 def AddNode(node, path):
408 path += '/' + node.name
409 for subnode in node.subnodes:
410 for prop in subnode.props.values():
411 if prop.name in prop_names:
412 prop_path = path + '/' + subnode.name + ':' + prop.name
413 tree[prop_path[len('/binman/'):]] = fdt_util.fdt32_to_cpu(
415 AddNode(subnode, path)
418 AddNode(dtb.GetRoot(), '')
422 """Test a basic run with valid args"""
423 result = self._RunBinman('-h')
425 def testFullHelp(self):
426 """Test that the full help is displayed with -H"""
427 result = self._RunBinman('-H')
428 help_file = os.path.join(self._binman_dir, 'README')
429 # Remove possible extraneous strings
430 extra = '::::::::::::::\n' + help_file + '\n::::::::::::::\n'
431 gothelp = result.stdout.replace(extra, '')
432 self.assertEqual(len(gothelp), os.path.getsize(help_file))
433 self.assertEqual(0, len(result.stderr))
434 self.assertEqual(0, result.return_code)
436 def testFullHelpInternal(self):
437 """Test that the full help is displayed with -H"""
439 command.test_result = command.CommandResult()
440 result = self._DoBinman('-H')
441 help_file = os.path.join(self._binman_dir, 'README')
443 command.test_result = None
446 """Test that the basic help is displayed with -h"""
447 result = self._RunBinman('-h')
448 self.assertTrue(len(result.stdout) > 200)
449 self.assertEqual(0, len(result.stderr))
450 self.assertEqual(0, result.return_code)
453 """Test that we can run it with a specific board"""
454 self._SetupDtb('05_simple.dts', 'sandbox/u-boot.dtb')
455 TestFunctional._MakeInputFile('sandbox/u-boot.bin', U_BOOT_DATA)
456 result = self._DoBinman('-b', 'sandbox')
457 self.assertEqual(0, result)
459 def testNeedBoard(self):
460 """Test that we get an error when no board ius supplied"""
461 with self.assertRaises(ValueError) as e:
462 result = self._DoBinman()
463 self.assertIn("Must provide a board to process (use -b <board>)",
466 def testMissingDt(self):
467 """Test that an invalid device-tree file generates an error"""
468 with self.assertRaises(Exception) as e:
469 self._RunBinman('-d', 'missing_file')
470 # We get one error from libfdt, and a different one from fdtget.
471 self.AssertInList(["Couldn't open blob from 'missing_file'",
472 'No such file or directory'], str(e.exception))
474 def testBrokenDt(self):
475 """Test that an invalid device-tree source file generates an error
477 Since this is a source file it should be compiled and the error
478 will come from the device-tree compiler (dtc).
480 with self.assertRaises(Exception) as e:
481 self._RunBinman('-d', self.TestFile('01_invalid.dts'))
482 self.assertIn("FATAL ERROR: Unable to parse input tree",
485 def testMissingNode(self):
486 """Test that a device tree without a 'binman' node generates an error"""
487 with self.assertRaises(Exception) as e:
488 self._DoBinman('-d', self.TestFile('02_missing_node.dts'))
489 self.assertIn("does not have a 'binman' node", str(e.exception))
492 """Test that an empty binman node works OK (i.e. does nothing)"""
493 result = self._RunBinman('-d', self.TestFile('03_empty.dts'))
494 self.assertEqual(0, len(result.stderr))
495 self.assertEqual(0, result.return_code)
497 def testInvalidEntry(self):
498 """Test that an invalid entry is flagged"""
499 with self.assertRaises(Exception) as e:
500 result = self._RunBinman('-d',
501 self.TestFile('04_invalid_entry.dts'))
502 self.assertIn("Unknown entry type 'not-a-valid-type' in node "
503 "'/binman/not-a-valid-type'", str(e.exception))
505 def testSimple(self):
506 """Test a simple binman with a single file"""
507 data = self._DoReadFile('05_simple.dts')
508 self.assertEqual(U_BOOT_DATA, data)
510 def testSimpleDebug(self):
511 """Test a simple binman run with debugging enabled"""
512 data = self._DoTestFile('05_simple.dts', debug=True)
515 """Test that we can handle creating two images
517 This also tests image padding.
519 retcode = self._DoTestFile('06_dual_image.dts')
520 self.assertEqual(0, retcode)
522 image = control.images['image1']
523 self.assertEqual(len(U_BOOT_DATA), image._size)
524 fname = tools.GetOutputFilename('image1.bin')
525 self.assertTrue(os.path.exists(fname))
526 with open(fname) as fd:
528 self.assertEqual(U_BOOT_DATA, data)
530 image = control.images['image2']
531 self.assertEqual(3 + len(U_BOOT_DATA) + 5, image._size)
532 fname = tools.GetOutputFilename('image2.bin')
533 self.assertTrue(os.path.exists(fname))
534 with open(fname) as fd:
536 self.assertEqual(U_BOOT_DATA, data[3:7])
537 self.assertEqual(chr(0) * 3, data[:3])
538 self.assertEqual(chr(0) * 5, data[7:])
540 def testBadAlign(self):
541 """Test that an invalid alignment value is detected"""
542 with self.assertRaises(ValueError) as e:
543 self._DoTestFile('07_bad_align.dts')
544 self.assertIn("Node '/binman/u-boot': Alignment 23 must be a power "
545 "of two", str(e.exception))
547 def testPackSimple(self):
548 """Test that packing works as expected"""
549 retcode = self._DoTestFile('08_pack.dts')
550 self.assertEqual(0, retcode)
551 self.assertIn('image', control.images)
552 image = control.images['image']
553 entries = image.GetEntries()
554 self.assertEqual(5, len(entries))
557 self.assertIn('u-boot', entries)
558 entry = entries['u-boot']
559 self.assertEqual(0, entry.offset)
560 self.assertEqual(len(U_BOOT_DATA), entry.size)
562 # Second u-boot, aligned to 16-byte boundary
563 self.assertIn('u-boot-align', entries)
564 entry = entries['u-boot-align']
565 self.assertEqual(16, entry.offset)
566 self.assertEqual(len(U_BOOT_DATA), entry.size)
568 # Third u-boot, size 23 bytes
569 self.assertIn('u-boot-size', entries)
570 entry = entries['u-boot-size']
571 self.assertEqual(20, entry.offset)
572 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
573 self.assertEqual(23, entry.size)
575 # Fourth u-boot, placed immediate after the above
576 self.assertIn('u-boot-next', entries)
577 entry = entries['u-boot-next']
578 self.assertEqual(43, entry.offset)
579 self.assertEqual(len(U_BOOT_DATA), entry.size)
581 # Fifth u-boot, placed at a fixed offset
582 self.assertIn('u-boot-fixed', entries)
583 entry = entries['u-boot-fixed']
584 self.assertEqual(61, entry.offset)
585 self.assertEqual(len(U_BOOT_DATA), entry.size)
587 self.assertEqual(65, image._size)
589 def testPackExtra(self):
590 """Test that extra packing feature works as expected"""
591 retcode = self._DoTestFile('09_pack_extra.dts')
593 self.assertEqual(0, retcode)
594 self.assertIn('image', control.images)
595 image = control.images['image']
596 entries = image.GetEntries()
597 self.assertEqual(5, len(entries))
599 # First u-boot with padding before and after
600 self.assertIn('u-boot', entries)
601 entry = entries['u-boot']
602 self.assertEqual(0, entry.offset)
603 self.assertEqual(3, entry.pad_before)
604 self.assertEqual(3 + 5 + len(U_BOOT_DATA), entry.size)
606 # Second u-boot has an aligned size, but it has no effect
607 self.assertIn('u-boot-align-size-nop', entries)
608 entry = entries['u-boot-align-size-nop']
609 self.assertEqual(12, entry.offset)
610 self.assertEqual(4, entry.size)
612 # Third u-boot has an aligned size too
613 self.assertIn('u-boot-align-size', entries)
614 entry = entries['u-boot-align-size']
615 self.assertEqual(16, entry.offset)
616 self.assertEqual(32, entry.size)
618 # Fourth u-boot has an aligned end
619 self.assertIn('u-boot-align-end', entries)
620 entry = entries['u-boot-align-end']
621 self.assertEqual(48, entry.offset)
622 self.assertEqual(16, entry.size)
624 # Fifth u-boot immediately afterwards
625 self.assertIn('u-boot-align-both', entries)
626 entry = entries['u-boot-align-both']
627 self.assertEqual(64, entry.offset)
628 self.assertEqual(64, entry.size)
630 self.CheckNoGaps(entries)
631 self.assertEqual(128, image._size)
633 def testPackAlignPowerOf2(self):
634 """Test that invalid entry alignment is detected"""
635 with self.assertRaises(ValueError) as e:
636 self._DoTestFile('10_pack_align_power2.dts')
637 self.assertIn("Node '/binman/u-boot': Alignment 5 must be a power "
638 "of two", str(e.exception))
640 def testPackAlignSizePowerOf2(self):
641 """Test that invalid entry size alignment is detected"""
642 with self.assertRaises(ValueError) as e:
643 self._DoTestFile('11_pack_align_size_power2.dts')
644 self.assertIn("Node '/binman/u-boot': Alignment size 55 must be a "
645 "power of two", str(e.exception))
647 def testPackInvalidAlign(self):
648 """Test detection of an offset that does not match its alignment"""
649 with self.assertRaises(ValueError) as e:
650 self._DoTestFile('12_pack_inv_align.dts')
651 self.assertIn("Node '/binman/u-boot': Offset 0x5 (5) does not match "
652 "align 0x4 (4)", str(e.exception))
654 def testPackInvalidSizeAlign(self):
655 """Test that invalid entry size alignment is detected"""
656 with self.assertRaises(ValueError) as e:
657 self._DoTestFile('13_pack_inv_size_align.dts')
658 self.assertIn("Node '/binman/u-boot': Size 0x5 (5) does not match "
659 "align-size 0x4 (4)", str(e.exception))
661 def testPackOverlap(self):
662 """Test that overlapping regions are detected"""
663 with self.assertRaises(ValueError) as e:
664 self._DoTestFile('14_pack_overlap.dts')
665 self.assertIn("Node '/binman/u-boot-align': Offset 0x3 (3) overlaps "
666 "with previous entry '/binman/u-boot' ending at 0x4 (4)",
669 def testPackEntryOverflow(self):
670 """Test that entries that overflow their size are detected"""
671 with self.assertRaises(ValueError) as e:
672 self._DoTestFile('15_pack_overflow.dts')
673 self.assertIn("Node '/binman/u-boot': Entry contents size is 0x4 (4) "
674 "but entry size is 0x3 (3)", str(e.exception))
676 def testPackImageOverflow(self):
677 """Test that entries which overflow the image size are detected"""
678 with self.assertRaises(ValueError) as e:
679 self._DoTestFile('16_pack_image_overflow.dts')
680 self.assertIn("Section '/binman': contents size 0x4 (4) exceeds section "
681 "size 0x3 (3)", str(e.exception))
683 def testPackImageSize(self):
684 """Test that the image size can be set"""
685 retcode = self._DoTestFile('17_pack_image_size.dts')
686 self.assertEqual(0, retcode)
687 self.assertIn('image', control.images)
688 image = control.images['image']
689 self.assertEqual(7, image._size)
691 def testPackImageSizeAlign(self):
692 """Test that image size alignemnt works as expected"""
693 retcode = self._DoTestFile('18_pack_image_align.dts')
694 self.assertEqual(0, retcode)
695 self.assertIn('image', control.images)
696 image = control.images['image']
697 self.assertEqual(16, image._size)
699 def testPackInvalidImageAlign(self):
700 """Test that invalid image alignment is detected"""
701 with self.assertRaises(ValueError) as e:
702 self._DoTestFile('19_pack_inv_image_align.dts')
703 self.assertIn("Section '/binman': Size 0x7 (7) does not match "
704 "align-size 0x8 (8)", str(e.exception))
706 def testPackAlignPowerOf2(self):
707 """Test that invalid image alignment is detected"""
708 with self.assertRaises(ValueError) as e:
709 self._DoTestFile('20_pack_inv_image_align_power2.dts')
710 self.assertIn("Section '/binman': Alignment size 131 must be a power of "
711 "two", str(e.exception))
713 def testImagePadByte(self):
714 """Test that the image pad byte can be specified"""
715 with open(self.TestFile('bss_data')) as fd:
716 TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
717 data = self._DoReadFile('21_image_pad.dts')
718 self.assertEqual(U_BOOT_SPL_DATA + (chr(0xff) * 1) + U_BOOT_DATA, data)
720 def testImageName(self):
721 """Test that image files can be named"""
722 retcode = self._DoTestFile('22_image_name.dts')
723 self.assertEqual(0, retcode)
724 image = control.images['image1']
725 fname = tools.GetOutputFilename('test-name')
726 self.assertTrue(os.path.exists(fname))
728 image = control.images['image2']
729 fname = tools.GetOutputFilename('test-name.xx')
730 self.assertTrue(os.path.exists(fname))
732 def testBlobFilename(self):
733 """Test that generic blobs can be provided by filename"""
734 data = self._DoReadFile('23_blob.dts')
735 self.assertEqual(BLOB_DATA, data)
737 def testPackSorted(self):
738 """Test that entries can be sorted"""
739 data = self._DoReadFile('24_sorted.dts')
740 self.assertEqual(chr(0) * 1 + U_BOOT_SPL_DATA + chr(0) * 2 +
743 def testPackZeroOffset(self):
744 """Test that an entry at offset 0 is not given a new offset"""
745 with self.assertRaises(ValueError) as e:
746 self._DoTestFile('25_pack_zero_size.dts')
747 self.assertIn("Node '/binman/u-boot-spl': Offset 0x0 (0) overlaps "
748 "with previous entry '/binman/u-boot' ending at 0x4 (4)",
751 def testPackUbootDtb(self):
752 """Test that a device tree can be added to U-Boot"""
753 data = self._DoReadFile('26_pack_u_boot_dtb.dts')
754 self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA, data)
756 def testPackX86RomNoSize(self):
757 """Test that the end-at-4gb property requires a size property"""
758 with self.assertRaises(ValueError) as e:
759 self._DoTestFile('27_pack_4gb_no_size.dts')
760 self.assertIn("Section '/binman': Section size must be provided when "
761 "using end-at-4gb", str(e.exception))
763 def testPackX86RomOutside(self):
764 """Test that the end-at-4gb property checks for offset boundaries"""
765 with self.assertRaises(ValueError) as e:
766 self._DoTestFile('28_pack_4gb_outside.dts')
767 self.assertIn("Node '/binman/u-boot': Offset 0x0 (0) is outside "
768 "the section starting at 0xffffffe0 (4294967264)",
771 def testPackX86Rom(self):
772 """Test that a basic x86 ROM can be created"""
773 data = self._DoReadFile('29_x86-rom.dts')
774 self.assertEqual(U_BOOT_DATA + chr(0) * 7 + U_BOOT_SPL_DATA +
777 def testPackX86RomMeNoDesc(self):
778 """Test that an invalid Intel descriptor entry is detected"""
779 TestFunctional._MakeInputFile('descriptor.bin', '')
780 with self.assertRaises(ValueError) as e:
781 self._DoTestFile('31_x86-rom-me.dts')
782 self.assertIn("Node '/binman/intel-descriptor': Cannot find FD "
783 "signature", str(e.exception))
785 def testPackX86RomBadDesc(self):
786 """Test that the Intel requires a descriptor entry"""
787 with self.assertRaises(ValueError) as e:
788 self._DoTestFile('30_x86-rom-me-no-desc.dts')
789 self.assertIn("Node '/binman/intel-me': No offset set with "
790 "offset-unset: should another entry provide this correct "
791 "offset?", str(e.exception))
793 def testPackX86RomMe(self):
794 """Test that an x86 ROM with an ME region can be created"""
795 data = self._DoReadFile('31_x86-rom-me.dts')
796 self.assertEqual(ME_DATA, data[0x1000:0x1000 + len(ME_DATA)])
798 def testPackVga(self):
799 """Test that an image with a VGA binary can be created"""
800 data = self._DoReadFile('32_intel-vga.dts')
801 self.assertEqual(VGA_DATA, data[:len(VGA_DATA)])
803 def testPackStart16(self):
804 """Test that an image with an x86 start16 region can be created"""
805 data = self._DoReadFile('33_x86-start16.dts')
806 self.assertEqual(X86_START16_DATA, data[:len(X86_START16_DATA)])
808 def _RunMicrocodeTest(self, dts_fname, nodtb_data, ucode_second=False):
809 """Handle running a test for insertion of microcode
812 dts_fname: Name of test .dts file
813 nodtb_data: Data that we expect in the first section
814 ucode_second: True if the microsecond entry is second instead of
819 Contents of first region (U-Boot or SPL)
820 Offset and size components of microcode pointer, as inserted
821 in the above (two 4-byte words)
823 data = self._DoReadFile(dts_fname, True)
825 # Now check the device tree has no microcode
827 ucode_content = data[len(nodtb_data):]
828 ucode_pos = len(nodtb_data)
829 dtb_with_ucode = ucode_content[16:]
830 fdt_len = self.GetFdtLen(dtb_with_ucode)
832 dtb_with_ucode = data[len(nodtb_data):]
833 fdt_len = self.GetFdtLen(dtb_with_ucode)
834 ucode_content = dtb_with_ucode[fdt_len:]
835 ucode_pos = len(nodtb_data) + fdt_len
836 fname = tools.GetOutputFilename('test.dtb')
837 with open(fname, 'wb') as fd:
838 fd.write(dtb_with_ucode)
839 dtb = fdt.FdtScan(fname)
840 ucode = dtb.GetNode('/microcode')
841 self.assertTrue(ucode)
842 for node in ucode.subnodes:
843 self.assertFalse(node.props.get('data'))
845 # Check that the microcode appears immediately after the Fdt
846 # This matches the concatenation of the data properties in
847 # the /microcode/update@xxx nodes in 34_x86_ucode.dts.
848 ucode_data = struct.pack('>4L', 0x12345678, 0x12345679, 0xabcd0000,
850 self.assertEqual(ucode_data, ucode_content[:len(ucode_data)])
852 # Check that the microcode pointer was inserted. It should match the
853 # expected offset and size
854 pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
856 u_boot = data[:len(nodtb_data)]
857 return u_boot, pos_and_size
859 def testPackUbootMicrocode(self):
860 """Test that x86 microcode can be handled correctly
862 We expect to see the following in the image, in order:
863 u-boot-nodtb.bin with a microcode pointer inserted at the correct
865 u-boot.dtb with the microcode removed
868 first, pos_and_size = self._RunMicrocodeTest('34_x86_ucode.dts',
870 self.assertEqual('nodtb with microcode' + pos_and_size +
871 ' somewhere in here', first)
873 def _RunPackUbootSingleMicrocode(self):
874 """Test that x86 microcode can be handled correctly
876 We expect to see the following in the image, in order:
877 u-boot-nodtb.bin with a microcode pointer inserted at the correct
879 u-boot.dtb with the microcode
880 an empty microcode region
882 # We need the libfdt library to run this test since only that allows
883 # finding the offset of a property. This is required by
884 # Entry_u_boot_dtb_with_ucode.ObtainContents().
885 data = self._DoReadFile('35_x86_single_ucode.dts', True)
887 second = data[len(U_BOOT_NODTB_DATA):]
889 fdt_len = self.GetFdtLen(second)
890 third = second[fdt_len:]
891 second = second[:fdt_len]
893 ucode_data = struct.pack('>2L', 0x12345678, 0x12345679)
894 self.assertIn(ucode_data, second)
895 ucode_pos = second.find(ucode_data) + len(U_BOOT_NODTB_DATA)
897 # Check that the microcode pointer was inserted. It should match the
898 # expected offset and size
899 pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
901 first = data[:len(U_BOOT_NODTB_DATA)]
902 self.assertEqual('nodtb with microcode' + pos_and_size +
903 ' somewhere in here', first)
905 def testPackUbootSingleMicrocode(self):
906 """Test that x86 microcode can be handled correctly with fdt_normal.
908 self._RunPackUbootSingleMicrocode()
910 def testUBootImg(self):
911 """Test that u-boot.img can be put in a file"""
912 data = self._DoReadFile('36_u_boot_img.dts')
913 self.assertEqual(U_BOOT_IMG_DATA, data)
915 def testNoMicrocode(self):
916 """Test that a missing microcode region is detected"""
917 with self.assertRaises(ValueError) as e:
918 self._DoReadFile('37_x86_no_ucode.dts', True)
919 self.assertIn("Node '/binman/u-boot-dtb-with-ucode': No /microcode "
920 "node found in ", str(e.exception))
922 def testMicrocodeWithoutNode(self):
923 """Test that a missing u-boot-dtb-with-ucode node is detected"""
924 with self.assertRaises(ValueError) as e:
925 self._DoReadFile('38_x86_ucode_missing_node.dts', True)
926 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
927 "microcode region u-boot-dtb-with-ucode", str(e.exception))
929 def testMicrocodeWithoutNode2(self):
930 """Test that a missing u-boot-ucode node is detected"""
931 with self.assertRaises(ValueError) as e:
932 self._DoReadFile('39_x86_ucode_missing_node2.dts', True)
933 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
934 "microcode region u-boot-ucode", str(e.exception))
936 def testMicrocodeWithoutPtrInElf(self):
937 """Test that a U-Boot binary without the microcode symbol is detected"""
938 # ELF file without a '_dt_ucode_base_size' symbol
940 with open(self.TestFile('u_boot_no_ucode_ptr')) as fd:
941 TestFunctional._MakeInputFile('u-boot', fd.read())
943 with self.assertRaises(ValueError) as e:
944 self._RunPackUbootSingleMicrocode()
945 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot locate "
946 "_dt_ucode_base_size symbol in u-boot", str(e.exception))
949 # Put the original file back
950 with open(self.TestFile('u_boot_ucode_ptr')) as fd:
951 TestFunctional._MakeInputFile('u-boot', fd.read())
953 def testMicrocodeNotInImage(self):
954 """Test that microcode must be placed within the image"""
955 with self.assertRaises(ValueError) as e:
956 self._DoReadFile('40_x86_ucode_not_in_image.dts', True)
957 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Microcode "
958 "pointer _dt_ucode_base_size at fffffe14 is outside the "
959 "section ranging from 00000000 to 0000002e", str(e.exception))
961 def testWithoutMicrocode(self):
962 """Test that we can cope with an image without microcode (e.g. qemu)"""
963 with open(self.TestFile('u_boot_no_ucode_ptr')) as fd:
964 TestFunctional._MakeInputFile('u-boot', fd.read())
965 data, dtb, _, _ = self._DoReadFileDtb('44_x86_optional_ucode.dts', True)
967 # Now check the device tree has no microcode
968 self.assertEqual(U_BOOT_NODTB_DATA, data[:len(U_BOOT_NODTB_DATA)])
969 second = data[len(U_BOOT_NODTB_DATA):]
971 fdt_len = self.GetFdtLen(second)
972 self.assertEqual(dtb, second[:fdt_len])
974 used_len = len(U_BOOT_NODTB_DATA) + fdt_len
975 third = data[used_len:]
976 self.assertEqual(chr(0) * (0x200 - used_len), third)
978 def testUnknownPosSize(self):
979 """Test that microcode must be placed within the image"""
980 with self.assertRaises(ValueError) as e:
981 self._DoReadFile('41_unknown_pos_size.dts', True)
982 self.assertIn("Section '/binman': Unable to set offset/size for unknown "
983 "entry 'invalid-entry'", str(e.exception))
985 def testPackFsp(self):
986 """Test that an image with a FSP binary can be created"""
987 data = self._DoReadFile('42_intel-fsp.dts')
988 self.assertEqual(FSP_DATA, data[:len(FSP_DATA)])
990 def testPackCmc(self):
991 """Test that an image with a CMC binary can be created"""
992 data = self._DoReadFile('43_intel-cmc.dts')
993 self.assertEqual(CMC_DATA, data[:len(CMC_DATA)])
995 def testPackVbt(self):
996 """Test that an image with a VBT binary can be created"""
997 data = self._DoReadFile('46_intel-vbt.dts')
998 self.assertEqual(VBT_DATA, data[:len(VBT_DATA)])
1000 def testSplBssPad(self):
1001 """Test that we can pad SPL's BSS with zeros"""
1002 # ELF file with a '__bss_size' symbol
1003 with open(self.TestFile('bss_data')) as fd:
1004 TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
1005 data = self._DoReadFile('47_spl_bss_pad.dts')
1006 self.assertEqual(U_BOOT_SPL_DATA + (chr(0) * 10) + U_BOOT_DATA, data)
1008 with open(self.TestFile('u_boot_ucode_ptr')) as fd:
1009 TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
1010 with self.assertRaises(ValueError) as e:
1011 data = self._DoReadFile('47_spl_bss_pad.dts')
1012 self.assertIn('Expected __bss_size symbol in spl/u-boot-spl',
1015 def testPackStart16Spl(self):
1016 """Test that an image with an x86 start16 SPL region can be created"""
1017 data = self._DoReadFile('48_x86-start16-spl.dts')
1018 self.assertEqual(X86_START16_SPL_DATA, data[:len(X86_START16_SPL_DATA)])
1020 def _PackUbootSplMicrocode(self, dts, ucode_second=False):
1021 """Helper function for microcode tests
1023 We expect to see the following in the image, in order:
1024 u-boot-spl-nodtb.bin with a microcode pointer inserted at the
1026 u-boot.dtb with the microcode removed
1030 dts: Device tree file to use for test
1031 ucode_second: True if the microsecond entry is second instead of
1034 # ELF file with a '_dt_ucode_base_size' symbol
1035 with open(self.TestFile('u_boot_ucode_ptr')) as fd:
1036 TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
1037 first, pos_and_size = self._RunMicrocodeTest(dts, U_BOOT_SPL_NODTB_DATA,
1038 ucode_second=ucode_second)
1039 self.assertEqual('splnodtb with microc' + pos_and_size +
1040 'ter somewhere in here', first)
1042 def testPackUbootSplMicrocode(self):
1043 """Test that x86 microcode can be handled correctly in SPL"""
1044 self._PackUbootSplMicrocode('49_x86_ucode_spl.dts')
1046 def testPackUbootSplMicrocodeReorder(self):
1047 """Test that order doesn't matter for microcode entries
1049 This is the same as testPackUbootSplMicrocode but when we process the
1050 u-boot-ucode entry we have not yet seen the u-boot-dtb-with-ucode
1051 entry, so we reply on binman to try later.
1053 self._PackUbootSplMicrocode('58_x86_ucode_spl_needs_retry.dts',
1056 def testPackMrc(self):
1057 """Test that an image with an MRC binary can be created"""
1058 data = self._DoReadFile('50_intel_mrc.dts')
1059 self.assertEqual(MRC_DATA, data[:len(MRC_DATA)])
1061 def testSplDtb(self):
1062 """Test that an image with spl/u-boot-spl.dtb can be created"""
1063 data = self._DoReadFile('51_u_boot_spl_dtb.dts')
1064 self.assertEqual(U_BOOT_SPL_DTB_DATA, data[:len(U_BOOT_SPL_DTB_DATA)])
1066 def testSplNoDtb(self):
1067 """Test that an image with spl/u-boot-spl-nodtb.bin can be created"""
1068 data = self._DoReadFile('52_u_boot_spl_nodtb.dts')
1069 self.assertEqual(U_BOOT_SPL_NODTB_DATA, data[:len(U_BOOT_SPL_NODTB_DATA)])
1071 def testSymbols(self):
1072 """Test binman can assign symbols embedded in U-Boot"""
1073 elf_fname = self.TestFile('u_boot_binman_syms')
1074 syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
1075 addr = elf.GetSymbolAddress(elf_fname, '__image_copy_start')
1076 self.assertEqual(syms['_binman_u_boot_spl_prop_offset'].address, addr)
1078 with open(self.TestFile('u_boot_binman_syms')) as fd:
1079 TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
1080 data = self._DoReadFile('53_symbols.dts')
1081 sym_values = struct.pack('<LQL', 0x24 + 0, 0x24 + 24, 0x24 + 20)
1082 expected = (sym_values + U_BOOT_SPL_DATA[16:] + chr(0xff) +
1084 sym_values + U_BOOT_SPL_DATA[16:])
1085 self.assertEqual(expected, data)
1087 def testPackUnitAddress(self):
1088 """Test that we support multiple binaries with the same name"""
1089 data = self._DoReadFile('54_unit_address.dts')
1090 self.assertEqual(U_BOOT_DATA + U_BOOT_DATA, data)
1092 def testSections(self):
1093 """Basic test of sections"""
1094 data = self._DoReadFile('55_sections.dts')
1095 expected = (U_BOOT_DATA + '!' * 12 + U_BOOT_DATA + 'a' * 12 +
1096 U_BOOT_DATA + '&' * 4)
1097 self.assertEqual(expected, data)
1100 """Tests outputting a map of the images"""
1101 _, _, map_data, _ = self._DoReadFileDtb('55_sections.dts', map=True)
1102 self.assertEqual('''ImagePos Offset Size Name
1103 00000000 00000000 00000028 main-section
1104 00000000 00000000 00000010 section@0
1105 00000000 00000000 00000004 u-boot
1106 00000010 00000010 00000010 section@1
1107 00000010 00000000 00000004 u-boot
1108 00000020 00000020 00000004 section@2
1109 00000020 00000000 00000004 u-boot
1112 def testNamePrefix(self):
1113 """Tests that name prefixes are used"""
1114 _, _, map_data, _ = self._DoReadFileDtb('56_name_prefix.dts', map=True)
1115 self.assertEqual('''ImagePos Offset Size Name
1116 00000000 00000000 00000028 main-section
1117 00000000 00000000 00000010 section@0
1118 00000000 00000000 00000004 ro-u-boot
1119 00000010 00000010 00000010 section@1
1120 00000010 00000000 00000004 rw-u-boot
1123 def testUnknownContents(self):
1124 """Test that obtaining the contents works as expected"""
1125 with self.assertRaises(ValueError) as e:
1126 self._DoReadFile('57_unknown_contents.dts', True)
1127 self.assertIn("Section '/binman': Internal error: Could not complete "
1128 "processing of contents: remaining [<_testing.Entry__testing ",
1131 def testBadChangeSize(self):
1132 """Test that trying to change the size of an entry fails"""
1133 with self.assertRaises(ValueError) as e:
1134 self._DoReadFile('59_change_size.dts', True)
1135 self.assertIn("Node '/binman/_testing': Cannot update entry size from "
1136 '2 to 1', str(e.exception))
1138 def testUpdateFdt(self):
1139 """Test that we can update the device tree with offset/size info"""
1140 _, _, _, out_dtb_fname = self._DoReadFileDtb('60_fdt_update.dts',
1142 dtb = fdt.Fdt(out_dtb_fname)
1144 props = self._GetPropTree(dtb, ['offset', 'size', 'image-pos'])
1148 '_testing:offset': 32,
1150 '_testing:image-pos': 32,
1151 'section@0/u-boot:offset': 0,
1152 'section@0/u-boot:size': len(U_BOOT_DATA),
1153 'section@0/u-boot:image-pos': 0,
1154 'section@0:offset': 0,
1155 'section@0:size': 16,
1156 'section@0:image-pos': 0,
1158 'section@1/u-boot:offset': 0,
1159 'section@1/u-boot:size': len(U_BOOT_DATA),
1160 'section@1/u-boot:image-pos': 16,
1161 'section@1:offset': 16,
1162 'section@1:size': 16,
1163 'section@1:image-pos': 16,
1167 def testUpdateFdtBad(self):
1168 """Test that we detect when ProcessFdt never completes"""
1169 with self.assertRaises(ValueError) as e:
1170 self._DoReadFileDtb('61_fdt_update_bad.dts', update_dtb=True)
1171 self.assertIn('Could not complete processing of Fdt: remaining '
1172 '[<_testing.Entry__testing', str(e.exception))
1174 def testEntryArgs(self):
1175 """Test passing arguments to entries from the command line"""
1177 'test-str-arg': 'test1',
1178 'test-int-arg': '456',
1180 self._DoReadFileDtb('62_entry_args.dts', entry_args=entry_args)
1181 self.assertIn('image', control.images)
1182 entry = control.images['image'].GetEntries()['_testing']
1183 self.assertEqual('test0', entry.test_str_fdt)
1184 self.assertEqual('test1', entry.test_str_arg)
1185 self.assertEqual(123, entry.test_int_fdt)
1186 self.assertEqual(456, entry.test_int_arg)
1188 def testEntryArgsMissing(self):
1189 """Test missing arguments and properties"""
1191 'test-int-arg': '456',
1193 self._DoReadFileDtb('63_entry_args_missing.dts', entry_args=entry_args)
1194 entry = control.images['image'].GetEntries()['_testing']
1195 self.assertEqual('test0', entry.test_str_fdt)
1196 self.assertEqual(None, entry.test_str_arg)
1197 self.assertEqual(None, entry.test_int_fdt)
1198 self.assertEqual(456, entry.test_int_arg)
1200 def testEntryArgsRequired(self):
1201 """Test missing arguments and properties"""
1203 'test-int-arg': '456',
1205 with self.assertRaises(ValueError) as e:
1206 self._DoReadFileDtb('64_entry_args_required.dts')
1207 self.assertIn("Node '/binman/_testing': Missing required "
1208 'properties/entry args: test-str-arg, test-int-fdt, test-int-arg',
1211 def testEntryArgsInvalidFormat(self):
1212 """Test that an invalid entry-argument format is detected"""
1213 args = ['-d', self.TestFile('64_entry_args_required.dts'), '-ano-value']
1214 with self.assertRaises(ValueError) as e:
1215 self._DoBinman(*args)
1216 self.assertIn("Invalid entry arguemnt 'no-value'", str(e.exception))
1218 def testEntryArgsInvalidInteger(self):
1219 """Test that an invalid entry-argument integer is detected"""
1221 'test-int-arg': 'abc',
1223 with self.assertRaises(ValueError) as e:
1224 self._DoReadFileDtb('62_entry_args.dts', entry_args=entry_args)
1225 self.assertIn("Node '/binman/_testing': Cannot convert entry arg "
1226 "'test-int-arg' (value 'abc') to integer",
1229 def testEntryArgsInvalidDatatype(self):
1230 """Test that an invalid entry-argument datatype is detected
1232 This test could be written in entry_test.py except that it needs
1233 access to control.entry_args, which seems more than that module should
1237 'test-bad-datatype-arg': '12',
1239 with self.assertRaises(ValueError) as e:
1240 self._DoReadFileDtb('65_entry_args_unknown_datatype.dts',
1241 entry_args=entry_args)
1242 self.assertIn('GetArg() internal error: Unknown data type ',
1246 """Test for a text entry type"""
1248 'test-id': TEXT_DATA,
1249 'test-id2': TEXT_DATA2,
1250 'test-id3': TEXT_DATA3,
1252 data, _, _, _ = self._DoReadFileDtb('66_text.dts',
1253 entry_args=entry_args)
1254 expected = (TEXT_DATA + chr(0) * (8 - len(TEXT_DATA)) + TEXT_DATA2 +
1255 TEXT_DATA3 + 'some text')
1256 self.assertEqual(expected, data)
1258 def testEntryDocs(self):
1259 """Test for creation of entry documentation"""
1260 with test_util.capture_sys_output() as (stdout, stderr):
1261 control.WriteEntryDocs(binman.GetEntryModules())
1262 self.assertTrue(len(stdout.getvalue()) > 0)
1264 def testEntryDocsMissing(self):
1265 """Test handling of missing entry documentation"""
1266 with self.assertRaises(ValueError) as e:
1267 with test_util.capture_sys_output() as (stdout, stderr):
1268 control.WriteEntryDocs(binman.GetEntryModules(), 'u_boot')
1269 self.assertIn('Documentation is missing for modules: u_boot',
1273 """Basic test of generation of a flashrom fmap"""
1274 data = self._DoReadFile('67_fmap.dts')
1275 fhdr, fentries = fmap_util.DecodeFmap(data[32:])
1276 expected = U_BOOT_DATA + '!' * 12 + U_BOOT_DATA + 'a' * 12
1277 self.assertEqual(expected, data[:32])
1278 self.assertEqual('__FMAP__', fhdr.signature)
1279 self.assertEqual(1, fhdr.ver_major)
1280 self.assertEqual(0, fhdr.ver_minor)
1281 self.assertEqual(0, fhdr.base)
1282 self.assertEqual(16 + 16 +
1283 fmap_util.FMAP_HEADER_LEN +
1284 fmap_util.FMAP_AREA_LEN * 3, fhdr.image_size)
1285 self.assertEqual('FMAP', fhdr.name)
1286 self.assertEqual(3, fhdr.nareas)
1287 for fentry in fentries:
1288 self.assertEqual(0, fentry.flags)
1290 self.assertEqual(0, fentries[0].offset)
1291 self.assertEqual(4, fentries[0].size)
1292 self.assertEqual('RO_U_BOOT', fentries[0].name)
1294 self.assertEqual(16, fentries[1].offset)
1295 self.assertEqual(4, fentries[1].size)
1296 self.assertEqual('RW_U_BOOT', fentries[1].name)
1298 self.assertEqual(32, fentries[2].offset)
1299 self.assertEqual(fmap_util.FMAP_HEADER_LEN +
1300 fmap_util.FMAP_AREA_LEN * 3, fentries[2].size)
1301 self.assertEqual('FMAP', fentries[2].name)
1303 def testBlobNamedByArg(self):
1304 """Test we can add a blob with the filename coming from an entry arg"""
1306 'cros-ec-rw-path': 'ecrw.bin',
1308 data, _, _, _ = self._DoReadFileDtb('68_blob_named_by_arg.dts',
1309 entry_args=entry_args)
1312 """Test for an fill entry type"""
1313 data = self._DoReadFile('69_fill.dts')
1314 expected = 8 * chr(0xff) + 8 * chr(0)
1315 self.assertEqual(expected, data)
1317 def testFillNoSize(self):
1318 """Test for an fill entry type with no size"""
1319 with self.assertRaises(ValueError) as e:
1320 self._DoReadFile('70_fill_no_size.dts')
1321 self.assertIn("'fill' entry must have a size property",
1324 def _HandleGbbCommand(self, pipe_list):
1325 """Fake calls to the futility utility"""
1326 if pipe_list[0][0] == 'futility':
1327 fname = pipe_list[0][-1]
1328 # Append our GBB data to the file, which will happen every time the
1329 # futility command is called.
1330 with open(fname, 'a') as fd:
1332 return command.CommandResult()
1335 """Test for the Chromium OS Google Binary Block"""
1336 command.test_result = self._HandleGbbCommand
1338 'keydir': 'devkeys',
1339 'bmpblk': 'bmpblk.bin',
1341 data, _, _, _ = self._DoReadFileDtb('71_gbb.dts', entry_args=entry_args)
1344 expected = GBB_DATA + GBB_DATA + 8 * chr(0) + (0x2180 - 16) * chr(0)
1345 self.assertEqual(expected, data)
1347 def testGbbTooSmall(self):
1348 """Test for the Chromium OS Google Binary Block being large enough"""
1349 with self.assertRaises(ValueError) as e:
1350 self._DoReadFileDtb('72_gbb_too_small.dts')
1351 self.assertIn("Node '/binman/gbb': GBB is too small",
1354 def testGbbNoSize(self):
1355 """Test for the Chromium OS Google Binary Block having a size"""
1356 with self.assertRaises(ValueError) as e:
1357 self._DoReadFileDtb('73_gbb_no_size.dts')
1358 self.assertIn("Node '/binman/gbb': GBB must have a fixed size",
1361 def _HandleVblockCommand(self, pipe_list):
1362 """Fake calls to the futility utility"""
1363 if pipe_list[0][0] == 'futility':
1364 fname = pipe_list[0][3]
1365 with open(fname, 'wb') as fd:
1366 fd.write(VBLOCK_DATA)
1367 return command.CommandResult()
1369 def testVblock(self):
1370 """Test for the Chromium OS Verified Boot Block"""
1371 command.test_result = self._HandleVblockCommand
1373 'keydir': 'devkeys',
1375 data, _, _, _ = self._DoReadFileDtb('74_vblock.dts',
1376 entry_args=entry_args)
1377 expected = U_BOOT_DATA + VBLOCK_DATA + U_BOOT_DTB_DATA
1378 self.assertEqual(expected, data)
1380 def testVblockNoContent(self):
1381 """Test we detect a vblock which has no content to sign"""
1382 with self.assertRaises(ValueError) as e:
1383 self._DoReadFile('75_vblock_no_content.dts')
1384 self.assertIn("Node '/binman/vblock': Vblock must have a 'content' "
1385 'property', str(e.exception))
1387 def testVblockBadPhandle(self):
1388 """Test that we detect a vblock with an invalid phandle in contents"""
1389 with self.assertRaises(ValueError) as e:
1390 self._DoReadFile('76_vblock_bad_phandle.dts')
1391 self.assertIn("Node '/binman/vblock': Cannot find node for phandle "
1392 '1000', str(e.exception))
1394 def testVblockBadEntry(self):
1395 """Test that we detect an entry that points to a non-entry"""
1396 with self.assertRaises(ValueError) as e:
1397 self._DoReadFile('77_vblock_bad_entry.dts')
1398 self.assertIn("Node '/binman/vblock': Cannot find entry for node "
1399 "'other'", str(e.exception))
1402 """Test that an image with TPL and ots device tree can be created"""
1403 # ELF file with a '__bss_size' symbol
1404 with open(self.TestFile('bss_data')) as fd:
1405 TestFunctional._MakeInputFile('tpl/u-boot-tpl', fd.read())
1406 data = self._DoReadFile('78_u_boot_tpl.dts')
1407 self.assertEqual(U_BOOT_TPL_DATA + U_BOOT_TPL_DTB_DATA, data)
1409 def testUsesPos(self):
1410 """Test that the 'pos' property cannot be used anymore"""
1411 with self.assertRaises(ValueError) as e:
1412 data = self._DoReadFile('79_uses_pos.dts')
1413 self.assertIn("Node '/binman/u-boot': Please use 'offset' instead of "
1414 "'pos'", str(e.exception))
1416 def testFillZero(self):
1417 """Test for an fill entry type with a size of 0"""
1418 data = self._DoReadFile('80_fill_empty.dts')
1419 self.assertEqual(chr(0) * 16, data)
1421 def testTextMissing(self):
1422 """Test for a text entry type where there is no text"""
1423 with self.assertRaises(ValueError) as e:
1424 self._DoReadFileDtb('66_text.dts',)
1425 self.assertIn("Node '/binman/text': No value provided for text label "
1426 "'test-id'", str(e.exception))
1428 def testPackStart16Tpl(self):
1429 """Test that an image with an x86 start16 TPL region can be created"""
1430 data = self._DoReadFile('81_x86-start16-tpl.dts')
1431 self.assertEqual(X86_START16_TPL_DATA, data[:len(X86_START16_TPL_DATA)])
1433 def testSelectImage(self):
1434 """Test that we can select which images to build"""
1435 with test_util.capture_sys_output() as (stdout, stderr):
1436 retcode = self._DoTestFile('06_dual_image.dts', images=['image2'])
1437 self.assertEqual(0, retcode)
1438 self.assertIn('Skipping images: image1', stdout.getvalue())
1440 self.assertFalse(os.path.exists(tools.GetOutputFilename('image1.bin')))
1441 self.assertTrue(os.path.exists(tools.GetOutputFilename('image2.bin')))
1443 def testUpdateFdtAll(self):
1444 """Test that all device trees are updated with offset/size info"""
1445 data, _, _, _ = self._DoReadFileDtb('82_fdt_update_all.dts',
1446 use_real_dtb=True, update_dtb=True)
1449 'section:image-pos': 0,
1450 'u-boot-tpl-dtb:size': 513,
1451 'u-boot-spl-dtb:size': 513,
1452 'u-boot-spl-dtb:offset': 493,
1454 'section/u-boot-dtb:image-pos': 0,
1455 'u-boot-spl-dtb:image-pos': 493,
1456 'section/u-boot-dtb:size': 493,
1457 'u-boot-tpl-dtb:image-pos': 1006,
1458 'section/u-boot-dtb:offset': 0,
1459 'section:size': 493,
1461 'section:offset': 0,
1462 'u-boot-tpl-dtb:offset': 1006,
1466 # We expect three device-tree files in the output, one after the other.
1467 # Read them in sequence. We look for an 'spl' property in the SPL tree,
1468 # and 'tpl' in the TPL tree, to make sure they are distinct from the
1469 # main U-Boot tree. All three should have the same postions and offset.
1471 for item in ['', 'spl', 'tpl']:
1472 dtb = fdt.Fdt.FromData(data[start:])
1474 props = self._GetPropTree(dtb, ['offset', 'size', 'image-pos',
1476 expected = dict(base_expected)
1479 self.assertEqual(expected, props)
1480 start += dtb._fdt_obj.totalsize()
1482 def testUpdateFdtOutput(self):
1483 """Test that output DTB files are updated"""
1485 data, dtb_data, _, _ = self._DoReadFileDtb('82_fdt_update_all.dts',
1486 use_real_dtb=True, update_dtb=True, reset_dtbs=False)
1488 # Unfortunately, compiling a source file always results in a file
1489 # called source.dtb (see fdt_util.EnsureCompiled()). The test
1490 # source file (e.g. test/75_fdt_update_all.dts) thus does not enter
1491 # binman as a file called u-boot.dtb. To fix this, copy the file
1492 # over to the expected place.
1493 #tools.WriteFile(os.path.join(self._indir, 'u-boot.dtb'),
1494 #tools.ReadFile(tools.GetOutputFilename('source.dtb')))
1496 for fname in ['u-boot.dtb.out', 'spl/u-boot-spl.dtb.out',
1497 'tpl/u-boot-tpl.dtb.out']:
1498 dtb = fdt.Fdt.FromData(data[start:])
1499 size = dtb._fdt_obj.totalsize()
1500 pathname = tools.GetOutputFilename(os.path.split(fname)[1])
1501 outdata = tools.ReadFile(pathname)
1502 name = os.path.split(fname)[0]
1505 orig_indata = self._GetDtbContentsForSplTpl(dtb_data, name)
1507 orig_indata = dtb_data
1508 self.assertNotEqual(outdata, orig_indata,
1509 "Expected output file '%s' be updated" % pathname)
1510 self.assertEqual(outdata, data[start:start + size],
1511 "Expected output file '%s' to match output image" %
1517 def _decompress(self, data):
1518 out = os.path.join(self._indir, 'lz4.tmp')
1519 with open(out, 'wb') as fd:
1521 return tools.Run('lz4', '-dc', out)
1524 orig = lz4.frame.decompress(data)
1525 except AttributeError:
1526 orig = lz4.decompress(data)
1529 def testCompress(self):
1530 """Test compression of blobs"""
1531 data, _, _, out_dtb_fname = self._DoReadFileDtb('83_compress.dts',
1532 use_real_dtb=True, update_dtb=True)
1533 dtb = fdt.Fdt(out_dtb_fname)
1535 props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
1536 orig = self._decompress(data)
1537 self.assertEquals(COMPRESS_DATA, orig)
1539 'blob:uncomp-size': len(COMPRESS_DATA),
1540 'blob:size': len(data),
1543 self.assertEqual(expected, props)
1545 def testFiles(self):
1546 """Test bringing in multiple files"""
1547 data = self._DoReadFile('84_files.dts')
1548 self.assertEqual(FILES_DATA, data)
1550 def testFilesCompress(self):
1551 """Test bringing in multiple files and compressing them"""
1552 data = self._DoReadFile('85_files_compress.dts')
1554 image = control.images['image']
1555 entries = image.GetEntries()
1556 files = entries['files']
1557 entries = files._section._entries
1560 for i in range(1, 3):
1562 start = entries[key].image_pos
1563 len = entries[key].size
1564 chunk = data[start:start + len]
1565 orig += self._decompress(chunk)
1567 self.assertEqual(FILES_DATA, orig)
1569 def testFilesMissing(self):
1570 """Test missing files"""
1571 with self.assertRaises(ValueError) as e:
1572 data = self._DoReadFile('86_files_none.dts')
1573 self.assertIn("Node '/binman/files': Pattern \'files/*.none\' matched "
1574 'no files', str(e.exception))
1576 def testFilesNoPattern(self):
1577 """Test missing files"""
1578 with self.assertRaises(ValueError) as e:
1579 data = self._DoReadFile('87_files_no_pattern.dts')
1580 self.assertIn("Node '/binman/files': Missing 'pattern' property",
1583 def testExpandSize(self):
1584 """Test an expanding entry"""
1585 data, _, map_data, _ = self._DoReadFileDtb('88_expand_size.dts',
1587 expect = ('a' * 8 + U_BOOT_DATA +
1588 MRC_DATA + 'b' * 1 + U_BOOT_DATA +
1589 'c' * 8 + U_BOOT_DATA +
1591 self.assertEqual(expect, data)
1592 self.assertEqual('''ImagePos Offset Size Name
1593 00000000 00000000 00000028 main-section
1594 00000000 00000000 00000008 fill
1595 00000008 00000008 00000004 u-boot
1596 0000000c 0000000c 00000004 section
1597 0000000c 00000000 00000003 intel-mrc
1598 00000010 00000010 00000004 u-boot2
1599 00000014 00000014 0000000c section2
1600 00000014 00000000 00000008 fill
1601 0000001c 00000008 00000004 u-boot
1602 00000020 00000020 00000008 fill2
1605 def testExpandSizeBad(self):
1606 """Test an expanding entry which fails to provide contents"""
1607 with self.assertRaises(ValueError) as e:
1608 self._DoReadFileDtb('89_expand_size_bad.dts', map=True)
1609 self.assertIn("Node '/binman/_testing': Cannot obtain contents when "
1610 'expanding entry', str(e.exception))
1613 """Test hashing of the contents of an entry"""
1614 _, _, _, out_dtb_fname = self._DoReadFileDtb('90_hash.dts',
1615 use_real_dtb=True, update_dtb=True)
1616 dtb = fdt.Fdt(out_dtb_fname)
1618 hash_node = dtb.GetNode('/binman/u-boot/hash').props['value']
1619 m = hashlib.sha256()
1620 m.update(U_BOOT_DATA)
1621 self.assertEqual(m.digest(), ''.join(hash_node.value))
1623 def testHashNoAlgo(self):
1624 with self.assertRaises(ValueError) as e:
1625 self._DoReadFileDtb('91_hash_no_algo.dts', update_dtb=True)
1626 self.assertIn("Node \'/binman/u-boot\': Missing \'algo\' property for "
1627 'hash node', str(e.exception))
1629 def testHashBadAlgo(self):
1630 with self.assertRaises(ValueError) as e:
1631 self._DoReadFileDtb('92_hash_bad_algo.dts', update_dtb=True)
1632 self.assertIn("Node '/binman/u-boot': Unknown hash algorithm",
1635 def testHashSection(self):
1636 """Test hashing of the contents of an entry"""
1637 _, _, _, out_dtb_fname = self._DoReadFileDtb('99_hash_section.dts',
1638 use_real_dtb=True, update_dtb=True)
1639 dtb = fdt.Fdt(out_dtb_fname)
1641 hash_node = dtb.GetNode('/binman/section/hash').props['value']
1642 m = hashlib.sha256()
1643 m.update(U_BOOT_DATA)
1645 self.assertEqual(m.digest(), ''.join(hash_node.value))
1648 if __name__ == "__main__":