"Das U-Boot" Source Tree
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()