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 20import unittest.mock 21import urllib.error 22 23from binman import bintool 24from binman import cbfs_util 25from binman import cmdline 26from binman import control 27from binman import elf 28from binman import elf_test 29from binman import fip_util 30from binman import fmap_util 31from binman import state 32from dtoc import fdt 33from dtoc import fdt_util 34from binman.etype import fdtmap 35from binman.etype import image_header 36from binman.image import Image 37from u_boot_pylib import command 38from u_boot_pylib import test_util 39from u_boot_pylib import tools 40from u_boot_pylib import tout 41 42# Contents of test files, corresponding to different entry types 43U_BOOT_DATA = b'1234' 44U_BOOT_IMG_DATA = b'img' 45U_BOOT_SPL_DATA = b'56780123456789abcdefghijklm' 46U_BOOT_TPL_DATA = b'tpl9876543210fedcbazywvuts' 47U_BOOT_VPL_DATA = b'vpl76543210fedcbazywxyz_' 48BLOB_DATA = b'89' 49ME_DATA = b'0abcd' 50VGA_DATA = b'vga' 51EFI_CAPSULE_DATA = b'efi' 52U_BOOT_DTB_DATA = b'udtb' 53U_BOOT_SPL_DTB_DATA = b'spldtb' 54U_BOOT_TPL_DTB_DATA = b'tpldtb' 55U_BOOT_VPL_DTB_DATA = b'vpldtb' 56X86_START16_DATA = b'start16' 57X86_START16_SPL_DATA = b'start16spl' 58X86_START16_TPL_DATA = b'start16tpl' 59X86_RESET16_DATA = b'reset16' 60X86_RESET16_SPL_DATA = b'reset16spl' 61X86_RESET16_TPL_DATA = b'reset16tpl' 62PPC_MPC85XX_BR_DATA = b'ppcmpc85xxbr' 63U_BOOT_NODTB_DATA = b'nodtb with microcode pointer somewhere in here' 64U_BOOT_SPL_NODTB_DATA = b'splnodtb with microcode pointer somewhere in here' 65U_BOOT_TPL_NODTB_DATA = b'tplnodtb with microcode pointer somewhere in here' 66U_BOOT_VPL_NODTB_DATA = b'vplnodtb' 67U_BOOT_EXP_DATA = U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA 68U_BOOT_SPL_EXP_DATA = U_BOOT_SPL_NODTB_DATA + U_BOOT_SPL_DTB_DATA 69U_BOOT_TPL_EXP_DATA = U_BOOT_TPL_NODTB_DATA + U_BOOT_TPL_DTB_DATA 70FSP_DATA = b'fsp' 71CMC_DATA = b'cmc' 72VBT_DATA = b'vbt' 73MRC_DATA = b'mrc' 74TEXT_DATA = 'text' 75TEXT_DATA2 = 'text2' 76TEXT_DATA3 = 'text3' 77CROS_EC_RW_DATA = b'ecrw' 78GBB_DATA = b'gbbd' 79BMPBLK_DATA = b'bmp' 80VBLOCK_DATA = b'vblk' 81FILES_DATA = (b"sorry I'm late\nOh, don't bother apologising, I'm " + 82 b"sorry you're alive\n") 83COMPRESS_DATA = b'compress xxxxxxxxxxxxxxxxxxxxxx data' 84COMPRESS_DATA_BIG = COMPRESS_DATA * 2 85REFCODE_DATA = b'refcode' 86FSP_M_DATA = b'fsp_m' 87FSP_S_DATA = b'fsp_s' 88FSP_T_DATA = b'fsp_t' 89ATF_BL31_DATA = b'bl31' 90TEE_OS_DATA = b'this is some tee OS data' 91TI_DM_DATA = b'tidmtidm' 92ATF_BL2U_DATA = b'bl2u' 93OPENSBI_DATA = b'opensbi' 94SCP_DATA = b'scp' 95ROCKCHIP_TPL_DATA = b'rockchip-tpl' 96TEST_FDT1_DATA = b'fdt1' 97TEST_FDT2_DATA = b'test-fdt2' 98ENV_DATA = b'var1=1\nvar2="2"' 99ENCRYPTED_IV_DATA = b'123456' 100ENCRYPTED_KEY_DATA = b'abcde' 101PRE_LOAD_MAGIC = b'UBSH' 102PRE_LOAD_VERSION = 0x11223344.to_bytes(4, 'big') 103PRE_LOAD_HDR_SIZE = 0x00001000.to_bytes(4, 'big') 104TI_BOARD_CONFIG_DATA = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' 105TI_UNSECURE_DATA = b'unsecuredata' 106 107# Subdirectory of the input dir to use to put test FDTs 108TEST_FDT_SUBDIR = 'fdts' 109 110# The expected size for the device tree in some tests 111EXTRACT_DTB_SIZE = 0x3c9 112 113# Properties expected to be in the device tree when update_dtb is used 114BASE_DTB_PROPS = ['offset', 'size', 'image-pos'] 115 116# Extra properties expected to be in the device tree when allow-repack is used 117REPACK_DTB_PROPS = ['orig-offset', 'orig-size'] 118 119# Supported compression bintools 120COMP_BINTOOLS = ['bzip2', 'gzip', 'lz4', 'lzma_alone', 'lzop', 'xz', 'zstd'] 121 122TEE_ADDR = 0x5678 123 124# Firmware Management Protocol(FMP) GUID 125FW_MGMT_GUID = '6dcbd5ed-e82d-4c44-bda1-7194199ad92a' 126# Image GUID specified in the DTS 127CAPSULE_IMAGE_GUID = '09d7cf52-0720-4710-91d1-08469b7fe9c8' 128# Windows cert GUID 129WIN_CERT_TYPE_EFI_GUID = '4aafd29d-68df-49ee-8aa9-347d375665a7' 130# Empty capsule GUIDs 131EMPTY_CAPSULE_ACCEPT_GUID = '0c996046-bcc0-4d04-85ec-e1fcedf1c6f8' 132EMPTY_CAPSULE_REVERT_GUID = 'acd58b4b-c0e8-475f-99b5-6b3f7e07aaf0' 133 134class TestFunctional(unittest.TestCase): 135 """Functional tests for binman 136 137 Most of these use a sample .dts file to build an image and then check 138 that it looks correct. The sample files are in the test/ subdirectory 139 and are numbered. 140 141 For each entry type a very small test file is created using fixed 142 string contents. This makes it easy to test that things look right, and 143 debug problems. 144 145 In some cases a 'real' file must be used - these are also supplied in 146 the test/ diurectory. 147 """ 148 @classmethod 149 def setUpClass(cls): 150 global entry 151 from binman import entry 152 153 # Handle the case where argv[0] is 'python' 154 cls._binman_dir = os.path.dirname(os.path.realpath(sys.argv[0])) 155 cls._binman_pathname = os.path.join(cls._binman_dir, 'binman') 156 157 # Create a temporary directory for input files 158 cls._indir = tempfile.mkdtemp(prefix='binmant.') 159 160 # Create some test files 161 TestFunctional._MakeInputFile('u-boot.bin', U_BOOT_DATA) 162 TestFunctional._MakeInputFile('u-boot.img', U_BOOT_IMG_DATA) 163 TestFunctional._MakeInputFile('spl/u-boot-spl.bin', U_BOOT_SPL_DATA) 164 TestFunctional._MakeInputFile('tpl/u-boot-tpl.bin', U_BOOT_TPL_DATA) 165 TestFunctional._MakeInputFile('vpl/u-boot-vpl.bin', U_BOOT_VPL_DATA) 166 TestFunctional._MakeInputFile('blobfile', BLOB_DATA) 167 TestFunctional._MakeInputFile('me.bin', ME_DATA) 168 TestFunctional._MakeInputFile('vga.bin', VGA_DATA) 169 cls._ResetDtbs() 170 171 TestFunctional._MakeInputFile('u-boot-br.bin', PPC_MPC85XX_BR_DATA) 172 173 TestFunctional._MakeInputFile('u-boot-x86-start16.bin', X86_START16_DATA) 174 TestFunctional._MakeInputFile('spl/u-boot-x86-start16-spl.bin', 175 X86_START16_SPL_DATA) 176 TestFunctional._MakeInputFile('tpl/u-boot-x86-start16-tpl.bin', 177 X86_START16_TPL_DATA) 178 179 TestFunctional._MakeInputFile('u-boot-x86-reset16.bin', 180 X86_RESET16_DATA) 181 TestFunctional._MakeInputFile('spl/u-boot-x86-reset16-spl.bin', 182 X86_RESET16_SPL_DATA) 183 TestFunctional._MakeInputFile('tpl/u-boot-x86-reset16-tpl.bin', 184 X86_RESET16_TPL_DATA) 185 186 TestFunctional._MakeInputFile('u-boot-nodtb.bin', U_BOOT_NODTB_DATA) 187 TestFunctional._MakeInputFile('spl/u-boot-spl-nodtb.bin', 188 U_BOOT_SPL_NODTB_DATA) 189 TestFunctional._MakeInputFile('tpl/u-boot-tpl-nodtb.bin', 190 U_BOOT_TPL_NODTB_DATA) 191 TestFunctional._MakeInputFile('vpl/u-boot-vpl-nodtb.bin', 192 U_BOOT_VPL_NODTB_DATA) 193 TestFunctional._MakeInputFile('fsp.bin', FSP_DATA) 194 TestFunctional._MakeInputFile('cmc.bin', CMC_DATA) 195 TestFunctional._MakeInputFile('vbt.bin', VBT_DATA) 196 TestFunctional._MakeInputFile('mrc.bin', MRC_DATA) 197 TestFunctional._MakeInputFile('ecrw.bin', CROS_EC_RW_DATA) 198 TestFunctional._MakeInputDir('devkeys') 199 TestFunctional._MakeInputFile('bmpblk.bin', BMPBLK_DATA) 200 TestFunctional._MakeInputFile('refcode.bin', REFCODE_DATA) 201 TestFunctional._MakeInputFile('fsp_m.bin', FSP_M_DATA) 202 TestFunctional._MakeInputFile('fsp_s.bin', FSP_S_DATA) 203 TestFunctional._MakeInputFile('fsp_t.bin', FSP_T_DATA) 204 205 cls._elf_testdir = os.path.join(cls._indir, 'elftest') 206 elf_test.BuildElfTestFiles(cls._elf_testdir) 207 208 # ELF file with a '_dt_ucode_base_size' symbol 209 TestFunctional._MakeInputFile('u-boot', 210 tools.read_file(cls.ElfTestFile('u_boot_ucode_ptr'))) 211 212 # Intel flash descriptor file 213 cls._SetupDescriptor() 214 215 shutil.copytree(cls.TestFile('files'), 216 os.path.join(cls._indir, 'files')) 217 218 shutil.copytree(cls.TestFile('yaml'), 219 os.path.join(cls._indir, 'yaml')) 220 221 TestFunctional._MakeInputFile('compress', COMPRESS_DATA) 222 TestFunctional._MakeInputFile('compress_big', COMPRESS_DATA_BIG) 223 TestFunctional._MakeInputFile('bl31.bin', ATF_BL31_DATA) 224 TestFunctional._MakeInputFile('tee-pager.bin', TEE_OS_DATA) 225 TestFunctional._MakeInputFile('dm.bin', TI_DM_DATA) 226 TestFunctional._MakeInputFile('bl2u.bin', ATF_BL2U_DATA) 227 TestFunctional._MakeInputFile('fw_dynamic.bin', OPENSBI_DATA) 228 TestFunctional._MakeInputFile('scp.bin', SCP_DATA) 229 TestFunctional._MakeInputFile('rockchip-tpl.bin', ROCKCHIP_TPL_DATA) 230 TestFunctional._MakeInputFile('ti_unsecure.bin', TI_UNSECURE_DATA) 231 TestFunctional._MakeInputFile('capsule_input.bin', EFI_CAPSULE_DATA) 232 233 # Add a few .dtb files for testing 234 TestFunctional._MakeInputFile('%s/test-fdt1.dtb' % TEST_FDT_SUBDIR, 235 TEST_FDT1_DATA) 236 TestFunctional._MakeInputFile('%s/test-fdt2.dtb' % TEST_FDT_SUBDIR, 237 TEST_FDT2_DATA) 238 239 TestFunctional._MakeInputFile('env.txt', ENV_DATA) 240 241 # ELF file with two sections in different parts of memory, used for both 242 # ATF and OP_TEE 243 TestFunctional._MakeInputFile('bl31.elf', 244 tools.read_file(cls.ElfTestFile('elf_sections'))) 245 TestFunctional._MakeInputFile('tee.elf', 246 tools.read_file(cls.ElfTestFile('elf_sections'))) 247 248 # Newer OP_TEE file in v1 binary format 249 cls.make_tee_bin('tee.bin') 250 251 # test files for encrypted tests 252 TestFunctional._MakeInputFile('encrypted-file.iv', ENCRYPTED_IV_DATA) 253 TestFunctional._MakeInputFile('encrypted-file.key', ENCRYPTED_KEY_DATA) 254 255 cls.comp_bintools = {} 256 for name in COMP_BINTOOLS: 257 cls.comp_bintools[name] = bintool.Bintool.create(name) 258 259 @classmethod 260 def tearDownClass(cls): 261 """Remove the temporary input directory and its contents""" 262 if cls.preserve_indir: 263 print('Preserving input dir: %s' % cls._indir) 264 else: 265 if cls._indir: 266 shutil.rmtree(cls._indir) 267 cls._indir = None 268 269 @classmethod 270 def setup_test_args(cls, preserve_indir=False, preserve_outdirs=False, 271 toolpath=None, verbosity=None): 272 """Accept arguments controlling test execution 273 274 Args: 275 preserve_indir: Preserve the shared input directory used by all 276 tests in this class. 277 preserve_outdir: Preserve the output directories used by tests. Each 278 test has its own, so this is normally only useful when running a 279 single test. 280 toolpath: ist of paths to use for tools 281 """ 282 cls.preserve_indir = preserve_indir 283 cls.preserve_outdirs = preserve_outdirs 284 cls.toolpath = toolpath 285 cls.verbosity = verbosity 286 287 def _CheckBintool(self, bintool): 288 if not bintool.is_present(): 289 self.skipTest('%s not available' % bintool.name) 290 291 def _CheckLz4(self): 292 bintool = self.comp_bintools['lz4'] 293 self._CheckBintool(bintool) 294 295 def _CleanupOutputDir(self): 296 """Remove the temporary output directory""" 297 if self.preserve_outdirs: 298 print('Preserving output dir: %s' % tools.outdir) 299 else: 300 tools._finalise_for_test() 301 302 def setUp(self): 303 # Enable this to turn on debugging output 304 # tout.init(tout.DEBUG) 305 command.test_result = None 306 307 def tearDown(self): 308 """Remove the temporary output directory""" 309 self._CleanupOutputDir() 310 311 def _SetupImageInTmpdir(self): 312 """Set up the output image in a new temporary directory 313 314 This is used when an image has been generated in the output directory, 315 but we want to run binman again. This will create a new output 316 directory and fail to delete the original one. 317 318 This creates a new temporary directory, copies the image to it (with a 319 new name) and removes the old output directory. 320 321 Returns: 322 Tuple: 323 Temporary directory to use 324 New image filename 325 """ 326 image_fname = tools.get_output_filename('image.bin') 327 tmpdir = tempfile.mkdtemp(prefix='binman.') 328 updated_fname = os.path.join(tmpdir, 'image-updated.bin') 329 tools.write_file(updated_fname, tools.read_file(image_fname)) 330 self._CleanupOutputDir() 331 return tmpdir, updated_fname 332 333 @classmethod 334 def _ResetDtbs(cls): 335 TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA) 336 TestFunctional._MakeInputFile('spl/u-boot-spl.dtb', U_BOOT_SPL_DTB_DATA) 337 TestFunctional._MakeInputFile('tpl/u-boot-tpl.dtb', U_BOOT_TPL_DTB_DATA) 338 TestFunctional._MakeInputFile('vpl/u-boot-vpl.dtb', U_BOOT_VPL_DTB_DATA) 339 340 def _RunBinman(self, *args, **kwargs): 341 """Run binman using the command line 342 343 Args: 344 Arguments to pass, as a list of strings 345 kwargs: Arguments to pass to Command.RunPipe() 346 """ 347 result = command.run_pipe([[self._binman_pathname] + list(args)], 348 capture=True, capture_stderr=True, raise_on_error=False) 349 if result.return_code and kwargs.get('raise_on_error', True): 350 raise Exception("Error running '%s': %s" % (' '.join(args), 351 result.stdout + result.stderr)) 352 return result 353 354 def _DoBinman(self, *argv): 355 """Run binman using directly (in the same process) 356 357 Args: 358 Arguments to pass, as a list of strings 359 Returns: 360 Return value (0 for success) 361 """ 362 argv = list(argv) 363 args = cmdline.ParseArgs(argv) 364 args.pager = 'binman-invalid-pager' 365 args.build_dir = self._indir 366 367 # For testing, you can force an increase in verbosity here 368 # args.verbosity = tout.DEBUG 369 return control.Binman(args) 370 371 def _DoTestFile(self, fname, debug=False, map=False, update_dtb=False, 372 entry_args=None, images=None, use_real_dtb=False, 373 use_expanded=False, verbosity=None, allow_missing=False, 374 allow_fake_blobs=False, extra_indirs=None, threads=None, 375 test_section_timeout=False, update_fdt_in_elf=None, 376 force_missing_bintools='', ignore_missing=False, output_dir=None): 377 """Run binman with a given test file 378 379 Args: 380 fname: Device-tree source filename to use (e.g. 005_simple.dts) 381 debug: True to enable debugging output 382 map: True to output map files for the images 383 update_dtb: Update the offset and size of each entry in the device 384 tree before packing it into the image 385 entry_args: Dict of entry args to supply to binman 386 key: arg name 387 value: value of that arg 388 images: List of image names to build 389 use_real_dtb: True to use the test file as the contents of 390 the u-boot-dtb entry. Normally this is not needed and the 391 test contents (the U_BOOT_DTB_DATA string) can be used. 392 But in some test we need the real contents. 393 use_expanded: True to use expanded entries where available, e.g. 394 'u-boot-expanded' instead of 'u-boot' 395 verbosity: Verbosity level to use (0-3, None=don't set it) 396 allow_missing: Set the '--allow-missing' flag so that missing 397 external binaries just produce a warning instead of an error 398 allow_fake_blobs: Set the '--fake-ext-blobs' flag 399 extra_indirs: Extra input directories to add using -I 400 threads: Number of threads to use (None for default, 0 for 401 single-threaded) 402 test_section_timeout: True to force the first time to timeout, as 403 used in testThreadTimeout() 404 update_fdt_in_elf: Value to pass with --update-fdt-in-elf=xxx 405 force_missing_tools (str): comma-separated list of bintools to 406 regard as missing 407 output_dir: Specific output directory to use for image using -O 408 409 Returns: 410 int return code, 0 on success 411 """ 412 args = [] 413 if debug: 414 args.append('-D') 415 if verbosity is not None: 416 args.append('-v%d' % verbosity) 417 elif self.verbosity: 418 args.append('-v%d' % self.verbosity) 419 if self.toolpath: 420 for path in self.toolpath: 421 args += ['--toolpath', path] 422 if threads is not None: 423 args.append('-T%d' % threads) 424 if test_section_timeout: 425 args.append('--test-section-timeout') 426 args += ['build', '-p', '-I', self._indir, '-d', self.TestFile(fname)] 427 if map: 428 args.append('-m') 429 if update_dtb: 430 args.append('-u') 431 if not use_real_dtb: 432 args.append('--fake-dtb') 433 if not use_expanded: 434 args.append('--no-expanded') 435 if entry_args: 436 for arg, value in entry_args.items(): 437 args.append('-a%s=%s' % (arg, value)) 438 if allow_missing: 439 args.append('-M') 440 if ignore_missing: 441 args.append('-W') 442 if allow_fake_blobs: 443 args.append('--fake-ext-blobs') 444 if force_missing_bintools: 445 args += ['--force-missing-bintools', force_missing_bintools] 446 if update_fdt_in_elf: 447 args += ['--update-fdt-in-elf', update_fdt_in_elf] 448 if images: 449 for image in images: 450 args += ['-i', image] 451 if extra_indirs: 452 for indir in extra_indirs: 453 args += ['-I', indir] 454 if output_dir: 455 args += ['-O', output_dir] 456 return self._DoBinman(*args) 457 458 def _SetupDtb(self, fname, outfile='u-boot.dtb'): 459 """Set up a new test device-tree file 460 461 The given file is compiled and set up as the device tree to be used 462 for ths test. 463 464 Args: 465 fname: Filename of .dts file to read 466 outfile: Output filename for compiled device-tree binary 467 468 Returns: 469 Contents of device-tree binary 470 """ 471 tmpdir = tempfile.mkdtemp(prefix='binmant.') 472 dtb = fdt_util.EnsureCompiled(self.TestFile(fname), tmpdir) 473 with open(dtb, 'rb') as fd: 474 data = fd.read() 475 TestFunctional._MakeInputFile(outfile, data) 476 shutil.rmtree(tmpdir) 477 return data 478 479 def _GetDtbContentsForSpls(self, dtb_data, name): 480 """Create a version of the main DTB for SPL / TPL / VPL 481 482 For testing we don't actually have different versions of the DTB. With 483 U-Boot we normally run fdtgrep to remove unwanted nodes, but for tests 484 we don't normally have any unwanted nodes. 485 486 We still want the DTBs for SPL and TPL to be different though, since 487 otherwise it is confusing to know which one we are looking at. So add 488 an 'spl' or 'tpl' property to the top-level node. 489 490 Args: 491 dtb_data: dtb data to modify (this should be a value devicetree) 492 name: Name of a new property to add 493 494 Returns: 495 New dtb data with the property added 496 """ 497 dtb = fdt.Fdt.FromData(dtb_data) 498 dtb.Scan() 499 dtb.GetNode('/binman').AddZeroProp(name) 500 dtb.Sync(auto_resize=True) 501 dtb.Pack() 502 return dtb.GetContents() 503 504 def _DoReadFileDtb(self, fname, use_real_dtb=False, use_expanded=False, 505 map=False, update_dtb=False, entry_args=None, 506 reset_dtbs=True, extra_indirs=None, threads=None): 507 """Run binman and return the resulting image 508 509 This runs binman with a given test file and then reads the resulting 510 output file. It is a shortcut function since most tests need to do 511 these steps. 512 513 Raises an assertion failure if binman returns a non-zero exit code. 514 515 Args: 516 fname: Device-tree source filename to use (e.g. 005_simple.dts) 517 use_real_dtb: True to use the test file as the contents of 518 the u-boot-dtb entry. Normally this is not needed and the 519 test contents (the U_BOOT_DTB_DATA string) can be used. 520 But in some test we need the real contents. 521 use_expanded: True to use expanded entries where available, e.g. 522 'u-boot-expanded' instead of 'u-boot' 523 map: True to output map files for the images 524 update_dtb: Update the offset and size of each entry in the device 525 tree before packing it into the image 526 entry_args: Dict of entry args to supply to binman 527 key: arg name 528 value: value of that arg 529 reset_dtbs: With use_real_dtb the test dtb is overwritten by this 530 function. If reset_dtbs is True, then the original test dtb 531 is written back before this function finishes 532 extra_indirs: Extra input directories to add using -I 533 threads: Number of threads to use (None for default, 0 for 534 single-threaded) 535 536 Returns: 537 Tuple: 538 Resulting image contents 539 Device tree contents 540 Map data showing contents of image (or None if none) 541 Output device tree binary filename ('u-boot.dtb' path) 542 """ 543 dtb_data = None 544 # Use the compiled test file as the u-boot-dtb input 545 if use_real_dtb: 546 dtb_data = self._SetupDtb(fname) 547 548 # For testing purposes, make a copy of the DT for SPL and TPL. Add 549 # a node indicating which it is, so aid verification. 550 for name in ['spl', 'tpl', 'vpl']: 551 dtb_fname = '%s/u-boot-%s.dtb' % (name, name) 552 outfile = os.path.join(self._indir, dtb_fname) 553 TestFunctional._MakeInputFile(dtb_fname, 554 self._GetDtbContentsForSpls(dtb_data, name)) 555 556 try: 557 retcode = self._DoTestFile(fname, map=map, update_dtb=update_dtb, 558 entry_args=entry_args, use_real_dtb=use_real_dtb, 559 use_expanded=use_expanded, extra_indirs=extra_indirs, 560 threads=threads) 561 self.assertEqual(0, retcode) 562 out_dtb_fname = tools.get_output_filename('u-boot.dtb.out') 563 564 # Find the (only) image, read it and return its contents 565 image = control.images['image'] 566 image_fname = tools.get_output_filename('image.bin') 567 self.assertTrue(os.path.exists(image_fname)) 568 if map: 569 map_fname = tools.get_output_filename('image.map') 570 with open(map_fname) as fd: 571 map_data = fd.read() 572 else: 573 map_data = None 574 with open(image_fname, 'rb') as fd: 575 return fd.read(), dtb_data, map_data, out_dtb_fname 576 finally: 577 # Put the test file back 578 if reset_dtbs and use_real_dtb: 579 self._ResetDtbs() 580 581 def _DoReadFileRealDtb(self, fname): 582 """Run binman with a real .dtb file and return the resulting data 583 584 Args: 585 fname: DT source filename to use (e.g. 082_fdt_update_all.dts) 586 587 Returns: 588 Resulting image contents 589 """ 590 return self._DoReadFileDtb(fname, use_real_dtb=True, update_dtb=True)[0] 591 592 def _DoReadFile(self, fname, use_real_dtb=False): 593 """Helper function which discards the device-tree binary 594 595 Args: 596 fname: Device-tree source filename to use (e.g. 005_simple.dts) 597 use_real_dtb: True to use the test file as the contents of 598 the u-boot-dtb entry. Normally this is not needed and the 599 test contents (the U_BOOT_DTB_DATA string) can be used. 600 But in some test we need the real contents. 601 602 Returns: 603 Resulting image contents 604 """ 605 return self._DoReadFileDtb(fname, use_real_dtb)[0] 606 607 @classmethod 608 def _MakeInputFile(cls, fname, contents): 609 """Create a new test input file, creating directories as needed 610 611 Args: 612 fname: Filename to create 613 contents: File contents to write in to the file 614 Returns: 615 Full pathname of file created 616 """ 617 pathname = os.path.join(cls._indir, fname) 618 dirname = os.path.dirname(pathname) 619 if dirname and not os.path.exists(dirname): 620 os.makedirs(dirname) 621 with open(pathname, 'wb') as fd: 622 fd.write(contents) 623 return pathname 624 625 @classmethod 626 def _MakeInputDir(cls, dirname): 627 """Create a new test input directory, creating directories as needed 628 629 Args: 630 dirname: Directory name to create 631 632 Returns: 633 Full pathname of directory created 634 """ 635 pathname = os.path.join(cls._indir, dirname) 636 if not os.path.exists(pathname): 637 os.makedirs(pathname) 638 return pathname 639 640 @classmethod 641 def _SetupSplElf(cls, src_fname='bss_data'): 642 """Set up an ELF file with a '_dt_ucode_base_size' symbol 643 644 Args: 645 Filename of ELF file to use as SPL 646 """ 647 TestFunctional._MakeInputFile('spl/u-boot-spl', 648 tools.read_file(cls.ElfTestFile(src_fname))) 649 650 @classmethod 651 def _SetupTplElf(cls, src_fname='bss_data'): 652 """Set up an ELF file with a '_dt_ucode_base_size' symbol 653 654 Args: 655 Filename of ELF file to use as TPL 656 """ 657 TestFunctional._MakeInputFile('tpl/u-boot-tpl', 658 tools.read_file(cls.ElfTestFile(src_fname))) 659 660 @classmethod 661 def _SetupVplElf(cls, src_fname='bss_data'): 662 """Set up an ELF file with a '_dt_ucode_base_size' symbol 663 664 Args: 665 Filename of ELF file to use as VPL 666 """ 667 TestFunctional._MakeInputFile('vpl/u-boot-vpl', 668 tools.read_file(cls.ElfTestFile(src_fname))) 669 670 @classmethod 671 def _SetupPmuFwlElf(cls, src_fname='bss_data'): 672 """Set up an ELF file with a '_dt_ucode_base_size' symbol 673 674 Args: 675 Filename of ELF file to use as VPL 676 """ 677 TestFunctional._MakeInputFile('pmu-firmware.elf', 678 tools.read_file(cls.ElfTestFile(src_fname))) 679 680 @classmethod 681 def _SetupDescriptor(cls): 682 with open(cls.TestFile('descriptor.bin'), 'rb') as fd: 683 TestFunctional._MakeInputFile('descriptor.bin', fd.read()) 684 685 @classmethod 686 def TestFile(cls, fname): 687 return os.path.join(cls._binman_dir, 'test', fname) 688 689 @classmethod 690 def ElfTestFile(cls, fname): 691 return os.path.join(cls._elf_testdir, fname) 692 693 @classmethod 694 def make_tee_bin(cls, fname, paged_sz=0, extra_data=b''): 695 init_sz, start_hi, start_lo, dummy = (len(U_BOOT_DATA), 0, TEE_ADDR, 0) 696 data = b'OPTE\x01xxx' + struct.pack('<5I', init_sz, start_hi, start_lo, 697 dummy, paged_sz) + U_BOOT_DATA 698 data += extra_data 699 TestFunctional._MakeInputFile(fname, data) 700 701 def AssertInList(self, grep_list, target): 702 """Assert that at least one of a list of things is in a target 703 704 Args: 705 grep_list: List of strings to check 706 target: Target string 707 """ 708 for grep in grep_list: 709 if grep in target: 710 return 711 self.fail("Error: '%s' not found in '%s'" % (grep_list, target)) 712 713 def CheckNoGaps(self, entries): 714 """Check that all entries fit together without gaps 715 716 Args: 717 entries: List of entries to check 718 """ 719 offset = 0 720 for entry in entries.values(): 721 self.assertEqual(offset, entry.offset) 722 offset += entry.size 723 724 def GetFdtLen(self, dtb): 725 """Get the totalsize field from a device-tree binary 726 727 Args: 728 dtb: Device-tree binary contents 729 730 Returns: 731 Total size of device-tree binary, from the header 732 """ 733 return struct.unpack('>L', dtb[4:8])[0] 734 735 def _GetPropTree(self, dtb, prop_names, prefix='/binman/'): 736 def AddNode(node, path): 737 if node.name != '/': 738 path += '/' + node.name 739 for prop in node.props.values(): 740 if prop.name in prop_names: 741 prop_path = path + ':' + prop.name 742 tree[prop_path[len(prefix):]] = fdt_util.fdt32_to_cpu( 743 prop.value) 744 for subnode in node.subnodes: 745 AddNode(subnode, path) 746 747 tree = {} 748 AddNode(dtb.GetRoot(), '') 749 return tree 750 751 def _CheckSign(self, fit, key): 752 try: 753 tools.run('fit_check_sign', '-k', key, '-f', fit) 754 except: 755 self.fail('Expected signed FIT container') 756 return False 757 return True 758 759 def testRun(self): 760 """Test a basic run with valid args""" 761 result = self._RunBinman('-h') 762 763 def testFullHelp(self): 764 """Test that the full help is displayed with -H""" 765 result = self._RunBinman('-H') 766 help_file = os.path.join(self._binman_dir, 'README.rst') 767 # Remove possible extraneous strings 768 extra = '::::::::::::::\n' + help_file + '\n::::::::::::::\n' 769 gothelp = result.stdout.replace(extra, '') 770 self.assertEqual(len(gothelp), os.path.getsize(help_file)) 771 self.assertEqual(0, len(result.stderr)) 772 self.assertEqual(0, result.return_code) 773 774 def testFullHelpInternal(self): 775 """Test that the full help is displayed with -H""" 776 try: 777 command.test_result = command.CommandResult() 778 result = self._DoBinman('-H') 779 help_file = os.path.join(self._binman_dir, 'README.rst') 780 finally: 781 command.test_result = None 782 783 def testHelp(self): 784 """Test that the basic help is displayed with -h""" 785 result = self._RunBinman('-h') 786 self.assertTrue(len(result.stdout) > 200) 787 self.assertEqual(0, len(result.stderr)) 788 self.assertEqual(0, result.return_code) 789 790 def testBoard(self): 791 """Test that we can run it with a specific board""" 792 self._SetupDtb('005_simple.dts', 'sandbox/u-boot.dtb') 793 TestFunctional._MakeInputFile('sandbox/u-boot.bin', U_BOOT_DATA) 794 result = self._DoBinman('build', '-n', '-b', 'sandbox') 795 self.assertEqual(0, result) 796 797 def testNeedBoard(self): 798 """Test that we get an error when no board ius supplied""" 799 with self.assertRaises(ValueError) as e: 800 result = self._DoBinman('build') 801 self.assertIn("Must provide a board to process (use -b <board>)", 802 str(e.exception)) 803 804 def testMissingDt(self): 805 """Test that an invalid device-tree file generates an error""" 806 with self.assertRaises(Exception) as e: 807 self._RunBinman('build', '-d', 'missing_file') 808 # We get one error from libfdt, and a different one from fdtget. 809 self.AssertInList(["Couldn't open blob from 'missing_file'", 810 'No such file or directory'], str(e.exception)) 811 812 def testBrokenDt(self): 813 """Test that an invalid device-tree source file generates an error 814 815 Since this is a source file it should be compiled and the error 816 will come from the device-tree compiler (dtc). 817 """ 818 with self.assertRaises(Exception) as e: 819 self._RunBinman('build', '-d', self.TestFile('001_invalid.dts')) 820 self.assertIn("FATAL ERROR: Unable to parse input tree", 821 str(e.exception)) 822 823 def testMissingNode(self): 824 """Test that a device tree without a 'binman' node generates an error""" 825 with self.assertRaises(Exception) as e: 826 self._DoBinman('build', '-d', self.TestFile('002_missing_node.dts')) 827 self.assertIn("does not have a 'binman' node", str(e.exception)) 828 829 def testEmpty(self): 830 """Test that an empty binman node works OK (i.e. does nothing)""" 831 result = self._RunBinman('build', '-d', self.TestFile('003_empty.dts')) 832 self.assertEqual(0, len(result.stderr)) 833 self.assertEqual(0, result.return_code) 834 835 def testInvalidEntry(self): 836 """Test that an invalid entry is flagged""" 837 with self.assertRaises(Exception) as e: 838 result = self._RunBinman('build', '-d', 839 self.TestFile('004_invalid_entry.dts')) 840 self.assertIn("Unknown entry type 'not-a-valid-type' in node " 841 "'/binman/not-a-valid-type'", str(e.exception)) 842 843 def testSimple(self): 844 """Test a simple binman with a single file""" 845 data = self._DoReadFile('005_simple.dts') 846 self.assertEqual(U_BOOT_DATA, data) 847 848 def testSimpleDebug(self): 849 """Test a simple binman run with debugging enabled""" 850 self._DoTestFile('005_simple.dts', debug=True) 851 852 def testDual(self): 853 """Test that we can handle creating two images 854 855 This also tests image padding. 856 """ 857 retcode = self._DoTestFile('006_dual_image.dts') 858 self.assertEqual(0, retcode) 859 860 image = control.images['image1'] 861 self.assertEqual(len(U_BOOT_DATA), image.size) 862 fname = tools.get_output_filename('image1.bin') 863 self.assertTrue(os.path.exists(fname)) 864 with open(fname, 'rb') as fd: 865 data = fd.read() 866 self.assertEqual(U_BOOT_DATA, data) 867 868 image = control.images['image2'] 869 self.assertEqual(3 + len(U_BOOT_DATA) + 5, image.size) 870 fname = tools.get_output_filename('image2.bin') 871 self.assertTrue(os.path.exists(fname)) 872 with open(fname, 'rb') as fd: 873 data = fd.read() 874 self.assertEqual(U_BOOT_DATA, data[3:7]) 875 self.assertEqual(tools.get_bytes(0, 3), data[:3]) 876 self.assertEqual(tools.get_bytes(0, 5), data[7:]) 877 878 def testBadAlign(self): 879 """Test that an invalid alignment value is detected""" 880 with self.assertRaises(ValueError) as e: 881 self._DoTestFile('007_bad_align.dts') 882 self.assertIn("Node '/binman/u-boot': Alignment 23 must be a power " 883 "of two", str(e.exception)) 884 885 def testPackSimple(self): 886 """Test that packing works as expected""" 887 retcode = self._DoTestFile('008_pack.dts') 888 self.assertEqual(0, retcode) 889 self.assertIn('image', control.images) 890 image = control.images['image'] 891 entries = image.GetEntries() 892 self.assertEqual(5, len(entries)) 893 894 # First u-boot 895 self.assertIn('u-boot', entries) 896 entry = entries['u-boot'] 897 self.assertEqual(0, entry.offset) 898 self.assertEqual(len(U_BOOT_DATA), entry.size) 899 900 # Second u-boot, aligned to 16-byte boundary 901 self.assertIn('u-boot-align', entries) 902 entry = entries['u-boot-align'] 903 self.assertEqual(16, entry.offset) 904 self.assertEqual(len(U_BOOT_DATA), entry.size) 905 906 # Third u-boot, size 23 bytes 907 self.assertIn('u-boot-size', entries) 908 entry = entries['u-boot-size'] 909 self.assertEqual(20, entry.offset) 910 self.assertEqual(len(U_BOOT_DATA), entry.contents_size) 911 self.assertEqual(23, entry.size) 912 913 # Fourth u-boot, placed immediate after the above 914 self.assertIn('u-boot-next', entries) 915 entry = entries['u-boot-next'] 916 self.assertEqual(43, entry.offset) 917 self.assertEqual(len(U_BOOT_DATA), entry.size) 918 919 # Fifth u-boot, placed at a fixed offset 920 self.assertIn('u-boot-fixed', entries) 921 entry = entries['u-boot-fixed'] 922 self.assertEqual(61, entry.offset) 923 self.assertEqual(len(U_BOOT_DATA), entry.size) 924 925 self.assertEqual(65, image.size) 926 927 def testPackExtra(self): 928 """Test that extra packing feature works as expected""" 929 data, _, _, out_dtb_fname = self._DoReadFileDtb('009_pack_extra.dts', 930 update_dtb=True) 931 932 self.assertIn('image', control.images) 933 image = control.images['image'] 934 entries = image.GetEntries() 935 self.assertEqual(6, len(entries)) 936 937 # First u-boot with padding before and after (included in minimum size) 938 self.assertIn('u-boot', entries) 939 entry = entries['u-boot'] 940 self.assertEqual(0, entry.offset) 941 self.assertEqual(3, entry.pad_before) 942 self.assertEqual(3 + 5 + len(U_BOOT_DATA), entry.size) 943 self.assertEqual(U_BOOT_DATA, entry.data) 944 self.assertEqual(tools.get_bytes(0, 3) + U_BOOT_DATA + 945 tools.get_bytes(0, 5), data[:entry.size]) 946 pos = entry.size 947 948 # Second u-boot has an aligned size, but it has no effect 949 self.assertIn('u-boot-align-size-nop', entries) 950 entry = entries['u-boot-align-size-nop'] 951 self.assertEqual(pos, entry.offset) 952 self.assertEqual(len(U_BOOT_DATA), entry.size) 953 self.assertEqual(U_BOOT_DATA, entry.data) 954 self.assertEqual(U_BOOT_DATA, data[pos:pos + entry.size]) 955 pos += entry.size 956 957 # Third u-boot has an aligned size too 958 self.assertIn('u-boot-align-size', entries) 959 entry = entries['u-boot-align-size'] 960 self.assertEqual(pos, entry.offset) 961 self.assertEqual(32, entry.size) 962 self.assertEqual(U_BOOT_DATA, entry.data) 963 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 32 - len(U_BOOT_DATA)), 964 data[pos:pos + entry.size]) 965 pos += entry.size 966 967 # Fourth u-boot has an aligned end 968 self.assertIn('u-boot-align-end', entries) 969 entry = entries['u-boot-align-end'] 970 self.assertEqual(48, entry.offset) 971 self.assertEqual(16, entry.size) 972 self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)]) 973 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 16 - len(U_BOOT_DATA)), 974 data[pos:pos + entry.size]) 975 pos += entry.size 976 977 # Fifth u-boot immediately afterwards 978 self.assertIn('u-boot-align-both', entries) 979 entry = entries['u-boot-align-both'] 980 self.assertEqual(64, entry.offset) 981 self.assertEqual(64, entry.size) 982 self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)]) 983 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 64 - len(U_BOOT_DATA)), 984 data[pos:pos + entry.size]) 985 986 # Sixth u-boot with both minimum size and aligned size 987 self.assertIn('u-boot-min-size', entries) 988 entry = entries['u-boot-min-size'] 989 self.assertEqual(128, entry.offset) 990 self.assertEqual(32, entry.size) 991 self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)]) 992 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 32 - len(U_BOOT_DATA)), 993 data[pos:pos + entry.size]) 994 995 self.CheckNoGaps(entries) 996 self.assertEqual(160, image.size) 997 998 dtb = fdt.Fdt(out_dtb_fname) 999 dtb.Scan() 1000 props = self._GetPropTree(dtb, ['size', 'offset', 'image-pos']) 1001 expected = { 1002 'image-pos': 0, 1003 'offset': 0, 1004 'size': 160, 1005 1006 'u-boot:image-pos': 0, 1007 'u-boot:offset': 0, 1008 'u-boot:size': 3 + 5 + len(U_BOOT_DATA), 1009 1010 'u-boot-align-size-nop:image-pos': 12, 1011 'u-boot-align-size-nop:offset': 12, 1012 'u-boot-align-size-nop:size': 4, 1013 1014 'u-boot-align-size:image-pos': 16, 1015 'u-boot-align-size:offset': 16, 1016 'u-boot-align-size:size': 32, 1017 1018 'u-boot-align-end:image-pos': 48, 1019 'u-boot-align-end:offset': 48, 1020 'u-boot-align-end:size': 16, 1021 1022 'u-boot-align-both:image-pos': 64, 1023 'u-boot-align-both:offset': 64, 1024 'u-boot-align-both:size': 64, 1025 1026 'u-boot-min-size:image-pos': 128, 1027 'u-boot-min-size:offset': 128, 1028 'u-boot-min-size:size': 32, 1029 } 1030 self.assertEqual(expected, props) 1031 1032 def testPackAlignPowerOf2(self): 1033 """Test that invalid entry alignment is detected""" 1034 with self.assertRaises(ValueError) as e: 1035 self._DoTestFile('010_pack_align_power2.dts') 1036 self.assertIn("Node '/binman/u-boot': Alignment 5 must be a power " 1037 "of two", str(e.exception)) 1038 1039 def testPackAlignSizePowerOf2(self): 1040 """Test that invalid entry size alignment is detected""" 1041 with self.assertRaises(ValueError) as e: 1042 self._DoTestFile('011_pack_align_size_power2.dts') 1043 self.assertIn("Node '/binman/u-boot': Alignment size 55 must be a " 1044 "power of two", str(e.exception)) 1045 1046 def testPackInvalidAlign(self): 1047 """Test detection of an offset that does not match its alignment""" 1048 with self.assertRaises(ValueError) as e: 1049 self._DoTestFile('012_pack_inv_align.dts') 1050 self.assertIn("Node '/binman/u-boot': Offset 0x5 (5) does not match " 1051 "align 0x4 (4)", str(e.exception)) 1052 1053 def testPackInvalidSizeAlign(self): 1054 """Test that invalid entry size alignment is detected""" 1055 with self.assertRaises(ValueError) as e: 1056 self._DoTestFile('013_pack_inv_size_align.dts') 1057 self.assertIn("Node '/binman/u-boot': Size 0x5 (5) does not match " 1058 "align-size 0x4 (4)", str(e.exception)) 1059 1060 def testPackOverlap(self): 1061 """Test that overlapping regions are detected""" 1062 with self.assertRaises(ValueError) as e: 1063 self._DoTestFile('014_pack_overlap.dts') 1064 self.assertIn("Node '/binman/u-boot-align': Offset 0x3 (3) overlaps " 1065 "with previous entry '/binman/u-boot' ending at 0x4 (4)", 1066 str(e.exception)) 1067 1068 def testPackEntryOverflow(self): 1069 """Test that entries that overflow their size are detected""" 1070 with self.assertRaises(ValueError) as e: 1071 self._DoTestFile('015_pack_overflow.dts') 1072 self.assertIn("Node '/binman/u-boot': Entry contents size is 0x4 (4) " 1073 "but entry size is 0x3 (3)", str(e.exception)) 1074 1075 def testPackImageOverflow(self): 1076 """Test that entries which overflow the image size are detected""" 1077 with self.assertRaises(ValueError) as e: 1078 self._DoTestFile('016_pack_image_overflow.dts') 1079 self.assertIn("Section '/binman': contents size 0x4 (4) exceeds section " 1080 "size 0x3 (3)", str(e.exception)) 1081 1082 def testPackImageSize(self): 1083 """Test that the image size can be set""" 1084 retcode = self._DoTestFile('017_pack_image_size.dts') 1085 self.assertEqual(0, retcode) 1086 self.assertIn('image', control.images) 1087 image = control.images['image'] 1088 self.assertEqual(7, image.size) 1089 1090 def testPackImageSizeAlign(self): 1091 """Test that image size alignemnt works as expected""" 1092 retcode = self._DoTestFile('018_pack_image_align.dts') 1093 self.assertEqual(0, retcode) 1094 self.assertIn('image', control.images) 1095 image = control.images['image'] 1096 self.assertEqual(16, image.size) 1097 1098 def testPackInvalidImageAlign(self): 1099 """Test that invalid image alignment is detected""" 1100 with self.assertRaises(ValueError) as e: 1101 self._DoTestFile('019_pack_inv_image_align.dts') 1102 self.assertIn("Section '/binman': Size 0x7 (7) does not match " 1103 "align-size 0x8 (8)", str(e.exception)) 1104 1105 def testPackAlignPowerOf2Inv(self): 1106 """Test that invalid image alignment is detected""" 1107 with self.assertRaises(ValueError) as e: 1108 self._DoTestFile('020_pack_inv_image_align_power2.dts') 1109 self.assertIn("Image '/binman': Alignment size 131 must be a power of " 1110 "two", str(e.exception)) 1111 1112 def testImagePadByte(self): 1113 """Test that the image pad byte can be specified""" 1114 self._SetupSplElf() 1115 data = self._DoReadFile('021_image_pad.dts') 1116 self.assertEqual(U_BOOT_SPL_DATA + tools.get_bytes(0xff, 1) + 1117 U_BOOT_DATA, data) 1118 1119 def testImageName(self): 1120 """Test that image files can be named""" 1121 retcode = self._DoTestFile('022_image_name.dts') 1122 self.assertEqual(0, retcode) 1123 image = control.images['image1'] 1124 fname = tools.get_output_filename('test-name') 1125 self.assertTrue(os.path.exists(fname)) 1126 1127 image = control.images['image2'] 1128 fname = tools.get_output_filename('test-name.xx') 1129 self.assertTrue(os.path.exists(fname)) 1130 1131 def testBlobFilename(self): 1132 """Test that generic blobs can be provided by filename""" 1133 data = self._DoReadFile('023_blob.dts') 1134 self.assertEqual(BLOB_DATA, data) 1135 1136 def testPackSorted(self): 1137 """Test that entries can be sorted""" 1138 self._SetupSplElf() 1139 data = self._DoReadFile('024_sorted.dts') 1140 self.assertEqual(tools.get_bytes(0, 1) + U_BOOT_SPL_DATA + 1141 tools.get_bytes(0, 2) + U_BOOT_DATA, data) 1142 1143 def testPackZeroOffset(self): 1144 """Test that an entry at offset 0 is not given a new offset""" 1145 self._SetupSplElf() 1146 with self.assertRaises(ValueError) as e: 1147 self._DoTestFile('025_pack_zero_size.dts') 1148 self.assertIn("Node '/binman/u-boot-spl': Offset 0x0 (0) overlaps " 1149 "with previous entry '/binman/u-boot' ending at 0x4 (4)", 1150 str(e.exception)) 1151 1152 def testPackUbootDtb(self): 1153 """Test that a device tree can be added to U-Boot""" 1154 data = self._DoReadFile('026_pack_u_boot_dtb.dts') 1155 self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA, data) 1156 1157 def testPackX86RomNoSize(self): 1158 """Test that the end-at-4gb property requires a size property""" 1159 self._SetupSplElf() 1160 with self.assertRaises(ValueError) as e: 1161 self._DoTestFile('027_pack_4gb_no_size.dts') 1162 self.assertIn("Image '/binman': Section size must be provided when " 1163 "using end-at-4gb", str(e.exception)) 1164 1165 def test4gbAndSkipAtStartTogether(self): 1166 """Test that the end-at-4gb and skip-at-size property can't be used 1167 together""" 1168 self._SetupSplElf() 1169 with self.assertRaises(ValueError) as e: 1170 self._DoTestFile('098_4gb_and_skip_at_start_together.dts') 1171 self.assertIn("Image '/binman': Provide either 'end-at-4gb' or " 1172 "'skip-at-start'", str(e.exception)) 1173 1174 def testPackX86RomOutside(self): 1175 """Test that the end-at-4gb property checks for offset boundaries""" 1176 self._SetupSplElf() 1177 with self.assertRaises(ValueError) as e: 1178 self._DoTestFile('028_pack_4gb_outside.dts') 1179 self.assertIn("Node '/binman/u-boot': Offset 0x0 (0) size 0x4 (4) " 1180 "is outside the section '/binman' starting at " 1181 '0xffffffe0 (4294967264) of size 0x20 (32)', 1182 str(e.exception)) 1183 1184 def testPackX86Rom(self): 1185 """Test that a basic x86 ROM can be created""" 1186 self._SetupSplElf() 1187 data = self._DoReadFile('029_x86_rom.dts') 1188 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 3) + U_BOOT_SPL_DATA + 1189 tools.get_bytes(0, 2), data) 1190 1191 def testPackX86RomMeNoDesc(self): 1192 """Test that an invalid Intel descriptor entry is detected""" 1193 try: 1194 TestFunctional._MakeInputFile('descriptor-empty.bin', b'') 1195 with self.assertRaises(ValueError) as e: 1196 self._DoTestFile('163_x86_rom_me_empty.dts') 1197 self.assertIn("Node '/binman/intel-descriptor': Cannot find Intel Flash Descriptor (FD) signature", 1198 str(e.exception)) 1199 finally: 1200 self._SetupDescriptor() 1201 1202 def testPackX86RomBadDesc(self): 1203 """Test that the Intel requires a descriptor entry""" 1204 with self.assertRaises(ValueError) as e: 1205 self._DoTestFile('030_x86_rom_me_no_desc.dts') 1206 self.assertIn("Node '/binman/intel-me': No offset set with " 1207 "offset-unset: should another entry provide this correct " 1208 "offset?", str(e.exception)) 1209 1210 def testPackX86RomMe(self): 1211 """Test that an x86 ROM with an ME region can be created""" 1212 data = self._DoReadFile('031_x86_rom_me.dts') 1213 expected_desc = tools.read_file(self.TestFile('descriptor.bin')) 1214 if data[:0x1000] != expected_desc: 1215 self.fail('Expected descriptor binary at start of image') 1216 self.assertEqual(ME_DATA, data[0x1000:0x1000 + len(ME_DATA)]) 1217 1218 def testPackVga(self): 1219 """Test that an image with a VGA binary can be created""" 1220 data = self._DoReadFile('032_intel_vga.dts') 1221 self.assertEqual(VGA_DATA, data[:len(VGA_DATA)]) 1222 1223 def testPackStart16(self): 1224 """Test that an image with an x86 start16 region can be created""" 1225 data = self._DoReadFile('033_x86_start16.dts') 1226 self.assertEqual(X86_START16_DATA, data[:len(X86_START16_DATA)]) 1227 1228 def testPackPowerpcMpc85xxBootpgResetvec(self): 1229 """Test that an image with powerpc-mpc85xx-bootpg-resetvec can be 1230 created""" 1231 data = self._DoReadFile('150_powerpc_mpc85xx_bootpg_resetvec.dts') 1232 self.assertEqual(PPC_MPC85XX_BR_DATA, data[:len(PPC_MPC85XX_BR_DATA)]) 1233 1234 def _RunMicrocodeTest(self, dts_fname, nodtb_data, ucode_second=False): 1235 """Handle running a test for insertion of microcode 1236 1237 Args: 1238 dts_fname: Name of test .dts file 1239 nodtb_data: Data that we expect in the first section 1240 ucode_second: True if the microsecond entry is second instead of 1241 third 1242 1243 Returns: 1244 Tuple: 1245 Contents of first region (U-Boot or SPL) 1246 Offset and size components of microcode pointer, as inserted 1247 in the above (two 4-byte words) 1248 """ 1249 data = self._DoReadFile(dts_fname, True) 1250 1251 # Now check the device tree has no microcode 1252 if ucode_second: 1253 ucode_content = data[len(nodtb_data):] 1254 ucode_pos = len(nodtb_data) 1255 dtb_with_ucode = ucode_content[16:] 1256 fdt_len = self.GetFdtLen(dtb_with_ucode) 1257 else: 1258 dtb_with_ucode = data[len(nodtb_data):] 1259 fdt_len = self.GetFdtLen(dtb_with_ucode) 1260 ucode_content = dtb_with_ucode[fdt_len:] 1261 ucode_pos = len(nodtb_data) + fdt_len 1262 fname = tools.get_output_filename('test.dtb') 1263 with open(fname, 'wb') as fd: 1264 fd.write(dtb_with_ucode) 1265 dtb = fdt.FdtScan(fname) 1266 ucode = dtb.GetNode('/microcode') 1267 self.assertTrue(ucode) 1268 for node in ucode.subnodes: 1269 self.assertFalse(node.props.get('data')) 1270 1271 # Check that the microcode appears immediately after the Fdt 1272 # This matches the concatenation of the data properties in 1273 # the /microcode/update@xxx nodes in 34_x86_ucode.dts. 1274 ucode_data = struct.pack('>4L', 0x12345678, 0x12345679, 0xabcd0000, 1275 0x78235609) 1276 self.assertEqual(ucode_data, ucode_content[:len(ucode_data)]) 1277 1278 # Check that the microcode pointer was inserted. It should match the 1279 # expected offset and size 1280 pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos, 1281 len(ucode_data)) 1282 u_boot = data[:len(nodtb_data)] 1283 return u_boot, pos_and_size 1284 1285 def testPackUbootMicrocode(self): 1286 """Test that x86 microcode can be handled correctly 1287 1288 We expect to see the following in the image, in order: 1289 u-boot-nodtb.bin with a microcode pointer inserted at the correct 1290 place 1291 u-boot.dtb with the microcode removed 1292 the microcode 1293 """ 1294 first, pos_and_size = self._RunMicrocodeTest('034_x86_ucode.dts', 1295 U_BOOT_NODTB_DATA) 1296 self.assertEqual(b'nodtb with microcode' + pos_and_size + 1297 b' somewhere in here', first) 1298 1299 def _RunPackUbootSingleMicrocode(self): 1300 """Test that x86 microcode can be handled correctly 1301 1302 We expect to see the following in the image, in order: 1303 u-boot-nodtb.bin with a microcode pointer inserted at the correct 1304 place 1305 u-boot.dtb with the microcode 1306 an empty microcode region 1307 """ 1308 # We need the libfdt library to run this test since only that allows 1309 # finding the offset of a property. This is required by 1310 # Entry_u_boot_dtb_with_ucode.ObtainContents(). 1311 data = self._DoReadFile('035_x86_single_ucode.dts', True) 1312 1313 second = data[len(U_BOOT_NODTB_DATA):] 1314 1315 fdt_len = self.GetFdtLen(second) 1316 third = second[fdt_len:] 1317 second = second[:fdt_len] 1318 1319 ucode_data = struct.pack('>2L', 0x12345678, 0x12345679) 1320 self.assertIn(ucode_data, second) 1321 ucode_pos = second.find(ucode_data) + len(U_BOOT_NODTB_DATA) 1322 1323 # Check that the microcode pointer was inserted. It should match the 1324 # expected offset and size 1325 pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos, 1326 len(ucode_data)) 1327 first = data[:len(U_BOOT_NODTB_DATA)] 1328 self.assertEqual(b'nodtb with microcode' + pos_and_size + 1329 b' somewhere in here', first) 1330 1331 def testPackUbootSingleMicrocode(self): 1332 """Test that x86 microcode can be handled correctly with fdt_normal. 1333 """ 1334 self._RunPackUbootSingleMicrocode() 1335 1336 def testUBootImg(self): 1337 """Test that u-boot.img can be put in a file""" 1338 data = self._DoReadFile('036_u_boot_img.dts') 1339 self.assertEqual(U_BOOT_IMG_DATA, data) 1340 1341 def testNoMicrocode(self): 1342 """Test that a missing microcode region is detected""" 1343 with self.assertRaises(ValueError) as e: 1344 self._DoReadFile('037_x86_no_ucode.dts', True) 1345 self.assertIn("Node '/binman/u-boot-dtb-with-ucode': No /microcode " 1346 "node found in ", str(e.exception)) 1347 1348 def testMicrocodeWithoutNode(self): 1349 """Test that a missing u-boot-dtb-with-ucode node is detected""" 1350 with self.assertRaises(ValueError) as e: 1351 self._DoReadFile('038_x86_ucode_missing_node.dts', True) 1352 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find " 1353 "microcode region u-boot-dtb-with-ucode", str(e.exception)) 1354 1355 def testMicrocodeWithoutNode2(self): 1356 """Test that a missing u-boot-ucode node is detected""" 1357 with self.assertRaises(ValueError) as e: 1358 self._DoReadFile('039_x86_ucode_missing_node2.dts', True) 1359 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find " 1360 "microcode region u-boot-ucode", str(e.exception)) 1361 1362 def testMicrocodeWithoutPtrInElf(self): 1363 """Test that a U-Boot binary without the microcode symbol is detected""" 1364 # ELF file without a '_dt_ucode_base_size' symbol 1365 try: 1366 TestFunctional._MakeInputFile('u-boot', 1367 tools.read_file(self.ElfTestFile('u_boot_no_ucode_ptr'))) 1368 1369 with self.assertRaises(ValueError) as e: 1370 self._RunPackUbootSingleMicrocode() 1371 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot locate " 1372 "_dt_ucode_base_size symbol in u-boot", str(e.exception)) 1373 1374 finally: 1375 # Put the original file back 1376 TestFunctional._MakeInputFile('u-boot', 1377 tools.read_file(self.ElfTestFile('u_boot_ucode_ptr'))) 1378 1379 def testMicrocodeNotInImage(self): 1380 """Test that microcode must be placed within the image""" 1381 with self.assertRaises(ValueError) as e: 1382 self._DoReadFile('040_x86_ucode_not_in_image.dts', True) 1383 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Microcode " 1384 "pointer _dt_ucode_base_size at fffffe14 is outside the " 1385 "section ranging from 00000000 to 0000002e", str(e.exception)) 1386 1387 def testWithoutMicrocode(self): 1388 """Test that we can cope with an image without microcode (e.g. qemu)""" 1389 TestFunctional._MakeInputFile('u-boot', 1390 tools.read_file(self.ElfTestFile('u_boot_no_ucode_ptr'))) 1391 data, dtb, _, _ = self._DoReadFileDtb('044_x86_optional_ucode.dts', True) 1392 1393 # Now check the device tree has no microcode 1394 self.assertEqual(U_BOOT_NODTB_DATA, data[:len(U_BOOT_NODTB_DATA)]) 1395 second = data[len(U_BOOT_NODTB_DATA):] 1396 1397 fdt_len = self.GetFdtLen(second) 1398 self.assertEqual(dtb, second[:fdt_len]) 1399 1400 used_len = len(U_BOOT_NODTB_DATA) + fdt_len 1401 third = data[used_len:] 1402 self.assertEqual(tools.get_bytes(0, 0x200 - used_len), third) 1403 1404 def testUnknownPosSize(self): 1405 """Test that microcode must be placed within the image""" 1406 with self.assertRaises(ValueError) as e: 1407 self._DoReadFile('041_unknown_pos_size.dts', True) 1408 self.assertIn("Section '/binman': Unable to set offset/size for unknown " 1409 "entry 'invalid-entry'", str(e.exception)) 1410 1411 def testPackFsp(self): 1412 """Test that an image with a FSP binary can be created""" 1413 data = self._DoReadFile('042_intel_fsp.dts') 1414 self.assertEqual(FSP_DATA, data[:len(FSP_DATA)]) 1415 1416 def testPackCmc(self): 1417 """Test that an image with a CMC binary can be created""" 1418 data = self._DoReadFile('043_intel_cmc.dts') 1419 self.assertEqual(CMC_DATA, data[:len(CMC_DATA)]) 1420 1421 def testPackVbt(self): 1422 """Test that an image with a VBT binary can be created""" 1423 data = self._DoReadFile('046_intel_vbt.dts') 1424 self.assertEqual(VBT_DATA, data[:len(VBT_DATA)]) 1425 1426 def testSplBssPad(self): 1427 """Test that we can pad SPL's BSS with zeros""" 1428 # ELF file with a '__bss_size' symbol 1429 self._SetupSplElf() 1430 data = self._DoReadFile('047_spl_bss_pad.dts') 1431 self.assertEqual(U_BOOT_SPL_DATA + tools.get_bytes(0, 10) + U_BOOT_DATA, 1432 data) 1433 1434 def testSplBssPadMissing(self): 1435 """Test that a missing symbol is detected""" 1436 self._SetupSplElf('u_boot_ucode_ptr') 1437 with self.assertRaises(ValueError) as e: 1438 self._DoReadFile('047_spl_bss_pad.dts') 1439 self.assertIn('Expected __bss_size symbol in spl/u-boot-spl', 1440 str(e.exception)) 1441 1442 def testPackStart16Spl(self): 1443 """Test that an image with an x86 start16 SPL region can be created""" 1444 data = self._DoReadFile('048_x86_start16_spl.dts') 1445 self.assertEqual(X86_START16_SPL_DATA, data[:len(X86_START16_SPL_DATA)]) 1446 1447 def _PackUbootSplMicrocode(self, dts, ucode_second=False): 1448 """Helper function for microcode tests 1449 1450 We expect to see the following in the image, in order: 1451 u-boot-spl-nodtb.bin with a microcode pointer inserted at the 1452 correct place 1453 u-boot.dtb with the microcode removed 1454 the microcode 1455 1456 Args: 1457 dts: Device tree file to use for test 1458 ucode_second: True if the microsecond entry is second instead of 1459 third 1460 """ 1461 self._SetupSplElf('u_boot_ucode_ptr') 1462 first, pos_and_size = self._RunMicrocodeTest(dts, U_BOOT_SPL_NODTB_DATA, 1463 ucode_second=ucode_second) 1464 self.assertEqual(b'splnodtb with microc' + pos_and_size + 1465 b'ter somewhere in here', first) 1466 1467 def testPackUbootSplMicrocode(self): 1468 """Test that x86 microcode can be handled correctly in SPL""" 1469 self._SetupSplElf() 1470 self._PackUbootSplMicrocode('049_x86_ucode_spl.dts') 1471 1472 def testPackUbootSplMicrocodeReorder(self): 1473 """Test that order doesn't matter for microcode entries 1474 1475 This is the same as testPackUbootSplMicrocode but when we process the 1476 u-boot-ucode entry we have not yet seen the u-boot-dtb-with-ucode 1477 entry, so we reply on binman to try later. 1478 """ 1479 self._PackUbootSplMicrocode('058_x86_ucode_spl_needs_retry.dts', 1480 ucode_second=True) 1481 1482 def testPackMrc(self): 1483 """Test that an image with an MRC binary can be created""" 1484 data = self._DoReadFile('050_intel_mrc.dts') 1485 self.assertEqual(MRC_DATA, data[:len(MRC_DATA)]) 1486 1487 def testSplDtb(self): 1488 """Test that an image with spl/u-boot-spl.dtb can be created""" 1489 self._SetupSplElf() 1490 data = self._DoReadFile('051_u_boot_spl_dtb.dts') 1491 self.assertEqual(U_BOOT_SPL_DTB_DATA, data[:len(U_BOOT_SPL_DTB_DATA)]) 1492 1493 def testSplNoDtb(self): 1494 """Test that an image with spl/u-boot-spl-nodtb.bin can be created""" 1495 self._SetupSplElf() 1496 data = self._DoReadFile('052_u_boot_spl_nodtb.dts') 1497 self.assertEqual(U_BOOT_SPL_NODTB_DATA, data[:len(U_BOOT_SPL_NODTB_DATA)]) 1498 1499 def checkSymbols(self, dts, base_data, u_boot_offset, entry_args=None, 1500 use_expanded=False, no_write_symbols=False): 1501 """Check the image contains the expected symbol values 1502 1503 Args: 1504 dts: Device tree file to use for test 1505 base_data: Data before and after 'u-boot' section 1506 u_boot_offset: Offset of 'u-boot' section in image 1507 entry_args: Dict of entry args to supply to binman 1508 key: arg name 1509 value: value of that arg 1510 use_expanded: True to use expanded entries where available, e.g. 1511 'u-boot-expanded' instead of 'u-boot' 1512 """ 1513 elf_fname = self.ElfTestFile('u_boot_binman_syms') 1514 syms = elf.GetSymbols(elf_fname, ['binman', 'image']) 1515 addr = elf.GetSymbolAddress(elf_fname, '__image_copy_start') 1516 self.assertEqual(syms['_binman_sym_magic'].address, addr) 1517 self.assertEqual(syms['_binman_u_boot_spl_any_prop_offset'].address, 1518 addr + 4) 1519 1520 self._SetupSplElf('u_boot_binman_syms') 1521 data = self._DoReadFileDtb(dts, entry_args=entry_args, 1522 use_expanded=use_expanded)[0] 1523 # The image should contain the symbols from u_boot_binman_syms.c 1524 # Note that image_pos is adjusted by the base address of the image, 1525 # which is 0x10 in our test image 1526 sym_values = struct.pack('<LLQLL', elf.BINMAN_SYM_MAGIC_VALUE, 1527 0x00, u_boot_offset + len(U_BOOT_DATA), 1528 0x10 + u_boot_offset, 0x04) 1529 if no_write_symbols: 1530 expected = (base_data + 1531 tools.get_bytes(0xff, 0x38 - len(base_data)) + 1532 U_BOOT_DATA + base_data) 1533 else: 1534 expected = (sym_values + base_data[24:] + 1535 tools.get_bytes(0xff, 1) + U_BOOT_DATA + sym_values + 1536 base_data[24:]) 1537 self.assertEqual(expected, data) 1538 1539 def testSymbols(self): 1540 """Test binman can assign symbols embedded in U-Boot""" 1541 self.checkSymbols('053_symbols.dts', U_BOOT_SPL_DATA, 0x1c) 1542 1543 def testSymbolsNoDtb(self): 1544 """Test binman can assign symbols embedded in U-Boot SPL""" 1545 self.checkSymbols('196_symbols_nodtb.dts', 1546 U_BOOT_SPL_NODTB_DATA + U_BOOT_SPL_DTB_DATA, 1547 0x38) 1548 1549 def testPackUnitAddress(self): 1550 """Test that we support multiple binaries with the same name""" 1551 data = self._DoReadFile('054_unit_address.dts') 1552 self.assertEqual(U_BOOT_DATA + U_BOOT_DATA, data) 1553 1554 def testSections(self): 1555 """Basic test of sections""" 1556 data = self._DoReadFile('055_sections.dts') 1557 expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) + 1558 U_BOOT_DATA + tools.get_bytes(ord('a'), 12) + 1559 U_BOOT_DATA + tools.get_bytes(ord('&'), 4)) 1560 self.assertEqual(expected, data) 1561 1562 def testMap(self): 1563 """Tests outputting a map of the images""" 1564 _, _, map_data, _ = self._DoReadFileDtb('055_sections.dts', map=True) 1565 self.assertEqual('''ImagePos Offset Size Name 156600000000 00000000 00000028 image 156700000000 00000000 00000010 section@0 156800000000 00000000 00000004 u-boot 156900000010 00000010 00000010 section@1 157000000010 00000000 00000004 u-boot 157100000020 00000020 00000004 section@2 157200000020 00000000 00000004 u-boot 1573''', map_data) 1574 1575 def testNamePrefix(self): 1576 """Tests that name prefixes are used""" 1577 _, _, map_data, _ = self._DoReadFileDtb('056_name_prefix.dts', map=True) 1578 self.assertEqual('''ImagePos Offset Size Name 157900000000 00000000 00000028 image 158000000000 00000000 00000010 section@0 158100000000 00000000 00000004 ro-u-boot 158200000010 00000010 00000010 section@1 158300000010 00000000 00000004 rw-u-boot 1584''', map_data) 1585 1586 def testUnknownContents(self): 1587 """Test that obtaining the contents works as expected""" 1588 with self.assertRaises(ValueError) as e: 1589 self._DoReadFile('057_unknown_contents.dts', True) 1590 self.assertIn("Image '/binman': Internal error: Could not complete " 1591 "processing of contents: remaining [" 1592 "<binman.etype._testing.Entry__testing ", str(e.exception)) 1593 1594 def testBadChangeSize(self): 1595 """Test that trying to change the size of an entry fails""" 1596 try: 1597 state.SetAllowEntryExpansion(False) 1598 with self.assertRaises(ValueError) as e: 1599 self._DoReadFile('059_change_size.dts', True) 1600 self.assertIn("Node '/binman/_testing': Cannot update entry size from 2 to 3", 1601 str(e.exception)) 1602 finally: 1603 state.SetAllowEntryExpansion(True) 1604 1605 def testUpdateFdt(self): 1606 """Test that we can update the device tree with offset/size info""" 1607 _, _, _, out_dtb_fname = self._DoReadFileDtb('060_fdt_update.dts', 1608 update_dtb=True) 1609 dtb = fdt.Fdt(out_dtb_fname) 1610 dtb.Scan() 1611 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS) 1612 self.assertEqual({ 1613 'image-pos': 0, 1614 'offset': 0, 1615 '_testing:offset': 32, 1616 '_testing:size': 2, 1617 '_testing:image-pos': 32, 1618 'section@0/u-boot:offset': 0, 1619 'section@0/u-boot:size': len(U_BOOT_DATA), 1620 'section@0/u-boot:image-pos': 0, 1621 'section@0:offset': 0, 1622 'section@0:size': 16, 1623 'section@0:image-pos': 0, 1624 1625 'section@1/u-boot:offset': 0, 1626 'section@1/u-boot:size': len(U_BOOT_DATA), 1627 'section@1/u-boot:image-pos': 16, 1628 'section@1:offset': 16, 1629 'section@1:size': 16, 1630 'section@1:image-pos': 16, 1631 'size': 40 1632 }, props) 1633 1634 def testUpdateFdtBad(self): 1635 """Test that we detect when ProcessFdt never completes""" 1636 with self.assertRaises(ValueError) as e: 1637 self._DoReadFileDtb('061_fdt_update_bad.dts', update_dtb=True) 1638 self.assertIn('Could not complete processing of Fdt: remaining ' 1639 '[<binman.etype._testing.Entry__testing', 1640 str(e.exception)) 1641 1642 def testEntryArgs(self): 1643 """Test passing arguments to entries from the command line""" 1644 entry_args = { 1645 'test-str-arg': 'test1', 1646 'test-int-arg': '456', 1647 } 1648 self._DoReadFileDtb('062_entry_args.dts', entry_args=entry_args) 1649 self.assertIn('image', control.images) 1650 entry = control.images['image'].GetEntries()['_testing'] 1651 self.assertEqual('test0', entry.test_str_fdt) 1652 self.assertEqual('test1', entry.test_str_arg) 1653 self.assertEqual(123, entry.test_int_fdt) 1654 self.assertEqual(456, entry.test_int_arg) 1655 1656 def testEntryArgsMissing(self): 1657 """Test missing arguments and properties""" 1658 entry_args = { 1659 'test-int-arg': '456', 1660 } 1661 self._DoReadFileDtb('063_entry_args_missing.dts', entry_args=entry_args) 1662 entry = control.images['image'].GetEntries()['_testing'] 1663 self.assertEqual('test0', entry.test_str_fdt) 1664 self.assertEqual(None, entry.test_str_arg) 1665 self.assertEqual(None, entry.test_int_fdt) 1666 self.assertEqual(456, entry.test_int_arg) 1667 1668 def testEntryArgsRequired(self): 1669 """Test missing arguments and properties""" 1670 entry_args = { 1671 'test-int-arg': '456', 1672 } 1673 with self.assertRaises(ValueError) as e: 1674 self._DoReadFileDtb('064_entry_args_required.dts') 1675 self.assertIn("Node '/binman/_testing': " 1676 'Missing required properties/entry args: test-str-arg, ' 1677 'test-int-fdt, test-int-arg', 1678 str(e.exception)) 1679 1680 def testEntryArgsInvalidFormat(self): 1681 """Test that an invalid entry-argument format is detected""" 1682 args = ['build', '-d', self.TestFile('064_entry_args_required.dts'), 1683 '-ano-value'] 1684 with self.assertRaises(ValueError) as e: 1685 self._DoBinman(*args) 1686 self.assertIn("Invalid entry arguemnt 'no-value'", str(e.exception)) 1687 1688 def testEntryArgsInvalidInteger(self): 1689 """Test that an invalid entry-argument integer is detected""" 1690 entry_args = { 1691 'test-int-arg': 'abc', 1692 } 1693 with self.assertRaises(ValueError) as e: 1694 self._DoReadFileDtb('062_entry_args.dts', entry_args=entry_args) 1695 self.assertIn("Node '/binman/_testing': Cannot convert entry arg " 1696 "'test-int-arg' (value 'abc') to integer", 1697 str(e.exception)) 1698 1699 def testEntryArgsInvalidDatatype(self): 1700 """Test that an invalid entry-argument datatype is detected 1701 1702 This test could be written in entry_test.py except that it needs 1703 access to control.entry_args, which seems more than that module should 1704 be able to see. 1705 """ 1706 entry_args = { 1707 'test-bad-datatype-arg': '12', 1708 } 1709 with self.assertRaises(ValueError) as e: 1710 self._DoReadFileDtb('065_entry_args_unknown_datatype.dts', 1711 entry_args=entry_args) 1712 self.assertIn('GetArg() internal error: Unknown data type ', 1713 str(e.exception)) 1714 1715 def testText(self): 1716 """Test for a text entry type""" 1717 entry_args = { 1718 'test-id': TEXT_DATA, 1719 'test-id2': TEXT_DATA2, 1720 'test-id3': TEXT_DATA3, 1721 } 1722 data, _, _, _ = self._DoReadFileDtb('066_text.dts', 1723 entry_args=entry_args) 1724 expected = (tools.to_bytes(TEXT_DATA) + 1725 tools.get_bytes(0, 8 - len(TEXT_DATA)) + 1726 tools.to_bytes(TEXT_DATA2) + tools.to_bytes(TEXT_DATA3) + 1727 b'some text' + b'more text') 1728 self.assertEqual(expected, data) 1729 1730 def testEntryDocs(self): 1731 """Test for creation of entry documentation""" 1732 with test_util.capture_sys_output() as (stdout, stderr): 1733 control.WriteEntryDocs(control.GetEntryModules()) 1734 self.assertTrue(len(stdout.getvalue()) > 0) 1735 1736 def testEntryDocsMissing(self): 1737 """Test handling of missing entry documentation""" 1738 with self.assertRaises(ValueError) as e: 1739 with test_util.capture_sys_output() as (stdout, stderr): 1740 control.WriteEntryDocs(control.GetEntryModules(), 'u_boot') 1741 self.assertIn('Documentation is missing for modules: u_boot', 1742 str(e.exception)) 1743 1744 def testFmap(self): 1745 """Basic test of generation of a flashrom fmap""" 1746 data = self._DoReadFile('067_fmap.dts') 1747 fhdr, fentries = fmap_util.DecodeFmap(data[32:]) 1748 expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) + 1749 U_BOOT_DATA + tools.get_bytes(ord('a'), 12)) 1750 self.assertEqual(expected, data[:32]) 1751 self.assertEqual(b'__FMAP__', fhdr.signature) 1752 self.assertEqual(1, fhdr.ver_major) 1753 self.assertEqual(0, fhdr.ver_minor) 1754 self.assertEqual(0, fhdr.base) 1755 expect_size = fmap_util.FMAP_HEADER_LEN + fmap_util.FMAP_AREA_LEN * 5 1756 self.assertEqual(16 + 16 + expect_size, fhdr.image_size) 1757 self.assertEqual(b'FMAP', fhdr.name) 1758 self.assertEqual(5, fhdr.nareas) 1759 fiter = iter(fentries) 1760 1761 fentry = next(fiter) 1762 self.assertEqual(b'SECTION0', fentry.name) 1763 self.assertEqual(0, fentry.offset) 1764 self.assertEqual(16, fentry.size) 1765 self.assertEqual(fmap_util.FMAP_AREA_PRESERVE, fentry.flags) 1766 1767 fentry = next(fiter) 1768 self.assertEqual(b'RO_U_BOOT', fentry.name) 1769 self.assertEqual(0, fentry.offset) 1770 self.assertEqual(4, fentry.size) 1771 self.assertEqual(0, fentry.flags) 1772 1773 fentry = next(fiter) 1774 self.assertEqual(b'SECTION1', fentry.name) 1775 self.assertEqual(16, fentry.offset) 1776 self.assertEqual(16, fentry.size) 1777 self.assertEqual(0, fentry.flags) 1778 1779 fentry = next(fiter) 1780 self.assertEqual(b'RW_U_BOOT', fentry.name) 1781 self.assertEqual(16, fentry.offset) 1782 self.assertEqual(4, fentry.size) 1783 self.assertEqual(0, fentry.flags) 1784 1785 fentry = next(fiter) 1786 self.assertEqual(b'FMAP', fentry.name) 1787 self.assertEqual(32, fentry.offset) 1788 self.assertEqual(expect_size, fentry.size) 1789 self.assertEqual(0, fentry.flags) 1790 1791 def testBlobNamedByArg(self): 1792 """Test we can add a blob with the filename coming from an entry arg""" 1793 entry_args = { 1794 'cros-ec-rw-path': 'ecrw.bin', 1795 } 1796 self._DoReadFileDtb('068_blob_named_by_arg.dts', entry_args=entry_args) 1797 1798 def testFill(self): 1799 """Test for an fill entry type""" 1800 data = self._DoReadFile('069_fill.dts') 1801 expected = tools.get_bytes(0xff, 8) + tools.get_bytes(0, 8) 1802 self.assertEqual(expected, data) 1803 1804 def testFillNoSize(self): 1805 """Test for an fill entry type with no size""" 1806 with self.assertRaises(ValueError) as e: 1807 self._DoReadFile('070_fill_no_size.dts') 1808 self.assertIn("'fill' entry is missing properties: size", 1809 str(e.exception)) 1810 1811 def _HandleGbbCommand(self, pipe_list): 1812 """Fake calls to the futility utility""" 1813 if 'futility' in pipe_list[0][0]: 1814 fname = pipe_list[0][-1] 1815 # Append our GBB data to the file, which will happen every time the 1816 # futility command is called. 1817 with open(fname, 'ab') as fd: 1818 fd.write(GBB_DATA) 1819 return command.CommandResult() 1820 1821 def testGbb(self): 1822 """Test for the Chromium OS Google Binary Block""" 1823 command.test_result = self._HandleGbbCommand 1824 entry_args = { 1825 'keydir': 'devkeys', 1826 'bmpblk': 'bmpblk.bin', 1827 } 1828 data, _, _, _ = self._DoReadFileDtb('071_gbb.dts', entry_args=entry_args) 1829 1830 # Since futility 1831 expected = (GBB_DATA + GBB_DATA + tools.get_bytes(0, 8) + 1832 tools.get_bytes(0, 0x2180 - 16)) 1833 self.assertEqual(expected, data) 1834 1835 def testGbbTooSmall(self): 1836 """Test for the Chromium OS Google Binary Block being large enough""" 1837 with self.assertRaises(ValueError) as e: 1838 self._DoReadFileDtb('072_gbb_too_small.dts') 1839 self.assertIn("Node '/binman/gbb': GBB is too small", 1840 str(e.exception)) 1841 1842 def testGbbNoSize(self): 1843 """Test for the Chromium OS Google Binary Block having a size""" 1844 with self.assertRaises(ValueError) as e: 1845 self._DoReadFileDtb('073_gbb_no_size.dts') 1846 self.assertIn("Node '/binman/gbb': GBB must have a fixed size", 1847 str(e.exception)) 1848 1849 def testGbbMissing(self): 1850 """Test that binman still produces an image if futility is missing""" 1851 entry_args = { 1852 'keydir': 'devkeys', 1853 } 1854 with test_util.capture_sys_output() as (_, stderr): 1855 self._DoTestFile('071_gbb.dts', force_missing_bintools='futility', 1856 entry_args=entry_args) 1857 err = stderr.getvalue() 1858 self.assertRegex(err, "Image 'image'.*missing bintools.*: futility") 1859 1860 def _HandleVblockCommand(self, pipe_list): 1861 """Fake calls to the futility utility 1862 1863 The expected pipe is: 1864 1865 [('futility', 'vbutil_firmware', '--vblock', 1866 'vblock.vblock', '--keyblock', 'devkeys/firmware.keyblock', 1867 '--signprivate', 'devkeys/firmware_data_key.vbprivk', 1868 '--version', '1', '--fv', 'input.vblock', '--kernelkey', 1869 'devkeys/kernel_subkey.vbpubk', '--flags', '1')] 1870 1871 This writes to the output file (here, 'vblock.vblock'). If 1872 self._hash_data is False, it writes VBLOCK_DATA, else it writes a hash 1873 of the input data (here, 'input.vblock'). 1874 """ 1875 if 'futility' in pipe_list[0][0]: 1876 fname = pipe_list[0][3] 1877 with open(fname, 'wb') as fd: 1878 if self._hash_data: 1879 infile = pipe_list[0][11] 1880 m = hashlib.sha256() 1881 data = tools.read_file(infile) 1882 m.update(data) 1883 fd.write(m.digest()) 1884 else: 1885 fd.write(VBLOCK_DATA) 1886 1887 return command.CommandResult() 1888 1889 def testVblock(self): 1890 """Test for the Chromium OS Verified Boot Block""" 1891 self._hash_data = False 1892 command.test_result = self._HandleVblockCommand 1893 entry_args = { 1894 'keydir': 'devkeys', 1895 } 1896 data, _, _, _ = self._DoReadFileDtb('074_vblock.dts', 1897 entry_args=entry_args) 1898 expected = U_BOOT_DATA + VBLOCK_DATA + U_BOOT_DTB_DATA 1899 self.assertEqual(expected, data) 1900 1901 def testVblockNoContent(self): 1902 """Test we detect a vblock which has no content to sign""" 1903 with self.assertRaises(ValueError) as e: 1904 self._DoReadFile('075_vblock_no_content.dts') 1905 self.assertIn("Node '/binman/vblock': Collection must have a 'content' " 1906 'property', str(e.exception)) 1907 1908 def testVblockBadPhandle(self): 1909 """Test that we detect a vblock with an invalid phandle in contents""" 1910 with self.assertRaises(ValueError) as e: 1911 self._DoReadFile('076_vblock_bad_phandle.dts') 1912 self.assertIn("Node '/binman/vblock': Cannot find node for phandle " 1913 '1000', str(e.exception)) 1914 1915 def testVblockBadEntry(self): 1916 """Test that we detect an entry that points to a non-entry""" 1917 with self.assertRaises(ValueError) as e: 1918 self._DoReadFile('077_vblock_bad_entry.dts') 1919 self.assertIn("Node '/binman/vblock': Cannot find entry for node " 1920 "'other'", str(e.exception)) 1921 1922 def testVblockContent(self): 1923 """Test that the vblock signs the right data""" 1924 self._hash_data = True 1925 command.test_result = self._HandleVblockCommand 1926 entry_args = { 1927 'keydir': 'devkeys', 1928 } 1929 data = self._DoReadFileDtb( 1930 '189_vblock_content.dts', use_real_dtb=True, update_dtb=True, 1931 entry_args=entry_args)[0] 1932 hashlen = 32 # SHA256 hash is 32 bytes 1933 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)]) 1934 hashval = data[-hashlen:] 1935 dtb = data[len(U_BOOT_DATA):-hashlen] 1936 1937 expected_data = U_BOOT_DATA + dtb 1938 1939 # The hashval should be a hash of the dtb 1940 m = hashlib.sha256() 1941 m.update(expected_data) 1942 expected_hashval = m.digest() 1943 self.assertEqual(expected_hashval, hashval) 1944 1945 def testVblockMissing(self): 1946 """Test that binman still produces an image if futility is missing""" 1947 entry_args = { 1948 'keydir': 'devkeys', 1949 } 1950 with test_util.capture_sys_output() as (_, stderr): 1951 self._DoTestFile('074_vblock.dts', 1952 force_missing_bintools='futility', 1953 entry_args=entry_args) 1954 err = stderr.getvalue() 1955 self.assertRegex(err, "Image 'image'.*missing bintools.*: futility") 1956 1957 def testTpl(self): 1958 """Test that an image with TPL and its device tree can be created""" 1959 # ELF file with a '__bss_size' symbol 1960 self._SetupTplElf() 1961 data = self._DoReadFile('078_u_boot_tpl.dts') 1962 self.assertEqual(U_BOOT_TPL_DATA + U_BOOT_TPL_DTB_DATA, data) 1963 1964 def testUsesPos(self): 1965 """Test that the 'pos' property cannot be used anymore""" 1966 with self.assertRaises(ValueError) as e: 1967 data = self._DoReadFile('079_uses_pos.dts') 1968 self.assertIn("Node '/binman/u-boot': Please use 'offset' instead of " 1969 "'pos'", str(e.exception)) 1970 1971 def testFillZero(self): 1972 """Test for an fill entry type with a size of 0""" 1973 data = self._DoReadFile('080_fill_empty.dts') 1974 self.assertEqual(tools.get_bytes(0, 16), data) 1975 1976 def testTextMissing(self): 1977 """Test for a text entry type where there is no text""" 1978 with self.assertRaises(ValueError) as e: 1979 self._DoReadFileDtb('066_text.dts',) 1980 self.assertIn("Node '/binman/text': No value provided for text label " 1981 "'test-id'", str(e.exception)) 1982 1983 def testPackStart16Tpl(self): 1984 """Test that an image with an x86 start16 TPL region can be created""" 1985 data = self._DoReadFile('081_x86_start16_tpl.dts') 1986 self.assertEqual(X86_START16_TPL_DATA, data[:len(X86_START16_TPL_DATA)]) 1987 1988 def testSelectImage(self): 1989 """Test that we can select which images to build""" 1990 expected = 'Skipping images: image1' 1991 1992 # We should only get the expected message in verbose mode 1993 for verbosity in (0, 2): 1994 with test_util.capture_sys_output() as (stdout, stderr): 1995 retcode = self._DoTestFile('006_dual_image.dts', 1996 verbosity=verbosity, 1997 images=['image2']) 1998 self.assertEqual(0, retcode) 1999 if verbosity: 2000 self.assertIn(expected, stdout.getvalue()) 2001 else: 2002 self.assertNotIn(expected, stdout.getvalue()) 2003 2004 self.assertFalse(os.path.exists(tools.get_output_filename('image1.bin'))) 2005 self.assertTrue(os.path.exists(tools.get_output_filename('image2.bin'))) 2006 self._CleanupOutputDir() 2007 2008 def testUpdateFdtAll(self): 2009 """Test that all device trees are updated with offset/size info""" 2010 self._SetupSplElf() 2011 self._SetupTplElf() 2012 data = self._DoReadFileRealDtb('082_fdt_update_all.dts') 2013 2014 base_expected = { 2015 'offset': 0, 2016 'image-pos': 0, 2017 'size': 2320, 2018 'section:offset': 0, 2019 'section:image-pos': 0, 2020 'section:size': 565, 2021 'section/u-boot-dtb:offset': 0, 2022 'section/u-boot-dtb:image-pos': 0, 2023 'section/u-boot-dtb:size': 565, 2024 'u-boot-spl-dtb:offset': 565, 2025 'u-boot-spl-dtb:image-pos': 565, 2026 'u-boot-spl-dtb:size': 585, 2027 'u-boot-tpl-dtb:offset': 1150, 2028 'u-boot-tpl-dtb:image-pos': 1150, 2029 'u-boot-tpl-dtb:size': 585, 2030 'u-boot-vpl-dtb:image-pos': 1735, 2031 'u-boot-vpl-dtb:offset': 1735, 2032 'u-boot-vpl-dtb:size': 585, 2033 } 2034 2035 # We expect three device-tree files in the output, one after the other. 2036 # Read them in sequence. We look for an 'spl' property in the SPL tree, 2037 # and 'tpl' in the TPL tree, to make sure they are distinct from the 2038 # main U-Boot tree. All three should have the same postions and offset. 2039 start = 0 2040 self.maxDiff = None 2041 for item in ['', 'spl', 'tpl', 'vpl']: 2042 dtb = fdt.Fdt.FromData(data[start:]) 2043 dtb.Scan() 2044 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS + 2045 ['spl', 'tpl', 'vpl']) 2046 expected = dict(base_expected) 2047 if item: 2048 expected[item] = 0 2049 self.assertEqual(expected, props) 2050 start += dtb._fdt_obj.totalsize() 2051 2052 def testUpdateFdtOutput(self): 2053 """Test that output DTB files are updated""" 2054 try: 2055 data, dtb_data, _, _ = self._DoReadFileDtb('082_fdt_update_all.dts', 2056 use_real_dtb=True, update_dtb=True, reset_dtbs=False) 2057 2058 # Unfortunately, compiling a source file always results in a file 2059 # called source.dtb (see fdt_util.EnsureCompiled()). The test 2060 # source file (e.g. test/075_fdt_update_all.dts) thus does not enter 2061 # binman as a file called u-boot.dtb. To fix this, copy the file 2062 # over to the expected place. 2063 start = 0 2064 for fname in ['u-boot.dtb.out', 'spl/u-boot-spl.dtb.out', 2065 'tpl/u-boot-tpl.dtb.out', 'vpl/u-boot-vpl.dtb.out']: 2066 dtb = fdt.Fdt.FromData(data[start:]) 2067 size = dtb._fdt_obj.totalsize() 2068 pathname = tools.get_output_filename(os.path.split(fname)[1]) 2069 outdata = tools.read_file(pathname) 2070 name = os.path.split(fname)[0] 2071 2072 if name: 2073 orig_indata = self._GetDtbContentsForSpls(dtb_data, name) 2074 else: 2075 orig_indata = dtb_data 2076 self.assertNotEqual(outdata, orig_indata, 2077 "Expected output file '%s' be updated" % pathname) 2078 self.assertEqual(outdata, data[start:start + size], 2079 "Expected output file '%s' to match output image" % 2080 pathname) 2081 start += size 2082 finally: 2083 self._ResetDtbs() 2084 2085 def _decompress(self, data): 2086 bintool = self.comp_bintools['lz4'] 2087 return bintool.decompress(data) 2088 2089 def testCompress(self): 2090 """Test compression of blobs""" 2091 self._CheckLz4() 2092 data, _, _, out_dtb_fname = self._DoReadFileDtb('083_compress.dts', 2093 use_real_dtb=True, update_dtb=True) 2094 dtb = fdt.Fdt(out_dtb_fname) 2095 dtb.Scan() 2096 props = self._GetPropTree(dtb, ['size', 'uncomp-size']) 2097 orig = self._decompress(data) 2098 self.assertEquals(COMPRESS_DATA, orig) 2099 2100 # Do a sanity check on various fields 2101 image = control.images['image'] 2102 entries = image.GetEntries() 2103 self.assertEqual(1, len(entries)) 2104 2105 entry = entries['blob'] 2106 self.assertEqual(COMPRESS_DATA, entry.uncomp_data) 2107 self.assertEqual(len(COMPRESS_DATA), entry.uncomp_size) 2108 orig = self._decompress(entry.data) 2109 self.assertEqual(orig, entry.uncomp_data) 2110 2111 self.assertEqual(image.data, entry.data) 2112 2113 expected = { 2114 'blob:uncomp-size': len(COMPRESS_DATA), 2115 'blob:size': len(data), 2116 'size': len(data), 2117 } 2118 self.assertEqual(expected, props) 2119 2120 def testFiles(self): 2121 """Test bringing in multiple files""" 2122 data = self._DoReadFile('084_files.dts') 2123 self.assertEqual(FILES_DATA, data) 2124 2125 def testFilesCompress(self): 2126 """Test bringing in multiple files and compressing them""" 2127 self._CheckLz4() 2128 data = self._DoReadFile('085_files_compress.dts') 2129 2130 image = control.images['image'] 2131 entries = image.GetEntries() 2132 files = entries['files'] 2133 entries = files._entries 2134 2135 orig = b'' 2136 for i in range(1, 3): 2137 key = '%d.dat' % i 2138 start = entries[key].image_pos 2139 len = entries[key].size 2140 chunk = data[start:start + len] 2141 orig += self._decompress(chunk) 2142 2143 self.assertEqual(FILES_DATA, orig) 2144 2145 def testFilesMissing(self): 2146 """Test missing files""" 2147 with self.assertRaises(ValueError) as e: 2148 data = self._DoReadFile('086_files_none.dts') 2149 self.assertIn("Node '/binman/files': Pattern \'files/*.none\' matched " 2150 'no files', str(e.exception)) 2151 2152 def testFilesNoPattern(self): 2153 """Test missing files""" 2154 with self.assertRaises(ValueError) as e: 2155 data = self._DoReadFile('087_files_no_pattern.dts') 2156 self.assertIn("Node '/binman/files': Missing 'pattern' property", 2157 str(e.exception)) 2158 2159 def testExtendSize(self): 2160 """Test an extending entry""" 2161 data, _, map_data, _ = self._DoReadFileDtb('088_extend_size.dts', 2162 map=True) 2163 expect = (tools.get_bytes(ord('a'), 8) + U_BOOT_DATA + 2164 MRC_DATA + tools.get_bytes(ord('b'), 1) + U_BOOT_DATA + 2165 tools.get_bytes(ord('c'), 8) + U_BOOT_DATA + 2166 tools.get_bytes(ord('d'), 8)) 2167 self.assertEqual(expect, data) 2168 self.assertEqual('''ImagePos Offset Size Name 216900000000 00000000 00000028 image 217000000000 00000000 00000008 fill 217100000008 00000008 00000004 u-boot 21720000000c 0000000c 00000004 section 21730000000c 00000000 00000003 intel-mrc 217400000010 00000010 00000004 u-boot2 217500000014 00000014 0000000c section2 217600000014 00000000 00000008 fill 21770000001c 00000008 00000004 u-boot 217800000020 00000020 00000008 fill2 2179''', map_data) 2180 2181 def testExtendSizeBad(self): 2182 """Test an extending entry which fails to provide contents""" 2183 with test_util.capture_sys_output() as (stdout, stderr): 2184 with self.assertRaises(ValueError) as e: 2185 self._DoReadFileDtb('089_extend_size_bad.dts', map=True) 2186 self.assertIn("Node '/binman/_testing': Cannot obtain contents when " 2187 'expanding entry', str(e.exception)) 2188 2189 def testHash(self): 2190 """Test hashing of the contents of an entry""" 2191 _, _, _, out_dtb_fname = self._DoReadFileDtb('090_hash.dts', 2192 use_real_dtb=True, update_dtb=True) 2193 dtb = fdt.Fdt(out_dtb_fname) 2194 dtb.Scan() 2195 hash_node = dtb.GetNode('/binman/u-boot/hash').props['value'] 2196 m = hashlib.sha256() 2197 m.update(U_BOOT_DATA) 2198 self.assertEqual(m.digest(), b''.join(hash_node.value)) 2199 2200 def testHashNoAlgo(self): 2201 with self.assertRaises(ValueError) as e: 2202 self._DoReadFileDtb('091_hash_no_algo.dts', update_dtb=True) 2203 self.assertIn("Node \'/binman/u-boot\': Missing \'algo\' property for " 2204 'hash node', str(e.exception)) 2205 2206 def testHashBadAlgo(self): 2207 with self.assertRaises(ValueError) as e: 2208 self._DoReadFileDtb('092_hash_bad_algo.dts', update_dtb=True) 2209 self.assertIn("Node '/binman/u-boot': Unknown hash algorithm 'invalid'", 2210 str(e.exception)) 2211 2212 def testHashSection(self): 2213 """Test hashing of the contents of an entry""" 2214 _, _, _, out_dtb_fname = self._DoReadFileDtb('099_hash_section.dts', 2215 use_real_dtb=True, update_dtb=True) 2216 dtb = fdt.Fdt(out_dtb_fname) 2217 dtb.Scan() 2218 hash_node = dtb.GetNode('/binman/section/hash').props['value'] 2219 m = hashlib.sha256() 2220 m.update(U_BOOT_DATA) 2221 m.update(tools.get_bytes(ord('a'), 16)) 2222 self.assertEqual(m.digest(), b''.join(hash_node.value)) 2223 2224 def testPackUBootTplMicrocode(self): 2225 """Test that x86 microcode can be handled correctly in TPL 2226 2227 We expect to see the following in the image, in order: 2228 u-boot-tpl-nodtb.bin with a microcode pointer inserted at the correct 2229 place 2230 u-boot-tpl.dtb with the microcode removed 2231 the microcode 2232 """ 2233 self._SetupTplElf('u_boot_ucode_ptr') 2234 first, pos_and_size = self._RunMicrocodeTest('093_x86_tpl_ucode.dts', 2235 U_BOOT_TPL_NODTB_DATA) 2236 self.assertEqual(b'tplnodtb with microc' + pos_and_size + 2237 b'ter somewhere in here', first) 2238 2239 def testFmapX86(self): 2240 """Basic test of generation of a flashrom fmap""" 2241 data = self._DoReadFile('094_fmap_x86.dts') 2242 fhdr, fentries = fmap_util.DecodeFmap(data[32:]) 2243 expected = U_BOOT_DATA + MRC_DATA + tools.get_bytes(ord('a'), 32 - 7) 2244 self.assertEqual(expected, data[:32]) 2245 fhdr, fentries = fmap_util.DecodeFmap(data[32:]) 2246 2247 self.assertEqual(0x100, fhdr.image_size) 2248 2249 self.assertEqual(0, fentries[0].offset) 2250 self.assertEqual(4, fentries[0].size) 2251 self.assertEqual(b'U_BOOT', fentries[0].name) 2252 2253 self.assertEqual(4, fentries[1].offset) 2254 self.assertEqual(3, fentries[1].size) 2255 self.assertEqual(b'INTEL_MRC', fentries[1].name) 2256 2257 self.assertEqual(32, fentries[2].offset) 2258 self.assertEqual(fmap_util.FMAP_HEADER_LEN + 2259 fmap_util.FMAP_AREA_LEN * 3, fentries[2].size) 2260 self.assertEqual(b'FMAP', fentries[2].name) 2261 2262 def testFmapX86Section(self): 2263 """Basic test of generation of a flashrom fmap""" 2264 data = self._DoReadFile('095_fmap_x86_section.dts') 2265 expected = U_BOOT_DATA + MRC_DATA + tools.get_bytes(ord('b'), 32 - 7) 2266 self.assertEqual(expected, data[:32]) 2267 fhdr, fentries = fmap_util.DecodeFmap(data[36:]) 2268 2269 self.assertEqual(0x180, fhdr.image_size) 2270 expect_size = fmap_util.FMAP_HEADER_LEN + fmap_util.FMAP_AREA_LEN * 4 2271 fiter = iter(fentries) 2272 2273 fentry = next(fiter) 2274 self.assertEqual(b'U_BOOT', fentry.name) 2275 self.assertEqual(0, fentry.offset) 2276 self.assertEqual(4, fentry.size) 2277 2278 fentry = next(fiter) 2279 self.assertEqual(b'SECTION', fentry.name) 2280 self.assertEqual(4, fentry.offset) 2281 self.assertEqual(0x20 + expect_size, fentry.size) 2282 2283 fentry = next(fiter) 2284 self.assertEqual(b'INTEL_MRC', fentry.name) 2285 self.assertEqual(4, fentry.offset) 2286 self.assertEqual(3, fentry.size) 2287 2288 fentry = next(fiter) 2289 self.assertEqual(b'FMAP', fentry.name) 2290 self.assertEqual(36, fentry.offset) 2291 self.assertEqual(expect_size, fentry.size) 2292 2293 def testElf(self): 2294 """Basic test of ELF entries""" 2295 self._SetupSplElf() 2296 self._SetupTplElf() 2297 with open(self.ElfTestFile('bss_data'), 'rb') as fd: 2298 TestFunctional._MakeInputFile('-boot', fd.read()) 2299 data = self._DoReadFile('096_elf.dts') 2300 2301 def testElfStrip(self): 2302 """Basic test of ELF entries""" 2303 self._SetupSplElf() 2304 with open(self.ElfTestFile('bss_data'), 'rb') as fd: 2305 TestFunctional._MakeInputFile('-boot', fd.read()) 2306 data = self._DoReadFile('097_elf_strip.dts') 2307 2308 def testPackOverlapMap(self): 2309 """Test that overlapping regions are detected""" 2310 with test_util.capture_sys_output() as (stdout, stderr): 2311 with self.assertRaises(ValueError) as e: 2312 self._DoTestFile('014_pack_overlap.dts', map=True) 2313 map_fname = tools.get_output_filename('image.map') 2314 self.assertEqual("Wrote map file '%s' to show errors\n" % map_fname, 2315 stdout.getvalue()) 2316 2317 # We should not get an inmage, but there should be a map file 2318 self.assertFalse(os.path.exists(tools.get_output_filename('image.bin'))) 2319 self.assertTrue(os.path.exists(map_fname)) 2320 map_data = tools.read_file(map_fname, binary=False) 2321 self.assertEqual('''ImagePos Offset Size Name 2322<none> 00000000 00000008 image 2323<none> 00000000 00000004 u-boot 2324<none> 00000003 00000004 u-boot-align 2325''', map_data) 2326 2327 def testPackRefCode(self): 2328 """Test that an image with an Intel Reference code binary works""" 2329 data = self._DoReadFile('100_intel_refcode.dts') 2330 self.assertEqual(REFCODE_DATA, data[:len(REFCODE_DATA)]) 2331 2332 def testSectionOffset(self): 2333 """Tests use of a section with an offset""" 2334 data, _, map_data, _ = self._DoReadFileDtb('101_sections_offset.dts', 2335 map=True) 2336 self.assertEqual('''ImagePos Offset Size Name 233700000000 00000000 00000038 image 233800000004 00000004 00000010 section@0 233900000004 00000000 00000004 u-boot 234000000018 00000018 00000010 section@1 234100000018 00000000 00000004 u-boot 23420000002c 0000002c 00000004 section@2 23430000002c 00000000 00000004 u-boot 2344''', map_data) 2345 self.assertEqual(data, 2346 tools.get_bytes(0x26, 4) + U_BOOT_DATA + 2347 tools.get_bytes(0x21, 12) + 2348 tools.get_bytes(0x26, 4) + U_BOOT_DATA + 2349 tools.get_bytes(0x61, 12) + 2350 tools.get_bytes(0x26, 4) + U_BOOT_DATA + 2351 tools.get_bytes(0x26, 8)) 2352 2353 def testCbfsRaw(self): 2354 """Test base handling of a Coreboot Filesystem (CBFS) 2355 2356 The exact contents of the CBFS is verified by similar tests in 2357 cbfs_util_test.py. The tests here merely check that the files added to 2358 the CBFS can be found in the final image. 2359 """ 2360 data = self._DoReadFile('102_cbfs_raw.dts') 2361 size = 0xb0 2362 2363 cbfs = cbfs_util.CbfsReader(data) 2364 self.assertEqual(size, cbfs.rom_size) 2365 2366 self.assertIn('u-boot-dtb', cbfs.files) 2367 cfile = cbfs.files['u-boot-dtb'] 2368 self.assertEqual(U_BOOT_DTB_DATA, cfile.data) 2369 2370 def testCbfsArch(self): 2371 """Test on non-x86 architecture""" 2372 data = self._DoReadFile('103_cbfs_raw_ppc.dts') 2373 size = 0x100 2374 2375 cbfs = cbfs_util.CbfsReader(data) 2376 self.assertEqual(size, cbfs.rom_size) 2377 2378 self.assertIn('u-boot-dtb', cbfs.files) 2379 cfile = cbfs.files['u-boot-dtb'] 2380 self.assertEqual(U_BOOT_DTB_DATA, cfile.data) 2381 2382 def testCbfsStage(self): 2383 """Tests handling of a Coreboot Filesystem (CBFS)""" 2384 if not elf.ELF_TOOLS: 2385 self.skipTest('Python elftools not available') 2386 elf_fname = os.path.join(self._indir, 'cbfs-stage.elf') 2387 elf.MakeElf(elf_fname, U_BOOT_DATA, U_BOOT_DTB_DATA) 2388 size = 0xb0 2389 2390 data = self._DoReadFile('104_cbfs_stage.dts') 2391 cbfs = cbfs_util.CbfsReader(data) 2392 self.assertEqual(size, cbfs.rom_size) 2393 2394 self.assertIn('u-boot', cbfs.files) 2395 cfile = cbfs.files['u-boot'] 2396 self.assertEqual(U_BOOT_DATA + U_BOOT_DTB_DATA, cfile.data) 2397 2398 def testCbfsRawCompress(self): 2399 """Test handling of compressing raw files""" 2400 self._CheckLz4() 2401 data = self._DoReadFile('105_cbfs_raw_compress.dts') 2402 size = 0x140 2403 2404 cbfs = cbfs_util.CbfsReader(data) 2405 self.assertIn('u-boot', cbfs.files) 2406 cfile = cbfs.files['u-boot'] 2407 self.assertEqual(COMPRESS_DATA, cfile.data) 2408 2409 def testCbfsBadArch(self): 2410 """Test handling of a bad architecture""" 2411 with self.assertRaises(ValueError) as e: 2412 self._DoReadFile('106_cbfs_bad_arch.dts') 2413 self.assertIn("Invalid architecture 'bad-arch'", str(e.exception)) 2414 2415 def testCbfsNoSize(self): 2416 """Test handling of a missing size property""" 2417 with self.assertRaises(ValueError) as e: 2418 self._DoReadFile('107_cbfs_no_size.dts') 2419 self.assertIn('entry must have a size property', str(e.exception)) 2420 2421 def testCbfsNoContents(self): 2422 """Test handling of a CBFS entry which does not provide contentsy""" 2423 with self.assertRaises(ValueError) as e: 2424 self._DoReadFile('108_cbfs_no_contents.dts') 2425 self.assertIn('Could not complete processing of contents', 2426 str(e.exception)) 2427 2428 def testCbfsBadCompress(self): 2429 """Test handling of a bad architecture""" 2430 with self.assertRaises(ValueError) as e: 2431 self._DoReadFile('109_cbfs_bad_compress.dts') 2432 self.assertIn("Invalid compression in 'u-boot': 'invalid-algo'", 2433 str(e.exception)) 2434 2435 def testCbfsNamedEntries(self): 2436 """Test handling of named entries""" 2437 data = self._DoReadFile('110_cbfs_name.dts') 2438 2439 cbfs = cbfs_util.CbfsReader(data) 2440 self.assertIn('FRED', cbfs.files) 2441 cfile1 = cbfs.files['FRED'] 2442 self.assertEqual(U_BOOT_DATA, cfile1.data) 2443 2444 self.assertIn('hello', cbfs.files) 2445 cfile2 = cbfs.files['hello'] 2446 self.assertEqual(U_BOOT_DTB_DATA, cfile2.data) 2447 2448 def _SetupIfwi(self, fname): 2449 """Set up to run an IFWI test 2450 2451 Args: 2452 fname: Filename of input file to provide (fitimage.bin or ifwi.bin) 2453 """ 2454 self._SetupSplElf() 2455 self._SetupTplElf() 2456 2457 # Intel Integrated Firmware Image (IFWI) file 2458 with gzip.open(self.TestFile('%s.gz' % fname), 'rb') as fd: 2459 data = fd.read() 2460 TestFunctional._MakeInputFile(fname,data) 2461 2462 def _CheckIfwi(self, data): 2463 """Check that an image with an IFWI contains the correct output 2464 2465 Args: 2466 data: Conents of output file 2467 """ 2468 expected_desc = tools.read_file(self.TestFile('descriptor.bin')) 2469 if data[:0x1000] != expected_desc: 2470 self.fail('Expected descriptor binary at start of image') 2471 2472 # We expect to find the TPL wil in subpart IBBP entry IBBL 2473 image_fname = tools.get_output_filename('image.bin') 2474 tpl_fname = tools.get_output_filename('tpl.out') 2475 ifwitool = bintool.Bintool.create('ifwitool') 2476 ifwitool.extract(image_fname, 'IBBP', 'IBBL', tpl_fname) 2477 2478 tpl_data = tools.read_file(tpl_fname) 2479 self.assertEqual(U_BOOT_TPL_DATA, tpl_data[:len(U_BOOT_TPL_DATA)]) 2480 2481 def testPackX86RomIfwi(self): 2482 """Test that an x86 ROM with Integrated Firmware Image can be created""" 2483 self._SetupIfwi('fitimage.bin') 2484 data = self._DoReadFile('111_x86_rom_ifwi.dts') 2485 self._CheckIfwi(data) 2486 2487 def testPackX86RomIfwiNoDesc(self): 2488 """Test that an x86 ROM with IFWI can be created from an ifwi.bin file""" 2489 self._SetupIfwi('ifwi.bin') 2490 data = self._DoReadFile('112_x86_rom_ifwi_nodesc.dts') 2491 self._CheckIfwi(data) 2492 2493 def testPackX86RomIfwiNoData(self): 2494 """Test that an x86 ROM with IFWI handles missing data""" 2495 self._SetupIfwi('ifwi.bin') 2496 with self.assertRaises(ValueError) as e: 2497 data = self._DoReadFile('113_x86_rom_ifwi_nodata.dts') 2498 self.assertIn('Could not complete processing of contents', 2499 str(e.exception)) 2500 2501 def testIfwiMissing(self): 2502 """Test that binman still produces an image if ifwitool is missing""" 2503 self._SetupIfwi('fitimage.bin') 2504 with test_util.capture_sys_output() as (_, stderr): 2505 self._DoTestFile('111_x86_rom_ifwi.dts', 2506 force_missing_bintools='ifwitool') 2507 err = stderr.getvalue() 2508 self.assertRegex(err, 2509 "Image 'image'.*missing bintools.*: ifwitool") 2510 2511 def testCbfsOffset(self): 2512 """Test a CBFS with files at particular offsets 2513 2514 Like all CFBS tests, this is just checking the logic that calls 2515 cbfs_util. See cbfs_util_test for fully tests (e.g. test_cbfs_offset()). 2516 """ 2517 data = self._DoReadFile('114_cbfs_offset.dts') 2518 size = 0x200 2519 2520 cbfs = cbfs_util.CbfsReader(data) 2521 self.assertEqual(size, cbfs.rom_size) 2522 2523 self.assertIn('u-boot', cbfs.files) 2524 cfile = cbfs.files['u-boot'] 2525 self.assertEqual(U_BOOT_DATA, cfile.data) 2526 self.assertEqual(0x40, cfile.cbfs_offset) 2527 2528 self.assertIn('u-boot-dtb', cbfs.files) 2529 cfile2 = cbfs.files['u-boot-dtb'] 2530 self.assertEqual(U_BOOT_DTB_DATA, cfile2.data) 2531 self.assertEqual(0x140, cfile2.cbfs_offset) 2532 2533 def testFdtmap(self): 2534 """Test an FDT map can be inserted in the image""" 2535 data = self.data = self._DoReadFileRealDtb('115_fdtmap.dts') 2536 fdtmap_data = data[len(U_BOOT_DATA):] 2537 magic = fdtmap_data[:8] 2538 self.assertEqual(b'_FDTMAP_', magic) 2539 self.assertEqual(tools.get_bytes(0, 8), fdtmap_data[8:16]) 2540 2541 fdt_data = fdtmap_data[16:] 2542 dtb = fdt.Fdt.FromData(fdt_data) 2543 dtb.Scan() 2544 props = self._GetPropTree(dtb, BASE_DTB_PROPS, prefix='/') 2545 self.assertEqual({ 2546 'image-pos': 0, 2547 'offset': 0, 2548 'u-boot:offset': 0, 2549 'u-boot:size': len(U_BOOT_DATA), 2550 'u-boot:image-pos': 0, 2551 'fdtmap:image-pos': 4, 2552 'fdtmap:offset': 4, 2553 'fdtmap:size': len(fdtmap_data), 2554 'size': len(data), 2555 }, props) 2556 2557 def testFdtmapNoMatch(self): 2558 """Check handling of an FDT map when the section cannot be found""" 2559 self.data = self._DoReadFileRealDtb('115_fdtmap.dts') 2560 2561 # Mangle the section name, which should cause a mismatch between the 2562 # correct FDT path and the one expected by the section 2563 image = control.images['image'] 2564 image._node.path += '-suffix' 2565 entries = image.GetEntries() 2566 fdtmap = entries['fdtmap'] 2567 with self.assertRaises(ValueError) as e: 2568 fdtmap._GetFdtmap() 2569 self.assertIn("Cannot locate node for path '/binman-suffix'", 2570 str(e.exception)) 2571 2572 def testFdtmapHeader(self): 2573 """Test an FDT map and image header can be inserted in the image""" 2574 data = self.data = self._DoReadFileRealDtb('116_fdtmap_hdr.dts') 2575 fdtmap_pos = len(U_BOOT_DATA) 2576 fdtmap_data = data[fdtmap_pos:] 2577 fdt_data = fdtmap_data[16:] 2578 dtb = fdt.Fdt.FromData(fdt_data) 2579 fdt_size = dtb.GetFdtObj().totalsize() 2580 hdr_data = data[-8:] 2581 self.assertEqual(b'BinM', hdr_data[:4]) 2582 offset = struct.unpack('<I', hdr_data[4:])[0] & 0xffffffff 2583 self.assertEqual(fdtmap_pos - 0x400, offset - (1 << 32)) 2584 2585 def testFdtmapHeaderStart(self): 2586 """Test an image header can be inserted at the image start""" 2587 data = self.data = self._DoReadFileRealDtb('117_fdtmap_hdr_start.dts') 2588 fdtmap_pos = 0x100 + len(U_BOOT_DATA) 2589 hdr_data = data[:8] 2590 self.assertEqual(b'BinM', hdr_data[:4]) 2591 offset = struct.unpack('<I', hdr_data[4:])[0] 2592 self.assertEqual(fdtmap_pos, offset) 2593 2594 def testFdtmapHeaderPos(self): 2595 """Test an image header can be inserted at a chosen position""" 2596 data = self.data = self._DoReadFileRealDtb('118_fdtmap_hdr_pos.dts') 2597 fdtmap_pos = 0x100 + len(U_BOOT_DATA) 2598 hdr_data = data[0x80:0x88] 2599 self.assertEqual(b'BinM', hdr_data[:4]) 2600 offset = struct.unpack('<I', hdr_data[4:])[0] 2601 self.assertEqual(fdtmap_pos, offset) 2602 2603 def testHeaderMissingFdtmap(self): 2604 """Test an image header requires an fdtmap""" 2605 with self.assertRaises(ValueError) as e: 2606 self.data = self._DoReadFileRealDtb('119_fdtmap_hdr_missing.dts') 2607 self.assertIn("'image_header' section must have an 'fdtmap' sibling", 2608 str(e.exception)) 2609 2610 def testHeaderNoLocation(self): 2611 """Test an image header with a no specified location is detected""" 2612 with self.assertRaises(ValueError) as e: 2613 self.data = self._DoReadFileRealDtb('120_hdr_no_location.dts') 2614 self.assertIn("Invalid location 'None', expected 'start' or 'end'", 2615 str(e.exception)) 2616 2617 def testEntryExpand(self): 2618 """Test extending an entry after it is packed""" 2619 data = self._DoReadFile('121_entry_extend.dts') 2620 self.assertEqual(b'aaa', data[:3]) 2621 self.assertEqual(U_BOOT_DATA, data[3:3 + len(U_BOOT_DATA)]) 2622 self.assertEqual(b'aaa', data[-3:]) 2623 2624 def testEntryExtendBad(self): 2625 """Test extending an entry after it is packed, twice""" 2626 with self.assertRaises(ValueError) as e: 2627 self._DoReadFile('122_entry_extend_twice.dts') 2628 self.assertIn("Image '/binman': Entries changed size after packing", 2629 str(e.exception)) 2630 2631 def testEntryExtendSection(self): 2632 """Test extending an entry within a section after it is packed""" 2633 data = self._DoReadFile('123_entry_extend_section.dts') 2634 self.assertEqual(b'aaa', data[:3]) 2635 self.assertEqual(U_BOOT_DATA, data[3:3 + len(U_BOOT_DATA)]) 2636 self.assertEqual(b'aaa', data[-3:]) 2637 2638 def testCompressDtb(self): 2639 """Test that compress of device-tree files is supported""" 2640 self._CheckLz4() 2641 data = self.data = self._DoReadFileRealDtb('124_compress_dtb.dts') 2642 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)]) 2643 comp_data = data[len(U_BOOT_DATA):] 2644 orig = self._decompress(comp_data) 2645 dtb = fdt.Fdt.FromData(orig) 2646 dtb.Scan() 2647 props = self._GetPropTree(dtb, ['size', 'uncomp-size']) 2648 expected = { 2649 'u-boot:size': len(U_BOOT_DATA), 2650 'u-boot-dtb:uncomp-size': len(orig), 2651 'u-boot-dtb:size': len(comp_data), 2652 'size': len(data), 2653 } 2654 self.assertEqual(expected, props) 2655 2656 def testCbfsUpdateFdt(self): 2657 """Test that we can update the device tree with CBFS offset/size info""" 2658 self._CheckLz4() 2659 data, _, _, out_dtb_fname = self._DoReadFileDtb('125_cbfs_update.dts', 2660 update_dtb=True) 2661 dtb = fdt.Fdt(out_dtb_fname) 2662 dtb.Scan() 2663 props = self._GetPropTree(dtb, BASE_DTB_PROPS + ['uncomp-size']) 2664 del props['cbfs/u-boot:size'] 2665 self.assertEqual({ 2666 'offset': 0, 2667 'size': len(data), 2668 'image-pos': 0, 2669 'cbfs:offset': 0, 2670 'cbfs:size': len(data), 2671 'cbfs:image-pos': 0, 2672 'cbfs/u-boot:offset': 0x30, 2673 'cbfs/u-boot:uncomp-size': len(U_BOOT_DATA), 2674 'cbfs/u-boot:image-pos': 0x30, 2675 'cbfs/u-boot-dtb:offset': 0xa4, 2676 'cbfs/u-boot-dtb:size': len(U_BOOT_DATA), 2677 'cbfs/u-boot-dtb:image-pos': 0xa4, 2678 }, props) 2679 2680 def testCbfsBadType(self): 2681 """Test an image header with a no specified location is detected""" 2682 with self.assertRaises(ValueError) as e: 2683 self._DoReadFile('126_cbfs_bad_type.dts') 2684 self.assertIn("Unknown cbfs-type 'badtype'", str(e.exception)) 2685 2686 def testList(self): 2687 """Test listing the files in an image""" 2688 self._CheckLz4() 2689 data = self._DoReadFile('127_list.dts') 2690 image = control.images['image'] 2691 entries = image.BuildEntryList() 2692 self.assertEqual(7, len(entries)) 2693 2694 ent = entries[0] 2695 self.assertEqual(0, ent.indent) 2696 self.assertEqual('image', ent.name) 2697 self.assertEqual('section', ent.etype) 2698 self.assertEqual(len(data), ent.size) 2699 self.assertEqual(0, ent.image_pos) 2700 self.assertEqual(None, ent.uncomp_size) 2701 self.assertEqual(0, ent.offset) 2702 2703 ent = entries[1] 2704 self.assertEqual(1, ent.indent) 2705 self.assertEqual('u-boot', ent.name) 2706 self.assertEqual('u-boot', ent.etype) 2707 self.assertEqual(len(U_BOOT_DATA), ent.size) 2708 self.assertEqual(0, ent.image_pos) 2709 self.assertEqual(None, ent.uncomp_size) 2710 self.assertEqual(0, ent.offset) 2711 2712 ent = entries[2] 2713 self.assertEqual(1, ent.indent) 2714 self.assertEqual('section', ent.name) 2715 self.assertEqual('section', ent.etype) 2716 section_size = ent.size 2717 self.assertEqual(0x100, ent.image_pos) 2718 self.assertEqual(None, ent.uncomp_size) 2719 self.assertEqual(0x100, ent.offset) 2720 2721 ent = entries[3] 2722 self.assertEqual(2, ent.indent) 2723 self.assertEqual('cbfs', ent.name) 2724 self.assertEqual('cbfs', ent.etype) 2725 self.assertEqual(0x400, ent.size) 2726 self.assertEqual(0x100, ent.image_pos) 2727 self.assertEqual(None, ent.uncomp_size) 2728 self.assertEqual(0, ent.offset) 2729 2730 ent = entries[4] 2731 self.assertEqual(3, ent.indent) 2732 self.assertEqual('u-boot', ent.name) 2733 self.assertEqual('u-boot', ent.etype) 2734 self.assertEqual(len(U_BOOT_DATA), ent.size) 2735 self.assertEqual(0x138, ent.image_pos) 2736 self.assertEqual(None, ent.uncomp_size) 2737 self.assertEqual(0x38, ent.offset) 2738 2739 ent = entries[5] 2740 self.assertEqual(3, ent.indent) 2741 self.assertEqual('u-boot-dtb', ent.name) 2742 self.assertEqual('text', ent.etype) 2743 self.assertGreater(len(COMPRESS_DATA), ent.size) 2744 self.assertEqual(0x178, ent.image_pos) 2745 self.assertEqual(len(COMPRESS_DATA), ent.uncomp_size) 2746 self.assertEqual(0x78, ent.offset) 2747 2748 ent = entries[6] 2749 self.assertEqual(2, ent.indent) 2750 self.assertEqual('u-boot-dtb', ent.name) 2751 self.assertEqual('u-boot-dtb', ent.etype) 2752 self.assertEqual(0x500, ent.image_pos) 2753 self.assertEqual(len(U_BOOT_DTB_DATA), ent.uncomp_size) 2754 dtb_size = ent.size 2755 # Compressing this data expands it since headers are added 2756 self.assertGreater(dtb_size, len(U_BOOT_DTB_DATA)) 2757 self.assertEqual(0x400, ent.offset) 2758 2759 self.assertEqual(len(data), 0x100 + section_size) 2760 self.assertEqual(section_size, 0x400 + dtb_size) 2761 2762 def testFindFdtmap(self): 2763 """Test locating an FDT map in an image""" 2764 self._CheckLz4() 2765 data = self.data = self._DoReadFileRealDtb('128_decode_image.dts') 2766 image = control.images['image'] 2767 entries = image.GetEntries() 2768 entry = entries['fdtmap'] 2769 self.assertEqual(entry.image_pos, fdtmap.LocateFdtmap(data)) 2770 2771 def testFindFdtmapMissing(self): 2772 """Test failing to locate an FDP map""" 2773 data = self._DoReadFile('005_simple.dts') 2774 self.assertEqual(None, fdtmap.LocateFdtmap(data)) 2775 2776 def testFindImageHeader(self): 2777 """Test locating a image header""" 2778 self._CheckLz4() 2779 data = self.data = self._DoReadFileRealDtb('128_decode_image.dts') 2780 image = control.images['image'] 2781 entries = image.GetEntries() 2782 entry = entries['fdtmap'] 2783 # The header should point to the FDT map 2784 self.assertEqual(entry.image_pos, image_header.LocateHeaderOffset(data)) 2785 2786 def testFindImageHeaderStart(self): 2787 """Test locating a image header located at the start of an image""" 2788 data = self.data = self._DoReadFileRealDtb('117_fdtmap_hdr_start.dts') 2789 image = control.images['image'] 2790 entries = image.GetEntries() 2791 entry = entries['fdtmap'] 2792 # The header should point to the FDT map 2793 self.assertEqual(entry.image_pos, image_header.LocateHeaderOffset(data)) 2794 2795 def testFindImageHeaderMissing(self): 2796 """Test failing to locate an image header""" 2797 data = self._DoReadFile('005_simple.dts') 2798 self.assertEqual(None, image_header.LocateHeaderOffset(data)) 2799 2800 def testReadImage(self): 2801 """Test reading an image and accessing its FDT map""" 2802 self._CheckLz4() 2803 data = self.data = self._DoReadFileRealDtb('128_decode_image.dts') 2804 image_fname = tools.get_output_filename('image.bin') 2805 orig_image = control.images['image'] 2806 image = Image.FromFile(image_fname) 2807 self.assertEqual(orig_image.GetEntries().keys(), 2808 image.GetEntries().keys()) 2809 2810 orig_entry = orig_image.GetEntries()['fdtmap'] 2811 entry = image.GetEntries()['fdtmap'] 2812 self.assertEquals(orig_entry.offset, entry.offset) 2813 self.assertEquals(orig_entry.size, entry.size) 2814 self.assertEquals(orig_entry.image_pos, entry.image_pos) 2815 2816 def testReadImageNoHeader(self): 2817 """Test accessing an image's FDT map without an image header""" 2818 self._CheckLz4() 2819 data = self._DoReadFileRealDtb('129_decode_image_nohdr.dts') 2820 image_fname = tools.get_output_filename('image.bin') 2821 image = Image.FromFile(image_fname) 2822 self.assertTrue(isinstance(image, Image)) 2823 self.assertEqual('image', image.image_name[-5:]) 2824 2825 def testReadImageFail(self): 2826 """Test failing to read an image image's FDT map""" 2827 self._DoReadFile('005_simple.dts') 2828 image_fname = tools.get_output_filename('image.bin') 2829 with self.assertRaises(ValueError) as e: 2830 image = Image.FromFile(image_fname) 2831 self.assertIn("Cannot find FDT map in image", str(e.exception)) 2832 2833 def testListCmd(self): 2834 """Test listing the files in an image using an Fdtmap""" 2835 self._CheckLz4() 2836 data = self._DoReadFileRealDtb('130_list_fdtmap.dts') 2837 2838 # lz4 compression size differs depending on the version 2839 image = control.images['image'] 2840 entries = image.GetEntries() 2841 section_size = entries['section'].size 2842 fdt_size = entries['section'].GetEntries()['u-boot-dtb'].size 2843 fdtmap_offset = entries['fdtmap'].offset 2844 2845 tmpdir = None 2846 try: 2847 tmpdir, updated_fname = self._SetupImageInTmpdir() 2848 with test_util.capture_sys_output() as (stdout, stderr): 2849 self._DoBinman('ls', '-i', updated_fname) 2850 finally: 2851 if tmpdir: 2852 shutil.rmtree(tmpdir) 2853 lines = stdout.getvalue().splitlines() 2854 expected = [ 2855'Name Image-pos Size Entry-type Offset Uncomp-size', 2856'----------------------------------------------------------------------', 2857'image 0 c00 section 0', 2858' u-boot 0 4 u-boot 0', 2859' section 100 %x section 100' % section_size, 2860' cbfs 100 400 cbfs 0', 2861' u-boot 120 4 u-boot 20', 2862' u-boot-dtb 180 105 u-boot-dtb 80 3c9', 2863' u-boot-dtb 500 %x u-boot-dtb 400 3c9' % fdt_size, 2864' fdtmap %x 3bd fdtmap %x' % 2865 (fdtmap_offset, fdtmap_offset), 2866' image-header bf8 8 image-header bf8', 2867 ] 2868 self.assertEqual(expected, lines) 2869 2870 def testListCmdFail(self): 2871 """Test failing to list an image""" 2872 self._DoReadFile('005_simple.dts') 2873 tmpdir = None 2874 try: 2875 tmpdir, updated_fname = self._SetupImageInTmpdir() 2876 with self.assertRaises(ValueError) as e: 2877 self._DoBinman('ls', '-i', updated_fname) 2878 finally: 2879 if tmpdir: 2880 shutil.rmtree(tmpdir) 2881 self.assertIn("Cannot find FDT map in image", str(e.exception)) 2882 2883 def _RunListCmd(self, paths, expected): 2884 """List out entries and check the result 2885 2886 Args: 2887 paths: List of paths to pass to the list command 2888 expected: Expected list of filenames to be returned, in order 2889 """ 2890 self._CheckLz4() 2891 self._DoReadFileRealDtb('130_list_fdtmap.dts') 2892 image_fname = tools.get_output_filename('image.bin') 2893 image = Image.FromFile(image_fname) 2894 lines = image.GetListEntries(paths)[1] 2895 files = [line[0].strip() for line in lines[1:]] 2896 self.assertEqual(expected, files) 2897 2898 def testListCmdSection(self): 2899 """Test listing the files in a section""" 2900 self._RunListCmd(['section'], 2901 ['section', 'cbfs', 'u-boot', 'u-boot-dtb', 'u-boot-dtb']) 2902 2903 def testListCmdFile(self): 2904 """Test listing a particular file""" 2905 self._RunListCmd(['*u-boot-dtb'], ['u-boot-dtb', 'u-boot-dtb']) 2906 2907 def testListCmdWildcard(self): 2908 """Test listing a wildcarded file""" 2909 self._RunListCmd(['*boot*'], 2910 ['u-boot', 'u-boot', 'u-boot-dtb', 'u-boot-dtb']) 2911 2912 def testListCmdWildcardMulti(self): 2913 """Test listing a wildcarded file""" 2914 self._RunListCmd(['*cb*', '*head*'], 2915 ['cbfs', 'u-boot', 'u-boot-dtb', 'image-header']) 2916 2917 def testListCmdEmpty(self): 2918 """Test listing a wildcarded file""" 2919 self._RunListCmd(['nothing'], []) 2920 2921 def testListCmdPath(self): 2922 """Test listing the files in a sub-entry of a section""" 2923 self._RunListCmd(['section/cbfs'], ['cbfs', 'u-boot', 'u-boot-dtb']) 2924 2925 def _RunExtractCmd(self, entry_name, decomp=True): 2926 """Extract an entry from an image 2927 2928 Args: 2929 entry_name: Entry name to extract 2930 decomp: True to decompress the data if compressed, False to leave 2931 it in its raw uncompressed format 2932 2933 Returns: 2934 data from entry 2935 """ 2936 self._CheckLz4() 2937 self._DoReadFileRealDtb('130_list_fdtmap.dts') 2938 image_fname = tools.get_output_filename('image.bin') 2939 return control.ReadEntry(image_fname, entry_name, decomp) 2940 2941 def testExtractSimple(self): 2942 """Test extracting a single file""" 2943 data = self._RunExtractCmd('u-boot') 2944 self.assertEqual(U_BOOT_DATA, data) 2945 2946 def testExtractSection(self): 2947 """Test extracting the files in a section""" 2948 data = self._RunExtractCmd('section') 2949 cbfs_data = data[:0x400] 2950 cbfs = cbfs_util.CbfsReader(cbfs_data) 2951 self.assertEqual(['u-boot', 'u-boot-dtb', ''], list(cbfs.files.keys())) 2952 dtb_data = data[0x400:] 2953 dtb = self._decompress(dtb_data) 2954 self.assertEqual(EXTRACT_DTB_SIZE, len(dtb)) 2955 2956 def testExtractCompressed(self): 2957 """Test extracting compressed data""" 2958 data = self._RunExtractCmd('section/u-boot-dtb') 2959 self.assertEqual(EXTRACT_DTB_SIZE, len(data)) 2960 2961 def testExtractRaw(self): 2962 """Test extracting compressed data without decompressing it""" 2963 data = self._RunExtractCmd('section/u-boot-dtb', decomp=False) 2964 dtb = self._decompress(data) 2965 self.assertEqual(EXTRACT_DTB_SIZE, len(dtb)) 2966 2967 def testExtractCbfs(self): 2968 """Test extracting CBFS data""" 2969 data = self._RunExtractCmd('section/cbfs/u-boot') 2970 self.assertEqual(U_BOOT_DATA, data) 2971 2972 def testExtractCbfsCompressed(self): 2973 """Test extracting CBFS compressed data""" 2974 data = self._RunExtractCmd('section/cbfs/u-boot-dtb') 2975 self.assertEqual(EXTRACT_DTB_SIZE, len(data)) 2976 2977 def testExtractCbfsRaw(self): 2978 """Test extracting CBFS compressed data without decompressing it""" 2979 bintool = self.comp_bintools['lzma_alone'] 2980 self._CheckBintool(bintool) 2981 data = self._RunExtractCmd('section/cbfs/u-boot-dtb', decomp=False) 2982 dtb = bintool.decompress(data) 2983 self.assertEqual(EXTRACT_DTB_SIZE, len(dtb)) 2984 2985 def testExtractBadEntry(self): 2986 """Test extracting a bad section path""" 2987 with self.assertRaises(ValueError) as e: 2988 self._RunExtractCmd('section/does-not-exist') 2989 self.assertIn("Entry 'does-not-exist' not found in '/section'", 2990 str(e.exception)) 2991 2992 def testExtractMissingFile(self): 2993 """Test extracting file that does not exist""" 2994 with self.assertRaises(IOError) as e: 2995 control.ReadEntry('missing-file', 'name') 2996 2997 def testExtractBadFile(self): 2998 """Test extracting an invalid file""" 2999 fname = os.path.join(self._indir, 'badfile') 3000 tools.write_file(fname, b'') 3001 with self.assertRaises(ValueError) as e: 3002 control.ReadEntry(fname, 'name') 3003 3004 def testExtractCmd(self): 3005 """Test extracting a file fron an image on the command line""" 3006 self._CheckLz4() 3007 self._DoReadFileRealDtb('130_list_fdtmap.dts') 3008 fname = os.path.join(self._indir, 'output.extact') 3009 tmpdir = None 3010 try: 3011 tmpdir, updated_fname = self._SetupImageInTmpdir() 3012 with test_util.capture_sys_output() as (stdout, stderr): 3013 self._DoBinman('extract', '-i', updated_fname, 'u-boot', 3014 '-f', fname) 3015 finally: 3016 if tmpdir: 3017 shutil.rmtree(tmpdir) 3018 data = tools.read_file(fname) 3019 self.assertEqual(U_BOOT_DATA, data) 3020 3021 def testExtractOneEntry(self): 3022 """Test extracting a single entry fron an image """ 3023 self._CheckLz4() 3024 self._DoReadFileRealDtb('130_list_fdtmap.dts') 3025 image_fname = tools.get_output_filename('image.bin') 3026 fname = os.path.join(self._indir, 'output.extact') 3027 control.ExtractEntries(image_fname, fname, None, ['u-boot']) 3028 data = tools.read_file(fname) 3029 self.assertEqual(U_BOOT_DATA, data) 3030 3031 def _CheckExtractOutput(self, decomp): 3032 """Helper to test file output with and without decompression 3033 3034 Args: 3035 decomp: True to decompress entry data, False to output it raw 3036 """ 3037 def _CheckPresent(entry_path, expect_data, expect_size=None): 3038 """Check and remove expected file 3039 3040 This checks the data/size of a file and removes the file both from 3041 the outfiles set and from the output directory. Once all files are 3042 processed, both the set and directory should be empty. 3043 3044 Args: 3045 entry_path: Entry path 3046 expect_data: Data to expect in file, or None to skip check 3047 expect_size: Size of data to expect in file, or None to skip 3048 """ 3049 path = os.path.join(outdir, entry_path) 3050 data = tools.read_file(path) 3051 os.remove(path) 3052 if expect_data: 3053 self.assertEqual(expect_data, data) 3054 elif expect_size: 3055 self.assertEqual(expect_size, len(data)) 3056 outfiles.remove(path) 3057 3058 def _CheckDirPresent(name): 3059 """Remove expected directory 3060 3061 This gives an error if the directory does not exist as expected 3062 3063 Args: 3064 name: Name of directory to remove 3065 """ 3066 path = os.path.join(outdir, name) 3067 os.rmdir(path) 3068 3069 self._DoReadFileRealDtb('130_list_fdtmap.dts') 3070 image_fname = tools.get_output_filename('image.bin') 3071 outdir = os.path.join(self._indir, 'extract') 3072 einfos = control.ExtractEntries(image_fname, None, outdir, [], decomp) 3073 3074 # Create a set of all file that were output (should be 9) 3075 outfiles = set() 3076 for root, dirs, files in os.walk(outdir): 3077 outfiles |= set([os.path.join(root, fname) for fname in files]) 3078 self.assertEqual(9, len(outfiles)) 3079 self.assertEqual(9, len(einfos)) 3080 3081 image = control.images['image'] 3082 entries = image.GetEntries() 3083 3084 # Check the 9 files in various ways 3085 section = entries['section'] 3086 section_entries = section.GetEntries() 3087 cbfs_entries = section_entries['cbfs'].GetEntries() 3088 _CheckPresent('u-boot', U_BOOT_DATA) 3089 _CheckPresent('section/cbfs/u-boot', U_BOOT_DATA) 3090 dtb_len = EXTRACT_DTB_SIZE 3091 if not decomp: 3092 dtb_len = cbfs_entries['u-boot-dtb'].size 3093 _CheckPresent('section/cbfs/u-boot-dtb', None, dtb_len) 3094 if not decomp: 3095 dtb_len = section_entries['u-boot-dtb'].size 3096 _CheckPresent('section/u-boot-dtb', None, dtb_len) 3097 3098 fdtmap = entries['fdtmap'] 3099 _CheckPresent('fdtmap', fdtmap.data) 3100 hdr = entries['image-header'] 3101 _CheckPresent('image-header', hdr.data) 3102 3103 _CheckPresent('section/root', section.data) 3104 cbfs = section_entries['cbfs'] 3105 _CheckPresent('section/cbfs/root', cbfs.data) 3106 data = tools.read_file(image_fname) 3107 _CheckPresent('root', data) 3108 3109 # There should be no files left. Remove all the directories to check. 3110 # If there are any files/dirs remaining, one of these checks will fail. 3111 self.assertEqual(0, len(outfiles)) 3112 _CheckDirPresent('section/cbfs') 3113 _CheckDirPresent('section') 3114 _CheckDirPresent('') 3115 self.assertFalse(os.path.exists(outdir)) 3116 3117 def testExtractAllEntries(self): 3118 """Test extracting all entries""" 3119 self._CheckLz4() 3120 self._CheckExtractOutput(decomp=True) 3121 3122 def testExtractAllEntriesRaw(self): 3123 """Test extracting all entries without decompressing them""" 3124 self._CheckLz4() 3125 self._CheckExtractOutput(decomp=False) 3126 3127 def testExtractSelectedEntries(self): 3128 """Test extracting some entries""" 3129 self._CheckLz4() 3130 self._DoReadFileRealDtb('130_list_fdtmap.dts') 3131 image_fname = tools.get_output_filename('image.bin') 3132 outdir = os.path.join(self._indir, 'extract') 3133 einfos = control.ExtractEntries(image_fname, None, outdir, 3134 ['*cb*', '*head*']) 3135 3136 # File output is tested by testExtractAllEntries(), so just check that 3137 # the expected entries are selected 3138 names = [einfo.name for einfo in einfos] 3139 self.assertEqual(names, 3140 ['cbfs', 'u-boot', 'u-boot-dtb', 'image-header']) 3141 3142 def testExtractNoEntryPaths(self): 3143 """Test extracting some entries""" 3144 self._CheckLz4() 3145 self._DoReadFileRealDtb('130_list_fdtmap.dts') 3146 image_fname = tools.get_output_filename('image.bin') 3147 with self.assertRaises(ValueError) as e: 3148 control.ExtractEntries(image_fname, 'fname', None, []) 3149 self.assertIn('Must specify an entry path to write with -f', 3150 str(e.exception)) 3151 3152 def testExtractTooManyEntryPaths(self): 3153 """Test extracting some entries""" 3154 self._CheckLz4() 3155 self._DoReadFileRealDtb('130_list_fdtmap.dts') 3156 image_fname = tools.get_output_filename('image.bin') 3157 with self.assertRaises(ValueError) as e: 3158 control.ExtractEntries(image_fname, 'fname', None, ['a', 'b']) 3159 self.assertIn('Must specify exactly one entry path to write with -f', 3160 str(e.exception)) 3161 3162 def testPackAlignSection(self): 3163 """Test that sections can have alignment""" 3164 self._DoReadFile('131_pack_align_section.dts') 3165 3166 self.assertIn('image', control.images) 3167 image = control.images['image'] 3168 entries = image.GetEntries() 3169 self.assertEqual(3, len(entries)) 3170 3171 # First u-boot 3172 self.assertIn('u-boot', entries) 3173 entry = entries['u-boot'] 3174 self.assertEqual(0, entry.offset) 3175 self.assertEqual(0, entry.image_pos) 3176 self.assertEqual(len(U_BOOT_DATA), entry.contents_size) 3177 self.assertEqual(len(U_BOOT_DATA), entry.size) 3178 3179 # Section0 3180 self.assertIn('section0', entries) 3181 section0 = entries['section0'] 3182 self.assertEqual(0x10, section0.offset) 3183 self.assertEqual(0x10, section0.image_pos) 3184 self.assertEqual(len(U_BOOT_DATA), section0.size) 3185 3186 # Second u-boot 3187 section_entries = section0.GetEntries() 3188 self.assertIn('u-boot', section_entries) 3189 entry = section_entries['u-boot'] 3190 self.assertEqual(0, entry.offset) 3191 self.assertEqual(0x10, entry.image_pos) 3192 self.assertEqual(len(U_BOOT_DATA), entry.contents_size) 3193 self.assertEqual(len(U_BOOT_DATA), entry.size) 3194 3195 # Section1 3196 self.assertIn('section1', entries) 3197 section1 = entries['section1'] 3198 self.assertEqual(0x14, section1.offset) 3199 self.assertEqual(0x14, section1.image_pos) 3200 self.assertEqual(0x20, section1.size) 3201 3202 # Second u-boot 3203 section_entries = section1.GetEntries() 3204 self.assertIn('u-boot', section_entries) 3205 entry = section_entries['u-boot'] 3206 self.assertEqual(0, entry.offset) 3207 self.assertEqual(0x14, entry.image_pos) 3208 self.assertEqual(len(U_BOOT_DATA), entry.contents_size) 3209 self.assertEqual(len(U_BOOT_DATA), entry.size) 3210 3211 # Section2 3212 self.assertIn('section2', section_entries) 3213 section2 = section_entries['section2'] 3214 self.assertEqual(0x4, section2.offset) 3215 self.assertEqual(0x18, section2.image_pos) 3216 self.assertEqual(4, section2.size) 3217 3218 # Third u-boot 3219 section_entries = section2.GetEntries() 3220 self.assertIn('u-boot', section_entries) 3221 entry = section_entries['u-boot'] 3222 self.assertEqual(0, entry.offset) 3223 self.assertEqual(0x18, entry.image_pos) 3224 self.assertEqual(len(U_BOOT_DATA), entry.contents_size) 3225 self.assertEqual(len(U_BOOT_DATA), entry.size) 3226 3227 def _RunReplaceCmd(self, entry_name, data, decomp=True, allow_resize=True, 3228 dts='132_replace.dts'): 3229 """Replace an entry in an image 3230 3231 This writes the entry data to update it, then opens the updated file and 3232 returns the value that it now finds there. 3233 3234 Args: 3235 entry_name: Entry name to replace 3236 data: Data to replace it with 3237 decomp: True to compress the data if needed, False if data is 3238 already compressed so should be used as is 3239 allow_resize: True to allow entries to change size, False to raise 3240 an exception 3241 3242 Returns: 3243 Tuple: 3244 data from entry 3245 data from fdtmap (excluding header) 3246 Image object that was modified 3247 """ 3248 dtb_data = self._DoReadFileDtb(dts, use_real_dtb=True, 3249 update_dtb=True)[1] 3250 3251 self.assertIn('image', control.images) 3252 image = control.images['image'] 3253 entries = image.GetEntries() 3254 orig_dtb_data = entries['u-boot-dtb'].data 3255 orig_fdtmap_data = entries['fdtmap'].data 3256 3257 image_fname = tools.get_output_filename('image.bin') 3258 updated_fname = tools.get_output_filename('image-updated.bin') 3259 tools.write_file(updated_fname, tools.read_file(image_fname)) 3260 image = control.WriteEntry(updated_fname, entry_name, data, decomp, 3261 allow_resize) 3262 data = control.ReadEntry(updated_fname, entry_name, decomp) 3263 3264 # The DT data should not change unless resized: 3265 if not allow_resize: 3266 new_dtb_data = entries['u-boot-dtb'].data 3267 self.assertEqual(new_dtb_data, orig_dtb_data) 3268 new_fdtmap_data = entries['fdtmap'].data 3269 self.assertEqual(new_fdtmap_data, orig_fdtmap_data) 3270 3271 return data, orig_fdtmap_data[fdtmap.FDTMAP_HDR_LEN:], image 3272 3273 def testReplaceSimple(self): 3274 """Test replacing a single file""" 3275 expected = b'x' * len(U_BOOT_DATA) 3276 data, expected_fdtmap, _ = self._RunReplaceCmd('u-boot', expected, 3277 allow_resize=False) 3278 self.assertEqual(expected, data) 3279 3280 # Test that the state looks right. There should be an FDT for the fdtmap 3281 # that we jsut read back in, and it should match what we find in the 3282 # 'control' tables. Checking for an FDT that does not exist should 3283 # return None. 3284 path, fdtmap = state.GetFdtContents('fdtmap') 3285 self.assertIsNotNone(path) 3286 self.assertEqual(expected_fdtmap, fdtmap) 3287 3288 dtb = state.GetFdtForEtype('fdtmap') 3289 self.assertEqual(dtb.GetContents(), fdtmap) 3290 3291 missing_path, missing_fdtmap = state.GetFdtContents('missing') 3292 self.assertIsNone(missing_path) 3293 self.assertIsNone(missing_fdtmap) 3294 3295 missing_dtb = state.GetFdtForEtype('missing') 3296 self.assertIsNone(missing_dtb) 3297 3298 self.assertEqual('/binman', state.fdt_path_prefix) 3299 3300 def testReplaceResizeFail(self): 3301 """Test replacing a file by something larger""" 3302 expected = U_BOOT_DATA + b'x' 3303 with self.assertRaises(ValueError) as e: 3304 self._RunReplaceCmd('u-boot', expected, allow_resize=False, 3305 dts='139_replace_repack.dts') 3306 self.assertIn("Node '/u-boot': Entry data size does not match, but resize is disabled", 3307 str(e.exception)) 3308 3309 def testReplaceMulti(self): 3310 """Test replacing entry data where multiple images are generated""" 3311 data = self._DoReadFileDtb('133_replace_multi.dts', use_real_dtb=True, 3312 update_dtb=True)[0] 3313 expected = b'x' * len(U_BOOT_DATA) 3314 updated_fname = tools.get_output_filename('image-updated.bin') 3315 tools.write_file(updated_fname, data) 3316 entry_name = 'u-boot' 3317 control.WriteEntry(updated_fname, entry_name, expected, 3318 allow_resize=False) 3319 data = control.ReadEntry(updated_fname, entry_name) 3320 self.assertEqual(expected, data) 3321 3322 # Check the state looks right. 3323 self.assertEqual('/binman/image', state.fdt_path_prefix) 3324 3325 # Now check we can write the first image 3326 image_fname = tools.get_output_filename('first-image.bin') 3327 updated_fname = tools.get_output_filename('first-updated.bin') 3328 tools.write_file(updated_fname, tools.read_file(image_fname)) 3329 entry_name = 'u-boot' 3330 control.WriteEntry(updated_fname, entry_name, expected, 3331 allow_resize=False) 3332 data = control.ReadEntry(updated_fname, entry_name) 3333 self.assertEqual(expected, data) 3334 3335 # Check the state looks right. 3336 self.assertEqual('/binman/first-image', state.fdt_path_prefix) 3337 3338 def testUpdateFdtAllRepack(self): 3339 """Test that all device trees are updated with offset/size info""" 3340 self._SetupSplElf() 3341 self._SetupTplElf() 3342 data = self._DoReadFileRealDtb('134_fdt_update_all_repack.dts') 3343 SECTION_SIZE = 0x300 3344 DTB_SIZE = 602 3345 FDTMAP_SIZE = 608 3346 base_expected = { 3347 'offset': 0, 3348 'size': SECTION_SIZE + DTB_SIZE * 2 + FDTMAP_SIZE, 3349 'image-pos': 0, 3350 'section:offset': 0, 3351 'section:size': SECTION_SIZE, 3352 'section:image-pos': 0, 3353 'section/u-boot-dtb:offset': 4, 3354 'section/u-boot-dtb:size': 636, 3355 'section/u-boot-dtb:image-pos': 4, 3356 'u-boot-spl-dtb:offset': SECTION_SIZE, 3357 'u-boot-spl-dtb:size': DTB_SIZE, 3358 'u-boot-spl-dtb:image-pos': SECTION_SIZE, 3359 'u-boot-tpl-dtb:offset': SECTION_SIZE + DTB_SIZE, 3360 'u-boot-tpl-dtb:image-pos': SECTION_SIZE + DTB_SIZE, 3361 'u-boot-tpl-dtb:size': DTB_SIZE, 3362 'fdtmap:offset': SECTION_SIZE + DTB_SIZE * 2, 3363 'fdtmap:size': FDTMAP_SIZE, 3364 'fdtmap:image-pos': SECTION_SIZE + DTB_SIZE * 2, 3365 } 3366 main_expected = { 3367 'section:orig-size': SECTION_SIZE, 3368 'section/u-boot-dtb:orig-offset': 4, 3369 } 3370 3371 # We expect three device-tree files in the output, with the first one 3372 # within a fixed-size section. 3373 # Read them in sequence. We look for an 'spl' property in the SPL tree, 3374 # and 'tpl' in the TPL tree, to make sure they are distinct from the 3375 # main U-Boot tree. All three should have the same positions and offset 3376 # except that the main tree should include the main_expected properties 3377 start = 4 3378 for item in ['', 'spl', 'tpl', None]: 3379 if item is None: 3380 start += 16 # Move past fdtmap header 3381 dtb = fdt.Fdt.FromData(data[start:]) 3382 dtb.Scan() 3383 props = self._GetPropTree(dtb, 3384 BASE_DTB_PROPS + REPACK_DTB_PROPS + ['spl', 'tpl'], 3385 prefix='/' if item is None else '/binman/') 3386 expected = dict(base_expected) 3387 if item: 3388 expected[item] = 0 3389 else: 3390 # Main DTB and fdtdec should include the 'orig-' properties 3391 expected.update(main_expected) 3392 # Helpful for debugging: 3393 #for prop in sorted(props): 3394 #print('prop %s %s %s' % (prop, props[prop], expected[prop])) 3395 self.assertEqual(expected, props) 3396 if item == '': 3397 start = SECTION_SIZE 3398 else: 3399 start += dtb._fdt_obj.totalsize() 3400 3401 def testFdtmapHeaderMiddle(self): 3402 """Test an FDT map in the middle of an image when it should be at end""" 3403 with self.assertRaises(ValueError) as e: 3404 self._DoReadFileRealDtb('135_fdtmap_hdr_middle.dts') 3405 self.assertIn("Invalid sibling order 'middle' for image-header: Must be at 'end' to match location", 3406 str(e.exception)) 3407 3408 def testFdtmapHeaderStartBad(self): 3409 """Test an FDT map in middle of an image when it should be at start""" 3410 with self.assertRaises(ValueError) as e: 3411 self._DoReadFileRealDtb('136_fdtmap_hdr_startbad.dts') 3412 self.assertIn("Invalid sibling order 'end' for image-header: Must be at 'start' to match location", 3413 str(e.exception)) 3414 3415 def testFdtmapHeaderEndBad(self): 3416 """Test an FDT map at the start of an image when it should be at end""" 3417 with self.assertRaises(ValueError) as e: 3418 self._DoReadFileRealDtb('137_fdtmap_hdr_endbad.dts') 3419 self.assertIn("Invalid sibling order 'start' for image-header: Must be at 'end' to match location", 3420 str(e.exception)) 3421 3422 def testFdtmapHeaderNoSize(self): 3423 """Test an image header at the end of an image with undefined size""" 3424 self._DoReadFileRealDtb('138_fdtmap_hdr_nosize.dts') 3425 3426 def testReplaceResize(self): 3427 """Test replacing a single file in an entry with a larger file""" 3428 expected = U_BOOT_DATA + b'x' 3429 data, _, image = self._RunReplaceCmd('u-boot', expected, 3430 dts='139_replace_repack.dts') 3431 self.assertEqual(expected, data) 3432 3433 entries = image.GetEntries() 3434 dtb_data = entries['u-boot-dtb'].data 3435 dtb = fdt.Fdt.FromData(dtb_data) 3436 dtb.Scan() 3437 3438 # The u-boot section should now be larger in the dtb 3439 node = dtb.GetNode('/binman/u-boot') 3440 self.assertEqual(len(expected), fdt_util.GetInt(node, 'size')) 3441 3442 # Same for the fdtmap 3443 fdata = entries['fdtmap'].data 3444 fdtb = fdt.Fdt.FromData(fdata[fdtmap.FDTMAP_HDR_LEN:]) 3445 fdtb.Scan() 3446 fnode = fdtb.GetNode('/u-boot') 3447 self.assertEqual(len(expected), fdt_util.GetInt(fnode, 'size')) 3448 3449 def testReplaceResizeNoRepack(self): 3450 """Test replacing an entry with a larger file when not allowed""" 3451 expected = U_BOOT_DATA + b'x' 3452 with self.assertRaises(ValueError) as e: 3453 self._RunReplaceCmd('u-boot', expected) 3454 self.assertIn('Entry data size does not match, but allow-repack is not present for this image', 3455 str(e.exception)) 3456 3457 def testEntryShrink(self): 3458 """Test contracting an entry after it is packed""" 3459 try: 3460 state.SetAllowEntryContraction(True) 3461 data = self._DoReadFileDtb('140_entry_shrink.dts', 3462 update_dtb=True)[0] 3463 finally: 3464 state.SetAllowEntryContraction(False) 3465 self.assertEqual(b'a', data[:1]) 3466 self.assertEqual(U_BOOT_DATA, data[1:1 + len(U_BOOT_DATA)]) 3467 self.assertEqual(b'a', data[-1:]) 3468 3469 def testEntryShrinkFail(self): 3470 """Test not being allowed to contract an entry after it is packed""" 3471 data = self._DoReadFileDtb('140_entry_shrink.dts', update_dtb=True)[0] 3472 3473 # In this case there is a spare byte at the end of the data. The size of 3474 # the contents is only 1 byte but we still have the size before it 3475 # shrunk. 3476 self.assertEqual(b'a\0', data[:2]) 3477 self.assertEqual(U_BOOT_DATA, data[2:2 + len(U_BOOT_DATA)]) 3478 self.assertEqual(b'a\0', data[-2:]) 3479 3480 def testDescriptorOffset(self): 3481 """Test that the Intel descriptor is always placed at at the start""" 3482 data = self._DoReadFileDtb('141_descriptor_offset.dts') 3483 image = control.images['image'] 3484 entries = image.GetEntries() 3485 desc = entries['intel-descriptor'] 3486 self.assertEqual(0xff800000, desc.offset); 3487 self.assertEqual(0xff800000, desc.image_pos); 3488 3489 def testReplaceCbfs(self): 3490 """Test replacing a single file in CBFS without changing the size""" 3491 self._CheckLz4() 3492 expected = b'x' * len(U_BOOT_DATA) 3493 data = self._DoReadFileRealDtb('142_replace_cbfs.dts') 3494 updated_fname = tools.get_output_filename('image-updated.bin') 3495 tools.write_file(updated_fname, data) 3496 entry_name = 'section/cbfs/u-boot' 3497 control.WriteEntry(updated_fname, entry_name, expected, 3498 allow_resize=True) 3499 data = control.ReadEntry(updated_fname, entry_name) 3500 self.assertEqual(expected, data) 3501 3502 def testReplaceResizeCbfs(self): 3503 """Test replacing a single file in CBFS with one of a different size""" 3504 self._CheckLz4() 3505 expected = U_BOOT_DATA + b'x' 3506 data = self._DoReadFileRealDtb('142_replace_cbfs.dts') 3507 updated_fname = tools.get_output_filename('image-updated.bin') 3508 tools.write_file(updated_fname, data) 3509 entry_name = 'section/cbfs/u-boot' 3510 control.WriteEntry(updated_fname, entry_name, expected, 3511 allow_resize=True) 3512 data = control.ReadEntry(updated_fname, entry_name) 3513 self.assertEqual(expected, data) 3514 3515 def _SetupForReplace(self): 3516 """Set up some files to use to replace entries 3517 3518 This generates an image, copies it to a new file, extracts all the files 3519 in it and updates some of them 3520 3521 Returns: 3522 List 3523 Image filename 3524 Output directory 3525 Expected values for updated entries, each a string 3526 """ 3527 data = self._DoReadFileRealDtb('143_replace_all.dts') 3528 3529 updated_fname = tools.get_output_filename('image-updated.bin') 3530 tools.write_file(updated_fname, data) 3531 3532 outdir = os.path.join(self._indir, 'extract') 3533 einfos = control.ExtractEntries(updated_fname, None, outdir, []) 3534 3535 expected1 = b'x' + U_BOOT_DATA + b'y' 3536 u_boot_fname1 = os.path.join(outdir, 'u-boot') 3537 tools.write_file(u_boot_fname1, expected1) 3538 3539 expected2 = b'a' + U_BOOT_DATA + b'b' 3540 u_boot_fname2 = os.path.join(outdir, 'u-boot2') 3541 tools.write_file(u_boot_fname2, expected2) 3542 3543 expected_text = b'not the same text' 3544 text_fname = os.path.join(outdir, 'text') 3545 tools.write_file(text_fname, expected_text) 3546 3547 dtb_fname = os.path.join(outdir, 'u-boot-dtb') 3548 dtb = fdt.FdtScan(dtb_fname) 3549 node = dtb.GetNode('/binman/text') 3550 node.AddString('my-property', 'the value') 3551 dtb.Sync(auto_resize=True) 3552 dtb.Flush() 3553 3554 return updated_fname, outdir, expected1, expected2, expected_text 3555 3556 def _CheckReplaceMultiple(self, entry_paths): 3557 """Handle replacing the contents of multiple entries 3558 3559 Args: 3560 entry_paths: List of entry paths to replace 3561 3562 Returns: 3563 List 3564 Dict of entries in the image: 3565 key: Entry name 3566 Value: Entry object 3567 Expected values for updated entries, each a string 3568 """ 3569 updated_fname, outdir, expected1, expected2, expected_text = ( 3570 self._SetupForReplace()) 3571 control.ReplaceEntries(updated_fname, None, outdir, entry_paths) 3572 3573 image = Image.FromFile(updated_fname) 3574 image.LoadData() 3575 return image.GetEntries(), expected1, expected2, expected_text 3576 3577 def testReplaceAll(self): 3578 """Test replacing the contents of all entries""" 3579 entries, expected1, expected2, expected_text = ( 3580 self._CheckReplaceMultiple([])) 3581 data = entries['u-boot'].data 3582 self.assertEqual(expected1, data) 3583 3584 data = entries['u-boot2'].data 3585 self.assertEqual(expected2, data) 3586 3587 data = entries['text'].data 3588 self.assertEqual(expected_text, data) 3589 3590 # Check that the device tree is updated 3591 data = entries['u-boot-dtb'].data 3592 dtb = fdt.Fdt.FromData(data) 3593 dtb.Scan() 3594 node = dtb.GetNode('/binman/text') 3595 self.assertEqual('the value', node.props['my-property'].value) 3596 3597 def testReplaceSome(self): 3598 """Test replacing the contents of a few entries""" 3599 entries, expected1, expected2, expected_text = ( 3600 self._CheckReplaceMultiple(['u-boot2', 'text'])) 3601 3602 # This one should not change 3603 data = entries['u-boot'].data 3604 self.assertEqual(U_BOOT_DATA, data) 3605 3606 data = entries['u-boot2'].data 3607 self.assertEqual(expected2, data) 3608 3609 data = entries['text'].data 3610 self.assertEqual(expected_text, data) 3611 3612 def testReplaceCmd(self): 3613 """Test replacing a file fron an image on the command line""" 3614 self._DoReadFileRealDtb('143_replace_all.dts') 3615 3616 try: 3617 tmpdir, updated_fname = self._SetupImageInTmpdir() 3618 3619 fname = os.path.join(tmpdir, 'update-u-boot.bin') 3620 expected = b'x' * len(U_BOOT_DATA) 3621 tools.write_file(fname, expected) 3622 3623 self._DoBinman('replace', '-i', updated_fname, 'u-boot', '-f', fname) 3624 data = tools.read_file(updated_fname) 3625 self.assertEqual(expected, data[:len(expected)]) 3626 map_fname = os.path.join(tmpdir, 'image-updated.map') 3627 self.assertFalse(os.path.exists(map_fname)) 3628 finally: 3629 shutil.rmtree(tmpdir) 3630 3631 def testReplaceCmdSome(self): 3632 """Test replacing some files fron an image on the command line""" 3633 updated_fname, outdir, expected1, expected2, expected_text = ( 3634 self._SetupForReplace()) 3635 3636 self._DoBinman('replace', '-i', updated_fname, '-I', outdir, 3637 'u-boot2', 'text') 3638 3639 tools.prepare_output_dir(None) 3640 image = Image.FromFile(updated_fname) 3641 image.LoadData() 3642 entries = image.GetEntries() 3643 3644 # This one should not change 3645 data = entries['u-boot'].data 3646 self.assertEqual(U_BOOT_DATA, data) 3647 3648 data = entries['u-boot2'].data 3649 self.assertEqual(expected2, data) 3650 3651 data = entries['text'].data 3652 self.assertEqual(expected_text, data) 3653 3654 def testReplaceMissing(self): 3655 """Test replacing entries where the file is missing""" 3656 updated_fname, outdir, expected1, expected2, expected_text = ( 3657 self._SetupForReplace()) 3658 3659 # Remove one of the files, to generate a warning 3660 u_boot_fname1 = os.path.join(outdir, 'u-boot') 3661 os.remove(u_boot_fname1) 3662 3663 with test_util.capture_sys_output() as (stdout, stderr): 3664 control.ReplaceEntries(updated_fname, None, outdir, []) 3665 self.assertIn("Skipping entry '/u-boot' from missing file", 3666 stderr.getvalue()) 3667 3668 def testReplaceCmdMap(self): 3669 """Test replacing a file fron an image on the command line""" 3670 self._DoReadFileRealDtb('143_replace_all.dts') 3671 3672 try: 3673 tmpdir, updated_fname = self._SetupImageInTmpdir() 3674 3675 fname = os.path.join(self._indir, 'update-u-boot.bin') 3676 expected = b'x' * len(U_BOOT_DATA) 3677 tools.write_file(fname, expected) 3678 3679 self._DoBinman('replace', '-i', updated_fname, 'u-boot', 3680 '-f', fname, '-m') 3681 map_fname = os.path.join(tmpdir, 'image-updated.map') 3682 self.assertTrue(os.path.exists(map_fname)) 3683 finally: 3684 shutil.rmtree(tmpdir) 3685 3686 def testReplaceNoEntryPaths(self): 3687 """Test replacing an entry without an entry path""" 3688 self._DoReadFileRealDtb('143_replace_all.dts') 3689 image_fname = tools.get_output_filename('image.bin') 3690 with self.assertRaises(ValueError) as e: 3691 control.ReplaceEntries(image_fname, 'fname', None, []) 3692 self.assertIn('Must specify an entry path to read with -f', 3693 str(e.exception)) 3694 3695 def testReplaceTooManyEntryPaths(self): 3696 """Test extracting some entries""" 3697 self._DoReadFileRealDtb('143_replace_all.dts') 3698 image_fname = tools.get_output_filename('image.bin') 3699 with self.assertRaises(ValueError) as e: 3700 control.ReplaceEntries(image_fname, 'fname', None, ['a', 'b']) 3701 self.assertIn('Must specify exactly one entry path to write with -f', 3702 str(e.exception)) 3703 3704 def testPackReset16(self): 3705 """Test that an image with an x86 reset16 region can be created""" 3706 data = self._DoReadFile('144_x86_reset16.dts') 3707 self.assertEqual(X86_RESET16_DATA, data[:len(X86_RESET16_DATA)]) 3708 3709 def testPackReset16Spl(self): 3710 """Test that an image with an x86 reset16-spl region can be created""" 3711 data = self._DoReadFile('145_x86_reset16_spl.dts') 3712 self.assertEqual(X86_RESET16_SPL_DATA, data[:len(X86_RESET16_SPL_DATA)]) 3713 3714 def testPackReset16Tpl(self): 3715 """Test that an image with an x86 reset16-tpl region can be created""" 3716 data = self._DoReadFile('146_x86_reset16_tpl.dts') 3717 self.assertEqual(X86_RESET16_TPL_DATA, data[:len(X86_RESET16_TPL_DATA)]) 3718 3719 def testPackIntelFit(self): 3720 """Test that an image with an Intel FIT and pointer can be created""" 3721 data = self._DoReadFile('147_intel_fit.dts') 3722 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)]) 3723 fit = data[16:32]; 3724 self.assertEqual(b'_FIT_ \x01\x00\x00\x00\x00\x01\x80}' , fit) 3725 ptr = struct.unpack('<i', data[0x40:0x44])[0] 3726 3727 image = control.images['image'] 3728 entries = image.GetEntries() 3729 expected_ptr = entries['intel-fit'].image_pos - (1 << 32) 3730 self.assertEqual(expected_ptr, ptr) 3731 3732 def testPackIntelFitMissing(self): 3733 """Test detection of a FIT pointer with not FIT region""" 3734 with self.assertRaises(ValueError) as e: 3735 self._DoReadFile('148_intel_fit_missing.dts') 3736 self.assertIn("'intel-fit-ptr' section must have an 'intel-fit' sibling", 3737 str(e.exception)) 3738 3739 def _CheckSymbolsTplSection(self, dts, expected_vals): 3740 data = self._DoReadFile(dts) 3741 sym_values = struct.pack('<LLQLL', elf.BINMAN_SYM_MAGIC_VALUE, *expected_vals) 3742 upto1 = 4 + len(U_BOOT_SPL_DATA) 3743 expected1 = tools.get_bytes(0xff, 4) + sym_values + U_BOOT_SPL_DATA[24:] 3744 self.assertEqual(expected1, data[:upto1]) 3745 3746 upto2 = upto1 + 1 + len(U_BOOT_SPL_DATA) 3747 expected2 = tools.get_bytes(0xff, 1) + sym_values + U_BOOT_SPL_DATA[24:] 3748 self.assertEqual(expected2, data[upto1:upto2]) 3749 3750 upto3 = 0x3c + len(U_BOOT_DATA) 3751 expected3 = tools.get_bytes(0xff, 1) + U_BOOT_DATA 3752 self.assertEqual(expected3, data[upto2:upto3]) 3753 3754 expected4 = sym_values + U_BOOT_TPL_DATA[24:] 3755 self.assertEqual(expected4, data[upto3:upto3 + len(U_BOOT_TPL_DATA)]) 3756 3757 def testSymbolsTplSection(self): 3758 """Test binman can assign symbols embedded in U-Boot TPL in a section""" 3759 self._SetupSplElf('u_boot_binman_syms') 3760 self._SetupTplElf('u_boot_binman_syms') 3761 self._CheckSymbolsTplSection('149_symbols_tpl.dts', 3762 [0x04, 0x20, 0x10 + 0x3c, 0x04]) 3763 3764 def testSymbolsTplSectionX86(self): 3765 """Test binman can assign symbols in a section with end-at-4gb""" 3766 self._SetupSplElf('u_boot_binman_syms_x86') 3767 self._SetupTplElf('u_boot_binman_syms_x86') 3768 self._CheckSymbolsTplSection('155_symbols_tpl_x86.dts', 3769 [0xffffff04, 0xffffff20, 0xffffff3c, 3770 0x04]) 3771 3772 def testPackX86RomIfwiSectiom(self): 3773 """Test that a section can be placed in an IFWI region""" 3774 self._SetupIfwi('fitimage.bin') 3775 data = self._DoReadFile('151_x86_rom_ifwi_section.dts') 3776 self._CheckIfwi(data) 3777 3778 def testPackFspM(self): 3779 """Test that an image with a FSP memory-init binary can be created""" 3780 data = self._DoReadFile('152_intel_fsp_m.dts') 3781 self.assertEqual(FSP_M_DATA, data[:len(FSP_M_DATA)]) 3782 3783 def testPackFspS(self): 3784 """Test that an image with a FSP silicon-init binary can be created""" 3785 data = self._DoReadFile('153_intel_fsp_s.dts') 3786 self.assertEqual(FSP_S_DATA, data[:len(FSP_S_DATA)]) 3787 3788 def testPackFspT(self): 3789 """Test that an image with a FSP temp-ram-init binary can be created""" 3790 data = self._DoReadFile('154_intel_fsp_t.dts') 3791 self.assertEqual(FSP_T_DATA, data[:len(FSP_T_DATA)]) 3792 3793 def testMkimage(self): 3794 """Test using mkimage to build an image""" 3795 self._SetupSplElf() 3796 data = self._DoReadFile('156_mkimage.dts') 3797 3798 # Just check that the data appears in the file somewhere 3799 self.assertIn(U_BOOT_SPL_DATA, data) 3800 3801 def testMkimageMissing(self): 3802 """Test that binman still produces an image if mkimage is missing""" 3803 self._SetupSplElf() 3804 with test_util.capture_sys_output() as (_, stderr): 3805 self._DoTestFile('156_mkimage.dts', 3806 force_missing_bintools='mkimage') 3807 err = stderr.getvalue() 3808 self.assertRegex(err, "Image 'image'.*missing bintools.*: mkimage") 3809 3810 def testExtblob(self): 3811 """Test an image with an external blob""" 3812 data = self._DoReadFile('157_blob_ext.dts') 3813 self.assertEqual(REFCODE_DATA, data) 3814 3815 def testExtblobMissing(self): 3816 """Test an image with a missing external blob""" 3817 with self.assertRaises(ValueError) as e: 3818 self._DoReadFile('158_blob_ext_missing.dts') 3819 self.assertIn("Filename 'missing-file' not found in input path", 3820 str(e.exception)) 3821 3822 def testExtblobMissingOk(self): 3823 """Test an image with an missing external blob that is allowed""" 3824 with test_util.capture_sys_output() as (stdout, stderr): 3825 ret = self._DoTestFile('158_blob_ext_missing.dts', 3826 allow_missing=True) 3827 self.assertEqual(103, ret) 3828 err = stderr.getvalue() 3829 self.assertIn('(missing-file)', err) 3830 self.assertRegex(err, "Image 'image'.*missing.*: blob-ext") 3831 self.assertIn('Some images are invalid', err) 3832 3833 def testExtblobMissingOkFlag(self): 3834 """Test an image with an missing external blob allowed with -W""" 3835 with test_util.capture_sys_output() as (stdout, stderr): 3836 ret = self._DoTestFile('158_blob_ext_missing.dts', 3837 allow_missing=True, ignore_missing=True) 3838 self.assertEqual(0, ret) 3839 err = stderr.getvalue() 3840 self.assertIn('(missing-file)', err) 3841 self.assertRegex(err, "Image 'image'.*missing.*: blob-ext") 3842 self.assertIn('Some images are invalid', err) 3843 3844 def testExtblobMissingOkSect(self): 3845 """Test an image with an missing external blob that is allowed""" 3846 with test_util.capture_sys_output() as (stdout, stderr): 3847 self._DoTestFile('159_blob_ext_missing_sect.dts', 3848 allow_missing=True) 3849 err = stderr.getvalue() 3850 self.assertRegex(err, "Image 'image'.*missing.*: blob-ext blob-ext2") 3851 3852 def testPackX86RomMeMissingDesc(self): 3853 """Test that an missing Intel descriptor entry is allowed""" 3854 with test_util.capture_sys_output() as (stdout, stderr): 3855 self._DoTestFile('164_x86_rom_me_missing.dts', allow_missing=True) 3856 err = stderr.getvalue() 3857 self.assertRegex(err, "Image 'image'.*missing.*: intel-descriptor") 3858 3859 def testPackX86RomMissingIfwi(self): 3860 """Test that an x86 ROM with Integrated Firmware Image can be created""" 3861 self._SetupIfwi('fitimage.bin') 3862 pathname = os.path.join(self._indir, 'fitimage.bin') 3863 os.remove(pathname) 3864 with test_util.capture_sys_output() as (stdout, stderr): 3865 self._DoTestFile('111_x86_rom_ifwi.dts', allow_missing=True) 3866 err = stderr.getvalue() 3867 self.assertRegex(err, "Image 'image'.*missing.*: intel-ifwi") 3868 3869 def testPackOverlapZero(self): 3870 """Test that zero-size overlapping regions are ignored""" 3871 self._DoTestFile('160_pack_overlap_zero.dts') 3872 3873 def _CheckSimpleFitData(self, fit_data, kernel_data, fdt1_data): 3874 # The data should be inside the FIT 3875 dtb = fdt.Fdt.FromData(fit_data) 3876 dtb.Scan() 3877 fnode = dtb.GetNode('/images/kernel') 3878 self.assertIn('data', fnode.props) 3879 3880 fname = os.path.join(self._indir, 'fit_data.fit') 3881 tools.write_file(fname, fit_data) 3882 out = tools.run('dumpimage', '-l', fname) 3883 3884 # Check a few features to make sure the plumbing works. We don't need 3885 # to test the operation of mkimage or dumpimage here. First convert the 3886 # output into a dict where the keys are the fields printed by dumpimage 3887 # and the values are a list of values for each field 3888 lines = out.splitlines() 3889 3890 # Converts "Compression: gzip compressed" into two groups: 3891 # 'Compression' and 'gzip compressed' 3892 re_line = re.compile(r'^ *([^:]*)(?:: *(.*))?$') 3893 vals = collections.defaultdict(list) 3894 for line in lines: 3895 mat = re_line.match(line) 3896 vals[mat.group(1)].append(mat.group(2)) 3897 3898 self.assertEquals('FIT description: test-desc', lines[0]) 3899 self.assertIn('Created:', lines[1]) 3900 self.assertIn('Image 0 (kernel)', vals) 3901 self.assertIn('Hash value', vals) 3902 data_sizes = vals.get('Data Size') 3903 self.assertIsNotNone(data_sizes) 3904 self.assertEqual(2, len(data_sizes)) 3905 # Format is "4 Bytes = 0.00 KiB = 0.00 MiB" so take the first word 3906 self.assertEqual(len(kernel_data), int(data_sizes[0].split()[0])) 3907 self.assertEqual(len(fdt1_data), int(data_sizes[1].split()[0])) 3908 3909 # Check if entry listing correctly omits /images/ 3910 image = control.images['image'] 3911 fit_entry = image.GetEntries()['fit'] 3912 subentries = list(fit_entry.GetEntries().keys()) 3913 expected = ['kernel', 'fdt-1'] 3914 self.assertEqual(expected, subentries) 3915 3916 def testSimpleFit(self): 3917 """Test an image with a FIT inside""" 3918 self._SetupSplElf() 3919 data = self._DoReadFile('161_fit.dts') 3920 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)]) 3921 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):]) 3922 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)] 3923 3924 self._CheckSimpleFitData(fit_data, U_BOOT_DATA, U_BOOT_SPL_DTB_DATA) 3925 3926 def testSimpleFitExpandsSubentries(self): 3927 """Test that FIT images expand their subentries""" 3928 data = self._DoReadFileDtb('161_fit.dts', use_expanded=True)[0] 3929 self.assertEqual(U_BOOT_EXP_DATA, data[:len(U_BOOT_EXP_DATA)]) 3930 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):]) 3931 fit_data = data[len(U_BOOT_EXP_DATA):-len(U_BOOT_NODTB_DATA)] 3932 3933 self._CheckSimpleFitData(fit_data, U_BOOT_EXP_DATA, U_BOOT_SPL_DTB_DATA) 3934 3935 def testSimpleFitImagePos(self): 3936 """Test that we have correct image-pos for FIT subentries""" 3937 data, _, _, out_dtb_fname = self._DoReadFileDtb('161_fit.dts', 3938 update_dtb=True) 3939 dtb = fdt.Fdt(out_dtb_fname) 3940 dtb.Scan() 3941 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS) 3942 3943 self.maxDiff = None 3944 self.assertEqual({ 3945 'image-pos': 0, 3946 'offset': 0, 3947 'size': 1890, 3948 3949 'u-boot:image-pos': 0, 3950 'u-boot:offset': 0, 3951 'u-boot:size': 4, 3952 3953 'fit:image-pos': 4, 3954 'fit:offset': 4, 3955 'fit:size': 1840, 3956 3957 'fit/images/kernel:image-pos': 304, 3958 'fit/images/kernel:offset': 300, 3959 'fit/images/kernel:size': 4, 3960 3961 'fit/images/kernel/u-boot:image-pos': 304, 3962 'fit/images/kernel/u-boot:offset': 0, 3963 'fit/images/kernel/u-boot:size': 4, 3964 3965 'fit/images/fdt-1:image-pos': 552, 3966 'fit/images/fdt-1:offset': 548, 3967 'fit/images/fdt-1:size': 6, 3968 3969 'fit/images/fdt-1/u-boot-spl-dtb:image-pos': 552, 3970 'fit/images/fdt-1/u-boot-spl-dtb:offset': 0, 3971 'fit/images/fdt-1/u-boot-spl-dtb:size': 6, 3972 3973 'u-boot-nodtb:image-pos': 1844, 3974 'u-boot-nodtb:offset': 1844, 3975 'u-boot-nodtb:size': 46, 3976 }, props) 3977 3978 # Actually check the data is where we think it is 3979 for node, expected in [ 3980 ("u-boot", U_BOOT_DATA), 3981 ("fit/images/kernel", U_BOOT_DATA), 3982 ("fit/images/kernel/u-boot", U_BOOT_DATA), 3983 ("fit/images/fdt-1", U_BOOT_SPL_DTB_DATA), 3984 ("fit/images/fdt-1/u-boot-spl-dtb", U_BOOT_SPL_DTB_DATA), 3985 ("u-boot-nodtb", U_BOOT_NODTB_DATA), 3986 ]: 3987 image_pos = props[f"{node}:image-pos"] 3988 size = props[f"{node}:size"] 3989 self.assertEqual(len(expected), size) 3990 self.assertEqual(expected, data[image_pos:image_pos+size]) 3991 3992 def testFitExternal(self): 3993 """Test an image with an FIT with external images""" 3994 data = self._DoReadFile('162_fit_external.dts') 3995 fit_data = data[len(U_BOOT_DATA):-2] # _testing is 2 bytes 3996 3997 # Size of the external-data region as set up by mkimage 3998 external_data_size = len(U_BOOT_DATA) + 2 3999 expected_size = (len(U_BOOT_DATA) + 0x400 + 4000 tools.align(external_data_size, 4) + 4001 len(U_BOOT_NODTB_DATA)) 4002 4003 # The data should be outside the FIT 4004 dtb = fdt.Fdt.FromData(fit_data) 4005 dtb.Scan() 4006 fnode = dtb.GetNode('/images/kernel') 4007 self.assertNotIn('data', fnode.props) 4008 self.assertEqual(len(U_BOOT_DATA), 4009 fdt_util.fdt32_to_cpu(fnode.props['data-size'].value)) 4010 fit_pos = 0x400; 4011 self.assertEqual( 4012 fit_pos, 4013 fdt_util.fdt32_to_cpu(fnode.props['data-position'].value)) 4014 4015 self.assertEquals(expected_size, len(data)) 4016 actual_pos = len(U_BOOT_DATA) + fit_pos 4017 self.assertEqual(U_BOOT_DATA + b'aa', 4018 data[actual_pos:actual_pos + external_data_size]) 4019 4020 def testFitExternalImagePos(self): 4021 """Test that we have correct image-pos for external FIT subentries""" 4022 data, _, _, out_dtb_fname = self._DoReadFileDtb('162_fit_external.dts', 4023 update_dtb=True) 4024 dtb = fdt.Fdt(out_dtb_fname) 4025 dtb.Scan() 4026 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS) 4027 4028 self.assertEqual({ 4029 'image-pos': 0, 4030 'offset': 0, 4031 'size': 1082, 4032 4033 'u-boot:image-pos': 0, 4034 'u-boot:offset': 0, 4035 'u-boot:size': 4, 4036 4037 'fit:size': 1032, 4038 'fit:offset': 4, 4039 'fit:image-pos': 4, 4040 4041 'fit/images/kernel:size': 4, 4042 'fit/images/kernel:offset': 1024, 4043 'fit/images/kernel:image-pos': 1028, 4044 4045 'fit/images/kernel/u-boot:size': 4, 4046 'fit/images/kernel/u-boot:offset': 0, 4047 'fit/images/kernel/u-boot:image-pos': 1028, 4048 4049 'fit/images/fdt-1:size': 2, 4050 'fit/images/fdt-1:offset': 1028, 4051 'fit/images/fdt-1:image-pos': 1032, 4052 4053 'fit/images/fdt-1/_testing:size': 2, 4054 'fit/images/fdt-1/_testing:offset': 0, 4055 'fit/images/fdt-1/_testing:image-pos': 1032, 4056 4057 'u-boot-nodtb:image-pos': 1036, 4058 'u-boot-nodtb:offset': 1036, 4059 'u-boot-nodtb:size': 46, 4060 }, props) 4061 4062 # Actually check the data is where we think it is 4063 for node, expected in [ 4064 ("u-boot", U_BOOT_DATA), 4065 ("fit/images/kernel", U_BOOT_DATA), 4066 ("fit/images/kernel/u-boot", U_BOOT_DATA), 4067 ("fit/images/fdt-1", b'aa'), 4068 ("fit/images/fdt-1/_testing", b'aa'), 4069 ("u-boot-nodtb", U_BOOT_NODTB_DATA), 4070 ]: 4071 image_pos = props[f"{node}:image-pos"] 4072 size = props[f"{node}:size"] 4073 self.assertEqual(len(expected), size) 4074 self.assertEqual(expected, data[image_pos:image_pos+size]) 4075 4076 def testFitMissing(self): 4077 """Test that binman complains if mkimage is missing""" 4078 with self.assertRaises(ValueError) as e: 4079 self._DoTestFile('162_fit_external.dts', 4080 force_missing_bintools='mkimage') 4081 self.assertIn("Node '/binman/fit': Missing tool: 'mkimage'", 4082 str(e.exception)) 4083 4084 def testFitMissingOK(self): 4085 """Test that binman still produces a FIT image if mkimage is missing""" 4086 with test_util.capture_sys_output() as (_, stderr): 4087 self._DoTestFile('162_fit_external.dts', allow_missing=True, 4088 force_missing_bintools='mkimage') 4089 err = stderr.getvalue() 4090 self.assertRegex(err, "Image 'image'.*missing bintools.*: mkimage") 4091 4092 def testSectionIgnoreHashSignature(self): 4093 """Test that sections ignore hash, signature nodes for its data""" 4094 data = self._DoReadFile('165_section_ignore_hash_signature.dts') 4095 expected = (U_BOOT_DATA + U_BOOT_DATA) 4096 self.assertEqual(expected, data) 4097 4098 def testPadInSections(self): 4099 """Test pad-before, pad-after for entries in sections""" 4100 data, _, _, out_dtb_fname = self._DoReadFileDtb( 4101 '166_pad_in_sections.dts', update_dtb=True) 4102 expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) + 4103 U_BOOT_DATA + tools.get_bytes(ord('!'), 6) + 4104 U_BOOT_DATA) 4105 self.assertEqual(expected, data) 4106 4107 dtb = fdt.Fdt(out_dtb_fname) 4108 dtb.Scan() 4109 props = self._GetPropTree(dtb, ['size', 'image-pos', 'offset']) 4110 expected = { 4111 'image-pos': 0, 4112 'offset': 0, 4113 'size': 12 + 6 + 3 * len(U_BOOT_DATA), 4114 4115 'section:image-pos': 0, 4116 'section:offset': 0, 4117 'section:size': 12 + 6 + 3 * len(U_BOOT_DATA), 4118 4119 'section/before:image-pos': 0, 4120 'section/before:offset': 0, 4121 'section/before:size': len(U_BOOT_DATA), 4122 4123 'section/u-boot:image-pos': 4, 4124 'section/u-boot:offset': 4, 4125 'section/u-boot:size': 12 + len(U_BOOT_DATA) + 6, 4126 4127 'section/after:image-pos': 26, 4128 'section/after:offset': 26, 4129 'section/after:size': len(U_BOOT_DATA), 4130 } 4131 self.assertEqual(expected, props) 4132 4133 def testFitImageSubentryAlignment(self): 4134 """Test relative alignability of FIT image subentries""" 4135 self._SetupSplElf() 4136 entry_args = { 4137 'test-id': TEXT_DATA, 4138 } 4139 data, _, _, _ = self._DoReadFileDtb('167_fit_image_subentry_alignment.dts', 4140 entry_args=entry_args) 4141 dtb = fdt.Fdt.FromData(data) 4142 dtb.Scan() 4143 4144 node = dtb.GetNode('/images/kernel') 4145 data = dtb.GetProps(node)["data"].bytes 4146 align_pad = 0x10 - (len(U_BOOT_SPL_DATA) % 0x10) 4147 expected = (tools.get_bytes(0, 0x20) + U_BOOT_SPL_DATA + 4148 tools.get_bytes(0, align_pad) + U_BOOT_DATA) 4149 self.assertEqual(expected, data) 4150 4151 node = dtb.GetNode('/images/fdt-1') 4152 data = dtb.GetProps(node)["data"].bytes 4153 expected = (U_BOOT_SPL_DTB_DATA + tools.get_bytes(0, 20) + 4154 tools.to_bytes(TEXT_DATA) + tools.get_bytes(0, 30) + 4155 U_BOOT_DTB_DATA) 4156 self.assertEqual(expected, data) 4157 4158 def testFitExtblobMissingOk(self): 4159 """Test a FIT with a missing external blob that is allowed""" 4160 with test_util.capture_sys_output() as (stdout, stderr): 4161 self._DoTestFile('168_fit_missing_blob.dts', 4162 allow_missing=True) 4163 err = stderr.getvalue() 4164 self.assertRegex(err, "Image 'image'.*missing.*: atf-bl31") 4165 4166 def testBlobNamedByArgMissing(self): 4167 """Test handling of a missing entry arg""" 4168 with self.assertRaises(ValueError) as e: 4169 self._DoReadFile('068_blob_named_by_arg.dts') 4170 self.assertIn("Missing required properties/entry args: cros-ec-rw-path", 4171 str(e.exception)) 4172 4173 def testPackBl31(self): 4174 """Test that an image with an ATF BL31 binary can be created""" 4175 data = self._DoReadFile('169_atf_bl31.dts') 4176 self.assertEqual(ATF_BL31_DATA, data[:len(ATF_BL31_DATA)]) 4177 4178 def testPackScp(self): 4179 """Test that an image with an SCP binary can be created""" 4180 data = self._DoReadFile('172_scp.dts') 4181 self.assertEqual(SCP_DATA, data[:len(SCP_DATA)]) 4182 4183 def testFitFdt(self): 4184 """Test an image with an FIT with multiple FDT images""" 4185 def _CheckFdt(seq, expected_data): 4186 """Check the FDT nodes 4187 4188 Args: 4189 seq: Sequence number to check (0 or 1) 4190 expected_data: Expected contents of 'data' property 4191 """ 4192 name = 'fdt-%d' % seq 4193 fnode = dtb.GetNode('/images/%s' % name) 4194 self.assertIsNotNone(fnode) 4195 self.assertEqual({'description','type', 'compression', 'data'}, 4196 set(fnode.props.keys())) 4197 self.assertEqual(expected_data, fnode.props['data'].bytes) 4198 self.assertEqual('fdt-test-fdt%d.dtb' % seq, 4199 fnode.props['description'].value) 4200 self.assertEqual(fnode.subnodes[0].name, 'hash') 4201 4202 def _CheckConfig(seq, expected_data): 4203 """Check the configuration nodes 4204 4205 Args: 4206 seq: Sequence number to check (0 or 1) 4207 expected_data: Expected contents of 'data' property 4208 """ 4209 cnode = dtb.GetNode('/configurations') 4210 self.assertIn('default', cnode.props) 4211 self.assertEqual('config-2', cnode.props['default'].value) 4212 4213 name = 'config-%d' % seq 4214 fnode = dtb.GetNode('/configurations/%s' % name) 4215 self.assertIsNotNone(fnode) 4216 self.assertEqual({'description','firmware', 'loadables', 'fdt'}, 4217 set(fnode.props.keys())) 4218 self.assertEqual('conf-test-fdt%d.dtb' % seq, 4219 fnode.props['description'].value) 4220 self.assertEqual('fdt-%d' % seq, fnode.props['fdt'].value) 4221 4222 entry_args = { 4223 'of-list': 'test-fdt1 test-fdt2', 4224 'default-dt': 'test-fdt2', 4225 } 4226 data = self._DoReadFileDtb( 4227 '170_fit_fdt.dts', 4228 entry_args=entry_args, 4229 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0] 4230 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):]) 4231 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)] 4232 4233 dtb = fdt.Fdt.FromData(fit_data) 4234 dtb.Scan() 4235 fnode = dtb.GetNode('/images/kernel') 4236 self.assertIn('data', fnode.props) 4237 4238 # Check all the properties in fdt-1 and fdt-2 4239 _CheckFdt(1, TEST_FDT1_DATA) 4240 _CheckFdt(2, TEST_FDT2_DATA) 4241 4242 # Check configurations 4243 _CheckConfig(1, TEST_FDT1_DATA) 4244 _CheckConfig(2, TEST_FDT2_DATA) 4245 4246 def testFitFdtMissingList(self): 4247 """Test handling of a missing 'of-list' entry arg""" 4248 with self.assertRaises(ValueError) as e: 4249 self._DoReadFile('170_fit_fdt.dts') 4250 self.assertIn("Generator node requires 'of-list' entry argument", 4251 str(e.exception)) 4252 4253 def testFitFdtEmptyList(self): 4254 """Test handling of an empty 'of-list' entry arg""" 4255 entry_args = { 4256 'of-list': '', 4257 } 4258 data = self._DoReadFileDtb('170_fit_fdt.dts', entry_args=entry_args)[0] 4259 4260 def testFitFdtMissingProp(self): 4261 """Test handling of a missing 'fit,fdt-list' property""" 4262 with self.assertRaises(ValueError) as e: 4263 self._DoReadFile('171_fit_fdt_missing_prop.dts') 4264 self.assertIn("Generator node requires 'fit,fdt-list' property", 4265 str(e.exception)) 4266 4267 def testFitFdtMissing(self): 4268 """Test handling of a missing 'default-dt' entry arg""" 4269 entry_args = { 4270 'of-list': 'test-fdt1 test-fdt2', 4271 } 4272 with self.assertRaises(ValueError) as e: 4273 self._DoReadFileDtb( 4274 '170_fit_fdt.dts', 4275 entry_args=entry_args, 4276 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0] 4277 self.assertIn("Generated 'default' node requires default-dt entry argument", 4278 str(e.exception)) 4279 4280 def testFitFdtNotInList(self): 4281 """Test handling of a default-dt that is not in the of-list""" 4282 entry_args = { 4283 'of-list': 'test-fdt1 test-fdt2', 4284 'default-dt': 'test-fdt3', 4285 } 4286 with self.assertRaises(ValueError) as e: 4287 self._DoReadFileDtb( 4288 '170_fit_fdt.dts', 4289 entry_args=entry_args, 4290 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0] 4291 self.assertIn("default-dt entry argument 'test-fdt3' not found in fdt list: test-fdt1, test-fdt2", 4292 str(e.exception)) 4293 4294 def testFitExtblobMissingHelp(self): 4295 """Test display of help messages when an external blob is missing""" 4296 control.missing_blob_help = control._ReadMissingBlobHelp() 4297 control.missing_blob_help['wibble'] = 'Wibble test' 4298 control.missing_blob_help['another'] = 'Another test' 4299 with test_util.capture_sys_output() as (stdout, stderr): 4300 self._DoTestFile('168_fit_missing_blob.dts', 4301 allow_missing=True) 4302 err = stderr.getvalue() 4303 4304 # We can get the tag from the name, the type or the missing-msg 4305 # property. Check all three. 4306 self.assertIn('You may need to build ARM Trusted', err) 4307 self.assertIn('Wibble test', err) 4308 self.assertIn('Another test', err) 4309 4310 def testMissingBlob(self): 4311 """Test handling of a blob containing a missing file""" 4312 with self.assertRaises(ValueError) as e: 4313 self._DoTestFile('173_missing_blob.dts', allow_missing=True) 4314 self.assertIn("Filename 'missing' not found in input path", 4315 str(e.exception)) 4316 4317 def testEnvironment(self): 4318 """Test adding a U-Boot environment""" 4319 data = self._DoReadFile('174_env.dts') 4320 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)]) 4321 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):]) 4322 env = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)] 4323 self.assertEqual(b'\x1b\x97\x22\x7c\x01var1=1\0var2="2"\0\0\xff\xff', 4324 env) 4325 4326 def testEnvironmentNoSize(self): 4327 """Test that a missing 'size' property is detected""" 4328 with self.assertRaises(ValueError) as e: 4329 self._DoTestFile('175_env_no_size.dts') 4330 self.assertIn("'u-boot-env' entry must have a size property", 4331 str(e.exception)) 4332 4333 def testEnvironmentTooSmall(self): 4334 """Test handling of an environment that does not fit""" 4335 with self.assertRaises(ValueError) as e: 4336 self._DoTestFile('176_env_too_small.dts') 4337 4338 # checksum, start byte, environment with \0 terminator, final \0 4339 need = 4 + 1 + len(ENV_DATA) + 1 + 1 4340 short = need - 0x8 4341 self.assertIn("too small to hold data (need %#x more bytes)" % short, 4342 str(e.exception)) 4343 4344 def testSkipAtStart(self): 4345 """Test handling of skip-at-start section""" 4346 data = self._DoReadFile('177_skip_at_start.dts') 4347 self.assertEqual(U_BOOT_DATA, data) 4348 4349 image = control.images['image'] 4350 entries = image.GetEntries() 4351 section = entries['section'] 4352 self.assertEqual(0, section.offset) 4353 self.assertEqual(len(U_BOOT_DATA), section.size) 4354 self.assertEqual(U_BOOT_DATA, section.GetData()) 4355 4356 entry = section.GetEntries()['u-boot'] 4357 self.assertEqual(16, entry.offset) 4358 self.assertEqual(len(U_BOOT_DATA), entry.size) 4359 self.assertEqual(U_BOOT_DATA, entry.data) 4360 4361 def testSkipAtStartPad(self): 4362 """Test handling of skip-at-start section with padded entry""" 4363 data = self._DoReadFile('178_skip_at_start_pad.dts') 4364 before = tools.get_bytes(0, 8) 4365 after = tools.get_bytes(0, 4) 4366 all = before + U_BOOT_DATA + after 4367 self.assertEqual(all, data) 4368 4369 image = control.images['image'] 4370 entries = image.GetEntries() 4371 section = entries['section'] 4372 self.assertEqual(0, section.offset) 4373 self.assertEqual(len(all), section.size) 4374 self.assertEqual(all, section.GetData()) 4375 4376 entry = section.GetEntries()['u-boot'] 4377 self.assertEqual(16, entry.offset) 4378 self.assertEqual(len(all), entry.size) 4379 self.assertEqual(U_BOOT_DATA, entry.data) 4380 4381 def testSkipAtStartSectionPad(self): 4382 """Test handling of skip-at-start section with padding""" 4383 data = self._DoReadFile('179_skip_at_start_section_pad.dts') 4384 before = tools.get_bytes(0, 8) 4385 after = tools.get_bytes(0, 4) 4386 all = before + U_BOOT_DATA + after 4387 self.assertEqual(all, data) 4388 4389 image = control.images['image'] 4390 entries = image.GetEntries() 4391 section = entries['section'] 4392 self.assertEqual(0, section.offset) 4393 self.assertEqual(len(all), section.size) 4394 self.assertEqual(U_BOOT_DATA, section.data) 4395 self.assertEqual(all, section.GetPaddedData()) 4396 4397 entry = section.GetEntries()['u-boot'] 4398 self.assertEqual(16, entry.offset) 4399 self.assertEqual(len(U_BOOT_DATA), entry.size) 4400 self.assertEqual(U_BOOT_DATA, entry.data) 4401 4402 def testSectionPad(self): 4403 """Testing padding with sections""" 4404 data = self._DoReadFile('180_section_pad.dts') 4405 expected = (tools.get_bytes(ord('&'), 3) + 4406 tools.get_bytes(ord('!'), 5) + 4407 U_BOOT_DATA + 4408 tools.get_bytes(ord('!'), 1) + 4409 tools.get_bytes(ord('&'), 2)) 4410 self.assertEqual(expected, data) 4411 4412 def testSectionAlign(self): 4413 """Testing alignment with sections""" 4414 data = self._DoReadFileDtb('181_section_align.dts', map=True)[0] 4415 expected = (b'\0' + # fill section 4416 tools.get_bytes(ord('&'), 1) + # padding to section align 4417 b'\0' + # fill section 4418 tools.get_bytes(ord('!'), 3) + # padding to u-boot align 4419 U_BOOT_DATA + 4420 tools.get_bytes(ord('!'), 4) + # padding to u-boot size 4421 tools.get_bytes(ord('!'), 4)) # padding to section size 4422 self.assertEqual(expected, data) 4423 4424 def testCompressImage(self): 4425 """Test compression of the entire image""" 4426 self._CheckLz4() 4427 data, _, _, out_dtb_fname = self._DoReadFileDtb( 4428 '182_compress_image.dts', use_real_dtb=True, update_dtb=True) 4429 dtb = fdt.Fdt(out_dtb_fname) 4430 dtb.Scan() 4431 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size', 4432 'uncomp-size']) 4433 orig = self._decompress(data) 4434 self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, orig) 4435 4436 # Do a sanity check on various fields 4437 image = control.images['image'] 4438 entries = image.GetEntries() 4439 self.assertEqual(2, len(entries)) 4440 4441 entry = entries['blob'] 4442 self.assertEqual(COMPRESS_DATA, entry.data) 4443 self.assertEqual(len(COMPRESS_DATA), entry.size) 4444 4445 entry = entries['u-boot'] 4446 self.assertEqual(U_BOOT_DATA, entry.data) 4447 self.assertEqual(len(U_BOOT_DATA), entry.size) 4448 4449 self.assertEqual(len(data), image.size) 4450 self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, image.uncomp_data) 4451 self.assertEqual(len(COMPRESS_DATA + U_BOOT_DATA), image.uncomp_size) 4452 orig = self._decompress(image.data) 4453 self.assertEqual(orig, image.uncomp_data) 4454 4455 expected = { 4456 'blob:offset': 0, 4457 'blob:size': len(COMPRESS_DATA), 4458 'u-boot:offset': len(COMPRESS_DATA), 4459 'u-boot:size': len(U_BOOT_DATA), 4460 'uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA), 4461 'offset': 0, 4462 'image-pos': 0, 4463 'size': len(data), 4464 } 4465 self.assertEqual(expected, props) 4466 4467 def testCompressImageLess(self): 4468 """Test compression where compression reduces the image size""" 4469 self._CheckLz4() 4470 data, _, _, out_dtb_fname = self._DoReadFileDtb( 4471 '183_compress_image_less.dts', use_real_dtb=True, update_dtb=True) 4472 dtb = fdt.Fdt(out_dtb_fname) 4473 dtb.Scan() 4474 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size', 4475 'uncomp-size']) 4476 orig = self._decompress(data) 4477 4478 self.assertEquals(COMPRESS_DATA + COMPRESS_DATA + U_BOOT_DATA, orig) 4479 4480 # Do a sanity check on various fields 4481 image = control.images['image'] 4482 entries = image.GetEntries() 4483 self.assertEqual(2, len(entries)) 4484 4485 entry = entries['blob'] 4486 self.assertEqual(COMPRESS_DATA_BIG, entry.data) 4487 self.assertEqual(len(COMPRESS_DATA_BIG), entry.size) 4488 4489 entry = entries['u-boot'] 4490 self.assertEqual(U_BOOT_DATA, entry.data) 4491 self.assertEqual(len(U_BOOT_DATA), entry.size) 4492 4493 self.assertEqual(len(data), image.size) 4494 self.assertEqual(COMPRESS_DATA_BIG + U_BOOT_DATA, image.uncomp_data) 4495 self.assertEqual(len(COMPRESS_DATA_BIG + U_BOOT_DATA), 4496 image.uncomp_size) 4497 orig = self._decompress(image.data) 4498 self.assertEqual(orig, image.uncomp_data) 4499 4500 expected = { 4501 'blob:offset': 0, 4502 'blob:size': len(COMPRESS_DATA_BIG), 4503 'u-boot:offset': len(COMPRESS_DATA_BIG), 4504 'u-boot:size': len(U_BOOT_DATA), 4505 'uncomp-size': len(COMPRESS_DATA_BIG + U_BOOT_DATA), 4506 'offset': 0, 4507 'image-pos': 0, 4508 'size': len(data), 4509 } 4510 self.assertEqual(expected, props) 4511 4512 def testCompressSectionSize(self): 4513 """Test compression of a section with a fixed size""" 4514 self._CheckLz4() 4515 data, _, _, out_dtb_fname = self._DoReadFileDtb( 4516 '184_compress_section_size.dts', use_real_dtb=True, update_dtb=True) 4517 dtb = fdt.Fdt(out_dtb_fname) 4518 dtb.Scan() 4519 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size', 4520 'uncomp-size']) 4521 orig = self._decompress(data) 4522 self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, orig) 4523 expected = { 4524 'section/blob:offset': 0, 4525 'section/blob:size': len(COMPRESS_DATA), 4526 'section/u-boot:offset': len(COMPRESS_DATA), 4527 'section/u-boot:size': len(U_BOOT_DATA), 4528 'section:offset': 0, 4529 'section:image-pos': 0, 4530 'section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA), 4531 'section:size': 0x30, 4532 'offset': 0, 4533 'image-pos': 0, 4534 'size': 0x30, 4535 } 4536 self.assertEqual(expected, props) 4537 4538 def testCompressSection(self): 4539 """Test compression of a section with no fixed size""" 4540 self._CheckLz4() 4541 data, _, _, out_dtb_fname = self._DoReadFileDtb( 4542 '185_compress_section.dts', use_real_dtb=True, update_dtb=True) 4543 dtb = fdt.Fdt(out_dtb_fname) 4544 dtb.Scan() 4545 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size', 4546 'uncomp-size']) 4547 orig = self._decompress(data) 4548 self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, orig) 4549 expected = { 4550 'section/blob:offset': 0, 4551 'section/blob:size': len(COMPRESS_DATA), 4552 'section/u-boot:offset': len(COMPRESS_DATA), 4553 'section/u-boot:size': len(U_BOOT_DATA), 4554 'section:offset': 0, 4555 'section:image-pos': 0, 4556 'section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA), 4557 'section:size': len(data), 4558 'offset': 0, 4559 'image-pos': 0, 4560 'size': len(data), 4561 } 4562 self.assertEqual(expected, props) 4563 4564 def testLz4Missing(self): 4565 """Test that binman still produces an image if lz4 is missing""" 4566 with test_util.capture_sys_output() as (_, stderr): 4567 self._DoTestFile('185_compress_section.dts', 4568 force_missing_bintools='lz4') 4569 err = stderr.getvalue() 4570 self.assertRegex(err, "Image 'image'.*missing bintools.*: lz4") 4571 4572 def testCompressExtra(self): 4573 """Test compression of a section with no fixed size""" 4574 self._CheckLz4() 4575 data, _, _, out_dtb_fname = self._DoReadFileDtb( 4576 '186_compress_extra.dts', use_real_dtb=True, update_dtb=True) 4577 dtb = fdt.Fdt(out_dtb_fname) 4578 dtb.Scan() 4579 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size', 4580 'uncomp-size']) 4581 4582 base = data[len(U_BOOT_DATA):] 4583 self.assertEquals(U_BOOT_DATA, base[:len(U_BOOT_DATA)]) 4584 rest = base[len(U_BOOT_DATA):] 4585 4586 # Check compressed data 4587 bintool = self.comp_bintools['lz4'] 4588 expect1 = bintool.compress(COMPRESS_DATA + U_BOOT_DATA) 4589 data1 = rest[:len(expect1)] 4590 section1 = self._decompress(data1) 4591 self.assertEquals(expect1, data1) 4592 self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, section1) 4593 rest1 = rest[len(expect1):] 4594 4595 expect2 = bintool.compress(COMPRESS_DATA + COMPRESS_DATA) 4596 data2 = rest1[:len(expect2)] 4597 section2 = self._decompress(data2) 4598 self.assertEquals(expect2, data2) 4599 self.assertEquals(COMPRESS_DATA + COMPRESS_DATA, section2) 4600 rest2 = rest1[len(expect2):] 4601 4602 expect_size = (len(U_BOOT_DATA) + len(U_BOOT_DATA) + len(expect1) + 4603 len(expect2) + len(U_BOOT_DATA)) 4604 #self.assertEquals(expect_size, len(data)) 4605 4606 #self.assertEquals(U_BOOT_DATA, rest2) 4607 4608 self.maxDiff = None 4609 expected = { 4610 'u-boot:offset': 0, 4611 'u-boot:image-pos': 0, 4612 'u-boot:size': len(U_BOOT_DATA), 4613 4614 'base:offset': len(U_BOOT_DATA), 4615 'base:image-pos': len(U_BOOT_DATA), 4616 'base:size': len(data) - len(U_BOOT_DATA), 4617 'base/u-boot:offset': 0, 4618 'base/u-boot:image-pos': len(U_BOOT_DATA), 4619 'base/u-boot:size': len(U_BOOT_DATA), 4620 'base/u-boot2:offset': len(U_BOOT_DATA) + len(expect1) + 4621 len(expect2), 4622 'base/u-boot2:image-pos': len(U_BOOT_DATA) * 2 + len(expect1) + 4623 len(expect2), 4624 'base/u-boot2:size': len(U_BOOT_DATA), 4625 4626 'base/section:offset': len(U_BOOT_DATA), 4627 'base/section:image-pos': len(U_BOOT_DATA) * 2, 4628 'base/section:size': len(expect1), 4629 'base/section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA), 4630 'base/section/blob:offset': 0, 4631 'base/section/blob:size': len(COMPRESS_DATA), 4632 'base/section/u-boot:offset': len(COMPRESS_DATA), 4633 'base/section/u-boot:size': len(U_BOOT_DATA), 4634 4635 'base/section2:offset': len(U_BOOT_DATA) + len(expect1), 4636 'base/section2:image-pos': len(U_BOOT_DATA) * 2 + len(expect1), 4637 'base/section2:size': len(expect2), 4638 'base/section2:uncomp-size': len(COMPRESS_DATA + COMPRESS_DATA), 4639 'base/section2/blob:offset': 0, 4640 'base/section2/blob:size': len(COMPRESS_DATA), 4641 'base/section2/blob2:offset': len(COMPRESS_DATA), 4642 'base/section2/blob2:size': len(COMPRESS_DATA), 4643 4644 'offset': 0, 4645 'image-pos': 0, 4646 'size': len(data), 4647 } 4648 self.assertEqual(expected, props) 4649 4650 def testSymbolsSubsection(self): 4651 """Test binman can assign symbols from a subsection""" 4652 self.checkSymbols('187_symbols_sub.dts', U_BOOT_SPL_DATA, 0x1c) 4653 4654 def testReadImageEntryArg(self): 4655 """Test reading an image that would need an entry arg to generate""" 4656 entry_args = { 4657 'cros-ec-rw-path': 'ecrw.bin', 4658 } 4659 data = self.data = self._DoReadFileDtb( 4660 '188_image_entryarg.dts',use_real_dtb=True, update_dtb=True, 4661 entry_args=entry_args) 4662 4663 image_fname = tools.get_output_filename('image.bin') 4664 orig_image = control.images['image'] 4665 4666 # This should not generate an error about the missing 'cros-ec-rw-path' 4667 # since we are reading the image from a file. Compare with 4668 # testEntryArgsRequired() 4669 image = Image.FromFile(image_fname) 4670 self.assertEqual(orig_image.GetEntries().keys(), 4671 image.GetEntries().keys()) 4672 4673 def testFilesAlign(self): 4674 """Test alignment with files""" 4675 data = self._DoReadFile('190_files_align.dts') 4676 4677 # The first string is 15 bytes so will align to 16 4678 expect = FILES_DATA[:15] + b'\0' + FILES_DATA[15:] 4679 self.assertEqual(expect, data) 4680 4681 def testReadImageSkip(self): 4682 """Test reading an image and accessing its FDT map""" 4683 data = self.data = self._DoReadFileRealDtb('191_read_image_skip.dts') 4684 image_fname = tools.get_output_filename('image.bin') 4685 orig_image = control.images['image'] 4686 image = Image.FromFile(image_fname) 4687 self.assertEqual(orig_image.GetEntries().keys(), 4688 image.GetEntries().keys()) 4689 4690 orig_entry = orig_image.GetEntries()['fdtmap'] 4691 entry = image.GetEntries()['fdtmap'] 4692 self.assertEqual(orig_entry.offset, entry.offset) 4693 self.assertEqual(orig_entry.size, entry.size) 4694 self.assertEqual(16, entry.image_pos) 4695 4696 u_boot = image.GetEntries()['section'].GetEntries()['u-boot'] 4697 4698 self.assertEquals(U_BOOT_DATA, u_boot.ReadData()) 4699 4700 def testTplNoDtb(self): 4701 """Test that an image with tpl/u-boot-tpl-nodtb.bin can be created""" 4702 self._SetupTplElf() 4703 data = self._DoReadFile('192_u_boot_tpl_nodtb.dts') 4704 self.assertEqual(U_BOOT_TPL_NODTB_DATA, 4705 data[:len(U_BOOT_TPL_NODTB_DATA)]) 4706 4707 def testTplBssPad(self): 4708 """Test that we can pad TPL's BSS with zeros""" 4709 # ELF file with a '__bss_size' symbol 4710 self._SetupTplElf() 4711 data = self._DoReadFile('193_tpl_bss_pad.dts') 4712 self.assertEqual(U_BOOT_TPL_DATA + tools.get_bytes(0, 10) + U_BOOT_DATA, 4713 data) 4714 4715 def testTplBssPadMissing(self): 4716 """Test that a missing symbol is detected""" 4717 self._SetupTplElf('u_boot_ucode_ptr') 4718 with self.assertRaises(ValueError) as e: 4719 self._DoReadFile('193_tpl_bss_pad.dts') 4720 self.assertIn('Expected __bss_size symbol in tpl/u-boot-tpl', 4721 str(e.exception)) 4722 4723 def checkDtbSizes(self, data, pad_len, start): 4724 """Check the size arguments in a dtb embedded in an image 4725 4726 Args: 4727 data: The image data 4728 pad_len: Length of the pad section in the image, in bytes 4729 start: Start offset of the devicetree to examine, within the image 4730 4731 Returns: 4732 Size of the devicetree in bytes 4733 """ 4734 dtb_data = data[start:] 4735 dtb = fdt.Fdt.FromData(dtb_data) 4736 fdt_size = dtb.GetFdtObj().totalsize() 4737 dtb.Scan() 4738 props = self._GetPropTree(dtb, 'size') 4739 self.assertEqual({ 4740 'size': len(data), 4741 'u-boot-spl/u-boot-spl-bss-pad:size': pad_len, 4742 'u-boot-spl/u-boot-spl-dtb:size': 801, 4743 'u-boot-spl/u-boot-spl-nodtb:size': len(U_BOOT_SPL_NODTB_DATA), 4744 'u-boot-spl:size': 860, 4745 'u-boot-tpl:size': len(U_BOOT_TPL_DATA), 4746 'u-boot/u-boot-dtb:size': 781, 4747 'u-boot/u-boot-nodtb:size': len(U_BOOT_NODTB_DATA), 4748 'u-boot:size': 827, 4749 }, props) 4750 return fdt_size 4751 4752 def testExpanded(self): 4753 """Test that an expanded entry type is selected when needed""" 4754 self._SetupSplElf() 4755 self._SetupTplElf() 4756 4757 # SPL has a devicetree, TPL does not 4758 entry_args = { 4759 'spl-dtb': '1', 4760 'spl-bss-pad': 'y', 4761 'tpl-dtb': '', 4762 } 4763 self._DoReadFileDtb('194_fdt_incl.dts', use_expanded=True, 4764 entry_args=entry_args) 4765 image = control.images['image'] 4766 entries = image.GetEntries() 4767 self.assertEqual(3, len(entries)) 4768 4769 # First, u-boot, which should be expanded into u-boot-nodtb and dtb 4770 self.assertIn('u-boot', entries) 4771 entry = entries['u-boot'] 4772 self.assertEqual('u-boot-expanded', entry.etype) 4773 subent = entry.GetEntries() 4774 self.assertEqual(2, len(subent)) 4775 self.assertIn('u-boot-nodtb', subent) 4776 self.assertIn('u-boot-dtb', subent) 4777 4778 # Second, u-boot-spl, which should be expanded into three parts 4779 self.assertIn('u-boot-spl', entries) 4780 entry = entries['u-boot-spl'] 4781 self.assertEqual('u-boot-spl-expanded', entry.etype) 4782 subent = entry.GetEntries() 4783 self.assertEqual(3, len(subent)) 4784 self.assertIn('u-boot-spl-nodtb', subent) 4785 self.assertIn('u-boot-spl-bss-pad', subent) 4786 self.assertIn('u-boot-spl-dtb', subent) 4787 4788 # Third, u-boot-tpl, which should be not be expanded, since TPL has no 4789 # devicetree 4790 self.assertIn('u-boot-tpl', entries) 4791 entry = entries['u-boot-tpl'] 4792 self.assertEqual('u-boot-tpl', entry.etype) 4793 self.assertEqual(None, entry.GetEntries()) 4794 4795 def testExpandedTpl(self): 4796 """Test that an expanded entry type is selected for TPL when needed""" 4797 self._SetupTplElf() 4798 4799 entry_args = { 4800 'tpl-bss-pad': 'y', 4801 'tpl-dtb': 'y', 4802 } 4803 self._DoReadFileDtb('195_fdt_incl_tpl.dts', use_expanded=True, 4804 entry_args=entry_args) 4805 image = control.images['image'] 4806 entries = image.GetEntries() 4807 self.assertEqual(1, len(entries)) 4808 4809 # We only have u-boot-tpl, which be expanded 4810 self.assertIn('u-boot-tpl', entries) 4811 entry = entries['u-boot-tpl'] 4812 self.assertEqual('u-boot-tpl-expanded', entry.etype) 4813 subent = entry.GetEntries() 4814 self.assertEqual(3, len(subent)) 4815 self.assertIn('u-boot-tpl-nodtb', subent) 4816 self.assertIn('u-boot-tpl-bss-pad', subent) 4817 self.assertIn('u-boot-tpl-dtb', subent) 4818 4819 def testExpandedNoPad(self): 4820 """Test an expanded entry without BSS pad enabled""" 4821 self._SetupSplElf() 4822 self._SetupTplElf() 4823 4824 # SPL has a devicetree, TPL does not 4825 entry_args = { 4826 'spl-dtb': 'something', 4827 'spl-bss-pad': 'n', 4828 'tpl-dtb': '', 4829 } 4830 self._DoReadFileDtb('194_fdt_incl.dts', use_expanded=True, 4831 entry_args=entry_args) 4832 image = control.images['image'] 4833 entries = image.GetEntries() 4834 4835 # Just check u-boot-spl, which should be expanded into two parts 4836 self.assertIn('u-boot-spl', entries) 4837 entry = entries['u-boot-spl'] 4838 self.assertEqual('u-boot-spl-expanded', entry.etype) 4839 subent = entry.GetEntries() 4840 self.assertEqual(2, len(subent)) 4841 self.assertIn('u-boot-spl-nodtb', subent) 4842 self.assertIn('u-boot-spl-dtb', subent) 4843 4844 def testExpandedTplNoPad(self): 4845 """Test that an expanded entry type with padding disabled in TPL""" 4846 self._SetupTplElf() 4847 4848 entry_args = { 4849 'tpl-bss-pad': '', 4850 'tpl-dtb': 'y', 4851 } 4852 self._DoReadFileDtb('195_fdt_incl_tpl.dts', use_expanded=True, 4853 entry_args=entry_args) 4854 image = control.images['image'] 4855 entries = image.GetEntries() 4856 self.assertEqual(1, len(entries)) 4857 4858 # We only have u-boot-tpl, which be expanded 4859 self.assertIn('u-boot-tpl', entries) 4860 entry = entries['u-boot-tpl'] 4861 self.assertEqual('u-boot-tpl-expanded', entry.etype) 4862 subent = entry.GetEntries() 4863 self.assertEqual(2, len(subent)) 4864 self.assertIn('u-boot-tpl-nodtb', subent) 4865 self.assertIn('u-boot-tpl-dtb', subent) 4866 4867 def testFdtInclude(self): 4868 """Test that an Fdt is update within all binaries""" 4869 self._SetupSplElf() 4870 self._SetupTplElf() 4871 4872 # SPL has a devicetree, TPL does not 4873 self.maxDiff = None 4874 entry_args = { 4875 'spl-dtb': '1', 4876 'spl-bss-pad': 'y', 4877 'tpl-dtb': '', 4878 } 4879 # Build the image. It includes two separate devicetree binaries, each 4880 # with their own contents, but all contain the binman definition. 4881 data = self._DoReadFileDtb( 4882 '194_fdt_incl.dts', use_real_dtb=True, use_expanded=True, 4883 update_dtb=True, entry_args=entry_args)[0] 4884 pad_len = 10 4885 4886 # Check the U-Boot dtb 4887 start = len(U_BOOT_NODTB_DATA) 4888 fdt_size = self.checkDtbSizes(data, pad_len, start) 4889 4890 # Now check SPL 4891 start += fdt_size + len(U_BOOT_SPL_NODTB_DATA) + pad_len 4892 fdt_size = self.checkDtbSizes(data, pad_len, start) 4893 4894 # TPL has no devicetree 4895 start += fdt_size + len(U_BOOT_TPL_DATA) 4896 self.assertEqual(len(data), start) 4897 4898 def testSymbolsExpanded(self): 4899 """Test binman can assign symbols in expanded entries""" 4900 entry_args = { 4901 'spl-dtb': '1', 4902 } 4903 self.checkSymbols('197_symbols_expand.dts', U_BOOT_SPL_NODTB_DATA + 4904 U_BOOT_SPL_DTB_DATA, 0x38, 4905 entry_args=entry_args, use_expanded=True) 4906 4907 def testCollection(self): 4908 """Test a collection""" 4909 data = self._DoReadFile('198_collection.dts') 4910 self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA + 4911 tools.get_bytes(0xff, 2) + U_BOOT_NODTB_DATA + 4912 tools.get_bytes(0xfe, 3) + U_BOOT_DTB_DATA, 4913 data) 4914 4915 def testCollectionSection(self): 4916 """Test a collection where a section must be built first""" 4917 # Sections never have their contents when GetData() is called, but when 4918 # BuildSectionData() is called with required=True, a section will force 4919 # building the contents, producing an error is anything is still 4920 # missing. 4921 data = self._DoReadFile('199_collection_section.dts') 4922 section = U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA 4923 self.assertEqual(section + U_BOOT_DATA + tools.get_bytes(0xff, 2) + 4924 section + tools.get_bytes(0xfe, 3) + U_BOOT_DATA, 4925 data) 4926 4927 def testAlignDefault(self): 4928 """Test that default alignment works on sections""" 4929 data = self._DoReadFile('200_align_default.dts') 4930 expected = (U_BOOT_DATA + tools.get_bytes(0, 8 - len(U_BOOT_DATA)) + 4931 U_BOOT_DATA) 4932 # Special alignment for section 4933 expected += tools.get_bytes(0, 32 - len(expected)) 4934 # No alignment within the nested section 4935 expected += U_BOOT_DATA + U_BOOT_NODTB_DATA; 4936 # Now the final piece, which should be default-aligned 4937 expected += tools.get_bytes(0, 88 - len(expected)) + U_BOOT_NODTB_DATA 4938 self.assertEqual(expected, data) 4939 4940 def testPackOpenSBI(self): 4941 """Test that an image with an OpenSBI binary can be created""" 4942 data = self._DoReadFile('201_opensbi.dts') 4943 self.assertEqual(OPENSBI_DATA, data[:len(OPENSBI_DATA)]) 4944 4945 def testSectionsSingleThread(self): 4946 """Test sections without multithreading""" 4947 data = self._DoReadFileDtb('055_sections.dts', threads=0)[0] 4948 expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) + 4949 U_BOOT_DATA + tools.get_bytes(ord('a'), 12) + 4950 U_BOOT_DATA + tools.get_bytes(ord('&'), 4)) 4951 self.assertEqual(expected, data) 4952 4953 def testThreadTimeout(self): 4954 """Test handling a thread that takes too long""" 4955 with self.assertRaises(ValueError) as e: 4956 self._DoTestFile('202_section_timeout.dts', 4957 test_section_timeout=True) 4958 self.assertIn("Timed out obtaining contents", str(e.exception)) 4959 4960 def testTiming(self): 4961 """Test output of timing information""" 4962 data = self._DoReadFile('055_sections.dts') 4963 with test_util.capture_sys_output() as (stdout, stderr): 4964 state.TimingShow() 4965 self.assertIn('read:', stdout.getvalue()) 4966 self.assertIn('compress:', stdout.getvalue()) 4967 4968 def testUpdateFdtInElf(self): 4969 """Test that we can update the devicetree in an ELF file""" 4970 if not elf.ELF_TOOLS: 4971 self.skipTest('Python elftools not available') 4972 infile = elf_fname = self.ElfTestFile('u_boot_binman_embed') 4973 outfile = os.path.join(self._indir, 'u-boot.out') 4974 begin_sym = 'dtb_embed_begin' 4975 end_sym = 'dtb_embed_end' 4976 retcode = self._DoTestFile( 4977 '060_fdt_update.dts', update_dtb=True, 4978 update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym])) 4979 self.assertEqual(0, retcode) 4980 4981 # Check that the output file does in fact contact a dtb with the binman 4982 # definition in the correct place 4983 syms = elf.GetSymbolFileOffset(infile, 4984 ['dtb_embed_begin', 'dtb_embed_end']) 4985 data = tools.read_file(outfile) 4986 dtb_data = data[syms['dtb_embed_begin'].offset: 4987 syms['dtb_embed_end'].offset] 4988 4989 dtb = fdt.Fdt.FromData(dtb_data) 4990 dtb.Scan() 4991 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS) 4992 self.assertEqual({ 4993 'image-pos': 0, 4994 'offset': 0, 4995 '_testing:offset': 32, 4996 '_testing:size': 2, 4997 '_testing:image-pos': 32, 4998 'section@0/u-boot:offset': 0, 4999 'section@0/u-boot:size': len(U_BOOT_DATA), 5000 'section@0/u-boot:image-pos': 0, 5001 'section@0:offset': 0, 5002 'section@0:size': 16, 5003 'section@0:image-pos': 0, 5004 5005 'section@1/u-boot:offset': 0, 5006 'section@1/u-boot:size': len(U_BOOT_DATA), 5007 'section@1/u-boot:image-pos': 16, 5008 'section@1:offset': 16, 5009 'section@1:size': 16, 5010 'section@1:image-pos': 16, 5011 'size': 40 5012 }, props) 5013 5014 def testUpdateFdtInElfInvalid(self): 5015 """Test that invalid args are detected with --update-fdt-in-elf""" 5016 with self.assertRaises(ValueError) as e: 5017 self._DoTestFile('060_fdt_update.dts', update_fdt_in_elf='fred') 5018 self.assertIn("Invalid args ['fred'] to --update-fdt-in-elf", 5019 str(e.exception)) 5020 5021 def testUpdateFdtInElfNoSyms(self): 5022 """Test that missing symbols are detected with --update-fdt-in-elf""" 5023 if not elf.ELF_TOOLS: 5024 self.skipTest('Python elftools not available') 5025 infile = elf_fname = self.ElfTestFile('u_boot_binman_embed') 5026 outfile = '' 5027 begin_sym = 'wrong_begin' 5028 end_sym = 'wrong_end' 5029 with self.assertRaises(ValueError) as e: 5030 self._DoTestFile( 5031 '060_fdt_update.dts', 5032 update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym])) 5033 self.assertIn("Expected two symbols 'wrong_begin' and 'wrong_end': got 0:", 5034 str(e.exception)) 5035 5036 def testUpdateFdtInElfTooSmall(self): 5037 """Test that an over-large dtb is detected with --update-fdt-in-elf""" 5038 if not elf.ELF_TOOLS: 5039 self.skipTest('Python elftools not available') 5040 infile = elf_fname = self.ElfTestFile('u_boot_binman_embed_sm') 5041 outfile = os.path.join(self._indir, 'u-boot.out') 5042 begin_sym = 'dtb_embed_begin' 5043 end_sym = 'dtb_embed_end' 5044 with self.assertRaises(ValueError) as e: 5045 self._DoTestFile( 5046 '060_fdt_update.dts', update_dtb=True, 5047 update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym])) 5048 self.assertRegex( 5049 str(e.exception), 5050 "Not enough space in '.*u_boot_binman_embed_sm' for data length.*") 5051 5052 def testVersion(self): 5053 """Test we can get the binman version""" 5054 version = '(unreleased)' 5055 self.assertEqual(version, state.GetVersion(self._indir)) 5056 5057 with self.assertRaises(SystemExit): 5058 with test_util.capture_sys_output() as (_, stderr): 5059 self._DoBinman('-V') 5060 self.assertEqual('Binman %s\n' % version, stderr.getvalue()) 5061 5062 # Try running the tool too, just to be safe 5063 result = self._RunBinman('-V') 5064 self.assertEqual('Binman %s\n' % version, result.stderr) 5065 5066 # Set up a version file to make sure that works 5067 version = 'v2025.01-rc2' 5068 tools.write_file(os.path.join(self._indir, 'version'), version, 5069 binary=False) 5070 self.assertEqual(version, state.GetVersion(self._indir)) 5071 5072 def testAltFormat(self): 5073 """Test that alternative formats can be used to extract""" 5074 self._DoReadFileRealDtb('213_fdtmap_alt_format.dts') 5075 5076 try: 5077 tmpdir, updated_fname = self._SetupImageInTmpdir() 5078 with test_util.capture_sys_output() as (stdout, _): 5079 self._DoBinman('extract', '-i', updated_fname, '-F', 'list') 5080 self.assertEqual( 5081 '''Flag (-F) Entry type Description 5082fdt fdtmap Extract the devicetree blob from the fdtmap 5083''', 5084 stdout.getvalue()) 5085 5086 dtb = os.path.join(tmpdir, 'fdt.dtb') 5087 self._DoBinman('extract', '-i', updated_fname, '-F', 'fdt', '-f', 5088 dtb, 'fdtmap') 5089 5090 # Check that we can read it and it can be scanning, meaning it does 5091 # not have a 16-byte fdtmap header 5092 data = tools.read_file(dtb) 5093 dtb = fdt.Fdt.FromData(data) 5094 dtb.Scan() 5095 5096 # Now check u-boot which has no alt_format 5097 fname = os.path.join(tmpdir, 'fdt.dtb') 5098 self._DoBinman('extract', '-i', updated_fname, '-F', 'dummy', 5099 '-f', fname, 'u-boot') 5100 data = tools.read_file(fname) 5101 self.assertEqual(U_BOOT_DATA, data) 5102 5103 finally: 5104 shutil.rmtree(tmpdir) 5105 5106 def testExtblobList(self): 5107 """Test an image with an external blob list""" 5108 data = self._DoReadFile('215_blob_ext_list.dts') 5109 self.assertEqual(REFCODE_DATA + FSP_M_DATA, data) 5110 5111 def testExtblobListMissing(self): 5112 """Test an image with a missing external blob""" 5113 with self.assertRaises(ValueError) as e: 5114 self._DoReadFile('216_blob_ext_list_missing.dts') 5115 self.assertIn("Filename 'missing-file' not found in input path", 5116 str(e.exception)) 5117 5118 def testExtblobListMissingOk(self): 5119 """Test an image with an missing external blob that is allowed""" 5120 with test_util.capture_sys_output() as (stdout, stderr): 5121 self._DoTestFile('216_blob_ext_list_missing.dts', 5122 allow_missing=True) 5123 err = stderr.getvalue() 5124 self.assertRegex(err, "Image 'image'.*missing.*: blob-ext") 5125 5126 def testFip(self): 5127 """Basic test of generation of an ARM Firmware Image Package (FIP)""" 5128 data = self._DoReadFile('203_fip.dts') 5129 hdr, fents = fip_util.decode_fip(data) 5130 self.assertEqual(fip_util.HEADER_MAGIC, hdr.name) 5131 self.assertEqual(fip_util.HEADER_SERIAL, hdr.serial) 5132 self.assertEqual(0x123, hdr.flags) 5133 5134 self.assertEqual(2, len(fents)) 5135 5136 fent = fents[0] 5137 self.assertEqual( 5138 bytes([0x47, 0xd4, 0x08, 0x6d, 0x4c, 0xfe, 0x98, 0x46, 5139 0x9b, 0x95, 0x29, 0x50, 0xcb, 0xbd, 0x5a, 0x0]), fent.uuid) 5140 self.assertEqual('soc-fw', fent.fip_type) 5141 self.assertEqual(0x88, fent.offset) 5142 self.assertEqual(len(ATF_BL31_DATA), fent.size) 5143 self.assertEqual(0x123456789abcdef, fent.flags) 5144 self.assertEqual(ATF_BL31_DATA, fent.data) 5145 self.assertEqual(True, fent.valid) 5146 5147 fent = fents[1] 5148 self.assertEqual( 5149 bytes([0x65, 0x92, 0x27, 0x03, 0x2f, 0x74, 0xe6, 0x44, 5150 0x8d, 0xff, 0x57, 0x9a, 0xc1, 0xff, 0x06, 0x10]), fent.uuid) 5151 self.assertEqual('scp-fwu-cfg', fent.fip_type) 5152 self.assertEqual(0x8c, fent.offset) 5153 self.assertEqual(len(ATF_BL31_DATA), fent.size) 5154 self.assertEqual(0, fent.flags) 5155 self.assertEqual(ATF_BL2U_DATA, fent.data) 5156 self.assertEqual(True, fent.valid) 5157 5158 def testFipOther(self): 5159 """Basic FIP with something that isn't a external blob""" 5160 data = self._DoReadFile('204_fip_other.dts') 5161 hdr, fents = fip_util.decode_fip(data) 5162 5163 self.assertEqual(2, len(fents)) 5164 fent = fents[1] 5165 self.assertEqual('rot-cert', fent.fip_type) 5166 self.assertEqual(b'aa', fent.data) 5167 5168 def testFipNoType(self): 5169 """FIP with an entry of an unknown type""" 5170 with self.assertRaises(ValueError) as e: 5171 self._DoReadFile('205_fip_no_type.dts') 5172 self.assertIn("Must provide a fip-type (node name 'u-boot' is not a known FIP type)", 5173 str(e.exception)) 5174 5175 def testFipUuid(self): 5176 """Basic FIP with a manual uuid""" 5177 data = self._DoReadFile('206_fip_uuid.dts') 5178 hdr, fents = fip_util.decode_fip(data) 5179 5180 self.assertEqual(2, len(fents)) 5181 fent = fents[1] 5182 self.assertEqual(None, fent.fip_type) 5183 self.assertEqual( 5184 bytes([0xfc, 0x65, 0x13, 0x92, 0x4a, 0x5b, 0x11, 0xec, 5185 0x94, 0x35, 0xff, 0x2d, 0x1c, 0xfc, 0x79, 0x9c]), 5186 fent.uuid) 5187 self.assertEqual(U_BOOT_DATA, fent.data) 5188 5189 def testFipLs(self): 5190 """Test listing a FIP""" 5191 data = self._DoReadFileRealDtb('207_fip_ls.dts') 5192 hdr, fents = fip_util.decode_fip(data) 5193 5194 tmpdir = None 5195 try: 5196 tmpdir, updated_fname = self._SetupImageInTmpdir() 5197 with test_util.capture_sys_output() as (stdout, stderr): 5198 self._DoBinman('ls', '-i', updated_fname) 5199 finally: 5200 if tmpdir: 5201 shutil.rmtree(tmpdir) 5202 lines = stdout.getvalue().splitlines() 5203 expected = [ 5204'Name Image-pos Size Entry-type Offset Uncomp-size', 5205'--------------------------------------------------------------', 5206'image 0 2d3 section 0', 5207' atf-fip 0 90 atf-fip 0', 5208' soc-fw 88 4 blob-ext 88', 5209' u-boot 8c 4 u-boot 8c', 5210' fdtmap 90 243 fdtmap 90', 5211] 5212 self.assertEqual(expected, lines) 5213 5214 image = control.images['image'] 5215 entries = image.GetEntries() 5216 fdtmap = entries['fdtmap'] 5217 5218 fdtmap_data = data[fdtmap.image_pos:fdtmap.image_pos + fdtmap.size] 5219 magic = fdtmap_data[:8] 5220 self.assertEqual(b'_FDTMAP_', magic) 5221 self.assertEqual(tools.get_bytes(0, 8), fdtmap_data[8:16]) 5222 5223 fdt_data = fdtmap_data[16:] 5224 dtb = fdt.Fdt.FromData(fdt_data) 5225 dtb.Scan() 5226 props = self._GetPropTree(dtb, BASE_DTB_PROPS, prefix='/') 5227 self.assertEqual({ 5228 'atf-fip/soc-fw:image-pos': 136, 5229 'atf-fip/soc-fw:offset': 136, 5230 'atf-fip/soc-fw:size': 4, 5231 'atf-fip/u-boot:image-pos': 140, 5232 'atf-fip/u-boot:offset': 140, 5233 'atf-fip/u-boot:size': 4, 5234 'atf-fip:image-pos': 0, 5235 'atf-fip:offset': 0, 5236 'atf-fip:size': 144, 5237 'image-pos': 0, 5238 'offset': 0, 5239 'fdtmap:image-pos': fdtmap.image_pos, 5240 'fdtmap:offset': fdtmap.offset, 5241 'fdtmap:size': len(fdtmap_data), 5242 'size': len(data), 5243 }, props) 5244 5245 def testFipExtractOneEntry(self): 5246 """Test extracting a single entry fron an FIP""" 5247 self._DoReadFileRealDtb('207_fip_ls.dts') 5248 image_fname = tools.get_output_filename('image.bin') 5249 fname = os.path.join(self._indir, 'output.extact') 5250 control.ExtractEntries(image_fname, fname, None, ['atf-fip/u-boot']) 5251 data = tools.read_file(fname) 5252 self.assertEqual(U_BOOT_DATA, data) 5253 5254 def testFipReplace(self): 5255 """Test replacing a single file in a FIP""" 5256 expected = U_BOOT_DATA + tools.get_bytes(0x78, 50) 5257 data = self._DoReadFileRealDtb('208_fip_replace.dts') 5258 updated_fname = tools.get_output_filename('image-updated.bin') 5259 tools.write_file(updated_fname, data) 5260 entry_name = 'atf-fip/u-boot' 5261 control.WriteEntry(updated_fname, entry_name, expected, 5262 allow_resize=True) 5263 actual = control.ReadEntry(updated_fname, entry_name) 5264 self.assertEqual(expected, actual) 5265 5266 new_data = tools.read_file(updated_fname) 5267 hdr, fents = fip_util.decode_fip(new_data) 5268 5269 self.assertEqual(2, len(fents)) 5270 5271 # Check that the FIP entry is updated 5272 fent = fents[1] 5273 self.assertEqual(0x8c, fent.offset) 5274 self.assertEqual(len(expected), fent.size) 5275 self.assertEqual(0, fent.flags) 5276 self.assertEqual(expected, fent.data) 5277 self.assertEqual(True, fent.valid) 5278 5279 def testFipMissing(self): 5280 with test_util.capture_sys_output() as (stdout, stderr): 5281 self._DoTestFile('209_fip_missing.dts', allow_missing=True) 5282 err = stderr.getvalue() 5283 self.assertRegex(err, "Image 'image'.*missing.*: rmm-fw") 5284 5285 def testFipSize(self): 5286 """Test a FIP with a size property""" 5287 data = self._DoReadFile('210_fip_size.dts') 5288 self.assertEqual(0x100 + len(U_BOOT_DATA), len(data)) 5289 hdr, fents = fip_util.decode_fip(data) 5290 self.assertEqual(fip_util.HEADER_MAGIC, hdr.name) 5291 self.assertEqual(fip_util.HEADER_SERIAL, hdr.serial) 5292 5293 self.assertEqual(1, len(fents)) 5294 5295 fent = fents[0] 5296 self.assertEqual('soc-fw', fent.fip_type) 5297 self.assertEqual(0x60, fent.offset) 5298 self.assertEqual(len(ATF_BL31_DATA), fent.size) 5299 self.assertEqual(ATF_BL31_DATA, fent.data) 5300 self.assertEqual(True, fent.valid) 5301 5302 rest = data[0x60 + len(ATF_BL31_DATA):0x100] 5303 self.assertEqual(tools.get_bytes(0xff, len(rest)), rest) 5304 5305 def testFipBadAlign(self): 5306 """Test that an invalid alignment value in a FIP is detected""" 5307 with self.assertRaises(ValueError) as e: 5308 self._DoTestFile('211_fip_bad_align.dts') 5309 self.assertIn( 5310 "Node \'/binman/atf-fip\': FIP alignment 31 must be a power of two", 5311 str(e.exception)) 5312 5313 def testFipCollection(self): 5314 """Test using a FIP in a collection""" 5315 data = self._DoReadFile('212_fip_collection.dts') 5316 entry1 = control.images['image'].GetEntries()['collection'] 5317 data1 = data[:entry1.size] 5318 hdr1, fents2 = fip_util.decode_fip(data1) 5319 5320 entry2 = control.images['image'].GetEntries()['atf-fip'] 5321 data2 = data[entry2.offset:entry2.offset + entry2.size] 5322 hdr1, fents2 = fip_util.decode_fip(data2) 5323 5324 # The 'collection' entry should have U-Boot included at the end 5325 self.assertEqual(entry1.size - len(U_BOOT_DATA), entry2.size) 5326 self.assertEqual(data1, data2 + U_BOOT_DATA) 5327 self.assertEqual(U_BOOT_DATA, data1[-4:]) 5328 5329 # There should be a U-Boot after the final FIP 5330 self.assertEqual(U_BOOT_DATA, data[-4:]) 5331 5332 def testFakeBlob(self): 5333 """Test handling of faking an external blob""" 5334 with test_util.capture_sys_output() as (stdout, stderr): 5335 self._DoTestFile('217_fake_blob.dts', allow_missing=True, 5336 allow_fake_blobs=True) 5337 err = stderr.getvalue() 5338 self.assertRegex( 5339 err, 5340 "Image '.*' has faked external blobs and is non-functional: .*") 5341 5342 def testExtblobListFaked(self): 5343 """Test an extblob with missing external blob that are faked""" 5344 with test_util.capture_sys_output() as (stdout, stderr): 5345 self._DoTestFile('216_blob_ext_list_missing.dts', 5346 allow_fake_blobs=True) 5347 err = stderr.getvalue() 5348 self.assertRegex(err, "Image 'image'.*faked.*: blob-ext-list") 5349 5350 def testListBintools(self): 5351 args = ['tool', '--list'] 5352 with test_util.capture_sys_output() as (stdout, _): 5353 self._DoBinman(*args) 5354 out = stdout.getvalue().splitlines() 5355 self.assertTrue(len(out) >= 2) 5356 5357 def testFetchBintools(self): 5358 def fail_download(url): 5359 """Take the tools.download() function by raising an exception""" 5360 raise urllib.error.URLError('my error') 5361 5362 args = ['tool'] 5363 with self.assertRaises(ValueError) as e: 5364 self._DoBinman(*args) 5365 self.assertIn("Invalid arguments to 'tool' subcommand", 5366 str(e.exception)) 5367 5368 args = ['tool', '--fetch'] 5369 with self.assertRaises(ValueError) as e: 5370 self._DoBinman(*args) 5371 self.assertIn('Please specify bintools to fetch', str(e.exception)) 5372 5373 args = ['tool', '--fetch', '_testing'] 5374 with unittest.mock.patch.object(tools, 'download', 5375 side_effect=fail_download): 5376 with test_util.capture_sys_output() as (stdout, _): 5377 self._DoBinman(*args) 5378 self.assertIn('failed to fetch with all methods', stdout.getvalue()) 5379 5380 def testBintoolDocs(self): 5381 """Test for creation of bintool documentation""" 5382 with test_util.capture_sys_output() as (stdout, stderr): 5383 control.write_bintool_docs(control.bintool.Bintool.get_tool_list()) 5384 self.assertTrue(len(stdout.getvalue()) > 0) 5385 5386 def testBintoolDocsMissing(self): 5387 """Test handling of missing bintool documentation""" 5388 with self.assertRaises(ValueError) as e: 5389 with test_util.capture_sys_output() as (stdout, stderr): 5390 control.write_bintool_docs( 5391 control.bintool.Bintool.get_tool_list(), 'mkimage') 5392 self.assertIn('Documentation is missing for modules: mkimage', 5393 str(e.exception)) 5394 5395 def testListWithGenNode(self): 5396 """Check handling of an FDT map when the section cannot be found""" 5397 entry_args = { 5398 'of-list': 'test-fdt1 test-fdt2', 5399 } 5400 data = self._DoReadFileDtb( 5401 '219_fit_gennode.dts', 5402 entry_args=entry_args, 5403 use_real_dtb=True, 5404 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)]) 5405 5406 tmpdir = None 5407 try: 5408 tmpdir, updated_fname = self._SetupImageInTmpdir() 5409 with test_util.capture_sys_output() as (stdout, stderr): 5410 self._RunBinman('ls', '-i', updated_fname) 5411 finally: 5412 if tmpdir: 5413 shutil.rmtree(tmpdir) 5414 5415 def testFitSubentryUsesBintool(self): 5416 """Test that binman FIT subentries can use bintools""" 5417 command.test_result = self._HandleGbbCommand 5418 entry_args = { 5419 'keydir': 'devkeys', 5420 'bmpblk': 'bmpblk.bin', 5421 } 5422 data, _, _, _ = self._DoReadFileDtb('220_fit_subentry_bintool.dts', 5423 entry_args=entry_args) 5424 5425 expected = (GBB_DATA + GBB_DATA + tools.get_bytes(0, 8) + 5426 tools.get_bytes(0, 0x2180 - 16)) 5427 self.assertIn(expected, data) 5428 5429 def testFitSubentryMissingBintool(self): 5430 """Test that binman reports missing bintools for FIT subentries""" 5431 entry_args = { 5432 'keydir': 'devkeys', 5433 } 5434 with test_util.capture_sys_output() as (_, stderr): 5435 self._DoTestFile('220_fit_subentry_bintool.dts', 5436 force_missing_bintools='futility', entry_args=entry_args) 5437 err = stderr.getvalue() 5438 self.assertRegex(err, "Image 'image'.*missing bintools.*: futility") 5439 5440 def testFitSubentryHashSubnode(self): 5441 """Test an image with a FIT inside""" 5442 self._SetupSplElf() 5443 data, _, _, out_dtb_name = self._DoReadFileDtb( 5444 '221_fit_subentry_hash.dts', use_real_dtb=True, update_dtb=True) 5445 5446 mkimage_dtb = fdt.Fdt.FromData(data) 5447 mkimage_dtb.Scan() 5448 binman_dtb = fdt.Fdt(out_dtb_name) 5449 binman_dtb.Scan() 5450 5451 # Check that binman didn't add hash values 5452 fnode = binman_dtb.GetNode('/binman/fit/images/kernel/hash') 5453 self.assertNotIn('value', fnode.props) 5454 5455 fnode = binman_dtb.GetNode('/binman/fit/images/fdt-1/hash') 5456 self.assertNotIn('value', fnode.props) 5457 5458 # Check that mkimage added hash values 5459 fnode = mkimage_dtb.GetNode('/images/kernel/hash') 5460 self.assertIn('value', fnode.props) 5461 5462 fnode = mkimage_dtb.GetNode('/images/fdt-1/hash') 5463 self.assertIn('value', fnode.props) 5464 5465 def testPackTeeOs(self): 5466 """Test that an image with an TEE binary can be created""" 5467 data = self._DoReadFile('222_tee_os.dts') 5468 self.assertEqual(TEE_OS_DATA, data[:len(TEE_OS_DATA)]) 5469 5470 def testPackTiDm(self): 5471 """Test that an image with a TI DM binary can be created""" 5472 data = self._DoReadFile('225_ti_dm.dts') 5473 self.assertEqual(TI_DM_DATA, data[:len(TI_DM_DATA)]) 5474 5475 def testFitFdtOper(self): 5476 """Check handling of a specified FIT operation""" 5477 entry_args = { 5478 'of-list': 'test-fdt1 test-fdt2', 5479 'default-dt': 'test-fdt2', 5480 } 5481 self._DoReadFileDtb( 5482 '223_fit_fdt_oper.dts', 5483 entry_args=entry_args, 5484 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0] 5485 5486 def testFitFdtBadOper(self): 5487 """Check handling of an FDT map when the section cannot be found""" 5488 with self.assertRaises(ValueError) as exc: 5489 self._DoReadFileDtb('224_fit_bad_oper.dts') 5490 self.assertIn("Node '/binman/fit': subnode 'images/@fdt-SEQ': Unknown operation 'unknown'", 5491 str(exc.exception)) 5492 5493 def test_uses_expand_size(self): 5494 """Test that the 'expand-size' property cannot be used anymore""" 5495 with self.assertRaises(ValueError) as e: 5496 data = self._DoReadFile('225_expand_size_bad.dts') 5497 self.assertIn( 5498 "Node '/binman/u-boot': Please use 'extend-size' instead of 'expand-size'", 5499 str(e.exception)) 5500 5501 def testFitSplitElf(self): 5502 """Test an image with an FIT with an split-elf operation""" 5503 if not elf.ELF_TOOLS: 5504 self.skipTest('Python elftools not available') 5505 entry_args = { 5506 'of-list': 'test-fdt1 test-fdt2', 5507 'default-dt': 'test-fdt2', 5508 'atf-bl31-path': 'bl31.elf', 5509 'tee-os-path': 'tee.elf', 5510 } 5511 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR) 5512 data = self._DoReadFileDtb( 5513 '226_fit_split_elf.dts', 5514 entry_args=entry_args, 5515 extra_indirs=[test_subdir])[0] 5516 5517 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):]) 5518 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)] 5519 5520 base_keys = {'description', 'type', 'arch', 'os', 'compression', 5521 'data', 'load'} 5522 dtb = fdt.Fdt.FromData(fit_data) 5523 dtb.Scan() 5524 5525 elf_data = tools.read_file(os.path.join(self._indir, 'bl31.elf')) 5526 segments, entry = elf.read_loadable_segments(elf_data) 5527 5528 # We assume there are two segments 5529 self.assertEquals(2, len(segments)) 5530 5531 atf1 = dtb.GetNode('/images/atf-1') 5532 _, start, data = segments[0] 5533 self.assertEqual(base_keys | {'entry'}, atf1.props.keys()) 5534 self.assertEqual(entry, 5535 fdt_util.fdt32_to_cpu(atf1.props['entry'].value)) 5536 self.assertEqual(start, 5537 fdt_util.fdt32_to_cpu(atf1.props['load'].value)) 5538 self.assertEqual(data, atf1.props['data'].bytes) 5539 5540 hash_node = atf1.FindNode('hash') 5541 self.assertIsNotNone(hash_node) 5542 self.assertEqual({'algo', 'value'}, hash_node.props.keys()) 5543 5544 atf2 = dtb.GetNode('/images/atf-2') 5545 self.assertEqual(base_keys, atf2.props.keys()) 5546 _, start, data = segments[1] 5547 self.assertEqual(start, 5548 fdt_util.fdt32_to_cpu(atf2.props['load'].value)) 5549 self.assertEqual(data, atf2.props['data'].bytes) 5550 5551 hash_node = atf2.FindNode('hash') 5552 self.assertIsNotNone(hash_node) 5553 self.assertEqual({'algo', 'value'}, hash_node.props.keys()) 5554 5555 hash_node = dtb.GetNode('/images/tee-1/hash-1') 5556 self.assertIsNotNone(hash_node) 5557 self.assertEqual({'algo', 'value'}, hash_node.props.keys()) 5558 5559 conf = dtb.GetNode('/configurations') 5560 self.assertEqual({'default'}, conf.props.keys()) 5561 5562 for subnode in conf.subnodes: 5563 self.assertEqual({'description', 'fdt', 'loadables'}, 5564 subnode.props.keys()) 5565 self.assertEqual( 5566 ['atf-1', 'atf-2', 'tee-1', 'tee-2'], 5567 fdt_util.GetStringList(subnode, 'loadables')) 5568 5569 def _check_bad_fit(self, dts): 5570 """Check a bad FIT 5571 5572 This runs with the given dts and returns the assertion raised 5573 5574 Args: 5575 dts (str): dts filename to use 5576 5577 Returns: 5578 str: Assertion string raised 5579 """ 5580 entry_args = { 5581 'of-list': 'test-fdt1 test-fdt2', 5582 'default-dt': 'test-fdt2', 5583 'atf-bl31-path': 'bl31.elf', 5584 'tee-os-path': 'tee.elf', 5585 } 5586 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR) 5587 with self.assertRaises(ValueError) as exc: 5588 self._DoReadFileDtb(dts, entry_args=entry_args, 5589 extra_indirs=[test_subdir])[0] 5590 return str(exc.exception) 5591 5592 def testFitSplitElfBadElf(self): 5593 """Test a FIT split-elf operation with an invalid ELF file""" 5594 if not elf.ELF_TOOLS: 5595 self.skipTest('Python elftools not available') 5596 TestFunctional._MakeInputFile('bad.elf', tools.get_bytes(100, 100)) 5597 entry_args = { 5598 'of-list': 'test-fdt1 test-fdt2', 5599 'default-dt': 'test-fdt2', 5600 'atf-bl31-path': 'bad.elf', 5601 'tee-os-path': 'tee.elf', 5602 } 5603 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR) 5604 with self.assertRaises(ValueError) as exc: 5605 self._DoReadFileDtb( 5606 '226_fit_split_elf.dts', 5607 entry_args=entry_args, 5608 extra_indirs=[test_subdir])[0] 5609 self.assertIn( 5610 "Node '/binman/fit': subnode 'images/@atf-SEQ': Failed to read ELF file: Magic number does not match", 5611 str(exc.exception)) 5612 5613 def checkFitSplitElf(self, **kwargs): 5614 """Test an split-elf FIT with a missing ELF file 5615 5616 Args: 5617 kwargs (dict of str): Arguments to pass to _DoTestFile() 5618 5619 Returns: 5620 tuple: 5621 str: stdout result 5622 str: stderr result 5623 """ 5624 entry_args = { 5625 'of-list': 'test-fdt1 test-fdt2', 5626 'default-dt': 'test-fdt2', 5627 'atf-bl31-path': 'bl31.elf', 5628 'tee-os-path': 'missing.elf', 5629 } 5630 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR) 5631 with test_util.capture_sys_output() as (stdout, stderr): 5632 self._DoTestFile( 5633 '226_fit_split_elf.dts', entry_args=entry_args, 5634 extra_indirs=[test_subdir], verbosity=3, **kwargs) 5635 out = stdout.getvalue() 5636 err = stderr.getvalue() 5637 return out, err 5638 5639 def testFitSplitElfBadDirective(self): 5640 """Test a FIT split-elf invalid fit,xxx directive in an image node""" 5641 if not elf.ELF_TOOLS: 5642 self.skipTest('Python elftools not available') 5643 err = self._check_bad_fit('227_fit_bad_dir.dts') 5644 self.assertIn( 5645 "Node '/binman/fit': subnode 'images/@atf-SEQ': Unknown directive 'fit,something'", 5646 err) 5647 5648 def testFitSplitElfBadDirectiveConfig(self): 5649 """Test a FIT split-elf with invalid fit,xxx directive in config""" 5650 if not elf.ELF_TOOLS: 5651 self.skipTest('Python elftools not available') 5652 err = self._check_bad_fit('228_fit_bad_dir_config.dts') 5653 self.assertEqual( 5654 "Node '/binman/fit': subnode 'configurations/@config-SEQ': Unknown directive 'fit,config'", 5655 err) 5656 5657 5658 def testFitSplitElfMissing(self): 5659 """Test an split-elf FIT with a missing ELF file""" 5660 if not elf.ELF_TOOLS: 5661 self.skipTest('Python elftools not available') 5662 out, err = self.checkFitSplitElf(allow_missing=True) 5663 self.assertRegex( 5664 err, 5665 "Image '.*' is missing external blobs and is non-functional: .*") 5666 self.assertNotRegex(out, '.*Faked blob.*') 5667 fname = tools.get_output_filename('binman-fake/missing.elf') 5668 self.assertFalse(os.path.exists(fname)) 5669 5670 def testFitSplitElfFaked(self): 5671 """Test an split-elf FIT with faked ELF file""" 5672 if not elf.ELF_TOOLS: 5673 self.skipTest('Python elftools not available') 5674 out, err = self.checkFitSplitElf(allow_missing=True, allow_fake_blobs=True) 5675 self.assertRegex( 5676 err, 5677 "Image '.*' is missing external blobs and is non-functional: .*") 5678 self.assertRegex( 5679 out, 5680 "Entry '/binman/fit/images/@tee-SEQ/tee-os': Faked blob '.*binman-fake/missing.elf") 5681 fname = tools.get_output_filename('binman-fake/missing.elf') 5682 self.assertTrue(os.path.exists(fname)) 5683 5684 def testMkimageMissingBlob(self): 5685 """Test using mkimage to build an image""" 5686 with test_util.capture_sys_output() as (stdout, stderr): 5687 self._DoTestFile('229_mkimage_missing.dts', allow_missing=True, 5688 allow_fake_blobs=True) 5689 err = stderr.getvalue() 5690 self.assertRegex( 5691 err, 5692 "Image '.*' has faked external blobs and is non-functional: .*") 5693 5694 def testPreLoad(self): 5695 """Test an image with a pre-load header""" 5696 entry_args = { 5697 'pre-load-key-path': os.path.join(self._binman_dir, 'test'), 5698 } 5699 data = self._DoReadFileDtb( 5700 '230_pre_load.dts', entry_args=entry_args, 5701 extra_indirs=[os.path.join(self._binman_dir, 'test')])[0] 5702 self.assertEqual(PRE_LOAD_MAGIC, data[:len(PRE_LOAD_MAGIC)]) 5703 self.assertEqual(PRE_LOAD_VERSION, data[4:4 + len(PRE_LOAD_VERSION)]) 5704 self.assertEqual(PRE_LOAD_HDR_SIZE, data[8:8 + len(PRE_LOAD_HDR_SIZE)]) 5705 5706 def testPreLoadNoKey(self): 5707 """Test an image with a pre-load heade0r with missing key""" 5708 with self.assertRaises(FileNotFoundError) as exc: 5709 self._DoReadFile('230_pre_load.dts') 5710 self.assertIn("No such file or directory: 'dev.key'", 5711 str(exc.exception)) 5712 5713 def testPreLoadPkcs(self): 5714 """Test an image with a pre-load header with padding pkcs""" 5715 entry_args = { 5716 'pre-load-key-path': os.path.join(self._binman_dir, 'test'), 5717 } 5718 data = self._DoReadFileDtb('231_pre_load_pkcs.dts', 5719 entry_args=entry_args)[0] 5720 self.assertEqual(PRE_LOAD_MAGIC, data[:len(PRE_LOAD_MAGIC)]) 5721 self.assertEqual(PRE_LOAD_VERSION, data[4:4 + len(PRE_LOAD_VERSION)]) 5722 self.assertEqual(PRE_LOAD_HDR_SIZE, data[8:8 + len(PRE_LOAD_HDR_SIZE)]) 5723 5724 def testPreLoadPss(self): 5725 """Test an image with a pre-load header with padding pss""" 5726 entry_args = { 5727 'pre-load-key-path': os.path.join(self._binman_dir, 'test'), 5728 } 5729 data = self._DoReadFileDtb('232_pre_load_pss.dts', 5730 entry_args=entry_args)[0] 5731 self.assertEqual(PRE_LOAD_MAGIC, data[:len(PRE_LOAD_MAGIC)]) 5732 self.assertEqual(PRE_LOAD_VERSION, data[4:4 + len(PRE_LOAD_VERSION)]) 5733 self.assertEqual(PRE_LOAD_HDR_SIZE, data[8:8 + len(PRE_LOAD_HDR_SIZE)]) 5734 5735 def testPreLoadInvalidPadding(self): 5736 """Test an image with a pre-load header with an invalid padding""" 5737 entry_args = { 5738 'pre-load-key-path': os.path.join(self._binman_dir, 'test'), 5739 } 5740 with self.assertRaises(ValueError) as e: 5741 self._DoReadFileDtb('233_pre_load_invalid_padding.dts', 5742 entry_args=entry_args) 5743 5744 def testPreLoadInvalidSha(self): 5745 """Test an image with a pre-load header with an invalid hash""" 5746 entry_args = { 5747 'pre-load-key-path': os.path.join(self._binman_dir, 'test'), 5748 } 5749 with self.assertRaises(ValueError) as e: 5750 self._DoReadFileDtb('234_pre_load_invalid_sha.dts', 5751 entry_args=entry_args) 5752 5753 def testPreLoadInvalidAlgo(self): 5754 """Test an image with a pre-load header with an invalid algo""" 5755 with self.assertRaises(ValueError) as e: 5756 data = self._DoReadFile('235_pre_load_invalid_algo.dts') 5757 5758 def testPreLoadInvalidKey(self): 5759 """Test an image with a pre-load header with an invalid key""" 5760 entry_args = { 5761 'pre-load-key-path': os.path.join(self._binman_dir, 'test'), 5762 } 5763 with self.assertRaises(ValueError) as e: 5764 data = self._DoReadFileDtb('236_pre_load_invalid_key.dts', 5765 entry_args=entry_args) 5766 5767 def _CheckSafeUniqueNames(self, *images): 5768 """Check all entries of given images for unsafe unique names""" 5769 for image in images: 5770 entries = {} 5771 image._CollectEntries(entries, {}, image) 5772 for entry in entries.values(): 5773 uniq = entry.GetUniqueName() 5774 5775 # Used as part of a filename, so must not be absolute paths. 5776 self.assertFalse(os.path.isabs(uniq)) 5777 5778 def testSafeUniqueNames(self): 5779 """Test entry unique names are safe in single image configuration""" 5780 data = self._DoReadFileRealDtb('237_unique_names.dts') 5781 5782 orig_image = control.images['image'] 5783 image_fname = tools.get_output_filename('image.bin') 5784 image = Image.FromFile(image_fname) 5785 5786 self._CheckSafeUniqueNames(orig_image, image) 5787 5788 def testSafeUniqueNamesMulti(self): 5789 """Test entry unique names are safe with multiple images""" 5790 data = self._DoReadFileRealDtb('238_unique_names_multi.dts') 5791 5792 orig_image = control.images['image'] 5793 image_fname = tools.get_output_filename('image.bin') 5794 image = Image.FromFile(image_fname) 5795 5796 self._CheckSafeUniqueNames(orig_image, image) 5797 5798 def testReplaceCmdWithBintool(self): 5799 """Test replacing an entry that needs a bintool to pack""" 5800 data = self._DoReadFileRealDtb('239_replace_with_bintool.dts') 5801 expected = U_BOOT_DATA + b'aa' 5802 self.assertEqual(expected, data[:len(expected)]) 5803 5804 try: 5805 tmpdir, updated_fname = self._SetupImageInTmpdir() 5806 fname = os.path.join(tmpdir, 'update-testing.bin') 5807 tools.write_file(fname, b'zz') 5808 self._DoBinman('replace', '-i', updated_fname, 5809 '_testing', '-f', fname) 5810 5811 data = tools.read_file(updated_fname) 5812 expected = U_BOOT_DATA + b'zz' 5813 self.assertEqual(expected, data[:len(expected)]) 5814 finally: 5815 shutil.rmtree(tmpdir) 5816 5817 def testReplaceCmdOtherWithBintool(self): 5818 """Test replacing an entry when another needs a bintool to pack""" 5819 data = self._DoReadFileRealDtb('239_replace_with_bintool.dts') 5820 expected = U_BOOT_DATA + b'aa' 5821 self.assertEqual(expected, data[:len(expected)]) 5822 5823 try: 5824 tmpdir, updated_fname = self._SetupImageInTmpdir() 5825 fname = os.path.join(tmpdir, 'update-u-boot.bin') 5826 tools.write_file(fname, b'x' * len(U_BOOT_DATA)) 5827 self._DoBinman('replace', '-i', updated_fname, 5828 'u-boot', '-f', fname) 5829 5830 data = tools.read_file(updated_fname) 5831 expected = b'x' * len(U_BOOT_DATA) + b'aa' 5832 self.assertEqual(expected, data[:len(expected)]) 5833 finally: 5834 shutil.rmtree(tmpdir) 5835 5836 def testReplaceResizeNoRepackSameSize(self): 5837 """Test replacing entries with same-size data without repacking""" 5838 expected = b'x' * len(U_BOOT_DATA) 5839 data, expected_fdtmap, _ = self._RunReplaceCmd('u-boot', expected) 5840 self.assertEqual(expected, data) 5841 5842 path, fdtmap = state.GetFdtContents('fdtmap') 5843 self.assertIsNotNone(path) 5844 self.assertEqual(expected_fdtmap, fdtmap) 5845 5846 def testReplaceResizeNoRepackSmallerSize(self): 5847 """Test replacing entries with smaller-size data without repacking""" 5848 new_data = b'x' 5849 data, expected_fdtmap, _ = self._RunReplaceCmd('u-boot', new_data) 5850 expected = new_data.ljust(len(U_BOOT_DATA), b'\0') 5851 self.assertEqual(expected, data) 5852 5853 path, fdtmap = state.GetFdtContents('fdtmap') 5854 self.assertIsNotNone(path) 5855 self.assertEqual(expected_fdtmap, fdtmap) 5856 5857 def testExtractFit(self): 5858 """Test extracting a FIT section""" 5859 self._DoReadFileRealDtb('240_fit_extract_replace.dts') 5860 image_fname = tools.get_output_filename('image.bin') 5861 5862 fit_data = control.ReadEntry(image_fname, 'fit') 5863 fit = fdt.Fdt.FromData(fit_data) 5864 fit.Scan() 5865 5866 # Check subentry data inside the extracted fit 5867 for node_path, expected in [ 5868 ('/images/kernel', U_BOOT_DATA), 5869 ('/images/fdt-1', U_BOOT_NODTB_DATA), 5870 ('/images/scr-1', COMPRESS_DATA), 5871 ]: 5872 node = fit.GetNode(node_path) 5873 data = fit.GetProps(node)['data'].bytes 5874 self.assertEqual(expected, data) 5875 5876 def testExtractFitSubentries(self): 5877 """Test extracting FIT section subentries""" 5878 self._DoReadFileRealDtb('240_fit_extract_replace.dts') 5879 image_fname = tools.get_output_filename('image.bin') 5880 5881 for entry_path, expected in [ 5882 ('fit/kernel', U_BOOT_DATA), 5883 ('fit/kernel/u-boot', U_BOOT_DATA), 5884 ('fit/fdt-1', U_BOOT_NODTB_DATA), 5885 ('fit/fdt-1/u-boot-nodtb', U_BOOT_NODTB_DATA), 5886 ('fit/scr-1', COMPRESS_DATA), 5887 ('fit/scr-1/blob', COMPRESS_DATA), 5888 ]: 5889 data = control.ReadEntry(image_fname, entry_path) 5890 self.assertEqual(expected, data) 5891 5892 def testReplaceFitSubentryLeafSameSize(self): 5893 """Test replacing a FIT leaf subentry with same-size data""" 5894 new_data = b'x' * len(U_BOOT_DATA) 5895 data, expected_fdtmap, _ = self._RunReplaceCmd( 5896 'fit/kernel/u-boot', new_data, 5897 dts='240_fit_extract_replace.dts') 5898 self.assertEqual(new_data, data) 5899 5900 path, fdtmap = state.GetFdtContents('fdtmap') 5901 self.assertIsNotNone(path) 5902 self.assertEqual(expected_fdtmap, fdtmap) 5903 5904 def testReplaceFitSubentryLeafBiggerSize(self): 5905 """Test replacing a FIT leaf subentry with bigger-size data""" 5906 new_data = b'ub' * len(U_BOOT_NODTB_DATA) 5907 data, expected_fdtmap, _ = self._RunReplaceCmd( 5908 'fit/fdt-1/u-boot-nodtb', new_data, 5909 dts='240_fit_extract_replace.dts') 5910 self.assertEqual(new_data, data) 5911 5912 # Will be repacked, so fdtmap must change 5913 path, fdtmap = state.GetFdtContents('fdtmap') 5914 self.assertIsNotNone(path) 5915 self.assertNotEqual(expected_fdtmap, fdtmap) 5916 5917 def testReplaceFitSubentryLeafSmallerSize(self): 5918 """Test replacing a FIT leaf subentry with smaller-size data""" 5919 new_data = b'x' 5920 expected = new_data.ljust(len(U_BOOT_NODTB_DATA), b'\0') 5921 data, expected_fdtmap, _ = self._RunReplaceCmd( 5922 'fit/fdt-1/u-boot-nodtb', new_data, 5923 dts='240_fit_extract_replace.dts') 5924 self.assertEqual(expected, data) 5925 5926 path, fdtmap = state.GetFdtContents('fdtmap') 5927 self.assertIsNotNone(path) 5928 self.assertEqual(expected_fdtmap, fdtmap) 5929 5930 def testReplaceSectionSimple(self): 5931 """Test replacing a simple section with same-sized data""" 5932 new_data = b'w' * len(COMPRESS_DATA + U_BOOT_DATA) 5933 data, expected_fdtmap, image = self._RunReplaceCmd('section', 5934 new_data, dts='241_replace_section_simple.dts') 5935 self.assertEqual(new_data, data) 5936 5937 entries = image.GetEntries() 5938 self.assertIn('section', entries) 5939 entry = entries['section'] 5940 self.assertEqual(len(new_data), entry.size) 5941 5942 def testReplaceSectionLarger(self): 5943 """Test replacing a simple section with larger data""" 5944 new_data = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) + 1) 5945 data, expected_fdtmap, image = self._RunReplaceCmd('section', 5946 new_data, dts='241_replace_section_simple.dts') 5947 self.assertEqual(new_data, data) 5948 5949 entries = image.GetEntries() 5950 self.assertIn('section', entries) 5951 entry = entries['section'] 5952 self.assertEqual(len(new_data), entry.size) 5953 fentry = entries['fdtmap'] 5954 self.assertEqual(entry.offset + entry.size, fentry.offset) 5955 5956 def testReplaceSectionSmaller(self): 5957 """Test replacing a simple section with smaller data""" 5958 new_data = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) - 1) + b'\0' 5959 data, expected_fdtmap, image = self._RunReplaceCmd('section', 5960 new_data, dts='241_replace_section_simple.dts') 5961 self.assertEqual(new_data, data) 5962 5963 # The new size is the same as the old, just with a pad byte at the end 5964 entries = image.GetEntries() 5965 self.assertIn('section', entries) 5966 entry = entries['section'] 5967 self.assertEqual(len(new_data), entry.size) 5968 5969 def testReplaceSectionSmallerAllow(self): 5970 """Test failing to replace a simple section with smaller data""" 5971 new_data = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) - 1) 5972 try: 5973 state.SetAllowEntryContraction(True) 5974 with self.assertRaises(ValueError) as exc: 5975 self._RunReplaceCmd('section', new_data, 5976 dts='241_replace_section_simple.dts') 5977 finally: 5978 state.SetAllowEntryContraction(False) 5979 5980 # Since we have no information about the position of things within the 5981 # section, we cannot adjust the position of /section-u-boot so it ends 5982 # up outside the section 5983 self.assertIn( 5984 "Node '/section/u-boot': Offset 0x24 (36) size 0x4 (4) is outside " 5985 "the section '/section' starting at 0x0 (0) of size 0x27 (39)", 5986 str(exc.exception)) 5987 5988 def testMkimageImagename(self): 5989 """Test using mkimage with -n holding the data too""" 5990 self._SetupSplElf() 5991 data = self._DoReadFile('242_mkimage_name.dts') 5992 5993 # Check that the data appears in the file somewhere 5994 self.assertIn(U_BOOT_SPL_DATA, data) 5995 5996 # Get struct legacy_img_hdr -> ih_name 5997 name = data[0x20:0x40] 5998 5999 # Build the filename that we expect to be placed in there, by virtue of 6000 # the -n paraameter 6001 expect = os.path.join(tools.get_output_dir(), 'mkimage.mkimage') 6002 6003 # Check that the image name is set to the temporary filename used 6004 self.assertEqual(expect.encode('utf-8')[:0x20], name) 6005 6006 def testMkimageImage(self): 6007 """Test using mkimage with -n holding the data too""" 6008 self._SetupSplElf() 6009 data = self._DoReadFile('243_mkimage_image.dts') 6010 6011 # Check that the data appears in the file somewhere 6012 self.assertIn(U_BOOT_SPL_DATA, data) 6013 6014 # Get struct legacy_img_hdr -> ih_name 6015 name = data[0x20:0x40] 6016 6017 # Build the filename that we expect to be placed in there, by virtue of 6018 # the -n paraameter 6019 expect = os.path.join(tools.get_output_dir(), 'mkimage-n.mkimage') 6020 6021 # Check that the image name is set to the temporary filename used 6022 self.assertEqual(expect.encode('utf-8')[:0x20], name) 6023 6024 # Check the corect data is in the imagename file 6025 self.assertEqual(U_BOOT_DATA, tools.read_file(expect)) 6026 6027 def testMkimageImageNoContent(self): 6028 """Test using mkimage with -n and no data""" 6029 self._SetupSplElf() 6030 with self.assertRaises(ValueError) as exc: 6031 self._DoReadFile('244_mkimage_image_no_content.dts') 6032 self.assertIn('Could not complete processing of contents', 6033 str(exc.exception)) 6034 6035 def testMkimageImageBad(self): 6036 """Test using mkimage with imagename node and data-to-imagename""" 6037 self._SetupSplElf() 6038 with self.assertRaises(ValueError) as exc: 6039 self._DoReadFile('245_mkimage_image_bad.dts') 6040 self.assertIn('Cannot use both imagename node and data-to-imagename', 6041 str(exc.exception)) 6042 6043 def testCollectionOther(self): 6044 """Test a collection where the data comes from another section""" 6045 data = self._DoReadFile('246_collection_other.dts') 6046 self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA + 6047 tools.get_bytes(0xff, 2) + U_BOOT_NODTB_DATA + 6048 tools.get_bytes(0xfe, 3) + U_BOOT_DTB_DATA, 6049 data) 6050 6051 def testMkimageCollection(self): 6052 """Test using a collection referring to an entry in a mkimage entry""" 6053 self._SetupSplElf() 6054 data = self._DoReadFile('247_mkimage_coll.dts') 6055 expect = U_BOOT_SPL_DATA + U_BOOT_DATA 6056 self.assertEqual(expect, data[:len(expect)]) 6057 6058 def testCompressDtbPrependInvalid(self): 6059 """Test that invalid header is detected""" 6060 with self.assertRaises(ValueError) as e: 6061 self._DoReadFileDtb('248_compress_dtb_prepend_invalid.dts') 6062 self.assertIn("Node '/binman/u-boot-dtb': Invalid prepend in " 6063 "'u-boot-dtb': 'invalid'", str(e.exception)) 6064 6065 def testCompressDtbPrependLength(self): 6066 """Test that compress with length header works as expected""" 6067 data = self._DoReadFileRealDtb('249_compress_dtb_prepend_length.dts') 6068 image = control.images['image'] 6069 entries = image.GetEntries() 6070 self.assertIn('u-boot-dtb', entries) 6071 u_boot_dtb = entries['u-boot-dtb'] 6072 self.assertIn('fdtmap', entries) 6073 fdtmap = entries['fdtmap'] 6074 6075 image_fname = tools.get_output_filename('image.bin') 6076 orig = control.ReadEntry(image_fname, 'u-boot-dtb') 6077 dtb = fdt.Fdt.FromData(orig) 6078 dtb.Scan() 6079 props = self._GetPropTree(dtb, ['size', 'uncomp-size']) 6080 expected = { 6081 'u-boot:size': len(U_BOOT_DATA), 6082 'u-boot-dtb:uncomp-size': len(orig), 6083 'u-boot-dtb:size': u_boot_dtb.size, 6084 'fdtmap:size': fdtmap.size, 6085 'size': len(data), 6086 } 6087 self.assertEqual(expected, props) 6088 6089 # Check implementation 6090 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)]) 6091 rest = data[len(U_BOOT_DATA):] 6092 comp_data_len = struct.unpack('<I', rest[:4])[0] 6093 comp_data = rest[4:4 + comp_data_len] 6094 orig2 = self._decompress(comp_data) 6095 self.assertEqual(orig, orig2) 6096 6097 def testInvalidCompress(self): 6098 """Test that invalid compress algorithm is detected""" 6099 with self.assertRaises(ValueError) as e: 6100 self._DoTestFile('250_compress_dtb_invalid.dts') 6101 self.assertIn("Unknown algorithm 'invalid'", str(e.exception)) 6102 6103 def testCompUtilCompressions(self): 6104 """Test compression algorithms""" 6105 for bintool in self.comp_bintools.values(): 6106 self._CheckBintool(bintool) 6107 data = bintool.compress(COMPRESS_DATA) 6108 self.assertNotEqual(COMPRESS_DATA, data) 6109 orig = bintool.decompress(data) 6110 self.assertEquals(COMPRESS_DATA, orig) 6111 6112 def testCompUtilVersions(self): 6113 """Test tool version of compression algorithms""" 6114 for bintool in self.comp_bintools.values(): 6115 self._CheckBintool(bintool) 6116 version = bintool.version() 6117 self.assertRegex(version, '^v?[0-9]+[0-9.]*') 6118 6119 def testCompUtilPadding(self): 6120 """Test padding of compression algorithms""" 6121 # Skip zstd because it doesn't support padding 6122 for bintool in [v for k,v in self.comp_bintools.items() if k != 'zstd']: 6123 self._CheckBintool(bintool) 6124 data = bintool.compress(COMPRESS_DATA) 6125 self.assertNotEqual(COMPRESS_DATA, data) 6126 data += tools.get_bytes(0, 64) 6127 orig = bintool.decompress(data) 6128 self.assertEquals(COMPRESS_DATA, orig) 6129 6130 def testCompressDtbZstd(self): 6131 """Test that zstd compress of device-tree files failed""" 6132 with self.assertRaises(ValueError) as e: 6133 self._DoTestFile('251_compress_dtb_zstd.dts') 6134 self.assertIn("Node '/binman/u-boot-dtb': The zstd compression " 6135 "requires a length header", str(e.exception)) 6136 6137 def testMkimageMultipleDataFiles(self): 6138 """Test passing multiple files to mkimage in a mkimage entry""" 6139 self._SetupSplElf() 6140 self._SetupTplElf() 6141 data = self._DoReadFile('252_mkimage_mult_data.dts') 6142 # Size of files are packed in their 4B big-endian format 6143 expect = struct.pack('>I', len(U_BOOT_TPL_DATA)) 6144 expect += struct.pack('>I', len(U_BOOT_SPL_DATA)) 6145 # Size info is always followed by a 4B zero value. 6146 expect += tools.get_bytes(0, 4) 6147 expect += U_BOOT_TPL_DATA 6148 # All but last files are 4B-aligned 6149 align_pad = len(U_BOOT_TPL_DATA) % 4 6150 if align_pad: 6151 expect += tools.get_bytes(0, align_pad) 6152 expect += U_BOOT_SPL_DATA 6153 self.assertEqual(expect, data[-len(expect):]) 6154 6155 def testMkimageMultipleExpanded(self): 6156 """Test passing multiple files to mkimage in a mkimage entry""" 6157 self._SetupSplElf() 6158 self._SetupTplElf() 6159 entry_args = { 6160 'spl-bss-pad': 'y', 6161 'spl-dtb': 'y', 6162 } 6163 data = self._DoReadFileDtb('252_mkimage_mult_data.dts', 6164 use_expanded=True, entry_args=entry_args)[0] 6165 pad_len = 10 6166 tpl_expect = U_BOOT_TPL_DATA 6167 spl_expect = U_BOOT_SPL_NODTB_DATA + tools.get_bytes(0, pad_len) 6168 spl_expect += U_BOOT_SPL_DTB_DATA 6169 6170 content = data[0x40:] 6171 lens = struct.unpack('>III', content[:12]) 6172 6173 # Size of files are packed in their 4B big-endian format 6174 # Size info is always followed by a 4B zero value. 6175 self.assertEqual(len(tpl_expect), lens[0]) 6176 self.assertEqual(len(spl_expect), lens[1]) 6177 self.assertEqual(0, lens[2]) 6178 6179 rest = content[12:] 6180 self.assertEqual(tpl_expect, rest[:len(tpl_expect)]) 6181 6182 rest = rest[len(tpl_expect):] 6183 align_pad = len(tpl_expect) % 4 6184 self.assertEqual(tools.get_bytes(0, align_pad), rest[:align_pad]) 6185 rest = rest[align_pad:] 6186 self.assertEqual(spl_expect, rest) 6187 6188 def testMkimageMultipleNoContent(self): 6189 """Test passing multiple data files to mkimage with one data file having no content""" 6190 self._SetupSplElf() 6191 with self.assertRaises(ValueError) as exc: 6192 self._DoReadFile('253_mkimage_mult_no_content.dts') 6193 self.assertIn('Could not complete processing of contents', 6194 str(exc.exception)) 6195 6196 def testMkimageFilename(self): 6197 """Test using mkimage to build a binary with a filename""" 6198 self._SetupSplElf() 6199 retcode = self._DoTestFile('254_mkimage_filename.dts') 6200 self.assertEqual(0, retcode) 6201 fname = tools.get_output_filename('mkimage-test.bin') 6202 self.assertTrue(os.path.exists(fname)) 6203 6204 def testVpl(self): 6205 """Test that an image with VPL and its device tree can be created""" 6206 # ELF file with a '__bss_size' symbol 6207 self._SetupVplElf() 6208 data = self._DoReadFile('255_u_boot_vpl.dts') 6209 self.assertEqual(U_BOOT_VPL_DATA + U_BOOT_VPL_DTB_DATA, data) 6210 6211 def testVplNoDtb(self): 6212 """Test that an image with vpl/u-boot-vpl-nodtb.bin can be created""" 6213 self._SetupVplElf() 6214 data = self._DoReadFile('256_u_boot_vpl_nodtb.dts') 6215 self.assertEqual(U_BOOT_VPL_NODTB_DATA, 6216 data[:len(U_BOOT_VPL_NODTB_DATA)]) 6217 6218 def testExpandedVpl(self): 6219 """Test that an expanded entry type is selected for TPL when needed""" 6220 self._SetupVplElf() 6221 6222 entry_args = { 6223 'vpl-bss-pad': 'y', 6224 'vpl-dtb': 'y', 6225 } 6226 self._DoReadFileDtb('257_fdt_incl_vpl.dts', use_expanded=True, 6227 entry_args=entry_args) 6228 image = control.images['image'] 6229 entries = image.GetEntries() 6230 self.assertEqual(1, len(entries)) 6231 6232 # We only have u-boot-vpl, which be expanded 6233 self.assertIn('u-boot-vpl', entries) 6234 entry = entries['u-boot-vpl'] 6235 self.assertEqual('u-boot-vpl-expanded', entry.etype) 6236 subent = entry.GetEntries() 6237 self.assertEqual(3, len(subent)) 6238 self.assertIn('u-boot-vpl-nodtb', subent) 6239 self.assertIn('u-boot-vpl-bss-pad', subent) 6240 self.assertIn('u-boot-vpl-dtb', subent) 6241 6242 def testVplBssPadMissing(self): 6243 """Test that a missing symbol is detected""" 6244 self._SetupVplElf('u_boot_ucode_ptr') 6245 with self.assertRaises(ValueError) as e: 6246 self._DoReadFile('258_vpl_bss_pad.dts') 6247 self.assertIn('Expected __bss_size symbol in vpl/u-boot-vpl', 6248 str(e.exception)) 6249 6250 def testSymlink(self): 6251 """Test that image files can be symlinked""" 6252 retcode = self._DoTestFile('259_symlink.dts', debug=True, map=True) 6253 self.assertEqual(0, retcode) 6254 image = control.images['test_image'] 6255 fname = tools.get_output_filename('test_image.bin') 6256 sname = tools.get_output_filename('symlink_to_test.bin') 6257 self.assertTrue(os.path.islink(sname)) 6258 self.assertEqual(os.readlink(sname), fname) 6259 6260 def testSymlinkOverwrite(self): 6261 """Test that symlinked images can be overwritten""" 6262 testdir = TestFunctional._MakeInputDir('symlinktest') 6263 self._DoTestFile('259_symlink.dts', debug=True, map=True, output_dir=testdir) 6264 # build the same image again in the same directory so that existing symlink is present 6265 self._DoTestFile('259_symlink.dts', debug=True, map=True, output_dir=testdir) 6266 fname = tools.get_output_filename('test_image.bin') 6267 sname = tools.get_output_filename('symlink_to_test.bin') 6268 self.assertTrue(os.path.islink(sname)) 6269 self.assertEqual(os.readlink(sname), fname) 6270 6271 def testSymbolsElf(self): 6272 """Test binman can assign symbols embedded in an ELF file""" 6273 if not elf.ELF_TOOLS: 6274 self.skipTest('Python elftools not available') 6275 self._SetupTplElf('u_boot_binman_syms') 6276 self._SetupVplElf('u_boot_binman_syms') 6277 self._SetupSplElf('u_boot_binman_syms') 6278 data = self._DoReadFileDtb('260_symbols_elf.dts')[0] 6279 image_fname = tools.get_output_filename('image.bin') 6280 6281 image = control.images['image'] 6282 entries = image.GetEntries() 6283 6284 for entry in entries.values(): 6285 # No symbols in u-boot and it has faked contents anyway 6286 if entry.name == 'u-boot': 6287 continue 6288 edata = data[entry.image_pos:entry.image_pos + entry.size] 6289 efname = tools.get_output_filename(f'edata-{entry.name}') 6290 tools.write_file(efname, edata) 6291 6292 syms = elf.GetSymbolFileOffset(efname, ['_binman_u_boot']) 6293 re_name = re.compile('_binman_(u_boot_(.*))_prop_(.*)') 6294 for name, sym in syms.items(): 6295 msg = 'test' 6296 val = elf.GetSymbolValue(sym, edata, msg) 6297 entry_m = re_name.match(name) 6298 if entry_m: 6299 ename, prop = entry_m.group(1), entry_m.group(3) 6300 entry, entry_name, prop_name = image.LookupEntry(entries, 6301 name, msg) 6302 if prop_name == 'offset': 6303 expect_val = entry.offset 6304 elif prop_name == 'image_pos': 6305 expect_val = entry.image_pos 6306 elif prop_name == 'size': 6307 expect_val = entry.size 6308 self.assertEqual(expect_val, val) 6309 6310 def testSymbolsElfBad(self): 6311 """Check error when trying to write symbols without the elftools lib""" 6312 if not elf.ELF_TOOLS: 6313 self.skipTest('Python elftools not available') 6314 self._SetupTplElf('u_boot_binman_syms') 6315 self._SetupVplElf('u_boot_binman_syms') 6316 self._SetupSplElf('u_boot_binman_syms') 6317 try: 6318 elf.ELF_TOOLS = False 6319 with self.assertRaises(ValueError) as exc: 6320 self._DoReadFileDtb('260_symbols_elf.dts') 6321 finally: 6322 elf.ELF_TOOLS = True 6323 self.assertIn( 6324 "Section '/binman': entry '/binman/u-boot-spl-elf': " 6325 'Cannot write symbols to an ELF file without Python elftools', 6326 str(exc.exception)) 6327 6328 def testSectionFilename(self): 6329 """Check writing of section contents to a file""" 6330 data = self._DoReadFile('261_section_fname.dts') 6331 expected = (b'&&' + U_BOOT_DATA + b'&&&' + 6332 tools.get_bytes(ord('!'), 7) + 6333 U_BOOT_DATA + tools.get_bytes(ord('&'), 12)) 6334 self.assertEqual(expected, data) 6335 6336 sect_fname = tools.get_output_filename('outfile.bin') 6337 self.assertTrue(os.path.exists(sect_fname)) 6338 sect_data = tools.read_file(sect_fname) 6339 self.assertEqual(U_BOOT_DATA, sect_data) 6340 6341 def testAbsent(self): 6342 """Check handling of absent entries""" 6343 data = self._DoReadFile('262_absent.dts') 6344 self.assertEqual(U_BOOT_DATA + U_BOOT_IMG_DATA, data) 6345 6346 def testPackTeeOsOptional(self): 6347 """Test that an image with an optional TEE binary can be created""" 6348 entry_args = { 6349 'tee-os-path': 'tee.elf', 6350 } 6351 data = self._DoReadFileDtb('263_tee_os_opt.dts', 6352 entry_args=entry_args)[0] 6353 self.assertEqual(U_BOOT_DATA + U_BOOT_IMG_DATA, data) 6354 6355 def checkFitTee(self, dts, tee_fname): 6356 """Check that a tee-os entry works and returns data 6357 6358 Args: 6359 dts (str): Device tree filename to use 6360 tee_fname (str): filename containing tee-os 6361 6362 Returns: 6363 bytes: Image contents 6364 """ 6365 if not elf.ELF_TOOLS: 6366 self.skipTest('Python elftools not available') 6367 entry_args = { 6368 'of-list': 'test-fdt1 test-fdt2', 6369 'default-dt': 'test-fdt2', 6370 'tee-os-path': tee_fname, 6371 } 6372 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR) 6373 data = self._DoReadFileDtb(dts, entry_args=entry_args, 6374 extra_indirs=[test_subdir])[0] 6375 return data 6376 6377 def testFitTeeOsOptionalFit(self): 6378 """Test an image with a FIT with an optional OP-TEE binary""" 6379 data = self.checkFitTee('264_tee_os_opt_fit.dts', 'tee.bin') 6380 6381 # There should be only one node, holding the data set up in SetUpClass() 6382 # for tee.bin 6383 dtb = fdt.Fdt.FromData(data) 6384 dtb.Scan() 6385 node = dtb.GetNode('/images/tee-1') 6386 self.assertEqual(TEE_ADDR, 6387 fdt_util.fdt32_to_cpu(node.props['load'].value)) 6388 self.assertEqual(TEE_ADDR, 6389 fdt_util.fdt32_to_cpu(node.props['entry'].value)) 6390 self.assertEqual(U_BOOT_DATA, node.props['data'].bytes) 6391 6392 with test_util.capture_sys_output() as (stdout, stderr): 6393 self.checkFitTee('264_tee_os_opt_fit.dts', '') 6394 err = stderr.getvalue() 6395 self.assertRegex( 6396 err, 6397 "Image '.*' is missing optional external blobs but is still functional: tee-os") 6398 6399 def testFitTeeOsOptionalFitBad(self): 6400 """Test an image with a FIT with an optional OP-TEE binary""" 6401 with self.assertRaises(ValueError) as exc: 6402 self.checkFitTee('265_tee_os_opt_fit_bad.dts', 'tee.bin') 6403 self.assertIn( 6404 "Node '/binman/fit': subnode 'images/@tee-SEQ': Failed to read ELF file: Magic number does not match", 6405 str(exc.exception)) 6406 6407 def testFitTeeOsBad(self): 6408 """Test an OP-TEE binary with wrong formats""" 6409 self.make_tee_bin('tee.bad1', 123) 6410 with self.assertRaises(ValueError) as exc: 6411 self.checkFitTee('264_tee_os_opt_fit.dts', 'tee.bad1') 6412 self.assertIn( 6413 "Node '/binman/fit/images/@tee-SEQ/tee-os': OP-TEE paged mode not supported", 6414 str(exc.exception)) 6415 6416 self.make_tee_bin('tee.bad2', 0, b'extra data') 6417 with self.assertRaises(ValueError) as exc: 6418 self.checkFitTee('264_tee_os_opt_fit.dts', 'tee.bad2') 6419 self.assertIn( 6420 "Node '/binman/fit/images/@tee-SEQ/tee-os': Invalid OP-TEE file: size mismatch (expected 0x4, have 0xe)", 6421 str(exc.exception)) 6422 6423 def testExtblobOptional(self): 6424 """Test an image with an external blob that is optional""" 6425 with test_util.capture_sys_output() as (stdout, stderr): 6426 data = self._DoReadFile('266_blob_ext_opt.dts') 6427 self.assertEqual(REFCODE_DATA, data) 6428 err = stderr.getvalue() 6429 self.assertRegex( 6430 err, 6431 "Image '.*' is missing optional external blobs but is still functional: missing") 6432 6433 def testSectionInner(self): 6434 """Test an inner section with a size""" 6435 data = self._DoReadFile('267_section_inner.dts') 6436 expected = U_BOOT_DATA + tools.get_bytes(0, 12) 6437 self.assertEqual(expected, data) 6438 6439 def testNull(self): 6440 """Test an image with a null entry""" 6441 data = self._DoReadFile('268_null.dts') 6442 self.assertEqual(U_BOOT_DATA + b'\xff\xff\xff\xff' + U_BOOT_IMG_DATA, data) 6443 6444 def testOverlap(self): 6445 """Test an image with a overlapping entry""" 6446 data = self._DoReadFile('269_overlap.dts') 6447 self.assertEqual(U_BOOT_DATA[:1] + b'aa' + U_BOOT_DATA[3:], data) 6448 6449 image = control.images['image'] 6450 entries = image.GetEntries() 6451 6452 self.assertIn('inset', entries) 6453 inset = entries['inset'] 6454 self.assertEqual(1, inset.offset); 6455 self.assertEqual(1, inset.image_pos); 6456 self.assertEqual(2, inset.size); 6457 6458 def testOverlapNull(self): 6459 """Test an image with a null overlap""" 6460 data = self._DoReadFile('270_overlap_null.dts') 6461 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)]) 6462 6463 # Check the FMAP 6464 fhdr, fentries = fmap_util.DecodeFmap(data[len(U_BOOT_DATA):]) 6465 self.assertEqual(4, fhdr.nareas) 6466 fiter = iter(fentries) 6467 6468 fentry = next(fiter) 6469 self.assertEqual(b'SECTION', fentry.name) 6470 self.assertEqual(0, fentry.offset) 6471 self.assertEqual(len(U_BOOT_DATA), fentry.size) 6472 self.assertEqual(0, fentry.flags) 6473 6474 fentry = next(fiter) 6475 self.assertEqual(b'U_BOOT', fentry.name) 6476 self.assertEqual(0, fentry.offset) 6477 self.assertEqual(len(U_BOOT_DATA), fentry.size) 6478 self.assertEqual(0, fentry.flags) 6479 6480 # Make sure that the NULL entry appears in the FMAP 6481 fentry = next(fiter) 6482 self.assertEqual(b'NULL', fentry.name) 6483 self.assertEqual(1, fentry.offset) 6484 self.assertEqual(2, fentry.size) 6485 self.assertEqual(0, fentry.flags) 6486 6487 fentry = next(fiter) 6488 self.assertEqual(b'FMAP', fentry.name) 6489 self.assertEqual(len(U_BOOT_DATA), fentry.offset) 6490 6491 def testOverlapBad(self): 6492 """Test an image with a bad overlapping entry""" 6493 with self.assertRaises(ValueError) as exc: 6494 self._DoReadFile('271_overlap_bad.dts') 6495 self.assertIn( 6496 "Node '/binman/inset': Offset 0x10 (16) ending at 0x12 (18) must overlap with existing entries", 6497 str(exc.exception)) 6498 6499 def testOverlapNoOffset(self): 6500 """Test an image with a bad overlapping entry""" 6501 with self.assertRaises(ValueError) as exc: 6502 self._DoReadFile('272_overlap_no_size.dts') 6503 self.assertIn( 6504 "Node '/binman/inset': 'fill' entry is missing properties: size", 6505 str(exc.exception)) 6506 6507 def testBlobSymbol(self): 6508 """Test a blob with symbols read from an ELF file""" 6509 elf_fname = self.ElfTestFile('blob_syms') 6510 TestFunctional._MakeInputFile('blob_syms', tools.read_file(elf_fname)) 6511 TestFunctional._MakeInputFile('blob_syms.bin', 6512 tools.read_file(self.ElfTestFile('blob_syms.bin'))) 6513 6514 data = self._DoReadFile('273_blob_symbol.dts') 6515 6516 syms = elf.GetSymbols(elf_fname, ['binman', 'image']) 6517 addr = elf.GetSymbolAddress(elf_fname, '__my_start_sym') 6518 self.assertEqual(syms['_binman_sym_magic'].address, addr) 6519 self.assertEqual(syms['_binman_inset_prop_offset'].address, addr + 4) 6520 self.assertEqual(syms['_binman_inset_prop_size'].address, addr + 8) 6521 6522 sym_values = struct.pack('<LLL', elf.BINMAN_SYM_MAGIC_VALUE, 4, 8) 6523 expected = sym_values 6524 self.assertEqual(expected, data[:len(expected)]) 6525 6526 def testOffsetFromElf(self): 6527 """Test a blob with symbols read from an ELF file""" 6528 elf_fname = self.ElfTestFile('blob_syms') 6529 TestFunctional._MakeInputFile('blob_syms', tools.read_file(elf_fname)) 6530 TestFunctional._MakeInputFile('blob_syms.bin', 6531 tools.read_file(self.ElfTestFile('blob_syms.bin'))) 6532 6533 data = self._DoReadFile('274_offset_from_elf.dts') 6534 6535 syms = elf.GetSymbols(elf_fname, ['binman', 'image']) 6536 base = elf.GetSymbolAddress(elf_fname, '__my_start_sym') 6537 6538 image = control.images['image'] 6539 entries = image.GetEntries() 6540 6541 self.assertIn('inset', entries) 6542 inset = entries['inset'] 6543 6544 self.assertEqual(base + 4, inset.offset); 6545 self.assertEqual(base + 4, inset.image_pos); 6546 self.assertEqual(4, inset.size); 6547 6548 self.assertIn('inset2', entries) 6549 inset = entries['inset2'] 6550 self.assertEqual(base + 8, inset.offset); 6551 self.assertEqual(base + 8, inset.image_pos); 6552 self.assertEqual(4, inset.size); 6553 6554 def testFitAlign(self): 6555 """Test an image with an FIT with aligned external data""" 6556 data = self._DoReadFile('275_fit_align.dts') 6557 self.assertEqual(4096, len(data)) 6558 6559 dtb = fdt.Fdt.FromData(data) 6560 dtb.Scan() 6561 6562 props = self._GetPropTree(dtb, ['data-position']) 6563 expected = { 6564 'u-boot:data-position': 1024, 6565 'fdt-1:data-position': 2048, 6566 'fdt-2:data-position': 3072, 6567 } 6568 self.assertEqual(expected, props) 6569 6570 def testFitFirmwareLoadables(self): 6571 """Test an image with an FIT that use fit,firmware""" 6572 if not elf.ELF_TOOLS: 6573 self.skipTest('Python elftools not available') 6574 entry_args = { 6575 'of-list': 'test-fdt1', 6576 'default-dt': 'test-fdt1', 6577 'atf-bl31-path': 'bl31.elf', 6578 'tee-os-path': 'missing.bin', 6579 } 6580 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR) 6581 with test_util.capture_sys_output() as (stdout, stderr): 6582 data = self._DoReadFileDtb( 6583 '276_fit_firmware_loadables.dts', 6584 entry_args=entry_args, 6585 extra_indirs=[test_subdir])[0] 6586 6587 dtb = fdt.Fdt.FromData(data) 6588 dtb.Scan() 6589 6590 node = dtb.GetNode('/configurations/conf-uboot-1') 6591 self.assertEqual('u-boot', node.props['firmware'].value) 6592 self.assertEqual(['atf-1', 'atf-2'], 6593 fdt_util.GetStringList(node, 'loadables')) 6594 6595 node = dtb.GetNode('/configurations/conf-atf-1') 6596 self.assertEqual('atf-1', node.props['firmware'].value) 6597 self.assertEqual(['u-boot', 'atf-2'], 6598 fdt_util.GetStringList(node, 'loadables')) 6599 6600 node = dtb.GetNode('/configurations/conf-missing-uboot-1') 6601 self.assertEqual('u-boot', node.props['firmware'].value) 6602 self.assertEqual(['atf-1', 'atf-2'], 6603 fdt_util.GetStringList(node, 'loadables')) 6604 6605 node = dtb.GetNode('/configurations/conf-missing-atf-1') 6606 self.assertEqual('atf-1', node.props['firmware'].value) 6607 self.assertEqual(['u-boot', 'atf-2'], 6608 fdt_util.GetStringList(node, 'loadables')) 6609 6610 node = dtb.GetNode('/configurations/conf-missing-tee-1') 6611 self.assertEqual('atf-1', node.props['firmware'].value) 6612 self.assertEqual(['u-boot', 'atf-2'], 6613 fdt_util.GetStringList(node, 'loadables')) 6614 6615 def testTooldir(self): 6616 """Test that we can specify the tooldir""" 6617 with test_util.capture_sys_output() as (stdout, stderr): 6618 self.assertEqual(0, self._DoBinman('--tooldir', 'fred', 6619 'tool', '-l')) 6620 self.assertEqual('fred', bintool.Bintool.tooldir) 6621 6622 # Check that the toolpath is updated correctly 6623 self.assertEqual(['fred'], tools.tool_search_paths) 6624 6625 # Try with a few toolpaths; the tooldir should be at the end 6626 with test_util.capture_sys_output() as (stdout, stderr): 6627 self.assertEqual(0, self._DoBinman( 6628 '--toolpath', 'mary', '--toolpath', 'anna', '--tooldir', 'fred', 6629 'tool', '-l')) 6630 self.assertEqual(['mary', 'anna', 'fred'], tools.tool_search_paths) 6631 6632 def testReplaceSectionEntry(self): 6633 """Test replacing an entry in a section""" 6634 expect_data = b'w' * len(U_BOOT_DATA + COMPRESS_DATA) 6635 entry_data, expected_fdtmap, image = self._RunReplaceCmd('section/blob', 6636 expect_data, dts='241_replace_section_simple.dts') 6637 self.assertEqual(expect_data, entry_data) 6638 6639 entries = image.GetEntries() 6640 self.assertIn('section', entries) 6641 section = entries['section'] 6642 6643 sect_entries = section.GetEntries() 6644 self.assertIn('blob', sect_entries) 6645 entry = sect_entries['blob'] 6646 self.assertEqual(len(expect_data), entry.size) 6647 6648 fname = tools.get_output_filename('image-updated.bin') 6649 data = tools.read_file(fname) 6650 6651 new_blob_data = data[entry.image_pos:entry.image_pos + len(expect_data)] 6652 self.assertEqual(expect_data, new_blob_data) 6653 6654 self.assertEqual(U_BOOT_DATA, 6655 data[entry.image_pos + len(expect_data):] 6656 [:len(U_BOOT_DATA)]) 6657 6658 def testReplaceSectionDeep(self): 6659 """Test replacing an entry in two levels of sections""" 6660 expect_data = b'w' * len(U_BOOT_DATA + COMPRESS_DATA) 6661 entry_data, expected_fdtmap, image = self._RunReplaceCmd( 6662 'section/section/blob', expect_data, 6663 dts='278_replace_section_deep.dts') 6664 self.assertEqual(expect_data, entry_data) 6665 6666 entries = image.GetEntries() 6667 self.assertIn('section', entries) 6668 section = entries['section'] 6669 6670 subentries = section.GetEntries() 6671 self.assertIn('section', subentries) 6672 section = subentries['section'] 6673 6674 sect_entries = section.GetEntries() 6675 self.assertIn('blob', sect_entries) 6676 entry = sect_entries['blob'] 6677 self.assertEqual(len(expect_data), entry.size) 6678 6679 fname = tools.get_output_filename('image-updated.bin') 6680 data = tools.read_file(fname) 6681 6682 new_blob_data = data[entry.image_pos:entry.image_pos + len(expect_data)] 6683 self.assertEqual(expect_data, new_blob_data) 6684 6685 self.assertEqual(U_BOOT_DATA, 6686 data[entry.image_pos + len(expect_data):] 6687 [:len(U_BOOT_DATA)]) 6688 6689 def testReplaceFitSibling(self): 6690 """Test an image with a FIT inside where we replace its sibling""" 6691 self._SetupSplElf() 6692 fname = TestFunctional._MakeInputFile('once', b'available once') 6693 self._DoReadFileRealDtb('277_replace_fit_sibling.dts') 6694 os.remove(fname) 6695 6696 try: 6697 tmpdir, updated_fname = self._SetupImageInTmpdir() 6698 6699 fname = os.path.join(tmpdir, 'update-blob') 6700 expected = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) + 1) 6701 tools.write_file(fname, expected) 6702 6703 self._DoBinman('replace', '-i', updated_fname, 'blob', '-f', fname) 6704 data = tools.read_file(updated_fname) 6705 start = len(U_BOOT_DTB_DATA) 6706 self.assertEqual(expected, data[start:start + len(expected)]) 6707 map_fname = os.path.join(tmpdir, 'image-updated.map') 6708 self.assertFalse(os.path.exists(map_fname)) 6709 finally: 6710 shutil.rmtree(tmpdir) 6711 6712 def testX509Cert(self): 6713 """Test creating an X509 certificate""" 6714 keyfile = self.TestFile('key.key') 6715 entry_args = { 6716 'keyfile': keyfile, 6717 } 6718 data = self._DoReadFileDtb('279_x509_cert.dts', 6719 entry_args=entry_args)[0] 6720 cert = data[:-4] 6721 self.assertEqual(U_BOOT_DATA, data[-4:]) 6722 6723 # TODO: verify the signature 6724 6725 def testX509CertMissing(self): 6726 """Test that binman still produces an image if openssl is missing""" 6727 keyfile = self.TestFile('key.key') 6728 entry_args = { 6729 'keyfile': 'keyfile', 6730 } 6731 with test_util.capture_sys_output() as (_, stderr): 6732 self._DoTestFile('279_x509_cert.dts', 6733 force_missing_bintools='openssl', 6734 entry_args=entry_args) 6735 err = stderr.getvalue() 6736 self.assertRegex(err, "Image 'image'.*missing bintools.*: openssl") 6737 6738 def testPackRockchipTpl(self): 6739 """Test that an image with a Rockchip TPL binary can be created""" 6740 data = self._DoReadFile('291_rockchip_tpl.dts') 6741 self.assertEqual(ROCKCHIP_TPL_DATA, data[:len(ROCKCHIP_TPL_DATA)]) 6742 6743 def testMkimageMissingBlobMultiple(self): 6744 """Test missing blob with mkimage entry and multiple-data-files""" 6745 with test_util.capture_sys_output() as (stdout, stderr): 6746 self._DoTestFile('292_mkimage_missing_multiple.dts', allow_missing=True) 6747 err = stderr.getvalue() 6748 self.assertIn("is missing external blobs and is non-functional", err) 6749 6750 with self.assertRaises(ValueError) as e: 6751 self._DoTestFile('292_mkimage_missing_multiple.dts', allow_missing=False) 6752 self.assertIn("not found in input path", str(e.exception)) 6753 6754 def _PrepareSignEnv(self, dts='280_fit_sign.dts'): 6755 """Prepare sign environment 6756 6757 Create private and public keys, add pubkey into dtb. 6758 6759 Returns: 6760 Tuple: 6761 FIT container 6762 Image name 6763 Private key 6764 DTB 6765 """ 6766 self._SetupSplElf() 6767 data = self._DoReadFileRealDtb(dts) 6768 updated_fname = tools.get_output_filename('image-updated.bin') 6769 tools.write_file(updated_fname, data) 6770 dtb = tools.get_output_filename('source.dtb') 6771 private_key = tools.get_output_filename('test_key.key') 6772 public_key = tools.get_output_filename('test_key.crt') 6773 fit = tools.get_output_filename('fit.fit') 6774 key_dir = tools.get_output_dir() 6775 6776 tools.run('openssl', 'req', '-batch' , '-newkey', 'rsa:4096', 6777 '-sha256', '-new', '-nodes', '-x509', '-keyout', 6778 private_key, '-out', public_key) 6779 tools.run('fdt_add_pubkey', '-a', 'sha256,rsa4096', '-k', key_dir, 6780 '-n', 'test_key', '-r', 'conf', dtb) 6781 6782 return fit, updated_fname, private_key, dtb 6783 6784 def testSignSimple(self): 6785 """Test that a FIT container can be signed in image""" 6786 is_signed = False 6787 fit, fname, private_key, dtb = self._PrepareSignEnv() 6788 6789 # do sign with private key 6790 control.SignEntries(fname, None, private_key, 'sha256,rsa4096', 6791 ['fit']) 6792 is_signed = self._CheckSign(fit, dtb) 6793 6794 self.assertEqual(is_signed, True) 6795 6796 def testSignExactFIT(self): 6797 """Test that a FIT container can be signed and replaced in image""" 6798 is_signed = False 6799 fit, fname, private_key, dtb = self._PrepareSignEnv() 6800 6801 # Make sure we propagate the toolpath, since mkimage may not be on PATH 6802 args = [] 6803 if self.toolpath: 6804 for path in self.toolpath: 6805 args += ['--toolpath', path] 6806 6807 # do sign with private key 6808 self._DoBinman(*args, 'sign', '-i', fname, '-k', private_key, '-a', 6809 'sha256,rsa4096', '-f', fit, 'fit') 6810 is_signed = self._CheckSign(fit, dtb) 6811 6812 self.assertEqual(is_signed, True) 6813 6814 def testSignNonFit(self): 6815 """Test a non-FIT entry cannot be signed""" 6816 is_signed = False 6817 fit, fname, private_key, _ = self._PrepareSignEnv( 6818 '281_sign_non_fit.dts') 6819 6820 # do sign with private key 6821 with self.assertRaises(ValueError) as e: 6822 self._DoBinman('sign', '-i', fname, '-k', private_key, '-a', 6823 'sha256,rsa4096', '-f', fit, 'u-boot') 6824 self.assertIn( 6825 "Node '/u-boot': Updating signatures is not supported with this entry type", 6826 str(e.exception)) 6827 6828 def testSignMissingMkimage(self): 6829 """Test that FIT signing handles a missing mkimage tool""" 6830 fit, fname, private_key, _ = self._PrepareSignEnv() 6831 6832 # try to sign with a missing mkimage tool 6833 bintool.Bintool.set_missing_list(['mkimage']) 6834 with self.assertRaises(ValueError) as e: 6835 control.SignEntries(fname, None, private_key, 'sha256,rsa4096', 6836 ['fit']) 6837 self.assertIn("Node '/fit': Missing tool: 'mkimage'", str(e.exception)) 6838 6839 def testSymbolNoWrite(self): 6840 """Test disabling of symbol writing""" 6841 self._SetupSplElf() 6842 self.checkSymbols('282_symbols_disable.dts', U_BOOT_SPL_DATA, 0x1c, 6843 no_write_symbols=True) 6844 6845 def testSymbolNoWriteExpanded(self): 6846 """Test disabling of symbol writing in expanded entries""" 6847 entry_args = { 6848 'spl-dtb': '1', 6849 } 6850 self.checkSymbols('282_symbols_disable.dts', U_BOOT_SPL_NODTB_DATA + 6851 U_BOOT_SPL_DTB_DATA, 0x38, 6852 entry_args=entry_args, use_expanded=True, 6853 no_write_symbols=True) 6854 6855 def testMkimageSpecial(self): 6856 """Test mkimage ignores special hash-1 node""" 6857 data = self._DoReadFile('283_mkimage_special.dts') 6858 6859 # Just check that the data appears in the file somewhere 6860 self.assertIn(U_BOOT_DATA, data) 6861 6862 def testFitFdtList(self): 6863 """Test an image with an FIT with the fit,fdt-list-val option""" 6864 entry_args = { 6865 'default-dt': 'test-fdt2', 6866 } 6867 data = self._DoReadFileDtb( 6868 '284_fit_fdt_list.dts', 6869 entry_args=entry_args, 6870 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0] 6871 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):]) 6872 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)] 6873 6874 def testSplEmptyBss(self): 6875 """Test an expanded SPL with a zero-size BSS""" 6876 # ELF file with a '__bss_size' symbol 6877 self._SetupSplElf(src_fname='bss_data_zero') 6878 6879 entry_args = { 6880 'spl-bss-pad': 'y', 6881 'spl-dtb': 'y', 6882 } 6883 data = self._DoReadFileDtb('285_spl_expand.dts', 6884 use_expanded=True, entry_args=entry_args)[0] 6885 6886 def testTemplate(self): 6887 """Test using a template""" 6888 TestFunctional._MakeInputFile('vga2.bin', b'#' + VGA_DATA) 6889 data = self._DoReadFile('286_template.dts') 6890 first = U_BOOT_DATA + VGA_DATA + U_BOOT_DTB_DATA 6891 second = U_BOOT_DATA + b'#' + VGA_DATA + U_BOOT_DTB_DATA 6892 self.assertEqual(U_BOOT_IMG_DATA + first + second, data) 6893 6894 dtb_fname1 = tools.get_output_filename('u-boot.dtb.tmpl1') 6895 self.assertTrue(os.path.exists(dtb_fname1)) 6896 dtb = fdt.Fdt.FromData(tools.read_file(dtb_fname1)) 6897 dtb.Scan() 6898 node1 = dtb.GetNode('/binman/template') 6899 self.assertTrue(node1) 6900 vga = dtb.GetNode('/binman/first/intel-vga') 6901 self.assertTrue(vga) 6902 6903 dtb_fname2 = tools.get_output_filename('u-boot.dtb.tmpl2') 6904 self.assertTrue(os.path.exists(dtb_fname2)) 6905 dtb2 = fdt.Fdt.FromData(tools.read_file(dtb_fname2)) 6906 dtb2.Scan() 6907 node2 = dtb2.GetNode('/binman/template') 6908 self.assertFalse(node2) 6909 6910 def testTemplateBlobMulti(self): 6911 """Test using a template with 'multiple-images' enabled""" 6912 TestFunctional._MakeInputFile('my-blob.bin', b'blob') 6913 TestFunctional._MakeInputFile('my-blob2.bin', b'other') 6914 retcode = self._DoTestFile('287_template_multi.dts') 6915 6916 self.assertEqual(0, retcode) 6917 image = control.images['image'] 6918 image_fname = tools.get_output_filename('my-image.bin') 6919 data = tools.read_file(image_fname) 6920 self.assertEqual(b'blob@@@@other', data) 6921 6922 def testTemplateFit(self): 6923 """Test using a template in a FIT""" 6924 fit_data = self._DoReadFile('288_template_fit.dts') 6925 fname = os.path.join(self._indir, 'fit_data.fit') 6926 tools.write_file(fname, fit_data) 6927 out = tools.run('dumpimage', '-l', fname) 6928 6929 def testTemplateSection(self): 6930 """Test using a template in a section (not at top level)""" 6931 TestFunctional._MakeInputFile('vga2.bin', b'#' + VGA_DATA) 6932 data = self._DoReadFile('289_template_section.dts') 6933 first = U_BOOT_DATA + VGA_DATA + U_BOOT_DTB_DATA 6934 second = U_BOOT_DATA + b'#' + VGA_DATA + U_BOOT_DTB_DATA 6935 self.assertEqual(U_BOOT_IMG_DATA + first + second + first, data) 6936 6937 def testMkimageSymbols(self): 6938 """Test using mkimage to build an image with symbols in it""" 6939 self._SetupSplElf('u_boot_binman_syms') 6940 data = self._DoReadFile('290_mkimage_sym.dts') 6941 6942 image = control.images['image'] 6943 entries = image.GetEntries() 6944 self.assertIn('u-boot', entries) 6945 u_boot = entries['u-boot'] 6946 6947 mkim = entries['mkimage'] 6948 mkim_entries = mkim.GetEntries() 6949 self.assertIn('u-boot-spl', mkim_entries) 6950 spl = mkim_entries['u-boot-spl'] 6951 self.assertIn('u-boot-spl2', mkim_entries) 6952 spl2 = mkim_entries['u-boot-spl2'] 6953 6954 # skip the mkimage header and the area sizes 6955 mk_data = data[mkim.offset + 0x40:] 6956 size, term = struct.unpack('>LL', mk_data[:8]) 6957 6958 # There should be only one image, so check that the zero terminator is 6959 # present 6960 self.assertEqual(0, term) 6961 6962 content = mk_data[8:8 + size] 6963 6964 # The image should contain the symbols from u_boot_binman_syms.c 6965 # Note that image_pos is adjusted by the base address of the image, 6966 # which is 0x10 in our test image 6967 spl_data = content[:0x18] 6968 content = content[0x1b:] 6969 6970 # After the header is a table of offsets for each image. There should 6971 # only be one image, then a 0 terminator, so figure out the real start 6972 # of the image data 6973 base = 0x40 + 8 6974 6975 # Check symbols in both u-boot-spl and u-boot-spl2 6976 for i in range(2): 6977 vals = struct.unpack('<LLQLL', spl_data) 6978 6979 # The image should contain the symbols from u_boot_binman_syms.c 6980 # Note that image_pos is adjusted by the base address of the image, 6981 # which is 0x10 in our 'u_boot_binman_syms' test image 6982 self.assertEqual(elf.BINMAN_SYM_MAGIC_VALUE, vals[0]) 6983 self.assertEqual(base, vals[1]) 6984 self.assertEqual(spl2.offset, vals[2]) 6985 # figure out the internal positions of its components 6986 self.assertEqual(0x10 + u_boot.image_pos, vals[3]) 6987 6988 # Check that spl and spl2 are actually at the indicated positions 6989 self.assertEqual( 6990 elf.BINMAN_SYM_MAGIC_VALUE, 6991 struct.unpack('<I', data[spl.image_pos:spl.image_pos + 4])[0]) 6992 self.assertEqual( 6993 elf.BINMAN_SYM_MAGIC_VALUE, 6994 struct.unpack('<I', data[spl2.image_pos:spl2.image_pos + 4])[0]) 6995 6996 self.assertEqual(len(U_BOOT_DATA), vals[4]) 6997 6998 # Move to next 6999 spl_data = content[:0x18] 7000 7001 def testTemplatePhandle(self): 7002 """Test using a template in a node containing a phandle""" 7003 entry_args = { 7004 'atf-bl31-path': 'bl31.elf', 7005 } 7006 data = self._DoReadFileDtb('309_template_phandle.dts', 7007 entry_args=entry_args) 7008 fname = tools.get_output_filename('image.bin') 7009 out = tools.run('dumpimage', '-l', fname) 7010 7011 # We should see the FIT description and one for each of the two images 7012 lines = out.splitlines() 7013 descs = [line.split()[-1] for line in lines if 'escription' in line] 7014 self.assertEqual(['test-desc', 'atf', 'fdt'], descs) 7015 7016 def testTemplatePhandleDup(self): 7017 """Test using a template in a node containing a phandle""" 7018 entry_args = { 7019 'atf-bl31-path': 'bl31.elf', 7020 } 7021 with self.assertRaises(ValueError) as e: 7022 self._DoReadFileDtb('310_template_phandle_dup.dts', 7023 entry_args=entry_args) 7024 self.assertIn( 7025 'Duplicate phandle 1 in nodes /binman/image/fit/images/atf/atf-bl31 and /binman/image-2/fit/images/atf/atf-bl31', 7026 str(e.exception)) 7027 7028 def testTIBoardConfig(self): 7029 """Test that a schema validated board config file can be generated""" 7030 data = self._DoReadFile('293_ti_board_cfg.dts') 7031 self.assertEqual(TI_BOARD_CONFIG_DATA, data) 7032 7033 def testTIBoardConfigLint(self): 7034 """Test that an incorrectly linted config file would generate error""" 7035 with self.assertRaises(ValueError) as e: 7036 data = self._DoReadFile('323_ti_board_cfg_phony.dts') 7037 self.assertIn("Yamllint error", str(e.exception)) 7038 7039 def testTIBoardConfigCombined(self): 7040 """Test that a schema validated combined board config file can be generated""" 7041 data = self._DoReadFile('294_ti_board_cfg_combined.dts') 7042 configlen_noheader = TI_BOARD_CONFIG_DATA * 4 7043 self.assertGreater(data, configlen_noheader) 7044 7045 def testTIBoardConfigNoDataType(self): 7046 """Test that error is thrown when data type is not supported""" 7047 with self.assertRaises(ValueError) as e: 7048 data = self._DoReadFile('295_ti_board_cfg_no_type.dts') 7049 self.assertIn("Schema validation error", str(e.exception)) 7050 7051 def testPackTiSecure(self): 7052 """Test that an image with a TI secured binary can be created""" 7053 keyfile = self.TestFile('key.key') 7054 entry_args = { 7055 'keyfile': keyfile, 7056 } 7057 data = self._DoReadFileDtb('296_ti_secure.dts', 7058 entry_args=entry_args)[0] 7059 self.assertGreater(len(data), len(TI_UNSECURE_DATA)) 7060 7061 def testPackTiSecureFirewall(self): 7062 """Test that an image with a TI secured binary can be created""" 7063 keyfile = self.TestFile('key.key') 7064 entry_args = { 7065 'keyfile': keyfile, 7066 } 7067 data_no_firewall = self._DoReadFileDtb('296_ti_secure.dts', 7068 entry_args=entry_args)[0] 7069 data_firewall = self._DoReadFileDtb('324_ti_secure_firewall.dts', 7070 entry_args=entry_args)[0] 7071 self.assertGreater(len(data_firewall),len(data_no_firewall)) 7072 7073 def testPackTiSecureFirewallMissingProperty(self): 7074 """Test that an image with a TI secured binary can be created""" 7075 keyfile = self.TestFile('key.key') 7076 entry_args = { 7077 'keyfile': keyfile, 7078 } 7079 with self.assertRaises(ValueError) as e: 7080 data_firewall = self._DoReadFileDtb('325_ti_secure_firewall_missing_property.dts', 7081 entry_args=entry_args)[0] 7082 self.assertRegex(str(e.exception), "Node '/binman/ti-secure': Subnode 'firewall-0-2' is missing properties: id,region") 7083 7084 def testPackTiSecureMissingTool(self): 7085 """Test that an image with a TI secured binary (non-functional) can be created 7086 when openssl is missing""" 7087 keyfile = self.TestFile('key.key') 7088 entry_args = { 7089 'keyfile': keyfile, 7090 } 7091 with test_util.capture_sys_output() as (_, stderr): 7092 self._DoTestFile('296_ti_secure.dts', 7093 force_missing_bintools='openssl', 7094 entry_args=entry_args) 7095 err = stderr.getvalue() 7096 self.assertRegex(err, "Image 'image'.*missing bintools.*: openssl") 7097 7098 def testPackTiSecureROM(self): 7099 """Test that a ROM image with a TI secured binary can be created""" 7100 keyfile = self.TestFile('key.key') 7101 entry_args = { 7102 'keyfile': keyfile, 7103 } 7104 data = self._DoReadFileDtb('297_ti_secure_rom.dts', 7105 entry_args=entry_args)[0] 7106 data_a = self._DoReadFileDtb('299_ti_secure_rom_a.dts', 7107 entry_args=entry_args)[0] 7108 data_b = self._DoReadFileDtb('300_ti_secure_rom_b.dts', 7109 entry_args=entry_args)[0] 7110 self.assertGreater(len(data), len(TI_UNSECURE_DATA)) 7111 self.assertGreater(len(data_a), len(TI_UNSECURE_DATA)) 7112 self.assertGreater(len(data_b), len(TI_UNSECURE_DATA)) 7113 7114 def testPackTiSecureROMCombined(self): 7115 """Test that a ROM image with a TI secured binary can be created""" 7116 keyfile = self.TestFile('key.key') 7117 entry_args = { 7118 'keyfile': keyfile, 7119 } 7120 data = self._DoReadFileDtb('298_ti_secure_rom_combined.dts', 7121 entry_args=entry_args)[0] 7122 self.assertGreater(len(data), len(TI_UNSECURE_DATA)) 7123 7124 def testEncryptedNoAlgo(self): 7125 """Test encrypted node with missing required properties""" 7126 with self.assertRaises(ValueError) as e: 7127 self._DoReadFileDtb('301_encrypted_no_algo.dts') 7128 self.assertIn( 7129 "Node '/binman/fit/images/u-boot/encrypted': 'encrypted' entry is missing properties: algo iv-filename", 7130 str(e.exception)) 7131 7132 def testEncryptedInvalidIvfile(self): 7133 """Test encrypted node with invalid iv file""" 7134 with self.assertRaises(ValueError) as e: 7135 self._DoReadFileDtb('302_encrypted_invalid_iv_file.dts') 7136 self.assertIn("Filename 'invalid-iv-file' not found in input path", 7137 str(e.exception)) 7138 7139 def testEncryptedMissingKey(self): 7140 """Test encrypted node with missing key properties""" 7141 with self.assertRaises(ValueError) as e: 7142 self._DoReadFileDtb('303_encrypted_missing_key.dts') 7143 self.assertIn( 7144 "Node '/binman/fit/images/u-boot/encrypted': Provide either 'key-filename' or 'key-source'", 7145 str(e.exception)) 7146 7147 def testEncryptedKeySource(self): 7148 """Test encrypted node with key-source property""" 7149 data = self._DoReadFileDtb('304_encrypted_key_source.dts')[0] 7150 7151 dtb = fdt.Fdt.FromData(data) 7152 dtb.Scan() 7153 7154 node = dtb.GetNode('/images/u-boot/cipher') 7155 self.assertEqual('algo-name', node.props['algo'].value) 7156 self.assertEqual('key-source-value', node.props['key-source'].value) 7157 self.assertEqual(ENCRYPTED_IV_DATA, 7158 tools.to_bytes(''.join(node.props['iv'].value))) 7159 self.assertNotIn('key', node.props) 7160 7161 def testEncryptedKeyFile(self): 7162 """Test encrypted node with key-filename property""" 7163 data = self._DoReadFileDtb('305_encrypted_key_file.dts')[0] 7164 7165 dtb = fdt.Fdt.FromData(data) 7166 dtb.Scan() 7167 7168 node = dtb.GetNode('/images/u-boot/cipher') 7169 self.assertEqual('algo-name', node.props['algo'].value) 7170 self.assertEqual(ENCRYPTED_IV_DATA, 7171 tools.to_bytes(''.join(node.props['iv'].value))) 7172 self.assertEqual(ENCRYPTED_KEY_DATA, 7173 tools.to_bytes(''.join(node.props['key'].value))) 7174 self.assertNotIn('key-source', node.props) 7175 7176 7177 def testSplPubkeyDtb(self): 7178 """Test u_boot_spl_pubkey_dtb etype""" 7179 data = tools.read_file(self.TestFile("key.pem")) 7180 self._MakeInputFile("key.crt", data) 7181 self._DoReadFileRealDtb('306_spl_pubkey_dtb.dts') 7182 image = control.images['image'] 7183 entries = image.GetEntries() 7184 dtb_entry = entries['u-boot-spl-pubkey-dtb'] 7185 dtb_data = dtb_entry.GetData() 7186 dtb = fdt.Fdt.FromData(dtb_data) 7187 dtb.Scan() 7188 7189 signature_node = dtb.GetNode('/signature') 7190 self.assertIsNotNone(signature_node) 7191 key_node = signature_node.FindNode("key-key") 7192 self.assertIsNotNone(key_node) 7193 self.assertEqual(fdt_util.GetString(key_node, "required"), 7194 "conf") 7195 self.assertEqual(fdt_util.GetString(key_node, "algo"), 7196 "sha384,rsa4096") 7197 self.assertEqual(fdt_util.GetString(key_node, "key-name-hint"), 7198 "key") 7199 7200 def testXilinxBootgenSigning(self): 7201 """Test xilinx-bootgen etype""" 7202 bootgen = bintool.Bintool.create('bootgen') 7203 self._CheckBintool(bootgen) 7204 data = tools.read_file(self.TestFile("key.key")) 7205 self._MakeInputFile("psk.pem", data) 7206 self._MakeInputFile("ssk.pem", data) 7207 self._SetupPmuFwlElf() 7208 self._SetupSplElf() 7209 self._DoReadFileRealDtb('307_xilinx_bootgen_sign.dts') 7210 image_fname = tools.get_output_filename('image.bin') 7211 7212 # Read partition header table and check if authentication is enabled 7213 bootgen_out = bootgen.run_cmd("-arch", "zynqmp", 7214 "-read", image_fname, "pht").splitlines() 7215 attributes = {"authentication": None, 7216 "core": None, 7217 "encryption": None} 7218 7219 for l in bootgen_out: 7220 for a in attributes.keys(): 7221 if a in l: 7222 m = re.match(fr".*{a} \[([^]]+)\]", l) 7223 attributes[a] = m.group(1) 7224 7225 self.assertTrue(attributes['authentication'] == "rsa") 7226 self.assertTrue(attributes['core'] == "a53-0") 7227 self.assertTrue(attributes['encryption'] == "no") 7228 7229 def testXilinxBootgenSigningEncryption(self): 7230 """Test xilinx-bootgen etype""" 7231 bootgen = bintool.Bintool.create('bootgen') 7232 self._CheckBintool(bootgen) 7233 data = tools.read_file(self.TestFile("key.key")) 7234 self._MakeInputFile("psk.pem", data) 7235 self._MakeInputFile("ssk.pem", data) 7236 self._SetupPmuFwlElf() 7237 self._SetupSplElf() 7238 self._DoReadFileRealDtb('308_xilinx_bootgen_sign_enc.dts') 7239 image_fname = tools.get_output_filename('image.bin') 7240 7241 # Read boot header in order to verify encryption source and 7242 # encryption parameter 7243 bootgen_out = bootgen.run_cmd("-arch", "zynqmp", 7244 "-read", image_fname, "bh").splitlines() 7245 attributes = {"auth_only": 7246 {"re": r".*auth_only \[([^]]+)\]", "value": None}, 7247 "encryption_keystore": 7248 {"re": r" *encryption_keystore \(0x28\) : (.*)", 7249 "value": None}, 7250 } 7251 7252 for l in bootgen_out: 7253 for a in attributes.keys(): 7254 if a in l: 7255 m = re.match(attributes[a]['re'], l) 7256 attributes[a] = m.group(1) 7257 7258 # Check if fsbl-attribute is set correctly 7259 self.assertTrue(attributes['auth_only'] == "true") 7260 # Check if key is stored in efuse 7261 self.assertTrue(attributes['encryption_keystore'] == "0xa5c3c5a3") 7262 7263 def testXilinxBootgenMissing(self): 7264 """Test that binman still produces an image if bootgen is missing""" 7265 data = tools.read_file(self.TestFile("key.key")) 7266 self._MakeInputFile("psk.pem", data) 7267 self._MakeInputFile("ssk.pem", data) 7268 self._SetupPmuFwlElf() 7269 self._SetupSplElf() 7270 with test_util.capture_sys_output() as (_, stderr): 7271 self._DoTestFile('307_xilinx_bootgen_sign.dts', 7272 force_missing_bintools='bootgen') 7273 err = stderr.getvalue() 7274 self.assertRegex(err, 7275 "Image 'image'.*missing bintools.*: bootgen") 7276 7277 def _GetCapsuleHeaders(self, data): 7278 """Get the capsule header contents 7279 7280 Args: 7281 data: Capsule file contents 7282 7283 Returns: 7284 Dict: 7285 key: Capsule Header name (str) 7286 value: Header field value (str) 7287 """ 7288 capsule_file = os.path.join(self._indir, 'test.capsule') 7289 tools.write_file(capsule_file, data) 7290 7291 out = tools.run('mkeficapsule', '--dump-capsule', capsule_file) 7292 lines = out.splitlines() 7293 7294 re_line = re.compile(r'^([^:\-\t]*)(?:\t*\s*:\s*(.*))?$') 7295 vals = {} 7296 for line in lines: 7297 mat = re_line.match(line) 7298 if mat: 7299 vals[mat.group(1)] = mat.group(2) 7300 7301 return vals 7302 7303 def _CheckCapsule(self, data, signed_capsule=False, version_check=False, 7304 capoemflags=False): 7305 fmp_signature = "3153534D" # 'M', 'S', 'S', '1' 7306 fmp_size = "00000010" 7307 fmp_fw_version = "00000002" 7308 capsule_image_index = "00000001" 7309 oemflag = "00018000" 7310 auth_hdr_revision = "00000200" 7311 auth_hdr_cert_type = "00000EF1" 7312 7313 payload_data_len = len(EFI_CAPSULE_DATA) 7314 7315 hdr = self._GetCapsuleHeaders(data) 7316 7317 self.assertEqual(FW_MGMT_GUID.upper(), hdr['EFI_CAPSULE_HDR.CAPSULE_GUID']) 7318 7319 self.assertEqual(CAPSULE_IMAGE_GUID.upper(), 7320 hdr['FMP_CAPSULE_IMAGE_HDR.UPDATE_IMAGE_TYPE_ID']) 7321 self.assertEqual(capsule_image_index, 7322 hdr['FMP_CAPSULE_IMAGE_HDR.UPDATE_IMAGE_INDEX']) 7323 7324 if capoemflags: 7325 self.assertEqual(oemflag, hdr['EFI_CAPSULE_HDR.FLAGS']) 7326 7327 if signed_capsule: 7328 self.assertEqual(auth_hdr_revision, 7329 hdr['EFI_FIRMWARE_IMAGE_AUTH.AUTH_INFO.HDR.wREVISION']) 7330 self.assertEqual(auth_hdr_cert_type, 7331 hdr['EFI_FIRMWARE_IMAGE_AUTH.AUTH_INFO.HDR.wCERTTYPE']) 7332 self.assertEqual(WIN_CERT_TYPE_EFI_GUID.upper(), 7333 hdr['EFI_FIRMWARE_IMAGE_AUTH.AUTH_INFO.CERT_TYPE']) 7334 7335 if version_check: 7336 self.assertEqual(fmp_signature, 7337 hdr['FMP_PAYLOAD_HDR.SIGNATURE']) 7338 self.assertEqual(fmp_size, 7339 hdr['FMP_PAYLOAD_HDR.HEADER_SIZE']) 7340 self.assertEqual(fmp_fw_version, 7341 hdr['FMP_PAYLOAD_HDR.FW_VERSION']) 7342 7343 self.assertEqual(payload_data_len, int(hdr['Payload Image Size'])) 7344 7345 def _CheckEmptyCapsule(self, data, accept_capsule=False): 7346 if accept_capsule: 7347 capsule_hdr_guid = EMPTY_CAPSULE_ACCEPT_GUID 7348 else: 7349 capsule_hdr_guid = EMPTY_CAPSULE_REVERT_GUID 7350 7351 hdr = self._GetCapsuleHeaders(data) 7352 7353 self.assertEqual(capsule_hdr_guid.upper(), 7354 hdr['EFI_CAPSULE_HDR.CAPSULE_GUID']) 7355 7356 if accept_capsule: 7357 capsule_size = "0000002C" 7358 else: 7359 capsule_size = "0000001C" 7360 self.assertEqual(capsule_size, 7361 hdr['EFI_CAPSULE_HDR.CAPSULE_IMAGE_SIZE']) 7362 7363 if accept_capsule: 7364 self.assertEqual(CAPSULE_IMAGE_GUID.upper(), hdr['ACCEPT_IMAGE_GUID']) 7365 7366 def testCapsuleGen(self): 7367 """Test generation of EFI capsule""" 7368 data = self._DoReadFile('311_capsule.dts') 7369 7370 self._CheckCapsule(data) 7371 7372 def testSignedCapsuleGen(self): 7373 """Test generation of EFI capsule""" 7374 data = tools.read_file(self.TestFile("key.key")) 7375 self._MakeInputFile("key.key", data) 7376 data = tools.read_file(self.TestFile("key.pem")) 7377 self._MakeInputFile("key.crt", data) 7378 7379 data = self._DoReadFile('312_capsule_signed.dts') 7380 7381 self._CheckCapsule(data, signed_capsule=True) 7382 7383 def testCapsuleGenVersionSupport(self): 7384 """Test generation of EFI capsule with version support""" 7385 data = self._DoReadFile('313_capsule_version.dts') 7386 7387 self._CheckCapsule(data, version_check=True) 7388 7389 def testCapsuleGenSignedVer(self): 7390 """Test generation of signed EFI capsule with version information""" 7391 data = tools.read_file(self.TestFile("key.key")) 7392 self._MakeInputFile("key.key", data) 7393 data = tools.read_file(self.TestFile("key.pem")) 7394 self._MakeInputFile("key.crt", data) 7395 7396 data = self._DoReadFile('314_capsule_signed_ver.dts') 7397 7398 self._CheckCapsule(data, signed_capsule=True, version_check=True) 7399 7400 def testCapsuleGenCapOemFlags(self): 7401 """Test generation of EFI capsule with OEM Flags set""" 7402 data = self._DoReadFile('315_capsule_oemflags.dts') 7403 7404 self._CheckCapsule(data, capoemflags=True) 7405 7406 def testCapsuleGenKeyMissing(self): 7407 """Test that binman errors out on missing key""" 7408 with self.assertRaises(ValueError) as e: 7409 self._DoReadFile('316_capsule_missing_key.dts') 7410 7411 self.assertIn("Both private key and public key certificate need to be provided", 7412 str(e.exception)) 7413 7414 def testCapsuleGenIndexMissing(self): 7415 """Test that binman errors out on missing image index""" 7416 with self.assertRaises(ValueError) as e: 7417 self._DoReadFile('317_capsule_missing_index.dts') 7418 7419 self.assertIn("entry is missing properties: image-index", 7420 str(e.exception)) 7421 7422 def testCapsuleGenGuidMissing(self): 7423 """Test that binman errors out on missing image GUID""" 7424 with self.assertRaises(ValueError) as e: 7425 self._DoReadFile('318_capsule_missing_guid.dts') 7426 7427 self.assertIn("entry is missing properties: image-guid", 7428 str(e.exception)) 7429 7430 def testCapsuleGenAcceptCapsule(self): 7431 """Test generationg of accept EFI capsule""" 7432 data = self._DoReadFile('319_capsule_accept.dts') 7433 7434 self._CheckEmptyCapsule(data, accept_capsule=True) 7435 7436 def testCapsuleGenRevertCapsule(self): 7437 """Test generationg of revert EFI capsule""" 7438 data = self._DoReadFile('320_capsule_revert.dts') 7439 7440 self._CheckEmptyCapsule(data) 7441 7442 def testCapsuleGenAcceptGuidMissing(self): 7443 """Test that binman errors out on missing image GUID for accept capsule""" 7444 with self.assertRaises(ValueError) as e: 7445 self._DoReadFile('321_capsule_accept_missing_guid.dts') 7446 7447 self.assertIn("Image GUID needed for generating accept capsule", 7448 str(e.exception)) 7449 7450 def testCapsuleGenEmptyCapsuleTypeMissing(self): 7451 """Test that capsule-type is specified""" 7452 with self.assertRaises(ValueError) as e: 7453 self._DoReadFile('322_empty_capsule_type_missing.dts') 7454 7455 self.assertIn("entry is missing properties: capsule-type", 7456 str(e.exception)) 7457 7458 def testCapsuleGenAcceptOrRevertMissing(self): 7459 """Test that both accept and revert capsule are not specified""" 7460 with self.assertRaises(ValueError) as e: 7461 self._DoReadFile('323_capsule_accept_revert_missing.dts') 7462 7463if __name__ == "__main__": 7464 unittest.main() 7465