blob: 4a8d330c8f89adc3c248199760a936dd243158a2 [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
Yannic Moog4a333ea2025-06-13 14:02:41 +020087MISSING_DATA = b'missing'
Simon Glass303f62f2019-05-17 22:00:46 -060088REFCODE_DATA = b'refcode'
Simon Glassba7985d2019-08-24 07:23:07 -060089FSP_M_DATA = b'fsp_m'
Simon Glass4d9086d2019-10-20 21:31:35 -060090FSP_S_DATA = b'fsp_s'
Simon Glass9ea87b22019-10-20 21:31:36 -060091FSP_T_DATA = b'fsp_t'
Bryan Brattlof5b45f4b2025-05-15 10:16:19 -050092ATF_BL1_DATA = b'bl1'
Simon Glass559c4de2020-09-01 05:13:58 -060093ATF_BL31_DATA = b'bl31'
Roger Quadros5cdcea02022-02-19 20:50:04 +020094TEE_OS_DATA = b'this is some tee OS data'
Neha Malcom Francis59be2552023-12-05 15:12:18 +053095TI_DM_DATA = b'tidmtidm'
Simon Glass3efb2972021-11-23 21:08:59 -070096ATF_BL2U_DATA = b'bl2u'
Bin Mengc0b15742021-05-10 20:23:33 +080097OPENSBI_DATA = b'opensbi'
Samuel Holland9d8cc632020-10-21 21:12:15 -050098SCP_DATA = b'scp'
Jonas Karlman35305492023-02-25 19:01:33 +000099ROCKCHIP_TPL_DATA = b'rockchip-tpl'
Simon Glassa435cd12020-09-01 05:13:59 -0600100TEST_FDT1_DATA = b'fdt1'
101TEST_FDT2_DATA = b'test-fdt2'
Simon Glassa0729502020-09-06 10:35:33 -0600102ENV_DATA = b'var1=1\nvar2="2"'
Christian Taedcke62ac29a2023-07-17 09:05:54 +0200103ENCRYPTED_IV_DATA = b'123456'
104ENCRYPTED_KEY_DATA = b'abcde'
Philippe Reynesebe96cb2022-03-28 22:57:04 +0200105PRE_LOAD_MAGIC = b'UBSH'
106PRE_LOAD_VERSION = 0x11223344.to_bytes(4, 'big')
107PRE_LOAD_HDR_SIZE = 0x00001000.to_bytes(4, 'big')
Neha Malcom Francis3b788942023-07-22 00:14:24 +0530108TI_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 +0530109TI_UNSECURE_DATA = b'unsecuredata'
Alice Guo1d334022025-04-28 18:37:39 +0800110IMX_LPDDR_IMEM_DATA = b'qwertyuiop1234567890'
111IMX_LPDDR_DMEM_DATA = b'asdfghjklzxcvbnm'
Simon Glassa435cd12020-09-01 05:13:59 -0600112
113# Subdirectory of the input dir to use to put test FDTs
114TEST_FDT_SUBDIR = 'fdts'
Simon Glassdb168d42018-07-17 13:25:39 -0600115
Simon Glass2c6adba2019-07-20 12:23:47 -0600116# The expected size for the device tree in some tests
Simon Glass4c613bf2019-07-08 14:25:50 -0600117EXTRACT_DTB_SIZE = 0x3c9
118
Simon Glass2c6adba2019-07-20 12:23:47 -0600119# Properties expected to be in the device tree when update_dtb is used
120BASE_DTB_PROPS = ['offset', 'size', 'image-pos']
121
Simon Glassfb30e292019-07-20 12:23:51 -0600122# Extra properties expected to be in the device tree when allow-repack is used
123REPACK_DTB_PROPS = ['orig-offset', 'orig-size']
124
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +0200125# Supported compression bintools
Stefan Herbrechtsmeiera5e4dcb2022-08-19 16:25:38 +0200126COMP_BINTOOLS = ['bzip2', 'gzip', 'lz4', 'lzma_alone', 'lzop', 'xz', 'zstd']
Simon Glass57454f42016-11-25 20:15:52 -0700127
Simon Glassad5cfe12023-01-07 14:07:14 -0700128TEE_ADDR = 0x5678
129
Sughosh Ganu269ee6d2023-08-22 23:09:59 +0530130# Firmware Management Protocol(FMP) GUID
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +0530131FW_MGMT_GUID = '6dcbd5ed-e82d-4c44-bda1-7194199ad92a'
Sughosh Ganu269ee6d2023-08-22 23:09:59 +0530132# Image GUID specified in the DTS
Caleb Connolly4bdc9602024-08-30 13:34:35 +0100133CAPSULE_IMAGE_GUID = '985F2937-7C2E-5E9A-8A5E-8E063312964B'
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +0530134# Windows cert GUID
135WIN_CERT_TYPE_EFI_GUID = '4aafd29d-68df-49ee-8aa9-347d375665a7'
Sughosh Ganu6b2d18a2023-10-10 14:40:59 +0530136# Empty capsule GUIDs
137EMPTY_CAPSULE_ACCEPT_GUID = '0c996046-bcc0-4d04-85ec-e1fcedf1c6f8'
138EMPTY_CAPSULE_REVERT_GUID = 'acd58b4b-c0e8-475f-99b5-6b3f7e07aaf0'
Sughosh Ganu269ee6d2023-08-22 23:09:59 +0530139
Simon Glass57454f42016-11-25 20:15:52 -0700140class TestFunctional(unittest.TestCase):
141 """Functional tests for binman
142
143 Most of these use a sample .dts file to build an image and then check
144 that it looks correct. The sample files are in the test/ subdirectory
145 and are numbered.
146
147 For each entry type a very small test file is created using fixed
148 string contents. This makes it easy to test that things look right, and
149 debug problems.
150
151 In some cases a 'real' file must be used - these are also supplied in
152 the test/ diurectory.
153 """
154 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600155 def setUpClass(cls):
Simon Glassb3393262017-11-12 21:52:20 -0700156 global entry
Simon Glassc585dd42020-04-17 18:09:03 -0600157 from binman import entry
Simon Glassb3393262017-11-12 21:52:20 -0700158
Simon Glass57454f42016-11-25 20:15:52 -0700159 # Handle the case where argv[0] is 'python'
Simon Glass862f8e22019-08-24 07:22:43 -0600160 cls._binman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
161 cls._binman_pathname = os.path.join(cls._binman_dir, 'binman')
Simon Glass57454f42016-11-25 20:15:52 -0700162
163 # Create a temporary directory for input files
Simon Glass862f8e22019-08-24 07:22:43 -0600164 cls._indir = tempfile.mkdtemp(prefix='binmant.')
Simon Glass57454f42016-11-25 20:15:52 -0700165
166 # Create some test files
167 TestFunctional._MakeInputFile('u-boot.bin', U_BOOT_DATA)
168 TestFunctional._MakeInputFile('u-boot.img', U_BOOT_IMG_DATA)
169 TestFunctional._MakeInputFile('spl/u-boot-spl.bin', U_BOOT_SPL_DATA)
Simon Glass8425a1f2018-07-17 13:25:48 -0600170 TestFunctional._MakeInputFile('tpl/u-boot-tpl.bin', U_BOOT_TPL_DATA)
Simon Glass56d05412022-02-28 07:16:54 -0700171 TestFunctional._MakeInputFile('vpl/u-boot-vpl.bin', U_BOOT_VPL_DATA)
Simon Glass57454f42016-11-25 20:15:52 -0700172 TestFunctional._MakeInputFile('blobfile', BLOB_DATA)
Simon Glass72232452016-11-25 20:15:53 -0700173 TestFunctional._MakeInputFile('me.bin', ME_DATA)
174 TestFunctional._MakeInputFile('vga.bin', VGA_DATA)
Simon Glass862f8e22019-08-24 07:22:43 -0600175 cls._ResetDtbs()
Simon Glass0b074d62019-08-24 07:22:48 -0600176
Jagdish Gediya311d4842018-09-03 21:35:08 +0530177 TestFunctional._MakeInputFile('u-boot-br.bin', PPC_MPC85XX_BR_DATA)
Simon Glass0b074d62019-08-24 07:22:48 -0600178
Simon Glassabab18c2019-08-24 07:22:49 -0600179 TestFunctional._MakeInputFile('u-boot-x86-start16.bin', X86_START16_DATA)
180 TestFunctional._MakeInputFile('spl/u-boot-x86-start16-spl.bin',
Simon Glasse83679d2017-11-12 21:52:26 -0700181 X86_START16_SPL_DATA)
Simon Glassabab18c2019-08-24 07:22:49 -0600182 TestFunctional._MakeInputFile('tpl/u-boot-x86-start16-tpl.bin',
Simon Glassed40e962018-09-14 04:57:10 -0600183 X86_START16_TPL_DATA)
Simon Glass0b074d62019-08-24 07:22:48 -0600184
185 TestFunctional._MakeInputFile('u-boot-x86-reset16.bin',
186 X86_RESET16_DATA)
187 TestFunctional._MakeInputFile('spl/u-boot-x86-reset16-spl.bin',
188 X86_RESET16_SPL_DATA)
189 TestFunctional._MakeInputFile('tpl/u-boot-x86-reset16-tpl.bin',
190 X86_RESET16_TPL_DATA)
191
Simon Glass57454f42016-11-25 20:15:52 -0700192 TestFunctional._MakeInputFile('u-boot-nodtb.bin', U_BOOT_NODTB_DATA)
Simon Glass3d274232017-11-12 21:52:27 -0700193 TestFunctional._MakeInputFile('spl/u-boot-spl-nodtb.bin',
194 U_BOOT_SPL_NODTB_DATA)
Simon Glass3fb4f422018-09-14 04:57:32 -0600195 TestFunctional._MakeInputFile('tpl/u-boot-tpl-nodtb.bin',
196 U_BOOT_TPL_NODTB_DATA)
Simon Glass56d05412022-02-28 07:16:54 -0700197 TestFunctional._MakeInputFile('vpl/u-boot-vpl-nodtb.bin',
198 U_BOOT_VPL_NODTB_DATA)
Simon Glassb4176d42016-11-25 20:15:56 -0700199 TestFunctional._MakeInputFile('fsp.bin', FSP_DATA)
200 TestFunctional._MakeInputFile('cmc.bin', CMC_DATA)
Bin Mengd7bcdf52017-08-15 22:41:54 -0700201 TestFunctional._MakeInputFile('vbt.bin', VBT_DATA)
Simon Glassa409c932017-11-12 21:52:28 -0700202 TestFunctional._MakeInputFile('mrc.bin', MRC_DATA)
Simon Glassdb168d42018-07-17 13:25:39 -0600203 TestFunctional._MakeInputFile('ecrw.bin', CROS_EC_RW_DATA)
Simon Glassc1ae83c2018-07-17 13:25:44 -0600204 TestFunctional._MakeInputDir('devkeys')
205 TestFunctional._MakeInputFile('bmpblk.bin', BMPBLK_DATA)
Simon Glass41902e42018-10-01 12:22:31 -0600206 TestFunctional._MakeInputFile('refcode.bin', REFCODE_DATA)
Simon Glassba7985d2019-08-24 07:23:07 -0600207 TestFunctional._MakeInputFile('fsp_m.bin', FSP_M_DATA)
Simon Glass4d9086d2019-10-20 21:31:35 -0600208 TestFunctional._MakeInputFile('fsp_s.bin', FSP_S_DATA)
Simon Glass9ea87b22019-10-20 21:31:36 -0600209 TestFunctional._MakeInputFile('fsp_t.bin', FSP_T_DATA)
Alice Guo1d334022025-04-28 18:37:39 +0800210 TestFunctional._MakeInputFile('lpddr5_imem.bin', IMX_LPDDR_IMEM_DATA)
211 TestFunctional._MakeInputFile('lpddr5_dmem.bin', IMX_LPDDR_DMEM_DATA)
Simon Glass57454f42016-11-25 20:15:52 -0700212
Simon Glassf6290892019-08-24 07:22:53 -0600213 cls._elf_testdir = os.path.join(cls._indir, 'elftest')
214 elf_test.BuildElfTestFiles(cls._elf_testdir)
215
Simon Glass72232452016-11-25 20:15:53 -0700216 # ELF file with a '_dt_ucode_base_size' symbol
Simon Glass4affd4b2019-08-24 07:22:54 -0600217 TestFunctional._MakeInputFile('u-boot',
Simon Glass80025522022-01-29 14:14:04 -0700218 tools.read_file(cls.ElfTestFile('u_boot_ucode_ptr')))
Simon Glass72232452016-11-25 20:15:53 -0700219
220 # Intel flash descriptor file
Simon Glasse88cef92020-07-09 18:39:41 -0600221 cls._SetupDescriptor()
Simon Glass72232452016-11-25 20:15:53 -0700222
Simon Glass862f8e22019-08-24 07:22:43 -0600223 shutil.copytree(cls.TestFile('files'),
224 os.path.join(cls._indir, 'files'))
Simon Glassac6328c2018-09-14 04:57:28 -0600225
Neha Malcom Francis3b788942023-07-22 00:14:24 +0530226 shutil.copytree(cls.TestFile('yaml'),
227 os.path.join(cls._indir, 'yaml'))
228
Simon Glass7ba33592018-09-14 04:57:26 -0600229 TestFunctional._MakeInputFile('compress', COMPRESS_DATA)
Simon Glassd92c8362020-10-26 17:40:25 -0600230 TestFunctional._MakeInputFile('compress_big', COMPRESS_DATA_BIG)
Bryan Brattlof5b45f4b2025-05-15 10:16:19 -0500231 TestFunctional._MakeInputFile('bl1.bin', ATF_BL1_DATA)
Simon Glass559c4de2020-09-01 05:13:58 -0600232 TestFunctional._MakeInputFile('bl31.bin', ATF_BL31_DATA)
Roger Quadros5cdcea02022-02-19 20:50:04 +0200233 TestFunctional._MakeInputFile('tee-pager.bin', TEE_OS_DATA)
Neha Malcom Francis59be2552023-12-05 15:12:18 +0530234 TestFunctional._MakeInputFile('dm.bin', TI_DM_DATA)
Simon Glass3efb2972021-11-23 21:08:59 -0700235 TestFunctional._MakeInputFile('bl2u.bin', ATF_BL2U_DATA)
Bin Mengc0b15742021-05-10 20:23:33 +0800236 TestFunctional._MakeInputFile('fw_dynamic.bin', OPENSBI_DATA)
Samuel Holland9d8cc632020-10-21 21:12:15 -0500237 TestFunctional._MakeInputFile('scp.bin', SCP_DATA)
Jonas Karlman35305492023-02-25 19:01:33 +0000238 TestFunctional._MakeInputFile('rockchip-tpl.bin', ROCKCHIP_TPL_DATA)
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +0530239 TestFunctional._MakeInputFile('ti_unsecure.bin', TI_UNSECURE_DATA)
Sughosh Ganu269ee6d2023-08-22 23:09:59 +0530240 TestFunctional._MakeInputFile('capsule_input.bin', EFI_CAPSULE_DATA)
Simon Glass7ba33592018-09-14 04:57:26 -0600241
Simon Glassa435cd12020-09-01 05:13:59 -0600242 # Add a few .dtb files for testing
243 TestFunctional._MakeInputFile('%s/test-fdt1.dtb' % TEST_FDT_SUBDIR,
244 TEST_FDT1_DATA)
245 TestFunctional._MakeInputFile('%s/test-fdt2.dtb' % TEST_FDT_SUBDIR,
246 TEST_FDT2_DATA)
247
Simon Glassa0729502020-09-06 10:35:33 -0600248 TestFunctional._MakeInputFile('env.txt', ENV_DATA)
249
Simon Glass5f423422022-03-05 20:19:12 -0700250 # ELF file with two sections in different parts of memory, used for both
251 # ATF and OP_TEE
252 TestFunctional._MakeInputFile('bl31.elf',
253 tools.read_file(cls.ElfTestFile('elf_sections')))
254 TestFunctional._MakeInputFile('tee.elf',
255 tools.read_file(cls.ElfTestFile('elf_sections')))
256
Simon Glassad5cfe12023-01-07 14:07:14 -0700257 # Newer OP_TEE file in v1 binary format
258 cls.make_tee_bin('tee.bin')
259
Christian Taedcke62ac29a2023-07-17 09:05:54 +0200260 # test files for encrypted tests
261 TestFunctional._MakeInputFile('encrypted-file.iv', ENCRYPTED_IV_DATA)
262 TestFunctional._MakeInputFile('encrypted-file.key', ENCRYPTED_KEY_DATA)
263
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +0200264 cls.comp_bintools = {}
265 for name in COMP_BINTOOLS:
266 cls.comp_bintools[name] = bintool.Bintool.create(name)
Simon Glass1de34482019-07-08 13:18:53 -0600267
Simon Glass57454f42016-11-25 20:15:52 -0700268 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600269 def tearDownClass(cls):
Simon Glass57454f42016-11-25 20:15:52 -0700270 """Remove the temporary input directory and its contents"""
Simon Glass862f8e22019-08-24 07:22:43 -0600271 if cls.preserve_indir:
272 print('Preserving input dir: %s' % cls._indir)
Simon Glass1c420c92019-07-08 13:18:49 -0600273 else:
Simon Glass862f8e22019-08-24 07:22:43 -0600274 if cls._indir:
275 shutil.rmtree(cls._indir)
276 cls._indir = None
Simon Glass57454f42016-11-25 20:15:52 -0700277
Simon Glass1c420c92019-07-08 13:18:49 -0600278 @classmethod
Simon Glasscebfab22019-07-08 13:18:50 -0600279 def setup_test_args(cls, preserve_indir=False, preserve_outdirs=False,
Simon Glass17899e42025-04-29 07:22:06 -0600280 toolpath=None, verbosity=None, no_capture=False):
Simon Glass1c420c92019-07-08 13:18:49 -0600281 """Accept arguments controlling test execution
282
283 Args:
284 preserve_indir: Preserve the shared input directory used by all
285 tests in this class.
286 preserve_outdir: Preserve the output directories used by tests. Each
287 test has its own, so this is normally only useful when running a
288 single test.
Simon Glass1a0b27d2025-04-29 07:22:00 -0600289 toolpath: list of paths to use for tools
Simon Glass1c420c92019-07-08 13:18:49 -0600290 """
291 cls.preserve_indir = preserve_indir
292 cls.preserve_outdirs = preserve_outdirs
Simon Glasscebfab22019-07-08 13:18:50 -0600293 cls.toolpath = toolpath
Simon Glassf46732a2019-07-08 14:25:29 -0600294 cls.verbosity = verbosity
Simon Glass17899e42025-04-29 07:22:06 -0600295 cls.no_capture = no_capture
Simon Glass1c420c92019-07-08 13:18:49 -0600296
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +0200297 def _CheckBintool(self, bintool):
298 if not bintool.is_present():
299 self.skipTest('%s not available' % bintool.name)
300
Simon Glass1de34482019-07-08 13:18:53 -0600301 def _CheckLz4(self):
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +0200302 bintool = self.comp_bintools['lz4']
303 self._CheckBintool(bintool)
Simon Glass1de34482019-07-08 13:18:53 -0600304
Simon Glassee9d10d2019-07-20 12:24:09 -0600305 def _CleanupOutputDir(self):
306 """Remove the temporary output directory"""
307 if self.preserve_outdirs:
308 print('Preserving output dir: %s' % tools.outdir)
309 else:
Simon Glass80025522022-01-29 14:14:04 -0700310 tools._finalise_for_test()
Simon Glassee9d10d2019-07-20 12:24:09 -0600311
Simon Glass57454f42016-11-25 20:15:52 -0700312 def setUp(self):
313 # Enable this to turn on debugging output
Simon Glass011f1b32022-01-29 14:14:15 -0700314 # tout.init(tout.DEBUG)
Simon Glass5dc22cf2025-02-03 09:26:42 -0700315 command.TEST_RESULT = None
Simon Glass57454f42016-11-25 20:15:52 -0700316
317 def tearDown(self):
318 """Remove the temporary output directory"""
Simon Glassee9d10d2019-07-20 12:24:09 -0600319 self._CleanupOutputDir()
Simon Glass57454f42016-11-25 20:15:52 -0700320
Simon Glassb3d6fc72019-07-20 12:24:10 -0600321 def _SetupImageInTmpdir(self):
322 """Set up the output image in a new temporary directory
323
324 This is used when an image has been generated in the output directory,
325 but we want to run binman again. This will create a new output
326 directory and fail to delete the original one.
327
328 This creates a new temporary directory, copies the image to it (with a
329 new name) and removes the old output directory.
330
331 Returns:
332 Tuple:
333 Temporary directory to use
334 New image filename
335 """
Simon Glass80025522022-01-29 14:14:04 -0700336 image_fname = tools.get_output_filename('image.bin')
Simon Glassb3d6fc72019-07-20 12:24:10 -0600337 tmpdir = tempfile.mkdtemp(prefix='binman.')
338 updated_fname = os.path.join(tmpdir, 'image-updated.bin')
Simon Glass80025522022-01-29 14:14:04 -0700339 tools.write_file(updated_fname, tools.read_file(image_fname))
Simon Glassb3d6fc72019-07-20 12:24:10 -0600340 self._CleanupOutputDir()
341 return tmpdir, updated_fname
342
Simon Glass8425a1f2018-07-17 13:25:48 -0600343 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600344 def _ResetDtbs(cls):
Simon Glass8425a1f2018-07-17 13:25:48 -0600345 TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA)
346 TestFunctional._MakeInputFile('spl/u-boot-spl.dtb', U_BOOT_SPL_DTB_DATA)
347 TestFunctional._MakeInputFile('tpl/u-boot-tpl.dtb', U_BOOT_TPL_DTB_DATA)
Simon Glass56d05412022-02-28 07:16:54 -0700348 TestFunctional._MakeInputFile('vpl/u-boot-vpl.dtb', U_BOOT_VPL_DTB_DATA)
Simon Glass8425a1f2018-07-17 13:25:48 -0600349
Simon Glass57454f42016-11-25 20:15:52 -0700350 def _RunBinman(self, *args, **kwargs):
351 """Run binman using the command line
352
353 Args:
354 Arguments to pass, as a list of strings
355 kwargs: Arguments to pass to Command.RunPipe()
356 """
Simon Glass51f55182025-02-03 09:26:45 -0700357 all_args = [self._binman_pathname] + list(args)
358 result = command.run_one(*all_args, capture=True, capture_stderr=True,
359 raise_on_error=False)
Simon Glass57454f42016-11-25 20:15:52 -0700360 if result.return_code and kwargs.get('raise_on_error', True):
361 raise Exception("Error running '%s': %s" % (' '.join(args),
362 result.stdout + result.stderr))
363 return result
364
Simon Glassf46732a2019-07-08 14:25:29 -0600365 def _DoBinman(self, *argv):
Simon Glass57454f42016-11-25 20:15:52 -0700366 """Run binman using directly (in the same process)
367
368 Args:
369 Arguments to pass, as a list of strings
370 Returns:
371 Return value (0 for success)
372 """
Simon Glassf46732a2019-07-08 14:25:29 -0600373 argv = list(argv)
374 args = cmdline.ParseArgs(argv)
375 args.pager = 'binman-invalid-pager'
376 args.build_dir = self._indir
Simon Glass57454f42016-11-25 20:15:52 -0700377
378 # For testing, you can force an increase in verbosity here
Simon Glassf46732a2019-07-08 14:25:29 -0600379 # args.verbosity = tout.DEBUG
380 return control.Binman(args)
Simon Glass57454f42016-11-25 20:15:52 -0700381
Simon Glass91710b32018-07-17 13:25:32 -0600382 def _DoTestFile(self, fname, debug=False, map=False, update_dtb=False,
Simon Glassb4595d82019-04-25 21:58:34 -0600383 entry_args=None, images=None, use_real_dtb=False,
Simon Glassed930672021-03-18 20:25:05 +1300384 use_expanded=False, verbosity=None, allow_missing=False,
Heiko Thiery6d451362022-01-06 11:49:41 +0100385 allow_fake_blobs=False, extra_indirs=None, threads=None,
Simon Glass66152ce2022-01-09 20:14:09 -0700386 test_section_timeout=False, update_fdt_in_elf=None,
Andrew Davis6b463da2023-07-22 00:14:44 +0530387 force_missing_bintools='', ignore_missing=False, output_dir=None):
Simon Glass57454f42016-11-25 20:15:52 -0700388 """Run binman with a given test file
389
390 Args:
Simon Glass511f6582018-10-01 12:22:30 -0600391 fname: Device-tree source filename to use (e.g. 005_simple.dts)
Simon Glass1e324002018-06-01 09:38:19 -0600392 debug: True to enable debugging output
Simon Glass30732662018-06-01 09:38:20 -0600393 map: True to output map files for the images
Simon Glasse8561af2018-08-01 15:22:37 -0600394 update_dtb: Update the offset and size of each entry in the device
Simon Glassa87014e2018-07-06 10:27:42 -0600395 tree before packing it into the image
Simon Glass3b376c32018-09-14 04:57:12 -0600396 entry_args: Dict of entry args to supply to binman
397 key: arg name
398 value: value of that arg
399 images: List of image names to build
Simon Glass31ee50f2020-09-01 05:13:55 -0600400 use_real_dtb: True to use the test file as the contents of
401 the u-boot-dtb entry. Normally this is not needed and the
402 test contents (the U_BOOT_DTB_DATA string) can be used.
403 But in some test we need the real contents.
Simon Glassed930672021-03-18 20:25:05 +1300404 use_expanded: True to use expanded entries where available, e.g.
405 'u-boot-expanded' instead of 'u-boot'
Simon Glass31ee50f2020-09-01 05:13:55 -0600406 verbosity: Verbosity level to use (0-3, None=don't set it)
407 allow_missing: Set the '--allow-missing' flag so that missing
408 external binaries just produce a warning instead of an error
Heiko Thiery6d451362022-01-06 11:49:41 +0100409 allow_fake_blobs: Set the '--fake-ext-blobs' flag
Simon Glassa435cd12020-09-01 05:13:59 -0600410 extra_indirs: Extra input directories to add using -I
Simon Glass76f496d2021-07-06 10:36:37 -0600411 threads: Number of threads to use (None for default, 0 for
412 single-threaded)
Simon Glass9a798402021-11-03 21:09:17 -0600413 test_section_timeout: True to force the first time to timeout, as
414 used in testThreadTimeout()
Simon Glassadfb8492021-11-03 21:09:18 -0600415 update_fdt_in_elf: Value to pass with --update-fdt-in-elf=xxx
Simon Glassb553e8a2024-08-26 13:11:29 -0600416 force_missing_bintools (str): comma-separated list of bintools to
Simon Glass66152ce2022-01-09 20:14:09 -0700417 regard as missing
Simon Glassb553e8a2024-08-26 13:11:29 -0600418 ignore_missing (bool): True to return success even if there are
419 missing blobs or bintools
Andrew Davis6b463da2023-07-22 00:14:44 +0530420 output_dir: Specific output directory to use for image using -O
Simon Glass9a798402021-11-03 21:09:17 -0600421
422 Returns:
423 int return code, 0 on success
Simon Glass57454f42016-11-25 20:15:52 -0700424 """
Simon Glassf46732a2019-07-08 14:25:29 -0600425 args = []
Simon Glass075a45c2017-11-13 18:55:00 -0700426 if debug:
427 args.append('-D')
Simon Glassf46732a2019-07-08 14:25:29 -0600428 if verbosity is not None:
429 args.append('-v%d' % verbosity)
430 elif self.verbosity:
431 args.append('-v%d' % self.verbosity)
432 if self.toolpath:
433 for path in self.toolpath:
434 args += ['--toolpath', path]
Simon Glass76f496d2021-07-06 10:36:37 -0600435 if threads is not None:
436 args.append('-T%d' % threads)
437 if test_section_timeout:
438 args.append('--test-section-timeout')
Simon Glassf46732a2019-07-08 14:25:29 -0600439 args += ['build', '-p', '-I', self._indir, '-d', self.TestFile(fname)]
Simon Glass30732662018-06-01 09:38:20 -0600440 if map:
441 args.append('-m')
Simon Glassa87014e2018-07-06 10:27:42 -0600442 if update_dtb:
Simon Glass38a411c2019-07-08 13:18:47 -0600443 args.append('-u')
Simon Glass31402012018-09-14 04:57:23 -0600444 if not use_real_dtb:
445 args.append('--fake-dtb')
Simon Glassed930672021-03-18 20:25:05 +1300446 if not use_expanded:
447 args.append('--no-expanded')
Simon Glass91710b32018-07-17 13:25:32 -0600448 if entry_args:
Simon Glass5f3645b2019-05-14 15:53:41 -0600449 for arg, value in entry_args.items():
Simon Glass91710b32018-07-17 13:25:32 -0600450 args.append('-a%s=%s' % (arg, value))
Simon Glass5d94cc62020-07-09 18:39:38 -0600451 if allow_missing:
452 args.append('-M')
Simon Glass6bce5dc2022-11-09 19:14:42 -0700453 if ignore_missing:
454 args.append('-W')
Heiko Thiery6d451362022-01-06 11:49:41 +0100455 if allow_fake_blobs:
456 args.append('--fake-ext-blobs')
Simon Glass66152ce2022-01-09 20:14:09 -0700457 if force_missing_bintools:
458 args += ['--force-missing-bintools', force_missing_bintools]
Simon Glassadfb8492021-11-03 21:09:18 -0600459 if update_fdt_in_elf:
460 args += ['--update-fdt-in-elf', update_fdt_in_elf]
Simon Glass3b376c32018-09-14 04:57:12 -0600461 if images:
462 for image in images:
463 args += ['-i', image]
Simon Glassa435cd12020-09-01 05:13:59 -0600464 if extra_indirs:
465 for indir in extra_indirs:
466 args += ['-I', indir]
Andrew Davis6b463da2023-07-22 00:14:44 +0530467 if output_dir:
468 args += ['-O', output_dir]
Simon Glass075a45c2017-11-13 18:55:00 -0700469 return self._DoBinman(*args)
Simon Glass57454f42016-11-25 20:15:52 -0700470
471 def _SetupDtb(self, fname, outfile='u-boot.dtb'):
Simon Glass72232452016-11-25 20:15:53 -0700472 """Set up a new test device-tree file
473
474 The given file is compiled and set up as the device tree to be used
475 for ths test.
476
477 Args:
478 fname: Filename of .dts file to read
Simon Glass1e324002018-06-01 09:38:19 -0600479 outfile: Output filename for compiled device-tree binary
Simon Glass72232452016-11-25 20:15:53 -0700480
481 Returns:
Simon Glass1e324002018-06-01 09:38:19 -0600482 Contents of device-tree binary
Simon Glass72232452016-11-25 20:15:53 -0700483 """
Simon Glassb8d2daa2019-07-20 12:23:49 -0600484 tmpdir = tempfile.mkdtemp(prefix='binmant.')
485 dtb = fdt_util.EnsureCompiled(self.TestFile(fname), tmpdir)
Simon Glass33486662019-05-14 15:53:42 -0600486 with open(dtb, 'rb') as fd:
Simon Glass57454f42016-11-25 20:15:52 -0700487 data = fd.read()
488 TestFunctional._MakeInputFile(outfile, data)
Simon Glassb8d2daa2019-07-20 12:23:49 -0600489 shutil.rmtree(tmpdir)
Simon Glass752e7552018-10-01 21:12:41 -0600490 return data
Simon Glass57454f42016-11-25 20:15:52 -0700491
Simon Glass56d05412022-02-28 07:16:54 -0700492 def _GetDtbContentsForSpls(self, dtb_data, name):
493 """Create a version of the main DTB for SPL / TPL / VPL
Simon Glasse219aa42018-09-14 04:57:24 -0600494
495 For testing we don't actually have different versions of the DTB. With
496 U-Boot we normally run fdtgrep to remove unwanted nodes, but for tests
497 we don't normally have any unwanted nodes.
498
499 We still want the DTBs for SPL and TPL to be different though, since
500 otherwise it is confusing to know which one we are looking at. So add
501 an 'spl' or 'tpl' property to the top-level node.
Simon Glass31ee50f2020-09-01 05:13:55 -0600502
503 Args:
504 dtb_data: dtb data to modify (this should be a value devicetree)
505 name: Name of a new property to add
506
507 Returns:
508 New dtb data with the property added
Simon Glasse219aa42018-09-14 04:57:24 -0600509 """
510 dtb = fdt.Fdt.FromData(dtb_data)
511 dtb.Scan()
512 dtb.GetNode('/binman').AddZeroProp(name)
513 dtb.Sync(auto_resize=True)
514 dtb.Pack()
515 return dtb.GetContents()
516
Simon Glassed930672021-03-18 20:25:05 +1300517 def _DoReadFileDtb(self, fname, use_real_dtb=False, use_expanded=False,
Simon Glass3eb30a42024-08-26 13:11:42 -0600518 verbosity=None, map=False, update_dtb=False,
519 entry_args=None, reset_dtbs=True, extra_indirs=None,
520 threads=None):
Simon Glass57454f42016-11-25 20:15:52 -0700521 """Run binman and return the resulting image
522
523 This runs binman with a given test file and then reads the resulting
524 output file. It is a shortcut function since most tests need to do
525 these steps.
526
527 Raises an assertion failure if binman returns a non-zero exit code.
528
529 Args:
Simon Glass511f6582018-10-01 12:22:30 -0600530 fname: Device-tree source filename to use (e.g. 005_simple.dts)
Simon Glass57454f42016-11-25 20:15:52 -0700531 use_real_dtb: True to use the test file as the contents of
532 the u-boot-dtb entry. Normally this is not needed and the
533 test contents (the U_BOOT_DTB_DATA string) can be used.
534 But in some test we need the real contents.
Simon Glassed930672021-03-18 20:25:05 +1300535 use_expanded: True to use expanded entries where available, e.g.
536 'u-boot-expanded' instead of 'u-boot'
Simon Glass3eb30a42024-08-26 13:11:42 -0600537 verbosity: Verbosity level to use (0-3, None=don't set it)
Simon Glass30732662018-06-01 09:38:20 -0600538 map: True to output map files for the images
Simon Glasse8561af2018-08-01 15:22:37 -0600539 update_dtb: Update the offset and size of each entry in the device
Simon Glassa87014e2018-07-06 10:27:42 -0600540 tree before packing it into the image
Simon Glass31ee50f2020-09-01 05:13:55 -0600541 entry_args: Dict of entry args to supply to binman
542 key: arg name
543 value: value of that arg
544 reset_dtbs: With use_real_dtb the test dtb is overwritten by this
545 function. If reset_dtbs is True, then the original test dtb
546 is written back before this function finishes
Simon Glassa435cd12020-09-01 05:13:59 -0600547 extra_indirs: Extra input directories to add using -I
Simon Glass76f496d2021-07-06 10:36:37 -0600548 threads: Number of threads to use (None for default, 0 for
549 single-threaded)
Simon Glass72232452016-11-25 20:15:53 -0700550
551 Returns:
552 Tuple:
553 Resulting image contents
554 Device tree contents
Simon Glass30732662018-06-01 09:38:20 -0600555 Map data showing contents of image (or None if none)
Simon Glassdef77b52018-07-17 13:25:27 -0600556 Output device tree binary filename ('u-boot.dtb' path)
Simon Glass57454f42016-11-25 20:15:52 -0700557 """
Simon Glass72232452016-11-25 20:15:53 -0700558 dtb_data = None
Simon Glass57454f42016-11-25 20:15:52 -0700559 # Use the compiled test file as the u-boot-dtb input
560 if use_real_dtb:
Simon Glass72232452016-11-25 20:15:53 -0700561 dtb_data = self._SetupDtb(fname)
Simon Glasse219aa42018-09-14 04:57:24 -0600562
563 # For testing purposes, make a copy of the DT for SPL and TPL. Add
Simon Glassd9e01d22024-07-20 11:49:40 +0100564 # a node indicating which it is, to aid verification.
Simon Glass56d05412022-02-28 07:16:54 -0700565 for name in ['spl', 'tpl', 'vpl']:
Simon Glasse219aa42018-09-14 04:57:24 -0600566 dtb_fname = '%s/u-boot-%s.dtb' % (name, name)
567 outfile = os.path.join(self._indir, dtb_fname)
568 TestFunctional._MakeInputFile(dtb_fname,
Simon Glass56d05412022-02-28 07:16:54 -0700569 self._GetDtbContentsForSpls(dtb_data, name))
Simon Glass57454f42016-11-25 20:15:52 -0700570
571 try:
Simon Glass91710b32018-07-17 13:25:32 -0600572 retcode = self._DoTestFile(fname, map=map, update_dtb=update_dtb,
Simon Glassa435cd12020-09-01 05:13:59 -0600573 entry_args=entry_args, use_real_dtb=use_real_dtb,
Simon Glass3eb30a42024-08-26 13:11:42 -0600574 use_expanded=use_expanded, verbosity=verbosity,
575 extra_indirs=extra_indirs,
Simon Glass76f496d2021-07-06 10:36:37 -0600576 threads=threads)
Simon Glass57454f42016-11-25 20:15:52 -0700577 self.assertEqual(0, retcode)
Simon Glass80025522022-01-29 14:14:04 -0700578 out_dtb_fname = tools.get_output_filename('u-boot.dtb.out')
Simon Glass57454f42016-11-25 20:15:52 -0700579
580 # Find the (only) image, read it and return its contents
581 image = control.images['image']
Simon Glass80025522022-01-29 14:14:04 -0700582 image_fname = tools.get_output_filename('image.bin')
Simon Glassa87014e2018-07-06 10:27:42 -0600583 self.assertTrue(os.path.exists(image_fname))
Simon Glass30732662018-06-01 09:38:20 -0600584 if map:
Simon Glass80025522022-01-29 14:14:04 -0700585 map_fname = tools.get_output_filename('image.map')
Simon Glass30732662018-06-01 09:38:20 -0600586 with open(map_fname) as fd:
587 map_data = fd.read()
588 else:
589 map_data = None
Simon Glass33486662019-05-14 15:53:42 -0600590 with open(image_fname, 'rb') as fd:
Simon Glassa87014e2018-07-06 10:27:42 -0600591 return fd.read(), dtb_data, map_data, out_dtb_fname
Simon Glass57454f42016-11-25 20:15:52 -0700592 finally:
593 # Put the test file back
Simon Glasse219aa42018-09-14 04:57:24 -0600594 if reset_dtbs and use_real_dtb:
Simon Glass8425a1f2018-07-17 13:25:48 -0600595 self._ResetDtbs()
Simon Glass57454f42016-11-25 20:15:52 -0700596
Simon Glass5b4bce32019-07-08 14:25:26 -0600597 def _DoReadFileRealDtb(self, fname):
598 """Run binman with a real .dtb file and return the resulting data
599
600 Args:
601 fname: DT source filename to use (e.g. 082_fdt_update_all.dts)
602
603 Returns:
604 Resulting image contents
605 """
606 return self._DoReadFileDtb(fname, use_real_dtb=True, update_dtb=True)[0]
607
Simon Glass72232452016-11-25 20:15:53 -0700608 def _DoReadFile(self, fname, use_real_dtb=False):
Simon Glass1e324002018-06-01 09:38:19 -0600609 """Helper function which discards the device-tree binary
610
611 Args:
Simon Glass511f6582018-10-01 12:22:30 -0600612 fname: Device-tree source filename to use (e.g. 005_simple.dts)
Simon Glass1e324002018-06-01 09:38:19 -0600613 use_real_dtb: True to use the test file as the contents of
614 the u-boot-dtb entry. Normally this is not needed and the
615 test contents (the U_BOOT_DTB_DATA string) can be used.
616 But in some test we need the real contents.
Simon Glassdef77b52018-07-17 13:25:27 -0600617
618 Returns:
619 Resulting image contents
Simon Glass1e324002018-06-01 09:38:19 -0600620 """
Simon Glass72232452016-11-25 20:15:53 -0700621 return self._DoReadFileDtb(fname, use_real_dtb)[0]
622
Simon Glass57454f42016-11-25 20:15:52 -0700623 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600624 def _MakeInputFile(cls, fname, contents):
Simon Glass57454f42016-11-25 20:15:52 -0700625 """Create a new test input file, creating directories as needed
626
627 Args:
Simon Glasse8561af2018-08-01 15:22:37 -0600628 fname: Filename to create
Simon Glass57454f42016-11-25 20:15:52 -0700629 contents: File contents to write in to the file
630 Returns:
631 Full pathname of file created
632 """
Simon Glass862f8e22019-08-24 07:22:43 -0600633 pathname = os.path.join(cls._indir, fname)
Simon Glass57454f42016-11-25 20:15:52 -0700634 dirname = os.path.dirname(pathname)
635 if dirname and not os.path.exists(dirname):
636 os.makedirs(dirname)
637 with open(pathname, 'wb') as fd:
638 fd.write(contents)
639 return pathname
640
641 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600642 def _MakeInputDir(cls, dirname):
Simon Glassc1ae83c2018-07-17 13:25:44 -0600643 """Create a new test input directory, creating directories as needed
644
645 Args:
646 dirname: Directory name to create
647
648 Returns:
649 Full pathname of directory created
650 """
Simon Glass862f8e22019-08-24 07:22:43 -0600651 pathname = os.path.join(cls._indir, dirname)
Simon Glassc1ae83c2018-07-17 13:25:44 -0600652 if not os.path.exists(pathname):
653 os.makedirs(pathname)
654 return pathname
655
656 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600657 def _SetupSplElf(cls, src_fname='bss_data'):
Simon Glass7057d022018-10-01 21:12:47 -0600658 """Set up an ELF file with a '_dt_ucode_base_size' symbol
659
660 Args:
661 Filename of ELF file to use as SPL
662 """
Simon Glass93a806f2019-08-24 07:22:59 -0600663 TestFunctional._MakeInputFile('spl/u-boot-spl',
Simon Glass80025522022-01-29 14:14:04 -0700664 tools.read_file(cls.ElfTestFile(src_fname)))
Simon Glass7057d022018-10-01 21:12:47 -0600665
666 @classmethod
Simon Glass3eb5b202019-08-24 07:23:00 -0600667 def _SetupTplElf(cls, src_fname='bss_data'):
668 """Set up an ELF file with a '_dt_ucode_base_size' symbol
669
670 Args:
671 Filename of ELF file to use as TPL
672 """
673 TestFunctional._MakeInputFile('tpl/u-boot-tpl',
Simon Glass80025522022-01-29 14:14:04 -0700674 tools.read_file(cls.ElfTestFile(src_fname)))
Simon Glass3eb5b202019-08-24 07:23:00 -0600675
676 @classmethod
Simon Glass56d05412022-02-28 07:16:54 -0700677 def _SetupVplElf(cls, src_fname='bss_data'):
678 """Set up an ELF file with a '_dt_ucode_base_size' symbol
679
680 Args:
681 Filename of ELF file to use as VPL
682 """
683 TestFunctional._MakeInputFile('vpl/u-boot-vpl',
684 tools.read_file(cls.ElfTestFile(src_fname)))
685
686 @classmethod
Lukas Funkee901faf2023-07-18 13:53:13 +0200687 def _SetupPmuFwlElf(cls, src_fname='bss_data'):
688 """Set up an ELF file with a '_dt_ucode_base_size' symbol
689
690 Args:
691 Filename of ELF file to use as VPL
692 """
693 TestFunctional._MakeInputFile('pmu-firmware.elf',
694 tools.read_file(cls.ElfTestFile(src_fname)))
695
696 @classmethod
Simon Glasse88cef92020-07-09 18:39:41 -0600697 def _SetupDescriptor(cls):
698 with open(cls.TestFile('descriptor.bin'), 'rb') as fd:
699 TestFunctional._MakeInputFile('descriptor.bin', fd.read())
700
701 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600702 def TestFile(cls, fname):
703 return os.path.join(cls._binman_dir, 'test', fname)
Simon Glass57454f42016-11-25 20:15:52 -0700704
Simon Glassf6290892019-08-24 07:22:53 -0600705 @classmethod
706 def ElfTestFile(cls, fname):
707 return os.path.join(cls._elf_testdir, fname)
708
Simon Glassad5cfe12023-01-07 14:07:14 -0700709 @classmethod
710 def make_tee_bin(cls, fname, paged_sz=0, extra_data=b''):
711 init_sz, start_hi, start_lo, dummy = (len(U_BOOT_DATA), 0, TEE_ADDR, 0)
712 data = b'OPTE\x01xxx' + struct.pack('<5I', init_sz, start_hi, start_lo,
713 dummy, paged_sz) + U_BOOT_DATA
714 data += extra_data
715 TestFunctional._MakeInputFile(fname, data)
716
Simon Glass57454f42016-11-25 20:15:52 -0700717 def AssertInList(self, grep_list, target):
718 """Assert that at least one of a list of things is in a target
719
720 Args:
721 grep_list: List of strings to check
722 target: Target string
723 """
724 for grep in grep_list:
725 if grep in target:
726 return
Simon Glass848cdb52019-05-17 22:00:50 -0600727 self.fail("Error: '%s' not found in '%s'" % (grep_list, target))
Simon Glass57454f42016-11-25 20:15:52 -0700728
729 def CheckNoGaps(self, entries):
730 """Check that all entries fit together without gaps
731
732 Args:
733 entries: List of entries to check
734 """
Simon Glasse8561af2018-08-01 15:22:37 -0600735 offset = 0
Simon Glass57454f42016-11-25 20:15:52 -0700736 for entry in entries.values():
Simon Glasse8561af2018-08-01 15:22:37 -0600737 self.assertEqual(offset, entry.offset)
738 offset += entry.size
Simon Glass57454f42016-11-25 20:15:52 -0700739
Simon Glass72232452016-11-25 20:15:53 -0700740 def GetFdtLen(self, dtb):
Simon Glass1e324002018-06-01 09:38:19 -0600741 """Get the totalsize field from a device-tree binary
Simon Glass72232452016-11-25 20:15:53 -0700742
743 Args:
Simon Glass1e324002018-06-01 09:38:19 -0600744 dtb: Device-tree binary contents
Simon Glass72232452016-11-25 20:15:53 -0700745
746 Returns:
Simon Glass1e324002018-06-01 09:38:19 -0600747 Total size of device-tree binary, from the header
Simon Glass72232452016-11-25 20:15:53 -0700748 """
749 return struct.unpack('>L', dtb[4:8])[0]
750
Simon Glass0f621332019-07-08 14:25:27 -0600751 def _GetPropTree(self, dtb, prop_names, prefix='/binman/'):
Simon Glassa87014e2018-07-06 10:27:42 -0600752 def AddNode(node, path):
753 if node.name != '/':
754 path += '/' + node.name
Simon Glass0f621332019-07-08 14:25:27 -0600755 for prop in node.props.values():
756 if prop.name in prop_names:
757 prop_path = path + ':' + prop.name
758 tree[prop_path[len(prefix):]] = fdt_util.fdt32_to_cpu(
759 prop.value)
Simon Glassa87014e2018-07-06 10:27:42 -0600760 for subnode in node.subnodes:
Simon Glassa87014e2018-07-06 10:27:42 -0600761 AddNode(subnode, path)
762
763 tree = {}
Simon Glassa87014e2018-07-06 10:27:42 -0600764 AddNode(dtb.GetRoot(), '')
765 return tree
766
Ivan Mikhaylov3cfcaa4d2023-03-08 01:13:40 +0000767 def _CheckSign(self, fit, key):
768 try:
769 tools.run('fit_check_sign', '-k', key, '-f', fit)
770 except:
771 self.fail('Expected signed FIT container')
772 return False
773 return True
774
Paul HENRYS5cf82892025-02-24 22:20:55 +0100775 def _CheckPreload(self, image, key, algo="sha256,rsa2048",
776 padding="pkcs-1.5"):
777 try:
778 tools.run('preload_check_sign', '-k', key, '-a', algo, '-p',
779 padding, '-f', image)
780 except:
781 self.fail('Expected image signed with a pre-load')
782 return False
783 return True
784
Simon Glass57454f42016-11-25 20:15:52 -0700785 def testRun(self):
786 """Test a basic run with valid args"""
787 result = self._RunBinman('-h')
788
789 def testFullHelp(self):
790 """Test that the full help is displayed with -H"""
791 result = self._RunBinman('-H')
Simon Glass75ead662021-03-18 20:25:13 +1300792 help_file = os.path.join(self._binman_dir, 'README.rst')
Tom Rinic3c0b6d2018-01-16 15:29:50 -0500793 # Remove possible extraneous strings
794 extra = '::::::::::::::\n' + help_file + '\n::::::::::::::\n'
795 gothelp = result.stdout.replace(extra, '')
796 self.assertEqual(len(gothelp), os.path.getsize(help_file))
Simon Glass57454f42016-11-25 20:15:52 -0700797 self.assertEqual(0, len(result.stderr))
798 self.assertEqual(0, result.return_code)
799
800 def testFullHelpInternal(self):
801 """Test that the full help is displayed with -H"""
802 try:
Simon Glass5dc22cf2025-02-03 09:26:42 -0700803 command.TEST_RESULT = command.CommandResult()
Simon Glass57454f42016-11-25 20:15:52 -0700804 result = self._DoBinman('-H')
Simon Glass75ead662021-03-18 20:25:13 +1300805 help_file = os.path.join(self._binman_dir, 'README.rst')
Simon Glass57454f42016-11-25 20:15:52 -0700806 finally:
Simon Glass5dc22cf2025-02-03 09:26:42 -0700807 command.TEST_RESULT = None
Simon Glass57454f42016-11-25 20:15:52 -0700808
809 def testHelp(self):
810 """Test that the basic help is displayed with -h"""
811 result = self._RunBinman('-h')
812 self.assertTrue(len(result.stdout) > 200)
813 self.assertEqual(0, len(result.stderr))
814 self.assertEqual(0, result.return_code)
815
Simon Glass57454f42016-11-25 20:15:52 -0700816 def testBoard(self):
817 """Test that we can run it with a specific board"""
Simon Glass511f6582018-10-01 12:22:30 -0600818 self._SetupDtb('005_simple.dts', 'sandbox/u-boot.dtb')
Simon Glass57454f42016-11-25 20:15:52 -0700819 TestFunctional._MakeInputFile('sandbox/u-boot.bin', U_BOOT_DATA)
Simon Glassed930672021-03-18 20:25:05 +1300820 result = self._DoBinman('build', '-n', '-b', 'sandbox')
Simon Glass57454f42016-11-25 20:15:52 -0700821 self.assertEqual(0, result)
822
823 def testNeedBoard(self):
824 """Test that we get an error when no board ius supplied"""
825 with self.assertRaises(ValueError) as e:
Simon Glassf46732a2019-07-08 14:25:29 -0600826 result = self._DoBinman('build')
Simon Glass57454f42016-11-25 20:15:52 -0700827 self.assertIn("Must provide a board to process (use -b <board>)",
828 str(e.exception))
829
830 def testMissingDt(self):
Simon Glass1e324002018-06-01 09:38:19 -0600831 """Test that an invalid device-tree file generates an error"""
Simon Glass57454f42016-11-25 20:15:52 -0700832 with self.assertRaises(Exception) as e:
Simon Glassf46732a2019-07-08 14:25:29 -0600833 self._RunBinman('build', '-d', 'missing_file')
Simon Glass57454f42016-11-25 20:15:52 -0700834 # We get one error from libfdt, and a different one from fdtget.
835 self.AssertInList(["Couldn't open blob from 'missing_file'",
836 'No such file or directory'], str(e.exception))
837
838 def testBrokenDt(self):
Simon Glass1e324002018-06-01 09:38:19 -0600839 """Test that an invalid device-tree source file generates an error
Simon Glass57454f42016-11-25 20:15:52 -0700840
841 Since this is a source file it should be compiled and the error
842 will come from the device-tree compiler (dtc).
843 """
844 with self.assertRaises(Exception) as e:
Simon Glassf46732a2019-07-08 14:25:29 -0600845 self._RunBinman('build', '-d', self.TestFile('001_invalid.dts'))
Simon Glass57454f42016-11-25 20:15:52 -0700846 self.assertIn("FATAL ERROR: Unable to parse input tree",
847 str(e.exception))
848
849 def testMissingNode(self):
850 """Test that a device tree without a 'binman' node generates an error"""
851 with self.assertRaises(Exception) as e:
Simon Glassf46732a2019-07-08 14:25:29 -0600852 self._DoBinman('build', '-d', self.TestFile('002_missing_node.dts'))
Simon Glass57454f42016-11-25 20:15:52 -0700853 self.assertIn("does not have a 'binman' node", str(e.exception))
854
855 def testEmpty(self):
856 """Test that an empty binman node works OK (i.e. does nothing)"""
Simon Glassf46732a2019-07-08 14:25:29 -0600857 result = self._RunBinman('build', '-d', self.TestFile('003_empty.dts'))
Simon Glass57454f42016-11-25 20:15:52 -0700858 self.assertEqual(0, len(result.stderr))
859 self.assertEqual(0, result.return_code)
860
861 def testInvalidEntry(self):
862 """Test that an invalid entry is flagged"""
863 with self.assertRaises(Exception) as e:
Simon Glassf46732a2019-07-08 14:25:29 -0600864 result = self._RunBinman('build', '-d',
Simon Glass511f6582018-10-01 12:22:30 -0600865 self.TestFile('004_invalid_entry.dts'))
Simon Glass57454f42016-11-25 20:15:52 -0700866 self.assertIn("Unknown entry type 'not-a-valid-type' in node "
867 "'/binman/not-a-valid-type'", str(e.exception))
868
869 def testSimple(self):
870 """Test a simple binman with a single file"""
Simon Glass511f6582018-10-01 12:22:30 -0600871 data = self._DoReadFile('005_simple.dts')
Simon Glass57454f42016-11-25 20:15:52 -0700872 self.assertEqual(U_BOOT_DATA, data)
873
Simon Glass075a45c2017-11-13 18:55:00 -0700874 def testSimpleDebug(self):
875 """Test a simple binman run with debugging enabled"""
Simon Glass52d06212019-07-08 14:25:53 -0600876 self._DoTestFile('005_simple.dts', debug=True)
Simon Glass075a45c2017-11-13 18:55:00 -0700877
Simon Glass57454f42016-11-25 20:15:52 -0700878 def testDual(self):
879 """Test that we can handle creating two images
880
881 This also tests image padding.
882 """
Simon Glass511f6582018-10-01 12:22:30 -0600883 retcode = self._DoTestFile('006_dual_image.dts')
Simon Glass57454f42016-11-25 20:15:52 -0700884 self.assertEqual(0, retcode)
885
886 image = control.images['image1']
Simon Glass39dd2152019-07-08 14:25:47 -0600887 self.assertEqual(len(U_BOOT_DATA), image.size)
Simon Glass80025522022-01-29 14:14:04 -0700888 fname = tools.get_output_filename('image1.bin')
Simon Glass57454f42016-11-25 20:15:52 -0700889 self.assertTrue(os.path.exists(fname))
Simon Glass33486662019-05-14 15:53:42 -0600890 with open(fname, 'rb') as fd:
Simon Glass57454f42016-11-25 20:15:52 -0700891 data = fd.read()
892 self.assertEqual(U_BOOT_DATA, data)
893
894 image = control.images['image2']
Simon Glass39dd2152019-07-08 14:25:47 -0600895 self.assertEqual(3 + len(U_BOOT_DATA) + 5, image.size)
Simon Glass80025522022-01-29 14:14:04 -0700896 fname = tools.get_output_filename('image2.bin')
Simon Glass57454f42016-11-25 20:15:52 -0700897 self.assertTrue(os.path.exists(fname))
Simon Glass33486662019-05-14 15:53:42 -0600898 with open(fname, 'rb') as fd:
Simon Glass57454f42016-11-25 20:15:52 -0700899 data = fd.read()
900 self.assertEqual(U_BOOT_DATA, data[3:7])
Simon Glass80025522022-01-29 14:14:04 -0700901 self.assertEqual(tools.get_bytes(0, 3), data[:3])
902 self.assertEqual(tools.get_bytes(0, 5), data[7:])
Simon Glass57454f42016-11-25 20:15:52 -0700903
904 def testBadAlign(self):
905 """Test that an invalid alignment value is detected"""
906 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -0600907 self._DoTestFile('007_bad_align.dts')
Simon Glass57454f42016-11-25 20:15:52 -0700908 self.assertIn("Node '/binman/u-boot': Alignment 23 must be a power "
909 "of two", str(e.exception))
910
911 def testPackSimple(self):
912 """Test that packing works as expected"""
Simon Glass511f6582018-10-01 12:22:30 -0600913 retcode = self._DoTestFile('008_pack.dts')
Simon Glass57454f42016-11-25 20:15:52 -0700914 self.assertEqual(0, retcode)
915 self.assertIn('image', control.images)
916 image = control.images['image']
Simon Glasseca32212018-06-01 09:38:12 -0600917 entries = image.GetEntries()
Simon Glass57454f42016-11-25 20:15:52 -0700918 self.assertEqual(5, len(entries))
919
920 # First u-boot
921 self.assertIn('u-boot', entries)
922 entry = entries['u-boot']
Simon Glasse8561af2018-08-01 15:22:37 -0600923 self.assertEqual(0, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700924 self.assertEqual(len(U_BOOT_DATA), entry.size)
925
926 # Second u-boot, aligned to 16-byte boundary
927 self.assertIn('u-boot-align', entries)
928 entry = entries['u-boot-align']
Simon Glasse8561af2018-08-01 15:22:37 -0600929 self.assertEqual(16, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700930 self.assertEqual(len(U_BOOT_DATA), entry.size)
931
932 # Third u-boot, size 23 bytes
933 self.assertIn('u-boot-size', entries)
934 entry = entries['u-boot-size']
Simon Glasse8561af2018-08-01 15:22:37 -0600935 self.assertEqual(20, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700936 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
937 self.assertEqual(23, entry.size)
938
939 # Fourth u-boot, placed immediate after the above
940 self.assertIn('u-boot-next', entries)
941 entry = entries['u-boot-next']
Simon Glasse8561af2018-08-01 15:22:37 -0600942 self.assertEqual(43, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700943 self.assertEqual(len(U_BOOT_DATA), entry.size)
944
Simon Glasse8561af2018-08-01 15:22:37 -0600945 # Fifth u-boot, placed at a fixed offset
Simon Glass57454f42016-11-25 20:15:52 -0700946 self.assertIn('u-boot-fixed', entries)
947 entry = entries['u-boot-fixed']
Simon Glasse8561af2018-08-01 15:22:37 -0600948 self.assertEqual(61, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700949 self.assertEqual(len(U_BOOT_DATA), entry.size)
950
Simon Glass39dd2152019-07-08 14:25:47 -0600951 self.assertEqual(65, image.size)
Simon Glass57454f42016-11-25 20:15:52 -0700952
953 def testPackExtra(self):
954 """Test that extra packing feature works as expected"""
Simon Glassafb9caa2020-10-26 17:40:10 -0600955 data, _, _, out_dtb_fname = self._DoReadFileDtb('009_pack_extra.dts',
956 update_dtb=True)
Simon Glass57454f42016-11-25 20:15:52 -0700957
Simon Glass57454f42016-11-25 20:15:52 -0700958 self.assertIn('image', control.images)
959 image = control.images['image']
Simon Glasseca32212018-06-01 09:38:12 -0600960 entries = image.GetEntries()
Samuel Hollande2574022023-01-21 17:25:16 -0600961 self.assertEqual(6, len(entries))
Simon Glass57454f42016-11-25 20:15:52 -0700962
Samuel Hollande2574022023-01-21 17:25:16 -0600963 # First u-boot with padding before and after (included in minimum size)
Simon Glass57454f42016-11-25 20:15:52 -0700964 self.assertIn('u-boot', entries)
965 entry = entries['u-boot']
Simon Glasse8561af2018-08-01 15:22:37 -0600966 self.assertEqual(0, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700967 self.assertEqual(3, entry.pad_before)
968 self.assertEqual(3 + 5 + len(U_BOOT_DATA), entry.size)
Simon Glass187202f2020-10-26 17:40:08 -0600969 self.assertEqual(U_BOOT_DATA, entry.data)
Simon Glass80025522022-01-29 14:14:04 -0700970 self.assertEqual(tools.get_bytes(0, 3) + U_BOOT_DATA +
971 tools.get_bytes(0, 5), data[:entry.size])
Simon Glass187202f2020-10-26 17:40:08 -0600972 pos = entry.size
Simon Glass57454f42016-11-25 20:15:52 -0700973
974 # Second u-boot has an aligned size, but it has no effect
975 self.assertIn('u-boot-align-size-nop', entries)
976 entry = entries['u-boot-align-size-nop']
Simon Glass187202f2020-10-26 17:40:08 -0600977 self.assertEqual(pos, entry.offset)
978 self.assertEqual(len(U_BOOT_DATA), entry.size)
979 self.assertEqual(U_BOOT_DATA, entry.data)
980 self.assertEqual(U_BOOT_DATA, data[pos:pos + entry.size])
981 pos += entry.size
Simon Glass57454f42016-11-25 20:15:52 -0700982
983 # Third u-boot has an aligned size too
984 self.assertIn('u-boot-align-size', entries)
985 entry = entries['u-boot-align-size']
Simon Glass187202f2020-10-26 17:40:08 -0600986 self.assertEqual(pos, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700987 self.assertEqual(32, entry.size)
Simon Glass187202f2020-10-26 17:40:08 -0600988 self.assertEqual(U_BOOT_DATA, entry.data)
Simon Glass80025522022-01-29 14:14:04 -0700989 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 32 - len(U_BOOT_DATA)),
Simon Glass187202f2020-10-26 17:40:08 -0600990 data[pos:pos + entry.size])
991 pos += entry.size
Simon Glass57454f42016-11-25 20:15:52 -0700992
993 # Fourth u-boot has an aligned end
994 self.assertIn('u-boot-align-end', entries)
995 entry = entries['u-boot-align-end']
Simon Glasse8561af2018-08-01 15:22:37 -0600996 self.assertEqual(48, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700997 self.assertEqual(16, entry.size)
Simon Glass187202f2020-10-26 17:40:08 -0600998 self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)])
Simon Glass80025522022-01-29 14:14:04 -0700999 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 16 - len(U_BOOT_DATA)),
Simon Glass187202f2020-10-26 17:40:08 -06001000 data[pos:pos + entry.size])
1001 pos += entry.size
Simon Glass57454f42016-11-25 20:15:52 -07001002
1003 # Fifth u-boot immediately afterwards
1004 self.assertIn('u-boot-align-both', entries)
1005 entry = entries['u-boot-align-both']
Simon Glasse8561af2018-08-01 15:22:37 -06001006 self.assertEqual(64, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -07001007 self.assertEqual(64, entry.size)
Simon Glass187202f2020-10-26 17:40:08 -06001008 self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)])
Simon Glass80025522022-01-29 14:14:04 -07001009 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 64 - len(U_BOOT_DATA)),
Simon Glass187202f2020-10-26 17:40:08 -06001010 data[pos:pos + entry.size])
Simon Glass57454f42016-11-25 20:15:52 -07001011
Samuel Hollande2574022023-01-21 17:25:16 -06001012 # Sixth u-boot with both minimum size and aligned size
1013 self.assertIn('u-boot-min-size', entries)
1014 entry = entries['u-boot-min-size']
1015 self.assertEqual(128, entry.offset)
1016 self.assertEqual(32, entry.size)
1017 self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)])
1018 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 32 - len(U_BOOT_DATA)),
1019 data[pos:pos + entry.size])
1020
Simon Glass57454f42016-11-25 20:15:52 -07001021 self.CheckNoGaps(entries)
Samuel Hollande2574022023-01-21 17:25:16 -06001022 self.assertEqual(160, image.size)
Simon Glass57454f42016-11-25 20:15:52 -07001023
Simon Glassafb9caa2020-10-26 17:40:10 -06001024 dtb = fdt.Fdt(out_dtb_fname)
1025 dtb.Scan()
1026 props = self._GetPropTree(dtb, ['size', 'offset', 'image-pos'])
1027 expected = {
1028 'image-pos': 0,
1029 'offset': 0,
Samuel Hollande2574022023-01-21 17:25:16 -06001030 'size': 160,
Simon Glassafb9caa2020-10-26 17:40:10 -06001031
1032 'u-boot:image-pos': 0,
1033 'u-boot:offset': 0,
1034 'u-boot:size': 3 + 5 + len(U_BOOT_DATA),
1035
1036 'u-boot-align-size-nop:image-pos': 12,
1037 'u-boot-align-size-nop:offset': 12,
1038 'u-boot-align-size-nop:size': 4,
1039
1040 'u-boot-align-size:image-pos': 16,
1041 'u-boot-align-size:offset': 16,
1042 'u-boot-align-size:size': 32,
1043
1044 'u-boot-align-end:image-pos': 48,
1045 'u-boot-align-end:offset': 48,
1046 'u-boot-align-end:size': 16,
1047
1048 'u-boot-align-both:image-pos': 64,
1049 'u-boot-align-both:offset': 64,
1050 'u-boot-align-both:size': 64,
Samuel Hollande2574022023-01-21 17:25:16 -06001051
1052 'u-boot-min-size:image-pos': 128,
1053 'u-boot-min-size:offset': 128,
1054 'u-boot-min-size:size': 32,
Simon Glassafb9caa2020-10-26 17:40:10 -06001055 }
1056 self.assertEqual(expected, props)
1057
Simon Glass57454f42016-11-25 20:15:52 -07001058 def testPackAlignPowerOf2(self):
1059 """Test that invalid entry alignment is detected"""
1060 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001061 self._DoTestFile('010_pack_align_power2.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001062 self.assertIn("Node '/binman/u-boot': Alignment 5 must be a power "
1063 "of two", str(e.exception))
1064
1065 def testPackAlignSizePowerOf2(self):
1066 """Test that invalid entry size alignment is detected"""
1067 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001068 self._DoTestFile('011_pack_align_size_power2.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001069 self.assertIn("Node '/binman/u-boot': Alignment size 55 must be a "
1070 "power of two", str(e.exception))
1071
1072 def testPackInvalidAlign(self):
Simon Glasse8561af2018-08-01 15:22:37 -06001073 """Test detection of an offset that does not match its alignment"""
Simon Glass57454f42016-11-25 20:15:52 -07001074 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001075 self._DoTestFile('012_pack_inv_align.dts')
Simon Glasse8561af2018-08-01 15:22:37 -06001076 self.assertIn("Node '/binman/u-boot': Offset 0x5 (5) does not match "
Simon Glass57454f42016-11-25 20:15:52 -07001077 "align 0x4 (4)", str(e.exception))
1078
1079 def testPackInvalidSizeAlign(self):
1080 """Test that invalid entry size alignment is detected"""
1081 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001082 self._DoTestFile('013_pack_inv_size_align.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001083 self.assertIn("Node '/binman/u-boot': Size 0x5 (5) does not match "
1084 "align-size 0x4 (4)", str(e.exception))
1085
1086 def testPackOverlap(self):
1087 """Test that overlapping regions are detected"""
1088 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001089 self._DoTestFile('014_pack_overlap.dts')
Simon Glasse8561af2018-08-01 15:22:37 -06001090 self.assertIn("Node '/binman/u-boot-align': Offset 0x3 (3) overlaps "
Simon Glass57454f42016-11-25 20:15:52 -07001091 "with previous entry '/binman/u-boot' ending at 0x4 (4)",
1092 str(e.exception))
1093
1094 def testPackEntryOverflow(self):
1095 """Test that entries that overflow their size are detected"""
1096 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001097 self._DoTestFile('015_pack_overflow.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001098 self.assertIn("Node '/binman/u-boot': Entry contents size is 0x4 (4) "
1099 "but entry size is 0x3 (3)", str(e.exception))
1100
1101 def testPackImageOverflow(self):
1102 """Test that entries which overflow the image size are detected"""
1103 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001104 self._DoTestFile('016_pack_image_overflow.dts')
Simon Glasseca32212018-06-01 09:38:12 -06001105 self.assertIn("Section '/binman': contents size 0x4 (4) exceeds section "
Simon Glass57454f42016-11-25 20:15:52 -07001106 "size 0x3 (3)", str(e.exception))
1107
1108 def testPackImageSize(self):
1109 """Test that the image size can be set"""
Simon Glass511f6582018-10-01 12:22:30 -06001110 retcode = self._DoTestFile('017_pack_image_size.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001111 self.assertEqual(0, retcode)
1112 self.assertIn('image', control.images)
1113 image = control.images['image']
Simon Glass39dd2152019-07-08 14:25:47 -06001114 self.assertEqual(7, image.size)
Simon Glass57454f42016-11-25 20:15:52 -07001115
1116 def testPackImageSizeAlign(self):
1117 """Test that image size alignemnt works as expected"""
Simon Glass511f6582018-10-01 12:22:30 -06001118 retcode = self._DoTestFile('018_pack_image_align.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001119 self.assertEqual(0, retcode)
1120 self.assertIn('image', control.images)
1121 image = control.images['image']
Simon Glass39dd2152019-07-08 14:25:47 -06001122 self.assertEqual(16, image.size)
Simon Glass57454f42016-11-25 20:15:52 -07001123
1124 def testPackInvalidImageAlign(self):
1125 """Test that invalid image alignment is detected"""
1126 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001127 self._DoTestFile('019_pack_inv_image_align.dts')
Simon Glasseca32212018-06-01 09:38:12 -06001128 self.assertIn("Section '/binman': Size 0x7 (7) does not match "
Simon Glass57454f42016-11-25 20:15:52 -07001129 "align-size 0x8 (8)", str(e.exception))
1130
Simon Glass2a0fa982022-02-11 13:23:21 -07001131 def testPackAlignPowerOf2Inv(self):
Simon Glass57454f42016-11-25 20:15:52 -07001132 """Test that invalid image alignment is detected"""
1133 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001134 self._DoTestFile('020_pack_inv_image_align_power2.dts')
Simon Glass39dd2152019-07-08 14:25:47 -06001135 self.assertIn("Image '/binman': Alignment size 131 must be a power of "
Simon Glass57454f42016-11-25 20:15:52 -07001136 "two", str(e.exception))
1137
1138 def testImagePadByte(self):
1139 """Test that the image pad byte can be specified"""
Simon Glass7057d022018-10-01 21:12:47 -06001140 self._SetupSplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001141 data = self._DoReadFile('021_image_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07001142 self.assertEqual(U_BOOT_SPL_DATA + tools.get_bytes(0xff, 1) +
Simon Glassac0d4952019-05-14 15:53:47 -06001143 U_BOOT_DATA, data)
Simon Glass57454f42016-11-25 20:15:52 -07001144
1145 def testImageName(self):
1146 """Test that image files can be named"""
Simon Glass511f6582018-10-01 12:22:30 -06001147 retcode = self._DoTestFile('022_image_name.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001148 self.assertEqual(0, retcode)
1149 image = control.images['image1']
Simon Glass80025522022-01-29 14:14:04 -07001150 fname = tools.get_output_filename('test-name')
Simon Glass57454f42016-11-25 20:15:52 -07001151 self.assertTrue(os.path.exists(fname))
1152
1153 image = control.images['image2']
Simon Glass80025522022-01-29 14:14:04 -07001154 fname = tools.get_output_filename('test-name.xx')
Simon Glass57454f42016-11-25 20:15:52 -07001155 self.assertTrue(os.path.exists(fname))
1156
1157 def testBlobFilename(self):
1158 """Test that generic blobs can be provided by filename"""
Simon Glass511f6582018-10-01 12:22:30 -06001159 data = self._DoReadFile('023_blob.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001160 self.assertEqual(BLOB_DATA, data)
1161
1162 def testPackSorted(self):
1163 """Test that entries can be sorted"""
Simon Glass7057d022018-10-01 21:12:47 -06001164 self._SetupSplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001165 data = self._DoReadFile('024_sorted.dts')
Simon Glass80025522022-01-29 14:14:04 -07001166 self.assertEqual(tools.get_bytes(0, 1) + U_BOOT_SPL_DATA +
1167 tools.get_bytes(0, 2) + U_BOOT_DATA, data)
Simon Glass57454f42016-11-25 20:15:52 -07001168
Simon Glasse8561af2018-08-01 15:22:37 -06001169 def testPackZeroOffset(self):
1170 """Test that an entry at offset 0 is not given a new offset"""
Marek Vasutf7413f02023-07-18 07:23:58 -06001171 self._SetupSplElf()
Simon Glass57454f42016-11-25 20:15:52 -07001172 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001173 self._DoTestFile('025_pack_zero_size.dts')
Simon Glasse8561af2018-08-01 15:22:37 -06001174 self.assertIn("Node '/binman/u-boot-spl': Offset 0x0 (0) overlaps "
Simon Glass57454f42016-11-25 20:15:52 -07001175 "with previous entry '/binman/u-boot' ending at 0x4 (4)",
1176 str(e.exception))
1177
1178 def testPackUbootDtb(self):
1179 """Test that a device tree can be added to U-Boot"""
Simon Glass511f6582018-10-01 12:22:30 -06001180 data = self._DoReadFile('026_pack_u_boot_dtb.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001181 self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA, data)
Simon Glass72232452016-11-25 20:15:53 -07001182
1183 def testPackX86RomNoSize(self):
1184 """Test that the end-at-4gb property requires a size property"""
Marek Vasutf7413f02023-07-18 07:23:58 -06001185 self._SetupSplElf()
Simon Glass72232452016-11-25 20:15:53 -07001186 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001187 self._DoTestFile('027_pack_4gb_no_size.dts')
Simon Glass39dd2152019-07-08 14:25:47 -06001188 self.assertIn("Image '/binman': Section size must be provided when "
Simon Glass72232452016-11-25 20:15:53 -07001189 "using end-at-4gb", str(e.exception))
1190
Jagdish Gediya0fb978c2018-09-03 21:35:07 +05301191 def test4gbAndSkipAtStartTogether(self):
1192 """Test that the end-at-4gb and skip-at-size property can't be used
1193 together"""
Marek Vasutf7413f02023-07-18 07:23:58 -06001194 self._SetupSplElf()
Jagdish Gediya0fb978c2018-09-03 21:35:07 +05301195 with self.assertRaises(ValueError) as e:
Simon Glass11f2bd02019-08-24 07:23:02 -06001196 self._DoTestFile('098_4gb_and_skip_at_start_together.dts')
Simon Glass39dd2152019-07-08 14:25:47 -06001197 self.assertIn("Image '/binman': Provide either 'end-at-4gb' or "
Jagdish Gediya0fb978c2018-09-03 21:35:07 +05301198 "'skip-at-start'", str(e.exception))
1199
Simon Glass72232452016-11-25 20:15:53 -07001200 def testPackX86RomOutside(self):
Simon Glasse8561af2018-08-01 15:22:37 -06001201 """Test that the end-at-4gb property checks for offset boundaries"""
Marek Vasutf7413f02023-07-18 07:23:58 -06001202 self._SetupSplElf()
Simon Glass72232452016-11-25 20:15:53 -07001203 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001204 self._DoTestFile('028_pack_4gb_outside.dts')
Simon Glassd6179862020-10-26 17:40:05 -06001205 self.assertIn("Node '/binman/u-boot': Offset 0x0 (0) size 0x4 (4) "
1206 "is outside the section '/binman' starting at "
1207 '0xffffffe0 (4294967264) of size 0x20 (32)',
Simon Glass72232452016-11-25 20:15:53 -07001208 str(e.exception))
1209
1210 def testPackX86Rom(self):
1211 """Test that a basic x86 ROM can be created"""
Simon Glass7057d022018-10-01 21:12:47 -06001212 self._SetupSplElf()
Simon Glass1d167762019-08-24 07:23:01 -06001213 data = self._DoReadFile('029_x86_rom.dts')
Simon Glass80025522022-01-29 14:14:04 -07001214 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 3) + U_BOOT_SPL_DATA +
1215 tools.get_bytes(0, 2), data)
Simon Glass72232452016-11-25 20:15:53 -07001216
1217 def testPackX86RomMeNoDesc(self):
1218 """Test that an invalid Intel descriptor entry is detected"""
Simon Glasse88cef92020-07-09 18:39:41 -06001219 try:
Simon Glass14c596c2020-07-25 15:11:19 -06001220 TestFunctional._MakeInputFile('descriptor-empty.bin', b'')
Simon Glasse88cef92020-07-09 18:39:41 -06001221 with self.assertRaises(ValueError) as e:
Simon Glass14c596c2020-07-25 15:11:19 -06001222 self._DoTestFile('163_x86_rom_me_empty.dts')
Simon Glasse88cef92020-07-09 18:39:41 -06001223 self.assertIn("Node '/binman/intel-descriptor': Cannot find Intel Flash Descriptor (FD) signature",
1224 str(e.exception))
1225 finally:
1226 self._SetupDescriptor()
Simon Glass72232452016-11-25 20:15:53 -07001227
1228 def testPackX86RomBadDesc(self):
1229 """Test that the Intel requires a descriptor entry"""
1230 with self.assertRaises(ValueError) as e:
Simon Glass1d167762019-08-24 07:23:01 -06001231 self._DoTestFile('030_x86_rom_me_no_desc.dts')
Simon Glasse8561af2018-08-01 15:22:37 -06001232 self.assertIn("Node '/binman/intel-me': No offset set with "
1233 "offset-unset: should another entry provide this correct "
1234 "offset?", str(e.exception))
Simon Glass72232452016-11-25 20:15:53 -07001235
1236 def testPackX86RomMe(self):
1237 """Test that an x86 ROM with an ME region can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001238 data = self._DoReadFile('031_x86_rom_me.dts')
Simon Glass80025522022-01-29 14:14:04 -07001239 expected_desc = tools.read_file(self.TestFile('descriptor.bin'))
Simon Glass759af872019-07-08 13:18:54 -06001240 if data[:0x1000] != expected_desc:
1241 self.fail('Expected descriptor binary at start of image')
Simon Glass72232452016-11-25 20:15:53 -07001242 self.assertEqual(ME_DATA, data[0x1000:0x1000 + len(ME_DATA)])
1243
1244 def testPackVga(self):
1245 """Test that an image with a VGA binary can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001246 data = self._DoReadFile('032_intel_vga.dts')
Simon Glass72232452016-11-25 20:15:53 -07001247 self.assertEqual(VGA_DATA, data[:len(VGA_DATA)])
1248
1249 def testPackStart16(self):
1250 """Test that an image with an x86 start16 region can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001251 data = self._DoReadFile('033_x86_start16.dts')
Simon Glass72232452016-11-25 20:15:53 -07001252 self.assertEqual(X86_START16_DATA, data[:len(X86_START16_DATA)])
1253
Jagdish Gediya311d4842018-09-03 21:35:08 +05301254 def testPackPowerpcMpc85xxBootpgResetvec(self):
1255 """Test that an image with powerpc-mpc85xx-bootpg-resetvec can be
1256 created"""
Simon Glass11f2bd02019-08-24 07:23:02 -06001257 data = self._DoReadFile('150_powerpc_mpc85xx_bootpg_resetvec.dts')
Jagdish Gediya311d4842018-09-03 21:35:08 +05301258 self.assertEqual(PPC_MPC85XX_BR_DATA, data[:len(PPC_MPC85XX_BR_DATA)])
1259
Simon Glass6ba679c2018-07-06 10:27:17 -06001260 def _RunMicrocodeTest(self, dts_fname, nodtb_data, ucode_second=False):
Simon Glass820af1d2018-07-06 10:27:16 -06001261 """Handle running a test for insertion of microcode
1262
1263 Args:
1264 dts_fname: Name of test .dts file
1265 nodtb_data: Data that we expect in the first section
Simon Glass6ba679c2018-07-06 10:27:17 -06001266 ucode_second: True if the microsecond entry is second instead of
1267 third
Simon Glass820af1d2018-07-06 10:27:16 -06001268
1269 Returns:
1270 Tuple:
1271 Contents of first region (U-Boot or SPL)
Simon Glasse8561af2018-08-01 15:22:37 -06001272 Offset and size components of microcode pointer, as inserted
Simon Glass820af1d2018-07-06 10:27:16 -06001273 in the above (two 4-byte words)
1274 """
Simon Glass3d274232017-11-12 21:52:27 -07001275 data = self._DoReadFile(dts_fname, True)
Simon Glass72232452016-11-25 20:15:53 -07001276
1277 # Now check the device tree has no microcode
Simon Glass6ba679c2018-07-06 10:27:17 -06001278 if ucode_second:
1279 ucode_content = data[len(nodtb_data):]
1280 ucode_pos = len(nodtb_data)
1281 dtb_with_ucode = ucode_content[16:]
1282 fdt_len = self.GetFdtLen(dtb_with_ucode)
1283 else:
1284 dtb_with_ucode = data[len(nodtb_data):]
1285 fdt_len = self.GetFdtLen(dtb_with_ucode)
1286 ucode_content = dtb_with_ucode[fdt_len:]
1287 ucode_pos = len(nodtb_data) + fdt_len
Simon Glass80025522022-01-29 14:14:04 -07001288 fname = tools.get_output_filename('test.dtb')
Simon Glass72232452016-11-25 20:15:53 -07001289 with open(fname, 'wb') as fd:
Simon Glass820af1d2018-07-06 10:27:16 -06001290 fd.write(dtb_with_ucode)
Simon Glass22c92ca2017-05-27 07:38:29 -06001291 dtb = fdt.FdtScan(fname)
1292 ucode = dtb.GetNode('/microcode')
Simon Glass72232452016-11-25 20:15:53 -07001293 self.assertTrue(ucode)
1294 for node in ucode.subnodes:
1295 self.assertFalse(node.props.get('data'))
1296
Simon Glass72232452016-11-25 20:15:53 -07001297 # Check that the microcode appears immediately after the Fdt
1298 # This matches the concatenation of the data properties in
Simon Glasse83679d2017-11-12 21:52:26 -07001299 # the /microcode/update@xxx nodes in 34_x86_ucode.dts.
Simon Glass72232452016-11-25 20:15:53 -07001300 ucode_data = struct.pack('>4L', 0x12345678, 0x12345679, 0xabcd0000,
1301 0x78235609)
Simon Glass820af1d2018-07-06 10:27:16 -06001302 self.assertEqual(ucode_data, ucode_content[:len(ucode_data)])
Simon Glass72232452016-11-25 20:15:53 -07001303
1304 # Check that the microcode pointer was inserted. It should match the
Simon Glasse8561af2018-08-01 15:22:37 -06001305 # expected offset and size
Simon Glass72232452016-11-25 20:15:53 -07001306 pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
1307 len(ucode_data))
Simon Glass6ba679c2018-07-06 10:27:17 -06001308 u_boot = data[:len(nodtb_data)]
1309 return u_boot, pos_and_size
Simon Glass3d274232017-11-12 21:52:27 -07001310
1311 def testPackUbootMicrocode(self):
1312 """Test that x86 microcode can be handled correctly
1313
1314 We expect to see the following in the image, in order:
1315 u-boot-nodtb.bin with a microcode pointer inserted at the correct
1316 place
1317 u-boot.dtb with the microcode removed
1318 the microcode
1319 """
Simon Glass511f6582018-10-01 12:22:30 -06001320 first, pos_and_size = self._RunMicrocodeTest('034_x86_ucode.dts',
Simon Glass3d274232017-11-12 21:52:27 -07001321 U_BOOT_NODTB_DATA)
Simon Glass303f62f2019-05-17 22:00:46 -06001322 self.assertEqual(b'nodtb with microcode' + pos_and_size +
1323 b' somewhere in here', first)
Simon Glass72232452016-11-25 20:15:53 -07001324
Simon Glassbac25c82017-05-27 07:38:26 -06001325 def _RunPackUbootSingleMicrocode(self):
Simon Glass72232452016-11-25 20:15:53 -07001326 """Test that x86 microcode can be handled correctly
1327
1328 We expect to see the following in the image, in order:
1329 u-boot-nodtb.bin with a microcode pointer inserted at the correct
1330 place
1331 u-boot.dtb with the microcode
1332 an empty microcode region
1333 """
1334 # We need the libfdt library to run this test since only that allows
1335 # finding the offset of a property. This is required by
1336 # Entry_u_boot_dtb_with_ucode.ObtainContents().
Simon Glass511f6582018-10-01 12:22:30 -06001337 data = self._DoReadFile('035_x86_single_ucode.dts', True)
Simon Glass72232452016-11-25 20:15:53 -07001338
1339 second = data[len(U_BOOT_NODTB_DATA):]
1340
1341 fdt_len = self.GetFdtLen(second)
1342 third = second[fdt_len:]
1343 second = second[:fdt_len]
1344
Simon Glassbac25c82017-05-27 07:38:26 -06001345 ucode_data = struct.pack('>2L', 0x12345678, 0x12345679)
1346 self.assertIn(ucode_data, second)
1347 ucode_pos = second.find(ucode_data) + len(U_BOOT_NODTB_DATA)
Simon Glass72232452016-11-25 20:15:53 -07001348
Simon Glassbac25c82017-05-27 07:38:26 -06001349 # Check that the microcode pointer was inserted. It should match the
Simon Glasse8561af2018-08-01 15:22:37 -06001350 # expected offset and size
Simon Glassbac25c82017-05-27 07:38:26 -06001351 pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
1352 len(ucode_data))
1353 first = data[:len(U_BOOT_NODTB_DATA)]
Simon Glass303f62f2019-05-17 22:00:46 -06001354 self.assertEqual(b'nodtb with microcode' + pos_and_size +
1355 b' somewhere in here', first)
Simon Glass996021e2016-11-25 20:15:54 -07001356
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001357 def testPackUbootSingleMicrocode(self):
1358 """Test that x86 microcode can be handled correctly with fdt_normal.
1359 """
Simon Glassbac25c82017-05-27 07:38:26 -06001360 self._RunPackUbootSingleMicrocode()
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001361
Simon Glass996021e2016-11-25 20:15:54 -07001362 def testUBootImg(self):
1363 """Test that u-boot.img can be put in a file"""
Simon Glass511f6582018-10-01 12:22:30 -06001364 data = self._DoReadFile('036_u_boot_img.dts')
Simon Glass996021e2016-11-25 20:15:54 -07001365 self.assertEqual(U_BOOT_IMG_DATA, data)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001366
1367 def testNoMicrocode(self):
1368 """Test that a missing microcode region is detected"""
1369 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001370 self._DoReadFile('037_x86_no_ucode.dts', True)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001371 self.assertIn("Node '/binman/u-boot-dtb-with-ucode': No /microcode "
1372 "node found in ", str(e.exception))
1373
1374 def testMicrocodeWithoutNode(self):
1375 """Test that a missing u-boot-dtb-with-ucode node is detected"""
1376 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001377 self._DoReadFile('038_x86_ucode_missing_node.dts', True)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001378 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
1379 "microcode region u-boot-dtb-with-ucode", str(e.exception))
1380
1381 def testMicrocodeWithoutNode2(self):
1382 """Test that a missing u-boot-ucode node is detected"""
1383 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001384 self._DoReadFile('039_x86_ucode_missing_node2.dts', True)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001385 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
1386 "microcode region u-boot-ucode", str(e.exception))
1387
1388 def testMicrocodeWithoutPtrInElf(self):
1389 """Test that a U-Boot binary without the microcode symbol is detected"""
1390 # ELF file without a '_dt_ucode_base_size' symbol
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001391 try:
Simon Glassfaaaa162019-08-24 07:22:55 -06001392 TestFunctional._MakeInputFile('u-boot',
Simon Glass80025522022-01-29 14:14:04 -07001393 tools.read_file(self.ElfTestFile('u_boot_no_ucode_ptr')))
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001394
1395 with self.assertRaises(ValueError) as e:
Simon Glassbac25c82017-05-27 07:38:26 -06001396 self._RunPackUbootSingleMicrocode()
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001397 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot locate "
1398 "_dt_ucode_base_size symbol in u-boot", str(e.exception))
1399
1400 finally:
1401 # Put the original file back
Simon Glass4affd4b2019-08-24 07:22:54 -06001402 TestFunctional._MakeInputFile('u-boot',
Simon Glass80025522022-01-29 14:14:04 -07001403 tools.read_file(self.ElfTestFile('u_boot_ucode_ptr')))
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001404
1405 def testMicrocodeNotInImage(self):
1406 """Test that microcode must be placed within the image"""
1407 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001408 self._DoReadFile('040_x86_ucode_not_in_image.dts', True)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001409 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Microcode "
1410 "pointer _dt_ucode_base_size at fffffe14 is outside the "
Simon Glassad5a7712018-06-01 09:38:14 -06001411 "section ranging from 00000000 to 0000002e", str(e.exception))
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001412
1413 def testWithoutMicrocode(self):
1414 """Test that we can cope with an image without microcode (e.g. qemu)"""
Simon Glassfaaaa162019-08-24 07:22:55 -06001415 TestFunctional._MakeInputFile('u-boot',
Simon Glass80025522022-01-29 14:14:04 -07001416 tools.read_file(self.ElfTestFile('u_boot_no_ucode_ptr')))
Simon Glass511f6582018-10-01 12:22:30 -06001417 data, dtb, _, _ = self._DoReadFileDtb('044_x86_optional_ucode.dts', True)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001418
1419 # Now check the device tree has no microcode
1420 self.assertEqual(U_BOOT_NODTB_DATA, data[:len(U_BOOT_NODTB_DATA)])
1421 second = data[len(U_BOOT_NODTB_DATA):]
1422
1423 fdt_len = self.GetFdtLen(second)
1424 self.assertEqual(dtb, second[:fdt_len])
1425
1426 used_len = len(U_BOOT_NODTB_DATA) + fdt_len
1427 third = data[used_len:]
Simon Glass80025522022-01-29 14:14:04 -07001428 self.assertEqual(tools.get_bytes(0, 0x200 - used_len), third)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001429
1430 def testUnknownPosSize(self):
1431 """Test that microcode must be placed within the image"""
1432 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001433 self._DoReadFile('041_unknown_pos_size.dts', True)
Simon Glasse8561af2018-08-01 15:22:37 -06001434 self.assertIn("Section '/binman': Unable to set offset/size for unknown "
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001435 "entry 'invalid-entry'", str(e.exception))
Simon Glassb4176d42016-11-25 20:15:56 -07001436
1437 def testPackFsp(self):
1438 """Test that an image with a FSP binary can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001439 data = self._DoReadFile('042_intel_fsp.dts')
Simon Glassb4176d42016-11-25 20:15:56 -07001440 self.assertEqual(FSP_DATA, data[:len(FSP_DATA)])
1441
1442 def testPackCmc(self):
Bin Mengd7bcdf52017-08-15 22:41:54 -07001443 """Test that an image with a CMC binary can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001444 data = self._DoReadFile('043_intel_cmc.dts')
Simon Glassb4176d42016-11-25 20:15:56 -07001445 self.assertEqual(CMC_DATA, data[:len(CMC_DATA)])
Bin Mengd7bcdf52017-08-15 22:41:54 -07001446
1447 def testPackVbt(self):
1448 """Test that an image with a VBT binary can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001449 data = self._DoReadFile('046_intel_vbt.dts')
Bin Mengd7bcdf52017-08-15 22:41:54 -07001450 self.assertEqual(VBT_DATA, data[:len(VBT_DATA)])
Simon Glassac599912017-11-12 21:52:22 -07001451
Simon Glass7f94e832017-11-12 21:52:25 -07001452 def testSplBssPad(self):
1453 """Test that we can pad SPL's BSS with zeros"""
Simon Glass3d274232017-11-12 21:52:27 -07001454 # ELF file with a '__bss_size' symbol
Simon Glass7057d022018-10-01 21:12:47 -06001455 self._SetupSplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001456 data = self._DoReadFile('047_spl_bss_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07001457 self.assertEqual(U_BOOT_SPL_DATA + tools.get_bytes(0, 10) + U_BOOT_DATA,
Simon Glassac0d4952019-05-14 15:53:47 -06001458 data)
Simon Glass7f94e832017-11-12 21:52:25 -07001459
Simon Glass04cda032018-10-01 21:12:42 -06001460 def testSplBssPadMissing(self):
1461 """Test that a missing symbol is detected"""
Simon Glass7057d022018-10-01 21:12:47 -06001462 self._SetupSplElf('u_boot_ucode_ptr')
Simon Glass24ad3652017-11-13 18:54:54 -07001463 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001464 self._DoReadFile('047_spl_bss_pad.dts')
Simon Glass24ad3652017-11-13 18:54:54 -07001465 self.assertIn('Expected __bss_size symbol in spl/u-boot-spl',
1466 str(e.exception))
1467
Simon Glasse83679d2017-11-12 21:52:26 -07001468 def testPackStart16Spl(self):
Simon Glassed40e962018-09-14 04:57:10 -06001469 """Test that an image with an x86 start16 SPL region can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001470 data = self._DoReadFile('048_x86_start16_spl.dts')
Simon Glasse83679d2017-11-12 21:52:26 -07001471 self.assertEqual(X86_START16_SPL_DATA, data[:len(X86_START16_SPL_DATA)])
1472
Simon Glass6ba679c2018-07-06 10:27:17 -06001473 def _PackUbootSplMicrocode(self, dts, ucode_second=False):
1474 """Helper function for microcode tests
Simon Glass3d274232017-11-12 21:52:27 -07001475
1476 We expect to see the following in the image, in order:
1477 u-boot-spl-nodtb.bin with a microcode pointer inserted at the
1478 correct place
1479 u-boot.dtb with the microcode removed
1480 the microcode
Simon Glass6ba679c2018-07-06 10:27:17 -06001481
1482 Args:
1483 dts: Device tree file to use for test
1484 ucode_second: True if the microsecond entry is second instead of
1485 third
Simon Glass3d274232017-11-12 21:52:27 -07001486 """
Simon Glass7057d022018-10-01 21:12:47 -06001487 self._SetupSplElf('u_boot_ucode_ptr')
Simon Glass6ba679c2018-07-06 10:27:17 -06001488 first, pos_and_size = self._RunMicrocodeTest(dts, U_BOOT_SPL_NODTB_DATA,
1489 ucode_second=ucode_second)
Simon Glass303f62f2019-05-17 22:00:46 -06001490 self.assertEqual(b'splnodtb with microc' + pos_and_size +
1491 b'ter somewhere in here', first)
Simon Glass3d274232017-11-12 21:52:27 -07001492
Simon Glass6ba679c2018-07-06 10:27:17 -06001493 def testPackUbootSplMicrocode(self):
1494 """Test that x86 microcode can be handled correctly in SPL"""
Marek Vasutf7413f02023-07-18 07:23:58 -06001495 self._SetupSplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001496 self._PackUbootSplMicrocode('049_x86_ucode_spl.dts')
Simon Glass6ba679c2018-07-06 10:27:17 -06001497
1498 def testPackUbootSplMicrocodeReorder(self):
1499 """Test that order doesn't matter for microcode entries
1500
1501 This is the same as testPackUbootSplMicrocode but when we process the
1502 u-boot-ucode entry we have not yet seen the u-boot-dtb-with-ucode
1503 entry, so we reply on binman to try later.
1504 """
Simon Glass511f6582018-10-01 12:22:30 -06001505 self._PackUbootSplMicrocode('058_x86_ucode_spl_needs_retry.dts',
Simon Glass6ba679c2018-07-06 10:27:17 -06001506 ucode_second=True)
1507
Simon Glassa409c932017-11-12 21:52:28 -07001508 def testPackMrc(self):
1509 """Test that an image with an MRC binary can be created"""
Simon Glass511f6582018-10-01 12:22:30 -06001510 data = self._DoReadFile('050_intel_mrc.dts')
Simon Glassa409c932017-11-12 21:52:28 -07001511 self.assertEqual(MRC_DATA, data[:len(MRC_DATA)])
1512
Simon Glass9aa6a6f2017-11-13 18:54:55 -07001513 def testSplDtb(self):
1514 """Test that an image with spl/u-boot-spl.dtb can be created"""
Marek Vasutf7413f02023-07-18 07:23:58 -06001515 self._SetupSplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001516 data = self._DoReadFile('051_u_boot_spl_dtb.dts')
Simon Glass9aa6a6f2017-11-13 18:54:55 -07001517 self.assertEqual(U_BOOT_SPL_DTB_DATA, data[:len(U_BOOT_SPL_DTB_DATA)])
1518
Simon Glass0a6da312017-11-13 18:54:56 -07001519 def testSplNoDtb(self):
1520 """Test that an image with spl/u-boot-spl-nodtb.bin can be created"""
Simon Glass13089cc2021-04-25 08:39:32 +12001521 self._SetupSplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001522 data = self._DoReadFile('052_u_boot_spl_nodtb.dts')
Simon Glass0a6da312017-11-13 18:54:56 -07001523 self.assertEqual(U_BOOT_SPL_NODTB_DATA, data[:len(U_BOOT_SPL_NODTB_DATA)])
1524
Simon Glass7098b7f2021-03-21 18:24:30 +13001525 def checkSymbols(self, dts, base_data, u_boot_offset, entry_args=None,
Simon Glass4b0f4142024-08-26 13:11:40 -06001526 use_expanded=False, no_write_symbols=False,
1527 symbols_base=None):
Simon Glass31e04cb2021-03-18 20:24:56 +13001528 """Check the image contains the expected symbol values
1529
1530 Args:
1531 dts: Device tree file to use for test
1532 base_data: Data before and after 'u-boot' section
Simon Glass3eb30a42024-08-26 13:11:42 -06001533 u_boot_offset (int): Offset of 'u-boot' section in image, or None if
1534 the offset not available due to it being in a compressed section
Simon Glass7098b7f2021-03-21 18:24:30 +13001535 entry_args: Dict of entry args to supply to binman
1536 key: arg name
1537 value: value of that arg
1538 use_expanded: True to use expanded entries where available, e.g.
1539 'u-boot-expanded' instead of 'u-boot'
Simon Glass4b0f4142024-08-26 13:11:40 -06001540 symbols_base (int): Value to expect for symbols-base in u-boot-spl,
1541 None if none
Simon Glass31e04cb2021-03-18 20:24:56 +13001542 """
Simon Glass5d0c0262019-08-24 07:22:56 -06001543 elf_fname = self.ElfTestFile('u_boot_binman_syms')
Simon Glass4ca8e042017-11-13 18:55:01 -07001544 syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
1545 addr = elf.GetSymbolAddress(elf_fname, '__image_copy_start')
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03001546 self.assertEqual(syms['_binman_sym_magic'].address, addr)
Simon Glass31e04cb2021-03-18 20:24:56 +13001547 self.assertEqual(syms['_binman_u_boot_spl_any_prop_offset'].address,
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03001548 addr + 4)
Simon Glass4ca8e042017-11-13 18:55:01 -07001549
Simon Glass7057d022018-10-01 21:12:47 -06001550 self._SetupSplElf('u_boot_binman_syms')
Simon Glass7098b7f2021-03-21 18:24:30 +13001551 data = self._DoReadFileDtb(dts, entry_args=entry_args,
Simon Glass3eb30a42024-08-26 13:11:42 -06001552 use_expanded=use_expanded,
1553 verbosity=None if u_boot_offset else 3)[0]
1554
1555 # The lz4-compressed version of the U-Boot data is 19 bytes long
1556 comp_uboot_len = 19
1557
Simon Glass31e04cb2021-03-18 20:24:56 +13001558 # The image should contain the symbols from u_boot_binman_syms.c
1559 # Note that image_pos is adjusted by the base address of the image,
1560 # which is 0x10 in our test image
Simon Glass3eb30a42024-08-26 13:11:42 -06001561 # If u_boot_offset is None, Binman should write -1U into the image
Simon Glass4b0f4142024-08-26 13:11:40 -06001562 vals2 = (elf.BINMAN_SYM_MAGIC_VALUE, 0x00,
Simon Glass3eb30a42024-08-26 13:11:42 -06001563 u_boot_offset + len(U_BOOT_DATA) if u_boot_offset else
1564 len(U_BOOT_SPL_DATA) + 1 + comp_uboot_len,
1565 0x10 + u_boot_offset if u_boot_offset else 0xffffffff, 0x04)
Simon Glass4b0f4142024-08-26 13:11:40 -06001566
1567 # u-boot-spl has a symbols-base property, so take that into account if
1568 # required. The caller must supply the value
1569 vals = list(vals2)
1570 if symbols_base is not None:
1571 vals[3] = symbols_base + u_boot_offset
1572 vals = tuple(vals)
1573
Simon Glass4b4049e2024-08-26 13:11:39 -06001574 sym_values = struct.pack('<LLQLL', *vals)
Simon Glass4b0f4142024-08-26 13:11:40 -06001575 sym_values2 = struct.pack('<LLQLL', *vals2)
Simon Glass4abf7842023-07-18 07:23:54 -06001576 if no_write_symbols:
Simon Glass4b4049e2024-08-26 13:11:39 -06001577 self.assertEqual(
1578 base_data +
1579 tools.get_bytes(0xff, 0x38 - len(base_data)) +
1580 U_BOOT_DATA + base_data, data)
Simon Glass4abf7842023-07-18 07:23:54 -06001581 else:
Simon Glass4b4049e2024-08-26 13:11:39 -06001582 got_vals = struct.unpack('<LLQLL', data[:24])
1583
1584 # For debugging:
1585 #print('expect:', list(f'{v:x}' for v in vals))
1586 #print(' got:', list(f'{v:x}' for v in got_vals))
1587
1588 self.assertEqual(vals, got_vals)
1589 self.assertEqual(sym_values, data[:24])
1590
1591 blen = len(base_data)
1592 self.assertEqual(base_data[24:], data[24:blen])
1593 self.assertEqual(0xff, data[blen])
1594
Simon Glass3eb30a42024-08-26 13:11:42 -06001595 if u_boot_offset:
1596 ofs = blen + 1 + len(U_BOOT_DATA)
1597 self.assertEqual(U_BOOT_DATA, data[blen + 1:ofs])
1598 else:
1599 ofs = blen + 1 + comp_uboot_len
Simon Glass4b4049e2024-08-26 13:11:39 -06001600
Simon Glass4b0f4142024-08-26 13:11:40 -06001601 self.assertEqual(sym_values2, data[ofs:ofs + 24])
Simon Glass4b4049e2024-08-26 13:11:39 -06001602 self.assertEqual(base_data[24:], data[ofs + 24:])
1603
1604 # Just repeating the above asserts all at once, for clarity
Simon Glass3eb30a42024-08-26 13:11:42 -06001605 if u_boot_offset:
1606 expected = (sym_values + base_data[24:] +
1607 tools.get_bytes(0xff, 1) + U_BOOT_DATA +
1608 sym_values2 + base_data[24:])
1609 self.assertEqual(expected, data)
Simon Glass4ca8e042017-11-13 18:55:01 -07001610
Simon Glass31e04cb2021-03-18 20:24:56 +13001611 def testSymbols(self):
1612 """Test binman can assign symbols embedded in U-Boot"""
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03001613 self.checkSymbols('053_symbols.dts', U_BOOT_SPL_DATA, 0x1c)
Simon Glass31e04cb2021-03-18 20:24:56 +13001614
1615 def testSymbolsNoDtb(self):
1616 """Test binman can assign symbols embedded in U-Boot SPL"""
Simon Glass3bbc9932021-03-21 18:24:29 +13001617 self.checkSymbols('196_symbols_nodtb.dts',
Simon Glass31e04cb2021-03-18 20:24:56 +13001618 U_BOOT_SPL_NODTB_DATA + U_BOOT_SPL_DTB_DATA,
1619 0x38)
1620
Simon Glasse76a3e62018-06-01 09:38:11 -06001621 def testPackUnitAddress(self):
1622 """Test that we support multiple binaries with the same name"""
Simon Glass511f6582018-10-01 12:22:30 -06001623 data = self._DoReadFile('054_unit_address.dts')
Simon Glasse76a3e62018-06-01 09:38:11 -06001624 self.assertEqual(U_BOOT_DATA + U_BOOT_DATA, data)
1625
Simon Glassa91e1152018-06-01 09:38:16 -06001626 def testSections(self):
1627 """Basic test of sections"""
Simon Glass511f6582018-10-01 12:22:30 -06001628 data = self._DoReadFile('055_sections.dts')
Simon Glass80025522022-01-29 14:14:04 -07001629 expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) +
1630 U_BOOT_DATA + tools.get_bytes(ord('a'), 12) +
1631 U_BOOT_DATA + tools.get_bytes(ord('&'), 4))
Simon Glassa91e1152018-06-01 09:38:16 -06001632 self.assertEqual(expected, data)
Simon Glassac599912017-11-12 21:52:22 -07001633
Simon Glass30732662018-06-01 09:38:20 -06001634 def testMap(self):
1635 """Tests outputting a map of the images"""
Simon Glass511f6582018-10-01 12:22:30 -06001636 _, _, map_data, _ = self._DoReadFileDtb('055_sections.dts', map=True)
Simon Glass7eca7922018-07-17 13:25:49 -06001637 self.assertEqual('''ImagePos Offset Size Name
Simon Glass49cd2b32023-02-07 14:34:18 -0700163800000000 00000000 00000028 image
Simon Glass7eca7922018-07-17 13:25:49 -0600163900000000 00000000 00000010 section@0
164000000000 00000000 00000004 u-boot
164100000010 00000010 00000010 section@1
164200000010 00000000 00000004 u-boot
164300000020 00000020 00000004 section@2
164400000020 00000000 00000004 u-boot
Simon Glass30732662018-06-01 09:38:20 -06001645''', map_data)
1646
Simon Glass3b78d532018-06-01 09:38:21 -06001647 def testNamePrefix(self):
1648 """Tests that name prefixes are used"""
Simon Glass511f6582018-10-01 12:22:30 -06001649 _, _, map_data, _ = self._DoReadFileDtb('056_name_prefix.dts', map=True)
Simon Glass7eca7922018-07-17 13:25:49 -06001650 self.assertEqual('''ImagePos Offset Size Name
Simon Glass49cd2b32023-02-07 14:34:18 -0700165100000000 00000000 00000028 image
Simon Glass7eca7922018-07-17 13:25:49 -0600165200000000 00000000 00000010 section@0
165300000000 00000000 00000004 ro-u-boot
165400000010 00000010 00000010 section@1
165500000010 00000000 00000004 rw-u-boot
Simon Glass3b78d532018-06-01 09:38:21 -06001656''', map_data)
1657
Simon Glass6ba679c2018-07-06 10:27:17 -06001658 def testUnknownContents(self):
1659 """Test that obtaining the contents works as expected"""
1660 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001661 self._DoReadFile('057_unknown_contents.dts', True)
Simon Glass39dd2152019-07-08 14:25:47 -06001662 self.assertIn("Image '/binman': Internal error: Could not complete "
Simon Glassc585dd42020-04-17 18:09:03 -06001663 "processing of contents: remaining ["
1664 "<binman.etype._testing.Entry__testing ", str(e.exception))
Simon Glass6ba679c2018-07-06 10:27:17 -06001665
Simon Glass2e1169f2018-07-06 10:27:19 -06001666 def testBadChangeSize(self):
1667 """Test that trying to change the size of an entry fails"""
Simon Glasse61b6f62019-07-08 14:25:37 -06001668 try:
1669 state.SetAllowEntryExpansion(False)
1670 with self.assertRaises(ValueError) as e:
1671 self._DoReadFile('059_change_size.dts', True)
Simon Glass8c702fb2019-07-20 12:23:57 -06001672 self.assertIn("Node '/binman/_testing': Cannot update entry size from 2 to 3",
Simon Glasse61b6f62019-07-08 14:25:37 -06001673 str(e.exception))
1674 finally:
1675 state.SetAllowEntryExpansion(True)
Simon Glass2e1169f2018-07-06 10:27:19 -06001676
Simon Glassa87014e2018-07-06 10:27:42 -06001677 def testUpdateFdt(self):
Simon Glasse8561af2018-08-01 15:22:37 -06001678 """Test that we can update the device tree with offset/size info"""
Simon Glass511f6582018-10-01 12:22:30 -06001679 _, _, _, out_dtb_fname = self._DoReadFileDtb('060_fdt_update.dts',
Simon Glassa87014e2018-07-06 10:27:42 -06001680 update_dtb=True)
Simon Glass5463a6a2018-07-17 13:25:52 -06001681 dtb = fdt.Fdt(out_dtb_fname)
1682 dtb.Scan()
Simon Glassfb30e292019-07-20 12:23:51 -06001683 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
Simon Glassa87014e2018-07-06 10:27:42 -06001684 self.assertEqual({
Simon Glass9dcc8612018-08-01 15:22:42 -06001685 'image-pos': 0,
Simon Glass3a9a2b82018-07-17 13:25:28 -06001686 'offset': 0,
Simon Glasse8561af2018-08-01 15:22:37 -06001687 '_testing:offset': 32,
Simon Glass8c702fb2019-07-20 12:23:57 -06001688 '_testing:size': 2,
Simon Glass9dcc8612018-08-01 15:22:42 -06001689 '_testing:image-pos': 32,
Simon Glasse8561af2018-08-01 15:22:37 -06001690 'section@0/u-boot:offset': 0,
Simon Glassa87014e2018-07-06 10:27:42 -06001691 'section@0/u-boot:size': len(U_BOOT_DATA),
Simon Glass9dcc8612018-08-01 15:22:42 -06001692 'section@0/u-boot:image-pos': 0,
Simon Glasse8561af2018-08-01 15:22:37 -06001693 'section@0:offset': 0,
Simon Glassa87014e2018-07-06 10:27:42 -06001694 'section@0:size': 16,
Simon Glass9dcc8612018-08-01 15:22:42 -06001695 'section@0:image-pos': 0,
Simon Glassa87014e2018-07-06 10:27:42 -06001696
Simon Glasse8561af2018-08-01 15:22:37 -06001697 'section@1/u-boot:offset': 0,
Simon Glassa87014e2018-07-06 10:27:42 -06001698 'section@1/u-boot:size': len(U_BOOT_DATA),
Simon Glass9dcc8612018-08-01 15:22:42 -06001699 'section@1/u-boot:image-pos': 16,
Simon Glasse8561af2018-08-01 15:22:37 -06001700 'section@1:offset': 16,
Simon Glassa87014e2018-07-06 10:27:42 -06001701 'section@1:size': 16,
Simon Glass9dcc8612018-08-01 15:22:42 -06001702 'section@1:image-pos': 16,
Simon Glassa87014e2018-07-06 10:27:42 -06001703 'size': 40
1704 }, props)
1705
1706 def testUpdateFdtBad(self):
1707 """Test that we detect when ProcessFdt never completes"""
1708 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001709 self._DoReadFileDtb('061_fdt_update_bad.dts', update_dtb=True)
Simon Glassa87014e2018-07-06 10:27:42 -06001710 self.assertIn('Could not complete processing of Fdt: remaining '
Simon Glassc585dd42020-04-17 18:09:03 -06001711 '[<binman.etype._testing.Entry__testing',
1712 str(e.exception))
Simon Glass2e1169f2018-07-06 10:27:19 -06001713
Simon Glass91710b32018-07-17 13:25:32 -06001714 def testEntryArgs(self):
1715 """Test passing arguments to entries from the command line"""
1716 entry_args = {
1717 'test-str-arg': 'test1',
1718 'test-int-arg': '456',
1719 }
Simon Glass511f6582018-10-01 12:22:30 -06001720 self._DoReadFileDtb('062_entry_args.dts', entry_args=entry_args)
Simon Glass91710b32018-07-17 13:25:32 -06001721 self.assertIn('image', control.images)
1722 entry = control.images['image'].GetEntries()['_testing']
1723 self.assertEqual('test0', entry.test_str_fdt)
1724 self.assertEqual('test1', entry.test_str_arg)
1725 self.assertEqual(123, entry.test_int_fdt)
1726 self.assertEqual(456, entry.test_int_arg)
1727
1728 def testEntryArgsMissing(self):
1729 """Test missing arguments and properties"""
1730 entry_args = {
1731 'test-int-arg': '456',
1732 }
Simon Glass511f6582018-10-01 12:22:30 -06001733 self._DoReadFileDtb('063_entry_args_missing.dts', entry_args=entry_args)
Simon Glass91710b32018-07-17 13:25:32 -06001734 entry = control.images['image'].GetEntries()['_testing']
1735 self.assertEqual('test0', entry.test_str_fdt)
1736 self.assertEqual(None, entry.test_str_arg)
1737 self.assertEqual(None, entry.test_int_fdt)
1738 self.assertEqual(456, entry.test_int_arg)
1739
1740 def testEntryArgsRequired(self):
1741 """Test missing arguments and properties"""
1742 entry_args = {
1743 'test-int-arg': '456',
1744 }
1745 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001746 self._DoReadFileDtb('064_entry_args_required.dts')
Simon Glass21db0ff2020-09-01 05:13:54 -06001747 self.assertIn("Node '/binman/_testing': "
1748 'Missing required properties/entry args: test-str-arg, '
1749 'test-int-fdt, test-int-arg',
Simon Glass91710b32018-07-17 13:25:32 -06001750 str(e.exception))
1751
1752 def testEntryArgsInvalidFormat(self):
1753 """Test that an invalid entry-argument format is detected"""
Simon Glassf46732a2019-07-08 14:25:29 -06001754 args = ['build', '-d', self.TestFile('064_entry_args_required.dts'),
1755 '-ano-value']
Simon Glass91710b32018-07-17 13:25:32 -06001756 with self.assertRaises(ValueError) as e:
1757 self._DoBinman(*args)
1758 self.assertIn("Invalid entry arguemnt 'no-value'", str(e.exception))
1759
1760 def testEntryArgsInvalidInteger(self):
1761 """Test that an invalid entry-argument integer is detected"""
1762 entry_args = {
1763 'test-int-arg': 'abc',
1764 }
1765 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001766 self._DoReadFileDtb('062_entry_args.dts', entry_args=entry_args)
Simon Glass91710b32018-07-17 13:25:32 -06001767 self.assertIn("Node '/binman/_testing': Cannot convert entry arg "
1768 "'test-int-arg' (value 'abc') to integer",
1769 str(e.exception))
1770
1771 def testEntryArgsInvalidDatatype(self):
1772 """Test that an invalid entry-argument datatype is detected
1773
1774 This test could be written in entry_test.py except that it needs
1775 access to control.entry_args, which seems more than that module should
1776 be able to see.
1777 """
1778 entry_args = {
1779 'test-bad-datatype-arg': '12',
1780 }
1781 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001782 self._DoReadFileDtb('065_entry_args_unknown_datatype.dts',
Simon Glass91710b32018-07-17 13:25:32 -06001783 entry_args=entry_args)
1784 self.assertIn('GetArg() internal error: Unknown data type ',
1785 str(e.exception))
1786
Simon Glass2ca52032018-07-17 13:25:33 -06001787 def testText(self):
1788 """Test for a text entry type"""
1789 entry_args = {
1790 'test-id': TEXT_DATA,
1791 'test-id2': TEXT_DATA2,
1792 'test-id3': TEXT_DATA3,
1793 }
Simon Glass511f6582018-10-01 12:22:30 -06001794 data, _, _, _ = self._DoReadFileDtb('066_text.dts',
Simon Glass2ca52032018-07-17 13:25:33 -06001795 entry_args=entry_args)
Simon Glass80025522022-01-29 14:14:04 -07001796 expected = (tools.to_bytes(TEXT_DATA) +
1797 tools.get_bytes(0, 8 - len(TEXT_DATA)) +
1798 tools.to_bytes(TEXT_DATA2) + tools.to_bytes(TEXT_DATA3) +
Simon Glass47f6a622019-07-08 13:18:40 -06001799 b'some text' + b'more text')
Simon Glass2ca52032018-07-17 13:25:33 -06001800 self.assertEqual(expected, data)
1801
Simon Glass969616c2018-07-17 13:25:36 -06001802 def testEntryDocs(self):
1803 """Test for creation of entry documentation"""
Simon Glass14d64e32025-04-29 07:21:59 -06001804 with terminal.capture() as (stdout, stderr):
Simon Glass220ff5f2020-08-05 13:27:46 -06001805 control.WriteEntryDocs(control.GetEntryModules())
Simon Glass969616c2018-07-17 13:25:36 -06001806 self.assertTrue(len(stdout.getvalue()) > 0)
1807
1808 def testEntryDocsMissing(self):
1809 """Test handling of missing entry documentation"""
1810 with self.assertRaises(ValueError) as e:
Simon Glass14d64e32025-04-29 07:21:59 -06001811 with terminal.capture() as (stdout, stderr):
Simon Glass220ff5f2020-08-05 13:27:46 -06001812 control.WriteEntryDocs(control.GetEntryModules(), 'u_boot')
Simon Glass969616c2018-07-17 13:25:36 -06001813 self.assertIn('Documentation is missing for modules: u_boot',
1814 str(e.exception))
1815
Simon Glass704784b2018-07-17 13:25:38 -06001816 def testFmap(self):
1817 """Basic test of generation of a flashrom fmap"""
Simon Glass511f6582018-10-01 12:22:30 -06001818 data = self._DoReadFile('067_fmap.dts')
Simon Glass704784b2018-07-17 13:25:38 -06001819 fhdr, fentries = fmap_util.DecodeFmap(data[32:])
Simon Glass80025522022-01-29 14:14:04 -07001820 expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) +
1821 U_BOOT_DATA + tools.get_bytes(ord('a'), 12))
Simon Glass704784b2018-07-17 13:25:38 -06001822 self.assertEqual(expected, data[:32])
Simon Glass303f62f2019-05-17 22:00:46 -06001823 self.assertEqual(b'__FMAP__', fhdr.signature)
Simon Glass704784b2018-07-17 13:25:38 -06001824 self.assertEqual(1, fhdr.ver_major)
1825 self.assertEqual(0, fhdr.ver_minor)
1826 self.assertEqual(0, fhdr.base)
Simon Glassb1d414c2021-04-03 11:05:10 +13001827 expect_size = fmap_util.FMAP_HEADER_LEN + fmap_util.FMAP_AREA_LEN * 5
Simon Glass82059c22021-04-03 11:05:09 +13001828 self.assertEqual(16 + 16 + expect_size, fhdr.image_size)
Simon Glass303f62f2019-05-17 22:00:46 -06001829 self.assertEqual(b'FMAP', fhdr.name)
Simon Glassb1d414c2021-04-03 11:05:10 +13001830 self.assertEqual(5, fhdr.nareas)
Simon Glass82059c22021-04-03 11:05:09 +13001831 fiter = iter(fentries)
Simon Glass704784b2018-07-17 13:25:38 -06001832
Simon Glass82059c22021-04-03 11:05:09 +13001833 fentry = next(fiter)
Simon Glassb1d414c2021-04-03 11:05:10 +13001834 self.assertEqual(b'SECTION0', fentry.name)
1835 self.assertEqual(0, fentry.offset)
1836 self.assertEqual(16, fentry.size)
Simon Glasscda991e2023-02-12 17:11:15 -07001837 self.assertEqual(fmap_util.FMAP_AREA_PRESERVE, fentry.flags)
Simon Glassb1d414c2021-04-03 11:05:10 +13001838
1839 fentry = next(fiter)
Simon Glass82059c22021-04-03 11:05:09 +13001840 self.assertEqual(b'RO_U_BOOT', fentry.name)
1841 self.assertEqual(0, fentry.offset)
1842 self.assertEqual(4, fentry.size)
1843 self.assertEqual(0, fentry.flags)
Simon Glass704784b2018-07-17 13:25:38 -06001844
Simon Glass82059c22021-04-03 11:05:09 +13001845 fentry = next(fiter)
Simon Glassb1d414c2021-04-03 11:05:10 +13001846 self.assertEqual(b'SECTION1', fentry.name)
1847 self.assertEqual(16, fentry.offset)
1848 self.assertEqual(16, fentry.size)
1849 self.assertEqual(0, fentry.flags)
1850
1851 fentry = next(fiter)
Simon Glass82059c22021-04-03 11:05:09 +13001852 self.assertEqual(b'RW_U_BOOT', fentry.name)
1853 self.assertEqual(16, fentry.offset)
1854 self.assertEqual(4, fentry.size)
1855 self.assertEqual(0, fentry.flags)
Simon Glass704784b2018-07-17 13:25:38 -06001856
Simon Glass82059c22021-04-03 11:05:09 +13001857 fentry = next(fiter)
1858 self.assertEqual(b'FMAP', fentry.name)
1859 self.assertEqual(32, fentry.offset)
1860 self.assertEqual(expect_size, fentry.size)
1861 self.assertEqual(0, fentry.flags)
Simon Glass704784b2018-07-17 13:25:38 -06001862
Simon Glassdb168d42018-07-17 13:25:39 -06001863 def testBlobNamedByArg(self):
1864 """Test we can add a blob with the filename coming from an entry arg"""
1865 entry_args = {
1866 'cros-ec-rw-path': 'ecrw.bin',
1867 }
Simon Glass21db0ff2020-09-01 05:13:54 -06001868 self._DoReadFileDtb('068_blob_named_by_arg.dts', entry_args=entry_args)
Simon Glassdb168d42018-07-17 13:25:39 -06001869
Simon Glass53f53992018-07-17 13:25:40 -06001870 def testFill(self):
1871 """Test for an fill entry type"""
Simon Glass511f6582018-10-01 12:22:30 -06001872 data = self._DoReadFile('069_fill.dts')
Simon Glass80025522022-01-29 14:14:04 -07001873 expected = tools.get_bytes(0xff, 8) + tools.get_bytes(0, 8)
Simon Glass53f53992018-07-17 13:25:40 -06001874 self.assertEqual(expected, data)
1875
1876 def testFillNoSize(self):
1877 """Test for an fill entry type with no size"""
1878 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001879 self._DoReadFile('070_fill_no_size.dts')
Simon Glass0cf5bce2022-08-13 11:40:44 -06001880 self.assertIn("'fill' entry is missing properties: size",
Simon Glass53f53992018-07-17 13:25:40 -06001881 str(e.exception))
1882
Simon Glassc1ae83c2018-07-17 13:25:44 -06001883 def _HandleGbbCommand(self, pipe_list):
1884 """Fake calls to the futility utility"""
Simon Glass9a1c7262023-02-22 12:14:49 -07001885 if 'futility' in pipe_list[0][0]:
Simon Glassc1ae83c2018-07-17 13:25:44 -06001886 fname = pipe_list[0][-1]
1887 # Append our GBB data to the file, which will happen every time the
1888 # futility command is called.
Simon Glass33486662019-05-14 15:53:42 -06001889 with open(fname, 'ab') as fd:
Simon Glassc1ae83c2018-07-17 13:25:44 -06001890 fd.write(GBB_DATA)
1891 return command.CommandResult()
1892
1893 def testGbb(self):
1894 """Test for the Chromium OS Google Binary Block"""
Simon Glass5dc22cf2025-02-03 09:26:42 -07001895 command.TEST_RESULT = self._HandleGbbCommand
Simon Glassc1ae83c2018-07-17 13:25:44 -06001896 entry_args = {
1897 'keydir': 'devkeys',
1898 'bmpblk': 'bmpblk.bin',
1899 }
Simon Glass511f6582018-10-01 12:22:30 -06001900 data, _, _, _ = self._DoReadFileDtb('071_gbb.dts', entry_args=entry_args)
Simon Glassc1ae83c2018-07-17 13:25:44 -06001901
1902 # Since futility
Simon Glass80025522022-01-29 14:14:04 -07001903 expected = (GBB_DATA + GBB_DATA + tools.get_bytes(0, 8) +
1904 tools.get_bytes(0, 0x2180 - 16))
Simon Glassc1ae83c2018-07-17 13:25:44 -06001905 self.assertEqual(expected, data)
1906
1907 def testGbbTooSmall(self):
1908 """Test for the Chromium OS Google Binary Block being large enough"""
1909 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001910 self._DoReadFileDtb('072_gbb_too_small.dts')
Simon Glassc1ae83c2018-07-17 13:25:44 -06001911 self.assertIn("Node '/binman/gbb': GBB is too small",
1912 str(e.exception))
1913
1914 def testGbbNoSize(self):
1915 """Test for the Chromium OS Google Binary Block having a size"""
1916 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001917 self._DoReadFileDtb('073_gbb_no_size.dts')
Simon Glassc1ae83c2018-07-17 13:25:44 -06001918 self.assertIn("Node '/binman/gbb': GBB must have a fixed size",
1919 str(e.exception))
1920
Simon Glass66152ce2022-01-09 20:14:09 -07001921 def testGbbMissing(self):
1922 """Test that binman still produces an image if futility is missing"""
1923 entry_args = {
1924 'keydir': 'devkeys',
1925 }
Simon Glass14d64e32025-04-29 07:21:59 -06001926 with terminal.capture() as (_, stderr):
Simon Glass66152ce2022-01-09 20:14:09 -07001927 self._DoTestFile('071_gbb.dts', force_missing_bintools='futility',
1928 entry_args=entry_args)
1929 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07001930 self.assertRegex(err, "Image 'image'.*missing bintools.*: futility")
Simon Glass66152ce2022-01-09 20:14:09 -07001931
Simon Glass5c350162018-07-17 13:25:47 -06001932 def _HandleVblockCommand(self, pipe_list):
Simon Glass220c6222021-01-06 21:35:17 -07001933 """Fake calls to the futility utility
1934
1935 The expected pipe is:
1936
1937 [('futility', 'vbutil_firmware', '--vblock',
1938 'vblock.vblock', '--keyblock', 'devkeys/firmware.keyblock',
1939 '--signprivate', 'devkeys/firmware_data_key.vbprivk',
1940 '--version', '1', '--fv', 'input.vblock', '--kernelkey',
1941 'devkeys/kernel_subkey.vbpubk', '--flags', '1')]
1942
1943 This writes to the output file (here, 'vblock.vblock'). If
1944 self._hash_data is False, it writes VBLOCK_DATA, else it writes a hash
1945 of the input data (here, 'input.vblock').
1946 """
Simon Glass9a1c7262023-02-22 12:14:49 -07001947 if 'futility' in pipe_list[0][0]:
Simon Glass5c350162018-07-17 13:25:47 -06001948 fname = pipe_list[0][3]
Simon Glass639505b2018-09-14 04:57:11 -06001949 with open(fname, 'wb') as fd:
Simon Glass220c6222021-01-06 21:35:17 -07001950 if self._hash_data:
1951 infile = pipe_list[0][11]
1952 m = hashlib.sha256()
Simon Glass80025522022-01-29 14:14:04 -07001953 data = tools.read_file(infile)
Simon Glass220c6222021-01-06 21:35:17 -07001954 m.update(data)
1955 fd.write(m.digest())
1956 else:
1957 fd.write(VBLOCK_DATA)
1958
Simon Glass5c350162018-07-17 13:25:47 -06001959 return command.CommandResult()
1960
1961 def testVblock(self):
1962 """Test for the Chromium OS Verified Boot Block"""
Simon Glass220c6222021-01-06 21:35:17 -07001963 self._hash_data = False
Simon Glass5dc22cf2025-02-03 09:26:42 -07001964 command.TEST_RESULT = self._HandleVblockCommand
Simon Glass5c350162018-07-17 13:25:47 -06001965 entry_args = {
1966 'keydir': 'devkeys',
1967 }
Simon Glass511f6582018-10-01 12:22:30 -06001968 data, _, _, _ = self._DoReadFileDtb('074_vblock.dts',
Simon Glass5c350162018-07-17 13:25:47 -06001969 entry_args=entry_args)
1970 expected = U_BOOT_DATA + VBLOCK_DATA + U_BOOT_DTB_DATA
1971 self.assertEqual(expected, data)
1972
1973 def testVblockNoContent(self):
1974 """Test we detect a vblock which has no content to sign"""
1975 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001976 self._DoReadFile('075_vblock_no_content.dts')
Simon Glasse1915782021-03-21 18:24:31 +13001977 self.assertIn("Node '/binman/vblock': Collection must have a 'content' "
Simon Glass5c350162018-07-17 13:25:47 -06001978 'property', str(e.exception))
1979
1980 def testVblockBadPhandle(self):
1981 """Test that we detect a vblock with an invalid phandle in contents"""
1982 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001983 self._DoReadFile('076_vblock_bad_phandle.dts')
Simon Glass5c350162018-07-17 13:25:47 -06001984 self.assertIn("Node '/binman/vblock': Cannot find node for phandle "
1985 '1000', str(e.exception))
1986
1987 def testVblockBadEntry(self):
1988 """Test that we detect an entry that points to a non-entry"""
1989 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001990 self._DoReadFile('077_vblock_bad_entry.dts')
Simon Glass5c350162018-07-17 13:25:47 -06001991 self.assertIn("Node '/binman/vblock': Cannot find entry for node "
1992 "'other'", str(e.exception))
1993
Simon Glass220c6222021-01-06 21:35:17 -07001994 def testVblockContent(self):
1995 """Test that the vblock signs the right data"""
1996 self._hash_data = True
Simon Glass5dc22cf2025-02-03 09:26:42 -07001997 command.TEST_RESULT = self._HandleVblockCommand
Simon Glass220c6222021-01-06 21:35:17 -07001998 entry_args = {
1999 'keydir': 'devkeys',
2000 }
2001 data = self._DoReadFileDtb(
2002 '189_vblock_content.dts', use_real_dtb=True, update_dtb=True,
2003 entry_args=entry_args)[0]
2004 hashlen = 32 # SHA256 hash is 32 bytes
2005 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
2006 hashval = data[-hashlen:]
2007 dtb = data[len(U_BOOT_DATA):-hashlen]
2008
2009 expected_data = U_BOOT_DATA + dtb
2010
2011 # The hashval should be a hash of the dtb
2012 m = hashlib.sha256()
2013 m.update(expected_data)
2014 expected_hashval = m.digest()
2015 self.assertEqual(expected_hashval, hashval)
2016
Simon Glass66152ce2022-01-09 20:14:09 -07002017 def testVblockMissing(self):
2018 """Test that binman still produces an image if futility is missing"""
2019 entry_args = {
2020 'keydir': 'devkeys',
2021 }
Simon Glass14d64e32025-04-29 07:21:59 -06002022 with terminal.capture() as (_, stderr):
Simon Glass66152ce2022-01-09 20:14:09 -07002023 self._DoTestFile('074_vblock.dts',
2024 force_missing_bintools='futility',
2025 entry_args=entry_args)
2026 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07002027 self.assertRegex(err, "Image 'image'.*missing bintools.*: futility")
Simon Glass66152ce2022-01-09 20:14:09 -07002028
Simon Glass8425a1f2018-07-17 13:25:48 -06002029 def testTpl(self):
Simon Glass3eb5b202019-08-24 07:23:00 -06002030 """Test that an image with TPL and its device tree can be created"""
Simon Glass8425a1f2018-07-17 13:25:48 -06002031 # ELF file with a '__bss_size' symbol
Simon Glass3eb5b202019-08-24 07:23:00 -06002032 self._SetupTplElf()
Simon Glass511f6582018-10-01 12:22:30 -06002033 data = self._DoReadFile('078_u_boot_tpl.dts')
Simon Glass8425a1f2018-07-17 13:25:48 -06002034 self.assertEqual(U_BOOT_TPL_DATA + U_BOOT_TPL_DTB_DATA, data)
2035
Simon Glass24b97442018-07-17 13:25:51 -06002036 def testUsesPos(self):
2037 """Test that the 'pos' property cannot be used anymore"""
2038 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002039 data = self._DoReadFile('079_uses_pos.dts')
Simon Glass24b97442018-07-17 13:25:51 -06002040 self.assertIn("Node '/binman/u-boot': Please use 'offset' instead of "
2041 "'pos'", str(e.exception))
2042
Simon Glass274bf092018-09-14 04:57:08 -06002043 def testFillZero(self):
2044 """Test for an fill entry type with a size of 0"""
Simon Glass511f6582018-10-01 12:22:30 -06002045 data = self._DoReadFile('080_fill_empty.dts')
Simon Glass80025522022-01-29 14:14:04 -07002046 self.assertEqual(tools.get_bytes(0, 16), data)
Simon Glass274bf092018-09-14 04:57:08 -06002047
Simon Glass267de432018-09-14 04:57:09 -06002048 def testTextMissing(self):
2049 """Test for a text entry type where there is no text"""
2050 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002051 self._DoReadFileDtb('066_text.dts',)
Simon Glass267de432018-09-14 04:57:09 -06002052 self.assertIn("Node '/binman/text': No value provided for text label "
2053 "'test-id'", str(e.exception))
2054
Simon Glassed40e962018-09-14 04:57:10 -06002055 def testPackStart16Tpl(self):
2056 """Test that an image with an x86 start16 TPL region can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06002057 data = self._DoReadFile('081_x86_start16_tpl.dts')
Simon Glassed40e962018-09-14 04:57:10 -06002058 self.assertEqual(X86_START16_TPL_DATA, data[:len(X86_START16_TPL_DATA)])
2059
Simon Glass3b376c32018-09-14 04:57:12 -06002060 def testSelectImage(self):
2061 """Test that we can select which images to build"""
Simon Glassb4595d82019-04-25 21:58:34 -06002062 expected = 'Skipping images: image1'
2063
2064 # We should only get the expected message in verbose mode
Simon Glass8a50b4a2019-07-08 13:18:48 -06002065 for verbosity in (0, 2):
Simon Glass14d64e32025-04-29 07:21:59 -06002066 with terminal.capture() as (stdout, stderr):
Simon Glassb4595d82019-04-25 21:58:34 -06002067 retcode = self._DoTestFile('006_dual_image.dts',
2068 verbosity=verbosity,
2069 images=['image2'])
2070 self.assertEqual(0, retcode)
2071 if verbosity:
2072 self.assertIn(expected, stdout.getvalue())
2073 else:
2074 self.assertNotIn(expected, stdout.getvalue())
Simon Glass3b376c32018-09-14 04:57:12 -06002075
Simon Glass80025522022-01-29 14:14:04 -07002076 self.assertFalse(os.path.exists(tools.get_output_filename('image1.bin')))
2077 self.assertTrue(os.path.exists(tools.get_output_filename('image2.bin')))
Simon Glassb3d6fc72019-07-20 12:24:10 -06002078 self._CleanupOutputDir()
Simon Glass3b376c32018-09-14 04:57:12 -06002079
Simon Glasse219aa42018-09-14 04:57:24 -06002080 def testUpdateFdtAll(self):
2081 """Test that all device trees are updated with offset/size info"""
Marek Vasutf7413f02023-07-18 07:23:58 -06002082 self._SetupSplElf()
2083 self._SetupTplElf()
Simon Glass5b4bce32019-07-08 14:25:26 -06002084 data = self._DoReadFileRealDtb('082_fdt_update_all.dts')
Simon Glasse219aa42018-09-14 04:57:24 -06002085
2086 base_expected = {
Simon Glasse219aa42018-09-14 04:57:24 -06002087 'offset': 0,
Simon Glass56d05412022-02-28 07:16:54 -07002088 'image-pos': 0,
2089 'size': 2320,
Simon Glasse219aa42018-09-14 04:57:24 -06002090 'section:offset': 0,
Simon Glass56d05412022-02-28 07:16:54 -07002091 'section:image-pos': 0,
2092 'section:size': 565,
2093 'section/u-boot-dtb:offset': 0,
2094 'section/u-boot-dtb:image-pos': 0,
2095 'section/u-boot-dtb:size': 565,
2096 'u-boot-spl-dtb:offset': 565,
2097 'u-boot-spl-dtb:image-pos': 565,
2098 'u-boot-spl-dtb:size': 585,
2099 'u-boot-tpl-dtb:offset': 1150,
2100 'u-boot-tpl-dtb:image-pos': 1150,
2101 'u-boot-tpl-dtb:size': 585,
2102 'u-boot-vpl-dtb:image-pos': 1735,
2103 'u-boot-vpl-dtb:offset': 1735,
2104 'u-boot-vpl-dtb:size': 585,
Simon Glasse219aa42018-09-14 04:57:24 -06002105 }
2106
2107 # We expect three device-tree files in the output, one after the other.
2108 # Read them in sequence. We look for an 'spl' property in the SPL tree,
2109 # and 'tpl' in the TPL tree, to make sure they are distinct from the
2110 # main U-Boot tree. All three should have the same postions and offset.
2111 start = 0
Simon Glass56d05412022-02-28 07:16:54 -07002112 self.maxDiff = None
2113 for item in ['', 'spl', 'tpl', 'vpl']:
Simon Glasse219aa42018-09-14 04:57:24 -06002114 dtb = fdt.Fdt.FromData(data[start:])
2115 dtb.Scan()
Simon Glassfb30e292019-07-20 12:23:51 -06002116 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS +
Simon Glass56d05412022-02-28 07:16:54 -07002117 ['spl', 'tpl', 'vpl'])
Simon Glasse219aa42018-09-14 04:57:24 -06002118 expected = dict(base_expected)
2119 if item:
2120 expected[item] = 0
2121 self.assertEqual(expected, props)
2122 start += dtb._fdt_obj.totalsize()
2123
2124 def testUpdateFdtOutput(self):
2125 """Test that output DTB files are updated"""
2126 try:
Simon Glass511f6582018-10-01 12:22:30 -06002127 data, dtb_data, _, _ = self._DoReadFileDtb('082_fdt_update_all.dts',
Simon Glasse219aa42018-09-14 04:57:24 -06002128 use_real_dtb=True, update_dtb=True, reset_dtbs=False)
2129
2130 # Unfortunately, compiling a source file always results in a file
2131 # called source.dtb (see fdt_util.EnsureCompiled()). The test
Simon Glass511f6582018-10-01 12:22:30 -06002132 # source file (e.g. test/075_fdt_update_all.dts) thus does not enter
Simon Glasse219aa42018-09-14 04:57:24 -06002133 # binman as a file called u-boot.dtb. To fix this, copy the file
2134 # over to the expected place.
Simon Glasse219aa42018-09-14 04:57:24 -06002135 start = 0
2136 for fname in ['u-boot.dtb.out', 'spl/u-boot-spl.dtb.out',
Simon Glass56d05412022-02-28 07:16:54 -07002137 'tpl/u-boot-tpl.dtb.out', 'vpl/u-boot-vpl.dtb.out']:
Simon Glasse219aa42018-09-14 04:57:24 -06002138 dtb = fdt.Fdt.FromData(data[start:])
2139 size = dtb._fdt_obj.totalsize()
Simon Glass80025522022-01-29 14:14:04 -07002140 pathname = tools.get_output_filename(os.path.split(fname)[1])
2141 outdata = tools.read_file(pathname)
Simon Glasse219aa42018-09-14 04:57:24 -06002142 name = os.path.split(fname)[0]
2143
2144 if name:
Simon Glass56d05412022-02-28 07:16:54 -07002145 orig_indata = self._GetDtbContentsForSpls(dtb_data, name)
Simon Glasse219aa42018-09-14 04:57:24 -06002146 else:
2147 orig_indata = dtb_data
2148 self.assertNotEqual(outdata, orig_indata,
2149 "Expected output file '%s' be updated" % pathname)
2150 self.assertEqual(outdata, data[start:start + size],
2151 "Expected output file '%s' to match output image" %
2152 pathname)
2153 start += size
2154 finally:
2155 self._ResetDtbs()
2156
Simon Glass7ba33592018-09-14 04:57:26 -06002157 def _decompress(self, data):
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +02002158 bintool = self.comp_bintools['lz4']
2159 return bintool.decompress(data)
Simon Glass7ba33592018-09-14 04:57:26 -06002160
2161 def testCompress(self):
2162 """Test compression of blobs"""
Simon Glass1de34482019-07-08 13:18:53 -06002163 self._CheckLz4()
Simon Glass511f6582018-10-01 12:22:30 -06002164 data, _, _, out_dtb_fname = self._DoReadFileDtb('083_compress.dts',
Simon Glass7ba33592018-09-14 04:57:26 -06002165 use_real_dtb=True, update_dtb=True)
2166 dtb = fdt.Fdt(out_dtb_fname)
2167 dtb.Scan()
2168 props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
2169 orig = self._decompress(data)
Brandon Maiera657bc62024-06-04 16:16:05 +00002170 self.assertEqual(COMPRESS_DATA, orig)
Simon Glass789b34402020-10-26 17:40:15 -06002171
2172 # Do a sanity check on various fields
2173 image = control.images['image']
2174 entries = image.GetEntries()
2175 self.assertEqual(1, len(entries))
2176
2177 entry = entries['blob']
2178 self.assertEqual(COMPRESS_DATA, entry.uncomp_data)
2179 self.assertEqual(len(COMPRESS_DATA), entry.uncomp_size)
2180 orig = self._decompress(entry.data)
2181 self.assertEqual(orig, entry.uncomp_data)
2182
Simon Glass72eeff12020-10-26 17:40:16 -06002183 self.assertEqual(image.data, entry.data)
2184
Simon Glass7ba33592018-09-14 04:57:26 -06002185 expected = {
2186 'blob:uncomp-size': len(COMPRESS_DATA),
2187 'blob:size': len(data),
2188 'size': len(data),
2189 }
2190 self.assertEqual(expected, props)
2191
Simon Glassac6328c2018-09-14 04:57:28 -06002192 def testFiles(self):
2193 """Test bringing in multiple files"""
Simon Glass511f6582018-10-01 12:22:30 -06002194 data = self._DoReadFile('084_files.dts')
Simon Glassac6328c2018-09-14 04:57:28 -06002195 self.assertEqual(FILES_DATA, data)
2196
2197 def testFilesCompress(self):
2198 """Test bringing in multiple files and compressing them"""
Simon Glass1de34482019-07-08 13:18:53 -06002199 self._CheckLz4()
Simon Glass511f6582018-10-01 12:22:30 -06002200 data = self._DoReadFile('085_files_compress.dts')
Simon Glassac6328c2018-09-14 04:57:28 -06002201
2202 image = control.images['image']
2203 entries = image.GetEntries()
2204 files = entries['files']
Simon Glass39dd2152019-07-08 14:25:47 -06002205 entries = files._entries
Simon Glassac6328c2018-09-14 04:57:28 -06002206
Simon Glass303f62f2019-05-17 22:00:46 -06002207 orig = b''
Simon Glassac6328c2018-09-14 04:57:28 -06002208 for i in range(1, 3):
2209 key = '%d.dat' % i
2210 start = entries[key].image_pos
2211 len = entries[key].size
2212 chunk = data[start:start + len]
2213 orig += self._decompress(chunk)
2214
2215 self.assertEqual(FILES_DATA, orig)
2216
2217 def testFilesMissing(self):
2218 """Test missing files"""
2219 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002220 data = self._DoReadFile('086_files_none.dts')
Simon Glassac6328c2018-09-14 04:57:28 -06002221 self.assertIn("Node '/binman/files': Pattern \'files/*.none\' matched "
2222 'no files', str(e.exception))
2223
2224 def testFilesNoPattern(self):
2225 """Test missing files"""
2226 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002227 data = self._DoReadFile('087_files_no_pattern.dts')
Simon Glassac6328c2018-09-14 04:57:28 -06002228 self.assertIn("Node '/binman/files': Missing 'pattern' property",
2229 str(e.exception))
2230
Simon Glassdd156a42022-03-05 20:18:59 -07002231 def testExtendSize(self):
2232 """Test an extending entry"""
2233 data, _, map_data, _ = self._DoReadFileDtb('088_extend_size.dts',
Simon Glassfa79a812018-09-14 04:57:29 -06002234 map=True)
Simon Glass80025522022-01-29 14:14:04 -07002235 expect = (tools.get_bytes(ord('a'), 8) + U_BOOT_DATA +
2236 MRC_DATA + tools.get_bytes(ord('b'), 1) + U_BOOT_DATA +
2237 tools.get_bytes(ord('c'), 8) + U_BOOT_DATA +
2238 tools.get_bytes(ord('d'), 8))
Simon Glassfa79a812018-09-14 04:57:29 -06002239 self.assertEqual(expect, data)
2240 self.assertEqual('''ImagePos Offset Size Name
Simon Glass49cd2b32023-02-07 14:34:18 -0700224100000000 00000000 00000028 image
Simon Glassfa79a812018-09-14 04:57:29 -0600224200000000 00000000 00000008 fill
224300000008 00000008 00000004 u-boot
22440000000c 0000000c 00000004 section
22450000000c 00000000 00000003 intel-mrc
224600000010 00000010 00000004 u-boot2
224700000014 00000014 0000000c section2
224800000014 00000000 00000008 fill
22490000001c 00000008 00000004 u-boot
225000000020 00000020 00000008 fill2
2251''', map_data)
2252
Simon Glassdd156a42022-03-05 20:18:59 -07002253 def testExtendSizeBad(self):
2254 """Test an extending entry which fails to provide contents"""
Simon Glass14d64e32025-04-29 07:21:59 -06002255 with terminal.capture() as (stdout, stderr):
Simon Glasscd817d52018-09-14 04:57:36 -06002256 with self.assertRaises(ValueError) as e:
Simon Glassdd156a42022-03-05 20:18:59 -07002257 self._DoReadFileDtb('089_extend_size_bad.dts', map=True)
Simon Glassfa79a812018-09-14 04:57:29 -06002258 self.assertIn("Node '/binman/_testing': Cannot obtain contents when "
2259 'expanding entry', str(e.exception))
2260
Simon Glassae7cf032018-09-14 04:57:31 -06002261 def testHash(self):
2262 """Test hashing of the contents of an entry"""
Simon Glass511f6582018-10-01 12:22:30 -06002263 _, _, _, out_dtb_fname = self._DoReadFileDtb('090_hash.dts',
Simon Glassae7cf032018-09-14 04:57:31 -06002264 use_real_dtb=True, update_dtb=True)
2265 dtb = fdt.Fdt(out_dtb_fname)
2266 dtb.Scan()
2267 hash_node = dtb.GetNode('/binman/u-boot/hash').props['value']
2268 m = hashlib.sha256()
2269 m.update(U_BOOT_DATA)
Simon Glass303f62f2019-05-17 22:00:46 -06002270 self.assertEqual(m.digest(), b''.join(hash_node.value))
Simon Glassae7cf032018-09-14 04:57:31 -06002271
2272 def testHashNoAlgo(self):
2273 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002274 self._DoReadFileDtb('091_hash_no_algo.dts', update_dtb=True)
Simon Glassae7cf032018-09-14 04:57:31 -06002275 self.assertIn("Node \'/binman/u-boot\': Missing \'algo\' property for "
2276 'hash node', str(e.exception))
2277
2278 def testHashBadAlgo(self):
2279 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002280 self._DoReadFileDtb('092_hash_bad_algo.dts', update_dtb=True)
Simon Glass64af7c22022-02-08 10:59:44 -07002281 self.assertIn("Node '/binman/u-boot': Unknown hash algorithm 'invalid'",
Simon Glassae7cf032018-09-14 04:57:31 -06002282 str(e.exception))
2283
2284 def testHashSection(self):
2285 """Test hashing of the contents of an entry"""
Simon Glass511f6582018-10-01 12:22:30 -06002286 _, _, _, out_dtb_fname = self._DoReadFileDtb('099_hash_section.dts',
Simon Glassae7cf032018-09-14 04:57:31 -06002287 use_real_dtb=True, update_dtb=True)
2288 dtb = fdt.Fdt(out_dtb_fname)
2289 dtb.Scan()
2290 hash_node = dtb.GetNode('/binman/section/hash').props['value']
2291 m = hashlib.sha256()
2292 m.update(U_BOOT_DATA)
Simon Glass80025522022-01-29 14:14:04 -07002293 m.update(tools.get_bytes(ord('a'), 16))
Simon Glass303f62f2019-05-17 22:00:46 -06002294 self.assertEqual(m.digest(), b''.join(hash_node.value))
Simon Glassae7cf032018-09-14 04:57:31 -06002295
Simon Glass3fb4f422018-09-14 04:57:32 -06002296 def testPackUBootTplMicrocode(self):
2297 """Test that x86 microcode can be handled correctly in TPL
2298
2299 We expect to see the following in the image, in order:
2300 u-boot-tpl-nodtb.bin with a microcode pointer inserted at the correct
2301 place
2302 u-boot-tpl.dtb with the microcode removed
2303 the microcode
2304 """
Simon Glass3eb5b202019-08-24 07:23:00 -06002305 self._SetupTplElf('u_boot_ucode_ptr')
Simon Glass511f6582018-10-01 12:22:30 -06002306 first, pos_and_size = self._RunMicrocodeTest('093_x86_tpl_ucode.dts',
Simon Glass3fb4f422018-09-14 04:57:32 -06002307 U_BOOT_TPL_NODTB_DATA)
Simon Glass303f62f2019-05-17 22:00:46 -06002308 self.assertEqual(b'tplnodtb with microc' + pos_and_size +
2309 b'ter somewhere in here', first)
Simon Glass3fb4f422018-09-14 04:57:32 -06002310
Simon Glassc64aea52018-09-14 04:57:34 -06002311 def testFmapX86(self):
2312 """Basic test of generation of a flashrom fmap"""
Simon Glass511f6582018-10-01 12:22:30 -06002313 data = self._DoReadFile('094_fmap_x86.dts')
Simon Glassc64aea52018-09-14 04:57:34 -06002314 fhdr, fentries = fmap_util.DecodeFmap(data[32:])
Simon Glass80025522022-01-29 14:14:04 -07002315 expected = U_BOOT_DATA + MRC_DATA + tools.get_bytes(ord('a'), 32 - 7)
Simon Glassc64aea52018-09-14 04:57:34 -06002316 self.assertEqual(expected, data[:32])
2317 fhdr, fentries = fmap_util.DecodeFmap(data[32:])
2318
2319 self.assertEqual(0x100, fhdr.image_size)
Simon Glassed836ac2025-02-26 09:26:17 -07002320 base = (1 << 32) - 0x100
Simon Glassc64aea52018-09-14 04:57:34 -06002321
Simon Glassed836ac2025-02-26 09:26:17 -07002322 self.assertEqual(base, fentries[0].offset)
Simon Glassc64aea52018-09-14 04:57:34 -06002323 self.assertEqual(4, fentries[0].size)
Simon Glass303f62f2019-05-17 22:00:46 -06002324 self.assertEqual(b'U_BOOT', fentries[0].name)
Simon Glassc64aea52018-09-14 04:57:34 -06002325
Simon Glassed836ac2025-02-26 09:26:17 -07002326 self.assertEqual(base + 4, fentries[1].offset)
Simon Glassc64aea52018-09-14 04:57:34 -06002327 self.assertEqual(3, fentries[1].size)
Simon Glass303f62f2019-05-17 22:00:46 -06002328 self.assertEqual(b'INTEL_MRC', fentries[1].name)
Simon Glassc64aea52018-09-14 04:57:34 -06002329
Simon Glassed836ac2025-02-26 09:26:17 -07002330 self.assertEqual(base + 32, fentries[2].offset)
Simon Glassc64aea52018-09-14 04:57:34 -06002331 self.assertEqual(fmap_util.FMAP_HEADER_LEN +
2332 fmap_util.FMAP_AREA_LEN * 3, fentries[2].size)
Simon Glass303f62f2019-05-17 22:00:46 -06002333 self.assertEqual(b'FMAP', fentries[2].name)
Simon Glassc64aea52018-09-14 04:57:34 -06002334
2335 def testFmapX86Section(self):
2336 """Basic test of generation of a flashrom fmap"""
Simon Glass511f6582018-10-01 12:22:30 -06002337 data = self._DoReadFile('095_fmap_x86_section.dts')
Simon Glass80025522022-01-29 14:14:04 -07002338 expected = U_BOOT_DATA + MRC_DATA + tools.get_bytes(ord('b'), 32 - 7)
Simon Glassc64aea52018-09-14 04:57:34 -06002339 self.assertEqual(expected, data[:32])
2340 fhdr, fentries = fmap_util.DecodeFmap(data[36:])
2341
Simon Glassb1d414c2021-04-03 11:05:10 +13002342 self.assertEqual(0x180, fhdr.image_size)
Simon Glassed836ac2025-02-26 09:26:17 -07002343 base = (1 << 32) - 0x180
Simon Glassb1d414c2021-04-03 11:05:10 +13002344 expect_size = fmap_util.FMAP_HEADER_LEN + fmap_util.FMAP_AREA_LEN * 4
Simon Glass82059c22021-04-03 11:05:09 +13002345 fiter = iter(fentries)
Simon Glassc64aea52018-09-14 04:57:34 -06002346
Simon Glass82059c22021-04-03 11:05:09 +13002347 fentry = next(fiter)
2348 self.assertEqual(b'U_BOOT', fentry.name)
Simon Glassed836ac2025-02-26 09:26:17 -07002349 self.assertEqual(base, fentry.offset)
Simon Glass82059c22021-04-03 11:05:09 +13002350 self.assertEqual(4, fentry.size)
Simon Glassc64aea52018-09-14 04:57:34 -06002351
Simon Glass82059c22021-04-03 11:05:09 +13002352 fentry = next(fiter)
Simon Glassb1d414c2021-04-03 11:05:10 +13002353 self.assertEqual(b'SECTION', fentry.name)
Simon Glassed836ac2025-02-26 09:26:17 -07002354 self.assertEqual(base + 4, fentry.offset)
Simon Glassb1d414c2021-04-03 11:05:10 +13002355 self.assertEqual(0x20 + expect_size, fentry.size)
2356
2357 fentry = next(fiter)
Simon Glass82059c22021-04-03 11:05:09 +13002358 self.assertEqual(b'INTEL_MRC', fentry.name)
Simon Glassed836ac2025-02-26 09:26:17 -07002359 self.assertEqual(base + 4, fentry.offset)
Simon Glass82059c22021-04-03 11:05:09 +13002360 self.assertEqual(3, fentry.size)
Simon Glassc64aea52018-09-14 04:57:34 -06002361
Simon Glass82059c22021-04-03 11:05:09 +13002362 fentry = next(fiter)
2363 self.assertEqual(b'FMAP', fentry.name)
Simon Glassed836ac2025-02-26 09:26:17 -07002364 self.assertEqual(base + 36, fentry.offset)
Simon Glass82059c22021-04-03 11:05:09 +13002365 self.assertEqual(expect_size, fentry.size)
Simon Glassc64aea52018-09-14 04:57:34 -06002366
Simon Glassb1714232018-09-14 04:57:35 -06002367 def testElf(self):
2368 """Basic test of ELF entries"""
Simon Glass7057d022018-10-01 21:12:47 -06002369 self._SetupSplElf()
Simon Glass3eb5b202019-08-24 07:23:00 -06002370 self._SetupTplElf()
Simon Glassf6290892019-08-24 07:22:53 -06002371 with open(self.ElfTestFile('bss_data'), 'rb') as fd:
Simon Glassb1714232018-09-14 04:57:35 -06002372 TestFunctional._MakeInputFile('-boot', fd.read())
Simon Glass511f6582018-10-01 12:22:30 -06002373 data = self._DoReadFile('096_elf.dts')
Simon Glassb1714232018-09-14 04:57:35 -06002374
Simon Glass0d673792019-07-08 13:18:25 -06002375 def testElfStrip(self):
Simon Glassb1714232018-09-14 04:57:35 -06002376 """Basic test of ELF entries"""
Simon Glass7057d022018-10-01 21:12:47 -06002377 self._SetupSplElf()
Simon Glassf6290892019-08-24 07:22:53 -06002378 with open(self.ElfTestFile('bss_data'), 'rb') as fd:
Simon Glassb1714232018-09-14 04:57:35 -06002379 TestFunctional._MakeInputFile('-boot', fd.read())
Simon Glass511f6582018-10-01 12:22:30 -06002380 data = self._DoReadFile('097_elf_strip.dts')
Simon Glassb1714232018-09-14 04:57:35 -06002381
Simon Glasscd817d52018-09-14 04:57:36 -06002382 def testPackOverlapMap(self):
2383 """Test that overlapping regions are detected"""
Simon Glass14d64e32025-04-29 07:21:59 -06002384 with terminal.capture() as (stdout, stderr):
Simon Glasscd817d52018-09-14 04:57:36 -06002385 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002386 self._DoTestFile('014_pack_overlap.dts', map=True)
Simon Glass80025522022-01-29 14:14:04 -07002387 map_fname = tools.get_output_filename('image.map')
Simon Glasscd817d52018-09-14 04:57:36 -06002388 self.assertEqual("Wrote map file '%s' to show errors\n" % map_fname,
2389 stdout.getvalue())
2390
2391 # We should not get an inmage, but there should be a map file
Simon Glass80025522022-01-29 14:14:04 -07002392 self.assertFalse(os.path.exists(tools.get_output_filename('image.bin')))
Simon Glasscd817d52018-09-14 04:57:36 -06002393 self.assertTrue(os.path.exists(map_fname))
Simon Glass80025522022-01-29 14:14:04 -07002394 map_data = tools.read_file(map_fname, binary=False)
Simon Glasscd817d52018-09-14 04:57:36 -06002395 self.assertEqual('''ImagePos Offset Size Name
Simon Glass49cd2b32023-02-07 14:34:18 -07002396<none> 00000000 00000008 image
Simon Glasscd817d52018-09-14 04:57:36 -06002397<none> 00000000 00000004 u-boot
2398<none> 00000003 00000004 u-boot-align
2399''', map_data)
2400
Simon Glass0d673792019-07-08 13:18:25 -06002401 def testPackRefCode(self):
Simon Glass41902e42018-10-01 12:22:31 -06002402 """Test that an image with an Intel Reference code binary works"""
2403 data = self._DoReadFile('100_intel_refcode.dts')
2404 self.assertEqual(REFCODE_DATA, data[:len(REFCODE_DATA)])
2405
Simon Glasseb023b32019-04-25 21:58:39 -06002406 def testSectionOffset(self):
2407 """Tests use of a section with an offset"""
2408 data, _, map_data, _ = self._DoReadFileDtb('101_sections_offset.dts',
2409 map=True)
2410 self.assertEqual('''ImagePos Offset Size Name
Simon Glass49cd2b32023-02-07 14:34:18 -0700241100000000 00000000 00000038 image
Simon Glasseb023b32019-04-25 21:58:39 -0600241200000004 00000004 00000010 section@0
241300000004 00000000 00000004 u-boot
241400000018 00000018 00000010 section@1
241500000018 00000000 00000004 u-boot
24160000002c 0000002c 00000004 section@2
24170000002c 00000000 00000004 u-boot
2418''', map_data)
2419 self.assertEqual(data,
Simon Glass80025522022-01-29 14:14:04 -07002420 tools.get_bytes(0x26, 4) + U_BOOT_DATA +
2421 tools.get_bytes(0x21, 12) +
2422 tools.get_bytes(0x26, 4) + U_BOOT_DATA +
2423 tools.get_bytes(0x61, 12) +
2424 tools.get_bytes(0x26, 4) + U_BOOT_DATA +
2425 tools.get_bytes(0x26, 8))
Simon Glasseb023b32019-04-25 21:58:39 -06002426
Simon Glass1de34482019-07-08 13:18:53 -06002427 def testCbfsRaw(self):
2428 """Test base handling of a Coreboot Filesystem (CBFS)
2429
2430 The exact contents of the CBFS is verified by similar tests in
2431 cbfs_util_test.py. The tests here merely check that the files added to
2432 the CBFS can be found in the final image.
2433 """
2434 data = self._DoReadFile('102_cbfs_raw.dts')
2435 size = 0xb0
2436
2437 cbfs = cbfs_util.CbfsReader(data)
2438 self.assertEqual(size, cbfs.rom_size)
2439
2440 self.assertIn('u-boot-dtb', cbfs.files)
2441 cfile = cbfs.files['u-boot-dtb']
2442 self.assertEqual(U_BOOT_DTB_DATA, cfile.data)
2443
2444 def testCbfsArch(self):
2445 """Test on non-x86 architecture"""
2446 data = self._DoReadFile('103_cbfs_raw_ppc.dts')
2447 size = 0x100
2448
2449 cbfs = cbfs_util.CbfsReader(data)
2450 self.assertEqual(size, cbfs.rom_size)
2451
2452 self.assertIn('u-boot-dtb', cbfs.files)
2453 cfile = cbfs.files['u-boot-dtb']
2454 self.assertEqual(U_BOOT_DTB_DATA, cfile.data)
2455
2456 def testCbfsStage(self):
2457 """Tests handling of a Coreboot Filesystem (CBFS)"""
2458 if not elf.ELF_TOOLS:
2459 self.skipTest('Python elftools not available')
2460 elf_fname = os.path.join(self._indir, 'cbfs-stage.elf')
2461 elf.MakeElf(elf_fname, U_BOOT_DATA, U_BOOT_DTB_DATA)
2462 size = 0xb0
2463
2464 data = self._DoReadFile('104_cbfs_stage.dts')
2465 cbfs = cbfs_util.CbfsReader(data)
2466 self.assertEqual(size, cbfs.rom_size)
2467
2468 self.assertIn('u-boot', cbfs.files)
2469 cfile = cbfs.files['u-boot']
2470 self.assertEqual(U_BOOT_DATA + U_BOOT_DTB_DATA, cfile.data)
2471
2472 def testCbfsRawCompress(self):
2473 """Test handling of compressing raw files"""
2474 self._CheckLz4()
2475 data = self._DoReadFile('105_cbfs_raw_compress.dts')
2476 size = 0x140
2477
2478 cbfs = cbfs_util.CbfsReader(data)
2479 self.assertIn('u-boot', cbfs.files)
2480 cfile = cbfs.files['u-boot']
2481 self.assertEqual(COMPRESS_DATA, cfile.data)
2482
2483 def testCbfsBadArch(self):
2484 """Test handling of a bad architecture"""
2485 with self.assertRaises(ValueError) as e:
2486 self._DoReadFile('106_cbfs_bad_arch.dts')
2487 self.assertIn("Invalid architecture 'bad-arch'", str(e.exception))
2488
2489 def testCbfsNoSize(self):
2490 """Test handling of a missing size property"""
2491 with self.assertRaises(ValueError) as e:
2492 self._DoReadFile('107_cbfs_no_size.dts')
2493 self.assertIn('entry must have a size property', str(e.exception))
2494
Simon Glass3e28f4f2021-11-23 11:03:54 -07002495 def testCbfsNoContents(self):
Simon Glass1de34482019-07-08 13:18:53 -06002496 """Test handling of a CBFS entry which does not provide contentsy"""
2497 with self.assertRaises(ValueError) as e:
2498 self._DoReadFile('108_cbfs_no_contents.dts')
2499 self.assertIn('Could not complete processing of contents',
2500 str(e.exception))
2501
2502 def testCbfsBadCompress(self):
2503 """Test handling of a bad architecture"""
2504 with self.assertRaises(ValueError) as e:
2505 self._DoReadFile('109_cbfs_bad_compress.dts')
2506 self.assertIn("Invalid compression in 'u-boot': 'invalid-algo'",
2507 str(e.exception))
2508
2509 def testCbfsNamedEntries(self):
2510 """Test handling of named entries"""
2511 data = self._DoReadFile('110_cbfs_name.dts')
2512
2513 cbfs = cbfs_util.CbfsReader(data)
2514 self.assertIn('FRED', cbfs.files)
2515 cfile1 = cbfs.files['FRED']
2516 self.assertEqual(U_BOOT_DATA, cfile1.data)
2517
2518 self.assertIn('hello', cbfs.files)
2519 cfile2 = cbfs.files['hello']
2520 self.assertEqual(U_BOOT_DTB_DATA, cfile2.data)
2521
Simon Glass759af872019-07-08 13:18:54 -06002522 def _SetupIfwi(self, fname):
2523 """Set up to run an IFWI test
2524
2525 Args:
2526 fname: Filename of input file to provide (fitimage.bin or ifwi.bin)
2527 """
2528 self._SetupSplElf()
Simon Glass3eb5b202019-08-24 07:23:00 -06002529 self._SetupTplElf()
Simon Glass759af872019-07-08 13:18:54 -06002530
2531 # Intel Integrated Firmware Image (IFWI) file
2532 with gzip.open(self.TestFile('%s.gz' % fname), 'rb') as fd:
2533 data = fd.read()
2534 TestFunctional._MakeInputFile(fname,data)
2535
2536 def _CheckIfwi(self, data):
2537 """Check that an image with an IFWI contains the correct output
2538
2539 Args:
2540 data: Conents of output file
2541 """
Simon Glass80025522022-01-29 14:14:04 -07002542 expected_desc = tools.read_file(self.TestFile('descriptor.bin'))
Simon Glass759af872019-07-08 13:18:54 -06002543 if data[:0x1000] != expected_desc:
2544 self.fail('Expected descriptor binary at start of image')
2545
2546 # We expect to find the TPL wil in subpart IBBP entry IBBL
Simon Glass80025522022-01-29 14:14:04 -07002547 image_fname = tools.get_output_filename('image.bin')
2548 tpl_fname = tools.get_output_filename('tpl.out')
Simon Glass57c7a482022-01-09 20:14:01 -07002549 ifwitool = bintool.Bintool.create('ifwitool')
2550 ifwitool.extract(image_fname, 'IBBP', 'IBBL', tpl_fname)
Simon Glass759af872019-07-08 13:18:54 -06002551
Simon Glass80025522022-01-29 14:14:04 -07002552 tpl_data = tools.read_file(tpl_fname)
Simon Glassf55bd692019-08-24 07:22:51 -06002553 self.assertEqual(U_BOOT_TPL_DATA, tpl_data[:len(U_BOOT_TPL_DATA)])
Simon Glass759af872019-07-08 13:18:54 -06002554
2555 def testPackX86RomIfwi(self):
2556 """Test that an x86 ROM with Integrated Firmware Image can be created"""
2557 self._SetupIfwi('fitimage.bin')
Simon Glass1d167762019-08-24 07:23:01 -06002558 data = self._DoReadFile('111_x86_rom_ifwi.dts')
Simon Glass759af872019-07-08 13:18:54 -06002559 self._CheckIfwi(data)
2560
2561 def testPackX86RomIfwiNoDesc(self):
2562 """Test that an x86 ROM with IFWI can be created from an ifwi.bin file"""
2563 self._SetupIfwi('ifwi.bin')
Simon Glass1d167762019-08-24 07:23:01 -06002564 data = self._DoReadFile('112_x86_rom_ifwi_nodesc.dts')
Simon Glass759af872019-07-08 13:18:54 -06002565 self._CheckIfwi(data)
2566
2567 def testPackX86RomIfwiNoData(self):
2568 """Test that an x86 ROM with IFWI handles missing data"""
2569 self._SetupIfwi('ifwi.bin')
2570 with self.assertRaises(ValueError) as e:
Simon Glass1d167762019-08-24 07:23:01 -06002571 data = self._DoReadFile('113_x86_rom_ifwi_nodata.dts')
Simon Glass759af872019-07-08 13:18:54 -06002572 self.assertIn('Could not complete processing of contents',
2573 str(e.exception))
Simon Glass91710b32018-07-17 13:25:32 -06002574
Simon Glass66152ce2022-01-09 20:14:09 -07002575 def testIfwiMissing(self):
2576 """Test that binman still produces an image if ifwitool is missing"""
2577 self._SetupIfwi('fitimage.bin')
Simon Glass14d64e32025-04-29 07:21:59 -06002578 with terminal.capture() as (_, stderr):
Simon Glass66152ce2022-01-09 20:14:09 -07002579 self._DoTestFile('111_x86_rom_ifwi.dts',
2580 force_missing_bintools='ifwitool')
2581 err = stderr.getvalue()
2582 self.assertRegex(err,
Simon Glass49cd2b32023-02-07 14:34:18 -07002583 "Image 'image'.*missing bintools.*: ifwitool")
Simon Glass66152ce2022-01-09 20:14:09 -07002584
Simon Glassc2f1aed2019-07-08 13:18:56 -06002585 def testCbfsOffset(self):
2586 """Test a CBFS with files at particular offsets
2587
2588 Like all CFBS tests, this is just checking the logic that calls
2589 cbfs_util. See cbfs_util_test for fully tests (e.g. test_cbfs_offset()).
2590 """
2591 data = self._DoReadFile('114_cbfs_offset.dts')
2592 size = 0x200
2593
2594 cbfs = cbfs_util.CbfsReader(data)
2595 self.assertEqual(size, cbfs.rom_size)
2596
2597 self.assertIn('u-boot', cbfs.files)
2598 cfile = cbfs.files['u-boot']
2599 self.assertEqual(U_BOOT_DATA, cfile.data)
2600 self.assertEqual(0x40, cfile.cbfs_offset)
2601
2602 self.assertIn('u-boot-dtb', cbfs.files)
2603 cfile2 = cbfs.files['u-boot-dtb']
2604 self.assertEqual(U_BOOT_DTB_DATA, cfile2.data)
2605 self.assertEqual(0x140, cfile2.cbfs_offset)
2606
Simon Glass0f621332019-07-08 14:25:27 -06002607 def testFdtmap(self):
2608 """Test an FDT map can be inserted in the image"""
2609 data = self.data = self._DoReadFileRealDtb('115_fdtmap.dts')
2610 fdtmap_data = data[len(U_BOOT_DATA):]
2611 magic = fdtmap_data[:8]
Simon Glassc5fd10a2019-10-31 07:43:03 -06002612 self.assertEqual(b'_FDTMAP_', magic)
Simon Glass80025522022-01-29 14:14:04 -07002613 self.assertEqual(tools.get_bytes(0, 8), fdtmap_data[8:16])
Simon Glass0f621332019-07-08 14:25:27 -06002614
2615 fdt_data = fdtmap_data[16:]
2616 dtb = fdt.Fdt.FromData(fdt_data)
2617 dtb.Scan()
Simon Glass2c6adba2019-07-20 12:23:47 -06002618 props = self._GetPropTree(dtb, BASE_DTB_PROPS, prefix='/')
Simon Glass0f621332019-07-08 14:25:27 -06002619 self.assertEqual({
2620 'image-pos': 0,
2621 'offset': 0,
2622 'u-boot:offset': 0,
2623 'u-boot:size': len(U_BOOT_DATA),
2624 'u-boot:image-pos': 0,
2625 'fdtmap:image-pos': 4,
2626 'fdtmap:offset': 4,
2627 'fdtmap:size': len(fdtmap_data),
2628 'size': len(data),
2629 }, props)
2630
2631 def testFdtmapNoMatch(self):
2632 """Check handling of an FDT map when the section cannot be found"""
2633 self.data = self._DoReadFileRealDtb('115_fdtmap.dts')
2634
2635 # Mangle the section name, which should cause a mismatch between the
2636 # correct FDT path and the one expected by the section
2637 image = control.images['image']
Simon Glasscec34ba2019-07-08 14:25:28 -06002638 image._node.path += '-suffix'
Simon Glass0f621332019-07-08 14:25:27 -06002639 entries = image.GetEntries()
2640 fdtmap = entries['fdtmap']
2641 with self.assertRaises(ValueError) as e:
2642 fdtmap._GetFdtmap()
2643 self.assertIn("Cannot locate node for path '/binman-suffix'",
2644 str(e.exception))
2645
Simon Glasscec34ba2019-07-08 14:25:28 -06002646 def testFdtmapHeader(self):
2647 """Test an FDT map and image header can be inserted in the image"""
2648 data = self.data = self._DoReadFileRealDtb('116_fdtmap_hdr.dts')
2649 fdtmap_pos = len(U_BOOT_DATA)
2650 fdtmap_data = data[fdtmap_pos:]
2651 fdt_data = fdtmap_data[16:]
2652 dtb = fdt.Fdt.FromData(fdt_data)
2653 fdt_size = dtb.GetFdtObj().totalsize()
2654 hdr_data = data[-8:]
Simon Glassc5fd10a2019-10-31 07:43:03 -06002655 self.assertEqual(b'BinM', hdr_data[:4])
Simon Glasscec34ba2019-07-08 14:25:28 -06002656 offset = struct.unpack('<I', hdr_data[4:])[0] & 0xffffffff
2657 self.assertEqual(fdtmap_pos - 0x400, offset - (1 << 32))
2658
2659 def testFdtmapHeaderStart(self):
2660 """Test an image header can be inserted at the image start"""
2661 data = self.data = self._DoReadFileRealDtb('117_fdtmap_hdr_start.dts')
2662 fdtmap_pos = 0x100 + len(U_BOOT_DATA)
2663 hdr_data = data[:8]
Simon Glassc5fd10a2019-10-31 07:43:03 -06002664 self.assertEqual(b'BinM', hdr_data[:4])
Simon Glasscec34ba2019-07-08 14:25:28 -06002665 offset = struct.unpack('<I', hdr_data[4:])[0]
2666 self.assertEqual(fdtmap_pos, offset)
2667
2668 def testFdtmapHeaderPos(self):
2669 """Test an image header can be inserted at a chosen position"""
2670 data = self.data = self._DoReadFileRealDtb('118_fdtmap_hdr_pos.dts')
2671 fdtmap_pos = 0x100 + len(U_BOOT_DATA)
2672 hdr_data = data[0x80:0x88]
Simon Glassc5fd10a2019-10-31 07:43:03 -06002673 self.assertEqual(b'BinM', hdr_data[:4])
Simon Glasscec34ba2019-07-08 14:25:28 -06002674 offset = struct.unpack('<I', hdr_data[4:])[0]
2675 self.assertEqual(fdtmap_pos, offset)
2676
2677 def testHeaderMissingFdtmap(self):
2678 """Test an image header requires an fdtmap"""
2679 with self.assertRaises(ValueError) as e:
2680 self.data = self._DoReadFileRealDtb('119_fdtmap_hdr_missing.dts')
2681 self.assertIn("'image_header' section must have an 'fdtmap' sibling",
2682 str(e.exception))
2683
2684 def testHeaderNoLocation(self):
2685 """Test an image header with a no specified location is detected"""
2686 with self.assertRaises(ValueError) as e:
2687 self.data = self._DoReadFileRealDtb('120_hdr_no_location.dts')
2688 self.assertIn("Invalid location 'None', expected 'start' or 'end'",
2689 str(e.exception))
2690
Simon Glasse61b6f62019-07-08 14:25:37 -06002691 def testEntryExpand(self):
Simon Glassdd156a42022-03-05 20:18:59 -07002692 """Test extending an entry after it is packed"""
2693 data = self._DoReadFile('121_entry_extend.dts')
Simon Glass8c702fb2019-07-20 12:23:57 -06002694 self.assertEqual(b'aaa', data[:3])
2695 self.assertEqual(U_BOOT_DATA, data[3:3 + len(U_BOOT_DATA)])
2696 self.assertEqual(b'aaa', data[-3:])
Simon Glasse61b6f62019-07-08 14:25:37 -06002697
Simon Glassdd156a42022-03-05 20:18:59 -07002698 def testEntryExtendBad(self):
2699 """Test extending an entry after it is packed, twice"""
Simon Glasse61b6f62019-07-08 14:25:37 -06002700 with self.assertRaises(ValueError) as e:
Simon Glassdd156a42022-03-05 20:18:59 -07002701 self._DoReadFile('122_entry_extend_twice.dts')
Simon Glass9d8ee322019-07-20 12:23:58 -06002702 self.assertIn("Image '/binman': Entries changed size after packing",
Simon Glasse61b6f62019-07-08 14:25:37 -06002703 str(e.exception))
2704
Simon Glassdd156a42022-03-05 20:18:59 -07002705 def testEntryExtendSection(self):
2706 """Test extending an entry within a section after it is packed"""
2707 data = self._DoReadFile('123_entry_extend_section.dts')
Simon Glass8c702fb2019-07-20 12:23:57 -06002708 self.assertEqual(b'aaa', data[:3])
2709 self.assertEqual(U_BOOT_DATA, data[3:3 + len(U_BOOT_DATA)])
2710 self.assertEqual(b'aaa', data[-3:])
Simon Glasse61b6f62019-07-08 14:25:37 -06002711
Simon Glass90d29682019-07-08 14:25:38 -06002712 def testCompressDtb(self):
2713 """Test that compress of device-tree files is supported"""
2714 self._CheckLz4()
2715 data = self.data = self._DoReadFileRealDtb('124_compress_dtb.dts')
2716 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
2717 comp_data = data[len(U_BOOT_DATA):]
2718 orig = self._decompress(comp_data)
2719 dtb = fdt.Fdt.FromData(orig)
2720 dtb.Scan()
2721 props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
2722 expected = {
2723 'u-boot:size': len(U_BOOT_DATA),
2724 'u-boot-dtb:uncomp-size': len(orig),
2725 'u-boot-dtb:size': len(comp_data),
2726 'size': len(data),
2727 }
2728 self.assertEqual(expected, props)
2729
Simon Glass151bbbf2019-07-08 14:25:41 -06002730 def testCbfsUpdateFdt(self):
2731 """Test that we can update the device tree with CBFS offset/size info"""
2732 self._CheckLz4()
2733 data, _, _, out_dtb_fname = self._DoReadFileDtb('125_cbfs_update.dts',
2734 update_dtb=True)
2735 dtb = fdt.Fdt(out_dtb_fname)
2736 dtb.Scan()
Simon Glass2c6adba2019-07-20 12:23:47 -06002737 props = self._GetPropTree(dtb, BASE_DTB_PROPS + ['uncomp-size'])
Simon Glass151bbbf2019-07-08 14:25:41 -06002738 del props['cbfs/u-boot:size']
2739 self.assertEqual({
2740 'offset': 0,
2741 'size': len(data),
2742 'image-pos': 0,
2743 'cbfs:offset': 0,
2744 'cbfs:size': len(data),
2745 'cbfs:image-pos': 0,
Simon Glassfa144222023-10-14 14:40:28 -06002746 'cbfs/u-boot:offset': 0x30,
Simon Glass151bbbf2019-07-08 14:25:41 -06002747 'cbfs/u-boot:uncomp-size': len(U_BOOT_DATA),
Simon Glassfa144222023-10-14 14:40:28 -06002748 'cbfs/u-boot:image-pos': 0x30,
2749 'cbfs/u-boot-dtb:offset': 0xa4,
Simon Glass151bbbf2019-07-08 14:25:41 -06002750 'cbfs/u-boot-dtb:size': len(U_BOOT_DATA),
Simon Glassfa144222023-10-14 14:40:28 -06002751 'cbfs/u-boot-dtb:image-pos': 0xa4,
Simon Glass151bbbf2019-07-08 14:25:41 -06002752 }, props)
2753
Simon Glass3c9b4f22019-07-08 14:25:42 -06002754 def testCbfsBadType(self):
2755 """Test an image header with a no specified location is detected"""
2756 with self.assertRaises(ValueError) as e:
2757 self._DoReadFile('126_cbfs_bad_type.dts')
2758 self.assertIn("Unknown cbfs-type 'badtype'", str(e.exception))
2759
Simon Glass6b156f82019-07-08 14:25:43 -06002760 def testList(self):
2761 """Test listing the files in an image"""
2762 self._CheckLz4()
2763 data = self._DoReadFile('127_list.dts')
2764 image = control.images['image']
2765 entries = image.BuildEntryList()
2766 self.assertEqual(7, len(entries))
2767
2768 ent = entries[0]
2769 self.assertEqual(0, ent.indent)
Simon Glass49cd2b32023-02-07 14:34:18 -07002770 self.assertEqual('image', ent.name)
Simon Glass6b156f82019-07-08 14:25:43 -06002771 self.assertEqual('section', ent.etype)
2772 self.assertEqual(len(data), ent.size)
2773 self.assertEqual(0, ent.image_pos)
2774 self.assertEqual(None, ent.uncomp_size)
Simon Glass39dd2152019-07-08 14:25:47 -06002775 self.assertEqual(0, ent.offset)
Simon Glass6b156f82019-07-08 14:25:43 -06002776
2777 ent = entries[1]
2778 self.assertEqual(1, ent.indent)
2779 self.assertEqual('u-boot', ent.name)
2780 self.assertEqual('u-boot', ent.etype)
2781 self.assertEqual(len(U_BOOT_DATA), ent.size)
2782 self.assertEqual(0, ent.image_pos)
2783 self.assertEqual(None, ent.uncomp_size)
2784 self.assertEqual(0, ent.offset)
2785
2786 ent = entries[2]
2787 self.assertEqual(1, ent.indent)
2788 self.assertEqual('section', ent.name)
2789 self.assertEqual('section', ent.etype)
2790 section_size = ent.size
2791 self.assertEqual(0x100, ent.image_pos)
2792 self.assertEqual(None, ent.uncomp_size)
Simon Glass39dd2152019-07-08 14:25:47 -06002793 self.assertEqual(0x100, ent.offset)
Simon Glass6b156f82019-07-08 14:25:43 -06002794
2795 ent = entries[3]
2796 self.assertEqual(2, ent.indent)
2797 self.assertEqual('cbfs', ent.name)
2798 self.assertEqual('cbfs', ent.etype)
2799 self.assertEqual(0x400, ent.size)
2800 self.assertEqual(0x100, ent.image_pos)
2801 self.assertEqual(None, ent.uncomp_size)
2802 self.assertEqual(0, ent.offset)
2803
2804 ent = entries[4]
2805 self.assertEqual(3, ent.indent)
2806 self.assertEqual('u-boot', ent.name)
2807 self.assertEqual('u-boot', ent.etype)
2808 self.assertEqual(len(U_BOOT_DATA), ent.size)
2809 self.assertEqual(0x138, ent.image_pos)
2810 self.assertEqual(None, ent.uncomp_size)
2811 self.assertEqual(0x38, ent.offset)
2812
2813 ent = entries[5]
2814 self.assertEqual(3, ent.indent)
2815 self.assertEqual('u-boot-dtb', ent.name)
2816 self.assertEqual('text', ent.etype)
2817 self.assertGreater(len(COMPRESS_DATA), ent.size)
2818 self.assertEqual(0x178, ent.image_pos)
2819 self.assertEqual(len(COMPRESS_DATA), ent.uncomp_size)
2820 self.assertEqual(0x78, ent.offset)
2821
2822 ent = entries[6]
2823 self.assertEqual(2, ent.indent)
2824 self.assertEqual('u-boot-dtb', ent.name)
2825 self.assertEqual('u-boot-dtb', ent.etype)
2826 self.assertEqual(0x500, ent.image_pos)
2827 self.assertEqual(len(U_BOOT_DTB_DATA), ent.uncomp_size)
2828 dtb_size = ent.size
2829 # Compressing this data expands it since headers are added
2830 self.assertGreater(dtb_size, len(U_BOOT_DTB_DATA))
2831 self.assertEqual(0x400, ent.offset)
2832
2833 self.assertEqual(len(data), 0x100 + section_size)
2834 self.assertEqual(section_size, 0x400 + dtb_size)
2835
Simon Glass8d8bf4e2019-07-08 14:25:44 -06002836 def testFindFdtmap(self):
2837 """Test locating an FDT map in an image"""
2838 self._CheckLz4()
2839 data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
2840 image = control.images['image']
2841 entries = image.GetEntries()
2842 entry = entries['fdtmap']
2843 self.assertEqual(entry.image_pos, fdtmap.LocateFdtmap(data))
2844
2845 def testFindFdtmapMissing(self):
2846 """Test failing to locate an FDP map"""
2847 data = self._DoReadFile('005_simple.dts')
2848 self.assertEqual(None, fdtmap.LocateFdtmap(data))
2849
Simon Glassed39a3c2019-07-08 14:25:45 -06002850 def testFindImageHeader(self):
2851 """Test locating a image header"""
2852 self._CheckLz4()
Simon Glassb8424fa2019-07-08 14:25:46 -06002853 data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
Simon Glassed39a3c2019-07-08 14:25:45 -06002854 image = control.images['image']
2855 entries = image.GetEntries()
2856 entry = entries['fdtmap']
2857 # The header should point to the FDT map
2858 self.assertEqual(entry.image_pos, image_header.LocateHeaderOffset(data))
2859
2860 def testFindImageHeaderStart(self):
2861 """Test locating a image header located at the start of an image"""
Simon Glassb8424fa2019-07-08 14:25:46 -06002862 data = self.data = self._DoReadFileRealDtb('117_fdtmap_hdr_start.dts')
Simon Glassed39a3c2019-07-08 14:25:45 -06002863 image = control.images['image']
2864 entries = image.GetEntries()
2865 entry = entries['fdtmap']
2866 # The header should point to the FDT map
2867 self.assertEqual(entry.image_pos, image_header.LocateHeaderOffset(data))
2868
2869 def testFindImageHeaderMissing(self):
2870 """Test failing to locate an image header"""
2871 data = self._DoReadFile('005_simple.dts')
2872 self.assertEqual(None, image_header.LocateHeaderOffset(data))
2873
Simon Glassb8424fa2019-07-08 14:25:46 -06002874 def testReadImage(self):
2875 """Test reading an image and accessing its FDT map"""
2876 self._CheckLz4()
2877 data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
Simon Glass80025522022-01-29 14:14:04 -07002878 image_fname = tools.get_output_filename('image.bin')
Simon Glassb8424fa2019-07-08 14:25:46 -06002879 orig_image = control.images['image']
2880 image = Image.FromFile(image_fname)
2881 self.assertEqual(orig_image.GetEntries().keys(),
2882 image.GetEntries().keys())
2883
2884 orig_entry = orig_image.GetEntries()['fdtmap']
2885 entry = image.GetEntries()['fdtmap']
Brandon Maiera657bc62024-06-04 16:16:05 +00002886 self.assertEqual(orig_entry.offset, entry.offset)
2887 self.assertEqual(orig_entry.size, entry.size)
2888 self.assertEqual(orig_entry.image_pos, entry.image_pos)
Simon Glassb8424fa2019-07-08 14:25:46 -06002889
2890 def testReadImageNoHeader(self):
2891 """Test accessing an image's FDT map without an image header"""
2892 self._CheckLz4()
2893 data = self._DoReadFileRealDtb('129_decode_image_nohdr.dts')
Simon Glass80025522022-01-29 14:14:04 -07002894 image_fname = tools.get_output_filename('image.bin')
Simon Glassb8424fa2019-07-08 14:25:46 -06002895 image = Image.FromFile(image_fname)
2896 self.assertTrue(isinstance(image, Image))
Simon Glass072959a2019-07-20 12:23:50 -06002897 self.assertEqual('image', image.image_name[-5:])
Simon Glassb8424fa2019-07-08 14:25:46 -06002898
2899 def testReadImageFail(self):
2900 """Test failing to read an image image's FDT map"""
2901 self._DoReadFile('005_simple.dts')
Simon Glass80025522022-01-29 14:14:04 -07002902 image_fname = tools.get_output_filename('image.bin')
Simon Glassb8424fa2019-07-08 14:25:46 -06002903 with self.assertRaises(ValueError) as e:
2904 image = Image.FromFile(image_fname)
2905 self.assertIn("Cannot find FDT map in image", str(e.exception))
Simon Glassc2f1aed2019-07-08 13:18:56 -06002906
Simon Glassb2fd11d2019-07-08 14:25:48 -06002907 def testListCmd(self):
2908 """Test listing the files in an image using an Fdtmap"""
2909 self._CheckLz4()
2910 data = self._DoReadFileRealDtb('130_list_fdtmap.dts')
2911
2912 # lz4 compression size differs depending on the version
2913 image = control.images['image']
2914 entries = image.GetEntries()
2915 section_size = entries['section'].size
2916 fdt_size = entries['section'].GetEntries()['u-boot-dtb'].size
2917 fdtmap_offset = entries['fdtmap'].offset
2918
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01002919 tmpdir = None
Simon Glassb3d6fc72019-07-20 12:24:10 -06002920 try:
2921 tmpdir, updated_fname = self._SetupImageInTmpdir()
Simon Glass14d64e32025-04-29 07:21:59 -06002922 with terminal.capture() as (stdout, stderr):
Simon Glassb3d6fc72019-07-20 12:24:10 -06002923 self._DoBinman('ls', '-i', updated_fname)
2924 finally:
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01002925 if tmpdir:
2926 shutil.rmtree(tmpdir)
Simon Glassb2fd11d2019-07-08 14:25:48 -06002927 lines = stdout.getvalue().splitlines()
2928 expected = [
2929'Name Image-pos Size Entry-type Offset Uncomp-size',
2930'----------------------------------------------------------------------',
Simon Glass49cd2b32023-02-07 14:34:18 -07002931'image 0 c00 section 0',
Simon Glassb2fd11d2019-07-08 14:25:48 -06002932' u-boot 0 4 u-boot 0',
2933' section 100 %x section 100' % section_size,
2934' cbfs 100 400 cbfs 0',
Simon Glassfa144222023-10-14 14:40:28 -06002935' u-boot 120 4 u-boot 20',
Simon Glassc5fd10a2019-10-31 07:43:03 -06002936' u-boot-dtb 180 105 u-boot-dtb 80 3c9',
Simon Glassb2fd11d2019-07-08 14:25:48 -06002937' u-boot-dtb 500 %x u-boot-dtb 400 3c9' % fdt_size,
Simon Glassc5fd10a2019-10-31 07:43:03 -06002938' fdtmap %x 3bd fdtmap %x' %
Simon Glassb2fd11d2019-07-08 14:25:48 -06002939 (fdtmap_offset, fdtmap_offset),
2940' image-header bf8 8 image-header bf8',
2941 ]
2942 self.assertEqual(expected, lines)
2943
2944 def testListCmdFail(self):
2945 """Test failing to list an image"""
2946 self._DoReadFile('005_simple.dts')
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01002947 tmpdir = None
Simon Glassb3d6fc72019-07-20 12:24:10 -06002948 try:
2949 tmpdir, updated_fname = self._SetupImageInTmpdir()
2950 with self.assertRaises(ValueError) as e:
2951 self._DoBinman('ls', '-i', updated_fname)
2952 finally:
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01002953 if tmpdir:
2954 shutil.rmtree(tmpdir)
Simon Glassb2fd11d2019-07-08 14:25:48 -06002955 self.assertIn("Cannot find FDT map in image", str(e.exception))
2956
2957 def _RunListCmd(self, paths, expected):
2958 """List out entries and check the result
2959
2960 Args:
2961 paths: List of paths to pass to the list command
2962 expected: Expected list of filenames to be returned, in order
2963 """
2964 self._CheckLz4()
2965 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07002966 image_fname = tools.get_output_filename('image.bin')
Simon Glassb2fd11d2019-07-08 14:25:48 -06002967 image = Image.FromFile(image_fname)
2968 lines = image.GetListEntries(paths)[1]
2969 files = [line[0].strip() for line in lines[1:]]
2970 self.assertEqual(expected, files)
2971
2972 def testListCmdSection(self):
2973 """Test listing the files in a section"""
2974 self._RunListCmd(['section'],
2975 ['section', 'cbfs', 'u-boot', 'u-boot-dtb', 'u-boot-dtb'])
2976
2977 def testListCmdFile(self):
2978 """Test listing a particular file"""
2979 self._RunListCmd(['*u-boot-dtb'], ['u-boot-dtb', 'u-boot-dtb'])
2980
2981 def testListCmdWildcard(self):
2982 """Test listing a wildcarded file"""
2983 self._RunListCmd(['*boot*'],
2984 ['u-boot', 'u-boot', 'u-boot-dtb', 'u-boot-dtb'])
2985
2986 def testListCmdWildcardMulti(self):
2987 """Test listing a wildcarded file"""
2988 self._RunListCmd(['*cb*', '*head*'],
2989 ['cbfs', 'u-boot', 'u-boot-dtb', 'image-header'])
2990
2991 def testListCmdEmpty(self):
2992 """Test listing a wildcarded file"""
2993 self._RunListCmd(['nothing'], [])
2994
2995 def testListCmdPath(self):
2996 """Test listing the files in a sub-entry of a section"""
2997 self._RunListCmd(['section/cbfs'], ['cbfs', 'u-boot', 'u-boot-dtb'])
2998
Simon Glass4c613bf2019-07-08 14:25:50 -06002999 def _RunExtractCmd(self, entry_name, decomp=True):
3000 """Extract an entry from an image
3001
3002 Args:
3003 entry_name: Entry name to extract
3004 decomp: True to decompress the data if compressed, False to leave
3005 it in its raw uncompressed format
3006
3007 Returns:
3008 data from entry
3009 """
3010 self._CheckLz4()
3011 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07003012 image_fname = tools.get_output_filename('image.bin')
Simon Glass4c613bf2019-07-08 14:25:50 -06003013 return control.ReadEntry(image_fname, entry_name, decomp)
3014
3015 def testExtractSimple(self):
3016 """Test extracting a single file"""
3017 data = self._RunExtractCmd('u-boot')
3018 self.assertEqual(U_BOOT_DATA, data)
3019
Simon Glass980a2842019-07-08 14:25:52 -06003020 def testExtractSection(self):
3021 """Test extracting the files in a section"""
3022 data = self._RunExtractCmd('section')
3023 cbfs_data = data[:0x400]
3024 cbfs = cbfs_util.CbfsReader(cbfs_data)
Simon Glassc5fd10a2019-10-31 07:43:03 -06003025 self.assertEqual(['u-boot', 'u-boot-dtb', ''], list(cbfs.files.keys()))
Simon Glass980a2842019-07-08 14:25:52 -06003026 dtb_data = data[0x400:]
3027 dtb = self._decompress(dtb_data)
3028 self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
3029
3030 def testExtractCompressed(self):
3031 """Test extracting compressed data"""
3032 data = self._RunExtractCmd('section/u-boot-dtb')
3033 self.assertEqual(EXTRACT_DTB_SIZE, len(data))
3034
3035 def testExtractRaw(self):
3036 """Test extracting compressed data without decompressing it"""
3037 data = self._RunExtractCmd('section/u-boot-dtb', decomp=False)
3038 dtb = self._decompress(data)
3039 self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
3040
3041 def testExtractCbfs(self):
3042 """Test extracting CBFS data"""
3043 data = self._RunExtractCmd('section/cbfs/u-boot')
3044 self.assertEqual(U_BOOT_DATA, data)
3045
3046 def testExtractCbfsCompressed(self):
3047 """Test extracting CBFS compressed data"""
3048 data = self._RunExtractCmd('section/cbfs/u-boot-dtb')
3049 self.assertEqual(EXTRACT_DTB_SIZE, len(data))
3050
3051 def testExtractCbfsRaw(self):
3052 """Test extracting CBFS compressed data without decompressing it"""
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +02003053 bintool = self.comp_bintools['lzma_alone']
3054 self._CheckBintool(bintool)
Simon Glass980a2842019-07-08 14:25:52 -06003055 data = self._RunExtractCmd('section/cbfs/u-boot-dtb', decomp=False)
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +02003056 dtb = bintool.decompress(data)
Simon Glass980a2842019-07-08 14:25:52 -06003057 self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
3058
Simon Glass4c613bf2019-07-08 14:25:50 -06003059 def testExtractBadEntry(self):
3060 """Test extracting a bad section path"""
3061 with self.assertRaises(ValueError) as e:
3062 self._RunExtractCmd('section/does-not-exist')
3063 self.assertIn("Entry 'does-not-exist' not found in '/section'",
3064 str(e.exception))
3065
3066 def testExtractMissingFile(self):
3067 """Test extracting file that does not exist"""
3068 with self.assertRaises(IOError) as e:
3069 control.ReadEntry('missing-file', 'name')
3070
3071 def testExtractBadFile(self):
3072 """Test extracting an invalid file"""
3073 fname = os.path.join(self._indir, 'badfile')
Simon Glass80025522022-01-29 14:14:04 -07003074 tools.write_file(fname, b'')
Simon Glass4c613bf2019-07-08 14:25:50 -06003075 with self.assertRaises(ValueError) as e:
3076 control.ReadEntry(fname, 'name')
3077
Simon Glass980a2842019-07-08 14:25:52 -06003078 def testExtractCmd(self):
3079 """Test extracting a file fron an image on the command line"""
3080 self._CheckLz4()
3081 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass980a2842019-07-08 14:25:52 -06003082 fname = os.path.join(self._indir, 'output.extact')
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01003083 tmpdir = None
Simon Glassb3d6fc72019-07-20 12:24:10 -06003084 try:
3085 tmpdir, updated_fname = self._SetupImageInTmpdir()
Simon Glass14d64e32025-04-29 07:21:59 -06003086 with terminal.capture() as (stdout, stderr):
Simon Glassb3d6fc72019-07-20 12:24:10 -06003087 self._DoBinman('extract', '-i', updated_fname, 'u-boot',
3088 '-f', fname)
3089 finally:
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01003090 if tmpdir:
3091 shutil.rmtree(tmpdir)
Simon Glass80025522022-01-29 14:14:04 -07003092 data = tools.read_file(fname)
Simon Glass980a2842019-07-08 14:25:52 -06003093 self.assertEqual(U_BOOT_DATA, data)
3094
3095 def testExtractOneEntry(self):
3096 """Test extracting a single entry fron an image """
3097 self._CheckLz4()
3098 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07003099 image_fname = tools.get_output_filename('image.bin')
Simon Glass980a2842019-07-08 14:25:52 -06003100 fname = os.path.join(self._indir, 'output.extact')
3101 control.ExtractEntries(image_fname, fname, None, ['u-boot'])
Simon Glass80025522022-01-29 14:14:04 -07003102 data = tools.read_file(fname)
Simon Glass980a2842019-07-08 14:25:52 -06003103 self.assertEqual(U_BOOT_DATA, data)
3104
3105 def _CheckExtractOutput(self, decomp):
3106 """Helper to test file output with and without decompression
3107
3108 Args:
3109 decomp: True to decompress entry data, False to output it raw
3110 """
3111 def _CheckPresent(entry_path, expect_data, expect_size=None):
3112 """Check and remove expected file
3113
3114 This checks the data/size of a file and removes the file both from
3115 the outfiles set and from the output directory. Once all files are
3116 processed, both the set and directory should be empty.
3117
3118 Args:
3119 entry_path: Entry path
3120 expect_data: Data to expect in file, or None to skip check
3121 expect_size: Size of data to expect in file, or None to skip
3122 """
3123 path = os.path.join(outdir, entry_path)
Simon Glass80025522022-01-29 14:14:04 -07003124 data = tools.read_file(path)
Simon Glass980a2842019-07-08 14:25:52 -06003125 os.remove(path)
3126 if expect_data:
3127 self.assertEqual(expect_data, data)
3128 elif expect_size:
3129 self.assertEqual(expect_size, len(data))
3130 outfiles.remove(path)
3131
3132 def _CheckDirPresent(name):
3133 """Remove expected directory
3134
3135 This gives an error if the directory does not exist as expected
3136
3137 Args:
3138 name: Name of directory to remove
3139 """
3140 path = os.path.join(outdir, name)
3141 os.rmdir(path)
3142
3143 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07003144 image_fname = tools.get_output_filename('image.bin')
Simon Glass980a2842019-07-08 14:25:52 -06003145 outdir = os.path.join(self._indir, 'extract')
3146 einfos = control.ExtractEntries(image_fname, None, outdir, [], decomp)
3147
3148 # Create a set of all file that were output (should be 9)
3149 outfiles = set()
3150 for root, dirs, files in os.walk(outdir):
3151 outfiles |= set([os.path.join(root, fname) for fname in files])
3152 self.assertEqual(9, len(outfiles))
3153 self.assertEqual(9, len(einfos))
3154
3155 image = control.images['image']
3156 entries = image.GetEntries()
3157
3158 # Check the 9 files in various ways
3159 section = entries['section']
3160 section_entries = section.GetEntries()
3161 cbfs_entries = section_entries['cbfs'].GetEntries()
3162 _CheckPresent('u-boot', U_BOOT_DATA)
3163 _CheckPresent('section/cbfs/u-boot', U_BOOT_DATA)
3164 dtb_len = EXTRACT_DTB_SIZE
3165 if not decomp:
3166 dtb_len = cbfs_entries['u-boot-dtb'].size
3167 _CheckPresent('section/cbfs/u-boot-dtb', None, dtb_len)
3168 if not decomp:
3169 dtb_len = section_entries['u-boot-dtb'].size
3170 _CheckPresent('section/u-boot-dtb', None, dtb_len)
3171
3172 fdtmap = entries['fdtmap']
3173 _CheckPresent('fdtmap', fdtmap.data)
3174 hdr = entries['image-header']
3175 _CheckPresent('image-header', hdr.data)
3176
3177 _CheckPresent('section/root', section.data)
3178 cbfs = section_entries['cbfs']
3179 _CheckPresent('section/cbfs/root', cbfs.data)
Simon Glass80025522022-01-29 14:14:04 -07003180 data = tools.read_file(image_fname)
Simon Glass980a2842019-07-08 14:25:52 -06003181 _CheckPresent('root', data)
3182
3183 # There should be no files left. Remove all the directories to check.
3184 # If there are any files/dirs remaining, one of these checks will fail.
3185 self.assertEqual(0, len(outfiles))
3186 _CheckDirPresent('section/cbfs')
3187 _CheckDirPresent('section')
3188 _CheckDirPresent('')
3189 self.assertFalse(os.path.exists(outdir))
3190
3191 def testExtractAllEntries(self):
3192 """Test extracting all entries"""
3193 self._CheckLz4()
3194 self._CheckExtractOutput(decomp=True)
3195
3196 def testExtractAllEntriesRaw(self):
3197 """Test extracting all entries without decompressing them"""
3198 self._CheckLz4()
3199 self._CheckExtractOutput(decomp=False)
3200
3201 def testExtractSelectedEntries(self):
3202 """Test extracting some entries"""
3203 self._CheckLz4()
3204 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07003205 image_fname = tools.get_output_filename('image.bin')
Simon Glass980a2842019-07-08 14:25:52 -06003206 outdir = os.path.join(self._indir, 'extract')
3207 einfos = control.ExtractEntries(image_fname, None, outdir,
3208 ['*cb*', '*head*'])
3209
3210 # File output is tested by testExtractAllEntries(), so just check that
3211 # the expected entries are selected
3212 names = [einfo.name for einfo in einfos]
3213 self.assertEqual(names,
3214 ['cbfs', 'u-boot', 'u-boot-dtb', 'image-header'])
3215
3216 def testExtractNoEntryPaths(self):
3217 """Test extracting some entries"""
3218 self._CheckLz4()
3219 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07003220 image_fname = tools.get_output_filename('image.bin')
Simon Glass980a2842019-07-08 14:25:52 -06003221 with self.assertRaises(ValueError) as e:
3222 control.ExtractEntries(image_fname, 'fname', None, [])
Simon Glassa772d3f2019-07-20 12:24:14 -06003223 self.assertIn('Must specify an entry path to write with -f',
Simon Glass980a2842019-07-08 14:25:52 -06003224 str(e.exception))
3225
3226 def testExtractTooManyEntryPaths(self):
3227 """Test extracting some entries"""
3228 self._CheckLz4()
3229 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07003230 image_fname = tools.get_output_filename('image.bin')
Simon Glass980a2842019-07-08 14:25:52 -06003231 with self.assertRaises(ValueError) as e:
3232 control.ExtractEntries(image_fname, 'fname', None, ['a', 'b'])
Simon Glassa772d3f2019-07-20 12:24:14 -06003233 self.assertIn('Must specify exactly one entry path to write with -f',
Simon Glass980a2842019-07-08 14:25:52 -06003234 str(e.exception))
3235
Simon Glass52d06212019-07-08 14:25:53 -06003236 def testPackAlignSection(self):
3237 """Test that sections can have alignment"""
3238 self._DoReadFile('131_pack_align_section.dts')
3239
3240 self.assertIn('image', control.images)
3241 image = control.images['image']
3242 entries = image.GetEntries()
3243 self.assertEqual(3, len(entries))
3244
3245 # First u-boot
3246 self.assertIn('u-boot', entries)
3247 entry = entries['u-boot']
3248 self.assertEqual(0, entry.offset)
3249 self.assertEqual(0, entry.image_pos)
3250 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3251 self.assertEqual(len(U_BOOT_DATA), entry.size)
3252
3253 # Section0
3254 self.assertIn('section0', entries)
3255 section0 = entries['section0']
3256 self.assertEqual(0x10, section0.offset)
3257 self.assertEqual(0x10, section0.image_pos)
3258 self.assertEqual(len(U_BOOT_DATA), section0.size)
3259
3260 # Second u-boot
3261 section_entries = section0.GetEntries()
3262 self.assertIn('u-boot', section_entries)
3263 entry = section_entries['u-boot']
3264 self.assertEqual(0, entry.offset)
3265 self.assertEqual(0x10, entry.image_pos)
3266 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3267 self.assertEqual(len(U_BOOT_DATA), entry.size)
3268
3269 # Section1
3270 self.assertIn('section1', entries)
3271 section1 = entries['section1']
3272 self.assertEqual(0x14, section1.offset)
3273 self.assertEqual(0x14, section1.image_pos)
3274 self.assertEqual(0x20, section1.size)
3275
3276 # Second u-boot
3277 section_entries = section1.GetEntries()
3278 self.assertIn('u-boot', section_entries)
3279 entry = section_entries['u-boot']
3280 self.assertEqual(0, entry.offset)
3281 self.assertEqual(0x14, entry.image_pos)
3282 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3283 self.assertEqual(len(U_BOOT_DATA), entry.size)
3284
3285 # Section2
3286 self.assertIn('section2', section_entries)
3287 section2 = section_entries['section2']
3288 self.assertEqual(0x4, section2.offset)
3289 self.assertEqual(0x18, section2.image_pos)
3290 self.assertEqual(4, section2.size)
3291
3292 # Third u-boot
3293 section_entries = section2.GetEntries()
3294 self.assertIn('u-boot', section_entries)
3295 entry = section_entries['u-boot']
3296 self.assertEqual(0, entry.offset)
3297 self.assertEqual(0x18, entry.image_pos)
3298 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3299 self.assertEqual(len(U_BOOT_DATA), entry.size)
3300
Simon Glassf8a54bc2019-07-20 12:23:56 -06003301 def _RunReplaceCmd(self, entry_name, data, decomp=True, allow_resize=True,
3302 dts='132_replace.dts'):
Simon Glass072959a2019-07-20 12:23:50 -06003303 """Replace an entry in an image
3304
3305 This writes the entry data to update it, then opens the updated file and
3306 returns the value that it now finds there.
3307
3308 Args:
3309 entry_name: Entry name to replace
3310 data: Data to replace it with
3311 decomp: True to compress the data if needed, False if data is
3312 already compressed so should be used as is
Simon Glassf8a54bc2019-07-20 12:23:56 -06003313 allow_resize: True to allow entries to change size, False to raise
3314 an exception
Simon Glass072959a2019-07-20 12:23:50 -06003315
3316 Returns:
3317 Tuple:
3318 data from entry
3319 data from fdtmap (excluding header)
Simon Glassf8a54bc2019-07-20 12:23:56 -06003320 Image object that was modified
Simon Glass072959a2019-07-20 12:23:50 -06003321 """
Simon Glassf8a54bc2019-07-20 12:23:56 -06003322 dtb_data = self._DoReadFileDtb(dts, use_real_dtb=True,
Simon Glass072959a2019-07-20 12:23:50 -06003323 update_dtb=True)[1]
3324
3325 self.assertIn('image', control.images)
3326 image = control.images['image']
3327 entries = image.GetEntries()
3328 orig_dtb_data = entries['u-boot-dtb'].data
3329 orig_fdtmap_data = entries['fdtmap'].data
3330
Simon Glass80025522022-01-29 14:14:04 -07003331 image_fname = tools.get_output_filename('image.bin')
3332 updated_fname = tools.get_output_filename('image-updated.bin')
3333 tools.write_file(updated_fname, tools.read_file(image_fname))
Simon Glassf8a54bc2019-07-20 12:23:56 -06003334 image = control.WriteEntry(updated_fname, entry_name, data, decomp,
3335 allow_resize)
Simon Glass072959a2019-07-20 12:23:50 -06003336 data = control.ReadEntry(updated_fname, entry_name, decomp)
3337
Simon Glassf8a54bc2019-07-20 12:23:56 -06003338 # The DT data should not change unless resized:
3339 if not allow_resize:
3340 new_dtb_data = entries['u-boot-dtb'].data
3341 self.assertEqual(new_dtb_data, orig_dtb_data)
3342 new_fdtmap_data = entries['fdtmap'].data
3343 self.assertEqual(new_fdtmap_data, orig_fdtmap_data)
Simon Glass072959a2019-07-20 12:23:50 -06003344
Simon Glassf8a54bc2019-07-20 12:23:56 -06003345 return data, orig_fdtmap_data[fdtmap.FDTMAP_HDR_LEN:], image
Simon Glass072959a2019-07-20 12:23:50 -06003346
3347 def testReplaceSimple(self):
3348 """Test replacing a single file"""
3349 expected = b'x' * len(U_BOOT_DATA)
Simon Glassf8a54bc2019-07-20 12:23:56 -06003350 data, expected_fdtmap, _ = self._RunReplaceCmd('u-boot', expected,
3351 allow_resize=False)
Simon Glass072959a2019-07-20 12:23:50 -06003352 self.assertEqual(expected, data)
3353
3354 # Test that the state looks right. There should be an FDT for the fdtmap
3355 # that we jsut read back in, and it should match what we find in the
3356 # 'control' tables. Checking for an FDT that does not exist should
3357 # return None.
3358 path, fdtmap = state.GetFdtContents('fdtmap')
Simon Glassf8a54bc2019-07-20 12:23:56 -06003359 self.assertIsNotNone(path)
Simon Glass072959a2019-07-20 12:23:50 -06003360 self.assertEqual(expected_fdtmap, fdtmap)
3361
3362 dtb = state.GetFdtForEtype('fdtmap')
3363 self.assertEqual(dtb.GetContents(), fdtmap)
3364
3365 missing_path, missing_fdtmap = state.GetFdtContents('missing')
3366 self.assertIsNone(missing_path)
3367 self.assertIsNone(missing_fdtmap)
3368
3369 missing_dtb = state.GetFdtForEtype('missing')
3370 self.assertIsNone(missing_dtb)
3371
3372 self.assertEqual('/binman', state.fdt_path_prefix)
3373
3374 def testReplaceResizeFail(self):
3375 """Test replacing a file by something larger"""
3376 expected = U_BOOT_DATA + b'x'
3377 with self.assertRaises(ValueError) as e:
Simon Glassf8a54bc2019-07-20 12:23:56 -06003378 self._RunReplaceCmd('u-boot', expected, allow_resize=False,
3379 dts='139_replace_repack.dts')
Simon Glass072959a2019-07-20 12:23:50 -06003380 self.assertIn("Node '/u-boot': Entry data size does not match, but resize is disabled",
3381 str(e.exception))
3382
3383 def testReplaceMulti(self):
3384 """Test replacing entry data where multiple images are generated"""
3385 data = self._DoReadFileDtb('133_replace_multi.dts', use_real_dtb=True,
3386 update_dtb=True)[0]
3387 expected = b'x' * len(U_BOOT_DATA)
Simon Glass80025522022-01-29 14:14:04 -07003388 updated_fname = tools.get_output_filename('image-updated.bin')
3389 tools.write_file(updated_fname, data)
Simon Glass072959a2019-07-20 12:23:50 -06003390 entry_name = 'u-boot'
Simon Glassf8a54bc2019-07-20 12:23:56 -06003391 control.WriteEntry(updated_fname, entry_name, expected,
3392 allow_resize=False)
Simon Glass072959a2019-07-20 12:23:50 -06003393 data = control.ReadEntry(updated_fname, entry_name)
3394 self.assertEqual(expected, data)
3395
3396 # Check the state looks right.
3397 self.assertEqual('/binman/image', state.fdt_path_prefix)
3398
3399 # Now check we can write the first image
Simon Glass80025522022-01-29 14:14:04 -07003400 image_fname = tools.get_output_filename('first-image.bin')
3401 updated_fname = tools.get_output_filename('first-updated.bin')
3402 tools.write_file(updated_fname, tools.read_file(image_fname))
Simon Glass072959a2019-07-20 12:23:50 -06003403 entry_name = 'u-boot'
Simon Glassf8a54bc2019-07-20 12:23:56 -06003404 control.WriteEntry(updated_fname, entry_name, expected,
3405 allow_resize=False)
Simon Glass072959a2019-07-20 12:23:50 -06003406 data = control.ReadEntry(updated_fname, entry_name)
3407 self.assertEqual(expected, data)
3408
3409 # Check the state looks right.
3410 self.assertEqual('/binman/first-image', state.fdt_path_prefix)
Simon Glass39dd2152019-07-08 14:25:47 -06003411
Simon Glassfb30e292019-07-20 12:23:51 -06003412 def testUpdateFdtAllRepack(self):
3413 """Test that all device trees are updated with offset/size info"""
Marek Vasutf7413f02023-07-18 07:23:58 -06003414 self._SetupSplElf()
3415 self._SetupTplElf()
Simon Glassfb30e292019-07-20 12:23:51 -06003416 data = self._DoReadFileRealDtb('134_fdt_update_all_repack.dts')
3417 SECTION_SIZE = 0x300
3418 DTB_SIZE = 602
3419 FDTMAP_SIZE = 608
3420 base_expected = {
3421 'offset': 0,
3422 'size': SECTION_SIZE + DTB_SIZE * 2 + FDTMAP_SIZE,
3423 'image-pos': 0,
3424 'section:offset': 0,
3425 'section:size': SECTION_SIZE,
3426 'section:image-pos': 0,
3427 'section/u-boot-dtb:offset': 4,
3428 'section/u-boot-dtb:size': 636,
3429 'section/u-boot-dtb:image-pos': 4,
3430 'u-boot-spl-dtb:offset': SECTION_SIZE,
3431 'u-boot-spl-dtb:size': DTB_SIZE,
3432 'u-boot-spl-dtb:image-pos': SECTION_SIZE,
3433 'u-boot-tpl-dtb:offset': SECTION_SIZE + DTB_SIZE,
3434 'u-boot-tpl-dtb:image-pos': SECTION_SIZE + DTB_SIZE,
3435 'u-boot-tpl-dtb:size': DTB_SIZE,
3436 'fdtmap:offset': SECTION_SIZE + DTB_SIZE * 2,
3437 'fdtmap:size': FDTMAP_SIZE,
3438 'fdtmap:image-pos': SECTION_SIZE + DTB_SIZE * 2,
3439 }
3440 main_expected = {
3441 'section:orig-size': SECTION_SIZE,
3442 'section/u-boot-dtb:orig-offset': 4,
3443 }
3444
3445 # We expect three device-tree files in the output, with the first one
3446 # within a fixed-size section.
3447 # Read them in sequence. We look for an 'spl' property in the SPL tree,
3448 # and 'tpl' in the TPL tree, to make sure they are distinct from the
3449 # main U-Boot tree. All three should have the same positions and offset
3450 # except that the main tree should include the main_expected properties
3451 start = 4
3452 for item in ['', 'spl', 'tpl', None]:
3453 if item is None:
3454 start += 16 # Move past fdtmap header
3455 dtb = fdt.Fdt.FromData(data[start:])
3456 dtb.Scan()
3457 props = self._GetPropTree(dtb,
3458 BASE_DTB_PROPS + REPACK_DTB_PROPS + ['spl', 'tpl'],
3459 prefix='/' if item is None else '/binman/')
3460 expected = dict(base_expected)
3461 if item:
3462 expected[item] = 0
3463 else:
3464 # Main DTB and fdtdec should include the 'orig-' properties
3465 expected.update(main_expected)
3466 # Helpful for debugging:
3467 #for prop in sorted(props):
3468 #print('prop %s %s %s' % (prop, props[prop], expected[prop]))
3469 self.assertEqual(expected, props)
3470 if item == '':
3471 start = SECTION_SIZE
3472 else:
3473 start += dtb._fdt_obj.totalsize()
3474
Simon Glass11453762019-07-20 12:23:55 -06003475 def testFdtmapHeaderMiddle(self):
3476 """Test an FDT map in the middle of an image when it should be at end"""
3477 with self.assertRaises(ValueError) as e:
3478 self._DoReadFileRealDtb('135_fdtmap_hdr_middle.dts')
3479 self.assertIn("Invalid sibling order 'middle' for image-header: Must be at 'end' to match location",
3480 str(e.exception))
3481
3482 def testFdtmapHeaderStartBad(self):
3483 """Test an FDT map in middle of an image when it should be at start"""
3484 with self.assertRaises(ValueError) as e:
3485 self._DoReadFileRealDtb('136_fdtmap_hdr_startbad.dts')
3486 self.assertIn("Invalid sibling order 'end' for image-header: Must be at 'start' to match location",
3487 str(e.exception))
3488
3489 def testFdtmapHeaderEndBad(self):
3490 """Test an FDT map at the start of an image when it should be at end"""
3491 with self.assertRaises(ValueError) as e:
3492 self._DoReadFileRealDtb('137_fdtmap_hdr_endbad.dts')
3493 self.assertIn("Invalid sibling order 'start' for image-header: Must be at 'end' to match location",
3494 str(e.exception))
3495
3496 def testFdtmapHeaderNoSize(self):
3497 """Test an image header at the end of an image with undefined size"""
3498 self._DoReadFileRealDtb('138_fdtmap_hdr_nosize.dts')
3499
Simon Glassf8a54bc2019-07-20 12:23:56 -06003500 def testReplaceResize(self):
3501 """Test replacing a single file in an entry with a larger file"""
3502 expected = U_BOOT_DATA + b'x'
3503 data, _, image = self._RunReplaceCmd('u-boot', expected,
3504 dts='139_replace_repack.dts')
3505 self.assertEqual(expected, data)
3506
3507 entries = image.GetEntries()
3508 dtb_data = entries['u-boot-dtb'].data
3509 dtb = fdt.Fdt.FromData(dtb_data)
3510 dtb.Scan()
3511
3512 # The u-boot section should now be larger in the dtb
3513 node = dtb.GetNode('/binman/u-boot')
3514 self.assertEqual(len(expected), fdt_util.GetInt(node, 'size'))
3515
3516 # Same for the fdtmap
3517 fdata = entries['fdtmap'].data
3518 fdtb = fdt.Fdt.FromData(fdata[fdtmap.FDTMAP_HDR_LEN:])
3519 fdtb.Scan()
3520 fnode = fdtb.GetNode('/u-boot')
3521 self.assertEqual(len(expected), fdt_util.GetInt(fnode, 'size'))
3522
3523 def testReplaceResizeNoRepack(self):
3524 """Test replacing an entry with a larger file when not allowed"""
3525 expected = U_BOOT_DATA + b'x'
3526 with self.assertRaises(ValueError) as e:
3527 self._RunReplaceCmd('u-boot', expected)
3528 self.assertIn('Entry data size does not match, but allow-repack is not present for this image',
3529 str(e.exception))
3530
Simon Glass9d8ee322019-07-20 12:23:58 -06003531 def testEntryShrink(self):
3532 """Test contracting an entry after it is packed"""
3533 try:
3534 state.SetAllowEntryContraction(True)
3535 data = self._DoReadFileDtb('140_entry_shrink.dts',
3536 update_dtb=True)[0]
3537 finally:
3538 state.SetAllowEntryContraction(False)
3539 self.assertEqual(b'a', data[:1])
3540 self.assertEqual(U_BOOT_DATA, data[1:1 + len(U_BOOT_DATA)])
3541 self.assertEqual(b'a', data[-1:])
3542
3543 def testEntryShrinkFail(self):
3544 """Test not being allowed to contract an entry after it is packed"""
3545 data = self._DoReadFileDtb('140_entry_shrink.dts', update_dtb=True)[0]
3546
3547 # In this case there is a spare byte at the end of the data. The size of
3548 # the contents is only 1 byte but we still have the size before it
3549 # shrunk.
3550 self.assertEqual(b'a\0', data[:2])
3551 self.assertEqual(U_BOOT_DATA, data[2:2 + len(U_BOOT_DATA)])
3552 self.assertEqual(b'a\0', data[-2:])
3553
Simon Glass70e32982019-07-20 12:24:01 -06003554 def testDescriptorOffset(self):
3555 """Test that the Intel descriptor is always placed at at the start"""
3556 data = self._DoReadFileDtb('141_descriptor_offset.dts')
3557 image = control.images['image']
3558 entries = image.GetEntries()
3559 desc = entries['intel-descriptor']
Simon Glassed836ac2025-02-26 09:26:17 -07003560 self.assertEqual(0xff800000, desc.offset)
3561 self.assertEqual(0xff800000, desc.image_pos)
Simon Glass70e32982019-07-20 12:24:01 -06003562
Simon Glass37fdd142019-07-20 12:24:06 -06003563 def testReplaceCbfs(self):
3564 """Test replacing a single file in CBFS without changing the size"""
3565 self._CheckLz4()
3566 expected = b'x' * len(U_BOOT_DATA)
3567 data = self._DoReadFileRealDtb('142_replace_cbfs.dts')
Simon Glass80025522022-01-29 14:14:04 -07003568 updated_fname = tools.get_output_filename('image-updated.bin')
3569 tools.write_file(updated_fname, data)
Simon Glass37fdd142019-07-20 12:24:06 -06003570 entry_name = 'section/cbfs/u-boot'
3571 control.WriteEntry(updated_fname, entry_name, expected,
3572 allow_resize=True)
3573 data = control.ReadEntry(updated_fname, entry_name)
3574 self.assertEqual(expected, data)
3575
3576 def testReplaceResizeCbfs(self):
3577 """Test replacing a single file in CBFS with one of a different size"""
3578 self._CheckLz4()
3579 expected = U_BOOT_DATA + b'x'
3580 data = self._DoReadFileRealDtb('142_replace_cbfs.dts')
Simon Glass80025522022-01-29 14:14:04 -07003581 updated_fname = tools.get_output_filename('image-updated.bin')
3582 tools.write_file(updated_fname, data)
Simon Glass37fdd142019-07-20 12:24:06 -06003583 entry_name = 'section/cbfs/u-boot'
3584 control.WriteEntry(updated_fname, entry_name, expected,
3585 allow_resize=True)
3586 data = control.ReadEntry(updated_fname, entry_name)
3587 self.assertEqual(expected, data)
3588
Simon Glass30033c22019-07-20 12:24:15 -06003589 def _SetupForReplace(self):
3590 """Set up some files to use to replace entries
3591
3592 This generates an image, copies it to a new file, extracts all the files
3593 in it and updates some of them
3594
3595 Returns:
3596 List
3597 Image filename
3598 Output directory
3599 Expected values for updated entries, each a string
3600 """
3601 data = self._DoReadFileRealDtb('143_replace_all.dts')
3602
Simon Glass80025522022-01-29 14:14:04 -07003603 updated_fname = tools.get_output_filename('image-updated.bin')
3604 tools.write_file(updated_fname, data)
Simon Glass30033c22019-07-20 12:24:15 -06003605
3606 outdir = os.path.join(self._indir, 'extract')
3607 einfos = control.ExtractEntries(updated_fname, None, outdir, [])
3608
3609 expected1 = b'x' + U_BOOT_DATA + b'y'
3610 u_boot_fname1 = os.path.join(outdir, 'u-boot')
Simon Glass80025522022-01-29 14:14:04 -07003611 tools.write_file(u_boot_fname1, expected1)
Simon Glass30033c22019-07-20 12:24:15 -06003612
3613 expected2 = b'a' + U_BOOT_DATA + b'b'
3614 u_boot_fname2 = os.path.join(outdir, 'u-boot2')
Simon Glass80025522022-01-29 14:14:04 -07003615 tools.write_file(u_boot_fname2, expected2)
Simon Glass30033c22019-07-20 12:24:15 -06003616
3617 expected_text = b'not the same text'
3618 text_fname = os.path.join(outdir, 'text')
Simon Glass80025522022-01-29 14:14:04 -07003619 tools.write_file(text_fname, expected_text)
Simon Glass30033c22019-07-20 12:24:15 -06003620
3621 dtb_fname = os.path.join(outdir, 'u-boot-dtb')
3622 dtb = fdt.FdtScan(dtb_fname)
3623 node = dtb.GetNode('/binman/text')
3624 node.AddString('my-property', 'the value')
3625 dtb.Sync(auto_resize=True)
3626 dtb.Flush()
3627
3628 return updated_fname, outdir, expected1, expected2, expected_text
3629
3630 def _CheckReplaceMultiple(self, entry_paths):
3631 """Handle replacing the contents of multiple entries
3632
3633 Args:
3634 entry_paths: List of entry paths to replace
3635
3636 Returns:
3637 List
3638 Dict of entries in the image:
3639 key: Entry name
3640 Value: Entry object
3641 Expected values for updated entries, each a string
3642 """
3643 updated_fname, outdir, expected1, expected2, expected_text = (
3644 self._SetupForReplace())
3645 control.ReplaceEntries(updated_fname, None, outdir, entry_paths)
3646
3647 image = Image.FromFile(updated_fname)
3648 image.LoadData()
3649 return image.GetEntries(), expected1, expected2, expected_text
3650
3651 def testReplaceAll(self):
3652 """Test replacing the contents of all entries"""
3653 entries, expected1, expected2, expected_text = (
3654 self._CheckReplaceMultiple([]))
3655 data = entries['u-boot'].data
3656 self.assertEqual(expected1, data)
3657
3658 data = entries['u-boot2'].data
3659 self.assertEqual(expected2, data)
3660
3661 data = entries['text'].data
3662 self.assertEqual(expected_text, data)
3663
3664 # Check that the device tree is updated
3665 data = entries['u-boot-dtb'].data
3666 dtb = fdt.Fdt.FromData(data)
3667 dtb.Scan()
3668 node = dtb.GetNode('/binman/text')
3669 self.assertEqual('the value', node.props['my-property'].value)
3670
3671 def testReplaceSome(self):
3672 """Test replacing the contents of a few entries"""
3673 entries, expected1, expected2, expected_text = (
3674 self._CheckReplaceMultiple(['u-boot2', 'text']))
3675
3676 # This one should not change
3677 data = entries['u-boot'].data
3678 self.assertEqual(U_BOOT_DATA, data)
3679
3680 data = entries['u-boot2'].data
3681 self.assertEqual(expected2, data)
3682
3683 data = entries['text'].data
3684 self.assertEqual(expected_text, data)
3685
3686 def testReplaceCmd(self):
3687 """Test replacing a file fron an image on the command line"""
3688 self._DoReadFileRealDtb('143_replace_all.dts')
3689
3690 try:
3691 tmpdir, updated_fname = self._SetupImageInTmpdir()
3692
3693 fname = os.path.join(tmpdir, 'update-u-boot.bin')
3694 expected = b'x' * len(U_BOOT_DATA)
Simon Glass80025522022-01-29 14:14:04 -07003695 tools.write_file(fname, expected)
Simon Glass30033c22019-07-20 12:24:15 -06003696
3697 self._DoBinman('replace', '-i', updated_fname, 'u-boot', '-f', fname)
Simon Glass80025522022-01-29 14:14:04 -07003698 data = tools.read_file(updated_fname)
Simon Glass30033c22019-07-20 12:24:15 -06003699 self.assertEqual(expected, data[:len(expected)])
3700 map_fname = os.path.join(tmpdir, 'image-updated.map')
3701 self.assertFalse(os.path.exists(map_fname))
3702 finally:
3703 shutil.rmtree(tmpdir)
3704
3705 def testReplaceCmdSome(self):
3706 """Test replacing some files fron an image on the command line"""
3707 updated_fname, outdir, expected1, expected2, expected_text = (
3708 self._SetupForReplace())
3709
3710 self._DoBinman('replace', '-i', updated_fname, '-I', outdir,
3711 'u-boot2', 'text')
3712
Simon Glass80025522022-01-29 14:14:04 -07003713 tools.prepare_output_dir(None)
Simon Glass30033c22019-07-20 12:24:15 -06003714 image = Image.FromFile(updated_fname)
3715 image.LoadData()
3716 entries = image.GetEntries()
3717
3718 # This one should not change
3719 data = entries['u-boot'].data
3720 self.assertEqual(U_BOOT_DATA, data)
3721
3722 data = entries['u-boot2'].data
3723 self.assertEqual(expected2, data)
3724
3725 data = entries['text'].data
3726 self.assertEqual(expected_text, data)
3727
3728 def testReplaceMissing(self):
3729 """Test replacing entries where the file is missing"""
3730 updated_fname, outdir, expected1, expected2, expected_text = (
3731 self._SetupForReplace())
3732
3733 # Remove one of the files, to generate a warning
3734 u_boot_fname1 = os.path.join(outdir, 'u-boot')
3735 os.remove(u_boot_fname1)
3736
Simon Glass14d64e32025-04-29 07:21:59 -06003737 with terminal.capture() as (stdout, stderr):
Simon Glass30033c22019-07-20 12:24:15 -06003738 control.ReplaceEntries(updated_fname, None, outdir, [])
3739 self.assertIn("Skipping entry '/u-boot' from missing file",
Simon Glass6e02f7c2020-07-09 18:39:39 -06003740 stderr.getvalue())
Simon Glass30033c22019-07-20 12:24:15 -06003741
3742 def testReplaceCmdMap(self):
3743 """Test replacing a file fron an image on the command line"""
3744 self._DoReadFileRealDtb('143_replace_all.dts')
3745
3746 try:
3747 tmpdir, updated_fname = self._SetupImageInTmpdir()
3748
3749 fname = os.path.join(self._indir, 'update-u-boot.bin')
3750 expected = b'x' * len(U_BOOT_DATA)
Simon Glass80025522022-01-29 14:14:04 -07003751 tools.write_file(fname, expected)
Simon Glass30033c22019-07-20 12:24:15 -06003752
3753 self._DoBinman('replace', '-i', updated_fname, 'u-boot',
3754 '-f', fname, '-m')
3755 map_fname = os.path.join(tmpdir, 'image-updated.map')
3756 self.assertTrue(os.path.exists(map_fname))
3757 finally:
3758 shutil.rmtree(tmpdir)
3759
3760 def testReplaceNoEntryPaths(self):
3761 """Test replacing an entry without an entry path"""
3762 self._DoReadFileRealDtb('143_replace_all.dts')
Simon Glass80025522022-01-29 14:14:04 -07003763 image_fname = tools.get_output_filename('image.bin')
Simon Glass30033c22019-07-20 12:24:15 -06003764 with self.assertRaises(ValueError) as e:
3765 control.ReplaceEntries(image_fname, 'fname', None, [])
3766 self.assertIn('Must specify an entry path to read with -f',
3767 str(e.exception))
3768
3769 def testReplaceTooManyEntryPaths(self):
3770 """Test extracting some entries"""
3771 self._DoReadFileRealDtb('143_replace_all.dts')
Simon Glass80025522022-01-29 14:14:04 -07003772 image_fname = tools.get_output_filename('image.bin')
Simon Glass30033c22019-07-20 12:24:15 -06003773 with self.assertRaises(ValueError) as e:
3774 control.ReplaceEntries(image_fname, 'fname', None, ['a', 'b'])
3775 self.assertIn('Must specify exactly one entry path to write with -f',
3776 str(e.exception))
3777
Simon Glass0b074d62019-08-24 07:22:48 -06003778 def testPackReset16(self):
3779 """Test that an image with an x86 reset16 region can be created"""
3780 data = self._DoReadFile('144_x86_reset16.dts')
3781 self.assertEqual(X86_RESET16_DATA, data[:len(X86_RESET16_DATA)])
3782
3783 def testPackReset16Spl(self):
3784 """Test that an image with an x86 reset16-spl region can be created"""
3785 data = self._DoReadFile('145_x86_reset16_spl.dts')
3786 self.assertEqual(X86_RESET16_SPL_DATA, data[:len(X86_RESET16_SPL_DATA)])
3787
3788 def testPackReset16Tpl(self):
3789 """Test that an image with an x86 reset16-tpl region can be created"""
3790 data = self._DoReadFile('146_x86_reset16_tpl.dts')
3791 self.assertEqual(X86_RESET16_TPL_DATA, data[:len(X86_RESET16_TPL_DATA)])
3792
Simon Glass232f90c2019-08-24 07:22:50 -06003793 def testPackIntelFit(self):
3794 """Test that an image with an Intel FIT and pointer can be created"""
3795 data = self._DoReadFile('147_intel_fit.dts')
3796 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
3797 fit = data[16:32];
3798 self.assertEqual(b'_FIT_ \x01\x00\x00\x00\x00\x01\x80}' , fit)
3799 ptr = struct.unpack('<i', data[0x40:0x44])[0]
3800
3801 image = control.images['image']
3802 entries = image.GetEntries()
Simon Glassed836ac2025-02-26 09:26:17 -07003803 expected_ptr = entries['intel-fit'].image_pos #- (1 << 32)
3804 self.assertEqual(expected_ptr, ptr + (1 << 32))
Simon Glass232f90c2019-08-24 07:22:50 -06003805
3806 def testPackIntelFitMissing(self):
3807 """Test detection of a FIT pointer with not FIT region"""
3808 with self.assertRaises(ValueError) as e:
3809 self._DoReadFile('148_intel_fit_missing.dts')
3810 self.assertIn("'intel-fit-ptr' section must have an 'intel-fit' sibling",
3811 str(e.exception))
3812
Simon Glass72555fa2019-11-06 17:22:44 -07003813 def _CheckSymbolsTplSection(self, dts, expected_vals):
3814 data = self._DoReadFile(dts)
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003815 sym_values = struct.pack('<LLQLL', elf.BINMAN_SYM_MAGIC_VALUE, *expected_vals)
Simon Glass3eb5b202019-08-24 07:23:00 -06003816 upto1 = 4 + len(U_BOOT_SPL_DATA)
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003817 expected1 = tools.get_bytes(0xff, 4) + sym_values + U_BOOT_SPL_DATA[24:]
Simon Glass3eb5b202019-08-24 07:23:00 -06003818 self.assertEqual(expected1, data[:upto1])
3819
3820 upto2 = upto1 + 1 + len(U_BOOT_SPL_DATA)
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003821 expected2 = tools.get_bytes(0xff, 1) + sym_values + U_BOOT_SPL_DATA[24:]
Simon Glass3eb5b202019-08-24 07:23:00 -06003822 self.assertEqual(expected2, data[upto1:upto2])
3823
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003824 upto3 = 0x3c + len(U_BOOT_DATA)
Simon Glass80025522022-01-29 14:14:04 -07003825 expected3 = tools.get_bytes(0xff, 1) + U_BOOT_DATA
Simon Glass3eb5b202019-08-24 07:23:00 -06003826 self.assertEqual(expected3, data[upto2:upto3])
3827
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003828 expected4 = sym_values + U_BOOT_TPL_DATA[24:]
Simon Glass72555fa2019-11-06 17:22:44 -07003829 self.assertEqual(expected4, data[upto3:upto3 + len(U_BOOT_TPL_DATA)])
3830
3831 def testSymbolsTplSection(self):
3832 """Test binman can assign symbols embedded in U-Boot TPL in a section"""
3833 self._SetupSplElf('u_boot_binman_syms')
3834 self._SetupTplElf('u_boot_binman_syms')
3835 self._CheckSymbolsTplSection('149_symbols_tpl.dts',
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003836 [0x04, 0x20, 0x10 + 0x3c, 0x04])
Simon Glass72555fa2019-11-06 17:22:44 -07003837
3838 def testSymbolsTplSectionX86(self):
3839 """Test binman can assign symbols in a section with end-at-4gb"""
3840 self._SetupSplElf('u_boot_binman_syms_x86')
3841 self._SetupTplElf('u_boot_binman_syms_x86')
3842 self._CheckSymbolsTplSection('155_symbols_tpl_x86.dts',
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003843 [0xffffff04, 0xffffff20, 0xffffff3c,
Simon Glass72555fa2019-11-06 17:22:44 -07003844 0x04])
Simon Glass3eb5b202019-08-24 07:23:00 -06003845
Simon Glass98c59572019-08-24 07:23:03 -06003846 def testPackX86RomIfwiSectiom(self):
3847 """Test that a section can be placed in an IFWI region"""
3848 self._SetupIfwi('fitimage.bin')
3849 data = self._DoReadFile('151_x86_rom_ifwi_section.dts')
3850 self._CheckIfwi(data)
3851
Simon Glassba7985d2019-08-24 07:23:07 -06003852 def testPackFspM(self):
3853 """Test that an image with a FSP memory-init binary can be created"""
3854 data = self._DoReadFile('152_intel_fsp_m.dts')
3855 self.assertEqual(FSP_M_DATA, data[:len(FSP_M_DATA)])
3856
Simon Glass4d9086d2019-10-20 21:31:35 -06003857 def testPackFspS(self):
3858 """Test that an image with a FSP silicon-init binary can be created"""
3859 data = self._DoReadFile('153_intel_fsp_s.dts')
3860 self.assertEqual(FSP_S_DATA, data[:len(FSP_S_DATA)])
Simon Glassba7985d2019-08-24 07:23:07 -06003861
Simon Glass9ea87b22019-10-20 21:31:36 -06003862 def testPackFspT(self):
3863 """Test that an image with a FSP temp-ram-init binary can be created"""
3864 data = self._DoReadFile('154_intel_fsp_t.dts')
3865 self.assertEqual(FSP_T_DATA, data[:len(FSP_T_DATA)])
3866
Simon Glass48f3aad2020-07-09 18:39:31 -06003867 def testMkimage(self):
3868 """Test using mkimage to build an image"""
Marek Vasutf7413f02023-07-18 07:23:58 -06003869 self._SetupSplElf()
Simon Glass48f3aad2020-07-09 18:39:31 -06003870 data = self._DoReadFile('156_mkimage.dts')
3871
3872 # Just check that the data appears in the file somewhere
3873 self.assertIn(U_BOOT_SPL_DATA, data)
3874
Simon Glass66152ce2022-01-09 20:14:09 -07003875 def testMkimageMissing(self):
3876 """Test that binman still produces an image if mkimage is missing"""
Marek Vasutf7413f02023-07-18 07:23:58 -06003877 self._SetupSplElf()
Simon Glass14d64e32025-04-29 07:21:59 -06003878 with terminal.capture() as (_, stderr):
Simon Glass66152ce2022-01-09 20:14:09 -07003879 self._DoTestFile('156_mkimage.dts',
3880 force_missing_bintools='mkimage')
3881 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07003882 self.assertRegex(err, "Image 'image'.*missing bintools.*: mkimage")
Simon Glass66152ce2022-01-09 20:14:09 -07003883
Simon Glass5e560182020-07-09 18:39:36 -06003884 def testExtblob(self):
3885 """Test an image with an external blob"""
3886 data = self._DoReadFile('157_blob_ext.dts')
3887 self.assertEqual(REFCODE_DATA, data)
3888
3889 def testExtblobMissing(self):
3890 """Test an image with a missing external blob"""
3891 with self.assertRaises(ValueError) as e:
3892 self._DoReadFile('158_blob_ext_missing.dts')
3893 self.assertIn("Filename 'missing-file' not found in input path",
3894 str(e.exception))
3895
Simon Glass5d94cc62020-07-09 18:39:38 -06003896 def testExtblobMissingOk(self):
3897 """Test an image with an missing external blob that is allowed"""
Simon Glass14d64e32025-04-29 07:21:59 -06003898 with terminal.capture() as (stdout, stderr):
Simon Glass6bce5dc2022-11-09 19:14:42 -07003899 ret = self._DoTestFile('158_blob_ext_missing.dts',
3900 allow_missing=True)
3901 self.assertEqual(103, ret)
Simon Glassa003cd32020-07-09 18:39:40 -06003902 err = stderr.getvalue()
Jonas Karlmanda423fc2023-07-18 20:34:39 +00003903 self.assertIn('(missing-file)', err)
Simon Glass49cd2b32023-02-07 14:34:18 -07003904 self.assertRegex(err, "Image 'image'.*missing.*: blob-ext")
Simon Glass6bce5dc2022-11-09 19:14:42 -07003905 self.assertIn('Some images are invalid', err)
3906
3907 def testExtblobMissingOkFlag(self):
3908 """Test an image with an missing external blob allowed with -W"""
Simon Glass14d64e32025-04-29 07:21:59 -06003909 with terminal.capture() as (stdout, stderr):
Simon Glass6bce5dc2022-11-09 19:14:42 -07003910 ret = self._DoTestFile('158_blob_ext_missing.dts',
3911 allow_missing=True, ignore_missing=True)
3912 self.assertEqual(0, ret)
3913 err = stderr.getvalue()
Jonas Karlmanda423fc2023-07-18 20:34:39 +00003914 self.assertIn('(missing-file)', err)
Simon Glass49cd2b32023-02-07 14:34:18 -07003915 self.assertRegex(err, "Image 'image'.*missing.*: blob-ext")
Simon Glass6bce5dc2022-11-09 19:14:42 -07003916 self.assertIn('Some images are invalid', err)
Simon Glassa003cd32020-07-09 18:39:40 -06003917
3918 def testExtblobMissingOkSect(self):
3919 """Test an image with an missing external blob that is allowed"""
Simon Glass14d64e32025-04-29 07:21:59 -06003920 with terminal.capture() as (stdout, stderr):
Simon Glassa003cd32020-07-09 18:39:40 -06003921 self._DoTestFile('159_blob_ext_missing_sect.dts',
3922 allow_missing=True)
3923 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07003924 self.assertRegex(err, "Image 'image'.*missing.*: blob-ext blob-ext2")
Simon Glass5d94cc62020-07-09 18:39:38 -06003925
Simon Glasse88cef92020-07-09 18:39:41 -06003926 def testPackX86RomMeMissingDesc(self):
3927 """Test that an missing Intel descriptor entry is allowed"""
Simon Glass14d64e32025-04-29 07:21:59 -06003928 with terminal.capture() as (stdout, stderr):
Simon Glass14c596c2020-07-25 15:11:19 -06003929 self._DoTestFile('164_x86_rom_me_missing.dts', allow_missing=True)
Simon Glasse88cef92020-07-09 18:39:41 -06003930 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07003931 self.assertRegex(err, "Image 'image'.*missing.*: intel-descriptor")
Simon Glasse88cef92020-07-09 18:39:41 -06003932
3933 def testPackX86RomMissingIfwi(self):
3934 """Test that an x86 ROM with Integrated Firmware Image can be created"""
3935 self._SetupIfwi('fitimage.bin')
3936 pathname = os.path.join(self._indir, 'fitimage.bin')
3937 os.remove(pathname)
Simon Glass14d64e32025-04-29 07:21:59 -06003938 with terminal.capture() as (stdout, stderr):
Simon Glasse88cef92020-07-09 18:39:41 -06003939 self._DoTestFile('111_x86_rom_ifwi.dts', allow_missing=True)
3940 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07003941 self.assertRegex(err, "Image 'image'.*missing.*: intel-ifwi")
Simon Glasse88cef92020-07-09 18:39:41 -06003942
Simon Glass2a0fa982022-02-11 13:23:21 -07003943 def testPackOverlapZero(self):
Simon Glassd70829a2020-07-09 18:39:42 -06003944 """Test that zero-size overlapping regions are ignored"""
3945 self._DoTestFile('160_pack_overlap_zero.dts')
3946
Alper Nebi Yasak09fb0612022-02-08 01:08:04 +03003947 def _CheckSimpleFitData(self, fit_data, kernel_data, fdt1_data):
Simon Glass45d556d2020-07-09 18:39:45 -06003948 # The data should be inside the FIT
3949 dtb = fdt.Fdt.FromData(fit_data)
3950 dtb.Scan()
3951 fnode = dtb.GetNode('/images/kernel')
3952 self.assertIn('data', fnode.props)
3953
3954 fname = os.path.join(self._indir, 'fit_data.fit')
Simon Glass80025522022-01-29 14:14:04 -07003955 tools.write_file(fname, fit_data)
3956 out = tools.run('dumpimage', '-l', fname)
Simon Glass45d556d2020-07-09 18:39:45 -06003957
3958 # Check a few features to make sure the plumbing works. We don't need
3959 # to test the operation of mkimage or dumpimage here. First convert the
3960 # output into a dict where the keys are the fields printed by dumpimage
3961 # and the values are a list of values for each field
3962 lines = out.splitlines()
3963
3964 # Converts "Compression: gzip compressed" into two groups:
3965 # 'Compression' and 'gzip compressed'
3966 re_line = re.compile(r'^ *([^:]*)(?:: *(.*))?$')
3967 vals = collections.defaultdict(list)
3968 for line in lines:
3969 mat = re_line.match(line)
3970 vals[mat.group(1)].append(mat.group(2))
3971
Brandon Maiera657bc62024-06-04 16:16:05 +00003972 self.assertEqual('FIT description: test-desc', lines[0])
Simon Glass45d556d2020-07-09 18:39:45 -06003973 self.assertIn('Created:', lines[1])
3974 self.assertIn('Image 0 (kernel)', vals)
3975 self.assertIn('Hash value', vals)
3976 data_sizes = vals.get('Data Size')
3977 self.assertIsNotNone(data_sizes)
3978 self.assertEqual(2, len(data_sizes))
3979 # Format is "4 Bytes = 0.00 KiB = 0.00 MiB" so take the first word
Alper Nebi Yasak09fb0612022-02-08 01:08:04 +03003980 self.assertEqual(len(kernel_data), int(data_sizes[0].split()[0]))
3981 self.assertEqual(len(fdt1_data), int(data_sizes[1].split()[0]))
3982
Alper Nebi Yasak1a0ee0f2022-03-27 18:31:47 +03003983 # Check if entry listing correctly omits /images/
3984 image = control.images['image']
3985 fit_entry = image.GetEntries()['fit']
3986 subentries = list(fit_entry.GetEntries().keys())
3987 expected = ['kernel', 'fdt-1']
3988 self.assertEqual(expected, subentries)
3989
Alper Nebi Yasak09fb0612022-02-08 01:08:04 +03003990 def testSimpleFit(self):
3991 """Test an image with a FIT inside"""
Marek Vasutf7413f02023-07-18 07:23:58 -06003992 self._SetupSplElf()
Alper Nebi Yasak09fb0612022-02-08 01:08:04 +03003993 data = self._DoReadFile('161_fit.dts')
3994 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
3995 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
3996 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
3997
3998 self._CheckSimpleFitData(fit_data, U_BOOT_DATA, U_BOOT_SPL_DTB_DATA)
3999
4000 def testSimpleFitExpandsSubentries(self):
4001 """Test that FIT images expand their subentries"""
4002 data = self._DoReadFileDtb('161_fit.dts', use_expanded=True)[0]
4003 self.assertEqual(U_BOOT_EXP_DATA, data[:len(U_BOOT_EXP_DATA)])
4004 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
4005 fit_data = data[len(U_BOOT_EXP_DATA):-len(U_BOOT_NODTB_DATA)]
4006
4007 self._CheckSimpleFitData(fit_data, U_BOOT_EXP_DATA, U_BOOT_SPL_DTB_DATA)
Simon Glass45d556d2020-07-09 18:39:45 -06004008
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03004009 def testSimpleFitImagePos(self):
4010 """Test that we have correct image-pos for FIT subentries"""
4011 data, _, _, out_dtb_fname = self._DoReadFileDtb('161_fit.dts',
4012 update_dtb=True)
4013 dtb = fdt.Fdt(out_dtb_fname)
4014 dtb.Scan()
4015 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
4016
Simon Glassb7bad182022-03-05 20:19:01 -07004017 self.maxDiff = None
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03004018 self.assertEqual({
4019 'image-pos': 0,
4020 'offset': 0,
4021 'size': 1890,
4022
4023 'u-boot:image-pos': 0,
4024 'u-boot:offset': 0,
4025 'u-boot:size': 4,
4026
4027 'fit:image-pos': 4,
4028 'fit:offset': 4,
4029 'fit:size': 1840,
4030
Simon Glassb7bad182022-03-05 20:19:01 -07004031 'fit/images/kernel:image-pos': 304,
4032 'fit/images/kernel:offset': 300,
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03004033 'fit/images/kernel:size': 4,
4034
Simon Glassb7bad182022-03-05 20:19:01 -07004035 'fit/images/kernel/u-boot:image-pos': 304,
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03004036 'fit/images/kernel/u-boot:offset': 0,
4037 'fit/images/kernel/u-boot:size': 4,
4038
Simon Glassb7bad182022-03-05 20:19:01 -07004039 'fit/images/fdt-1:image-pos': 552,
4040 'fit/images/fdt-1:offset': 548,
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03004041 'fit/images/fdt-1:size': 6,
4042
Simon Glassb7bad182022-03-05 20:19:01 -07004043 'fit/images/fdt-1/u-boot-spl-dtb:image-pos': 552,
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03004044 'fit/images/fdt-1/u-boot-spl-dtb:offset': 0,
4045 'fit/images/fdt-1/u-boot-spl-dtb:size': 6,
4046
4047 'u-boot-nodtb:image-pos': 1844,
4048 'u-boot-nodtb:offset': 1844,
4049 'u-boot-nodtb:size': 46,
4050 }, props)
4051
4052 # Actually check the data is where we think it is
4053 for node, expected in [
4054 ("u-boot", U_BOOT_DATA),
4055 ("fit/images/kernel", U_BOOT_DATA),
4056 ("fit/images/kernel/u-boot", U_BOOT_DATA),
4057 ("fit/images/fdt-1", U_BOOT_SPL_DTB_DATA),
4058 ("fit/images/fdt-1/u-boot-spl-dtb", U_BOOT_SPL_DTB_DATA),
4059 ("u-boot-nodtb", U_BOOT_NODTB_DATA),
4060 ]:
4061 image_pos = props[f"{node}:image-pos"]
4062 size = props[f"{node}:size"]
4063 self.assertEqual(len(expected), size)
4064 self.assertEqual(expected, data[image_pos:image_pos+size])
4065
Simon Glass45d556d2020-07-09 18:39:45 -06004066 def testFitExternal(self):
Simon Glass31ee50f2020-09-01 05:13:55 -06004067 """Test an image with an FIT with external images"""
Simon Glass45d556d2020-07-09 18:39:45 -06004068 data = self._DoReadFile('162_fit_external.dts')
4069 fit_data = data[len(U_BOOT_DATA):-2] # _testing is 2 bytes
4070
Simon Glass7932c882022-01-09 20:13:39 -07004071 # Size of the external-data region as set up by mkimage
4072 external_data_size = len(U_BOOT_DATA) + 2
4073 expected_size = (len(U_BOOT_DATA) + 0x400 +
Simon Glass80025522022-01-29 14:14:04 -07004074 tools.align(external_data_size, 4) +
Simon Glass7932c882022-01-09 20:13:39 -07004075 len(U_BOOT_NODTB_DATA))
4076
Simon Glass45d556d2020-07-09 18:39:45 -06004077 # The data should be outside the FIT
4078 dtb = fdt.Fdt.FromData(fit_data)
4079 dtb.Scan()
4080 fnode = dtb.GetNode('/images/kernel')
4081 self.assertNotIn('data', fnode.props)
Simon Glass7932c882022-01-09 20:13:39 -07004082 self.assertEqual(len(U_BOOT_DATA),
4083 fdt_util.fdt32_to_cpu(fnode.props['data-size'].value))
4084 fit_pos = 0x400;
4085 self.assertEqual(
4086 fit_pos,
4087 fdt_util.fdt32_to_cpu(fnode.props['data-position'].value))
4088
Brandon Maiera657bc62024-06-04 16:16:05 +00004089 self.assertEqual(expected_size, len(data))
Simon Glass7932c882022-01-09 20:13:39 -07004090 actual_pos = len(U_BOOT_DATA) + fit_pos
4091 self.assertEqual(U_BOOT_DATA + b'aa',
4092 data[actual_pos:actual_pos + external_data_size])
Simon Glassfb30e292019-07-20 12:23:51 -06004093
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03004094 def testFitExternalImagePos(self):
4095 """Test that we have correct image-pos for external FIT subentries"""
4096 data, _, _, out_dtb_fname = self._DoReadFileDtb('162_fit_external.dts',
4097 update_dtb=True)
4098 dtb = fdt.Fdt(out_dtb_fname)
4099 dtb.Scan()
4100 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
4101
4102 self.assertEqual({
4103 'image-pos': 0,
4104 'offset': 0,
4105 'size': 1082,
4106
4107 'u-boot:image-pos': 0,
4108 'u-boot:offset': 0,
4109 'u-boot:size': 4,
4110
4111 'fit:size': 1032,
4112 'fit:offset': 4,
4113 'fit:image-pos': 4,
4114
4115 'fit/images/kernel:size': 4,
4116 'fit/images/kernel:offset': 1024,
4117 'fit/images/kernel:image-pos': 1028,
4118
4119 'fit/images/kernel/u-boot:size': 4,
4120 'fit/images/kernel/u-boot:offset': 0,
4121 'fit/images/kernel/u-boot:image-pos': 1028,
4122
4123 'fit/images/fdt-1:size': 2,
4124 'fit/images/fdt-1:offset': 1028,
4125 'fit/images/fdt-1:image-pos': 1032,
4126
4127 'fit/images/fdt-1/_testing:size': 2,
4128 'fit/images/fdt-1/_testing:offset': 0,
4129 'fit/images/fdt-1/_testing:image-pos': 1032,
4130
4131 'u-boot-nodtb:image-pos': 1036,
4132 'u-boot-nodtb:offset': 1036,
4133 'u-boot-nodtb:size': 46,
4134 }, props)
4135
4136 # Actually check the data is where we think it is
4137 for node, expected in [
4138 ("u-boot", U_BOOT_DATA),
4139 ("fit/images/kernel", U_BOOT_DATA),
4140 ("fit/images/kernel/u-boot", U_BOOT_DATA),
4141 ("fit/images/fdt-1", b'aa'),
4142 ("fit/images/fdt-1/_testing", b'aa'),
4143 ("u-boot-nodtb", U_BOOT_NODTB_DATA),
4144 ]:
4145 image_pos = props[f"{node}:image-pos"]
4146 size = props[f"{node}:size"]
4147 self.assertEqual(len(expected), size)
4148 self.assertEqual(expected, data[image_pos:image_pos+size])
4149
Simon Glass66152ce2022-01-09 20:14:09 -07004150 def testFitMissing(self):
Simon Glass039d65f2023-03-02 17:02:43 -07004151 """Test that binman complains if mkimage is missing"""
4152 with self.assertRaises(ValueError) as e:
4153 self._DoTestFile('162_fit_external.dts',
4154 force_missing_bintools='mkimage')
4155 self.assertIn("Node '/binman/fit': Missing tool: 'mkimage'",
4156 str(e.exception))
4157
4158 def testFitMissingOK(self):
Simon Glass66152ce2022-01-09 20:14:09 -07004159 """Test that binman still produces a FIT image if mkimage is missing"""
Simon Glass14d64e32025-04-29 07:21:59 -06004160 with terminal.capture() as (_, stderr):
Simon Glass039d65f2023-03-02 17:02:43 -07004161 self._DoTestFile('162_fit_external.dts', allow_missing=True,
Simon Glass66152ce2022-01-09 20:14:09 -07004162 force_missing_bintools='mkimage')
4163 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07004164 self.assertRegex(err, "Image 'image'.*missing bintools.*: mkimage")
Simon Glass66152ce2022-01-09 20:14:09 -07004165
Alper Nebi Yasak6aae2392020-08-31 12:58:18 +03004166 def testSectionIgnoreHashSignature(self):
4167 """Test that sections ignore hash, signature nodes for its data"""
4168 data = self._DoReadFile('165_section_ignore_hash_signature.dts')
4169 expected = (U_BOOT_DATA + U_BOOT_DATA)
4170 self.assertEqual(expected, data)
4171
Alper Nebi Yasakc5030602020-08-31 12:58:19 +03004172 def testPadInSections(self):
4173 """Test pad-before, pad-after for entries in sections"""
Simon Glassd12599d2020-10-26 17:40:09 -06004174 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4175 '166_pad_in_sections.dts', update_dtb=True)
Simon Glass80025522022-01-29 14:14:04 -07004176 expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) +
4177 U_BOOT_DATA + tools.get_bytes(ord('!'), 6) +
Alper Nebi Yasakc5030602020-08-31 12:58:19 +03004178 U_BOOT_DATA)
4179 self.assertEqual(expected, data)
4180
Simon Glassd12599d2020-10-26 17:40:09 -06004181 dtb = fdt.Fdt(out_dtb_fname)
4182 dtb.Scan()
4183 props = self._GetPropTree(dtb, ['size', 'image-pos', 'offset'])
4184 expected = {
4185 'image-pos': 0,
4186 'offset': 0,
4187 'size': 12 + 6 + 3 * len(U_BOOT_DATA),
4188
4189 'section:image-pos': 0,
4190 'section:offset': 0,
4191 'section:size': 12 + 6 + 3 * len(U_BOOT_DATA),
4192
4193 'section/before:image-pos': 0,
4194 'section/before:offset': 0,
4195 'section/before:size': len(U_BOOT_DATA),
4196
4197 'section/u-boot:image-pos': 4,
4198 'section/u-boot:offset': 4,
4199 'section/u-boot:size': 12 + len(U_BOOT_DATA) + 6,
4200
4201 'section/after:image-pos': 26,
4202 'section/after:offset': 26,
4203 'section/after:size': len(U_BOOT_DATA),
4204 }
4205 self.assertEqual(expected, props)
4206
Alper Nebi Yasakc261ec12020-08-31 12:58:20 +03004207 def testFitImageSubentryAlignment(self):
4208 """Test relative alignability of FIT image subentries"""
Alper Nebi Yasakd4553262022-02-08 01:08:07 +03004209 self._SetupSplElf()
Alper Nebi Yasakc261ec12020-08-31 12:58:20 +03004210 entry_args = {
4211 'test-id': TEXT_DATA,
4212 }
4213 data, _, _, _ = self._DoReadFileDtb('167_fit_image_subentry_alignment.dts',
4214 entry_args=entry_args)
4215 dtb = fdt.Fdt.FromData(data)
4216 dtb.Scan()
4217
4218 node = dtb.GetNode('/images/kernel')
4219 data = dtb.GetProps(node)["data"].bytes
4220 align_pad = 0x10 - (len(U_BOOT_SPL_DATA) % 0x10)
Simon Glass80025522022-01-29 14:14:04 -07004221 expected = (tools.get_bytes(0, 0x20) + U_BOOT_SPL_DATA +
4222 tools.get_bytes(0, align_pad) + U_BOOT_DATA)
Alper Nebi Yasakc261ec12020-08-31 12:58:20 +03004223 self.assertEqual(expected, data)
4224
4225 node = dtb.GetNode('/images/fdt-1')
4226 data = dtb.GetProps(node)["data"].bytes
Simon Glass80025522022-01-29 14:14:04 -07004227 expected = (U_BOOT_SPL_DTB_DATA + tools.get_bytes(0, 20) +
4228 tools.to_bytes(TEXT_DATA) + tools.get_bytes(0, 30) +
Alper Nebi Yasakc261ec12020-08-31 12:58:20 +03004229 U_BOOT_DTB_DATA)
4230 self.assertEqual(expected, data)
4231
4232 def testFitExtblobMissingOk(self):
4233 """Test a FIT with a missing external blob that is allowed"""
Simon Glass14d64e32025-04-29 07:21:59 -06004234 with terminal.capture() as (stdout, stderr):
Alper Nebi Yasakc261ec12020-08-31 12:58:20 +03004235 self._DoTestFile('168_fit_missing_blob.dts',
4236 allow_missing=True)
4237 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07004238 self.assertRegex(err, "Image 'image'.*missing.*: atf-bl31")
Alper Nebi Yasakc261ec12020-08-31 12:58:20 +03004239
Simon Glass21db0ff2020-09-01 05:13:54 -06004240 def testBlobNamedByArgMissing(self):
4241 """Test handling of a missing entry arg"""
4242 with self.assertRaises(ValueError) as e:
4243 self._DoReadFile('068_blob_named_by_arg.dts')
4244 self.assertIn("Missing required properties/entry args: cros-ec-rw-path",
4245 str(e.exception))
4246
Simon Glass559c4de2020-09-01 05:13:58 -06004247 def testPackBl31(self):
4248 """Test that an image with an ATF BL31 binary can be created"""
4249 data = self._DoReadFile('169_atf_bl31.dts')
4250 self.assertEqual(ATF_BL31_DATA, data[:len(ATF_BL31_DATA)])
4251
Samuel Holland9d8cc632020-10-21 21:12:15 -05004252 def testPackScp(self):
4253 """Test that an image with an SCP binary can be created"""
4254 data = self._DoReadFile('172_scp.dts')
4255 self.assertEqual(SCP_DATA, data[:len(SCP_DATA)])
4256
Simon Glassd2a9d6e2024-08-26 13:11:37 -06004257 def CheckFitFdt(self, dts='170_fit_fdt.dts', use_fdt_list=True,
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004258 default_dt=None, use_seq_num=True):
Simon Glasscd2783e2024-07-20 11:49:46 +01004259 """Check an image with an FIT with multiple FDT images"""
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004260 def _CheckFdt(val, expected_data):
Simon Glassa435cd12020-09-01 05:13:59 -06004261 """Check the FDT nodes
4262
4263 Args:
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004264 val: Sequence number to check (0 or 1) or fdt name
Simon Glassa435cd12020-09-01 05:13:59 -06004265 expected_data: Expected contents of 'data' property
4266 """
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004267 name = 'fdt-%s' % val
Simon Glassa435cd12020-09-01 05:13:59 -06004268 fnode = dtb.GetNode('/images/%s' % name)
4269 self.assertIsNotNone(fnode)
4270 self.assertEqual({'description','type', 'compression', 'data'},
4271 set(fnode.props.keys()))
4272 self.assertEqual(expected_data, fnode.props['data'].bytes)
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004273 description = (
4274 'fdt-test-fdt%s.dtb' % val if len(val) == 1 else
4275 'fdt-%s.dtb' % val
4276 )
4277 self.assertEqual(description, fnode.props['description'].value)
Jan Kiszkaa1419df2022-02-28 17:06:20 +01004278 self.assertEqual(fnode.subnodes[0].name, 'hash')
Simon Glassa435cd12020-09-01 05:13:59 -06004279
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004280 def _CheckConfig(val, expected_data):
Simon Glassa435cd12020-09-01 05:13:59 -06004281 """Check the configuration nodes
4282
4283 Args:
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004284 val: Sequence number to check (0 or 1) or fdt name
Simon Glassa435cd12020-09-01 05:13:59 -06004285 expected_data: Expected contents of 'data' property
4286 """
4287 cnode = dtb.GetNode('/configurations')
4288 self.assertIn('default', cnode.props)
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004289 default = (
4290 'config-2' if len(val) == 1 else
4291 'config-test-fdt2'
4292 )
4293 self.assertEqual(default, cnode.props['default'].value)
Simon Glassa435cd12020-09-01 05:13:59 -06004294
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004295 name = 'config-%s' % val
Simon Glassa435cd12020-09-01 05:13:59 -06004296 fnode = dtb.GetNode('/configurations/%s' % name)
4297 self.assertIsNotNone(fnode)
4298 self.assertEqual({'description','firmware', 'loadables', 'fdt'},
4299 set(fnode.props.keys()))
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004300 description = (
4301 'conf-test-fdt%s.dtb' % val if len(val) == 1 else
4302 'conf-%s.dtb' % val
4303 )
4304 self.assertEqual(description, fnode.props['description'].value)
4305 self.assertEqual('fdt-%s' % val, fnode.props['fdt'].value)
Simon Glassa435cd12020-09-01 05:13:59 -06004306
4307 entry_args = {
Simon Glass1032acc2020-09-06 10:39:08 -06004308 'default-dt': 'test-fdt2',
Simon Glassa435cd12020-09-01 05:13:59 -06004309 }
Paul HENRYS87a8e7c2024-11-25 19:16:54 +01004310 extra_indirs = None
Simon Glasscd2783e2024-07-20 11:49:46 +01004311 if use_fdt_list:
4312 entry_args['of-list'] = 'test-fdt1 test-fdt2'
Simon Glassd2a9d6e2024-08-26 13:11:37 -06004313 if default_dt:
4314 entry_args['default-dt'] = default_dt
Paul HENRYS87a8e7c2024-11-25 19:16:54 +01004315 if use_fdt_list:
4316 extra_indirs = [os.path.join(self._indir, TEST_FDT_SUBDIR)]
Simon Glassa435cd12020-09-01 05:13:59 -06004317 data = self._DoReadFileDtb(
Simon Glasscd2783e2024-07-20 11:49:46 +01004318 dts,
Simon Glassa435cd12020-09-01 05:13:59 -06004319 entry_args=entry_args,
Paul HENRYS87a8e7c2024-11-25 19:16:54 +01004320 extra_indirs=extra_indirs)[0]
Simon Glassa435cd12020-09-01 05:13:59 -06004321 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
4322 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
4323
4324 dtb = fdt.Fdt.FromData(fit_data)
4325 dtb.Scan()
4326 fnode = dtb.GetNode('/images/kernel')
4327 self.assertIn('data', fnode.props)
4328
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004329 if use_seq_num == True:
4330 # Check all the properties in fdt-1 and fdt-2
4331 _CheckFdt('1', TEST_FDT1_DATA)
4332 _CheckFdt('2', TEST_FDT2_DATA)
Simon Glassa435cd12020-09-01 05:13:59 -06004333
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004334 # Check configurations
4335 _CheckConfig('1', TEST_FDT1_DATA)
4336 _CheckConfig('2', TEST_FDT2_DATA)
4337 else:
4338 # Check all the properties in fdt-1 and fdt-2
4339 _CheckFdt('test-fdt1', TEST_FDT1_DATA)
4340 _CheckFdt('test-fdt2', TEST_FDT2_DATA)
4341
4342 # Check configurations
4343 _CheckConfig('test-fdt1', TEST_FDT1_DATA)
4344 _CheckConfig('test-fdt2', TEST_FDT2_DATA)
Simon Glassa435cd12020-09-01 05:13:59 -06004345
Simon Glasscd2783e2024-07-20 11:49:46 +01004346 def testFitFdt(self):
4347 """Test an image with an FIT with multiple FDT images"""
4348 self.CheckFitFdt()
4349
Simon Glassa435cd12020-09-01 05:13:59 -06004350 def testFitFdtMissingList(self):
4351 """Test handling of a missing 'of-list' entry arg"""
4352 with self.assertRaises(ValueError) as e:
Bin Meng16cf5662021-05-10 20:23:32 +08004353 self._DoReadFile('170_fit_fdt.dts')
Simon Glassa435cd12020-09-01 05:13:59 -06004354 self.assertIn("Generator node requires 'of-list' entry argument",
4355 str(e.exception))
4356
4357 def testFitFdtEmptyList(self):
4358 """Test handling of an empty 'of-list' entry arg"""
4359 entry_args = {
4360 'of-list': '',
4361 }
4362 data = self._DoReadFileDtb('170_fit_fdt.dts', entry_args=entry_args)[0]
4363
4364 def testFitFdtMissingProp(self):
4365 """Test handling of a missing 'fit,fdt-list' property"""
4366 with self.assertRaises(ValueError) as e:
4367 self._DoReadFile('171_fit_fdt_missing_prop.dts')
4368 self.assertIn("Generator node requires 'fit,fdt-list' property",
4369 str(e.exception))
Simon Glass559c4de2020-09-01 05:13:58 -06004370
Simon Glass1032acc2020-09-06 10:39:08 -06004371 def testFitFdtMissing(self):
4372 """Test handling of a missing 'default-dt' entry arg"""
4373 entry_args = {
4374 'of-list': 'test-fdt1 test-fdt2',
4375 }
4376 with self.assertRaises(ValueError) as e:
4377 self._DoReadFileDtb(
Bin Meng16cf5662021-05-10 20:23:32 +08004378 '170_fit_fdt.dts',
Simon Glass1032acc2020-09-06 10:39:08 -06004379 entry_args=entry_args,
4380 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
4381 self.assertIn("Generated 'default' node requires default-dt entry argument",
4382 str(e.exception))
4383
4384 def testFitFdtNotInList(self):
4385 """Test handling of a default-dt that is not in the of-list"""
4386 entry_args = {
4387 'of-list': 'test-fdt1 test-fdt2',
4388 'default-dt': 'test-fdt3',
4389 }
4390 with self.assertRaises(ValueError) as e:
4391 self._DoReadFileDtb(
Bin Meng16cf5662021-05-10 20:23:32 +08004392 '170_fit_fdt.dts',
Simon Glass1032acc2020-09-06 10:39:08 -06004393 entry_args=entry_args,
4394 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
4395 self.assertIn("default-dt entry argument 'test-fdt3' not found in fdt list: test-fdt1, test-fdt2",
4396 str(e.exception))
4397
Simon Glassa820af72020-09-06 10:39:09 -06004398 def testFitExtblobMissingHelp(self):
4399 """Test display of help messages when an external blob is missing"""
4400 control.missing_blob_help = control._ReadMissingBlobHelp()
4401 control.missing_blob_help['wibble'] = 'Wibble test'
4402 control.missing_blob_help['another'] = 'Another test'
Simon Glass14d64e32025-04-29 07:21:59 -06004403 with terminal.capture() as (stdout, stderr):
Simon Glassa820af72020-09-06 10:39:09 -06004404 self._DoTestFile('168_fit_missing_blob.dts',
4405 allow_missing=True)
4406 err = stderr.getvalue()
4407
4408 # We can get the tag from the name, the type or the missing-msg
4409 # property. Check all three.
4410 self.assertIn('You may need to build ARM Trusted', err)
4411 self.assertIn('Wibble test', err)
4412 self.assertIn('Another test', err)
4413
Simon Glass6f1f4d42020-09-06 10:35:32 -06004414 def testMissingBlob(self):
4415 """Test handling of a blob containing a missing file"""
4416 with self.assertRaises(ValueError) as e:
4417 self._DoTestFile('173_missing_blob.dts', allow_missing=True)
4418 self.assertIn("Filename 'missing' not found in input path",
4419 str(e.exception))
4420
Simon Glassa0729502020-09-06 10:35:33 -06004421 def testEnvironment(self):
4422 """Test adding a U-Boot environment"""
4423 data = self._DoReadFile('174_env.dts')
4424 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
4425 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
4426 env = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
4427 self.assertEqual(b'\x1b\x97\x22\x7c\x01var1=1\0var2="2"\0\0\xff\xff',
4428 env)
4429
4430 def testEnvironmentNoSize(self):
4431 """Test that a missing 'size' property is detected"""
4432 with self.assertRaises(ValueError) as e:
Simon Glass8cdc08a2020-10-26 17:40:00 -06004433 self._DoTestFile('175_env_no_size.dts')
Simon Glassa0729502020-09-06 10:35:33 -06004434 self.assertIn("'u-boot-env' entry must have a size property",
4435 str(e.exception))
4436
4437 def testEnvironmentTooSmall(self):
4438 """Test handling of an environment that does not fit"""
4439 with self.assertRaises(ValueError) as e:
Simon Glass8cdc08a2020-10-26 17:40:00 -06004440 self._DoTestFile('176_env_too_small.dts')
Simon Glassa0729502020-09-06 10:35:33 -06004441
4442 # checksum, start byte, environment with \0 terminator, final \0
4443 need = 4 + 1 + len(ENV_DATA) + 1 + 1
4444 short = need - 0x8
4445 self.assertIn("too small to hold data (need %#x more bytes)" % short,
4446 str(e.exception))
4447
Simon Glassd1fdf752020-10-26 17:40:01 -06004448 def testSkipAtStart(self):
4449 """Test handling of skip-at-start section"""
4450 data = self._DoReadFile('177_skip_at_start.dts')
4451 self.assertEqual(U_BOOT_DATA, data)
4452
4453 image = control.images['image']
4454 entries = image.GetEntries()
4455 section = entries['section']
4456 self.assertEqual(0, section.offset)
4457 self.assertEqual(len(U_BOOT_DATA), section.size)
4458 self.assertEqual(U_BOOT_DATA, section.GetData())
4459
4460 entry = section.GetEntries()['u-boot']
4461 self.assertEqual(16, entry.offset)
4462 self.assertEqual(len(U_BOOT_DATA), entry.size)
4463 self.assertEqual(U_BOOT_DATA, entry.data)
4464
4465 def testSkipAtStartPad(self):
4466 """Test handling of skip-at-start section with padded entry"""
4467 data = self._DoReadFile('178_skip_at_start_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07004468 before = tools.get_bytes(0, 8)
4469 after = tools.get_bytes(0, 4)
Simon Glassd1fdf752020-10-26 17:40:01 -06004470 all = before + U_BOOT_DATA + after
4471 self.assertEqual(all, data)
4472
4473 image = control.images['image']
4474 entries = image.GetEntries()
4475 section = entries['section']
4476 self.assertEqual(0, section.offset)
4477 self.assertEqual(len(all), section.size)
4478 self.assertEqual(all, section.GetData())
4479
4480 entry = section.GetEntries()['u-boot']
4481 self.assertEqual(16, entry.offset)
4482 self.assertEqual(len(all), entry.size)
4483 self.assertEqual(U_BOOT_DATA, entry.data)
4484
4485 def testSkipAtStartSectionPad(self):
4486 """Test handling of skip-at-start section with padding"""
4487 data = self._DoReadFile('179_skip_at_start_section_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07004488 before = tools.get_bytes(0, 8)
4489 after = tools.get_bytes(0, 4)
Simon Glassd1fdf752020-10-26 17:40:01 -06004490 all = before + U_BOOT_DATA + after
Simon Glass510ef0f2020-10-26 17:40:13 -06004491 self.assertEqual(all, data)
Simon Glassd1fdf752020-10-26 17:40:01 -06004492
4493 image = control.images['image']
4494 entries = image.GetEntries()
4495 section = entries['section']
4496 self.assertEqual(0, section.offset)
4497 self.assertEqual(len(all), section.size)
Simon Glass72eeff12020-10-26 17:40:16 -06004498 self.assertEqual(U_BOOT_DATA, section.data)
Simon Glass510ef0f2020-10-26 17:40:13 -06004499 self.assertEqual(all, section.GetPaddedData())
Simon Glassd1fdf752020-10-26 17:40:01 -06004500
4501 entry = section.GetEntries()['u-boot']
4502 self.assertEqual(16, entry.offset)
4503 self.assertEqual(len(U_BOOT_DATA), entry.size)
4504 self.assertEqual(U_BOOT_DATA, entry.data)
Simon Glassa0729502020-09-06 10:35:33 -06004505
Simon Glassbb395742020-10-26 17:40:14 -06004506 def testSectionPad(self):
4507 """Testing padding with sections"""
4508 data = self._DoReadFile('180_section_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07004509 expected = (tools.get_bytes(ord('&'), 3) +
4510 tools.get_bytes(ord('!'), 5) +
Simon Glassbb395742020-10-26 17:40:14 -06004511 U_BOOT_DATA +
Simon Glass80025522022-01-29 14:14:04 -07004512 tools.get_bytes(ord('!'), 1) +
4513 tools.get_bytes(ord('&'), 2))
Simon Glassbb395742020-10-26 17:40:14 -06004514 self.assertEqual(expected, data)
4515
4516 def testSectionAlign(self):
4517 """Testing alignment with sections"""
4518 data = self._DoReadFileDtb('181_section_align.dts', map=True)[0]
4519 expected = (b'\0' + # fill section
Simon Glass80025522022-01-29 14:14:04 -07004520 tools.get_bytes(ord('&'), 1) + # padding to section align
Simon Glassbb395742020-10-26 17:40:14 -06004521 b'\0' + # fill section
Simon Glass80025522022-01-29 14:14:04 -07004522 tools.get_bytes(ord('!'), 3) + # padding to u-boot align
Simon Glassbb395742020-10-26 17:40:14 -06004523 U_BOOT_DATA +
Simon Glass80025522022-01-29 14:14:04 -07004524 tools.get_bytes(ord('!'), 4) + # padding to u-boot size
4525 tools.get_bytes(ord('!'), 4)) # padding to section size
Simon Glassbb395742020-10-26 17:40:14 -06004526 self.assertEqual(expected, data)
4527
Simon Glassd92c8362020-10-26 17:40:25 -06004528 def testCompressImage(self):
4529 """Test compression of the entire image"""
4530 self._CheckLz4()
4531 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4532 '182_compress_image.dts', use_real_dtb=True, update_dtb=True)
4533 dtb = fdt.Fdt(out_dtb_fname)
4534 dtb.Scan()
4535 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4536 'uncomp-size'])
4537 orig = self._decompress(data)
Brandon Maiera657bc62024-06-04 16:16:05 +00004538 self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, orig)
Simon Glassd92c8362020-10-26 17:40:25 -06004539
4540 # Do a sanity check on various fields
4541 image = control.images['image']
4542 entries = image.GetEntries()
4543 self.assertEqual(2, len(entries))
4544
4545 entry = entries['blob']
4546 self.assertEqual(COMPRESS_DATA, entry.data)
4547 self.assertEqual(len(COMPRESS_DATA), entry.size)
4548
4549 entry = entries['u-boot']
4550 self.assertEqual(U_BOOT_DATA, entry.data)
4551 self.assertEqual(len(U_BOOT_DATA), entry.size)
4552
4553 self.assertEqual(len(data), image.size)
4554 self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, image.uncomp_data)
4555 self.assertEqual(len(COMPRESS_DATA + U_BOOT_DATA), image.uncomp_size)
4556 orig = self._decompress(image.data)
4557 self.assertEqual(orig, image.uncomp_data)
4558
4559 expected = {
4560 'blob:offset': 0,
4561 'blob:size': len(COMPRESS_DATA),
4562 'u-boot:offset': len(COMPRESS_DATA),
4563 'u-boot:size': len(U_BOOT_DATA),
4564 'uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4565 'offset': 0,
4566 'image-pos': 0,
4567 'size': len(data),
4568 }
4569 self.assertEqual(expected, props)
4570
4571 def testCompressImageLess(self):
4572 """Test compression where compression reduces the image size"""
4573 self._CheckLz4()
4574 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4575 '183_compress_image_less.dts', use_real_dtb=True, update_dtb=True)
4576 dtb = fdt.Fdt(out_dtb_fname)
4577 dtb.Scan()
4578 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4579 'uncomp-size'])
4580 orig = self._decompress(data)
4581
Brandon Maiera657bc62024-06-04 16:16:05 +00004582 self.assertEqual(COMPRESS_DATA + COMPRESS_DATA + U_BOOT_DATA, orig)
Simon Glassd92c8362020-10-26 17:40:25 -06004583
4584 # Do a sanity check on various fields
4585 image = control.images['image']
4586 entries = image.GetEntries()
4587 self.assertEqual(2, len(entries))
4588
4589 entry = entries['blob']
4590 self.assertEqual(COMPRESS_DATA_BIG, entry.data)
4591 self.assertEqual(len(COMPRESS_DATA_BIG), entry.size)
4592
4593 entry = entries['u-boot']
4594 self.assertEqual(U_BOOT_DATA, entry.data)
4595 self.assertEqual(len(U_BOOT_DATA), entry.size)
4596
4597 self.assertEqual(len(data), image.size)
4598 self.assertEqual(COMPRESS_DATA_BIG + U_BOOT_DATA, image.uncomp_data)
4599 self.assertEqual(len(COMPRESS_DATA_BIG + U_BOOT_DATA),
4600 image.uncomp_size)
4601 orig = self._decompress(image.data)
4602 self.assertEqual(orig, image.uncomp_data)
4603
4604 expected = {
4605 'blob:offset': 0,
4606 'blob:size': len(COMPRESS_DATA_BIG),
4607 'u-boot:offset': len(COMPRESS_DATA_BIG),
4608 'u-boot:size': len(U_BOOT_DATA),
4609 'uncomp-size': len(COMPRESS_DATA_BIG + U_BOOT_DATA),
4610 'offset': 0,
4611 'image-pos': 0,
4612 'size': len(data),
4613 }
4614 self.assertEqual(expected, props)
4615
4616 def testCompressSectionSize(self):
4617 """Test compression of a section with a fixed size"""
4618 self._CheckLz4()
4619 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4620 '184_compress_section_size.dts', use_real_dtb=True, update_dtb=True)
4621 dtb = fdt.Fdt(out_dtb_fname)
4622 dtb.Scan()
4623 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4624 'uncomp-size'])
Jiaxun Yangc6931742025-04-10 06:43:03 -06004625 data = data[:0x30]
4626 data = data.rstrip(b'\xff')
Simon Glassd92c8362020-10-26 17:40:25 -06004627 orig = self._decompress(data)
Brandon Maiera657bc62024-06-04 16:16:05 +00004628 self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, orig)
Simon Glassd92c8362020-10-26 17:40:25 -06004629 expected = {
4630 'section/blob:offset': 0,
4631 'section/blob:size': len(COMPRESS_DATA),
4632 'section/u-boot:offset': len(COMPRESS_DATA),
4633 'section/u-boot:size': len(U_BOOT_DATA),
4634 'section:offset': 0,
4635 'section:image-pos': 0,
4636 'section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4637 'section:size': 0x30,
4638 'offset': 0,
4639 'image-pos': 0,
4640 'size': 0x30,
4641 }
4642 self.assertEqual(expected, props)
4643
4644 def testCompressSection(self):
4645 """Test compression of a section with no fixed size"""
4646 self._CheckLz4()
4647 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4648 '185_compress_section.dts', use_real_dtb=True, update_dtb=True)
4649 dtb = fdt.Fdt(out_dtb_fname)
4650 dtb.Scan()
4651 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4652 'uncomp-size'])
4653 orig = self._decompress(data)
Brandon Maiera657bc62024-06-04 16:16:05 +00004654 self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, orig)
Simon Glassd92c8362020-10-26 17:40:25 -06004655 expected = {
4656 'section/blob:offset': 0,
4657 'section/blob:size': len(COMPRESS_DATA),
4658 'section/u-boot:offset': len(COMPRESS_DATA),
4659 'section/u-boot:size': len(U_BOOT_DATA),
4660 'section:offset': 0,
4661 'section:image-pos': 0,
4662 'section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4663 'section:size': len(data),
4664 'offset': 0,
4665 'image-pos': 0,
4666 'size': len(data),
4667 }
4668 self.assertEqual(expected, props)
4669
Stefan Herbrechtsmeier94813a02022-08-19 16:25:31 +02004670 def testLz4Missing(self):
4671 """Test that binman still produces an image if lz4 is missing"""
Simon Glass14d64e32025-04-29 07:21:59 -06004672 with terminal.capture() as (_, stderr):
Stefan Herbrechtsmeier94813a02022-08-19 16:25:31 +02004673 self._DoTestFile('185_compress_section.dts',
4674 force_missing_bintools='lz4')
4675 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07004676 self.assertRegex(err, "Image 'image'.*missing bintools.*: lz4")
Stefan Herbrechtsmeier94813a02022-08-19 16:25:31 +02004677
Simon Glassd92c8362020-10-26 17:40:25 -06004678 def testCompressExtra(self):
4679 """Test compression of a section with no fixed size"""
4680 self._CheckLz4()
4681 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4682 '186_compress_extra.dts', use_real_dtb=True, update_dtb=True)
4683 dtb = fdt.Fdt(out_dtb_fname)
4684 dtb.Scan()
4685 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4686 'uncomp-size'])
4687
4688 base = data[len(U_BOOT_DATA):]
Brandon Maiera657bc62024-06-04 16:16:05 +00004689 self.assertEqual(U_BOOT_DATA, base[:len(U_BOOT_DATA)])
Simon Glassd92c8362020-10-26 17:40:25 -06004690 rest = base[len(U_BOOT_DATA):]
4691
4692 # Check compressed data
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +02004693 bintool = self.comp_bintools['lz4']
4694 expect1 = bintool.compress(COMPRESS_DATA + U_BOOT_DATA)
Stefan Herbrechtsmeier86f1fc02022-08-19 16:25:23 +02004695 data1 = rest[:len(expect1)]
4696 section1 = self._decompress(data1)
Brandon Maiera657bc62024-06-04 16:16:05 +00004697 self.assertEqual(expect1, data1)
4698 self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, section1)
Simon Glassd92c8362020-10-26 17:40:25 -06004699 rest1 = rest[len(expect1):]
4700
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +02004701 expect2 = bintool.compress(COMPRESS_DATA + COMPRESS_DATA)
Stefan Herbrechtsmeier86f1fc02022-08-19 16:25:23 +02004702 data2 = rest1[:len(expect2)]
4703 section2 = self._decompress(data2)
Brandon Maiera657bc62024-06-04 16:16:05 +00004704 self.assertEqual(expect2, data2)
4705 self.assertEqual(COMPRESS_DATA + COMPRESS_DATA, section2)
Simon Glassd92c8362020-10-26 17:40:25 -06004706 rest2 = rest1[len(expect2):]
4707
4708 expect_size = (len(U_BOOT_DATA) + len(U_BOOT_DATA) + len(expect1) +
4709 len(expect2) + len(U_BOOT_DATA))
Brandon Maiera657bc62024-06-04 16:16:05 +00004710 #self.assertEqual(expect_size, len(data))
Simon Glassd92c8362020-10-26 17:40:25 -06004711
Brandon Maiera657bc62024-06-04 16:16:05 +00004712 #self.assertEqual(U_BOOT_DATA, rest2)
Simon Glassd92c8362020-10-26 17:40:25 -06004713
4714 self.maxDiff = None
4715 expected = {
4716 'u-boot:offset': 0,
4717 'u-boot:image-pos': 0,
4718 'u-boot:size': len(U_BOOT_DATA),
4719
4720 'base:offset': len(U_BOOT_DATA),
4721 'base:image-pos': len(U_BOOT_DATA),
4722 'base:size': len(data) - len(U_BOOT_DATA),
4723 'base/u-boot:offset': 0,
4724 'base/u-boot:image-pos': len(U_BOOT_DATA),
4725 'base/u-boot:size': len(U_BOOT_DATA),
4726 'base/u-boot2:offset': len(U_BOOT_DATA) + len(expect1) +
4727 len(expect2),
4728 'base/u-boot2:image-pos': len(U_BOOT_DATA) * 2 + len(expect1) +
4729 len(expect2),
4730 'base/u-boot2:size': len(U_BOOT_DATA),
4731
4732 'base/section:offset': len(U_BOOT_DATA),
4733 'base/section:image-pos': len(U_BOOT_DATA) * 2,
4734 'base/section:size': len(expect1),
4735 'base/section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4736 'base/section/blob:offset': 0,
4737 'base/section/blob:size': len(COMPRESS_DATA),
4738 'base/section/u-boot:offset': len(COMPRESS_DATA),
4739 'base/section/u-boot:size': len(U_BOOT_DATA),
4740
4741 'base/section2:offset': len(U_BOOT_DATA) + len(expect1),
4742 'base/section2:image-pos': len(U_BOOT_DATA) * 2 + len(expect1),
4743 'base/section2:size': len(expect2),
4744 'base/section2:uncomp-size': len(COMPRESS_DATA + COMPRESS_DATA),
4745 'base/section2/blob:offset': 0,
4746 'base/section2/blob:size': len(COMPRESS_DATA),
4747 'base/section2/blob2:offset': len(COMPRESS_DATA),
4748 'base/section2/blob2:size': len(COMPRESS_DATA),
4749
4750 'offset': 0,
4751 'image-pos': 0,
4752 'size': len(data),
4753 }
4754 self.assertEqual(expected, props)
4755
Simon Glassecbe4732021-01-06 21:35:15 -07004756 def testSymbolsSubsection(self):
4757 """Test binman can assign symbols from a subsection"""
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03004758 self.checkSymbols('187_symbols_sub.dts', U_BOOT_SPL_DATA, 0x1c)
Simon Glassecbe4732021-01-06 21:35:15 -07004759
Simon Glass3fb25402021-01-06 21:35:16 -07004760 def testReadImageEntryArg(self):
4761 """Test reading an image that would need an entry arg to generate"""
4762 entry_args = {
4763 'cros-ec-rw-path': 'ecrw.bin',
4764 }
4765 data = self.data = self._DoReadFileDtb(
4766 '188_image_entryarg.dts',use_real_dtb=True, update_dtb=True,
4767 entry_args=entry_args)
4768
Simon Glass80025522022-01-29 14:14:04 -07004769 image_fname = tools.get_output_filename('image.bin')
Simon Glass3fb25402021-01-06 21:35:16 -07004770 orig_image = control.images['image']
4771
4772 # This should not generate an error about the missing 'cros-ec-rw-path'
4773 # since we are reading the image from a file. Compare with
4774 # testEntryArgsRequired()
4775 image = Image.FromFile(image_fname)
4776 self.assertEqual(orig_image.GetEntries().keys(),
4777 image.GetEntries().keys())
4778
Simon Glassa2af7302021-01-06 21:35:18 -07004779 def testFilesAlign(self):
4780 """Test alignment with files"""
4781 data = self._DoReadFile('190_files_align.dts')
4782
4783 # The first string is 15 bytes so will align to 16
4784 expect = FILES_DATA[:15] + b'\0' + FILES_DATA[15:]
4785 self.assertEqual(expect, data)
4786
Simon Glassdb84b562021-01-06 21:35:19 -07004787 def testReadImageSkip(self):
4788 """Test reading an image and accessing its FDT map"""
4789 data = self.data = self._DoReadFileRealDtb('191_read_image_skip.dts')
Simon Glass80025522022-01-29 14:14:04 -07004790 image_fname = tools.get_output_filename('image.bin')
Simon Glassdb84b562021-01-06 21:35:19 -07004791 orig_image = control.images['image']
4792 image = Image.FromFile(image_fname)
4793 self.assertEqual(orig_image.GetEntries().keys(),
4794 image.GetEntries().keys())
4795
4796 orig_entry = orig_image.GetEntries()['fdtmap']
4797 entry = image.GetEntries()['fdtmap']
4798 self.assertEqual(orig_entry.offset, entry.offset)
4799 self.assertEqual(orig_entry.size, entry.size)
Simon Glassed836ac2025-02-26 09:26:17 -07004800 self.assertEqual((1 << 32) - 0x400 + 16, entry.image_pos)
Simon Glassdb84b562021-01-06 21:35:19 -07004801
4802 u_boot = image.GetEntries()['section'].GetEntries()['u-boot']
4803
Brandon Maiera657bc62024-06-04 16:16:05 +00004804 self.assertEqual(U_BOOT_DATA, u_boot.ReadData())
Simon Glassdb84b562021-01-06 21:35:19 -07004805
Simon Glassc98de972021-03-18 20:24:57 +13004806 def testTplNoDtb(self):
4807 """Test that an image with tpl/u-boot-tpl-nodtb.bin can be created"""
Simon Glass13089cc2021-04-25 08:39:32 +12004808 self._SetupTplElf()
Simon Glassc98de972021-03-18 20:24:57 +13004809 data = self._DoReadFile('192_u_boot_tpl_nodtb.dts')
4810 self.assertEqual(U_BOOT_TPL_NODTB_DATA,
4811 data[:len(U_BOOT_TPL_NODTB_DATA)])
4812
Simon Glass63f41d42021-03-18 20:24:58 +13004813 def testTplBssPad(self):
4814 """Test that we can pad TPL's BSS with zeros"""
4815 # ELF file with a '__bss_size' symbol
4816 self._SetupTplElf()
4817 data = self._DoReadFile('193_tpl_bss_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07004818 self.assertEqual(U_BOOT_TPL_DATA + tools.get_bytes(0, 10) + U_BOOT_DATA,
Simon Glass63f41d42021-03-18 20:24:58 +13004819 data)
4820
4821 def testTplBssPadMissing(self):
4822 """Test that a missing symbol is detected"""
4823 self._SetupTplElf('u_boot_ucode_ptr')
4824 with self.assertRaises(ValueError) as e:
4825 self._DoReadFile('193_tpl_bss_pad.dts')
4826 self.assertIn('Expected __bss_size symbol in tpl/u-boot-tpl',
4827 str(e.exception))
4828
Simon Glass718b5292021-03-18 20:25:07 +13004829 def checkDtbSizes(self, data, pad_len, start):
4830 """Check the size arguments in a dtb embedded in an image
4831
4832 Args:
4833 data: The image data
4834 pad_len: Length of the pad section in the image, in bytes
4835 start: Start offset of the devicetree to examine, within the image
4836
4837 Returns:
4838 Size of the devicetree in bytes
4839 """
4840 dtb_data = data[start:]
4841 dtb = fdt.Fdt.FromData(dtb_data)
4842 fdt_size = dtb.GetFdtObj().totalsize()
4843 dtb.Scan()
4844 props = self._GetPropTree(dtb, 'size')
4845 self.assertEqual({
4846 'size': len(data),
4847 'u-boot-spl/u-boot-spl-bss-pad:size': pad_len,
4848 'u-boot-spl/u-boot-spl-dtb:size': 801,
4849 'u-boot-spl/u-boot-spl-nodtb:size': len(U_BOOT_SPL_NODTB_DATA),
4850 'u-boot-spl:size': 860,
4851 'u-boot-tpl:size': len(U_BOOT_TPL_DATA),
4852 'u-boot/u-boot-dtb:size': 781,
4853 'u-boot/u-boot-nodtb:size': len(U_BOOT_NODTB_DATA),
4854 'u-boot:size': 827,
4855 }, props)
4856 return fdt_size
4857
4858 def testExpanded(self):
4859 """Test that an expanded entry type is selected when needed"""
4860 self._SetupSplElf()
4861 self._SetupTplElf()
4862
4863 # SPL has a devicetree, TPL does not
4864 entry_args = {
4865 'spl-dtb': '1',
4866 'spl-bss-pad': 'y',
4867 'tpl-dtb': '',
4868 }
4869 self._DoReadFileDtb('194_fdt_incl.dts', use_expanded=True,
4870 entry_args=entry_args)
4871 image = control.images['image']
4872 entries = image.GetEntries()
4873 self.assertEqual(3, len(entries))
4874
4875 # First, u-boot, which should be expanded into u-boot-nodtb and dtb
4876 self.assertIn('u-boot', entries)
4877 entry = entries['u-boot']
4878 self.assertEqual('u-boot-expanded', entry.etype)
4879 subent = entry.GetEntries()
4880 self.assertEqual(2, len(subent))
4881 self.assertIn('u-boot-nodtb', subent)
4882 self.assertIn('u-boot-dtb', subent)
4883
4884 # Second, u-boot-spl, which should be expanded into three parts
4885 self.assertIn('u-boot-spl', entries)
4886 entry = entries['u-boot-spl']
4887 self.assertEqual('u-boot-spl-expanded', entry.etype)
4888 subent = entry.GetEntries()
4889 self.assertEqual(3, len(subent))
4890 self.assertIn('u-boot-spl-nodtb', subent)
4891 self.assertIn('u-boot-spl-bss-pad', subent)
4892 self.assertIn('u-boot-spl-dtb', subent)
4893
4894 # Third, u-boot-tpl, which should be not be expanded, since TPL has no
4895 # devicetree
4896 self.assertIn('u-boot-tpl', entries)
4897 entry = entries['u-boot-tpl']
4898 self.assertEqual('u-boot-tpl', entry.etype)
4899 self.assertEqual(None, entry.GetEntries())
4900
4901 def testExpandedTpl(self):
4902 """Test that an expanded entry type is selected for TPL when needed"""
4903 self._SetupTplElf()
4904
4905 entry_args = {
4906 'tpl-bss-pad': 'y',
4907 'tpl-dtb': 'y',
4908 }
4909 self._DoReadFileDtb('195_fdt_incl_tpl.dts', use_expanded=True,
4910 entry_args=entry_args)
4911 image = control.images['image']
4912 entries = image.GetEntries()
4913 self.assertEqual(1, len(entries))
4914
4915 # We only have u-boot-tpl, which be expanded
4916 self.assertIn('u-boot-tpl', entries)
4917 entry = entries['u-boot-tpl']
4918 self.assertEqual('u-boot-tpl-expanded', entry.etype)
4919 subent = entry.GetEntries()
4920 self.assertEqual(3, len(subent))
4921 self.assertIn('u-boot-tpl-nodtb', subent)
4922 self.assertIn('u-boot-tpl-bss-pad', subent)
4923 self.assertIn('u-boot-tpl-dtb', subent)
4924
4925 def testExpandedNoPad(self):
4926 """Test an expanded entry without BSS pad enabled"""
4927 self._SetupSplElf()
4928 self._SetupTplElf()
4929
4930 # SPL has a devicetree, TPL does not
4931 entry_args = {
4932 'spl-dtb': 'something',
4933 'spl-bss-pad': 'n',
4934 'tpl-dtb': '',
4935 }
4936 self._DoReadFileDtb('194_fdt_incl.dts', use_expanded=True,
4937 entry_args=entry_args)
4938 image = control.images['image']
4939 entries = image.GetEntries()
4940
4941 # Just check u-boot-spl, which should be expanded into two parts
4942 self.assertIn('u-boot-spl', entries)
4943 entry = entries['u-boot-spl']
4944 self.assertEqual('u-boot-spl-expanded', entry.etype)
4945 subent = entry.GetEntries()
4946 self.assertEqual(2, len(subent))
4947 self.assertIn('u-boot-spl-nodtb', subent)
4948 self.assertIn('u-boot-spl-dtb', subent)
4949
4950 def testExpandedTplNoPad(self):
4951 """Test that an expanded entry type with padding disabled in TPL"""
4952 self._SetupTplElf()
4953
4954 entry_args = {
4955 'tpl-bss-pad': '',
4956 'tpl-dtb': 'y',
4957 }
4958 self._DoReadFileDtb('195_fdt_incl_tpl.dts', use_expanded=True,
4959 entry_args=entry_args)
4960 image = control.images['image']
4961 entries = image.GetEntries()
4962 self.assertEqual(1, len(entries))
4963
4964 # We only have u-boot-tpl, which be expanded
4965 self.assertIn('u-boot-tpl', entries)
4966 entry = entries['u-boot-tpl']
4967 self.assertEqual('u-boot-tpl-expanded', entry.etype)
4968 subent = entry.GetEntries()
4969 self.assertEqual(2, len(subent))
4970 self.assertIn('u-boot-tpl-nodtb', subent)
4971 self.assertIn('u-boot-tpl-dtb', subent)
4972
4973 def testFdtInclude(self):
4974 """Test that an Fdt is update within all binaries"""
4975 self._SetupSplElf()
4976 self._SetupTplElf()
4977
4978 # SPL has a devicetree, TPL does not
4979 self.maxDiff = None
4980 entry_args = {
4981 'spl-dtb': '1',
4982 'spl-bss-pad': 'y',
4983 'tpl-dtb': '',
4984 }
4985 # Build the image. It includes two separate devicetree binaries, each
4986 # with their own contents, but all contain the binman definition.
4987 data = self._DoReadFileDtb(
4988 '194_fdt_incl.dts', use_real_dtb=True, use_expanded=True,
4989 update_dtb=True, entry_args=entry_args)[0]
4990 pad_len = 10
4991
4992 # Check the U-Boot dtb
4993 start = len(U_BOOT_NODTB_DATA)
4994 fdt_size = self.checkDtbSizes(data, pad_len, start)
4995
4996 # Now check SPL
4997 start += fdt_size + len(U_BOOT_SPL_NODTB_DATA) + pad_len
4998 fdt_size = self.checkDtbSizes(data, pad_len, start)
4999
5000 # TPL has no devicetree
5001 start += fdt_size + len(U_BOOT_TPL_DATA)
5002 self.assertEqual(len(data), start)
Simon Glassbb395742020-10-26 17:40:14 -06005003
Simon Glass7098b7f2021-03-21 18:24:30 +13005004 def testSymbolsExpanded(self):
5005 """Test binman can assign symbols in expanded entries"""
5006 entry_args = {
5007 'spl-dtb': '1',
5008 }
5009 self.checkSymbols('197_symbols_expand.dts', U_BOOT_SPL_NODTB_DATA +
5010 U_BOOT_SPL_DTB_DATA, 0x38,
5011 entry_args=entry_args, use_expanded=True)
5012
Simon Glasse1915782021-03-21 18:24:31 +13005013 def testCollection(self):
5014 """Test a collection"""
5015 data = self._DoReadFile('198_collection.dts')
5016 self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA +
Simon Glass80025522022-01-29 14:14:04 -07005017 tools.get_bytes(0xff, 2) + U_BOOT_NODTB_DATA +
5018 tools.get_bytes(0xfe, 3) + U_BOOT_DTB_DATA,
Simon Glasse1915782021-03-21 18:24:31 +13005019 data)
5020
Simon Glass27a7f772021-03-21 18:24:32 +13005021 def testCollectionSection(self):
5022 """Test a collection where a section must be built first"""
5023 # Sections never have their contents when GetData() is called, but when
Simon Glass7e3f89f2021-11-23 11:03:47 -07005024 # BuildSectionData() is called with required=True, a section will force
Simon Glass27a7f772021-03-21 18:24:32 +13005025 # building the contents, producing an error is anything is still
5026 # missing.
5027 data = self._DoReadFile('199_collection_section.dts')
5028 section = U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA
Simon Glass80025522022-01-29 14:14:04 -07005029 self.assertEqual(section + U_BOOT_DATA + tools.get_bytes(0xff, 2) +
5030 section + tools.get_bytes(0xfe, 3) + U_BOOT_DATA,
Simon Glass27a7f772021-03-21 18:24:32 +13005031 data)
5032
Simon Glassf427c5f2021-03-21 18:24:33 +13005033 def testAlignDefault(self):
5034 """Test that default alignment works on sections"""
5035 data = self._DoReadFile('200_align_default.dts')
Simon Glass80025522022-01-29 14:14:04 -07005036 expected = (U_BOOT_DATA + tools.get_bytes(0, 8 - len(U_BOOT_DATA)) +
Simon Glassf427c5f2021-03-21 18:24:33 +13005037 U_BOOT_DATA)
5038 # Special alignment for section
Simon Glass80025522022-01-29 14:14:04 -07005039 expected += tools.get_bytes(0, 32 - len(expected))
Simon Glassf427c5f2021-03-21 18:24:33 +13005040 # No alignment within the nested section
5041 expected += U_BOOT_DATA + U_BOOT_NODTB_DATA;
5042 # Now the final piece, which should be default-aligned
Simon Glass80025522022-01-29 14:14:04 -07005043 expected += tools.get_bytes(0, 88 - len(expected)) + U_BOOT_NODTB_DATA
Simon Glassf427c5f2021-03-21 18:24:33 +13005044 self.assertEqual(expected, data)
Simon Glass27a7f772021-03-21 18:24:32 +13005045
Bin Mengc0b15742021-05-10 20:23:33 +08005046 def testPackOpenSBI(self):
5047 """Test that an image with an OpenSBI binary can be created"""
5048 data = self._DoReadFile('201_opensbi.dts')
5049 self.assertEqual(OPENSBI_DATA, data[:len(OPENSBI_DATA)])
5050
Simon Glass76f496d2021-07-06 10:36:37 -06005051 def testSectionsSingleThread(self):
5052 """Test sections without multithreading"""
5053 data = self._DoReadFileDtb('055_sections.dts', threads=0)[0]
Simon Glass80025522022-01-29 14:14:04 -07005054 expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) +
5055 U_BOOT_DATA + tools.get_bytes(ord('a'), 12) +
5056 U_BOOT_DATA + tools.get_bytes(ord('&'), 4))
Simon Glass76f496d2021-07-06 10:36:37 -06005057 self.assertEqual(expected, data)
5058
5059 def testThreadTimeout(self):
5060 """Test handling a thread that takes too long"""
5061 with self.assertRaises(ValueError) as e:
5062 self._DoTestFile('202_section_timeout.dts',
5063 test_section_timeout=True)
Simon Glass2d59d152021-10-18 12:13:15 -06005064 self.assertIn("Timed out obtaining contents", str(e.exception))
Simon Glass76f496d2021-07-06 10:36:37 -06005065
Simon Glass748a1d42021-07-06 10:36:41 -06005066 def testTiming(self):
5067 """Test output of timing information"""
5068 data = self._DoReadFile('055_sections.dts')
Simon Glass14d64e32025-04-29 07:21:59 -06005069 with terminal.capture() as (stdout, stderr):
Simon Glass748a1d42021-07-06 10:36:41 -06005070 state.TimingShow()
5071 self.assertIn('read:', stdout.getvalue())
5072 self.assertIn('compress:', stdout.getvalue())
5073
Simon Glassadfb8492021-11-03 21:09:18 -06005074 def testUpdateFdtInElf(self):
5075 """Test that we can update the devicetree in an ELF file"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005076 if not elf.ELF_TOOLS:
5077 self.skipTest('Python elftools not available')
Simon Glassadfb8492021-11-03 21:09:18 -06005078 infile = elf_fname = self.ElfTestFile('u_boot_binman_embed')
5079 outfile = os.path.join(self._indir, 'u-boot.out')
5080 begin_sym = 'dtb_embed_begin'
5081 end_sym = 'dtb_embed_end'
5082 retcode = self._DoTestFile(
5083 '060_fdt_update.dts', update_dtb=True,
5084 update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym]))
5085 self.assertEqual(0, retcode)
5086
5087 # Check that the output file does in fact contact a dtb with the binman
5088 # definition in the correct place
5089 syms = elf.GetSymbolFileOffset(infile,
5090 ['dtb_embed_begin', 'dtb_embed_end'])
Simon Glass80025522022-01-29 14:14:04 -07005091 data = tools.read_file(outfile)
Simon Glassadfb8492021-11-03 21:09:18 -06005092 dtb_data = data[syms['dtb_embed_begin'].offset:
5093 syms['dtb_embed_end'].offset]
5094
5095 dtb = fdt.Fdt.FromData(dtb_data)
5096 dtb.Scan()
5097 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
5098 self.assertEqual({
5099 'image-pos': 0,
5100 'offset': 0,
5101 '_testing:offset': 32,
5102 '_testing:size': 2,
5103 '_testing:image-pos': 32,
5104 'section@0/u-boot:offset': 0,
5105 'section@0/u-boot:size': len(U_BOOT_DATA),
5106 'section@0/u-boot:image-pos': 0,
5107 'section@0:offset': 0,
5108 'section@0:size': 16,
5109 'section@0:image-pos': 0,
5110
5111 'section@1/u-boot:offset': 0,
5112 'section@1/u-boot:size': len(U_BOOT_DATA),
5113 'section@1/u-boot:image-pos': 16,
5114 'section@1:offset': 16,
5115 'section@1:size': 16,
5116 'section@1:image-pos': 16,
5117 'size': 40
5118 }, props)
5119
5120 def testUpdateFdtInElfInvalid(self):
5121 """Test that invalid args are detected with --update-fdt-in-elf"""
5122 with self.assertRaises(ValueError) as e:
5123 self._DoTestFile('060_fdt_update.dts', update_fdt_in_elf='fred')
5124 self.assertIn("Invalid args ['fred'] to --update-fdt-in-elf",
5125 str(e.exception))
5126
5127 def testUpdateFdtInElfNoSyms(self):
5128 """Test that missing symbols are detected with --update-fdt-in-elf"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005129 if not elf.ELF_TOOLS:
5130 self.skipTest('Python elftools not available')
Simon Glassadfb8492021-11-03 21:09:18 -06005131 infile = elf_fname = self.ElfTestFile('u_boot_binman_embed')
5132 outfile = ''
5133 begin_sym = 'wrong_begin'
5134 end_sym = 'wrong_end'
5135 with self.assertRaises(ValueError) as e:
5136 self._DoTestFile(
5137 '060_fdt_update.dts',
5138 update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym]))
5139 self.assertIn("Expected two symbols 'wrong_begin' and 'wrong_end': got 0:",
5140 str(e.exception))
5141
5142 def testUpdateFdtInElfTooSmall(self):
5143 """Test that an over-large dtb is detected with --update-fdt-in-elf"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005144 if not elf.ELF_TOOLS:
5145 self.skipTest('Python elftools not available')
Simon Glassadfb8492021-11-03 21:09:18 -06005146 infile = elf_fname = self.ElfTestFile('u_boot_binman_embed_sm')
5147 outfile = os.path.join(self._indir, 'u-boot.out')
5148 begin_sym = 'dtb_embed_begin'
5149 end_sym = 'dtb_embed_end'
5150 with self.assertRaises(ValueError) as e:
5151 self._DoTestFile(
5152 '060_fdt_update.dts', update_dtb=True,
5153 update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym]))
5154 self.assertRegex(
5155 str(e.exception),
5156 "Not enough space in '.*u_boot_binman_embed_sm' for data length.*")
5157
Simon Glass88e04da2021-11-23 11:03:42 -07005158 def testVersion(self):
5159 """Test we can get the binman version"""
5160 version = '(unreleased)'
5161 self.assertEqual(version, state.GetVersion(self._indir))
5162
5163 with self.assertRaises(SystemExit):
Simon Glass14d64e32025-04-29 07:21:59 -06005164 with terminal.capture() as (_, stderr):
Simon Glass88e04da2021-11-23 11:03:42 -07005165 self._DoBinman('-V')
5166 self.assertEqual('Binman %s\n' % version, stderr.getvalue())
5167
5168 # Try running the tool too, just to be safe
5169 result = self._RunBinman('-V')
5170 self.assertEqual('Binman %s\n' % version, result.stderr)
5171
5172 # Set up a version file to make sure that works
5173 version = 'v2025.01-rc2'
Simon Glass80025522022-01-29 14:14:04 -07005174 tools.write_file(os.path.join(self._indir, 'version'), version,
Simon Glass88e04da2021-11-23 11:03:42 -07005175 binary=False)
5176 self.assertEqual(version, state.GetVersion(self._indir))
5177
Simon Glass637958f2021-11-23 21:09:50 -07005178 def testAltFormat(self):
5179 """Test that alternative formats can be used to extract"""
5180 self._DoReadFileRealDtb('213_fdtmap_alt_format.dts')
5181
5182 try:
5183 tmpdir, updated_fname = self._SetupImageInTmpdir()
Simon Glass14d64e32025-04-29 07:21:59 -06005184 with terminal.capture() as (stdout, _):
Simon Glass637958f2021-11-23 21:09:50 -07005185 self._DoBinman('extract', '-i', updated_fname, '-F', 'list')
5186 self.assertEqual(
5187 '''Flag (-F) Entry type Description
5188fdt fdtmap Extract the devicetree blob from the fdtmap
5189''',
5190 stdout.getvalue())
5191
5192 dtb = os.path.join(tmpdir, 'fdt.dtb')
5193 self._DoBinman('extract', '-i', updated_fname, '-F', 'fdt', '-f',
5194 dtb, 'fdtmap')
5195
5196 # Check that we can read it and it can be scanning, meaning it does
5197 # not have a 16-byte fdtmap header
Simon Glass80025522022-01-29 14:14:04 -07005198 data = tools.read_file(dtb)
Simon Glass637958f2021-11-23 21:09:50 -07005199 dtb = fdt.Fdt.FromData(data)
5200 dtb.Scan()
5201
5202 # Now check u-boot which has no alt_format
5203 fname = os.path.join(tmpdir, 'fdt.dtb')
5204 self._DoBinman('extract', '-i', updated_fname, '-F', 'dummy',
5205 '-f', fname, 'u-boot')
Simon Glass80025522022-01-29 14:14:04 -07005206 data = tools.read_file(fname)
Simon Glass637958f2021-11-23 21:09:50 -07005207 self.assertEqual(U_BOOT_DATA, data)
5208
5209 finally:
5210 shutil.rmtree(tmpdir)
5211
Simon Glass0b00ae62021-11-23 21:09:52 -07005212 def testExtblobList(self):
5213 """Test an image with an external blob list"""
5214 data = self._DoReadFile('215_blob_ext_list.dts')
5215 self.assertEqual(REFCODE_DATA + FSP_M_DATA, data)
5216
5217 def testExtblobListMissing(self):
5218 """Test an image with a missing external blob"""
5219 with self.assertRaises(ValueError) as e:
5220 self._DoReadFile('216_blob_ext_list_missing.dts')
5221 self.assertIn("Filename 'missing-file' not found in input path",
5222 str(e.exception))
5223
5224 def testExtblobListMissingOk(self):
5225 """Test an image with an missing external blob that is allowed"""
Simon Glass14d64e32025-04-29 07:21:59 -06005226 with terminal.capture() as (stdout, stderr):
Simon Glass0b00ae62021-11-23 21:09:52 -07005227 self._DoTestFile('216_blob_ext_list_missing.dts',
5228 allow_missing=True)
5229 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07005230 self.assertRegex(err, "Image 'image'.*missing.*: blob-ext")
Simon Glass0b00ae62021-11-23 21:09:52 -07005231
Simon Glass3efb2972021-11-23 21:08:59 -07005232 def testFip(self):
5233 """Basic test of generation of an ARM Firmware Image Package (FIP)"""
5234 data = self._DoReadFile('203_fip.dts')
5235 hdr, fents = fip_util.decode_fip(data)
5236 self.assertEqual(fip_util.HEADER_MAGIC, hdr.name)
5237 self.assertEqual(fip_util.HEADER_SERIAL, hdr.serial)
5238 self.assertEqual(0x123, hdr.flags)
5239
5240 self.assertEqual(2, len(fents))
5241
5242 fent = fents[0]
5243 self.assertEqual(
5244 bytes([0x47, 0xd4, 0x08, 0x6d, 0x4c, 0xfe, 0x98, 0x46,
5245 0x9b, 0x95, 0x29, 0x50, 0xcb, 0xbd, 0x5a, 0x0]), fent.uuid)
5246 self.assertEqual('soc-fw', fent.fip_type)
5247 self.assertEqual(0x88, fent.offset)
5248 self.assertEqual(len(ATF_BL31_DATA), fent.size)
5249 self.assertEqual(0x123456789abcdef, fent.flags)
5250 self.assertEqual(ATF_BL31_DATA, fent.data)
5251 self.assertEqual(True, fent.valid)
5252
5253 fent = fents[1]
5254 self.assertEqual(
5255 bytes([0x65, 0x92, 0x27, 0x03, 0x2f, 0x74, 0xe6, 0x44,
5256 0x8d, 0xff, 0x57, 0x9a, 0xc1, 0xff, 0x06, 0x10]), fent.uuid)
5257 self.assertEqual('scp-fwu-cfg', fent.fip_type)
5258 self.assertEqual(0x8c, fent.offset)
5259 self.assertEqual(len(ATF_BL31_DATA), fent.size)
5260 self.assertEqual(0, fent.flags)
5261 self.assertEqual(ATF_BL2U_DATA, fent.data)
5262 self.assertEqual(True, fent.valid)
5263
5264 def testFipOther(self):
5265 """Basic FIP with something that isn't a external blob"""
5266 data = self._DoReadFile('204_fip_other.dts')
5267 hdr, fents = fip_util.decode_fip(data)
5268
5269 self.assertEqual(2, len(fents))
5270 fent = fents[1]
5271 self.assertEqual('rot-cert', fent.fip_type)
5272 self.assertEqual(b'aa', fent.data)
5273
Simon Glass3efb2972021-11-23 21:08:59 -07005274 def testFipNoType(self):
5275 """FIP with an entry of an unknown type"""
5276 with self.assertRaises(ValueError) as e:
5277 self._DoReadFile('205_fip_no_type.dts')
5278 self.assertIn("Must provide a fip-type (node name 'u-boot' is not a known FIP type)",
5279 str(e.exception))
5280
5281 def testFipUuid(self):
5282 """Basic FIP with a manual uuid"""
5283 data = self._DoReadFile('206_fip_uuid.dts')
5284 hdr, fents = fip_util.decode_fip(data)
5285
5286 self.assertEqual(2, len(fents))
5287 fent = fents[1]
5288 self.assertEqual(None, fent.fip_type)
5289 self.assertEqual(
5290 bytes([0xfc, 0x65, 0x13, 0x92, 0x4a, 0x5b, 0x11, 0xec,
5291 0x94, 0x35, 0xff, 0x2d, 0x1c, 0xfc, 0x79, 0x9c]),
5292 fent.uuid)
5293 self.assertEqual(U_BOOT_DATA, fent.data)
5294
5295 def testFipLs(self):
5296 """Test listing a FIP"""
5297 data = self._DoReadFileRealDtb('207_fip_ls.dts')
5298 hdr, fents = fip_util.decode_fip(data)
5299
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01005300 tmpdir = None
Simon Glass3efb2972021-11-23 21:08:59 -07005301 try:
5302 tmpdir, updated_fname = self._SetupImageInTmpdir()
Simon Glass14d64e32025-04-29 07:21:59 -06005303 with terminal.capture() as (stdout, stderr):
Simon Glass3efb2972021-11-23 21:08:59 -07005304 self._DoBinman('ls', '-i', updated_fname)
5305 finally:
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01005306 if tmpdir:
5307 shutil.rmtree(tmpdir)
Simon Glass3efb2972021-11-23 21:08:59 -07005308 lines = stdout.getvalue().splitlines()
5309 expected = [
Simon Glass49cd2b32023-02-07 14:34:18 -07005310'Name Image-pos Size Entry-type Offset Uncomp-size',
5311'--------------------------------------------------------------',
5312'image 0 2d3 section 0',
5313' atf-fip 0 90 atf-fip 0',
5314' soc-fw 88 4 blob-ext 88',
5315' u-boot 8c 4 u-boot 8c',
5316' fdtmap 90 243 fdtmap 90',
Simon Glass3efb2972021-11-23 21:08:59 -07005317]
5318 self.assertEqual(expected, lines)
5319
5320 image = control.images['image']
5321 entries = image.GetEntries()
5322 fdtmap = entries['fdtmap']
5323
5324 fdtmap_data = data[fdtmap.image_pos:fdtmap.image_pos + fdtmap.size]
5325 magic = fdtmap_data[:8]
5326 self.assertEqual(b'_FDTMAP_', magic)
Simon Glass80025522022-01-29 14:14:04 -07005327 self.assertEqual(tools.get_bytes(0, 8), fdtmap_data[8:16])
Simon Glass3efb2972021-11-23 21:08:59 -07005328
5329 fdt_data = fdtmap_data[16:]
5330 dtb = fdt.Fdt.FromData(fdt_data)
5331 dtb.Scan()
5332 props = self._GetPropTree(dtb, BASE_DTB_PROPS, prefix='/')
5333 self.assertEqual({
5334 'atf-fip/soc-fw:image-pos': 136,
5335 'atf-fip/soc-fw:offset': 136,
5336 'atf-fip/soc-fw:size': 4,
5337 'atf-fip/u-boot:image-pos': 140,
5338 'atf-fip/u-boot:offset': 140,
5339 'atf-fip/u-boot:size': 4,
5340 'atf-fip:image-pos': 0,
5341 'atf-fip:offset': 0,
5342 'atf-fip:size': 144,
5343 'image-pos': 0,
5344 'offset': 0,
5345 'fdtmap:image-pos': fdtmap.image_pos,
5346 'fdtmap:offset': fdtmap.offset,
5347 'fdtmap:size': len(fdtmap_data),
5348 'size': len(data),
5349 }, props)
5350
5351 def testFipExtractOneEntry(self):
5352 """Test extracting a single entry fron an FIP"""
5353 self._DoReadFileRealDtb('207_fip_ls.dts')
Simon Glass80025522022-01-29 14:14:04 -07005354 image_fname = tools.get_output_filename('image.bin')
Simon Glass3efb2972021-11-23 21:08:59 -07005355 fname = os.path.join(self._indir, 'output.extact')
5356 control.ExtractEntries(image_fname, fname, None, ['atf-fip/u-boot'])
Simon Glass80025522022-01-29 14:14:04 -07005357 data = tools.read_file(fname)
Simon Glass3efb2972021-11-23 21:08:59 -07005358 self.assertEqual(U_BOOT_DATA, data)
5359
5360 def testFipReplace(self):
5361 """Test replacing a single file in a FIP"""
Simon Glass80025522022-01-29 14:14:04 -07005362 expected = U_BOOT_DATA + tools.get_bytes(0x78, 50)
Simon Glass3efb2972021-11-23 21:08:59 -07005363 data = self._DoReadFileRealDtb('208_fip_replace.dts')
Simon Glass80025522022-01-29 14:14:04 -07005364 updated_fname = tools.get_output_filename('image-updated.bin')
5365 tools.write_file(updated_fname, data)
Simon Glass3efb2972021-11-23 21:08:59 -07005366 entry_name = 'atf-fip/u-boot'
5367 control.WriteEntry(updated_fname, entry_name, expected,
5368 allow_resize=True)
5369 actual = control.ReadEntry(updated_fname, entry_name)
5370 self.assertEqual(expected, actual)
5371
Simon Glass80025522022-01-29 14:14:04 -07005372 new_data = tools.read_file(updated_fname)
Simon Glass3efb2972021-11-23 21:08:59 -07005373 hdr, fents = fip_util.decode_fip(new_data)
5374
5375 self.assertEqual(2, len(fents))
5376
5377 # Check that the FIP entry is updated
5378 fent = fents[1]
5379 self.assertEqual(0x8c, fent.offset)
5380 self.assertEqual(len(expected), fent.size)
5381 self.assertEqual(0, fent.flags)
5382 self.assertEqual(expected, fent.data)
5383 self.assertEqual(True, fent.valid)
5384
5385 def testFipMissing(self):
Simon Glass14d64e32025-04-29 07:21:59 -06005386 with terminal.capture() as (stdout, stderr):
Simon Glass3efb2972021-11-23 21:08:59 -07005387 self._DoTestFile('209_fip_missing.dts', allow_missing=True)
5388 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07005389 self.assertRegex(err, "Image 'image'.*missing.*: rmm-fw")
Simon Glass3efb2972021-11-23 21:08:59 -07005390
5391 def testFipSize(self):
5392 """Test a FIP with a size property"""
5393 data = self._DoReadFile('210_fip_size.dts')
5394 self.assertEqual(0x100 + len(U_BOOT_DATA), len(data))
5395 hdr, fents = fip_util.decode_fip(data)
5396 self.assertEqual(fip_util.HEADER_MAGIC, hdr.name)
5397 self.assertEqual(fip_util.HEADER_SERIAL, hdr.serial)
5398
5399 self.assertEqual(1, len(fents))
5400
5401 fent = fents[0]
5402 self.assertEqual('soc-fw', fent.fip_type)
5403 self.assertEqual(0x60, fent.offset)
5404 self.assertEqual(len(ATF_BL31_DATA), fent.size)
5405 self.assertEqual(ATF_BL31_DATA, fent.data)
5406 self.assertEqual(True, fent.valid)
5407
5408 rest = data[0x60 + len(ATF_BL31_DATA):0x100]
Simon Glass80025522022-01-29 14:14:04 -07005409 self.assertEqual(tools.get_bytes(0xff, len(rest)), rest)
Simon Glass3efb2972021-11-23 21:08:59 -07005410
5411 def testFipBadAlign(self):
5412 """Test that an invalid alignment value in a FIP is detected"""
5413 with self.assertRaises(ValueError) as e:
5414 self._DoTestFile('211_fip_bad_align.dts')
5415 self.assertIn(
5416 "Node \'/binman/atf-fip\': FIP alignment 31 must be a power of two",
5417 str(e.exception))
5418
5419 def testFipCollection(self):
5420 """Test using a FIP in a collection"""
5421 data = self._DoReadFile('212_fip_collection.dts')
5422 entry1 = control.images['image'].GetEntries()['collection']
5423 data1 = data[:entry1.size]
5424 hdr1, fents2 = fip_util.decode_fip(data1)
5425
5426 entry2 = control.images['image'].GetEntries()['atf-fip']
5427 data2 = data[entry2.offset:entry2.offset + entry2.size]
5428 hdr1, fents2 = fip_util.decode_fip(data2)
5429
5430 # The 'collection' entry should have U-Boot included at the end
5431 self.assertEqual(entry1.size - len(U_BOOT_DATA), entry2.size)
5432 self.assertEqual(data1, data2 + U_BOOT_DATA)
5433 self.assertEqual(U_BOOT_DATA, data1[-4:])
5434
5435 # There should be a U-Boot after the final FIP
5436 self.assertEqual(U_BOOT_DATA, data[-4:])
Simon Glass76f496d2021-07-06 10:36:37 -06005437
Simon Glassccae6862022-01-12 13:10:35 -07005438 def testFakeBlob(self):
5439 """Test handling of faking an external blob"""
Simon Glass14d64e32025-04-29 07:21:59 -06005440 with terminal.capture() as (stdout, stderr):
Simon Glassccae6862022-01-12 13:10:35 -07005441 self._DoTestFile('217_fake_blob.dts', allow_missing=True,
5442 allow_fake_blobs=True)
5443 err = stderr.getvalue()
5444 self.assertRegex(
5445 err,
5446 "Image '.*' has faked external blobs and is non-functional: .*")
Simon Glassccae6862022-01-12 13:10:35 -07005447
Simon Glassceb5f912022-01-09 20:13:46 -07005448 def testExtblobListFaked(self):
5449 """Test an extblob with missing external blob that are faked"""
Simon Glass14d64e32025-04-29 07:21:59 -06005450 with terminal.capture() as (stdout, stderr):
Simon Glassceb5f912022-01-09 20:13:46 -07005451 self._DoTestFile('216_blob_ext_list_missing.dts',
5452 allow_fake_blobs=True)
5453 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07005454 self.assertRegex(err, "Image 'image'.*faked.*: blob-ext-list")
Simon Glassceb5f912022-01-09 20:13:46 -07005455
Simon Glass162017b2022-01-09 20:13:57 -07005456 def testListBintools(self):
5457 args = ['tool', '--list']
Simon Glass14d64e32025-04-29 07:21:59 -06005458 with terminal.capture() as (stdout, _):
Simon Glass162017b2022-01-09 20:13:57 -07005459 self._DoBinman(*args)
5460 out = stdout.getvalue().splitlines()
5461 self.assertTrue(len(out) >= 2)
5462
5463 def testFetchBintools(self):
5464 def fail_download(url):
Simon Glass80025522022-01-29 14:14:04 -07005465 """Take the tools.download() function by raising an exception"""
Simon Glass162017b2022-01-09 20:13:57 -07005466 raise urllib.error.URLError('my error')
5467
5468 args = ['tool']
5469 with self.assertRaises(ValueError) as e:
5470 self._DoBinman(*args)
5471 self.assertIn("Invalid arguments to 'tool' subcommand",
5472 str(e.exception))
5473
5474 args = ['tool', '--fetch']
5475 with self.assertRaises(ValueError) as e:
5476 self._DoBinman(*args)
5477 self.assertIn('Please specify bintools to fetch', str(e.exception))
5478
5479 args = ['tool', '--fetch', '_testing']
Simon Glass80025522022-01-29 14:14:04 -07005480 with unittest.mock.patch.object(tools, 'download',
Simon Glass162017b2022-01-09 20:13:57 -07005481 side_effect=fail_download):
Simon Glass14d64e32025-04-29 07:21:59 -06005482 with terminal.capture() as (stdout, _):
Simon Glass162017b2022-01-09 20:13:57 -07005483 self._DoBinman(*args)
5484 self.assertIn('failed to fetch with all methods', stdout.getvalue())
5485
Simon Glass620c4462022-01-09 20:14:11 -07005486 def testBintoolDocs(self):
5487 """Test for creation of bintool documentation"""
Simon Glass14d64e32025-04-29 07:21:59 -06005488 with terminal.capture() as (stdout, stderr):
Simon Glass620c4462022-01-09 20:14:11 -07005489 control.write_bintool_docs(control.bintool.Bintool.get_tool_list())
5490 self.assertTrue(len(stdout.getvalue()) > 0)
5491
5492 def testBintoolDocsMissing(self):
5493 """Test handling of missing bintool documentation"""
5494 with self.assertRaises(ValueError) as e:
Simon Glass14d64e32025-04-29 07:21:59 -06005495 with terminal.capture() as (stdout, stderr):
Simon Glass620c4462022-01-09 20:14:11 -07005496 control.write_bintool_docs(
5497 control.bintool.Bintool.get_tool_list(), 'mkimage')
5498 self.assertIn('Documentation is missing for modules: mkimage',
5499 str(e.exception))
5500
Jan Kiszka58c407f2022-01-28 20:37:53 +01005501 def testListWithGenNode(self):
5502 """Check handling of an FDT map when the section cannot be found"""
5503 entry_args = {
5504 'of-list': 'test-fdt1 test-fdt2',
5505 }
5506 data = self._DoReadFileDtb(
5507 '219_fit_gennode.dts',
5508 entry_args=entry_args,
5509 use_real_dtb=True,
5510 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])
5511
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01005512 tmpdir = None
Jan Kiszka58c407f2022-01-28 20:37:53 +01005513 try:
5514 tmpdir, updated_fname = self._SetupImageInTmpdir()
Simon Glass14d64e32025-04-29 07:21:59 -06005515 with terminal.capture() as (stdout, stderr):
Jan Kiszka58c407f2022-01-28 20:37:53 +01005516 self._RunBinman('ls', '-i', updated_fname)
5517 finally:
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01005518 if tmpdir:
5519 shutil.rmtree(tmpdir)
Jan Kiszka58c407f2022-01-28 20:37:53 +01005520
Alper Nebi Yasake706d872022-02-08 01:08:05 +03005521 def testFitSubentryUsesBintool(self):
5522 """Test that binman FIT subentries can use bintools"""
Simon Glass5dc22cf2025-02-03 09:26:42 -07005523 command.TEST_RESULT = self._HandleGbbCommand
Alper Nebi Yasake706d872022-02-08 01:08:05 +03005524 entry_args = {
5525 'keydir': 'devkeys',
5526 'bmpblk': 'bmpblk.bin',
5527 }
5528 data, _, _, _ = self._DoReadFileDtb('220_fit_subentry_bintool.dts',
5529 entry_args=entry_args)
5530
Alper Nebi Yasakd4553262022-02-08 01:08:07 +03005531 expected = (GBB_DATA + GBB_DATA + tools.get_bytes(0, 8) +
5532 tools.get_bytes(0, 0x2180 - 16))
Alper Nebi Yasake706d872022-02-08 01:08:05 +03005533 self.assertIn(expected, data)
5534
5535 def testFitSubentryMissingBintool(self):
5536 """Test that binman reports missing bintools for FIT subentries"""
5537 entry_args = {
5538 'keydir': 'devkeys',
5539 }
Simon Glass14d64e32025-04-29 07:21:59 -06005540 with terminal.capture() as (_, stderr):
Alper Nebi Yasake706d872022-02-08 01:08:05 +03005541 self._DoTestFile('220_fit_subentry_bintool.dts',
5542 force_missing_bintools='futility', entry_args=entry_args)
5543 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07005544 self.assertRegex(err, "Image 'image'.*missing bintools.*: futility")
Simon Glassccae6862022-01-12 13:10:35 -07005545
Alper Nebi Yasak1e4ffd82022-02-09 22:02:35 +03005546 def testFitSubentryHashSubnode(self):
5547 """Test an image with a FIT inside"""
Marek Vasutf7413f02023-07-18 07:23:58 -06005548 self._SetupSplElf()
Alper Nebi Yasak1e4ffd82022-02-09 22:02:35 +03005549 data, _, _, out_dtb_name = self._DoReadFileDtb(
5550 '221_fit_subentry_hash.dts', use_real_dtb=True, update_dtb=True)
5551
5552 mkimage_dtb = fdt.Fdt.FromData(data)
5553 mkimage_dtb.Scan()
5554 binman_dtb = fdt.Fdt(out_dtb_name)
5555 binman_dtb.Scan()
5556
5557 # Check that binman didn't add hash values
5558 fnode = binman_dtb.GetNode('/binman/fit/images/kernel/hash')
5559 self.assertNotIn('value', fnode.props)
5560
5561 fnode = binman_dtb.GetNode('/binman/fit/images/fdt-1/hash')
5562 self.assertNotIn('value', fnode.props)
5563
5564 # Check that mkimage added hash values
5565 fnode = mkimage_dtb.GetNode('/images/kernel/hash')
5566 self.assertIn('value', fnode.props)
5567
5568 fnode = mkimage_dtb.GetNode('/images/fdt-1/hash')
5569 self.assertIn('value', fnode.props)
5570
Roger Quadros5cdcea02022-02-19 20:50:04 +02005571 def testPackTeeOs(self):
5572 """Test that an image with an TEE binary can be created"""
5573 data = self._DoReadFile('222_tee_os.dts')
5574 self.assertEqual(TEE_OS_DATA, data[:len(TEE_OS_DATA)])
5575
Neha Malcom Francis59be2552023-12-05 15:12:18 +05305576 def testPackTiDm(self):
5577 """Test that an image with a TI DM binary can be created"""
5578 data = self._DoReadFile('225_ti_dm.dts')
5579 self.assertEqual(TI_DM_DATA, data[:len(TI_DM_DATA)])
5580
Bryan Brattlof5b45f4b2025-05-15 10:16:19 -05005581 def testPackBl1(self):
5582 """test if an image with a bl1 binary can be created"""
5583 data = self._DoReadFile('347_bl1.dts')
5584 self.assertEqual(ATF_BL1_DATA, data[:len(ATF_BL1_DATA)])
5585
Simon Glass912339f2022-02-08 11:50:03 -07005586 def testFitFdtOper(self):
5587 """Check handling of a specified FIT operation"""
5588 entry_args = {
5589 'of-list': 'test-fdt1 test-fdt2',
5590 'default-dt': 'test-fdt2',
5591 }
5592 self._DoReadFileDtb(
5593 '223_fit_fdt_oper.dts',
5594 entry_args=entry_args,
5595 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
5596
5597 def testFitFdtBadOper(self):
5598 """Check handling of an FDT map when the section cannot be found"""
5599 with self.assertRaises(ValueError) as exc:
5600 self._DoReadFileDtb('224_fit_bad_oper.dts')
Simon Glass05f71dc2022-03-05 20:19:09 -07005601 self.assertIn("Node '/binman/fit': subnode 'images/@fdt-SEQ': Unknown operation 'unknown'",
Simon Glass912339f2022-02-08 11:50:03 -07005602 str(exc.exception))
5603
Simon Glassdd156a42022-03-05 20:18:59 -07005604 def test_uses_expand_size(self):
5605 """Test that the 'expand-size' property cannot be used anymore"""
5606 with self.assertRaises(ValueError) as e:
5607 data = self._DoReadFile('225_expand_size_bad.dts')
5608 self.assertIn(
5609 "Node '/binman/u-boot': Please use 'extend-size' instead of 'expand-size'",
5610 str(e.exception))
5611
Simon Glass5f423422022-03-05 20:19:12 -07005612 def testFitSplitElf(self):
5613 """Test an image with an FIT with an split-elf operation"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005614 if not elf.ELF_TOOLS:
5615 self.skipTest('Python elftools not available')
Simon Glass5f423422022-03-05 20:19:12 -07005616 entry_args = {
5617 'of-list': 'test-fdt1 test-fdt2',
5618 'default-dt': 'test-fdt2',
5619 'atf-bl31-path': 'bl31.elf',
5620 'tee-os-path': 'tee.elf',
5621 }
5622 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
5623 data = self._DoReadFileDtb(
5624 '226_fit_split_elf.dts',
5625 entry_args=entry_args,
5626 extra_indirs=[test_subdir])[0]
5627
5628 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
5629 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
5630
5631 base_keys = {'description', 'type', 'arch', 'os', 'compression',
5632 'data', 'load'}
5633 dtb = fdt.Fdt.FromData(fit_data)
5634 dtb.Scan()
5635
5636 elf_data = tools.read_file(os.path.join(self._indir, 'bl31.elf'))
5637 segments, entry = elf.read_loadable_segments(elf_data)
5638
5639 # We assume there are two segments
Brandon Maiera657bc62024-06-04 16:16:05 +00005640 self.assertEqual(2, len(segments))
Simon Glass5f423422022-03-05 20:19:12 -07005641
5642 atf1 = dtb.GetNode('/images/atf-1')
5643 _, start, data = segments[0]
5644 self.assertEqual(base_keys | {'entry'}, atf1.props.keys())
5645 self.assertEqual(entry,
5646 fdt_util.fdt32_to_cpu(atf1.props['entry'].value))
5647 self.assertEqual(start,
5648 fdt_util.fdt32_to_cpu(atf1.props['load'].value))
5649 self.assertEqual(data, atf1.props['data'].bytes)
5650
Jonas Karlmand2c7d902023-01-21 19:01:48 +00005651 hash_node = atf1.FindNode('hash')
5652 self.assertIsNotNone(hash_node)
5653 self.assertEqual({'algo', 'value'}, hash_node.props.keys())
5654
Simon Glass5f423422022-03-05 20:19:12 -07005655 atf2 = dtb.GetNode('/images/atf-2')
5656 self.assertEqual(base_keys, atf2.props.keys())
5657 _, start, data = segments[1]
5658 self.assertEqual(start,
5659 fdt_util.fdt32_to_cpu(atf2.props['load'].value))
5660 self.assertEqual(data, atf2.props['data'].bytes)
5661
Jonas Karlmand2c7d902023-01-21 19:01:48 +00005662 hash_node = atf2.FindNode('hash')
5663 self.assertIsNotNone(hash_node)
5664 self.assertEqual({'algo', 'value'}, hash_node.props.keys())
5665
5666 hash_node = dtb.GetNode('/images/tee-1/hash-1')
5667 self.assertIsNotNone(hash_node)
5668 self.assertEqual({'algo', 'value'}, hash_node.props.keys())
5669
Simon Glass5f423422022-03-05 20:19:12 -07005670 conf = dtb.GetNode('/configurations')
5671 self.assertEqual({'default'}, conf.props.keys())
5672
5673 for subnode in conf.subnodes:
5674 self.assertEqual({'description', 'fdt', 'loadables'},
5675 subnode.props.keys())
5676 self.assertEqual(
5677 ['atf-1', 'atf-2', 'tee-1', 'tee-2'],
5678 fdt_util.GetStringList(subnode, 'loadables'))
5679
5680 def _check_bad_fit(self, dts):
5681 """Check a bad FIT
5682
5683 This runs with the given dts and returns the assertion raised
5684
5685 Args:
5686 dts (str): dts filename to use
5687
5688 Returns:
5689 str: Assertion string raised
5690 """
5691 entry_args = {
5692 'of-list': 'test-fdt1 test-fdt2',
5693 'default-dt': 'test-fdt2',
5694 'atf-bl31-path': 'bl31.elf',
5695 'tee-os-path': 'tee.elf',
5696 }
5697 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
5698 with self.assertRaises(ValueError) as exc:
5699 self._DoReadFileDtb(dts, entry_args=entry_args,
5700 extra_indirs=[test_subdir])[0]
5701 return str(exc.exception)
5702
5703 def testFitSplitElfBadElf(self):
5704 """Test a FIT split-elf operation with an invalid ELF file"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005705 if not elf.ELF_TOOLS:
5706 self.skipTest('Python elftools not available')
Simon Glass5f423422022-03-05 20:19:12 -07005707 TestFunctional._MakeInputFile('bad.elf', tools.get_bytes(100, 100))
5708 entry_args = {
5709 'of-list': 'test-fdt1 test-fdt2',
5710 'default-dt': 'test-fdt2',
5711 'atf-bl31-path': 'bad.elf',
5712 'tee-os-path': 'tee.elf',
5713 }
5714 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
5715 with self.assertRaises(ValueError) as exc:
5716 self._DoReadFileDtb(
5717 '226_fit_split_elf.dts',
5718 entry_args=entry_args,
5719 extra_indirs=[test_subdir])[0]
5720 self.assertIn(
5721 "Node '/binman/fit': subnode 'images/@atf-SEQ': Failed to read ELF file: Magic number does not match",
5722 str(exc.exception))
5723
Simon Glass5f423422022-03-05 20:19:12 -07005724 def checkFitSplitElf(self, **kwargs):
Simon Glass7d3e4072022-08-07 09:46:46 -06005725 """Test an split-elf FIT with a missing ELF file
5726
5727 Args:
5728 kwargs (dict of str): Arguments to pass to _DoTestFile()
5729
5730 Returns:
5731 tuple:
5732 str: stdout result
5733 str: stderr result
5734 """
Simon Glass5f423422022-03-05 20:19:12 -07005735 entry_args = {
5736 'of-list': 'test-fdt1 test-fdt2',
5737 'default-dt': 'test-fdt2',
5738 'atf-bl31-path': 'bl31.elf',
5739 'tee-os-path': 'missing.elf',
5740 }
5741 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
Simon Glass14d64e32025-04-29 07:21:59 -06005742 with terminal.capture() as (stdout, stderr):
Simon Glass5f423422022-03-05 20:19:12 -07005743 self._DoTestFile(
5744 '226_fit_split_elf.dts', entry_args=entry_args,
Simon Glass7d3e4072022-08-07 09:46:46 -06005745 extra_indirs=[test_subdir], verbosity=3, **kwargs)
5746 out = stdout.getvalue()
5747 err = stderr.getvalue()
5748 return out, err
Simon Glass5f423422022-03-05 20:19:12 -07005749
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005750 def testFitSplitElfBadDirective(self):
5751 """Test a FIT split-elf invalid fit,xxx directive in an image node"""
5752 if not elf.ELF_TOOLS:
5753 self.skipTest('Python elftools not available')
5754 err = self._check_bad_fit('227_fit_bad_dir.dts')
5755 self.assertIn(
5756 "Node '/binman/fit': subnode 'images/@atf-SEQ': Unknown directive 'fit,something'",
5757 err)
5758
5759 def testFitSplitElfBadDirectiveConfig(self):
5760 """Test a FIT split-elf with invalid fit,xxx directive in config"""
5761 if not elf.ELF_TOOLS:
5762 self.skipTest('Python elftools not available')
5763 err = self._check_bad_fit('228_fit_bad_dir_config.dts')
5764 self.assertEqual(
5765 "Node '/binman/fit': subnode 'configurations/@config-SEQ': Unknown directive 'fit,config'",
5766 err)
5767
5768
Simon Glass5f423422022-03-05 20:19:12 -07005769 def testFitSplitElfMissing(self):
5770 """Test an split-elf FIT with a missing ELF file"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005771 if not elf.ELF_TOOLS:
5772 self.skipTest('Python elftools not available')
Simon Glass7d3e4072022-08-07 09:46:46 -06005773 out, err = self.checkFitSplitElf(allow_missing=True)
Simon Glass5f423422022-03-05 20:19:12 -07005774 self.assertRegex(
5775 err,
5776 "Image '.*' is missing external blobs and is non-functional: .*")
Simon Glass7d3e4072022-08-07 09:46:46 -06005777 self.assertNotRegex(out, '.*Faked blob.*')
5778 fname = tools.get_output_filename('binman-fake/missing.elf')
5779 self.assertFalse(os.path.exists(fname))
Simon Glass5f423422022-03-05 20:19:12 -07005780
5781 def testFitSplitElfFaked(self):
5782 """Test an split-elf FIT with faked ELF file"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005783 if not elf.ELF_TOOLS:
5784 self.skipTest('Python elftools not available')
Simon Glass7d3e4072022-08-07 09:46:46 -06005785 out, err = self.checkFitSplitElf(allow_missing=True, allow_fake_blobs=True)
Simon Glass5f423422022-03-05 20:19:12 -07005786 self.assertRegex(
5787 err,
5788 "Image '.*' is missing external blobs and is non-functional: .*")
Simon Glass7d3e4072022-08-07 09:46:46 -06005789 self.assertRegex(
5790 out,
5791 "Entry '/binman/fit/images/@tee-SEQ/tee-os': Faked blob '.*binman-fake/missing.elf")
5792 fname = tools.get_output_filename('binman-fake/missing.elf')
5793 self.assertTrue(os.path.exists(fname))
Simon Glass5f423422022-03-05 20:19:12 -07005794
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005795 def testMkimageMissingBlob(self):
5796 """Test using mkimage to build an image"""
Simon Glass14d64e32025-04-29 07:21:59 -06005797 with terminal.capture() as (stdout, stderr):
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005798 self._DoTestFile('229_mkimage_missing.dts', allow_missing=True,
5799 allow_fake_blobs=True)
5800 err = stderr.getvalue()
5801 self.assertRegex(
5802 err,
5803 "Image '.*' has faked external blobs and is non-functional: .*")
5804
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005805 def testPreLoad(self):
5806 """Test an image with a pre-load header"""
5807 entry_args = {
Simon Glasse2dfb962023-07-24 09:19:57 -06005808 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005809 }
Simon Glasse2dfb962023-07-24 09:19:57 -06005810 data = self._DoReadFileDtb(
5811 '230_pre_load.dts', entry_args=entry_args,
5812 extra_indirs=[os.path.join(self._binman_dir, 'test')])[0]
Paul HENRYS5cf82892025-02-24 22:20:55 +01005813
5814 image_fname = tools.get_output_filename('image.bin')
5815 is_signed = self._CheckPreload(image_fname, self.TestFile("dev.key"))
5816
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005817 self.assertEqual(PRE_LOAD_MAGIC, data[:len(PRE_LOAD_MAGIC)])
5818 self.assertEqual(PRE_LOAD_VERSION, data[4:4 + len(PRE_LOAD_VERSION)])
5819 self.assertEqual(PRE_LOAD_HDR_SIZE, data[8:8 + len(PRE_LOAD_HDR_SIZE)])
Paul HENRYS5cf82892025-02-24 22:20:55 +01005820 self.assertEqual(is_signed, True)
Simon Glasse2dfb962023-07-24 09:19:57 -06005821
5822 def testPreLoadNoKey(self):
5823 """Test an image with a pre-load heade0r with missing key"""
5824 with self.assertRaises(FileNotFoundError) as exc:
5825 self._DoReadFile('230_pre_load.dts')
5826 self.assertIn("No such file or directory: 'dev.key'",
5827 str(exc.exception))
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005828
5829 def testPreLoadPkcs(self):
5830 """Test an image with a pre-load header with padding pkcs"""
Simon Glasse2dfb962023-07-24 09:19:57 -06005831 entry_args = {
5832 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5833 }
5834 data = self._DoReadFileDtb('231_pre_load_pkcs.dts',
5835 entry_args=entry_args)[0]
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005836 self.assertEqual(PRE_LOAD_MAGIC, data[:len(PRE_LOAD_MAGIC)])
5837 self.assertEqual(PRE_LOAD_VERSION, data[4:4 + len(PRE_LOAD_VERSION)])
5838 self.assertEqual(PRE_LOAD_HDR_SIZE, data[8:8 + len(PRE_LOAD_HDR_SIZE)])
5839
5840 def testPreLoadPss(self):
5841 """Test an image with a pre-load header with padding pss"""
Simon Glasse2dfb962023-07-24 09:19:57 -06005842 entry_args = {
5843 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5844 }
5845 data = self._DoReadFileDtb('232_pre_load_pss.dts',
5846 entry_args=entry_args)[0]
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005847 self.assertEqual(PRE_LOAD_MAGIC, data[:len(PRE_LOAD_MAGIC)])
5848 self.assertEqual(PRE_LOAD_VERSION, data[4:4 + len(PRE_LOAD_VERSION)])
5849 self.assertEqual(PRE_LOAD_HDR_SIZE, data[8:8 + len(PRE_LOAD_HDR_SIZE)])
5850
5851 def testPreLoadInvalidPadding(self):
5852 """Test an image with a pre-load header with an invalid padding"""
Simon Glasse2dfb962023-07-24 09:19:57 -06005853 entry_args = {
5854 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5855 }
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005856 with self.assertRaises(ValueError) as e:
Simon Glasse2dfb962023-07-24 09:19:57 -06005857 self._DoReadFileDtb('233_pre_load_invalid_padding.dts',
5858 entry_args=entry_args)
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005859
5860 def testPreLoadInvalidSha(self):
5861 """Test an image with a pre-load header with an invalid hash"""
Simon Glasse2dfb962023-07-24 09:19:57 -06005862 entry_args = {
5863 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5864 }
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005865 with self.assertRaises(ValueError) as e:
Simon Glasse2dfb962023-07-24 09:19:57 -06005866 self._DoReadFileDtb('234_pre_load_invalid_sha.dts',
5867 entry_args=entry_args)
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005868
5869 def testPreLoadInvalidAlgo(self):
5870 """Test an image with a pre-load header with an invalid algo"""
5871 with self.assertRaises(ValueError) as e:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005872 data = self._DoReadFile('235_pre_load_invalid_algo.dts')
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005873
5874 def testPreLoadInvalidKey(self):
5875 """Test an image with a pre-load header with an invalid key"""
Simon Glasse2dfb962023-07-24 09:19:57 -06005876 entry_args = {
5877 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5878 }
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005879 with self.assertRaises(ValueError) as e:
Simon Glasse2dfb962023-07-24 09:19:57 -06005880 data = self._DoReadFileDtb('236_pre_load_invalid_key.dts',
5881 entry_args=entry_args)
Roger Quadros5cdcea02022-02-19 20:50:04 +02005882
Alper Nebi Yasak5cff63f2022-03-27 18:31:44 +03005883 def _CheckSafeUniqueNames(self, *images):
5884 """Check all entries of given images for unsafe unique names"""
5885 for image in images:
5886 entries = {}
5887 image._CollectEntries(entries, {}, image)
5888 for entry in entries.values():
5889 uniq = entry.GetUniqueName()
5890
5891 # Used as part of a filename, so must not be absolute paths.
5892 self.assertFalse(os.path.isabs(uniq))
5893
5894 def testSafeUniqueNames(self):
5895 """Test entry unique names are safe in single image configuration"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005896 data = self._DoReadFileRealDtb('237_unique_names.dts')
Alper Nebi Yasak5cff63f2022-03-27 18:31:44 +03005897
5898 orig_image = control.images['image']
5899 image_fname = tools.get_output_filename('image.bin')
5900 image = Image.FromFile(image_fname)
5901
5902 self._CheckSafeUniqueNames(orig_image, image)
5903
5904 def testSafeUniqueNamesMulti(self):
5905 """Test entry unique names are safe with multiple images"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005906 data = self._DoReadFileRealDtb('238_unique_names_multi.dts')
Alper Nebi Yasak5cff63f2022-03-27 18:31:44 +03005907
5908 orig_image = control.images['image']
5909 image_fname = tools.get_output_filename('image.bin')
5910 image = Image.FromFile(image_fname)
5911
5912 self._CheckSafeUniqueNames(orig_image, image)
5913
Alper Nebi Yasake63ca5a2022-03-27 18:31:45 +03005914 def testReplaceCmdWithBintool(self):
5915 """Test replacing an entry that needs a bintool to pack"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005916 data = self._DoReadFileRealDtb('239_replace_with_bintool.dts')
Alper Nebi Yasake63ca5a2022-03-27 18:31:45 +03005917 expected = U_BOOT_DATA + b'aa'
5918 self.assertEqual(expected, data[:len(expected)])
5919
5920 try:
5921 tmpdir, updated_fname = self._SetupImageInTmpdir()
5922 fname = os.path.join(tmpdir, 'update-testing.bin')
5923 tools.write_file(fname, b'zz')
5924 self._DoBinman('replace', '-i', updated_fname,
5925 '_testing', '-f', fname)
5926
5927 data = tools.read_file(updated_fname)
5928 expected = U_BOOT_DATA + b'zz'
5929 self.assertEqual(expected, data[:len(expected)])
5930 finally:
5931 shutil.rmtree(tmpdir)
5932
5933 def testReplaceCmdOtherWithBintool(self):
5934 """Test replacing an entry when another needs a bintool to pack"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005935 data = self._DoReadFileRealDtb('239_replace_with_bintool.dts')
Alper Nebi Yasake63ca5a2022-03-27 18:31:45 +03005936 expected = U_BOOT_DATA + b'aa'
5937 self.assertEqual(expected, data[:len(expected)])
5938
5939 try:
5940 tmpdir, updated_fname = self._SetupImageInTmpdir()
5941 fname = os.path.join(tmpdir, 'update-u-boot.bin')
5942 tools.write_file(fname, b'x' * len(U_BOOT_DATA))
5943 self._DoBinman('replace', '-i', updated_fname,
5944 'u-boot', '-f', fname)
5945
5946 data = tools.read_file(updated_fname)
5947 expected = b'x' * len(U_BOOT_DATA) + b'aa'
5948 self.assertEqual(expected, data[:len(expected)])
5949 finally:
5950 shutil.rmtree(tmpdir)
5951
Alper Nebi Yasak00c68f12022-03-27 18:31:46 +03005952 def testReplaceResizeNoRepackSameSize(self):
5953 """Test replacing entries with same-size data without repacking"""
5954 expected = b'x' * len(U_BOOT_DATA)
5955 data, expected_fdtmap, _ = self._RunReplaceCmd('u-boot', expected)
5956 self.assertEqual(expected, data)
5957
5958 path, fdtmap = state.GetFdtContents('fdtmap')
5959 self.assertIsNotNone(path)
5960 self.assertEqual(expected_fdtmap, fdtmap)
5961
5962 def testReplaceResizeNoRepackSmallerSize(self):
5963 """Test replacing entries with smaller-size data without repacking"""
5964 new_data = b'x'
5965 data, expected_fdtmap, _ = self._RunReplaceCmd('u-boot', new_data)
5966 expected = new_data.ljust(len(U_BOOT_DATA), b'\0')
5967 self.assertEqual(expected, data)
5968
5969 path, fdtmap = state.GetFdtContents('fdtmap')
5970 self.assertIsNotNone(path)
5971 self.assertEqual(expected_fdtmap, fdtmap)
5972
Alper Nebi Yasak6cadc502022-03-27 18:31:48 +03005973 def testExtractFit(self):
5974 """Test extracting a FIT section"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005975 self._DoReadFileRealDtb('240_fit_extract_replace.dts')
Alper Nebi Yasak6cadc502022-03-27 18:31:48 +03005976 image_fname = tools.get_output_filename('image.bin')
5977
5978 fit_data = control.ReadEntry(image_fname, 'fit')
5979 fit = fdt.Fdt.FromData(fit_data)
5980 fit.Scan()
5981
5982 # Check subentry data inside the extracted fit
5983 for node_path, expected in [
5984 ('/images/kernel', U_BOOT_DATA),
5985 ('/images/fdt-1', U_BOOT_NODTB_DATA),
5986 ('/images/scr-1', COMPRESS_DATA),
5987 ]:
5988 node = fit.GetNode(node_path)
5989 data = fit.GetProps(node)['data'].bytes
5990 self.assertEqual(expected, data)
5991
5992 def testExtractFitSubentries(self):
5993 """Test extracting FIT section subentries"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005994 self._DoReadFileRealDtb('240_fit_extract_replace.dts')
Alper Nebi Yasak6cadc502022-03-27 18:31:48 +03005995 image_fname = tools.get_output_filename('image.bin')
5996
5997 for entry_path, expected in [
5998 ('fit/kernel', U_BOOT_DATA),
5999 ('fit/kernel/u-boot', U_BOOT_DATA),
6000 ('fit/fdt-1', U_BOOT_NODTB_DATA),
6001 ('fit/fdt-1/u-boot-nodtb', U_BOOT_NODTB_DATA),
6002 ('fit/scr-1', COMPRESS_DATA),
6003 ('fit/scr-1/blob', COMPRESS_DATA),
6004 ]:
6005 data = control.ReadEntry(image_fname, entry_path)
6006 self.assertEqual(expected, data)
6007
Alper Nebi Yasak49892642022-03-27 18:31:49 +03006008 def testReplaceFitSubentryLeafSameSize(self):
6009 """Test replacing a FIT leaf subentry with same-size data"""
6010 new_data = b'x' * len(U_BOOT_DATA)
6011 data, expected_fdtmap, _ = self._RunReplaceCmd(
6012 'fit/kernel/u-boot', new_data,
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006013 dts='240_fit_extract_replace.dts')
Alper Nebi Yasak49892642022-03-27 18:31:49 +03006014 self.assertEqual(new_data, data)
6015
6016 path, fdtmap = state.GetFdtContents('fdtmap')
6017 self.assertIsNotNone(path)
6018 self.assertEqual(expected_fdtmap, fdtmap)
6019
6020 def testReplaceFitSubentryLeafBiggerSize(self):
6021 """Test replacing a FIT leaf subentry with bigger-size data"""
6022 new_data = b'ub' * len(U_BOOT_NODTB_DATA)
6023 data, expected_fdtmap, _ = self._RunReplaceCmd(
6024 'fit/fdt-1/u-boot-nodtb', new_data,
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006025 dts='240_fit_extract_replace.dts')
Alper Nebi Yasak49892642022-03-27 18:31:49 +03006026 self.assertEqual(new_data, data)
6027
6028 # Will be repacked, so fdtmap must change
6029 path, fdtmap = state.GetFdtContents('fdtmap')
6030 self.assertIsNotNone(path)
6031 self.assertNotEqual(expected_fdtmap, fdtmap)
6032
6033 def testReplaceFitSubentryLeafSmallerSize(self):
6034 """Test replacing a FIT leaf subentry with smaller-size data"""
6035 new_data = b'x'
6036 expected = new_data.ljust(len(U_BOOT_NODTB_DATA), b'\0')
6037 data, expected_fdtmap, _ = self._RunReplaceCmd(
6038 'fit/fdt-1/u-boot-nodtb', new_data,
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006039 dts='240_fit_extract_replace.dts')
Alper Nebi Yasak49892642022-03-27 18:31:49 +03006040 self.assertEqual(expected, data)
6041
6042 path, fdtmap = state.GetFdtContents('fdtmap')
6043 self.assertIsNotNone(path)
6044 self.assertEqual(expected_fdtmap, fdtmap)
6045
Alper Nebi Yasak1d44c8e2022-03-27 18:31:50 +03006046 def testReplaceSectionSimple(self):
Simon Glass49b77e82023-03-02 17:02:44 -07006047 """Test replacing a simple section with same-sized data"""
Alper Nebi Yasak1d44c8e2022-03-27 18:31:50 +03006048 new_data = b'w' * len(COMPRESS_DATA + U_BOOT_DATA)
Simon Glass49b77e82023-03-02 17:02:44 -07006049 data, expected_fdtmap, image = self._RunReplaceCmd('section',
6050 new_data, dts='241_replace_section_simple.dts')
6051 self.assertEqual(new_data, data)
6052
6053 entries = image.GetEntries()
6054 self.assertIn('section', entries)
6055 entry = entries['section']
6056 self.assertEqual(len(new_data), entry.size)
6057
6058 def testReplaceSectionLarger(self):
6059 """Test replacing a simple section with larger data"""
6060 new_data = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) + 1)
6061 data, expected_fdtmap, image = self._RunReplaceCmd('section',
6062 new_data, dts='241_replace_section_simple.dts')
6063 self.assertEqual(new_data, data)
6064
6065 entries = image.GetEntries()
6066 self.assertIn('section', entries)
6067 entry = entries['section']
6068 self.assertEqual(len(new_data), entry.size)
6069 fentry = entries['fdtmap']
6070 self.assertEqual(entry.offset + entry.size, fentry.offset)
6071
6072 def testReplaceSectionSmaller(self):
6073 """Test replacing a simple section with smaller data"""
6074 new_data = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) - 1) + b'\0'
6075 data, expected_fdtmap, image = self._RunReplaceCmd('section',
6076 new_data, dts='241_replace_section_simple.dts')
6077 self.assertEqual(new_data, data)
6078
6079 # The new size is the same as the old, just with a pad byte at the end
6080 entries = image.GetEntries()
6081 self.assertIn('section', entries)
6082 entry = entries['section']
6083 self.assertEqual(len(new_data), entry.size)
6084
6085 def testReplaceSectionSmallerAllow(self):
6086 """Test failing to replace a simple section with smaller data"""
6087 new_data = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) - 1)
6088 try:
6089 state.SetAllowEntryContraction(True)
6090 with self.assertRaises(ValueError) as exc:
6091 self._RunReplaceCmd('section', new_data,
6092 dts='241_replace_section_simple.dts')
6093 finally:
6094 state.SetAllowEntryContraction(False)
6095
6096 # Since we have no information about the position of things within the
6097 # section, we cannot adjust the position of /section-u-boot so it ends
6098 # up outside the section
Simon Glassc6b283f2022-08-13 11:40:46 -06006099 self.assertIn(
Simon Glass49b77e82023-03-02 17:02:44 -07006100 "Node '/section/u-boot': Offset 0x24 (36) size 0x4 (4) is outside "
6101 "the section '/section' starting at 0x0 (0) of size 0x27 (39)",
Simon Glassc6b283f2022-08-13 11:40:46 -06006102 str(exc.exception))
Alper Nebi Yasak1d44c8e2022-03-27 18:31:50 +03006103
Simon Glass8fbca772022-08-13 11:40:48 -06006104 def testMkimageImagename(self):
6105 """Test using mkimage with -n holding the data too"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006106 self._SetupSplElf()
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006107 data = self._DoReadFile('242_mkimage_name.dts')
Simon Glass8fbca772022-08-13 11:40:48 -06006108
6109 # Check that the data appears in the file somewhere
6110 self.assertIn(U_BOOT_SPL_DATA, data)
6111
Simon Glassbb7d3bb2022-09-06 20:26:52 -06006112 # Get struct legacy_img_hdr -> ih_name
Simon Glass8fbca772022-08-13 11:40:48 -06006113 name = data[0x20:0x40]
6114
6115 # Build the filename that we expect to be placed in there, by virtue of
6116 # the -n paraameter
6117 expect = os.path.join(tools.get_output_dir(), 'mkimage.mkimage')
6118
6119 # Check that the image name is set to the temporary filename used
6120 self.assertEqual(expect.encode('utf-8')[:0x20], name)
6121
Simon Glassb1669752022-08-13 11:40:49 -06006122 def testMkimageImage(self):
6123 """Test using mkimage with -n holding the data too"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006124 self._SetupSplElf()
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006125 data = self._DoReadFile('243_mkimage_image.dts')
Simon Glassb1669752022-08-13 11:40:49 -06006126
6127 # Check that the data appears in the file somewhere
6128 self.assertIn(U_BOOT_SPL_DATA, data)
6129
Simon Glassbb7d3bb2022-09-06 20:26:52 -06006130 # Get struct legacy_img_hdr -> ih_name
Simon Glassb1669752022-08-13 11:40:49 -06006131 name = data[0x20:0x40]
6132
6133 # Build the filename that we expect to be placed in there, by virtue of
6134 # the -n paraameter
6135 expect = os.path.join(tools.get_output_dir(), 'mkimage-n.mkimage')
6136
6137 # Check that the image name is set to the temporary filename used
6138 self.assertEqual(expect.encode('utf-8')[:0x20], name)
6139
6140 # Check the corect data is in the imagename file
6141 self.assertEqual(U_BOOT_DATA, tools.read_file(expect))
6142
6143 def testMkimageImageNoContent(self):
6144 """Test using mkimage with -n and no data"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006145 self._SetupSplElf()
Simon Glassb1669752022-08-13 11:40:49 -06006146 with self.assertRaises(ValueError) as exc:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006147 self._DoReadFile('244_mkimage_image_no_content.dts')
Simon Glassb1669752022-08-13 11:40:49 -06006148 self.assertIn('Could not complete processing of contents',
6149 str(exc.exception))
6150
6151 def testMkimageImageBad(self):
6152 """Test using mkimage with imagename node and data-to-imagename"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006153 self._SetupSplElf()
Simon Glassb1669752022-08-13 11:40:49 -06006154 with self.assertRaises(ValueError) as exc:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006155 self._DoReadFile('245_mkimage_image_bad.dts')
Simon Glassb1669752022-08-13 11:40:49 -06006156 self.assertIn('Cannot use both imagename node and data-to-imagename',
6157 str(exc.exception))
6158
Simon Glassbd5cd882022-08-13 11:40:50 -06006159 def testCollectionOther(self):
6160 """Test a collection where the data comes from another section"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006161 data = self._DoReadFile('246_collection_other.dts')
Simon Glassbd5cd882022-08-13 11:40:50 -06006162 self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA +
6163 tools.get_bytes(0xff, 2) + U_BOOT_NODTB_DATA +
6164 tools.get_bytes(0xfe, 3) + U_BOOT_DTB_DATA,
6165 data)
6166
6167 def testMkimageCollection(self):
6168 """Test using a collection referring to an entry in a mkimage entry"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006169 self._SetupSplElf()
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006170 data = self._DoReadFile('247_mkimage_coll.dts')
Simon Glassbd5cd882022-08-13 11:40:50 -06006171 expect = U_BOOT_SPL_DATA + U_BOOT_DATA
6172 self.assertEqual(expect, data[:len(expect)])
6173
Stefan Herbrechtsmeier11121d32022-08-19 16:25:25 +02006174 def testCompressDtbPrependInvalid(self):
6175 """Test that invalid header is detected"""
6176 with self.assertRaises(ValueError) as e:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006177 self._DoReadFileDtb('248_compress_dtb_prepend_invalid.dts')
Stefan Herbrechtsmeier11121d32022-08-19 16:25:25 +02006178 self.assertIn("Node '/binman/u-boot-dtb': Invalid prepend in "
6179 "'u-boot-dtb': 'invalid'", str(e.exception))
6180
6181 def testCompressDtbPrependLength(self):
6182 """Test that compress with length header works as expected"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006183 data = self._DoReadFileRealDtb('249_compress_dtb_prepend_length.dts')
Stefan Herbrechtsmeier11121d32022-08-19 16:25:25 +02006184 image = control.images['image']
6185 entries = image.GetEntries()
6186 self.assertIn('u-boot-dtb', entries)
6187 u_boot_dtb = entries['u-boot-dtb']
6188 self.assertIn('fdtmap', entries)
6189 fdtmap = entries['fdtmap']
6190
6191 image_fname = tools.get_output_filename('image.bin')
6192 orig = control.ReadEntry(image_fname, 'u-boot-dtb')
6193 dtb = fdt.Fdt.FromData(orig)
6194 dtb.Scan()
6195 props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
6196 expected = {
6197 'u-boot:size': len(U_BOOT_DATA),
6198 'u-boot-dtb:uncomp-size': len(orig),
6199 'u-boot-dtb:size': u_boot_dtb.size,
6200 'fdtmap:size': fdtmap.size,
6201 'size': len(data),
6202 }
6203 self.assertEqual(expected, props)
6204
6205 # Check implementation
6206 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
6207 rest = data[len(U_BOOT_DATA):]
6208 comp_data_len = struct.unpack('<I', rest[:4])[0]
6209 comp_data = rest[4:4 + comp_data_len]
6210 orig2 = self._decompress(comp_data)
6211 self.assertEqual(orig, orig2)
6212
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +02006213 def testInvalidCompress(self):
6214 """Test that invalid compress algorithm is detected"""
6215 with self.assertRaises(ValueError) as e:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006216 self._DoTestFile('250_compress_dtb_invalid.dts')
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +02006217 self.assertIn("Unknown algorithm 'invalid'", str(e.exception))
6218
Stefan Herbrechtsmeiera14bee02022-08-19 16:25:32 +02006219 def testCompUtilCompressions(self):
6220 """Test compression algorithms"""
6221 for bintool in self.comp_bintools.values():
6222 self._CheckBintool(bintool)
6223 data = bintool.compress(COMPRESS_DATA)
6224 self.assertNotEqual(COMPRESS_DATA, data)
6225 orig = bintool.decompress(data)
Brandon Maiera657bc62024-06-04 16:16:05 +00006226 self.assertEqual(COMPRESS_DATA, orig)
Stefan Herbrechtsmeiera14bee02022-08-19 16:25:32 +02006227
6228 def testCompUtilVersions(self):
6229 """Test tool version of compression algorithms"""
6230 for bintool in self.comp_bintools.values():
6231 self._CheckBintool(bintool)
6232 version = bintool.version()
6233 self.assertRegex(version, '^v?[0-9]+[0-9.]*')
6234
6235 def testCompUtilPadding(self):
6236 """Test padding of compression algorithms"""
Jiaxun Yangc6931742025-04-10 06:43:03 -06006237 # Skip zstd and lz4 because they doesn't support padding
6238 for bintool in [v for k,v in self.comp_bintools.items()
6239 if not k in ['zstd', 'lz4']]:
Stefan Herbrechtsmeiera14bee02022-08-19 16:25:32 +02006240 self._CheckBintool(bintool)
6241 data = bintool.compress(COMPRESS_DATA)
6242 self.assertNotEqual(COMPRESS_DATA, data)
6243 data += tools.get_bytes(0, 64)
6244 orig = bintool.decompress(data)
Brandon Maiera657bc62024-06-04 16:16:05 +00006245 self.assertEqual(COMPRESS_DATA, orig)
Stefan Herbrechtsmeiera14bee02022-08-19 16:25:32 +02006246
Stefan Herbrechtsmeiera5e4dcb2022-08-19 16:25:38 +02006247 def testCompressDtbZstd(self):
6248 """Test that zstd compress of device-tree files failed"""
6249 with self.assertRaises(ValueError) as e:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006250 self._DoTestFile('251_compress_dtb_zstd.dts')
Stefan Herbrechtsmeiera5e4dcb2022-08-19 16:25:38 +02006251 self.assertIn("Node '/binman/u-boot-dtb': The zstd compression "
6252 "requires a length header", str(e.exception))
6253
Quentin Schulz9b5c6482022-09-02 15:10:48 +02006254 def testMkimageMultipleDataFiles(self):
6255 """Test passing multiple files to mkimage in a mkimage entry"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006256 self._SetupSplElf()
6257 self._SetupTplElf()
Quentin Schulz9b5c6482022-09-02 15:10:48 +02006258 data = self._DoReadFile('252_mkimage_mult_data.dts')
6259 # Size of files are packed in their 4B big-endian format
6260 expect = struct.pack('>I', len(U_BOOT_TPL_DATA))
6261 expect += struct.pack('>I', len(U_BOOT_SPL_DATA))
6262 # Size info is always followed by a 4B zero value.
6263 expect += tools.get_bytes(0, 4)
6264 expect += U_BOOT_TPL_DATA
6265 # All but last files are 4B-aligned
6266 align_pad = len(U_BOOT_TPL_DATA) % 4
6267 if align_pad:
6268 expect += tools.get_bytes(0, align_pad)
6269 expect += U_BOOT_SPL_DATA
6270 self.assertEqual(expect, data[-len(expect):])
6271
Marek Vasutf7413f02023-07-18 07:23:58 -06006272 def testMkimageMultipleExpanded(self):
6273 """Test passing multiple files to mkimage in a mkimage entry"""
6274 self._SetupSplElf()
6275 self._SetupTplElf()
6276 entry_args = {
6277 'spl-bss-pad': 'y',
6278 'spl-dtb': 'y',
6279 }
6280 data = self._DoReadFileDtb('252_mkimage_mult_data.dts',
6281 use_expanded=True, entry_args=entry_args)[0]
6282 pad_len = 10
6283 tpl_expect = U_BOOT_TPL_DATA
6284 spl_expect = U_BOOT_SPL_NODTB_DATA + tools.get_bytes(0, pad_len)
6285 spl_expect += U_BOOT_SPL_DTB_DATA
6286
6287 content = data[0x40:]
6288 lens = struct.unpack('>III', content[:12])
6289
6290 # Size of files are packed in their 4B big-endian format
6291 # Size info is always followed by a 4B zero value.
6292 self.assertEqual(len(tpl_expect), lens[0])
6293 self.assertEqual(len(spl_expect), lens[1])
6294 self.assertEqual(0, lens[2])
6295
6296 rest = content[12:]
6297 self.assertEqual(tpl_expect, rest[:len(tpl_expect)])
6298
6299 rest = rest[len(tpl_expect):]
6300 align_pad = len(tpl_expect) % 4
6301 self.assertEqual(tools.get_bytes(0, align_pad), rest[:align_pad])
6302 rest = rest[align_pad:]
6303 self.assertEqual(spl_expect, rest)
6304
Quentin Schulz9b5c6482022-09-02 15:10:48 +02006305 def testMkimageMultipleNoContent(self):
6306 """Test passing multiple data files to mkimage with one data file having no content"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006307 self._SetupSplElf()
Quentin Schulz9b5c6482022-09-02 15:10:48 +02006308 with self.assertRaises(ValueError) as exc:
6309 self._DoReadFile('253_mkimage_mult_no_content.dts')
6310 self.assertIn('Could not complete processing of contents',
6311 str(exc.exception))
6312
Quentin Schulz0d3a9262022-09-02 15:10:49 +02006313 def testMkimageFilename(self):
6314 """Test using mkimage to build a binary with a filename"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006315 self._SetupSplElf()
Quentin Schulz0d3a9262022-09-02 15:10:49 +02006316 retcode = self._DoTestFile('254_mkimage_filename.dts')
6317 self.assertEqual(0, retcode)
6318 fname = tools.get_output_filename('mkimage-test.bin')
6319 self.assertTrue(os.path.exists(fname))
6320
Simon Glass56d05412022-02-28 07:16:54 -07006321 def testVpl(self):
6322 """Test that an image with VPL and its device tree can be created"""
6323 # ELF file with a '__bss_size' symbol
6324 self._SetupVplElf()
6325 data = self._DoReadFile('255_u_boot_vpl.dts')
6326 self.assertEqual(U_BOOT_VPL_DATA + U_BOOT_VPL_DTB_DATA, data)
6327
6328 def testVplNoDtb(self):
6329 """Test that an image with vpl/u-boot-vpl-nodtb.bin can be created"""
6330 self._SetupVplElf()
6331 data = self._DoReadFile('256_u_boot_vpl_nodtb.dts')
6332 self.assertEqual(U_BOOT_VPL_NODTB_DATA,
6333 data[:len(U_BOOT_VPL_NODTB_DATA)])
6334
6335 def testExpandedVpl(self):
6336 """Test that an expanded entry type is selected for TPL when needed"""
6337 self._SetupVplElf()
6338
6339 entry_args = {
6340 'vpl-bss-pad': 'y',
6341 'vpl-dtb': 'y',
6342 }
6343 self._DoReadFileDtb('257_fdt_incl_vpl.dts', use_expanded=True,
6344 entry_args=entry_args)
6345 image = control.images['image']
6346 entries = image.GetEntries()
6347 self.assertEqual(1, len(entries))
6348
6349 # We only have u-boot-vpl, which be expanded
6350 self.assertIn('u-boot-vpl', entries)
6351 entry = entries['u-boot-vpl']
6352 self.assertEqual('u-boot-vpl-expanded', entry.etype)
6353 subent = entry.GetEntries()
6354 self.assertEqual(3, len(subent))
6355 self.assertIn('u-boot-vpl-nodtb', subent)
6356 self.assertIn('u-boot-vpl-bss-pad', subent)
6357 self.assertIn('u-boot-vpl-dtb', subent)
6358
6359 def testVplBssPadMissing(self):
6360 """Test that a missing symbol is detected"""
6361 self._SetupVplElf('u_boot_ucode_ptr')
6362 with self.assertRaises(ValueError) as e:
6363 self._DoReadFile('258_vpl_bss_pad.dts')
6364 self.assertIn('Expected __bss_size symbol in vpl/u-boot-vpl',
6365 str(e.exception))
6366
Neha Malcom Francis3eb4be32022-10-17 16:36:25 +05306367 def testSymlink(self):
Andrew Davis6b463da2023-07-22 00:14:44 +05306368 """Test that image files can be symlinked"""
Neha Malcom Francis3eb4be32022-10-17 16:36:25 +05306369 retcode = self._DoTestFile('259_symlink.dts', debug=True, map=True)
6370 self.assertEqual(0, retcode)
6371 image = control.images['test_image']
6372 fname = tools.get_output_filename('test_image.bin')
6373 sname = tools.get_output_filename('symlink_to_test.bin')
6374 self.assertTrue(os.path.islink(sname))
6375 self.assertEqual(os.readlink(sname), fname)
Alper Nebi Yasake63ca5a2022-03-27 18:31:45 +03006376
Andrew Davis6b463da2023-07-22 00:14:44 +05306377 def testSymlinkOverwrite(self):
6378 """Test that symlinked images can be overwritten"""
6379 testdir = TestFunctional._MakeInputDir('symlinktest')
6380 self._DoTestFile('259_symlink.dts', debug=True, map=True, output_dir=testdir)
6381 # build the same image again in the same directory so that existing symlink is present
6382 self._DoTestFile('259_symlink.dts', debug=True, map=True, output_dir=testdir)
6383 fname = tools.get_output_filename('test_image.bin')
6384 sname = tools.get_output_filename('symlink_to_test.bin')
6385 self.assertTrue(os.path.islink(sname))
6386 self.assertEqual(os.readlink(sname), fname)
6387
Simon Glass37f85de2022-10-20 18:22:47 -06006388 def testSymbolsElf(self):
6389 """Test binman can assign symbols embedded in an ELF file"""
6390 if not elf.ELF_TOOLS:
6391 self.skipTest('Python elftools not available')
6392 self._SetupTplElf('u_boot_binman_syms')
6393 self._SetupVplElf('u_boot_binman_syms')
6394 self._SetupSplElf('u_boot_binman_syms')
6395 data = self._DoReadFileDtb('260_symbols_elf.dts')[0]
6396 image_fname = tools.get_output_filename('image.bin')
6397
6398 image = control.images['image']
6399 entries = image.GetEntries()
6400
6401 for entry in entries.values():
6402 # No symbols in u-boot and it has faked contents anyway
6403 if entry.name == 'u-boot':
6404 continue
6405 edata = data[entry.image_pos:entry.image_pos + entry.size]
6406 efname = tools.get_output_filename(f'edata-{entry.name}')
6407 tools.write_file(efname, edata)
6408
6409 syms = elf.GetSymbolFileOffset(efname, ['_binman_u_boot'])
6410 re_name = re.compile('_binman_(u_boot_(.*))_prop_(.*)')
6411 for name, sym in syms.items():
6412 msg = 'test'
6413 val = elf.GetSymbolValue(sym, edata, msg)
6414 entry_m = re_name.match(name)
6415 if entry_m:
6416 ename, prop = entry_m.group(1), entry_m.group(3)
6417 entry, entry_name, prop_name = image.LookupEntry(entries,
6418 name, msg)
Simon Glassd3d3a102025-02-19 08:11:16 -07006419 expect_val = None
Simon Glass37f85de2022-10-20 18:22:47 -06006420 if prop_name == 'offset':
6421 expect_val = entry.offset
6422 elif prop_name == 'image_pos':
6423 expect_val = entry.image_pos
6424 elif prop_name == 'size':
6425 expect_val = entry.size
6426 self.assertEqual(expect_val, val)
6427
6428 def testSymbolsElfBad(self):
6429 """Check error when trying to write symbols without the elftools lib"""
6430 if not elf.ELF_TOOLS:
6431 self.skipTest('Python elftools not available')
6432 self._SetupTplElf('u_boot_binman_syms')
6433 self._SetupVplElf('u_boot_binman_syms')
6434 self._SetupSplElf('u_boot_binman_syms')
6435 try:
6436 elf.ELF_TOOLS = False
6437 with self.assertRaises(ValueError) as exc:
6438 self._DoReadFileDtb('260_symbols_elf.dts')
6439 finally:
6440 elf.ELF_TOOLS = True
6441 self.assertIn(
6442 "Section '/binman': entry '/binman/u-boot-spl-elf': "
6443 'Cannot write symbols to an ELF file without Python elftools',
6444 str(exc.exception))
6445
Simon Glassde244162023-01-07 14:07:08 -07006446 def testSectionFilename(self):
6447 """Check writing of section contents to a file"""
6448 data = self._DoReadFile('261_section_fname.dts')
6449 expected = (b'&&' + U_BOOT_DATA + b'&&&' +
6450 tools.get_bytes(ord('!'), 7) +
6451 U_BOOT_DATA + tools.get_bytes(ord('&'), 12))
6452 self.assertEqual(expected, data)
6453
6454 sect_fname = tools.get_output_filename('outfile.bin')
6455 self.assertTrue(os.path.exists(sect_fname))
6456 sect_data = tools.read_file(sect_fname)
6457 self.assertEqual(U_BOOT_DATA, sect_data)
6458
Simon Glass1e9e61c2023-01-07 14:07:12 -07006459 def testAbsent(self):
6460 """Check handling of absent entries"""
6461 data = self._DoReadFile('262_absent.dts')
6462 self.assertEqual(U_BOOT_DATA + U_BOOT_IMG_DATA, data)
6463
Simon Glassad5cfe12023-01-07 14:07:14 -07006464 def testPackTeeOsOptional(self):
6465 """Test that an image with an optional TEE binary can be created"""
6466 entry_args = {
6467 'tee-os-path': 'tee.elf',
6468 }
6469 data = self._DoReadFileDtb('263_tee_os_opt.dts',
6470 entry_args=entry_args)[0]
6471 self.assertEqual(U_BOOT_DATA + U_BOOT_IMG_DATA, data)
6472
6473 def checkFitTee(self, dts, tee_fname):
6474 """Check that a tee-os entry works and returns data
6475
6476 Args:
6477 dts (str): Device tree filename to use
6478 tee_fname (str): filename containing tee-os
6479
6480 Returns:
6481 bytes: Image contents
6482 """
6483 if not elf.ELF_TOOLS:
6484 self.skipTest('Python elftools not available')
6485 entry_args = {
6486 'of-list': 'test-fdt1 test-fdt2',
6487 'default-dt': 'test-fdt2',
6488 'tee-os-path': tee_fname,
6489 }
6490 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
6491 data = self._DoReadFileDtb(dts, entry_args=entry_args,
6492 extra_indirs=[test_subdir])[0]
6493 return data
6494
6495 def testFitTeeOsOptionalFit(self):
6496 """Test an image with a FIT with an optional OP-TEE binary"""
6497 data = self.checkFitTee('264_tee_os_opt_fit.dts', 'tee.bin')
6498
6499 # There should be only one node, holding the data set up in SetUpClass()
6500 # for tee.bin
6501 dtb = fdt.Fdt.FromData(data)
6502 dtb.Scan()
6503 node = dtb.GetNode('/images/tee-1')
6504 self.assertEqual(TEE_ADDR,
6505 fdt_util.fdt32_to_cpu(node.props['load'].value))
6506 self.assertEqual(TEE_ADDR,
6507 fdt_util.fdt32_to_cpu(node.props['entry'].value))
6508 self.assertEqual(U_BOOT_DATA, node.props['data'].bytes)
6509
Simon Glass14d64e32025-04-29 07:21:59 -06006510 with terminal.capture() as (stdout, stderr):
Jonas Karlmanb2be3e42023-07-18 20:34:36 +00006511 self.checkFitTee('264_tee_os_opt_fit.dts', '')
6512 err = stderr.getvalue()
6513 self.assertRegex(
6514 err,
6515 "Image '.*' is missing optional external blobs but is still functional: tee-os")
6516
Simon Glassad5cfe12023-01-07 14:07:14 -07006517 def testFitTeeOsOptionalFitBad(self):
6518 """Test an image with a FIT with an optional OP-TEE binary"""
6519 with self.assertRaises(ValueError) as exc:
6520 self.checkFitTee('265_tee_os_opt_fit_bad.dts', 'tee.bin')
6521 self.assertIn(
6522 "Node '/binman/fit': subnode 'images/@tee-SEQ': Failed to read ELF file: Magic number does not match",
6523 str(exc.exception))
6524
6525 def testFitTeeOsBad(self):
6526 """Test an OP-TEE binary with wrong formats"""
6527 self.make_tee_bin('tee.bad1', 123)
6528 with self.assertRaises(ValueError) as exc:
6529 self.checkFitTee('264_tee_os_opt_fit.dts', 'tee.bad1')
6530 self.assertIn(
6531 "Node '/binman/fit/images/@tee-SEQ/tee-os': OP-TEE paged mode not supported",
6532 str(exc.exception))
6533
6534 self.make_tee_bin('tee.bad2', 0, b'extra data')
6535 with self.assertRaises(ValueError) as exc:
6536 self.checkFitTee('264_tee_os_opt_fit.dts', 'tee.bad2')
6537 self.assertIn(
6538 "Node '/binman/fit/images/@tee-SEQ/tee-os': Invalid OP-TEE file: size mismatch (expected 0x4, have 0xe)",
6539 str(exc.exception))
6540
Yannic Moog4a333ea2025-06-13 14:02:41 +02006541 def testExtblobMissingOptional(self):
Simon Glass63328f12023-01-07 14:07:15 -07006542 """Test an image with an external blob that is optional"""
Simon Glass14d64e32025-04-29 07:21:59 -06006543 with terminal.capture() as (stdout, stderr):
Simon Glass63328f12023-01-07 14:07:15 -07006544 data = self._DoReadFile('266_blob_ext_opt.dts')
6545 self.assertEqual(REFCODE_DATA, data)
Yannic Moog4a333ea2025-06-13 14:02:41 +02006546 self.assertNotIn(MISSING_DATA, data)
Simon Glass63328f12023-01-07 14:07:15 -07006547
Simon Glass7447a9d2023-01-11 16:10:12 -07006548 def testSectionInner(self):
6549 """Test an inner section with a size"""
6550 data = self._DoReadFile('267_section_inner.dts')
6551 expected = U_BOOT_DATA + tools.get_bytes(0, 12)
6552 self.assertEqual(expected, data)
6553
Simon Glassa4948b22023-01-11 16:10:14 -07006554 def testNull(self):
6555 """Test an image with a null entry"""
6556 data = self._DoReadFile('268_null.dts')
6557 self.assertEqual(U_BOOT_DATA + b'\xff\xff\xff\xff' + U_BOOT_IMG_DATA, data)
6558
Simon Glassf1ee03b2023-01-11 16:10:16 -07006559 def testOverlap(self):
6560 """Test an image with a overlapping entry"""
6561 data = self._DoReadFile('269_overlap.dts')
6562 self.assertEqual(U_BOOT_DATA[:1] + b'aa' + U_BOOT_DATA[3:], data)
6563
6564 image = control.images['image']
6565 entries = image.GetEntries()
6566
6567 self.assertIn('inset', entries)
6568 inset = entries['inset']
6569 self.assertEqual(1, inset.offset);
6570 self.assertEqual(1, inset.image_pos);
6571 self.assertEqual(2, inset.size);
6572
6573 def testOverlapNull(self):
6574 """Test an image with a null overlap"""
6575 data = self._DoReadFile('270_overlap_null.dts')
6576 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
6577
6578 # Check the FMAP
6579 fhdr, fentries = fmap_util.DecodeFmap(data[len(U_BOOT_DATA):])
6580 self.assertEqual(4, fhdr.nareas)
6581 fiter = iter(fentries)
6582
6583 fentry = next(fiter)
6584 self.assertEqual(b'SECTION', fentry.name)
6585 self.assertEqual(0, fentry.offset)
6586 self.assertEqual(len(U_BOOT_DATA), fentry.size)
6587 self.assertEqual(0, fentry.flags)
6588
6589 fentry = next(fiter)
6590 self.assertEqual(b'U_BOOT', fentry.name)
6591 self.assertEqual(0, fentry.offset)
6592 self.assertEqual(len(U_BOOT_DATA), fentry.size)
6593 self.assertEqual(0, fentry.flags)
6594
6595 # Make sure that the NULL entry appears in the FMAP
6596 fentry = next(fiter)
6597 self.assertEqual(b'NULL', fentry.name)
6598 self.assertEqual(1, fentry.offset)
6599 self.assertEqual(2, fentry.size)
6600 self.assertEqual(0, fentry.flags)
6601
6602 fentry = next(fiter)
6603 self.assertEqual(b'FMAP', fentry.name)
6604 self.assertEqual(len(U_BOOT_DATA), fentry.offset)
6605
6606 def testOverlapBad(self):
6607 """Test an image with a bad overlapping entry"""
6608 with self.assertRaises(ValueError) as exc:
6609 self._DoReadFile('271_overlap_bad.dts')
6610 self.assertIn(
6611 "Node '/binman/inset': Offset 0x10 (16) ending at 0x12 (18) must overlap with existing entries",
6612 str(exc.exception))
6613
6614 def testOverlapNoOffset(self):
6615 """Test an image with a bad overlapping entry"""
6616 with self.assertRaises(ValueError) as exc:
6617 self._DoReadFile('272_overlap_no_size.dts')
6618 self.assertIn(
6619 "Node '/binman/inset': 'fill' entry is missing properties: size",
6620 str(exc.exception))
6621
Simon Glasse0035c92023-01-11 16:10:17 -07006622 def testBlobSymbol(self):
6623 """Test a blob with symbols read from an ELF file"""
6624 elf_fname = self.ElfTestFile('blob_syms')
6625 TestFunctional._MakeInputFile('blob_syms', tools.read_file(elf_fname))
6626 TestFunctional._MakeInputFile('blob_syms.bin',
6627 tools.read_file(self.ElfTestFile('blob_syms.bin')))
6628
6629 data = self._DoReadFile('273_blob_symbol.dts')
6630
6631 syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
6632 addr = elf.GetSymbolAddress(elf_fname, '__my_start_sym')
6633 self.assertEqual(syms['_binman_sym_magic'].address, addr)
6634 self.assertEqual(syms['_binman_inset_prop_offset'].address, addr + 4)
6635 self.assertEqual(syms['_binman_inset_prop_size'].address, addr + 8)
6636
6637 sym_values = struct.pack('<LLL', elf.BINMAN_SYM_MAGIC_VALUE, 4, 8)
6638 expected = sym_values
6639 self.assertEqual(expected, data[:len(expected)])
6640
Simon Glass49e9c002023-01-11 16:10:19 -07006641 def testOffsetFromElf(self):
6642 """Test a blob with symbols read from an ELF file"""
6643 elf_fname = self.ElfTestFile('blob_syms')
6644 TestFunctional._MakeInputFile('blob_syms', tools.read_file(elf_fname))
6645 TestFunctional._MakeInputFile('blob_syms.bin',
6646 tools.read_file(self.ElfTestFile('blob_syms.bin')))
6647
6648 data = self._DoReadFile('274_offset_from_elf.dts')
6649
6650 syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
6651 base = elf.GetSymbolAddress(elf_fname, '__my_start_sym')
6652
6653 image = control.images['image']
6654 entries = image.GetEntries()
6655
6656 self.assertIn('inset', entries)
6657 inset = entries['inset']
6658
6659 self.assertEqual(base + 4, inset.offset);
6660 self.assertEqual(base + 4, inset.image_pos);
6661 self.assertEqual(4, inset.size);
6662
6663 self.assertIn('inset2', entries)
6664 inset = entries['inset2']
6665 self.assertEqual(base + 8, inset.offset);
6666 self.assertEqual(base + 8, inset.image_pos);
6667 self.assertEqual(4, inset.size);
6668
Jonas Karlmanc59ea892023-01-21 19:01:39 +00006669 def testFitAlign(self):
6670 """Test an image with an FIT with aligned external data"""
6671 data = self._DoReadFile('275_fit_align.dts')
6672 self.assertEqual(4096, len(data))
6673
6674 dtb = fdt.Fdt.FromData(data)
6675 dtb.Scan()
6676
6677 props = self._GetPropTree(dtb, ['data-position'])
6678 expected = {
6679 'u-boot:data-position': 1024,
6680 'fdt-1:data-position': 2048,
6681 'fdt-2:data-position': 3072,
6682 }
6683 self.assertEqual(expected, props)
6684
Jonas Karlman490f73c2023-01-21 19:02:12 +00006685 def testFitFirmwareLoadables(self):
6686 """Test an image with an FIT that use fit,firmware"""
6687 if not elf.ELF_TOOLS:
6688 self.skipTest('Python elftools not available')
6689 entry_args = {
6690 'of-list': 'test-fdt1',
6691 'default-dt': 'test-fdt1',
6692 'atf-bl31-path': 'bl31.elf',
6693 'tee-os-path': 'missing.bin',
6694 }
6695 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
Simon Glass14d64e32025-04-29 07:21:59 -06006696 with terminal.capture() as (stdout, stderr):
Simon Glass62f85902023-02-23 18:18:01 -07006697 data = self._DoReadFileDtb(
6698 '276_fit_firmware_loadables.dts',
6699 entry_args=entry_args,
6700 extra_indirs=[test_subdir])[0]
Jonas Karlman490f73c2023-01-21 19:02:12 +00006701
6702 dtb = fdt.Fdt.FromData(data)
6703 dtb.Scan()
6704
6705 node = dtb.GetNode('/configurations/conf-uboot-1')
6706 self.assertEqual('u-boot', node.props['firmware'].value)
6707 self.assertEqual(['atf-1', 'atf-2'],
6708 fdt_util.GetStringList(node, 'loadables'))
6709
6710 node = dtb.GetNode('/configurations/conf-atf-1')
6711 self.assertEqual('atf-1', node.props['firmware'].value)
6712 self.assertEqual(['u-boot', 'atf-2'],
6713 fdt_util.GetStringList(node, 'loadables'))
6714
6715 node = dtb.GetNode('/configurations/conf-missing-uboot-1')
6716 self.assertEqual('u-boot', node.props['firmware'].value)
6717 self.assertEqual(['atf-1', 'atf-2'],
6718 fdt_util.GetStringList(node, 'loadables'))
6719
6720 node = dtb.GetNode('/configurations/conf-missing-atf-1')
6721 self.assertEqual('atf-1', node.props['firmware'].value)
6722 self.assertEqual(['u-boot', 'atf-2'],
6723 fdt_util.GetStringList(node, 'loadables'))
6724
6725 node = dtb.GetNode('/configurations/conf-missing-tee-1')
6726 self.assertEqual('atf-1', node.props['firmware'].value)
6727 self.assertEqual(['u-boot', 'atf-2'],
6728 fdt_util.GetStringList(node, 'loadables'))
6729
Simon Glass9a1c7262023-02-22 12:14:49 -07006730 def testTooldir(self):
6731 """Test that we can specify the tooldir"""
Simon Glass14d64e32025-04-29 07:21:59 -06006732 with terminal.capture() as (stdout, stderr):
Simon Glass9a1c7262023-02-22 12:14:49 -07006733 self.assertEqual(0, self._DoBinman('--tooldir', 'fred',
6734 'tool', '-l'))
6735 self.assertEqual('fred', bintool.Bintool.tooldir)
6736
6737 # Check that the toolpath is updated correctly
6738 self.assertEqual(['fred'], tools.tool_search_paths)
6739
6740 # Try with a few toolpaths; the tooldir should be at the end
Simon Glass14d64e32025-04-29 07:21:59 -06006741 with terminal.capture() as (stdout, stderr):
Simon Glass9a1c7262023-02-22 12:14:49 -07006742 self.assertEqual(0, self._DoBinman(
6743 '--toolpath', 'mary', '--toolpath', 'anna', '--tooldir', 'fred',
6744 'tool', '-l'))
6745 self.assertEqual(['mary', 'anna', 'fred'], tools.tool_search_paths)
6746
Simon Glass49b77e82023-03-02 17:02:44 -07006747 def testReplaceSectionEntry(self):
6748 """Test replacing an entry in a section"""
6749 expect_data = b'w' * len(U_BOOT_DATA + COMPRESS_DATA)
6750 entry_data, expected_fdtmap, image = self._RunReplaceCmd('section/blob',
6751 expect_data, dts='241_replace_section_simple.dts')
6752 self.assertEqual(expect_data, entry_data)
6753
6754 entries = image.GetEntries()
6755 self.assertIn('section', entries)
6756 section = entries['section']
6757
6758 sect_entries = section.GetEntries()
6759 self.assertIn('blob', sect_entries)
6760 entry = sect_entries['blob']
6761 self.assertEqual(len(expect_data), entry.size)
6762
6763 fname = tools.get_output_filename('image-updated.bin')
6764 data = tools.read_file(fname)
6765
6766 new_blob_data = data[entry.image_pos:entry.image_pos + len(expect_data)]
6767 self.assertEqual(expect_data, new_blob_data)
6768
6769 self.assertEqual(U_BOOT_DATA,
6770 data[entry.image_pos + len(expect_data):]
6771 [:len(U_BOOT_DATA)])
6772
6773 def testReplaceSectionDeep(self):
6774 """Test replacing an entry in two levels of sections"""
6775 expect_data = b'w' * len(U_BOOT_DATA + COMPRESS_DATA)
6776 entry_data, expected_fdtmap, image = self._RunReplaceCmd(
6777 'section/section/blob', expect_data,
6778 dts='278_replace_section_deep.dts')
6779 self.assertEqual(expect_data, entry_data)
6780
6781 entries = image.GetEntries()
6782 self.assertIn('section', entries)
6783 section = entries['section']
6784
6785 subentries = section.GetEntries()
6786 self.assertIn('section', subentries)
6787 section = subentries['section']
6788
6789 sect_entries = section.GetEntries()
6790 self.assertIn('blob', sect_entries)
6791 entry = sect_entries['blob']
6792 self.assertEqual(len(expect_data), entry.size)
6793
6794 fname = tools.get_output_filename('image-updated.bin')
6795 data = tools.read_file(fname)
6796
6797 new_blob_data = data[entry.image_pos:entry.image_pos + len(expect_data)]
6798 self.assertEqual(expect_data, new_blob_data)
6799
6800 self.assertEqual(U_BOOT_DATA,
6801 data[entry.image_pos + len(expect_data):]
6802 [:len(U_BOOT_DATA)])
6803
6804 def testReplaceFitSibling(self):
6805 """Test an image with a FIT inside where we replace its sibling"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006806 self._SetupSplElf()
Simon Glass49b77e82023-03-02 17:02:44 -07006807 fname = TestFunctional._MakeInputFile('once', b'available once')
6808 self._DoReadFileRealDtb('277_replace_fit_sibling.dts')
6809 os.remove(fname)
6810
6811 try:
6812 tmpdir, updated_fname = self._SetupImageInTmpdir()
6813
6814 fname = os.path.join(tmpdir, 'update-blob')
6815 expected = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) + 1)
6816 tools.write_file(fname, expected)
6817
6818 self._DoBinman('replace', '-i', updated_fname, 'blob', '-f', fname)
6819 data = tools.read_file(updated_fname)
6820 start = len(U_BOOT_DTB_DATA)
6821 self.assertEqual(expected, data[start:start + len(expected)])
6822 map_fname = os.path.join(tmpdir, 'image-updated.map')
6823 self.assertFalse(os.path.exists(map_fname))
6824 finally:
6825 shutil.rmtree(tmpdir)
6826
Simon Glassc3fe97f2023-03-02 17:02:45 -07006827 def testX509Cert(self):
6828 """Test creating an X509 certificate"""
6829 keyfile = self.TestFile('key.key')
6830 entry_args = {
6831 'keyfile': keyfile,
6832 }
6833 data = self._DoReadFileDtb('279_x509_cert.dts',
6834 entry_args=entry_args)[0]
6835 cert = data[:-4]
6836 self.assertEqual(U_BOOT_DATA, data[-4:])
6837
6838 # TODO: verify the signature
6839
6840 def testX509CertMissing(self):
6841 """Test that binman still produces an image if openssl is missing"""
6842 keyfile = self.TestFile('key.key')
6843 entry_args = {
6844 'keyfile': 'keyfile',
6845 }
Simon Glass14d64e32025-04-29 07:21:59 -06006846 with terminal.capture() as (_, stderr):
Simon Glassc3fe97f2023-03-02 17:02:45 -07006847 self._DoTestFile('279_x509_cert.dts',
6848 force_missing_bintools='openssl',
6849 entry_args=entry_args)
6850 err = stderr.getvalue()
6851 self.assertRegex(err, "Image 'image'.*missing bintools.*: openssl")
6852
Jonas Karlman35305492023-02-25 19:01:33 +00006853 def testPackRockchipTpl(self):
6854 """Test that an image with a Rockchip TPL binary can be created"""
Simon Glasse3ef5ed2023-07-24 09:19:58 -06006855 data = self._DoReadFile('291_rockchip_tpl.dts')
Jonas Karlman35305492023-02-25 19:01:33 +00006856 self.assertEqual(ROCKCHIP_TPL_DATA, data[:len(ROCKCHIP_TPL_DATA)])
6857
Jonas Karlman1016ec72023-02-25 19:01:35 +00006858 def testMkimageMissingBlobMultiple(self):
6859 """Test missing blob with mkimage entry and multiple-data-files"""
Simon Glass14d64e32025-04-29 07:21:59 -06006860 with terminal.capture() as (stdout, stderr):
Simon Glasse3ef5ed2023-07-24 09:19:58 -06006861 self._DoTestFile('292_mkimage_missing_multiple.dts', allow_missing=True)
Jonas Karlman1016ec72023-02-25 19:01:35 +00006862 err = stderr.getvalue()
6863 self.assertIn("is missing external blobs and is non-functional", err)
6864
6865 with self.assertRaises(ValueError) as e:
Simon Glasse3ef5ed2023-07-24 09:19:58 -06006866 self._DoTestFile('292_mkimage_missing_multiple.dts', allow_missing=False)
Jonas Karlman1016ec72023-02-25 19:01:35 +00006867 self.assertIn("not found in input path", str(e.exception))
6868
Ivan Mikhaylov3cfcaa4d2023-03-08 01:13:40 +00006869 def _PrepareSignEnv(self, dts='280_fit_sign.dts'):
6870 """Prepare sign environment
6871
6872 Create private and public keys, add pubkey into dtb.
6873
6874 Returns:
6875 Tuple:
6876 FIT container
6877 Image name
6878 Private key
6879 DTB
6880 """
Marek Vasutf7413f02023-07-18 07:23:58 -06006881 self._SetupSplElf()
Ivan Mikhaylov3cfcaa4d2023-03-08 01:13:40 +00006882 data = self._DoReadFileRealDtb(dts)
6883 updated_fname = tools.get_output_filename('image-updated.bin')
6884 tools.write_file(updated_fname, data)
6885 dtb = tools.get_output_filename('source.dtb')
6886 private_key = tools.get_output_filename('test_key.key')
6887 public_key = tools.get_output_filename('test_key.crt')
6888 fit = tools.get_output_filename('fit.fit')
6889 key_dir = tools.get_output_dir()
6890
6891 tools.run('openssl', 'req', '-batch' , '-newkey', 'rsa:4096',
6892 '-sha256', '-new', '-nodes', '-x509', '-keyout',
6893 private_key, '-out', public_key)
6894 tools.run('fdt_add_pubkey', '-a', 'sha256,rsa4096', '-k', key_dir,
6895 '-n', 'test_key', '-r', 'conf', dtb)
6896
6897 return fit, updated_fname, private_key, dtb
6898
6899 def testSignSimple(self):
6900 """Test that a FIT container can be signed in image"""
6901 is_signed = False
6902 fit, fname, private_key, dtb = self._PrepareSignEnv()
6903
6904 # do sign with private key
6905 control.SignEntries(fname, None, private_key, 'sha256,rsa4096',
6906 ['fit'])
6907 is_signed = self._CheckSign(fit, dtb)
6908
6909 self.assertEqual(is_signed, True)
6910
6911 def testSignExactFIT(self):
6912 """Test that a FIT container can be signed and replaced in image"""
6913 is_signed = False
6914 fit, fname, private_key, dtb = self._PrepareSignEnv()
6915
6916 # Make sure we propagate the toolpath, since mkimage may not be on PATH
6917 args = []
6918 if self.toolpath:
6919 for path in self.toolpath:
6920 args += ['--toolpath', path]
6921
6922 # do sign with private key
6923 self._DoBinman(*args, 'sign', '-i', fname, '-k', private_key, '-a',
6924 'sha256,rsa4096', '-f', fit, 'fit')
6925 is_signed = self._CheckSign(fit, dtb)
6926
6927 self.assertEqual(is_signed, True)
6928
6929 def testSignNonFit(self):
6930 """Test a non-FIT entry cannot be signed"""
6931 is_signed = False
6932 fit, fname, private_key, _ = self._PrepareSignEnv(
6933 '281_sign_non_fit.dts')
6934
6935 # do sign with private key
6936 with self.assertRaises(ValueError) as e:
6937 self._DoBinman('sign', '-i', fname, '-k', private_key, '-a',
6938 'sha256,rsa4096', '-f', fit, 'u-boot')
6939 self.assertIn(
6940 "Node '/u-boot': Updating signatures is not supported with this entry type",
6941 str(e.exception))
6942
6943 def testSignMissingMkimage(self):
6944 """Test that FIT signing handles a missing mkimage tool"""
6945 fit, fname, private_key, _ = self._PrepareSignEnv()
6946
6947 # try to sign with a missing mkimage tool
6948 bintool.Bintool.set_missing_list(['mkimage'])
6949 with self.assertRaises(ValueError) as e:
6950 control.SignEntries(fname, None, private_key, 'sha256,rsa4096',
6951 ['fit'])
6952 self.assertIn("Node '/fit': Missing tool: 'mkimage'", str(e.exception))
6953
Simon Glass4abf7842023-07-18 07:23:54 -06006954 def testSymbolNoWrite(self):
6955 """Test disabling of symbol writing"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006956 self._SetupSplElf()
Simon Glass4abf7842023-07-18 07:23:54 -06006957 self.checkSymbols('282_symbols_disable.dts', U_BOOT_SPL_DATA, 0x1c,
6958 no_write_symbols=True)
6959
6960 def testSymbolNoWriteExpanded(self):
6961 """Test disabling of symbol writing in expanded entries"""
6962 entry_args = {
6963 'spl-dtb': '1',
6964 }
6965 self.checkSymbols('282_symbols_disable.dts', U_BOOT_SPL_NODTB_DATA +
6966 U_BOOT_SPL_DTB_DATA, 0x38,
6967 entry_args=entry_args, use_expanded=True,
6968 no_write_symbols=True)
6969
Marek Vasutf7413f02023-07-18 07:23:58 -06006970 def testMkimageSpecial(self):
6971 """Test mkimage ignores special hash-1 node"""
6972 data = self._DoReadFile('283_mkimage_special.dts')
6973
6974 # Just check that the data appears in the file somewhere
6975 self.assertIn(U_BOOT_DATA, data)
6976
Simon Glass2d94c422023-07-18 07:23:59 -06006977 def testFitFdtList(self):
6978 """Test an image with an FIT with the fit,fdt-list-val option"""
6979 entry_args = {
6980 'default-dt': 'test-fdt2',
6981 }
6982 data = self._DoReadFileDtb(
6983 '284_fit_fdt_list.dts',
6984 entry_args=entry_args,
6985 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
6986 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
6987 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
6988
Simon Glass83b8bfe2023-07-18 07:24:01 -06006989 def testSplEmptyBss(self):
6990 """Test an expanded SPL with a zero-size BSS"""
6991 # ELF file with a '__bss_size' symbol
6992 self._SetupSplElf(src_fname='bss_data_zero')
6993
6994 entry_args = {
6995 'spl-bss-pad': 'y',
6996 'spl-dtb': 'y',
6997 }
6998 data = self._DoReadFileDtb('285_spl_expand.dts',
6999 use_expanded=True, entry_args=entry_args)[0]
7000
Simon Glassfc792842023-07-18 07:24:04 -06007001 def testTemplate(self):
7002 """Test using a template"""
7003 TestFunctional._MakeInputFile('vga2.bin', b'#' + VGA_DATA)
7004 data = self._DoReadFile('286_template.dts')
7005 first = U_BOOT_DATA + VGA_DATA + U_BOOT_DTB_DATA
7006 second = U_BOOT_DATA + b'#' + VGA_DATA + U_BOOT_DTB_DATA
7007 self.assertEqual(U_BOOT_IMG_DATA + first + second, data)
7008
Simon Glass09490b02023-07-22 21:43:52 -06007009 dtb_fname1 = tools.get_output_filename('u-boot.dtb.tmpl1')
7010 self.assertTrue(os.path.exists(dtb_fname1))
7011 dtb = fdt.Fdt.FromData(tools.read_file(dtb_fname1))
7012 dtb.Scan()
7013 node1 = dtb.GetNode('/binman/template')
7014 self.assertTrue(node1)
7015 vga = dtb.GetNode('/binman/first/intel-vga')
7016 self.assertTrue(vga)
7017
Simon Glass54825e12023-07-22 21:43:56 -06007018 dtb_fname2 = tools.get_output_filename('u-boot.dtb.tmpl2')
7019 self.assertTrue(os.path.exists(dtb_fname2))
7020 dtb2 = fdt.Fdt.FromData(tools.read_file(dtb_fname2))
7021 dtb2.Scan()
7022 node2 = dtb2.GetNode('/binman/template')
7023 self.assertFalse(node2)
7024
Simon Glass9909c112023-07-18 07:24:05 -06007025 def testTemplateBlobMulti(self):
7026 """Test using a template with 'multiple-images' enabled"""
7027 TestFunctional._MakeInputFile('my-blob.bin', b'blob')
7028 TestFunctional._MakeInputFile('my-blob2.bin', b'other')
7029 retcode = self._DoTestFile('287_template_multi.dts')
7030
7031 self.assertEqual(0, retcode)
7032 image = control.images['image']
7033 image_fname = tools.get_output_filename('my-image.bin')
7034 data = tools.read_file(image_fname)
7035 self.assertEqual(b'blob@@@@other', data)
7036
Simon Glass5dc511b2023-07-18 07:24:06 -06007037 def testTemplateFit(self):
7038 """Test using a template in a FIT"""
7039 fit_data = self._DoReadFile('288_template_fit.dts')
7040 fname = os.path.join(self._indir, 'fit_data.fit')
7041 tools.write_file(fname, fit_data)
7042 out = tools.run('dumpimage', '-l', fname)
7043
Simon Glassaa6e0552023-07-18 07:24:07 -06007044 def testTemplateSection(self):
7045 """Test using a template in a section (not at top level)"""
7046 TestFunctional._MakeInputFile('vga2.bin', b'#' + VGA_DATA)
7047 data = self._DoReadFile('289_template_section.dts')
7048 first = U_BOOT_DATA + VGA_DATA + U_BOOT_DTB_DATA
7049 second = U_BOOT_DATA + b'#' + VGA_DATA + U_BOOT_DTB_DATA
7050 self.assertEqual(U_BOOT_IMG_DATA + first + second + first, data)
7051
Simon Glassf53a7bc2023-07-18 07:24:08 -06007052 def testMkimageSymbols(self):
7053 """Test using mkimage to build an image with symbols in it"""
7054 self._SetupSplElf('u_boot_binman_syms')
7055 data = self._DoReadFile('290_mkimage_sym.dts')
7056
7057 image = control.images['image']
7058 entries = image.GetEntries()
7059 self.assertIn('u-boot', entries)
7060 u_boot = entries['u-boot']
7061
7062 mkim = entries['mkimage']
7063 mkim_entries = mkim.GetEntries()
7064 self.assertIn('u-boot-spl', mkim_entries)
7065 spl = mkim_entries['u-boot-spl']
7066 self.assertIn('u-boot-spl2', mkim_entries)
7067 spl2 = mkim_entries['u-boot-spl2']
7068
7069 # skip the mkimage header and the area sizes
7070 mk_data = data[mkim.offset + 0x40:]
7071 size, term = struct.unpack('>LL', mk_data[:8])
7072
7073 # There should be only one image, so check that the zero terminator is
7074 # present
7075 self.assertEqual(0, term)
7076
7077 content = mk_data[8:8 + size]
7078
7079 # The image should contain the symbols from u_boot_binman_syms.c
7080 # Note that image_pos is adjusted by the base address of the image,
7081 # which is 0x10 in our test image
7082 spl_data = content[:0x18]
7083 content = content[0x1b:]
7084
7085 # After the header is a table of offsets for each image. There should
7086 # only be one image, then a 0 terminator, so figure out the real start
7087 # of the image data
7088 base = 0x40 + 8
7089
7090 # Check symbols in both u-boot-spl and u-boot-spl2
7091 for i in range(2):
7092 vals = struct.unpack('<LLQLL', spl_data)
7093
7094 # The image should contain the symbols from u_boot_binman_syms.c
7095 # Note that image_pos is adjusted by the base address of the image,
7096 # which is 0x10 in our 'u_boot_binman_syms' test image
7097 self.assertEqual(elf.BINMAN_SYM_MAGIC_VALUE, vals[0])
7098 self.assertEqual(base, vals[1])
7099 self.assertEqual(spl2.offset, vals[2])
7100 # figure out the internal positions of its components
7101 self.assertEqual(0x10 + u_boot.image_pos, vals[3])
7102
7103 # Check that spl and spl2 are actually at the indicated positions
7104 self.assertEqual(
7105 elf.BINMAN_SYM_MAGIC_VALUE,
7106 struct.unpack('<I', data[spl.image_pos:spl.image_pos + 4])[0])
7107 self.assertEqual(
7108 elf.BINMAN_SYM_MAGIC_VALUE,
7109 struct.unpack('<I', data[spl2.image_pos:spl2.image_pos + 4])[0])
7110
7111 self.assertEqual(len(U_BOOT_DATA), vals[4])
7112
7113 # Move to next
7114 spl_data = content[:0x18]
7115
Simon Glass86b3e472023-07-22 21:43:57 -06007116 def testTemplatePhandle(self):
7117 """Test using a template in a node containing a phandle"""
7118 entry_args = {
7119 'atf-bl31-path': 'bl31.elf',
7120 }
Simon Glass76ee0ca2023-08-03 17:23:58 -06007121 data = self._DoReadFileDtb('309_template_phandle.dts',
Simon Glass86b3e472023-07-22 21:43:57 -06007122 entry_args=entry_args)
7123 fname = tools.get_output_filename('image.bin')
7124 out = tools.run('dumpimage', '-l', fname)
7125
7126 # We should see the FIT description and one for each of the two images
7127 lines = out.splitlines()
7128 descs = [line.split()[-1] for line in lines if 'escription' in line]
7129 self.assertEqual(['test-desc', 'atf', 'fdt'], descs)
7130
7131 def testTemplatePhandleDup(self):
7132 """Test using a template in a node containing a phandle"""
7133 entry_args = {
7134 'atf-bl31-path': 'bl31.elf',
7135 }
7136 with self.assertRaises(ValueError) as e:
Simon Glass76ee0ca2023-08-03 17:23:58 -06007137 self._DoReadFileDtb('310_template_phandle_dup.dts',
Simon Glass86b3e472023-07-22 21:43:57 -06007138 entry_args=entry_args)
7139 self.assertIn(
7140 'Duplicate phandle 1 in nodes /binman/image/fit/images/atf/atf-bl31 and /binman/image-2/fit/images/atf/atf-bl31',
7141 str(e.exception))
7142
Neha Malcom Francis3b788942023-07-22 00:14:24 +05307143 def testTIBoardConfig(self):
7144 """Test that a schema validated board config file can be generated"""
Simon Glassf1264ba2023-07-24 09:19:59 -06007145 data = self._DoReadFile('293_ti_board_cfg.dts')
Neha Malcom Francis3b788942023-07-22 00:14:24 +05307146 self.assertEqual(TI_BOARD_CONFIG_DATA, data)
7147
Neha Malcom Francis8cd04512024-01-05 17:09:17 +05307148 def testTIBoardConfigLint(self):
7149 """Test that an incorrectly linted config file would generate error"""
7150 with self.assertRaises(ValueError) as e:
7151 data = self._DoReadFile('323_ti_board_cfg_phony.dts')
7152 self.assertIn("Yamllint error", str(e.exception))
7153
Neha Malcom Francis3b788942023-07-22 00:14:24 +05307154 def testTIBoardConfigCombined(self):
7155 """Test that a schema validated combined board config file can be generated"""
Simon Glassf1264ba2023-07-24 09:19:59 -06007156 data = self._DoReadFile('294_ti_board_cfg_combined.dts')
Neha Malcom Francis3b788942023-07-22 00:14:24 +05307157 configlen_noheader = TI_BOARD_CONFIG_DATA * 4
7158 self.assertGreater(data, configlen_noheader)
7159
7160 def testTIBoardConfigNoDataType(self):
7161 """Test that error is thrown when data type is not supported"""
7162 with self.assertRaises(ValueError) as e:
Simon Glassf1264ba2023-07-24 09:19:59 -06007163 data = self._DoReadFile('295_ti_board_cfg_no_type.dts')
Neha Malcom Francis3b788942023-07-22 00:14:24 +05307164 self.assertIn("Schema validation error", str(e.exception))
Simon Glassde244162023-01-07 14:07:08 -07007165
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307166 def testPackTiSecure(self):
7167 """Test that an image with a TI secured binary can be created"""
7168 keyfile = self.TestFile('key.key')
7169 entry_args = {
7170 'keyfile': keyfile,
7171 }
Simon Glassf1264ba2023-07-24 09:19:59 -06007172 data = self._DoReadFileDtb('296_ti_secure.dts',
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307173 entry_args=entry_args)[0]
7174 self.assertGreater(len(data), len(TI_UNSECURE_DATA))
7175
Manorit Chawdhry2e523b02023-12-29 16:16:27 +05307176 def testPackTiSecureFirewall(self):
7177 """Test that an image with a TI secured binary can be created"""
7178 keyfile = self.TestFile('key.key')
7179 entry_args = {
7180 'keyfile': keyfile,
7181 }
7182 data_no_firewall = self._DoReadFileDtb('296_ti_secure.dts',
7183 entry_args=entry_args)[0]
7184 data_firewall = self._DoReadFileDtb('324_ti_secure_firewall.dts',
7185 entry_args=entry_args)[0]
7186 self.assertGreater(len(data_firewall),len(data_no_firewall))
7187
7188 def testPackTiSecureFirewallMissingProperty(self):
7189 """Test that an image with a TI secured binary can be created"""
7190 keyfile = self.TestFile('key.key')
7191 entry_args = {
7192 'keyfile': keyfile,
7193 }
7194 with self.assertRaises(ValueError) as e:
7195 data_firewall = self._DoReadFileDtb('325_ti_secure_firewall_missing_property.dts',
7196 entry_args=entry_args)[0]
7197 self.assertRegex(str(e.exception), "Node '/binman/ti-secure': Subnode 'firewall-0-2' is missing properties: id,region")
7198
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307199 def testPackTiSecureMissingTool(self):
7200 """Test that an image with a TI secured binary (non-functional) can be created
7201 when openssl is missing"""
7202 keyfile = self.TestFile('key.key')
7203 entry_args = {
7204 'keyfile': keyfile,
7205 }
Simon Glass14d64e32025-04-29 07:21:59 -06007206 with terminal.capture() as (_, stderr):
Simon Glassf1264ba2023-07-24 09:19:59 -06007207 self._DoTestFile('296_ti_secure.dts',
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307208 force_missing_bintools='openssl',
7209 entry_args=entry_args)
7210 err = stderr.getvalue()
7211 self.assertRegex(err, "Image 'image'.*missing bintools.*: openssl")
7212
7213 def testPackTiSecureROM(self):
7214 """Test that a ROM image with a TI secured binary can be created"""
7215 keyfile = self.TestFile('key.key')
7216 entry_args = {
7217 'keyfile': keyfile,
7218 }
Simon Glassf1264ba2023-07-24 09:19:59 -06007219 data = self._DoReadFileDtb('297_ti_secure_rom.dts',
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307220 entry_args=entry_args)[0]
Simon Glassf1264ba2023-07-24 09:19:59 -06007221 data_a = self._DoReadFileDtb('299_ti_secure_rom_a.dts',
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307222 entry_args=entry_args)[0]
Simon Glassf1264ba2023-07-24 09:19:59 -06007223 data_b = self._DoReadFileDtb('300_ti_secure_rom_b.dts',
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307224 entry_args=entry_args)[0]
7225 self.assertGreater(len(data), len(TI_UNSECURE_DATA))
7226 self.assertGreater(len(data_a), len(TI_UNSECURE_DATA))
7227 self.assertGreater(len(data_b), len(TI_UNSECURE_DATA))
7228
7229 def testPackTiSecureROMCombined(self):
7230 """Test that a ROM image with a TI secured binary can be created"""
7231 keyfile = self.TestFile('key.key')
7232 entry_args = {
7233 'keyfile': keyfile,
7234 }
Simon Glassf1264ba2023-07-24 09:19:59 -06007235 data = self._DoReadFileDtb('298_ti_secure_rom_combined.dts',
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307236 entry_args=entry_args)[0]
7237 self.assertGreater(len(data), len(TI_UNSECURE_DATA))
7238
Christian Taedcke62ac29a2023-07-17 09:05:54 +02007239 def testEncryptedNoAlgo(self):
7240 """Test encrypted node with missing required properties"""
7241 with self.assertRaises(ValueError) as e:
7242 self._DoReadFileDtb('301_encrypted_no_algo.dts')
7243 self.assertIn(
7244 "Node '/binman/fit/images/u-boot/encrypted': 'encrypted' entry is missing properties: algo iv-filename",
7245 str(e.exception))
7246
7247 def testEncryptedInvalidIvfile(self):
7248 """Test encrypted node with invalid iv file"""
7249 with self.assertRaises(ValueError) as e:
7250 self._DoReadFileDtb('302_encrypted_invalid_iv_file.dts')
7251 self.assertIn("Filename 'invalid-iv-file' not found in input path",
7252 str(e.exception))
7253
7254 def testEncryptedMissingKey(self):
7255 """Test encrypted node with missing key properties"""
7256 with self.assertRaises(ValueError) as e:
7257 self._DoReadFileDtb('303_encrypted_missing_key.dts')
7258 self.assertIn(
7259 "Node '/binman/fit/images/u-boot/encrypted': Provide either 'key-filename' or 'key-source'",
7260 str(e.exception))
7261
7262 def testEncryptedKeySource(self):
7263 """Test encrypted node with key-source property"""
7264 data = self._DoReadFileDtb('304_encrypted_key_source.dts')[0]
7265
7266 dtb = fdt.Fdt.FromData(data)
7267 dtb.Scan()
7268
7269 node = dtb.GetNode('/images/u-boot/cipher')
7270 self.assertEqual('algo-name', node.props['algo'].value)
7271 self.assertEqual('key-source-value', node.props['key-source'].value)
7272 self.assertEqual(ENCRYPTED_IV_DATA,
7273 tools.to_bytes(''.join(node.props['iv'].value)))
7274 self.assertNotIn('key', node.props)
7275
7276 def testEncryptedKeyFile(self):
7277 """Test encrypted node with key-filename property"""
7278 data = self._DoReadFileDtb('305_encrypted_key_file.dts')[0]
7279
7280 dtb = fdt.Fdt.FromData(data)
7281 dtb.Scan()
7282
7283 node = dtb.GetNode('/images/u-boot/cipher')
7284 self.assertEqual('algo-name', node.props['algo'].value)
7285 self.assertEqual(ENCRYPTED_IV_DATA,
7286 tools.to_bytes(''.join(node.props['iv'].value)))
7287 self.assertEqual(ENCRYPTED_KEY_DATA,
7288 tools.to_bytes(''.join(node.props['key'].value)))
7289 self.assertNotIn('key-source', node.props)
7290
Lukas Funkee901faf2023-07-18 13:53:13 +02007291
7292 def testSplPubkeyDtb(self):
Simon Glass4b861272024-07-20 11:49:41 +01007293 """Test u_boot_spl_pubkey_dtb etype"""
7294 data = tools.read_file(self.TestFile("key.pem"))
7295 self._MakeInputFile("key.crt", data)
7296 self._DoReadFileRealDtb('306_spl_pubkey_dtb.dts')
7297 image = control.images['image']
7298 entries = image.GetEntries()
7299 dtb_entry = entries['u-boot-spl-pubkey-dtb']
7300 dtb_data = dtb_entry.GetData()
7301 dtb = fdt.Fdt.FromData(dtb_data)
7302 dtb.Scan()
Lukas Funkee901faf2023-07-18 13:53:13 +02007303
Simon Glass4b861272024-07-20 11:49:41 +01007304 signature_node = dtb.GetNode('/signature')
7305 self.assertIsNotNone(signature_node)
7306 key_node = signature_node.FindNode("key-key")
7307 self.assertIsNotNone(key_node)
7308 self.assertEqual(fdt_util.GetString(key_node, "required"), "conf")
7309 self.assertEqual(fdt_util.GetString(key_node, "algo"), "sha384,rsa4096")
7310 self.assertEqual(fdt_util.GetString(key_node, "key-name-hint"), "key")
Christian Taedcke62ac29a2023-07-17 09:05:54 +02007311
Lukas Funke712e1062023-08-03 17:22:14 +02007312 def testXilinxBootgenSigning(self):
7313 """Test xilinx-bootgen etype"""
7314 bootgen = bintool.Bintool.create('bootgen')
7315 self._CheckBintool(bootgen)
7316 data = tools.read_file(self.TestFile("key.key"))
7317 self._MakeInputFile("psk.pem", data)
7318 self._MakeInputFile("ssk.pem", data)
7319 self._SetupPmuFwlElf()
7320 self._SetupSplElf()
7321 self._DoReadFileRealDtb('307_xilinx_bootgen_sign.dts')
7322 image_fname = tools.get_output_filename('image.bin')
7323
7324 # Read partition header table and check if authentication is enabled
7325 bootgen_out = bootgen.run_cmd("-arch", "zynqmp",
7326 "-read", image_fname, "pht").splitlines()
7327 attributes = {"authentication": None,
7328 "core": None,
7329 "encryption": None}
7330
7331 for l in bootgen_out:
7332 for a in attributes.keys():
7333 if a in l:
7334 m = re.match(fr".*{a} \[([^]]+)\]", l)
7335 attributes[a] = m.group(1)
7336
7337 self.assertTrue(attributes['authentication'] == "rsa")
7338 self.assertTrue(attributes['core'] == "a53-0")
7339 self.assertTrue(attributes['encryption'] == "no")
7340
7341 def testXilinxBootgenSigningEncryption(self):
7342 """Test xilinx-bootgen etype"""
7343 bootgen = bintool.Bintool.create('bootgen')
7344 self._CheckBintool(bootgen)
7345 data = tools.read_file(self.TestFile("key.key"))
7346 self._MakeInputFile("psk.pem", data)
7347 self._MakeInputFile("ssk.pem", data)
7348 self._SetupPmuFwlElf()
7349 self._SetupSplElf()
7350 self._DoReadFileRealDtb('308_xilinx_bootgen_sign_enc.dts')
7351 image_fname = tools.get_output_filename('image.bin')
7352
7353 # Read boot header in order to verify encryption source and
7354 # encryption parameter
7355 bootgen_out = bootgen.run_cmd("-arch", "zynqmp",
7356 "-read", image_fname, "bh").splitlines()
7357 attributes = {"auth_only":
7358 {"re": r".*auth_only \[([^]]+)\]", "value": None},
7359 "encryption_keystore":
7360 {"re": r" *encryption_keystore \(0x28\) : (.*)",
7361 "value": None},
7362 }
7363
7364 for l in bootgen_out:
7365 for a in attributes.keys():
7366 if a in l:
7367 m = re.match(attributes[a]['re'], l)
7368 attributes[a] = m.group(1)
7369
7370 # Check if fsbl-attribute is set correctly
7371 self.assertTrue(attributes['auth_only'] == "true")
7372 # Check if key is stored in efuse
7373 self.assertTrue(attributes['encryption_keystore'] == "0xa5c3c5a3")
7374
7375 def testXilinxBootgenMissing(self):
7376 """Test that binman still produces an image if bootgen is missing"""
7377 data = tools.read_file(self.TestFile("key.key"))
7378 self._MakeInputFile("psk.pem", data)
7379 self._MakeInputFile("ssk.pem", data)
7380 self._SetupPmuFwlElf()
7381 self._SetupSplElf()
Simon Glass14d64e32025-04-29 07:21:59 -06007382 with terminal.capture() as (_, stderr):
Lukas Funke712e1062023-08-03 17:22:14 +02007383 self._DoTestFile('307_xilinx_bootgen_sign.dts',
7384 force_missing_bintools='bootgen')
7385 err = stderr.getvalue()
7386 self.assertRegex(err,
7387 "Image 'image'.*missing bintools.*: bootgen")
7388
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +05307389 def _GetCapsuleHeaders(self, data):
7390 """Get the capsule header contents
7391
7392 Args:
7393 data: Capsule file contents
7394
7395 Returns:
7396 Dict:
7397 key: Capsule Header name (str)
7398 value: Header field value (str)
7399 """
7400 capsule_file = os.path.join(self._indir, 'test.capsule')
7401 tools.write_file(capsule_file, data)
7402
7403 out = tools.run('mkeficapsule', '--dump-capsule', capsule_file)
7404 lines = out.splitlines()
7405
7406 re_line = re.compile(r'^([^:\-\t]*)(?:\t*\s*:\s*(.*))?$')
7407 vals = {}
7408 for line in lines:
7409 mat = re_line.match(line)
7410 if mat:
7411 vals[mat.group(1)] = mat.group(2)
7412
7413 return vals
7414
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307415 def _CheckCapsule(self, data, signed_capsule=False, version_check=False,
7416 capoemflags=False):
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +05307417 fmp_signature = "3153534D" # 'M', 'S', 'S', '1'
7418 fmp_size = "00000010"
7419 fmp_fw_version = "00000002"
7420 capsule_image_index = "00000001"
7421 oemflag = "00018000"
7422 auth_hdr_revision = "00000200"
7423 auth_hdr_cert_type = "00000EF1"
7424
7425 payload_data_len = len(EFI_CAPSULE_DATA)
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307426
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +05307427 hdr = self._GetCapsuleHeaders(data)
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307428
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +05307429 self.assertEqual(FW_MGMT_GUID.upper(), hdr['EFI_CAPSULE_HDR.CAPSULE_GUID'])
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307430
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +05307431 self.assertEqual(CAPSULE_IMAGE_GUID.upper(),
7432 hdr['FMP_CAPSULE_IMAGE_HDR.UPDATE_IMAGE_TYPE_ID'])
7433 self.assertEqual(capsule_image_index,
7434 hdr['FMP_CAPSULE_IMAGE_HDR.UPDATE_IMAGE_INDEX'])
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307435
7436 if capoemflags:
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +05307437 self.assertEqual(oemflag, hdr['EFI_CAPSULE_HDR.FLAGS'])
7438
7439 if signed_capsule:
7440 self.assertEqual(auth_hdr_revision,
7441 hdr['EFI_FIRMWARE_IMAGE_AUTH.AUTH_INFO.HDR.wREVISION'])
7442 self.assertEqual(auth_hdr_cert_type,
7443 hdr['EFI_FIRMWARE_IMAGE_AUTH.AUTH_INFO.HDR.wCERTTYPE'])
7444 self.assertEqual(WIN_CERT_TYPE_EFI_GUID.upper(),
7445 hdr['EFI_FIRMWARE_IMAGE_AUTH.AUTH_INFO.CERT_TYPE'])
7446
7447 if version_check:
7448 self.assertEqual(fmp_signature,
7449 hdr['FMP_PAYLOAD_HDR.SIGNATURE'])
7450 self.assertEqual(fmp_size,
7451 hdr['FMP_PAYLOAD_HDR.HEADER_SIZE'])
7452 self.assertEqual(fmp_fw_version,
7453 hdr['FMP_PAYLOAD_HDR.FW_VERSION'])
7454
7455 self.assertEqual(payload_data_len, int(hdr['Payload Image Size']))
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307456
Sughosh Ganu6b2d18a2023-10-10 14:40:59 +05307457 def _CheckEmptyCapsule(self, data, accept_capsule=False):
7458 if accept_capsule:
7459 capsule_hdr_guid = EMPTY_CAPSULE_ACCEPT_GUID
7460 else:
7461 capsule_hdr_guid = EMPTY_CAPSULE_REVERT_GUID
7462
7463 hdr = self._GetCapsuleHeaders(data)
7464
7465 self.assertEqual(capsule_hdr_guid.upper(),
7466 hdr['EFI_CAPSULE_HDR.CAPSULE_GUID'])
7467
7468 if accept_capsule:
7469 capsule_size = "0000002C"
7470 else:
7471 capsule_size = "0000001C"
7472 self.assertEqual(capsule_size,
7473 hdr['EFI_CAPSULE_HDR.CAPSULE_IMAGE_SIZE'])
7474
7475 if accept_capsule:
7476 self.assertEqual(CAPSULE_IMAGE_GUID.upper(), hdr['ACCEPT_IMAGE_GUID'])
7477
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307478 def testCapsuleGen(self):
7479 """Test generation of EFI capsule"""
7480 data = self._DoReadFile('311_capsule.dts')
7481
7482 self._CheckCapsule(data)
7483
7484 def testSignedCapsuleGen(self):
7485 """Test generation of EFI capsule"""
7486 data = tools.read_file(self.TestFile("key.key"))
7487 self._MakeInputFile("key.key", data)
7488 data = tools.read_file(self.TestFile("key.pem"))
7489 self._MakeInputFile("key.crt", data)
7490
7491 data = self._DoReadFile('312_capsule_signed.dts')
7492
7493 self._CheckCapsule(data, signed_capsule=True)
7494
7495 def testCapsuleGenVersionSupport(self):
7496 """Test generation of EFI capsule with version support"""
7497 data = self._DoReadFile('313_capsule_version.dts')
7498
7499 self._CheckCapsule(data, version_check=True)
7500
7501 def testCapsuleGenSignedVer(self):
7502 """Test generation of signed EFI capsule with version information"""
7503 data = tools.read_file(self.TestFile("key.key"))
7504 self._MakeInputFile("key.key", data)
7505 data = tools.read_file(self.TestFile("key.pem"))
7506 self._MakeInputFile("key.crt", data)
7507
7508 data = self._DoReadFile('314_capsule_signed_ver.dts')
7509
7510 self._CheckCapsule(data, signed_capsule=True, version_check=True)
7511
7512 def testCapsuleGenCapOemFlags(self):
7513 """Test generation of EFI capsule with OEM Flags set"""
7514 data = self._DoReadFile('315_capsule_oemflags.dts')
7515
7516 self._CheckCapsule(data, capoemflags=True)
7517
7518 def testCapsuleGenKeyMissing(self):
7519 """Test that binman errors out on missing key"""
7520 with self.assertRaises(ValueError) as e:
7521 self._DoReadFile('316_capsule_missing_key.dts')
7522
7523 self.assertIn("Both private key and public key certificate need to be provided",
7524 str(e.exception))
7525
7526 def testCapsuleGenIndexMissing(self):
7527 """Test that binman errors out on missing image index"""
7528 with self.assertRaises(ValueError) as e:
7529 self._DoReadFile('317_capsule_missing_index.dts')
7530
7531 self.assertIn("entry is missing properties: image-index",
7532 str(e.exception))
7533
7534 def testCapsuleGenGuidMissing(self):
7535 """Test that binman errors out on missing image GUID"""
7536 with self.assertRaises(ValueError) as e:
7537 self._DoReadFile('318_capsule_missing_guid.dts')
7538
7539 self.assertIn("entry is missing properties: image-guid",
7540 str(e.exception))
7541
Sughosh Ganu6b2d18a2023-10-10 14:40:59 +05307542 def testCapsuleGenAcceptCapsule(self):
7543 """Test generationg of accept EFI capsule"""
7544 data = self._DoReadFile('319_capsule_accept.dts')
7545
7546 self._CheckEmptyCapsule(data, accept_capsule=True)
7547
7548 def testCapsuleGenRevertCapsule(self):
7549 """Test generationg of revert EFI capsule"""
7550 data = self._DoReadFile('320_capsule_revert.dts')
7551
7552 self._CheckEmptyCapsule(data)
7553
7554 def testCapsuleGenAcceptGuidMissing(self):
7555 """Test that binman errors out on missing image GUID for accept capsule"""
7556 with self.assertRaises(ValueError) as e:
7557 self._DoReadFile('321_capsule_accept_missing_guid.dts')
7558
7559 self.assertIn("Image GUID needed for generating accept capsule",
7560 str(e.exception))
7561
7562 def testCapsuleGenEmptyCapsuleTypeMissing(self):
7563 """Test that capsule-type is specified"""
7564 with self.assertRaises(ValueError) as e:
7565 self._DoReadFile('322_empty_capsule_type_missing.dts')
7566
7567 self.assertIn("entry is missing properties: capsule-type",
7568 str(e.exception))
7569
7570 def testCapsuleGenAcceptOrRevertMissing(self):
7571 """Test that both accept and revert capsule are not specified"""
7572 with self.assertRaises(ValueError) as e:
7573 self._DoReadFile('323_capsule_accept_revert_missing.dts')
7574
Simon Glassa360b8f2024-06-23 11:55:06 -06007575 def test_assume_size(self):
7576 """Test handling of the assume-size property for external blob"""
7577 with self.assertRaises(ValueError) as e:
7578 self._DoTestFile('326_assume_size.dts', allow_missing=True,
7579 allow_fake_blobs=True)
7580 self.assertIn("contents size 0xa (10) exceeds section size 0x9 (9)",
7581 str(e.exception))
7582
7583 def test_assume_size_ok(self):
7584 """Test handling of the assume-size where it fits OK"""
Simon Glass14d64e32025-04-29 07:21:59 -06007585 with terminal.capture() as (stdout, stderr):
Simon Glassa360b8f2024-06-23 11:55:06 -06007586 self._DoTestFile('327_assume_size_ok.dts', allow_missing=True,
7587 allow_fake_blobs=True)
7588 err = stderr.getvalue()
7589 self.assertRegex(
7590 err,
7591 "Image '.*' has faked external blobs and is non-functional: .*")
7592
7593 def test_assume_size_no_fake(self):
7594 """Test handling of the assume-size where it fits OK"""
Simon Glass14d64e32025-04-29 07:21:59 -06007595 with terminal.capture() as (stdout, stderr):
Simon Glassa360b8f2024-06-23 11:55:06 -06007596 self._DoTestFile('327_assume_size_ok.dts', allow_missing=True)
7597 err = stderr.getvalue()
7598 self.assertRegex(
7599 err,
7600 "Image '.*' is missing external blobs and is non-functional: .*")
7601
Simon Glass5f7aadf2024-07-20 11:49:47 +01007602 def SetupAlternateDts(self):
7603 """Compile the .dts test files for alternative-fdt
7604
7605 Returns:
7606 tuple:
7607 str: Test directory created
7608 list of str: '.bin' files which we expect Binman to create
7609 """
7610 testdir = TestFunctional._MakeInputDir('dtb')
7611 dtb_list = []
7612 for fname in glob.glob(f'{self.TestFile("alt_dts")}/*.dts'):
7613 tmp_fname = fdt_util.EnsureCompiled(fname, testdir)
7614 base = os.path.splitext(os.path.basename(fname))[0]
7615 dtb_list.append(base + '.bin')
7616 shutil.move(tmp_fname, os.path.join(testdir, base + '.dtb'))
7617
7618 return testdir, dtb_list
7619
Simon Glassf3598922024-07-20 11:49:45 +01007620 def CheckAlternates(self, dts, phase, xpl_data):
7621 """Run the test for the alterative-fdt etype
7622
7623 Args:
7624 dts (str): Devicetree file to process
7625 phase (str): Phase to process ('spl', 'tpl' or 'vpl')
7626 xpl_data (bytes): Expected data for the phase's binary
7627
7628 Returns:
7629 dict of .dtb files produced
7630 key: str filename
7631 value: Fdt object
7632 """
Simon Glass5f7aadf2024-07-20 11:49:47 +01007633 dtb_list = self.SetupAlternateDts()[1]
Simon Glassf3598922024-07-20 11:49:45 +01007634
7635 entry_args = {
7636 f'{phase}-dtb': '1',
7637 f'{phase}-bss-pad': 'y',
7638 'of-spl-remove-props': 'prop-to-remove another-prop-to-get-rid-of',
7639 }
7640 data = self._DoReadFileDtb(dts, use_real_dtb=True, update_dtb=True,
7641 use_expanded=True, entry_args=entry_args)[0]
7642 self.assertEqual(xpl_data, data[:len(xpl_data)])
7643 rest = data[len(xpl_data):]
7644 pad_len = 10
7645 self.assertEqual(tools.get_bytes(0, pad_len), rest[:pad_len])
7646
7647 # Check the dtb is using the test file
7648 dtb_data = rest[pad_len:]
7649 dtb = fdt.Fdt.FromData(dtb_data)
7650 dtb.Scan()
7651 fdt_size = dtb.GetFdtObj().totalsize()
7652 self.assertEqual('model-not-set',
7653 fdt_util.GetString(dtb.GetRoot(), 'compatible'))
7654
7655 pad_len = 10
7656
7657 # Check the other output files
7658 dtbs = {}
7659 for fname in dtb_list:
7660 pathname = tools.get_output_filename(fname)
7661 self.assertTrue(os.path.exists(pathname))
7662
7663 data = tools.read_file(pathname)
7664 self.assertEqual(xpl_data, data[:len(xpl_data)])
7665 rest = data[len(xpl_data):]
7666
7667 self.assertEqual(tools.get_bytes(0, pad_len), rest[:pad_len])
7668 rest = rest[pad_len:]
7669
7670 dtb = fdt.Fdt.FromData(rest)
7671 dtb.Scan()
7672 dtbs[fname] = dtb
7673
7674 expected = 'one' if '1' in fname else 'two'
7675 self.assertEqual(f'u-boot,model-{expected}',
7676 fdt_util.GetString(dtb.GetRoot(), 'compatible'))
7677
7678 # Make sure the FDT is the same size as the 'main' one
7679 rest = rest[fdt_size:]
7680
7681 self.assertEqual(b'', rest)
7682 return dtbs
7683
7684 def testAlternatesFdt(self):
7685 """Test handling of alternates-fdt etype"""
7686 self._SetupTplElf()
7687 dtbs = self.CheckAlternates('328_alternates_fdt.dts', 'tpl',
7688 U_BOOT_TPL_NODTB_DATA)
7689 for dtb in dtbs.values():
7690 # Check for the node with the tag
7691 node = dtb.GetNode('/node')
7692 self.assertIsNotNone(node)
7693 self.assertEqual(5, len(node.props.keys()))
7694
7695 # Make sure the other node is still there
7696 self.assertIsNotNone(dtb.GetNode('/node/other-node'))
7697
7698 def testAlternatesFdtgrep(self):
7699 """Test handling of alternates-fdt etype using fdtgrep"""
7700 self._SetupTplElf()
7701 dtbs = self.CheckAlternates('329_alternates_fdtgrep.dts', 'tpl',
7702 U_BOOT_TPL_NODTB_DATA)
7703 for dtb in dtbs.values():
7704 # Check for the node with the tag
7705 node = dtb.GetNode('/node')
7706 self.assertIsNotNone(node)
7707 self.assertEqual({'some-prop', 'not-a-prop-to-remove'},
7708 node.props.keys())
7709
7710 # Make sure the other node is gone
7711 self.assertIsNone(dtb.GetNode('/node/other-node'))
7712
7713 def testAlternatesFdtgrepVpl(self):
7714 """Test handling of alternates-fdt etype using fdtgrep with vpl"""
7715 self._SetupVplElf()
7716 dtbs = self.CheckAlternates('330_alternates_vpl.dts', 'vpl',
7717 U_BOOT_VPL_NODTB_DATA)
7718
7719 def testAlternatesFdtgrepSpl(self):
7720 """Test handling of alternates-fdt etype using fdtgrep with spl"""
7721 self._SetupSplElf()
7722 dtbs = self.CheckAlternates('331_alternates_spl.dts', 'spl',
7723 U_BOOT_SPL_NODTB_DATA)
7724
7725 def testAlternatesFdtgrepInval(self):
7726 """Test alternates-fdt etype using fdtgrep with invalid phase"""
7727 self._SetupSplElf()
7728 with self.assertRaises(ValueError) as e:
7729 dtbs = self.CheckAlternates('332_alternates_inval.dts', 'spl',
7730 U_BOOT_SPL_NODTB_DATA)
7731 self.assertIn("Invalid U-Boot phase 'bad-phase': Use tpl/vpl/spl",
7732 str(e.exception))
7733
Simon Glasscd2783e2024-07-20 11:49:46 +01007734 def testFitFdtListDir(self):
7735 """Test an image with an FIT with FDT images using fit,fdt-list-dir"""
Simon Glass1bba8942024-08-26 13:11:34 -06007736 old_dir = os.getcwd()
7737 try:
7738 os.chdir(self._indir)
7739 self.CheckFitFdt('333_fit_fdt_dir.dts', False)
7740 finally:
7741 os.chdir(old_dir)
Simon Glasscd2783e2024-07-20 11:49:46 +01007742
Simon Glassd2a9d6e2024-08-26 13:11:37 -06007743 def testFitFdtListDirDefault(self):
7744 """Test an FIT fit,fdt-list-dir where the default DT in is a subdir"""
7745 old_dir = os.getcwd()
7746 try:
7747 os.chdir(self._indir)
7748 self.CheckFitFdt('333_fit_fdt_dir.dts', False,
7749 default_dt='rockchip/test-fdt2')
7750 finally:
7751 os.chdir(old_dir)
7752
Simon Glass5f7aadf2024-07-20 11:49:47 +01007753 def testFitFdtCompat(self):
7754 """Test an image with an FIT with compatible in the config nodes"""
7755 entry_args = {
7756 'of-list': 'model1 model2',
7757 'default-dt': 'model2',
7758 }
7759 testdir, dtb_list = self.SetupAlternateDts()
7760 data = self._DoReadFileDtb(
7761 '334_fit_fdt_compat.dts', use_real_dtb=True, update_dtb=True,
7762 entry_args=entry_args, extra_indirs=[testdir])[0]
7763
7764 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
7765
7766 fit = fdt.Fdt.FromData(fit_data)
7767 fit.Scan()
7768
7769 cnode = fit.GetNode('/configurations')
7770 self.assertIn('default', cnode.props)
7771 self.assertEqual('config-2', cnode.props['default'].value)
7772
7773 for seq in range(1, 2):
7774 name = f'config-{seq}'
7775 fnode = fit.GetNode('/configurations/%s' % name)
7776 self.assertIsNotNone(fnode)
7777 self.assertIn('compatible', fnode.props.keys())
7778 expected = 'one' if seq == 1 else 'two'
7779 self.assertEqual(f'u-boot,model-{expected}',
7780 fnode.props['compatible'].value)
7781
Simon Glassa04b9942024-07-20 11:49:48 +01007782 def testFitFdtPhase(self):
7783 """Test an image with an FIT with fdt-phase in the fdt nodes"""
7784 phase = 'tpl'
7785 entry_args = {
7786 f'{phase}-dtb': '1',
7787 f'{phase}-bss-pad': 'y',
7788 'of-spl-remove-props': 'prop-to-remove another-prop-to-get-rid-of',
7789 'of-list': 'model1 model2',
7790 'default-dt': 'model2',
7791 }
7792 testdir, dtb_list = self.SetupAlternateDts()
7793 data = self._DoReadFileDtb(
7794 '335_fit_fdt_phase.dts', use_real_dtb=True, update_dtb=True,
7795 entry_args=entry_args, extra_indirs=[testdir])[0]
7796 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
7797 fit = fdt.Fdt.FromData(fit_data)
7798 fit.Scan()
7799
7800 # Check that each FDT has only the expected properties for the phase
7801 for seq in range(1, 2):
7802 fnode = fit.GetNode(f'/images/fdt-{seq}')
7803 self.assertIsNotNone(fnode)
7804 dtb = fdt.Fdt.FromData(fnode.props['data'].bytes)
7805 dtb.Scan()
7806
7807 # Make sure that the 'bootph-pre-sram' tag in /node protects it from
7808 # removal
7809 node = dtb.GetNode('/node')
7810 self.assertIsNotNone(node)
7811 self.assertEqual({'some-prop', 'not-a-prop-to-remove'},
7812 node.props.keys())
7813
7814 # Make sure the other node is gone
7815 self.assertIsNone(dtb.GetNode('/node/other-node'))
7816
Simon Glassb553e8a2024-08-26 13:11:29 -06007817 def testMkeficapsuleMissing(self):
7818 """Test that binman complains if mkeficapsule is missing"""
7819 with self.assertRaises(ValueError) as e:
7820 self._DoTestFile('311_capsule.dts',
7821 force_missing_bintools='mkeficapsule')
7822 self.assertIn("Node '/binman/efi-capsule': Missing tool: 'mkeficapsule'",
7823 str(e.exception))
7824
7825 def testMkeficapsuleMissingOk(self):
7826 """Test that binman deals with mkeficapsule being missing"""
Simon Glass14d64e32025-04-29 07:21:59 -06007827 with terminal.capture() as (stdout, stderr):
Simon Glassb553e8a2024-08-26 13:11:29 -06007828 ret = self._DoTestFile('311_capsule.dts',
7829 force_missing_bintools='mkeficapsule',
7830 allow_missing=True)
7831 self.assertEqual(103, ret)
7832 err = stderr.getvalue()
7833 self.assertRegex(err, "Image 'image'.*missing bintools.*: mkeficapsule")
7834
Simon Glass4b0f4142024-08-26 13:11:40 -06007835 def testSymbolsBase(self):
7836 """Test handling of symbols-base"""
7837 self.checkSymbols('336_symbols_base.dts', U_BOOT_SPL_DATA, 0x1c,
7838 symbols_base=0)
7839
7840 def testSymbolsBaseExpanded(self):
7841 """Test handling of symbols-base with expanded entries"""
7842 entry_args = {
7843 'spl-dtb': '1',
7844 }
7845 self.checkSymbols('337_symbols_base_expand.dts', U_BOOT_SPL_NODTB_DATA +
7846 U_BOOT_SPL_DTB_DATA, 0x38,
7847 entry_args=entry_args, use_expanded=True,
7848 symbols_base=0)
7849
Simon Glass3eb30a42024-08-26 13:11:42 -06007850 def testSymbolsCompressed(self):
7851 """Test binman complains about symbols from a compressed section"""
Simon Glass14d64e32025-04-29 07:21:59 -06007852 with terminal.capture() as (stdout, stderr):
Simon Glass3eb30a42024-08-26 13:11:42 -06007853 self.checkSymbols('338_symbols_comp.dts', U_BOOT_SPL_DATA, None)
7854 out = stdout.getvalue()
7855 self.assertIn('Symbol-writing: no value for /binman/section/u-boot',
7856 out)
7857
Simon Glass9c25ef22024-08-26 13:11:43 -06007858 def testNxpImx8Image(self):
7859 """Test that binman can produce an iMX8 image"""
7860 self._DoTestFile('339_nxp_imx8.dts')
7861
Alice Guo1d334022025-04-28 18:37:39 +08007862 def testNxpHeaderDdrfw(self):
7863 """Test that binman can add a header to DDR PHY firmware images"""
7864 data = self._DoReadFile('346_nxp_ddrfw_imx95.dts')
7865 self.assertEqual(len(IMX_LPDDR_IMEM_DATA).to_bytes(4, 'little') +
7866 len(IMX_LPDDR_DMEM_DATA).to_bytes(4, 'little') +
7867 IMX_LPDDR_IMEM_DATA + IMX_LPDDR_DMEM_DATA, data)
7868
Alexander Kochetkova730a282024-09-16 11:24:46 +03007869 def testFitSignSimple(self):
7870 """Test that image with FIT and signature nodes can be signed"""
7871 if not elf.ELF_TOOLS:
7872 self.skipTest('Python elftools not available')
7873 entry_args = {
7874 'of-list': 'test-fdt1',
7875 'default-dt': 'test-fdt1',
7876 'atf-bl31-path': 'bl31.elf',
7877 }
7878 data = tools.read_file(self.TestFile("340_rsa2048.key"))
7879 self._MakeInputFile("keys/rsa2048.key", data)
7880
7881 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
7882 keys_subdir = os.path.join(self._indir, "keys")
7883 data = self._DoReadFileDtb(
7884 '340_fit_signature.dts',
7885 entry_args=entry_args,
7886 extra_indirs=[test_subdir, keys_subdir])[0]
7887
7888 dtb = fdt.Fdt.FromData(data)
7889 dtb.Scan()
7890
7891 conf = dtb.GetNode('/configurations/conf-uboot-1')
7892 self.assertIsNotNone(conf)
7893 signature = conf.FindNode('signature')
7894 self.assertIsNotNone(signature)
7895 self.assertIsNotNone(signature.props.get('value'))
7896
7897 images = dtb.GetNode('/images')
7898 self.assertIsNotNone(images)
7899 for subnode in images.subnodes:
7900 signature = subnode.FindNode('signature')
7901 self.assertIsNotNone(signature)
7902 self.assertIsNotNone(signature.props.get('value'))
7903
7904 def testFitSignKeyNotFound(self):
7905 """Test that missing keys raise an error"""
7906 if not elf.ELF_TOOLS:
7907 self.skipTest('Python elftools not available')
7908 entry_args = {
7909 'of-list': 'test-fdt1',
7910 'default-dt': 'test-fdt1',
7911 'atf-bl31-path': 'bl31.elf',
7912 }
7913 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
7914 with self.assertRaises(ValueError) as e:
7915 self._DoReadFileDtb(
7916 '340_fit_signature.dts',
7917 entry_args=entry_args,
7918 extra_indirs=[test_subdir])[0]
7919 self.assertIn(
7920 'Filename \'rsa2048.key\' not found in input path',
7921 str(e.exception))
7922
7923 def testFitSignMultipleKeyPaths(self):
7924 """Test that keys found in multiple paths raise an error"""
7925 if not elf.ELF_TOOLS:
7926 self.skipTest('Python elftools not available')
7927 entry_args = {
7928 'of-list': 'test-fdt1',
7929 'default-dt': 'test-fdt1',
7930 'atf-bl31-path': 'bl31.elf',
7931 }
7932 data = tools.read_file(self.TestFile("340_rsa2048.key"))
7933 self._MakeInputFile("keys1/rsa2048.key", data)
7934 data = tools.read_file(self.TestFile("340_rsa2048.key"))
7935 self._MakeInputFile("keys2/conf-rsa2048.key", data)
7936
7937 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
7938 keys_subdir1 = os.path.join(self._indir, "keys1")
7939 keys_subdir2 = os.path.join(self._indir, "keys2")
7940 with self.assertRaises(ValueError) as e:
7941 self._DoReadFileDtb(
7942 '341_fit_signature.dts',
7943 entry_args=entry_args,
7944 extra_indirs=[test_subdir, keys_subdir1, keys_subdir2])[0]
7945 self.assertIn(
7946 'Node \'/binman/fit\': multiple key paths found',
7947 str(e.exception))
7948
7949 def testFitSignNoSingatureNodes(self):
7950 """Test that fit,sign doens't raise error if no signature nodes found"""
7951 if not elf.ELF_TOOLS:
7952 self.skipTest('Python elftools not available')
7953 entry_args = {
7954 'of-list': 'test-fdt1',
7955 'default-dt': 'test-fdt1',
7956 'atf-bl31-path': 'bl31.elf',
7957 }
7958 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
7959 self._DoReadFileDtb(
7960 '342_fit_signature.dts',
7961 entry_args=entry_args,
7962 extra_indirs=[test_subdir])[0]
7963
Simon Glassa360b8f2024-06-23 11:55:06 -06007964
Paul HENRYSff318462024-11-25 18:47:17 +01007965 def testSimpleFitEncryptedData(self):
7966 """Test an image with a FIT containing data to be encrypted"""
7967 data = tools.read_file(self.TestFile("aes256.bin"))
7968 self._MakeInputFile("keys/aes256.bin", data)
7969
7970 keys_subdir = os.path.join(self._indir, "keys")
7971 data = self._DoReadFileDtb(
7972 '343_fit_encrypt_data.dts',
7973 extra_indirs=[keys_subdir])[0]
7974
7975 fit = fdt.Fdt.FromData(data)
7976 fit.Scan()
7977
7978 # Extract the encrypted data and the Initialization Vector from the FIT
7979 node = fit.GetNode('/images/u-boot')
7980 subnode = fit.GetNode('/images/u-boot/cipher')
7981 data_size_unciphered = int.from_bytes(fit.GetProps(node)['data-size-unciphered'].bytes,
7982 byteorder='big')
7983 self.assertEqual(data_size_unciphered, len(U_BOOT_NODTB_DATA))
7984
7985 # Retrieve the key name from the FIT removing any null byte
7986 key_name = fit.GetProps(subnode)['key-name-hint'].bytes.replace(b'\x00', b'')
7987 with open(self.TestFile(key_name.decode('ascii') + '.bin'), 'rb') as file:
7988 key = file.read()
7989 iv = fit.GetProps(subnode)['iv'].bytes.hex()
7990 enc_data = fit.GetProps(node)['data'].bytes
7991 outdir = tools.get_output_dir()
7992 enc_data_file = os.path.join(outdir, 'encrypted_data.bin')
7993 tools.write_file(enc_data_file, enc_data)
7994 data_file = os.path.join(outdir, 'data.bin')
7995
7996 # Decrypt the encrypted data from the FIT and compare the data
7997 tools.run('openssl', 'enc', '-aes-256-cbc', '-nosalt', '-d', '-in',
7998 enc_data_file, '-out', data_file, '-K', key.hex(), '-iv', iv)
7999 with open(data_file, 'r') as file:
8000 dec_data = file.read()
8001 self.assertEqual(U_BOOT_NODTB_DATA, dec_data.encode('ascii'))
8002
8003 def testSimpleFitEncryptedDataMissingKey(self):
8004 """Test an image with a FIT containing data to be encrypted but with a missing key"""
8005 with self.assertRaises(ValueError) as e:
8006 self._DoReadFile('344_fit_encrypt_data_no_key.dts')
8007
8008 self.assertIn("Filename 'aes256.bin' not found in input path", str(e.exception))
8009
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01008010 def testFitFdtName(self):
8011 """Test an image with an FIT with multiple FDT images using NAME"""
8012 self.CheckFitFdt('345_fit_fdt_name.dts', use_seq_num=False)
8013
Neha Malcom Francisa25b4832025-03-17 10:24:20 +05308014 def testRemoveTemplate(self):
8015 """Test whether template is removed"""
8016 TestFunctional._MakeInputFile('my-blob.bin', b'blob')
8017 TestFunctional._MakeInputFile('my-blob2.bin', b'other')
8018 self._DoTestFile('346_remove_template.dts',
8019 force_missing_bintools='openssl',)
8020
Simon Glassac599912017-11-12 21:52:22 -07008021if __name__ == "__main__":
8022 unittest.main()