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