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 9import collections 10import gzip 11import hashlib 12from optparse import OptionParser 13import os 14import re 15import shutil 16import struct 17import sys 18import tempfile 19import unittest 20 21from binman import cbfs_util 22from binman import cmdline 23from binman import control 24from binman import elf 25from binman import elf_test 26from binman import fmap_util 27from binman import state 28from dtoc import fdt 29from dtoc import fdt_util 30from binman.etype import fdtmap 31from binman.etype import image_header 32from binman.image import Image 33from patman import command 34from patman import test_util 35from patman import tools 36from patman import tout 37 38# Contents of test files, corresponding to different entry types 39U_BOOT_DATA = b'1234' 40U_BOOT_IMG_DATA = b'img' 41U_BOOT_SPL_DATA = b'56780123456789abcdefghi' 42U_BOOT_TPL_DATA = b'tpl9876543210fedcbazyw' 43BLOB_DATA = b'89' 44ME_DATA = b'0abcd' 45VGA_DATA = b'vga' 46U_BOOT_DTB_DATA = b'udtb' 47U_BOOT_SPL_DTB_DATA = b'spldtb' 48U_BOOT_TPL_DTB_DATA = b'tpldtb' 49X86_START16_DATA = b'start16' 50X86_START16_SPL_DATA = b'start16spl' 51X86_START16_TPL_DATA = b'start16tpl' 52X86_RESET16_DATA = b'reset16' 53X86_RESET16_SPL_DATA = b'reset16spl' 54X86_RESET16_TPL_DATA = b'reset16tpl' 55PPC_MPC85XX_BR_DATA = b'ppcmpc85xxbr' 56U_BOOT_NODTB_DATA = b'nodtb with microcode pointer somewhere in here' 57U_BOOT_SPL_NODTB_DATA = b'splnodtb with microcode pointer somewhere in here' 58U_BOOT_TPL_NODTB_DATA = b'tplnodtb with microcode pointer somewhere in here' 59FSP_DATA = b'fsp' 60CMC_DATA = b'cmc' 61VBT_DATA = b'vbt' 62MRC_DATA = b'mrc' 63TEXT_DATA = 'text' 64TEXT_DATA2 = 'text2' 65TEXT_DATA3 = 'text3' 66CROS_EC_RW_DATA = b'ecrw' 67GBB_DATA = b'gbbd' 68BMPBLK_DATA = b'bmp' 69VBLOCK_DATA = b'vblk' 70FILES_DATA = (b"sorry I'm late\nOh, don't bother apologising, I'm " + 71 b"sorry you're alive\n") 72COMPRESS_DATA = b'compress xxxxxxxxxxxxxxxxxxxxxx data' 73COMPRESS_DATA_BIG = COMPRESS_DATA * 2 74REFCODE_DATA = b'refcode' 75FSP_M_DATA = b'fsp_m' 76FSP_S_DATA = b'fsp_s' 77FSP_T_DATA = b'fsp_t' 78ATF_BL31_DATA = b'bl31' 79OPENSBI_DATA = b'opensbi' 80SCP_DATA = b'scp' 81TEST_FDT1_DATA = b'fdt1' 82TEST_FDT2_DATA = b'test-fdt2' 83ENV_DATA = b'var1=1\nvar2="2"' 84 85# Subdirectory of the input dir to use to put test FDTs 86TEST_FDT_SUBDIR = 'fdts' 87 88# The expected size for the device tree in some tests 89EXTRACT_DTB_SIZE = 0x3c9 90 91# Properties expected to be in the device tree when update_dtb is used 92BASE_DTB_PROPS = ['offset', 'size', 'image-pos'] 93 94# Extra properties expected to be in the device tree when allow-repack is used 95REPACK_DTB_PROPS = ['orig-offset', 'orig-size'] 96 97 98class TestFunctional(unittest.TestCase): 99 """Functional tests for binman 100 101 Most of these use a sample .dts file to build an image and then check 102 that it looks correct. The sample files are in the test/ subdirectory 103 and are numbered. 104 105 For each entry type a very small test file is created using fixed 106 string contents. This makes it easy to test that things look right, and 107 debug problems. 108 109 In some cases a 'real' file must be used - these are also supplied in 110 the test/ diurectory. 111 """ 112 @classmethod 113 def setUpClass(cls): 114 global entry 115 from binman import entry 116 117 # Handle the case where argv[0] is 'python' 118 cls._binman_dir = os.path.dirname(os.path.realpath(sys.argv[0])) 119 cls._binman_pathname = os.path.join(cls._binman_dir, 'binman') 120 121 # Create a temporary directory for input files 122 cls._indir = tempfile.mkdtemp(prefix='binmant.') 123 124 # Create some test files 125 TestFunctional._MakeInputFile('u-boot.bin', U_BOOT_DATA) 126 TestFunctional._MakeInputFile('u-boot.img', U_BOOT_IMG_DATA) 127 TestFunctional._MakeInputFile('spl/u-boot-spl.bin', U_BOOT_SPL_DATA) 128 TestFunctional._MakeInputFile('tpl/u-boot-tpl.bin', U_BOOT_TPL_DATA) 129 TestFunctional._MakeInputFile('blobfile', BLOB_DATA) 130 TestFunctional._MakeInputFile('me.bin', ME_DATA) 131 TestFunctional._MakeInputFile('vga.bin', VGA_DATA) 132 cls._ResetDtbs() 133 134 TestFunctional._MakeInputFile('u-boot-br.bin', PPC_MPC85XX_BR_DATA) 135 136 TestFunctional._MakeInputFile('u-boot-x86-start16.bin', X86_START16_DATA) 137 TestFunctional._MakeInputFile('spl/u-boot-x86-start16-spl.bin', 138 X86_START16_SPL_DATA) 139 TestFunctional._MakeInputFile('tpl/u-boot-x86-start16-tpl.bin', 140 X86_START16_TPL_DATA) 141 142 TestFunctional._MakeInputFile('u-boot-x86-reset16.bin', 143 X86_RESET16_DATA) 144 TestFunctional._MakeInputFile('spl/u-boot-x86-reset16-spl.bin', 145 X86_RESET16_SPL_DATA) 146 TestFunctional._MakeInputFile('tpl/u-boot-x86-reset16-tpl.bin', 147 X86_RESET16_TPL_DATA) 148 149 TestFunctional._MakeInputFile('u-boot-nodtb.bin', U_BOOT_NODTB_DATA) 150 TestFunctional._MakeInputFile('spl/u-boot-spl-nodtb.bin', 151 U_BOOT_SPL_NODTB_DATA) 152 TestFunctional._MakeInputFile('tpl/u-boot-tpl-nodtb.bin', 153 U_BOOT_TPL_NODTB_DATA) 154 TestFunctional._MakeInputFile('fsp.bin', FSP_DATA) 155 TestFunctional._MakeInputFile('cmc.bin', CMC_DATA) 156 TestFunctional._MakeInputFile('vbt.bin', VBT_DATA) 157 TestFunctional._MakeInputFile('mrc.bin', MRC_DATA) 158 TestFunctional._MakeInputFile('ecrw.bin', CROS_EC_RW_DATA) 159 TestFunctional._MakeInputDir('devkeys') 160 TestFunctional._MakeInputFile('bmpblk.bin', BMPBLK_DATA) 161 TestFunctional._MakeInputFile('refcode.bin', REFCODE_DATA) 162 TestFunctional._MakeInputFile('fsp_m.bin', FSP_M_DATA) 163 TestFunctional._MakeInputFile('fsp_s.bin', FSP_S_DATA) 164 TestFunctional._MakeInputFile('fsp_t.bin', FSP_T_DATA) 165 166 cls._elf_testdir = os.path.join(cls._indir, 'elftest') 167 elf_test.BuildElfTestFiles(cls._elf_testdir) 168 169 # ELF file with a '_dt_ucode_base_size' symbol 170 TestFunctional._MakeInputFile('u-boot', 171 tools.ReadFile(cls.ElfTestFile('u_boot_ucode_ptr'))) 172 173 # Intel flash descriptor file 174 cls._SetupDescriptor() 175 176 shutil.copytree(cls.TestFile('files'), 177 os.path.join(cls._indir, 'files')) 178 179 TestFunctional._MakeInputFile('compress', COMPRESS_DATA) 180 TestFunctional._MakeInputFile('compress_big', COMPRESS_DATA_BIG) 181 TestFunctional._MakeInputFile('bl31.bin', ATF_BL31_DATA) 182 TestFunctional._MakeInputFile('fw_dynamic.bin', OPENSBI_DATA) 183 TestFunctional._MakeInputFile('scp.bin', SCP_DATA) 184 185 # Add a few .dtb files for testing 186 TestFunctional._MakeInputFile('%s/test-fdt1.dtb' % TEST_FDT_SUBDIR, 187 TEST_FDT1_DATA) 188 TestFunctional._MakeInputFile('%s/test-fdt2.dtb' % TEST_FDT_SUBDIR, 189 TEST_FDT2_DATA) 190 191 TestFunctional._MakeInputFile('env.txt', ENV_DATA) 192 193 # Travis-CI may have an old lz4 194 cls.have_lz4 = True 195 try: 196 tools.Run('lz4', '--no-frame-crc', '-c', 197 os.path.join(cls._indir, 'u-boot.bin'), binary=True) 198 except: 199 cls.have_lz4 = False 200 201 @classmethod 202 def tearDownClass(cls): 203 """Remove the temporary input directory and its contents""" 204 if cls.preserve_indir: 205 print('Preserving input dir: %s' % cls._indir) 206 else: 207 if cls._indir: 208 shutil.rmtree(cls._indir) 209 cls._indir = None 210 211 @classmethod 212 def setup_test_args(cls, preserve_indir=False, preserve_outdirs=False, 213 toolpath=None, verbosity=None): 214 """Accept arguments controlling test execution 215 216 Args: 217 preserve_indir: Preserve the shared input directory used by all 218 tests in this class. 219 preserve_outdir: Preserve the output directories used by tests. Each 220 test has its own, so this is normally only useful when running a 221 single test. 222 toolpath: ist of paths to use for tools 223 """ 224 cls.preserve_indir = preserve_indir 225 cls.preserve_outdirs = preserve_outdirs 226 cls.toolpath = toolpath 227 cls.verbosity = verbosity 228 229 def _CheckLz4(self): 230 if not self.have_lz4: 231 self.skipTest('lz4 --no-frame-crc not available') 232 233 def _CleanupOutputDir(self): 234 """Remove the temporary output directory""" 235 if self.preserve_outdirs: 236 print('Preserving output dir: %s' % tools.outdir) 237 else: 238 tools._FinaliseForTest() 239 240 def setUp(self): 241 # Enable this to turn on debugging output 242 # tout.Init(tout.DEBUG) 243 command.test_result = None 244 245 def tearDown(self): 246 """Remove the temporary output directory""" 247 self._CleanupOutputDir() 248 249 def _SetupImageInTmpdir(self): 250 """Set up the output image in a new temporary directory 251 252 This is used when an image has been generated in the output directory, 253 but we want to run binman again. This will create a new output 254 directory and fail to delete the original one. 255 256 This creates a new temporary directory, copies the image to it (with a 257 new name) and removes the old output directory. 258 259 Returns: 260 Tuple: 261 Temporary directory to use 262 New image filename 263 """ 264 image_fname = tools.GetOutputFilename('image.bin') 265 tmpdir = tempfile.mkdtemp(prefix='binman.') 266 updated_fname = os.path.join(tmpdir, 'image-updated.bin') 267 tools.WriteFile(updated_fname, tools.ReadFile(image_fname)) 268 self._CleanupOutputDir() 269 return tmpdir, updated_fname 270 271 @classmethod 272 def _ResetDtbs(cls): 273 TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA) 274 TestFunctional._MakeInputFile('spl/u-boot-spl.dtb', U_BOOT_SPL_DTB_DATA) 275 TestFunctional._MakeInputFile('tpl/u-boot-tpl.dtb', U_BOOT_TPL_DTB_DATA) 276 277 def _RunBinman(self, *args, **kwargs): 278 """Run binman using the command line 279 280 Args: 281 Arguments to pass, as a list of strings 282 kwargs: Arguments to pass to Command.RunPipe() 283 """ 284 result = command.RunPipe([[self._binman_pathname] + list(args)], 285 capture=True, capture_stderr=True, raise_on_error=False) 286 if result.return_code and kwargs.get('raise_on_error', True): 287 raise Exception("Error running '%s': %s" % (' '.join(args), 288 result.stdout + result.stderr)) 289 return result 290 291 def _DoBinman(self, *argv): 292 """Run binman using directly (in the same process) 293 294 Args: 295 Arguments to pass, as a list of strings 296 Returns: 297 Return value (0 for success) 298 """ 299 argv = list(argv) 300 args = cmdline.ParseArgs(argv) 301 args.pager = 'binman-invalid-pager' 302 args.build_dir = self._indir 303 304 # For testing, you can force an increase in verbosity here 305 # args.verbosity = tout.DEBUG 306 return control.Binman(args) 307 308 def _DoTestFile(self, fname, debug=False, map=False, update_dtb=False, 309 entry_args=None, images=None, use_real_dtb=False, 310 use_expanded=False, verbosity=None, allow_missing=False, 311 allow_fake_blobs=False, extra_indirs=None, threads=None, 312 test_section_timeout=False, update_fdt_in_elf=None): 313 """Run binman with a given test file 314 315 Args: 316 fname: Device-tree source filename to use (e.g. 005_simple.dts) 317 debug: True to enable debugging output 318 map: True to output map files for the images 319 update_dtb: Update the offset and size of each entry in the device 320 tree before packing it into the image 321 entry_args: Dict of entry args to supply to binman 322 key: arg name 323 value: value of that arg 324 images: List of image names to build 325 use_real_dtb: True to use the test file as the contents of 326 the u-boot-dtb entry. Normally this is not needed and the 327 test contents (the U_BOOT_DTB_DATA string) can be used. 328 But in some test we need the real contents. 329 use_expanded: True to use expanded entries where available, e.g. 330 'u-boot-expanded' instead of 'u-boot' 331 verbosity: Verbosity level to use (0-3, None=don't set it) 332 allow_missing: Set the '--allow-missing' flag so that missing 333 external binaries just produce a warning instead of an error 334 allow_fake_blobs: Set the '--fake-ext-blobs' flag 335 extra_indirs: Extra input directories to add using -I 336 threads: Number of threads to use (None for default, 0 for 337 single-threaded) 338 test_section_timeout: True to force the first time to timeout, as 339 used in testThreadTimeout() 340 update_fdt_in_elf: Value to pass with --update-fdt-in-elf=xxx 341 342 Returns: 343 int return code, 0 on success 344 """ 345 args = [] 346 if debug: 347 args.append('-D') 348 if verbosity is not None: 349 args.append('-v%d' % verbosity) 350 elif self.verbosity: 351 args.append('-v%d' % self.verbosity) 352 if self.toolpath: 353 for path in self.toolpath: 354 args += ['--toolpath', path] 355 if threads is not None: 356 args.append('-T%d' % threads) 357 if test_section_timeout: 358 args.append('--test-section-timeout') 359 args += ['build', '-p', '-I', self._indir, '-d', self.TestFile(fname)] 360 if map: 361 args.append('-m') 362 if update_dtb: 363 args.append('-u') 364 if not use_real_dtb: 365 args.append('--fake-dtb') 366 if not use_expanded: 367 args.append('--no-expanded') 368 if entry_args: 369 for arg, value in entry_args.items(): 370 args.append('-a%s=%s' % (arg, value)) 371 if allow_missing: 372 args.append('-M') 373 if allow_fake_blobs: 374 args.append('--fake-ext-blobs') 375 if update_fdt_in_elf: 376 args += ['--update-fdt-in-elf', update_fdt_in_elf] 377 if images: 378 for image in images: 379 args += ['-i', image] 380 if extra_indirs: 381 for indir in extra_indirs: 382 args += ['-I', indir] 383 return self._DoBinman(*args) 384 385 def _SetupDtb(self, fname, outfile='u-boot.dtb'): 386 """Set up a new test device-tree file 387 388 The given file is compiled and set up as the device tree to be used 389 for ths test. 390 391 Args: 392 fname: Filename of .dts file to read 393 outfile: Output filename for compiled device-tree binary 394 395 Returns: 396 Contents of device-tree binary 397 """ 398 tmpdir = tempfile.mkdtemp(prefix='binmant.') 399 dtb = fdt_util.EnsureCompiled(self.TestFile(fname), tmpdir) 400 with open(dtb, 'rb') as fd: 401 data = fd.read() 402 TestFunctional._MakeInputFile(outfile, data) 403 shutil.rmtree(tmpdir) 404 return data 405 406 def _GetDtbContentsForSplTpl(self, dtb_data, name): 407 """Create a version of the main DTB for SPL or SPL 408 409 For testing we don't actually have different versions of the DTB. With 410 U-Boot we normally run fdtgrep to remove unwanted nodes, but for tests 411 we don't normally have any unwanted nodes. 412 413 We still want the DTBs for SPL and TPL to be different though, since 414 otherwise it is confusing to know which one we are looking at. So add 415 an 'spl' or 'tpl' property to the top-level node. 416 417 Args: 418 dtb_data: dtb data to modify (this should be a value devicetree) 419 name: Name of a new property to add 420 421 Returns: 422 New dtb data with the property added 423 """ 424 dtb = fdt.Fdt.FromData(dtb_data) 425 dtb.Scan() 426 dtb.GetNode('/binman').AddZeroProp(name) 427 dtb.Sync(auto_resize=True) 428 dtb.Pack() 429 return dtb.GetContents() 430 431 def _DoReadFileDtb(self, fname, use_real_dtb=False, use_expanded=False, 432 map=False, update_dtb=False, entry_args=None, 433 reset_dtbs=True, extra_indirs=None, threads=None): 434 """Run binman and return the resulting image 435 436 This runs binman with a given test file and then reads the resulting 437 output file. It is a shortcut function since most tests need to do 438 these steps. 439 440 Raises an assertion failure if binman returns a non-zero exit code. 441 442 Args: 443 fname: Device-tree source filename to use (e.g. 005_simple.dts) 444 use_real_dtb: True to use the test file as the contents of 445 the u-boot-dtb entry. Normally this is not needed and the 446 test contents (the U_BOOT_DTB_DATA string) can be used. 447 But in some test we need the real contents. 448 use_expanded: True to use expanded entries where available, e.g. 449 'u-boot-expanded' instead of 'u-boot' 450 map: True to output map files for the images 451 update_dtb: Update the offset and size of each entry in the device 452 tree before packing it into the image 453 entry_args: Dict of entry args to supply to binman 454 key: arg name 455 value: value of that arg 456 reset_dtbs: With use_real_dtb the test dtb is overwritten by this 457 function. If reset_dtbs is True, then the original test dtb 458 is written back before this function finishes 459 extra_indirs: Extra input directories to add using -I 460 threads: Number of threads to use (None for default, 0 for 461 single-threaded) 462 463 Returns: 464 Tuple: 465 Resulting image contents 466 Device tree contents 467 Map data showing contents of image (or None if none) 468 Output device tree binary filename ('u-boot.dtb' path) 469 """ 470 dtb_data = None 471 # Use the compiled test file as the u-boot-dtb input 472 if use_real_dtb: 473 dtb_data = self._SetupDtb(fname) 474 475 # For testing purposes, make a copy of the DT for SPL and TPL. Add 476 # a node indicating which it is, so aid verification. 477 for name in ['spl', 'tpl']: 478 dtb_fname = '%s/u-boot-%s.dtb' % (name, name) 479 outfile = os.path.join(self._indir, dtb_fname) 480 TestFunctional._MakeInputFile(dtb_fname, 481 self._GetDtbContentsForSplTpl(dtb_data, name)) 482 483 try: 484 retcode = self._DoTestFile(fname, map=map, update_dtb=update_dtb, 485 entry_args=entry_args, use_real_dtb=use_real_dtb, 486 use_expanded=use_expanded, extra_indirs=extra_indirs, 487 threads=threads) 488 self.assertEqual(0, retcode) 489 out_dtb_fname = tools.GetOutputFilename('u-boot.dtb.out') 490 491 # Find the (only) image, read it and return its contents 492 image = control.images['image'] 493 image_fname = tools.GetOutputFilename('image.bin') 494 self.assertTrue(os.path.exists(image_fname)) 495 if map: 496 map_fname = tools.GetOutputFilename('image.map') 497 with open(map_fname) as fd: 498 map_data = fd.read() 499 else: 500 map_data = None 501 with open(image_fname, 'rb') as fd: 502 return fd.read(), dtb_data, map_data, out_dtb_fname 503 finally: 504 # Put the test file back 505 if reset_dtbs and use_real_dtb: 506 self._ResetDtbs() 507 508 def _DoReadFileRealDtb(self, fname): 509 """Run binman with a real .dtb file and return the resulting data 510 511 Args: 512 fname: DT source filename to use (e.g. 082_fdt_update_all.dts) 513 514 Returns: 515 Resulting image contents 516 """ 517 return self._DoReadFileDtb(fname, use_real_dtb=True, update_dtb=True)[0] 518 519 def _DoReadFile(self, fname, use_real_dtb=False): 520 """Helper function which discards the device-tree binary 521 522 Args: 523 fname: Device-tree source filename to use (e.g. 005_simple.dts) 524 use_real_dtb: True to use the test file as the contents of 525 the u-boot-dtb entry. Normally this is not needed and the 526 test contents (the U_BOOT_DTB_DATA string) can be used. 527 But in some test we need the real contents. 528 529 Returns: 530 Resulting image contents 531 """ 532 return self._DoReadFileDtb(fname, use_real_dtb)[0] 533 534 @classmethod 535 def _MakeInputFile(cls, fname, contents): 536 """Create a new test input file, creating directories as needed 537 538 Args: 539 fname: Filename to create 540 contents: File contents to write in to the file 541 Returns: 542 Full pathname of file created 543 """ 544 pathname = os.path.join(cls._indir, fname) 545 dirname = os.path.dirname(pathname) 546 if dirname and not os.path.exists(dirname): 547 os.makedirs(dirname) 548 with open(pathname, 'wb') as fd: 549 fd.write(contents) 550 return pathname 551 552 @classmethod 553 def _MakeInputDir(cls, dirname): 554 """Create a new test input directory, creating directories as needed 555 556 Args: 557 dirname: Directory name to create 558 559 Returns: 560 Full pathname of directory created 561 """ 562 pathname = os.path.join(cls._indir, dirname) 563 if not os.path.exists(pathname): 564 os.makedirs(pathname) 565 return pathname 566 567 @classmethod 568 def _SetupSplElf(cls, src_fname='bss_data'): 569 """Set up an ELF file with a '_dt_ucode_base_size' symbol 570 571 Args: 572 Filename of ELF file to use as SPL 573 """ 574 TestFunctional._MakeInputFile('spl/u-boot-spl', 575 tools.ReadFile(cls.ElfTestFile(src_fname))) 576 577 @classmethod 578 def _SetupTplElf(cls, src_fname='bss_data'): 579 """Set up an ELF file with a '_dt_ucode_base_size' symbol 580 581 Args: 582 Filename of ELF file to use as TPL 583 """ 584 TestFunctional._MakeInputFile('tpl/u-boot-tpl', 585 tools.ReadFile(cls.ElfTestFile(src_fname))) 586 587 @classmethod 588 def _SetupDescriptor(cls): 589 with open(cls.TestFile('descriptor.bin'), 'rb') as fd: 590 TestFunctional._MakeInputFile('descriptor.bin', fd.read()) 591 592 @classmethod 593 def TestFile(cls, fname): 594 return os.path.join(cls._binman_dir, 'test', fname) 595 596 @classmethod 597 def ElfTestFile(cls, fname): 598 return os.path.join(cls._elf_testdir, fname) 599 600 def AssertInList(self, grep_list, target): 601 """Assert that at least one of a list of things is in a target 602 603 Args: 604 grep_list: List of strings to check 605 target: Target string 606 """ 607 for grep in grep_list: 608 if grep in target: 609 return 610 self.fail("Error: '%s' not found in '%s'" % (grep_list, target)) 611 612 def CheckNoGaps(self, entries): 613 """Check that all entries fit together without gaps 614 615 Args: 616 entries: List of entries to check 617 """ 618 offset = 0 619 for entry in entries.values(): 620 self.assertEqual(offset, entry.offset) 621 offset += entry.size 622 623 def GetFdtLen(self, dtb): 624 """Get the totalsize field from a device-tree binary 625 626 Args: 627 dtb: Device-tree binary contents 628 629 Returns: 630 Total size of device-tree binary, from the header 631 """ 632 return struct.unpack('>L', dtb[4:8])[0] 633 634 def _GetPropTree(self, dtb, prop_names, prefix='/binman/'): 635 def AddNode(node, path): 636 if node.name != '/': 637 path += '/' + node.name 638 for prop in node.props.values(): 639 if prop.name in prop_names: 640 prop_path = path + ':' + prop.name 641 tree[prop_path[len(prefix):]] = fdt_util.fdt32_to_cpu( 642 prop.value) 643 for subnode in node.subnodes: 644 AddNode(subnode, path) 645 646 tree = {} 647 AddNode(dtb.GetRoot(), '') 648 return tree 649 650 def testRun(self): 651 """Test a basic run with valid args""" 652 result = self._RunBinman('-h') 653 654 def testFullHelp(self): 655 """Test that the full help is displayed with -H""" 656 result = self._RunBinman('-H') 657 help_file = os.path.join(self._binman_dir, 'README.rst') 658 # Remove possible extraneous strings 659 extra = '::::::::::::::\n' + help_file + '\n::::::::::::::\n' 660 gothelp = result.stdout.replace(extra, '') 661 self.assertEqual(len(gothelp), os.path.getsize(help_file)) 662 self.assertEqual(0, len(result.stderr)) 663 self.assertEqual(0, result.return_code) 664 665 def testFullHelpInternal(self): 666 """Test that the full help is displayed with -H""" 667 try: 668 command.test_result = command.CommandResult() 669 result = self._DoBinman('-H') 670 help_file = os.path.join(self._binman_dir, 'README.rst') 671 finally: 672 command.test_result = None 673 674 def testHelp(self): 675 """Test that the basic help is displayed with -h""" 676 result = self._RunBinman('-h') 677 self.assertTrue(len(result.stdout) > 200) 678 self.assertEqual(0, len(result.stderr)) 679 self.assertEqual(0, result.return_code) 680 681 def testBoard(self): 682 """Test that we can run it with a specific board""" 683 self._SetupDtb('005_simple.dts', 'sandbox/u-boot.dtb') 684 TestFunctional._MakeInputFile('sandbox/u-boot.bin', U_BOOT_DATA) 685 result = self._DoBinman('build', '-n', '-b', 'sandbox') 686 self.assertEqual(0, result) 687 688 def testNeedBoard(self): 689 """Test that we get an error when no board ius supplied""" 690 with self.assertRaises(ValueError) as e: 691 result = self._DoBinman('build') 692 self.assertIn("Must provide a board to process (use -b <board>)", 693 str(e.exception)) 694 695 def testMissingDt(self): 696 """Test that an invalid device-tree file generates an error""" 697 with self.assertRaises(Exception) as e: 698 self._RunBinman('build', '-d', 'missing_file') 699 # We get one error from libfdt, and a different one from fdtget. 700 self.AssertInList(["Couldn't open blob from 'missing_file'", 701 'No such file or directory'], str(e.exception)) 702 703 def testBrokenDt(self): 704 """Test that an invalid device-tree source file generates an error 705 706 Since this is a source file it should be compiled and the error 707 will come from the device-tree compiler (dtc). 708 """ 709 with self.assertRaises(Exception) as e: 710 self._RunBinman('build', '-d', self.TestFile('001_invalid.dts')) 711 self.assertIn("FATAL ERROR: Unable to parse input tree", 712 str(e.exception)) 713 714 def testMissingNode(self): 715 """Test that a device tree without a 'binman' node generates an error""" 716 with self.assertRaises(Exception) as e: 717 self._DoBinman('build', '-d', self.TestFile('002_missing_node.dts')) 718 self.assertIn("does not have a 'binman' node", str(e.exception)) 719 720 def testEmpty(self): 721 """Test that an empty binman node works OK (i.e. does nothing)""" 722 result = self._RunBinman('build', '-d', self.TestFile('003_empty.dts')) 723 self.assertEqual(0, len(result.stderr)) 724 self.assertEqual(0, result.return_code) 725 726 def testInvalidEntry(self): 727 """Test that an invalid entry is flagged""" 728 with self.assertRaises(Exception) as e: 729 result = self._RunBinman('build', '-d', 730 self.TestFile('004_invalid_entry.dts')) 731 self.assertIn("Unknown entry type 'not-a-valid-type' in node " 732 "'/binman/not-a-valid-type'", str(e.exception)) 733 734 def testSimple(self): 735 """Test a simple binman with a single file""" 736 data = self._DoReadFile('005_simple.dts') 737 self.assertEqual(U_BOOT_DATA, data) 738 739 def testSimpleDebug(self): 740 """Test a simple binman run with debugging enabled""" 741 self._DoTestFile('005_simple.dts', debug=True) 742 743 def testDual(self): 744 """Test that we can handle creating two images 745 746 This also tests image padding. 747 """ 748 retcode = self._DoTestFile('006_dual_image.dts') 749 self.assertEqual(0, retcode) 750 751 image = control.images['image1'] 752 self.assertEqual(len(U_BOOT_DATA), image.size) 753 fname = tools.GetOutputFilename('image1.bin') 754 self.assertTrue(os.path.exists(fname)) 755 with open(fname, 'rb') as fd: 756 data = fd.read() 757 self.assertEqual(U_BOOT_DATA, data) 758 759 image = control.images['image2'] 760 self.assertEqual(3 + len(U_BOOT_DATA) + 5, image.size) 761 fname = tools.GetOutputFilename('image2.bin') 762 self.assertTrue(os.path.exists(fname)) 763 with open(fname, 'rb') as fd: 764 data = fd.read() 765 self.assertEqual(U_BOOT_DATA, data[3:7]) 766 self.assertEqual(tools.GetBytes(0, 3), data[:3]) 767 self.assertEqual(tools.GetBytes(0, 5), data[7:]) 768 769 def testBadAlign(self): 770 """Test that an invalid alignment value is detected""" 771 with self.assertRaises(ValueError) as e: 772 self._DoTestFile('007_bad_align.dts') 773 self.assertIn("Node '/binman/u-boot': Alignment 23 must be a power " 774 "of two", str(e.exception)) 775 776 def testPackSimple(self): 777 """Test that packing works as expected""" 778 retcode = self._DoTestFile('008_pack.dts') 779 self.assertEqual(0, retcode) 780 self.assertIn('image', control.images) 781 image = control.images['image'] 782 entries = image.GetEntries() 783 self.assertEqual(5, len(entries)) 784 785 # First u-boot 786 self.assertIn('u-boot', entries) 787 entry = entries['u-boot'] 788 self.assertEqual(0, entry.offset) 789 self.assertEqual(len(U_BOOT_DATA), entry.size) 790 791 # Second u-boot, aligned to 16-byte boundary 792 self.assertIn('u-boot-align', entries) 793 entry = entries['u-boot-align'] 794 self.assertEqual(16, entry.offset) 795 self.assertEqual(len(U_BOOT_DATA), entry.size) 796 797 # Third u-boot, size 23 bytes 798 self.assertIn('u-boot-size', entries) 799 entry = entries['u-boot-size'] 800 self.assertEqual(20, entry.offset) 801 self.assertEqual(len(U_BOOT_DATA), entry.contents_size) 802 self.assertEqual(23, entry.size) 803 804 # Fourth u-boot, placed immediate after the above 805 self.assertIn('u-boot-next', entries) 806 entry = entries['u-boot-next'] 807 self.assertEqual(43, entry.offset) 808 self.assertEqual(len(U_BOOT_DATA), entry.size) 809 810 # Fifth u-boot, placed at a fixed offset 811 self.assertIn('u-boot-fixed', entries) 812 entry = entries['u-boot-fixed'] 813 self.assertEqual(61, entry.offset) 814 self.assertEqual(len(U_BOOT_DATA), entry.size) 815 816 self.assertEqual(65, image.size) 817 818 def testPackExtra(self): 819 """Test that extra packing feature works as expected""" 820 data, _, _, out_dtb_fname = self._DoReadFileDtb('009_pack_extra.dts', 821 update_dtb=True) 822 823 self.assertIn('image', control.images) 824 image = control.images['image'] 825 entries = image.GetEntries() 826 self.assertEqual(5, len(entries)) 827 828 # First u-boot with padding before and after 829 self.assertIn('u-boot', entries) 830 entry = entries['u-boot'] 831 self.assertEqual(0, entry.offset) 832 self.assertEqual(3, entry.pad_before) 833 self.assertEqual(3 + 5 + len(U_BOOT_DATA), entry.size) 834 self.assertEqual(U_BOOT_DATA, entry.data) 835 self.assertEqual(tools.GetBytes(0, 3) + U_BOOT_DATA + 836 tools.GetBytes(0, 5), data[:entry.size]) 837 pos = entry.size 838 839 # Second u-boot has an aligned size, but it has no effect 840 self.assertIn('u-boot-align-size-nop', entries) 841 entry = entries['u-boot-align-size-nop'] 842 self.assertEqual(pos, entry.offset) 843 self.assertEqual(len(U_BOOT_DATA), entry.size) 844 self.assertEqual(U_BOOT_DATA, entry.data) 845 self.assertEqual(U_BOOT_DATA, data[pos:pos + entry.size]) 846 pos += entry.size 847 848 # Third u-boot has an aligned size too 849 self.assertIn('u-boot-align-size', entries) 850 entry = entries['u-boot-align-size'] 851 self.assertEqual(pos, entry.offset) 852 self.assertEqual(32, entry.size) 853 self.assertEqual(U_BOOT_DATA, entry.data) 854 self.assertEqual(U_BOOT_DATA + tools.GetBytes(0, 32 - len(U_BOOT_DATA)), 855 data[pos:pos + entry.size]) 856 pos += entry.size 857 858 # Fourth u-boot has an aligned end 859 self.assertIn('u-boot-align-end', entries) 860 entry = entries['u-boot-align-end'] 861 self.assertEqual(48, entry.offset) 862 self.assertEqual(16, entry.size) 863 self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)]) 864 self.assertEqual(U_BOOT_DATA + tools.GetBytes(0, 16 - len(U_BOOT_DATA)), 865 data[pos:pos + entry.size]) 866 pos += entry.size 867 868 # Fifth u-boot immediately afterwards 869 self.assertIn('u-boot-align-both', entries) 870 entry = entries['u-boot-align-both'] 871 self.assertEqual(64, entry.offset) 872 self.assertEqual(64, entry.size) 873 self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)]) 874 self.assertEqual(U_BOOT_DATA + tools.GetBytes(0, 64 - len(U_BOOT_DATA)), 875 data[pos:pos + entry.size]) 876 877 self.CheckNoGaps(entries) 878 self.assertEqual(128, image.size) 879 880 dtb = fdt.Fdt(out_dtb_fname) 881 dtb.Scan() 882 props = self._GetPropTree(dtb, ['size', 'offset', 'image-pos']) 883 expected = { 884 'image-pos': 0, 885 'offset': 0, 886 'size': 128, 887 888 'u-boot:image-pos': 0, 889 'u-boot:offset': 0, 890 'u-boot:size': 3 + 5 + len(U_BOOT_DATA), 891 892 'u-boot-align-size-nop:image-pos': 12, 893 'u-boot-align-size-nop:offset': 12, 894 'u-boot-align-size-nop:size': 4, 895 896 'u-boot-align-size:image-pos': 16, 897 'u-boot-align-size:offset': 16, 898 'u-boot-align-size:size': 32, 899 900 'u-boot-align-end:image-pos': 48, 901 'u-boot-align-end:offset': 48, 902 'u-boot-align-end:size': 16, 903 904 'u-boot-align-both:image-pos': 64, 905 'u-boot-align-both:offset': 64, 906 'u-boot-align-both:size': 64, 907 } 908 self.assertEqual(expected, props) 909 910 def testPackAlignPowerOf2(self): 911 """Test that invalid entry alignment is detected""" 912 with self.assertRaises(ValueError) as e: 913 self._DoTestFile('010_pack_align_power2.dts') 914 self.assertIn("Node '/binman/u-boot': Alignment 5 must be a power " 915 "of two", str(e.exception)) 916 917 def testPackAlignSizePowerOf2(self): 918 """Test that invalid entry size alignment is detected""" 919 with self.assertRaises(ValueError) as e: 920 self._DoTestFile('011_pack_align_size_power2.dts') 921 self.assertIn("Node '/binman/u-boot': Alignment size 55 must be a " 922 "power of two", str(e.exception)) 923 924 def testPackInvalidAlign(self): 925 """Test detection of an offset that does not match its alignment""" 926 with self.assertRaises(ValueError) as e: 927 self._DoTestFile('012_pack_inv_align.dts') 928 self.assertIn("Node '/binman/u-boot': Offset 0x5 (5) does not match " 929 "align 0x4 (4)", str(e.exception)) 930 931 def testPackInvalidSizeAlign(self): 932 """Test that invalid entry size alignment is detected""" 933 with self.assertRaises(ValueError) as e: 934 self._DoTestFile('013_pack_inv_size_align.dts') 935 self.assertIn("Node '/binman/u-boot': Size 0x5 (5) does not match " 936 "align-size 0x4 (4)", str(e.exception)) 937 938 def testPackOverlap(self): 939 """Test that overlapping regions are detected""" 940 with self.assertRaises(ValueError) as e: 941 self._DoTestFile('014_pack_overlap.dts') 942 self.assertIn("Node '/binman/u-boot-align': Offset 0x3 (3) overlaps " 943 "with previous entry '/binman/u-boot' ending at 0x4 (4)", 944 str(e.exception)) 945 946 def testPackEntryOverflow(self): 947 """Test that entries that overflow their size are detected""" 948 with self.assertRaises(ValueError) as e: 949 self._DoTestFile('015_pack_overflow.dts') 950 self.assertIn("Node '/binman/u-boot': Entry contents size is 0x4 (4) " 951 "but entry size is 0x3 (3)", str(e.exception)) 952 953 def testPackImageOverflow(self): 954 """Test that entries which overflow the image size are detected""" 955 with self.assertRaises(ValueError) as e: 956 self._DoTestFile('016_pack_image_overflow.dts') 957 self.assertIn("Section '/binman': contents size 0x4 (4) exceeds section " 958 "size 0x3 (3)", str(e.exception)) 959 960 def testPackImageSize(self): 961 """Test that the image size can be set""" 962 retcode = self._DoTestFile('017_pack_image_size.dts') 963 self.assertEqual(0, retcode) 964 self.assertIn('image', control.images) 965 image = control.images['image'] 966 self.assertEqual(7, image.size) 967 968 def testPackImageSizeAlign(self): 969 """Test that image size alignemnt works as expected""" 970 retcode = self._DoTestFile('018_pack_image_align.dts') 971 self.assertEqual(0, retcode) 972 self.assertIn('image', control.images) 973 image = control.images['image'] 974 self.assertEqual(16, image.size) 975 976 def testPackInvalidImageAlign(self): 977 """Test that invalid image alignment is detected""" 978 with self.assertRaises(ValueError) as e: 979 self._DoTestFile('019_pack_inv_image_align.dts') 980 self.assertIn("Section '/binman': Size 0x7 (7) does not match " 981 "align-size 0x8 (8)", str(e.exception)) 982 983 def testPackAlignPowerOf2(self): 984 """Test that invalid image alignment is detected""" 985 with self.assertRaises(ValueError) as e: 986 self._DoTestFile('020_pack_inv_image_align_power2.dts') 987 self.assertIn("Image '/binman': Alignment size 131 must be a power of " 988 "two", str(e.exception)) 989 990 def testImagePadByte(self): 991 """Test that the image pad byte can be specified""" 992 self._SetupSplElf() 993 data = self._DoReadFile('021_image_pad.dts') 994 self.assertEqual(U_BOOT_SPL_DATA + tools.GetBytes(0xff, 1) + 995 U_BOOT_DATA, data) 996 997 def testImageName(self): 998 """Test that image files can be named""" 999 retcode = self._DoTestFile('022_image_name.dts') 1000 self.assertEqual(0, retcode)
1001 image = control.images['image1'] 1002 fname = tools.GetOutputFilename('test-name') 1003 self.assertTrue(os.path.exists(fname)) 1004 1005 image = control.images['image2'] 1006 fname = tools.GetOutputFilename('test-name.xx') 1007 self.assertTrue(os.path.exists(fname)) 1008 1009 def testBlobFilename(self): 1010 """Test that generic blobs can be provided by filename""" 1011 data = self._DoReadFile('023_blob.dts') 1012 self.assertEqual(BLOB_DATA, data) 1013 1014 def testPackSorted(self): 1015 """Test that entries can be sorted""" 1016 self._SetupSplElf() 1017 data = self._DoReadFile('024_sorted.dts') 1018 self.assertEqual(tools.GetBytes(0, 1) + U_BOOT_SPL_DATA + 1019 tools.GetBytes(0, 2) + U_BOOT_DATA, data) 1020 1021 def testPackZeroOffset(self): 1022 """Test that an entry at offset 0 is not given a new offset""" 1023 with self.assertRaises(ValueError) as e: 1024 self._DoTestFile('025_pack_zero_size.dts') 1025 self.assertIn("Node '/binman/u-boot-spl': Offset 0x0 (0) overlaps " 1026 "with previous entry '/binman/u-boot' ending at 0x4 (4)", 1027 str(e.exception)) 1028 1029 def testPackUbootDtb(self): 1030 """Test that a device tree can be added to U-Boot""" 1031 data = self._DoReadFile('026_pack_u_boot_dtb.dts') 1032 self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA, data) 1033 1034 def testPackX86RomNoSize(self): 1035 """Test that the end-at-4gb property requires a size property""" 1036 with self.assertRaises(ValueError) as e: 1037 self._DoTestFile('027_pack_4gb_no_size.dts') 1038 self.assertIn("Image '/binman': Section size must be provided when " 1039 "using end-at-4gb", str(e.exception)) 1040 1041 def test4gbAndSkipAtStartTogether(self): 1042 """Test that the end-at-4gb and skip-at-size property can't be used 1043 together""" 1044 with self.assertRaises(ValueError) as e: 1045 self._DoTestFile('098_4gb_and_skip_at_start_together.dts') 1046 self.assertIn("Image '/binman': Provide either 'end-at-4gb' or " 1047 "'skip-at-start'", str(e.exception)) 1048 1049 def testPackX86RomOutside(self): 1050 """Test that the end-at-4gb property checks for offset boundaries""" 1051 with self.assertRaises(ValueError) as e: 1052 self._DoTestFile('028_pack_4gb_outside.dts') 1053 self.assertIn("Node '/binman/u-boot': Offset 0x0 (0) size 0x4 (4) " 1054 "is outside the section '/binman' starting at " 1055 '0xffffffe0 (4294967264) of size 0x20 (32)', 1056 str(e.exception)) 1057 1058 def testPackX86Rom(self): 1059 """Test that a basic x86 ROM can be created""" 1060 self._SetupSplElf() 1061 data = self._DoReadFile('029_x86_rom.dts') 1062 self.assertEqual(U_BOOT_DATA + tools.GetBytes(0, 3) + U_BOOT_SPL_DATA + 1063 tools.GetBytes(0, 2), data) 1064 1065 def testPackX86RomMeNoDesc(self): 1066 """Test that an invalid Intel descriptor entry is detected""" 1067 try: 1068 TestFunctional._MakeInputFile('descriptor-empty.bin', b'') 1069 with self.assertRaises(ValueError) as e: 1070 self._DoTestFile('163_x86_rom_me_empty.dts') 1071 self.assertIn("Node '/binman/intel-descriptor': Cannot find Intel Flash Descriptor (FD) signature", 1072 str(e.exception)) 1073 finally: 1074 self._SetupDescriptor() 1075 1076 def testPackX86RomBadDesc(self): 1077 """Test that the Intel requires a descriptor entry""" 1078 with self.assertRaises(ValueError) as e: 1079 self._DoTestFile('030_x86_rom_me_no_desc.dts') 1080 self.assertIn("Node '/binman/intel-me': No offset set with " 1081 "offset-unset: should another entry provide this correct " 1082 "offset?", str(e.exception)) 1083 1084 def testPackX86RomMe(self): 1085 """Test that an x86 ROM with an ME region can be created""" 1086 data = self._DoReadFile('031_x86_rom_me.dts') 1087 expected_desc = tools.ReadFile(self.TestFile('descriptor.bin')) 1088 if data[:0x1000] != expected_desc: 1089 self.fail('Expected descriptor binary at start of image') 1090 self.assertEqual(ME_DATA, data[0x1000:0x1000 + len(ME_DATA)]) 1091 1092 def testPackVga(self): 1093 """Test that an image with a VGA binary can be created""" 1094 data = self._DoReadFile('032_intel_vga.dts') 1095 self.assertEqual(VGA_DATA, data[:len(VGA_DATA)]) 1096 1097 def testPackStart16(self): 1098 """Test that an image with an x86 start16 region can be created""" 1099 data = self._DoReadFile('033_x86_start16.dts') 1100 self.assertEqual(X86_START16_DATA, data[:len(X86_START16_DATA)]) 1101 1102 def testPackPowerpcMpc85xxBootpgResetvec(self): 1103 """Test that an image with powerpc-mpc85xx-bootpg-resetvec can be 1104 created""" 1105 data = self._DoReadFile('150_powerpc_mpc85xx_bootpg_resetvec.dts') 1106 self.assertEqual(PPC_MPC85XX_BR_DATA, data[:len(PPC_MPC85XX_BR_DATA)]) 1107 1108 def _RunMicrocodeTest(self, dts_fname, nodtb_data, ucode_second=False): 1109 """Handle running a test for insertion of microcode 1110 1111 Args: 1112 dts_fname: Name of test .dts file 1113 nodtb_data: Data that we expect in the first section 1114 ucode_second: True if the microsecond entry is second instead of 1115 third 1116 1117 Returns: 1118 Tuple: 1119 Contents of first region (U-Boot or SPL) 1120 Offset and size components of microcode pointer, as inserted 1121 in the above (two 4-byte words) 1122 """ 1123 data = self._DoReadFile(dts_fname, True) 1124 1125 # Now check the device tree has no microcode 1126 if ucode_second: 1127 ucode_content = data[len(nodtb_data):] 1128 ucode_pos = len(nodtb_data) 1129 dtb_with_ucode = ucode_content[16:] 1130 fdt_len = self.GetFdtLen(dtb_with_ucode) 1131 else: 1132 dtb_with_ucode = data[len(nodtb_data):] 1133 fdt_len = self.GetFdtLen(dtb_with_ucode) 1134 ucode_content = dtb_with_ucode[fdt_len:] 1135 ucode_pos = len(nodtb_data) + fdt_len 1136 fname = tools.GetOutputFilename('test.dtb') 1137 with open(fname, 'wb') as fd: 1138 fd.write(dtb_with_ucode) 1139 dtb = fdt.FdtScan(fname) 1140 ucode = dtb.GetNode('/microcode') 1141 self.assertTrue(ucode) 1142 for node in ucode.subnodes: 1143 self.assertFalse(node.props.get('data')) 1144 1145 # Check that the microcode appears immediately after the Fdt 1146 # This matches the concatenation of the data properties in 1147 # the /microcode/update@xxx nodes in 34_x86_ucode.dts. 1148 ucode_data = struct.pack('>4L', 0x12345678, 0x12345679, 0xabcd0000, 1149 0x78235609) 1150 self.assertEqual(ucode_data, ucode_content[:len(ucode_data)]) 1151 1152 # Check that the microcode pointer was inserted. It should match the 1153 # expected offset and size 1154 pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos, 1155 len(ucode_data)) 1156 u_boot = data[:len(nodtb_data)] 1157 return u_boot, pos_and_size 1158 1159 def testPackUbootMicrocode(self): 1160 """Test that x86 microcode can be handled correctly 1161 1162 We expect to see the following in the image, in order: 1163 u-boot-nodtb.bin with a microcode pointer inserted at the correct 1164 place 1165 u-boot.dtb with the microcode removed 1166 the microcode 1167 """ 1168 first, pos_and_size = self._RunMicrocodeTest('034_x86_ucode.dts', 1169 U_BOOT_NODTB_DATA) 1170 self.assertEqual(b'nodtb with microcode' + pos_and_size + 1171 b' somewhere in here', first) 1172 1173 def _RunPackUbootSingleMicrocode(self): 1174 """Test that x86 microcode can be handled correctly 1175 1176 We expect to see the following in the image, in order: 1177 u-boot-nodtb.bin with a microcode pointer inserted at the correct 1178 place 1179 u-boot.dtb with the microcode 1180 an empty microcode region 1181 """ 1182 # We need the libfdt library to run this test since only that allows 1183 # finding the offset of a property. This is required by 1184 # Entry_u_boot_dtb_with_ucode.ObtainContents(). 1185 data = self._DoReadFile('035_x86_single_ucode.dts', True) 1186 1187 second = data[len(U_BOOT_NODTB_DATA):] 1188 1189 fdt_len = self.GetFdtLen(second) 1190 third = second[fdt_len:] 1191 second = second[:fdt_len] 1192 1193 ucode_data = struct.pack('>2L', 0x12345678, 0x12345679) 1194 self.assertIn(ucode_data, second) 1195 ucode_pos = second.find(ucode_data) + len(U_BOOT_NODTB_DATA) 1196 1197 # Check that the microcode pointer was inserted. It should match the 1198 # expected offset and size 1199 pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos, 1200 len(ucode_data)) 1201 first = data[:len(U_BOOT_NODTB_DATA)] 1202 self.assertEqual(b'nodtb with microcode' + pos_and_size + 1203 b' somewhere in here', first) 1204 1205 def testPackUbootSingleMicrocode(self): 1206 """Test that x86 microcode can be handled correctly with fdt_normal. 1207 """ 1208 self._RunPackUbootSingleMicrocode() 1209 1210 def testUBootImg(self): 1211 """Test that u-boot.img can be put in a file""" 1212 data = self._DoReadFile('036_u_boot_img.dts') 1213 self.assertEqual(U_BOOT_IMG_DATA, data) 1214 1215 def testNoMicrocode(self): 1216 """Test that a missing microcode region is detected""" 1217 with self.assertRaises(ValueError) as e: 1218 self._DoReadFile('037_x86_no_ucode.dts', True) 1219 self.assertIn("Node '/binman/u-boot-dtb-with-ucode': No /microcode " 1220 "node found in ", str(e.exception)) 1221 1222 def testMicrocodeWithoutNode(self): 1223 """Test that a missing u-boot-dtb-with-ucode node is detected""" 1224 with self.assertRaises(ValueError) as e: 1225 self._DoReadFile('038_x86_ucode_missing_node.dts', True) 1226 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find " 1227 "microcode region u-boot-dtb-with-ucode", str(e.exception)) 1228 1229 def testMicrocodeWithoutNode2(self): 1230 """Test that a missing u-boot-ucode node is detected""" 1231 with self.assertRaises(ValueError) as e: 1232 self._DoReadFile('039_x86_ucode_missing_node2.dts', True) 1233 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find " 1234 "microcode region u-boot-ucode", str(e.exception)) 1235 1236 def testMicrocodeWithoutPtrInElf(self): 1237 """Test that a U-Boot binary without the microcode symbol is detected""" 1238 # ELF file without a '_dt_ucode_base_size' symbol 1239 try: 1240 TestFunctional._MakeInputFile('u-boot', 1241 tools.ReadFile(self.ElfTestFile('u_boot_no_ucode_ptr'))) 1242 1243 with self.assertRaises(ValueError) as e: 1244 self._RunPackUbootSingleMicrocode() 1245 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot locate " 1246 "_dt_ucode_base_size symbol in u-boot", str(e.exception)) 1247 1248 finally: 1249 # Put the original file back 1250 TestFunctional._MakeInputFile('u-boot', 1251 tools.ReadFile(self.ElfTestFile('u_boot_ucode_ptr'))) 1252 1253 def testMicrocodeNotInImage(self): 1254 """Test that microcode must be placed within the image""" 1255 with self.assertRaises(ValueError) as e: 1256 self._DoReadFile('040_x86_ucode_not_in_image.dts', True) 1257 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Microcode " 1258 "pointer _dt_ucode_base_size at fffffe14 is outside the " 1259 "section ranging from 00000000 to 0000002e", str(e.exception)) 1260 1261 def testWithoutMicrocode(self): 1262 """Test that we can cope with an image without microcode (e.g. qemu)""" 1263 TestFunctional._MakeInputFile('u-boot', 1264 tools.ReadFile(self.ElfTestFile('u_boot_no_ucode_ptr'))) 1265 data, dtb, _, _ = self._DoReadFileDtb('044_x86_optional_ucode.dts', True) 1266 1267 # Now check the device tree has no microcode 1268 self.assertEqual(U_BOOT_NODTB_DATA, data[:len(U_BOOT_NODTB_DATA)]) 1269 second = data[len(U_BOOT_NODTB_DATA):] 1270 1271 fdt_len = self.GetFdtLen(second) 1272 self.assertEqual(dtb, second[:fdt_len]) 1273 1274 used_len = len(U_BOOT_NODTB_DATA) + fdt_len 1275 third = data[used_len:] 1276 self.assertEqual(tools.GetBytes(0, 0x200 - used_len), third) 1277 1278 def testUnknownPosSize(self): 1279 """Test that microcode must be placed within the image""" 1280 with self.assertRaises(ValueError) as e: 1281 self._DoReadFile('041_unknown_pos_size.dts', True) 1282 self.assertIn("Section '/binman': Unable to set offset/size for unknown " 1283 "entry 'invalid-entry'", str(e.exception)) 1284 1285 def testPackFsp(self): 1286 """Test that an image with a FSP binary can be created""" 1287 data = self._DoReadFile('042_intel_fsp.dts') 1288 self.assertEqual(FSP_DATA, data[:len(FSP_DATA)]) 1289 1290 def testPackCmc(self): 1291 """Test that an image with a CMC binary can be created""" 1292 data = self._DoReadFile('043_intel_cmc.dts') 1293 self.assertEqual(CMC_DATA, data[:len(CMC_DATA)]) 1294 1295 def testPackVbt(self): 1296 """Test that an image with a VBT binary can be created""" 1297 data = self._DoReadFile('046_intel_vbt.dts') 1298 self.assertEqual(VBT_DATA, data[:len(VBT_DATA)]) 1299 1300 def testSplBssPad(self): 1301 """Test that we can pad SPL's BSS with zeros""" 1302 # ELF file with a '__bss_size' symbol 1303 self._SetupSplElf() 1304 data = self._DoReadFile('047_spl_bss_pad.dts') 1305 self.assertEqual(U_BOOT_SPL_DATA + tools.GetBytes(0, 10) + U_BOOT_DATA, 1306 data) 1307 1308 def testSplBssPadMissing(self): 1309 """Test that a missing symbol is detected""" 1310 self._SetupSplElf('u_boot_ucode_ptr') 1311 with self.assertRaises(ValueError) as e: 1312 self._DoReadFile('047_spl_bss_pad.dts') 1313 self.assertIn('Expected __bss_size symbol in spl/u-boot-spl', 1314 str(e.exception)) 1315 1316 def testPackStart16Spl(self): 1317 """Test that an image with an x86 start16 SPL region can be created""" 1318 data = self._DoReadFile('048_x86_start16_spl.dts') 1319 self.assertEqual(X86_START16_SPL_DATA, data[:len(X86_START16_SPL_DATA)]) 1320 1321 def _PackUbootSplMicrocode(self, dts, ucode_second=False): 1322 """Helper function for microcode tests 1323 1324 We expect to see the following in the image, in order: 1325 u-boot-spl-nodtb.bin with a microcode pointer inserted at the 1326 correct place 1327 u-boot.dtb with the microcode removed 1328 the microcode 1329 1330 Args: 1331 dts: Device tree file to use for test 1332 ucode_second: True if the microsecond entry is second instead of 1333 third 1334 """ 1335 self._SetupSplElf('u_boot_ucode_ptr') 1336 first, pos_and_size = self._RunMicrocodeTest(dts, U_BOOT_SPL_NODTB_DATA, 1337 ucode_second=ucode_second) 1338 self.assertEqual(b'splnodtb with microc' + pos_and_size + 1339 b'ter somewhere in here', first) 1340 1341 def testPackUbootSplMicrocode(self): 1342 """Test that x86 microcode can be handled correctly in SPL""" 1343 self._PackUbootSplMicrocode('049_x86_ucode_spl.dts') 1344 1345 def testPackUbootSplMicrocodeReorder(self): 1346 """Test that order doesn't matter for microcode entries 1347 1348 This is the same as testPackUbootSplMicrocode but when we process the 1349 u-boot-ucode entry we have not yet seen the u-boot-dtb-with-ucode 1350 entry, so we reply on binman to try later. 1351 """ 1352 self._PackUbootSplMicrocode('058_x86_ucode_spl_needs_retry.dts', 1353 ucode_second=True) 1354 1355 def testPackMrc(self): 1356 """Test that an image with an MRC binary can be created""" 1357 data = self._DoReadFile('050_intel_mrc.dts') 1358 self.assertEqual(MRC_DATA, data[:len(MRC_DATA)]) 1359 1360 def testSplDtb(self): 1361 """Test that an image with spl/u-boot-spl.dtb can be created""" 1362 data = self._DoReadFile('051_u_boot_spl_dtb.dts') 1363 self.assertEqual(U_BOOT_SPL_DTB_DATA, data[:len(U_BOOT_SPL_DTB_DATA)]) 1364 1365 def testSplNoDtb(self): 1366 """Test that an image with spl/u-boot-spl-nodtb.bin can be created""" 1367 self._SetupSplElf() 1368 data = self._DoReadFile('052_u_boot_spl_nodtb.dts') 1369 self.assertEqual(U_BOOT_SPL_NODTB_DATA, data[:len(U_BOOT_SPL_NODTB_DATA)]) 1370 1371 def checkSymbols(self, dts, base_data, u_boot_offset, entry_args=None, 1372 use_expanded=False): 1373 """Check the image contains the expected symbol values 1374 1375 Args: 1376 dts: Device tree file to use for test 1377 base_data: Data before and after 'u-boot' section 1378 u_boot_offset: Offset of 'u-boot' section in image 1379 entry_args: Dict of entry args to supply to binman 1380 key: arg name 1381 value: value of that arg 1382 use_expanded: True to use expanded entries where available, e.g. 1383 'u-boot-expanded' instead of 'u-boot' 1384 """ 1385 elf_fname = self.ElfTestFile('u_boot_binman_syms') 1386 syms = elf.GetSymbols(elf_fname, ['binman', 'image']) 1387 addr = elf.GetSymbolAddress(elf_fname, '__image_copy_start') 1388 self.assertEqual(syms['_binman_u_boot_spl_any_prop_offset'].address, 1389 addr) 1390 1391 self._SetupSplElf('u_boot_binman_syms') 1392 data = self._DoReadFileDtb(dts, entry_args=entry_args, 1393 use_expanded=use_expanded)[0] 1394 # The image should contain the symbols from u_boot_binman_syms.c 1395 # Note that image_pos is adjusted by the base address of the image, 1396 # which is 0x10 in our test image 1397 sym_values = struct.pack('<LQLL', 0x00, 1398 u_boot_offset + len(U_BOOT_DATA), 1399 0x10 + u_boot_offset, 0x04) 1400 expected = (sym_values + base_data[20:] + 1401 tools.GetBytes(0xff, 1) + U_BOOT_DATA + sym_values + 1402 base_data[20:]) 1403 self.assertEqual(expected, data) 1404 1405 def testSymbols(self): 1406 """Test binman can assign symbols embedded in U-Boot""" 1407 self.checkSymbols('053_symbols.dts', U_BOOT_SPL_DATA, 0x18) 1408 1409 def testSymbolsNoDtb(self): 1410 """Test binman can assign symbols embedded in U-Boot SPL""" 1411 self.checkSymbols('196_symbols_nodtb.dts', 1412 U_BOOT_SPL_NODTB_DATA + U_BOOT_SPL_DTB_DATA, 1413 0x38) 1414 1415 def testPackUnitAddress(self): 1416 """Test that we support multiple binaries with the same name""" 1417 data = self._DoReadFile('054_unit_address.dts') 1418 self.assertEqual(U_BOOT_DATA + U_BOOT_DATA, data) 1419 1420 def testSections(self): 1421 """Basic test of sections""" 1422 data = self._DoReadFile('055_sections.dts') 1423 expected = (U_BOOT_DATA + tools.GetBytes(ord('!'), 12) + 1424 U_BOOT_DATA + tools.GetBytes(ord('a'), 12) + 1425 U_BOOT_DATA + tools.GetBytes(ord('&'), 4)) 1426 self.assertEqual(expected, data) 1427 1428 def testMap(self): 1429 """Tests outputting a map of the images""" 1430 _, _, map_data, _ = self._DoReadFileDtb('055_sections.dts', map=True) 1431 self.assertEqual('''ImagePos Offset Size Name 143200000000 00000000 00000028 main-section 143300000000 00000000 00000010 section@0 143400000000 00000000 00000004 u-boot 143500000010 00000010 00000010 section@1 143600000010 00000000 00000004 u-boot 143700000020 00000020 00000004 section@2 143800000020 00000000 00000004 u-boot 1439''', map_data) 1440 1441 def testNamePrefix(self): 1442 """Tests that name prefixes are used""" 1443 _, _, map_data, _ = self._DoReadFileDtb('056_name_prefix.dts', map=True) 1444 self.assertEqual('''ImagePos Offset Size Name 144500000000 00000000 00000028 main-section 144600000000 00000000 00000010 section@0 144700000000 00000000 00000004 ro-u-boot 144800000010 00000010 00000010 section@1 144900000010 00000000 00000004 rw-u-boot 1450''', map_data) 1451 1452 def testUnknownContents(self): 1453 """Test that obtaining the contents works as expected""" 1454 with self.assertRaises(ValueError) as e: 1455 self._DoReadFile('057_unknown_contents.dts', True) 1456 self.assertIn("Image '/binman': Internal error: Could not complete " 1457 "processing of contents: remaining [" 1458 "<binman.etype._testing.Entry__testing ", str(e.exception)) 1459 1460 def testBadChangeSize(self): 1461 """Test that trying to change the size of an entry fails""" 1462 try: 1463 state.SetAllowEntryExpansion(False) 1464 with self.assertRaises(ValueError) as e: 1465 self._DoReadFile('059_change_size.dts', True) 1466 self.assertIn("Node '/binman/_testing': Cannot update entry size from 2 to 3", 1467 str(e.exception)) 1468 finally: 1469 state.SetAllowEntryExpansion(True) 1470 1471 def testUpdateFdt(self): 1472 """Test that we can update the device tree with offset/size info""" 1473 _, _, _, out_dtb_fname = self._DoReadFileDtb('060_fdt_update.dts', 1474 update_dtb=True) 1475 dtb = fdt.Fdt(out_dtb_fname) 1476 dtb.Scan() 1477 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS) 1478 self.assertEqual({ 1479 'image-pos': 0, 1480 'offset': 0, 1481 '_testing:offset': 32, 1482 '_testing:size': 2, 1483 '_testing:image-pos': 32, 1484 'section@0/u-boot:offset': 0, 1485 'section@0/u-boot:size': len(U_BOOT_DATA), 1486 'section@0/u-boot:image-pos': 0, 1487 'section@0:offset': 0, 1488 'section@0:size': 16, 1489 'section@0:image-pos': 0, 1490 1491 'section@1/u-boot:offset': 0, 1492 'section@1/u-boot:size': len(U_BOOT_DATA), 1493 'section@1/u-boot:image-pos': 16, 1494 'section@1:offset': 16, 1495 'section@1:size': 16, 1496 'section@1:image-pos': 16, 1497 'size': 40 1498 }, props) 1499 1500 def testUpdateFdtBad(self): 1501 """Test that we detect when ProcessFdt never completes""" 1502 with self.assertRaises(ValueError) as e: 1503 self._DoReadFileDtb('061_fdt_update_bad.dts', update_dtb=True) 1504 self.assertIn('Could not complete processing of Fdt: remaining ' 1505 '[<binman.etype._testing.Entry__testing', 1506 str(e.exception)) 1507 1508 def testEntryArgs(self): 1509 """Test passing arguments to entries from the command line""" 1510 entry_args = { 1511 'test-str-arg': 'test1', 1512 'test-int-arg': '456', 1513 } 1514 self._DoReadFileDtb('062_entry_args.dts', entry_args=entry_args) 1515 self.assertIn('image', control.images) 1516 entry = control.images['image'].GetEntries()['_testing'] 1517 self.assertEqual('test0', entry.test_str_fdt) 1518 self.assertEqual('test1', entry.test_str_arg) 1519 self.assertEqual(123, entry.test_int_fdt) 1520 self.assertEqual(456, entry.test_int_arg) 1521 1522 def testEntryArgsMissing(self): 1523 """Test missing arguments and properties""" 1524 entry_args = { 1525 'test-int-arg': '456', 1526 } 1527 self._DoReadFileDtb('063_entry_args_missing.dts', entry_args=entry_args) 1528 entry = control.images['image'].GetEntries()['_testing'] 1529 self.assertEqual('test0', entry.test_str_fdt) 1530 self.assertEqual(None, entry.test_str_arg) 1531 self.assertEqual(None, entry.test_int_fdt) 1532 self.assertEqual(456, entry.test_int_arg) 1533 1534 def testEntryArgsRequired(self): 1535 """Test missing arguments and properties""" 1536 entry_args = { 1537 'test-int-arg': '456', 1538 } 1539 with self.assertRaises(ValueError) as e: 1540 self._DoReadFileDtb('064_entry_args_required.dts') 1541 self.assertIn("Node '/binman/_testing': " 1542 'Missing required properties/entry args: test-str-arg, ' 1543 'test-int-fdt, test-int-arg', 1544 str(e.exception)) 1545 1546 def testEntryArgsInvalidFormat(self): 1547 """Test that an invalid entry-argument format is detected""" 1548 args = ['build', '-d', self.TestFile('064_entry_args_required.dts'), 1549 '-ano-value'] 1550 with self.assertRaises(ValueError) as e: 1551 self._DoBinman(*args) 1552 self.assertIn("Invalid entry arguemnt 'no-value'", str(e.exception)) 1553 1554 def testEntryArgsInvalidInteger(self): 1555 """Test that an invalid entry-argument integer is detected""" 1556 entry_args = { 1557 'test-int-arg': 'abc', 1558 } 1559 with self.assertRaises(ValueError) as e: 1560 self._DoReadFileDtb('062_entry_args.dts', entry_args=entry_args) 1561 self.assertIn("Node '/binman/_testing': Cannot convert entry arg " 1562 "'test-int-arg' (value 'abc') to integer", 1563 str(e.exception)) 1564 1565 def testEntryArgsInvalidDatatype(self): 1566 """Test that an invalid entry-argument datatype is detected 1567 1568 This test could be written in entry_test.py except that it needs 1569 access to control.entry_args, which seems more than that module should 1570 be able to see. 1571 """ 1572 entry_args = { 1573 'test-bad-datatype-arg': '12', 1574 } 1575 with self.assertRaises(ValueError) as e: 1576 self._DoReadFileDtb('065_entry_args_unknown_datatype.dts', 1577 entry_args=entry_args) 1578 self.assertIn('GetArg() internal error: Unknown data type ', 1579 str(e.exception)) 1580 1581 def testText(self): 1582 """Test for a text entry type""" 1583 entry_args = { 1584 'test-id': TEXT_DATA, 1585 'test-id2': TEXT_DATA2, 1586 'test-id3': TEXT_DATA3, 1587 } 1588 data, _, _, _ = self._DoReadFileDtb('066_text.dts', 1589 entry_args=entry_args) 1590 expected = (tools.ToBytes(TEXT_DATA) + 1591 tools.GetBytes(0, 8 - len(TEXT_DATA)) + 1592 tools.ToBytes(TEXT_DATA2) + tools.ToBytes(TEXT_DATA3) + 1593 b'some text' + b'more text') 1594 self.assertEqual(expected, data) 1595 1596 def testEntryDocs(self): 1597 """Test for creation of entry documentation""" 1598 with test_util.capture_sys_output() as (stdout, stderr): 1599 control.WriteEntryDocs(control.GetEntryModules()) 1600 self.assertTrue(len(stdout.getvalue()) > 0) 1601 1602 def testEntryDocsMissing(self): 1603 """Test handling of missing entry documentation""" 1604 with self.assertRaises(ValueError) as e: 1605 with test_util.capture_sys_output() as (stdout, stderr): 1606 control.WriteEntryDocs(control.GetEntryModules(), 'u_boot') 1607 self.assertIn('Documentation is missing for modules: u_boot', 1608 str(e.exception)) 1609 1610 def testFmap(self): 1611 """Basic test of generation of a flashrom fmap""" 1612 data = self._DoReadFile('067_fmap.dts') 1613 fhdr, fentries = fmap_util.DecodeFmap(data[32:]) 1614 expected = (U_BOOT_DATA + tools.GetBytes(ord('!'), 12) + 1615 U_BOOT_DATA + tools.GetBytes(ord('a'), 12)) 1616 self.assertEqual(expected, data[:32]) 1617 self.assertEqual(b'__FMAP__', fhdr.signature) 1618 self.assertEqual(1, fhdr.ver_major) 1619 self.assertEqual(0, fhdr.ver_minor) 1620 self.assertEqual(0, fhdr.base) 1621 expect_size = fmap_util.FMAP_HEADER_LEN + fmap_util.FMAP_AREA_LEN * 5 1622 self.assertEqual(16 + 16 + expect_size, fhdr.image_size) 1623 self.assertEqual(b'FMAP', fhdr.name) 1624 self.assertEqual(5, fhdr.nareas) 1625 fiter = iter(fentries) 1626 1627 fentry = next(fiter) 1628 self.assertEqual(b'SECTION0', fentry.name) 1629 self.assertEqual(0, fentry.offset) 1630 self.assertEqual(16, fentry.size) 1631 self.assertEqual(0, fentry.flags) 1632 1633 fentry = next(fiter) 1634 self.assertEqual(b'RO_U_BOOT', fentry.name) 1635 self.assertEqual(0, fentry.offset) 1636 self.assertEqual(4, fentry.size) 1637 self.assertEqual(0, fentry.flags) 1638 1639 fentry = next(fiter) 1640 self.assertEqual(b'SECTION1', fentry.name) 1641 self.assertEqual(16, fentry.offset) 1642 self.assertEqual(16, fentry.size) 1643 self.assertEqual(0, fentry.flags) 1644 1645 fentry = next(fiter) 1646 self.assertEqual(b'RW_U_BOOT', fentry.name) 1647 self.assertEqual(16, fentry.offset) 1648 self.assertEqual(4, fentry.size) 1649 self.assertEqual(0, fentry.flags) 1650 1651 fentry = next(fiter) 1652 self.assertEqual(b'FMAP', fentry.name) 1653 self.assertEqual(32, fentry.offset) 1654 self.assertEqual(expect_size, fentry.size) 1655 self.assertEqual(0, fentry.flags) 1656 1657 def testBlobNamedByArg(self): 1658 """Test we can add a blob with the filename coming from an entry arg""" 1659 entry_args = { 1660 'cros-ec-rw-path': 'ecrw.bin', 1661 } 1662 self._DoReadFileDtb('068_blob_named_by_arg.dts', entry_args=entry_args) 1663 1664 def testFill(self): 1665 """Test for an fill entry type""" 1666 data = self._DoReadFile('069_fill.dts') 1667 expected = tools.GetBytes(0xff, 8) + tools.GetBytes(0, 8) 1668 self.assertEqual(expected, data) 1669 1670 def testFillNoSize(self): 1671 """Test for an fill entry type with no size""" 1672 with self.assertRaises(ValueError) as e: 1673 self._DoReadFile('070_fill_no_size.dts') 1674 self.assertIn("'fill' entry must have a size property", 1675 str(e.exception)) 1676 1677 def _HandleGbbCommand(self, pipe_list): 1678 """Fake calls to the futility utility""" 1679 if pipe_list[0][0] == 'futility': 1680 fname = pipe_list[0][-1] 1681 # Append our GBB data to the file, which will happen every time the 1682 # futility command is called. 1683 with open(fname, 'ab') as fd: 1684 fd.write(GBB_DATA) 1685 return command.CommandResult() 1686 1687 def testGbb(self): 1688 """Test for the Chromium OS Google Binary Block""" 1689 command.test_result = self._HandleGbbCommand 1690 entry_args = { 1691 'keydir': 'devkeys', 1692 'bmpblk': 'bmpblk.bin', 1693 } 1694 data, _, _, _ = self._DoReadFileDtb('071_gbb.dts', entry_args=entry_args) 1695 1696 # Since futility 1697 expected = (GBB_DATA + GBB_DATA + tools.GetBytes(0, 8) + 1698 tools.GetBytes(0, 0x2180 - 16)) 1699 self.assertEqual(expected, data) 1700 1701 def testGbbTooSmall(self): 1702 """Test for the Chromium OS Google Binary Block being large enough""" 1703 with self.assertRaises(ValueError) as e: 1704 self._DoReadFileDtb('072_gbb_too_small.dts') 1705 self.assertIn("Node '/binman/gbb': GBB is too small", 1706 str(e.exception)) 1707 1708 def testGbbNoSize(self): 1709 """Test for the Chromium OS Google Binary Block having a size""" 1710 with self.assertRaises(ValueError) as e: 1711 self._DoReadFileDtb('073_gbb_no_size.dts') 1712 self.assertIn("Node '/binman/gbb': GBB must have a fixed size", 1713 str(e.exception)) 1714 1715 def _HandleVblockCommand(self, pipe_list): 1716 """Fake calls to the futility utility 1717 1718 The expected pipe is: 1719 1720 [('futility', 'vbutil_firmware', '--vblock', 1721 'vblock.vblock', '--keyblock', 'devkeys/firmware.keyblock', 1722 '--signprivate', 'devkeys/firmware_data_key.vbprivk', 1723 '--version', '1', '--fv', 'input.vblock', '--kernelkey', 1724 'devkeys/kernel_subkey.vbpubk', '--flags', '1')] 1725 1726 This writes to the output file (here, 'vblock.vblock'). If 1727 self._hash_data is False, it writes VBLOCK_DATA, else it writes a hash 1728 of the input data (here, 'input.vblock'). 1729 """ 1730 if pipe_list[0][0] == 'futility': 1731 fname = pipe_list[0][3] 1732 with open(fname, 'wb') as fd: 1733 if self._hash_data: 1734 infile = pipe_list[0][11] 1735 m = hashlib.sha256() 1736 data = tools.ReadFile(infile) 1737 m.update(data) 1738 fd.write(m.digest()) 1739 else: 1740 fd.write(VBLOCK_DATA) 1741 1742 return command.CommandResult() 1743 1744 def testVblock(self): 1745 """Test for the Chromium OS Verified Boot Block""" 1746 self._hash_data = False 1747 command.test_result = self._HandleVblockCommand 1748 entry_args = { 1749 'keydir': 'devkeys', 1750 } 1751 data, _, _, _ = self._DoReadFileDtb('074_vblock.dts', 1752 entry_args=entry_args) 1753 expected = U_BOOT_DATA + VBLOCK_DATA + U_BOOT_DTB_DATA 1754 self.assertEqual(expected, data) 1755 1756 def testVblockNoContent(self): 1757 """Test we detect a vblock which has no content to sign""" 1758 with self.assertRaises(ValueError) as e: 1759 self._DoReadFile('075_vblock_no_content.dts') 1760 self.assertIn("Node '/binman/vblock': Collection must have a 'content' " 1761 'property', str(e.exception)) 1762 1763 def testVblockBadPhandle(self): 1764 """Test that we detect a vblock with an invalid phandle in contents""" 1765 with self.assertRaises(ValueError) as e: 1766 self._DoReadFile('076_vblock_bad_phandle.dts') 1767 self.assertIn("Node '/binman/vblock': Cannot find node for phandle " 1768 '1000', str(e.exception)) 1769 1770 def testVblockBadEntry(self): 1771 """Test that we detect an entry that points to a non-entry""" 1772 with self.assertRaises(ValueError) as e: 1773 self._DoReadFile('077_vblock_bad_entry.dts') 1774 self.assertIn("Node '/binman/vblock': Cannot find entry for node " 1775 "'other'", str(e.exception)) 1776 1777 def testVblockContent(self): 1778 """Test that the vblock signs the right data""" 1779 self._hash_data = True 1780 command.test_result = self._HandleVblockCommand 1781 entry_args = { 1782 'keydir': 'devkeys', 1783 } 1784 data = self._DoReadFileDtb( 1785 '189_vblock_content.dts', use_real_dtb=True, update_dtb=True, 1786 entry_args=entry_args)[0] 1787 hashlen = 32 # SHA256 hash is 32 bytes 1788 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)]) 1789 hashval = data[-hashlen:] 1790 dtb = data[len(U_BOOT_DATA):-hashlen] 1791 1792 expected_data = U_BOOT_DATA + dtb 1793 1794 # The hashval should be a hash of the dtb 1795 m = hashlib.sha256() 1796 m.update(expected_data) 1797 expected_hashval = m.digest() 1798 self.assertEqual(expected_hashval, hashval) 1799 1800 def testTpl(self): 1801 """Test that an image with TPL and its device tree can be created""" 1802 # ELF file with a '__bss_size' symbol 1803 self._SetupTplElf() 1804 data = self._DoReadFile('078_u_boot_tpl.dts') 1805 self.assertEqual(U_BOOT_TPL_DATA + U_BOOT_TPL_DTB_DATA, data) 1806 1807 def testUsesPos(self): 1808 """Test that the 'pos' property cannot be used anymore""" 1809 with self.assertRaises(ValueError) as e: 1810 data = self._DoReadFile('079_uses_pos.dts') 1811 self.assertIn("Node '/binman/u-boot': Please use 'offset' instead of " 1812 "'pos'", str(e.exception)) 1813 1814 def testFillZero(self): 1815 """Test for an fill entry type with a size of 0""" 1816 data = self._DoReadFile('080_fill_empty.dts') 1817 self.assertEqual(tools.GetBytes(0, 16), data) 1818 1819 def testTextMissing(self): 1820 """Test for a text entry type where there is no text""" 1821 with self.assertRaises(ValueError) as e: 1822 self._DoReadFileDtb('066_text.dts',) 1823 self.assertIn("Node '/binman/text': No value provided for text label " 1824 "'test-id'", str(e.exception)) 1825 1826 def testPackStart16Tpl(self): 1827 """Test that an image with an x86 start16 TPL region can be created""" 1828 data = self._DoReadFile('081_x86_start16_tpl.dts') 1829 self.assertEqual(X86_START16_TPL_DATA, data[:len(X86_START16_TPL_DATA)]) 1830 1831 def testSelectImage(self): 1832 """Test that we can select which images to build""" 1833 expected = 'Skipping images: image1' 1834 1835 # We should only get the expected message in verbose mode 1836 for verbosity in (0, 2): 1837 with test_util.capture_sys_output() as (stdout, stderr): 1838 retcode = self._DoTestFile('006_dual_image.dts', 1839 verbosity=verbosity, 1840 images=['image2']) 1841 self.assertEqual(0, retcode) 1842 if verbosity: 1843 self.assertIn(expected, stdout.getvalue()) 1844 else: 1845 self.assertNotIn(expected, stdout.getvalue()) 1846 1847 self.assertFalse(os.path.exists(tools.GetOutputFilename('image1.bin'))) 1848 self.assertTrue(os.path.exists(tools.GetOutputFilename('image2.bin'))) 1849 self._CleanupOutputDir() 1850 1851 def testUpdateFdtAll(self): 1852 """Test that all device trees are updated with offset/size info""" 1853 data = self._DoReadFileRealDtb('082_fdt_update_all.dts') 1854 1855 base_expected = { 1856 'section:image-pos': 0, 1857 'u-boot-tpl-dtb:size': 513, 1858 'u-boot-spl-dtb:size': 513, 1859 'u-boot-spl-dtb:offset': 493, 1860 'image-pos': 0, 1861 'section/u-boot-dtb:image-pos': 0, 1862 'u-boot-spl-dtb:image-pos': 493, 1863 'section/u-boot-dtb:size': 493, 1864 'u-boot-tpl-dtb:image-pos': 1006, 1865 'section/u-boot-dtb:offset': 0, 1866 'section:size': 493, 1867 'offset': 0, 1868 'section:offset': 0, 1869 'u-boot-tpl-dtb:offset': 1006, 1870 'size': 1519 1871 } 1872 1873 # We expect three device-tree files in the output, one after the other. 1874 # Read them in sequence. We look for an 'spl' property in the SPL tree, 1875 # and 'tpl' in the TPL tree, to make sure they are distinct from the 1876 # main U-Boot tree. All three should have the same postions and offset. 1877 start = 0 1878 for item in ['', 'spl', 'tpl']: 1879 dtb = fdt.Fdt.FromData(data[start:]) 1880 dtb.Scan() 1881 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS + 1882 ['spl', 'tpl']) 1883 expected = dict(base_expected) 1884 if item: 1885 expected[item] = 0 1886 self.assertEqual(expected, props) 1887 start += dtb._fdt_obj.totalsize() 1888 1889 def testUpdateFdtOutput(self): 1890 """Test that output DTB files are updated""" 1891 try: 1892 data, dtb_data, _, _ = self._DoReadFileDtb('082_fdt_update_all.dts', 1893 use_real_dtb=True, update_dtb=True, reset_dtbs=False) 1894 1895 # Unfortunately, compiling a source file always results in a file 1896 # called source.dtb (see fdt_util.EnsureCompiled()). The test 1897 # source file (e.g. test/075_fdt_update_all.dts) thus does not enter 1898 # binman as a file called u-boot.dtb. To fix this, copy the file 1899 # over to the expected place. 1900 start = 0 1901 for fname in ['u-boot.dtb.out', 'spl/u-boot-spl.dtb.out', 1902 'tpl/u-boot-tpl.dtb.out']: 1903 dtb = fdt.Fdt.FromData(data[start:]) 1904 size = dtb._fdt_obj.totalsize() 1905 pathname = tools.GetOutputFilename(os.path.split(fname)[1]) 1906 outdata = tools.ReadFile(pathname) 1907 name = os.path.split(fname)[0] 1908 1909 if name: 1910 orig_indata = self._GetDtbContentsForSplTpl(dtb_data, name) 1911 else: 1912 orig_indata = dtb_data 1913 self.assertNotEqual(outdata, orig_indata, 1914 "Expected output file '%s' be updated" % pathname) 1915 self.assertEqual(outdata, data[start:start + size], 1916 "Expected output file '%s' to match output image" % 1917 pathname) 1918 start += size 1919 finally: 1920 self._ResetDtbs() 1921 1922 def _decompress(self, data): 1923 return tools.Decompress(data, 'lz4') 1924 1925 def testCompress(self): 1926 """Test compression of blobs""" 1927 self._CheckLz4() 1928 data, _, _, out_dtb_fname = self._DoReadFileDtb('083_compress.dts', 1929 use_real_dtb=True, update_dtb=True) 1930 dtb = fdt.Fdt(out_dtb_fname) 1931 dtb.Scan() 1932 props = self._GetPropTree(dtb, ['size', 'uncomp-size']) 1933 orig = self._decompress(data) 1934 self.assertEquals(COMPRESS_DATA, orig) 1935 1936 # Do a sanity check on various fields 1937 image = control.images['image'] 1938 entries = image.GetEntries() 1939 self.assertEqual(1, len(entries)) 1940 1941 entry = entries['blob'] 1942 self.assertEqual(COMPRESS_DATA, entry.uncomp_data) 1943 self.assertEqual(len(COMPRESS_DATA), entry.uncomp_size) 1944 orig = self._decompress(entry.data) 1945 self.assertEqual(orig, entry.uncomp_data) 1946 1947 self.assertEqual(image.data, entry.data) 1948 1949 expected = { 1950 'blob:uncomp-size': len(COMPRESS_DATA), 1951 'blob:size': len(data), 1952 'size': len(data), 1953 } 1954 self.assertEqual(expected, props) 1955 1956 def testFiles(self): 1957 """Test bringing in multiple files""" 1958 data = self._DoReadFile('084_files.dts') 1959 self.assertEqual(FILES_DATA, data) 1960 1961 def testFilesCompress(self): 1962 """Test bringing in multiple files and compressing them""" 1963 self._CheckLz4() 1964 data = self._DoReadFile('085_files_compress.dts') 1965 1966 image = control.images['image'] 1967 entries = image.GetEntries() 1968 files = entries['files'] 1969 entries = files._entries 1970 1971 orig = b'' 1972 for i in range(1, 3): 1973 key = '%d.dat' % i 1974 start = entries[key].image_pos 1975 len = entries[key].size 1976 chunk = data[start:start + len] 1977 orig += self._decompress(chunk) 1978 1979 self.assertEqual(FILES_DATA, orig) 1980 1981 def testFilesMissing(self): 1982 """Test missing files""" 1983 with self.assertRaises(ValueError) as e: 1984 data = self._DoReadFile('086_files_none.dts') 1985 self.assertIn("Node '/binman/files': Pattern \'files/*.none\' matched " 1986 'no files', str(e.exception)) 1987 1988 def testFilesNoPattern(self): 1989 """Test missing files""" 1990 with self.assertRaises(ValueError) as e: 1991 data = self._DoReadFile('087_files_no_pattern.dts') 1992 self.assertIn("Node '/binman/files': Missing 'pattern' property", 1993 str(e.exception)) 1994 1995 def testExpandSize(self): 1996 """Test an expanding entry""" 1997 data, _, map_data, _ = self._DoReadFileDtb('088_expand_size.dts', 1998 map=True) 1999 expect = (tools.GetBytes(ord('a'), 8) + U_BOOT_DATA + 2000 MRC_DATA + tools.GetBytes(ord('b'), 1) + U_BOOT_DATA +
2001 tools.GetBytes(ord('c'), 8) + U_BOOT_DATA + 2002 tools.GetBytes(ord('d'), 8)) 2003 self.assertEqual(expect, data) 2004 self.assertEqual('''ImagePos Offset Size Name 200500000000 00000000 00000028 main-section 200600000000 00000000 00000008 fill 200700000008 00000008 00000004 u-boot 20080000000c 0000000c 00000004 section 20090000000c 00000000 00000003 intel-mrc 201000000010 00000010 00000004 u-boot2 201100000014 00000014 0000000c section2 201200000014 00000000 00000008 fill 20130000001c 00000008 00000004 u-boot 201400000020 00000020 00000008 fill2 2015''', map_data) 2016 2017 def testExpandSizeBad(self): 2018 """Test an expanding entry which fails to provide contents""" 2019 with test_util.capture_sys_output() as (stdout, stderr): 2020 with self.assertRaises(ValueError) as e: 2021 self._DoReadFileDtb('089_expand_size_bad.dts', map=True) 2022 self.assertIn("Node '/binman/_testing': Cannot obtain contents when " 2023 'expanding entry', str(e.exception)) 2024 2025 def testHash(self): 2026 """Test hashing of the contents of an entry""" 2027 _, _, _, out_dtb_fname = self._DoReadFileDtb('090_hash.dts', 2028 use_real_dtb=True, update_dtb=True) 2029 dtb = fdt.Fdt(out_dtb_fname) 2030 dtb.Scan() 2031 hash_node = dtb.GetNode('/binman/u-boot/hash').props['value'] 2032 m = hashlib.sha256() 2033 m.update(U_BOOT_DATA) 2034 self.assertEqual(m.digest(), b''.join(hash_node.value)) 2035 2036 def testHashNoAlgo(self): 2037 with self.assertRaises(ValueError) as e: 2038 self._DoReadFileDtb('091_hash_no_algo.dts', update_dtb=True) 2039 self.assertIn("Node \'/binman/u-boot\': Missing \'algo\' property for " 2040 'hash node', str(e.exception)) 2041 2042 def testHashBadAlgo(self): 2043 with self.assertRaises(ValueError) as e: 2044 self._DoReadFileDtb('092_hash_bad_algo.dts', update_dtb=True) 2045 self.assertIn("Node '/binman/u-boot': Unknown hash algorithm", 2046 str(e.exception)) 2047 2048 def testHashSection(self): 2049 """Test hashing of the contents of an entry""" 2050 _, _, _, out_dtb_fname = self._DoReadFileDtb('099_hash_section.dts', 2051 use_real_dtb=True, update_dtb=True) 2052 dtb = fdt.Fdt(out_dtb_fname) 2053 dtb.Scan() 2054 hash_node = dtb.GetNode('/binman/section/hash').props['value'] 2055 m = hashlib.sha256() 2056 m.update(U_BOOT_DATA) 2057 m.update(tools.GetBytes(ord('a'), 16)) 2058 self.assertEqual(m.digest(), b''.join(hash_node.value)) 2059 2060 def testPackUBootTplMicrocode(self): 2061 """Test that x86 microcode can be handled correctly in TPL 2062 2063 We expect to see the following in the image, in order: 2064 u-boot-tpl-nodtb.bin with a microcode pointer inserted at the correct 2065 place 2066 u-boot-tpl.dtb with the microcode removed 2067 the microcode 2068 """ 2069 self._SetupTplElf('u_boot_ucode_ptr') 2070 first, pos_and_size = self._RunMicrocodeTest('093_x86_tpl_ucode.dts', 2071 U_BOOT_TPL_NODTB_DATA) 2072 self.assertEqual(b'tplnodtb with microc' + pos_and_size + 2073 b'ter somewhere in here', first) 2074 2075 def testFmapX86(self): 2076 """Basic test of generation of a flashrom fmap""" 2077 data = self._DoReadFile('094_fmap_x86.dts') 2078 fhdr, fentries = fmap_util.DecodeFmap(data[32:]) 2079 expected = U_BOOT_DATA + MRC_DATA + tools.GetBytes(ord('a'), 32 - 7) 2080 self.assertEqual(expected, data[:32]) 2081 fhdr, fentries = fmap_util.DecodeFmap(data[32:]) 2082 2083 self.assertEqual(0x100, fhdr.image_size) 2084 2085 self.assertEqual(0, fentries[0].offset) 2086 self.assertEqual(4, fentries[0].size) 2087 self.assertEqual(b'U_BOOT', fentries[0].name) 2088 2089 self.assertEqual(4, fentries[1].offset) 2090 self.assertEqual(3, fentries[1].size) 2091 self.assertEqual(b'INTEL_MRC', fentries[1].name) 2092 2093 self.assertEqual(32, fentries[2].offset) 2094 self.assertEqual(fmap_util.FMAP_HEADER_LEN + 2095 fmap_util.FMAP_AREA_LEN * 3, fentries[2].size) 2096 self.assertEqual(b'FMAP', fentries[2].name) 2097 2098 def testFmapX86Section(self): 2099 """Basic test of generation of a flashrom fmap""" 2100 data = self._DoReadFile('095_fmap_x86_section.dts') 2101 expected = U_BOOT_DATA + MRC_DATA + tools.GetBytes(ord('b'), 32 - 7) 2102 self.assertEqual(expected, data[:32]) 2103 fhdr, fentries = fmap_util.DecodeFmap(data[36:]) 2104 2105 self.assertEqual(0x180, fhdr.image_size) 2106 expect_size = fmap_util.FMAP_HEADER_LEN + fmap_util.FMAP_AREA_LEN * 4 2107 fiter = iter(fentries) 2108 2109 fentry = next(fiter) 2110 self.assertEqual(b'U_BOOT', fentry.name) 2111 self.assertEqual(0, fentry.offset) 2112 self.assertEqual(4, fentry.size) 2113 2114 fentry = next(fiter) 2115 self.assertEqual(b'SECTION', fentry.name) 2116 self.assertEqual(4, fentry.offset) 2117 self.assertEqual(0x20 + expect_size, fentry.size) 2118 2119 fentry = next(fiter) 2120 self.assertEqual(b'INTEL_MRC', fentry.name) 2121 self.assertEqual(4, fentry.offset) 2122 self.assertEqual(3, fentry.size) 2123 2124 fentry = next(fiter) 2125 self.assertEqual(b'FMAP', fentry.name) 2126 self.assertEqual(36, fentry.offset) 2127 self.assertEqual(expect_size, fentry.size) 2128 2129 def testElf(self): 2130 """Basic test of ELF entries""" 2131 self._SetupSplElf() 2132 self._SetupTplElf() 2133 with open(self.ElfTestFile('bss_data'), 'rb') as fd: 2134 TestFunctional._MakeInputFile('-boot', fd.read()) 2135 data = self._DoReadFile('096_elf.dts') 2136 2137 def testElfStrip(self): 2138 """Basic test of ELF entries""" 2139 self._SetupSplElf() 2140 with open(self.ElfTestFile('bss_data'), 'rb') as fd: 2141 TestFunctional._MakeInputFile('-boot', fd.read()) 2142 data = self._DoReadFile('097_elf_strip.dts') 2143 2144 def testPackOverlapMap(self): 2145 """Test that overlapping regions are detected""" 2146 with test_util.capture_sys_output() as (stdout, stderr): 2147 with self.assertRaises(ValueError) as e: 2148 self._DoTestFile('014_pack_overlap.dts', map=True) 2149 map_fname = tools.GetOutputFilename('image.map') 2150 self.assertEqual("Wrote map file '%s' to show errors\n" % map_fname, 2151 stdout.getvalue()) 2152 2153 # We should not get an inmage, but there should be a map file 2154 self.assertFalse(os.path.exists(tools.GetOutputFilename('image.bin'))) 2155 self.assertTrue(os.path.exists(map_fname)) 2156 map_data = tools.ReadFile(map_fname, binary=False) 2157 self.assertEqual('''ImagePos Offset Size Name 2158<none> 00000000 00000008 main-section 2159<none> 00000000 00000004 u-boot 2160<none> 00000003 00000004 u-boot-align 2161''', map_data) 2162 2163 def testPackRefCode(self): 2164 """Test that an image with an Intel Reference code binary works""" 2165 data = self._DoReadFile('100_intel_refcode.dts') 2166 self.assertEqual(REFCODE_DATA, data[:len(REFCODE_DATA)]) 2167 2168 def testSectionOffset(self): 2169 """Tests use of a section with an offset""" 2170 data, _, map_data, _ = self._DoReadFileDtb('101_sections_offset.dts', 2171 map=True) 2172 self.assertEqual('''ImagePos Offset Size Name 217300000000 00000000 00000038 main-section 217400000004 00000004 00000010 section@0 217500000004 00000000 00000004 u-boot 217600000018 00000018 00000010 section@1 217700000018 00000000 00000004 u-boot 21780000002c 0000002c 00000004 section@2 21790000002c 00000000 00000004 u-boot 2180''', map_data) 2181 self.assertEqual(data, 2182 tools.GetBytes(0x26, 4) + U_BOOT_DATA + 2183 tools.GetBytes(0x21, 12) + 2184 tools.GetBytes(0x26, 4) + U_BOOT_DATA + 2185 tools.GetBytes(0x61, 12) + 2186 tools.GetBytes(0x26, 4) + U_BOOT_DATA + 2187 tools.GetBytes(0x26, 8)) 2188 2189 def testCbfsRaw(self): 2190 """Test base handling of a Coreboot Filesystem (CBFS) 2191 2192 The exact contents of the CBFS is verified by similar tests in 2193 cbfs_util_test.py. The tests here merely check that the files added to 2194 the CBFS can be found in the final image. 2195 """ 2196 data = self._DoReadFile('102_cbfs_raw.dts') 2197 size = 0xb0 2198 2199 cbfs = cbfs_util.CbfsReader(data) 2200 self.assertEqual(size, cbfs.rom_size) 2201 2202 self.assertIn('u-boot-dtb', cbfs.files) 2203 cfile = cbfs.files['u-boot-dtb'] 2204 self.assertEqual(U_BOOT_DTB_DATA, cfile.data) 2205 2206 def testCbfsArch(self): 2207 """Test on non-x86 architecture""" 2208 data = self._DoReadFile('103_cbfs_raw_ppc.dts') 2209 size = 0x100 2210 2211 cbfs = cbfs_util.CbfsReader(data) 2212 self.assertEqual(size, cbfs.rom_size) 2213 2214 self.assertIn('u-boot-dtb', cbfs.files) 2215 cfile = cbfs.files['u-boot-dtb'] 2216 self.assertEqual(U_BOOT_DTB_DATA, cfile.data) 2217 2218 def testCbfsStage(self): 2219 """Tests handling of a Coreboot Filesystem (CBFS)""" 2220 if not elf.ELF_TOOLS: 2221 self.skipTest('Python elftools not available') 2222 elf_fname = os.path.join(self._indir, 'cbfs-stage.elf') 2223 elf.MakeElf(elf_fname, U_BOOT_DATA, U_BOOT_DTB_DATA) 2224 size = 0xb0 2225 2226 data = self._DoReadFile('104_cbfs_stage.dts') 2227 cbfs = cbfs_util.CbfsReader(data) 2228 self.assertEqual(size, cbfs.rom_size) 2229 2230 self.assertIn('u-boot', cbfs.files) 2231 cfile = cbfs.files['u-boot'] 2232 self.assertEqual(U_BOOT_DATA + U_BOOT_DTB_DATA, cfile.data) 2233 2234 def testCbfsRawCompress(self): 2235 """Test handling of compressing raw files""" 2236 self._CheckLz4() 2237 data = self._DoReadFile('105_cbfs_raw_compress.dts') 2238 size = 0x140 2239 2240 cbfs = cbfs_util.CbfsReader(data) 2241 self.assertIn('u-boot', cbfs.files) 2242 cfile = cbfs.files['u-boot'] 2243 self.assertEqual(COMPRESS_DATA, cfile.data) 2244 2245 def testCbfsBadArch(self): 2246 """Test handling of a bad architecture""" 2247 with self.assertRaises(ValueError) as e: 2248 self._DoReadFile('106_cbfs_bad_arch.dts') 2249 self.assertIn("Invalid architecture 'bad-arch'", str(e.exception)) 2250 2251 def testCbfsNoSize(self): 2252 """Test handling of a missing size property""" 2253 with self.assertRaises(ValueError) as e: 2254 self._DoReadFile('107_cbfs_no_size.dts') 2255 self.assertIn('entry must have a size property', str(e.exception)) 2256 2257 def testCbfsNoCOntents(self): 2258 """Test handling of a CBFS entry which does not provide contentsy""" 2259 with self.assertRaises(ValueError) as e: 2260 self._DoReadFile('108_cbfs_no_contents.dts') 2261 self.assertIn('Could not complete processing of contents', 2262 str(e.exception)) 2263 2264 def testCbfsBadCompress(self): 2265 """Test handling of a bad architecture""" 2266 with self.assertRaises(ValueError) as e: 2267 self._DoReadFile('109_cbfs_bad_compress.dts') 2268 self.assertIn("Invalid compression in 'u-boot': 'invalid-algo'", 2269 str(e.exception)) 2270 2271 def testCbfsNamedEntries(self): 2272 """Test handling of named entries""" 2273 data = self._DoReadFile('110_cbfs_name.dts') 2274 2275 cbfs = cbfs_util.CbfsReader(data) 2276 self.assertIn('FRED', cbfs.files) 2277 cfile1 = cbfs.files['FRED'] 2278 self.assertEqual(U_BOOT_DATA, cfile1.data) 2279 2280 self.assertIn('hello', cbfs.files) 2281 cfile2 = cbfs.files['hello'] 2282 self.assertEqual(U_BOOT_DTB_DATA, cfile2.data) 2283 2284 def _SetupIfwi(self, fname): 2285 """Set up to run an IFWI test 2286 2287 Args: 2288 fname: Filename of input file to provide (fitimage.bin or ifwi.bin) 2289 """ 2290 self._SetupSplElf() 2291 self._SetupTplElf() 2292 2293 # Intel Integrated Firmware Image (IFWI) file 2294 with gzip.open(self.TestFile('%s.gz' % fname), 'rb') as fd: 2295 data = fd.read() 2296 TestFunctional._MakeInputFile(fname,data) 2297 2298 def _CheckIfwi(self, data): 2299 """Check that an image with an IFWI contains the correct output 2300 2301 Args: 2302 data: Conents of output file 2303 """ 2304 expected_desc = tools.ReadFile(self.TestFile('descriptor.bin')) 2305 if data[:0x1000] != expected_desc: 2306 self.fail('Expected descriptor binary at start of image') 2307 2308 # We expect to find the TPL wil in subpart IBBP entry IBBL 2309 image_fname = tools.GetOutputFilename('image.bin') 2310 tpl_fname = tools.GetOutputFilename('tpl.out') 2311 tools.RunIfwiTool(image_fname, tools.CMD_EXTRACT, fname=tpl_fname, 2312 subpart='IBBP', entry_name='IBBL') 2313 2314 tpl_data = tools.ReadFile(tpl_fname) 2315 self.assertEqual(U_BOOT_TPL_DATA, tpl_data[:len(U_BOOT_TPL_DATA)]) 2316 2317 def testPackX86RomIfwi(self): 2318 """Test that an x86 ROM with Integrated Firmware Image can be created""" 2319 self._SetupIfwi('fitimage.bin') 2320 data = self._DoReadFile('111_x86_rom_ifwi.dts') 2321 self._CheckIfwi(data) 2322 2323 def testPackX86RomIfwiNoDesc(self): 2324 """Test that an x86 ROM with IFWI can be created from an ifwi.bin file""" 2325 self._SetupIfwi('ifwi.bin') 2326 data = self._DoReadFile('112_x86_rom_ifwi_nodesc.dts') 2327 self._CheckIfwi(data) 2328 2329 def testPackX86RomIfwiNoData(self): 2330 """Test that an x86 ROM with IFWI handles missing data""" 2331 self._SetupIfwi('ifwi.bin') 2332 with self.assertRaises(ValueError) as e: 2333 data = self._DoReadFile('113_x86_rom_ifwi_nodata.dts') 2334 self.assertIn('Could not complete processing of contents', 2335 str(e.exception)) 2336 2337 def testCbfsOffset(self): 2338 """Test a CBFS with files at particular offsets 2339 2340 Like all CFBS tests, this is just checking the logic that calls 2341 cbfs_util. See cbfs_util_test for fully tests (e.g. test_cbfs_offset()). 2342 """ 2343 data = self._DoReadFile('114_cbfs_offset.dts') 2344 size = 0x200 2345 2346 cbfs = cbfs_util.CbfsReader(data) 2347 self.assertEqual(size, cbfs.rom_size) 2348 2349 self.assertIn('u-boot', cbfs.files) 2350 cfile = cbfs.files['u-boot'] 2351 self.assertEqual(U_BOOT_DATA, cfile.data) 2352 self.assertEqual(0x40, cfile.cbfs_offset) 2353 2354 self.assertIn('u-boot-dtb', cbfs.files) 2355 cfile2 = cbfs.files['u-boot-dtb'] 2356 self.assertEqual(U_BOOT_DTB_DATA, cfile2.data) 2357 self.assertEqual(0x140, cfile2.cbfs_offset) 2358 2359 def testFdtmap(self): 2360 """Test an FDT map can be inserted in the image""" 2361 data = self.data = self._DoReadFileRealDtb('115_fdtmap.dts') 2362 fdtmap_data = data[len(U_BOOT_DATA):] 2363 magic = fdtmap_data[:8] 2364 self.assertEqual(b'_FDTMAP_', magic) 2365 self.assertEqual(tools.GetBytes(0, 8), fdtmap_data[8:16]) 2366 2367 fdt_data = fdtmap_data[16:] 2368 dtb = fdt.Fdt.FromData(fdt_data) 2369 dtb.Scan() 2370 props = self._GetPropTree(dtb, BASE_DTB_PROPS, prefix='/') 2371 self.assertEqual({ 2372 'image-pos': 0, 2373 'offset': 0, 2374 'u-boot:offset': 0, 2375 'u-boot:size': len(U_BOOT_DATA), 2376 'u-boot:image-pos': 0, 2377 'fdtmap:image-pos': 4, 2378 'fdtmap:offset': 4, 2379 'fdtmap:size': len(fdtmap_data), 2380 'size': len(data), 2381 }, props) 2382 2383 def testFdtmapNoMatch(self): 2384 """Check handling of an FDT map when the section cannot be found""" 2385 self.data = self._DoReadFileRealDtb('115_fdtmap.dts') 2386 2387 # Mangle the section name, which should cause a mismatch between the 2388 # correct FDT path and the one expected by the section 2389 image = control.images['image'] 2390 image._node.path += '-suffix' 2391 entries = image.GetEntries() 2392 fdtmap = entries['fdtmap'] 2393 with self.assertRaises(ValueError) as e: 2394 fdtmap._GetFdtmap() 2395 self.assertIn("Cannot locate node for path '/binman-suffix'", 2396 str(e.exception)) 2397 2398 def testFdtmapHeader(self): 2399 """Test an FDT map and image header can be inserted in the image""" 2400 data = self.data = self._DoReadFileRealDtb('116_fdtmap_hdr.dts') 2401 fdtmap_pos = len(U_BOOT_DATA) 2402 fdtmap_data = data[fdtmap_pos:] 2403 fdt_data = fdtmap_data[16:] 2404 dtb = fdt.Fdt.FromData(fdt_data) 2405 fdt_size = dtb.GetFdtObj().totalsize() 2406 hdr_data = data[-8:] 2407 self.assertEqual(b'BinM', hdr_data[:4]) 2408 offset = struct.unpack('<I', hdr_data[4:])[0] & 0xffffffff 2409 self.assertEqual(fdtmap_pos - 0x400, offset - (1 << 32)) 2410 2411 def testFdtmapHeaderStart(self): 2412 """Test an image header can be inserted at the image start""" 2413 data = self.data = self._DoReadFileRealDtb('117_fdtmap_hdr_start.dts') 2414 fdtmap_pos = 0x100 + len(U_BOOT_DATA) 2415 hdr_data = data[:8] 2416 self.assertEqual(b'BinM', hdr_data[:4]) 2417 offset = struct.unpack('<I', hdr_data[4:])[0] 2418 self.assertEqual(fdtmap_pos, offset) 2419 2420 def testFdtmapHeaderPos(self): 2421 """Test an image header can be inserted at a chosen position""" 2422 data = self.data = self._DoReadFileRealDtb('118_fdtmap_hdr_pos.dts') 2423 fdtmap_pos = 0x100 + len(U_BOOT_DATA) 2424 hdr_data = data[0x80:0x88] 2425 self.assertEqual(b'BinM', hdr_data[:4]) 2426 offset = struct.unpack('<I', hdr_data[4:])[0] 2427 self.assertEqual(fdtmap_pos, offset) 2428 2429 def testHeaderMissingFdtmap(self): 2430 """Test an image header requires an fdtmap""" 2431 with self.assertRaises(ValueError) as e: 2432 self.data = self._DoReadFileRealDtb('119_fdtmap_hdr_missing.dts') 2433 self.assertIn("'image_header' section must have an 'fdtmap' sibling", 2434 str(e.exception)) 2435 2436 def testHeaderNoLocation(self): 2437 """Test an image header with a no specified location is detected""" 2438 with self.assertRaises(ValueError) as e: 2439 self.data = self._DoReadFileRealDtb('120_hdr_no_location.dts') 2440 self.assertIn("Invalid location 'None', expected 'start' or 'end'", 2441 str(e.exception)) 2442 2443 def testEntryExpand(self): 2444 """Test expanding an entry after it is packed""" 2445 data = self._DoReadFile('121_entry_expand.dts') 2446 self.assertEqual(b'aaa', data[:3]) 2447 self.assertEqual(U_BOOT_DATA, data[3:3 + len(U_BOOT_DATA)]) 2448 self.assertEqual(b'aaa', data[-3:]) 2449 2450 def testEntryExpandBad(self): 2451 """Test expanding an entry after it is packed, twice""" 2452 with self.assertRaises(ValueError) as e: 2453 self._DoReadFile('122_entry_expand_twice.dts') 2454 self.assertIn("Image '/binman': Entries changed size after packing", 2455 str(e.exception)) 2456 2457 def testEntryExpandSection(self): 2458 """Test expanding an entry within a section after it is packed""" 2459 data = self._DoReadFile('123_entry_expand_section.dts') 2460 self.assertEqual(b'aaa', data[:3]) 2461 self.assertEqual(U_BOOT_DATA, data[3:3 + len(U_BOOT_DATA)]) 2462 self.assertEqual(b'aaa', data[-3:]) 2463 2464 def testCompressDtb(self): 2465 """Test that compress of device-tree files is supported""" 2466 self._CheckLz4() 2467 data = self.data = self._DoReadFileRealDtb('124_compress_dtb.dts') 2468 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)]) 2469 comp_data = data[len(U_BOOT_DATA):] 2470 orig = self._decompress(comp_data) 2471 dtb = fdt.Fdt.FromData(orig) 2472 dtb.Scan() 2473 props = self._GetPropTree(dtb, ['size', 'uncomp-size']) 2474 expected = { 2475 'u-boot:size': len(U_BOOT_DATA), 2476 'u-boot-dtb:uncomp-size': len(orig), 2477 'u-boot-dtb:size': len(comp_data), 2478 'size': len(data), 2479 } 2480 self.assertEqual(expected, props) 2481 2482 def testCbfsUpdateFdt(self): 2483 """Test that we can update the device tree with CBFS offset/size info""" 2484 self._CheckLz4() 2485 data, _, _, out_dtb_fname = self._DoReadFileDtb('125_cbfs_update.dts', 2486 update_dtb=True) 2487 dtb = fdt.Fdt(out_dtb_fname) 2488 dtb.Scan() 2489 props = self._GetPropTree(dtb, BASE_DTB_PROPS + ['uncomp-size']) 2490 del props['cbfs/u-boot:size'] 2491 self.assertEqual({ 2492 'offset': 0, 2493 'size': len(data), 2494 'image-pos': 0, 2495 'cbfs:offset': 0, 2496 'cbfs:size': len(data), 2497 'cbfs:image-pos': 0, 2498 'cbfs/u-boot:offset': 0x38, 2499 'cbfs/u-boot:uncomp-size': len(U_BOOT_DATA), 2500 'cbfs/u-boot:image-pos': 0x38, 2501 'cbfs/u-boot-dtb:offset': 0xb8, 2502 'cbfs/u-boot-dtb:size': len(U_BOOT_DATA), 2503 'cbfs/u-boot-dtb:image-pos': 0xb8, 2504 }, props) 2505 2506 def testCbfsBadType(self): 2507 """Test an image header with a no specified location is detected""" 2508 with self.assertRaises(ValueError) as e: 2509 self._DoReadFile('126_cbfs_bad_type.dts') 2510 self.assertIn("Unknown cbfs-type 'badtype'", str(e.exception)) 2511 2512 def testList(self): 2513 """Test listing the files in an image""" 2514 self._CheckLz4() 2515 data = self._DoReadFile('127_list.dts') 2516 image = control.images['image'] 2517 entries = image.BuildEntryList() 2518 self.assertEqual(7, len(entries)) 2519 2520 ent = entries[0] 2521 self.assertEqual(0, ent.indent) 2522 self.assertEqual('main-section', ent.name) 2523 self.assertEqual('section', ent.etype) 2524 self.assertEqual(len(data), ent.size) 2525 self.assertEqual(0, ent.image_pos) 2526 self.assertEqual(None, ent.uncomp_size) 2527 self.assertEqual(0, ent.offset) 2528 2529 ent = entries[1] 2530 self.assertEqual(1, ent.indent) 2531 self.assertEqual('u-boot', ent.name) 2532 self.assertEqual('u-boot', ent.etype) 2533 self.assertEqual(len(U_BOOT_DATA), ent.size) 2534 self.assertEqual(0, ent.image_pos) 2535 self.assertEqual(None, ent.uncomp_size) 2536 self.assertEqual(0, ent.offset) 2537 2538 ent = entries[2] 2539 self.assertEqual(1, ent.indent) 2540 self.assertEqual('section', ent.name) 2541 self.assertEqual('section', ent.etype) 2542 section_size = ent.size 2543 self.assertEqual(0x100, ent.image_pos) 2544 self.assertEqual(None, ent.uncomp_size) 2545 self.assertEqual(0x100, ent.offset) 2546 2547 ent = entries[3] 2548 self.assertEqual(2, ent.indent) 2549 self.assertEqual('cbfs', ent.name) 2550 self.assertEqual('cbfs', ent.etype) 2551 self.assertEqual(0x400, ent.size) 2552 self.assertEqual(0x100, ent.image_pos) 2553 self.assertEqual(None, ent.uncomp_size) 2554 self.assertEqual(0, ent.offset) 2555 2556 ent = entries[4] 2557 self.assertEqual(3, ent.indent) 2558 self.assertEqual('u-boot', ent.name) 2559 self.assertEqual('u-boot', ent.etype) 2560 self.assertEqual(len(U_BOOT_DATA), ent.size) 2561 self.assertEqual(0x138, ent.image_pos) 2562 self.assertEqual(None, ent.uncomp_size) 2563 self.assertEqual(0x38, ent.offset) 2564 2565 ent = entries[5] 2566 self.assertEqual(3, ent.indent) 2567 self.assertEqual('u-boot-dtb', ent.name) 2568 self.assertEqual('text', ent.etype) 2569 self.assertGreater(len(COMPRESS_DATA), ent.size) 2570 self.assertEqual(0x178, ent.image_pos) 2571 self.assertEqual(len(COMPRESS_DATA), ent.uncomp_size) 2572 self.assertEqual(0x78, ent.offset) 2573 2574 ent = entries[6] 2575 self.assertEqual(2, ent.indent) 2576 self.assertEqual('u-boot-dtb', ent.name) 2577 self.assertEqual('u-boot-dtb', ent.etype) 2578 self.assertEqual(0x500, ent.image_pos) 2579 self.assertEqual(len(U_BOOT_DTB_DATA), ent.uncomp_size) 2580 dtb_size = ent.size 2581 # Compressing this data expands it since headers are added 2582 self.assertGreater(dtb_size, len(U_BOOT_DTB_DATA)) 2583 self.assertEqual(0x400, ent.offset) 2584 2585 self.assertEqual(len(data), 0x100 + section_size) 2586 self.assertEqual(section_size, 0x400 + dtb_size) 2587 2588 def testFindFdtmap(self): 2589 """Test locating an FDT map in an image""" 2590 self._CheckLz4() 2591 data = self.data = self._DoReadFileRealDtb('128_decode_image.dts') 2592 image = control.images['image'] 2593 entries = image.GetEntries() 2594 entry = entries['fdtmap'] 2595 self.assertEqual(entry.image_pos, fdtmap.LocateFdtmap(data)) 2596 2597 def testFindFdtmapMissing(self): 2598 """Test failing to locate an FDP map""" 2599 data = self._DoReadFile('005_simple.dts') 2600 self.assertEqual(None, fdtmap.LocateFdtmap(data)) 2601 2602 def testFindImageHeader(self): 2603 """Test locating a image header""" 2604 self._CheckLz4() 2605 data = self.data = self._DoReadFileRealDtb('128_decode_image.dts') 2606 image = control.images['image'] 2607 entries = image.GetEntries() 2608 entry = entries['fdtmap'] 2609 # The header should point to the FDT map 2610 self.assertEqual(entry.image_pos, image_header.LocateHeaderOffset(data)) 2611 2612 def testFindImageHeaderStart(self): 2613 """Test locating a image header located at the start of an image""" 2614 data = self.data = self._DoReadFileRealDtb('117_fdtmap_hdr_start.dts') 2615 image = control.images['image'] 2616 entries = image.GetEntries() 2617 entry = entries['fdtmap'] 2618 # The header should point to the FDT map 2619 self.assertEqual(entry.image_pos, image_header.LocateHeaderOffset(data)) 2620 2621 def testFindImageHeaderMissing(self): 2622 """Test failing to locate an image header""" 2623 data = self._DoReadFile('005_simple.dts') 2624 self.assertEqual(None, image_header.LocateHeaderOffset(data)) 2625 2626 def testReadImage(self): 2627 """Test reading an image and accessing its FDT map""" 2628 self._CheckLz4() 2629 data = self.data = self._DoReadFileRealDtb('128_decode_image.dts') 2630 image_fname = tools.GetOutputFilename('image.bin') 2631 orig_image = control.images['image'] 2632 image = Image.FromFile(image_fname) 2633 self.assertEqual(orig_image.GetEntries().keys(), 2634 image.GetEntries().keys()) 2635 2636 orig_entry = orig_image.GetEntries()['fdtmap'] 2637 entry = image.GetEntries()['fdtmap'] 2638 self.assertEquals(orig_entry.offset, entry.offset) 2639 self.assertEquals(orig_entry.size, entry.size) 2640 self.assertEquals(orig_entry.image_pos, entry.image_pos) 2641 2642 def testReadImageNoHeader(self): 2643 """Test accessing an image's FDT map without an image header""" 2644 self._CheckLz4() 2645 data = self._DoReadFileRealDtb('129_decode_image_nohdr.dts') 2646 image_fname = tools.GetOutputFilename('image.bin') 2647 image = Image.FromFile(image_fname) 2648 self.assertTrue(isinstance(image, Image)) 2649 self.assertEqual('image', image.image_name[-5:]) 2650 2651 def testReadImageFail(self): 2652 """Test failing to read an image image's FDT map""" 2653 self._DoReadFile('005_simple.dts') 2654 image_fname = tools.GetOutputFilename('image.bin') 2655 with self.assertRaises(ValueError) as e: 2656 image = Image.FromFile(image_fname) 2657 self.assertIn("Cannot find FDT map in image", str(e.exception)) 2658 2659 def testListCmd(self): 2660 """Test listing the files in an image using an Fdtmap""" 2661 self._CheckLz4() 2662 data = self._DoReadFileRealDtb('130_list_fdtmap.dts') 2663 2664 # lz4 compression size differs depending on the version 2665 image = control.images['image'] 2666 entries = image.GetEntries() 2667 section_size = entries['section'].size 2668 fdt_size = entries['section'].GetEntries()['u-boot-dtb'].size 2669 fdtmap_offset = entries['fdtmap'].offset 2670 2671 try: 2672 tmpdir, updated_fname = self._SetupImageInTmpdir() 2673 with test_util.capture_sys_output() as (stdout, stderr): 2674 self._DoBinman('ls', '-i', updated_fname) 2675 finally: 2676 shutil.rmtree(tmpdir) 2677 lines = stdout.getvalue().splitlines() 2678 expected = [ 2679'Name Image-pos Size Entry-type Offset Uncomp-size', 2680'----------------------------------------------------------------------', 2681'main-section 0 c00 section 0', 2682' u-boot 0 4 u-boot 0', 2683' section 100 %x section 100' % section_size, 2684' cbfs 100 400 cbfs 0', 2685' u-boot 138 4 u-boot 38', 2686' u-boot-dtb 180 105 u-boot-dtb 80 3c9', 2687' u-boot-dtb 500 %x u-boot-dtb 400 3c9' % fdt_size, 2688' fdtmap %x 3bd fdtmap %x' % 2689 (fdtmap_offset, fdtmap_offset), 2690' image-header bf8 8 image-header bf8', 2691 ] 2692 self.assertEqual(expected, lines) 2693 2694 def testListCmdFail(self): 2695 """Test failing to list an image""" 2696 self._DoReadFile('005_simple.dts') 2697 try: 2698 tmpdir, updated_fname = self._SetupImageInTmpdir() 2699 with self.assertRaises(ValueError) as e: 2700 self._DoBinman('ls', '-i', updated_fname) 2701 finally: 2702 shutil.rmtree(tmpdir) 2703 self.assertIn("Cannot find FDT map in image", str(e.exception)) 2704 2705 def _RunListCmd(self, paths, expected): 2706 """List out entries and check the result 2707 2708 Args: 2709 paths: List of paths to pass to the list command 2710 expected: Expected list of filenames to be returned, in order 2711 """ 2712 self._CheckLz4() 2713 self._DoReadFileRealDtb('130_list_fdtmap.dts') 2714 image_fname = tools.GetOutputFilename('image.bin') 2715 image = Image.FromFile(image_fname) 2716 lines = image.GetListEntries(paths)[1] 2717 files = [line[0].strip() for line in lines[1:]] 2718 self.assertEqual(expected, files) 2719 2720 def testListCmdSection(self): 2721 """Test listing the files in a section""" 2722 self._RunListCmd(['section'], 2723 ['section', 'cbfs', 'u-boot', 'u-boot-dtb', 'u-boot-dtb']) 2724 2725 def testListCmdFile(self): 2726 """Test listing a particular file""" 2727 self._RunListCmd(['*u-boot-dtb'], ['u-boot-dtb', 'u-boot-dtb']) 2728 2729 def testListCmdWildcard(self): 2730 """Test listing a wildcarded file""" 2731 self._RunListCmd(['*boot*'], 2732 ['u-boot', 'u-boot', 'u-boot-dtb', 'u-boot-dtb']) 2733 2734 def testListCmdWildcardMulti(self): 2735 """Test listing a wildcarded file""" 2736 self._RunListCmd(['*cb*', '*head*'], 2737 ['cbfs', 'u-boot', 'u-boot-dtb', 'image-header']) 2738 2739 def testListCmdEmpty(self): 2740 """Test listing a wildcarded file""" 2741 self._RunListCmd(['nothing'], []) 2742 2743 def testListCmdPath(self): 2744 """Test listing the files in a sub-entry of a section""" 2745 self._RunListCmd(['section/cbfs'], ['cbfs', 'u-boot', 'u-boot-dtb']) 2746 2747 def _RunExtractCmd(self, entry_name, decomp=True): 2748 """Extract an entry from an image 2749 2750 Args: 2751 entry_name: Entry name to extract 2752 decomp: True to decompress the data if compressed, False to leave 2753 it in its raw uncompressed format 2754 2755 Returns: 2756 data from entry 2757 """ 2758 self._CheckLz4() 2759 self._DoReadFileRealDtb('130_list_fdtmap.dts') 2760 image_fname = tools.GetOutputFilename('image.bin') 2761 return control.ReadEntry(image_fname, entry_name, decomp) 2762 2763 def testExtractSimple(self): 2764 """Test extracting a single file""" 2765 data = self._RunExtractCmd('u-boot') 2766 self.assertEqual(U_BOOT_DATA, data) 2767 2768 def testExtractSection(self): 2769 """Test extracting the files in a section""" 2770 data = self._RunExtractCmd('section') 2771 cbfs_data = data[:0x400] 2772 cbfs = cbfs_util.CbfsReader(cbfs_data) 2773 self.assertEqual(['u-boot', 'u-boot-dtb', ''], list(cbfs.files.keys())) 2774 dtb_data = data[0x400:] 2775 dtb = self._decompress(dtb_data) 2776 self.assertEqual(EXTRACT_DTB_SIZE, len(dtb)) 2777 2778 def testExtractCompressed(self): 2779 """Test extracting compressed data""" 2780 data = self._RunExtractCmd('section/u-boot-dtb') 2781 self.assertEqual(EXTRACT_DTB_SIZE, len(data)) 2782 2783 def testExtractRaw(self): 2784 """Test extracting compressed data without decompressing it""" 2785 data = self._RunExtractCmd('section/u-boot-dtb', decomp=False) 2786 dtb = self._decompress(data) 2787 self.assertEqual(EXTRACT_DTB_SIZE, len(dtb)) 2788 2789 def testExtractCbfs(self): 2790 """Test extracting CBFS data""" 2791 data = self._RunExtractCmd('section/cbfs/u-boot') 2792 self.assertEqual(U_BOOT_DATA, data) 2793 2794 def testExtractCbfsCompressed(self): 2795 """Test extracting CBFS compressed data""" 2796 data = self._RunExtractCmd('section/cbfs/u-boot-dtb') 2797 self.assertEqual(EXTRACT_DTB_SIZE, len(data)) 2798 2799 def testExtractCbfsRaw(self): 2800 """Test extracting CBFS compressed data without decompressing it""" 2801 data = self._RunExtractCmd('section/cbfs/u-boot-dtb', decomp=False) 2802 dtb = tools.Decompress(data, 'lzma', with_header=False) 2803 self.assertEqual(EXTRACT_DTB_SIZE, len(dtb)) 2804 2805 def testExtractBadEntry(self): 2806 """Test extracting a bad section path""" 2807 with self.assertRaises(ValueError) as e: 2808 self._RunExtractCmd('section/does-not-exist') 2809 self.assertIn("Entry 'does-not-exist' not found in '/section'", 2810 str(e.exception)) 2811 2812 def testExtractMissingFile(self): 2813 """Test extracting file that does not exist""" 2814 with self.assertRaises(IOError) as e: 2815 control.ReadEntry('missing-file', 'name') 2816 2817 def testExtractBadFile(self): 2818 """Test extracting an invalid file""" 2819 fname = os.path.join(self._indir, 'badfile') 2820 tools.WriteFile(fname, b'') 2821 with self.assertRaises(ValueError) as e: 2822 control.ReadEntry(fname, 'name') 2823 2824 def testExtractCmd(self): 2825 """Test extracting a file fron an image on the command line""" 2826 self._CheckLz4() 2827 self._DoReadFileRealDtb('130_list_fdtmap.dts') 2828 fname = os.path.join(self._indir, 'output.extact') 2829 try: 2830 tmpdir, updated_fname = self._SetupImageInTmpdir() 2831 with test_util.capture_sys_output() as (stdout, stderr): 2832 self._DoBinman('extract', '-i', updated_fname, 'u-boot', 2833 '-f', fname) 2834 finally: 2835 shutil.rmtree(tmpdir) 2836 data = tools.ReadFile(fname) 2837 self.assertEqual(U_BOOT_DATA, data) 2838 2839 def testExtractOneEntry(self): 2840 """Test extracting a single entry fron an image """ 2841 self._CheckLz4() 2842 self._DoReadFileRealDtb('130_list_fdtmap.dts') 2843 image_fname = tools.GetOutputFilename('image.bin') 2844 fname = os.path.join(self._indir, 'output.extact') 2845 control.ExtractEntries(image_fname, fname, None, ['u-boot']) 2846 data = tools.ReadFile(fname) 2847 self.assertEqual(U_BOOT_DATA, data) 2848 2849 def _CheckExtractOutput(self, decomp): 2850 """Helper to test file output with and without decompression 2851 2852 Args: 2853 decomp: True to decompress entry data, False to output it raw 2854 """ 2855 def _CheckPresent(entry_path, expect_data, expect_size=None): 2856 """Check and remove expected file 2857 2858 This checks the data/size of a file and removes the file both from 2859 the outfiles set and from the output directory. Once all files are 2860 processed, both the set and directory should be empty. 2861 2862 Args: 2863 entry_path: Entry path 2864 expect_data: Data to expect in file, or None to skip check 2865 expect_size: Size of data to expect in file, or None to skip 2866 """ 2867 path = os.path.join(outdir, entry_path) 2868 data = tools.ReadFile(path) 2869 os.remove(path) 2870 if expect_data: 2871 self.assertEqual(expect_data, data) 2872 elif expect_size: 2873 self.assertEqual(expect_size, len(data)) 2874 outfiles.remove(path) 2875 2876 def _CheckDirPresent(name): 2877 """Remove expected directory 2878 2879 This gives an error if the directory does not exist as expected 2880 2881 Args: 2882 name: Name of directory to remove 2883 """ 2884 path = os.path.join(outdir, name) 2885 os.rmdir(path) 2886 2887 self._DoReadFileRealDtb('130_list_fdtmap.dts') 2888 image_fname = tools.GetOutputFilename('image.bin') 2889 outdir = os.path.join(self._indir, 'extract') 2890 einfos = control.ExtractEntries(image_fname, None, outdir, [], decomp) 2891 2892 # Create a set of all file that were output (should be 9) 2893 outfiles = set() 2894 for root, dirs, files in os.walk(outdir): 2895 outfiles |= set([os.path.join(root, fname) for fname in files]) 2896 self.assertEqual(9, len(outfiles)) 2897 self.assertEqual(9, len(einfos)) 2898 2899 image = control.images['image'] 2900 entries = image.GetEntries() 2901 2902 # Check the 9 files in various ways 2903 section = entries['section'] 2904 section_entries = section.GetEntries() 2905 cbfs_entries = section_entries['cbfs'].GetEntries() 2906 _CheckPresent('u-boot', U_BOOT_DATA) 2907 _CheckPresent('section/cbfs/u-boot', U_BOOT_DATA) 2908 dtb_len = EXTRACT_DTB_SIZE 2909 if not decomp: 2910 dtb_len = cbfs_entries['u-boot-dtb'].size 2911 _CheckPresent('section/cbfs/u-boot-dtb', None, dtb_len) 2912 if not decomp: 2913 dtb_len = section_entries['u-boot-dtb'].size 2914 _CheckPresent('section/u-boot-dtb', None, dtb_len) 2915 2916 fdtmap = entries['fdtmap'] 2917 _CheckPresent('fdtmap', fdtmap.data) 2918 hdr = entries['image-header'] 2919 _CheckPresent('image-header', hdr.data) 2920 2921 _CheckPresent('section/root', section.data) 2922 cbfs = section_entries['cbfs'] 2923 _CheckPresent('section/cbfs/root', cbfs.data) 2924 data = tools.ReadFile(image_fname) 2925 _CheckPresent('root', data) 2926 2927 # There should be no files left. Remove all the directories to check. 2928 # If there are any files/dirs remaining, one of these checks will fail. 2929 self.assertEqual(0, len(outfiles)) 2930 _CheckDirPresent('section/cbfs') 2931 _CheckDirPresent('section') 2932 _CheckDirPresent('') 2933 self.assertFalse(os.path.exists(outdir)) 2934 2935 def testExtractAllEntries(self): 2936 """Test extracting all entries""" 2937 self._CheckLz4() 2938 self._CheckExtractOutput(decomp=True) 2939 2940 def testExtractAllEntriesRaw(self): 2941 """Test extracting all entries without decompressing them""" 2942 self._CheckLz4() 2943 self._CheckExtractOutput(decomp=False) 2944 2945 def testExtractSelectedEntries(self): 2946 """Test extracting some entries""" 2947 self._CheckLz4() 2948 self._DoReadFileRealDtb('130_list_fdtmap.dts') 2949 image_fname = tools.GetOutputFilename('image.bin') 2950 outdir = os.path.join(self._indir, 'extract') 2951 einfos = control.ExtractEntries(image_fname, None, outdir, 2952 ['*cb*', '*head*']) 2953 2954 # File output is tested by testExtractAllEntries(), so just check that 2955 # the expected entries are selected 2956 names = [einfo.name for einfo in einfos] 2957 self.assertEqual(names, 2958 ['cbfs', 'u-boot', 'u-boot-dtb', 'image-header']) 2959 2960 def testExtractNoEntryPaths(self): 2961 """Test extracting some entries""" 2962 self._CheckLz4() 2963 self._DoReadFileRealDtb('130_list_fdtmap.dts') 2964 image_fname = tools.GetOutputFilename('image.bin') 2965 with self.assertRaises(ValueError) as e: 2966 control.ExtractEntries(image_fname, 'fname', None, []) 2967 self.assertIn('Must specify an entry path to write with -f', 2968 str(e.exception)) 2969 2970 def testExtractTooManyEntryPaths(self): 2971 """Test extracting some entries""" 2972 self._CheckLz4() 2973 self._DoReadFileRealDtb('130_list_fdtmap.dts') 2974 image_fname = tools.GetOutputFilename('image.bin') 2975 with self.assertRaises(ValueError) as e: 2976 control.ExtractEntries(image_fname, 'fname', None, ['a', 'b']) 2977 self.assertIn('Must specify exactly one entry path to write with -f', 2978 str(e.exception)) 2979 2980 def testPackAlignSection(self): 2981 """Test that sections can have alignment""" 2982 self._DoReadFile('131_pack_align_section.dts') 2983 2984 self.assertIn('image', control.images) 2985 image = control.images['image'] 2986 entries = image.GetEntries() 2987 self.assertEqual(3, len(entries)) 2988 2989 # First u-boot 2990 self.assertIn('u-boot', entries) 2991 entry = entries['u-boot'] 2992 self.assertEqual(0, entry.offset) 2993 self.assertEqual(0, entry.image_pos) 2994 self.assertEqual(len(U_BOOT_DATA), entry.contents_size) 2995 self.assertEqual(len(U_BOOT_DATA), entry.size) 2996 2997 # Section0 2998 self.assertIn('section0', entries) 2999 section0 = entries['section0'] 3000 self.assertEqual(0x10, section0.offset)
3001 self.assertEqual(0x10, section0.image_pos) 3002 self.assertEqual(len(U_BOOT_DATA), section0.size) 3003 3004 # Second u-boot 3005 section_entries = section0.GetEntries() 3006 self.assertIn('u-boot', section_entries) 3007 entry = section_entries['u-boot'] 3008 self.assertEqual(0, entry.offset) 3009 self.assertEqual(0x10, entry.image_pos) 3010 self.assertEqual(len(U_BOOT_DATA), entry.contents_size) 3011 self.assertEqual(len(U_BOOT_DATA), entry.size) 3012 3013 # Section1 3014 self.assertIn('section1', entries) 3015 section1 = entries['section1'] 3016 self.assertEqual(0x14, section1.offset) 3017 self.assertEqual(0x14, section1.image_pos) 3018 self.assertEqual(0x20, section1.size) 3019 3020 # Second u-boot 3021 section_entries = section1.GetEntries() 3022 self.assertIn('u-boot', section_entries) 3023 entry = section_entries['u-boot'] 3024 self.assertEqual(0, entry.offset) 3025 self.assertEqual(0x14, entry.image_pos) 3026 self.assertEqual(len(U_BOOT_DATA), entry.contents_size) 3027 self.assertEqual(len(U_BOOT_DATA), entry.size) 3028 3029 # Section2 3030 self.assertIn('section2', section_entries) 3031 section2 = section_entries['section2'] 3032 self.assertEqual(0x4, section2.offset) 3033 self.assertEqual(0x18, section2.image_pos) 3034 self.assertEqual(4, section2.size) 3035 3036 # Third u-boot 3037 section_entries = section2.GetEntries() 3038 self.assertIn('u-boot', section_entries) 3039 entry = section_entries['u-boot'] 3040 self.assertEqual(0, entry.offset) 3041 self.assertEqual(0x18, entry.image_pos) 3042 self.assertEqual(len(U_BOOT_DATA), entry.contents_size) 3043 self.assertEqual(len(U_BOOT_DATA), entry.size) 3044 3045 def _RunReplaceCmd(self, entry_name, data, decomp=True, allow_resize=True, 3046 dts='132_replace.dts'): 3047 """Replace an entry in an image 3048 3049 This writes the entry data to update it, then opens the updated file and 3050 returns the value that it now finds there. 3051 3052 Args: 3053 entry_name: Entry name to replace 3054 data: Data to replace it with 3055 decomp: True to compress the data if needed, False if data is 3056 already compressed so should be used as is 3057 allow_resize: True to allow entries to change size, False to raise 3058 an exception 3059 3060 Returns: 3061 Tuple: 3062 data from entry 3063 data from fdtmap (excluding header) 3064 Image object that was modified 3065 """ 3066 dtb_data = self._DoReadFileDtb(dts, use_real_dtb=True, 3067 update_dtb=True)[1] 3068 3069 self.assertIn('image', control.images) 3070 image = control.images['image'] 3071 entries = image.GetEntries() 3072 orig_dtb_data = entries['u-boot-dtb'].data 3073 orig_fdtmap_data = entries['fdtmap'].data 3074 3075 image_fname = tools.GetOutputFilename('image.bin') 3076 updated_fname = tools.GetOutputFilename('image-updated.bin') 3077 tools.WriteFile(updated_fname, tools.ReadFile(image_fname)) 3078 image = control.WriteEntry(updated_fname, entry_name, data, decomp, 3079 allow_resize) 3080 data = control.ReadEntry(updated_fname, entry_name, decomp) 3081 3082 # The DT data should not change unless resized: 3083 if not allow_resize: 3084 new_dtb_data = entries['u-boot-dtb'].data 3085 self.assertEqual(new_dtb_data, orig_dtb_data) 3086 new_fdtmap_data = entries['fdtmap'].data 3087 self.assertEqual(new_fdtmap_data, orig_fdtmap_data) 3088 3089 return data, orig_fdtmap_data[fdtmap.FDTMAP_HDR_LEN:], image 3090 3091 def testReplaceSimple(self): 3092 """Test replacing a single file""" 3093 expected = b'x' * len(U_BOOT_DATA) 3094 data, expected_fdtmap, _ = self._RunReplaceCmd('u-boot', expected, 3095 allow_resize=False) 3096 self.assertEqual(expected, data) 3097 3098 # Test that the state looks right. There should be an FDT for the fdtmap 3099 # that we jsut read back in, and it should match what we find in the 3100 # 'control' tables. Checking for an FDT that does not exist should 3101 # return None. 3102 path, fdtmap = state.GetFdtContents('fdtmap') 3103 self.assertIsNotNone(path) 3104 self.assertEqual(expected_fdtmap, fdtmap) 3105 3106 dtb = state.GetFdtForEtype('fdtmap') 3107 self.assertEqual(dtb.GetContents(), fdtmap) 3108 3109 missing_path, missing_fdtmap = state.GetFdtContents('missing') 3110 self.assertIsNone(missing_path) 3111 self.assertIsNone(missing_fdtmap) 3112 3113 missing_dtb = state.GetFdtForEtype('missing') 3114 self.assertIsNone(missing_dtb) 3115 3116 self.assertEqual('/binman', state.fdt_path_prefix) 3117 3118 def testReplaceResizeFail(self): 3119 """Test replacing a file by something larger""" 3120 expected = U_BOOT_DATA + b'x' 3121 with self.assertRaises(ValueError) as e: 3122 self._RunReplaceCmd('u-boot', expected, allow_resize=False, 3123 dts='139_replace_repack.dts') 3124 self.assertIn("Node '/u-boot': Entry data size does not match, but resize is disabled", 3125 str(e.exception)) 3126 3127 def testReplaceMulti(self): 3128 """Test replacing entry data where multiple images are generated""" 3129 data = self._DoReadFileDtb('133_replace_multi.dts', use_real_dtb=True, 3130 update_dtb=True)[0] 3131 expected = b'x' * len(U_BOOT_DATA) 3132 updated_fname = tools.GetOutputFilename('image-updated.bin') 3133 tools.WriteFile(updated_fname, data) 3134 entry_name = 'u-boot' 3135 control.WriteEntry(updated_fname, entry_name, expected, 3136 allow_resize=False) 3137 data = control.ReadEntry(updated_fname, entry_name) 3138 self.assertEqual(expected, data) 3139 3140 # Check the state looks right. 3141 self.assertEqual('/binman/image', state.fdt_path_prefix) 3142 3143 # Now check we can write the first image 3144 image_fname = tools.GetOutputFilename('first-image.bin') 3145 updated_fname = tools.GetOutputFilename('first-updated.bin') 3146 tools.WriteFile(updated_fname, tools.ReadFile(image_fname)) 3147 entry_name = 'u-boot' 3148 control.WriteEntry(updated_fname, entry_name, expected, 3149 allow_resize=False) 3150 data = control.ReadEntry(updated_fname, entry_name) 3151 self.assertEqual(expected, data) 3152 3153 # Check the state looks right. 3154 self.assertEqual('/binman/first-image', state.fdt_path_prefix) 3155 3156 def testUpdateFdtAllRepack(self): 3157 """Test that all device trees are updated with offset/size info""" 3158 data = self._DoReadFileRealDtb('134_fdt_update_all_repack.dts') 3159 SECTION_SIZE = 0x300 3160 DTB_SIZE = 602 3161 FDTMAP_SIZE = 608 3162 base_expected = { 3163 'offset': 0, 3164 'size': SECTION_SIZE + DTB_SIZE * 2 + FDTMAP_SIZE, 3165 'image-pos': 0, 3166 'section:offset': 0, 3167 'section:size': SECTION_SIZE, 3168 'section:image-pos': 0, 3169 'section/u-boot-dtb:offset': 4, 3170 'section/u-boot-dtb:size': 636, 3171 'section/u-boot-dtb:image-pos': 4, 3172 'u-boot-spl-dtb:offset': SECTION_SIZE, 3173 'u-boot-spl-dtb:size': DTB_SIZE, 3174 'u-boot-spl-dtb:image-pos': SECTION_SIZE, 3175 'u-boot-tpl-dtb:offset': SECTION_SIZE + DTB_SIZE, 3176 'u-boot-tpl-dtb:image-pos': SECTION_SIZE + DTB_SIZE, 3177 'u-boot-tpl-dtb:size': DTB_SIZE, 3178 'fdtmap:offset': SECTION_SIZE + DTB_SIZE * 2, 3179 'fdtmap:size': FDTMAP_SIZE, 3180 'fdtmap:image-pos': SECTION_SIZE + DTB_SIZE * 2, 3181 } 3182 main_expected = { 3183 'section:orig-size': SECTION_SIZE, 3184 'section/u-boot-dtb:orig-offset': 4, 3185 } 3186 3187 # We expect three device-tree files in the output, with the first one 3188 # within a fixed-size section. 3189 # Read them in sequence. We look for an 'spl' property in the SPL tree, 3190 # and 'tpl' in the TPL tree, to make sure they are distinct from the 3191 # main U-Boot tree. All three should have the same positions and offset 3192 # except that the main tree should include the main_expected properties 3193 start = 4 3194 for item in ['', 'spl', 'tpl', None]: 3195 if item is None: 3196 start += 16 # Move past fdtmap header 3197 dtb = fdt.Fdt.FromData(data[start:]) 3198 dtb.Scan() 3199 props = self._GetPropTree(dtb, 3200 BASE_DTB_PROPS + REPACK_DTB_PROPS + ['spl', 'tpl'], 3201 prefix='/' if item is None else '/binman/') 3202 expected = dict(base_expected) 3203 if item: 3204 expected[item] = 0 3205 else: 3206 # Main DTB and fdtdec should include the 'orig-' properties 3207 expected.update(main_expected) 3208 # Helpful for debugging: 3209 #for prop in sorted(props): 3210 #print('prop %s %s %s' % (prop, props[prop], expected[prop])) 3211 self.assertEqual(expected, props) 3212 if item == '': 3213 start = SECTION_SIZE 3214 else: 3215 start += dtb._fdt_obj.totalsize() 3216 3217 def testFdtmapHeaderMiddle(self): 3218 """Test an FDT map in the middle of an image when it should be at end""" 3219 with self.assertRaises(ValueError) as e: 3220 self._DoReadFileRealDtb('135_fdtmap_hdr_middle.dts') 3221 self.assertIn("Invalid sibling order 'middle' for image-header: Must be at 'end' to match location", 3222 str(e.exception)) 3223 3224 def testFdtmapHeaderStartBad(self): 3225 """Test an FDT map in middle of an image when it should be at start""" 3226 with self.assertRaises(ValueError) as e: 3227 self._DoReadFileRealDtb('136_fdtmap_hdr_startbad.dts') 3228 self.assertIn("Invalid sibling order 'end' for image-header: Must be at 'start' to match location", 3229 str(e.exception)) 3230 3231 def testFdtmapHeaderEndBad(self): 3232 """Test an FDT map at the start of an image when it should be at end""" 3233 with self.assertRaises(ValueError) as e: 3234 self._DoReadFileRealDtb('137_fdtmap_hdr_endbad.dts') 3235 self.assertIn("Invalid sibling order 'start' for image-header: Must be at 'end' to match location", 3236 str(e.exception)) 3237 3238 def testFdtmapHeaderNoSize(self): 3239 """Test an image header at the end of an image with undefined size""" 3240 self._DoReadFileRealDtb('138_fdtmap_hdr_nosize.dts') 3241 3242 def testReplaceResize(self): 3243 """Test replacing a single file in an entry with a larger file""" 3244 expected = U_BOOT_DATA + b'x' 3245 data, _, image = self._RunReplaceCmd('u-boot', expected, 3246 dts='139_replace_repack.dts') 3247 self.assertEqual(expected, data) 3248 3249 entries = image.GetEntries() 3250 dtb_data = entries['u-boot-dtb'].data 3251 dtb = fdt.Fdt.FromData(dtb_data) 3252 dtb.Scan() 3253 3254 # The u-boot section should now be larger in the dtb 3255 node = dtb.GetNode('/binman/u-boot') 3256 self.assertEqual(len(expected), fdt_util.GetInt(node, 'size')) 3257 3258 # Same for the fdtmap 3259 fdata = entries['fdtmap'].data 3260 fdtb = fdt.Fdt.FromData(fdata[fdtmap.FDTMAP_HDR_LEN:]) 3261 fdtb.Scan() 3262 fnode = fdtb.GetNode('/u-boot') 3263 self.assertEqual(len(expected), fdt_util.GetInt(fnode, 'size')) 3264 3265 def testReplaceResizeNoRepack(self): 3266 """Test replacing an entry with a larger file when not allowed""" 3267 expected = U_BOOT_DATA + b'x' 3268 with self.assertRaises(ValueError) as e: 3269 self._RunReplaceCmd('u-boot', expected) 3270 self.assertIn('Entry data size does not match, but allow-repack is not present for this image', 3271 str(e.exception)) 3272 3273 def testEntryShrink(self): 3274 """Test contracting an entry after it is packed""" 3275 try: 3276 state.SetAllowEntryContraction(True) 3277 data = self._DoReadFileDtb('140_entry_shrink.dts', 3278 update_dtb=True)[0] 3279 finally: 3280 state.SetAllowEntryContraction(False) 3281 self.assertEqual(b'a', data[:1]) 3282 self.assertEqual(U_BOOT_DATA, data[1:1 + len(U_BOOT_DATA)]) 3283 self.assertEqual(b'a', data[-1:]) 3284 3285 def testEntryShrinkFail(self): 3286 """Test not being allowed to contract an entry after it is packed""" 3287 data = self._DoReadFileDtb('140_entry_shrink.dts', update_dtb=True)[0] 3288 3289 # In this case there is a spare byte at the end of the data. The size of 3290 # the contents is only 1 byte but we still have the size before it 3291 # shrunk. 3292 self.assertEqual(b'a\0', data[:2]) 3293 self.assertEqual(U_BOOT_DATA, data[2:2 + len(U_BOOT_DATA)]) 3294 self.assertEqual(b'a\0', data[-2:]) 3295 3296 def testDescriptorOffset(self): 3297 """Test that the Intel descriptor is always placed at at the start""" 3298 data = self._DoReadFileDtb('141_descriptor_offset.dts') 3299 image = control.images['image'] 3300 entries = image.GetEntries() 3301 desc = entries['intel-descriptor'] 3302 self.assertEqual(0xff800000, desc.offset); 3303 self.assertEqual(0xff800000, desc.image_pos); 3304 3305 def testReplaceCbfs(self): 3306 """Test replacing a single file in CBFS without changing the size""" 3307 self._CheckLz4() 3308 expected = b'x' * len(U_BOOT_DATA) 3309 data = self._DoReadFileRealDtb('142_replace_cbfs.dts') 3310 updated_fname = tools.GetOutputFilename('image-updated.bin') 3311 tools.WriteFile(updated_fname, data) 3312 entry_name = 'section/cbfs/u-boot' 3313 control.WriteEntry(updated_fname, entry_name, expected, 3314 allow_resize=True) 3315 data = control.ReadEntry(updated_fname, entry_name) 3316 self.assertEqual(expected, data) 3317 3318 def testReplaceResizeCbfs(self): 3319 """Test replacing a single file in CBFS with one of a different size""" 3320 self._CheckLz4() 3321 expected = U_BOOT_DATA + b'x' 3322 data = self._DoReadFileRealDtb('142_replace_cbfs.dts') 3323 updated_fname = tools.GetOutputFilename('image-updated.bin') 3324 tools.WriteFile(updated_fname, data) 3325 entry_name = 'section/cbfs/u-boot' 3326 control.WriteEntry(updated_fname, entry_name, expected, 3327 allow_resize=True) 3328 data = control.ReadEntry(updated_fname, entry_name) 3329 self.assertEqual(expected, data) 3330 3331 def _SetupForReplace(self): 3332 """Set up some files to use to replace entries 3333 3334 This generates an image, copies it to a new file, extracts all the files 3335 in it and updates some of them 3336 3337 Returns: 3338 List 3339 Image filename 3340 Output directory 3341 Expected values for updated entries, each a string 3342 """ 3343 data = self._DoReadFileRealDtb('143_replace_all.dts') 3344 3345 updated_fname = tools.GetOutputFilename('image-updated.bin') 3346 tools.WriteFile(updated_fname, data) 3347 3348 outdir = os.path.join(self._indir, 'extract') 3349 einfos = control.ExtractEntries(updated_fname, None, outdir, []) 3350 3351 expected1 = b'x' + U_BOOT_DATA + b'y' 3352 u_boot_fname1 = os.path.join(outdir, 'u-boot') 3353 tools.WriteFile(u_boot_fname1, expected1) 3354 3355 expected2 = b'a' + U_BOOT_DATA + b'b' 3356 u_boot_fname2 = os.path.join(outdir, 'u-boot2') 3357 tools.WriteFile(u_boot_fname2, expected2) 3358 3359 expected_text = b'not the same text' 3360 text_fname = os.path.join(outdir, 'text') 3361 tools.WriteFile(text_fname, expected_text) 3362 3363 dtb_fname = os.path.join(outdir, 'u-boot-dtb') 3364 dtb = fdt.FdtScan(dtb_fname) 3365 node = dtb.GetNode('/binman/text') 3366 node.AddString('my-property', 'the value') 3367 dtb.Sync(auto_resize=True) 3368 dtb.Flush() 3369 3370 return updated_fname, outdir, expected1, expected2, expected_text 3371 3372 def _CheckReplaceMultiple(self, entry_paths): 3373 """Handle replacing the contents of multiple entries 3374 3375 Args: 3376 entry_paths: List of entry paths to replace 3377 3378 Returns: 3379 List 3380 Dict of entries in the image: 3381 key: Entry name 3382 Value: Entry object 3383 Expected values for updated entries, each a string 3384 """ 3385 updated_fname, outdir, expected1, expected2, expected_text = ( 3386 self._SetupForReplace()) 3387 control.ReplaceEntries(updated_fname, None, outdir, entry_paths) 3388 3389 image = Image.FromFile(updated_fname) 3390 image.LoadData() 3391 return image.GetEntries(), expected1, expected2, expected_text 3392 3393 def testReplaceAll(self): 3394 """Test replacing the contents of all entries""" 3395 entries, expected1, expected2, expected_text = ( 3396 self._CheckReplaceMultiple([])) 3397 data = entries['u-boot'].data 3398 self.assertEqual(expected1, data) 3399 3400 data = entries['u-boot2'].data 3401 self.assertEqual(expected2, data) 3402 3403 data = entries['text'].data 3404 self.assertEqual(expected_text, data) 3405 3406 # Check that the device tree is updated 3407 data = entries['u-boot-dtb'].data 3408 dtb = fdt.Fdt.FromData(data) 3409 dtb.Scan() 3410 node = dtb.GetNode('/binman/text') 3411 self.assertEqual('the value', node.props['my-property'].value) 3412 3413 def testReplaceSome(self): 3414 """Test replacing the contents of a few entries""" 3415 entries, expected1, expected2, expected_text = ( 3416 self._CheckReplaceMultiple(['u-boot2', 'text'])) 3417 3418 # This one should not change 3419 data = entries['u-boot'].data 3420 self.assertEqual(U_BOOT_DATA, data) 3421 3422 data = entries['u-boot2'].data 3423 self.assertEqual(expected2, data) 3424 3425 data = entries['text'].data 3426 self.assertEqual(expected_text, data) 3427 3428 def testReplaceCmd(self): 3429 """Test replacing a file fron an image on the command line""" 3430 self._DoReadFileRealDtb('143_replace_all.dts') 3431 3432 try: 3433 tmpdir, updated_fname = self._SetupImageInTmpdir() 3434 3435 fname = os.path.join(tmpdir, 'update-u-boot.bin') 3436 expected = b'x' * len(U_BOOT_DATA) 3437 tools.WriteFile(fname, expected) 3438 3439 self._DoBinman('replace', '-i', updated_fname, 'u-boot', '-f', fname) 3440 data = tools.ReadFile(updated_fname) 3441 self.assertEqual(expected, data[:len(expected)]) 3442 map_fname = os.path.join(tmpdir, 'image-updated.map') 3443 self.assertFalse(os.path.exists(map_fname)) 3444 finally: 3445 shutil.rmtree(tmpdir) 3446 3447 def testReplaceCmdSome(self): 3448 """Test replacing some files fron an image on the command line""" 3449 updated_fname, outdir, expected1, expected2, expected_text = ( 3450 self._SetupForReplace()) 3451 3452 self._DoBinman('replace', '-i', updated_fname, '-I', outdir, 3453 'u-boot2', 'text') 3454 3455 tools.PrepareOutputDir(None) 3456 image = Image.FromFile(updated_fname) 3457 image.LoadData() 3458 entries = image.GetEntries() 3459 3460 # This one should not change 3461 data = entries['u-boot'].data 3462 self.assertEqual(U_BOOT_DATA, data) 3463 3464 data = entries['u-boot2'].data 3465 self.assertEqual(expected2, data) 3466 3467 data = entries['text'].data 3468 self.assertEqual(expected_text, data) 3469 3470 def testReplaceMissing(self): 3471 """Test replacing entries where the file is missing""" 3472 updated_fname, outdir, expected1, expected2, expected_text = ( 3473 self._SetupForReplace()) 3474 3475 # Remove one of the files, to generate a warning 3476 u_boot_fname1 = os.path.join(outdir, 'u-boot') 3477 os.remove(u_boot_fname1) 3478 3479 with test_util.capture_sys_output() as (stdout, stderr): 3480 control.ReplaceEntries(updated_fname, None, outdir, []) 3481 self.assertIn("Skipping entry '/u-boot' from missing file", 3482 stderr.getvalue()) 3483 3484 def testReplaceCmdMap(self): 3485 """Test replacing a file fron an image on the command line""" 3486 self._DoReadFileRealDtb('143_replace_all.dts') 3487 3488 try: 3489 tmpdir, updated_fname = self._SetupImageInTmpdir() 3490 3491 fname = os.path.join(self._indir, 'update-u-boot.bin') 3492 expected = b'x' * len(U_BOOT_DATA) 3493 tools.WriteFile(fname, expected) 3494 3495 self._DoBinman('replace', '-i', updated_fname, 'u-boot', 3496 '-f', fname, '-m') 3497 map_fname = os.path.join(tmpdir, 'image-updated.map') 3498 self.assertTrue(os.path.exists(map_fname)) 3499 finally: 3500 shutil.rmtree(tmpdir) 3501 3502 def testReplaceNoEntryPaths(self): 3503 """Test replacing an entry without an entry path""" 3504 self._DoReadFileRealDtb('143_replace_all.dts') 3505 image_fname = tools.GetOutputFilename('image.bin') 3506 with self.assertRaises(ValueError) as e: 3507 control.ReplaceEntries(image_fname, 'fname', None, []) 3508 self.assertIn('Must specify an entry path to read with -f', 3509 str(e.exception)) 3510 3511 def testReplaceTooManyEntryPaths(self): 3512 """Test extracting some entries""" 3513 self._DoReadFileRealDtb('143_replace_all.dts') 3514 image_fname = tools.GetOutputFilename('image.bin') 3515 with self.assertRaises(ValueError) as e: 3516 control.ReplaceEntries(image_fname, 'fname', None, ['a', 'b']) 3517 self.assertIn('Must specify exactly one entry path to write with -f', 3518 str(e.exception)) 3519 3520 def testPackReset16(self): 3521 """Test that an image with an x86 reset16 region can be created""" 3522 data = self._DoReadFile('144_x86_reset16.dts') 3523 self.assertEqual(X86_RESET16_DATA, data[:len(X86_RESET16_DATA)]) 3524 3525 def testPackReset16Spl(self): 3526 """Test that an image with an x86 reset16-spl region can be created""" 3527 data = self._DoReadFile('145_x86_reset16_spl.dts') 3528 self.assertEqual(X86_RESET16_SPL_DATA, data[:len(X86_RESET16_SPL_DATA)]) 3529 3530 def testPackReset16Tpl(self): 3531 """Test that an image with an x86 reset16-tpl region can be created""" 3532 data = self._DoReadFile('146_x86_reset16_tpl.dts') 3533 self.assertEqual(X86_RESET16_TPL_DATA, data[:len(X86_RESET16_TPL_DATA)]) 3534 3535 def testPackIntelFit(self): 3536 """Test that an image with an Intel FIT and pointer can be created""" 3537 data = self._DoReadFile('147_intel_fit.dts') 3538 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)]) 3539 fit = data[16:32]; 3540 self.assertEqual(b'_FIT_ \x01\x00\x00\x00\x00\x01\x80}' , fit) 3541 ptr = struct.unpack('<i', data[0x40:0x44])[0] 3542 3543 image = control.images['image'] 3544 entries = image.GetEntries() 3545 expected_ptr = entries['intel-fit'].image_pos - (1 << 32) 3546 self.assertEqual(expected_ptr, ptr) 3547 3548 def testPackIntelFitMissing(self): 3549 """Test detection of a FIT pointer with not FIT region""" 3550 with self.assertRaises(ValueError) as e: 3551 self._DoReadFile('148_intel_fit_missing.dts') 3552 self.assertIn("'intel-fit-ptr' section must have an 'intel-fit' sibling", 3553 str(e.exception)) 3554 3555 def _CheckSymbolsTplSection(self, dts, expected_vals): 3556 data = self._DoReadFile(dts) 3557 sym_values = struct.pack('<LQLL', *expected_vals) 3558 upto1 = 4 + len(U_BOOT_SPL_DATA) 3559 expected1 = tools.GetBytes(0xff, 4) + sym_values + U_BOOT_SPL_DATA[20:] 3560 self.assertEqual(expected1, data[:upto1]) 3561 3562 upto2 = upto1 + 1 + len(U_BOOT_SPL_DATA) 3563 expected2 = tools.GetBytes(0xff, 1) + sym_values + U_BOOT_SPL_DATA[20:] 3564 self.assertEqual(expected2, data[upto1:upto2]) 3565 3566 upto3 = 0x34 + len(U_BOOT_DATA) 3567 expected3 = tools.GetBytes(0xff, 1) + U_BOOT_DATA 3568 self.assertEqual(expected3, data[upto2:upto3]) 3569 3570 expected4 = sym_values + U_BOOT_TPL_DATA[20:] 3571 self.assertEqual(expected4, data[upto3:upto3 + len(U_BOOT_TPL_DATA)]) 3572 3573 def testSymbolsTplSection(self): 3574 """Test binman can assign symbols embedded in U-Boot TPL in a section""" 3575 self._SetupSplElf('u_boot_binman_syms') 3576 self._SetupTplElf('u_boot_binman_syms') 3577 self._CheckSymbolsTplSection('149_symbols_tpl.dts', 3578 [0x04, 0x1c, 0x10 + 0x34, 0x04]) 3579 3580 def testSymbolsTplSectionX86(self): 3581 """Test binman can assign symbols in a section with end-at-4gb""" 3582 self._SetupSplElf('u_boot_binman_syms_x86') 3583 self._SetupTplElf('u_boot_binman_syms_x86') 3584 self._CheckSymbolsTplSection('155_symbols_tpl_x86.dts', 3585 [0xffffff04, 0xffffff1c, 0xffffff34, 3586 0x04]) 3587 3588 def testPackX86RomIfwiSectiom(self): 3589 """Test that a section can be placed in an IFWI region""" 3590 self._SetupIfwi('fitimage.bin') 3591 data = self._DoReadFile('151_x86_rom_ifwi_section.dts') 3592 self._CheckIfwi(data) 3593 3594 def testPackFspM(self): 3595 """Test that an image with a FSP memory-init binary can be created""" 3596 data = self._DoReadFile('152_intel_fsp_m.dts') 3597 self.assertEqual(FSP_M_DATA, data[:len(FSP_M_DATA)]) 3598 3599 def testPackFspS(self): 3600 """Test that an image with a FSP silicon-init binary can be created""" 3601 data = self._DoReadFile('153_intel_fsp_s.dts') 3602 self.assertEqual(FSP_S_DATA, data[:len(FSP_S_DATA)]) 3603 3604 def testPackFspT(self): 3605 """Test that an image with a FSP temp-ram-init binary can be created""" 3606 data = self._DoReadFile('154_intel_fsp_t.dts') 3607 self.assertEqual(FSP_T_DATA, data[:len(FSP_T_DATA)]) 3608 3609 def testMkimage(self): 3610 """Test using mkimage to build an image""" 3611 data = self._DoReadFile('156_mkimage.dts') 3612 3613 # Just check that the data appears in the file somewhere 3614 self.assertIn(U_BOOT_SPL_DATA, data) 3615 3616 def testExtblob(self): 3617 """Test an image with an external blob""" 3618 data = self._DoReadFile('157_blob_ext.dts') 3619 self.assertEqual(REFCODE_DATA, data) 3620 3621 def testExtblobMissing(self): 3622 """Test an image with a missing external blob""" 3623 with self.assertRaises(ValueError) as e: 3624 self._DoReadFile('158_blob_ext_missing.dts') 3625 self.assertIn("Filename 'missing-file' not found in input path", 3626 str(e.exception)) 3627 3628 def testExtblobMissingOk(self): 3629 """Test an image with an missing external blob that is allowed""" 3630 with test_util.capture_sys_output() as (stdout, stderr): 3631 self._DoTestFile('158_blob_ext_missing.dts', allow_missing=True) 3632 err = stderr.getvalue() 3633 self.assertRegex(err, "Image 'main-section'.*missing.*: blob-ext") 3634 3635 def testExtblobMissingOkSect(self): 3636 """Test an image with an missing external blob that is allowed""" 3637 with test_util.capture_sys_output() as (stdout, stderr): 3638 self._DoTestFile('159_blob_ext_missing_sect.dts', 3639 allow_missing=True) 3640 err = stderr.getvalue() 3641 self.assertRegex(err, "Image 'main-section'.*missing.*: " 3642 "blob-ext blob-ext2") 3643 3644 def testPackX86RomMeMissingDesc(self): 3645 """Test that an missing Intel descriptor entry is allowed""" 3646 with test_util.capture_sys_output() as (stdout, stderr): 3647 self._DoTestFile('164_x86_rom_me_missing.dts', allow_missing=True) 3648 err = stderr.getvalue() 3649 self.assertRegex(err, 3650 "Image 'main-section'.*missing.*: intel-descriptor") 3651 3652 def testPackX86RomMissingIfwi(self): 3653 """Test that an x86 ROM with Integrated Firmware Image can be created""" 3654 self._SetupIfwi('fitimage.bin') 3655 pathname = os.path.join(self._indir, 'fitimage.bin') 3656 os.remove(pathname) 3657 with test_util.capture_sys_output() as (stdout, stderr): 3658 self._DoTestFile('111_x86_rom_ifwi.dts', allow_missing=True) 3659 err = stderr.getvalue() 3660 self.assertRegex(err, "Image 'main-section'.*missing.*: intel-ifwi") 3661 3662 def testPackOverlap(self): 3663 """Test that zero-size overlapping regions are ignored""" 3664 self._DoTestFile('160_pack_overlap_zero.dts') 3665 3666 def testSimpleFit(self): 3667 """Test an image with a FIT inside""" 3668 data = self._DoReadFile('161_fit.dts') 3669 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)]) 3670 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):]) 3671 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)] 3672 3673 # The data should be inside the FIT 3674 dtb = fdt.Fdt.FromData(fit_data) 3675 dtb.Scan() 3676 fnode = dtb.GetNode('/images/kernel') 3677 self.assertIn('data', fnode.props) 3678 3679 fname = os.path.join(self._indir, 'fit_data.fit') 3680 tools.WriteFile(fname, fit_data) 3681 out = tools.Run('dumpimage', '-l', fname) 3682 3683 # Check a few features to make sure the plumbing works. We don't need 3684 # to test the operation of mkimage or dumpimage here. First convert the 3685 # output into a dict where the keys are the fields printed by dumpimage 3686 # and the values are a list of values for each field 3687 lines = out.splitlines() 3688 3689 # Converts "Compression: gzip compressed" into two groups: 3690 # 'Compression' and 'gzip compressed' 3691 re_line = re.compile(r'^ *([^:]*)(?:: *(.*))?$') 3692 vals = collections.defaultdict(list) 3693 for line in lines: 3694 mat = re_line.match(line) 3695 vals[mat.group(1)].append(mat.group(2)) 3696 3697 self.assertEquals('FIT description: test-desc', lines[0]) 3698 self.assertIn('Created:', lines[1]) 3699 self.assertIn('Image 0 (kernel)', vals) 3700 self.assertIn('Hash value', vals) 3701 data_sizes = vals.get('Data Size') 3702 self.assertIsNotNone(data_sizes) 3703 self.assertEqual(2, len(data_sizes)) 3704 # Format is "4 Bytes = 0.00 KiB = 0.00 MiB" so take the first word 3705 self.assertEqual(len(U_BOOT_DATA), int(data_sizes[0].split()[0])) 3706 self.assertEqual(len(U_BOOT_SPL_DTB_DATA), int(data_sizes[1].split()[0])) 3707 3708 def testFitExternal(self): 3709 """Test an image with an FIT with external images""" 3710 data = self._DoReadFile('162_fit_external.dts') 3711 fit_data = data[len(U_BOOT_DATA):-2] # _testing is 2 bytes 3712 3713 # The data should be outside the FIT 3714 dtb = fdt.Fdt.FromData(fit_data) 3715 dtb.Scan() 3716 fnode = dtb.GetNode('/images/kernel') 3717 self.assertNotIn('data', fnode.props) 3718 3719 def testSectionIgnoreHashSignature(self): 3720 """Test that sections ignore hash, signature nodes for its data""" 3721 data = self._DoReadFile('165_section_ignore_hash_signature.dts') 3722 expected = (U_BOOT_DATA + U_BOOT_DATA) 3723 self.assertEqual(expected, data) 3724 3725 def testPadInSections(self): 3726 """Test pad-before, pad-after for entries in sections""" 3727 data, _, _, out_dtb_fname = self._DoReadFileDtb( 3728 '166_pad_in_sections.dts', update_dtb=True) 3729 expected = (U_BOOT_DATA + tools.GetBytes(ord('!'), 12) + 3730 U_BOOT_DATA + tools.GetBytes(ord('!'), 6) + 3731 U_BOOT_DATA) 3732 self.assertEqual(expected, data) 3733 3734 dtb = fdt.Fdt(out_dtb_fname) 3735 dtb.Scan() 3736 props = self._GetPropTree(dtb, ['size', 'image-pos', 'offset']) 3737 expected = { 3738 'image-pos': 0, 3739 'offset': 0, 3740 'size': 12 + 6 + 3 * len(U_BOOT_DATA), 3741 3742 'section:image-pos': 0, 3743 'section:offset': 0, 3744 'section:size': 12 + 6 + 3 * len(U_BOOT_DATA), 3745 3746 'section/before:image-pos': 0, 3747 'section/before:offset': 0, 3748 'section/before:size': len(U_BOOT_DATA), 3749 3750 'section/u-boot:image-pos': 4, 3751 'section/u-boot:offset': 4, 3752 'section/u-boot:size': 12 + len(U_BOOT_DATA) + 6, 3753 3754 'section/after:image-pos': 26, 3755 'section/after:offset': 26, 3756 'section/after:size': len(U_BOOT_DATA), 3757 } 3758 self.assertEqual(expected, props) 3759 3760 def testFitImageSubentryAlignment(self): 3761 """Test relative alignability of FIT image subentries""" 3762 entry_args = { 3763 'test-id': TEXT_DATA, 3764 } 3765 data, _, _, _ = self._DoReadFileDtb('167_fit_image_subentry_alignment.dts', 3766 entry_args=entry_args) 3767 dtb = fdt.Fdt.FromData(data) 3768 dtb.Scan() 3769 3770 node = dtb.GetNode('/images/kernel') 3771 data = dtb.GetProps(node)["data"].bytes 3772 align_pad = 0x10 - (len(U_BOOT_SPL_DATA) % 0x10) 3773 expected = (tools.GetBytes(0, 0x20) + U_BOOT_SPL_DATA + 3774 tools.GetBytes(0, align_pad) + U_BOOT_DATA) 3775 self.assertEqual(expected, data) 3776 3777 node = dtb.GetNode('/images/fdt-1') 3778 data = dtb.GetProps(node)["data"].bytes 3779 expected = (U_BOOT_SPL_DTB_DATA + tools.GetBytes(0, 20) + 3780 tools.ToBytes(TEXT_DATA) + tools.GetBytes(0, 30) + 3781 U_BOOT_DTB_DATA) 3782 self.assertEqual(expected, data) 3783 3784 def testFitExtblobMissingOk(self): 3785 """Test a FIT with a missing external blob that is allowed""" 3786 with test_util.capture_sys_output() as (stdout, stderr): 3787 self._DoTestFile('168_fit_missing_blob.dts', 3788 allow_missing=True) 3789 err = stderr.getvalue() 3790 self.assertRegex(err, "Image 'main-section'.*missing.*: atf-bl31") 3791 3792 def testBlobNamedByArgMissing(self): 3793 """Test handling of a missing entry arg""" 3794 with self.assertRaises(ValueError) as e: 3795 self._DoReadFile('068_blob_named_by_arg.dts') 3796 self.assertIn("Missing required properties/entry args: cros-ec-rw-path", 3797 str(e.exception)) 3798 3799 def testPackBl31(self): 3800 """Test that an image with an ATF BL31 binary can be created""" 3801 data = self._DoReadFile('169_atf_bl31.dts') 3802 self.assertEqual(ATF_BL31_DATA, data[:len(ATF_BL31_DATA)]) 3803 3804 def testPackScp(self): 3805 """Test that an image with an SCP binary can be created""" 3806 data = self._DoReadFile('172_scp.dts') 3807 self.assertEqual(SCP_DATA, data[:len(SCP_DATA)]) 3808 3809 def testFitFdt(self): 3810 """Test an image with an FIT with multiple FDT images""" 3811 def _CheckFdt(seq, expected_data): 3812 """Check the FDT nodes 3813 3814 Args: 3815 seq: Sequence number to check (0 or 1) 3816 expected_data: Expected contents of 'data' property 3817 """ 3818 name = 'fdt-%d' % seq 3819 fnode = dtb.GetNode('/images/%s' % name) 3820 self.assertIsNotNone(fnode) 3821 self.assertEqual({'description','type', 'compression', 'data'}, 3822 set(fnode.props.keys())) 3823 self.assertEqual(expected_data, fnode.props['data'].bytes) 3824 self.assertEqual('fdt-test-fdt%d.dtb' % seq, 3825 fnode.props['description'].value) 3826 3827 def _CheckConfig(seq, expected_data): 3828 """Check the configuration nodes 3829 3830 Args: 3831 seq: Sequence number to check (0 or 1) 3832 expected_data: Expected contents of 'data' property 3833 """ 3834 cnode = dtb.GetNode('/configurations') 3835 self.assertIn('default', cnode.props) 3836 self.assertEqual('config-2', cnode.props['default'].value) 3837 3838 name = 'config-%d' % seq 3839 fnode = dtb.GetNode('/configurations/%s' % name) 3840 self.assertIsNotNone(fnode) 3841 self.assertEqual({'description','firmware', 'loadables', 'fdt'}, 3842 set(fnode.props.keys())) 3843 self.assertEqual('conf-test-fdt%d.dtb' % seq, 3844 fnode.props['description'].value) 3845 self.assertEqual('fdt-%d' % seq, fnode.props['fdt'].value) 3846 3847 entry_args = { 3848 'of-list': 'test-fdt1 test-fdt2', 3849 'default-dt': 'test-fdt2', 3850 } 3851 data = self._DoReadFileDtb( 3852 '170_fit_fdt.dts', 3853 entry_args=entry_args, 3854 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0] 3855 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):]) 3856 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)] 3857 3858 dtb = fdt.Fdt.FromData(fit_data) 3859 dtb.Scan() 3860 fnode = dtb.GetNode('/images/kernel') 3861 self.assertIn('data', fnode.props) 3862 3863 # Check all the properties in fdt-1 and fdt-2 3864 _CheckFdt(1, TEST_FDT1_DATA) 3865 _CheckFdt(2, TEST_FDT2_DATA) 3866 3867 # Check configurations 3868 _CheckConfig(1, TEST_FDT1_DATA) 3869 _CheckConfig(2, TEST_FDT2_DATA) 3870 3871 def testFitFdtMissingList(self): 3872 """Test handling of a missing 'of-list' entry arg""" 3873 with self.assertRaises(ValueError) as e: 3874 self._DoReadFile('170_fit_fdt.dts') 3875 self.assertIn("Generator node requires 'of-list' entry argument", 3876 str(e.exception)) 3877 3878 def testFitFdtEmptyList(self): 3879 """Test handling of an empty 'of-list' entry arg""" 3880 entry_args = { 3881 'of-list': '', 3882 } 3883 data = self._DoReadFileDtb('170_fit_fdt.dts', entry_args=entry_args)[0] 3884 3885 def testFitFdtMissingProp(self): 3886 """Test handling of a missing 'fit,fdt-list' property""" 3887 with self.assertRaises(ValueError) as e: 3888 self._DoReadFile('171_fit_fdt_missing_prop.dts') 3889 self.assertIn("Generator node requires 'fit,fdt-list' property", 3890 str(e.exception)) 3891 3892 def testFitFdtEmptyList(self): 3893 """Test handling of an empty 'of-list' entry arg""" 3894 entry_args = { 3895 'of-list': '', 3896 } 3897 data = self._DoReadFileDtb('170_fit_fdt.dts', entry_args=entry_args)[0] 3898 3899 def testFitFdtMissing(self): 3900 """Test handling of a missing 'default-dt' entry arg""" 3901 entry_args = { 3902 'of-list': 'test-fdt1 test-fdt2', 3903 } 3904 with self.assertRaises(ValueError) as e: 3905 self._DoReadFileDtb( 3906 '170_fit_fdt.dts', 3907 entry_args=entry_args, 3908 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0] 3909 self.assertIn("Generated 'default' node requires default-dt entry argument", 3910 str(e.exception)) 3911 3912 def testFitFdtNotInList(self): 3913 """Test handling of a default-dt that is not in the of-list""" 3914 entry_args = { 3915 'of-list': 'test-fdt1 test-fdt2', 3916 'default-dt': 'test-fdt3', 3917 } 3918 with self.assertRaises(ValueError) as e: 3919 self._DoReadFileDtb( 3920 '170_fit_fdt.dts', 3921 entry_args=entry_args, 3922 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0] 3923 self.assertIn("default-dt entry argument 'test-fdt3' not found in fdt list: test-fdt1, test-fdt2", 3924 str(e.exception)) 3925 3926 def testFitExtblobMissingHelp(self): 3927 """Test display of help messages when an external blob is missing""" 3928 control.missing_blob_help = control._ReadMissingBlobHelp() 3929 control.missing_blob_help['wibble'] = 'Wibble test' 3930 control.missing_blob_help['another'] = 'Another test' 3931 with test_util.capture_sys_output() as (stdout, stderr): 3932 self._DoTestFile('168_fit_missing_blob.dts', 3933 allow_missing=True) 3934 err = stderr.getvalue() 3935 3936 # We can get the tag from the name, the type or the missing-msg 3937 # property. Check all three. 3938 self.assertIn('You may need to build ARM Trusted', err) 3939 self.assertIn('Wibble test', err) 3940 self.assertIn('Another test', err) 3941 3942 def testMissingBlob(self): 3943 """Test handling of a blob containing a missing file""" 3944 with self.assertRaises(ValueError) as e: 3945 self._DoTestFile('173_missing_blob.dts', allow_missing=True) 3946 self.assertIn("Filename 'missing' not found in input path", 3947 str(e.exception)) 3948 3949 def testEnvironment(self): 3950 """Test adding a U-Boot environment""" 3951 data = self._DoReadFile('174_env.dts') 3952 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)]) 3953 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):]) 3954 env = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)] 3955 self.assertEqual(b'\x1b\x97\x22\x7c\x01var1=1\0var2="2"\0\0\xff\xff', 3956 env) 3957 3958 def testEnvironmentNoSize(self): 3959 """Test that a missing 'size' property is detected""" 3960 with self.assertRaises(ValueError) as e: 3961 self._DoTestFile('175_env_no_size.dts') 3962 self.assertIn("'u-boot-env' entry must have a size property", 3963 str(e.exception)) 3964 3965 def testEnvironmentTooSmall(self): 3966 """Test handling of an environment that does not fit""" 3967 with self.assertRaises(ValueError) as e: 3968 self._DoTestFile('176_env_too_small.dts') 3969 3970 # checksum, start byte, environment with \0 terminator, final \0 3971 need = 4 + 1 + len(ENV_DATA) + 1 + 1 3972 short = need - 0x8 3973 self.assertIn("too small to hold data (need %#x more bytes)" % short, 3974 str(e.exception)) 3975 3976 def testSkipAtStart(self): 3977 """Test handling of skip-at-start section""" 3978 data = self._DoReadFile('177_skip_at_start.dts') 3979 self.assertEqual(U_BOOT_DATA, data) 3980 3981 image = control.images['image'] 3982 entries = image.GetEntries() 3983 section = entries['section'] 3984 self.assertEqual(0, section.offset) 3985 self.assertEqual(len(U_BOOT_DATA), section.size) 3986 self.assertEqual(U_BOOT_DATA, section.GetData()) 3987 3988 entry = section.GetEntries()['u-boot'] 3989 self.assertEqual(16, entry.offset) 3990 self.assertEqual(len(U_BOOT_DATA), entry.size) 3991 self.assertEqual(U_BOOT_DATA, entry.data) 3992 3993 def testSkipAtStartPad(self): 3994 """Test handling of skip-at-start section with padded entry""" 3995 data = self._DoReadFile('178_skip_at_start_pad.dts') 3996 before = tools.GetBytes(0, 8) 3997 after = tools.GetBytes(0, 4) 3998 all = before + U_BOOT_DATA + after 3999 self.assertEqual(all, data) 4000
4001 image = control.images['image'] 4002 entries = image.GetEntries() 4003 section = entries['section'] 4004 self.assertEqual(0, section.offset) 4005 self.assertEqual(len(all), section.size) 4006 self.assertEqual(all, section.GetData()) 4007 4008 entry = section.GetEntries()['u-boot'] 4009 self.assertEqual(16, entry.offset) 4010 self.assertEqual(len(all), entry.size) 4011 self.assertEqual(U_BOOT_DATA, entry.data) 4012 4013 def testSkipAtStartSectionPad(self): 4014 """Test handling of skip-at-start section with padding""" 4015 data = self._DoReadFile('179_skip_at_start_section_pad.dts') 4016 before = tools.GetBytes(0, 8) 4017 after = tools.GetBytes(0, 4) 4018 all = before + U_BOOT_DATA + after 4019 self.assertEqual(all, data) 4020 4021 image = control.images['image'] 4022 entries = image.GetEntries() 4023 section = entries['section'] 4024 self.assertEqual(0, section.offset) 4025 self.assertEqual(len(all), section.size) 4026 self.assertEqual(U_BOOT_DATA, section.data) 4027 self.assertEqual(all, section.GetPaddedData()) 4028 4029 entry = section.GetEntries()['u-boot'] 4030 self.assertEqual(16, entry.offset) 4031 self.assertEqual(len(U_BOOT_DATA), entry.size) 4032 self.assertEqual(U_BOOT_DATA, entry.data) 4033 4034 def testSectionPad(self): 4035 """Testing padding with sections""" 4036 data = self._DoReadFile('180_section_pad.dts') 4037 expected = (tools.GetBytes(ord('&'), 3) + 4038 tools.GetBytes(ord('!'), 5) + 4039 U_BOOT_DATA + 4040 tools.GetBytes(ord('!'), 1) + 4041 tools.GetBytes(ord('&'), 2)) 4042 self.assertEqual(expected, data) 4043 4044 def testSectionAlign(self): 4045 """Testing alignment with sections""" 4046 data = self._DoReadFileDtb('181_section_align.dts', map=True)[0] 4047 expected = (b'\0' + # fill section 4048 tools.GetBytes(ord('&'), 1) + # padding to section align 4049 b'\0' + # fill section 4050 tools.GetBytes(ord('!'), 3) + # padding to u-boot align 4051 U_BOOT_DATA + 4052 tools.GetBytes(ord('!'), 4) + # padding to u-boot size 4053 tools.GetBytes(ord('!'), 4)) # padding to section size 4054 self.assertEqual(expected, data) 4055 4056 def testCompressImage(self): 4057 """Test compression of the entire image""" 4058 self._CheckLz4() 4059 data, _, _, out_dtb_fname = self._DoReadFileDtb( 4060 '182_compress_image.dts', use_real_dtb=True, update_dtb=True) 4061 dtb = fdt.Fdt(out_dtb_fname) 4062 dtb.Scan() 4063 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size', 4064 'uncomp-size']) 4065 orig = self._decompress(data) 4066 self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, orig) 4067 4068 # Do a sanity check on various fields 4069 image = control.images['image'] 4070 entries = image.GetEntries() 4071 self.assertEqual(2, len(entries)) 4072 4073 entry = entries['blob'] 4074 self.assertEqual(COMPRESS_DATA, entry.data) 4075 self.assertEqual(len(COMPRESS_DATA), entry.size) 4076 4077 entry = entries['u-boot'] 4078 self.assertEqual(U_BOOT_DATA, entry.data) 4079 self.assertEqual(len(U_BOOT_DATA), entry.size) 4080 4081 self.assertEqual(len(data), image.size) 4082 self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, image.uncomp_data) 4083 self.assertEqual(len(COMPRESS_DATA + U_BOOT_DATA), image.uncomp_size) 4084 orig = self._decompress(image.data) 4085 self.assertEqual(orig, image.uncomp_data) 4086 4087 expected = { 4088 'blob:offset': 0, 4089 'blob:size': len(COMPRESS_DATA), 4090 'u-boot:offset': len(COMPRESS_DATA), 4091 'u-boot:size': len(U_BOOT_DATA), 4092 'uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA), 4093 'offset': 0, 4094 'image-pos': 0, 4095 'size': len(data), 4096 } 4097 self.assertEqual(expected, props) 4098 4099 def testCompressImageLess(self): 4100 """Test compression where compression reduces the image size""" 4101 self._CheckLz4() 4102 data, _, _, out_dtb_fname = self._DoReadFileDtb( 4103 '183_compress_image_less.dts', use_real_dtb=True, update_dtb=True) 4104 dtb = fdt.Fdt(out_dtb_fname) 4105 dtb.Scan() 4106 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size', 4107 'uncomp-size']) 4108 orig = self._decompress(data) 4109 4110 self.assertEquals(COMPRESS_DATA + COMPRESS_DATA + U_BOOT_DATA, orig) 4111 4112 # Do a sanity check on various fields 4113 image = control.images['image'] 4114 entries = image.GetEntries() 4115 self.assertEqual(2, len(entries)) 4116 4117 entry = entries['blob'] 4118 self.assertEqual(COMPRESS_DATA_BIG, entry.data) 4119 self.assertEqual(len(COMPRESS_DATA_BIG), entry.size) 4120 4121 entry = entries['u-boot'] 4122 self.assertEqual(U_BOOT_DATA, entry.data) 4123 self.assertEqual(len(U_BOOT_DATA), entry.size) 4124 4125 self.assertEqual(len(data), image.size) 4126 self.assertEqual(COMPRESS_DATA_BIG + U_BOOT_DATA, image.uncomp_data) 4127 self.assertEqual(len(COMPRESS_DATA_BIG + U_BOOT_DATA), 4128 image.uncomp_size) 4129 orig = self._decompress(image.data) 4130 self.assertEqual(orig, image.uncomp_data) 4131 4132 expected = { 4133 'blob:offset': 0, 4134 'blob:size': len(COMPRESS_DATA_BIG), 4135 'u-boot:offset': len(COMPRESS_DATA_BIG), 4136 'u-boot:size': len(U_BOOT_DATA), 4137 'uncomp-size': len(COMPRESS_DATA_BIG + U_BOOT_DATA), 4138 'offset': 0, 4139 'image-pos': 0, 4140 'size': len(data), 4141 } 4142 self.assertEqual(expected, props) 4143 4144 def testCompressSectionSize(self): 4145 """Test compression of a section with a fixed size""" 4146 self._CheckLz4() 4147 data, _, _, out_dtb_fname = self._DoReadFileDtb( 4148 '184_compress_section_size.dts', use_real_dtb=True, update_dtb=True) 4149 dtb = fdt.Fdt(out_dtb_fname) 4150 dtb.Scan() 4151 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size', 4152 'uncomp-size']) 4153 orig = self._decompress(data) 4154 self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, orig) 4155 expected = { 4156 'section/blob:offset': 0, 4157 'section/blob:size': len(COMPRESS_DATA), 4158 'section/u-boot:offset': len(COMPRESS_DATA), 4159 'section/u-boot:size': len(U_BOOT_DATA), 4160 'section:offset': 0, 4161 'section:image-pos': 0, 4162 'section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA), 4163 'section:size': 0x30, 4164 'offset': 0, 4165 'image-pos': 0, 4166 'size': 0x30, 4167 } 4168 self.assertEqual(expected, props) 4169 4170 def testCompressSection(self): 4171 """Test compression of a section with no fixed size""" 4172 self._CheckLz4() 4173 data, _, _, out_dtb_fname = self._DoReadFileDtb( 4174 '185_compress_section.dts', use_real_dtb=True, update_dtb=True) 4175 dtb = fdt.Fdt(out_dtb_fname) 4176 dtb.Scan() 4177 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size', 4178 'uncomp-size']) 4179 orig = self._decompress(data) 4180 self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, orig) 4181 expected = { 4182 'section/blob:offset': 0, 4183 'section/blob:size': len(COMPRESS_DATA), 4184 'section/u-boot:offset': len(COMPRESS_DATA), 4185 'section/u-boot:size': len(U_BOOT_DATA), 4186 'section:offset': 0, 4187 'section:image-pos': 0, 4188 'section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA), 4189 'section:size': len(data), 4190 'offset': 0, 4191 'image-pos': 0, 4192 'size': len(data), 4193 } 4194 self.assertEqual(expected, props) 4195 4196 def testCompressExtra(self): 4197 """Test compression of a section with no fixed size""" 4198 self._CheckLz4() 4199 data, _, _, out_dtb_fname = self._DoReadFileDtb( 4200 '186_compress_extra.dts', use_real_dtb=True, update_dtb=True) 4201 dtb = fdt.Fdt(out_dtb_fname) 4202 dtb.Scan() 4203 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size', 4204 'uncomp-size']) 4205 4206 base = data[len(U_BOOT_DATA):] 4207 self.assertEquals(U_BOOT_DATA, base[:len(U_BOOT_DATA)]) 4208 rest = base[len(U_BOOT_DATA):] 4209 4210 # Check compressed data 4211 section1 = self._decompress(rest) 4212 expect1 = tools.Compress(COMPRESS_DATA + U_BOOT_DATA, 'lz4') 4213 self.assertEquals(expect1, rest[:len(expect1)]) 4214 self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, section1) 4215 rest1 = rest[len(expect1):] 4216 4217 section2 = self._decompress(rest1) 4218 expect2 = tools.Compress(COMPRESS_DATA + COMPRESS_DATA, 'lz4') 4219 self.assertEquals(expect2, rest1[:len(expect2)]) 4220 self.assertEquals(COMPRESS_DATA + COMPRESS_DATA, section2) 4221 rest2 = rest1[len(expect2):] 4222 4223 expect_size = (len(U_BOOT_DATA) + len(U_BOOT_DATA) + len(expect1) + 4224 len(expect2) + len(U_BOOT_DATA)) 4225 #self.assertEquals(expect_size, len(data)) 4226 4227 #self.assertEquals(U_BOOT_DATA, rest2) 4228 4229 self.maxDiff = None 4230 expected = { 4231 'u-boot:offset': 0, 4232 'u-boot:image-pos': 0, 4233 'u-boot:size': len(U_BOOT_DATA), 4234 4235 'base:offset': len(U_BOOT_DATA), 4236 'base:image-pos': len(U_BOOT_DATA), 4237 'base:size': len(data) - len(U_BOOT_DATA), 4238 'base/u-boot:offset': 0, 4239 'base/u-boot:image-pos': len(U_BOOT_DATA), 4240 'base/u-boot:size': len(U_BOOT_DATA), 4241 'base/u-boot2:offset': len(U_BOOT_DATA) + len(expect1) + 4242 len(expect2), 4243 'base/u-boot2:image-pos': len(U_BOOT_DATA) * 2 + len(expect1) + 4244 len(expect2), 4245 'base/u-boot2:size': len(U_BOOT_DATA), 4246 4247 'base/section:offset': len(U_BOOT_DATA), 4248 'base/section:image-pos': len(U_BOOT_DATA) * 2, 4249 'base/section:size': len(expect1), 4250 'base/section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA), 4251 'base/section/blob:offset': 0, 4252 'base/section/blob:size': len(COMPRESS_DATA), 4253 'base/section/u-boot:offset': len(COMPRESS_DATA), 4254 'base/section/u-boot:size': len(U_BOOT_DATA), 4255 4256 'base/section2:offset': len(U_BOOT_DATA) + len(expect1), 4257 'base/section2:image-pos': len(U_BOOT_DATA) * 2 + len(expect1), 4258 'base/section2:size': len(expect2), 4259 'base/section2:uncomp-size': len(COMPRESS_DATA + COMPRESS_DATA), 4260 'base/section2/blob:offset': 0, 4261 'base/section2/blob:size': len(COMPRESS_DATA), 4262 'base/section2/blob2:offset': len(COMPRESS_DATA), 4263 'base/section2/blob2:size': len(COMPRESS_DATA), 4264 4265 'offset': 0, 4266 'image-pos': 0, 4267 'size': len(data), 4268 } 4269 self.assertEqual(expected, props) 4270 4271 def testSymbolsSubsection(self): 4272 """Test binman can assign symbols from a subsection""" 4273 self.checkSymbols('187_symbols_sub.dts', U_BOOT_SPL_DATA, 0x18) 4274 4275 def testReadImageEntryArg(self): 4276 """Test reading an image that would need an entry arg to generate""" 4277 entry_args = { 4278 'cros-ec-rw-path': 'ecrw.bin', 4279 } 4280 data = self.data = self._DoReadFileDtb( 4281 '188_image_entryarg.dts',use_real_dtb=True, update_dtb=True, 4282 entry_args=entry_args) 4283 4284 image_fname = tools.GetOutputFilename('image.bin') 4285 orig_image = control.images['image'] 4286 4287 # This should not generate an error about the missing 'cros-ec-rw-path' 4288 # since we are reading the image from a file. Compare with 4289 # testEntryArgsRequired() 4290 image = Image.FromFile(image_fname) 4291 self.assertEqual(orig_image.GetEntries().keys(), 4292 image.GetEntries().keys()) 4293 4294 def testFilesAlign(self): 4295 """Test alignment with files""" 4296 data = self._DoReadFile('190_files_align.dts') 4297 4298 # The first string is 15 bytes so will align to 16 4299 expect = FILES_DATA[:15] + b'\0' + FILES_DATA[15:] 4300 self.assertEqual(expect, data) 4301 4302 def testReadImageSkip(self): 4303 """Test reading an image and accessing its FDT map""" 4304 data = self.data = self._DoReadFileRealDtb('191_read_image_skip.dts') 4305 image_fname = tools.GetOutputFilename('image.bin') 4306 orig_image = control.images['image'] 4307 image = Image.FromFile(image_fname) 4308 self.assertEqual(orig_image.GetEntries().keys(), 4309 image.GetEntries().keys()) 4310 4311 orig_entry = orig_image.GetEntries()['fdtmap'] 4312 entry = image.GetEntries()['fdtmap'] 4313 self.assertEqual(orig_entry.offset, entry.offset) 4314 self.assertEqual(orig_entry.size, entry.size) 4315 self.assertEqual(16, entry.image_pos) 4316 4317 u_boot = image.GetEntries()['section'].GetEntries()['u-boot'] 4318 4319 self.assertEquals(U_BOOT_DATA, u_boot.ReadData()) 4320 4321 def testTplNoDtb(self): 4322 """Test that an image with tpl/u-boot-tpl-nodtb.bin can be created""" 4323 self._SetupTplElf() 4324 data = self._DoReadFile('192_u_boot_tpl_nodtb.dts') 4325 self.assertEqual(U_BOOT_TPL_NODTB_DATA, 4326 data[:len(U_BOOT_TPL_NODTB_DATA)]) 4327 4328 def testTplBssPad(self): 4329 """Test that we can pad TPL's BSS with zeros""" 4330 # ELF file with a '__bss_size' symbol 4331 self._SetupTplElf() 4332 data = self._DoReadFile('193_tpl_bss_pad.dts') 4333 self.assertEqual(U_BOOT_TPL_DATA + tools.GetBytes(0, 10) + U_BOOT_DATA, 4334 data) 4335 4336 def testTplBssPadMissing(self): 4337 """Test that a missing symbol is detected""" 4338 self._SetupTplElf('u_boot_ucode_ptr') 4339 with self.assertRaises(ValueError) as e: 4340 self._DoReadFile('193_tpl_bss_pad.dts') 4341 self.assertIn('Expected __bss_size symbol in tpl/u-boot-tpl', 4342 str(e.exception)) 4343 4344 def checkDtbSizes(self, data, pad_len, start): 4345 """Check the size arguments in a dtb embedded in an image 4346 4347 Args: 4348 data: The image data 4349 pad_len: Length of the pad section in the image, in bytes 4350 start: Start offset of the devicetree to examine, within the image 4351 4352 Returns: 4353 Size of the devicetree in bytes 4354 """ 4355 dtb_data = data[start:] 4356 dtb = fdt.Fdt.FromData(dtb_data) 4357 fdt_size = dtb.GetFdtObj().totalsize() 4358 dtb.Scan() 4359 props = self._GetPropTree(dtb, 'size') 4360 self.assertEqual({ 4361 'size': len(data), 4362 'u-boot-spl/u-boot-spl-bss-pad:size': pad_len, 4363 'u-boot-spl/u-boot-spl-dtb:size': 801, 4364 'u-boot-spl/u-boot-spl-nodtb:size': len(U_BOOT_SPL_NODTB_DATA), 4365 'u-boot-spl:size': 860, 4366 'u-boot-tpl:size': len(U_BOOT_TPL_DATA), 4367 'u-boot/u-boot-dtb:size': 781, 4368 'u-boot/u-boot-nodtb:size': len(U_BOOT_NODTB_DATA), 4369 'u-boot:size': 827, 4370 }, props) 4371 return fdt_size 4372 4373 def testExpanded(self): 4374 """Test that an expanded entry type is selected when needed""" 4375 self._SetupSplElf() 4376 self._SetupTplElf() 4377 4378 # SPL has a devicetree, TPL does not 4379 entry_args = { 4380 'spl-dtb': '1', 4381 'spl-bss-pad': 'y', 4382 'tpl-dtb': '', 4383 } 4384 self._DoReadFileDtb('194_fdt_incl.dts', use_expanded=True, 4385 entry_args=entry_args) 4386 image = control.images['image'] 4387 entries = image.GetEntries() 4388 self.assertEqual(3, len(entries)) 4389 4390 # First, u-boot, which should be expanded into u-boot-nodtb and dtb 4391 self.assertIn('u-boot', entries) 4392 entry = entries['u-boot'] 4393 self.assertEqual('u-boot-expanded', entry.etype) 4394 subent = entry.GetEntries() 4395 self.assertEqual(2, len(subent)) 4396 self.assertIn('u-boot-nodtb', subent) 4397 self.assertIn('u-boot-dtb', subent) 4398 4399 # Second, u-boot-spl, which should be expanded into three parts 4400 self.assertIn('u-boot-spl', entries) 4401 entry = entries['u-boot-spl'] 4402 self.assertEqual('u-boot-spl-expanded', entry.etype) 4403 subent = entry.GetEntries() 4404 self.assertEqual(3, len(subent)) 4405 self.assertIn('u-boot-spl-nodtb', subent) 4406 self.assertIn('u-boot-spl-bss-pad', subent) 4407 self.assertIn('u-boot-spl-dtb', subent) 4408 4409 # Third, u-boot-tpl, which should be not be expanded, since TPL has no 4410 # devicetree 4411 self.assertIn('u-boot-tpl', entries) 4412 entry = entries['u-boot-tpl'] 4413 self.assertEqual('u-boot-tpl', entry.etype) 4414 self.assertEqual(None, entry.GetEntries()) 4415 4416 def testExpandedTpl(self): 4417 """Test that an expanded entry type is selected for TPL when needed""" 4418 self._SetupTplElf() 4419 4420 entry_args = { 4421 'tpl-bss-pad': 'y', 4422 'tpl-dtb': 'y', 4423 } 4424 self._DoReadFileDtb('195_fdt_incl_tpl.dts', use_expanded=True, 4425 entry_args=entry_args) 4426 image = control.images['image'] 4427 entries = image.GetEntries() 4428 self.assertEqual(1, len(entries)) 4429 4430 # We only have u-boot-tpl, which be expanded 4431 self.assertIn('u-boot-tpl', entries) 4432 entry = entries['u-boot-tpl'] 4433 self.assertEqual('u-boot-tpl-expanded', entry.etype) 4434 subent = entry.GetEntries() 4435 self.assertEqual(3, len(subent)) 4436 self.assertIn('u-boot-tpl-nodtb', subent) 4437 self.assertIn('u-boot-tpl-bss-pad', subent) 4438 self.assertIn('u-boot-tpl-dtb', subent) 4439 4440 def testExpandedNoPad(self): 4441 """Test an expanded entry without BSS pad enabled""" 4442 self._SetupSplElf() 4443 self._SetupTplElf() 4444 4445 # SPL has a devicetree, TPL does not 4446 entry_args = { 4447 'spl-dtb': 'something', 4448 'spl-bss-pad': 'n', 4449 'tpl-dtb': '', 4450 } 4451 self._DoReadFileDtb('194_fdt_incl.dts', use_expanded=True, 4452 entry_args=entry_args) 4453 image = control.images['image'] 4454 entries = image.GetEntries() 4455 4456 # Just check u-boot-spl, which should be expanded into two parts 4457 self.assertIn('u-boot-spl', entries) 4458 entry = entries['u-boot-spl'] 4459 self.assertEqual('u-boot-spl-expanded', entry.etype) 4460 subent = entry.GetEntries() 4461 self.assertEqual(2, len(subent)) 4462 self.assertIn('u-boot-spl-nodtb', subent) 4463 self.assertIn('u-boot-spl-dtb', subent) 4464 4465 def testExpandedTplNoPad(self): 4466 """Test that an expanded entry type with padding disabled in TPL""" 4467 self._SetupTplElf() 4468 4469 entry_args = { 4470 'tpl-bss-pad': '', 4471 'tpl-dtb': 'y', 4472 } 4473 self._DoReadFileDtb('195_fdt_incl_tpl.dts', use_expanded=True, 4474 entry_args=entry_args) 4475 image = control.images['image'] 4476 entries = image.GetEntries() 4477 self.assertEqual(1, len(entries)) 4478 4479 # We only have u-boot-tpl, which be expanded 4480 self.assertIn('u-boot-tpl', entries) 4481 entry = entries['u-boot-tpl'] 4482 self.assertEqual('u-boot-tpl-expanded', entry.etype) 4483 subent = entry.GetEntries() 4484 self.assertEqual(2, len(subent)) 4485 self.assertIn('u-boot-tpl-nodtb', subent) 4486 self.assertIn('u-boot-tpl-dtb', subent) 4487 4488 def testFdtInclude(self): 4489 """Test that an Fdt is update within all binaries""" 4490 self._SetupSplElf() 4491 self._SetupTplElf() 4492 4493 # SPL has a devicetree, TPL does not 4494 self.maxDiff = None 4495 entry_args = { 4496 'spl-dtb': '1', 4497 'spl-bss-pad': 'y', 4498 'tpl-dtb': '', 4499 } 4500 # Build the image. It includes two separate devicetree binaries, each 4501 # with their own contents, but all contain the binman definition. 4502 data = self._DoReadFileDtb( 4503 '194_fdt_incl.dts', use_real_dtb=True, use_expanded=True, 4504 update_dtb=True, entry_args=entry_args)[0] 4505 pad_len = 10 4506 4507 # Check the U-Boot dtb 4508 start = len(U_BOOT_NODTB_DATA) 4509 fdt_size = self.checkDtbSizes(data, pad_len, start) 4510 4511 # Now check SPL 4512 start += fdt_size + len(U_BOOT_SPL_NODTB_DATA) + pad_len 4513 fdt_size = self.checkDtbSizes(data, pad_len, start) 4514 4515 # TPL has no devicetree 4516 start += fdt_size + len(U_BOOT_TPL_DATA) 4517 self.assertEqual(len(data), start) 4518 4519 def testSymbolsExpanded(self): 4520 """Test binman can assign symbols in expanded entries""" 4521 entry_args = { 4522 'spl-dtb': '1', 4523 } 4524 self.checkSymbols('197_symbols_expand.dts', U_BOOT_SPL_NODTB_DATA + 4525 U_BOOT_SPL_DTB_DATA, 0x38, 4526 entry_args=entry_args, use_expanded=True) 4527 4528 def testCollection(self): 4529 """Test a collection""" 4530 data = self._DoReadFile('198_collection.dts') 4531 self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA + 4532 tools.GetBytes(0xff, 2) + U_BOOT_NODTB_DATA + 4533 tools.GetBytes(0xfe, 3) + U_BOOT_DTB_DATA, 4534 data) 4535 4536 def testCollectionSection(self): 4537 """Test a collection where a section must be built first""" 4538 # Sections never have their contents when GetData() is called, but when 4539 # _BuildSectionData() is called with required=True, a section will force 4540 # building the contents, producing an error is anything is still 4541 # missing. 4542 data = self._DoReadFile('199_collection_section.dts') 4543 section = U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA 4544 self.assertEqual(section + U_BOOT_DATA + tools.GetBytes(0xff, 2) + 4545 section + tools.GetBytes(0xfe, 3) + U_BOOT_DATA, 4546 data) 4547 4548 def testAlignDefault(self): 4549 """Test that default alignment works on sections""" 4550 data = self._DoReadFile('200_align_default.dts') 4551 expected = (U_BOOT_DATA + tools.GetBytes(0, 8 - len(U_BOOT_DATA)) + 4552 U_BOOT_DATA) 4553 # Special alignment for section 4554 expected += tools.GetBytes(0, 32 - len(expected)) 4555 # No alignment within the nested section 4556 expected += U_BOOT_DATA + U_BOOT_NODTB_DATA; 4557 # Now the final piece, which should be default-aligned 4558 expected += tools.GetBytes(0, 88 - len(expected)) + U_BOOT_NODTB_DATA 4559 self.assertEqual(expected, data) 4560 4561 def testPackOpenSBI(self): 4562 """Test that an image with an OpenSBI binary can be created""" 4563 data = self._DoReadFile('201_opensbi.dts') 4564 self.assertEqual(OPENSBI_DATA, data[:len(OPENSBI_DATA)]) 4565 4566 def testSectionsSingleThread(self): 4567 """Test sections without multithreading""" 4568 data = self._DoReadFileDtb('055_sections.dts', threads=0)[0] 4569 expected = (U_BOOT_DATA + tools.GetBytes(ord('!'), 12) + 4570 U_BOOT_DATA + tools.GetBytes(ord('a'), 12) + 4571 U_BOOT_DATA + tools.GetBytes(ord('&'), 4)) 4572 self.assertEqual(expected, data) 4573 4574 def testThreadTimeout(self): 4575 """Test handling a thread that takes too long""" 4576 with self.assertRaises(ValueError) as e: 4577 self._DoTestFile('202_section_timeout.dts', 4578 test_section_timeout=True) 4579 self.assertIn("Timed out obtaining contents", str(e.exception)) 4580 4581 def testTiming(self): 4582 """Test output of timing information""" 4583 data = self._DoReadFile('055_sections.dts') 4584 with test_util.capture_sys_output() as (stdout, stderr): 4585 state.TimingShow() 4586 self.assertIn('read:', stdout.getvalue()) 4587 self.assertIn('compress:', stdout.getvalue()) 4588 4589 def testUpdateFdtInElf(self): 4590 """Test that we can update the devicetree in an ELF file""" 4591 infile = elf_fname = self.ElfTestFile('u_boot_binman_embed') 4592 outfile = os.path.join(self._indir, 'u-boot.out') 4593 begin_sym = 'dtb_embed_begin' 4594 end_sym = 'dtb_embed_end' 4595 retcode = self._DoTestFile( 4596 '060_fdt_update.dts', update_dtb=True, 4597 update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym])) 4598 self.assertEqual(0, retcode) 4599 4600 # Check that the output file does in fact contact a dtb with the binman 4601 # definition in the correct place 4602 syms = elf.GetSymbolFileOffset(infile, 4603 ['dtb_embed_begin', 'dtb_embed_end']) 4604 data = tools.ReadFile(outfile) 4605 dtb_data = data[syms['dtb_embed_begin'].offset: 4606 syms['dtb_embed_end'].offset] 4607 4608 dtb = fdt.Fdt.FromData(dtb_data) 4609 dtb.Scan() 4610 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS) 4611 self.assertEqual({ 4612 'image-pos': 0, 4613 'offset': 0, 4614 '_testing:offset': 32, 4615 '_testing:size': 2, 4616 '_testing:image-pos': 32, 4617 'section@0/u-boot:offset': 0, 4618 'section@0/u-boot:size': len(U_BOOT_DATA), 4619 'section@0/u-boot:image-pos': 0, 4620 'section@0:offset': 0, 4621 'section@0:size': 16, 4622 'section@0:image-pos': 0, 4623 4624 'section@1/u-boot:offset': 0, 4625 'section@1/u-boot:size': len(U_BOOT_DATA), 4626 'section@1/u-boot:image-pos': 16, 4627 'section@1:offset': 16, 4628 'section@1:size': 16, 4629 'section@1:image-pos': 16, 4630 'size': 40 4631 }, props) 4632 4633 def testUpdateFdtInElfInvalid(self): 4634 """Test that invalid args are detected with --update-fdt-in-elf""" 4635 with self.assertRaises(ValueError) as e: 4636 self._DoTestFile('060_fdt_update.dts', update_fdt_in_elf='fred') 4637 self.assertIn("Invalid args ['fred'] to --update-fdt-in-elf", 4638 str(e.exception)) 4639 4640 def testUpdateFdtInElfNoSyms(self): 4641 """Test that missing symbols are detected with --update-fdt-in-elf""" 4642 infile = elf_fname = self.ElfTestFile('u_boot_binman_embed') 4643 outfile = '' 4644 begin_sym = 'wrong_begin' 4645 end_sym = 'wrong_end' 4646 with self.assertRaises(ValueError) as e: 4647 self._DoTestFile( 4648 '060_fdt_update.dts', 4649 update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym])) 4650 self.assertIn("Expected two symbols 'wrong_begin' and 'wrong_end': got 0:", 4651 str(e.exception)) 4652 4653 def testUpdateFdtInElfTooSmall(self): 4654 """Test that an over-large dtb is detected with --update-fdt-in-elf""" 4655 infile = elf_fname = self.ElfTestFile('u_boot_binman_embed_sm') 4656 outfile = os.path.join(self._indir, 'u-boot.out') 4657 begin_sym = 'dtb_embed_begin' 4658 end_sym = 'dtb_embed_end' 4659 with self.assertRaises(ValueError) as e: 4660 self._DoTestFile( 4661 '060_fdt_update.dts', update_dtb=True, 4662 update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym])) 4663 self.assertRegex( 4664 str(e.exception), 4665 "Not enough space in '.*u_boot_binman_embed_sm' for data length.*") 4666 4667 def testFakeBlob(self): 4668 """Test handling of faking an external blob""" 4669 with test_util.capture_sys_output() as (stdout, stderr): 4670 self._DoTestFile('203_fake_blob.dts', allow_missing=True, 4671 allow_fake_blobs=True) 4672 err = stderr.getvalue() 4673 self.assertRegex(err, 4674 "Image '.*' has faked external blobs and is non-functional: .*") 4675 os.remove('binman_faking_test_blob') 4676 4677 4678if __name__ == "__main__": 4679 unittest.main() 4680