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