blob: 1e570aba2875e985aecd582e4acbbb9f7a807205 [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')))
Yannic Moog911ba9a2025-06-13 14:02:42 +0200254 TestFunctional.tee_elf_path = TestFunctional._MakeInputFile('tee.elf',
Simon Glass5f423422022-03-05 20:19:12 -0700255 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,
Yannic Moog6f2fd072025-06-13 14:02:44 +0200518 verbosity=None, allow_fake_blobs=True, map=False,
519 update_dtb=False, entry_args=None, reset_dtbs=True,
520 extra_indirs=None, 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)
Yannic Moog6f2fd072025-06-13 14:02:44 +0200538 allow_fake_blobs: whether binman should fake missing ext blobs
Simon Glass30732662018-06-01 09:38:20 -0600539 map: True to output map files for the images
Simon Glasse8561af2018-08-01 15:22:37 -0600540 update_dtb: Update the offset and size of each entry in the device
Simon Glassa87014e2018-07-06 10:27:42 -0600541 tree before packing it into the image
Simon Glass31ee50f2020-09-01 05:13:55 -0600542 entry_args: Dict of entry args to supply to binman
543 key: arg name
544 value: value of that arg
545 reset_dtbs: With use_real_dtb the test dtb is overwritten by this
546 function. If reset_dtbs is True, then the original test dtb
547 is written back before this function finishes
Simon Glassa435cd12020-09-01 05:13:59 -0600548 extra_indirs: Extra input directories to add using -I
Simon Glass76f496d2021-07-06 10:36:37 -0600549 threads: Number of threads to use (None for default, 0 for
550 single-threaded)
Simon Glass72232452016-11-25 20:15:53 -0700551
552 Returns:
553 Tuple:
554 Resulting image contents
555 Device tree contents
Simon Glass30732662018-06-01 09:38:20 -0600556 Map data showing contents of image (or None if none)
Simon Glassdef77b52018-07-17 13:25:27 -0600557 Output device tree binary filename ('u-boot.dtb' path)
Simon Glass57454f42016-11-25 20:15:52 -0700558 """
Simon Glass72232452016-11-25 20:15:53 -0700559 dtb_data = None
Simon Glass57454f42016-11-25 20:15:52 -0700560 # Use the compiled test file as the u-boot-dtb input
561 if use_real_dtb:
Simon Glass72232452016-11-25 20:15:53 -0700562 dtb_data = self._SetupDtb(fname)
Simon Glasse219aa42018-09-14 04:57:24 -0600563
564 # For testing purposes, make a copy of the DT for SPL and TPL. Add
Simon Glassd9e01d22024-07-20 11:49:40 +0100565 # a node indicating which it is, to aid verification.
Simon Glass56d05412022-02-28 07:16:54 -0700566 for name in ['spl', 'tpl', 'vpl']:
Simon Glasse219aa42018-09-14 04:57:24 -0600567 dtb_fname = '%s/u-boot-%s.dtb' % (name, name)
568 outfile = os.path.join(self._indir, dtb_fname)
569 TestFunctional._MakeInputFile(dtb_fname,
Simon Glass56d05412022-02-28 07:16:54 -0700570 self._GetDtbContentsForSpls(dtb_data, name))
Simon Glass57454f42016-11-25 20:15:52 -0700571
572 try:
Simon Glass91710b32018-07-17 13:25:32 -0600573 retcode = self._DoTestFile(fname, map=map, update_dtb=update_dtb,
Simon Glassa435cd12020-09-01 05:13:59 -0600574 entry_args=entry_args, use_real_dtb=use_real_dtb,
Simon Glass3eb30a42024-08-26 13:11:42 -0600575 use_expanded=use_expanded, verbosity=verbosity,
Yannic Moog6f2fd072025-06-13 14:02:44 +0200576 allow_fake_blobs=allow_fake_blobs, extra_indirs=extra_indirs,
Simon Glass76f496d2021-07-06 10:36:37 -0600577 threads=threads)
Simon Glass57454f42016-11-25 20:15:52 -0700578 self.assertEqual(0, retcode)
Simon Glass80025522022-01-29 14:14:04 -0700579 out_dtb_fname = tools.get_output_filename('u-boot.dtb.out')
Simon Glass57454f42016-11-25 20:15:52 -0700580
581 # Find the (only) image, read it and return its contents
582 image = control.images['image']
Simon Glass80025522022-01-29 14:14:04 -0700583 image_fname = tools.get_output_filename('image.bin')
Simon Glassa87014e2018-07-06 10:27:42 -0600584 self.assertTrue(os.path.exists(image_fname))
Simon Glass30732662018-06-01 09:38:20 -0600585 if map:
Simon Glass80025522022-01-29 14:14:04 -0700586 map_fname = tools.get_output_filename('image.map')
Simon Glass30732662018-06-01 09:38:20 -0600587 with open(map_fname) as fd:
588 map_data = fd.read()
589 else:
590 map_data = None
Simon Glass33486662019-05-14 15:53:42 -0600591 with open(image_fname, 'rb') as fd:
Simon Glassa87014e2018-07-06 10:27:42 -0600592 return fd.read(), dtb_data, map_data, out_dtb_fname
Simon Glass57454f42016-11-25 20:15:52 -0700593 finally:
594 # Put the test file back
Simon Glasse219aa42018-09-14 04:57:24 -0600595 if reset_dtbs and use_real_dtb:
Simon Glass8425a1f2018-07-17 13:25:48 -0600596 self._ResetDtbs()
Simon Glass57454f42016-11-25 20:15:52 -0700597
Simon Glass5b4bce32019-07-08 14:25:26 -0600598 def _DoReadFileRealDtb(self, fname):
599 """Run binman with a real .dtb file and return the resulting data
600
601 Args:
602 fname: DT source filename to use (e.g. 082_fdt_update_all.dts)
603
604 Returns:
605 Resulting image contents
606 """
607 return self._DoReadFileDtb(fname, use_real_dtb=True, update_dtb=True)[0]
608
Simon Glass72232452016-11-25 20:15:53 -0700609 def _DoReadFile(self, fname, use_real_dtb=False):
Simon Glass1e324002018-06-01 09:38:19 -0600610 """Helper function which discards the device-tree binary
611
612 Args:
Simon Glass511f6582018-10-01 12:22:30 -0600613 fname: Device-tree source filename to use (e.g. 005_simple.dts)
Simon Glass1e324002018-06-01 09:38:19 -0600614 use_real_dtb: True to use the test file as the contents of
615 the u-boot-dtb entry. Normally this is not needed and the
616 test contents (the U_BOOT_DTB_DATA string) can be used.
617 But in some test we need the real contents.
Simon Glassdef77b52018-07-17 13:25:27 -0600618
619 Returns:
620 Resulting image contents
Simon Glass1e324002018-06-01 09:38:19 -0600621 """
Simon Glass72232452016-11-25 20:15:53 -0700622 return self._DoReadFileDtb(fname, use_real_dtb)[0]
623
Simon Glass57454f42016-11-25 20:15:52 -0700624 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600625 def _MakeInputFile(cls, fname, contents):
Simon Glass57454f42016-11-25 20:15:52 -0700626 """Create a new test input file, creating directories as needed
627
628 Args:
Simon Glasse8561af2018-08-01 15:22:37 -0600629 fname: Filename to create
Simon Glass57454f42016-11-25 20:15:52 -0700630 contents: File contents to write in to the file
631 Returns:
632 Full pathname of file created
633 """
Simon Glass862f8e22019-08-24 07:22:43 -0600634 pathname = os.path.join(cls._indir, fname)
Simon Glass57454f42016-11-25 20:15:52 -0700635 dirname = os.path.dirname(pathname)
636 if dirname and not os.path.exists(dirname):
637 os.makedirs(dirname)
638 with open(pathname, 'wb') as fd:
639 fd.write(contents)
640 return pathname
641
642 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600643 def _MakeInputDir(cls, dirname):
Simon Glassc1ae83c2018-07-17 13:25:44 -0600644 """Create a new test input directory, creating directories as needed
645
646 Args:
647 dirname: Directory name to create
648
649 Returns:
650 Full pathname of directory created
651 """
Simon Glass862f8e22019-08-24 07:22:43 -0600652 pathname = os.path.join(cls._indir, dirname)
Simon Glassc1ae83c2018-07-17 13:25:44 -0600653 if not os.path.exists(pathname):
654 os.makedirs(pathname)
655 return pathname
656
657 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600658 def _SetupSplElf(cls, src_fname='bss_data'):
Simon Glass7057d022018-10-01 21:12:47 -0600659 """Set up an ELF file with a '_dt_ucode_base_size' symbol
660
661 Args:
662 Filename of ELF file to use as SPL
663 """
Simon Glass93a806f2019-08-24 07:22:59 -0600664 TestFunctional._MakeInputFile('spl/u-boot-spl',
Simon Glass80025522022-01-29 14:14:04 -0700665 tools.read_file(cls.ElfTestFile(src_fname)))
Simon Glass7057d022018-10-01 21:12:47 -0600666
667 @classmethod
Simon Glass3eb5b202019-08-24 07:23:00 -0600668 def _SetupTplElf(cls, src_fname='bss_data'):
669 """Set up an ELF file with a '_dt_ucode_base_size' symbol
670
671 Args:
672 Filename of ELF file to use as TPL
673 """
674 TestFunctional._MakeInputFile('tpl/u-boot-tpl',
Simon Glass80025522022-01-29 14:14:04 -0700675 tools.read_file(cls.ElfTestFile(src_fname)))
Simon Glass3eb5b202019-08-24 07:23:00 -0600676
677 @classmethod
Simon Glass56d05412022-02-28 07:16:54 -0700678 def _SetupVplElf(cls, src_fname='bss_data'):
679 """Set up an ELF file with a '_dt_ucode_base_size' symbol
680
681 Args:
682 Filename of ELF file to use as VPL
683 """
684 TestFunctional._MakeInputFile('vpl/u-boot-vpl',
685 tools.read_file(cls.ElfTestFile(src_fname)))
686
687 @classmethod
Lukas Funkee901faf2023-07-18 13:53:13 +0200688 def _SetupPmuFwlElf(cls, src_fname='bss_data'):
689 """Set up an ELF file with a '_dt_ucode_base_size' symbol
690
691 Args:
692 Filename of ELF file to use as VPL
693 """
694 TestFunctional._MakeInputFile('pmu-firmware.elf',
695 tools.read_file(cls.ElfTestFile(src_fname)))
696
697 @classmethod
Simon Glasse88cef92020-07-09 18:39:41 -0600698 def _SetupDescriptor(cls):
699 with open(cls.TestFile('descriptor.bin'), 'rb') as fd:
700 TestFunctional._MakeInputFile('descriptor.bin', fd.read())
701
702 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600703 def TestFile(cls, fname):
704 return os.path.join(cls._binman_dir, 'test', fname)
Simon Glass57454f42016-11-25 20:15:52 -0700705
Simon Glassf6290892019-08-24 07:22:53 -0600706 @classmethod
707 def ElfTestFile(cls, fname):
708 return os.path.join(cls._elf_testdir, fname)
709
Simon Glassad5cfe12023-01-07 14:07:14 -0700710 @classmethod
711 def make_tee_bin(cls, fname, paged_sz=0, extra_data=b''):
712 init_sz, start_hi, start_lo, dummy = (len(U_BOOT_DATA), 0, TEE_ADDR, 0)
713 data = b'OPTE\x01xxx' + struct.pack('<5I', init_sz, start_hi, start_lo,
714 dummy, paged_sz) + U_BOOT_DATA
715 data += extra_data
716 TestFunctional._MakeInputFile(fname, data)
717
Simon Glass57454f42016-11-25 20:15:52 -0700718 def AssertInList(self, grep_list, target):
719 """Assert that at least one of a list of things is in a target
720
721 Args:
722 grep_list: List of strings to check
723 target: Target string
724 """
725 for grep in grep_list:
726 if grep in target:
727 return
Simon Glass848cdb52019-05-17 22:00:50 -0600728 self.fail("Error: '%s' not found in '%s'" % (grep_list, target))
Simon Glass57454f42016-11-25 20:15:52 -0700729
730 def CheckNoGaps(self, entries):
731 """Check that all entries fit together without gaps
732
733 Args:
734 entries: List of entries to check
735 """
Simon Glasse8561af2018-08-01 15:22:37 -0600736 offset = 0
Simon Glass57454f42016-11-25 20:15:52 -0700737 for entry in entries.values():
Simon Glasse8561af2018-08-01 15:22:37 -0600738 self.assertEqual(offset, entry.offset)
739 offset += entry.size
Simon Glass57454f42016-11-25 20:15:52 -0700740
Simon Glass72232452016-11-25 20:15:53 -0700741 def GetFdtLen(self, dtb):
Simon Glass1e324002018-06-01 09:38:19 -0600742 """Get the totalsize field from a device-tree binary
Simon Glass72232452016-11-25 20:15:53 -0700743
744 Args:
Simon Glass1e324002018-06-01 09:38:19 -0600745 dtb: Device-tree binary contents
Simon Glass72232452016-11-25 20:15:53 -0700746
747 Returns:
Simon Glass1e324002018-06-01 09:38:19 -0600748 Total size of device-tree binary, from the header
Simon Glass72232452016-11-25 20:15:53 -0700749 """
750 return struct.unpack('>L', dtb[4:8])[0]
751
Simon Glass0f621332019-07-08 14:25:27 -0600752 def _GetPropTree(self, dtb, prop_names, prefix='/binman/'):
Simon Glassa87014e2018-07-06 10:27:42 -0600753 def AddNode(node, path):
754 if node.name != '/':
755 path += '/' + node.name
Simon Glass0f621332019-07-08 14:25:27 -0600756 for prop in node.props.values():
757 if prop.name in prop_names:
758 prop_path = path + ':' + prop.name
759 tree[prop_path[len(prefix):]] = fdt_util.fdt32_to_cpu(
760 prop.value)
Simon Glassa87014e2018-07-06 10:27:42 -0600761 for subnode in node.subnodes:
Simon Glassa87014e2018-07-06 10:27:42 -0600762 AddNode(subnode, path)
763
764 tree = {}
Simon Glassa87014e2018-07-06 10:27:42 -0600765 AddNode(dtb.GetRoot(), '')
766 return tree
767
Ivan Mikhaylov3cfcaa4d2023-03-08 01:13:40 +0000768 def _CheckSign(self, fit, key):
769 try:
770 tools.run('fit_check_sign', '-k', key, '-f', fit)
771 except:
772 self.fail('Expected signed FIT container')
773 return False
774 return True
775
Paul HENRYS5cf82892025-02-24 22:20:55 +0100776 def _CheckPreload(self, image, key, algo="sha256,rsa2048",
777 padding="pkcs-1.5"):
778 try:
779 tools.run('preload_check_sign', '-k', key, '-a', algo, '-p',
780 padding, '-f', image)
781 except:
782 self.fail('Expected image signed with a pre-load')
783 return False
784 return True
785
Simon Glass57454f42016-11-25 20:15:52 -0700786 def testRun(self):
787 """Test a basic run with valid args"""
788 result = self._RunBinman('-h')
789
790 def testFullHelp(self):
791 """Test that the full help is displayed with -H"""
792 result = self._RunBinman('-H')
Simon Glass75ead662021-03-18 20:25:13 +1300793 help_file = os.path.join(self._binman_dir, 'README.rst')
Tom Rinic3c0b6d2018-01-16 15:29:50 -0500794 # Remove possible extraneous strings
795 extra = '::::::::::::::\n' + help_file + '\n::::::::::::::\n'
796 gothelp = result.stdout.replace(extra, '')
797 self.assertEqual(len(gothelp), os.path.getsize(help_file))
Simon Glass57454f42016-11-25 20:15:52 -0700798 self.assertEqual(0, len(result.stderr))
799 self.assertEqual(0, result.return_code)
800
801 def testFullHelpInternal(self):
802 """Test that the full help is displayed with -H"""
803 try:
Simon Glass5dc22cf2025-02-03 09:26:42 -0700804 command.TEST_RESULT = command.CommandResult()
Simon Glass57454f42016-11-25 20:15:52 -0700805 result = self._DoBinman('-H')
Simon Glass75ead662021-03-18 20:25:13 +1300806 help_file = os.path.join(self._binman_dir, 'README.rst')
Simon Glass57454f42016-11-25 20:15:52 -0700807 finally:
Simon Glass5dc22cf2025-02-03 09:26:42 -0700808 command.TEST_RESULT = None
Simon Glass57454f42016-11-25 20:15:52 -0700809
810 def testHelp(self):
811 """Test that the basic help is displayed with -h"""
812 result = self._RunBinman('-h')
813 self.assertTrue(len(result.stdout) > 200)
814 self.assertEqual(0, len(result.stderr))
815 self.assertEqual(0, result.return_code)
816
Simon Glass57454f42016-11-25 20:15:52 -0700817 def testBoard(self):
818 """Test that we can run it with a specific board"""
Simon Glass511f6582018-10-01 12:22:30 -0600819 self._SetupDtb('005_simple.dts', 'sandbox/u-boot.dtb')
Simon Glass57454f42016-11-25 20:15:52 -0700820 TestFunctional._MakeInputFile('sandbox/u-boot.bin', U_BOOT_DATA)
Simon Glassed930672021-03-18 20:25:05 +1300821 result = self._DoBinman('build', '-n', '-b', 'sandbox')
Simon Glass57454f42016-11-25 20:15:52 -0700822 self.assertEqual(0, result)
823
824 def testNeedBoard(self):
825 """Test that we get an error when no board ius supplied"""
826 with self.assertRaises(ValueError) as e:
Simon Glassf46732a2019-07-08 14:25:29 -0600827 result = self._DoBinman('build')
Simon Glass57454f42016-11-25 20:15:52 -0700828 self.assertIn("Must provide a board to process (use -b <board>)",
829 str(e.exception))
830
831 def testMissingDt(self):
Simon Glass1e324002018-06-01 09:38:19 -0600832 """Test that an invalid device-tree file generates an error"""
Simon Glass57454f42016-11-25 20:15:52 -0700833 with self.assertRaises(Exception) as e:
Simon Glassf46732a2019-07-08 14:25:29 -0600834 self._RunBinman('build', '-d', 'missing_file')
Simon Glass57454f42016-11-25 20:15:52 -0700835 # We get one error from libfdt, and a different one from fdtget.
836 self.AssertInList(["Couldn't open blob from 'missing_file'",
837 'No such file or directory'], str(e.exception))
838
839 def testBrokenDt(self):
Simon Glass1e324002018-06-01 09:38:19 -0600840 """Test that an invalid device-tree source file generates an error
Simon Glass57454f42016-11-25 20:15:52 -0700841
842 Since this is a source file it should be compiled and the error
843 will come from the device-tree compiler (dtc).
844 """
845 with self.assertRaises(Exception) as e:
Simon Glassf46732a2019-07-08 14:25:29 -0600846 self._RunBinman('build', '-d', self.TestFile('001_invalid.dts'))
Simon Glass57454f42016-11-25 20:15:52 -0700847 self.assertIn("FATAL ERROR: Unable to parse input tree",
848 str(e.exception))
849
850 def testMissingNode(self):
851 """Test that a device tree without a 'binman' node generates an error"""
852 with self.assertRaises(Exception) as e:
Simon Glassf46732a2019-07-08 14:25:29 -0600853 self._DoBinman('build', '-d', self.TestFile('002_missing_node.dts'))
Simon Glass57454f42016-11-25 20:15:52 -0700854 self.assertIn("does not have a 'binman' node", str(e.exception))
855
856 def testEmpty(self):
857 """Test that an empty binman node works OK (i.e. does nothing)"""
Simon Glassf46732a2019-07-08 14:25:29 -0600858 result = self._RunBinman('build', '-d', self.TestFile('003_empty.dts'))
Simon Glass57454f42016-11-25 20:15:52 -0700859 self.assertEqual(0, len(result.stderr))
860 self.assertEqual(0, result.return_code)
861
862 def testInvalidEntry(self):
863 """Test that an invalid entry is flagged"""
864 with self.assertRaises(Exception) as e:
Simon Glassf46732a2019-07-08 14:25:29 -0600865 result = self._RunBinman('build', '-d',
Simon Glass511f6582018-10-01 12:22:30 -0600866 self.TestFile('004_invalid_entry.dts'))
Simon Glass57454f42016-11-25 20:15:52 -0700867 self.assertIn("Unknown entry type 'not-a-valid-type' in node "
868 "'/binman/not-a-valid-type'", str(e.exception))
869
870 def testSimple(self):
871 """Test a simple binman with a single file"""
Simon Glass511f6582018-10-01 12:22:30 -0600872 data = self._DoReadFile('005_simple.dts')
Simon Glass57454f42016-11-25 20:15:52 -0700873 self.assertEqual(U_BOOT_DATA, data)
874
Simon Glass075a45c2017-11-13 18:55:00 -0700875 def testSimpleDebug(self):
876 """Test a simple binman run with debugging enabled"""
Simon Glass52d06212019-07-08 14:25:53 -0600877 self._DoTestFile('005_simple.dts', debug=True)
Simon Glass075a45c2017-11-13 18:55:00 -0700878
Simon Glass57454f42016-11-25 20:15:52 -0700879 def testDual(self):
880 """Test that we can handle creating two images
881
882 This also tests image padding.
883 """
Simon Glass511f6582018-10-01 12:22:30 -0600884 retcode = self._DoTestFile('006_dual_image.dts')
Simon Glass57454f42016-11-25 20:15:52 -0700885 self.assertEqual(0, retcode)
886
887 image = control.images['image1']
Simon Glass39dd2152019-07-08 14:25:47 -0600888 self.assertEqual(len(U_BOOT_DATA), image.size)
Simon Glass80025522022-01-29 14:14:04 -0700889 fname = tools.get_output_filename('image1.bin')
Simon Glass57454f42016-11-25 20:15:52 -0700890 self.assertTrue(os.path.exists(fname))
Simon Glass33486662019-05-14 15:53:42 -0600891 with open(fname, 'rb') as fd:
Simon Glass57454f42016-11-25 20:15:52 -0700892 data = fd.read()
893 self.assertEqual(U_BOOT_DATA, data)
894
895 image = control.images['image2']
Simon Glass39dd2152019-07-08 14:25:47 -0600896 self.assertEqual(3 + len(U_BOOT_DATA) + 5, image.size)
Simon Glass80025522022-01-29 14:14:04 -0700897 fname = tools.get_output_filename('image2.bin')
Simon Glass57454f42016-11-25 20:15:52 -0700898 self.assertTrue(os.path.exists(fname))
Simon Glass33486662019-05-14 15:53:42 -0600899 with open(fname, 'rb') as fd:
Simon Glass57454f42016-11-25 20:15:52 -0700900 data = fd.read()
901 self.assertEqual(U_BOOT_DATA, data[3:7])
Simon Glass80025522022-01-29 14:14:04 -0700902 self.assertEqual(tools.get_bytes(0, 3), data[:3])
903 self.assertEqual(tools.get_bytes(0, 5), data[7:])
Simon Glass57454f42016-11-25 20:15:52 -0700904
905 def testBadAlign(self):
906 """Test that an invalid alignment value is detected"""
907 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -0600908 self._DoTestFile('007_bad_align.dts')
Simon Glass57454f42016-11-25 20:15:52 -0700909 self.assertIn("Node '/binman/u-boot': Alignment 23 must be a power "
910 "of two", str(e.exception))
911
912 def testPackSimple(self):
913 """Test that packing works as expected"""
Simon Glass511f6582018-10-01 12:22:30 -0600914 retcode = self._DoTestFile('008_pack.dts')
Simon Glass57454f42016-11-25 20:15:52 -0700915 self.assertEqual(0, retcode)
916 self.assertIn('image', control.images)
917 image = control.images['image']
Simon Glasseca32212018-06-01 09:38:12 -0600918 entries = image.GetEntries()
Simon Glass57454f42016-11-25 20:15:52 -0700919 self.assertEqual(5, len(entries))
920
921 # First u-boot
922 self.assertIn('u-boot', entries)
923 entry = entries['u-boot']
Simon Glasse8561af2018-08-01 15:22:37 -0600924 self.assertEqual(0, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700925 self.assertEqual(len(U_BOOT_DATA), entry.size)
926
927 # Second u-boot, aligned to 16-byte boundary
928 self.assertIn('u-boot-align', entries)
929 entry = entries['u-boot-align']
Simon Glasse8561af2018-08-01 15:22:37 -0600930 self.assertEqual(16, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700931 self.assertEqual(len(U_BOOT_DATA), entry.size)
932
933 # Third u-boot, size 23 bytes
934 self.assertIn('u-boot-size', entries)
935 entry = entries['u-boot-size']
Simon Glasse8561af2018-08-01 15:22:37 -0600936 self.assertEqual(20, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700937 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
938 self.assertEqual(23, entry.size)
939
940 # Fourth u-boot, placed immediate after the above
941 self.assertIn('u-boot-next', entries)
942 entry = entries['u-boot-next']
Simon Glasse8561af2018-08-01 15:22:37 -0600943 self.assertEqual(43, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700944 self.assertEqual(len(U_BOOT_DATA), entry.size)
945
Simon Glasse8561af2018-08-01 15:22:37 -0600946 # Fifth u-boot, placed at a fixed offset
Simon Glass57454f42016-11-25 20:15:52 -0700947 self.assertIn('u-boot-fixed', entries)
948 entry = entries['u-boot-fixed']
Simon Glasse8561af2018-08-01 15:22:37 -0600949 self.assertEqual(61, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700950 self.assertEqual(len(U_BOOT_DATA), entry.size)
951
Simon Glass39dd2152019-07-08 14:25:47 -0600952 self.assertEqual(65, image.size)
Simon Glass57454f42016-11-25 20:15:52 -0700953
954 def testPackExtra(self):
955 """Test that extra packing feature works as expected"""
Simon Glassafb9caa2020-10-26 17:40:10 -0600956 data, _, _, out_dtb_fname = self._DoReadFileDtb('009_pack_extra.dts',
957 update_dtb=True)
Simon Glass57454f42016-11-25 20:15:52 -0700958
Simon Glass57454f42016-11-25 20:15:52 -0700959 self.assertIn('image', control.images)
960 image = control.images['image']
Simon Glasseca32212018-06-01 09:38:12 -0600961 entries = image.GetEntries()
Samuel Hollande2574022023-01-21 17:25:16 -0600962 self.assertEqual(6, len(entries))
Simon Glass57454f42016-11-25 20:15:52 -0700963
Samuel Hollande2574022023-01-21 17:25:16 -0600964 # First u-boot with padding before and after (included in minimum size)
Simon Glass57454f42016-11-25 20:15:52 -0700965 self.assertIn('u-boot', entries)
966 entry = entries['u-boot']
Simon Glasse8561af2018-08-01 15:22:37 -0600967 self.assertEqual(0, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700968 self.assertEqual(3, entry.pad_before)
969 self.assertEqual(3 + 5 + len(U_BOOT_DATA), entry.size)
Simon Glass187202f2020-10-26 17:40:08 -0600970 self.assertEqual(U_BOOT_DATA, entry.data)
Simon Glass80025522022-01-29 14:14:04 -0700971 self.assertEqual(tools.get_bytes(0, 3) + U_BOOT_DATA +
972 tools.get_bytes(0, 5), data[:entry.size])
Simon Glass187202f2020-10-26 17:40:08 -0600973 pos = entry.size
Simon Glass57454f42016-11-25 20:15:52 -0700974
975 # Second u-boot has an aligned size, but it has no effect
976 self.assertIn('u-boot-align-size-nop', entries)
977 entry = entries['u-boot-align-size-nop']
Simon Glass187202f2020-10-26 17:40:08 -0600978 self.assertEqual(pos, entry.offset)
979 self.assertEqual(len(U_BOOT_DATA), entry.size)
980 self.assertEqual(U_BOOT_DATA, entry.data)
981 self.assertEqual(U_BOOT_DATA, data[pos:pos + entry.size])
982 pos += entry.size
Simon Glass57454f42016-11-25 20:15:52 -0700983
984 # Third u-boot has an aligned size too
985 self.assertIn('u-boot-align-size', entries)
986 entry = entries['u-boot-align-size']
Simon Glass187202f2020-10-26 17:40:08 -0600987 self.assertEqual(pos, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700988 self.assertEqual(32, entry.size)
Simon Glass187202f2020-10-26 17:40:08 -0600989 self.assertEqual(U_BOOT_DATA, entry.data)
Simon Glass80025522022-01-29 14:14:04 -0700990 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 32 - len(U_BOOT_DATA)),
Simon Glass187202f2020-10-26 17:40:08 -0600991 data[pos:pos + entry.size])
992 pos += entry.size
Simon Glass57454f42016-11-25 20:15:52 -0700993
994 # Fourth u-boot has an aligned end
995 self.assertIn('u-boot-align-end', entries)
996 entry = entries['u-boot-align-end']
Simon Glasse8561af2018-08-01 15:22:37 -0600997 self.assertEqual(48, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700998 self.assertEqual(16, entry.size)
Simon Glass187202f2020-10-26 17:40:08 -0600999 self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)])
Simon Glass80025522022-01-29 14:14:04 -07001000 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 16 - len(U_BOOT_DATA)),
Simon Glass187202f2020-10-26 17:40:08 -06001001 data[pos:pos + entry.size])
1002 pos += entry.size
Simon Glass57454f42016-11-25 20:15:52 -07001003
1004 # Fifth u-boot immediately afterwards
1005 self.assertIn('u-boot-align-both', entries)
1006 entry = entries['u-boot-align-both']
Simon Glasse8561af2018-08-01 15:22:37 -06001007 self.assertEqual(64, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -07001008 self.assertEqual(64, entry.size)
Simon Glass187202f2020-10-26 17:40:08 -06001009 self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)])
Simon Glass80025522022-01-29 14:14:04 -07001010 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 64 - len(U_BOOT_DATA)),
Simon Glass187202f2020-10-26 17:40:08 -06001011 data[pos:pos + entry.size])
Simon Glass57454f42016-11-25 20:15:52 -07001012
Samuel Hollande2574022023-01-21 17:25:16 -06001013 # Sixth u-boot with both minimum size and aligned size
1014 self.assertIn('u-boot-min-size', entries)
1015 entry = entries['u-boot-min-size']
1016 self.assertEqual(128, entry.offset)
1017 self.assertEqual(32, entry.size)
1018 self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)])
1019 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 32 - len(U_BOOT_DATA)),
1020 data[pos:pos + entry.size])
1021
Simon Glass57454f42016-11-25 20:15:52 -07001022 self.CheckNoGaps(entries)
Samuel Hollande2574022023-01-21 17:25:16 -06001023 self.assertEqual(160, image.size)
Simon Glass57454f42016-11-25 20:15:52 -07001024
Simon Glassafb9caa2020-10-26 17:40:10 -06001025 dtb = fdt.Fdt(out_dtb_fname)
1026 dtb.Scan()
1027 props = self._GetPropTree(dtb, ['size', 'offset', 'image-pos'])
1028 expected = {
1029 'image-pos': 0,
1030 'offset': 0,
Samuel Hollande2574022023-01-21 17:25:16 -06001031 'size': 160,
Simon Glassafb9caa2020-10-26 17:40:10 -06001032
1033 'u-boot:image-pos': 0,
1034 'u-boot:offset': 0,
1035 'u-boot:size': 3 + 5 + len(U_BOOT_DATA),
1036
1037 'u-boot-align-size-nop:image-pos': 12,
1038 'u-boot-align-size-nop:offset': 12,
1039 'u-boot-align-size-nop:size': 4,
1040
1041 'u-boot-align-size:image-pos': 16,
1042 'u-boot-align-size:offset': 16,
1043 'u-boot-align-size:size': 32,
1044
1045 'u-boot-align-end:image-pos': 48,
1046 'u-boot-align-end:offset': 48,
1047 'u-boot-align-end:size': 16,
1048
1049 'u-boot-align-both:image-pos': 64,
1050 'u-boot-align-both:offset': 64,
1051 'u-boot-align-both:size': 64,
Samuel Hollande2574022023-01-21 17:25:16 -06001052
1053 'u-boot-min-size:image-pos': 128,
1054 'u-boot-min-size:offset': 128,
1055 'u-boot-min-size:size': 32,
Simon Glassafb9caa2020-10-26 17:40:10 -06001056 }
1057 self.assertEqual(expected, props)
1058
Simon Glass57454f42016-11-25 20:15:52 -07001059 def testPackAlignPowerOf2(self):
1060 """Test that invalid entry alignment is detected"""
1061 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001062 self._DoTestFile('010_pack_align_power2.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001063 self.assertIn("Node '/binman/u-boot': Alignment 5 must be a power "
1064 "of two", str(e.exception))
1065
1066 def testPackAlignSizePowerOf2(self):
1067 """Test that invalid entry size alignment is detected"""
1068 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001069 self._DoTestFile('011_pack_align_size_power2.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001070 self.assertIn("Node '/binman/u-boot': Alignment size 55 must be a "
1071 "power of two", str(e.exception))
1072
1073 def testPackInvalidAlign(self):
Simon Glasse8561af2018-08-01 15:22:37 -06001074 """Test detection of an offset that does not match its alignment"""
Simon Glass57454f42016-11-25 20:15:52 -07001075 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001076 self._DoTestFile('012_pack_inv_align.dts')
Simon Glasse8561af2018-08-01 15:22:37 -06001077 self.assertIn("Node '/binman/u-boot': Offset 0x5 (5) does not match "
Simon Glass57454f42016-11-25 20:15:52 -07001078 "align 0x4 (4)", str(e.exception))
1079
1080 def testPackInvalidSizeAlign(self):
1081 """Test that invalid entry size alignment is detected"""
1082 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001083 self._DoTestFile('013_pack_inv_size_align.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001084 self.assertIn("Node '/binman/u-boot': Size 0x5 (5) does not match "
1085 "align-size 0x4 (4)", str(e.exception))
1086
1087 def testPackOverlap(self):
1088 """Test that overlapping regions are detected"""
1089 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001090 self._DoTestFile('014_pack_overlap.dts')
Simon Glasse8561af2018-08-01 15:22:37 -06001091 self.assertIn("Node '/binman/u-boot-align': Offset 0x3 (3) overlaps "
Simon Glass57454f42016-11-25 20:15:52 -07001092 "with previous entry '/binman/u-boot' ending at 0x4 (4)",
1093 str(e.exception))
1094
1095 def testPackEntryOverflow(self):
1096 """Test that entries that overflow their size are detected"""
1097 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001098 self._DoTestFile('015_pack_overflow.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001099 self.assertIn("Node '/binman/u-boot': Entry contents size is 0x4 (4) "
1100 "but entry size is 0x3 (3)", str(e.exception))
1101
1102 def testPackImageOverflow(self):
1103 """Test that entries which overflow the image size are detected"""
1104 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001105 self._DoTestFile('016_pack_image_overflow.dts')
Simon Glasseca32212018-06-01 09:38:12 -06001106 self.assertIn("Section '/binman': contents size 0x4 (4) exceeds section "
Simon Glass57454f42016-11-25 20:15:52 -07001107 "size 0x3 (3)", str(e.exception))
1108
1109 def testPackImageSize(self):
1110 """Test that the image size can be set"""
Simon Glass511f6582018-10-01 12:22:30 -06001111 retcode = self._DoTestFile('017_pack_image_size.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001112 self.assertEqual(0, retcode)
1113 self.assertIn('image', control.images)
1114 image = control.images['image']
Simon Glass39dd2152019-07-08 14:25:47 -06001115 self.assertEqual(7, image.size)
Simon Glass57454f42016-11-25 20:15:52 -07001116
1117 def testPackImageSizeAlign(self):
1118 """Test that image size alignemnt works as expected"""
Simon Glass511f6582018-10-01 12:22:30 -06001119 retcode = self._DoTestFile('018_pack_image_align.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001120 self.assertEqual(0, retcode)
1121 self.assertIn('image', control.images)
1122 image = control.images['image']
Simon Glass39dd2152019-07-08 14:25:47 -06001123 self.assertEqual(16, image.size)
Simon Glass57454f42016-11-25 20:15:52 -07001124
1125 def testPackInvalidImageAlign(self):
1126 """Test that invalid image alignment is detected"""
1127 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001128 self._DoTestFile('019_pack_inv_image_align.dts')
Simon Glasseca32212018-06-01 09:38:12 -06001129 self.assertIn("Section '/binman': Size 0x7 (7) does not match "
Simon Glass57454f42016-11-25 20:15:52 -07001130 "align-size 0x8 (8)", str(e.exception))
1131
Simon Glass2a0fa982022-02-11 13:23:21 -07001132 def testPackAlignPowerOf2Inv(self):
Simon Glass57454f42016-11-25 20:15:52 -07001133 """Test that invalid image alignment is detected"""
1134 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001135 self._DoTestFile('020_pack_inv_image_align_power2.dts')
Simon Glass39dd2152019-07-08 14:25:47 -06001136 self.assertIn("Image '/binman': Alignment size 131 must be a power of "
Simon Glass57454f42016-11-25 20:15:52 -07001137 "two", str(e.exception))
1138
1139 def testImagePadByte(self):
1140 """Test that the image pad byte can be specified"""
Simon Glass7057d022018-10-01 21:12:47 -06001141 self._SetupSplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001142 data = self._DoReadFile('021_image_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07001143 self.assertEqual(U_BOOT_SPL_DATA + tools.get_bytes(0xff, 1) +
Simon Glassac0d4952019-05-14 15:53:47 -06001144 U_BOOT_DATA, data)
Simon Glass57454f42016-11-25 20:15:52 -07001145
1146 def testImageName(self):
1147 """Test that image files can be named"""
Simon Glass511f6582018-10-01 12:22:30 -06001148 retcode = self._DoTestFile('022_image_name.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001149 self.assertEqual(0, retcode)
1150 image = control.images['image1']
Simon Glass80025522022-01-29 14:14:04 -07001151 fname = tools.get_output_filename('test-name')
Simon Glass57454f42016-11-25 20:15:52 -07001152 self.assertTrue(os.path.exists(fname))
1153
1154 image = control.images['image2']
Simon Glass80025522022-01-29 14:14:04 -07001155 fname = tools.get_output_filename('test-name.xx')
Simon Glass57454f42016-11-25 20:15:52 -07001156 self.assertTrue(os.path.exists(fname))
1157
1158 def testBlobFilename(self):
1159 """Test that generic blobs can be provided by filename"""
Simon Glass511f6582018-10-01 12:22:30 -06001160 data = self._DoReadFile('023_blob.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001161 self.assertEqual(BLOB_DATA, data)
1162
1163 def testPackSorted(self):
1164 """Test that entries can be sorted"""
Simon Glass7057d022018-10-01 21:12:47 -06001165 self._SetupSplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001166 data = self._DoReadFile('024_sorted.dts')
Simon Glass80025522022-01-29 14:14:04 -07001167 self.assertEqual(tools.get_bytes(0, 1) + U_BOOT_SPL_DATA +
1168 tools.get_bytes(0, 2) + U_BOOT_DATA, data)
Simon Glass57454f42016-11-25 20:15:52 -07001169
Simon Glasse8561af2018-08-01 15:22:37 -06001170 def testPackZeroOffset(self):
1171 """Test that an entry at offset 0 is not given a new offset"""
Marek Vasutf7413f02023-07-18 07:23:58 -06001172 self._SetupSplElf()
Simon Glass57454f42016-11-25 20:15:52 -07001173 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001174 self._DoTestFile('025_pack_zero_size.dts')
Simon Glasse8561af2018-08-01 15:22:37 -06001175 self.assertIn("Node '/binman/u-boot-spl': Offset 0x0 (0) overlaps "
Simon Glass57454f42016-11-25 20:15:52 -07001176 "with previous entry '/binman/u-boot' ending at 0x4 (4)",
1177 str(e.exception))
1178
1179 def testPackUbootDtb(self):
1180 """Test that a device tree can be added to U-Boot"""
Simon Glass511f6582018-10-01 12:22:30 -06001181 data = self._DoReadFile('026_pack_u_boot_dtb.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001182 self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA, data)
Simon Glass72232452016-11-25 20:15:53 -07001183
1184 def testPackX86RomNoSize(self):
1185 """Test that the end-at-4gb property requires a size property"""
Marek Vasutf7413f02023-07-18 07:23:58 -06001186 self._SetupSplElf()
Simon Glass72232452016-11-25 20:15:53 -07001187 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001188 self._DoTestFile('027_pack_4gb_no_size.dts')
Simon Glass39dd2152019-07-08 14:25:47 -06001189 self.assertIn("Image '/binman': Section size must be provided when "
Simon Glass72232452016-11-25 20:15:53 -07001190 "using end-at-4gb", str(e.exception))
1191
Jagdish Gediya0fb978c2018-09-03 21:35:07 +05301192 def test4gbAndSkipAtStartTogether(self):
1193 """Test that the end-at-4gb and skip-at-size property can't be used
1194 together"""
Marek Vasutf7413f02023-07-18 07:23:58 -06001195 self._SetupSplElf()
Jagdish Gediya0fb978c2018-09-03 21:35:07 +05301196 with self.assertRaises(ValueError) as e:
Simon Glass11f2bd02019-08-24 07:23:02 -06001197 self._DoTestFile('098_4gb_and_skip_at_start_together.dts')
Simon Glass39dd2152019-07-08 14:25:47 -06001198 self.assertIn("Image '/binman': Provide either 'end-at-4gb' or "
Jagdish Gediya0fb978c2018-09-03 21:35:07 +05301199 "'skip-at-start'", str(e.exception))
1200
Simon Glass72232452016-11-25 20:15:53 -07001201 def testPackX86RomOutside(self):
Simon Glasse8561af2018-08-01 15:22:37 -06001202 """Test that the end-at-4gb property checks for offset boundaries"""
Marek Vasutf7413f02023-07-18 07:23:58 -06001203 self._SetupSplElf()
Simon Glass72232452016-11-25 20:15:53 -07001204 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001205 self._DoTestFile('028_pack_4gb_outside.dts')
Simon Glassd6179862020-10-26 17:40:05 -06001206 self.assertIn("Node '/binman/u-boot': Offset 0x0 (0) size 0x4 (4) "
1207 "is outside the section '/binman' starting at "
1208 '0xffffffe0 (4294967264) of size 0x20 (32)',
Simon Glass72232452016-11-25 20:15:53 -07001209 str(e.exception))
1210
1211 def testPackX86Rom(self):
1212 """Test that a basic x86 ROM can be created"""
Simon Glass7057d022018-10-01 21:12:47 -06001213 self._SetupSplElf()
Simon Glass1d167762019-08-24 07:23:01 -06001214 data = self._DoReadFile('029_x86_rom.dts')
Simon Glass80025522022-01-29 14:14:04 -07001215 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 3) + U_BOOT_SPL_DATA +
1216 tools.get_bytes(0, 2), data)
Simon Glass72232452016-11-25 20:15:53 -07001217
1218 def testPackX86RomMeNoDesc(self):
1219 """Test that an invalid Intel descriptor entry is detected"""
Simon Glasse88cef92020-07-09 18:39:41 -06001220 try:
Simon Glass14c596c2020-07-25 15:11:19 -06001221 TestFunctional._MakeInputFile('descriptor-empty.bin', b'')
Simon Glasse88cef92020-07-09 18:39:41 -06001222 with self.assertRaises(ValueError) as e:
Simon Glass14c596c2020-07-25 15:11:19 -06001223 self._DoTestFile('163_x86_rom_me_empty.dts')
Simon Glasse88cef92020-07-09 18:39:41 -06001224 self.assertIn("Node '/binman/intel-descriptor': Cannot find Intel Flash Descriptor (FD) signature",
1225 str(e.exception))
1226 finally:
1227 self._SetupDescriptor()
Simon Glass72232452016-11-25 20:15:53 -07001228
1229 def testPackX86RomBadDesc(self):
1230 """Test that the Intel requires a descriptor entry"""
1231 with self.assertRaises(ValueError) as e:
Simon Glass1d167762019-08-24 07:23:01 -06001232 self._DoTestFile('030_x86_rom_me_no_desc.dts')
Simon Glasse8561af2018-08-01 15:22:37 -06001233 self.assertIn("Node '/binman/intel-me': No offset set with "
1234 "offset-unset: should another entry provide this correct "
1235 "offset?", str(e.exception))
Simon Glass72232452016-11-25 20:15:53 -07001236
1237 def testPackX86RomMe(self):
1238 """Test that an x86 ROM with an ME region can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001239 data = self._DoReadFile('031_x86_rom_me.dts')
Simon Glass80025522022-01-29 14:14:04 -07001240 expected_desc = tools.read_file(self.TestFile('descriptor.bin'))
Simon Glass759af872019-07-08 13:18:54 -06001241 if data[:0x1000] != expected_desc:
1242 self.fail('Expected descriptor binary at start of image')
Simon Glass72232452016-11-25 20:15:53 -07001243 self.assertEqual(ME_DATA, data[0x1000:0x1000 + len(ME_DATA)])
1244
1245 def testPackVga(self):
1246 """Test that an image with a VGA binary can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001247 data = self._DoReadFile('032_intel_vga.dts')
Simon Glass72232452016-11-25 20:15:53 -07001248 self.assertEqual(VGA_DATA, data[:len(VGA_DATA)])
1249
1250 def testPackStart16(self):
1251 """Test that an image with an x86 start16 region can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001252 data = self._DoReadFile('033_x86_start16.dts')
Simon Glass72232452016-11-25 20:15:53 -07001253 self.assertEqual(X86_START16_DATA, data[:len(X86_START16_DATA)])
1254
Jagdish Gediya311d4842018-09-03 21:35:08 +05301255 def testPackPowerpcMpc85xxBootpgResetvec(self):
1256 """Test that an image with powerpc-mpc85xx-bootpg-resetvec can be
1257 created"""
Simon Glass11f2bd02019-08-24 07:23:02 -06001258 data = self._DoReadFile('150_powerpc_mpc85xx_bootpg_resetvec.dts')
Jagdish Gediya311d4842018-09-03 21:35:08 +05301259 self.assertEqual(PPC_MPC85XX_BR_DATA, data[:len(PPC_MPC85XX_BR_DATA)])
1260
Simon Glass6ba679c2018-07-06 10:27:17 -06001261 def _RunMicrocodeTest(self, dts_fname, nodtb_data, ucode_second=False):
Simon Glass820af1d2018-07-06 10:27:16 -06001262 """Handle running a test for insertion of microcode
1263
1264 Args:
1265 dts_fname: Name of test .dts file
1266 nodtb_data: Data that we expect in the first section
Simon Glass6ba679c2018-07-06 10:27:17 -06001267 ucode_second: True if the microsecond entry is second instead of
1268 third
Simon Glass820af1d2018-07-06 10:27:16 -06001269
1270 Returns:
1271 Tuple:
1272 Contents of first region (U-Boot or SPL)
Simon Glasse8561af2018-08-01 15:22:37 -06001273 Offset and size components of microcode pointer, as inserted
Simon Glass820af1d2018-07-06 10:27:16 -06001274 in the above (two 4-byte words)
1275 """
Simon Glass3d274232017-11-12 21:52:27 -07001276 data = self._DoReadFile(dts_fname, True)
Simon Glass72232452016-11-25 20:15:53 -07001277
1278 # Now check the device tree has no microcode
Simon Glass6ba679c2018-07-06 10:27:17 -06001279 if ucode_second:
1280 ucode_content = data[len(nodtb_data):]
1281 ucode_pos = len(nodtb_data)
1282 dtb_with_ucode = ucode_content[16:]
1283 fdt_len = self.GetFdtLen(dtb_with_ucode)
1284 else:
1285 dtb_with_ucode = data[len(nodtb_data):]
1286 fdt_len = self.GetFdtLen(dtb_with_ucode)
1287 ucode_content = dtb_with_ucode[fdt_len:]
1288 ucode_pos = len(nodtb_data) + fdt_len
Simon Glass80025522022-01-29 14:14:04 -07001289 fname = tools.get_output_filename('test.dtb')
Simon Glass72232452016-11-25 20:15:53 -07001290 with open(fname, 'wb') as fd:
Simon Glass820af1d2018-07-06 10:27:16 -06001291 fd.write(dtb_with_ucode)
Simon Glass22c92ca2017-05-27 07:38:29 -06001292 dtb = fdt.FdtScan(fname)
1293 ucode = dtb.GetNode('/microcode')
Simon Glass72232452016-11-25 20:15:53 -07001294 self.assertTrue(ucode)
1295 for node in ucode.subnodes:
1296 self.assertFalse(node.props.get('data'))
1297
Simon Glass72232452016-11-25 20:15:53 -07001298 # Check that the microcode appears immediately after the Fdt
1299 # This matches the concatenation of the data properties in
Simon Glasse83679d2017-11-12 21:52:26 -07001300 # the /microcode/update@xxx nodes in 34_x86_ucode.dts.
Simon Glass72232452016-11-25 20:15:53 -07001301 ucode_data = struct.pack('>4L', 0x12345678, 0x12345679, 0xabcd0000,
1302 0x78235609)
Simon Glass820af1d2018-07-06 10:27:16 -06001303 self.assertEqual(ucode_data, ucode_content[:len(ucode_data)])
Simon Glass72232452016-11-25 20:15:53 -07001304
1305 # Check that the microcode pointer was inserted. It should match the
Simon Glasse8561af2018-08-01 15:22:37 -06001306 # expected offset and size
Simon Glass72232452016-11-25 20:15:53 -07001307 pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
1308 len(ucode_data))
Simon Glass6ba679c2018-07-06 10:27:17 -06001309 u_boot = data[:len(nodtb_data)]
1310 return u_boot, pos_and_size
Simon Glass3d274232017-11-12 21:52:27 -07001311
1312 def testPackUbootMicrocode(self):
1313 """Test that x86 microcode can be handled correctly
1314
1315 We expect to see the following in the image, in order:
1316 u-boot-nodtb.bin with a microcode pointer inserted at the correct
1317 place
1318 u-boot.dtb with the microcode removed
1319 the microcode
1320 """
Simon Glass511f6582018-10-01 12:22:30 -06001321 first, pos_and_size = self._RunMicrocodeTest('034_x86_ucode.dts',
Simon Glass3d274232017-11-12 21:52:27 -07001322 U_BOOT_NODTB_DATA)
Simon Glass303f62f2019-05-17 22:00:46 -06001323 self.assertEqual(b'nodtb with microcode' + pos_and_size +
1324 b' somewhere in here', first)
Simon Glass72232452016-11-25 20:15:53 -07001325
Simon Glassbac25c82017-05-27 07:38:26 -06001326 def _RunPackUbootSingleMicrocode(self):
Simon Glass72232452016-11-25 20:15:53 -07001327 """Test that x86 microcode can be handled correctly
1328
1329 We expect to see the following in the image, in order:
1330 u-boot-nodtb.bin with a microcode pointer inserted at the correct
1331 place
1332 u-boot.dtb with the microcode
1333 an empty microcode region
1334 """
1335 # We need the libfdt library to run this test since only that allows
1336 # finding the offset of a property. This is required by
1337 # Entry_u_boot_dtb_with_ucode.ObtainContents().
Simon Glass511f6582018-10-01 12:22:30 -06001338 data = self._DoReadFile('035_x86_single_ucode.dts', True)
Simon Glass72232452016-11-25 20:15:53 -07001339
1340 second = data[len(U_BOOT_NODTB_DATA):]
1341
1342 fdt_len = self.GetFdtLen(second)
1343 third = second[fdt_len:]
1344 second = second[:fdt_len]
1345
Simon Glassbac25c82017-05-27 07:38:26 -06001346 ucode_data = struct.pack('>2L', 0x12345678, 0x12345679)
1347 self.assertIn(ucode_data, second)
1348 ucode_pos = second.find(ucode_data) + len(U_BOOT_NODTB_DATA)
Simon Glass72232452016-11-25 20:15:53 -07001349
Simon Glassbac25c82017-05-27 07:38:26 -06001350 # Check that the microcode pointer was inserted. It should match the
Simon Glasse8561af2018-08-01 15:22:37 -06001351 # expected offset and size
Simon Glassbac25c82017-05-27 07:38:26 -06001352 pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
1353 len(ucode_data))
1354 first = data[:len(U_BOOT_NODTB_DATA)]
Simon Glass303f62f2019-05-17 22:00:46 -06001355 self.assertEqual(b'nodtb with microcode' + pos_and_size +
1356 b' somewhere in here', first)
Simon Glass996021e2016-11-25 20:15:54 -07001357
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001358 def testPackUbootSingleMicrocode(self):
1359 """Test that x86 microcode can be handled correctly with fdt_normal.
1360 """
Simon Glassbac25c82017-05-27 07:38:26 -06001361 self._RunPackUbootSingleMicrocode()
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001362
Simon Glass996021e2016-11-25 20:15:54 -07001363 def testUBootImg(self):
1364 """Test that u-boot.img can be put in a file"""
Simon Glass511f6582018-10-01 12:22:30 -06001365 data = self._DoReadFile('036_u_boot_img.dts')
Simon Glass996021e2016-11-25 20:15:54 -07001366 self.assertEqual(U_BOOT_IMG_DATA, data)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001367
1368 def testNoMicrocode(self):
1369 """Test that a missing microcode region is detected"""
1370 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001371 self._DoReadFile('037_x86_no_ucode.dts', True)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001372 self.assertIn("Node '/binman/u-boot-dtb-with-ucode': No /microcode "
1373 "node found in ", str(e.exception))
1374
1375 def testMicrocodeWithoutNode(self):
1376 """Test that a missing u-boot-dtb-with-ucode node is detected"""
1377 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001378 self._DoReadFile('038_x86_ucode_missing_node.dts', True)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001379 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
1380 "microcode region u-boot-dtb-with-ucode", str(e.exception))
1381
1382 def testMicrocodeWithoutNode2(self):
1383 """Test that a missing u-boot-ucode node is detected"""
1384 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001385 self._DoReadFile('039_x86_ucode_missing_node2.dts', True)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001386 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
1387 "microcode region u-boot-ucode", str(e.exception))
1388
1389 def testMicrocodeWithoutPtrInElf(self):
1390 """Test that a U-Boot binary without the microcode symbol is detected"""
1391 # ELF file without a '_dt_ucode_base_size' symbol
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001392 try:
Simon Glassfaaaa162019-08-24 07:22:55 -06001393 TestFunctional._MakeInputFile('u-boot',
Simon Glass80025522022-01-29 14:14:04 -07001394 tools.read_file(self.ElfTestFile('u_boot_no_ucode_ptr')))
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001395
1396 with self.assertRaises(ValueError) as e:
Simon Glassbac25c82017-05-27 07:38:26 -06001397 self._RunPackUbootSingleMicrocode()
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001398 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot locate "
1399 "_dt_ucode_base_size symbol in u-boot", str(e.exception))
1400
1401 finally:
1402 # Put the original file back
Simon Glass4affd4b2019-08-24 07:22:54 -06001403 TestFunctional._MakeInputFile('u-boot',
Simon Glass80025522022-01-29 14:14:04 -07001404 tools.read_file(self.ElfTestFile('u_boot_ucode_ptr')))
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001405
1406 def testMicrocodeNotInImage(self):
1407 """Test that microcode must be placed within the image"""
1408 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001409 self._DoReadFile('040_x86_ucode_not_in_image.dts', True)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001410 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Microcode "
1411 "pointer _dt_ucode_base_size at fffffe14 is outside the "
Simon Glassad5a7712018-06-01 09:38:14 -06001412 "section ranging from 00000000 to 0000002e", str(e.exception))
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001413
1414 def testWithoutMicrocode(self):
1415 """Test that we can cope with an image without microcode (e.g. qemu)"""
Simon Glassfaaaa162019-08-24 07:22:55 -06001416 TestFunctional._MakeInputFile('u-boot',
Simon Glass80025522022-01-29 14:14:04 -07001417 tools.read_file(self.ElfTestFile('u_boot_no_ucode_ptr')))
Simon Glass511f6582018-10-01 12:22:30 -06001418 data, dtb, _, _ = self._DoReadFileDtb('044_x86_optional_ucode.dts', True)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001419
1420 # Now check the device tree has no microcode
1421 self.assertEqual(U_BOOT_NODTB_DATA, data[:len(U_BOOT_NODTB_DATA)])
1422 second = data[len(U_BOOT_NODTB_DATA):]
1423
1424 fdt_len = self.GetFdtLen(second)
1425 self.assertEqual(dtb, second[:fdt_len])
1426
1427 used_len = len(U_BOOT_NODTB_DATA) + fdt_len
1428 third = data[used_len:]
Simon Glass80025522022-01-29 14:14:04 -07001429 self.assertEqual(tools.get_bytes(0, 0x200 - used_len), third)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001430
1431 def testUnknownPosSize(self):
1432 """Test that microcode must be placed within the image"""
1433 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001434 self._DoReadFile('041_unknown_pos_size.dts', True)
Simon Glasse8561af2018-08-01 15:22:37 -06001435 self.assertIn("Section '/binman': Unable to set offset/size for unknown "
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001436 "entry 'invalid-entry'", str(e.exception))
Simon Glassb4176d42016-11-25 20:15:56 -07001437
1438 def testPackFsp(self):
1439 """Test that an image with a FSP binary can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001440 data = self._DoReadFile('042_intel_fsp.dts')
Simon Glassb4176d42016-11-25 20:15:56 -07001441 self.assertEqual(FSP_DATA, data[:len(FSP_DATA)])
1442
1443 def testPackCmc(self):
Bin Mengd7bcdf52017-08-15 22:41:54 -07001444 """Test that an image with a CMC binary can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001445 data = self._DoReadFile('043_intel_cmc.dts')
Simon Glassb4176d42016-11-25 20:15:56 -07001446 self.assertEqual(CMC_DATA, data[:len(CMC_DATA)])
Bin Mengd7bcdf52017-08-15 22:41:54 -07001447
1448 def testPackVbt(self):
1449 """Test that an image with a VBT binary can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001450 data = self._DoReadFile('046_intel_vbt.dts')
Bin Mengd7bcdf52017-08-15 22:41:54 -07001451 self.assertEqual(VBT_DATA, data[:len(VBT_DATA)])
Simon Glassac599912017-11-12 21:52:22 -07001452
Simon Glass7f94e832017-11-12 21:52:25 -07001453 def testSplBssPad(self):
1454 """Test that we can pad SPL's BSS with zeros"""
Simon Glass3d274232017-11-12 21:52:27 -07001455 # ELF file with a '__bss_size' symbol
Simon Glass7057d022018-10-01 21:12:47 -06001456 self._SetupSplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001457 data = self._DoReadFile('047_spl_bss_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07001458 self.assertEqual(U_BOOT_SPL_DATA + tools.get_bytes(0, 10) + U_BOOT_DATA,
Simon Glassac0d4952019-05-14 15:53:47 -06001459 data)
Simon Glass7f94e832017-11-12 21:52:25 -07001460
Simon Glass04cda032018-10-01 21:12:42 -06001461 def testSplBssPadMissing(self):
1462 """Test that a missing symbol is detected"""
Simon Glass7057d022018-10-01 21:12:47 -06001463 self._SetupSplElf('u_boot_ucode_ptr')
Simon Glass24ad3652017-11-13 18:54:54 -07001464 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001465 self._DoReadFile('047_spl_bss_pad.dts')
Simon Glass24ad3652017-11-13 18:54:54 -07001466 self.assertIn('Expected __bss_size symbol in spl/u-boot-spl',
1467 str(e.exception))
1468
Simon Glasse83679d2017-11-12 21:52:26 -07001469 def testPackStart16Spl(self):
Simon Glassed40e962018-09-14 04:57:10 -06001470 """Test that an image with an x86 start16 SPL region can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001471 data = self._DoReadFile('048_x86_start16_spl.dts')
Simon Glasse83679d2017-11-12 21:52:26 -07001472 self.assertEqual(X86_START16_SPL_DATA, data[:len(X86_START16_SPL_DATA)])
1473
Simon Glass6ba679c2018-07-06 10:27:17 -06001474 def _PackUbootSplMicrocode(self, dts, ucode_second=False):
1475 """Helper function for microcode tests
Simon Glass3d274232017-11-12 21:52:27 -07001476
1477 We expect to see the following in the image, in order:
1478 u-boot-spl-nodtb.bin with a microcode pointer inserted at the
1479 correct place
1480 u-boot.dtb with the microcode removed
1481 the microcode
Simon Glass6ba679c2018-07-06 10:27:17 -06001482
1483 Args:
1484 dts: Device tree file to use for test
1485 ucode_second: True if the microsecond entry is second instead of
1486 third
Simon Glass3d274232017-11-12 21:52:27 -07001487 """
Simon Glass7057d022018-10-01 21:12:47 -06001488 self._SetupSplElf('u_boot_ucode_ptr')
Simon Glass6ba679c2018-07-06 10:27:17 -06001489 first, pos_and_size = self._RunMicrocodeTest(dts, U_BOOT_SPL_NODTB_DATA,
1490 ucode_second=ucode_second)
Simon Glass303f62f2019-05-17 22:00:46 -06001491 self.assertEqual(b'splnodtb with microc' + pos_and_size +
1492 b'ter somewhere in here', first)
Simon Glass3d274232017-11-12 21:52:27 -07001493
Simon Glass6ba679c2018-07-06 10:27:17 -06001494 def testPackUbootSplMicrocode(self):
1495 """Test that x86 microcode can be handled correctly in SPL"""
Marek Vasutf7413f02023-07-18 07:23:58 -06001496 self._SetupSplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001497 self._PackUbootSplMicrocode('049_x86_ucode_spl.dts')
Simon Glass6ba679c2018-07-06 10:27:17 -06001498
1499 def testPackUbootSplMicrocodeReorder(self):
1500 """Test that order doesn't matter for microcode entries
1501
1502 This is the same as testPackUbootSplMicrocode but when we process the
1503 u-boot-ucode entry we have not yet seen the u-boot-dtb-with-ucode
1504 entry, so we reply on binman to try later.
1505 """
Simon Glass511f6582018-10-01 12:22:30 -06001506 self._PackUbootSplMicrocode('058_x86_ucode_spl_needs_retry.dts',
Simon Glass6ba679c2018-07-06 10:27:17 -06001507 ucode_second=True)
1508
Simon Glassa409c932017-11-12 21:52:28 -07001509 def testPackMrc(self):
1510 """Test that an image with an MRC binary can be created"""
Simon Glass511f6582018-10-01 12:22:30 -06001511 data = self._DoReadFile('050_intel_mrc.dts')
Simon Glassa409c932017-11-12 21:52:28 -07001512 self.assertEqual(MRC_DATA, data[:len(MRC_DATA)])
1513
Simon Glass9aa6a6f2017-11-13 18:54:55 -07001514 def testSplDtb(self):
1515 """Test that an image with spl/u-boot-spl.dtb can be created"""
Marek Vasutf7413f02023-07-18 07:23:58 -06001516 self._SetupSplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001517 data = self._DoReadFile('051_u_boot_spl_dtb.dts')
Simon Glass9aa6a6f2017-11-13 18:54:55 -07001518 self.assertEqual(U_BOOT_SPL_DTB_DATA, data[:len(U_BOOT_SPL_DTB_DATA)])
1519
Simon Glass0a6da312017-11-13 18:54:56 -07001520 def testSplNoDtb(self):
1521 """Test that an image with spl/u-boot-spl-nodtb.bin can be created"""
Simon Glass13089cc2021-04-25 08:39:32 +12001522 self._SetupSplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001523 data = self._DoReadFile('052_u_boot_spl_nodtb.dts')
Simon Glass0a6da312017-11-13 18:54:56 -07001524 self.assertEqual(U_BOOT_SPL_NODTB_DATA, data[:len(U_BOOT_SPL_NODTB_DATA)])
1525
Simon Glass7098b7f2021-03-21 18:24:30 +13001526 def checkSymbols(self, dts, base_data, u_boot_offset, entry_args=None,
Simon Glass4b0f4142024-08-26 13:11:40 -06001527 use_expanded=False, no_write_symbols=False,
1528 symbols_base=None):
Simon Glass31e04cb2021-03-18 20:24:56 +13001529 """Check the image contains the expected symbol values
1530
1531 Args:
1532 dts: Device tree file to use for test
1533 base_data: Data before and after 'u-boot' section
Simon Glass3eb30a42024-08-26 13:11:42 -06001534 u_boot_offset (int): Offset of 'u-boot' section in image, or None if
1535 the offset not available due to it being in a compressed section
Simon Glass7098b7f2021-03-21 18:24:30 +13001536 entry_args: Dict of entry args to supply to binman
1537 key: arg name
1538 value: value of that arg
1539 use_expanded: True to use expanded entries where available, e.g.
1540 'u-boot-expanded' instead of 'u-boot'
Simon Glass4b0f4142024-08-26 13:11:40 -06001541 symbols_base (int): Value to expect for symbols-base in u-boot-spl,
1542 None if none
Simon Glass31e04cb2021-03-18 20:24:56 +13001543 """
Simon Glass5d0c0262019-08-24 07:22:56 -06001544 elf_fname = self.ElfTestFile('u_boot_binman_syms')
Simon Glass4ca8e042017-11-13 18:55:01 -07001545 syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
1546 addr = elf.GetSymbolAddress(elf_fname, '__image_copy_start')
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03001547 self.assertEqual(syms['_binman_sym_magic'].address, addr)
Simon Glass31e04cb2021-03-18 20:24:56 +13001548 self.assertEqual(syms['_binman_u_boot_spl_any_prop_offset'].address,
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03001549 addr + 4)
Simon Glass4ca8e042017-11-13 18:55:01 -07001550
Simon Glass7057d022018-10-01 21:12:47 -06001551 self._SetupSplElf('u_boot_binman_syms')
Simon Glass7098b7f2021-03-21 18:24:30 +13001552 data = self._DoReadFileDtb(dts, entry_args=entry_args,
Simon Glass3eb30a42024-08-26 13:11:42 -06001553 use_expanded=use_expanded,
1554 verbosity=None if u_boot_offset else 3)[0]
1555
1556 # The lz4-compressed version of the U-Boot data is 19 bytes long
1557 comp_uboot_len = 19
1558
Simon Glass31e04cb2021-03-18 20:24:56 +13001559 # The image should contain the symbols from u_boot_binman_syms.c
1560 # Note that image_pos is adjusted by the base address of the image,
1561 # which is 0x10 in our test image
Simon Glass3eb30a42024-08-26 13:11:42 -06001562 # If u_boot_offset is None, Binman should write -1U into the image
Simon Glass4b0f4142024-08-26 13:11:40 -06001563 vals2 = (elf.BINMAN_SYM_MAGIC_VALUE, 0x00,
Simon Glass3eb30a42024-08-26 13:11:42 -06001564 u_boot_offset + len(U_BOOT_DATA) if u_boot_offset else
1565 len(U_BOOT_SPL_DATA) + 1 + comp_uboot_len,
1566 0x10 + u_boot_offset if u_boot_offset else 0xffffffff, 0x04)
Simon Glass4b0f4142024-08-26 13:11:40 -06001567
1568 # u-boot-spl has a symbols-base property, so take that into account if
1569 # required. The caller must supply the value
1570 vals = list(vals2)
1571 if symbols_base is not None:
1572 vals[3] = symbols_base + u_boot_offset
1573 vals = tuple(vals)
1574
Simon Glass4b4049e2024-08-26 13:11:39 -06001575 sym_values = struct.pack('<LLQLL', *vals)
Simon Glass4b0f4142024-08-26 13:11:40 -06001576 sym_values2 = struct.pack('<LLQLL', *vals2)
Simon Glass4abf7842023-07-18 07:23:54 -06001577 if no_write_symbols:
Simon Glass4b4049e2024-08-26 13:11:39 -06001578 self.assertEqual(
1579 base_data +
1580 tools.get_bytes(0xff, 0x38 - len(base_data)) +
1581 U_BOOT_DATA + base_data, data)
Simon Glass4abf7842023-07-18 07:23:54 -06001582 else:
Simon Glass4b4049e2024-08-26 13:11:39 -06001583 got_vals = struct.unpack('<LLQLL', data[:24])
1584
1585 # For debugging:
1586 #print('expect:', list(f'{v:x}' for v in vals))
1587 #print(' got:', list(f'{v:x}' for v in got_vals))
1588
1589 self.assertEqual(vals, got_vals)
1590 self.assertEqual(sym_values, data[:24])
1591
1592 blen = len(base_data)
1593 self.assertEqual(base_data[24:], data[24:blen])
1594 self.assertEqual(0xff, data[blen])
1595
Simon Glass3eb30a42024-08-26 13:11:42 -06001596 if u_boot_offset:
1597 ofs = blen + 1 + len(U_BOOT_DATA)
1598 self.assertEqual(U_BOOT_DATA, data[blen + 1:ofs])
1599 else:
1600 ofs = blen + 1 + comp_uboot_len
Simon Glass4b4049e2024-08-26 13:11:39 -06001601
Simon Glass4b0f4142024-08-26 13:11:40 -06001602 self.assertEqual(sym_values2, data[ofs:ofs + 24])
Simon Glass4b4049e2024-08-26 13:11:39 -06001603 self.assertEqual(base_data[24:], data[ofs + 24:])
1604
1605 # Just repeating the above asserts all at once, for clarity
Simon Glass3eb30a42024-08-26 13:11:42 -06001606 if u_boot_offset:
1607 expected = (sym_values + base_data[24:] +
1608 tools.get_bytes(0xff, 1) + U_BOOT_DATA +
1609 sym_values2 + base_data[24:])
1610 self.assertEqual(expected, data)
Simon Glass4ca8e042017-11-13 18:55:01 -07001611
Simon Glass31e04cb2021-03-18 20:24:56 +13001612 def testSymbols(self):
1613 """Test binman can assign symbols embedded in U-Boot"""
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03001614 self.checkSymbols('053_symbols.dts', U_BOOT_SPL_DATA, 0x1c)
Simon Glass31e04cb2021-03-18 20:24:56 +13001615
1616 def testSymbolsNoDtb(self):
1617 """Test binman can assign symbols embedded in U-Boot SPL"""
Simon Glass3bbc9932021-03-21 18:24:29 +13001618 self.checkSymbols('196_symbols_nodtb.dts',
Simon Glass31e04cb2021-03-18 20:24:56 +13001619 U_BOOT_SPL_NODTB_DATA + U_BOOT_SPL_DTB_DATA,
1620 0x38)
1621
Simon Glasse76a3e62018-06-01 09:38:11 -06001622 def testPackUnitAddress(self):
1623 """Test that we support multiple binaries with the same name"""
Simon Glass511f6582018-10-01 12:22:30 -06001624 data = self._DoReadFile('054_unit_address.dts')
Simon Glasse76a3e62018-06-01 09:38:11 -06001625 self.assertEqual(U_BOOT_DATA + U_BOOT_DATA, data)
1626
Simon Glassa91e1152018-06-01 09:38:16 -06001627 def testSections(self):
1628 """Basic test of sections"""
Simon Glass511f6582018-10-01 12:22:30 -06001629 data = self._DoReadFile('055_sections.dts')
Simon Glass80025522022-01-29 14:14:04 -07001630 expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) +
1631 U_BOOT_DATA + tools.get_bytes(ord('a'), 12) +
1632 U_BOOT_DATA + tools.get_bytes(ord('&'), 4))
Simon Glassa91e1152018-06-01 09:38:16 -06001633 self.assertEqual(expected, data)
Simon Glassac599912017-11-12 21:52:22 -07001634
Simon Glass30732662018-06-01 09:38:20 -06001635 def testMap(self):
1636 """Tests outputting a map of the images"""
Simon Glass511f6582018-10-01 12:22:30 -06001637 _, _, map_data, _ = self._DoReadFileDtb('055_sections.dts', map=True)
Simon Glass7eca7922018-07-17 13:25:49 -06001638 self.assertEqual('''ImagePos Offset Size Name
Simon Glass49cd2b32023-02-07 14:34:18 -0700163900000000 00000000 00000028 image
Simon Glass7eca7922018-07-17 13:25:49 -0600164000000000 00000000 00000010 section@0
164100000000 00000000 00000004 u-boot
164200000010 00000010 00000010 section@1
164300000010 00000000 00000004 u-boot
164400000020 00000020 00000004 section@2
164500000020 00000000 00000004 u-boot
Simon Glass30732662018-06-01 09:38:20 -06001646''', map_data)
1647
Simon Glass3b78d532018-06-01 09:38:21 -06001648 def testNamePrefix(self):
1649 """Tests that name prefixes are used"""
Simon Glass511f6582018-10-01 12:22:30 -06001650 _, _, map_data, _ = self._DoReadFileDtb('056_name_prefix.dts', map=True)
Simon Glass7eca7922018-07-17 13:25:49 -06001651 self.assertEqual('''ImagePos Offset Size Name
Simon Glass49cd2b32023-02-07 14:34:18 -0700165200000000 00000000 00000028 image
Simon Glass7eca7922018-07-17 13:25:49 -0600165300000000 00000000 00000010 section@0
165400000000 00000000 00000004 ro-u-boot
165500000010 00000010 00000010 section@1
165600000010 00000000 00000004 rw-u-boot
Simon Glass3b78d532018-06-01 09:38:21 -06001657''', map_data)
1658
Simon Glass6ba679c2018-07-06 10:27:17 -06001659 def testUnknownContents(self):
1660 """Test that obtaining the contents works as expected"""
1661 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001662 self._DoReadFile('057_unknown_contents.dts', True)
Simon Glass39dd2152019-07-08 14:25:47 -06001663 self.assertIn("Image '/binman': Internal error: Could not complete "
Simon Glassc585dd42020-04-17 18:09:03 -06001664 "processing of contents: remaining ["
1665 "<binman.etype._testing.Entry__testing ", str(e.exception))
Simon Glass6ba679c2018-07-06 10:27:17 -06001666
Simon Glass2e1169f2018-07-06 10:27:19 -06001667 def testBadChangeSize(self):
1668 """Test that trying to change the size of an entry fails"""
Simon Glasse61b6f62019-07-08 14:25:37 -06001669 try:
1670 state.SetAllowEntryExpansion(False)
1671 with self.assertRaises(ValueError) as e:
1672 self._DoReadFile('059_change_size.dts', True)
Simon Glass8c702fb2019-07-20 12:23:57 -06001673 self.assertIn("Node '/binman/_testing': Cannot update entry size from 2 to 3",
Simon Glasse61b6f62019-07-08 14:25:37 -06001674 str(e.exception))
1675 finally:
1676 state.SetAllowEntryExpansion(True)
Simon Glass2e1169f2018-07-06 10:27:19 -06001677
Simon Glassa87014e2018-07-06 10:27:42 -06001678 def testUpdateFdt(self):
Simon Glasse8561af2018-08-01 15:22:37 -06001679 """Test that we can update the device tree with offset/size info"""
Simon Glass511f6582018-10-01 12:22:30 -06001680 _, _, _, out_dtb_fname = self._DoReadFileDtb('060_fdt_update.dts',
Simon Glassa87014e2018-07-06 10:27:42 -06001681 update_dtb=True)
Simon Glass5463a6a2018-07-17 13:25:52 -06001682 dtb = fdt.Fdt(out_dtb_fname)
1683 dtb.Scan()
Simon Glassfb30e292019-07-20 12:23:51 -06001684 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
Simon Glassa87014e2018-07-06 10:27:42 -06001685 self.assertEqual({
Simon Glass9dcc8612018-08-01 15:22:42 -06001686 'image-pos': 0,
Simon Glass3a9a2b82018-07-17 13:25:28 -06001687 'offset': 0,
Simon Glasse8561af2018-08-01 15:22:37 -06001688 '_testing:offset': 32,
Simon Glass8c702fb2019-07-20 12:23:57 -06001689 '_testing:size': 2,
Simon Glass9dcc8612018-08-01 15:22:42 -06001690 '_testing:image-pos': 32,
Simon Glasse8561af2018-08-01 15:22:37 -06001691 'section@0/u-boot:offset': 0,
Simon Glassa87014e2018-07-06 10:27:42 -06001692 'section@0/u-boot:size': len(U_BOOT_DATA),
Simon Glass9dcc8612018-08-01 15:22:42 -06001693 'section@0/u-boot:image-pos': 0,
Simon Glasse8561af2018-08-01 15:22:37 -06001694 'section@0:offset': 0,
Simon Glassa87014e2018-07-06 10:27:42 -06001695 'section@0:size': 16,
Simon Glass9dcc8612018-08-01 15:22:42 -06001696 'section@0:image-pos': 0,
Simon Glassa87014e2018-07-06 10:27:42 -06001697
Simon Glasse8561af2018-08-01 15:22:37 -06001698 'section@1/u-boot:offset': 0,
Simon Glassa87014e2018-07-06 10:27:42 -06001699 'section@1/u-boot:size': len(U_BOOT_DATA),
Simon Glass9dcc8612018-08-01 15:22:42 -06001700 'section@1/u-boot:image-pos': 16,
Simon Glasse8561af2018-08-01 15:22:37 -06001701 'section@1:offset': 16,
Simon Glassa87014e2018-07-06 10:27:42 -06001702 'section@1:size': 16,
Simon Glass9dcc8612018-08-01 15:22:42 -06001703 'section@1:image-pos': 16,
Simon Glassa87014e2018-07-06 10:27:42 -06001704 'size': 40
1705 }, props)
1706
1707 def testUpdateFdtBad(self):
1708 """Test that we detect when ProcessFdt never completes"""
1709 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001710 self._DoReadFileDtb('061_fdt_update_bad.dts', update_dtb=True)
Simon Glassa87014e2018-07-06 10:27:42 -06001711 self.assertIn('Could not complete processing of Fdt: remaining '
Simon Glassc585dd42020-04-17 18:09:03 -06001712 '[<binman.etype._testing.Entry__testing',
1713 str(e.exception))
Simon Glass2e1169f2018-07-06 10:27:19 -06001714
Simon Glass91710b32018-07-17 13:25:32 -06001715 def testEntryArgs(self):
1716 """Test passing arguments to entries from the command line"""
1717 entry_args = {
1718 'test-str-arg': 'test1',
1719 'test-int-arg': '456',
1720 }
Simon Glass511f6582018-10-01 12:22:30 -06001721 self._DoReadFileDtb('062_entry_args.dts', entry_args=entry_args)
Simon Glass91710b32018-07-17 13:25:32 -06001722 self.assertIn('image', control.images)
1723 entry = control.images['image'].GetEntries()['_testing']
1724 self.assertEqual('test0', entry.test_str_fdt)
1725 self.assertEqual('test1', entry.test_str_arg)
1726 self.assertEqual(123, entry.test_int_fdt)
1727 self.assertEqual(456, entry.test_int_arg)
1728
1729 def testEntryArgsMissing(self):
1730 """Test missing arguments and properties"""
1731 entry_args = {
1732 'test-int-arg': '456',
1733 }
Simon Glass511f6582018-10-01 12:22:30 -06001734 self._DoReadFileDtb('063_entry_args_missing.dts', entry_args=entry_args)
Simon Glass91710b32018-07-17 13:25:32 -06001735 entry = control.images['image'].GetEntries()['_testing']
1736 self.assertEqual('test0', entry.test_str_fdt)
1737 self.assertEqual(None, entry.test_str_arg)
1738 self.assertEqual(None, entry.test_int_fdt)
1739 self.assertEqual(456, entry.test_int_arg)
1740
1741 def testEntryArgsRequired(self):
1742 """Test missing arguments and properties"""
1743 entry_args = {
1744 'test-int-arg': '456',
1745 }
1746 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001747 self._DoReadFileDtb('064_entry_args_required.dts')
Simon Glass21db0ff2020-09-01 05:13:54 -06001748 self.assertIn("Node '/binman/_testing': "
1749 'Missing required properties/entry args: test-str-arg, '
1750 'test-int-fdt, test-int-arg',
Simon Glass91710b32018-07-17 13:25:32 -06001751 str(e.exception))
1752
1753 def testEntryArgsInvalidFormat(self):
1754 """Test that an invalid entry-argument format is detected"""
Simon Glassf46732a2019-07-08 14:25:29 -06001755 args = ['build', '-d', self.TestFile('064_entry_args_required.dts'),
1756 '-ano-value']
Simon Glass91710b32018-07-17 13:25:32 -06001757 with self.assertRaises(ValueError) as e:
1758 self._DoBinman(*args)
1759 self.assertIn("Invalid entry arguemnt 'no-value'", str(e.exception))
1760
1761 def testEntryArgsInvalidInteger(self):
1762 """Test that an invalid entry-argument integer is detected"""
1763 entry_args = {
1764 'test-int-arg': 'abc',
1765 }
1766 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001767 self._DoReadFileDtb('062_entry_args.dts', entry_args=entry_args)
Simon Glass91710b32018-07-17 13:25:32 -06001768 self.assertIn("Node '/binman/_testing': Cannot convert entry arg "
1769 "'test-int-arg' (value 'abc') to integer",
1770 str(e.exception))
1771
1772 def testEntryArgsInvalidDatatype(self):
1773 """Test that an invalid entry-argument datatype is detected
1774
1775 This test could be written in entry_test.py except that it needs
1776 access to control.entry_args, which seems more than that module should
1777 be able to see.
1778 """
1779 entry_args = {
1780 'test-bad-datatype-arg': '12',
1781 }
1782 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001783 self._DoReadFileDtb('065_entry_args_unknown_datatype.dts',
Simon Glass91710b32018-07-17 13:25:32 -06001784 entry_args=entry_args)
1785 self.assertIn('GetArg() internal error: Unknown data type ',
1786 str(e.exception))
1787
Simon Glass2ca52032018-07-17 13:25:33 -06001788 def testText(self):
1789 """Test for a text entry type"""
1790 entry_args = {
1791 'test-id': TEXT_DATA,
1792 'test-id2': TEXT_DATA2,
1793 'test-id3': TEXT_DATA3,
1794 }
Simon Glass511f6582018-10-01 12:22:30 -06001795 data, _, _, _ = self._DoReadFileDtb('066_text.dts',
Simon Glass2ca52032018-07-17 13:25:33 -06001796 entry_args=entry_args)
Simon Glass80025522022-01-29 14:14:04 -07001797 expected = (tools.to_bytes(TEXT_DATA) +
1798 tools.get_bytes(0, 8 - len(TEXT_DATA)) +
1799 tools.to_bytes(TEXT_DATA2) + tools.to_bytes(TEXT_DATA3) +
Simon Glass47f6a622019-07-08 13:18:40 -06001800 b'some text' + b'more text')
Simon Glass2ca52032018-07-17 13:25:33 -06001801 self.assertEqual(expected, data)
1802
Simon Glass969616c2018-07-17 13:25:36 -06001803 def testEntryDocs(self):
1804 """Test for creation of entry documentation"""
Simon Glass14d64e32025-04-29 07:21:59 -06001805 with terminal.capture() as (stdout, stderr):
Simon Glass220ff5f2020-08-05 13:27:46 -06001806 control.WriteEntryDocs(control.GetEntryModules())
Simon Glass969616c2018-07-17 13:25:36 -06001807 self.assertTrue(len(stdout.getvalue()) > 0)
1808
1809 def testEntryDocsMissing(self):
1810 """Test handling of missing entry documentation"""
1811 with self.assertRaises(ValueError) as e:
Simon Glass14d64e32025-04-29 07:21:59 -06001812 with terminal.capture() as (stdout, stderr):
Simon Glass220ff5f2020-08-05 13:27:46 -06001813 control.WriteEntryDocs(control.GetEntryModules(), 'u_boot')
Simon Glass969616c2018-07-17 13:25:36 -06001814 self.assertIn('Documentation is missing for modules: u_boot',
1815 str(e.exception))
1816
Simon Glass704784b2018-07-17 13:25:38 -06001817 def testFmap(self):
1818 """Basic test of generation of a flashrom fmap"""
Simon Glass511f6582018-10-01 12:22:30 -06001819 data = self._DoReadFile('067_fmap.dts')
Simon Glass704784b2018-07-17 13:25:38 -06001820 fhdr, fentries = fmap_util.DecodeFmap(data[32:])
Simon Glass80025522022-01-29 14:14:04 -07001821 expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) +
1822 U_BOOT_DATA + tools.get_bytes(ord('a'), 12))
Simon Glass704784b2018-07-17 13:25:38 -06001823 self.assertEqual(expected, data[:32])
Simon Glass303f62f2019-05-17 22:00:46 -06001824 self.assertEqual(b'__FMAP__', fhdr.signature)
Simon Glass704784b2018-07-17 13:25:38 -06001825 self.assertEqual(1, fhdr.ver_major)
1826 self.assertEqual(0, fhdr.ver_minor)
1827 self.assertEqual(0, fhdr.base)
Simon Glassb1d414c2021-04-03 11:05:10 +13001828 expect_size = fmap_util.FMAP_HEADER_LEN + fmap_util.FMAP_AREA_LEN * 5
Simon Glass82059c22021-04-03 11:05:09 +13001829 self.assertEqual(16 + 16 + expect_size, fhdr.image_size)
Simon Glass303f62f2019-05-17 22:00:46 -06001830 self.assertEqual(b'FMAP', fhdr.name)
Simon Glassb1d414c2021-04-03 11:05:10 +13001831 self.assertEqual(5, fhdr.nareas)
Simon Glass82059c22021-04-03 11:05:09 +13001832 fiter = iter(fentries)
Simon Glass704784b2018-07-17 13:25:38 -06001833
Simon Glass82059c22021-04-03 11:05:09 +13001834 fentry = next(fiter)
Simon Glassb1d414c2021-04-03 11:05:10 +13001835 self.assertEqual(b'SECTION0', fentry.name)
1836 self.assertEqual(0, fentry.offset)
1837 self.assertEqual(16, fentry.size)
Simon Glasscda991e2023-02-12 17:11:15 -07001838 self.assertEqual(fmap_util.FMAP_AREA_PRESERVE, fentry.flags)
Simon Glassb1d414c2021-04-03 11:05:10 +13001839
1840 fentry = next(fiter)
Simon Glass82059c22021-04-03 11:05:09 +13001841 self.assertEqual(b'RO_U_BOOT', fentry.name)
1842 self.assertEqual(0, fentry.offset)
1843 self.assertEqual(4, fentry.size)
1844 self.assertEqual(0, fentry.flags)
Simon Glass704784b2018-07-17 13:25:38 -06001845
Simon Glass82059c22021-04-03 11:05:09 +13001846 fentry = next(fiter)
Simon Glassb1d414c2021-04-03 11:05:10 +13001847 self.assertEqual(b'SECTION1', fentry.name)
1848 self.assertEqual(16, fentry.offset)
1849 self.assertEqual(16, fentry.size)
1850 self.assertEqual(0, fentry.flags)
1851
1852 fentry = next(fiter)
Simon Glass82059c22021-04-03 11:05:09 +13001853 self.assertEqual(b'RW_U_BOOT', fentry.name)
1854 self.assertEqual(16, fentry.offset)
1855 self.assertEqual(4, fentry.size)
1856 self.assertEqual(0, fentry.flags)
Simon Glass704784b2018-07-17 13:25:38 -06001857
Simon Glass82059c22021-04-03 11:05:09 +13001858 fentry = next(fiter)
1859 self.assertEqual(b'FMAP', fentry.name)
1860 self.assertEqual(32, fentry.offset)
1861 self.assertEqual(expect_size, fentry.size)
1862 self.assertEqual(0, fentry.flags)
Simon Glass704784b2018-07-17 13:25:38 -06001863
Simon Glassdb168d42018-07-17 13:25:39 -06001864 def testBlobNamedByArg(self):
1865 """Test we can add a blob with the filename coming from an entry arg"""
1866 entry_args = {
1867 'cros-ec-rw-path': 'ecrw.bin',
1868 }
Simon Glass21db0ff2020-09-01 05:13:54 -06001869 self._DoReadFileDtb('068_blob_named_by_arg.dts', entry_args=entry_args)
Simon Glassdb168d42018-07-17 13:25:39 -06001870
Simon Glass53f53992018-07-17 13:25:40 -06001871 def testFill(self):
1872 """Test for an fill entry type"""
Simon Glass511f6582018-10-01 12:22:30 -06001873 data = self._DoReadFile('069_fill.dts')
Simon Glass80025522022-01-29 14:14:04 -07001874 expected = tools.get_bytes(0xff, 8) + tools.get_bytes(0, 8)
Simon Glass53f53992018-07-17 13:25:40 -06001875 self.assertEqual(expected, data)
1876
1877 def testFillNoSize(self):
1878 """Test for an fill entry type with no size"""
1879 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001880 self._DoReadFile('070_fill_no_size.dts')
Simon Glass0cf5bce2022-08-13 11:40:44 -06001881 self.assertIn("'fill' entry is missing properties: size",
Simon Glass53f53992018-07-17 13:25:40 -06001882 str(e.exception))
1883
Simon Glassc1ae83c2018-07-17 13:25:44 -06001884 def _HandleGbbCommand(self, pipe_list):
1885 """Fake calls to the futility utility"""
Simon Glass9a1c7262023-02-22 12:14:49 -07001886 if 'futility' in pipe_list[0][0]:
Simon Glassc1ae83c2018-07-17 13:25:44 -06001887 fname = pipe_list[0][-1]
1888 # Append our GBB data to the file, which will happen every time the
1889 # futility command is called.
Simon Glass33486662019-05-14 15:53:42 -06001890 with open(fname, 'ab') as fd:
Simon Glassc1ae83c2018-07-17 13:25:44 -06001891 fd.write(GBB_DATA)
1892 return command.CommandResult()
1893
1894 def testGbb(self):
1895 """Test for the Chromium OS Google Binary Block"""
Simon Glass5dc22cf2025-02-03 09:26:42 -07001896 command.TEST_RESULT = self._HandleGbbCommand
Simon Glassc1ae83c2018-07-17 13:25:44 -06001897 entry_args = {
1898 'keydir': 'devkeys',
1899 'bmpblk': 'bmpblk.bin',
1900 }
Simon Glass511f6582018-10-01 12:22:30 -06001901 data, _, _, _ = self._DoReadFileDtb('071_gbb.dts', entry_args=entry_args)
Simon Glassc1ae83c2018-07-17 13:25:44 -06001902
1903 # Since futility
Simon Glass80025522022-01-29 14:14:04 -07001904 expected = (GBB_DATA + GBB_DATA + tools.get_bytes(0, 8) +
1905 tools.get_bytes(0, 0x2180 - 16))
Simon Glassc1ae83c2018-07-17 13:25:44 -06001906 self.assertEqual(expected, data)
1907
1908 def testGbbTooSmall(self):
1909 """Test for the Chromium OS Google Binary Block being large enough"""
1910 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001911 self._DoReadFileDtb('072_gbb_too_small.dts')
Simon Glassc1ae83c2018-07-17 13:25:44 -06001912 self.assertIn("Node '/binman/gbb': GBB is too small",
1913 str(e.exception))
1914
1915 def testGbbNoSize(self):
1916 """Test for the Chromium OS Google Binary Block having a size"""
1917 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001918 self._DoReadFileDtb('073_gbb_no_size.dts')
Simon Glassc1ae83c2018-07-17 13:25:44 -06001919 self.assertIn("Node '/binman/gbb': GBB must have a fixed size",
1920 str(e.exception))
1921
Simon Glass66152ce2022-01-09 20:14:09 -07001922 def testGbbMissing(self):
1923 """Test that binman still produces an image if futility is missing"""
1924 entry_args = {
1925 'keydir': 'devkeys',
1926 }
Simon Glass14d64e32025-04-29 07:21:59 -06001927 with terminal.capture() as (_, stderr):
Simon Glass66152ce2022-01-09 20:14:09 -07001928 self._DoTestFile('071_gbb.dts', force_missing_bintools='futility',
1929 entry_args=entry_args)
1930 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07001931 self.assertRegex(err, "Image 'image'.*missing bintools.*: futility")
Simon Glass66152ce2022-01-09 20:14:09 -07001932
Simon Glass5c350162018-07-17 13:25:47 -06001933 def _HandleVblockCommand(self, pipe_list):
Simon Glass220c6222021-01-06 21:35:17 -07001934 """Fake calls to the futility utility
1935
1936 The expected pipe is:
1937
1938 [('futility', 'vbutil_firmware', '--vblock',
1939 'vblock.vblock', '--keyblock', 'devkeys/firmware.keyblock',
1940 '--signprivate', 'devkeys/firmware_data_key.vbprivk',
1941 '--version', '1', '--fv', 'input.vblock', '--kernelkey',
1942 'devkeys/kernel_subkey.vbpubk', '--flags', '1')]
1943
1944 This writes to the output file (here, 'vblock.vblock'). If
1945 self._hash_data is False, it writes VBLOCK_DATA, else it writes a hash
1946 of the input data (here, 'input.vblock').
1947 """
Simon Glass9a1c7262023-02-22 12:14:49 -07001948 if 'futility' in pipe_list[0][0]:
Simon Glass5c350162018-07-17 13:25:47 -06001949 fname = pipe_list[0][3]
Simon Glass639505b2018-09-14 04:57:11 -06001950 with open(fname, 'wb') as fd:
Simon Glass220c6222021-01-06 21:35:17 -07001951 if self._hash_data:
1952 infile = pipe_list[0][11]
1953 m = hashlib.sha256()
Simon Glass80025522022-01-29 14:14:04 -07001954 data = tools.read_file(infile)
Simon Glass220c6222021-01-06 21:35:17 -07001955 m.update(data)
1956 fd.write(m.digest())
1957 else:
1958 fd.write(VBLOCK_DATA)
1959
Simon Glass5c350162018-07-17 13:25:47 -06001960 return command.CommandResult()
1961
1962 def testVblock(self):
1963 """Test for the Chromium OS Verified Boot Block"""
Simon Glass220c6222021-01-06 21:35:17 -07001964 self._hash_data = False
Simon Glass5dc22cf2025-02-03 09:26:42 -07001965 command.TEST_RESULT = self._HandleVblockCommand
Simon Glass5c350162018-07-17 13:25:47 -06001966 entry_args = {
1967 'keydir': 'devkeys',
1968 }
Simon Glass511f6582018-10-01 12:22:30 -06001969 data, _, _, _ = self._DoReadFileDtb('074_vblock.dts',
Simon Glass5c350162018-07-17 13:25:47 -06001970 entry_args=entry_args)
1971 expected = U_BOOT_DATA + VBLOCK_DATA + U_BOOT_DTB_DATA
1972 self.assertEqual(expected, data)
1973
1974 def testVblockNoContent(self):
1975 """Test we detect a vblock which has no content to sign"""
1976 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001977 self._DoReadFile('075_vblock_no_content.dts')
Simon Glasse1915782021-03-21 18:24:31 +13001978 self.assertIn("Node '/binman/vblock': Collection must have a 'content' "
Simon Glass5c350162018-07-17 13:25:47 -06001979 'property', str(e.exception))
1980
1981 def testVblockBadPhandle(self):
1982 """Test that we detect a vblock with an invalid phandle in contents"""
1983 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001984 self._DoReadFile('076_vblock_bad_phandle.dts')
Simon Glass5c350162018-07-17 13:25:47 -06001985 self.assertIn("Node '/binman/vblock': Cannot find node for phandle "
1986 '1000', str(e.exception))
1987
1988 def testVblockBadEntry(self):
1989 """Test that we detect an entry that points to a non-entry"""
1990 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001991 self._DoReadFile('077_vblock_bad_entry.dts')
Simon Glass5c350162018-07-17 13:25:47 -06001992 self.assertIn("Node '/binman/vblock': Cannot find entry for node "
1993 "'other'", str(e.exception))
1994
Simon Glass220c6222021-01-06 21:35:17 -07001995 def testVblockContent(self):
1996 """Test that the vblock signs the right data"""
1997 self._hash_data = True
Simon Glass5dc22cf2025-02-03 09:26:42 -07001998 command.TEST_RESULT = self._HandleVblockCommand
Simon Glass220c6222021-01-06 21:35:17 -07001999 entry_args = {
2000 'keydir': 'devkeys',
2001 }
2002 data = self._DoReadFileDtb(
2003 '189_vblock_content.dts', use_real_dtb=True, update_dtb=True,
2004 entry_args=entry_args)[0]
2005 hashlen = 32 # SHA256 hash is 32 bytes
2006 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
2007 hashval = data[-hashlen:]
2008 dtb = data[len(U_BOOT_DATA):-hashlen]
2009
2010 expected_data = U_BOOT_DATA + dtb
2011
2012 # The hashval should be a hash of the dtb
2013 m = hashlib.sha256()
2014 m.update(expected_data)
2015 expected_hashval = m.digest()
2016 self.assertEqual(expected_hashval, hashval)
2017
Simon Glass66152ce2022-01-09 20:14:09 -07002018 def testVblockMissing(self):
2019 """Test that binman still produces an image if futility is missing"""
2020 entry_args = {
2021 'keydir': 'devkeys',
2022 }
Simon Glass14d64e32025-04-29 07:21:59 -06002023 with terminal.capture() as (_, stderr):
Simon Glass66152ce2022-01-09 20:14:09 -07002024 self._DoTestFile('074_vblock.dts',
2025 force_missing_bintools='futility',
2026 entry_args=entry_args)
2027 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07002028 self.assertRegex(err, "Image 'image'.*missing bintools.*: futility")
Simon Glass66152ce2022-01-09 20:14:09 -07002029
Simon Glass8425a1f2018-07-17 13:25:48 -06002030 def testTpl(self):
Simon Glass3eb5b202019-08-24 07:23:00 -06002031 """Test that an image with TPL and its device tree can be created"""
Simon Glass8425a1f2018-07-17 13:25:48 -06002032 # ELF file with a '__bss_size' symbol
Simon Glass3eb5b202019-08-24 07:23:00 -06002033 self._SetupTplElf()
Simon Glass511f6582018-10-01 12:22:30 -06002034 data = self._DoReadFile('078_u_boot_tpl.dts')
Simon Glass8425a1f2018-07-17 13:25:48 -06002035 self.assertEqual(U_BOOT_TPL_DATA + U_BOOT_TPL_DTB_DATA, data)
2036
Simon Glass24b97442018-07-17 13:25:51 -06002037 def testUsesPos(self):
2038 """Test that the 'pos' property cannot be used anymore"""
2039 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002040 data = self._DoReadFile('079_uses_pos.dts')
Simon Glass24b97442018-07-17 13:25:51 -06002041 self.assertIn("Node '/binman/u-boot': Please use 'offset' instead of "
2042 "'pos'", str(e.exception))
2043
Simon Glass274bf092018-09-14 04:57:08 -06002044 def testFillZero(self):
2045 """Test for an fill entry type with a size of 0"""
Simon Glass511f6582018-10-01 12:22:30 -06002046 data = self._DoReadFile('080_fill_empty.dts')
Simon Glass80025522022-01-29 14:14:04 -07002047 self.assertEqual(tools.get_bytes(0, 16), data)
Simon Glass274bf092018-09-14 04:57:08 -06002048
Simon Glass267de432018-09-14 04:57:09 -06002049 def testTextMissing(self):
2050 """Test for a text entry type where there is no text"""
2051 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002052 self._DoReadFileDtb('066_text.dts',)
Simon Glass267de432018-09-14 04:57:09 -06002053 self.assertIn("Node '/binman/text': No value provided for text label "
2054 "'test-id'", str(e.exception))
2055
Simon Glassed40e962018-09-14 04:57:10 -06002056 def testPackStart16Tpl(self):
2057 """Test that an image with an x86 start16 TPL region can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06002058 data = self._DoReadFile('081_x86_start16_tpl.dts')
Simon Glassed40e962018-09-14 04:57:10 -06002059 self.assertEqual(X86_START16_TPL_DATA, data[:len(X86_START16_TPL_DATA)])
2060
Simon Glass3b376c32018-09-14 04:57:12 -06002061 def testSelectImage(self):
2062 """Test that we can select which images to build"""
Simon Glassb4595d82019-04-25 21:58:34 -06002063 expected = 'Skipping images: image1'
2064
2065 # We should only get the expected message in verbose mode
Simon Glass8a50b4a2019-07-08 13:18:48 -06002066 for verbosity in (0, 2):
Simon Glass14d64e32025-04-29 07:21:59 -06002067 with terminal.capture() as (stdout, stderr):
Simon Glassb4595d82019-04-25 21:58:34 -06002068 retcode = self._DoTestFile('006_dual_image.dts',
2069 verbosity=verbosity,
2070 images=['image2'])
2071 self.assertEqual(0, retcode)
2072 if verbosity:
2073 self.assertIn(expected, stdout.getvalue())
2074 else:
2075 self.assertNotIn(expected, stdout.getvalue())
Simon Glass3b376c32018-09-14 04:57:12 -06002076
Simon Glass80025522022-01-29 14:14:04 -07002077 self.assertFalse(os.path.exists(tools.get_output_filename('image1.bin')))
2078 self.assertTrue(os.path.exists(tools.get_output_filename('image2.bin')))
Simon Glassb3d6fc72019-07-20 12:24:10 -06002079 self._CleanupOutputDir()
Simon Glass3b376c32018-09-14 04:57:12 -06002080
Simon Glasse219aa42018-09-14 04:57:24 -06002081 def testUpdateFdtAll(self):
2082 """Test that all device trees are updated with offset/size info"""
Marek Vasutf7413f02023-07-18 07:23:58 -06002083 self._SetupSplElf()
2084 self._SetupTplElf()
Simon Glass5b4bce32019-07-08 14:25:26 -06002085 data = self._DoReadFileRealDtb('082_fdt_update_all.dts')
Simon Glasse219aa42018-09-14 04:57:24 -06002086
2087 base_expected = {
Simon Glasse219aa42018-09-14 04:57:24 -06002088 'offset': 0,
Simon Glass56d05412022-02-28 07:16:54 -07002089 'image-pos': 0,
2090 'size': 2320,
Simon Glasse219aa42018-09-14 04:57:24 -06002091 'section:offset': 0,
Simon Glass56d05412022-02-28 07:16:54 -07002092 'section:image-pos': 0,
2093 'section:size': 565,
2094 'section/u-boot-dtb:offset': 0,
2095 'section/u-boot-dtb:image-pos': 0,
2096 'section/u-boot-dtb:size': 565,
2097 'u-boot-spl-dtb:offset': 565,
2098 'u-boot-spl-dtb:image-pos': 565,
2099 'u-boot-spl-dtb:size': 585,
2100 'u-boot-tpl-dtb:offset': 1150,
2101 'u-boot-tpl-dtb:image-pos': 1150,
2102 'u-boot-tpl-dtb:size': 585,
2103 'u-boot-vpl-dtb:image-pos': 1735,
2104 'u-boot-vpl-dtb:offset': 1735,
2105 'u-boot-vpl-dtb:size': 585,
Simon Glasse219aa42018-09-14 04:57:24 -06002106 }
2107
2108 # We expect three device-tree files in the output, one after the other.
2109 # Read them in sequence. We look for an 'spl' property in the SPL tree,
2110 # and 'tpl' in the TPL tree, to make sure they are distinct from the
2111 # main U-Boot tree. All three should have the same postions and offset.
2112 start = 0
Simon Glass56d05412022-02-28 07:16:54 -07002113 self.maxDiff = None
2114 for item in ['', 'spl', 'tpl', 'vpl']:
Simon Glasse219aa42018-09-14 04:57:24 -06002115 dtb = fdt.Fdt.FromData(data[start:])
2116 dtb.Scan()
Simon Glassfb30e292019-07-20 12:23:51 -06002117 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS +
Simon Glass56d05412022-02-28 07:16:54 -07002118 ['spl', 'tpl', 'vpl'])
Simon Glasse219aa42018-09-14 04:57:24 -06002119 expected = dict(base_expected)
2120 if item:
2121 expected[item] = 0
2122 self.assertEqual(expected, props)
2123 start += dtb._fdt_obj.totalsize()
2124
2125 def testUpdateFdtOutput(self):
2126 """Test that output DTB files are updated"""
2127 try:
Simon Glass511f6582018-10-01 12:22:30 -06002128 data, dtb_data, _, _ = self._DoReadFileDtb('082_fdt_update_all.dts',
Simon Glasse219aa42018-09-14 04:57:24 -06002129 use_real_dtb=True, update_dtb=True, reset_dtbs=False)
2130
2131 # Unfortunately, compiling a source file always results in a file
2132 # called source.dtb (see fdt_util.EnsureCompiled()). The test
Simon Glass511f6582018-10-01 12:22:30 -06002133 # source file (e.g. test/075_fdt_update_all.dts) thus does not enter
Simon Glasse219aa42018-09-14 04:57:24 -06002134 # binman as a file called u-boot.dtb. To fix this, copy the file
2135 # over to the expected place.
Simon Glasse219aa42018-09-14 04:57:24 -06002136 start = 0
2137 for fname in ['u-boot.dtb.out', 'spl/u-boot-spl.dtb.out',
Simon Glass56d05412022-02-28 07:16:54 -07002138 'tpl/u-boot-tpl.dtb.out', 'vpl/u-boot-vpl.dtb.out']:
Simon Glasse219aa42018-09-14 04:57:24 -06002139 dtb = fdt.Fdt.FromData(data[start:])
2140 size = dtb._fdt_obj.totalsize()
Simon Glass80025522022-01-29 14:14:04 -07002141 pathname = tools.get_output_filename(os.path.split(fname)[1])
2142 outdata = tools.read_file(pathname)
Simon Glasse219aa42018-09-14 04:57:24 -06002143 name = os.path.split(fname)[0]
2144
2145 if name:
Simon Glass56d05412022-02-28 07:16:54 -07002146 orig_indata = self._GetDtbContentsForSpls(dtb_data, name)
Simon Glasse219aa42018-09-14 04:57:24 -06002147 else:
2148 orig_indata = dtb_data
2149 self.assertNotEqual(outdata, orig_indata,
2150 "Expected output file '%s' be updated" % pathname)
2151 self.assertEqual(outdata, data[start:start + size],
2152 "Expected output file '%s' to match output image" %
2153 pathname)
2154 start += size
2155 finally:
2156 self._ResetDtbs()
2157
Simon Glass7ba33592018-09-14 04:57:26 -06002158 def _decompress(self, data):
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +02002159 bintool = self.comp_bintools['lz4']
2160 return bintool.decompress(data)
Simon Glass7ba33592018-09-14 04:57:26 -06002161
2162 def testCompress(self):
2163 """Test compression of blobs"""
Simon Glass1de34482019-07-08 13:18:53 -06002164 self._CheckLz4()
Simon Glass511f6582018-10-01 12:22:30 -06002165 data, _, _, out_dtb_fname = self._DoReadFileDtb('083_compress.dts',
Simon Glass7ba33592018-09-14 04:57:26 -06002166 use_real_dtb=True, update_dtb=True)
2167 dtb = fdt.Fdt(out_dtb_fname)
2168 dtb.Scan()
2169 props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
2170 orig = self._decompress(data)
Brandon Maiera657bc62024-06-04 16:16:05 +00002171 self.assertEqual(COMPRESS_DATA, orig)
Simon Glass789b34402020-10-26 17:40:15 -06002172
2173 # Do a sanity check on various fields
2174 image = control.images['image']
2175 entries = image.GetEntries()
2176 self.assertEqual(1, len(entries))
2177
2178 entry = entries['blob']
2179 self.assertEqual(COMPRESS_DATA, entry.uncomp_data)
2180 self.assertEqual(len(COMPRESS_DATA), entry.uncomp_size)
2181 orig = self._decompress(entry.data)
2182 self.assertEqual(orig, entry.uncomp_data)
2183
Simon Glass72eeff12020-10-26 17:40:16 -06002184 self.assertEqual(image.data, entry.data)
2185
Simon Glass7ba33592018-09-14 04:57:26 -06002186 expected = {
2187 'blob:uncomp-size': len(COMPRESS_DATA),
2188 'blob:size': len(data),
2189 'size': len(data),
2190 }
2191 self.assertEqual(expected, props)
2192
Simon Glassac6328c2018-09-14 04:57:28 -06002193 def testFiles(self):
2194 """Test bringing in multiple files"""
Simon Glass511f6582018-10-01 12:22:30 -06002195 data = self._DoReadFile('084_files.dts')
Simon Glassac6328c2018-09-14 04:57:28 -06002196 self.assertEqual(FILES_DATA, data)
2197
2198 def testFilesCompress(self):
2199 """Test bringing in multiple files and compressing them"""
Simon Glass1de34482019-07-08 13:18:53 -06002200 self._CheckLz4()
Simon Glass511f6582018-10-01 12:22:30 -06002201 data = self._DoReadFile('085_files_compress.dts')
Simon Glassac6328c2018-09-14 04:57:28 -06002202
2203 image = control.images['image']
2204 entries = image.GetEntries()
2205 files = entries['files']
Simon Glass39dd2152019-07-08 14:25:47 -06002206 entries = files._entries
Simon Glassac6328c2018-09-14 04:57:28 -06002207
Simon Glass303f62f2019-05-17 22:00:46 -06002208 orig = b''
Simon Glassac6328c2018-09-14 04:57:28 -06002209 for i in range(1, 3):
2210 key = '%d.dat' % i
2211 start = entries[key].image_pos
2212 len = entries[key].size
2213 chunk = data[start:start + len]
2214 orig += self._decompress(chunk)
2215
2216 self.assertEqual(FILES_DATA, orig)
2217
2218 def testFilesMissing(self):
2219 """Test missing files"""
2220 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002221 data = self._DoReadFile('086_files_none.dts')
Simon Glassac6328c2018-09-14 04:57:28 -06002222 self.assertIn("Node '/binman/files': Pattern \'files/*.none\' matched "
2223 'no files', str(e.exception))
2224
2225 def testFilesNoPattern(self):
2226 """Test missing files"""
2227 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002228 data = self._DoReadFile('087_files_no_pattern.dts')
Simon Glassac6328c2018-09-14 04:57:28 -06002229 self.assertIn("Node '/binman/files': Missing 'pattern' property",
2230 str(e.exception))
2231
Simon Glassdd156a42022-03-05 20:18:59 -07002232 def testExtendSize(self):
2233 """Test an extending entry"""
2234 data, _, map_data, _ = self._DoReadFileDtb('088_extend_size.dts',
Simon Glassfa79a812018-09-14 04:57:29 -06002235 map=True)
Simon Glass80025522022-01-29 14:14:04 -07002236 expect = (tools.get_bytes(ord('a'), 8) + U_BOOT_DATA +
2237 MRC_DATA + tools.get_bytes(ord('b'), 1) + U_BOOT_DATA +
2238 tools.get_bytes(ord('c'), 8) + U_BOOT_DATA +
2239 tools.get_bytes(ord('d'), 8))
Simon Glassfa79a812018-09-14 04:57:29 -06002240 self.assertEqual(expect, data)
2241 self.assertEqual('''ImagePos Offset Size Name
Simon Glass49cd2b32023-02-07 14:34:18 -0700224200000000 00000000 00000028 image
Simon Glassfa79a812018-09-14 04:57:29 -0600224300000000 00000000 00000008 fill
224400000008 00000008 00000004 u-boot
22450000000c 0000000c 00000004 section
22460000000c 00000000 00000003 intel-mrc
224700000010 00000010 00000004 u-boot2
224800000014 00000014 0000000c section2
224900000014 00000000 00000008 fill
22500000001c 00000008 00000004 u-boot
225100000020 00000020 00000008 fill2
2252''', map_data)
2253
Simon Glassdd156a42022-03-05 20:18:59 -07002254 def testExtendSizeBad(self):
2255 """Test an extending entry which fails to provide contents"""
Simon Glass14d64e32025-04-29 07:21:59 -06002256 with terminal.capture() as (stdout, stderr):
Simon Glasscd817d52018-09-14 04:57:36 -06002257 with self.assertRaises(ValueError) as e:
Simon Glassdd156a42022-03-05 20:18:59 -07002258 self._DoReadFileDtb('089_extend_size_bad.dts', map=True)
Simon Glassfa79a812018-09-14 04:57:29 -06002259 self.assertIn("Node '/binman/_testing': Cannot obtain contents when "
2260 'expanding entry', str(e.exception))
2261
Simon Glassae7cf032018-09-14 04:57:31 -06002262 def testHash(self):
2263 """Test hashing of the contents of an entry"""
Simon Glass511f6582018-10-01 12:22:30 -06002264 _, _, _, out_dtb_fname = self._DoReadFileDtb('090_hash.dts',
Simon Glassae7cf032018-09-14 04:57:31 -06002265 use_real_dtb=True, update_dtb=True)
2266 dtb = fdt.Fdt(out_dtb_fname)
2267 dtb.Scan()
2268 hash_node = dtb.GetNode('/binman/u-boot/hash').props['value']
2269 m = hashlib.sha256()
2270 m.update(U_BOOT_DATA)
Simon Glass303f62f2019-05-17 22:00:46 -06002271 self.assertEqual(m.digest(), b''.join(hash_node.value))
Simon Glassae7cf032018-09-14 04:57:31 -06002272
2273 def testHashNoAlgo(self):
2274 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002275 self._DoReadFileDtb('091_hash_no_algo.dts', update_dtb=True)
Simon Glassae7cf032018-09-14 04:57:31 -06002276 self.assertIn("Node \'/binman/u-boot\': Missing \'algo\' property for "
2277 'hash node', str(e.exception))
2278
2279 def testHashBadAlgo(self):
2280 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002281 self._DoReadFileDtb('092_hash_bad_algo.dts', update_dtb=True)
Simon Glass64af7c22022-02-08 10:59:44 -07002282 self.assertIn("Node '/binman/u-boot': Unknown hash algorithm 'invalid'",
Simon Glassae7cf032018-09-14 04:57:31 -06002283 str(e.exception))
2284
2285 def testHashSection(self):
2286 """Test hashing of the contents of an entry"""
Simon Glass511f6582018-10-01 12:22:30 -06002287 _, _, _, out_dtb_fname = self._DoReadFileDtb('099_hash_section.dts',
Simon Glassae7cf032018-09-14 04:57:31 -06002288 use_real_dtb=True, update_dtb=True)
2289 dtb = fdt.Fdt(out_dtb_fname)
2290 dtb.Scan()
2291 hash_node = dtb.GetNode('/binman/section/hash').props['value']
2292 m = hashlib.sha256()
2293 m.update(U_BOOT_DATA)
Simon Glass80025522022-01-29 14:14:04 -07002294 m.update(tools.get_bytes(ord('a'), 16))
Simon Glass303f62f2019-05-17 22:00:46 -06002295 self.assertEqual(m.digest(), b''.join(hash_node.value))
Simon Glassae7cf032018-09-14 04:57:31 -06002296
Simon Glass3fb4f422018-09-14 04:57:32 -06002297 def testPackUBootTplMicrocode(self):
2298 """Test that x86 microcode can be handled correctly in TPL
2299
2300 We expect to see the following in the image, in order:
2301 u-boot-tpl-nodtb.bin with a microcode pointer inserted at the correct
2302 place
2303 u-boot-tpl.dtb with the microcode removed
2304 the microcode
2305 """
Simon Glass3eb5b202019-08-24 07:23:00 -06002306 self._SetupTplElf('u_boot_ucode_ptr')
Simon Glass511f6582018-10-01 12:22:30 -06002307 first, pos_and_size = self._RunMicrocodeTest('093_x86_tpl_ucode.dts',
Simon Glass3fb4f422018-09-14 04:57:32 -06002308 U_BOOT_TPL_NODTB_DATA)
Simon Glass303f62f2019-05-17 22:00:46 -06002309 self.assertEqual(b'tplnodtb with microc' + pos_and_size +
2310 b'ter somewhere in here', first)
Simon Glass3fb4f422018-09-14 04:57:32 -06002311
Simon Glassc64aea52018-09-14 04:57:34 -06002312 def testFmapX86(self):
2313 """Basic test of generation of a flashrom fmap"""
Simon Glass511f6582018-10-01 12:22:30 -06002314 data = self._DoReadFile('094_fmap_x86.dts')
Simon Glassc64aea52018-09-14 04:57:34 -06002315 fhdr, fentries = fmap_util.DecodeFmap(data[32:])
Simon Glass80025522022-01-29 14:14:04 -07002316 expected = U_BOOT_DATA + MRC_DATA + tools.get_bytes(ord('a'), 32 - 7)
Simon Glassc64aea52018-09-14 04:57:34 -06002317 self.assertEqual(expected, data[:32])
2318 fhdr, fentries = fmap_util.DecodeFmap(data[32:])
2319
2320 self.assertEqual(0x100, fhdr.image_size)
Simon Glassed836ac2025-02-26 09:26:17 -07002321 base = (1 << 32) - 0x100
Simon Glassc64aea52018-09-14 04:57:34 -06002322
Simon Glassed836ac2025-02-26 09:26:17 -07002323 self.assertEqual(base, fentries[0].offset)
Simon Glassc64aea52018-09-14 04:57:34 -06002324 self.assertEqual(4, fentries[0].size)
Simon Glass303f62f2019-05-17 22:00:46 -06002325 self.assertEqual(b'U_BOOT', fentries[0].name)
Simon Glassc64aea52018-09-14 04:57:34 -06002326
Simon Glassed836ac2025-02-26 09:26:17 -07002327 self.assertEqual(base + 4, fentries[1].offset)
Simon Glassc64aea52018-09-14 04:57:34 -06002328 self.assertEqual(3, fentries[1].size)
Simon Glass303f62f2019-05-17 22:00:46 -06002329 self.assertEqual(b'INTEL_MRC', fentries[1].name)
Simon Glassc64aea52018-09-14 04:57:34 -06002330
Simon Glassed836ac2025-02-26 09:26:17 -07002331 self.assertEqual(base + 32, fentries[2].offset)
Simon Glassc64aea52018-09-14 04:57:34 -06002332 self.assertEqual(fmap_util.FMAP_HEADER_LEN +
2333 fmap_util.FMAP_AREA_LEN * 3, fentries[2].size)
Simon Glass303f62f2019-05-17 22:00:46 -06002334 self.assertEqual(b'FMAP', fentries[2].name)
Simon Glassc64aea52018-09-14 04:57:34 -06002335
2336 def testFmapX86Section(self):
2337 """Basic test of generation of a flashrom fmap"""
Simon Glass511f6582018-10-01 12:22:30 -06002338 data = self._DoReadFile('095_fmap_x86_section.dts')
Simon Glass80025522022-01-29 14:14:04 -07002339 expected = U_BOOT_DATA + MRC_DATA + tools.get_bytes(ord('b'), 32 - 7)
Simon Glassc64aea52018-09-14 04:57:34 -06002340 self.assertEqual(expected, data[:32])
2341 fhdr, fentries = fmap_util.DecodeFmap(data[36:])
2342
Simon Glassb1d414c2021-04-03 11:05:10 +13002343 self.assertEqual(0x180, fhdr.image_size)
Simon Glassed836ac2025-02-26 09:26:17 -07002344 base = (1 << 32) - 0x180
Simon Glassb1d414c2021-04-03 11:05:10 +13002345 expect_size = fmap_util.FMAP_HEADER_LEN + fmap_util.FMAP_AREA_LEN * 4
Simon Glass82059c22021-04-03 11:05:09 +13002346 fiter = iter(fentries)
Simon Glassc64aea52018-09-14 04:57:34 -06002347
Simon Glass82059c22021-04-03 11:05:09 +13002348 fentry = next(fiter)
2349 self.assertEqual(b'U_BOOT', fentry.name)
Simon Glassed836ac2025-02-26 09:26:17 -07002350 self.assertEqual(base, fentry.offset)
Simon Glass82059c22021-04-03 11:05:09 +13002351 self.assertEqual(4, fentry.size)
Simon Glassc64aea52018-09-14 04:57:34 -06002352
Simon Glass82059c22021-04-03 11:05:09 +13002353 fentry = next(fiter)
Simon Glassb1d414c2021-04-03 11:05:10 +13002354 self.assertEqual(b'SECTION', fentry.name)
Simon Glassed836ac2025-02-26 09:26:17 -07002355 self.assertEqual(base + 4, fentry.offset)
Simon Glassb1d414c2021-04-03 11:05:10 +13002356 self.assertEqual(0x20 + expect_size, fentry.size)
2357
2358 fentry = next(fiter)
Simon Glass82059c22021-04-03 11:05:09 +13002359 self.assertEqual(b'INTEL_MRC', fentry.name)
Simon Glassed836ac2025-02-26 09:26:17 -07002360 self.assertEqual(base + 4, fentry.offset)
Simon Glass82059c22021-04-03 11:05:09 +13002361 self.assertEqual(3, fentry.size)
Simon Glassc64aea52018-09-14 04:57:34 -06002362
Simon Glass82059c22021-04-03 11:05:09 +13002363 fentry = next(fiter)
2364 self.assertEqual(b'FMAP', fentry.name)
Simon Glassed836ac2025-02-26 09:26:17 -07002365 self.assertEqual(base + 36, fentry.offset)
Simon Glass82059c22021-04-03 11:05:09 +13002366 self.assertEqual(expect_size, fentry.size)
Simon Glassc64aea52018-09-14 04:57:34 -06002367
Simon Glassb1714232018-09-14 04:57:35 -06002368 def testElf(self):
2369 """Basic test of ELF entries"""
Simon Glass7057d022018-10-01 21:12:47 -06002370 self._SetupSplElf()
Simon Glass3eb5b202019-08-24 07:23:00 -06002371 self._SetupTplElf()
Simon Glassf6290892019-08-24 07:22:53 -06002372 with open(self.ElfTestFile('bss_data'), 'rb') as fd:
Simon Glassb1714232018-09-14 04:57:35 -06002373 TestFunctional._MakeInputFile('-boot', fd.read())
Simon Glass511f6582018-10-01 12:22:30 -06002374 data = self._DoReadFile('096_elf.dts')
Simon Glassb1714232018-09-14 04:57:35 -06002375
Simon Glass0d673792019-07-08 13:18:25 -06002376 def testElfStrip(self):
Simon Glassb1714232018-09-14 04:57:35 -06002377 """Basic test of ELF entries"""
Simon Glass7057d022018-10-01 21:12:47 -06002378 self._SetupSplElf()
Simon Glassf6290892019-08-24 07:22:53 -06002379 with open(self.ElfTestFile('bss_data'), 'rb') as fd:
Simon Glassb1714232018-09-14 04:57:35 -06002380 TestFunctional._MakeInputFile('-boot', fd.read())
Simon Glass511f6582018-10-01 12:22:30 -06002381 data = self._DoReadFile('097_elf_strip.dts')
Simon Glassb1714232018-09-14 04:57:35 -06002382
Simon Glasscd817d52018-09-14 04:57:36 -06002383 def testPackOverlapMap(self):
2384 """Test that overlapping regions are detected"""
Simon Glass14d64e32025-04-29 07:21:59 -06002385 with terminal.capture() as (stdout, stderr):
Simon Glasscd817d52018-09-14 04:57:36 -06002386 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002387 self._DoTestFile('014_pack_overlap.dts', map=True)
Simon Glass80025522022-01-29 14:14:04 -07002388 map_fname = tools.get_output_filename('image.map')
Simon Glasscd817d52018-09-14 04:57:36 -06002389 self.assertEqual("Wrote map file '%s' to show errors\n" % map_fname,
2390 stdout.getvalue())
2391
2392 # We should not get an inmage, but there should be a map file
Simon Glass80025522022-01-29 14:14:04 -07002393 self.assertFalse(os.path.exists(tools.get_output_filename('image.bin')))
Simon Glasscd817d52018-09-14 04:57:36 -06002394 self.assertTrue(os.path.exists(map_fname))
Simon Glass80025522022-01-29 14:14:04 -07002395 map_data = tools.read_file(map_fname, binary=False)
Simon Glasscd817d52018-09-14 04:57:36 -06002396 self.assertEqual('''ImagePos Offset Size Name
Simon Glass49cd2b32023-02-07 14:34:18 -07002397<none> 00000000 00000008 image
Simon Glasscd817d52018-09-14 04:57:36 -06002398<none> 00000000 00000004 u-boot
2399<none> 00000003 00000004 u-boot-align
2400''', map_data)
2401
Simon Glass0d673792019-07-08 13:18:25 -06002402 def testPackRefCode(self):
Simon Glass41902e42018-10-01 12:22:31 -06002403 """Test that an image with an Intel Reference code binary works"""
2404 data = self._DoReadFile('100_intel_refcode.dts')
2405 self.assertEqual(REFCODE_DATA, data[:len(REFCODE_DATA)])
2406
Simon Glasseb023b32019-04-25 21:58:39 -06002407 def testSectionOffset(self):
2408 """Tests use of a section with an offset"""
2409 data, _, map_data, _ = self._DoReadFileDtb('101_sections_offset.dts',
2410 map=True)
2411 self.assertEqual('''ImagePos Offset Size Name
Simon Glass49cd2b32023-02-07 14:34:18 -0700241200000000 00000000 00000038 image
Simon Glasseb023b32019-04-25 21:58:39 -0600241300000004 00000004 00000010 section@0
241400000004 00000000 00000004 u-boot
241500000018 00000018 00000010 section@1
241600000018 00000000 00000004 u-boot
24170000002c 0000002c 00000004 section@2
24180000002c 00000000 00000004 u-boot
2419''', map_data)
2420 self.assertEqual(data,
Simon Glass80025522022-01-29 14:14:04 -07002421 tools.get_bytes(0x26, 4) + U_BOOT_DATA +
2422 tools.get_bytes(0x21, 12) +
2423 tools.get_bytes(0x26, 4) + U_BOOT_DATA +
2424 tools.get_bytes(0x61, 12) +
2425 tools.get_bytes(0x26, 4) + U_BOOT_DATA +
2426 tools.get_bytes(0x26, 8))
Simon Glasseb023b32019-04-25 21:58:39 -06002427
Simon Glass1de34482019-07-08 13:18:53 -06002428 def testCbfsRaw(self):
2429 """Test base handling of a Coreboot Filesystem (CBFS)
2430
2431 The exact contents of the CBFS is verified by similar tests in
2432 cbfs_util_test.py. The tests here merely check that the files added to
2433 the CBFS can be found in the final image.
2434 """
2435 data = self._DoReadFile('102_cbfs_raw.dts')
2436 size = 0xb0
2437
2438 cbfs = cbfs_util.CbfsReader(data)
2439 self.assertEqual(size, cbfs.rom_size)
2440
2441 self.assertIn('u-boot-dtb', cbfs.files)
2442 cfile = cbfs.files['u-boot-dtb']
2443 self.assertEqual(U_BOOT_DTB_DATA, cfile.data)
2444
2445 def testCbfsArch(self):
2446 """Test on non-x86 architecture"""
2447 data = self._DoReadFile('103_cbfs_raw_ppc.dts')
2448 size = 0x100
2449
2450 cbfs = cbfs_util.CbfsReader(data)
2451 self.assertEqual(size, cbfs.rom_size)
2452
2453 self.assertIn('u-boot-dtb', cbfs.files)
2454 cfile = cbfs.files['u-boot-dtb']
2455 self.assertEqual(U_BOOT_DTB_DATA, cfile.data)
2456
2457 def testCbfsStage(self):
2458 """Tests handling of a Coreboot Filesystem (CBFS)"""
2459 if not elf.ELF_TOOLS:
2460 self.skipTest('Python elftools not available')
2461 elf_fname = os.path.join(self._indir, 'cbfs-stage.elf')
2462 elf.MakeElf(elf_fname, U_BOOT_DATA, U_BOOT_DTB_DATA)
2463 size = 0xb0
2464
2465 data = self._DoReadFile('104_cbfs_stage.dts')
2466 cbfs = cbfs_util.CbfsReader(data)
2467 self.assertEqual(size, cbfs.rom_size)
2468
2469 self.assertIn('u-boot', cbfs.files)
2470 cfile = cbfs.files['u-boot']
2471 self.assertEqual(U_BOOT_DATA + U_BOOT_DTB_DATA, cfile.data)
2472
2473 def testCbfsRawCompress(self):
2474 """Test handling of compressing raw files"""
2475 self._CheckLz4()
2476 data = self._DoReadFile('105_cbfs_raw_compress.dts')
2477 size = 0x140
2478
2479 cbfs = cbfs_util.CbfsReader(data)
2480 self.assertIn('u-boot', cbfs.files)
2481 cfile = cbfs.files['u-boot']
2482 self.assertEqual(COMPRESS_DATA, cfile.data)
2483
2484 def testCbfsBadArch(self):
2485 """Test handling of a bad architecture"""
2486 with self.assertRaises(ValueError) as e:
2487 self._DoReadFile('106_cbfs_bad_arch.dts')
2488 self.assertIn("Invalid architecture 'bad-arch'", str(e.exception))
2489
2490 def testCbfsNoSize(self):
2491 """Test handling of a missing size property"""
2492 with self.assertRaises(ValueError) as e:
2493 self._DoReadFile('107_cbfs_no_size.dts')
2494 self.assertIn('entry must have a size property', str(e.exception))
2495
Simon Glass3e28f4f2021-11-23 11:03:54 -07002496 def testCbfsNoContents(self):
Simon Glass1de34482019-07-08 13:18:53 -06002497 """Test handling of a CBFS entry which does not provide contentsy"""
2498 with self.assertRaises(ValueError) as e:
2499 self._DoReadFile('108_cbfs_no_contents.dts')
2500 self.assertIn('Could not complete processing of contents',
2501 str(e.exception))
2502
2503 def testCbfsBadCompress(self):
2504 """Test handling of a bad architecture"""
2505 with self.assertRaises(ValueError) as e:
2506 self._DoReadFile('109_cbfs_bad_compress.dts')
2507 self.assertIn("Invalid compression in 'u-boot': 'invalid-algo'",
2508 str(e.exception))
2509
2510 def testCbfsNamedEntries(self):
2511 """Test handling of named entries"""
2512 data = self._DoReadFile('110_cbfs_name.dts')
2513
2514 cbfs = cbfs_util.CbfsReader(data)
2515 self.assertIn('FRED', cbfs.files)
2516 cfile1 = cbfs.files['FRED']
2517 self.assertEqual(U_BOOT_DATA, cfile1.data)
2518
2519 self.assertIn('hello', cbfs.files)
2520 cfile2 = cbfs.files['hello']
2521 self.assertEqual(U_BOOT_DTB_DATA, cfile2.data)
2522
Simon Glass759af872019-07-08 13:18:54 -06002523 def _SetupIfwi(self, fname):
2524 """Set up to run an IFWI test
2525
2526 Args:
2527 fname: Filename of input file to provide (fitimage.bin or ifwi.bin)
2528 """
2529 self._SetupSplElf()
Simon Glass3eb5b202019-08-24 07:23:00 -06002530 self._SetupTplElf()
Simon Glass759af872019-07-08 13:18:54 -06002531
2532 # Intel Integrated Firmware Image (IFWI) file
2533 with gzip.open(self.TestFile('%s.gz' % fname), 'rb') as fd:
2534 data = fd.read()
2535 TestFunctional._MakeInputFile(fname,data)
2536
2537 def _CheckIfwi(self, data):
2538 """Check that an image with an IFWI contains the correct output
2539
2540 Args:
2541 data: Conents of output file
2542 """
Simon Glass80025522022-01-29 14:14:04 -07002543 expected_desc = tools.read_file(self.TestFile('descriptor.bin'))
Simon Glass759af872019-07-08 13:18:54 -06002544 if data[:0x1000] != expected_desc:
2545 self.fail('Expected descriptor binary at start of image')
2546
2547 # We expect to find the TPL wil in subpart IBBP entry IBBL
Simon Glass80025522022-01-29 14:14:04 -07002548 image_fname = tools.get_output_filename('image.bin')
2549 tpl_fname = tools.get_output_filename('tpl.out')
Simon Glass57c7a482022-01-09 20:14:01 -07002550 ifwitool = bintool.Bintool.create('ifwitool')
2551 ifwitool.extract(image_fname, 'IBBP', 'IBBL', tpl_fname)
Simon Glass759af872019-07-08 13:18:54 -06002552
Simon Glass80025522022-01-29 14:14:04 -07002553 tpl_data = tools.read_file(tpl_fname)
Simon Glassf55bd692019-08-24 07:22:51 -06002554 self.assertEqual(U_BOOT_TPL_DATA, tpl_data[:len(U_BOOT_TPL_DATA)])
Simon Glass759af872019-07-08 13:18:54 -06002555
2556 def testPackX86RomIfwi(self):
2557 """Test that an x86 ROM with Integrated Firmware Image can be created"""
2558 self._SetupIfwi('fitimage.bin')
Simon Glass1d167762019-08-24 07:23:01 -06002559 data = self._DoReadFile('111_x86_rom_ifwi.dts')
Simon Glass759af872019-07-08 13:18:54 -06002560 self._CheckIfwi(data)
2561
2562 def testPackX86RomIfwiNoDesc(self):
2563 """Test that an x86 ROM with IFWI can be created from an ifwi.bin file"""
2564 self._SetupIfwi('ifwi.bin')
Simon Glass1d167762019-08-24 07:23:01 -06002565 data = self._DoReadFile('112_x86_rom_ifwi_nodesc.dts')
Simon Glass759af872019-07-08 13:18:54 -06002566 self._CheckIfwi(data)
2567
2568 def testPackX86RomIfwiNoData(self):
2569 """Test that an x86 ROM with IFWI handles missing data"""
2570 self._SetupIfwi('ifwi.bin')
2571 with self.assertRaises(ValueError) as e:
Simon Glass1d167762019-08-24 07:23:01 -06002572 data = self._DoReadFile('113_x86_rom_ifwi_nodata.dts')
Simon Glass759af872019-07-08 13:18:54 -06002573 self.assertIn('Could not complete processing of contents',
2574 str(e.exception))
Simon Glass91710b32018-07-17 13:25:32 -06002575
Simon Glass66152ce2022-01-09 20:14:09 -07002576 def testIfwiMissing(self):
2577 """Test that binman still produces an image if ifwitool is missing"""
2578 self._SetupIfwi('fitimage.bin')
Simon Glass14d64e32025-04-29 07:21:59 -06002579 with terminal.capture() as (_, stderr):
Simon Glass66152ce2022-01-09 20:14:09 -07002580 self._DoTestFile('111_x86_rom_ifwi.dts',
2581 force_missing_bintools='ifwitool')
2582 err = stderr.getvalue()
2583 self.assertRegex(err,
Simon Glass49cd2b32023-02-07 14:34:18 -07002584 "Image 'image'.*missing bintools.*: ifwitool")
Simon Glass66152ce2022-01-09 20:14:09 -07002585
Simon Glassc2f1aed2019-07-08 13:18:56 -06002586 def testCbfsOffset(self):
2587 """Test a CBFS with files at particular offsets
2588
2589 Like all CFBS tests, this is just checking the logic that calls
2590 cbfs_util. See cbfs_util_test for fully tests (e.g. test_cbfs_offset()).
2591 """
2592 data = self._DoReadFile('114_cbfs_offset.dts')
2593 size = 0x200
2594
2595 cbfs = cbfs_util.CbfsReader(data)
2596 self.assertEqual(size, cbfs.rom_size)
2597
2598 self.assertIn('u-boot', cbfs.files)
2599 cfile = cbfs.files['u-boot']
2600 self.assertEqual(U_BOOT_DATA, cfile.data)
2601 self.assertEqual(0x40, cfile.cbfs_offset)
2602
2603 self.assertIn('u-boot-dtb', cbfs.files)
2604 cfile2 = cbfs.files['u-boot-dtb']
2605 self.assertEqual(U_BOOT_DTB_DATA, cfile2.data)
2606 self.assertEqual(0x140, cfile2.cbfs_offset)
2607
Simon Glass0f621332019-07-08 14:25:27 -06002608 def testFdtmap(self):
2609 """Test an FDT map can be inserted in the image"""
2610 data = self.data = self._DoReadFileRealDtb('115_fdtmap.dts')
2611 fdtmap_data = data[len(U_BOOT_DATA):]
2612 magic = fdtmap_data[:8]
Simon Glassc5fd10a2019-10-31 07:43:03 -06002613 self.assertEqual(b'_FDTMAP_', magic)
Simon Glass80025522022-01-29 14:14:04 -07002614 self.assertEqual(tools.get_bytes(0, 8), fdtmap_data[8:16])
Simon Glass0f621332019-07-08 14:25:27 -06002615
2616 fdt_data = fdtmap_data[16:]
2617 dtb = fdt.Fdt.FromData(fdt_data)
2618 dtb.Scan()
Simon Glass2c6adba2019-07-20 12:23:47 -06002619 props = self._GetPropTree(dtb, BASE_DTB_PROPS, prefix='/')
Simon Glass0f621332019-07-08 14:25:27 -06002620 self.assertEqual({
2621 'image-pos': 0,
2622 'offset': 0,
2623 'u-boot:offset': 0,
2624 'u-boot:size': len(U_BOOT_DATA),
2625 'u-boot:image-pos': 0,
2626 'fdtmap:image-pos': 4,
2627 'fdtmap:offset': 4,
2628 'fdtmap:size': len(fdtmap_data),
2629 'size': len(data),
2630 }, props)
2631
2632 def testFdtmapNoMatch(self):
2633 """Check handling of an FDT map when the section cannot be found"""
2634 self.data = self._DoReadFileRealDtb('115_fdtmap.dts')
2635
2636 # Mangle the section name, which should cause a mismatch between the
2637 # correct FDT path and the one expected by the section
2638 image = control.images['image']
Simon Glasscec34ba2019-07-08 14:25:28 -06002639 image._node.path += '-suffix'
Simon Glass0f621332019-07-08 14:25:27 -06002640 entries = image.GetEntries()
2641 fdtmap = entries['fdtmap']
2642 with self.assertRaises(ValueError) as e:
2643 fdtmap._GetFdtmap()
2644 self.assertIn("Cannot locate node for path '/binman-suffix'",
2645 str(e.exception))
2646
Simon Glasscec34ba2019-07-08 14:25:28 -06002647 def testFdtmapHeader(self):
2648 """Test an FDT map and image header can be inserted in the image"""
2649 data = self.data = self._DoReadFileRealDtb('116_fdtmap_hdr.dts')
2650 fdtmap_pos = len(U_BOOT_DATA)
2651 fdtmap_data = data[fdtmap_pos:]
2652 fdt_data = fdtmap_data[16:]
2653 dtb = fdt.Fdt.FromData(fdt_data)
2654 fdt_size = dtb.GetFdtObj().totalsize()
2655 hdr_data = data[-8:]
Simon Glassc5fd10a2019-10-31 07:43:03 -06002656 self.assertEqual(b'BinM', hdr_data[:4])
Simon Glasscec34ba2019-07-08 14:25:28 -06002657 offset = struct.unpack('<I', hdr_data[4:])[0] & 0xffffffff
2658 self.assertEqual(fdtmap_pos - 0x400, offset - (1 << 32))
2659
2660 def testFdtmapHeaderStart(self):
2661 """Test an image header can be inserted at the image start"""
2662 data = self.data = self._DoReadFileRealDtb('117_fdtmap_hdr_start.dts')
2663 fdtmap_pos = 0x100 + len(U_BOOT_DATA)
2664 hdr_data = data[:8]
Simon Glassc5fd10a2019-10-31 07:43:03 -06002665 self.assertEqual(b'BinM', hdr_data[:4])
Simon Glasscec34ba2019-07-08 14:25:28 -06002666 offset = struct.unpack('<I', hdr_data[4:])[0]
2667 self.assertEqual(fdtmap_pos, offset)
2668
2669 def testFdtmapHeaderPos(self):
2670 """Test an image header can be inserted at a chosen position"""
2671 data = self.data = self._DoReadFileRealDtb('118_fdtmap_hdr_pos.dts')
2672 fdtmap_pos = 0x100 + len(U_BOOT_DATA)
2673 hdr_data = data[0x80:0x88]
Simon Glassc5fd10a2019-10-31 07:43:03 -06002674 self.assertEqual(b'BinM', hdr_data[:4])
Simon Glasscec34ba2019-07-08 14:25:28 -06002675 offset = struct.unpack('<I', hdr_data[4:])[0]
2676 self.assertEqual(fdtmap_pos, offset)
2677
2678 def testHeaderMissingFdtmap(self):
2679 """Test an image header requires an fdtmap"""
2680 with self.assertRaises(ValueError) as e:
2681 self.data = self._DoReadFileRealDtb('119_fdtmap_hdr_missing.dts')
2682 self.assertIn("'image_header' section must have an 'fdtmap' sibling",
2683 str(e.exception))
2684
2685 def testHeaderNoLocation(self):
2686 """Test an image header with a no specified location is detected"""
2687 with self.assertRaises(ValueError) as e:
2688 self.data = self._DoReadFileRealDtb('120_hdr_no_location.dts')
2689 self.assertIn("Invalid location 'None', expected 'start' or 'end'",
2690 str(e.exception))
2691
Simon Glasse61b6f62019-07-08 14:25:37 -06002692 def testEntryExpand(self):
Simon Glassdd156a42022-03-05 20:18:59 -07002693 """Test extending an entry after it is packed"""
2694 data = self._DoReadFile('121_entry_extend.dts')
Simon Glass8c702fb2019-07-20 12:23:57 -06002695 self.assertEqual(b'aaa', data[:3])
2696 self.assertEqual(U_BOOT_DATA, data[3:3 + len(U_BOOT_DATA)])
2697 self.assertEqual(b'aaa', data[-3:])
Simon Glasse61b6f62019-07-08 14:25:37 -06002698
Simon Glassdd156a42022-03-05 20:18:59 -07002699 def testEntryExtendBad(self):
2700 """Test extending an entry after it is packed, twice"""
Simon Glasse61b6f62019-07-08 14:25:37 -06002701 with self.assertRaises(ValueError) as e:
Simon Glassdd156a42022-03-05 20:18:59 -07002702 self._DoReadFile('122_entry_extend_twice.dts')
Simon Glass9d8ee322019-07-20 12:23:58 -06002703 self.assertIn("Image '/binman': Entries changed size after packing",
Simon Glasse61b6f62019-07-08 14:25:37 -06002704 str(e.exception))
2705
Simon Glassdd156a42022-03-05 20:18:59 -07002706 def testEntryExtendSection(self):
2707 """Test extending an entry within a section after it is packed"""
2708 data = self._DoReadFile('123_entry_extend_section.dts')
Simon Glass8c702fb2019-07-20 12:23:57 -06002709 self.assertEqual(b'aaa', data[:3])
2710 self.assertEqual(U_BOOT_DATA, data[3:3 + len(U_BOOT_DATA)])
2711 self.assertEqual(b'aaa', data[-3:])
Simon Glasse61b6f62019-07-08 14:25:37 -06002712
Simon Glass90d29682019-07-08 14:25:38 -06002713 def testCompressDtb(self):
2714 """Test that compress of device-tree files is supported"""
2715 self._CheckLz4()
2716 data = self.data = self._DoReadFileRealDtb('124_compress_dtb.dts')
2717 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
2718 comp_data = data[len(U_BOOT_DATA):]
2719 orig = self._decompress(comp_data)
2720 dtb = fdt.Fdt.FromData(orig)
2721 dtb.Scan()
2722 props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
2723 expected = {
2724 'u-boot:size': len(U_BOOT_DATA),
2725 'u-boot-dtb:uncomp-size': len(orig),
2726 'u-boot-dtb:size': len(comp_data),
2727 'size': len(data),
2728 }
2729 self.assertEqual(expected, props)
2730
Simon Glass151bbbf2019-07-08 14:25:41 -06002731 def testCbfsUpdateFdt(self):
2732 """Test that we can update the device tree with CBFS offset/size info"""
2733 self._CheckLz4()
2734 data, _, _, out_dtb_fname = self._DoReadFileDtb('125_cbfs_update.dts',
2735 update_dtb=True)
2736 dtb = fdt.Fdt(out_dtb_fname)
2737 dtb.Scan()
Simon Glass2c6adba2019-07-20 12:23:47 -06002738 props = self._GetPropTree(dtb, BASE_DTB_PROPS + ['uncomp-size'])
Simon Glass151bbbf2019-07-08 14:25:41 -06002739 del props['cbfs/u-boot:size']
2740 self.assertEqual({
2741 'offset': 0,
2742 'size': len(data),
2743 'image-pos': 0,
2744 'cbfs:offset': 0,
2745 'cbfs:size': len(data),
2746 'cbfs:image-pos': 0,
Simon Glassfa144222023-10-14 14:40:28 -06002747 'cbfs/u-boot:offset': 0x30,
Simon Glass151bbbf2019-07-08 14:25:41 -06002748 'cbfs/u-boot:uncomp-size': len(U_BOOT_DATA),
Simon Glassfa144222023-10-14 14:40:28 -06002749 'cbfs/u-boot:image-pos': 0x30,
2750 'cbfs/u-boot-dtb:offset': 0xa4,
Simon Glass151bbbf2019-07-08 14:25:41 -06002751 'cbfs/u-boot-dtb:size': len(U_BOOT_DATA),
Simon Glassfa144222023-10-14 14:40:28 -06002752 'cbfs/u-boot-dtb:image-pos': 0xa4,
Simon Glass151bbbf2019-07-08 14:25:41 -06002753 }, props)
2754
Simon Glass3c9b4f22019-07-08 14:25:42 -06002755 def testCbfsBadType(self):
2756 """Test an image header with a no specified location is detected"""
2757 with self.assertRaises(ValueError) as e:
2758 self._DoReadFile('126_cbfs_bad_type.dts')
2759 self.assertIn("Unknown cbfs-type 'badtype'", str(e.exception))
2760
Simon Glass6b156f82019-07-08 14:25:43 -06002761 def testList(self):
2762 """Test listing the files in an image"""
2763 self._CheckLz4()
2764 data = self._DoReadFile('127_list.dts')
2765 image = control.images['image']
2766 entries = image.BuildEntryList()
2767 self.assertEqual(7, len(entries))
2768
2769 ent = entries[0]
2770 self.assertEqual(0, ent.indent)
Simon Glass49cd2b32023-02-07 14:34:18 -07002771 self.assertEqual('image', ent.name)
Simon Glass6b156f82019-07-08 14:25:43 -06002772 self.assertEqual('section', ent.etype)
2773 self.assertEqual(len(data), ent.size)
2774 self.assertEqual(0, ent.image_pos)
2775 self.assertEqual(None, ent.uncomp_size)
Simon Glass39dd2152019-07-08 14:25:47 -06002776 self.assertEqual(0, ent.offset)
Simon Glass6b156f82019-07-08 14:25:43 -06002777
2778 ent = entries[1]
2779 self.assertEqual(1, ent.indent)
2780 self.assertEqual('u-boot', ent.name)
2781 self.assertEqual('u-boot', ent.etype)
2782 self.assertEqual(len(U_BOOT_DATA), ent.size)
2783 self.assertEqual(0, ent.image_pos)
2784 self.assertEqual(None, ent.uncomp_size)
2785 self.assertEqual(0, ent.offset)
2786
2787 ent = entries[2]
2788 self.assertEqual(1, ent.indent)
2789 self.assertEqual('section', ent.name)
2790 self.assertEqual('section', ent.etype)
2791 section_size = ent.size
2792 self.assertEqual(0x100, ent.image_pos)
2793 self.assertEqual(None, ent.uncomp_size)
Simon Glass39dd2152019-07-08 14:25:47 -06002794 self.assertEqual(0x100, ent.offset)
Simon Glass6b156f82019-07-08 14:25:43 -06002795
2796 ent = entries[3]
2797 self.assertEqual(2, ent.indent)
2798 self.assertEqual('cbfs', ent.name)
2799 self.assertEqual('cbfs', ent.etype)
2800 self.assertEqual(0x400, ent.size)
2801 self.assertEqual(0x100, ent.image_pos)
2802 self.assertEqual(None, ent.uncomp_size)
2803 self.assertEqual(0, ent.offset)
2804
2805 ent = entries[4]
2806 self.assertEqual(3, ent.indent)
2807 self.assertEqual('u-boot', ent.name)
2808 self.assertEqual('u-boot', ent.etype)
2809 self.assertEqual(len(U_BOOT_DATA), ent.size)
2810 self.assertEqual(0x138, ent.image_pos)
2811 self.assertEqual(None, ent.uncomp_size)
2812 self.assertEqual(0x38, ent.offset)
2813
2814 ent = entries[5]
2815 self.assertEqual(3, ent.indent)
2816 self.assertEqual('u-boot-dtb', ent.name)
2817 self.assertEqual('text', ent.etype)
2818 self.assertGreater(len(COMPRESS_DATA), ent.size)
2819 self.assertEqual(0x178, ent.image_pos)
2820 self.assertEqual(len(COMPRESS_DATA), ent.uncomp_size)
2821 self.assertEqual(0x78, ent.offset)
2822
2823 ent = entries[6]
2824 self.assertEqual(2, ent.indent)
2825 self.assertEqual('u-boot-dtb', ent.name)
2826 self.assertEqual('u-boot-dtb', ent.etype)
2827 self.assertEqual(0x500, ent.image_pos)
2828 self.assertEqual(len(U_BOOT_DTB_DATA), ent.uncomp_size)
2829 dtb_size = ent.size
2830 # Compressing this data expands it since headers are added
2831 self.assertGreater(dtb_size, len(U_BOOT_DTB_DATA))
2832 self.assertEqual(0x400, ent.offset)
2833
2834 self.assertEqual(len(data), 0x100 + section_size)
2835 self.assertEqual(section_size, 0x400 + dtb_size)
2836
Simon Glass8d8bf4e2019-07-08 14:25:44 -06002837 def testFindFdtmap(self):
2838 """Test locating an FDT map in an image"""
2839 self._CheckLz4()
2840 data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
2841 image = control.images['image']
2842 entries = image.GetEntries()
2843 entry = entries['fdtmap']
2844 self.assertEqual(entry.image_pos, fdtmap.LocateFdtmap(data))
2845
2846 def testFindFdtmapMissing(self):
2847 """Test failing to locate an FDP map"""
2848 data = self._DoReadFile('005_simple.dts')
2849 self.assertEqual(None, fdtmap.LocateFdtmap(data))
2850
Simon Glassed39a3c2019-07-08 14:25:45 -06002851 def testFindImageHeader(self):
2852 """Test locating a image header"""
2853 self._CheckLz4()
Simon Glassb8424fa2019-07-08 14:25:46 -06002854 data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
Simon Glassed39a3c2019-07-08 14:25:45 -06002855 image = control.images['image']
2856 entries = image.GetEntries()
2857 entry = entries['fdtmap']
2858 # The header should point to the FDT map
2859 self.assertEqual(entry.image_pos, image_header.LocateHeaderOffset(data))
2860
2861 def testFindImageHeaderStart(self):
2862 """Test locating a image header located at the start of an image"""
Simon Glassb8424fa2019-07-08 14:25:46 -06002863 data = self.data = self._DoReadFileRealDtb('117_fdtmap_hdr_start.dts')
Simon Glassed39a3c2019-07-08 14:25:45 -06002864 image = control.images['image']
2865 entries = image.GetEntries()
2866 entry = entries['fdtmap']
2867 # The header should point to the FDT map
2868 self.assertEqual(entry.image_pos, image_header.LocateHeaderOffset(data))
2869
2870 def testFindImageHeaderMissing(self):
2871 """Test failing to locate an image header"""
2872 data = self._DoReadFile('005_simple.dts')
2873 self.assertEqual(None, image_header.LocateHeaderOffset(data))
2874
Simon Glassb8424fa2019-07-08 14:25:46 -06002875 def testReadImage(self):
2876 """Test reading an image and accessing its FDT map"""
2877 self._CheckLz4()
2878 data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
Simon Glass80025522022-01-29 14:14:04 -07002879 image_fname = tools.get_output_filename('image.bin')
Simon Glassb8424fa2019-07-08 14:25:46 -06002880 orig_image = control.images['image']
2881 image = Image.FromFile(image_fname)
2882 self.assertEqual(orig_image.GetEntries().keys(),
2883 image.GetEntries().keys())
2884
2885 orig_entry = orig_image.GetEntries()['fdtmap']
2886 entry = image.GetEntries()['fdtmap']
Brandon Maiera657bc62024-06-04 16:16:05 +00002887 self.assertEqual(orig_entry.offset, entry.offset)
2888 self.assertEqual(orig_entry.size, entry.size)
2889 self.assertEqual(orig_entry.image_pos, entry.image_pos)
Simon Glassb8424fa2019-07-08 14:25:46 -06002890
2891 def testReadImageNoHeader(self):
2892 """Test accessing an image's FDT map without an image header"""
2893 self._CheckLz4()
2894 data = self._DoReadFileRealDtb('129_decode_image_nohdr.dts')
Simon Glass80025522022-01-29 14:14:04 -07002895 image_fname = tools.get_output_filename('image.bin')
Simon Glassb8424fa2019-07-08 14:25:46 -06002896 image = Image.FromFile(image_fname)
2897 self.assertTrue(isinstance(image, Image))
Simon Glass072959a2019-07-20 12:23:50 -06002898 self.assertEqual('image', image.image_name[-5:])
Simon Glassb8424fa2019-07-08 14:25:46 -06002899
2900 def testReadImageFail(self):
2901 """Test failing to read an image image's FDT map"""
2902 self._DoReadFile('005_simple.dts')
Simon Glass80025522022-01-29 14:14:04 -07002903 image_fname = tools.get_output_filename('image.bin')
Simon Glassb8424fa2019-07-08 14:25:46 -06002904 with self.assertRaises(ValueError) as e:
2905 image = Image.FromFile(image_fname)
2906 self.assertIn("Cannot find FDT map in image", str(e.exception))
Simon Glassc2f1aed2019-07-08 13:18:56 -06002907
Simon Glassb2fd11d2019-07-08 14:25:48 -06002908 def testListCmd(self):
2909 """Test listing the files in an image using an Fdtmap"""
2910 self._CheckLz4()
2911 data = self._DoReadFileRealDtb('130_list_fdtmap.dts')
2912
2913 # lz4 compression size differs depending on the version
2914 image = control.images['image']
2915 entries = image.GetEntries()
2916 section_size = entries['section'].size
2917 fdt_size = entries['section'].GetEntries()['u-boot-dtb'].size
2918 fdtmap_offset = entries['fdtmap'].offset
2919
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01002920 tmpdir = None
Simon Glassb3d6fc72019-07-20 12:24:10 -06002921 try:
2922 tmpdir, updated_fname = self._SetupImageInTmpdir()
Simon Glass14d64e32025-04-29 07:21:59 -06002923 with terminal.capture() as (stdout, stderr):
Simon Glassb3d6fc72019-07-20 12:24:10 -06002924 self._DoBinman('ls', '-i', updated_fname)
2925 finally:
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01002926 if tmpdir:
2927 shutil.rmtree(tmpdir)
Simon Glassb2fd11d2019-07-08 14:25:48 -06002928 lines = stdout.getvalue().splitlines()
2929 expected = [
2930'Name Image-pos Size Entry-type Offset Uncomp-size',
2931'----------------------------------------------------------------------',
Simon Glass49cd2b32023-02-07 14:34:18 -07002932'image 0 c00 section 0',
Simon Glassb2fd11d2019-07-08 14:25:48 -06002933' u-boot 0 4 u-boot 0',
2934' section 100 %x section 100' % section_size,
2935' cbfs 100 400 cbfs 0',
Simon Glassfa144222023-10-14 14:40:28 -06002936' u-boot 120 4 u-boot 20',
Simon Glassc5fd10a2019-10-31 07:43:03 -06002937' u-boot-dtb 180 105 u-boot-dtb 80 3c9',
Simon Glassb2fd11d2019-07-08 14:25:48 -06002938' u-boot-dtb 500 %x u-boot-dtb 400 3c9' % fdt_size,
Simon Glassc5fd10a2019-10-31 07:43:03 -06002939' fdtmap %x 3bd fdtmap %x' %
Simon Glassb2fd11d2019-07-08 14:25:48 -06002940 (fdtmap_offset, fdtmap_offset),
2941' image-header bf8 8 image-header bf8',
2942 ]
2943 self.assertEqual(expected, lines)
2944
2945 def testListCmdFail(self):
2946 """Test failing to list an image"""
2947 self._DoReadFile('005_simple.dts')
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01002948 tmpdir = None
Simon Glassb3d6fc72019-07-20 12:24:10 -06002949 try:
2950 tmpdir, updated_fname = self._SetupImageInTmpdir()
2951 with self.assertRaises(ValueError) as e:
2952 self._DoBinman('ls', '-i', updated_fname)
2953 finally:
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01002954 if tmpdir:
2955 shutil.rmtree(tmpdir)
Simon Glassb2fd11d2019-07-08 14:25:48 -06002956 self.assertIn("Cannot find FDT map in image", str(e.exception))
2957
2958 def _RunListCmd(self, paths, expected):
2959 """List out entries and check the result
2960
2961 Args:
2962 paths: List of paths to pass to the list command
2963 expected: Expected list of filenames to be returned, in order
2964 """
2965 self._CheckLz4()
2966 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07002967 image_fname = tools.get_output_filename('image.bin')
Simon Glassb2fd11d2019-07-08 14:25:48 -06002968 image = Image.FromFile(image_fname)
2969 lines = image.GetListEntries(paths)[1]
2970 files = [line[0].strip() for line in lines[1:]]
2971 self.assertEqual(expected, files)
2972
2973 def testListCmdSection(self):
2974 """Test listing the files in a section"""
2975 self._RunListCmd(['section'],
2976 ['section', 'cbfs', 'u-boot', 'u-boot-dtb', 'u-boot-dtb'])
2977
2978 def testListCmdFile(self):
2979 """Test listing a particular file"""
2980 self._RunListCmd(['*u-boot-dtb'], ['u-boot-dtb', 'u-boot-dtb'])
2981
2982 def testListCmdWildcard(self):
2983 """Test listing a wildcarded file"""
2984 self._RunListCmd(['*boot*'],
2985 ['u-boot', 'u-boot', 'u-boot-dtb', 'u-boot-dtb'])
2986
2987 def testListCmdWildcardMulti(self):
2988 """Test listing a wildcarded file"""
2989 self._RunListCmd(['*cb*', '*head*'],
2990 ['cbfs', 'u-boot', 'u-boot-dtb', 'image-header'])
2991
2992 def testListCmdEmpty(self):
2993 """Test listing a wildcarded file"""
2994 self._RunListCmd(['nothing'], [])
2995
2996 def testListCmdPath(self):
2997 """Test listing the files in a sub-entry of a section"""
2998 self._RunListCmd(['section/cbfs'], ['cbfs', 'u-boot', 'u-boot-dtb'])
2999
Simon Glass4c613bf2019-07-08 14:25:50 -06003000 def _RunExtractCmd(self, entry_name, decomp=True):
3001 """Extract an entry from an image
3002
3003 Args:
3004 entry_name: Entry name to extract
3005 decomp: True to decompress the data if compressed, False to leave
3006 it in its raw uncompressed format
3007
3008 Returns:
3009 data from entry
3010 """
3011 self._CheckLz4()
3012 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07003013 image_fname = tools.get_output_filename('image.bin')
Simon Glass4c613bf2019-07-08 14:25:50 -06003014 return control.ReadEntry(image_fname, entry_name, decomp)
3015
3016 def testExtractSimple(self):
3017 """Test extracting a single file"""
3018 data = self._RunExtractCmd('u-boot')
3019 self.assertEqual(U_BOOT_DATA, data)
3020
Simon Glass980a2842019-07-08 14:25:52 -06003021 def testExtractSection(self):
3022 """Test extracting the files in a section"""
3023 data = self._RunExtractCmd('section')
3024 cbfs_data = data[:0x400]
3025 cbfs = cbfs_util.CbfsReader(cbfs_data)
Simon Glassc5fd10a2019-10-31 07:43:03 -06003026 self.assertEqual(['u-boot', 'u-boot-dtb', ''], list(cbfs.files.keys()))
Simon Glass980a2842019-07-08 14:25:52 -06003027 dtb_data = data[0x400:]
3028 dtb = self._decompress(dtb_data)
3029 self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
3030
3031 def testExtractCompressed(self):
3032 """Test extracting compressed data"""
3033 data = self._RunExtractCmd('section/u-boot-dtb')
3034 self.assertEqual(EXTRACT_DTB_SIZE, len(data))
3035
3036 def testExtractRaw(self):
3037 """Test extracting compressed data without decompressing it"""
3038 data = self._RunExtractCmd('section/u-boot-dtb', decomp=False)
3039 dtb = self._decompress(data)
3040 self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
3041
3042 def testExtractCbfs(self):
3043 """Test extracting CBFS data"""
3044 data = self._RunExtractCmd('section/cbfs/u-boot')
3045 self.assertEqual(U_BOOT_DATA, data)
3046
3047 def testExtractCbfsCompressed(self):
3048 """Test extracting CBFS compressed data"""
3049 data = self._RunExtractCmd('section/cbfs/u-boot-dtb')
3050 self.assertEqual(EXTRACT_DTB_SIZE, len(data))
3051
3052 def testExtractCbfsRaw(self):
3053 """Test extracting CBFS compressed data without decompressing it"""
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +02003054 bintool = self.comp_bintools['lzma_alone']
3055 self._CheckBintool(bintool)
Simon Glass980a2842019-07-08 14:25:52 -06003056 data = self._RunExtractCmd('section/cbfs/u-boot-dtb', decomp=False)
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +02003057 dtb = bintool.decompress(data)
Simon Glass980a2842019-07-08 14:25:52 -06003058 self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
3059
Simon Glass4c613bf2019-07-08 14:25:50 -06003060 def testExtractBadEntry(self):
3061 """Test extracting a bad section path"""
3062 with self.assertRaises(ValueError) as e:
3063 self._RunExtractCmd('section/does-not-exist')
3064 self.assertIn("Entry 'does-not-exist' not found in '/section'",
3065 str(e.exception))
3066
3067 def testExtractMissingFile(self):
3068 """Test extracting file that does not exist"""
3069 with self.assertRaises(IOError) as e:
3070 control.ReadEntry('missing-file', 'name')
3071
3072 def testExtractBadFile(self):
3073 """Test extracting an invalid file"""
3074 fname = os.path.join(self._indir, 'badfile')
Simon Glass80025522022-01-29 14:14:04 -07003075 tools.write_file(fname, b'')
Simon Glass4c613bf2019-07-08 14:25:50 -06003076 with self.assertRaises(ValueError) as e:
3077 control.ReadEntry(fname, 'name')
3078
Simon Glass980a2842019-07-08 14:25:52 -06003079 def testExtractCmd(self):
3080 """Test extracting a file fron an image on the command line"""
3081 self._CheckLz4()
3082 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass980a2842019-07-08 14:25:52 -06003083 fname = os.path.join(self._indir, 'output.extact')
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01003084 tmpdir = None
Simon Glassb3d6fc72019-07-20 12:24:10 -06003085 try:
3086 tmpdir, updated_fname = self._SetupImageInTmpdir()
Simon Glass14d64e32025-04-29 07:21:59 -06003087 with terminal.capture() as (stdout, stderr):
Simon Glassb3d6fc72019-07-20 12:24:10 -06003088 self._DoBinman('extract', '-i', updated_fname, 'u-boot',
3089 '-f', fname)
3090 finally:
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01003091 if tmpdir:
3092 shutil.rmtree(tmpdir)
Simon Glass80025522022-01-29 14:14:04 -07003093 data = tools.read_file(fname)
Simon Glass980a2842019-07-08 14:25:52 -06003094 self.assertEqual(U_BOOT_DATA, data)
3095
3096 def testExtractOneEntry(self):
3097 """Test extracting a single entry fron an image """
3098 self._CheckLz4()
3099 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07003100 image_fname = tools.get_output_filename('image.bin')
Simon Glass980a2842019-07-08 14:25:52 -06003101 fname = os.path.join(self._indir, 'output.extact')
3102 control.ExtractEntries(image_fname, fname, None, ['u-boot'])
Simon Glass80025522022-01-29 14:14:04 -07003103 data = tools.read_file(fname)
Simon Glass980a2842019-07-08 14:25:52 -06003104 self.assertEqual(U_BOOT_DATA, data)
3105
3106 def _CheckExtractOutput(self, decomp):
3107 """Helper to test file output with and without decompression
3108
3109 Args:
3110 decomp: True to decompress entry data, False to output it raw
3111 """
3112 def _CheckPresent(entry_path, expect_data, expect_size=None):
3113 """Check and remove expected file
3114
3115 This checks the data/size of a file and removes the file both from
3116 the outfiles set and from the output directory. Once all files are
3117 processed, both the set and directory should be empty.
3118
3119 Args:
3120 entry_path: Entry path
3121 expect_data: Data to expect in file, or None to skip check
3122 expect_size: Size of data to expect in file, or None to skip
3123 """
3124 path = os.path.join(outdir, entry_path)
Simon Glass80025522022-01-29 14:14:04 -07003125 data = tools.read_file(path)
Simon Glass980a2842019-07-08 14:25:52 -06003126 os.remove(path)
3127 if expect_data:
3128 self.assertEqual(expect_data, data)
3129 elif expect_size:
3130 self.assertEqual(expect_size, len(data))
3131 outfiles.remove(path)
3132
3133 def _CheckDirPresent(name):
3134 """Remove expected directory
3135
3136 This gives an error if the directory does not exist as expected
3137
3138 Args:
3139 name: Name of directory to remove
3140 """
3141 path = os.path.join(outdir, name)
3142 os.rmdir(path)
3143
3144 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07003145 image_fname = tools.get_output_filename('image.bin')
Simon Glass980a2842019-07-08 14:25:52 -06003146 outdir = os.path.join(self._indir, 'extract')
3147 einfos = control.ExtractEntries(image_fname, None, outdir, [], decomp)
3148
3149 # Create a set of all file that were output (should be 9)
3150 outfiles = set()
3151 for root, dirs, files in os.walk(outdir):
3152 outfiles |= set([os.path.join(root, fname) for fname in files])
3153 self.assertEqual(9, len(outfiles))
3154 self.assertEqual(9, len(einfos))
3155
3156 image = control.images['image']
3157 entries = image.GetEntries()
3158
3159 # Check the 9 files in various ways
3160 section = entries['section']
3161 section_entries = section.GetEntries()
3162 cbfs_entries = section_entries['cbfs'].GetEntries()
3163 _CheckPresent('u-boot', U_BOOT_DATA)
3164 _CheckPresent('section/cbfs/u-boot', U_BOOT_DATA)
3165 dtb_len = EXTRACT_DTB_SIZE
3166 if not decomp:
3167 dtb_len = cbfs_entries['u-boot-dtb'].size
3168 _CheckPresent('section/cbfs/u-boot-dtb', None, dtb_len)
3169 if not decomp:
3170 dtb_len = section_entries['u-boot-dtb'].size
3171 _CheckPresent('section/u-boot-dtb', None, dtb_len)
3172
3173 fdtmap = entries['fdtmap']
3174 _CheckPresent('fdtmap', fdtmap.data)
3175 hdr = entries['image-header']
3176 _CheckPresent('image-header', hdr.data)
3177
3178 _CheckPresent('section/root', section.data)
3179 cbfs = section_entries['cbfs']
3180 _CheckPresent('section/cbfs/root', cbfs.data)
Simon Glass80025522022-01-29 14:14:04 -07003181 data = tools.read_file(image_fname)
Simon Glass980a2842019-07-08 14:25:52 -06003182 _CheckPresent('root', data)
3183
3184 # There should be no files left. Remove all the directories to check.
3185 # If there are any files/dirs remaining, one of these checks will fail.
3186 self.assertEqual(0, len(outfiles))
3187 _CheckDirPresent('section/cbfs')
3188 _CheckDirPresent('section')
3189 _CheckDirPresent('')
3190 self.assertFalse(os.path.exists(outdir))
3191
3192 def testExtractAllEntries(self):
3193 """Test extracting all entries"""
3194 self._CheckLz4()
3195 self._CheckExtractOutput(decomp=True)
3196
3197 def testExtractAllEntriesRaw(self):
3198 """Test extracting all entries without decompressing them"""
3199 self._CheckLz4()
3200 self._CheckExtractOutput(decomp=False)
3201
3202 def testExtractSelectedEntries(self):
3203 """Test extracting some entries"""
3204 self._CheckLz4()
3205 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07003206 image_fname = tools.get_output_filename('image.bin')
Simon Glass980a2842019-07-08 14:25:52 -06003207 outdir = os.path.join(self._indir, 'extract')
3208 einfos = control.ExtractEntries(image_fname, None, outdir,
3209 ['*cb*', '*head*'])
3210
3211 # File output is tested by testExtractAllEntries(), so just check that
3212 # the expected entries are selected
3213 names = [einfo.name for einfo in einfos]
3214 self.assertEqual(names,
3215 ['cbfs', 'u-boot', 'u-boot-dtb', 'image-header'])
3216
3217 def testExtractNoEntryPaths(self):
3218 """Test extracting some entries"""
3219 self._CheckLz4()
3220 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07003221 image_fname = tools.get_output_filename('image.bin')
Simon Glass980a2842019-07-08 14:25:52 -06003222 with self.assertRaises(ValueError) as e:
3223 control.ExtractEntries(image_fname, 'fname', None, [])
Simon Glassa772d3f2019-07-20 12:24:14 -06003224 self.assertIn('Must specify an entry path to write with -f',
Simon Glass980a2842019-07-08 14:25:52 -06003225 str(e.exception))
3226
3227 def testExtractTooManyEntryPaths(self):
3228 """Test extracting some entries"""
3229 self._CheckLz4()
3230 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07003231 image_fname = tools.get_output_filename('image.bin')
Simon Glass980a2842019-07-08 14:25:52 -06003232 with self.assertRaises(ValueError) as e:
3233 control.ExtractEntries(image_fname, 'fname', None, ['a', 'b'])
Simon Glassa772d3f2019-07-20 12:24:14 -06003234 self.assertIn('Must specify exactly one entry path to write with -f',
Simon Glass980a2842019-07-08 14:25:52 -06003235 str(e.exception))
3236
Simon Glass52d06212019-07-08 14:25:53 -06003237 def testPackAlignSection(self):
3238 """Test that sections can have alignment"""
3239 self._DoReadFile('131_pack_align_section.dts')
3240
3241 self.assertIn('image', control.images)
3242 image = control.images['image']
3243 entries = image.GetEntries()
3244 self.assertEqual(3, len(entries))
3245
3246 # First u-boot
3247 self.assertIn('u-boot', entries)
3248 entry = entries['u-boot']
3249 self.assertEqual(0, entry.offset)
3250 self.assertEqual(0, entry.image_pos)
3251 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3252 self.assertEqual(len(U_BOOT_DATA), entry.size)
3253
3254 # Section0
3255 self.assertIn('section0', entries)
3256 section0 = entries['section0']
3257 self.assertEqual(0x10, section0.offset)
3258 self.assertEqual(0x10, section0.image_pos)
3259 self.assertEqual(len(U_BOOT_DATA), section0.size)
3260
3261 # Second u-boot
3262 section_entries = section0.GetEntries()
3263 self.assertIn('u-boot', section_entries)
3264 entry = section_entries['u-boot']
3265 self.assertEqual(0, entry.offset)
3266 self.assertEqual(0x10, entry.image_pos)
3267 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3268 self.assertEqual(len(U_BOOT_DATA), entry.size)
3269
3270 # Section1
3271 self.assertIn('section1', entries)
3272 section1 = entries['section1']
3273 self.assertEqual(0x14, section1.offset)
3274 self.assertEqual(0x14, section1.image_pos)
3275 self.assertEqual(0x20, section1.size)
3276
3277 # Second u-boot
3278 section_entries = section1.GetEntries()
3279 self.assertIn('u-boot', section_entries)
3280 entry = section_entries['u-boot']
3281 self.assertEqual(0, entry.offset)
3282 self.assertEqual(0x14, entry.image_pos)
3283 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3284 self.assertEqual(len(U_BOOT_DATA), entry.size)
3285
3286 # Section2
3287 self.assertIn('section2', section_entries)
3288 section2 = section_entries['section2']
3289 self.assertEqual(0x4, section2.offset)
3290 self.assertEqual(0x18, section2.image_pos)
3291 self.assertEqual(4, section2.size)
3292
3293 # Third u-boot
3294 section_entries = section2.GetEntries()
3295 self.assertIn('u-boot', section_entries)
3296 entry = section_entries['u-boot']
3297 self.assertEqual(0, entry.offset)
3298 self.assertEqual(0x18, entry.image_pos)
3299 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3300 self.assertEqual(len(U_BOOT_DATA), entry.size)
3301
Simon Glassf8a54bc2019-07-20 12:23:56 -06003302 def _RunReplaceCmd(self, entry_name, data, decomp=True, allow_resize=True,
3303 dts='132_replace.dts'):
Simon Glass072959a2019-07-20 12:23:50 -06003304 """Replace an entry in an image
3305
3306 This writes the entry data to update it, then opens the updated file and
3307 returns the value that it now finds there.
3308
3309 Args:
3310 entry_name: Entry name to replace
3311 data: Data to replace it with
3312 decomp: True to compress the data if needed, False if data is
3313 already compressed so should be used as is
Simon Glassf8a54bc2019-07-20 12:23:56 -06003314 allow_resize: True to allow entries to change size, False to raise
3315 an exception
Simon Glass072959a2019-07-20 12:23:50 -06003316
3317 Returns:
3318 Tuple:
3319 data from entry
3320 data from fdtmap (excluding header)
Simon Glassf8a54bc2019-07-20 12:23:56 -06003321 Image object that was modified
Simon Glass072959a2019-07-20 12:23:50 -06003322 """
Simon Glassf8a54bc2019-07-20 12:23:56 -06003323 dtb_data = self._DoReadFileDtb(dts, use_real_dtb=True,
Simon Glass072959a2019-07-20 12:23:50 -06003324 update_dtb=True)[1]
3325
3326 self.assertIn('image', control.images)
3327 image = control.images['image']
3328 entries = image.GetEntries()
3329 orig_dtb_data = entries['u-boot-dtb'].data
3330 orig_fdtmap_data = entries['fdtmap'].data
3331
Simon Glass80025522022-01-29 14:14:04 -07003332 image_fname = tools.get_output_filename('image.bin')
3333 updated_fname = tools.get_output_filename('image-updated.bin')
3334 tools.write_file(updated_fname, tools.read_file(image_fname))
Simon Glassf8a54bc2019-07-20 12:23:56 -06003335 image = control.WriteEntry(updated_fname, entry_name, data, decomp,
3336 allow_resize)
Simon Glass072959a2019-07-20 12:23:50 -06003337 data = control.ReadEntry(updated_fname, entry_name, decomp)
3338
Simon Glassf8a54bc2019-07-20 12:23:56 -06003339 # The DT data should not change unless resized:
3340 if not allow_resize:
3341 new_dtb_data = entries['u-boot-dtb'].data
3342 self.assertEqual(new_dtb_data, orig_dtb_data)
3343 new_fdtmap_data = entries['fdtmap'].data
3344 self.assertEqual(new_fdtmap_data, orig_fdtmap_data)
Simon Glass072959a2019-07-20 12:23:50 -06003345
Simon Glassf8a54bc2019-07-20 12:23:56 -06003346 return data, orig_fdtmap_data[fdtmap.FDTMAP_HDR_LEN:], image
Simon Glass072959a2019-07-20 12:23:50 -06003347
3348 def testReplaceSimple(self):
3349 """Test replacing a single file"""
3350 expected = b'x' * len(U_BOOT_DATA)
Simon Glassf8a54bc2019-07-20 12:23:56 -06003351 data, expected_fdtmap, _ = self._RunReplaceCmd('u-boot', expected,
3352 allow_resize=False)
Simon Glass072959a2019-07-20 12:23:50 -06003353 self.assertEqual(expected, data)
3354
3355 # Test that the state looks right. There should be an FDT for the fdtmap
3356 # that we jsut read back in, and it should match what we find in the
3357 # 'control' tables. Checking for an FDT that does not exist should
3358 # return None.
3359 path, fdtmap = state.GetFdtContents('fdtmap')
Simon Glassf8a54bc2019-07-20 12:23:56 -06003360 self.assertIsNotNone(path)
Simon Glass072959a2019-07-20 12:23:50 -06003361 self.assertEqual(expected_fdtmap, fdtmap)
3362
3363 dtb = state.GetFdtForEtype('fdtmap')
3364 self.assertEqual(dtb.GetContents(), fdtmap)
3365
3366 missing_path, missing_fdtmap = state.GetFdtContents('missing')
3367 self.assertIsNone(missing_path)
3368 self.assertIsNone(missing_fdtmap)
3369
3370 missing_dtb = state.GetFdtForEtype('missing')
3371 self.assertIsNone(missing_dtb)
3372
3373 self.assertEqual('/binman', state.fdt_path_prefix)
3374
3375 def testReplaceResizeFail(self):
3376 """Test replacing a file by something larger"""
3377 expected = U_BOOT_DATA + b'x'
3378 with self.assertRaises(ValueError) as e:
Simon Glassf8a54bc2019-07-20 12:23:56 -06003379 self._RunReplaceCmd('u-boot', expected, allow_resize=False,
3380 dts='139_replace_repack.dts')
Simon Glass072959a2019-07-20 12:23:50 -06003381 self.assertIn("Node '/u-boot': Entry data size does not match, but resize is disabled",
3382 str(e.exception))
3383
3384 def testReplaceMulti(self):
3385 """Test replacing entry data where multiple images are generated"""
3386 data = self._DoReadFileDtb('133_replace_multi.dts', use_real_dtb=True,
3387 update_dtb=True)[0]
3388 expected = b'x' * len(U_BOOT_DATA)
Simon Glass80025522022-01-29 14:14:04 -07003389 updated_fname = tools.get_output_filename('image-updated.bin')
3390 tools.write_file(updated_fname, data)
Simon Glass072959a2019-07-20 12:23:50 -06003391 entry_name = 'u-boot'
Simon Glassf8a54bc2019-07-20 12:23:56 -06003392 control.WriteEntry(updated_fname, entry_name, expected,
3393 allow_resize=False)
Simon Glass072959a2019-07-20 12:23:50 -06003394 data = control.ReadEntry(updated_fname, entry_name)
3395 self.assertEqual(expected, data)
3396
3397 # Check the state looks right.
3398 self.assertEqual('/binman/image', state.fdt_path_prefix)
3399
3400 # Now check we can write the first image
Simon Glass80025522022-01-29 14:14:04 -07003401 image_fname = tools.get_output_filename('first-image.bin')
3402 updated_fname = tools.get_output_filename('first-updated.bin')
3403 tools.write_file(updated_fname, tools.read_file(image_fname))
Simon Glass072959a2019-07-20 12:23:50 -06003404 entry_name = 'u-boot'
Simon Glassf8a54bc2019-07-20 12:23:56 -06003405 control.WriteEntry(updated_fname, entry_name, expected,
3406 allow_resize=False)
Simon Glass072959a2019-07-20 12:23:50 -06003407 data = control.ReadEntry(updated_fname, entry_name)
3408 self.assertEqual(expected, data)
3409
3410 # Check the state looks right.
3411 self.assertEqual('/binman/first-image', state.fdt_path_prefix)
Simon Glass39dd2152019-07-08 14:25:47 -06003412
Simon Glassfb30e292019-07-20 12:23:51 -06003413 def testUpdateFdtAllRepack(self):
3414 """Test that all device trees are updated with offset/size info"""
Marek Vasutf7413f02023-07-18 07:23:58 -06003415 self._SetupSplElf()
3416 self._SetupTplElf()
Simon Glassfb30e292019-07-20 12:23:51 -06003417 data = self._DoReadFileRealDtb('134_fdt_update_all_repack.dts')
3418 SECTION_SIZE = 0x300
3419 DTB_SIZE = 602
3420 FDTMAP_SIZE = 608
3421 base_expected = {
3422 'offset': 0,
3423 'size': SECTION_SIZE + DTB_SIZE * 2 + FDTMAP_SIZE,
3424 'image-pos': 0,
3425 'section:offset': 0,
3426 'section:size': SECTION_SIZE,
3427 'section:image-pos': 0,
3428 'section/u-boot-dtb:offset': 4,
3429 'section/u-boot-dtb:size': 636,
3430 'section/u-boot-dtb:image-pos': 4,
3431 'u-boot-spl-dtb:offset': SECTION_SIZE,
3432 'u-boot-spl-dtb:size': DTB_SIZE,
3433 'u-boot-spl-dtb:image-pos': SECTION_SIZE,
3434 'u-boot-tpl-dtb:offset': SECTION_SIZE + DTB_SIZE,
3435 'u-boot-tpl-dtb:image-pos': SECTION_SIZE + DTB_SIZE,
3436 'u-boot-tpl-dtb:size': DTB_SIZE,
3437 'fdtmap:offset': SECTION_SIZE + DTB_SIZE * 2,
3438 'fdtmap:size': FDTMAP_SIZE,
3439 'fdtmap:image-pos': SECTION_SIZE + DTB_SIZE * 2,
3440 }
3441 main_expected = {
3442 'section:orig-size': SECTION_SIZE,
3443 'section/u-boot-dtb:orig-offset': 4,
3444 }
3445
3446 # We expect three device-tree files in the output, with the first one
3447 # within a fixed-size section.
3448 # Read them in sequence. We look for an 'spl' property in the SPL tree,
3449 # and 'tpl' in the TPL tree, to make sure they are distinct from the
3450 # main U-Boot tree. All three should have the same positions and offset
3451 # except that the main tree should include the main_expected properties
3452 start = 4
3453 for item in ['', 'spl', 'tpl', None]:
3454 if item is None:
3455 start += 16 # Move past fdtmap header
3456 dtb = fdt.Fdt.FromData(data[start:])
3457 dtb.Scan()
3458 props = self._GetPropTree(dtb,
3459 BASE_DTB_PROPS + REPACK_DTB_PROPS + ['spl', 'tpl'],
3460 prefix='/' if item is None else '/binman/')
3461 expected = dict(base_expected)
3462 if item:
3463 expected[item] = 0
3464 else:
3465 # Main DTB and fdtdec should include the 'orig-' properties
3466 expected.update(main_expected)
3467 # Helpful for debugging:
3468 #for prop in sorted(props):
3469 #print('prop %s %s %s' % (prop, props[prop], expected[prop]))
3470 self.assertEqual(expected, props)
3471 if item == '':
3472 start = SECTION_SIZE
3473 else:
3474 start += dtb._fdt_obj.totalsize()
3475
Simon Glass11453762019-07-20 12:23:55 -06003476 def testFdtmapHeaderMiddle(self):
3477 """Test an FDT map in the middle of an image when it should be at end"""
3478 with self.assertRaises(ValueError) as e:
3479 self._DoReadFileRealDtb('135_fdtmap_hdr_middle.dts')
3480 self.assertIn("Invalid sibling order 'middle' for image-header: Must be at 'end' to match location",
3481 str(e.exception))
3482
3483 def testFdtmapHeaderStartBad(self):
3484 """Test an FDT map in middle of an image when it should be at start"""
3485 with self.assertRaises(ValueError) as e:
3486 self._DoReadFileRealDtb('136_fdtmap_hdr_startbad.dts')
3487 self.assertIn("Invalid sibling order 'end' for image-header: Must be at 'start' to match location",
3488 str(e.exception))
3489
3490 def testFdtmapHeaderEndBad(self):
3491 """Test an FDT map at the start of an image when it should be at end"""
3492 with self.assertRaises(ValueError) as e:
3493 self._DoReadFileRealDtb('137_fdtmap_hdr_endbad.dts')
3494 self.assertIn("Invalid sibling order 'start' for image-header: Must be at 'end' to match location",
3495 str(e.exception))
3496
3497 def testFdtmapHeaderNoSize(self):
3498 """Test an image header at the end of an image with undefined size"""
3499 self._DoReadFileRealDtb('138_fdtmap_hdr_nosize.dts')
3500
Simon Glassf8a54bc2019-07-20 12:23:56 -06003501 def testReplaceResize(self):
3502 """Test replacing a single file in an entry with a larger file"""
3503 expected = U_BOOT_DATA + b'x'
3504 data, _, image = self._RunReplaceCmd('u-boot', expected,
3505 dts='139_replace_repack.dts')
3506 self.assertEqual(expected, data)
3507
3508 entries = image.GetEntries()
3509 dtb_data = entries['u-boot-dtb'].data
3510 dtb = fdt.Fdt.FromData(dtb_data)
3511 dtb.Scan()
3512
3513 # The u-boot section should now be larger in the dtb
3514 node = dtb.GetNode('/binman/u-boot')
3515 self.assertEqual(len(expected), fdt_util.GetInt(node, 'size'))
3516
3517 # Same for the fdtmap
3518 fdata = entries['fdtmap'].data
3519 fdtb = fdt.Fdt.FromData(fdata[fdtmap.FDTMAP_HDR_LEN:])
3520 fdtb.Scan()
3521 fnode = fdtb.GetNode('/u-boot')
3522 self.assertEqual(len(expected), fdt_util.GetInt(fnode, 'size'))
3523
3524 def testReplaceResizeNoRepack(self):
3525 """Test replacing an entry with a larger file when not allowed"""
3526 expected = U_BOOT_DATA + b'x'
3527 with self.assertRaises(ValueError) as e:
3528 self._RunReplaceCmd('u-boot', expected)
3529 self.assertIn('Entry data size does not match, but allow-repack is not present for this image',
3530 str(e.exception))
3531
Simon Glass9d8ee322019-07-20 12:23:58 -06003532 def testEntryShrink(self):
3533 """Test contracting an entry after it is packed"""
3534 try:
3535 state.SetAllowEntryContraction(True)
3536 data = self._DoReadFileDtb('140_entry_shrink.dts',
3537 update_dtb=True)[0]
3538 finally:
3539 state.SetAllowEntryContraction(False)
3540 self.assertEqual(b'a', data[:1])
3541 self.assertEqual(U_BOOT_DATA, data[1:1 + len(U_BOOT_DATA)])
3542 self.assertEqual(b'a', data[-1:])
3543
3544 def testEntryShrinkFail(self):
3545 """Test not being allowed to contract an entry after it is packed"""
3546 data = self._DoReadFileDtb('140_entry_shrink.dts', update_dtb=True)[0]
3547
3548 # In this case there is a spare byte at the end of the data. The size of
3549 # the contents is only 1 byte but we still have the size before it
3550 # shrunk.
3551 self.assertEqual(b'a\0', data[:2])
3552 self.assertEqual(U_BOOT_DATA, data[2:2 + len(U_BOOT_DATA)])
3553 self.assertEqual(b'a\0', data[-2:])
3554
Simon Glass70e32982019-07-20 12:24:01 -06003555 def testDescriptorOffset(self):
3556 """Test that the Intel descriptor is always placed at at the start"""
3557 data = self._DoReadFileDtb('141_descriptor_offset.dts')
3558 image = control.images['image']
3559 entries = image.GetEntries()
3560 desc = entries['intel-descriptor']
Simon Glassed836ac2025-02-26 09:26:17 -07003561 self.assertEqual(0xff800000, desc.offset)
3562 self.assertEqual(0xff800000, desc.image_pos)
Simon Glass70e32982019-07-20 12:24:01 -06003563
Simon Glass37fdd142019-07-20 12:24:06 -06003564 def testReplaceCbfs(self):
3565 """Test replacing a single file in CBFS without changing the size"""
3566 self._CheckLz4()
3567 expected = b'x' * len(U_BOOT_DATA)
3568 data = self._DoReadFileRealDtb('142_replace_cbfs.dts')
Simon Glass80025522022-01-29 14:14:04 -07003569 updated_fname = tools.get_output_filename('image-updated.bin')
3570 tools.write_file(updated_fname, data)
Simon Glass37fdd142019-07-20 12:24:06 -06003571 entry_name = 'section/cbfs/u-boot'
3572 control.WriteEntry(updated_fname, entry_name, expected,
3573 allow_resize=True)
3574 data = control.ReadEntry(updated_fname, entry_name)
3575 self.assertEqual(expected, data)
3576
3577 def testReplaceResizeCbfs(self):
3578 """Test replacing a single file in CBFS with one of a different size"""
3579 self._CheckLz4()
3580 expected = U_BOOT_DATA + b'x'
3581 data = self._DoReadFileRealDtb('142_replace_cbfs.dts')
Simon Glass80025522022-01-29 14:14:04 -07003582 updated_fname = tools.get_output_filename('image-updated.bin')
3583 tools.write_file(updated_fname, data)
Simon Glass37fdd142019-07-20 12:24:06 -06003584 entry_name = 'section/cbfs/u-boot'
3585 control.WriteEntry(updated_fname, entry_name, expected,
3586 allow_resize=True)
3587 data = control.ReadEntry(updated_fname, entry_name)
3588 self.assertEqual(expected, data)
3589
Simon Glass30033c22019-07-20 12:24:15 -06003590 def _SetupForReplace(self):
3591 """Set up some files to use to replace entries
3592
3593 This generates an image, copies it to a new file, extracts all the files
3594 in it and updates some of them
3595
3596 Returns:
3597 List
3598 Image filename
3599 Output directory
3600 Expected values for updated entries, each a string
3601 """
3602 data = self._DoReadFileRealDtb('143_replace_all.dts')
3603
Simon Glass80025522022-01-29 14:14:04 -07003604 updated_fname = tools.get_output_filename('image-updated.bin')
3605 tools.write_file(updated_fname, data)
Simon Glass30033c22019-07-20 12:24:15 -06003606
3607 outdir = os.path.join(self._indir, 'extract')
3608 einfos = control.ExtractEntries(updated_fname, None, outdir, [])
3609
3610 expected1 = b'x' + U_BOOT_DATA + b'y'
3611 u_boot_fname1 = os.path.join(outdir, 'u-boot')
Simon Glass80025522022-01-29 14:14:04 -07003612 tools.write_file(u_boot_fname1, expected1)
Simon Glass30033c22019-07-20 12:24:15 -06003613
3614 expected2 = b'a' + U_BOOT_DATA + b'b'
3615 u_boot_fname2 = os.path.join(outdir, 'u-boot2')
Simon Glass80025522022-01-29 14:14:04 -07003616 tools.write_file(u_boot_fname2, expected2)
Simon Glass30033c22019-07-20 12:24:15 -06003617
3618 expected_text = b'not the same text'
3619 text_fname = os.path.join(outdir, 'text')
Simon Glass80025522022-01-29 14:14:04 -07003620 tools.write_file(text_fname, expected_text)
Simon Glass30033c22019-07-20 12:24:15 -06003621
3622 dtb_fname = os.path.join(outdir, 'u-boot-dtb')
3623 dtb = fdt.FdtScan(dtb_fname)
3624 node = dtb.GetNode('/binman/text')
3625 node.AddString('my-property', 'the value')
3626 dtb.Sync(auto_resize=True)
3627 dtb.Flush()
3628
3629 return updated_fname, outdir, expected1, expected2, expected_text
3630
3631 def _CheckReplaceMultiple(self, entry_paths):
3632 """Handle replacing the contents of multiple entries
3633
3634 Args:
3635 entry_paths: List of entry paths to replace
3636
3637 Returns:
3638 List
3639 Dict of entries in the image:
3640 key: Entry name
3641 Value: Entry object
3642 Expected values for updated entries, each a string
3643 """
3644 updated_fname, outdir, expected1, expected2, expected_text = (
3645 self._SetupForReplace())
3646 control.ReplaceEntries(updated_fname, None, outdir, entry_paths)
3647
3648 image = Image.FromFile(updated_fname)
3649 image.LoadData()
3650 return image.GetEntries(), expected1, expected2, expected_text
3651
3652 def testReplaceAll(self):
3653 """Test replacing the contents of all entries"""
3654 entries, expected1, expected2, expected_text = (
3655 self._CheckReplaceMultiple([]))
3656 data = entries['u-boot'].data
3657 self.assertEqual(expected1, data)
3658
3659 data = entries['u-boot2'].data
3660 self.assertEqual(expected2, data)
3661
3662 data = entries['text'].data
3663 self.assertEqual(expected_text, data)
3664
3665 # Check that the device tree is updated
3666 data = entries['u-boot-dtb'].data
3667 dtb = fdt.Fdt.FromData(data)
3668 dtb.Scan()
3669 node = dtb.GetNode('/binman/text')
3670 self.assertEqual('the value', node.props['my-property'].value)
3671
3672 def testReplaceSome(self):
3673 """Test replacing the contents of a few entries"""
3674 entries, expected1, expected2, expected_text = (
3675 self._CheckReplaceMultiple(['u-boot2', 'text']))
3676
3677 # This one should not change
3678 data = entries['u-boot'].data
3679 self.assertEqual(U_BOOT_DATA, data)
3680
3681 data = entries['u-boot2'].data
3682 self.assertEqual(expected2, data)
3683
3684 data = entries['text'].data
3685 self.assertEqual(expected_text, data)
3686
3687 def testReplaceCmd(self):
3688 """Test replacing a file fron an image on the command line"""
3689 self._DoReadFileRealDtb('143_replace_all.dts')
3690
3691 try:
3692 tmpdir, updated_fname = self._SetupImageInTmpdir()
3693
3694 fname = os.path.join(tmpdir, 'update-u-boot.bin')
3695 expected = b'x' * len(U_BOOT_DATA)
Simon Glass80025522022-01-29 14:14:04 -07003696 tools.write_file(fname, expected)
Simon Glass30033c22019-07-20 12:24:15 -06003697
3698 self._DoBinman('replace', '-i', updated_fname, 'u-boot', '-f', fname)
Simon Glass80025522022-01-29 14:14:04 -07003699 data = tools.read_file(updated_fname)
Simon Glass30033c22019-07-20 12:24:15 -06003700 self.assertEqual(expected, data[:len(expected)])
3701 map_fname = os.path.join(tmpdir, 'image-updated.map')
3702 self.assertFalse(os.path.exists(map_fname))
3703 finally:
3704 shutil.rmtree(tmpdir)
3705
3706 def testReplaceCmdSome(self):
3707 """Test replacing some files fron an image on the command line"""
3708 updated_fname, outdir, expected1, expected2, expected_text = (
3709 self._SetupForReplace())
3710
3711 self._DoBinman('replace', '-i', updated_fname, '-I', outdir,
3712 'u-boot2', 'text')
3713
Simon Glass80025522022-01-29 14:14:04 -07003714 tools.prepare_output_dir(None)
Simon Glass30033c22019-07-20 12:24:15 -06003715 image = Image.FromFile(updated_fname)
3716 image.LoadData()
3717 entries = image.GetEntries()
3718
3719 # This one should not change
3720 data = entries['u-boot'].data
3721 self.assertEqual(U_BOOT_DATA, data)
3722
3723 data = entries['u-boot2'].data
3724 self.assertEqual(expected2, data)
3725
3726 data = entries['text'].data
3727 self.assertEqual(expected_text, data)
3728
3729 def testReplaceMissing(self):
3730 """Test replacing entries where the file is missing"""
3731 updated_fname, outdir, expected1, expected2, expected_text = (
3732 self._SetupForReplace())
3733
3734 # Remove one of the files, to generate a warning
3735 u_boot_fname1 = os.path.join(outdir, 'u-boot')
3736 os.remove(u_boot_fname1)
3737
Simon Glass14d64e32025-04-29 07:21:59 -06003738 with terminal.capture() as (stdout, stderr):
Simon Glass30033c22019-07-20 12:24:15 -06003739 control.ReplaceEntries(updated_fname, None, outdir, [])
3740 self.assertIn("Skipping entry '/u-boot' from missing file",
Simon Glass6e02f7c2020-07-09 18:39:39 -06003741 stderr.getvalue())
Simon Glass30033c22019-07-20 12:24:15 -06003742
3743 def testReplaceCmdMap(self):
3744 """Test replacing a file fron an image on the command line"""
3745 self._DoReadFileRealDtb('143_replace_all.dts')
3746
3747 try:
3748 tmpdir, updated_fname = self._SetupImageInTmpdir()
3749
3750 fname = os.path.join(self._indir, 'update-u-boot.bin')
3751 expected = b'x' * len(U_BOOT_DATA)
Simon Glass80025522022-01-29 14:14:04 -07003752 tools.write_file(fname, expected)
Simon Glass30033c22019-07-20 12:24:15 -06003753
3754 self._DoBinman('replace', '-i', updated_fname, 'u-boot',
3755 '-f', fname, '-m')
3756 map_fname = os.path.join(tmpdir, 'image-updated.map')
3757 self.assertTrue(os.path.exists(map_fname))
3758 finally:
3759 shutil.rmtree(tmpdir)
3760
3761 def testReplaceNoEntryPaths(self):
3762 """Test replacing an entry without an entry path"""
3763 self._DoReadFileRealDtb('143_replace_all.dts')
Simon Glass80025522022-01-29 14:14:04 -07003764 image_fname = tools.get_output_filename('image.bin')
Simon Glass30033c22019-07-20 12:24:15 -06003765 with self.assertRaises(ValueError) as e:
3766 control.ReplaceEntries(image_fname, 'fname', None, [])
3767 self.assertIn('Must specify an entry path to read with -f',
3768 str(e.exception))
3769
3770 def testReplaceTooManyEntryPaths(self):
3771 """Test extracting some entries"""
3772 self._DoReadFileRealDtb('143_replace_all.dts')
Simon Glass80025522022-01-29 14:14:04 -07003773 image_fname = tools.get_output_filename('image.bin')
Simon Glass30033c22019-07-20 12:24:15 -06003774 with self.assertRaises(ValueError) as e:
3775 control.ReplaceEntries(image_fname, 'fname', None, ['a', 'b'])
3776 self.assertIn('Must specify exactly one entry path to write with -f',
3777 str(e.exception))
3778
Simon Glass0b074d62019-08-24 07:22:48 -06003779 def testPackReset16(self):
3780 """Test that an image with an x86 reset16 region can be created"""
3781 data = self._DoReadFile('144_x86_reset16.dts')
3782 self.assertEqual(X86_RESET16_DATA, data[:len(X86_RESET16_DATA)])
3783
3784 def testPackReset16Spl(self):
3785 """Test that an image with an x86 reset16-spl region can be created"""
3786 data = self._DoReadFile('145_x86_reset16_spl.dts')
3787 self.assertEqual(X86_RESET16_SPL_DATA, data[:len(X86_RESET16_SPL_DATA)])
3788
3789 def testPackReset16Tpl(self):
3790 """Test that an image with an x86 reset16-tpl region can be created"""
3791 data = self._DoReadFile('146_x86_reset16_tpl.dts')
3792 self.assertEqual(X86_RESET16_TPL_DATA, data[:len(X86_RESET16_TPL_DATA)])
3793
Simon Glass232f90c2019-08-24 07:22:50 -06003794 def testPackIntelFit(self):
3795 """Test that an image with an Intel FIT and pointer can be created"""
3796 data = self._DoReadFile('147_intel_fit.dts')
3797 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
3798 fit = data[16:32];
3799 self.assertEqual(b'_FIT_ \x01\x00\x00\x00\x00\x01\x80}' , fit)
3800 ptr = struct.unpack('<i', data[0x40:0x44])[0]
3801
3802 image = control.images['image']
3803 entries = image.GetEntries()
Simon Glassed836ac2025-02-26 09:26:17 -07003804 expected_ptr = entries['intel-fit'].image_pos #- (1 << 32)
3805 self.assertEqual(expected_ptr, ptr + (1 << 32))
Simon Glass232f90c2019-08-24 07:22:50 -06003806
3807 def testPackIntelFitMissing(self):
3808 """Test detection of a FIT pointer with not FIT region"""
3809 with self.assertRaises(ValueError) as e:
3810 self._DoReadFile('148_intel_fit_missing.dts')
3811 self.assertIn("'intel-fit-ptr' section must have an 'intel-fit' sibling",
3812 str(e.exception))
3813
Simon Glass72555fa2019-11-06 17:22:44 -07003814 def _CheckSymbolsTplSection(self, dts, expected_vals):
3815 data = self._DoReadFile(dts)
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003816 sym_values = struct.pack('<LLQLL', elf.BINMAN_SYM_MAGIC_VALUE, *expected_vals)
Simon Glass3eb5b202019-08-24 07:23:00 -06003817 upto1 = 4 + len(U_BOOT_SPL_DATA)
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003818 expected1 = tools.get_bytes(0xff, 4) + sym_values + U_BOOT_SPL_DATA[24:]
Simon Glass3eb5b202019-08-24 07:23:00 -06003819 self.assertEqual(expected1, data[:upto1])
3820
3821 upto2 = upto1 + 1 + len(U_BOOT_SPL_DATA)
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003822 expected2 = tools.get_bytes(0xff, 1) + sym_values + U_BOOT_SPL_DATA[24:]
Simon Glass3eb5b202019-08-24 07:23:00 -06003823 self.assertEqual(expected2, data[upto1:upto2])
3824
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003825 upto3 = 0x3c + len(U_BOOT_DATA)
Simon Glass80025522022-01-29 14:14:04 -07003826 expected3 = tools.get_bytes(0xff, 1) + U_BOOT_DATA
Simon Glass3eb5b202019-08-24 07:23:00 -06003827 self.assertEqual(expected3, data[upto2:upto3])
3828
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003829 expected4 = sym_values + U_BOOT_TPL_DATA[24:]
Simon Glass72555fa2019-11-06 17:22:44 -07003830 self.assertEqual(expected4, data[upto3:upto3 + len(U_BOOT_TPL_DATA)])
3831
3832 def testSymbolsTplSection(self):
3833 """Test binman can assign symbols embedded in U-Boot TPL in a section"""
3834 self._SetupSplElf('u_boot_binman_syms')
3835 self._SetupTplElf('u_boot_binman_syms')
3836 self._CheckSymbolsTplSection('149_symbols_tpl.dts',
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003837 [0x04, 0x20, 0x10 + 0x3c, 0x04])
Simon Glass72555fa2019-11-06 17:22:44 -07003838
3839 def testSymbolsTplSectionX86(self):
3840 """Test binman can assign symbols in a section with end-at-4gb"""
3841 self._SetupSplElf('u_boot_binman_syms_x86')
3842 self._SetupTplElf('u_boot_binman_syms_x86')
3843 self._CheckSymbolsTplSection('155_symbols_tpl_x86.dts',
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003844 [0xffffff04, 0xffffff20, 0xffffff3c,
Simon Glass72555fa2019-11-06 17:22:44 -07003845 0x04])
Simon Glass3eb5b202019-08-24 07:23:00 -06003846
Simon Glass98c59572019-08-24 07:23:03 -06003847 def testPackX86RomIfwiSectiom(self):
3848 """Test that a section can be placed in an IFWI region"""
3849 self._SetupIfwi('fitimage.bin')
3850 data = self._DoReadFile('151_x86_rom_ifwi_section.dts')
3851 self._CheckIfwi(data)
3852
Simon Glassba7985d2019-08-24 07:23:07 -06003853 def testPackFspM(self):
3854 """Test that an image with a FSP memory-init binary can be created"""
3855 data = self._DoReadFile('152_intel_fsp_m.dts')
3856 self.assertEqual(FSP_M_DATA, data[:len(FSP_M_DATA)])
3857
Simon Glass4d9086d2019-10-20 21:31:35 -06003858 def testPackFspS(self):
3859 """Test that an image with a FSP silicon-init binary can be created"""
3860 data = self._DoReadFile('153_intel_fsp_s.dts')
3861 self.assertEqual(FSP_S_DATA, data[:len(FSP_S_DATA)])
Simon Glassba7985d2019-08-24 07:23:07 -06003862
Simon Glass9ea87b22019-10-20 21:31:36 -06003863 def testPackFspT(self):
3864 """Test that an image with a FSP temp-ram-init binary can be created"""
3865 data = self._DoReadFile('154_intel_fsp_t.dts')
3866 self.assertEqual(FSP_T_DATA, data[:len(FSP_T_DATA)])
3867
Simon Glass48f3aad2020-07-09 18:39:31 -06003868 def testMkimage(self):
3869 """Test using mkimage to build an image"""
Marek Vasutf7413f02023-07-18 07:23:58 -06003870 self._SetupSplElf()
Simon Glass48f3aad2020-07-09 18:39:31 -06003871 data = self._DoReadFile('156_mkimage.dts')
3872
3873 # Just check that the data appears in the file somewhere
3874 self.assertIn(U_BOOT_SPL_DATA, data)
3875
Simon Glass66152ce2022-01-09 20:14:09 -07003876 def testMkimageMissing(self):
3877 """Test that binman still produces an image if mkimage is missing"""
Marek Vasutf7413f02023-07-18 07:23:58 -06003878 self._SetupSplElf()
Simon Glass14d64e32025-04-29 07:21:59 -06003879 with terminal.capture() as (_, stderr):
Simon Glass66152ce2022-01-09 20:14:09 -07003880 self._DoTestFile('156_mkimage.dts',
3881 force_missing_bintools='mkimage')
3882 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07003883 self.assertRegex(err, "Image 'image'.*missing bintools.*: mkimage")
Simon Glass66152ce2022-01-09 20:14:09 -07003884
Simon Glass5e560182020-07-09 18:39:36 -06003885 def testExtblob(self):
3886 """Test an image with an external blob"""
3887 data = self._DoReadFile('157_blob_ext.dts')
3888 self.assertEqual(REFCODE_DATA, data)
3889
3890 def testExtblobMissing(self):
3891 """Test an image with a missing external blob"""
3892 with self.assertRaises(ValueError) as e:
3893 self._DoReadFile('158_blob_ext_missing.dts')
3894 self.assertIn("Filename 'missing-file' not found in input path",
3895 str(e.exception))
3896
Simon Glass5d94cc62020-07-09 18:39:38 -06003897 def testExtblobMissingOk(self):
3898 """Test an image with an missing external blob that is allowed"""
Simon Glass14d64e32025-04-29 07:21:59 -06003899 with terminal.capture() as (stdout, stderr):
Simon Glass6bce5dc2022-11-09 19:14:42 -07003900 ret = self._DoTestFile('158_blob_ext_missing.dts',
3901 allow_missing=True)
3902 self.assertEqual(103, ret)
Simon Glassa003cd32020-07-09 18:39:40 -06003903 err = stderr.getvalue()
Jonas Karlmanda423fc2023-07-18 20:34:39 +00003904 self.assertIn('(missing-file)', err)
Simon Glass49cd2b32023-02-07 14:34:18 -07003905 self.assertRegex(err, "Image 'image'.*missing.*: blob-ext")
Simon Glass6bce5dc2022-11-09 19:14:42 -07003906 self.assertIn('Some images are invalid', err)
3907
3908 def testExtblobMissingOkFlag(self):
3909 """Test an image with an missing external blob allowed with -W"""
Simon Glass14d64e32025-04-29 07:21:59 -06003910 with terminal.capture() as (stdout, stderr):
Simon Glass6bce5dc2022-11-09 19:14:42 -07003911 ret = self._DoTestFile('158_blob_ext_missing.dts',
3912 allow_missing=True, ignore_missing=True)
3913 self.assertEqual(0, ret)
3914 err = stderr.getvalue()
Jonas Karlmanda423fc2023-07-18 20:34:39 +00003915 self.assertIn('(missing-file)', err)
Simon Glass49cd2b32023-02-07 14:34:18 -07003916 self.assertRegex(err, "Image 'image'.*missing.*: blob-ext")
Simon Glass6bce5dc2022-11-09 19:14:42 -07003917 self.assertIn('Some images are invalid', err)
Simon Glassa003cd32020-07-09 18:39:40 -06003918
3919 def testExtblobMissingOkSect(self):
3920 """Test an image with an missing external blob that is allowed"""
Simon Glass14d64e32025-04-29 07:21:59 -06003921 with terminal.capture() as (stdout, stderr):
Simon Glassa003cd32020-07-09 18:39:40 -06003922 self._DoTestFile('159_blob_ext_missing_sect.dts',
3923 allow_missing=True)
3924 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07003925 self.assertRegex(err, "Image 'image'.*missing.*: blob-ext blob-ext2")
Simon Glass5d94cc62020-07-09 18:39:38 -06003926
Simon Glasse88cef92020-07-09 18:39:41 -06003927 def testPackX86RomMeMissingDesc(self):
3928 """Test that an missing Intel descriptor entry is allowed"""
Simon Glass14d64e32025-04-29 07:21:59 -06003929 with terminal.capture() as (stdout, stderr):
Simon Glass14c596c2020-07-25 15:11:19 -06003930 self._DoTestFile('164_x86_rom_me_missing.dts', allow_missing=True)
Simon Glasse88cef92020-07-09 18:39:41 -06003931 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07003932 self.assertRegex(err, "Image 'image'.*missing.*: intel-descriptor")
Simon Glasse88cef92020-07-09 18:39:41 -06003933
3934 def testPackX86RomMissingIfwi(self):
3935 """Test that an x86 ROM with Integrated Firmware Image can be created"""
3936 self._SetupIfwi('fitimage.bin')
3937 pathname = os.path.join(self._indir, 'fitimage.bin')
3938 os.remove(pathname)
Simon Glass14d64e32025-04-29 07:21:59 -06003939 with terminal.capture() as (stdout, stderr):
Simon Glasse88cef92020-07-09 18:39:41 -06003940 self._DoTestFile('111_x86_rom_ifwi.dts', allow_missing=True)
3941 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07003942 self.assertRegex(err, "Image 'image'.*missing.*: intel-ifwi")
Simon Glasse88cef92020-07-09 18:39:41 -06003943
Simon Glass2a0fa982022-02-11 13:23:21 -07003944 def testPackOverlapZero(self):
Simon Glassd70829a2020-07-09 18:39:42 -06003945 """Test that zero-size overlapping regions are ignored"""
3946 self._DoTestFile('160_pack_overlap_zero.dts')
3947
Alper Nebi Yasak09fb0612022-02-08 01:08:04 +03003948 def _CheckSimpleFitData(self, fit_data, kernel_data, fdt1_data):
Simon Glass45d556d2020-07-09 18:39:45 -06003949 # The data should be inside the FIT
3950 dtb = fdt.Fdt.FromData(fit_data)
3951 dtb.Scan()
3952 fnode = dtb.GetNode('/images/kernel')
3953 self.assertIn('data', fnode.props)
3954
3955 fname = os.path.join(self._indir, 'fit_data.fit')
Simon Glass80025522022-01-29 14:14:04 -07003956 tools.write_file(fname, fit_data)
3957 out = tools.run('dumpimage', '-l', fname)
Simon Glass45d556d2020-07-09 18:39:45 -06003958
3959 # Check a few features to make sure the plumbing works. We don't need
3960 # to test the operation of mkimage or dumpimage here. First convert the
3961 # output into a dict where the keys are the fields printed by dumpimage
3962 # and the values are a list of values for each field
3963 lines = out.splitlines()
3964
3965 # Converts "Compression: gzip compressed" into two groups:
3966 # 'Compression' and 'gzip compressed'
3967 re_line = re.compile(r'^ *([^:]*)(?:: *(.*))?$')
3968 vals = collections.defaultdict(list)
3969 for line in lines:
3970 mat = re_line.match(line)
3971 vals[mat.group(1)].append(mat.group(2))
3972
Brandon Maiera657bc62024-06-04 16:16:05 +00003973 self.assertEqual('FIT description: test-desc', lines[0])
Simon Glass45d556d2020-07-09 18:39:45 -06003974 self.assertIn('Created:', lines[1])
3975 self.assertIn('Image 0 (kernel)', vals)
3976 self.assertIn('Hash value', vals)
3977 data_sizes = vals.get('Data Size')
3978 self.assertIsNotNone(data_sizes)
3979 self.assertEqual(2, len(data_sizes))
3980 # Format is "4 Bytes = 0.00 KiB = 0.00 MiB" so take the first word
Alper Nebi Yasak09fb0612022-02-08 01:08:04 +03003981 self.assertEqual(len(kernel_data), int(data_sizes[0].split()[0]))
3982 self.assertEqual(len(fdt1_data), int(data_sizes[1].split()[0]))
3983
Alper Nebi Yasak1a0ee0f2022-03-27 18:31:47 +03003984 # Check if entry listing correctly omits /images/
3985 image = control.images['image']
3986 fit_entry = image.GetEntries()['fit']
3987 subentries = list(fit_entry.GetEntries().keys())
3988 expected = ['kernel', 'fdt-1']
3989 self.assertEqual(expected, subentries)
3990
Alper Nebi Yasak09fb0612022-02-08 01:08:04 +03003991 def testSimpleFit(self):
3992 """Test an image with a FIT inside"""
Marek Vasutf7413f02023-07-18 07:23:58 -06003993 self._SetupSplElf()
Alper Nebi Yasak09fb0612022-02-08 01:08:04 +03003994 data = self._DoReadFile('161_fit.dts')
3995 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
3996 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
3997 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
3998
3999 self._CheckSimpleFitData(fit_data, U_BOOT_DATA, U_BOOT_SPL_DTB_DATA)
4000
4001 def testSimpleFitExpandsSubentries(self):
4002 """Test that FIT images expand their subentries"""
4003 data = self._DoReadFileDtb('161_fit.dts', use_expanded=True)[0]
4004 self.assertEqual(U_BOOT_EXP_DATA, data[:len(U_BOOT_EXP_DATA)])
4005 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
4006 fit_data = data[len(U_BOOT_EXP_DATA):-len(U_BOOT_NODTB_DATA)]
4007
4008 self._CheckSimpleFitData(fit_data, U_BOOT_EXP_DATA, U_BOOT_SPL_DTB_DATA)
Simon Glass45d556d2020-07-09 18:39:45 -06004009
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03004010 def testSimpleFitImagePos(self):
4011 """Test that we have correct image-pos for FIT subentries"""
4012 data, _, _, out_dtb_fname = self._DoReadFileDtb('161_fit.dts',
4013 update_dtb=True)
4014 dtb = fdt.Fdt(out_dtb_fname)
4015 dtb.Scan()
4016 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
4017
Simon Glassb7bad182022-03-05 20:19:01 -07004018 self.maxDiff = None
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03004019 self.assertEqual({
4020 'image-pos': 0,
4021 'offset': 0,
4022 'size': 1890,
4023
4024 'u-boot:image-pos': 0,
4025 'u-boot:offset': 0,
4026 'u-boot:size': 4,
4027
4028 'fit:image-pos': 4,
4029 'fit:offset': 4,
4030 'fit:size': 1840,
4031
Simon Glassb7bad182022-03-05 20:19:01 -07004032 'fit/images/kernel:image-pos': 304,
4033 'fit/images/kernel:offset': 300,
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03004034 'fit/images/kernel:size': 4,
4035
Simon Glassb7bad182022-03-05 20:19:01 -07004036 'fit/images/kernel/u-boot:image-pos': 304,
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03004037 'fit/images/kernel/u-boot:offset': 0,
4038 'fit/images/kernel/u-boot:size': 4,
4039
Simon Glassb7bad182022-03-05 20:19:01 -07004040 'fit/images/fdt-1:image-pos': 552,
4041 'fit/images/fdt-1:offset': 548,
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03004042 'fit/images/fdt-1:size': 6,
4043
Simon Glassb7bad182022-03-05 20:19:01 -07004044 'fit/images/fdt-1/u-boot-spl-dtb:image-pos': 552,
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03004045 'fit/images/fdt-1/u-boot-spl-dtb:offset': 0,
4046 'fit/images/fdt-1/u-boot-spl-dtb:size': 6,
4047
4048 'u-boot-nodtb:image-pos': 1844,
4049 'u-boot-nodtb:offset': 1844,
4050 'u-boot-nodtb:size': 46,
4051 }, props)
4052
4053 # Actually check the data is where we think it is
4054 for node, expected in [
4055 ("u-boot", U_BOOT_DATA),
4056 ("fit/images/kernel", U_BOOT_DATA),
4057 ("fit/images/kernel/u-boot", U_BOOT_DATA),
4058 ("fit/images/fdt-1", U_BOOT_SPL_DTB_DATA),
4059 ("fit/images/fdt-1/u-boot-spl-dtb", U_BOOT_SPL_DTB_DATA),
4060 ("u-boot-nodtb", U_BOOT_NODTB_DATA),
4061 ]:
4062 image_pos = props[f"{node}:image-pos"]
4063 size = props[f"{node}:size"]
4064 self.assertEqual(len(expected), size)
4065 self.assertEqual(expected, data[image_pos:image_pos+size])
4066
Simon Glass45d556d2020-07-09 18:39:45 -06004067 def testFitExternal(self):
Simon Glass31ee50f2020-09-01 05:13:55 -06004068 """Test an image with an FIT with external images"""
Simon Glass45d556d2020-07-09 18:39:45 -06004069 data = self._DoReadFile('162_fit_external.dts')
4070 fit_data = data[len(U_BOOT_DATA):-2] # _testing is 2 bytes
4071
Simon Glass7932c882022-01-09 20:13:39 -07004072 # Size of the external-data region as set up by mkimage
4073 external_data_size = len(U_BOOT_DATA) + 2
4074 expected_size = (len(U_BOOT_DATA) + 0x400 +
Simon Glass80025522022-01-29 14:14:04 -07004075 tools.align(external_data_size, 4) +
Simon Glass7932c882022-01-09 20:13:39 -07004076 len(U_BOOT_NODTB_DATA))
4077
Simon Glass45d556d2020-07-09 18:39:45 -06004078 # The data should be outside the FIT
4079 dtb = fdt.Fdt.FromData(fit_data)
4080 dtb.Scan()
4081 fnode = dtb.GetNode('/images/kernel')
4082 self.assertNotIn('data', fnode.props)
Simon Glass7932c882022-01-09 20:13:39 -07004083 self.assertEqual(len(U_BOOT_DATA),
4084 fdt_util.fdt32_to_cpu(fnode.props['data-size'].value))
4085 fit_pos = 0x400;
4086 self.assertEqual(
4087 fit_pos,
4088 fdt_util.fdt32_to_cpu(fnode.props['data-position'].value))
4089
Brandon Maiera657bc62024-06-04 16:16:05 +00004090 self.assertEqual(expected_size, len(data))
Simon Glass7932c882022-01-09 20:13:39 -07004091 actual_pos = len(U_BOOT_DATA) + fit_pos
4092 self.assertEqual(U_BOOT_DATA + b'aa',
4093 data[actual_pos:actual_pos + external_data_size])
Simon Glassfb30e292019-07-20 12:23:51 -06004094
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03004095 def testFitExternalImagePos(self):
4096 """Test that we have correct image-pos for external FIT subentries"""
4097 data, _, _, out_dtb_fname = self._DoReadFileDtb('162_fit_external.dts',
4098 update_dtb=True)
4099 dtb = fdt.Fdt(out_dtb_fname)
4100 dtb.Scan()
4101 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
4102
4103 self.assertEqual({
4104 'image-pos': 0,
4105 'offset': 0,
4106 'size': 1082,
4107
4108 'u-boot:image-pos': 0,
4109 'u-boot:offset': 0,
4110 'u-boot:size': 4,
4111
4112 'fit:size': 1032,
4113 'fit:offset': 4,
4114 'fit:image-pos': 4,
4115
4116 'fit/images/kernel:size': 4,
4117 'fit/images/kernel:offset': 1024,
4118 'fit/images/kernel:image-pos': 1028,
4119
4120 'fit/images/kernel/u-boot:size': 4,
4121 'fit/images/kernel/u-boot:offset': 0,
4122 'fit/images/kernel/u-boot:image-pos': 1028,
4123
4124 'fit/images/fdt-1:size': 2,
4125 'fit/images/fdt-1:offset': 1028,
4126 'fit/images/fdt-1:image-pos': 1032,
4127
4128 'fit/images/fdt-1/_testing:size': 2,
4129 'fit/images/fdt-1/_testing:offset': 0,
4130 'fit/images/fdt-1/_testing:image-pos': 1032,
4131
4132 'u-boot-nodtb:image-pos': 1036,
4133 'u-boot-nodtb:offset': 1036,
4134 'u-boot-nodtb:size': 46,
4135 }, props)
4136
4137 # Actually check the data is where we think it is
4138 for node, expected in [
4139 ("u-boot", U_BOOT_DATA),
4140 ("fit/images/kernel", U_BOOT_DATA),
4141 ("fit/images/kernel/u-boot", U_BOOT_DATA),
4142 ("fit/images/fdt-1", b'aa'),
4143 ("fit/images/fdt-1/_testing", b'aa'),
4144 ("u-boot-nodtb", U_BOOT_NODTB_DATA),
4145 ]:
4146 image_pos = props[f"{node}:image-pos"]
4147 size = props[f"{node}:size"]
4148 self.assertEqual(len(expected), size)
4149 self.assertEqual(expected, data[image_pos:image_pos+size])
4150
Simon Glass66152ce2022-01-09 20:14:09 -07004151 def testFitMissing(self):
Simon Glass039d65f2023-03-02 17:02:43 -07004152 """Test that binman complains if mkimage is missing"""
4153 with self.assertRaises(ValueError) as e:
4154 self._DoTestFile('162_fit_external.dts',
4155 force_missing_bintools='mkimage')
4156 self.assertIn("Node '/binman/fit': Missing tool: 'mkimage'",
4157 str(e.exception))
4158
4159 def testFitMissingOK(self):
Simon Glass66152ce2022-01-09 20:14:09 -07004160 """Test that binman still produces a FIT image if mkimage is missing"""
Simon Glass14d64e32025-04-29 07:21:59 -06004161 with terminal.capture() as (_, stderr):
Simon Glass039d65f2023-03-02 17:02:43 -07004162 self._DoTestFile('162_fit_external.dts', allow_missing=True,
Simon Glass66152ce2022-01-09 20:14:09 -07004163 force_missing_bintools='mkimage')
4164 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07004165 self.assertRegex(err, "Image 'image'.*missing bintools.*: mkimage")
Simon Glass66152ce2022-01-09 20:14:09 -07004166
Alper Nebi Yasak6aae2392020-08-31 12:58:18 +03004167 def testSectionIgnoreHashSignature(self):
4168 """Test that sections ignore hash, signature nodes for its data"""
4169 data = self._DoReadFile('165_section_ignore_hash_signature.dts')
4170 expected = (U_BOOT_DATA + U_BOOT_DATA)
4171 self.assertEqual(expected, data)
4172
Alper Nebi Yasakc5030602020-08-31 12:58:19 +03004173 def testPadInSections(self):
4174 """Test pad-before, pad-after for entries in sections"""
Simon Glassd12599d2020-10-26 17:40:09 -06004175 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4176 '166_pad_in_sections.dts', update_dtb=True)
Simon Glass80025522022-01-29 14:14:04 -07004177 expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) +
4178 U_BOOT_DATA + tools.get_bytes(ord('!'), 6) +
Alper Nebi Yasakc5030602020-08-31 12:58:19 +03004179 U_BOOT_DATA)
4180 self.assertEqual(expected, data)
4181
Simon Glassd12599d2020-10-26 17:40:09 -06004182 dtb = fdt.Fdt(out_dtb_fname)
4183 dtb.Scan()
4184 props = self._GetPropTree(dtb, ['size', 'image-pos', 'offset'])
4185 expected = {
4186 'image-pos': 0,
4187 'offset': 0,
4188 'size': 12 + 6 + 3 * len(U_BOOT_DATA),
4189
4190 'section:image-pos': 0,
4191 'section:offset': 0,
4192 'section:size': 12 + 6 + 3 * len(U_BOOT_DATA),
4193
4194 'section/before:image-pos': 0,
4195 'section/before:offset': 0,
4196 'section/before:size': len(U_BOOT_DATA),
4197
4198 'section/u-boot:image-pos': 4,
4199 'section/u-boot:offset': 4,
4200 'section/u-boot:size': 12 + len(U_BOOT_DATA) + 6,
4201
4202 'section/after:image-pos': 26,
4203 'section/after:offset': 26,
4204 'section/after:size': len(U_BOOT_DATA),
4205 }
4206 self.assertEqual(expected, props)
4207
Alper Nebi Yasakc261ec12020-08-31 12:58:20 +03004208 def testFitImageSubentryAlignment(self):
4209 """Test relative alignability of FIT image subentries"""
Alper Nebi Yasakd4553262022-02-08 01:08:07 +03004210 self._SetupSplElf()
Alper Nebi Yasakc261ec12020-08-31 12:58:20 +03004211 entry_args = {
4212 'test-id': TEXT_DATA,
4213 }
4214 data, _, _, _ = self._DoReadFileDtb('167_fit_image_subentry_alignment.dts',
4215 entry_args=entry_args)
4216 dtb = fdt.Fdt.FromData(data)
4217 dtb.Scan()
4218
4219 node = dtb.GetNode('/images/kernel')
4220 data = dtb.GetProps(node)["data"].bytes
4221 align_pad = 0x10 - (len(U_BOOT_SPL_DATA) % 0x10)
Simon Glass80025522022-01-29 14:14:04 -07004222 expected = (tools.get_bytes(0, 0x20) + U_BOOT_SPL_DATA +
4223 tools.get_bytes(0, align_pad) + U_BOOT_DATA)
Alper Nebi Yasakc261ec12020-08-31 12:58:20 +03004224 self.assertEqual(expected, data)
4225
4226 node = dtb.GetNode('/images/fdt-1')
4227 data = dtb.GetProps(node)["data"].bytes
Simon Glass80025522022-01-29 14:14:04 -07004228 expected = (U_BOOT_SPL_DTB_DATA + tools.get_bytes(0, 20) +
4229 tools.to_bytes(TEXT_DATA) + tools.get_bytes(0, 30) +
Alper Nebi Yasakc261ec12020-08-31 12:58:20 +03004230 U_BOOT_DTB_DATA)
4231 self.assertEqual(expected, data)
4232
4233 def testFitExtblobMissingOk(self):
4234 """Test a FIT with a missing external blob that is allowed"""
Simon Glass14d64e32025-04-29 07:21:59 -06004235 with terminal.capture() as (stdout, stderr):
Alper Nebi Yasakc261ec12020-08-31 12:58:20 +03004236 self._DoTestFile('168_fit_missing_blob.dts',
4237 allow_missing=True)
4238 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07004239 self.assertRegex(err, "Image 'image'.*missing.*: atf-bl31")
Alper Nebi Yasakc261ec12020-08-31 12:58:20 +03004240
Simon Glass21db0ff2020-09-01 05:13:54 -06004241 def testBlobNamedByArgMissing(self):
4242 """Test handling of a missing entry arg"""
4243 with self.assertRaises(ValueError) as e:
4244 self._DoReadFile('068_blob_named_by_arg.dts')
4245 self.assertIn("Missing required properties/entry args: cros-ec-rw-path",
4246 str(e.exception))
4247
Simon Glass559c4de2020-09-01 05:13:58 -06004248 def testPackBl31(self):
4249 """Test that an image with an ATF BL31 binary can be created"""
4250 data = self._DoReadFile('169_atf_bl31.dts')
4251 self.assertEqual(ATF_BL31_DATA, data[:len(ATF_BL31_DATA)])
4252
Samuel Holland9d8cc632020-10-21 21:12:15 -05004253 def testPackScp(self):
4254 """Test that an image with an SCP binary can be created"""
4255 data = self._DoReadFile('172_scp.dts')
4256 self.assertEqual(SCP_DATA, data[:len(SCP_DATA)])
4257
Simon Glassd2a9d6e2024-08-26 13:11:37 -06004258 def CheckFitFdt(self, dts='170_fit_fdt.dts', use_fdt_list=True,
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004259 default_dt=None, use_seq_num=True):
Simon Glasscd2783e2024-07-20 11:49:46 +01004260 """Check an image with an FIT with multiple FDT images"""
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004261 def _CheckFdt(val, expected_data):
Simon Glassa435cd12020-09-01 05:13:59 -06004262 """Check the FDT nodes
4263
4264 Args:
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004265 val: Sequence number to check (0 or 1) or fdt name
Simon Glassa435cd12020-09-01 05:13:59 -06004266 expected_data: Expected contents of 'data' property
4267 """
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004268 name = 'fdt-%s' % val
Simon Glassa435cd12020-09-01 05:13:59 -06004269 fnode = dtb.GetNode('/images/%s' % name)
4270 self.assertIsNotNone(fnode)
4271 self.assertEqual({'description','type', 'compression', 'data'},
4272 set(fnode.props.keys()))
4273 self.assertEqual(expected_data, fnode.props['data'].bytes)
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004274 description = (
4275 'fdt-test-fdt%s.dtb' % val if len(val) == 1 else
4276 'fdt-%s.dtb' % val
4277 )
4278 self.assertEqual(description, fnode.props['description'].value)
Jan Kiszkaa1419df2022-02-28 17:06:20 +01004279 self.assertEqual(fnode.subnodes[0].name, 'hash')
Simon Glassa435cd12020-09-01 05:13:59 -06004280
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004281 def _CheckConfig(val, expected_data):
Simon Glassa435cd12020-09-01 05:13:59 -06004282 """Check the configuration nodes
4283
4284 Args:
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004285 val: Sequence number to check (0 or 1) or fdt name
Simon Glassa435cd12020-09-01 05:13:59 -06004286 expected_data: Expected contents of 'data' property
4287 """
4288 cnode = dtb.GetNode('/configurations')
4289 self.assertIn('default', cnode.props)
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004290 default = (
4291 'config-2' if len(val) == 1 else
4292 'config-test-fdt2'
4293 )
4294 self.assertEqual(default, cnode.props['default'].value)
Simon Glassa435cd12020-09-01 05:13:59 -06004295
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004296 name = 'config-%s' % val
Simon Glassa435cd12020-09-01 05:13:59 -06004297 fnode = dtb.GetNode('/configurations/%s' % name)
4298 self.assertIsNotNone(fnode)
4299 self.assertEqual({'description','firmware', 'loadables', 'fdt'},
4300 set(fnode.props.keys()))
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004301 description = (
4302 'conf-test-fdt%s.dtb' % val if len(val) == 1 else
4303 'conf-%s.dtb' % val
4304 )
4305 self.assertEqual(description, fnode.props['description'].value)
4306 self.assertEqual('fdt-%s' % val, fnode.props['fdt'].value)
Simon Glassa435cd12020-09-01 05:13:59 -06004307
4308 entry_args = {
Simon Glass1032acc2020-09-06 10:39:08 -06004309 'default-dt': 'test-fdt2',
Simon Glassa435cd12020-09-01 05:13:59 -06004310 }
Paul HENRYS87a8e7c2024-11-25 19:16:54 +01004311 extra_indirs = None
Simon Glasscd2783e2024-07-20 11:49:46 +01004312 if use_fdt_list:
4313 entry_args['of-list'] = 'test-fdt1 test-fdt2'
Simon Glassd2a9d6e2024-08-26 13:11:37 -06004314 if default_dt:
4315 entry_args['default-dt'] = default_dt
Paul HENRYS87a8e7c2024-11-25 19:16:54 +01004316 if use_fdt_list:
4317 extra_indirs = [os.path.join(self._indir, TEST_FDT_SUBDIR)]
Simon Glassa435cd12020-09-01 05:13:59 -06004318 data = self._DoReadFileDtb(
Simon Glasscd2783e2024-07-20 11:49:46 +01004319 dts,
Simon Glassa435cd12020-09-01 05:13:59 -06004320 entry_args=entry_args,
Paul HENRYS87a8e7c2024-11-25 19:16:54 +01004321 extra_indirs=extra_indirs)[0]
Simon Glassa435cd12020-09-01 05:13:59 -06004322 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
4323 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
4324
4325 dtb = fdt.Fdt.FromData(fit_data)
4326 dtb.Scan()
4327 fnode = dtb.GetNode('/images/kernel')
4328 self.assertIn('data', fnode.props)
4329
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004330 if use_seq_num == True:
4331 # Check all the properties in fdt-1 and fdt-2
4332 _CheckFdt('1', TEST_FDT1_DATA)
4333 _CheckFdt('2', TEST_FDT2_DATA)
Simon Glassa435cd12020-09-01 05:13:59 -06004334
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004335 # Check configurations
4336 _CheckConfig('1', TEST_FDT1_DATA)
4337 _CheckConfig('2', TEST_FDT2_DATA)
4338 else:
4339 # Check all the properties in fdt-1 and fdt-2
4340 _CheckFdt('test-fdt1', TEST_FDT1_DATA)
4341 _CheckFdt('test-fdt2', TEST_FDT2_DATA)
4342
4343 # Check configurations
4344 _CheckConfig('test-fdt1', TEST_FDT1_DATA)
4345 _CheckConfig('test-fdt2', TEST_FDT2_DATA)
Simon Glassa435cd12020-09-01 05:13:59 -06004346
Simon Glasscd2783e2024-07-20 11:49:46 +01004347 def testFitFdt(self):
4348 """Test an image with an FIT with multiple FDT images"""
4349 self.CheckFitFdt()
4350
Simon Glassa435cd12020-09-01 05:13:59 -06004351 def testFitFdtMissingList(self):
4352 """Test handling of a missing 'of-list' entry arg"""
4353 with self.assertRaises(ValueError) as e:
Bin Meng16cf5662021-05-10 20:23:32 +08004354 self._DoReadFile('170_fit_fdt.dts')
Simon Glassa435cd12020-09-01 05:13:59 -06004355 self.assertIn("Generator node requires 'of-list' entry argument",
4356 str(e.exception))
4357
4358 def testFitFdtEmptyList(self):
4359 """Test handling of an empty 'of-list' entry arg"""
4360 entry_args = {
4361 'of-list': '',
4362 }
4363 data = self._DoReadFileDtb('170_fit_fdt.dts', entry_args=entry_args)[0]
4364
4365 def testFitFdtMissingProp(self):
4366 """Test handling of a missing 'fit,fdt-list' property"""
4367 with self.assertRaises(ValueError) as e:
4368 self._DoReadFile('171_fit_fdt_missing_prop.dts')
4369 self.assertIn("Generator node requires 'fit,fdt-list' property",
4370 str(e.exception))
Simon Glass559c4de2020-09-01 05:13:58 -06004371
Simon Glass1032acc2020-09-06 10:39:08 -06004372 def testFitFdtMissing(self):
4373 """Test handling of a missing 'default-dt' entry arg"""
4374 entry_args = {
4375 'of-list': 'test-fdt1 test-fdt2',
4376 }
4377 with self.assertRaises(ValueError) as e:
4378 self._DoReadFileDtb(
Bin Meng16cf5662021-05-10 20:23:32 +08004379 '170_fit_fdt.dts',
Simon Glass1032acc2020-09-06 10:39:08 -06004380 entry_args=entry_args,
4381 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
4382 self.assertIn("Generated 'default' node requires default-dt entry argument",
4383 str(e.exception))
4384
4385 def testFitFdtNotInList(self):
4386 """Test handling of a default-dt that is not in the of-list"""
4387 entry_args = {
4388 'of-list': 'test-fdt1 test-fdt2',
4389 'default-dt': 'test-fdt3',
4390 }
4391 with self.assertRaises(ValueError) as e:
4392 self._DoReadFileDtb(
Bin Meng16cf5662021-05-10 20:23:32 +08004393 '170_fit_fdt.dts',
Simon Glass1032acc2020-09-06 10:39:08 -06004394 entry_args=entry_args,
4395 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
4396 self.assertIn("default-dt entry argument 'test-fdt3' not found in fdt list: test-fdt1, test-fdt2",
4397 str(e.exception))
4398
Simon Glassa820af72020-09-06 10:39:09 -06004399 def testFitExtblobMissingHelp(self):
4400 """Test display of help messages when an external blob is missing"""
4401 control.missing_blob_help = control._ReadMissingBlobHelp()
4402 control.missing_blob_help['wibble'] = 'Wibble test'
4403 control.missing_blob_help['another'] = 'Another test'
Simon Glass14d64e32025-04-29 07:21:59 -06004404 with terminal.capture() as (stdout, stderr):
Simon Glassa820af72020-09-06 10:39:09 -06004405 self._DoTestFile('168_fit_missing_blob.dts',
4406 allow_missing=True)
4407 err = stderr.getvalue()
4408
4409 # We can get the tag from the name, the type or the missing-msg
4410 # property. Check all three.
4411 self.assertIn('You may need to build ARM Trusted', err)
4412 self.assertIn('Wibble test', err)
4413 self.assertIn('Another test', err)
4414
Simon Glass6f1f4d42020-09-06 10:35:32 -06004415 def testMissingBlob(self):
4416 """Test handling of a blob containing a missing file"""
4417 with self.assertRaises(ValueError) as e:
4418 self._DoTestFile('173_missing_blob.dts', allow_missing=True)
4419 self.assertIn("Filename 'missing' not found in input path",
4420 str(e.exception))
4421
Simon Glassa0729502020-09-06 10:35:33 -06004422 def testEnvironment(self):
4423 """Test adding a U-Boot environment"""
4424 data = self._DoReadFile('174_env.dts')
4425 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
4426 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
4427 env = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
4428 self.assertEqual(b'\x1b\x97\x22\x7c\x01var1=1\0var2="2"\0\0\xff\xff',
4429 env)
4430
4431 def testEnvironmentNoSize(self):
4432 """Test that a missing 'size' property is detected"""
4433 with self.assertRaises(ValueError) as e:
Simon Glass8cdc08a2020-10-26 17:40:00 -06004434 self._DoTestFile('175_env_no_size.dts')
Simon Glassa0729502020-09-06 10:35:33 -06004435 self.assertIn("'u-boot-env' entry must have a size property",
4436 str(e.exception))
4437
4438 def testEnvironmentTooSmall(self):
4439 """Test handling of an environment that does not fit"""
4440 with self.assertRaises(ValueError) as e:
Simon Glass8cdc08a2020-10-26 17:40:00 -06004441 self._DoTestFile('176_env_too_small.dts')
Simon Glassa0729502020-09-06 10:35:33 -06004442
4443 # checksum, start byte, environment with \0 terminator, final \0
4444 need = 4 + 1 + len(ENV_DATA) + 1 + 1
4445 short = need - 0x8
4446 self.assertIn("too small to hold data (need %#x more bytes)" % short,
4447 str(e.exception))
4448
Simon Glassd1fdf752020-10-26 17:40:01 -06004449 def testSkipAtStart(self):
4450 """Test handling of skip-at-start section"""
4451 data = self._DoReadFile('177_skip_at_start.dts')
4452 self.assertEqual(U_BOOT_DATA, data)
4453
4454 image = control.images['image']
4455 entries = image.GetEntries()
4456 section = entries['section']
4457 self.assertEqual(0, section.offset)
4458 self.assertEqual(len(U_BOOT_DATA), section.size)
4459 self.assertEqual(U_BOOT_DATA, section.GetData())
4460
4461 entry = section.GetEntries()['u-boot']
4462 self.assertEqual(16, entry.offset)
4463 self.assertEqual(len(U_BOOT_DATA), entry.size)
4464 self.assertEqual(U_BOOT_DATA, entry.data)
4465
4466 def testSkipAtStartPad(self):
4467 """Test handling of skip-at-start section with padded entry"""
4468 data = self._DoReadFile('178_skip_at_start_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07004469 before = tools.get_bytes(0, 8)
4470 after = tools.get_bytes(0, 4)
Simon Glassd1fdf752020-10-26 17:40:01 -06004471 all = before + U_BOOT_DATA + after
4472 self.assertEqual(all, data)
4473
4474 image = control.images['image']
4475 entries = image.GetEntries()
4476 section = entries['section']
4477 self.assertEqual(0, section.offset)
4478 self.assertEqual(len(all), section.size)
4479 self.assertEqual(all, section.GetData())
4480
4481 entry = section.GetEntries()['u-boot']
4482 self.assertEqual(16, entry.offset)
4483 self.assertEqual(len(all), entry.size)
4484 self.assertEqual(U_BOOT_DATA, entry.data)
4485
4486 def testSkipAtStartSectionPad(self):
4487 """Test handling of skip-at-start section with padding"""
4488 data = self._DoReadFile('179_skip_at_start_section_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07004489 before = tools.get_bytes(0, 8)
4490 after = tools.get_bytes(0, 4)
Simon Glassd1fdf752020-10-26 17:40:01 -06004491 all = before + U_BOOT_DATA + after
Simon Glass510ef0f2020-10-26 17:40:13 -06004492 self.assertEqual(all, data)
Simon Glassd1fdf752020-10-26 17:40:01 -06004493
4494 image = control.images['image']
4495 entries = image.GetEntries()
4496 section = entries['section']
4497 self.assertEqual(0, section.offset)
4498 self.assertEqual(len(all), section.size)
Simon Glass72eeff12020-10-26 17:40:16 -06004499 self.assertEqual(U_BOOT_DATA, section.data)
Simon Glass510ef0f2020-10-26 17:40:13 -06004500 self.assertEqual(all, section.GetPaddedData())
Simon Glassd1fdf752020-10-26 17:40:01 -06004501
4502 entry = section.GetEntries()['u-boot']
4503 self.assertEqual(16, entry.offset)
4504 self.assertEqual(len(U_BOOT_DATA), entry.size)
4505 self.assertEqual(U_BOOT_DATA, entry.data)
Simon Glassa0729502020-09-06 10:35:33 -06004506
Simon Glassbb395742020-10-26 17:40:14 -06004507 def testSectionPad(self):
4508 """Testing padding with sections"""
4509 data = self._DoReadFile('180_section_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07004510 expected = (tools.get_bytes(ord('&'), 3) +
4511 tools.get_bytes(ord('!'), 5) +
Simon Glassbb395742020-10-26 17:40:14 -06004512 U_BOOT_DATA +
Simon Glass80025522022-01-29 14:14:04 -07004513 tools.get_bytes(ord('!'), 1) +
4514 tools.get_bytes(ord('&'), 2))
Simon Glassbb395742020-10-26 17:40:14 -06004515 self.assertEqual(expected, data)
4516
4517 def testSectionAlign(self):
4518 """Testing alignment with sections"""
4519 data = self._DoReadFileDtb('181_section_align.dts', map=True)[0]
4520 expected = (b'\0' + # fill section
Simon Glass80025522022-01-29 14:14:04 -07004521 tools.get_bytes(ord('&'), 1) + # padding to section align
Simon Glassbb395742020-10-26 17:40:14 -06004522 b'\0' + # fill section
Simon Glass80025522022-01-29 14:14:04 -07004523 tools.get_bytes(ord('!'), 3) + # padding to u-boot align
Simon Glassbb395742020-10-26 17:40:14 -06004524 U_BOOT_DATA +
Simon Glass80025522022-01-29 14:14:04 -07004525 tools.get_bytes(ord('!'), 4) + # padding to u-boot size
4526 tools.get_bytes(ord('!'), 4)) # padding to section size
Simon Glassbb395742020-10-26 17:40:14 -06004527 self.assertEqual(expected, data)
4528
Simon Glassd92c8362020-10-26 17:40:25 -06004529 def testCompressImage(self):
4530 """Test compression of the entire image"""
4531 self._CheckLz4()
4532 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4533 '182_compress_image.dts', use_real_dtb=True, update_dtb=True)
4534 dtb = fdt.Fdt(out_dtb_fname)
4535 dtb.Scan()
4536 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4537 'uncomp-size'])
4538 orig = self._decompress(data)
Brandon Maiera657bc62024-06-04 16:16:05 +00004539 self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, orig)
Simon Glassd92c8362020-10-26 17:40:25 -06004540
4541 # Do a sanity check on various fields
4542 image = control.images['image']
4543 entries = image.GetEntries()
4544 self.assertEqual(2, len(entries))
4545
4546 entry = entries['blob']
4547 self.assertEqual(COMPRESS_DATA, entry.data)
4548 self.assertEqual(len(COMPRESS_DATA), entry.size)
4549
4550 entry = entries['u-boot']
4551 self.assertEqual(U_BOOT_DATA, entry.data)
4552 self.assertEqual(len(U_BOOT_DATA), entry.size)
4553
4554 self.assertEqual(len(data), image.size)
4555 self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, image.uncomp_data)
4556 self.assertEqual(len(COMPRESS_DATA + U_BOOT_DATA), image.uncomp_size)
4557 orig = self._decompress(image.data)
4558 self.assertEqual(orig, image.uncomp_data)
4559
4560 expected = {
4561 'blob:offset': 0,
4562 'blob:size': len(COMPRESS_DATA),
4563 'u-boot:offset': len(COMPRESS_DATA),
4564 'u-boot:size': len(U_BOOT_DATA),
4565 'uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4566 'offset': 0,
4567 'image-pos': 0,
4568 'size': len(data),
4569 }
4570 self.assertEqual(expected, props)
4571
4572 def testCompressImageLess(self):
4573 """Test compression where compression reduces the image size"""
4574 self._CheckLz4()
4575 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4576 '183_compress_image_less.dts', use_real_dtb=True, update_dtb=True)
4577 dtb = fdt.Fdt(out_dtb_fname)
4578 dtb.Scan()
4579 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4580 'uncomp-size'])
4581 orig = self._decompress(data)
4582
Brandon Maiera657bc62024-06-04 16:16:05 +00004583 self.assertEqual(COMPRESS_DATA + COMPRESS_DATA + U_BOOT_DATA, orig)
Simon Glassd92c8362020-10-26 17:40:25 -06004584
4585 # Do a sanity check on various fields
4586 image = control.images['image']
4587 entries = image.GetEntries()
4588 self.assertEqual(2, len(entries))
4589
4590 entry = entries['blob']
4591 self.assertEqual(COMPRESS_DATA_BIG, entry.data)
4592 self.assertEqual(len(COMPRESS_DATA_BIG), entry.size)
4593
4594 entry = entries['u-boot']
4595 self.assertEqual(U_BOOT_DATA, entry.data)
4596 self.assertEqual(len(U_BOOT_DATA), entry.size)
4597
4598 self.assertEqual(len(data), image.size)
4599 self.assertEqual(COMPRESS_DATA_BIG + U_BOOT_DATA, image.uncomp_data)
4600 self.assertEqual(len(COMPRESS_DATA_BIG + U_BOOT_DATA),
4601 image.uncomp_size)
4602 orig = self._decompress(image.data)
4603 self.assertEqual(orig, image.uncomp_data)
4604
4605 expected = {
4606 'blob:offset': 0,
4607 'blob:size': len(COMPRESS_DATA_BIG),
4608 'u-boot:offset': len(COMPRESS_DATA_BIG),
4609 'u-boot:size': len(U_BOOT_DATA),
4610 'uncomp-size': len(COMPRESS_DATA_BIG + U_BOOT_DATA),
4611 'offset': 0,
4612 'image-pos': 0,
4613 'size': len(data),
4614 }
4615 self.assertEqual(expected, props)
4616
4617 def testCompressSectionSize(self):
4618 """Test compression of a section with a fixed size"""
4619 self._CheckLz4()
4620 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4621 '184_compress_section_size.dts', use_real_dtb=True, update_dtb=True)
4622 dtb = fdt.Fdt(out_dtb_fname)
4623 dtb.Scan()
4624 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4625 'uncomp-size'])
Jiaxun Yangc6931742025-04-10 06:43:03 -06004626 data = data[:0x30]
4627 data = data.rstrip(b'\xff')
Simon Glassd92c8362020-10-26 17:40:25 -06004628 orig = self._decompress(data)
Brandon Maiera657bc62024-06-04 16:16:05 +00004629 self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, orig)
Simon Glassd92c8362020-10-26 17:40:25 -06004630 expected = {
4631 'section/blob:offset': 0,
4632 'section/blob:size': len(COMPRESS_DATA),
4633 'section/u-boot:offset': len(COMPRESS_DATA),
4634 'section/u-boot:size': len(U_BOOT_DATA),
4635 'section:offset': 0,
4636 'section:image-pos': 0,
4637 'section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4638 'section:size': 0x30,
4639 'offset': 0,
4640 'image-pos': 0,
4641 'size': 0x30,
4642 }
4643 self.assertEqual(expected, props)
4644
4645 def testCompressSection(self):
4646 """Test compression of a section with no fixed size"""
4647 self._CheckLz4()
4648 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4649 '185_compress_section.dts', use_real_dtb=True, update_dtb=True)
4650 dtb = fdt.Fdt(out_dtb_fname)
4651 dtb.Scan()
4652 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4653 'uncomp-size'])
4654 orig = self._decompress(data)
Brandon Maiera657bc62024-06-04 16:16:05 +00004655 self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, orig)
Simon Glassd92c8362020-10-26 17:40:25 -06004656 expected = {
4657 'section/blob:offset': 0,
4658 'section/blob:size': len(COMPRESS_DATA),
4659 'section/u-boot:offset': len(COMPRESS_DATA),
4660 'section/u-boot:size': len(U_BOOT_DATA),
4661 'section:offset': 0,
4662 'section:image-pos': 0,
4663 'section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4664 'section:size': len(data),
4665 'offset': 0,
4666 'image-pos': 0,
4667 'size': len(data),
4668 }
4669 self.assertEqual(expected, props)
4670
Stefan Herbrechtsmeier94813a02022-08-19 16:25:31 +02004671 def testLz4Missing(self):
4672 """Test that binman still produces an image if lz4 is missing"""
Simon Glass14d64e32025-04-29 07:21:59 -06004673 with terminal.capture() as (_, stderr):
Stefan Herbrechtsmeier94813a02022-08-19 16:25:31 +02004674 self._DoTestFile('185_compress_section.dts',
4675 force_missing_bintools='lz4')
4676 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07004677 self.assertRegex(err, "Image 'image'.*missing bintools.*: lz4")
Stefan Herbrechtsmeier94813a02022-08-19 16:25:31 +02004678
Simon Glassd92c8362020-10-26 17:40:25 -06004679 def testCompressExtra(self):
4680 """Test compression of a section with no fixed size"""
4681 self._CheckLz4()
4682 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4683 '186_compress_extra.dts', use_real_dtb=True, update_dtb=True)
4684 dtb = fdt.Fdt(out_dtb_fname)
4685 dtb.Scan()
4686 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4687 'uncomp-size'])
4688
4689 base = data[len(U_BOOT_DATA):]
Brandon Maiera657bc62024-06-04 16:16:05 +00004690 self.assertEqual(U_BOOT_DATA, base[:len(U_BOOT_DATA)])
Simon Glassd92c8362020-10-26 17:40:25 -06004691 rest = base[len(U_BOOT_DATA):]
4692
4693 # Check compressed data
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +02004694 bintool = self.comp_bintools['lz4']
4695 expect1 = bintool.compress(COMPRESS_DATA + U_BOOT_DATA)
Stefan Herbrechtsmeier86f1fc02022-08-19 16:25:23 +02004696 data1 = rest[:len(expect1)]
4697 section1 = self._decompress(data1)
Brandon Maiera657bc62024-06-04 16:16:05 +00004698 self.assertEqual(expect1, data1)
4699 self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, section1)
Simon Glassd92c8362020-10-26 17:40:25 -06004700 rest1 = rest[len(expect1):]
4701
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +02004702 expect2 = bintool.compress(COMPRESS_DATA + COMPRESS_DATA)
Stefan Herbrechtsmeier86f1fc02022-08-19 16:25:23 +02004703 data2 = rest1[:len(expect2)]
4704 section2 = self._decompress(data2)
Brandon Maiera657bc62024-06-04 16:16:05 +00004705 self.assertEqual(expect2, data2)
4706 self.assertEqual(COMPRESS_DATA + COMPRESS_DATA, section2)
Simon Glassd92c8362020-10-26 17:40:25 -06004707 rest2 = rest1[len(expect2):]
4708
4709 expect_size = (len(U_BOOT_DATA) + len(U_BOOT_DATA) + len(expect1) +
4710 len(expect2) + len(U_BOOT_DATA))
Brandon Maiera657bc62024-06-04 16:16:05 +00004711 #self.assertEqual(expect_size, len(data))
Simon Glassd92c8362020-10-26 17:40:25 -06004712
Brandon Maiera657bc62024-06-04 16:16:05 +00004713 #self.assertEqual(U_BOOT_DATA, rest2)
Simon Glassd92c8362020-10-26 17:40:25 -06004714
4715 self.maxDiff = None
4716 expected = {
4717 'u-boot:offset': 0,
4718 'u-boot:image-pos': 0,
4719 'u-boot:size': len(U_BOOT_DATA),
4720
4721 'base:offset': len(U_BOOT_DATA),
4722 'base:image-pos': len(U_BOOT_DATA),
4723 'base:size': len(data) - len(U_BOOT_DATA),
4724 'base/u-boot:offset': 0,
4725 'base/u-boot:image-pos': len(U_BOOT_DATA),
4726 'base/u-boot:size': len(U_BOOT_DATA),
4727 'base/u-boot2:offset': len(U_BOOT_DATA) + len(expect1) +
4728 len(expect2),
4729 'base/u-boot2:image-pos': len(U_BOOT_DATA) * 2 + len(expect1) +
4730 len(expect2),
4731 'base/u-boot2:size': len(U_BOOT_DATA),
4732
4733 'base/section:offset': len(U_BOOT_DATA),
4734 'base/section:image-pos': len(U_BOOT_DATA) * 2,
4735 'base/section:size': len(expect1),
4736 'base/section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4737 'base/section/blob:offset': 0,
4738 'base/section/blob:size': len(COMPRESS_DATA),
4739 'base/section/u-boot:offset': len(COMPRESS_DATA),
4740 'base/section/u-boot:size': len(U_BOOT_DATA),
4741
4742 'base/section2:offset': len(U_BOOT_DATA) + len(expect1),
4743 'base/section2:image-pos': len(U_BOOT_DATA) * 2 + len(expect1),
4744 'base/section2:size': len(expect2),
4745 'base/section2:uncomp-size': len(COMPRESS_DATA + COMPRESS_DATA),
4746 'base/section2/blob:offset': 0,
4747 'base/section2/blob:size': len(COMPRESS_DATA),
4748 'base/section2/blob2:offset': len(COMPRESS_DATA),
4749 'base/section2/blob2:size': len(COMPRESS_DATA),
4750
4751 'offset': 0,
4752 'image-pos': 0,
4753 'size': len(data),
4754 }
4755 self.assertEqual(expected, props)
4756
Simon Glassecbe4732021-01-06 21:35:15 -07004757 def testSymbolsSubsection(self):
4758 """Test binman can assign symbols from a subsection"""
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03004759 self.checkSymbols('187_symbols_sub.dts', U_BOOT_SPL_DATA, 0x1c)
Simon Glassecbe4732021-01-06 21:35:15 -07004760
Simon Glass3fb25402021-01-06 21:35:16 -07004761 def testReadImageEntryArg(self):
4762 """Test reading an image that would need an entry arg to generate"""
4763 entry_args = {
4764 'cros-ec-rw-path': 'ecrw.bin',
4765 }
4766 data = self.data = self._DoReadFileDtb(
4767 '188_image_entryarg.dts',use_real_dtb=True, update_dtb=True,
4768 entry_args=entry_args)
4769
Simon Glass80025522022-01-29 14:14:04 -07004770 image_fname = tools.get_output_filename('image.bin')
Simon Glass3fb25402021-01-06 21:35:16 -07004771 orig_image = control.images['image']
4772
4773 # This should not generate an error about the missing 'cros-ec-rw-path'
4774 # since we are reading the image from a file. Compare with
4775 # testEntryArgsRequired()
4776 image = Image.FromFile(image_fname)
4777 self.assertEqual(orig_image.GetEntries().keys(),
4778 image.GetEntries().keys())
4779
Simon Glassa2af7302021-01-06 21:35:18 -07004780 def testFilesAlign(self):
4781 """Test alignment with files"""
4782 data = self._DoReadFile('190_files_align.dts')
4783
4784 # The first string is 15 bytes so will align to 16
4785 expect = FILES_DATA[:15] + b'\0' + FILES_DATA[15:]
4786 self.assertEqual(expect, data)
4787
Simon Glassdb84b562021-01-06 21:35:19 -07004788 def testReadImageSkip(self):
4789 """Test reading an image and accessing its FDT map"""
4790 data = self.data = self._DoReadFileRealDtb('191_read_image_skip.dts')
Simon Glass80025522022-01-29 14:14:04 -07004791 image_fname = tools.get_output_filename('image.bin')
Simon Glassdb84b562021-01-06 21:35:19 -07004792 orig_image = control.images['image']
4793 image = Image.FromFile(image_fname)
4794 self.assertEqual(orig_image.GetEntries().keys(),
4795 image.GetEntries().keys())
4796
4797 orig_entry = orig_image.GetEntries()['fdtmap']
4798 entry = image.GetEntries()['fdtmap']
4799 self.assertEqual(orig_entry.offset, entry.offset)
4800 self.assertEqual(orig_entry.size, entry.size)
Simon Glassed836ac2025-02-26 09:26:17 -07004801 self.assertEqual((1 << 32) - 0x400 + 16, entry.image_pos)
Simon Glassdb84b562021-01-06 21:35:19 -07004802
4803 u_boot = image.GetEntries()['section'].GetEntries()['u-boot']
4804
Brandon Maiera657bc62024-06-04 16:16:05 +00004805 self.assertEqual(U_BOOT_DATA, u_boot.ReadData())
Simon Glassdb84b562021-01-06 21:35:19 -07004806
Simon Glassc98de972021-03-18 20:24:57 +13004807 def testTplNoDtb(self):
4808 """Test that an image with tpl/u-boot-tpl-nodtb.bin can be created"""
Simon Glass13089cc2021-04-25 08:39:32 +12004809 self._SetupTplElf()
Simon Glassc98de972021-03-18 20:24:57 +13004810 data = self._DoReadFile('192_u_boot_tpl_nodtb.dts')
4811 self.assertEqual(U_BOOT_TPL_NODTB_DATA,
4812 data[:len(U_BOOT_TPL_NODTB_DATA)])
4813
Simon Glass63f41d42021-03-18 20:24:58 +13004814 def testTplBssPad(self):
4815 """Test that we can pad TPL's BSS with zeros"""
4816 # ELF file with a '__bss_size' symbol
4817 self._SetupTplElf()
4818 data = self._DoReadFile('193_tpl_bss_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07004819 self.assertEqual(U_BOOT_TPL_DATA + tools.get_bytes(0, 10) + U_BOOT_DATA,
Simon Glass63f41d42021-03-18 20:24:58 +13004820 data)
4821
4822 def testTplBssPadMissing(self):
4823 """Test that a missing symbol is detected"""
4824 self._SetupTplElf('u_boot_ucode_ptr')
4825 with self.assertRaises(ValueError) as e:
4826 self._DoReadFile('193_tpl_bss_pad.dts')
4827 self.assertIn('Expected __bss_size symbol in tpl/u-boot-tpl',
4828 str(e.exception))
4829
Simon Glass718b5292021-03-18 20:25:07 +13004830 def checkDtbSizes(self, data, pad_len, start):
4831 """Check the size arguments in a dtb embedded in an image
4832
4833 Args:
4834 data: The image data
4835 pad_len: Length of the pad section in the image, in bytes
4836 start: Start offset of the devicetree to examine, within the image
4837
4838 Returns:
4839 Size of the devicetree in bytes
4840 """
4841 dtb_data = data[start:]
4842 dtb = fdt.Fdt.FromData(dtb_data)
4843 fdt_size = dtb.GetFdtObj().totalsize()
4844 dtb.Scan()
4845 props = self._GetPropTree(dtb, 'size')
4846 self.assertEqual({
4847 'size': len(data),
4848 'u-boot-spl/u-boot-spl-bss-pad:size': pad_len,
4849 'u-boot-spl/u-boot-spl-dtb:size': 801,
4850 'u-boot-spl/u-boot-spl-nodtb:size': len(U_BOOT_SPL_NODTB_DATA),
4851 'u-boot-spl:size': 860,
4852 'u-boot-tpl:size': len(U_BOOT_TPL_DATA),
4853 'u-boot/u-boot-dtb:size': 781,
4854 'u-boot/u-boot-nodtb:size': len(U_BOOT_NODTB_DATA),
4855 'u-boot:size': 827,
4856 }, props)
4857 return fdt_size
4858
4859 def testExpanded(self):
4860 """Test that an expanded entry type is selected when needed"""
4861 self._SetupSplElf()
4862 self._SetupTplElf()
4863
4864 # SPL has a devicetree, TPL does not
4865 entry_args = {
4866 'spl-dtb': '1',
4867 'spl-bss-pad': 'y',
4868 'tpl-dtb': '',
4869 }
4870 self._DoReadFileDtb('194_fdt_incl.dts', use_expanded=True,
4871 entry_args=entry_args)
4872 image = control.images['image']
4873 entries = image.GetEntries()
4874 self.assertEqual(3, len(entries))
4875
4876 # First, u-boot, which should be expanded into u-boot-nodtb and dtb
4877 self.assertIn('u-boot', entries)
4878 entry = entries['u-boot']
4879 self.assertEqual('u-boot-expanded', entry.etype)
4880 subent = entry.GetEntries()
4881 self.assertEqual(2, len(subent))
4882 self.assertIn('u-boot-nodtb', subent)
4883 self.assertIn('u-boot-dtb', subent)
4884
4885 # Second, u-boot-spl, which should be expanded into three parts
4886 self.assertIn('u-boot-spl', entries)
4887 entry = entries['u-boot-spl']
4888 self.assertEqual('u-boot-spl-expanded', entry.etype)
4889 subent = entry.GetEntries()
4890 self.assertEqual(3, len(subent))
4891 self.assertIn('u-boot-spl-nodtb', subent)
4892 self.assertIn('u-boot-spl-bss-pad', subent)
4893 self.assertIn('u-boot-spl-dtb', subent)
4894
4895 # Third, u-boot-tpl, which should be not be expanded, since TPL has no
4896 # devicetree
4897 self.assertIn('u-boot-tpl', entries)
4898 entry = entries['u-boot-tpl']
4899 self.assertEqual('u-boot-tpl', entry.etype)
4900 self.assertEqual(None, entry.GetEntries())
4901
4902 def testExpandedTpl(self):
4903 """Test that an expanded entry type is selected for TPL when needed"""
4904 self._SetupTplElf()
4905
4906 entry_args = {
4907 'tpl-bss-pad': 'y',
4908 'tpl-dtb': 'y',
4909 }
4910 self._DoReadFileDtb('195_fdt_incl_tpl.dts', use_expanded=True,
4911 entry_args=entry_args)
4912 image = control.images['image']
4913 entries = image.GetEntries()
4914 self.assertEqual(1, len(entries))
4915
4916 # We only have u-boot-tpl, which be expanded
4917 self.assertIn('u-boot-tpl', entries)
4918 entry = entries['u-boot-tpl']
4919 self.assertEqual('u-boot-tpl-expanded', entry.etype)
4920 subent = entry.GetEntries()
4921 self.assertEqual(3, len(subent))
4922 self.assertIn('u-boot-tpl-nodtb', subent)
4923 self.assertIn('u-boot-tpl-bss-pad', subent)
4924 self.assertIn('u-boot-tpl-dtb', subent)
4925
4926 def testExpandedNoPad(self):
4927 """Test an expanded entry without BSS pad enabled"""
4928 self._SetupSplElf()
4929 self._SetupTplElf()
4930
4931 # SPL has a devicetree, TPL does not
4932 entry_args = {
4933 'spl-dtb': 'something',
4934 'spl-bss-pad': 'n',
4935 'tpl-dtb': '',
4936 }
4937 self._DoReadFileDtb('194_fdt_incl.dts', use_expanded=True,
4938 entry_args=entry_args)
4939 image = control.images['image']
4940 entries = image.GetEntries()
4941
4942 # Just check u-boot-spl, which should be expanded into two parts
4943 self.assertIn('u-boot-spl', entries)
4944 entry = entries['u-boot-spl']
4945 self.assertEqual('u-boot-spl-expanded', entry.etype)
4946 subent = entry.GetEntries()
4947 self.assertEqual(2, len(subent))
4948 self.assertIn('u-boot-spl-nodtb', subent)
4949 self.assertIn('u-boot-spl-dtb', subent)
4950
4951 def testExpandedTplNoPad(self):
4952 """Test that an expanded entry type with padding disabled in TPL"""
4953 self._SetupTplElf()
4954
4955 entry_args = {
4956 'tpl-bss-pad': '',
4957 'tpl-dtb': 'y',
4958 }
4959 self._DoReadFileDtb('195_fdt_incl_tpl.dts', use_expanded=True,
4960 entry_args=entry_args)
4961 image = control.images['image']
4962 entries = image.GetEntries()
4963 self.assertEqual(1, len(entries))
4964
4965 # We only have u-boot-tpl, which be expanded
4966 self.assertIn('u-boot-tpl', entries)
4967 entry = entries['u-boot-tpl']
4968 self.assertEqual('u-boot-tpl-expanded', entry.etype)
4969 subent = entry.GetEntries()
4970 self.assertEqual(2, len(subent))
4971 self.assertIn('u-boot-tpl-nodtb', subent)
4972 self.assertIn('u-boot-tpl-dtb', subent)
4973
4974 def testFdtInclude(self):
4975 """Test that an Fdt is update within all binaries"""
4976 self._SetupSplElf()
4977 self._SetupTplElf()
4978
4979 # SPL has a devicetree, TPL does not
4980 self.maxDiff = None
4981 entry_args = {
4982 'spl-dtb': '1',
4983 'spl-bss-pad': 'y',
4984 'tpl-dtb': '',
4985 }
4986 # Build the image. It includes two separate devicetree binaries, each
4987 # with their own contents, but all contain the binman definition.
4988 data = self._DoReadFileDtb(
4989 '194_fdt_incl.dts', use_real_dtb=True, use_expanded=True,
4990 update_dtb=True, entry_args=entry_args)[0]
4991 pad_len = 10
4992
4993 # Check the U-Boot dtb
4994 start = len(U_BOOT_NODTB_DATA)
4995 fdt_size = self.checkDtbSizes(data, pad_len, start)
4996
4997 # Now check SPL
4998 start += fdt_size + len(U_BOOT_SPL_NODTB_DATA) + pad_len
4999 fdt_size = self.checkDtbSizes(data, pad_len, start)
5000
5001 # TPL has no devicetree
5002 start += fdt_size + len(U_BOOT_TPL_DATA)
5003 self.assertEqual(len(data), start)
Simon Glassbb395742020-10-26 17:40:14 -06005004
Simon Glass7098b7f2021-03-21 18:24:30 +13005005 def testSymbolsExpanded(self):
5006 """Test binman can assign symbols in expanded entries"""
5007 entry_args = {
5008 'spl-dtb': '1',
5009 }
5010 self.checkSymbols('197_symbols_expand.dts', U_BOOT_SPL_NODTB_DATA +
5011 U_BOOT_SPL_DTB_DATA, 0x38,
5012 entry_args=entry_args, use_expanded=True)
5013
Simon Glasse1915782021-03-21 18:24:31 +13005014 def testCollection(self):
5015 """Test a collection"""
5016 data = self._DoReadFile('198_collection.dts')
5017 self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA +
Simon Glass80025522022-01-29 14:14:04 -07005018 tools.get_bytes(0xff, 2) + U_BOOT_NODTB_DATA +
5019 tools.get_bytes(0xfe, 3) + U_BOOT_DTB_DATA,
Simon Glasse1915782021-03-21 18:24:31 +13005020 data)
5021
Simon Glass27a7f772021-03-21 18:24:32 +13005022 def testCollectionSection(self):
5023 """Test a collection where a section must be built first"""
5024 # Sections never have their contents when GetData() is called, but when
Simon Glass7e3f89f2021-11-23 11:03:47 -07005025 # BuildSectionData() is called with required=True, a section will force
Simon Glass27a7f772021-03-21 18:24:32 +13005026 # building the contents, producing an error is anything is still
5027 # missing.
5028 data = self._DoReadFile('199_collection_section.dts')
5029 section = U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA
Simon Glass80025522022-01-29 14:14:04 -07005030 self.assertEqual(section + U_BOOT_DATA + tools.get_bytes(0xff, 2) +
5031 section + tools.get_bytes(0xfe, 3) + U_BOOT_DATA,
Simon Glass27a7f772021-03-21 18:24:32 +13005032 data)
5033
Simon Glassf427c5f2021-03-21 18:24:33 +13005034 def testAlignDefault(self):
5035 """Test that default alignment works on sections"""
5036 data = self._DoReadFile('200_align_default.dts')
Simon Glass80025522022-01-29 14:14:04 -07005037 expected = (U_BOOT_DATA + tools.get_bytes(0, 8 - len(U_BOOT_DATA)) +
Simon Glassf427c5f2021-03-21 18:24:33 +13005038 U_BOOT_DATA)
5039 # Special alignment for section
Simon Glass80025522022-01-29 14:14:04 -07005040 expected += tools.get_bytes(0, 32 - len(expected))
Simon Glassf427c5f2021-03-21 18:24:33 +13005041 # No alignment within the nested section
5042 expected += U_BOOT_DATA + U_BOOT_NODTB_DATA;
5043 # Now the final piece, which should be default-aligned
Simon Glass80025522022-01-29 14:14:04 -07005044 expected += tools.get_bytes(0, 88 - len(expected)) + U_BOOT_NODTB_DATA
Simon Glassf427c5f2021-03-21 18:24:33 +13005045 self.assertEqual(expected, data)
Simon Glass27a7f772021-03-21 18:24:32 +13005046
Bin Mengc0b15742021-05-10 20:23:33 +08005047 def testPackOpenSBI(self):
5048 """Test that an image with an OpenSBI binary can be created"""
5049 data = self._DoReadFile('201_opensbi.dts')
5050 self.assertEqual(OPENSBI_DATA, data[:len(OPENSBI_DATA)])
5051
Simon Glass76f496d2021-07-06 10:36:37 -06005052 def testSectionsSingleThread(self):
5053 """Test sections without multithreading"""
5054 data = self._DoReadFileDtb('055_sections.dts', threads=0)[0]
Simon Glass80025522022-01-29 14:14:04 -07005055 expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) +
5056 U_BOOT_DATA + tools.get_bytes(ord('a'), 12) +
5057 U_BOOT_DATA + tools.get_bytes(ord('&'), 4))
Simon Glass76f496d2021-07-06 10:36:37 -06005058 self.assertEqual(expected, data)
5059
5060 def testThreadTimeout(self):
5061 """Test handling a thread that takes too long"""
5062 with self.assertRaises(ValueError) as e:
5063 self._DoTestFile('202_section_timeout.dts',
5064 test_section_timeout=True)
Simon Glass2d59d152021-10-18 12:13:15 -06005065 self.assertIn("Timed out obtaining contents", str(e.exception))
Simon Glass76f496d2021-07-06 10:36:37 -06005066
Simon Glass748a1d42021-07-06 10:36:41 -06005067 def testTiming(self):
5068 """Test output of timing information"""
5069 data = self._DoReadFile('055_sections.dts')
Simon Glass14d64e32025-04-29 07:21:59 -06005070 with terminal.capture() as (stdout, stderr):
Simon Glass748a1d42021-07-06 10:36:41 -06005071 state.TimingShow()
5072 self.assertIn('read:', stdout.getvalue())
5073 self.assertIn('compress:', stdout.getvalue())
5074
Simon Glassadfb8492021-11-03 21:09:18 -06005075 def testUpdateFdtInElf(self):
5076 """Test that we can update the devicetree in an ELF file"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005077 if not elf.ELF_TOOLS:
5078 self.skipTest('Python elftools not available')
Simon Glassadfb8492021-11-03 21:09:18 -06005079 infile = elf_fname = self.ElfTestFile('u_boot_binman_embed')
5080 outfile = os.path.join(self._indir, 'u-boot.out')
5081 begin_sym = 'dtb_embed_begin'
5082 end_sym = 'dtb_embed_end'
5083 retcode = self._DoTestFile(
5084 '060_fdt_update.dts', update_dtb=True,
5085 update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym]))
5086 self.assertEqual(0, retcode)
5087
5088 # Check that the output file does in fact contact a dtb with the binman
5089 # definition in the correct place
5090 syms = elf.GetSymbolFileOffset(infile,
5091 ['dtb_embed_begin', 'dtb_embed_end'])
Simon Glass80025522022-01-29 14:14:04 -07005092 data = tools.read_file(outfile)
Simon Glassadfb8492021-11-03 21:09:18 -06005093 dtb_data = data[syms['dtb_embed_begin'].offset:
5094 syms['dtb_embed_end'].offset]
5095
5096 dtb = fdt.Fdt.FromData(dtb_data)
5097 dtb.Scan()
5098 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
5099 self.assertEqual({
5100 'image-pos': 0,
5101 'offset': 0,
5102 '_testing:offset': 32,
5103 '_testing:size': 2,
5104 '_testing:image-pos': 32,
5105 'section@0/u-boot:offset': 0,
5106 'section@0/u-boot:size': len(U_BOOT_DATA),
5107 'section@0/u-boot:image-pos': 0,
5108 'section@0:offset': 0,
5109 'section@0:size': 16,
5110 'section@0:image-pos': 0,
5111
5112 'section@1/u-boot:offset': 0,
5113 'section@1/u-boot:size': len(U_BOOT_DATA),
5114 'section@1/u-boot:image-pos': 16,
5115 'section@1:offset': 16,
5116 'section@1:size': 16,
5117 'section@1:image-pos': 16,
5118 'size': 40
5119 }, props)
5120
5121 def testUpdateFdtInElfInvalid(self):
5122 """Test that invalid args are detected with --update-fdt-in-elf"""
5123 with self.assertRaises(ValueError) as e:
5124 self._DoTestFile('060_fdt_update.dts', update_fdt_in_elf='fred')
5125 self.assertIn("Invalid args ['fred'] to --update-fdt-in-elf",
5126 str(e.exception))
5127
5128 def testUpdateFdtInElfNoSyms(self):
5129 """Test that missing symbols are detected with --update-fdt-in-elf"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005130 if not elf.ELF_TOOLS:
5131 self.skipTest('Python elftools not available')
Simon Glassadfb8492021-11-03 21:09:18 -06005132 infile = elf_fname = self.ElfTestFile('u_boot_binman_embed')
5133 outfile = ''
5134 begin_sym = 'wrong_begin'
5135 end_sym = 'wrong_end'
5136 with self.assertRaises(ValueError) as e:
5137 self._DoTestFile(
5138 '060_fdt_update.dts',
5139 update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym]))
5140 self.assertIn("Expected two symbols 'wrong_begin' and 'wrong_end': got 0:",
5141 str(e.exception))
5142
5143 def testUpdateFdtInElfTooSmall(self):
5144 """Test that an over-large dtb is detected with --update-fdt-in-elf"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005145 if not elf.ELF_TOOLS:
5146 self.skipTest('Python elftools not available')
Simon Glassadfb8492021-11-03 21:09:18 -06005147 infile = elf_fname = self.ElfTestFile('u_boot_binman_embed_sm')
5148 outfile = os.path.join(self._indir, 'u-boot.out')
5149 begin_sym = 'dtb_embed_begin'
5150 end_sym = 'dtb_embed_end'
5151 with self.assertRaises(ValueError) as e:
5152 self._DoTestFile(
5153 '060_fdt_update.dts', update_dtb=True,
5154 update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym]))
5155 self.assertRegex(
5156 str(e.exception),
5157 "Not enough space in '.*u_boot_binman_embed_sm' for data length.*")
5158
Simon Glass88e04da2021-11-23 11:03:42 -07005159 def testVersion(self):
5160 """Test we can get the binman version"""
5161 version = '(unreleased)'
5162 self.assertEqual(version, state.GetVersion(self._indir))
5163
5164 with self.assertRaises(SystemExit):
Simon Glass14d64e32025-04-29 07:21:59 -06005165 with terminal.capture() as (_, stderr):
Simon Glass88e04da2021-11-23 11:03:42 -07005166 self._DoBinman('-V')
5167 self.assertEqual('Binman %s\n' % version, stderr.getvalue())
5168
5169 # Try running the tool too, just to be safe
5170 result = self._RunBinman('-V')
5171 self.assertEqual('Binman %s\n' % version, result.stderr)
5172
5173 # Set up a version file to make sure that works
5174 version = 'v2025.01-rc2'
Simon Glass80025522022-01-29 14:14:04 -07005175 tools.write_file(os.path.join(self._indir, 'version'), version,
Simon Glass88e04da2021-11-23 11:03:42 -07005176 binary=False)
5177 self.assertEqual(version, state.GetVersion(self._indir))
5178
Simon Glass637958f2021-11-23 21:09:50 -07005179 def testAltFormat(self):
5180 """Test that alternative formats can be used to extract"""
5181 self._DoReadFileRealDtb('213_fdtmap_alt_format.dts')
5182
5183 try:
5184 tmpdir, updated_fname = self._SetupImageInTmpdir()
Simon Glass14d64e32025-04-29 07:21:59 -06005185 with terminal.capture() as (stdout, _):
Simon Glass637958f2021-11-23 21:09:50 -07005186 self._DoBinman('extract', '-i', updated_fname, '-F', 'list')
5187 self.assertEqual(
5188 '''Flag (-F) Entry type Description
5189fdt fdtmap Extract the devicetree blob from the fdtmap
5190''',
5191 stdout.getvalue())
5192
5193 dtb = os.path.join(tmpdir, 'fdt.dtb')
5194 self._DoBinman('extract', '-i', updated_fname, '-F', 'fdt', '-f',
5195 dtb, 'fdtmap')
5196
5197 # Check that we can read it and it can be scanning, meaning it does
5198 # not have a 16-byte fdtmap header
Simon Glass80025522022-01-29 14:14:04 -07005199 data = tools.read_file(dtb)
Simon Glass637958f2021-11-23 21:09:50 -07005200 dtb = fdt.Fdt.FromData(data)
5201 dtb.Scan()
5202
5203 # Now check u-boot which has no alt_format
5204 fname = os.path.join(tmpdir, 'fdt.dtb')
5205 self._DoBinman('extract', '-i', updated_fname, '-F', 'dummy',
5206 '-f', fname, 'u-boot')
Simon Glass80025522022-01-29 14:14:04 -07005207 data = tools.read_file(fname)
Simon Glass637958f2021-11-23 21:09:50 -07005208 self.assertEqual(U_BOOT_DATA, data)
5209
5210 finally:
5211 shutil.rmtree(tmpdir)
5212
Simon Glass0b00ae62021-11-23 21:09:52 -07005213 def testExtblobList(self):
5214 """Test an image with an external blob list"""
Yannic Moog6f2fd072025-06-13 14:02:44 +02005215 data = self._DoReadFileDtb('215_blob_ext_list.dts',
5216 allow_fake_blobs=False)
5217 self.assertEqual(REFCODE_DATA + FSP_M_DATA, data[0])
Simon Glass0b00ae62021-11-23 21:09:52 -07005218
5219 def testExtblobListMissing(self):
5220 """Test an image with a missing external blob"""
5221 with self.assertRaises(ValueError) as e:
Yannic Moog6f2fd072025-06-13 14:02:44 +02005222 self._DoReadFileDtb('216_blob_ext_list_missing.dts',
5223 allow_fake_blobs=False)
Simon Glass0b00ae62021-11-23 21:09:52 -07005224 self.assertIn("Filename 'missing-file' not found in input path",
5225 str(e.exception))
5226
5227 def testExtblobListMissingOk(self):
5228 """Test an image with an missing external blob that is allowed"""
Simon Glass14d64e32025-04-29 07:21:59 -06005229 with terminal.capture() as (stdout, stderr):
Simon Glass0b00ae62021-11-23 21:09:52 -07005230 self._DoTestFile('216_blob_ext_list_missing.dts',
Yannic Moog6f2fd072025-06-13 14:02:44 +02005231 allow_missing=True, allow_fake_blobs=False)
Simon Glass0b00ae62021-11-23 21:09:52 -07005232 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07005233 self.assertRegex(err, "Image 'image'.*missing.*: blob-ext")
Simon Glass0b00ae62021-11-23 21:09:52 -07005234
Simon Glass3efb2972021-11-23 21:08:59 -07005235 def testFip(self):
5236 """Basic test of generation of an ARM Firmware Image Package (FIP)"""
5237 data = self._DoReadFile('203_fip.dts')
5238 hdr, fents = fip_util.decode_fip(data)
5239 self.assertEqual(fip_util.HEADER_MAGIC, hdr.name)
5240 self.assertEqual(fip_util.HEADER_SERIAL, hdr.serial)
5241 self.assertEqual(0x123, hdr.flags)
5242
5243 self.assertEqual(2, len(fents))
5244
5245 fent = fents[0]
5246 self.assertEqual(
5247 bytes([0x47, 0xd4, 0x08, 0x6d, 0x4c, 0xfe, 0x98, 0x46,
5248 0x9b, 0x95, 0x29, 0x50, 0xcb, 0xbd, 0x5a, 0x0]), fent.uuid)
5249 self.assertEqual('soc-fw', fent.fip_type)
5250 self.assertEqual(0x88, fent.offset)
5251 self.assertEqual(len(ATF_BL31_DATA), fent.size)
5252 self.assertEqual(0x123456789abcdef, fent.flags)
5253 self.assertEqual(ATF_BL31_DATA, fent.data)
5254 self.assertEqual(True, fent.valid)
5255
5256 fent = fents[1]
5257 self.assertEqual(
5258 bytes([0x65, 0x92, 0x27, 0x03, 0x2f, 0x74, 0xe6, 0x44,
5259 0x8d, 0xff, 0x57, 0x9a, 0xc1, 0xff, 0x06, 0x10]), fent.uuid)
5260 self.assertEqual('scp-fwu-cfg', fent.fip_type)
5261 self.assertEqual(0x8c, fent.offset)
5262 self.assertEqual(len(ATF_BL31_DATA), fent.size)
5263 self.assertEqual(0, fent.flags)
5264 self.assertEqual(ATF_BL2U_DATA, fent.data)
5265 self.assertEqual(True, fent.valid)
5266
5267 def testFipOther(self):
5268 """Basic FIP with something that isn't a external blob"""
5269 data = self._DoReadFile('204_fip_other.dts')
5270 hdr, fents = fip_util.decode_fip(data)
5271
5272 self.assertEqual(2, len(fents))
5273 fent = fents[1]
5274 self.assertEqual('rot-cert', fent.fip_type)
5275 self.assertEqual(b'aa', fent.data)
5276
Simon Glass3efb2972021-11-23 21:08:59 -07005277 def testFipNoType(self):
5278 """FIP with an entry of an unknown type"""
5279 with self.assertRaises(ValueError) as e:
5280 self._DoReadFile('205_fip_no_type.dts')
5281 self.assertIn("Must provide a fip-type (node name 'u-boot' is not a known FIP type)",
5282 str(e.exception))
5283
5284 def testFipUuid(self):
5285 """Basic FIP with a manual uuid"""
5286 data = self._DoReadFile('206_fip_uuid.dts')
5287 hdr, fents = fip_util.decode_fip(data)
5288
5289 self.assertEqual(2, len(fents))
5290 fent = fents[1]
5291 self.assertEqual(None, fent.fip_type)
5292 self.assertEqual(
5293 bytes([0xfc, 0x65, 0x13, 0x92, 0x4a, 0x5b, 0x11, 0xec,
5294 0x94, 0x35, 0xff, 0x2d, 0x1c, 0xfc, 0x79, 0x9c]),
5295 fent.uuid)
5296 self.assertEqual(U_BOOT_DATA, fent.data)
5297
5298 def testFipLs(self):
5299 """Test listing a FIP"""
5300 data = self._DoReadFileRealDtb('207_fip_ls.dts')
5301 hdr, fents = fip_util.decode_fip(data)
5302
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01005303 tmpdir = None
Simon Glass3efb2972021-11-23 21:08:59 -07005304 try:
5305 tmpdir, updated_fname = self._SetupImageInTmpdir()
Simon Glass14d64e32025-04-29 07:21:59 -06005306 with terminal.capture() as (stdout, stderr):
Simon Glass3efb2972021-11-23 21:08:59 -07005307 self._DoBinman('ls', '-i', updated_fname)
5308 finally:
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01005309 if tmpdir:
5310 shutil.rmtree(tmpdir)
Simon Glass3efb2972021-11-23 21:08:59 -07005311 lines = stdout.getvalue().splitlines()
5312 expected = [
Simon Glass49cd2b32023-02-07 14:34:18 -07005313'Name Image-pos Size Entry-type Offset Uncomp-size',
5314'--------------------------------------------------------------',
5315'image 0 2d3 section 0',
5316' atf-fip 0 90 atf-fip 0',
5317' soc-fw 88 4 blob-ext 88',
5318' u-boot 8c 4 u-boot 8c',
5319' fdtmap 90 243 fdtmap 90',
Simon Glass3efb2972021-11-23 21:08:59 -07005320]
5321 self.assertEqual(expected, lines)
5322
5323 image = control.images['image']
5324 entries = image.GetEntries()
5325 fdtmap = entries['fdtmap']
5326
5327 fdtmap_data = data[fdtmap.image_pos:fdtmap.image_pos + fdtmap.size]
5328 magic = fdtmap_data[:8]
5329 self.assertEqual(b'_FDTMAP_', magic)
Simon Glass80025522022-01-29 14:14:04 -07005330 self.assertEqual(tools.get_bytes(0, 8), fdtmap_data[8:16])
Simon Glass3efb2972021-11-23 21:08:59 -07005331
5332 fdt_data = fdtmap_data[16:]
5333 dtb = fdt.Fdt.FromData(fdt_data)
5334 dtb.Scan()
5335 props = self._GetPropTree(dtb, BASE_DTB_PROPS, prefix='/')
5336 self.assertEqual({
5337 'atf-fip/soc-fw:image-pos': 136,
5338 'atf-fip/soc-fw:offset': 136,
5339 'atf-fip/soc-fw:size': 4,
5340 'atf-fip/u-boot:image-pos': 140,
5341 'atf-fip/u-boot:offset': 140,
5342 'atf-fip/u-boot:size': 4,
5343 'atf-fip:image-pos': 0,
5344 'atf-fip:offset': 0,
5345 'atf-fip:size': 144,
5346 'image-pos': 0,
5347 'offset': 0,
5348 'fdtmap:image-pos': fdtmap.image_pos,
5349 'fdtmap:offset': fdtmap.offset,
5350 'fdtmap:size': len(fdtmap_data),
5351 'size': len(data),
5352 }, props)
5353
5354 def testFipExtractOneEntry(self):
5355 """Test extracting a single entry fron an FIP"""
5356 self._DoReadFileRealDtb('207_fip_ls.dts')
Simon Glass80025522022-01-29 14:14:04 -07005357 image_fname = tools.get_output_filename('image.bin')
Simon Glass3efb2972021-11-23 21:08:59 -07005358 fname = os.path.join(self._indir, 'output.extact')
5359 control.ExtractEntries(image_fname, fname, None, ['atf-fip/u-boot'])
Simon Glass80025522022-01-29 14:14:04 -07005360 data = tools.read_file(fname)
Simon Glass3efb2972021-11-23 21:08:59 -07005361 self.assertEqual(U_BOOT_DATA, data)
5362
5363 def testFipReplace(self):
5364 """Test replacing a single file in a FIP"""
Simon Glass80025522022-01-29 14:14:04 -07005365 expected = U_BOOT_DATA + tools.get_bytes(0x78, 50)
Simon Glass3efb2972021-11-23 21:08:59 -07005366 data = self._DoReadFileRealDtb('208_fip_replace.dts')
Simon Glass80025522022-01-29 14:14:04 -07005367 updated_fname = tools.get_output_filename('image-updated.bin')
5368 tools.write_file(updated_fname, data)
Simon Glass3efb2972021-11-23 21:08:59 -07005369 entry_name = 'atf-fip/u-boot'
5370 control.WriteEntry(updated_fname, entry_name, expected,
5371 allow_resize=True)
5372 actual = control.ReadEntry(updated_fname, entry_name)
5373 self.assertEqual(expected, actual)
5374
Simon Glass80025522022-01-29 14:14:04 -07005375 new_data = tools.read_file(updated_fname)
Simon Glass3efb2972021-11-23 21:08:59 -07005376 hdr, fents = fip_util.decode_fip(new_data)
5377
5378 self.assertEqual(2, len(fents))
5379
5380 # Check that the FIP entry is updated
5381 fent = fents[1]
5382 self.assertEqual(0x8c, fent.offset)
5383 self.assertEqual(len(expected), fent.size)
5384 self.assertEqual(0, fent.flags)
5385 self.assertEqual(expected, fent.data)
5386 self.assertEqual(True, fent.valid)
5387
5388 def testFipMissing(self):
Simon Glass14d64e32025-04-29 07:21:59 -06005389 with terminal.capture() as (stdout, stderr):
Simon Glass3efb2972021-11-23 21:08:59 -07005390 self._DoTestFile('209_fip_missing.dts', allow_missing=True)
5391 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07005392 self.assertRegex(err, "Image 'image'.*missing.*: rmm-fw")
Simon Glass3efb2972021-11-23 21:08:59 -07005393
5394 def testFipSize(self):
5395 """Test a FIP with a size property"""
5396 data = self._DoReadFile('210_fip_size.dts')
5397 self.assertEqual(0x100 + len(U_BOOT_DATA), len(data))
5398 hdr, fents = fip_util.decode_fip(data)
5399 self.assertEqual(fip_util.HEADER_MAGIC, hdr.name)
5400 self.assertEqual(fip_util.HEADER_SERIAL, hdr.serial)
5401
5402 self.assertEqual(1, len(fents))
5403
5404 fent = fents[0]
5405 self.assertEqual('soc-fw', fent.fip_type)
5406 self.assertEqual(0x60, fent.offset)
5407 self.assertEqual(len(ATF_BL31_DATA), fent.size)
5408 self.assertEqual(ATF_BL31_DATA, fent.data)
5409 self.assertEqual(True, fent.valid)
5410
5411 rest = data[0x60 + len(ATF_BL31_DATA):0x100]
Simon Glass80025522022-01-29 14:14:04 -07005412 self.assertEqual(tools.get_bytes(0xff, len(rest)), rest)
Simon Glass3efb2972021-11-23 21:08:59 -07005413
5414 def testFipBadAlign(self):
5415 """Test that an invalid alignment value in a FIP is detected"""
5416 with self.assertRaises(ValueError) as e:
5417 self._DoTestFile('211_fip_bad_align.dts')
5418 self.assertIn(
5419 "Node \'/binman/atf-fip\': FIP alignment 31 must be a power of two",
5420 str(e.exception))
5421
5422 def testFipCollection(self):
5423 """Test using a FIP in a collection"""
5424 data = self._DoReadFile('212_fip_collection.dts')
5425 entry1 = control.images['image'].GetEntries()['collection']
5426 data1 = data[:entry1.size]
5427 hdr1, fents2 = fip_util.decode_fip(data1)
5428
5429 entry2 = control.images['image'].GetEntries()['atf-fip']
5430 data2 = data[entry2.offset:entry2.offset + entry2.size]
5431 hdr1, fents2 = fip_util.decode_fip(data2)
5432
5433 # The 'collection' entry should have U-Boot included at the end
5434 self.assertEqual(entry1.size - len(U_BOOT_DATA), entry2.size)
5435 self.assertEqual(data1, data2 + U_BOOT_DATA)
5436 self.assertEqual(U_BOOT_DATA, data1[-4:])
5437
5438 # There should be a U-Boot after the final FIP
5439 self.assertEqual(U_BOOT_DATA, data[-4:])
Simon Glass76f496d2021-07-06 10:36:37 -06005440
Simon Glassccae6862022-01-12 13:10:35 -07005441 def testFakeBlob(self):
5442 """Test handling of faking an external blob"""
Simon Glass14d64e32025-04-29 07:21:59 -06005443 with terminal.capture() as (stdout, stderr):
Simon Glassccae6862022-01-12 13:10:35 -07005444 self._DoTestFile('217_fake_blob.dts', allow_missing=True,
5445 allow_fake_blobs=True)
5446 err = stderr.getvalue()
5447 self.assertRegex(
5448 err,
5449 "Image '.*' has faked external blobs and is non-functional: .*")
Simon Glassccae6862022-01-12 13:10:35 -07005450
Simon Glassceb5f912022-01-09 20:13:46 -07005451 def testExtblobListFaked(self):
5452 """Test an extblob with missing external blob that are faked"""
Simon Glass14d64e32025-04-29 07:21:59 -06005453 with terminal.capture() as (stdout, stderr):
Simon Glassceb5f912022-01-09 20:13:46 -07005454 self._DoTestFile('216_blob_ext_list_missing.dts',
5455 allow_fake_blobs=True)
5456 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07005457 self.assertRegex(err, "Image 'image'.*faked.*: blob-ext-list")
Simon Glassceb5f912022-01-09 20:13:46 -07005458
Simon Glass162017b2022-01-09 20:13:57 -07005459 def testListBintools(self):
5460 args = ['tool', '--list']
Simon Glass14d64e32025-04-29 07:21:59 -06005461 with terminal.capture() as (stdout, _):
Simon Glass162017b2022-01-09 20:13:57 -07005462 self._DoBinman(*args)
5463 out = stdout.getvalue().splitlines()
5464 self.assertTrue(len(out) >= 2)
5465
5466 def testFetchBintools(self):
5467 def fail_download(url):
Simon Glass80025522022-01-29 14:14:04 -07005468 """Take the tools.download() function by raising an exception"""
Simon Glass162017b2022-01-09 20:13:57 -07005469 raise urllib.error.URLError('my error')
5470
5471 args = ['tool']
5472 with self.assertRaises(ValueError) as e:
5473 self._DoBinman(*args)
5474 self.assertIn("Invalid arguments to 'tool' subcommand",
5475 str(e.exception))
5476
5477 args = ['tool', '--fetch']
5478 with self.assertRaises(ValueError) as e:
5479 self._DoBinman(*args)
5480 self.assertIn('Please specify bintools to fetch', str(e.exception))
5481
5482 args = ['tool', '--fetch', '_testing']
Simon Glass80025522022-01-29 14:14:04 -07005483 with unittest.mock.patch.object(tools, 'download',
Simon Glass162017b2022-01-09 20:13:57 -07005484 side_effect=fail_download):
Simon Glass14d64e32025-04-29 07:21:59 -06005485 with terminal.capture() as (stdout, _):
Simon Glass162017b2022-01-09 20:13:57 -07005486 self._DoBinman(*args)
5487 self.assertIn('failed to fetch with all methods', stdout.getvalue())
5488
Simon Glass620c4462022-01-09 20:14:11 -07005489 def testBintoolDocs(self):
5490 """Test for creation of bintool documentation"""
Simon Glass14d64e32025-04-29 07:21:59 -06005491 with terminal.capture() as (stdout, stderr):
Simon Glass620c4462022-01-09 20:14:11 -07005492 control.write_bintool_docs(control.bintool.Bintool.get_tool_list())
5493 self.assertTrue(len(stdout.getvalue()) > 0)
5494
5495 def testBintoolDocsMissing(self):
5496 """Test handling of missing bintool documentation"""
5497 with self.assertRaises(ValueError) as e:
Simon Glass14d64e32025-04-29 07:21:59 -06005498 with terminal.capture() as (stdout, stderr):
Simon Glass620c4462022-01-09 20:14:11 -07005499 control.write_bintool_docs(
5500 control.bintool.Bintool.get_tool_list(), 'mkimage')
5501 self.assertIn('Documentation is missing for modules: mkimage',
5502 str(e.exception))
5503
Jan Kiszka58c407f2022-01-28 20:37:53 +01005504 def testListWithGenNode(self):
5505 """Check handling of an FDT map when the section cannot be found"""
5506 entry_args = {
5507 'of-list': 'test-fdt1 test-fdt2',
5508 }
5509 data = self._DoReadFileDtb(
5510 '219_fit_gennode.dts',
5511 entry_args=entry_args,
5512 use_real_dtb=True,
5513 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])
5514
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01005515 tmpdir = None
Jan Kiszka58c407f2022-01-28 20:37:53 +01005516 try:
5517 tmpdir, updated_fname = self._SetupImageInTmpdir()
Simon Glass14d64e32025-04-29 07:21:59 -06005518 with terminal.capture() as (stdout, stderr):
Jan Kiszka58c407f2022-01-28 20:37:53 +01005519 self._RunBinman('ls', '-i', updated_fname)
5520 finally:
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01005521 if tmpdir:
5522 shutil.rmtree(tmpdir)
Jan Kiszka58c407f2022-01-28 20:37:53 +01005523
Alper Nebi Yasake706d872022-02-08 01:08:05 +03005524 def testFitSubentryUsesBintool(self):
5525 """Test that binman FIT subentries can use bintools"""
Simon Glass5dc22cf2025-02-03 09:26:42 -07005526 command.TEST_RESULT = self._HandleGbbCommand
Alper Nebi Yasake706d872022-02-08 01:08:05 +03005527 entry_args = {
5528 'keydir': 'devkeys',
5529 'bmpblk': 'bmpblk.bin',
5530 }
5531 data, _, _, _ = self._DoReadFileDtb('220_fit_subentry_bintool.dts',
5532 entry_args=entry_args)
5533
Alper Nebi Yasakd4553262022-02-08 01:08:07 +03005534 expected = (GBB_DATA + GBB_DATA + tools.get_bytes(0, 8) +
5535 tools.get_bytes(0, 0x2180 - 16))
Alper Nebi Yasake706d872022-02-08 01:08:05 +03005536 self.assertIn(expected, data)
5537
5538 def testFitSubentryMissingBintool(self):
5539 """Test that binman reports missing bintools for FIT subentries"""
5540 entry_args = {
5541 'keydir': 'devkeys',
5542 }
Simon Glass14d64e32025-04-29 07:21:59 -06005543 with terminal.capture() as (_, stderr):
Alper Nebi Yasake706d872022-02-08 01:08:05 +03005544 self._DoTestFile('220_fit_subentry_bintool.dts',
5545 force_missing_bintools='futility', entry_args=entry_args)
5546 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07005547 self.assertRegex(err, "Image 'image'.*missing bintools.*: futility")
Simon Glassccae6862022-01-12 13:10:35 -07005548
Alper Nebi Yasak1e4ffd82022-02-09 22:02:35 +03005549 def testFitSubentryHashSubnode(self):
5550 """Test an image with a FIT inside"""
Marek Vasutf7413f02023-07-18 07:23:58 -06005551 self._SetupSplElf()
Alper Nebi Yasak1e4ffd82022-02-09 22:02:35 +03005552 data, _, _, out_dtb_name = self._DoReadFileDtb(
5553 '221_fit_subentry_hash.dts', use_real_dtb=True, update_dtb=True)
5554
5555 mkimage_dtb = fdt.Fdt.FromData(data)
5556 mkimage_dtb.Scan()
5557 binman_dtb = fdt.Fdt(out_dtb_name)
5558 binman_dtb.Scan()
5559
5560 # Check that binman didn't add hash values
5561 fnode = binman_dtb.GetNode('/binman/fit/images/kernel/hash')
5562 self.assertNotIn('value', fnode.props)
5563
5564 fnode = binman_dtb.GetNode('/binman/fit/images/fdt-1/hash')
5565 self.assertNotIn('value', fnode.props)
5566
5567 # Check that mkimage added hash values
5568 fnode = mkimage_dtb.GetNode('/images/kernel/hash')
5569 self.assertIn('value', fnode.props)
5570
5571 fnode = mkimage_dtb.GetNode('/images/fdt-1/hash')
5572 self.assertIn('value', fnode.props)
5573
Roger Quadros5cdcea02022-02-19 20:50:04 +02005574 def testPackTeeOs(self):
5575 """Test that an image with an TEE binary can be created"""
5576 data = self._DoReadFile('222_tee_os.dts')
5577 self.assertEqual(TEE_OS_DATA, data[:len(TEE_OS_DATA)])
5578
Neha Malcom Francis59be2552023-12-05 15:12:18 +05305579 def testPackTiDm(self):
5580 """Test that an image with a TI DM binary can be created"""
5581 data = self._DoReadFile('225_ti_dm.dts')
5582 self.assertEqual(TI_DM_DATA, data[:len(TI_DM_DATA)])
5583
Bryan Brattlof5b45f4b2025-05-15 10:16:19 -05005584 def testPackBl1(self):
5585 """test if an image with a bl1 binary can be created"""
5586 data = self._DoReadFile('347_bl1.dts')
5587 self.assertEqual(ATF_BL1_DATA, data[:len(ATF_BL1_DATA)])
5588
Simon Glass912339f2022-02-08 11:50:03 -07005589 def testFitFdtOper(self):
5590 """Check handling of a specified FIT operation"""
5591 entry_args = {
5592 'of-list': 'test-fdt1 test-fdt2',
5593 'default-dt': 'test-fdt2',
5594 }
5595 self._DoReadFileDtb(
5596 '223_fit_fdt_oper.dts',
5597 entry_args=entry_args,
5598 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
5599
5600 def testFitFdtBadOper(self):
5601 """Check handling of an FDT map when the section cannot be found"""
5602 with self.assertRaises(ValueError) as exc:
5603 self._DoReadFileDtb('224_fit_bad_oper.dts')
Simon Glass05f71dc2022-03-05 20:19:09 -07005604 self.assertIn("Node '/binman/fit': subnode 'images/@fdt-SEQ': Unknown operation 'unknown'",
Simon Glass912339f2022-02-08 11:50:03 -07005605 str(exc.exception))
5606
Simon Glassdd156a42022-03-05 20:18:59 -07005607 def test_uses_expand_size(self):
5608 """Test that the 'expand-size' property cannot be used anymore"""
5609 with self.assertRaises(ValueError) as e:
5610 data = self._DoReadFile('225_expand_size_bad.dts')
5611 self.assertIn(
5612 "Node '/binman/u-boot': Please use 'extend-size' instead of 'expand-size'",
5613 str(e.exception))
5614
Simon Glass5f423422022-03-05 20:19:12 -07005615 def testFitSplitElf(self):
5616 """Test an image with an FIT with an split-elf operation"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005617 if not elf.ELF_TOOLS:
5618 self.skipTest('Python elftools not available')
Simon Glass5f423422022-03-05 20:19:12 -07005619 entry_args = {
5620 'of-list': 'test-fdt1 test-fdt2',
5621 'default-dt': 'test-fdt2',
5622 'atf-bl31-path': 'bl31.elf',
5623 'tee-os-path': 'tee.elf',
5624 }
5625 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
5626 data = self._DoReadFileDtb(
5627 '226_fit_split_elf.dts',
5628 entry_args=entry_args,
5629 extra_indirs=[test_subdir])[0]
5630
5631 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
5632 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
5633
5634 base_keys = {'description', 'type', 'arch', 'os', 'compression',
5635 'data', 'load'}
5636 dtb = fdt.Fdt.FromData(fit_data)
5637 dtb.Scan()
5638
5639 elf_data = tools.read_file(os.path.join(self._indir, 'bl31.elf'))
5640 segments, entry = elf.read_loadable_segments(elf_data)
5641
5642 # We assume there are two segments
Brandon Maiera657bc62024-06-04 16:16:05 +00005643 self.assertEqual(2, len(segments))
Simon Glass5f423422022-03-05 20:19:12 -07005644
5645 atf1 = dtb.GetNode('/images/atf-1')
5646 _, start, data = segments[0]
5647 self.assertEqual(base_keys | {'entry'}, atf1.props.keys())
5648 self.assertEqual(entry,
5649 fdt_util.fdt32_to_cpu(atf1.props['entry'].value))
5650 self.assertEqual(start,
5651 fdt_util.fdt32_to_cpu(atf1.props['load'].value))
5652 self.assertEqual(data, atf1.props['data'].bytes)
5653
Jonas Karlmand2c7d902023-01-21 19:01:48 +00005654 hash_node = atf1.FindNode('hash')
5655 self.assertIsNotNone(hash_node)
5656 self.assertEqual({'algo', 'value'}, hash_node.props.keys())
5657
Simon Glass5f423422022-03-05 20:19:12 -07005658 atf2 = dtb.GetNode('/images/atf-2')
5659 self.assertEqual(base_keys, atf2.props.keys())
5660 _, start, data = segments[1]
5661 self.assertEqual(start,
5662 fdt_util.fdt32_to_cpu(atf2.props['load'].value))
5663 self.assertEqual(data, atf2.props['data'].bytes)
5664
Jonas Karlmand2c7d902023-01-21 19:01:48 +00005665 hash_node = atf2.FindNode('hash')
5666 self.assertIsNotNone(hash_node)
5667 self.assertEqual({'algo', 'value'}, hash_node.props.keys())
5668
5669 hash_node = dtb.GetNode('/images/tee-1/hash-1')
5670 self.assertIsNotNone(hash_node)
5671 self.assertEqual({'algo', 'value'}, hash_node.props.keys())
5672
Simon Glass5f423422022-03-05 20:19:12 -07005673 conf = dtb.GetNode('/configurations')
5674 self.assertEqual({'default'}, conf.props.keys())
5675
5676 for subnode in conf.subnodes:
5677 self.assertEqual({'description', 'fdt', 'loadables'},
5678 subnode.props.keys())
5679 self.assertEqual(
5680 ['atf-1', 'atf-2', 'tee-1', 'tee-2'],
5681 fdt_util.GetStringList(subnode, 'loadables'))
5682
5683 def _check_bad_fit(self, dts):
5684 """Check a bad FIT
5685
5686 This runs with the given dts and returns the assertion raised
5687
5688 Args:
5689 dts (str): dts filename to use
5690
5691 Returns:
5692 str: Assertion string raised
5693 """
5694 entry_args = {
5695 'of-list': 'test-fdt1 test-fdt2',
5696 'default-dt': 'test-fdt2',
5697 'atf-bl31-path': 'bl31.elf',
5698 'tee-os-path': 'tee.elf',
5699 }
5700 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
5701 with self.assertRaises(ValueError) as exc:
5702 self._DoReadFileDtb(dts, entry_args=entry_args,
5703 extra_indirs=[test_subdir])[0]
5704 return str(exc.exception)
5705
5706 def testFitSplitElfBadElf(self):
5707 """Test a FIT split-elf operation with an invalid ELF file"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005708 if not elf.ELF_TOOLS:
5709 self.skipTest('Python elftools not available')
Simon Glass5f423422022-03-05 20:19:12 -07005710 TestFunctional._MakeInputFile('bad.elf', tools.get_bytes(100, 100))
5711 entry_args = {
5712 'of-list': 'test-fdt1 test-fdt2',
5713 'default-dt': 'test-fdt2',
5714 'atf-bl31-path': 'bad.elf',
5715 'tee-os-path': 'tee.elf',
5716 }
5717 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
5718 with self.assertRaises(ValueError) as exc:
5719 self._DoReadFileDtb(
5720 '226_fit_split_elf.dts',
5721 entry_args=entry_args,
5722 extra_indirs=[test_subdir])[0]
5723 self.assertIn(
5724 "Node '/binman/fit': subnode 'images/@atf-SEQ': Failed to read ELF file: Magic number does not match",
5725 str(exc.exception))
5726
Simon Glass5f423422022-03-05 20:19:12 -07005727 def checkFitSplitElf(self, **kwargs):
Simon Glass7d3e4072022-08-07 09:46:46 -06005728 """Test an split-elf FIT with a missing ELF file
5729
5730 Args:
5731 kwargs (dict of str): Arguments to pass to _DoTestFile()
5732
5733 Returns:
5734 tuple:
5735 str: stdout result
5736 str: stderr result
5737 """
Simon Glass5f423422022-03-05 20:19:12 -07005738 entry_args = {
5739 'of-list': 'test-fdt1 test-fdt2',
5740 'default-dt': 'test-fdt2',
5741 'atf-bl31-path': 'bl31.elf',
5742 'tee-os-path': 'missing.elf',
5743 }
5744 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
Simon Glass14d64e32025-04-29 07:21:59 -06005745 with terminal.capture() as (stdout, stderr):
Simon Glass5f423422022-03-05 20:19:12 -07005746 self._DoTestFile(
5747 '226_fit_split_elf.dts', entry_args=entry_args,
Simon Glass7d3e4072022-08-07 09:46:46 -06005748 extra_indirs=[test_subdir], verbosity=3, **kwargs)
5749 out = stdout.getvalue()
5750 err = stderr.getvalue()
5751 return out, err
Simon Glass5f423422022-03-05 20:19:12 -07005752
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005753 def testFitSplitElfBadDirective(self):
5754 """Test a FIT split-elf invalid fit,xxx directive in an image node"""
5755 if not elf.ELF_TOOLS:
5756 self.skipTest('Python elftools not available')
5757 err = self._check_bad_fit('227_fit_bad_dir.dts')
5758 self.assertIn(
5759 "Node '/binman/fit': subnode 'images/@atf-SEQ': Unknown directive 'fit,something'",
5760 err)
5761
5762 def testFitSplitElfBadDirectiveConfig(self):
5763 """Test a FIT split-elf with invalid fit,xxx directive in config"""
5764 if not elf.ELF_TOOLS:
5765 self.skipTest('Python elftools not available')
5766 err = self._check_bad_fit('228_fit_bad_dir_config.dts')
5767 self.assertEqual(
5768 "Node '/binman/fit': subnode 'configurations/@config-SEQ': Unknown directive 'fit,config'",
5769 err)
5770
5771
Simon Glass5f423422022-03-05 20:19:12 -07005772 def testFitSplitElfMissing(self):
Yannic Moog6f2fd072025-06-13 14:02:44 +02005773 """Test an split-elf FIT with a missing ELF file. Don't fake the file."""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005774 if not elf.ELF_TOOLS:
5775 self.skipTest('Python elftools not available')
Yannic Moog6f2fd072025-06-13 14:02:44 +02005776 out, err = self.checkFitSplitElf(allow_missing=True, allow_fake_blobs=False)
Simon Glass5f423422022-03-05 20:19:12 -07005777 self.assertRegex(
5778 err,
5779 "Image '.*' is missing external blobs and is non-functional: .*")
Simon Glass7d3e4072022-08-07 09:46:46 -06005780 self.assertNotRegex(out, '.*Faked blob.*')
5781 fname = tools.get_output_filename('binman-fake/missing.elf')
5782 self.assertFalse(os.path.exists(fname))
Simon Glass5f423422022-03-05 20:19:12 -07005783
5784 def testFitSplitElfFaked(self):
5785 """Test an split-elf FIT with faked ELF file"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005786 if not elf.ELF_TOOLS:
5787 self.skipTest('Python elftools not available')
Simon Glass7d3e4072022-08-07 09:46:46 -06005788 out, err = self.checkFitSplitElf(allow_missing=True, allow_fake_blobs=True)
Simon Glass5f423422022-03-05 20:19:12 -07005789 self.assertRegex(
5790 err,
5791 "Image '.*' is missing external blobs and is non-functional: .*")
Simon Glass7d3e4072022-08-07 09:46:46 -06005792 self.assertRegex(
5793 out,
5794 "Entry '/binman/fit/images/@tee-SEQ/tee-os': Faked blob '.*binman-fake/missing.elf")
5795 fname = tools.get_output_filename('binman-fake/missing.elf')
5796 self.assertTrue(os.path.exists(fname))
Simon Glass5f423422022-03-05 20:19:12 -07005797
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005798 def testMkimageMissingBlob(self):
5799 """Test using mkimage to build an image"""
Simon Glass14d64e32025-04-29 07:21:59 -06005800 with terminal.capture() as (stdout, stderr):
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005801 self._DoTestFile('229_mkimage_missing.dts', allow_missing=True,
5802 allow_fake_blobs=True)
5803 err = stderr.getvalue()
5804 self.assertRegex(
5805 err,
5806 "Image '.*' has faked external blobs and is non-functional: .*")
5807
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005808 def testPreLoad(self):
5809 """Test an image with a pre-load header"""
5810 entry_args = {
Simon Glasse2dfb962023-07-24 09:19:57 -06005811 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005812 }
Simon Glasse2dfb962023-07-24 09:19:57 -06005813 data = self._DoReadFileDtb(
5814 '230_pre_load.dts', entry_args=entry_args,
5815 extra_indirs=[os.path.join(self._binman_dir, 'test')])[0]
Paul HENRYS5cf82892025-02-24 22:20:55 +01005816
5817 image_fname = tools.get_output_filename('image.bin')
5818 is_signed = self._CheckPreload(image_fname, self.TestFile("dev.key"))
5819
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005820 self.assertEqual(PRE_LOAD_MAGIC, data[:len(PRE_LOAD_MAGIC)])
5821 self.assertEqual(PRE_LOAD_VERSION, data[4:4 + len(PRE_LOAD_VERSION)])
5822 self.assertEqual(PRE_LOAD_HDR_SIZE, data[8:8 + len(PRE_LOAD_HDR_SIZE)])
Paul HENRYS5cf82892025-02-24 22:20:55 +01005823 self.assertEqual(is_signed, True)
Simon Glasse2dfb962023-07-24 09:19:57 -06005824
5825 def testPreLoadNoKey(self):
5826 """Test an image with a pre-load heade0r with missing key"""
5827 with self.assertRaises(FileNotFoundError) as exc:
5828 self._DoReadFile('230_pre_load.dts')
5829 self.assertIn("No such file or directory: 'dev.key'",
5830 str(exc.exception))
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005831
5832 def testPreLoadPkcs(self):
5833 """Test an image with a pre-load header with padding pkcs"""
Simon Glasse2dfb962023-07-24 09:19:57 -06005834 entry_args = {
5835 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5836 }
5837 data = self._DoReadFileDtb('231_pre_load_pkcs.dts',
5838 entry_args=entry_args)[0]
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005839 self.assertEqual(PRE_LOAD_MAGIC, data[:len(PRE_LOAD_MAGIC)])
5840 self.assertEqual(PRE_LOAD_VERSION, data[4:4 + len(PRE_LOAD_VERSION)])
5841 self.assertEqual(PRE_LOAD_HDR_SIZE, data[8:8 + len(PRE_LOAD_HDR_SIZE)])
5842
5843 def testPreLoadPss(self):
5844 """Test an image with a pre-load header with padding pss"""
Simon Glasse2dfb962023-07-24 09:19:57 -06005845 entry_args = {
5846 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5847 }
5848 data = self._DoReadFileDtb('232_pre_load_pss.dts',
5849 entry_args=entry_args)[0]
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005850 self.assertEqual(PRE_LOAD_MAGIC, data[:len(PRE_LOAD_MAGIC)])
5851 self.assertEqual(PRE_LOAD_VERSION, data[4:4 + len(PRE_LOAD_VERSION)])
5852 self.assertEqual(PRE_LOAD_HDR_SIZE, data[8:8 + len(PRE_LOAD_HDR_SIZE)])
5853
5854 def testPreLoadInvalidPadding(self):
5855 """Test an image with a pre-load header with an invalid padding"""
Simon Glasse2dfb962023-07-24 09:19:57 -06005856 entry_args = {
5857 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5858 }
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005859 with self.assertRaises(ValueError) as e:
Simon Glasse2dfb962023-07-24 09:19:57 -06005860 self._DoReadFileDtb('233_pre_load_invalid_padding.dts',
5861 entry_args=entry_args)
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005862
5863 def testPreLoadInvalidSha(self):
5864 """Test an image with a pre-load header with an invalid hash"""
Simon Glasse2dfb962023-07-24 09:19:57 -06005865 entry_args = {
5866 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5867 }
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005868 with self.assertRaises(ValueError) as e:
Simon Glasse2dfb962023-07-24 09:19:57 -06005869 self._DoReadFileDtb('234_pre_load_invalid_sha.dts',
5870 entry_args=entry_args)
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005871
5872 def testPreLoadInvalidAlgo(self):
5873 """Test an image with a pre-load header with an invalid algo"""
5874 with self.assertRaises(ValueError) as e:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005875 data = self._DoReadFile('235_pre_load_invalid_algo.dts')
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005876
5877 def testPreLoadInvalidKey(self):
5878 """Test an image with a pre-load header with an invalid key"""
Simon Glasse2dfb962023-07-24 09:19:57 -06005879 entry_args = {
5880 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5881 }
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005882 with self.assertRaises(ValueError) as e:
Simon Glasse2dfb962023-07-24 09:19:57 -06005883 data = self._DoReadFileDtb('236_pre_load_invalid_key.dts',
5884 entry_args=entry_args)
Roger Quadros5cdcea02022-02-19 20:50:04 +02005885
Alper Nebi Yasak5cff63f2022-03-27 18:31:44 +03005886 def _CheckSafeUniqueNames(self, *images):
5887 """Check all entries of given images for unsafe unique names"""
5888 for image in images:
5889 entries = {}
5890 image._CollectEntries(entries, {}, image)
5891 for entry in entries.values():
5892 uniq = entry.GetUniqueName()
5893
5894 # Used as part of a filename, so must not be absolute paths.
5895 self.assertFalse(os.path.isabs(uniq))
5896
5897 def testSafeUniqueNames(self):
5898 """Test entry unique names are safe in single image configuration"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005899 data = self._DoReadFileRealDtb('237_unique_names.dts')
Alper Nebi Yasak5cff63f2022-03-27 18:31:44 +03005900
5901 orig_image = control.images['image']
5902 image_fname = tools.get_output_filename('image.bin')
5903 image = Image.FromFile(image_fname)
5904
5905 self._CheckSafeUniqueNames(orig_image, image)
5906
5907 def testSafeUniqueNamesMulti(self):
5908 """Test entry unique names are safe with multiple images"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005909 data = self._DoReadFileRealDtb('238_unique_names_multi.dts')
Alper Nebi Yasak5cff63f2022-03-27 18:31:44 +03005910
5911 orig_image = control.images['image']
5912 image_fname = tools.get_output_filename('image.bin')
5913 image = Image.FromFile(image_fname)
5914
5915 self._CheckSafeUniqueNames(orig_image, image)
5916
Alper Nebi Yasake63ca5a2022-03-27 18:31:45 +03005917 def testReplaceCmdWithBintool(self):
5918 """Test replacing an entry that needs a bintool to pack"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005919 data = self._DoReadFileRealDtb('239_replace_with_bintool.dts')
Alper Nebi Yasake63ca5a2022-03-27 18:31:45 +03005920 expected = U_BOOT_DATA + b'aa'
5921 self.assertEqual(expected, data[:len(expected)])
5922
5923 try:
5924 tmpdir, updated_fname = self._SetupImageInTmpdir()
5925 fname = os.path.join(tmpdir, 'update-testing.bin')
5926 tools.write_file(fname, b'zz')
5927 self._DoBinman('replace', '-i', updated_fname,
5928 '_testing', '-f', fname)
5929
5930 data = tools.read_file(updated_fname)
5931 expected = U_BOOT_DATA + b'zz'
5932 self.assertEqual(expected, data[:len(expected)])
5933 finally:
5934 shutil.rmtree(tmpdir)
5935
5936 def testReplaceCmdOtherWithBintool(self):
5937 """Test replacing an entry when another needs a bintool to pack"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005938 data = self._DoReadFileRealDtb('239_replace_with_bintool.dts')
Alper Nebi Yasake63ca5a2022-03-27 18:31:45 +03005939 expected = U_BOOT_DATA + b'aa'
5940 self.assertEqual(expected, data[:len(expected)])
5941
5942 try:
5943 tmpdir, updated_fname = self._SetupImageInTmpdir()
5944 fname = os.path.join(tmpdir, 'update-u-boot.bin')
5945 tools.write_file(fname, b'x' * len(U_BOOT_DATA))
5946 self._DoBinman('replace', '-i', updated_fname,
5947 'u-boot', '-f', fname)
5948
5949 data = tools.read_file(updated_fname)
5950 expected = b'x' * len(U_BOOT_DATA) + b'aa'
5951 self.assertEqual(expected, data[:len(expected)])
5952 finally:
5953 shutil.rmtree(tmpdir)
5954
Alper Nebi Yasak00c68f12022-03-27 18:31:46 +03005955 def testReplaceResizeNoRepackSameSize(self):
5956 """Test replacing entries with same-size data without repacking"""
5957 expected = b'x' * len(U_BOOT_DATA)
5958 data, expected_fdtmap, _ = self._RunReplaceCmd('u-boot', expected)
5959 self.assertEqual(expected, data)
5960
5961 path, fdtmap = state.GetFdtContents('fdtmap')
5962 self.assertIsNotNone(path)
5963 self.assertEqual(expected_fdtmap, fdtmap)
5964
5965 def testReplaceResizeNoRepackSmallerSize(self):
5966 """Test replacing entries with smaller-size data without repacking"""
5967 new_data = b'x'
5968 data, expected_fdtmap, _ = self._RunReplaceCmd('u-boot', new_data)
5969 expected = new_data.ljust(len(U_BOOT_DATA), b'\0')
5970 self.assertEqual(expected, data)
5971
5972 path, fdtmap = state.GetFdtContents('fdtmap')
5973 self.assertIsNotNone(path)
5974 self.assertEqual(expected_fdtmap, fdtmap)
5975
Alper Nebi Yasak6cadc502022-03-27 18:31:48 +03005976 def testExtractFit(self):
5977 """Test extracting a FIT section"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005978 self._DoReadFileRealDtb('240_fit_extract_replace.dts')
Alper Nebi Yasak6cadc502022-03-27 18:31:48 +03005979 image_fname = tools.get_output_filename('image.bin')
5980
5981 fit_data = control.ReadEntry(image_fname, 'fit')
5982 fit = fdt.Fdt.FromData(fit_data)
5983 fit.Scan()
5984
5985 # Check subentry data inside the extracted fit
5986 for node_path, expected in [
5987 ('/images/kernel', U_BOOT_DATA),
5988 ('/images/fdt-1', U_BOOT_NODTB_DATA),
5989 ('/images/scr-1', COMPRESS_DATA),
5990 ]:
5991 node = fit.GetNode(node_path)
5992 data = fit.GetProps(node)['data'].bytes
5993 self.assertEqual(expected, data)
5994
5995 def testExtractFitSubentries(self):
5996 """Test extracting FIT section subentries"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005997 self._DoReadFileRealDtb('240_fit_extract_replace.dts')
Alper Nebi Yasak6cadc502022-03-27 18:31:48 +03005998 image_fname = tools.get_output_filename('image.bin')
5999
6000 for entry_path, expected in [
6001 ('fit/kernel', U_BOOT_DATA),
6002 ('fit/kernel/u-boot', U_BOOT_DATA),
6003 ('fit/fdt-1', U_BOOT_NODTB_DATA),
6004 ('fit/fdt-1/u-boot-nodtb', U_BOOT_NODTB_DATA),
6005 ('fit/scr-1', COMPRESS_DATA),
6006 ('fit/scr-1/blob', COMPRESS_DATA),
6007 ]:
6008 data = control.ReadEntry(image_fname, entry_path)
6009 self.assertEqual(expected, data)
6010
Alper Nebi Yasak49892642022-03-27 18:31:49 +03006011 def testReplaceFitSubentryLeafSameSize(self):
6012 """Test replacing a FIT leaf subentry with same-size data"""
6013 new_data = b'x' * len(U_BOOT_DATA)
6014 data, expected_fdtmap, _ = self._RunReplaceCmd(
6015 'fit/kernel/u-boot', new_data,
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006016 dts='240_fit_extract_replace.dts')
Alper Nebi Yasak49892642022-03-27 18:31:49 +03006017 self.assertEqual(new_data, data)
6018
6019 path, fdtmap = state.GetFdtContents('fdtmap')
6020 self.assertIsNotNone(path)
6021 self.assertEqual(expected_fdtmap, fdtmap)
6022
6023 def testReplaceFitSubentryLeafBiggerSize(self):
6024 """Test replacing a FIT leaf subentry with bigger-size data"""
6025 new_data = b'ub' * len(U_BOOT_NODTB_DATA)
6026 data, expected_fdtmap, _ = self._RunReplaceCmd(
6027 'fit/fdt-1/u-boot-nodtb', new_data,
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006028 dts='240_fit_extract_replace.dts')
Alper Nebi Yasak49892642022-03-27 18:31:49 +03006029 self.assertEqual(new_data, data)
6030
6031 # Will be repacked, so fdtmap must change
6032 path, fdtmap = state.GetFdtContents('fdtmap')
6033 self.assertIsNotNone(path)
6034 self.assertNotEqual(expected_fdtmap, fdtmap)
6035
6036 def testReplaceFitSubentryLeafSmallerSize(self):
6037 """Test replacing a FIT leaf subentry with smaller-size data"""
6038 new_data = b'x'
6039 expected = new_data.ljust(len(U_BOOT_NODTB_DATA), b'\0')
6040 data, expected_fdtmap, _ = self._RunReplaceCmd(
6041 'fit/fdt-1/u-boot-nodtb', new_data,
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006042 dts='240_fit_extract_replace.dts')
Alper Nebi Yasak49892642022-03-27 18:31:49 +03006043 self.assertEqual(expected, data)
6044
6045 path, fdtmap = state.GetFdtContents('fdtmap')
6046 self.assertIsNotNone(path)
6047 self.assertEqual(expected_fdtmap, fdtmap)
6048
Alper Nebi Yasak1d44c8e2022-03-27 18:31:50 +03006049 def testReplaceSectionSimple(self):
Simon Glass49b77e82023-03-02 17:02:44 -07006050 """Test replacing a simple section with same-sized data"""
Alper Nebi Yasak1d44c8e2022-03-27 18:31:50 +03006051 new_data = b'w' * len(COMPRESS_DATA + U_BOOT_DATA)
Simon Glass49b77e82023-03-02 17:02:44 -07006052 data, expected_fdtmap, image = self._RunReplaceCmd('section',
6053 new_data, dts='241_replace_section_simple.dts')
6054 self.assertEqual(new_data, data)
6055
6056 entries = image.GetEntries()
6057 self.assertIn('section', entries)
6058 entry = entries['section']
6059 self.assertEqual(len(new_data), entry.size)
6060
6061 def testReplaceSectionLarger(self):
6062 """Test replacing a simple section with larger data"""
6063 new_data = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) + 1)
6064 data, expected_fdtmap, image = self._RunReplaceCmd('section',
6065 new_data, dts='241_replace_section_simple.dts')
6066 self.assertEqual(new_data, data)
6067
6068 entries = image.GetEntries()
6069 self.assertIn('section', entries)
6070 entry = entries['section']
6071 self.assertEqual(len(new_data), entry.size)
6072 fentry = entries['fdtmap']
6073 self.assertEqual(entry.offset + entry.size, fentry.offset)
6074
6075 def testReplaceSectionSmaller(self):
6076 """Test replacing a simple section with smaller data"""
6077 new_data = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) - 1) + b'\0'
6078 data, expected_fdtmap, image = self._RunReplaceCmd('section',
6079 new_data, dts='241_replace_section_simple.dts')
6080 self.assertEqual(new_data, data)
6081
6082 # The new size is the same as the old, just with a pad byte at the end
6083 entries = image.GetEntries()
6084 self.assertIn('section', entries)
6085 entry = entries['section']
6086 self.assertEqual(len(new_data), entry.size)
6087
6088 def testReplaceSectionSmallerAllow(self):
6089 """Test failing to replace a simple section with smaller data"""
6090 new_data = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) - 1)
6091 try:
6092 state.SetAllowEntryContraction(True)
6093 with self.assertRaises(ValueError) as exc:
6094 self._RunReplaceCmd('section', new_data,
6095 dts='241_replace_section_simple.dts')
6096 finally:
6097 state.SetAllowEntryContraction(False)
6098
6099 # Since we have no information about the position of things within the
6100 # section, we cannot adjust the position of /section-u-boot so it ends
6101 # up outside the section
Simon Glassc6b283f2022-08-13 11:40:46 -06006102 self.assertIn(
Simon Glass49b77e82023-03-02 17:02:44 -07006103 "Node '/section/u-boot': Offset 0x24 (36) size 0x4 (4) is outside "
6104 "the section '/section' starting at 0x0 (0) of size 0x27 (39)",
Simon Glassc6b283f2022-08-13 11:40:46 -06006105 str(exc.exception))
Alper Nebi Yasak1d44c8e2022-03-27 18:31:50 +03006106
Simon Glass8fbca772022-08-13 11:40:48 -06006107 def testMkimageImagename(self):
6108 """Test using mkimage with -n holding the data too"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006109 self._SetupSplElf()
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006110 data = self._DoReadFile('242_mkimage_name.dts')
Simon Glass8fbca772022-08-13 11:40:48 -06006111
6112 # Check that the data appears in the file somewhere
6113 self.assertIn(U_BOOT_SPL_DATA, data)
6114
Simon Glassbb7d3bb2022-09-06 20:26:52 -06006115 # Get struct legacy_img_hdr -> ih_name
Simon Glass8fbca772022-08-13 11:40:48 -06006116 name = data[0x20:0x40]
6117
6118 # Build the filename that we expect to be placed in there, by virtue of
6119 # the -n paraameter
6120 expect = os.path.join(tools.get_output_dir(), 'mkimage.mkimage')
6121
6122 # Check that the image name is set to the temporary filename used
6123 self.assertEqual(expect.encode('utf-8')[:0x20], name)
6124
Simon Glassb1669752022-08-13 11:40:49 -06006125 def testMkimageImage(self):
6126 """Test using mkimage with -n holding the data too"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006127 self._SetupSplElf()
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006128 data = self._DoReadFile('243_mkimage_image.dts')
Simon Glassb1669752022-08-13 11:40:49 -06006129
6130 # Check that the data appears in the file somewhere
6131 self.assertIn(U_BOOT_SPL_DATA, data)
6132
Simon Glassbb7d3bb2022-09-06 20:26:52 -06006133 # Get struct legacy_img_hdr -> ih_name
Simon Glassb1669752022-08-13 11:40:49 -06006134 name = data[0x20:0x40]
6135
6136 # Build the filename that we expect to be placed in there, by virtue of
6137 # the -n paraameter
6138 expect = os.path.join(tools.get_output_dir(), 'mkimage-n.mkimage')
6139
6140 # Check that the image name is set to the temporary filename used
6141 self.assertEqual(expect.encode('utf-8')[:0x20], name)
6142
6143 # Check the corect data is in the imagename file
6144 self.assertEqual(U_BOOT_DATA, tools.read_file(expect))
6145
6146 def testMkimageImageNoContent(self):
6147 """Test using mkimage with -n and no data"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006148 self._SetupSplElf()
Simon Glassb1669752022-08-13 11:40:49 -06006149 with self.assertRaises(ValueError) as exc:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006150 self._DoReadFile('244_mkimage_image_no_content.dts')
Simon Glassb1669752022-08-13 11:40:49 -06006151 self.assertIn('Could not complete processing of contents',
6152 str(exc.exception))
6153
6154 def testMkimageImageBad(self):
6155 """Test using mkimage with imagename node and data-to-imagename"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006156 self._SetupSplElf()
Simon Glassb1669752022-08-13 11:40:49 -06006157 with self.assertRaises(ValueError) as exc:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006158 self._DoReadFile('245_mkimage_image_bad.dts')
Simon Glassb1669752022-08-13 11:40:49 -06006159 self.assertIn('Cannot use both imagename node and data-to-imagename',
6160 str(exc.exception))
6161
Simon Glassbd5cd882022-08-13 11:40:50 -06006162 def testCollectionOther(self):
6163 """Test a collection where the data comes from another section"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006164 data = self._DoReadFile('246_collection_other.dts')
Simon Glassbd5cd882022-08-13 11:40:50 -06006165 self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA +
6166 tools.get_bytes(0xff, 2) + U_BOOT_NODTB_DATA +
6167 tools.get_bytes(0xfe, 3) + U_BOOT_DTB_DATA,
6168 data)
6169
6170 def testMkimageCollection(self):
6171 """Test using a collection referring to an entry in a mkimage entry"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006172 self._SetupSplElf()
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006173 data = self._DoReadFile('247_mkimage_coll.dts')
Simon Glassbd5cd882022-08-13 11:40:50 -06006174 expect = U_BOOT_SPL_DATA + U_BOOT_DATA
6175 self.assertEqual(expect, data[:len(expect)])
6176
Stefan Herbrechtsmeier11121d32022-08-19 16:25:25 +02006177 def testCompressDtbPrependInvalid(self):
6178 """Test that invalid header is detected"""
6179 with self.assertRaises(ValueError) as e:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006180 self._DoReadFileDtb('248_compress_dtb_prepend_invalid.dts')
Stefan Herbrechtsmeier11121d32022-08-19 16:25:25 +02006181 self.assertIn("Node '/binman/u-boot-dtb': Invalid prepend in "
6182 "'u-boot-dtb': 'invalid'", str(e.exception))
6183
6184 def testCompressDtbPrependLength(self):
6185 """Test that compress with length header works as expected"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006186 data = self._DoReadFileRealDtb('249_compress_dtb_prepend_length.dts')
Stefan Herbrechtsmeier11121d32022-08-19 16:25:25 +02006187 image = control.images['image']
6188 entries = image.GetEntries()
6189 self.assertIn('u-boot-dtb', entries)
6190 u_boot_dtb = entries['u-boot-dtb']
6191 self.assertIn('fdtmap', entries)
6192 fdtmap = entries['fdtmap']
6193
6194 image_fname = tools.get_output_filename('image.bin')
6195 orig = control.ReadEntry(image_fname, 'u-boot-dtb')
6196 dtb = fdt.Fdt.FromData(orig)
6197 dtb.Scan()
6198 props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
6199 expected = {
6200 'u-boot:size': len(U_BOOT_DATA),
6201 'u-boot-dtb:uncomp-size': len(orig),
6202 'u-boot-dtb:size': u_boot_dtb.size,
6203 'fdtmap:size': fdtmap.size,
6204 'size': len(data),
6205 }
6206 self.assertEqual(expected, props)
6207
6208 # Check implementation
6209 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
6210 rest = data[len(U_BOOT_DATA):]
6211 comp_data_len = struct.unpack('<I', rest[:4])[0]
6212 comp_data = rest[4:4 + comp_data_len]
6213 orig2 = self._decompress(comp_data)
6214 self.assertEqual(orig, orig2)
6215
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +02006216 def testInvalidCompress(self):
6217 """Test that invalid compress algorithm is detected"""
6218 with self.assertRaises(ValueError) as e:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006219 self._DoTestFile('250_compress_dtb_invalid.dts')
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +02006220 self.assertIn("Unknown algorithm 'invalid'", str(e.exception))
6221
Stefan Herbrechtsmeiera14bee02022-08-19 16:25:32 +02006222 def testCompUtilCompressions(self):
6223 """Test compression algorithms"""
6224 for bintool in self.comp_bintools.values():
6225 self._CheckBintool(bintool)
6226 data = bintool.compress(COMPRESS_DATA)
6227 self.assertNotEqual(COMPRESS_DATA, data)
6228 orig = bintool.decompress(data)
Brandon Maiera657bc62024-06-04 16:16:05 +00006229 self.assertEqual(COMPRESS_DATA, orig)
Stefan Herbrechtsmeiera14bee02022-08-19 16:25:32 +02006230
6231 def testCompUtilVersions(self):
6232 """Test tool version of compression algorithms"""
6233 for bintool in self.comp_bintools.values():
6234 self._CheckBintool(bintool)
6235 version = bintool.version()
6236 self.assertRegex(version, '^v?[0-9]+[0-9.]*')
6237
6238 def testCompUtilPadding(self):
6239 """Test padding of compression algorithms"""
Jiaxun Yangc6931742025-04-10 06:43:03 -06006240 # Skip zstd and lz4 because they doesn't support padding
6241 for bintool in [v for k,v in self.comp_bintools.items()
6242 if not k in ['zstd', 'lz4']]:
Stefan Herbrechtsmeiera14bee02022-08-19 16:25:32 +02006243 self._CheckBintool(bintool)
6244 data = bintool.compress(COMPRESS_DATA)
6245 self.assertNotEqual(COMPRESS_DATA, data)
6246 data += tools.get_bytes(0, 64)
6247 orig = bintool.decompress(data)
Brandon Maiera657bc62024-06-04 16:16:05 +00006248 self.assertEqual(COMPRESS_DATA, orig)
Stefan Herbrechtsmeiera14bee02022-08-19 16:25:32 +02006249
Stefan Herbrechtsmeiera5e4dcb2022-08-19 16:25:38 +02006250 def testCompressDtbZstd(self):
6251 """Test that zstd compress of device-tree files failed"""
6252 with self.assertRaises(ValueError) as e:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006253 self._DoTestFile('251_compress_dtb_zstd.dts')
Stefan Herbrechtsmeiera5e4dcb2022-08-19 16:25:38 +02006254 self.assertIn("Node '/binman/u-boot-dtb': The zstd compression "
6255 "requires a length header", str(e.exception))
6256
Quentin Schulz9b5c6482022-09-02 15:10:48 +02006257 def testMkimageMultipleDataFiles(self):
6258 """Test passing multiple files to mkimage in a mkimage entry"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006259 self._SetupSplElf()
6260 self._SetupTplElf()
Quentin Schulz9b5c6482022-09-02 15:10:48 +02006261 data = self._DoReadFile('252_mkimage_mult_data.dts')
6262 # Size of files are packed in their 4B big-endian format
6263 expect = struct.pack('>I', len(U_BOOT_TPL_DATA))
6264 expect += struct.pack('>I', len(U_BOOT_SPL_DATA))
6265 # Size info is always followed by a 4B zero value.
6266 expect += tools.get_bytes(0, 4)
6267 expect += U_BOOT_TPL_DATA
6268 # All but last files are 4B-aligned
6269 align_pad = len(U_BOOT_TPL_DATA) % 4
6270 if align_pad:
6271 expect += tools.get_bytes(0, align_pad)
6272 expect += U_BOOT_SPL_DATA
6273 self.assertEqual(expect, data[-len(expect):])
6274
Marek Vasutf7413f02023-07-18 07:23:58 -06006275 def testMkimageMultipleExpanded(self):
6276 """Test passing multiple files to mkimage in a mkimage entry"""
6277 self._SetupSplElf()
6278 self._SetupTplElf()
6279 entry_args = {
6280 'spl-bss-pad': 'y',
6281 'spl-dtb': 'y',
6282 }
6283 data = self._DoReadFileDtb('252_mkimage_mult_data.dts',
6284 use_expanded=True, entry_args=entry_args)[0]
6285 pad_len = 10
6286 tpl_expect = U_BOOT_TPL_DATA
6287 spl_expect = U_BOOT_SPL_NODTB_DATA + tools.get_bytes(0, pad_len)
6288 spl_expect += U_BOOT_SPL_DTB_DATA
6289
6290 content = data[0x40:]
6291 lens = struct.unpack('>III', content[:12])
6292
6293 # Size of files are packed in their 4B big-endian format
6294 # Size info is always followed by a 4B zero value.
6295 self.assertEqual(len(tpl_expect), lens[0])
6296 self.assertEqual(len(spl_expect), lens[1])
6297 self.assertEqual(0, lens[2])
6298
6299 rest = content[12:]
6300 self.assertEqual(tpl_expect, rest[:len(tpl_expect)])
6301
6302 rest = rest[len(tpl_expect):]
6303 align_pad = len(tpl_expect) % 4
6304 self.assertEqual(tools.get_bytes(0, align_pad), rest[:align_pad])
6305 rest = rest[align_pad:]
6306 self.assertEqual(spl_expect, rest)
6307
Quentin Schulz9b5c6482022-09-02 15:10:48 +02006308 def testMkimageMultipleNoContent(self):
6309 """Test passing multiple data files to mkimage with one data file having no content"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006310 self._SetupSplElf()
Quentin Schulz9b5c6482022-09-02 15:10:48 +02006311 with self.assertRaises(ValueError) as exc:
6312 self._DoReadFile('253_mkimage_mult_no_content.dts')
6313 self.assertIn('Could not complete processing of contents',
6314 str(exc.exception))
6315
Quentin Schulz0d3a9262022-09-02 15:10:49 +02006316 def testMkimageFilename(self):
6317 """Test using mkimage to build a binary with a filename"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006318 self._SetupSplElf()
Quentin Schulz0d3a9262022-09-02 15:10:49 +02006319 retcode = self._DoTestFile('254_mkimage_filename.dts')
6320 self.assertEqual(0, retcode)
6321 fname = tools.get_output_filename('mkimage-test.bin')
6322 self.assertTrue(os.path.exists(fname))
6323
Simon Glass56d05412022-02-28 07:16:54 -07006324 def testVpl(self):
6325 """Test that an image with VPL and its device tree can be created"""
6326 # ELF file with a '__bss_size' symbol
6327 self._SetupVplElf()
6328 data = self._DoReadFile('255_u_boot_vpl.dts')
6329 self.assertEqual(U_BOOT_VPL_DATA + U_BOOT_VPL_DTB_DATA, data)
6330
6331 def testVplNoDtb(self):
6332 """Test that an image with vpl/u-boot-vpl-nodtb.bin can be created"""
6333 self._SetupVplElf()
6334 data = self._DoReadFile('256_u_boot_vpl_nodtb.dts')
6335 self.assertEqual(U_BOOT_VPL_NODTB_DATA,
6336 data[:len(U_BOOT_VPL_NODTB_DATA)])
6337
6338 def testExpandedVpl(self):
6339 """Test that an expanded entry type is selected for TPL when needed"""
6340 self._SetupVplElf()
6341
6342 entry_args = {
6343 'vpl-bss-pad': 'y',
6344 'vpl-dtb': 'y',
6345 }
6346 self._DoReadFileDtb('257_fdt_incl_vpl.dts', use_expanded=True,
6347 entry_args=entry_args)
6348 image = control.images['image']
6349 entries = image.GetEntries()
6350 self.assertEqual(1, len(entries))
6351
6352 # We only have u-boot-vpl, which be expanded
6353 self.assertIn('u-boot-vpl', entries)
6354 entry = entries['u-boot-vpl']
6355 self.assertEqual('u-boot-vpl-expanded', entry.etype)
6356 subent = entry.GetEntries()
6357 self.assertEqual(3, len(subent))
6358 self.assertIn('u-boot-vpl-nodtb', subent)
6359 self.assertIn('u-boot-vpl-bss-pad', subent)
6360 self.assertIn('u-boot-vpl-dtb', subent)
6361
6362 def testVplBssPadMissing(self):
6363 """Test that a missing symbol is detected"""
6364 self._SetupVplElf('u_boot_ucode_ptr')
6365 with self.assertRaises(ValueError) as e:
6366 self._DoReadFile('258_vpl_bss_pad.dts')
6367 self.assertIn('Expected __bss_size symbol in vpl/u-boot-vpl',
6368 str(e.exception))
6369
Neha Malcom Francis3eb4be32022-10-17 16:36:25 +05306370 def testSymlink(self):
Andrew Davis6b463da2023-07-22 00:14:44 +05306371 """Test that image files can be symlinked"""
Neha Malcom Francis3eb4be32022-10-17 16:36:25 +05306372 retcode = self._DoTestFile('259_symlink.dts', debug=True, map=True)
6373 self.assertEqual(0, retcode)
6374 image = control.images['test_image']
6375 fname = tools.get_output_filename('test_image.bin')
6376 sname = tools.get_output_filename('symlink_to_test.bin')
6377 self.assertTrue(os.path.islink(sname))
6378 self.assertEqual(os.readlink(sname), fname)
Alper Nebi Yasake63ca5a2022-03-27 18:31:45 +03006379
Andrew Davis6b463da2023-07-22 00:14:44 +05306380 def testSymlinkOverwrite(self):
6381 """Test that symlinked images can be overwritten"""
6382 testdir = TestFunctional._MakeInputDir('symlinktest')
6383 self._DoTestFile('259_symlink.dts', debug=True, map=True, output_dir=testdir)
6384 # build the same image again in the same directory so that existing symlink is present
6385 self._DoTestFile('259_symlink.dts', debug=True, map=True, output_dir=testdir)
6386 fname = tools.get_output_filename('test_image.bin')
6387 sname = tools.get_output_filename('symlink_to_test.bin')
6388 self.assertTrue(os.path.islink(sname))
6389 self.assertEqual(os.readlink(sname), fname)
6390
Simon Glass37f85de2022-10-20 18:22:47 -06006391 def testSymbolsElf(self):
6392 """Test binman can assign symbols embedded in an ELF file"""
6393 if not elf.ELF_TOOLS:
6394 self.skipTest('Python elftools not available')
6395 self._SetupTplElf('u_boot_binman_syms')
6396 self._SetupVplElf('u_boot_binman_syms')
6397 self._SetupSplElf('u_boot_binman_syms')
6398 data = self._DoReadFileDtb('260_symbols_elf.dts')[0]
6399 image_fname = tools.get_output_filename('image.bin')
6400
6401 image = control.images['image']
6402 entries = image.GetEntries()
6403
6404 for entry in entries.values():
6405 # No symbols in u-boot and it has faked contents anyway
6406 if entry.name == 'u-boot':
6407 continue
6408 edata = data[entry.image_pos:entry.image_pos + entry.size]
6409 efname = tools.get_output_filename(f'edata-{entry.name}')
6410 tools.write_file(efname, edata)
6411
6412 syms = elf.GetSymbolFileOffset(efname, ['_binman_u_boot'])
6413 re_name = re.compile('_binman_(u_boot_(.*))_prop_(.*)')
6414 for name, sym in syms.items():
6415 msg = 'test'
6416 val = elf.GetSymbolValue(sym, edata, msg)
6417 entry_m = re_name.match(name)
6418 if entry_m:
6419 ename, prop = entry_m.group(1), entry_m.group(3)
6420 entry, entry_name, prop_name = image.LookupEntry(entries,
6421 name, msg)
Simon Glassd3d3a102025-02-19 08:11:16 -07006422 expect_val = None
Simon Glass37f85de2022-10-20 18:22:47 -06006423 if prop_name == 'offset':
6424 expect_val = entry.offset
6425 elif prop_name == 'image_pos':
6426 expect_val = entry.image_pos
6427 elif prop_name == 'size':
6428 expect_val = entry.size
6429 self.assertEqual(expect_val, val)
6430
6431 def testSymbolsElfBad(self):
6432 """Check error when trying to write symbols without the elftools lib"""
6433 if not elf.ELF_TOOLS:
6434 self.skipTest('Python elftools not available')
6435 self._SetupTplElf('u_boot_binman_syms')
6436 self._SetupVplElf('u_boot_binman_syms')
6437 self._SetupSplElf('u_boot_binman_syms')
6438 try:
6439 elf.ELF_TOOLS = False
6440 with self.assertRaises(ValueError) as exc:
6441 self._DoReadFileDtb('260_symbols_elf.dts')
6442 finally:
6443 elf.ELF_TOOLS = True
6444 self.assertIn(
6445 "Section '/binman': entry '/binman/u-boot-spl-elf': "
6446 'Cannot write symbols to an ELF file without Python elftools',
6447 str(exc.exception))
6448
Simon Glassde244162023-01-07 14:07:08 -07006449 def testSectionFilename(self):
6450 """Check writing of section contents to a file"""
6451 data = self._DoReadFile('261_section_fname.dts')
6452 expected = (b'&&' + U_BOOT_DATA + b'&&&' +
6453 tools.get_bytes(ord('!'), 7) +
6454 U_BOOT_DATA + tools.get_bytes(ord('&'), 12))
6455 self.assertEqual(expected, data)
6456
6457 sect_fname = tools.get_output_filename('outfile.bin')
6458 self.assertTrue(os.path.exists(sect_fname))
6459 sect_data = tools.read_file(sect_fname)
6460 self.assertEqual(U_BOOT_DATA, sect_data)
6461
Simon Glass1e9e61c2023-01-07 14:07:12 -07006462 def testAbsent(self):
6463 """Check handling of absent entries"""
6464 data = self._DoReadFile('262_absent.dts')
Yannic Moog911ba9a2025-06-13 14:02:42 +02006465 self.assertEqual(U_BOOT_DATA + b'aa' + U_BOOT_IMG_DATA, data)
Simon Glass1e9e61c2023-01-07 14:07:12 -07006466
Yannic Moog911ba9a2025-06-13 14:02:42 +02006467 def testPackTeeOsElf(self):
6468 """Test that an image with a TEE elf binary can be created"""
Simon Glassad5cfe12023-01-07 14:07:14 -07006469 entry_args = {
6470 'tee-os-path': 'tee.elf',
6471 }
Yannic Moog911ba9a2025-06-13 14:02:42 +02006472 tee_path = self.tee_elf_path
Simon Glassad5cfe12023-01-07 14:07:14 -07006473 data = self._DoReadFileDtb('263_tee_os_opt.dts',
6474 entry_args=entry_args)[0]
Yannic Moog911ba9a2025-06-13 14:02:42 +02006475 self.assertEqual(U_BOOT_DATA + tools.read_file(tee_path) +
6476 U_BOOT_IMG_DATA, data)
Simon Glassad5cfe12023-01-07 14:07:14 -07006477
6478 def checkFitTee(self, dts, tee_fname):
6479 """Check that a tee-os entry works and returns data
6480
6481 Args:
6482 dts (str): Device tree filename to use
6483 tee_fname (str): filename containing tee-os
6484
6485 Returns:
6486 bytes: Image contents
6487 """
6488 if not elf.ELF_TOOLS:
6489 self.skipTest('Python elftools not available')
6490 entry_args = {
6491 'of-list': 'test-fdt1 test-fdt2',
6492 'default-dt': 'test-fdt2',
6493 'tee-os-path': tee_fname,
6494 }
6495 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
6496 data = self._DoReadFileDtb(dts, entry_args=entry_args,
6497 extra_indirs=[test_subdir])[0]
6498 return data
6499
6500 def testFitTeeOsOptionalFit(self):
6501 """Test an image with a FIT with an optional OP-TEE binary"""
6502 data = self.checkFitTee('264_tee_os_opt_fit.dts', 'tee.bin')
6503
6504 # There should be only one node, holding the data set up in SetUpClass()
6505 # for tee.bin
6506 dtb = fdt.Fdt.FromData(data)
6507 dtb.Scan()
6508 node = dtb.GetNode('/images/tee-1')
6509 self.assertEqual(TEE_ADDR,
6510 fdt_util.fdt32_to_cpu(node.props['load'].value))
6511 self.assertEqual(TEE_ADDR,
6512 fdt_util.fdt32_to_cpu(node.props['entry'].value))
6513 self.assertEqual(U_BOOT_DATA, node.props['data'].bytes)
6514
Simon Glass14d64e32025-04-29 07:21:59 -06006515 with terminal.capture() as (stdout, stderr):
Jonas Karlmanb2be3e42023-07-18 20:34:36 +00006516 self.checkFitTee('264_tee_os_opt_fit.dts', '')
6517 err = stderr.getvalue()
6518 self.assertRegex(
6519 err,
6520 "Image '.*' is missing optional external blobs but is still functional: tee-os")
Yannic Moogb14c4622025-06-13 14:02:45 +02006521 self.assertNotRegex(
6522 err,
6523 "Image '.*' has faked external blobs and is non-functional: tee-os")
Jonas Karlmanb2be3e42023-07-18 20:34:36 +00006524
Simon Glassad5cfe12023-01-07 14:07:14 -07006525 def testFitTeeOsOptionalFitBad(self):
6526 """Test an image with a FIT with an optional OP-TEE binary"""
6527 with self.assertRaises(ValueError) as exc:
6528 self.checkFitTee('265_tee_os_opt_fit_bad.dts', 'tee.bin')
6529 self.assertIn(
6530 "Node '/binman/fit': subnode 'images/@tee-SEQ': Failed to read ELF file: Magic number does not match",
6531 str(exc.exception))
6532
6533 def testFitTeeOsBad(self):
6534 """Test an OP-TEE binary with wrong formats"""
6535 self.make_tee_bin('tee.bad1', 123)
6536 with self.assertRaises(ValueError) as exc:
6537 self.checkFitTee('264_tee_os_opt_fit.dts', 'tee.bad1')
6538 self.assertIn(
6539 "Node '/binman/fit/images/@tee-SEQ/tee-os': OP-TEE paged mode not supported",
6540 str(exc.exception))
6541
6542 self.make_tee_bin('tee.bad2', 0, b'extra data')
6543 with self.assertRaises(ValueError) as exc:
6544 self.checkFitTee('264_tee_os_opt_fit.dts', 'tee.bad2')
6545 self.assertIn(
6546 "Node '/binman/fit/images/@tee-SEQ/tee-os': Invalid OP-TEE file: size mismatch (expected 0x4, have 0xe)",
6547 str(exc.exception))
6548
Yannic Moog4a333ea2025-06-13 14:02:41 +02006549 def testExtblobMissingOptional(self):
Simon Glass63328f12023-01-07 14:07:15 -07006550 """Test an image with an external blob that is optional"""
Simon Glass14d64e32025-04-29 07:21:59 -06006551 with terminal.capture() as (stdout, stderr):
Yannic Moog6f2fd072025-06-13 14:02:44 +02006552 data = self._DoReadFileDtb('266_blob_ext_opt.dts',
6553 allow_fake_blobs=False)[0]
Simon Glass63328f12023-01-07 14:07:15 -07006554 self.assertEqual(REFCODE_DATA, data)
Yannic Moog4a333ea2025-06-13 14:02:41 +02006555 self.assertNotIn(MISSING_DATA, data)
Simon Glass63328f12023-01-07 14:07:15 -07006556
Yannic Moogb14c4622025-06-13 14:02:45 +02006557 def testExtblobFakedOptional(self):
6558 """Test an image with an external blob that is optional"""
6559 with terminal.capture() as (stdout, stderr):
6560 data = self._DoReadFile('266_blob_ext_opt.dts')
6561 self.assertEqual(REFCODE_DATA, data)
6562 err = stderr.getvalue()
6563 self.assertRegex(
6564 err,
6565 "Image '.*' is missing optional external blobs but is still functional: missing")
6566 self.assertNotRegex(
6567 err,
6568 "Image '.*' has faked external blobs and is non-functional: missing")
6569
Simon Glass7447a9d2023-01-11 16:10:12 -07006570 def testSectionInner(self):
6571 """Test an inner section with a size"""
6572 data = self._DoReadFile('267_section_inner.dts')
6573 expected = U_BOOT_DATA + tools.get_bytes(0, 12)
6574 self.assertEqual(expected, data)
6575
Simon Glassa4948b22023-01-11 16:10:14 -07006576 def testNull(self):
6577 """Test an image with a null entry"""
6578 data = self._DoReadFile('268_null.dts')
6579 self.assertEqual(U_BOOT_DATA + b'\xff\xff\xff\xff' + U_BOOT_IMG_DATA, data)
6580
Simon Glassf1ee03b2023-01-11 16:10:16 -07006581 def testOverlap(self):
6582 """Test an image with a overlapping entry"""
6583 data = self._DoReadFile('269_overlap.dts')
6584 self.assertEqual(U_BOOT_DATA[:1] + b'aa' + U_BOOT_DATA[3:], data)
6585
6586 image = control.images['image']
6587 entries = image.GetEntries()
6588
6589 self.assertIn('inset', entries)
6590 inset = entries['inset']
6591 self.assertEqual(1, inset.offset);
6592 self.assertEqual(1, inset.image_pos);
6593 self.assertEqual(2, inset.size);
6594
6595 def testOverlapNull(self):
6596 """Test an image with a null overlap"""
6597 data = self._DoReadFile('270_overlap_null.dts')
6598 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
6599
6600 # Check the FMAP
6601 fhdr, fentries = fmap_util.DecodeFmap(data[len(U_BOOT_DATA):])
6602 self.assertEqual(4, fhdr.nareas)
6603 fiter = iter(fentries)
6604
6605 fentry = next(fiter)
6606 self.assertEqual(b'SECTION', fentry.name)
6607 self.assertEqual(0, fentry.offset)
6608 self.assertEqual(len(U_BOOT_DATA), fentry.size)
6609 self.assertEqual(0, fentry.flags)
6610
6611 fentry = next(fiter)
6612 self.assertEqual(b'U_BOOT', fentry.name)
6613 self.assertEqual(0, fentry.offset)
6614 self.assertEqual(len(U_BOOT_DATA), fentry.size)
6615 self.assertEqual(0, fentry.flags)
6616
6617 # Make sure that the NULL entry appears in the FMAP
6618 fentry = next(fiter)
6619 self.assertEqual(b'NULL', fentry.name)
6620 self.assertEqual(1, fentry.offset)
6621 self.assertEqual(2, fentry.size)
6622 self.assertEqual(0, fentry.flags)
6623
6624 fentry = next(fiter)
6625 self.assertEqual(b'FMAP', fentry.name)
6626 self.assertEqual(len(U_BOOT_DATA), fentry.offset)
6627
6628 def testOverlapBad(self):
6629 """Test an image with a bad overlapping entry"""
6630 with self.assertRaises(ValueError) as exc:
6631 self._DoReadFile('271_overlap_bad.dts')
6632 self.assertIn(
6633 "Node '/binman/inset': Offset 0x10 (16) ending at 0x12 (18) must overlap with existing entries",
6634 str(exc.exception))
6635
6636 def testOverlapNoOffset(self):
6637 """Test an image with a bad overlapping entry"""
6638 with self.assertRaises(ValueError) as exc:
6639 self._DoReadFile('272_overlap_no_size.dts')
6640 self.assertIn(
6641 "Node '/binman/inset': 'fill' entry is missing properties: size",
6642 str(exc.exception))
6643
Simon Glasse0035c92023-01-11 16:10:17 -07006644 def testBlobSymbol(self):
6645 """Test a blob with symbols read from an ELF file"""
6646 elf_fname = self.ElfTestFile('blob_syms')
6647 TestFunctional._MakeInputFile('blob_syms', tools.read_file(elf_fname))
6648 TestFunctional._MakeInputFile('blob_syms.bin',
6649 tools.read_file(self.ElfTestFile('blob_syms.bin')))
6650
6651 data = self._DoReadFile('273_blob_symbol.dts')
6652
6653 syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
6654 addr = elf.GetSymbolAddress(elf_fname, '__my_start_sym')
6655 self.assertEqual(syms['_binman_sym_magic'].address, addr)
6656 self.assertEqual(syms['_binman_inset_prop_offset'].address, addr + 4)
6657 self.assertEqual(syms['_binman_inset_prop_size'].address, addr + 8)
6658
6659 sym_values = struct.pack('<LLL', elf.BINMAN_SYM_MAGIC_VALUE, 4, 8)
6660 expected = sym_values
6661 self.assertEqual(expected, data[:len(expected)])
6662
Simon Glass49e9c002023-01-11 16:10:19 -07006663 def testOffsetFromElf(self):
6664 """Test a blob with symbols read from an ELF file"""
6665 elf_fname = self.ElfTestFile('blob_syms')
6666 TestFunctional._MakeInputFile('blob_syms', tools.read_file(elf_fname))
6667 TestFunctional._MakeInputFile('blob_syms.bin',
6668 tools.read_file(self.ElfTestFile('blob_syms.bin')))
6669
6670 data = self._DoReadFile('274_offset_from_elf.dts')
6671
6672 syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
6673 base = elf.GetSymbolAddress(elf_fname, '__my_start_sym')
6674
6675 image = control.images['image']
6676 entries = image.GetEntries()
6677
6678 self.assertIn('inset', entries)
6679 inset = entries['inset']
6680
6681 self.assertEqual(base + 4, inset.offset);
6682 self.assertEqual(base + 4, inset.image_pos);
6683 self.assertEqual(4, inset.size);
6684
6685 self.assertIn('inset2', entries)
6686 inset = entries['inset2']
6687 self.assertEqual(base + 8, inset.offset);
6688 self.assertEqual(base + 8, inset.image_pos);
6689 self.assertEqual(4, inset.size);
6690
Jonas Karlmanc59ea892023-01-21 19:01:39 +00006691 def testFitAlign(self):
6692 """Test an image with an FIT with aligned external data"""
6693 data = self._DoReadFile('275_fit_align.dts')
6694 self.assertEqual(4096, len(data))
6695
6696 dtb = fdt.Fdt.FromData(data)
6697 dtb.Scan()
6698
6699 props = self._GetPropTree(dtb, ['data-position'])
6700 expected = {
6701 'u-boot:data-position': 1024,
6702 'fdt-1:data-position': 2048,
6703 'fdt-2:data-position': 3072,
6704 }
6705 self.assertEqual(expected, props)
6706
Jonas Karlman490f73c2023-01-21 19:02:12 +00006707 def testFitFirmwareLoadables(self):
6708 """Test an image with an FIT that use fit,firmware"""
6709 if not elf.ELF_TOOLS:
6710 self.skipTest('Python elftools not available')
6711 entry_args = {
6712 'of-list': 'test-fdt1',
6713 'default-dt': 'test-fdt1',
6714 'atf-bl31-path': 'bl31.elf',
6715 'tee-os-path': 'missing.bin',
6716 }
6717 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
Simon Glass14d64e32025-04-29 07:21:59 -06006718 with terminal.capture() as (stdout, stderr):
Simon Glass62f85902023-02-23 18:18:01 -07006719 data = self._DoReadFileDtb(
6720 '276_fit_firmware_loadables.dts',
6721 entry_args=entry_args,
6722 extra_indirs=[test_subdir])[0]
Jonas Karlman490f73c2023-01-21 19:02:12 +00006723
6724 dtb = fdt.Fdt.FromData(data)
6725 dtb.Scan()
6726
6727 node = dtb.GetNode('/configurations/conf-uboot-1')
6728 self.assertEqual('u-boot', node.props['firmware'].value)
6729 self.assertEqual(['atf-1', 'atf-2'],
6730 fdt_util.GetStringList(node, 'loadables'))
6731
6732 node = dtb.GetNode('/configurations/conf-atf-1')
6733 self.assertEqual('atf-1', node.props['firmware'].value)
6734 self.assertEqual(['u-boot', 'atf-2'],
6735 fdt_util.GetStringList(node, 'loadables'))
6736
6737 node = dtb.GetNode('/configurations/conf-missing-uboot-1')
6738 self.assertEqual('u-boot', node.props['firmware'].value)
6739 self.assertEqual(['atf-1', 'atf-2'],
6740 fdt_util.GetStringList(node, 'loadables'))
6741
6742 node = dtb.GetNode('/configurations/conf-missing-atf-1')
6743 self.assertEqual('atf-1', node.props['firmware'].value)
6744 self.assertEqual(['u-boot', 'atf-2'],
6745 fdt_util.GetStringList(node, 'loadables'))
6746
6747 node = dtb.GetNode('/configurations/conf-missing-tee-1')
6748 self.assertEqual('atf-1', node.props['firmware'].value)
Yannic Moog911ba9a2025-06-13 14:02:42 +02006749 self.assertEqual(['u-boot', 'tee', 'atf-2'],
Jonas Karlman490f73c2023-01-21 19:02:12 +00006750 fdt_util.GetStringList(node, 'loadables'))
6751
Simon Glass9a1c7262023-02-22 12:14:49 -07006752 def testTooldir(self):
6753 """Test that we can specify the tooldir"""
Simon Glass14d64e32025-04-29 07:21:59 -06006754 with terminal.capture() as (stdout, stderr):
Simon Glass9a1c7262023-02-22 12:14:49 -07006755 self.assertEqual(0, self._DoBinman('--tooldir', 'fred',
6756 'tool', '-l'))
6757 self.assertEqual('fred', bintool.Bintool.tooldir)
6758
6759 # Check that the toolpath is updated correctly
6760 self.assertEqual(['fred'], tools.tool_search_paths)
6761
6762 # Try with a few toolpaths; the tooldir should be at the end
Simon Glass14d64e32025-04-29 07:21:59 -06006763 with terminal.capture() as (stdout, stderr):
Simon Glass9a1c7262023-02-22 12:14:49 -07006764 self.assertEqual(0, self._DoBinman(
6765 '--toolpath', 'mary', '--toolpath', 'anna', '--tooldir', 'fred',
6766 'tool', '-l'))
6767 self.assertEqual(['mary', 'anna', 'fred'], tools.tool_search_paths)
6768
Simon Glass49b77e82023-03-02 17:02:44 -07006769 def testReplaceSectionEntry(self):
6770 """Test replacing an entry in a section"""
6771 expect_data = b'w' * len(U_BOOT_DATA + COMPRESS_DATA)
6772 entry_data, expected_fdtmap, image = self._RunReplaceCmd('section/blob',
6773 expect_data, dts='241_replace_section_simple.dts')
6774 self.assertEqual(expect_data, entry_data)
6775
6776 entries = image.GetEntries()
6777 self.assertIn('section', entries)
6778 section = entries['section']
6779
6780 sect_entries = section.GetEntries()
6781 self.assertIn('blob', sect_entries)
6782 entry = sect_entries['blob']
6783 self.assertEqual(len(expect_data), entry.size)
6784
6785 fname = tools.get_output_filename('image-updated.bin')
6786 data = tools.read_file(fname)
6787
6788 new_blob_data = data[entry.image_pos:entry.image_pos + len(expect_data)]
6789 self.assertEqual(expect_data, new_blob_data)
6790
6791 self.assertEqual(U_BOOT_DATA,
6792 data[entry.image_pos + len(expect_data):]
6793 [:len(U_BOOT_DATA)])
6794
6795 def testReplaceSectionDeep(self):
6796 """Test replacing an entry in two levels of sections"""
6797 expect_data = b'w' * len(U_BOOT_DATA + COMPRESS_DATA)
6798 entry_data, expected_fdtmap, image = self._RunReplaceCmd(
6799 'section/section/blob', expect_data,
6800 dts='278_replace_section_deep.dts')
6801 self.assertEqual(expect_data, entry_data)
6802
6803 entries = image.GetEntries()
6804 self.assertIn('section', entries)
6805 section = entries['section']
6806
6807 subentries = section.GetEntries()
6808 self.assertIn('section', subentries)
6809 section = subentries['section']
6810
6811 sect_entries = section.GetEntries()
6812 self.assertIn('blob', sect_entries)
6813 entry = sect_entries['blob']
6814 self.assertEqual(len(expect_data), entry.size)
6815
6816 fname = tools.get_output_filename('image-updated.bin')
6817 data = tools.read_file(fname)
6818
6819 new_blob_data = data[entry.image_pos:entry.image_pos + len(expect_data)]
6820 self.assertEqual(expect_data, new_blob_data)
6821
6822 self.assertEqual(U_BOOT_DATA,
6823 data[entry.image_pos + len(expect_data):]
6824 [:len(U_BOOT_DATA)])
6825
6826 def testReplaceFitSibling(self):
6827 """Test an image with a FIT inside where we replace its sibling"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006828 self._SetupSplElf()
Simon Glass49b77e82023-03-02 17:02:44 -07006829 fname = TestFunctional._MakeInputFile('once', b'available once')
6830 self._DoReadFileRealDtb('277_replace_fit_sibling.dts')
6831 os.remove(fname)
6832
6833 try:
6834 tmpdir, updated_fname = self._SetupImageInTmpdir()
6835
6836 fname = os.path.join(tmpdir, 'update-blob')
6837 expected = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) + 1)
6838 tools.write_file(fname, expected)
6839
6840 self._DoBinman('replace', '-i', updated_fname, 'blob', '-f', fname)
6841 data = tools.read_file(updated_fname)
6842 start = len(U_BOOT_DTB_DATA)
6843 self.assertEqual(expected, data[start:start + len(expected)])
6844 map_fname = os.path.join(tmpdir, 'image-updated.map')
6845 self.assertFalse(os.path.exists(map_fname))
6846 finally:
6847 shutil.rmtree(tmpdir)
6848
Simon Glassc3fe97f2023-03-02 17:02:45 -07006849 def testX509Cert(self):
6850 """Test creating an X509 certificate"""
6851 keyfile = self.TestFile('key.key')
6852 entry_args = {
6853 'keyfile': keyfile,
6854 }
6855 data = self._DoReadFileDtb('279_x509_cert.dts',
6856 entry_args=entry_args)[0]
6857 cert = data[:-4]
6858 self.assertEqual(U_BOOT_DATA, data[-4:])
6859
6860 # TODO: verify the signature
6861
6862 def testX509CertMissing(self):
6863 """Test that binman still produces an image if openssl is missing"""
6864 keyfile = self.TestFile('key.key')
6865 entry_args = {
6866 'keyfile': 'keyfile',
6867 }
Simon Glass14d64e32025-04-29 07:21:59 -06006868 with terminal.capture() as (_, stderr):
Simon Glassc3fe97f2023-03-02 17:02:45 -07006869 self._DoTestFile('279_x509_cert.dts',
6870 force_missing_bintools='openssl',
6871 entry_args=entry_args)
6872 err = stderr.getvalue()
6873 self.assertRegex(err, "Image 'image'.*missing bintools.*: openssl")
6874
Jonas Karlman35305492023-02-25 19:01:33 +00006875 def testPackRockchipTpl(self):
6876 """Test that an image with a Rockchip TPL binary can be created"""
Simon Glasse3ef5ed2023-07-24 09:19:58 -06006877 data = self._DoReadFile('291_rockchip_tpl.dts')
Jonas Karlman35305492023-02-25 19:01:33 +00006878 self.assertEqual(ROCKCHIP_TPL_DATA, data[:len(ROCKCHIP_TPL_DATA)])
6879
Jonas Karlman1016ec72023-02-25 19:01:35 +00006880 def testMkimageMissingBlobMultiple(self):
6881 """Test missing blob with mkimage entry and multiple-data-files"""
Simon Glass14d64e32025-04-29 07:21:59 -06006882 with terminal.capture() as (stdout, stderr):
Simon Glasse3ef5ed2023-07-24 09:19:58 -06006883 self._DoTestFile('292_mkimage_missing_multiple.dts', allow_missing=True)
Jonas Karlman1016ec72023-02-25 19:01:35 +00006884 err = stderr.getvalue()
6885 self.assertIn("is missing external blobs and is non-functional", err)
6886
6887 with self.assertRaises(ValueError) as e:
Simon Glasse3ef5ed2023-07-24 09:19:58 -06006888 self._DoTestFile('292_mkimage_missing_multiple.dts', allow_missing=False)
Jonas Karlman1016ec72023-02-25 19:01:35 +00006889 self.assertIn("not found in input path", str(e.exception))
6890
Ivan Mikhaylov3cfcaa4d2023-03-08 01:13:40 +00006891 def _PrepareSignEnv(self, dts='280_fit_sign.dts'):
6892 """Prepare sign environment
6893
6894 Create private and public keys, add pubkey into dtb.
6895
6896 Returns:
6897 Tuple:
6898 FIT container
6899 Image name
6900 Private key
6901 DTB
6902 """
Marek Vasutf7413f02023-07-18 07:23:58 -06006903 self._SetupSplElf()
Ivan Mikhaylov3cfcaa4d2023-03-08 01:13:40 +00006904 data = self._DoReadFileRealDtb(dts)
6905 updated_fname = tools.get_output_filename('image-updated.bin')
6906 tools.write_file(updated_fname, data)
6907 dtb = tools.get_output_filename('source.dtb')
6908 private_key = tools.get_output_filename('test_key.key')
6909 public_key = tools.get_output_filename('test_key.crt')
6910 fit = tools.get_output_filename('fit.fit')
6911 key_dir = tools.get_output_dir()
6912
6913 tools.run('openssl', 'req', '-batch' , '-newkey', 'rsa:4096',
6914 '-sha256', '-new', '-nodes', '-x509', '-keyout',
6915 private_key, '-out', public_key)
6916 tools.run('fdt_add_pubkey', '-a', 'sha256,rsa4096', '-k', key_dir,
6917 '-n', 'test_key', '-r', 'conf', dtb)
6918
6919 return fit, updated_fname, private_key, dtb
6920
6921 def testSignSimple(self):
6922 """Test that a FIT container can be signed in image"""
6923 is_signed = False
6924 fit, fname, private_key, dtb = self._PrepareSignEnv()
6925
6926 # do sign with private key
6927 control.SignEntries(fname, None, private_key, 'sha256,rsa4096',
6928 ['fit'])
6929 is_signed = self._CheckSign(fit, dtb)
6930
6931 self.assertEqual(is_signed, True)
6932
6933 def testSignExactFIT(self):
6934 """Test that a FIT container can be signed and replaced in image"""
6935 is_signed = False
6936 fit, fname, private_key, dtb = self._PrepareSignEnv()
6937
6938 # Make sure we propagate the toolpath, since mkimage may not be on PATH
6939 args = []
6940 if self.toolpath:
6941 for path in self.toolpath:
6942 args += ['--toolpath', path]
6943
6944 # do sign with private key
6945 self._DoBinman(*args, 'sign', '-i', fname, '-k', private_key, '-a',
6946 'sha256,rsa4096', '-f', fit, 'fit')
6947 is_signed = self._CheckSign(fit, dtb)
6948
6949 self.assertEqual(is_signed, True)
6950
6951 def testSignNonFit(self):
6952 """Test a non-FIT entry cannot be signed"""
6953 is_signed = False
6954 fit, fname, private_key, _ = self._PrepareSignEnv(
6955 '281_sign_non_fit.dts')
6956
6957 # do sign with private key
6958 with self.assertRaises(ValueError) as e:
6959 self._DoBinman('sign', '-i', fname, '-k', private_key, '-a',
6960 'sha256,rsa4096', '-f', fit, 'u-boot')
6961 self.assertIn(
6962 "Node '/u-boot': Updating signatures is not supported with this entry type",
6963 str(e.exception))
6964
6965 def testSignMissingMkimage(self):
6966 """Test that FIT signing handles a missing mkimage tool"""
6967 fit, fname, private_key, _ = self._PrepareSignEnv()
6968
6969 # try to sign with a missing mkimage tool
6970 bintool.Bintool.set_missing_list(['mkimage'])
6971 with self.assertRaises(ValueError) as e:
6972 control.SignEntries(fname, None, private_key, 'sha256,rsa4096',
6973 ['fit'])
6974 self.assertIn("Node '/fit': Missing tool: 'mkimage'", str(e.exception))
6975
Simon Glass4abf7842023-07-18 07:23:54 -06006976 def testSymbolNoWrite(self):
6977 """Test disabling of symbol writing"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006978 self._SetupSplElf()
Simon Glass4abf7842023-07-18 07:23:54 -06006979 self.checkSymbols('282_symbols_disable.dts', U_BOOT_SPL_DATA, 0x1c,
6980 no_write_symbols=True)
6981
6982 def testSymbolNoWriteExpanded(self):
6983 """Test disabling of symbol writing in expanded entries"""
6984 entry_args = {
6985 'spl-dtb': '1',
6986 }
6987 self.checkSymbols('282_symbols_disable.dts', U_BOOT_SPL_NODTB_DATA +
6988 U_BOOT_SPL_DTB_DATA, 0x38,
6989 entry_args=entry_args, use_expanded=True,
6990 no_write_symbols=True)
6991
Marek Vasutf7413f02023-07-18 07:23:58 -06006992 def testMkimageSpecial(self):
6993 """Test mkimage ignores special hash-1 node"""
6994 data = self._DoReadFile('283_mkimage_special.dts')
6995
6996 # Just check that the data appears in the file somewhere
6997 self.assertIn(U_BOOT_DATA, data)
6998
Simon Glass2d94c422023-07-18 07:23:59 -06006999 def testFitFdtList(self):
7000 """Test an image with an FIT with the fit,fdt-list-val option"""
7001 entry_args = {
7002 'default-dt': 'test-fdt2',
7003 }
7004 data = self._DoReadFileDtb(
7005 '284_fit_fdt_list.dts',
7006 entry_args=entry_args,
7007 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
7008 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
7009 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
7010
Simon Glass83b8bfe2023-07-18 07:24:01 -06007011 def testSplEmptyBss(self):
7012 """Test an expanded SPL with a zero-size BSS"""
7013 # ELF file with a '__bss_size' symbol
7014 self._SetupSplElf(src_fname='bss_data_zero')
7015
7016 entry_args = {
7017 'spl-bss-pad': 'y',
7018 'spl-dtb': 'y',
7019 }
7020 data = self._DoReadFileDtb('285_spl_expand.dts',
7021 use_expanded=True, entry_args=entry_args)[0]
7022
Simon Glassfc792842023-07-18 07:24:04 -06007023 def testTemplate(self):
7024 """Test using a template"""
7025 TestFunctional._MakeInputFile('vga2.bin', b'#' + VGA_DATA)
7026 data = self._DoReadFile('286_template.dts')
7027 first = U_BOOT_DATA + VGA_DATA + U_BOOT_DTB_DATA
7028 second = U_BOOT_DATA + b'#' + VGA_DATA + U_BOOT_DTB_DATA
7029 self.assertEqual(U_BOOT_IMG_DATA + first + second, data)
7030
Simon Glass09490b02023-07-22 21:43:52 -06007031 dtb_fname1 = tools.get_output_filename('u-boot.dtb.tmpl1')
7032 self.assertTrue(os.path.exists(dtb_fname1))
7033 dtb = fdt.Fdt.FromData(tools.read_file(dtb_fname1))
7034 dtb.Scan()
7035 node1 = dtb.GetNode('/binman/template')
7036 self.assertTrue(node1)
7037 vga = dtb.GetNode('/binman/first/intel-vga')
7038 self.assertTrue(vga)
7039
Simon Glass54825e12023-07-22 21:43:56 -06007040 dtb_fname2 = tools.get_output_filename('u-boot.dtb.tmpl2')
7041 self.assertTrue(os.path.exists(dtb_fname2))
7042 dtb2 = fdt.Fdt.FromData(tools.read_file(dtb_fname2))
7043 dtb2.Scan()
7044 node2 = dtb2.GetNode('/binman/template')
7045 self.assertFalse(node2)
7046
Simon Glass9909c112023-07-18 07:24:05 -06007047 def testTemplateBlobMulti(self):
7048 """Test using a template with 'multiple-images' enabled"""
7049 TestFunctional._MakeInputFile('my-blob.bin', b'blob')
7050 TestFunctional._MakeInputFile('my-blob2.bin', b'other')
7051 retcode = self._DoTestFile('287_template_multi.dts')
7052
7053 self.assertEqual(0, retcode)
7054 image = control.images['image']
7055 image_fname = tools.get_output_filename('my-image.bin')
7056 data = tools.read_file(image_fname)
7057 self.assertEqual(b'blob@@@@other', data)
7058
Simon Glass5dc511b2023-07-18 07:24:06 -06007059 def testTemplateFit(self):
7060 """Test using a template in a FIT"""
7061 fit_data = self._DoReadFile('288_template_fit.dts')
7062 fname = os.path.join(self._indir, 'fit_data.fit')
7063 tools.write_file(fname, fit_data)
7064 out = tools.run('dumpimage', '-l', fname)
7065
Simon Glassaa6e0552023-07-18 07:24:07 -06007066 def testTemplateSection(self):
7067 """Test using a template in a section (not at top level)"""
7068 TestFunctional._MakeInputFile('vga2.bin', b'#' + VGA_DATA)
7069 data = self._DoReadFile('289_template_section.dts')
7070 first = U_BOOT_DATA + VGA_DATA + U_BOOT_DTB_DATA
7071 second = U_BOOT_DATA + b'#' + VGA_DATA + U_BOOT_DTB_DATA
7072 self.assertEqual(U_BOOT_IMG_DATA + first + second + first, data)
7073
Simon Glassf53a7bc2023-07-18 07:24:08 -06007074 def testMkimageSymbols(self):
7075 """Test using mkimage to build an image with symbols in it"""
7076 self._SetupSplElf('u_boot_binman_syms')
7077 data = self._DoReadFile('290_mkimage_sym.dts')
7078
7079 image = control.images['image']
7080 entries = image.GetEntries()
7081 self.assertIn('u-boot', entries)
7082 u_boot = entries['u-boot']
7083
7084 mkim = entries['mkimage']
7085 mkim_entries = mkim.GetEntries()
7086 self.assertIn('u-boot-spl', mkim_entries)
7087 spl = mkim_entries['u-boot-spl']
7088 self.assertIn('u-boot-spl2', mkim_entries)
7089 spl2 = mkim_entries['u-boot-spl2']
7090
7091 # skip the mkimage header and the area sizes
7092 mk_data = data[mkim.offset + 0x40:]
7093 size, term = struct.unpack('>LL', mk_data[:8])
7094
7095 # There should be only one image, so check that the zero terminator is
7096 # present
7097 self.assertEqual(0, term)
7098
7099 content = mk_data[8:8 + size]
7100
7101 # The image should contain the symbols from u_boot_binman_syms.c
7102 # Note that image_pos is adjusted by the base address of the image,
7103 # which is 0x10 in our test image
7104 spl_data = content[:0x18]
7105 content = content[0x1b:]
7106
7107 # After the header is a table of offsets for each image. There should
7108 # only be one image, then a 0 terminator, so figure out the real start
7109 # of the image data
7110 base = 0x40 + 8
7111
7112 # Check symbols in both u-boot-spl and u-boot-spl2
7113 for i in range(2):
7114 vals = struct.unpack('<LLQLL', spl_data)
7115
7116 # The image should contain the symbols from u_boot_binman_syms.c
7117 # Note that image_pos is adjusted by the base address of the image,
7118 # which is 0x10 in our 'u_boot_binman_syms' test image
7119 self.assertEqual(elf.BINMAN_SYM_MAGIC_VALUE, vals[0])
7120 self.assertEqual(base, vals[1])
7121 self.assertEqual(spl2.offset, vals[2])
7122 # figure out the internal positions of its components
7123 self.assertEqual(0x10 + u_boot.image_pos, vals[3])
7124
7125 # Check that spl and spl2 are actually at the indicated positions
7126 self.assertEqual(
7127 elf.BINMAN_SYM_MAGIC_VALUE,
7128 struct.unpack('<I', data[spl.image_pos:spl.image_pos + 4])[0])
7129 self.assertEqual(
7130 elf.BINMAN_SYM_MAGIC_VALUE,
7131 struct.unpack('<I', data[spl2.image_pos:spl2.image_pos + 4])[0])
7132
7133 self.assertEqual(len(U_BOOT_DATA), vals[4])
7134
7135 # Move to next
7136 spl_data = content[:0x18]
7137
Simon Glass86b3e472023-07-22 21:43:57 -06007138 def testTemplatePhandle(self):
7139 """Test using a template in a node containing a phandle"""
7140 entry_args = {
7141 'atf-bl31-path': 'bl31.elf',
7142 }
Simon Glass76ee0ca2023-08-03 17:23:58 -06007143 data = self._DoReadFileDtb('309_template_phandle.dts',
Simon Glass86b3e472023-07-22 21:43:57 -06007144 entry_args=entry_args)
7145 fname = tools.get_output_filename('image.bin')
7146 out = tools.run('dumpimage', '-l', fname)
7147
7148 # We should see the FIT description and one for each of the two images
7149 lines = out.splitlines()
7150 descs = [line.split()[-1] for line in lines if 'escription' in line]
7151 self.assertEqual(['test-desc', 'atf', 'fdt'], descs)
7152
7153 def testTemplatePhandleDup(self):
7154 """Test using a template in a node containing a phandle"""
7155 entry_args = {
7156 'atf-bl31-path': 'bl31.elf',
7157 }
7158 with self.assertRaises(ValueError) as e:
Simon Glass76ee0ca2023-08-03 17:23:58 -06007159 self._DoReadFileDtb('310_template_phandle_dup.dts',
Simon Glass86b3e472023-07-22 21:43:57 -06007160 entry_args=entry_args)
7161 self.assertIn(
7162 'Duplicate phandle 1 in nodes /binman/image/fit/images/atf/atf-bl31 and /binman/image-2/fit/images/atf/atf-bl31',
7163 str(e.exception))
7164
Neha Malcom Francis3b788942023-07-22 00:14:24 +05307165 def testTIBoardConfig(self):
7166 """Test that a schema validated board config file can be generated"""
Simon Glassf1264ba2023-07-24 09:19:59 -06007167 data = self._DoReadFile('293_ti_board_cfg.dts')
Neha Malcom Francis3b788942023-07-22 00:14:24 +05307168 self.assertEqual(TI_BOARD_CONFIG_DATA, data)
7169
Neha Malcom Francis8cd04512024-01-05 17:09:17 +05307170 def testTIBoardConfigLint(self):
7171 """Test that an incorrectly linted config file would generate error"""
7172 with self.assertRaises(ValueError) as e:
7173 data = self._DoReadFile('323_ti_board_cfg_phony.dts')
7174 self.assertIn("Yamllint error", str(e.exception))
7175
Neha Malcom Francis3b788942023-07-22 00:14:24 +05307176 def testTIBoardConfigCombined(self):
7177 """Test that a schema validated combined board config file can be generated"""
Simon Glassf1264ba2023-07-24 09:19:59 -06007178 data = self._DoReadFile('294_ti_board_cfg_combined.dts')
Neha Malcom Francis3b788942023-07-22 00:14:24 +05307179 configlen_noheader = TI_BOARD_CONFIG_DATA * 4
7180 self.assertGreater(data, configlen_noheader)
7181
7182 def testTIBoardConfigNoDataType(self):
7183 """Test that error is thrown when data type is not supported"""
7184 with self.assertRaises(ValueError) as e:
Simon Glassf1264ba2023-07-24 09:19:59 -06007185 data = self._DoReadFile('295_ti_board_cfg_no_type.dts')
Neha Malcom Francis3b788942023-07-22 00:14:24 +05307186 self.assertIn("Schema validation error", str(e.exception))
Simon Glassde244162023-01-07 14:07:08 -07007187
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307188 def testPackTiSecure(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 }
Simon Glassf1264ba2023-07-24 09:19:59 -06007194 data = self._DoReadFileDtb('296_ti_secure.dts',
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307195 entry_args=entry_args)[0]
7196 self.assertGreater(len(data), len(TI_UNSECURE_DATA))
7197
Manorit Chawdhry2e523b02023-12-29 16:16:27 +05307198 def testPackTiSecureFirewall(self):
7199 """Test that an image with a TI secured binary can be created"""
7200 keyfile = self.TestFile('key.key')
7201 entry_args = {
7202 'keyfile': keyfile,
7203 }
7204 data_no_firewall = self._DoReadFileDtb('296_ti_secure.dts',
7205 entry_args=entry_args)[0]
7206 data_firewall = self._DoReadFileDtb('324_ti_secure_firewall.dts',
7207 entry_args=entry_args)[0]
7208 self.assertGreater(len(data_firewall),len(data_no_firewall))
7209
7210 def testPackTiSecureFirewallMissingProperty(self):
7211 """Test that an image with a TI secured binary can be created"""
7212 keyfile = self.TestFile('key.key')
7213 entry_args = {
7214 'keyfile': keyfile,
7215 }
7216 with self.assertRaises(ValueError) as e:
7217 data_firewall = self._DoReadFileDtb('325_ti_secure_firewall_missing_property.dts',
7218 entry_args=entry_args)[0]
7219 self.assertRegex(str(e.exception), "Node '/binman/ti-secure': Subnode 'firewall-0-2' is missing properties: id,region")
7220
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307221 def testPackTiSecureMissingTool(self):
7222 """Test that an image with a TI secured binary (non-functional) can be created
7223 when openssl is missing"""
7224 keyfile = self.TestFile('key.key')
7225 entry_args = {
7226 'keyfile': keyfile,
7227 }
Simon Glass14d64e32025-04-29 07:21:59 -06007228 with terminal.capture() as (_, stderr):
Simon Glassf1264ba2023-07-24 09:19:59 -06007229 self._DoTestFile('296_ti_secure.dts',
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307230 force_missing_bintools='openssl',
7231 entry_args=entry_args)
7232 err = stderr.getvalue()
7233 self.assertRegex(err, "Image 'image'.*missing bintools.*: openssl")
7234
7235 def testPackTiSecureROM(self):
7236 """Test that a ROM image with a TI secured binary can be created"""
7237 keyfile = self.TestFile('key.key')
7238 entry_args = {
7239 'keyfile': keyfile,
7240 }
Simon Glassf1264ba2023-07-24 09:19:59 -06007241 data = self._DoReadFileDtb('297_ti_secure_rom.dts',
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307242 entry_args=entry_args)[0]
Simon Glassf1264ba2023-07-24 09:19:59 -06007243 data_a = self._DoReadFileDtb('299_ti_secure_rom_a.dts',
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307244 entry_args=entry_args)[0]
Simon Glassf1264ba2023-07-24 09:19:59 -06007245 data_b = self._DoReadFileDtb('300_ti_secure_rom_b.dts',
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307246 entry_args=entry_args)[0]
7247 self.assertGreater(len(data), len(TI_UNSECURE_DATA))
7248 self.assertGreater(len(data_a), len(TI_UNSECURE_DATA))
7249 self.assertGreater(len(data_b), len(TI_UNSECURE_DATA))
7250
7251 def testPackTiSecureROMCombined(self):
7252 """Test that a ROM image with a TI secured binary can be created"""
7253 keyfile = self.TestFile('key.key')
7254 entry_args = {
7255 'keyfile': keyfile,
7256 }
Simon Glassf1264ba2023-07-24 09:19:59 -06007257 data = self._DoReadFileDtb('298_ti_secure_rom_combined.dts',
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307258 entry_args=entry_args)[0]
7259 self.assertGreater(len(data), len(TI_UNSECURE_DATA))
7260
Christian Taedcke62ac29a2023-07-17 09:05:54 +02007261 def testEncryptedNoAlgo(self):
7262 """Test encrypted node with missing required properties"""
7263 with self.assertRaises(ValueError) as e:
7264 self._DoReadFileDtb('301_encrypted_no_algo.dts')
7265 self.assertIn(
7266 "Node '/binman/fit/images/u-boot/encrypted': 'encrypted' entry is missing properties: algo iv-filename",
7267 str(e.exception))
7268
7269 def testEncryptedInvalidIvfile(self):
7270 """Test encrypted node with invalid iv file"""
7271 with self.assertRaises(ValueError) as e:
7272 self._DoReadFileDtb('302_encrypted_invalid_iv_file.dts')
7273 self.assertIn("Filename 'invalid-iv-file' not found in input path",
7274 str(e.exception))
7275
7276 def testEncryptedMissingKey(self):
7277 """Test encrypted node with missing key properties"""
7278 with self.assertRaises(ValueError) as e:
7279 self._DoReadFileDtb('303_encrypted_missing_key.dts')
7280 self.assertIn(
7281 "Node '/binman/fit/images/u-boot/encrypted': Provide either 'key-filename' or 'key-source'",
7282 str(e.exception))
7283
7284 def testEncryptedKeySource(self):
7285 """Test encrypted node with key-source property"""
7286 data = self._DoReadFileDtb('304_encrypted_key_source.dts')[0]
7287
7288 dtb = fdt.Fdt.FromData(data)
7289 dtb.Scan()
7290
7291 node = dtb.GetNode('/images/u-boot/cipher')
7292 self.assertEqual('algo-name', node.props['algo'].value)
7293 self.assertEqual('key-source-value', node.props['key-source'].value)
7294 self.assertEqual(ENCRYPTED_IV_DATA,
7295 tools.to_bytes(''.join(node.props['iv'].value)))
7296 self.assertNotIn('key', node.props)
7297
7298 def testEncryptedKeyFile(self):
7299 """Test encrypted node with key-filename property"""
7300 data = self._DoReadFileDtb('305_encrypted_key_file.dts')[0]
7301
7302 dtb = fdt.Fdt.FromData(data)
7303 dtb.Scan()
7304
7305 node = dtb.GetNode('/images/u-boot/cipher')
7306 self.assertEqual('algo-name', node.props['algo'].value)
7307 self.assertEqual(ENCRYPTED_IV_DATA,
7308 tools.to_bytes(''.join(node.props['iv'].value)))
7309 self.assertEqual(ENCRYPTED_KEY_DATA,
7310 tools.to_bytes(''.join(node.props['key'].value)))
7311 self.assertNotIn('key-source', node.props)
7312
Lukas Funkee901faf2023-07-18 13:53:13 +02007313
7314 def testSplPubkeyDtb(self):
Simon Glass4b861272024-07-20 11:49:41 +01007315 """Test u_boot_spl_pubkey_dtb etype"""
7316 data = tools.read_file(self.TestFile("key.pem"))
7317 self._MakeInputFile("key.crt", data)
7318 self._DoReadFileRealDtb('306_spl_pubkey_dtb.dts')
7319 image = control.images['image']
7320 entries = image.GetEntries()
7321 dtb_entry = entries['u-boot-spl-pubkey-dtb']
7322 dtb_data = dtb_entry.GetData()
7323 dtb = fdt.Fdt.FromData(dtb_data)
7324 dtb.Scan()
Lukas Funkee901faf2023-07-18 13:53:13 +02007325
Simon Glass4b861272024-07-20 11:49:41 +01007326 signature_node = dtb.GetNode('/signature')
7327 self.assertIsNotNone(signature_node)
7328 key_node = signature_node.FindNode("key-key")
7329 self.assertIsNotNone(key_node)
7330 self.assertEqual(fdt_util.GetString(key_node, "required"), "conf")
7331 self.assertEqual(fdt_util.GetString(key_node, "algo"), "sha384,rsa4096")
7332 self.assertEqual(fdt_util.GetString(key_node, "key-name-hint"), "key")
Christian Taedcke62ac29a2023-07-17 09:05:54 +02007333
Lukas Funke712e1062023-08-03 17:22:14 +02007334 def testXilinxBootgenSigning(self):
7335 """Test xilinx-bootgen etype"""
7336 bootgen = bintool.Bintool.create('bootgen')
7337 self._CheckBintool(bootgen)
7338 data = tools.read_file(self.TestFile("key.key"))
7339 self._MakeInputFile("psk.pem", data)
7340 self._MakeInputFile("ssk.pem", data)
7341 self._SetupPmuFwlElf()
7342 self._SetupSplElf()
7343 self._DoReadFileRealDtb('307_xilinx_bootgen_sign.dts')
7344 image_fname = tools.get_output_filename('image.bin')
7345
7346 # Read partition header table and check if authentication is enabled
7347 bootgen_out = bootgen.run_cmd("-arch", "zynqmp",
7348 "-read", image_fname, "pht").splitlines()
7349 attributes = {"authentication": None,
7350 "core": None,
7351 "encryption": None}
7352
7353 for l in bootgen_out:
7354 for a in attributes.keys():
7355 if a in l:
7356 m = re.match(fr".*{a} \[([^]]+)\]", l)
7357 attributes[a] = m.group(1)
7358
7359 self.assertTrue(attributes['authentication'] == "rsa")
7360 self.assertTrue(attributes['core'] == "a53-0")
7361 self.assertTrue(attributes['encryption'] == "no")
7362
7363 def testXilinxBootgenSigningEncryption(self):
7364 """Test xilinx-bootgen etype"""
7365 bootgen = bintool.Bintool.create('bootgen')
7366 self._CheckBintool(bootgen)
7367 data = tools.read_file(self.TestFile("key.key"))
7368 self._MakeInputFile("psk.pem", data)
7369 self._MakeInputFile("ssk.pem", data)
7370 self._SetupPmuFwlElf()
7371 self._SetupSplElf()
7372 self._DoReadFileRealDtb('308_xilinx_bootgen_sign_enc.dts')
7373 image_fname = tools.get_output_filename('image.bin')
7374
7375 # Read boot header in order to verify encryption source and
7376 # encryption parameter
7377 bootgen_out = bootgen.run_cmd("-arch", "zynqmp",
7378 "-read", image_fname, "bh").splitlines()
7379 attributes = {"auth_only":
7380 {"re": r".*auth_only \[([^]]+)\]", "value": None},
7381 "encryption_keystore":
7382 {"re": r" *encryption_keystore \(0x28\) : (.*)",
7383 "value": None},
7384 }
7385
7386 for l in bootgen_out:
7387 for a in attributes.keys():
7388 if a in l:
7389 m = re.match(attributes[a]['re'], l)
7390 attributes[a] = m.group(1)
7391
7392 # Check if fsbl-attribute is set correctly
7393 self.assertTrue(attributes['auth_only'] == "true")
7394 # Check if key is stored in efuse
7395 self.assertTrue(attributes['encryption_keystore'] == "0xa5c3c5a3")
7396
7397 def testXilinxBootgenMissing(self):
7398 """Test that binman still produces an image if bootgen is missing"""
7399 data = tools.read_file(self.TestFile("key.key"))
7400 self._MakeInputFile("psk.pem", data)
7401 self._MakeInputFile("ssk.pem", data)
7402 self._SetupPmuFwlElf()
7403 self._SetupSplElf()
Simon Glass14d64e32025-04-29 07:21:59 -06007404 with terminal.capture() as (_, stderr):
Lukas Funke712e1062023-08-03 17:22:14 +02007405 self._DoTestFile('307_xilinx_bootgen_sign.dts',
7406 force_missing_bintools='bootgen')
7407 err = stderr.getvalue()
7408 self.assertRegex(err,
7409 "Image 'image'.*missing bintools.*: bootgen")
7410
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +05307411 def _GetCapsuleHeaders(self, data):
7412 """Get the capsule header contents
7413
7414 Args:
7415 data: Capsule file contents
7416
7417 Returns:
7418 Dict:
7419 key: Capsule Header name (str)
7420 value: Header field value (str)
7421 """
7422 capsule_file = os.path.join(self._indir, 'test.capsule')
7423 tools.write_file(capsule_file, data)
7424
7425 out = tools.run('mkeficapsule', '--dump-capsule', capsule_file)
7426 lines = out.splitlines()
7427
7428 re_line = re.compile(r'^([^:\-\t]*)(?:\t*\s*:\s*(.*))?$')
7429 vals = {}
7430 for line in lines:
7431 mat = re_line.match(line)
7432 if mat:
7433 vals[mat.group(1)] = mat.group(2)
7434
7435 return vals
7436
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307437 def _CheckCapsule(self, data, signed_capsule=False, version_check=False,
7438 capoemflags=False):
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +05307439 fmp_signature = "3153534D" # 'M', 'S', 'S', '1'
7440 fmp_size = "00000010"
7441 fmp_fw_version = "00000002"
7442 capsule_image_index = "00000001"
7443 oemflag = "00018000"
7444 auth_hdr_revision = "00000200"
7445 auth_hdr_cert_type = "00000EF1"
7446
7447 payload_data_len = len(EFI_CAPSULE_DATA)
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307448
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +05307449 hdr = self._GetCapsuleHeaders(data)
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307450
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +05307451 self.assertEqual(FW_MGMT_GUID.upper(), hdr['EFI_CAPSULE_HDR.CAPSULE_GUID'])
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307452
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +05307453 self.assertEqual(CAPSULE_IMAGE_GUID.upper(),
7454 hdr['FMP_CAPSULE_IMAGE_HDR.UPDATE_IMAGE_TYPE_ID'])
7455 self.assertEqual(capsule_image_index,
7456 hdr['FMP_CAPSULE_IMAGE_HDR.UPDATE_IMAGE_INDEX'])
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307457
7458 if capoemflags:
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +05307459 self.assertEqual(oemflag, hdr['EFI_CAPSULE_HDR.FLAGS'])
7460
7461 if signed_capsule:
7462 self.assertEqual(auth_hdr_revision,
7463 hdr['EFI_FIRMWARE_IMAGE_AUTH.AUTH_INFO.HDR.wREVISION'])
7464 self.assertEqual(auth_hdr_cert_type,
7465 hdr['EFI_FIRMWARE_IMAGE_AUTH.AUTH_INFO.HDR.wCERTTYPE'])
7466 self.assertEqual(WIN_CERT_TYPE_EFI_GUID.upper(),
7467 hdr['EFI_FIRMWARE_IMAGE_AUTH.AUTH_INFO.CERT_TYPE'])
7468
7469 if version_check:
7470 self.assertEqual(fmp_signature,
7471 hdr['FMP_PAYLOAD_HDR.SIGNATURE'])
7472 self.assertEqual(fmp_size,
7473 hdr['FMP_PAYLOAD_HDR.HEADER_SIZE'])
7474 self.assertEqual(fmp_fw_version,
7475 hdr['FMP_PAYLOAD_HDR.FW_VERSION'])
7476
7477 self.assertEqual(payload_data_len, int(hdr['Payload Image Size']))
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307478
Sughosh Ganu6b2d18a2023-10-10 14:40:59 +05307479 def _CheckEmptyCapsule(self, data, accept_capsule=False):
7480 if accept_capsule:
7481 capsule_hdr_guid = EMPTY_CAPSULE_ACCEPT_GUID
7482 else:
7483 capsule_hdr_guid = EMPTY_CAPSULE_REVERT_GUID
7484
7485 hdr = self._GetCapsuleHeaders(data)
7486
7487 self.assertEqual(capsule_hdr_guid.upper(),
7488 hdr['EFI_CAPSULE_HDR.CAPSULE_GUID'])
7489
7490 if accept_capsule:
7491 capsule_size = "0000002C"
7492 else:
7493 capsule_size = "0000001C"
7494 self.assertEqual(capsule_size,
7495 hdr['EFI_CAPSULE_HDR.CAPSULE_IMAGE_SIZE'])
7496
7497 if accept_capsule:
7498 self.assertEqual(CAPSULE_IMAGE_GUID.upper(), hdr['ACCEPT_IMAGE_GUID'])
7499
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307500 def testCapsuleGen(self):
7501 """Test generation of EFI capsule"""
7502 data = self._DoReadFile('311_capsule.dts')
7503
7504 self._CheckCapsule(data)
7505
7506 def testSignedCapsuleGen(self):
7507 """Test generation of EFI capsule"""
7508 data = tools.read_file(self.TestFile("key.key"))
7509 self._MakeInputFile("key.key", data)
7510 data = tools.read_file(self.TestFile("key.pem"))
7511 self._MakeInputFile("key.crt", data)
7512
7513 data = self._DoReadFile('312_capsule_signed.dts')
7514
7515 self._CheckCapsule(data, signed_capsule=True)
7516
7517 def testCapsuleGenVersionSupport(self):
7518 """Test generation of EFI capsule with version support"""
7519 data = self._DoReadFile('313_capsule_version.dts')
7520
7521 self._CheckCapsule(data, version_check=True)
7522
7523 def testCapsuleGenSignedVer(self):
7524 """Test generation of signed EFI capsule with version information"""
7525 data = tools.read_file(self.TestFile("key.key"))
7526 self._MakeInputFile("key.key", data)
7527 data = tools.read_file(self.TestFile("key.pem"))
7528 self._MakeInputFile("key.crt", data)
7529
7530 data = self._DoReadFile('314_capsule_signed_ver.dts')
7531
7532 self._CheckCapsule(data, signed_capsule=True, version_check=True)
7533
7534 def testCapsuleGenCapOemFlags(self):
7535 """Test generation of EFI capsule with OEM Flags set"""
7536 data = self._DoReadFile('315_capsule_oemflags.dts')
7537
7538 self._CheckCapsule(data, capoemflags=True)
7539
7540 def testCapsuleGenKeyMissing(self):
7541 """Test that binman errors out on missing key"""
7542 with self.assertRaises(ValueError) as e:
7543 self._DoReadFile('316_capsule_missing_key.dts')
7544
7545 self.assertIn("Both private key and public key certificate need to be provided",
7546 str(e.exception))
7547
7548 def testCapsuleGenIndexMissing(self):
7549 """Test that binman errors out on missing image index"""
7550 with self.assertRaises(ValueError) as e:
7551 self._DoReadFile('317_capsule_missing_index.dts')
7552
7553 self.assertIn("entry is missing properties: image-index",
7554 str(e.exception))
7555
7556 def testCapsuleGenGuidMissing(self):
7557 """Test that binman errors out on missing image GUID"""
7558 with self.assertRaises(ValueError) as e:
7559 self._DoReadFile('318_capsule_missing_guid.dts')
7560
7561 self.assertIn("entry is missing properties: image-guid",
7562 str(e.exception))
7563
Sughosh Ganu6b2d18a2023-10-10 14:40:59 +05307564 def testCapsuleGenAcceptCapsule(self):
7565 """Test generationg of accept EFI capsule"""
7566 data = self._DoReadFile('319_capsule_accept.dts')
7567
7568 self._CheckEmptyCapsule(data, accept_capsule=True)
7569
7570 def testCapsuleGenRevertCapsule(self):
7571 """Test generationg of revert EFI capsule"""
7572 data = self._DoReadFile('320_capsule_revert.dts')
7573
7574 self._CheckEmptyCapsule(data)
7575
7576 def testCapsuleGenAcceptGuidMissing(self):
7577 """Test that binman errors out on missing image GUID for accept capsule"""
7578 with self.assertRaises(ValueError) as e:
7579 self._DoReadFile('321_capsule_accept_missing_guid.dts')
7580
7581 self.assertIn("Image GUID needed for generating accept capsule",
7582 str(e.exception))
7583
7584 def testCapsuleGenEmptyCapsuleTypeMissing(self):
7585 """Test that capsule-type is specified"""
7586 with self.assertRaises(ValueError) as e:
7587 self._DoReadFile('322_empty_capsule_type_missing.dts')
7588
7589 self.assertIn("entry is missing properties: capsule-type",
7590 str(e.exception))
7591
7592 def testCapsuleGenAcceptOrRevertMissing(self):
7593 """Test that both accept and revert capsule are not specified"""
7594 with self.assertRaises(ValueError) as e:
7595 self._DoReadFile('323_capsule_accept_revert_missing.dts')
7596
Simon Glassa360b8f2024-06-23 11:55:06 -06007597 def test_assume_size(self):
7598 """Test handling of the assume-size property for external blob"""
7599 with self.assertRaises(ValueError) as e:
7600 self._DoTestFile('326_assume_size.dts', allow_missing=True,
7601 allow_fake_blobs=True)
7602 self.assertIn("contents size 0xa (10) exceeds section size 0x9 (9)",
7603 str(e.exception))
7604
7605 def test_assume_size_ok(self):
7606 """Test handling of the assume-size where it fits OK"""
Simon Glass14d64e32025-04-29 07:21:59 -06007607 with terminal.capture() as (stdout, stderr):
Simon Glassa360b8f2024-06-23 11:55:06 -06007608 self._DoTestFile('327_assume_size_ok.dts', allow_missing=True,
7609 allow_fake_blobs=True)
7610 err = stderr.getvalue()
7611 self.assertRegex(
7612 err,
7613 "Image '.*' has faked external blobs and is non-functional: .*")
7614
7615 def test_assume_size_no_fake(self):
7616 """Test handling of the assume-size where it fits OK"""
Simon Glass14d64e32025-04-29 07:21:59 -06007617 with terminal.capture() as (stdout, stderr):
Simon Glassa360b8f2024-06-23 11:55:06 -06007618 self._DoTestFile('327_assume_size_ok.dts', allow_missing=True)
7619 err = stderr.getvalue()
7620 self.assertRegex(
7621 err,
7622 "Image '.*' is missing external blobs and is non-functional: .*")
7623
Simon Glass5f7aadf2024-07-20 11:49:47 +01007624 def SetupAlternateDts(self):
7625 """Compile the .dts test files for alternative-fdt
7626
7627 Returns:
7628 tuple:
7629 str: Test directory created
7630 list of str: '.bin' files which we expect Binman to create
7631 """
7632 testdir = TestFunctional._MakeInputDir('dtb')
7633 dtb_list = []
7634 for fname in glob.glob(f'{self.TestFile("alt_dts")}/*.dts'):
7635 tmp_fname = fdt_util.EnsureCompiled(fname, testdir)
7636 base = os.path.splitext(os.path.basename(fname))[0]
7637 dtb_list.append(base + '.bin')
7638 shutil.move(tmp_fname, os.path.join(testdir, base + '.dtb'))
7639
7640 return testdir, dtb_list
7641
Simon Glassf3598922024-07-20 11:49:45 +01007642 def CheckAlternates(self, dts, phase, xpl_data):
7643 """Run the test for the alterative-fdt etype
7644
7645 Args:
7646 dts (str): Devicetree file to process
7647 phase (str): Phase to process ('spl', 'tpl' or 'vpl')
7648 xpl_data (bytes): Expected data for the phase's binary
7649
7650 Returns:
7651 dict of .dtb files produced
7652 key: str filename
7653 value: Fdt object
7654 """
Simon Glass5f7aadf2024-07-20 11:49:47 +01007655 dtb_list = self.SetupAlternateDts()[1]
Simon Glassf3598922024-07-20 11:49:45 +01007656
7657 entry_args = {
7658 f'{phase}-dtb': '1',
7659 f'{phase}-bss-pad': 'y',
7660 'of-spl-remove-props': 'prop-to-remove another-prop-to-get-rid-of',
7661 }
7662 data = self._DoReadFileDtb(dts, use_real_dtb=True, update_dtb=True,
7663 use_expanded=True, entry_args=entry_args)[0]
7664 self.assertEqual(xpl_data, data[:len(xpl_data)])
7665 rest = data[len(xpl_data):]
7666 pad_len = 10
7667 self.assertEqual(tools.get_bytes(0, pad_len), rest[:pad_len])
7668
7669 # Check the dtb is using the test file
7670 dtb_data = rest[pad_len:]
7671 dtb = fdt.Fdt.FromData(dtb_data)
7672 dtb.Scan()
7673 fdt_size = dtb.GetFdtObj().totalsize()
7674 self.assertEqual('model-not-set',
7675 fdt_util.GetString(dtb.GetRoot(), 'compatible'))
7676
7677 pad_len = 10
7678
7679 # Check the other output files
7680 dtbs = {}
7681 for fname in dtb_list:
7682 pathname = tools.get_output_filename(fname)
7683 self.assertTrue(os.path.exists(pathname))
7684
7685 data = tools.read_file(pathname)
7686 self.assertEqual(xpl_data, data[:len(xpl_data)])
7687 rest = data[len(xpl_data):]
7688
7689 self.assertEqual(tools.get_bytes(0, pad_len), rest[:pad_len])
7690 rest = rest[pad_len:]
7691
7692 dtb = fdt.Fdt.FromData(rest)
7693 dtb.Scan()
7694 dtbs[fname] = dtb
7695
7696 expected = 'one' if '1' in fname else 'two'
7697 self.assertEqual(f'u-boot,model-{expected}',
7698 fdt_util.GetString(dtb.GetRoot(), 'compatible'))
7699
7700 # Make sure the FDT is the same size as the 'main' one
7701 rest = rest[fdt_size:]
7702
7703 self.assertEqual(b'', rest)
7704 return dtbs
7705
7706 def testAlternatesFdt(self):
7707 """Test handling of alternates-fdt etype"""
7708 self._SetupTplElf()
7709 dtbs = self.CheckAlternates('328_alternates_fdt.dts', 'tpl',
7710 U_BOOT_TPL_NODTB_DATA)
7711 for dtb in dtbs.values():
7712 # Check for the node with the tag
7713 node = dtb.GetNode('/node')
7714 self.assertIsNotNone(node)
7715 self.assertEqual(5, len(node.props.keys()))
7716
7717 # Make sure the other node is still there
7718 self.assertIsNotNone(dtb.GetNode('/node/other-node'))
7719
7720 def testAlternatesFdtgrep(self):
7721 """Test handling of alternates-fdt etype using fdtgrep"""
7722 self._SetupTplElf()
7723 dtbs = self.CheckAlternates('329_alternates_fdtgrep.dts', 'tpl',
7724 U_BOOT_TPL_NODTB_DATA)
7725 for dtb in dtbs.values():
7726 # Check for the node with the tag
7727 node = dtb.GetNode('/node')
7728 self.assertIsNotNone(node)
7729 self.assertEqual({'some-prop', 'not-a-prop-to-remove'},
7730 node.props.keys())
7731
7732 # Make sure the other node is gone
7733 self.assertIsNone(dtb.GetNode('/node/other-node'))
7734
7735 def testAlternatesFdtgrepVpl(self):
7736 """Test handling of alternates-fdt etype using fdtgrep with vpl"""
7737 self._SetupVplElf()
7738 dtbs = self.CheckAlternates('330_alternates_vpl.dts', 'vpl',
7739 U_BOOT_VPL_NODTB_DATA)
7740
7741 def testAlternatesFdtgrepSpl(self):
7742 """Test handling of alternates-fdt etype using fdtgrep with spl"""
7743 self._SetupSplElf()
7744 dtbs = self.CheckAlternates('331_alternates_spl.dts', 'spl',
7745 U_BOOT_SPL_NODTB_DATA)
7746
7747 def testAlternatesFdtgrepInval(self):
7748 """Test alternates-fdt etype using fdtgrep with invalid phase"""
7749 self._SetupSplElf()
7750 with self.assertRaises(ValueError) as e:
7751 dtbs = self.CheckAlternates('332_alternates_inval.dts', 'spl',
7752 U_BOOT_SPL_NODTB_DATA)
7753 self.assertIn("Invalid U-Boot phase 'bad-phase': Use tpl/vpl/spl",
7754 str(e.exception))
7755
Simon Glasscd2783e2024-07-20 11:49:46 +01007756 def testFitFdtListDir(self):
7757 """Test an image with an FIT with FDT images using fit,fdt-list-dir"""
Simon Glass1bba8942024-08-26 13:11:34 -06007758 old_dir = os.getcwd()
7759 try:
7760 os.chdir(self._indir)
7761 self.CheckFitFdt('333_fit_fdt_dir.dts', False)
7762 finally:
7763 os.chdir(old_dir)
Simon Glasscd2783e2024-07-20 11:49:46 +01007764
Simon Glassd2a9d6e2024-08-26 13:11:37 -06007765 def testFitFdtListDirDefault(self):
7766 """Test an FIT fit,fdt-list-dir where the default DT in is a subdir"""
7767 old_dir = os.getcwd()
7768 try:
7769 os.chdir(self._indir)
7770 self.CheckFitFdt('333_fit_fdt_dir.dts', False,
7771 default_dt='rockchip/test-fdt2')
7772 finally:
7773 os.chdir(old_dir)
7774
Simon Glass5f7aadf2024-07-20 11:49:47 +01007775 def testFitFdtCompat(self):
7776 """Test an image with an FIT with compatible in the config nodes"""
7777 entry_args = {
7778 'of-list': 'model1 model2',
7779 'default-dt': 'model2',
7780 }
7781 testdir, dtb_list = self.SetupAlternateDts()
7782 data = self._DoReadFileDtb(
7783 '334_fit_fdt_compat.dts', use_real_dtb=True, update_dtb=True,
7784 entry_args=entry_args, extra_indirs=[testdir])[0]
7785
7786 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
7787
7788 fit = fdt.Fdt.FromData(fit_data)
7789 fit.Scan()
7790
7791 cnode = fit.GetNode('/configurations')
7792 self.assertIn('default', cnode.props)
7793 self.assertEqual('config-2', cnode.props['default'].value)
7794
7795 for seq in range(1, 2):
7796 name = f'config-{seq}'
7797 fnode = fit.GetNode('/configurations/%s' % name)
7798 self.assertIsNotNone(fnode)
7799 self.assertIn('compatible', fnode.props.keys())
7800 expected = 'one' if seq == 1 else 'two'
7801 self.assertEqual(f'u-boot,model-{expected}',
7802 fnode.props['compatible'].value)
7803
Simon Glassa04b9942024-07-20 11:49:48 +01007804 def testFitFdtPhase(self):
7805 """Test an image with an FIT with fdt-phase in the fdt nodes"""
7806 phase = 'tpl'
7807 entry_args = {
7808 f'{phase}-dtb': '1',
7809 f'{phase}-bss-pad': 'y',
7810 'of-spl-remove-props': 'prop-to-remove another-prop-to-get-rid-of',
7811 'of-list': 'model1 model2',
7812 'default-dt': 'model2',
7813 }
7814 testdir, dtb_list = self.SetupAlternateDts()
7815 data = self._DoReadFileDtb(
7816 '335_fit_fdt_phase.dts', use_real_dtb=True, update_dtb=True,
7817 entry_args=entry_args, extra_indirs=[testdir])[0]
7818 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
7819 fit = fdt.Fdt.FromData(fit_data)
7820 fit.Scan()
7821
7822 # Check that each FDT has only the expected properties for the phase
7823 for seq in range(1, 2):
7824 fnode = fit.GetNode(f'/images/fdt-{seq}')
7825 self.assertIsNotNone(fnode)
7826 dtb = fdt.Fdt.FromData(fnode.props['data'].bytes)
7827 dtb.Scan()
7828
7829 # Make sure that the 'bootph-pre-sram' tag in /node protects it from
7830 # removal
7831 node = dtb.GetNode('/node')
7832 self.assertIsNotNone(node)
7833 self.assertEqual({'some-prop', 'not-a-prop-to-remove'},
7834 node.props.keys())
7835
7836 # Make sure the other node is gone
7837 self.assertIsNone(dtb.GetNode('/node/other-node'))
7838
Simon Glassb553e8a2024-08-26 13:11:29 -06007839 def testMkeficapsuleMissing(self):
7840 """Test that binman complains if mkeficapsule is missing"""
7841 with self.assertRaises(ValueError) as e:
7842 self._DoTestFile('311_capsule.dts',
7843 force_missing_bintools='mkeficapsule')
7844 self.assertIn("Node '/binman/efi-capsule': Missing tool: 'mkeficapsule'",
7845 str(e.exception))
7846
7847 def testMkeficapsuleMissingOk(self):
7848 """Test that binman deals with mkeficapsule being missing"""
Simon Glass14d64e32025-04-29 07:21:59 -06007849 with terminal.capture() as (stdout, stderr):
Simon Glassb553e8a2024-08-26 13:11:29 -06007850 ret = self._DoTestFile('311_capsule.dts',
7851 force_missing_bintools='mkeficapsule',
7852 allow_missing=True)
7853 self.assertEqual(103, ret)
7854 err = stderr.getvalue()
7855 self.assertRegex(err, "Image 'image'.*missing bintools.*: mkeficapsule")
7856
Simon Glass4b0f4142024-08-26 13:11:40 -06007857 def testSymbolsBase(self):
7858 """Test handling of symbols-base"""
7859 self.checkSymbols('336_symbols_base.dts', U_BOOT_SPL_DATA, 0x1c,
7860 symbols_base=0)
7861
7862 def testSymbolsBaseExpanded(self):
7863 """Test handling of symbols-base with expanded entries"""
7864 entry_args = {
7865 'spl-dtb': '1',
7866 }
7867 self.checkSymbols('337_symbols_base_expand.dts', U_BOOT_SPL_NODTB_DATA +
7868 U_BOOT_SPL_DTB_DATA, 0x38,
7869 entry_args=entry_args, use_expanded=True,
7870 symbols_base=0)
7871
Simon Glass3eb30a42024-08-26 13:11:42 -06007872 def testSymbolsCompressed(self):
7873 """Test binman complains about symbols from a compressed section"""
Simon Glass14d64e32025-04-29 07:21:59 -06007874 with terminal.capture() as (stdout, stderr):
Simon Glass3eb30a42024-08-26 13:11:42 -06007875 self.checkSymbols('338_symbols_comp.dts', U_BOOT_SPL_DATA, None)
7876 out = stdout.getvalue()
7877 self.assertIn('Symbol-writing: no value for /binman/section/u-boot',
7878 out)
7879
Simon Glass9c25ef22024-08-26 13:11:43 -06007880 def testNxpImx8Image(self):
7881 """Test that binman can produce an iMX8 image"""
7882 self._DoTestFile('339_nxp_imx8.dts')
7883
Alice Guo1d334022025-04-28 18:37:39 +08007884 def testNxpHeaderDdrfw(self):
7885 """Test that binman can add a header to DDR PHY firmware images"""
7886 data = self._DoReadFile('346_nxp_ddrfw_imx95.dts')
7887 self.assertEqual(len(IMX_LPDDR_IMEM_DATA).to_bytes(4, 'little') +
7888 len(IMX_LPDDR_DMEM_DATA).to_bytes(4, 'little') +
7889 IMX_LPDDR_IMEM_DATA + IMX_LPDDR_DMEM_DATA, data)
7890
Alexander Kochetkova730a282024-09-16 11:24:46 +03007891 def testFitSignSimple(self):
7892 """Test that image with FIT and signature nodes can be signed"""
7893 if not elf.ELF_TOOLS:
7894 self.skipTest('Python elftools not available')
7895 entry_args = {
7896 'of-list': 'test-fdt1',
7897 'default-dt': 'test-fdt1',
7898 'atf-bl31-path': 'bl31.elf',
7899 }
7900 data = tools.read_file(self.TestFile("340_rsa2048.key"))
7901 self._MakeInputFile("keys/rsa2048.key", data)
7902
7903 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
7904 keys_subdir = os.path.join(self._indir, "keys")
7905 data = self._DoReadFileDtb(
7906 '340_fit_signature.dts',
7907 entry_args=entry_args,
7908 extra_indirs=[test_subdir, keys_subdir])[0]
7909
7910 dtb = fdt.Fdt.FromData(data)
7911 dtb.Scan()
7912
7913 conf = dtb.GetNode('/configurations/conf-uboot-1')
7914 self.assertIsNotNone(conf)
7915 signature = conf.FindNode('signature')
7916 self.assertIsNotNone(signature)
7917 self.assertIsNotNone(signature.props.get('value'))
7918
7919 images = dtb.GetNode('/images')
7920 self.assertIsNotNone(images)
7921 for subnode in images.subnodes:
7922 signature = subnode.FindNode('signature')
7923 self.assertIsNotNone(signature)
7924 self.assertIsNotNone(signature.props.get('value'))
7925
7926 def testFitSignKeyNotFound(self):
7927 """Test that missing keys raise an error"""
7928 if not elf.ELF_TOOLS:
7929 self.skipTest('Python elftools not available')
7930 entry_args = {
7931 'of-list': 'test-fdt1',
7932 'default-dt': 'test-fdt1',
7933 'atf-bl31-path': 'bl31.elf',
7934 }
7935 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
7936 with self.assertRaises(ValueError) as e:
7937 self._DoReadFileDtb(
7938 '340_fit_signature.dts',
7939 entry_args=entry_args,
7940 extra_indirs=[test_subdir])[0]
7941 self.assertIn(
7942 'Filename \'rsa2048.key\' not found in input path',
7943 str(e.exception))
7944
7945 def testFitSignMultipleKeyPaths(self):
7946 """Test that keys found in multiple paths raise an error"""
7947 if not elf.ELF_TOOLS:
7948 self.skipTest('Python elftools not available')
7949 entry_args = {
7950 'of-list': 'test-fdt1',
7951 'default-dt': 'test-fdt1',
7952 'atf-bl31-path': 'bl31.elf',
7953 }
7954 data = tools.read_file(self.TestFile("340_rsa2048.key"))
7955 self._MakeInputFile("keys1/rsa2048.key", data)
7956 data = tools.read_file(self.TestFile("340_rsa2048.key"))
7957 self._MakeInputFile("keys2/conf-rsa2048.key", data)
7958
7959 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
7960 keys_subdir1 = os.path.join(self._indir, "keys1")
7961 keys_subdir2 = os.path.join(self._indir, "keys2")
7962 with self.assertRaises(ValueError) as e:
7963 self._DoReadFileDtb(
7964 '341_fit_signature.dts',
7965 entry_args=entry_args,
7966 extra_indirs=[test_subdir, keys_subdir1, keys_subdir2])[0]
7967 self.assertIn(
7968 'Node \'/binman/fit\': multiple key paths found',
7969 str(e.exception))
7970
7971 def testFitSignNoSingatureNodes(self):
7972 """Test that fit,sign doens't raise error if no signature nodes found"""
7973 if not elf.ELF_TOOLS:
7974 self.skipTest('Python elftools not available')
7975 entry_args = {
7976 'of-list': 'test-fdt1',
7977 'default-dt': 'test-fdt1',
7978 'atf-bl31-path': 'bl31.elf',
7979 }
7980 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
7981 self._DoReadFileDtb(
7982 '342_fit_signature.dts',
7983 entry_args=entry_args,
7984 extra_indirs=[test_subdir])[0]
7985
Simon Glassa360b8f2024-06-23 11:55:06 -06007986
Paul HENRYSff318462024-11-25 18:47:17 +01007987 def testSimpleFitEncryptedData(self):
7988 """Test an image with a FIT containing data to be encrypted"""
7989 data = tools.read_file(self.TestFile("aes256.bin"))
7990 self._MakeInputFile("keys/aes256.bin", data)
7991
7992 keys_subdir = os.path.join(self._indir, "keys")
7993 data = self._DoReadFileDtb(
7994 '343_fit_encrypt_data.dts',
7995 extra_indirs=[keys_subdir])[0]
7996
7997 fit = fdt.Fdt.FromData(data)
7998 fit.Scan()
7999
8000 # Extract the encrypted data and the Initialization Vector from the FIT
8001 node = fit.GetNode('/images/u-boot')
8002 subnode = fit.GetNode('/images/u-boot/cipher')
8003 data_size_unciphered = int.from_bytes(fit.GetProps(node)['data-size-unciphered'].bytes,
8004 byteorder='big')
8005 self.assertEqual(data_size_unciphered, len(U_BOOT_NODTB_DATA))
8006
8007 # Retrieve the key name from the FIT removing any null byte
8008 key_name = fit.GetProps(subnode)['key-name-hint'].bytes.replace(b'\x00', b'')
8009 with open(self.TestFile(key_name.decode('ascii') + '.bin'), 'rb') as file:
8010 key = file.read()
8011 iv = fit.GetProps(subnode)['iv'].bytes.hex()
8012 enc_data = fit.GetProps(node)['data'].bytes
8013 outdir = tools.get_output_dir()
8014 enc_data_file = os.path.join(outdir, 'encrypted_data.bin')
8015 tools.write_file(enc_data_file, enc_data)
8016 data_file = os.path.join(outdir, 'data.bin')
8017
8018 # Decrypt the encrypted data from the FIT and compare the data
8019 tools.run('openssl', 'enc', '-aes-256-cbc', '-nosalt', '-d', '-in',
8020 enc_data_file, '-out', data_file, '-K', key.hex(), '-iv', iv)
8021 with open(data_file, 'r') as file:
8022 dec_data = file.read()
8023 self.assertEqual(U_BOOT_NODTB_DATA, dec_data.encode('ascii'))
8024
8025 def testSimpleFitEncryptedDataMissingKey(self):
8026 """Test an image with a FIT containing data to be encrypted but with a missing key"""
8027 with self.assertRaises(ValueError) as e:
8028 self._DoReadFile('344_fit_encrypt_data_no_key.dts')
8029
8030 self.assertIn("Filename 'aes256.bin' not found in input path", str(e.exception))
8031
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01008032 def testFitFdtName(self):
8033 """Test an image with an FIT with multiple FDT images using NAME"""
8034 self.CheckFitFdt('345_fit_fdt_name.dts', use_seq_num=False)
8035
Neha Malcom Francisa25b4832025-03-17 10:24:20 +05308036 def testRemoveTemplate(self):
8037 """Test whether template is removed"""
8038 TestFunctional._MakeInputFile('my-blob.bin', b'blob')
8039 TestFunctional._MakeInputFile('my-blob2.bin', b'other')
8040 self._DoTestFile('346_remove_template.dts',
8041 force_missing_bintools='openssl',)
8042
Moteen Shahce5c2582025-05-16 17:11:48 +05308043 def testBootphPropagation(self):
8044 """Test that bootph-* properties are propagated correctly to supernodes"""
8045 _, _, _, out_dtb_fname = self._DoReadFileDtb(
8046 '347_bootph_prop.dts', use_real_dtb=True, update_dtb=True)
8047 dtb = fdt.Fdt(out_dtb_fname)
8048 dtb.Scan()
8049 root = dtb.GetRoot()
8050 parent_node = root.FindNode('dummy-parent')
8051 subnode1 = parent_node.FindNode('subnode-1')
8052 subnode2 = subnode1.FindNode('subnode-2')
8053 subnode3 = subnode1.FindNode('subnode-3')
8054 subnode4 = subnode3.FindNode('subnode-4')
8055
8056 self.assertIn('bootph-some-ram', subnode1.props,
8057 "Child node is missing 'bootph-some-ram' property")
8058 self.assertIn('bootph-all', subnode1.props,
8059 "Child node is missing 'bootph-all' property")
8060 self.assertIn('bootph-some-ram', parent_node.props,
8061 "Parent node is missing 'bootph-some-ram' property")
8062 self.assertIn('bootph-all', parent_node.props,
8063 "Parent node is missing 'bootph-all' property")
8064 self.assertEqual(len(subnode4.props), 0,
8065 "subnode shouldn't have any properties")
8066
Simon Glassac599912017-11-12 21:52:22 -07008067if __name__ == "__main__":
8068 unittest.main()