blob: fc02e6461cadabd701be186ee0695f230e45fc60 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001# SPDX-License-Identifier: GPL-2.0+
Simon Glass57454f42016-11-25 20:15:52 -07002# Copyright (c) 2016 Google, Inc
3# Written by Simon Glass <sjg@chromium.org>
4#
Simon Glass57454f42016-11-25 20:15:52 -07005# To run a single test, change to this directory, and:
6#
7# python -m unittest func_test.TestFunctional.testHelp
8
Simon Glass45d556d2020-07-09 18:39:45 -06009import collections
Simon Glassf3598922024-07-20 11:49:45 +010010import glob
Simon Glassc585dd42020-04-17 18:09:03 -060011import gzip
Simon Glassae7cf032018-09-14 04:57:31 -060012import hashlib
Simon Glass57454f42016-11-25 20:15:52 -070013from optparse import OptionParser
14import os
Simon Glass45d556d2020-07-09 18:39:45 -060015import re
Simon Glass57454f42016-11-25 20:15:52 -070016import shutil
17import struct
18import sys
19import tempfile
20import unittest
Simon Glass162017b2022-01-09 20:13:57 -070021import unittest.mock
22import urllib.error
Simon Glass57454f42016-11-25 20:15:52 -070023
Simon Glass4eae9252022-01-09 20:13:50 -070024from binman import bintool
Simon Glassc585dd42020-04-17 18:09:03 -060025from binman import cbfs_util
26from binman import cmdline
27from binman import control
28from binman import elf
29from binman import elf_test
Simon Glass3efb2972021-11-23 21:08:59 -070030from binman import fip_util
Simon Glassc585dd42020-04-17 18:09:03 -060031from binman import fmap_util
Simon Glassc585dd42020-04-17 18:09:03 -060032from binman import state
33from dtoc import fdt
34from dtoc import fdt_util
35from binman.etype import fdtmap
36from binman.etype import image_header
Simon Glass90cd6f02020-08-05 13:27:47 -060037from binman.image import Image
Simon Glass131444f2023-02-23 18:18:04 -070038from u_boot_pylib import command
Simon Glass14d64e32025-04-29 07:21:59 -060039from u_boot_pylib import terminal
Simon Glass131444f2023-02-23 18:18:04 -070040from u_boot_pylib import test_util
41from u_boot_pylib import tools
42from u_boot_pylib import tout
Simon Glass57454f42016-11-25 20:15:52 -070043
44# Contents of test files, corresponding to different entry types
Simon Glass303f62f2019-05-17 22:00:46 -060045U_BOOT_DATA = b'1234'
46U_BOOT_IMG_DATA = b'img'
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +030047U_BOOT_SPL_DATA = b'56780123456789abcdefghijklm'
48U_BOOT_TPL_DATA = b'tpl9876543210fedcbazywvuts'
Simon Glass56d05412022-02-28 07:16:54 -070049U_BOOT_VPL_DATA = b'vpl76543210fedcbazywxyz_'
Simon Glass303f62f2019-05-17 22:00:46 -060050BLOB_DATA = b'89'
51ME_DATA = b'0abcd'
52VGA_DATA = b'vga'
Sughosh Ganu269ee6d2023-08-22 23:09:59 +053053EFI_CAPSULE_DATA = b'efi'
Simon Glass303f62f2019-05-17 22:00:46 -060054U_BOOT_DTB_DATA = b'udtb'
55U_BOOT_SPL_DTB_DATA = b'spldtb'
56U_BOOT_TPL_DTB_DATA = b'tpldtb'
Simon Glass56d05412022-02-28 07:16:54 -070057U_BOOT_VPL_DTB_DATA = b'vpldtb'
Simon Glass303f62f2019-05-17 22:00:46 -060058X86_START16_DATA = b'start16'
59X86_START16_SPL_DATA = b'start16spl'
60X86_START16_TPL_DATA = b'start16tpl'
Simon Glass0b074d62019-08-24 07:22:48 -060061X86_RESET16_DATA = b'reset16'
62X86_RESET16_SPL_DATA = b'reset16spl'
63X86_RESET16_TPL_DATA = b'reset16tpl'
Simon Glass303f62f2019-05-17 22:00:46 -060064PPC_MPC85XX_BR_DATA = b'ppcmpc85xxbr'
65U_BOOT_NODTB_DATA = b'nodtb with microcode pointer somewhere in here'
66U_BOOT_SPL_NODTB_DATA = b'splnodtb with microcode pointer somewhere in here'
67U_BOOT_TPL_NODTB_DATA = b'tplnodtb with microcode pointer somewhere in here'
Simon Glass56d05412022-02-28 07:16:54 -070068U_BOOT_VPL_NODTB_DATA = b'vplnodtb'
Alper Nebi Yasak09fb0612022-02-08 01:08:04 +030069U_BOOT_EXP_DATA = U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA
70U_BOOT_SPL_EXP_DATA = U_BOOT_SPL_NODTB_DATA + U_BOOT_SPL_DTB_DATA
71U_BOOT_TPL_EXP_DATA = U_BOOT_TPL_NODTB_DATA + U_BOOT_TPL_DTB_DATA
Simon Glass303f62f2019-05-17 22:00:46 -060072FSP_DATA = b'fsp'
73CMC_DATA = b'cmc'
74VBT_DATA = b'vbt'
75MRC_DATA = b'mrc'
Simon Glass2ca52032018-07-17 13:25:33 -060076TEXT_DATA = 'text'
77TEXT_DATA2 = 'text2'
78TEXT_DATA3 = 'text3'
Simon Glass303f62f2019-05-17 22:00:46 -060079CROS_EC_RW_DATA = b'ecrw'
80GBB_DATA = b'gbbd'
81BMPBLK_DATA = b'bmp'
82VBLOCK_DATA = b'vblk'
83FILES_DATA = (b"sorry I'm late\nOh, don't bother apologising, I'm " +
84 b"sorry you're alive\n")
Simon Glassccec0262019-07-08 13:18:42 -060085COMPRESS_DATA = b'compress xxxxxxxxxxxxxxxxxxxxxx data'
Simon Glassd92c8362020-10-26 17:40:25 -060086COMPRESS_DATA_BIG = COMPRESS_DATA * 2
Simon Glass303f62f2019-05-17 22:00:46 -060087REFCODE_DATA = b'refcode'
Simon Glassba7985d2019-08-24 07:23:07 -060088FSP_M_DATA = b'fsp_m'
Simon Glass4d9086d2019-10-20 21:31:35 -060089FSP_S_DATA = b'fsp_s'
Simon Glass9ea87b22019-10-20 21:31:36 -060090FSP_T_DATA = b'fsp_t'
Simon Glass559c4de2020-09-01 05:13:58 -060091ATF_BL31_DATA = b'bl31'
Roger Quadros5cdcea02022-02-19 20:50:04 +020092TEE_OS_DATA = b'this is some tee OS data'
Neha Malcom Francis59be2552023-12-05 15:12:18 +053093TI_DM_DATA = b'tidmtidm'
Simon Glass3efb2972021-11-23 21:08:59 -070094ATF_BL2U_DATA = b'bl2u'
Bin Mengc0b15742021-05-10 20:23:33 +080095OPENSBI_DATA = b'opensbi'
Samuel Holland9d8cc632020-10-21 21:12:15 -050096SCP_DATA = b'scp'
Jonas Karlman35305492023-02-25 19:01:33 +000097ROCKCHIP_TPL_DATA = b'rockchip-tpl'
Simon Glassa435cd12020-09-01 05:13:59 -060098TEST_FDT1_DATA = b'fdt1'
99TEST_FDT2_DATA = b'test-fdt2'
Simon Glassa0729502020-09-06 10:35:33 -0600100ENV_DATA = b'var1=1\nvar2="2"'
Christian Taedcke62ac29a2023-07-17 09:05:54 +0200101ENCRYPTED_IV_DATA = b'123456'
102ENCRYPTED_KEY_DATA = b'abcde'
Philippe Reynesebe96cb2022-03-28 22:57:04 +0200103PRE_LOAD_MAGIC = b'UBSH'
104PRE_LOAD_VERSION = 0x11223344.to_bytes(4, 'big')
105PRE_LOAD_HDR_SIZE = 0x00001000.to_bytes(4, 'big')
Neha Malcom Francis3b788942023-07-22 00:14:24 +0530106TI_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 +0530107TI_UNSECURE_DATA = b'unsecuredata'
Alice Guo1d334022025-04-28 18:37:39 +0800108IMX_LPDDR_IMEM_DATA = b'qwertyuiop1234567890'
109IMX_LPDDR_DMEM_DATA = b'asdfghjklzxcvbnm'
Simon Glassa435cd12020-09-01 05:13:59 -0600110
111# Subdirectory of the input dir to use to put test FDTs
112TEST_FDT_SUBDIR = 'fdts'
Simon Glassdb168d42018-07-17 13:25:39 -0600113
Simon Glass2c6adba2019-07-20 12:23:47 -0600114# The expected size for the device tree in some tests
Simon Glass4c613bf2019-07-08 14:25:50 -0600115EXTRACT_DTB_SIZE = 0x3c9
116
Simon Glass2c6adba2019-07-20 12:23:47 -0600117# Properties expected to be in the device tree when update_dtb is used
118BASE_DTB_PROPS = ['offset', 'size', 'image-pos']
119
Simon Glassfb30e292019-07-20 12:23:51 -0600120# Extra properties expected to be in the device tree when allow-repack is used
121REPACK_DTB_PROPS = ['orig-offset', 'orig-size']
122
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +0200123# Supported compression bintools
Stefan Herbrechtsmeiera5e4dcb2022-08-19 16:25:38 +0200124COMP_BINTOOLS = ['bzip2', 'gzip', 'lz4', 'lzma_alone', 'lzop', 'xz', 'zstd']
Simon Glass57454f42016-11-25 20:15:52 -0700125
Simon Glassad5cfe12023-01-07 14:07:14 -0700126TEE_ADDR = 0x5678
127
Sughosh Ganu269ee6d2023-08-22 23:09:59 +0530128# Firmware Management Protocol(FMP) GUID
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +0530129FW_MGMT_GUID = '6dcbd5ed-e82d-4c44-bda1-7194199ad92a'
Sughosh Ganu269ee6d2023-08-22 23:09:59 +0530130# Image GUID specified in the DTS
Caleb Connolly4bdc9602024-08-30 13:34:35 +0100131CAPSULE_IMAGE_GUID = '985F2937-7C2E-5E9A-8A5E-8E063312964B'
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +0530132# Windows cert GUID
133WIN_CERT_TYPE_EFI_GUID = '4aafd29d-68df-49ee-8aa9-347d375665a7'
Sughosh Ganu6b2d18a2023-10-10 14:40:59 +0530134# Empty capsule GUIDs
135EMPTY_CAPSULE_ACCEPT_GUID = '0c996046-bcc0-4d04-85ec-e1fcedf1c6f8'
136EMPTY_CAPSULE_REVERT_GUID = 'acd58b4b-c0e8-475f-99b5-6b3f7e07aaf0'
Sughosh Ganu269ee6d2023-08-22 23:09:59 +0530137
Simon Glass57454f42016-11-25 20:15:52 -0700138class TestFunctional(unittest.TestCase):
139 """Functional tests for binman
140
141 Most of these use a sample .dts file to build an image and then check
142 that it looks correct. The sample files are in the test/ subdirectory
143 and are numbered.
144
145 For each entry type a very small test file is created using fixed
146 string contents. This makes it easy to test that things look right, and
147 debug problems.
148
149 In some cases a 'real' file must be used - these are also supplied in
150 the test/ diurectory.
151 """
152 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600153 def setUpClass(cls):
Simon Glassb3393262017-11-12 21:52:20 -0700154 global entry
Simon Glassc585dd42020-04-17 18:09:03 -0600155 from binman import entry
Simon Glassb3393262017-11-12 21:52:20 -0700156
Simon Glass57454f42016-11-25 20:15:52 -0700157 # Handle the case where argv[0] is 'python'
Simon Glass862f8e22019-08-24 07:22:43 -0600158 cls._binman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
159 cls._binman_pathname = os.path.join(cls._binman_dir, 'binman')
Simon Glass57454f42016-11-25 20:15:52 -0700160
161 # Create a temporary directory for input files
Simon Glass862f8e22019-08-24 07:22:43 -0600162 cls._indir = tempfile.mkdtemp(prefix='binmant.')
Simon Glass57454f42016-11-25 20:15:52 -0700163
164 # Create some test files
165 TestFunctional._MakeInputFile('u-boot.bin', U_BOOT_DATA)
166 TestFunctional._MakeInputFile('u-boot.img', U_BOOT_IMG_DATA)
167 TestFunctional._MakeInputFile('spl/u-boot-spl.bin', U_BOOT_SPL_DATA)
Simon Glass8425a1f2018-07-17 13:25:48 -0600168 TestFunctional._MakeInputFile('tpl/u-boot-tpl.bin', U_BOOT_TPL_DATA)
Simon Glass56d05412022-02-28 07:16:54 -0700169 TestFunctional._MakeInputFile('vpl/u-boot-vpl.bin', U_BOOT_VPL_DATA)
Simon Glass57454f42016-11-25 20:15:52 -0700170 TestFunctional._MakeInputFile('blobfile', BLOB_DATA)
Simon Glass72232452016-11-25 20:15:53 -0700171 TestFunctional._MakeInputFile('me.bin', ME_DATA)
172 TestFunctional._MakeInputFile('vga.bin', VGA_DATA)
Simon Glass862f8e22019-08-24 07:22:43 -0600173 cls._ResetDtbs()
Simon Glass0b074d62019-08-24 07:22:48 -0600174
Jagdish Gediya311d4842018-09-03 21:35:08 +0530175 TestFunctional._MakeInputFile('u-boot-br.bin', PPC_MPC85XX_BR_DATA)
Simon Glass0b074d62019-08-24 07:22:48 -0600176
Simon Glassabab18c2019-08-24 07:22:49 -0600177 TestFunctional._MakeInputFile('u-boot-x86-start16.bin', X86_START16_DATA)
178 TestFunctional._MakeInputFile('spl/u-boot-x86-start16-spl.bin',
Simon Glasse83679d2017-11-12 21:52:26 -0700179 X86_START16_SPL_DATA)
Simon Glassabab18c2019-08-24 07:22:49 -0600180 TestFunctional._MakeInputFile('tpl/u-boot-x86-start16-tpl.bin',
Simon Glassed40e962018-09-14 04:57:10 -0600181 X86_START16_TPL_DATA)
Simon Glass0b074d62019-08-24 07:22:48 -0600182
183 TestFunctional._MakeInputFile('u-boot-x86-reset16.bin',
184 X86_RESET16_DATA)
185 TestFunctional._MakeInputFile('spl/u-boot-x86-reset16-spl.bin',
186 X86_RESET16_SPL_DATA)
187 TestFunctional._MakeInputFile('tpl/u-boot-x86-reset16-tpl.bin',
188 X86_RESET16_TPL_DATA)
189
Simon Glass57454f42016-11-25 20:15:52 -0700190 TestFunctional._MakeInputFile('u-boot-nodtb.bin', U_BOOT_NODTB_DATA)
Simon Glass3d274232017-11-12 21:52:27 -0700191 TestFunctional._MakeInputFile('spl/u-boot-spl-nodtb.bin',
192 U_BOOT_SPL_NODTB_DATA)
Simon Glass3fb4f422018-09-14 04:57:32 -0600193 TestFunctional._MakeInputFile('tpl/u-boot-tpl-nodtb.bin',
194 U_BOOT_TPL_NODTB_DATA)
Simon Glass56d05412022-02-28 07:16:54 -0700195 TestFunctional._MakeInputFile('vpl/u-boot-vpl-nodtb.bin',
196 U_BOOT_VPL_NODTB_DATA)
Simon Glassb4176d42016-11-25 20:15:56 -0700197 TestFunctional._MakeInputFile('fsp.bin', FSP_DATA)
198 TestFunctional._MakeInputFile('cmc.bin', CMC_DATA)
Bin Mengd7bcdf52017-08-15 22:41:54 -0700199 TestFunctional._MakeInputFile('vbt.bin', VBT_DATA)
Simon Glassa409c932017-11-12 21:52:28 -0700200 TestFunctional._MakeInputFile('mrc.bin', MRC_DATA)
Simon Glassdb168d42018-07-17 13:25:39 -0600201 TestFunctional._MakeInputFile('ecrw.bin', CROS_EC_RW_DATA)
Simon Glassc1ae83c2018-07-17 13:25:44 -0600202 TestFunctional._MakeInputDir('devkeys')
203 TestFunctional._MakeInputFile('bmpblk.bin', BMPBLK_DATA)
Simon Glass41902e42018-10-01 12:22:31 -0600204 TestFunctional._MakeInputFile('refcode.bin', REFCODE_DATA)
Simon Glassba7985d2019-08-24 07:23:07 -0600205 TestFunctional._MakeInputFile('fsp_m.bin', FSP_M_DATA)
Simon Glass4d9086d2019-10-20 21:31:35 -0600206 TestFunctional._MakeInputFile('fsp_s.bin', FSP_S_DATA)
Simon Glass9ea87b22019-10-20 21:31:36 -0600207 TestFunctional._MakeInputFile('fsp_t.bin', FSP_T_DATA)
Alice Guo1d334022025-04-28 18:37:39 +0800208 TestFunctional._MakeInputFile('lpddr5_imem.bin', IMX_LPDDR_IMEM_DATA)
209 TestFunctional._MakeInputFile('lpddr5_dmem.bin', IMX_LPDDR_DMEM_DATA)
Simon Glass57454f42016-11-25 20:15:52 -0700210
Simon Glassf6290892019-08-24 07:22:53 -0600211 cls._elf_testdir = os.path.join(cls._indir, 'elftest')
212 elf_test.BuildElfTestFiles(cls._elf_testdir)
213
Simon Glass72232452016-11-25 20:15:53 -0700214 # ELF file with a '_dt_ucode_base_size' symbol
Simon Glass4affd4b2019-08-24 07:22:54 -0600215 TestFunctional._MakeInputFile('u-boot',
Simon Glass80025522022-01-29 14:14:04 -0700216 tools.read_file(cls.ElfTestFile('u_boot_ucode_ptr')))
Simon Glass72232452016-11-25 20:15:53 -0700217
218 # Intel flash descriptor file
Simon Glasse88cef92020-07-09 18:39:41 -0600219 cls._SetupDescriptor()
Simon Glass72232452016-11-25 20:15:53 -0700220
Simon Glass862f8e22019-08-24 07:22:43 -0600221 shutil.copytree(cls.TestFile('files'),
222 os.path.join(cls._indir, 'files'))
Simon Glassac6328c2018-09-14 04:57:28 -0600223
Neha Malcom Francis3b788942023-07-22 00:14:24 +0530224 shutil.copytree(cls.TestFile('yaml'),
225 os.path.join(cls._indir, 'yaml'))
226
Simon Glass7ba33592018-09-14 04:57:26 -0600227 TestFunctional._MakeInputFile('compress', COMPRESS_DATA)
Simon Glassd92c8362020-10-26 17:40:25 -0600228 TestFunctional._MakeInputFile('compress_big', COMPRESS_DATA_BIG)
Simon Glass559c4de2020-09-01 05:13:58 -0600229 TestFunctional._MakeInputFile('bl31.bin', ATF_BL31_DATA)
Roger Quadros5cdcea02022-02-19 20:50:04 +0200230 TestFunctional._MakeInputFile('tee-pager.bin', TEE_OS_DATA)
Neha Malcom Francis59be2552023-12-05 15:12:18 +0530231 TestFunctional._MakeInputFile('dm.bin', TI_DM_DATA)
Simon Glass3efb2972021-11-23 21:08:59 -0700232 TestFunctional._MakeInputFile('bl2u.bin', ATF_BL2U_DATA)
Bin Mengc0b15742021-05-10 20:23:33 +0800233 TestFunctional._MakeInputFile('fw_dynamic.bin', OPENSBI_DATA)
Samuel Holland9d8cc632020-10-21 21:12:15 -0500234 TestFunctional._MakeInputFile('scp.bin', SCP_DATA)
Jonas Karlman35305492023-02-25 19:01:33 +0000235 TestFunctional._MakeInputFile('rockchip-tpl.bin', ROCKCHIP_TPL_DATA)
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +0530236 TestFunctional._MakeInputFile('ti_unsecure.bin', TI_UNSECURE_DATA)
Sughosh Ganu269ee6d2023-08-22 23:09:59 +0530237 TestFunctional._MakeInputFile('capsule_input.bin', EFI_CAPSULE_DATA)
Simon Glass7ba33592018-09-14 04:57:26 -0600238
Simon Glassa435cd12020-09-01 05:13:59 -0600239 # Add a few .dtb files for testing
240 TestFunctional._MakeInputFile('%s/test-fdt1.dtb' % TEST_FDT_SUBDIR,
241 TEST_FDT1_DATA)
242 TestFunctional._MakeInputFile('%s/test-fdt2.dtb' % TEST_FDT_SUBDIR,
243 TEST_FDT2_DATA)
244
Simon Glassa0729502020-09-06 10:35:33 -0600245 TestFunctional._MakeInputFile('env.txt', ENV_DATA)
246
Simon Glass5f423422022-03-05 20:19:12 -0700247 # ELF file with two sections in different parts of memory, used for both
248 # ATF and OP_TEE
249 TestFunctional._MakeInputFile('bl31.elf',
250 tools.read_file(cls.ElfTestFile('elf_sections')))
251 TestFunctional._MakeInputFile('tee.elf',
252 tools.read_file(cls.ElfTestFile('elf_sections')))
253
Simon Glassad5cfe12023-01-07 14:07:14 -0700254 # Newer OP_TEE file in v1 binary format
255 cls.make_tee_bin('tee.bin')
256
Christian Taedcke62ac29a2023-07-17 09:05:54 +0200257 # test files for encrypted tests
258 TestFunctional._MakeInputFile('encrypted-file.iv', ENCRYPTED_IV_DATA)
259 TestFunctional._MakeInputFile('encrypted-file.key', ENCRYPTED_KEY_DATA)
260
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +0200261 cls.comp_bintools = {}
262 for name in COMP_BINTOOLS:
263 cls.comp_bintools[name] = bintool.Bintool.create(name)
Simon Glass1de34482019-07-08 13:18:53 -0600264
Simon Glass57454f42016-11-25 20:15:52 -0700265 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600266 def tearDownClass(cls):
Simon Glass57454f42016-11-25 20:15:52 -0700267 """Remove the temporary input directory and its contents"""
Simon Glass862f8e22019-08-24 07:22:43 -0600268 if cls.preserve_indir:
269 print('Preserving input dir: %s' % cls._indir)
Simon Glass1c420c92019-07-08 13:18:49 -0600270 else:
Simon Glass862f8e22019-08-24 07:22:43 -0600271 if cls._indir:
272 shutil.rmtree(cls._indir)
273 cls._indir = None
Simon Glass57454f42016-11-25 20:15:52 -0700274
Simon Glass1c420c92019-07-08 13:18:49 -0600275 @classmethod
Simon Glasscebfab22019-07-08 13:18:50 -0600276 def setup_test_args(cls, preserve_indir=False, preserve_outdirs=False,
Simon Glassf46732a2019-07-08 14:25:29 -0600277 toolpath=None, verbosity=None):
Simon Glass1c420c92019-07-08 13:18:49 -0600278 """Accept arguments controlling test execution
279
280 Args:
281 preserve_indir: Preserve the shared input directory used by all
282 tests in this class.
283 preserve_outdir: Preserve the output directories used by tests. Each
284 test has its own, so this is normally only useful when running a
285 single test.
Simon Glasscebfab22019-07-08 13:18:50 -0600286 toolpath: ist of paths to use for tools
Simon Glass1c420c92019-07-08 13:18:49 -0600287 """
288 cls.preserve_indir = preserve_indir
289 cls.preserve_outdirs = preserve_outdirs
Simon Glasscebfab22019-07-08 13:18:50 -0600290 cls.toolpath = toolpath
Simon Glassf46732a2019-07-08 14:25:29 -0600291 cls.verbosity = verbosity
Simon Glass1c420c92019-07-08 13:18:49 -0600292
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +0200293 def _CheckBintool(self, bintool):
294 if not bintool.is_present():
295 self.skipTest('%s not available' % bintool.name)
296
Simon Glass1de34482019-07-08 13:18:53 -0600297 def _CheckLz4(self):
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +0200298 bintool = self.comp_bintools['lz4']
299 self._CheckBintool(bintool)
Simon Glass1de34482019-07-08 13:18:53 -0600300
Simon Glassee9d10d2019-07-20 12:24:09 -0600301 def _CleanupOutputDir(self):
302 """Remove the temporary output directory"""
303 if self.preserve_outdirs:
304 print('Preserving output dir: %s' % tools.outdir)
305 else:
Simon Glass80025522022-01-29 14:14:04 -0700306 tools._finalise_for_test()
Simon Glassee9d10d2019-07-20 12:24:09 -0600307
Simon Glass57454f42016-11-25 20:15:52 -0700308 def setUp(self):
309 # Enable this to turn on debugging output
Simon Glass011f1b32022-01-29 14:14:15 -0700310 # tout.init(tout.DEBUG)
Simon Glass5dc22cf2025-02-03 09:26:42 -0700311 command.TEST_RESULT = None
Simon Glass57454f42016-11-25 20:15:52 -0700312
313 def tearDown(self):
314 """Remove the temporary output directory"""
Simon Glassee9d10d2019-07-20 12:24:09 -0600315 self._CleanupOutputDir()
Simon Glass57454f42016-11-25 20:15:52 -0700316
Simon Glassb3d6fc72019-07-20 12:24:10 -0600317 def _SetupImageInTmpdir(self):
318 """Set up the output image in a new temporary directory
319
320 This is used when an image has been generated in the output directory,
321 but we want to run binman again. This will create a new output
322 directory and fail to delete the original one.
323
324 This creates a new temporary directory, copies the image to it (with a
325 new name) and removes the old output directory.
326
327 Returns:
328 Tuple:
329 Temporary directory to use
330 New image filename
331 """
Simon Glass80025522022-01-29 14:14:04 -0700332 image_fname = tools.get_output_filename('image.bin')
Simon Glassb3d6fc72019-07-20 12:24:10 -0600333 tmpdir = tempfile.mkdtemp(prefix='binman.')
334 updated_fname = os.path.join(tmpdir, 'image-updated.bin')
Simon Glass80025522022-01-29 14:14:04 -0700335 tools.write_file(updated_fname, tools.read_file(image_fname))
Simon Glassb3d6fc72019-07-20 12:24:10 -0600336 self._CleanupOutputDir()
337 return tmpdir, updated_fname
338
Simon Glass8425a1f2018-07-17 13:25:48 -0600339 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600340 def _ResetDtbs(cls):
Simon Glass8425a1f2018-07-17 13:25:48 -0600341 TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA)
342 TestFunctional._MakeInputFile('spl/u-boot-spl.dtb', U_BOOT_SPL_DTB_DATA)
343 TestFunctional._MakeInputFile('tpl/u-boot-tpl.dtb', U_BOOT_TPL_DTB_DATA)
Simon Glass56d05412022-02-28 07:16:54 -0700344 TestFunctional._MakeInputFile('vpl/u-boot-vpl.dtb', U_BOOT_VPL_DTB_DATA)
Simon Glass8425a1f2018-07-17 13:25:48 -0600345
Simon Glass57454f42016-11-25 20:15:52 -0700346 def _RunBinman(self, *args, **kwargs):
347 """Run binman using the command line
348
349 Args:
350 Arguments to pass, as a list of strings
351 kwargs: Arguments to pass to Command.RunPipe()
352 """
Simon Glass51f55182025-02-03 09:26:45 -0700353 all_args = [self._binman_pathname] + list(args)
354 result = command.run_one(*all_args, capture=True, capture_stderr=True,
355 raise_on_error=False)
Simon Glass57454f42016-11-25 20:15:52 -0700356 if result.return_code and kwargs.get('raise_on_error', True):
357 raise Exception("Error running '%s': %s" % (' '.join(args),
358 result.stdout + result.stderr))
359 return result
360
Simon Glassf46732a2019-07-08 14:25:29 -0600361 def _DoBinman(self, *argv):
Simon Glass57454f42016-11-25 20:15:52 -0700362 """Run binman using directly (in the same process)
363
364 Args:
365 Arguments to pass, as a list of strings
366 Returns:
367 Return value (0 for success)
368 """
Simon Glassf46732a2019-07-08 14:25:29 -0600369 argv = list(argv)
370 args = cmdline.ParseArgs(argv)
371 args.pager = 'binman-invalid-pager'
372 args.build_dir = self._indir
Simon Glass57454f42016-11-25 20:15:52 -0700373
374 # For testing, you can force an increase in verbosity here
Simon Glassf46732a2019-07-08 14:25:29 -0600375 # args.verbosity = tout.DEBUG
376 return control.Binman(args)
Simon Glass57454f42016-11-25 20:15:52 -0700377
Simon Glass91710b32018-07-17 13:25:32 -0600378 def _DoTestFile(self, fname, debug=False, map=False, update_dtb=False,
Simon Glassb4595d82019-04-25 21:58:34 -0600379 entry_args=None, images=None, use_real_dtb=False,
Simon Glassed930672021-03-18 20:25:05 +1300380 use_expanded=False, verbosity=None, allow_missing=False,
Heiko Thiery6d451362022-01-06 11:49:41 +0100381 allow_fake_blobs=False, extra_indirs=None, threads=None,
Simon Glass66152ce2022-01-09 20:14:09 -0700382 test_section_timeout=False, update_fdt_in_elf=None,
Andrew Davis6b463da2023-07-22 00:14:44 +0530383 force_missing_bintools='', ignore_missing=False, output_dir=None):
Simon Glass57454f42016-11-25 20:15:52 -0700384 """Run binman with a given test file
385
386 Args:
Simon Glass511f6582018-10-01 12:22:30 -0600387 fname: Device-tree source filename to use (e.g. 005_simple.dts)
Simon Glass1e324002018-06-01 09:38:19 -0600388 debug: True to enable debugging output
Simon Glass30732662018-06-01 09:38:20 -0600389 map: True to output map files for the images
Simon Glasse8561af2018-08-01 15:22:37 -0600390 update_dtb: Update the offset and size of each entry in the device
Simon Glassa87014e2018-07-06 10:27:42 -0600391 tree before packing it into the image
Simon Glass3b376c32018-09-14 04:57:12 -0600392 entry_args: Dict of entry args to supply to binman
393 key: arg name
394 value: value of that arg
395 images: List of image names to build
Simon Glass31ee50f2020-09-01 05:13:55 -0600396 use_real_dtb: True to use the test file as the contents of
397 the u-boot-dtb entry. Normally this is not needed and the
398 test contents (the U_BOOT_DTB_DATA string) can be used.
399 But in some test we need the real contents.
Simon Glassed930672021-03-18 20:25:05 +1300400 use_expanded: True to use expanded entries where available, e.g.
401 'u-boot-expanded' instead of 'u-boot'
Simon Glass31ee50f2020-09-01 05:13:55 -0600402 verbosity: Verbosity level to use (0-3, None=don't set it)
403 allow_missing: Set the '--allow-missing' flag so that missing
404 external binaries just produce a warning instead of an error
Heiko Thiery6d451362022-01-06 11:49:41 +0100405 allow_fake_blobs: Set the '--fake-ext-blobs' flag
Simon Glassa435cd12020-09-01 05:13:59 -0600406 extra_indirs: Extra input directories to add using -I
Simon Glass76f496d2021-07-06 10:36:37 -0600407 threads: Number of threads to use (None for default, 0 for
408 single-threaded)
Simon Glass9a798402021-11-03 21:09:17 -0600409 test_section_timeout: True to force the first time to timeout, as
410 used in testThreadTimeout()
Simon Glassadfb8492021-11-03 21:09:18 -0600411 update_fdt_in_elf: Value to pass with --update-fdt-in-elf=xxx
Simon Glassb553e8a2024-08-26 13:11:29 -0600412 force_missing_bintools (str): comma-separated list of bintools to
Simon Glass66152ce2022-01-09 20:14:09 -0700413 regard as missing
Simon Glassb553e8a2024-08-26 13:11:29 -0600414 ignore_missing (bool): True to return success even if there are
415 missing blobs or bintools
Andrew Davis6b463da2023-07-22 00:14:44 +0530416 output_dir: Specific output directory to use for image using -O
Simon Glass9a798402021-11-03 21:09:17 -0600417
418 Returns:
419 int return code, 0 on success
Simon Glass57454f42016-11-25 20:15:52 -0700420 """
Simon Glassf46732a2019-07-08 14:25:29 -0600421 args = []
Simon Glass075a45c2017-11-13 18:55:00 -0700422 if debug:
423 args.append('-D')
Simon Glassf46732a2019-07-08 14:25:29 -0600424 if verbosity is not None:
425 args.append('-v%d' % verbosity)
426 elif self.verbosity:
427 args.append('-v%d' % self.verbosity)
428 if self.toolpath:
429 for path in self.toolpath:
430 args += ['--toolpath', path]
Simon Glass76f496d2021-07-06 10:36:37 -0600431 if threads is not None:
432 args.append('-T%d' % threads)
433 if test_section_timeout:
434 args.append('--test-section-timeout')
Simon Glassf46732a2019-07-08 14:25:29 -0600435 args += ['build', '-p', '-I', self._indir, '-d', self.TestFile(fname)]
Simon Glass30732662018-06-01 09:38:20 -0600436 if map:
437 args.append('-m')
Simon Glassa87014e2018-07-06 10:27:42 -0600438 if update_dtb:
Simon Glass38a411c2019-07-08 13:18:47 -0600439 args.append('-u')
Simon Glass31402012018-09-14 04:57:23 -0600440 if not use_real_dtb:
441 args.append('--fake-dtb')
Simon Glassed930672021-03-18 20:25:05 +1300442 if not use_expanded:
443 args.append('--no-expanded')
Simon Glass91710b32018-07-17 13:25:32 -0600444 if entry_args:
Simon Glass5f3645b2019-05-14 15:53:41 -0600445 for arg, value in entry_args.items():
Simon Glass91710b32018-07-17 13:25:32 -0600446 args.append('-a%s=%s' % (arg, value))
Simon Glass5d94cc62020-07-09 18:39:38 -0600447 if allow_missing:
448 args.append('-M')
Simon Glass6bce5dc2022-11-09 19:14:42 -0700449 if ignore_missing:
450 args.append('-W')
Heiko Thiery6d451362022-01-06 11:49:41 +0100451 if allow_fake_blobs:
452 args.append('--fake-ext-blobs')
Simon Glass66152ce2022-01-09 20:14:09 -0700453 if force_missing_bintools:
454 args += ['--force-missing-bintools', force_missing_bintools]
Simon Glassadfb8492021-11-03 21:09:18 -0600455 if update_fdt_in_elf:
456 args += ['--update-fdt-in-elf', update_fdt_in_elf]
Simon Glass3b376c32018-09-14 04:57:12 -0600457 if images:
458 for image in images:
459 args += ['-i', image]
Simon Glassa435cd12020-09-01 05:13:59 -0600460 if extra_indirs:
461 for indir in extra_indirs:
462 args += ['-I', indir]
Andrew Davis6b463da2023-07-22 00:14:44 +0530463 if output_dir:
464 args += ['-O', output_dir]
Simon Glass075a45c2017-11-13 18:55:00 -0700465 return self._DoBinman(*args)
Simon Glass57454f42016-11-25 20:15:52 -0700466
467 def _SetupDtb(self, fname, outfile='u-boot.dtb'):
Simon Glass72232452016-11-25 20:15:53 -0700468 """Set up a new test device-tree file
469
470 The given file is compiled and set up as the device tree to be used
471 for ths test.
472
473 Args:
474 fname: Filename of .dts file to read
Simon Glass1e324002018-06-01 09:38:19 -0600475 outfile: Output filename for compiled device-tree binary
Simon Glass72232452016-11-25 20:15:53 -0700476
477 Returns:
Simon Glass1e324002018-06-01 09:38:19 -0600478 Contents of device-tree binary
Simon Glass72232452016-11-25 20:15:53 -0700479 """
Simon Glassb8d2daa2019-07-20 12:23:49 -0600480 tmpdir = tempfile.mkdtemp(prefix='binmant.')
481 dtb = fdt_util.EnsureCompiled(self.TestFile(fname), tmpdir)
Simon Glass33486662019-05-14 15:53:42 -0600482 with open(dtb, 'rb') as fd:
Simon Glass57454f42016-11-25 20:15:52 -0700483 data = fd.read()
484 TestFunctional._MakeInputFile(outfile, data)
Simon Glassb8d2daa2019-07-20 12:23:49 -0600485 shutil.rmtree(tmpdir)
Simon Glass752e7552018-10-01 21:12:41 -0600486 return data
Simon Glass57454f42016-11-25 20:15:52 -0700487
Simon Glass56d05412022-02-28 07:16:54 -0700488 def _GetDtbContentsForSpls(self, dtb_data, name):
489 """Create a version of the main DTB for SPL / TPL / VPL
Simon Glasse219aa42018-09-14 04:57:24 -0600490
491 For testing we don't actually have different versions of the DTB. With
492 U-Boot we normally run fdtgrep to remove unwanted nodes, but for tests
493 we don't normally have any unwanted nodes.
494
495 We still want the DTBs for SPL and TPL to be different though, since
496 otherwise it is confusing to know which one we are looking at. So add
497 an 'spl' or 'tpl' property to the top-level node.
Simon Glass31ee50f2020-09-01 05:13:55 -0600498
499 Args:
500 dtb_data: dtb data to modify (this should be a value devicetree)
501 name: Name of a new property to add
502
503 Returns:
504 New dtb data with the property added
Simon Glasse219aa42018-09-14 04:57:24 -0600505 """
506 dtb = fdt.Fdt.FromData(dtb_data)
507 dtb.Scan()
508 dtb.GetNode('/binman').AddZeroProp(name)
509 dtb.Sync(auto_resize=True)
510 dtb.Pack()
511 return dtb.GetContents()
512
Simon Glassed930672021-03-18 20:25:05 +1300513 def _DoReadFileDtb(self, fname, use_real_dtb=False, use_expanded=False,
Simon Glass3eb30a42024-08-26 13:11:42 -0600514 verbosity=None, map=False, update_dtb=False,
515 entry_args=None, reset_dtbs=True, extra_indirs=None,
516 threads=None):
Simon Glass57454f42016-11-25 20:15:52 -0700517 """Run binman and return the resulting image
518
519 This runs binman with a given test file and then reads the resulting
520 output file. It is a shortcut function since most tests need to do
521 these steps.
522
523 Raises an assertion failure if binman returns a non-zero exit code.
524
525 Args:
Simon Glass511f6582018-10-01 12:22:30 -0600526 fname: Device-tree source filename to use (e.g. 005_simple.dts)
Simon Glass57454f42016-11-25 20:15:52 -0700527 use_real_dtb: True to use the test file as the contents of
528 the u-boot-dtb entry. Normally this is not needed and the
529 test contents (the U_BOOT_DTB_DATA string) can be used.
530 But in some test we need the real contents.
Simon Glassed930672021-03-18 20:25:05 +1300531 use_expanded: True to use expanded entries where available, e.g.
532 'u-boot-expanded' instead of 'u-boot'
Simon Glass3eb30a42024-08-26 13:11:42 -0600533 verbosity: Verbosity level to use (0-3, None=don't set it)
Simon Glass30732662018-06-01 09:38:20 -0600534 map: True to output map files for the images
Simon Glasse8561af2018-08-01 15:22:37 -0600535 update_dtb: Update the offset and size of each entry in the device
Simon Glassa87014e2018-07-06 10:27:42 -0600536 tree before packing it into the image
Simon Glass31ee50f2020-09-01 05:13:55 -0600537 entry_args: Dict of entry args to supply to binman
538 key: arg name
539 value: value of that arg
540 reset_dtbs: With use_real_dtb the test dtb is overwritten by this
541 function. If reset_dtbs is True, then the original test dtb
542 is written back before this function finishes
Simon Glassa435cd12020-09-01 05:13:59 -0600543 extra_indirs: Extra input directories to add using -I
Simon Glass76f496d2021-07-06 10:36:37 -0600544 threads: Number of threads to use (None for default, 0 for
545 single-threaded)
Simon Glass72232452016-11-25 20:15:53 -0700546
547 Returns:
548 Tuple:
549 Resulting image contents
550 Device tree contents
Simon Glass30732662018-06-01 09:38:20 -0600551 Map data showing contents of image (or None if none)
Simon Glassdef77b52018-07-17 13:25:27 -0600552 Output device tree binary filename ('u-boot.dtb' path)
Simon Glass57454f42016-11-25 20:15:52 -0700553 """
Simon Glass72232452016-11-25 20:15:53 -0700554 dtb_data = None
Simon Glass57454f42016-11-25 20:15:52 -0700555 # Use the compiled test file as the u-boot-dtb input
556 if use_real_dtb:
Simon Glass72232452016-11-25 20:15:53 -0700557 dtb_data = self._SetupDtb(fname)
Simon Glasse219aa42018-09-14 04:57:24 -0600558
559 # For testing purposes, make a copy of the DT for SPL and TPL. Add
Simon Glassd9e01d22024-07-20 11:49:40 +0100560 # a node indicating which it is, to aid verification.
Simon Glass56d05412022-02-28 07:16:54 -0700561 for name in ['spl', 'tpl', 'vpl']:
Simon Glasse219aa42018-09-14 04:57:24 -0600562 dtb_fname = '%s/u-boot-%s.dtb' % (name, name)
563 outfile = os.path.join(self._indir, dtb_fname)
564 TestFunctional._MakeInputFile(dtb_fname,
Simon Glass56d05412022-02-28 07:16:54 -0700565 self._GetDtbContentsForSpls(dtb_data, name))
Simon Glass57454f42016-11-25 20:15:52 -0700566
567 try:
Simon Glass91710b32018-07-17 13:25:32 -0600568 retcode = self._DoTestFile(fname, map=map, update_dtb=update_dtb,
Simon Glassa435cd12020-09-01 05:13:59 -0600569 entry_args=entry_args, use_real_dtb=use_real_dtb,
Simon Glass3eb30a42024-08-26 13:11:42 -0600570 use_expanded=use_expanded, verbosity=verbosity,
571 extra_indirs=extra_indirs,
Simon Glass76f496d2021-07-06 10:36:37 -0600572 threads=threads)
Simon Glass57454f42016-11-25 20:15:52 -0700573 self.assertEqual(0, retcode)
Simon Glass80025522022-01-29 14:14:04 -0700574 out_dtb_fname = tools.get_output_filename('u-boot.dtb.out')
Simon Glass57454f42016-11-25 20:15:52 -0700575
576 # Find the (only) image, read it and return its contents
577 image = control.images['image']
Simon Glass80025522022-01-29 14:14:04 -0700578 image_fname = tools.get_output_filename('image.bin')
Simon Glassa87014e2018-07-06 10:27:42 -0600579 self.assertTrue(os.path.exists(image_fname))
Simon Glass30732662018-06-01 09:38:20 -0600580 if map:
Simon Glass80025522022-01-29 14:14:04 -0700581 map_fname = tools.get_output_filename('image.map')
Simon Glass30732662018-06-01 09:38:20 -0600582 with open(map_fname) as fd:
583 map_data = fd.read()
584 else:
585 map_data = None
Simon Glass33486662019-05-14 15:53:42 -0600586 with open(image_fname, 'rb') as fd:
Simon Glassa87014e2018-07-06 10:27:42 -0600587 return fd.read(), dtb_data, map_data, out_dtb_fname
Simon Glass57454f42016-11-25 20:15:52 -0700588 finally:
589 # Put the test file back
Simon Glasse219aa42018-09-14 04:57:24 -0600590 if reset_dtbs and use_real_dtb:
Simon Glass8425a1f2018-07-17 13:25:48 -0600591 self._ResetDtbs()
Simon Glass57454f42016-11-25 20:15:52 -0700592
Simon Glass5b4bce32019-07-08 14:25:26 -0600593 def _DoReadFileRealDtb(self, fname):
594 """Run binman with a real .dtb file and return the resulting data
595
596 Args:
597 fname: DT source filename to use (e.g. 082_fdt_update_all.dts)
598
599 Returns:
600 Resulting image contents
601 """
602 return self._DoReadFileDtb(fname, use_real_dtb=True, update_dtb=True)[0]
603
Simon Glass72232452016-11-25 20:15:53 -0700604 def _DoReadFile(self, fname, use_real_dtb=False):
Simon Glass1e324002018-06-01 09:38:19 -0600605 """Helper function which discards the device-tree binary
606
607 Args:
Simon Glass511f6582018-10-01 12:22:30 -0600608 fname: Device-tree source filename to use (e.g. 005_simple.dts)
Simon Glass1e324002018-06-01 09:38:19 -0600609 use_real_dtb: True to use the test file as the contents of
610 the u-boot-dtb entry. Normally this is not needed and the
611 test contents (the U_BOOT_DTB_DATA string) can be used.
612 But in some test we need the real contents.
Simon Glassdef77b52018-07-17 13:25:27 -0600613
614 Returns:
615 Resulting image contents
Simon Glass1e324002018-06-01 09:38:19 -0600616 """
Simon Glass72232452016-11-25 20:15:53 -0700617 return self._DoReadFileDtb(fname, use_real_dtb)[0]
618
Simon Glass57454f42016-11-25 20:15:52 -0700619 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600620 def _MakeInputFile(cls, fname, contents):
Simon Glass57454f42016-11-25 20:15:52 -0700621 """Create a new test input file, creating directories as needed
622
623 Args:
Simon Glasse8561af2018-08-01 15:22:37 -0600624 fname: Filename to create
Simon Glass57454f42016-11-25 20:15:52 -0700625 contents: File contents to write in to the file
626 Returns:
627 Full pathname of file created
628 """
Simon Glass862f8e22019-08-24 07:22:43 -0600629 pathname = os.path.join(cls._indir, fname)
Simon Glass57454f42016-11-25 20:15:52 -0700630 dirname = os.path.dirname(pathname)
631 if dirname and not os.path.exists(dirname):
632 os.makedirs(dirname)
633 with open(pathname, 'wb') as fd:
634 fd.write(contents)
635 return pathname
636
637 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600638 def _MakeInputDir(cls, dirname):
Simon Glassc1ae83c2018-07-17 13:25:44 -0600639 """Create a new test input directory, creating directories as needed
640
641 Args:
642 dirname: Directory name to create
643
644 Returns:
645 Full pathname of directory created
646 """
Simon Glass862f8e22019-08-24 07:22:43 -0600647 pathname = os.path.join(cls._indir, dirname)
Simon Glassc1ae83c2018-07-17 13:25:44 -0600648 if not os.path.exists(pathname):
649 os.makedirs(pathname)
650 return pathname
651
652 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600653 def _SetupSplElf(cls, src_fname='bss_data'):
Simon Glass7057d022018-10-01 21:12:47 -0600654 """Set up an ELF file with a '_dt_ucode_base_size' symbol
655
656 Args:
657 Filename of ELF file to use as SPL
658 """
Simon Glass93a806f2019-08-24 07:22:59 -0600659 TestFunctional._MakeInputFile('spl/u-boot-spl',
Simon Glass80025522022-01-29 14:14:04 -0700660 tools.read_file(cls.ElfTestFile(src_fname)))
Simon Glass7057d022018-10-01 21:12:47 -0600661
662 @classmethod
Simon Glass3eb5b202019-08-24 07:23:00 -0600663 def _SetupTplElf(cls, src_fname='bss_data'):
664 """Set up an ELF file with a '_dt_ucode_base_size' symbol
665
666 Args:
667 Filename of ELF file to use as TPL
668 """
669 TestFunctional._MakeInputFile('tpl/u-boot-tpl',
Simon Glass80025522022-01-29 14:14:04 -0700670 tools.read_file(cls.ElfTestFile(src_fname)))
Simon Glass3eb5b202019-08-24 07:23:00 -0600671
672 @classmethod
Simon Glass56d05412022-02-28 07:16:54 -0700673 def _SetupVplElf(cls, src_fname='bss_data'):
674 """Set up an ELF file with a '_dt_ucode_base_size' symbol
675
676 Args:
677 Filename of ELF file to use as VPL
678 """
679 TestFunctional._MakeInputFile('vpl/u-boot-vpl',
680 tools.read_file(cls.ElfTestFile(src_fname)))
681
682 @classmethod
Lukas Funkee901faf2023-07-18 13:53:13 +0200683 def _SetupPmuFwlElf(cls, src_fname='bss_data'):
684 """Set up an ELF file with a '_dt_ucode_base_size' symbol
685
686 Args:
687 Filename of ELF file to use as VPL
688 """
689 TestFunctional._MakeInputFile('pmu-firmware.elf',
690 tools.read_file(cls.ElfTestFile(src_fname)))
691
692 @classmethod
Simon Glasse88cef92020-07-09 18:39:41 -0600693 def _SetupDescriptor(cls):
694 with open(cls.TestFile('descriptor.bin'), 'rb') as fd:
695 TestFunctional._MakeInputFile('descriptor.bin', fd.read())
696
697 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600698 def TestFile(cls, fname):
699 return os.path.join(cls._binman_dir, 'test', fname)
Simon Glass57454f42016-11-25 20:15:52 -0700700
Simon Glassf6290892019-08-24 07:22:53 -0600701 @classmethod
702 def ElfTestFile(cls, fname):
703 return os.path.join(cls._elf_testdir, fname)
704
Simon Glassad5cfe12023-01-07 14:07:14 -0700705 @classmethod
706 def make_tee_bin(cls, fname, paged_sz=0, extra_data=b''):
707 init_sz, start_hi, start_lo, dummy = (len(U_BOOT_DATA), 0, TEE_ADDR, 0)
708 data = b'OPTE\x01xxx' + struct.pack('<5I', init_sz, start_hi, start_lo,
709 dummy, paged_sz) + U_BOOT_DATA
710 data += extra_data
711 TestFunctional._MakeInputFile(fname, data)
712
Simon Glass57454f42016-11-25 20:15:52 -0700713 def AssertInList(self, grep_list, target):
714 """Assert that at least one of a list of things is in a target
715
716 Args:
717 grep_list: List of strings to check
718 target: Target string
719 """
720 for grep in grep_list:
721 if grep in target:
722 return
Simon Glass848cdb52019-05-17 22:00:50 -0600723 self.fail("Error: '%s' not found in '%s'" % (grep_list, target))
Simon Glass57454f42016-11-25 20:15:52 -0700724
725 def CheckNoGaps(self, entries):
726 """Check that all entries fit together without gaps
727
728 Args:
729 entries: List of entries to check
730 """
Simon Glasse8561af2018-08-01 15:22:37 -0600731 offset = 0
Simon Glass57454f42016-11-25 20:15:52 -0700732 for entry in entries.values():
Simon Glasse8561af2018-08-01 15:22:37 -0600733 self.assertEqual(offset, entry.offset)
734 offset += entry.size
Simon Glass57454f42016-11-25 20:15:52 -0700735
Simon Glass72232452016-11-25 20:15:53 -0700736 def GetFdtLen(self, dtb):
Simon Glass1e324002018-06-01 09:38:19 -0600737 """Get the totalsize field from a device-tree binary
Simon Glass72232452016-11-25 20:15:53 -0700738
739 Args:
Simon Glass1e324002018-06-01 09:38:19 -0600740 dtb: Device-tree binary contents
Simon Glass72232452016-11-25 20:15:53 -0700741
742 Returns:
Simon Glass1e324002018-06-01 09:38:19 -0600743 Total size of device-tree binary, from the header
Simon Glass72232452016-11-25 20:15:53 -0700744 """
745 return struct.unpack('>L', dtb[4:8])[0]
746
Simon Glass0f621332019-07-08 14:25:27 -0600747 def _GetPropTree(self, dtb, prop_names, prefix='/binman/'):
Simon Glassa87014e2018-07-06 10:27:42 -0600748 def AddNode(node, path):
749 if node.name != '/':
750 path += '/' + node.name
Simon Glass0f621332019-07-08 14:25:27 -0600751 for prop in node.props.values():
752 if prop.name in prop_names:
753 prop_path = path + ':' + prop.name
754 tree[prop_path[len(prefix):]] = fdt_util.fdt32_to_cpu(
755 prop.value)
Simon Glassa87014e2018-07-06 10:27:42 -0600756 for subnode in node.subnodes:
Simon Glassa87014e2018-07-06 10:27:42 -0600757 AddNode(subnode, path)
758
759 tree = {}
Simon Glassa87014e2018-07-06 10:27:42 -0600760 AddNode(dtb.GetRoot(), '')
761 return tree
762
Ivan Mikhaylov3cfcaa4d2023-03-08 01:13:40 +0000763 def _CheckSign(self, fit, key):
764 try:
765 tools.run('fit_check_sign', '-k', key, '-f', fit)
766 except:
767 self.fail('Expected signed FIT container')
768 return False
769 return True
770
Paul HENRYS5cf82892025-02-24 22:20:55 +0100771 def _CheckPreload(self, image, key, algo="sha256,rsa2048",
772 padding="pkcs-1.5"):
773 try:
774 tools.run('preload_check_sign', '-k', key, '-a', algo, '-p',
775 padding, '-f', image)
776 except:
777 self.fail('Expected image signed with a pre-load')
778 return False
779 return True
780
Simon Glass57454f42016-11-25 20:15:52 -0700781 def testRun(self):
782 """Test a basic run with valid args"""
783 result = self._RunBinman('-h')
784
785 def testFullHelp(self):
786 """Test that the full help is displayed with -H"""
787 result = self._RunBinman('-H')
Simon Glass75ead662021-03-18 20:25:13 +1300788 help_file = os.path.join(self._binman_dir, 'README.rst')
Tom Rinic3c0b6d2018-01-16 15:29:50 -0500789 # Remove possible extraneous strings
790 extra = '::::::::::::::\n' + help_file + '\n::::::::::::::\n'
791 gothelp = result.stdout.replace(extra, '')
792 self.assertEqual(len(gothelp), os.path.getsize(help_file))
Simon Glass57454f42016-11-25 20:15:52 -0700793 self.assertEqual(0, len(result.stderr))
794 self.assertEqual(0, result.return_code)
795
796 def testFullHelpInternal(self):
797 """Test that the full help is displayed with -H"""
798 try:
Simon Glass5dc22cf2025-02-03 09:26:42 -0700799 command.TEST_RESULT = command.CommandResult()
Simon Glass57454f42016-11-25 20:15:52 -0700800 result = self._DoBinman('-H')
Simon Glass75ead662021-03-18 20:25:13 +1300801 help_file = os.path.join(self._binman_dir, 'README.rst')
Simon Glass57454f42016-11-25 20:15:52 -0700802 finally:
Simon Glass5dc22cf2025-02-03 09:26:42 -0700803 command.TEST_RESULT = None
Simon Glass57454f42016-11-25 20:15:52 -0700804
805 def testHelp(self):
806 """Test that the basic help is displayed with -h"""
807 result = self._RunBinman('-h')
808 self.assertTrue(len(result.stdout) > 200)
809 self.assertEqual(0, len(result.stderr))
810 self.assertEqual(0, result.return_code)
811
Simon Glass57454f42016-11-25 20:15:52 -0700812 def testBoard(self):
813 """Test that we can run it with a specific board"""
Simon Glass511f6582018-10-01 12:22:30 -0600814 self._SetupDtb('005_simple.dts', 'sandbox/u-boot.dtb')
Simon Glass57454f42016-11-25 20:15:52 -0700815 TestFunctional._MakeInputFile('sandbox/u-boot.bin', U_BOOT_DATA)
Simon Glassed930672021-03-18 20:25:05 +1300816 result = self._DoBinman('build', '-n', '-b', 'sandbox')
Simon Glass57454f42016-11-25 20:15:52 -0700817 self.assertEqual(0, result)
818
819 def testNeedBoard(self):
820 """Test that we get an error when no board ius supplied"""
821 with self.assertRaises(ValueError) as e:
Simon Glassf46732a2019-07-08 14:25:29 -0600822 result = self._DoBinman('build')
Simon Glass57454f42016-11-25 20:15:52 -0700823 self.assertIn("Must provide a board to process (use -b <board>)",
824 str(e.exception))
825
826 def testMissingDt(self):
Simon Glass1e324002018-06-01 09:38:19 -0600827 """Test that an invalid device-tree file generates an error"""
Simon Glass57454f42016-11-25 20:15:52 -0700828 with self.assertRaises(Exception) as e:
Simon Glassf46732a2019-07-08 14:25:29 -0600829 self._RunBinman('build', '-d', 'missing_file')
Simon Glass57454f42016-11-25 20:15:52 -0700830 # We get one error from libfdt, and a different one from fdtget.
831 self.AssertInList(["Couldn't open blob from 'missing_file'",
832 'No such file or directory'], str(e.exception))
833
834 def testBrokenDt(self):
Simon Glass1e324002018-06-01 09:38:19 -0600835 """Test that an invalid device-tree source file generates an error
Simon Glass57454f42016-11-25 20:15:52 -0700836
837 Since this is a source file it should be compiled and the error
838 will come from the device-tree compiler (dtc).
839 """
840 with self.assertRaises(Exception) as e:
Simon Glassf46732a2019-07-08 14:25:29 -0600841 self._RunBinman('build', '-d', self.TestFile('001_invalid.dts'))
Simon Glass57454f42016-11-25 20:15:52 -0700842 self.assertIn("FATAL ERROR: Unable to parse input tree",
843 str(e.exception))
844
845 def testMissingNode(self):
846 """Test that a device tree without a 'binman' node generates an error"""
847 with self.assertRaises(Exception) as e:
Simon Glassf46732a2019-07-08 14:25:29 -0600848 self._DoBinman('build', '-d', self.TestFile('002_missing_node.dts'))
Simon Glass57454f42016-11-25 20:15:52 -0700849 self.assertIn("does not have a 'binman' node", str(e.exception))
850
851 def testEmpty(self):
852 """Test that an empty binman node works OK (i.e. does nothing)"""
Simon Glassf46732a2019-07-08 14:25:29 -0600853 result = self._RunBinman('build', '-d', self.TestFile('003_empty.dts'))
Simon Glass57454f42016-11-25 20:15:52 -0700854 self.assertEqual(0, len(result.stderr))
855 self.assertEqual(0, result.return_code)
856
857 def testInvalidEntry(self):
858 """Test that an invalid entry is flagged"""
859 with self.assertRaises(Exception) as e:
Simon Glassf46732a2019-07-08 14:25:29 -0600860 result = self._RunBinman('build', '-d',
Simon Glass511f6582018-10-01 12:22:30 -0600861 self.TestFile('004_invalid_entry.dts'))
Simon Glass57454f42016-11-25 20:15:52 -0700862 self.assertIn("Unknown entry type 'not-a-valid-type' in node "
863 "'/binman/not-a-valid-type'", str(e.exception))
864
865 def testSimple(self):
866 """Test a simple binman with a single file"""
Simon Glass511f6582018-10-01 12:22:30 -0600867 data = self._DoReadFile('005_simple.dts')
Simon Glass57454f42016-11-25 20:15:52 -0700868 self.assertEqual(U_BOOT_DATA, data)
869
Simon Glass075a45c2017-11-13 18:55:00 -0700870 def testSimpleDebug(self):
871 """Test a simple binman run with debugging enabled"""
Simon Glass52d06212019-07-08 14:25:53 -0600872 self._DoTestFile('005_simple.dts', debug=True)
Simon Glass075a45c2017-11-13 18:55:00 -0700873
Simon Glass57454f42016-11-25 20:15:52 -0700874 def testDual(self):
875 """Test that we can handle creating two images
876
877 This also tests image padding.
878 """
Simon Glass511f6582018-10-01 12:22:30 -0600879 retcode = self._DoTestFile('006_dual_image.dts')
Simon Glass57454f42016-11-25 20:15:52 -0700880 self.assertEqual(0, retcode)
881
882 image = control.images['image1']
Simon Glass39dd2152019-07-08 14:25:47 -0600883 self.assertEqual(len(U_BOOT_DATA), image.size)
Simon Glass80025522022-01-29 14:14:04 -0700884 fname = tools.get_output_filename('image1.bin')
Simon Glass57454f42016-11-25 20:15:52 -0700885 self.assertTrue(os.path.exists(fname))
Simon Glass33486662019-05-14 15:53:42 -0600886 with open(fname, 'rb') as fd:
Simon Glass57454f42016-11-25 20:15:52 -0700887 data = fd.read()
888 self.assertEqual(U_BOOT_DATA, data)
889
890 image = control.images['image2']
Simon Glass39dd2152019-07-08 14:25:47 -0600891 self.assertEqual(3 + len(U_BOOT_DATA) + 5, image.size)
Simon Glass80025522022-01-29 14:14:04 -0700892 fname = tools.get_output_filename('image2.bin')
Simon Glass57454f42016-11-25 20:15:52 -0700893 self.assertTrue(os.path.exists(fname))
Simon Glass33486662019-05-14 15:53:42 -0600894 with open(fname, 'rb') as fd:
Simon Glass57454f42016-11-25 20:15:52 -0700895 data = fd.read()
896 self.assertEqual(U_BOOT_DATA, data[3:7])
Simon Glass80025522022-01-29 14:14:04 -0700897 self.assertEqual(tools.get_bytes(0, 3), data[:3])
898 self.assertEqual(tools.get_bytes(0, 5), data[7:])
Simon Glass57454f42016-11-25 20:15:52 -0700899
900 def testBadAlign(self):
901 """Test that an invalid alignment value is detected"""
902 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -0600903 self._DoTestFile('007_bad_align.dts')
Simon Glass57454f42016-11-25 20:15:52 -0700904 self.assertIn("Node '/binman/u-boot': Alignment 23 must be a power "
905 "of two", str(e.exception))
906
907 def testPackSimple(self):
908 """Test that packing works as expected"""
Simon Glass511f6582018-10-01 12:22:30 -0600909 retcode = self._DoTestFile('008_pack.dts')
Simon Glass57454f42016-11-25 20:15:52 -0700910 self.assertEqual(0, retcode)
911 self.assertIn('image', control.images)
912 image = control.images['image']
Simon Glasseca32212018-06-01 09:38:12 -0600913 entries = image.GetEntries()
Simon Glass57454f42016-11-25 20:15:52 -0700914 self.assertEqual(5, len(entries))
915
916 # First u-boot
917 self.assertIn('u-boot', entries)
918 entry = entries['u-boot']
Simon Glasse8561af2018-08-01 15:22:37 -0600919 self.assertEqual(0, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700920 self.assertEqual(len(U_BOOT_DATA), entry.size)
921
922 # Second u-boot, aligned to 16-byte boundary
923 self.assertIn('u-boot-align', entries)
924 entry = entries['u-boot-align']
Simon Glasse8561af2018-08-01 15:22:37 -0600925 self.assertEqual(16, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700926 self.assertEqual(len(U_BOOT_DATA), entry.size)
927
928 # Third u-boot, size 23 bytes
929 self.assertIn('u-boot-size', entries)
930 entry = entries['u-boot-size']
Simon Glasse8561af2018-08-01 15:22:37 -0600931 self.assertEqual(20, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700932 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
933 self.assertEqual(23, entry.size)
934
935 # Fourth u-boot, placed immediate after the above
936 self.assertIn('u-boot-next', entries)
937 entry = entries['u-boot-next']
Simon Glasse8561af2018-08-01 15:22:37 -0600938 self.assertEqual(43, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700939 self.assertEqual(len(U_BOOT_DATA), entry.size)
940
Simon Glasse8561af2018-08-01 15:22:37 -0600941 # Fifth u-boot, placed at a fixed offset
Simon Glass57454f42016-11-25 20:15:52 -0700942 self.assertIn('u-boot-fixed', entries)
943 entry = entries['u-boot-fixed']
Simon Glasse8561af2018-08-01 15:22:37 -0600944 self.assertEqual(61, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700945 self.assertEqual(len(U_BOOT_DATA), entry.size)
946
Simon Glass39dd2152019-07-08 14:25:47 -0600947 self.assertEqual(65, image.size)
Simon Glass57454f42016-11-25 20:15:52 -0700948
949 def testPackExtra(self):
950 """Test that extra packing feature works as expected"""
Simon Glassafb9caa2020-10-26 17:40:10 -0600951 data, _, _, out_dtb_fname = self._DoReadFileDtb('009_pack_extra.dts',
952 update_dtb=True)
Simon Glass57454f42016-11-25 20:15:52 -0700953
Simon Glass57454f42016-11-25 20:15:52 -0700954 self.assertIn('image', control.images)
955 image = control.images['image']
Simon Glasseca32212018-06-01 09:38:12 -0600956 entries = image.GetEntries()
Samuel Hollande2574022023-01-21 17:25:16 -0600957 self.assertEqual(6, len(entries))
Simon Glass57454f42016-11-25 20:15:52 -0700958
Samuel Hollande2574022023-01-21 17:25:16 -0600959 # First u-boot with padding before and after (included in minimum size)
Simon Glass57454f42016-11-25 20:15:52 -0700960 self.assertIn('u-boot', entries)
961 entry = entries['u-boot']
Simon Glasse8561af2018-08-01 15:22:37 -0600962 self.assertEqual(0, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700963 self.assertEqual(3, entry.pad_before)
964 self.assertEqual(3 + 5 + len(U_BOOT_DATA), entry.size)
Simon Glass187202f2020-10-26 17:40:08 -0600965 self.assertEqual(U_BOOT_DATA, entry.data)
Simon Glass80025522022-01-29 14:14:04 -0700966 self.assertEqual(tools.get_bytes(0, 3) + U_BOOT_DATA +
967 tools.get_bytes(0, 5), data[:entry.size])
Simon Glass187202f2020-10-26 17:40:08 -0600968 pos = entry.size
Simon Glass57454f42016-11-25 20:15:52 -0700969
970 # Second u-boot has an aligned size, but it has no effect
971 self.assertIn('u-boot-align-size-nop', entries)
972 entry = entries['u-boot-align-size-nop']
Simon Glass187202f2020-10-26 17:40:08 -0600973 self.assertEqual(pos, entry.offset)
974 self.assertEqual(len(U_BOOT_DATA), entry.size)
975 self.assertEqual(U_BOOT_DATA, entry.data)
976 self.assertEqual(U_BOOT_DATA, data[pos:pos + entry.size])
977 pos += entry.size
Simon Glass57454f42016-11-25 20:15:52 -0700978
979 # Third u-boot has an aligned size too
980 self.assertIn('u-boot-align-size', entries)
981 entry = entries['u-boot-align-size']
Simon Glass187202f2020-10-26 17:40:08 -0600982 self.assertEqual(pos, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700983 self.assertEqual(32, entry.size)
Simon Glass187202f2020-10-26 17:40:08 -0600984 self.assertEqual(U_BOOT_DATA, entry.data)
Simon Glass80025522022-01-29 14:14:04 -0700985 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 32 - len(U_BOOT_DATA)),
Simon Glass187202f2020-10-26 17:40:08 -0600986 data[pos:pos + entry.size])
987 pos += entry.size
Simon Glass57454f42016-11-25 20:15:52 -0700988
989 # Fourth u-boot has an aligned end
990 self.assertIn('u-boot-align-end', entries)
991 entry = entries['u-boot-align-end']
Simon Glasse8561af2018-08-01 15:22:37 -0600992 self.assertEqual(48, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700993 self.assertEqual(16, entry.size)
Simon Glass187202f2020-10-26 17:40:08 -0600994 self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)])
Simon Glass80025522022-01-29 14:14:04 -0700995 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 16 - len(U_BOOT_DATA)),
Simon Glass187202f2020-10-26 17:40:08 -0600996 data[pos:pos + entry.size])
997 pos += entry.size
Simon Glass57454f42016-11-25 20:15:52 -0700998
999 # Fifth u-boot immediately afterwards
1000 self.assertIn('u-boot-align-both', entries)
1001 entry = entries['u-boot-align-both']
Simon Glasse8561af2018-08-01 15:22:37 -06001002 self.assertEqual(64, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -07001003 self.assertEqual(64, entry.size)
Simon Glass187202f2020-10-26 17:40:08 -06001004 self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)])
Simon Glass80025522022-01-29 14:14:04 -07001005 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 64 - len(U_BOOT_DATA)),
Simon Glass187202f2020-10-26 17:40:08 -06001006 data[pos:pos + entry.size])
Simon Glass57454f42016-11-25 20:15:52 -07001007
Samuel Hollande2574022023-01-21 17:25:16 -06001008 # Sixth u-boot with both minimum size and aligned size
1009 self.assertIn('u-boot-min-size', entries)
1010 entry = entries['u-boot-min-size']
1011 self.assertEqual(128, entry.offset)
1012 self.assertEqual(32, entry.size)
1013 self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)])
1014 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 32 - len(U_BOOT_DATA)),
1015 data[pos:pos + entry.size])
1016
Simon Glass57454f42016-11-25 20:15:52 -07001017 self.CheckNoGaps(entries)
Samuel Hollande2574022023-01-21 17:25:16 -06001018 self.assertEqual(160, image.size)
Simon Glass57454f42016-11-25 20:15:52 -07001019
Simon Glassafb9caa2020-10-26 17:40:10 -06001020 dtb = fdt.Fdt(out_dtb_fname)
1021 dtb.Scan()
1022 props = self._GetPropTree(dtb, ['size', 'offset', 'image-pos'])
1023 expected = {
1024 'image-pos': 0,
1025 'offset': 0,
Samuel Hollande2574022023-01-21 17:25:16 -06001026 'size': 160,
Simon Glassafb9caa2020-10-26 17:40:10 -06001027
1028 'u-boot:image-pos': 0,
1029 'u-boot:offset': 0,
1030 'u-boot:size': 3 + 5 + len(U_BOOT_DATA),
1031
1032 'u-boot-align-size-nop:image-pos': 12,
1033 'u-boot-align-size-nop:offset': 12,
1034 'u-boot-align-size-nop:size': 4,
1035
1036 'u-boot-align-size:image-pos': 16,
1037 'u-boot-align-size:offset': 16,
1038 'u-boot-align-size:size': 32,
1039
1040 'u-boot-align-end:image-pos': 48,
1041 'u-boot-align-end:offset': 48,
1042 'u-boot-align-end:size': 16,
1043
1044 'u-boot-align-both:image-pos': 64,
1045 'u-boot-align-both:offset': 64,
1046 'u-boot-align-both:size': 64,
Samuel Hollande2574022023-01-21 17:25:16 -06001047
1048 'u-boot-min-size:image-pos': 128,
1049 'u-boot-min-size:offset': 128,
1050 'u-boot-min-size:size': 32,
Simon Glassafb9caa2020-10-26 17:40:10 -06001051 }
1052 self.assertEqual(expected, props)
1053
Simon Glass57454f42016-11-25 20:15:52 -07001054 def testPackAlignPowerOf2(self):
1055 """Test that invalid entry alignment is detected"""
1056 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001057 self._DoTestFile('010_pack_align_power2.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001058 self.assertIn("Node '/binman/u-boot': Alignment 5 must be a power "
1059 "of two", str(e.exception))
1060
1061 def testPackAlignSizePowerOf2(self):
1062 """Test that invalid entry size alignment is detected"""
1063 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001064 self._DoTestFile('011_pack_align_size_power2.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001065 self.assertIn("Node '/binman/u-boot': Alignment size 55 must be a "
1066 "power of two", str(e.exception))
1067
1068 def testPackInvalidAlign(self):
Simon Glasse8561af2018-08-01 15:22:37 -06001069 """Test detection of an offset that does not match its alignment"""
Simon Glass57454f42016-11-25 20:15:52 -07001070 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001071 self._DoTestFile('012_pack_inv_align.dts')
Simon Glasse8561af2018-08-01 15:22:37 -06001072 self.assertIn("Node '/binman/u-boot': Offset 0x5 (5) does not match "
Simon Glass57454f42016-11-25 20:15:52 -07001073 "align 0x4 (4)", str(e.exception))
1074
1075 def testPackInvalidSizeAlign(self):
1076 """Test that invalid entry size alignment is detected"""
1077 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001078 self._DoTestFile('013_pack_inv_size_align.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001079 self.assertIn("Node '/binman/u-boot': Size 0x5 (5) does not match "
1080 "align-size 0x4 (4)", str(e.exception))
1081
1082 def testPackOverlap(self):
1083 """Test that overlapping regions are detected"""
1084 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001085 self._DoTestFile('014_pack_overlap.dts')
Simon Glasse8561af2018-08-01 15:22:37 -06001086 self.assertIn("Node '/binman/u-boot-align': Offset 0x3 (3) overlaps "
Simon Glass57454f42016-11-25 20:15:52 -07001087 "with previous entry '/binman/u-boot' ending at 0x4 (4)",
1088 str(e.exception))
1089
1090 def testPackEntryOverflow(self):
1091 """Test that entries that overflow their size are detected"""
1092 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001093 self._DoTestFile('015_pack_overflow.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001094 self.assertIn("Node '/binman/u-boot': Entry contents size is 0x4 (4) "
1095 "but entry size is 0x3 (3)", str(e.exception))
1096
1097 def testPackImageOverflow(self):
1098 """Test that entries which overflow the image size are detected"""
1099 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001100 self._DoTestFile('016_pack_image_overflow.dts')
Simon Glasseca32212018-06-01 09:38:12 -06001101 self.assertIn("Section '/binman': contents size 0x4 (4) exceeds section "
Simon Glass57454f42016-11-25 20:15:52 -07001102 "size 0x3 (3)", str(e.exception))
1103
1104 def testPackImageSize(self):
1105 """Test that the image size can be set"""
Simon Glass511f6582018-10-01 12:22:30 -06001106 retcode = self._DoTestFile('017_pack_image_size.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001107 self.assertEqual(0, retcode)
1108 self.assertIn('image', control.images)
1109 image = control.images['image']
Simon Glass39dd2152019-07-08 14:25:47 -06001110 self.assertEqual(7, image.size)
Simon Glass57454f42016-11-25 20:15:52 -07001111
1112 def testPackImageSizeAlign(self):
1113 """Test that image size alignemnt works as expected"""
Simon Glass511f6582018-10-01 12:22:30 -06001114 retcode = self._DoTestFile('018_pack_image_align.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001115 self.assertEqual(0, retcode)
1116 self.assertIn('image', control.images)
1117 image = control.images['image']
Simon Glass39dd2152019-07-08 14:25:47 -06001118 self.assertEqual(16, image.size)
Simon Glass57454f42016-11-25 20:15:52 -07001119
1120 def testPackInvalidImageAlign(self):
1121 """Test that invalid image alignment is detected"""
1122 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001123 self._DoTestFile('019_pack_inv_image_align.dts')
Simon Glasseca32212018-06-01 09:38:12 -06001124 self.assertIn("Section '/binman': Size 0x7 (7) does not match "
Simon Glass57454f42016-11-25 20:15:52 -07001125 "align-size 0x8 (8)", str(e.exception))
1126
Simon Glass2a0fa982022-02-11 13:23:21 -07001127 def testPackAlignPowerOf2Inv(self):
Simon Glass57454f42016-11-25 20:15:52 -07001128 """Test that invalid image alignment is detected"""
1129 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001130 self._DoTestFile('020_pack_inv_image_align_power2.dts')
Simon Glass39dd2152019-07-08 14:25:47 -06001131 self.assertIn("Image '/binman': Alignment size 131 must be a power of "
Simon Glass57454f42016-11-25 20:15:52 -07001132 "two", str(e.exception))
1133
1134 def testImagePadByte(self):
1135 """Test that the image pad byte can be specified"""
Simon Glass7057d022018-10-01 21:12:47 -06001136 self._SetupSplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001137 data = self._DoReadFile('021_image_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07001138 self.assertEqual(U_BOOT_SPL_DATA + tools.get_bytes(0xff, 1) +
Simon Glassac0d4952019-05-14 15:53:47 -06001139 U_BOOT_DATA, data)
Simon Glass57454f42016-11-25 20:15:52 -07001140
1141 def testImageName(self):
1142 """Test that image files can be named"""
Simon Glass511f6582018-10-01 12:22:30 -06001143 retcode = self._DoTestFile('022_image_name.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001144 self.assertEqual(0, retcode)
1145 image = control.images['image1']
Simon Glass80025522022-01-29 14:14:04 -07001146 fname = tools.get_output_filename('test-name')
Simon Glass57454f42016-11-25 20:15:52 -07001147 self.assertTrue(os.path.exists(fname))
1148
1149 image = control.images['image2']
Simon Glass80025522022-01-29 14:14:04 -07001150 fname = tools.get_output_filename('test-name.xx')
Simon Glass57454f42016-11-25 20:15:52 -07001151 self.assertTrue(os.path.exists(fname))
1152
1153 def testBlobFilename(self):
1154 """Test that generic blobs can be provided by filename"""
Simon Glass511f6582018-10-01 12:22:30 -06001155 data = self._DoReadFile('023_blob.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001156 self.assertEqual(BLOB_DATA, data)
1157
1158 def testPackSorted(self):
1159 """Test that entries can be sorted"""
Simon Glass7057d022018-10-01 21:12:47 -06001160 self._SetupSplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001161 data = self._DoReadFile('024_sorted.dts')
Simon Glass80025522022-01-29 14:14:04 -07001162 self.assertEqual(tools.get_bytes(0, 1) + U_BOOT_SPL_DATA +
1163 tools.get_bytes(0, 2) + U_BOOT_DATA, data)
Simon Glass57454f42016-11-25 20:15:52 -07001164
Simon Glasse8561af2018-08-01 15:22:37 -06001165 def testPackZeroOffset(self):
1166 """Test that an entry at offset 0 is not given a new offset"""
Marek Vasutf7413f02023-07-18 07:23:58 -06001167 self._SetupSplElf()
Simon Glass57454f42016-11-25 20:15:52 -07001168 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001169 self._DoTestFile('025_pack_zero_size.dts')
Simon Glasse8561af2018-08-01 15:22:37 -06001170 self.assertIn("Node '/binman/u-boot-spl': Offset 0x0 (0) overlaps "
Simon Glass57454f42016-11-25 20:15:52 -07001171 "with previous entry '/binman/u-boot' ending at 0x4 (4)",
1172 str(e.exception))
1173
1174 def testPackUbootDtb(self):
1175 """Test that a device tree can be added to U-Boot"""
Simon Glass511f6582018-10-01 12:22:30 -06001176 data = self._DoReadFile('026_pack_u_boot_dtb.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001177 self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA, data)
Simon Glass72232452016-11-25 20:15:53 -07001178
1179 def testPackX86RomNoSize(self):
1180 """Test that the end-at-4gb property requires a size property"""
Marek Vasutf7413f02023-07-18 07:23:58 -06001181 self._SetupSplElf()
Simon Glass72232452016-11-25 20:15:53 -07001182 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001183 self._DoTestFile('027_pack_4gb_no_size.dts')
Simon Glass39dd2152019-07-08 14:25:47 -06001184 self.assertIn("Image '/binman': Section size must be provided when "
Simon Glass72232452016-11-25 20:15:53 -07001185 "using end-at-4gb", str(e.exception))
1186
Jagdish Gediya0fb978c2018-09-03 21:35:07 +05301187 def test4gbAndSkipAtStartTogether(self):
1188 """Test that the end-at-4gb and skip-at-size property can't be used
1189 together"""
Marek Vasutf7413f02023-07-18 07:23:58 -06001190 self._SetupSplElf()
Jagdish Gediya0fb978c2018-09-03 21:35:07 +05301191 with self.assertRaises(ValueError) as e:
Simon Glass11f2bd02019-08-24 07:23:02 -06001192 self._DoTestFile('098_4gb_and_skip_at_start_together.dts')
Simon Glass39dd2152019-07-08 14:25:47 -06001193 self.assertIn("Image '/binman': Provide either 'end-at-4gb' or "
Jagdish Gediya0fb978c2018-09-03 21:35:07 +05301194 "'skip-at-start'", str(e.exception))
1195
Simon Glass72232452016-11-25 20:15:53 -07001196 def testPackX86RomOutside(self):
Simon Glasse8561af2018-08-01 15:22:37 -06001197 """Test that the end-at-4gb property checks for offset boundaries"""
Marek Vasutf7413f02023-07-18 07:23:58 -06001198 self._SetupSplElf()
Simon Glass72232452016-11-25 20:15:53 -07001199 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001200 self._DoTestFile('028_pack_4gb_outside.dts')
Simon Glassd6179862020-10-26 17:40:05 -06001201 self.assertIn("Node '/binman/u-boot': Offset 0x0 (0) size 0x4 (4) "
1202 "is outside the section '/binman' starting at "
1203 '0xffffffe0 (4294967264) of size 0x20 (32)',
Simon Glass72232452016-11-25 20:15:53 -07001204 str(e.exception))
1205
1206 def testPackX86Rom(self):
1207 """Test that a basic x86 ROM can be created"""
Simon Glass7057d022018-10-01 21:12:47 -06001208 self._SetupSplElf()
Simon Glass1d167762019-08-24 07:23:01 -06001209 data = self._DoReadFile('029_x86_rom.dts')
Simon Glass80025522022-01-29 14:14:04 -07001210 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 3) + U_BOOT_SPL_DATA +
1211 tools.get_bytes(0, 2), data)
Simon Glass72232452016-11-25 20:15:53 -07001212
1213 def testPackX86RomMeNoDesc(self):
1214 """Test that an invalid Intel descriptor entry is detected"""
Simon Glasse88cef92020-07-09 18:39:41 -06001215 try:
Simon Glass14c596c2020-07-25 15:11:19 -06001216 TestFunctional._MakeInputFile('descriptor-empty.bin', b'')
Simon Glasse88cef92020-07-09 18:39:41 -06001217 with self.assertRaises(ValueError) as e:
Simon Glass14c596c2020-07-25 15:11:19 -06001218 self._DoTestFile('163_x86_rom_me_empty.dts')
Simon Glasse88cef92020-07-09 18:39:41 -06001219 self.assertIn("Node '/binman/intel-descriptor': Cannot find Intel Flash Descriptor (FD) signature",
1220 str(e.exception))
1221 finally:
1222 self._SetupDescriptor()
Simon Glass72232452016-11-25 20:15:53 -07001223
1224 def testPackX86RomBadDesc(self):
1225 """Test that the Intel requires a descriptor entry"""
1226 with self.assertRaises(ValueError) as e:
Simon Glass1d167762019-08-24 07:23:01 -06001227 self._DoTestFile('030_x86_rom_me_no_desc.dts')
Simon Glasse8561af2018-08-01 15:22:37 -06001228 self.assertIn("Node '/binman/intel-me': No offset set with "
1229 "offset-unset: should another entry provide this correct "
1230 "offset?", str(e.exception))
Simon Glass72232452016-11-25 20:15:53 -07001231
1232 def testPackX86RomMe(self):
1233 """Test that an x86 ROM with an ME region can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001234 data = self._DoReadFile('031_x86_rom_me.dts')
Simon Glass80025522022-01-29 14:14:04 -07001235 expected_desc = tools.read_file(self.TestFile('descriptor.bin'))
Simon Glass759af872019-07-08 13:18:54 -06001236 if data[:0x1000] != expected_desc:
1237 self.fail('Expected descriptor binary at start of image')
Simon Glass72232452016-11-25 20:15:53 -07001238 self.assertEqual(ME_DATA, data[0x1000:0x1000 + len(ME_DATA)])
1239
1240 def testPackVga(self):
1241 """Test that an image with a VGA binary can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001242 data = self._DoReadFile('032_intel_vga.dts')
Simon Glass72232452016-11-25 20:15:53 -07001243 self.assertEqual(VGA_DATA, data[:len(VGA_DATA)])
1244
1245 def testPackStart16(self):
1246 """Test that an image with an x86 start16 region can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001247 data = self._DoReadFile('033_x86_start16.dts')
Simon Glass72232452016-11-25 20:15:53 -07001248 self.assertEqual(X86_START16_DATA, data[:len(X86_START16_DATA)])
1249
Jagdish Gediya311d4842018-09-03 21:35:08 +05301250 def testPackPowerpcMpc85xxBootpgResetvec(self):
1251 """Test that an image with powerpc-mpc85xx-bootpg-resetvec can be
1252 created"""
Simon Glass11f2bd02019-08-24 07:23:02 -06001253 data = self._DoReadFile('150_powerpc_mpc85xx_bootpg_resetvec.dts')
Jagdish Gediya311d4842018-09-03 21:35:08 +05301254 self.assertEqual(PPC_MPC85XX_BR_DATA, data[:len(PPC_MPC85XX_BR_DATA)])
1255
Simon Glass6ba679c2018-07-06 10:27:17 -06001256 def _RunMicrocodeTest(self, dts_fname, nodtb_data, ucode_second=False):
Simon Glass820af1d2018-07-06 10:27:16 -06001257 """Handle running a test for insertion of microcode
1258
1259 Args:
1260 dts_fname: Name of test .dts file
1261 nodtb_data: Data that we expect in the first section
Simon Glass6ba679c2018-07-06 10:27:17 -06001262 ucode_second: True if the microsecond entry is second instead of
1263 third
Simon Glass820af1d2018-07-06 10:27:16 -06001264
1265 Returns:
1266 Tuple:
1267 Contents of first region (U-Boot or SPL)
Simon Glasse8561af2018-08-01 15:22:37 -06001268 Offset and size components of microcode pointer, as inserted
Simon Glass820af1d2018-07-06 10:27:16 -06001269 in the above (two 4-byte words)
1270 """
Simon Glass3d274232017-11-12 21:52:27 -07001271 data = self._DoReadFile(dts_fname, True)
Simon Glass72232452016-11-25 20:15:53 -07001272
1273 # Now check the device tree has no microcode
Simon Glass6ba679c2018-07-06 10:27:17 -06001274 if ucode_second:
1275 ucode_content = data[len(nodtb_data):]
1276 ucode_pos = len(nodtb_data)
1277 dtb_with_ucode = ucode_content[16:]
1278 fdt_len = self.GetFdtLen(dtb_with_ucode)
1279 else:
1280 dtb_with_ucode = data[len(nodtb_data):]
1281 fdt_len = self.GetFdtLen(dtb_with_ucode)
1282 ucode_content = dtb_with_ucode[fdt_len:]
1283 ucode_pos = len(nodtb_data) + fdt_len
Simon Glass80025522022-01-29 14:14:04 -07001284 fname = tools.get_output_filename('test.dtb')
Simon Glass72232452016-11-25 20:15:53 -07001285 with open(fname, 'wb') as fd:
Simon Glass820af1d2018-07-06 10:27:16 -06001286 fd.write(dtb_with_ucode)
Simon Glass22c92ca2017-05-27 07:38:29 -06001287 dtb = fdt.FdtScan(fname)
1288 ucode = dtb.GetNode('/microcode')
Simon Glass72232452016-11-25 20:15:53 -07001289 self.assertTrue(ucode)
1290 for node in ucode.subnodes:
1291 self.assertFalse(node.props.get('data'))
1292
Simon Glass72232452016-11-25 20:15:53 -07001293 # Check that the microcode appears immediately after the Fdt
1294 # This matches the concatenation of the data properties in
Simon Glasse83679d2017-11-12 21:52:26 -07001295 # the /microcode/update@xxx nodes in 34_x86_ucode.dts.
Simon Glass72232452016-11-25 20:15:53 -07001296 ucode_data = struct.pack('>4L', 0x12345678, 0x12345679, 0xabcd0000,
1297 0x78235609)
Simon Glass820af1d2018-07-06 10:27:16 -06001298 self.assertEqual(ucode_data, ucode_content[:len(ucode_data)])
Simon Glass72232452016-11-25 20:15:53 -07001299
1300 # Check that the microcode pointer was inserted. It should match the
Simon Glasse8561af2018-08-01 15:22:37 -06001301 # expected offset and size
Simon Glass72232452016-11-25 20:15:53 -07001302 pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
1303 len(ucode_data))
Simon Glass6ba679c2018-07-06 10:27:17 -06001304 u_boot = data[:len(nodtb_data)]
1305 return u_boot, pos_and_size
Simon Glass3d274232017-11-12 21:52:27 -07001306
1307 def testPackUbootMicrocode(self):
1308 """Test that x86 microcode can be handled correctly
1309
1310 We expect to see the following in the image, in order:
1311 u-boot-nodtb.bin with a microcode pointer inserted at the correct
1312 place
1313 u-boot.dtb with the microcode removed
1314 the microcode
1315 """
Simon Glass511f6582018-10-01 12:22:30 -06001316 first, pos_and_size = self._RunMicrocodeTest('034_x86_ucode.dts',
Simon Glass3d274232017-11-12 21:52:27 -07001317 U_BOOT_NODTB_DATA)
Simon Glass303f62f2019-05-17 22:00:46 -06001318 self.assertEqual(b'nodtb with microcode' + pos_and_size +
1319 b' somewhere in here', first)
Simon Glass72232452016-11-25 20:15:53 -07001320
Simon Glassbac25c82017-05-27 07:38:26 -06001321 def _RunPackUbootSingleMicrocode(self):
Simon Glass72232452016-11-25 20:15:53 -07001322 """Test that x86 microcode can be handled correctly
1323
1324 We expect to see the following in the image, in order:
1325 u-boot-nodtb.bin with a microcode pointer inserted at the correct
1326 place
1327 u-boot.dtb with the microcode
1328 an empty microcode region
1329 """
1330 # We need the libfdt library to run this test since only that allows
1331 # finding the offset of a property. This is required by
1332 # Entry_u_boot_dtb_with_ucode.ObtainContents().
Simon Glass511f6582018-10-01 12:22:30 -06001333 data = self._DoReadFile('035_x86_single_ucode.dts', True)
Simon Glass72232452016-11-25 20:15:53 -07001334
1335 second = data[len(U_BOOT_NODTB_DATA):]
1336
1337 fdt_len = self.GetFdtLen(second)
1338 third = second[fdt_len:]
1339 second = second[:fdt_len]
1340
Simon Glassbac25c82017-05-27 07:38:26 -06001341 ucode_data = struct.pack('>2L', 0x12345678, 0x12345679)
1342 self.assertIn(ucode_data, second)
1343 ucode_pos = second.find(ucode_data) + len(U_BOOT_NODTB_DATA)
Simon Glass72232452016-11-25 20:15:53 -07001344
Simon Glassbac25c82017-05-27 07:38:26 -06001345 # Check that the microcode pointer was inserted. It should match the
Simon Glasse8561af2018-08-01 15:22:37 -06001346 # expected offset and size
Simon Glassbac25c82017-05-27 07:38:26 -06001347 pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
1348 len(ucode_data))
1349 first = data[:len(U_BOOT_NODTB_DATA)]
Simon Glass303f62f2019-05-17 22:00:46 -06001350 self.assertEqual(b'nodtb with microcode' + pos_and_size +
1351 b' somewhere in here', first)
Simon Glass996021e2016-11-25 20:15:54 -07001352
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001353 def testPackUbootSingleMicrocode(self):
1354 """Test that x86 microcode can be handled correctly with fdt_normal.
1355 """
Simon Glassbac25c82017-05-27 07:38:26 -06001356 self._RunPackUbootSingleMicrocode()
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001357
Simon Glass996021e2016-11-25 20:15:54 -07001358 def testUBootImg(self):
1359 """Test that u-boot.img can be put in a file"""
Simon Glass511f6582018-10-01 12:22:30 -06001360 data = self._DoReadFile('036_u_boot_img.dts')
Simon Glass996021e2016-11-25 20:15:54 -07001361 self.assertEqual(U_BOOT_IMG_DATA, data)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001362
1363 def testNoMicrocode(self):
1364 """Test that a missing microcode region is detected"""
1365 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001366 self._DoReadFile('037_x86_no_ucode.dts', True)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001367 self.assertIn("Node '/binman/u-boot-dtb-with-ucode': No /microcode "
1368 "node found in ", str(e.exception))
1369
1370 def testMicrocodeWithoutNode(self):
1371 """Test that a missing u-boot-dtb-with-ucode node is detected"""
1372 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001373 self._DoReadFile('038_x86_ucode_missing_node.dts', True)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001374 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
1375 "microcode region u-boot-dtb-with-ucode", str(e.exception))
1376
1377 def testMicrocodeWithoutNode2(self):
1378 """Test that a missing u-boot-ucode node is detected"""
1379 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001380 self._DoReadFile('039_x86_ucode_missing_node2.dts', True)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001381 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
1382 "microcode region u-boot-ucode", str(e.exception))
1383
1384 def testMicrocodeWithoutPtrInElf(self):
1385 """Test that a U-Boot binary without the microcode symbol is detected"""
1386 # ELF file without a '_dt_ucode_base_size' symbol
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001387 try:
Simon Glassfaaaa162019-08-24 07:22:55 -06001388 TestFunctional._MakeInputFile('u-boot',
Simon Glass80025522022-01-29 14:14:04 -07001389 tools.read_file(self.ElfTestFile('u_boot_no_ucode_ptr')))
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001390
1391 with self.assertRaises(ValueError) as e:
Simon Glassbac25c82017-05-27 07:38:26 -06001392 self._RunPackUbootSingleMicrocode()
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001393 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot locate "
1394 "_dt_ucode_base_size symbol in u-boot", str(e.exception))
1395
1396 finally:
1397 # Put the original file back
Simon Glass4affd4b2019-08-24 07:22:54 -06001398 TestFunctional._MakeInputFile('u-boot',
Simon Glass80025522022-01-29 14:14:04 -07001399 tools.read_file(self.ElfTestFile('u_boot_ucode_ptr')))
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001400
1401 def testMicrocodeNotInImage(self):
1402 """Test that microcode must be placed within the image"""
1403 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001404 self._DoReadFile('040_x86_ucode_not_in_image.dts', True)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001405 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Microcode "
1406 "pointer _dt_ucode_base_size at fffffe14 is outside the "
Simon Glassad5a7712018-06-01 09:38:14 -06001407 "section ranging from 00000000 to 0000002e", str(e.exception))
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001408
1409 def testWithoutMicrocode(self):
1410 """Test that we can cope with an image without microcode (e.g. qemu)"""
Simon Glassfaaaa162019-08-24 07:22:55 -06001411 TestFunctional._MakeInputFile('u-boot',
Simon Glass80025522022-01-29 14:14:04 -07001412 tools.read_file(self.ElfTestFile('u_boot_no_ucode_ptr')))
Simon Glass511f6582018-10-01 12:22:30 -06001413 data, dtb, _, _ = self._DoReadFileDtb('044_x86_optional_ucode.dts', True)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001414
1415 # Now check the device tree has no microcode
1416 self.assertEqual(U_BOOT_NODTB_DATA, data[:len(U_BOOT_NODTB_DATA)])
1417 second = data[len(U_BOOT_NODTB_DATA):]
1418
1419 fdt_len = self.GetFdtLen(second)
1420 self.assertEqual(dtb, second[:fdt_len])
1421
1422 used_len = len(U_BOOT_NODTB_DATA) + fdt_len
1423 third = data[used_len:]
Simon Glass80025522022-01-29 14:14:04 -07001424 self.assertEqual(tools.get_bytes(0, 0x200 - used_len), third)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001425
1426 def testUnknownPosSize(self):
1427 """Test that microcode must be placed within the image"""
1428 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001429 self._DoReadFile('041_unknown_pos_size.dts', True)
Simon Glasse8561af2018-08-01 15:22:37 -06001430 self.assertIn("Section '/binman': Unable to set offset/size for unknown "
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001431 "entry 'invalid-entry'", str(e.exception))
Simon Glassb4176d42016-11-25 20:15:56 -07001432
1433 def testPackFsp(self):
1434 """Test that an image with a FSP binary can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001435 data = self._DoReadFile('042_intel_fsp.dts')
Simon Glassb4176d42016-11-25 20:15:56 -07001436 self.assertEqual(FSP_DATA, data[:len(FSP_DATA)])
1437
1438 def testPackCmc(self):
Bin Mengd7bcdf52017-08-15 22:41:54 -07001439 """Test that an image with a CMC binary can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001440 data = self._DoReadFile('043_intel_cmc.dts')
Simon Glassb4176d42016-11-25 20:15:56 -07001441 self.assertEqual(CMC_DATA, data[:len(CMC_DATA)])
Bin Mengd7bcdf52017-08-15 22:41:54 -07001442
1443 def testPackVbt(self):
1444 """Test that an image with a VBT binary can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001445 data = self._DoReadFile('046_intel_vbt.dts')
Bin Mengd7bcdf52017-08-15 22:41:54 -07001446 self.assertEqual(VBT_DATA, data[:len(VBT_DATA)])
Simon Glassac599912017-11-12 21:52:22 -07001447
Simon Glass7f94e832017-11-12 21:52:25 -07001448 def testSplBssPad(self):
1449 """Test that we can pad SPL's BSS with zeros"""
Simon Glass3d274232017-11-12 21:52:27 -07001450 # ELF file with a '__bss_size' symbol
Simon Glass7057d022018-10-01 21:12:47 -06001451 self._SetupSplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001452 data = self._DoReadFile('047_spl_bss_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07001453 self.assertEqual(U_BOOT_SPL_DATA + tools.get_bytes(0, 10) + U_BOOT_DATA,
Simon Glassac0d4952019-05-14 15:53:47 -06001454 data)
Simon Glass7f94e832017-11-12 21:52:25 -07001455
Simon Glass04cda032018-10-01 21:12:42 -06001456 def testSplBssPadMissing(self):
1457 """Test that a missing symbol is detected"""
Simon Glass7057d022018-10-01 21:12:47 -06001458 self._SetupSplElf('u_boot_ucode_ptr')
Simon Glass24ad3652017-11-13 18:54:54 -07001459 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001460 self._DoReadFile('047_spl_bss_pad.dts')
Simon Glass24ad3652017-11-13 18:54:54 -07001461 self.assertIn('Expected __bss_size symbol in spl/u-boot-spl',
1462 str(e.exception))
1463
Simon Glasse83679d2017-11-12 21:52:26 -07001464 def testPackStart16Spl(self):
Simon Glassed40e962018-09-14 04:57:10 -06001465 """Test that an image with an x86 start16 SPL region can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001466 data = self._DoReadFile('048_x86_start16_spl.dts')
Simon Glasse83679d2017-11-12 21:52:26 -07001467 self.assertEqual(X86_START16_SPL_DATA, data[:len(X86_START16_SPL_DATA)])
1468
Simon Glass6ba679c2018-07-06 10:27:17 -06001469 def _PackUbootSplMicrocode(self, dts, ucode_second=False):
1470 """Helper function for microcode tests
Simon Glass3d274232017-11-12 21:52:27 -07001471
1472 We expect to see the following in the image, in order:
1473 u-boot-spl-nodtb.bin with a microcode pointer inserted at the
1474 correct place
1475 u-boot.dtb with the microcode removed
1476 the microcode
Simon Glass6ba679c2018-07-06 10:27:17 -06001477
1478 Args:
1479 dts: Device tree file to use for test
1480 ucode_second: True if the microsecond entry is second instead of
1481 third
Simon Glass3d274232017-11-12 21:52:27 -07001482 """
Simon Glass7057d022018-10-01 21:12:47 -06001483 self._SetupSplElf('u_boot_ucode_ptr')
Simon Glass6ba679c2018-07-06 10:27:17 -06001484 first, pos_and_size = self._RunMicrocodeTest(dts, U_BOOT_SPL_NODTB_DATA,
1485 ucode_second=ucode_second)
Simon Glass303f62f2019-05-17 22:00:46 -06001486 self.assertEqual(b'splnodtb with microc' + pos_and_size +
1487 b'ter somewhere in here', first)
Simon Glass3d274232017-11-12 21:52:27 -07001488
Simon Glass6ba679c2018-07-06 10:27:17 -06001489 def testPackUbootSplMicrocode(self):
1490 """Test that x86 microcode can be handled correctly in SPL"""
Marek Vasutf7413f02023-07-18 07:23:58 -06001491 self._SetupSplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001492 self._PackUbootSplMicrocode('049_x86_ucode_spl.dts')
Simon Glass6ba679c2018-07-06 10:27:17 -06001493
1494 def testPackUbootSplMicrocodeReorder(self):
1495 """Test that order doesn't matter for microcode entries
1496
1497 This is the same as testPackUbootSplMicrocode but when we process the
1498 u-boot-ucode entry we have not yet seen the u-boot-dtb-with-ucode
1499 entry, so we reply on binman to try later.
1500 """
Simon Glass511f6582018-10-01 12:22:30 -06001501 self._PackUbootSplMicrocode('058_x86_ucode_spl_needs_retry.dts',
Simon Glass6ba679c2018-07-06 10:27:17 -06001502 ucode_second=True)
1503
Simon Glassa409c932017-11-12 21:52:28 -07001504 def testPackMrc(self):
1505 """Test that an image with an MRC binary can be created"""
Simon Glass511f6582018-10-01 12:22:30 -06001506 data = self._DoReadFile('050_intel_mrc.dts')
Simon Glassa409c932017-11-12 21:52:28 -07001507 self.assertEqual(MRC_DATA, data[:len(MRC_DATA)])
1508
Simon Glass9aa6a6f2017-11-13 18:54:55 -07001509 def testSplDtb(self):
1510 """Test that an image with spl/u-boot-spl.dtb can be created"""
Marek Vasutf7413f02023-07-18 07:23:58 -06001511 self._SetupSplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001512 data = self._DoReadFile('051_u_boot_spl_dtb.dts')
Simon Glass9aa6a6f2017-11-13 18:54:55 -07001513 self.assertEqual(U_BOOT_SPL_DTB_DATA, data[:len(U_BOOT_SPL_DTB_DATA)])
1514
Simon Glass0a6da312017-11-13 18:54:56 -07001515 def testSplNoDtb(self):
1516 """Test that an image with spl/u-boot-spl-nodtb.bin can be created"""
Simon Glass13089cc2021-04-25 08:39:32 +12001517 self._SetupSplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001518 data = self._DoReadFile('052_u_boot_spl_nodtb.dts')
Simon Glass0a6da312017-11-13 18:54:56 -07001519 self.assertEqual(U_BOOT_SPL_NODTB_DATA, data[:len(U_BOOT_SPL_NODTB_DATA)])
1520
Simon Glass7098b7f2021-03-21 18:24:30 +13001521 def checkSymbols(self, dts, base_data, u_boot_offset, entry_args=None,
Simon Glass4b0f4142024-08-26 13:11:40 -06001522 use_expanded=False, no_write_symbols=False,
1523 symbols_base=None):
Simon Glass31e04cb2021-03-18 20:24:56 +13001524 """Check the image contains the expected symbol values
1525
1526 Args:
1527 dts: Device tree file to use for test
1528 base_data: Data before and after 'u-boot' section
Simon Glass3eb30a42024-08-26 13:11:42 -06001529 u_boot_offset (int): Offset of 'u-boot' section in image, or None if
1530 the offset not available due to it being in a compressed section
Simon Glass7098b7f2021-03-21 18:24:30 +13001531 entry_args: Dict of entry args to supply to binman
1532 key: arg name
1533 value: value of that arg
1534 use_expanded: True to use expanded entries where available, e.g.
1535 'u-boot-expanded' instead of 'u-boot'
Simon Glass4b0f4142024-08-26 13:11:40 -06001536 symbols_base (int): Value to expect for symbols-base in u-boot-spl,
1537 None if none
Simon Glass31e04cb2021-03-18 20:24:56 +13001538 """
Simon Glass5d0c0262019-08-24 07:22:56 -06001539 elf_fname = self.ElfTestFile('u_boot_binman_syms')
Simon Glass4ca8e042017-11-13 18:55:01 -07001540 syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
1541 addr = elf.GetSymbolAddress(elf_fname, '__image_copy_start')
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03001542 self.assertEqual(syms['_binman_sym_magic'].address, addr)
Simon Glass31e04cb2021-03-18 20:24:56 +13001543 self.assertEqual(syms['_binman_u_boot_spl_any_prop_offset'].address,
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03001544 addr + 4)
Simon Glass4ca8e042017-11-13 18:55:01 -07001545
Simon Glass7057d022018-10-01 21:12:47 -06001546 self._SetupSplElf('u_boot_binman_syms')
Simon Glass7098b7f2021-03-21 18:24:30 +13001547 data = self._DoReadFileDtb(dts, entry_args=entry_args,
Simon Glass3eb30a42024-08-26 13:11:42 -06001548 use_expanded=use_expanded,
1549 verbosity=None if u_boot_offset else 3)[0]
1550
1551 # The lz4-compressed version of the U-Boot data is 19 bytes long
1552 comp_uboot_len = 19
1553
Simon Glass31e04cb2021-03-18 20:24:56 +13001554 # The image should contain the symbols from u_boot_binman_syms.c
1555 # Note that image_pos is adjusted by the base address of the image,
1556 # which is 0x10 in our test image
Simon Glass3eb30a42024-08-26 13:11:42 -06001557 # If u_boot_offset is None, Binman should write -1U into the image
Simon Glass4b0f4142024-08-26 13:11:40 -06001558 vals2 = (elf.BINMAN_SYM_MAGIC_VALUE, 0x00,
Simon Glass3eb30a42024-08-26 13:11:42 -06001559 u_boot_offset + len(U_BOOT_DATA) if u_boot_offset else
1560 len(U_BOOT_SPL_DATA) + 1 + comp_uboot_len,
1561 0x10 + u_boot_offset if u_boot_offset else 0xffffffff, 0x04)
Simon Glass4b0f4142024-08-26 13:11:40 -06001562
1563 # u-boot-spl has a symbols-base property, so take that into account if
1564 # required. The caller must supply the value
1565 vals = list(vals2)
1566 if symbols_base is not None:
1567 vals[3] = symbols_base + u_boot_offset
1568 vals = tuple(vals)
1569
Simon Glass4b4049e2024-08-26 13:11:39 -06001570 sym_values = struct.pack('<LLQLL', *vals)
Simon Glass4b0f4142024-08-26 13:11:40 -06001571 sym_values2 = struct.pack('<LLQLL', *vals2)
Simon Glass4abf7842023-07-18 07:23:54 -06001572 if no_write_symbols:
Simon Glass4b4049e2024-08-26 13:11:39 -06001573 self.assertEqual(
1574 base_data +
1575 tools.get_bytes(0xff, 0x38 - len(base_data)) +
1576 U_BOOT_DATA + base_data, data)
Simon Glass4abf7842023-07-18 07:23:54 -06001577 else:
Simon Glass4b4049e2024-08-26 13:11:39 -06001578 got_vals = struct.unpack('<LLQLL', data[:24])
1579
1580 # For debugging:
1581 #print('expect:', list(f'{v:x}' for v in vals))
1582 #print(' got:', list(f'{v:x}' for v in got_vals))
1583
1584 self.assertEqual(vals, got_vals)
1585 self.assertEqual(sym_values, data[:24])
1586
1587 blen = len(base_data)
1588 self.assertEqual(base_data[24:], data[24:blen])
1589 self.assertEqual(0xff, data[blen])
1590
Simon Glass3eb30a42024-08-26 13:11:42 -06001591 if u_boot_offset:
1592 ofs = blen + 1 + len(U_BOOT_DATA)
1593 self.assertEqual(U_BOOT_DATA, data[blen + 1:ofs])
1594 else:
1595 ofs = blen + 1 + comp_uboot_len
Simon Glass4b4049e2024-08-26 13:11:39 -06001596
Simon Glass4b0f4142024-08-26 13:11:40 -06001597 self.assertEqual(sym_values2, data[ofs:ofs + 24])
Simon Glass4b4049e2024-08-26 13:11:39 -06001598 self.assertEqual(base_data[24:], data[ofs + 24:])
1599
1600 # Just repeating the above asserts all at once, for clarity
Simon Glass3eb30a42024-08-26 13:11:42 -06001601 if u_boot_offset:
1602 expected = (sym_values + base_data[24:] +
1603 tools.get_bytes(0xff, 1) + U_BOOT_DATA +
1604 sym_values2 + base_data[24:])
1605 self.assertEqual(expected, data)
Simon Glass4ca8e042017-11-13 18:55:01 -07001606
Simon Glass31e04cb2021-03-18 20:24:56 +13001607 def testSymbols(self):
1608 """Test binman can assign symbols embedded in U-Boot"""
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03001609 self.checkSymbols('053_symbols.dts', U_BOOT_SPL_DATA, 0x1c)
Simon Glass31e04cb2021-03-18 20:24:56 +13001610
1611 def testSymbolsNoDtb(self):
1612 """Test binman can assign symbols embedded in U-Boot SPL"""
Simon Glass3bbc9932021-03-21 18:24:29 +13001613 self.checkSymbols('196_symbols_nodtb.dts',
Simon Glass31e04cb2021-03-18 20:24:56 +13001614 U_BOOT_SPL_NODTB_DATA + U_BOOT_SPL_DTB_DATA,
1615 0x38)
1616
Simon Glasse76a3e62018-06-01 09:38:11 -06001617 def testPackUnitAddress(self):
1618 """Test that we support multiple binaries with the same name"""
Simon Glass511f6582018-10-01 12:22:30 -06001619 data = self._DoReadFile('054_unit_address.dts')
Simon Glasse76a3e62018-06-01 09:38:11 -06001620 self.assertEqual(U_BOOT_DATA + U_BOOT_DATA, data)
1621
Simon Glassa91e1152018-06-01 09:38:16 -06001622 def testSections(self):
1623 """Basic test of sections"""
Simon Glass511f6582018-10-01 12:22:30 -06001624 data = self._DoReadFile('055_sections.dts')
Simon Glass80025522022-01-29 14:14:04 -07001625 expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) +
1626 U_BOOT_DATA + tools.get_bytes(ord('a'), 12) +
1627 U_BOOT_DATA + tools.get_bytes(ord('&'), 4))
Simon Glassa91e1152018-06-01 09:38:16 -06001628 self.assertEqual(expected, data)
Simon Glassac599912017-11-12 21:52:22 -07001629
Simon Glass30732662018-06-01 09:38:20 -06001630 def testMap(self):
1631 """Tests outputting a map of the images"""
Simon Glass511f6582018-10-01 12:22:30 -06001632 _, _, map_data, _ = self._DoReadFileDtb('055_sections.dts', map=True)
Simon Glass7eca7922018-07-17 13:25:49 -06001633 self.assertEqual('''ImagePos Offset Size Name
Simon Glass49cd2b32023-02-07 14:34:18 -0700163400000000 00000000 00000028 image
Simon Glass7eca7922018-07-17 13:25:49 -0600163500000000 00000000 00000010 section@0
163600000000 00000000 00000004 u-boot
163700000010 00000010 00000010 section@1
163800000010 00000000 00000004 u-boot
163900000020 00000020 00000004 section@2
164000000020 00000000 00000004 u-boot
Simon Glass30732662018-06-01 09:38:20 -06001641''', map_data)
1642
Simon Glass3b78d532018-06-01 09:38:21 -06001643 def testNamePrefix(self):
1644 """Tests that name prefixes are used"""
Simon Glass511f6582018-10-01 12:22:30 -06001645 _, _, map_data, _ = self._DoReadFileDtb('056_name_prefix.dts', map=True)
Simon Glass7eca7922018-07-17 13:25:49 -06001646 self.assertEqual('''ImagePos Offset Size Name
Simon Glass49cd2b32023-02-07 14:34:18 -0700164700000000 00000000 00000028 image
Simon Glass7eca7922018-07-17 13:25:49 -0600164800000000 00000000 00000010 section@0
164900000000 00000000 00000004 ro-u-boot
165000000010 00000010 00000010 section@1
165100000010 00000000 00000004 rw-u-boot
Simon Glass3b78d532018-06-01 09:38:21 -06001652''', map_data)
1653
Simon Glass6ba679c2018-07-06 10:27:17 -06001654 def testUnknownContents(self):
1655 """Test that obtaining the contents works as expected"""
1656 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001657 self._DoReadFile('057_unknown_contents.dts', True)
Simon Glass39dd2152019-07-08 14:25:47 -06001658 self.assertIn("Image '/binman': Internal error: Could not complete "
Simon Glassc585dd42020-04-17 18:09:03 -06001659 "processing of contents: remaining ["
1660 "<binman.etype._testing.Entry__testing ", str(e.exception))
Simon Glass6ba679c2018-07-06 10:27:17 -06001661
Simon Glass2e1169f2018-07-06 10:27:19 -06001662 def testBadChangeSize(self):
1663 """Test that trying to change the size of an entry fails"""
Simon Glasse61b6f62019-07-08 14:25:37 -06001664 try:
1665 state.SetAllowEntryExpansion(False)
1666 with self.assertRaises(ValueError) as e:
1667 self._DoReadFile('059_change_size.dts', True)
Simon Glass8c702fb2019-07-20 12:23:57 -06001668 self.assertIn("Node '/binman/_testing': Cannot update entry size from 2 to 3",
Simon Glasse61b6f62019-07-08 14:25:37 -06001669 str(e.exception))
1670 finally:
1671 state.SetAllowEntryExpansion(True)
Simon Glass2e1169f2018-07-06 10:27:19 -06001672
Simon Glassa87014e2018-07-06 10:27:42 -06001673 def testUpdateFdt(self):
Simon Glasse8561af2018-08-01 15:22:37 -06001674 """Test that we can update the device tree with offset/size info"""
Simon Glass511f6582018-10-01 12:22:30 -06001675 _, _, _, out_dtb_fname = self._DoReadFileDtb('060_fdt_update.dts',
Simon Glassa87014e2018-07-06 10:27:42 -06001676 update_dtb=True)
Simon Glass5463a6a2018-07-17 13:25:52 -06001677 dtb = fdt.Fdt(out_dtb_fname)
1678 dtb.Scan()
Simon Glassfb30e292019-07-20 12:23:51 -06001679 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
Simon Glassa87014e2018-07-06 10:27:42 -06001680 self.assertEqual({
Simon Glass9dcc8612018-08-01 15:22:42 -06001681 'image-pos': 0,
Simon Glass3a9a2b82018-07-17 13:25:28 -06001682 'offset': 0,
Simon Glasse8561af2018-08-01 15:22:37 -06001683 '_testing:offset': 32,
Simon Glass8c702fb2019-07-20 12:23:57 -06001684 '_testing:size': 2,
Simon Glass9dcc8612018-08-01 15:22:42 -06001685 '_testing:image-pos': 32,
Simon Glasse8561af2018-08-01 15:22:37 -06001686 'section@0/u-boot:offset': 0,
Simon Glassa87014e2018-07-06 10:27:42 -06001687 'section@0/u-boot:size': len(U_BOOT_DATA),
Simon Glass9dcc8612018-08-01 15:22:42 -06001688 'section@0/u-boot:image-pos': 0,
Simon Glasse8561af2018-08-01 15:22:37 -06001689 'section@0:offset': 0,
Simon Glassa87014e2018-07-06 10:27:42 -06001690 'section@0:size': 16,
Simon Glass9dcc8612018-08-01 15:22:42 -06001691 'section@0:image-pos': 0,
Simon Glassa87014e2018-07-06 10:27:42 -06001692
Simon Glasse8561af2018-08-01 15:22:37 -06001693 'section@1/u-boot:offset': 0,
Simon Glassa87014e2018-07-06 10:27:42 -06001694 'section@1/u-boot:size': len(U_BOOT_DATA),
Simon Glass9dcc8612018-08-01 15:22:42 -06001695 'section@1/u-boot:image-pos': 16,
Simon Glasse8561af2018-08-01 15:22:37 -06001696 'section@1:offset': 16,
Simon Glassa87014e2018-07-06 10:27:42 -06001697 'section@1:size': 16,
Simon Glass9dcc8612018-08-01 15:22:42 -06001698 'section@1:image-pos': 16,
Simon Glassa87014e2018-07-06 10:27:42 -06001699 'size': 40
1700 }, props)
1701
1702 def testUpdateFdtBad(self):
1703 """Test that we detect when ProcessFdt never completes"""
1704 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001705 self._DoReadFileDtb('061_fdt_update_bad.dts', update_dtb=True)
Simon Glassa87014e2018-07-06 10:27:42 -06001706 self.assertIn('Could not complete processing of Fdt: remaining '
Simon Glassc585dd42020-04-17 18:09:03 -06001707 '[<binman.etype._testing.Entry__testing',
1708 str(e.exception))
Simon Glass2e1169f2018-07-06 10:27:19 -06001709
Simon Glass91710b32018-07-17 13:25:32 -06001710 def testEntryArgs(self):
1711 """Test passing arguments to entries from the command line"""
1712 entry_args = {
1713 'test-str-arg': 'test1',
1714 'test-int-arg': '456',
1715 }
Simon Glass511f6582018-10-01 12:22:30 -06001716 self._DoReadFileDtb('062_entry_args.dts', entry_args=entry_args)
Simon Glass91710b32018-07-17 13:25:32 -06001717 self.assertIn('image', control.images)
1718 entry = control.images['image'].GetEntries()['_testing']
1719 self.assertEqual('test0', entry.test_str_fdt)
1720 self.assertEqual('test1', entry.test_str_arg)
1721 self.assertEqual(123, entry.test_int_fdt)
1722 self.assertEqual(456, entry.test_int_arg)
1723
1724 def testEntryArgsMissing(self):
1725 """Test missing arguments and properties"""
1726 entry_args = {
1727 'test-int-arg': '456',
1728 }
Simon Glass511f6582018-10-01 12:22:30 -06001729 self._DoReadFileDtb('063_entry_args_missing.dts', entry_args=entry_args)
Simon Glass91710b32018-07-17 13:25:32 -06001730 entry = control.images['image'].GetEntries()['_testing']
1731 self.assertEqual('test0', entry.test_str_fdt)
1732 self.assertEqual(None, entry.test_str_arg)
1733 self.assertEqual(None, entry.test_int_fdt)
1734 self.assertEqual(456, entry.test_int_arg)
1735
1736 def testEntryArgsRequired(self):
1737 """Test missing arguments and properties"""
1738 entry_args = {
1739 'test-int-arg': '456',
1740 }
1741 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001742 self._DoReadFileDtb('064_entry_args_required.dts')
Simon Glass21db0ff2020-09-01 05:13:54 -06001743 self.assertIn("Node '/binman/_testing': "
1744 'Missing required properties/entry args: test-str-arg, '
1745 'test-int-fdt, test-int-arg',
Simon Glass91710b32018-07-17 13:25:32 -06001746 str(e.exception))
1747
1748 def testEntryArgsInvalidFormat(self):
1749 """Test that an invalid entry-argument format is detected"""
Simon Glassf46732a2019-07-08 14:25:29 -06001750 args = ['build', '-d', self.TestFile('064_entry_args_required.dts'),
1751 '-ano-value']
Simon Glass91710b32018-07-17 13:25:32 -06001752 with self.assertRaises(ValueError) as e:
1753 self._DoBinman(*args)
1754 self.assertIn("Invalid entry arguemnt 'no-value'", str(e.exception))
1755
1756 def testEntryArgsInvalidInteger(self):
1757 """Test that an invalid entry-argument integer is detected"""
1758 entry_args = {
1759 'test-int-arg': 'abc',
1760 }
1761 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001762 self._DoReadFileDtb('062_entry_args.dts', entry_args=entry_args)
Simon Glass91710b32018-07-17 13:25:32 -06001763 self.assertIn("Node '/binman/_testing': Cannot convert entry arg "
1764 "'test-int-arg' (value 'abc') to integer",
1765 str(e.exception))
1766
1767 def testEntryArgsInvalidDatatype(self):
1768 """Test that an invalid entry-argument datatype is detected
1769
1770 This test could be written in entry_test.py except that it needs
1771 access to control.entry_args, which seems more than that module should
1772 be able to see.
1773 """
1774 entry_args = {
1775 'test-bad-datatype-arg': '12',
1776 }
1777 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001778 self._DoReadFileDtb('065_entry_args_unknown_datatype.dts',
Simon Glass91710b32018-07-17 13:25:32 -06001779 entry_args=entry_args)
1780 self.assertIn('GetArg() internal error: Unknown data type ',
1781 str(e.exception))
1782
Simon Glass2ca52032018-07-17 13:25:33 -06001783 def testText(self):
1784 """Test for a text entry type"""
1785 entry_args = {
1786 'test-id': TEXT_DATA,
1787 'test-id2': TEXT_DATA2,
1788 'test-id3': TEXT_DATA3,
1789 }
Simon Glass511f6582018-10-01 12:22:30 -06001790 data, _, _, _ = self._DoReadFileDtb('066_text.dts',
Simon Glass2ca52032018-07-17 13:25:33 -06001791 entry_args=entry_args)
Simon Glass80025522022-01-29 14:14:04 -07001792 expected = (tools.to_bytes(TEXT_DATA) +
1793 tools.get_bytes(0, 8 - len(TEXT_DATA)) +
1794 tools.to_bytes(TEXT_DATA2) + tools.to_bytes(TEXT_DATA3) +
Simon Glass47f6a622019-07-08 13:18:40 -06001795 b'some text' + b'more text')
Simon Glass2ca52032018-07-17 13:25:33 -06001796 self.assertEqual(expected, data)
1797
Simon Glass969616c2018-07-17 13:25:36 -06001798 def testEntryDocs(self):
1799 """Test for creation of entry documentation"""
Simon Glass14d64e32025-04-29 07:21:59 -06001800 with terminal.capture() as (stdout, stderr):
Simon Glass220ff5f2020-08-05 13:27:46 -06001801 control.WriteEntryDocs(control.GetEntryModules())
Simon Glass969616c2018-07-17 13:25:36 -06001802 self.assertTrue(len(stdout.getvalue()) > 0)
1803
1804 def testEntryDocsMissing(self):
1805 """Test handling of missing entry documentation"""
1806 with self.assertRaises(ValueError) as e:
Simon Glass14d64e32025-04-29 07:21:59 -06001807 with terminal.capture() as (stdout, stderr):
Simon Glass220ff5f2020-08-05 13:27:46 -06001808 control.WriteEntryDocs(control.GetEntryModules(), 'u_boot')
Simon Glass969616c2018-07-17 13:25:36 -06001809 self.assertIn('Documentation is missing for modules: u_boot',
1810 str(e.exception))
1811
Simon Glass704784b2018-07-17 13:25:38 -06001812 def testFmap(self):
1813 """Basic test of generation of a flashrom fmap"""
Simon Glass511f6582018-10-01 12:22:30 -06001814 data = self._DoReadFile('067_fmap.dts')
Simon Glass704784b2018-07-17 13:25:38 -06001815 fhdr, fentries = fmap_util.DecodeFmap(data[32:])
Simon Glass80025522022-01-29 14:14:04 -07001816 expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) +
1817 U_BOOT_DATA + tools.get_bytes(ord('a'), 12))
Simon Glass704784b2018-07-17 13:25:38 -06001818 self.assertEqual(expected, data[:32])
Simon Glass303f62f2019-05-17 22:00:46 -06001819 self.assertEqual(b'__FMAP__', fhdr.signature)
Simon Glass704784b2018-07-17 13:25:38 -06001820 self.assertEqual(1, fhdr.ver_major)
1821 self.assertEqual(0, fhdr.ver_minor)
1822 self.assertEqual(0, fhdr.base)
Simon Glassb1d414c2021-04-03 11:05:10 +13001823 expect_size = fmap_util.FMAP_HEADER_LEN + fmap_util.FMAP_AREA_LEN * 5
Simon Glass82059c22021-04-03 11:05:09 +13001824 self.assertEqual(16 + 16 + expect_size, fhdr.image_size)
Simon Glass303f62f2019-05-17 22:00:46 -06001825 self.assertEqual(b'FMAP', fhdr.name)
Simon Glassb1d414c2021-04-03 11:05:10 +13001826 self.assertEqual(5, fhdr.nareas)
Simon Glass82059c22021-04-03 11:05:09 +13001827 fiter = iter(fentries)
Simon Glass704784b2018-07-17 13:25:38 -06001828
Simon Glass82059c22021-04-03 11:05:09 +13001829 fentry = next(fiter)
Simon Glassb1d414c2021-04-03 11:05:10 +13001830 self.assertEqual(b'SECTION0', fentry.name)
1831 self.assertEqual(0, fentry.offset)
1832 self.assertEqual(16, fentry.size)
Simon Glasscda991e2023-02-12 17:11:15 -07001833 self.assertEqual(fmap_util.FMAP_AREA_PRESERVE, fentry.flags)
Simon Glassb1d414c2021-04-03 11:05:10 +13001834
1835 fentry = next(fiter)
Simon Glass82059c22021-04-03 11:05:09 +13001836 self.assertEqual(b'RO_U_BOOT', fentry.name)
1837 self.assertEqual(0, fentry.offset)
1838 self.assertEqual(4, fentry.size)
1839 self.assertEqual(0, fentry.flags)
Simon Glass704784b2018-07-17 13:25:38 -06001840
Simon Glass82059c22021-04-03 11:05:09 +13001841 fentry = next(fiter)
Simon Glassb1d414c2021-04-03 11:05:10 +13001842 self.assertEqual(b'SECTION1', fentry.name)
1843 self.assertEqual(16, fentry.offset)
1844 self.assertEqual(16, fentry.size)
1845 self.assertEqual(0, fentry.flags)
1846
1847 fentry = next(fiter)
Simon Glass82059c22021-04-03 11:05:09 +13001848 self.assertEqual(b'RW_U_BOOT', fentry.name)
1849 self.assertEqual(16, fentry.offset)
1850 self.assertEqual(4, fentry.size)
1851 self.assertEqual(0, fentry.flags)
Simon Glass704784b2018-07-17 13:25:38 -06001852
Simon Glass82059c22021-04-03 11:05:09 +13001853 fentry = next(fiter)
1854 self.assertEqual(b'FMAP', fentry.name)
1855 self.assertEqual(32, fentry.offset)
1856 self.assertEqual(expect_size, fentry.size)
1857 self.assertEqual(0, fentry.flags)
Simon Glass704784b2018-07-17 13:25:38 -06001858
Simon Glassdb168d42018-07-17 13:25:39 -06001859 def testBlobNamedByArg(self):
1860 """Test we can add a blob with the filename coming from an entry arg"""
1861 entry_args = {
1862 'cros-ec-rw-path': 'ecrw.bin',
1863 }
Simon Glass21db0ff2020-09-01 05:13:54 -06001864 self._DoReadFileDtb('068_blob_named_by_arg.dts', entry_args=entry_args)
Simon Glassdb168d42018-07-17 13:25:39 -06001865
Simon Glass53f53992018-07-17 13:25:40 -06001866 def testFill(self):
1867 """Test for an fill entry type"""
Simon Glass511f6582018-10-01 12:22:30 -06001868 data = self._DoReadFile('069_fill.dts')
Simon Glass80025522022-01-29 14:14:04 -07001869 expected = tools.get_bytes(0xff, 8) + tools.get_bytes(0, 8)
Simon Glass53f53992018-07-17 13:25:40 -06001870 self.assertEqual(expected, data)
1871
1872 def testFillNoSize(self):
1873 """Test for an fill entry type with no size"""
1874 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001875 self._DoReadFile('070_fill_no_size.dts')
Simon Glass0cf5bce2022-08-13 11:40:44 -06001876 self.assertIn("'fill' entry is missing properties: size",
Simon Glass53f53992018-07-17 13:25:40 -06001877 str(e.exception))
1878
Simon Glassc1ae83c2018-07-17 13:25:44 -06001879 def _HandleGbbCommand(self, pipe_list):
1880 """Fake calls to the futility utility"""
Simon Glass9a1c7262023-02-22 12:14:49 -07001881 if 'futility' in pipe_list[0][0]:
Simon Glassc1ae83c2018-07-17 13:25:44 -06001882 fname = pipe_list[0][-1]
1883 # Append our GBB data to the file, which will happen every time the
1884 # futility command is called.
Simon Glass33486662019-05-14 15:53:42 -06001885 with open(fname, 'ab') as fd:
Simon Glassc1ae83c2018-07-17 13:25:44 -06001886 fd.write(GBB_DATA)
1887 return command.CommandResult()
1888
1889 def testGbb(self):
1890 """Test for the Chromium OS Google Binary Block"""
Simon Glass5dc22cf2025-02-03 09:26:42 -07001891 command.TEST_RESULT = self._HandleGbbCommand
Simon Glassc1ae83c2018-07-17 13:25:44 -06001892 entry_args = {
1893 'keydir': 'devkeys',
1894 'bmpblk': 'bmpblk.bin',
1895 }
Simon Glass511f6582018-10-01 12:22:30 -06001896 data, _, _, _ = self._DoReadFileDtb('071_gbb.dts', entry_args=entry_args)
Simon Glassc1ae83c2018-07-17 13:25:44 -06001897
1898 # Since futility
Simon Glass80025522022-01-29 14:14:04 -07001899 expected = (GBB_DATA + GBB_DATA + tools.get_bytes(0, 8) +
1900 tools.get_bytes(0, 0x2180 - 16))
Simon Glassc1ae83c2018-07-17 13:25:44 -06001901 self.assertEqual(expected, data)
1902
1903 def testGbbTooSmall(self):
1904 """Test for the Chromium OS Google Binary Block being large enough"""
1905 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001906 self._DoReadFileDtb('072_gbb_too_small.dts')
Simon Glassc1ae83c2018-07-17 13:25:44 -06001907 self.assertIn("Node '/binman/gbb': GBB is too small",
1908 str(e.exception))
1909
1910 def testGbbNoSize(self):
1911 """Test for the Chromium OS Google Binary Block having a size"""
1912 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001913 self._DoReadFileDtb('073_gbb_no_size.dts')
Simon Glassc1ae83c2018-07-17 13:25:44 -06001914 self.assertIn("Node '/binman/gbb': GBB must have a fixed size",
1915 str(e.exception))
1916
Simon Glass66152ce2022-01-09 20:14:09 -07001917 def testGbbMissing(self):
1918 """Test that binman still produces an image if futility is missing"""
1919 entry_args = {
1920 'keydir': 'devkeys',
1921 }
Simon Glass14d64e32025-04-29 07:21:59 -06001922 with terminal.capture() as (_, stderr):
Simon Glass66152ce2022-01-09 20:14:09 -07001923 self._DoTestFile('071_gbb.dts', force_missing_bintools='futility',
1924 entry_args=entry_args)
1925 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07001926 self.assertRegex(err, "Image 'image'.*missing bintools.*: futility")
Simon Glass66152ce2022-01-09 20:14:09 -07001927
Simon Glass5c350162018-07-17 13:25:47 -06001928 def _HandleVblockCommand(self, pipe_list):
Simon Glass220c6222021-01-06 21:35:17 -07001929 """Fake calls to the futility utility
1930
1931 The expected pipe is:
1932
1933 [('futility', 'vbutil_firmware', '--vblock',
1934 'vblock.vblock', '--keyblock', 'devkeys/firmware.keyblock',
1935 '--signprivate', 'devkeys/firmware_data_key.vbprivk',
1936 '--version', '1', '--fv', 'input.vblock', '--kernelkey',
1937 'devkeys/kernel_subkey.vbpubk', '--flags', '1')]
1938
1939 This writes to the output file (here, 'vblock.vblock'). If
1940 self._hash_data is False, it writes VBLOCK_DATA, else it writes a hash
1941 of the input data (here, 'input.vblock').
1942 """
Simon Glass9a1c7262023-02-22 12:14:49 -07001943 if 'futility' in pipe_list[0][0]:
Simon Glass5c350162018-07-17 13:25:47 -06001944 fname = pipe_list[0][3]
Simon Glass639505b2018-09-14 04:57:11 -06001945 with open(fname, 'wb') as fd:
Simon Glass220c6222021-01-06 21:35:17 -07001946 if self._hash_data:
1947 infile = pipe_list[0][11]
1948 m = hashlib.sha256()
Simon Glass80025522022-01-29 14:14:04 -07001949 data = tools.read_file(infile)
Simon Glass220c6222021-01-06 21:35:17 -07001950 m.update(data)
1951 fd.write(m.digest())
1952 else:
1953 fd.write(VBLOCK_DATA)
1954
Simon Glass5c350162018-07-17 13:25:47 -06001955 return command.CommandResult()
1956
1957 def testVblock(self):
1958 """Test for the Chromium OS Verified Boot Block"""
Simon Glass220c6222021-01-06 21:35:17 -07001959 self._hash_data = False
Simon Glass5dc22cf2025-02-03 09:26:42 -07001960 command.TEST_RESULT = self._HandleVblockCommand
Simon Glass5c350162018-07-17 13:25:47 -06001961 entry_args = {
1962 'keydir': 'devkeys',
1963 }
Simon Glass511f6582018-10-01 12:22:30 -06001964 data, _, _, _ = self._DoReadFileDtb('074_vblock.dts',
Simon Glass5c350162018-07-17 13:25:47 -06001965 entry_args=entry_args)
1966 expected = U_BOOT_DATA + VBLOCK_DATA + U_BOOT_DTB_DATA
1967 self.assertEqual(expected, data)
1968
1969 def testVblockNoContent(self):
1970 """Test we detect a vblock which has no content to sign"""
1971 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001972 self._DoReadFile('075_vblock_no_content.dts')
Simon Glasse1915782021-03-21 18:24:31 +13001973 self.assertIn("Node '/binman/vblock': Collection must have a 'content' "
Simon Glass5c350162018-07-17 13:25:47 -06001974 'property', str(e.exception))
1975
1976 def testVblockBadPhandle(self):
1977 """Test that we detect a vblock with an invalid phandle in contents"""
1978 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001979 self._DoReadFile('076_vblock_bad_phandle.dts')
Simon Glass5c350162018-07-17 13:25:47 -06001980 self.assertIn("Node '/binman/vblock': Cannot find node for phandle "
1981 '1000', str(e.exception))
1982
1983 def testVblockBadEntry(self):
1984 """Test that we detect an entry that points to a non-entry"""
1985 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001986 self._DoReadFile('077_vblock_bad_entry.dts')
Simon Glass5c350162018-07-17 13:25:47 -06001987 self.assertIn("Node '/binman/vblock': Cannot find entry for node "
1988 "'other'", str(e.exception))
1989
Simon Glass220c6222021-01-06 21:35:17 -07001990 def testVblockContent(self):
1991 """Test that the vblock signs the right data"""
1992 self._hash_data = True
Simon Glass5dc22cf2025-02-03 09:26:42 -07001993 command.TEST_RESULT = self._HandleVblockCommand
Simon Glass220c6222021-01-06 21:35:17 -07001994 entry_args = {
1995 'keydir': 'devkeys',
1996 }
1997 data = self._DoReadFileDtb(
1998 '189_vblock_content.dts', use_real_dtb=True, update_dtb=True,
1999 entry_args=entry_args)[0]
2000 hashlen = 32 # SHA256 hash is 32 bytes
2001 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
2002 hashval = data[-hashlen:]
2003 dtb = data[len(U_BOOT_DATA):-hashlen]
2004
2005 expected_data = U_BOOT_DATA + dtb
2006
2007 # The hashval should be a hash of the dtb
2008 m = hashlib.sha256()
2009 m.update(expected_data)
2010 expected_hashval = m.digest()
2011 self.assertEqual(expected_hashval, hashval)
2012
Simon Glass66152ce2022-01-09 20:14:09 -07002013 def testVblockMissing(self):
2014 """Test that binman still produces an image if futility is missing"""
2015 entry_args = {
2016 'keydir': 'devkeys',
2017 }
Simon Glass14d64e32025-04-29 07:21:59 -06002018 with terminal.capture() as (_, stderr):
Simon Glass66152ce2022-01-09 20:14:09 -07002019 self._DoTestFile('074_vblock.dts',
2020 force_missing_bintools='futility',
2021 entry_args=entry_args)
2022 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07002023 self.assertRegex(err, "Image 'image'.*missing bintools.*: futility")
Simon Glass66152ce2022-01-09 20:14:09 -07002024
Simon Glass8425a1f2018-07-17 13:25:48 -06002025 def testTpl(self):
Simon Glass3eb5b202019-08-24 07:23:00 -06002026 """Test that an image with TPL and its device tree can be created"""
Simon Glass8425a1f2018-07-17 13:25:48 -06002027 # ELF file with a '__bss_size' symbol
Simon Glass3eb5b202019-08-24 07:23:00 -06002028 self._SetupTplElf()
Simon Glass511f6582018-10-01 12:22:30 -06002029 data = self._DoReadFile('078_u_boot_tpl.dts')
Simon Glass8425a1f2018-07-17 13:25:48 -06002030 self.assertEqual(U_BOOT_TPL_DATA + U_BOOT_TPL_DTB_DATA, data)
2031
Simon Glass24b97442018-07-17 13:25:51 -06002032 def testUsesPos(self):
2033 """Test that the 'pos' property cannot be used anymore"""
2034 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002035 data = self._DoReadFile('079_uses_pos.dts')
Simon Glass24b97442018-07-17 13:25:51 -06002036 self.assertIn("Node '/binman/u-boot': Please use 'offset' instead of "
2037 "'pos'", str(e.exception))
2038
Simon Glass274bf092018-09-14 04:57:08 -06002039 def testFillZero(self):
2040 """Test for an fill entry type with a size of 0"""
Simon Glass511f6582018-10-01 12:22:30 -06002041 data = self._DoReadFile('080_fill_empty.dts')
Simon Glass80025522022-01-29 14:14:04 -07002042 self.assertEqual(tools.get_bytes(0, 16), data)
Simon Glass274bf092018-09-14 04:57:08 -06002043
Simon Glass267de432018-09-14 04:57:09 -06002044 def testTextMissing(self):
2045 """Test for a text entry type where there is no text"""
2046 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002047 self._DoReadFileDtb('066_text.dts',)
Simon Glass267de432018-09-14 04:57:09 -06002048 self.assertIn("Node '/binman/text': No value provided for text label "
2049 "'test-id'", str(e.exception))
2050
Simon Glassed40e962018-09-14 04:57:10 -06002051 def testPackStart16Tpl(self):
2052 """Test that an image with an x86 start16 TPL region can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06002053 data = self._DoReadFile('081_x86_start16_tpl.dts')
Simon Glassed40e962018-09-14 04:57:10 -06002054 self.assertEqual(X86_START16_TPL_DATA, data[:len(X86_START16_TPL_DATA)])
2055
Simon Glass3b376c32018-09-14 04:57:12 -06002056 def testSelectImage(self):
2057 """Test that we can select which images to build"""
Simon Glassb4595d82019-04-25 21:58:34 -06002058 expected = 'Skipping images: image1'
2059
2060 # We should only get the expected message in verbose mode
Simon Glass8a50b4a2019-07-08 13:18:48 -06002061 for verbosity in (0, 2):
Simon Glass14d64e32025-04-29 07:21:59 -06002062 with terminal.capture() as (stdout, stderr):
Simon Glassb4595d82019-04-25 21:58:34 -06002063 retcode = self._DoTestFile('006_dual_image.dts',
2064 verbosity=verbosity,
2065 images=['image2'])
2066 self.assertEqual(0, retcode)
2067 if verbosity:
2068 self.assertIn(expected, stdout.getvalue())
2069 else:
2070 self.assertNotIn(expected, stdout.getvalue())
Simon Glass3b376c32018-09-14 04:57:12 -06002071
Simon Glass80025522022-01-29 14:14:04 -07002072 self.assertFalse(os.path.exists(tools.get_output_filename('image1.bin')))
2073 self.assertTrue(os.path.exists(tools.get_output_filename('image2.bin')))
Simon Glassb3d6fc72019-07-20 12:24:10 -06002074 self._CleanupOutputDir()
Simon Glass3b376c32018-09-14 04:57:12 -06002075
Simon Glasse219aa42018-09-14 04:57:24 -06002076 def testUpdateFdtAll(self):
2077 """Test that all device trees are updated with offset/size info"""
Marek Vasutf7413f02023-07-18 07:23:58 -06002078 self._SetupSplElf()
2079 self._SetupTplElf()
Simon Glass5b4bce32019-07-08 14:25:26 -06002080 data = self._DoReadFileRealDtb('082_fdt_update_all.dts')
Simon Glasse219aa42018-09-14 04:57:24 -06002081
2082 base_expected = {
Simon Glasse219aa42018-09-14 04:57:24 -06002083 'offset': 0,
Simon Glass56d05412022-02-28 07:16:54 -07002084 'image-pos': 0,
2085 'size': 2320,
Simon Glasse219aa42018-09-14 04:57:24 -06002086 'section:offset': 0,
Simon Glass56d05412022-02-28 07:16:54 -07002087 'section:image-pos': 0,
2088 'section:size': 565,
2089 'section/u-boot-dtb:offset': 0,
2090 'section/u-boot-dtb:image-pos': 0,
2091 'section/u-boot-dtb:size': 565,
2092 'u-boot-spl-dtb:offset': 565,
2093 'u-boot-spl-dtb:image-pos': 565,
2094 'u-boot-spl-dtb:size': 585,
2095 'u-boot-tpl-dtb:offset': 1150,
2096 'u-boot-tpl-dtb:image-pos': 1150,
2097 'u-boot-tpl-dtb:size': 585,
2098 'u-boot-vpl-dtb:image-pos': 1735,
2099 'u-boot-vpl-dtb:offset': 1735,
2100 'u-boot-vpl-dtb:size': 585,
Simon Glasse219aa42018-09-14 04:57:24 -06002101 }
2102
2103 # We expect three device-tree files in the output, one after the other.
2104 # Read them in sequence. We look for an 'spl' property in the SPL tree,
2105 # and 'tpl' in the TPL tree, to make sure they are distinct from the
2106 # main U-Boot tree. All three should have the same postions and offset.
2107 start = 0
Simon Glass56d05412022-02-28 07:16:54 -07002108 self.maxDiff = None
2109 for item in ['', 'spl', 'tpl', 'vpl']:
Simon Glasse219aa42018-09-14 04:57:24 -06002110 dtb = fdt.Fdt.FromData(data[start:])
2111 dtb.Scan()
Simon Glassfb30e292019-07-20 12:23:51 -06002112 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS +
Simon Glass56d05412022-02-28 07:16:54 -07002113 ['spl', 'tpl', 'vpl'])
Simon Glasse219aa42018-09-14 04:57:24 -06002114 expected = dict(base_expected)
2115 if item:
2116 expected[item] = 0
2117 self.assertEqual(expected, props)
2118 start += dtb._fdt_obj.totalsize()
2119
2120 def testUpdateFdtOutput(self):
2121 """Test that output DTB files are updated"""
2122 try:
Simon Glass511f6582018-10-01 12:22:30 -06002123 data, dtb_data, _, _ = self._DoReadFileDtb('082_fdt_update_all.dts',
Simon Glasse219aa42018-09-14 04:57:24 -06002124 use_real_dtb=True, update_dtb=True, reset_dtbs=False)
2125
2126 # Unfortunately, compiling a source file always results in a file
2127 # called source.dtb (see fdt_util.EnsureCompiled()). The test
Simon Glass511f6582018-10-01 12:22:30 -06002128 # source file (e.g. test/075_fdt_update_all.dts) thus does not enter
Simon Glasse219aa42018-09-14 04:57:24 -06002129 # binman as a file called u-boot.dtb. To fix this, copy the file
2130 # over to the expected place.
Simon Glasse219aa42018-09-14 04:57:24 -06002131 start = 0
2132 for fname in ['u-boot.dtb.out', 'spl/u-boot-spl.dtb.out',
Simon Glass56d05412022-02-28 07:16:54 -07002133 'tpl/u-boot-tpl.dtb.out', 'vpl/u-boot-vpl.dtb.out']:
Simon Glasse219aa42018-09-14 04:57:24 -06002134 dtb = fdt.Fdt.FromData(data[start:])
2135 size = dtb._fdt_obj.totalsize()
Simon Glass80025522022-01-29 14:14:04 -07002136 pathname = tools.get_output_filename(os.path.split(fname)[1])
2137 outdata = tools.read_file(pathname)
Simon Glasse219aa42018-09-14 04:57:24 -06002138 name = os.path.split(fname)[0]
2139
2140 if name:
Simon Glass56d05412022-02-28 07:16:54 -07002141 orig_indata = self._GetDtbContentsForSpls(dtb_data, name)
Simon Glasse219aa42018-09-14 04:57:24 -06002142 else:
2143 orig_indata = dtb_data
2144 self.assertNotEqual(outdata, orig_indata,
2145 "Expected output file '%s' be updated" % pathname)
2146 self.assertEqual(outdata, data[start:start + size],
2147 "Expected output file '%s' to match output image" %
2148 pathname)
2149 start += size
2150 finally:
2151 self._ResetDtbs()
2152
Simon Glass7ba33592018-09-14 04:57:26 -06002153 def _decompress(self, data):
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +02002154 bintool = self.comp_bintools['lz4']
2155 return bintool.decompress(data)
Simon Glass7ba33592018-09-14 04:57:26 -06002156
2157 def testCompress(self):
2158 """Test compression of blobs"""
Simon Glass1de34482019-07-08 13:18:53 -06002159 self._CheckLz4()
Simon Glass511f6582018-10-01 12:22:30 -06002160 data, _, _, out_dtb_fname = self._DoReadFileDtb('083_compress.dts',
Simon Glass7ba33592018-09-14 04:57:26 -06002161 use_real_dtb=True, update_dtb=True)
2162 dtb = fdt.Fdt(out_dtb_fname)
2163 dtb.Scan()
2164 props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
2165 orig = self._decompress(data)
Brandon Maiera657bc62024-06-04 16:16:05 +00002166 self.assertEqual(COMPRESS_DATA, orig)
Simon Glass789b34402020-10-26 17:40:15 -06002167
2168 # Do a sanity check on various fields
2169 image = control.images['image']
2170 entries = image.GetEntries()
2171 self.assertEqual(1, len(entries))
2172
2173 entry = entries['blob']
2174 self.assertEqual(COMPRESS_DATA, entry.uncomp_data)
2175 self.assertEqual(len(COMPRESS_DATA), entry.uncomp_size)
2176 orig = self._decompress(entry.data)
2177 self.assertEqual(orig, entry.uncomp_data)
2178
Simon Glass72eeff12020-10-26 17:40:16 -06002179 self.assertEqual(image.data, entry.data)
2180
Simon Glass7ba33592018-09-14 04:57:26 -06002181 expected = {
2182 'blob:uncomp-size': len(COMPRESS_DATA),
2183 'blob:size': len(data),
2184 'size': len(data),
2185 }
2186 self.assertEqual(expected, props)
2187
Simon Glassac6328c2018-09-14 04:57:28 -06002188 def testFiles(self):
2189 """Test bringing in multiple files"""
Simon Glass511f6582018-10-01 12:22:30 -06002190 data = self._DoReadFile('084_files.dts')
Simon Glassac6328c2018-09-14 04:57:28 -06002191 self.assertEqual(FILES_DATA, data)
2192
2193 def testFilesCompress(self):
2194 """Test bringing in multiple files and compressing them"""
Simon Glass1de34482019-07-08 13:18:53 -06002195 self._CheckLz4()
Simon Glass511f6582018-10-01 12:22:30 -06002196 data = self._DoReadFile('085_files_compress.dts')
Simon Glassac6328c2018-09-14 04:57:28 -06002197
2198 image = control.images['image']
2199 entries = image.GetEntries()
2200 files = entries['files']
Simon Glass39dd2152019-07-08 14:25:47 -06002201 entries = files._entries
Simon Glassac6328c2018-09-14 04:57:28 -06002202
Simon Glass303f62f2019-05-17 22:00:46 -06002203 orig = b''
Simon Glassac6328c2018-09-14 04:57:28 -06002204 for i in range(1, 3):
2205 key = '%d.dat' % i
2206 start = entries[key].image_pos
2207 len = entries[key].size
2208 chunk = data[start:start + len]
2209 orig += self._decompress(chunk)
2210
2211 self.assertEqual(FILES_DATA, orig)
2212
2213 def testFilesMissing(self):
2214 """Test missing files"""
2215 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002216 data = self._DoReadFile('086_files_none.dts')
Simon Glassac6328c2018-09-14 04:57:28 -06002217 self.assertIn("Node '/binman/files': Pattern \'files/*.none\' matched "
2218 'no files', str(e.exception))
2219
2220 def testFilesNoPattern(self):
2221 """Test missing files"""
2222 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002223 data = self._DoReadFile('087_files_no_pattern.dts')
Simon Glassac6328c2018-09-14 04:57:28 -06002224 self.assertIn("Node '/binman/files': Missing 'pattern' property",
2225 str(e.exception))
2226
Simon Glassdd156a42022-03-05 20:18:59 -07002227 def testExtendSize(self):
2228 """Test an extending entry"""
2229 data, _, map_data, _ = self._DoReadFileDtb('088_extend_size.dts',
Simon Glassfa79a812018-09-14 04:57:29 -06002230 map=True)
Simon Glass80025522022-01-29 14:14:04 -07002231 expect = (tools.get_bytes(ord('a'), 8) + U_BOOT_DATA +
2232 MRC_DATA + tools.get_bytes(ord('b'), 1) + U_BOOT_DATA +
2233 tools.get_bytes(ord('c'), 8) + U_BOOT_DATA +
2234 tools.get_bytes(ord('d'), 8))
Simon Glassfa79a812018-09-14 04:57:29 -06002235 self.assertEqual(expect, data)
2236 self.assertEqual('''ImagePos Offset Size Name
Simon Glass49cd2b32023-02-07 14:34:18 -0700223700000000 00000000 00000028 image
Simon Glassfa79a812018-09-14 04:57:29 -0600223800000000 00000000 00000008 fill
223900000008 00000008 00000004 u-boot
22400000000c 0000000c 00000004 section
22410000000c 00000000 00000003 intel-mrc
224200000010 00000010 00000004 u-boot2
224300000014 00000014 0000000c section2
224400000014 00000000 00000008 fill
22450000001c 00000008 00000004 u-boot
224600000020 00000020 00000008 fill2
2247''', map_data)
2248
Simon Glassdd156a42022-03-05 20:18:59 -07002249 def testExtendSizeBad(self):
2250 """Test an extending entry which fails to provide contents"""
Simon Glass14d64e32025-04-29 07:21:59 -06002251 with terminal.capture() as (stdout, stderr):
Simon Glasscd817d52018-09-14 04:57:36 -06002252 with self.assertRaises(ValueError) as e:
Simon Glassdd156a42022-03-05 20:18:59 -07002253 self._DoReadFileDtb('089_extend_size_bad.dts', map=True)
Simon Glassfa79a812018-09-14 04:57:29 -06002254 self.assertIn("Node '/binman/_testing': Cannot obtain contents when "
2255 'expanding entry', str(e.exception))
2256
Simon Glassae7cf032018-09-14 04:57:31 -06002257 def testHash(self):
2258 """Test hashing of the contents of an entry"""
Simon Glass511f6582018-10-01 12:22:30 -06002259 _, _, _, out_dtb_fname = self._DoReadFileDtb('090_hash.dts',
Simon Glassae7cf032018-09-14 04:57:31 -06002260 use_real_dtb=True, update_dtb=True)
2261 dtb = fdt.Fdt(out_dtb_fname)
2262 dtb.Scan()
2263 hash_node = dtb.GetNode('/binman/u-boot/hash').props['value']
2264 m = hashlib.sha256()
2265 m.update(U_BOOT_DATA)
Simon Glass303f62f2019-05-17 22:00:46 -06002266 self.assertEqual(m.digest(), b''.join(hash_node.value))
Simon Glassae7cf032018-09-14 04:57:31 -06002267
2268 def testHashNoAlgo(self):
2269 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002270 self._DoReadFileDtb('091_hash_no_algo.dts', update_dtb=True)
Simon Glassae7cf032018-09-14 04:57:31 -06002271 self.assertIn("Node \'/binman/u-boot\': Missing \'algo\' property for "
2272 'hash node', str(e.exception))
2273
2274 def testHashBadAlgo(self):
2275 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002276 self._DoReadFileDtb('092_hash_bad_algo.dts', update_dtb=True)
Simon Glass64af7c22022-02-08 10:59:44 -07002277 self.assertIn("Node '/binman/u-boot': Unknown hash algorithm 'invalid'",
Simon Glassae7cf032018-09-14 04:57:31 -06002278 str(e.exception))
2279
2280 def testHashSection(self):
2281 """Test hashing of the contents of an entry"""
Simon Glass511f6582018-10-01 12:22:30 -06002282 _, _, _, out_dtb_fname = self._DoReadFileDtb('099_hash_section.dts',
Simon Glassae7cf032018-09-14 04:57:31 -06002283 use_real_dtb=True, update_dtb=True)
2284 dtb = fdt.Fdt(out_dtb_fname)
2285 dtb.Scan()
2286 hash_node = dtb.GetNode('/binman/section/hash').props['value']
2287 m = hashlib.sha256()
2288 m.update(U_BOOT_DATA)
Simon Glass80025522022-01-29 14:14:04 -07002289 m.update(tools.get_bytes(ord('a'), 16))
Simon Glass303f62f2019-05-17 22:00:46 -06002290 self.assertEqual(m.digest(), b''.join(hash_node.value))
Simon Glassae7cf032018-09-14 04:57:31 -06002291
Simon Glass3fb4f422018-09-14 04:57:32 -06002292 def testPackUBootTplMicrocode(self):
2293 """Test that x86 microcode can be handled correctly in TPL
2294
2295 We expect to see the following in the image, in order:
2296 u-boot-tpl-nodtb.bin with a microcode pointer inserted at the correct
2297 place
2298 u-boot-tpl.dtb with the microcode removed
2299 the microcode
2300 """
Simon Glass3eb5b202019-08-24 07:23:00 -06002301 self._SetupTplElf('u_boot_ucode_ptr')
Simon Glass511f6582018-10-01 12:22:30 -06002302 first, pos_and_size = self._RunMicrocodeTest('093_x86_tpl_ucode.dts',
Simon Glass3fb4f422018-09-14 04:57:32 -06002303 U_BOOT_TPL_NODTB_DATA)
Simon Glass303f62f2019-05-17 22:00:46 -06002304 self.assertEqual(b'tplnodtb with microc' + pos_and_size +
2305 b'ter somewhere in here', first)
Simon Glass3fb4f422018-09-14 04:57:32 -06002306
Simon Glassc64aea52018-09-14 04:57:34 -06002307 def testFmapX86(self):
2308 """Basic test of generation of a flashrom fmap"""
Simon Glass511f6582018-10-01 12:22:30 -06002309 data = self._DoReadFile('094_fmap_x86.dts')
Simon Glassc64aea52018-09-14 04:57:34 -06002310 fhdr, fentries = fmap_util.DecodeFmap(data[32:])
Simon Glass80025522022-01-29 14:14:04 -07002311 expected = U_BOOT_DATA + MRC_DATA + tools.get_bytes(ord('a'), 32 - 7)
Simon Glassc64aea52018-09-14 04:57:34 -06002312 self.assertEqual(expected, data[:32])
2313 fhdr, fentries = fmap_util.DecodeFmap(data[32:])
2314
2315 self.assertEqual(0x100, fhdr.image_size)
Simon Glassed836ac2025-02-26 09:26:17 -07002316 base = (1 << 32) - 0x100
Simon Glassc64aea52018-09-14 04:57:34 -06002317
Simon Glassed836ac2025-02-26 09:26:17 -07002318 self.assertEqual(base, fentries[0].offset)
Simon Glassc64aea52018-09-14 04:57:34 -06002319 self.assertEqual(4, fentries[0].size)
Simon Glass303f62f2019-05-17 22:00:46 -06002320 self.assertEqual(b'U_BOOT', fentries[0].name)
Simon Glassc64aea52018-09-14 04:57:34 -06002321
Simon Glassed836ac2025-02-26 09:26:17 -07002322 self.assertEqual(base + 4, fentries[1].offset)
Simon Glassc64aea52018-09-14 04:57:34 -06002323 self.assertEqual(3, fentries[1].size)
Simon Glass303f62f2019-05-17 22:00:46 -06002324 self.assertEqual(b'INTEL_MRC', fentries[1].name)
Simon Glassc64aea52018-09-14 04:57:34 -06002325
Simon Glassed836ac2025-02-26 09:26:17 -07002326 self.assertEqual(base + 32, fentries[2].offset)
Simon Glassc64aea52018-09-14 04:57:34 -06002327 self.assertEqual(fmap_util.FMAP_HEADER_LEN +
2328 fmap_util.FMAP_AREA_LEN * 3, fentries[2].size)
Simon Glass303f62f2019-05-17 22:00:46 -06002329 self.assertEqual(b'FMAP', fentries[2].name)
Simon Glassc64aea52018-09-14 04:57:34 -06002330
2331 def testFmapX86Section(self):
2332 """Basic test of generation of a flashrom fmap"""
Simon Glass511f6582018-10-01 12:22:30 -06002333 data = self._DoReadFile('095_fmap_x86_section.dts')
Simon Glass80025522022-01-29 14:14:04 -07002334 expected = U_BOOT_DATA + MRC_DATA + tools.get_bytes(ord('b'), 32 - 7)
Simon Glassc64aea52018-09-14 04:57:34 -06002335 self.assertEqual(expected, data[:32])
2336 fhdr, fentries = fmap_util.DecodeFmap(data[36:])
2337
Simon Glassb1d414c2021-04-03 11:05:10 +13002338 self.assertEqual(0x180, fhdr.image_size)
Simon Glassed836ac2025-02-26 09:26:17 -07002339 base = (1 << 32) - 0x180
Simon Glassb1d414c2021-04-03 11:05:10 +13002340 expect_size = fmap_util.FMAP_HEADER_LEN + fmap_util.FMAP_AREA_LEN * 4
Simon Glass82059c22021-04-03 11:05:09 +13002341 fiter = iter(fentries)
Simon Glassc64aea52018-09-14 04:57:34 -06002342
Simon Glass82059c22021-04-03 11:05:09 +13002343 fentry = next(fiter)
2344 self.assertEqual(b'U_BOOT', fentry.name)
Simon Glassed836ac2025-02-26 09:26:17 -07002345 self.assertEqual(base, fentry.offset)
Simon Glass82059c22021-04-03 11:05:09 +13002346 self.assertEqual(4, fentry.size)
Simon Glassc64aea52018-09-14 04:57:34 -06002347
Simon Glass82059c22021-04-03 11:05:09 +13002348 fentry = next(fiter)
Simon Glassb1d414c2021-04-03 11:05:10 +13002349 self.assertEqual(b'SECTION', fentry.name)
Simon Glassed836ac2025-02-26 09:26:17 -07002350 self.assertEqual(base + 4, fentry.offset)
Simon Glassb1d414c2021-04-03 11:05:10 +13002351 self.assertEqual(0x20 + expect_size, fentry.size)
2352
2353 fentry = next(fiter)
Simon Glass82059c22021-04-03 11:05:09 +13002354 self.assertEqual(b'INTEL_MRC', fentry.name)
Simon Glassed836ac2025-02-26 09:26:17 -07002355 self.assertEqual(base + 4, fentry.offset)
Simon Glass82059c22021-04-03 11:05:09 +13002356 self.assertEqual(3, fentry.size)
Simon Glassc64aea52018-09-14 04:57:34 -06002357
Simon Glass82059c22021-04-03 11:05:09 +13002358 fentry = next(fiter)
2359 self.assertEqual(b'FMAP', fentry.name)
Simon Glassed836ac2025-02-26 09:26:17 -07002360 self.assertEqual(base + 36, fentry.offset)
Simon Glass82059c22021-04-03 11:05:09 +13002361 self.assertEqual(expect_size, fentry.size)
Simon Glassc64aea52018-09-14 04:57:34 -06002362
Simon Glassb1714232018-09-14 04:57:35 -06002363 def testElf(self):
2364 """Basic test of ELF entries"""
Simon Glass7057d022018-10-01 21:12:47 -06002365 self._SetupSplElf()
Simon Glass3eb5b202019-08-24 07:23:00 -06002366 self._SetupTplElf()
Simon Glassf6290892019-08-24 07:22:53 -06002367 with open(self.ElfTestFile('bss_data'), 'rb') as fd:
Simon Glassb1714232018-09-14 04:57:35 -06002368 TestFunctional._MakeInputFile('-boot', fd.read())
Simon Glass511f6582018-10-01 12:22:30 -06002369 data = self._DoReadFile('096_elf.dts')
Simon Glassb1714232018-09-14 04:57:35 -06002370
Simon Glass0d673792019-07-08 13:18:25 -06002371 def testElfStrip(self):
Simon Glassb1714232018-09-14 04:57:35 -06002372 """Basic test of ELF entries"""
Simon Glass7057d022018-10-01 21:12:47 -06002373 self._SetupSplElf()
Simon Glassf6290892019-08-24 07:22:53 -06002374 with open(self.ElfTestFile('bss_data'), 'rb') as fd:
Simon Glassb1714232018-09-14 04:57:35 -06002375 TestFunctional._MakeInputFile('-boot', fd.read())
Simon Glass511f6582018-10-01 12:22:30 -06002376 data = self._DoReadFile('097_elf_strip.dts')
Simon Glassb1714232018-09-14 04:57:35 -06002377
Simon Glasscd817d52018-09-14 04:57:36 -06002378 def testPackOverlapMap(self):
2379 """Test that overlapping regions are detected"""
Simon Glass14d64e32025-04-29 07:21:59 -06002380 with terminal.capture() as (stdout, stderr):
Simon Glasscd817d52018-09-14 04:57:36 -06002381 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002382 self._DoTestFile('014_pack_overlap.dts', map=True)
Simon Glass80025522022-01-29 14:14:04 -07002383 map_fname = tools.get_output_filename('image.map')
Simon Glasscd817d52018-09-14 04:57:36 -06002384 self.assertEqual("Wrote map file '%s' to show errors\n" % map_fname,
2385 stdout.getvalue())
2386
2387 # We should not get an inmage, but there should be a map file
Simon Glass80025522022-01-29 14:14:04 -07002388 self.assertFalse(os.path.exists(tools.get_output_filename('image.bin')))
Simon Glasscd817d52018-09-14 04:57:36 -06002389 self.assertTrue(os.path.exists(map_fname))
Simon Glass80025522022-01-29 14:14:04 -07002390 map_data = tools.read_file(map_fname, binary=False)
Simon Glasscd817d52018-09-14 04:57:36 -06002391 self.assertEqual('''ImagePos Offset Size Name
Simon Glass49cd2b32023-02-07 14:34:18 -07002392<none> 00000000 00000008 image
Simon Glasscd817d52018-09-14 04:57:36 -06002393<none> 00000000 00000004 u-boot
2394<none> 00000003 00000004 u-boot-align
2395''', map_data)
2396
Simon Glass0d673792019-07-08 13:18:25 -06002397 def testPackRefCode(self):
Simon Glass41902e42018-10-01 12:22:31 -06002398 """Test that an image with an Intel Reference code binary works"""
2399 data = self._DoReadFile('100_intel_refcode.dts')
2400 self.assertEqual(REFCODE_DATA, data[:len(REFCODE_DATA)])
2401
Simon Glasseb023b32019-04-25 21:58:39 -06002402 def testSectionOffset(self):
2403 """Tests use of a section with an offset"""
2404 data, _, map_data, _ = self._DoReadFileDtb('101_sections_offset.dts',
2405 map=True)
2406 self.assertEqual('''ImagePos Offset Size Name
Simon Glass49cd2b32023-02-07 14:34:18 -0700240700000000 00000000 00000038 image
Simon Glasseb023b32019-04-25 21:58:39 -0600240800000004 00000004 00000010 section@0
240900000004 00000000 00000004 u-boot
241000000018 00000018 00000010 section@1
241100000018 00000000 00000004 u-boot
24120000002c 0000002c 00000004 section@2
24130000002c 00000000 00000004 u-boot
2414''', map_data)
2415 self.assertEqual(data,
Simon Glass80025522022-01-29 14:14:04 -07002416 tools.get_bytes(0x26, 4) + U_BOOT_DATA +
2417 tools.get_bytes(0x21, 12) +
2418 tools.get_bytes(0x26, 4) + U_BOOT_DATA +
2419 tools.get_bytes(0x61, 12) +
2420 tools.get_bytes(0x26, 4) + U_BOOT_DATA +
2421 tools.get_bytes(0x26, 8))
Simon Glasseb023b32019-04-25 21:58:39 -06002422
Simon Glass1de34482019-07-08 13:18:53 -06002423 def testCbfsRaw(self):
2424 """Test base handling of a Coreboot Filesystem (CBFS)
2425
2426 The exact contents of the CBFS is verified by similar tests in
2427 cbfs_util_test.py. The tests here merely check that the files added to
2428 the CBFS can be found in the final image.
2429 """
2430 data = self._DoReadFile('102_cbfs_raw.dts')
2431 size = 0xb0
2432
2433 cbfs = cbfs_util.CbfsReader(data)
2434 self.assertEqual(size, cbfs.rom_size)
2435
2436 self.assertIn('u-boot-dtb', cbfs.files)
2437 cfile = cbfs.files['u-boot-dtb']
2438 self.assertEqual(U_BOOT_DTB_DATA, cfile.data)
2439
2440 def testCbfsArch(self):
2441 """Test on non-x86 architecture"""
2442 data = self._DoReadFile('103_cbfs_raw_ppc.dts')
2443 size = 0x100
2444
2445 cbfs = cbfs_util.CbfsReader(data)
2446 self.assertEqual(size, cbfs.rom_size)
2447
2448 self.assertIn('u-boot-dtb', cbfs.files)
2449 cfile = cbfs.files['u-boot-dtb']
2450 self.assertEqual(U_BOOT_DTB_DATA, cfile.data)
2451
2452 def testCbfsStage(self):
2453 """Tests handling of a Coreboot Filesystem (CBFS)"""
2454 if not elf.ELF_TOOLS:
2455 self.skipTest('Python elftools not available')
2456 elf_fname = os.path.join(self._indir, 'cbfs-stage.elf')
2457 elf.MakeElf(elf_fname, U_BOOT_DATA, U_BOOT_DTB_DATA)
2458 size = 0xb0
2459
2460 data = self._DoReadFile('104_cbfs_stage.dts')
2461 cbfs = cbfs_util.CbfsReader(data)
2462 self.assertEqual(size, cbfs.rom_size)
2463
2464 self.assertIn('u-boot', cbfs.files)
2465 cfile = cbfs.files['u-boot']
2466 self.assertEqual(U_BOOT_DATA + U_BOOT_DTB_DATA, cfile.data)
2467
2468 def testCbfsRawCompress(self):
2469 """Test handling of compressing raw files"""
2470 self._CheckLz4()
2471 data = self._DoReadFile('105_cbfs_raw_compress.dts')
2472 size = 0x140
2473
2474 cbfs = cbfs_util.CbfsReader(data)
2475 self.assertIn('u-boot', cbfs.files)
2476 cfile = cbfs.files['u-boot']
2477 self.assertEqual(COMPRESS_DATA, cfile.data)
2478
2479 def testCbfsBadArch(self):
2480 """Test handling of a bad architecture"""
2481 with self.assertRaises(ValueError) as e:
2482 self._DoReadFile('106_cbfs_bad_arch.dts')
2483 self.assertIn("Invalid architecture 'bad-arch'", str(e.exception))
2484
2485 def testCbfsNoSize(self):
2486 """Test handling of a missing size property"""
2487 with self.assertRaises(ValueError) as e:
2488 self._DoReadFile('107_cbfs_no_size.dts')
2489 self.assertIn('entry must have a size property', str(e.exception))
2490
Simon Glass3e28f4f2021-11-23 11:03:54 -07002491 def testCbfsNoContents(self):
Simon Glass1de34482019-07-08 13:18:53 -06002492 """Test handling of a CBFS entry which does not provide contentsy"""
2493 with self.assertRaises(ValueError) as e:
2494 self._DoReadFile('108_cbfs_no_contents.dts')
2495 self.assertIn('Could not complete processing of contents',
2496 str(e.exception))
2497
2498 def testCbfsBadCompress(self):
2499 """Test handling of a bad architecture"""
2500 with self.assertRaises(ValueError) as e:
2501 self._DoReadFile('109_cbfs_bad_compress.dts')
2502 self.assertIn("Invalid compression in 'u-boot': 'invalid-algo'",
2503 str(e.exception))
2504
2505 def testCbfsNamedEntries(self):
2506 """Test handling of named entries"""
2507 data = self._DoReadFile('110_cbfs_name.dts')
2508
2509 cbfs = cbfs_util.CbfsReader(data)
2510 self.assertIn('FRED', cbfs.files)
2511 cfile1 = cbfs.files['FRED']
2512 self.assertEqual(U_BOOT_DATA, cfile1.data)
2513
2514 self.assertIn('hello', cbfs.files)
2515 cfile2 = cbfs.files['hello']
2516 self.assertEqual(U_BOOT_DTB_DATA, cfile2.data)
2517
Simon Glass759af872019-07-08 13:18:54 -06002518 def _SetupIfwi(self, fname):
2519 """Set up to run an IFWI test
2520
2521 Args:
2522 fname: Filename of input file to provide (fitimage.bin or ifwi.bin)
2523 """
2524 self._SetupSplElf()
Simon Glass3eb5b202019-08-24 07:23:00 -06002525 self._SetupTplElf()
Simon Glass759af872019-07-08 13:18:54 -06002526
2527 # Intel Integrated Firmware Image (IFWI) file
2528 with gzip.open(self.TestFile('%s.gz' % fname), 'rb') as fd:
2529 data = fd.read()
2530 TestFunctional._MakeInputFile(fname,data)
2531
2532 def _CheckIfwi(self, data):
2533 """Check that an image with an IFWI contains the correct output
2534
2535 Args:
2536 data: Conents of output file
2537 """
Simon Glass80025522022-01-29 14:14:04 -07002538 expected_desc = tools.read_file(self.TestFile('descriptor.bin'))
Simon Glass759af872019-07-08 13:18:54 -06002539 if data[:0x1000] != expected_desc:
2540 self.fail('Expected descriptor binary at start of image')
2541
2542 # We expect to find the TPL wil in subpart IBBP entry IBBL
Simon Glass80025522022-01-29 14:14:04 -07002543 image_fname = tools.get_output_filename('image.bin')
2544 tpl_fname = tools.get_output_filename('tpl.out')
Simon Glass57c7a482022-01-09 20:14:01 -07002545 ifwitool = bintool.Bintool.create('ifwitool')
2546 ifwitool.extract(image_fname, 'IBBP', 'IBBL', tpl_fname)
Simon Glass759af872019-07-08 13:18:54 -06002547
Simon Glass80025522022-01-29 14:14:04 -07002548 tpl_data = tools.read_file(tpl_fname)
Simon Glassf55bd692019-08-24 07:22:51 -06002549 self.assertEqual(U_BOOT_TPL_DATA, tpl_data[:len(U_BOOT_TPL_DATA)])
Simon Glass759af872019-07-08 13:18:54 -06002550
2551 def testPackX86RomIfwi(self):
2552 """Test that an x86 ROM with Integrated Firmware Image can be created"""
2553 self._SetupIfwi('fitimage.bin')
Simon Glass1d167762019-08-24 07:23:01 -06002554 data = self._DoReadFile('111_x86_rom_ifwi.dts')
Simon Glass759af872019-07-08 13:18:54 -06002555 self._CheckIfwi(data)
2556
2557 def testPackX86RomIfwiNoDesc(self):
2558 """Test that an x86 ROM with IFWI can be created from an ifwi.bin file"""
2559 self._SetupIfwi('ifwi.bin')
Simon Glass1d167762019-08-24 07:23:01 -06002560 data = self._DoReadFile('112_x86_rom_ifwi_nodesc.dts')
Simon Glass759af872019-07-08 13:18:54 -06002561 self._CheckIfwi(data)
2562
2563 def testPackX86RomIfwiNoData(self):
2564 """Test that an x86 ROM with IFWI handles missing data"""
2565 self._SetupIfwi('ifwi.bin')
2566 with self.assertRaises(ValueError) as e:
Simon Glass1d167762019-08-24 07:23:01 -06002567 data = self._DoReadFile('113_x86_rom_ifwi_nodata.dts')
Simon Glass759af872019-07-08 13:18:54 -06002568 self.assertIn('Could not complete processing of contents',
2569 str(e.exception))
Simon Glass91710b32018-07-17 13:25:32 -06002570
Simon Glass66152ce2022-01-09 20:14:09 -07002571 def testIfwiMissing(self):
2572 """Test that binman still produces an image if ifwitool is missing"""
2573 self._SetupIfwi('fitimage.bin')
Simon Glass14d64e32025-04-29 07:21:59 -06002574 with terminal.capture() as (_, stderr):
Simon Glass66152ce2022-01-09 20:14:09 -07002575 self._DoTestFile('111_x86_rom_ifwi.dts',
2576 force_missing_bintools='ifwitool')
2577 err = stderr.getvalue()
2578 self.assertRegex(err,
Simon Glass49cd2b32023-02-07 14:34:18 -07002579 "Image 'image'.*missing bintools.*: ifwitool")
Simon Glass66152ce2022-01-09 20:14:09 -07002580
Simon Glassc2f1aed2019-07-08 13:18:56 -06002581 def testCbfsOffset(self):
2582 """Test a CBFS with files at particular offsets
2583
2584 Like all CFBS tests, this is just checking the logic that calls
2585 cbfs_util. See cbfs_util_test for fully tests (e.g. test_cbfs_offset()).
2586 """
2587 data = self._DoReadFile('114_cbfs_offset.dts')
2588 size = 0x200
2589
2590 cbfs = cbfs_util.CbfsReader(data)
2591 self.assertEqual(size, cbfs.rom_size)
2592
2593 self.assertIn('u-boot', cbfs.files)
2594 cfile = cbfs.files['u-boot']
2595 self.assertEqual(U_BOOT_DATA, cfile.data)
2596 self.assertEqual(0x40, cfile.cbfs_offset)
2597
2598 self.assertIn('u-boot-dtb', cbfs.files)
2599 cfile2 = cbfs.files['u-boot-dtb']
2600 self.assertEqual(U_BOOT_DTB_DATA, cfile2.data)
2601 self.assertEqual(0x140, cfile2.cbfs_offset)
2602
Simon Glass0f621332019-07-08 14:25:27 -06002603 def testFdtmap(self):
2604 """Test an FDT map can be inserted in the image"""
2605 data = self.data = self._DoReadFileRealDtb('115_fdtmap.dts')
2606 fdtmap_data = data[len(U_BOOT_DATA):]
2607 magic = fdtmap_data[:8]
Simon Glassc5fd10a2019-10-31 07:43:03 -06002608 self.assertEqual(b'_FDTMAP_', magic)
Simon Glass80025522022-01-29 14:14:04 -07002609 self.assertEqual(tools.get_bytes(0, 8), fdtmap_data[8:16])
Simon Glass0f621332019-07-08 14:25:27 -06002610
2611 fdt_data = fdtmap_data[16:]
2612 dtb = fdt.Fdt.FromData(fdt_data)
2613 dtb.Scan()
Simon Glass2c6adba2019-07-20 12:23:47 -06002614 props = self._GetPropTree(dtb, BASE_DTB_PROPS, prefix='/')
Simon Glass0f621332019-07-08 14:25:27 -06002615 self.assertEqual({
2616 'image-pos': 0,
2617 'offset': 0,
2618 'u-boot:offset': 0,
2619 'u-boot:size': len(U_BOOT_DATA),
2620 'u-boot:image-pos': 0,
2621 'fdtmap:image-pos': 4,
2622 'fdtmap:offset': 4,
2623 'fdtmap:size': len(fdtmap_data),
2624 'size': len(data),
2625 }, props)
2626
2627 def testFdtmapNoMatch(self):
2628 """Check handling of an FDT map when the section cannot be found"""
2629 self.data = self._DoReadFileRealDtb('115_fdtmap.dts')
2630
2631 # Mangle the section name, which should cause a mismatch between the
2632 # correct FDT path and the one expected by the section
2633 image = control.images['image']
Simon Glasscec34ba2019-07-08 14:25:28 -06002634 image._node.path += '-suffix'
Simon Glass0f621332019-07-08 14:25:27 -06002635 entries = image.GetEntries()
2636 fdtmap = entries['fdtmap']
2637 with self.assertRaises(ValueError) as e:
2638 fdtmap._GetFdtmap()
2639 self.assertIn("Cannot locate node for path '/binman-suffix'",
2640 str(e.exception))
2641
Simon Glasscec34ba2019-07-08 14:25:28 -06002642 def testFdtmapHeader(self):
2643 """Test an FDT map and image header can be inserted in the image"""
2644 data = self.data = self._DoReadFileRealDtb('116_fdtmap_hdr.dts')
2645 fdtmap_pos = len(U_BOOT_DATA)
2646 fdtmap_data = data[fdtmap_pos:]
2647 fdt_data = fdtmap_data[16:]
2648 dtb = fdt.Fdt.FromData(fdt_data)
2649 fdt_size = dtb.GetFdtObj().totalsize()
2650 hdr_data = data[-8:]
Simon Glassc5fd10a2019-10-31 07:43:03 -06002651 self.assertEqual(b'BinM', hdr_data[:4])
Simon Glasscec34ba2019-07-08 14:25:28 -06002652 offset = struct.unpack('<I', hdr_data[4:])[0] & 0xffffffff
2653 self.assertEqual(fdtmap_pos - 0x400, offset - (1 << 32))
2654
2655 def testFdtmapHeaderStart(self):
2656 """Test an image header can be inserted at the image start"""
2657 data = self.data = self._DoReadFileRealDtb('117_fdtmap_hdr_start.dts')
2658 fdtmap_pos = 0x100 + len(U_BOOT_DATA)
2659 hdr_data = data[:8]
Simon Glassc5fd10a2019-10-31 07:43:03 -06002660 self.assertEqual(b'BinM', hdr_data[:4])
Simon Glasscec34ba2019-07-08 14:25:28 -06002661 offset = struct.unpack('<I', hdr_data[4:])[0]
2662 self.assertEqual(fdtmap_pos, offset)
2663
2664 def testFdtmapHeaderPos(self):
2665 """Test an image header can be inserted at a chosen position"""
2666 data = self.data = self._DoReadFileRealDtb('118_fdtmap_hdr_pos.dts')
2667 fdtmap_pos = 0x100 + len(U_BOOT_DATA)
2668 hdr_data = data[0x80:0x88]
Simon Glassc5fd10a2019-10-31 07:43:03 -06002669 self.assertEqual(b'BinM', hdr_data[:4])
Simon Glasscec34ba2019-07-08 14:25:28 -06002670 offset = struct.unpack('<I', hdr_data[4:])[0]
2671 self.assertEqual(fdtmap_pos, offset)
2672
2673 def testHeaderMissingFdtmap(self):
2674 """Test an image header requires an fdtmap"""
2675 with self.assertRaises(ValueError) as e:
2676 self.data = self._DoReadFileRealDtb('119_fdtmap_hdr_missing.dts')
2677 self.assertIn("'image_header' section must have an 'fdtmap' sibling",
2678 str(e.exception))
2679
2680 def testHeaderNoLocation(self):
2681 """Test an image header with a no specified location is detected"""
2682 with self.assertRaises(ValueError) as e:
2683 self.data = self._DoReadFileRealDtb('120_hdr_no_location.dts')
2684 self.assertIn("Invalid location 'None', expected 'start' or 'end'",
2685 str(e.exception))
2686
Simon Glasse61b6f62019-07-08 14:25:37 -06002687 def testEntryExpand(self):
Simon Glassdd156a42022-03-05 20:18:59 -07002688 """Test extending an entry after it is packed"""
2689 data = self._DoReadFile('121_entry_extend.dts')
Simon Glass8c702fb2019-07-20 12:23:57 -06002690 self.assertEqual(b'aaa', data[:3])
2691 self.assertEqual(U_BOOT_DATA, data[3:3 + len(U_BOOT_DATA)])
2692 self.assertEqual(b'aaa', data[-3:])
Simon Glasse61b6f62019-07-08 14:25:37 -06002693
Simon Glassdd156a42022-03-05 20:18:59 -07002694 def testEntryExtendBad(self):
2695 """Test extending an entry after it is packed, twice"""
Simon Glasse61b6f62019-07-08 14:25:37 -06002696 with self.assertRaises(ValueError) as e:
Simon Glassdd156a42022-03-05 20:18:59 -07002697 self._DoReadFile('122_entry_extend_twice.dts')
Simon Glass9d8ee322019-07-20 12:23:58 -06002698 self.assertIn("Image '/binman': Entries changed size after packing",
Simon Glasse61b6f62019-07-08 14:25:37 -06002699 str(e.exception))
2700
Simon Glassdd156a42022-03-05 20:18:59 -07002701 def testEntryExtendSection(self):
2702 """Test extending an entry within a section after it is packed"""
2703 data = self._DoReadFile('123_entry_extend_section.dts')
Simon Glass8c702fb2019-07-20 12:23:57 -06002704 self.assertEqual(b'aaa', data[:3])
2705 self.assertEqual(U_BOOT_DATA, data[3:3 + len(U_BOOT_DATA)])
2706 self.assertEqual(b'aaa', data[-3:])
Simon Glasse61b6f62019-07-08 14:25:37 -06002707
Simon Glass90d29682019-07-08 14:25:38 -06002708 def testCompressDtb(self):
2709 """Test that compress of device-tree files is supported"""
2710 self._CheckLz4()
2711 data = self.data = self._DoReadFileRealDtb('124_compress_dtb.dts')
2712 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
2713 comp_data = data[len(U_BOOT_DATA):]
2714 orig = self._decompress(comp_data)
2715 dtb = fdt.Fdt.FromData(orig)
2716 dtb.Scan()
2717 props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
2718 expected = {
2719 'u-boot:size': len(U_BOOT_DATA),
2720 'u-boot-dtb:uncomp-size': len(orig),
2721 'u-boot-dtb:size': len(comp_data),
2722 'size': len(data),
2723 }
2724 self.assertEqual(expected, props)
2725
Simon Glass151bbbf2019-07-08 14:25:41 -06002726 def testCbfsUpdateFdt(self):
2727 """Test that we can update the device tree with CBFS offset/size info"""
2728 self._CheckLz4()
2729 data, _, _, out_dtb_fname = self._DoReadFileDtb('125_cbfs_update.dts',
2730 update_dtb=True)
2731 dtb = fdt.Fdt(out_dtb_fname)
2732 dtb.Scan()
Simon Glass2c6adba2019-07-20 12:23:47 -06002733 props = self._GetPropTree(dtb, BASE_DTB_PROPS + ['uncomp-size'])
Simon Glass151bbbf2019-07-08 14:25:41 -06002734 del props['cbfs/u-boot:size']
2735 self.assertEqual({
2736 'offset': 0,
2737 'size': len(data),
2738 'image-pos': 0,
2739 'cbfs:offset': 0,
2740 'cbfs:size': len(data),
2741 'cbfs:image-pos': 0,
Simon Glassfa144222023-10-14 14:40:28 -06002742 'cbfs/u-boot:offset': 0x30,
Simon Glass151bbbf2019-07-08 14:25:41 -06002743 'cbfs/u-boot:uncomp-size': len(U_BOOT_DATA),
Simon Glassfa144222023-10-14 14:40:28 -06002744 'cbfs/u-boot:image-pos': 0x30,
2745 'cbfs/u-boot-dtb:offset': 0xa4,
Simon Glass151bbbf2019-07-08 14:25:41 -06002746 'cbfs/u-boot-dtb:size': len(U_BOOT_DATA),
Simon Glassfa144222023-10-14 14:40:28 -06002747 'cbfs/u-boot-dtb:image-pos': 0xa4,
Simon Glass151bbbf2019-07-08 14:25:41 -06002748 }, props)
2749
Simon Glass3c9b4f22019-07-08 14:25:42 -06002750 def testCbfsBadType(self):
2751 """Test an image header with a no specified location is detected"""
2752 with self.assertRaises(ValueError) as e:
2753 self._DoReadFile('126_cbfs_bad_type.dts')
2754 self.assertIn("Unknown cbfs-type 'badtype'", str(e.exception))
2755
Simon Glass6b156f82019-07-08 14:25:43 -06002756 def testList(self):
2757 """Test listing the files in an image"""
2758 self._CheckLz4()
2759 data = self._DoReadFile('127_list.dts')
2760 image = control.images['image']
2761 entries = image.BuildEntryList()
2762 self.assertEqual(7, len(entries))
2763
2764 ent = entries[0]
2765 self.assertEqual(0, ent.indent)
Simon Glass49cd2b32023-02-07 14:34:18 -07002766 self.assertEqual('image', ent.name)
Simon Glass6b156f82019-07-08 14:25:43 -06002767 self.assertEqual('section', ent.etype)
2768 self.assertEqual(len(data), ent.size)
2769 self.assertEqual(0, ent.image_pos)
2770 self.assertEqual(None, ent.uncomp_size)
Simon Glass39dd2152019-07-08 14:25:47 -06002771 self.assertEqual(0, ent.offset)
Simon Glass6b156f82019-07-08 14:25:43 -06002772
2773 ent = entries[1]
2774 self.assertEqual(1, ent.indent)
2775 self.assertEqual('u-boot', ent.name)
2776 self.assertEqual('u-boot', ent.etype)
2777 self.assertEqual(len(U_BOOT_DATA), ent.size)
2778 self.assertEqual(0, ent.image_pos)
2779 self.assertEqual(None, ent.uncomp_size)
2780 self.assertEqual(0, ent.offset)
2781
2782 ent = entries[2]
2783 self.assertEqual(1, ent.indent)
2784 self.assertEqual('section', ent.name)
2785 self.assertEqual('section', ent.etype)
2786 section_size = ent.size
2787 self.assertEqual(0x100, ent.image_pos)
2788 self.assertEqual(None, ent.uncomp_size)
Simon Glass39dd2152019-07-08 14:25:47 -06002789 self.assertEqual(0x100, ent.offset)
Simon Glass6b156f82019-07-08 14:25:43 -06002790
2791 ent = entries[3]
2792 self.assertEqual(2, ent.indent)
2793 self.assertEqual('cbfs', ent.name)
2794 self.assertEqual('cbfs', ent.etype)
2795 self.assertEqual(0x400, ent.size)
2796 self.assertEqual(0x100, ent.image_pos)
2797 self.assertEqual(None, ent.uncomp_size)
2798 self.assertEqual(0, ent.offset)
2799
2800 ent = entries[4]
2801 self.assertEqual(3, ent.indent)
2802 self.assertEqual('u-boot', ent.name)
2803 self.assertEqual('u-boot', ent.etype)
2804 self.assertEqual(len(U_BOOT_DATA), ent.size)
2805 self.assertEqual(0x138, ent.image_pos)
2806 self.assertEqual(None, ent.uncomp_size)
2807 self.assertEqual(0x38, ent.offset)
2808
2809 ent = entries[5]
2810 self.assertEqual(3, ent.indent)
2811 self.assertEqual('u-boot-dtb', ent.name)
2812 self.assertEqual('text', ent.etype)
2813 self.assertGreater(len(COMPRESS_DATA), ent.size)
2814 self.assertEqual(0x178, ent.image_pos)
2815 self.assertEqual(len(COMPRESS_DATA), ent.uncomp_size)
2816 self.assertEqual(0x78, ent.offset)
2817
2818 ent = entries[6]
2819 self.assertEqual(2, ent.indent)
2820 self.assertEqual('u-boot-dtb', ent.name)
2821 self.assertEqual('u-boot-dtb', ent.etype)
2822 self.assertEqual(0x500, ent.image_pos)
2823 self.assertEqual(len(U_BOOT_DTB_DATA), ent.uncomp_size)
2824 dtb_size = ent.size
2825 # Compressing this data expands it since headers are added
2826 self.assertGreater(dtb_size, len(U_BOOT_DTB_DATA))
2827 self.assertEqual(0x400, ent.offset)
2828
2829 self.assertEqual(len(data), 0x100 + section_size)
2830 self.assertEqual(section_size, 0x400 + dtb_size)
2831
Simon Glass8d8bf4e2019-07-08 14:25:44 -06002832 def testFindFdtmap(self):
2833 """Test locating an FDT map in an image"""
2834 self._CheckLz4()
2835 data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
2836 image = control.images['image']
2837 entries = image.GetEntries()
2838 entry = entries['fdtmap']
2839 self.assertEqual(entry.image_pos, fdtmap.LocateFdtmap(data))
2840
2841 def testFindFdtmapMissing(self):
2842 """Test failing to locate an FDP map"""
2843 data = self._DoReadFile('005_simple.dts')
2844 self.assertEqual(None, fdtmap.LocateFdtmap(data))
2845
Simon Glassed39a3c2019-07-08 14:25:45 -06002846 def testFindImageHeader(self):
2847 """Test locating a image header"""
2848 self._CheckLz4()
Simon Glassb8424fa2019-07-08 14:25:46 -06002849 data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
Simon Glassed39a3c2019-07-08 14:25:45 -06002850 image = control.images['image']
2851 entries = image.GetEntries()
2852 entry = entries['fdtmap']
2853 # The header should point to the FDT map
2854 self.assertEqual(entry.image_pos, image_header.LocateHeaderOffset(data))
2855
2856 def testFindImageHeaderStart(self):
2857 """Test locating a image header located at the start of an image"""
Simon Glassb8424fa2019-07-08 14:25:46 -06002858 data = self.data = self._DoReadFileRealDtb('117_fdtmap_hdr_start.dts')
Simon Glassed39a3c2019-07-08 14:25:45 -06002859 image = control.images['image']
2860 entries = image.GetEntries()
2861 entry = entries['fdtmap']
2862 # The header should point to the FDT map
2863 self.assertEqual(entry.image_pos, image_header.LocateHeaderOffset(data))
2864
2865 def testFindImageHeaderMissing(self):
2866 """Test failing to locate an image header"""
2867 data = self._DoReadFile('005_simple.dts')
2868 self.assertEqual(None, image_header.LocateHeaderOffset(data))
2869
Simon Glassb8424fa2019-07-08 14:25:46 -06002870 def testReadImage(self):
2871 """Test reading an image and accessing its FDT map"""
2872 self._CheckLz4()
2873 data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
Simon Glass80025522022-01-29 14:14:04 -07002874 image_fname = tools.get_output_filename('image.bin')
Simon Glassb8424fa2019-07-08 14:25:46 -06002875 orig_image = control.images['image']
2876 image = Image.FromFile(image_fname)
2877 self.assertEqual(orig_image.GetEntries().keys(),
2878 image.GetEntries().keys())
2879
2880 orig_entry = orig_image.GetEntries()['fdtmap']
2881 entry = image.GetEntries()['fdtmap']
Brandon Maiera657bc62024-06-04 16:16:05 +00002882 self.assertEqual(orig_entry.offset, entry.offset)
2883 self.assertEqual(orig_entry.size, entry.size)
2884 self.assertEqual(orig_entry.image_pos, entry.image_pos)
Simon Glassb8424fa2019-07-08 14:25:46 -06002885
2886 def testReadImageNoHeader(self):
2887 """Test accessing an image's FDT map without an image header"""
2888 self._CheckLz4()
2889 data = self._DoReadFileRealDtb('129_decode_image_nohdr.dts')
Simon Glass80025522022-01-29 14:14:04 -07002890 image_fname = tools.get_output_filename('image.bin')
Simon Glassb8424fa2019-07-08 14:25:46 -06002891 image = Image.FromFile(image_fname)
2892 self.assertTrue(isinstance(image, Image))
Simon Glass072959a2019-07-20 12:23:50 -06002893 self.assertEqual('image', image.image_name[-5:])
Simon Glassb8424fa2019-07-08 14:25:46 -06002894
2895 def testReadImageFail(self):
2896 """Test failing to read an image image's FDT map"""
2897 self._DoReadFile('005_simple.dts')
Simon Glass80025522022-01-29 14:14:04 -07002898 image_fname = tools.get_output_filename('image.bin')
Simon Glassb8424fa2019-07-08 14:25:46 -06002899 with self.assertRaises(ValueError) as e:
2900 image = Image.FromFile(image_fname)
2901 self.assertIn("Cannot find FDT map in image", str(e.exception))
Simon Glassc2f1aed2019-07-08 13:18:56 -06002902
Simon Glassb2fd11d2019-07-08 14:25:48 -06002903 def testListCmd(self):
2904 """Test listing the files in an image using an Fdtmap"""
2905 self._CheckLz4()
2906 data = self._DoReadFileRealDtb('130_list_fdtmap.dts')
2907
2908 # lz4 compression size differs depending on the version
2909 image = control.images['image']
2910 entries = image.GetEntries()
2911 section_size = entries['section'].size
2912 fdt_size = entries['section'].GetEntries()['u-boot-dtb'].size
2913 fdtmap_offset = entries['fdtmap'].offset
2914
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01002915 tmpdir = None
Simon Glassb3d6fc72019-07-20 12:24:10 -06002916 try:
2917 tmpdir, updated_fname = self._SetupImageInTmpdir()
Simon Glass14d64e32025-04-29 07:21:59 -06002918 with terminal.capture() as (stdout, stderr):
Simon Glassb3d6fc72019-07-20 12:24:10 -06002919 self._DoBinman('ls', '-i', updated_fname)
2920 finally:
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01002921 if tmpdir:
2922 shutil.rmtree(tmpdir)
Simon Glassb2fd11d2019-07-08 14:25:48 -06002923 lines = stdout.getvalue().splitlines()
2924 expected = [
2925'Name Image-pos Size Entry-type Offset Uncomp-size',
2926'----------------------------------------------------------------------',
Simon Glass49cd2b32023-02-07 14:34:18 -07002927'image 0 c00 section 0',
Simon Glassb2fd11d2019-07-08 14:25:48 -06002928' u-boot 0 4 u-boot 0',
2929' section 100 %x section 100' % section_size,
2930' cbfs 100 400 cbfs 0',
Simon Glassfa144222023-10-14 14:40:28 -06002931' u-boot 120 4 u-boot 20',
Simon Glassc5fd10a2019-10-31 07:43:03 -06002932' u-boot-dtb 180 105 u-boot-dtb 80 3c9',
Simon Glassb2fd11d2019-07-08 14:25:48 -06002933' u-boot-dtb 500 %x u-boot-dtb 400 3c9' % fdt_size,
Simon Glassc5fd10a2019-10-31 07:43:03 -06002934' fdtmap %x 3bd fdtmap %x' %
Simon Glassb2fd11d2019-07-08 14:25:48 -06002935 (fdtmap_offset, fdtmap_offset),
2936' image-header bf8 8 image-header bf8',
2937 ]
2938 self.assertEqual(expected, lines)
2939
2940 def testListCmdFail(self):
2941 """Test failing to list an image"""
2942 self._DoReadFile('005_simple.dts')
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01002943 tmpdir = None
Simon Glassb3d6fc72019-07-20 12:24:10 -06002944 try:
2945 tmpdir, updated_fname = self._SetupImageInTmpdir()
2946 with self.assertRaises(ValueError) as e:
2947 self._DoBinman('ls', '-i', updated_fname)
2948 finally:
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01002949 if tmpdir:
2950 shutil.rmtree(tmpdir)
Simon Glassb2fd11d2019-07-08 14:25:48 -06002951 self.assertIn("Cannot find FDT map in image", str(e.exception))
2952
2953 def _RunListCmd(self, paths, expected):
2954 """List out entries and check the result
2955
2956 Args:
2957 paths: List of paths to pass to the list command
2958 expected: Expected list of filenames to be returned, in order
2959 """
2960 self._CheckLz4()
2961 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07002962 image_fname = tools.get_output_filename('image.bin')
Simon Glassb2fd11d2019-07-08 14:25:48 -06002963 image = Image.FromFile(image_fname)
2964 lines = image.GetListEntries(paths)[1]
2965 files = [line[0].strip() for line in lines[1:]]
2966 self.assertEqual(expected, files)
2967
2968 def testListCmdSection(self):
2969 """Test listing the files in a section"""
2970 self._RunListCmd(['section'],
2971 ['section', 'cbfs', 'u-boot', 'u-boot-dtb', 'u-boot-dtb'])
2972
2973 def testListCmdFile(self):
2974 """Test listing a particular file"""
2975 self._RunListCmd(['*u-boot-dtb'], ['u-boot-dtb', 'u-boot-dtb'])
2976
2977 def testListCmdWildcard(self):
2978 """Test listing a wildcarded file"""
2979 self._RunListCmd(['*boot*'],
2980 ['u-boot', 'u-boot', 'u-boot-dtb', 'u-boot-dtb'])
2981
2982 def testListCmdWildcardMulti(self):
2983 """Test listing a wildcarded file"""
2984 self._RunListCmd(['*cb*', '*head*'],
2985 ['cbfs', 'u-boot', 'u-boot-dtb', 'image-header'])
2986
2987 def testListCmdEmpty(self):
2988 """Test listing a wildcarded file"""
2989 self._RunListCmd(['nothing'], [])
2990
2991 def testListCmdPath(self):
2992 """Test listing the files in a sub-entry of a section"""
2993 self._RunListCmd(['section/cbfs'], ['cbfs', 'u-boot', 'u-boot-dtb'])
2994
Simon Glass4c613bf2019-07-08 14:25:50 -06002995 def _RunExtractCmd(self, entry_name, decomp=True):
2996 """Extract an entry from an image
2997
2998 Args:
2999 entry_name: Entry name to extract
3000 decomp: True to decompress the data if compressed, False to leave
3001 it in its raw uncompressed format
3002
3003 Returns:
3004 data from entry
3005 """
3006 self._CheckLz4()
3007 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07003008 image_fname = tools.get_output_filename('image.bin')
Simon Glass4c613bf2019-07-08 14:25:50 -06003009 return control.ReadEntry(image_fname, entry_name, decomp)
3010
3011 def testExtractSimple(self):
3012 """Test extracting a single file"""
3013 data = self._RunExtractCmd('u-boot')
3014 self.assertEqual(U_BOOT_DATA, data)
3015
Simon Glass980a2842019-07-08 14:25:52 -06003016 def testExtractSection(self):
3017 """Test extracting the files in a section"""
3018 data = self._RunExtractCmd('section')
3019 cbfs_data = data[:0x400]
3020 cbfs = cbfs_util.CbfsReader(cbfs_data)
Simon Glassc5fd10a2019-10-31 07:43:03 -06003021 self.assertEqual(['u-boot', 'u-boot-dtb', ''], list(cbfs.files.keys()))
Simon Glass980a2842019-07-08 14:25:52 -06003022 dtb_data = data[0x400:]
3023 dtb = self._decompress(dtb_data)
3024 self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
3025
3026 def testExtractCompressed(self):
3027 """Test extracting compressed data"""
3028 data = self._RunExtractCmd('section/u-boot-dtb')
3029 self.assertEqual(EXTRACT_DTB_SIZE, len(data))
3030
3031 def testExtractRaw(self):
3032 """Test extracting compressed data without decompressing it"""
3033 data = self._RunExtractCmd('section/u-boot-dtb', decomp=False)
3034 dtb = self._decompress(data)
3035 self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
3036
3037 def testExtractCbfs(self):
3038 """Test extracting CBFS data"""
3039 data = self._RunExtractCmd('section/cbfs/u-boot')
3040 self.assertEqual(U_BOOT_DATA, data)
3041
3042 def testExtractCbfsCompressed(self):
3043 """Test extracting CBFS compressed data"""
3044 data = self._RunExtractCmd('section/cbfs/u-boot-dtb')
3045 self.assertEqual(EXTRACT_DTB_SIZE, len(data))
3046
3047 def testExtractCbfsRaw(self):
3048 """Test extracting CBFS compressed data without decompressing it"""
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +02003049 bintool = self.comp_bintools['lzma_alone']
3050 self._CheckBintool(bintool)
Simon Glass980a2842019-07-08 14:25:52 -06003051 data = self._RunExtractCmd('section/cbfs/u-boot-dtb', decomp=False)
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +02003052 dtb = bintool.decompress(data)
Simon Glass980a2842019-07-08 14:25:52 -06003053 self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
3054
Simon Glass4c613bf2019-07-08 14:25:50 -06003055 def testExtractBadEntry(self):
3056 """Test extracting a bad section path"""
3057 with self.assertRaises(ValueError) as e:
3058 self._RunExtractCmd('section/does-not-exist')
3059 self.assertIn("Entry 'does-not-exist' not found in '/section'",
3060 str(e.exception))
3061
3062 def testExtractMissingFile(self):
3063 """Test extracting file that does not exist"""
3064 with self.assertRaises(IOError) as e:
3065 control.ReadEntry('missing-file', 'name')
3066
3067 def testExtractBadFile(self):
3068 """Test extracting an invalid file"""
3069 fname = os.path.join(self._indir, 'badfile')
Simon Glass80025522022-01-29 14:14:04 -07003070 tools.write_file(fname, b'')
Simon Glass4c613bf2019-07-08 14:25:50 -06003071 with self.assertRaises(ValueError) as e:
3072 control.ReadEntry(fname, 'name')
3073
Simon Glass980a2842019-07-08 14:25:52 -06003074 def testExtractCmd(self):
3075 """Test extracting a file fron an image on the command line"""
3076 self._CheckLz4()
3077 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass980a2842019-07-08 14:25:52 -06003078 fname = os.path.join(self._indir, 'output.extact')
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01003079 tmpdir = None
Simon Glassb3d6fc72019-07-20 12:24:10 -06003080 try:
3081 tmpdir, updated_fname = self._SetupImageInTmpdir()
Simon Glass14d64e32025-04-29 07:21:59 -06003082 with terminal.capture() as (stdout, stderr):
Simon Glassb3d6fc72019-07-20 12:24:10 -06003083 self._DoBinman('extract', '-i', updated_fname, 'u-boot',
3084 '-f', fname)
3085 finally:
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01003086 if tmpdir:
3087 shutil.rmtree(tmpdir)
Simon Glass80025522022-01-29 14:14:04 -07003088 data = tools.read_file(fname)
Simon Glass980a2842019-07-08 14:25:52 -06003089 self.assertEqual(U_BOOT_DATA, data)
3090
3091 def testExtractOneEntry(self):
3092 """Test extracting a single entry fron an image """
3093 self._CheckLz4()
3094 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07003095 image_fname = tools.get_output_filename('image.bin')
Simon Glass980a2842019-07-08 14:25:52 -06003096 fname = os.path.join(self._indir, 'output.extact')
3097 control.ExtractEntries(image_fname, fname, None, ['u-boot'])
Simon Glass80025522022-01-29 14:14:04 -07003098 data = tools.read_file(fname)
Simon Glass980a2842019-07-08 14:25:52 -06003099 self.assertEqual(U_BOOT_DATA, data)
3100
3101 def _CheckExtractOutput(self, decomp):
3102 """Helper to test file output with and without decompression
3103
3104 Args:
3105 decomp: True to decompress entry data, False to output it raw
3106 """
3107 def _CheckPresent(entry_path, expect_data, expect_size=None):
3108 """Check and remove expected file
3109
3110 This checks the data/size of a file and removes the file both from
3111 the outfiles set and from the output directory. Once all files are
3112 processed, both the set and directory should be empty.
3113
3114 Args:
3115 entry_path: Entry path
3116 expect_data: Data to expect in file, or None to skip check
3117 expect_size: Size of data to expect in file, or None to skip
3118 """
3119 path = os.path.join(outdir, entry_path)
Simon Glass80025522022-01-29 14:14:04 -07003120 data = tools.read_file(path)
Simon Glass980a2842019-07-08 14:25:52 -06003121 os.remove(path)
3122 if expect_data:
3123 self.assertEqual(expect_data, data)
3124 elif expect_size:
3125 self.assertEqual(expect_size, len(data))
3126 outfiles.remove(path)
3127
3128 def _CheckDirPresent(name):
3129 """Remove expected directory
3130
3131 This gives an error if the directory does not exist as expected
3132
3133 Args:
3134 name: Name of directory to remove
3135 """
3136 path = os.path.join(outdir, name)
3137 os.rmdir(path)
3138
3139 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07003140 image_fname = tools.get_output_filename('image.bin')
Simon Glass980a2842019-07-08 14:25:52 -06003141 outdir = os.path.join(self._indir, 'extract')
3142 einfos = control.ExtractEntries(image_fname, None, outdir, [], decomp)
3143
3144 # Create a set of all file that were output (should be 9)
3145 outfiles = set()
3146 for root, dirs, files in os.walk(outdir):
3147 outfiles |= set([os.path.join(root, fname) for fname in files])
3148 self.assertEqual(9, len(outfiles))
3149 self.assertEqual(9, len(einfos))
3150
3151 image = control.images['image']
3152 entries = image.GetEntries()
3153
3154 # Check the 9 files in various ways
3155 section = entries['section']
3156 section_entries = section.GetEntries()
3157 cbfs_entries = section_entries['cbfs'].GetEntries()
3158 _CheckPresent('u-boot', U_BOOT_DATA)
3159 _CheckPresent('section/cbfs/u-boot', U_BOOT_DATA)
3160 dtb_len = EXTRACT_DTB_SIZE
3161 if not decomp:
3162 dtb_len = cbfs_entries['u-boot-dtb'].size
3163 _CheckPresent('section/cbfs/u-boot-dtb', None, dtb_len)
3164 if not decomp:
3165 dtb_len = section_entries['u-boot-dtb'].size
3166 _CheckPresent('section/u-boot-dtb', None, dtb_len)
3167
3168 fdtmap = entries['fdtmap']
3169 _CheckPresent('fdtmap', fdtmap.data)
3170 hdr = entries['image-header']
3171 _CheckPresent('image-header', hdr.data)
3172
3173 _CheckPresent('section/root', section.data)
3174 cbfs = section_entries['cbfs']
3175 _CheckPresent('section/cbfs/root', cbfs.data)
Simon Glass80025522022-01-29 14:14:04 -07003176 data = tools.read_file(image_fname)
Simon Glass980a2842019-07-08 14:25:52 -06003177 _CheckPresent('root', data)
3178
3179 # There should be no files left. Remove all the directories to check.
3180 # If there are any files/dirs remaining, one of these checks will fail.
3181 self.assertEqual(0, len(outfiles))
3182 _CheckDirPresent('section/cbfs')
3183 _CheckDirPresent('section')
3184 _CheckDirPresent('')
3185 self.assertFalse(os.path.exists(outdir))
3186
3187 def testExtractAllEntries(self):
3188 """Test extracting all entries"""
3189 self._CheckLz4()
3190 self._CheckExtractOutput(decomp=True)
3191
3192 def testExtractAllEntriesRaw(self):
3193 """Test extracting all entries without decompressing them"""
3194 self._CheckLz4()
3195 self._CheckExtractOutput(decomp=False)
3196
3197 def testExtractSelectedEntries(self):
3198 """Test extracting some entries"""
3199 self._CheckLz4()
3200 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07003201 image_fname = tools.get_output_filename('image.bin')
Simon Glass980a2842019-07-08 14:25:52 -06003202 outdir = os.path.join(self._indir, 'extract')
3203 einfos = control.ExtractEntries(image_fname, None, outdir,
3204 ['*cb*', '*head*'])
3205
3206 # File output is tested by testExtractAllEntries(), so just check that
3207 # the expected entries are selected
3208 names = [einfo.name for einfo in einfos]
3209 self.assertEqual(names,
3210 ['cbfs', 'u-boot', 'u-boot-dtb', 'image-header'])
3211
3212 def testExtractNoEntryPaths(self):
3213 """Test extracting some entries"""
3214 self._CheckLz4()
3215 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07003216 image_fname = tools.get_output_filename('image.bin')
Simon Glass980a2842019-07-08 14:25:52 -06003217 with self.assertRaises(ValueError) as e:
3218 control.ExtractEntries(image_fname, 'fname', None, [])
Simon Glassa772d3f2019-07-20 12:24:14 -06003219 self.assertIn('Must specify an entry path to write with -f',
Simon Glass980a2842019-07-08 14:25:52 -06003220 str(e.exception))
3221
3222 def testExtractTooManyEntryPaths(self):
3223 """Test extracting some entries"""
3224 self._CheckLz4()
3225 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07003226 image_fname = tools.get_output_filename('image.bin')
Simon Glass980a2842019-07-08 14:25:52 -06003227 with self.assertRaises(ValueError) as e:
3228 control.ExtractEntries(image_fname, 'fname', None, ['a', 'b'])
Simon Glassa772d3f2019-07-20 12:24:14 -06003229 self.assertIn('Must specify exactly one entry path to write with -f',
Simon Glass980a2842019-07-08 14:25:52 -06003230 str(e.exception))
3231
Simon Glass52d06212019-07-08 14:25:53 -06003232 def testPackAlignSection(self):
3233 """Test that sections can have alignment"""
3234 self._DoReadFile('131_pack_align_section.dts')
3235
3236 self.assertIn('image', control.images)
3237 image = control.images['image']
3238 entries = image.GetEntries()
3239 self.assertEqual(3, len(entries))
3240
3241 # First u-boot
3242 self.assertIn('u-boot', entries)
3243 entry = entries['u-boot']
3244 self.assertEqual(0, entry.offset)
3245 self.assertEqual(0, entry.image_pos)
3246 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3247 self.assertEqual(len(U_BOOT_DATA), entry.size)
3248
3249 # Section0
3250 self.assertIn('section0', entries)
3251 section0 = entries['section0']
3252 self.assertEqual(0x10, section0.offset)
3253 self.assertEqual(0x10, section0.image_pos)
3254 self.assertEqual(len(U_BOOT_DATA), section0.size)
3255
3256 # Second u-boot
3257 section_entries = section0.GetEntries()
3258 self.assertIn('u-boot', section_entries)
3259 entry = section_entries['u-boot']
3260 self.assertEqual(0, entry.offset)
3261 self.assertEqual(0x10, entry.image_pos)
3262 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3263 self.assertEqual(len(U_BOOT_DATA), entry.size)
3264
3265 # Section1
3266 self.assertIn('section1', entries)
3267 section1 = entries['section1']
3268 self.assertEqual(0x14, section1.offset)
3269 self.assertEqual(0x14, section1.image_pos)
3270 self.assertEqual(0x20, section1.size)
3271
3272 # Second u-boot
3273 section_entries = section1.GetEntries()
3274 self.assertIn('u-boot', section_entries)
3275 entry = section_entries['u-boot']
3276 self.assertEqual(0, entry.offset)
3277 self.assertEqual(0x14, entry.image_pos)
3278 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3279 self.assertEqual(len(U_BOOT_DATA), entry.size)
3280
3281 # Section2
3282 self.assertIn('section2', section_entries)
3283 section2 = section_entries['section2']
3284 self.assertEqual(0x4, section2.offset)
3285 self.assertEqual(0x18, section2.image_pos)
3286 self.assertEqual(4, section2.size)
3287
3288 # Third u-boot
3289 section_entries = section2.GetEntries()
3290 self.assertIn('u-boot', section_entries)
3291 entry = section_entries['u-boot']
3292 self.assertEqual(0, entry.offset)
3293 self.assertEqual(0x18, entry.image_pos)
3294 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3295 self.assertEqual(len(U_BOOT_DATA), entry.size)
3296
Simon Glassf8a54bc2019-07-20 12:23:56 -06003297 def _RunReplaceCmd(self, entry_name, data, decomp=True, allow_resize=True,
3298 dts='132_replace.dts'):
Simon Glass072959a2019-07-20 12:23:50 -06003299 """Replace an entry in an image
3300
3301 This writes the entry data to update it, then opens the updated file and
3302 returns the value that it now finds there.
3303
3304 Args:
3305 entry_name: Entry name to replace
3306 data: Data to replace it with
3307 decomp: True to compress the data if needed, False if data is
3308 already compressed so should be used as is
Simon Glassf8a54bc2019-07-20 12:23:56 -06003309 allow_resize: True to allow entries to change size, False to raise
3310 an exception
Simon Glass072959a2019-07-20 12:23:50 -06003311
3312 Returns:
3313 Tuple:
3314 data from entry
3315 data from fdtmap (excluding header)
Simon Glassf8a54bc2019-07-20 12:23:56 -06003316 Image object that was modified
Simon Glass072959a2019-07-20 12:23:50 -06003317 """
Simon Glassf8a54bc2019-07-20 12:23:56 -06003318 dtb_data = self._DoReadFileDtb(dts, use_real_dtb=True,
Simon Glass072959a2019-07-20 12:23:50 -06003319 update_dtb=True)[1]
3320
3321 self.assertIn('image', control.images)
3322 image = control.images['image']
3323 entries = image.GetEntries()
3324 orig_dtb_data = entries['u-boot-dtb'].data
3325 orig_fdtmap_data = entries['fdtmap'].data
3326
Simon Glass80025522022-01-29 14:14:04 -07003327 image_fname = tools.get_output_filename('image.bin')
3328 updated_fname = tools.get_output_filename('image-updated.bin')
3329 tools.write_file(updated_fname, tools.read_file(image_fname))
Simon Glassf8a54bc2019-07-20 12:23:56 -06003330 image = control.WriteEntry(updated_fname, entry_name, data, decomp,
3331 allow_resize)
Simon Glass072959a2019-07-20 12:23:50 -06003332 data = control.ReadEntry(updated_fname, entry_name, decomp)
3333
Simon Glassf8a54bc2019-07-20 12:23:56 -06003334 # The DT data should not change unless resized:
3335 if not allow_resize:
3336 new_dtb_data = entries['u-boot-dtb'].data
3337 self.assertEqual(new_dtb_data, orig_dtb_data)
3338 new_fdtmap_data = entries['fdtmap'].data
3339 self.assertEqual(new_fdtmap_data, orig_fdtmap_data)
Simon Glass072959a2019-07-20 12:23:50 -06003340
Simon Glassf8a54bc2019-07-20 12:23:56 -06003341 return data, orig_fdtmap_data[fdtmap.FDTMAP_HDR_LEN:], image
Simon Glass072959a2019-07-20 12:23:50 -06003342
3343 def testReplaceSimple(self):
3344 """Test replacing a single file"""
3345 expected = b'x' * len(U_BOOT_DATA)
Simon Glassf8a54bc2019-07-20 12:23:56 -06003346 data, expected_fdtmap, _ = self._RunReplaceCmd('u-boot', expected,
3347 allow_resize=False)
Simon Glass072959a2019-07-20 12:23:50 -06003348 self.assertEqual(expected, data)
3349
3350 # Test that the state looks right. There should be an FDT for the fdtmap
3351 # that we jsut read back in, and it should match what we find in the
3352 # 'control' tables. Checking for an FDT that does not exist should
3353 # return None.
3354 path, fdtmap = state.GetFdtContents('fdtmap')
Simon Glassf8a54bc2019-07-20 12:23:56 -06003355 self.assertIsNotNone(path)
Simon Glass072959a2019-07-20 12:23:50 -06003356 self.assertEqual(expected_fdtmap, fdtmap)
3357
3358 dtb = state.GetFdtForEtype('fdtmap')
3359 self.assertEqual(dtb.GetContents(), fdtmap)
3360
3361 missing_path, missing_fdtmap = state.GetFdtContents('missing')
3362 self.assertIsNone(missing_path)
3363 self.assertIsNone(missing_fdtmap)
3364
3365 missing_dtb = state.GetFdtForEtype('missing')
3366 self.assertIsNone(missing_dtb)
3367
3368 self.assertEqual('/binman', state.fdt_path_prefix)
3369
3370 def testReplaceResizeFail(self):
3371 """Test replacing a file by something larger"""
3372 expected = U_BOOT_DATA + b'x'
3373 with self.assertRaises(ValueError) as e:
Simon Glassf8a54bc2019-07-20 12:23:56 -06003374 self._RunReplaceCmd('u-boot', expected, allow_resize=False,
3375 dts='139_replace_repack.dts')
Simon Glass072959a2019-07-20 12:23:50 -06003376 self.assertIn("Node '/u-boot': Entry data size does not match, but resize is disabled",
3377 str(e.exception))
3378
3379 def testReplaceMulti(self):
3380 """Test replacing entry data where multiple images are generated"""
3381 data = self._DoReadFileDtb('133_replace_multi.dts', use_real_dtb=True,
3382 update_dtb=True)[0]
3383 expected = b'x' * len(U_BOOT_DATA)
Simon Glass80025522022-01-29 14:14:04 -07003384 updated_fname = tools.get_output_filename('image-updated.bin')
3385 tools.write_file(updated_fname, data)
Simon Glass072959a2019-07-20 12:23:50 -06003386 entry_name = 'u-boot'
Simon Glassf8a54bc2019-07-20 12:23:56 -06003387 control.WriteEntry(updated_fname, entry_name, expected,
3388 allow_resize=False)
Simon Glass072959a2019-07-20 12:23:50 -06003389 data = control.ReadEntry(updated_fname, entry_name)
3390 self.assertEqual(expected, data)
3391
3392 # Check the state looks right.
3393 self.assertEqual('/binman/image', state.fdt_path_prefix)
3394
3395 # Now check we can write the first image
Simon Glass80025522022-01-29 14:14:04 -07003396 image_fname = tools.get_output_filename('first-image.bin')
3397 updated_fname = tools.get_output_filename('first-updated.bin')
3398 tools.write_file(updated_fname, tools.read_file(image_fname))
Simon Glass072959a2019-07-20 12:23:50 -06003399 entry_name = 'u-boot'
Simon Glassf8a54bc2019-07-20 12:23:56 -06003400 control.WriteEntry(updated_fname, entry_name, expected,
3401 allow_resize=False)
Simon Glass072959a2019-07-20 12:23:50 -06003402 data = control.ReadEntry(updated_fname, entry_name)
3403 self.assertEqual(expected, data)
3404
3405 # Check the state looks right.
3406 self.assertEqual('/binman/first-image', state.fdt_path_prefix)
Simon Glass39dd2152019-07-08 14:25:47 -06003407
Simon Glassfb30e292019-07-20 12:23:51 -06003408 def testUpdateFdtAllRepack(self):
3409 """Test that all device trees are updated with offset/size info"""
Marek Vasutf7413f02023-07-18 07:23:58 -06003410 self._SetupSplElf()
3411 self._SetupTplElf()
Simon Glassfb30e292019-07-20 12:23:51 -06003412 data = self._DoReadFileRealDtb('134_fdt_update_all_repack.dts')
3413 SECTION_SIZE = 0x300
3414 DTB_SIZE = 602
3415 FDTMAP_SIZE = 608
3416 base_expected = {
3417 'offset': 0,
3418 'size': SECTION_SIZE + DTB_SIZE * 2 + FDTMAP_SIZE,
3419 'image-pos': 0,
3420 'section:offset': 0,
3421 'section:size': SECTION_SIZE,
3422 'section:image-pos': 0,
3423 'section/u-boot-dtb:offset': 4,
3424 'section/u-boot-dtb:size': 636,
3425 'section/u-boot-dtb:image-pos': 4,
3426 'u-boot-spl-dtb:offset': SECTION_SIZE,
3427 'u-boot-spl-dtb:size': DTB_SIZE,
3428 'u-boot-spl-dtb:image-pos': SECTION_SIZE,
3429 'u-boot-tpl-dtb:offset': SECTION_SIZE + DTB_SIZE,
3430 'u-boot-tpl-dtb:image-pos': SECTION_SIZE + DTB_SIZE,
3431 'u-boot-tpl-dtb:size': DTB_SIZE,
3432 'fdtmap:offset': SECTION_SIZE + DTB_SIZE * 2,
3433 'fdtmap:size': FDTMAP_SIZE,
3434 'fdtmap:image-pos': SECTION_SIZE + DTB_SIZE * 2,
3435 }
3436 main_expected = {
3437 'section:orig-size': SECTION_SIZE,
3438 'section/u-boot-dtb:orig-offset': 4,
3439 }
3440
3441 # We expect three device-tree files in the output, with the first one
3442 # within a fixed-size section.
3443 # Read them in sequence. We look for an 'spl' property in the SPL tree,
3444 # and 'tpl' in the TPL tree, to make sure they are distinct from the
3445 # main U-Boot tree. All three should have the same positions and offset
3446 # except that the main tree should include the main_expected properties
3447 start = 4
3448 for item in ['', 'spl', 'tpl', None]:
3449 if item is None:
3450 start += 16 # Move past fdtmap header
3451 dtb = fdt.Fdt.FromData(data[start:])
3452 dtb.Scan()
3453 props = self._GetPropTree(dtb,
3454 BASE_DTB_PROPS + REPACK_DTB_PROPS + ['spl', 'tpl'],
3455 prefix='/' if item is None else '/binman/')
3456 expected = dict(base_expected)
3457 if item:
3458 expected[item] = 0
3459 else:
3460 # Main DTB and fdtdec should include the 'orig-' properties
3461 expected.update(main_expected)
3462 # Helpful for debugging:
3463 #for prop in sorted(props):
3464 #print('prop %s %s %s' % (prop, props[prop], expected[prop]))
3465 self.assertEqual(expected, props)
3466 if item == '':
3467 start = SECTION_SIZE
3468 else:
3469 start += dtb._fdt_obj.totalsize()
3470
Simon Glass11453762019-07-20 12:23:55 -06003471 def testFdtmapHeaderMiddle(self):
3472 """Test an FDT map in the middle of an image when it should be at end"""
3473 with self.assertRaises(ValueError) as e:
3474 self._DoReadFileRealDtb('135_fdtmap_hdr_middle.dts')
3475 self.assertIn("Invalid sibling order 'middle' for image-header: Must be at 'end' to match location",
3476 str(e.exception))
3477
3478 def testFdtmapHeaderStartBad(self):
3479 """Test an FDT map in middle of an image when it should be at start"""
3480 with self.assertRaises(ValueError) as e:
3481 self._DoReadFileRealDtb('136_fdtmap_hdr_startbad.dts')
3482 self.assertIn("Invalid sibling order 'end' for image-header: Must be at 'start' to match location",
3483 str(e.exception))
3484
3485 def testFdtmapHeaderEndBad(self):
3486 """Test an FDT map at the start of an image when it should be at end"""
3487 with self.assertRaises(ValueError) as e:
3488 self._DoReadFileRealDtb('137_fdtmap_hdr_endbad.dts')
3489 self.assertIn("Invalid sibling order 'start' for image-header: Must be at 'end' to match location",
3490 str(e.exception))
3491
3492 def testFdtmapHeaderNoSize(self):
3493 """Test an image header at the end of an image with undefined size"""
3494 self._DoReadFileRealDtb('138_fdtmap_hdr_nosize.dts')
3495
Simon Glassf8a54bc2019-07-20 12:23:56 -06003496 def testReplaceResize(self):
3497 """Test replacing a single file in an entry with a larger file"""
3498 expected = U_BOOT_DATA + b'x'
3499 data, _, image = self._RunReplaceCmd('u-boot', expected,
3500 dts='139_replace_repack.dts')
3501 self.assertEqual(expected, data)
3502
3503 entries = image.GetEntries()
3504 dtb_data = entries['u-boot-dtb'].data
3505 dtb = fdt.Fdt.FromData(dtb_data)
3506 dtb.Scan()
3507
3508 # The u-boot section should now be larger in the dtb
3509 node = dtb.GetNode('/binman/u-boot')
3510 self.assertEqual(len(expected), fdt_util.GetInt(node, 'size'))
3511
3512 # Same for the fdtmap
3513 fdata = entries['fdtmap'].data
3514 fdtb = fdt.Fdt.FromData(fdata[fdtmap.FDTMAP_HDR_LEN:])
3515 fdtb.Scan()
3516 fnode = fdtb.GetNode('/u-boot')
3517 self.assertEqual(len(expected), fdt_util.GetInt(fnode, 'size'))
3518
3519 def testReplaceResizeNoRepack(self):
3520 """Test replacing an entry with a larger file when not allowed"""
3521 expected = U_BOOT_DATA + b'x'
3522 with self.assertRaises(ValueError) as e:
3523 self._RunReplaceCmd('u-boot', expected)
3524 self.assertIn('Entry data size does not match, but allow-repack is not present for this image',
3525 str(e.exception))
3526
Simon Glass9d8ee322019-07-20 12:23:58 -06003527 def testEntryShrink(self):
3528 """Test contracting an entry after it is packed"""
3529 try:
3530 state.SetAllowEntryContraction(True)
3531 data = self._DoReadFileDtb('140_entry_shrink.dts',
3532 update_dtb=True)[0]
3533 finally:
3534 state.SetAllowEntryContraction(False)
3535 self.assertEqual(b'a', data[:1])
3536 self.assertEqual(U_BOOT_DATA, data[1:1 + len(U_BOOT_DATA)])
3537 self.assertEqual(b'a', data[-1:])
3538
3539 def testEntryShrinkFail(self):
3540 """Test not being allowed to contract an entry after it is packed"""
3541 data = self._DoReadFileDtb('140_entry_shrink.dts', update_dtb=True)[0]
3542
3543 # In this case there is a spare byte at the end of the data. The size of
3544 # the contents is only 1 byte but we still have the size before it
3545 # shrunk.
3546 self.assertEqual(b'a\0', data[:2])
3547 self.assertEqual(U_BOOT_DATA, data[2:2 + len(U_BOOT_DATA)])
3548 self.assertEqual(b'a\0', data[-2:])
3549
Simon Glass70e32982019-07-20 12:24:01 -06003550 def testDescriptorOffset(self):
3551 """Test that the Intel descriptor is always placed at at the start"""
3552 data = self._DoReadFileDtb('141_descriptor_offset.dts')
3553 image = control.images['image']
3554 entries = image.GetEntries()
3555 desc = entries['intel-descriptor']
Simon Glassed836ac2025-02-26 09:26:17 -07003556 self.assertEqual(0xff800000, desc.offset)
3557 self.assertEqual(0xff800000, desc.image_pos)
Simon Glass70e32982019-07-20 12:24:01 -06003558
Simon Glass37fdd142019-07-20 12:24:06 -06003559 def testReplaceCbfs(self):
3560 """Test replacing a single file in CBFS without changing the size"""
3561 self._CheckLz4()
3562 expected = b'x' * len(U_BOOT_DATA)
3563 data = self._DoReadFileRealDtb('142_replace_cbfs.dts')
Simon Glass80025522022-01-29 14:14:04 -07003564 updated_fname = tools.get_output_filename('image-updated.bin')
3565 tools.write_file(updated_fname, data)
Simon Glass37fdd142019-07-20 12:24:06 -06003566 entry_name = 'section/cbfs/u-boot'
3567 control.WriteEntry(updated_fname, entry_name, expected,
3568 allow_resize=True)
3569 data = control.ReadEntry(updated_fname, entry_name)
3570 self.assertEqual(expected, data)
3571
3572 def testReplaceResizeCbfs(self):
3573 """Test replacing a single file in CBFS with one of a different size"""
3574 self._CheckLz4()
3575 expected = U_BOOT_DATA + b'x'
3576 data = self._DoReadFileRealDtb('142_replace_cbfs.dts')
Simon Glass80025522022-01-29 14:14:04 -07003577 updated_fname = tools.get_output_filename('image-updated.bin')
3578 tools.write_file(updated_fname, data)
Simon Glass37fdd142019-07-20 12:24:06 -06003579 entry_name = 'section/cbfs/u-boot'
3580 control.WriteEntry(updated_fname, entry_name, expected,
3581 allow_resize=True)
3582 data = control.ReadEntry(updated_fname, entry_name)
3583 self.assertEqual(expected, data)
3584
Simon Glass30033c22019-07-20 12:24:15 -06003585 def _SetupForReplace(self):
3586 """Set up some files to use to replace entries
3587
3588 This generates an image, copies it to a new file, extracts all the files
3589 in it and updates some of them
3590
3591 Returns:
3592 List
3593 Image filename
3594 Output directory
3595 Expected values for updated entries, each a string
3596 """
3597 data = self._DoReadFileRealDtb('143_replace_all.dts')
3598
Simon Glass80025522022-01-29 14:14:04 -07003599 updated_fname = tools.get_output_filename('image-updated.bin')
3600 tools.write_file(updated_fname, data)
Simon Glass30033c22019-07-20 12:24:15 -06003601
3602 outdir = os.path.join(self._indir, 'extract')
3603 einfos = control.ExtractEntries(updated_fname, None, outdir, [])
3604
3605 expected1 = b'x' + U_BOOT_DATA + b'y'
3606 u_boot_fname1 = os.path.join(outdir, 'u-boot')
Simon Glass80025522022-01-29 14:14:04 -07003607 tools.write_file(u_boot_fname1, expected1)
Simon Glass30033c22019-07-20 12:24:15 -06003608
3609 expected2 = b'a' + U_BOOT_DATA + b'b'
3610 u_boot_fname2 = os.path.join(outdir, 'u-boot2')
Simon Glass80025522022-01-29 14:14:04 -07003611 tools.write_file(u_boot_fname2, expected2)
Simon Glass30033c22019-07-20 12:24:15 -06003612
3613 expected_text = b'not the same text'
3614 text_fname = os.path.join(outdir, 'text')
Simon Glass80025522022-01-29 14:14:04 -07003615 tools.write_file(text_fname, expected_text)
Simon Glass30033c22019-07-20 12:24:15 -06003616
3617 dtb_fname = os.path.join(outdir, 'u-boot-dtb')
3618 dtb = fdt.FdtScan(dtb_fname)
3619 node = dtb.GetNode('/binman/text')
3620 node.AddString('my-property', 'the value')
3621 dtb.Sync(auto_resize=True)
3622 dtb.Flush()
3623
3624 return updated_fname, outdir, expected1, expected2, expected_text
3625
3626 def _CheckReplaceMultiple(self, entry_paths):
3627 """Handle replacing the contents of multiple entries
3628
3629 Args:
3630 entry_paths: List of entry paths to replace
3631
3632 Returns:
3633 List
3634 Dict of entries in the image:
3635 key: Entry name
3636 Value: Entry object
3637 Expected values for updated entries, each a string
3638 """
3639 updated_fname, outdir, expected1, expected2, expected_text = (
3640 self._SetupForReplace())
3641 control.ReplaceEntries(updated_fname, None, outdir, entry_paths)
3642
3643 image = Image.FromFile(updated_fname)
3644 image.LoadData()
3645 return image.GetEntries(), expected1, expected2, expected_text
3646
3647 def testReplaceAll(self):
3648 """Test replacing the contents of all entries"""
3649 entries, expected1, expected2, expected_text = (
3650 self._CheckReplaceMultiple([]))
3651 data = entries['u-boot'].data
3652 self.assertEqual(expected1, data)
3653
3654 data = entries['u-boot2'].data
3655 self.assertEqual(expected2, data)
3656
3657 data = entries['text'].data
3658 self.assertEqual(expected_text, data)
3659
3660 # Check that the device tree is updated
3661 data = entries['u-boot-dtb'].data
3662 dtb = fdt.Fdt.FromData(data)
3663 dtb.Scan()
3664 node = dtb.GetNode('/binman/text')
3665 self.assertEqual('the value', node.props['my-property'].value)
3666
3667 def testReplaceSome(self):
3668 """Test replacing the contents of a few entries"""
3669 entries, expected1, expected2, expected_text = (
3670 self._CheckReplaceMultiple(['u-boot2', 'text']))
3671
3672 # This one should not change
3673 data = entries['u-boot'].data
3674 self.assertEqual(U_BOOT_DATA, data)
3675
3676 data = entries['u-boot2'].data
3677 self.assertEqual(expected2, data)
3678
3679 data = entries['text'].data
3680 self.assertEqual(expected_text, data)
3681
3682 def testReplaceCmd(self):
3683 """Test replacing a file fron an image on the command line"""
3684 self._DoReadFileRealDtb('143_replace_all.dts')
3685
3686 try:
3687 tmpdir, updated_fname = self._SetupImageInTmpdir()
3688
3689 fname = os.path.join(tmpdir, 'update-u-boot.bin')
3690 expected = b'x' * len(U_BOOT_DATA)
Simon Glass80025522022-01-29 14:14:04 -07003691 tools.write_file(fname, expected)
Simon Glass30033c22019-07-20 12:24:15 -06003692
3693 self._DoBinman('replace', '-i', updated_fname, 'u-boot', '-f', fname)
Simon Glass80025522022-01-29 14:14:04 -07003694 data = tools.read_file(updated_fname)
Simon Glass30033c22019-07-20 12:24:15 -06003695 self.assertEqual(expected, data[:len(expected)])
3696 map_fname = os.path.join(tmpdir, 'image-updated.map')
3697 self.assertFalse(os.path.exists(map_fname))
3698 finally:
3699 shutil.rmtree(tmpdir)
3700
3701 def testReplaceCmdSome(self):
3702 """Test replacing some files fron an image on the command line"""
3703 updated_fname, outdir, expected1, expected2, expected_text = (
3704 self._SetupForReplace())
3705
3706 self._DoBinman('replace', '-i', updated_fname, '-I', outdir,
3707 'u-boot2', 'text')
3708
Simon Glass80025522022-01-29 14:14:04 -07003709 tools.prepare_output_dir(None)
Simon Glass30033c22019-07-20 12:24:15 -06003710 image = Image.FromFile(updated_fname)
3711 image.LoadData()
3712 entries = image.GetEntries()
3713
3714 # This one should not change
3715 data = entries['u-boot'].data
3716 self.assertEqual(U_BOOT_DATA, data)
3717
3718 data = entries['u-boot2'].data
3719 self.assertEqual(expected2, data)
3720
3721 data = entries['text'].data
3722 self.assertEqual(expected_text, data)
3723
3724 def testReplaceMissing(self):
3725 """Test replacing entries where the file is missing"""
3726 updated_fname, outdir, expected1, expected2, expected_text = (
3727 self._SetupForReplace())
3728
3729 # Remove one of the files, to generate a warning
3730 u_boot_fname1 = os.path.join(outdir, 'u-boot')
3731 os.remove(u_boot_fname1)
3732
Simon Glass14d64e32025-04-29 07:21:59 -06003733 with terminal.capture() as (stdout, stderr):
Simon Glass30033c22019-07-20 12:24:15 -06003734 control.ReplaceEntries(updated_fname, None, outdir, [])
3735 self.assertIn("Skipping entry '/u-boot' from missing file",
Simon Glass6e02f7c2020-07-09 18:39:39 -06003736 stderr.getvalue())
Simon Glass30033c22019-07-20 12:24:15 -06003737
3738 def testReplaceCmdMap(self):
3739 """Test replacing a file fron an image on the command line"""
3740 self._DoReadFileRealDtb('143_replace_all.dts')
3741
3742 try:
3743 tmpdir, updated_fname = self._SetupImageInTmpdir()
3744
3745 fname = os.path.join(self._indir, 'update-u-boot.bin')
3746 expected = b'x' * len(U_BOOT_DATA)
Simon Glass80025522022-01-29 14:14:04 -07003747 tools.write_file(fname, expected)
Simon Glass30033c22019-07-20 12:24:15 -06003748
3749 self._DoBinman('replace', '-i', updated_fname, 'u-boot',
3750 '-f', fname, '-m')
3751 map_fname = os.path.join(tmpdir, 'image-updated.map')
3752 self.assertTrue(os.path.exists(map_fname))
3753 finally:
3754 shutil.rmtree(tmpdir)
3755
3756 def testReplaceNoEntryPaths(self):
3757 """Test replacing an entry without an entry path"""
3758 self._DoReadFileRealDtb('143_replace_all.dts')
Simon Glass80025522022-01-29 14:14:04 -07003759 image_fname = tools.get_output_filename('image.bin')
Simon Glass30033c22019-07-20 12:24:15 -06003760 with self.assertRaises(ValueError) as e:
3761 control.ReplaceEntries(image_fname, 'fname', None, [])
3762 self.assertIn('Must specify an entry path to read with -f',
3763 str(e.exception))
3764
3765 def testReplaceTooManyEntryPaths(self):
3766 """Test extracting some entries"""
3767 self._DoReadFileRealDtb('143_replace_all.dts')
Simon Glass80025522022-01-29 14:14:04 -07003768 image_fname = tools.get_output_filename('image.bin')
Simon Glass30033c22019-07-20 12:24:15 -06003769 with self.assertRaises(ValueError) as e:
3770 control.ReplaceEntries(image_fname, 'fname', None, ['a', 'b'])
3771 self.assertIn('Must specify exactly one entry path to write with -f',
3772 str(e.exception))
3773
Simon Glass0b074d62019-08-24 07:22:48 -06003774 def testPackReset16(self):
3775 """Test that an image with an x86 reset16 region can be created"""
3776 data = self._DoReadFile('144_x86_reset16.dts')
3777 self.assertEqual(X86_RESET16_DATA, data[:len(X86_RESET16_DATA)])
3778
3779 def testPackReset16Spl(self):
3780 """Test that an image with an x86 reset16-spl region can be created"""
3781 data = self._DoReadFile('145_x86_reset16_spl.dts')
3782 self.assertEqual(X86_RESET16_SPL_DATA, data[:len(X86_RESET16_SPL_DATA)])
3783
3784 def testPackReset16Tpl(self):
3785 """Test that an image with an x86 reset16-tpl region can be created"""
3786 data = self._DoReadFile('146_x86_reset16_tpl.dts')
3787 self.assertEqual(X86_RESET16_TPL_DATA, data[:len(X86_RESET16_TPL_DATA)])
3788
Simon Glass232f90c2019-08-24 07:22:50 -06003789 def testPackIntelFit(self):
3790 """Test that an image with an Intel FIT and pointer can be created"""
3791 data = self._DoReadFile('147_intel_fit.dts')
3792 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
3793 fit = data[16:32];
3794 self.assertEqual(b'_FIT_ \x01\x00\x00\x00\x00\x01\x80}' , fit)
3795 ptr = struct.unpack('<i', data[0x40:0x44])[0]
3796
3797 image = control.images['image']
3798 entries = image.GetEntries()
Simon Glassed836ac2025-02-26 09:26:17 -07003799 expected_ptr = entries['intel-fit'].image_pos #- (1 << 32)
3800 self.assertEqual(expected_ptr, ptr + (1 << 32))
Simon Glass232f90c2019-08-24 07:22:50 -06003801
3802 def testPackIntelFitMissing(self):
3803 """Test detection of a FIT pointer with not FIT region"""
3804 with self.assertRaises(ValueError) as e:
3805 self._DoReadFile('148_intel_fit_missing.dts')
3806 self.assertIn("'intel-fit-ptr' section must have an 'intel-fit' sibling",
3807 str(e.exception))
3808
Simon Glass72555fa2019-11-06 17:22:44 -07003809 def _CheckSymbolsTplSection(self, dts, expected_vals):
3810 data = self._DoReadFile(dts)
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003811 sym_values = struct.pack('<LLQLL', elf.BINMAN_SYM_MAGIC_VALUE, *expected_vals)
Simon Glass3eb5b202019-08-24 07:23:00 -06003812 upto1 = 4 + len(U_BOOT_SPL_DATA)
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003813 expected1 = tools.get_bytes(0xff, 4) + sym_values + U_BOOT_SPL_DATA[24:]
Simon Glass3eb5b202019-08-24 07:23:00 -06003814 self.assertEqual(expected1, data[:upto1])
3815
3816 upto2 = upto1 + 1 + len(U_BOOT_SPL_DATA)
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003817 expected2 = tools.get_bytes(0xff, 1) + sym_values + U_BOOT_SPL_DATA[24:]
Simon Glass3eb5b202019-08-24 07:23:00 -06003818 self.assertEqual(expected2, data[upto1:upto2])
3819
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003820 upto3 = 0x3c + len(U_BOOT_DATA)
Simon Glass80025522022-01-29 14:14:04 -07003821 expected3 = tools.get_bytes(0xff, 1) + U_BOOT_DATA
Simon Glass3eb5b202019-08-24 07:23:00 -06003822 self.assertEqual(expected3, data[upto2:upto3])
3823
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003824 expected4 = sym_values + U_BOOT_TPL_DATA[24:]
Simon Glass72555fa2019-11-06 17:22:44 -07003825 self.assertEqual(expected4, data[upto3:upto3 + len(U_BOOT_TPL_DATA)])
3826
3827 def testSymbolsTplSection(self):
3828 """Test binman can assign symbols embedded in U-Boot TPL in a section"""
3829 self._SetupSplElf('u_boot_binman_syms')
3830 self._SetupTplElf('u_boot_binman_syms')
3831 self._CheckSymbolsTplSection('149_symbols_tpl.dts',
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003832 [0x04, 0x20, 0x10 + 0x3c, 0x04])
Simon Glass72555fa2019-11-06 17:22:44 -07003833
3834 def testSymbolsTplSectionX86(self):
3835 """Test binman can assign symbols in a section with end-at-4gb"""
3836 self._SetupSplElf('u_boot_binman_syms_x86')
3837 self._SetupTplElf('u_boot_binman_syms_x86')
3838 self._CheckSymbolsTplSection('155_symbols_tpl_x86.dts',
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003839 [0xffffff04, 0xffffff20, 0xffffff3c,
Simon Glass72555fa2019-11-06 17:22:44 -07003840 0x04])
Simon Glass3eb5b202019-08-24 07:23:00 -06003841
Simon Glass98c59572019-08-24 07:23:03 -06003842 def testPackX86RomIfwiSectiom(self):
3843 """Test that a section can be placed in an IFWI region"""
3844 self._SetupIfwi('fitimage.bin')
3845 data = self._DoReadFile('151_x86_rom_ifwi_section.dts')
3846 self._CheckIfwi(data)
3847
Simon Glassba7985d2019-08-24 07:23:07 -06003848 def testPackFspM(self):
3849 """Test that an image with a FSP memory-init binary can be created"""
3850 data = self._DoReadFile('152_intel_fsp_m.dts')
3851 self.assertEqual(FSP_M_DATA, data[:len(FSP_M_DATA)])
3852
Simon Glass4d9086d2019-10-20 21:31:35 -06003853 def testPackFspS(self):
3854 """Test that an image with a FSP silicon-init binary can be created"""
3855 data = self._DoReadFile('153_intel_fsp_s.dts')
3856 self.assertEqual(FSP_S_DATA, data[:len(FSP_S_DATA)])
Simon Glassba7985d2019-08-24 07:23:07 -06003857
Simon Glass9ea87b22019-10-20 21:31:36 -06003858 def testPackFspT(self):
3859 """Test that an image with a FSP temp-ram-init binary can be created"""
3860 data = self._DoReadFile('154_intel_fsp_t.dts')
3861 self.assertEqual(FSP_T_DATA, data[:len(FSP_T_DATA)])
3862
Simon Glass48f3aad2020-07-09 18:39:31 -06003863 def testMkimage(self):
3864 """Test using mkimage to build an image"""
Marek Vasutf7413f02023-07-18 07:23:58 -06003865 self._SetupSplElf()
Simon Glass48f3aad2020-07-09 18:39:31 -06003866 data = self._DoReadFile('156_mkimage.dts')
3867
3868 # Just check that the data appears in the file somewhere
3869 self.assertIn(U_BOOT_SPL_DATA, data)
3870
Simon Glass66152ce2022-01-09 20:14:09 -07003871 def testMkimageMissing(self):
3872 """Test that binman still produces an image if mkimage is missing"""
Marek Vasutf7413f02023-07-18 07:23:58 -06003873 self._SetupSplElf()
Simon Glass14d64e32025-04-29 07:21:59 -06003874 with terminal.capture() as (_, stderr):
Simon Glass66152ce2022-01-09 20:14:09 -07003875 self._DoTestFile('156_mkimage.dts',
3876 force_missing_bintools='mkimage')
3877 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07003878 self.assertRegex(err, "Image 'image'.*missing bintools.*: mkimage")
Simon Glass66152ce2022-01-09 20:14:09 -07003879
Simon Glass5e560182020-07-09 18:39:36 -06003880 def testExtblob(self):
3881 """Test an image with an external blob"""
3882 data = self._DoReadFile('157_blob_ext.dts')
3883 self.assertEqual(REFCODE_DATA, data)
3884
3885 def testExtblobMissing(self):
3886 """Test an image with a missing external blob"""
3887 with self.assertRaises(ValueError) as e:
3888 self._DoReadFile('158_blob_ext_missing.dts')
3889 self.assertIn("Filename 'missing-file' not found in input path",
3890 str(e.exception))
3891
Simon Glass5d94cc62020-07-09 18:39:38 -06003892 def testExtblobMissingOk(self):
3893 """Test an image with an missing external blob that is allowed"""
Simon Glass14d64e32025-04-29 07:21:59 -06003894 with terminal.capture() as (stdout, stderr):
Simon Glass6bce5dc2022-11-09 19:14:42 -07003895 ret = self._DoTestFile('158_blob_ext_missing.dts',
3896 allow_missing=True)
3897 self.assertEqual(103, ret)
Simon Glassa003cd32020-07-09 18:39:40 -06003898 err = stderr.getvalue()
Jonas Karlmanda423fc2023-07-18 20:34:39 +00003899 self.assertIn('(missing-file)', err)
Simon Glass49cd2b32023-02-07 14:34:18 -07003900 self.assertRegex(err, "Image 'image'.*missing.*: blob-ext")
Simon Glass6bce5dc2022-11-09 19:14:42 -07003901 self.assertIn('Some images are invalid', err)
3902
3903 def testExtblobMissingOkFlag(self):
3904 """Test an image with an missing external blob allowed with -W"""
Simon Glass14d64e32025-04-29 07:21:59 -06003905 with terminal.capture() as (stdout, stderr):
Simon Glass6bce5dc2022-11-09 19:14:42 -07003906 ret = self._DoTestFile('158_blob_ext_missing.dts',
3907 allow_missing=True, ignore_missing=True)
3908 self.assertEqual(0, ret)
3909 err = stderr.getvalue()
Jonas Karlmanda423fc2023-07-18 20:34:39 +00003910 self.assertIn('(missing-file)', err)
Simon Glass49cd2b32023-02-07 14:34:18 -07003911 self.assertRegex(err, "Image 'image'.*missing.*: blob-ext")
Simon Glass6bce5dc2022-11-09 19:14:42 -07003912 self.assertIn('Some images are invalid', err)
Simon Glassa003cd32020-07-09 18:39:40 -06003913
3914 def testExtblobMissingOkSect(self):
3915 """Test an image with an missing external blob that is allowed"""
Simon Glass14d64e32025-04-29 07:21:59 -06003916 with terminal.capture() as (stdout, stderr):
Simon Glassa003cd32020-07-09 18:39:40 -06003917 self._DoTestFile('159_blob_ext_missing_sect.dts',
3918 allow_missing=True)
3919 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07003920 self.assertRegex(err, "Image 'image'.*missing.*: blob-ext blob-ext2")
Simon Glass5d94cc62020-07-09 18:39:38 -06003921
Simon Glasse88cef92020-07-09 18:39:41 -06003922 def testPackX86RomMeMissingDesc(self):
3923 """Test that an missing Intel descriptor entry is allowed"""
Simon Glass14d64e32025-04-29 07:21:59 -06003924 with terminal.capture() as (stdout, stderr):
Simon Glass14c596c2020-07-25 15:11:19 -06003925 self._DoTestFile('164_x86_rom_me_missing.dts', allow_missing=True)
Simon Glasse88cef92020-07-09 18:39:41 -06003926 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07003927 self.assertRegex(err, "Image 'image'.*missing.*: intel-descriptor")
Simon Glasse88cef92020-07-09 18:39:41 -06003928
3929 def testPackX86RomMissingIfwi(self):
3930 """Test that an x86 ROM with Integrated Firmware Image can be created"""
3931 self._SetupIfwi('fitimage.bin')
3932 pathname = os.path.join(self._indir, 'fitimage.bin')
3933 os.remove(pathname)
Simon Glass14d64e32025-04-29 07:21:59 -06003934 with terminal.capture() as (stdout, stderr):
Simon Glasse88cef92020-07-09 18:39:41 -06003935 self._DoTestFile('111_x86_rom_ifwi.dts', allow_missing=True)
3936 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07003937 self.assertRegex(err, "Image 'image'.*missing.*: intel-ifwi")
Simon Glasse88cef92020-07-09 18:39:41 -06003938
Simon Glass2a0fa982022-02-11 13:23:21 -07003939 def testPackOverlapZero(self):
Simon Glassd70829a2020-07-09 18:39:42 -06003940 """Test that zero-size overlapping regions are ignored"""
3941 self._DoTestFile('160_pack_overlap_zero.dts')
3942
Alper Nebi Yasak09fb0612022-02-08 01:08:04 +03003943 def _CheckSimpleFitData(self, fit_data, kernel_data, fdt1_data):
Simon Glass45d556d2020-07-09 18:39:45 -06003944 # The data should be inside the FIT
3945 dtb = fdt.Fdt.FromData(fit_data)
3946 dtb.Scan()
3947 fnode = dtb.GetNode('/images/kernel')
3948 self.assertIn('data', fnode.props)
3949
3950 fname = os.path.join(self._indir, 'fit_data.fit')
Simon Glass80025522022-01-29 14:14:04 -07003951 tools.write_file(fname, fit_data)
3952 out = tools.run('dumpimage', '-l', fname)
Simon Glass45d556d2020-07-09 18:39:45 -06003953
3954 # Check a few features to make sure the plumbing works. We don't need
3955 # to test the operation of mkimage or dumpimage here. First convert the
3956 # output into a dict where the keys are the fields printed by dumpimage
3957 # and the values are a list of values for each field
3958 lines = out.splitlines()
3959
3960 # Converts "Compression: gzip compressed" into two groups:
3961 # 'Compression' and 'gzip compressed'
3962 re_line = re.compile(r'^ *([^:]*)(?:: *(.*))?$')
3963 vals = collections.defaultdict(list)
3964 for line in lines:
3965 mat = re_line.match(line)
3966 vals[mat.group(1)].append(mat.group(2))
3967
Brandon Maiera657bc62024-06-04 16:16:05 +00003968 self.assertEqual('FIT description: test-desc', lines[0])
Simon Glass45d556d2020-07-09 18:39:45 -06003969 self.assertIn('Created:', lines[1])
3970 self.assertIn('Image 0 (kernel)', vals)
3971 self.assertIn('Hash value', vals)
3972 data_sizes = vals.get('Data Size')
3973 self.assertIsNotNone(data_sizes)
3974 self.assertEqual(2, len(data_sizes))
3975 # Format is "4 Bytes = 0.00 KiB = 0.00 MiB" so take the first word
Alper Nebi Yasak09fb0612022-02-08 01:08:04 +03003976 self.assertEqual(len(kernel_data), int(data_sizes[0].split()[0]))
3977 self.assertEqual(len(fdt1_data), int(data_sizes[1].split()[0]))
3978
Alper Nebi Yasak1a0ee0f2022-03-27 18:31:47 +03003979 # Check if entry listing correctly omits /images/
3980 image = control.images['image']
3981 fit_entry = image.GetEntries()['fit']
3982 subentries = list(fit_entry.GetEntries().keys())
3983 expected = ['kernel', 'fdt-1']
3984 self.assertEqual(expected, subentries)
3985
Alper Nebi Yasak09fb0612022-02-08 01:08:04 +03003986 def testSimpleFit(self):
3987 """Test an image with a FIT inside"""
Marek Vasutf7413f02023-07-18 07:23:58 -06003988 self._SetupSplElf()
Alper Nebi Yasak09fb0612022-02-08 01:08:04 +03003989 data = self._DoReadFile('161_fit.dts')
3990 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
3991 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
3992 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
3993
3994 self._CheckSimpleFitData(fit_data, U_BOOT_DATA, U_BOOT_SPL_DTB_DATA)
3995
3996 def testSimpleFitExpandsSubentries(self):
3997 """Test that FIT images expand their subentries"""
3998 data = self._DoReadFileDtb('161_fit.dts', use_expanded=True)[0]
3999 self.assertEqual(U_BOOT_EXP_DATA, data[:len(U_BOOT_EXP_DATA)])
4000 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
4001 fit_data = data[len(U_BOOT_EXP_DATA):-len(U_BOOT_NODTB_DATA)]
4002
4003 self._CheckSimpleFitData(fit_data, U_BOOT_EXP_DATA, U_BOOT_SPL_DTB_DATA)
Simon Glass45d556d2020-07-09 18:39:45 -06004004
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03004005 def testSimpleFitImagePos(self):
4006 """Test that we have correct image-pos for FIT subentries"""
4007 data, _, _, out_dtb_fname = self._DoReadFileDtb('161_fit.dts',
4008 update_dtb=True)
4009 dtb = fdt.Fdt(out_dtb_fname)
4010 dtb.Scan()
4011 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
4012
Simon Glassb7bad182022-03-05 20:19:01 -07004013 self.maxDiff = None
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03004014 self.assertEqual({
4015 'image-pos': 0,
4016 'offset': 0,
4017 'size': 1890,
4018
4019 'u-boot:image-pos': 0,
4020 'u-boot:offset': 0,
4021 'u-boot:size': 4,
4022
4023 'fit:image-pos': 4,
4024 'fit:offset': 4,
4025 'fit:size': 1840,
4026
Simon Glassb7bad182022-03-05 20:19:01 -07004027 'fit/images/kernel:image-pos': 304,
4028 'fit/images/kernel:offset': 300,
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03004029 'fit/images/kernel:size': 4,
4030
Simon Glassb7bad182022-03-05 20:19:01 -07004031 'fit/images/kernel/u-boot:image-pos': 304,
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03004032 'fit/images/kernel/u-boot:offset': 0,
4033 'fit/images/kernel/u-boot:size': 4,
4034
Simon Glassb7bad182022-03-05 20:19:01 -07004035 'fit/images/fdt-1:image-pos': 552,
4036 'fit/images/fdt-1:offset': 548,
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03004037 'fit/images/fdt-1:size': 6,
4038
Simon Glassb7bad182022-03-05 20:19:01 -07004039 'fit/images/fdt-1/u-boot-spl-dtb:image-pos': 552,
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03004040 'fit/images/fdt-1/u-boot-spl-dtb:offset': 0,
4041 'fit/images/fdt-1/u-boot-spl-dtb:size': 6,
4042
4043 'u-boot-nodtb:image-pos': 1844,
4044 'u-boot-nodtb:offset': 1844,
4045 'u-boot-nodtb:size': 46,
4046 }, props)
4047
4048 # Actually check the data is where we think it is
4049 for node, expected in [
4050 ("u-boot", U_BOOT_DATA),
4051 ("fit/images/kernel", U_BOOT_DATA),
4052 ("fit/images/kernel/u-boot", U_BOOT_DATA),
4053 ("fit/images/fdt-1", U_BOOT_SPL_DTB_DATA),
4054 ("fit/images/fdt-1/u-boot-spl-dtb", U_BOOT_SPL_DTB_DATA),
4055 ("u-boot-nodtb", U_BOOT_NODTB_DATA),
4056 ]:
4057 image_pos = props[f"{node}:image-pos"]
4058 size = props[f"{node}:size"]
4059 self.assertEqual(len(expected), size)
4060 self.assertEqual(expected, data[image_pos:image_pos+size])
4061
Simon Glass45d556d2020-07-09 18:39:45 -06004062 def testFitExternal(self):
Simon Glass31ee50f2020-09-01 05:13:55 -06004063 """Test an image with an FIT with external images"""
Simon Glass45d556d2020-07-09 18:39:45 -06004064 data = self._DoReadFile('162_fit_external.dts')
4065 fit_data = data[len(U_BOOT_DATA):-2] # _testing is 2 bytes
4066
Simon Glass7932c882022-01-09 20:13:39 -07004067 # Size of the external-data region as set up by mkimage
4068 external_data_size = len(U_BOOT_DATA) + 2
4069 expected_size = (len(U_BOOT_DATA) + 0x400 +
Simon Glass80025522022-01-29 14:14:04 -07004070 tools.align(external_data_size, 4) +
Simon Glass7932c882022-01-09 20:13:39 -07004071 len(U_BOOT_NODTB_DATA))
4072
Simon Glass45d556d2020-07-09 18:39:45 -06004073 # The data should be outside the FIT
4074 dtb = fdt.Fdt.FromData(fit_data)
4075 dtb.Scan()
4076 fnode = dtb.GetNode('/images/kernel')
4077 self.assertNotIn('data', fnode.props)
Simon Glass7932c882022-01-09 20:13:39 -07004078 self.assertEqual(len(U_BOOT_DATA),
4079 fdt_util.fdt32_to_cpu(fnode.props['data-size'].value))
4080 fit_pos = 0x400;
4081 self.assertEqual(
4082 fit_pos,
4083 fdt_util.fdt32_to_cpu(fnode.props['data-position'].value))
4084
Brandon Maiera657bc62024-06-04 16:16:05 +00004085 self.assertEqual(expected_size, len(data))
Simon Glass7932c882022-01-09 20:13:39 -07004086 actual_pos = len(U_BOOT_DATA) + fit_pos
4087 self.assertEqual(U_BOOT_DATA + b'aa',
4088 data[actual_pos:actual_pos + external_data_size])
Simon Glassfb30e292019-07-20 12:23:51 -06004089
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03004090 def testFitExternalImagePos(self):
4091 """Test that we have correct image-pos for external FIT subentries"""
4092 data, _, _, out_dtb_fname = self._DoReadFileDtb('162_fit_external.dts',
4093 update_dtb=True)
4094 dtb = fdt.Fdt(out_dtb_fname)
4095 dtb.Scan()
4096 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
4097
4098 self.assertEqual({
4099 'image-pos': 0,
4100 'offset': 0,
4101 'size': 1082,
4102
4103 'u-boot:image-pos': 0,
4104 'u-boot:offset': 0,
4105 'u-boot:size': 4,
4106
4107 'fit:size': 1032,
4108 'fit:offset': 4,
4109 'fit:image-pos': 4,
4110
4111 'fit/images/kernel:size': 4,
4112 'fit/images/kernel:offset': 1024,
4113 'fit/images/kernel:image-pos': 1028,
4114
4115 'fit/images/kernel/u-boot:size': 4,
4116 'fit/images/kernel/u-boot:offset': 0,
4117 'fit/images/kernel/u-boot:image-pos': 1028,
4118
4119 'fit/images/fdt-1:size': 2,
4120 'fit/images/fdt-1:offset': 1028,
4121 'fit/images/fdt-1:image-pos': 1032,
4122
4123 'fit/images/fdt-1/_testing:size': 2,
4124 'fit/images/fdt-1/_testing:offset': 0,
4125 'fit/images/fdt-1/_testing:image-pos': 1032,
4126
4127 'u-boot-nodtb:image-pos': 1036,
4128 'u-boot-nodtb:offset': 1036,
4129 'u-boot-nodtb:size': 46,
4130 }, props)
4131
4132 # Actually check the data is where we think it is
4133 for node, expected in [
4134 ("u-boot", U_BOOT_DATA),
4135 ("fit/images/kernel", U_BOOT_DATA),
4136 ("fit/images/kernel/u-boot", U_BOOT_DATA),
4137 ("fit/images/fdt-1", b'aa'),
4138 ("fit/images/fdt-1/_testing", b'aa'),
4139 ("u-boot-nodtb", U_BOOT_NODTB_DATA),
4140 ]:
4141 image_pos = props[f"{node}:image-pos"]
4142 size = props[f"{node}:size"]
4143 self.assertEqual(len(expected), size)
4144 self.assertEqual(expected, data[image_pos:image_pos+size])
4145
Simon Glass66152ce2022-01-09 20:14:09 -07004146 def testFitMissing(self):
Simon Glass039d65f2023-03-02 17:02:43 -07004147 """Test that binman complains if mkimage is missing"""
4148 with self.assertRaises(ValueError) as e:
4149 self._DoTestFile('162_fit_external.dts',
4150 force_missing_bintools='mkimage')
4151 self.assertIn("Node '/binman/fit': Missing tool: 'mkimage'",
4152 str(e.exception))
4153
4154 def testFitMissingOK(self):
Simon Glass66152ce2022-01-09 20:14:09 -07004155 """Test that binman still produces a FIT image if mkimage is missing"""
Simon Glass14d64e32025-04-29 07:21:59 -06004156 with terminal.capture() as (_, stderr):
Simon Glass039d65f2023-03-02 17:02:43 -07004157 self._DoTestFile('162_fit_external.dts', allow_missing=True,
Simon Glass66152ce2022-01-09 20:14:09 -07004158 force_missing_bintools='mkimage')
4159 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07004160 self.assertRegex(err, "Image 'image'.*missing bintools.*: mkimage")
Simon Glass66152ce2022-01-09 20:14:09 -07004161
Alper Nebi Yasak6aae2392020-08-31 12:58:18 +03004162 def testSectionIgnoreHashSignature(self):
4163 """Test that sections ignore hash, signature nodes for its data"""
4164 data = self._DoReadFile('165_section_ignore_hash_signature.dts')
4165 expected = (U_BOOT_DATA + U_BOOT_DATA)
4166 self.assertEqual(expected, data)
4167
Alper Nebi Yasakc5030602020-08-31 12:58:19 +03004168 def testPadInSections(self):
4169 """Test pad-before, pad-after for entries in sections"""
Simon Glassd12599d2020-10-26 17:40:09 -06004170 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4171 '166_pad_in_sections.dts', update_dtb=True)
Simon Glass80025522022-01-29 14:14:04 -07004172 expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) +
4173 U_BOOT_DATA + tools.get_bytes(ord('!'), 6) +
Alper Nebi Yasakc5030602020-08-31 12:58:19 +03004174 U_BOOT_DATA)
4175 self.assertEqual(expected, data)
4176
Simon Glassd12599d2020-10-26 17:40:09 -06004177 dtb = fdt.Fdt(out_dtb_fname)
4178 dtb.Scan()
4179 props = self._GetPropTree(dtb, ['size', 'image-pos', 'offset'])
4180 expected = {
4181 'image-pos': 0,
4182 'offset': 0,
4183 'size': 12 + 6 + 3 * len(U_BOOT_DATA),
4184
4185 'section:image-pos': 0,
4186 'section:offset': 0,
4187 'section:size': 12 + 6 + 3 * len(U_BOOT_DATA),
4188
4189 'section/before:image-pos': 0,
4190 'section/before:offset': 0,
4191 'section/before:size': len(U_BOOT_DATA),
4192
4193 'section/u-boot:image-pos': 4,
4194 'section/u-boot:offset': 4,
4195 'section/u-boot:size': 12 + len(U_BOOT_DATA) + 6,
4196
4197 'section/after:image-pos': 26,
4198 'section/after:offset': 26,
4199 'section/after:size': len(U_BOOT_DATA),
4200 }
4201 self.assertEqual(expected, props)
4202
Alper Nebi Yasakc261ec12020-08-31 12:58:20 +03004203 def testFitImageSubentryAlignment(self):
4204 """Test relative alignability of FIT image subentries"""
Alper Nebi Yasakd4553262022-02-08 01:08:07 +03004205 self._SetupSplElf()
Alper Nebi Yasakc261ec12020-08-31 12:58:20 +03004206 entry_args = {
4207 'test-id': TEXT_DATA,
4208 }
4209 data, _, _, _ = self._DoReadFileDtb('167_fit_image_subentry_alignment.dts',
4210 entry_args=entry_args)
4211 dtb = fdt.Fdt.FromData(data)
4212 dtb.Scan()
4213
4214 node = dtb.GetNode('/images/kernel')
4215 data = dtb.GetProps(node)["data"].bytes
4216 align_pad = 0x10 - (len(U_BOOT_SPL_DATA) % 0x10)
Simon Glass80025522022-01-29 14:14:04 -07004217 expected = (tools.get_bytes(0, 0x20) + U_BOOT_SPL_DATA +
4218 tools.get_bytes(0, align_pad) + U_BOOT_DATA)
Alper Nebi Yasakc261ec12020-08-31 12:58:20 +03004219 self.assertEqual(expected, data)
4220
4221 node = dtb.GetNode('/images/fdt-1')
4222 data = dtb.GetProps(node)["data"].bytes
Simon Glass80025522022-01-29 14:14:04 -07004223 expected = (U_BOOT_SPL_DTB_DATA + tools.get_bytes(0, 20) +
4224 tools.to_bytes(TEXT_DATA) + tools.get_bytes(0, 30) +
Alper Nebi Yasakc261ec12020-08-31 12:58:20 +03004225 U_BOOT_DTB_DATA)
4226 self.assertEqual(expected, data)
4227
4228 def testFitExtblobMissingOk(self):
4229 """Test a FIT with a missing external blob that is allowed"""
Simon Glass14d64e32025-04-29 07:21:59 -06004230 with terminal.capture() as (stdout, stderr):
Alper Nebi Yasakc261ec12020-08-31 12:58:20 +03004231 self._DoTestFile('168_fit_missing_blob.dts',
4232 allow_missing=True)
4233 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07004234 self.assertRegex(err, "Image 'image'.*missing.*: atf-bl31")
Alper Nebi Yasakc261ec12020-08-31 12:58:20 +03004235
Simon Glass21db0ff2020-09-01 05:13:54 -06004236 def testBlobNamedByArgMissing(self):
4237 """Test handling of a missing entry arg"""
4238 with self.assertRaises(ValueError) as e:
4239 self._DoReadFile('068_blob_named_by_arg.dts')
4240 self.assertIn("Missing required properties/entry args: cros-ec-rw-path",
4241 str(e.exception))
4242
Simon Glass559c4de2020-09-01 05:13:58 -06004243 def testPackBl31(self):
4244 """Test that an image with an ATF BL31 binary can be created"""
4245 data = self._DoReadFile('169_atf_bl31.dts')
4246 self.assertEqual(ATF_BL31_DATA, data[:len(ATF_BL31_DATA)])
4247
Samuel Holland9d8cc632020-10-21 21:12:15 -05004248 def testPackScp(self):
4249 """Test that an image with an SCP binary can be created"""
4250 data = self._DoReadFile('172_scp.dts')
4251 self.assertEqual(SCP_DATA, data[:len(SCP_DATA)])
4252
Simon Glassd2a9d6e2024-08-26 13:11:37 -06004253 def CheckFitFdt(self, dts='170_fit_fdt.dts', use_fdt_list=True,
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004254 default_dt=None, use_seq_num=True):
Simon Glasscd2783e2024-07-20 11:49:46 +01004255 """Check an image with an FIT with multiple FDT images"""
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004256 def _CheckFdt(val, expected_data):
Simon Glassa435cd12020-09-01 05:13:59 -06004257 """Check the FDT nodes
4258
4259 Args:
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004260 val: Sequence number to check (0 or 1) or fdt name
Simon Glassa435cd12020-09-01 05:13:59 -06004261 expected_data: Expected contents of 'data' property
4262 """
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004263 name = 'fdt-%s' % val
Simon Glassa435cd12020-09-01 05:13:59 -06004264 fnode = dtb.GetNode('/images/%s' % name)
4265 self.assertIsNotNone(fnode)
4266 self.assertEqual({'description','type', 'compression', 'data'},
4267 set(fnode.props.keys()))
4268 self.assertEqual(expected_data, fnode.props['data'].bytes)
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004269 description = (
4270 'fdt-test-fdt%s.dtb' % val if len(val) == 1 else
4271 'fdt-%s.dtb' % val
4272 )
4273 self.assertEqual(description, fnode.props['description'].value)
Jan Kiszkaa1419df2022-02-28 17:06:20 +01004274 self.assertEqual(fnode.subnodes[0].name, 'hash')
Simon Glassa435cd12020-09-01 05:13:59 -06004275
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004276 def _CheckConfig(val, expected_data):
Simon Glassa435cd12020-09-01 05:13:59 -06004277 """Check the configuration nodes
4278
4279 Args:
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004280 val: Sequence number to check (0 or 1) or fdt name
Simon Glassa435cd12020-09-01 05:13:59 -06004281 expected_data: Expected contents of 'data' property
4282 """
4283 cnode = dtb.GetNode('/configurations')
4284 self.assertIn('default', cnode.props)
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004285 default = (
4286 'config-2' if len(val) == 1 else
4287 'config-test-fdt2'
4288 )
4289 self.assertEqual(default, cnode.props['default'].value)
Simon Glassa435cd12020-09-01 05:13:59 -06004290
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004291 name = 'config-%s' % val
Simon Glassa435cd12020-09-01 05:13:59 -06004292 fnode = dtb.GetNode('/configurations/%s' % name)
4293 self.assertIsNotNone(fnode)
4294 self.assertEqual({'description','firmware', 'loadables', 'fdt'},
4295 set(fnode.props.keys()))
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004296 description = (
4297 'conf-test-fdt%s.dtb' % val if len(val) == 1 else
4298 'conf-%s.dtb' % val
4299 )
4300 self.assertEqual(description, fnode.props['description'].value)
4301 self.assertEqual('fdt-%s' % val, fnode.props['fdt'].value)
Simon Glassa435cd12020-09-01 05:13:59 -06004302
4303 entry_args = {
Simon Glass1032acc2020-09-06 10:39:08 -06004304 'default-dt': 'test-fdt2',
Simon Glassa435cd12020-09-01 05:13:59 -06004305 }
Paul HENRYS87a8e7c2024-11-25 19:16:54 +01004306 extra_indirs = None
Simon Glasscd2783e2024-07-20 11:49:46 +01004307 if use_fdt_list:
4308 entry_args['of-list'] = 'test-fdt1 test-fdt2'
Simon Glassd2a9d6e2024-08-26 13:11:37 -06004309 if default_dt:
4310 entry_args['default-dt'] = default_dt
Paul HENRYS87a8e7c2024-11-25 19:16:54 +01004311 if use_fdt_list:
4312 extra_indirs = [os.path.join(self._indir, TEST_FDT_SUBDIR)]
Simon Glassa435cd12020-09-01 05:13:59 -06004313 data = self._DoReadFileDtb(
Simon Glasscd2783e2024-07-20 11:49:46 +01004314 dts,
Simon Glassa435cd12020-09-01 05:13:59 -06004315 entry_args=entry_args,
Paul HENRYS87a8e7c2024-11-25 19:16:54 +01004316 extra_indirs=extra_indirs)[0]
Simon Glassa435cd12020-09-01 05:13:59 -06004317 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
4318 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
4319
4320 dtb = fdt.Fdt.FromData(fit_data)
4321 dtb.Scan()
4322 fnode = dtb.GetNode('/images/kernel')
4323 self.assertIn('data', fnode.props)
4324
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004325 if use_seq_num == True:
4326 # Check all the properties in fdt-1 and fdt-2
4327 _CheckFdt('1', TEST_FDT1_DATA)
4328 _CheckFdt('2', TEST_FDT2_DATA)
Simon Glassa435cd12020-09-01 05:13:59 -06004329
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004330 # Check configurations
4331 _CheckConfig('1', TEST_FDT1_DATA)
4332 _CheckConfig('2', TEST_FDT2_DATA)
4333 else:
4334 # Check all the properties in fdt-1 and fdt-2
4335 _CheckFdt('test-fdt1', TEST_FDT1_DATA)
4336 _CheckFdt('test-fdt2', TEST_FDT2_DATA)
4337
4338 # Check configurations
4339 _CheckConfig('test-fdt1', TEST_FDT1_DATA)
4340 _CheckConfig('test-fdt2', TEST_FDT2_DATA)
Simon Glassa435cd12020-09-01 05:13:59 -06004341
Simon Glasscd2783e2024-07-20 11:49:46 +01004342 def testFitFdt(self):
4343 """Test an image with an FIT with multiple FDT images"""
4344 self.CheckFitFdt()
4345
Simon Glassa435cd12020-09-01 05:13:59 -06004346 def testFitFdtMissingList(self):
4347 """Test handling of a missing 'of-list' entry arg"""
4348 with self.assertRaises(ValueError) as e:
Bin Meng16cf5662021-05-10 20:23:32 +08004349 self._DoReadFile('170_fit_fdt.dts')
Simon Glassa435cd12020-09-01 05:13:59 -06004350 self.assertIn("Generator node requires 'of-list' entry argument",
4351 str(e.exception))
4352
4353 def testFitFdtEmptyList(self):
4354 """Test handling of an empty 'of-list' entry arg"""
4355 entry_args = {
4356 'of-list': '',
4357 }
4358 data = self._DoReadFileDtb('170_fit_fdt.dts', entry_args=entry_args)[0]
4359
4360 def testFitFdtMissingProp(self):
4361 """Test handling of a missing 'fit,fdt-list' property"""
4362 with self.assertRaises(ValueError) as e:
4363 self._DoReadFile('171_fit_fdt_missing_prop.dts')
4364 self.assertIn("Generator node requires 'fit,fdt-list' property",
4365 str(e.exception))
Simon Glass559c4de2020-09-01 05:13:58 -06004366
Simon Glass1032acc2020-09-06 10:39:08 -06004367 def testFitFdtMissing(self):
4368 """Test handling of a missing 'default-dt' entry arg"""
4369 entry_args = {
4370 'of-list': 'test-fdt1 test-fdt2',
4371 }
4372 with self.assertRaises(ValueError) as e:
4373 self._DoReadFileDtb(
Bin Meng16cf5662021-05-10 20:23:32 +08004374 '170_fit_fdt.dts',
Simon Glass1032acc2020-09-06 10:39:08 -06004375 entry_args=entry_args,
4376 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
4377 self.assertIn("Generated 'default' node requires default-dt entry argument",
4378 str(e.exception))
4379
4380 def testFitFdtNotInList(self):
4381 """Test handling of a default-dt that is not in the of-list"""
4382 entry_args = {
4383 'of-list': 'test-fdt1 test-fdt2',
4384 'default-dt': 'test-fdt3',
4385 }
4386 with self.assertRaises(ValueError) as e:
4387 self._DoReadFileDtb(
Bin Meng16cf5662021-05-10 20:23:32 +08004388 '170_fit_fdt.dts',
Simon Glass1032acc2020-09-06 10:39:08 -06004389 entry_args=entry_args,
4390 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
4391 self.assertIn("default-dt entry argument 'test-fdt3' not found in fdt list: test-fdt1, test-fdt2",
4392 str(e.exception))
4393
Simon Glassa820af72020-09-06 10:39:09 -06004394 def testFitExtblobMissingHelp(self):
4395 """Test display of help messages when an external blob is missing"""
4396 control.missing_blob_help = control._ReadMissingBlobHelp()
4397 control.missing_blob_help['wibble'] = 'Wibble test'
4398 control.missing_blob_help['another'] = 'Another test'
Simon Glass14d64e32025-04-29 07:21:59 -06004399 with terminal.capture() as (stdout, stderr):
Simon Glassa820af72020-09-06 10:39:09 -06004400 self._DoTestFile('168_fit_missing_blob.dts',
4401 allow_missing=True)
4402 err = stderr.getvalue()
4403
4404 # We can get the tag from the name, the type or the missing-msg
4405 # property. Check all three.
4406 self.assertIn('You may need to build ARM Trusted', err)
4407 self.assertIn('Wibble test', err)
4408 self.assertIn('Another test', err)
4409
Simon Glass6f1f4d42020-09-06 10:35:32 -06004410 def testMissingBlob(self):
4411 """Test handling of a blob containing a missing file"""
4412 with self.assertRaises(ValueError) as e:
4413 self._DoTestFile('173_missing_blob.dts', allow_missing=True)
4414 self.assertIn("Filename 'missing' not found in input path",
4415 str(e.exception))
4416
Simon Glassa0729502020-09-06 10:35:33 -06004417 def testEnvironment(self):
4418 """Test adding a U-Boot environment"""
4419 data = self._DoReadFile('174_env.dts')
4420 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
4421 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
4422 env = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
4423 self.assertEqual(b'\x1b\x97\x22\x7c\x01var1=1\0var2="2"\0\0\xff\xff',
4424 env)
4425
4426 def testEnvironmentNoSize(self):
4427 """Test that a missing 'size' property is detected"""
4428 with self.assertRaises(ValueError) as e:
Simon Glass8cdc08a2020-10-26 17:40:00 -06004429 self._DoTestFile('175_env_no_size.dts')
Simon Glassa0729502020-09-06 10:35:33 -06004430 self.assertIn("'u-boot-env' entry must have a size property",
4431 str(e.exception))
4432
4433 def testEnvironmentTooSmall(self):
4434 """Test handling of an environment that does not fit"""
4435 with self.assertRaises(ValueError) as e:
Simon Glass8cdc08a2020-10-26 17:40:00 -06004436 self._DoTestFile('176_env_too_small.dts')
Simon Glassa0729502020-09-06 10:35:33 -06004437
4438 # checksum, start byte, environment with \0 terminator, final \0
4439 need = 4 + 1 + len(ENV_DATA) + 1 + 1
4440 short = need - 0x8
4441 self.assertIn("too small to hold data (need %#x more bytes)" % short,
4442 str(e.exception))
4443
Simon Glassd1fdf752020-10-26 17:40:01 -06004444 def testSkipAtStart(self):
4445 """Test handling of skip-at-start section"""
4446 data = self._DoReadFile('177_skip_at_start.dts')
4447 self.assertEqual(U_BOOT_DATA, data)
4448
4449 image = control.images['image']
4450 entries = image.GetEntries()
4451 section = entries['section']
4452 self.assertEqual(0, section.offset)
4453 self.assertEqual(len(U_BOOT_DATA), section.size)
4454 self.assertEqual(U_BOOT_DATA, section.GetData())
4455
4456 entry = section.GetEntries()['u-boot']
4457 self.assertEqual(16, entry.offset)
4458 self.assertEqual(len(U_BOOT_DATA), entry.size)
4459 self.assertEqual(U_BOOT_DATA, entry.data)
4460
4461 def testSkipAtStartPad(self):
4462 """Test handling of skip-at-start section with padded entry"""
4463 data = self._DoReadFile('178_skip_at_start_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07004464 before = tools.get_bytes(0, 8)
4465 after = tools.get_bytes(0, 4)
Simon Glassd1fdf752020-10-26 17:40:01 -06004466 all = before + U_BOOT_DATA + after
4467 self.assertEqual(all, data)
4468
4469 image = control.images['image']
4470 entries = image.GetEntries()
4471 section = entries['section']
4472 self.assertEqual(0, section.offset)
4473 self.assertEqual(len(all), section.size)
4474 self.assertEqual(all, section.GetData())
4475
4476 entry = section.GetEntries()['u-boot']
4477 self.assertEqual(16, entry.offset)
4478 self.assertEqual(len(all), entry.size)
4479 self.assertEqual(U_BOOT_DATA, entry.data)
4480
4481 def testSkipAtStartSectionPad(self):
4482 """Test handling of skip-at-start section with padding"""
4483 data = self._DoReadFile('179_skip_at_start_section_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07004484 before = tools.get_bytes(0, 8)
4485 after = tools.get_bytes(0, 4)
Simon Glassd1fdf752020-10-26 17:40:01 -06004486 all = before + U_BOOT_DATA + after
Simon Glass510ef0f2020-10-26 17:40:13 -06004487 self.assertEqual(all, data)
Simon Glassd1fdf752020-10-26 17:40:01 -06004488
4489 image = control.images['image']
4490 entries = image.GetEntries()
4491 section = entries['section']
4492 self.assertEqual(0, section.offset)
4493 self.assertEqual(len(all), section.size)
Simon Glass72eeff12020-10-26 17:40:16 -06004494 self.assertEqual(U_BOOT_DATA, section.data)
Simon Glass510ef0f2020-10-26 17:40:13 -06004495 self.assertEqual(all, section.GetPaddedData())
Simon Glassd1fdf752020-10-26 17:40:01 -06004496
4497 entry = section.GetEntries()['u-boot']
4498 self.assertEqual(16, entry.offset)
4499 self.assertEqual(len(U_BOOT_DATA), entry.size)
4500 self.assertEqual(U_BOOT_DATA, entry.data)
Simon Glassa0729502020-09-06 10:35:33 -06004501
Simon Glassbb395742020-10-26 17:40:14 -06004502 def testSectionPad(self):
4503 """Testing padding with sections"""
4504 data = self._DoReadFile('180_section_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07004505 expected = (tools.get_bytes(ord('&'), 3) +
4506 tools.get_bytes(ord('!'), 5) +
Simon Glassbb395742020-10-26 17:40:14 -06004507 U_BOOT_DATA +
Simon Glass80025522022-01-29 14:14:04 -07004508 tools.get_bytes(ord('!'), 1) +
4509 tools.get_bytes(ord('&'), 2))
Simon Glassbb395742020-10-26 17:40:14 -06004510 self.assertEqual(expected, data)
4511
4512 def testSectionAlign(self):
4513 """Testing alignment with sections"""
4514 data = self._DoReadFileDtb('181_section_align.dts', map=True)[0]
4515 expected = (b'\0' + # fill section
Simon Glass80025522022-01-29 14:14:04 -07004516 tools.get_bytes(ord('&'), 1) + # padding to section align
Simon Glassbb395742020-10-26 17:40:14 -06004517 b'\0' + # fill section
Simon Glass80025522022-01-29 14:14:04 -07004518 tools.get_bytes(ord('!'), 3) + # padding to u-boot align
Simon Glassbb395742020-10-26 17:40:14 -06004519 U_BOOT_DATA +
Simon Glass80025522022-01-29 14:14:04 -07004520 tools.get_bytes(ord('!'), 4) + # padding to u-boot size
4521 tools.get_bytes(ord('!'), 4)) # padding to section size
Simon Glassbb395742020-10-26 17:40:14 -06004522 self.assertEqual(expected, data)
4523
Simon Glassd92c8362020-10-26 17:40:25 -06004524 def testCompressImage(self):
4525 """Test compression of the entire image"""
4526 self._CheckLz4()
4527 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4528 '182_compress_image.dts', use_real_dtb=True, update_dtb=True)
4529 dtb = fdt.Fdt(out_dtb_fname)
4530 dtb.Scan()
4531 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4532 'uncomp-size'])
4533 orig = self._decompress(data)
Brandon Maiera657bc62024-06-04 16:16:05 +00004534 self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, orig)
Simon Glassd92c8362020-10-26 17:40:25 -06004535
4536 # Do a sanity check on various fields
4537 image = control.images['image']
4538 entries = image.GetEntries()
4539 self.assertEqual(2, len(entries))
4540
4541 entry = entries['blob']
4542 self.assertEqual(COMPRESS_DATA, entry.data)
4543 self.assertEqual(len(COMPRESS_DATA), entry.size)
4544
4545 entry = entries['u-boot']
4546 self.assertEqual(U_BOOT_DATA, entry.data)
4547 self.assertEqual(len(U_BOOT_DATA), entry.size)
4548
4549 self.assertEqual(len(data), image.size)
4550 self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, image.uncomp_data)
4551 self.assertEqual(len(COMPRESS_DATA + U_BOOT_DATA), image.uncomp_size)
4552 orig = self._decompress(image.data)
4553 self.assertEqual(orig, image.uncomp_data)
4554
4555 expected = {
4556 'blob:offset': 0,
4557 'blob:size': len(COMPRESS_DATA),
4558 'u-boot:offset': len(COMPRESS_DATA),
4559 'u-boot:size': len(U_BOOT_DATA),
4560 'uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4561 'offset': 0,
4562 'image-pos': 0,
4563 'size': len(data),
4564 }
4565 self.assertEqual(expected, props)
4566
4567 def testCompressImageLess(self):
4568 """Test compression where compression reduces the image size"""
4569 self._CheckLz4()
4570 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4571 '183_compress_image_less.dts', use_real_dtb=True, update_dtb=True)
4572 dtb = fdt.Fdt(out_dtb_fname)
4573 dtb.Scan()
4574 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4575 'uncomp-size'])
4576 orig = self._decompress(data)
4577
Brandon Maiera657bc62024-06-04 16:16:05 +00004578 self.assertEqual(COMPRESS_DATA + COMPRESS_DATA + U_BOOT_DATA, orig)
Simon Glassd92c8362020-10-26 17:40:25 -06004579
4580 # Do a sanity check on various fields
4581 image = control.images['image']
4582 entries = image.GetEntries()
4583 self.assertEqual(2, len(entries))
4584
4585 entry = entries['blob']
4586 self.assertEqual(COMPRESS_DATA_BIG, entry.data)
4587 self.assertEqual(len(COMPRESS_DATA_BIG), entry.size)
4588
4589 entry = entries['u-boot']
4590 self.assertEqual(U_BOOT_DATA, entry.data)
4591 self.assertEqual(len(U_BOOT_DATA), entry.size)
4592
4593 self.assertEqual(len(data), image.size)
4594 self.assertEqual(COMPRESS_DATA_BIG + U_BOOT_DATA, image.uncomp_data)
4595 self.assertEqual(len(COMPRESS_DATA_BIG + U_BOOT_DATA),
4596 image.uncomp_size)
4597 orig = self._decompress(image.data)
4598 self.assertEqual(orig, image.uncomp_data)
4599
4600 expected = {
4601 'blob:offset': 0,
4602 'blob:size': len(COMPRESS_DATA_BIG),
4603 'u-boot:offset': len(COMPRESS_DATA_BIG),
4604 'u-boot:size': len(U_BOOT_DATA),
4605 'uncomp-size': len(COMPRESS_DATA_BIG + U_BOOT_DATA),
4606 'offset': 0,
4607 'image-pos': 0,
4608 'size': len(data),
4609 }
4610 self.assertEqual(expected, props)
4611
4612 def testCompressSectionSize(self):
4613 """Test compression of a section with a fixed size"""
4614 self._CheckLz4()
4615 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4616 '184_compress_section_size.dts', use_real_dtb=True, update_dtb=True)
4617 dtb = fdt.Fdt(out_dtb_fname)
4618 dtb.Scan()
4619 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4620 'uncomp-size'])
Jiaxun Yangc6931742025-04-10 06:43:03 -06004621 data = data[:0x30]
4622 data = data.rstrip(b'\xff')
Simon Glassd92c8362020-10-26 17:40:25 -06004623 orig = self._decompress(data)
Brandon Maiera657bc62024-06-04 16:16:05 +00004624 self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, orig)
Simon Glassd92c8362020-10-26 17:40:25 -06004625 expected = {
4626 'section/blob:offset': 0,
4627 'section/blob:size': len(COMPRESS_DATA),
4628 'section/u-boot:offset': len(COMPRESS_DATA),
4629 'section/u-boot:size': len(U_BOOT_DATA),
4630 'section:offset': 0,
4631 'section:image-pos': 0,
4632 'section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4633 'section:size': 0x30,
4634 'offset': 0,
4635 'image-pos': 0,
4636 'size': 0x30,
4637 }
4638 self.assertEqual(expected, props)
4639
4640 def testCompressSection(self):
4641 """Test compression of a section with no fixed size"""
4642 self._CheckLz4()
4643 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4644 '185_compress_section.dts', use_real_dtb=True, update_dtb=True)
4645 dtb = fdt.Fdt(out_dtb_fname)
4646 dtb.Scan()
4647 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4648 'uncomp-size'])
4649 orig = self._decompress(data)
Brandon Maiera657bc62024-06-04 16:16:05 +00004650 self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, orig)
Simon Glassd92c8362020-10-26 17:40:25 -06004651 expected = {
4652 'section/blob:offset': 0,
4653 'section/blob:size': len(COMPRESS_DATA),
4654 'section/u-boot:offset': len(COMPRESS_DATA),
4655 'section/u-boot:size': len(U_BOOT_DATA),
4656 'section:offset': 0,
4657 'section:image-pos': 0,
4658 'section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4659 'section:size': len(data),
4660 'offset': 0,
4661 'image-pos': 0,
4662 'size': len(data),
4663 }
4664 self.assertEqual(expected, props)
4665
Stefan Herbrechtsmeier94813a02022-08-19 16:25:31 +02004666 def testLz4Missing(self):
4667 """Test that binman still produces an image if lz4 is missing"""
Simon Glass14d64e32025-04-29 07:21:59 -06004668 with terminal.capture() as (_, stderr):
Stefan Herbrechtsmeier94813a02022-08-19 16:25:31 +02004669 self._DoTestFile('185_compress_section.dts',
4670 force_missing_bintools='lz4')
4671 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07004672 self.assertRegex(err, "Image 'image'.*missing bintools.*: lz4")
Stefan Herbrechtsmeier94813a02022-08-19 16:25:31 +02004673
Simon Glassd92c8362020-10-26 17:40:25 -06004674 def testCompressExtra(self):
4675 """Test compression of a section with no fixed size"""
4676 self._CheckLz4()
4677 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4678 '186_compress_extra.dts', use_real_dtb=True, update_dtb=True)
4679 dtb = fdt.Fdt(out_dtb_fname)
4680 dtb.Scan()
4681 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4682 'uncomp-size'])
4683
4684 base = data[len(U_BOOT_DATA):]
Brandon Maiera657bc62024-06-04 16:16:05 +00004685 self.assertEqual(U_BOOT_DATA, base[:len(U_BOOT_DATA)])
Simon Glassd92c8362020-10-26 17:40:25 -06004686 rest = base[len(U_BOOT_DATA):]
4687
4688 # Check compressed data
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +02004689 bintool = self.comp_bintools['lz4']
4690 expect1 = bintool.compress(COMPRESS_DATA + U_BOOT_DATA)
Stefan Herbrechtsmeier86f1fc02022-08-19 16:25:23 +02004691 data1 = rest[:len(expect1)]
4692 section1 = self._decompress(data1)
Brandon Maiera657bc62024-06-04 16:16:05 +00004693 self.assertEqual(expect1, data1)
4694 self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, section1)
Simon Glassd92c8362020-10-26 17:40:25 -06004695 rest1 = rest[len(expect1):]
4696
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +02004697 expect2 = bintool.compress(COMPRESS_DATA + COMPRESS_DATA)
Stefan Herbrechtsmeier86f1fc02022-08-19 16:25:23 +02004698 data2 = rest1[:len(expect2)]
4699 section2 = self._decompress(data2)
Brandon Maiera657bc62024-06-04 16:16:05 +00004700 self.assertEqual(expect2, data2)
4701 self.assertEqual(COMPRESS_DATA + COMPRESS_DATA, section2)
Simon Glassd92c8362020-10-26 17:40:25 -06004702 rest2 = rest1[len(expect2):]
4703
4704 expect_size = (len(U_BOOT_DATA) + len(U_BOOT_DATA) + len(expect1) +
4705 len(expect2) + len(U_BOOT_DATA))
Brandon Maiera657bc62024-06-04 16:16:05 +00004706 #self.assertEqual(expect_size, len(data))
Simon Glassd92c8362020-10-26 17:40:25 -06004707
Brandon Maiera657bc62024-06-04 16:16:05 +00004708 #self.assertEqual(U_BOOT_DATA, rest2)
Simon Glassd92c8362020-10-26 17:40:25 -06004709
4710 self.maxDiff = None
4711 expected = {
4712 'u-boot:offset': 0,
4713 'u-boot:image-pos': 0,
4714 'u-boot:size': len(U_BOOT_DATA),
4715
4716 'base:offset': len(U_BOOT_DATA),
4717 'base:image-pos': len(U_BOOT_DATA),
4718 'base:size': len(data) - len(U_BOOT_DATA),
4719 'base/u-boot:offset': 0,
4720 'base/u-boot:image-pos': len(U_BOOT_DATA),
4721 'base/u-boot:size': len(U_BOOT_DATA),
4722 'base/u-boot2:offset': len(U_BOOT_DATA) + len(expect1) +
4723 len(expect2),
4724 'base/u-boot2:image-pos': len(U_BOOT_DATA) * 2 + len(expect1) +
4725 len(expect2),
4726 'base/u-boot2:size': len(U_BOOT_DATA),
4727
4728 'base/section:offset': len(U_BOOT_DATA),
4729 'base/section:image-pos': len(U_BOOT_DATA) * 2,
4730 'base/section:size': len(expect1),
4731 'base/section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4732 'base/section/blob:offset': 0,
4733 'base/section/blob:size': len(COMPRESS_DATA),
4734 'base/section/u-boot:offset': len(COMPRESS_DATA),
4735 'base/section/u-boot:size': len(U_BOOT_DATA),
4736
4737 'base/section2:offset': len(U_BOOT_DATA) + len(expect1),
4738 'base/section2:image-pos': len(U_BOOT_DATA) * 2 + len(expect1),
4739 'base/section2:size': len(expect2),
4740 'base/section2:uncomp-size': len(COMPRESS_DATA + COMPRESS_DATA),
4741 'base/section2/blob:offset': 0,
4742 'base/section2/blob:size': len(COMPRESS_DATA),
4743 'base/section2/blob2:offset': len(COMPRESS_DATA),
4744 'base/section2/blob2:size': len(COMPRESS_DATA),
4745
4746 'offset': 0,
4747 'image-pos': 0,
4748 'size': len(data),
4749 }
4750 self.assertEqual(expected, props)
4751
Simon Glassecbe4732021-01-06 21:35:15 -07004752 def testSymbolsSubsection(self):
4753 """Test binman can assign symbols from a subsection"""
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03004754 self.checkSymbols('187_symbols_sub.dts', U_BOOT_SPL_DATA, 0x1c)
Simon Glassecbe4732021-01-06 21:35:15 -07004755
Simon Glass3fb25402021-01-06 21:35:16 -07004756 def testReadImageEntryArg(self):
4757 """Test reading an image that would need an entry arg to generate"""
4758 entry_args = {
4759 'cros-ec-rw-path': 'ecrw.bin',
4760 }
4761 data = self.data = self._DoReadFileDtb(
4762 '188_image_entryarg.dts',use_real_dtb=True, update_dtb=True,
4763 entry_args=entry_args)
4764
Simon Glass80025522022-01-29 14:14:04 -07004765 image_fname = tools.get_output_filename('image.bin')
Simon Glass3fb25402021-01-06 21:35:16 -07004766 orig_image = control.images['image']
4767
4768 # This should not generate an error about the missing 'cros-ec-rw-path'
4769 # since we are reading the image from a file. Compare with
4770 # testEntryArgsRequired()
4771 image = Image.FromFile(image_fname)
4772 self.assertEqual(orig_image.GetEntries().keys(),
4773 image.GetEntries().keys())
4774
Simon Glassa2af7302021-01-06 21:35:18 -07004775 def testFilesAlign(self):
4776 """Test alignment with files"""
4777 data = self._DoReadFile('190_files_align.dts')
4778
4779 # The first string is 15 bytes so will align to 16
4780 expect = FILES_DATA[:15] + b'\0' + FILES_DATA[15:]
4781 self.assertEqual(expect, data)
4782
Simon Glassdb84b562021-01-06 21:35:19 -07004783 def testReadImageSkip(self):
4784 """Test reading an image and accessing its FDT map"""
4785 data = self.data = self._DoReadFileRealDtb('191_read_image_skip.dts')
Simon Glass80025522022-01-29 14:14:04 -07004786 image_fname = tools.get_output_filename('image.bin')
Simon Glassdb84b562021-01-06 21:35:19 -07004787 orig_image = control.images['image']
4788 image = Image.FromFile(image_fname)
4789 self.assertEqual(orig_image.GetEntries().keys(),
4790 image.GetEntries().keys())
4791
4792 orig_entry = orig_image.GetEntries()['fdtmap']
4793 entry = image.GetEntries()['fdtmap']
4794 self.assertEqual(orig_entry.offset, entry.offset)
4795 self.assertEqual(orig_entry.size, entry.size)
Simon Glassed836ac2025-02-26 09:26:17 -07004796 self.assertEqual((1 << 32) - 0x400 + 16, entry.image_pos)
Simon Glassdb84b562021-01-06 21:35:19 -07004797
4798 u_boot = image.GetEntries()['section'].GetEntries()['u-boot']
4799
Brandon Maiera657bc62024-06-04 16:16:05 +00004800 self.assertEqual(U_BOOT_DATA, u_boot.ReadData())
Simon Glassdb84b562021-01-06 21:35:19 -07004801
Simon Glassc98de972021-03-18 20:24:57 +13004802 def testTplNoDtb(self):
4803 """Test that an image with tpl/u-boot-tpl-nodtb.bin can be created"""
Simon Glass13089cc2021-04-25 08:39:32 +12004804 self._SetupTplElf()
Simon Glassc98de972021-03-18 20:24:57 +13004805 data = self._DoReadFile('192_u_boot_tpl_nodtb.dts')
4806 self.assertEqual(U_BOOT_TPL_NODTB_DATA,
4807 data[:len(U_BOOT_TPL_NODTB_DATA)])
4808
Simon Glass63f41d42021-03-18 20:24:58 +13004809 def testTplBssPad(self):
4810 """Test that we can pad TPL's BSS with zeros"""
4811 # ELF file with a '__bss_size' symbol
4812 self._SetupTplElf()
4813 data = self._DoReadFile('193_tpl_bss_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07004814 self.assertEqual(U_BOOT_TPL_DATA + tools.get_bytes(0, 10) + U_BOOT_DATA,
Simon Glass63f41d42021-03-18 20:24:58 +13004815 data)
4816
4817 def testTplBssPadMissing(self):
4818 """Test that a missing symbol is detected"""
4819 self._SetupTplElf('u_boot_ucode_ptr')
4820 with self.assertRaises(ValueError) as e:
4821 self._DoReadFile('193_tpl_bss_pad.dts')
4822 self.assertIn('Expected __bss_size symbol in tpl/u-boot-tpl',
4823 str(e.exception))
4824
Simon Glass718b5292021-03-18 20:25:07 +13004825 def checkDtbSizes(self, data, pad_len, start):
4826 """Check the size arguments in a dtb embedded in an image
4827
4828 Args:
4829 data: The image data
4830 pad_len: Length of the pad section in the image, in bytes
4831 start: Start offset of the devicetree to examine, within the image
4832
4833 Returns:
4834 Size of the devicetree in bytes
4835 """
4836 dtb_data = data[start:]
4837 dtb = fdt.Fdt.FromData(dtb_data)
4838 fdt_size = dtb.GetFdtObj().totalsize()
4839 dtb.Scan()
4840 props = self._GetPropTree(dtb, 'size')
4841 self.assertEqual({
4842 'size': len(data),
4843 'u-boot-spl/u-boot-spl-bss-pad:size': pad_len,
4844 'u-boot-spl/u-boot-spl-dtb:size': 801,
4845 'u-boot-spl/u-boot-spl-nodtb:size': len(U_BOOT_SPL_NODTB_DATA),
4846 'u-boot-spl:size': 860,
4847 'u-boot-tpl:size': len(U_BOOT_TPL_DATA),
4848 'u-boot/u-boot-dtb:size': 781,
4849 'u-boot/u-boot-nodtb:size': len(U_BOOT_NODTB_DATA),
4850 'u-boot:size': 827,
4851 }, props)
4852 return fdt_size
4853
4854 def testExpanded(self):
4855 """Test that an expanded entry type is selected when needed"""
4856 self._SetupSplElf()
4857 self._SetupTplElf()
4858
4859 # SPL has a devicetree, TPL does not
4860 entry_args = {
4861 'spl-dtb': '1',
4862 'spl-bss-pad': 'y',
4863 'tpl-dtb': '',
4864 }
4865 self._DoReadFileDtb('194_fdt_incl.dts', use_expanded=True,
4866 entry_args=entry_args)
4867 image = control.images['image']
4868 entries = image.GetEntries()
4869 self.assertEqual(3, len(entries))
4870
4871 # First, u-boot, which should be expanded into u-boot-nodtb and dtb
4872 self.assertIn('u-boot', entries)
4873 entry = entries['u-boot']
4874 self.assertEqual('u-boot-expanded', entry.etype)
4875 subent = entry.GetEntries()
4876 self.assertEqual(2, len(subent))
4877 self.assertIn('u-boot-nodtb', subent)
4878 self.assertIn('u-boot-dtb', subent)
4879
4880 # Second, u-boot-spl, which should be expanded into three parts
4881 self.assertIn('u-boot-spl', entries)
4882 entry = entries['u-boot-spl']
4883 self.assertEqual('u-boot-spl-expanded', entry.etype)
4884 subent = entry.GetEntries()
4885 self.assertEqual(3, len(subent))
4886 self.assertIn('u-boot-spl-nodtb', subent)
4887 self.assertIn('u-boot-spl-bss-pad', subent)
4888 self.assertIn('u-boot-spl-dtb', subent)
4889
4890 # Third, u-boot-tpl, which should be not be expanded, since TPL has no
4891 # devicetree
4892 self.assertIn('u-boot-tpl', entries)
4893 entry = entries['u-boot-tpl']
4894 self.assertEqual('u-boot-tpl', entry.etype)
4895 self.assertEqual(None, entry.GetEntries())
4896
4897 def testExpandedTpl(self):
4898 """Test that an expanded entry type is selected for TPL when needed"""
4899 self._SetupTplElf()
4900
4901 entry_args = {
4902 'tpl-bss-pad': 'y',
4903 'tpl-dtb': 'y',
4904 }
4905 self._DoReadFileDtb('195_fdt_incl_tpl.dts', use_expanded=True,
4906 entry_args=entry_args)
4907 image = control.images['image']
4908 entries = image.GetEntries()
4909 self.assertEqual(1, len(entries))
4910
4911 # We only have u-boot-tpl, which be expanded
4912 self.assertIn('u-boot-tpl', entries)
4913 entry = entries['u-boot-tpl']
4914 self.assertEqual('u-boot-tpl-expanded', entry.etype)
4915 subent = entry.GetEntries()
4916 self.assertEqual(3, len(subent))
4917 self.assertIn('u-boot-tpl-nodtb', subent)
4918 self.assertIn('u-boot-tpl-bss-pad', subent)
4919 self.assertIn('u-boot-tpl-dtb', subent)
4920
4921 def testExpandedNoPad(self):
4922 """Test an expanded entry without BSS pad enabled"""
4923 self._SetupSplElf()
4924 self._SetupTplElf()
4925
4926 # SPL has a devicetree, TPL does not
4927 entry_args = {
4928 'spl-dtb': 'something',
4929 'spl-bss-pad': 'n',
4930 'tpl-dtb': '',
4931 }
4932 self._DoReadFileDtb('194_fdt_incl.dts', use_expanded=True,
4933 entry_args=entry_args)
4934 image = control.images['image']
4935 entries = image.GetEntries()
4936
4937 # Just check u-boot-spl, which should be expanded into two parts
4938 self.assertIn('u-boot-spl', entries)
4939 entry = entries['u-boot-spl']
4940 self.assertEqual('u-boot-spl-expanded', entry.etype)
4941 subent = entry.GetEntries()
4942 self.assertEqual(2, len(subent))
4943 self.assertIn('u-boot-spl-nodtb', subent)
4944 self.assertIn('u-boot-spl-dtb', subent)
4945
4946 def testExpandedTplNoPad(self):
4947 """Test that an expanded entry type with padding disabled in TPL"""
4948 self._SetupTplElf()
4949
4950 entry_args = {
4951 'tpl-bss-pad': '',
4952 'tpl-dtb': 'y',
4953 }
4954 self._DoReadFileDtb('195_fdt_incl_tpl.dts', use_expanded=True,
4955 entry_args=entry_args)
4956 image = control.images['image']
4957 entries = image.GetEntries()
4958 self.assertEqual(1, len(entries))
4959
4960 # We only have u-boot-tpl, which be expanded
4961 self.assertIn('u-boot-tpl', entries)
4962 entry = entries['u-boot-tpl']
4963 self.assertEqual('u-boot-tpl-expanded', entry.etype)
4964 subent = entry.GetEntries()
4965 self.assertEqual(2, len(subent))
4966 self.assertIn('u-boot-tpl-nodtb', subent)
4967 self.assertIn('u-boot-tpl-dtb', subent)
4968
4969 def testFdtInclude(self):
4970 """Test that an Fdt is update within all binaries"""
4971 self._SetupSplElf()
4972 self._SetupTplElf()
4973
4974 # SPL has a devicetree, TPL does not
4975 self.maxDiff = None
4976 entry_args = {
4977 'spl-dtb': '1',
4978 'spl-bss-pad': 'y',
4979 'tpl-dtb': '',
4980 }
4981 # Build the image. It includes two separate devicetree binaries, each
4982 # with their own contents, but all contain the binman definition.
4983 data = self._DoReadFileDtb(
4984 '194_fdt_incl.dts', use_real_dtb=True, use_expanded=True,
4985 update_dtb=True, entry_args=entry_args)[0]
4986 pad_len = 10
4987
4988 # Check the U-Boot dtb
4989 start = len(U_BOOT_NODTB_DATA)
4990 fdt_size = self.checkDtbSizes(data, pad_len, start)
4991
4992 # Now check SPL
4993 start += fdt_size + len(U_BOOT_SPL_NODTB_DATA) + pad_len
4994 fdt_size = self.checkDtbSizes(data, pad_len, start)
4995
4996 # TPL has no devicetree
4997 start += fdt_size + len(U_BOOT_TPL_DATA)
4998 self.assertEqual(len(data), start)
Simon Glassbb395742020-10-26 17:40:14 -06004999
Simon Glass7098b7f2021-03-21 18:24:30 +13005000 def testSymbolsExpanded(self):
5001 """Test binman can assign symbols in expanded entries"""
5002 entry_args = {
5003 'spl-dtb': '1',
5004 }
5005 self.checkSymbols('197_symbols_expand.dts', U_BOOT_SPL_NODTB_DATA +
5006 U_BOOT_SPL_DTB_DATA, 0x38,
5007 entry_args=entry_args, use_expanded=True)
5008
Simon Glasse1915782021-03-21 18:24:31 +13005009 def testCollection(self):
5010 """Test a collection"""
5011 data = self._DoReadFile('198_collection.dts')
5012 self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA +
Simon Glass80025522022-01-29 14:14:04 -07005013 tools.get_bytes(0xff, 2) + U_BOOT_NODTB_DATA +
5014 tools.get_bytes(0xfe, 3) + U_BOOT_DTB_DATA,
Simon Glasse1915782021-03-21 18:24:31 +13005015 data)
5016
Simon Glass27a7f772021-03-21 18:24:32 +13005017 def testCollectionSection(self):
5018 """Test a collection where a section must be built first"""
5019 # Sections never have their contents when GetData() is called, but when
Simon Glass7e3f89f2021-11-23 11:03:47 -07005020 # BuildSectionData() is called with required=True, a section will force
Simon Glass27a7f772021-03-21 18:24:32 +13005021 # building the contents, producing an error is anything is still
5022 # missing.
5023 data = self._DoReadFile('199_collection_section.dts')
5024 section = U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA
Simon Glass80025522022-01-29 14:14:04 -07005025 self.assertEqual(section + U_BOOT_DATA + tools.get_bytes(0xff, 2) +
5026 section + tools.get_bytes(0xfe, 3) + U_BOOT_DATA,
Simon Glass27a7f772021-03-21 18:24:32 +13005027 data)
5028
Simon Glassf427c5f2021-03-21 18:24:33 +13005029 def testAlignDefault(self):
5030 """Test that default alignment works on sections"""
5031 data = self._DoReadFile('200_align_default.dts')
Simon Glass80025522022-01-29 14:14:04 -07005032 expected = (U_BOOT_DATA + tools.get_bytes(0, 8 - len(U_BOOT_DATA)) +
Simon Glassf427c5f2021-03-21 18:24:33 +13005033 U_BOOT_DATA)
5034 # Special alignment for section
Simon Glass80025522022-01-29 14:14:04 -07005035 expected += tools.get_bytes(0, 32 - len(expected))
Simon Glassf427c5f2021-03-21 18:24:33 +13005036 # No alignment within the nested section
5037 expected += U_BOOT_DATA + U_BOOT_NODTB_DATA;
5038 # Now the final piece, which should be default-aligned
Simon Glass80025522022-01-29 14:14:04 -07005039 expected += tools.get_bytes(0, 88 - len(expected)) + U_BOOT_NODTB_DATA
Simon Glassf427c5f2021-03-21 18:24:33 +13005040 self.assertEqual(expected, data)
Simon Glass27a7f772021-03-21 18:24:32 +13005041
Bin Mengc0b15742021-05-10 20:23:33 +08005042 def testPackOpenSBI(self):
5043 """Test that an image with an OpenSBI binary can be created"""
5044 data = self._DoReadFile('201_opensbi.dts')
5045 self.assertEqual(OPENSBI_DATA, data[:len(OPENSBI_DATA)])
5046
Simon Glass76f496d2021-07-06 10:36:37 -06005047 def testSectionsSingleThread(self):
5048 """Test sections without multithreading"""
5049 data = self._DoReadFileDtb('055_sections.dts', threads=0)[0]
Simon Glass80025522022-01-29 14:14:04 -07005050 expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) +
5051 U_BOOT_DATA + tools.get_bytes(ord('a'), 12) +
5052 U_BOOT_DATA + tools.get_bytes(ord('&'), 4))
Simon Glass76f496d2021-07-06 10:36:37 -06005053 self.assertEqual(expected, data)
5054
5055 def testThreadTimeout(self):
5056 """Test handling a thread that takes too long"""
5057 with self.assertRaises(ValueError) as e:
5058 self._DoTestFile('202_section_timeout.dts',
5059 test_section_timeout=True)
Simon Glass2d59d152021-10-18 12:13:15 -06005060 self.assertIn("Timed out obtaining contents", str(e.exception))
Simon Glass76f496d2021-07-06 10:36:37 -06005061
Simon Glass748a1d42021-07-06 10:36:41 -06005062 def testTiming(self):
5063 """Test output of timing information"""
5064 data = self._DoReadFile('055_sections.dts')
Simon Glass14d64e32025-04-29 07:21:59 -06005065 with terminal.capture() as (stdout, stderr):
Simon Glass748a1d42021-07-06 10:36:41 -06005066 state.TimingShow()
5067 self.assertIn('read:', stdout.getvalue())
5068 self.assertIn('compress:', stdout.getvalue())
5069
Simon Glassadfb8492021-11-03 21:09:18 -06005070 def testUpdateFdtInElf(self):
5071 """Test that we can update the devicetree in an ELF file"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005072 if not elf.ELF_TOOLS:
5073 self.skipTest('Python elftools not available')
Simon Glassadfb8492021-11-03 21:09:18 -06005074 infile = elf_fname = self.ElfTestFile('u_boot_binman_embed')
5075 outfile = os.path.join(self._indir, 'u-boot.out')
5076 begin_sym = 'dtb_embed_begin'
5077 end_sym = 'dtb_embed_end'
5078 retcode = self._DoTestFile(
5079 '060_fdt_update.dts', update_dtb=True,
5080 update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym]))
5081 self.assertEqual(0, retcode)
5082
5083 # Check that the output file does in fact contact a dtb with the binman
5084 # definition in the correct place
5085 syms = elf.GetSymbolFileOffset(infile,
5086 ['dtb_embed_begin', 'dtb_embed_end'])
Simon Glass80025522022-01-29 14:14:04 -07005087 data = tools.read_file(outfile)
Simon Glassadfb8492021-11-03 21:09:18 -06005088 dtb_data = data[syms['dtb_embed_begin'].offset:
5089 syms['dtb_embed_end'].offset]
5090
5091 dtb = fdt.Fdt.FromData(dtb_data)
5092 dtb.Scan()
5093 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
5094 self.assertEqual({
5095 'image-pos': 0,
5096 'offset': 0,
5097 '_testing:offset': 32,
5098 '_testing:size': 2,
5099 '_testing:image-pos': 32,
5100 'section@0/u-boot:offset': 0,
5101 'section@0/u-boot:size': len(U_BOOT_DATA),
5102 'section@0/u-boot:image-pos': 0,
5103 'section@0:offset': 0,
5104 'section@0:size': 16,
5105 'section@0:image-pos': 0,
5106
5107 'section@1/u-boot:offset': 0,
5108 'section@1/u-boot:size': len(U_BOOT_DATA),
5109 'section@1/u-boot:image-pos': 16,
5110 'section@1:offset': 16,
5111 'section@1:size': 16,
5112 'section@1:image-pos': 16,
5113 'size': 40
5114 }, props)
5115
5116 def testUpdateFdtInElfInvalid(self):
5117 """Test that invalid args are detected with --update-fdt-in-elf"""
5118 with self.assertRaises(ValueError) as e:
5119 self._DoTestFile('060_fdt_update.dts', update_fdt_in_elf='fred')
5120 self.assertIn("Invalid args ['fred'] to --update-fdt-in-elf",
5121 str(e.exception))
5122
5123 def testUpdateFdtInElfNoSyms(self):
5124 """Test that missing symbols are detected with --update-fdt-in-elf"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005125 if not elf.ELF_TOOLS:
5126 self.skipTest('Python elftools not available')
Simon Glassadfb8492021-11-03 21:09:18 -06005127 infile = elf_fname = self.ElfTestFile('u_boot_binman_embed')
5128 outfile = ''
5129 begin_sym = 'wrong_begin'
5130 end_sym = 'wrong_end'
5131 with self.assertRaises(ValueError) as e:
5132 self._DoTestFile(
5133 '060_fdt_update.dts',
5134 update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym]))
5135 self.assertIn("Expected two symbols 'wrong_begin' and 'wrong_end': got 0:",
5136 str(e.exception))
5137
5138 def testUpdateFdtInElfTooSmall(self):
5139 """Test that an over-large dtb is detected with --update-fdt-in-elf"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005140 if not elf.ELF_TOOLS:
5141 self.skipTest('Python elftools not available')
Simon Glassadfb8492021-11-03 21:09:18 -06005142 infile = elf_fname = self.ElfTestFile('u_boot_binman_embed_sm')
5143 outfile = os.path.join(self._indir, 'u-boot.out')
5144 begin_sym = 'dtb_embed_begin'
5145 end_sym = 'dtb_embed_end'
5146 with self.assertRaises(ValueError) as e:
5147 self._DoTestFile(
5148 '060_fdt_update.dts', update_dtb=True,
5149 update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym]))
5150 self.assertRegex(
5151 str(e.exception),
5152 "Not enough space in '.*u_boot_binman_embed_sm' for data length.*")
5153
Simon Glass88e04da2021-11-23 11:03:42 -07005154 def testVersion(self):
5155 """Test we can get the binman version"""
5156 version = '(unreleased)'
5157 self.assertEqual(version, state.GetVersion(self._indir))
5158
5159 with self.assertRaises(SystemExit):
Simon Glass14d64e32025-04-29 07:21:59 -06005160 with terminal.capture() as (_, stderr):
Simon Glass88e04da2021-11-23 11:03:42 -07005161 self._DoBinman('-V')
5162 self.assertEqual('Binman %s\n' % version, stderr.getvalue())
5163
5164 # Try running the tool too, just to be safe
5165 result = self._RunBinman('-V')
5166 self.assertEqual('Binman %s\n' % version, result.stderr)
5167
5168 # Set up a version file to make sure that works
5169 version = 'v2025.01-rc2'
Simon Glass80025522022-01-29 14:14:04 -07005170 tools.write_file(os.path.join(self._indir, 'version'), version,
Simon Glass88e04da2021-11-23 11:03:42 -07005171 binary=False)
5172 self.assertEqual(version, state.GetVersion(self._indir))
5173
Simon Glass637958f2021-11-23 21:09:50 -07005174 def testAltFormat(self):
5175 """Test that alternative formats can be used to extract"""
5176 self._DoReadFileRealDtb('213_fdtmap_alt_format.dts')
5177
5178 try:
5179 tmpdir, updated_fname = self._SetupImageInTmpdir()
Simon Glass14d64e32025-04-29 07:21:59 -06005180 with terminal.capture() as (stdout, _):
Simon Glass637958f2021-11-23 21:09:50 -07005181 self._DoBinman('extract', '-i', updated_fname, '-F', 'list')
5182 self.assertEqual(
5183 '''Flag (-F) Entry type Description
5184fdt fdtmap Extract the devicetree blob from the fdtmap
5185''',
5186 stdout.getvalue())
5187
5188 dtb = os.path.join(tmpdir, 'fdt.dtb')
5189 self._DoBinman('extract', '-i', updated_fname, '-F', 'fdt', '-f',
5190 dtb, 'fdtmap')
5191
5192 # Check that we can read it and it can be scanning, meaning it does
5193 # not have a 16-byte fdtmap header
Simon Glass80025522022-01-29 14:14:04 -07005194 data = tools.read_file(dtb)
Simon Glass637958f2021-11-23 21:09:50 -07005195 dtb = fdt.Fdt.FromData(data)
5196 dtb.Scan()
5197
5198 # Now check u-boot which has no alt_format
5199 fname = os.path.join(tmpdir, 'fdt.dtb')
5200 self._DoBinman('extract', '-i', updated_fname, '-F', 'dummy',
5201 '-f', fname, 'u-boot')
Simon Glass80025522022-01-29 14:14:04 -07005202 data = tools.read_file(fname)
Simon Glass637958f2021-11-23 21:09:50 -07005203 self.assertEqual(U_BOOT_DATA, data)
5204
5205 finally:
5206 shutil.rmtree(tmpdir)
5207
Simon Glass0b00ae62021-11-23 21:09:52 -07005208 def testExtblobList(self):
5209 """Test an image with an external blob list"""
5210 data = self._DoReadFile('215_blob_ext_list.dts')
5211 self.assertEqual(REFCODE_DATA + FSP_M_DATA, data)
5212
5213 def testExtblobListMissing(self):
5214 """Test an image with a missing external blob"""
5215 with self.assertRaises(ValueError) as e:
5216 self._DoReadFile('216_blob_ext_list_missing.dts')
5217 self.assertIn("Filename 'missing-file' not found in input path",
5218 str(e.exception))
5219
5220 def testExtblobListMissingOk(self):
5221 """Test an image with an missing external blob that is allowed"""
Simon Glass14d64e32025-04-29 07:21:59 -06005222 with terminal.capture() as (stdout, stderr):
Simon Glass0b00ae62021-11-23 21:09:52 -07005223 self._DoTestFile('216_blob_ext_list_missing.dts',
5224 allow_missing=True)
5225 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07005226 self.assertRegex(err, "Image 'image'.*missing.*: blob-ext")
Simon Glass0b00ae62021-11-23 21:09:52 -07005227
Simon Glass3efb2972021-11-23 21:08:59 -07005228 def testFip(self):
5229 """Basic test of generation of an ARM Firmware Image Package (FIP)"""
5230 data = self._DoReadFile('203_fip.dts')
5231 hdr, fents = fip_util.decode_fip(data)
5232 self.assertEqual(fip_util.HEADER_MAGIC, hdr.name)
5233 self.assertEqual(fip_util.HEADER_SERIAL, hdr.serial)
5234 self.assertEqual(0x123, hdr.flags)
5235
5236 self.assertEqual(2, len(fents))
5237
5238 fent = fents[0]
5239 self.assertEqual(
5240 bytes([0x47, 0xd4, 0x08, 0x6d, 0x4c, 0xfe, 0x98, 0x46,
5241 0x9b, 0x95, 0x29, 0x50, 0xcb, 0xbd, 0x5a, 0x0]), fent.uuid)
5242 self.assertEqual('soc-fw', fent.fip_type)
5243 self.assertEqual(0x88, fent.offset)
5244 self.assertEqual(len(ATF_BL31_DATA), fent.size)
5245 self.assertEqual(0x123456789abcdef, fent.flags)
5246 self.assertEqual(ATF_BL31_DATA, fent.data)
5247 self.assertEqual(True, fent.valid)
5248
5249 fent = fents[1]
5250 self.assertEqual(
5251 bytes([0x65, 0x92, 0x27, 0x03, 0x2f, 0x74, 0xe6, 0x44,
5252 0x8d, 0xff, 0x57, 0x9a, 0xc1, 0xff, 0x06, 0x10]), fent.uuid)
5253 self.assertEqual('scp-fwu-cfg', fent.fip_type)
5254 self.assertEqual(0x8c, fent.offset)
5255 self.assertEqual(len(ATF_BL31_DATA), fent.size)
5256 self.assertEqual(0, fent.flags)
5257 self.assertEqual(ATF_BL2U_DATA, fent.data)
5258 self.assertEqual(True, fent.valid)
5259
5260 def testFipOther(self):
5261 """Basic FIP with something that isn't a external blob"""
5262 data = self._DoReadFile('204_fip_other.dts')
5263 hdr, fents = fip_util.decode_fip(data)
5264
5265 self.assertEqual(2, len(fents))
5266 fent = fents[1]
5267 self.assertEqual('rot-cert', fent.fip_type)
5268 self.assertEqual(b'aa', fent.data)
5269
Simon Glass3efb2972021-11-23 21:08:59 -07005270 def testFipNoType(self):
5271 """FIP with an entry of an unknown type"""
5272 with self.assertRaises(ValueError) as e:
5273 self._DoReadFile('205_fip_no_type.dts')
5274 self.assertIn("Must provide a fip-type (node name 'u-boot' is not a known FIP type)",
5275 str(e.exception))
5276
5277 def testFipUuid(self):
5278 """Basic FIP with a manual uuid"""
5279 data = self._DoReadFile('206_fip_uuid.dts')
5280 hdr, fents = fip_util.decode_fip(data)
5281
5282 self.assertEqual(2, len(fents))
5283 fent = fents[1]
5284 self.assertEqual(None, fent.fip_type)
5285 self.assertEqual(
5286 bytes([0xfc, 0x65, 0x13, 0x92, 0x4a, 0x5b, 0x11, 0xec,
5287 0x94, 0x35, 0xff, 0x2d, 0x1c, 0xfc, 0x79, 0x9c]),
5288 fent.uuid)
5289 self.assertEqual(U_BOOT_DATA, fent.data)
5290
5291 def testFipLs(self):
5292 """Test listing a FIP"""
5293 data = self._DoReadFileRealDtb('207_fip_ls.dts')
5294 hdr, fents = fip_util.decode_fip(data)
5295
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01005296 tmpdir = None
Simon Glass3efb2972021-11-23 21:08:59 -07005297 try:
5298 tmpdir, updated_fname = self._SetupImageInTmpdir()
Simon Glass14d64e32025-04-29 07:21:59 -06005299 with terminal.capture() as (stdout, stderr):
Simon Glass3efb2972021-11-23 21:08:59 -07005300 self._DoBinman('ls', '-i', updated_fname)
5301 finally:
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01005302 if tmpdir:
5303 shutil.rmtree(tmpdir)
Simon Glass3efb2972021-11-23 21:08:59 -07005304 lines = stdout.getvalue().splitlines()
5305 expected = [
Simon Glass49cd2b32023-02-07 14:34:18 -07005306'Name Image-pos Size Entry-type Offset Uncomp-size',
5307'--------------------------------------------------------------',
5308'image 0 2d3 section 0',
5309' atf-fip 0 90 atf-fip 0',
5310' soc-fw 88 4 blob-ext 88',
5311' u-boot 8c 4 u-boot 8c',
5312' fdtmap 90 243 fdtmap 90',
Simon Glass3efb2972021-11-23 21:08:59 -07005313]
5314 self.assertEqual(expected, lines)
5315
5316 image = control.images['image']
5317 entries = image.GetEntries()
5318 fdtmap = entries['fdtmap']
5319
5320 fdtmap_data = data[fdtmap.image_pos:fdtmap.image_pos + fdtmap.size]
5321 magic = fdtmap_data[:8]
5322 self.assertEqual(b'_FDTMAP_', magic)
Simon Glass80025522022-01-29 14:14:04 -07005323 self.assertEqual(tools.get_bytes(0, 8), fdtmap_data[8:16])
Simon Glass3efb2972021-11-23 21:08:59 -07005324
5325 fdt_data = fdtmap_data[16:]
5326 dtb = fdt.Fdt.FromData(fdt_data)
5327 dtb.Scan()
5328 props = self._GetPropTree(dtb, BASE_DTB_PROPS, prefix='/')
5329 self.assertEqual({
5330 'atf-fip/soc-fw:image-pos': 136,
5331 'atf-fip/soc-fw:offset': 136,
5332 'atf-fip/soc-fw:size': 4,
5333 'atf-fip/u-boot:image-pos': 140,
5334 'atf-fip/u-boot:offset': 140,
5335 'atf-fip/u-boot:size': 4,
5336 'atf-fip:image-pos': 0,
5337 'atf-fip:offset': 0,
5338 'atf-fip:size': 144,
5339 'image-pos': 0,
5340 'offset': 0,
5341 'fdtmap:image-pos': fdtmap.image_pos,
5342 'fdtmap:offset': fdtmap.offset,
5343 'fdtmap:size': len(fdtmap_data),
5344 'size': len(data),
5345 }, props)
5346
5347 def testFipExtractOneEntry(self):
5348 """Test extracting a single entry fron an FIP"""
5349 self._DoReadFileRealDtb('207_fip_ls.dts')
Simon Glass80025522022-01-29 14:14:04 -07005350 image_fname = tools.get_output_filename('image.bin')
Simon Glass3efb2972021-11-23 21:08:59 -07005351 fname = os.path.join(self._indir, 'output.extact')
5352 control.ExtractEntries(image_fname, fname, None, ['atf-fip/u-boot'])
Simon Glass80025522022-01-29 14:14:04 -07005353 data = tools.read_file(fname)
Simon Glass3efb2972021-11-23 21:08:59 -07005354 self.assertEqual(U_BOOT_DATA, data)
5355
5356 def testFipReplace(self):
5357 """Test replacing a single file in a FIP"""
Simon Glass80025522022-01-29 14:14:04 -07005358 expected = U_BOOT_DATA + tools.get_bytes(0x78, 50)
Simon Glass3efb2972021-11-23 21:08:59 -07005359 data = self._DoReadFileRealDtb('208_fip_replace.dts')
Simon Glass80025522022-01-29 14:14:04 -07005360 updated_fname = tools.get_output_filename('image-updated.bin')
5361 tools.write_file(updated_fname, data)
Simon Glass3efb2972021-11-23 21:08:59 -07005362 entry_name = 'atf-fip/u-boot'
5363 control.WriteEntry(updated_fname, entry_name, expected,
5364 allow_resize=True)
5365 actual = control.ReadEntry(updated_fname, entry_name)
5366 self.assertEqual(expected, actual)
5367
Simon Glass80025522022-01-29 14:14:04 -07005368 new_data = tools.read_file(updated_fname)
Simon Glass3efb2972021-11-23 21:08:59 -07005369 hdr, fents = fip_util.decode_fip(new_data)
5370
5371 self.assertEqual(2, len(fents))
5372
5373 # Check that the FIP entry is updated
5374 fent = fents[1]
5375 self.assertEqual(0x8c, fent.offset)
5376 self.assertEqual(len(expected), fent.size)
5377 self.assertEqual(0, fent.flags)
5378 self.assertEqual(expected, fent.data)
5379 self.assertEqual(True, fent.valid)
5380
5381 def testFipMissing(self):
Simon Glass14d64e32025-04-29 07:21:59 -06005382 with terminal.capture() as (stdout, stderr):
Simon Glass3efb2972021-11-23 21:08:59 -07005383 self._DoTestFile('209_fip_missing.dts', allow_missing=True)
5384 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07005385 self.assertRegex(err, "Image 'image'.*missing.*: rmm-fw")
Simon Glass3efb2972021-11-23 21:08:59 -07005386
5387 def testFipSize(self):
5388 """Test a FIP with a size property"""
5389 data = self._DoReadFile('210_fip_size.dts')
5390 self.assertEqual(0x100 + len(U_BOOT_DATA), len(data))
5391 hdr, fents = fip_util.decode_fip(data)
5392 self.assertEqual(fip_util.HEADER_MAGIC, hdr.name)
5393 self.assertEqual(fip_util.HEADER_SERIAL, hdr.serial)
5394
5395 self.assertEqual(1, len(fents))
5396
5397 fent = fents[0]
5398 self.assertEqual('soc-fw', fent.fip_type)
5399 self.assertEqual(0x60, fent.offset)
5400 self.assertEqual(len(ATF_BL31_DATA), fent.size)
5401 self.assertEqual(ATF_BL31_DATA, fent.data)
5402 self.assertEqual(True, fent.valid)
5403
5404 rest = data[0x60 + len(ATF_BL31_DATA):0x100]
Simon Glass80025522022-01-29 14:14:04 -07005405 self.assertEqual(tools.get_bytes(0xff, len(rest)), rest)
Simon Glass3efb2972021-11-23 21:08:59 -07005406
5407 def testFipBadAlign(self):
5408 """Test that an invalid alignment value in a FIP is detected"""
5409 with self.assertRaises(ValueError) as e:
5410 self._DoTestFile('211_fip_bad_align.dts')
5411 self.assertIn(
5412 "Node \'/binman/atf-fip\': FIP alignment 31 must be a power of two",
5413 str(e.exception))
5414
5415 def testFipCollection(self):
5416 """Test using a FIP in a collection"""
5417 data = self._DoReadFile('212_fip_collection.dts')
5418 entry1 = control.images['image'].GetEntries()['collection']
5419 data1 = data[:entry1.size]
5420 hdr1, fents2 = fip_util.decode_fip(data1)
5421
5422 entry2 = control.images['image'].GetEntries()['atf-fip']
5423 data2 = data[entry2.offset:entry2.offset + entry2.size]
5424 hdr1, fents2 = fip_util.decode_fip(data2)
5425
5426 # The 'collection' entry should have U-Boot included at the end
5427 self.assertEqual(entry1.size - len(U_BOOT_DATA), entry2.size)
5428 self.assertEqual(data1, data2 + U_BOOT_DATA)
5429 self.assertEqual(U_BOOT_DATA, data1[-4:])
5430
5431 # There should be a U-Boot after the final FIP
5432 self.assertEqual(U_BOOT_DATA, data[-4:])
Simon Glass76f496d2021-07-06 10:36:37 -06005433
Simon Glassccae6862022-01-12 13:10:35 -07005434 def testFakeBlob(self):
5435 """Test handling of faking an external blob"""
Simon Glass14d64e32025-04-29 07:21:59 -06005436 with terminal.capture() as (stdout, stderr):
Simon Glassccae6862022-01-12 13:10:35 -07005437 self._DoTestFile('217_fake_blob.dts', allow_missing=True,
5438 allow_fake_blobs=True)
5439 err = stderr.getvalue()
5440 self.assertRegex(
5441 err,
5442 "Image '.*' has faked external blobs and is non-functional: .*")
Simon Glassccae6862022-01-12 13:10:35 -07005443
Simon Glassceb5f912022-01-09 20:13:46 -07005444 def testExtblobListFaked(self):
5445 """Test an extblob with missing external blob that are faked"""
Simon Glass14d64e32025-04-29 07:21:59 -06005446 with terminal.capture() as (stdout, stderr):
Simon Glassceb5f912022-01-09 20:13:46 -07005447 self._DoTestFile('216_blob_ext_list_missing.dts',
5448 allow_fake_blobs=True)
5449 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07005450 self.assertRegex(err, "Image 'image'.*faked.*: blob-ext-list")
Simon Glassceb5f912022-01-09 20:13:46 -07005451
Simon Glass162017b2022-01-09 20:13:57 -07005452 def testListBintools(self):
5453 args = ['tool', '--list']
Simon Glass14d64e32025-04-29 07:21:59 -06005454 with terminal.capture() as (stdout, _):
Simon Glass162017b2022-01-09 20:13:57 -07005455 self._DoBinman(*args)
5456 out = stdout.getvalue().splitlines()
5457 self.assertTrue(len(out) >= 2)
5458
5459 def testFetchBintools(self):
5460 def fail_download(url):
Simon Glass80025522022-01-29 14:14:04 -07005461 """Take the tools.download() function by raising an exception"""
Simon Glass162017b2022-01-09 20:13:57 -07005462 raise urllib.error.URLError('my error')
5463
5464 args = ['tool']
5465 with self.assertRaises(ValueError) as e:
5466 self._DoBinman(*args)
5467 self.assertIn("Invalid arguments to 'tool' subcommand",
5468 str(e.exception))
5469
5470 args = ['tool', '--fetch']
5471 with self.assertRaises(ValueError) as e:
5472 self._DoBinman(*args)
5473 self.assertIn('Please specify bintools to fetch', str(e.exception))
5474
5475 args = ['tool', '--fetch', '_testing']
Simon Glass80025522022-01-29 14:14:04 -07005476 with unittest.mock.patch.object(tools, 'download',
Simon Glass162017b2022-01-09 20:13:57 -07005477 side_effect=fail_download):
Simon Glass14d64e32025-04-29 07:21:59 -06005478 with terminal.capture() as (stdout, _):
Simon Glass162017b2022-01-09 20:13:57 -07005479 self._DoBinman(*args)
5480 self.assertIn('failed to fetch with all methods', stdout.getvalue())
5481
Simon Glass620c4462022-01-09 20:14:11 -07005482 def testBintoolDocs(self):
5483 """Test for creation of bintool documentation"""
Simon Glass14d64e32025-04-29 07:21:59 -06005484 with terminal.capture() as (stdout, stderr):
Simon Glass620c4462022-01-09 20:14:11 -07005485 control.write_bintool_docs(control.bintool.Bintool.get_tool_list())
5486 self.assertTrue(len(stdout.getvalue()) > 0)
5487
5488 def testBintoolDocsMissing(self):
5489 """Test handling of missing bintool documentation"""
5490 with self.assertRaises(ValueError) as e:
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(
5493 control.bintool.Bintool.get_tool_list(), 'mkimage')
5494 self.assertIn('Documentation is missing for modules: mkimage',
5495 str(e.exception))
5496
Jan Kiszka58c407f2022-01-28 20:37:53 +01005497 def testListWithGenNode(self):
5498 """Check handling of an FDT map when the section cannot be found"""
5499 entry_args = {
5500 'of-list': 'test-fdt1 test-fdt2',
5501 }
5502 data = self._DoReadFileDtb(
5503 '219_fit_gennode.dts',
5504 entry_args=entry_args,
5505 use_real_dtb=True,
5506 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])
5507
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01005508 tmpdir = None
Jan Kiszka58c407f2022-01-28 20:37:53 +01005509 try:
5510 tmpdir, updated_fname = self._SetupImageInTmpdir()
Simon Glass14d64e32025-04-29 07:21:59 -06005511 with terminal.capture() as (stdout, stderr):
Jan Kiszka58c407f2022-01-28 20:37:53 +01005512 self._RunBinman('ls', '-i', updated_fname)
5513 finally:
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01005514 if tmpdir:
5515 shutil.rmtree(tmpdir)
Jan Kiszka58c407f2022-01-28 20:37:53 +01005516
Alper Nebi Yasake706d872022-02-08 01:08:05 +03005517 def testFitSubentryUsesBintool(self):
5518 """Test that binman FIT subentries can use bintools"""
Simon Glass5dc22cf2025-02-03 09:26:42 -07005519 command.TEST_RESULT = self._HandleGbbCommand
Alper Nebi Yasake706d872022-02-08 01:08:05 +03005520 entry_args = {
5521 'keydir': 'devkeys',
5522 'bmpblk': 'bmpblk.bin',
5523 }
5524 data, _, _, _ = self._DoReadFileDtb('220_fit_subentry_bintool.dts',
5525 entry_args=entry_args)
5526
Alper Nebi Yasakd4553262022-02-08 01:08:07 +03005527 expected = (GBB_DATA + GBB_DATA + tools.get_bytes(0, 8) +
5528 tools.get_bytes(0, 0x2180 - 16))
Alper Nebi Yasake706d872022-02-08 01:08:05 +03005529 self.assertIn(expected, data)
5530
5531 def testFitSubentryMissingBintool(self):
5532 """Test that binman reports missing bintools for FIT subentries"""
5533 entry_args = {
5534 'keydir': 'devkeys',
5535 }
Simon Glass14d64e32025-04-29 07:21:59 -06005536 with terminal.capture() as (_, stderr):
Alper Nebi Yasake706d872022-02-08 01:08:05 +03005537 self._DoTestFile('220_fit_subentry_bintool.dts',
5538 force_missing_bintools='futility', entry_args=entry_args)
5539 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07005540 self.assertRegex(err, "Image 'image'.*missing bintools.*: futility")
Simon Glassccae6862022-01-12 13:10:35 -07005541
Alper Nebi Yasak1e4ffd82022-02-09 22:02:35 +03005542 def testFitSubentryHashSubnode(self):
5543 """Test an image with a FIT inside"""
Marek Vasutf7413f02023-07-18 07:23:58 -06005544 self._SetupSplElf()
Alper Nebi Yasak1e4ffd82022-02-09 22:02:35 +03005545 data, _, _, out_dtb_name = self._DoReadFileDtb(
5546 '221_fit_subentry_hash.dts', use_real_dtb=True, update_dtb=True)
5547
5548 mkimage_dtb = fdt.Fdt.FromData(data)
5549 mkimage_dtb.Scan()
5550 binman_dtb = fdt.Fdt(out_dtb_name)
5551 binman_dtb.Scan()
5552
5553 # Check that binman didn't add hash values
5554 fnode = binman_dtb.GetNode('/binman/fit/images/kernel/hash')
5555 self.assertNotIn('value', fnode.props)
5556
5557 fnode = binman_dtb.GetNode('/binman/fit/images/fdt-1/hash')
5558 self.assertNotIn('value', fnode.props)
5559
5560 # Check that mkimage added hash values
5561 fnode = mkimage_dtb.GetNode('/images/kernel/hash')
5562 self.assertIn('value', fnode.props)
5563
5564 fnode = mkimage_dtb.GetNode('/images/fdt-1/hash')
5565 self.assertIn('value', fnode.props)
5566
Roger Quadros5cdcea02022-02-19 20:50:04 +02005567 def testPackTeeOs(self):
5568 """Test that an image with an TEE binary can be created"""
5569 data = self._DoReadFile('222_tee_os.dts')
5570 self.assertEqual(TEE_OS_DATA, data[:len(TEE_OS_DATA)])
5571
Neha Malcom Francis59be2552023-12-05 15:12:18 +05305572 def testPackTiDm(self):
5573 """Test that an image with a TI DM binary can be created"""
5574 data = self._DoReadFile('225_ti_dm.dts')
5575 self.assertEqual(TI_DM_DATA, data[:len(TI_DM_DATA)])
5576
Simon Glass912339f2022-02-08 11:50:03 -07005577 def testFitFdtOper(self):
5578 """Check handling of a specified FIT operation"""
5579 entry_args = {
5580 'of-list': 'test-fdt1 test-fdt2',
5581 'default-dt': 'test-fdt2',
5582 }
5583 self._DoReadFileDtb(
5584 '223_fit_fdt_oper.dts',
5585 entry_args=entry_args,
5586 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
5587
5588 def testFitFdtBadOper(self):
5589 """Check handling of an FDT map when the section cannot be found"""
5590 with self.assertRaises(ValueError) as exc:
5591 self._DoReadFileDtb('224_fit_bad_oper.dts')
Simon Glass05f71dc2022-03-05 20:19:09 -07005592 self.assertIn("Node '/binman/fit': subnode 'images/@fdt-SEQ': Unknown operation 'unknown'",
Simon Glass912339f2022-02-08 11:50:03 -07005593 str(exc.exception))
5594
Simon Glassdd156a42022-03-05 20:18:59 -07005595 def test_uses_expand_size(self):
5596 """Test that the 'expand-size' property cannot be used anymore"""
5597 with self.assertRaises(ValueError) as e:
5598 data = self._DoReadFile('225_expand_size_bad.dts')
5599 self.assertIn(
5600 "Node '/binman/u-boot': Please use 'extend-size' instead of 'expand-size'",
5601 str(e.exception))
5602
Simon Glass5f423422022-03-05 20:19:12 -07005603 def testFitSplitElf(self):
5604 """Test an image with an FIT with an split-elf operation"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005605 if not elf.ELF_TOOLS:
5606 self.skipTest('Python elftools not available')
Simon Glass5f423422022-03-05 20:19:12 -07005607 entry_args = {
5608 'of-list': 'test-fdt1 test-fdt2',
5609 'default-dt': 'test-fdt2',
5610 'atf-bl31-path': 'bl31.elf',
5611 'tee-os-path': 'tee.elf',
5612 }
5613 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
5614 data = self._DoReadFileDtb(
5615 '226_fit_split_elf.dts',
5616 entry_args=entry_args,
5617 extra_indirs=[test_subdir])[0]
5618
5619 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
5620 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
5621
5622 base_keys = {'description', 'type', 'arch', 'os', 'compression',
5623 'data', 'load'}
5624 dtb = fdt.Fdt.FromData(fit_data)
5625 dtb.Scan()
5626
5627 elf_data = tools.read_file(os.path.join(self._indir, 'bl31.elf'))
5628 segments, entry = elf.read_loadable_segments(elf_data)
5629
5630 # We assume there are two segments
Brandon Maiera657bc62024-06-04 16:16:05 +00005631 self.assertEqual(2, len(segments))
Simon Glass5f423422022-03-05 20:19:12 -07005632
5633 atf1 = dtb.GetNode('/images/atf-1')
5634 _, start, data = segments[0]
5635 self.assertEqual(base_keys | {'entry'}, atf1.props.keys())
5636 self.assertEqual(entry,
5637 fdt_util.fdt32_to_cpu(atf1.props['entry'].value))
5638 self.assertEqual(start,
5639 fdt_util.fdt32_to_cpu(atf1.props['load'].value))
5640 self.assertEqual(data, atf1.props['data'].bytes)
5641
Jonas Karlmand2c7d902023-01-21 19:01:48 +00005642 hash_node = atf1.FindNode('hash')
5643 self.assertIsNotNone(hash_node)
5644 self.assertEqual({'algo', 'value'}, hash_node.props.keys())
5645
Simon Glass5f423422022-03-05 20:19:12 -07005646 atf2 = dtb.GetNode('/images/atf-2')
5647 self.assertEqual(base_keys, atf2.props.keys())
5648 _, start, data = segments[1]
5649 self.assertEqual(start,
5650 fdt_util.fdt32_to_cpu(atf2.props['load'].value))
5651 self.assertEqual(data, atf2.props['data'].bytes)
5652
Jonas Karlmand2c7d902023-01-21 19:01:48 +00005653 hash_node = atf2.FindNode('hash')
5654 self.assertIsNotNone(hash_node)
5655 self.assertEqual({'algo', 'value'}, hash_node.props.keys())
5656
5657 hash_node = dtb.GetNode('/images/tee-1/hash-1')
5658 self.assertIsNotNone(hash_node)
5659 self.assertEqual({'algo', 'value'}, hash_node.props.keys())
5660
Simon Glass5f423422022-03-05 20:19:12 -07005661 conf = dtb.GetNode('/configurations')
5662 self.assertEqual({'default'}, conf.props.keys())
5663
5664 for subnode in conf.subnodes:
5665 self.assertEqual({'description', 'fdt', 'loadables'},
5666 subnode.props.keys())
5667 self.assertEqual(
5668 ['atf-1', 'atf-2', 'tee-1', 'tee-2'],
5669 fdt_util.GetStringList(subnode, 'loadables'))
5670
5671 def _check_bad_fit(self, dts):
5672 """Check a bad FIT
5673
5674 This runs with the given dts and returns the assertion raised
5675
5676 Args:
5677 dts (str): dts filename to use
5678
5679 Returns:
5680 str: Assertion string raised
5681 """
5682 entry_args = {
5683 'of-list': 'test-fdt1 test-fdt2',
5684 'default-dt': 'test-fdt2',
5685 'atf-bl31-path': 'bl31.elf',
5686 'tee-os-path': 'tee.elf',
5687 }
5688 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
5689 with self.assertRaises(ValueError) as exc:
5690 self._DoReadFileDtb(dts, entry_args=entry_args,
5691 extra_indirs=[test_subdir])[0]
5692 return str(exc.exception)
5693
5694 def testFitSplitElfBadElf(self):
5695 """Test a FIT split-elf operation with an invalid ELF file"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005696 if not elf.ELF_TOOLS:
5697 self.skipTest('Python elftools not available')
Simon Glass5f423422022-03-05 20:19:12 -07005698 TestFunctional._MakeInputFile('bad.elf', tools.get_bytes(100, 100))
5699 entry_args = {
5700 'of-list': 'test-fdt1 test-fdt2',
5701 'default-dt': 'test-fdt2',
5702 'atf-bl31-path': 'bad.elf',
5703 'tee-os-path': 'tee.elf',
5704 }
5705 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
5706 with self.assertRaises(ValueError) as exc:
5707 self._DoReadFileDtb(
5708 '226_fit_split_elf.dts',
5709 entry_args=entry_args,
5710 extra_indirs=[test_subdir])[0]
5711 self.assertIn(
5712 "Node '/binman/fit': subnode 'images/@atf-SEQ': Failed to read ELF file: Magic number does not match",
5713 str(exc.exception))
5714
Simon Glass5f423422022-03-05 20:19:12 -07005715 def checkFitSplitElf(self, **kwargs):
Simon Glass7d3e4072022-08-07 09:46:46 -06005716 """Test an split-elf FIT with a missing ELF file
5717
5718 Args:
5719 kwargs (dict of str): Arguments to pass to _DoTestFile()
5720
5721 Returns:
5722 tuple:
5723 str: stdout result
5724 str: stderr result
5725 """
Simon Glass5f423422022-03-05 20:19:12 -07005726 entry_args = {
5727 'of-list': 'test-fdt1 test-fdt2',
5728 'default-dt': 'test-fdt2',
5729 'atf-bl31-path': 'bl31.elf',
5730 'tee-os-path': 'missing.elf',
5731 }
5732 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
Simon Glass14d64e32025-04-29 07:21:59 -06005733 with terminal.capture() as (stdout, stderr):
Simon Glass5f423422022-03-05 20:19:12 -07005734 self._DoTestFile(
5735 '226_fit_split_elf.dts', entry_args=entry_args,
Simon Glass7d3e4072022-08-07 09:46:46 -06005736 extra_indirs=[test_subdir], verbosity=3, **kwargs)
5737 out = stdout.getvalue()
5738 err = stderr.getvalue()
5739 return out, err
Simon Glass5f423422022-03-05 20:19:12 -07005740
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005741 def testFitSplitElfBadDirective(self):
5742 """Test a FIT split-elf invalid fit,xxx directive in an image node"""
5743 if not elf.ELF_TOOLS:
5744 self.skipTest('Python elftools not available')
5745 err = self._check_bad_fit('227_fit_bad_dir.dts')
5746 self.assertIn(
5747 "Node '/binman/fit': subnode 'images/@atf-SEQ': Unknown directive 'fit,something'",
5748 err)
5749
5750 def testFitSplitElfBadDirectiveConfig(self):
5751 """Test a FIT split-elf with invalid fit,xxx directive in config"""
5752 if not elf.ELF_TOOLS:
5753 self.skipTest('Python elftools not available')
5754 err = self._check_bad_fit('228_fit_bad_dir_config.dts')
5755 self.assertEqual(
5756 "Node '/binman/fit': subnode 'configurations/@config-SEQ': Unknown directive 'fit,config'",
5757 err)
5758
5759
Simon Glass5f423422022-03-05 20:19:12 -07005760 def testFitSplitElfMissing(self):
5761 """Test an split-elf FIT with a missing ELF file"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005762 if not elf.ELF_TOOLS:
5763 self.skipTest('Python elftools not available')
Simon Glass7d3e4072022-08-07 09:46:46 -06005764 out, err = self.checkFitSplitElf(allow_missing=True)
Simon Glass5f423422022-03-05 20:19:12 -07005765 self.assertRegex(
5766 err,
5767 "Image '.*' is missing external blobs and is non-functional: .*")
Simon Glass7d3e4072022-08-07 09:46:46 -06005768 self.assertNotRegex(out, '.*Faked blob.*')
5769 fname = tools.get_output_filename('binman-fake/missing.elf')
5770 self.assertFalse(os.path.exists(fname))
Simon Glass5f423422022-03-05 20:19:12 -07005771
5772 def testFitSplitElfFaked(self):
5773 """Test an split-elf FIT with faked ELF file"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005774 if not elf.ELF_TOOLS:
5775 self.skipTest('Python elftools not available')
Simon Glass7d3e4072022-08-07 09:46:46 -06005776 out, err = self.checkFitSplitElf(allow_missing=True, allow_fake_blobs=True)
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.assertRegex(
5781 out,
5782 "Entry '/binman/fit/images/@tee-SEQ/tee-os': Faked blob '.*binman-fake/missing.elf")
5783 fname = tools.get_output_filename('binman-fake/missing.elf')
5784 self.assertTrue(os.path.exists(fname))
Simon Glass5f423422022-03-05 20:19:12 -07005785
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005786 def testMkimageMissingBlob(self):
5787 """Test using mkimage to build an image"""
Simon Glass14d64e32025-04-29 07:21:59 -06005788 with terminal.capture() as (stdout, stderr):
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005789 self._DoTestFile('229_mkimage_missing.dts', allow_missing=True,
5790 allow_fake_blobs=True)
5791 err = stderr.getvalue()
5792 self.assertRegex(
5793 err,
5794 "Image '.*' has faked external blobs and is non-functional: .*")
5795
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005796 def testPreLoad(self):
5797 """Test an image with a pre-load header"""
5798 entry_args = {
Simon Glasse2dfb962023-07-24 09:19:57 -06005799 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005800 }
Simon Glasse2dfb962023-07-24 09:19:57 -06005801 data = self._DoReadFileDtb(
5802 '230_pre_load.dts', entry_args=entry_args,
5803 extra_indirs=[os.path.join(self._binman_dir, 'test')])[0]
Paul HENRYS5cf82892025-02-24 22:20:55 +01005804
5805 image_fname = tools.get_output_filename('image.bin')
5806 is_signed = self._CheckPreload(image_fname, self.TestFile("dev.key"))
5807
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005808 self.assertEqual(PRE_LOAD_MAGIC, data[:len(PRE_LOAD_MAGIC)])
5809 self.assertEqual(PRE_LOAD_VERSION, data[4:4 + len(PRE_LOAD_VERSION)])
5810 self.assertEqual(PRE_LOAD_HDR_SIZE, data[8:8 + len(PRE_LOAD_HDR_SIZE)])
Paul HENRYS5cf82892025-02-24 22:20:55 +01005811 self.assertEqual(is_signed, True)
Simon Glasse2dfb962023-07-24 09:19:57 -06005812
5813 def testPreLoadNoKey(self):
5814 """Test an image with a pre-load heade0r with missing key"""
5815 with self.assertRaises(FileNotFoundError) as exc:
5816 self._DoReadFile('230_pre_load.dts')
5817 self.assertIn("No such file or directory: 'dev.key'",
5818 str(exc.exception))
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005819
5820 def testPreLoadPkcs(self):
5821 """Test an image with a pre-load header with padding pkcs"""
Simon Glasse2dfb962023-07-24 09:19:57 -06005822 entry_args = {
5823 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5824 }
5825 data = self._DoReadFileDtb('231_pre_load_pkcs.dts',
5826 entry_args=entry_args)[0]
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005827 self.assertEqual(PRE_LOAD_MAGIC, data[:len(PRE_LOAD_MAGIC)])
5828 self.assertEqual(PRE_LOAD_VERSION, data[4:4 + len(PRE_LOAD_VERSION)])
5829 self.assertEqual(PRE_LOAD_HDR_SIZE, data[8:8 + len(PRE_LOAD_HDR_SIZE)])
5830
5831 def testPreLoadPss(self):
5832 """Test an image with a pre-load header with padding pss"""
Simon Glasse2dfb962023-07-24 09:19:57 -06005833 entry_args = {
5834 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5835 }
5836 data = self._DoReadFileDtb('232_pre_load_pss.dts',
5837 entry_args=entry_args)[0]
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005838 self.assertEqual(PRE_LOAD_MAGIC, data[:len(PRE_LOAD_MAGIC)])
5839 self.assertEqual(PRE_LOAD_VERSION, data[4:4 + len(PRE_LOAD_VERSION)])
5840 self.assertEqual(PRE_LOAD_HDR_SIZE, data[8:8 + len(PRE_LOAD_HDR_SIZE)])
5841
5842 def testPreLoadInvalidPadding(self):
5843 """Test an image with a pre-load header with an invalid padding"""
Simon Glasse2dfb962023-07-24 09:19:57 -06005844 entry_args = {
5845 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5846 }
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005847 with self.assertRaises(ValueError) as e:
Simon Glasse2dfb962023-07-24 09:19:57 -06005848 self._DoReadFileDtb('233_pre_load_invalid_padding.dts',
5849 entry_args=entry_args)
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005850
5851 def testPreLoadInvalidSha(self):
5852 """Test an image with a pre-load header with an invalid hash"""
Simon Glasse2dfb962023-07-24 09:19:57 -06005853 entry_args = {
5854 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5855 }
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005856 with self.assertRaises(ValueError) as e:
Simon Glasse2dfb962023-07-24 09:19:57 -06005857 self._DoReadFileDtb('234_pre_load_invalid_sha.dts',
5858 entry_args=entry_args)
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005859
5860 def testPreLoadInvalidAlgo(self):
5861 """Test an image with a pre-load header with an invalid algo"""
5862 with self.assertRaises(ValueError) as e:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005863 data = self._DoReadFile('235_pre_load_invalid_algo.dts')
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005864
5865 def testPreLoadInvalidKey(self):
5866 """Test an image with a pre-load header with an invalid key"""
Simon Glasse2dfb962023-07-24 09:19:57 -06005867 entry_args = {
5868 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5869 }
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005870 with self.assertRaises(ValueError) as e:
Simon Glasse2dfb962023-07-24 09:19:57 -06005871 data = self._DoReadFileDtb('236_pre_load_invalid_key.dts',
5872 entry_args=entry_args)
Roger Quadros5cdcea02022-02-19 20:50:04 +02005873
Alper Nebi Yasak5cff63f2022-03-27 18:31:44 +03005874 def _CheckSafeUniqueNames(self, *images):
5875 """Check all entries of given images for unsafe unique names"""
5876 for image in images:
5877 entries = {}
5878 image._CollectEntries(entries, {}, image)
5879 for entry in entries.values():
5880 uniq = entry.GetUniqueName()
5881
5882 # Used as part of a filename, so must not be absolute paths.
5883 self.assertFalse(os.path.isabs(uniq))
5884
5885 def testSafeUniqueNames(self):
5886 """Test entry unique names are safe in single image configuration"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005887 data = self._DoReadFileRealDtb('237_unique_names.dts')
Alper Nebi Yasak5cff63f2022-03-27 18:31:44 +03005888
5889 orig_image = control.images['image']
5890 image_fname = tools.get_output_filename('image.bin')
5891 image = Image.FromFile(image_fname)
5892
5893 self._CheckSafeUniqueNames(orig_image, image)
5894
5895 def testSafeUniqueNamesMulti(self):
5896 """Test entry unique names are safe with multiple images"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005897 data = self._DoReadFileRealDtb('238_unique_names_multi.dts')
Alper Nebi Yasak5cff63f2022-03-27 18:31:44 +03005898
5899 orig_image = control.images['image']
5900 image_fname = tools.get_output_filename('image.bin')
5901 image = Image.FromFile(image_fname)
5902
5903 self._CheckSafeUniqueNames(orig_image, image)
5904
Alper Nebi Yasake63ca5a2022-03-27 18:31:45 +03005905 def testReplaceCmdWithBintool(self):
5906 """Test replacing an entry that needs a bintool to pack"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005907 data = self._DoReadFileRealDtb('239_replace_with_bintool.dts')
Alper Nebi Yasake63ca5a2022-03-27 18:31:45 +03005908 expected = U_BOOT_DATA + b'aa'
5909 self.assertEqual(expected, data[:len(expected)])
5910
5911 try:
5912 tmpdir, updated_fname = self._SetupImageInTmpdir()
5913 fname = os.path.join(tmpdir, 'update-testing.bin')
5914 tools.write_file(fname, b'zz')
5915 self._DoBinman('replace', '-i', updated_fname,
5916 '_testing', '-f', fname)
5917
5918 data = tools.read_file(updated_fname)
5919 expected = U_BOOT_DATA + b'zz'
5920 self.assertEqual(expected, data[:len(expected)])
5921 finally:
5922 shutil.rmtree(tmpdir)
5923
5924 def testReplaceCmdOtherWithBintool(self):
5925 """Test replacing an entry when another needs a bintool to pack"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005926 data = self._DoReadFileRealDtb('239_replace_with_bintool.dts')
Alper Nebi Yasake63ca5a2022-03-27 18:31:45 +03005927 expected = U_BOOT_DATA + b'aa'
5928 self.assertEqual(expected, data[:len(expected)])
5929
5930 try:
5931 tmpdir, updated_fname = self._SetupImageInTmpdir()
5932 fname = os.path.join(tmpdir, 'update-u-boot.bin')
5933 tools.write_file(fname, b'x' * len(U_BOOT_DATA))
5934 self._DoBinman('replace', '-i', updated_fname,
5935 'u-boot', '-f', fname)
5936
5937 data = tools.read_file(updated_fname)
5938 expected = b'x' * len(U_BOOT_DATA) + b'aa'
5939 self.assertEqual(expected, data[:len(expected)])
5940 finally:
5941 shutil.rmtree(tmpdir)
5942
Alper Nebi Yasak00c68f12022-03-27 18:31:46 +03005943 def testReplaceResizeNoRepackSameSize(self):
5944 """Test replacing entries with same-size data without repacking"""
5945 expected = b'x' * len(U_BOOT_DATA)
5946 data, expected_fdtmap, _ = self._RunReplaceCmd('u-boot', expected)
5947 self.assertEqual(expected, data)
5948
5949 path, fdtmap = state.GetFdtContents('fdtmap')
5950 self.assertIsNotNone(path)
5951 self.assertEqual(expected_fdtmap, fdtmap)
5952
5953 def testReplaceResizeNoRepackSmallerSize(self):
5954 """Test replacing entries with smaller-size data without repacking"""
5955 new_data = b'x'
5956 data, expected_fdtmap, _ = self._RunReplaceCmd('u-boot', new_data)
5957 expected = new_data.ljust(len(U_BOOT_DATA), b'\0')
5958 self.assertEqual(expected, data)
5959
5960 path, fdtmap = state.GetFdtContents('fdtmap')
5961 self.assertIsNotNone(path)
5962 self.assertEqual(expected_fdtmap, fdtmap)
5963
Alper Nebi Yasak6cadc502022-03-27 18:31:48 +03005964 def testExtractFit(self):
5965 """Test extracting a FIT section"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005966 self._DoReadFileRealDtb('240_fit_extract_replace.dts')
Alper Nebi Yasak6cadc502022-03-27 18:31:48 +03005967 image_fname = tools.get_output_filename('image.bin')
5968
5969 fit_data = control.ReadEntry(image_fname, 'fit')
5970 fit = fdt.Fdt.FromData(fit_data)
5971 fit.Scan()
5972
5973 # Check subentry data inside the extracted fit
5974 for node_path, expected in [
5975 ('/images/kernel', U_BOOT_DATA),
5976 ('/images/fdt-1', U_BOOT_NODTB_DATA),
5977 ('/images/scr-1', COMPRESS_DATA),
5978 ]:
5979 node = fit.GetNode(node_path)
5980 data = fit.GetProps(node)['data'].bytes
5981 self.assertEqual(expected, data)
5982
5983 def testExtractFitSubentries(self):
5984 """Test extracting FIT section subentries"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005985 self._DoReadFileRealDtb('240_fit_extract_replace.dts')
Alper Nebi Yasak6cadc502022-03-27 18:31:48 +03005986 image_fname = tools.get_output_filename('image.bin')
5987
5988 for entry_path, expected in [
5989 ('fit/kernel', U_BOOT_DATA),
5990 ('fit/kernel/u-boot', U_BOOT_DATA),
5991 ('fit/fdt-1', U_BOOT_NODTB_DATA),
5992 ('fit/fdt-1/u-boot-nodtb', U_BOOT_NODTB_DATA),
5993 ('fit/scr-1', COMPRESS_DATA),
5994 ('fit/scr-1/blob', COMPRESS_DATA),
5995 ]:
5996 data = control.ReadEntry(image_fname, entry_path)
5997 self.assertEqual(expected, data)
5998
Alper Nebi Yasak49892642022-03-27 18:31:49 +03005999 def testReplaceFitSubentryLeafSameSize(self):
6000 """Test replacing a FIT leaf subentry with same-size data"""
6001 new_data = b'x' * len(U_BOOT_DATA)
6002 data, expected_fdtmap, _ = self._RunReplaceCmd(
6003 'fit/kernel/u-boot', new_data,
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006004 dts='240_fit_extract_replace.dts')
Alper Nebi Yasak49892642022-03-27 18:31:49 +03006005 self.assertEqual(new_data, data)
6006
6007 path, fdtmap = state.GetFdtContents('fdtmap')
6008 self.assertIsNotNone(path)
6009 self.assertEqual(expected_fdtmap, fdtmap)
6010
6011 def testReplaceFitSubentryLeafBiggerSize(self):
6012 """Test replacing a FIT leaf subentry with bigger-size data"""
6013 new_data = b'ub' * len(U_BOOT_NODTB_DATA)
6014 data, expected_fdtmap, _ = self._RunReplaceCmd(
6015 'fit/fdt-1/u-boot-nodtb', 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 # Will be repacked, so fdtmap must change
6020 path, fdtmap = state.GetFdtContents('fdtmap')
6021 self.assertIsNotNone(path)
6022 self.assertNotEqual(expected_fdtmap, fdtmap)
6023
6024 def testReplaceFitSubentryLeafSmallerSize(self):
6025 """Test replacing a FIT leaf subentry with smaller-size data"""
6026 new_data = b'x'
6027 expected = new_data.ljust(len(U_BOOT_NODTB_DATA), b'\0')
6028 data, expected_fdtmap, _ = self._RunReplaceCmd(
6029 'fit/fdt-1/u-boot-nodtb', new_data,
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006030 dts='240_fit_extract_replace.dts')
Alper Nebi Yasak49892642022-03-27 18:31:49 +03006031 self.assertEqual(expected, data)
6032
6033 path, fdtmap = state.GetFdtContents('fdtmap')
6034 self.assertIsNotNone(path)
6035 self.assertEqual(expected_fdtmap, fdtmap)
6036
Alper Nebi Yasak1d44c8e2022-03-27 18:31:50 +03006037 def testReplaceSectionSimple(self):
Simon Glass49b77e82023-03-02 17:02:44 -07006038 """Test replacing a simple section with same-sized data"""
Alper Nebi Yasak1d44c8e2022-03-27 18:31:50 +03006039 new_data = b'w' * len(COMPRESS_DATA + U_BOOT_DATA)
Simon Glass49b77e82023-03-02 17:02:44 -07006040 data, expected_fdtmap, image = self._RunReplaceCmd('section',
6041 new_data, dts='241_replace_section_simple.dts')
6042 self.assertEqual(new_data, data)
6043
6044 entries = image.GetEntries()
6045 self.assertIn('section', entries)
6046 entry = entries['section']
6047 self.assertEqual(len(new_data), entry.size)
6048
6049 def testReplaceSectionLarger(self):
6050 """Test replacing a simple section with larger data"""
6051 new_data = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) + 1)
6052 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 fentry = entries['fdtmap']
6061 self.assertEqual(entry.offset + entry.size, fentry.offset)
6062
6063 def testReplaceSectionSmaller(self):
6064 """Test replacing a simple section with smaller data"""
6065 new_data = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) - 1) + b'\0'
6066 data, expected_fdtmap, image = self._RunReplaceCmd('section',
6067 new_data, dts='241_replace_section_simple.dts')
6068 self.assertEqual(new_data, data)
6069
6070 # The new size is the same as the old, just with a pad byte at the end
6071 entries = image.GetEntries()
6072 self.assertIn('section', entries)
6073 entry = entries['section']
6074 self.assertEqual(len(new_data), entry.size)
6075
6076 def testReplaceSectionSmallerAllow(self):
6077 """Test failing to replace a simple section with smaller data"""
6078 new_data = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) - 1)
6079 try:
6080 state.SetAllowEntryContraction(True)
6081 with self.assertRaises(ValueError) as exc:
6082 self._RunReplaceCmd('section', new_data,
6083 dts='241_replace_section_simple.dts')
6084 finally:
6085 state.SetAllowEntryContraction(False)
6086
6087 # Since we have no information about the position of things within the
6088 # section, we cannot adjust the position of /section-u-boot so it ends
6089 # up outside the section
Simon Glassc6b283f2022-08-13 11:40:46 -06006090 self.assertIn(
Simon Glass49b77e82023-03-02 17:02:44 -07006091 "Node '/section/u-boot': Offset 0x24 (36) size 0x4 (4) is outside "
6092 "the section '/section' starting at 0x0 (0) of size 0x27 (39)",
Simon Glassc6b283f2022-08-13 11:40:46 -06006093 str(exc.exception))
Alper Nebi Yasak1d44c8e2022-03-27 18:31:50 +03006094
Simon Glass8fbca772022-08-13 11:40:48 -06006095 def testMkimageImagename(self):
6096 """Test using mkimage with -n holding the data too"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006097 self._SetupSplElf()
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006098 data = self._DoReadFile('242_mkimage_name.dts')
Simon Glass8fbca772022-08-13 11:40:48 -06006099
6100 # Check that the data appears in the file somewhere
6101 self.assertIn(U_BOOT_SPL_DATA, data)
6102
Simon Glassbb7d3bb2022-09-06 20:26:52 -06006103 # Get struct legacy_img_hdr -> ih_name
Simon Glass8fbca772022-08-13 11:40:48 -06006104 name = data[0x20:0x40]
6105
6106 # Build the filename that we expect to be placed in there, by virtue of
6107 # the -n paraameter
6108 expect = os.path.join(tools.get_output_dir(), 'mkimage.mkimage')
6109
6110 # Check that the image name is set to the temporary filename used
6111 self.assertEqual(expect.encode('utf-8')[:0x20], name)
6112
Simon Glassb1669752022-08-13 11:40:49 -06006113 def testMkimageImage(self):
6114 """Test using mkimage with -n holding the data too"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006115 self._SetupSplElf()
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006116 data = self._DoReadFile('243_mkimage_image.dts')
Simon Glassb1669752022-08-13 11:40:49 -06006117
6118 # Check that the data appears in the file somewhere
6119 self.assertIn(U_BOOT_SPL_DATA, data)
6120
Simon Glassbb7d3bb2022-09-06 20:26:52 -06006121 # Get struct legacy_img_hdr -> ih_name
Simon Glassb1669752022-08-13 11:40:49 -06006122 name = data[0x20:0x40]
6123
6124 # Build the filename that we expect to be placed in there, by virtue of
6125 # the -n paraameter
6126 expect = os.path.join(tools.get_output_dir(), 'mkimage-n.mkimage')
6127
6128 # Check that the image name is set to the temporary filename used
6129 self.assertEqual(expect.encode('utf-8')[:0x20], name)
6130
6131 # Check the corect data is in the imagename file
6132 self.assertEqual(U_BOOT_DATA, tools.read_file(expect))
6133
6134 def testMkimageImageNoContent(self):
6135 """Test using mkimage with -n and no data"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006136 self._SetupSplElf()
Simon Glassb1669752022-08-13 11:40:49 -06006137 with self.assertRaises(ValueError) as exc:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006138 self._DoReadFile('244_mkimage_image_no_content.dts')
Simon Glassb1669752022-08-13 11:40:49 -06006139 self.assertIn('Could not complete processing of contents',
6140 str(exc.exception))
6141
6142 def testMkimageImageBad(self):
6143 """Test using mkimage with imagename node and data-to-imagename"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006144 self._SetupSplElf()
Simon Glassb1669752022-08-13 11:40:49 -06006145 with self.assertRaises(ValueError) as exc:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006146 self._DoReadFile('245_mkimage_image_bad.dts')
Simon Glassb1669752022-08-13 11:40:49 -06006147 self.assertIn('Cannot use both imagename node and data-to-imagename',
6148 str(exc.exception))
6149
Simon Glassbd5cd882022-08-13 11:40:50 -06006150 def testCollectionOther(self):
6151 """Test a collection where the data comes from another section"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006152 data = self._DoReadFile('246_collection_other.dts')
Simon Glassbd5cd882022-08-13 11:40:50 -06006153 self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA +
6154 tools.get_bytes(0xff, 2) + U_BOOT_NODTB_DATA +
6155 tools.get_bytes(0xfe, 3) + U_BOOT_DTB_DATA,
6156 data)
6157
6158 def testMkimageCollection(self):
6159 """Test using a collection referring to an entry in a mkimage entry"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006160 self._SetupSplElf()
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006161 data = self._DoReadFile('247_mkimage_coll.dts')
Simon Glassbd5cd882022-08-13 11:40:50 -06006162 expect = U_BOOT_SPL_DATA + U_BOOT_DATA
6163 self.assertEqual(expect, data[:len(expect)])
6164
Stefan Herbrechtsmeier11121d32022-08-19 16:25:25 +02006165 def testCompressDtbPrependInvalid(self):
6166 """Test that invalid header is detected"""
6167 with self.assertRaises(ValueError) as e:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006168 self._DoReadFileDtb('248_compress_dtb_prepend_invalid.dts')
Stefan Herbrechtsmeier11121d32022-08-19 16:25:25 +02006169 self.assertIn("Node '/binman/u-boot-dtb': Invalid prepend in "
6170 "'u-boot-dtb': 'invalid'", str(e.exception))
6171
6172 def testCompressDtbPrependLength(self):
6173 """Test that compress with length header works as expected"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006174 data = self._DoReadFileRealDtb('249_compress_dtb_prepend_length.dts')
Stefan Herbrechtsmeier11121d32022-08-19 16:25:25 +02006175 image = control.images['image']
6176 entries = image.GetEntries()
6177 self.assertIn('u-boot-dtb', entries)
6178 u_boot_dtb = entries['u-boot-dtb']
6179 self.assertIn('fdtmap', entries)
6180 fdtmap = entries['fdtmap']
6181
6182 image_fname = tools.get_output_filename('image.bin')
6183 orig = control.ReadEntry(image_fname, 'u-boot-dtb')
6184 dtb = fdt.Fdt.FromData(orig)
6185 dtb.Scan()
6186 props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
6187 expected = {
6188 'u-boot:size': len(U_BOOT_DATA),
6189 'u-boot-dtb:uncomp-size': len(orig),
6190 'u-boot-dtb:size': u_boot_dtb.size,
6191 'fdtmap:size': fdtmap.size,
6192 'size': len(data),
6193 }
6194 self.assertEqual(expected, props)
6195
6196 # Check implementation
6197 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
6198 rest = data[len(U_BOOT_DATA):]
6199 comp_data_len = struct.unpack('<I', rest[:4])[0]
6200 comp_data = rest[4:4 + comp_data_len]
6201 orig2 = self._decompress(comp_data)
6202 self.assertEqual(orig, orig2)
6203
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +02006204 def testInvalidCompress(self):
6205 """Test that invalid compress algorithm is detected"""
6206 with self.assertRaises(ValueError) as e:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006207 self._DoTestFile('250_compress_dtb_invalid.dts')
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +02006208 self.assertIn("Unknown algorithm 'invalid'", str(e.exception))
6209
Stefan Herbrechtsmeiera14bee02022-08-19 16:25:32 +02006210 def testCompUtilCompressions(self):
6211 """Test compression algorithms"""
6212 for bintool in self.comp_bintools.values():
6213 self._CheckBintool(bintool)
6214 data = bintool.compress(COMPRESS_DATA)
6215 self.assertNotEqual(COMPRESS_DATA, data)
6216 orig = bintool.decompress(data)
Brandon Maiera657bc62024-06-04 16:16:05 +00006217 self.assertEqual(COMPRESS_DATA, orig)
Stefan Herbrechtsmeiera14bee02022-08-19 16:25:32 +02006218
6219 def testCompUtilVersions(self):
6220 """Test tool version of compression algorithms"""
6221 for bintool in self.comp_bintools.values():
6222 self._CheckBintool(bintool)
6223 version = bintool.version()
6224 self.assertRegex(version, '^v?[0-9]+[0-9.]*')
6225
6226 def testCompUtilPadding(self):
6227 """Test padding of compression algorithms"""
Jiaxun Yangc6931742025-04-10 06:43:03 -06006228 # Skip zstd and lz4 because they doesn't support padding
6229 for bintool in [v for k,v in self.comp_bintools.items()
6230 if not k in ['zstd', 'lz4']]:
Stefan Herbrechtsmeiera14bee02022-08-19 16:25:32 +02006231 self._CheckBintool(bintool)
6232 data = bintool.compress(COMPRESS_DATA)
6233 self.assertNotEqual(COMPRESS_DATA, data)
6234 data += tools.get_bytes(0, 64)
6235 orig = bintool.decompress(data)
Brandon Maiera657bc62024-06-04 16:16:05 +00006236 self.assertEqual(COMPRESS_DATA, orig)
Stefan Herbrechtsmeiera14bee02022-08-19 16:25:32 +02006237
Stefan Herbrechtsmeiera5e4dcb2022-08-19 16:25:38 +02006238 def testCompressDtbZstd(self):
6239 """Test that zstd compress of device-tree files failed"""
6240 with self.assertRaises(ValueError) as e:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006241 self._DoTestFile('251_compress_dtb_zstd.dts')
Stefan Herbrechtsmeiera5e4dcb2022-08-19 16:25:38 +02006242 self.assertIn("Node '/binman/u-boot-dtb': The zstd compression "
6243 "requires a length header", str(e.exception))
6244
Quentin Schulz9b5c6482022-09-02 15:10:48 +02006245 def testMkimageMultipleDataFiles(self):
6246 """Test passing multiple files to mkimage in a mkimage entry"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006247 self._SetupSplElf()
6248 self._SetupTplElf()
Quentin Schulz9b5c6482022-09-02 15:10:48 +02006249 data = self._DoReadFile('252_mkimage_mult_data.dts')
6250 # Size of files are packed in their 4B big-endian format
6251 expect = struct.pack('>I', len(U_BOOT_TPL_DATA))
6252 expect += struct.pack('>I', len(U_BOOT_SPL_DATA))
6253 # Size info is always followed by a 4B zero value.
6254 expect += tools.get_bytes(0, 4)
6255 expect += U_BOOT_TPL_DATA
6256 # All but last files are 4B-aligned
6257 align_pad = len(U_BOOT_TPL_DATA) % 4
6258 if align_pad:
6259 expect += tools.get_bytes(0, align_pad)
6260 expect += U_BOOT_SPL_DATA
6261 self.assertEqual(expect, data[-len(expect):])
6262
Marek Vasutf7413f02023-07-18 07:23:58 -06006263 def testMkimageMultipleExpanded(self):
6264 """Test passing multiple files to mkimage in a mkimage entry"""
6265 self._SetupSplElf()
6266 self._SetupTplElf()
6267 entry_args = {
6268 'spl-bss-pad': 'y',
6269 'spl-dtb': 'y',
6270 }
6271 data = self._DoReadFileDtb('252_mkimage_mult_data.dts',
6272 use_expanded=True, entry_args=entry_args)[0]
6273 pad_len = 10
6274 tpl_expect = U_BOOT_TPL_DATA
6275 spl_expect = U_BOOT_SPL_NODTB_DATA + tools.get_bytes(0, pad_len)
6276 spl_expect += U_BOOT_SPL_DTB_DATA
6277
6278 content = data[0x40:]
6279 lens = struct.unpack('>III', content[:12])
6280
6281 # Size of files are packed in their 4B big-endian format
6282 # Size info is always followed by a 4B zero value.
6283 self.assertEqual(len(tpl_expect), lens[0])
6284 self.assertEqual(len(spl_expect), lens[1])
6285 self.assertEqual(0, lens[2])
6286
6287 rest = content[12:]
6288 self.assertEqual(tpl_expect, rest[:len(tpl_expect)])
6289
6290 rest = rest[len(tpl_expect):]
6291 align_pad = len(tpl_expect) % 4
6292 self.assertEqual(tools.get_bytes(0, align_pad), rest[:align_pad])
6293 rest = rest[align_pad:]
6294 self.assertEqual(spl_expect, rest)
6295
Quentin Schulz9b5c6482022-09-02 15:10:48 +02006296 def testMkimageMultipleNoContent(self):
6297 """Test passing multiple data files to mkimage with one data file having no content"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006298 self._SetupSplElf()
Quentin Schulz9b5c6482022-09-02 15:10:48 +02006299 with self.assertRaises(ValueError) as exc:
6300 self._DoReadFile('253_mkimage_mult_no_content.dts')
6301 self.assertIn('Could not complete processing of contents',
6302 str(exc.exception))
6303
Quentin Schulz0d3a9262022-09-02 15:10:49 +02006304 def testMkimageFilename(self):
6305 """Test using mkimage to build a binary with a filename"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006306 self._SetupSplElf()
Quentin Schulz0d3a9262022-09-02 15:10:49 +02006307 retcode = self._DoTestFile('254_mkimage_filename.dts')
6308 self.assertEqual(0, retcode)
6309 fname = tools.get_output_filename('mkimage-test.bin')
6310 self.assertTrue(os.path.exists(fname))
6311
Simon Glass56d05412022-02-28 07:16:54 -07006312 def testVpl(self):
6313 """Test that an image with VPL and its device tree can be created"""
6314 # ELF file with a '__bss_size' symbol
6315 self._SetupVplElf()
6316 data = self._DoReadFile('255_u_boot_vpl.dts')
6317 self.assertEqual(U_BOOT_VPL_DATA + U_BOOT_VPL_DTB_DATA, data)
6318
6319 def testVplNoDtb(self):
6320 """Test that an image with vpl/u-boot-vpl-nodtb.bin can be created"""
6321 self._SetupVplElf()
6322 data = self._DoReadFile('256_u_boot_vpl_nodtb.dts')
6323 self.assertEqual(U_BOOT_VPL_NODTB_DATA,
6324 data[:len(U_BOOT_VPL_NODTB_DATA)])
6325
6326 def testExpandedVpl(self):
6327 """Test that an expanded entry type is selected for TPL when needed"""
6328 self._SetupVplElf()
6329
6330 entry_args = {
6331 'vpl-bss-pad': 'y',
6332 'vpl-dtb': 'y',
6333 }
6334 self._DoReadFileDtb('257_fdt_incl_vpl.dts', use_expanded=True,
6335 entry_args=entry_args)
6336 image = control.images['image']
6337 entries = image.GetEntries()
6338 self.assertEqual(1, len(entries))
6339
6340 # We only have u-boot-vpl, which be expanded
6341 self.assertIn('u-boot-vpl', entries)
6342 entry = entries['u-boot-vpl']
6343 self.assertEqual('u-boot-vpl-expanded', entry.etype)
6344 subent = entry.GetEntries()
6345 self.assertEqual(3, len(subent))
6346 self.assertIn('u-boot-vpl-nodtb', subent)
6347 self.assertIn('u-boot-vpl-bss-pad', subent)
6348 self.assertIn('u-boot-vpl-dtb', subent)
6349
6350 def testVplBssPadMissing(self):
6351 """Test that a missing symbol is detected"""
6352 self._SetupVplElf('u_boot_ucode_ptr')
6353 with self.assertRaises(ValueError) as e:
6354 self._DoReadFile('258_vpl_bss_pad.dts')
6355 self.assertIn('Expected __bss_size symbol in vpl/u-boot-vpl',
6356 str(e.exception))
6357
Neha Malcom Francis3eb4be32022-10-17 16:36:25 +05306358 def testSymlink(self):
Andrew Davis6b463da2023-07-22 00:14:44 +05306359 """Test that image files can be symlinked"""
Neha Malcom Francis3eb4be32022-10-17 16:36:25 +05306360 retcode = self._DoTestFile('259_symlink.dts', debug=True, map=True)
6361 self.assertEqual(0, retcode)
6362 image = control.images['test_image']
6363 fname = tools.get_output_filename('test_image.bin')
6364 sname = tools.get_output_filename('symlink_to_test.bin')
6365 self.assertTrue(os.path.islink(sname))
6366 self.assertEqual(os.readlink(sname), fname)
Alper Nebi Yasake63ca5a2022-03-27 18:31:45 +03006367
Andrew Davis6b463da2023-07-22 00:14:44 +05306368 def testSymlinkOverwrite(self):
6369 """Test that symlinked images can be overwritten"""
6370 testdir = TestFunctional._MakeInputDir('symlinktest')
6371 self._DoTestFile('259_symlink.dts', debug=True, map=True, output_dir=testdir)
6372 # build the same image again in the same directory so that existing symlink is present
6373 self._DoTestFile('259_symlink.dts', debug=True, map=True, output_dir=testdir)
6374 fname = tools.get_output_filename('test_image.bin')
6375 sname = tools.get_output_filename('symlink_to_test.bin')
6376 self.assertTrue(os.path.islink(sname))
6377 self.assertEqual(os.readlink(sname), fname)
6378
Simon Glass37f85de2022-10-20 18:22:47 -06006379 def testSymbolsElf(self):
6380 """Test binman can assign symbols embedded in an ELF file"""
6381 if not elf.ELF_TOOLS:
6382 self.skipTest('Python elftools not available')
6383 self._SetupTplElf('u_boot_binman_syms')
6384 self._SetupVplElf('u_boot_binman_syms')
6385 self._SetupSplElf('u_boot_binman_syms')
6386 data = self._DoReadFileDtb('260_symbols_elf.dts')[0]
6387 image_fname = tools.get_output_filename('image.bin')
6388
6389 image = control.images['image']
6390 entries = image.GetEntries()
6391
6392 for entry in entries.values():
6393 # No symbols in u-boot and it has faked contents anyway
6394 if entry.name == 'u-boot':
6395 continue
6396 edata = data[entry.image_pos:entry.image_pos + entry.size]
6397 efname = tools.get_output_filename(f'edata-{entry.name}')
6398 tools.write_file(efname, edata)
6399
6400 syms = elf.GetSymbolFileOffset(efname, ['_binman_u_boot'])
6401 re_name = re.compile('_binman_(u_boot_(.*))_prop_(.*)')
6402 for name, sym in syms.items():
6403 msg = 'test'
6404 val = elf.GetSymbolValue(sym, edata, msg)
6405 entry_m = re_name.match(name)
6406 if entry_m:
6407 ename, prop = entry_m.group(1), entry_m.group(3)
6408 entry, entry_name, prop_name = image.LookupEntry(entries,
6409 name, msg)
Simon Glassd3d3a102025-02-19 08:11:16 -07006410 expect_val = None
Simon Glass37f85de2022-10-20 18:22:47 -06006411 if prop_name == 'offset':
6412 expect_val = entry.offset
6413 elif prop_name == 'image_pos':
6414 expect_val = entry.image_pos
6415 elif prop_name == 'size':
6416 expect_val = entry.size
6417 self.assertEqual(expect_val, val)
6418
6419 def testSymbolsElfBad(self):
6420 """Check error when trying to write symbols without the elftools lib"""
6421 if not elf.ELF_TOOLS:
6422 self.skipTest('Python elftools not available')
6423 self._SetupTplElf('u_boot_binman_syms')
6424 self._SetupVplElf('u_boot_binman_syms')
6425 self._SetupSplElf('u_boot_binman_syms')
6426 try:
6427 elf.ELF_TOOLS = False
6428 with self.assertRaises(ValueError) as exc:
6429 self._DoReadFileDtb('260_symbols_elf.dts')
6430 finally:
6431 elf.ELF_TOOLS = True
6432 self.assertIn(
6433 "Section '/binman': entry '/binman/u-boot-spl-elf': "
6434 'Cannot write symbols to an ELF file without Python elftools',
6435 str(exc.exception))
6436
Simon Glassde244162023-01-07 14:07:08 -07006437 def testSectionFilename(self):
6438 """Check writing of section contents to a file"""
6439 data = self._DoReadFile('261_section_fname.dts')
6440 expected = (b'&&' + U_BOOT_DATA + b'&&&' +
6441 tools.get_bytes(ord('!'), 7) +
6442 U_BOOT_DATA + tools.get_bytes(ord('&'), 12))
6443 self.assertEqual(expected, data)
6444
6445 sect_fname = tools.get_output_filename('outfile.bin')
6446 self.assertTrue(os.path.exists(sect_fname))
6447 sect_data = tools.read_file(sect_fname)
6448 self.assertEqual(U_BOOT_DATA, sect_data)
6449
Simon Glass1e9e61c2023-01-07 14:07:12 -07006450 def testAbsent(self):
6451 """Check handling of absent entries"""
6452 data = self._DoReadFile('262_absent.dts')
6453 self.assertEqual(U_BOOT_DATA + U_BOOT_IMG_DATA, data)
6454
Simon Glassad5cfe12023-01-07 14:07:14 -07006455 def testPackTeeOsOptional(self):
6456 """Test that an image with an optional TEE binary can be created"""
6457 entry_args = {
6458 'tee-os-path': 'tee.elf',
6459 }
6460 data = self._DoReadFileDtb('263_tee_os_opt.dts',
6461 entry_args=entry_args)[0]
6462 self.assertEqual(U_BOOT_DATA + U_BOOT_IMG_DATA, data)
6463
6464 def checkFitTee(self, dts, tee_fname):
6465 """Check that a tee-os entry works and returns data
6466
6467 Args:
6468 dts (str): Device tree filename to use
6469 tee_fname (str): filename containing tee-os
6470
6471 Returns:
6472 bytes: Image contents
6473 """
6474 if not elf.ELF_TOOLS:
6475 self.skipTest('Python elftools not available')
6476 entry_args = {
6477 'of-list': 'test-fdt1 test-fdt2',
6478 'default-dt': 'test-fdt2',
6479 'tee-os-path': tee_fname,
6480 }
6481 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
6482 data = self._DoReadFileDtb(dts, entry_args=entry_args,
6483 extra_indirs=[test_subdir])[0]
6484 return data
6485
6486 def testFitTeeOsOptionalFit(self):
6487 """Test an image with a FIT with an optional OP-TEE binary"""
6488 data = self.checkFitTee('264_tee_os_opt_fit.dts', 'tee.bin')
6489
6490 # There should be only one node, holding the data set up in SetUpClass()
6491 # for tee.bin
6492 dtb = fdt.Fdt.FromData(data)
6493 dtb.Scan()
6494 node = dtb.GetNode('/images/tee-1')
6495 self.assertEqual(TEE_ADDR,
6496 fdt_util.fdt32_to_cpu(node.props['load'].value))
6497 self.assertEqual(TEE_ADDR,
6498 fdt_util.fdt32_to_cpu(node.props['entry'].value))
6499 self.assertEqual(U_BOOT_DATA, node.props['data'].bytes)
6500
Simon Glass14d64e32025-04-29 07:21:59 -06006501 with terminal.capture() as (stdout, stderr):
Jonas Karlmanb2be3e42023-07-18 20:34:36 +00006502 self.checkFitTee('264_tee_os_opt_fit.dts', '')
6503 err = stderr.getvalue()
6504 self.assertRegex(
6505 err,
6506 "Image '.*' is missing optional external blobs but is still functional: tee-os")
6507
Simon Glassad5cfe12023-01-07 14:07:14 -07006508 def testFitTeeOsOptionalFitBad(self):
6509 """Test an image with a FIT with an optional OP-TEE binary"""
6510 with self.assertRaises(ValueError) as exc:
6511 self.checkFitTee('265_tee_os_opt_fit_bad.dts', 'tee.bin')
6512 self.assertIn(
6513 "Node '/binman/fit': subnode 'images/@tee-SEQ': Failed to read ELF file: Magic number does not match",
6514 str(exc.exception))
6515
6516 def testFitTeeOsBad(self):
6517 """Test an OP-TEE binary with wrong formats"""
6518 self.make_tee_bin('tee.bad1', 123)
6519 with self.assertRaises(ValueError) as exc:
6520 self.checkFitTee('264_tee_os_opt_fit.dts', 'tee.bad1')
6521 self.assertIn(
6522 "Node '/binman/fit/images/@tee-SEQ/tee-os': OP-TEE paged mode not supported",
6523 str(exc.exception))
6524
6525 self.make_tee_bin('tee.bad2', 0, b'extra data')
6526 with self.assertRaises(ValueError) as exc:
6527 self.checkFitTee('264_tee_os_opt_fit.dts', 'tee.bad2')
6528 self.assertIn(
6529 "Node '/binman/fit/images/@tee-SEQ/tee-os': Invalid OP-TEE file: size mismatch (expected 0x4, have 0xe)",
6530 str(exc.exception))
6531
Simon Glass63328f12023-01-07 14:07:15 -07006532 def testExtblobOptional(self):
6533 """Test an image with an external blob that is optional"""
Simon Glass14d64e32025-04-29 07:21:59 -06006534 with terminal.capture() as (stdout, stderr):
Simon Glass63328f12023-01-07 14:07:15 -07006535 data = self._DoReadFile('266_blob_ext_opt.dts')
6536 self.assertEqual(REFCODE_DATA, data)
6537 err = stderr.getvalue()
6538 self.assertRegex(
6539 err,
Jonas Karlman9f96b812023-07-18 20:34:34 +00006540 "Image '.*' is missing optional external blobs but is still functional: missing")
Simon Glass63328f12023-01-07 14:07:15 -07006541
Simon Glass7447a9d2023-01-11 16:10:12 -07006542 def testSectionInner(self):
6543 """Test an inner section with a size"""
6544 data = self._DoReadFile('267_section_inner.dts')
6545 expected = U_BOOT_DATA + tools.get_bytes(0, 12)
6546 self.assertEqual(expected, data)
6547
Simon Glassa4948b22023-01-11 16:10:14 -07006548 def testNull(self):
6549 """Test an image with a null entry"""
6550 data = self._DoReadFile('268_null.dts')
6551 self.assertEqual(U_BOOT_DATA + b'\xff\xff\xff\xff' + U_BOOT_IMG_DATA, data)
6552
Simon Glassf1ee03b2023-01-11 16:10:16 -07006553 def testOverlap(self):
6554 """Test an image with a overlapping entry"""
6555 data = self._DoReadFile('269_overlap.dts')
6556 self.assertEqual(U_BOOT_DATA[:1] + b'aa' + U_BOOT_DATA[3:], data)
6557
6558 image = control.images['image']
6559 entries = image.GetEntries()
6560
6561 self.assertIn('inset', entries)
6562 inset = entries['inset']
6563 self.assertEqual(1, inset.offset);
6564 self.assertEqual(1, inset.image_pos);
6565 self.assertEqual(2, inset.size);
6566
6567 def testOverlapNull(self):
6568 """Test an image with a null overlap"""
6569 data = self._DoReadFile('270_overlap_null.dts')
6570 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
6571
6572 # Check the FMAP
6573 fhdr, fentries = fmap_util.DecodeFmap(data[len(U_BOOT_DATA):])
6574 self.assertEqual(4, fhdr.nareas)
6575 fiter = iter(fentries)
6576
6577 fentry = next(fiter)
6578 self.assertEqual(b'SECTION', fentry.name)
6579 self.assertEqual(0, fentry.offset)
6580 self.assertEqual(len(U_BOOT_DATA), fentry.size)
6581 self.assertEqual(0, fentry.flags)
6582
6583 fentry = next(fiter)
6584 self.assertEqual(b'U_BOOT', fentry.name)
6585 self.assertEqual(0, fentry.offset)
6586 self.assertEqual(len(U_BOOT_DATA), fentry.size)
6587 self.assertEqual(0, fentry.flags)
6588
6589 # Make sure that the NULL entry appears in the FMAP
6590 fentry = next(fiter)
6591 self.assertEqual(b'NULL', fentry.name)
6592 self.assertEqual(1, fentry.offset)
6593 self.assertEqual(2, fentry.size)
6594 self.assertEqual(0, fentry.flags)
6595
6596 fentry = next(fiter)
6597 self.assertEqual(b'FMAP', fentry.name)
6598 self.assertEqual(len(U_BOOT_DATA), fentry.offset)
6599
6600 def testOverlapBad(self):
6601 """Test an image with a bad overlapping entry"""
6602 with self.assertRaises(ValueError) as exc:
6603 self._DoReadFile('271_overlap_bad.dts')
6604 self.assertIn(
6605 "Node '/binman/inset': Offset 0x10 (16) ending at 0x12 (18) must overlap with existing entries",
6606 str(exc.exception))
6607
6608 def testOverlapNoOffset(self):
6609 """Test an image with a bad overlapping entry"""
6610 with self.assertRaises(ValueError) as exc:
6611 self._DoReadFile('272_overlap_no_size.dts')
6612 self.assertIn(
6613 "Node '/binman/inset': 'fill' entry is missing properties: size",
6614 str(exc.exception))
6615
Simon Glasse0035c92023-01-11 16:10:17 -07006616 def testBlobSymbol(self):
6617 """Test a blob with symbols read from an ELF file"""
6618 elf_fname = self.ElfTestFile('blob_syms')
6619 TestFunctional._MakeInputFile('blob_syms', tools.read_file(elf_fname))
6620 TestFunctional._MakeInputFile('blob_syms.bin',
6621 tools.read_file(self.ElfTestFile('blob_syms.bin')))
6622
6623 data = self._DoReadFile('273_blob_symbol.dts')
6624
6625 syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
6626 addr = elf.GetSymbolAddress(elf_fname, '__my_start_sym')
6627 self.assertEqual(syms['_binman_sym_magic'].address, addr)
6628 self.assertEqual(syms['_binman_inset_prop_offset'].address, addr + 4)
6629 self.assertEqual(syms['_binman_inset_prop_size'].address, addr + 8)
6630
6631 sym_values = struct.pack('<LLL', elf.BINMAN_SYM_MAGIC_VALUE, 4, 8)
6632 expected = sym_values
6633 self.assertEqual(expected, data[:len(expected)])
6634
Simon Glass49e9c002023-01-11 16:10:19 -07006635 def testOffsetFromElf(self):
6636 """Test a blob with symbols read from an ELF file"""
6637 elf_fname = self.ElfTestFile('blob_syms')
6638 TestFunctional._MakeInputFile('blob_syms', tools.read_file(elf_fname))
6639 TestFunctional._MakeInputFile('blob_syms.bin',
6640 tools.read_file(self.ElfTestFile('blob_syms.bin')))
6641
6642 data = self._DoReadFile('274_offset_from_elf.dts')
6643
6644 syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
6645 base = elf.GetSymbolAddress(elf_fname, '__my_start_sym')
6646
6647 image = control.images['image']
6648 entries = image.GetEntries()
6649
6650 self.assertIn('inset', entries)
6651 inset = entries['inset']
6652
6653 self.assertEqual(base + 4, inset.offset);
6654 self.assertEqual(base + 4, inset.image_pos);
6655 self.assertEqual(4, inset.size);
6656
6657 self.assertIn('inset2', entries)
6658 inset = entries['inset2']
6659 self.assertEqual(base + 8, inset.offset);
6660 self.assertEqual(base + 8, inset.image_pos);
6661 self.assertEqual(4, inset.size);
6662
Jonas Karlmanc59ea892023-01-21 19:01:39 +00006663 def testFitAlign(self):
6664 """Test an image with an FIT with aligned external data"""
6665 data = self._DoReadFile('275_fit_align.dts')
6666 self.assertEqual(4096, len(data))
6667
6668 dtb = fdt.Fdt.FromData(data)
6669 dtb.Scan()
6670
6671 props = self._GetPropTree(dtb, ['data-position'])
6672 expected = {
6673 'u-boot:data-position': 1024,
6674 'fdt-1:data-position': 2048,
6675 'fdt-2:data-position': 3072,
6676 }
6677 self.assertEqual(expected, props)
6678
Jonas Karlman490f73c2023-01-21 19:02:12 +00006679 def testFitFirmwareLoadables(self):
6680 """Test an image with an FIT that use fit,firmware"""
6681 if not elf.ELF_TOOLS:
6682 self.skipTest('Python elftools not available')
6683 entry_args = {
6684 'of-list': 'test-fdt1',
6685 'default-dt': 'test-fdt1',
6686 'atf-bl31-path': 'bl31.elf',
6687 'tee-os-path': 'missing.bin',
6688 }
6689 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
Simon Glass14d64e32025-04-29 07:21:59 -06006690 with terminal.capture() as (stdout, stderr):
Simon Glass62f85902023-02-23 18:18:01 -07006691 data = self._DoReadFileDtb(
6692 '276_fit_firmware_loadables.dts',
6693 entry_args=entry_args,
6694 extra_indirs=[test_subdir])[0]
Jonas Karlman490f73c2023-01-21 19:02:12 +00006695
6696 dtb = fdt.Fdt.FromData(data)
6697 dtb.Scan()
6698
6699 node = dtb.GetNode('/configurations/conf-uboot-1')
6700 self.assertEqual('u-boot', node.props['firmware'].value)
6701 self.assertEqual(['atf-1', 'atf-2'],
6702 fdt_util.GetStringList(node, 'loadables'))
6703
6704 node = dtb.GetNode('/configurations/conf-atf-1')
6705 self.assertEqual('atf-1', node.props['firmware'].value)
6706 self.assertEqual(['u-boot', 'atf-2'],
6707 fdt_util.GetStringList(node, 'loadables'))
6708
6709 node = dtb.GetNode('/configurations/conf-missing-uboot-1')
6710 self.assertEqual('u-boot', node.props['firmware'].value)
6711 self.assertEqual(['atf-1', 'atf-2'],
6712 fdt_util.GetStringList(node, 'loadables'))
6713
6714 node = dtb.GetNode('/configurations/conf-missing-atf-1')
6715 self.assertEqual('atf-1', node.props['firmware'].value)
6716 self.assertEqual(['u-boot', 'atf-2'],
6717 fdt_util.GetStringList(node, 'loadables'))
6718
6719 node = dtb.GetNode('/configurations/conf-missing-tee-1')
6720 self.assertEqual('atf-1', node.props['firmware'].value)
6721 self.assertEqual(['u-boot', 'atf-2'],
6722 fdt_util.GetStringList(node, 'loadables'))
6723
Simon Glass9a1c7262023-02-22 12:14:49 -07006724 def testTooldir(self):
6725 """Test that we can specify the tooldir"""
Simon Glass14d64e32025-04-29 07:21:59 -06006726 with terminal.capture() as (stdout, stderr):
Simon Glass9a1c7262023-02-22 12:14:49 -07006727 self.assertEqual(0, self._DoBinman('--tooldir', 'fred',
6728 'tool', '-l'))
6729 self.assertEqual('fred', bintool.Bintool.tooldir)
6730
6731 # Check that the toolpath is updated correctly
6732 self.assertEqual(['fred'], tools.tool_search_paths)
6733
6734 # Try with a few toolpaths; the tooldir should be at the end
Simon Glass14d64e32025-04-29 07:21:59 -06006735 with terminal.capture() as (stdout, stderr):
Simon Glass9a1c7262023-02-22 12:14:49 -07006736 self.assertEqual(0, self._DoBinman(
6737 '--toolpath', 'mary', '--toolpath', 'anna', '--tooldir', 'fred',
6738 'tool', '-l'))
6739 self.assertEqual(['mary', 'anna', 'fred'], tools.tool_search_paths)
6740
Simon Glass49b77e82023-03-02 17:02:44 -07006741 def testReplaceSectionEntry(self):
6742 """Test replacing an entry in a section"""
6743 expect_data = b'w' * len(U_BOOT_DATA + COMPRESS_DATA)
6744 entry_data, expected_fdtmap, image = self._RunReplaceCmd('section/blob',
6745 expect_data, dts='241_replace_section_simple.dts')
6746 self.assertEqual(expect_data, entry_data)
6747
6748 entries = image.GetEntries()
6749 self.assertIn('section', entries)
6750 section = entries['section']
6751
6752 sect_entries = section.GetEntries()
6753 self.assertIn('blob', sect_entries)
6754 entry = sect_entries['blob']
6755 self.assertEqual(len(expect_data), entry.size)
6756
6757 fname = tools.get_output_filename('image-updated.bin')
6758 data = tools.read_file(fname)
6759
6760 new_blob_data = data[entry.image_pos:entry.image_pos + len(expect_data)]
6761 self.assertEqual(expect_data, new_blob_data)
6762
6763 self.assertEqual(U_BOOT_DATA,
6764 data[entry.image_pos + len(expect_data):]
6765 [:len(U_BOOT_DATA)])
6766
6767 def testReplaceSectionDeep(self):
6768 """Test replacing an entry in two levels of sections"""
6769 expect_data = b'w' * len(U_BOOT_DATA + COMPRESS_DATA)
6770 entry_data, expected_fdtmap, image = self._RunReplaceCmd(
6771 'section/section/blob', expect_data,
6772 dts='278_replace_section_deep.dts')
6773 self.assertEqual(expect_data, entry_data)
6774
6775 entries = image.GetEntries()
6776 self.assertIn('section', entries)
6777 section = entries['section']
6778
6779 subentries = section.GetEntries()
6780 self.assertIn('section', subentries)
6781 section = subentries['section']
6782
6783 sect_entries = section.GetEntries()
6784 self.assertIn('blob', sect_entries)
6785 entry = sect_entries['blob']
6786 self.assertEqual(len(expect_data), entry.size)
6787
6788 fname = tools.get_output_filename('image-updated.bin')
6789 data = tools.read_file(fname)
6790
6791 new_blob_data = data[entry.image_pos:entry.image_pos + len(expect_data)]
6792 self.assertEqual(expect_data, new_blob_data)
6793
6794 self.assertEqual(U_BOOT_DATA,
6795 data[entry.image_pos + len(expect_data):]
6796 [:len(U_BOOT_DATA)])
6797
6798 def testReplaceFitSibling(self):
6799 """Test an image with a FIT inside where we replace its sibling"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006800 self._SetupSplElf()
Simon Glass49b77e82023-03-02 17:02:44 -07006801 fname = TestFunctional._MakeInputFile('once', b'available once')
6802 self._DoReadFileRealDtb('277_replace_fit_sibling.dts')
6803 os.remove(fname)
6804
6805 try:
6806 tmpdir, updated_fname = self._SetupImageInTmpdir()
6807
6808 fname = os.path.join(tmpdir, 'update-blob')
6809 expected = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) + 1)
6810 tools.write_file(fname, expected)
6811
6812 self._DoBinman('replace', '-i', updated_fname, 'blob', '-f', fname)
6813 data = tools.read_file(updated_fname)
6814 start = len(U_BOOT_DTB_DATA)
6815 self.assertEqual(expected, data[start:start + len(expected)])
6816 map_fname = os.path.join(tmpdir, 'image-updated.map')
6817 self.assertFalse(os.path.exists(map_fname))
6818 finally:
6819 shutil.rmtree(tmpdir)
6820
Simon Glassc3fe97f2023-03-02 17:02:45 -07006821 def testX509Cert(self):
6822 """Test creating an X509 certificate"""
6823 keyfile = self.TestFile('key.key')
6824 entry_args = {
6825 'keyfile': keyfile,
6826 }
6827 data = self._DoReadFileDtb('279_x509_cert.dts',
6828 entry_args=entry_args)[0]
6829 cert = data[:-4]
6830 self.assertEqual(U_BOOT_DATA, data[-4:])
6831
6832 # TODO: verify the signature
6833
6834 def testX509CertMissing(self):
6835 """Test that binman still produces an image if openssl is missing"""
6836 keyfile = self.TestFile('key.key')
6837 entry_args = {
6838 'keyfile': 'keyfile',
6839 }
Simon Glass14d64e32025-04-29 07:21:59 -06006840 with terminal.capture() as (_, stderr):
Simon Glassc3fe97f2023-03-02 17:02:45 -07006841 self._DoTestFile('279_x509_cert.dts',
6842 force_missing_bintools='openssl',
6843 entry_args=entry_args)
6844 err = stderr.getvalue()
6845 self.assertRegex(err, "Image 'image'.*missing bintools.*: openssl")
6846
Jonas Karlman35305492023-02-25 19:01:33 +00006847 def testPackRockchipTpl(self):
6848 """Test that an image with a Rockchip TPL binary can be created"""
Simon Glasse3ef5ed2023-07-24 09:19:58 -06006849 data = self._DoReadFile('291_rockchip_tpl.dts')
Jonas Karlman35305492023-02-25 19:01:33 +00006850 self.assertEqual(ROCKCHIP_TPL_DATA, data[:len(ROCKCHIP_TPL_DATA)])
6851
Jonas Karlman1016ec72023-02-25 19:01:35 +00006852 def testMkimageMissingBlobMultiple(self):
6853 """Test missing blob with mkimage entry and multiple-data-files"""
Simon Glass14d64e32025-04-29 07:21:59 -06006854 with terminal.capture() as (stdout, stderr):
Simon Glasse3ef5ed2023-07-24 09:19:58 -06006855 self._DoTestFile('292_mkimage_missing_multiple.dts', allow_missing=True)
Jonas Karlman1016ec72023-02-25 19:01:35 +00006856 err = stderr.getvalue()
6857 self.assertIn("is missing external blobs and is non-functional", err)
6858
6859 with self.assertRaises(ValueError) as e:
Simon Glasse3ef5ed2023-07-24 09:19:58 -06006860 self._DoTestFile('292_mkimage_missing_multiple.dts', allow_missing=False)
Jonas Karlman1016ec72023-02-25 19:01:35 +00006861 self.assertIn("not found in input path", str(e.exception))
6862
Ivan Mikhaylov3cfcaa4d2023-03-08 01:13:40 +00006863 def _PrepareSignEnv(self, dts='280_fit_sign.dts'):
6864 """Prepare sign environment
6865
6866 Create private and public keys, add pubkey into dtb.
6867
6868 Returns:
6869 Tuple:
6870 FIT container
6871 Image name
6872 Private key
6873 DTB
6874 """
Marek Vasutf7413f02023-07-18 07:23:58 -06006875 self._SetupSplElf()
Ivan Mikhaylov3cfcaa4d2023-03-08 01:13:40 +00006876 data = self._DoReadFileRealDtb(dts)
6877 updated_fname = tools.get_output_filename('image-updated.bin')
6878 tools.write_file(updated_fname, data)
6879 dtb = tools.get_output_filename('source.dtb')
6880 private_key = tools.get_output_filename('test_key.key')
6881 public_key = tools.get_output_filename('test_key.crt')
6882 fit = tools.get_output_filename('fit.fit')
6883 key_dir = tools.get_output_dir()
6884
6885 tools.run('openssl', 'req', '-batch' , '-newkey', 'rsa:4096',
6886 '-sha256', '-new', '-nodes', '-x509', '-keyout',
6887 private_key, '-out', public_key)
6888 tools.run('fdt_add_pubkey', '-a', 'sha256,rsa4096', '-k', key_dir,
6889 '-n', 'test_key', '-r', 'conf', dtb)
6890
6891 return fit, updated_fname, private_key, dtb
6892
6893 def testSignSimple(self):
6894 """Test that a FIT container can be signed in image"""
6895 is_signed = False
6896 fit, fname, private_key, dtb = self._PrepareSignEnv()
6897
6898 # do sign with private key
6899 control.SignEntries(fname, None, private_key, 'sha256,rsa4096',
6900 ['fit'])
6901 is_signed = self._CheckSign(fit, dtb)
6902
6903 self.assertEqual(is_signed, True)
6904
6905 def testSignExactFIT(self):
6906 """Test that a FIT container can be signed and replaced in image"""
6907 is_signed = False
6908 fit, fname, private_key, dtb = self._PrepareSignEnv()
6909
6910 # Make sure we propagate the toolpath, since mkimage may not be on PATH
6911 args = []
6912 if self.toolpath:
6913 for path in self.toolpath:
6914 args += ['--toolpath', path]
6915
6916 # do sign with private key
6917 self._DoBinman(*args, 'sign', '-i', fname, '-k', private_key, '-a',
6918 'sha256,rsa4096', '-f', fit, 'fit')
6919 is_signed = self._CheckSign(fit, dtb)
6920
6921 self.assertEqual(is_signed, True)
6922
6923 def testSignNonFit(self):
6924 """Test a non-FIT entry cannot be signed"""
6925 is_signed = False
6926 fit, fname, private_key, _ = self._PrepareSignEnv(
6927 '281_sign_non_fit.dts')
6928
6929 # do sign with private key
6930 with self.assertRaises(ValueError) as e:
6931 self._DoBinman('sign', '-i', fname, '-k', private_key, '-a',
6932 'sha256,rsa4096', '-f', fit, 'u-boot')
6933 self.assertIn(
6934 "Node '/u-boot': Updating signatures is not supported with this entry type",
6935 str(e.exception))
6936
6937 def testSignMissingMkimage(self):
6938 """Test that FIT signing handles a missing mkimage tool"""
6939 fit, fname, private_key, _ = self._PrepareSignEnv()
6940
6941 # try to sign with a missing mkimage tool
6942 bintool.Bintool.set_missing_list(['mkimage'])
6943 with self.assertRaises(ValueError) as e:
6944 control.SignEntries(fname, None, private_key, 'sha256,rsa4096',
6945 ['fit'])
6946 self.assertIn("Node '/fit': Missing tool: 'mkimage'", str(e.exception))
6947
Simon Glass4abf7842023-07-18 07:23:54 -06006948 def testSymbolNoWrite(self):
6949 """Test disabling of symbol writing"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006950 self._SetupSplElf()
Simon Glass4abf7842023-07-18 07:23:54 -06006951 self.checkSymbols('282_symbols_disable.dts', U_BOOT_SPL_DATA, 0x1c,
6952 no_write_symbols=True)
6953
6954 def testSymbolNoWriteExpanded(self):
6955 """Test disabling of symbol writing in expanded entries"""
6956 entry_args = {
6957 'spl-dtb': '1',
6958 }
6959 self.checkSymbols('282_symbols_disable.dts', U_BOOT_SPL_NODTB_DATA +
6960 U_BOOT_SPL_DTB_DATA, 0x38,
6961 entry_args=entry_args, use_expanded=True,
6962 no_write_symbols=True)
6963
Marek Vasutf7413f02023-07-18 07:23:58 -06006964 def testMkimageSpecial(self):
6965 """Test mkimage ignores special hash-1 node"""
6966 data = self._DoReadFile('283_mkimage_special.dts')
6967
6968 # Just check that the data appears in the file somewhere
6969 self.assertIn(U_BOOT_DATA, data)
6970
Simon Glass2d94c422023-07-18 07:23:59 -06006971 def testFitFdtList(self):
6972 """Test an image with an FIT with the fit,fdt-list-val option"""
6973 entry_args = {
6974 'default-dt': 'test-fdt2',
6975 }
6976 data = self._DoReadFileDtb(
6977 '284_fit_fdt_list.dts',
6978 entry_args=entry_args,
6979 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
6980 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
6981 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
6982
Simon Glass83b8bfe2023-07-18 07:24:01 -06006983 def testSplEmptyBss(self):
6984 """Test an expanded SPL with a zero-size BSS"""
6985 # ELF file with a '__bss_size' symbol
6986 self._SetupSplElf(src_fname='bss_data_zero')
6987
6988 entry_args = {
6989 'spl-bss-pad': 'y',
6990 'spl-dtb': 'y',
6991 }
6992 data = self._DoReadFileDtb('285_spl_expand.dts',
6993 use_expanded=True, entry_args=entry_args)[0]
6994
Simon Glassfc792842023-07-18 07:24:04 -06006995 def testTemplate(self):
6996 """Test using a template"""
6997 TestFunctional._MakeInputFile('vga2.bin', b'#' + VGA_DATA)
6998 data = self._DoReadFile('286_template.dts')
6999 first = U_BOOT_DATA + VGA_DATA + U_BOOT_DTB_DATA
7000 second = U_BOOT_DATA + b'#' + VGA_DATA + U_BOOT_DTB_DATA
7001 self.assertEqual(U_BOOT_IMG_DATA + first + second, data)
7002
Simon Glass09490b02023-07-22 21:43:52 -06007003 dtb_fname1 = tools.get_output_filename('u-boot.dtb.tmpl1')
7004 self.assertTrue(os.path.exists(dtb_fname1))
7005 dtb = fdt.Fdt.FromData(tools.read_file(dtb_fname1))
7006 dtb.Scan()
7007 node1 = dtb.GetNode('/binman/template')
7008 self.assertTrue(node1)
7009 vga = dtb.GetNode('/binman/first/intel-vga')
7010 self.assertTrue(vga)
7011
Simon Glass54825e12023-07-22 21:43:56 -06007012 dtb_fname2 = tools.get_output_filename('u-boot.dtb.tmpl2')
7013 self.assertTrue(os.path.exists(dtb_fname2))
7014 dtb2 = fdt.Fdt.FromData(tools.read_file(dtb_fname2))
7015 dtb2.Scan()
7016 node2 = dtb2.GetNode('/binman/template')
7017 self.assertFalse(node2)
7018
Simon Glass9909c112023-07-18 07:24:05 -06007019 def testTemplateBlobMulti(self):
7020 """Test using a template with 'multiple-images' enabled"""
7021 TestFunctional._MakeInputFile('my-blob.bin', b'blob')
7022 TestFunctional._MakeInputFile('my-blob2.bin', b'other')
7023 retcode = self._DoTestFile('287_template_multi.dts')
7024
7025 self.assertEqual(0, retcode)
7026 image = control.images['image']
7027 image_fname = tools.get_output_filename('my-image.bin')
7028 data = tools.read_file(image_fname)
7029 self.assertEqual(b'blob@@@@other', data)
7030
Simon Glass5dc511b2023-07-18 07:24:06 -06007031 def testTemplateFit(self):
7032 """Test using a template in a FIT"""
7033 fit_data = self._DoReadFile('288_template_fit.dts')
7034 fname = os.path.join(self._indir, 'fit_data.fit')
7035 tools.write_file(fname, fit_data)
7036 out = tools.run('dumpimage', '-l', fname)
7037
Simon Glassaa6e0552023-07-18 07:24:07 -06007038 def testTemplateSection(self):
7039 """Test using a template in a section (not at top level)"""
7040 TestFunctional._MakeInputFile('vga2.bin', b'#' + VGA_DATA)
7041 data = self._DoReadFile('289_template_section.dts')
7042 first = U_BOOT_DATA + VGA_DATA + U_BOOT_DTB_DATA
7043 second = U_BOOT_DATA + b'#' + VGA_DATA + U_BOOT_DTB_DATA
7044 self.assertEqual(U_BOOT_IMG_DATA + first + second + first, data)
7045
Simon Glassf53a7bc2023-07-18 07:24:08 -06007046 def testMkimageSymbols(self):
7047 """Test using mkimage to build an image with symbols in it"""
7048 self._SetupSplElf('u_boot_binman_syms')
7049 data = self._DoReadFile('290_mkimage_sym.dts')
7050
7051 image = control.images['image']
7052 entries = image.GetEntries()
7053 self.assertIn('u-boot', entries)
7054 u_boot = entries['u-boot']
7055
7056 mkim = entries['mkimage']
7057 mkim_entries = mkim.GetEntries()
7058 self.assertIn('u-boot-spl', mkim_entries)
7059 spl = mkim_entries['u-boot-spl']
7060 self.assertIn('u-boot-spl2', mkim_entries)
7061 spl2 = mkim_entries['u-boot-spl2']
7062
7063 # skip the mkimage header and the area sizes
7064 mk_data = data[mkim.offset + 0x40:]
7065 size, term = struct.unpack('>LL', mk_data[:8])
7066
7067 # There should be only one image, so check that the zero terminator is
7068 # present
7069 self.assertEqual(0, term)
7070
7071 content = mk_data[8:8 + size]
7072
7073 # The image should contain the symbols from u_boot_binman_syms.c
7074 # Note that image_pos is adjusted by the base address of the image,
7075 # which is 0x10 in our test image
7076 spl_data = content[:0x18]
7077 content = content[0x1b:]
7078
7079 # After the header is a table of offsets for each image. There should
7080 # only be one image, then a 0 terminator, so figure out the real start
7081 # of the image data
7082 base = 0x40 + 8
7083
7084 # Check symbols in both u-boot-spl and u-boot-spl2
7085 for i in range(2):
7086 vals = struct.unpack('<LLQLL', spl_data)
7087
7088 # The image should contain the symbols from u_boot_binman_syms.c
7089 # Note that image_pos is adjusted by the base address of the image,
7090 # which is 0x10 in our 'u_boot_binman_syms' test image
7091 self.assertEqual(elf.BINMAN_SYM_MAGIC_VALUE, vals[0])
7092 self.assertEqual(base, vals[1])
7093 self.assertEqual(spl2.offset, vals[2])
7094 # figure out the internal positions of its components
7095 self.assertEqual(0x10 + u_boot.image_pos, vals[3])
7096
7097 # Check that spl and spl2 are actually at the indicated positions
7098 self.assertEqual(
7099 elf.BINMAN_SYM_MAGIC_VALUE,
7100 struct.unpack('<I', data[spl.image_pos:spl.image_pos + 4])[0])
7101 self.assertEqual(
7102 elf.BINMAN_SYM_MAGIC_VALUE,
7103 struct.unpack('<I', data[spl2.image_pos:spl2.image_pos + 4])[0])
7104
7105 self.assertEqual(len(U_BOOT_DATA), vals[4])
7106
7107 # Move to next
7108 spl_data = content[:0x18]
7109
Simon Glass86b3e472023-07-22 21:43:57 -06007110 def testTemplatePhandle(self):
7111 """Test using a template in a node containing a phandle"""
7112 entry_args = {
7113 'atf-bl31-path': 'bl31.elf',
7114 }
Simon Glass76ee0ca2023-08-03 17:23:58 -06007115 data = self._DoReadFileDtb('309_template_phandle.dts',
Simon Glass86b3e472023-07-22 21:43:57 -06007116 entry_args=entry_args)
7117 fname = tools.get_output_filename('image.bin')
7118 out = tools.run('dumpimage', '-l', fname)
7119
7120 # We should see the FIT description and one for each of the two images
7121 lines = out.splitlines()
7122 descs = [line.split()[-1] for line in lines if 'escription' in line]
7123 self.assertEqual(['test-desc', 'atf', 'fdt'], descs)
7124
7125 def testTemplatePhandleDup(self):
7126 """Test using a template in a node containing a phandle"""
7127 entry_args = {
7128 'atf-bl31-path': 'bl31.elf',
7129 }
7130 with self.assertRaises(ValueError) as e:
Simon Glass76ee0ca2023-08-03 17:23:58 -06007131 self._DoReadFileDtb('310_template_phandle_dup.dts',
Simon Glass86b3e472023-07-22 21:43:57 -06007132 entry_args=entry_args)
7133 self.assertIn(
7134 'Duplicate phandle 1 in nodes /binman/image/fit/images/atf/atf-bl31 and /binman/image-2/fit/images/atf/atf-bl31',
7135 str(e.exception))
7136
Neha Malcom Francis3b788942023-07-22 00:14:24 +05307137 def testTIBoardConfig(self):
7138 """Test that a schema validated board config file can be generated"""
Simon Glassf1264ba2023-07-24 09:19:59 -06007139 data = self._DoReadFile('293_ti_board_cfg.dts')
Neha Malcom Francis3b788942023-07-22 00:14:24 +05307140 self.assertEqual(TI_BOARD_CONFIG_DATA, data)
7141
Neha Malcom Francis8cd04512024-01-05 17:09:17 +05307142 def testTIBoardConfigLint(self):
7143 """Test that an incorrectly linted config file would generate error"""
7144 with self.assertRaises(ValueError) as e:
7145 data = self._DoReadFile('323_ti_board_cfg_phony.dts')
7146 self.assertIn("Yamllint error", str(e.exception))
7147
Neha Malcom Francis3b788942023-07-22 00:14:24 +05307148 def testTIBoardConfigCombined(self):
7149 """Test that a schema validated combined board config file can be generated"""
Simon Glassf1264ba2023-07-24 09:19:59 -06007150 data = self._DoReadFile('294_ti_board_cfg_combined.dts')
Neha Malcom Francis3b788942023-07-22 00:14:24 +05307151 configlen_noheader = TI_BOARD_CONFIG_DATA * 4
7152 self.assertGreater(data, configlen_noheader)
7153
7154 def testTIBoardConfigNoDataType(self):
7155 """Test that error is thrown when data type is not supported"""
7156 with self.assertRaises(ValueError) as e:
Simon Glassf1264ba2023-07-24 09:19:59 -06007157 data = self._DoReadFile('295_ti_board_cfg_no_type.dts')
Neha Malcom Francis3b788942023-07-22 00:14:24 +05307158 self.assertIn("Schema validation error", str(e.exception))
Simon Glassde244162023-01-07 14:07:08 -07007159
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307160 def testPackTiSecure(self):
7161 """Test that an image with a TI secured binary can be created"""
7162 keyfile = self.TestFile('key.key')
7163 entry_args = {
7164 'keyfile': keyfile,
7165 }
Simon Glassf1264ba2023-07-24 09:19:59 -06007166 data = self._DoReadFileDtb('296_ti_secure.dts',
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307167 entry_args=entry_args)[0]
7168 self.assertGreater(len(data), len(TI_UNSECURE_DATA))
7169
Manorit Chawdhry2e523b02023-12-29 16:16:27 +05307170 def testPackTiSecureFirewall(self):
7171 """Test that an image with a TI secured binary can be created"""
7172 keyfile = self.TestFile('key.key')
7173 entry_args = {
7174 'keyfile': keyfile,
7175 }
7176 data_no_firewall = self._DoReadFileDtb('296_ti_secure.dts',
7177 entry_args=entry_args)[0]
7178 data_firewall = self._DoReadFileDtb('324_ti_secure_firewall.dts',
7179 entry_args=entry_args)[0]
7180 self.assertGreater(len(data_firewall),len(data_no_firewall))
7181
7182 def testPackTiSecureFirewallMissingProperty(self):
7183 """Test that an image with a TI secured binary can be created"""
7184 keyfile = self.TestFile('key.key')
7185 entry_args = {
7186 'keyfile': keyfile,
7187 }
7188 with self.assertRaises(ValueError) as e:
7189 data_firewall = self._DoReadFileDtb('325_ti_secure_firewall_missing_property.dts',
7190 entry_args=entry_args)[0]
7191 self.assertRegex(str(e.exception), "Node '/binman/ti-secure': Subnode 'firewall-0-2' is missing properties: id,region")
7192
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307193 def testPackTiSecureMissingTool(self):
7194 """Test that an image with a TI secured binary (non-functional) can be created
7195 when openssl is missing"""
7196 keyfile = self.TestFile('key.key')
7197 entry_args = {
7198 'keyfile': keyfile,
7199 }
Simon Glass14d64e32025-04-29 07:21:59 -06007200 with terminal.capture() as (_, stderr):
Simon Glassf1264ba2023-07-24 09:19:59 -06007201 self._DoTestFile('296_ti_secure.dts',
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307202 force_missing_bintools='openssl',
7203 entry_args=entry_args)
7204 err = stderr.getvalue()
7205 self.assertRegex(err, "Image 'image'.*missing bintools.*: openssl")
7206
7207 def testPackTiSecureROM(self):
7208 """Test that a ROM image with a TI secured binary can be created"""
7209 keyfile = self.TestFile('key.key')
7210 entry_args = {
7211 'keyfile': keyfile,
7212 }
Simon Glassf1264ba2023-07-24 09:19:59 -06007213 data = self._DoReadFileDtb('297_ti_secure_rom.dts',
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307214 entry_args=entry_args)[0]
Simon Glassf1264ba2023-07-24 09:19:59 -06007215 data_a = self._DoReadFileDtb('299_ti_secure_rom_a.dts',
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307216 entry_args=entry_args)[0]
Simon Glassf1264ba2023-07-24 09:19:59 -06007217 data_b = self._DoReadFileDtb('300_ti_secure_rom_b.dts',
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307218 entry_args=entry_args)[0]
7219 self.assertGreater(len(data), len(TI_UNSECURE_DATA))
7220 self.assertGreater(len(data_a), len(TI_UNSECURE_DATA))
7221 self.assertGreater(len(data_b), len(TI_UNSECURE_DATA))
7222
7223 def testPackTiSecureROMCombined(self):
7224 """Test that a ROM image with a TI secured binary can be created"""
7225 keyfile = self.TestFile('key.key')
7226 entry_args = {
7227 'keyfile': keyfile,
7228 }
Simon Glassf1264ba2023-07-24 09:19:59 -06007229 data = self._DoReadFileDtb('298_ti_secure_rom_combined.dts',
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307230 entry_args=entry_args)[0]
7231 self.assertGreater(len(data), len(TI_UNSECURE_DATA))
7232
Christian Taedcke62ac29a2023-07-17 09:05:54 +02007233 def testEncryptedNoAlgo(self):
7234 """Test encrypted node with missing required properties"""
7235 with self.assertRaises(ValueError) as e:
7236 self._DoReadFileDtb('301_encrypted_no_algo.dts')
7237 self.assertIn(
7238 "Node '/binman/fit/images/u-boot/encrypted': 'encrypted' entry is missing properties: algo iv-filename",
7239 str(e.exception))
7240
7241 def testEncryptedInvalidIvfile(self):
7242 """Test encrypted node with invalid iv file"""
7243 with self.assertRaises(ValueError) as e:
7244 self._DoReadFileDtb('302_encrypted_invalid_iv_file.dts')
7245 self.assertIn("Filename 'invalid-iv-file' not found in input path",
7246 str(e.exception))
7247
7248 def testEncryptedMissingKey(self):
7249 """Test encrypted node with missing key properties"""
7250 with self.assertRaises(ValueError) as e:
7251 self._DoReadFileDtb('303_encrypted_missing_key.dts')
7252 self.assertIn(
7253 "Node '/binman/fit/images/u-boot/encrypted': Provide either 'key-filename' or 'key-source'",
7254 str(e.exception))
7255
7256 def testEncryptedKeySource(self):
7257 """Test encrypted node with key-source property"""
7258 data = self._DoReadFileDtb('304_encrypted_key_source.dts')[0]
7259
7260 dtb = fdt.Fdt.FromData(data)
7261 dtb.Scan()
7262
7263 node = dtb.GetNode('/images/u-boot/cipher')
7264 self.assertEqual('algo-name', node.props['algo'].value)
7265 self.assertEqual('key-source-value', node.props['key-source'].value)
7266 self.assertEqual(ENCRYPTED_IV_DATA,
7267 tools.to_bytes(''.join(node.props['iv'].value)))
7268 self.assertNotIn('key', node.props)
7269
7270 def testEncryptedKeyFile(self):
7271 """Test encrypted node with key-filename property"""
7272 data = self._DoReadFileDtb('305_encrypted_key_file.dts')[0]
7273
7274 dtb = fdt.Fdt.FromData(data)
7275 dtb.Scan()
7276
7277 node = dtb.GetNode('/images/u-boot/cipher')
7278 self.assertEqual('algo-name', node.props['algo'].value)
7279 self.assertEqual(ENCRYPTED_IV_DATA,
7280 tools.to_bytes(''.join(node.props['iv'].value)))
7281 self.assertEqual(ENCRYPTED_KEY_DATA,
7282 tools.to_bytes(''.join(node.props['key'].value)))
7283 self.assertNotIn('key-source', node.props)
7284
Lukas Funkee901faf2023-07-18 13:53:13 +02007285
7286 def testSplPubkeyDtb(self):
Simon Glass4b861272024-07-20 11:49:41 +01007287 """Test u_boot_spl_pubkey_dtb etype"""
7288 data = tools.read_file(self.TestFile("key.pem"))
7289 self._MakeInputFile("key.crt", data)
7290 self._DoReadFileRealDtb('306_spl_pubkey_dtb.dts')
7291 image = control.images['image']
7292 entries = image.GetEntries()
7293 dtb_entry = entries['u-boot-spl-pubkey-dtb']
7294 dtb_data = dtb_entry.GetData()
7295 dtb = fdt.Fdt.FromData(dtb_data)
7296 dtb.Scan()
Lukas Funkee901faf2023-07-18 13:53:13 +02007297
Simon Glass4b861272024-07-20 11:49:41 +01007298 signature_node = dtb.GetNode('/signature')
7299 self.assertIsNotNone(signature_node)
7300 key_node = signature_node.FindNode("key-key")
7301 self.assertIsNotNone(key_node)
7302 self.assertEqual(fdt_util.GetString(key_node, "required"), "conf")
7303 self.assertEqual(fdt_util.GetString(key_node, "algo"), "sha384,rsa4096")
7304 self.assertEqual(fdt_util.GetString(key_node, "key-name-hint"), "key")
Christian Taedcke62ac29a2023-07-17 09:05:54 +02007305
Lukas Funke712e1062023-08-03 17:22:14 +02007306 def testXilinxBootgenSigning(self):
7307 """Test xilinx-bootgen etype"""
7308 bootgen = bintool.Bintool.create('bootgen')
7309 self._CheckBintool(bootgen)
7310 data = tools.read_file(self.TestFile("key.key"))
7311 self._MakeInputFile("psk.pem", data)
7312 self._MakeInputFile("ssk.pem", data)
7313 self._SetupPmuFwlElf()
7314 self._SetupSplElf()
7315 self._DoReadFileRealDtb('307_xilinx_bootgen_sign.dts')
7316 image_fname = tools.get_output_filename('image.bin')
7317
7318 # Read partition header table and check if authentication is enabled
7319 bootgen_out = bootgen.run_cmd("-arch", "zynqmp",
7320 "-read", image_fname, "pht").splitlines()
7321 attributes = {"authentication": None,
7322 "core": None,
7323 "encryption": None}
7324
7325 for l in bootgen_out:
7326 for a in attributes.keys():
7327 if a in l:
7328 m = re.match(fr".*{a} \[([^]]+)\]", l)
7329 attributes[a] = m.group(1)
7330
7331 self.assertTrue(attributes['authentication'] == "rsa")
7332 self.assertTrue(attributes['core'] == "a53-0")
7333 self.assertTrue(attributes['encryption'] == "no")
7334
7335 def testXilinxBootgenSigningEncryption(self):
7336 """Test xilinx-bootgen etype"""
7337 bootgen = bintool.Bintool.create('bootgen')
7338 self._CheckBintool(bootgen)
7339 data = tools.read_file(self.TestFile("key.key"))
7340 self._MakeInputFile("psk.pem", data)
7341 self._MakeInputFile("ssk.pem", data)
7342 self._SetupPmuFwlElf()
7343 self._SetupSplElf()
7344 self._DoReadFileRealDtb('308_xilinx_bootgen_sign_enc.dts')
7345 image_fname = tools.get_output_filename('image.bin')
7346
7347 # Read boot header in order to verify encryption source and
7348 # encryption parameter
7349 bootgen_out = bootgen.run_cmd("-arch", "zynqmp",
7350 "-read", image_fname, "bh").splitlines()
7351 attributes = {"auth_only":
7352 {"re": r".*auth_only \[([^]]+)\]", "value": None},
7353 "encryption_keystore":
7354 {"re": r" *encryption_keystore \(0x28\) : (.*)",
7355 "value": None},
7356 }
7357
7358 for l in bootgen_out:
7359 for a in attributes.keys():
7360 if a in l:
7361 m = re.match(attributes[a]['re'], l)
7362 attributes[a] = m.group(1)
7363
7364 # Check if fsbl-attribute is set correctly
7365 self.assertTrue(attributes['auth_only'] == "true")
7366 # Check if key is stored in efuse
7367 self.assertTrue(attributes['encryption_keystore'] == "0xa5c3c5a3")
7368
7369 def testXilinxBootgenMissing(self):
7370 """Test that binman still produces an image if bootgen is missing"""
7371 data = tools.read_file(self.TestFile("key.key"))
7372 self._MakeInputFile("psk.pem", data)
7373 self._MakeInputFile("ssk.pem", data)
7374 self._SetupPmuFwlElf()
7375 self._SetupSplElf()
Simon Glass14d64e32025-04-29 07:21:59 -06007376 with terminal.capture() as (_, stderr):
Lukas Funke712e1062023-08-03 17:22:14 +02007377 self._DoTestFile('307_xilinx_bootgen_sign.dts',
7378 force_missing_bintools='bootgen')
7379 err = stderr.getvalue()
7380 self.assertRegex(err,
7381 "Image 'image'.*missing bintools.*: bootgen")
7382
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +05307383 def _GetCapsuleHeaders(self, data):
7384 """Get the capsule header contents
7385
7386 Args:
7387 data: Capsule file contents
7388
7389 Returns:
7390 Dict:
7391 key: Capsule Header name (str)
7392 value: Header field value (str)
7393 """
7394 capsule_file = os.path.join(self._indir, 'test.capsule')
7395 tools.write_file(capsule_file, data)
7396
7397 out = tools.run('mkeficapsule', '--dump-capsule', capsule_file)
7398 lines = out.splitlines()
7399
7400 re_line = re.compile(r'^([^:\-\t]*)(?:\t*\s*:\s*(.*))?$')
7401 vals = {}
7402 for line in lines:
7403 mat = re_line.match(line)
7404 if mat:
7405 vals[mat.group(1)] = mat.group(2)
7406
7407 return vals
7408
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307409 def _CheckCapsule(self, data, signed_capsule=False, version_check=False,
7410 capoemflags=False):
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +05307411 fmp_signature = "3153534D" # 'M', 'S', 'S', '1'
7412 fmp_size = "00000010"
7413 fmp_fw_version = "00000002"
7414 capsule_image_index = "00000001"
7415 oemflag = "00018000"
7416 auth_hdr_revision = "00000200"
7417 auth_hdr_cert_type = "00000EF1"
7418
7419 payload_data_len = len(EFI_CAPSULE_DATA)
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307420
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +05307421 hdr = self._GetCapsuleHeaders(data)
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307422
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +05307423 self.assertEqual(FW_MGMT_GUID.upper(), hdr['EFI_CAPSULE_HDR.CAPSULE_GUID'])
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307424
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +05307425 self.assertEqual(CAPSULE_IMAGE_GUID.upper(),
7426 hdr['FMP_CAPSULE_IMAGE_HDR.UPDATE_IMAGE_TYPE_ID'])
7427 self.assertEqual(capsule_image_index,
7428 hdr['FMP_CAPSULE_IMAGE_HDR.UPDATE_IMAGE_INDEX'])
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307429
7430 if capoemflags:
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +05307431 self.assertEqual(oemflag, hdr['EFI_CAPSULE_HDR.FLAGS'])
7432
7433 if signed_capsule:
7434 self.assertEqual(auth_hdr_revision,
7435 hdr['EFI_FIRMWARE_IMAGE_AUTH.AUTH_INFO.HDR.wREVISION'])
7436 self.assertEqual(auth_hdr_cert_type,
7437 hdr['EFI_FIRMWARE_IMAGE_AUTH.AUTH_INFO.HDR.wCERTTYPE'])
7438 self.assertEqual(WIN_CERT_TYPE_EFI_GUID.upper(),
7439 hdr['EFI_FIRMWARE_IMAGE_AUTH.AUTH_INFO.CERT_TYPE'])
7440
7441 if version_check:
7442 self.assertEqual(fmp_signature,
7443 hdr['FMP_PAYLOAD_HDR.SIGNATURE'])
7444 self.assertEqual(fmp_size,
7445 hdr['FMP_PAYLOAD_HDR.HEADER_SIZE'])
7446 self.assertEqual(fmp_fw_version,
7447 hdr['FMP_PAYLOAD_HDR.FW_VERSION'])
7448
7449 self.assertEqual(payload_data_len, int(hdr['Payload Image Size']))
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307450
Sughosh Ganu6b2d18a2023-10-10 14:40:59 +05307451 def _CheckEmptyCapsule(self, data, accept_capsule=False):
7452 if accept_capsule:
7453 capsule_hdr_guid = EMPTY_CAPSULE_ACCEPT_GUID
7454 else:
7455 capsule_hdr_guid = EMPTY_CAPSULE_REVERT_GUID
7456
7457 hdr = self._GetCapsuleHeaders(data)
7458
7459 self.assertEqual(capsule_hdr_guid.upper(),
7460 hdr['EFI_CAPSULE_HDR.CAPSULE_GUID'])
7461
7462 if accept_capsule:
7463 capsule_size = "0000002C"
7464 else:
7465 capsule_size = "0000001C"
7466 self.assertEqual(capsule_size,
7467 hdr['EFI_CAPSULE_HDR.CAPSULE_IMAGE_SIZE'])
7468
7469 if accept_capsule:
7470 self.assertEqual(CAPSULE_IMAGE_GUID.upper(), hdr['ACCEPT_IMAGE_GUID'])
7471
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307472 def testCapsuleGen(self):
7473 """Test generation of EFI capsule"""
7474 data = self._DoReadFile('311_capsule.dts')
7475
7476 self._CheckCapsule(data)
7477
7478 def testSignedCapsuleGen(self):
7479 """Test generation of EFI capsule"""
7480 data = tools.read_file(self.TestFile("key.key"))
7481 self._MakeInputFile("key.key", data)
7482 data = tools.read_file(self.TestFile("key.pem"))
7483 self._MakeInputFile("key.crt", data)
7484
7485 data = self._DoReadFile('312_capsule_signed.dts')
7486
7487 self._CheckCapsule(data, signed_capsule=True)
7488
7489 def testCapsuleGenVersionSupport(self):
7490 """Test generation of EFI capsule with version support"""
7491 data = self._DoReadFile('313_capsule_version.dts')
7492
7493 self._CheckCapsule(data, version_check=True)
7494
7495 def testCapsuleGenSignedVer(self):
7496 """Test generation of signed EFI capsule with version information"""
7497 data = tools.read_file(self.TestFile("key.key"))
7498 self._MakeInputFile("key.key", data)
7499 data = tools.read_file(self.TestFile("key.pem"))
7500 self._MakeInputFile("key.crt", data)
7501
7502 data = self._DoReadFile('314_capsule_signed_ver.dts')
7503
7504 self._CheckCapsule(data, signed_capsule=True, version_check=True)
7505
7506 def testCapsuleGenCapOemFlags(self):
7507 """Test generation of EFI capsule with OEM Flags set"""
7508 data = self._DoReadFile('315_capsule_oemflags.dts')
7509
7510 self._CheckCapsule(data, capoemflags=True)
7511
7512 def testCapsuleGenKeyMissing(self):
7513 """Test that binman errors out on missing key"""
7514 with self.assertRaises(ValueError) as e:
7515 self._DoReadFile('316_capsule_missing_key.dts')
7516
7517 self.assertIn("Both private key and public key certificate need to be provided",
7518 str(e.exception))
7519
7520 def testCapsuleGenIndexMissing(self):
7521 """Test that binman errors out on missing image index"""
7522 with self.assertRaises(ValueError) as e:
7523 self._DoReadFile('317_capsule_missing_index.dts')
7524
7525 self.assertIn("entry is missing properties: image-index",
7526 str(e.exception))
7527
7528 def testCapsuleGenGuidMissing(self):
7529 """Test that binman errors out on missing image GUID"""
7530 with self.assertRaises(ValueError) as e:
7531 self._DoReadFile('318_capsule_missing_guid.dts')
7532
7533 self.assertIn("entry is missing properties: image-guid",
7534 str(e.exception))
7535
Sughosh Ganu6b2d18a2023-10-10 14:40:59 +05307536 def testCapsuleGenAcceptCapsule(self):
7537 """Test generationg of accept EFI capsule"""
7538 data = self._DoReadFile('319_capsule_accept.dts')
7539
7540 self._CheckEmptyCapsule(data, accept_capsule=True)
7541
7542 def testCapsuleGenRevertCapsule(self):
7543 """Test generationg of revert EFI capsule"""
7544 data = self._DoReadFile('320_capsule_revert.dts')
7545
7546 self._CheckEmptyCapsule(data)
7547
7548 def testCapsuleGenAcceptGuidMissing(self):
7549 """Test that binman errors out on missing image GUID for accept capsule"""
7550 with self.assertRaises(ValueError) as e:
7551 self._DoReadFile('321_capsule_accept_missing_guid.dts')
7552
7553 self.assertIn("Image GUID needed for generating accept capsule",
7554 str(e.exception))
7555
7556 def testCapsuleGenEmptyCapsuleTypeMissing(self):
7557 """Test that capsule-type is specified"""
7558 with self.assertRaises(ValueError) as e:
7559 self._DoReadFile('322_empty_capsule_type_missing.dts')
7560
7561 self.assertIn("entry is missing properties: capsule-type",
7562 str(e.exception))
7563
7564 def testCapsuleGenAcceptOrRevertMissing(self):
7565 """Test that both accept and revert capsule are not specified"""
7566 with self.assertRaises(ValueError) as e:
7567 self._DoReadFile('323_capsule_accept_revert_missing.dts')
7568
Simon Glassa360b8f2024-06-23 11:55:06 -06007569 def test_assume_size(self):
7570 """Test handling of the assume-size property for external blob"""
7571 with self.assertRaises(ValueError) as e:
7572 self._DoTestFile('326_assume_size.dts', allow_missing=True,
7573 allow_fake_blobs=True)
7574 self.assertIn("contents size 0xa (10) exceeds section size 0x9 (9)",
7575 str(e.exception))
7576
7577 def test_assume_size_ok(self):
7578 """Test handling of the assume-size where it fits OK"""
Simon Glass14d64e32025-04-29 07:21:59 -06007579 with terminal.capture() as (stdout, stderr):
Simon Glassa360b8f2024-06-23 11:55:06 -06007580 self._DoTestFile('327_assume_size_ok.dts', allow_missing=True,
7581 allow_fake_blobs=True)
7582 err = stderr.getvalue()
7583 self.assertRegex(
7584 err,
7585 "Image '.*' has faked external blobs and is non-functional: .*")
7586
7587 def test_assume_size_no_fake(self):
7588 """Test handling of the assume-size where it fits OK"""
Simon Glass14d64e32025-04-29 07:21:59 -06007589 with terminal.capture() as (stdout, stderr):
Simon Glassa360b8f2024-06-23 11:55:06 -06007590 self._DoTestFile('327_assume_size_ok.dts', allow_missing=True)
7591 err = stderr.getvalue()
7592 self.assertRegex(
7593 err,
7594 "Image '.*' is missing external blobs and is non-functional: .*")
7595
Simon Glass5f7aadf2024-07-20 11:49:47 +01007596 def SetupAlternateDts(self):
7597 """Compile the .dts test files for alternative-fdt
7598
7599 Returns:
7600 tuple:
7601 str: Test directory created
7602 list of str: '.bin' files which we expect Binman to create
7603 """
7604 testdir = TestFunctional._MakeInputDir('dtb')
7605 dtb_list = []
7606 for fname in glob.glob(f'{self.TestFile("alt_dts")}/*.dts'):
7607 tmp_fname = fdt_util.EnsureCompiled(fname, testdir)
7608 base = os.path.splitext(os.path.basename(fname))[0]
7609 dtb_list.append(base + '.bin')
7610 shutil.move(tmp_fname, os.path.join(testdir, base + '.dtb'))
7611
7612 return testdir, dtb_list
7613
Simon Glassf3598922024-07-20 11:49:45 +01007614 def CheckAlternates(self, dts, phase, xpl_data):
7615 """Run the test for the alterative-fdt etype
7616
7617 Args:
7618 dts (str): Devicetree file to process
7619 phase (str): Phase to process ('spl', 'tpl' or 'vpl')
7620 xpl_data (bytes): Expected data for the phase's binary
7621
7622 Returns:
7623 dict of .dtb files produced
7624 key: str filename
7625 value: Fdt object
7626 """
Simon Glass5f7aadf2024-07-20 11:49:47 +01007627 dtb_list = self.SetupAlternateDts()[1]
Simon Glassf3598922024-07-20 11:49:45 +01007628
7629 entry_args = {
7630 f'{phase}-dtb': '1',
7631 f'{phase}-bss-pad': 'y',
7632 'of-spl-remove-props': 'prop-to-remove another-prop-to-get-rid-of',
7633 }
7634 data = self._DoReadFileDtb(dts, use_real_dtb=True, update_dtb=True,
7635 use_expanded=True, entry_args=entry_args)[0]
7636 self.assertEqual(xpl_data, data[:len(xpl_data)])
7637 rest = data[len(xpl_data):]
7638 pad_len = 10
7639 self.assertEqual(tools.get_bytes(0, pad_len), rest[:pad_len])
7640
7641 # Check the dtb is using the test file
7642 dtb_data = rest[pad_len:]
7643 dtb = fdt.Fdt.FromData(dtb_data)
7644 dtb.Scan()
7645 fdt_size = dtb.GetFdtObj().totalsize()
7646 self.assertEqual('model-not-set',
7647 fdt_util.GetString(dtb.GetRoot(), 'compatible'))
7648
7649 pad_len = 10
7650
7651 # Check the other output files
7652 dtbs = {}
7653 for fname in dtb_list:
7654 pathname = tools.get_output_filename(fname)
7655 self.assertTrue(os.path.exists(pathname))
7656
7657 data = tools.read_file(pathname)
7658 self.assertEqual(xpl_data, data[:len(xpl_data)])
7659 rest = data[len(xpl_data):]
7660
7661 self.assertEqual(tools.get_bytes(0, pad_len), rest[:pad_len])
7662 rest = rest[pad_len:]
7663
7664 dtb = fdt.Fdt.FromData(rest)
7665 dtb.Scan()
7666 dtbs[fname] = dtb
7667
7668 expected = 'one' if '1' in fname else 'two'
7669 self.assertEqual(f'u-boot,model-{expected}',
7670 fdt_util.GetString(dtb.GetRoot(), 'compatible'))
7671
7672 # Make sure the FDT is the same size as the 'main' one
7673 rest = rest[fdt_size:]
7674
7675 self.assertEqual(b'', rest)
7676 return dtbs
7677
7678 def testAlternatesFdt(self):
7679 """Test handling of alternates-fdt etype"""
7680 self._SetupTplElf()
7681 dtbs = self.CheckAlternates('328_alternates_fdt.dts', 'tpl',
7682 U_BOOT_TPL_NODTB_DATA)
7683 for dtb in dtbs.values():
7684 # Check for the node with the tag
7685 node = dtb.GetNode('/node')
7686 self.assertIsNotNone(node)
7687 self.assertEqual(5, len(node.props.keys()))
7688
7689 # Make sure the other node is still there
7690 self.assertIsNotNone(dtb.GetNode('/node/other-node'))
7691
7692 def testAlternatesFdtgrep(self):
7693 """Test handling of alternates-fdt etype using fdtgrep"""
7694 self._SetupTplElf()
7695 dtbs = self.CheckAlternates('329_alternates_fdtgrep.dts', 'tpl',
7696 U_BOOT_TPL_NODTB_DATA)
7697 for dtb in dtbs.values():
7698 # Check for the node with the tag
7699 node = dtb.GetNode('/node')
7700 self.assertIsNotNone(node)
7701 self.assertEqual({'some-prop', 'not-a-prop-to-remove'},
7702 node.props.keys())
7703
7704 # Make sure the other node is gone
7705 self.assertIsNone(dtb.GetNode('/node/other-node'))
7706
7707 def testAlternatesFdtgrepVpl(self):
7708 """Test handling of alternates-fdt etype using fdtgrep with vpl"""
7709 self._SetupVplElf()
7710 dtbs = self.CheckAlternates('330_alternates_vpl.dts', 'vpl',
7711 U_BOOT_VPL_NODTB_DATA)
7712
7713 def testAlternatesFdtgrepSpl(self):
7714 """Test handling of alternates-fdt etype using fdtgrep with spl"""
7715 self._SetupSplElf()
7716 dtbs = self.CheckAlternates('331_alternates_spl.dts', 'spl',
7717 U_BOOT_SPL_NODTB_DATA)
7718
7719 def testAlternatesFdtgrepInval(self):
7720 """Test alternates-fdt etype using fdtgrep with invalid phase"""
7721 self._SetupSplElf()
7722 with self.assertRaises(ValueError) as e:
7723 dtbs = self.CheckAlternates('332_alternates_inval.dts', 'spl',
7724 U_BOOT_SPL_NODTB_DATA)
7725 self.assertIn("Invalid U-Boot phase 'bad-phase': Use tpl/vpl/spl",
7726 str(e.exception))
7727
Simon Glasscd2783e2024-07-20 11:49:46 +01007728 def testFitFdtListDir(self):
7729 """Test an image with an FIT with FDT images using fit,fdt-list-dir"""
Simon Glass1bba8942024-08-26 13:11:34 -06007730 old_dir = os.getcwd()
7731 try:
7732 os.chdir(self._indir)
7733 self.CheckFitFdt('333_fit_fdt_dir.dts', False)
7734 finally:
7735 os.chdir(old_dir)
Simon Glasscd2783e2024-07-20 11:49:46 +01007736
Simon Glassd2a9d6e2024-08-26 13:11:37 -06007737 def testFitFdtListDirDefault(self):
7738 """Test an FIT fit,fdt-list-dir where the default DT in is a subdir"""
7739 old_dir = os.getcwd()
7740 try:
7741 os.chdir(self._indir)
7742 self.CheckFitFdt('333_fit_fdt_dir.dts', False,
7743 default_dt='rockchip/test-fdt2')
7744 finally:
7745 os.chdir(old_dir)
7746
Simon Glass5f7aadf2024-07-20 11:49:47 +01007747 def testFitFdtCompat(self):
7748 """Test an image with an FIT with compatible in the config nodes"""
7749 entry_args = {
7750 'of-list': 'model1 model2',
7751 'default-dt': 'model2',
7752 }
7753 testdir, dtb_list = self.SetupAlternateDts()
7754 data = self._DoReadFileDtb(
7755 '334_fit_fdt_compat.dts', use_real_dtb=True, update_dtb=True,
7756 entry_args=entry_args, extra_indirs=[testdir])[0]
7757
7758 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
7759
7760 fit = fdt.Fdt.FromData(fit_data)
7761 fit.Scan()
7762
7763 cnode = fit.GetNode('/configurations')
7764 self.assertIn('default', cnode.props)
7765 self.assertEqual('config-2', cnode.props['default'].value)
7766
7767 for seq in range(1, 2):
7768 name = f'config-{seq}'
7769 fnode = fit.GetNode('/configurations/%s' % name)
7770 self.assertIsNotNone(fnode)
7771 self.assertIn('compatible', fnode.props.keys())
7772 expected = 'one' if seq == 1 else 'two'
7773 self.assertEqual(f'u-boot,model-{expected}',
7774 fnode.props['compatible'].value)
7775
Simon Glassa04b9942024-07-20 11:49:48 +01007776 def testFitFdtPhase(self):
7777 """Test an image with an FIT with fdt-phase in the fdt nodes"""
7778 phase = 'tpl'
7779 entry_args = {
7780 f'{phase}-dtb': '1',
7781 f'{phase}-bss-pad': 'y',
7782 'of-spl-remove-props': 'prop-to-remove another-prop-to-get-rid-of',
7783 'of-list': 'model1 model2',
7784 'default-dt': 'model2',
7785 }
7786 testdir, dtb_list = self.SetupAlternateDts()
7787 data = self._DoReadFileDtb(
7788 '335_fit_fdt_phase.dts', use_real_dtb=True, update_dtb=True,
7789 entry_args=entry_args, extra_indirs=[testdir])[0]
7790 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
7791 fit = fdt.Fdt.FromData(fit_data)
7792 fit.Scan()
7793
7794 # Check that each FDT has only the expected properties for the phase
7795 for seq in range(1, 2):
7796 fnode = fit.GetNode(f'/images/fdt-{seq}')
7797 self.assertIsNotNone(fnode)
7798 dtb = fdt.Fdt.FromData(fnode.props['data'].bytes)
7799 dtb.Scan()
7800
7801 # Make sure that the 'bootph-pre-sram' tag in /node protects it from
7802 # removal
7803 node = dtb.GetNode('/node')
7804 self.assertIsNotNone(node)
7805 self.assertEqual({'some-prop', 'not-a-prop-to-remove'},
7806 node.props.keys())
7807
7808 # Make sure the other node is gone
7809 self.assertIsNone(dtb.GetNode('/node/other-node'))
7810
Simon Glassb553e8a2024-08-26 13:11:29 -06007811 def testMkeficapsuleMissing(self):
7812 """Test that binman complains if mkeficapsule is missing"""
7813 with self.assertRaises(ValueError) as e:
7814 self._DoTestFile('311_capsule.dts',
7815 force_missing_bintools='mkeficapsule')
7816 self.assertIn("Node '/binman/efi-capsule': Missing tool: 'mkeficapsule'",
7817 str(e.exception))
7818
7819 def testMkeficapsuleMissingOk(self):
7820 """Test that binman deals with mkeficapsule being missing"""
Simon Glass14d64e32025-04-29 07:21:59 -06007821 with terminal.capture() as (stdout, stderr):
Simon Glassb553e8a2024-08-26 13:11:29 -06007822 ret = self._DoTestFile('311_capsule.dts',
7823 force_missing_bintools='mkeficapsule',
7824 allow_missing=True)
7825 self.assertEqual(103, ret)
7826 err = stderr.getvalue()
7827 self.assertRegex(err, "Image 'image'.*missing bintools.*: mkeficapsule")
7828
Simon Glass4b0f4142024-08-26 13:11:40 -06007829 def testSymbolsBase(self):
7830 """Test handling of symbols-base"""
7831 self.checkSymbols('336_symbols_base.dts', U_BOOT_SPL_DATA, 0x1c,
7832 symbols_base=0)
7833
7834 def testSymbolsBaseExpanded(self):
7835 """Test handling of symbols-base with expanded entries"""
7836 entry_args = {
7837 'spl-dtb': '1',
7838 }
7839 self.checkSymbols('337_symbols_base_expand.dts', U_BOOT_SPL_NODTB_DATA +
7840 U_BOOT_SPL_DTB_DATA, 0x38,
7841 entry_args=entry_args, use_expanded=True,
7842 symbols_base=0)
7843
Simon Glass3eb30a42024-08-26 13:11:42 -06007844 def testSymbolsCompressed(self):
7845 """Test binman complains about symbols from a compressed section"""
Simon Glass14d64e32025-04-29 07:21:59 -06007846 with terminal.capture() as (stdout, stderr):
Simon Glass3eb30a42024-08-26 13:11:42 -06007847 self.checkSymbols('338_symbols_comp.dts', U_BOOT_SPL_DATA, None)
7848 out = stdout.getvalue()
7849 self.assertIn('Symbol-writing: no value for /binman/section/u-boot',
7850 out)
7851
Simon Glass9c25ef22024-08-26 13:11:43 -06007852 def testNxpImx8Image(self):
7853 """Test that binman can produce an iMX8 image"""
7854 self._DoTestFile('339_nxp_imx8.dts')
7855
Alice Guo1d334022025-04-28 18:37:39 +08007856 def testNxpHeaderDdrfw(self):
7857 """Test that binman can add a header to DDR PHY firmware images"""
7858 data = self._DoReadFile('346_nxp_ddrfw_imx95.dts')
7859 self.assertEqual(len(IMX_LPDDR_IMEM_DATA).to_bytes(4, 'little') +
7860 len(IMX_LPDDR_DMEM_DATA).to_bytes(4, 'little') +
7861 IMX_LPDDR_IMEM_DATA + IMX_LPDDR_DMEM_DATA, data)
7862
Alexander Kochetkova730a282024-09-16 11:24:46 +03007863 def testFitSignSimple(self):
7864 """Test that image with FIT and signature nodes can be signed"""
7865 if not elf.ELF_TOOLS:
7866 self.skipTest('Python elftools not available')
7867 entry_args = {
7868 'of-list': 'test-fdt1',
7869 'default-dt': 'test-fdt1',
7870 'atf-bl31-path': 'bl31.elf',
7871 }
7872 data = tools.read_file(self.TestFile("340_rsa2048.key"))
7873 self._MakeInputFile("keys/rsa2048.key", data)
7874
7875 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
7876 keys_subdir = os.path.join(self._indir, "keys")
7877 data = self._DoReadFileDtb(
7878 '340_fit_signature.dts',
7879 entry_args=entry_args,
7880 extra_indirs=[test_subdir, keys_subdir])[0]
7881
7882 dtb = fdt.Fdt.FromData(data)
7883 dtb.Scan()
7884
7885 conf = dtb.GetNode('/configurations/conf-uboot-1')
7886 self.assertIsNotNone(conf)
7887 signature = conf.FindNode('signature')
7888 self.assertIsNotNone(signature)
7889 self.assertIsNotNone(signature.props.get('value'))
7890
7891 images = dtb.GetNode('/images')
7892 self.assertIsNotNone(images)
7893 for subnode in images.subnodes:
7894 signature = subnode.FindNode('signature')
7895 self.assertIsNotNone(signature)
7896 self.assertIsNotNone(signature.props.get('value'))
7897
7898 def testFitSignKeyNotFound(self):
7899 """Test that missing keys raise an error"""
7900 if not elf.ELF_TOOLS:
7901 self.skipTest('Python elftools not available')
7902 entry_args = {
7903 'of-list': 'test-fdt1',
7904 'default-dt': 'test-fdt1',
7905 'atf-bl31-path': 'bl31.elf',
7906 }
7907 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
7908 with self.assertRaises(ValueError) as e:
7909 self._DoReadFileDtb(
7910 '340_fit_signature.dts',
7911 entry_args=entry_args,
7912 extra_indirs=[test_subdir])[0]
7913 self.assertIn(
7914 'Filename \'rsa2048.key\' not found in input path',
7915 str(e.exception))
7916
7917 def testFitSignMultipleKeyPaths(self):
7918 """Test that keys found in multiple paths raise an error"""
7919 if not elf.ELF_TOOLS:
7920 self.skipTest('Python elftools not available')
7921 entry_args = {
7922 'of-list': 'test-fdt1',
7923 'default-dt': 'test-fdt1',
7924 'atf-bl31-path': 'bl31.elf',
7925 }
7926 data = tools.read_file(self.TestFile("340_rsa2048.key"))
7927 self._MakeInputFile("keys1/rsa2048.key", data)
7928 data = tools.read_file(self.TestFile("340_rsa2048.key"))
7929 self._MakeInputFile("keys2/conf-rsa2048.key", data)
7930
7931 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
7932 keys_subdir1 = os.path.join(self._indir, "keys1")
7933 keys_subdir2 = os.path.join(self._indir, "keys2")
7934 with self.assertRaises(ValueError) as e:
7935 self._DoReadFileDtb(
7936 '341_fit_signature.dts',
7937 entry_args=entry_args,
7938 extra_indirs=[test_subdir, keys_subdir1, keys_subdir2])[0]
7939 self.assertIn(
7940 'Node \'/binman/fit\': multiple key paths found',
7941 str(e.exception))
7942
7943 def testFitSignNoSingatureNodes(self):
7944 """Test that fit,sign doens't raise error if no signature nodes found"""
7945 if not elf.ELF_TOOLS:
7946 self.skipTest('Python elftools not available')
7947 entry_args = {
7948 'of-list': 'test-fdt1',
7949 'default-dt': 'test-fdt1',
7950 'atf-bl31-path': 'bl31.elf',
7951 }
7952 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
7953 self._DoReadFileDtb(
7954 '342_fit_signature.dts',
7955 entry_args=entry_args,
7956 extra_indirs=[test_subdir])[0]
7957
Simon Glassa360b8f2024-06-23 11:55:06 -06007958
Paul HENRYSff318462024-11-25 18:47:17 +01007959 def testSimpleFitEncryptedData(self):
7960 """Test an image with a FIT containing data to be encrypted"""
7961 data = tools.read_file(self.TestFile("aes256.bin"))
7962 self._MakeInputFile("keys/aes256.bin", data)
7963
7964 keys_subdir = os.path.join(self._indir, "keys")
7965 data = self._DoReadFileDtb(
7966 '343_fit_encrypt_data.dts',
7967 extra_indirs=[keys_subdir])[0]
7968
7969 fit = fdt.Fdt.FromData(data)
7970 fit.Scan()
7971
7972 # Extract the encrypted data and the Initialization Vector from the FIT
7973 node = fit.GetNode('/images/u-boot')
7974 subnode = fit.GetNode('/images/u-boot/cipher')
7975 data_size_unciphered = int.from_bytes(fit.GetProps(node)['data-size-unciphered'].bytes,
7976 byteorder='big')
7977 self.assertEqual(data_size_unciphered, len(U_BOOT_NODTB_DATA))
7978
7979 # Retrieve the key name from the FIT removing any null byte
7980 key_name = fit.GetProps(subnode)['key-name-hint'].bytes.replace(b'\x00', b'')
7981 with open(self.TestFile(key_name.decode('ascii') + '.bin'), 'rb') as file:
7982 key = file.read()
7983 iv = fit.GetProps(subnode)['iv'].bytes.hex()
7984 enc_data = fit.GetProps(node)['data'].bytes
7985 outdir = tools.get_output_dir()
7986 enc_data_file = os.path.join(outdir, 'encrypted_data.bin')
7987 tools.write_file(enc_data_file, enc_data)
7988 data_file = os.path.join(outdir, 'data.bin')
7989
7990 # Decrypt the encrypted data from the FIT and compare the data
7991 tools.run('openssl', 'enc', '-aes-256-cbc', '-nosalt', '-d', '-in',
7992 enc_data_file, '-out', data_file, '-K', key.hex(), '-iv', iv)
7993 with open(data_file, 'r') as file:
7994 dec_data = file.read()
7995 self.assertEqual(U_BOOT_NODTB_DATA, dec_data.encode('ascii'))
7996
7997 def testSimpleFitEncryptedDataMissingKey(self):
7998 """Test an image with a FIT containing data to be encrypted but with a missing key"""
7999 with self.assertRaises(ValueError) as e:
8000 self._DoReadFile('344_fit_encrypt_data_no_key.dts')
8001
8002 self.assertIn("Filename 'aes256.bin' not found in input path", str(e.exception))
8003
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01008004 def testFitFdtName(self):
8005 """Test an image with an FIT with multiple FDT images using NAME"""
8006 self.CheckFitFdt('345_fit_fdt_name.dts', use_seq_num=False)
8007
Neha Malcom Francisa25b4832025-03-17 10:24:20 +05308008 def testRemoveTemplate(self):
8009 """Test whether template is removed"""
8010 TestFunctional._MakeInputFile('my-blob.bin', b'blob')
8011 TestFunctional._MakeInputFile('my-blob2.bin', b'other')
8012 self._DoTestFile('346_remove_template.dts',
8013 force_missing_bintools='openssl',)
8014
Simon Glassac599912017-11-12 21:52:22 -07008015if __name__ == "__main__":
8016 unittest.main()