blob: 4cf7dfc82165b37752d048e5fa76da6860d2003d [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001# SPDX-License-Identifier: GPL-2.0+
Simon Glass57454f42016-11-25 20:15:52 -07002# Copyright (c) 2016 Google, Inc
3# Written by Simon Glass <sjg@chromium.org>
4#
Simon Glass57454f42016-11-25 20:15:52 -07005# To run a single test, change to this directory, and:
6#
7# python -m unittest func_test.TestFunctional.testHelp
8
Simon Glass45d556d2020-07-09 18:39:45 -06009import collections
Simon Glassf3598922024-07-20 11:49:45 +010010import glob
Simon Glassc585dd42020-04-17 18:09:03 -060011import gzip
Simon Glassae7cf032018-09-14 04:57:31 -060012import hashlib
Simon Glass57454f42016-11-25 20:15:52 -070013from optparse import OptionParser
14import os
Simon Glass45d556d2020-07-09 18:39:45 -060015import re
Simon Glass57454f42016-11-25 20:15:52 -070016import shutil
17import struct
18import sys
19import tempfile
20import unittest
Simon Glass162017b2022-01-09 20:13:57 -070021import unittest.mock
22import urllib.error
Simon Glass57454f42016-11-25 20:15:52 -070023
Simon Glass4eae9252022-01-09 20:13:50 -070024from binman import bintool
Simon Glassc585dd42020-04-17 18:09:03 -060025from binman import cbfs_util
26from binman import cmdline
27from binman import control
28from binman import elf
29from binman import elf_test
Simon Glass3efb2972021-11-23 21:08:59 -070030from binman import fip_util
Simon Glassc585dd42020-04-17 18:09:03 -060031from binman import fmap_util
Simon Glassc585dd42020-04-17 18:09:03 -060032from binman import state
33from dtoc import fdt
34from dtoc import fdt_util
35from binman.etype import fdtmap
36from binman.etype import image_header
Simon Glass90cd6f02020-08-05 13:27:47 -060037from binman.image import Image
Simon Glass131444f2023-02-23 18:18:04 -070038from u_boot_pylib import command
Simon Glass14d64e32025-04-29 07:21:59 -060039from u_boot_pylib import terminal
Simon Glass131444f2023-02-23 18:18:04 -070040from u_boot_pylib import test_util
41from u_boot_pylib import tools
42from u_boot_pylib import tout
Simon Glass57454f42016-11-25 20:15:52 -070043
44# Contents of test files, corresponding to different entry types
Simon Glass303f62f2019-05-17 22:00:46 -060045U_BOOT_DATA = b'1234'
46U_BOOT_IMG_DATA = b'img'
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +030047U_BOOT_SPL_DATA = b'56780123456789abcdefghijklm'
48U_BOOT_TPL_DATA = b'tpl9876543210fedcbazywvuts'
Simon Glass56d05412022-02-28 07:16:54 -070049U_BOOT_VPL_DATA = b'vpl76543210fedcbazywxyz_'
Simon Glass303f62f2019-05-17 22:00:46 -060050BLOB_DATA = b'89'
51ME_DATA = b'0abcd'
52VGA_DATA = b'vga'
Sughosh Ganu269ee6d2023-08-22 23:09:59 +053053EFI_CAPSULE_DATA = b'efi'
Simon Glass303f62f2019-05-17 22:00:46 -060054U_BOOT_DTB_DATA = b'udtb'
55U_BOOT_SPL_DTB_DATA = b'spldtb'
56U_BOOT_TPL_DTB_DATA = b'tpldtb'
Simon Glass56d05412022-02-28 07:16:54 -070057U_BOOT_VPL_DTB_DATA = b'vpldtb'
Simon Glass303f62f2019-05-17 22:00:46 -060058X86_START16_DATA = b'start16'
59X86_START16_SPL_DATA = b'start16spl'
60X86_START16_TPL_DATA = b'start16tpl'
Simon Glass0b074d62019-08-24 07:22:48 -060061X86_RESET16_DATA = b'reset16'
62X86_RESET16_SPL_DATA = b'reset16spl'
63X86_RESET16_TPL_DATA = b'reset16tpl'
Simon Glass303f62f2019-05-17 22:00:46 -060064PPC_MPC85XX_BR_DATA = b'ppcmpc85xxbr'
65U_BOOT_NODTB_DATA = b'nodtb with microcode pointer somewhere in here'
66U_BOOT_SPL_NODTB_DATA = b'splnodtb with microcode pointer somewhere in here'
67U_BOOT_TPL_NODTB_DATA = b'tplnodtb with microcode pointer somewhere in here'
Simon Glass56d05412022-02-28 07:16:54 -070068U_BOOT_VPL_NODTB_DATA = b'vplnodtb'
Alper Nebi Yasak09fb0612022-02-08 01:08:04 +030069U_BOOT_EXP_DATA = U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA
70U_BOOT_SPL_EXP_DATA = U_BOOT_SPL_NODTB_DATA + U_BOOT_SPL_DTB_DATA
71U_BOOT_TPL_EXP_DATA = U_BOOT_TPL_NODTB_DATA + U_BOOT_TPL_DTB_DATA
Simon Glass303f62f2019-05-17 22:00:46 -060072FSP_DATA = b'fsp'
73CMC_DATA = b'cmc'
74VBT_DATA = b'vbt'
75MRC_DATA = b'mrc'
Simon Glass2ca52032018-07-17 13:25:33 -060076TEXT_DATA = 'text'
77TEXT_DATA2 = 'text2'
78TEXT_DATA3 = 'text3'
Simon Glass303f62f2019-05-17 22:00:46 -060079CROS_EC_RW_DATA = b'ecrw'
80GBB_DATA = b'gbbd'
81BMPBLK_DATA = b'bmp'
82VBLOCK_DATA = b'vblk'
83FILES_DATA = (b"sorry I'm late\nOh, don't bother apologising, I'm " +
84 b"sorry you're alive\n")
Simon Glassccec0262019-07-08 13:18:42 -060085COMPRESS_DATA = b'compress xxxxxxxxxxxxxxxxxxxxxx data'
Simon Glassd92c8362020-10-26 17:40:25 -060086COMPRESS_DATA_BIG = COMPRESS_DATA * 2
Simon Glass303f62f2019-05-17 22:00:46 -060087REFCODE_DATA = b'refcode'
Simon Glassba7985d2019-08-24 07:23:07 -060088FSP_M_DATA = b'fsp_m'
Simon Glass4d9086d2019-10-20 21:31:35 -060089FSP_S_DATA = b'fsp_s'
Simon Glass9ea87b22019-10-20 21:31:36 -060090FSP_T_DATA = b'fsp_t'
Bryan Brattlof5b45f4b2025-05-15 10:16:19 -050091ATF_BL1_DATA = b'bl1'
Simon Glass559c4de2020-09-01 05:13:58 -060092ATF_BL31_DATA = b'bl31'
Roger Quadros5cdcea02022-02-19 20:50:04 +020093TEE_OS_DATA = b'this is some tee OS data'
Neha Malcom Francis59be2552023-12-05 15:12:18 +053094TI_DM_DATA = b'tidmtidm'
Simon Glass3efb2972021-11-23 21:08:59 -070095ATF_BL2U_DATA = b'bl2u'
Bin Mengc0b15742021-05-10 20:23:33 +080096OPENSBI_DATA = b'opensbi'
Samuel Holland9d8cc632020-10-21 21:12:15 -050097SCP_DATA = b'scp'
Jonas Karlman35305492023-02-25 19:01:33 +000098ROCKCHIP_TPL_DATA = b'rockchip-tpl'
Simon Glassa435cd12020-09-01 05:13:59 -060099TEST_FDT1_DATA = b'fdt1'
100TEST_FDT2_DATA = b'test-fdt2'
Simon Glassa0729502020-09-06 10:35:33 -0600101ENV_DATA = b'var1=1\nvar2="2"'
Christian Taedcke62ac29a2023-07-17 09:05:54 +0200102ENCRYPTED_IV_DATA = b'123456'
103ENCRYPTED_KEY_DATA = b'abcde'
Philippe Reynesebe96cb2022-03-28 22:57:04 +0200104PRE_LOAD_MAGIC = b'UBSH'
105PRE_LOAD_VERSION = 0x11223344.to_bytes(4, 'big')
106PRE_LOAD_HDR_SIZE = 0x00001000.to_bytes(4, 'big')
Neha Malcom Francis3b788942023-07-22 00:14:24 +0530107TI_BOARD_CONFIG_DATA = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +0530108TI_UNSECURE_DATA = b'unsecuredata'
Alice Guo1d334022025-04-28 18:37:39 +0800109IMX_LPDDR_IMEM_DATA = b'qwertyuiop1234567890'
110IMX_LPDDR_DMEM_DATA = b'asdfghjklzxcvbnm'
Simon Glassa435cd12020-09-01 05:13:59 -0600111
112# Subdirectory of the input dir to use to put test FDTs
113TEST_FDT_SUBDIR = 'fdts'
Simon Glassdb168d42018-07-17 13:25:39 -0600114
Simon Glass2c6adba2019-07-20 12:23:47 -0600115# The expected size for the device tree in some tests
Simon Glass4c613bf2019-07-08 14:25:50 -0600116EXTRACT_DTB_SIZE = 0x3c9
117
Simon Glass2c6adba2019-07-20 12:23:47 -0600118# Properties expected to be in the device tree when update_dtb is used
119BASE_DTB_PROPS = ['offset', 'size', 'image-pos']
120
Simon Glassfb30e292019-07-20 12:23:51 -0600121# Extra properties expected to be in the device tree when allow-repack is used
122REPACK_DTB_PROPS = ['orig-offset', 'orig-size']
123
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +0200124# Supported compression bintools
Stefan Herbrechtsmeiera5e4dcb2022-08-19 16:25:38 +0200125COMP_BINTOOLS = ['bzip2', 'gzip', 'lz4', 'lzma_alone', 'lzop', 'xz', 'zstd']
Simon Glass57454f42016-11-25 20:15:52 -0700126
Simon Glassad5cfe12023-01-07 14:07:14 -0700127TEE_ADDR = 0x5678
128
Sughosh Ganu269ee6d2023-08-22 23:09:59 +0530129# Firmware Management Protocol(FMP) GUID
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +0530130FW_MGMT_GUID = '6dcbd5ed-e82d-4c44-bda1-7194199ad92a'
Sughosh Ganu269ee6d2023-08-22 23:09:59 +0530131# Image GUID specified in the DTS
Caleb Connolly4bdc9602024-08-30 13:34:35 +0100132CAPSULE_IMAGE_GUID = '985F2937-7C2E-5E9A-8A5E-8E063312964B'
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +0530133# Windows cert GUID
134WIN_CERT_TYPE_EFI_GUID = '4aafd29d-68df-49ee-8aa9-347d375665a7'
Sughosh Ganu6b2d18a2023-10-10 14:40:59 +0530135# Empty capsule GUIDs
136EMPTY_CAPSULE_ACCEPT_GUID = '0c996046-bcc0-4d04-85ec-e1fcedf1c6f8'
137EMPTY_CAPSULE_REVERT_GUID = 'acd58b4b-c0e8-475f-99b5-6b3f7e07aaf0'
Sughosh Ganu269ee6d2023-08-22 23:09:59 +0530138
Simon Glass57454f42016-11-25 20:15:52 -0700139class TestFunctional(unittest.TestCase):
140 """Functional tests for binman
141
142 Most of these use a sample .dts file to build an image and then check
143 that it looks correct. The sample files are in the test/ subdirectory
144 and are numbered.
145
146 For each entry type a very small test file is created using fixed
147 string contents. This makes it easy to test that things look right, and
148 debug problems.
149
150 In some cases a 'real' file must be used - these are also supplied in
151 the test/ diurectory.
152 """
153 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600154 def setUpClass(cls):
Simon Glassb3393262017-11-12 21:52:20 -0700155 global entry
Simon Glassc585dd42020-04-17 18:09:03 -0600156 from binman import entry
Simon Glassb3393262017-11-12 21:52:20 -0700157
Simon Glass57454f42016-11-25 20:15:52 -0700158 # Handle the case where argv[0] is 'python'
Simon Glass862f8e22019-08-24 07:22:43 -0600159 cls._binman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
160 cls._binman_pathname = os.path.join(cls._binman_dir, 'binman')
Simon Glass57454f42016-11-25 20:15:52 -0700161
162 # Create a temporary directory for input files
Simon Glass862f8e22019-08-24 07:22:43 -0600163 cls._indir = tempfile.mkdtemp(prefix='binmant.')
Simon Glass57454f42016-11-25 20:15:52 -0700164
165 # Create some test files
166 TestFunctional._MakeInputFile('u-boot.bin', U_BOOT_DATA)
167 TestFunctional._MakeInputFile('u-boot.img', U_BOOT_IMG_DATA)
168 TestFunctional._MakeInputFile('spl/u-boot-spl.bin', U_BOOT_SPL_DATA)
Simon Glass8425a1f2018-07-17 13:25:48 -0600169 TestFunctional._MakeInputFile('tpl/u-boot-tpl.bin', U_BOOT_TPL_DATA)
Simon Glass56d05412022-02-28 07:16:54 -0700170 TestFunctional._MakeInputFile('vpl/u-boot-vpl.bin', U_BOOT_VPL_DATA)
Simon Glass57454f42016-11-25 20:15:52 -0700171 TestFunctional._MakeInputFile('blobfile', BLOB_DATA)
Simon Glass72232452016-11-25 20:15:53 -0700172 TestFunctional._MakeInputFile('me.bin', ME_DATA)
173 TestFunctional._MakeInputFile('vga.bin', VGA_DATA)
Simon Glass862f8e22019-08-24 07:22:43 -0600174 cls._ResetDtbs()
Simon Glass0b074d62019-08-24 07:22:48 -0600175
Jagdish Gediya311d4842018-09-03 21:35:08 +0530176 TestFunctional._MakeInputFile('u-boot-br.bin', PPC_MPC85XX_BR_DATA)
Simon Glass0b074d62019-08-24 07:22:48 -0600177
Simon Glassabab18c2019-08-24 07:22:49 -0600178 TestFunctional._MakeInputFile('u-boot-x86-start16.bin', X86_START16_DATA)
179 TestFunctional._MakeInputFile('spl/u-boot-x86-start16-spl.bin',
Simon Glasse83679d2017-11-12 21:52:26 -0700180 X86_START16_SPL_DATA)
Simon Glassabab18c2019-08-24 07:22:49 -0600181 TestFunctional._MakeInputFile('tpl/u-boot-x86-start16-tpl.bin',
Simon Glassed40e962018-09-14 04:57:10 -0600182 X86_START16_TPL_DATA)
Simon Glass0b074d62019-08-24 07:22:48 -0600183
184 TestFunctional._MakeInputFile('u-boot-x86-reset16.bin',
185 X86_RESET16_DATA)
186 TestFunctional._MakeInputFile('spl/u-boot-x86-reset16-spl.bin',
187 X86_RESET16_SPL_DATA)
188 TestFunctional._MakeInputFile('tpl/u-boot-x86-reset16-tpl.bin',
189 X86_RESET16_TPL_DATA)
190
Simon Glass57454f42016-11-25 20:15:52 -0700191 TestFunctional._MakeInputFile('u-boot-nodtb.bin', U_BOOT_NODTB_DATA)
Simon Glass3d274232017-11-12 21:52:27 -0700192 TestFunctional._MakeInputFile('spl/u-boot-spl-nodtb.bin',
193 U_BOOT_SPL_NODTB_DATA)
Simon Glass3fb4f422018-09-14 04:57:32 -0600194 TestFunctional._MakeInputFile('tpl/u-boot-tpl-nodtb.bin',
195 U_BOOT_TPL_NODTB_DATA)
Simon Glass56d05412022-02-28 07:16:54 -0700196 TestFunctional._MakeInputFile('vpl/u-boot-vpl-nodtb.bin',
197 U_BOOT_VPL_NODTB_DATA)
Simon Glassb4176d42016-11-25 20:15:56 -0700198 TestFunctional._MakeInputFile('fsp.bin', FSP_DATA)
199 TestFunctional._MakeInputFile('cmc.bin', CMC_DATA)
Bin Mengd7bcdf52017-08-15 22:41:54 -0700200 TestFunctional._MakeInputFile('vbt.bin', VBT_DATA)
Simon Glassa409c932017-11-12 21:52:28 -0700201 TestFunctional._MakeInputFile('mrc.bin', MRC_DATA)
Simon Glassdb168d42018-07-17 13:25:39 -0600202 TestFunctional._MakeInputFile('ecrw.bin', CROS_EC_RW_DATA)
Simon Glassc1ae83c2018-07-17 13:25:44 -0600203 TestFunctional._MakeInputDir('devkeys')
204 TestFunctional._MakeInputFile('bmpblk.bin', BMPBLK_DATA)
Simon Glass41902e42018-10-01 12:22:31 -0600205 TestFunctional._MakeInputFile('refcode.bin', REFCODE_DATA)
Simon Glassba7985d2019-08-24 07:23:07 -0600206 TestFunctional._MakeInputFile('fsp_m.bin', FSP_M_DATA)
Simon Glass4d9086d2019-10-20 21:31:35 -0600207 TestFunctional._MakeInputFile('fsp_s.bin', FSP_S_DATA)
Simon Glass9ea87b22019-10-20 21:31:36 -0600208 TestFunctional._MakeInputFile('fsp_t.bin', FSP_T_DATA)
Alice Guo1d334022025-04-28 18:37:39 +0800209 TestFunctional._MakeInputFile('lpddr5_imem.bin', IMX_LPDDR_IMEM_DATA)
210 TestFunctional._MakeInputFile('lpddr5_dmem.bin', IMX_LPDDR_DMEM_DATA)
Simon Glass57454f42016-11-25 20:15:52 -0700211
Simon Glassf6290892019-08-24 07:22:53 -0600212 cls._elf_testdir = os.path.join(cls._indir, 'elftest')
213 elf_test.BuildElfTestFiles(cls._elf_testdir)
214
Simon Glass72232452016-11-25 20:15:53 -0700215 # ELF file with a '_dt_ucode_base_size' symbol
Simon Glass4affd4b2019-08-24 07:22:54 -0600216 TestFunctional._MakeInputFile('u-boot',
Simon Glass80025522022-01-29 14:14:04 -0700217 tools.read_file(cls.ElfTestFile('u_boot_ucode_ptr')))
Simon Glass72232452016-11-25 20:15:53 -0700218
219 # Intel flash descriptor file
Simon Glasse88cef92020-07-09 18:39:41 -0600220 cls._SetupDescriptor()
Simon Glass72232452016-11-25 20:15:53 -0700221
Simon Glass862f8e22019-08-24 07:22:43 -0600222 shutil.copytree(cls.TestFile('files'),
223 os.path.join(cls._indir, 'files'))
Simon Glassac6328c2018-09-14 04:57:28 -0600224
Neha Malcom Francis3b788942023-07-22 00:14:24 +0530225 shutil.copytree(cls.TestFile('yaml'),
226 os.path.join(cls._indir, 'yaml'))
227
Simon Glass7ba33592018-09-14 04:57:26 -0600228 TestFunctional._MakeInputFile('compress', COMPRESS_DATA)
Simon Glassd92c8362020-10-26 17:40:25 -0600229 TestFunctional._MakeInputFile('compress_big', COMPRESS_DATA_BIG)
Bryan Brattlof5b45f4b2025-05-15 10:16:19 -0500230 TestFunctional._MakeInputFile('bl1.bin', ATF_BL1_DATA)
Simon Glass559c4de2020-09-01 05:13:58 -0600231 TestFunctional._MakeInputFile('bl31.bin', ATF_BL31_DATA)
Roger Quadros5cdcea02022-02-19 20:50:04 +0200232 TestFunctional._MakeInputFile('tee-pager.bin', TEE_OS_DATA)
Neha Malcom Francis59be2552023-12-05 15:12:18 +0530233 TestFunctional._MakeInputFile('dm.bin', TI_DM_DATA)
Simon Glass3efb2972021-11-23 21:08:59 -0700234 TestFunctional._MakeInputFile('bl2u.bin', ATF_BL2U_DATA)
Bin Mengc0b15742021-05-10 20:23:33 +0800235 TestFunctional._MakeInputFile('fw_dynamic.bin', OPENSBI_DATA)
Samuel Holland9d8cc632020-10-21 21:12:15 -0500236 TestFunctional._MakeInputFile('scp.bin', SCP_DATA)
Jonas Karlman35305492023-02-25 19:01:33 +0000237 TestFunctional._MakeInputFile('rockchip-tpl.bin', ROCKCHIP_TPL_DATA)
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +0530238 TestFunctional._MakeInputFile('ti_unsecure.bin', TI_UNSECURE_DATA)
Sughosh Ganu269ee6d2023-08-22 23:09:59 +0530239 TestFunctional._MakeInputFile('capsule_input.bin', EFI_CAPSULE_DATA)
Simon Glass7ba33592018-09-14 04:57:26 -0600240
Simon Glassa435cd12020-09-01 05:13:59 -0600241 # Add a few .dtb files for testing
242 TestFunctional._MakeInputFile('%s/test-fdt1.dtb' % TEST_FDT_SUBDIR,
243 TEST_FDT1_DATA)
244 TestFunctional._MakeInputFile('%s/test-fdt2.dtb' % TEST_FDT_SUBDIR,
245 TEST_FDT2_DATA)
246
Simon Glassa0729502020-09-06 10:35:33 -0600247 TestFunctional._MakeInputFile('env.txt', ENV_DATA)
248
Simon Glass5f423422022-03-05 20:19:12 -0700249 # ELF file with two sections in different parts of memory, used for both
250 # ATF and OP_TEE
251 TestFunctional._MakeInputFile('bl31.elf',
252 tools.read_file(cls.ElfTestFile('elf_sections')))
253 TestFunctional._MakeInputFile('tee.elf',
254 tools.read_file(cls.ElfTestFile('elf_sections')))
255
Simon Glassad5cfe12023-01-07 14:07:14 -0700256 # Newer OP_TEE file in v1 binary format
257 cls.make_tee_bin('tee.bin')
258
Christian Taedcke62ac29a2023-07-17 09:05:54 +0200259 # test files for encrypted tests
260 TestFunctional._MakeInputFile('encrypted-file.iv', ENCRYPTED_IV_DATA)
261 TestFunctional._MakeInputFile('encrypted-file.key', ENCRYPTED_KEY_DATA)
262
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +0200263 cls.comp_bintools = {}
264 for name in COMP_BINTOOLS:
265 cls.comp_bintools[name] = bintool.Bintool.create(name)
Simon Glass1de34482019-07-08 13:18:53 -0600266
Simon Glass57454f42016-11-25 20:15:52 -0700267 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600268 def tearDownClass(cls):
Simon Glass57454f42016-11-25 20:15:52 -0700269 """Remove the temporary input directory and its contents"""
Simon Glass862f8e22019-08-24 07:22:43 -0600270 if cls.preserve_indir:
271 print('Preserving input dir: %s' % cls._indir)
Simon Glass1c420c92019-07-08 13:18:49 -0600272 else:
Simon Glass862f8e22019-08-24 07:22:43 -0600273 if cls._indir:
274 shutil.rmtree(cls._indir)
275 cls._indir = None
Simon Glass57454f42016-11-25 20:15:52 -0700276
Simon Glass1c420c92019-07-08 13:18:49 -0600277 @classmethod
Simon Glasscebfab22019-07-08 13:18:50 -0600278 def setup_test_args(cls, preserve_indir=False, preserve_outdirs=False,
Simon Glass17899e42025-04-29 07:22:06 -0600279 toolpath=None, verbosity=None, no_capture=False):
Simon Glass1c420c92019-07-08 13:18:49 -0600280 """Accept arguments controlling test execution
281
282 Args:
283 preserve_indir: Preserve the shared input directory used by all
284 tests in this class.
285 preserve_outdir: Preserve the output directories used by tests. Each
286 test has its own, so this is normally only useful when running a
287 single test.
Simon Glass1a0b27d2025-04-29 07:22:00 -0600288 toolpath: list of paths to use for tools
Simon Glass1c420c92019-07-08 13:18:49 -0600289 """
290 cls.preserve_indir = preserve_indir
291 cls.preserve_outdirs = preserve_outdirs
Simon Glasscebfab22019-07-08 13:18:50 -0600292 cls.toolpath = toolpath
Simon Glassf46732a2019-07-08 14:25:29 -0600293 cls.verbosity = verbosity
Simon Glass17899e42025-04-29 07:22:06 -0600294 cls.no_capture = no_capture
Simon Glass1c420c92019-07-08 13:18:49 -0600295
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +0200296 def _CheckBintool(self, bintool):
297 if not bintool.is_present():
298 self.skipTest('%s not available' % bintool.name)
299
Simon Glass1de34482019-07-08 13:18:53 -0600300 def _CheckLz4(self):
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +0200301 bintool = self.comp_bintools['lz4']
302 self._CheckBintool(bintool)
Simon Glass1de34482019-07-08 13:18:53 -0600303
Simon Glassee9d10d2019-07-20 12:24:09 -0600304 def _CleanupOutputDir(self):
305 """Remove the temporary output directory"""
306 if self.preserve_outdirs:
307 print('Preserving output dir: %s' % tools.outdir)
308 else:
Simon Glass80025522022-01-29 14:14:04 -0700309 tools._finalise_for_test()
Simon Glassee9d10d2019-07-20 12:24:09 -0600310
Simon Glass57454f42016-11-25 20:15:52 -0700311 def setUp(self):
312 # Enable this to turn on debugging output
Simon Glass011f1b32022-01-29 14:14:15 -0700313 # tout.init(tout.DEBUG)
Simon Glass5dc22cf2025-02-03 09:26:42 -0700314 command.TEST_RESULT = None
Simon Glass57454f42016-11-25 20:15:52 -0700315
316 def tearDown(self):
317 """Remove the temporary output directory"""
Simon Glassee9d10d2019-07-20 12:24:09 -0600318 self._CleanupOutputDir()
Simon Glass57454f42016-11-25 20:15:52 -0700319
Simon Glassb3d6fc72019-07-20 12:24:10 -0600320 def _SetupImageInTmpdir(self):
321 """Set up the output image in a new temporary directory
322
323 This is used when an image has been generated in the output directory,
324 but we want to run binman again. This will create a new output
325 directory and fail to delete the original one.
326
327 This creates a new temporary directory, copies the image to it (with a
328 new name) and removes the old output directory.
329
330 Returns:
331 Tuple:
332 Temporary directory to use
333 New image filename
334 """
Simon Glass80025522022-01-29 14:14:04 -0700335 image_fname = tools.get_output_filename('image.bin')
Simon Glassb3d6fc72019-07-20 12:24:10 -0600336 tmpdir = tempfile.mkdtemp(prefix='binman.')
337 updated_fname = os.path.join(tmpdir, 'image-updated.bin')
Simon Glass80025522022-01-29 14:14:04 -0700338 tools.write_file(updated_fname, tools.read_file(image_fname))
Simon Glassb3d6fc72019-07-20 12:24:10 -0600339 self._CleanupOutputDir()
340 return tmpdir, updated_fname
341
Simon Glass8425a1f2018-07-17 13:25:48 -0600342 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600343 def _ResetDtbs(cls):
Simon Glass8425a1f2018-07-17 13:25:48 -0600344 TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA)
345 TestFunctional._MakeInputFile('spl/u-boot-spl.dtb', U_BOOT_SPL_DTB_DATA)
346 TestFunctional._MakeInputFile('tpl/u-boot-tpl.dtb', U_BOOT_TPL_DTB_DATA)
Simon Glass56d05412022-02-28 07:16:54 -0700347 TestFunctional._MakeInputFile('vpl/u-boot-vpl.dtb', U_BOOT_VPL_DTB_DATA)
Simon Glass8425a1f2018-07-17 13:25:48 -0600348
Simon Glass57454f42016-11-25 20:15:52 -0700349 def _RunBinman(self, *args, **kwargs):
350 """Run binman using the command line
351
352 Args:
353 Arguments to pass, as a list of strings
354 kwargs: Arguments to pass to Command.RunPipe()
355 """
Simon Glass51f55182025-02-03 09:26:45 -0700356 all_args = [self._binman_pathname] + list(args)
357 result = command.run_one(*all_args, capture=True, capture_stderr=True,
358 raise_on_error=False)
Simon Glass57454f42016-11-25 20:15:52 -0700359 if result.return_code and kwargs.get('raise_on_error', True):
360 raise Exception("Error running '%s': %s" % (' '.join(args),
361 result.stdout + result.stderr))
362 return result
363
Simon Glassf46732a2019-07-08 14:25:29 -0600364 def _DoBinman(self, *argv):
Simon Glass57454f42016-11-25 20:15:52 -0700365 """Run binman using directly (in the same process)
366
367 Args:
368 Arguments to pass, as a list of strings
369 Returns:
370 Return value (0 for success)
371 """
Simon Glassf46732a2019-07-08 14:25:29 -0600372 argv = list(argv)
373 args = cmdline.ParseArgs(argv)
374 args.pager = 'binman-invalid-pager'
375 args.build_dir = self._indir
Simon Glass57454f42016-11-25 20:15:52 -0700376
377 # For testing, you can force an increase in verbosity here
Simon Glassf46732a2019-07-08 14:25:29 -0600378 # args.verbosity = tout.DEBUG
379 return control.Binman(args)
Simon Glass57454f42016-11-25 20:15:52 -0700380
Simon Glass91710b32018-07-17 13:25:32 -0600381 def _DoTestFile(self, fname, debug=False, map=False, update_dtb=False,
Simon Glassb4595d82019-04-25 21:58:34 -0600382 entry_args=None, images=None, use_real_dtb=False,
Simon Glassed930672021-03-18 20:25:05 +1300383 use_expanded=False, verbosity=None, allow_missing=False,
Heiko Thiery6d451362022-01-06 11:49:41 +0100384 allow_fake_blobs=False, extra_indirs=None, threads=None,
Simon Glass66152ce2022-01-09 20:14:09 -0700385 test_section_timeout=False, update_fdt_in_elf=None,
Andrew Davis6b463da2023-07-22 00:14:44 +0530386 force_missing_bintools='', ignore_missing=False, output_dir=None):
Simon Glass57454f42016-11-25 20:15:52 -0700387 """Run binman with a given test file
388
389 Args:
Simon Glass511f6582018-10-01 12:22:30 -0600390 fname: Device-tree source filename to use (e.g. 005_simple.dts)
Simon Glass1e324002018-06-01 09:38:19 -0600391 debug: True to enable debugging output
Simon Glass30732662018-06-01 09:38:20 -0600392 map: True to output map files for the images
Simon Glasse8561af2018-08-01 15:22:37 -0600393 update_dtb: Update the offset and size of each entry in the device
Simon Glassa87014e2018-07-06 10:27:42 -0600394 tree before packing it into the image
Simon Glass3b376c32018-09-14 04:57:12 -0600395 entry_args: Dict of entry args to supply to binman
396 key: arg name
397 value: value of that arg
398 images: List of image names to build
Simon Glass31ee50f2020-09-01 05:13:55 -0600399 use_real_dtb: True to use the test file as the contents of
400 the u-boot-dtb entry. Normally this is not needed and the
401 test contents (the U_BOOT_DTB_DATA string) can be used.
402 But in some test we need the real contents.
Simon Glassed930672021-03-18 20:25:05 +1300403 use_expanded: True to use expanded entries where available, e.g.
404 'u-boot-expanded' instead of 'u-boot'
Simon Glass31ee50f2020-09-01 05:13:55 -0600405 verbosity: Verbosity level to use (0-3, None=don't set it)
406 allow_missing: Set the '--allow-missing' flag so that missing
407 external binaries just produce a warning instead of an error
Heiko Thiery6d451362022-01-06 11:49:41 +0100408 allow_fake_blobs: Set the '--fake-ext-blobs' flag
Simon Glassa435cd12020-09-01 05:13:59 -0600409 extra_indirs: Extra input directories to add using -I
Simon Glass76f496d2021-07-06 10:36:37 -0600410 threads: Number of threads to use (None for default, 0 for
411 single-threaded)
Simon Glass9a798402021-11-03 21:09:17 -0600412 test_section_timeout: True to force the first time to timeout, as
413 used in testThreadTimeout()
Simon Glassadfb8492021-11-03 21:09:18 -0600414 update_fdt_in_elf: Value to pass with --update-fdt-in-elf=xxx
Simon Glassb553e8a2024-08-26 13:11:29 -0600415 force_missing_bintools (str): comma-separated list of bintools to
Simon Glass66152ce2022-01-09 20:14:09 -0700416 regard as missing
Simon Glassb553e8a2024-08-26 13:11:29 -0600417 ignore_missing (bool): True to return success even if there are
418 missing blobs or bintools
Andrew Davis6b463da2023-07-22 00:14:44 +0530419 output_dir: Specific output directory to use for image using -O
Simon Glass9a798402021-11-03 21:09:17 -0600420
421 Returns:
422 int return code, 0 on success
Simon Glass57454f42016-11-25 20:15:52 -0700423 """
Simon Glassf46732a2019-07-08 14:25:29 -0600424 args = []
Simon Glass075a45c2017-11-13 18:55:00 -0700425 if debug:
426 args.append('-D')
Simon Glassf46732a2019-07-08 14:25:29 -0600427 if verbosity is not None:
428 args.append('-v%d' % verbosity)
429 elif self.verbosity:
430 args.append('-v%d' % self.verbosity)
431 if self.toolpath:
432 for path in self.toolpath:
433 args += ['--toolpath', path]
Simon Glass76f496d2021-07-06 10:36:37 -0600434 if threads is not None:
435 args.append('-T%d' % threads)
436 if test_section_timeout:
437 args.append('--test-section-timeout')
Simon Glassf46732a2019-07-08 14:25:29 -0600438 args += ['build', '-p', '-I', self._indir, '-d', self.TestFile(fname)]
Simon Glass30732662018-06-01 09:38:20 -0600439 if map:
440 args.append('-m')
Simon Glassa87014e2018-07-06 10:27:42 -0600441 if update_dtb:
Simon Glass38a411c2019-07-08 13:18:47 -0600442 args.append('-u')
Simon Glass31402012018-09-14 04:57:23 -0600443 if not use_real_dtb:
444 args.append('--fake-dtb')
Simon Glassed930672021-03-18 20:25:05 +1300445 if not use_expanded:
446 args.append('--no-expanded')
Simon Glass91710b32018-07-17 13:25:32 -0600447 if entry_args:
Simon Glass5f3645b2019-05-14 15:53:41 -0600448 for arg, value in entry_args.items():
Simon Glass91710b32018-07-17 13:25:32 -0600449 args.append('-a%s=%s' % (arg, value))
Simon Glass5d94cc62020-07-09 18:39:38 -0600450 if allow_missing:
451 args.append('-M')
Simon Glass6bce5dc2022-11-09 19:14:42 -0700452 if ignore_missing:
453 args.append('-W')
Heiko Thiery6d451362022-01-06 11:49:41 +0100454 if allow_fake_blobs:
455 args.append('--fake-ext-blobs')
Simon Glass66152ce2022-01-09 20:14:09 -0700456 if force_missing_bintools:
457 args += ['--force-missing-bintools', force_missing_bintools]
Simon Glassadfb8492021-11-03 21:09:18 -0600458 if update_fdt_in_elf:
459 args += ['--update-fdt-in-elf', update_fdt_in_elf]
Simon Glass3b376c32018-09-14 04:57:12 -0600460 if images:
461 for image in images:
462 args += ['-i', image]
Simon Glassa435cd12020-09-01 05:13:59 -0600463 if extra_indirs:
464 for indir in extra_indirs:
465 args += ['-I', indir]
Andrew Davis6b463da2023-07-22 00:14:44 +0530466 if output_dir:
467 args += ['-O', output_dir]
Simon Glass075a45c2017-11-13 18:55:00 -0700468 return self._DoBinman(*args)
Simon Glass57454f42016-11-25 20:15:52 -0700469
470 def _SetupDtb(self, fname, outfile='u-boot.dtb'):
Simon Glass72232452016-11-25 20:15:53 -0700471 """Set up a new test device-tree file
472
473 The given file is compiled and set up as the device tree to be used
474 for ths test.
475
476 Args:
477 fname: Filename of .dts file to read
Simon Glass1e324002018-06-01 09:38:19 -0600478 outfile: Output filename for compiled device-tree binary
Simon Glass72232452016-11-25 20:15:53 -0700479
480 Returns:
Simon Glass1e324002018-06-01 09:38:19 -0600481 Contents of device-tree binary
Simon Glass72232452016-11-25 20:15:53 -0700482 """
Simon Glassb8d2daa2019-07-20 12:23:49 -0600483 tmpdir = tempfile.mkdtemp(prefix='binmant.')
484 dtb = fdt_util.EnsureCompiled(self.TestFile(fname), tmpdir)
Simon Glass33486662019-05-14 15:53:42 -0600485 with open(dtb, 'rb') as fd:
Simon Glass57454f42016-11-25 20:15:52 -0700486 data = fd.read()
487 TestFunctional._MakeInputFile(outfile, data)
Simon Glassb8d2daa2019-07-20 12:23:49 -0600488 shutil.rmtree(tmpdir)
Simon Glass752e7552018-10-01 21:12:41 -0600489 return data
Simon Glass57454f42016-11-25 20:15:52 -0700490
Simon Glass56d05412022-02-28 07:16:54 -0700491 def _GetDtbContentsForSpls(self, dtb_data, name):
492 """Create a version of the main DTB for SPL / TPL / VPL
Simon Glasse219aa42018-09-14 04:57:24 -0600493
494 For testing we don't actually have different versions of the DTB. With
495 U-Boot we normally run fdtgrep to remove unwanted nodes, but for tests
496 we don't normally have any unwanted nodes.
497
498 We still want the DTBs for SPL and TPL to be different though, since
499 otherwise it is confusing to know which one we are looking at. So add
500 an 'spl' or 'tpl' property to the top-level node.
Simon Glass31ee50f2020-09-01 05:13:55 -0600501
502 Args:
503 dtb_data: dtb data to modify (this should be a value devicetree)
504 name: Name of a new property to add
505
506 Returns:
507 New dtb data with the property added
Simon Glasse219aa42018-09-14 04:57:24 -0600508 """
509 dtb = fdt.Fdt.FromData(dtb_data)
510 dtb.Scan()
511 dtb.GetNode('/binman').AddZeroProp(name)
512 dtb.Sync(auto_resize=True)
513 dtb.Pack()
514 return dtb.GetContents()
515
Simon Glassed930672021-03-18 20:25:05 +1300516 def _DoReadFileDtb(self, fname, use_real_dtb=False, use_expanded=False,
Simon Glass3eb30a42024-08-26 13:11:42 -0600517 verbosity=None, map=False, update_dtb=False,
518 entry_args=None, reset_dtbs=True, extra_indirs=None,
519 threads=None):
Simon Glass57454f42016-11-25 20:15:52 -0700520 """Run binman and return the resulting image
521
522 This runs binman with a given test file and then reads the resulting
523 output file. It is a shortcut function since most tests need to do
524 these steps.
525
526 Raises an assertion failure if binman returns a non-zero exit code.
527
528 Args:
Simon Glass511f6582018-10-01 12:22:30 -0600529 fname: Device-tree source filename to use (e.g. 005_simple.dts)
Simon Glass57454f42016-11-25 20:15:52 -0700530 use_real_dtb: True to use the test file as the contents of
531 the u-boot-dtb entry. Normally this is not needed and the
532 test contents (the U_BOOT_DTB_DATA string) can be used.
533 But in some test we need the real contents.
Simon Glassed930672021-03-18 20:25:05 +1300534 use_expanded: True to use expanded entries where available, e.g.
535 'u-boot-expanded' instead of 'u-boot'
Simon Glass3eb30a42024-08-26 13:11:42 -0600536 verbosity: Verbosity level to use (0-3, None=don't set it)
Simon Glass30732662018-06-01 09:38:20 -0600537 map: True to output map files for the images
Simon Glasse8561af2018-08-01 15:22:37 -0600538 update_dtb: Update the offset and size of each entry in the device
Simon Glassa87014e2018-07-06 10:27:42 -0600539 tree before packing it into the image
Simon Glass31ee50f2020-09-01 05:13:55 -0600540 entry_args: Dict of entry args to supply to binman
541 key: arg name
542 value: value of that arg
543 reset_dtbs: With use_real_dtb the test dtb is overwritten by this
544 function. If reset_dtbs is True, then the original test dtb
545 is written back before this function finishes
Simon Glassa435cd12020-09-01 05:13:59 -0600546 extra_indirs: Extra input directories to add using -I
Simon Glass76f496d2021-07-06 10:36:37 -0600547 threads: Number of threads to use (None for default, 0 for
548 single-threaded)
Simon Glass72232452016-11-25 20:15:53 -0700549
550 Returns:
551 Tuple:
552 Resulting image contents
553 Device tree contents
Simon Glass30732662018-06-01 09:38:20 -0600554 Map data showing contents of image (or None if none)
Simon Glassdef77b52018-07-17 13:25:27 -0600555 Output device tree binary filename ('u-boot.dtb' path)
Simon Glass57454f42016-11-25 20:15:52 -0700556 """
Simon Glass72232452016-11-25 20:15:53 -0700557 dtb_data = None
Simon Glass57454f42016-11-25 20:15:52 -0700558 # Use the compiled test file as the u-boot-dtb input
559 if use_real_dtb:
Simon Glass72232452016-11-25 20:15:53 -0700560 dtb_data = self._SetupDtb(fname)
Simon Glasse219aa42018-09-14 04:57:24 -0600561
562 # For testing purposes, make a copy of the DT for SPL and TPL. Add
Simon Glassd9e01d22024-07-20 11:49:40 +0100563 # a node indicating which it is, to aid verification.
Simon Glass56d05412022-02-28 07:16:54 -0700564 for name in ['spl', 'tpl', 'vpl']:
Simon Glasse219aa42018-09-14 04:57:24 -0600565 dtb_fname = '%s/u-boot-%s.dtb' % (name, name)
566 outfile = os.path.join(self._indir, dtb_fname)
567 TestFunctional._MakeInputFile(dtb_fname,
Simon Glass56d05412022-02-28 07:16:54 -0700568 self._GetDtbContentsForSpls(dtb_data, name))
Simon Glass57454f42016-11-25 20:15:52 -0700569
570 try:
Simon Glass91710b32018-07-17 13:25:32 -0600571 retcode = self._DoTestFile(fname, map=map, update_dtb=update_dtb,
Simon Glassa435cd12020-09-01 05:13:59 -0600572 entry_args=entry_args, use_real_dtb=use_real_dtb,
Simon Glass3eb30a42024-08-26 13:11:42 -0600573 use_expanded=use_expanded, verbosity=verbosity,
574 extra_indirs=extra_indirs,
Simon Glass76f496d2021-07-06 10:36:37 -0600575 threads=threads)
Simon Glass57454f42016-11-25 20:15:52 -0700576 self.assertEqual(0, retcode)
Simon Glass80025522022-01-29 14:14:04 -0700577 out_dtb_fname = tools.get_output_filename('u-boot.dtb.out')
Simon Glass57454f42016-11-25 20:15:52 -0700578
579 # Find the (only) image, read it and return its contents
580 image = control.images['image']
Simon Glass80025522022-01-29 14:14:04 -0700581 image_fname = tools.get_output_filename('image.bin')
Simon Glassa87014e2018-07-06 10:27:42 -0600582 self.assertTrue(os.path.exists(image_fname))
Simon Glass30732662018-06-01 09:38:20 -0600583 if map:
Simon Glass80025522022-01-29 14:14:04 -0700584 map_fname = tools.get_output_filename('image.map')
Simon Glass30732662018-06-01 09:38:20 -0600585 with open(map_fname) as fd:
586 map_data = fd.read()
587 else:
588 map_data = None
Simon Glass33486662019-05-14 15:53:42 -0600589 with open(image_fname, 'rb') as fd:
Simon Glassa87014e2018-07-06 10:27:42 -0600590 return fd.read(), dtb_data, map_data, out_dtb_fname
Simon Glass57454f42016-11-25 20:15:52 -0700591 finally:
592 # Put the test file back
Simon Glasse219aa42018-09-14 04:57:24 -0600593 if reset_dtbs and use_real_dtb:
Simon Glass8425a1f2018-07-17 13:25:48 -0600594 self._ResetDtbs()
Simon Glass57454f42016-11-25 20:15:52 -0700595
Simon Glass5b4bce32019-07-08 14:25:26 -0600596 def _DoReadFileRealDtb(self, fname):
597 """Run binman with a real .dtb file and return the resulting data
598
599 Args:
600 fname: DT source filename to use (e.g. 082_fdt_update_all.dts)
601
602 Returns:
603 Resulting image contents
604 """
605 return self._DoReadFileDtb(fname, use_real_dtb=True, update_dtb=True)[0]
606
Simon Glass72232452016-11-25 20:15:53 -0700607 def _DoReadFile(self, fname, use_real_dtb=False):
Simon Glass1e324002018-06-01 09:38:19 -0600608 """Helper function which discards the device-tree binary
609
610 Args:
Simon Glass511f6582018-10-01 12:22:30 -0600611 fname: Device-tree source filename to use (e.g. 005_simple.dts)
Simon Glass1e324002018-06-01 09:38:19 -0600612 use_real_dtb: True to use the test file as the contents of
613 the u-boot-dtb entry. Normally this is not needed and the
614 test contents (the U_BOOT_DTB_DATA string) can be used.
615 But in some test we need the real contents.
Simon Glassdef77b52018-07-17 13:25:27 -0600616
617 Returns:
618 Resulting image contents
Simon Glass1e324002018-06-01 09:38:19 -0600619 """
Simon Glass72232452016-11-25 20:15:53 -0700620 return self._DoReadFileDtb(fname, use_real_dtb)[0]
621
Simon Glass57454f42016-11-25 20:15:52 -0700622 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600623 def _MakeInputFile(cls, fname, contents):
Simon Glass57454f42016-11-25 20:15:52 -0700624 """Create a new test input file, creating directories as needed
625
626 Args:
Simon Glasse8561af2018-08-01 15:22:37 -0600627 fname: Filename to create
Simon Glass57454f42016-11-25 20:15:52 -0700628 contents: File contents to write in to the file
629 Returns:
630 Full pathname of file created
631 """
Simon Glass862f8e22019-08-24 07:22:43 -0600632 pathname = os.path.join(cls._indir, fname)
Simon Glass57454f42016-11-25 20:15:52 -0700633 dirname = os.path.dirname(pathname)
634 if dirname and not os.path.exists(dirname):
635 os.makedirs(dirname)
636 with open(pathname, 'wb') as fd:
637 fd.write(contents)
638 return pathname
639
640 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600641 def _MakeInputDir(cls, dirname):
Simon Glassc1ae83c2018-07-17 13:25:44 -0600642 """Create a new test input directory, creating directories as needed
643
644 Args:
645 dirname: Directory name to create
646
647 Returns:
648 Full pathname of directory created
649 """
Simon Glass862f8e22019-08-24 07:22:43 -0600650 pathname = os.path.join(cls._indir, dirname)
Simon Glassc1ae83c2018-07-17 13:25:44 -0600651 if not os.path.exists(pathname):
652 os.makedirs(pathname)
653 return pathname
654
655 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600656 def _SetupSplElf(cls, src_fname='bss_data'):
Simon Glass7057d022018-10-01 21:12:47 -0600657 """Set up an ELF file with a '_dt_ucode_base_size' symbol
658
659 Args:
660 Filename of ELF file to use as SPL
661 """
Simon Glass93a806f2019-08-24 07:22:59 -0600662 TestFunctional._MakeInputFile('spl/u-boot-spl',
Simon Glass80025522022-01-29 14:14:04 -0700663 tools.read_file(cls.ElfTestFile(src_fname)))
Simon Glass7057d022018-10-01 21:12:47 -0600664
665 @classmethod
Simon Glass3eb5b202019-08-24 07:23:00 -0600666 def _SetupTplElf(cls, src_fname='bss_data'):
667 """Set up an ELF file with a '_dt_ucode_base_size' symbol
668
669 Args:
670 Filename of ELF file to use as TPL
671 """
672 TestFunctional._MakeInputFile('tpl/u-boot-tpl',
Simon Glass80025522022-01-29 14:14:04 -0700673 tools.read_file(cls.ElfTestFile(src_fname)))
Simon Glass3eb5b202019-08-24 07:23:00 -0600674
675 @classmethod
Simon Glass56d05412022-02-28 07:16:54 -0700676 def _SetupVplElf(cls, src_fname='bss_data'):
677 """Set up an ELF file with a '_dt_ucode_base_size' symbol
678
679 Args:
680 Filename of ELF file to use as VPL
681 """
682 TestFunctional._MakeInputFile('vpl/u-boot-vpl',
683 tools.read_file(cls.ElfTestFile(src_fname)))
684
685 @classmethod
Lukas Funkee901faf2023-07-18 13:53:13 +0200686 def _SetupPmuFwlElf(cls, src_fname='bss_data'):
687 """Set up an ELF file with a '_dt_ucode_base_size' symbol
688
689 Args:
690 Filename of ELF file to use as VPL
691 """
692 TestFunctional._MakeInputFile('pmu-firmware.elf',
693 tools.read_file(cls.ElfTestFile(src_fname)))
694
695 @classmethod
Simon Glasse88cef92020-07-09 18:39:41 -0600696 def _SetupDescriptor(cls):
697 with open(cls.TestFile('descriptor.bin'), 'rb') as fd:
698 TestFunctional._MakeInputFile('descriptor.bin', fd.read())
699
700 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600701 def TestFile(cls, fname):
702 return os.path.join(cls._binman_dir, 'test', fname)
Simon Glass57454f42016-11-25 20:15:52 -0700703
Simon Glassf6290892019-08-24 07:22:53 -0600704 @classmethod
705 def ElfTestFile(cls, fname):
706 return os.path.join(cls._elf_testdir, fname)
707
Simon Glassad5cfe12023-01-07 14:07:14 -0700708 @classmethod
709 def make_tee_bin(cls, fname, paged_sz=0, extra_data=b''):
710 init_sz, start_hi, start_lo, dummy = (len(U_BOOT_DATA), 0, TEE_ADDR, 0)
711 data = b'OPTE\x01xxx' + struct.pack('<5I', init_sz, start_hi, start_lo,
712 dummy, paged_sz) + U_BOOT_DATA
713 data += extra_data
714 TestFunctional._MakeInputFile(fname, data)
715
Simon Glass57454f42016-11-25 20:15:52 -0700716 def AssertInList(self, grep_list, target):
717 """Assert that at least one of a list of things is in a target
718
719 Args:
720 grep_list: List of strings to check
721 target: Target string
722 """
723 for grep in grep_list:
724 if grep in target:
725 return
Simon Glass848cdb52019-05-17 22:00:50 -0600726 self.fail("Error: '%s' not found in '%s'" % (grep_list, target))
Simon Glass57454f42016-11-25 20:15:52 -0700727
728 def CheckNoGaps(self, entries):
729 """Check that all entries fit together without gaps
730
731 Args:
732 entries: List of entries to check
733 """
Simon Glasse8561af2018-08-01 15:22:37 -0600734 offset = 0
Simon Glass57454f42016-11-25 20:15:52 -0700735 for entry in entries.values():
Simon Glasse8561af2018-08-01 15:22:37 -0600736 self.assertEqual(offset, entry.offset)
737 offset += entry.size
Simon Glass57454f42016-11-25 20:15:52 -0700738
Simon Glass72232452016-11-25 20:15:53 -0700739 def GetFdtLen(self, dtb):
Simon Glass1e324002018-06-01 09:38:19 -0600740 """Get the totalsize field from a device-tree binary
Simon Glass72232452016-11-25 20:15:53 -0700741
742 Args:
Simon Glass1e324002018-06-01 09:38:19 -0600743 dtb: Device-tree binary contents
Simon Glass72232452016-11-25 20:15:53 -0700744
745 Returns:
Simon Glass1e324002018-06-01 09:38:19 -0600746 Total size of device-tree binary, from the header
Simon Glass72232452016-11-25 20:15:53 -0700747 """
748 return struct.unpack('>L', dtb[4:8])[0]
749
Simon Glass0f621332019-07-08 14:25:27 -0600750 def _GetPropTree(self, dtb, prop_names, prefix='/binman/'):
Simon Glassa87014e2018-07-06 10:27:42 -0600751 def AddNode(node, path):
752 if node.name != '/':
753 path += '/' + node.name
Simon Glass0f621332019-07-08 14:25:27 -0600754 for prop in node.props.values():
755 if prop.name in prop_names:
756 prop_path = path + ':' + prop.name
757 tree[prop_path[len(prefix):]] = fdt_util.fdt32_to_cpu(
758 prop.value)
Simon Glassa87014e2018-07-06 10:27:42 -0600759 for subnode in node.subnodes:
Simon Glassa87014e2018-07-06 10:27:42 -0600760 AddNode(subnode, path)
761
762 tree = {}
Simon Glassa87014e2018-07-06 10:27:42 -0600763 AddNode(dtb.GetRoot(), '')
764 return tree
765
Ivan Mikhaylov3cfcaa4d2023-03-08 01:13:40 +0000766 def _CheckSign(self, fit, key):
767 try:
768 tools.run('fit_check_sign', '-k', key, '-f', fit)
769 except:
770 self.fail('Expected signed FIT container')
771 return False
772 return True
773
Paul HENRYS5cf82892025-02-24 22:20:55 +0100774 def _CheckPreload(self, image, key, algo="sha256,rsa2048",
775 padding="pkcs-1.5"):
776 try:
777 tools.run('preload_check_sign', '-k', key, '-a', algo, '-p',
778 padding, '-f', image)
779 except:
780 self.fail('Expected image signed with a pre-load')
781 return False
782 return True
783
Simon Glass57454f42016-11-25 20:15:52 -0700784 def testRun(self):
785 """Test a basic run with valid args"""
786 result = self._RunBinman('-h')
787
788 def testFullHelp(self):
789 """Test that the full help is displayed with -H"""
790 result = self._RunBinman('-H')
Simon Glass75ead662021-03-18 20:25:13 +1300791 help_file = os.path.join(self._binman_dir, 'README.rst')
Tom Rinic3c0b6d2018-01-16 15:29:50 -0500792 # Remove possible extraneous strings
793 extra = '::::::::::::::\n' + help_file + '\n::::::::::::::\n'
794 gothelp = result.stdout.replace(extra, '')
795 self.assertEqual(len(gothelp), os.path.getsize(help_file))
Simon Glass57454f42016-11-25 20:15:52 -0700796 self.assertEqual(0, len(result.stderr))
797 self.assertEqual(0, result.return_code)
798
799 def testFullHelpInternal(self):
800 """Test that the full help is displayed with -H"""
801 try:
Simon Glass5dc22cf2025-02-03 09:26:42 -0700802 command.TEST_RESULT = command.CommandResult()
Simon Glass57454f42016-11-25 20:15:52 -0700803 result = self._DoBinman('-H')
Simon Glass75ead662021-03-18 20:25:13 +1300804 help_file = os.path.join(self._binman_dir, 'README.rst')
Simon Glass57454f42016-11-25 20:15:52 -0700805 finally:
Simon Glass5dc22cf2025-02-03 09:26:42 -0700806 command.TEST_RESULT = None
Simon Glass57454f42016-11-25 20:15:52 -0700807
808 def testHelp(self):
809 """Test that the basic help is displayed with -h"""
810 result = self._RunBinman('-h')
811 self.assertTrue(len(result.stdout) > 200)
812 self.assertEqual(0, len(result.stderr))
813 self.assertEqual(0, result.return_code)
814
Simon Glass57454f42016-11-25 20:15:52 -0700815 def testBoard(self):
816 """Test that we can run it with a specific board"""
Simon Glass511f6582018-10-01 12:22:30 -0600817 self._SetupDtb('005_simple.dts', 'sandbox/u-boot.dtb')
Simon Glass57454f42016-11-25 20:15:52 -0700818 TestFunctional._MakeInputFile('sandbox/u-boot.bin', U_BOOT_DATA)
Simon Glassed930672021-03-18 20:25:05 +1300819 result = self._DoBinman('build', '-n', '-b', 'sandbox')
Simon Glass57454f42016-11-25 20:15:52 -0700820 self.assertEqual(0, result)
821
822 def testNeedBoard(self):
823 """Test that we get an error when no board ius supplied"""
824 with self.assertRaises(ValueError) as e:
Simon Glassf46732a2019-07-08 14:25:29 -0600825 result = self._DoBinman('build')
Simon Glass57454f42016-11-25 20:15:52 -0700826 self.assertIn("Must provide a board to process (use -b <board>)",
827 str(e.exception))
828
829 def testMissingDt(self):
Simon Glass1e324002018-06-01 09:38:19 -0600830 """Test that an invalid device-tree file generates an error"""
Simon Glass57454f42016-11-25 20:15:52 -0700831 with self.assertRaises(Exception) as e:
Simon Glassf46732a2019-07-08 14:25:29 -0600832 self._RunBinman('build', '-d', 'missing_file')
Simon Glass57454f42016-11-25 20:15:52 -0700833 # We get one error from libfdt, and a different one from fdtget.
834 self.AssertInList(["Couldn't open blob from 'missing_file'",
835 'No such file or directory'], str(e.exception))
836
837 def testBrokenDt(self):
Simon Glass1e324002018-06-01 09:38:19 -0600838 """Test that an invalid device-tree source file generates an error
Simon Glass57454f42016-11-25 20:15:52 -0700839
840 Since this is a source file it should be compiled and the error
841 will come from the device-tree compiler (dtc).
842 """
843 with self.assertRaises(Exception) as e:
Simon Glassf46732a2019-07-08 14:25:29 -0600844 self._RunBinman('build', '-d', self.TestFile('001_invalid.dts'))
Simon Glass57454f42016-11-25 20:15:52 -0700845 self.assertIn("FATAL ERROR: Unable to parse input tree",
846 str(e.exception))
847
848 def testMissingNode(self):
849 """Test that a device tree without a 'binman' node generates an error"""
850 with self.assertRaises(Exception) as e:
Simon Glassf46732a2019-07-08 14:25:29 -0600851 self._DoBinman('build', '-d', self.TestFile('002_missing_node.dts'))
Simon Glass57454f42016-11-25 20:15:52 -0700852 self.assertIn("does not have a 'binman' node", str(e.exception))
853
854 def testEmpty(self):
855 """Test that an empty binman node works OK (i.e. does nothing)"""
Simon Glassf46732a2019-07-08 14:25:29 -0600856 result = self._RunBinman('build', '-d', self.TestFile('003_empty.dts'))
Simon Glass57454f42016-11-25 20:15:52 -0700857 self.assertEqual(0, len(result.stderr))
858 self.assertEqual(0, result.return_code)
859
860 def testInvalidEntry(self):
861 """Test that an invalid entry is flagged"""
862 with self.assertRaises(Exception) as e:
Simon Glassf46732a2019-07-08 14:25:29 -0600863 result = self._RunBinman('build', '-d',
Simon Glass511f6582018-10-01 12:22:30 -0600864 self.TestFile('004_invalid_entry.dts'))
Simon Glass57454f42016-11-25 20:15:52 -0700865 self.assertIn("Unknown entry type 'not-a-valid-type' in node "
866 "'/binman/not-a-valid-type'", str(e.exception))
867
868 def testSimple(self):
869 """Test a simple binman with a single file"""
Simon Glass511f6582018-10-01 12:22:30 -0600870 data = self._DoReadFile('005_simple.dts')
Simon Glass57454f42016-11-25 20:15:52 -0700871 self.assertEqual(U_BOOT_DATA, data)
872
Simon Glass075a45c2017-11-13 18:55:00 -0700873 def testSimpleDebug(self):
874 """Test a simple binman run with debugging enabled"""
Simon Glass52d06212019-07-08 14:25:53 -0600875 self._DoTestFile('005_simple.dts', debug=True)
Simon Glass075a45c2017-11-13 18:55:00 -0700876
Simon Glass57454f42016-11-25 20:15:52 -0700877 def testDual(self):
878 """Test that we can handle creating two images
879
880 This also tests image padding.
881 """
Simon Glass511f6582018-10-01 12:22:30 -0600882 retcode = self._DoTestFile('006_dual_image.dts')
Simon Glass57454f42016-11-25 20:15:52 -0700883 self.assertEqual(0, retcode)
884
885 image = control.images['image1']
Simon Glass39dd2152019-07-08 14:25:47 -0600886 self.assertEqual(len(U_BOOT_DATA), image.size)
Simon Glass80025522022-01-29 14:14:04 -0700887 fname = tools.get_output_filename('image1.bin')
Simon Glass57454f42016-11-25 20:15:52 -0700888 self.assertTrue(os.path.exists(fname))
Simon Glass33486662019-05-14 15:53:42 -0600889 with open(fname, 'rb') as fd:
Simon Glass57454f42016-11-25 20:15:52 -0700890 data = fd.read()
891 self.assertEqual(U_BOOT_DATA, data)
892
893 image = control.images['image2']
Simon Glass39dd2152019-07-08 14:25:47 -0600894 self.assertEqual(3 + len(U_BOOT_DATA) + 5, image.size)
Simon Glass80025522022-01-29 14:14:04 -0700895 fname = tools.get_output_filename('image2.bin')
Simon Glass57454f42016-11-25 20:15:52 -0700896 self.assertTrue(os.path.exists(fname))
Simon Glass33486662019-05-14 15:53:42 -0600897 with open(fname, 'rb') as fd:
Simon Glass57454f42016-11-25 20:15:52 -0700898 data = fd.read()
899 self.assertEqual(U_BOOT_DATA, data[3:7])
Simon Glass80025522022-01-29 14:14:04 -0700900 self.assertEqual(tools.get_bytes(0, 3), data[:3])
901 self.assertEqual(tools.get_bytes(0, 5), data[7:])
Simon Glass57454f42016-11-25 20:15:52 -0700902
903 def testBadAlign(self):
904 """Test that an invalid alignment value is detected"""
905 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -0600906 self._DoTestFile('007_bad_align.dts')
Simon Glass57454f42016-11-25 20:15:52 -0700907 self.assertIn("Node '/binman/u-boot': Alignment 23 must be a power "
908 "of two", str(e.exception))
909
910 def testPackSimple(self):
911 """Test that packing works as expected"""
Simon Glass511f6582018-10-01 12:22:30 -0600912 retcode = self._DoTestFile('008_pack.dts')
Simon Glass57454f42016-11-25 20:15:52 -0700913 self.assertEqual(0, retcode)
914 self.assertIn('image', control.images)
915 image = control.images['image']
Simon Glasseca32212018-06-01 09:38:12 -0600916 entries = image.GetEntries()
Simon Glass57454f42016-11-25 20:15:52 -0700917 self.assertEqual(5, len(entries))
918
919 # First u-boot
920 self.assertIn('u-boot', entries)
921 entry = entries['u-boot']
Simon Glasse8561af2018-08-01 15:22:37 -0600922 self.assertEqual(0, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700923 self.assertEqual(len(U_BOOT_DATA), entry.size)
924
925 # Second u-boot, aligned to 16-byte boundary
926 self.assertIn('u-boot-align', entries)
927 entry = entries['u-boot-align']
Simon Glasse8561af2018-08-01 15:22:37 -0600928 self.assertEqual(16, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700929 self.assertEqual(len(U_BOOT_DATA), entry.size)
930
931 # Third u-boot, size 23 bytes
932 self.assertIn('u-boot-size', entries)
933 entry = entries['u-boot-size']
Simon Glasse8561af2018-08-01 15:22:37 -0600934 self.assertEqual(20, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700935 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
936 self.assertEqual(23, entry.size)
937
938 # Fourth u-boot, placed immediate after the above
939 self.assertIn('u-boot-next', entries)
940 entry = entries['u-boot-next']
Simon Glasse8561af2018-08-01 15:22:37 -0600941 self.assertEqual(43, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700942 self.assertEqual(len(U_BOOT_DATA), entry.size)
943
Simon Glasse8561af2018-08-01 15:22:37 -0600944 # Fifth u-boot, placed at a fixed offset
Simon Glass57454f42016-11-25 20:15:52 -0700945 self.assertIn('u-boot-fixed', entries)
946 entry = entries['u-boot-fixed']
Simon Glasse8561af2018-08-01 15:22:37 -0600947 self.assertEqual(61, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700948 self.assertEqual(len(U_BOOT_DATA), entry.size)
949
Simon Glass39dd2152019-07-08 14:25:47 -0600950 self.assertEqual(65, image.size)
Simon Glass57454f42016-11-25 20:15:52 -0700951
952 def testPackExtra(self):
953 """Test that extra packing feature works as expected"""
Simon Glassafb9caa2020-10-26 17:40:10 -0600954 data, _, _, out_dtb_fname = self._DoReadFileDtb('009_pack_extra.dts',
955 update_dtb=True)
Simon Glass57454f42016-11-25 20:15:52 -0700956
Simon Glass57454f42016-11-25 20:15:52 -0700957 self.assertIn('image', control.images)
958 image = control.images['image']
Simon Glasseca32212018-06-01 09:38:12 -0600959 entries = image.GetEntries()
Samuel Hollande2574022023-01-21 17:25:16 -0600960 self.assertEqual(6, len(entries))
Simon Glass57454f42016-11-25 20:15:52 -0700961
Samuel Hollande2574022023-01-21 17:25:16 -0600962 # First u-boot with padding before and after (included in minimum size)
Simon Glass57454f42016-11-25 20:15:52 -0700963 self.assertIn('u-boot', entries)
964 entry = entries['u-boot']
Simon Glasse8561af2018-08-01 15:22:37 -0600965 self.assertEqual(0, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700966 self.assertEqual(3, entry.pad_before)
967 self.assertEqual(3 + 5 + len(U_BOOT_DATA), entry.size)
Simon Glass187202f2020-10-26 17:40:08 -0600968 self.assertEqual(U_BOOT_DATA, entry.data)
Simon Glass80025522022-01-29 14:14:04 -0700969 self.assertEqual(tools.get_bytes(0, 3) + U_BOOT_DATA +
970 tools.get_bytes(0, 5), data[:entry.size])
Simon Glass187202f2020-10-26 17:40:08 -0600971 pos = entry.size
Simon Glass57454f42016-11-25 20:15:52 -0700972
973 # Second u-boot has an aligned size, but it has no effect
974 self.assertIn('u-boot-align-size-nop', entries)
975 entry = entries['u-boot-align-size-nop']
Simon Glass187202f2020-10-26 17:40:08 -0600976 self.assertEqual(pos, entry.offset)
977 self.assertEqual(len(U_BOOT_DATA), entry.size)
978 self.assertEqual(U_BOOT_DATA, entry.data)
979 self.assertEqual(U_BOOT_DATA, data[pos:pos + entry.size])
980 pos += entry.size
Simon Glass57454f42016-11-25 20:15:52 -0700981
982 # Third u-boot has an aligned size too
983 self.assertIn('u-boot-align-size', entries)
984 entry = entries['u-boot-align-size']
Simon Glass187202f2020-10-26 17:40:08 -0600985 self.assertEqual(pos, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700986 self.assertEqual(32, entry.size)
Simon Glass187202f2020-10-26 17:40:08 -0600987 self.assertEqual(U_BOOT_DATA, entry.data)
Simon Glass80025522022-01-29 14:14:04 -0700988 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 32 - len(U_BOOT_DATA)),
Simon Glass187202f2020-10-26 17:40:08 -0600989 data[pos:pos + entry.size])
990 pos += entry.size
Simon Glass57454f42016-11-25 20:15:52 -0700991
992 # Fourth u-boot has an aligned end
993 self.assertIn('u-boot-align-end', entries)
994 entry = entries['u-boot-align-end']
Simon Glasse8561af2018-08-01 15:22:37 -0600995 self.assertEqual(48, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700996 self.assertEqual(16, entry.size)
Simon Glass187202f2020-10-26 17:40:08 -0600997 self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)])
Simon Glass80025522022-01-29 14:14:04 -0700998 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 16 - len(U_BOOT_DATA)),
Simon Glass187202f2020-10-26 17:40:08 -0600999 data[pos:pos + entry.size])
1000 pos += entry.size
Simon Glass57454f42016-11-25 20:15:52 -07001001
1002 # Fifth u-boot immediately afterwards
1003 self.assertIn('u-boot-align-both', entries)
1004 entry = entries['u-boot-align-both']
Simon Glasse8561af2018-08-01 15:22:37 -06001005 self.assertEqual(64, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -07001006 self.assertEqual(64, entry.size)
Simon Glass187202f2020-10-26 17:40:08 -06001007 self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)])
Simon Glass80025522022-01-29 14:14:04 -07001008 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 64 - len(U_BOOT_DATA)),
Simon Glass187202f2020-10-26 17:40:08 -06001009 data[pos:pos + entry.size])
Simon Glass57454f42016-11-25 20:15:52 -07001010
Samuel Hollande2574022023-01-21 17:25:16 -06001011 # Sixth u-boot with both minimum size and aligned size
1012 self.assertIn('u-boot-min-size', entries)
1013 entry = entries['u-boot-min-size']
1014 self.assertEqual(128, entry.offset)
1015 self.assertEqual(32, entry.size)
1016 self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)])
1017 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 32 - len(U_BOOT_DATA)),
1018 data[pos:pos + entry.size])
1019
Simon Glass57454f42016-11-25 20:15:52 -07001020 self.CheckNoGaps(entries)
Samuel Hollande2574022023-01-21 17:25:16 -06001021 self.assertEqual(160, image.size)
Simon Glass57454f42016-11-25 20:15:52 -07001022
Simon Glassafb9caa2020-10-26 17:40:10 -06001023 dtb = fdt.Fdt(out_dtb_fname)
1024 dtb.Scan()
1025 props = self._GetPropTree(dtb, ['size', 'offset', 'image-pos'])
1026 expected = {
1027 'image-pos': 0,
1028 'offset': 0,
Samuel Hollande2574022023-01-21 17:25:16 -06001029 'size': 160,
Simon Glassafb9caa2020-10-26 17:40:10 -06001030
1031 'u-boot:image-pos': 0,
1032 'u-boot:offset': 0,
1033 'u-boot:size': 3 + 5 + len(U_BOOT_DATA),
1034
1035 'u-boot-align-size-nop:image-pos': 12,
1036 'u-boot-align-size-nop:offset': 12,
1037 'u-boot-align-size-nop:size': 4,
1038
1039 'u-boot-align-size:image-pos': 16,
1040 'u-boot-align-size:offset': 16,
1041 'u-boot-align-size:size': 32,
1042
1043 'u-boot-align-end:image-pos': 48,
1044 'u-boot-align-end:offset': 48,
1045 'u-boot-align-end:size': 16,
1046
1047 'u-boot-align-both:image-pos': 64,
1048 'u-boot-align-both:offset': 64,
1049 'u-boot-align-both:size': 64,
Samuel Hollande2574022023-01-21 17:25:16 -06001050
1051 'u-boot-min-size:image-pos': 128,
1052 'u-boot-min-size:offset': 128,
1053 'u-boot-min-size:size': 32,
Simon Glassafb9caa2020-10-26 17:40:10 -06001054 }
1055 self.assertEqual(expected, props)
1056
Simon Glass57454f42016-11-25 20:15:52 -07001057 def testPackAlignPowerOf2(self):
1058 """Test that invalid entry alignment is detected"""
1059 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001060 self._DoTestFile('010_pack_align_power2.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001061 self.assertIn("Node '/binman/u-boot': Alignment 5 must be a power "
1062 "of two", str(e.exception))
1063
1064 def testPackAlignSizePowerOf2(self):
1065 """Test that invalid entry size alignment is detected"""
1066 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001067 self._DoTestFile('011_pack_align_size_power2.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001068 self.assertIn("Node '/binman/u-boot': Alignment size 55 must be a "
1069 "power of two", str(e.exception))
1070
1071 def testPackInvalidAlign(self):
Simon Glasse8561af2018-08-01 15:22:37 -06001072 """Test detection of an offset that does not match its alignment"""
Simon Glass57454f42016-11-25 20:15:52 -07001073 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001074 self._DoTestFile('012_pack_inv_align.dts')
Simon Glasse8561af2018-08-01 15:22:37 -06001075 self.assertIn("Node '/binman/u-boot': Offset 0x5 (5) does not match "
Simon Glass57454f42016-11-25 20:15:52 -07001076 "align 0x4 (4)", str(e.exception))
1077
1078 def testPackInvalidSizeAlign(self):
1079 """Test that invalid entry size alignment is detected"""
1080 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001081 self._DoTestFile('013_pack_inv_size_align.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001082 self.assertIn("Node '/binman/u-boot': Size 0x5 (5) does not match "
1083 "align-size 0x4 (4)", str(e.exception))
1084
1085 def testPackOverlap(self):
1086 """Test that overlapping regions are detected"""
1087 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001088 self._DoTestFile('014_pack_overlap.dts')
Simon Glasse8561af2018-08-01 15:22:37 -06001089 self.assertIn("Node '/binman/u-boot-align': Offset 0x3 (3) overlaps "
Simon Glass57454f42016-11-25 20:15:52 -07001090 "with previous entry '/binman/u-boot' ending at 0x4 (4)",
1091 str(e.exception))
1092
1093 def testPackEntryOverflow(self):
1094 """Test that entries that overflow their size are detected"""
1095 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001096 self._DoTestFile('015_pack_overflow.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001097 self.assertIn("Node '/binman/u-boot': Entry contents size is 0x4 (4) "
1098 "but entry size is 0x3 (3)", str(e.exception))
1099
1100 def testPackImageOverflow(self):
1101 """Test that entries which overflow the image size are detected"""
1102 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001103 self._DoTestFile('016_pack_image_overflow.dts')
Simon Glasseca32212018-06-01 09:38:12 -06001104 self.assertIn("Section '/binman': contents size 0x4 (4) exceeds section "
Simon Glass57454f42016-11-25 20:15:52 -07001105 "size 0x3 (3)", str(e.exception))
1106
1107 def testPackImageSize(self):
1108 """Test that the image size can be set"""
Simon Glass511f6582018-10-01 12:22:30 -06001109 retcode = self._DoTestFile('017_pack_image_size.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001110 self.assertEqual(0, retcode)
1111 self.assertIn('image', control.images)
1112 image = control.images['image']
Simon Glass39dd2152019-07-08 14:25:47 -06001113 self.assertEqual(7, image.size)
Simon Glass57454f42016-11-25 20:15:52 -07001114
1115 def testPackImageSizeAlign(self):
1116 """Test that image size alignemnt works as expected"""
Simon Glass511f6582018-10-01 12:22:30 -06001117 retcode = self._DoTestFile('018_pack_image_align.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001118 self.assertEqual(0, retcode)
1119 self.assertIn('image', control.images)
1120 image = control.images['image']
Simon Glass39dd2152019-07-08 14:25:47 -06001121 self.assertEqual(16, image.size)
Simon Glass57454f42016-11-25 20:15:52 -07001122
1123 def testPackInvalidImageAlign(self):
1124 """Test that invalid image alignment is detected"""
1125 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001126 self._DoTestFile('019_pack_inv_image_align.dts')
Simon Glasseca32212018-06-01 09:38:12 -06001127 self.assertIn("Section '/binman': Size 0x7 (7) does not match "
Simon Glass57454f42016-11-25 20:15:52 -07001128 "align-size 0x8 (8)", str(e.exception))
1129
Simon Glass2a0fa982022-02-11 13:23:21 -07001130 def testPackAlignPowerOf2Inv(self):
Simon Glass57454f42016-11-25 20:15:52 -07001131 """Test that invalid image alignment is detected"""
1132 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001133 self._DoTestFile('020_pack_inv_image_align_power2.dts')
Simon Glass39dd2152019-07-08 14:25:47 -06001134 self.assertIn("Image '/binman': Alignment size 131 must be a power of "
Simon Glass57454f42016-11-25 20:15:52 -07001135 "two", str(e.exception))
1136
1137 def testImagePadByte(self):
1138 """Test that the image pad byte can be specified"""
Simon Glass7057d022018-10-01 21:12:47 -06001139 self._SetupSplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001140 data = self._DoReadFile('021_image_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07001141 self.assertEqual(U_BOOT_SPL_DATA + tools.get_bytes(0xff, 1) +
Simon Glassac0d4952019-05-14 15:53:47 -06001142 U_BOOT_DATA, data)
Simon Glass57454f42016-11-25 20:15:52 -07001143
1144 def testImageName(self):
1145 """Test that image files can be named"""
Simon Glass511f6582018-10-01 12:22:30 -06001146 retcode = self._DoTestFile('022_image_name.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001147 self.assertEqual(0, retcode)
1148 image = control.images['image1']
Simon Glass80025522022-01-29 14:14:04 -07001149 fname = tools.get_output_filename('test-name')
Simon Glass57454f42016-11-25 20:15:52 -07001150 self.assertTrue(os.path.exists(fname))
1151
1152 image = control.images['image2']
Simon Glass80025522022-01-29 14:14:04 -07001153 fname = tools.get_output_filename('test-name.xx')
Simon Glass57454f42016-11-25 20:15:52 -07001154 self.assertTrue(os.path.exists(fname))
1155
1156 def testBlobFilename(self):
1157 """Test that generic blobs can be provided by filename"""
Simon Glass511f6582018-10-01 12:22:30 -06001158 data = self._DoReadFile('023_blob.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001159 self.assertEqual(BLOB_DATA, data)
1160
1161 def testPackSorted(self):
1162 """Test that entries can be sorted"""
Simon Glass7057d022018-10-01 21:12:47 -06001163 self._SetupSplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001164 data = self._DoReadFile('024_sorted.dts')
Simon Glass80025522022-01-29 14:14:04 -07001165 self.assertEqual(tools.get_bytes(0, 1) + U_BOOT_SPL_DATA +
1166 tools.get_bytes(0, 2) + U_BOOT_DATA, data)
Simon Glass57454f42016-11-25 20:15:52 -07001167
Simon Glasse8561af2018-08-01 15:22:37 -06001168 def testPackZeroOffset(self):
1169 """Test that an entry at offset 0 is not given a new offset"""
Marek Vasutf7413f02023-07-18 07:23:58 -06001170 self._SetupSplElf()
Simon Glass57454f42016-11-25 20:15:52 -07001171 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001172 self._DoTestFile('025_pack_zero_size.dts')
Simon Glasse8561af2018-08-01 15:22:37 -06001173 self.assertIn("Node '/binman/u-boot-spl': Offset 0x0 (0) overlaps "
Simon Glass57454f42016-11-25 20:15:52 -07001174 "with previous entry '/binman/u-boot' ending at 0x4 (4)",
1175 str(e.exception))
1176
1177 def testPackUbootDtb(self):
1178 """Test that a device tree can be added to U-Boot"""
Simon Glass511f6582018-10-01 12:22:30 -06001179 data = self._DoReadFile('026_pack_u_boot_dtb.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001180 self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA, data)
Simon Glass72232452016-11-25 20:15:53 -07001181
1182 def testPackX86RomNoSize(self):
1183 """Test that the end-at-4gb property requires a size property"""
Marek Vasutf7413f02023-07-18 07:23:58 -06001184 self._SetupSplElf()
Simon Glass72232452016-11-25 20:15:53 -07001185 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001186 self._DoTestFile('027_pack_4gb_no_size.dts')
Simon Glass39dd2152019-07-08 14:25:47 -06001187 self.assertIn("Image '/binman': Section size must be provided when "
Simon Glass72232452016-11-25 20:15:53 -07001188 "using end-at-4gb", str(e.exception))
1189
Jagdish Gediya0fb978c2018-09-03 21:35:07 +05301190 def test4gbAndSkipAtStartTogether(self):
1191 """Test that the end-at-4gb and skip-at-size property can't be used
1192 together"""
Marek Vasutf7413f02023-07-18 07:23:58 -06001193 self._SetupSplElf()
Jagdish Gediya0fb978c2018-09-03 21:35:07 +05301194 with self.assertRaises(ValueError) as e:
Simon Glass11f2bd02019-08-24 07:23:02 -06001195 self._DoTestFile('098_4gb_and_skip_at_start_together.dts')
Simon Glass39dd2152019-07-08 14:25:47 -06001196 self.assertIn("Image '/binman': Provide either 'end-at-4gb' or "
Jagdish Gediya0fb978c2018-09-03 21:35:07 +05301197 "'skip-at-start'", str(e.exception))
1198
Simon Glass72232452016-11-25 20:15:53 -07001199 def testPackX86RomOutside(self):
Simon Glasse8561af2018-08-01 15:22:37 -06001200 """Test that the end-at-4gb property checks for offset boundaries"""
Marek Vasutf7413f02023-07-18 07:23:58 -06001201 self._SetupSplElf()
Simon Glass72232452016-11-25 20:15:53 -07001202 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001203 self._DoTestFile('028_pack_4gb_outside.dts')
Simon Glassd6179862020-10-26 17:40:05 -06001204 self.assertIn("Node '/binman/u-boot': Offset 0x0 (0) size 0x4 (4) "
1205 "is outside the section '/binman' starting at "
1206 '0xffffffe0 (4294967264) of size 0x20 (32)',
Simon Glass72232452016-11-25 20:15:53 -07001207 str(e.exception))
1208
1209 def testPackX86Rom(self):
1210 """Test that a basic x86 ROM can be created"""
Simon Glass7057d022018-10-01 21:12:47 -06001211 self._SetupSplElf()
Simon Glass1d167762019-08-24 07:23:01 -06001212 data = self._DoReadFile('029_x86_rom.dts')
Simon Glass80025522022-01-29 14:14:04 -07001213 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 3) + U_BOOT_SPL_DATA +
1214 tools.get_bytes(0, 2), data)
Simon Glass72232452016-11-25 20:15:53 -07001215
1216 def testPackX86RomMeNoDesc(self):
1217 """Test that an invalid Intel descriptor entry is detected"""
Simon Glasse88cef92020-07-09 18:39:41 -06001218 try:
Simon Glass14c596c2020-07-25 15:11:19 -06001219 TestFunctional._MakeInputFile('descriptor-empty.bin', b'')
Simon Glasse88cef92020-07-09 18:39:41 -06001220 with self.assertRaises(ValueError) as e:
Simon Glass14c596c2020-07-25 15:11:19 -06001221 self._DoTestFile('163_x86_rom_me_empty.dts')
Simon Glasse88cef92020-07-09 18:39:41 -06001222 self.assertIn("Node '/binman/intel-descriptor': Cannot find Intel Flash Descriptor (FD) signature",
1223 str(e.exception))
1224 finally:
1225 self._SetupDescriptor()
Simon Glass72232452016-11-25 20:15:53 -07001226
1227 def testPackX86RomBadDesc(self):
1228 """Test that the Intel requires a descriptor entry"""
1229 with self.assertRaises(ValueError) as e:
Simon Glass1d167762019-08-24 07:23:01 -06001230 self._DoTestFile('030_x86_rom_me_no_desc.dts')
Simon Glasse8561af2018-08-01 15:22:37 -06001231 self.assertIn("Node '/binman/intel-me': No offset set with "
1232 "offset-unset: should another entry provide this correct "
1233 "offset?", str(e.exception))
Simon Glass72232452016-11-25 20:15:53 -07001234
1235 def testPackX86RomMe(self):
1236 """Test that an x86 ROM with an ME region can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001237 data = self._DoReadFile('031_x86_rom_me.dts')
Simon Glass80025522022-01-29 14:14:04 -07001238 expected_desc = tools.read_file(self.TestFile('descriptor.bin'))
Simon Glass759af872019-07-08 13:18:54 -06001239 if data[:0x1000] != expected_desc:
1240 self.fail('Expected descriptor binary at start of image')
Simon Glass72232452016-11-25 20:15:53 -07001241 self.assertEqual(ME_DATA, data[0x1000:0x1000 + len(ME_DATA)])
1242
1243 def testPackVga(self):
1244 """Test that an image with a VGA binary can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001245 data = self._DoReadFile('032_intel_vga.dts')
Simon Glass72232452016-11-25 20:15:53 -07001246 self.assertEqual(VGA_DATA, data[:len(VGA_DATA)])
1247
1248 def testPackStart16(self):
1249 """Test that an image with an x86 start16 region can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001250 data = self._DoReadFile('033_x86_start16.dts')
Simon Glass72232452016-11-25 20:15:53 -07001251 self.assertEqual(X86_START16_DATA, data[:len(X86_START16_DATA)])
1252
Jagdish Gediya311d4842018-09-03 21:35:08 +05301253 def testPackPowerpcMpc85xxBootpgResetvec(self):
1254 """Test that an image with powerpc-mpc85xx-bootpg-resetvec can be
1255 created"""
Simon Glass11f2bd02019-08-24 07:23:02 -06001256 data = self._DoReadFile('150_powerpc_mpc85xx_bootpg_resetvec.dts')
Jagdish Gediya311d4842018-09-03 21:35:08 +05301257 self.assertEqual(PPC_MPC85XX_BR_DATA, data[:len(PPC_MPC85XX_BR_DATA)])
1258
Simon Glass6ba679c2018-07-06 10:27:17 -06001259 def _RunMicrocodeTest(self, dts_fname, nodtb_data, ucode_second=False):
Simon Glass820af1d2018-07-06 10:27:16 -06001260 """Handle running a test for insertion of microcode
1261
1262 Args:
1263 dts_fname: Name of test .dts file
1264 nodtb_data: Data that we expect in the first section
Simon Glass6ba679c2018-07-06 10:27:17 -06001265 ucode_second: True if the microsecond entry is second instead of
1266 third
Simon Glass820af1d2018-07-06 10:27:16 -06001267
1268 Returns:
1269 Tuple:
1270 Contents of first region (U-Boot or SPL)
Simon Glasse8561af2018-08-01 15:22:37 -06001271 Offset and size components of microcode pointer, as inserted
Simon Glass820af1d2018-07-06 10:27:16 -06001272 in the above (two 4-byte words)
1273 """
Simon Glass3d274232017-11-12 21:52:27 -07001274 data = self._DoReadFile(dts_fname, True)
Simon Glass72232452016-11-25 20:15:53 -07001275
1276 # Now check the device tree has no microcode
Simon Glass6ba679c2018-07-06 10:27:17 -06001277 if ucode_second:
1278 ucode_content = data[len(nodtb_data):]
1279 ucode_pos = len(nodtb_data)
1280 dtb_with_ucode = ucode_content[16:]
1281 fdt_len = self.GetFdtLen(dtb_with_ucode)
1282 else:
1283 dtb_with_ucode = data[len(nodtb_data):]
1284 fdt_len = self.GetFdtLen(dtb_with_ucode)
1285 ucode_content = dtb_with_ucode[fdt_len:]
1286 ucode_pos = len(nodtb_data) + fdt_len
Simon Glass80025522022-01-29 14:14:04 -07001287 fname = tools.get_output_filename('test.dtb')
Simon Glass72232452016-11-25 20:15:53 -07001288 with open(fname, 'wb') as fd:
Simon Glass820af1d2018-07-06 10:27:16 -06001289 fd.write(dtb_with_ucode)
Simon Glass22c92ca2017-05-27 07:38:29 -06001290 dtb = fdt.FdtScan(fname)
1291 ucode = dtb.GetNode('/microcode')
Simon Glass72232452016-11-25 20:15:53 -07001292 self.assertTrue(ucode)
1293 for node in ucode.subnodes:
1294 self.assertFalse(node.props.get('data'))
1295
Simon Glass72232452016-11-25 20:15:53 -07001296 # Check that the microcode appears immediately after the Fdt
1297 # This matches the concatenation of the data properties in
Simon Glasse83679d2017-11-12 21:52:26 -07001298 # the /microcode/update@xxx nodes in 34_x86_ucode.dts.
Simon Glass72232452016-11-25 20:15:53 -07001299 ucode_data = struct.pack('>4L', 0x12345678, 0x12345679, 0xabcd0000,
1300 0x78235609)
Simon Glass820af1d2018-07-06 10:27:16 -06001301 self.assertEqual(ucode_data, ucode_content[:len(ucode_data)])
Simon Glass72232452016-11-25 20:15:53 -07001302
1303 # Check that the microcode pointer was inserted. It should match the
Simon Glasse8561af2018-08-01 15:22:37 -06001304 # expected offset and size
Simon Glass72232452016-11-25 20:15:53 -07001305 pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
1306 len(ucode_data))
Simon Glass6ba679c2018-07-06 10:27:17 -06001307 u_boot = data[:len(nodtb_data)]
1308 return u_boot, pos_and_size
Simon Glass3d274232017-11-12 21:52:27 -07001309
1310 def testPackUbootMicrocode(self):
1311 """Test that x86 microcode can be handled correctly
1312
1313 We expect to see the following in the image, in order:
1314 u-boot-nodtb.bin with a microcode pointer inserted at the correct
1315 place
1316 u-boot.dtb with the microcode removed
1317 the microcode
1318 """
Simon Glass511f6582018-10-01 12:22:30 -06001319 first, pos_and_size = self._RunMicrocodeTest('034_x86_ucode.dts',
Simon Glass3d274232017-11-12 21:52:27 -07001320 U_BOOT_NODTB_DATA)
Simon Glass303f62f2019-05-17 22:00:46 -06001321 self.assertEqual(b'nodtb with microcode' + pos_and_size +
1322 b' somewhere in here', first)
Simon Glass72232452016-11-25 20:15:53 -07001323
Simon Glassbac25c82017-05-27 07:38:26 -06001324 def _RunPackUbootSingleMicrocode(self):
Simon Glass72232452016-11-25 20:15:53 -07001325 """Test that x86 microcode can be handled correctly
1326
1327 We expect to see the following in the image, in order:
1328 u-boot-nodtb.bin with a microcode pointer inserted at the correct
1329 place
1330 u-boot.dtb with the microcode
1331 an empty microcode region
1332 """
1333 # We need the libfdt library to run this test since only that allows
1334 # finding the offset of a property. This is required by
1335 # Entry_u_boot_dtb_with_ucode.ObtainContents().
Simon Glass511f6582018-10-01 12:22:30 -06001336 data = self._DoReadFile('035_x86_single_ucode.dts', True)
Simon Glass72232452016-11-25 20:15:53 -07001337
1338 second = data[len(U_BOOT_NODTB_DATA):]
1339
1340 fdt_len = self.GetFdtLen(second)
1341 third = second[fdt_len:]
1342 second = second[:fdt_len]
1343
Simon Glassbac25c82017-05-27 07:38:26 -06001344 ucode_data = struct.pack('>2L', 0x12345678, 0x12345679)
1345 self.assertIn(ucode_data, second)
1346 ucode_pos = second.find(ucode_data) + len(U_BOOT_NODTB_DATA)
Simon Glass72232452016-11-25 20:15:53 -07001347
Simon Glassbac25c82017-05-27 07:38:26 -06001348 # Check that the microcode pointer was inserted. It should match the
Simon Glasse8561af2018-08-01 15:22:37 -06001349 # expected offset and size
Simon Glassbac25c82017-05-27 07:38:26 -06001350 pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
1351 len(ucode_data))
1352 first = data[:len(U_BOOT_NODTB_DATA)]
Simon Glass303f62f2019-05-17 22:00:46 -06001353 self.assertEqual(b'nodtb with microcode' + pos_and_size +
1354 b' somewhere in here', first)
Simon Glass996021e2016-11-25 20:15:54 -07001355
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001356 def testPackUbootSingleMicrocode(self):
1357 """Test that x86 microcode can be handled correctly with fdt_normal.
1358 """
Simon Glassbac25c82017-05-27 07:38:26 -06001359 self._RunPackUbootSingleMicrocode()
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001360
Simon Glass996021e2016-11-25 20:15:54 -07001361 def testUBootImg(self):
1362 """Test that u-boot.img can be put in a file"""
Simon Glass511f6582018-10-01 12:22:30 -06001363 data = self._DoReadFile('036_u_boot_img.dts')
Simon Glass996021e2016-11-25 20:15:54 -07001364 self.assertEqual(U_BOOT_IMG_DATA, data)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001365
1366 def testNoMicrocode(self):
1367 """Test that a missing microcode region is detected"""
1368 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001369 self._DoReadFile('037_x86_no_ucode.dts', True)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001370 self.assertIn("Node '/binman/u-boot-dtb-with-ucode': No /microcode "
1371 "node found in ", str(e.exception))
1372
1373 def testMicrocodeWithoutNode(self):
1374 """Test that a missing u-boot-dtb-with-ucode node is detected"""
1375 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001376 self._DoReadFile('038_x86_ucode_missing_node.dts', True)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001377 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
1378 "microcode region u-boot-dtb-with-ucode", str(e.exception))
1379
1380 def testMicrocodeWithoutNode2(self):
1381 """Test that a missing u-boot-ucode node is detected"""
1382 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001383 self._DoReadFile('039_x86_ucode_missing_node2.dts', True)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001384 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
1385 "microcode region u-boot-ucode", str(e.exception))
1386
1387 def testMicrocodeWithoutPtrInElf(self):
1388 """Test that a U-Boot binary without the microcode symbol is detected"""
1389 # ELF file without a '_dt_ucode_base_size' symbol
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001390 try:
Simon Glassfaaaa162019-08-24 07:22:55 -06001391 TestFunctional._MakeInputFile('u-boot',
Simon Glass80025522022-01-29 14:14:04 -07001392 tools.read_file(self.ElfTestFile('u_boot_no_ucode_ptr')))
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001393
1394 with self.assertRaises(ValueError) as e:
Simon Glassbac25c82017-05-27 07:38:26 -06001395 self._RunPackUbootSingleMicrocode()
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001396 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot locate "
1397 "_dt_ucode_base_size symbol in u-boot", str(e.exception))
1398
1399 finally:
1400 # Put the original file back
Simon Glass4affd4b2019-08-24 07:22:54 -06001401 TestFunctional._MakeInputFile('u-boot',
Simon Glass80025522022-01-29 14:14:04 -07001402 tools.read_file(self.ElfTestFile('u_boot_ucode_ptr')))
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001403
1404 def testMicrocodeNotInImage(self):
1405 """Test that microcode must be placed within the image"""
1406 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001407 self._DoReadFile('040_x86_ucode_not_in_image.dts', True)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001408 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Microcode "
1409 "pointer _dt_ucode_base_size at fffffe14 is outside the "
Simon Glassad5a7712018-06-01 09:38:14 -06001410 "section ranging from 00000000 to 0000002e", str(e.exception))
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001411
1412 def testWithoutMicrocode(self):
1413 """Test that we can cope with an image without microcode (e.g. qemu)"""
Simon Glassfaaaa162019-08-24 07:22:55 -06001414 TestFunctional._MakeInputFile('u-boot',
Simon Glass80025522022-01-29 14:14:04 -07001415 tools.read_file(self.ElfTestFile('u_boot_no_ucode_ptr')))
Simon Glass511f6582018-10-01 12:22:30 -06001416 data, dtb, _, _ = self._DoReadFileDtb('044_x86_optional_ucode.dts', True)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001417
1418 # Now check the device tree has no microcode
1419 self.assertEqual(U_BOOT_NODTB_DATA, data[:len(U_BOOT_NODTB_DATA)])
1420 second = data[len(U_BOOT_NODTB_DATA):]
1421
1422 fdt_len = self.GetFdtLen(second)
1423 self.assertEqual(dtb, second[:fdt_len])
1424
1425 used_len = len(U_BOOT_NODTB_DATA) + fdt_len
1426 third = data[used_len:]
Simon Glass80025522022-01-29 14:14:04 -07001427 self.assertEqual(tools.get_bytes(0, 0x200 - used_len), third)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001428
1429 def testUnknownPosSize(self):
1430 """Test that microcode must be placed within the image"""
1431 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001432 self._DoReadFile('041_unknown_pos_size.dts', True)
Simon Glasse8561af2018-08-01 15:22:37 -06001433 self.assertIn("Section '/binman': Unable to set offset/size for unknown "
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001434 "entry 'invalid-entry'", str(e.exception))
Simon Glassb4176d42016-11-25 20:15:56 -07001435
1436 def testPackFsp(self):
1437 """Test that an image with a FSP binary can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001438 data = self._DoReadFile('042_intel_fsp.dts')
Simon Glassb4176d42016-11-25 20:15:56 -07001439 self.assertEqual(FSP_DATA, data[:len(FSP_DATA)])
1440
1441 def testPackCmc(self):
Bin Mengd7bcdf52017-08-15 22:41:54 -07001442 """Test that an image with a CMC binary can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001443 data = self._DoReadFile('043_intel_cmc.dts')
Simon Glassb4176d42016-11-25 20:15:56 -07001444 self.assertEqual(CMC_DATA, data[:len(CMC_DATA)])
Bin Mengd7bcdf52017-08-15 22:41:54 -07001445
1446 def testPackVbt(self):
1447 """Test that an image with a VBT binary can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001448 data = self._DoReadFile('046_intel_vbt.dts')
Bin Mengd7bcdf52017-08-15 22:41:54 -07001449 self.assertEqual(VBT_DATA, data[:len(VBT_DATA)])
Simon Glassac599912017-11-12 21:52:22 -07001450
Simon Glass7f94e832017-11-12 21:52:25 -07001451 def testSplBssPad(self):
1452 """Test that we can pad SPL's BSS with zeros"""
Simon Glass3d274232017-11-12 21:52:27 -07001453 # ELF file with a '__bss_size' symbol
Simon Glass7057d022018-10-01 21:12:47 -06001454 self._SetupSplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001455 data = self._DoReadFile('047_spl_bss_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07001456 self.assertEqual(U_BOOT_SPL_DATA + tools.get_bytes(0, 10) + U_BOOT_DATA,
Simon Glassac0d4952019-05-14 15:53:47 -06001457 data)
Simon Glass7f94e832017-11-12 21:52:25 -07001458
Simon Glass04cda032018-10-01 21:12:42 -06001459 def testSplBssPadMissing(self):
1460 """Test that a missing symbol is detected"""
Simon Glass7057d022018-10-01 21:12:47 -06001461 self._SetupSplElf('u_boot_ucode_ptr')
Simon Glass24ad3652017-11-13 18:54:54 -07001462 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001463 self._DoReadFile('047_spl_bss_pad.dts')
Simon Glass24ad3652017-11-13 18:54:54 -07001464 self.assertIn('Expected __bss_size symbol in spl/u-boot-spl',
1465 str(e.exception))
1466
Simon Glasse83679d2017-11-12 21:52:26 -07001467 def testPackStart16Spl(self):
Simon Glassed40e962018-09-14 04:57:10 -06001468 """Test that an image with an x86 start16 SPL region can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001469 data = self._DoReadFile('048_x86_start16_spl.dts')
Simon Glasse83679d2017-11-12 21:52:26 -07001470 self.assertEqual(X86_START16_SPL_DATA, data[:len(X86_START16_SPL_DATA)])
1471
Simon Glass6ba679c2018-07-06 10:27:17 -06001472 def _PackUbootSplMicrocode(self, dts, ucode_second=False):
1473 """Helper function for microcode tests
Simon Glass3d274232017-11-12 21:52:27 -07001474
1475 We expect to see the following in the image, in order:
1476 u-boot-spl-nodtb.bin with a microcode pointer inserted at the
1477 correct place
1478 u-boot.dtb with the microcode removed
1479 the microcode
Simon Glass6ba679c2018-07-06 10:27:17 -06001480
1481 Args:
1482 dts: Device tree file to use for test
1483 ucode_second: True if the microsecond entry is second instead of
1484 third
Simon Glass3d274232017-11-12 21:52:27 -07001485 """
Simon Glass7057d022018-10-01 21:12:47 -06001486 self._SetupSplElf('u_boot_ucode_ptr')
Simon Glass6ba679c2018-07-06 10:27:17 -06001487 first, pos_and_size = self._RunMicrocodeTest(dts, U_BOOT_SPL_NODTB_DATA,
1488 ucode_second=ucode_second)
Simon Glass303f62f2019-05-17 22:00:46 -06001489 self.assertEqual(b'splnodtb with microc' + pos_and_size +
1490 b'ter somewhere in here', first)
Simon Glass3d274232017-11-12 21:52:27 -07001491
Simon Glass6ba679c2018-07-06 10:27:17 -06001492 def testPackUbootSplMicrocode(self):
1493 """Test that x86 microcode can be handled correctly in SPL"""
Marek Vasutf7413f02023-07-18 07:23:58 -06001494 self._SetupSplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001495 self._PackUbootSplMicrocode('049_x86_ucode_spl.dts')
Simon Glass6ba679c2018-07-06 10:27:17 -06001496
1497 def testPackUbootSplMicrocodeReorder(self):
1498 """Test that order doesn't matter for microcode entries
1499
1500 This is the same as testPackUbootSplMicrocode but when we process the
1501 u-boot-ucode entry we have not yet seen the u-boot-dtb-with-ucode
1502 entry, so we reply on binman to try later.
1503 """
Simon Glass511f6582018-10-01 12:22:30 -06001504 self._PackUbootSplMicrocode('058_x86_ucode_spl_needs_retry.dts',
Simon Glass6ba679c2018-07-06 10:27:17 -06001505 ucode_second=True)
1506
Simon Glassa409c932017-11-12 21:52:28 -07001507 def testPackMrc(self):
1508 """Test that an image with an MRC binary can be created"""
Simon Glass511f6582018-10-01 12:22:30 -06001509 data = self._DoReadFile('050_intel_mrc.dts')
Simon Glassa409c932017-11-12 21:52:28 -07001510 self.assertEqual(MRC_DATA, data[:len(MRC_DATA)])
1511
Simon Glass9aa6a6f2017-11-13 18:54:55 -07001512 def testSplDtb(self):
1513 """Test that an image with spl/u-boot-spl.dtb can be created"""
Marek Vasutf7413f02023-07-18 07:23:58 -06001514 self._SetupSplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001515 data = self._DoReadFile('051_u_boot_spl_dtb.dts')
Simon Glass9aa6a6f2017-11-13 18:54:55 -07001516 self.assertEqual(U_BOOT_SPL_DTB_DATA, data[:len(U_BOOT_SPL_DTB_DATA)])
1517
Simon Glass0a6da312017-11-13 18:54:56 -07001518 def testSplNoDtb(self):
1519 """Test that an image with spl/u-boot-spl-nodtb.bin can be created"""
Simon Glass13089cc2021-04-25 08:39:32 +12001520 self._SetupSplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001521 data = self._DoReadFile('052_u_boot_spl_nodtb.dts')
Simon Glass0a6da312017-11-13 18:54:56 -07001522 self.assertEqual(U_BOOT_SPL_NODTB_DATA, data[:len(U_BOOT_SPL_NODTB_DATA)])
1523
Simon Glass7098b7f2021-03-21 18:24:30 +13001524 def checkSymbols(self, dts, base_data, u_boot_offset, entry_args=None,
Simon Glass4b0f4142024-08-26 13:11:40 -06001525 use_expanded=False, no_write_symbols=False,
1526 symbols_base=None):
Simon Glass31e04cb2021-03-18 20:24:56 +13001527 """Check the image contains the expected symbol values
1528
1529 Args:
1530 dts: Device tree file to use for test
1531 base_data: Data before and after 'u-boot' section
Simon Glass3eb30a42024-08-26 13:11:42 -06001532 u_boot_offset (int): Offset of 'u-boot' section in image, or None if
1533 the offset not available due to it being in a compressed section
Simon Glass7098b7f2021-03-21 18:24:30 +13001534 entry_args: Dict of entry args to supply to binman
1535 key: arg name
1536 value: value of that arg
1537 use_expanded: True to use expanded entries where available, e.g.
1538 'u-boot-expanded' instead of 'u-boot'
Simon Glass4b0f4142024-08-26 13:11:40 -06001539 symbols_base (int): Value to expect for symbols-base in u-boot-spl,
1540 None if none
Simon Glass31e04cb2021-03-18 20:24:56 +13001541 """
Simon Glass5d0c0262019-08-24 07:22:56 -06001542 elf_fname = self.ElfTestFile('u_boot_binman_syms')
Simon Glass4ca8e042017-11-13 18:55:01 -07001543 syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
1544 addr = elf.GetSymbolAddress(elf_fname, '__image_copy_start')
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03001545 self.assertEqual(syms['_binman_sym_magic'].address, addr)
Simon Glass31e04cb2021-03-18 20:24:56 +13001546 self.assertEqual(syms['_binman_u_boot_spl_any_prop_offset'].address,
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03001547 addr + 4)
Simon Glass4ca8e042017-11-13 18:55:01 -07001548
Simon Glass7057d022018-10-01 21:12:47 -06001549 self._SetupSplElf('u_boot_binman_syms')
Simon Glass7098b7f2021-03-21 18:24:30 +13001550 data = self._DoReadFileDtb(dts, entry_args=entry_args,
Simon Glass3eb30a42024-08-26 13:11:42 -06001551 use_expanded=use_expanded,
1552 verbosity=None if u_boot_offset else 3)[0]
1553
1554 # The lz4-compressed version of the U-Boot data is 19 bytes long
1555 comp_uboot_len = 19
1556
Simon Glass31e04cb2021-03-18 20:24:56 +13001557 # The image should contain the symbols from u_boot_binman_syms.c
1558 # Note that image_pos is adjusted by the base address of the image,
1559 # which is 0x10 in our test image
Simon Glass3eb30a42024-08-26 13:11:42 -06001560 # If u_boot_offset is None, Binman should write -1U into the image
Simon Glass4b0f4142024-08-26 13:11:40 -06001561 vals2 = (elf.BINMAN_SYM_MAGIC_VALUE, 0x00,
Simon Glass3eb30a42024-08-26 13:11:42 -06001562 u_boot_offset + len(U_BOOT_DATA) if u_boot_offset else
1563 len(U_BOOT_SPL_DATA) + 1 + comp_uboot_len,
1564 0x10 + u_boot_offset if u_boot_offset else 0xffffffff, 0x04)
Simon Glass4b0f4142024-08-26 13:11:40 -06001565
1566 # u-boot-spl has a symbols-base property, so take that into account if
1567 # required. The caller must supply the value
1568 vals = list(vals2)
1569 if symbols_base is not None:
1570 vals[3] = symbols_base + u_boot_offset
1571 vals = tuple(vals)
1572
Simon Glass4b4049e2024-08-26 13:11:39 -06001573 sym_values = struct.pack('<LLQLL', *vals)
Simon Glass4b0f4142024-08-26 13:11:40 -06001574 sym_values2 = struct.pack('<LLQLL', *vals2)
Simon Glass4abf7842023-07-18 07:23:54 -06001575 if no_write_symbols:
Simon Glass4b4049e2024-08-26 13:11:39 -06001576 self.assertEqual(
1577 base_data +
1578 tools.get_bytes(0xff, 0x38 - len(base_data)) +
1579 U_BOOT_DATA + base_data, data)
Simon Glass4abf7842023-07-18 07:23:54 -06001580 else:
Simon Glass4b4049e2024-08-26 13:11:39 -06001581 got_vals = struct.unpack('<LLQLL', data[:24])
1582
1583 # For debugging:
1584 #print('expect:', list(f'{v:x}' for v in vals))
1585 #print(' got:', list(f'{v:x}' for v in got_vals))
1586
1587 self.assertEqual(vals, got_vals)
1588 self.assertEqual(sym_values, data[:24])
1589
1590 blen = len(base_data)
1591 self.assertEqual(base_data[24:], data[24:blen])
1592 self.assertEqual(0xff, data[blen])
1593
Simon Glass3eb30a42024-08-26 13:11:42 -06001594 if u_boot_offset:
1595 ofs = blen + 1 + len(U_BOOT_DATA)
1596 self.assertEqual(U_BOOT_DATA, data[blen + 1:ofs])
1597 else:
1598 ofs = blen + 1 + comp_uboot_len
Simon Glass4b4049e2024-08-26 13:11:39 -06001599
Simon Glass4b0f4142024-08-26 13:11:40 -06001600 self.assertEqual(sym_values2, data[ofs:ofs + 24])
Simon Glass4b4049e2024-08-26 13:11:39 -06001601 self.assertEqual(base_data[24:], data[ofs + 24:])
1602
1603 # Just repeating the above asserts all at once, for clarity
Simon Glass3eb30a42024-08-26 13:11:42 -06001604 if u_boot_offset:
1605 expected = (sym_values + base_data[24:] +
1606 tools.get_bytes(0xff, 1) + U_BOOT_DATA +
1607 sym_values2 + base_data[24:])
1608 self.assertEqual(expected, data)
Simon Glass4ca8e042017-11-13 18:55:01 -07001609
Simon Glass31e04cb2021-03-18 20:24:56 +13001610 def testSymbols(self):
1611 """Test binman can assign symbols embedded in U-Boot"""
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03001612 self.checkSymbols('053_symbols.dts', U_BOOT_SPL_DATA, 0x1c)
Simon Glass31e04cb2021-03-18 20:24:56 +13001613
1614 def testSymbolsNoDtb(self):
1615 """Test binman can assign symbols embedded in U-Boot SPL"""
Simon Glass3bbc9932021-03-21 18:24:29 +13001616 self.checkSymbols('196_symbols_nodtb.dts',
Simon Glass31e04cb2021-03-18 20:24:56 +13001617 U_BOOT_SPL_NODTB_DATA + U_BOOT_SPL_DTB_DATA,
1618 0x38)
1619
Simon Glasse76a3e62018-06-01 09:38:11 -06001620 def testPackUnitAddress(self):
1621 """Test that we support multiple binaries with the same name"""
Simon Glass511f6582018-10-01 12:22:30 -06001622 data = self._DoReadFile('054_unit_address.dts')
Simon Glasse76a3e62018-06-01 09:38:11 -06001623 self.assertEqual(U_BOOT_DATA + U_BOOT_DATA, data)
1624
Simon Glassa91e1152018-06-01 09:38:16 -06001625 def testSections(self):
1626 """Basic test of sections"""
Simon Glass511f6582018-10-01 12:22:30 -06001627 data = self._DoReadFile('055_sections.dts')
Simon Glass80025522022-01-29 14:14:04 -07001628 expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) +
1629 U_BOOT_DATA + tools.get_bytes(ord('a'), 12) +
1630 U_BOOT_DATA + tools.get_bytes(ord('&'), 4))
Simon Glassa91e1152018-06-01 09:38:16 -06001631 self.assertEqual(expected, data)
Simon Glassac599912017-11-12 21:52:22 -07001632
Simon Glass30732662018-06-01 09:38:20 -06001633 def testMap(self):
1634 """Tests outputting a map of the images"""
Simon Glass511f6582018-10-01 12:22:30 -06001635 _, _, map_data, _ = self._DoReadFileDtb('055_sections.dts', map=True)
Simon Glass7eca7922018-07-17 13:25:49 -06001636 self.assertEqual('''ImagePos Offset Size Name
Simon Glass49cd2b32023-02-07 14:34:18 -0700163700000000 00000000 00000028 image
Simon Glass7eca7922018-07-17 13:25:49 -0600163800000000 00000000 00000010 section@0
163900000000 00000000 00000004 u-boot
164000000010 00000010 00000010 section@1
164100000010 00000000 00000004 u-boot
164200000020 00000020 00000004 section@2
164300000020 00000000 00000004 u-boot
Simon Glass30732662018-06-01 09:38:20 -06001644''', map_data)
1645
Simon Glass3b78d532018-06-01 09:38:21 -06001646 def testNamePrefix(self):
1647 """Tests that name prefixes are used"""
Simon Glass511f6582018-10-01 12:22:30 -06001648 _, _, map_data, _ = self._DoReadFileDtb('056_name_prefix.dts', map=True)
Simon Glass7eca7922018-07-17 13:25:49 -06001649 self.assertEqual('''ImagePos Offset Size Name
Simon Glass49cd2b32023-02-07 14:34:18 -0700165000000000 00000000 00000028 image
Simon Glass7eca7922018-07-17 13:25:49 -0600165100000000 00000000 00000010 section@0
165200000000 00000000 00000004 ro-u-boot
165300000010 00000010 00000010 section@1
165400000010 00000000 00000004 rw-u-boot
Simon Glass3b78d532018-06-01 09:38:21 -06001655''', map_data)
1656
Simon Glass6ba679c2018-07-06 10:27:17 -06001657 def testUnknownContents(self):
1658 """Test that obtaining the contents works as expected"""
1659 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001660 self._DoReadFile('057_unknown_contents.dts', True)
Simon Glass39dd2152019-07-08 14:25:47 -06001661 self.assertIn("Image '/binman': Internal error: Could not complete "
Simon Glassc585dd42020-04-17 18:09:03 -06001662 "processing of contents: remaining ["
1663 "<binman.etype._testing.Entry__testing ", str(e.exception))
Simon Glass6ba679c2018-07-06 10:27:17 -06001664
Simon Glass2e1169f2018-07-06 10:27:19 -06001665 def testBadChangeSize(self):
1666 """Test that trying to change the size of an entry fails"""
Simon Glasse61b6f62019-07-08 14:25:37 -06001667 try:
1668 state.SetAllowEntryExpansion(False)
1669 with self.assertRaises(ValueError) as e:
1670 self._DoReadFile('059_change_size.dts', True)
Simon Glass8c702fb2019-07-20 12:23:57 -06001671 self.assertIn("Node '/binman/_testing': Cannot update entry size from 2 to 3",
Simon Glasse61b6f62019-07-08 14:25:37 -06001672 str(e.exception))
1673 finally:
1674 state.SetAllowEntryExpansion(True)
Simon Glass2e1169f2018-07-06 10:27:19 -06001675
Simon Glassa87014e2018-07-06 10:27:42 -06001676 def testUpdateFdt(self):
Simon Glasse8561af2018-08-01 15:22:37 -06001677 """Test that we can update the device tree with offset/size info"""
Simon Glass511f6582018-10-01 12:22:30 -06001678 _, _, _, out_dtb_fname = self._DoReadFileDtb('060_fdt_update.dts',
Simon Glassa87014e2018-07-06 10:27:42 -06001679 update_dtb=True)
Simon Glass5463a6a2018-07-17 13:25:52 -06001680 dtb = fdt.Fdt(out_dtb_fname)
1681 dtb.Scan()
Simon Glassfb30e292019-07-20 12:23:51 -06001682 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
Simon Glassa87014e2018-07-06 10:27:42 -06001683 self.assertEqual({
Simon Glass9dcc8612018-08-01 15:22:42 -06001684 'image-pos': 0,
Simon Glass3a9a2b82018-07-17 13:25:28 -06001685 'offset': 0,
Simon Glasse8561af2018-08-01 15:22:37 -06001686 '_testing:offset': 32,
Simon Glass8c702fb2019-07-20 12:23:57 -06001687 '_testing:size': 2,
Simon Glass9dcc8612018-08-01 15:22:42 -06001688 '_testing:image-pos': 32,
Simon Glasse8561af2018-08-01 15:22:37 -06001689 'section@0/u-boot:offset': 0,
Simon Glassa87014e2018-07-06 10:27:42 -06001690 'section@0/u-boot:size': len(U_BOOT_DATA),
Simon Glass9dcc8612018-08-01 15:22:42 -06001691 'section@0/u-boot:image-pos': 0,
Simon Glasse8561af2018-08-01 15:22:37 -06001692 'section@0:offset': 0,
Simon Glassa87014e2018-07-06 10:27:42 -06001693 'section@0:size': 16,
Simon Glass9dcc8612018-08-01 15:22:42 -06001694 'section@0:image-pos': 0,
Simon Glassa87014e2018-07-06 10:27:42 -06001695
Simon Glasse8561af2018-08-01 15:22:37 -06001696 'section@1/u-boot:offset': 0,
Simon Glassa87014e2018-07-06 10:27:42 -06001697 'section@1/u-boot:size': len(U_BOOT_DATA),
Simon Glass9dcc8612018-08-01 15:22:42 -06001698 'section@1/u-boot:image-pos': 16,
Simon Glasse8561af2018-08-01 15:22:37 -06001699 'section@1:offset': 16,
Simon Glassa87014e2018-07-06 10:27:42 -06001700 'section@1:size': 16,
Simon Glass9dcc8612018-08-01 15:22:42 -06001701 'section@1:image-pos': 16,
Simon Glassa87014e2018-07-06 10:27:42 -06001702 'size': 40
1703 }, props)
1704
1705 def testUpdateFdtBad(self):
1706 """Test that we detect when ProcessFdt never completes"""
1707 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001708 self._DoReadFileDtb('061_fdt_update_bad.dts', update_dtb=True)
Simon Glassa87014e2018-07-06 10:27:42 -06001709 self.assertIn('Could not complete processing of Fdt: remaining '
Simon Glassc585dd42020-04-17 18:09:03 -06001710 '[<binman.etype._testing.Entry__testing',
1711 str(e.exception))
Simon Glass2e1169f2018-07-06 10:27:19 -06001712
Simon Glass91710b32018-07-17 13:25:32 -06001713 def testEntryArgs(self):
1714 """Test passing arguments to entries from the command line"""
1715 entry_args = {
1716 'test-str-arg': 'test1',
1717 'test-int-arg': '456',
1718 }
Simon Glass511f6582018-10-01 12:22:30 -06001719 self._DoReadFileDtb('062_entry_args.dts', entry_args=entry_args)
Simon Glass91710b32018-07-17 13:25:32 -06001720 self.assertIn('image', control.images)
1721 entry = control.images['image'].GetEntries()['_testing']
1722 self.assertEqual('test0', entry.test_str_fdt)
1723 self.assertEqual('test1', entry.test_str_arg)
1724 self.assertEqual(123, entry.test_int_fdt)
1725 self.assertEqual(456, entry.test_int_arg)
1726
1727 def testEntryArgsMissing(self):
1728 """Test missing arguments and properties"""
1729 entry_args = {
1730 'test-int-arg': '456',
1731 }
Simon Glass511f6582018-10-01 12:22:30 -06001732 self._DoReadFileDtb('063_entry_args_missing.dts', entry_args=entry_args)
Simon Glass91710b32018-07-17 13:25:32 -06001733 entry = control.images['image'].GetEntries()['_testing']
1734 self.assertEqual('test0', entry.test_str_fdt)
1735 self.assertEqual(None, entry.test_str_arg)
1736 self.assertEqual(None, entry.test_int_fdt)
1737 self.assertEqual(456, entry.test_int_arg)
1738
1739 def testEntryArgsRequired(self):
1740 """Test missing arguments and properties"""
1741 entry_args = {
1742 'test-int-arg': '456',
1743 }
1744 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001745 self._DoReadFileDtb('064_entry_args_required.dts')
Simon Glass21db0ff2020-09-01 05:13:54 -06001746 self.assertIn("Node '/binman/_testing': "
1747 'Missing required properties/entry args: test-str-arg, '
1748 'test-int-fdt, test-int-arg',
Simon Glass91710b32018-07-17 13:25:32 -06001749 str(e.exception))
1750
1751 def testEntryArgsInvalidFormat(self):
1752 """Test that an invalid entry-argument format is detected"""
Simon Glassf46732a2019-07-08 14:25:29 -06001753 args = ['build', '-d', self.TestFile('064_entry_args_required.dts'),
1754 '-ano-value']
Simon Glass91710b32018-07-17 13:25:32 -06001755 with self.assertRaises(ValueError) as e:
1756 self._DoBinman(*args)
1757 self.assertIn("Invalid entry arguemnt 'no-value'", str(e.exception))
1758
1759 def testEntryArgsInvalidInteger(self):
1760 """Test that an invalid entry-argument integer is detected"""
1761 entry_args = {
1762 'test-int-arg': 'abc',
1763 }
1764 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001765 self._DoReadFileDtb('062_entry_args.dts', entry_args=entry_args)
Simon Glass91710b32018-07-17 13:25:32 -06001766 self.assertIn("Node '/binman/_testing': Cannot convert entry arg "
1767 "'test-int-arg' (value 'abc') to integer",
1768 str(e.exception))
1769
1770 def testEntryArgsInvalidDatatype(self):
1771 """Test that an invalid entry-argument datatype is detected
1772
1773 This test could be written in entry_test.py except that it needs
1774 access to control.entry_args, which seems more than that module should
1775 be able to see.
1776 """
1777 entry_args = {
1778 'test-bad-datatype-arg': '12',
1779 }
1780 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001781 self._DoReadFileDtb('065_entry_args_unknown_datatype.dts',
Simon Glass91710b32018-07-17 13:25:32 -06001782 entry_args=entry_args)
1783 self.assertIn('GetArg() internal error: Unknown data type ',
1784 str(e.exception))
1785
Simon Glass2ca52032018-07-17 13:25:33 -06001786 def testText(self):
1787 """Test for a text entry type"""
1788 entry_args = {
1789 'test-id': TEXT_DATA,
1790 'test-id2': TEXT_DATA2,
1791 'test-id3': TEXT_DATA3,
1792 }
Simon Glass511f6582018-10-01 12:22:30 -06001793 data, _, _, _ = self._DoReadFileDtb('066_text.dts',
Simon Glass2ca52032018-07-17 13:25:33 -06001794 entry_args=entry_args)
Simon Glass80025522022-01-29 14:14:04 -07001795 expected = (tools.to_bytes(TEXT_DATA) +
1796 tools.get_bytes(0, 8 - len(TEXT_DATA)) +
1797 tools.to_bytes(TEXT_DATA2) + tools.to_bytes(TEXT_DATA3) +
Simon Glass47f6a622019-07-08 13:18:40 -06001798 b'some text' + b'more text')
Simon Glass2ca52032018-07-17 13:25:33 -06001799 self.assertEqual(expected, data)
1800
Simon Glass969616c2018-07-17 13:25:36 -06001801 def testEntryDocs(self):
1802 """Test for creation of entry documentation"""
Simon Glass14d64e32025-04-29 07:21:59 -06001803 with terminal.capture() as (stdout, stderr):
Simon Glass220ff5f2020-08-05 13:27:46 -06001804 control.WriteEntryDocs(control.GetEntryModules())
Simon Glass969616c2018-07-17 13:25:36 -06001805 self.assertTrue(len(stdout.getvalue()) > 0)
1806
1807 def testEntryDocsMissing(self):
1808 """Test handling of missing entry documentation"""
1809 with self.assertRaises(ValueError) as e:
Simon Glass14d64e32025-04-29 07:21:59 -06001810 with terminal.capture() as (stdout, stderr):
Simon Glass220ff5f2020-08-05 13:27:46 -06001811 control.WriteEntryDocs(control.GetEntryModules(), 'u_boot')
Simon Glass969616c2018-07-17 13:25:36 -06001812 self.assertIn('Documentation is missing for modules: u_boot',
1813 str(e.exception))
1814
Simon Glass704784b2018-07-17 13:25:38 -06001815 def testFmap(self):
1816 """Basic test of generation of a flashrom fmap"""
Simon Glass511f6582018-10-01 12:22:30 -06001817 data = self._DoReadFile('067_fmap.dts')
Simon Glass704784b2018-07-17 13:25:38 -06001818 fhdr, fentries = fmap_util.DecodeFmap(data[32:])
Simon Glass80025522022-01-29 14:14:04 -07001819 expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) +
1820 U_BOOT_DATA + tools.get_bytes(ord('a'), 12))
Simon Glass704784b2018-07-17 13:25:38 -06001821 self.assertEqual(expected, data[:32])
Simon Glass303f62f2019-05-17 22:00:46 -06001822 self.assertEqual(b'__FMAP__', fhdr.signature)
Simon Glass704784b2018-07-17 13:25:38 -06001823 self.assertEqual(1, fhdr.ver_major)
1824 self.assertEqual(0, fhdr.ver_minor)
1825 self.assertEqual(0, fhdr.base)
Simon Glassb1d414c2021-04-03 11:05:10 +13001826 expect_size = fmap_util.FMAP_HEADER_LEN + fmap_util.FMAP_AREA_LEN * 5
Simon Glass82059c22021-04-03 11:05:09 +13001827 self.assertEqual(16 + 16 + expect_size, fhdr.image_size)
Simon Glass303f62f2019-05-17 22:00:46 -06001828 self.assertEqual(b'FMAP', fhdr.name)
Simon Glassb1d414c2021-04-03 11:05:10 +13001829 self.assertEqual(5, fhdr.nareas)
Simon Glass82059c22021-04-03 11:05:09 +13001830 fiter = iter(fentries)
Simon Glass704784b2018-07-17 13:25:38 -06001831
Simon Glass82059c22021-04-03 11:05:09 +13001832 fentry = next(fiter)
Simon Glassb1d414c2021-04-03 11:05:10 +13001833 self.assertEqual(b'SECTION0', fentry.name)
1834 self.assertEqual(0, fentry.offset)
1835 self.assertEqual(16, fentry.size)
Simon Glasscda991e2023-02-12 17:11:15 -07001836 self.assertEqual(fmap_util.FMAP_AREA_PRESERVE, fentry.flags)
Simon Glassb1d414c2021-04-03 11:05:10 +13001837
1838 fentry = next(fiter)
Simon Glass82059c22021-04-03 11:05:09 +13001839 self.assertEqual(b'RO_U_BOOT', fentry.name)
1840 self.assertEqual(0, fentry.offset)
1841 self.assertEqual(4, fentry.size)
1842 self.assertEqual(0, fentry.flags)
Simon Glass704784b2018-07-17 13:25:38 -06001843
Simon Glass82059c22021-04-03 11:05:09 +13001844 fentry = next(fiter)
Simon Glassb1d414c2021-04-03 11:05:10 +13001845 self.assertEqual(b'SECTION1', fentry.name)
1846 self.assertEqual(16, fentry.offset)
1847 self.assertEqual(16, fentry.size)
1848 self.assertEqual(0, fentry.flags)
1849
1850 fentry = next(fiter)
Simon Glass82059c22021-04-03 11:05:09 +13001851 self.assertEqual(b'RW_U_BOOT', fentry.name)
1852 self.assertEqual(16, fentry.offset)
1853 self.assertEqual(4, fentry.size)
1854 self.assertEqual(0, fentry.flags)
Simon Glass704784b2018-07-17 13:25:38 -06001855
Simon Glass82059c22021-04-03 11:05:09 +13001856 fentry = next(fiter)
1857 self.assertEqual(b'FMAP', fentry.name)
1858 self.assertEqual(32, fentry.offset)
1859 self.assertEqual(expect_size, fentry.size)
1860 self.assertEqual(0, fentry.flags)
Simon Glass704784b2018-07-17 13:25:38 -06001861
Simon Glassdb168d42018-07-17 13:25:39 -06001862 def testBlobNamedByArg(self):
1863 """Test we can add a blob with the filename coming from an entry arg"""
1864 entry_args = {
1865 'cros-ec-rw-path': 'ecrw.bin',
1866 }
Simon Glass21db0ff2020-09-01 05:13:54 -06001867 self._DoReadFileDtb('068_blob_named_by_arg.dts', entry_args=entry_args)
Simon Glassdb168d42018-07-17 13:25:39 -06001868
Simon Glass53f53992018-07-17 13:25:40 -06001869 def testFill(self):
1870 """Test for an fill entry type"""
Simon Glass511f6582018-10-01 12:22:30 -06001871 data = self._DoReadFile('069_fill.dts')
Simon Glass80025522022-01-29 14:14:04 -07001872 expected = tools.get_bytes(0xff, 8) + tools.get_bytes(0, 8)
Simon Glass53f53992018-07-17 13:25:40 -06001873 self.assertEqual(expected, data)
1874
1875 def testFillNoSize(self):
1876 """Test for an fill entry type with no size"""
1877 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001878 self._DoReadFile('070_fill_no_size.dts')
Simon Glass0cf5bce2022-08-13 11:40:44 -06001879 self.assertIn("'fill' entry is missing properties: size",
Simon Glass53f53992018-07-17 13:25:40 -06001880 str(e.exception))
1881
Simon Glassc1ae83c2018-07-17 13:25:44 -06001882 def _HandleGbbCommand(self, pipe_list):
1883 """Fake calls to the futility utility"""
Simon Glass9a1c7262023-02-22 12:14:49 -07001884 if 'futility' in pipe_list[0][0]:
Simon Glassc1ae83c2018-07-17 13:25:44 -06001885 fname = pipe_list[0][-1]
1886 # Append our GBB data to the file, which will happen every time the
1887 # futility command is called.
Simon Glass33486662019-05-14 15:53:42 -06001888 with open(fname, 'ab') as fd:
Simon Glassc1ae83c2018-07-17 13:25:44 -06001889 fd.write(GBB_DATA)
1890 return command.CommandResult()
1891
1892 def testGbb(self):
1893 """Test for the Chromium OS Google Binary Block"""
Simon Glass5dc22cf2025-02-03 09:26:42 -07001894 command.TEST_RESULT = self._HandleGbbCommand
Simon Glassc1ae83c2018-07-17 13:25:44 -06001895 entry_args = {
1896 'keydir': 'devkeys',
1897 'bmpblk': 'bmpblk.bin',
1898 }
Simon Glass511f6582018-10-01 12:22:30 -06001899 data, _, _, _ = self._DoReadFileDtb('071_gbb.dts', entry_args=entry_args)
Simon Glassc1ae83c2018-07-17 13:25:44 -06001900
1901 # Since futility
Simon Glass80025522022-01-29 14:14:04 -07001902 expected = (GBB_DATA + GBB_DATA + tools.get_bytes(0, 8) +
1903 tools.get_bytes(0, 0x2180 - 16))
Simon Glassc1ae83c2018-07-17 13:25:44 -06001904 self.assertEqual(expected, data)
1905
1906 def testGbbTooSmall(self):
1907 """Test for the Chromium OS Google Binary Block being large enough"""
1908 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001909 self._DoReadFileDtb('072_gbb_too_small.dts')
Simon Glassc1ae83c2018-07-17 13:25:44 -06001910 self.assertIn("Node '/binman/gbb': GBB is too small",
1911 str(e.exception))
1912
1913 def testGbbNoSize(self):
1914 """Test for the Chromium OS Google Binary Block having a size"""
1915 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001916 self._DoReadFileDtb('073_gbb_no_size.dts')
Simon Glassc1ae83c2018-07-17 13:25:44 -06001917 self.assertIn("Node '/binman/gbb': GBB must have a fixed size",
1918 str(e.exception))
1919
Simon Glass66152ce2022-01-09 20:14:09 -07001920 def testGbbMissing(self):
1921 """Test that binman still produces an image if futility is missing"""
1922 entry_args = {
1923 'keydir': 'devkeys',
1924 }
Simon Glass14d64e32025-04-29 07:21:59 -06001925 with terminal.capture() as (_, stderr):
Simon Glass66152ce2022-01-09 20:14:09 -07001926 self._DoTestFile('071_gbb.dts', force_missing_bintools='futility',
1927 entry_args=entry_args)
1928 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07001929 self.assertRegex(err, "Image 'image'.*missing bintools.*: futility")
Simon Glass66152ce2022-01-09 20:14:09 -07001930
Simon Glass5c350162018-07-17 13:25:47 -06001931 def _HandleVblockCommand(self, pipe_list):
Simon Glass220c6222021-01-06 21:35:17 -07001932 """Fake calls to the futility utility
1933
1934 The expected pipe is:
1935
1936 [('futility', 'vbutil_firmware', '--vblock',
1937 'vblock.vblock', '--keyblock', 'devkeys/firmware.keyblock',
1938 '--signprivate', 'devkeys/firmware_data_key.vbprivk',
1939 '--version', '1', '--fv', 'input.vblock', '--kernelkey',
1940 'devkeys/kernel_subkey.vbpubk', '--flags', '1')]
1941
1942 This writes to the output file (here, 'vblock.vblock'). If
1943 self._hash_data is False, it writes VBLOCK_DATA, else it writes a hash
1944 of the input data (here, 'input.vblock').
1945 """
Simon Glass9a1c7262023-02-22 12:14:49 -07001946 if 'futility' in pipe_list[0][0]:
Simon Glass5c350162018-07-17 13:25:47 -06001947 fname = pipe_list[0][3]
Simon Glass639505b2018-09-14 04:57:11 -06001948 with open(fname, 'wb') as fd:
Simon Glass220c6222021-01-06 21:35:17 -07001949 if self._hash_data:
1950 infile = pipe_list[0][11]
1951 m = hashlib.sha256()
Simon Glass80025522022-01-29 14:14:04 -07001952 data = tools.read_file(infile)
Simon Glass220c6222021-01-06 21:35:17 -07001953 m.update(data)
1954 fd.write(m.digest())
1955 else:
1956 fd.write(VBLOCK_DATA)
1957
Simon Glass5c350162018-07-17 13:25:47 -06001958 return command.CommandResult()
1959
1960 def testVblock(self):
1961 """Test for the Chromium OS Verified Boot Block"""
Simon Glass220c6222021-01-06 21:35:17 -07001962 self._hash_data = False
Simon Glass5dc22cf2025-02-03 09:26:42 -07001963 command.TEST_RESULT = self._HandleVblockCommand
Simon Glass5c350162018-07-17 13:25:47 -06001964 entry_args = {
1965 'keydir': 'devkeys',
1966 }
Simon Glass511f6582018-10-01 12:22:30 -06001967 data, _, _, _ = self._DoReadFileDtb('074_vblock.dts',
Simon Glass5c350162018-07-17 13:25:47 -06001968 entry_args=entry_args)
1969 expected = U_BOOT_DATA + VBLOCK_DATA + U_BOOT_DTB_DATA
1970 self.assertEqual(expected, data)
1971
1972 def testVblockNoContent(self):
1973 """Test we detect a vblock which has no content to sign"""
1974 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001975 self._DoReadFile('075_vblock_no_content.dts')
Simon Glasse1915782021-03-21 18:24:31 +13001976 self.assertIn("Node '/binman/vblock': Collection must have a 'content' "
Simon Glass5c350162018-07-17 13:25:47 -06001977 'property', str(e.exception))
1978
1979 def testVblockBadPhandle(self):
1980 """Test that we detect a vblock with an invalid phandle in contents"""
1981 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001982 self._DoReadFile('076_vblock_bad_phandle.dts')
Simon Glass5c350162018-07-17 13:25:47 -06001983 self.assertIn("Node '/binman/vblock': Cannot find node for phandle "
1984 '1000', str(e.exception))
1985
1986 def testVblockBadEntry(self):
1987 """Test that we detect an entry that points to a non-entry"""
1988 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001989 self._DoReadFile('077_vblock_bad_entry.dts')
Simon Glass5c350162018-07-17 13:25:47 -06001990 self.assertIn("Node '/binman/vblock': Cannot find entry for node "
1991 "'other'", str(e.exception))
1992
Simon Glass220c6222021-01-06 21:35:17 -07001993 def testVblockContent(self):
1994 """Test that the vblock signs the right data"""
1995 self._hash_data = True
Simon Glass5dc22cf2025-02-03 09:26:42 -07001996 command.TEST_RESULT = self._HandleVblockCommand
Simon Glass220c6222021-01-06 21:35:17 -07001997 entry_args = {
1998 'keydir': 'devkeys',
1999 }
2000 data = self._DoReadFileDtb(
2001 '189_vblock_content.dts', use_real_dtb=True, update_dtb=True,
2002 entry_args=entry_args)[0]
2003 hashlen = 32 # SHA256 hash is 32 bytes
2004 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
2005 hashval = data[-hashlen:]
2006 dtb = data[len(U_BOOT_DATA):-hashlen]
2007
2008 expected_data = U_BOOT_DATA + dtb
2009
2010 # The hashval should be a hash of the dtb
2011 m = hashlib.sha256()
2012 m.update(expected_data)
2013 expected_hashval = m.digest()
2014 self.assertEqual(expected_hashval, hashval)
2015
Simon Glass66152ce2022-01-09 20:14:09 -07002016 def testVblockMissing(self):
2017 """Test that binman still produces an image if futility is missing"""
2018 entry_args = {
2019 'keydir': 'devkeys',
2020 }
Simon Glass14d64e32025-04-29 07:21:59 -06002021 with terminal.capture() as (_, stderr):
Simon Glass66152ce2022-01-09 20:14:09 -07002022 self._DoTestFile('074_vblock.dts',
2023 force_missing_bintools='futility',
2024 entry_args=entry_args)
2025 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07002026 self.assertRegex(err, "Image 'image'.*missing bintools.*: futility")
Simon Glass66152ce2022-01-09 20:14:09 -07002027
Simon Glass8425a1f2018-07-17 13:25:48 -06002028 def testTpl(self):
Simon Glass3eb5b202019-08-24 07:23:00 -06002029 """Test that an image with TPL and its device tree can be created"""
Simon Glass8425a1f2018-07-17 13:25:48 -06002030 # ELF file with a '__bss_size' symbol
Simon Glass3eb5b202019-08-24 07:23:00 -06002031 self._SetupTplElf()
Simon Glass511f6582018-10-01 12:22:30 -06002032 data = self._DoReadFile('078_u_boot_tpl.dts')
Simon Glass8425a1f2018-07-17 13:25:48 -06002033 self.assertEqual(U_BOOT_TPL_DATA + U_BOOT_TPL_DTB_DATA, data)
2034
Simon Glass24b97442018-07-17 13:25:51 -06002035 def testUsesPos(self):
2036 """Test that the 'pos' property cannot be used anymore"""
2037 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002038 data = self._DoReadFile('079_uses_pos.dts')
Simon Glass24b97442018-07-17 13:25:51 -06002039 self.assertIn("Node '/binman/u-boot': Please use 'offset' instead of "
2040 "'pos'", str(e.exception))
2041
Simon Glass274bf092018-09-14 04:57:08 -06002042 def testFillZero(self):
2043 """Test for an fill entry type with a size of 0"""
Simon Glass511f6582018-10-01 12:22:30 -06002044 data = self._DoReadFile('080_fill_empty.dts')
Simon Glass80025522022-01-29 14:14:04 -07002045 self.assertEqual(tools.get_bytes(0, 16), data)
Simon Glass274bf092018-09-14 04:57:08 -06002046
Simon Glass267de432018-09-14 04:57:09 -06002047 def testTextMissing(self):
2048 """Test for a text entry type where there is no text"""
2049 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002050 self._DoReadFileDtb('066_text.dts',)
Simon Glass267de432018-09-14 04:57:09 -06002051 self.assertIn("Node '/binman/text': No value provided for text label "
2052 "'test-id'", str(e.exception))
2053
Simon Glassed40e962018-09-14 04:57:10 -06002054 def testPackStart16Tpl(self):
2055 """Test that an image with an x86 start16 TPL region can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06002056 data = self._DoReadFile('081_x86_start16_tpl.dts')
Simon Glassed40e962018-09-14 04:57:10 -06002057 self.assertEqual(X86_START16_TPL_DATA, data[:len(X86_START16_TPL_DATA)])
2058
Simon Glass3b376c32018-09-14 04:57:12 -06002059 def testSelectImage(self):
2060 """Test that we can select which images to build"""
Simon Glassb4595d82019-04-25 21:58:34 -06002061 expected = 'Skipping images: image1'
2062
2063 # We should only get the expected message in verbose mode
Simon Glass8a50b4a2019-07-08 13:18:48 -06002064 for verbosity in (0, 2):
Simon Glass14d64e32025-04-29 07:21:59 -06002065 with terminal.capture() as (stdout, stderr):
Simon Glassb4595d82019-04-25 21:58:34 -06002066 retcode = self._DoTestFile('006_dual_image.dts',
2067 verbosity=verbosity,
2068 images=['image2'])
2069 self.assertEqual(0, retcode)
2070 if verbosity:
2071 self.assertIn(expected, stdout.getvalue())
2072 else:
2073 self.assertNotIn(expected, stdout.getvalue())
Simon Glass3b376c32018-09-14 04:57:12 -06002074
Simon Glass80025522022-01-29 14:14:04 -07002075 self.assertFalse(os.path.exists(tools.get_output_filename('image1.bin')))
2076 self.assertTrue(os.path.exists(tools.get_output_filename('image2.bin')))
Simon Glassb3d6fc72019-07-20 12:24:10 -06002077 self._CleanupOutputDir()
Simon Glass3b376c32018-09-14 04:57:12 -06002078
Simon Glasse219aa42018-09-14 04:57:24 -06002079 def testUpdateFdtAll(self):
2080 """Test that all device trees are updated with offset/size info"""
Marek Vasutf7413f02023-07-18 07:23:58 -06002081 self._SetupSplElf()
2082 self._SetupTplElf()
Simon Glass5b4bce32019-07-08 14:25:26 -06002083 data = self._DoReadFileRealDtb('082_fdt_update_all.dts')
Simon Glasse219aa42018-09-14 04:57:24 -06002084
2085 base_expected = {
Simon Glasse219aa42018-09-14 04:57:24 -06002086 'offset': 0,
Simon Glass56d05412022-02-28 07:16:54 -07002087 'image-pos': 0,
2088 'size': 2320,
Simon Glasse219aa42018-09-14 04:57:24 -06002089 'section:offset': 0,
Simon Glass56d05412022-02-28 07:16:54 -07002090 'section:image-pos': 0,
2091 'section:size': 565,
2092 'section/u-boot-dtb:offset': 0,
2093 'section/u-boot-dtb:image-pos': 0,
2094 'section/u-boot-dtb:size': 565,
2095 'u-boot-spl-dtb:offset': 565,
2096 'u-boot-spl-dtb:image-pos': 565,
2097 'u-boot-spl-dtb:size': 585,
2098 'u-boot-tpl-dtb:offset': 1150,
2099 'u-boot-tpl-dtb:image-pos': 1150,
2100 'u-boot-tpl-dtb:size': 585,
2101 'u-boot-vpl-dtb:image-pos': 1735,
2102 'u-boot-vpl-dtb:offset': 1735,
2103 'u-boot-vpl-dtb:size': 585,
Simon Glasse219aa42018-09-14 04:57:24 -06002104 }
2105
2106 # We expect three device-tree files in the output, one after the other.
2107 # Read them in sequence. We look for an 'spl' property in the SPL tree,
2108 # and 'tpl' in the TPL tree, to make sure they are distinct from the
2109 # main U-Boot tree. All three should have the same postions and offset.
2110 start = 0
Simon Glass56d05412022-02-28 07:16:54 -07002111 self.maxDiff = None
2112 for item in ['', 'spl', 'tpl', 'vpl']:
Simon Glasse219aa42018-09-14 04:57:24 -06002113 dtb = fdt.Fdt.FromData(data[start:])
2114 dtb.Scan()
Simon Glassfb30e292019-07-20 12:23:51 -06002115 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS +
Simon Glass56d05412022-02-28 07:16:54 -07002116 ['spl', 'tpl', 'vpl'])
Simon Glasse219aa42018-09-14 04:57:24 -06002117 expected = dict(base_expected)
2118 if item:
2119 expected[item] = 0
2120 self.assertEqual(expected, props)
2121 start += dtb._fdt_obj.totalsize()
2122
2123 def testUpdateFdtOutput(self):
2124 """Test that output DTB files are updated"""
2125 try:
Simon Glass511f6582018-10-01 12:22:30 -06002126 data, dtb_data, _, _ = self._DoReadFileDtb('082_fdt_update_all.dts',
Simon Glasse219aa42018-09-14 04:57:24 -06002127 use_real_dtb=True, update_dtb=True, reset_dtbs=False)
2128
2129 # Unfortunately, compiling a source file always results in a file
2130 # called source.dtb (see fdt_util.EnsureCompiled()). The test
Simon Glass511f6582018-10-01 12:22:30 -06002131 # source file (e.g. test/075_fdt_update_all.dts) thus does not enter
Simon Glasse219aa42018-09-14 04:57:24 -06002132 # binman as a file called u-boot.dtb. To fix this, copy the file
2133 # over to the expected place.
Simon Glasse219aa42018-09-14 04:57:24 -06002134 start = 0
2135 for fname in ['u-boot.dtb.out', 'spl/u-boot-spl.dtb.out',
Simon Glass56d05412022-02-28 07:16:54 -07002136 'tpl/u-boot-tpl.dtb.out', 'vpl/u-boot-vpl.dtb.out']:
Simon Glasse219aa42018-09-14 04:57:24 -06002137 dtb = fdt.Fdt.FromData(data[start:])
2138 size = dtb._fdt_obj.totalsize()
Simon Glass80025522022-01-29 14:14:04 -07002139 pathname = tools.get_output_filename(os.path.split(fname)[1])
2140 outdata = tools.read_file(pathname)
Simon Glasse219aa42018-09-14 04:57:24 -06002141 name = os.path.split(fname)[0]
2142
2143 if name:
Simon Glass56d05412022-02-28 07:16:54 -07002144 orig_indata = self._GetDtbContentsForSpls(dtb_data, name)
Simon Glasse219aa42018-09-14 04:57:24 -06002145 else:
2146 orig_indata = dtb_data
2147 self.assertNotEqual(outdata, orig_indata,
2148 "Expected output file '%s' be updated" % pathname)
2149 self.assertEqual(outdata, data[start:start + size],
2150 "Expected output file '%s' to match output image" %
2151 pathname)
2152 start += size
2153 finally:
2154 self._ResetDtbs()
2155
Simon Glass7ba33592018-09-14 04:57:26 -06002156 def _decompress(self, data):
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +02002157 bintool = self.comp_bintools['lz4']
2158 return bintool.decompress(data)
Simon Glass7ba33592018-09-14 04:57:26 -06002159
2160 def testCompress(self):
2161 """Test compression of blobs"""
Simon Glass1de34482019-07-08 13:18:53 -06002162 self._CheckLz4()
Simon Glass511f6582018-10-01 12:22:30 -06002163 data, _, _, out_dtb_fname = self._DoReadFileDtb('083_compress.dts',
Simon Glass7ba33592018-09-14 04:57:26 -06002164 use_real_dtb=True, update_dtb=True)
2165 dtb = fdt.Fdt(out_dtb_fname)
2166 dtb.Scan()
2167 props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
2168 orig = self._decompress(data)
Brandon Maiera657bc62024-06-04 16:16:05 +00002169 self.assertEqual(COMPRESS_DATA, orig)
Simon Glass789b34402020-10-26 17:40:15 -06002170
2171 # Do a sanity check on various fields
2172 image = control.images['image']
2173 entries = image.GetEntries()
2174 self.assertEqual(1, len(entries))
2175
2176 entry = entries['blob']
2177 self.assertEqual(COMPRESS_DATA, entry.uncomp_data)
2178 self.assertEqual(len(COMPRESS_DATA), entry.uncomp_size)
2179 orig = self._decompress(entry.data)
2180 self.assertEqual(orig, entry.uncomp_data)
2181
Simon Glass72eeff12020-10-26 17:40:16 -06002182 self.assertEqual(image.data, entry.data)
2183
Simon Glass7ba33592018-09-14 04:57:26 -06002184 expected = {
2185 'blob:uncomp-size': len(COMPRESS_DATA),
2186 'blob:size': len(data),
2187 'size': len(data),
2188 }
2189 self.assertEqual(expected, props)
2190
Simon Glassac6328c2018-09-14 04:57:28 -06002191 def testFiles(self):
2192 """Test bringing in multiple files"""
Simon Glass511f6582018-10-01 12:22:30 -06002193 data = self._DoReadFile('084_files.dts')
Simon Glassac6328c2018-09-14 04:57:28 -06002194 self.assertEqual(FILES_DATA, data)
2195
2196 def testFilesCompress(self):
2197 """Test bringing in multiple files and compressing them"""
Simon Glass1de34482019-07-08 13:18:53 -06002198 self._CheckLz4()
Simon Glass511f6582018-10-01 12:22:30 -06002199 data = self._DoReadFile('085_files_compress.dts')
Simon Glassac6328c2018-09-14 04:57:28 -06002200
2201 image = control.images['image']
2202 entries = image.GetEntries()
2203 files = entries['files']
Simon Glass39dd2152019-07-08 14:25:47 -06002204 entries = files._entries
Simon Glassac6328c2018-09-14 04:57:28 -06002205
Simon Glass303f62f2019-05-17 22:00:46 -06002206 orig = b''
Simon Glassac6328c2018-09-14 04:57:28 -06002207 for i in range(1, 3):
2208 key = '%d.dat' % i
2209 start = entries[key].image_pos
2210 len = entries[key].size
2211 chunk = data[start:start + len]
2212 orig += self._decompress(chunk)
2213
2214 self.assertEqual(FILES_DATA, orig)
2215
2216 def testFilesMissing(self):
2217 """Test missing files"""
2218 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002219 data = self._DoReadFile('086_files_none.dts')
Simon Glassac6328c2018-09-14 04:57:28 -06002220 self.assertIn("Node '/binman/files': Pattern \'files/*.none\' matched "
2221 'no files', str(e.exception))
2222
2223 def testFilesNoPattern(self):
2224 """Test missing files"""
2225 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002226 data = self._DoReadFile('087_files_no_pattern.dts')
Simon Glassac6328c2018-09-14 04:57:28 -06002227 self.assertIn("Node '/binman/files': Missing 'pattern' property",
2228 str(e.exception))
2229
Simon Glassdd156a42022-03-05 20:18:59 -07002230 def testExtendSize(self):
2231 """Test an extending entry"""
2232 data, _, map_data, _ = self._DoReadFileDtb('088_extend_size.dts',
Simon Glassfa79a812018-09-14 04:57:29 -06002233 map=True)
Simon Glass80025522022-01-29 14:14:04 -07002234 expect = (tools.get_bytes(ord('a'), 8) + U_BOOT_DATA +
2235 MRC_DATA + tools.get_bytes(ord('b'), 1) + U_BOOT_DATA +
2236 tools.get_bytes(ord('c'), 8) + U_BOOT_DATA +
2237 tools.get_bytes(ord('d'), 8))
Simon Glassfa79a812018-09-14 04:57:29 -06002238 self.assertEqual(expect, data)
2239 self.assertEqual('''ImagePos Offset Size Name
Simon Glass49cd2b32023-02-07 14:34:18 -0700224000000000 00000000 00000028 image
Simon Glassfa79a812018-09-14 04:57:29 -0600224100000000 00000000 00000008 fill
224200000008 00000008 00000004 u-boot
22430000000c 0000000c 00000004 section
22440000000c 00000000 00000003 intel-mrc
224500000010 00000010 00000004 u-boot2
224600000014 00000014 0000000c section2
224700000014 00000000 00000008 fill
22480000001c 00000008 00000004 u-boot
224900000020 00000020 00000008 fill2
2250''', map_data)
2251
Simon Glassdd156a42022-03-05 20:18:59 -07002252 def testExtendSizeBad(self):
2253 """Test an extending entry which fails to provide contents"""
Simon Glass14d64e32025-04-29 07:21:59 -06002254 with terminal.capture() as (stdout, stderr):
Simon Glasscd817d52018-09-14 04:57:36 -06002255 with self.assertRaises(ValueError) as e:
Simon Glassdd156a42022-03-05 20:18:59 -07002256 self._DoReadFileDtb('089_extend_size_bad.dts', map=True)
Simon Glassfa79a812018-09-14 04:57:29 -06002257 self.assertIn("Node '/binman/_testing': Cannot obtain contents when "
2258 'expanding entry', str(e.exception))
2259
Simon Glassae7cf032018-09-14 04:57:31 -06002260 def testHash(self):
2261 """Test hashing of the contents of an entry"""
Simon Glass511f6582018-10-01 12:22:30 -06002262 _, _, _, out_dtb_fname = self._DoReadFileDtb('090_hash.dts',
Simon Glassae7cf032018-09-14 04:57:31 -06002263 use_real_dtb=True, update_dtb=True)
2264 dtb = fdt.Fdt(out_dtb_fname)
2265 dtb.Scan()
2266 hash_node = dtb.GetNode('/binman/u-boot/hash').props['value']
2267 m = hashlib.sha256()
2268 m.update(U_BOOT_DATA)
Simon Glass303f62f2019-05-17 22:00:46 -06002269 self.assertEqual(m.digest(), b''.join(hash_node.value))
Simon Glassae7cf032018-09-14 04:57:31 -06002270
2271 def testHashNoAlgo(self):
2272 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002273 self._DoReadFileDtb('091_hash_no_algo.dts', update_dtb=True)
Simon Glassae7cf032018-09-14 04:57:31 -06002274 self.assertIn("Node \'/binman/u-boot\': Missing \'algo\' property for "
2275 'hash node', str(e.exception))
2276
2277 def testHashBadAlgo(self):
2278 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002279 self._DoReadFileDtb('092_hash_bad_algo.dts', update_dtb=True)
Simon Glass64af7c22022-02-08 10:59:44 -07002280 self.assertIn("Node '/binman/u-boot': Unknown hash algorithm 'invalid'",
Simon Glassae7cf032018-09-14 04:57:31 -06002281 str(e.exception))
2282
2283 def testHashSection(self):
2284 """Test hashing of the contents of an entry"""
Simon Glass511f6582018-10-01 12:22:30 -06002285 _, _, _, out_dtb_fname = self._DoReadFileDtb('099_hash_section.dts',
Simon Glassae7cf032018-09-14 04:57:31 -06002286 use_real_dtb=True, update_dtb=True)
2287 dtb = fdt.Fdt(out_dtb_fname)
2288 dtb.Scan()
2289 hash_node = dtb.GetNode('/binman/section/hash').props['value']
2290 m = hashlib.sha256()
2291 m.update(U_BOOT_DATA)
Simon Glass80025522022-01-29 14:14:04 -07002292 m.update(tools.get_bytes(ord('a'), 16))
Simon Glass303f62f2019-05-17 22:00:46 -06002293 self.assertEqual(m.digest(), b''.join(hash_node.value))
Simon Glassae7cf032018-09-14 04:57:31 -06002294
Simon Glass3fb4f422018-09-14 04:57:32 -06002295 def testPackUBootTplMicrocode(self):
2296 """Test that x86 microcode can be handled correctly in TPL
2297
2298 We expect to see the following in the image, in order:
2299 u-boot-tpl-nodtb.bin with a microcode pointer inserted at the correct
2300 place
2301 u-boot-tpl.dtb with the microcode removed
2302 the microcode
2303 """
Simon Glass3eb5b202019-08-24 07:23:00 -06002304 self._SetupTplElf('u_boot_ucode_ptr')
Simon Glass511f6582018-10-01 12:22:30 -06002305 first, pos_and_size = self._RunMicrocodeTest('093_x86_tpl_ucode.dts',
Simon Glass3fb4f422018-09-14 04:57:32 -06002306 U_BOOT_TPL_NODTB_DATA)
Simon Glass303f62f2019-05-17 22:00:46 -06002307 self.assertEqual(b'tplnodtb with microc' + pos_and_size +
2308 b'ter somewhere in here', first)
Simon Glass3fb4f422018-09-14 04:57:32 -06002309
Simon Glassc64aea52018-09-14 04:57:34 -06002310 def testFmapX86(self):
2311 """Basic test of generation of a flashrom fmap"""
Simon Glass511f6582018-10-01 12:22:30 -06002312 data = self._DoReadFile('094_fmap_x86.dts')
Simon Glassc64aea52018-09-14 04:57:34 -06002313 fhdr, fentries = fmap_util.DecodeFmap(data[32:])
Simon Glass80025522022-01-29 14:14:04 -07002314 expected = U_BOOT_DATA + MRC_DATA + tools.get_bytes(ord('a'), 32 - 7)
Simon Glassc64aea52018-09-14 04:57:34 -06002315 self.assertEqual(expected, data[:32])
2316 fhdr, fentries = fmap_util.DecodeFmap(data[32:])
2317
2318 self.assertEqual(0x100, fhdr.image_size)
Simon Glassed836ac2025-02-26 09:26:17 -07002319 base = (1 << 32) - 0x100
Simon Glassc64aea52018-09-14 04:57:34 -06002320
Simon Glassed836ac2025-02-26 09:26:17 -07002321 self.assertEqual(base, fentries[0].offset)
Simon Glassc64aea52018-09-14 04:57:34 -06002322 self.assertEqual(4, fentries[0].size)
Simon Glass303f62f2019-05-17 22:00:46 -06002323 self.assertEqual(b'U_BOOT', fentries[0].name)
Simon Glassc64aea52018-09-14 04:57:34 -06002324
Simon Glassed836ac2025-02-26 09:26:17 -07002325 self.assertEqual(base + 4, fentries[1].offset)
Simon Glassc64aea52018-09-14 04:57:34 -06002326 self.assertEqual(3, fentries[1].size)
Simon Glass303f62f2019-05-17 22:00:46 -06002327 self.assertEqual(b'INTEL_MRC', fentries[1].name)
Simon Glassc64aea52018-09-14 04:57:34 -06002328
Simon Glassed836ac2025-02-26 09:26:17 -07002329 self.assertEqual(base + 32, fentries[2].offset)
Simon Glassc64aea52018-09-14 04:57:34 -06002330 self.assertEqual(fmap_util.FMAP_HEADER_LEN +
2331 fmap_util.FMAP_AREA_LEN * 3, fentries[2].size)
Simon Glass303f62f2019-05-17 22:00:46 -06002332 self.assertEqual(b'FMAP', fentries[2].name)
Simon Glassc64aea52018-09-14 04:57:34 -06002333
2334 def testFmapX86Section(self):
2335 """Basic test of generation of a flashrom fmap"""
Simon Glass511f6582018-10-01 12:22:30 -06002336 data = self._DoReadFile('095_fmap_x86_section.dts')
Simon Glass80025522022-01-29 14:14:04 -07002337 expected = U_BOOT_DATA + MRC_DATA + tools.get_bytes(ord('b'), 32 - 7)
Simon Glassc64aea52018-09-14 04:57:34 -06002338 self.assertEqual(expected, data[:32])
2339 fhdr, fentries = fmap_util.DecodeFmap(data[36:])
2340
Simon Glassb1d414c2021-04-03 11:05:10 +13002341 self.assertEqual(0x180, fhdr.image_size)
Simon Glassed836ac2025-02-26 09:26:17 -07002342 base = (1 << 32) - 0x180
Simon Glassb1d414c2021-04-03 11:05:10 +13002343 expect_size = fmap_util.FMAP_HEADER_LEN + fmap_util.FMAP_AREA_LEN * 4
Simon Glass82059c22021-04-03 11:05:09 +13002344 fiter = iter(fentries)
Simon Glassc64aea52018-09-14 04:57:34 -06002345
Simon Glass82059c22021-04-03 11:05:09 +13002346 fentry = next(fiter)
2347 self.assertEqual(b'U_BOOT', fentry.name)
Simon Glassed836ac2025-02-26 09:26:17 -07002348 self.assertEqual(base, fentry.offset)
Simon Glass82059c22021-04-03 11:05:09 +13002349 self.assertEqual(4, fentry.size)
Simon Glassc64aea52018-09-14 04:57:34 -06002350
Simon Glass82059c22021-04-03 11:05:09 +13002351 fentry = next(fiter)
Simon Glassb1d414c2021-04-03 11:05:10 +13002352 self.assertEqual(b'SECTION', fentry.name)
Simon Glassed836ac2025-02-26 09:26:17 -07002353 self.assertEqual(base + 4, fentry.offset)
Simon Glassb1d414c2021-04-03 11:05:10 +13002354 self.assertEqual(0x20 + expect_size, fentry.size)
2355
2356 fentry = next(fiter)
Simon Glass82059c22021-04-03 11:05:09 +13002357 self.assertEqual(b'INTEL_MRC', fentry.name)
Simon Glassed836ac2025-02-26 09:26:17 -07002358 self.assertEqual(base + 4, fentry.offset)
Simon Glass82059c22021-04-03 11:05:09 +13002359 self.assertEqual(3, fentry.size)
Simon Glassc64aea52018-09-14 04:57:34 -06002360
Simon Glass82059c22021-04-03 11:05:09 +13002361 fentry = next(fiter)
2362 self.assertEqual(b'FMAP', fentry.name)
Simon Glassed836ac2025-02-26 09:26:17 -07002363 self.assertEqual(base + 36, fentry.offset)
Simon Glass82059c22021-04-03 11:05:09 +13002364 self.assertEqual(expect_size, fentry.size)
Simon Glassc64aea52018-09-14 04:57:34 -06002365
Simon Glassb1714232018-09-14 04:57:35 -06002366 def testElf(self):
2367 """Basic test of ELF entries"""
Simon Glass7057d022018-10-01 21:12:47 -06002368 self._SetupSplElf()
Simon Glass3eb5b202019-08-24 07:23:00 -06002369 self._SetupTplElf()
Simon Glassf6290892019-08-24 07:22:53 -06002370 with open(self.ElfTestFile('bss_data'), 'rb') as fd:
Simon Glassb1714232018-09-14 04:57:35 -06002371 TestFunctional._MakeInputFile('-boot', fd.read())
Simon Glass511f6582018-10-01 12:22:30 -06002372 data = self._DoReadFile('096_elf.dts')
Simon Glassb1714232018-09-14 04:57:35 -06002373
Simon Glass0d673792019-07-08 13:18:25 -06002374 def testElfStrip(self):
Simon Glassb1714232018-09-14 04:57:35 -06002375 """Basic test of ELF entries"""
Simon Glass7057d022018-10-01 21:12:47 -06002376 self._SetupSplElf()
Simon Glassf6290892019-08-24 07:22:53 -06002377 with open(self.ElfTestFile('bss_data'), 'rb') as fd:
Simon Glassb1714232018-09-14 04:57:35 -06002378 TestFunctional._MakeInputFile('-boot', fd.read())
Simon Glass511f6582018-10-01 12:22:30 -06002379 data = self._DoReadFile('097_elf_strip.dts')
Simon Glassb1714232018-09-14 04:57:35 -06002380
Simon Glasscd817d52018-09-14 04:57:36 -06002381 def testPackOverlapMap(self):
2382 """Test that overlapping regions are detected"""
Simon Glass14d64e32025-04-29 07:21:59 -06002383 with terminal.capture() as (stdout, stderr):
Simon Glasscd817d52018-09-14 04:57:36 -06002384 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002385 self._DoTestFile('014_pack_overlap.dts', map=True)
Simon Glass80025522022-01-29 14:14:04 -07002386 map_fname = tools.get_output_filename('image.map')
Simon Glasscd817d52018-09-14 04:57:36 -06002387 self.assertEqual("Wrote map file '%s' to show errors\n" % map_fname,
2388 stdout.getvalue())
2389
2390 # We should not get an inmage, but there should be a map file
Simon Glass80025522022-01-29 14:14:04 -07002391 self.assertFalse(os.path.exists(tools.get_output_filename('image.bin')))
Simon Glasscd817d52018-09-14 04:57:36 -06002392 self.assertTrue(os.path.exists(map_fname))
Simon Glass80025522022-01-29 14:14:04 -07002393 map_data = tools.read_file(map_fname, binary=False)
Simon Glasscd817d52018-09-14 04:57:36 -06002394 self.assertEqual('''ImagePos Offset Size Name
Simon Glass49cd2b32023-02-07 14:34:18 -07002395<none> 00000000 00000008 image
Simon Glasscd817d52018-09-14 04:57:36 -06002396<none> 00000000 00000004 u-boot
2397<none> 00000003 00000004 u-boot-align
2398''', map_data)
2399
Simon Glass0d673792019-07-08 13:18:25 -06002400 def testPackRefCode(self):
Simon Glass41902e42018-10-01 12:22:31 -06002401 """Test that an image with an Intel Reference code binary works"""
2402 data = self._DoReadFile('100_intel_refcode.dts')
2403 self.assertEqual(REFCODE_DATA, data[:len(REFCODE_DATA)])
2404
Simon Glasseb023b32019-04-25 21:58:39 -06002405 def testSectionOffset(self):
2406 """Tests use of a section with an offset"""
2407 data, _, map_data, _ = self._DoReadFileDtb('101_sections_offset.dts',
2408 map=True)
2409 self.assertEqual('''ImagePos Offset Size Name
Simon Glass49cd2b32023-02-07 14:34:18 -0700241000000000 00000000 00000038 image
Simon Glasseb023b32019-04-25 21:58:39 -0600241100000004 00000004 00000010 section@0
241200000004 00000000 00000004 u-boot
241300000018 00000018 00000010 section@1
241400000018 00000000 00000004 u-boot
24150000002c 0000002c 00000004 section@2
24160000002c 00000000 00000004 u-boot
2417''', map_data)
2418 self.assertEqual(data,
Simon Glass80025522022-01-29 14:14:04 -07002419 tools.get_bytes(0x26, 4) + U_BOOT_DATA +
2420 tools.get_bytes(0x21, 12) +
2421 tools.get_bytes(0x26, 4) + U_BOOT_DATA +
2422 tools.get_bytes(0x61, 12) +
2423 tools.get_bytes(0x26, 4) + U_BOOT_DATA +
2424 tools.get_bytes(0x26, 8))
Simon Glasseb023b32019-04-25 21:58:39 -06002425
Simon Glass1de34482019-07-08 13:18:53 -06002426 def testCbfsRaw(self):
2427 """Test base handling of a Coreboot Filesystem (CBFS)
2428
2429 The exact contents of the CBFS is verified by similar tests in
2430 cbfs_util_test.py. The tests here merely check that the files added to
2431 the CBFS can be found in the final image.
2432 """
2433 data = self._DoReadFile('102_cbfs_raw.dts')
2434 size = 0xb0
2435
2436 cbfs = cbfs_util.CbfsReader(data)
2437 self.assertEqual(size, cbfs.rom_size)
2438
2439 self.assertIn('u-boot-dtb', cbfs.files)
2440 cfile = cbfs.files['u-boot-dtb']
2441 self.assertEqual(U_BOOT_DTB_DATA, cfile.data)
2442
2443 def testCbfsArch(self):
2444 """Test on non-x86 architecture"""
2445 data = self._DoReadFile('103_cbfs_raw_ppc.dts')
2446 size = 0x100
2447
2448 cbfs = cbfs_util.CbfsReader(data)
2449 self.assertEqual(size, cbfs.rom_size)
2450
2451 self.assertIn('u-boot-dtb', cbfs.files)
2452 cfile = cbfs.files['u-boot-dtb']
2453 self.assertEqual(U_BOOT_DTB_DATA, cfile.data)
2454
2455 def testCbfsStage(self):
2456 """Tests handling of a Coreboot Filesystem (CBFS)"""
2457 if not elf.ELF_TOOLS:
2458 self.skipTest('Python elftools not available')
2459 elf_fname = os.path.join(self._indir, 'cbfs-stage.elf')
2460 elf.MakeElf(elf_fname, U_BOOT_DATA, U_BOOT_DTB_DATA)
2461 size = 0xb0
2462
2463 data = self._DoReadFile('104_cbfs_stage.dts')
2464 cbfs = cbfs_util.CbfsReader(data)
2465 self.assertEqual(size, cbfs.rom_size)
2466
2467 self.assertIn('u-boot', cbfs.files)
2468 cfile = cbfs.files['u-boot']
2469 self.assertEqual(U_BOOT_DATA + U_BOOT_DTB_DATA, cfile.data)
2470
2471 def testCbfsRawCompress(self):
2472 """Test handling of compressing raw files"""
2473 self._CheckLz4()
2474 data = self._DoReadFile('105_cbfs_raw_compress.dts')
2475 size = 0x140
2476
2477 cbfs = cbfs_util.CbfsReader(data)
2478 self.assertIn('u-boot', cbfs.files)
2479 cfile = cbfs.files['u-boot']
2480 self.assertEqual(COMPRESS_DATA, cfile.data)
2481
2482 def testCbfsBadArch(self):
2483 """Test handling of a bad architecture"""
2484 with self.assertRaises(ValueError) as e:
2485 self._DoReadFile('106_cbfs_bad_arch.dts')
2486 self.assertIn("Invalid architecture 'bad-arch'", str(e.exception))
2487
2488 def testCbfsNoSize(self):
2489 """Test handling of a missing size property"""
2490 with self.assertRaises(ValueError) as e:
2491 self._DoReadFile('107_cbfs_no_size.dts')
2492 self.assertIn('entry must have a size property', str(e.exception))
2493
Simon Glass3e28f4f2021-11-23 11:03:54 -07002494 def testCbfsNoContents(self):
Simon Glass1de34482019-07-08 13:18:53 -06002495 """Test handling of a CBFS entry which does not provide contentsy"""
2496 with self.assertRaises(ValueError) as e:
2497 self._DoReadFile('108_cbfs_no_contents.dts')
2498 self.assertIn('Could not complete processing of contents',
2499 str(e.exception))
2500
2501 def testCbfsBadCompress(self):
2502 """Test handling of a bad architecture"""
2503 with self.assertRaises(ValueError) as e:
2504 self._DoReadFile('109_cbfs_bad_compress.dts')
2505 self.assertIn("Invalid compression in 'u-boot': 'invalid-algo'",
2506 str(e.exception))
2507
2508 def testCbfsNamedEntries(self):
2509 """Test handling of named entries"""
2510 data = self._DoReadFile('110_cbfs_name.dts')
2511
2512 cbfs = cbfs_util.CbfsReader(data)
2513 self.assertIn('FRED', cbfs.files)
2514 cfile1 = cbfs.files['FRED']
2515 self.assertEqual(U_BOOT_DATA, cfile1.data)
2516
2517 self.assertIn('hello', cbfs.files)
2518 cfile2 = cbfs.files['hello']
2519 self.assertEqual(U_BOOT_DTB_DATA, cfile2.data)
2520
Simon Glass759af872019-07-08 13:18:54 -06002521 def _SetupIfwi(self, fname):
2522 """Set up to run an IFWI test
2523
2524 Args:
2525 fname: Filename of input file to provide (fitimage.bin or ifwi.bin)
2526 """
2527 self._SetupSplElf()
Simon Glass3eb5b202019-08-24 07:23:00 -06002528 self._SetupTplElf()
Simon Glass759af872019-07-08 13:18:54 -06002529
2530 # Intel Integrated Firmware Image (IFWI) file
2531 with gzip.open(self.TestFile('%s.gz' % fname), 'rb') as fd:
2532 data = fd.read()
2533 TestFunctional._MakeInputFile(fname,data)
2534
2535 def _CheckIfwi(self, data):
2536 """Check that an image with an IFWI contains the correct output
2537
2538 Args:
2539 data: Conents of output file
2540 """
Simon Glass80025522022-01-29 14:14:04 -07002541 expected_desc = tools.read_file(self.TestFile('descriptor.bin'))
Simon Glass759af872019-07-08 13:18:54 -06002542 if data[:0x1000] != expected_desc:
2543 self.fail('Expected descriptor binary at start of image')
2544
2545 # We expect to find the TPL wil in subpart IBBP entry IBBL
Simon Glass80025522022-01-29 14:14:04 -07002546 image_fname = tools.get_output_filename('image.bin')
2547 tpl_fname = tools.get_output_filename('tpl.out')
Simon Glass57c7a482022-01-09 20:14:01 -07002548 ifwitool = bintool.Bintool.create('ifwitool')
2549 ifwitool.extract(image_fname, 'IBBP', 'IBBL', tpl_fname)
Simon Glass759af872019-07-08 13:18:54 -06002550
Simon Glass80025522022-01-29 14:14:04 -07002551 tpl_data = tools.read_file(tpl_fname)
Simon Glassf55bd692019-08-24 07:22:51 -06002552 self.assertEqual(U_BOOT_TPL_DATA, tpl_data[:len(U_BOOT_TPL_DATA)])
Simon Glass759af872019-07-08 13:18:54 -06002553
2554 def testPackX86RomIfwi(self):
2555 """Test that an x86 ROM with Integrated Firmware Image can be created"""
2556 self._SetupIfwi('fitimage.bin')
Simon Glass1d167762019-08-24 07:23:01 -06002557 data = self._DoReadFile('111_x86_rom_ifwi.dts')
Simon Glass759af872019-07-08 13:18:54 -06002558 self._CheckIfwi(data)
2559
2560 def testPackX86RomIfwiNoDesc(self):
2561 """Test that an x86 ROM with IFWI can be created from an ifwi.bin file"""
2562 self._SetupIfwi('ifwi.bin')
Simon Glass1d167762019-08-24 07:23:01 -06002563 data = self._DoReadFile('112_x86_rom_ifwi_nodesc.dts')
Simon Glass759af872019-07-08 13:18:54 -06002564 self._CheckIfwi(data)
2565
2566 def testPackX86RomIfwiNoData(self):
2567 """Test that an x86 ROM with IFWI handles missing data"""
2568 self._SetupIfwi('ifwi.bin')
2569 with self.assertRaises(ValueError) as e:
Simon Glass1d167762019-08-24 07:23:01 -06002570 data = self._DoReadFile('113_x86_rom_ifwi_nodata.dts')
Simon Glass759af872019-07-08 13:18:54 -06002571 self.assertIn('Could not complete processing of contents',
2572 str(e.exception))
Simon Glass91710b32018-07-17 13:25:32 -06002573
Simon Glass66152ce2022-01-09 20:14:09 -07002574 def testIfwiMissing(self):
2575 """Test that binman still produces an image if ifwitool is missing"""
2576 self._SetupIfwi('fitimage.bin')
Simon Glass14d64e32025-04-29 07:21:59 -06002577 with terminal.capture() as (_, stderr):
Simon Glass66152ce2022-01-09 20:14:09 -07002578 self._DoTestFile('111_x86_rom_ifwi.dts',
2579 force_missing_bintools='ifwitool')
2580 err = stderr.getvalue()
2581 self.assertRegex(err,
Simon Glass49cd2b32023-02-07 14:34:18 -07002582 "Image 'image'.*missing bintools.*: ifwitool")
Simon Glass66152ce2022-01-09 20:14:09 -07002583
Simon Glassc2f1aed2019-07-08 13:18:56 -06002584 def testCbfsOffset(self):
2585 """Test a CBFS with files at particular offsets
2586
2587 Like all CFBS tests, this is just checking the logic that calls
2588 cbfs_util. See cbfs_util_test for fully tests (e.g. test_cbfs_offset()).
2589 """
2590 data = self._DoReadFile('114_cbfs_offset.dts')
2591 size = 0x200
2592
2593 cbfs = cbfs_util.CbfsReader(data)
2594 self.assertEqual(size, cbfs.rom_size)
2595
2596 self.assertIn('u-boot', cbfs.files)
2597 cfile = cbfs.files['u-boot']
2598 self.assertEqual(U_BOOT_DATA, cfile.data)
2599 self.assertEqual(0x40, cfile.cbfs_offset)
2600
2601 self.assertIn('u-boot-dtb', cbfs.files)
2602 cfile2 = cbfs.files['u-boot-dtb']
2603 self.assertEqual(U_BOOT_DTB_DATA, cfile2.data)
2604 self.assertEqual(0x140, cfile2.cbfs_offset)
2605
Simon Glass0f621332019-07-08 14:25:27 -06002606 def testFdtmap(self):
2607 """Test an FDT map can be inserted in the image"""
2608 data = self.data = self._DoReadFileRealDtb('115_fdtmap.dts')
2609 fdtmap_data = data[len(U_BOOT_DATA):]
2610 magic = fdtmap_data[:8]
Simon Glassc5fd10a2019-10-31 07:43:03 -06002611 self.assertEqual(b'_FDTMAP_', magic)
Simon Glass80025522022-01-29 14:14:04 -07002612 self.assertEqual(tools.get_bytes(0, 8), fdtmap_data[8:16])
Simon Glass0f621332019-07-08 14:25:27 -06002613
2614 fdt_data = fdtmap_data[16:]
2615 dtb = fdt.Fdt.FromData(fdt_data)
2616 dtb.Scan()
Simon Glass2c6adba2019-07-20 12:23:47 -06002617 props = self._GetPropTree(dtb, BASE_DTB_PROPS, prefix='/')
Simon Glass0f621332019-07-08 14:25:27 -06002618 self.assertEqual({
2619 'image-pos': 0,
2620 'offset': 0,
2621 'u-boot:offset': 0,
2622 'u-boot:size': len(U_BOOT_DATA),
2623 'u-boot:image-pos': 0,
2624 'fdtmap:image-pos': 4,
2625 'fdtmap:offset': 4,
2626 'fdtmap:size': len(fdtmap_data),
2627 'size': len(data),
2628 }, props)
2629
2630 def testFdtmapNoMatch(self):
2631 """Check handling of an FDT map when the section cannot be found"""
2632 self.data = self._DoReadFileRealDtb('115_fdtmap.dts')
2633
2634 # Mangle the section name, which should cause a mismatch between the
2635 # correct FDT path and the one expected by the section
2636 image = control.images['image']
Simon Glasscec34ba2019-07-08 14:25:28 -06002637 image._node.path += '-suffix'
Simon Glass0f621332019-07-08 14:25:27 -06002638 entries = image.GetEntries()
2639 fdtmap = entries['fdtmap']
2640 with self.assertRaises(ValueError) as e:
2641 fdtmap._GetFdtmap()
2642 self.assertIn("Cannot locate node for path '/binman-suffix'",
2643 str(e.exception))
2644
Simon Glasscec34ba2019-07-08 14:25:28 -06002645 def testFdtmapHeader(self):
2646 """Test an FDT map and image header can be inserted in the image"""
2647 data = self.data = self._DoReadFileRealDtb('116_fdtmap_hdr.dts')
2648 fdtmap_pos = len(U_BOOT_DATA)
2649 fdtmap_data = data[fdtmap_pos:]
2650 fdt_data = fdtmap_data[16:]
2651 dtb = fdt.Fdt.FromData(fdt_data)
2652 fdt_size = dtb.GetFdtObj().totalsize()
2653 hdr_data = data[-8:]
Simon Glassc5fd10a2019-10-31 07:43:03 -06002654 self.assertEqual(b'BinM', hdr_data[:4])
Simon Glasscec34ba2019-07-08 14:25:28 -06002655 offset = struct.unpack('<I', hdr_data[4:])[0] & 0xffffffff
2656 self.assertEqual(fdtmap_pos - 0x400, offset - (1 << 32))
2657
2658 def testFdtmapHeaderStart(self):
2659 """Test an image header can be inserted at the image start"""
2660 data = self.data = self._DoReadFileRealDtb('117_fdtmap_hdr_start.dts')
2661 fdtmap_pos = 0x100 + len(U_BOOT_DATA)
2662 hdr_data = data[:8]
Simon Glassc5fd10a2019-10-31 07:43:03 -06002663 self.assertEqual(b'BinM', hdr_data[:4])
Simon Glasscec34ba2019-07-08 14:25:28 -06002664 offset = struct.unpack('<I', hdr_data[4:])[0]
2665 self.assertEqual(fdtmap_pos, offset)
2666
2667 def testFdtmapHeaderPos(self):
2668 """Test an image header can be inserted at a chosen position"""
2669 data = self.data = self._DoReadFileRealDtb('118_fdtmap_hdr_pos.dts')
2670 fdtmap_pos = 0x100 + len(U_BOOT_DATA)
2671 hdr_data = data[0x80:0x88]
Simon Glassc5fd10a2019-10-31 07:43:03 -06002672 self.assertEqual(b'BinM', hdr_data[:4])
Simon Glasscec34ba2019-07-08 14:25:28 -06002673 offset = struct.unpack('<I', hdr_data[4:])[0]
2674 self.assertEqual(fdtmap_pos, offset)
2675
2676 def testHeaderMissingFdtmap(self):
2677 """Test an image header requires an fdtmap"""
2678 with self.assertRaises(ValueError) as e:
2679 self.data = self._DoReadFileRealDtb('119_fdtmap_hdr_missing.dts')
2680 self.assertIn("'image_header' section must have an 'fdtmap' sibling",
2681 str(e.exception))
2682
2683 def testHeaderNoLocation(self):
2684 """Test an image header with a no specified location is detected"""
2685 with self.assertRaises(ValueError) as e:
2686 self.data = self._DoReadFileRealDtb('120_hdr_no_location.dts')
2687 self.assertIn("Invalid location 'None', expected 'start' or 'end'",
2688 str(e.exception))
2689
Simon Glasse61b6f62019-07-08 14:25:37 -06002690 def testEntryExpand(self):
Simon Glassdd156a42022-03-05 20:18:59 -07002691 """Test extending an entry after it is packed"""
2692 data = self._DoReadFile('121_entry_extend.dts')
Simon Glass8c702fb2019-07-20 12:23:57 -06002693 self.assertEqual(b'aaa', data[:3])
2694 self.assertEqual(U_BOOT_DATA, data[3:3 + len(U_BOOT_DATA)])
2695 self.assertEqual(b'aaa', data[-3:])
Simon Glasse61b6f62019-07-08 14:25:37 -06002696
Simon Glassdd156a42022-03-05 20:18:59 -07002697 def testEntryExtendBad(self):
2698 """Test extending an entry after it is packed, twice"""
Simon Glasse61b6f62019-07-08 14:25:37 -06002699 with self.assertRaises(ValueError) as e:
Simon Glassdd156a42022-03-05 20:18:59 -07002700 self._DoReadFile('122_entry_extend_twice.dts')
Simon Glass9d8ee322019-07-20 12:23:58 -06002701 self.assertIn("Image '/binman': Entries changed size after packing",
Simon Glasse61b6f62019-07-08 14:25:37 -06002702 str(e.exception))
2703
Simon Glassdd156a42022-03-05 20:18:59 -07002704 def testEntryExtendSection(self):
2705 """Test extending an entry within a section after it is packed"""
2706 data = self._DoReadFile('123_entry_extend_section.dts')
Simon Glass8c702fb2019-07-20 12:23:57 -06002707 self.assertEqual(b'aaa', data[:3])
2708 self.assertEqual(U_BOOT_DATA, data[3:3 + len(U_BOOT_DATA)])
2709 self.assertEqual(b'aaa', data[-3:])
Simon Glasse61b6f62019-07-08 14:25:37 -06002710
Simon Glass90d29682019-07-08 14:25:38 -06002711 def testCompressDtb(self):
2712 """Test that compress of device-tree files is supported"""
2713 self._CheckLz4()
2714 data = self.data = self._DoReadFileRealDtb('124_compress_dtb.dts')
2715 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
2716 comp_data = data[len(U_BOOT_DATA):]
2717 orig = self._decompress(comp_data)
2718 dtb = fdt.Fdt.FromData(orig)
2719 dtb.Scan()
2720 props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
2721 expected = {
2722 'u-boot:size': len(U_BOOT_DATA),
2723 'u-boot-dtb:uncomp-size': len(orig),
2724 'u-boot-dtb:size': len(comp_data),
2725 'size': len(data),
2726 }
2727 self.assertEqual(expected, props)
2728
Simon Glass151bbbf2019-07-08 14:25:41 -06002729 def testCbfsUpdateFdt(self):
2730 """Test that we can update the device tree with CBFS offset/size info"""
2731 self._CheckLz4()
2732 data, _, _, out_dtb_fname = self._DoReadFileDtb('125_cbfs_update.dts',
2733 update_dtb=True)
2734 dtb = fdt.Fdt(out_dtb_fname)
2735 dtb.Scan()
Simon Glass2c6adba2019-07-20 12:23:47 -06002736 props = self._GetPropTree(dtb, BASE_DTB_PROPS + ['uncomp-size'])
Simon Glass151bbbf2019-07-08 14:25:41 -06002737 del props['cbfs/u-boot:size']
2738 self.assertEqual({
2739 'offset': 0,
2740 'size': len(data),
2741 'image-pos': 0,
2742 'cbfs:offset': 0,
2743 'cbfs:size': len(data),
2744 'cbfs:image-pos': 0,
Simon Glassfa144222023-10-14 14:40:28 -06002745 'cbfs/u-boot:offset': 0x30,
Simon Glass151bbbf2019-07-08 14:25:41 -06002746 'cbfs/u-boot:uncomp-size': len(U_BOOT_DATA),
Simon Glassfa144222023-10-14 14:40:28 -06002747 'cbfs/u-boot:image-pos': 0x30,
2748 'cbfs/u-boot-dtb:offset': 0xa4,
Simon Glass151bbbf2019-07-08 14:25:41 -06002749 'cbfs/u-boot-dtb:size': len(U_BOOT_DATA),
Simon Glassfa144222023-10-14 14:40:28 -06002750 'cbfs/u-boot-dtb:image-pos': 0xa4,
Simon Glass151bbbf2019-07-08 14:25:41 -06002751 }, props)
2752
Simon Glass3c9b4f22019-07-08 14:25:42 -06002753 def testCbfsBadType(self):
2754 """Test an image header with a no specified location is detected"""
2755 with self.assertRaises(ValueError) as e:
2756 self._DoReadFile('126_cbfs_bad_type.dts')
2757 self.assertIn("Unknown cbfs-type 'badtype'", str(e.exception))
2758
Simon Glass6b156f82019-07-08 14:25:43 -06002759 def testList(self):
2760 """Test listing the files in an image"""
2761 self._CheckLz4()
2762 data = self._DoReadFile('127_list.dts')
2763 image = control.images['image']
2764 entries = image.BuildEntryList()
2765 self.assertEqual(7, len(entries))
2766
2767 ent = entries[0]
2768 self.assertEqual(0, ent.indent)
Simon Glass49cd2b32023-02-07 14:34:18 -07002769 self.assertEqual('image', ent.name)
Simon Glass6b156f82019-07-08 14:25:43 -06002770 self.assertEqual('section', ent.etype)
2771 self.assertEqual(len(data), ent.size)
2772 self.assertEqual(0, ent.image_pos)
2773 self.assertEqual(None, ent.uncomp_size)
Simon Glass39dd2152019-07-08 14:25:47 -06002774 self.assertEqual(0, ent.offset)
Simon Glass6b156f82019-07-08 14:25:43 -06002775
2776 ent = entries[1]
2777 self.assertEqual(1, ent.indent)
2778 self.assertEqual('u-boot', ent.name)
2779 self.assertEqual('u-boot', ent.etype)
2780 self.assertEqual(len(U_BOOT_DATA), ent.size)
2781 self.assertEqual(0, ent.image_pos)
2782 self.assertEqual(None, ent.uncomp_size)
2783 self.assertEqual(0, ent.offset)
2784
2785 ent = entries[2]
2786 self.assertEqual(1, ent.indent)
2787 self.assertEqual('section', ent.name)
2788 self.assertEqual('section', ent.etype)
2789 section_size = ent.size
2790 self.assertEqual(0x100, ent.image_pos)
2791 self.assertEqual(None, ent.uncomp_size)
Simon Glass39dd2152019-07-08 14:25:47 -06002792 self.assertEqual(0x100, ent.offset)
Simon Glass6b156f82019-07-08 14:25:43 -06002793
2794 ent = entries[3]
2795 self.assertEqual(2, ent.indent)
2796 self.assertEqual('cbfs', ent.name)
2797 self.assertEqual('cbfs', ent.etype)
2798 self.assertEqual(0x400, ent.size)
2799 self.assertEqual(0x100, ent.image_pos)
2800 self.assertEqual(None, ent.uncomp_size)
2801 self.assertEqual(0, ent.offset)
2802
2803 ent = entries[4]
2804 self.assertEqual(3, ent.indent)
2805 self.assertEqual('u-boot', ent.name)
2806 self.assertEqual('u-boot', ent.etype)
2807 self.assertEqual(len(U_BOOT_DATA), ent.size)
2808 self.assertEqual(0x138, ent.image_pos)
2809 self.assertEqual(None, ent.uncomp_size)
2810 self.assertEqual(0x38, ent.offset)
2811
2812 ent = entries[5]
2813 self.assertEqual(3, ent.indent)
2814 self.assertEqual('u-boot-dtb', ent.name)
2815 self.assertEqual('text', ent.etype)
2816 self.assertGreater(len(COMPRESS_DATA), ent.size)
2817 self.assertEqual(0x178, ent.image_pos)
2818 self.assertEqual(len(COMPRESS_DATA), ent.uncomp_size)
2819 self.assertEqual(0x78, ent.offset)
2820
2821 ent = entries[6]
2822 self.assertEqual(2, ent.indent)
2823 self.assertEqual('u-boot-dtb', ent.name)
2824 self.assertEqual('u-boot-dtb', ent.etype)
2825 self.assertEqual(0x500, ent.image_pos)
2826 self.assertEqual(len(U_BOOT_DTB_DATA), ent.uncomp_size)
2827 dtb_size = ent.size
2828 # Compressing this data expands it since headers are added
2829 self.assertGreater(dtb_size, len(U_BOOT_DTB_DATA))
2830 self.assertEqual(0x400, ent.offset)
2831
2832 self.assertEqual(len(data), 0x100 + section_size)
2833 self.assertEqual(section_size, 0x400 + dtb_size)
2834
Simon Glass8d8bf4e2019-07-08 14:25:44 -06002835 def testFindFdtmap(self):
2836 """Test locating an FDT map in an image"""
2837 self._CheckLz4()
2838 data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
2839 image = control.images['image']
2840 entries = image.GetEntries()
2841 entry = entries['fdtmap']
2842 self.assertEqual(entry.image_pos, fdtmap.LocateFdtmap(data))
2843
2844 def testFindFdtmapMissing(self):
2845 """Test failing to locate an FDP map"""
2846 data = self._DoReadFile('005_simple.dts')
2847 self.assertEqual(None, fdtmap.LocateFdtmap(data))
2848
Simon Glassed39a3c2019-07-08 14:25:45 -06002849 def testFindImageHeader(self):
2850 """Test locating a image header"""
2851 self._CheckLz4()
Simon Glassb8424fa2019-07-08 14:25:46 -06002852 data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
Simon Glassed39a3c2019-07-08 14:25:45 -06002853 image = control.images['image']
2854 entries = image.GetEntries()
2855 entry = entries['fdtmap']
2856 # The header should point to the FDT map
2857 self.assertEqual(entry.image_pos, image_header.LocateHeaderOffset(data))
2858
2859 def testFindImageHeaderStart(self):
2860 """Test locating a image header located at the start of an image"""
Simon Glassb8424fa2019-07-08 14:25:46 -06002861 data = self.data = self._DoReadFileRealDtb('117_fdtmap_hdr_start.dts')
Simon Glassed39a3c2019-07-08 14:25:45 -06002862 image = control.images['image']
2863 entries = image.GetEntries()
2864 entry = entries['fdtmap']
2865 # The header should point to the FDT map
2866 self.assertEqual(entry.image_pos, image_header.LocateHeaderOffset(data))
2867
2868 def testFindImageHeaderMissing(self):
2869 """Test failing to locate an image header"""
2870 data = self._DoReadFile('005_simple.dts')
2871 self.assertEqual(None, image_header.LocateHeaderOffset(data))
2872
Simon Glassb8424fa2019-07-08 14:25:46 -06002873 def testReadImage(self):
2874 """Test reading an image and accessing its FDT map"""
2875 self._CheckLz4()
2876 data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
Simon Glass80025522022-01-29 14:14:04 -07002877 image_fname = tools.get_output_filename('image.bin')
Simon Glassb8424fa2019-07-08 14:25:46 -06002878 orig_image = control.images['image']
2879 image = Image.FromFile(image_fname)
2880 self.assertEqual(orig_image.GetEntries().keys(),
2881 image.GetEntries().keys())
2882
2883 orig_entry = orig_image.GetEntries()['fdtmap']
2884 entry = image.GetEntries()['fdtmap']
Brandon Maiera657bc62024-06-04 16:16:05 +00002885 self.assertEqual(orig_entry.offset, entry.offset)
2886 self.assertEqual(orig_entry.size, entry.size)
2887 self.assertEqual(orig_entry.image_pos, entry.image_pos)
Simon Glassb8424fa2019-07-08 14:25:46 -06002888
2889 def testReadImageNoHeader(self):
2890 """Test accessing an image's FDT map without an image header"""
2891 self._CheckLz4()
2892 data = self._DoReadFileRealDtb('129_decode_image_nohdr.dts')
Simon Glass80025522022-01-29 14:14:04 -07002893 image_fname = tools.get_output_filename('image.bin')
Simon Glassb8424fa2019-07-08 14:25:46 -06002894 image = Image.FromFile(image_fname)
2895 self.assertTrue(isinstance(image, Image))
Simon Glass072959a2019-07-20 12:23:50 -06002896 self.assertEqual('image', image.image_name[-5:])
Simon Glassb8424fa2019-07-08 14:25:46 -06002897
2898 def testReadImageFail(self):
2899 """Test failing to read an image image's FDT map"""
2900 self._DoReadFile('005_simple.dts')
Simon Glass80025522022-01-29 14:14:04 -07002901 image_fname = tools.get_output_filename('image.bin')
Simon Glassb8424fa2019-07-08 14:25:46 -06002902 with self.assertRaises(ValueError) as e:
2903 image = Image.FromFile(image_fname)
2904 self.assertIn("Cannot find FDT map in image", str(e.exception))
Simon Glassc2f1aed2019-07-08 13:18:56 -06002905
Simon Glassb2fd11d2019-07-08 14:25:48 -06002906 def testListCmd(self):
2907 """Test listing the files in an image using an Fdtmap"""
2908 self._CheckLz4()
2909 data = self._DoReadFileRealDtb('130_list_fdtmap.dts')
2910
2911 # lz4 compression size differs depending on the version
2912 image = control.images['image']
2913 entries = image.GetEntries()
2914 section_size = entries['section'].size
2915 fdt_size = entries['section'].GetEntries()['u-boot-dtb'].size
2916 fdtmap_offset = entries['fdtmap'].offset
2917
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01002918 tmpdir = None
Simon Glassb3d6fc72019-07-20 12:24:10 -06002919 try:
2920 tmpdir, updated_fname = self._SetupImageInTmpdir()
Simon Glass14d64e32025-04-29 07:21:59 -06002921 with terminal.capture() as (stdout, stderr):
Simon Glassb3d6fc72019-07-20 12:24:10 -06002922 self._DoBinman('ls', '-i', updated_fname)
2923 finally:
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01002924 if tmpdir:
2925 shutil.rmtree(tmpdir)
Simon Glassb2fd11d2019-07-08 14:25:48 -06002926 lines = stdout.getvalue().splitlines()
2927 expected = [
2928'Name Image-pos Size Entry-type Offset Uncomp-size',
2929'----------------------------------------------------------------------',
Simon Glass49cd2b32023-02-07 14:34:18 -07002930'image 0 c00 section 0',
Simon Glassb2fd11d2019-07-08 14:25:48 -06002931' u-boot 0 4 u-boot 0',
2932' section 100 %x section 100' % section_size,
2933' cbfs 100 400 cbfs 0',
Simon Glassfa144222023-10-14 14:40:28 -06002934' u-boot 120 4 u-boot 20',
Simon Glassc5fd10a2019-10-31 07:43:03 -06002935' u-boot-dtb 180 105 u-boot-dtb 80 3c9',
Simon Glassb2fd11d2019-07-08 14:25:48 -06002936' u-boot-dtb 500 %x u-boot-dtb 400 3c9' % fdt_size,
Simon Glassc5fd10a2019-10-31 07:43:03 -06002937' fdtmap %x 3bd fdtmap %x' %
Simon Glassb2fd11d2019-07-08 14:25:48 -06002938 (fdtmap_offset, fdtmap_offset),
2939' image-header bf8 8 image-header bf8',
2940 ]
2941 self.assertEqual(expected, lines)
2942
2943 def testListCmdFail(self):
2944 """Test failing to list an image"""
2945 self._DoReadFile('005_simple.dts')
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01002946 tmpdir = None
Simon Glassb3d6fc72019-07-20 12:24:10 -06002947 try:
2948 tmpdir, updated_fname = self._SetupImageInTmpdir()
2949 with self.assertRaises(ValueError) as e:
2950 self._DoBinman('ls', '-i', updated_fname)
2951 finally:
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01002952 if tmpdir:
2953 shutil.rmtree(tmpdir)
Simon Glassb2fd11d2019-07-08 14:25:48 -06002954 self.assertIn("Cannot find FDT map in image", str(e.exception))
2955
2956 def _RunListCmd(self, paths, expected):
2957 """List out entries and check the result
2958
2959 Args:
2960 paths: List of paths to pass to the list command
2961 expected: Expected list of filenames to be returned, in order
2962 """
2963 self._CheckLz4()
2964 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07002965 image_fname = tools.get_output_filename('image.bin')
Simon Glassb2fd11d2019-07-08 14:25:48 -06002966 image = Image.FromFile(image_fname)
2967 lines = image.GetListEntries(paths)[1]
2968 files = [line[0].strip() for line in lines[1:]]
2969 self.assertEqual(expected, files)
2970
2971 def testListCmdSection(self):
2972 """Test listing the files in a section"""
2973 self._RunListCmd(['section'],
2974 ['section', 'cbfs', 'u-boot', 'u-boot-dtb', 'u-boot-dtb'])
2975
2976 def testListCmdFile(self):
2977 """Test listing a particular file"""
2978 self._RunListCmd(['*u-boot-dtb'], ['u-boot-dtb', 'u-boot-dtb'])
2979
2980 def testListCmdWildcard(self):
2981 """Test listing a wildcarded file"""
2982 self._RunListCmd(['*boot*'],
2983 ['u-boot', 'u-boot', 'u-boot-dtb', 'u-boot-dtb'])
2984
2985 def testListCmdWildcardMulti(self):
2986 """Test listing a wildcarded file"""
2987 self._RunListCmd(['*cb*', '*head*'],
2988 ['cbfs', 'u-boot', 'u-boot-dtb', 'image-header'])
2989
2990 def testListCmdEmpty(self):
2991 """Test listing a wildcarded file"""
2992 self._RunListCmd(['nothing'], [])
2993
2994 def testListCmdPath(self):
2995 """Test listing the files in a sub-entry of a section"""
2996 self._RunListCmd(['section/cbfs'], ['cbfs', 'u-boot', 'u-boot-dtb'])
2997
Simon Glass4c613bf2019-07-08 14:25:50 -06002998 def _RunExtractCmd(self, entry_name, decomp=True):
2999 """Extract an entry from an image
3000
3001 Args:
3002 entry_name: Entry name to extract
3003 decomp: True to decompress the data if compressed, False to leave
3004 it in its raw uncompressed format
3005
3006 Returns:
3007 data from entry
3008 """
3009 self._CheckLz4()
3010 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07003011 image_fname = tools.get_output_filename('image.bin')
Simon Glass4c613bf2019-07-08 14:25:50 -06003012 return control.ReadEntry(image_fname, entry_name, decomp)
3013
3014 def testExtractSimple(self):
3015 """Test extracting a single file"""
3016 data = self._RunExtractCmd('u-boot')
3017 self.assertEqual(U_BOOT_DATA, data)
3018
Simon Glass980a2842019-07-08 14:25:52 -06003019 def testExtractSection(self):
3020 """Test extracting the files in a section"""
3021 data = self._RunExtractCmd('section')
3022 cbfs_data = data[:0x400]
3023 cbfs = cbfs_util.CbfsReader(cbfs_data)
Simon Glassc5fd10a2019-10-31 07:43:03 -06003024 self.assertEqual(['u-boot', 'u-boot-dtb', ''], list(cbfs.files.keys()))
Simon Glass980a2842019-07-08 14:25:52 -06003025 dtb_data = data[0x400:]
3026 dtb = self._decompress(dtb_data)
3027 self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
3028
3029 def testExtractCompressed(self):
3030 """Test extracting compressed data"""
3031 data = self._RunExtractCmd('section/u-boot-dtb')
3032 self.assertEqual(EXTRACT_DTB_SIZE, len(data))
3033
3034 def testExtractRaw(self):
3035 """Test extracting compressed data without decompressing it"""
3036 data = self._RunExtractCmd('section/u-boot-dtb', decomp=False)
3037 dtb = self._decompress(data)
3038 self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
3039
3040 def testExtractCbfs(self):
3041 """Test extracting CBFS data"""
3042 data = self._RunExtractCmd('section/cbfs/u-boot')
3043 self.assertEqual(U_BOOT_DATA, data)
3044
3045 def testExtractCbfsCompressed(self):
3046 """Test extracting CBFS compressed data"""
3047 data = self._RunExtractCmd('section/cbfs/u-boot-dtb')
3048 self.assertEqual(EXTRACT_DTB_SIZE, len(data))
3049
3050 def testExtractCbfsRaw(self):
3051 """Test extracting CBFS compressed data without decompressing it"""
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +02003052 bintool = self.comp_bintools['lzma_alone']
3053 self._CheckBintool(bintool)
Simon Glass980a2842019-07-08 14:25:52 -06003054 data = self._RunExtractCmd('section/cbfs/u-boot-dtb', decomp=False)
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +02003055 dtb = bintool.decompress(data)
Simon Glass980a2842019-07-08 14:25:52 -06003056 self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
3057
Simon Glass4c613bf2019-07-08 14:25:50 -06003058 def testExtractBadEntry(self):
3059 """Test extracting a bad section path"""
3060 with self.assertRaises(ValueError) as e:
3061 self._RunExtractCmd('section/does-not-exist')
3062 self.assertIn("Entry 'does-not-exist' not found in '/section'",
3063 str(e.exception))
3064
3065 def testExtractMissingFile(self):
3066 """Test extracting file that does not exist"""
3067 with self.assertRaises(IOError) as e:
3068 control.ReadEntry('missing-file', 'name')
3069
3070 def testExtractBadFile(self):
3071 """Test extracting an invalid file"""
3072 fname = os.path.join(self._indir, 'badfile')
Simon Glass80025522022-01-29 14:14:04 -07003073 tools.write_file(fname, b'')
Simon Glass4c613bf2019-07-08 14:25:50 -06003074 with self.assertRaises(ValueError) as e:
3075 control.ReadEntry(fname, 'name')
3076
Simon Glass980a2842019-07-08 14:25:52 -06003077 def testExtractCmd(self):
3078 """Test extracting a file fron an image on the command line"""
3079 self._CheckLz4()
3080 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass980a2842019-07-08 14:25:52 -06003081 fname = os.path.join(self._indir, 'output.extact')
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01003082 tmpdir = None
Simon Glassb3d6fc72019-07-20 12:24:10 -06003083 try:
3084 tmpdir, updated_fname = self._SetupImageInTmpdir()
Simon Glass14d64e32025-04-29 07:21:59 -06003085 with terminal.capture() as (stdout, stderr):
Simon Glassb3d6fc72019-07-20 12:24:10 -06003086 self._DoBinman('extract', '-i', updated_fname, 'u-boot',
3087 '-f', fname)
3088 finally:
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01003089 if tmpdir:
3090 shutil.rmtree(tmpdir)
Simon Glass80025522022-01-29 14:14:04 -07003091 data = tools.read_file(fname)
Simon Glass980a2842019-07-08 14:25:52 -06003092 self.assertEqual(U_BOOT_DATA, data)
3093
3094 def testExtractOneEntry(self):
3095 """Test extracting a single entry fron an image """
3096 self._CheckLz4()
3097 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07003098 image_fname = tools.get_output_filename('image.bin')
Simon Glass980a2842019-07-08 14:25:52 -06003099 fname = os.path.join(self._indir, 'output.extact')
3100 control.ExtractEntries(image_fname, fname, None, ['u-boot'])
Simon Glass80025522022-01-29 14:14:04 -07003101 data = tools.read_file(fname)
Simon Glass980a2842019-07-08 14:25:52 -06003102 self.assertEqual(U_BOOT_DATA, data)
3103
3104 def _CheckExtractOutput(self, decomp):
3105 """Helper to test file output with and without decompression
3106
3107 Args:
3108 decomp: True to decompress entry data, False to output it raw
3109 """
3110 def _CheckPresent(entry_path, expect_data, expect_size=None):
3111 """Check and remove expected file
3112
3113 This checks the data/size of a file and removes the file both from
3114 the outfiles set and from the output directory. Once all files are
3115 processed, both the set and directory should be empty.
3116
3117 Args:
3118 entry_path: Entry path
3119 expect_data: Data to expect in file, or None to skip check
3120 expect_size: Size of data to expect in file, or None to skip
3121 """
3122 path = os.path.join(outdir, entry_path)
Simon Glass80025522022-01-29 14:14:04 -07003123 data = tools.read_file(path)
Simon Glass980a2842019-07-08 14:25:52 -06003124 os.remove(path)
3125 if expect_data:
3126 self.assertEqual(expect_data, data)
3127 elif expect_size:
3128 self.assertEqual(expect_size, len(data))
3129 outfiles.remove(path)
3130
3131 def _CheckDirPresent(name):
3132 """Remove expected directory
3133
3134 This gives an error if the directory does not exist as expected
3135
3136 Args:
3137 name: Name of directory to remove
3138 """
3139 path = os.path.join(outdir, name)
3140 os.rmdir(path)
3141
3142 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07003143 image_fname = tools.get_output_filename('image.bin')
Simon Glass980a2842019-07-08 14:25:52 -06003144 outdir = os.path.join(self._indir, 'extract')
3145 einfos = control.ExtractEntries(image_fname, None, outdir, [], decomp)
3146
3147 # Create a set of all file that were output (should be 9)
3148 outfiles = set()
3149 for root, dirs, files in os.walk(outdir):
3150 outfiles |= set([os.path.join(root, fname) for fname in files])
3151 self.assertEqual(9, len(outfiles))
3152 self.assertEqual(9, len(einfos))
3153
3154 image = control.images['image']
3155 entries = image.GetEntries()
3156
3157 # Check the 9 files in various ways
3158 section = entries['section']
3159 section_entries = section.GetEntries()
3160 cbfs_entries = section_entries['cbfs'].GetEntries()
3161 _CheckPresent('u-boot', U_BOOT_DATA)
3162 _CheckPresent('section/cbfs/u-boot', U_BOOT_DATA)
3163 dtb_len = EXTRACT_DTB_SIZE
3164 if not decomp:
3165 dtb_len = cbfs_entries['u-boot-dtb'].size
3166 _CheckPresent('section/cbfs/u-boot-dtb', None, dtb_len)
3167 if not decomp:
3168 dtb_len = section_entries['u-boot-dtb'].size
3169 _CheckPresent('section/u-boot-dtb', None, dtb_len)
3170
3171 fdtmap = entries['fdtmap']
3172 _CheckPresent('fdtmap', fdtmap.data)
3173 hdr = entries['image-header']
3174 _CheckPresent('image-header', hdr.data)
3175
3176 _CheckPresent('section/root', section.data)
3177 cbfs = section_entries['cbfs']
3178 _CheckPresent('section/cbfs/root', cbfs.data)
Simon Glass80025522022-01-29 14:14:04 -07003179 data = tools.read_file(image_fname)
Simon Glass980a2842019-07-08 14:25:52 -06003180 _CheckPresent('root', data)
3181
3182 # There should be no files left. Remove all the directories to check.
3183 # If there are any files/dirs remaining, one of these checks will fail.
3184 self.assertEqual(0, len(outfiles))
3185 _CheckDirPresent('section/cbfs')
3186 _CheckDirPresent('section')
3187 _CheckDirPresent('')
3188 self.assertFalse(os.path.exists(outdir))
3189
3190 def testExtractAllEntries(self):
3191 """Test extracting all entries"""
3192 self._CheckLz4()
3193 self._CheckExtractOutput(decomp=True)
3194
3195 def testExtractAllEntriesRaw(self):
3196 """Test extracting all entries without decompressing them"""
3197 self._CheckLz4()
3198 self._CheckExtractOutput(decomp=False)
3199
3200 def testExtractSelectedEntries(self):
3201 """Test extracting some entries"""
3202 self._CheckLz4()
3203 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07003204 image_fname = tools.get_output_filename('image.bin')
Simon Glass980a2842019-07-08 14:25:52 -06003205 outdir = os.path.join(self._indir, 'extract')
3206 einfos = control.ExtractEntries(image_fname, None, outdir,
3207 ['*cb*', '*head*'])
3208
3209 # File output is tested by testExtractAllEntries(), so just check that
3210 # the expected entries are selected
3211 names = [einfo.name for einfo in einfos]
3212 self.assertEqual(names,
3213 ['cbfs', 'u-boot', 'u-boot-dtb', 'image-header'])
3214
3215 def testExtractNoEntryPaths(self):
3216 """Test extracting some entries"""
3217 self._CheckLz4()
3218 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07003219 image_fname = tools.get_output_filename('image.bin')
Simon Glass980a2842019-07-08 14:25:52 -06003220 with self.assertRaises(ValueError) as e:
3221 control.ExtractEntries(image_fname, 'fname', None, [])
Simon Glassa772d3f2019-07-20 12:24:14 -06003222 self.assertIn('Must specify an entry path to write with -f',
Simon Glass980a2842019-07-08 14:25:52 -06003223 str(e.exception))
3224
3225 def testExtractTooManyEntryPaths(self):
3226 """Test extracting some entries"""
3227 self._CheckLz4()
3228 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07003229 image_fname = tools.get_output_filename('image.bin')
Simon Glass980a2842019-07-08 14:25:52 -06003230 with self.assertRaises(ValueError) as e:
3231 control.ExtractEntries(image_fname, 'fname', None, ['a', 'b'])
Simon Glassa772d3f2019-07-20 12:24:14 -06003232 self.assertIn('Must specify exactly one entry path to write with -f',
Simon Glass980a2842019-07-08 14:25:52 -06003233 str(e.exception))
3234
Simon Glass52d06212019-07-08 14:25:53 -06003235 def testPackAlignSection(self):
3236 """Test that sections can have alignment"""
3237 self._DoReadFile('131_pack_align_section.dts')
3238
3239 self.assertIn('image', control.images)
3240 image = control.images['image']
3241 entries = image.GetEntries()
3242 self.assertEqual(3, len(entries))
3243
3244 # First u-boot
3245 self.assertIn('u-boot', entries)
3246 entry = entries['u-boot']
3247 self.assertEqual(0, entry.offset)
3248 self.assertEqual(0, entry.image_pos)
3249 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3250 self.assertEqual(len(U_BOOT_DATA), entry.size)
3251
3252 # Section0
3253 self.assertIn('section0', entries)
3254 section0 = entries['section0']
3255 self.assertEqual(0x10, section0.offset)
3256 self.assertEqual(0x10, section0.image_pos)
3257 self.assertEqual(len(U_BOOT_DATA), section0.size)
3258
3259 # Second u-boot
3260 section_entries = section0.GetEntries()
3261 self.assertIn('u-boot', section_entries)
3262 entry = section_entries['u-boot']
3263 self.assertEqual(0, entry.offset)
3264 self.assertEqual(0x10, entry.image_pos)
3265 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3266 self.assertEqual(len(U_BOOT_DATA), entry.size)
3267
3268 # Section1
3269 self.assertIn('section1', entries)
3270 section1 = entries['section1']
3271 self.assertEqual(0x14, section1.offset)
3272 self.assertEqual(0x14, section1.image_pos)
3273 self.assertEqual(0x20, section1.size)
3274
3275 # Second u-boot
3276 section_entries = section1.GetEntries()
3277 self.assertIn('u-boot', section_entries)
3278 entry = section_entries['u-boot']
3279 self.assertEqual(0, entry.offset)
3280 self.assertEqual(0x14, entry.image_pos)
3281 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3282 self.assertEqual(len(U_BOOT_DATA), entry.size)
3283
3284 # Section2
3285 self.assertIn('section2', section_entries)
3286 section2 = section_entries['section2']
3287 self.assertEqual(0x4, section2.offset)
3288 self.assertEqual(0x18, section2.image_pos)
3289 self.assertEqual(4, section2.size)
3290
3291 # Third u-boot
3292 section_entries = section2.GetEntries()
3293 self.assertIn('u-boot', section_entries)
3294 entry = section_entries['u-boot']
3295 self.assertEqual(0, entry.offset)
3296 self.assertEqual(0x18, entry.image_pos)
3297 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3298 self.assertEqual(len(U_BOOT_DATA), entry.size)
3299
Simon Glassf8a54bc2019-07-20 12:23:56 -06003300 def _RunReplaceCmd(self, entry_name, data, decomp=True, allow_resize=True,
3301 dts='132_replace.dts'):
Simon Glass072959a2019-07-20 12:23:50 -06003302 """Replace an entry in an image
3303
3304 This writes the entry data to update it, then opens the updated file and
3305 returns the value that it now finds there.
3306
3307 Args:
3308 entry_name: Entry name to replace
3309 data: Data to replace it with
3310 decomp: True to compress the data if needed, False if data is
3311 already compressed so should be used as is
Simon Glassf8a54bc2019-07-20 12:23:56 -06003312 allow_resize: True to allow entries to change size, False to raise
3313 an exception
Simon Glass072959a2019-07-20 12:23:50 -06003314
3315 Returns:
3316 Tuple:
3317 data from entry
3318 data from fdtmap (excluding header)
Simon Glassf8a54bc2019-07-20 12:23:56 -06003319 Image object that was modified
Simon Glass072959a2019-07-20 12:23:50 -06003320 """
Simon Glassf8a54bc2019-07-20 12:23:56 -06003321 dtb_data = self._DoReadFileDtb(dts, use_real_dtb=True,
Simon Glass072959a2019-07-20 12:23:50 -06003322 update_dtb=True)[1]
3323
3324 self.assertIn('image', control.images)
3325 image = control.images['image']
3326 entries = image.GetEntries()
3327 orig_dtb_data = entries['u-boot-dtb'].data
3328 orig_fdtmap_data = entries['fdtmap'].data
3329
Simon Glass80025522022-01-29 14:14:04 -07003330 image_fname = tools.get_output_filename('image.bin')
3331 updated_fname = tools.get_output_filename('image-updated.bin')
3332 tools.write_file(updated_fname, tools.read_file(image_fname))
Simon Glassf8a54bc2019-07-20 12:23:56 -06003333 image = control.WriteEntry(updated_fname, entry_name, data, decomp,
3334 allow_resize)
Simon Glass072959a2019-07-20 12:23:50 -06003335 data = control.ReadEntry(updated_fname, entry_name, decomp)
3336
Simon Glassf8a54bc2019-07-20 12:23:56 -06003337 # The DT data should not change unless resized:
3338 if not allow_resize:
3339 new_dtb_data = entries['u-boot-dtb'].data
3340 self.assertEqual(new_dtb_data, orig_dtb_data)
3341 new_fdtmap_data = entries['fdtmap'].data
3342 self.assertEqual(new_fdtmap_data, orig_fdtmap_data)
Simon Glass072959a2019-07-20 12:23:50 -06003343
Simon Glassf8a54bc2019-07-20 12:23:56 -06003344 return data, orig_fdtmap_data[fdtmap.FDTMAP_HDR_LEN:], image
Simon Glass072959a2019-07-20 12:23:50 -06003345
3346 def testReplaceSimple(self):
3347 """Test replacing a single file"""
3348 expected = b'x' * len(U_BOOT_DATA)
Simon Glassf8a54bc2019-07-20 12:23:56 -06003349 data, expected_fdtmap, _ = self._RunReplaceCmd('u-boot', expected,
3350 allow_resize=False)
Simon Glass072959a2019-07-20 12:23:50 -06003351 self.assertEqual(expected, data)
3352
3353 # Test that the state looks right. There should be an FDT for the fdtmap
3354 # that we jsut read back in, and it should match what we find in the
3355 # 'control' tables. Checking for an FDT that does not exist should
3356 # return None.
3357 path, fdtmap = state.GetFdtContents('fdtmap')
Simon Glassf8a54bc2019-07-20 12:23:56 -06003358 self.assertIsNotNone(path)
Simon Glass072959a2019-07-20 12:23:50 -06003359 self.assertEqual(expected_fdtmap, fdtmap)
3360
3361 dtb = state.GetFdtForEtype('fdtmap')
3362 self.assertEqual(dtb.GetContents(), fdtmap)
3363
3364 missing_path, missing_fdtmap = state.GetFdtContents('missing')
3365 self.assertIsNone(missing_path)
3366 self.assertIsNone(missing_fdtmap)
3367
3368 missing_dtb = state.GetFdtForEtype('missing')
3369 self.assertIsNone(missing_dtb)
3370
3371 self.assertEqual('/binman', state.fdt_path_prefix)
3372
3373 def testReplaceResizeFail(self):
3374 """Test replacing a file by something larger"""
3375 expected = U_BOOT_DATA + b'x'
3376 with self.assertRaises(ValueError) as e:
Simon Glassf8a54bc2019-07-20 12:23:56 -06003377 self._RunReplaceCmd('u-boot', expected, allow_resize=False,
3378 dts='139_replace_repack.dts')
Simon Glass072959a2019-07-20 12:23:50 -06003379 self.assertIn("Node '/u-boot': Entry data size does not match, but resize is disabled",
3380 str(e.exception))
3381
3382 def testReplaceMulti(self):
3383 """Test replacing entry data where multiple images are generated"""
3384 data = self._DoReadFileDtb('133_replace_multi.dts', use_real_dtb=True,
3385 update_dtb=True)[0]
3386 expected = b'x' * len(U_BOOT_DATA)
Simon Glass80025522022-01-29 14:14:04 -07003387 updated_fname = tools.get_output_filename('image-updated.bin')
3388 tools.write_file(updated_fname, data)
Simon Glass072959a2019-07-20 12:23:50 -06003389 entry_name = 'u-boot'
Simon Glassf8a54bc2019-07-20 12:23:56 -06003390 control.WriteEntry(updated_fname, entry_name, expected,
3391 allow_resize=False)
Simon Glass072959a2019-07-20 12:23:50 -06003392 data = control.ReadEntry(updated_fname, entry_name)
3393 self.assertEqual(expected, data)
3394
3395 # Check the state looks right.
3396 self.assertEqual('/binman/image', state.fdt_path_prefix)
3397
3398 # Now check we can write the first image
Simon Glass80025522022-01-29 14:14:04 -07003399 image_fname = tools.get_output_filename('first-image.bin')
3400 updated_fname = tools.get_output_filename('first-updated.bin')
3401 tools.write_file(updated_fname, tools.read_file(image_fname))
Simon Glass072959a2019-07-20 12:23:50 -06003402 entry_name = 'u-boot'
Simon Glassf8a54bc2019-07-20 12:23:56 -06003403 control.WriteEntry(updated_fname, entry_name, expected,
3404 allow_resize=False)
Simon Glass072959a2019-07-20 12:23:50 -06003405 data = control.ReadEntry(updated_fname, entry_name)
3406 self.assertEqual(expected, data)
3407
3408 # Check the state looks right.
3409 self.assertEqual('/binman/first-image', state.fdt_path_prefix)
Simon Glass39dd2152019-07-08 14:25:47 -06003410
Simon Glassfb30e292019-07-20 12:23:51 -06003411 def testUpdateFdtAllRepack(self):
3412 """Test that all device trees are updated with offset/size info"""
Marek Vasutf7413f02023-07-18 07:23:58 -06003413 self._SetupSplElf()
3414 self._SetupTplElf()
Simon Glassfb30e292019-07-20 12:23:51 -06003415 data = self._DoReadFileRealDtb('134_fdt_update_all_repack.dts')
3416 SECTION_SIZE = 0x300
3417 DTB_SIZE = 602
3418 FDTMAP_SIZE = 608
3419 base_expected = {
3420 'offset': 0,
3421 'size': SECTION_SIZE + DTB_SIZE * 2 + FDTMAP_SIZE,
3422 'image-pos': 0,
3423 'section:offset': 0,
3424 'section:size': SECTION_SIZE,
3425 'section:image-pos': 0,
3426 'section/u-boot-dtb:offset': 4,
3427 'section/u-boot-dtb:size': 636,
3428 'section/u-boot-dtb:image-pos': 4,
3429 'u-boot-spl-dtb:offset': SECTION_SIZE,
3430 'u-boot-spl-dtb:size': DTB_SIZE,
3431 'u-boot-spl-dtb:image-pos': SECTION_SIZE,
3432 'u-boot-tpl-dtb:offset': SECTION_SIZE + DTB_SIZE,
3433 'u-boot-tpl-dtb:image-pos': SECTION_SIZE + DTB_SIZE,
3434 'u-boot-tpl-dtb:size': DTB_SIZE,
3435 'fdtmap:offset': SECTION_SIZE + DTB_SIZE * 2,
3436 'fdtmap:size': FDTMAP_SIZE,
3437 'fdtmap:image-pos': SECTION_SIZE + DTB_SIZE * 2,
3438 }
3439 main_expected = {
3440 'section:orig-size': SECTION_SIZE,
3441 'section/u-boot-dtb:orig-offset': 4,
3442 }
3443
3444 # We expect three device-tree files in the output, with the first one
3445 # within a fixed-size section.
3446 # Read them in sequence. We look for an 'spl' property in the SPL tree,
3447 # and 'tpl' in the TPL tree, to make sure they are distinct from the
3448 # main U-Boot tree. All three should have the same positions and offset
3449 # except that the main tree should include the main_expected properties
3450 start = 4
3451 for item in ['', 'spl', 'tpl', None]:
3452 if item is None:
3453 start += 16 # Move past fdtmap header
3454 dtb = fdt.Fdt.FromData(data[start:])
3455 dtb.Scan()
3456 props = self._GetPropTree(dtb,
3457 BASE_DTB_PROPS + REPACK_DTB_PROPS + ['spl', 'tpl'],
3458 prefix='/' if item is None else '/binman/')
3459 expected = dict(base_expected)
3460 if item:
3461 expected[item] = 0
3462 else:
3463 # Main DTB and fdtdec should include the 'orig-' properties
3464 expected.update(main_expected)
3465 # Helpful for debugging:
3466 #for prop in sorted(props):
3467 #print('prop %s %s %s' % (prop, props[prop], expected[prop]))
3468 self.assertEqual(expected, props)
3469 if item == '':
3470 start = SECTION_SIZE
3471 else:
3472 start += dtb._fdt_obj.totalsize()
3473
Simon Glass11453762019-07-20 12:23:55 -06003474 def testFdtmapHeaderMiddle(self):
3475 """Test an FDT map in the middle of an image when it should be at end"""
3476 with self.assertRaises(ValueError) as e:
3477 self._DoReadFileRealDtb('135_fdtmap_hdr_middle.dts')
3478 self.assertIn("Invalid sibling order 'middle' for image-header: Must be at 'end' to match location",
3479 str(e.exception))
3480
3481 def testFdtmapHeaderStartBad(self):
3482 """Test an FDT map in middle of an image when it should be at start"""
3483 with self.assertRaises(ValueError) as e:
3484 self._DoReadFileRealDtb('136_fdtmap_hdr_startbad.dts')
3485 self.assertIn("Invalid sibling order 'end' for image-header: Must be at 'start' to match location",
3486 str(e.exception))
3487
3488 def testFdtmapHeaderEndBad(self):
3489 """Test an FDT map at the start of an image when it should be at end"""
3490 with self.assertRaises(ValueError) as e:
3491 self._DoReadFileRealDtb('137_fdtmap_hdr_endbad.dts')
3492 self.assertIn("Invalid sibling order 'start' for image-header: Must be at 'end' to match location",
3493 str(e.exception))
3494
3495 def testFdtmapHeaderNoSize(self):
3496 """Test an image header at the end of an image with undefined size"""
3497 self._DoReadFileRealDtb('138_fdtmap_hdr_nosize.dts')
3498
Simon Glassf8a54bc2019-07-20 12:23:56 -06003499 def testReplaceResize(self):
3500 """Test replacing a single file in an entry with a larger file"""
3501 expected = U_BOOT_DATA + b'x'
3502 data, _, image = self._RunReplaceCmd('u-boot', expected,
3503 dts='139_replace_repack.dts')
3504 self.assertEqual(expected, data)
3505
3506 entries = image.GetEntries()
3507 dtb_data = entries['u-boot-dtb'].data
3508 dtb = fdt.Fdt.FromData(dtb_data)
3509 dtb.Scan()
3510
3511 # The u-boot section should now be larger in the dtb
3512 node = dtb.GetNode('/binman/u-boot')
3513 self.assertEqual(len(expected), fdt_util.GetInt(node, 'size'))
3514
3515 # Same for the fdtmap
3516 fdata = entries['fdtmap'].data
3517 fdtb = fdt.Fdt.FromData(fdata[fdtmap.FDTMAP_HDR_LEN:])
3518 fdtb.Scan()
3519 fnode = fdtb.GetNode('/u-boot')
3520 self.assertEqual(len(expected), fdt_util.GetInt(fnode, 'size'))
3521
3522 def testReplaceResizeNoRepack(self):
3523 """Test replacing an entry with a larger file when not allowed"""
3524 expected = U_BOOT_DATA + b'x'
3525 with self.assertRaises(ValueError) as e:
3526 self._RunReplaceCmd('u-boot', expected)
3527 self.assertIn('Entry data size does not match, but allow-repack is not present for this image',
3528 str(e.exception))
3529
Simon Glass9d8ee322019-07-20 12:23:58 -06003530 def testEntryShrink(self):
3531 """Test contracting an entry after it is packed"""
3532 try:
3533 state.SetAllowEntryContraction(True)
3534 data = self._DoReadFileDtb('140_entry_shrink.dts',
3535 update_dtb=True)[0]
3536 finally:
3537 state.SetAllowEntryContraction(False)
3538 self.assertEqual(b'a', data[:1])
3539 self.assertEqual(U_BOOT_DATA, data[1:1 + len(U_BOOT_DATA)])
3540 self.assertEqual(b'a', data[-1:])
3541
3542 def testEntryShrinkFail(self):
3543 """Test not being allowed to contract an entry after it is packed"""
3544 data = self._DoReadFileDtb('140_entry_shrink.dts', update_dtb=True)[0]
3545
3546 # In this case there is a spare byte at the end of the data. The size of
3547 # the contents is only 1 byte but we still have the size before it
3548 # shrunk.
3549 self.assertEqual(b'a\0', data[:2])
3550 self.assertEqual(U_BOOT_DATA, data[2:2 + len(U_BOOT_DATA)])
3551 self.assertEqual(b'a\0', data[-2:])
3552
Simon Glass70e32982019-07-20 12:24:01 -06003553 def testDescriptorOffset(self):
3554 """Test that the Intel descriptor is always placed at at the start"""
3555 data = self._DoReadFileDtb('141_descriptor_offset.dts')
3556 image = control.images['image']
3557 entries = image.GetEntries()
3558 desc = entries['intel-descriptor']
Simon Glassed836ac2025-02-26 09:26:17 -07003559 self.assertEqual(0xff800000, desc.offset)
3560 self.assertEqual(0xff800000, desc.image_pos)
Simon Glass70e32982019-07-20 12:24:01 -06003561
Simon Glass37fdd142019-07-20 12:24:06 -06003562 def testReplaceCbfs(self):
3563 """Test replacing a single file in CBFS without changing the size"""
3564 self._CheckLz4()
3565 expected = b'x' * len(U_BOOT_DATA)
3566 data = self._DoReadFileRealDtb('142_replace_cbfs.dts')
Simon Glass80025522022-01-29 14:14:04 -07003567 updated_fname = tools.get_output_filename('image-updated.bin')
3568 tools.write_file(updated_fname, data)
Simon Glass37fdd142019-07-20 12:24:06 -06003569 entry_name = 'section/cbfs/u-boot'
3570 control.WriteEntry(updated_fname, entry_name, expected,
3571 allow_resize=True)
3572 data = control.ReadEntry(updated_fname, entry_name)
3573 self.assertEqual(expected, data)
3574
3575 def testReplaceResizeCbfs(self):
3576 """Test replacing a single file in CBFS with one of a different size"""
3577 self._CheckLz4()
3578 expected = U_BOOT_DATA + b'x'
3579 data = self._DoReadFileRealDtb('142_replace_cbfs.dts')
Simon Glass80025522022-01-29 14:14:04 -07003580 updated_fname = tools.get_output_filename('image-updated.bin')
3581 tools.write_file(updated_fname, data)
Simon Glass37fdd142019-07-20 12:24:06 -06003582 entry_name = 'section/cbfs/u-boot'
3583 control.WriteEntry(updated_fname, entry_name, expected,
3584 allow_resize=True)
3585 data = control.ReadEntry(updated_fname, entry_name)
3586 self.assertEqual(expected, data)
3587
Simon Glass30033c22019-07-20 12:24:15 -06003588 def _SetupForReplace(self):
3589 """Set up some files to use to replace entries
3590
3591 This generates an image, copies it to a new file, extracts all the files
3592 in it and updates some of them
3593
3594 Returns:
3595 List
3596 Image filename
3597 Output directory
3598 Expected values for updated entries, each a string
3599 """
3600 data = self._DoReadFileRealDtb('143_replace_all.dts')
3601
Simon Glass80025522022-01-29 14:14:04 -07003602 updated_fname = tools.get_output_filename('image-updated.bin')
3603 tools.write_file(updated_fname, data)
Simon Glass30033c22019-07-20 12:24:15 -06003604
3605 outdir = os.path.join(self._indir, 'extract')
3606 einfos = control.ExtractEntries(updated_fname, None, outdir, [])
3607
3608 expected1 = b'x' + U_BOOT_DATA + b'y'
3609 u_boot_fname1 = os.path.join(outdir, 'u-boot')
Simon Glass80025522022-01-29 14:14:04 -07003610 tools.write_file(u_boot_fname1, expected1)
Simon Glass30033c22019-07-20 12:24:15 -06003611
3612 expected2 = b'a' + U_BOOT_DATA + b'b'
3613 u_boot_fname2 = os.path.join(outdir, 'u-boot2')
Simon Glass80025522022-01-29 14:14:04 -07003614 tools.write_file(u_boot_fname2, expected2)
Simon Glass30033c22019-07-20 12:24:15 -06003615
3616 expected_text = b'not the same text'
3617 text_fname = os.path.join(outdir, 'text')
Simon Glass80025522022-01-29 14:14:04 -07003618 tools.write_file(text_fname, expected_text)
Simon Glass30033c22019-07-20 12:24:15 -06003619
3620 dtb_fname = os.path.join(outdir, 'u-boot-dtb')
3621 dtb = fdt.FdtScan(dtb_fname)
3622 node = dtb.GetNode('/binman/text')
3623 node.AddString('my-property', 'the value')
3624 dtb.Sync(auto_resize=True)
3625 dtb.Flush()
3626
3627 return updated_fname, outdir, expected1, expected2, expected_text
3628
3629 def _CheckReplaceMultiple(self, entry_paths):
3630 """Handle replacing the contents of multiple entries
3631
3632 Args:
3633 entry_paths: List of entry paths to replace
3634
3635 Returns:
3636 List
3637 Dict of entries in the image:
3638 key: Entry name
3639 Value: Entry object
3640 Expected values for updated entries, each a string
3641 """
3642 updated_fname, outdir, expected1, expected2, expected_text = (
3643 self._SetupForReplace())
3644 control.ReplaceEntries(updated_fname, None, outdir, entry_paths)
3645
3646 image = Image.FromFile(updated_fname)
3647 image.LoadData()
3648 return image.GetEntries(), expected1, expected2, expected_text
3649
3650 def testReplaceAll(self):
3651 """Test replacing the contents of all entries"""
3652 entries, expected1, expected2, expected_text = (
3653 self._CheckReplaceMultiple([]))
3654 data = entries['u-boot'].data
3655 self.assertEqual(expected1, data)
3656
3657 data = entries['u-boot2'].data
3658 self.assertEqual(expected2, data)
3659
3660 data = entries['text'].data
3661 self.assertEqual(expected_text, data)
3662
3663 # Check that the device tree is updated
3664 data = entries['u-boot-dtb'].data
3665 dtb = fdt.Fdt.FromData(data)
3666 dtb.Scan()
3667 node = dtb.GetNode('/binman/text')
3668 self.assertEqual('the value', node.props['my-property'].value)
3669
3670 def testReplaceSome(self):
3671 """Test replacing the contents of a few entries"""
3672 entries, expected1, expected2, expected_text = (
3673 self._CheckReplaceMultiple(['u-boot2', 'text']))
3674
3675 # This one should not change
3676 data = entries['u-boot'].data
3677 self.assertEqual(U_BOOT_DATA, data)
3678
3679 data = entries['u-boot2'].data
3680 self.assertEqual(expected2, data)
3681
3682 data = entries['text'].data
3683 self.assertEqual(expected_text, data)
3684
3685 def testReplaceCmd(self):
3686 """Test replacing a file fron an image on the command line"""
3687 self._DoReadFileRealDtb('143_replace_all.dts')
3688
3689 try:
3690 tmpdir, updated_fname = self._SetupImageInTmpdir()
3691
3692 fname = os.path.join(tmpdir, 'update-u-boot.bin')
3693 expected = b'x' * len(U_BOOT_DATA)
Simon Glass80025522022-01-29 14:14:04 -07003694 tools.write_file(fname, expected)
Simon Glass30033c22019-07-20 12:24:15 -06003695
3696 self._DoBinman('replace', '-i', updated_fname, 'u-boot', '-f', fname)
Simon Glass80025522022-01-29 14:14:04 -07003697 data = tools.read_file(updated_fname)
Simon Glass30033c22019-07-20 12:24:15 -06003698 self.assertEqual(expected, data[:len(expected)])
3699 map_fname = os.path.join(tmpdir, 'image-updated.map')
3700 self.assertFalse(os.path.exists(map_fname))
3701 finally:
3702 shutil.rmtree(tmpdir)
3703
3704 def testReplaceCmdSome(self):
3705 """Test replacing some files fron an image on the command line"""
3706 updated_fname, outdir, expected1, expected2, expected_text = (
3707 self._SetupForReplace())
3708
3709 self._DoBinman('replace', '-i', updated_fname, '-I', outdir,
3710 'u-boot2', 'text')
3711
Simon Glass80025522022-01-29 14:14:04 -07003712 tools.prepare_output_dir(None)
Simon Glass30033c22019-07-20 12:24:15 -06003713 image = Image.FromFile(updated_fname)
3714 image.LoadData()
3715 entries = image.GetEntries()
3716
3717 # This one should not change
3718 data = entries['u-boot'].data
3719 self.assertEqual(U_BOOT_DATA, data)
3720
3721 data = entries['u-boot2'].data
3722 self.assertEqual(expected2, data)
3723
3724 data = entries['text'].data
3725 self.assertEqual(expected_text, data)
3726
3727 def testReplaceMissing(self):
3728 """Test replacing entries where the file is missing"""
3729 updated_fname, outdir, expected1, expected2, expected_text = (
3730 self._SetupForReplace())
3731
3732 # Remove one of the files, to generate a warning
3733 u_boot_fname1 = os.path.join(outdir, 'u-boot')
3734 os.remove(u_boot_fname1)
3735
Simon Glass14d64e32025-04-29 07:21:59 -06003736 with terminal.capture() as (stdout, stderr):
Simon Glass30033c22019-07-20 12:24:15 -06003737 control.ReplaceEntries(updated_fname, None, outdir, [])
3738 self.assertIn("Skipping entry '/u-boot' from missing file",
Simon Glass6e02f7c2020-07-09 18:39:39 -06003739 stderr.getvalue())
Simon Glass30033c22019-07-20 12:24:15 -06003740
3741 def testReplaceCmdMap(self):
3742 """Test replacing a file fron an image on the command line"""
3743 self._DoReadFileRealDtb('143_replace_all.dts')
3744
3745 try:
3746 tmpdir, updated_fname = self._SetupImageInTmpdir()
3747
3748 fname = os.path.join(self._indir, 'update-u-boot.bin')
3749 expected = b'x' * len(U_BOOT_DATA)
Simon Glass80025522022-01-29 14:14:04 -07003750 tools.write_file(fname, expected)
Simon Glass30033c22019-07-20 12:24:15 -06003751
3752 self._DoBinman('replace', '-i', updated_fname, 'u-boot',
3753 '-f', fname, '-m')
3754 map_fname = os.path.join(tmpdir, 'image-updated.map')
3755 self.assertTrue(os.path.exists(map_fname))
3756 finally:
3757 shutil.rmtree(tmpdir)
3758
3759 def testReplaceNoEntryPaths(self):
3760 """Test replacing an entry without an entry path"""
3761 self._DoReadFileRealDtb('143_replace_all.dts')
Simon Glass80025522022-01-29 14:14:04 -07003762 image_fname = tools.get_output_filename('image.bin')
Simon Glass30033c22019-07-20 12:24:15 -06003763 with self.assertRaises(ValueError) as e:
3764 control.ReplaceEntries(image_fname, 'fname', None, [])
3765 self.assertIn('Must specify an entry path to read with -f',
3766 str(e.exception))
3767
3768 def testReplaceTooManyEntryPaths(self):
3769 """Test extracting some entries"""
3770 self._DoReadFileRealDtb('143_replace_all.dts')
Simon Glass80025522022-01-29 14:14:04 -07003771 image_fname = tools.get_output_filename('image.bin')
Simon Glass30033c22019-07-20 12:24:15 -06003772 with self.assertRaises(ValueError) as e:
3773 control.ReplaceEntries(image_fname, 'fname', None, ['a', 'b'])
3774 self.assertIn('Must specify exactly one entry path to write with -f',
3775 str(e.exception))
3776
Simon Glass0b074d62019-08-24 07:22:48 -06003777 def testPackReset16(self):
3778 """Test that an image with an x86 reset16 region can be created"""
3779 data = self._DoReadFile('144_x86_reset16.dts')
3780 self.assertEqual(X86_RESET16_DATA, data[:len(X86_RESET16_DATA)])
3781
3782 def testPackReset16Spl(self):
3783 """Test that an image with an x86 reset16-spl region can be created"""
3784 data = self._DoReadFile('145_x86_reset16_spl.dts')
3785 self.assertEqual(X86_RESET16_SPL_DATA, data[:len(X86_RESET16_SPL_DATA)])
3786
3787 def testPackReset16Tpl(self):
3788 """Test that an image with an x86 reset16-tpl region can be created"""
3789 data = self._DoReadFile('146_x86_reset16_tpl.dts')
3790 self.assertEqual(X86_RESET16_TPL_DATA, data[:len(X86_RESET16_TPL_DATA)])
3791
Simon Glass232f90c2019-08-24 07:22:50 -06003792 def testPackIntelFit(self):
3793 """Test that an image with an Intel FIT and pointer can be created"""
3794 data = self._DoReadFile('147_intel_fit.dts')
3795 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
3796 fit = data[16:32];
3797 self.assertEqual(b'_FIT_ \x01\x00\x00\x00\x00\x01\x80}' , fit)
3798 ptr = struct.unpack('<i', data[0x40:0x44])[0]
3799
3800 image = control.images['image']
3801 entries = image.GetEntries()
Simon Glassed836ac2025-02-26 09:26:17 -07003802 expected_ptr = entries['intel-fit'].image_pos #- (1 << 32)
3803 self.assertEqual(expected_ptr, ptr + (1 << 32))
Simon Glass232f90c2019-08-24 07:22:50 -06003804
3805 def testPackIntelFitMissing(self):
3806 """Test detection of a FIT pointer with not FIT region"""
3807 with self.assertRaises(ValueError) as e:
3808 self._DoReadFile('148_intel_fit_missing.dts')
3809 self.assertIn("'intel-fit-ptr' section must have an 'intel-fit' sibling",
3810 str(e.exception))
3811
Simon Glass72555fa2019-11-06 17:22:44 -07003812 def _CheckSymbolsTplSection(self, dts, expected_vals):
3813 data = self._DoReadFile(dts)
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003814 sym_values = struct.pack('<LLQLL', elf.BINMAN_SYM_MAGIC_VALUE, *expected_vals)
Simon Glass3eb5b202019-08-24 07:23:00 -06003815 upto1 = 4 + len(U_BOOT_SPL_DATA)
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003816 expected1 = tools.get_bytes(0xff, 4) + sym_values + U_BOOT_SPL_DATA[24:]
Simon Glass3eb5b202019-08-24 07:23:00 -06003817 self.assertEqual(expected1, data[:upto1])
3818
3819 upto2 = upto1 + 1 + len(U_BOOT_SPL_DATA)
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003820 expected2 = tools.get_bytes(0xff, 1) + sym_values + U_BOOT_SPL_DATA[24:]
Simon Glass3eb5b202019-08-24 07:23:00 -06003821 self.assertEqual(expected2, data[upto1:upto2])
3822
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003823 upto3 = 0x3c + len(U_BOOT_DATA)
Simon Glass80025522022-01-29 14:14:04 -07003824 expected3 = tools.get_bytes(0xff, 1) + U_BOOT_DATA
Simon Glass3eb5b202019-08-24 07:23:00 -06003825 self.assertEqual(expected3, data[upto2:upto3])
3826
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003827 expected4 = sym_values + U_BOOT_TPL_DATA[24:]
Simon Glass72555fa2019-11-06 17:22:44 -07003828 self.assertEqual(expected4, data[upto3:upto3 + len(U_BOOT_TPL_DATA)])
3829
3830 def testSymbolsTplSection(self):
3831 """Test binman can assign symbols embedded in U-Boot TPL in a section"""
3832 self._SetupSplElf('u_boot_binman_syms')
3833 self._SetupTplElf('u_boot_binman_syms')
3834 self._CheckSymbolsTplSection('149_symbols_tpl.dts',
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003835 [0x04, 0x20, 0x10 + 0x3c, 0x04])
Simon Glass72555fa2019-11-06 17:22:44 -07003836
3837 def testSymbolsTplSectionX86(self):
3838 """Test binman can assign symbols in a section with end-at-4gb"""
3839 self._SetupSplElf('u_boot_binman_syms_x86')
3840 self._SetupTplElf('u_boot_binman_syms_x86')
3841 self._CheckSymbolsTplSection('155_symbols_tpl_x86.dts',
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003842 [0xffffff04, 0xffffff20, 0xffffff3c,
Simon Glass72555fa2019-11-06 17:22:44 -07003843 0x04])
Simon Glass3eb5b202019-08-24 07:23:00 -06003844
Simon Glass98c59572019-08-24 07:23:03 -06003845 def testPackX86RomIfwiSectiom(self):
3846 """Test that a section can be placed in an IFWI region"""
3847 self._SetupIfwi('fitimage.bin')
3848 data = self._DoReadFile('151_x86_rom_ifwi_section.dts')
3849 self._CheckIfwi(data)
3850
Simon Glassba7985d2019-08-24 07:23:07 -06003851 def testPackFspM(self):
3852 """Test that an image with a FSP memory-init binary can be created"""
3853 data = self._DoReadFile('152_intel_fsp_m.dts')
3854 self.assertEqual(FSP_M_DATA, data[:len(FSP_M_DATA)])
3855
Simon Glass4d9086d2019-10-20 21:31:35 -06003856 def testPackFspS(self):
3857 """Test that an image with a FSP silicon-init binary can be created"""
3858 data = self._DoReadFile('153_intel_fsp_s.dts')
3859 self.assertEqual(FSP_S_DATA, data[:len(FSP_S_DATA)])
Simon Glassba7985d2019-08-24 07:23:07 -06003860
Simon Glass9ea87b22019-10-20 21:31:36 -06003861 def testPackFspT(self):
3862 """Test that an image with a FSP temp-ram-init binary can be created"""
3863 data = self._DoReadFile('154_intel_fsp_t.dts')
3864 self.assertEqual(FSP_T_DATA, data[:len(FSP_T_DATA)])
3865
Simon Glass48f3aad2020-07-09 18:39:31 -06003866 def testMkimage(self):
3867 """Test using mkimage to build an image"""
Marek Vasutf7413f02023-07-18 07:23:58 -06003868 self._SetupSplElf()
Simon Glass48f3aad2020-07-09 18:39:31 -06003869 data = self._DoReadFile('156_mkimage.dts')
3870
3871 # Just check that the data appears in the file somewhere
3872 self.assertIn(U_BOOT_SPL_DATA, data)
3873
Simon Glass66152ce2022-01-09 20:14:09 -07003874 def testMkimageMissing(self):
3875 """Test that binman still produces an image if mkimage is missing"""
Marek Vasutf7413f02023-07-18 07:23:58 -06003876 self._SetupSplElf()
Simon Glass14d64e32025-04-29 07:21:59 -06003877 with terminal.capture() as (_, stderr):
Simon Glass66152ce2022-01-09 20:14:09 -07003878 self._DoTestFile('156_mkimage.dts',
3879 force_missing_bintools='mkimage')
3880 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07003881 self.assertRegex(err, "Image 'image'.*missing bintools.*: mkimage")
Simon Glass66152ce2022-01-09 20:14:09 -07003882
Simon Glass5e560182020-07-09 18:39:36 -06003883 def testExtblob(self):
3884 """Test an image with an external blob"""
3885 data = self._DoReadFile('157_blob_ext.dts')
3886 self.assertEqual(REFCODE_DATA, data)
3887
3888 def testExtblobMissing(self):
3889 """Test an image with a missing external blob"""
3890 with self.assertRaises(ValueError) as e:
3891 self._DoReadFile('158_blob_ext_missing.dts')
3892 self.assertIn("Filename 'missing-file' not found in input path",
3893 str(e.exception))
3894
Simon Glass5d94cc62020-07-09 18:39:38 -06003895 def testExtblobMissingOk(self):
3896 """Test an image with an missing external blob that is allowed"""
Simon Glass14d64e32025-04-29 07:21:59 -06003897 with terminal.capture() as (stdout, stderr):
Simon Glass6bce5dc2022-11-09 19:14:42 -07003898 ret = self._DoTestFile('158_blob_ext_missing.dts',
3899 allow_missing=True)
3900 self.assertEqual(103, ret)
Simon Glassa003cd32020-07-09 18:39:40 -06003901 err = stderr.getvalue()
Jonas Karlmanda423fc2023-07-18 20:34:39 +00003902 self.assertIn('(missing-file)', err)
Simon Glass49cd2b32023-02-07 14:34:18 -07003903 self.assertRegex(err, "Image 'image'.*missing.*: blob-ext")
Simon Glass6bce5dc2022-11-09 19:14:42 -07003904 self.assertIn('Some images are invalid', err)
3905
3906 def testExtblobMissingOkFlag(self):
3907 """Test an image with an missing external blob allowed with -W"""
Simon Glass14d64e32025-04-29 07:21:59 -06003908 with terminal.capture() as (stdout, stderr):
Simon Glass6bce5dc2022-11-09 19:14:42 -07003909 ret = self._DoTestFile('158_blob_ext_missing.dts',
3910 allow_missing=True, ignore_missing=True)
3911 self.assertEqual(0, ret)
3912 err = stderr.getvalue()
Jonas Karlmanda423fc2023-07-18 20:34:39 +00003913 self.assertIn('(missing-file)', err)
Simon Glass49cd2b32023-02-07 14:34:18 -07003914 self.assertRegex(err, "Image 'image'.*missing.*: blob-ext")
Simon Glass6bce5dc2022-11-09 19:14:42 -07003915 self.assertIn('Some images are invalid', err)
Simon Glassa003cd32020-07-09 18:39:40 -06003916
3917 def testExtblobMissingOkSect(self):
3918 """Test an image with an missing external blob that is allowed"""
Simon Glass14d64e32025-04-29 07:21:59 -06003919 with terminal.capture() as (stdout, stderr):
Simon Glassa003cd32020-07-09 18:39:40 -06003920 self._DoTestFile('159_blob_ext_missing_sect.dts',
3921 allow_missing=True)
3922 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07003923 self.assertRegex(err, "Image 'image'.*missing.*: blob-ext blob-ext2")
Simon Glass5d94cc62020-07-09 18:39:38 -06003924
Simon Glasse88cef92020-07-09 18:39:41 -06003925 def testPackX86RomMeMissingDesc(self):
3926 """Test that an missing Intel descriptor entry is allowed"""
Simon Glass14d64e32025-04-29 07:21:59 -06003927 with terminal.capture() as (stdout, stderr):
Simon Glass14c596c2020-07-25 15:11:19 -06003928 self._DoTestFile('164_x86_rom_me_missing.dts', allow_missing=True)
Simon Glasse88cef92020-07-09 18:39:41 -06003929 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07003930 self.assertRegex(err, "Image 'image'.*missing.*: intel-descriptor")
Simon Glasse88cef92020-07-09 18:39:41 -06003931
3932 def testPackX86RomMissingIfwi(self):
3933 """Test that an x86 ROM with Integrated Firmware Image can be created"""
3934 self._SetupIfwi('fitimage.bin')
3935 pathname = os.path.join(self._indir, 'fitimage.bin')
3936 os.remove(pathname)
Simon Glass14d64e32025-04-29 07:21:59 -06003937 with terminal.capture() as (stdout, stderr):
Simon Glasse88cef92020-07-09 18:39:41 -06003938 self._DoTestFile('111_x86_rom_ifwi.dts', allow_missing=True)
3939 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07003940 self.assertRegex(err, "Image 'image'.*missing.*: intel-ifwi")
Simon Glasse88cef92020-07-09 18:39:41 -06003941
Simon Glass2a0fa982022-02-11 13:23:21 -07003942 def testPackOverlapZero(self):
Simon Glassd70829a2020-07-09 18:39:42 -06003943 """Test that zero-size overlapping regions are ignored"""
3944 self._DoTestFile('160_pack_overlap_zero.dts')
3945
Alper Nebi Yasak09fb0612022-02-08 01:08:04 +03003946 def _CheckSimpleFitData(self, fit_data, kernel_data, fdt1_data):
Simon Glass45d556d2020-07-09 18:39:45 -06003947 # The data should be inside the FIT
3948 dtb = fdt.Fdt.FromData(fit_data)
3949 dtb.Scan()
3950 fnode = dtb.GetNode('/images/kernel')
3951 self.assertIn('data', fnode.props)
3952
3953 fname = os.path.join(self._indir, 'fit_data.fit')
Simon Glass80025522022-01-29 14:14:04 -07003954 tools.write_file(fname, fit_data)
3955 out = tools.run('dumpimage', '-l', fname)
Simon Glass45d556d2020-07-09 18:39:45 -06003956
3957 # Check a few features to make sure the plumbing works. We don't need
3958 # to test the operation of mkimage or dumpimage here. First convert the
3959 # output into a dict where the keys are the fields printed by dumpimage
3960 # and the values are a list of values for each field
3961 lines = out.splitlines()
3962
3963 # Converts "Compression: gzip compressed" into two groups:
3964 # 'Compression' and 'gzip compressed'
3965 re_line = re.compile(r'^ *([^:]*)(?:: *(.*))?$')
3966 vals = collections.defaultdict(list)
3967 for line in lines:
3968 mat = re_line.match(line)
3969 vals[mat.group(1)].append(mat.group(2))
3970
Brandon Maiera657bc62024-06-04 16:16:05 +00003971 self.assertEqual('FIT description: test-desc', lines[0])
Simon Glass45d556d2020-07-09 18:39:45 -06003972 self.assertIn('Created:', lines[1])
3973 self.assertIn('Image 0 (kernel)', vals)
3974 self.assertIn('Hash value', vals)
3975 data_sizes = vals.get('Data Size')
3976 self.assertIsNotNone(data_sizes)
3977 self.assertEqual(2, len(data_sizes))
3978 # Format is "4 Bytes = 0.00 KiB = 0.00 MiB" so take the first word
Alper Nebi Yasak09fb0612022-02-08 01:08:04 +03003979 self.assertEqual(len(kernel_data), int(data_sizes[0].split()[0]))
3980 self.assertEqual(len(fdt1_data), int(data_sizes[1].split()[0]))
3981
Alper Nebi Yasak1a0ee0f2022-03-27 18:31:47 +03003982 # Check if entry listing correctly omits /images/
3983 image = control.images['image']
3984 fit_entry = image.GetEntries()['fit']
3985 subentries = list(fit_entry.GetEntries().keys())
3986 expected = ['kernel', 'fdt-1']
3987 self.assertEqual(expected, subentries)
3988
Alper Nebi Yasak09fb0612022-02-08 01:08:04 +03003989 def testSimpleFit(self):
3990 """Test an image with a FIT inside"""
Marek Vasutf7413f02023-07-18 07:23:58 -06003991 self._SetupSplElf()
Alper Nebi Yasak09fb0612022-02-08 01:08:04 +03003992 data = self._DoReadFile('161_fit.dts')
3993 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
3994 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
3995 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
3996
3997 self._CheckSimpleFitData(fit_data, U_BOOT_DATA, U_BOOT_SPL_DTB_DATA)
3998
3999 def testSimpleFitExpandsSubentries(self):
4000 """Test that FIT images expand their subentries"""
4001 data = self._DoReadFileDtb('161_fit.dts', use_expanded=True)[0]
4002 self.assertEqual(U_BOOT_EXP_DATA, data[:len(U_BOOT_EXP_DATA)])
4003 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
4004 fit_data = data[len(U_BOOT_EXP_DATA):-len(U_BOOT_NODTB_DATA)]
4005
4006 self._CheckSimpleFitData(fit_data, U_BOOT_EXP_DATA, U_BOOT_SPL_DTB_DATA)
Simon Glass45d556d2020-07-09 18:39:45 -06004007
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03004008 def testSimpleFitImagePos(self):
4009 """Test that we have correct image-pos for FIT subentries"""
4010 data, _, _, out_dtb_fname = self._DoReadFileDtb('161_fit.dts',
4011 update_dtb=True)
4012 dtb = fdt.Fdt(out_dtb_fname)
4013 dtb.Scan()
4014 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
4015
Simon Glassb7bad182022-03-05 20:19:01 -07004016 self.maxDiff = None
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03004017 self.assertEqual({
4018 'image-pos': 0,
4019 'offset': 0,
4020 'size': 1890,
4021
4022 'u-boot:image-pos': 0,
4023 'u-boot:offset': 0,
4024 'u-boot:size': 4,
4025
4026 'fit:image-pos': 4,
4027 'fit:offset': 4,
4028 'fit:size': 1840,
4029
Simon Glassb7bad182022-03-05 20:19:01 -07004030 'fit/images/kernel:image-pos': 304,
4031 'fit/images/kernel:offset': 300,
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03004032 'fit/images/kernel:size': 4,
4033
Simon Glassb7bad182022-03-05 20:19:01 -07004034 'fit/images/kernel/u-boot:image-pos': 304,
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03004035 'fit/images/kernel/u-boot:offset': 0,
4036 'fit/images/kernel/u-boot:size': 4,
4037
Simon Glassb7bad182022-03-05 20:19:01 -07004038 'fit/images/fdt-1:image-pos': 552,
4039 'fit/images/fdt-1:offset': 548,
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03004040 'fit/images/fdt-1:size': 6,
4041
Simon Glassb7bad182022-03-05 20:19:01 -07004042 'fit/images/fdt-1/u-boot-spl-dtb:image-pos': 552,
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03004043 'fit/images/fdt-1/u-boot-spl-dtb:offset': 0,
4044 'fit/images/fdt-1/u-boot-spl-dtb:size': 6,
4045
4046 'u-boot-nodtb:image-pos': 1844,
4047 'u-boot-nodtb:offset': 1844,
4048 'u-boot-nodtb:size': 46,
4049 }, props)
4050
4051 # Actually check the data is where we think it is
4052 for node, expected in [
4053 ("u-boot", U_BOOT_DATA),
4054 ("fit/images/kernel", U_BOOT_DATA),
4055 ("fit/images/kernel/u-boot", U_BOOT_DATA),
4056 ("fit/images/fdt-1", U_BOOT_SPL_DTB_DATA),
4057 ("fit/images/fdt-1/u-boot-spl-dtb", U_BOOT_SPL_DTB_DATA),
4058 ("u-boot-nodtb", U_BOOT_NODTB_DATA),
4059 ]:
4060 image_pos = props[f"{node}:image-pos"]
4061 size = props[f"{node}:size"]
4062 self.assertEqual(len(expected), size)
4063 self.assertEqual(expected, data[image_pos:image_pos+size])
4064
Simon Glass45d556d2020-07-09 18:39:45 -06004065 def testFitExternal(self):
Simon Glass31ee50f2020-09-01 05:13:55 -06004066 """Test an image with an FIT with external images"""
Simon Glass45d556d2020-07-09 18:39:45 -06004067 data = self._DoReadFile('162_fit_external.dts')
4068 fit_data = data[len(U_BOOT_DATA):-2] # _testing is 2 bytes
4069
Simon Glass7932c882022-01-09 20:13:39 -07004070 # Size of the external-data region as set up by mkimage
4071 external_data_size = len(U_BOOT_DATA) + 2
4072 expected_size = (len(U_BOOT_DATA) + 0x400 +
Simon Glass80025522022-01-29 14:14:04 -07004073 tools.align(external_data_size, 4) +
Simon Glass7932c882022-01-09 20:13:39 -07004074 len(U_BOOT_NODTB_DATA))
4075
Simon Glass45d556d2020-07-09 18:39:45 -06004076 # The data should be outside the FIT
4077 dtb = fdt.Fdt.FromData(fit_data)
4078 dtb.Scan()
4079 fnode = dtb.GetNode('/images/kernel')
4080 self.assertNotIn('data', fnode.props)
Simon Glass7932c882022-01-09 20:13:39 -07004081 self.assertEqual(len(U_BOOT_DATA),
4082 fdt_util.fdt32_to_cpu(fnode.props['data-size'].value))
4083 fit_pos = 0x400;
4084 self.assertEqual(
4085 fit_pos,
4086 fdt_util.fdt32_to_cpu(fnode.props['data-position'].value))
4087
Brandon Maiera657bc62024-06-04 16:16:05 +00004088 self.assertEqual(expected_size, len(data))
Simon Glass7932c882022-01-09 20:13:39 -07004089 actual_pos = len(U_BOOT_DATA) + fit_pos
4090 self.assertEqual(U_BOOT_DATA + b'aa',
4091 data[actual_pos:actual_pos + external_data_size])
Simon Glassfb30e292019-07-20 12:23:51 -06004092
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03004093 def testFitExternalImagePos(self):
4094 """Test that we have correct image-pos for external FIT subentries"""
4095 data, _, _, out_dtb_fname = self._DoReadFileDtb('162_fit_external.dts',
4096 update_dtb=True)
4097 dtb = fdt.Fdt(out_dtb_fname)
4098 dtb.Scan()
4099 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
4100
4101 self.assertEqual({
4102 'image-pos': 0,
4103 'offset': 0,
4104 'size': 1082,
4105
4106 'u-boot:image-pos': 0,
4107 'u-boot:offset': 0,
4108 'u-boot:size': 4,
4109
4110 'fit:size': 1032,
4111 'fit:offset': 4,
4112 'fit:image-pos': 4,
4113
4114 'fit/images/kernel:size': 4,
4115 'fit/images/kernel:offset': 1024,
4116 'fit/images/kernel:image-pos': 1028,
4117
4118 'fit/images/kernel/u-boot:size': 4,
4119 'fit/images/kernel/u-boot:offset': 0,
4120 'fit/images/kernel/u-boot:image-pos': 1028,
4121
4122 'fit/images/fdt-1:size': 2,
4123 'fit/images/fdt-1:offset': 1028,
4124 'fit/images/fdt-1:image-pos': 1032,
4125
4126 'fit/images/fdt-1/_testing:size': 2,
4127 'fit/images/fdt-1/_testing:offset': 0,
4128 'fit/images/fdt-1/_testing:image-pos': 1032,
4129
4130 'u-boot-nodtb:image-pos': 1036,
4131 'u-boot-nodtb:offset': 1036,
4132 'u-boot-nodtb:size': 46,
4133 }, props)
4134
4135 # Actually check the data is where we think it is
4136 for node, expected in [
4137 ("u-boot", U_BOOT_DATA),
4138 ("fit/images/kernel", U_BOOT_DATA),
4139 ("fit/images/kernel/u-boot", U_BOOT_DATA),
4140 ("fit/images/fdt-1", b'aa'),
4141 ("fit/images/fdt-1/_testing", b'aa'),
4142 ("u-boot-nodtb", U_BOOT_NODTB_DATA),
4143 ]:
4144 image_pos = props[f"{node}:image-pos"]
4145 size = props[f"{node}:size"]
4146 self.assertEqual(len(expected), size)
4147 self.assertEqual(expected, data[image_pos:image_pos+size])
4148
Simon Glass66152ce2022-01-09 20:14:09 -07004149 def testFitMissing(self):
Simon Glass039d65f2023-03-02 17:02:43 -07004150 """Test that binman complains if mkimage is missing"""
4151 with self.assertRaises(ValueError) as e:
4152 self._DoTestFile('162_fit_external.dts',
4153 force_missing_bintools='mkimage')
4154 self.assertIn("Node '/binman/fit': Missing tool: 'mkimage'",
4155 str(e.exception))
4156
4157 def testFitMissingOK(self):
Simon Glass66152ce2022-01-09 20:14:09 -07004158 """Test that binman still produces a FIT image if mkimage is missing"""
Simon Glass14d64e32025-04-29 07:21:59 -06004159 with terminal.capture() as (_, stderr):
Simon Glass039d65f2023-03-02 17:02:43 -07004160 self._DoTestFile('162_fit_external.dts', allow_missing=True,
Simon Glass66152ce2022-01-09 20:14:09 -07004161 force_missing_bintools='mkimage')
4162 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07004163 self.assertRegex(err, "Image 'image'.*missing bintools.*: mkimage")
Simon Glass66152ce2022-01-09 20:14:09 -07004164
Alper Nebi Yasak6aae2392020-08-31 12:58:18 +03004165 def testSectionIgnoreHashSignature(self):
4166 """Test that sections ignore hash, signature nodes for its data"""
4167 data = self._DoReadFile('165_section_ignore_hash_signature.dts')
4168 expected = (U_BOOT_DATA + U_BOOT_DATA)
4169 self.assertEqual(expected, data)
4170
Alper Nebi Yasakc5030602020-08-31 12:58:19 +03004171 def testPadInSections(self):
4172 """Test pad-before, pad-after for entries in sections"""
Simon Glassd12599d2020-10-26 17:40:09 -06004173 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4174 '166_pad_in_sections.dts', update_dtb=True)
Simon Glass80025522022-01-29 14:14:04 -07004175 expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) +
4176 U_BOOT_DATA + tools.get_bytes(ord('!'), 6) +
Alper Nebi Yasakc5030602020-08-31 12:58:19 +03004177 U_BOOT_DATA)
4178 self.assertEqual(expected, data)
4179
Simon Glassd12599d2020-10-26 17:40:09 -06004180 dtb = fdt.Fdt(out_dtb_fname)
4181 dtb.Scan()
4182 props = self._GetPropTree(dtb, ['size', 'image-pos', 'offset'])
4183 expected = {
4184 'image-pos': 0,
4185 'offset': 0,
4186 'size': 12 + 6 + 3 * len(U_BOOT_DATA),
4187
4188 'section:image-pos': 0,
4189 'section:offset': 0,
4190 'section:size': 12 + 6 + 3 * len(U_BOOT_DATA),
4191
4192 'section/before:image-pos': 0,
4193 'section/before:offset': 0,
4194 'section/before:size': len(U_BOOT_DATA),
4195
4196 'section/u-boot:image-pos': 4,
4197 'section/u-boot:offset': 4,
4198 'section/u-boot:size': 12 + len(U_BOOT_DATA) + 6,
4199
4200 'section/after:image-pos': 26,
4201 'section/after:offset': 26,
4202 'section/after:size': len(U_BOOT_DATA),
4203 }
4204 self.assertEqual(expected, props)
4205
Alper Nebi Yasakc261ec12020-08-31 12:58:20 +03004206 def testFitImageSubentryAlignment(self):
4207 """Test relative alignability of FIT image subentries"""
Alper Nebi Yasakd4553262022-02-08 01:08:07 +03004208 self._SetupSplElf()
Alper Nebi Yasakc261ec12020-08-31 12:58:20 +03004209 entry_args = {
4210 'test-id': TEXT_DATA,
4211 }
4212 data, _, _, _ = self._DoReadFileDtb('167_fit_image_subentry_alignment.dts',
4213 entry_args=entry_args)
4214 dtb = fdt.Fdt.FromData(data)
4215 dtb.Scan()
4216
4217 node = dtb.GetNode('/images/kernel')
4218 data = dtb.GetProps(node)["data"].bytes
4219 align_pad = 0x10 - (len(U_BOOT_SPL_DATA) % 0x10)
Simon Glass80025522022-01-29 14:14:04 -07004220 expected = (tools.get_bytes(0, 0x20) + U_BOOT_SPL_DATA +
4221 tools.get_bytes(0, align_pad) + U_BOOT_DATA)
Alper Nebi Yasakc261ec12020-08-31 12:58:20 +03004222 self.assertEqual(expected, data)
4223
4224 node = dtb.GetNode('/images/fdt-1')
4225 data = dtb.GetProps(node)["data"].bytes
Simon Glass80025522022-01-29 14:14:04 -07004226 expected = (U_BOOT_SPL_DTB_DATA + tools.get_bytes(0, 20) +
4227 tools.to_bytes(TEXT_DATA) + tools.get_bytes(0, 30) +
Alper Nebi Yasakc261ec12020-08-31 12:58:20 +03004228 U_BOOT_DTB_DATA)
4229 self.assertEqual(expected, data)
4230
4231 def testFitExtblobMissingOk(self):
4232 """Test a FIT with a missing external blob that is allowed"""
Simon Glass14d64e32025-04-29 07:21:59 -06004233 with terminal.capture() as (stdout, stderr):
Alper Nebi Yasakc261ec12020-08-31 12:58:20 +03004234 self._DoTestFile('168_fit_missing_blob.dts',
4235 allow_missing=True)
4236 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07004237 self.assertRegex(err, "Image 'image'.*missing.*: atf-bl31")
Alper Nebi Yasakc261ec12020-08-31 12:58:20 +03004238
Simon Glass21db0ff2020-09-01 05:13:54 -06004239 def testBlobNamedByArgMissing(self):
4240 """Test handling of a missing entry arg"""
4241 with self.assertRaises(ValueError) as e:
4242 self._DoReadFile('068_blob_named_by_arg.dts')
4243 self.assertIn("Missing required properties/entry args: cros-ec-rw-path",
4244 str(e.exception))
4245
Simon Glass559c4de2020-09-01 05:13:58 -06004246 def testPackBl31(self):
4247 """Test that an image with an ATF BL31 binary can be created"""
4248 data = self._DoReadFile('169_atf_bl31.dts')
4249 self.assertEqual(ATF_BL31_DATA, data[:len(ATF_BL31_DATA)])
4250
Samuel Holland9d8cc632020-10-21 21:12:15 -05004251 def testPackScp(self):
4252 """Test that an image with an SCP binary can be created"""
4253 data = self._DoReadFile('172_scp.dts')
4254 self.assertEqual(SCP_DATA, data[:len(SCP_DATA)])
4255
Simon Glassd2a9d6e2024-08-26 13:11:37 -06004256 def CheckFitFdt(self, dts='170_fit_fdt.dts', use_fdt_list=True,
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004257 default_dt=None, use_seq_num=True):
Simon Glasscd2783e2024-07-20 11:49:46 +01004258 """Check an image with an FIT with multiple FDT images"""
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004259 def _CheckFdt(val, expected_data):
Simon Glassa435cd12020-09-01 05:13:59 -06004260 """Check the FDT nodes
4261
4262 Args:
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004263 val: Sequence number to check (0 or 1) or fdt name
Simon Glassa435cd12020-09-01 05:13:59 -06004264 expected_data: Expected contents of 'data' property
4265 """
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004266 name = 'fdt-%s' % val
Simon Glassa435cd12020-09-01 05:13:59 -06004267 fnode = dtb.GetNode('/images/%s' % name)
4268 self.assertIsNotNone(fnode)
4269 self.assertEqual({'description','type', 'compression', 'data'},
4270 set(fnode.props.keys()))
4271 self.assertEqual(expected_data, fnode.props['data'].bytes)
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004272 description = (
4273 'fdt-test-fdt%s.dtb' % val if len(val) == 1 else
4274 'fdt-%s.dtb' % val
4275 )
4276 self.assertEqual(description, fnode.props['description'].value)
Jan Kiszkaa1419df2022-02-28 17:06:20 +01004277 self.assertEqual(fnode.subnodes[0].name, 'hash')
Simon Glassa435cd12020-09-01 05:13:59 -06004278
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004279 def _CheckConfig(val, expected_data):
Simon Glassa435cd12020-09-01 05:13:59 -06004280 """Check the configuration nodes
4281
4282 Args:
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004283 val: Sequence number to check (0 or 1) or fdt name
Simon Glassa435cd12020-09-01 05:13:59 -06004284 expected_data: Expected contents of 'data' property
4285 """
4286 cnode = dtb.GetNode('/configurations')
4287 self.assertIn('default', cnode.props)
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004288 default = (
4289 'config-2' if len(val) == 1 else
4290 'config-test-fdt2'
4291 )
4292 self.assertEqual(default, cnode.props['default'].value)
Simon Glassa435cd12020-09-01 05:13:59 -06004293
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004294 name = 'config-%s' % val
Simon Glassa435cd12020-09-01 05:13:59 -06004295 fnode = dtb.GetNode('/configurations/%s' % name)
4296 self.assertIsNotNone(fnode)
4297 self.assertEqual({'description','firmware', 'loadables', 'fdt'},
4298 set(fnode.props.keys()))
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004299 description = (
4300 'conf-test-fdt%s.dtb' % val if len(val) == 1 else
4301 'conf-%s.dtb' % val
4302 )
4303 self.assertEqual(description, fnode.props['description'].value)
4304 self.assertEqual('fdt-%s' % val, fnode.props['fdt'].value)
Simon Glassa435cd12020-09-01 05:13:59 -06004305
4306 entry_args = {
Simon Glass1032acc2020-09-06 10:39:08 -06004307 'default-dt': 'test-fdt2',
Simon Glassa435cd12020-09-01 05:13:59 -06004308 }
Paul HENRYS87a8e7c2024-11-25 19:16:54 +01004309 extra_indirs = None
Simon Glasscd2783e2024-07-20 11:49:46 +01004310 if use_fdt_list:
4311 entry_args['of-list'] = 'test-fdt1 test-fdt2'
Simon Glassd2a9d6e2024-08-26 13:11:37 -06004312 if default_dt:
4313 entry_args['default-dt'] = default_dt
Paul HENRYS87a8e7c2024-11-25 19:16:54 +01004314 if use_fdt_list:
4315 extra_indirs = [os.path.join(self._indir, TEST_FDT_SUBDIR)]
Simon Glassa435cd12020-09-01 05:13:59 -06004316 data = self._DoReadFileDtb(
Simon Glasscd2783e2024-07-20 11:49:46 +01004317 dts,
Simon Glassa435cd12020-09-01 05:13:59 -06004318 entry_args=entry_args,
Paul HENRYS87a8e7c2024-11-25 19:16:54 +01004319 extra_indirs=extra_indirs)[0]
Simon Glassa435cd12020-09-01 05:13:59 -06004320 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
4321 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
4322
4323 dtb = fdt.Fdt.FromData(fit_data)
4324 dtb.Scan()
4325 fnode = dtb.GetNode('/images/kernel')
4326 self.assertIn('data', fnode.props)
4327
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004328 if use_seq_num == True:
4329 # Check all the properties in fdt-1 and fdt-2
4330 _CheckFdt('1', TEST_FDT1_DATA)
4331 _CheckFdt('2', TEST_FDT2_DATA)
Simon Glassa435cd12020-09-01 05:13:59 -06004332
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004333 # Check configurations
4334 _CheckConfig('1', TEST_FDT1_DATA)
4335 _CheckConfig('2', TEST_FDT2_DATA)
4336 else:
4337 # Check all the properties in fdt-1 and fdt-2
4338 _CheckFdt('test-fdt1', TEST_FDT1_DATA)
4339 _CheckFdt('test-fdt2', TEST_FDT2_DATA)
4340
4341 # Check configurations
4342 _CheckConfig('test-fdt1', TEST_FDT1_DATA)
4343 _CheckConfig('test-fdt2', TEST_FDT2_DATA)
Simon Glassa435cd12020-09-01 05:13:59 -06004344
Simon Glasscd2783e2024-07-20 11:49:46 +01004345 def testFitFdt(self):
4346 """Test an image with an FIT with multiple FDT images"""
4347 self.CheckFitFdt()
4348
Simon Glassa435cd12020-09-01 05:13:59 -06004349 def testFitFdtMissingList(self):
4350 """Test handling of a missing 'of-list' entry arg"""
4351 with self.assertRaises(ValueError) as e:
Bin Meng16cf5662021-05-10 20:23:32 +08004352 self._DoReadFile('170_fit_fdt.dts')
Simon Glassa435cd12020-09-01 05:13:59 -06004353 self.assertIn("Generator node requires 'of-list' entry argument",
4354 str(e.exception))
4355
4356 def testFitFdtEmptyList(self):
4357 """Test handling of an empty 'of-list' entry arg"""
4358 entry_args = {
4359 'of-list': '',
4360 }
4361 data = self._DoReadFileDtb('170_fit_fdt.dts', entry_args=entry_args)[0]
4362
4363 def testFitFdtMissingProp(self):
4364 """Test handling of a missing 'fit,fdt-list' property"""
4365 with self.assertRaises(ValueError) as e:
4366 self._DoReadFile('171_fit_fdt_missing_prop.dts')
4367 self.assertIn("Generator node requires 'fit,fdt-list' property",
4368 str(e.exception))
Simon Glass559c4de2020-09-01 05:13:58 -06004369
Simon Glass1032acc2020-09-06 10:39:08 -06004370 def testFitFdtMissing(self):
4371 """Test handling of a missing 'default-dt' entry arg"""
4372 entry_args = {
4373 'of-list': 'test-fdt1 test-fdt2',
4374 }
4375 with self.assertRaises(ValueError) as e:
4376 self._DoReadFileDtb(
Bin Meng16cf5662021-05-10 20:23:32 +08004377 '170_fit_fdt.dts',
Simon Glass1032acc2020-09-06 10:39:08 -06004378 entry_args=entry_args,
4379 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
4380 self.assertIn("Generated 'default' node requires default-dt entry argument",
4381 str(e.exception))
4382
4383 def testFitFdtNotInList(self):
4384 """Test handling of a default-dt that is not in the of-list"""
4385 entry_args = {
4386 'of-list': 'test-fdt1 test-fdt2',
4387 'default-dt': 'test-fdt3',
4388 }
4389 with self.assertRaises(ValueError) as e:
4390 self._DoReadFileDtb(
Bin Meng16cf5662021-05-10 20:23:32 +08004391 '170_fit_fdt.dts',
Simon Glass1032acc2020-09-06 10:39:08 -06004392 entry_args=entry_args,
4393 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
4394 self.assertIn("default-dt entry argument 'test-fdt3' not found in fdt list: test-fdt1, test-fdt2",
4395 str(e.exception))
4396
Simon Glassa820af72020-09-06 10:39:09 -06004397 def testFitExtblobMissingHelp(self):
4398 """Test display of help messages when an external blob is missing"""
4399 control.missing_blob_help = control._ReadMissingBlobHelp()
4400 control.missing_blob_help['wibble'] = 'Wibble test'
4401 control.missing_blob_help['another'] = 'Another test'
Simon Glass14d64e32025-04-29 07:21:59 -06004402 with terminal.capture() as (stdout, stderr):
Simon Glassa820af72020-09-06 10:39:09 -06004403 self._DoTestFile('168_fit_missing_blob.dts',
4404 allow_missing=True)
4405 err = stderr.getvalue()
4406
4407 # We can get the tag from the name, the type or the missing-msg
4408 # property. Check all three.
4409 self.assertIn('You may need to build ARM Trusted', err)
4410 self.assertIn('Wibble test', err)
4411 self.assertIn('Another test', err)
4412
Simon Glass6f1f4d42020-09-06 10:35:32 -06004413 def testMissingBlob(self):
4414 """Test handling of a blob containing a missing file"""
4415 with self.assertRaises(ValueError) as e:
4416 self._DoTestFile('173_missing_blob.dts', allow_missing=True)
4417 self.assertIn("Filename 'missing' not found in input path",
4418 str(e.exception))
4419
Simon Glassa0729502020-09-06 10:35:33 -06004420 def testEnvironment(self):
4421 """Test adding a U-Boot environment"""
4422 data = self._DoReadFile('174_env.dts')
4423 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
4424 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
4425 env = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
4426 self.assertEqual(b'\x1b\x97\x22\x7c\x01var1=1\0var2="2"\0\0\xff\xff',
4427 env)
4428
4429 def testEnvironmentNoSize(self):
4430 """Test that a missing 'size' property is detected"""
4431 with self.assertRaises(ValueError) as e:
Simon Glass8cdc08a2020-10-26 17:40:00 -06004432 self._DoTestFile('175_env_no_size.dts')
Simon Glassa0729502020-09-06 10:35:33 -06004433 self.assertIn("'u-boot-env' entry must have a size property",
4434 str(e.exception))
4435
4436 def testEnvironmentTooSmall(self):
4437 """Test handling of an environment that does not fit"""
4438 with self.assertRaises(ValueError) as e:
Simon Glass8cdc08a2020-10-26 17:40:00 -06004439 self._DoTestFile('176_env_too_small.dts')
Simon Glassa0729502020-09-06 10:35:33 -06004440
4441 # checksum, start byte, environment with \0 terminator, final \0
4442 need = 4 + 1 + len(ENV_DATA) + 1 + 1
4443 short = need - 0x8
4444 self.assertIn("too small to hold data (need %#x more bytes)" % short,
4445 str(e.exception))
4446
Simon Glassd1fdf752020-10-26 17:40:01 -06004447 def testSkipAtStart(self):
4448 """Test handling of skip-at-start section"""
4449 data = self._DoReadFile('177_skip_at_start.dts')
4450 self.assertEqual(U_BOOT_DATA, data)
4451
4452 image = control.images['image']
4453 entries = image.GetEntries()
4454 section = entries['section']
4455 self.assertEqual(0, section.offset)
4456 self.assertEqual(len(U_BOOT_DATA), section.size)
4457 self.assertEqual(U_BOOT_DATA, section.GetData())
4458
4459 entry = section.GetEntries()['u-boot']
4460 self.assertEqual(16, entry.offset)
4461 self.assertEqual(len(U_BOOT_DATA), entry.size)
4462 self.assertEqual(U_BOOT_DATA, entry.data)
4463
4464 def testSkipAtStartPad(self):
4465 """Test handling of skip-at-start section with padded entry"""
4466 data = self._DoReadFile('178_skip_at_start_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07004467 before = tools.get_bytes(0, 8)
4468 after = tools.get_bytes(0, 4)
Simon Glassd1fdf752020-10-26 17:40:01 -06004469 all = before + U_BOOT_DATA + after
4470 self.assertEqual(all, data)
4471
4472 image = control.images['image']
4473 entries = image.GetEntries()
4474 section = entries['section']
4475 self.assertEqual(0, section.offset)
4476 self.assertEqual(len(all), section.size)
4477 self.assertEqual(all, section.GetData())
4478
4479 entry = section.GetEntries()['u-boot']
4480 self.assertEqual(16, entry.offset)
4481 self.assertEqual(len(all), entry.size)
4482 self.assertEqual(U_BOOT_DATA, entry.data)
4483
4484 def testSkipAtStartSectionPad(self):
4485 """Test handling of skip-at-start section with padding"""
4486 data = self._DoReadFile('179_skip_at_start_section_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07004487 before = tools.get_bytes(0, 8)
4488 after = tools.get_bytes(0, 4)
Simon Glassd1fdf752020-10-26 17:40:01 -06004489 all = before + U_BOOT_DATA + after
Simon Glass510ef0f2020-10-26 17:40:13 -06004490 self.assertEqual(all, data)
Simon Glassd1fdf752020-10-26 17:40:01 -06004491
4492 image = control.images['image']
4493 entries = image.GetEntries()
4494 section = entries['section']
4495 self.assertEqual(0, section.offset)
4496 self.assertEqual(len(all), section.size)
Simon Glass72eeff12020-10-26 17:40:16 -06004497 self.assertEqual(U_BOOT_DATA, section.data)
Simon Glass510ef0f2020-10-26 17:40:13 -06004498 self.assertEqual(all, section.GetPaddedData())
Simon Glassd1fdf752020-10-26 17:40:01 -06004499
4500 entry = section.GetEntries()['u-boot']
4501 self.assertEqual(16, entry.offset)
4502 self.assertEqual(len(U_BOOT_DATA), entry.size)
4503 self.assertEqual(U_BOOT_DATA, entry.data)
Simon Glassa0729502020-09-06 10:35:33 -06004504
Simon Glassbb395742020-10-26 17:40:14 -06004505 def testSectionPad(self):
4506 """Testing padding with sections"""
4507 data = self._DoReadFile('180_section_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07004508 expected = (tools.get_bytes(ord('&'), 3) +
4509 tools.get_bytes(ord('!'), 5) +
Simon Glassbb395742020-10-26 17:40:14 -06004510 U_BOOT_DATA +
Simon Glass80025522022-01-29 14:14:04 -07004511 tools.get_bytes(ord('!'), 1) +
4512 tools.get_bytes(ord('&'), 2))
Simon Glassbb395742020-10-26 17:40:14 -06004513 self.assertEqual(expected, data)
4514
4515 def testSectionAlign(self):
4516 """Testing alignment with sections"""
4517 data = self._DoReadFileDtb('181_section_align.dts', map=True)[0]
4518 expected = (b'\0' + # fill section
Simon Glass80025522022-01-29 14:14:04 -07004519 tools.get_bytes(ord('&'), 1) + # padding to section align
Simon Glassbb395742020-10-26 17:40:14 -06004520 b'\0' + # fill section
Simon Glass80025522022-01-29 14:14:04 -07004521 tools.get_bytes(ord('!'), 3) + # padding to u-boot align
Simon Glassbb395742020-10-26 17:40:14 -06004522 U_BOOT_DATA +
Simon Glass80025522022-01-29 14:14:04 -07004523 tools.get_bytes(ord('!'), 4) + # padding to u-boot size
4524 tools.get_bytes(ord('!'), 4)) # padding to section size
Simon Glassbb395742020-10-26 17:40:14 -06004525 self.assertEqual(expected, data)
4526
Simon Glassd92c8362020-10-26 17:40:25 -06004527 def testCompressImage(self):
4528 """Test compression of the entire image"""
4529 self._CheckLz4()
4530 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4531 '182_compress_image.dts', use_real_dtb=True, update_dtb=True)
4532 dtb = fdt.Fdt(out_dtb_fname)
4533 dtb.Scan()
4534 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4535 'uncomp-size'])
4536 orig = self._decompress(data)
Brandon Maiera657bc62024-06-04 16:16:05 +00004537 self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, orig)
Simon Glassd92c8362020-10-26 17:40:25 -06004538
4539 # Do a sanity check on various fields
4540 image = control.images['image']
4541 entries = image.GetEntries()
4542 self.assertEqual(2, len(entries))
4543
4544 entry = entries['blob']
4545 self.assertEqual(COMPRESS_DATA, entry.data)
4546 self.assertEqual(len(COMPRESS_DATA), entry.size)
4547
4548 entry = entries['u-boot']
4549 self.assertEqual(U_BOOT_DATA, entry.data)
4550 self.assertEqual(len(U_BOOT_DATA), entry.size)
4551
4552 self.assertEqual(len(data), image.size)
4553 self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, image.uncomp_data)
4554 self.assertEqual(len(COMPRESS_DATA + U_BOOT_DATA), image.uncomp_size)
4555 orig = self._decompress(image.data)
4556 self.assertEqual(orig, image.uncomp_data)
4557
4558 expected = {
4559 'blob:offset': 0,
4560 'blob:size': len(COMPRESS_DATA),
4561 'u-boot:offset': len(COMPRESS_DATA),
4562 'u-boot:size': len(U_BOOT_DATA),
4563 'uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4564 'offset': 0,
4565 'image-pos': 0,
4566 'size': len(data),
4567 }
4568 self.assertEqual(expected, props)
4569
4570 def testCompressImageLess(self):
4571 """Test compression where compression reduces the image size"""
4572 self._CheckLz4()
4573 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4574 '183_compress_image_less.dts', use_real_dtb=True, update_dtb=True)
4575 dtb = fdt.Fdt(out_dtb_fname)
4576 dtb.Scan()
4577 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4578 'uncomp-size'])
4579 orig = self._decompress(data)
4580
Brandon Maiera657bc62024-06-04 16:16:05 +00004581 self.assertEqual(COMPRESS_DATA + COMPRESS_DATA + U_BOOT_DATA, orig)
Simon Glassd92c8362020-10-26 17:40:25 -06004582
4583 # Do a sanity check on various fields
4584 image = control.images['image']
4585 entries = image.GetEntries()
4586 self.assertEqual(2, len(entries))
4587
4588 entry = entries['blob']
4589 self.assertEqual(COMPRESS_DATA_BIG, entry.data)
4590 self.assertEqual(len(COMPRESS_DATA_BIG), entry.size)
4591
4592 entry = entries['u-boot']
4593 self.assertEqual(U_BOOT_DATA, entry.data)
4594 self.assertEqual(len(U_BOOT_DATA), entry.size)
4595
4596 self.assertEqual(len(data), image.size)
4597 self.assertEqual(COMPRESS_DATA_BIG + U_BOOT_DATA, image.uncomp_data)
4598 self.assertEqual(len(COMPRESS_DATA_BIG + U_BOOT_DATA),
4599 image.uncomp_size)
4600 orig = self._decompress(image.data)
4601 self.assertEqual(orig, image.uncomp_data)
4602
4603 expected = {
4604 'blob:offset': 0,
4605 'blob:size': len(COMPRESS_DATA_BIG),
4606 'u-boot:offset': len(COMPRESS_DATA_BIG),
4607 'u-boot:size': len(U_BOOT_DATA),
4608 'uncomp-size': len(COMPRESS_DATA_BIG + U_BOOT_DATA),
4609 'offset': 0,
4610 'image-pos': 0,
4611 'size': len(data),
4612 }
4613 self.assertEqual(expected, props)
4614
4615 def testCompressSectionSize(self):
4616 """Test compression of a section with a fixed size"""
4617 self._CheckLz4()
4618 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4619 '184_compress_section_size.dts', use_real_dtb=True, update_dtb=True)
4620 dtb = fdt.Fdt(out_dtb_fname)
4621 dtb.Scan()
4622 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4623 'uncomp-size'])
Jiaxun Yangc6931742025-04-10 06:43:03 -06004624 data = data[:0x30]
4625 data = data.rstrip(b'\xff')
Simon Glassd92c8362020-10-26 17:40:25 -06004626 orig = self._decompress(data)
Brandon Maiera657bc62024-06-04 16:16:05 +00004627 self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, orig)
Simon Glassd92c8362020-10-26 17:40:25 -06004628 expected = {
4629 'section/blob:offset': 0,
4630 'section/blob:size': len(COMPRESS_DATA),
4631 'section/u-boot:offset': len(COMPRESS_DATA),
4632 'section/u-boot:size': len(U_BOOT_DATA),
4633 'section:offset': 0,
4634 'section:image-pos': 0,
4635 'section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4636 'section:size': 0x30,
4637 'offset': 0,
4638 'image-pos': 0,
4639 'size': 0x30,
4640 }
4641 self.assertEqual(expected, props)
4642
4643 def testCompressSection(self):
4644 """Test compression of a section with no fixed size"""
4645 self._CheckLz4()
4646 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4647 '185_compress_section.dts', use_real_dtb=True, update_dtb=True)
4648 dtb = fdt.Fdt(out_dtb_fname)
4649 dtb.Scan()
4650 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4651 'uncomp-size'])
4652 orig = self._decompress(data)
Brandon Maiera657bc62024-06-04 16:16:05 +00004653 self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, orig)
Simon Glassd92c8362020-10-26 17:40:25 -06004654 expected = {
4655 'section/blob:offset': 0,
4656 'section/blob:size': len(COMPRESS_DATA),
4657 'section/u-boot:offset': len(COMPRESS_DATA),
4658 'section/u-boot:size': len(U_BOOT_DATA),
4659 'section:offset': 0,
4660 'section:image-pos': 0,
4661 'section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4662 'section:size': len(data),
4663 'offset': 0,
4664 'image-pos': 0,
4665 'size': len(data),
4666 }
4667 self.assertEqual(expected, props)
4668
Stefan Herbrechtsmeier94813a02022-08-19 16:25:31 +02004669 def testLz4Missing(self):
4670 """Test that binman still produces an image if lz4 is missing"""
Simon Glass14d64e32025-04-29 07:21:59 -06004671 with terminal.capture() as (_, stderr):
Stefan Herbrechtsmeier94813a02022-08-19 16:25:31 +02004672 self._DoTestFile('185_compress_section.dts',
4673 force_missing_bintools='lz4')
4674 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07004675 self.assertRegex(err, "Image 'image'.*missing bintools.*: lz4")
Stefan Herbrechtsmeier94813a02022-08-19 16:25:31 +02004676
Simon Glassd92c8362020-10-26 17:40:25 -06004677 def testCompressExtra(self):
4678 """Test compression of a section with no fixed size"""
4679 self._CheckLz4()
4680 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4681 '186_compress_extra.dts', use_real_dtb=True, update_dtb=True)
4682 dtb = fdt.Fdt(out_dtb_fname)
4683 dtb.Scan()
4684 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4685 'uncomp-size'])
4686
4687 base = data[len(U_BOOT_DATA):]
Brandon Maiera657bc62024-06-04 16:16:05 +00004688 self.assertEqual(U_BOOT_DATA, base[:len(U_BOOT_DATA)])
Simon Glassd92c8362020-10-26 17:40:25 -06004689 rest = base[len(U_BOOT_DATA):]
4690
4691 # Check compressed data
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +02004692 bintool = self.comp_bintools['lz4']
4693 expect1 = bintool.compress(COMPRESS_DATA + U_BOOT_DATA)
Stefan Herbrechtsmeier86f1fc02022-08-19 16:25:23 +02004694 data1 = rest[:len(expect1)]
4695 section1 = self._decompress(data1)
Brandon Maiera657bc62024-06-04 16:16:05 +00004696 self.assertEqual(expect1, data1)
4697 self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, section1)
Simon Glassd92c8362020-10-26 17:40:25 -06004698 rest1 = rest[len(expect1):]
4699
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +02004700 expect2 = bintool.compress(COMPRESS_DATA + COMPRESS_DATA)
Stefan Herbrechtsmeier86f1fc02022-08-19 16:25:23 +02004701 data2 = rest1[:len(expect2)]
4702 section2 = self._decompress(data2)
Brandon Maiera657bc62024-06-04 16:16:05 +00004703 self.assertEqual(expect2, data2)
4704 self.assertEqual(COMPRESS_DATA + COMPRESS_DATA, section2)
Simon Glassd92c8362020-10-26 17:40:25 -06004705 rest2 = rest1[len(expect2):]
4706
4707 expect_size = (len(U_BOOT_DATA) + len(U_BOOT_DATA) + len(expect1) +
4708 len(expect2) + len(U_BOOT_DATA))
Brandon Maiera657bc62024-06-04 16:16:05 +00004709 #self.assertEqual(expect_size, len(data))
Simon Glassd92c8362020-10-26 17:40:25 -06004710
Brandon Maiera657bc62024-06-04 16:16:05 +00004711 #self.assertEqual(U_BOOT_DATA, rest2)
Simon Glassd92c8362020-10-26 17:40:25 -06004712
4713 self.maxDiff = None
4714 expected = {
4715 'u-boot:offset': 0,
4716 'u-boot:image-pos': 0,
4717 'u-boot:size': len(U_BOOT_DATA),
4718
4719 'base:offset': len(U_BOOT_DATA),
4720 'base:image-pos': len(U_BOOT_DATA),
4721 'base:size': len(data) - len(U_BOOT_DATA),
4722 'base/u-boot:offset': 0,
4723 'base/u-boot:image-pos': len(U_BOOT_DATA),
4724 'base/u-boot:size': len(U_BOOT_DATA),
4725 'base/u-boot2:offset': len(U_BOOT_DATA) + len(expect1) +
4726 len(expect2),
4727 'base/u-boot2:image-pos': len(U_BOOT_DATA) * 2 + len(expect1) +
4728 len(expect2),
4729 'base/u-boot2:size': len(U_BOOT_DATA),
4730
4731 'base/section:offset': len(U_BOOT_DATA),
4732 'base/section:image-pos': len(U_BOOT_DATA) * 2,
4733 'base/section:size': len(expect1),
4734 'base/section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4735 'base/section/blob:offset': 0,
4736 'base/section/blob:size': len(COMPRESS_DATA),
4737 'base/section/u-boot:offset': len(COMPRESS_DATA),
4738 'base/section/u-boot:size': len(U_BOOT_DATA),
4739
4740 'base/section2:offset': len(U_BOOT_DATA) + len(expect1),
4741 'base/section2:image-pos': len(U_BOOT_DATA) * 2 + len(expect1),
4742 'base/section2:size': len(expect2),
4743 'base/section2:uncomp-size': len(COMPRESS_DATA + COMPRESS_DATA),
4744 'base/section2/blob:offset': 0,
4745 'base/section2/blob:size': len(COMPRESS_DATA),
4746 'base/section2/blob2:offset': len(COMPRESS_DATA),
4747 'base/section2/blob2:size': len(COMPRESS_DATA),
4748
4749 'offset': 0,
4750 'image-pos': 0,
4751 'size': len(data),
4752 }
4753 self.assertEqual(expected, props)
4754
Simon Glassecbe4732021-01-06 21:35:15 -07004755 def testSymbolsSubsection(self):
4756 """Test binman can assign symbols from a subsection"""
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03004757 self.checkSymbols('187_symbols_sub.dts', U_BOOT_SPL_DATA, 0x1c)
Simon Glassecbe4732021-01-06 21:35:15 -07004758
Simon Glass3fb25402021-01-06 21:35:16 -07004759 def testReadImageEntryArg(self):
4760 """Test reading an image that would need an entry arg to generate"""
4761 entry_args = {
4762 'cros-ec-rw-path': 'ecrw.bin',
4763 }
4764 data = self.data = self._DoReadFileDtb(
4765 '188_image_entryarg.dts',use_real_dtb=True, update_dtb=True,
4766 entry_args=entry_args)
4767
Simon Glass80025522022-01-29 14:14:04 -07004768 image_fname = tools.get_output_filename('image.bin')
Simon Glass3fb25402021-01-06 21:35:16 -07004769 orig_image = control.images['image']
4770
4771 # This should not generate an error about the missing 'cros-ec-rw-path'
4772 # since we are reading the image from a file. Compare with
4773 # testEntryArgsRequired()
4774 image = Image.FromFile(image_fname)
4775 self.assertEqual(orig_image.GetEntries().keys(),
4776 image.GetEntries().keys())
4777
Simon Glassa2af7302021-01-06 21:35:18 -07004778 def testFilesAlign(self):
4779 """Test alignment with files"""
4780 data = self._DoReadFile('190_files_align.dts')
4781
4782 # The first string is 15 bytes so will align to 16
4783 expect = FILES_DATA[:15] + b'\0' + FILES_DATA[15:]
4784 self.assertEqual(expect, data)
4785
Simon Glassdb84b562021-01-06 21:35:19 -07004786 def testReadImageSkip(self):
4787 """Test reading an image and accessing its FDT map"""
4788 data = self.data = self._DoReadFileRealDtb('191_read_image_skip.dts')
Simon Glass80025522022-01-29 14:14:04 -07004789 image_fname = tools.get_output_filename('image.bin')
Simon Glassdb84b562021-01-06 21:35:19 -07004790 orig_image = control.images['image']
4791 image = Image.FromFile(image_fname)
4792 self.assertEqual(orig_image.GetEntries().keys(),
4793 image.GetEntries().keys())
4794
4795 orig_entry = orig_image.GetEntries()['fdtmap']
4796 entry = image.GetEntries()['fdtmap']
4797 self.assertEqual(orig_entry.offset, entry.offset)
4798 self.assertEqual(orig_entry.size, entry.size)
Simon Glassed836ac2025-02-26 09:26:17 -07004799 self.assertEqual((1 << 32) - 0x400 + 16, entry.image_pos)
Simon Glassdb84b562021-01-06 21:35:19 -07004800
4801 u_boot = image.GetEntries()['section'].GetEntries()['u-boot']
4802
Brandon Maiera657bc62024-06-04 16:16:05 +00004803 self.assertEqual(U_BOOT_DATA, u_boot.ReadData())
Simon Glassdb84b562021-01-06 21:35:19 -07004804
Simon Glassc98de972021-03-18 20:24:57 +13004805 def testTplNoDtb(self):
4806 """Test that an image with tpl/u-boot-tpl-nodtb.bin can be created"""
Simon Glass13089cc2021-04-25 08:39:32 +12004807 self._SetupTplElf()
Simon Glassc98de972021-03-18 20:24:57 +13004808 data = self._DoReadFile('192_u_boot_tpl_nodtb.dts')
4809 self.assertEqual(U_BOOT_TPL_NODTB_DATA,
4810 data[:len(U_BOOT_TPL_NODTB_DATA)])
4811
Simon Glass63f41d42021-03-18 20:24:58 +13004812 def testTplBssPad(self):
4813 """Test that we can pad TPL's BSS with zeros"""
4814 # ELF file with a '__bss_size' symbol
4815 self._SetupTplElf()
4816 data = self._DoReadFile('193_tpl_bss_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07004817 self.assertEqual(U_BOOT_TPL_DATA + tools.get_bytes(0, 10) + U_BOOT_DATA,
Simon Glass63f41d42021-03-18 20:24:58 +13004818 data)
4819
4820 def testTplBssPadMissing(self):
4821 """Test that a missing symbol is detected"""
4822 self._SetupTplElf('u_boot_ucode_ptr')
4823 with self.assertRaises(ValueError) as e:
4824 self._DoReadFile('193_tpl_bss_pad.dts')
4825 self.assertIn('Expected __bss_size symbol in tpl/u-boot-tpl',
4826 str(e.exception))
4827
Simon Glass718b5292021-03-18 20:25:07 +13004828 def checkDtbSizes(self, data, pad_len, start):
4829 """Check the size arguments in a dtb embedded in an image
4830
4831 Args:
4832 data: The image data
4833 pad_len: Length of the pad section in the image, in bytes
4834 start: Start offset of the devicetree to examine, within the image
4835
4836 Returns:
4837 Size of the devicetree in bytes
4838 """
4839 dtb_data = data[start:]
4840 dtb = fdt.Fdt.FromData(dtb_data)
4841 fdt_size = dtb.GetFdtObj().totalsize()
4842 dtb.Scan()
4843 props = self._GetPropTree(dtb, 'size')
4844 self.assertEqual({
4845 'size': len(data),
4846 'u-boot-spl/u-boot-spl-bss-pad:size': pad_len,
4847 'u-boot-spl/u-boot-spl-dtb:size': 801,
4848 'u-boot-spl/u-boot-spl-nodtb:size': len(U_BOOT_SPL_NODTB_DATA),
4849 'u-boot-spl:size': 860,
4850 'u-boot-tpl:size': len(U_BOOT_TPL_DATA),
4851 'u-boot/u-boot-dtb:size': 781,
4852 'u-boot/u-boot-nodtb:size': len(U_BOOT_NODTB_DATA),
4853 'u-boot:size': 827,
4854 }, props)
4855 return fdt_size
4856
4857 def testExpanded(self):
4858 """Test that an expanded entry type is selected when needed"""
4859 self._SetupSplElf()
4860 self._SetupTplElf()
4861
4862 # SPL has a devicetree, TPL does not
4863 entry_args = {
4864 'spl-dtb': '1',
4865 'spl-bss-pad': 'y',
4866 'tpl-dtb': '',
4867 }
4868 self._DoReadFileDtb('194_fdt_incl.dts', use_expanded=True,
4869 entry_args=entry_args)
4870 image = control.images['image']
4871 entries = image.GetEntries()
4872 self.assertEqual(3, len(entries))
4873
4874 # First, u-boot, which should be expanded into u-boot-nodtb and dtb
4875 self.assertIn('u-boot', entries)
4876 entry = entries['u-boot']
4877 self.assertEqual('u-boot-expanded', entry.etype)
4878 subent = entry.GetEntries()
4879 self.assertEqual(2, len(subent))
4880 self.assertIn('u-boot-nodtb', subent)
4881 self.assertIn('u-boot-dtb', subent)
4882
4883 # Second, u-boot-spl, which should be expanded into three parts
4884 self.assertIn('u-boot-spl', entries)
4885 entry = entries['u-boot-spl']
4886 self.assertEqual('u-boot-spl-expanded', entry.etype)
4887 subent = entry.GetEntries()
4888 self.assertEqual(3, len(subent))
4889 self.assertIn('u-boot-spl-nodtb', subent)
4890 self.assertIn('u-boot-spl-bss-pad', subent)
4891 self.assertIn('u-boot-spl-dtb', subent)
4892
4893 # Third, u-boot-tpl, which should be not be expanded, since TPL has no
4894 # devicetree
4895 self.assertIn('u-boot-tpl', entries)
4896 entry = entries['u-boot-tpl']
4897 self.assertEqual('u-boot-tpl', entry.etype)
4898 self.assertEqual(None, entry.GetEntries())
4899
4900 def testExpandedTpl(self):
4901 """Test that an expanded entry type is selected for TPL when needed"""
4902 self._SetupTplElf()
4903
4904 entry_args = {
4905 'tpl-bss-pad': 'y',
4906 'tpl-dtb': 'y',
4907 }
4908 self._DoReadFileDtb('195_fdt_incl_tpl.dts', use_expanded=True,
4909 entry_args=entry_args)
4910 image = control.images['image']
4911 entries = image.GetEntries()
4912 self.assertEqual(1, len(entries))
4913
4914 # We only have u-boot-tpl, which be expanded
4915 self.assertIn('u-boot-tpl', entries)
4916 entry = entries['u-boot-tpl']
4917 self.assertEqual('u-boot-tpl-expanded', entry.etype)
4918 subent = entry.GetEntries()
4919 self.assertEqual(3, len(subent))
4920 self.assertIn('u-boot-tpl-nodtb', subent)
4921 self.assertIn('u-boot-tpl-bss-pad', subent)
4922 self.assertIn('u-boot-tpl-dtb', subent)
4923
4924 def testExpandedNoPad(self):
4925 """Test an expanded entry without BSS pad enabled"""
4926 self._SetupSplElf()
4927 self._SetupTplElf()
4928
4929 # SPL has a devicetree, TPL does not
4930 entry_args = {
4931 'spl-dtb': 'something',
4932 'spl-bss-pad': 'n',
4933 'tpl-dtb': '',
4934 }
4935 self._DoReadFileDtb('194_fdt_incl.dts', use_expanded=True,
4936 entry_args=entry_args)
4937 image = control.images['image']
4938 entries = image.GetEntries()
4939
4940 # Just check u-boot-spl, which should be expanded into two parts
4941 self.assertIn('u-boot-spl', entries)
4942 entry = entries['u-boot-spl']
4943 self.assertEqual('u-boot-spl-expanded', entry.etype)
4944 subent = entry.GetEntries()
4945 self.assertEqual(2, len(subent))
4946 self.assertIn('u-boot-spl-nodtb', subent)
4947 self.assertIn('u-boot-spl-dtb', subent)
4948
4949 def testExpandedTplNoPad(self):
4950 """Test that an expanded entry type with padding disabled in TPL"""
4951 self._SetupTplElf()
4952
4953 entry_args = {
4954 'tpl-bss-pad': '',
4955 'tpl-dtb': 'y',
4956 }
4957 self._DoReadFileDtb('195_fdt_incl_tpl.dts', use_expanded=True,
4958 entry_args=entry_args)
4959 image = control.images['image']
4960 entries = image.GetEntries()
4961 self.assertEqual(1, len(entries))
4962
4963 # We only have u-boot-tpl, which be expanded
4964 self.assertIn('u-boot-tpl', entries)
4965 entry = entries['u-boot-tpl']
4966 self.assertEqual('u-boot-tpl-expanded', entry.etype)
4967 subent = entry.GetEntries()
4968 self.assertEqual(2, len(subent))
4969 self.assertIn('u-boot-tpl-nodtb', subent)
4970 self.assertIn('u-boot-tpl-dtb', subent)
4971
4972 def testFdtInclude(self):
4973 """Test that an Fdt is update within all binaries"""
4974 self._SetupSplElf()
4975 self._SetupTplElf()
4976
4977 # SPL has a devicetree, TPL does not
4978 self.maxDiff = None
4979 entry_args = {
4980 'spl-dtb': '1',
4981 'spl-bss-pad': 'y',
4982 'tpl-dtb': '',
4983 }
4984 # Build the image. It includes two separate devicetree binaries, each
4985 # with their own contents, but all contain the binman definition.
4986 data = self._DoReadFileDtb(
4987 '194_fdt_incl.dts', use_real_dtb=True, use_expanded=True,
4988 update_dtb=True, entry_args=entry_args)[0]
4989 pad_len = 10
4990
4991 # Check the U-Boot dtb
4992 start = len(U_BOOT_NODTB_DATA)
4993 fdt_size = self.checkDtbSizes(data, pad_len, start)
4994
4995 # Now check SPL
4996 start += fdt_size + len(U_BOOT_SPL_NODTB_DATA) + pad_len
4997 fdt_size = self.checkDtbSizes(data, pad_len, start)
4998
4999 # TPL has no devicetree
5000 start += fdt_size + len(U_BOOT_TPL_DATA)
5001 self.assertEqual(len(data), start)
Simon Glassbb395742020-10-26 17:40:14 -06005002
Simon Glass7098b7f2021-03-21 18:24:30 +13005003 def testSymbolsExpanded(self):
5004 """Test binman can assign symbols in expanded entries"""
5005 entry_args = {
5006 'spl-dtb': '1',
5007 }
5008 self.checkSymbols('197_symbols_expand.dts', U_BOOT_SPL_NODTB_DATA +
5009 U_BOOT_SPL_DTB_DATA, 0x38,
5010 entry_args=entry_args, use_expanded=True)
5011
Simon Glasse1915782021-03-21 18:24:31 +13005012 def testCollection(self):
5013 """Test a collection"""
5014 data = self._DoReadFile('198_collection.dts')
5015 self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA +
Simon Glass80025522022-01-29 14:14:04 -07005016 tools.get_bytes(0xff, 2) + U_BOOT_NODTB_DATA +
5017 tools.get_bytes(0xfe, 3) + U_BOOT_DTB_DATA,
Simon Glasse1915782021-03-21 18:24:31 +13005018 data)
5019
Simon Glass27a7f772021-03-21 18:24:32 +13005020 def testCollectionSection(self):
5021 """Test a collection where a section must be built first"""
5022 # Sections never have their contents when GetData() is called, but when
Simon Glass7e3f89f2021-11-23 11:03:47 -07005023 # BuildSectionData() is called with required=True, a section will force
Simon Glass27a7f772021-03-21 18:24:32 +13005024 # building the contents, producing an error is anything is still
5025 # missing.
5026 data = self._DoReadFile('199_collection_section.dts')
5027 section = U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA
Simon Glass80025522022-01-29 14:14:04 -07005028 self.assertEqual(section + U_BOOT_DATA + tools.get_bytes(0xff, 2) +
5029 section + tools.get_bytes(0xfe, 3) + U_BOOT_DATA,
Simon Glass27a7f772021-03-21 18:24:32 +13005030 data)
5031
Simon Glassf427c5f2021-03-21 18:24:33 +13005032 def testAlignDefault(self):
5033 """Test that default alignment works on sections"""
5034 data = self._DoReadFile('200_align_default.dts')
Simon Glass80025522022-01-29 14:14:04 -07005035 expected = (U_BOOT_DATA + tools.get_bytes(0, 8 - len(U_BOOT_DATA)) +
Simon Glassf427c5f2021-03-21 18:24:33 +13005036 U_BOOT_DATA)
5037 # Special alignment for section
Simon Glass80025522022-01-29 14:14:04 -07005038 expected += tools.get_bytes(0, 32 - len(expected))
Simon Glassf427c5f2021-03-21 18:24:33 +13005039 # No alignment within the nested section
5040 expected += U_BOOT_DATA + U_BOOT_NODTB_DATA;
5041 # Now the final piece, which should be default-aligned
Simon Glass80025522022-01-29 14:14:04 -07005042 expected += tools.get_bytes(0, 88 - len(expected)) + U_BOOT_NODTB_DATA
Simon Glassf427c5f2021-03-21 18:24:33 +13005043 self.assertEqual(expected, data)
Simon Glass27a7f772021-03-21 18:24:32 +13005044
Bin Mengc0b15742021-05-10 20:23:33 +08005045 def testPackOpenSBI(self):
5046 """Test that an image with an OpenSBI binary can be created"""
5047 data = self._DoReadFile('201_opensbi.dts')
5048 self.assertEqual(OPENSBI_DATA, data[:len(OPENSBI_DATA)])
5049
Simon Glass76f496d2021-07-06 10:36:37 -06005050 def testSectionsSingleThread(self):
5051 """Test sections without multithreading"""
5052 data = self._DoReadFileDtb('055_sections.dts', threads=0)[0]
Simon Glass80025522022-01-29 14:14:04 -07005053 expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) +
5054 U_BOOT_DATA + tools.get_bytes(ord('a'), 12) +
5055 U_BOOT_DATA + tools.get_bytes(ord('&'), 4))
Simon Glass76f496d2021-07-06 10:36:37 -06005056 self.assertEqual(expected, data)
5057
5058 def testThreadTimeout(self):
5059 """Test handling a thread that takes too long"""
5060 with self.assertRaises(ValueError) as e:
5061 self._DoTestFile('202_section_timeout.dts',
5062 test_section_timeout=True)
Simon Glass2d59d152021-10-18 12:13:15 -06005063 self.assertIn("Timed out obtaining contents", str(e.exception))
Simon Glass76f496d2021-07-06 10:36:37 -06005064
Simon Glass748a1d42021-07-06 10:36:41 -06005065 def testTiming(self):
5066 """Test output of timing information"""
5067 data = self._DoReadFile('055_sections.dts')
Simon Glass14d64e32025-04-29 07:21:59 -06005068 with terminal.capture() as (stdout, stderr):
Simon Glass748a1d42021-07-06 10:36:41 -06005069 state.TimingShow()
5070 self.assertIn('read:', stdout.getvalue())
5071 self.assertIn('compress:', stdout.getvalue())
5072
Simon Glassadfb8492021-11-03 21:09:18 -06005073 def testUpdateFdtInElf(self):
5074 """Test that we can update the devicetree in an ELF file"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005075 if not elf.ELF_TOOLS:
5076 self.skipTest('Python elftools not available')
Simon Glassadfb8492021-11-03 21:09:18 -06005077 infile = elf_fname = self.ElfTestFile('u_boot_binman_embed')
5078 outfile = os.path.join(self._indir, 'u-boot.out')
5079 begin_sym = 'dtb_embed_begin'
5080 end_sym = 'dtb_embed_end'
5081 retcode = self._DoTestFile(
5082 '060_fdt_update.dts', update_dtb=True,
5083 update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym]))
5084 self.assertEqual(0, retcode)
5085
5086 # Check that the output file does in fact contact a dtb with the binman
5087 # definition in the correct place
5088 syms = elf.GetSymbolFileOffset(infile,
5089 ['dtb_embed_begin', 'dtb_embed_end'])
Simon Glass80025522022-01-29 14:14:04 -07005090 data = tools.read_file(outfile)
Simon Glassadfb8492021-11-03 21:09:18 -06005091 dtb_data = data[syms['dtb_embed_begin'].offset:
5092 syms['dtb_embed_end'].offset]
5093
5094 dtb = fdt.Fdt.FromData(dtb_data)
5095 dtb.Scan()
5096 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
5097 self.assertEqual({
5098 'image-pos': 0,
5099 'offset': 0,
5100 '_testing:offset': 32,
5101 '_testing:size': 2,
5102 '_testing:image-pos': 32,
5103 'section@0/u-boot:offset': 0,
5104 'section@0/u-boot:size': len(U_BOOT_DATA),
5105 'section@0/u-boot:image-pos': 0,
5106 'section@0:offset': 0,
5107 'section@0:size': 16,
5108 'section@0:image-pos': 0,
5109
5110 'section@1/u-boot:offset': 0,
5111 'section@1/u-boot:size': len(U_BOOT_DATA),
5112 'section@1/u-boot:image-pos': 16,
5113 'section@1:offset': 16,
5114 'section@1:size': 16,
5115 'section@1:image-pos': 16,
5116 'size': 40
5117 }, props)
5118
5119 def testUpdateFdtInElfInvalid(self):
5120 """Test that invalid args are detected with --update-fdt-in-elf"""
5121 with self.assertRaises(ValueError) as e:
5122 self._DoTestFile('060_fdt_update.dts', update_fdt_in_elf='fred')
5123 self.assertIn("Invalid args ['fred'] to --update-fdt-in-elf",
5124 str(e.exception))
5125
5126 def testUpdateFdtInElfNoSyms(self):
5127 """Test that missing symbols are detected with --update-fdt-in-elf"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005128 if not elf.ELF_TOOLS:
5129 self.skipTest('Python elftools not available')
Simon Glassadfb8492021-11-03 21:09:18 -06005130 infile = elf_fname = self.ElfTestFile('u_boot_binman_embed')
5131 outfile = ''
5132 begin_sym = 'wrong_begin'
5133 end_sym = 'wrong_end'
5134 with self.assertRaises(ValueError) as e:
5135 self._DoTestFile(
5136 '060_fdt_update.dts',
5137 update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym]))
5138 self.assertIn("Expected two symbols 'wrong_begin' and 'wrong_end': got 0:",
5139 str(e.exception))
5140
5141 def testUpdateFdtInElfTooSmall(self):
5142 """Test that an over-large dtb is detected with --update-fdt-in-elf"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005143 if not elf.ELF_TOOLS:
5144 self.skipTest('Python elftools not available')
Simon Glassadfb8492021-11-03 21:09:18 -06005145 infile = elf_fname = self.ElfTestFile('u_boot_binman_embed_sm')
5146 outfile = os.path.join(self._indir, 'u-boot.out')
5147 begin_sym = 'dtb_embed_begin'
5148 end_sym = 'dtb_embed_end'
5149 with self.assertRaises(ValueError) as e:
5150 self._DoTestFile(
5151 '060_fdt_update.dts', update_dtb=True,
5152 update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym]))
5153 self.assertRegex(
5154 str(e.exception),
5155 "Not enough space in '.*u_boot_binman_embed_sm' for data length.*")
5156
Simon Glass88e04da2021-11-23 11:03:42 -07005157 def testVersion(self):
5158 """Test we can get the binman version"""
5159 version = '(unreleased)'
5160 self.assertEqual(version, state.GetVersion(self._indir))
5161
5162 with self.assertRaises(SystemExit):
Simon Glass14d64e32025-04-29 07:21:59 -06005163 with terminal.capture() as (_, stderr):
Simon Glass88e04da2021-11-23 11:03:42 -07005164 self._DoBinman('-V')
5165 self.assertEqual('Binman %s\n' % version, stderr.getvalue())
5166
5167 # Try running the tool too, just to be safe
5168 result = self._RunBinman('-V')
5169 self.assertEqual('Binman %s\n' % version, result.stderr)
5170
5171 # Set up a version file to make sure that works
5172 version = 'v2025.01-rc2'
Simon Glass80025522022-01-29 14:14:04 -07005173 tools.write_file(os.path.join(self._indir, 'version'), version,
Simon Glass88e04da2021-11-23 11:03:42 -07005174 binary=False)
5175 self.assertEqual(version, state.GetVersion(self._indir))
5176
Simon Glass637958f2021-11-23 21:09:50 -07005177 def testAltFormat(self):
5178 """Test that alternative formats can be used to extract"""
5179 self._DoReadFileRealDtb('213_fdtmap_alt_format.dts')
5180
5181 try:
5182 tmpdir, updated_fname = self._SetupImageInTmpdir()
Simon Glass14d64e32025-04-29 07:21:59 -06005183 with terminal.capture() as (stdout, _):
Simon Glass637958f2021-11-23 21:09:50 -07005184 self._DoBinman('extract', '-i', updated_fname, '-F', 'list')
5185 self.assertEqual(
5186 '''Flag (-F) Entry type Description
5187fdt fdtmap Extract the devicetree blob from the fdtmap
5188''',
5189 stdout.getvalue())
5190
5191 dtb = os.path.join(tmpdir, 'fdt.dtb')
5192 self._DoBinman('extract', '-i', updated_fname, '-F', 'fdt', '-f',
5193 dtb, 'fdtmap')
5194
5195 # Check that we can read it and it can be scanning, meaning it does
5196 # not have a 16-byte fdtmap header
Simon Glass80025522022-01-29 14:14:04 -07005197 data = tools.read_file(dtb)
Simon Glass637958f2021-11-23 21:09:50 -07005198 dtb = fdt.Fdt.FromData(data)
5199 dtb.Scan()
5200
5201 # Now check u-boot which has no alt_format
5202 fname = os.path.join(tmpdir, 'fdt.dtb')
5203 self._DoBinman('extract', '-i', updated_fname, '-F', 'dummy',
5204 '-f', fname, 'u-boot')
Simon Glass80025522022-01-29 14:14:04 -07005205 data = tools.read_file(fname)
Simon Glass637958f2021-11-23 21:09:50 -07005206 self.assertEqual(U_BOOT_DATA, data)
5207
5208 finally:
5209 shutil.rmtree(tmpdir)
5210
Simon Glass0b00ae62021-11-23 21:09:52 -07005211 def testExtblobList(self):
5212 """Test an image with an external blob list"""
5213 data = self._DoReadFile('215_blob_ext_list.dts')
5214 self.assertEqual(REFCODE_DATA + FSP_M_DATA, data)
5215
5216 def testExtblobListMissing(self):
5217 """Test an image with a missing external blob"""
5218 with self.assertRaises(ValueError) as e:
5219 self._DoReadFile('216_blob_ext_list_missing.dts')
5220 self.assertIn("Filename 'missing-file' not found in input path",
5221 str(e.exception))
5222
5223 def testExtblobListMissingOk(self):
5224 """Test an image with an missing external blob that is allowed"""
Simon Glass14d64e32025-04-29 07:21:59 -06005225 with terminal.capture() as (stdout, stderr):
Simon Glass0b00ae62021-11-23 21:09:52 -07005226 self._DoTestFile('216_blob_ext_list_missing.dts',
5227 allow_missing=True)
5228 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07005229 self.assertRegex(err, "Image 'image'.*missing.*: blob-ext")
Simon Glass0b00ae62021-11-23 21:09:52 -07005230
Simon Glass3efb2972021-11-23 21:08:59 -07005231 def testFip(self):
5232 """Basic test of generation of an ARM Firmware Image Package (FIP)"""
5233 data = self._DoReadFile('203_fip.dts')
5234 hdr, fents = fip_util.decode_fip(data)
5235 self.assertEqual(fip_util.HEADER_MAGIC, hdr.name)
5236 self.assertEqual(fip_util.HEADER_SERIAL, hdr.serial)
5237 self.assertEqual(0x123, hdr.flags)
5238
5239 self.assertEqual(2, len(fents))
5240
5241 fent = fents[0]
5242 self.assertEqual(
5243 bytes([0x47, 0xd4, 0x08, 0x6d, 0x4c, 0xfe, 0x98, 0x46,
5244 0x9b, 0x95, 0x29, 0x50, 0xcb, 0xbd, 0x5a, 0x0]), fent.uuid)
5245 self.assertEqual('soc-fw', fent.fip_type)
5246 self.assertEqual(0x88, fent.offset)
5247 self.assertEqual(len(ATF_BL31_DATA), fent.size)
5248 self.assertEqual(0x123456789abcdef, fent.flags)
5249 self.assertEqual(ATF_BL31_DATA, fent.data)
5250 self.assertEqual(True, fent.valid)
5251
5252 fent = fents[1]
5253 self.assertEqual(
5254 bytes([0x65, 0x92, 0x27, 0x03, 0x2f, 0x74, 0xe6, 0x44,
5255 0x8d, 0xff, 0x57, 0x9a, 0xc1, 0xff, 0x06, 0x10]), fent.uuid)
5256 self.assertEqual('scp-fwu-cfg', fent.fip_type)
5257 self.assertEqual(0x8c, fent.offset)
5258 self.assertEqual(len(ATF_BL31_DATA), fent.size)
5259 self.assertEqual(0, fent.flags)
5260 self.assertEqual(ATF_BL2U_DATA, fent.data)
5261 self.assertEqual(True, fent.valid)
5262
5263 def testFipOther(self):
5264 """Basic FIP with something that isn't a external blob"""
5265 data = self._DoReadFile('204_fip_other.dts')
5266 hdr, fents = fip_util.decode_fip(data)
5267
5268 self.assertEqual(2, len(fents))
5269 fent = fents[1]
5270 self.assertEqual('rot-cert', fent.fip_type)
5271 self.assertEqual(b'aa', fent.data)
5272
Simon Glass3efb2972021-11-23 21:08:59 -07005273 def testFipNoType(self):
5274 """FIP with an entry of an unknown type"""
5275 with self.assertRaises(ValueError) as e:
5276 self._DoReadFile('205_fip_no_type.dts')
5277 self.assertIn("Must provide a fip-type (node name 'u-boot' is not a known FIP type)",
5278 str(e.exception))
5279
5280 def testFipUuid(self):
5281 """Basic FIP with a manual uuid"""
5282 data = self._DoReadFile('206_fip_uuid.dts')
5283 hdr, fents = fip_util.decode_fip(data)
5284
5285 self.assertEqual(2, len(fents))
5286 fent = fents[1]
5287 self.assertEqual(None, fent.fip_type)
5288 self.assertEqual(
5289 bytes([0xfc, 0x65, 0x13, 0x92, 0x4a, 0x5b, 0x11, 0xec,
5290 0x94, 0x35, 0xff, 0x2d, 0x1c, 0xfc, 0x79, 0x9c]),
5291 fent.uuid)
5292 self.assertEqual(U_BOOT_DATA, fent.data)
5293
5294 def testFipLs(self):
5295 """Test listing a FIP"""
5296 data = self._DoReadFileRealDtb('207_fip_ls.dts')
5297 hdr, fents = fip_util.decode_fip(data)
5298
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01005299 tmpdir = None
Simon Glass3efb2972021-11-23 21:08:59 -07005300 try:
5301 tmpdir, updated_fname = self._SetupImageInTmpdir()
Simon Glass14d64e32025-04-29 07:21:59 -06005302 with terminal.capture() as (stdout, stderr):
Simon Glass3efb2972021-11-23 21:08:59 -07005303 self._DoBinman('ls', '-i', updated_fname)
5304 finally:
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01005305 if tmpdir:
5306 shutil.rmtree(tmpdir)
Simon Glass3efb2972021-11-23 21:08:59 -07005307 lines = stdout.getvalue().splitlines()
5308 expected = [
Simon Glass49cd2b32023-02-07 14:34:18 -07005309'Name Image-pos Size Entry-type Offset Uncomp-size',
5310'--------------------------------------------------------------',
5311'image 0 2d3 section 0',
5312' atf-fip 0 90 atf-fip 0',
5313' soc-fw 88 4 blob-ext 88',
5314' u-boot 8c 4 u-boot 8c',
5315' fdtmap 90 243 fdtmap 90',
Simon Glass3efb2972021-11-23 21:08:59 -07005316]
5317 self.assertEqual(expected, lines)
5318
5319 image = control.images['image']
5320 entries = image.GetEntries()
5321 fdtmap = entries['fdtmap']
5322
5323 fdtmap_data = data[fdtmap.image_pos:fdtmap.image_pos + fdtmap.size]
5324 magic = fdtmap_data[:8]
5325 self.assertEqual(b'_FDTMAP_', magic)
Simon Glass80025522022-01-29 14:14:04 -07005326 self.assertEqual(tools.get_bytes(0, 8), fdtmap_data[8:16])
Simon Glass3efb2972021-11-23 21:08:59 -07005327
5328 fdt_data = fdtmap_data[16:]
5329 dtb = fdt.Fdt.FromData(fdt_data)
5330 dtb.Scan()
5331 props = self._GetPropTree(dtb, BASE_DTB_PROPS, prefix='/')
5332 self.assertEqual({
5333 'atf-fip/soc-fw:image-pos': 136,
5334 'atf-fip/soc-fw:offset': 136,
5335 'atf-fip/soc-fw:size': 4,
5336 'atf-fip/u-boot:image-pos': 140,
5337 'atf-fip/u-boot:offset': 140,
5338 'atf-fip/u-boot:size': 4,
5339 'atf-fip:image-pos': 0,
5340 'atf-fip:offset': 0,
5341 'atf-fip:size': 144,
5342 'image-pos': 0,
5343 'offset': 0,
5344 'fdtmap:image-pos': fdtmap.image_pos,
5345 'fdtmap:offset': fdtmap.offset,
5346 'fdtmap:size': len(fdtmap_data),
5347 'size': len(data),
5348 }, props)
5349
5350 def testFipExtractOneEntry(self):
5351 """Test extracting a single entry fron an FIP"""
5352 self._DoReadFileRealDtb('207_fip_ls.dts')
Simon Glass80025522022-01-29 14:14:04 -07005353 image_fname = tools.get_output_filename('image.bin')
Simon Glass3efb2972021-11-23 21:08:59 -07005354 fname = os.path.join(self._indir, 'output.extact')
5355 control.ExtractEntries(image_fname, fname, None, ['atf-fip/u-boot'])
Simon Glass80025522022-01-29 14:14:04 -07005356 data = tools.read_file(fname)
Simon Glass3efb2972021-11-23 21:08:59 -07005357 self.assertEqual(U_BOOT_DATA, data)
5358
5359 def testFipReplace(self):
5360 """Test replacing a single file in a FIP"""
Simon Glass80025522022-01-29 14:14:04 -07005361 expected = U_BOOT_DATA + tools.get_bytes(0x78, 50)
Simon Glass3efb2972021-11-23 21:08:59 -07005362 data = self._DoReadFileRealDtb('208_fip_replace.dts')
Simon Glass80025522022-01-29 14:14:04 -07005363 updated_fname = tools.get_output_filename('image-updated.bin')
5364 tools.write_file(updated_fname, data)
Simon Glass3efb2972021-11-23 21:08:59 -07005365 entry_name = 'atf-fip/u-boot'
5366 control.WriteEntry(updated_fname, entry_name, expected,
5367 allow_resize=True)
5368 actual = control.ReadEntry(updated_fname, entry_name)
5369 self.assertEqual(expected, actual)
5370
Simon Glass80025522022-01-29 14:14:04 -07005371 new_data = tools.read_file(updated_fname)
Simon Glass3efb2972021-11-23 21:08:59 -07005372 hdr, fents = fip_util.decode_fip(new_data)
5373
5374 self.assertEqual(2, len(fents))
5375
5376 # Check that the FIP entry is updated
5377 fent = fents[1]
5378 self.assertEqual(0x8c, fent.offset)
5379 self.assertEqual(len(expected), fent.size)
5380 self.assertEqual(0, fent.flags)
5381 self.assertEqual(expected, fent.data)
5382 self.assertEqual(True, fent.valid)
5383
5384 def testFipMissing(self):
Simon Glass14d64e32025-04-29 07:21:59 -06005385 with terminal.capture() as (stdout, stderr):
Simon Glass3efb2972021-11-23 21:08:59 -07005386 self._DoTestFile('209_fip_missing.dts', allow_missing=True)
5387 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07005388 self.assertRegex(err, "Image 'image'.*missing.*: rmm-fw")
Simon Glass3efb2972021-11-23 21:08:59 -07005389
5390 def testFipSize(self):
5391 """Test a FIP with a size property"""
5392 data = self._DoReadFile('210_fip_size.dts')
5393 self.assertEqual(0x100 + len(U_BOOT_DATA), len(data))
5394 hdr, fents = fip_util.decode_fip(data)
5395 self.assertEqual(fip_util.HEADER_MAGIC, hdr.name)
5396 self.assertEqual(fip_util.HEADER_SERIAL, hdr.serial)
5397
5398 self.assertEqual(1, len(fents))
5399
5400 fent = fents[0]
5401 self.assertEqual('soc-fw', fent.fip_type)
5402 self.assertEqual(0x60, fent.offset)
5403 self.assertEqual(len(ATF_BL31_DATA), fent.size)
5404 self.assertEqual(ATF_BL31_DATA, fent.data)
5405 self.assertEqual(True, fent.valid)
5406
5407 rest = data[0x60 + len(ATF_BL31_DATA):0x100]
Simon Glass80025522022-01-29 14:14:04 -07005408 self.assertEqual(tools.get_bytes(0xff, len(rest)), rest)
Simon Glass3efb2972021-11-23 21:08:59 -07005409
5410 def testFipBadAlign(self):
5411 """Test that an invalid alignment value in a FIP is detected"""
5412 with self.assertRaises(ValueError) as e:
5413 self._DoTestFile('211_fip_bad_align.dts')
5414 self.assertIn(
5415 "Node \'/binman/atf-fip\': FIP alignment 31 must be a power of two",
5416 str(e.exception))
5417
5418 def testFipCollection(self):
5419 """Test using a FIP in a collection"""
5420 data = self._DoReadFile('212_fip_collection.dts')
5421 entry1 = control.images['image'].GetEntries()['collection']
5422 data1 = data[:entry1.size]
5423 hdr1, fents2 = fip_util.decode_fip(data1)
5424
5425 entry2 = control.images['image'].GetEntries()['atf-fip']
5426 data2 = data[entry2.offset:entry2.offset + entry2.size]
5427 hdr1, fents2 = fip_util.decode_fip(data2)
5428
5429 # The 'collection' entry should have U-Boot included at the end
5430 self.assertEqual(entry1.size - len(U_BOOT_DATA), entry2.size)
5431 self.assertEqual(data1, data2 + U_BOOT_DATA)
5432 self.assertEqual(U_BOOT_DATA, data1[-4:])
5433
5434 # There should be a U-Boot after the final FIP
5435 self.assertEqual(U_BOOT_DATA, data[-4:])
Simon Glass76f496d2021-07-06 10:36:37 -06005436
Simon Glassccae6862022-01-12 13:10:35 -07005437 def testFakeBlob(self):
5438 """Test handling of faking an external blob"""
Simon Glass14d64e32025-04-29 07:21:59 -06005439 with terminal.capture() as (stdout, stderr):
Simon Glassccae6862022-01-12 13:10:35 -07005440 self._DoTestFile('217_fake_blob.dts', allow_missing=True,
5441 allow_fake_blobs=True)
5442 err = stderr.getvalue()
5443 self.assertRegex(
5444 err,
5445 "Image '.*' has faked external blobs and is non-functional: .*")
Simon Glassccae6862022-01-12 13:10:35 -07005446
Simon Glassceb5f912022-01-09 20:13:46 -07005447 def testExtblobListFaked(self):
5448 """Test an extblob with missing external blob that are faked"""
Simon Glass14d64e32025-04-29 07:21:59 -06005449 with terminal.capture() as (stdout, stderr):
Simon Glassceb5f912022-01-09 20:13:46 -07005450 self._DoTestFile('216_blob_ext_list_missing.dts',
5451 allow_fake_blobs=True)
5452 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07005453 self.assertRegex(err, "Image 'image'.*faked.*: blob-ext-list")
Simon Glassceb5f912022-01-09 20:13:46 -07005454
Simon Glass162017b2022-01-09 20:13:57 -07005455 def testListBintools(self):
5456 args = ['tool', '--list']
Simon Glass14d64e32025-04-29 07:21:59 -06005457 with terminal.capture() as (stdout, _):
Simon Glass162017b2022-01-09 20:13:57 -07005458 self._DoBinman(*args)
5459 out = stdout.getvalue().splitlines()
5460 self.assertTrue(len(out) >= 2)
5461
5462 def testFetchBintools(self):
5463 def fail_download(url):
Simon Glass80025522022-01-29 14:14:04 -07005464 """Take the tools.download() function by raising an exception"""
Simon Glass162017b2022-01-09 20:13:57 -07005465 raise urllib.error.URLError('my error')
5466
5467 args = ['tool']
5468 with self.assertRaises(ValueError) as e:
5469 self._DoBinman(*args)
5470 self.assertIn("Invalid arguments to 'tool' subcommand",
5471 str(e.exception))
5472
5473 args = ['tool', '--fetch']
5474 with self.assertRaises(ValueError) as e:
5475 self._DoBinman(*args)
5476 self.assertIn('Please specify bintools to fetch', str(e.exception))
5477
5478 args = ['tool', '--fetch', '_testing']
Simon Glass80025522022-01-29 14:14:04 -07005479 with unittest.mock.patch.object(tools, 'download',
Simon Glass162017b2022-01-09 20:13:57 -07005480 side_effect=fail_download):
Simon Glass14d64e32025-04-29 07:21:59 -06005481 with terminal.capture() as (stdout, _):
Simon Glass162017b2022-01-09 20:13:57 -07005482 self._DoBinman(*args)
5483 self.assertIn('failed to fetch with all methods', stdout.getvalue())
5484
Simon Glass620c4462022-01-09 20:14:11 -07005485 def testBintoolDocs(self):
5486 """Test for creation of bintool documentation"""
Simon Glass14d64e32025-04-29 07:21:59 -06005487 with terminal.capture() as (stdout, stderr):
Simon Glass620c4462022-01-09 20:14:11 -07005488 control.write_bintool_docs(control.bintool.Bintool.get_tool_list())
5489 self.assertTrue(len(stdout.getvalue()) > 0)
5490
5491 def testBintoolDocsMissing(self):
5492 """Test handling of missing bintool documentation"""
5493 with self.assertRaises(ValueError) as e:
Simon Glass14d64e32025-04-29 07:21:59 -06005494 with terminal.capture() as (stdout, stderr):
Simon Glass620c4462022-01-09 20:14:11 -07005495 control.write_bintool_docs(
5496 control.bintool.Bintool.get_tool_list(), 'mkimage')
5497 self.assertIn('Documentation is missing for modules: mkimage',
5498 str(e.exception))
5499
Jan Kiszka58c407f2022-01-28 20:37:53 +01005500 def testListWithGenNode(self):
5501 """Check handling of an FDT map when the section cannot be found"""
5502 entry_args = {
5503 'of-list': 'test-fdt1 test-fdt2',
5504 }
5505 data = self._DoReadFileDtb(
5506 '219_fit_gennode.dts',
5507 entry_args=entry_args,
5508 use_real_dtb=True,
5509 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])
5510
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01005511 tmpdir = None
Jan Kiszka58c407f2022-01-28 20:37:53 +01005512 try:
5513 tmpdir, updated_fname = self._SetupImageInTmpdir()
Simon Glass14d64e32025-04-29 07:21:59 -06005514 with terminal.capture() as (stdout, stderr):
Jan Kiszka58c407f2022-01-28 20:37:53 +01005515 self._RunBinman('ls', '-i', updated_fname)
5516 finally:
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01005517 if tmpdir:
5518 shutil.rmtree(tmpdir)
Jan Kiszka58c407f2022-01-28 20:37:53 +01005519
Alper Nebi Yasake706d872022-02-08 01:08:05 +03005520 def testFitSubentryUsesBintool(self):
5521 """Test that binman FIT subentries can use bintools"""
Simon Glass5dc22cf2025-02-03 09:26:42 -07005522 command.TEST_RESULT = self._HandleGbbCommand
Alper Nebi Yasake706d872022-02-08 01:08:05 +03005523 entry_args = {
5524 'keydir': 'devkeys',
5525 'bmpblk': 'bmpblk.bin',
5526 }
5527 data, _, _, _ = self._DoReadFileDtb('220_fit_subentry_bintool.dts',
5528 entry_args=entry_args)
5529
Alper Nebi Yasakd4553262022-02-08 01:08:07 +03005530 expected = (GBB_DATA + GBB_DATA + tools.get_bytes(0, 8) +
5531 tools.get_bytes(0, 0x2180 - 16))
Alper Nebi Yasake706d872022-02-08 01:08:05 +03005532 self.assertIn(expected, data)
5533
5534 def testFitSubentryMissingBintool(self):
5535 """Test that binman reports missing bintools for FIT subentries"""
5536 entry_args = {
5537 'keydir': 'devkeys',
5538 }
Simon Glass14d64e32025-04-29 07:21:59 -06005539 with terminal.capture() as (_, stderr):
Alper Nebi Yasake706d872022-02-08 01:08:05 +03005540 self._DoTestFile('220_fit_subentry_bintool.dts',
5541 force_missing_bintools='futility', entry_args=entry_args)
5542 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07005543 self.assertRegex(err, "Image 'image'.*missing bintools.*: futility")
Simon Glassccae6862022-01-12 13:10:35 -07005544
Alper Nebi Yasak1e4ffd82022-02-09 22:02:35 +03005545 def testFitSubentryHashSubnode(self):
5546 """Test an image with a FIT inside"""
Marek Vasutf7413f02023-07-18 07:23:58 -06005547 self._SetupSplElf()
Alper Nebi Yasak1e4ffd82022-02-09 22:02:35 +03005548 data, _, _, out_dtb_name = self._DoReadFileDtb(
5549 '221_fit_subentry_hash.dts', use_real_dtb=True, update_dtb=True)
5550
5551 mkimage_dtb = fdt.Fdt.FromData(data)
5552 mkimage_dtb.Scan()
5553 binman_dtb = fdt.Fdt(out_dtb_name)
5554 binman_dtb.Scan()
5555
5556 # Check that binman didn't add hash values
5557 fnode = binman_dtb.GetNode('/binman/fit/images/kernel/hash')
5558 self.assertNotIn('value', fnode.props)
5559
5560 fnode = binman_dtb.GetNode('/binman/fit/images/fdt-1/hash')
5561 self.assertNotIn('value', fnode.props)
5562
5563 # Check that mkimage added hash values
5564 fnode = mkimage_dtb.GetNode('/images/kernel/hash')
5565 self.assertIn('value', fnode.props)
5566
5567 fnode = mkimage_dtb.GetNode('/images/fdt-1/hash')
5568 self.assertIn('value', fnode.props)
5569
Roger Quadros5cdcea02022-02-19 20:50:04 +02005570 def testPackTeeOs(self):
5571 """Test that an image with an TEE binary can be created"""
5572 data = self._DoReadFile('222_tee_os.dts')
5573 self.assertEqual(TEE_OS_DATA, data[:len(TEE_OS_DATA)])
5574
Neha Malcom Francis59be2552023-12-05 15:12:18 +05305575 def testPackTiDm(self):
5576 """Test that an image with a TI DM binary can be created"""
5577 data = self._DoReadFile('225_ti_dm.dts')
5578 self.assertEqual(TI_DM_DATA, data[:len(TI_DM_DATA)])
5579
Bryan Brattlof5b45f4b2025-05-15 10:16:19 -05005580 def testPackBl1(self):
5581 """test if an image with a bl1 binary can be created"""
5582 data = self._DoReadFile('347_bl1.dts')
5583 self.assertEqual(ATF_BL1_DATA, data[:len(ATF_BL1_DATA)])
5584
Simon Glass912339f2022-02-08 11:50:03 -07005585 def testFitFdtOper(self):
5586 """Check handling of a specified FIT operation"""
5587 entry_args = {
5588 'of-list': 'test-fdt1 test-fdt2',
5589 'default-dt': 'test-fdt2',
5590 }
5591 self._DoReadFileDtb(
5592 '223_fit_fdt_oper.dts',
5593 entry_args=entry_args,
5594 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
5595
5596 def testFitFdtBadOper(self):
5597 """Check handling of an FDT map when the section cannot be found"""
5598 with self.assertRaises(ValueError) as exc:
5599 self._DoReadFileDtb('224_fit_bad_oper.dts')
Simon Glass05f71dc2022-03-05 20:19:09 -07005600 self.assertIn("Node '/binman/fit': subnode 'images/@fdt-SEQ': Unknown operation 'unknown'",
Simon Glass912339f2022-02-08 11:50:03 -07005601 str(exc.exception))
5602
Simon Glassdd156a42022-03-05 20:18:59 -07005603 def test_uses_expand_size(self):
5604 """Test that the 'expand-size' property cannot be used anymore"""
5605 with self.assertRaises(ValueError) as e:
5606 data = self._DoReadFile('225_expand_size_bad.dts')
5607 self.assertIn(
5608 "Node '/binman/u-boot': Please use 'extend-size' instead of 'expand-size'",
5609 str(e.exception))
5610
Simon Glass5f423422022-03-05 20:19:12 -07005611 def testFitSplitElf(self):
5612 """Test an image with an FIT with an split-elf operation"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005613 if not elf.ELF_TOOLS:
5614 self.skipTest('Python elftools not available')
Simon Glass5f423422022-03-05 20:19:12 -07005615 entry_args = {
5616 'of-list': 'test-fdt1 test-fdt2',
5617 'default-dt': 'test-fdt2',
5618 'atf-bl31-path': 'bl31.elf',
5619 'tee-os-path': 'tee.elf',
5620 }
5621 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
5622 data = self._DoReadFileDtb(
5623 '226_fit_split_elf.dts',
5624 entry_args=entry_args,
5625 extra_indirs=[test_subdir])[0]
5626
5627 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
5628 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
5629
5630 base_keys = {'description', 'type', 'arch', 'os', 'compression',
5631 'data', 'load'}
5632 dtb = fdt.Fdt.FromData(fit_data)
5633 dtb.Scan()
5634
5635 elf_data = tools.read_file(os.path.join(self._indir, 'bl31.elf'))
5636 segments, entry = elf.read_loadable_segments(elf_data)
5637
5638 # We assume there are two segments
Brandon Maiera657bc62024-06-04 16:16:05 +00005639 self.assertEqual(2, len(segments))
Simon Glass5f423422022-03-05 20:19:12 -07005640
5641 atf1 = dtb.GetNode('/images/atf-1')
5642 _, start, data = segments[0]
5643 self.assertEqual(base_keys | {'entry'}, atf1.props.keys())
5644 self.assertEqual(entry,
5645 fdt_util.fdt32_to_cpu(atf1.props['entry'].value))
5646 self.assertEqual(start,
5647 fdt_util.fdt32_to_cpu(atf1.props['load'].value))
5648 self.assertEqual(data, atf1.props['data'].bytes)
5649
Jonas Karlmand2c7d902023-01-21 19:01:48 +00005650 hash_node = atf1.FindNode('hash')
5651 self.assertIsNotNone(hash_node)
5652 self.assertEqual({'algo', 'value'}, hash_node.props.keys())
5653
Simon Glass5f423422022-03-05 20:19:12 -07005654 atf2 = dtb.GetNode('/images/atf-2')
5655 self.assertEqual(base_keys, atf2.props.keys())
5656 _, start, data = segments[1]
5657 self.assertEqual(start,
5658 fdt_util.fdt32_to_cpu(atf2.props['load'].value))
5659 self.assertEqual(data, atf2.props['data'].bytes)
5660
Jonas Karlmand2c7d902023-01-21 19:01:48 +00005661 hash_node = atf2.FindNode('hash')
5662 self.assertIsNotNone(hash_node)
5663 self.assertEqual({'algo', 'value'}, hash_node.props.keys())
5664
5665 hash_node = dtb.GetNode('/images/tee-1/hash-1')
5666 self.assertIsNotNone(hash_node)
5667 self.assertEqual({'algo', 'value'}, hash_node.props.keys())
5668
Simon Glass5f423422022-03-05 20:19:12 -07005669 conf = dtb.GetNode('/configurations')
5670 self.assertEqual({'default'}, conf.props.keys())
5671
5672 for subnode in conf.subnodes:
5673 self.assertEqual({'description', 'fdt', 'loadables'},
5674 subnode.props.keys())
5675 self.assertEqual(
5676 ['atf-1', 'atf-2', 'tee-1', 'tee-2'],
5677 fdt_util.GetStringList(subnode, 'loadables'))
5678
5679 def _check_bad_fit(self, dts):
5680 """Check a bad FIT
5681
5682 This runs with the given dts and returns the assertion raised
5683
5684 Args:
5685 dts (str): dts filename to use
5686
5687 Returns:
5688 str: Assertion string raised
5689 """
5690 entry_args = {
5691 'of-list': 'test-fdt1 test-fdt2',
5692 'default-dt': 'test-fdt2',
5693 'atf-bl31-path': 'bl31.elf',
5694 'tee-os-path': 'tee.elf',
5695 }
5696 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
5697 with self.assertRaises(ValueError) as exc:
5698 self._DoReadFileDtb(dts, entry_args=entry_args,
5699 extra_indirs=[test_subdir])[0]
5700 return str(exc.exception)
5701
5702 def testFitSplitElfBadElf(self):
5703 """Test a FIT split-elf operation with an invalid ELF file"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005704 if not elf.ELF_TOOLS:
5705 self.skipTest('Python elftools not available')
Simon Glass5f423422022-03-05 20:19:12 -07005706 TestFunctional._MakeInputFile('bad.elf', tools.get_bytes(100, 100))
5707 entry_args = {
5708 'of-list': 'test-fdt1 test-fdt2',
5709 'default-dt': 'test-fdt2',
5710 'atf-bl31-path': 'bad.elf',
5711 'tee-os-path': 'tee.elf',
5712 }
5713 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
5714 with self.assertRaises(ValueError) as exc:
5715 self._DoReadFileDtb(
5716 '226_fit_split_elf.dts',
5717 entry_args=entry_args,
5718 extra_indirs=[test_subdir])[0]
5719 self.assertIn(
5720 "Node '/binman/fit': subnode 'images/@atf-SEQ': Failed to read ELF file: Magic number does not match",
5721 str(exc.exception))
5722
Simon Glass5f423422022-03-05 20:19:12 -07005723 def checkFitSplitElf(self, **kwargs):
Simon Glass7d3e4072022-08-07 09:46:46 -06005724 """Test an split-elf FIT with a missing ELF file
5725
5726 Args:
5727 kwargs (dict of str): Arguments to pass to _DoTestFile()
5728
5729 Returns:
5730 tuple:
5731 str: stdout result
5732 str: stderr result
5733 """
Simon Glass5f423422022-03-05 20:19:12 -07005734 entry_args = {
5735 'of-list': 'test-fdt1 test-fdt2',
5736 'default-dt': 'test-fdt2',
5737 'atf-bl31-path': 'bl31.elf',
5738 'tee-os-path': 'missing.elf',
5739 }
5740 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
Simon Glass14d64e32025-04-29 07:21:59 -06005741 with terminal.capture() as (stdout, stderr):
Simon Glass5f423422022-03-05 20:19:12 -07005742 self._DoTestFile(
5743 '226_fit_split_elf.dts', entry_args=entry_args,
Simon Glass7d3e4072022-08-07 09:46:46 -06005744 extra_indirs=[test_subdir], verbosity=3, **kwargs)
5745 out = stdout.getvalue()
5746 err = stderr.getvalue()
5747 return out, err
Simon Glass5f423422022-03-05 20:19:12 -07005748
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005749 def testFitSplitElfBadDirective(self):
5750 """Test a FIT split-elf invalid fit,xxx directive in an image node"""
5751 if not elf.ELF_TOOLS:
5752 self.skipTest('Python elftools not available')
5753 err = self._check_bad_fit('227_fit_bad_dir.dts')
5754 self.assertIn(
5755 "Node '/binman/fit': subnode 'images/@atf-SEQ': Unknown directive 'fit,something'",
5756 err)
5757
5758 def testFitSplitElfBadDirectiveConfig(self):
5759 """Test a FIT split-elf with invalid fit,xxx directive in config"""
5760 if not elf.ELF_TOOLS:
5761 self.skipTest('Python elftools not available')
5762 err = self._check_bad_fit('228_fit_bad_dir_config.dts')
5763 self.assertEqual(
5764 "Node '/binman/fit': subnode 'configurations/@config-SEQ': Unknown directive 'fit,config'",
5765 err)
5766
5767
Simon Glass5f423422022-03-05 20:19:12 -07005768 def testFitSplitElfMissing(self):
5769 """Test an split-elf FIT with a missing ELF file"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005770 if not elf.ELF_TOOLS:
5771 self.skipTest('Python elftools not available')
Simon Glass7d3e4072022-08-07 09:46:46 -06005772 out, err = self.checkFitSplitElf(allow_missing=True)
Simon Glass5f423422022-03-05 20:19:12 -07005773 self.assertRegex(
5774 err,
5775 "Image '.*' is missing external blobs and is non-functional: .*")
Simon Glass7d3e4072022-08-07 09:46:46 -06005776 self.assertNotRegex(out, '.*Faked blob.*')
5777 fname = tools.get_output_filename('binman-fake/missing.elf')
5778 self.assertFalse(os.path.exists(fname))
Simon Glass5f423422022-03-05 20:19:12 -07005779
5780 def testFitSplitElfFaked(self):
5781 """Test an split-elf FIT with faked ELF file"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005782 if not elf.ELF_TOOLS:
5783 self.skipTest('Python elftools not available')
Simon Glass7d3e4072022-08-07 09:46:46 -06005784 out, err = self.checkFitSplitElf(allow_missing=True, allow_fake_blobs=True)
Simon Glass5f423422022-03-05 20:19:12 -07005785 self.assertRegex(
5786 err,
5787 "Image '.*' is missing external blobs and is non-functional: .*")
Simon Glass7d3e4072022-08-07 09:46:46 -06005788 self.assertRegex(
5789 out,
5790 "Entry '/binman/fit/images/@tee-SEQ/tee-os': Faked blob '.*binman-fake/missing.elf")
5791 fname = tools.get_output_filename('binman-fake/missing.elf')
5792 self.assertTrue(os.path.exists(fname))
Simon Glass5f423422022-03-05 20:19:12 -07005793
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005794 def testMkimageMissingBlob(self):
5795 """Test using mkimage to build an image"""
Simon Glass14d64e32025-04-29 07:21:59 -06005796 with terminal.capture() as (stdout, stderr):
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005797 self._DoTestFile('229_mkimage_missing.dts', allow_missing=True,
5798 allow_fake_blobs=True)
5799 err = stderr.getvalue()
5800 self.assertRegex(
5801 err,
5802 "Image '.*' has faked external blobs and is non-functional: .*")
5803
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005804 def testPreLoad(self):
5805 """Test an image with a pre-load header"""
5806 entry_args = {
Simon Glasse2dfb962023-07-24 09:19:57 -06005807 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005808 }
Simon Glasse2dfb962023-07-24 09:19:57 -06005809 data = self._DoReadFileDtb(
5810 '230_pre_load.dts', entry_args=entry_args,
5811 extra_indirs=[os.path.join(self._binman_dir, 'test')])[0]
Paul HENRYS5cf82892025-02-24 22:20:55 +01005812
5813 image_fname = tools.get_output_filename('image.bin')
5814 is_signed = self._CheckPreload(image_fname, self.TestFile("dev.key"))
5815
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005816 self.assertEqual(PRE_LOAD_MAGIC, data[:len(PRE_LOAD_MAGIC)])
5817 self.assertEqual(PRE_LOAD_VERSION, data[4:4 + len(PRE_LOAD_VERSION)])
5818 self.assertEqual(PRE_LOAD_HDR_SIZE, data[8:8 + len(PRE_LOAD_HDR_SIZE)])
Paul HENRYS5cf82892025-02-24 22:20:55 +01005819 self.assertEqual(is_signed, True)
Simon Glasse2dfb962023-07-24 09:19:57 -06005820
5821 def testPreLoadNoKey(self):
5822 """Test an image with a pre-load heade0r with missing key"""
5823 with self.assertRaises(FileNotFoundError) as exc:
5824 self._DoReadFile('230_pre_load.dts')
5825 self.assertIn("No such file or directory: 'dev.key'",
5826 str(exc.exception))
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005827
5828 def testPreLoadPkcs(self):
5829 """Test an image with a pre-load header with padding pkcs"""
Simon Glasse2dfb962023-07-24 09:19:57 -06005830 entry_args = {
5831 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5832 }
5833 data = self._DoReadFileDtb('231_pre_load_pkcs.dts',
5834 entry_args=entry_args)[0]
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005835 self.assertEqual(PRE_LOAD_MAGIC, data[:len(PRE_LOAD_MAGIC)])
5836 self.assertEqual(PRE_LOAD_VERSION, data[4:4 + len(PRE_LOAD_VERSION)])
5837 self.assertEqual(PRE_LOAD_HDR_SIZE, data[8:8 + len(PRE_LOAD_HDR_SIZE)])
5838
5839 def testPreLoadPss(self):
5840 """Test an image with a pre-load header with padding pss"""
Simon Glasse2dfb962023-07-24 09:19:57 -06005841 entry_args = {
5842 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5843 }
5844 data = self._DoReadFileDtb('232_pre_load_pss.dts',
5845 entry_args=entry_args)[0]
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005846 self.assertEqual(PRE_LOAD_MAGIC, data[:len(PRE_LOAD_MAGIC)])
5847 self.assertEqual(PRE_LOAD_VERSION, data[4:4 + len(PRE_LOAD_VERSION)])
5848 self.assertEqual(PRE_LOAD_HDR_SIZE, data[8:8 + len(PRE_LOAD_HDR_SIZE)])
5849
5850 def testPreLoadInvalidPadding(self):
5851 """Test an image with a pre-load header with an invalid padding"""
Simon Glasse2dfb962023-07-24 09:19:57 -06005852 entry_args = {
5853 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5854 }
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005855 with self.assertRaises(ValueError) as e:
Simon Glasse2dfb962023-07-24 09:19:57 -06005856 self._DoReadFileDtb('233_pre_load_invalid_padding.dts',
5857 entry_args=entry_args)
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005858
5859 def testPreLoadInvalidSha(self):
5860 """Test an image with a pre-load header with an invalid hash"""
Simon Glasse2dfb962023-07-24 09:19:57 -06005861 entry_args = {
5862 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5863 }
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005864 with self.assertRaises(ValueError) as e:
Simon Glasse2dfb962023-07-24 09:19:57 -06005865 self._DoReadFileDtb('234_pre_load_invalid_sha.dts',
5866 entry_args=entry_args)
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005867
5868 def testPreLoadInvalidAlgo(self):
5869 """Test an image with a pre-load header with an invalid algo"""
5870 with self.assertRaises(ValueError) as e:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005871 data = self._DoReadFile('235_pre_load_invalid_algo.dts')
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005872
5873 def testPreLoadInvalidKey(self):
5874 """Test an image with a pre-load header with an invalid key"""
Simon Glasse2dfb962023-07-24 09:19:57 -06005875 entry_args = {
5876 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5877 }
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005878 with self.assertRaises(ValueError) as e:
Simon Glasse2dfb962023-07-24 09:19:57 -06005879 data = self._DoReadFileDtb('236_pre_load_invalid_key.dts',
5880 entry_args=entry_args)
Roger Quadros5cdcea02022-02-19 20:50:04 +02005881
Alper Nebi Yasak5cff63f2022-03-27 18:31:44 +03005882 def _CheckSafeUniqueNames(self, *images):
5883 """Check all entries of given images for unsafe unique names"""
5884 for image in images:
5885 entries = {}
5886 image._CollectEntries(entries, {}, image)
5887 for entry in entries.values():
5888 uniq = entry.GetUniqueName()
5889
5890 # Used as part of a filename, so must not be absolute paths.
5891 self.assertFalse(os.path.isabs(uniq))
5892
5893 def testSafeUniqueNames(self):
5894 """Test entry unique names are safe in single image configuration"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005895 data = self._DoReadFileRealDtb('237_unique_names.dts')
Alper Nebi Yasak5cff63f2022-03-27 18:31:44 +03005896
5897 orig_image = control.images['image']
5898 image_fname = tools.get_output_filename('image.bin')
5899 image = Image.FromFile(image_fname)
5900
5901 self._CheckSafeUniqueNames(orig_image, image)
5902
5903 def testSafeUniqueNamesMulti(self):
5904 """Test entry unique names are safe with multiple images"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005905 data = self._DoReadFileRealDtb('238_unique_names_multi.dts')
Alper Nebi Yasak5cff63f2022-03-27 18:31:44 +03005906
5907 orig_image = control.images['image']
5908 image_fname = tools.get_output_filename('image.bin')
5909 image = Image.FromFile(image_fname)
5910
5911 self._CheckSafeUniqueNames(orig_image, image)
5912
Alper Nebi Yasake63ca5a2022-03-27 18:31:45 +03005913 def testReplaceCmdWithBintool(self):
5914 """Test replacing an entry that needs a bintool to pack"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005915 data = self._DoReadFileRealDtb('239_replace_with_bintool.dts')
Alper Nebi Yasake63ca5a2022-03-27 18:31:45 +03005916 expected = U_BOOT_DATA + b'aa'
5917 self.assertEqual(expected, data[:len(expected)])
5918
5919 try:
5920 tmpdir, updated_fname = self._SetupImageInTmpdir()
5921 fname = os.path.join(tmpdir, 'update-testing.bin')
5922 tools.write_file(fname, b'zz')
5923 self._DoBinman('replace', '-i', updated_fname,
5924 '_testing', '-f', fname)
5925
5926 data = tools.read_file(updated_fname)
5927 expected = U_BOOT_DATA + b'zz'
5928 self.assertEqual(expected, data[:len(expected)])
5929 finally:
5930 shutil.rmtree(tmpdir)
5931
5932 def testReplaceCmdOtherWithBintool(self):
5933 """Test replacing an entry when another needs a bintool to pack"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005934 data = self._DoReadFileRealDtb('239_replace_with_bintool.dts')
Alper Nebi Yasake63ca5a2022-03-27 18:31:45 +03005935 expected = U_BOOT_DATA + b'aa'
5936 self.assertEqual(expected, data[:len(expected)])
5937
5938 try:
5939 tmpdir, updated_fname = self._SetupImageInTmpdir()
5940 fname = os.path.join(tmpdir, 'update-u-boot.bin')
5941 tools.write_file(fname, b'x' * len(U_BOOT_DATA))
5942 self._DoBinman('replace', '-i', updated_fname,
5943 'u-boot', '-f', fname)
5944
5945 data = tools.read_file(updated_fname)
5946 expected = b'x' * len(U_BOOT_DATA) + b'aa'
5947 self.assertEqual(expected, data[:len(expected)])
5948 finally:
5949 shutil.rmtree(tmpdir)
5950
Alper Nebi Yasak00c68f12022-03-27 18:31:46 +03005951 def testReplaceResizeNoRepackSameSize(self):
5952 """Test replacing entries with same-size data without repacking"""
5953 expected = b'x' * len(U_BOOT_DATA)
5954 data, expected_fdtmap, _ = self._RunReplaceCmd('u-boot', expected)
5955 self.assertEqual(expected, data)
5956
5957 path, fdtmap = state.GetFdtContents('fdtmap')
5958 self.assertIsNotNone(path)
5959 self.assertEqual(expected_fdtmap, fdtmap)
5960
5961 def testReplaceResizeNoRepackSmallerSize(self):
5962 """Test replacing entries with smaller-size data without repacking"""
5963 new_data = b'x'
5964 data, expected_fdtmap, _ = self._RunReplaceCmd('u-boot', new_data)
5965 expected = new_data.ljust(len(U_BOOT_DATA), b'\0')
5966 self.assertEqual(expected, data)
5967
5968 path, fdtmap = state.GetFdtContents('fdtmap')
5969 self.assertIsNotNone(path)
5970 self.assertEqual(expected_fdtmap, fdtmap)
5971
Alper Nebi Yasak6cadc502022-03-27 18:31:48 +03005972 def testExtractFit(self):
5973 """Test extracting a FIT section"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005974 self._DoReadFileRealDtb('240_fit_extract_replace.dts')
Alper Nebi Yasak6cadc502022-03-27 18:31:48 +03005975 image_fname = tools.get_output_filename('image.bin')
5976
5977 fit_data = control.ReadEntry(image_fname, 'fit')
5978 fit = fdt.Fdt.FromData(fit_data)
5979 fit.Scan()
5980
5981 # Check subentry data inside the extracted fit
5982 for node_path, expected in [
5983 ('/images/kernel', U_BOOT_DATA),
5984 ('/images/fdt-1', U_BOOT_NODTB_DATA),
5985 ('/images/scr-1', COMPRESS_DATA),
5986 ]:
5987 node = fit.GetNode(node_path)
5988 data = fit.GetProps(node)['data'].bytes
5989 self.assertEqual(expected, data)
5990
5991 def testExtractFitSubentries(self):
5992 """Test extracting FIT section subentries"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005993 self._DoReadFileRealDtb('240_fit_extract_replace.dts')
Alper Nebi Yasak6cadc502022-03-27 18:31:48 +03005994 image_fname = tools.get_output_filename('image.bin')
5995
5996 for entry_path, expected in [
5997 ('fit/kernel', U_BOOT_DATA),
5998 ('fit/kernel/u-boot', U_BOOT_DATA),
5999 ('fit/fdt-1', U_BOOT_NODTB_DATA),
6000 ('fit/fdt-1/u-boot-nodtb', U_BOOT_NODTB_DATA),
6001 ('fit/scr-1', COMPRESS_DATA),
6002 ('fit/scr-1/blob', COMPRESS_DATA),
6003 ]:
6004 data = control.ReadEntry(image_fname, entry_path)
6005 self.assertEqual(expected, data)
6006
Alper Nebi Yasak49892642022-03-27 18:31:49 +03006007 def testReplaceFitSubentryLeafSameSize(self):
6008 """Test replacing a FIT leaf subentry with same-size data"""
6009 new_data = b'x' * len(U_BOOT_DATA)
6010 data, expected_fdtmap, _ = self._RunReplaceCmd(
6011 'fit/kernel/u-boot', new_data,
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006012 dts='240_fit_extract_replace.dts')
Alper Nebi Yasak49892642022-03-27 18:31:49 +03006013 self.assertEqual(new_data, data)
6014
6015 path, fdtmap = state.GetFdtContents('fdtmap')
6016 self.assertIsNotNone(path)
6017 self.assertEqual(expected_fdtmap, fdtmap)
6018
6019 def testReplaceFitSubentryLeafBiggerSize(self):
6020 """Test replacing a FIT leaf subentry with bigger-size data"""
6021 new_data = b'ub' * len(U_BOOT_NODTB_DATA)
6022 data, expected_fdtmap, _ = self._RunReplaceCmd(
6023 'fit/fdt-1/u-boot-nodtb', new_data,
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006024 dts='240_fit_extract_replace.dts')
Alper Nebi Yasak49892642022-03-27 18:31:49 +03006025 self.assertEqual(new_data, data)
6026
6027 # Will be repacked, so fdtmap must change
6028 path, fdtmap = state.GetFdtContents('fdtmap')
6029 self.assertIsNotNone(path)
6030 self.assertNotEqual(expected_fdtmap, fdtmap)
6031
6032 def testReplaceFitSubentryLeafSmallerSize(self):
6033 """Test replacing a FIT leaf subentry with smaller-size data"""
6034 new_data = b'x'
6035 expected = new_data.ljust(len(U_BOOT_NODTB_DATA), b'\0')
6036 data, expected_fdtmap, _ = self._RunReplaceCmd(
6037 'fit/fdt-1/u-boot-nodtb', new_data,
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006038 dts='240_fit_extract_replace.dts')
Alper Nebi Yasak49892642022-03-27 18:31:49 +03006039 self.assertEqual(expected, data)
6040
6041 path, fdtmap = state.GetFdtContents('fdtmap')
6042 self.assertIsNotNone(path)
6043 self.assertEqual(expected_fdtmap, fdtmap)
6044
Alper Nebi Yasak1d44c8e2022-03-27 18:31:50 +03006045 def testReplaceSectionSimple(self):
Simon Glass49b77e82023-03-02 17:02:44 -07006046 """Test replacing a simple section with same-sized data"""
Alper Nebi Yasak1d44c8e2022-03-27 18:31:50 +03006047 new_data = b'w' * len(COMPRESS_DATA + U_BOOT_DATA)
Simon Glass49b77e82023-03-02 17:02:44 -07006048 data, expected_fdtmap, image = self._RunReplaceCmd('section',
6049 new_data, dts='241_replace_section_simple.dts')
6050 self.assertEqual(new_data, data)
6051
6052 entries = image.GetEntries()
6053 self.assertIn('section', entries)
6054 entry = entries['section']
6055 self.assertEqual(len(new_data), entry.size)
6056
6057 def testReplaceSectionLarger(self):
6058 """Test replacing a simple section with larger data"""
6059 new_data = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) + 1)
6060 data, expected_fdtmap, image = self._RunReplaceCmd('section',
6061 new_data, dts='241_replace_section_simple.dts')
6062 self.assertEqual(new_data, data)
6063
6064 entries = image.GetEntries()
6065 self.assertIn('section', entries)
6066 entry = entries['section']
6067 self.assertEqual(len(new_data), entry.size)
6068 fentry = entries['fdtmap']
6069 self.assertEqual(entry.offset + entry.size, fentry.offset)
6070
6071 def testReplaceSectionSmaller(self):
6072 """Test replacing a simple section with smaller data"""
6073 new_data = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) - 1) + b'\0'
6074 data, expected_fdtmap, image = self._RunReplaceCmd('section',
6075 new_data, dts='241_replace_section_simple.dts')
6076 self.assertEqual(new_data, data)
6077
6078 # The new size is the same as the old, just with a pad byte at the end
6079 entries = image.GetEntries()
6080 self.assertIn('section', entries)
6081 entry = entries['section']
6082 self.assertEqual(len(new_data), entry.size)
6083
6084 def testReplaceSectionSmallerAllow(self):
6085 """Test failing to replace a simple section with smaller data"""
6086 new_data = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) - 1)
6087 try:
6088 state.SetAllowEntryContraction(True)
6089 with self.assertRaises(ValueError) as exc:
6090 self._RunReplaceCmd('section', new_data,
6091 dts='241_replace_section_simple.dts')
6092 finally:
6093 state.SetAllowEntryContraction(False)
6094
6095 # Since we have no information about the position of things within the
6096 # section, we cannot adjust the position of /section-u-boot so it ends
6097 # up outside the section
Simon Glassc6b283f2022-08-13 11:40:46 -06006098 self.assertIn(
Simon Glass49b77e82023-03-02 17:02:44 -07006099 "Node '/section/u-boot': Offset 0x24 (36) size 0x4 (4) is outside "
6100 "the section '/section' starting at 0x0 (0) of size 0x27 (39)",
Simon Glassc6b283f2022-08-13 11:40:46 -06006101 str(exc.exception))
Alper Nebi Yasak1d44c8e2022-03-27 18:31:50 +03006102
Simon Glass8fbca772022-08-13 11:40:48 -06006103 def testMkimageImagename(self):
6104 """Test using mkimage with -n holding the data too"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006105 self._SetupSplElf()
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006106 data = self._DoReadFile('242_mkimage_name.dts')
Simon Glass8fbca772022-08-13 11:40:48 -06006107
6108 # Check that the data appears in the file somewhere
6109 self.assertIn(U_BOOT_SPL_DATA, data)
6110
Simon Glassbb7d3bb2022-09-06 20:26:52 -06006111 # Get struct legacy_img_hdr -> ih_name
Simon Glass8fbca772022-08-13 11:40:48 -06006112 name = data[0x20:0x40]
6113
6114 # Build the filename that we expect to be placed in there, by virtue of
6115 # the -n paraameter
6116 expect = os.path.join(tools.get_output_dir(), 'mkimage.mkimage')
6117
6118 # Check that the image name is set to the temporary filename used
6119 self.assertEqual(expect.encode('utf-8')[:0x20], name)
6120
Simon Glassb1669752022-08-13 11:40:49 -06006121 def testMkimageImage(self):
6122 """Test using mkimage with -n holding the data too"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006123 self._SetupSplElf()
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006124 data = self._DoReadFile('243_mkimage_image.dts')
Simon Glassb1669752022-08-13 11:40:49 -06006125
6126 # Check that the data appears in the file somewhere
6127 self.assertIn(U_BOOT_SPL_DATA, data)
6128
Simon Glassbb7d3bb2022-09-06 20:26:52 -06006129 # Get struct legacy_img_hdr -> ih_name
Simon Glassb1669752022-08-13 11:40:49 -06006130 name = data[0x20:0x40]
6131
6132 # Build the filename that we expect to be placed in there, by virtue of
6133 # the -n paraameter
6134 expect = os.path.join(tools.get_output_dir(), 'mkimage-n.mkimage')
6135
6136 # Check that the image name is set to the temporary filename used
6137 self.assertEqual(expect.encode('utf-8')[:0x20], name)
6138
6139 # Check the corect data is in the imagename file
6140 self.assertEqual(U_BOOT_DATA, tools.read_file(expect))
6141
6142 def testMkimageImageNoContent(self):
6143 """Test using mkimage with -n and no data"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006144 self._SetupSplElf()
Simon Glassb1669752022-08-13 11:40:49 -06006145 with self.assertRaises(ValueError) as exc:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006146 self._DoReadFile('244_mkimage_image_no_content.dts')
Simon Glassb1669752022-08-13 11:40:49 -06006147 self.assertIn('Could not complete processing of contents',
6148 str(exc.exception))
6149
6150 def testMkimageImageBad(self):
6151 """Test using mkimage with imagename node and data-to-imagename"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006152 self._SetupSplElf()
Simon Glassb1669752022-08-13 11:40:49 -06006153 with self.assertRaises(ValueError) as exc:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006154 self._DoReadFile('245_mkimage_image_bad.dts')
Simon Glassb1669752022-08-13 11:40:49 -06006155 self.assertIn('Cannot use both imagename node and data-to-imagename',
6156 str(exc.exception))
6157
Simon Glassbd5cd882022-08-13 11:40:50 -06006158 def testCollectionOther(self):
6159 """Test a collection where the data comes from another section"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006160 data = self._DoReadFile('246_collection_other.dts')
Simon Glassbd5cd882022-08-13 11:40:50 -06006161 self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA +
6162 tools.get_bytes(0xff, 2) + U_BOOT_NODTB_DATA +
6163 tools.get_bytes(0xfe, 3) + U_BOOT_DTB_DATA,
6164 data)
6165
6166 def testMkimageCollection(self):
6167 """Test using a collection referring to an entry in a mkimage entry"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006168 self._SetupSplElf()
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006169 data = self._DoReadFile('247_mkimage_coll.dts')
Simon Glassbd5cd882022-08-13 11:40:50 -06006170 expect = U_BOOT_SPL_DATA + U_BOOT_DATA
6171 self.assertEqual(expect, data[:len(expect)])
6172
Stefan Herbrechtsmeier11121d32022-08-19 16:25:25 +02006173 def testCompressDtbPrependInvalid(self):
6174 """Test that invalid header is detected"""
6175 with self.assertRaises(ValueError) as e:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006176 self._DoReadFileDtb('248_compress_dtb_prepend_invalid.dts')
Stefan Herbrechtsmeier11121d32022-08-19 16:25:25 +02006177 self.assertIn("Node '/binman/u-boot-dtb': Invalid prepend in "
6178 "'u-boot-dtb': 'invalid'", str(e.exception))
6179
6180 def testCompressDtbPrependLength(self):
6181 """Test that compress with length header works as expected"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006182 data = self._DoReadFileRealDtb('249_compress_dtb_prepend_length.dts')
Stefan Herbrechtsmeier11121d32022-08-19 16:25:25 +02006183 image = control.images['image']
6184 entries = image.GetEntries()
6185 self.assertIn('u-boot-dtb', entries)
6186 u_boot_dtb = entries['u-boot-dtb']
6187 self.assertIn('fdtmap', entries)
6188 fdtmap = entries['fdtmap']
6189
6190 image_fname = tools.get_output_filename('image.bin')
6191 orig = control.ReadEntry(image_fname, 'u-boot-dtb')
6192 dtb = fdt.Fdt.FromData(orig)
6193 dtb.Scan()
6194 props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
6195 expected = {
6196 'u-boot:size': len(U_BOOT_DATA),
6197 'u-boot-dtb:uncomp-size': len(orig),
6198 'u-boot-dtb:size': u_boot_dtb.size,
6199 'fdtmap:size': fdtmap.size,
6200 'size': len(data),
6201 }
6202 self.assertEqual(expected, props)
6203
6204 # Check implementation
6205 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
6206 rest = data[len(U_BOOT_DATA):]
6207 comp_data_len = struct.unpack('<I', rest[:4])[0]
6208 comp_data = rest[4:4 + comp_data_len]
6209 orig2 = self._decompress(comp_data)
6210 self.assertEqual(orig, orig2)
6211
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +02006212 def testInvalidCompress(self):
6213 """Test that invalid compress algorithm is detected"""
6214 with self.assertRaises(ValueError) as e:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006215 self._DoTestFile('250_compress_dtb_invalid.dts')
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +02006216 self.assertIn("Unknown algorithm 'invalid'", str(e.exception))
6217
Stefan Herbrechtsmeiera14bee02022-08-19 16:25:32 +02006218 def testCompUtilCompressions(self):
6219 """Test compression algorithms"""
6220 for bintool in self.comp_bintools.values():
6221 self._CheckBintool(bintool)
6222 data = bintool.compress(COMPRESS_DATA)
6223 self.assertNotEqual(COMPRESS_DATA, data)
6224 orig = bintool.decompress(data)
Brandon Maiera657bc62024-06-04 16:16:05 +00006225 self.assertEqual(COMPRESS_DATA, orig)
Stefan Herbrechtsmeiera14bee02022-08-19 16:25:32 +02006226
6227 def testCompUtilVersions(self):
6228 """Test tool version of compression algorithms"""
6229 for bintool in self.comp_bintools.values():
6230 self._CheckBintool(bintool)
6231 version = bintool.version()
6232 self.assertRegex(version, '^v?[0-9]+[0-9.]*')
6233
6234 def testCompUtilPadding(self):
6235 """Test padding of compression algorithms"""
Jiaxun Yangc6931742025-04-10 06:43:03 -06006236 # Skip zstd and lz4 because they doesn't support padding
6237 for bintool in [v for k,v in self.comp_bintools.items()
6238 if not k in ['zstd', 'lz4']]:
Stefan Herbrechtsmeiera14bee02022-08-19 16:25:32 +02006239 self._CheckBintool(bintool)
6240 data = bintool.compress(COMPRESS_DATA)
6241 self.assertNotEqual(COMPRESS_DATA, data)
6242 data += tools.get_bytes(0, 64)
6243 orig = bintool.decompress(data)
Brandon Maiera657bc62024-06-04 16:16:05 +00006244 self.assertEqual(COMPRESS_DATA, orig)
Stefan Herbrechtsmeiera14bee02022-08-19 16:25:32 +02006245
Stefan Herbrechtsmeiera5e4dcb2022-08-19 16:25:38 +02006246 def testCompressDtbZstd(self):
6247 """Test that zstd compress of device-tree files failed"""
6248 with self.assertRaises(ValueError) as e:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006249 self._DoTestFile('251_compress_dtb_zstd.dts')
Stefan Herbrechtsmeiera5e4dcb2022-08-19 16:25:38 +02006250 self.assertIn("Node '/binman/u-boot-dtb': The zstd compression "
6251 "requires a length header", str(e.exception))
6252
Quentin Schulz9b5c6482022-09-02 15:10:48 +02006253 def testMkimageMultipleDataFiles(self):
6254 """Test passing multiple files to mkimage in a mkimage entry"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006255 self._SetupSplElf()
6256 self._SetupTplElf()
Quentin Schulz9b5c6482022-09-02 15:10:48 +02006257 data = self._DoReadFile('252_mkimage_mult_data.dts')
6258 # Size of files are packed in their 4B big-endian format
6259 expect = struct.pack('>I', len(U_BOOT_TPL_DATA))
6260 expect += struct.pack('>I', len(U_BOOT_SPL_DATA))
6261 # Size info is always followed by a 4B zero value.
6262 expect += tools.get_bytes(0, 4)
6263 expect += U_BOOT_TPL_DATA
6264 # All but last files are 4B-aligned
6265 align_pad = len(U_BOOT_TPL_DATA) % 4
6266 if align_pad:
6267 expect += tools.get_bytes(0, align_pad)
6268 expect += U_BOOT_SPL_DATA
6269 self.assertEqual(expect, data[-len(expect):])
6270
Marek Vasutf7413f02023-07-18 07:23:58 -06006271 def testMkimageMultipleExpanded(self):
6272 """Test passing multiple files to mkimage in a mkimage entry"""
6273 self._SetupSplElf()
6274 self._SetupTplElf()
6275 entry_args = {
6276 'spl-bss-pad': 'y',
6277 'spl-dtb': 'y',
6278 }
6279 data = self._DoReadFileDtb('252_mkimage_mult_data.dts',
6280 use_expanded=True, entry_args=entry_args)[0]
6281 pad_len = 10
6282 tpl_expect = U_BOOT_TPL_DATA
6283 spl_expect = U_BOOT_SPL_NODTB_DATA + tools.get_bytes(0, pad_len)
6284 spl_expect += U_BOOT_SPL_DTB_DATA
6285
6286 content = data[0x40:]
6287 lens = struct.unpack('>III', content[:12])
6288
6289 # Size of files are packed in their 4B big-endian format
6290 # Size info is always followed by a 4B zero value.
6291 self.assertEqual(len(tpl_expect), lens[0])
6292 self.assertEqual(len(spl_expect), lens[1])
6293 self.assertEqual(0, lens[2])
6294
6295 rest = content[12:]
6296 self.assertEqual(tpl_expect, rest[:len(tpl_expect)])
6297
6298 rest = rest[len(tpl_expect):]
6299 align_pad = len(tpl_expect) % 4
6300 self.assertEqual(tools.get_bytes(0, align_pad), rest[:align_pad])
6301 rest = rest[align_pad:]
6302 self.assertEqual(spl_expect, rest)
6303
Quentin Schulz9b5c6482022-09-02 15:10:48 +02006304 def testMkimageMultipleNoContent(self):
6305 """Test passing multiple data files to mkimage with one data file having no content"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006306 self._SetupSplElf()
Quentin Schulz9b5c6482022-09-02 15:10:48 +02006307 with self.assertRaises(ValueError) as exc:
6308 self._DoReadFile('253_mkimage_mult_no_content.dts')
6309 self.assertIn('Could not complete processing of contents',
6310 str(exc.exception))
6311
Quentin Schulz0d3a9262022-09-02 15:10:49 +02006312 def testMkimageFilename(self):
6313 """Test using mkimage to build a binary with a filename"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006314 self._SetupSplElf()
Quentin Schulz0d3a9262022-09-02 15:10:49 +02006315 retcode = self._DoTestFile('254_mkimage_filename.dts')
6316 self.assertEqual(0, retcode)
6317 fname = tools.get_output_filename('mkimage-test.bin')
6318 self.assertTrue(os.path.exists(fname))
6319
Simon Glass56d05412022-02-28 07:16:54 -07006320 def testVpl(self):
6321 """Test that an image with VPL and its device tree can be created"""
6322 # ELF file with a '__bss_size' symbol
6323 self._SetupVplElf()
6324 data = self._DoReadFile('255_u_boot_vpl.dts')
6325 self.assertEqual(U_BOOT_VPL_DATA + U_BOOT_VPL_DTB_DATA, data)
6326
6327 def testVplNoDtb(self):
6328 """Test that an image with vpl/u-boot-vpl-nodtb.bin can be created"""
6329 self._SetupVplElf()
6330 data = self._DoReadFile('256_u_boot_vpl_nodtb.dts')
6331 self.assertEqual(U_BOOT_VPL_NODTB_DATA,
6332 data[:len(U_BOOT_VPL_NODTB_DATA)])
6333
6334 def testExpandedVpl(self):
6335 """Test that an expanded entry type is selected for TPL when needed"""
6336 self._SetupVplElf()
6337
6338 entry_args = {
6339 'vpl-bss-pad': 'y',
6340 'vpl-dtb': 'y',
6341 }
6342 self._DoReadFileDtb('257_fdt_incl_vpl.dts', use_expanded=True,
6343 entry_args=entry_args)
6344 image = control.images['image']
6345 entries = image.GetEntries()
6346 self.assertEqual(1, len(entries))
6347
6348 # We only have u-boot-vpl, which be expanded
6349 self.assertIn('u-boot-vpl', entries)
6350 entry = entries['u-boot-vpl']
6351 self.assertEqual('u-boot-vpl-expanded', entry.etype)
6352 subent = entry.GetEntries()
6353 self.assertEqual(3, len(subent))
6354 self.assertIn('u-boot-vpl-nodtb', subent)
6355 self.assertIn('u-boot-vpl-bss-pad', subent)
6356 self.assertIn('u-boot-vpl-dtb', subent)
6357
6358 def testVplBssPadMissing(self):
6359 """Test that a missing symbol is detected"""
6360 self._SetupVplElf('u_boot_ucode_ptr')
6361 with self.assertRaises(ValueError) as e:
6362 self._DoReadFile('258_vpl_bss_pad.dts')
6363 self.assertIn('Expected __bss_size symbol in vpl/u-boot-vpl',
6364 str(e.exception))
6365
Neha Malcom Francis3eb4be32022-10-17 16:36:25 +05306366 def testSymlink(self):
Andrew Davis6b463da2023-07-22 00:14:44 +05306367 """Test that image files can be symlinked"""
Neha Malcom Francis3eb4be32022-10-17 16:36:25 +05306368 retcode = self._DoTestFile('259_symlink.dts', debug=True, map=True)
6369 self.assertEqual(0, retcode)
6370 image = control.images['test_image']
6371 fname = tools.get_output_filename('test_image.bin')
6372 sname = tools.get_output_filename('symlink_to_test.bin')
6373 self.assertTrue(os.path.islink(sname))
6374 self.assertEqual(os.readlink(sname), fname)
Alper Nebi Yasake63ca5a2022-03-27 18:31:45 +03006375
Andrew Davis6b463da2023-07-22 00:14:44 +05306376 def testSymlinkOverwrite(self):
6377 """Test that symlinked images can be overwritten"""
6378 testdir = TestFunctional._MakeInputDir('symlinktest')
6379 self._DoTestFile('259_symlink.dts', debug=True, map=True, output_dir=testdir)
6380 # build the same image again in the same directory so that existing symlink is present
6381 self._DoTestFile('259_symlink.dts', debug=True, map=True, output_dir=testdir)
6382 fname = tools.get_output_filename('test_image.bin')
6383 sname = tools.get_output_filename('symlink_to_test.bin')
6384 self.assertTrue(os.path.islink(sname))
6385 self.assertEqual(os.readlink(sname), fname)
6386
Simon Glass37f85de2022-10-20 18:22:47 -06006387 def testSymbolsElf(self):
6388 """Test binman can assign symbols embedded in an ELF file"""
6389 if not elf.ELF_TOOLS:
6390 self.skipTest('Python elftools not available')
6391 self._SetupTplElf('u_boot_binman_syms')
6392 self._SetupVplElf('u_boot_binman_syms')
6393 self._SetupSplElf('u_boot_binman_syms')
6394 data = self._DoReadFileDtb('260_symbols_elf.dts')[0]
6395 image_fname = tools.get_output_filename('image.bin')
6396
6397 image = control.images['image']
6398 entries = image.GetEntries()
6399
6400 for entry in entries.values():
6401 # No symbols in u-boot and it has faked contents anyway
6402 if entry.name == 'u-boot':
6403 continue
6404 edata = data[entry.image_pos:entry.image_pos + entry.size]
6405 efname = tools.get_output_filename(f'edata-{entry.name}')
6406 tools.write_file(efname, edata)
6407
6408 syms = elf.GetSymbolFileOffset(efname, ['_binman_u_boot'])
6409 re_name = re.compile('_binman_(u_boot_(.*))_prop_(.*)')
6410 for name, sym in syms.items():
6411 msg = 'test'
6412 val = elf.GetSymbolValue(sym, edata, msg)
6413 entry_m = re_name.match(name)
6414 if entry_m:
6415 ename, prop = entry_m.group(1), entry_m.group(3)
6416 entry, entry_name, prop_name = image.LookupEntry(entries,
6417 name, msg)
Simon Glassd3d3a102025-02-19 08:11:16 -07006418 expect_val = None
Simon Glass37f85de2022-10-20 18:22:47 -06006419 if prop_name == 'offset':
6420 expect_val = entry.offset
6421 elif prop_name == 'image_pos':
6422 expect_val = entry.image_pos
6423 elif prop_name == 'size':
6424 expect_val = entry.size
6425 self.assertEqual(expect_val, val)
6426
6427 def testSymbolsElfBad(self):
6428 """Check error when trying to write symbols without the elftools lib"""
6429 if not elf.ELF_TOOLS:
6430 self.skipTest('Python elftools not available')
6431 self._SetupTplElf('u_boot_binman_syms')
6432 self._SetupVplElf('u_boot_binman_syms')
6433 self._SetupSplElf('u_boot_binman_syms')
6434 try:
6435 elf.ELF_TOOLS = False
6436 with self.assertRaises(ValueError) as exc:
6437 self._DoReadFileDtb('260_symbols_elf.dts')
6438 finally:
6439 elf.ELF_TOOLS = True
6440 self.assertIn(
6441 "Section '/binman': entry '/binman/u-boot-spl-elf': "
6442 'Cannot write symbols to an ELF file without Python elftools',
6443 str(exc.exception))
6444
Simon Glassde244162023-01-07 14:07:08 -07006445 def testSectionFilename(self):
6446 """Check writing of section contents to a file"""
6447 data = self._DoReadFile('261_section_fname.dts')
6448 expected = (b'&&' + U_BOOT_DATA + b'&&&' +
6449 tools.get_bytes(ord('!'), 7) +
6450 U_BOOT_DATA + tools.get_bytes(ord('&'), 12))
6451 self.assertEqual(expected, data)
6452
6453 sect_fname = tools.get_output_filename('outfile.bin')
6454 self.assertTrue(os.path.exists(sect_fname))
6455 sect_data = tools.read_file(sect_fname)
6456 self.assertEqual(U_BOOT_DATA, sect_data)
6457
Simon Glass1e9e61c2023-01-07 14:07:12 -07006458 def testAbsent(self):
6459 """Check handling of absent entries"""
6460 data = self._DoReadFile('262_absent.dts')
6461 self.assertEqual(U_BOOT_DATA + U_BOOT_IMG_DATA, data)
6462
Simon Glassad5cfe12023-01-07 14:07:14 -07006463 def testPackTeeOsOptional(self):
6464 """Test that an image with an optional TEE binary can be created"""
6465 entry_args = {
6466 'tee-os-path': 'tee.elf',
6467 }
6468 data = self._DoReadFileDtb('263_tee_os_opt.dts',
6469 entry_args=entry_args)[0]
6470 self.assertEqual(U_BOOT_DATA + U_BOOT_IMG_DATA, data)
6471
6472 def checkFitTee(self, dts, tee_fname):
6473 """Check that a tee-os entry works and returns data
6474
6475 Args:
6476 dts (str): Device tree filename to use
6477 tee_fname (str): filename containing tee-os
6478
6479 Returns:
6480 bytes: Image contents
6481 """
6482 if not elf.ELF_TOOLS:
6483 self.skipTest('Python elftools not available')
6484 entry_args = {
6485 'of-list': 'test-fdt1 test-fdt2',
6486 'default-dt': 'test-fdt2',
6487 'tee-os-path': tee_fname,
6488 }
6489 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
6490 data = self._DoReadFileDtb(dts, entry_args=entry_args,
6491 extra_indirs=[test_subdir])[0]
6492 return data
6493
6494 def testFitTeeOsOptionalFit(self):
6495 """Test an image with a FIT with an optional OP-TEE binary"""
6496 data = self.checkFitTee('264_tee_os_opt_fit.dts', 'tee.bin')
6497
6498 # There should be only one node, holding the data set up in SetUpClass()
6499 # for tee.bin
6500 dtb = fdt.Fdt.FromData(data)
6501 dtb.Scan()
6502 node = dtb.GetNode('/images/tee-1')
6503 self.assertEqual(TEE_ADDR,
6504 fdt_util.fdt32_to_cpu(node.props['load'].value))
6505 self.assertEqual(TEE_ADDR,
6506 fdt_util.fdt32_to_cpu(node.props['entry'].value))
6507 self.assertEqual(U_BOOT_DATA, node.props['data'].bytes)
6508
Simon Glass14d64e32025-04-29 07:21:59 -06006509 with terminal.capture() as (stdout, stderr):
Jonas Karlmanb2be3e42023-07-18 20:34:36 +00006510 self.checkFitTee('264_tee_os_opt_fit.dts', '')
6511 err = stderr.getvalue()
6512 self.assertRegex(
6513 err,
6514 "Image '.*' is missing optional external blobs but is still functional: tee-os")
6515
Simon Glassad5cfe12023-01-07 14:07:14 -07006516 def testFitTeeOsOptionalFitBad(self):
6517 """Test an image with a FIT with an optional OP-TEE binary"""
6518 with self.assertRaises(ValueError) as exc:
6519 self.checkFitTee('265_tee_os_opt_fit_bad.dts', 'tee.bin')
6520 self.assertIn(
6521 "Node '/binman/fit': subnode 'images/@tee-SEQ': Failed to read ELF file: Magic number does not match",
6522 str(exc.exception))
6523
6524 def testFitTeeOsBad(self):
6525 """Test an OP-TEE binary with wrong formats"""
6526 self.make_tee_bin('tee.bad1', 123)
6527 with self.assertRaises(ValueError) as exc:
6528 self.checkFitTee('264_tee_os_opt_fit.dts', 'tee.bad1')
6529 self.assertIn(
6530 "Node '/binman/fit/images/@tee-SEQ/tee-os': OP-TEE paged mode not supported",
6531 str(exc.exception))
6532
6533 self.make_tee_bin('tee.bad2', 0, b'extra data')
6534 with self.assertRaises(ValueError) as exc:
6535 self.checkFitTee('264_tee_os_opt_fit.dts', 'tee.bad2')
6536 self.assertIn(
6537 "Node '/binman/fit/images/@tee-SEQ/tee-os': Invalid OP-TEE file: size mismatch (expected 0x4, have 0xe)",
6538 str(exc.exception))
6539
Simon Glass63328f12023-01-07 14:07:15 -07006540 def testExtblobOptional(self):
6541 """Test an image with an external blob that is optional"""
Simon Glass14d64e32025-04-29 07:21:59 -06006542 with terminal.capture() as (stdout, stderr):
Simon Glass63328f12023-01-07 14:07:15 -07006543 data = self._DoReadFile('266_blob_ext_opt.dts')
6544 self.assertEqual(REFCODE_DATA, data)
6545 err = stderr.getvalue()
6546 self.assertRegex(
6547 err,
Jonas Karlman9f96b812023-07-18 20:34:34 +00006548 "Image '.*' is missing optional external blobs but is still functional: missing")
Simon Glass63328f12023-01-07 14:07:15 -07006549
Simon Glass7447a9d2023-01-11 16:10:12 -07006550 def testSectionInner(self):
6551 """Test an inner section with a size"""
6552 data = self._DoReadFile('267_section_inner.dts')
6553 expected = U_BOOT_DATA + tools.get_bytes(0, 12)
6554 self.assertEqual(expected, data)
6555
Simon Glassa4948b22023-01-11 16:10:14 -07006556 def testNull(self):
6557 """Test an image with a null entry"""
6558 data = self._DoReadFile('268_null.dts')
6559 self.assertEqual(U_BOOT_DATA + b'\xff\xff\xff\xff' + U_BOOT_IMG_DATA, data)
6560
Simon Glassf1ee03b2023-01-11 16:10:16 -07006561 def testOverlap(self):
6562 """Test an image with a overlapping entry"""
6563 data = self._DoReadFile('269_overlap.dts')
6564 self.assertEqual(U_BOOT_DATA[:1] + b'aa' + U_BOOT_DATA[3:], data)
6565
6566 image = control.images['image']
6567 entries = image.GetEntries()
6568
6569 self.assertIn('inset', entries)
6570 inset = entries['inset']
6571 self.assertEqual(1, inset.offset);
6572 self.assertEqual(1, inset.image_pos);
6573 self.assertEqual(2, inset.size);
6574
6575 def testOverlapNull(self):
6576 """Test an image with a null overlap"""
6577 data = self._DoReadFile('270_overlap_null.dts')
6578 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
6579
6580 # Check the FMAP
6581 fhdr, fentries = fmap_util.DecodeFmap(data[len(U_BOOT_DATA):])
6582 self.assertEqual(4, fhdr.nareas)
6583 fiter = iter(fentries)
6584
6585 fentry = next(fiter)
6586 self.assertEqual(b'SECTION', fentry.name)
6587 self.assertEqual(0, fentry.offset)
6588 self.assertEqual(len(U_BOOT_DATA), fentry.size)
6589 self.assertEqual(0, fentry.flags)
6590
6591 fentry = next(fiter)
6592 self.assertEqual(b'U_BOOT', fentry.name)
6593 self.assertEqual(0, fentry.offset)
6594 self.assertEqual(len(U_BOOT_DATA), fentry.size)
6595 self.assertEqual(0, fentry.flags)
6596
6597 # Make sure that the NULL entry appears in the FMAP
6598 fentry = next(fiter)
6599 self.assertEqual(b'NULL', fentry.name)
6600 self.assertEqual(1, fentry.offset)
6601 self.assertEqual(2, fentry.size)
6602 self.assertEqual(0, fentry.flags)
6603
6604 fentry = next(fiter)
6605 self.assertEqual(b'FMAP', fentry.name)
6606 self.assertEqual(len(U_BOOT_DATA), fentry.offset)
6607
6608 def testOverlapBad(self):
6609 """Test an image with a bad overlapping entry"""
6610 with self.assertRaises(ValueError) as exc:
6611 self._DoReadFile('271_overlap_bad.dts')
6612 self.assertIn(
6613 "Node '/binman/inset': Offset 0x10 (16) ending at 0x12 (18) must overlap with existing entries",
6614 str(exc.exception))
6615
6616 def testOverlapNoOffset(self):
6617 """Test an image with a bad overlapping entry"""
6618 with self.assertRaises(ValueError) as exc:
6619 self._DoReadFile('272_overlap_no_size.dts')
6620 self.assertIn(
6621 "Node '/binman/inset': 'fill' entry is missing properties: size",
6622 str(exc.exception))
6623
Simon Glasse0035c92023-01-11 16:10:17 -07006624 def testBlobSymbol(self):
6625 """Test a blob with symbols read from an ELF file"""
6626 elf_fname = self.ElfTestFile('blob_syms')
6627 TestFunctional._MakeInputFile('blob_syms', tools.read_file(elf_fname))
6628 TestFunctional._MakeInputFile('blob_syms.bin',
6629 tools.read_file(self.ElfTestFile('blob_syms.bin')))
6630
6631 data = self._DoReadFile('273_blob_symbol.dts')
6632
6633 syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
6634 addr = elf.GetSymbolAddress(elf_fname, '__my_start_sym')
6635 self.assertEqual(syms['_binman_sym_magic'].address, addr)
6636 self.assertEqual(syms['_binman_inset_prop_offset'].address, addr + 4)
6637 self.assertEqual(syms['_binman_inset_prop_size'].address, addr + 8)
6638
6639 sym_values = struct.pack('<LLL', elf.BINMAN_SYM_MAGIC_VALUE, 4, 8)
6640 expected = sym_values
6641 self.assertEqual(expected, data[:len(expected)])
6642
Simon Glass49e9c002023-01-11 16:10:19 -07006643 def testOffsetFromElf(self):
6644 """Test a blob with symbols read from an ELF file"""
6645 elf_fname = self.ElfTestFile('blob_syms')
6646 TestFunctional._MakeInputFile('blob_syms', tools.read_file(elf_fname))
6647 TestFunctional._MakeInputFile('blob_syms.bin',
6648 tools.read_file(self.ElfTestFile('blob_syms.bin')))
6649
6650 data = self._DoReadFile('274_offset_from_elf.dts')
6651
6652 syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
6653 base = elf.GetSymbolAddress(elf_fname, '__my_start_sym')
6654
6655 image = control.images['image']
6656 entries = image.GetEntries()
6657
6658 self.assertIn('inset', entries)
6659 inset = entries['inset']
6660
6661 self.assertEqual(base + 4, inset.offset);
6662 self.assertEqual(base + 4, inset.image_pos);
6663 self.assertEqual(4, inset.size);
6664
6665 self.assertIn('inset2', entries)
6666 inset = entries['inset2']
6667 self.assertEqual(base + 8, inset.offset);
6668 self.assertEqual(base + 8, inset.image_pos);
6669 self.assertEqual(4, inset.size);
6670
Jonas Karlmanc59ea892023-01-21 19:01:39 +00006671 def testFitAlign(self):
6672 """Test an image with an FIT with aligned external data"""
6673 data = self._DoReadFile('275_fit_align.dts')
6674 self.assertEqual(4096, len(data))
6675
6676 dtb = fdt.Fdt.FromData(data)
6677 dtb.Scan()
6678
6679 props = self._GetPropTree(dtb, ['data-position'])
6680 expected = {
6681 'u-boot:data-position': 1024,
6682 'fdt-1:data-position': 2048,
6683 'fdt-2:data-position': 3072,
6684 }
6685 self.assertEqual(expected, props)
6686
Jonas Karlman490f73c2023-01-21 19:02:12 +00006687 def testFitFirmwareLoadables(self):
6688 """Test an image with an FIT that use fit,firmware"""
6689 if not elf.ELF_TOOLS:
6690 self.skipTest('Python elftools not available')
6691 entry_args = {
6692 'of-list': 'test-fdt1',
6693 'default-dt': 'test-fdt1',
6694 'atf-bl31-path': 'bl31.elf',
6695 'tee-os-path': 'missing.bin',
6696 }
6697 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
Simon Glass14d64e32025-04-29 07:21:59 -06006698 with terminal.capture() as (stdout, stderr):
Simon Glass62f85902023-02-23 18:18:01 -07006699 data = self._DoReadFileDtb(
6700 '276_fit_firmware_loadables.dts',
6701 entry_args=entry_args,
6702 extra_indirs=[test_subdir])[0]
Jonas Karlman490f73c2023-01-21 19:02:12 +00006703
6704 dtb = fdt.Fdt.FromData(data)
6705 dtb.Scan()
6706
6707 node = dtb.GetNode('/configurations/conf-uboot-1')
6708 self.assertEqual('u-boot', node.props['firmware'].value)
6709 self.assertEqual(['atf-1', 'atf-2'],
6710 fdt_util.GetStringList(node, 'loadables'))
6711
6712 node = dtb.GetNode('/configurations/conf-atf-1')
6713 self.assertEqual('atf-1', node.props['firmware'].value)
6714 self.assertEqual(['u-boot', 'atf-2'],
6715 fdt_util.GetStringList(node, 'loadables'))
6716
6717 node = dtb.GetNode('/configurations/conf-missing-uboot-1')
6718 self.assertEqual('u-boot', node.props['firmware'].value)
6719 self.assertEqual(['atf-1', 'atf-2'],
6720 fdt_util.GetStringList(node, 'loadables'))
6721
6722 node = dtb.GetNode('/configurations/conf-missing-atf-1')
6723 self.assertEqual('atf-1', node.props['firmware'].value)
6724 self.assertEqual(['u-boot', 'atf-2'],
6725 fdt_util.GetStringList(node, 'loadables'))
6726
6727 node = dtb.GetNode('/configurations/conf-missing-tee-1')
6728 self.assertEqual('atf-1', node.props['firmware'].value)
6729 self.assertEqual(['u-boot', 'atf-2'],
6730 fdt_util.GetStringList(node, 'loadables'))
6731
Simon Glass9a1c7262023-02-22 12:14:49 -07006732 def testTooldir(self):
6733 """Test that we can specify the tooldir"""
Simon Glass14d64e32025-04-29 07:21:59 -06006734 with terminal.capture() as (stdout, stderr):
Simon Glass9a1c7262023-02-22 12:14:49 -07006735 self.assertEqual(0, self._DoBinman('--tooldir', 'fred',
6736 'tool', '-l'))
6737 self.assertEqual('fred', bintool.Bintool.tooldir)
6738
6739 # Check that the toolpath is updated correctly
6740 self.assertEqual(['fred'], tools.tool_search_paths)
6741
6742 # Try with a few toolpaths; the tooldir should be at the end
Simon Glass14d64e32025-04-29 07:21:59 -06006743 with terminal.capture() as (stdout, stderr):
Simon Glass9a1c7262023-02-22 12:14:49 -07006744 self.assertEqual(0, self._DoBinman(
6745 '--toolpath', 'mary', '--toolpath', 'anna', '--tooldir', 'fred',
6746 'tool', '-l'))
6747 self.assertEqual(['mary', 'anna', 'fred'], tools.tool_search_paths)
6748
Simon Glass49b77e82023-03-02 17:02:44 -07006749 def testReplaceSectionEntry(self):
6750 """Test replacing an entry in a section"""
6751 expect_data = b'w' * len(U_BOOT_DATA + COMPRESS_DATA)
6752 entry_data, expected_fdtmap, image = self._RunReplaceCmd('section/blob',
6753 expect_data, dts='241_replace_section_simple.dts')
6754 self.assertEqual(expect_data, entry_data)
6755
6756 entries = image.GetEntries()
6757 self.assertIn('section', entries)
6758 section = entries['section']
6759
6760 sect_entries = section.GetEntries()
6761 self.assertIn('blob', sect_entries)
6762 entry = sect_entries['blob']
6763 self.assertEqual(len(expect_data), entry.size)
6764
6765 fname = tools.get_output_filename('image-updated.bin')
6766 data = tools.read_file(fname)
6767
6768 new_blob_data = data[entry.image_pos:entry.image_pos + len(expect_data)]
6769 self.assertEqual(expect_data, new_blob_data)
6770
6771 self.assertEqual(U_BOOT_DATA,
6772 data[entry.image_pos + len(expect_data):]
6773 [:len(U_BOOT_DATA)])
6774
6775 def testReplaceSectionDeep(self):
6776 """Test replacing an entry in two levels of sections"""
6777 expect_data = b'w' * len(U_BOOT_DATA + COMPRESS_DATA)
6778 entry_data, expected_fdtmap, image = self._RunReplaceCmd(
6779 'section/section/blob', expect_data,
6780 dts='278_replace_section_deep.dts')
6781 self.assertEqual(expect_data, entry_data)
6782
6783 entries = image.GetEntries()
6784 self.assertIn('section', entries)
6785 section = entries['section']
6786
6787 subentries = section.GetEntries()
6788 self.assertIn('section', subentries)
6789 section = subentries['section']
6790
6791 sect_entries = section.GetEntries()
6792 self.assertIn('blob', sect_entries)
6793 entry = sect_entries['blob']
6794 self.assertEqual(len(expect_data), entry.size)
6795
6796 fname = tools.get_output_filename('image-updated.bin')
6797 data = tools.read_file(fname)
6798
6799 new_blob_data = data[entry.image_pos:entry.image_pos + len(expect_data)]
6800 self.assertEqual(expect_data, new_blob_data)
6801
6802 self.assertEqual(U_BOOT_DATA,
6803 data[entry.image_pos + len(expect_data):]
6804 [:len(U_BOOT_DATA)])
6805
6806 def testReplaceFitSibling(self):
6807 """Test an image with a FIT inside where we replace its sibling"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006808 self._SetupSplElf()
Simon Glass49b77e82023-03-02 17:02:44 -07006809 fname = TestFunctional._MakeInputFile('once', b'available once')
6810 self._DoReadFileRealDtb('277_replace_fit_sibling.dts')
6811 os.remove(fname)
6812
6813 try:
6814 tmpdir, updated_fname = self._SetupImageInTmpdir()
6815
6816 fname = os.path.join(tmpdir, 'update-blob')
6817 expected = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) + 1)
6818 tools.write_file(fname, expected)
6819
6820 self._DoBinman('replace', '-i', updated_fname, 'blob', '-f', fname)
6821 data = tools.read_file(updated_fname)
6822 start = len(U_BOOT_DTB_DATA)
6823 self.assertEqual(expected, data[start:start + len(expected)])
6824 map_fname = os.path.join(tmpdir, 'image-updated.map')
6825 self.assertFalse(os.path.exists(map_fname))
6826 finally:
6827 shutil.rmtree(tmpdir)
6828
Simon Glassc3fe97f2023-03-02 17:02:45 -07006829 def testX509Cert(self):
6830 """Test creating an X509 certificate"""
6831 keyfile = self.TestFile('key.key')
6832 entry_args = {
6833 'keyfile': keyfile,
6834 }
6835 data = self._DoReadFileDtb('279_x509_cert.dts',
6836 entry_args=entry_args)[0]
6837 cert = data[:-4]
6838 self.assertEqual(U_BOOT_DATA, data[-4:])
6839
6840 # TODO: verify the signature
6841
6842 def testX509CertMissing(self):
6843 """Test that binman still produces an image if openssl is missing"""
6844 keyfile = self.TestFile('key.key')
6845 entry_args = {
6846 'keyfile': 'keyfile',
6847 }
Simon Glass14d64e32025-04-29 07:21:59 -06006848 with terminal.capture() as (_, stderr):
Simon Glassc3fe97f2023-03-02 17:02:45 -07006849 self._DoTestFile('279_x509_cert.dts',
6850 force_missing_bintools='openssl',
6851 entry_args=entry_args)
6852 err = stderr.getvalue()
6853 self.assertRegex(err, "Image 'image'.*missing bintools.*: openssl")
6854
Jonas Karlman35305492023-02-25 19:01:33 +00006855 def testPackRockchipTpl(self):
6856 """Test that an image with a Rockchip TPL binary can be created"""
Simon Glasse3ef5ed2023-07-24 09:19:58 -06006857 data = self._DoReadFile('291_rockchip_tpl.dts')
Jonas Karlman35305492023-02-25 19:01:33 +00006858 self.assertEqual(ROCKCHIP_TPL_DATA, data[:len(ROCKCHIP_TPL_DATA)])
6859
Jonas Karlman1016ec72023-02-25 19:01:35 +00006860 def testMkimageMissingBlobMultiple(self):
6861 """Test missing blob with mkimage entry and multiple-data-files"""
Simon Glass14d64e32025-04-29 07:21:59 -06006862 with terminal.capture() as (stdout, stderr):
Simon Glasse3ef5ed2023-07-24 09:19:58 -06006863 self._DoTestFile('292_mkimage_missing_multiple.dts', allow_missing=True)
Jonas Karlman1016ec72023-02-25 19:01:35 +00006864 err = stderr.getvalue()
6865 self.assertIn("is missing external blobs and is non-functional", err)
6866
6867 with self.assertRaises(ValueError) as e:
Simon Glasse3ef5ed2023-07-24 09:19:58 -06006868 self._DoTestFile('292_mkimage_missing_multiple.dts', allow_missing=False)
Jonas Karlman1016ec72023-02-25 19:01:35 +00006869 self.assertIn("not found in input path", str(e.exception))
6870
Ivan Mikhaylov3cfcaa4d2023-03-08 01:13:40 +00006871 def _PrepareSignEnv(self, dts='280_fit_sign.dts'):
6872 """Prepare sign environment
6873
6874 Create private and public keys, add pubkey into dtb.
6875
6876 Returns:
6877 Tuple:
6878 FIT container
6879 Image name
6880 Private key
6881 DTB
6882 """
Marek Vasutf7413f02023-07-18 07:23:58 -06006883 self._SetupSplElf()
Ivan Mikhaylov3cfcaa4d2023-03-08 01:13:40 +00006884 data = self._DoReadFileRealDtb(dts)
6885 updated_fname = tools.get_output_filename('image-updated.bin')
6886 tools.write_file(updated_fname, data)
6887 dtb = tools.get_output_filename('source.dtb')
6888 private_key = tools.get_output_filename('test_key.key')
6889 public_key = tools.get_output_filename('test_key.crt')
6890 fit = tools.get_output_filename('fit.fit')
6891 key_dir = tools.get_output_dir()
6892
6893 tools.run('openssl', 'req', '-batch' , '-newkey', 'rsa:4096',
6894 '-sha256', '-new', '-nodes', '-x509', '-keyout',
6895 private_key, '-out', public_key)
6896 tools.run('fdt_add_pubkey', '-a', 'sha256,rsa4096', '-k', key_dir,
6897 '-n', 'test_key', '-r', 'conf', dtb)
6898
6899 return fit, updated_fname, private_key, dtb
6900
6901 def testSignSimple(self):
6902 """Test that a FIT container can be signed in image"""
6903 is_signed = False
6904 fit, fname, private_key, dtb = self._PrepareSignEnv()
6905
6906 # do sign with private key
6907 control.SignEntries(fname, None, private_key, 'sha256,rsa4096',
6908 ['fit'])
6909 is_signed = self._CheckSign(fit, dtb)
6910
6911 self.assertEqual(is_signed, True)
6912
6913 def testSignExactFIT(self):
6914 """Test that a FIT container can be signed and replaced in image"""
6915 is_signed = False
6916 fit, fname, private_key, dtb = self._PrepareSignEnv()
6917
6918 # Make sure we propagate the toolpath, since mkimage may not be on PATH
6919 args = []
6920 if self.toolpath:
6921 for path in self.toolpath:
6922 args += ['--toolpath', path]
6923
6924 # do sign with private key
6925 self._DoBinman(*args, 'sign', '-i', fname, '-k', private_key, '-a',
6926 'sha256,rsa4096', '-f', fit, 'fit')
6927 is_signed = self._CheckSign(fit, dtb)
6928
6929 self.assertEqual(is_signed, True)
6930
6931 def testSignNonFit(self):
6932 """Test a non-FIT entry cannot be signed"""
6933 is_signed = False
6934 fit, fname, private_key, _ = self._PrepareSignEnv(
6935 '281_sign_non_fit.dts')
6936
6937 # do sign with private key
6938 with self.assertRaises(ValueError) as e:
6939 self._DoBinman('sign', '-i', fname, '-k', private_key, '-a',
6940 'sha256,rsa4096', '-f', fit, 'u-boot')
6941 self.assertIn(
6942 "Node '/u-boot': Updating signatures is not supported with this entry type",
6943 str(e.exception))
6944
6945 def testSignMissingMkimage(self):
6946 """Test that FIT signing handles a missing mkimage tool"""
6947 fit, fname, private_key, _ = self._PrepareSignEnv()
6948
6949 # try to sign with a missing mkimage tool
6950 bintool.Bintool.set_missing_list(['mkimage'])
6951 with self.assertRaises(ValueError) as e:
6952 control.SignEntries(fname, None, private_key, 'sha256,rsa4096',
6953 ['fit'])
6954 self.assertIn("Node '/fit': Missing tool: 'mkimage'", str(e.exception))
6955
Simon Glass4abf7842023-07-18 07:23:54 -06006956 def testSymbolNoWrite(self):
6957 """Test disabling of symbol writing"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006958 self._SetupSplElf()
Simon Glass4abf7842023-07-18 07:23:54 -06006959 self.checkSymbols('282_symbols_disable.dts', U_BOOT_SPL_DATA, 0x1c,
6960 no_write_symbols=True)
6961
6962 def testSymbolNoWriteExpanded(self):
6963 """Test disabling of symbol writing in expanded entries"""
6964 entry_args = {
6965 'spl-dtb': '1',
6966 }
6967 self.checkSymbols('282_symbols_disable.dts', U_BOOT_SPL_NODTB_DATA +
6968 U_BOOT_SPL_DTB_DATA, 0x38,
6969 entry_args=entry_args, use_expanded=True,
6970 no_write_symbols=True)
6971
Marek Vasutf7413f02023-07-18 07:23:58 -06006972 def testMkimageSpecial(self):
6973 """Test mkimage ignores special hash-1 node"""
6974 data = self._DoReadFile('283_mkimage_special.dts')
6975
6976 # Just check that the data appears in the file somewhere
6977 self.assertIn(U_BOOT_DATA, data)
6978
Simon Glass2d94c422023-07-18 07:23:59 -06006979 def testFitFdtList(self):
6980 """Test an image with an FIT with the fit,fdt-list-val option"""
6981 entry_args = {
6982 'default-dt': 'test-fdt2',
6983 }
6984 data = self._DoReadFileDtb(
6985 '284_fit_fdt_list.dts',
6986 entry_args=entry_args,
6987 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
6988 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
6989 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
6990
Simon Glass83b8bfe2023-07-18 07:24:01 -06006991 def testSplEmptyBss(self):
6992 """Test an expanded SPL with a zero-size BSS"""
6993 # ELF file with a '__bss_size' symbol
6994 self._SetupSplElf(src_fname='bss_data_zero')
6995
6996 entry_args = {
6997 'spl-bss-pad': 'y',
6998 'spl-dtb': 'y',
6999 }
7000 data = self._DoReadFileDtb('285_spl_expand.dts',
7001 use_expanded=True, entry_args=entry_args)[0]
7002
Simon Glassfc792842023-07-18 07:24:04 -06007003 def testTemplate(self):
7004 """Test using a template"""
7005 TestFunctional._MakeInputFile('vga2.bin', b'#' + VGA_DATA)
7006 data = self._DoReadFile('286_template.dts')
7007 first = U_BOOT_DATA + VGA_DATA + U_BOOT_DTB_DATA
7008 second = U_BOOT_DATA + b'#' + VGA_DATA + U_BOOT_DTB_DATA
7009 self.assertEqual(U_BOOT_IMG_DATA + first + second, data)
7010
Simon Glass09490b02023-07-22 21:43:52 -06007011 dtb_fname1 = tools.get_output_filename('u-boot.dtb.tmpl1')
7012 self.assertTrue(os.path.exists(dtb_fname1))
7013 dtb = fdt.Fdt.FromData(tools.read_file(dtb_fname1))
7014 dtb.Scan()
7015 node1 = dtb.GetNode('/binman/template')
7016 self.assertTrue(node1)
7017 vga = dtb.GetNode('/binman/first/intel-vga')
7018 self.assertTrue(vga)
7019
Simon Glass54825e12023-07-22 21:43:56 -06007020 dtb_fname2 = tools.get_output_filename('u-boot.dtb.tmpl2')
7021 self.assertTrue(os.path.exists(dtb_fname2))
7022 dtb2 = fdt.Fdt.FromData(tools.read_file(dtb_fname2))
7023 dtb2.Scan()
7024 node2 = dtb2.GetNode('/binman/template')
7025 self.assertFalse(node2)
7026
Simon Glass9909c112023-07-18 07:24:05 -06007027 def testTemplateBlobMulti(self):
7028 """Test using a template with 'multiple-images' enabled"""
7029 TestFunctional._MakeInputFile('my-blob.bin', b'blob')
7030 TestFunctional._MakeInputFile('my-blob2.bin', b'other')
7031 retcode = self._DoTestFile('287_template_multi.dts')
7032
7033 self.assertEqual(0, retcode)
7034 image = control.images['image']
7035 image_fname = tools.get_output_filename('my-image.bin')
7036 data = tools.read_file(image_fname)
7037 self.assertEqual(b'blob@@@@other', data)
7038
Simon Glass5dc511b2023-07-18 07:24:06 -06007039 def testTemplateFit(self):
7040 """Test using a template in a FIT"""
7041 fit_data = self._DoReadFile('288_template_fit.dts')
7042 fname = os.path.join(self._indir, 'fit_data.fit')
7043 tools.write_file(fname, fit_data)
7044 out = tools.run('dumpimage', '-l', fname)
7045
Simon Glassaa6e0552023-07-18 07:24:07 -06007046 def testTemplateSection(self):
7047 """Test using a template in a section (not at top level)"""
7048 TestFunctional._MakeInputFile('vga2.bin', b'#' + VGA_DATA)
7049 data = self._DoReadFile('289_template_section.dts')
7050 first = U_BOOT_DATA + VGA_DATA + U_BOOT_DTB_DATA
7051 second = U_BOOT_DATA + b'#' + VGA_DATA + U_BOOT_DTB_DATA
7052 self.assertEqual(U_BOOT_IMG_DATA + first + second + first, data)
7053
Simon Glassf53a7bc2023-07-18 07:24:08 -06007054 def testMkimageSymbols(self):
7055 """Test using mkimage to build an image with symbols in it"""
7056 self._SetupSplElf('u_boot_binman_syms')
7057 data = self._DoReadFile('290_mkimage_sym.dts')
7058
7059 image = control.images['image']
7060 entries = image.GetEntries()
7061 self.assertIn('u-boot', entries)
7062 u_boot = entries['u-boot']
7063
7064 mkim = entries['mkimage']
7065 mkim_entries = mkim.GetEntries()
7066 self.assertIn('u-boot-spl', mkim_entries)
7067 spl = mkim_entries['u-boot-spl']
7068 self.assertIn('u-boot-spl2', mkim_entries)
7069 spl2 = mkim_entries['u-boot-spl2']
7070
7071 # skip the mkimage header and the area sizes
7072 mk_data = data[mkim.offset + 0x40:]
7073 size, term = struct.unpack('>LL', mk_data[:8])
7074
7075 # There should be only one image, so check that the zero terminator is
7076 # present
7077 self.assertEqual(0, term)
7078
7079 content = mk_data[8:8 + size]
7080
7081 # The image should contain the symbols from u_boot_binman_syms.c
7082 # Note that image_pos is adjusted by the base address of the image,
7083 # which is 0x10 in our test image
7084 spl_data = content[:0x18]
7085 content = content[0x1b:]
7086
7087 # After the header is a table of offsets for each image. There should
7088 # only be one image, then a 0 terminator, so figure out the real start
7089 # of the image data
7090 base = 0x40 + 8
7091
7092 # Check symbols in both u-boot-spl and u-boot-spl2
7093 for i in range(2):
7094 vals = struct.unpack('<LLQLL', spl_data)
7095
7096 # The image should contain the symbols from u_boot_binman_syms.c
7097 # Note that image_pos is adjusted by the base address of the image,
7098 # which is 0x10 in our 'u_boot_binman_syms' test image
7099 self.assertEqual(elf.BINMAN_SYM_MAGIC_VALUE, vals[0])
7100 self.assertEqual(base, vals[1])
7101 self.assertEqual(spl2.offset, vals[2])
7102 # figure out the internal positions of its components
7103 self.assertEqual(0x10 + u_boot.image_pos, vals[3])
7104
7105 # Check that spl and spl2 are actually at the indicated positions
7106 self.assertEqual(
7107 elf.BINMAN_SYM_MAGIC_VALUE,
7108 struct.unpack('<I', data[spl.image_pos:spl.image_pos + 4])[0])
7109 self.assertEqual(
7110 elf.BINMAN_SYM_MAGIC_VALUE,
7111 struct.unpack('<I', data[spl2.image_pos:spl2.image_pos + 4])[0])
7112
7113 self.assertEqual(len(U_BOOT_DATA), vals[4])
7114
7115 # Move to next
7116 spl_data = content[:0x18]
7117
Simon Glass86b3e472023-07-22 21:43:57 -06007118 def testTemplatePhandle(self):
7119 """Test using a template in a node containing a phandle"""
7120 entry_args = {
7121 'atf-bl31-path': 'bl31.elf',
7122 }
Simon Glass76ee0ca2023-08-03 17:23:58 -06007123 data = self._DoReadFileDtb('309_template_phandle.dts',
Simon Glass86b3e472023-07-22 21:43:57 -06007124 entry_args=entry_args)
7125 fname = tools.get_output_filename('image.bin')
7126 out = tools.run('dumpimage', '-l', fname)
7127
7128 # We should see the FIT description and one for each of the two images
7129 lines = out.splitlines()
7130 descs = [line.split()[-1] for line in lines if 'escription' in line]
7131 self.assertEqual(['test-desc', 'atf', 'fdt'], descs)
7132
7133 def testTemplatePhandleDup(self):
7134 """Test using a template in a node containing a phandle"""
7135 entry_args = {
7136 'atf-bl31-path': 'bl31.elf',
7137 }
7138 with self.assertRaises(ValueError) as e:
Simon Glass76ee0ca2023-08-03 17:23:58 -06007139 self._DoReadFileDtb('310_template_phandle_dup.dts',
Simon Glass86b3e472023-07-22 21:43:57 -06007140 entry_args=entry_args)
7141 self.assertIn(
7142 'Duplicate phandle 1 in nodes /binman/image/fit/images/atf/atf-bl31 and /binman/image-2/fit/images/atf/atf-bl31',
7143 str(e.exception))
7144
Neha Malcom Francis3b788942023-07-22 00:14:24 +05307145 def testTIBoardConfig(self):
7146 """Test that a schema validated board config file can be generated"""
Simon Glassf1264ba2023-07-24 09:19:59 -06007147 data = self._DoReadFile('293_ti_board_cfg.dts')
Neha Malcom Francis3b788942023-07-22 00:14:24 +05307148 self.assertEqual(TI_BOARD_CONFIG_DATA, data)
7149
Neha Malcom Francis8cd04512024-01-05 17:09:17 +05307150 def testTIBoardConfigLint(self):
7151 """Test that an incorrectly linted config file would generate error"""
7152 with self.assertRaises(ValueError) as e:
7153 data = self._DoReadFile('323_ti_board_cfg_phony.dts')
7154 self.assertIn("Yamllint error", str(e.exception))
7155
Neha Malcom Francis3b788942023-07-22 00:14:24 +05307156 def testTIBoardConfigCombined(self):
7157 """Test that a schema validated combined board config file can be generated"""
Simon Glassf1264ba2023-07-24 09:19:59 -06007158 data = self._DoReadFile('294_ti_board_cfg_combined.dts')
Neha Malcom Francis3b788942023-07-22 00:14:24 +05307159 configlen_noheader = TI_BOARD_CONFIG_DATA * 4
7160 self.assertGreater(data, configlen_noheader)
7161
7162 def testTIBoardConfigNoDataType(self):
7163 """Test that error is thrown when data type is not supported"""
7164 with self.assertRaises(ValueError) as e:
Simon Glassf1264ba2023-07-24 09:19:59 -06007165 data = self._DoReadFile('295_ti_board_cfg_no_type.dts')
Neha Malcom Francis3b788942023-07-22 00:14:24 +05307166 self.assertIn("Schema validation error", str(e.exception))
Simon Glassde244162023-01-07 14:07:08 -07007167
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307168 def testPackTiSecure(self):
7169 """Test that an image with a TI secured binary can be created"""
7170 keyfile = self.TestFile('key.key')
7171 entry_args = {
7172 'keyfile': keyfile,
7173 }
Simon Glassf1264ba2023-07-24 09:19:59 -06007174 data = self._DoReadFileDtb('296_ti_secure.dts',
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307175 entry_args=entry_args)[0]
7176 self.assertGreater(len(data), len(TI_UNSECURE_DATA))
7177
Manorit Chawdhry2e523b02023-12-29 16:16:27 +05307178 def testPackTiSecureFirewall(self):
7179 """Test that an image with a TI secured binary can be created"""
7180 keyfile = self.TestFile('key.key')
7181 entry_args = {
7182 'keyfile': keyfile,
7183 }
7184 data_no_firewall = self._DoReadFileDtb('296_ti_secure.dts',
7185 entry_args=entry_args)[0]
7186 data_firewall = self._DoReadFileDtb('324_ti_secure_firewall.dts',
7187 entry_args=entry_args)[0]
7188 self.assertGreater(len(data_firewall),len(data_no_firewall))
7189
7190 def testPackTiSecureFirewallMissingProperty(self):
7191 """Test that an image with a TI secured binary can be created"""
7192 keyfile = self.TestFile('key.key')
7193 entry_args = {
7194 'keyfile': keyfile,
7195 }
7196 with self.assertRaises(ValueError) as e:
7197 data_firewall = self._DoReadFileDtb('325_ti_secure_firewall_missing_property.dts',
7198 entry_args=entry_args)[0]
7199 self.assertRegex(str(e.exception), "Node '/binman/ti-secure': Subnode 'firewall-0-2' is missing properties: id,region")
7200
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307201 def testPackTiSecureMissingTool(self):
7202 """Test that an image with a TI secured binary (non-functional) can be created
7203 when openssl is missing"""
7204 keyfile = self.TestFile('key.key')
7205 entry_args = {
7206 'keyfile': keyfile,
7207 }
Simon Glass14d64e32025-04-29 07:21:59 -06007208 with terminal.capture() as (_, stderr):
Simon Glassf1264ba2023-07-24 09:19:59 -06007209 self._DoTestFile('296_ti_secure.dts',
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307210 force_missing_bintools='openssl',
7211 entry_args=entry_args)
7212 err = stderr.getvalue()
7213 self.assertRegex(err, "Image 'image'.*missing bintools.*: openssl")
7214
7215 def testPackTiSecureROM(self):
7216 """Test that a ROM image with a TI secured binary can be created"""
7217 keyfile = self.TestFile('key.key')
7218 entry_args = {
7219 'keyfile': keyfile,
7220 }
Simon Glassf1264ba2023-07-24 09:19:59 -06007221 data = self._DoReadFileDtb('297_ti_secure_rom.dts',
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307222 entry_args=entry_args)[0]
Simon Glassf1264ba2023-07-24 09:19:59 -06007223 data_a = self._DoReadFileDtb('299_ti_secure_rom_a.dts',
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307224 entry_args=entry_args)[0]
Simon Glassf1264ba2023-07-24 09:19:59 -06007225 data_b = self._DoReadFileDtb('300_ti_secure_rom_b.dts',
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307226 entry_args=entry_args)[0]
7227 self.assertGreater(len(data), len(TI_UNSECURE_DATA))
7228 self.assertGreater(len(data_a), len(TI_UNSECURE_DATA))
7229 self.assertGreater(len(data_b), len(TI_UNSECURE_DATA))
7230
7231 def testPackTiSecureROMCombined(self):
7232 """Test that a ROM image with a TI secured binary can be created"""
7233 keyfile = self.TestFile('key.key')
7234 entry_args = {
7235 'keyfile': keyfile,
7236 }
Simon Glassf1264ba2023-07-24 09:19:59 -06007237 data = self._DoReadFileDtb('298_ti_secure_rom_combined.dts',
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307238 entry_args=entry_args)[0]
7239 self.assertGreater(len(data), len(TI_UNSECURE_DATA))
7240
Christian Taedcke62ac29a2023-07-17 09:05:54 +02007241 def testEncryptedNoAlgo(self):
7242 """Test encrypted node with missing required properties"""
7243 with self.assertRaises(ValueError) as e:
7244 self._DoReadFileDtb('301_encrypted_no_algo.dts')
7245 self.assertIn(
7246 "Node '/binman/fit/images/u-boot/encrypted': 'encrypted' entry is missing properties: algo iv-filename",
7247 str(e.exception))
7248
7249 def testEncryptedInvalidIvfile(self):
7250 """Test encrypted node with invalid iv file"""
7251 with self.assertRaises(ValueError) as e:
7252 self._DoReadFileDtb('302_encrypted_invalid_iv_file.dts')
7253 self.assertIn("Filename 'invalid-iv-file' not found in input path",
7254 str(e.exception))
7255
7256 def testEncryptedMissingKey(self):
7257 """Test encrypted node with missing key properties"""
7258 with self.assertRaises(ValueError) as e:
7259 self._DoReadFileDtb('303_encrypted_missing_key.dts')
7260 self.assertIn(
7261 "Node '/binman/fit/images/u-boot/encrypted': Provide either 'key-filename' or 'key-source'",
7262 str(e.exception))
7263
7264 def testEncryptedKeySource(self):
7265 """Test encrypted node with key-source property"""
7266 data = self._DoReadFileDtb('304_encrypted_key_source.dts')[0]
7267
7268 dtb = fdt.Fdt.FromData(data)
7269 dtb.Scan()
7270
7271 node = dtb.GetNode('/images/u-boot/cipher')
7272 self.assertEqual('algo-name', node.props['algo'].value)
7273 self.assertEqual('key-source-value', node.props['key-source'].value)
7274 self.assertEqual(ENCRYPTED_IV_DATA,
7275 tools.to_bytes(''.join(node.props['iv'].value)))
7276 self.assertNotIn('key', node.props)
7277
7278 def testEncryptedKeyFile(self):
7279 """Test encrypted node with key-filename property"""
7280 data = self._DoReadFileDtb('305_encrypted_key_file.dts')[0]
7281
7282 dtb = fdt.Fdt.FromData(data)
7283 dtb.Scan()
7284
7285 node = dtb.GetNode('/images/u-boot/cipher')
7286 self.assertEqual('algo-name', node.props['algo'].value)
7287 self.assertEqual(ENCRYPTED_IV_DATA,
7288 tools.to_bytes(''.join(node.props['iv'].value)))
7289 self.assertEqual(ENCRYPTED_KEY_DATA,
7290 tools.to_bytes(''.join(node.props['key'].value)))
7291 self.assertNotIn('key-source', node.props)
7292
Lukas Funkee901faf2023-07-18 13:53:13 +02007293
7294 def testSplPubkeyDtb(self):
Simon Glass4b861272024-07-20 11:49:41 +01007295 """Test u_boot_spl_pubkey_dtb etype"""
7296 data = tools.read_file(self.TestFile("key.pem"))
7297 self._MakeInputFile("key.crt", data)
7298 self._DoReadFileRealDtb('306_spl_pubkey_dtb.dts')
7299 image = control.images['image']
7300 entries = image.GetEntries()
7301 dtb_entry = entries['u-boot-spl-pubkey-dtb']
7302 dtb_data = dtb_entry.GetData()
7303 dtb = fdt.Fdt.FromData(dtb_data)
7304 dtb.Scan()
Lukas Funkee901faf2023-07-18 13:53:13 +02007305
Simon Glass4b861272024-07-20 11:49:41 +01007306 signature_node = dtb.GetNode('/signature')
7307 self.assertIsNotNone(signature_node)
7308 key_node = signature_node.FindNode("key-key")
7309 self.assertIsNotNone(key_node)
7310 self.assertEqual(fdt_util.GetString(key_node, "required"), "conf")
7311 self.assertEqual(fdt_util.GetString(key_node, "algo"), "sha384,rsa4096")
7312 self.assertEqual(fdt_util.GetString(key_node, "key-name-hint"), "key")
Christian Taedcke62ac29a2023-07-17 09:05:54 +02007313
Lukas Funke712e1062023-08-03 17:22:14 +02007314 def testXilinxBootgenSigning(self):
7315 """Test xilinx-bootgen etype"""
7316 bootgen = bintool.Bintool.create('bootgen')
7317 self._CheckBintool(bootgen)
7318 data = tools.read_file(self.TestFile("key.key"))
7319 self._MakeInputFile("psk.pem", data)
7320 self._MakeInputFile("ssk.pem", data)
7321 self._SetupPmuFwlElf()
7322 self._SetupSplElf()
7323 self._DoReadFileRealDtb('307_xilinx_bootgen_sign.dts')
7324 image_fname = tools.get_output_filename('image.bin')
7325
7326 # Read partition header table and check if authentication is enabled
7327 bootgen_out = bootgen.run_cmd("-arch", "zynqmp",
7328 "-read", image_fname, "pht").splitlines()
7329 attributes = {"authentication": None,
7330 "core": None,
7331 "encryption": None}
7332
7333 for l in bootgen_out:
7334 for a in attributes.keys():
7335 if a in l:
7336 m = re.match(fr".*{a} \[([^]]+)\]", l)
7337 attributes[a] = m.group(1)
7338
7339 self.assertTrue(attributes['authentication'] == "rsa")
7340 self.assertTrue(attributes['core'] == "a53-0")
7341 self.assertTrue(attributes['encryption'] == "no")
7342
7343 def testXilinxBootgenSigningEncryption(self):
7344 """Test xilinx-bootgen etype"""
7345 bootgen = bintool.Bintool.create('bootgen')
7346 self._CheckBintool(bootgen)
7347 data = tools.read_file(self.TestFile("key.key"))
7348 self._MakeInputFile("psk.pem", data)
7349 self._MakeInputFile("ssk.pem", data)
7350 self._SetupPmuFwlElf()
7351 self._SetupSplElf()
7352 self._DoReadFileRealDtb('308_xilinx_bootgen_sign_enc.dts')
7353 image_fname = tools.get_output_filename('image.bin')
7354
7355 # Read boot header in order to verify encryption source and
7356 # encryption parameter
7357 bootgen_out = bootgen.run_cmd("-arch", "zynqmp",
7358 "-read", image_fname, "bh").splitlines()
7359 attributes = {"auth_only":
7360 {"re": r".*auth_only \[([^]]+)\]", "value": None},
7361 "encryption_keystore":
7362 {"re": r" *encryption_keystore \(0x28\) : (.*)",
7363 "value": None},
7364 }
7365
7366 for l in bootgen_out:
7367 for a in attributes.keys():
7368 if a in l:
7369 m = re.match(attributes[a]['re'], l)
7370 attributes[a] = m.group(1)
7371
7372 # Check if fsbl-attribute is set correctly
7373 self.assertTrue(attributes['auth_only'] == "true")
7374 # Check if key is stored in efuse
7375 self.assertTrue(attributes['encryption_keystore'] == "0xa5c3c5a3")
7376
7377 def testXilinxBootgenMissing(self):
7378 """Test that binman still produces an image if bootgen is missing"""
7379 data = tools.read_file(self.TestFile("key.key"))
7380 self._MakeInputFile("psk.pem", data)
7381 self._MakeInputFile("ssk.pem", data)
7382 self._SetupPmuFwlElf()
7383 self._SetupSplElf()
Simon Glass14d64e32025-04-29 07:21:59 -06007384 with terminal.capture() as (_, stderr):
Lukas Funke712e1062023-08-03 17:22:14 +02007385 self._DoTestFile('307_xilinx_bootgen_sign.dts',
7386 force_missing_bintools='bootgen')
7387 err = stderr.getvalue()
7388 self.assertRegex(err,
7389 "Image 'image'.*missing bintools.*: bootgen")
7390
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +05307391 def _GetCapsuleHeaders(self, data):
7392 """Get the capsule header contents
7393
7394 Args:
7395 data: Capsule file contents
7396
7397 Returns:
7398 Dict:
7399 key: Capsule Header name (str)
7400 value: Header field value (str)
7401 """
7402 capsule_file = os.path.join(self._indir, 'test.capsule')
7403 tools.write_file(capsule_file, data)
7404
7405 out = tools.run('mkeficapsule', '--dump-capsule', capsule_file)
7406 lines = out.splitlines()
7407
7408 re_line = re.compile(r'^([^:\-\t]*)(?:\t*\s*:\s*(.*))?$')
7409 vals = {}
7410 for line in lines:
7411 mat = re_line.match(line)
7412 if mat:
7413 vals[mat.group(1)] = mat.group(2)
7414
7415 return vals
7416
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307417 def _CheckCapsule(self, data, signed_capsule=False, version_check=False,
7418 capoemflags=False):
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +05307419 fmp_signature = "3153534D" # 'M', 'S', 'S', '1'
7420 fmp_size = "00000010"
7421 fmp_fw_version = "00000002"
7422 capsule_image_index = "00000001"
7423 oemflag = "00018000"
7424 auth_hdr_revision = "00000200"
7425 auth_hdr_cert_type = "00000EF1"
7426
7427 payload_data_len = len(EFI_CAPSULE_DATA)
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307428
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +05307429 hdr = self._GetCapsuleHeaders(data)
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307430
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +05307431 self.assertEqual(FW_MGMT_GUID.upper(), hdr['EFI_CAPSULE_HDR.CAPSULE_GUID'])
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307432
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +05307433 self.assertEqual(CAPSULE_IMAGE_GUID.upper(),
7434 hdr['FMP_CAPSULE_IMAGE_HDR.UPDATE_IMAGE_TYPE_ID'])
7435 self.assertEqual(capsule_image_index,
7436 hdr['FMP_CAPSULE_IMAGE_HDR.UPDATE_IMAGE_INDEX'])
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307437
7438 if capoemflags:
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +05307439 self.assertEqual(oemflag, hdr['EFI_CAPSULE_HDR.FLAGS'])
7440
7441 if signed_capsule:
7442 self.assertEqual(auth_hdr_revision,
7443 hdr['EFI_FIRMWARE_IMAGE_AUTH.AUTH_INFO.HDR.wREVISION'])
7444 self.assertEqual(auth_hdr_cert_type,
7445 hdr['EFI_FIRMWARE_IMAGE_AUTH.AUTH_INFO.HDR.wCERTTYPE'])
7446 self.assertEqual(WIN_CERT_TYPE_EFI_GUID.upper(),
7447 hdr['EFI_FIRMWARE_IMAGE_AUTH.AUTH_INFO.CERT_TYPE'])
7448
7449 if version_check:
7450 self.assertEqual(fmp_signature,
7451 hdr['FMP_PAYLOAD_HDR.SIGNATURE'])
7452 self.assertEqual(fmp_size,
7453 hdr['FMP_PAYLOAD_HDR.HEADER_SIZE'])
7454 self.assertEqual(fmp_fw_version,
7455 hdr['FMP_PAYLOAD_HDR.FW_VERSION'])
7456
7457 self.assertEqual(payload_data_len, int(hdr['Payload Image Size']))
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307458
Sughosh Ganu6b2d18a2023-10-10 14:40:59 +05307459 def _CheckEmptyCapsule(self, data, accept_capsule=False):
7460 if accept_capsule:
7461 capsule_hdr_guid = EMPTY_CAPSULE_ACCEPT_GUID
7462 else:
7463 capsule_hdr_guid = EMPTY_CAPSULE_REVERT_GUID
7464
7465 hdr = self._GetCapsuleHeaders(data)
7466
7467 self.assertEqual(capsule_hdr_guid.upper(),
7468 hdr['EFI_CAPSULE_HDR.CAPSULE_GUID'])
7469
7470 if accept_capsule:
7471 capsule_size = "0000002C"
7472 else:
7473 capsule_size = "0000001C"
7474 self.assertEqual(capsule_size,
7475 hdr['EFI_CAPSULE_HDR.CAPSULE_IMAGE_SIZE'])
7476
7477 if accept_capsule:
7478 self.assertEqual(CAPSULE_IMAGE_GUID.upper(), hdr['ACCEPT_IMAGE_GUID'])
7479
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307480 def testCapsuleGen(self):
7481 """Test generation of EFI capsule"""
7482 data = self._DoReadFile('311_capsule.dts')
7483
7484 self._CheckCapsule(data)
7485
7486 def testSignedCapsuleGen(self):
7487 """Test generation of EFI capsule"""
7488 data = tools.read_file(self.TestFile("key.key"))
7489 self._MakeInputFile("key.key", data)
7490 data = tools.read_file(self.TestFile("key.pem"))
7491 self._MakeInputFile("key.crt", data)
7492
7493 data = self._DoReadFile('312_capsule_signed.dts')
7494
7495 self._CheckCapsule(data, signed_capsule=True)
7496
7497 def testCapsuleGenVersionSupport(self):
7498 """Test generation of EFI capsule with version support"""
7499 data = self._DoReadFile('313_capsule_version.dts')
7500
7501 self._CheckCapsule(data, version_check=True)
7502
7503 def testCapsuleGenSignedVer(self):
7504 """Test generation of signed EFI capsule with version information"""
7505 data = tools.read_file(self.TestFile("key.key"))
7506 self._MakeInputFile("key.key", data)
7507 data = tools.read_file(self.TestFile("key.pem"))
7508 self._MakeInputFile("key.crt", data)
7509
7510 data = self._DoReadFile('314_capsule_signed_ver.dts')
7511
7512 self._CheckCapsule(data, signed_capsule=True, version_check=True)
7513
7514 def testCapsuleGenCapOemFlags(self):
7515 """Test generation of EFI capsule with OEM Flags set"""
7516 data = self._DoReadFile('315_capsule_oemflags.dts')
7517
7518 self._CheckCapsule(data, capoemflags=True)
7519
7520 def testCapsuleGenKeyMissing(self):
7521 """Test that binman errors out on missing key"""
7522 with self.assertRaises(ValueError) as e:
7523 self._DoReadFile('316_capsule_missing_key.dts')
7524
7525 self.assertIn("Both private key and public key certificate need to be provided",
7526 str(e.exception))
7527
7528 def testCapsuleGenIndexMissing(self):
7529 """Test that binman errors out on missing image index"""
7530 with self.assertRaises(ValueError) as e:
7531 self._DoReadFile('317_capsule_missing_index.dts')
7532
7533 self.assertIn("entry is missing properties: image-index",
7534 str(e.exception))
7535
7536 def testCapsuleGenGuidMissing(self):
7537 """Test that binman errors out on missing image GUID"""
7538 with self.assertRaises(ValueError) as e:
7539 self._DoReadFile('318_capsule_missing_guid.dts')
7540
7541 self.assertIn("entry is missing properties: image-guid",
7542 str(e.exception))
7543
Sughosh Ganu6b2d18a2023-10-10 14:40:59 +05307544 def testCapsuleGenAcceptCapsule(self):
7545 """Test generationg of accept EFI capsule"""
7546 data = self._DoReadFile('319_capsule_accept.dts')
7547
7548 self._CheckEmptyCapsule(data, accept_capsule=True)
7549
7550 def testCapsuleGenRevertCapsule(self):
7551 """Test generationg of revert EFI capsule"""
7552 data = self._DoReadFile('320_capsule_revert.dts')
7553
7554 self._CheckEmptyCapsule(data)
7555
7556 def testCapsuleGenAcceptGuidMissing(self):
7557 """Test that binman errors out on missing image GUID for accept capsule"""
7558 with self.assertRaises(ValueError) as e:
7559 self._DoReadFile('321_capsule_accept_missing_guid.dts')
7560
7561 self.assertIn("Image GUID needed for generating accept capsule",
7562 str(e.exception))
7563
7564 def testCapsuleGenEmptyCapsuleTypeMissing(self):
7565 """Test that capsule-type is specified"""
7566 with self.assertRaises(ValueError) as e:
7567 self._DoReadFile('322_empty_capsule_type_missing.dts')
7568
7569 self.assertIn("entry is missing properties: capsule-type",
7570 str(e.exception))
7571
7572 def testCapsuleGenAcceptOrRevertMissing(self):
7573 """Test that both accept and revert capsule are not specified"""
7574 with self.assertRaises(ValueError) as e:
7575 self._DoReadFile('323_capsule_accept_revert_missing.dts')
7576
Simon Glassa360b8f2024-06-23 11:55:06 -06007577 def test_assume_size(self):
7578 """Test handling of the assume-size property for external blob"""
7579 with self.assertRaises(ValueError) as e:
7580 self._DoTestFile('326_assume_size.dts', allow_missing=True,
7581 allow_fake_blobs=True)
7582 self.assertIn("contents size 0xa (10) exceeds section size 0x9 (9)",
7583 str(e.exception))
7584
7585 def test_assume_size_ok(self):
7586 """Test handling of the assume-size where it fits OK"""
Simon Glass14d64e32025-04-29 07:21:59 -06007587 with terminal.capture() as (stdout, stderr):
Simon Glassa360b8f2024-06-23 11:55:06 -06007588 self._DoTestFile('327_assume_size_ok.dts', allow_missing=True,
7589 allow_fake_blobs=True)
7590 err = stderr.getvalue()
7591 self.assertRegex(
7592 err,
7593 "Image '.*' has faked external blobs and is non-functional: .*")
7594
7595 def test_assume_size_no_fake(self):
7596 """Test handling of the assume-size where it fits OK"""
Simon Glass14d64e32025-04-29 07:21:59 -06007597 with terminal.capture() as (stdout, stderr):
Simon Glassa360b8f2024-06-23 11:55:06 -06007598 self._DoTestFile('327_assume_size_ok.dts', allow_missing=True)
7599 err = stderr.getvalue()
7600 self.assertRegex(
7601 err,
7602 "Image '.*' is missing external blobs and is non-functional: .*")
7603
Simon Glass5f7aadf2024-07-20 11:49:47 +01007604 def SetupAlternateDts(self):
7605 """Compile the .dts test files for alternative-fdt
7606
7607 Returns:
7608 tuple:
7609 str: Test directory created
7610 list of str: '.bin' files which we expect Binman to create
7611 """
7612 testdir = TestFunctional._MakeInputDir('dtb')
7613 dtb_list = []
7614 for fname in glob.glob(f'{self.TestFile("alt_dts")}/*.dts'):
7615 tmp_fname = fdt_util.EnsureCompiled(fname, testdir)
7616 base = os.path.splitext(os.path.basename(fname))[0]
7617 dtb_list.append(base + '.bin')
7618 shutil.move(tmp_fname, os.path.join(testdir, base + '.dtb'))
7619
7620 return testdir, dtb_list
7621
Simon Glassf3598922024-07-20 11:49:45 +01007622 def CheckAlternates(self, dts, phase, xpl_data):
7623 """Run the test for the alterative-fdt etype
7624
7625 Args:
7626 dts (str): Devicetree file to process
7627 phase (str): Phase to process ('spl', 'tpl' or 'vpl')
7628 xpl_data (bytes): Expected data for the phase's binary
7629
7630 Returns:
7631 dict of .dtb files produced
7632 key: str filename
7633 value: Fdt object
7634 """
Simon Glass5f7aadf2024-07-20 11:49:47 +01007635 dtb_list = self.SetupAlternateDts()[1]
Simon Glassf3598922024-07-20 11:49:45 +01007636
7637 entry_args = {
7638 f'{phase}-dtb': '1',
7639 f'{phase}-bss-pad': 'y',
7640 'of-spl-remove-props': 'prop-to-remove another-prop-to-get-rid-of',
7641 }
7642 data = self._DoReadFileDtb(dts, use_real_dtb=True, update_dtb=True,
7643 use_expanded=True, entry_args=entry_args)[0]
7644 self.assertEqual(xpl_data, data[:len(xpl_data)])
7645 rest = data[len(xpl_data):]
7646 pad_len = 10
7647 self.assertEqual(tools.get_bytes(0, pad_len), rest[:pad_len])
7648
7649 # Check the dtb is using the test file
7650 dtb_data = rest[pad_len:]
7651 dtb = fdt.Fdt.FromData(dtb_data)
7652 dtb.Scan()
7653 fdt_size = dtb.GetFdtObj().totalsize()
7654 self.assertEqual('model-not-set',
7655 fdt_util.GetString(dtb.GetRoot(), 'compatible'))
7656
7657 pad_len = 10
7658
7659 # Check the other output files
7660 dtbs = {}
7661 for fname in dtb_list:
7662 pathname = tools.get_output_filename(fname)
7663 self.assertTrue(os.path.exists(pathname))
7664
7665 data = tools.read_file(pathname)
7666 self.assertEqual(xpl_data, data[:len(xpl_data)])
7667 rest = data[len(xpl_data):]
7668
7669 self.assertEqual(tools.get_bytes(0, pad_len), rest[:pad_len])
7670 rest = rest[pad_len:]
7671
7672 dtb = fdt.Fdt.FromData(rest)
7673 dtb.Scan()
7674 dtbs[fname] = dtb
7675
7676 expected = 'one' if '1' in fname else 'two'
7677 self.assertEqual(f'u-boot,model-{expected}',
7678 fdt_util.GetString(dtb.GetRoot(), 'compatible'))
7679
7680 # Make sure the FDT is the same size as the 'main' one
7681 rest = rest[fdt_size:]
7682
7683 self.assertEqual(b'', rest)
7684 return dtbs
7685
7686 def testAlternatesFdt(self):
7687 """Test handling of alternates-fdt etype"""
7688 self._SetupTplElf()
7689 dtbs = self.CheckAlternates('328_alternates_fdt.dts', 'tpl',
7690 U_BOOT_TPL_NODTB_DATA)
7691 for dtb in dtbs.values():
7692 # Check for the node with the tag
7693 node = dtb.GetNode('/node')
7694 self.assertIsNotNone(node)
7695 self.assertEqual(5, len(node.props.keys()))
7696
7697 # Make sure the other node is still there
7698 self.assertIsNotNone(dtb.GetNode('/node/other-node'))
7699
7700 def testAlternatesFdtgrep(self):
7701 """Test handling of alternates-fdt etype using fdtgrep"""
7702 self._SetupTplElf()
7703 dtbs = self.CheckAlternates('329_alternates_fdtgrep.dts', 'tpl',
7704 U_BOOT_TPL_NODTB_DATA)
7705 for dtb in dtbs.values():
7706 # Check for the node with the tag
7707 node = dtb.GetNode('/node')
7708 self.assertIsNotNone(node)
7709 self.assertEqual({'some-prop', 'not-a-prop-to-remove'},
7710 node.props.keys())
7711
7712 # Make sure the other node is gone
7713 self.assertIsNone(dtb.GetNode('/node/other-node'))
7714
7715 def testAlternatesFdtgrepVpl(self):
7716 """Test handling of alternates-fdt etype using fdtgrep with vpl"""
7717 self._SetupVplElf()
7718 dtbs = self.CheckAlternates('330_alternates_vpl.dts', 'vpl',
7719 U_BOOT_VPL_NODTB_DATA)
7720
7721 def testAlternatesFdtgrepSpl(self):
7722 """Test handling of alternates-fdt etype using fdtgrep with spl"""
7723 self._SetupSplElf()
7724 dtbs = self.CheckAlternates('331_alternates_spl.dts', 'spl',
7725 U_BOOT_SPL_NODTB_DATA)
7726
7727 def testAlternatesFdtgrepInval(self):
7728 """Test alternates-fdt etype using fdtgrep with invalid phase"""
7729 self._SetupSplElf()
7730 with self.assertRaises(ValueError) as e:
7731 dtbs = self.CheckAlternates('332_alternates_inval.dts', 'spl',
7732 U_BOOT_SPL_NODTB_DATA)
7733 self.assertIn("Invalid U-Boot phase 'bad-phase': Use tpl/vpl/spl",
7734 str(e.exception))
7735
Simon Glasscd2783e2024-07-20 11:49:46 +01007736 def testFitFdtListDir(self):
7737 """Test an image with an FIT with FDT images using fit,fdt-list-dir"""
Simon Glass1bba8942024-08-26 13:11:34 -06007738 old_dir = os.getcwd()
7739 try:
7740 os.chdir(self._indir)
7741 self.CheckFitFdt('333_fit_fdt_dir.dts', False)
7742 finally:
7743 os.chdir(old_dir)
Simon Glasscd2783e2024-07-20 11:49:46 +01007744
Simon Glassd2a9d6e2024-08-26 13:11:37 -06007745 def testFitFdtListDirDefault(self):
7746 """Test an FIT fit,fdt-list-dir where the default DT in is a subdir"""
7747 old_dir = os.getcwd()
7748 try:
7749 os.chdir(self._indir)
7750 self.CheckFitFdt('333_fit_fdt_dir.dts', False,
7751 default_dt='rockchip/test-fdt2')
7752 finally:
7753 os.chdir(old_dir)
7754
Simon Glass5f7aadf2024-07-20 11:49:47 +01007755 def testFitFdtCompat(self):
7756 """Test an image with an FIT with compatible in the config nodes"""
7757 entry_args = {
7758 'of-list': 'model1 model2',
7759 'default-dt': 'model2',
7760 }
7761 testdir, dtb_list = self.SetupAlternateDts()
7762 data = self._DoReadFileDtb(
7763 '334_fit_fdt_compat.dts', use_real_dtb=True, update_dtb=True,
7764 entry_args=entry_args, extra_indirs=[testdir])[0]
7765
7766 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
7767
7768 fit = fdt.Fdt.FromData(fit_data)
7769 fit.Scan()
7770
7771 cnode = fit.GetNode('/configurations')
7772 self.assertIn('default', cnode.props)
7773 self.assertEqual('config-2', cnode.props['default'].value)
7774
7775 for seq in range(1, 2):
7776 name = f'config-{seq}'
7777 fnode = fit.GetNode('/configurations/%s' % name)
7778 self.assertIsNotNone(fnode)
7779 self.assertIn('compatible', fnode.props.keys())
7780 expected = 'one' if seq == 1 else 'two'
7781 self.assertEqual(f'u-boot,model-{expected}',
7782 fnode.props['compatible'].value)
7783
Simon Glassa04b9942024-07-20 11:49:48 +01007784 def testFitFdtPhase(self):
7785 """Test an image with an FIT with fdt-phase in the fdt nodes"""
7786 phase = 'tpl'
7787 entry_args = {
7788 f'{phase}-dtb': '1',
7789 f'{phase}-bss-pad': 'y',
7790 'of-spl-remove-props': 'prop-to-remove another-prop-to-get-rid-of',
7791 'of-list': 'model1 model2',
7792 'default-dt': 'model2',
7793 }
7794 testdir, dtb_list = self.SetupAlternateDts()
7795 data = self._DoReadFileDtb(
7796 '335_fit_fdt_phase.dts', use_real_dtb=True, update_dtb=True,
7797 entry_args=entry_args, extra_indirs=[testdir])[0]
7798 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
7799 fit = fdt.Fdt.FromData(fit_data)
7800 fit.Scan()
7801
7802 # Check that each FDT has only the expected properties for the phase
7803 for seq in range(1, 2):
7804 fnode = fit.GetNode(f'/images/fdt-{seq}')
7805 self.assertIsNotNone(fnode)
7806 dtb = fdt.Fdt.FromData(fnode.props['data'].bytes)
7807 dtb.Scan()
7808
7809 # Make sure that the 'bootph-pre-sram' tag in /node protects it from
7810 # removal
7811 node = dtb.GetNode('/node')
7812 self.assertIsNotNone(node)
7813 self.assertEqual({'some-prop', 'not-a-prop-to-remove'},
7814 node.props.keys())
7815
7816 # Make sure the other node is gone
7817 self.assertIsNone(dtb.GetNode('/node/other-node'))
7818
Simon Glassb553e8a2024-08-26 13:11:29 -06007819 def testMkeficapsuleMissing(self):
7820 """Test that binman complains if mkeficapsule is missing"""
7821 with self.assertRaises(ValueError) as e:
7822 self._DoTestFile('311_capsule.dts',
7823 force_missing_bintools='mkeficapsule')
7824 self.assertIn("Node '/binman/efi-capsule': Missing tool: 'mkeficapsule'",
7825 str(e.exception))
7826
7827 def testMkeficapsuleMissingOk(self):
7828 """Test that binman deals with mkeficapsule being missing"""
Simon Glass14d64e32025-04-29 07:21:59 -06007829 with terminal.capture() as (stdout, stderr):
Simon Glassb553e8a2024-08-26 13:11:29 -06007830 ret = self._DoTestFile('311_capsule.dts',
7831 force_missing_bintools='mkeficapsule',
7832 allow_missing=True)
7833 self.assertEqual(103, ret)
7834 err = stderr.getvalue()
7835 self.assertRegex(err, "Image 'image'.*missing bintools.*: mkeficapsule")
7836
Simon Glass4b0f4142024-08-26 13:11:40 -06007837 def testSymbolsBase(self):
7838 """Test handling of symbols-base"""
7839 self.checkSymbols('336_symbols_base.dts', U_BOOT_SPL_DATA, 0x1c,
7840 symbols_base=0)
7841
7842 def testSymbolsBaseExpanded(self):
7843 """Test handling of symbols-base with expanded entries"""
7844 entry_args = {
7845 'spl-dtb': '1',
7846 }
7847 self.checkSymbols('337_symbols_base_expand.dts', U_BOOT_SPL_NODTB_DATA +
7848 U_BOOT_SPL_DTB_DATA, 0x38,
7849 entry_args=entry_args, use_expanded=True,
7850 symbols_base=0)
7851
Simon Glass3eb30a42024-08-26 13:11:42 -06007852 def testSymbolsCompressed(self):
7853 """Test binman complains about symbols from a compressed section"""
Simon Glass14d64e32025-04-29 07:21:59 -06007854 with terminal.capture() as (stdout, stderr):
Simon Glass3eb30a42024-08-26 13:11:42 -06007855 self.checkSymbols('338_symbols_comp.dts', U_BOOT_SPL_DATA, None)
7856 out = stdout.getvalue()
7857 self.assertIn('Symbol-writing: no value for /binman/section/u-boot',
7858 out)
7859
Simon Glass9c25ef22024-08-26 13:11:43 -06007860 def testNxpImx8Image(self):
7861 """Test that binman can produce an iMX8 image"""
7862 self._DoTestFile('339_nxp_imx8.dts')
7863
Alice Guo1d334022025-04-28 18:37:39 +08007864 def testNxpHeaderDdrfw(self):
7865 """Test that binman can add a header to DDR PHY firmware images"""
7866 data = self._DoReadFile('346_nxp_ddrfw_imx95.dts')
7867 self.assertEqual(len(IMX_LPDDR_IMEM_DATA).to_bytes(4, 'little') +
7868 len(IMX_LPDDR_DMEM_DATA).to_bytes(4, 'little') +
7869 IMX_LPDDR_IMEM_DATA + IMX_LPDDR_DMEM_DATA, data)
7870
Alexander Kochetkova730a282024-09-16 11:24:46 +03007871 def testFitSignSimple(self):
7872 """Test that image with FIT and signature nodes can be signed"""
7873 if not elf.ELF_TOOLS:
7874 self.skipTest('Python elftools not available')
7875 entry_args = {
7876 'of-list': 'test-fdt1',
7877 'default-dt': 'test-fdt1',
7878 'atf-bl31-path': 'bl31.elf',
7879 }
7880 data = tools.read_file(self.TestFile("340_rsa2048.key"))
7881 self._MakeInputFile("keys/rsa2048.key", data)
7882
7883 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
7884 keys_subdir = os.path.join(self._indir, "keys")
7885 data = self._DoReadFileDtb(
7886 '340_fit_signature.dts',
7887 entry_args=entry_args,
7888 extra_indirs=[test_subdir, keys_subdir])[0]
7889
7890 dtb = fdt.Fdt.FromData(data)
7891 dtb.Scan()
7892
7893 conf = dtb.GetNode('/configurations/conf-uboot-1')
7894 self.assertIsNotNone(conf)
7895 signature = conf.FindNode('signature')
7896 self.assertIsNotNone(signature)
7897 self.assertIsNotNone(signature.props.get('value'))
7898
7899 images = dtb.GetNode('/images')
7900 self.assertIsNotNone(images)
7901 for subnode in images.subnodes:
7902 signature = subnode.FindNode('signature')
7903 self.assertIsNotNone(signature)
7904 self.assertIsNotNone(signature.props.get('value'))
7905
7906 def testFitSignKeyNotFound(self):
7907 """Test that missing keys raise an error"""
7908 if not elf.ELF_TOOLS:
7909 self.skipTest('Python elftools not available')
7910 entry_args = {
7911 'of-list': 'test-fdt1',
7912 'default-dt': 'test-fdt1',
7913 'atf-bl31-path': 'bl31.elf',
7914 }
7915 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
7916 with self.assertRaises(ValueError) as e:
7917 self._DoReadFileDtb(
7918 '340_fit_signature.dts',
7919 entry_args=entry_args,
7920 extra_indirs=[test_subdir])[0]
7921 self.assertIn(
7922 'Filename \'rsa2048.key\' not found in input path',
7923 str(e.exception))
7924
7925 def testFitSignMultipleKeyPaths(self):
7926 """Test that keys found in multiple paths raise an error"""
7927 if not elf.ELF_TOOLS:
7928 self.skipTest('Python elftools not available')
7929 entry_args = {
7930 'of-list': 'test-fdt1',
7931 'default-dt': 'test-fdt1',
7932 'atf-bl31-path': 'bl31.elf',
7933 }
7934 data = tools.read_file(self.TestFile("340_rsa2048.key"))
7935 self._MakeInputFile("keys1/rsa2048.key", data)
7936 data = tools.read_file(self.TestFile("340_rsa2048.key"))
7937 self._MakeInputFile("keys2/conf-rsa2048.key", data)
7938
7939 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
7940 keys_subdir1 = os.path.join(self._indir, "keys1")
7941 keys_subdir2 = os.path.join(self._indir, "keys2")
7942 with self.assertRaises(ValueError) as e:
7943 self._DoReadFileDtb(
7944 '341_fit_signature.dts',
7945 entry_args=entry_args,
7946 extra_indirs=[test_subdir, keys_subdir1, keys_subdir2])[0]
7947 self.assertIn(
7948 'Node \'/binman/fit\': multiple key paths found',
7949 str(e.exception))
7950
7951 def testFitSignNoSingatureNodes(self):
7952 """Test that fit,sign doens't raise error if no signature nodes found"""
7953 if not elf.ELF_TOOLS:
7954 self.skipTest('Python elftools not available')
7955 entry_args = {
7956 'of-list': 'test-fdt1',
7957 'default-dt': 'test-fdt1',
7958 'atf-bl31-path': 'bl31.elf',
7959 }
7960 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
7961 self._DoReadFileDtb(
7962 '342_fit_signature.dts',
7963 entry_args=entry_args,
7964 extra_indirs=[test_subdir])[0]
7965
Simon Glassa360b8f2024-06-23 11:55:06 -06007966
Paul HENRYSff318462024-11-25 18:47:17 +01007967 def testSimpleFitEncryptedData(self):
7968 """Test an image with a FIT containing data to be encrypted"""
7969 data = tools.read_file(self.TestFile("aes256.bin"))
7970 self._MakeInputFile("keys/aes256.bin", data)
7971
7972 keys_subdir = os.path.join(self._indir, "keys")
7973 data = self._DoReadFileDtb(
7974 '343_fit_encrypt_data.dts',
7975 extra_indirs=[keys_subdir])[0]
7976
7977 fit = fdt.Fdt.FromData(data)
7978 fit.Scan()
7979
7980 # Extract the encrypted data and the Initialization Vector from the FIT
7981 node = fit.GetNode('/images/u-boot')
7982 subnode = fit.GetNode('/images/u-boot/cipher')
7983 data_size_unciphered = int.from_bytes(fit.GetProps(node)['data-size-unciphered'].bytes,
7984 byteorder='big')
7985 self.assertEqual(data_size_unciphered, len(U_BOOT_NODTB_DATA))
7986
7987 # Retrieve the key name from the FIT removing any null byte
7988 key_name = fit.GetProps(subnode)['key-name-hint'].bytes.replace(b'\x00', b'')
7989 with open(self.TestFile(key_name.decode('ascii') + '.bin'), 'rb') as file:
7990 key = file.read()
7991 iv = fit.GetProps(subnode)['iv'].bytes.hex()
7992 enc_data = fit.GetProps(node)['data'].bytes
7993 outdir = tools.get_output_dir()
7994 enc_data_file = os.path.join(outdir, 'encrypted_data.bin')
7995 tools.write_file(enc_data_file, enc_data)
7996 data_file = os.path.join(outdir, 'data.bin')
7997
7998 # Decrypt the encrypted data from the FIT and compare the data
7999 tools.run('openssl', 'enc', '-aes-256-cbc', '-nosalt', '-d', '-in',
8000 enc_data_file, '-out', data_file, '-K', key.hex(), '-iv', iv)
8001 with open(data_file, 'r') as file:
8002 dec_data = file.read()
8003 self.assertEqual(U_BOOT_NODTB_DATA, dec_data.encode('ascii'))
8004
8005 def testSimpleFitEncryptedDataMissingKey(self):
8006 """Test an image with a FIT containing data to be encrypted but with a missing key"""
8007 with self.assertRaises(ValueError) as e:
8008 self._DoReadFile('344_fit_encrypt_data_no_key.dts')
8009
8010 self.assertIn("Filename 'aes256.bin' not found in input path", str(e.exception))
8011
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01008012 def testFitFdtName(self):
8013 """Test an image with an FIT with multiple FDT images using NAME"""
8014 self.CheckFitFdt('345_fit_fdt_name.dts', use_seq_num=False)
8015
Neha Malcom Francisa25b4832025-03-17 10:24:20 +05308016 def testRemoveTemplate(self):
8017 """Test whether template is removed"""
8018 TestFunctional._MakeInputFile('my-blob.bin', b'blob')
8019 TestFunctional._MakeInputFile('my-blob2.bin', b'other')
8020 self._DoTestFile('346_remove_template.dts',
8021 force_missing_bintools='openssl',)
8022
Simon Glassac599912017-11-12 21:52:22 -07008023if __name__ == "__main__":
8024 unittest.main()