blob: ffef213c0ffef6ebe359eec75d58d36311db306e [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 Glass17899e42025-04-29 07:22:06 -0600277 toolpath=None, verbosity=None, no_capture=False):
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 Glass1a0b27d2025-04-29 07:22:00 -0600286 toolpath: list 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 Glass17899e42025-04-29 07:22:06 -0600292 cls.no_capture = no_capture
Simon Glass1c420c92019-07-08 13:18:49 -0600293
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +0200294 def _CheckBintool(self, bintool):
295 if not bintool.is_present():
296 self.skipTest('%s not available' % bintool.name)
297
Simon Glass1de34482019-07-08 13:18:53 -0600298 def _CheckLz4(self):
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +0200299 bintool = self.comp_bintools['lz4']
300 self._CheckBintool(bintool)
Simon Glass1de34482019-07-08 13:18:53 -0600301
Simon Glassee9d10d2019-07-20 12:24:09 -0600302 def _CleanupOutputDir(self):
303 """Remove the temporary output directory"""
304 if self.preserve_outdirs:
305 print('Preserving output dir: %s' % tools.outdir)
306 else:
Simon Glass80025522022-01-29 14:14:04 -0700307 tools._finalise_for_test()
Simon Glassee9d10d2019-07-20 12:24:09 -0600308
Simon Glass57454f42016-11-25 20:15:52 -0700309 def setUp(self):
310 # Enable this to turn on debugging output
Simon Glass011f1b32022-01-29 14:14:15 -0700311 # tout.init(tout.DEBUG)
Simon Glass5dc22cf2025-02-03 09:26:42 -0700312 command.TEST_RESULT = None
Simon Glass57454f42016-11-25 20:15:52 -0700313
314 def tearDown(self):
315 """Remove the temporary output directory"""
Simon Glassee9d10d2019-07-20 12:24:09 -0600316 self._CleanupOutputDir()
Simon Glass57454f42016-11-25 20:15:52 -0700317
Simon Glassb3d6fc72019-07-20 12:24:10 -0600318 def _SetupImageInTmpdir(self):
319 """Set up the output image in a new temporary directory
320
321 This is used when an image has been generated in the output directory,
322 but we want to run binman again. This will create a new output
323 directory and fail to delete the original one.
324
325 This creates a new temporary directory, copies the image to it (with a
326 new name) and removes the old output directory.
327
328 Returns:
329 Tuple:
330 Temporary directory to use
331 New image filename
332 """
Simon Glass80025522022-01-29 14:14:04 -0700333 image_fname = tools.get_output_filename('image.bin')
Simon Glassb3d6fc72019-07-20 12:24:10 -0600334 tmpdir = tempfile.mkdtemp(prefix='binman.')
335 updated_fname = os.path.join(tmpdir, 'image-updated.bin')
Simon Glass80025522022-01-29 14:14:04 -0700336 tools.write_file(updated_fname, tools.read_file(image_fname))
Simon Glassb3d6fc72019-07-20 12:24:10 -0600337 self._CleanupOutputDir()
338 return tmpdir, updated_fname
339
Simon Glass8425a1f2018-07-17 13:25:48 -0600340 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600341 def _ResetDtbs(cls):
Simon Glass8425a1f2018-07-17 13:25:48 -0600342 TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA)
343 TestFunctional._MakeInputFile('spl/u-boot-spl.dtb', U_BOOT_SPL_DTB_DATA)
344 TestFunctional._MakeInputFile('tpl/u-boot-tpl.dtb', U_BOOT_TPL_DTB_DATA)
Simon Glass56d05412022-02-28 07:16:54 -0700345 TestFunctional._MakeInputFile('vpl/u-boot-vpl.dtb', U_BOOT_VPL_DTB_DATA)
Simon Glass8425a1f2018-07-17 13:25:48 -0600346
Simon Glass57454f42016-11-25 20:15:52 -0700347 def _RunBinman(self, *args, **kwargs):
348 """Run binman using the command line
349
350 Args:
351 Arguments to pass, as a list of strings
352 kwargs: Arguments to pass to Command.RunPipe()
353 """
Simon Glass51f55182025-02-03 09:26:45 -0700354 all_args = [self._binman_pathname] + list(args)
355 result = command.run_one(*all_args, capture=True, capture_stderr=True,
356 raise_on_error=False)
Simon Glass57454f42016-11-25 20:15:52 -0700357 if result.return_code and kwargs.get('raise_on_error', True):
358 raise Exception("Error running '%s': %s" % (' '.join(args),
359 result.stdout + result.stderr))
360 return result
361
Simon Glassf46732a2019-07-08 14:25:29 -0600362 def _DoBinman(self, *argv):
Simon Glass57454f42016-11-25 20:15:52 -0700363 """Run binman using directly (in the same process)
364
365 Args:
366 Arguments to pass, as a list of strings
367 Returns:
368 Return value (0 for success)
369 """
Simon Glassf46732a2019-07-08 14:25:29 -0600370 argv = list(argv)
371 args = cmdline.ParseArgs(argv)
372 args.pager = 'binman-invalid-pager'
373 args.build_dir = self._indir
Simon Glass57454f42016-11-25 20:15:52 -0700374
375 # For testing, you can force an increase in verbosity here
Simon Glassf46732a2019-07-08 14:25:29 -0600376 # args.verbosity = tout.DEBUG
377 return control.Binman(args)
Simon Glass57454f42016-11-25 20:15:52 -0700378
Simon Glass91710b32018-07-17 13:25:32 -0600379 def _DoTestFile(self, fname, debug=False, map=False, update_dtb=False,
Simon Glassb4595d82019-04-25 21:58:34 -0600380 entry_args=None, images=None, use_real_dtb=False,
Simon Glassed930672021-03-18 20:25:05 +1300381 use_expanded=False, verbosity=None, allow_missing=False,
Heiko Thiery6d451362022-01-06 11:49:41 +0100382 allow_fake_blobs=False, extra_indirs=None, threads=None,
Simon Glass66152ce2022-01-09 20:14:09 -0700383 test_section_timeout=False, update_fdt_in_elf=None,
Andrew Davis6b463da2023-07-22 00:14:44 +0530384 force_missing_bintools='', ignore_missing=False, output_dir=None):
Simon Glass57454f42016-11-25 20:15:52 -0700385 """Run binman with a given test file
386
387 Args:
Simon Glass511f6582018-10-01 12:22:30 -0600388 fname: Device-tree source filename to use (e.g. 005_simple.dts)
Simon Glass1e324002018-06-01 09:38:19 -0600389 debug: True to enable debugging output
Simon Glass30732662018-06-01 09:38:20 -0600390 map: True to output map files for the images
Simon Glasse8561af2018-08-01 15:22:37 -0600391 update_dtb: Update the offset and size of each entry in the device
Simon Glassa87014e2018-07-06 10:27:42 -0600392 tree before packing it into the image
Simon Glass3b376c32018-09-14 04:57:12 -0600393 entry_args: Dict of entry args to supply to binman
394 key: arg name
395 value: value of that arg
396 images: List of image names to build
Simon Glass31ee50f2020-09-01 05:13:55 -0600397 use_real_dtb: True to use the test file as the contents of
398 the u-boot-dtb entry. Normally this is not needed and the
399 test contents (the U_BOOT_DTB_DATA string) can be used.
400 But in some test we need the real contents.
Simon Glassed930672021-03-18 20:25:05 +1300401 use_expanded: True to use expanded entries where available, e.g.
402 'u-boot-expanded' instead of 'u-boot'
Simon Glass31ee50f2020-09-01 05:13:55 -0600403 verbosity: Verbosity level to use (0-3, None=don't set it)
404 allow_missing: Set the '--allow-missing' flag so that missing
405 external binaries just produce a warning instead of an error
Heiko Thiery6d451362022-01-06 11:49:41 +0100406 allow_fake_blobs: Set the '--fake-ext-blobs' flag
Simon Glassa435cd12020-09-01 05:13:59 -0600407 extra_indirs: Extra input directories to add using -I
Simon Glass76f496d2021-07-06 10:36:37 -0600408 threads: Number of threads to use (None for default, 0 for
409 single-threaded)
Simon Glass9a798402021-11-03 21:09:17 -0600410 test_section_timeout: True to force the first time to timeout, as
411 used in testThreadTimeout()
Simon Glassadfb8492021-11-03 21:09:18 -0600412 update_fdt_in_elf: Value to pass with --update-fdt-in-elf=xxx
Simon Glassb553e8a2024-08-26 13:11:29 -0600413 force_missing_bintools (str): comma-separated list of bintools to
Simon Glass66152ce2022-01-09 20:14:09 -0700414 regard as missing
Simon Glassb553e8a2024-08-26 13:11:29 -0600415 ignore_missing (bool): True to return success even if there are
416 missing blobs or bintools
Andrew Davis6b463da2023-07-22 00:14:44 +0530417 output_dir: Specific output directory to use for image using -O
Simon Glass9a798402021-11-03 21:09:17 -0600418
419 Returns:
420 int return code, 0 on success
Simon Glass57454f42016-11-25 20:15:52 -0700421 """
Simon Glassf46732a2019-07-08 14:25:29 -0600422 args = []
Simon Glass075a45c2017-11-13 18:55:00 -0700423 if debug:
424 args.append('-D')
Simon Glassf46732a2019-07-08 14:25:29 -0600425 if verbosity is not None:
426 args.append('-v%d' % verbosity)
427 elif self.verbosity:
428 args.append('-v%d' % self.verbosity)
429 if self.toolpath:
430 for path in self.toolpath:
431 args += ['--toolpath', path]
Simon Glass76f496d2021-07-06 10:36:37 -0600432 if threads is not None:
433 args.append('-T%d' % threads)
434 if test_section_timeout:
435 args.append('--test-section-timeout')
Simon Glassf46732a2019-07-08 14:25:29 -0600436 args += ['build', '-p', '-I', self._indir, '-d', self.TestFile(fname)]
Simon Glass30732662018-06-01 09:38:20 -0600437 if map:
438 args.append('-m')
Simon Glassa87014e2018-07-06 10:27:42 -0600439 if update_dtb:
Simon Glass38a411c2019-07-08 13:18:47 -0600440 args.append('-u')
Simon Glass31402012018-09-14 04:57:23 -0600441 if not use_real_dtb:
442 args.append('--fake-dtb')
Simon Glassed930672021-03-18 20:25:05 +1300443 if not use_expanded:
444 args.append('--no-expanded')
Simon Glass91710b32018-07-17 13:25:32 -0600445 if entry_args:
Simon Glass5f3645b2019-05-14 15:53:41 -0600446 for arg, value in entry_args.items():
Simon Glass91710b32018-07-17 13:25:32 -0600447 args.append('-a%s=%s' % (arg, value))
Simon Glass5d94cc62020-07-09 18:39:38 -0600448 if allow_missing:
449 args.append('-M')
Simon Glass6bce5dc2022-11-09 19:14:42 -0700450 if ignore_missing:
451 args.append('-W')
Heiko Thiery6d451362022-01-06 11:49:41 +0100452 if allow_fake_blobs:
453 args.append('--fake-ext-blobs')
Simon Glass66152ce2022-01-09 20:14:09 -0700454 if force_missing_bintools:
455 args += ['--force-missing-bintools', force_missing_bintools]
Simon Glassadfb8492021-11-03 21:09:18 -0600456 if update_fdt_in_elf:
457 args += ['--update-fdt-in-elf', update_fdt_in_elf]
Simon Glass3b376c32018-09-14 04:57:12 -0600458 if images:
459 for image in images:
460 args += ['-i', image]
Simon Glassa435cd12020-09-01 05:13:59 -0600461 if extra_indirs:
462 for indir in extra_indirs:
463 args += ['-I', indir]
Andrew Davis6b463da2023-07-22 00:14:44 +0530464 if output_dir:
465 args += ['-O', output_dir]
Simon Glass075a45c2017-11-13 18:55:00 -0700466 return self._DoBinman(*args)
Simon Glass57454f42016-11-25 20:15:52 -0700467
468 def _SetupDtb(self, fname, outfile='u-boot.dtb'):
Simon Glass72232452016-11-25 20:15:53 -0700469 """Set up a new test device-tree file
470
471 The given file is compiled and set up as the device tree to be used
472 for ths test.
473
474 Args:
475 fname: Filename of .dts file to read
Simon Glass1e324002018-06-01 09:38:19 -0600476 outfile: Output filename for compiled device-tree binary
Simon Glass72232452016-11-25 20:15:53 -0700477
478 Returns:
Simon Glass1e324002018-06-01 09:38:19 -0600479 Contents of device-tree binary
Simon Glass72232452016-11-25 20:15:53 -0700480 """
Simon Glassb8d2daa2019-07-20 12:23:49 -0600481 tmpdir = tempfile.mkdtemp(prefix='binmant.')
482 dtb = fdt_util.EnsureCompiled(self.TestFile(fname), tmpdir)
Simon Glass33486662019-05-14 15:53:42 -0600483 with open(dtb, 'rb') as fd:
Simon Glass57454f42016-11-25 20:15:52 -0700484 data = fd.read()
485 TestFunctional._MakeInputFile(outfile, data)
Simon Glassb8d2daa2019-07-20 12:23:49 -0600486 shutil.rmtree(tmpdir)
Simon Glass752e7552018-10-01 21:12:41 -0600487 return data
Simon Glass57454f42016-11-25 20:15:52 -0700488
Simon Glass56d05412022-02-28 07:16:54 -0700489 def _GetDtbContentsForSpls(self, dtb_data, name):
490 """Create a version of the main DTB for SPL / TPL / VPL
Simon Glasse219aa42018-09-14 04:57:24 -0600491
492 For testing we don't actually have different versions of the DTB. With
493 U-Boot we normally run fdtgrep to remove unwanted nodes, but for tests
494 we don't normally have any unwanted nodes.
495
496 We still want the DTBs for SPL and TPL to be different though, since
497 otherwise it is confusing to know which one we are looking at. So add
498 an 'spl' or 'tpl' property to the top-level node.
Simon Glass31ee50f2020-09-01 05:13:55 -0600499
500 Args:
501 dtb_data: dtb data to modify (this should be a value devicetree)
502 name: Name of a new property to add
503
504 Returns:
505 New dtb data with the property added
Simon Glasse219aa42018-09-14 04:57:24 -0600506 """
507 dtb = fdt.Fdt.FromData(dtb_data)
508 dtb.Scan()
509 dtb.GetNode('/binman').AddZeroProp(name)
510 dtb.Sync(auto_resize=True)
511 dtb.Pack()
512 return dtb.GetContents()
513
Simon Glassed930672021-03-18 20:25:05 +1300514 def _DoReadFileDtb(self, fname, use_real_dtb=False, use_expanded=False,
Simon Glass3eb30a42024-08-26 13:11:42 -0600515 verbosity=None, map=False, update_dtb=False,
516 entry_args=None, reset_dtbs=True, extra_indirs=None,
517 threads=None):
Simon Glass57454f42016-11-25 20:15:52 -0700518 """Run binman and return the resulting image
519
520 This runs binman with a given test file and then reads the resulting
521 output file. It is a shortcut function since most tests need to do
522 these steps.
523
524 Raises an assertion failure if binman returns a non-zero exit code.
525
526 Args:
Simon Glass511f6582018-10-01 12:22:30 -0600527 fname: Device-tree source filename to use (e.g. 005_simple.dts)
Simon Glass57454f42016-11-25 20:15:52 -0700528 use_real_dtb: True to use the test file as the contents of
529 the u-boot-dtb entry. Normally this is not needed and the
530 test contents (the U_BOOT_DTB_DATA string) can be used.
531 But in some test we need the real contents.
Simon Glassed930672021-03-18 20:25:05 +1300532 use_expanded: True to use expanded entries where available, e.g.
533 'u-boot-expanded' instead of 'u-boot'
Simon Glass3eb30a42024-08-26 13:11:42 -0600534 verbosity: Verbosity level to use (0-3, None=don't set it)
Simon Glass30732662018-06-01 09:38:20 -0600535 map: True to output map files for the images
Simon Glasse8561af2018-08-01 15:22:37 -0600536 update_dtb: Update the offset and size of each entry in the device
Simon Glassa87014e2018-07-06 10:27:42 -0600537 tree before packing it into the image
Simon Glass31ee50f2020-09-01 05:13:55 -0600538 entry_args: Dict of entry args to supply to binman
539 key: arg name
540 value: value of that arg
541 reset_dtbs: With use_real_dtb the test dtb is overwritten by this
542 function. If reset_dtbs is True, then the original test dtb
543 is written back before this function finishes
Simon Glassa435cd12020-09-01 05:13:59 -0600544 extra_indirs: Extra input directories to add using -I
Simon Glass76f496d2021-07-06 10:36:37 -0600545 threads: Number of threads to use (None for default, 0 for
546 single-threaded)
Simon Glass72232452016-11-25 20:15:53 -0700547
548 Returns:
549 Tuple:
550 Resulting image contents
551 Device tree contents
Simon Glass30732662018-06-01 09:38:20 -0600552 Map data showing contents of image (or None if none)
Simon Glassdef77b52018-07-17 13:25:27 -0600553 Output device tree binary filename ('u-boot.dtb' path)
Simon Glass57454f42016-11-25 20:15:52 -0700554 """
Simon Glass72232452016-11-25 20:15:53 -0700555 dtb_data = None
Simon Glass57454f42016-11-25 20:15:52 -0700556 # Use the compiled test file as the u-boot-dtb input
557 if use_real_dtb:
Simon Glass72232452016-11-25 20:15:53 -0700558 dtb_data = self._SetupDtb(fname)
Simon Glasse219aa42018-09-14 04:57:24 -0600559
560 # For testing purposes, make a copy of the DT for SPL and TPL. Add
Simon Glassd9e01d22024-07-20 11:49:40 +0100561 # a node indicating which it is, to aid verification.
Simon Glass56d05412022-02-28 07:16:54 -0700562 for name in ['spl', 'tpl', 'vpl']:
Simon Glasse219aa42018-09-14 04:57:24 -0600563 dtb_fname = '%s/u-boot-%s.dtb' % (name, name)
564 outfile = os.path.join(self._indir, dtb_fname)
565 TestFunctional._MakeInputFile(dtb_fname,
Simon Glass56d05412022-02-28 07:16:54 -0700566 self._GetDtbContentsForSpls(dtb_data, name))
Simon Glass57454f42016-11-25 20:15:52 -0700567
568 try:
Simon Glass91710b32018-07-17 13:25:32 -0600569 retcode = self._DoTestFile(fname, map=map, update_dtb=update_dtb,
Simon Glassa435cd12020-09-01 05:13:59 -0600570 entry_args=entry_args, use_real_dtb=use_real_dtb,
Simon Glass3eb30a42024-08-26 13:11:42 -0600571 use_expanded=use_expanded, verbosity=verbosity,
572 extra_indirs=extra_indirs,
Simon Glass76f496d2021-07-06 10:36:37 -0600573 threads=threads)
Simon Glass57454f42016-11-25 20:15:52 -0700574 self.assertEqual(0, retcode)
Simon Glass80025522022-01-29 14:14:04 -0700575 out_dtb_fname = tools.get_output_filename('u-boot.dtb.out')
Simon Glass57454f42016-11-25 20:15:52 -0700576
577 # Find the (only) image, read it and return its contents
578 image = control.images['image']
Simon Glass80025522022-01-29 14:14:04 -0700579 image_fname = tools.get_output_filename('image.bin')
Simon Glassa87014e2018-07-06 10:27:42 -0600580 self.assertTrue(os.path.exists(image_fname))
Simon Glass30732662018-06-01 09:38:20 -0600581 if map:
Simon Glass80025522022-01-29 14:14:04 -0700582 map_fname = tools.get_output_filename('image.map')
Simon Glass30732662018-06-01 09:38:20 -0600583 with open(map_fname) as fd:
584 map_data = fd.read()
585 else:
586 map_data = None
Simon Glass33486662019-05-14 15:53:42 -0600587 with open(image_fname, 'rb') as fd:
Simon Glassa87014e2018-07-06 10:27:42 -0600588 return fd.read(), dtb_data, map_data, out_dtb_fname
Simon Glass57454f42016-11-25 20:15:52 -0700589 finally:
590 # Put the test file back
Simon Glasse219aa42018-09-14 04:57:24 -0600591 if reset_dtbs and use_real_dtb:
Simon Glass8425a1f2018-07-17 13:25:48 -0600592 self._ResetDtbs()
Simon Glass57454f42016-11-25 20:15:52 -0700593
Simon Glass5b4bce32019-07-08 14:25:26 -0600594 def _DoReadFileRealDtb(self, fname):
595 """Run binman with a real .dtb file and return the resulting data
596
597 Args:
598 fname: DT source filename to use (e.g. 082_fdt_update_all.dts)
599
600 Returns:
601 Resulting image contents
602 """
603 return self._DoReadFileDtb(fname, use_real_dtb=True, update_dtb=True)[0]
604
Simon Glass72232452016-11-25 20:15:53 -0700605 def _DoReadFile(self, fname, use_real_dtb=False):
Simon Glass1e324002018-06-01 09:38:19 -0600606 """Helper function which discards the device-tree binary
607
608 Args:
Simon Glass511f6582018-10-01 12:22:30 -0600609 fname: Device-tree source filename to use (e.g. 005_simple.dts)
Simon Glass1e324002018-06-01 09:38:19 -0600610 use_real_dtb: True to use the test file as the contents of
611 the u-boot-dtb entry. Normally this is not needed and the
612 test contents (the U_BOOT_DTB_DATA string) can be used.
613 But in some test we need the real contents.
Simon Glassdef77b52018-07-17 13:25:27 -0600614
615 Returns:
616 Resulting image contents
Simon Glass1e324002018-06-01 09:38:19 -0600617 """
Simon Glass72232452016-11-25 20:15:53 -0700618 return self._DoReadFileDtb(fname, use_real_dtb)[0]
619
Simon Glass57454f42016-11-25 20:15:52 -0700620 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600621 def _MakeInputFile(cls, fname, contents):
Simon Glass57454f42016-11-25 20:15:52 -0700622 """Create a new test input file, creating directories as needed
623
624 Args:
Simon Glasse8561af2018-08-01 15:22:37 -0600625 fname: Filename to create
Simon Glass57454f42016-11-25 20:15:52 -0700626 contents: File contents to write in to the file
627 Returns:
628 Full pathname of file created
629 """
Simon Glass862f8e22019-08-24 07:22:43 -0600630 pathname = os.path.join(cls._indir, fname)
Simon Glass57454f42016-11-25 20:15:52 -0700631 dirname = os.path.dirname(pathname)
632 if dirname and not os.path.exists(dirname):
633 os.makedirs(dirname)
634 with open(pathname, 'wb') as fd:
635 fd.write(contents)
636 return pathname
637
638 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600639 def _MakeInputDir(cls, dirname):
Simon Glassc1ae83c2018-07-17 13:25:44 -0600640 """Create a new test input directory, creating directories as needed
641
642 Args:
643 dirname: Directory name to create
644
645 Returns:
646 Full pathname of directory created
647 """
Simon Glass862f8e22019-08-24 07:22:43 -0600648 pathname = os.path.join(cls._indir, dirname)
Simon Glassc1ae83c2018-07-17 13:25:44 -0600649 if not os.path.exists(pathname):
650 os.makedirs(pathname)
651 return pathname
652
653 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600654 def _SetupSplElf(cls, src_fname='bss_data'):
Simon Glass7057d022018-10-01 21:12:47 -0600655 """Set up an ELF file with a '_dt_ucode_base_size' symbol
656
657 Args:
658 Filename of ELF file to use as SPL
659 """
Simon Glass93a806f2019-08-24 07:22:59 -0600660 TestFunctional._MakeInputFile('spl/u-boot-spl',
Simon Glass80025522022-01-29 14:14:04 -0700661 tools.read_file(cls.ElfTestFile(src_fname)))
Simon Glass7057d022018-10-01 21:12:47 -0600662
663 @classmethod
Simon Glass3eb5b202019-08-24 07:23:00 -0600664 def _SetupTplElf(cls, src_fname='bss_data'):
665 """Set up an ELF file with a '_dt_ucode_base_size' symbol
666
667 Args:
668 Filename of ELF file to use as TPL
669 """
670 TestFunctional._MakeInputFile('tpl/u-boot-tpl',
Simon Glass80025522022-01-29 14:14:04 -0700671 tools.read_file(cls.ElfTestFile(src_fname)))
Simon Glass3eb5b202019-08-24 07:23:00 -0600672
673 @classmethod
Simon Glass56d05412022-02-28 07:16:54 -0700674 def _SetupVplElf(cls, src_fname='bss_data'):
675 """Set up an ELF file with a '_dt_ucode_base_size' symbol
676
677 Args:
678 Filename of ELF file to use as VPL
679 """
680 TestFunctional._MakeInputFile('vpl/u-boot-vpl',
681 tools.read_file(cls.ElfTestFile(src_fname)))
682
683 @classmethod
Lukas Funkee901faf2023-07-18 13:53:13 +0200684 def _SetupPmuFwlElf(cls, src_fname='bss_data'):
685 """Set up an ELF file with a '_dt_ucode_base_size' symbol
686
687 Args:
688 Filename of ELF file to use as VPL
689 """
690 TestFunctional._MakeInputFile('pmu-firmware.elf',
691 tools.read_file(cls.ElfTestFile(src_fname)))
692
693 @classmethod
Simon Glasse88cef92020-07-09 18:39:41 -0600694 def _SetupDescriptor(cls):
695 with open(cls.TestFile('descriptor.bin'), 'rb') as fd:
696 TestFunctional._MakeInputFile('descriptor.bin', fd.read())
697
698 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600699 def TestFile(cls, fname):
700 return os.path.join(cls._binman_dir, 'test', fname)
Simon Glass57454f42016-11-25 20:15:52 -0700701
Simon Glassf6290892019-08-24 07:22:53 -0600702 @classmethod
703 def ElfTestFile(cls, fname):
704 return os.path.join(cls._elf_testdir, fname)
705
Simon Glassad5cfe12023-01-07 14:07:14 -0700706 @classmethod
707 def make_tee_bin(cls, fname, paged_sz=0, extra_data=b''):
708 init_sz, start_hi, start_lo, dummy = (len(U_BOOT_DATA), 0, TEE_ADDR, 0)
709 data = b'OPTE\x01xxx' + struct.pack('<5I', init_sz, start_hi, start_lo,
710 dummy, paged_sz) + U_BOOT_DATA
711 data += extra_data
712 TestFunctional._MakeInputFile(fname, data)
713
Simon Glass57454f42016-11-25 20:15:52 -0700714 def AssertInList(self, grep_list, target):
715 """Assert that at least one of a list of things is in a target
716
717 Args:
718 grep_list: List of strings to check
719 target: Target string
720 """
721 for grep in grep_list:
722 if grep in target:
723 return
Simon Glass848cdb52019-05-17 22:00:50 -0600724 self.fail("Error: '%s' not found in '%s'" % (grep_list, target))
Simon Glass57454f42016-11-25 20:15:52 -0700725
726 def CheckNoGaps(self, entries):
727 """Check that all entries fit together without gaps
728
729 Args:
730 entries: List of entries to check
731 """
Simon Glasse8561af2018-08-01 15:22:37 -0600732 offset = 0
Simon Glass57454f42016-11-25 20:15:52 -0700733 for entry in entries.values():
Simon Glasse8561af2018-08-01 15:22:37 -0600734 self.assertEqual(offset, entry.offset)
735 offset += entry.size
Simon Glass57454f42016-11-25 20:15:52 -0700736
Simon Glass72232452016-11-25 20:15:53 -0700737 def GetFdtLen(self, dtb):
Simon Glass1e324002018-06-01 09:38:19 -0600738 """Get the totalsize field from a device-tree binary
Simon Glass72232452016-11-25 20:15:53 -0700739
740 Args:
Simon Glass1e324002018-06-01 09:38:19 -0600741 dtb: Device-tree binary contents
Simon Glass72232452016-11-25 20:15:53 -0700742
743 Returns:
Simon Glass1e324002018-06-01 09:38:19 -0600744 Total size of device-tree binary, from the header
Simon Glass72232452016-11-25 20:15:53 -0700745 """
746 return struct.unpack('>L', dtb[4:8])[0]
747
Simon Glass0f621332019-07-08 14:25:27 -0600748 def _GetPropTree(self, dtb, prop_names, prefix='/binman/'):
Simon Glassa87014e2018-07-06 10:27:42 -0600749 def AddNode(node, path):
750 if node.name != '/':
751 path += '/' + node.name
Simon Glass0f621332019-07-08 14:25:27 -0600752 for prop in node.props.values():
753 if prop.name in prop_names:
754 prop_path = path + ':' + prop.name
755 tree[prop_path[len(prefix):]] = fdt_util.fdt32_to_cpu(
756 prop.value)
Simon Glassa87014e2018-07-06 10:27:42 -0600757 for subnode in node.subnodes:
Simon Glassa87014e2018-07-06 10:27:42 -0600758 AddNode(subnode, path)
759
760 tree = {}
Simon Glassa87014e2018-07-06 10:27:42 -0600761 AddNode(dtb.GetRoot(), '')
762 return tree
763
Ivan Mikhaylov3cfcaa4d2023-03-08 01:13:40 +0000764 def _CheckSign(self, fit, key):
765 try:
766 tools.run('fit_check_sign', '-k', key, '-f', fit)
767 except:
768 self.fail('Expected signed FIT container')
769 return False
770 return True
771
Paul HENRYS5cf82892025-02-24 22:20:55 +0100772 def _CheckPreload(self, image, key, algo="sha256,rsa2048",
773 padding="pkcs-1.5"):
774 try:
775 tools.run('preload_check_sign', '-k', key, '-a', algo, '-p',
776 padding, '-f', image)
777 except:
778 self.fail('Expected image signed with a pre-load')
779 return False
780 return True
781
Simon Glass57454f42016-11-25 20:15:52 -0700782 def testRun(self):
783 """Test a basic run with valid args"""
784 result = self._RunBinman('-h')
785
786 def testFullHelp(self):
787 """Test that the full help is displayed with -H"""
788 result = self._RunBinman('-H')
Simon Glass75ead662021-03-18 20:25:13 +1300789 help_file = os.path.join(self._binman_dir, 'README.rst')
Tom Rinic3c0b6d2018-01-16 15:29:50 -0500790 # Remove possible extraneous strings
791 extra = '::::::::::::::\n' + help_file + '\n::::::::::::::\n'
792 gothelp = result.stdout.replace(extra, '')
793 self.assertEqual(len(gothelp), os.path.getsize(help_file))
Simon Glass57454f42016-11-25 20:15:52 -0700794 self.assertEqual(0, len(result.stderr))
795 self.assertEqual(0, result.return_code)
796
797 def testFullHelpInternal(self):
798 """Test that the full help is displayed with -H"""
799 try:
Simon Glass5dc22cf2025-02-03 09:26:42 -0700800 command.TEST_RESULT = command.CommandResult()
Simon Glass57454f42016-11-25 20:15:52 -0700801 result = self._DoBinman('-H')
Simon Glass75ead662021-03-18 20:25:13 +1300802 help_file = os.path.join(self._binman_dir, 'README.rst')
Simon Glass57454f42016-11-25 20:15:52 -0700803 finally:
Simon Glass5dc22cf2025-02-03 09:26:42 -0700804 command.TEST_RESULT = None
Simon Glass57454f42016-11-25 20:15:52 -0700805
806 def testHelp(self):
807 """Test that the basic help is displayed with -h"""
808 result = self._RunBinman('-h')
809 self.assertTrue(len(result.stdout) > 200)
810 self.assertEqual(0, len(result.stderr))
811 self.assertEqual(0, result.return_code)
812
Simon Glass57454f42016-11-25 20:15:52 -0700813 def testBoard(self):
814 """Test that we can run it with a specific board"""
Simon Glass511f6582018-10-01 12:22:30 -0600815 self._SetupDtb('005_simple.dts', 'sandbox/u-boot.dtb')
Simon Glass57454f42016-11-25 20:15:52 -0700816 TestFunctional._MakeInputFile('sandbox/u-boot.bin', U_BOOT_DATA)
Simon Glassed930672021-03-18 20:25:05 +1300817 result = self._DoBinman('build', '-n', '-b', 'sandbox')
Simon Glass57454f42016-11-25 20:15:52 -0700818 self.assertEqual(0, result)
819
820 def testNeedBoard(self):
821 """Test that we get an error when no board ius supplied"""
822 with self.assertRaises(ValueError) as e:
Simon Glassf46732a2019-07-08 14:25:29 -0600823 result = self._DoBinman('build')
Simon Glass57454f42016-11-25 20:15:52 -0700824 self.assertIn("Must provide a board to process (use -b <board>)",
825 str(e.exception))
826
827 def testMissingDt(self):
Simon Glass1e324002018-06-01 09:38:19 -0600828 """Test that an invalid device-tree file generates an error"""
Simon Glass57454f42016-11-25 20:15:52 -0700829 with self.assertRaises(Exception) as e:
Simon Glassf46732a2019-07-08 14:25:29 -0600830 self._RunBinman('build', '-d', 'missing_file')
Simon Glass57454f42016-11-25 20:15:52 -0700831 # We get one error from libfdt, and a different one from fdtget.
832 self.AssertInList(["Couldn't open blob from 'missing_file'",
833 'No such file or directory'], str(e.exception))
834
835 def testBrokenDt(self):
Simon Glass1e324002018-06-01 09:38:19 -0600836 """Test that an invalid device-tree source file generates an error
Simon Glass57454f42016-11-25 20:15:52 -0700837
838 Since this is a source file it should be compiled and the error
839 will come from the device-tree compiler (dtc).
840 """
841 with self.assertRaises(Exception) as e:
Simon Glassf46732a2019-07-08 14:25:29 -0600842 self._RunBinman('build', '-d', self.TestFile('001_invalid.dts'))
Simon Glass57454f42016-11-25 20:15:52 -0700843 self.assertIn("FATAL ERROR: Unable to parse input tree",
844 str(e.exception))
845
846 def testMissingNode(self):
847 """Test that a device tree without a 'binman' node generates an error"""
848 with self.assertRaises(Exception) as e:
Simon Glassf46732a2019-07-08 14:25:29 -0600849 self._DoBinman('build', '-d', self.TestFile('002_missing_node.dts'))
Simon Glass57454f42016-11-25 20:15:52 -0700850 self.assertIn("does not have a 'binman' node", str(e.exception))
851
852 def testEmpty(self):
853 """Test that an empty binman node works OK (i.e. does nothing)"""
Simon Glassf46732a2019-07-08 14:25:29 -0600854 result = self._RunBinman('build', '-d', self.TestFile('003_empty.dts'))
Simon Glass57454f42016-11-25 20:15:52 -0700855 self.assertEqual(0, len(result.stderr))
856 self.assertEqual(0, result.return_code)
857
858 def testInvalidEntry(self):
859 """Test that an invalid entry is flagged"""
860 with self.assertRaises(Exception) as e:
Simon Glassf46732a2019-07-08 14:25:29 -0600861 result = self._RunBinman('build', '-d',
Simon Glass511f6582018-10-01 12:22:30 -0600862 self.TestFile('004_invalid_entry.dts'))
Simon Glass57454f42016-11-25 20:15:52 -0700863 self.assertIn("Unknown entry type 'not-a-valid-type' in node "
864 "'/binman/not-a-valid-type'", str(e.exception))
865
866 def testSimple(self):
867 """Test a simple binman with a single file"""
Simon Glass511f6582018-10-01 12:22:30 -0600868 data = self._DoReadFile('005_simple.dts')
Simon Glass57454f42016-11-25 20:15:52 -0700869 self.assertEqual(U_BOOT_DATA, data)
870
Simon Glass075a45c2017-11-13 18:55:00 -0700871 def testSimpleDebug(self):
872 """Test a simple binman run with debugging enabled"""
Simon Glass52d06212019-07-08 14:25:53 -0600873 self._DoTestFile('005_simple.dts', debug=True)
Simon Glass075a45c2017-11-13 18:55:00 -0700874
Simon Glass57454f42016-11-25 20:15:52 -0700875 def testDual(self):
876 """Test that we can handle creating two images
877
878 This also tests image padding.
879 """
Simon Glass511f6582018-10-01 12:22:30 -0600880 retcode = self._DoTestFile('006_dual_image.dts')
Simon Glass57454f42016-11-25 20:15:52 -0700881 self.assertEqual(0, retcode)
882
883 image = control.images['image1']
Simon Glass39dd2152019-07-08 14:25:47 -0600884 self.assertEqual(len(U_BOOT_DATA), image.size)
Simon Glass80025522022-01-29 14:14:04 -0700885 fname = tools.get_output_filename('image1.bin')
Simon Glass57454f42016-11-25 20:15:52 -0700886 self.assertTrue(os.path.exists(fname))
Simon Glass33486662019-05-14 15:53:42 -0600887 with open(fname, 'rb') as fd:
Simon Glass57454f42016-11-25 20:15:52 -0700888 data = fd.read()
889 self.assertEqual(U_BOOT_DATA, data)
890
891 image = control.images['image2']
Simon Glass39dd2152019-07-08 14:25:47 -0600892 self.assertEqual(3 + len(U_BOOT_DATA) + 5, image.size)
Simon Glass80025522022-01-29 14:14:04 -0700893 fname = tools.get_output_filename('image2.bin')
Simon Glass57454f42016-11-25 20:15:52 -0700894 self.assertTrue(os.path.exists(fname))
Simon Glass33486662019-05-14 15:53:42 -0600895 with open(fname, 'rb') as fd:
Simon Glass57454f42016-11-25 20:15:52 -0700896 data = fd.read()
897 self.assertEqual(U_BOOT_DATA, data[3:7])
Simon Glass80025522022-01-29 14:14:04 -0700898 self.assertEqual(tools.get_bytes(0, 3), data[:3])
899 self.assertEqual(tools.get_bytes(0, 5), data[7:])
Simon Glass57454f42016-11-25 20:15:52 -0700900
901 def testBadAlign(self):
902 """Test that an invalid alignment value is detected"""
903 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -0600904 self._DoTestFile('007_bad_align.dts')
Simon Glass57454f42016-11-25 20:15:52 -0700905 self.assertIn("Node '/binman/u-boot': Alignment 23 must be a power "
906 "of two", str(e.exception))
907
908 def testPackSimple(self):
909 """Test that packing works as expected"""
Simon Glass511f6582018-10-01 12:22:30 -0600910 retcode = self._DoTestFile('008_pack.dts')
Simon Glass57454f42016-11-25 20:15:52 -0700911 self.assertEqual(0, retcode)
912 self.assertIn('image', control.images)
913 image = control.images['image']
Simon Glasseca32212018-06-01 09:38:12 -0600914 entries = image.GetEntries()
Simon Glass57454f42016-11-25 20:15:52 -0700915 self.assertEqual(5, len(entries))
916
917 # First u-boot
918 self.assertIn('u-boot', entries)
919 entry = entries['u-boot']
Simon Glasse8561af2018-08-01 15:22:37 -0600920 self.assertEqual(0, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700921 self.assertEqual(len(U_BOOT_DATA), entry.size)
922
923 # Second u-boot, aligned to 16-byte boundary
924 self.assertIn('u-boot-align', entries)
925 entry = entries['u-boot-align']
Simon Glasse8561af2018-08-01 15:22:37 -0600926 self.assertEqual(16, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700927 self.assertEqual(len(U_BOOT_DATA), entry.size)
928
929 # Third u-boot, size 23 bytes
930 self.assertIn('u-boot-size', entries)
931 entry = entries['u-boot-size']
Simon Glasse8561af2018-08-01 15:22:37 -0600932 self.assertEqual(20, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700933 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
934 self.assertEqual(23, entry.size)
935
936 # Fourth u-boot, placed immediate after the above
937 self.assertIn('u-boot-next', entries)
938 entry = entries['u-boot-next']
Simon Glasse8561af2018-08-01 15:22:37 -0600939 self.assertEqual(43, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700940 self.assertEqual(len(U_BOOT_DATA), entry.size)
941
Simon Glasse8561af2018-08-01 15:22:37 -0600942 # Fifth u-boot, placed at a fixed offset
Simon Glass57454f42016-11-25 20:15:52 -0700943 self.assertIn('u-boot-fixed', entries)
944 entry = entries['u-boot-fixed']
Simon Glasse8561af2018-08-01 15:22:37 -0600945 self.assertEqual(61, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700946 self.assertEqual(len(U_BOOT_DATA), entry.size)
947
Simon Glass39dd2152019-07-08 14:25:47 -0600948 self.assertEqual(65, image.size)
Simon Glass57454f42016-11-25 20:15:52 -0700949
950 def testPackExtra(self):
951 """Test that extra packing feature works as expected"""
Simon Glassafb9caa2020-10-26 17:40:10 -0600952 data, _, _, out_dtb_fname = self._DoReadFileDtb('009_pack_extra.dts',
953 update_dtb=True)
Simon Glass57454f42016-11-25 20:15:52 -0700954
Simon Glass57454f42016-11-25 20:15:52 -0700955 self.assertIn('image', control.images)
956 image = control.images['image']
Simon Glasseca32212018-06-01 09:38:12 -0600957 entries = image.GetEntries()
Samuel Hollande2574022023-01-21 17:25:16 -0600958 self.assertEqual(6, len(entries))
Simon Glass57454f42016-11-25 20:15:52 -0700959
Samuel Hollande2574022023-01-21 17:25:16 -0600960 # First u-boot with padding before and after (included in minimum size)
Simon Glass57454f42016-11-25 20:15:52 -0700961 self.assertIn('u-boot', entries)
962 entry = entries['u-boot']
Simon Glasse8561af2018-08-01 15:22:37 -0600963 self.assertEqual(0, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700964 self.assertEqual(3, entry.pad_before)
965 self.assertEqual(3 + 5 + len(U_BOOT_DATA), entry.size)
Simon Glass187202f2020-10-26 17:40:08 -0600966 self.assertEqual(U_BOOT_DATA, entry.data)
Simon Glass80025522022-01-29 14:14:04 -0700967 self.assertEqual(tools.get_bytes(0, 3) + U_BOOT_DATA +
968 tools.get_bytes(0, 5), data[:entry.size])
Simon Glass187202f2020-10-26 17:40:08 -0600969 pos = entry.size
Simon Glass57454f42016-11-25 20:15:52 -0700970
971 # Second u-boot has an aligned size, but it has no effect
972 self.assertIn('u-boot-align-size-nop', entries)
973 entry = entries['u-boot-align-size-nop']
Simon Glass187202f2020-10-26 17:40:08 -0600974 self.assertEqual(pos, entry.offset)
975 self.assertEqual(len(U_BOOT_DATA), entry.size)
976 self.assertEqual(U_BOOT_DATA, entry.data)
977 self.assertEqual(U_BOOT_DATA, data[pos:pos + entry.size])
978 pos += entry.size
Simon Glass57454f42016-11-25 20:15:52 -0700979
980 # Third u-boot has an aligned size too
981 self.assertIn('u-boot-align-size', entries)
982 entry = entries['u-boot-align-size']
Simon Glass187202f2020-10-26 17:40:08 -0600983 self.assertEqual(pos, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700984 self.assertEqual(32, entry.size)
Simon Glass187202f2020-10-26 17:40:08 -0600985 self.assertEqual(U_BOOT_DATA, entry.data)
Simon Glass80025522022-01-29 14:14:04 -0700986 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 32 - len(U_BOOT_DATA)),
Simon Glass187202f2020-10-26 17:40:08 -0600987 data[pos:pos + entry.size])
988 pos += entry.size
Simon Glass57454f42016-11-25 20:15:52 -0700989
990 # Fourth u-boot has an aligned end
991 self.assertIn('u-boot-align-end', entries)
992 entry = entries['u-boot-align-end']
Simon Glasse8561af2018-08-01 15:22:37 -0600993 self.assertEqual(48, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700994 self.assertEqual(16, entry.size)
Simon Glass187202f2020-10-26 17:40:08 -0600995 self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)])
Simon Glass80025522022-01-29 14:14:04 -0700996 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 16 - len(U_BOOT_DATA)),
Simon Glass187202f2020-10-26 17:40:08 -0600997 data[pos:pos + entry.size])
998 pos += entry.size
Simon Glass57454f42016-11-25 20:15:52 -0700999
1000 # Fifth u-boot immediately afterwards
1001 self.assertIn('u-boot-align-both', entries)
1002 entry = entries['u-boot-align-both']
Simon Glasse8561af2018-08-01 15:22:37 -06001003 self.assertEqual(64, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -07001004 self.assertEqual(64, entry.size)
Simon Glass187202f2020-10-26 17:40:08 -06001005 self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)])
Simon Glass80025522022-01-29 14:14:04 -07001006 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 64 - len(U_BOOT_DATA)),
Simon Glass187202f2020-10-26 17:40:08 -06001007 data[pos:pos + entry.size])
Simon Glass57454f42016-11-25 20:15:52 -07001008
Samuel Hollande2574022023-01-21 17:25:16 -06001009 # Sixth u-boot with both minimum size and aligned size
1010 self.assertIn('u-boot-min-size', entries)
1011 entry = entries['u-boot-min-size']
1012 self.assertEqual(128, entry.offset)
1013 self.assertEqual(32, entry.size)
1014 self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)])
1015 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 32 - len(U_BOOT_DATA)),
1016 data[pos:pos + entry.size])
1017
Simon Glass57454f42016-11-25 20:15:52 -07001018 self.CheckNoGaps(entries)
Samuel Hollande2574022023-01-21 17:25:16 -06001019 self.assertEqual(160, image.size)
Simon Glass57454f42016-11-25 20:15:52 -07001020
Simon Glassafb9caa2020-10-26 17:40:10 -06001021 dtb = fdt.Fdt(out_dtb_fname)
1022 dtb.Scan()
1023 props = self._GetPropTree(dtb, ['size', 'offset', 'image-pos'])
1024 expected = {
1025 'image-pos': 0,
1026 'offset': 0,
Samuel Hollande2574022023-01-21 17:25:16 -06001027 'size': 160,
Simon Glassafb9caa2020-10-26 17:40:10 -06001028
1029 'u-boot:image-pos': 0,
1030 'u-boot:offset': 0,
1031 'u-boot:size': 3 + 5 + len(U_BOOT_DATA),
1032
1033 'u-boot-align-size-nop:image-pos': 12,
1034 'u-boot-align-size-nop:offset': 12,
1035 'u-boot-align-size-nop:size': 4,
1036
1037 'u-boot-align-size:image-pos': 16,
1038 'u-boot-align-size:offset': 16,
1039 'u-boot-align-size:size': 32,
1040
1041 'u-boot-align-end:image-pos': 48,
1042 'u-boot-align-end:offset': 48,
1043 'u-boot-align-end:size': 16,
1044
1045 'u-boot-align-both:image-pos': 64,
1046 'u-boot-align-both:offset': 64,
1047 'u-boot-align-both:size': 64,
Samuel Hollande2574022023-01-21 17:25:16 -06001048
1049 'u-boot-min-size:image-pos': 128,
1050 'u-boot-min-size:offset': 128,
1051 'u-boot-min-size:size': 32,
Simon Glassafb9caa2020-10-26 17:40:10 -06001052 }
1053 self.assertEqual(expected, props)
1054
Simon Glass57454f42016-11-25 20:15:52 -07001055 def testPackAlignPowerOf2(self):
1056 """Test that invalid entry alignment is detected"""
1057 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001058 self._DoTestFile('010_pack_align_power2.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001059 self.assertIn("Node '/binman/u-boot': Alignment 5 must be a power "
1060 "of two", str(e.exception))
1061
1062 def testPackAlignSizePowerOf2(self):
1063 """Test that invalid entry size alignment is detected"""
1064 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001065 self._DoTestFile('011_pack_align_size_power2.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001066 self.assertIn("Node '/binman/u-boot': Alignment size 55 must be a "
1067 "power of two", str(e.exception))
1068
1069 def testPackInvalidAlign(self):
Simon Glasse8561af2018-08-01 15:22:37 -06001070 """Test detection of an offset that does not match its alignment"""
Simon Glass57454f42016-11-25 20:15:52 -07001071 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001072 self._DoTestFile('012_pack_inv_align.dts')
Simon Glasse8561af2018-08-01 15:22:37 -06001073 self.assertIn("Node '/binman/u-boot': Offset 0x5 (5) does not match "
Simon Glass57454f42016-11-25 20:15:52 -07001074 "align 0x4 (4)", str(e.exception))
1075
1076 def testPackInvalidSizeAlign(self):
1077 """Test that invalid entry size alignment is detected"""
1078 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001079 self._DoTestFile('013_pack_inv_size_align.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001080 self.assertIn("Node '/binman/u-boot': Size 0x5 (5) does not match "
1081 "align-size 0x4 (4)", str(e.exception))
1082
1083 def testPackOverlap(self):
1084 """Test that overlapping regions are detected"""
1085 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001086 self._DoTestFile('014_pack_overlap.dts')
Simon Glasse8561af2018-08-01 15:22:37 -06001087 self.assertIn("Node '/binman/u-boot-align': Offset 0x3 (3) overlaps "
Simon Glass57454f42016-11-25 20:15:52 -07001088 "with previous entry '/binman/u-boot' ending at 0x4 (4)",
1089 str(e.exception))
1090
1091 def testPackEntryOverflow(self):
1092 """Test that entries that overflow their size are detected"""
1093 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001094 self._DoTestFile('015_pack_overflow.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001095 self.assertIn("Node '/binman/u-boot': Entry contents size is 0x4 (4) "
1096 "but entry size is 0x3 (3)", str(e.exception))
1097
1098 def testPackImageOverflow(self):
1099 """Test that entries which overflow the image size are detected"""
1100 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001101 self._DoTestFile('016_pack_image_overflow.dts')
Simon Glasseca32212018-06-01 09:38:12 -06001102 self.assertIn("Section '/binman': contents size 0x4 (4) exceeds section "
Simon Glass57454f42016-11-25 20:15:52 -07001103 "size 0x3 (3)", str(e.exception))
1104
1105 def testPackImageSize(self):
1106 """Test that the image size can be set"""
Simon Glass511f6582018-10-01 12:22:30 -06001107 retcode = self._DoTestFile('017_pack_image_size.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001108 self.assertEqual(0, retcode)
1109 self.assertIn('image', control.images)
1110 image = control.images['image']
Simon Glass39dd2152019-07-08 14:25:47 -06001111 self.assertEqual(7, image.size)
Simon Glass57454f42016-11-25 20:15:52 -07001112
1113 def testPackImageSizeAlign(self):
1114 """Test that image size alignemnt works as expected"""
Simon Glass511f6582018-10-01 12:22:30 -06001115 retcode = self._DoTestFile('018_pack_image_align.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001116 self.assertEqual(0, retcode)
1117 self.assertIn('image', control.images)
1118 image = control.images['image']
Simon Glass39dd2152019-07-08 14:25:47 -06001119 self.assertEqual(16, image.size)
Simon Glass57454f42016-11-25 20:15:52 -07001120
1121 def testPackInvalidImageAlign(self):
1122 """Test that invalid image alignment is detected"""
1123 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001124 self._DoTestFile('019_pack_inv_image_align.dts')
Simon Glasseca32212018-06-01 09:38:12 -06001125 self.assertIn("Section '/binman': Size 0x7 (7) does not match "
Simon Glass57454f42016-11-25 20:15:52 -07001126 "align-size 0x8 (8)", str(e.exception))
1127
Simon Glass2a0fa982022-02-11 13:23:21 -07001128 def testPackAlignPowerOf2Inv(self):
Simon Glass57454f42016-11-25 20:15:52 -07001129 """Test that invalid image alignment is detected"""
1130 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001131 self._DoTestFile('020_pack_inv_image_align_power2.dts')
Simon Glass39dd2152019-07-08 14:25:47 -06001132 self.assertIn("Image '/binman': Alignment size 131 must be a power of "
Simon Glass57454f42016-11-25 20:15:52 -07001133 "two", str(e.exception))
1134
1135 def testImagePadByte(self):
1136 """Test that the image pad byte can be specified"""
Simon Glass7057d022018-10-01 21:12:47 -06001137 self._SetupSplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001138 data = self._DoReadFile('021_image_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07001139 self.assertEqual(U_BOOT_SPL_DATA + tools.get_bytes(0xff, 1) +
Simon Glassac0d4952019-05-14 15:53:47 -06001140 U_BOOT_DATA, data)
Simon Glass57454f42016-11-25 20:15:52 -07001141
1142 def testImageName(self):
1143 """Test that image files can be named"""
Simon Glass511f6582018-10-01 12:22:30 -06001144 retcode = self._DoTestFile('022_image_name.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001145 self.assertEqual(0, retcode)
1146 image = control.images['image1']
Simon Glass80025522022-01-29 14:14:04 -07001147 fname = tools.get_output_filename('test-name')
Simon Glass57454f42016-11-25 20:15:52 -07001148 self.assertTrue(os.path.exists(fname))
1149
1150 image = control.images['image2']
Simon Glass80025522022-01-29 14:14:04 -07001151 fname = tools.get_output_filename('test-name.xx')
Simon Glass57454f42016-11-25 20:15:52 -07001152 self.assertTrue(os.path.exists(fname))
1153
1154 def testBlobFilename(self):
1155 """Test that generic blobs can be provided by filename"""
Simon Glass511f6582018-10-01 12:22:30 -06001156 data = self._DoReadFile('023_blob.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001157 self.assertEqual(BLOB_DATA, data)
1158
1159 def testPackSorted(self):
1160 """Test that entries can be sorted"""
Simon Glass7057d022018-10-01 21:12:47 -06001161 self._SetupSplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001162 data = self._DoReadFile('024_sorted.dts')
Simon Glass80025522022-01-29 14:14:04 -07001163 self.assertEqual(tools.get_bytes(0, 1) + U_BOOT_SPL_DATA +
1164 tools.get_bytes(0, 2) + U_BOOT_DATA, data)
Simon Glass57454f42016-11-25 20:15:52 -07001165
Simon Glasse8561af2018-08-01 15:22:37 -06001166 def testPackZeroOffset(self):
1167 """Test that an entry at offset 0 is not given a new offset"""
Marek Vasutf7413f02023-07-18 07:23:58 -06001168 self._SetupSplElf()
Simon Glass57454f42016-11-25 20:15:52 -07001169 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001170 self._DoTestFile('025_pack_zero_size.dts')
Simon Glasse8561af2018-08-01 15:22:37 -06001171 self.assertIn("Node '/binman/u-boot-spl': Offset 0x0 (0) overlaps "
Simon Glass57454f42016-11-25 20:15:52 -07001172 "with previous entry '/binman/u-boot' ending at 0x4 (4)",
1173 str(e.exception))
1174
1175 def testPackUbootDtb(self):
1176 """Test that a device tree can be added to U-Boot"""
Simon Glass511f6582018-10-01 12:22:30 -06001177 data = self._DoReadFile('026_pack_u_boot_dtb.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001178 self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA, data)
Simon Glass72232452016-11-25 20:15:53 -07001179
1180 def testPackX86RomNoSize(self):
1181 """Test that the end-at-4gb property requires a size property"""
Marek Vasutf7413f02023-07-18 07:23:58 -06001182 self._SetupSplElf()
Simon Glass72232452016-11-25 20:15:53 -07001183 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001184 self._DoTestFile('027_pack_4gb_no_size.dts')
Simon Glass39dd2152019-07-08 14:25:47 -06001185 self.assertIn("Image '/binman': Section size must be provided when "
Simon Glass72232452016-11-25 20:15:53 -07001186 "using end-at-4gb", str(e.exception))
1187
Jagdish Gediya0fb978c2018-09-03 21:35:07 +05301188 def test4gbAndSkipAtStartTogether(self):
1189 """Test that the end-at-4gb and skip-at-size property can't be used
1190 together"""
Marek Vasutf7413f02023-07-18 07:23:58 -06001191 self._SetupSplElf()
Jagdish Gediya0fb978c2018-09-03 21:35:07 +05301192 with self.assertRaises(ValueError) as e:
Simon Glass11f2bd02019-08-24 07:23:02 -06001193 self._DoTestFile('098_4gb_and_skip_at_start_together.dts')
Simon Glass39dd2152019-07-08 14:25:47 -06001194 self.assertIn("Image '/binman': Provide either 'end-at-4gb' or "
Jagdish Gediya0fb978c2018-09-03 21:35:07 +05301195 "'skip-at-start'", str(e.exception))
1196
Simon Glass72232452016-11-25 20:15:53 -07001197 def testPackX86RomOutside(self):
Simon Glasse8561af2018-08-01 15:22:37 -06001198 """Test that the end-at-4gb property checks for offset boundaries"""
Marek Vasutf7413f02023-07-18 07:23:58 -06001199 self._SetupSplElf()
Simon Glass72232452016-11-25 20:15:53 -07001200 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001201 self._DoTestFile('028_pack_4gb_outside.dts')
Simon Glassd6179862020-10-26 17:40:05 -06001202 self.assertIn("Node '/binman/u-boot': Offset 0x0 (0) size 0x4 (4) "
1203 "is outside the section '/binman' starting at "
1204 '0xffffffe0 (4294967264) of size 0x20 (32)',
Simon Glass72232452016-11-25 20:15:53 -07001205 str(e.exception))
1206
1207 def testPackX86Rom(self):
1208 """Test that a basic x86 ROM can be created"""
Simon Glass7057d022018-10-01 21:12:47 -06001209 self._SetupSplElf()
Simon Glass1d167762019-08-24 07:23:01 -06001210 data = self._DoReadFile('029_x86_rom.dts')
Simon Glass80025522022-01-29 14:14:04 -07001211 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 3) + U_BOOT_SPL_DATA +
1212 tools.get_bytes(0, 2), data)
Simon Glass72232452016-11-25 20:15:53 -07001213
1214 def testPackX86RomMeNoDesc(self):
1215 """Test that an invalid Intel descriptor entry is detected"""
Simon Glasse88cef92020-07-09 18:39:41 -06001216 try:
Simon Glass14c596c2020-07-25 15:11:19 -06001217 TestFunctional._MakeInputFile('descriptor-empty.bin', b'')
Simon Glasse88cef92020-07-09 18:39:41 -06001218 with self.assertRaises(ValueError) as e:
Simon Glass14c596c2020-07-25 15:11:19 -06001219 self._DoTestFile('163_x86_rom_me_empty.dts')
Simon Glasse88cef92020-07-09 18:39:41 -06001220 self.assertIn("Node '/binman/intel-descriptor': Cannot find Intel Flash Descriptor (FD) signature",
1221 str(e.exception))
1222 finally:
1223 self._SetupDescriptor()
Simon Glass72232452016-11-25 20:15:53 -07001224
1225 def testPackX86RomBadDesc(self):
1226 """Test that the Intel requires a descriptor entry"""
1227 with self.assertRaises(ValueError) as e:
Simon Glass1d167762019-08-24 07:23:01 -06001228 self._DoTestFile('030_x86_rom_me_no_desc.dts')
Simon Glasse8561af2018-08-01 15:22:37 -06001229 self.assertIn("Node '/binman/intel-me': No offset set with "
1230 "offset-unset: should another entry provide this correct "
1231 "offset?", str(e.exception))
Simon Glass72232452016-11-25 20:15:53 -07001232
1233 def testPackX86RomMe(self):
1234 """Test that an x86 ROM with an ME region can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001235 data = self._DoReadFile('031_x86_rom_me.dts')
Simon Glass80025522022-01-29 14:14:04 -07001236 expected_desc = tools.read_file(self.TestFile('descriptor.bin'))
Simon Glass759af872019-07-08 13:18:54 -06001237 if data[:0x1000] != expected_desc:
1238 self.fail('Expected descriptor binary at start of image')
Simon Glass72232452016-11-25 20:15:53 -07001239 self.assertEqual(ME_DATA, data[0x1000:0x1000 + len(ME_DATA)])
1240
1241 def testPackVga(self):
1242 """Test that an image with a VGA binary can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001243 data = self._DoReadFile('032_intel_vga.dts')
Simon Glass72232452016-11-25 20:15:53 -07001244 self.assertEqual(VGA_DATA, data[:len(VGA_DATA)])
1245
1246 def testPackStart16(self):
1247 """Test that an image with an x86 start16 region can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001248 data = self._DoReadFile('033_x86_start16.dts')
Simon Glass72232452016-11-25 20:15:53 -07001249 self.assertEqual(X86_START16_DATA, data[:len(X86_START16_DATA)])
1250
Jagdish Gediya311d4842018-09-03 21:35:08 +05301251 def testPackPowerpcMpc85xxBootpgResetvec(self):
1252 """Test that an image with powerpc-mpc85xx-bootpg-resetvec can be
1253 created"""
Simon Glass11f2bd02019-08-24 07:23:02 -06001254 data = self._DoReadFile('150_powerpc_mpc85xx_bootpg_resetvec.dts')
Jagdish Gediya311d4842018-09-03 21:35:08 +05301255 self.assertEqual(PPC_MPC85XX_BR_DATA, data[:len(PPC_MPC85XX_BR_DATA)])
1256
Simon Glass6ba679c2018-07-06 10:27:17 -06001257 def _RunMicrocodeTest(self, dts_fname, nodtb_data, ucode_second=False):
Simon Glass820af1d2018-07-06 10:27:16 -06001258 """Handle running a test for insertion of microcode
1259
1260 Args:
1261 dts_fname: Name of test .dts file
1262 nodtb_data: Data that we expect in the first section
Simon Glass6ba679c2018-07-06 10:27:17 -06001263 ucode_second: True if the microsecond entry is second instead of
1264 third
Simon Glass820af1d2018-07-06 10:27:16 -06001265
1266 Returns:
1267 Tuple:
1268 Contents of first region (U-Boot or SPL)
Simon Glasse8561af2018-08-01 15:22:37 -06001269 Offset and size components of microcode pointer, as inserted
Simon Glass820af1d2018-07-06 10:27:16 -06001270 in the above (two 4-byte words)
1271 """
Simon Glass3d274232017-11-12 21:52:27 -07001272 data = self._DoReadFile(dts_fname, True)
Simon Glass72232452016-11-25 20:15:53 -07001273
1274 # Now check the device tree has no microcode
Simon Glass6ba679c2018-07-06 10:27:17 -06001275 if ucode_second:
1276 ucode_content = data[len(nodtb_data):]
1277 ucode_pos = len(nodtb_data)
1278 dtb_with_ucode = ucode_content[16:]
1279 fdt_len = self.GetFdtLen(dtb_with_ucode)
1280 else:
1281 dtb_with_ucode = data[len(nodtb_data):]
1282 fdt_len = self.GetFdtLen(dtb_with_ucode)
1283 ucode_content = dtb_with_ucode[fdt_len:]
1284 ucode_pos = len(nodtb_data) + fdt_len
Simon Glass80025522022-01-29 14:14:04 -07001285 fname = tools.get_output_filename('test.dtb')
Simon Glass72232452016-11-25 20:15:53 -07001286 with open(fname, 'wb') as fd:
Simon Glass820af1d2018-07-06 10:27:16 -06001287 fd.write(dtb_with_ucode)
Simon Glass22c92ca2017-05-27 07:38:29 -06001288 dtb = fdt.FdtScan(fname)
1289 ucode = dtb.GetNode('/microcode')
Simon Glass72232452016-11-25 20:15:53 -07001290 self.assertTrue(ucode)
1291 for node in ucode.subnodes:
1292 self.assertFalse(node.props.get('data'))
1293
Simon Glass72232452016-11-25 20:15:53 -07001294 # Check that the microcode appears immediately after the Fdt
1295 # This matches the concatenation of the data properties in
Simon Glasse83679d2017-11-12 21:52:26 -07001296 # the /microcode/update@xxx nodes in 34_x86_ucode.dts.
Simon Glass72232452016-11-25 20:15:53 -07001297 ucode_data = struct.pack('>4L', 0x12345678, 0x12345679, 0xabcd0000,
1298 0x78235609)
Simon Glass820af1d2018-07-06 10:27:16 -06001299 self.assertEqual(ucode_data, ucode_content[:len(ucode_data)])
Simon Glass72232452016-11-25 20:15:53 -07001300
1301 # Check that the microcode pointer was inserted. It should match the
Simon Glasse8561af2018-08-01 15:22:37 -06001302 # expected offset and size
Simon Glass72232452016-11-25 20:15:53 -07001303 pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
1304 len(ucode_data))
Simon Glass6ba679c2018-07-06 10:27:17 -06001305 u_boot = data[:len(nodtb_data)]
1306 return u_boot, pos_and_size
Simon Glass3d274232017-11-12 21:52:27 -07001307
1308 def testPackUbootMicrocode(self):
1309 """Test that x86 microcode can be handled correctly
1310
1311 We expect to see the following in the image, in order:
1312 u-boot-nodtb.bin with a microcode pointer inserted at the correct
1313 place
1314 u-boot.dtb with the microcode removed
1315 the microcode
1316 """
Simon Glass511f6582018-10-01 12:22:30 -06001317 first, pos_and_size = self._RunMicrocodeTest('034_x86_ucode.dts',
Simon Glass3d274232017-11-12 21:52:27 -07001318 U_BOOT_NODTB_DATA)
Simon Glass303f62f2019-05-17 22:00:46 -06001319 self.assertEqual(b'nodtb with microcode' + pos_and_size +
1320 b' somewhere in here', first)
Simon Glass72232452016-11-25 20:15:53 -07001321
Simon Glassbac25c82017-05-27 07:38:26 -06001322 def _RunPackUbootSingleMicrocode(self):
Simon Glass72232452016-11-25 20:15:53 -07001323 """Test that x86 microcode can be handled correctly
1324
1325 We expect to see the following in the image, in order:
1326 u-boot-nodtb.bin with a microcode pointer inserted at the correct
1327 place
1328 u-boot.dtb with the microcode
1329 an empty microcode region
1330 """
1331 # We need the libfdt library to run this test since only that allows
1332 # finding the offset of a property. This is required by
1333 # Entry_u_boot_dtb_with_ucode.ObtainContents().
Simon Glass511f6582018-10-01 12:22:30 -06001334 data = self._DoReadFile('035_x86_single_ucode.dts', True)
Simon Glass72232452016-11-25 20:15:53 -07001335
1336 second = data[len(U_BOOT_NODTB_DATA):]
1337
1338 fdt_len = self.GetFdtLen(second)
1339 third = second[fdt_len:]
1340 second = second[:fdt_len]
1341
Simon Glassbac25c82017-05-27 07:38:26 -06001342 ucode_data = struct.pack('>2L', 0x12345678, 0x12345679)
1343 self.assertIn(ucode_data, second)
1344 ucode_pos = second.find(ucode_data) + len(U_BOOT_NODTB_DATA)
Simon Glass72232452016-11-25 20:15:53 -07001345
Simon Glassbac25c82017-05-27 07:38:26 -06001346 # Check that the microcode pointer was inserted. It should match the
Simon Glasse8561af2018-08-01 15:22:37 -06001347 # expected offset and size
Simon Glassbac25c82017-05-27 07:38:26 -06001348 pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
1349 len(ucode_data))
1350 first = data[:len(U_BOOT_NODTB_DATA)]
Simon Glass303f62f2019-05-17 22:00:46 -06001351 self.assertEqual(b'nodtb with microcode' + pos_and_size +
1352 b' somewhere in here', first)
Simon Glass996021e2016-11-25 20:15:54 -07001353
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001354 def testPackUbootSingleMicrocode(self):
1355 """Test that x86 microcode can be handled correctly with fdt_normal.
1356 """
Simon Glassbac25c82017-05-27 07:38:26 -06001357 self._RunPackUbootSingleMicrocode()
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001358
Simon Glass996021e2016-11-25 20:15:54 -07001359 def testUBootImg(self):
1360 """Test that u-boot.img can be put in a file"""
Simon Glass511f6582018-10-01 12:22:30 -06001361 data = self._DoReadFile('036_u_boot_img.dts')
Simon Glass996021e2016-11-25 20:15:54 -07001362 self.assertEqual(U_BOOT_IMG_DATA, data)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001363
1364 def testNoMicrocode(self):
1365 """Test that a missing microcode region is detected"""
1366 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001367 self._DoReadFile('037_x86_no_ucode.dts', True)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001368 self.assertIn("Node '/binman/u-boot-dtb-with-ucode': No /microcode "
1369 "node found in ", str(e.exception))
1370
1371 def testMicrocodeWithoutNode(self):
1372 """Test that a missing u-boot-dtb-with-ucode node is detected"""
1373 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001374 self._DoReadFile('038_x86_ucode_missing_node.dts', True)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001375 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
1376 "microcode region u-boot-dtb-with-ucode", str(e.exception))
1377
1378 def testMicrocodeWithoutNode2(self):
1379 """Test that a missing u-boot-ucode node is detected"""
1380 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001381 self._DoReadFile('039_x86_ucode_missing_node2.dts', True)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001382 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
1383 "microcode region u-boot-ucode", str(e.exception))
1384
1385 def testMicrocodeWithoutPtrInElf(self):
1386 """Test that a U-Boot binary without the microcode symbol is detected"""
1387 # ELF file without a '_dt_ucode_base_size' symbol
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001388 try:
Simon Glassfaaaa162019-08-24 07:22:55 -06001389 TestFunctional._MakeInputFile('u-boot',
Simon Glass80025522022-01-29 14:14:04 -07001390 tools.read_file(self.ElfTestFile('u_boot_no_ucode_ptr')))
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001391
1392 with self.assertRaises(ValueError) as e:
Simon Glassbac25c82017-05-27 07:38:26 -06001393 self._RunPackUbootSingleMicrocode()
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001394 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot locate "
1395 "_dt_ucode_base_size symbol in u-boot", str(e.exception))
1396
1397 finally:
1398 # Put the original file back
Simon Glass4affd4b2019-08-24 07:22:54 -06001399 TestFunctional._MakeInputFile('u-boot',
Simon Glass80025522022-01-29 14:14:04 -07001400 tools.read_file(self.ElfTestFile('u_boot_ucode_ptr')))
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001401
1402 def testMicrocodeNotInImage(self):
1403 """Test that microcode must be placed within the image"""
1404 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001405 self._DoReadFile('040_x86_ucode_not_in_image.dts', True)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001406 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Microcode "
1407 "pointer _dt_ucode_base_size at fffffe14 is outside the "
Simon Glassad5a7712018-06-01 09:38:14 -06001408 "section ranging from 00000000 to 0000002e", str(e.exception))
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001409
1410 def testWithoutMicrocode(self):
1411 """Test that we can cope with an image without microcode (e.g. qemu)"""
Simon Glassfaaaa162019-08-24 07:22:55 -06001412 TestFunctional._MakeInputFile('u-boot',
Simon Glass80025522022-01-29 14:14:04 -07001413 tools.read_file(self.ElfTestFile('u_boot_no_ucode_ptr')))
Simon Glass511f6582018-10-01 12:22:30 -06001414 data, dtb, _, _ = self._DoReadFileDtb('044_x86_optional_ucode.dts', True)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001415
1416 # Now check the device tree has no microcode
1417 self.assertEqual(U_BOOT_NODTB_DATA, data[:len(U_BOOT_NODTB_DATA)])
1418 second = data[len(U_BOOT_NODTB_DATA):]
1419
1420 fdt_len = self.GetFdtLen(second)
1421 self.assertEqual(dtb, second[:fdt_len])
1422
1423 used_len = len(U_BOOT_NODTB_DATA) + fdt_len
1424 third = data[used_len:]
Simon Glass80025522022-01-29 14:14:04 -07001425 self.assertEqual(tools.get_bytes(0, 0x200 - used_len), third)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001426
1427 def testUnknownPosSize(self):
1428 """Test that microcode must be placed within the image"""
1429 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001430 self._DoReadFile('041_unknown_pos_size.dts', True)
Simon Glasse8561af2018-08-01 15:22:37 -06001431 self.assertIn("Section '/binman': Unable to set offset/size for unknown "
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001432 "entry 'invalid-entry'", str(e.exception))
Simon Glassb4176d42016-11-25 20:15:56 -07001433
1434 def testPackFsp(self):
1435 """Test that an image with a FSP binary can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001436 data = self._DoReadFile('042_intel_fsp.dts')
Simon Glassb4176d42016-11-25 20:15:56 -07001437 self.assertEqual(FSP_DATA, data[:len(FSP_DATA)])
1438
1439 def testPackCmc(self):
Bin Mengd7bcdf52017-08-15 22:41:54 -07001440 """Test that an image with a CMC binary can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001441 data = self._DoReadFile('043_intel_cmc.dts')
Simon Glassb4176d42016-11-25 20:15:56 -07001442 self.assertEqual(CMC_DATA, data[:len(CMC_DATA)])
Bin Mengd7bcdf52017-08-15 22:41:54 -07001443
1444 def testPackVbt(self):
1445 """Test that an image with a VBT binary can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001446 data = self._DoReadFile('046_intel_vbt.dts')
Bin Mengd7bcdf52017-08-15 22:41:54 -07001447 self.assertEqual(VBT_DATA, data[:len(VBT_DATA)])
Simon Glassac599912017-11-12 21:52:22 -07001448
Simon Glass7f94e832017-11-12 21:52:25 -07001449 def testSplBssPad(self):
1450 """Test that we can pad SPL's BSS with zeros"""
Simon Glass3d274232017-11-12 21:52:27 -07001451 # ELF file with a '__bss_size' symbol
Simon Glass7057d022018-10-01 21:12:47 -06001452 self._SetupSplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001453 data = self._DoReadFile('047_spl_bss_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07001454 self.assertEqual(U_BOOT_SPL_DATA + tools.get_bytes(0, 10) + U_BOOT_DATA,
Simon Glassac0d4952019-05-14 15:53:47 -06001455 data)
Simon Glass7f94e832017-11-12 21:52:25 -07001456
Simon Glass04cda032018-10-01 21:12:42 -06001457 def testSplBssPadMissing(self):
1458 """Test that a missing symbol is detected"""
Simon Glass7057d022018-10-01 21:12:47 -06001459 self._SetupSplElf('u_boot_ucode_ptr')
Simon Glass24ad3652017-11-13 18:54:54 -07001460 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001461 self._DoReadFile('047_spl_bss_pad.dts')
Simon Glass24ad3652017-11-13 18:54:54 -07001462 self.assertIn('Expected __bss_size symbol in spl/u-boot-spl',
1463 str(e.exception))
1464
Simon Glasse83679d2017-11-12 21:52:26 -07001465 def testPackStart16Spl(self):
Simon Glassed40e962018-09-14 04:57:10 -06001466 """Test that an image with an x86 start16 SPL region can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001467 data = self._DoReadFile('048_x86_start16_spl.dts')
Simon Glasse83679d2017-11-12 21:52:26 -07001468 self.assertEqual(X86_START16_SPL_DATA, data[:len(X86_START16_SPL_DATA)])
1469
Simon Glass6ba679c2018-07-06 10:27:17 -06001470 def _PackUbootSplMicrocode(self, dts, ucode_second=False):
1471 """Helper function for microcode tests
Simon Glass3d274232017-11-12 21:52:27 -07001472
1473 We expect to see the following in the image, in order:
1474 u-boot-spl-nodtb.bin with a microcode pointer inserted at the
1475 correct place
1476 u-boot.dtb with the microcode removed
1477 the microcode
Simon Glass6ba679c2018-07-06 10:27:17 -06001478
1479 Args:
1480 dts: Device tree file to use for test
1481 ucode_second: True if the microsecond entry is second instead of
1482 third
Simon Glass3d274232017-11-12 21:52:27 -07001483 """
Simon Glass7057d022018-10-01 21:12:47 -06001484 self._SetupSplElf('u_boot_ucode_ptr')
Simon Glass6ba679c2018-07-06 10:27:17 -06001485 first, pos_and_size = self._RunMicrocodeTest(dts, U_BOOT_SPL_NODTB_DATA,
1486 ucode_second=ucode_second)
Simon Glass303f62f2019-05-17 22:00:46 -06001487 self.assertEqual(b'splnodtb with microc' + pos_and_size +
1488 b'ter somewhere in here', first)
Simon Glass3d274232017-11-12 21:52:27 -07001489
Simon Glass6ba679c2018-07-06 10:27:17 -06001490 def testPackUbootSplMicrocode(self):
1491 """Test that x86 microcode can be handled correctly in SPL"""
Marek Vasutf7413f02023-07-18 07:23:58 -06001492 self._SetupSplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001493 self._PackUbootSplMicrocode('049_x86_ucode_spl.dts')
Simon Glass6ba679c2018-07-06 10:27:17 -06001494
1495 def testPackUbootSplMicrocodeReorder(self):
1496 """Test that order doesn't matter for microcode entries
1497
1498 This is the same as testPackUbootSplMicrocode but when we process the
1499 u-boot-ucode entry we have not yet seen the u-boot-dtb-with-ucode
1500 entry, so we reply on binman to try later.
1501 """
Simon Glass511f6582018-10-01 12:22:30 -06001502 self._PackUbootSplMicrocode('058_x86_ucode_spl_needs_retry.dts',
Simon Glass6ba679c2018-07-06 10:27:17 -06001503 ucode_second=True)
1504
Simon Glassa409c932017-11-12 21:52:28 -07001505 def testPackMrc(self):
1506 """Test that an image with an MRC binary can be created"""
Simon Glass511f6582018-10-01 12:22:30 -06001507 data = self._DoReadFile('050_intel_mrc.dts')
Simon Glassa409c932017-11-12 21:52:28 -07001508 self.assertEqual(MRC_DATA, data[:len(MRC_DATA)])
1509
Simon Glass9aa6a6f2017-11-13 18:54:55 -07001510 def testSplDtb(self):
1511 """Test that an image with spl/u-boot-spl.dtb can be created"""
Marek Vasutf7413f02023-07-18 07:23:58 -06001512 self._SetupSplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001513 data = self._DoReadFile('051_u_boot_spl_dtb.dts')
Simon Glass9aa6a6f2017-11-13 18:54:55 -07001514 self.assertEqual(U_BOOT_SPL_DTB_DATA, data[:len(U_BOOT_SPL_DTB_DATA)])
1515
Simon Glass0a6da312017-11-13 18:54:56 -07001516 def testSplNoDtb(self):
1517 """Test that an image with spl/u-boot-spl-nodtb.bin can be created"""
Simon Glass13089cc2021-04-25 08:39:32 +12001518 self._SetupSplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001519 data = self._DoReadFile('052_u_boot_spl_nodtb.dts')
Simon Glass0a6da312017-11-13 18:54:56 -07001520 self.assertEqual(U_BOOT_SPL_NODTB_DATA, data[:len(U_BOOT_SPL_NODTB_DATA)])
1521
Simon Glass7098b7f2021-03-21 18:24:30 +13001522 def checkSymbols(self, dts, base_data, u_boot_offset, entry_args=None,
Simon Glass4b0f4142024-08-26 13:11:40 -06001523 use_expanded=False, no_write_symbols=False,
1524 symbols_base=None):
Simon Glass31e04cb2021-03-18 20:24:56 +13001525 """Check the image contains the expected symbol values
1526
1527 Args:
1528 dts: Device tree file to use for test
1529 base_data: Data before and after 'u-boot' section
Simon Glass3eb30a42024-08-26 13:11:42 -06001530 u_boot_offset (int): Offset of 'u-boot' section in image, or None if
1531 the offset not available due to it being in a compressed section
Simon Glass7098b7f2021-03-21 18:24:30 +13001532 entry_args: Dict of entry args to supply to binman
1533 key: arg name
1534 value: value of that arg
1535 use_expanded: True to use expanded entries where available, e.g.
1536 'u-boot-expanded' instead of 'u-boot'
Simon Glass4b0f4142024-08-26 13:11:40 -06001537 symbols_base (int): Value to expect for symbols-base in u-boot-spl,
1538 None if none
Simon Glass31e04cb2021-03-18 20:24:56 +13001539 """
Simon Glass5d0c0262019-08-24 07:22:56 -06001540 elf_fname = self.ElfTestFile('u_boot_binman_syms')
Simon Glass4ca8e042017-11-13 18:55:01 -07001541 syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
1542 addr = elf.GetSymbolAddress(elf_fname, '__image_copy_start')
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03001543 self.assertEqual(syms['_binman_sym_magic'].address, addr)
Simon Glass31e04cb2021-03-18 20:24:56 +13001544 self.assertEqual(syms['_binman_u_boot_spl_any_prop_offset'].address,
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03001545 addr + 4)
Simon Glass4ca8e042017-11-13 18:55:01 -07001546
Simon Glass7057d022018-10-01 21:12:47 -06001547 self._SetupSplElf('u_boot_binman_syms')
Simon Glass7098b7f2021-03-21 18:24:30 +13001548 data = self._DoReadFileDtb(dts, entry_args=entry_args,
Simon Glass3eb30a42024-08-26 13:11:42 -06001549 use_expanded=use_expanded,
1550 verbosity=None if u_boot_offset else 3)[0]
1551
1552 # The lz4-compressed version of the U-Boot data is 19 bytes long
1553 comp_uboot_len = 19
1554
Simon Glass31e04cb2021-03-18 20:24:56 +13001555 # The image should contain the symbols from u_boot_binman_syms.c
1556 # Note that image_pos is adjusted by the base address of the image,
1557 # which is 0x10 in our test image
Simon Glass3eb30a42024-08-26 13:11:42 -06001558 # If u_boot_offset is None, Binman should write -1U into the image
Simon Glass4b0f4142024-08-26 13:11:40 -06001559 vals2 = (elf.BINMAN_SYM_MAGIC_VALUE, 0x00,
Simon Glass3eb30a42024-08-26 13:11:42 -06001560 u_boot_offset + len(U_BOOT_DATA) if u_boot_offset else
1561 len(U_BOOT_SPL_DATA) + 1 + comp_uboot_len,
1562 0x10 + u_boot_offset if u_boot_offset else 0xffffffff, 0x04)
Simon Glass4b0f4142024-08-26 13:11:40 -06001563
1564 # u-boot-spl has a symbols-base property, so take that into account if
1565 # required. The caller must supply the value
1566 vals = list(vals2)
1567 if symbols_base is not None:
1568 vals[3] = symbols_base + u_boot_offset
1569 vals = tuple(vals)
1570
Simon Glass4b4049e2024-08-26 13:11:39 -06001571 sym_values = struct.pack('<LLQLL', *vals)
Simon Glass4b0f4142024-08-26 13:11:40 -06001572 sym_values2 = struct.pack('<LLQLL', *vals2)
Simon Glass4abf7842023-07-18 07:23:54 -06001573 if no_write_symbols:
Simon Glass4b4049e2024-08-26 13:11:39 -06001574 self.assertEqual(
1575 base_data +
1576 tools.get_bytes(0xff, 0x38 - len(base_data)) +
1577 U_BOOT_DATA + base_data, data)
Simon Glass4abf7842023-07-18 07:23:54 -06001578 else:
Simon Glass4b4049e2024-08-26 13:11:39 -06001579 got_vals = struct.unpack('<LLQLL', data[:24])
1580
1581 # For debugging:
1582 #print('expect:', list(f'{v:x}' for v in vals))
1583 #print(' got:', list(f'{v:x}' for v in got_vals))
1584
1585 self.assertEqual(vals, got_vals)
1586 self.assertEqual(sym_values, data[:24])
1587
1588 blen = len(base_data)
1589 self.assertEqual(base_data[24:], data[24:blen])
1590 self.assertEqual(0xff, data[blen])
1591
Simon Glass3eb30a42024-08-26 13:11:42 -06001592 if u_boot_offset:
1593 ofs = blen + 1 + len(U_BOOT_DATA)
1594 self.assertEqual(U_BOOT_DATA, data[blen + 1:ofs])
1595 else:
1596 ofs = blen + 1 + comp_uboot_len
Simon Glass4b4049e2024-08-26 13:11:39 -06001597
Simon Glass4b0f4142024-08-26 13:11:40 -06001598 self.assertEqual(sym_values2, data[ofs:ofs + 24])
Simon Glass4b4049e2024-08-26 13:11:39 -06001599 self.assertEqual(base_data[24:], data[ofs + 24:])
1600
1601 # Just repeating the above asserts all at once, for clarity
Simon Glass3eb30a42024-08-26 13:11:42 -06001602 if u_boot_offset:
1603 expected = (sym_values + base_data[24:] +
1604 tools.get_bytes(0xff, 1) + U_BOOT_DATA +
1605 sym_values2 + base_data[24:])
1606 self.assertEqual(expected, data)
Simon Glass4ca8e042017-11-13 18:55:01 -07001607
Simon Glass31e04cb2021-03-18 20:24:56 +13001608 def testSymbols(self):
1609 """Test binman can assign symbols embedded in U-Boot"""
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03001610 self.checkSymbols('053_symbols.dts', U_BOOT_SPL_DATA, 0x1c)
Simon Glass31e04cb2021-03-18 20:24:56 +13001611
1612 def testSymbolsNoDtb(self):
1613 """Test binman can assign symbols embedded in U-Boot SPL"""
Simon Glass3bbc9932021-03-21 18:24:29 +13001614 self.checkSymbols('196_symbols_nodtb.dts',
Simon Glass31e04cb2021-03-18 20:24:56 +13001615 U_BOOT_SPL_NODTB_DATA + U_BOOT_SPL_DTB_DATA,
1616 0x38)
1617
Simon Glasse76a3e62018-06-01 09:38:11 -06001618 def testPackUnitAddress(self):
1619 """Test that we support multiple binaries with the same name"""
Simon Glass511f6582018-10-01 12:22:30 -06001620 data = self._DoReadFile('054_unit_address.dts')
Simon Glasse76a3e62018-06-01 09:38:11 -06001621 self.assertEqual(U_BOOT_DATA + U_BOOT_DATA, data)
1622
Simon Glassa91e1152018-06-01 09:38:16 -06001623 def testSections(self):
1624 """Basic test of sections"""
Simon Glass511f6582018-10-01 12:22:30 -06001625 data = self._DoReadFile('055_sections.dts')
Simon Glass80025522022-01-29 14:14:04 -07001626 expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) +
1627 U_BOOT_DATA + tools.get_bytes(ord('a'), 12) +
1628 U_BOOT_DATA + tools.get_bytes(ord('&'), 4))
Simon Glassa91e1152018-06-01 09:38:16 -06001629 self.assertEqual(expected, data)
Simon Glassac599912017-11-12 21:52:22 -07001630
Simon Glass30732662018-06-01 09:38:20 -06001631 def testMap(self):
1632 """Tests outputting a map of the images"""
Simon Glass511f6582018-10-01 12:22:30 -06001633 _, _, map_data, _ = self._DoReadFileDtb('055_sections.dts', map=True)
Simon Glass7eca7922018-07-17 13:25:49 -06001634 self.assertEqual('''ImagePos Offset Size Name
Simon Glass49cd2b32023-02-07 14:34:18 -0700163500000000 00000000 00000028 image
Simon Glass7eca7922018-07-17 13:25:49 -0600163600000000 00000000 00000010 section@0
163700000000 00000000 00000004 u-boot
163800000010 00000010 00000010 section@1
163900000010 00000000 00000004 u-boot
164000000020 00000020 00000004 section@2
164100000020 00000000 00000004 u-boot
Simon Glass30732662018-06-01 09:38:20 -06001642''', map_data)
1643
Simon Glass3b78d532018-06-01 09:38:21 -06001644 def testNamePrefix(self):
1645 """Tests that name prefixes are used"""
Simon Glass511f6582018-10-01 12:22:30 -06001646 _, _, map_data, _ = self._DoReadFileDtb('056_name_prefix.dts', map=True)
Simon Glass7eca7922018-07-17 13:25:49 -06001647 self.assertEqual('''ImagePos Offset Size Name
Simon Glass49cd2b32023-02-07 14:34:18 -0700164800000000 00000000 00000028 image
Simon Glass7eca7922018-07-17 13:25:49 -0600164900000000 00000000 00000010 section@0
165000000000 00000000 00000004 ro-u-boot
165100000010 00000010 00000010 section@1
165200000010 00000000 00000004 rw-u-boot
Simon Glass3b78d532018-06-01 09:38:21 -06001653''', map_data)
1654
Simon Glass6ba679c2018-07-06 10:27:17 -06001655 def testUnknownContents(self):
1656 """Test that obtaining the contents works as expected"""
1657 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001658 self._DoReadFile('057_unknown_contents.dts', True)
Simon Glass39dd2152019-07-08 14:25:47 -06001659 self.assertIn("Image '/binman': Internal error: Could not complete "
Simon Glassc585dd42020-04-17 18:09:03 -06001660 "processing of contents: remaining ["
1661 "<binman.etype._testing.Entry__testing ", str(e.exception))
Simon Glass6ba679c2018-07-06 10:27:17 -06001662
Simon Glass2e1169f2018-07-06 10:27:19 -06001663 def testBadChangeSize(self):
1664 """Test that trying to change the size of an entry fails"""
Simon Glasse61b6f62019-07-08 14:25:37 -06001665 try:
1666 state.SetAllowEntryExpansion(False)
1667 with self.assertRaises(ValueError) as e:
1668 self._DoReadFile('059_change_size.dts', True)
Simon Glass8c702fb2019-07-20 12:23:57 -06001669 self.assertIn("Node '/binman/_testing': Cannot update entry size from 2 to 3",
Simon Glasse61b6f62019-07-08 14:25:37 -06001670 str(e.exception))
1671 finally:
1672 state.SetAllowEntryExpansion(True)
Simon Glass2e1169f2018-07-06 10:27:19 -06001673
Simon Glassa87014e2018-07-06 10:27:42 -06001674 def testUpdateFdt(self):
Simon Glasse8561af2018-08-01 15:22:37 -06001675 """Test that we can update the device tree with offset/size info"""
Simon Glass511f6582018-10-01 12:22:30 -06001676 _, _, _, out_dtb_fname = self._DoReadFileDtb('060_fdt_update.dts',
Simon Glassa87014e2018-07-06 10:27:42 -06001677 update_dtb=True)
Simon Glass5463a6a2018-07-17 13:25:52 -06001678 dtb = fdt.Fdt(out_dtb_fname)
1679 dtb.Scan()
Simon Glassfb30e292019-07-20 12:23:51 -06001680 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
Simon Glassa87014e2018-07-06 10:27:42 -06001681 self.assertEqual({
Simon Glass9dcc8612018-08-01 15:22:42 -06001682 'image-pos': 0,
Simon Glass3a9a2b82018-07-17 13:25:28 -06001683 'offset': 0,
Simon Glasse8561af2018-08-01 15:22:37 -06001684 '_testing:offset': 32,
Simon Glass8c702fb2019-07-20 12:23:57 -06001685 '_testing:size': 2,
Simon Glass9dcc8612018-08-01 15:22:42 -06001686 '_testing:image-pos': 32,
Simon Glasse8561af2018-08-01 15:22:37 -06001687 'section@0/u-boot:offset': 0,
Simon Glassa87014e2018-07-06 10:27:42 -06001688 'section@0/u-boot:size': len(U_BOOT_DATA),
Simon Glass9dcc8612018-08-01 15:22:42 -06001689 'section@0/u-boot:image-pos': 0,
Simon Glasse8561af2018-08-01 15:22:37 -06001690 'section@0:offset': 0,
Simon Glassa87014e2018-07-06 10:27:42 -06001691 'section@0:size': 16,
Simon Glass9dcc8612018-08-01 15:22:42 -06001692 'section@0:image-pos': 0,
Simon Glassa87014e2018-07-06 10:27:42 -06001693
Simon Glasse8561af2018-08-01 15:22:37 -06001694 'section@1/u-boot:offset': 0,
Simon Glassa87014e2018-07-06 10:27:42 -06001695 'section@1/u-boot:size': len(U_BOOT_DATA),
Simon Glass9dcc8612018-08-01 15:22:42 -06001696 'section@1/u-boot:image-pos': 16,
Simon Glasse8561af2018-08-01 15:22:37 -06001697 'section@1:offset': 16,
Simon Glassa87014e2018-07-06 10:27:42 -06001698 'section@1:size': 16,
Simon Glass9dcc8612018-08-01 15:22:42 -06001699 'section@1:image-pos': 16,
Simon Glassa87014e2018-07-06 10:27:42 -06001700 'size': 40
1701 }, props)
1702
1703 def testUpdateFdtBad(self):
1704 """Test that we detect when ProcessFdt never completes"""
1705 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001706 self._DoReadFileDtb('061_fdt_update_bad.dts', update_dtb=True)
Simon Glassa87014e2018-07-06 10:27:42 -06001707 self.assertIn('Could not complete processing of Fdt: remaining '
Simon Glassc585dd42020-04-17 18:09:03 -06001708 '[<binman.etype._testing.Entry__testing',
1709 str(e.exception))
Simon Glass2e1169f2018-07-06 10:27:19 -06001710
Simon Glass91710b32018-07-17 13:25:32 -06001711 def testEntryArgs(self):
1712 """Test passing arguments to entries from the command line"""
1713 entry_args = {
1714 'test-str-arg': 'test1',
1715 'test-int-arg': '456',
1716 }
Simon Glass511f6582018-10-01 12:22:30 -06001717 self._DoReadFileDtb('062_entry_args.dts', entry_args=entry_args)
Simon Glass91710b32018-07-17 13:25:32 -06001718 self.assertIn('image', control.images)
1719 entry = control.images['image'].GetEntries()['_testing']
1720 self.assertEqual('test0', entry.test_str_fdt)
1721 self.assertEqual('test1', entry.test_str_arg)
1722 self.assertEqual(123, entry.test_int_fdt)
1723 self.assertEqual(456, entry.test_int_arg)
1724
1725 def testEntryArgsMissing(self):
1726 """Test missing arguments and properties"""
1727 entry_args = {
1728 'test-int-arg': '456',
1729 }
Simon Glass511f6582018-10-01 12:22:30 -06001730 self._DoReadFileDtb('063_entry_args_missing.dts', entry_args=entry_args)
Simon Glass91710b32018-07-17 13:25:32 -06001731 entry = control.images['image'].GetEntries()['_testing']
1732 self.assertEqual('test0', entry.test_str_fdt)
1733 self.assertEqual(None, entry.test_str_arg)
1734 self.assertEqual(None, entry.test_int_fdt)
1735 self.assertEqual(456, entry.test_int_arg)
1736
1737 def testEntryArgsRequired(self):
1738 """Test missing arguments and properties"""
1739 entry_args = {
1740 'test-int-arg': '456',
1741 }
1742 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001743 self._DoReadFileDtb('064_entry_args_required.dts')
Simon Glass21db0ff2020-09-01 05:13:54 -06001744 self.assertIn("Node '/binman/_testing': "
1745 'Missing required properties/entry args: test-str-arg, '
1746 'test-int-fdt, test-int-arg',
Simon Glass91710b32018-07-17 13:25:32 -06001747 str(e.exception))
1748
1749 def testEntryArgsInvalidFormat(self):
1750 """Test that an invalid entry-argument format is detected"""
Simon Glassf46732a2019-07-08 14:25:29 -06001751 args = ['build', '-d', self.TestFile('064_entry_args_required.dts'),
1752 '-ano-value']
Simon Glass91710b32018-07-17 13:25:32 -06001753 with self.assertRaises(ValueError) as e:
1754 self._DoBinman(*args)
1755 self.assertIn("Invalid entry arguemnt 'no-value'", str(e.exception))
1756
1757 def testEntryArgsInvalidInteger(self):
1758 """Test that an invalid entry-argument integer is detected"""
1759 entry_args = {
1760 'test-int-arg': 'abc',
1761 }
1762 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001763 self._DoReadFileDtb('062_entry_args.dts', entry_args=entry_args)
Simon Glass91710b32018-07-17 13:25:32 -06001764 self.assertIn("Node '/binman/_testing': Cannot convert entry arg "
1765 "'test-int-arg' (value 'abc') to integer",
1766 str(e.exception))
1767
1768 def testEntryArgsInvalidDatatype(self):
1769 """Test that an invalid entry-argument datatype is detected
1770
1771 This test could be written in entry_test.py except that it needs
1772 access to control.entry_args, which seems more than that module should
1773 be able to see.
1774 """
1775 entry_args = {
1776 'test-bad-datatype-arg': '12',
1777 }
1778 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001779 self._DoReadFileDtb('065_entry_args_unknown_datatype.dts',
Simon Glass91710b32018-07-17 13:25:32 -06001780 entry_args=entry_args)
1781 self.assertIn('GetArg() internal error: Unknown data type ',
1782 str(e.exception))
1783
Simon Glass2ca52032018-07-17 13:25:33 -06001784 def testText(self):
1785 """Test for a text entry type"""
1786 entry_args = {
1787 'test-id': TEXT_DATA,
1788 'test-id2': TEXT_DATA2,
1789 'test-id3': TEXT_DATA3,
1790 }
Simon Glass511f6582018-10-01 12:22:30 -06001791 data, _, _, _ = self._DoReadFileDtb('066_text.dts',
Simon Glass2ca52032018-07-17 13:25:33 -06001792 entry_args=entry_args)
Simon Glass80025522022-01-29 14:14:04 -07001793 expected = (tools.to_bytes(TEXT_DATA) +
1794 tools.get_bytes(0, 8 - len(TEXT_DATA)) +
1795 tools.to_bytes(TEXT_DATA2) + tools.to_bytes(TEXT_DATA3) +
Simon Glass47f6a622019-07-08 13:18:40 -06001796 b'some text' + b'more text')
Simon Glass2ca52032018-07-17 13:25:33 -06001797 self.assertEqual(expected, data)
1798
Simon Glass969616c2018-07-17 13:25:36 -06001799 def testEntryDocs(self):
1800 """Test for creation of entry documentation"""
Simon Glass14d64e32025-04-29 07:21:59 -06001801 with terminal.capture() as (stdout, stderr):
Simon Glass220ff5f2020-08-05 13:27:46 -06001802 control.WriteEntryDocs(control.GetEntryModules())
Simon Glass969616c2018-07-17 13:25:36 -06001803 self.assertTrue(len(stdout.getvalue()) > 0)
1804
1805 def testEntryDocsMissing(self):
1806 """Test handling of missing entry documentation"""
1807 with self.assertRaises(ValueError) as e:
Simon Glass14d64e32025-04-29 07:21:59 -06001808 with terminal.capture() as (stdout, stderr):
Simon Glass220ff5f2020-08-05 13:27:46 -06001809 control.WriteEntryDocs(control.GetEntryModules(), 'u_boot')
Simon Glass969616c2018-07-17 13:25:36 -06001810 self.assertIn('Documentation is missing for modules: u_boot',
1811 str(e.exception))
1812
Simon Glass704784b2018-07-17 13:25:38 -06001813 def testFmap(self):
1814 """Basic test of generation of a flashrom fmap"""
Simon Glass511f6582018-10-01 12:22:30 -06001815 data = self._DoReadFile('067_fmap.dts')
Simon Glass704784b2018-07-17 13:25:38 -06001816 fhdr, fentries = fmap_util.DecodeFmap(data[32:])
Simon Glass80025522022-01-29 14:14:04 -07001817 expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) +
1818 U_BOOT_DATA + tools.get_bytes(ord('a'), 12))
Simon Glass704784b2018-07-17 13:25:38 -06001819 self.assertEqual(expected, data[:32])
Simon Glass303f62f2019-05-17 22:00:46 -06001820 self.assertEqual(b'__FMAP__', fhdr.signature)
Simon Glass704784b2018-07-17 13:25:38 -06001821 self.assertEqual(1, fhdr.ver_major)
1822 self.assertEqual(0, fhdr.ver_minor)
1823 self.assertEqual(0, fhdr.base)
Simon Glassb1d414c2021-04-03 11:05:10 +13001824 expect_size = fmap_util.FMAP_HEADER_LEN + fmap_util.FMAP_AREA_LEN * 5
Simon Glass82059c22021-04-03 11:05:09 +13001825 self.assertEqual(16 + 16 + expect_size, fhdr.image_size)
Simon Glass303f62f2019-05-17 22:00:46 -06001826 self.assertEqual(b'FMAP', fhdr.name)
Simon Glassb1d414c2021-04-03 11:05:10 +13001827 self.assertEqual(5, fhdr.nareas)
Simon Glass82059c22021-04-03 11:05:09 +13001828 fiter = iter(fentries)
Simon Glass704784b2018-07-17 13:25:38 -06001829
Simon Glass82059c22021-04-03 11:05:09 +13001830 fentry = next(fiter)
Simon Glassb1d414c2021-04-03 11:05:10 +13001831 self.assertEqual(b'SECTION0', fentry.name)
1832 self.assertEqual(0, fentry.offset)
1833 self.assertEqual(16, fentry.size)
Simon Glasscda991e2023-02-12 17:11:15 -07001834 self.assertEqual(fmap_util.FMAP_AREA_PRESERVE, fentry.flags)
Simon Glassb1d414c2021-04-03 11:05:10 +13001835
1836 fentry = next(fiter)
Simon Glass82059c22021-04-03 11:05:09 +13001837 self.assertEqual(b'RO_U_BOOT', fentry.name)
1838 self.assertEqual(0, fentry.offset)
1839 self.assertEqual(4, fentry.size)
1840 self.assertEqual(0, fentry.flags)
Simon Glass704784b2018-07-17 13:25:38 -06001841
Simon Glass82059c22021-04-03 11:05:09 +13001842 fentry = next(fiter)
Simon Glassb1d414c2021-04-03 11:05:10 +13001843 self.assertEqual(b'SECTION1', fentry.name)
1844 self.assertEqual(16, fentry.offset)
1845 self.assertEqual(16, fentry.size)
1846 self.assertEqual(0, fentry.flags)
1847
1848 fentry = next(fiter)
Simon Glass82059c22021-04-03 11:05:09 +13001849 self.assertEqual(b'RW_U_BOOT', fentry.name)
1850 self.assertEqual(16, fentry.offset)
1851 self.assertEqual(4, fentry.size)
1852 self.assertEqual(0, fentry.flags)
Simon Glass704784b2018-07-17 13:25:38 -06001853
Simon Glass82059c22021-04-03 11:05:09 +13001854 fentry = next(fiter)
1855 self.assertEqual(b'FMAP', fentry.name)
1856 self.assertEqual(32, fentry.offset)
1857 self.assertEqual(expect_size, fentry.size)
1858 self.assertEqual(0, fentry.flags)
Simon Glass704784b2018-07-17 13:25:38 -06001859
Simon Glassdb168d42018-07-17 13:25:39 -06001860 def testBlobNamedByArg(self):
1861 """Test we can add a blob with the filename coming from an entry arg"""
1862 entry_args = {
1863 'cros-ec-rw-path': 'ecrw.bin',
1864 }
Simon Glass21db0ff2020-09-01 05:13:54 -06001865 self._DoReadFileDtb('068_blob_named_by_arg.dts', entry_args=entry_args)
Simon Glassdb168d42018-07-17 13:25:39 -06001866
Simon Glass53f53992018-07-17 13:25:40 -06001867 def testFill(self):
1868 """Test for an fill entry type"""
Simon Glass511f6582018-10-01 12:22:30 -06001869 data = self._DoReadFile('069_fill.dts')
Simon Glass80025522022-01-29 14:14:04 -07001870 expected = tools.get_bytes(0xff, 8) + tools.get_bytes(0, 8)
Simon Glass53f53992018-07-17 13:25:40 -06001871 self.assertEqual(expected, data)
1872
1873 def testFillNoSize(self):
1874 """Test for an fill entry type with no size"""
1875 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001876 self._DoReadFile('070_fill_no_size.dts')
Simon Glass0cf5bce2022-08-13 11:40:44 -06001877 self.assertIn("'fill' entry is missing properties: size",
Simon Glass53f53992018-07-17 13:25:40 -06001878 str(e.exception))
1879
Simon Glassc1ae83c2018-07-17 13:25:44 -06001880 def _HandleGbbCommand(self, pipe_list):
1881 """Fake calls to the futility utility"""
Simon Glass9a1c7262023-02-22 12:14:49 -07001882 if 'futility' in pipe_list[0][0]:
Simon Glassc1ae83c2018-07-17 13:25:44 -06001883 fname = pipe_list[0][-1]
1884 # Append our GBB data to the file, which will happen every time the
1885 # futility command is called.
Simon Glass33486662019-05-14 15:53:42 -06001886 with open(fname, 'ab') as fd:
Simon Glassc1ae83c2018-07-17 13:25:44 -06001887 fd.write(GBB_DATA)
1888 return command.CommandResult()
1889
1890 def testGbb(self):
1891 """Test for the Chromium OS Google Binary Block"""
Simon Glass5dc22cf2025-02-03 09:26:42 -07001892 command.TEST_RESULT = self._HandleGbbCommand
Simon Glassc1ae83c2018-07-17 13:25:44 -06001893 entry_args = {
1894 'keydir': 'devkeys',
1895 'bmpblk': 'bmpblk.bin',
1896 }
Simon Glass511f6582018-10-01 12:22:30 -06001897 data, _, _, _ = self._DoReadFileDtb('071_gbb.dts', entry_args=entry_args)
Simon Glassc1ae83c2018-07-17 13:25:44 -06001898
1899 # Since futility
Simon Glass80025522022-01-29 14:14:04 -07001900 expected = (GBB_DATA + GBB_DATA + tools.get_bytes(0, 8) +
1901 tools.get_bytes(0, 0x2180 - 16))
Simon Glassc1ae83c2018-07-17 13:25:44 -06001902 self.assertEqual(expected, data)
1903
1904 def testGbbTooSmall(self):
1905 """Test for the Chromium OS Google Binary Block being large enough"""
1906 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001907 self._DoReadFileDtb('072_gbb_too_small.dts')
Simon Glassc1ae83c2018-07-17 13:25:44 -06001908 self.assertIn("Node '/binman/gbb': GBB is too small",
1909 str(e.exception))
1910
1911 def testGbbNoSize(self):
1912 """Test for the Chromium OS Google Binary Block having a size"""
1913 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001914 self._DoReadFileDtb('073_gbb_no_size.dts')
Simon Glassc1ae83c2018-07-17 13:25:44 -06001915 self.assertIn("Node '/binman/gbb': GBB must have a fixed size",
1916 str(e.exception))
1917
Simon Glass66152ce2022-01-09 20:14:09 -07001918 def testGbbMissing(self):
1919 """Test that binman still produces an image if futility is missing"""
1920 entry_args = {
1921 'keydir': 'devkeys',
1922 }
Simon Glass14d64e32025-04-29 07:21:59 -06001923 with terminal.capture() as (_, stderr):
Simon Glass66152ce2022-01-09 20:14:09 -07001924 self._DoTestFile('071_gbb.dts', force_missing_bintools='futility',
1925 entry_args=entry_args)
1926 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07001927 self.assertRegex(err, "Image 'image'.*missing bintools.*: futility")
Simon Glass66152ce2022-01-09 20:14:09 -07001928
Simon Glass5c350162018-07-17 13:25:47 -06001929 def _HandleVblockCommand(self, pipe_list):
Simon Glass220c6222021-01-06 21:35:17 -07001930 """Fake calls to the futility utility
1931
1932 The expected pipe is:
1933
1934 [('futility', 'vbutil_firmware', '--vblock',
1935 'vblock.vblock', '--keyblock', 'devkeys/firmware.keyblock',
1936 '--signprivate', 'devkeys/firmware_data_key.vbprivk',
1937 '--version', '1', '--fv', 'input.vblock', '--kernelkey',
1938 'devkeys/kernel_subkey.vbpubk', '--flags', '1')]
1939
1940 This writes to the output file (here, 'vblock.vblock'). If
1941 self._hash_data is False, it writes VBLOCK_DATA, else it writes a hash
1942 of the input data (here, 'input.vblock').
1943 """
Simon Glass9a1c7262023-02-22 12:14:49 -07001944 if 'futility' in pipe_list[0][0]:
Simon Glass5c350162018-07-17 13:25:47 -06001945 fname = pipe_list[0][3]
Simon Glass639505b2018-09-14 04:57:11 -06001946 with open(fname, 'wb') as fd:
Simon Glass220c6222021-01-06 21:35:17 -07001947 if self._hash_data:
1948 infile = pipe_list[0][11]
1949 m = hashlib.sha256()
Simon Glass80025522022-01-29 14:14:04 -07001950 data = tools.read_file(infile)
Simon Glass220c6222021-01-06 21:35:17 -07001951 m.update(data)
1952 fd.write(m.digest())
1953 else:
1954 fd.write(VBLOCK_DATA)
1955
Simon Glass5c350162018-07-17 13:25:47 -06001956 return command.CommandResult()
1957
1958 def testVblock(self):
1959 """Test for the Chromium OS Verified Boot Block"""
Simon Glass220c6222021-01-06 21:35:17 -07001960 self._hash_data = False
Simon Glass5dc22cf2025-02-03 09:26:42 -07001961 command.TEST_RESULT = self._HandleVblockCommand
Simon Glass5c350162018-07-17 13:25:47 -06001962 entry_args = {
1963 'keydir': 'devkeys',
1964 }
Simon Glass511f6582018-10-01 12:22:30 -06001965 data, _, _, _ = self._DoReadFileDtb('074_vblock.dts',
Simon Glass5c350162018-07-17 13:25:47 -06001966 entry_args=entry_args)
1967 expected = U_BOOT_DATA + VBLOCK_DATA + U_BOOT_DTB_DATA
1968 self.assertEqual(expected, data)
1969
1970 def testVblockNoContent(self):
1971 """Test we detect a vblock which has no content to sign"""
1972 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001973 self._DoReadFile('075_vblock_no_content.dts')
Simon Glasse1915782021-03-21 18:24:31 +13001974 self.assertIn("Node '/binman/vblock': Collection must have a 'content' "
Simon Glass5c350162018-07-17 13:25:47 -06001975 'property', str(e.exception))
1976
1977 def testVblockBadPhandle(self):
1978 """Test that we detect a vblock with an invalid phandle in contents"""
1979 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001980 self._DoReadFile('076_vblock_bad_phandle.dts')
Simon Glass5c350162018-07-17 13:25:47 -06001981 self.assertIn("Node '/binman/vblock': Cannot find node for phandle "
1982 '1000', str(e.exception))
1983
1984 def testVblockBadEntry(self):
1985 """Test that we detect an entry that points to a non-entry"""
1986 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001987 self._DoReadFile('077_vblock_bad_entry.dts')
Simon Glass5c350162018-07-17 13:25:47 -06001988 self.assertIn("Node '/binman/vblock': Cannot find entry for node "
1989 "'other'", str(e.exception))
1990
Simon Glass220c6222021-01-06 21:35:17 -07001991 def testVblockContent(self):
1992 """Test that the vblock signs the right data"""
1993 self._hash_data = True
Simon Glass5dc22cf2025-02-03 09:26:42 -07001994 command.TEST_RESULT = self._HandleVblockCommand
Simon Glass220c6222021-01-06 21:35:17 -07001995 entry_args = {
1996 'keydir': 'devkeys',
1997 }
1998 data = self._DoReadFileDtb(
1999 '189_vblock_content.dts', use_real_dtb=True, update_dtb=True,
2000 entry_args=entry_args)[0]
2001 hashlen = 32 # SHA256 hash is 32 bytes
2002 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
2003 hashval = data[-hashlen:]
2004 dtb = data[len(U_BOOT_DATA):-hashlen]
2005
2006 expected_data = U_BOOT_DATA + dtb
2007
2008 # The hashval should be a hash of the dtb
2009 m = hashlib.sha256()
2010 m.update(expected_data)
2011 expected_hashval = m.digest()
2012 self.assertEqual(expected_hashval, hashval)
2013
Simon Glass66152ce2022-01-09 20:14:09 -07002014 def testVblockMissing(self):
2015 """Test that binman still produces an image if futility is missing"""
2016 entry_args = {
2017 'keydir': 'devkeys',
2018 }
Simon Glass14d64e32025-04-29 07:21:59 -06002019 with terminal.capture() as (_, stderr):
Simon Glass66152ce2022-01-09 20:14:09 -07002020 self._DoTestFile('074_vblock.dts',
2021 force_missing_bintools='futility',
2022 entry_args=entry_args)
2023 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07002024 self.assertRegex(err, "Image 'image'.*missing bintools.*: futility")
Simon Glass66152ce2022-01-09 20:14:09 -07002025
Simon Glass8425a1f2018-07-17 13:25:48 -06002026 def testTpl(self):
Simon Glass3eb5b202019-08-24 07:23:00 -06002027 """Test that an image with TPL and its device tree can be created"""
Simon Glass8425a1f2018-07-17 13:25:48 -06002028 # ELF file with a '__bss_size' symbol
Simon Glass3eb5b202019-08-24 07:23:00 -06002029 self._SetupTplElf()
Simon Glass511f6582018-10-01 12:22:30 -06002030 data = self._DoReadFile('078_u_boot_tpl.dts')
Simon Glass8425a1f2018-07-17 13:25:48 -06002031 self.assertEqual(U_BOOT_TPL_DATA + U_BOOT_TPL_DTB_DATA, data)
2032
Simon Glass24b97442018-07-17 13:25:51 -06002033 def testUsesPos(self):
2034 """Test that the 'pos' property cannot be used anymore"""
2035 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002036 data = self._DoReadFile('079_uses_pos.dts')
Simon Glass24b97442018-07-17 13:25:51 -06002037 self.assertIn("Node '/binman/u-boot': Please use 'offset' instead of "
2038 "'pos'", str(e.exception))
2039
Simon Glass274bf092018-09-14 04:57:08 -06002040 def testFillZero(self):
2041 """Test for an fill entry type with a size of 0"""
Simon Glass511f6582018-10-01 12:22:30 -06002042 data = self._DoReadFile('080_fill_empty.dts')
Simon Glass80025522022-01-29 14:14:04 -07002043 self.assertEqual(tools.get_bytes(0, 16), data)
Simon Glass274bf092018-09-14 04:57:08 -06002044
Simon Glass267de432018-09-14 04:57:09 -06002045 def testTextMissing(self):
2046 """Test for a text entry type where there is no text"""
2047 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002048 self._DoReadFileDtb('066_text.dts',)
Simon Glass267de432018-09-14 04:57:09 -06002049 self.assertIn("Node '/binman/text': No value provided for text label "
2050 "'test-id'", str(e.exception))
2051
Simon Glassed40e962018-09-14 04:57:10 -06002052 def testPackStart16Tpl(self):
2053 """Test that an image with an x86 start16 TPL region can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06002054 data = self._DoReadFile('081_x86_start16_tpl.dts')
Simon Glassed40e962018-09-14 04:57:10 -06002055 self.assertEqual(X86_START16_TPL_DATA, data[:len(X86_START16_TPL_DATA)])
2056
Simon Glass3b376c32018-09-14 04:57:12 -06002057 def testSelectImage(self):
2058 """Test that we can select which images to build"""
Simon Glassb4595d82019-04-25 21:58:34 -06002059 expected = 'Skipping images: image1'
2060
2061 # We should only get the expected message in verbose mode
Simon Glass8a50b4a2019-07-08 13:18:48 -06002062 for verbosity in (0, 2):
Simon Glass14d64e32025-04-29 07:21:59 -06002063 with terminal.capture() as (stdout, stderr):
Simon Glassb4595d82019-04-25 21:58:34 -06002064 retcode = self._DoTestFile('006_dual_image.dts',
2065 verbosity=verbosity,
2066 images=['image2'])
2067 self.assertEqual(0, retcode)
2068 if verbosity:
2069 self.assertIn(expected, stdout.getvalue())
2070 else:
2071 self.assertNotIn(expected, stdout.getvalue())
Simon Glass3b376c32018-09-14 04:57:12 -06002072
Simon Glass80025522022-01-29 14:14:04 -07002073 self.assertFalse(os.path.exists(tools.get_output_filename('image1.bin')))
2074 self.assertTrue(os.path.exists(tools.get_output_filename('image2.bin')))
Simon Glassb3d6fc72019-07-20 12:24:10 -06002075 self._CleanupOutputDir()
Simon Glass3b376c32018-09-14 04:57:12 -06002076
Simon Glasse219aa42018-09-14 04:57:24 -06002077 def testUpdateFdtAll(self):
2078 """Test that all device trees are updated with offset/size info"""
Marek Vasutf7413f02023-07-18 07:23:58 -06002079 self._SetupSplElf()
2080 self._SetupTplElf()
Simon Glass5b4bce32019-07-08 14:25:26 -06002081 data = self._DoReadFileRealDtb('082_fdt_update_all.dts')
Simon Glasse219aa42018-09-14 04:57:24 -06002082
2083 base_expected = {
Simon Glasse219aa42018-09-14 04:57:24 -06002084 'offset': 0,
Simon Glass56d05412022-02-28 07:16:54 -07002085 'image-pos': 0,
2086 'size': 2320,
Simon Glasse219aa42018-09-14 04:57:24 -06002087 'section:offset': 0,
Simon Glass56d05412022-02-28 07:16:54 -07002088 'section:image-pos': 0,
2089 'section:size': 565,
2090 'section/u-boot-dtb:offset': 0,
2091 'section/u-boot-dtb:image-pos': 0,
2092 'section/u-boot-dtb:size': 565,
2093 'u-boot-spl-dtb:offset': 565,
2094 'u-boot-spl-dtb:image-pos': 565,
2095 'u-boot-spl-dtb:size': 585,
2096 'u-boot-tpl-dtb:offset': 1150,
2097 'u-boot-tpl-dtb:image-pos': 1150,
2098 'u-boot-tpl-dtb:size': 585,
2099 'u-boot-vpl-dtb:image-pos': 1735,
2100 'u-boot-vpl-dtb:offset': 1735,
2101 'u-boot-vpl-dtb:size': 585,
Simon Glasse219aa42018-09-14 04:57:24 -06002102 }
2103
2104 # We expect three device-tree files in the output, one after the other.
2105 # Read them in sequence. We look for an 'spl' property in the SPL tree,
2106 # and 'tpl' in the TPL tree, to make sure they are distinct from the
2107 # main U-Boot tree. All three should have the same postions and offset.
2108 start = 0
Simon Glass56d05412022-02-28 07:16:54 -07002109 self.maxDiff = None
2110 for item in ['', 'spl', 'tpl', 'vpl']:
Simon Glasse219aa42018-09-14 04:57:24 -06002111 dtb = fdt.Fdt.FromData(data[start:])
2112 dtb.Scan()
Simon Glassfb30e292019-07-20 12:23:51 -06002113 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS +
Simon Glass56d05412022-02-28 07:16:54 -07002114 ['spl', 'tpl', 'vpl'])
Simon Glasse219aa42018-09-14 04:57:24 -06002115 expected = dict(base_expected)
2116 if item:
2117 expected[item] = 0
2118 self.assertEqual(expected, props)
2119 start += dtb._fdt_obj.totalsize()
2120
2121 def testUpdateFdtOutput(self):
2122 """Test that output DTB files are updated"""
2123 try:
Simon Glass511f6582018-10-01 12:22:30 -06002124 data, dtb_data, _, _ = self._DoReadFileDtb('082_fdt_update_all.dts',
Simon Glasse219aa42018-09-14 04:57:24 -06002125 use_real_dtb=True, update_dtb=True, reset_dtbs=False)
2126
2127 # Unfortunately, compiling a source file always results in a file
2128 # called source.dtb (see fdt_util.EnsureCompiled()). The test
Simon Glass511f6582018-10-01 12:22:30 -06002129 # source file (e.g. test/075_fdt_update_all.dts) thus does not enter
Simon Glasse219aa42018-09-14 04:57:24 -06002130 # binman as a file called u-boot.dtb. To fix this, copy the file
2131 # over to the expected place.
Simon Glasse219aa42018-09-14 04:57:24 -06002132 start = 0
2133 for fname in ['u-boot.dtb.out', 'spl/u-boot-spl.dtb.out',
Simon Glass56d05412022-02-28 07:16:54 -07002134 'tpl/u-boot-tpl.dtb.out', 'vpl/u-boot-vpl.dtb.out']:
Simon Glasse219aa42018-09-14 04:57:24 -06002135 dtb = fdt.Fdt.FromData(data[start:])
2136 size = dtb._fdt_obj.totalsize()
Simon Glass80025522022-01-29 14:14:04 -07002137 pathname = tools.get_output_filename(os.path.split(fname)[1])
2138 outdata = tools.read_file(pathname)
Simon Glasse219aa42018-09-14 04:57:24 -06002139 name = os.path.split(fname)[0]
2140
2141 if name:
Simon Glass56d05412022-02-28 07:16:54 -07002142 orig_indata = self._GetDtbContentsForSpls(dtb_data, name)
Simon Glasse219aa42018-09-14 04:57:24 -06002143 else:
2144 orig_indata = dtb_data
2145 self.assertNotEqual(outdata, orig_indata,
2146 "Expected output file '%s' be updated" % pathname)
2147 self.assertEqual(outdata, data[start:start + size],
2148 "Expected output file '%s' to match output image" %
2149 pathname)
2150 start += size
2151 finally:
2152 self._ResetDtbs()
2153
Simon Glass7ba33592018-09-14 04:57:26 -06002154 def _decompress(self, data):
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +02002155 bintool = self.comp_bintools['lz4']
2156 return bintool.decompress(data)
Simon Glass7ba33592018-09-14 04:57:26 -06002157
2158 def testCompress(self):
2159 """Test compression of blobs"""
Simon Glass1de34482019-07-08 13:18:53 -06002160 self._CheckLz4()
Simon Glass511f6582018-10-01 12:22:30 -06002161 data, _, _, out_dtb_fname = self._DoReadFileDtb('083_compress.dts',
Simon Glass7ba33592018-09-14 04:57:26 -06002162 use_real_dtb=True, update_dtb=True)
2163 dtb = fdt.Fdt(out_dtb_fname)
2164 dtb.Scan()
2165 props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
2166 orig = self._decompress(data)
Brandon Maiera657bc62024-06-04 16:16:05 +00002167 self.assertEqual(COMPRESS_DATA, orig)
Simon Glass789b34402020-10-26 17:40:15 -06002168
2169 # Do a sanity check on various fields
2170 image = control.images['image']
2171 entries = image.GetEntries()
2172 self.assertEqual(1, len(entries))
2173
2174 entry = entries['blob']
2175 self.assertEqual(COMPRESS_DATA, entry.uncomp_data)
2176 self.assertEqual(len(COMPRESS_DATA), entry.uncomp_size)
2177 orig = self._decompress(entry.data)
2178 self.assertEqual(orig, entry.uncomp_data)
2179
Simon Glass72eeff12020-10-26 17:40:16 -06002180 self.assertEqual(image.data, entry.data)
2181
Simon Glass7ba33592018-09-14 04:57:26 -06002182 expected = {
2183 'blob:uncomp-size': len(COMPRESS_DATA),
2184 'blob:size': len(data),
2185 'size': len(data),
2186 }
2187 self.assertEqual(expected, props)
2188
Simon Glassac6328c2018-09-14 04:57:28 -06002189 def testFiles(self):
2190 """Test bringing in multiple files"""
Simon Glass511f6582018-10-01 12:22:30 -06002191 data = self._DoReadFile('084_files.dts')
Simon Glassac6328c2018-09-14 04:57:28 -06002192 self.assertEqual(FILES_DATA, data)
2193
2194 def testFilesCompress(self):
2195 """Test bringing in multiple files and compressing them"""
Simon Glass1de34482019-07-08 13:18:53 -06002196 self._CheckLz4()
Simon Glass511f6582018-10-01 12:22:30 -06002197 data = self._DoReadFile('085_files_compress.dts')
Simon Glassac6328c2018-09-14 04:57:28 -06002198
2199 image = control.images['image']
2200 entries = image.GetEntries()
2201 files = entries['files']
Simon Glass39dd2152019-07-08 14:25:47 -06002202 entries = files._entries
Simon Glassac6328c2018-09-14 04:57:28 -06002203
Simon Glass303f62f2019-05-17 22:00:46 -06002204 orig = b''
Simon Glassac6328c2018-09-14 04:57:28 -06002205 for i in range(1, 3):
2206 key = '%d.dat' % i
2207 start = entries[key].image_pos
2208 len = entries[key].size
2209 chunk = data[start:start + len]
2210 orig += self._decompress(chunk)
2211
2212 self.assertEqual(FILES_DATA, orig)
2213
2214 def testFilesMissing(self):
2215 """Test missing files"""
2216 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002217 data = self._DoReadFile('086_files_none.dts')
Simon Glassac6328c2018-09-14 04:57:28 -06002218 self.assertIn("Node '/binman/files': Pattern \'files/*.none\' matched "
2219 'no files', str(e.exception))
2220
2221 def testFilesNoPattern(self):
2222 """Test missing files"""
2223 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002224 data = self._DoReadFile('087_files_no_pattern.dts')
Simon Glassac6328c2018-09-14 04:57:28 -06002225 self.assertIn("Node '/binman/files': Missing 'pattern' property",
2226 str(e.exception))
2227
Simon Glassdd156a42022-03-05 20:18:59 -07002228 def testExtendSize(self):
2229 """Test an extending entry"""
2230 data, _, map_data, _ = self._DoReadFileDtb('088_extend_size.dts',
Simon Glassfa79a812018-09-14 04:57:29 -06002231 map=True)
Simon Glass80025522022-01-29 14:14:04 -07002232 expect = (tools.get_bytes(ord('a'), 8) + U_BOOT_DATA +
2233 MRC_DATA + tools.get_bytes(ord('b'), 1) + U_BOOT_DATA +
2234 tools.get_bytes(ord('c'), 8) + U_BOOT_DATA +
2235 tools.get_bytes(ord('d'), 8))
Simon Glassfa79a812018-09-14 04:57:29 -06002236 self.assertEqual(expect, data)
2237 self.assertEqual('''ImagePos Offset Size Name
Simon Glass49cd2b32023-02-07 14:34:18 -0700223800000000 00000000 00000028 image
Simon Glassfa79a812018-09-14 04:57:29 -0600223900000000 00000000 00000008 fill
224000000008 00000008 00000004 u-boot
22410000000c 0000000c 00000004 section
22420000000c 00000000 00000003 intel-mrc
224300000010 00000010 00000004 u-boot2
224400000014 00000014 0000000c section2
224500000014 00000000 00000008 fill
22460000001c 00000008 00000004 u-boot
224700000020 00000020 00000008 fill2
2248''', map_data)
2249
Simon Glassdd156a42022-03-05 20:18:59 -07002250 def testExtendSizeBad(self):
2251 """Test an extending entry which fails to provide contents"""
Simon Glass14d64e32025-04-29 07:21:59 -06002252 with terminal.capture() as (stdout, stderr):
Simon Glasscd817d52018-09-14 04:57:36 -06002253 with self.assertRaises(ValueError) as e:
Simon Glassdd156a42022-03-05 20:18:59 -07002254 self._DoReadFileDtb('089_extend_size_bad.dts', map=True)
Simon Glassfa79a812018-09-14 04:57:29 -06002255 self.assertIn("Node '/binman/_testing': Cannot obtain contents when "
2256 'expanding entry', str(e.exception))
2257
Simon Glassae7cf032018-09-14 04:57:31 -06002258 def testHash(self):
2259 """Test hashing of the contents of an entry"""
Simon Glass511f6582018-10-01 12:22:30 -06002260 _, _, _, out_dtb_fname = self._DoReadFileDtb('090_hash.dts',
Simon Glassae7cf032018-09-14 04:57:31 -06002261 use_real_dtb=True, update_dtb=True)
2262 dtb = fdt.Fdt(out_dtb_fname)
2263 dtb.Scan()
2264 hash_node = dtb.GetNode('/binman/u-boot/hash').props['value']
2265 m = hashlib.sha256()
2266 m.update(U_BOOT_DATA)
Simon Glass303f62f2019-05-17 22:00:46 -06002267 self.assertEqual(m.digest(), b''.join(hash_node.value))
Simon Glassae7cf032018-09-14 04:57:31 -06002268
2269 def testHashNoAlgo(self):
2270 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002271 self._DoReadFileDtb('091_hash_no_algo.dts', update_dtb=True)
Simon Glassae7cf032018-09-14 04:57:31 -06002272 self.assertIn("Node \'/binman/u-boot\': Missing \'algo\' property for "
2273 'hash node', str(e.exception))
2274
2275 def testHashBadAlgo(self):
2276 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002277 self._DoReadFileDtb('092_hash_bad_algo.dts', update_dtb=True)
Simon Glass64af7c22022-02-08 10:59:44 -07002278 self.assertIn("Node '/binman/u-boot': Unknown hash algorithm 'invalid'",
Simon Glassae7cf032018-09-14 04:57:31 -06002279 str(e.exception))
2280
2281 def testHashSection(self):
2282 """Test hashing of the contents of an entry"""
Simon Glass511f6582018-10-01 12:22:30 -06002283 _, _, _, out_dtb_fname = self._DoReadFileDtb('099_hash_section.dts',
Simon Glassae7cf032018-09-14 04:57:31 -06002284 use_real_dtb=True, update_dtb=True)
2285 dtb = fdt.Fdt(out_dtb_fname)
2286 dtb.Scan()
2287 hash_node = dtb.GetNode('/binman/section/hash').props['value']
2288 m = hashlib.sha256()
2289 m.update(U_BOOT_DATA)
Simon Glass80025522022-01-29 14:14:04 -07002290 m.update(tools.get_bytes(ord('a'), 16))
Simon Glass303f62f2019-05-17 22:00:46 -06002291 self.assertEqual(m.digest(), b''.join(hash_node.value))
Simon Glassae7cf032018-09-14 04:57:31 -06002292
Simon Glass3fb4f422018-09-14 04:57:32 -06002293 def testPackUBootTplMicrocode(self):
2294 """Test that x86 microcode can be handled correctly in TPL
2295
2296 We expect to see the following in the image, in order:
2297 u-boot-tpl-nodtb.bin with a microcode pointer inserted at the correct
2298 place
2299 u-boot-tpl.dtb with the microcode removed
2300 the microcode
2301 """
Simon Glass3eb5b202019-08-24 07:23:00 -06002302 self._SetupTplElf('u_boot_ucode_ptr')
Simon Glass511f6582018-10-01 12:22:30 -06002303 first, pos_and_size = self._RunMicrocodeTest('093_x86_tpl_ucode.dts',
Simon Glass3fb4f422018-09-14 04:57:32 -06002304 U_BOOT_TPL_NODTB_DATA)
Simon Glass303f62f2019-05-17 22:00:46 -06002305 self.assertEqual(b'tplnodtb with microc' + pos_and_size +
2306 b'ter somewhere in here', first)
Simon Glass3fb4f422018-09-14 04:57:32 -06002307
Simon Glassc64aea52018-09-14 04:57:34 -06002308 def testFmapX86(self):
2309 """Basic test of generation of a flashrom fmap"""
Simon Glass511f6582018-10-01 12:22:30 -06002310 data = self._DoReadFile('094_fmap_x86.dts')
Simon Glassc64aea52018-09-14 04:57:34 -06002311 fhdr, fentries = fmap_util.DecodeFmap(data[32:])
Simon Glass80025522022-01-29 14:14:04 -07002312 expected = U_BOOT_DATA + MRC_DATA + tools.get_bytes(ord('a'), 32 - 7)
Simon Glassc64aea52018-09-14 04:57:34 -06002313 self.assertEqual(expected, data[:32])
2314 fhdr, fentries = fmap_util.DecodeFmap(data[32:])
2315
2316 self.assertEqual(0x100, fhdr.image_size)
Simon Glassed836ac2025-02-26 09:26:17 -07002317 base = (1 << 32) - 0x100
Simon Glassc64aea52018-09-14 04:57:34 -06002318
Simon Glassed836ac2025-02-26 09:26:17 -07002319 self.assertEqual(base, fentries[0].offset)
Simon Glassc64aea52018-09-14 04:57:34 -06002320 self.assertEqual(4, fentries[0].size)
Simon Glass303f62f2019-05-17 22:00:46 -06002321 self.assertEqual(b'U_BOOT', fentries[0].name)
Simon Glassc64aea52018-09-14 04:57:34 -06002322
Simon Glassed836ac2025-02-26 09:26:17 -07002323 self.assertEqual(base + 4, fentries[1].offset)
Simon Glassc64aea52018-09-14 04:57:34 -06002324 self.assertEqual(3, fentries[1].size)
Simon Glass303f62f2019-05-17 22:00:46 -06002325 self.assertEqual(b'INTEL_MRC', fentries[1].name)
Simon Glassc64aea52018-09-14 04:57:34 -06002326
Simon Glassed836ac2025-02-26 09:26:17 -07002327 self.assertEqual(base + 32, fentries[2].offset)
Simon Glassc64aea52018-09-14 04:57:34 -06002328 self.assertEqual(fmap_util.FMAP_HEADER_LEN +
2329 fmap_util.FMAP_AREA_LEN * 3, fentries[2].size)
Simon Glass303f62f2019-05-17 22:00:46 -06002330 self.assertEqual(b'FMAP', fentries[2].name)
Simon Glassc64aea52018-09-14 04:57:34 -06002331
2332 def testFmapX86Section(self):
2333 """Basic test of generation of a flashrom fmap"""
Simon Glass511f6582018-10-01 12:22:30 -06002334 data = self._DoReadFile('095_fmap_x86_section.dts')
Simon Glass80025522022-01-29 14:14:04 -07002335 expected = U_BOOT_DATA + MRC_DATA + tools.get_bytes(ord('b'), 32 - 7)
Simon Glassc64aea52018-09-14 04:57:34 -06002336 self.assertEqual(expected, data[:32])
2337 fhdr, fentries = fmap_util.DecodeFmap(data[36:])
2338
Simon Glassb1d414c2021-04-03 11:05:10 +13002339 self.assertEqual(0x180, fhdr.image_size)
Simon Glassed836ac2025-02-26 09:26:17 -07002340 base = (1 << 32) - 0x180
Simon Glassb1d414c2021-04-03 11:05:10 +13002341 expect_size = fmap_util.FMAP_HEADER_LEN + fmap_util.FMAP_AREA_LEN * 4
Simon Glass82059c22021-04-03 11:05:09 +13002342 fiter = iter(fentries)
Simon Glassc64aea52018-09-14 04:57:34 -06002343
Simon Glass82059c22021-04-03 11:05:09 +13002344 fentry = next(fiter)
2345 self.assertEqual(b'U_BOOT', fentry.name)
Simon Glassed836ac2025-02-26 09:26:17 -07002346 self.assertEqual(base, fentry.offset)
Simon Glass82059c22021-04-03 11:05:09 +13002347 self.assertEqual(4, fentry.size)
Simon Glassc64aea52018-09-14 04:57:34 -06002348
Simon Glass82059c22021-04-03 11:05:09 +13002349 fentry = next(fiter)
Simon Glassb1d414c2021-04-03 11:05:10 +13002350 self.assertEqual(b'SECTION', fentry.name)
Simon Glassed836ac2025-02-26 09:26:17 -07002351 self.assertEqual(base + 4, fentry.offset)
Simon Glassb1d414c2021-04-03 11:05:10 +13002352 self.assertEqual(0x20 + expect_size, fentry.size)
2353
2354 fentry = next(fiter)
Simon Glass82059c22021-04-03 11:05:09 +13002355 self.assertEqual(b'INTEL_MRC', fentry.name)
Simon Glassed836ac2025-02-26 09:26:17 -07002356 self.assertEqual(base + 4, fentry.offset)
Simon Glass82059c22021-04-03 11:05:09 +13002357 self.assertEqual(3, fentry.size)
Simon Glassc64aea52018-09-14 04:57:34 -06002358
Simon Glass82059c22021-04-03 11:05:09 +13002359 fentry = next(fiter)
2360 self.assertEqual(b'FMAP', fentry.name)
Simon Glassed836ac2025-02-26 09:26:17 -07002361 self.assertEqual(base + 36, fentry.offset)
Simon Glass82059c22021-04-03 11:05:09 +13002362 self.assertEqual(expect_size, fentry.size)
Simon Glassc64aea52018-09-14 04:57:34 -06002363
Simon Glassb1714232018-09-14 04:57:35 -06002364 def testElf(self):
2365 """Basic test of ELF entries"""
Simon Glass7057d022018-10-01 21:12:47 -06002366 self._SetupSplElf()
Simon Glass3eb5b202019-08-24 07:23:00 -06002367 self._SetupTplElf()
Simon Glassf6290892019-08-24 07:22:53 -06002368 with open(self.ElfTestFile('bss_data'), 'rb') as fd:
Simon Glassb1714232018-09-14 04:57:35 -06002369 TestFunctional._MakeInputFile('-boot', fd.read())
Simon Glass511f6582018-10-01 12:22:30 -06002370 data = self._DoReadFile('096_elf.dts')
Simon Glassb1714232018-09-14 04:57:35 -06002371
Simon Glass0d673792019-07-08 13:18:25 -06002372 def testElfStrip(self):
Simon Glassb1714232018-09-14 04:57:35 -06002373 """Basic test of ELF entries"""
Simon Glass7057d022018-10-01 21:12:47 -06002374 self._SetupSplElf()
Simon Glassf6290892019-08-24 07:22:53 -06002375 with open(self.ElfTestFile('bss_data'), 'rb') as fd:
Simon Glassb1714232018-09-14 04:57:35 -06002376 TestFunctional._MakeInputFile('-boot', fd.read())
Simon Glass511f6582018-10-01 12:22:30 -06002377 data = self._DoReadFile('097_elf_strip.dts')
Simon Glassb1714232018-09-14 04:57:35 -06002378
Simon Glasscd817d52018-09-14 04:57:36 -06002379 def testPackOverlapMap(self):
2380 """Test that overlapping regions are detected"""
Simon Glass14d64e32025-04-29 07:21:59 -06002381 with terminal.capture() as (stdout, stderr):
Simon Glasscd817d52018-09-14 04:57:36 -06002382 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002383 self._DoTestFile('014_pack_overlap.dts', map=True)
Simon Glass80025522022-01-29 14:14:04 -07002384 map_fname = tools.get_output_filename('image.map')
Simon Glasscd817d52018-09-14 04:57:36 -06002385 self.assertEqual("Wrote map file '%s' to show errors\n" % map_fname,
2386 stdout.getvalue())
2387
2388 # We should not get an inmage, but there should be a map file
Simon Glass80025522022-01-29 14:14:04 -07002389 self.assertFalse(os.path.exists(tools.get_output_filename('image.bin')))
Simon Glasscd817d52018-09-14 04:57:36 -06002390 self.assertTrue(os.path.exists(map_fname))
Simon Glass80025522022-01-29 14:14:04 -07002391 map_data = tools.read_file(map_fname, binary=False)
Simon Glasscd817d52018-09-14 04:57:36 -06002392 self.assertEqual('''ImagePos Offset Size Name
Simon Glass49cd2b32023-02-07 14:34:18 -07002393<none> 00000000 00000008 image
Simon Glasscd817d52018-09-14 04:57:36 -06002394<none> 00000000 00000004 u-boot
2395<none> 00000003 00000004 u-boot-align
2396''', map_data)
2397
Simon Glass0d673792019-07-08 13:18:25 -06002398 def testPackRefCode(self):
Simon Glass41902e42018-10-01 12:22:31 -06002399 """Test that an image with an Intel Reference code binary works"""
2400 data = self._DoReadFile('100_intel_refcode.dts')
2401 self.assertEqual(REFCODE_DATA, data[:len(REFCODE_DATA)])
2402
Simon Glasseb023b32019-04-25 21:58:39 -06002403 def testSectionOffset(self):
2404 """Tests use of a section with an offset"""
2405 data, _, map_data, _ = self._DoReadFileDtb('101_sections_offset.dts',
2406 map=True)
2407 self.assertEqual('''ImagePos Offset Size Name
Simon Glass49cd2b32023-02-07 14:34:18 -0700240800000000 00000000 00000038 image
Simon Glasseb023b32019-04-25 21:58:39 -0600240900000004 00000004 00000010 section@0
241000000004 00000000 00000004 u-boot
241100000018 00000018 00000010 section@1
241200000018 00000000 00000004 u-boot
24130000002c 0000002c 00000004 section@2
24140000002c 00000000 00000004 u-boot
2415''', map_data)
2416 self.assertEqual(data,
Simon Glass80025522022-01-29 14:14:04 -07002417 tools.get_bytes(0x26, 4) + U_BOOT_DATA +
2418 tools.get_bytes(0x21, 12) +
2419 tools.get_bytes(0x26, 4) + U_BOOT_DATA +
2420 tools.get_bytes(0x61, 12) +
2421 tools.get_bytes(0x26, 4) + U_BOOT_DATA +
2422 tools.get_bytes(0x26, 8))
Simon Glasseb023b32019-04-25 21:58:39 -06002423
Simon Glass1de34482019-07-08 13:18:53 -06002424 def testCbfsRaw(self):
2425 """Test base handling of a Coreboot Filesystem (CBFS)
2426
2427 The exact contents of the CBFS is verified by similar tests in
2428 cbfs_util_test.py. The tests here merely check that the files added to
2429 the CBFS can be found in the final image.
2430 """
2431 data = self._DoReadFile('102_cbfs_raw.dts')
2432 size = 0xb0
2433
2434 cbfs = cbfs_util.CbfsReader(data)
2435 self.assertEqual(size, cbfs.rom_size)
2436
2437 self.assertIn('u-boot-dtb', cbfs.files)
2438 cfile = cbfs.files['u-boot-dtb']
2439 self.assertEqual(U_BOOT_DTB_DATA, cfile.data)
2440
2441 def testCbfsArch(self):
2442 """Test on non-x86 architecture"""
2443 data = self._DoReadFile('103_cbfs_raw_ppc.dts')
2444 size = 0x100
2445
2446 cbfs = cbfs_util.CbfsReader(data)
2447 self.assertEqual(size, cbfs.rom_size)
2448
2449 self.assertIn('u-boot-dtb', cbfs.files)
2450 cfile = cbfs.files['u-boot-dtb']
2451 self.assertEqual(U_BOOT_DTB_DATA, cfile.data)
2452
2453 def testCbfsStage(self):
2454 """Tests handling of a Coreboot Filesystem (CBFS)"""
2455 if not elf.ELF_TOOLS:
2456 self.skipTest('Python elftools not available')
2457 elf_fname = os.path.join(self._indir, 'cbfs-stage.elf')
2458 elf.MakeElf(elf_fname, U_BOOT_DATA, U_BOOT_DTB_DATA)
2459 size = 0xb0
2460
2461 data = self._DoReadFile('104_cbfs_stage.dts')
2462 cbfs = cbfs_util.CbfsReader(data)
2463 self.assertEqual(size, cbfs.rom_size)
2464
2465 self.assertIn('u-boot', cbfs.files)
2466 cfile = cbfs.files['u-boot']
2467 self.assertEqual(U_BOOT_DATA + U_BOOT_DTB_DATA, cfile.data)
2468
2469 def testCbfsRawCompress(self):
2470 """Test handling of compressing raw files"""
2471 self._CheckLz4()
2472 data = self._DoReadFile('105_cbfs_raw_compress.dts')
2473 size = 0x140
2474
2475 cbfs = cbfs_util.CbfsReader(data)
2476 self.assertIn('u-boot', cbfs.files)
2477 cfile = cbfs.files['u-boot']
2478 self.assertEqual(COMPRESS_DATA, cfile.data)
2479
2480 def testCbfsBadArch(self):
2481 """Test handling of a bad architecture"""
2482 with self.assertRaises(ValueError) as e:
2483 self._DoReadFile('106_cbfs_bad_arch.dts')
2484 self.assertIn("Invalid architecture 'bad-arch'", str(e.exception))
2485
2486 def testCbfsNoSize(self):
2487 """Test handling of a missing size property"""
2488 with self.assertRaises(ValueError) as e:
2489 self._DoReadFile('107_cbfs_no_size.dts')
2490 self.assertIn('entry must have a size property', str(e.exception))
2491
Simon Glass3e28f4f2021-11-23 11:03:54 -07002492 def testCbfsNoContents(self):
Simon Glass1de34482019-07-08 13:18:53 -06002493 """Test handling of a CBFS entry which does not provide contentsy"""
2494 with self.assertRaises(ValueError) as e:
2495 self._DoReadFile('108_cbfs_no_contents.dts')
2496 self.assertIn('Could not complete processing of contents',
2497 str(e.exception))
2498
2499 def testCbfsBadCompress(self):
2500 """Test handling of a bad architecture"""
2501 with self.assertRaises(ValueError) as e:
2502 self._DoReadFile('109_cbfs_bad_compress.dts')
2503 self.assertIn("Invalid compression in 'u-boot': 'invalid-algo'",
2504 str(e.exception))
2505
2506 def testCbfsNamedEntries(self):
2507 """Test handling of named entries"""
2508 data = self._DoReadFile('110_cbfs_name.dts')
2509
2510 cbfs = cbfs_util.CbfsReader(data)
2511 self.assertIn('FRED', cbfs.files)
2512 cfile1 = cbfs.files['FRED']
2513 self.assertEqual(U_BOOT_DATA, cfile1.data)
2514
2515 self.assertIn('hello', cbfs.files)
2516 cfile2 = cbfs.files['hello']
2517 self.assertEqual(U_BOOT_DTB_DATA, cfile2.data)
2518
Simon Glass759af872019-07-08 13:18:54 -06002519 def _SetupIfwi(self, fname):
2520 """Set up to run an IFWI test
2521
2522 Args:
2523 fname: Filename of input file to provide (fitimage.bin or ifwi.bin)
2524 """
2525 self._SetupSplElf()
Simon Glass3eb5b202019-08-24 07:23:00 -06002526 self._SetupTplElf()
Simon Glass759af872019-07-08 13:18:54 -06002527
2528 # Intel Integrated Firmware Image (IFWI) file
2529 with gzip.open(self.TestFile('%s.gz' % fname), 'rb') as fd:
2530 data = fd.read()
2531 TestFunctional._MakeInputFile(fname,data)
2532
2533 def _CheckIfwi(self, data):
2534 """Check that an image with an IFWI contains the correct output
2535
2536 Args:
2537 data: Conents of output file
2538 """
Simon Glass80025522022-01-29 14:14:04 -07002539 expected_desc = tools.read_file(self.TestFile('descriptor.bin'))
Simon Glass759af872019-07-08 13:18:54 -06002540 if data[:0x1000] != expected_desc:
2541 self.fail('Expected descriptor binary at start of image')
2542
2543 # We expect to find the TPL wil in subpart IBBP entry IBBL
Simon Glass80025522022-01-29 14:14:04 -07002544 image_fname = tools.get_output_filename('image.bin')
2545 tpl_fname = tools.get_output_filename('tpl.out')
Simon Glass57c7a482022-01-09 20:14:01 -07002546 ifwitool = bintool.Bintool.create('ifwitool')
2547 ifwitool.extract(image_fname, 'IBBP', 'IBBL', tpl_fname)
Simon Glass759af872019-07-08 13:18:54 -06002548
Simon Glass80025522022-01-29 14:14:04 -07002549 tpl_data = tools.read_file(tpl_fname)
Simon Glassf55bd692019-08-24 07:22:51 -06002550 self.assertEqual(U_BOOT_TPL_DATA, tpl_data[:len(U_BOOT_TPL_DATA)])
Simon Glass759af872019-07-08 13:18:54 -06002551
2552 def testPackX86RomIfwi(self):
2553 """Test that an x86 ROM with Integrated Firmware Image can be created"""
2554 self._SetupIfwi('fitimage.bin')
Simon Glass1d167762019-08-24 07:23:01 -06002555 data = self._DoReadFile('111_x86_rom_ifwi.dts')
Simon Glass759af872019-07-08 13:18:54 -06002556 self._CheckIfwi(data)
2557
2558 def testPackX86RomIfwiNoDesc(self):
2559 """Test that an x86 ROM with IFWI can be created from an ifwi.bin file"""
2560 self._SetupIfwi('ifwi.bin')
Simon Glass1d167762019-08-24 07:23:01 -06002561 data = self._DoReadFile('112_x86_rom_ifwi_nodesc.dts')
Simon Glass759af872019-07-08 13:18:54 -06002562 self._CheckIfwi(data)
2563
2564 def testPackX86RomIfwiNoData(self):
2565 """Test that an x86 ROM with IFWI handles missing data"""
2566 self._SetupIfwi('ifwi.bin')
2567 with self.assertRaises(ValueError) as e:
Simon Glass1d167762019-08-24 07:23:01 -06002568 data = self._DoReadFile('113_x86_rom_ifwi_nodata.dts')
Simon Glass759af872019-07-08 13:18:54 -06002569 self.assertIn('Could not complete processing of contents',
2570 str(e.exception))
Simon Glass91710b32018-07-17 13:25:32 -06002571
Simon Glass66152ce2022-01-09 20:14:09 -07002572 def testIfwiMissing(self):
2573 """Test that binman still produces an image if ifwitool is missing"""
2574 self._SetupIfwi('fitimage.bin')
Simon Glass14d64e32025-04-29 07:21:59 -06002575 with terminal.capture() as (_, stderr):
Simon Glass66152ce2022-01-09 20:14:09 -07002576 self._DoTestFile('111_x86_rom_ifwi.dts',
2577 force_missing_bintools='ifwitool')
2578 err = stderr.getvalue()
2579 self.assertRegex(err,
Simon Glass49cd2b32023-02-07 14:34:18 -07002580 "Image 'image'.*missing bintools.*: ifwitool")
Simon Glass66152ce2022-01-09 20:14:09 -07002581
Simon Glassc2f1aed2019-07-08 13:18:56 -06002582 def testCbfsOffset(self):
2583 """Test a CBFS with files at particular offsets
2584
2585 Like all CFBS tests, this is just checking the logic that calls
2586 cbfs_util. See cbfs_util_test for fully tests (e.g. test_cbfs_offset()).
2587 """
2588 data = self._DoReadFile('114_cbfs_offset.dts')
2589 size = 0x200
2590
2591 cbfs = cbfs_util.CbfsReader(data)
2592 self.assertEqual(size, cbfs.rom_size)
2593
2594 self.assertIn('u-boot', cbfs.files)
2595 cfile = cbfs.files['u-boot']
2596 self.assertEqual(U_BOOT_DATA, cfile.data)
2597 self.assertEqual(0x40, cfile.cbfs_offset)
2598
2599 self.assertIn('u-boot-dtb', cbfs.files)
2600 cfile2 = cbfs.files['u-boot-dtb']
2601 self.assertEqual(U_BOOT_DTB_DATA, cfile2.data)
2602 self.assertEqual(0x140, cfile2.cbfs_offset)
2603
Simon Glass0f621332019-07-08 14:25:27 -06002604 def testFdtmap(self):
2605 """Test an FDT map can be inserted in the image"""
2606 data = self.data = self._DoReadFileRealDtb('115_fdtmap.dts')
2607 fdtmap_data = data[len(U_BOOT_DATA):]
2608 magic = fdtmap_data[:8]
Simon Glassc5fd10a2019-10-31 07:43:03 -06002609 self.assertEqual(b'_FDTMAP_', magic)
Simon Glass80025522022-01-29 14:14:04 -07002610 self.assertEqual(tools.get_bytes(0, 8), fdtmap_data[8:16])
Simon Glass0f621332019-07-08 14:25:27 -06002611
2612 fdt_data = fdtmap_data[16:]
2613 dtb = fdt.Fdt.FromData(fdt_data)
2614 dtb.Scan()
Simon Glass2c6adba2019-07-20 12:23:47 -06002615 props = self._GetPropTree(dtb, BASE_DTB_PROPS, prefix='/')
Simon Glass0f621332019-07-08 14:25:27 -06002616 self.assertEqual({
2617 'image-pos': 0,
2618 'offset': 0,
2619 'u-boot:offset': 0,
2620 'u-boot:size': len(U_BOOT_DATA),
2621 'u-boot:image-pos': 0,
2622 'fdtmap:image-pos': 4,
2623 'fdtmap:offset': 4,
2624 'fdtmap:size': len(fdtmap_data),
2625 'size': len(data),
2626 }, props)
2627
2628 def testFdtmapNoMatch(self):
2629 """Check handling of an FDT map when the section cannot be found"""
2630 self.data = self._DoReadFileRealDtb('115_fdtmap.dts')
2631
2632 # Mangle the section name, which should cause a mismatch between the
2633 # correct FDT path and the one expected by the section
2634 image = control.images['image']
Simon Glasscec34ba2019-07-08 14:25:28 -06002635 image._node.path += '-suffix'
Simon Glass0f621332019-07-08 14:25:27 -06002636 entries = image.GetEntries()
2637 fdtmap = entries['fdtmap']
2638 with self.assertRaises(ValueError) as e:
2639 fdtmap._GetFdtmap()
2640 self.assertIn("Cannot locate node for path '/binman-suffix'",
2641 str(e.exception))
2642
Simon Glasscec34ba2019-07-08 14:25:28 -06002643 def testFdtmapHeader(self):
2644 """Test an FDT map and image header can be inserted in the image"""
2645 data = self.data = self._DoReadFileRealDtb('116_fdtmap_hdr.dts')
2646 fdtmap_pos = len(U_BOOT_DATA)
2647 fdtmap_data = data[fdtmap_pos:]
2648 fdt_data = fdtmap_data[16:]
2649 dtb = fdt.Fdt.FromData(fdt_data)
2650 fdt_size = dtb.GetFdtObj().totalsize()
2651 hdr_data = data[-8:]
Simon Glassc5fd10a2019-10-31 07:43:03 -06002652 self.assertEqual(b'BinM', hdr_data[:4])
Simon Glasscec34ba2019-07-08 14:25:28 -06002653 offset = struct.unpack('<I', hdr_data[4:])[0] & 0xffffffff
2654 self.assertEqual(fdtmap_pos - 0x400, offset - (1 << 32))
2655
2656 def testFdtmapHeaderStart(self):
2657 """Test an image header can be inserted at the image start"""
2658 data = self.data = self._DoReadFileRealDtb('117_fdtmap_hdr_start.dts')
2659 fdtmap_pos = 0x100 + len(U_BOOT_DATA)
2660 hdr_data = data[:8]
Simon Glassc5fd10a2019-10-31 07:43:03 -06002661 self.assertEqual(b'BinM', hdr_data[:4])
Simon Glasscec34ba2019-07-08 14:25:28 -06002662 offset = struct.unpack('<I', hdr_data[4:])[0]
2663 self.assertEqual(fdtmap_pos, offset)
2664
2665 def testFdtmapHeaderPos(self):
2666 """Test an image header can be inserted at a chosen position"""
2667 data = self.data = self._DoReadFileRealDtb('118_fdtmap_hdr_pos.dts')
2668 fdtmap_pos = 0x100 + len(U_BOOT_DATA)
2669 hdr_data = data[0x80:0x88]
Simon Glassc5fd10a2019-10-31 07:43:03 -06002670 self.assertEqual(b'BinM', hdr_data[:4])
Simon Glasscec34ba2019-07-08 14:25:28 -06002671 offset = struct.unpack('<I', hdr_data[4:])[0]
2672 self.assertEqual(fdtmap_pos, offset)
2673
2674 def testHeaderMissingFdtmap(self):
2675 """Test an image header requires an fdtmap"""
2676 with self.assertRaises(ValueError) as e:
2677 self.data = self._DoReadFileRealDtb('119_fdtmap_hdr_missing.dts')
2678 self.assertIn("'image_header' section must have an 'fdtmap' sibling",
2679 str(e.exception))
2680
2681 def testHeaderNoLocation(self):
2682 """Test an image header with a no specified location is detected"""
2683 with self.assertRaises(ValueError) as e:
2684 self.data = self._DoReadFileRealDtb('120_hdr_no_location.dts')
2685 self.assertIn("Invalid location 'None', expected 'start' or 'end'",
2686 str(e.exception))
2687
Simon Glasse61b6f62019-07-08 14:25:37 -06002688 def testEntryExpand(self):
Simon Glassdd156a42022-03-05 20:18:59 -07002689 """Test extending an entry after it is packed"""
2690 data = self._DoReadFile('121_entry_extend.dts')
Simon Glass8c702fb2019-07-20 12:23:57 -06002691 self.assertEqual(b'aaa', data[:3])
2692 self.assertEqual(U_BOOT_DATA, data[3:3 + len(U_BOOT_DATA)])
2693 self.assertEqual(b'aaa', data[-3:])
Simon Glasse61b6f62019-07-08 14:25:37 -06002694
Simon Glassdd156a42022-03-05 20:18:59 -07002695 def testEntryExtendBad(self):
2696 """Test extending an entry after it is packed, twice"""
Simon Glasse61b6f62019-07-08 14:25:37 -06002697 with self.assertRaises(ValueError) as e:
Simon Glassdd156a42022-03-05 20:18:59 -07002698 self._DoReadFile('122_entry_extend_twice.dts')
Simon Glass9d8ee322019-07-20 12:23:58 -06002699 self.assertIn("Image '/binman': Entries changed size after packing",
Simon Glasse61b6f62019-07-08 14:25:37 -06002700 str(e.exception))
2701
Simon Glassdd156a42022-03-05 20:18:59 -07002702 def testEntryExtendSection(self):
2703 """Test extending an entry within a section after it is packed"""
2704 data = self._DoReadFile('123_entry_extend_section.dts')
Simon Glass8c702fb2019-07-20 12:23:57 -06002705 self.assertEqual(b'aaa', data[:3])
2706 self.assertEqual(U_BOOT_DATA, data[3:3 + len(U_BOOT_DATA)])
2707 self.assertEqual(b'aaa', data[-3:])
Simon Glasse61b6f62019-07-08 14:25:37 -06002708
Simon Glass90d29682019-07-08 14:25:38 -06002709 def testCompressDtb(self):
2710 """Test that compress of device-tree files is supported"""
2711 self._CheckLz4()
2712 data = self.data = self._DoReadFileRealDtb('124_compress_dtb.dts')
2713 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
2714 comp_data = data[len(U_BOOT_DATA):]
2715 orig = self._decompress(comp_data)
2716 dtb = fdt.Fdt.FromData(orig)
2717 dtb.Scan()
2718 props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
2719 expected = {
2720 'u-boot:size': len(U_BOOT_DATA),
2721 'u-boot-dtb:uncomp-size': len(orig),
2722 'u-boot-dtb:size': len(comp_data),
2723 'size': len(data),
2724 }
2725 self.assertEqual(expected, props)
2726
Simon Glass151bbbf2019-07-08 14:25:41 -06002727 def testCbfsUpdateFdt(self):
2728 """Test that we can update the device tree with CBFS offset/size info"""
2729 self._CheckLz4()
2730 data, _, _, out_dtb_fname = self._DoReadFileDtb('125_cbfs_update.dts',
2731 update_dtb=True)
2732 dtb = fdt.Fdt(out_dtb_fname)
2733 dtb.Scan()
Simon Glass2c6adba2019-07-20 12:23:47 -06002734 props = self._GetPropTree(dtb, BASE_DTB_PROPS + ['uncomp-size'])
Simon Glass151bbbf2019-07-08 14:25:41 -06002735 del props['cbfs/u-boot:size']
2736 self.assertEqual({
2737 'offset': 0,
2738 'size': len(data),
2739 'image-pos': 0,
2740 'cbfs:offset': 0,
2741 'cbfs:size': len(data),
2742 'cbfs:image-pos': 0,
Simon Glassfa144222023-10-14 14:40:28 -06002743 'cbfs/u-boot:offset': 0x30,
Simon Glass151bbbf2019-07-08 14:25:41 -06002744 'cbfs/u-boot:uncomp-size': len(U_BOOT_DATA),
Simon Glassfa144222023-10-14 14:40:28 -06002745 'cbfs/u-boot:image-pos': 0x30,
2746 'cbfs/u-boot-dtb:offset': 0xa4,
Simon Glass151bbbf2019-07-08 14:25:41 -06002747 'cbfs/u-boot-dtb:size': len(U_BOOT_DATA),
Simon Glassfa144222023-10-14 14:40:28 -06002748 'cbfs/u-boot-dtb:image-pos': 0xa4,
Simon Glass151bbbf2019-07-08 14:25:41 -06002749 }, props)
2750
Simon Glass3c9b4f22019-07-08 14:25:42 -06002751 def testCbfsBadType(self):
2752 """Test an image header with a no specified location is detected"""
2753 with self.assertRaises(ValueError) as e:
2754 self._DoReadFile('126_cbfs_bad_type.dts')
2755 self.assertIn("Unknown cbfs-type 'badtype'", str(e.exception))
2756
Simon Glass6b156f82019-07-08 14:25:43 -06002757 def testList(self):
2758 """Test listing the files in an image"""
2759 self._CheckLz4()
2760 data = self._DoReadFile('127_list.dts')
2761 image = control.images['image']
2762 entries = image.BuildEntryList()
2763 self.assertEqual(7, len(entries))
2764
2765 ent = entries[0]
2766 self.assertEqual(0, ent.indent)
Simon Glass49cd2b32023-02-07 14:34:18 -07002767 self.assertEqual('image', ent.name)
Simon Glass6b156f82019-07-08 14:25:43 -06002768 self.assertEqual('section', ent.etype)
2769 self.assertEqual(len(data), ent.size)
2770 self.assertEqual(0, ent.image_pos)
2771 self.assertEqual(None, ent.uncomp_size)
Simon Glass39dd2152019-07-08 14:25:47 -06002772 self.assertEqual(0, ent.offset)
Simon Glass6b156f82019-07-08 14:25:43 -06002773
2774 ent = entries[1]
2775 self.assertEqual(1, ent.indent)
2776 self.assertEqual('u-boot', ent.name)
2777 self.assertEqual('u-boot', ent.etype)
2778 self.assertEqual(len(U_BOOT_DATA), ent.size)
2779 self.assertEqual(0, ent.image_pos)
2780 self.assertEqual(None, ent.uncomp_size)
2781 self.assertEqual(0, ent.offset)
2782
2783 ent = entries[2]
2784 self.assertEqual(1, ent.indent)
2785 self.assertEqual('section', ent.name)
2786 self.assertEqual('section', ent.etype)
2787 section_size = ent.size
2788 self.assertEqual(0x100, ent.image_pos)
2789 self.assertEqual(None, ent.uncomp_size)
Simon Glass39dd2152019-07-08 14:25:47 -06002790 self.assertEqual(0x100, ent.offset)
Simon Glass6b156f82019-07-08 14:25:43 -06002791
2792 ent = entries[3]
2793 self.assertEqual(2, ent.indent)
2794 self.assertEqual('cbfs', ent.name)
2795 self.assertEqual('cbfs', ent.etype)
2796 self.assertEqual(0x400, ent.size)
2797 self.assertEqual(0x100, ent.image_pos)
2798 self.assertEqual(None, ent.uncomp_size)
2799 self.assertEqual(0, ent.offset)
2800
2801 ent = entries[4]
2802 self.assertEqual(3, ent.indent)
2803 self.assertEqual('u-boot', ent.name)
2804 self.assertEqual('u-boot', ent.etype)
2805 self.assertEqual(len(U_BOOT_DATA), ent.size)
2806 self.assertEqual(0x138, ent.image_pos)
2807 self.assertEqual(None, ent.uncomp_size)
2808 self.assertEqual(0x38, ent.offset)
2809
2810 ent = entries[5]
2811 self.assertEqual(3, ent.indent)
2812 self.assertEqual('u-boot-dtb', ent.name)
2813 self.assertEqual('text', ent.etype)
2814 self.assertGreater(len(COMPRESS_DATA), ent.size)
2815 self.assertEqual(0x178, ent.image_pos)
2816 self.assertEqual(len(COMPRESS_DATA), ent.uncomp_size)
2817 self.assertEqual(0x78, ent.offset)
2818
2819 ent = entries[6]
2820 self.assertEqual(2, ent.indent)
2821 self.assertEqual('u-boot-dtb', ent.name)
2822 self.assertEqual('u-boot-dtb', ent.etype)
2823 self.assertEqual(0x500, ent.image_pos)
2824 self.assertEqual(len(U_BOOT_DTB_DATA), ent.uncomp_size)
2825 dtb_size = ent.size
2826 # Compressing this data expands it since headers are added
2827 self.assertGreater(dtb_size, len(U_BOOT_DTB_DATA))
2828 self.assertEqual(0x400, ent.offset)
2829
2830 self.assertEqual(len(data), 0x100 + section_size)
2831 self.assertEqual(section_size, 0x400 + dtb_size)
2832
Simon Glass8d8bf4e2019-07-08 14:25:44 -06002833 def testFindFdtmap(self):
2834 """Test locating an FDT map in an image"""
2835 self._CheckLz4()
2836 data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
2837 image = control.images['image']
2838 entries = image.GetEntries()
2839 entry = entries['fdtmap']
2840 self.assertEqual(entry.image_pos, fdtmap.LocateFdtmap(data))
2841
2842 def testFindFdtmapMissing(self):
2843 """Test failing to locate an FDP map"""
2844 data = self._DoReadFile('005_simple.dts')
2845 self.assertEqual(None, fdtmap.LocateFdtmap(data))
2846
Simon Glassed39a3c2019-07-08 14:25:45 -06002847 def testFindImageHeader(self):
2848 """Test locating a image header"""
2849 self._CheckLz4()
Simon Glassb8424fa2019-07-08 14:25:46 -06002850 data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
Simon Glassed39a3c2019-07-08 14:25:45 -06002851 image = control.images['image']
2852 entries = image.GetEntries()
2853 entry = entries['fdtmap']
2854 # The header should point to the FDT map
2855 self.assertEqual(entry.image_pos, image_header.LocateHeaderOffset(data))
2856
2857 def testFindImageHeaderStart(self):
2858 """Test locating a image header located at the start of an image"""
Simon Glassb8424fa2019-07-08 14:25:46 -06002859 data = self.data = self._DoReadFileRealDtb('117_fdtmap_hdr_start.dts')
Simon Glassed39a3c2019-07-08 14:25:45 -06002860 image = control.images['image']
2861 entries = image.GetEntries()
2862 entry = entries['fdtmap']
2863 # The header should point to the FDT map
2864 self.assertEqual(entry.image_pos, image_header.LocateHeaderOffset(data))
2865
2866 def testFindImageHeaderMissing(self):
2867 """Test failing to locate an image header"""
2868 data = self._DoReadFile('005_simple.dts')
2869 self.assertEqual(None, image_header.LocateHeaderOffset(data))
2870
Simon Glassb8424fa2019-07-08 14:25:46 -06002871 def testReadImage(self):
2872 """Test reading an image and accessing its FDT map"""
2873 self._CheckLz4()
2874 data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
Simon Glass80025522022-01-29 14:14:04 -07002875 image_fname = tools.get_output_filename('image.bin')
Simon Glassb8424fa2019-07-08 14:25:46 -06002876 orig_image = control.images['image']
2877 image = Image.FromFile(image_fname)
2878 self.assertEqual(orig_image.GetEntries().keys(),
2879 image.GetEntries().keys())
2880
2881 orig_entry = orig_image.GetEntries()['fdtmap']
2882 entry = image.GetEntries()['fdtmap']
Brandon Maiera657bc62024-06-04 16:16:05 +00002883 self.assertEqual(orig_entry.offset, entry.offset)
2884 self.assertEqual(orig_entry.size, entry.size)
2885 self.assertEqual(orig_entry.image_pos, entry.image_pos)
Simon Glassb8424fa2019-07-08 14:25:46 -06002886
2887 def testReadImageNoHeader(self):
2888 """Test accessing an image's FDT map without an image header"""
2889 self._CheckLz4()
2890 data = self._DoReadFileRealDtb('129_decode_image_nohdr.dts')
Simon Glass80025522022-01-29 14:14:04 -07002891 image_fname = tools.get_output_filename('image.bin')
Simon Glassb8424fa2019-07-08 14:25:46 -06002892 image = Image.FromFile(image_fname)
2893 self.assertTrue(isinstance(image, Image))
Simon Glass072959a2019-07-20 12:23:50 -06002894 self.assertEqual('image', image.image_name[-5:])
Simon Glassb8424fa2019-07-08 14:25:46 -06002895
2896 def testReadImageFail(self):
2897 """Test failing to read an image image's FDT map"""
2898 self._DoReadFile('005_simple.dts')
Simon Glass80025522022-01-29 14:14:04 -07002899 image_fname = tools.get_output_filename('image.bin')
Simon Glassb8424fa2019-07-08 14:25:46 -06002900 with self.assertRaises(ValueError) as e:
2901 image = Image.FromFile(image_fname)
2902 self.assertIn("Cannot find FDT map in image", str(e.exception))
Simon Glassc2f1aed2019-07-08 13:18:56 -06002903
Simon Glassb2fd11d2019-07-08 14:25:48 -06002904 def testListCmd(self):
2905 """Test listing the files in an image using an Fdtmap"""
2906 self._CheckLz4()
2907 data = self._DoReadFileRealDtb('130_list_fdtmap.dts')
2908
2909 # lz4 compression size differs depending on the version
2910 image = control.images['image']
2911 entries = image.GetEntries()
2912 section_size = entries['section'].size
2913 fdt_size = entries['section'].GetEntries()['u-boot-dtb'].size
2914 fdtmap_offset = entries['fdtmap'].offset
2915
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01002916 tmpdir = None
Simon Glassb3d6fc72019-07-20 12:24:10 -06002917 try:
2918 tmpdir, updated_fname = self._SetupImageInTmpdir()
Simon Glass14d64e32025-04-29 07:21:59 -06002919 with terminal.capture() as (stdout, stderr):
Simon Glassb3d6fc72019-07-20 12:24:10 -06002920 self._DoBinman('ls', '-i', updated_fname)
2921 finally:
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01002922 if tmpdir:
2923 shutil.rmtree(tmpdir)
Simon Glassb2fd11d2019-07-08 14:25:48 -06002924 lines = stdout.getvalue().splitlines()
2925 expected = [
2926'Name Image-pos Size Entry-type Offset Uncomp-size',
2927'----------------------------------------------------------------------',
Simon Glass49cd2b32023-02-07 14:34:18 -07002928'image 0 c00 section 0',
Simon Glassb2fd11d2019-07-08 14:25:48 -06002929' u-boot 0 4 u-boot 0',
2930' section 100 %x section 100' % section_size,
2931' cbfs 100 400 cbfs 0',
Simon Glassfa144222023-10-14 14:40:28 -06002932' u-boot 120 4 u-boot 20',
Simon Glassc5fd10a2019-10-31 07:43:03 -06002933' u-boot-dtb 180 105 u-boot-dtb 80 3c9',
Simon Glassb2fd11d2019-07-08 14:25:48 -06002934' u-boot-dtb 500 %x u-boot-dtb 400 3c9' % fdt_size,
Simon Glassc5fd10a2019-10-31 07:43:03 -06002935' fdtmap %x 3bd fdtmap %x' %
Simon Glassb2fd11d2019-07-08 14:25:48 -06002936 (fdtmap_offset, fdtmap_offset),
2937' image-header bf8 8 image-header bf8',
2938 ]
2939 self.assertEqual(expected, lines)
2940
2941 def testListCmdFail(self):
2942 """Test failing to list an image"""
2943 self._DoReadFile('005_simple.dts')
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01002944 tmpdir = None
Simon Glassb3d6fc72019-07-20 12:24:10 -06002945 try:
2946 tmpdir, updated_fname = self._SetupImageInTmpdir()
2947 with self.assertRaises(ValueError) as e:
2948 self._DoBinman('ls', '-i', updated_fname)
2949 finally:
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01002950 if tmpdir:
2951 shutil.rmtree(tmpdir)
Simon Glassb2fd11d2019-07-08 14:25:48 -06002952 self.assertIn("Cannot find FDT map in image", str(e.exception))
2953
2954 def _RunListCmd(self, paths, expected):
2955 """List out entries and check the result
2956
2957 Args:
2958 paths: List of paths to pass to the list command
2959 expected: Expected list of filenames to be returned, in order
2960 """
2961 self._CheckLz4()
2962 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07002963 image_fname = tools.get_output_filename('image.bin')
Simon Glassb2fd11d2019-07-08 14:25:48 -06002964 image = Image.FromFile(image_fname)
2965 lines = image.GetListEntries(paths)[1]
2966 files = [line[0].strip() for line in lines[1:]]
2967 self.assertEqual(expected, files)
2968
2969 def testListCmdSection(self):
2970 """Test listing the files in a section"""
2971 self._RunListCmd(['section'],
2972 ['section', 'cbfs', 'u-boot', 'u-boot-dtb', 'u-boot-dtb'])
2973
2974 def testListCmdFile(self):
2975 """Test listing a particular file"""
2976 self._RunListCmd(['*u-boot-dtb'], ['u-boot-dtb', 'u-boot-dtb'])
2977
2978 def testListCmdWildcard(self):
2979 """Test listing a wildcarded file"""
2980 self._RunListCmd(['*boot*'],
2981 ['u-boot', 'u-boot', 'u-boot-dtb', 'u-boot-dtb'])
2982
2983 def testListCmdWildcardMulti(self):
2984 """Test listing a wildcarded file"""
2985 self._RunListCmd(['*cb*', '*head*'],
2986 ['cbfs', 'u-boot', 'u-boot-dtb', 'image-header'])
2987
2988 def testListCmdEmpty(self):
2989 """Test listing a wildcarded file"""
2990 self._RunListCmd(['nothing'], [])
2991
2992 def testListCmdPath(self):
2993 """Test listing the files in a sub-entry of a section"""
2994 self._RunListCmd(['section/cbfs'], ['cbfs', 'u-boot', 'u-boot-dtb'])
2995
Simon Glass4c613bf2019-07-08 14:25:50 -06002996 def _RunExtractCmd(self, entry_name, decomp=True):
2997 """Extract an entry from an image
2998
2999 Args:
3000 entry_name: Entry name to extract
3001 decomp: True to decompress the data if compressed, False to leave
3002 it in its raw uncompressed format
3003
3004 Returns:
3005 data from entry
3006 """
3007 self._CheckLz4()
3008 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07003009 image_fname = tools.get_output_filename('image.bin')
Simon Glass4c613bf2019-07-08 14:25:50 -06003010 return control.ReadEntry(image_fname, entry_name, decomp)
3011
3012 def testExtractSimple(self):
3013 """Test extracting a single file"""
3014 data = self._RunExtractCmd('u-boot')
3015 self.assertEqual(U_BOOT_DATA, data)
3016
Simon Glass980a2842019-07-08 14:25:52 -06003017 def testExtractSection(self):
3018 """Test extracting the files in a section"""
3019 data = self._RunExtractCmd('section')
3020 cbfs_data = data[:0x400]
3021 cbfs = cbfs_util.CbfsReader(cbfs_data)
Simon Glassc5fd10a2019-10-31 07:43:03 -06003022 self.assertEqual(['u-boot', 'u-boot-dtb', ''], list(cbfs.files.keys()))
Simon Glass980a2842019-07-08 14:25:52 -06003023 dtb_data = data[0x400:]
3024 dtb = self._decompress(dtb_data)
3025 self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
3026
3027 def testExtractCompressed(self):
3028 """Test extracting compressed data"""
3029 data = self._RunExtractCmd('section/u-boot-dtb')
3030 self.assertEqual(EXTRACT_DTB_SIZE, len(data))
3031
3032 def testExtractRaw(self):
3033 """Test extracting compressed data without decompressing it"""
3034 data = self._RunExtractCmd('section/u-boot-dtb', decomp=False)
3035 dtb = self._decompress(data)
3036 self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
3037
3038 def testExtractCbfs(self):
3039 """Test extracting CBFS data"""
3040 data = self._RunExtractCmd('section/cbfs/u-boot')
3041 self.assertEqual(U_BOOT_DATA, data)
3042
3043 def testExtractCbfsCompressed(self):
3044 """Test extracting CBFS compressed data"""
3045 data = self._RunExtractCmd('section/cbfs/u-boot-dtb')
3046 self.assertEqual(EXTRACT_DTB_SIZE, len(data))
3047
3048 def testExtractCbfsRaw(self):
3049 """Test extracting CBFS compressed data without decompressing it"""
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +02003050 bintool = self.comp_bintools['lzma_alone']
3051 self._CheckBintool(bintool)
Simon Glass980a2842019-07-08 14:25:52 -06003052 data = self._RunExtractCmd('section/cbfs/u-boot-dtb', decomp=False)
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +02003053 dtb = bintool.decompress(data)
Simon Glass980a2842019-07-08 14:25:52 -06003054 self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
3055
Simon Glass4c613bf2019-07-08 14:25:50 -06003056 def testExtractBadEntry(self):
3057 """Test extracting a bad section path"""
3058 with self.assertRaises(ValueError) as e:
3059 self._RunExtractCmd('section/does-not-exist')
3060 self.assertIn("Entry 'does-not-exist' not found in '/section'",
3061 str(e.exception))
3062
3063 def testExtractMissingFile(self):
3064 """Test extracting file that does not exist"""
3065 with self.assertRaises(IOError) as e:
3066 control.ReadEntry('missing-file', 'name')
3067
3068 def testExtractBadFile(self):
3069 """Test extracting an invalid file"""
3070 fname = os.path.join(self._indir, 'badfile')
Simon Glass80025522022-01-29 14:14:04 -07003071 tools.write_file(fname, b'')
Simon Glass4c613bf2019-07-08 14:25:50 -06003072 with self.assertRaises(ValueError) as e:
3073 control.ReadEntry(fname, 'name')
3074
Simon Glass980a2842019-07-08 14:25:52 -06003075 def testExtractCmd(self):
3076 """Test extracting a file fron an image on the command line"""
3077 self._CheckLz4()
3078 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass980a2842019-07-08 14:25:52 -06003079 fname = os.path.join(self._indir, 'output.extact')
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01003080 tmpdir = None
Simon Glassb3d6fc72019-07-20 12:24:10 -06003081 try:
3082 tmpdir, updated_fname = self._SetupImageInTmpdir()
Simon Glass14d64e32025-04-29 07:21:59 -06003083 with terminal.capture() as (stdout, stderr):
Simon Glassb3d6fc72019-07-20 12:24:10 -06003084 self._DoBinman('extract', '-i', updated_fname, 'u-boot',
3085 '-f', fname)
3086 finally:
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01003087 if tmpdir:
3088 shutil.rmtree(tmpdir)
Simon Glass80025522022-01-29 14:14:04 -07003089 data = tools.read_file(fname)
Simon Glass980a2842019-07-08 14:25:52 -06003090 self.assertEqual(U_BOOT_DATA, data)
3091
3092 def testExtractOneEntry(self):
3093 """Test extracting a single entry fron an image """
3094 self._CheckLz4()
3095 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07003096 image_fname = tools.get_output_filename('image.bin')
Simon Glass980a2842019-07-08 14:25:52 -06003097 fname = os.path.join(self._indir, 'output.extact')
3098 control.ExtractEntries(image_fname, fname, None, ['u-boot'])
Simon Glass80025522022-01-29 14:14:04 -07003099 data = tools.read_file(fname)
Simon Glass980a2842019-07-08 14:25:52 -06003100 self.assertEqual(U_BOOT_DATA, data)
3101
3102 def _CheckExtractOutput(self, decomp):
3103 """Helper to test file output with and without decompression
3104
3105 Args:
3106 decomp: True to decompress entry data, False to output it raw
3107 """
3108 def _CheckPresent(entry_path, expect_data, expect_size=None):
3109 """Check and remove expected file
3110
3111 This checks the data/size of a file and removes the file both from
3112 the outfiles set and from the output directory. Once all files are
3113 processed, both the set and directory should be empty.
3114
3115 Args:
3116 entry_path: Entry path
3117 expect_data: Data to expect in file, or None to skip check
3118 expect_size: Size of data to expect in file, or None to skip
3119 """
3120 path = os.path.join(outdir, entry_path)
Simon Glass80025522022-01-29 14:14:04 -07003121 data = tools.read_file(path)
Simon Glass980a2842019-07-08 14:25:52 -06003122 os.remove(path)
3123 if expect_data:
3124 self.assertEqual(expect_data, data)
3125 elif expect_size:
3126 self.assertEqual(expect_size, len(data))
3127 outfiles.remove(path)
3128
3129 def _CheckDirPresent(name):
3130 """Remove expected directory
3131
3132 This gives an error if the directory does not exist as expected
3133
3134 Args:
3135 name: Name of directory to remove
3136 """
3137 path = os.path.join(outdir, name)
3138 os.rmdir(path)
3139
3140 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07003141 image_fname = tools.get_output_filename('image.bin')
Simon Glass980a2842019-07-08 14:25:52 -06003142 outdir = os.path.join(self._indir, 'extract')
3143 einfos = control.ExtractEntries(image_fname, None, outdir, [], decomp)
3144
3145 # Create a set of all file that were output (should be 9)
3146 outfiles = set()
3147 for root, dirs, files in os.walk(outdir):
3148 outfiles |= set([os.path.join(root, fname) for fname in files])
3149 self.assertEqual(9, len(outfiles))
3150 self.assertEqual(9, len(einfos))
3151
3152 image = control.images['image']
3153 entries = image.GetEntries()
3154
3155 # Check the 9 files in various ways
3156 section = entries['section']
3157 section_entries = section.GetEntries()
3158 cbfs_entries = section_entries['cbfs'].GetEntries()
3159 _CheckPresent('u-boot', U_BOOT_DATA)
3160 _CheckPresent('section/cbfs/u-boot', U_BOOT_DATA)
3161 dtb_len = EXTRACT_DTB_SIZE
3162 if not decomp:
3163 dtb_len = cbfs_entries['u-boot-dtb'].size
3164 _CheckPresent('section/cbfs/u-boot-dtb', None, dtb_len)
3165 if not decomp:
3166 dtb_len = section_entries['u-boot-dtb'].size
3167 _CheckPresent('section/u-boot-dtb', None, dtb_len)
3168
3169 fdtmap = entries['fdtmap']
3170 _CheckPresent('fdtmap', fdtmap.data)
3171 hdr = entries['image-header']
3172 _CheckPresent('image-header', hdr.data)
3173
3174 _CheckPresent('section/root', section.data)
3175 cbfs = section_entries['cbfs']
3176 _CheckPresent('section/cbfs/root', cbfs.data)
Simon Glass80025522022-01-29 14:14:04 -07003177 data = tools.read_file(image_fname)
Simon Glass980a2842019-07-08 14:25:52 -06003178 _CheckPresent('root', data)
3179
3180 # There should be no files left. Remove all the directories to check.
3181 # If there are any files/dirs remaining, one of these checks will fail.
3182 self.assertEqual(0, len(outfiles))
3183 _CheckDirPresent('section/cbfs')
3184 _CheckDirPresent('section')
3185 _CheckDirPresent('')
3186 self.assertFalse(os.path.exists(outdir))
3187
3188 def testExtractAllEntries(self):
3189 """Test extracting all entries"""
3190 self._CheckLz4()
3191 self._CheckExtractOutput(decomp=True)
3192
3193 def testExtractAllEntriesRaw(self):
3194 """Test extracting all entries without decompressing them"""
3195 self._CheckLz4()
3196 self._CheckExtractOutput(decomp=False)
3197
3198 def testExtractSelectedEntries(self):
3199 """Test extracting some entries"""
3200 self._CheckLz4()
3201 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07003202 image_fname = tools.get_output_filename('image.bin')
Simon Glass980a2842019-07-08 14:25:52 -06003203 outdir = os.path.join(self._indir, 'extract')
3204 einfos = control.ExtractEntries(image_fname, None, outdir,
3205 ['*cb*', '*head*'])
3206
3207 # File output is tested by testExtractAllEntries(), so just check that
3208 # the expected entries are selected
3209 names = [einfo.name for einfo in einfos]
3210 self.assertEqual(names,
3211 ['cbfs', 'u-boot', 'u-boot-dtb', 'image-header'])
3212
3213 def testExtractNoEntryPaths(self):
3214 """Test extracting some entries"""
3215 self._CheckLz4()
3216 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07003217 image_fname = tools.get_output_filename('image.bin')
Simon Glass980a2842019-07-08 14:25:52 -06003218 with self.assertRaises(ValueError) as e:
3219 control.ExtractEntries(image_fname, 'fname', None, [])
Simon Glassa772d3f2019-07-20 12:24:14 -06003220 self.assertIn('Must specify an entry path to write with -f',
Simon Glass980a2842019-07-08 14:25:52 -06003221 str(e.exception))
3222
3223 def testExtractTooManyEntryPaths(self):
3224 """Test extracting some entries"""
3225 self._CheckLz4()
3226 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07003227 image_fname = tools.get_output_filename('image.bin')
Simon Glass980a2842019-07-08 14:25:52 -06003228 with self.assertRaises(ValueError) as e:
3229 control.ExtractEntries(image_fname, 'fname', None, ['a', 'b'])
Simon Glassa772d3f2019-07-20 12:24:14 -06003230 self.assertIn('Must specify exactly one entry path to write with -f',
Simon Glass980a2842019-07-08 14:25:52 -06003231 str(e.exception))
3232
Simon Glass52d06212019-07-08 14:25:53 -06003233 def testPackAlignSection(self):
3234 """Test that sections can have alignment"""
3235 self._DoReadFile('131_pack_align_section.dts')
3236
3237 self.assertIn('image', control.images)
3238 image = control.images['image']
3239 entries = image.GetEntries()
3240 self.assertEqual(3, len(entries))
3241
3242 # First u-boot
3243 self.assertIn('u-boot', entries)
3244 entry = entries['u-boot']
3245 self.assertEqual(0, entry.offset)
3246 self.assertEqual(0, entry.image_pos)
3247 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3248 self.assertEqual(len(U_BOOT_DATA), entry.size)
3249
3250 # Section0
3251 self.assertIn('section0', entries)
3252 section0 = entries['section0']
3253 self.assertEqual(0x10, section0.offset)
3254 self.assertEqual(0x10, section0.image_pos)
3255 self.assertEqual(len(U_BOOT_DATA), section0.size)
3256
3257 # Second u-boot
3258 section_entries = section0.GetEntries()
3259 self.assertIn('u-boot', section_entries)
3260 entry = section_entries['u-boot']
3261 self.assertEqual(0, entry.offset)
3262 self.assertEqual(0x10, entry.image_pos)
3263 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3264 self.assertEqual(len(U_BOOT_DATA), entry.size)
3265
3266 # Section1
3267 self.assertIn('section1', entries)
3268 section1 = entries['section1']
3269 self.assertEqual(0x14, section1.offset)
3270 self.assertEqual(0x14, section1.image_pos)
3271 self.assertEqual(0x20, section1.size)
3272
3273 # Second u-boot
3274 section_entries = section1.GetEntries()
3275 self.assertIn('u-boot', section_entries)
3276 entry = section_entries['u-boot']
3277 self.assertEqual(0, entry.offset)
3278 self.assertEqual(0x14, entry.image_pos)
3279 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3280 self.assertEqual(len(U_BOOT_DATA), entry.size)
3281
3282 # Section2
3283 self.assertIn('section2', section_entries)
3284 section2 = section_entries['section2']
3285 self.assertEqual(0x4, section2.offset)
3286 self.assertEqual(0x18, section2.image_pos)
3287 self.assertEqual(4, section2.size)
3288
3289 # Third u-boot
3290 section_entries = section2.GetEntries()
3291 self.assertIn('u-boot', section_entries)
3292 entry = section_entries['u-boot']
3293 self.assertEqual(0, entry.offset)
3294 self.assertEqual(0x18, entry.image_pos)
3295 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3296 self.assertEqual(len(U_BOOT_DATA), entry.size)
3297
Simon Glassf8a54bc2019-07-20 12:23:56 -06003298 def _RunReplaceCmd(self, entry_name, data, decomp=True, allow_resize=True,
3299 dts='132_replace.dts'):
Simon Glass072959a2019-07-20 12:23:50 -06003300 """Replace an entry in an image
3301
3302 This writes the entry data to update it, then opens the updated file and
3303 returns the value that it now finds there.
3304
3305 Args:
3306 entry_name: Entry name to replace
3307 data: Data to replace it with
3308 decomp: True to compress the data if needed, False if data is
3309 already compressed so should be used as is
Simon Glassf8a54bc2019-07-20 12:23:56 -06003310 allow_resize: True to allow entries to change size, False to raise
3311 an exception
Simon Glass072959a2019-07-20 12:23:50 -06003312
3313 Returns:
3314 Tuple:
3315 data from entry
3316 data from fdtmap (excluding header)
Simon Glassf8a54bc2019-07-20 12:23:56 -06003317 Image object that was modified
Simon Glass072959a2019-07-20 12:23:50 -06003318 """
Simon Glassf8a54bc2019-07-20 12:23:56 -06003319 dtb_data = self._DoReadFileDtb(dts, use_real_dtb=True,
Simon Glass072959a2019-07-20 12:23:50 -06003320 update_dtb=True)[1]
3321
3322 self.assertIn('image', control.images)
3323 image = control.images['image']
3324 entries = image.GetEntries()
3325 orig_dtb_data = entries['u-boot-dtb'].data
3326 orig_fdtmap_data = entries['fdtmap'].data
3327
Simon Glass80025522022-01-29 14:14:04 -07003328 image_fname = tools.get_output_filename('image.bin')
3329 updated_fname = tools.get_output_filename('image-updated.bin')
3330 tools.write_file(updated_fname, tools.read_file(image_fname))
Simon Glassf8a54bc2019-07-20 12:23:56 -06003331 image = control.WriteEntry(updated_fname, entry_name, data, decomp,
3332 allow_resize)
Simon Glass072959a2019-07-20 12:23:50 -06003333 data = control.ReadEntry(updated_fname, entry_name, decomp)
3334
Simon Glassf8a54bc2019-07-20 12:23:56 -06003335 # The DT data should not change unless resized:
3336 if not allow_resize:
3337 new_dtb_data = entries['u-boot-dtb'].data
3338 self.assertEqual(new_dtb_data, orig_dtb_data)
3339 new_fdtmap_data = entries['fdtmap'].data
3340 self.assertEqual(new_fdtmap_data, orig_fdtmap_data)
Simon Glass072959a2019-07-20 12:23:50 -06003341
Simon Glassf8a54bc2019-07-20 12:23:56 -06003342 return data, orig_fdtmap_data[fdtmap.FDTMAP_HDR_LEN:], image
Simon Glass072959a2019-07-20 12:23:50 -06003343
3344 def testReplaceSimple(self):
3345 """Test replacing a single file"""
3346 expected = b'x' * len(U_BOOT_DATA)
Simon Glassf8a54bc2019-07-20 12:23:56 -06003347 data, expected_fdtmap, _ = self._RunReplaceCmd('u-boot', expected,
3348 allow_resize=False)
Simon Glass072959a2019-07-20 12:23:50 -06003349 self.assertEqual(expected, data)
3350
3351 # Test that the state looks right. There should be an FDT for the fdtmap
3352 # that we jsut read back in, and it should match what we find in the
3353 # 'control' tables. Checking for an FDT that does not exist should
3354 # return None.
3355 path, fdtmap = state.GetFdtContents('fdtmap')
Simon Glassf8a54bc2019-07-20 12:23:56 -06003356 self.assertIsNotNone(path)
Simon Glass072959a2019-07-20 12:23:50 -06003357 self.assertEqual(expected_fdtmap, fdtmap)
3358
3359 dtb = state.GetFdtForEtype('fdtmap')
3360 self.assertEqual(dtb.GetContents(), fdtmap)
3361
3362 missing_path, missing_fdtmap = state.GetFdtContents('missing')
3363 self.assertIsNone(missing_path)
3364 self.assertIsNone(missing_fdtmap)
3365
3366 missing_dtb = state.GetFdtForEtype('missing')
3367 self.assertIsNone(missing_dtb)
3368
3369 self.assertEqual('/binman', state.fdt_path_prefix)
3370
3371 def testReplaceResizeFail(self):
3372 """Test replacing a file by something larger"""
3373 expected = U_BOOT_DATA + b'x'
3374 with self.assertRaises(ValueError) as e:
Simon Glassf8a54bc2019-07-20 12:23:56 -06003375 self._RunReplaceCmd('u-boot', expected, allow_resize=False,
3376 dts='139_replace_repack.dts')
Simon Glass072959a2019-07-20 12:23:50 -06003377 self.assertIn("Node '/u-boot': Entry data size does not match, but resize is disabled",
3378 str(e.exception))
3379
3380 def testReplaceMulti(self):
3381 """Test replacing entry data where multiple images are generated"""
3382 data = self._DoReadFileDtb('133_replace_multi.dts', use_real_dtb=True,
3383 update_dtb=True)[0]
3384 expected = b'x' * len(U_BOOT_DATA)
Simon Glass80025522022-01-29 14:14:04 -07003385 updated_fname = tools.get_output_filename('image-updated.bin')
3386 tools.write_file(updated_fname, data)
Simon Glass072959a2019-07-20 12:23:50 -06003387 entry_name = 'u-boot'
Simon Glassf8a54bc2019-07-20 12:23:56 -06003388 control.WriteEntry(updated_fname, entry_name, expected,
3389 allow_resize=False)
Simon Glass072959a2019-07-20 12:23:50 -06003390 data = control.ReadEntry(updated_fname, entry_name)
3391 self.assertEqual(expected, data)
3392
3393 # Check the state looks right.
3394 self.assertEqual('/binman/image', state.fdt_path_prefix)
3395
3396 # Now check we can write the first image
Simon Glass80025522022-01-29 14:14:04 -07003397 image_fname = tools.get_output_filename('first-image.bin')
3398 updated_fname = tools.get_output_filename('first-updated.bin')
3399 tools.write_file(updated_fname, tools.read_file(image_fname))
Simon Glass072959a2019-07-20 12:23:50 -06003400 entry_name = 'u-boot'
Simon Glassf8a54bc2019-07-20 12:23:56 -06003401 control.WriteEntry(updated_fname, entry_name, expected,
3402 allow_resize=False)
Simon Glass072959a2019-07-20 12:23:50 -06003403 data = control.ReadEntry(updated_fname, entry_name)
3404 self.assertEqual(expected, data)
3405
3406 # Check the state looks right.
3407 self.assertEqual('/binman/first-image', state.fdt_path_prefix)
Simon Glass39dd2152019-07-08 14:25:47 -06003408
Simon Glassfb30e292019-07-20 12:23:51 -06003409 def testUpdateFdtAllRepack(self):
3410 """Test that all device trees are updated with offset/size info"""
Marek Vasutf7413f02023-07-18 07:23:58 -06003411 self._SetupSplElf()
3412 self._SetupTplElf()
Simon Glassfb30e292019-07-20 12:23:51 -06003413 data = self._DoReadFileRealDtb('134_fdt_update_all_repack.dts')
3414 SECTION_SIZE = 0x300
3415 DTB_SIZE = 602
3416 FDTMAP_SIZE = 608
3417 base_expected = {
3418 'offset': 0,
3419 'size': SECTION_SIZE + DTB_SIZE * 2 + FDTMAP_SIZE,
3420 'image-pos': 0,
3421 'section:offset': 0,
3422 'section:size': SECTION_SIZE,
3423 'section:image-pos': 0,
3424 'section/u-boot-dtb:offset': 4,
3425 'section/u-boot-dtb:size': 636,
3426 'section/u-boot-dtb:image-pos': 4,
3427 'u-boot-spl-dtb:offset': SECTION_SIZE,
3428 'u-boot-spl-dtb:size': DTB_SIZE,
3429 'u-boot-spl-dtb:image-pos': SECTION_SIZE,
3430 'u-boot-tpl-dtb:offset': SECTION_SIZE + DTB_SIZE,
3431 'u-boot-tpl-dtb:image-pos': SECTION_SIZE + DTB_SIZE,
3432 'u-boot-tpl-dtb:size': DTB_SIZE,
3433 'fdtmap:offset': SECTION_SIZE + DTB_SIZE * 2,
3434 'fdtmap:size': FDTMAP_SIZE,
3435 'fdtmap:image-pos': SECTION_SIZE + DTB_SIZE * 2,
3436 }
3437 main_expected = {
3438 'section:orig-size': SECTION_SIZE,
3439 'section/u-boot-dtb:orig-offset': 4,
3440 }
3441
3442 # We expect three device-tree files in the output, with the first one
3443 # within a fixed-size section.
3444 # Read them in sequence. We look for an 'spl' property in the SPL tree,
3445 # and 'tpl' in the TPL tree, to make sure they are distinct from the
3446 # main U-Boot tree. All three should have the same positions and offset
3447 # except that the main tree should include the main_expected properties
3448 start = 4
3449 for item in ['', 'spl', 'tpl', None]:
3450 if item is None:
3451 start += 16 # Move past fdtmap header
3452 dtb = fdt.Fdt.FromData(data[start:])
3453 dtb.Scan()
3454 props = self._GetPropTree(dtb,
3455 BASE_DTB_PROPS + REPACK_DTB_PROPS + ['spl', 'tpl'],
3456 prefix='/' if item is None else '/binman/')
3457 expected = dict(base_expected)
3458 if item:
3459 expected[item] = 0
3460 else:
3461 # Main DTB and fdtdec should include the 'orig-' properties
3462 expected.update(main_expected)
3463 # Helpful for debugging:
3464 #for prop in sorted(props):
3465 #print('prop %s %s %s' % (prop, props[prop], expected[prop]))
3466 self.assertEqual(expected, props)
3467 if item == '':
3468 start = SECTION_SIZE
3469 else:
3470 start += dtb._fdt_obj.totalsize()
3471
Simon Glass11453762019-07-20 12:23:55 -06003472 def testFdtmapHeaderMiddle(self):
3473 """Test an FDT map in the middle of an image when it should be at end"""
3474 with self.assertRaises(ValueError) as e:
3475 self._DoReadFileRealDtb('135_fdtmap_hdr_middle.dts')
3476 self.assertIn("Invalid sibling order 'middle' for image-header: Must be at 'end' to match location",
3477 str(e.exception))
3478
3479 def testFdtmapHeaderStartBad(self):
3480 """Test an FDT map in middle of an image when it should be at start"""
3481 with self.assertRaises(ValueError) as e:
3482 self._DoReadFileRealDtb('136_fdtmap_hdr_startbad.dts')
3483 self.assertIn("Invalid sibling order 'end' for image-header: Must be at 'start' to match location",
3484 str(e.exception))
3485
3486 def testFdtmapHeaderEndBad(self):
3487 """Test an FDT map at the start of an image when it should be at end"""
3488 with self.assertRaises(ValueError) as e:
3489 self._DoReadFileRealDtb('137_fdtmap_hdr_endbad.dts')
3490 self.assertIn("Invalid sibling order 'start' for image-header: Must be at 'end' to match location",
3491 str(e.exception))
3492
3493 def testFdtmapHeaderNoSize(self):
3494 """Test an image header at the end of an image with undefined size"""
3495 self._DoReadFileRealDtb('138_fdtmap_hdr_nosize.dts')
3496
Simon Glassf8a54bc2019-07-20 12:23:56 -06003497 def testReplaceResize(self):
3498 """Test replacing a single file in an entry with a larger file"""
3499 expected = U_BOOT_DATA + b'x'
3500 data, _, image = self._RunReplaceCmd('u-boot', expected,
3501 dts='139_replace_repack.dts')
3502 self.assertEqual(expected, data)
3503
3504 entries = image.GetEntries()
3505 dtb_data = entries['u-boot-dtb'].data
3506 dtb = fdt.Fdt.FromData(dtb_data)
3507 dtb.Scan()
3508
3509 # The u-boot section should now be larger in the dtb
3510 node = dtb.GetNode('/binman/u-boot')
3511 self.assertEqual(len(expected), fdt_util.GetInt(node, 'size'))
3512
3513 # Same for the fdtmap
3514 fdata = entries['fdtmap'].data
3515 fdtb = fdt.Fdt.FromData(fdata[fdtmap.FDTMAP_HDR_LEN:])
3516 fdtb.Scan()
3517 fnode = fdtb.GetNode('/u-boot')
3518 self.assertEqual(len(expected), fdt_util.GetInt(fnode, 'size'))
3519
3520 def testReplaceResizeNoRepack(self):
3521 """Test replacing an entry with a larger file when not allowed"""
3522 expected = U_BOOT_DATA + b'x'
3523 with self.assertRaises(ValueError) as e:
3524 self._RunReplaceCmd('u-boot', expected)
3525 self.assertIn('Entry data size does not match, but allow-repack is not present for this image',
3526 str(e.exception))
3527
Simon Glass9d8ee322019-07-20 12:23:58 -06003528 def testEntryShrink(self):
3529 """Test contracting an entry after it is packed"""
3530 try:
3531 state.SetAllowEntryContraction(True)
3532 data = self._DoReadFileDtb('140_entry_shrink.dts',
3533 update_dtb=True)[0]
3534 finally:
3535 state.SetAllowEntryContraction(False)
3536 self.assertEqual(b'a', data[:1])
3537 self.assertEqual(U_BOOT_DATA, data[1:1 + len(U_BOOT_DATA)])
3538 self.assertEqual(b'a', data[-1:])
3539
3540 def testEntryShrinkFail(self):
3541 """Test not being allowed to contract an entry after it is packed"""
3542 data = self._DoReadFileDtb('140_entry_shrink.dts', update_dtb=True)[0]
3543
3544 # In this case there is a spare byte at the end of the data. The size of
3545 # the contents is only 1 byte but we still have the size before it
3546 # shrunk.
3547 self.assertEqual(b'a\0', data[:2])
3548 self.assertEqual(U_BOOT_DATA, data[2:2 + len(U_BOOT_DATA)])
3549 self.assertEqual(b'a\0', data[-2:])
3550
Simon Glass70e32982019-07-20 12:24:01 -06003551 def testDescriptorOffset(self):
3552 """Test that the Intel descriptor is always placed at at the start"""
3553 data = self._DoReadFileDtb('141_descriptor_offset.dts')
3554 image = control.images['image']
3555 entries = image.GetEntries()
3556 desc = entries['intel-descriptor']
Simon Glassed836ac2025-02-26 09:26:17 -07003557 self.assertEqual(0xff800000, desc.offset)
3558 self.assertEqual(0xff800000, desc.image_pos)
Simon Glass70e32982019-07-20 12:24:01 -06003559
Simon Glass37fdd142019-07-20 12:24:06 -06003560 def testReplaceCbfs(self):
3561 """Test replacing a single file in CBFS without changing the size"""
3562 self._CheckLz4()
3563 expected = b'x' * len(U_BOOT_DATA)
3564 data = self._DoReadFileRealDtb('142_replace_cbfs.dts')
Simon Glass80025522022-01-29 14:14:04 -07003565 updated_fname = tools.get_output_filename('image-updated.bin')
3566 tools.write_file(updated_fname, data)
Simon Glass37fdd142019-07-20 12:24:06 -06003567 entry_name = 'section/cbfs/u-boot'
3568 control.WriteEntry(updated_fname, entry_name, expected,
3569 allow_resize=True)
3570 data = control.ReadEntry(updated_fname, entry_name)
3571 self.assertEqual(expected, data)
3572
3573 def testReplaceResizeCbfs(self):
3574 """Test replacing a single file in CBFS with one of a different size"""
3575 self._CheckLz4()
3576 expected = U_BOOT_DATA + b'x'
3577 data = self._DoReadFileRealDtb('142_replace_cbfs.dts')
Simon Glass80025522022-01-29 14:14:04 -07003578 updated_fname = tools.get_output_filename('image-updated.bin')
3579 tools.write_file(updated_fname, data)
Simon Glass37fdd142019-07-20 12:24:06 -06003580 entry_name = 'section/cbfs/u-boot'
3581 control.WriteEntry(updated_fname, entry_name, expected,
3582 allow_resize=True)
3583 data = control.ReadEntry(updated_fname, entry_name)
3584 self.assertEqual(expected, data)
3585
Simon Glass30033c22019-07-20 12:24:15 -06003586 def _SetupForReplace(self):
3587 """Set up some files to use to replace entries
3588
3589 This generates an image, copies it to a new file, extracts all the files
3590 in it and updates some of them
3591
3592 Returns:
3593 List
3594 Image filename
3595 Output directory
3596 Expected values for updated entries, each a string
3597 """
3598 data = self._DoReadFileRealDtb('143_replace_all.dts')
3599
Simon Glass80025522022-01-29 14:14:04 -07003600 updated_fname = tools.get_output_filename('image-updated.bin')
3601 tools.write_file(updated_fname, data)
Simon Glass30033c22019-07-20 12:24:15 -06003602
3603 outdir = os.path.join(self._indir, 'extract')
3604 einfos = control.ExtractEntries(updated_fname, None, outdir, [])
3605
3606 expected1 = b'x' + U_BOOT_DATA + b'y'
3607 u_boot_fname1 = os.path.join(outdir, 'u-boot')
Simon Glass80025522022-01-29 14:14:04 -07003608 tools.write_file(u_boot_fname1, expected1)
Simon Glass30033c22019-07-20 12:24:15 -06003609
3610 expected2 = b'a' + U_BOOT_DATA + b'b'
3611 u_boot_fname2 = os.path.join(outdir, 'u-boot2')
Simon Glass80025522022-01-29 14:14:04 -07003612 tools.write_file(u_boot_fname2, expected2)
Simon Glass30033c22019-07-20 12:24:15 -06003613
3614 expected_text = b'not the same text'
3615 text_fname = os.path.join(outdir, 'text')
Simon Glass80025522022-01-29 14:14:04 -07003616 tools.write_file(text_fname, expected_text)
Simon Glass30033c22019-07-20 12:24:15 -06003617
3618 dtb_fname = os.path.join(outdir, 'u-boot-dtb')
3619 dtb = fdt.FdtScan(dtb_fname)
3620 node = dtb.GetNode('/binman/text')
3621 node.AddString('my-property', 'the value')
3622 dtb.Sync(auto_resize=True)
3623 dtb.Flush()
3624
3625 return updated_fname, outdir, expected1, expected2, expected_text
3626
3627 def _CheckReplaceMultiple(self, entry_paths):
3628 """Handle replacing the contents of multiple entries
3629
3630 Args:
3631 entry_paths: List of entry paths to replace
3632
3633 Returns:
3634 List
3635 Dict of entries in the image:
3636 key: Entry name
3637 Value: Entry object
3638 Expected values for updated entries, each a string
3639 """
3640 updated_fname, outdir, expected1, expected2, expected_text = (
3641 self._SetupForReplace())
3642 control.ReplaceEntries(updated_fname, None, outdir, entry_paths)
3643
3644 image = Image.FromFile(updated_fname)
3645 image.LoadData()
3646 return image.GetEntries(), expected1, expected2, expected_text
3647
3648 def testReplaceAll(self):
3649 """Test replacing the contents of all entries"""
3650 entries, expected1, expected2, expected_text = (
3651 self._CheckReplaceMultiple([]))
3652 data = entries['u-boot'].data
3653 self.assertEqual(expected1, data)
3654
3655 data = entries['u-boot2'].data
3656 self.assertEqual(expected2, data)
3657
3658 data = entries['text'].data
3659 self.assertEqual(expected_text, data)
3660
3661 # Check that the device tree is updated
3662 data = entries['u-boot-dtb'].data
3663 dtb = fdt.Fdt.FromData(data)
3664 dtb.Scan()
3665 node = dtb.GetNode('/binman/text')
3666 self.assertEqual('the value', node.props['my-property'].value)
3667
3668 def testReplaceSome(self):
3669 """Test replacing the contents of a few entries"""
3670 entries, expected1, expected2, expected_text = (
3671 self._CheckReplaceMultiple(['u-boot2', 'text']))
3672
3673 # This one should not change
3674 data = entries['u-boot'].data
3675 self.assertEqual(U_BOOT_DATA, data)
3676
3677 data = entries['u-boot2'].data
3678 self.assertEqual(expected2, data)
3679
3680 data = entries['text'].data
3681 self.assertEqual(expected_text, data)
3682
3683 def testReplaceCmd(self):
3684 """Test replacing a file fron an image on the command line"""
3685 self._DoReadFileRealDtb('143_replace_all.dts')
3686
3687 try:
3688 tmpdir, updated_fname = self._SetupImageInTmpdir()
3689
3690 fname = os.path.join(tmpdir, 'update-u-boot.bin')
3691 expected = b'x' * len(U_BOOT_DATA)
Simon Glass80025522022-01-29 14:14:04 -07003692 tools.write_file(fname, expected)
Simon Glass30033c22019-07-20 12:24:15 -06003693
3694 self._DoBinman('replace', '-i', updated_fname, 'u-boot', '-f', fname)
Simon Glass80025522022-01-29 14:14:04 -07003695 data = tools.read_file(updated_fname)
Simon Glass30033c22019-07-20 12:24:15 -06003696 self.assertEqual(expected, data[:len(expected)])
3697 map_fname = os.path.join(tmpdir, 'image-updated.map')
3698 self.assertFalse(os.path.exists(map_fname))
3699 finally:
3700 shutil.rmtree(tmpdir)
3701
3702 def testReplaceCmdSome(self):
3703 """Test replacing some files fron an image on the command line"""
3704 updated_fname, outdir, expected1, expected2, expected_text = (
3705 self._SetupForReplace())
3706
3707 self._DoBinman('replace', '-i', updated_fname, '-I', outdir,
3708 'u-boot2', 'text')
3709
Simon Glass80025522022-01-29 14:14:04 -07003710 tools.prepare_output_dir(None)
Simon Glass30033c22019-07-20 12:24:15 -06003711 image = Image.FromFile(updated_fname)
3712 image.LoadData()
3713 entries = image.GetEntries()
3714
3715 # This one should not change
3716 data = entries['u-boot'].data
3717 self.assertEqual(U_BOOT_DATA, data)
3718
3719 data = entries['u-boot2'].data
3720 self.assertEqual(expected2, data)
3721
3722 data = entries['text'].data
3723 self.assertEqual(expected_text, data)
3724
3725 def testReplaceMissing(self):
3726 """Test replacing entries where the file is missing"""
3727 updated_fname, outdir, expected1, expected2, expected_text = (
3728 self._SetupForReplace())
3729
3730 # Remove one of the files, to generate a warning
3731 u_boot_fname1 = os.path.join(outdir, 'u-boot')
3732 os.remove(u_boot_fname1)
3733
Simon Glass14d64e32025-04-29 07:21:59 -06003734 with terminal.capture() as (stdout, stderr):
Simon Glass30033c22019-07-20 12:24:15 -06003735 control.ReplaceEntries(updated_fname, None, outdir, [])
3736 self.assertIn("Skipping entry '/u-boot' from missing file",
Simon Glass6e02f7c2020-07-09 18:39:39 -06003737 stderr.getvalue())
Simon Glass30033c22019-07-20 12:24:15 -06003738
3739 def testReplaceCmdMap(self):
3740 """Test replacing a file fron an image on the command line"""
3741 self._DoReadFileRealDtb('143_replace_all.dts')
3742
3743 try:
3744 tmpdir, updated_fname = self._SetupImageInTmpdir()
3745
3746 fname = os.path.join(self._indir, 'update-u-boot.bin')
3747 expected = b'x' * len(U_BOOT_DATA)
Simon Glass80025522022-01-29 14:14:04 -07003748 tools.write_file(fname, expected)
Simon Glass30033c22019-07-20 12:24:15 -06003749
3750 self._DoBinman('replace', '-i', updated_fname, 'u-boot',
3751 '-f', fname, '-m')
3752 map_fname = os.path.join(tmpdir, 'image-updated.map')
3753 self.assertTrue(os.path.exists(map_fname))
3754 finally:
3755 shutil.rmtree(tmpdir)
3756
3757 def testReplaceNoEntryPaths(self):
3758 """Test replacing an entry without an entry path"""
3759 self._DoReadFileRealDtb('143_replace_all.dts')
Simon Glass80025522022-01-29 14:14:04 -07003760 image_fname = tools.get_output_filename('image.bin')
Simon Glass30033c22019-07-20 12:24:15 -06003761 with self.assertRaises(ValueError) as e:
3762 control.ReplaceEntries(image_fname, 'fname', None, [])
3763 self.assertIn('Must specify an entry path to read with -f',
3764 str(e.exception))
3765
3766 def testReplaceTooManyEntryPaths(self):
3767 """Test extracting some entries"""
3768 self._DoReadFileRealDtb('143_replace_all.dts')
Simon Glass80025522022-01-29 14:14:04 -07003769 image_fname = tools.get_output_filename('image.bin')
Simon Glass30033c22019-07-20 12:24:15 -06003770 with self.assertRaises(ValueError) as e:
3771 control.ReplaceEntries(image_fname, 'fname', None, ['a', 'b'])
3772 self.assertIn('Must specify exactly one entry path to write with -f',
3773 str(e.exception))
3774
Simon Glass0b074d62019-08-24 07:22:48 -06003775 def testPackReset16(self):
3776 """Test that an image with an x86 reset16 region can be created"""
3777 data = self._DoReadFile('144_x86_reset16.dts')
3778 self.assertEqual(X86_RESET16_DATA, data[:len(X86_RESET16_DATA)])
3779
3780 def testPackReset16Spl(self):
3781 """Test that an image with an x86 reset16-spl region can be created"""
3782 data = self._DoReadFile('145_x86_reset16_spl.dts')
3783 self.assertEqual(X86_RESET16_SPL_DATA, data[:len(X86_RESET16_SPL_DATA)])
3784
3785 def testPackReset16Tpl(self):
3786 """Test that an image with an x86 reset16-tpl region can be created"""
3787 data = self._DoReadFile('146_x86_reset16_tpl.dts')
3788 self.assertEqual(X86_RESET16_TPL_DATA, data[:len(X86_RESET16_TPL_DATA)])
3789
Simon Glass232f90c2019-08-24 07:22:50 -06003790 def testPackIntelFit(self):
3791 """Test that an image with an Intel FIT and pointer can be created"""
3792 data = self._DoReadFile('147_intel_fit.dts')
3793 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
3794 fit = data[16:32];
3795 self.assertEqual(b'_FIT_ \x01\x00\x00\x00\x00\x01\x80}' , fit)
3796 ptr = struct.unpack('<i', data[0x40:0x44])[0]
3797
3798 image = control.images['image']
3799 entries = image.GetEntries()
Simon Glassed836ac2025-02-26 09:26:17 -07003800 expected_ptr = entries['intel-fit'].image_pos #- (1 << 32)
3801 self.assertEqual(expected_ptr, ptr + (1 << 32))
Simon Glass232f90c2019-08-24 07:22:50 -06003802
3803 def testPackIntelFitMissing(self):
3804 """Test detection of a FIT pointer with not FIT region"""
3805 with self.assertRaises(ValueError) as e:
3806 self._DoReadFile('148_intel_fit_missing.dts')
3807 self.assertIn("'intel-fit-ptr' section must have an 'intel-fit' sibling",
3808 str(e.exception))
3809
Simon Glass72555fa2019-11-06 17:22:44 -07003810 def _CheckSymbolsTplSection(self, dts, expected_vals):
3811 data = self._DoReadFile(dts)
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003812 sym_values = struct.pack('<LLQLL', elf.BINMAN_SYM_MAGIC_VALUE, *expected_vals)
Simon Glass3eb5b202019-08-24 07:23:00 -06003813 upto1 = 4 + len(U_BOOT_SPL_DATA)
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003814 expected1 = tools.get_bytes(0xff, 4) + sym_values + U_BOOT_SPL_DATA[24:]
Simon Glass3eb5b202019-08-24 07:23:00 -06003815 self.assertEqual(expected1, data[:upto1])
3816
3817 upto2 = upto1 + 1 + len(U_BOOT_SPL_DATA)
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003818 expected2 = tools.get_bytes(0xff, 1) + sym_values + U_BOOT_SPL_DATA[24:]
Simon Glass3eb5b202019-08-24 07:23:00 -06003819 self.assertEqual(expected2, data[upto1:upto2])
3820
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003821 upto3 = 0x3c + len(U_BOOT_DATA)
Simon Glass80025522022-01-29 14:14:04 -07003822 expected3 = tools.get_bytes(0xff, 1) + U_BOOT_DATA
Simon Glass3eb5b202019-08-24 07:23:00 -06003823 self.assertEqual(expected3, data[upto2:upto3])
3824
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003825 expected4 = sym_values + U_BOOT_TPL_DATA[24:]
Simon Glass72555fa2019-11-06 17:22:44 -07003826 self.assertEqual(expected4, data[upto3:upto3 + len(U_BOOT_TPL_DATA)])
3827
3828 def testSymbolsTplSection(self):
3829 """Test binman can assign symbols embedded in U-Boot TPL in a section"""
3830 self._SetupSplElf('u_boot_binman_syms')
3831 self._SetupTplElf('u_boot_binman_syms')
3832 self._CheckSymbolsTplSection('149_symbols_tpl.dts',
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003833 [0x04, 0x20, 0x10 + 0x3c, 0x04])
Simon Glass72555fa2019-11-06 17:22:44 -07003834
3835 def testSymbolsTplSectionX86(self):
3836 """Test binman can assign symbols in a section with end-at-4gb"""
3837 self._SetupSplElf('u_boot_binman_syms_x86')
3838 self._SetupTplElf('u_boot_binman_syms_x86')
3839 self._CheckSymbolsTplSection('155_symbols_tpl_x86.dts',
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003840 [0xffffff04, 0xffffff20, 0xffffff3c,
Simon Glass72555fa2019-11-06 17:22:44 -07003841 0x04])
Simon Glass3eb5b202019-08-24 07:23:00 -06003842
Simon Glass98c59572019-08-24 07:23:03 -06003843 def testPackX86RomIfwiSectiom(self):
3844 """Test that a section can be placed in an IFWI region"""
3845 self._SetupIfwi('fitimage.bin')
3846 data = self._DoReadFile('151_x86_rom_ifwi_section.dts')
3847 self._CheckIfwi(data)
3848
Simon Glassba7985d2019-08-24 07:23:07 -06003849 def testPackFspM(self):
3850 """Test that an image with a FSP memory-init binary can be created"""
3851 data = self._DoReadFile('152_intel_fsp_m.dts')
3852 self.assertEqual(FSP_M_DATA, data[:len(FSP_M_DATA)])
3853
Simon Glass4d9086d2019-10-20 21:31:35 -06003854 def testPackFspS(self):
3855 """Test that an image with a FSP silicon-init binary can be created"""
3856 data = self._DoReadFile('153_intel_fsp_s.dts')
3857 self.assertEqual(FSP_S_DATA, data[:len(FSP_S_DATA)])
Simon Glassba7985d2019-08-24 07:23:07 -06003858
Simon Glass9ea87b22019-10-20 21:31:36 -06003859 def testPackFspT(self):
3860 """Test that an image with a FSP temp-ram-init binary can be created"""
3861 data = self._DoReadFile('154_intel_fsp_t.dts')
3862 self.assertEqual(FSP_T_DATA, data[:len(FSP_T_DATA)])
3863
Simon Glass48f3aad2020-07-09 18:39:31 -06003864 def testMkimage(self):
3865 """Test using mkimage to build an image"""
Marek Vasutf7413f02023-07-18 07:23:58 -06003866 self._SetupSplElf()
Simon Glass48f3aad2020-07-09 18:39:31 -06003867 data = self._DoReadFile('156_mkimage.dts')
3868
3869 # Just check that the data appears in the file somewhere
3870 self.assertIn(U_BOOT_SPL_DATA, data)
3871
Simon Glass66152ce2022-01-09 20:14:09 -07003872 def testMkimageMissing(self):
3873 """Test that binman still produces an image if mkimage is missing"""
Marek Vasutf7413f02023-07-18 07:23:58 -06003874 self._SetupSplElf()
Simon Glass14d64e32025-04-29 07:21:59 -06003875 with terminal.capture() as (_, stderr):
Simon Glass66152ce2022-01-09 20:14:09 -07003876 self._DoTestFile('156_mkimage.dts',
3877 force_missing_bintools='mkimage')
3878 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07003879 self.assertRegex(err, "Image 'image'.*missing bintools.*: mkimage")
Simon Glass66152ce2022-01-09 20:14:09 -07003880
Simon Glass5e560182020-07-09 18:39:36 -06003881 def testExtblob(self):
3882 """Test an image with an external blob"""
3883 data = self._DoReadFile('157_blob_ext.dts')
3884 self.assertEqual(REFCODE_DATA, data)
3885
3886 def testExtblobMissing(self):
3887 """Test an image with a missing external blob"""
3888 with self.assertRaises(ValueError) as e:
3889 self._DoReadFile('158_blob_ext_missing.dts')
3890 self.assertIn("Filename 'missing-file' not found in input path",
3891 str(e.exception))
3892
Simon Glass5d94cc62020-07-09 18:39:38 -06003893 def testExtblobMissingOk(self):
3894 """Test an image with an missing external blob that is allowed"""
Simon Glass14d64e32025-04-29 07:21:59 -06003895 with terminal.capture() as (stdout, stderr):
Simon Glass6bce5dc2022-11-09 19:14:42 -07003896 ret = self._DoTestFile('158_blob_ext_missing.dts',
3897 allow_missing=True)
3898 self.assertEqual(103, ret)
Simon Glassa003cd32020-07-09 18:39:40 -06003899 err = stderr.getvalue()
Jonas Karlmanda423fc2023-07-18 20:34:39 +00003900 self.assertIn('(missing-file)', err)
Simon Glass49cd2b32023-02-07 14:34:18 -07003901 self.assertRegex(err, "Image 'image'.*missing.*: blob-ext")
Simon Glass6bce5dc2022-11-09 19:14:42 -07003902 self.assertIn('Some images are invalid', err)
3903
3904 def testExtblobMissingOkFlag(self):
3905 """Test an image with an missing external blob allowed with -W"""
Simon Glass14d64e32025-04-29 07:21:59 -06003906 with terminal.capture() as (stdout, stderr):
Simon Glass6bce5dc2022-11-09 19:14:42 -07003907 ret = self._DoTestFile('158_blob_ext_missing.dts',
3908 allow_missing=True, ignore_missing=True)
3909 self.assertEqual(0, ret)
3910 err = stderr.getvalue()
Jonas Karlmanda423fc2023-07-18 20:34:39 +00003911 self.assertIn('(missing-file)', err)
Simon Glass49cd2b32023-02-07 14:34:18 -07003912 self.assertRegex(err, "Image 'image'.*missing.*: blob-ext")
Simon Glass6bce5dc2022-11-09 19:14:42 -07003913 self.assertIn('Some images are invalid', err)
Simon Glassa003cd32020-07-09 18:39:40 -06003914
3915 def testExtblobMissingOkSect(self):
3916 """Test an image with an missing external blob that is allowed"""
Simon Glass14d64e32025-04-29 07:21:59 -06003917 with terminal.capture() as (stdout, stderr):
Simon Glassa003cd32020-07-09 18:39:40 -06003918 self._DoTestFile('159_blob_ext_missing_sect.dts',
3919 allow_missing=True)
3920 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07003921 self.assertRegex(err, "Image 'image'.*missing.*: blob-ext blob-ext2")
Simon Glass5d94cc62020-07-09 18:39:38 -06003922
Simon Glasse88cef92020-07-09 18:39:41 -06003923 def testPackX86RomMeMissingDesc(self):
3924 """Test that an missing Intel descriptor entry is allowed"""
Simon Glass14d64e32025-04-29 07:21:59 -06003925 with terminal.capture() as (stdout, stderr):
Simon Glass14c596c2020-07-25 15:11:19 -06003926 self._DoTestFile('164_x86_rom_me_missing.dts', allow_missing=True)
Simon Glasse88cef92020-07-09 18:39:41 -06003927 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07003928 self.assertRegex(err, "Image 'image'.*missing.*: intel-descriptor")
Simon Glasse88cef92020-07-09 18:39:41 -06003929
3930 def testPackX86RomMissingIfwi(self):
3931 """Test that an x86 ROM with Integrated Firmware Image can be created"""
3932 self._SetupIfwi('fitimage.bin')
3933 pathname = os.path.join(self._indir, 'fitimage.bin')
3934 os.remove(pathname)
Simon Glass14d64e32025-04-29 07:21:59 -06003935 with terminal.capture() as (stdout, stderr):
Simon Glasse88cef92020-07-09 18:39:41 -06003936 self._DoTestFile('111_x86_rom_ifwi.dts', allow_missing=True)
3937 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07003938 self.assertRegex(err, "Image 'image'.*missing.*: intel-ifwi")
Simon Glasse88cef92020-07-09 18:39:41 -06003939
Simon Glass2a0fa982022-02-11 13:23:21 -07003940 def testPackOverlapZero(self):
Simon Glassd70829a2020-07-09 18:39:42 -06003941 """Test that zero-size overlapping regions are ignored"""
3942 self._DoTestFile('160_pack_overlap_zero.dts')
3943
Alper Nebi Yasak09fb0612022-02-08 01:08:04 +03003944 def _CheckSimpleFitData(self, fit_data, kernel_data, fdt1_data):
Simon Glass45d556d2020-07-09 18:39:45 -06003945 # The data should be inside the FIT
3946 dtb = fdt.Fdt.FromData(fit_data)
3947 dtb.Scan()
3948 fnode = dtb.GetNode('/images/kernel')
3949 self.assertIn('data', fnode.props)
3950
3951 fname = os.path.join(self._indir, 'fit_data.fit')
Simon Glass80025522022-01-29 14:14:04 -07003952 tools.write_file(fname, fit_data)
3953 out = tools.run('dumpimage', '-l', fname)
Simon Glass45d556d2020-07-09 18:39:45 -06003954
3955 # Check a few features to make sure the plumbing works. We don't need
3956 # to test the operation of mkimage or dumpimage here. First convert the
3957 # output into a dict where the keys are the fields printed by dumpimage
3958 # and the values are a list of values for each field
3959 lines = out.splitlines()
3960
3961 # Converts "Compression: gzip compressed" into two groups:
3962 # 'Compression' and 'gzip compressed'
3963 re_line = re.compile(r'^ *([^:]*)(?:: *(.*))?$')
3964 vals = collections.defaultdict(list)
3965 for line in lines:
3966 mat = re_line.match(line)
3967 vals[mat.group(1)].append(mat.group(2))
3968
Brandon Maiera657bc62024-06-04 16:16:05 +00003969 self.assertEqual('FIT description: test-desc', lines[0])
Simon Glass45d556d2020-07-09 18:39:45 -06003970 self.assertIn('Created:', lines[1])
3971 self.assertIn('Image 0 (kernel)', vals)
3972 self.assertIn('Hash value', vals)
3973 data_sizes = vals.get('Data Size')
3974 self.assertIsNotNone(data_sizes)
3975 self.assertEqual(2, len(data_sizes))
3976 # Format is "4 Bytes = 0.00 KiB = 0.00 MiB" so take the first word
Alper Nebi Yasak09fb0612022-02-08 01:08:04 +03003977 self.assertEqual(len(kernel_data), int(data_sizes[0].split()[0]))
3978 self.assertEqual(len(fdt1_data), int(data_sizes[1].split()[0]))
3979
Alper Nebi Yasak1a0ee0f2022-03-27 18:31:47 +03003980 # Check if entry listing correctly omits /images/
3981 image = control.images['image']
3982 fit_entry = image.GetEntries()['fit']
3983 subentries = list(fit_entry.GetEntries().keys())
3984 expected = ['kernel', 'fdt-1']
3985 self.assertEqual(expected, subentries)
3986
Alper Nebi Yasak09fb0612022-02-08 01:08:04 +03003987 def testSimpleFit(self):
3988 """Test an image with a FIT inside"""
Marek Vasutf7413f02023-07-18 07:23:58 -06003989 self._SetupSplElf()
Alper Nebi Yasak09fb0612022-02-08 01:08:04 +03003990 data = self._DoReadFile('161_fit.dts')
3991 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
3992 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
3993 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
3994
3995 self._CheckSimpleFitData(fit_data, U_BOOT_DATA, U_BOOT_SPL_DTB_DATA)
3996
3997 def testSimpleFitExpandsSubentries(self):
3998 """Test that FIT images expand their subentries"""
3999 data = self._DoReadFileDtb('161_fit.dts', use_expanded=True)[0]
4000 self.assertEqual(U_BOOT_EXP_DATA, data[:len(U_BOOT_EXP_DATA)])
4001 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
4002 fit_data = data[len(U_BOOT_EXP_DATA):-len(U_BOOT_NODTB_DATA)]
4003
4004 self._CheckSimpleFitData(fit_data, U_BOOT_EXP_DATA, U_BOOT_SPL_DTB_DATA)
Simon Glass45d556d2020-07-09 18:39:45 -06004005
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03004006 def testSimpleFitImagePos(self):
4007 """Test that we have correct image-pos for FIT subentries"""
4008 data, _, _, out_dtb_fname = self._DoReadFileDtb('161_fit.dts',
4009 update_dtb=True)
4010 dtb = fdt.Fdt(out_dtb_fname)
4011 dtb.Scan()
4012 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
4013
Simon Glassb7bad182022-03-05 20:19:01 -07004014 self.maxDiff = None
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03004015 self.assertEqual({
4016 'image-pos': 0,
4017 'offset': 0,
4018 'size': 1890,
4019
4020 'u-boot:image-pos': 0,
4021 'u-boot:offset': 0,
4022 'u-boot:size': 4,
4023
4024 'fit:image-pos': 4,
4025 'fit:offset': 4,
4026 'fit:size': 1840,
4027
Simon Glassb7bad182022-03-05 20:19:01 -07004028 'fit/images/kernel:image-pos': 304,
4029 'fit/images/kernel:offset': 300,
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03004030 'fit/images/kernel:size': 4,
4031
Simon Glassb7bad182022-03-05 20:19:01 -07004032 'fit/images/kernel/u-boot:image-pos': 304,
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03004033 'fit/images/kernel/u-boot:offset': 0,
4034 'fit/images/kernel/u-boot:size': 4,
4035
Simon Glassb7bad182022-03-05 20:19:01 -07004036 'fit/images/fdt-1:image-pos': 552,
4037 'fit/images/fdt-1:offset': 548,
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03004038 'fit/images/fdt-1:size': 6,
4039
Simon Glassb7bad182022-03-05 20:19:01 -07004040 'fit/images/fdt-1/u-boot-spl-dtb:image-pos': 552,
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03004041 'fit/images/fdt-1/u-boot-spl-dtb:offset': 0,
4042 'fit/images/fdt-1/u-boot-spl-dtb:size': 6,
4043
4044 'u-boot-nodtb:image-pos': 1844,
4045 'u-boot-nodtb:offset': 1844,
4046 'u-boot-nodtb:size': 46,
4047 }, props)
4048
4049 # Actually check the data is where we think it is
4050 for node, expected in [
4051 ("u-boot", U_BOOT_DATA),
4052 ("fit/images/kernel", U_BOOT_DATA),
4053 ("fit/images/kernel/u-boot", U_BOOT_DATA),
4054 ("fit/images/fdt-1", U_BOOT_SPL_DTB_DATA),
4055 ("fit/images/fdt-1/u-boot-spl-dtb", U_BOOT_SPL_DTB_DATA),
4056 ("u-boot-nodtb", U_BOOT_NODTB_DATA),
4057 ]:
4058 image_pos = props[f"{node}:image-pos"]
4059 size = props[f"{node}:size"]
4060 self.assertEqual(len(expected), size)
4061 self.assertEqual(expected, data[image_pos:image_pos+size])
4062
Simon Glass45d556d2020-07-09 18:39:45 -06004063 def testFitExternal(self):
Simon Glass31ee50f2020-09-01 05:13:55 -06004064 """Test an image with an FIT with external images"""
Simon Glass45d556d2020-07-09 18:39:45 -06004065 data = self._DoReadFile('162_fit_external.dts')
4066 fit_data = data[len(U_BOOT_DATA):-2] # _testing is 2 bytes
4067
Simon Glass7932c882022-01-09 20:13:39 -07004068 # Size of the external-data region as set up by mkimage
4069 external_data_size = len(U_BOOT_DATA) + 2
4070 expected_size = (len(U_BOOT_DATA) + 0x400 +
Simon Glass80025522022-01-29 14:14:04 -07004071 tools.align(external_data_size, 4) +
Simon Glass7932c882022-01-09 20:13:39 -07004072 len(U_BOOT_NODTB_DATA))
4073
Simon Glass45d556d2020-07-09 18:39:45 -06004074 # The data should be outside the FIT
4075 dtb = fdt.Fdt.FromData(fit_data)
4076 dtb.Scan()
4077 fnode = dtb.GetNode('/images/kernel')
4078 self.assertNotIn('data', fnode.props)
Simon Glass7932c882022-01-09 20:13:39 -07004079 self.assertEqual(len(U_BOOT_DATA),
4080 fdt_util.fdt32_to_cpu(fnode.props['data-size'].value))
4081 fit_pos = 0x400;
4082 self.assertEqual(
4083 fit_pos,
4084 fdt_util.fdt32_to_cpu(fnode.props['data-position'].value))
4085
Brandon Maiera657bc62024-06-04 16:16:05 +00004086 self.assertEqual(expected_size, len(data))
Simon Glass7932c882022-01-09 20:13:39 -07004087 actual_pos = len(U_BOOT_DATA) + fit_pos
4088 self.assertEqual(U_BOOT_DATA + b'aa',
4089 data[actual_pos:actual_pos + external_data_size])
Simon Glassfb30e292019-07-20 12:23:51 -06004090
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03004091 def testFitExternalImagePos(self):
4092 """Test that we have correct image-pos for external FIT subentries"""
4093 data, _, _, out_dtb_fname = self._DoReadFileDtb('162_fit_external.dts',
4094 update_dtb=True)
4095 dtb = fdt.Fdt(out_dtb_fname)
4096 dtb.Scan()
4097 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
4098
4099 self.assertEqual({
4100 'image-pos': 0,
4101 'offset': 0,
4102 'size': 1082,
4103
4104 'u-boot:image-pos': 0,
4105 'u-boot:offset': 0,
4106 'u-boot:size': 4,
4107
4108 'fit:size': 1032,
4109 'fit:offset': 4,
4110 'fit:image-pos': 4,
4111
4112 'fit/images/kernel:size': 4,
4113 'fit/images/kernel:offset': 1024,
4114 'fit/images/kernel:image-pos': 1028,
4115
4116 'fit/images/kernel/u-boot:size': 4,
4117 'fit/images/kernel/u-boot:offset': 0,
4118 'fit/images/kernel/u-boot:image-pos': 1028,
4119
4120 'fit/images/fdt-1:size': 2,
4121 'fit/images/fdt-1:offset': 1028,
4122 'fit/images/fdt-1:image-pos': 1032,
4123
4124 'fit/images/fdt-1/_testing:size': 2,
4125 'fit/images/fdt-1/_testing:offset': 0,
4126 'fit/images/fdt-1/_testing:image-pos': 1032,
4127
4128 'u-boot-nodtb:image-pos': 1036,
4129 'u-boot-nodtb:offset': 1036,
4130 'u-boot-nodtb:size': 46,
4131 }, props)
4132
4133 # Actually check the data is where we think it is
4134 for node, expected in [
4135 ("u-boot", U_BOOT_DATA),
4136 ("fit/images/kernel", U_BOOT_DATA),
4137 ("fit/images/kernel/u-boot", U_BOOT_DATA),
4138 ("fit/images/fdt-1", b'aa'),
4139 ("fit/images/fdt-1/_testing", b'aa'),
4140 ("u-boot-nodtb", U_BOOT_NODTB_DATA),
4141 ]:
4142 image_pos = props[f"{node}:image-pos"]
4143 size = props[f"{node}:size"]
4144 self.assertEqual(len(expected), size)
4145 self.assertEqual(expected, data[image_pos:image_pos+size])
4146
Simon Glass66152ce2022-01-09 20:14:09 -07004147 def testFitMissing(self):
Simon Glass039d65f2023-03-02 17:02:43 -07004148 """Test that binman complains if mkimage is missing"""
4149 with self.assertRaises(ValueError) as e:
4150 self._DoTestFile('162_fit_external.dts',
4151 force_missing_bintools='mkimage')
4152 self.assertIn("Node '/binman/fit': Missing tool: 'mkimage'",
4153 str(e.exception))
4154
4155 def testFitMissingOK(self):
Simon Glass66152ce2022-01-09 20:14:09 -07004156 """Test that binman still produces a FIT image if mkimage is missing"""
Simon Glass14d64e32025-04-29 07:21:59 -06004157 with terminal.capture() as (_, stderr):
Simon Glass039d65f2023-03-02 17:02:43 -07004158 self._DoTestFile('162_fit_external.dts', allow_missing=True,
Simon Glass66152ce2022-01-09 20:14:09 -07004159 force_missing_bintools='mkimage')
4160 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07004161 self.assertRegex(err, "Image 'image'.*missing bintools.*: mkimage")
Simon Glass66152ce2022-01-09 20:14:09 -07004162
Alper Nebi Yasak6aae2392020-08-31 12:58:18 +03004163 def testSectionIgnoreHashSignature(self):
4164 """Test that sections ignore hash, signature nodes for its data"""
4165 data = self._DoReadFile('165_section_ignore_hash_signature.dts')
4166 expected = (U_BOOT_DATA + U_BOOT_DATA)
4167 self.assertEqual(expected, data)
4168
Alper Nebi Yasakc5030602020-08-31 12:58:19 +03004169 def testPadInSections(self):
4170 """Test pad-before, pad-after for entries in sections"""
Simon Glassd12599d2020-10-26 17:40:09 -06004171 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4172 '166_pad_in_sections.dts', update_dtb=True)
Simon Glass80025522022-01-29 14:14:04 -07004173 expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) +
4174 U_BOOT_DATA + tools.get_bytes(ord('!'), 6) +
Alper Nebi Yasakc5030602020-08-31 12:58:19 +03004175 U_BOOT_DATA)
4176 self.assertEqual(expected, data)
4177
Simon Glassd12599d2020-10-26 17:40:09 -06004178 dtb = fdt.Fdt(out_dtb_fname)
4179 dtb.Scan()
4180 props = self._GetPropTree(dtb, ['size', 'image-pos', 'offset'])
4181 expected = {
4182 'image-pos': 0,
4183 'offset': 0,
4184 'size': 12 + 6 + 3 * len(U_BOOT_DATA),
4185
4186 'section:image-pos': 0,
4187 'section:offset': 0,
4188 'section:size': 12 + 6 + 3 * len(U_BOOT_DATA),
4189
4190 'section/before:image-pos': 0,
4191 'section/before:offset': 0,
4192 'section/before:size': len(U_BOOT_DATA),
4193
4194 'section/u-boot:image-pos': 4,
4195 'section/u-boot:offset': 4,
4196 'section/u-boot:size': 12 + len(U_BOOT_DATA) + 6,
4197
4198 'section/after:image-pos': 26,
4199 'section/after:offset': 26,
4200 'section/after:size': len(U_BOOT_DATA),
4201 }
4202 self.assertEqual(expected, props)
4203
Alper Nebi Yasakc261ec12020-08-31 12:58:20 +03004204 def testFitImageSubentryAlignment(self):
4205 """Test relative alignability of FIT image subentries"""
Alper Nebi Yasakd4553262022-02-08 01:08:07 +03004206 self._SetupSplElf()
Alper Nebi Yasakc261ec12020-08-31 12:58:20 +03004207 entry_args = {
4208 'test-id': TEXT_DATA,
4209 }
4210 data, _, _, _ = self._DoReadFileDtb('167_fit_image_subentry_alignment.dts',
4211 entry_args=entry_args)
4212 dtb = fdt.Fdt.FromData(data)
4213 dtb.Scan()
4214
4215 node = dtb.GetNode('/images/kernel')
4216 data = dtb.GetProps(node)["data"].bytes
4217 align_pad = 0x10 - (len(U_BOOT_SPL_DATA) % 0x10)
Simon Glass80025522022-01-29 14:14:04 -07004218 expected = (tools.get_bytes(0, 0x20) + U_BOOT_SPL_DATA +
4219 tools.get_bytes(0, align_pad) + U_BOOT_DATA)
Alper Nebi Yasakc261ec12020-08-31 12:58:20 +03004220 self.assertEqual(expected, data)
4221
4222 node = dtb.GetNode('/images/fdt-1')
4223 data = dtb.GetProps(node)["data"].bytes
Simon Glass80025522022-01-29 14:14:04 -07004224 expected = (U_BOOT_SPL_DTB_DATA + tools.get_bytes(0, 20) +
4225 tools.to_bytes(TEXT_DATA) + tools.get_bytes(0, 30) +
Alper Nebi Yasakc261ec12020-08-31 12:58:20 +03004226 U_BOOT_DTB_DATA)
4227 self.assertEqual(expected, data)
4228
4229 def testFitExtblobMissingOk(self):
4230 """Test a FIT with a missing external blob that is allowed"""
Simon Glass14d64e32025-04-29 07:21:59 -06004231 with terminal.capture() as (stdout, stderr):
Alper Nebi Yasakc261ec12020-08-31 12:58:20 +03004232 self._DoTestFile('168_fit_missing_blob.dts',
4233 allow_missing=True)
4234 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07004235 self.assertRegex(err, "Image 'image'.*missing.*: atf-bl31")
Alper Nebi Yasakc261ec12020-08-31 12:58:20 +03004236
Simon Glass21db0ff2020-09-01 05:13:54 -06004237 def testBlobNamedByArgMissing(self):
4238 """Test handling of a missing entry arg"""
4239 with self.assertRaises(ValueError) as e:
4240 self._DoReadFile('068_blob_named_by_arg.dts')
4241 self.assertIn("Missing required properties/entry args: cros-ec-rw-path",
4242 str(e.exception))
4243
Simon Glass559c4de2020-09-01 05:13:58 -06004244 def testPackBl31(self):
4245 """Test that an image with an ATF BL31 binary can be created"""
4246 data = self._DoReadFile('169_atf_bl31.dts')
4247 self.assertEqual(ATF_BL31_DATA, data[:len(ATF_BL31_DATA)])
4248
Samuel Holland9d8cc632020-10-21 21:12:15 -05004249 def testPackScp(self):
4250 """Test that an image with an SCP binary can be created"""
4251 data = self._DoReadFile('172_scp.dts')
4252 self.assertEqual(SCP_DATA, data[:len(SCP_DATA)])
4253
Simon Glassd2a9d6e2024-08-26 13:11:37 -06004254 def CheckFitFdt(self, dts='170_fit_fdt.dts', use_fdt_list=True,
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004255 default_dt=None, use_seq_num=True):
Simon Glasscd2783e2024-07-20 11:49:46 +01004256 """Check an image with an FIT with multiple FDT images"""
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004257 def _CheckFdt(val, expected_data):
Simon Glassa435cd12020-09-01 05:13:59 -06004258 """Check the FDT nodes
4259
4260 Args:
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004261 val: Sequence number to check (0 or 1) or fdt name
Simon Glassa435cd12020-09-01 05:13:59 -06004262 expected_data: Expected contents of 'data' property
4263 """
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004264 name = 'fdt-%s' % val
Simon Glassa435cd12020-09-01 05:13:59 -06004265 fnode = dtb.GetNode('/images/%s' % name)
4266 self.assertIsNotNone(fnode)
4267 self.assertEqual({'description','type', 'compression', 'data'},
4268 set(fnode.props.keys()))
4269 self.assertEqual(expected_data, fnode.props['data'].bytes)
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004270 description = (
4271 'fdt-test-fdt%s.dtb' % val if len(val) == 1 else
4272 'fdt-%s.dtb' % val
4273 )
4274 self.assertEqual(description, fnode.props['description'].value)
Jan Kiszkaa1419df2022-02-28 17:06:20 +01004275 self.assertEqual(fnode.subnodes[0].name, 'hash')
Simon Glassa435cd12020-09-01 05:13:59 -06004276
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004277 def _CheckConfig(val, expected_data):
Simon Glassa435cd12020-09-01 05:13:59 -06004278 """Check the configuration nodes
4279
4280 Args:
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004281 val: Sequence number to check (0 or 1) or fdt name
Simon Glassa435cd12020-09-01 05:13:59 -06004282 expected_data: Expected contents of 'data' property
4283 """
4284 cnode = dtb.GetNode('/configurations')
4285 self.assertIn('default', cnode.props)
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004286 default = (
4287 'config-2' if len(val) == 1 else
4288 'config-test-fdt2'
4289 )
4290 self.assertEqual(default, cnode.props['default'].value)
Simon Glassa435cd12020-09-01 05:13:59 -06004291
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004292 name = 'config-%s' % val
Simon Glassa435cd12020-09-01 05:13:59 -06004293 fnode = dtb.GetNode('/configurations/%s' % name)
4294 self.assertIsNotNone(fnode)
4295 self.assertEqual({'description','firmware', 'loadables', 'fdt'},
4296 set(fnode.props.keys()))
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004297 description = (
4298 'conf-test-fdt%s.dtb' % val if len(val) == 1 else
4299 'conf-%s.dtb' % val
4300 )
4301 self.assertEqual(description, fnode.props['description'].value)
4302 self.assertEqual('fdt-%s' % val, fnode.props['fdt'].value)
Simon Glassa435cd12020-09-01 05:13:59 -06004303
4304 entry_args = {
Simon Glass1032acc2020-09-06 10:39:08 -06004305 'default-dt': 'test-fdt2',
Simon Glassa435cd12020-09-01 05:13:59 -06004306 }
Paul HENRYS87a8e7c2024-11-25 19:16:54 +01004307 extra_indirs = None
Simon Glasscd2783e2024-07-20 11:49:46 +01004308 if use_fdt_list:
4309 entry_args['of-list'] = 'test-fdt1 test-fdt2'
Simon Glassd2a9d6e2024-08-26 13:11:37 -06004310 if default_dt:
4311 entry_args['default-dt'] = default_dt
Paul HENRYS87a8e7c2024-11-25 19:16:54 +01004312 if use_fdt_list:
4313 extra_indirs = [os.path.join(self._indir, TEST_FDT_SUBDIR)]
Simon Glassa435cd12020-09-01 05:13:59 -06004314 data = self._DoReadFileDtb(
Simon Glasscd2783e2024-07-20 11:49:46 +01004315 dts,
Simon Glassa435cd12020-09-01 05:13:59 -06004316 entry_args=entry_args,
Paul HENRYS87a8e7c2024-11-25 19:16:54 +01004317 extra_indirs=extra_indirs)[0]
Simon Glassa435cd12020-09-01 05:13:59 -06004318 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
4319 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
4320
4321 dtb = fdt.Fdt.FromData(fit_data)
4322 dtb.Scan()
4323 fnode = dtb.GetNode('/images/kernel')
4324 self.assertIn('data', fnode.props)
4325
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004326 if use_seq_num == True:
4327 # Check all the properties in fdt-1 and fdt-2
4328 _CheckFdt('1', TEST_FDT1_DATA)
4329 _CheckFdt('2', TEST_FDT2_DATA)
Simon Glassa435cd12020-09-01 05:13:59 -06004330
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01004331 # Check configurations
4332 _CheckConfig('1', TEST_FDT1_DATA)
4333 _CheckConfig('2', TEST_FDT2_DATA)
4334 else:
4335 # Check all the properties in fdt-1 and fdt-2
4336 _CheckFdt('test-fdt1', TEST_FDT1_DATA)
4337 _CheckFdt('test-fdt2', TEST_FDT2_DATA)
4338
4339 # Check configurations
4340 _CheckConfig('test-fdt1', TEST_FDT1_DATA)
4341 _CheckConfig('test-fdt2', TEST_FDT2_DATA)
Simon Glassa435cd12020-09-01 05:13:59 -06004342
Simon Glasscd2783e2024-07-20 11:49:46 +01004343 def testFitFdt(self):
4344 """Test an image with an FIT with multiple FDT images"""
4345 self.CheckFitFdt()
4346
Simon Glassa435cd12020-09-01 05:13:59 -06004347 def testFitFdtMissingList(self):
4348 """Test handling of a missing 'of-list' entry arg"""
4349 with self.assertRaises(ValueError) as e:
Bin Meng16cf5662021-05-10 20:23:32 +08004350 self._DoReadFile('170_fit_fdt.dts')
Simon Glassa435cd12020-09-01 05:13:59 -06004351 self.assertIn("Generator node requires 'of-list' entry argument",
4352 str(e.exception))
4353
4354 def testFitFdtEmptyList(self):
4355 """Test handling of an empty 'of-list' entry arg"""
4356 entry_args = {
4357 'of-list': '',
4358 }
4359 data = self._DoReadFileDtb('170_fit_fdt.dts', entry_args=entry_args)[0]
4360
4361 def testFitFdtMissingProp(self):
4362 """Test handling of a missing 'fit,fdt-list' property"""
4363 with self.assertRaises(ValueError) as e:
4364 self._DoReadFile('171_fit_fdt_missing_prop.dts')
4365 self.assertIn("Generator node requires 'fit,fdt-list' property",
4366 str(e.exception))
Simon Glass559c4de2020-09-01 05:13:58 -06004367
Simon Glass1032acc2020-09-06 10:39:08 -06004368 def testFitFdtMissing(self):
4369 """Test handling of a missing 'default-dt' entry arg"""
4370 entry_args = {
4371 'of-list': 'test-fdt1 test-fdt2',
4372 }
4373 with self.assertRaises(ValueError) as e:
4374 self._DoReadFileDtb(
Bin Meng16cf5662021-05-10 20:23:32 +08004375 '170_fit_fdt.dts',
Simon Glass1032acc2020-09-06 10:39:08 -06004376 entry_args=entry_args,
4377 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
4378 self.assertIn("Generated 'default' node requires default-dt entry argument",
4379 str(e.exception))
4380
4381 def testFitFdtNotInList(self):
4382 """Test handling of a default-dt that is not in the of-list"""
4383 entry_args = {
4384 'of-list': 'test-fdt1 test-fdt2',
4385 'default-dt': 'test-fdt3',
4386 }
4387 with self.assertRaises(ValueError) as e:
4388 self._DoReadFileDtb(
Bin Meng16cf5662021-05-10 20:23:32 +08004389 '170_fit_fdt.dts',
Simon Glass1032acc2020-09-06 10:39:08 -06004390 entry_args=entry_args,
4391 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
4392 self.assertIn("default-dt entry argument 'test-fdt3' not found in fdt list: test-fdt1, test-fdt2",
4393 str(e.exception))
4394
Simon Glassa820af72020-09-06 10:39:09 -06004395 def testFitExtblobMissingHelp(self):
4396 """Test display of help messages when an external blob is missing"""
4397 control.missing_blob_help = control._ReadMissingBlobHelp()
4398 control.missing_blob_help['wibble'] = 'Wibble test'
4399 control.missing_blob_help['another'] = 'Another test'
Simon Glass14d64e32025-04-29 07:21:59 -06004400 with terminal.capture() as (stdout, stderr):
Simon Glassa820af72020-09-06 10:39:09 -06004401 self._DoTestFile('168_fit_missing_blob.dts',
4402 allow_missing=True)
4403 err = stderr.getvalue()
4404
4405 # We can get the tag from the name, the type or the missing-msg
4406 # property. Check all three.
4407 self.assertIn('You may need to build ARM Trusted', err)
4408 self.assertIn('Wibble test', err)
4409 self.assertIn('Another test', err)
4410
Simon Glass6f1f4d42020-09-06 10:35:32 -06004411 def testMissingBlob(self):
4412 """Test handling of a blob containing a missing file"""
4413 with self.assertRaises(ValueError) as e:
4414 self._DoTestFile('173_missing_blob.dts', allow_missing=True)
4415 self.assertIn("Filename 'missing' not found in input path",
4416 str(e.exception))
4417
Simon Glassa0729502020-09-06 10:35:33 -06004418 def testEnvironment(self):
4419 """Test adding a U-Boot environment"""
4420 data = self._DoReadFile('174_env.dts')
4421 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
4422 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
4423 env = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
4424 self.assertEqual(b'\x1b\x97\x22\x7c\x01var1=1\0var2="2"\0\0\xff\xff',
4425 env)
4426
4427 def testEnvironmentNoSize(self):
4428 """Test that a missing 'size' property is detected"""
4429 with self.assertRaises(ValueError) as e:
Simon Glass8cdc08a2020-10-26 17:40:00 -06004430 self._DoTestFile('175_env_no_size.dts')
Simon Glassa0729502020-09-06 10:35:33 -06004431 self.assertIn("'u-boot-env' entry must have a size property",
4432 str(e.exception))
4433
4434 def testEnvironmentTooSmall(self):
4435 """Test handling of an environment that does not fit"""
4436 with self.assertRaises(ValueError) as e:
Simon Glass8cdc08a2020-10-26 17:40:00 -06004437 self._DoTestFile('176_env_too_small.dts')
Simon Glassa0729502020-09-06 10:35:33 -06004438
4439 # checksum, start byte, environment with \0 terminator, final \0
4440 need = 4 + 1 + len(ENV_DATA) + 1 + 1
4441 short = need - 0x8
4442 self.assertIn("too small to hold data (need %#x more bytes)" % short,
4443 str(e.exception))
4444
Simon Glassd1fdf752020-10-26 17:40:01 -06004445 def testSkipAtStart(self):
4446 """Test handling of skip-at-start section"""
4447 data = self._DoReadFile('177_skip_at_start.dts')
4448 self.assertEqual(U_BOOT_DATA, data)
4449
4450 image = control.images['image']
4451 entries = image.GetEntries()
4452 section = entries['section']
4453 self.assertEqual(0, section.offset)
4454 self.assertEqual(len(U_BOOT_DATA), section.size)
4455 self.assertEqual(U_BOOT_DATA, section.GetData())
4456
4457 entry = section.GetEntries()['u-boot']
4458 self.assertEqual(16, entry.offset)
4459 self.assertEqual(len(U_BOOT_DATA), entry.size)
4460 self.assertEqual(U_BOOT_DATA, entry.data)
4461
4462 def testSkipAtStartPad(self):
4463 """Test handling of skip-at-start section with padded entry"""
4464 data = self._DoReadFile('178_skip_at_start_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07004465 before = tools.get_bytes(0, 8)
4466 after = tools.get_bytes(0, 4)
Simon Glassd1fdf752020-10-26 17:40:01 -06004467 all = before + U_BOOT_DATA + after
4468 self.assertEqual(all, data)
4469
4470 image = control.images['image']
4471 entries = image.GetEntries()
4472 section = entries['section']
4473 self.assertEqual(0, section.offset)
4474 self.assertEqual(len(all), section.size)
4475 self.assertEqual(all, section.GetData())
4476
4477 entry = section.GetEntries()['u-boot']
4478 self.assertEqual(16, entry.offset)
4479 self.assertEqual(len(all), entry.size)
4480 self.assertEqual(U_BOOT_DATA, entry.data)
4481
4482 def testSkipAtStartSectionPad(self):
4483 """Test handling of skip-at-start section with padding"""
4484 data = self._DoReadFile('179_skip_at_start_section_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07004485 before = tools.get_bytes(0, 8)
4486 after = tools.get_bytes(0, 4)
Simon Glassd1fdf752020-10-26 17:40:01 -06004487 all = before + U_BOOT_DATA + after
Simon Glass510ef0f2020-10-26 17:40:13 -06004488 self.assertEqual(all, data)
Simon Glassd1fdf752020-10-26 17:40:01 -06004489
4490 image = control.images['image']
4491 entries = image.GetEntries()
4492 section = entries['section']
4493 self.assertEqual(0, section.offset)
4494 self.assertEqual(len(all), section.size)
Simon Glass72eeff12020-10-26 17:40:16 -06004495 self.assertEqual(U_BOOT_DATA, section.data)
Simon Glass510ef0f2020-10-26 17:40:13 -06004496 self.assertEqual(all, section.GetPaddedData())
Simon Glassd1fdf752020-10-26 17:40:01 -06004497
4498 entry = section.GetEntries()['u-boot']
4499 self.assertEqual(16, entry.offset)
4500 self.assertEqual(len(U_BOOT_DATA), entry.size)
4501 self.assertEqual(U_BOOT_DATA, entry.data)
Simon Glassa0729502020-09-06 10:35:33 -06004502
Simon Glassbb395742020-10-26 17:40:14 -06004503 def testSectionPad(self):
4504 """Testing padding with sections"""
4505 data = self._DoReadFile('180_section_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07004506 expected = (tools.get_bytes(ord('&'), 3) +
4507 tools.get_bytes(ord('!'), 5) +
Simon Glassbb395742020-10-26 17:40:14 -06004508 U_BOOT_DATA +
Simon Glass80025522022-01-29 14:14:04 -07004509 tools.get_bytes(ord('!'), 1) +
4510 tools.get_bytes(ord('&'), 2))
Simon Glassbb395742020-10-26 17:40:14 -06004511 self.assertEqual(expected, data)
4512
4513 def testSectionAlign(self):
4514 """Testing alignment with sections"""
4515 data = self._DoReadFileDtb('181_section_align.dts', map=True)[0]
4516 expected = (b'\0' + # fill section
Simon Glass80025522022-01-29 14:14:04 -07004517 tools.get_bytes(ord('&'), 1) + # padding to section align
Simon Glassbb395742020-10-26 17:40:14 -06004518 b'\0' + # fill section
Simon Glass80025522022-01-29 14:14:04 -07004519 tools.get_bytes(ord('!'), 3) + # padding to u-boot align
Simon Glassbb395742020-10-26 17:40:14 -06004520 U_BOOT_DATA +
Simon Glass80025522022-01-29 14:14:04 -07004521 tools.get_bytes(ord('!'), 4) + # padding to u-boot size
4522 tools.get_bytes(ord('!'), 4)) # padding to section size
Simon Glassbb395742020-10-26 17:40:14 -06004523 self.assertEqual(expected, data)
4524
Simon Glassd92c8362020-10-26 17:40:25 -06004525 def testCompressImage(self):
4526 """Test compression of the entire image"""
4527 self._CheckLz4()
4528 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4529 '182_compress_image.dts', use_real_dtb=True, update_dtb=True)
4530 dtb = fdt.Fdt(out_dtb_fname)
4531 dtb.Scan()
4532 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4533 'uncomp-size'])
4534 orig = self._decompress(data)
Brandon Maiera657bc62024-06-04 16:16:05 +00004535 self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, orig)
Simon Glassd92c8362020-10-26 17:40:25 -06004536
4537 # Do a sanity check on various fields
4538 image = control.images['image']
4539 entries = image.GetEntries()
4540 self.assertEqual(2, len(entries))
4541
4542 entry = entries['blob']
4543 self.assertEqual(COMPRESS_DATA, entry.data)
4544 self.assertEqual(len(COMPRESS_DATA), entry.size)
4545
4546 entry = entries['u-boot']
4547 self.assertEqual(U_BOOT_DATA, entry.data)
4548 self.assertEqual(len(U_BOOT_DATA), entry.size)
4549
4550 self.assertEqual(len(data), image.size)
4551 self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, image.uncomp_data)
4552 self.assertEqual(len(COMPRESS_DATA + U_BOOT_DATA), image.uncomp_size)
4553 orig = self._decompress(image.data)
4554 self.assertEqual(orig, image.uncomp_data)
4555
4556 expected = {
4557 'blob:offset': 0,
4558 'blob:size': len(COMPRESS_DATA),
4559 'u-boot:offset': len(COMPRESS_DATA),
4560 'u-boot:size': len(U_BOOT_DATA),
4561 'uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4562 'offset': 0,
4563 'image-pos': 0,
4564 'size': len(data),
4565 }
4566 self.assertEqual(expected, props)
4567
4568 def testCompressImageLess(self):
4569 """Test compression where compression reduces the image size"""
4570 self._CheckLz4()
4571 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4572 '183_compress_image_less.dts', use_real_dtb=True, update_dtb=True)
4573 dtb = fdt.Fdt(out_dtb_fname)
4574 dtb.Scan()
4575 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4576 'uncomp-size'])
4577 orig = self._decompress(data)
4578
Brandon Maiera657bc62024-06-04 16:16:05 +00004579 self.assertEqual(COMPRESS_DATA + COMPRESS_DATA + U_BOOT_DATA, orig)
Simon Glassd92c8362020-10-26 17:40:25 -06004580
4581 # Do a sanity check on various fields
4582 image = control.images['image']
4583 entries = image.GetEntries()
4584 self.assertEqual(2, len(entries))
4585
4586 entry = entries['blob']
4587 self.assertEqual(COMPRESS_DATA_BIG, entry.data)
4588 self.assertEqual(len(COMPRESS_DATA_BIG), entry.size)
4589
4590 entry = entries['u-boot']
4591 self.assertEqual(U_BOOT_DATA, entry.data)
4592 self.assertEqual(len(U_BOOT_DATA), entry.size)
4593
4594 self.assertEqual(len(data), image.size)
4595 self.assertEqual(COMPRESS_DATA_BIG + U_BOOT_DATA, image.uncomp_data)
4596 self.assertEqual(len(COMPRESS_DATA_BIG + U_BOOT_DATA),
4597 image.uncomp_size)
4598 orig = self._decompress(image.data)
4599 self.assertEqual(orig, image.uncomp_data)
4600
4601 expected = {
4602 'blob:offset': 0,
4603 'blob:size': len(COMPRESS_DATA_BIG),
4604 'u-boot:offset': len(COMPRESS_DATA_BIG),
4605 'u-boot:size': len(U_BOOT_DATA),
4606 'uncomp-size': len(COMPRESS_DATA_BIG + U_BOOT_DATA),
4607 'offset': 0,
4608 'image-pos': 0,
4609 'size': len(data),
4610 }
4611 self.assertEqual(expected, props)
4612
4613 def testCompressSectionSize(self):
4614 """Test compression of a section with a fixed size"""
4615 self._CheckLz4()
4616 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4617 '184_compress_section_size.dts', use_real_dtb=True, update_dtb=True)
4618 dtb = fdt.Fdt(out_dtb_fname)
4619 dtb.Scan()
4620 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4621 'uncomp-size'])
Jiaxun Yangc6931742025-04-10 06:43:03 -06004622 data = data[:0x30]
4623 data = data.rstrip(b'\xff')
Simon Glassd92c8362020-10-26 17:40:25 -06004624 orig = self._decompress(data)
Brandon Maiera657bc62024-06-04 16:16:05 +00004625 self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, orig)
Simon Glassd92c8362020-10-26 17:40:25 -06004626 expected = {
4627 'section/blob:offset': 0,
4628 'section/blob:size': len(COMPRESS_DATA),
4629 'section/u-boot:offset': len(COMPRESS_DATA),
4630 'section/u-boot:size': len(U_BOOT_DATA),
4631 'section:offset': 0,
4632 'section:image-pos': 0,
4633 'section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4634 'section:size': 0x30,
4635 'offset': 0,
4636 'image-pos': 0,
4637 'size': 0x30,
4638 }
4639 self.assertEqual(expected, props)
4640
4641 def testCompressSection(self):
4642 """Test compression of a section with no fixed size"""
4643 self._CheckLz4()
4644 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4645 '185_compress_section.dts', use_real_dtb=True, update_dtb=True)
4646 dtb = fdt.Fdt(out_dtb_fname)
4647 dtb.Scan()
4648 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4649 'uncomp-size'])
4650 orig = self._decompress(data)
Brandon Maiera657bc62024-06-04 16:16:05 +00004651 self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, orig)
Simon Glassd92c8362020-10-26 17:40:25 -06004652 expected = {
4653 'section/blob:offset': 0,
4654 'section/blob:size': len(COMPRESS_DATA),
4655 'section/u-boot:offset': len(COMPRESS_DATA),
4656 'section/u-boot:size': len(U_BOOT_DATA),
4657 'section:offset': 0,
4658 'section:image-pos': 0,
4659 'section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4660 'section:size': len(data),
4661 'offset': 0,
4662 'image-pos': 0,
4663 'size': len(data),
4664 }
4665 self.assertEqual(expected, props)
4666
Stefan Herbrechtsmeier94813a02022-08-19 16:25:31 +02004667 def testLz4Missing(self):
4668 """Test that binman still produces an image if lz4 is missing"""
Simon Glass14d64e32025-04-29 07:21:59 -06004669 with terminal.capture() as (_, stderr):
Stefan Herbrechtsmeier94813a02022-08-19 16:25:31 +02004670 self._DoTestFile('185_compress_section.dts',
4671 force_missing_bintools='lz4')
4672 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07004673 self.assertRegex(err, "Image 'image'.*missing bintools.*: lz4")
Stefan Herbrechtsmeier94813a02022-08-19 16:25:31 +02004674
Simon Glassd92c8362020-10-26 17:40:25 -06004675 def testCompressExtra(self):
4676 """Test compression of a section with no fixed size"""
4677 self._CheckLz4()
4678 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4679 '186_compress_extra.dts', use_real_dtb=True, update_dtb=True)
4680 dtb = fdt.Fdt(out_dtb_fname)
4681 dtb.Scan()
4682 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4683 'uncomp-size'])
4684
4685 base = data[len(U_BOOT_DATA):]
Brandon Maiera657bc62024-06-04 16:16:05 +00004686 self.assertEqual(U_BOOT_DATA, base[:len(U_BOOT_DATA)])
Simon Glassd92c8362020-10-26 17:40:25 -06004687 rest = base[len(U_BOOT_DATA):]
4688
4689 # Check compressed data
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +02004690 bintool = self.comp_bintools['lz4']
4691 expect1 = bintool.compress(COMPRESS_DATA + U_BOOT_DATA)
Stefan Herbrechtsmeier86f1fc02022-08-19 16:25:23 +02004692 data1 = rest[:len(expect1)]
4693 section1 = self._decompress(data1)
Brandon Maiera657bc62024-06-04 16:16:05 +00004694 self.assertEqual(expect1, data1)
4695 self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, section1)
Simon Glassd92c8362020-10-26 17:40:25 -06004696 rest1 = rest[len(expect1):]
4697
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +02004698 expect2 = bintool.compress(COMPRESS_DATA + COMPRESS_DATA)
Stefan Herbrechtsmeier86f1fc02022-08-19 16:25:23 +02004699 data2 = rest1[:len(expect2)]
4700 section2 = self._decompress(data2)
Brandon Maiera657bc62024-06-04 16:16:05 +00004701 self.assertEqual(expect2, data2)
4702 self.assertEqual(COMPRESS_DATA + COMPRESS_DATA, section2)
Simon Glassd92c8362020-10-26 17:40:25 -06004703 rest2 = rest1[len(expect2):]
4704
4705 expect_size = (len(U_BOOT_DATA) + len(U_BOOT_DATA) + len(expect1) +
4706 len(expect2) + len(U_BOOT_DATA))
Brandon Maiera657bc62024-06-04 16:16:05 +00004707 #self.assertEqual(expect_size, len(data))
Simon Glassd92c8362020-10-26 17:40:25 -06004708
Brandon Maiera657bc62024-06-04 16:16:05 +00004709 #self.assertEqual(U_BOOT_DATA, rest2)
Simon Glassd92c8362020-10-26 17:40:25 -06004710
4711 self.maxDiff = None
4712 expected = {
4713 'u-boot:offset': 0,
4714 'u-boot:image-pos': 0,
4715 'u-boot:size': len(U_BOOT_DATA),
4716
4717 'base:offset': len(U_BOOT_DATA),
4718 'base:image-pos': len(U_BOOT_DATA),
4719 'base:size': len(data) - len(U_BOOT_DATA),
4720 'base/u-boot:offset': 0,
4721 'base/u-boot:image-pos': len(U_BOOT_DATA),
4722 'base/u-boot:size': len(U_BOOT_DATA),
4723 'base/u-boot2:offset': len(U_BOOT_DATA) + len(expect1) +
4724 len(expect2),
4725 'base/u-boot2:image-pos': len(U_BOOT_DATA) * 2 + len(expect1) +
4726 len(expect2),
4727 'base/u-boot2:size': len(U_BOOT_DATA),
4728
4729 'base/section:offset': len(U_BOOT_DATA),
4730 'base/section:image-pos': len(U_BOOT_DATA) * 2,
4731 'base/section:size': len(expect1),
4732 'base/section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4733 'base/section/blob:offset': 0,
4734 'base/section/blob:size': len(COMPRESS_DATA),
4735 'base/section/u-boot:offset': len(COMPRESS_DATA),
4736 'base/section/u-boot:size': len(U_BOOT_DATA),
4737
4738 'base/section2:offset': len(U_BOOT_DATA) + len(expect1),
4739 'base/section2:image-pos': len(U_BOOT_DATA) * 2 + len(expect1),
4740 'base/section2:size': len(expect2),
4741 'base/section2:uncomp-size': len(COMPRESS_DATA + COMPRESS_DATA),
4742 'base/section2/blob:offset': 0,
4743 'base/section2/blob:size': len(COMPRESS_DATA),
4744 'base/section2/blob2:offset': len(COMPRESS_DATA),
4745 'base/section2/blob2:size': len(COMPRESS_DATA),
4746
4747 'offset': 0,
4748 'image-pos': 0,
4749 'size': len(data),
4750 }
4751 self.assertEqual(expected, props)
4752
Simon Glassecbe4732021-01-06 21:35:15 -07004753 def testSymbolsSubsection(self):
4754 """Test binman can assign symbols from a subsection"""
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03004755 self.checkSymbols('187_symbols_sub.dts', U_BOOT_SPL_DATA, 0x1c)
Simon Glassecbe4732021-01-06 21:35:15 -07004756
Simon Glass3fb25402021-01-06 21:35:16 -07004757 def testReadImageEntryArg(self):
4758 """Test reading an image that would need an entry arg to generate"""
4759 entry_args = {
4760 'cros-ec-rw-path': 'ecrw.bin',
4761 }
4762 data = self.data = self._DoReadFileDtb(
4763 '188_image_entryarg.dts',use_real_dtb=True, update_dtb=True,
4764 entry_args=entry_args)
4765
Simon Glass80025522022-01-29 14:14:04 -07004766 image_fname = tools.get_output_filename('image.bin')
Simon Glass3fb25402021-01-06 21:35:16 -07004767 orig_image = control.images['image']
4768
4769 # This should not generate an error about the missing 'cros-ec-rw-path'
4770 # since we are reading the image from a file. Compare with
4771 # testEntryArgsRequired()
4772 image = Image.FromFile(image_fname)
4773 self.assertEqual(orig_image.GetEntries().keys(),
4774 image.GetEntries().keys())
4775
Simon Glassa2af7302021-01-06 21:35:18 -07004776 def testFilesAlign(self):
4777 """Test alignment with files"""
4778 data = self._DoReadFile('190_files_align.dts')
4779
4780 # The first string is 15 bytes so will align to 16
4781 expect = FILES_DATA[:15] + b'\0' + FILES_DATA[15:]
4782 self.assertEqual(expect, data)
4783
Simon Glassdb84b562021-01-06 21:35:19 -07004784 def testReadImageSkip(self):
4785 """Test reading an image and accessing its FDT map"""
4786 data = self.data = self._DoReadFileRealDtb('191_read_image_skip.dts')
Simon Glass80025522022-01-29 14:14:04 -07004787 image_fname = tools.get_output_filename('image.bin')
Simon Glassdb84b562021-01-06 21:35:19 -07004788 orig_image = control.images['image']
4789 image = Image.FromFile(image_fname)
4790 self.assertEqual(orig_image.GetEntries().keys(),
4791 image.GetEntries().keys())
4792
4793 orig_entry = orig_image.GetEntries()['fdtmap']
4794 entry = image.GetEntries()['fdtmap']
4795 self.assertEqual(orig_entry.offset, entry.offset)
4796 self.assertEqual(orig_entry.size, entry.size)
Simon Glassed836ac2025-02-26 09:26:17 -07004797 self.assertEqual((1 << 32) - 0x400 + 16, entry.image_pos)
Simon Glassdb84b562021-01-06 21:35:19 -07004798
4799 u_boot = image.GetEntries()['section'].GetEntries()['u-boot']
4800
Brandon Maiera657bc62024-06-04 16:16:05 +00004801 self.assertEqual(U_BOOT_DATA, u_boot.ReadData())
Simon Glassdb84b562021-01-06 21:35:19 -07004802
Simon Glassc98de972021-03-18 20:24:57 +13004803 def testTplNoDtb(self):
4804 """Test that an image with tpl/u-boot-tpl-nodtb.bin can be created"""
Simon Glass13089cc2021-04-25 08:39:32 +12004805 self._SetupTplElf()
Simon Glassc98de972021-03-18 20:24:57 +13004806 data = self._DoReadFile('192_u_boot_tpl_nodtb.dts')
4807 self.assertEqual(U_BOOT_TPL_NODTB_DATA,
4808 data[:len(U_BOOT_TPL_NODTB_DATA)])
4809
Simon Glass63f41d42021-03-18 20:24:58 +13004810 def testTplBssPad(self):
4811 """Test that we can pad TPL's BSS with zeros"""
4812 # ELF file with a '__bss_size' symbol
4813 self._SetupTplElf()
4814 data = self._DoReadFile('193_tpl_bss_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07004815 self.assertEqual(U_BOOT_TPL_DATA + tools.get_bytes(0, 10) + U_BOOT_DATA,
Simon Glass63f41d42021-03-18 20:24:58 +13004816 data)
4817
4818 def testTplBssPadMissing(self):
4819 """Test that a missing symbol is detected"""
4820 self._SetupTplElf('u_boot_ucode_ptr')
4821 with self.assertRaises(ValueError) as e:
4822 self._DoReadFile('193_tpl_bss_pad.dts')
4823 self.assertIn('Expected __bss_size symbol in tpl/u-boot-tpl',
4824 str(e.exception))
4825
Simon Glass718b5292021-03-18 20:25:07 +13004826 def checkDtbSizes(self, data, pad_len, start):
4827 """Check the size arguments in a dtb embedded in an image
4828
4829 Args:
4830 data: The image data
4831 pad_len: Length of the pad section in the image, in bytes
4832 start: Start offset of the devicetree to examine, within the image
4833
4834 Returns:
4835 Size of the devicetree in bytes
4836 """
4837 dtb_data = data[start:]
4838 dtb = fdt.Fdt.FromData(dtb_data)
4839 fdt_size = dtb.GetFdtObj().totalsize()
4840 dtb.Scan()
4841 props = self._GetPropTree(dtb, 'size')
4842 self.assertEqual({
4843 'size': len(data),
4844 'u-boot-spl/u-boot-spl-bss-pad:size': pad_len,
4845 'u-boot-spl/u-boot-spl-dtb:size': 801,
4846 'u-boot-spl/u-boot-spl-nodtb:size': len(U_BOOT_SPL_NODTB_DATA),
4847 'u-boot-spl:size': 860,
4848 'u-boot-tpl:size': len(U_BOOT_TPL_DATA),
4849 'u-boot/u-boot-dtb:size': 781,
4850 'u-boot/u-boot-nodtb:size': len(U_BOOT_NODTB_DATA),
4851 'u-boot:size': 827,
4852 }, props)
4853 return fdt_size
4854
4855 def testExpanded(self):
4856 """Test that an expanded entry type is selected when needed"""
4857 self._SetupSplElf()
4858 self._SetupTplElf()
4859
4860 # SPL has a devicetree, TPL does not
4861 entry_args = {
4862 'spl-dtb': '1',
4863 'spl-bss-pad': 'y',
4864 'tpl-dtb': '',
4865 }
4866 self._DoReadFileDtb('194_fdt_incl.dts', use_expanded=True,
4867 entry_args=entry_args)
4868 image = control.images['image']
4869 entries = image.GetEntries()
4870 self.assertEqual(3, len(entries))
4871
4872 # First, u-boot, which should be expanded into u-boot-nodtb and dtb
4873 self.assertIn('u-boot', entries)
4874 entry = entries['u-boot']
4875 self.assertEqual('u-boot-expanded', entry.etype)
4876 subent = entry.GetEntries()
4877 self.assertEqual(2, len(subent))
4878 self.assertIn('u-boot-nodtb', subent)
4879 self.assertIn('u-boot-dtb', subent)
4880
4881 # Second, u-boot-spl, which should be expanded into three parts
4882 self.assertIn('u-boot-spl', entries)
4883 entry = entries['u-boot-spl']
4884 self.assertEqual('u-boot-spl-expanded', entry.etype)
4885 subent = entry.GetEntries()
4886 self.assertEqual(3, len(subent))
4887 self.assertIn('u-boot-spl-nodtb', subent)
4888 self.assertIn('u-boot-spl-bss-pad', subent)
4889 self.assertIn('u-boot-spl-dtb', subent)
4890
4891 # Third, u-boot-tpl, which should be not be expanded, since TPL has no
4892 # devicetree
4893 self.assertIn('u-boot-tpl', entries)
4894 entry = entries['u-boot-tpl']
4895 self.assertEqual('u-boot-tpl', entry.etype)
4896 self.assertEqual(None, entry.GetEntries())
4897
4898 def testExpandedTpl(self):
4899 """Test that an expanded entry type is selected for TPL when needed"""
4900 self._SetupTplElf()
4901
4902 entry_args = {
4903 'tpl-bss-pad': 'y',
4904 'tpl-dtb': 'y',
4905 }
4906 self._DoReadFileDtb('195_fdt_incl_tpl.dts', use_expanded=True,
4907 entry_args=entry_args)
4908 image = control.images['image']
4909 entries = image.GetEntries()
4910 self.assertEqual(1, len(entries))
4911
4912 # We only have u-boot-tpl, which be expanded
4913 self.assertIn('u-boot-tpl', entries)
4914 entry = entries['u-boot-tpl']
4915 self.assertEqual('u-boot-tpl-expanded', entry.etype)
4916 subent = entry.GetEntries()
4917 self.assertEqual(3, len(subent))
4918 self.assertIn('u-boot-tpl-nodtb', subent)
4919 self.assertIn('u-boot-tpl-bss-pad', subent)
4920 self.assertIn('u-boot-tpl-dtb', subent)
4921
4922 def testExpandedNoPad(self):
4923 """Test an expanded entry without BSS pad enabled"""
4924 self._SetupSplElf()
4925 self._SetupTplElf()
4926
4927 # SPL has a devicetree, TPL does not
4928 entry_args = {
4929 'spl-dtb': 'something',
4930 'spl-bss-pad': 'n',
4931 'tpl-dtb': '',
4932 }
4933 self._DoReadFileDtb('194_fdt_incl.dts', use_expanded=True,
4934 entry_args=entry_args)
4935 image = control.images['image']
4936 entries = image.GetEntries()
4937
4938 # Just check u-boot-spl, which should be expanded into two parts
4939 self.assertIn('u-boot-spl', entries)
4940 entry = entries['u-boot-spl']
4941 self.assertEqual('u-boot-spl-expanded', entry.etype)
4942 subent = entry.GetEntries()
4943 self.assertEqual(2, len(subent))
4944 self.assertIn('u-boot-spl-nodtb', subent)
4945 self.assertIn('u-boot-spl-dtb', subent)
4946
4947 def testExpandedTplNoPad(self):
4948 """Test that an expanded entry type with padding disabled in TPL"""
4949 self._SetupTplElf()
4950
4951 entry_args = {
4952 'tpl-bss-pad': '',
4953 'tpl-dtb': 'y',
4954 }
4955 self._DoReadFileDtb('195_fdt_incl_tpl.dts', use_expanded=True,
4956 entry_args=entry_args)
4957 image = control.images['image']
4958 entries = image.GetEntries()
4959 self.assertEqual(1, len(entries))
4960
4961 # We only have u-boot-tpl, which be expanded
4962 self.assertIn('u-boot-tpl', entries)
4963 entry = entries['u-boot-tpl']
4964 self.assertEqual('u-boot-tpl-expanded', entry.etype)
4965 subent = entry.GetEntries()
4966 self.assertEqual(2, len(subent))
4967 self.assertIn('u-boot-tpl-nodtb', subent)
4968 self.assertIn('u-boot-tpl-dtb', subent)
4969
4970 def testFdtInclude(self):
4971 """Test that an Fdt is update within all binaries"""
4972 self._SetupSplElf()
4973 self._SetupTplElf()
4974
4975 # SPL has a devicetree, TPL does not
4976 self.maxDiff = None
4977 entry_args = {
4978 'spl-dtb': '1',
4979 'spl-bss-pad': 'y',
4980 'tpl-dtb': '',
4981 }
4982 # Build the image. It includes two separate devicetree binaries, each
4983 # with their own contents, but all contain the binman definition.
4984 data = self._DoReadFileDtb(
4985 '194_fdt_incl.dts', use_real_dtb=True, use_expanded=True,
4986 update_dtb=True, entry_args=entry_args)[0]
4987 pad_len = 10
4988
4989 # Check the U-Boot dtb
4990 start = len(U_BOOT_NODTB_DATA)
4991 fdt_size = self.checkDtbSizes(data, pad_len, start)
4992
4993 # Now check SPL
4994 start += fdt_size + len(U_BOOT_SPL_NODTB_DATA) + pad_len
4995 fdt_size = self.checkDtbSizes(data, pad_len, start)
4996
4997 # TPL has no devicetree
4998 start += fdt_size + len(U_BOOT_TPL_DATA)
4999 self.assertEqual(len(data), start)
Simon Glassbb395742020-10-26 17:40:14 -06005000
Simon Glass7098b7f2021-03-21 18:24:30 +13005001 def testSymbolsExpanded(self):
5002 """Test binman can assign symbols in expanded entries"""
5003 entry_args = {
5004 'spl-dtb': '1',
5005 }
5006 self.checkSymbols('197_symbols_expand.dts', U_BOOT_SPL_NODTB_DATA +
5007 U_BOOT_SPL_DTB_DATA, 0x38,
5008 entry_args=entry_args, use_expanded=True)
5009
Simon Glasse1915782021-03-21 18:24:31 +13005010 def testCollection(self):
5011 """Test a collection"""
5012 data = self._DoReadFile('198_collection.dts')
5013 self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA +
Simon Glass80025522022-01-29 14:14:04 -07005014 tools.get_bytes(0xff, 2) + U_BOOT_NODTB_DATA +
5015 tools.get_bytes(0xfe, 3) + U_BOOT_DTB_DATA,
Simon Glasse1915782021-03-21 18:24:31 +13005016 data)
5017
Simon Glass27a7f772021-03-21 18:24:32 +13005018 def testCollectionSection(self):
5019 """Test a collection where a section must be built first"""
5020 # Sections never have their contents when GetData() is called, but when
Simon Glass7e3f89f2021-11-23 11:03:47 -07005021 # BuildSectionData() is called with required=True, a section will force
Simon Glass27a7f772021-03-21 18:24:32 +13005022 # building the contents, producing an error is anything is still
5023 # missing.
5024 data = self._DoReadFile('199_collection_section.dts')
5025 section = U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA
Simon Glass80025522022-01-29 14:14:04 -07005026 self.assertEqual(section + U_BOOT_DATA + tools.get_bytes(0xff, 2) +
5027 section + tools.get_bytes(0xfe, 3) + U_BOOT_DATA,
Simon Glass27a7f772021-03-21 18:24:32 +13005028 data)
5029
Simon Glassf427c5f2021-03-21 18:24:33 +13005030 def testAlignDefault(self):
5031 """Test that default alignment works on sections"""
5032 data = self._DoReadFile('200_align_default.dts')
Simon Glass80025522022-01-29 14:14:04 -07005033 expected = (U_BOOT_DATA + tools.get_bytes(0, 8 - len(U_BOOT_DATA)) +
Simon Glassf427c5f2021-03-21 18:24:33 +13005034 U_BOOT_DATA)
5035 # Special alignment for section
Simon Glass80025522022-01-29 14:14:04 -07005036 expected += tools.get_bytes(0, 32 - len(expected))
Simon Glassf427c5f2021-03-21 18:24:33 +13005037 # No alignment within the nested section
5038 expected += U_BOOT_DATA + U_BOOT_NODTB_DATA;
5039 # Now the final piece, which should be default-aligned
Simon Glass80025522022-01-29 14:14:04 -07005040 expected += tools.get_bytes(0, 88 - len(expected)) + U_BOOT_NODTB_DATA
Simon Glassf427c5f2021-03-21 18:24:33 +13005041 self.assertEqual(expected, data)
Simon Glass27a7f772021-03-21 18:24:32 +13005042
Bin Mengc0b15742021-05-10 20:23:33 +08005043 def testPackOpenSBI(self):
5044 """Test that an image with an OpenSBI binary can be created"""
5045 data = self._DoReadFile('201_opensbi.dts')
5046 self.assertEqual(OPENSBI_DATA, data[:len(OPENSBI_DATA)])
5047
Simon Glass76f496d2021-07-06 10:36:37 -06005048 def testSectionsSingleThread(self):
5049 """Test sections without multithreading"""
5050 data = self._DoReadFileDtb('055_sections.dts', threads=0)[0]
Simon Glass80025522022-01-29 14:14:04 -07005051 expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) +
5052 U_BOOT_DATA + tools.get_bytes(ord('a'), 12) +
5053 U_BOOT_DATA + tools.get_bytes(ord('&'), 4))
Simon Glass76f496d2021-07-06 10:36:37 -06005054 self.assertEqual(expected, data)
5055
5056 def testThreadTimeout(self):
5057 """Test handling a thread that takes too long"""
5058 with self.assertRaises(ValueError) as e:
5059 self._DoTestFile('202_section_timeout.dts',
5060 test_section_timeout=True)
Simon Glass2d59d152021-10-18 12:13:15 -06005061 self.assertIn("Timed out obtaining contents", str(e.exception))
Simon Glass76f496d2021-07-06 10:36:37 -06005062
Simon Glass748a1d42021-07-06 10:36:41 -06005063 def testTiming(self):
5064 """Test output of timing information"""
5065 data = self._DoReadFile('055_sections.dts')
Simon Glass14d64e32025-04-29 07:21:59 -06005066 with terminal.capture() as (stdout, stderr):
Simon Glass748a1d42021-07-06 10:36:41 -06005067 state.TimingShow()
5068 self.assertIn('read:', stdout.getvalue())
5069 self.assertIn('compress:', stdout.getvalue())
5070
Simon Glassadfb8492021-11-03 21:09:18 -06005071 def testUpdateFdtInElf(self):
5072 """Test that we can update the devicetree in an ELF file"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005073 if not elf.ELF_TOOLS:
5074 self.skipTest('Python elftools not available')
Simon Glassadfb8492021-11-03 21:09:18 -06005075 infile = elf_fname = self.ElfTestFile('u_boot_binman_embed')
5076 outfile = os.path.join(self._indir, 'u-boot.out')
5077 begin_sym = 'dtb_embed_begin'
5078 end_sym = 'dtb_embed_end'
5079 retcode = self._DoTestFile(
5080 '060_fdt_update.dts', update_dtb=True,
5081 update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym]))
5082 self.assertEqual(0, retcode)
5083
5084 # Check that the output file does in fact contact a dtb with the binman
5085 # definition in the correct place
5086 syms = elf.GetSymbolFileOffset(infile,
5087 ['dtb_embed_begin', 'dtb_embed_end'])
Simon Glass80025522022-01-29 14:14:04 -07005088 data = tools.read_file(outfile)
Simon Glassadfb8492021-11-03 21:09:18 -06005089 dtb_data = data[syms['dtb_embed_begin'].offset:
5090 syms['dtb_embed_end'].offset]
5091
5092 dtb = fdt.Fdt.FromData(dtb_data)
5093 dtb.Scan()
5094 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
5095 self.assertEqual({
5096 'image-pos': 0,
5097 'offset': 0,
5098 '_testing:offset': 32,
5099 '_testing:size': 2,
5100 '_testing:image-pos': 32,
5101 'section@0/u-boot:offset': 0,
5102 'section@0/u-boot:size': len(U_BOOT_DATA),
5103 'section@0/u-boot:image-pos': 0,
5104 'section@0:offset': 0,
5105 'section@0:size': 16,
5106 'section@0:image-pos': 0,
5107
5108 'section@1/u-boot:offset': 0,
5109 'section@1/u-boot:size': len(U_BOOT_DATA),
5110 'section@1/u-boot:image-pos': 16,
5111 'section@1:offset': 16,
5112 'section@1:size': 16,
5113 'section@1:image-pos': 16,
5114 'size': 40
5115 }, props)
5116
5117 def testUpdateFdtInElfInvalid(self):
5118 """Test that invalid args are detected with --update-fdt-in-elf"""
5119 with self.assertRaises(ValueError) as e:
5120 self._DoTestFile('060_fdt_update.dts', update_fdt_in_elf='fred')
5121 self.assertIn("Invalid args ['fred'] to --update-fdt-in-elf",
5122 str(e.exception))
5123
5124 def testUpdateFdtInElfNoSyms(self):
5125 """Test that missing symbols are detected with --update-fdt-in-elf"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005126 if not elf.ELF_TOOLS:
5127 self.skipTest('Python elftools not available')
Simon Glassadfb8492021-11-03 21:09:18 -06005128 infile = elf_fname = self.ElfTestFile('u_boot_binman_embed')
5129 outfile = ''
5130 begin_sym = 'wrong_begin'
5131 end_sym = 'wrong_end'
5132 with self.assertRaises(ValueError) as e:
5133 self._DoTestFile(
5134 '060_fdt_update.dts',
5135 update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym]))
5136 self.assertIn("Expected two symbols 'wrong_begin' and 'wrong_end': got 0:",
5137 str(e.exception))
5138
5139 def testUpdateFdtInElfTooSmall(self):
5140 """Test that an over-large dtb is detected with --update-fdt-in-elf"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005141 if not elf.ELF_TOOLS:
5142 self.skipTest('Python elftools not available')
Simon Glassadfb8492021-11-03 21:09:18 -06005143 infile = elf_fname = self.ElfTestFile('u_boot_binman_embed_sm')
5144 outfile = os.path.join(self._indir, 'u-boot.out')
5145 begin_sym = 'dtb_embed_begin'
5146 end_sym = 'dtb_embed_end'
5147 with self.assertRaises(ValueError) as e:
5148 self._DoTestFile(
5149 '060_fdt_update.dts', update_dtb=True,
5150 update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym]))
5151 self.assertRegex(
5152 str(e.exception),
5153 "Not enough space in '.*u_boot_binman_embed_sm' for data length.*")
5154
Simon Glass88e04da2021-11-23 11:03:42 -07005155 def testVersion(self):
5156 """Test we can get the binman version"""
5157 version = '(unreleased)'
5158 self.assertEqual(version, state.GetVersion(self._indir))
5159
5160 with self.assertRaises(SystemExit):
Simon Glass14d64e32025-04-29 07:21:59 -06005161 with terminal.capture() as (_, stderr):
Simon Glass88e04da2021-11-23 11:03:42 -07005162 self._DoBinman('-V')
5163 self.assertEqual('Binman %s\n' % version, stderr.getvalue())
5164
5165 # Try running the tool too, just to be safe
5166 result = self._RunBinman('-V')
5167 self.assertEqual('Binman %s\n' % version, result.stderr)
5168
5169 # Set up a version file to make sure that works
5170 version = 'v2025.01-rc2'
Simon Glass80025522022-01-29 14:14:04 -07005171 tools.write_file(os.path.join(self._indir, 'version'), version,
Simon Glass88e04da2021-11-23 11:03:42 -07005172 binary=False)
5173 self.assertEqual(version, state.GetVersion(self._indir))
5174
Simon Glass637958f2021-11-23 21:09:50 -07005175 def testAltFormat(self):
5176 """Test that alternative formats can be used to extract"""
5177 self._DoReadFileRealDtb('213_fdtmap_alt_format.dts')
5178
5179 try:
5180 tmpdir, updated_fname = self._SetupImageInTmpdir()
Simon Glass14d64e32025-04-29 07:21:59 -06005181 with terminal.capture() as (stdout, _):
Simon Glass637958f2021-11-23 21:09:50 -07005182 self._DoBinman('extract', '-i', updated_fname, '-F', 'list')
5183 self.assertEqual(
5184 '''Flag (-F) Entry type Description
5185fdt fdtmap Extract the devicetree blob from the fdtmap
5186''',
5187 stdout.getvalue())
5188
5189 dtb = os.path.join(tmpdir, 'fdt.dtb')
5190 self._DoBinman('extract', '-i', updated_fname, '-F', 'fdt', '-f',
5191 dtb, 'fdtmap')
5192
5193 # Check that we can read it and it can be scanning, meaning it does
5194 # not have a 16-byte fdtmap header
Simon Glass80025522022-01-29 14:14:04 -07005195 data = tools.read_file(dtb)
Simon Glass637958f2021-11-23 21:09:50 -07005196 dtb = fdt.Fdt.FromData(data)
5197 dtb.Scan()
5198
5199 # Now check u-boot which has no alt_format
5200 fname = os.path.join(tmpdir, 'fdt.dtb')
5201 self._DoBinman('extract', '-i', updated_fname, '-F', 'dummy',
5202 '-f', fname, 'u-boot')
Simon Glass80025522022-01-29 14:14:04 -07005203 data = tools.read_file(fname)
Simon Glass637958f2021-11-23 21:09:50 -07005204 self.assertEqual(U_BOOT_DATA, data)
5205
5206 finally:
5207 shutil.rmtree(tmpdir)
5208
Simon Glass0b00ae62021-11-23 21:09:52 -07005209 def testExtblobList(self):
5210 """Test an image with an external blob list"""
5211 data = self._DoReadFile('215_blob_ext_list.dts')
5212 self.assertEqual(REFCODE_DATA + FSP_M_DATA, data)
5213
5214 def testExtblobListMissing(self):
5215 """Test an image with a missing external blob"""
5216 with self.assertRaises(ValueError) as e:
5217 self._DoReadFile('216_blob_ext_list_missing.dts')
5218 self.assertIn("Filename 'missing-file' not found in input path",
5219 str(e.exception))
5220
5221 def testExtblobListMissingOk(self):
5222 """Test an image with an missing external blob that is allowed"""
Simon Glass14d64e32025-04-29 07:21:59 -06005223 with terminal.capture() as (stdout, stderr):
Simon Glass0b00ae62021-11-23 21:09:52 -07005224 self._DoTestFile('216_blob_ext_list_missing.dts',
5225 allow_missing=True)
5226 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07005227 self.assertRegex(err, "Image 'image'.*missing.*: blob-ext")
Simon Glass0b00ae62021-11-23 21:09:52 -07005228
Simon Glass3efb2972021-11-23 21:08:59 -07005229 def testFip(self):
5230 """Basic test of generation of an ARM Firmware Image Package (FIP)"""
5231 data = self._DoReadFile('203_fip.dts')
5232 hdr, fents = fip_util.decode_fip(data)
5233 self.assertEqual(fip_util.HEADER_MAGIC, hdr.name)
5234 self.assertEqual(fip_util.HEADER_SERIAL, hdr.serial)
5235 self.assertEqual(0x123, hdr.flags)
5236
5237 self.assertEqual(2, len(fents))
5238
5239 fent = fents[0]
5240 self.assertEqual(
5241 bytes([0x47, 0xd4, 0x08, 0x6d, 0x4c, 0xfe, 0x98, 0x46,
5242 0x9b, 0x95, 0x29, 0x50, 0xcb, 0xbd, 0x5a, 0x0]), fent.uuid)
5243 self.assertEqual('soc-fw', fent.fip_type)
5244 self.assertEqual(0x88, fent.offset)
5245 self.assertEqual(len(ATF_BL31_DATA), fent.size)
5246 self.assertEqual(0x123456789abcdef, fent.flags)
5247 self.assertEqual(ATF_BL31_DATA, fent.data)
5248 self.assertEqual(True, fent.valid)
5249
5250 fent = fents[1]
5251 self.assertEqual(
5252 bytes([0x65, 0x92, 0x27, 0x03, 0x2f, 0x74, 0xe6, 0x44,
5253 0x8d, 0xff, 0x57, 0x9a, 0xc1, 0xff, 0x06, 0x10]), fent.uuid)
5254 self.assertEqual('scp-fwu-cfg', fent.fip_type)
5255 self.assertEqual(0x8c, fent.offset)
5256 self.assertEqual(len(ATF_BL31_DATA), fent.size)
5257 self.assertEqual(0, fent.flags)
5258 self.assertEqual(ATF_BL2U_DATA, fent.data)
5259 self.assertEqual(True, fent.valid)
5260
5261 def testFipOther(self):
5262 """Basic FIP with something that isn't a external blob"""
5263 data = self._DoReadFile('204_fip_other.dts')
5264 hdr, fents = fip_util.decode_fip(data)
5265
5266 self.assertEqual(2, len(fents))
5267 fent = fents[1]
5268 self.assertEqual('rot-cert', fent.fip_type)
5269 self.assertEqual(b'aa', fent.data)
5270
Simon Glass3efb2972021-11-23 21:08:59 -07005271 def testFipNoType(self):
5272 """FIP with an entry of an unknown type"""
5273 with self.assertRaises(ValueError) as e:
5274 self._DoReadFile('205_fip_no_type.dts')
5275 self.assertIn("Must provide a fip-type (node name 'u-boot' is not a known FIP type)",
5276 str(e.exception))
5277
5278 def testFipUuid(self):
5279 """Basic FIP with a manual uuid"""
5280 data = self._DoReadFile('206_fip_uuid.dts')
5281 hdr, fents = fip_util.decode_fip(data)
5282
5283 self.assertEqual(2, len(fents))
5284 fent = fents[1]
5285 self.assertEqual(None, fent.fip_type)
5286 self.assertEqual(
5287 bytes([0xfc, 0x65, 0x13, 0x92, 0x4a, 0x5b, 0x11, 0xec,
5288 0x94, 0x35, 0xff, 0x2d, 0x1c, 0xfc, 0x79, 0x9c]),
5289 fent.uuid)
5290 self.assertEqual(U_BOOT_DATA, fent.data)
5291
5292 def testFipLs(self):
5293 """Test listing a FIP"""
5294 data = self._DoReadFileRealDtb('207_fip_ls.dts')
5295 hdr, fents = fip_util.decode_fip(data)
5296
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01005297 tmpdir = None
Simon Glass3efb2972021-11-23 21:08:59 -07005298 try:
5299 tmpdir, updated_fname = self._SetupImageInTmpdir()
Simon Glass14d64e32025-04-29 07:21:59 -06005300 with terminal.capture() as (stdout, stderr):
Simon Glass3efb2972021-11-23 21:08:59 -07005301 self._DoBinman('ls', '-i', updated_fname)
5302 finally:
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01005303 if tmpdir:
5304 shutil.rmtree(tmpdir)
Simon Glass3efb2972021-11-23 21:08:59 -07005305 lines = stdout.getvalue().splitlines()
5306 expected = [
Simon Glass49cd2b32023-02-07 14:34:18 -07005307'Name Image-pos Size Entry-type Offset Uncomp-size',
5308'--------------------------------------------------------------',
5309'image 0 2d3 section 0',
5310' atf-fip 0 90 atf-fip 0',
5311' soc-fw 88 4 blob-ext 88',
5312' u-boot 8c 4 u-boot 8c',
5313' fdtmap 90 243 fdtmap 90',
Simon Glass3efb2972021-11-23 21:08:59 -07005314]
5315 self.assertEqual(expected, lines)
5316
5317 image = control.images['image']
5318 entries = image.GetEntries()
5319 fdtmap = entries['fdtmap']
5320
5321 fdtmap_data = data[fdtmap.image_pos:fdtmap.image_pos + fdtmap.size]
5322 magic = fdtmap_data[:8]
5323 self.assertEqual(b'_FDTMAP_', magic)
Simon Glass80025522022-01-29 14:14:04 -07005324 self.assertEqual(tools.get_bytes(0, 8), fdtmap_data[8:16])
Simon Glass3efb2972021-11-23 21:08:59 -07005325
5326 fdt_data = fdtmap_data[16:]
5327 dtb = fdt.Fdt.FromData(fdt_data)
5328 dtb.Scan()
5329 props = self._GetPropTree(dtb, BASE_DTB_PROPS, prefix='/')
5330 self.assertEqual({
5331 'atf-fip/soc-fw:image-pos': 136,
5332 'atf-fip/soc-fw:offset': 136,
5333 'atf-fip/soc-fw:size': 4,
5334 'atf-fip/u-boot:image-pos': 140,
5335 'atf-fip/u-boot:offset': 140,
5336 'atf-fip/u-boot:size': 4,
5337 'atf-fip:image-pos': 0,
5338 'atf-fip:offset': 0,
5339 'atf-fip:size': 144,
5340 'image-pos': 0,
5341 'offset': 0,
5342 'fdtmap:image-pos': fdtmap.image_pos,
5343 'fdtmap:offset': fdtmap.offset,
5344 'fdtmap:size': len(fdtmap_data),
5345 'size': len(data),
5346 }, props)
5347
5348 def testFipExtractOneEntry(self):
5349 """Test extracting a single entry fron an FIP"""
5350 self._DoReadFileRealDtb('207_fip_ls.dts')
Simon Glass80025522022-01-29 14:14:04 -07005351 image_fname = tools.get_output_filename('image.bin')
Simon Glass3efb2972021-11-23 21:08:59 -07005352 fname = os.path.join(self._indir, 'output.extact')
5353 control.ExtractEntries(image_fname, fname, None, ['atf-fip/u-boot'])
Simon Glass80025522022-01-29 14:14:04 -07005354 data = tools.read_file(fname)
Simon Glass3efb2972021-11-23 21:08:59 -07005355 self.assertEqual(U_BOOT_DATA, data)
5356
5357 def testFipReplace(self):
5358 """Test replacing a single file in a FIP"""
Simon Glass80025522022-01-29 14:14:04 -07005359 expected = U_BOOT_DATA + tools.get_bytes(0x78, 50)
Simon Glass3efb2972021-11-23 21:08:59 -07005360 data = self._DoReadFileRealDtb('208_fip_replace.dts')
Simon Glass80025522022-01-29 14:14:04 -07005361 updated_fname = tools.get_output_filename('image-updated.bin')
5362 tools.write_file(updated_fname, data)
Simon Glass3efb2972021-11-23 21:08:59 -07005363 entry_name = 'atf-fip/u-boot'
5364 control.WriteEntry(updated_fname, entry_name, expected,
5365 allow_resize=True)
5366 actual = control.ReadEntry(updated_fname, entry_name)
5367 self.assertEqual(expected, actual)
5368
Simon Glass80025522022-01-29 14:14:04 -07005369 new_data = tools.read_file(updated_fname)
Simon Glass3efb2972021-11-23 21:08:59 -07005370 hdr, fents = fip_util.decode_fip(new_data)
5371
5372 self.assertEqual(2, len(fents))
5373
5374 # Check that the FIP entry is updated
5375 fent = fents[1]
5376 self.assertEqual(0x8c, fent.offset)
5377 self.assertEqual(len(expected), fent.size)
5378 self.assertEqual(0, fent.flags)
5379 self.assertEqual(expected, fent.data)
5380 self.assertEqual(True, fent.valid)
5381
5382 def testFipMissing(self):
Simon Glass14d64e32025-04-29 07:21:59 -06005383 with terminal.capture() as (stdout, stderr):
Simon Glass3efb2972021-11-23 21:08:59 -07005384 self._DoTestFile('209_fip_missing.dts', allow_missing=True)
5385 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07005386 self.assertRegex(err, "Image 'image'.*missing.*: rmm-fw")
Simon Glass3efb2972021-11-23 21:08:59 -07005387
5388 def testFipSize(self):
5389 """Test a FIP with a size property"""
5390 data = self._DoReadFile('210_fip_size.dts')
5391 self.assertEqual(0x100 + len(U_BOOT_DATA), len(data))
5392 hdr, fents = fip_util.decode_fip(data)
5393 self.assertEqual(fip_util.HEADER_MAGIC, hdr.name)
5394 self.assertEqual(fip_util.HEADER_SERIAL, hdr.serial)
5395
5396 self.assertEqual(1, len(fents))
5397
5398 fent = fents[0]
5399 self.assertEqual('soc-fw', fent.fip_type)
5400 self.assertEqual(0x60, fent.offset)
5401 self.assertEqual(len(ATF_BL31_DATA), fent.size)
5402 self.assertEqual(ATF_BL31_DATA, fent.data)
5403 self.assertEqual(True, fent.valid)
5404
5405 rest = data[0x60 + len(ATF_BL31_DATA):0x100]
Simon Glass80025522022-01-29 14:14:04 -07005406 self.assertEqual(tools.get_bytes(0xff, len(rest)), rest)
Simon Glass3efb2972021-11-23 21:08:59 -07005407
5408 def testFipBadAlign(self):
5409 """Test that an invalid alignment value in a FIP is detected"""
5410 with self.assertRaises(ValueError) as e:
5411 self._DoTestFile('211_fip_bad_align.dts')
5412 self.assertIn(
5413 "Node \'/binman/atf-fip\': FIP alignment 31 must be a power of two",
5414 str(e.exception))
5415
5416 def testFipCollection(self):
5417 """Test using a FIP in a collection"""
5418 data = self._DoReadFile('212_fip_collection.dts')
5419 entry1 = control.images['image'].GetEntries()['collection']
5420 data1 = data[:entry1.size]
5421 hdr1, fents2 = fip_util.decode_fip(data1)
5422
5423 entry2 = control.images['image'].GetEntries()['atf-fip']
5424 data2 = data[entry2.offset:entry2.offset + entry2.size]
5425 hdr1, fents2 = fip_util.decode_fip(data2)
5426
5427 # The 'collection' entry should have U-Boot included at the end
5428 self.assertEqual(entry1.size - len(U_BOOT_DATA), entry2.size)
5429 self.assertEqual(data1, data2 + U_BOOT_DATA)
5430 self.assertEqual(U_BOOT_DATA, data1[-4:])
5431
5432 # There should be a U-Boot after the final FIP
5433 self.assertEqual(U_BOOT_DATA, data[-4:])
Simon Glass76f496d2021-07-06 10:36:37 -06005434
Simon Glassccae6862022-01-12 13:10:35 -07005435 def testFakeBlob(self):
5436 """Test handling of faking an external blob"""
Simon Glass14d64e32025-04-29 07:21:59 -06005437 with terminal.capture() as (stdout, stderr):
Simon Glassccae6862022-01-12 13:10:35 -07005438 self._DoTestFile('217_fake_blob.dts', allow_missing=True,
5439 allow_fake_blobs=True)
5440 err = stderr.getvalue()
5441 self.assertRegex(
5442 err,
5443 "Image '.*' has faked external blobs and is non-functional: .*")
Simon Glassccae6862022-01-12 13:10:35 -07005444
Simon Glassceb5f912022-01-09 20:13:46 -07005445 def testExtblobListFaked(self):
5446 """Test an extblob with missing external blob that are faked"""
Simon Glass14d64e32025-04-29 07:21:59 -06005447 with terminal.capture() as (stdout, stderr):
Simon Glassceb5f912022-01-09 20:13:46 -07005448 self._DoTestFile('216_blob_ext_list_missing.dts',
5449 allow_fake_blobs=True)
5450 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07005451 self.assertRegex(err, "Image 'image'.*faked.*: blob-ext-list")
Simon Glassceb5f912022-01-09 20:13:46 -07005452
Simon Glass162017b2022-01-09 20:13:57 -07005453 def testListBintools(self):
5454 args = ['tool', '--list']
Simon Glass14d64e32025-04-29 07:21:59 -06005455 with terminal.capture() as (stdout, _):
Simon Glass162017b2022-01-09 20:13:57 -07005456 self._DoBinman(*args)
5457 out = stdout.getvalue().splitlines()
5458 self.assertTrue(len(out) >= 2)
5459
5460 def testFetchBintools(self):
5461 def fail_download(url):
Simon Glass80025522022-01-29 14:14:04 -07005462 """Take the tools.download() function by raising an exception"""
Simon Glass162017b2022-01-09 20:13:57 -07005463 raise urllib.error.URLError('my error')
5464
5465 args = ['tool']
5466 with self.assertRaises(ValueError) as e:
5467 self._DoBinman(*args)
5468 self.assertIn("Invalid arguments to 'tool' subcommand",
5469 str(e.exception))
5470
5471 args = ['tool', '--fetch']
5472 with self.assertRaises(ValueError) as e:
5473 self._DoBinman(*args)
5474 self.assertIn('Please specify bintools to fetch', str(e.exception))
5475
5476 args = ['tool', '--fetch', '_testing']
Simon Glass80025522022-01-29 14:14:04 -07005477 with unittest.mock.patch.object(tools, 'download',
Simon Glass162017b2022-01-09 20:13:57 -07005478 side_effect=fail_download):
Simon Glass14d64e32025-04-29 07:21:59 -06005479 with terminal.capture() as (stdout, _):
Simon Glass162017b2022-01-09 20:13:57 -07005480 self._DoBinman(*args)
5481 self.assertIn('failed to fetch with all methods', stdout.getvalue())
5482
Simon Glass620c4462022-01-09 20:14:11 -07005483 def testBintoolDocs(self):
5484 """Test for creation of bintool documentation"""
Simon Glass14d64e32025-04-29 07:21:59 -06005485 with terminal.capture() as (stdout, stderr):
Simon Glass620c4462022-01-09 20:14:11 -07005486 control.write_bintool_docs(control.bintool.Bintool.get_tool_list())
5487 self.assertTrue(len(stdout.getvalue()) > 0)
5488
5489 def testBintoolDocsMissing(self):
5490 """Test handling of missing bintool documentation"""
5491 with self.assertRaises(ValueError) as e:
Simon Glass14d64e32025-04-29 07:21:59 -06005492 with terminal.capture() as (stdout, stderr):
Simon Glass620c4462022-01-09 20:14:11 -07005493 control.write_bintool_docs(
5494 control.bintool.Bintool.get_tool_list(), 'mkimage')
5495 self.assertIn('Documentation is missing for modules: mkimage',
5496 str(e.exception))
5497
Jan Kiszka58c407f2022-01-28 20:37:53 +01005498 def testListWithGenNode(self):
5499 """Check handling of an FDT map when the section cannot be found"""
5500 entry_args = {
5501 'of-list': 'test-fdt1 test-fdt2',
5502 }
5503 data = self._DoReadFileDtb(
5504 '219_fit_gennode.dts',
5505 entry_args=entry_args,
5506 use_real_dtb=True,
5507 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])
5508
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01005509 tmpdir = None
Jan Kiszka58c407f2022-01-28 20:37:53 +01005510 try:
5511 tmpdir, updated_fname = self._SetupImageInTmpdir()
Simon Glass14d64e32025-04-29 07:21:59 -06005512 with terminal.capture() as (stdout, stderr):
Jan Kiszka58c407f2022-01-28 20:37:53 +01005513 self._RunBinman('ls', '-i', updated_fname)
5514 finally:
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01005515 if tmpdir:
5516 shutil.rmtree(tmpdir)
Jan Kiszka58c407f2022-01-28 20:37:53 +01005517
Alper Nebi Yasake706d872022-02-08 01:08:05 +03005518 def testFitSubentryUsesBintool(self):
5519 """Test that binman FIT subentries can use bintools"""
Simon Glass5dc22cf2025-02-03 09:26:42 -07005520 command.TEST_RESULT = self._HandleGbbCommand
Alper Nebi Yasake706d872022-02-08 01:08:05 +03005521 entry_args = {
5522 'keydir': 'devkeys',
5523 'bmpblk': 'bmpblk.bin',
5524 }
5525 data, _, _, _ = self._DoReadFileDtb('220_fit_subentry_bintool.dts',
5526 entry_args=entry_args)
5527
Alper Nebi Yasakd4553262022-02-08 01:08:07 +03005528 expected = (GBB_DATA + GBB_DATA + tools.get_bytes(0, 8) +
5529 tools.get_bytes(0, 0x2180 - 16))
Alper Nebi Yasake706d872022-02-08 01:08:05 +03005530 self.assertIn(expected, data)
5531
5532 def testFitSubentryMissingBintool(self):
5533 """Test that binman reports missing bintools for FIT subentries"""
5534 entry_args = {
5535 'keydir': 'devkeys',
5536 }
Simon Glass14d64e32025-04-29 07:21:59 -06005537 with terminal.capture() as (_, stderr):
Alper Nebi Yasake706d872022-02-08 01:08:05 +03005538 self._DoTestFile('220_fit_subentry_bintool.dts',
5539 force_missing_bintools='futility', entry_args=entry_args)
5540 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07005541 self.assertRegex(err, "Image 'image'.*missing bintools.*: futility")
Simon Glassccae6862022-01-12 13:10:35 -07005542
Alper Nebi Yasak1e4ffd82022-02-09 22:02:35 +03005543 def testFitSubentryHashSubnode(self):
5544 """Test an image with a FIT inside"""
Marek Vasutf7413f02023-07-18 07:23:58 -06005545 self._SetupSplElf()
Alper Nebi Yasak1e4ffd82022-02-09 22:02:35 +03005546 data, _, _, out_dtb_name = self._DoReadFileDtb(
5547 '221_fit_subentry_hash.dts', use_real_dtb=True, update_dtb=True)
5548
5549 mkimage_dtb = fdt.Fdt.FromData(data)
5550 mkimage_dtb.Scan()
5551 binman_dtb = fdt.Fdt(out_dtb_name)
5552 binman_dtb.Scan()
5553
5554 # Check that binman didn't add hash values
5555 fnode = binman_dtb.GetNode('/binman/fit/images/kernel/hash')
5556 self.assertNotIn('value', fnode.props)
5557
5558 fnode = binman_dtb.GetNode('/binman/fit/images/fdt-1/hash')
5559 self.assertNotIn('value', fnode.props)
5560
5561 # Check that mkimage added hash values
5562 fnode = mkimage_dtb.GetNode('/images/kernel/hash')
5563 self.assertIn('value', fnode.props)
5564
5565 fnode = mkimage_dtb.GetNode('/images/fdt-1/hash')
5566 self.assertIn('value', fnode.props)
5567
Roger Quadros5cdcea02022-02-19 20:50:04 +02005568 def testPackTeeOs(self):
5569 """Test that an image with an TEE binary can be created"""
5570 data = self._DoReadFile('222_tee_os.dts')
5571 self.assertEqual(TEE_OS_DATA, data[:len(TEE_OS_DATA)])
5572
Neha Malcom Francis59be2552023-12-05 15:12:18 +05305573 def testPackTiDm(self):
5574 """Test that an image with a TI DM binary can be created"""
5575 data = self._DoReadFile('225_ti_dm.dts')
5576 self.assertEqual(TI_DM_DATA, data[:len(TI_DM_DATA)])
5577
Simon Glass912339f2022-02-08 11:50:03 -07005578 def testFitFdtOper(self):
5579 """Check handling of a specified FIT operation"""
5580 entry_args = {
5581 'of-list': 'test-fdt1 test-fdt2',
5582 'default-dt': 'test-fdt2',
5583 }
5584 self._DoReadFileDtb(
5585 '223_fit_fdt_oper.dts',
5586 entry_args=entry_args,
5587 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
5588
5589 def testFitFdtBadOper(self):
5590 """Check handling of an FDT map when the section cannot be found"""
5591 with self.assertRaises(ValueError) as exc:
5592 self._DoReadFileDtb('224_fit_bad_oper.dts')
Simon Glass05f71dc2022-03-05 20:19:09 -07005593 self.assertIn("Node '/binman/fit': subnode 'images/@fdt-SEQ': Unknown operation 'unknown'",
Simon Glass912339f2022-02-08 11:50:03 -07005594 str(exc.exception))
5595
Simon Glassdd156a42022-03-05 20:18:59 -07005596 def test_uses_expand_size(self):
5597 """Test that the 'expand-size' property cannot be used anymore"""
5598 with self.assertRaises(ValueError) as e:
5599 data = self._DoReadFile('225_expand_size_bad.dts')
5600 self.assertIn(
5601 "Node '/binman/u-boot': Please use 'extend-size' instead of 'expand-size'",
5602 str(e.exception))
5603
Simon Glass5f423422022-03-05 20:19:12 -07005604 def testFitSplitElf(self):
5605 """Test an image with an FIT with an split-elf operation"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005606 if not elf.ELF_TOOLS:
5607 self.skipTest('Python elftools not available')
Simon Glass5f423422022-03-05 20:19:12 -07005608 entry_args = {
5609 'of-list': 'test-fdt1 test-fdt2',
5610 'default-dt': 'test-fdt2',
5611 'atf-bl31-path': 'bl31.elf',
5612 'tee-os-path': 'tee.elf',
5613 }
5614 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
5615 data = self._DoReadFileDtb(
5616 '226_fit_split_elf.dts',
5617 entry_args=entry_args,
5618 extra_indirs=[test_subdir])[0]
5619
5620 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
5621 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
5622
5623 base_keys = {'description', 'type', 'arch', 'os', 'compression',
5624 'data', 'load'}
5625 dtb = fdt.Fdt.FromData(fit_data)
5626 dtb.Scan()
5627
5628 elf_data = tools.read_file(os.path.join(self._indir, 'bl31.elf'))
5629 segments, entry = elf.read_loadable_segments(elf_data)
5630
5631 # We assume there are two segments
Brandon Maiera657bc62024-06-04 16:16:05 +00005632 self.assertEqual(2, len(segments))
Simon Glass5f423422022-03-05 20:19:12 -07005633
5634 atf1 = dtb.GetNode('/images/atf-1')
5635 _, start, data = segments[0]
5636 self.assertEqual(base_keys | {'entry'}, atf1.props.keys())
5637 self.assertEqual(entry,
5638 fdt_util.fdt32_to_cpu(atf1.props['entry'].value))
5639 self.assertEqual(start,
5640 fdt_util.fdt32_to_cpu(atf1.props['load'].value))
5641 self.assertEqual(data, atf1.props['data'].bytes)
5642
Jonas Karlmand2c7d902023-01-21 19:01:48 +00005643 hash_node = atf1.FindNode('hash')
5644 self.assertIsNotNone(hash_node)
5645 self.assertEqual({'algo', 'value'}, hash_node.props.keys())
5646
Simon Glass5f423422022-03-05 20:19:12 -07005647 atf2 = dtb.GetNode('/images/atf-2')
5648 self.assertEqual(base_keys, atf2.props.keys())
5649 _, start, data = segments[1]
5650 self.assertEqual(start,
5651 fdt_util.fdt32_to_cpu(atf2.props['load'].value))
5652 self.assertEqual(data, atf2.props['data'].bytes)
5653
Jonas Karlmand2c7d902023-01-21 19:01:48 +00005654 hash_node = atf2.FindNode('hash')
5655 self.assertIsNotNone(hash_node)
5656 self.assertEqual({'algo', 'value'}, hash_node.props.keys())
5657
5658 hash_node = dtb.GetNode('/images/tee-1/hash-1')
5659 self.assertIsNotNone(hash_node)
5660 self.assertEqual({'algo', 'value'}, hash_node.props.keys())
5661
Simon Glass5f423422022-03-05 20:19:12 -07005662 conf = dtb.GetNode('/configurations')
5663 self.assertEqual({'default'}, conf.props.keys())
5664
5665 for subnode in conf.subnodes:
5666 self.assertEqual({'description', 'fdt', 'loadables'},
5667 subnode.props.keys())
5668 self.assertEqual(
5669 ['atf-1', 'atf-2', 'tee-1', 'tee-2'],
5670 fdt_util.GetStringList(subnode, 'loadables'))
5671
5672 def _check_bad_fit(self, dts):
5673 """Check a bad FIT
5674
5675 This runs with the given dts and returns the assertion raised
5676
5677 Args:
5678 dts (str): dts filename to use
5679
5680 Returns:
5681 str: Assertion string raised
5682 """
5683 entry_args = {
5684 'of-list': 'test-fdt1 test-fdt2',
5685 'default-dt': 'test-fdt2',
5686 'atf-bl31-path': 'bl31.elf',
5687 'tee-os-path': 'tee.elf',
5688 }
5689 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
5690 with self.assertRaises(ValueError) as exc:
5691 self._DoReadFileDtb(dts, entry_args=entry_args,
5692 extra_indirs=[test_subdir])[0]
5693 return str(exc.exception)
5694
5695 def testFitSplitElfBadElf(self):
5696 """Test a FIT split-elf operation with an invalid ELF file"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005697 if not elf.ELF_TOOLS:
5698 self.skipTest('Python elftools not available')
Simon Glass5f423422022-03-05 20:19:12 -07005699 TestFunctional._MakeInputFile('bad.elf', tools.get_bytes(100, 100))
5700 entry_args = {
5701 'of-list': 'test-fdt1 test-fdt2',
5702 'default-dt': 'test-fdt2',
5703 'atf-bl31-path': 'bad.elf',
5704 'tee-os-path': 'tee.elf',
5705 }
5706 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
5707 with self.assertRaises(ValueError) as exc:
5708 self._DoReadFileDtb(
5709 '226_fit_split_elf.dts',
5710 entry_args=entry_args,
5711 extra_indirs=[test_subdir])[0]
5712 self.assertIn(
5713 "Node '/binman/fit': subnode 'images/@atf-SEQ': Failed to read ELF file: Magic number does not match",
5714 str(exc.exception))
5715
Simon Glass5f423422022-03-05 20:19:12 -07005716 def checkFitSplitElf(self, **kwargs):
Simon Glass7d3e4072022-08-07 09:46:46 -06005717 """Test an split-elf FIT with a missing ELF file
5718
5719 Args:
5720 kwargs (dict of str): Arguments to pass to _DoTestFile()
5721
5722 Returns:
5723 tuple:
5724 str: stdout result
5725 str: stderr result
5726 """
Simon Glass5f423422022-03-05 20:19:12 -07005727 entry_args = {
5728 'of-list': 'test-fdt1 test-fdt2',
5729 'default-dt': 'test-fdt2',
5730 'atf-bl31-path': 'bl31.elf',
5731 'tee-os-path': 'missing.elf',
5732 }
5733 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
Simon Glass14d64e32025-04-29 07:21:59 -06005734 with terminal.capture() as (stdout, stderr):
Simon Glass5f423422022-03-05 20:19:12 -07005735 self._DoTestFile(
5736 '226_fit_split_elf.dts', entry_args=entry_args,
Simon Glass7d3e4072022-08-07 09:46:46 -06005737 extra_indirs=[test_subdir], verbosity=3, **kwargs)
5738 out = stdout.getvalue()
5739 err = stderr.getvalue()
5740 return out, err
Simon Glass5f423422022-03-05 20:19:12 -07005741
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005742 def testFitSplitElfBadDirective(self):
5743 """Test a FIT split-elf invalid fit,xxx directive in an image node"""
5744 if not elf.ELF_TOOLS:
5745 self.skipTest('Python elftools not available')
5746 err = self._check_bad_fit('227_fit_bad_dir.dts')
5747 self.assertIn(
5748 "Node '/binman/fit': subnode 'images/@atf-SEQ': Unknown directive 'fit,something'",
5749 err)
5750
5751 def testFitSplitElfBadDirectiveConfig(self):
5752 """Test a FIT split-elf with invalid fit,xxx directive in config"""
5753 if not elf.ELF_TOOLS:
5754 self.skipTest('Python elftools not available')
5755 err = self._check_bad_fit('228_fit_bad_dir_config.dts')
5756 self.assertEqual(
5757 "Node '/binman/fit': subnode 'configurations/@config-SEQ': Unknown directive 'fit,config'",
5758 err)
5759
5760
Simon Glass5f423422022-03-05 20:19:12 -07005761 def testFitSplitElfMissing(self):
5762 """Test an split-elf FIT with a missing ELF file"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005763 if not elf.ELF_TOOLS:
5764 self.skipTest('Python elftools not available')
Simon Glass7d3e4072022-08-07 09:46:46 -06005765 out, err = self.checkFitSplitElf(allow_missing=True)
Simon Glass5f423422022-03-05 20:19:12 -07005766 self.assertRegex(
5767 err,
5768 "Image '.*' is missing external blobs and is non-functional: .*")
Simon Glass7d3e4072022-08-07 09:46:46 -06005769 self.assertNotRegex(out, '.*Faked blob.*')
5770 fname = tools.get_output_filename('binman-fake/missing.elf')
5771 self.assertFalse(os.path.exists(fname))
Simon Glass5f423422022-03-05 20:19:12 -07005772
5773 def testFitSplitElfFaked(self):
5774 """Test an split-elf FIT with faked ELF file"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005775 if not elf.ELF_TOOLS:
5776 self.skipTest('Python elftools not available')
Simon Glass7d3e4072022-08-07 09:46:46 -06005777 out, err = self.checkFitSplitElf(allow_missing=True, allow_fake_blobs=True)
Simon Glass5f423422022-03-05 20:19:12 -07005778 self.assertRegex(
5779 err,
5780 "Image '.*' is missing external blobs and is non-functional: .*")
Simon Glass7d3e4072022-08-07 09:46:46 -06005781 self.assertRegex(
5782 out,
5783 "Entry '/binman/fit/images/@tee-SEQ/tee-os': Faked blob '.*binman-fake/missing.elf")
5784 fname = tools.get_output_filename('binman-fake/missing.elf')
5785 self.assertTrue(os.path.exists(fname))
Simon Glass5f423422022-03-05 20:19:12 -07005786
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005787 def testMkimageMissingBlob(self):
5788 """Test using mkimage to build an image"""
Simon Glass14d64e32025-04-29 07:21:59 -06005789 with terminal.capture() as (stdout, stderr):
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005790 self._DoTestFile('229_mkimage_missing.dts', allow_missing=True,
5791 allow_fake_blobs=True)
5792 err = stderr.getvalue()
5793 self.assertRegex(
5794 err,
5795 "Image '.*' has faked external blobs and is non-functional: .*")
5796
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005797 def testPreLoad(self):
5798 """Test an image with a pre-load header"""
5799 entry_args = {
Simon Glasse2dfb962023-07-24 09:19:57 -06005800 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005801 }
Simon Glasse2dfb962023-07-24 09:19:57 -06005802 data = self._DoReadFileDtb(
5803 '230_pre_load.dts', entry_args=entry_args,
5804 extra_indirs=[os.path.join(self._binman_dir, 'test')])[0]
Paul HENRYS5cf82892025-02-24 22:20:55 +01005805
5806 image_fname = tools.get_output_filename('image.bin')
5807 is_signed = self._CheckPreload(image_fname, self.TestFile("dev.key"))
5808
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005809 self.assertEqual(PRE_LOAD_MAGIC, data[:len(PRE_LOAD_MAGIC)])
5810 self.assertEqual(PRE_LOAD_VERSION, data[4:4 + len(PRE_LOAD_VERSION)])
5811 self.assertEqual(PRE_LOAD_HDR_SIZE, data[8:8 + len(PRE_LOAD_HDR_SIZE)])
Paul HENRYS5cf82892025-02-24 22:20:55 +01005812 self.assertEqual(is_signed, True)
Simon Glasse2dfb962023-07-24 09:19:57 -06005813
5814 def testPreLoadNoKey(self):
5815 """Test an image with a pre-load heade0r with missing key"""
5816 with self.assertRaises(FileNotFoundError) as exc:
5817 self._DoReadFile('230_pre_load.dts')
5818 self.assertIn("No such file or directory: 'dev.key'",
5819 str(exc.exception))
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005820
5821 def testPreLoadPkcs(self):
5822 """Test an image with a pre-load header with padding pkcs"""
Simon Glasse2dfb962023-07-24 09:19:57 -06005823 entry_args = {
5824 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5825 }
5826 data = self._DoReadFileDtb('231_pre_load_pkcs.dts',
5827 entry_args=entry_args)[0]
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005828 self.assertEqual(PRE_LOAD_MAGIC, data[:len(PRE_LOAD_MAGIC)])
5829 self.assertEqual(PRE_LOAD_VERSION, data[4:4 + len(PRE_LOAD_VERSION)])
5830 self.assertEqual(PRE_LOAD_HDR_SIZE, data[8:8 + len(PRE_LOAD_HDR_SIZE)])
5831
5832 def testPreLoadPss(self):
5833 """Test an image with a pre-load header with padding pss"""
Simon Glasse2dfb962023-07-24 09:19:57 -06005834 entry_args = {
5835 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5836 }
5837 data = self._DoReadFileDtb('232_pre_load_pss.dts',
5838 entry_args=entry_args)[0]
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005839 self.assertEqual(PRE_LOAD_MAGIC, data[:len(PRE_LOAD_MAGIC)])
5840 self.assertEqual(PRE_LOAD_VERSION, data[4:4 + len(PRE_LOAD_VERSION)])
5841 self.assertEqual(PRE_LOAD_HDR_SIZE, data[8:8 + len(PRE_LOAD_HDR_SIZE)])
5842
5843 def testPreLoadInvalidPadding(self):
5844 """Test an image with a pre-load header with an invalid padding"""
Simon Glasse2dfb962023-07-24 09:19:57 -06005845 entry_args = {
5846 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5847 }
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005848 with self.assertRaises(ValueError) as e:
Simon Glasse2dfb962023-07-24 09:19:57 -06005849 self._DoReadFileDtb('233_pre_load_invalid_padding.dts',
5850 entry_args=entry_args)
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005851
5852 def testPreLoadInvalidSha(self):
5853 """Test an image with a pre-load header with an invalid hash"""
Simon Glasse2dfb962023-07-24 09:19:57 -06005854 entry_args = {
5855 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5856 }
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005857 with self.assertRaises(ValueError) as e:
Simon Glasse2dfb962023-07-24 09:19:57 -06005858 self._DoReadFileDtb('234_pre_load_invalid_sha.dts',
5859 entry_args=entry_args)
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005860
5861 def testPreLoadInvalidAlgo(self):
5862 """Test an image with a pre-load header with an invalid algo"""
5863 with self.assertRaises(ValueError) as e:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005864 data = self._DoReadFile('235_pre_load_invalid_algo.dts')
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005865
5866 def testPreLoadInvalidKey(self):
5867 """Test an image with a pre-load header with an invalid key"""
Simon Glasse2dfb962023-07-24 09:19:57 -06005868 entry_args = {
5869 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5870 }
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005871 with self.assertRaises(ValueError) as e:
Simon Glasse2dfb962023-07-24 09:19:57 -06005872 data = self._DoReadFileDtb('236_pre_load_invalid_key.dts',
5873 entry_args=entry_args)
Roger Quadros5cdcea02022-02-19 20:50:04 +02005874
Alper Nebi Yasak5cff63f2022-03-27 18:31:44 +03005875 def _CheckSafeUniqueNames(self, *images):
5876 """Check all entries of given images for unsafe unique names"""
5877 for image in images:
5878 entries = {}
5879 image._CollectEntries(entries, {}, image)
5880 for entry in entries.values():
5881 uniq = entry.GetUniqueName()
5882
5883 # Used as part of a filename, so must not be absolute paths.
5884 self.assertFalse(os.path.isabs(uniq))
5885
5886 def testSafeUniqueNames(self):
5887 """Test entry unique names are safe in single image configuration"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005888 data = self._DoReadFileRealDtb('237_unique_names.dts')
Alper Nebi Yasak5cff63f2022-03-27 18:31:44 +03005889
5890 orig_image = control.images['image']
5891 image_fname = tools.get_output_filename('image.bin')
5892 image = Image.FromFile(image_fname)
5893
5894 self._CheckSafeUniqueNames(orig_image, image)
5895
5896 def testSafeUniqueNamesMulti(self):
5897 """Test entry unique names are safe with multiple images"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005898 data = self._DoReadFileRealDtb('238_unique_names_multi.dts')
Alper Nebi Yasak5cff63f2022-03-27 18:31:44 +03005899
5900 orig_image = control.images['image']
5901 image_fname = tools.get_output_filename('image.bin')
5902 image = Image.FromFile(image_fname)
5903
5904 self._CheckSafeUniqueNames(orig_image, image)
5905
Alper Nebi Yasake63ca5a2022-03-27 18:31:45 +03005906 def testReplaceCmdWithBintool(self):
5907 """Test replacing an entry that needs a bintool to pack"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005908 data = self._DoReadFileRealDtb('239_replace_with_bintool.dts')
Alper Nebi Yasake63ca5a2022-03-27 18:31:45 +03005909 expected = U_BOOT_DATA + b'aa'
5910 self.assertEqual(expected, data[:len(expected)])
5911
5912 try:
5913 tmpdir, updated_fname = self._SetupImageInTmpdir()
5914 fname = os.path.join(tmpdir, 'update-testing.bin')
5915 tools.write_file(fname, b'zz')
5916 self._DoBinman('replace', '-i', updated_fname,
5917 '_testing', '-f', fname)
5918
5919 data = tools.read_file(updated_fname)
5920 expected = U_BOOT_DATA + b'zz'
5921 self.assertEqual(expected, data[:len(expected)])
5922 finally:
5923 shutil.rmtree(tmpdir)
5924
5925 def testReplaceCmdOtherWithBintool(self):
5926 """Test replacing an entry when another needs a bintool to pack"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005927 data = self._DoReadFileRealDtb('239_replace_with_bintool.dts')
Alper Nebi Yasake63ca5a2022-03-27 18:31:45 +03005928 expected = U_BOOT_DATA + b'aa'
5929 self.assertEqual(expected, data[:len(expected)])
5930
5931 try:
5932 tmpdir, updated_fname = self._SetupImageInTmpdir()
5933 fname = os.path.join(tmpdir, 'update-u-boot.bin')
5934 tools.write_file(fname, b'x' * len(U_BOOT_DATA))
5935 self._DoBinman('replace', '-i', updated_fname,
5936 'u-boot', '-f', fname)
5937
5938 data = tools.read_file(updated_fname)
5939 expected = b'x' * len(U_BOOT_DATA) + b'aa'
5940 self.assertEqual(expected, data[:len(expected)])
5941 finally:
5942 shutil.rmtree(tmpdir)
5943
Alper Nebi Yasak00c68f12022-03-27 18:31:46 +03005944 def testReplaceResizeNoRepackSameSize(self):
5945 """Test replacing entries with same-size data without repacking"""
5946 expected = b'x' * len(U_BOOT_DATA)
5947 data, expected_fdtmap, _ = self._RunReplaceCmd('u-boot', expected)
5948 self.assertEqual(expected, data)
5949
5950 path, fdtmap = state.GetFdtContents('fdtmap')
5951 self.assertIsNotNone(path)
5952 self.assertEqual(expected_fdtmap, fdtmap)
5953
5954 def testReplaceResizeNoRepackSmallerSize(self):
5955 """Test replacing entries with smaller-size data without repacking"""
5956 new_data = b'x'
5957 data, expected_fdtmap, _ = self._RunReplaceCmd('u-boot', new_data)
5958 expected = new_data.ljust(len(U_BOOT_DATA), b'\0')
5959 self.assertEqual(expected, data)
5960
5961 path, fdtmap = state.GetFdtContents('fdtmap')
5962 self.assertIsNotNone(path)
5963 self.assertEqual(expected_fdtmap, fdtmap)
5964
Alper Nebi Yasak6cadc502022-03-27 18:31:48 +03005965 def testExtractFit(self):
5966 """Test extracting a FIT section"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005967 self._DoReadFileRealDtb('240_fit_extract_replace.dts')
Alper Nebi Yasak6cadc502022-03-27 18:31:48 +03005968 image_fname = tools.get_output_filename('image.bin')
5969
5970 fit_data = control.ReadEntry(image_fname, 'fit')
5971 fit = fdt.Fdt.FromData(fit_data)
5972 fit.Scan()
5973
5974 # Check subentry data inside the extracted fit
5975 for node_path, expected in [
5976 ('/images/kernel', U_BOOT_DATA),
5977 ('/images/fdt-1', U_BOOT_NODTB_DATA),
5978 ('/images/scr-1', COMPRESS_DATA),
5979 ]:
5980 node = fit.GetNode(node_path)
5981 data = fit.GetProps(node)['data'].bytes
5982 self.assertEqual(expected, data)
5983
5984 def testExtractFitSubentries(self):
5985 """Test extracting FIT section subentries"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005986 self._DoReadFileRealDtb('240_fit_extract_replace.dts')
Alper Nebi Yasak6cadc502022-03-27 18:31:48 +03005987 image_fname = tools.get_output_filename('image.bin')
5988
5989 for entry_path, expected in [
5990 ('fit/kernel', U_BOOT_DATA),
5991 ('fit/kernel/u-boot', U_BOOT_DATA),
5992 ('fit/fdt-1', U_BOOT_NODTB_DATA),
5993 ('fit/fdt-1/u-boot-nodtb', U_BOOT_NODTB_DATA),
5994 ('fit/scr-1', COMPRESS_DATA),
5995 ('fit/scr-1/blob', COMPRESS_DATA),
5996 ]:
5997 data = control.ReadEntry(image_fname, entry_path)
5998 self.assertEqual(expected, data)
5999
Alper Nebi Yasak49892642022-03-27 18:31:49 +03006000 def testReplaceFitSubentryLeafSameSize(self):
6001 """Test replacing a FIT leaf subentry with same-size data"""
6002 new_data = b'x' * len(U_BOOT_DATA)
6003 data, expected_fdtmap, _ = self._RunReplaceCmd(
6004 'fit/kernel/u-boot', new_data,
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006005 dts='240_fit_extract_replace.dts')
Alper Nebi Yasak49892642022-03-27 18:31:49 +03006006 self.assertEqual(new_data, data)
6007
6008 path, fdtmap = state.GetFdtContents('fdtmap')
6009 self.assertIsNotNone(path)
6010 self.assertEqual(expected_fdtmap, fdtmap)
6011
6012 def testReplaceFitSubentryLeafBiggerSize(self):
6013 """Test replacing a FIT leaf subentry with bigger-size data"""
6014 new_data = b'ub' * len(U_BOOT_NODTB_DATA)
6015 data, expected_fdtmap, _ = self._RunReplaceCmd(
6016 'fit/fdt-1/u-boot-nodtb', new_data,
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006017 dts='240_fit_extract_replace.dts')
Alper Nebi Yasak49892642022-03-27 18:31:49 +03006018 self.assertEqual(new_data, data)
6019
6020 # Will be repacked, so fdtmap must change
6021 path, fdtmap = state.GetFdtContents('fdtmap')
6022 self.assertIsNotNone(path)
6023 self.assertNotEqual(expected_fdtmap, fdtmap)
6024
6025 def testReplaceFitSubentryLeafSmallerSize(self):
6026 """Test replacing a FIT leaf subentry with smaller-size data"""
6027 new_data = b'x'
6028 expected = new_data.ljust(len(U_BOOT_NODTB_DATA), b'\0')
6029 data, expected_fdtmap, _ = self._RunReplaceCmd(
6030 'fit/fdt-1/u-boot-nodtb', new_data,
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006031 dts='240_fit_extract_replace.dts')
Alper Nebi Yasak49892642022-03-27 18:31:49 +03006032 self.assertEqual(expected, data)
6033
6034 path, fdtmap = state.GetFdtContents('fdtmap')
6035 self.assertIsNotNone(path)
6036 self.assertEqual(expected_fdtmap, fdtmap)
6037
Alper Nebi Yasak1d44c8e2022-03-27 18:31:50 +03006038 def testReplaceSectionSimple(self):
Simon Glass49b77e82023-03-02 17:02:44 -07006039 """Test replacing a simple section with same-sized data"""
Alper Nebi Yasak1d44c8e2022-03-27 18:31:50 +03006040 new_data = b'w' * len(COMPRESS_DATA + U_BOOT_DATA)
Simon Glass49b77e82023-03-02 17:02:44 -07006041 data, expected_fdtmap, image = self._RunReplaceCmd('section',
6042 new_data, dts='241_replace_section_simple.dts')
6043 self.assertEqual(new_data, data)
6044
6045 entries = image.GetEntries()
6046 self.assertIn('section', entries)
6047 entry = entries['section']
6048 self.assertEqual(len(new_data), entry.size)
6049
6050 def testReplaceSectionLarger(self):
6051 """Test replacing a simple section with larger data"""
6052 new_data = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) + 1)
6053 data, expected_fdtmap, image = self._RunReplaceCmd('section',
6054 new_data, dts='241_replace_section_simple.dts')
6055 self.assertEqual(new_data, data)
6056
6057 entries = image.GetEntries()
6058 self.assertIn('section', entries)
6059 entry = entries['section']
6060 self.assertEqual(len(new_data), entry.size)
6061 fentry = entries['fdtmap']
6062 self.assertEqual(entry.offset + entry.size, fentry.offset)
6063
6064 def testReplaceSectionSmaller(self):
6065 """Test replacing a simple section with smaller data"""
6066 new_data = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) - 1) + b'\0'
6067 data, expected_fdtmap, image = self._RunReplaceCmd('section',
6068 new_data, dts='241_replace_section_simple.dts')
6069 self.assertEqual(new_data, data)
6070
6071 # The new size is the same as the old, just with a pad byte at the end
6072 entries = image.GetEntries()
6073 self.assertIn('section', entries)
6074 entry = entries['section']
6075 self.assertEqual(len(new_data), entry.size)
6076
6077 def testReplaceSectionSmallerAllow(self):
6078 """Test failing to replace a simple section with smaller data"""
6079 new_data = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) - 1)
6080 try:
6081 state.SetAllowEntryContraction(True)
6082 with self.assertRaises(ValueError) as exc:
6083 self._RunReplaceCmd('section', new_data,
6084 dts='241_replace_section_simple.dts')
6085 finally:
6086 state.SetAllowEntryContraction(False)
6087
6088 # Since we have no information about the position of things within the
6089 # section, we cannot adjust the position of /section-u-boot so it ends
6090 # up outside the section
Simon Glassc6b283f2022-08-13 11:40:46 -06006091 self.assertIn(
Simon Glass49b77e82023-03-02 17:02:44 -07006092 "Node '/section/u-boot': Offset 0x24 (36) size 0x4 (4) is outside "
6093 "the section '/section' starting at 0x0 (0) of size 0x27 (39)",
Simon Glassc6b283f2022-08-13 11:40:46 -06006094 str(exc.exception))
Alper Nebi Yasak1d44c8e2022-03-27 18:31:50 +03006095
Simon Glass8fbca772022-08-13 11:40:48 -06006096 def testMkimageImagename(self):
6097 """Test using mkimage with -n holding the data too"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006098 self._SetupSplElf()
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006099 data = self._DoReadFile('242_mkimage_name.dts')
Simon Glass8fbca772022-08-13 11:40:48 -06006100
6101 # Check that the data appears in the file somewhere
6102 self.assertIn(U_BOOT_SPL_DATA, data)
6103
Simon Glassbb7d3bb2022-09-06 20:26:52 -06006104 # Get struct legacy_img_hdr -> ih_name
Simon Glass8fbca772022-08-13 11:40:48 -06006105 name = data[0x20:0x40]
6106
6107 # Build the filename that we expect to be placed in there, by virtue of
6108 # the -n paraameter
6109 expect = os.path.join(tools.get_output_dir(), 'mkimage.mkimage')
6110
6111 # Check that the image name is set to the temporary filename used
6112 self.assertEqual(expect.encode('utf-8')[:0x20], name)
6113
Simon Glassb1669752022-08-13 11:40:49 -06006114 def testMkimageImage(self):
6115 """Test using mkimage with -n holding the data too"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006116 self._SetupSplElf()
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006117 data = self._DoReadFile('243_mkimage_image.dts')
Simon Glassb1669752022-08-13 11:40:49 -06006118
6119 # Check that the data appears in the file somewhere
6120 self.assertIn(U_BOOT_SPL_DATA, data)
6121
Simon Glassbb7d3bb2022-09-06 20:26:52 -06006122 # Get struct legacy_img_hdr -> ih_name
Simon Glassb1669752022-08-13 11:40:49 -06006123 name = data[0x20:0x40]
6124
6125 # Build the filename that we expect to be placed in there, by virtue of
6126 # the -n paraameter
6127 expect = os.path.join(tools.get_output_dir(), 'mkimage-n.mkimage')
6128
6129 # Check that the image name is set to the temporary filename used
6130 self.assertEqual(expect.encode('utf-8')[:0x20], name)
6131
6132 # Check the corect data is in the imagename file
6133 self.assertEqual(U_BOOT_DATA, tools.read_file(expect))
6134
6135 def testMkimageImageNoContent(self):
6136 """Test using mkimage with -n and no data"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006137 self._SetupSplElf()
Simon Glassb1669752022-08-13 11:40:49 -06006138 with self.assertRaises(ValueError) as exc:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006139 self._DoReadFile('244_mkimage_image_no_content.dts')
Simon Glassb1669752022-08-13 11:40:49 -06006140 self.assertIn('Could not complete processing of contents',
6141 str(exc.exception))
6142
6143 def testMkimageImageBad(self):
6144 """Test using mkimage with imagename node and data-to-imagename"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006145 self._SetupSplElf()
Simon Glassb1669752022-08-13 11:40:49 -06006146 with self.assertRaises(ValueError) as exc:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006147 self._DoReadFile('245_mkimage_image_bad.dts')
Simon Glassb1669752022-08-13 11:40:49 -06006148 self.assertIn('Cannot use both imagename node and data-to-imagename',
6149 str(exc.exception))
6150
Simon Glassbd5cd882022-08-13 11:40:50 -06006151 def testCollectionOther(self):
6152 """Test a collection where the data comes from another section"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006153 data = self._DoReadFile('246_collection_other.dts')
Simon Glassbd5cd882022-08-13 11:40:50 -06006154 self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA +
6155 tools.get_bytes(0xff, 2) + U_BOOT_NODTB_DATA +
6156 tools.get_bytes(0xfe, 3) + U_BOOT_DTB_DATA,
6157 data)
6158
6159 def testMkimageCollection(self):
6160 """Test using a collection referring to an entry in a mkimage entry"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006161 self._SetupSplElf()
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006162 data = self._DoReadFile('247_mkimage_coll.dts')
Simon Glassbd5cd882022-08-13 11:40:50 -06006163 expect = U_BOOT_SPL_DATA + U_BOOT_DATA
6164 self.assertEqual(expect, data[:len(expect)])
6165
Stefan Herbrechtsmeier11121d32022-08-19 16:25:25 +02006166 def testCompressDtbPrependInvalid(self):
6167 """Test that invalid header is detected"""
6168 with self.assertRaises(ValueError) as e:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006169 self._DoReadFileDtb('248_compress_dtb_prepend_invalid.dts')
Stefan Herbrechtsmeier11121d32022-08-19 16:25:25 +02006170 self.assertIn("Node '/binman/u-boot-dtb': Invalid prepend in "
6171 "'u-boot-dtb': 'invalid'", str(e.exception))
6172
6173 def testCompressDtbPrependLength(self):
6174 """Test that compress with length header works as expected"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006175 data = self._DoReadFileRealDtb('249_compress_dtb_prepend_length.dts')
Stefan Herbrechtsmeier11121d32022-08-19 16:25:25 +02006176 image = control.images['image']
6177 entries = image.GetEntries()
6178 self.assertIn('u-boot-dtb', entries)
6179 u_boot_dtb = entries['u-boot-dtb']
6180 self.assertIn('fdtmap', entries)
6181 fdtmap = entries['fdtmap']
6182
6183 image_fname = tools.get_output_filename('image.bin')
6184 orig = control.ReadEntry(image_fname, 'u-boot-dtb')
6185 dtb = fdt.Fdt.FromData(orig)
6186 dtb.Scan()
6187 props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
6188 expected = {
6189 'u-boot:size': len(U_BOOT_DATA),
6190 'u-boot-dtb:uncomp-size': len(orig),
6191 'u-boot-dtb:size': u_boot_dtb.size,
6192 'fdtmap:size': fdtmap.size,
6193 'size': len(data),
6194 }
6195 self.assertEqual(expected, props)
6196
6197 # Check implementation
6198 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
6199 rest = data[len(U_BOOT_DATA):]
6200 comp_data_len = struct.unpack('<I', rest[:4])[0]
6201 comp_data = rest[4:4 + comp_data_len]
6202 orig2 = self._decompress(comp_data)
6203 self.assertEqual(orig, orig2)
6204
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +02006205 def testInvalidCompress(self):
6206 """Test that invalid compress algorithm is detected"""
6207 with self.assertRaises(ValueError) as e:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006208 self._DoTestFile('250_compress_dtb_invalid.dts')
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +02006209 self.assertIn("Unknown algorithm 'invalid'", str(e.exception))
6210
Stefan Herbrechtsmeiera14bee02022-08-19 16:25:32 +02006211 def testCompUtilCompressions(self):
6212 """Test compression algorithms"""
6213 for bintool in self.comp_bintools.values():
6214 self._CheckBintool(bintool)
6215 data = bintool.compress(COMPRESS_DATA)
6216 self.assertNotEqual(COMPRESS_DATA, data)
6217 orig = bintool.decompress(data)
Brandon Maiera657bc62024-06-04 16:16:05 +00006218 self.assertEqual(COMPRESS_DATA, orig)
Stefan Herbrechtsmeiera14bee02022-08-19 16:25:32 +02006219
6220 def testCompUtilVersions(self):
6221 """Test tool version of compression algorithms"""
6222 for bintool in self.comp_bintools.values():
6223 self._CheckBintool(bintool)
6224 version = bintool.version()
6225 self.assertRegex(version, '^v?[0-9]+[0-9.]*')
6226
6227 def testCompUtilPadding(self):
6228 """Test padding of compression algorithms"""
Jiaxun Yangc6931742025-04-10 06:43:03 -06006229 # Skip zstd and lz4 because they doesn't support padding
6230 for bintool in [v for k,v in self.comp_bintools.items()
6231 if not k in ['zstd', 'lz4']]:
Stefan Herbrechtsmeiera14bee02022-08-19 16:25:32 +02006232 self._CheckBintool(bintool)
6233 data = bintool.compress(COMPRESS_DATA)
6234 self.assertNotEqual(COMPRESS_DATA, data)
6235 data += tools.get_bytes(0, 64)
6236 orig = bintool.decompress(data)
Brandon Maiera657bc62024-06-04 16:16:05 +00006237 self.assertEqual(COMPRESS_DATA, orig)
Stefan Herbrechtsmeiera14bee02022-08-19 16:25:32 +02006238
Stefan Herbrechtsmeiera5e4dcb2022-08-19 16:25:38 +02006239 def testCompressDtbZstd(self):
6240 """Test that zstd compress of device-tree files failed"""
6241 with self.assertRaises(ValueError) as e:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006242 self._DoTestFile('251_compress_dtb_zstd.dts')
Stefan Herbrechtsmeiera5e4dcb2022-08-19 16:25:38 +02006243 self.assertIn("Node '/binman/u-boot-dtb': The zstd compression "
6244 "requires a length header", str(e.exception))
6245
Quentin Schulz9b5c6482022-09-02 15:10:48 +02006246 def testMkimageMultipleDataFiles(self):
6247 """Test passing multiple files to mkimage in a mkimage entry"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006248 self._SetupSplElf()
6249 self._SetupTplElf()
Quentin Schulz9b5c6482022-09-02 15:10:48 +02006250 data = self._DoReadFile('252_mkimage_mult_data.dts')
6251 # Size of files are packed in their 4B big-endian format
6252 expect = struct.pack('>I', len(U_BOOT_TPL_DATA))
6253 expect += struct.pack('>I', len(U_BOOT_SPL_DATA))
6254 # Size info is always followed by a 4B zero value.
6255 expect += tools.get_bytes(0, 4)
6256 expect += U_BOOT_TPL_DATA
6257 # All but last files are 4B-aligned
6258 align_pad = len(U_BOOT_TPL_DATA) % 4
6259 if align_pad:
6260 expect += tools.get_bytes(0, align_pad)
6261 expect += U_BOOT_SPL_DATA
6262 self.assertEqual(expect, data[-len(expect):])
6263
Marek Vasutf7413f02023-07-18 07:23:58 -06006264 def testMkimageMultipleExpanded(self):
6265 """Test passing multiple files to mkimage in a mkimage entry"""
6266 self._SetupSplElf()
6267 self._SetupTplElf()
6268 entry_args = {
6269 'spl-bss-pad': 'y',
6270 'spl-dtb': 'y',
6271 }
6272 data = self._DoReadFileDtb('252_mkimage_mult_data.dts',
6273 use_expanded=True, entry_args=entry_args)[0]
6274 pad_len = 10
6275 tpl_expect = U_BOOT_TPL_DATA
6276 spl_expect = U_BOOT_SPL_NODTB_DATA + tools.get_bytes(0, pad_len)
6277 spl_expect += U_BOOT_SPL_DTB_DATA
6278
6279 content = data[0x40:]
6280 lens = struct.unpack('>III', content[:12])
6281
6282 # Size of files are packed in their 4B big-endian format
6283 # Size info is always followed by a 4B zero value.
6284 self.assertEqual(len(tpl_expect), lens[0])
6285 self.assertEqual(len(spl_expect), lens[1])
6286 self.assertEqual(0, lens[2])
6287
6288 rest = content[12:]
6289 self.assertEqual(tpl_expect, rest[:len(tpl_expect)])
6290
6291 rest = rest[len(tpl_expect):]
6292 align_pad = len(tpl_expect) % 4
6293 self.assertEqual(tools.get_bytes(0, align_pad), rest[:align_pad])
6294 rest = rest[align_pad:]
6295 self.assertEqual(spl_expect, rest)
6296
Quentin Schulz9b5c6482022-09-02 15:10:48 +02006297 def testMkimageMultipleNoContent(self):
6298 """Test passing multiple data files to mkimage with one data file having no content"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006299 self._SetupSplElf()
Quentin Schulz9b5c6482022-09-02 15:10:48 +02006300 with self.assertRaises(ValueError) as exc:
6301 self._DoReadFile('253_mkimage_mult_no_content.dts')
6302 self.assertIn('Could not complete processing of contents',
6303 str(exc.exception))
6304
Quentin Schulz0d3a9262022-09-02 15:10:49 +02006305 def testMkimageFilename(self):
6306 """Test using mkimage to build a binary with a filename"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006307 self._SetupSplElf()
Quentin Schulz0d3a9262022-09-02 15:10:49 +02006308 retcode = self._DoTestFile('254_mkimage_filename.dts')
6309 self.assertEqual(0, retcode)
6310 fname = tools.get_output_filename('mkimage-test.bin')
6311 self.assertTrue(os.path.exists(fname))
6312
Simon Glass56d05412022-02-28 07:16:54 -07006313 def testVpl(self):
6314 """Test that an image with VPL and its device tree can be created"""
6315 # ELF file with a '__bss_size' symbol
6316 self._SetupVplElf()
6317 data = self._DoReadFile('255_u_boot_vpl.dts')
6318 self.assertEqual(U_BOOT_VPL_DATA + U_BOOT_VPL_DTB_DATA, data)
6319
6320 def testVplNoDtb(self):
6321 """Test that an image with vpl/u-boot-vpl-nodtb.bin can be created"""
6322 self._SetupVplElf()
6323 data = self._DoReadFile('256_u_boot_vpl_nodtb.dts')
6324 self.assertEqual(U_BOOT_VPL_NODTB_DATA,
6325 data[:len(U_BOOT_VPL_NODTB_DATA)])
6326
6327 def testExpandedVpl(self):
6328 """Test that an expanded entry type is selected for TPL when needed"""
6329 self._SetupVplElf()
6330
6331 entry_args = {
6332 'vpl-bss-pad': 'y',
6333 'vpl-dtb': 'y',
6334 }
6335 self._DoReadFileDtb('257_fdt_incl_vpl.dts', use_expanded=True,
6336 entry_args=entry_args)
6337 image = control.images['image']
6338 entries = image.GetEntries()
6339 self.assertEqual(1, len(entries))
6340
6341 # We only have u-boot-vpl, which be expanded
6342 self.assertIn('u-boot-vpl', entries)
6343 entry = entries['u-boot-vpl']
6344 self.assertEqual('u-boot-vpl-expanded', entry.etype)
6345 subent = entry.GetEntries()
6346 self.assertEqual(3, len(subent))
6347 self.assertIn('u-boot-vpl-nodtb', subent)
6348 self.assertIn('u-boot-vpl-bss-pad', subent)
6349 self.assertIn('u-boot-vpl-dtb', subent)
6350
6351 def testVplBssPadMissing(self):
6352 """Test that a missing symbol is detected"""
6353 self._SetupVplElf('u_boot_ucode_ptr')
6354 with self.assertRaises(ValueError) as e:
6355 self._DoReadFile('258_vpl_bss_pad.dts')
6356 self.assertIn('Expected __bss_size symbol in vpl/u-boot-vpl',
6357 str(e.exception))
6358
Neha Malcom Francis3eb4be32022-10-17 16:36:25 +05306359 def testSymlink(self):
Andrew Davis6b463da2023-07-22 00:14:44 +05306360 """Test that image files can be symlinked"""
Neha Malcom Francis3eb4be32022-10-17 16:36:25 +05306361 retcode = self._DoTestFile('259_symlink.dts', debug=True, map=True)
6362 self.assertEqual(0, retcode)
6363 image = control.images['test_image']
6364 fname = tools.get_output_filename('test_image.bin')
6365 sname = tools.get_output_filename('symlink_to_test.bin')
6366 self.assertTrue(os.path.islink(sname))
6367 self.assertEqual(os.readlink(sname), fname)
Alper Nebi Yasake63ca5a2022-03-27 18:31:45 +03006368
Andrew Davis6b463da2023-07-22 00:14:44 +05306369 def testSymlinkOverwrite(self):
6370 """Test that symlinked images can be overwritten"""
6371 testdir = TestFunctional._MakeInputDir('symlinktest')
6372 self._DoTestFile('259_symlink.dts', debug=True, map=True, output_dir=testdir)
6373 # build the same image again in the same directory so that existing symlink is present
6374 self._DoTestFile('259_symlink.dts', debug=True, map=True, output_dir=testdir)
6375 fname = tools.get_output_filename('test_image.bin')
6376 sname = tools.get_output_filename('symlink_to_test.bin')
6377 self.assertTrue(os.path.islink(sname))
6378 self.assertEqual(os.readlink(sname), fname)
6379
Simon Glass37f85de2022-10-20 18:22:47 -06006380 def testSymbolsElf(self):
6381 """Test binman can assign symbols embedded in an ELF file"""
6382 if not elf.ELF_TOOLS:
6383 self.skipTest('Python elftools not available')
6384 self._SetupTplElf('u_boot_binman_syms')
6385 self._SetupVplElf('u_boot_binman_syms')
6386 self._SetupSplElf('u_boot_binman_syms')
6387 data = self._DoReadFileDtb('260_symbols_elf.dts')[0]
6388 image_fname = tools.get_output_filename('image.bin')
6389
6390 image = control.images['image']
6391 entries = image.GetEntries()
6392
6393 for entry in entries.values():
6394 # No symbols in u-boot and it has faked contents anyway
6395 if entry.name == 'u-boot':
6396 continue
6397 edata = data[entry.image_pos:entry.image_pos + entry.size]
6398 efname = tools.get_output_filename(f'edata-{entry.name}')
6399 tools.write_file(efname, edata)
6400
6401 syms = elf.GetSymbolFileOffset(efname, ['_binman_u_boot'])
6402 re_name = re.compile('_binman_(u_boot_(.*))_prop_(.*)')
6403 for name, sym in syms.items():
6404 msg = 'test'
6405 val = elf.GetSymbolValue(sym, edata, msg)
6406 entry_m = re_name.match(name)
6407 if entry_m:
6408 ename, prop = entry_m.group(1), entry_m.group(3)
6409 entry, entry_name, prop_name = image.LookupEntry(entries,
6410 name, msg)
Simon Glassd3d3a102025-02-19 08:11:16 -07006411 expect_val = None
Simon Glass37f85de2022-10-20 18:22:47 -06006412 if prop_name == 'offset':
6413 expect_val = entry.offset
6414 elif prop_name == 'image_pos':
6415 expect_val = entry.image_pos
6416 elif prop_name == 'size':
6417 expect_val = entry.size
6418 self.assertEqual(expect_val, val)
6419
6420 def testSymbolsElfBad(self):
6421 """Check error when trying to write symbols without the elftools lib"""
6422 if not elf.ELF_TOOLS:
6423 self.skipTest('Python elftools not available')
6424 self._SetupTplElf('u_boot_binman_syms')
6425 self._SetupVplElf('u_boot_binman_syms')
6426 self._SetupSplElf('u_boot_binman_syms')
6427 try:
6428 elf.ELF_TOOLS = False
6429 with self.assertRaises(ValueError) as exc:
6430 self._DoReadFileDtb('260_symbols_elf.dts')
6431 finally:
6432 elf.ELF_TOOLS = True
6433 self.assertIn(
6434 "Section '/binman': entry '/binman/u-boot-spl-elf': "
6435 'Cannot write symbols to an ELF file without Python elftools',
6436 str(exc.exception))
6437
Simon Glassde244162023-01-07 14:07:08 -07006438 def testSectionFilename(self):
6439 """Check writing of section contents to a file"""
6440 data = self._DoReadFile('261_section_fname.dts')
6441 expected = (b'&&' + U_BOOT_DATA + b'&&&' +
6442 tools.get_bytes(ord('!'), 7) +
6443 U_BOOT_DATA + tools.get_bytes(ord('&'), 12))
6444 self.assertEqual(expected, data)
6445
6446 sect_fname = tools.get_output_filename('outfile.bin')
6447 self.assertTrue(os.path.exists(sect_fname))
6448 sect_data = tools.read_file(sect_fname)
6449 self.assertEqual(U_BOOT_DATA, sect_data)
6450
Simon Glass1e9e61c2023-01-07 14:07:12 -07006451 def testAbsent(self):
6452 """Check handling of absent entries"""
6453 data = self._DoReadFile('262_absent.dts')
6454 self.assertEqual(U_BOOT_DATA + U_BOOT_IMG_DATA, data)
6455
Simon Glassad5cfe12023-01-07 14:07:14 -07006456 def testPackTeeOsOptional(self):
6457 """Test that an image with an optional TEE binary can be created"""
6458 entry_args = {
6459 'tee-os-path': 'tee.elf',
6460 }
6461 data = self._DoReadFileDtb('263_tee_os_opt.dts',
6462 entry_args=entry_args)[0]
6463 self.assertEqual(U_BOOT_DATA + U_BOOT_IMG_DATA, data)
6464
6465 def checkFitTee(self, dts, tee_fname):
6466 """Check that a tee-os entry works and returns data
6467
6468 Args:
6469 dts (str): Device tree filename to use
6470 tee_fname (str): filename containing tee-os
6471
6472 Returns:
6473 bytes: Image contents
6474 """
6475 if not elf.ELF_TOOLS:
6476 self.skipTest('Python elftools not available')
6477 entry_args = {
6478 'of-list': 'test-fdt1 test-fdt2',
6479 'default-dt': 'test-fdt2',
6480 'tee-os-path': tee_fname,
6481 }
6482 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
6483 data = self._DoReadFileDtb(dts, entry_args=entry_args,
6484 extra_indirs=[test_subdir])[0]
6485 return data
6486
6487 def testFitTeeOsOptionalFit(self):
6488 """Test an image with a FIT with an optional OP-TEE binary"""
6489 data = self.checkFitTee('264_tee_os_opt_fit.dts', 'tee.bin')
6490
6491 # There should be only one node, holding the data set up in SetUpClass()
6492 # for tee.bin
6493 dtb = fdt.Fdt.FromData(data)
6494 dtb.Scan()
6495 node = dtb.GetNode('/images/tee-1')
6496 self.assertEqual(TEE_ADDR,
6497 fdt_util.fdt32_to_cpu(node.props['load'].value))
6498 self.assertEqual(TEE_ADDR,
6499 fdt_util.fdt32_to_cpu(node.props['entry'].value))
6500 self.assertEqual(U_BOOT_DATA, node.props['data'].bytes)
6501
Simon Glass14d64e32025-04-29 07:21:59 -06006502 with terminal.capture() as (stdout, stderr):
Jonas Karlmanb2be3e42023-07-18 20:34:36 +00006503 self.checkFitTee('264_tee_os_opt_fit.dts', '')
6504 err = stderr.getvalue()
6505 self.assertRegex(
6506 err,
6507 "Image '.*' is missing optional external blobs but is still functional: tee-os")
6508
Simon Glassad5cfe12023-01-07 14:07:14 -07006509 def testFitTeeOsOptionalFitBad(self):
6510 """Test an image with a FIT with an optional OP-TEE binary"""
6511 with self.assertRaises(ValueError) as exc:
6512 self.checkFitTee('265_tee_os_opt_fit_bad.dts', 'tee.bin')
6513 self.assertIn(
6514 "Node '/binman/fit': subnode 'images/@tee-SEQ': Failed to read ELF file: Magic number does not match",
6515 str(exc.exception))
6516
6517 def testFitTeeOsBad(self):
6518 """Test an OP-TEE binary with wrong formats"""
6519 self.make_tee_bin('tee.bad1', 123)
6520 with self.assertRaises(ValueError) as exc:
6521 self.checkFitTee('264_tee_os_opt_fit.dts', 'tee.bad1')
6522 self.assertIn(
6523 "Node '/binman/fit/images/@tee-SEQ/tee-os': OP-TEE paged mode not supported",
6524 str(exc.exception))
6525
6526 self.make_tee_bin('tee.bad2', 0, b'extra data')
6527 with self.assertRaises(ValueError) as exc:
6528 self.checkFitTee('264_tee_os_opt_fit.dts', 'tee.bad2')
6529 self.assertIn(
6530 "Node '/binman/fit/images/@tee-SEQ/tee-os': Invalid OP-TEE file: size mismatch (expected 0x4, have 0xe)",
6531 str(exc.exception))
6532
Simon Glass63328f12023-01-07 14:07:15 -07006533 def testExtblobOptional(self):
6534 """Test an image with an external blob that is optional"""
Simon Glass14d64e32025-04-29 07:21:59 -06006535 with terminal.capture() as (stdout, stderr):
Simon Glass63328f12023-01-07 14:07:15 -07006536 data = self._DoReadFile('266_blob_ext_opt.dts')
6537 self.assertEqual(REFCODE_DATA, data)
6538 err = stderr.getvalue()
6539 self.assertRegex(
6540 err,
Jonas Karlman9f96b812023-07-18 20:34:34 +00006541 "Image '.*' is missing optional external blobs but is still functional: missing")
Simon Glass63328f12023-01-07 14:07:15 -07006542
Simon Glass7447a9d2023-01-11 16:10:12 -07006543 def testSectionInner(self):
6544 """Test an inner section with a size"""
6545 data = self._DoReadFile('267_section_inner.dts')
6546 expected = U_BOOT_DATA + tools.get_bytes(0, 12)
6547 self.assertEqual(expected, data)
6548
Simon Glassa4948b22023-01-11 16:10:14 -07006549 def testNull(self):
6550 """Test an image with a null entry"""
6551 data = self._DoReadFile('268_null.dts')
6552 self.assertEqual(U_BOOT_DATA + b'\xff\xff\xff\xff' + U_BOOT_IMG_DATA, data)
6553
Simon Glassf1ee03b2023-01-11 16:10:16 -07006554 def testOverlap(self):
6555 """Test an image with a overlapping entry"""
6556 data = self._DoReadFile('269_overlap.dts')
6557 self.assertEqual(U_BOOT_DATA[:1] + b'aa' + U_BOOT_DATA[3:], data)
6558
6559 image = control.images['image']
6560 entries = image.GetEntries()
6561
6562 self.assertIn('inset', entries)
6563 inset = entries['inset']
6564 self.assertEqual(1, inset.offset);
6565 self.assertEqual(1, inset.image_pos);
6566 self.assertEqual(2, inset.size);
6567
6568 def testOverlapNull(self):
6569 """Test an image with a null overlap"""
6570 data = self._DoReadFile('270_overlap_null.dts')
6571 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
6572
6573 # Check the FMAP
6574 fhdr, fentries = fmap_util.DecodeFmap(data[len(U_BOOT_DATA):])
6575 self.assertEqual(4, fhdr.nareas)
6576 fiter = iter(fentries)
6577
6578 fentry = next(fiter)
6579 self.assertEqual(b'SECTION', fentry.name)
6580 self.assertEqual(0, fentry.offset)
6581 self.assertEqual(len(U_BOOT_DATA), fentry.size)
6582 self.assertEqual(0, fentry.flags)
6583
6584 fentry = next(fiter)
6585 self.assertEqual(b'U_BOOT', fentry.name)
6586 self.assertEqual(0, fentry.offset)
6587 self.assertEqual(len(U_BOOT_DATA), fentry.size)
6588 self.assertEqual(0, fentry.flags)
6589
6590 # Make sure that the NULL entry appears in the FMAP
6591 fentry = next(fiter)
6592 self.assertEqual(b'NULL', fentry.name)
6593 self.assertEqual(1, fentry.offset)
6594 self.assertEqual(2, fentry.size)
6595 self.assertEqual(0, fentry.flags)
6596
6597 fentry = next(fiter)
6598 self.assertEqual(b'FMAP', fentry.name)
6599 self.assertEqual(len(U_BOOT_DATA), fentry.offset)
6600
6601 def testOverlapBad(self):
6602 """Test an image with a bad overlapping entry"""
6603 with self.assertRaises(ValueError) as exc:
6604 self._DoReadFile('271_overlap_bad.dts')
6605 self.assertIn(
6606 "Node '/binman/inset': Offset 0x10 (16) ending at 0x12 (18) must overlap with existing entries",
6607 str(exc.exception))
6608
6609 def testOverlapNoOffset(self):
6610 """Test an image with a bad overlapping entry"""
6611 with self.assertRaises(ValueError) as exc:
6612 self._DoReadFile('272_overlap_no_size.dts')
6613 self.assertIn(
6614 "Node '/binman/inset': 'fill' entry is missing properties: size",
6615 str(exc.exception))
6616
Simon Glasse0035c92023-01-11 16:10:17 -07006617 def testBlobSymbol(self):
6618 """Test a blob with symbols read from an ELF file"""
6619 elf_fname = self.ElfTestFile('blob_syms')
6620 TestFunctional._MakeInputFile('blob_syms', tools.read_file(elf_fname))
6621 TestFunctional._MakeInputFile('blob_syms.bin',
6622 tools.read_file(self.ElfTestFile('blob_syms.bin')))
6623
6624 data = self._DoReadFile('273_blob_symbol.dts')
6625
6626 syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
6627 addr = elf.GetSymbolAddress(elf_fname, '__my_start_sym')
6628 self.assertEqual(syms['_binman_sym_magic'].address, addr)
6629 self.assertEqual(syms['_binman_inset_prop_offset'].address, addr + 4)
6630 self.assertEqual(syms['_binman_inset_prop_size'].address, addr + 8)
6631
6632 sym_values = struct.pack('<LLL', elf.BINMAN_SYM_MAGIC_VALUE, 4, 8)
6633 expected = sym_values
6634 self.assertEqual(expected, data[:len(expected)])
6635
Simon Glass49e9c002023-01-11 16:10:19 -07006636 def testOffsetFromElf(self):
6637 """Test a blob with symbols read from an ELF file"""
6638 elf_fname = self.ElfTestFile('blob_syms')
6639 TestFunctional._MakeInputFile('blob_syms', tools.read_file(elf_fname))
6640 TestFunctional._MakeInputFile('blob_syms.bin',
6641 tools.read_file(self.ElfTestFile('blob_syms.bin')))
6642
6643 data = self._DoReadFile('274_offset_from_elf.dts')
6644
6645 syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
6646 base = elf.GetSymbolAddress(elf_fname, '__my_start_sym')
6647
6648 image = control.images['image']
6649 entries = image.GetEntries()
6650
6651 self.assertIn('inset', entries)
6652 inset = entries['inset']
6653
6654 self.assertEqual(base + 4, inset.offset);
6655 self.assertEqual(base + 4, inset.image_pos);
6656 self.assertEqual(4, inset.size);
6657
6658 self.assertIn('inset2', entries)
6659 inset = entries['inset2']
6660 self.assertEqual(base + 8, inset.offset);
6661 self.assertEqual(base + 8, inset.image_pos);
6662 self.assertEqual(4, inset.size);
6663
Jonas Karlmanc59ea892023-01-21 19:01:39 +00006664 def testFitAlign(self):
6665 """Test an image with an FIT with aligned external data"""
6666 data = self._DoReadFile('275_fit_align.dts')
6667 self.assertEqual(4096, len(data))
6668
6669 dtb = fdt.Fdt.FromData(data)
6670 dtb.Scan()
6671
6672 props = self._GetPropTree(dtb, ['data-position'])
6673 expected = {
6674 'u-boot:data-position': 1024,
6675 'fdt-1:data-position': 2048,
6676 'fdt-2:data-position': 3072,
6677 }
6678 self.assertEqual(expected, props)
6679
Jonas Karlman490f73c2023-01-21 19:02:12 +00006680 def testFitFirmwareLoadables(self):
6681 """Test an image with an FIT that use fit,firmware"""
6682 if not elf.ELF_TOOLS:
6683 self.skipTest('Python elftools not available')
6684 entry_args = {
6685 'of-list': 'test-fdt1',
6686 'default-dt': 'test-fdt1',
6687 'atf-bl31-path': 'bl31.elf',
6688 'tee-os-path': 'missing.bin',
6689 }
6690 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
Simon Glass14d64e32025-04-29 07:21:59 -06006691 with terminal.capture() as (stdout, stderr):
Simon Glass62f85902023-02-23 18:18:01 -07006692 data = self._DoReadFileDtb(
6693 '276_fit_firmware_loadables.dts',
6694 entry_args=entry_args,
6695 extra_indirs=[test_subdir])[0]
Jonas Karlman490f73c2023-01-21 19:02:12 +00006696
6697 dtb = fdt.Fdt.FromData(data)
6698 dtb.Scan()
6699
6700 node = dtb.GetNode('/configurations/conf-uboot-1')
6701 self.assertEqual('u-boot', node.props['firmware'].value)
6702 self.assertEqual(['atf-1', 'atf-2'],
6703 fdt_util.GetStringList(node, 'loadables'))
6704
6705 node = dtb.GetNode('/configurations/conf-atf-1')
6706 self.assertEqual('atf-1', node.props['firmware'].value)
6707 self.assertEqual(['u-boot', 'atf-2'],
6708 fdt_util.GetStringList(node, 'loadables'))
6709
6710 node = dtb.GetNode('/configurations/conf-missing-uboot-1')
6711 self.assertEqual('u-boot', node.props['firmware'].value)
6712 self.assertEqual(['atf-1', 'atf-2'],
6713 fdt_util.GetStringList(node, 'loadables'))
6714
6715 node = dtb.GetNode('/configurations/conf-missing-atf-1')
6716 self.assertEqual('atf-1', node.props['firmware'].value)
6717 self.assertEqual(['u-boot', 'atf-2'],
6718 fdt_util.GetStringList(node, 'loadables'))
6719
6720 node = dtb.GetNode('/configurations/conf-missing-tee-1')
6721 self.assertEqual('atf-1', node.props['firmware'].value)
6722 self.assertEqual(['u-boot', 'atf-2'],
6723 fdt_util.GetStringList(node, 'loadables'))
6724
Simon Glass9a1c7262023-02-22 12:14:49 -07006725 def testTooldir(self):
6726 """Test that we can specify the tooldir"""
Simon Glass14d64e32025-04-29 07:21:59 -06006727 with terminal.capture() as (stdout, stderr):
Simon Glass9a1c7262023-02-22 12:14:49 -07006728 self.assertEqual(0, self._DoBinman('--tooldir', 'fred',
6729 'tool', '-l'))
6730 self.assertEqual('fred', bintool.Bintool.tooldir)
6731
6732 # Check that the toolpath is updated correctly
6733 self.assertEqual(['fred'], tools.tool_search_paths)
6734
6735 # Try with a few toolpaths; the tooldir should be at the end
Simon Glass14d64e32025-04-29 07:21:59 -06006736 with terminal.capture() as (stdout, stderr):
Simon Glass9a1c7262023-02-22 12:14:49 -07006737 self.assertEqual(0, self._DoBinman(
6738 '--toolpath', 'mary', '--toolpath', 'anna', '--tooldir', 'fred',
6739 'tool', '-l'))
6740 self.assertEqual(['mary', 'anna', 'fred'], tools.tool_search_paths)
6741
Simon Glass49b77e82023-03-02 17:02:44 -07006742 def testReplaceSectionEntry(self):
6743 """Test replacing an entry in a section"""
6744 expect_data = b'w' * len(U_BOOT_DATA + COMPRESS_DATA)
6745 entry_data, expected_fdtmap, image = self._RunReplaceCmd('section/blob',
6746 expect_data, dts='241_replace_section_simple.dts')
6747 self.assertEqual(expect_data, entry_data)
6748
6749 entries = image.GetEntries()
6750 self.assertIn('section', entries)
6751 section = entries['section']
6752
6753 sect_entries = section.GetEntries()
6754 self.assertIn('blob', sect_entries)
6755 entry = sect_entries['blob']
6756 self.assertEqual(len(expect_data), entry.size)
6757
6758 fname = tools.get_output_filename('image-updated.bin')
6759 data = tools.read_file(fname)
6760
6761 new_blob_data = data[entry.image_pos:entry.image_pos + len(expect_data)]
6762 self.assertEqual(expect_data, new_blob_data)
6763
6764 self.assertEqual(U_BOOT_DATA,
6765 data[entry.image_pos + len(expect_data):]
6766 [:len(U_BOOT_DATA)])
6767
6768 def testReplaceSectionDeep(self):
6769 """Test replacing an entry in two levels of sections"""
6770 expect_data = b'w' * len(U_BOOT_DATA + COMPRESS_DATA)
6771 entry_data, expected_fdtmap, image = self._RunReplaceCmd(
6772 'section/section/blob', expect_data,
6773 dts='278_replace_section_deep.dts')
6774 self.assertEqual(expect_data, entry_data)
6775
6776 entries = image.GetEntries()
6777 self.assertIn('section', entries)
6778 section = entries['section']
6779
6780 subentries = section.GetEntries()
6781 self.assertIn('section', subentries)
6782 section = subentries['section']
6783
6784 sect_entries = section.GetEntries()
6785 self.assertIn('blob', sect_entries)
6786 entry = sect_entries['blob']
6787 self.assertEqual(len(expect_data), entry.size)
6788
6789 fname = tools.get_output_filename('image-updated.bin')
6790 data = tools.read_file(fname)
6791
6792 new_blob_data = data[entry.image_pos:entry.image_pos + len(expect_data)]
6793 self.assertEqual(expect_data, new_blob_data)
6794
6795 self.assertEqual(U_BOOT_DATA,
6796 data[entry.image_pos + len(expect_data):]
6797 [:len(U_BOOT_DATA)])
6798
6799 def testReplaceFitSibling(self):
6800 """Test an image with a FIT inside where we replace its sibling"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006801 self._SetupSplElf()
Simon Glass49b77e82023-03-02 17:02:44 -07006802 fname = TestFunctional._MakeInputFile('once', b'available once')
6803 self._DoReadFileRealDtb('277_replace_fit_sibling.dts')
6804 os.remove(fname)
6805
6806 try:
6807 tmpdir, updated_fname = self._SetupImageInTmpdir()
6808
6809 fname = os.path.join(tmpdir, 'update-blob')
6810 expected = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) + 1)
6811 tools.write_file(fname, expected)
6812
6813 self._DoBinman('replace', '-i', updated_fname, 'blob', '-f', fname)
6814 data = tools.read_file(updated_fname)
6815 start = len(U_BOOT_DTB_DATA)
6816 self.assertEqual(expected, data[start:start + len(expected)])
6817 map_fname = os.path.join(tmpdir, 'image-updated.map')
6818 self.assertFalse(os.path.exists(map_fname))
6819 finally:
6820 shutil.rmtree(tmpdir)
6821
Simon Glassc3fe97f2023-03-02 17:02:45 -07006822 def testX509Cert(self):
6823 """Test creating an X509 certificate"""
6824 keyfile = self.TestFile('key.key')
6825 entry_args = {
6826 'keyfile': keyfile,
6827 }
6828 data = self._DoReadFileDtb('279_x509_cert.dts',
6829 entry_args=entry_args)[0]
6830 cert = data[:-4]
6831 self.assertEqual(U_BOOT_DATA, data[-4:])
6832
6833 # TODO: verify the signature
6834
6835 def testX509CertMissing(self):
6836 """Test that binman still produces an image if openssl is missing"""
6837 keyfile = self.TestFile('key.key')
6838 entry_args = {
6839 'keyfile': 'keyfile',
6840 }
Simon Glass14d64e32025-04-29 07:21:59 -06006841 with terminal.capture() as (_, stderr):
Simon Glassc3fe97f2023-03-02 17:02:45 -07006842 self._DoTestFile('279_x509_cert.dts',
6843 force_missing_bintools='openssl',
6844 entry_args=entry_args)
6845 err = stderr.getvalue()
6846 self.assertRegex(err, "Image 'image'.*missing bintools.*: openssl")
6847
Jonas Karlman35305492023-02-25 19:01:33 +00006848 def testPackRockchipTpl(self):
6849 """Test that an image with a Rockchip TPL binary can be created"""
Simon Glasse3ef5ed2023-07-24 09:19:58 -06006850 data = self._DoReadFile('291_rockchip_tpl.dts')
Jonas Karlman35305492023-02-25 19:01:33 +00006851 self.assertEqual(ROCKCHIP_TPL_DATA, data[:len(ROCKCHIP_TPL_DATA)])
6852
Jonas Karlman1016ec72023-02-25 19:01:35 +00006853 def testMkimageMissingBlobMultiple(self):
6854 """Test missing blob with mkimage entry and multiple-data-files"""
Simon Glass14d64e32025-04-29 07:21:59 -06006855 with terminal.capture() as (stdout, stderr):
Simon Glasse3ef5ed2023-07-24 09:19:58 -06006856 self._DoTestFile('292_mkimage_missing_multiple.dts', allow_missing=True)
Jonas Karlman1016ec72023-02-25 19:01:35 +00006857 err = stderr.getvalue()
6858 self.assertIn("is missing external blobs and is non-functional", err)
6859
6860 with self.assertRaises(ValueError) as e:
Simon Glasse3ef5ed2023-07-24 09:19:58 -06006861 self._DoTestFile('292_mkimage_missing_multiple.dts', allow_missing=False)
Jonas Karlman1016ec72023-02-25 19:01:35 +00006862 self.assertIn("not found in input path", str(e.exception))
6863
Ivan Mikhaylov3cfcaa4d2023-03-08 01:13:40 +00006864 def _PrepareSignEnv(self, dts='280_fit_sign.dts'):
6865 """Prepare sign environment
6866
6867 Create private and public keys, add pubkey into dtb.
6868
6869 Returns:
6870 Tuple:
6871 FIT container
6872 Image name
6873 Private key
6874 DTB
6875 """
Marek Vasutf7413f02023-07-18 07:23:58 -06006876 self._SetupSplElf()
Ivan Mikhaylov3cfcaa4d2023-03-08 01:13:40 +00006877 data = self._DoReadFileRealDtb(dts)
6878 updated_fname = tools.get_output_filename('image-updated.bin')
6879 tools.write_file(updated_fname, data)
6880 dtb = tools.get_output_filename('source.dtb')
6881 private_key = tools.get_output_filename('test_key.key')
6882 public_key = tools.get_output_filename('test_key.crt')
6883 fit = tools.get_output_filename('fit.fit')
6884 key_dir = tools.get_output_dir()
6885
6886 tools.run('openssl', 'req', '-batch' , '-newkey', 'rsa:4096',
6887 '-sha256', '-new', '-nodes', '-x509', '-keyout',
6888 private_key, '-out', public_key)
6889 tools.run('fdt_add_pubkey', '-a', 'sha256,rsa4096', '-k', key_dir,
6890 '-n', 'test_key', '-r', 'conf', dtb)
6891
6892 return fit, updated_fname, private_key, dtb
6893
6894 def testSignSimple(self):
6895 """Test that a FIT container can be signed in image"""
6896 is_signed = False
6897 fit, fname, private_key, dtb = self._PrepareSignEnv()
6898
6899 # do sign with private key
6900 control.SignEntries(fname, None, private_key, 'sha256,rsa4096',
6901 ['fit'])
6902 is_signed = self._CheckSign(fit, dtb)
6903
6904 self.assertEqual(is_signed, True)
6905
6906 def testSignExactFIT(self):
6907 """Test that a FIT container can be signed and replaced in image"""
6908 is_signed = False
6909 fit, fname, private_key, dtb = self._PrepareSignEnv()
6910
6911 # Make sure we propagate the toolpath, since mkimage may not be on PATH
6912 args = []
6913 if self.toolpath:
6914 for path in self.toolpath:
6915 args += ['--toolpath', path]
6916
6917 # do sign with private key
6918 self._DoBinman(*args, 'sign', '-i', fname, '-k', private_key, '-a',
6919 'sha256,rsa4096', '-f', fit, 'fit')
6920 is_signed = self._CheckSign(fit, dtb)
6921
6922 self.assertEqual(is_signed, True)
6923
6924 def testSignNonFit(self):
6925 """Test a non-FIT entry cannot be signed"""
6926 is_signed = False
6927 fit, fname, private_key, _ = self._PrepareSignEnv(
6928 '281_sign_non_fit.dts')
6929
6930 # do sign with private key
6931 with self.assertRaises(ValueError) as e:
6932 self._DoBinman('sign', '-i', fname, '-k', private_key, '-a',
6933 'sha256,rsa4096', '-f', fit, 'u-boot')
6934 self.assertIn(
6935 "Node '/u-boot': Updating signatures is not supported with this entry type",
6936 str(e.exception))
6937
6938 def testSignMissingMkimage(self):
6939 """Test that FIT signing handles a missing mkimage tool"""
6940 fit, fname, private_key, _ = self._PrepareSignEnv()
6941
6942 # try to sign with a missing mkimage tool
6943 bintool.Bintool.set_missing_list(['mkimage'])
6944 with self.assertRaises(ValueError) as e:
6945 control.SignEntries(fname, None, private_key, 'sha256,rsa4096',
6946 ['fit'])
6947 self.assertIn("Node '/fit': Missing tool: 'mkimage'", str(e.exception))
6948
Simon Glass4abf7842023-07-18 07:23:54 -06006949 def testSymbolNoWrite(self):
6950 """Test disabling of symbol writing"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006951 self._SetupSplElf()
Simon Glass4abf7842023-07-18 07:23:54 -06006952 self.checkSymbols('282_symbols_disable.dts', U_BOOT_SPL_DATA, 0x1c,
6953 no_write_symbols=True)
6954
6955 def testSymbolNoWriteExpanded(self):
6956 """Test disabling of symbol writing in expanded entries"""
6957 entry_args = {
6958 'spl-dtb': '1',
6959 }
6960 self.checkSymbols('282_symbols_disable.dts', U_BOOT_SPL_NODTB_DATA +
6961 U_BOOT_SPL_DTB_DATA, 0x38,
6962 entry_args=entry_args, use_expanded=True,
6963 no_write_symbols=True)
6964
Marek Vasutf7413f02023-07-18 07:23:58 -06006965 def testMkimageSpecial(self):
6966 """Test mkimage ignores special hash-1 node"""
6967 data = self._DoReadFile('283_mkimage_special.dts')
6968
6969 # Just check that the data appears in the file somewhere
6970 self.assertIn(U_BOOT_DATA, data)
6971
Simon Glass2d94c422023-07-18 07:23:59 -06006972 def testFitFdtList(self):
6973 """Test an image with an FIT with the fit,fdt-list-val option"""
6974 entry_args = {
6975 'default-dt': 'test-fdt2',
6976 }
6977 data = self._DoReadFileDtb(
6978 '284_fit_fdt_list.dts',
6979 entry_args=entry_args,
6980 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
6981 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
6982 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
6983
Simon Glass83b8bfe2023-07-18 07:24:01 -06006984 def testSplEmptyBss(self):
6985 """Test an expanded SPL with a zero-size BSS"""
6986 # ELF file with a '__bss_size' symbol
6987 self._SetupSplElf(src_fname='bss_data_zero')
6988
6989 entry_args = {
6990 'spl-bss-pad': 'y',
6991 'spl-dtb': 'y',
6992 }
6993 data = self._DoReadFileDtb('285_spl_expand.dts',
6994 use_expanded=True, entry_args=entry_args)[0]
6995
Simon Glassfc792842023-07-18 07:24:04 -06006996 def testTemplate(self):
6997 """Test using a template"""
6998 TestFunctional._MakeInputFile('vga2.bin', b'#' + VGA_DATA)
6999 data = self._DoReadFile('286_template.dts')
7000 first = U_BOOT_DATA + VGA_DATA + U_BOOT_DTB_DATA
7001 second = U_BOOT_DATA + b'#' + VGA_DATA + U_BOOT_DTB_DATA
7002 self.assertEqual(U_BOOT_IMG_DATA + first + second, data)
7003
Simon Glass09490b02023-07-22 21:43:52 -06007004 dtb_fname1 = tools.get_output_filename('u-boot.dtb.tmpl1')
7005 self.assertTrue(os.path.exists(dtb_fname1))
7006 dtb = fdt.Fdt.FromData(tools.read_file(dtb_fname1))
7007 dtb.Scan()
7008 node1 = dtb.GetNode('/binman/template')
7009 self.assertTrue(node1)
7010 vga = dtb.GetNode('/binman/first/intel-vga')
7011 self.assertTrue(vga)
7012
Simon Glass54825e12023-07-22 21:43:56 -06007013 dtb_fname2 = tools.get_output_filename('u-boot.dtb.tmpl2')
7014 self.assertTrue(os.path.exists(dtb_fname2))
7015 dtb2 = fdt.Fdt.FromData(tools.read_file(dtb_fname2))
7016 dtb2.Scan()
7017 node2 = dtb2.GetNode('/binman/template')
7018 self.assertFalse(node2)
7019
Simon Glass9909c112023-07-18 07:24:05 -06007020 def testTemplateBlobMulti(self):
7021 """Test using a template with 'multiple-images' enabled"""
7022 TestFunctional._MakeInputFile('my-blob.bin', b'blob')
7023 TestFunctional._MakeInputFile('my-blob2.bin', b'other')
7024 retcode = self._DoTestFile('287_template_multi.dts')
7025
7026 self.assertEqual(0, retcode)
7027 image = control.images['image']
7028 image_fname = tools.get_output_filename('my-image.bin')
7029 data = tools.read_file(image_fname)
7030 self.assertEqual(b'blob@@@@other', data)
7031
Simon Glass5dc511b2023-07-18 07:24:06 -06007032 def testTemplateFit(self):
7033 """Test using a template in a FIT"""
7034 fit_data = self._DoReadFile('288_template_fit.dts')
7035 fname = os.path.join(self._indir, 'fit_data.fit')
7036 tools.write_file(fname, fit_data)
7037 out = tools.run('dumpimage', '-l', fname)
7038
Simon Glassaa6e0552023-07-18 07:24:07 -06007039 def testTemplateSection(self):
7040 """Test using a template in a section (not at top level)"""
7041 TestFunctional._MakeInputFile('vga2.bin', b'#' + VGA_DATA)
7042 data = self._DoReadFile('289_template_section.dts')
7043 first = U_BOOT_DATA + VGA_DATA + U_BOOT_DTB_DATA
7044 second = U_BOOT_DATA + b'#' + VGA_DATA + U_BOOT_DTB_DATA
7045 self.assertEqual(U_BOOT_IMG_DATA + first + second + first, data)
7046
Simon Glassf53a7bc2023-07-18 07:24:08 -06007047 def testMkimageSymbols(self):
7048 """Test using mkimage to build an image with symbols in it"""
7049 self._SetupSplElf('u_boot_binman_syms')
7050 data = self._DoReadFile('290_mkimage_sym.dts')
7051
7052 image = control.images['image']
7053 entries = image.GetEntries()
7054 self.assertIn('u-boot', entries)
7055 u_boot = entries['u-boot']
7056
7057 mkim = entries['mkimage']
7058 mkim_entries = mkim.GetEntries()
7059 self.assertIn('u-boot-spl', mkim_entries)
7060 spl = mkim_entries['u-boot-spl']
7061 self.assertIn('u-boot-spl2', mkim_entries)
7062 spl2 = mkim_entries['u-boot-spl2']
7063
7064 # skip the mkimage header and the area sizes
7065 mk_data = data[mkim.offset + 0x40:]
7066 size, term = struct.unpack('>LL', mk_data[:8])
7067
7068 # There should be only one image, so check that the zero terminator is
7069 # present
7070 self.assertEqual(0, term)
7071
7072 content = mk_data[8:8 + size]
7073
7074 # The image should contain the symbols from u_boot_binman_syms.c
7075 # Note that image_pos is adjusted by the base address of the image,
7076 # which is 0x10 in our test image
7077 spl_data = content[:0x18]
7078 content = content[0x1b:]
7079
7080 # After the header is a table of offsets for each image. There should
7081 # only be one image, then a 0 terminator, so figure out the real start
7082 # of the image data
7083 base = 0x40 + 8
7084
7085 # Check symbols in both u-boot-spl and u-boot-spl2
7086 for i in range(2):
7087 vals = struct.unpack('<LLQLL', spl_data)
7088
7089 # The image should contain the symbols from u_boot_binman_syms.c
7090 # Note that image_pos is adjusted by the base address of the image,
7091 # which is 0x10 in our 'u_boot_binman_syms' test image
7092 self.assertEqual(elf.BINMAN_SYM_MAGIC_VALUE, vals[0])
7093 self.assertEqual(base, vals[1])
7094 self.assertEqual(spl2.offset, vals[2])
7095 # figure out the internal positions of its components
7096 self.assertEqual(0x10 + u_boot.image_pos, vals[3])
7097
7098 # Check that spl and spl2 are actually at the indicated positions
7099 self.assertEqual(
7100 elf.BINMAN_SYM_MAGIC_VALUE,
7101 struct.unpack('<I', data[spl.image_pos:spl.image_pos + 4])[0])
7102 self.assertEqual(
7103 elf.BINMAN_SYM_MAGIC_VALUE,
7104 struct.unpack('<I', data[spl2.image_pos:spl2.image_pos + 4])[0])
7105
7106 self.assertEqual(len(U_BOOT_DATA), vals[4])
7107
7108 # Move to next
7109 spl_data = content[:0x18]
7110
Simon Glass86b3e472023-07-22 21:43:57 -06007111 def testTemplatePhandle(self):
7112 """Test using a template in a node containing a phandle"""
7113 entry_args = {
7114 'atf-bl31-path': 'bl31.elf',
7115 }
Simon Glass76ee0ca2023-08-03 17:23:58 -06007116 data = self._DoReadFileDtb('309_template_phandle.dts',
Simon Glass86b3e472023-07-22 21:43:57 -06007117 entry_args=entry_args)
7118 fname = tools.get_output_filename('image.bin')
7119 out = tools.run('dumpimage', '-l', fname)
7120
7121 # We should see the FIT description and one for each of the two images
7122 lines = out.splitlines()
7123 descs = [line.split()[-1] for line in lines if 'escription' in line]
7124 self.assertEqual(['test-desc', 'atf', 'fdt'], descs)
7125
7126 def testTemplatePhandleDup(self):
7127 """Test using a template in a node containing a phandle"""
7128 entry_args = {
7129 'atf-bl31-path': 'bl31.elf',
7130 }
7131 with self.assertRaises(ValueError) as e:
Simon Glass76ee0ca2023-08-03 17:23:58 -06007132 self._DoReadFileDtb('310_template_phandle_dup.dts',
Simon Glass86b3e472023-07-22 21:43:57 -06007133 entry_args=entry_args)
7134 self.assertIn(
7135 'Duplicate phandle 1 in nodes /binman/image/fit/images/atf/atf-bl31 and /binman/image-2/fit/images/atf/atf-bl31',
7136 str(e.exception))
7137
Neha Malcom Francis3b788942023-07-22 00:14:24 +05307138 def testTIBoardConfig(self):
7139 """Test that a schema validated board config file can be generated"""
Simon Glassf1264ba2023-07-24 09:19:59 -06007140 data = self._DoReadFile('293_ti_board_cfg.dts')
Neha Malcom Francis3b788942023-07-22 00:14:24 +05307141 self.assertEqual(TI_BOARD_CONFIG_DATA, data)
7142
Neha Malcom Francis8cd04512024-01-05 17:09:17 +05307143 def testTIBoardConfigLint(self):
7144 """Test that an incorrectly linted config file would generate error"""
7145 with self.assertRaises(ValueError) as e:
7146 data = self._DoReadFile('323_ti_board_cfg_phony.dts')
7147 self.assertIn("Yamllint error", str(e.exception))
7148
Neha Malcom Francis3b788942023-07-22 00:14:24 +05307149 def testTIBoardConfigCombined(self):
7150 """Test that a schema validated combined board config file can be generated"""
Simon Glassf1264ba2023-07-24 09:19:59 -06007151 data = self._DoReadFile('294_ti_board_cfg_combined.dts')
Neha Malcom Francis3b788942023-07-22 00:14:24 +05307152 configlen_noheader = TI_BOARD_CONFIG_DATA * 4
7153 self.assertGreater(data, configlen_noheader)
7154
7155 def testTIBoardConfigNoDataType(self):
7156 """Test that error is thrown when data type is not supported"""
7157 with self.assertRaises(ValueError) as e:
Simon Glassf1264ba2023-07-24 09:19:59 -06007158 data = self._DoReadFile('295_ti_board_cfg_no_type.dts')
Neha Malcom Francis3b788942023-07-22 00:14:24 +05307159 self.assertIn("Schema validation error", str(e.exception))
Simon Glassde244162023-01-07 14:07:08 -07007160
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307161 def testPackTiSecure(self):
7162 """Test that an image with a TI secured binary can be created"""
7163 keyfile = self.TestFile('key.key')
7164 entry_args = {
7165 'keyfile': keyfile,
7166 }
Simon Glassf1264ba2023-07-24 09:19:59 -06007167 data = self._DoReadFileDtb('296_ti_secure.dts',
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307168 entry_args=entry_args)[0]
7169 self.assertGreater(len(data), len(TI_UNSECURE_DATA))
7170
Manorit Chawdhry2e523b02023-12-29 16:16:27 +05307171 def testPackTiSecureFirewall(self):
7172 """Test that an image with a TI secured binary can be created"""
7173 keyfile = self.TestFile('key.key')
7174 entry_args = {
7175 'keyfile': keyfile,
7176 }
7177 data_no_firewall = self._DoReadFileDtb('296_ti_secure.dts',
7178 entry_args=entry_args)[0]
7179 data_firewall = self._DoReadFileDtb('324_ti_secure_firewall.dts',
7180 entry_args=entry_args)[0]
7181 self.assertGreater(len(data_firewall),len(data_no_firewall))
7182
7183 def testPackTiSecureFirewallMissingProperty(self):
7184 """Test that an image with a TI secured binary can be created"""
7185 keyfile = self.TestFile('key.key')
7186 entry_args = {
7187 'keyfile': keyfile,
7188 }
7189 with self.assertRaises(ValueError) as e:
7190 data_firewall = self._DoReadFileDtb('325_ti_secure_firewall_missing_property.dts',
7191 entry_args=entry_args)[0]
7192 self.assertRegex(str(e.exception), "Node '/binman/ti-secure': Subnode 'firewall-0-2' is missing properties: id,region")
7193
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307194 def testPackTiSecureMissingTool(self):
7195 """Test that an image with a TI secured binary (non-functional) can be created
7196 when openssl is missing"""
7197 keyfile = self.TestFile('key.key')
7198 entry_args = {
7199 'keyfile': keyfile,
7200 }
Simon Glass14d64e32025-04-29 07:21:59 -06007201 with terminal.capture() as (_, stderr):
Simon Glassf1264ba2023-07-24 09:19:59 -06007202 self._DoTestFile('296_ti_secure.dts',
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307203 force_missing_bintools='openssl',
7204 entry_args=entry_args)
7205 err = stderr.getvalue()
7206 self.assertRegex(err, "Image 'image'.*missing bintools.*: openssl")
7207
7208 def testPackTiSecureROM(self):
7209 """Test that a ROM image with a TI secured binary can be created"""
7210 keyfile = self.TestFile('key.key')
7211 entry_args = {
7212 'keyfile': keyfile,
7213 }
Simon Glassf1264ba2023-07-24 09:19:59 -06007214 data = self._DoReadFileDtb('297_ti_secure_rom.dts',
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307215 entry_args=entry_args)[0]
Simon Glassf1264ba2023-07-24 09:19:59 -06007216 data_a = self._DoReadFileDtb('299_ti_secure_rom_a.dts',
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307217 entry_args=entry_args)[0]
Simon Glassf1264ba2023-07-24 09:19:59 -06007218 data_b = self._DoReadFileDtb('300_ti_secure_rom_b.dts',
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307219 entry_args=entry_args)[0]
7220 self.assertGreater(len(data), len(TI_UNSECURE_DATA))
7221 self.assertGreater(len(data_a), len(TI_UNSECURE_DATA))
7222 self.assertGreater(len(data_b), len(TI_UNSECURE_DATA))
7223
7224 def testPackTiSecureROMCombined(self):
7225 """Test that a ROM image with a TI secured binary can be created"""
7226 keyfile = self.TestFile('key.key')
7227 entry_args = {
7228 'keyfile': keyfile,
7229 }
Simon Glassf1264ba2023-07-24 09:19:59 -06007230 data = self._DoReadFileDtb('298_ti_secure_rom_combined.dts',
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307231 entry_args=entry_args)[0]
7232 self.assertGreater(len(data), len(TI_UNSECURE_DATA))
7233
Christian Taedcke62ac29a2023-07-17 09:05:54 +02007234 def testEncryptedNoAlgo(self):
7235 """Test encrypted node with missing required properties"""
7236 with self.assertRaises(ValueError) as e:
7237 self._DoReadFileDtb('301_encrypted_no_algo.dts')
7238 self.assertIn(
7239 "Node '/binman/fit/images/u-boot/encrypted': 'encrypted' entry is missing properties: algo iv-filename",
7240 str(e.exception))
7241
7242 def testEncryptedInvalidIvfile(self):
7243 """Test encrypted node with invalid iv file"""
7244 with self.assertRaises(ValueError) as e:
7245 self._DoReadFileDtb('302_encrypted_invalid_iv_file.dts')
7246 self.assertIn("Filename 'invalid-iv-file' not found in input path",
7247 str(e.exception))
7248
7249 def testEncryptedMissingKey(self):
7250 """Test encrypted node with missing key properties"""
7251 with self.assertRaises(ValueError) as e:
7252 self._DoReadFileDtb('303_encrypted_missing_key.dts')
7253 self.assertIn(
7254 "Node '/binman/fit/images/u-boot/encrypted': Provide either 'key-filename' or 'key-source'",
7255 str(e.exception))
7256
7257 def testEncryptedKeySource(self):
7258 """Test encrypted node with key-source property"""
7259 data = self._DoReadFileDtb('304_encrypted_key_source.dts')[0]
7260
7261 dtb = fdt.Fdt.FromData(data)
7262 dtb.Scan()
7263
7264 node = dtb.GetNode('/images/u-boot/cipher')
7265 self.assertEqual('algo-name', node.props['algo'].value)
7266 self.assertEqual('key-source-value', node.props['key-source'].value)
7267 self.assertEqual(ENCRYPTED_IV_DATA,
7268 tools.to_bytes(''.join(node.props['iv'].value)))
7269 self.assertNotIn('key', node.props)
7270
7271 def testEncryptedKeyFile(self):
7272 """Test encrypted node with key-filename property"""
7273 data = self._DoReadFileDtb('305_encrypted_key_file.dts')[0]
7274
7275 dtb = fdt.Fdt.FromData(data)
7276 dtb.Scan()
7277
7278 node = dtb.GetNode('/images/u-boot/cipher')
7279 self.assertEqual('algo-name', node.props['algo'].value)
7280 self.assertEqual(ENCRYPTED_IV_DATA,
7281 tools.to_bytes(''.join(node.props['iv'].value)))
7282 self.assertEqual(ENCRYPTED_KEY_DATA,
7283 tools.to_bytes(''.join(node.props['key'].value)))
7284 self.assertNotIn('key-source', node.props)
7285
Lukas Funkee901faf2023-07-18 13:53:13 +02007286
7287 def testSplPubkeyDtb(self):
Simon Glass4b861272024-07-20 11:49:41 +01007288 """Test u_boot_spl_pubkey_dtb etype"""
7289 data = tools.read_file(self.TestFile("key.pem"))
7290 self._MakeInputFile("key.crt", data)
7291 self._DoReadFileRealDtb('306_spl_pubkey_dtb.dts')
7292 image = control.images['image']
7293 entries = image.GetEntries()
7294 dtb_entry = entries['u-boot-spl-pubkey-dtb']
7295 dtb_data = dtb_entry.GetData()
7296 dtb = fdt.Fdt.FromData(dtb_data)
7297 dtb.Scan()
Lukas Funkee901faf2023-07-18 13:53:13 +02007298
Simon Glass4b861272024-07-20 11:49:41 +01007299 signature_node = dtb.GetNode('/signature')
7300 self.assertIsNotNone(signature_node)
7301 key_node = signature_node.FindNode("key-key")
7302 self.assertIsNotNone(key_node)
7303 self.assertEqual(fdt_util.GetString(key_node, "required"), "conf")
7304 self.assertEqual(fdt_util.GetString(key_node, "algo"), "sha384,rsa4096")
7305 self.assertEqual(fdt_util.GetString(key_node, "key-name-hint"), "key")
Christian Taedcke62ac29a2023-07-17 09:05:54 +02007306
Lukas Funke712e1062023-08-03 17:22:14 +02007307 def testXilinxBootgenSigning(self):
7308 """Test xilinx-bootgen etype"""
7309 bootgen = bintool.Bintool.create('bootgen')
7310 self._CheckBintool(bootgen)
7311 data = tools.read_file(self.TestFile("key.key"))
7312 self._MakeInputFile("psk.pem", data)
7313 self._MakeInputFile("ssk.pem", data)
7314 self._SetupPmuFwlElf()
7315 self._SetupSplElf()
7316 self._DoReadFileRealDtb('307_xilinx_bootgen_sign.dts')
7317 image_fname = tools.get_output_filename('image.bin')
7318
7319 # Read partition header table and check if authentication is enabled
7320 bootgen_out = bootgen.run_cmd("-arch", "zynqmp",
7321 "-read", image_fname, "pht").splitlines()
7322 attributes = {"authentication": None,
7323 "core": None,
7324 "encryption": None}
7325
7326 for l in bootgen_out:
7327 for a in attributes.keys():
7328 if a in l:
7329 m = re.match(fr".*{a} \[([^]]+)\]", l)
7330 attributes[a] = m.group(1)
7331
7332 self.assertTrue(attributes['authentication'] == "rsa")
7333 self.assertTrue(attributes['core'] == "a53-0")
7334 self.assertTrue(attributes['encryption'] == "no")
7335
7336 def testXilinxBootgenSigningEncryption(self):
7337 """Test xilinx-bootgen etype"""
7338 bootgen = bintool.Bintool.create('bootgen')
7339 self._CheckBintool(bootgen)
7340 data = tools.read_file(self.TestFile("key.key"))
7341 self._MakeInputFile("psk.pem", data)
7342 self._MakeInputFile("ssk.pem", data)
7343 self._SetupPmuFwlElf()
7344 self._SetupSplElf()
7345 self._DoReadFileRealDtb('308_xilinx_bootgen_sign_enc.dts')
7346 image_fname = tools.get_output_filename('image.bin')
7347
7348 # Read boot header in order to verify encryption source and
7349 # encryption parameter
7350 bootgen_out = bootgen.run_cmd("-arch", "zynqmp",
7351 "-read", image_fname, "bh").splitlines()
7352 attributes = {"auth_only":
7353 {"re": r".*auth_only \[([^]]+)\]", "value": None},
7354 "encryption_keystore":
7355 {"re": r" *encryption_keystore \(0x28\) : (.*)",
7356 "value": None},
7357 }
7358
7359 for l in bootgen_out:
7360 for a in attributes.keys():
7361 if a in l:
7362 m = re.match(attributes[a]['re'], l)
7363 attributes[a] = m.group(1)
7364
7365 # Check if fsbl-attribute is set correctly
7366 self.assertTrue(attributes['auth_only'] == "true")
7367 # Check if key is stored in efuse
7368 self.assertTrue(attributes['encryption_keystore'] == "0xa5c3c5a3")
7369
7370 def testXilinxBootgenMissing(self):
7371 """Test that binman still produces an image if bootgen is missing"""
7372 data = tools.read_file(self.TestFile("key.key"))
7373 self._MakeInputFile("psk.pem", data)
7374 self._MakeInputFile("ssk.pem", data)
7375 self._SetupPmuFwlElf()
7376 self._SetupSplElf()
Simon Glass14d64e32025-04-29 07:21:59 -06007377 with terminal.capture() as (_, stderr):
Lukas Funke712e1062023-08-03 17:22:14 +02007378 self._DoTestFile('307_xilinx_bootgen_sign.dts',
7379 force_missing_bintools='bootgen')
7380 err = stderr.getvalue()
7381 self.assertRegex(err,
7382 "Image 'image'.*missing bintools.*: bootgen")
7383
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +05307384 def _GetCapsuleHeaders(self, data):
7385 """Get the capsule header contents
7386
7387 Args:
7388 data: Capsule file contents
7389
7390 Returns:
7391 Dict:
7392 key: Capsule Header name (str)
7393 value: Header field value (str)
7394 """
7395 capsule_file = os.path.join(self._indir, 'test.capsule')
7396 tools.write_file(capsule_file, data)
7397
7398 out = tools.run('mkeficapsule', '--dump-capsule', capsule_file)
7399 lines = out.splitlines()
7400
7401 re_line = re.compile(r'^([^:\-\t]*)(?:\t*\s*:\s*(.*))?$')
7402 vals = {}
7403 for line in lines:
7404 mat = re_line.match(line)
7405 if mat:
7406 vals[mat.group(1)] = mat.group(2)
7407
7408 return vals
7409
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307410 def _CheckCapsule(self, data, signed_capsule=False, version_check=False,
7411 capoemflags=False):
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +05307412 fmp_signature = "3153534D" # 'M', 'S', 'S', '1'
7413 fmp_size = "00000010"
7414 fmp_fw_version = "00000002"
7415 capsule_image_index = "00000001"
7416 oemflag = "00018000"
7417 auth_hdr_revision = "00000200"
7418 auth_hdr_cert_type = "00000EF1"
7419
7420 payload_data_len = len(EFI_CAPSULE_DATA)
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307421
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +05307422 hdr = self._GetCapsuleHeaders(data)
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307423
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +05307424 self.assertEqual(FW_MGMT_GUID.upper(), hdr['EFI_CAPSULE_HDR.CAPSULE_GUID'])
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307425
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +05307426 self.assertEqual(CAPSULE_IMAGE_GUID.upper(),
7427 hdr['FMP_CAPSULE_IMAGE_HDR.UPDATE_IMAGE_TYPE_ID'])
7428 self.assertEqual(capsule_image_index,
7429 hdr['FMP_CAPSULE_IMAGE_HDR.UPDATE_IMAGE_INDEX'])
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307430
7431 if capoemflags:
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +05307432 self.assertEqual(oemflag, hdr['EFI_CAPSULE_HDR.FLAGS'])
7433
7434 if signed_capsule:
7435 self.assertEqual(auth_hdr_revision,
7436 hdr['EFI_FIRMWARE_IMAGE_AUTH.AUTH_INFO.HDR.wREVISION'])
7437 self.assertEqual(auth_hdr_cert_type,
7438 hdr['EFI_FIRMWARE_IMAGE_AUTH.AUTH_INFO.HDR.wCERTTYPE'])
7439 self.assertEqual(WIN_CERT_TYPE_EFI_GUID.upper(),
7440 hdr['EFI_FIRMWARE_IMAGE_AUTH.AUTH_INFO.CERT_TYPE'])
7441
7442 if version_check:
7443 self.assertEqual(fmp_signature,
7444 hdr['FMP_PAYLOAD_HDR.SIGNATURE'])
7445 self.assertEqual(fmp_size,
7446 hdr['FMP_PAYLOAD_HDR.HEADER_SIZE'])
7447 self.assertEqual(fmp_fw_version,
7448 hdr['FMP_PAYLOAD_HDR.FW_VERSION'])
7449
7450 self.assertEqual(payload_data_len, int(hdr['Payload Image Size']))
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307451
Sughosh Ganu6b2d18a2023-10-10 14:40:59 +05307452 def _CheckEmptyCapsule(self, data, accept_capsule=False):
7453 if accept_capsule:
7454 capsule_hdr_guid = EMPTY_CAPSULE_ACCEPT_GUID
7455 else:
7456 capsule_hdr_guid = EMPTY_CAPSULE_REVERT_GUID
7457
7458 hdr = self._GetCapsuleHeaders(data)
7459
7460 self.assertEqual(capsule_hdr_guid.upper(),
7461 hdr['EFI_CAPSULE_HDR.CAPSULE_GUID'])
7462
7463 if accept_capsule:
7464 capsule_size = "0000002C"
7465 else:
7466 capsule_size = "0000001C"
7467 self.assertEqual(capsule_size,
7468 hdr['EFI_CAPSULE_HDR.CAPSULE_IMAGE_SIZE'])
7469
7470 if accept_capsule:
7471 self.assertEqual(CAPSULE_IMAGE_GUID.upper(), hdr['ACCEPT_IMAGE_GUID'])
7472
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307473 def testCapsuleGen(self):
7474 """Test generation of EFI capsule"""
7475 data = self._DoReadFile('311_capsule.dts')
7476
7477 self._CheckCapsule(data)
7478
7479 def testSignedCapsuleGen(self):
7480 """Test generation of EFI capsule"""
7481 data = tools.read_file(self.TestFile("key.key"))
7482 self._MakeInputFile("key.key", data)
7483 data = tools.read_file(self.TestFile("key.pem"))
7484 self._MakeInputFile("key.crt", data)
7485
7486 data = self._DoReadFile('312_capsule_signed.dts')
7487
7488 self._CheckCapsule(data, signed_capsule=True)
7489
7490 def testCapsuleGenVersionSupport(self):
7491 """Test generation of EFI capsule with version support"""
7492 data = self._DoReadFile('313_capsule_version.dts')
7493
7494 self._CheckCapsule(data, version_check=True)
7495
7496 def testCapsuleGenSignedVer(self):
7497 """Test generation of signed EFI capsule with version information"""
7498 data = tools.read_file(self.TestFile("key.key"))
7499 self._MakeInputFile("key.key", data)
7500 data = tools.read_file(self.TestFile("key.pem"))
7501 self._MakeInputFile("key.crt", data)
7502
7503 data = self._DoReadFile('314_capsule_signed_ver.dts')
7504
7505 self._CheckCapsule(data, signed_capsule=True, version_check=True)
7506
7507 def testCapsuleGenCapOemFlags(self):
7508 """Test generation of EFI capsule with OEM Flags set"""
7509 data = self._DoReadFile('315_capsule_oemflags.dts')
7510
7511 self._CheckCapsule(data, capoemflags=True)
7512
7513 def testCapsuleGenKeyMissing(self):
7514 """Test that binman errors out on missing key"""
7515 with self.assertRaises(ValueError) as e:
7516 self._DoReadFile('316_capsule_missing_key.dts')
7517
7518 self.assertIn("Both private key and public key certificate need to be provided",
7519 str(e.exception))
7520
7521 def testCapsuleGenIndexMissing(self):
7522 """Test that binman errors out on missing image index"""
7523 with self.assertRaises(ValueError) as e:
7524 self._DoReadFile('317_capsule_missing_index.dts')
7525
7526 self.assertIn("entry is missing properties: image-index",
7527 str(e.exception))
7528
7529 def testCapsuleGenGuidMissing(self):
7530 """Test that binman errors out on missing image GUID"""
7531 with self.assertRaises(ValueError) as e:
7532 self._DoReadFile('318_capsule_missing_guid.dts')
7533
7534 self.assertIn("entry is missing properties: image-guid",
7535 str(e.exception))
7536
Sughosh Ganu6b2d18a2023-10-10 14:40:59 +05307537 def testCapsuleGenAcceptCapsule(self):
7538 """Test generationg of accept EFI capsule"""
7539 data = self._DoReadFile('319_capsule_accept.dts')
7540
7541 self._CheckEmptyCapsule(data, accept_capsule=True)
7542
7543 def testCapsuleGenRevertCapsule(self):
7544 """Test generationg of revert EFI capsule"""
7545 data = self._DoReadFile('320_capsule_revert.dts')
7546
7547 self._CheckEmptyCapsule(data)
7548
7549 def testCapsuleGenAcceptGuidMissing(self):
7550 """Test that binman errors out on missing image GUID for accept capsule"""
7551 with self.assertRaises(ValueError) as e:
7552 self._DoReadFile('321_capsule_accept_missing_guid.dts')
7553
7554 self.assertIn("Image GUID needed for generating accept capsule",
7555 str(e.exception))
7556
7557 def testCapsuleGenEmptyCapsuleTypeMissing(self):
7558 """Test that capsule-type is specified"""
7559 with self.assertRaises(ValueError) as e:
7560 self._DoReadFile('322_empty_capsule_type_missing.dts')
7561
7562 self.assertIn("entry is missing properties: capsule-type",
7563 str(e.exception))
7564
7565 def testCapsuleGenAcceptOrRevertMissing(self):
7566 """Test that both accept and revert capsule are not specified"""
7567 with self.assertRaises(ValueError) as e:
7568 self._DoReadFile('323_capsule_accept_revert_missing.dts')
7569
Simon Glassa360b8f2024-06-23 11:55:06 -06007570 def test_assume_size(self):
7571 """Test handling of the assume-size property for external blob"""
7572 with self.assertRaises(ValueError) as e:
7573 self._DoTestFile('326_assume_size.dts', allow_missing=True,
7574 allow_fake_blobs=True)
7575 self.assertIn("contents size 0xa (10) exceeds section size 0x9 (9)",
7576 str(e.exception))
7577
7578 def test_assume_size_ok(self):
7579 """Test handling of the assume-size where it fits OK"""
Simon Glass14d64e32025-04-29 07:21:59 -06007580 with terminal.capture() as (stdout, stderr):
Simon Glassa360b8f2024-06-23 11:55:06 -06007581 self._DoTestFile('327_assume_size_ok.dts', allow_missing=True,
7582 allow_fake_blobs=True)
7583 err = stderr.getvalue()
7584 self.assertRegex(
7585 err,
7586 "Image '.*' has faked external blobs and is non-functional: .*")
7587
7588 def test_assume_size_no_fake(self):
7589 """Test handling of the assume-size where it fits OK"""
Simon Glass14d64e32025-04-29 07:21:59 -06007590 with terminal.capture() as (stdout, stderr):
Simon Glassa360b8f2024-06-23 11:55:06 -06007591 self._DoTestFile('327_assume_size_ok.dts', allow_missing=True)
7592 err = stderr.getvalue()
7593 self.assertRegex(
7594 err,
7595 "Image '.*' is missing external blobs and is non-functional: .*")
7596
Simon Glass5f7aadf2024-07-20 11:49:47 +01007597 def SetupAlternateDts(self):
7598 """Compile the .dts test files for alternative-fdt
7599
7600 Returns:
7601 tuple:
7602 str: Test directory created
7603 list of str: '.bin' files which we expect Binman to create
7604 """
7605 testdir = TestFunctional._MakeInputDir('dtb')
7606 dtb_list = []
7607 for fname in glob.glob(f'{self.TestFile("alt_dts")}/*.dts'):
7608 tmp_fname = fdt_util.EnsureCompiled(fname, testdir)
7609 base = os.path.splitext(os.path.basename(fname))[0]
7610 dtb_list.append(base + '.bin')
7611 shutil.move(tmp_fname, os.path.join(testdir, base + '.dtb'))
7612
7613 return testdir, dtb_list
7614
Simon Glassf3598922024-07-20 11:49:45 +01007615 def CheckAlternates(self, dts, phase, xpl_data):
7616 """Run the test for the alterative-fdt etype
7617
7618 Args:
7619 dts (str): Devicetree file to process
7620 phase (str): Phase to process ('spl', 'tpl' or 'vpl')
7621 xpl_data (bytes): Expected data for the phase's binary
7622
7623 Returns:
7624 dict of .dtb files produced
7625 key: str filename
7626 value: Fdt object
7627 """
Simon Glass5f7aadf2024-07-20 11:49:47 +01007628 dtb_list = self.SetupAlternateDts()[1]
Simon Glassf3598922024-07-20 11:49:45 +01007629
7630 entry_args = {
7631 f'{phase}-dtb': '1',
7632 f'{phase}-bss-pad': 'y',
7633 'of-spl-remove-props': 'prop-to-remove another-prop-to-get-rid-of',
7634 }
7635 data = self._DoReadFileDtb(dts, use_real_dtb=True, update_dtb=True,
7636 use_expanded=True, entry_args=entry_args)[0]
7637 self.assertEqual(xpl_data, data[:len(xpl_data)])
7638 rest = data[len(xpl_data):]
7639 pad_len = 10
7640 self.assertEqual(tools.get_bytes(0, pad_len), rest[:pad_len])
7641
7642 # Check the dtb is using the test file
7643 dtb_data = rest[pad_len:]
7644 dtb = fdt.Fdt.FromData(dtb_data)
7645 dtb.Scan()
7646 fdt_size = dtb.GetFdtObj().totalsize()
7647 self.assertEqual('model-not-set',
7648 fdt_util.GetString(dtb.GetRoot(), 'compatible'))
7649
7650 pad_len = 10
7651
7652 # Check the other output files
7653 dtbs = {}
7654 for fname in dtb_list:
7655 pathname = tools.get_output_filename(fname)
7656 self.assertTrue(os.path.exists(pathname))
7657
7658 data = tools.read_file(pathname)
7659 self.assertEqual(xpl_data, data[:len(xpl_data)])
7660 rest = data[len(xpl_data):]
7661
7662 self.assertEqual(tools.get_bytes(0, pad_len), rest[:pad_len])
7663 rest = rest[pad_len:]
7664
7665 dtb = fdt.Fdt.FromData(rest)
7666 dtb.Scan()
7667 dtbs[fname] = dtb
7668
7669 expected = 'one' if '1' in fname else 'two'
7670 self.assertEqual(f'u-boot,model-{expected}',
7671 fdt_util.GetString(dtb.GetRoot(), 'compatible'))
7672
7673 # Make sure the FDT is the same size as the 'main' one
7674 rest = rest[fdt_size:]
7675
7676 self.assertEqual(b'', rest)
7677 return dtbs
7678
7679 def testAlternatesFdt(self):
7680 """Test handling of alternates-fdt etype"""
7681 self._SetupTplElf()
7682 dtbs = self.CheckAlternates('328_alternates_fdt.dts', 'tpl',
7683 U_BOOT_TPL_NODTB_DATA)
7684 for dtb in dtbs.values():
7685 # Check for the node with the tag
7686 node = dtb.GetNode('/node')
7687 self.assertIsNotNone(node)
7688 self.assertEqual(5, len(node.props.keys()))
7689
7690 # Make sure the other node is still there
7691 self.assertIsNotNone(dtb.GetNode('/node/other-node'))
7692
7693 def testAlternatesFdtgrep(self):
7694 """Test handling of alternates-fdt etype using fdtgrep"""
7695 self._SetupTplElf()
7696 dtbs = self.CheckAlternates('329_alternates_fdtgrep.dts', 'tpl',
7697 U_BOOT_TPL_NODTB_DATA)
7698 for dtb in dtbs.values():
7699 # Check for the node with the tag
7700 node = dtb.GetNode('/node')
7701 self.assertIsNotNone(node)
7702 self.assertEqual({'some-prop', 'not-a-prop-to-remove'},
7703 node.props.keys())
7704
7705 # Make sure the other node is gone
7706 self.assertIsNone(dtb.GetNode('/node/other-node'))
7707
7708 def testAlternatesFdtgrepVpl(self):
7709 """Test handling of alternates-fdt etype using fdtgrep with vpl"""
7710 self._SetupVplElf()
7711 dtbs = self.CheckAlternates('330_alternates_vpl.dts', 'vpl',
7712 U_BOOT_VPL_NODTB_DATA)
7713
7714 def testAlternatesFdtgrepSpl(self):
7715 """Test handling of alternates-fdt etype using fdtgrep with spl"""
7716 self._SetupSplElf()
7717 dtbs = self.CheckAlternates('331_alternates_spl.dts', 'spl',
7718 U_BOOT_SPL_NODTB_DATA)
7719
7720 def testAlternatesFdtgrepInval(self):
7721 """Test alternates-fdt etype using fdtgrep with invalid phase"""
7722 self._SetupSplElf()
7723 with self.assertRaises(ValueError) as e:
7724 dtbs = self.CheckAlternates('332_alternates_inval.dts', 'spl',
7725 U_BOOT_SPL_NODTB_DATA)
7726 self.assertIn("Invalid U-Boot phase 'bad-phase': Use tpl/vpl/spl",
7727 str(e.exception))
7728
Simon Glasscd2783e2024-07-20 11:49:46 +01007729 def testFitFdtListDir(self):
7730 """Test an image with an FIT with FDT images using fit,fdt-list-dir"""
Simon Glass1bba8942024-08-26 13:11:34 -06007731 old_dir = os.getcwd()
7732 try:
7733 os.chdir(self._indir)
7734 self.CheckFitFdt('333_fit_fdt_dir.dts', False)
7735 finally:
7736 os.chdir(old_dir)
Simon Glasscd2783e2024-07-20 11:49:46 +01007737
Simon Glassd2a9d6e2024-08-26 13:11:37 -06007738 def testFitFdtListDirDefault(self):
7739 """Test an FIT fit,fdt-list-dir where the default DT in is a subdir"""
7740 old_dir = os.getcwd()
7741 try:
7742 os.chdir(self._indir)
7743 self.CheckFitFdt('333_fit_fdt_dir.dts', False,
7744 default_dt='rockchip/test-fdt2')
7745 finally:
7746 os.chdir(old_dir)
7747
Simon Glass5f7aadf2024-07-20 11:49:47 +01007748 def testFitFdtCompat(self):
7749 """Test an image with an FIT with compatible in the config nodes"""
7750 entry_args = {
7751 'of-list': 'model1 model2',
7752 'default-dt': 'model2',
7753 }
7754 testdir, dtb_list = self.SetupAlternateDts()
7755 data = self._DoReadFileDtb(
7756 '334_fit_fdt_compat.dts', use_real_dtb=True, update_dtb=True,
7757 entry_args=entry_args, extra_indirs=[testdir])[0]
7758
7759 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
7760
7761 fit = fdt.Fdt.FromData(fit_data)
7762 fit.Scan()
7763
7764 cnode = fit.GetNode('/configurations')
7765 self.assertIn('default', cnode.props)
7766 self.assertEqual('config-2', cnode.props['default'].value)
7767
7768 for seq in range(1, 2):
7769 name = f'config-{seq}'
7770 fnode = fit.GetNode('/configurations/%s' % name)
7771 self.assertIsNotNone(fnode)
7772 self.assertIn('compatible', fnode.props.keys())
7773 expected = 'one' if seq == 1 else 'two'
7774 self.assertEqual(f'u-boot,model-{expected}',
7775 fnode.props['compatible'].value)
7776
Simon Glassa04b9942024-07-20 11:49:48 +01007777 def testFitFdtPhase(self):
7778 """Test an image with an FIT with fdt-phase in the fdt nodes"""
7779 phase = 'tpl'
7780 entry_args = {
7781 f'{phase}-dtb': '1',
7782 f'{phase}-bss-pad': 'y',
7783 'of-spl-remove-props': 'prop-to-remove another-prop-to-get-rid-of',
7784 'of-list': 'model1 model2',
7785 'default-dt': 'model2',
7786 }
7787 testdir, dtb_list = self.SetupAlternateDts()
7788 data = self._DoReadFileDtb(
7789 '335_fit_fdt_phase.dts', use_real_dtb=True, update_dtb=True,
7790 entry_args=entry_args, extra_indirs=[testdir])[0]
7791 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
7792 fit = fdt.Fdt.FromData(fit_data)
7793 fit.Scan()
7794
7795 # Check that each FDT has only the expected properties for the phase
7796 for seq in range(1, 2):
7797 fnode = fit.GetNode(f'/images/fdt-{seq}')
7798 self.assertIsNotNone(fnode)
7799 dtb = fdt.Fdt.FromData(fnode.props['data'].bytes)
7800 dtb.Scan()
7801
7802 # Make sure that the 'bootph-pre-sram' tag in /node protects it from
7803 # removal
7804 node = dtb.GetNode('/node')
7805 self.assertIsNotNone(node)
7806 self.assertEqual({'some-prop', 'not-a-prop-to-remove'},
7807 node.props.keys())
7808
7809 # Make sure the other node is gone
7810 self.assertIsNone(dtb.GetNode('/node/other-node'))
7811
Simon Glassb553e8a2024-08-26 13:11:29 -06007812 def testMkeficapsuleMissing(self):
7813 """Test that binman complains if mkeficapsule is missing"""
7814 with self.assertRaises(ValueError) as e:
7815 self._DoTestFile('311_capsule.dts',
7816 force_missing_bintools='mkeficapsule')
7817 self.assertIn("Node '/binman/efi-capsule': Missing tool: 'mkeficapsule'",
7818 str(e.exception))
7819
7820 def testMkeficapsuleMissingOk(self):
7821 """Test that binman deals with mkeficapsule being missing"""
Simon Glass14d64e32025-04-29 07:21:59 -06007822 with terminal.capture() as (stdout, stderr):
Simon Glassb553e8a2024-08-26 13:11:29 -06007823 ret = self._DoTestFile('311_capsule.dts',
7824 force_missing_bintools='mkeficapsule',
7825 allow_missing=True)
7826 self.assertEqual(103, ret)
7827 err = stderr.getvalue()
7828 self.assertRegex(err, "Image 'image'.*missing bintools.*: mkeficapsule")
7829
Simon Glass4b0f4142024-08-26 13:11:40 -06007830 def testSymbolsBase(self):
7831 """Test handling of symbols-base"""
7832 self.checkSymbols('336_symbols_base.dts', U_BOOT_SPL_DATA, 0x1c,
7833 symbols_base=0)
7834
7835 def testSymbolsBaseExpanded(self):
7836 """Test handling of symbols-base with expanded entries"""
7837 entry_args = {
7838 'spl-dtb': '1',
7839 }
7840 self.checkSymbols('337_symbols_base_expand.dts', U_BOOT_SPL_NODTB_DATA +
7841 U_BOOT_SPL_DTB_DATA, 0x38,
7842 entry_args=entry_args, use_expanded=True,
7843 symbols_base=0)
7844
Simon Glass3eb30a42024-08-26 13:11:42 -06007845 def testSymbolsCompressed(self):
7846 """Test binman complains about symbols from a compressed section"""
Simon Glass14d64e32025-04-29 07:21:59 -06007847 with terminal.capture() as (stdout, stderr):
Simon Glass3eb30a42024-08-26 13:11:42 -06007848 self.checkSymbols('338_symbols_comp.dts', U_BOOT_SPL_DATA, None)
7849 out = stdout.getvalue()
7850 self.assertIn('Symbol-writing: no value for /binman/section/u-boot',
7851 out)
7852
Simon Glass9c25ef22024-08-26 13:11:43 -06007853 def testNxpImx8Image(self):
7854 """Test that binman can produce an iMX8 image"""
7855 self._DoTestFile('339_nxp_imx8.dts')
7856
Alice Guo1d334022025-04-28 18:37:39 +08007857 def testNxpHeaderDdrfw(self):
7858 """Test that binman can add a header to DDR PHY firmware images"""
7859 data = self._DoReadFile('346_nxp_ddrfw_imx95.dts')
7860 self.assertEqual(len(IMX_LPDDR_IMEM_DATA).to_bytes(4, 'little') +
7861 len(IMX_LPDDR_DMEM_DATA).to_bytes(4, 'little') +
7862 IMX_LPDDR_IMEM_DATA + IMX_LPDDR_DMEM_DATA, data)
7863
Alexander Kochetkova730a282024-09-16 11:24:46 +03007864 def testFitSignSimple(self):
7865 """Test that image with FIT and signature nodes can be signed"""
7866 if not elf.ELF_TOOLS:
7867 self.skipTest('Python elftools not available')
7868 entry_args = {
7869 'of-list': 'test-fdt1',
7870 'default-dt': 'test-fdt1',
7871 'atf-bl31-path': 'bl31.elf',
7872 }
7873 data = tools.read_file(self.TestFile("340_rsa2048.key"))
7874 self._MakeInputFile("keys/rsa2048.key", data)
7875
7876 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
7877 keys_subdir = os.path.join(self._indir, "keys")
7878 data = self._DoReadFileDtb(
7879 '340_fit_signature.dts',
7880 entry_args=entry_args,
7881 extra_indirs=[test_subdir, keys_subdir])[0]
7882
7883 dtb = fdt.Fdt.FromData(data)
7884 dtb.Scan()
7885
7886 conf = dtb.GetNode('/configurations/conf-uboot-1')
7887 self.assertIsNotNone(conf)
7888 signature = conf.FindNode('signature')
7889 self.assertIsNotNone(signature)
7890 self.assertIsNotNone(signature.props.get('value'))
7891
7892 images = dtb.GetNode('/images')
7893 self.assertIsNotNone(images)
7894 for subnode in images.subnodes:
7895 signature = subnode.FindNode('signature')
7896 self.assertIsNotNone(signature)
7897 self.assertIsNotNone(signature.props.get('value'))
7898
7899 def testFitSignKeyNotFound(self):
7900 """Test that missing keys raise an error"""
7901 if not elf.ELF_TOOLS:
7902 self.skipTest('Python elftools not available')
7903 entry_args = {
7904 'of-list': 'test-fdt1',
7905 'default-dt': 'test-fdt1',
7906 'atf-bl31-path': 'bl31.elf',
7907 }
7908 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
7909 with self.assertRaises(ValueError) as e:
7910 self._DoReadFileDtb(
7911 '340_fit_signature.dts',
7912 entry_args=entry_args,
7913 extra_indirs=[test_subdir])[0]
7914 self.assertIn(
7915 'Filename \'rsa2048.key\' not found in input path',
7916 str(e.exception))
7917
7918 def testFitSignMultipleKeyPaths(self):
7919 """Test that keys found in multiple paths raise an error"""
7920 if not elf.ELF_TOOLS:
7921 self.skipTest('Python elftools not available')
7922 entry_args = {
7923 'of-list': 'test-fdt1',
7924 'default-dt': 'test-fdt1',
7925 'atf-bl31-path': 'bl31.elf',
7926 }
7927 data = tools.read_file(self.TestFile("340_rsa2048.key"))
7928 self._MakeInputFile("keys1/rsa2048.key", data)
7929 data = tools.read_file(self.TestFile("340_rsa2048.key"))
7930 self._MakeInputFile("keys2/conf-rsa2048.key", data)
7931
7932 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
7933 keys_subdir1 = os.path.join(self._indir, "keys1")
7934 keys_subdir2 = os.path.join(self._indir, "keys2")
7935 with self.assertRaises(ValueError) as e:
7936 self._DoReadFileDtb(
7937 '341_fit_signature.dts',
7938 entry_args=entry_args,
7939 extra_indirs=[test_subdir, keys_subdir1, keys_subdir2])[0]
7940 self.assertIn(
7941 'Node \'/binman/fit\': multiple key paths found',
7942 str(e.exception))
7943
7944 def testFitSignNoSingatureNodes(self):
7945 """Test that fit,sign doens't raise error if no signature nodes found"""
7946 if not elf.ELF_TOOLS:
7947 self.skipTest('Python elftools not available')
7948 entry_args = {
7949 'of-list': 'test-fdt1',
7950 'default-dt': 'test-fdt1',
7951 'atf-bl31-path': 'bl31.elf',
7952 }
7953 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
7954 self._DoReadFileDtb(
7955 '342_fit_signature.dts',
7956 entry_args=entry_args,
7957 extra_indirs=[test_subdir])[0]
7958
Simon Glassa360b8f2024-06-23 11:55:06 -06007959
Paul HENRYSff318462024-11-25 18:47:17 +01007960 def testSimpleFitEncryptedData(self):
7961 """Test an image with a FIT containing data to be encrypted"""
7962 data = tools.read_file(self.TestFile("aes256.bin"))
7963 self._MakeInputFile("keys/aes256.bin", data)
7964
7965 keys_subdir = os.path.join(self._indir, "keys")
7966 data = self._DoReadFileDtb(
7967 '343_fit_encrypt_data.dts',
7968 extra_indirs=[keys_subdir])[0]
7969
7970 fit = fdt.Fdt.FromData(data)
7971 fit.Scan()
7972
7973 # Extract the encrypted data and the Initialization Vector from the FIT
7974 node = fit.GetNode('/images/u-boot')
7975 subnode = fit.GetNode('/images/u-boot/cipher')
7976 data_size_unciphered = int.from_bytes(fit.GetProps(node)['data-size-unciphered'].bytes,
7977 byteorder='big')
7978 self.assertEqual(data_size_unciphered, len(U_BOOT_NODTB_DATA))
7979
7980 # Retrieve the key name from the FIT removing any null byte
7981 key_name = fit.GetProps(subnode)['key-name-hint'].bytes.replace(b'\x00', b'')
7982 with open(self.TestFile(key_name.decode('ascii') + '.bin'), 'rb') as file:
7983 key = file.read()
7984 iv = fit.GetProps(subnode)['iv'].bytes.hex()
7985 enc_data = fit.GetProps(node)['data'].bytes
7986 outdir = tools.get_output_dir()
7987 enc_data_file = os.path.join(outdir, 'encrypted_data.bin')
7988 tools.write_file(enc_data_file, enc_data)
7989 data_file = os.path.join(outdir, 'data.bin')
7990
7991 # Decrypt the encrypted data from the FIT and compare the data
7992 tools.run('openssl', 'enc', '-aes-256-cbc', '-nosalt', '-d', '-in',
7993 enc_data_file, '-out', data_file, '-K', key.hex(), '-iv', iv)
7994 with open(data_file, 'r') as file:
7995 dec_data = file.read()
7996 self.assertEqual(U_BOOT_NODTB_DATA, dec_data.encode('ascii'))
7997
7998 def testSimpleFitEncryptedDataMissingKey(self):
7999 """Test an image with a FIT containing data to be encrypted but with a missing key"""
8000 with self.assertRaises(ValueError) as e:
8001 self._DoReadFile('344_fit_encrypt_data_no_key.dts')
8002
8003 self.assertIn("Filename 'aes256.bin' not found in input path", str(e.exception))
8004
Paul HENRYS1b4bedb2024-11-25 18:54:21 +01008005 def testFitFdtName(self):
8006 """Test an image with an FIT with multiple FDT images using NAME"""
8007 self.CheckFitFdt('345_fit_fdt_name.dts', use_seq_num=False)
8008
Neha Malcom Francisa25b4832025-03-17 10:24:20 +05308009 def testRemoveTemplate(self):
8010 """Test whether template is removed"""
8011 TestFunctional._MakeInputFile('my-blob.bin', b'blob')
8012 TestFunctional._MakeInputFile('my-blob2.bin', b'other')
8013 self._DoTestFile('346_remove_template.dts',
8014 force_missing_bintools='openssl',)
8015
Simon Glassac599912017-11-12 21:52:22 -07008016if __name__ == "__main__":
8017 unittest.main()