blob: b133c76188cfa781a5aa65409fa0593421c36da6 [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
39from u_boot_pylib import test_util
40from u_boot_pylib import tools
41from u_boot_pylib import tout
Simon Glass57454f42016-11-25 20:15:52 -070042
43# Contents of test files, corresponding to different entry types
Simon Glass303f62f2019-05-17 22:00:46 -060044U_BOOT_DATA = b'1234'
45U_BOOT_IMG_DATA = b'img'
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +030046U_BOOT_SPL_DATA = b'56780123456789abcdefghijklm'
47U_BOOT_TPL_DATA = b'tpl9876543210fedcbazywvuts'
Simon Glass56d05412022-02-28 07:16:54 -070048U_BOOT_VPL_DATA = b'vpl76543210fedcbazywxyz_'
Simon Glass303f62f2019-05-17 22:00:46 -060049BLOB_DATA = b'89'
50ME_DATA = b'0abcd'
51VGA_DATA = b'vga'
Sughosh Ganu269ee6d2023-08-22 23:09:59 +053052EFI_CAPSULE_DATA = b'efi'
Simon Glass303f62f2019-05-17 22:00:46 -060053U_BOOT_DTB_DATA = b'udtb'
54U_BOOT_SPL_DTB_DATA = b'spldtb'
55U_BOOT_TPL_DTB_DATA = b'tpldtb'
Simon Glass56d05412022-02-28 07:16:54 -070056U_BOOT_VPL_DTB_DATA = b'vpldtb'
Simon Glass303f62f2019-05-17 22:00:46 -060057X86_START16_DATA = b'start16'
58X86_START16_SPL_DATA = b'start16spl'
59X86_START16_TPL_DATA = b'start16tpl'
Simon Glass0b074d62019-08-24 07:22:48 -060060X86_RESET16_DATA = b'reset16'
61X86_RESET16_SPL_DATA = b'reset16spl'
62X86_RESET16_TPL_DATA = b'reset16tpl'
Simon Glass303f62f2019-05-17 22:00:46 -060063PPC_MPC85XX_BR_DATA = b'ppcmpc85xxbr'
64U_BOOT_NODTB_DATA = b'nodtb with microcode pointer somewhere in here'
65U_BOOT_SPL_NODTB_DATA = b'splnodtb with microcode pointer somewhere in here'
66U_BOOT_TPL_NODTB_DATA = b'tplnodtb with microcode pointer somewhere in here'
Simon Glass56d05412022-02-28 07:16:54 -070067U_BOOT_VPL_NODTB_DATA = b'vplnodtb'
Alper Nebi Yasak09fb0612022-02-08 01:08:04 +030068U_BOOT_EXP_DATA = U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA
69U_BOOT_SPL_EXP_DATA = U_BOOT_SPL_NODTB_DATA + U_BOOT_SPL_DTB_DATA
70U_BOOT_TPL_EXP_DATA = U_BOOT_TPL_NODTB_DATA + U_BOOT_TPL_DTB_DATA
Simon Glass303f62f2019-05-17 22:00:46 -060071FSP_DATA = b'fsp'
72CMC_DATA = b'cmc'
73VBT_DATA = b'vbt'
74MRC_DATA = b'mrc'
Simon Glass2ca52032018-07-17 13:25:33 -060075TEXT_DATA = 'text'
76TEXT_DATA2 = 'text2'
77TEXT_DATA3 = 'text3'
Simon Glass303f62f2019-05-17 22:00:46 -060078CROS_EC_RW_DATA = b'ecrw'
79GBB_DATA = b'gbbd'
80BMPBLK_DATA = b'bmp'
81VBLOCK_DATA = b'vblk'
82FILES_DATA = (b"sorry I'm late\nOh, don't bother apologising, I'm " +
83 b"sorry you're alive\n")
Simon Glassccec0262019-07-08 13:18:42 -060084COMPRESS_DATA = b'compress xxxxxxxxxxxxxxxxxxxxxx data'
Simon Glassd92c8362020-10-26 17:40:25 -060085COMPRESS_DATA_BIG = COMPRESS_DATA * 2
Simon Glass303f62f2019-05-17 22:00:46 -060086REFCODE_DATA = b'refcode'
Simon Glassba7985d2019-08-24 07:23:07 -060087FSP_M_DATA = b'fsp_m'
Simon Glass4d9086d2019-10-20 21:31:35 -060088FSP_S_DATA = b'fsp_s'
Simon Glass9ea87b22019-10-20 21:31:36 -060089FSP_T_DATA = b'fsp_t'
Simon Glass559c4de2020-09-01 05:13:58 -060090ATF_BL31_DATA = b'bl31'
Roger Quadros5cdcea02022-02-19 20:50:04 +020091TEE_OS_DATA = b'this is some tee OS data'
Neha Malcom Francis59be2552023-12-05 15:12:18 +053092TI_DM_DATA = b'tidmtidm'
Simon Glass3efb2972021-11-23 21:08:59 -070093ATF_BL2U_DATA = b'bl2u'
Bin Mengc0b15742021-05-10 20:23:33 +080094OPENSBI_DATA = b'opensbi'
Samuel Holland9d8cc632020-10-21 21:12:15 -050095SCP_DATA = b'scp'
Jonas Karlman35305492023-02-25 19:01:33 +000096ROCKCHIP_TPL_DATA = b'rockchip-tpl'
Simon Glassa435cd12020-09-01 05:13:59 -060097TEST_FDT1_DATA = b'fdt1'
98TEST_FDT2_DATA = b'test-fdt2'
Simon Glassa0729502020-09-06 10:35:33 -060099ENV_DATA = b'var1=1\nvar2="2"'
Christian Taedcke62ac29a2023-07-17 09:05:54 +0200100ENCRYPTED_IV_DATA = b'123456'
101ENCRYPTED_KEY_DATA = b'abcde'
Philippe Reynesebe96cb2022-03-28 22:57:04 +0200102PRE_LOAD_MAGIC = b'UBSH'
103PRE_LOAD_VERSION = 0x11223344.to_bytes(4, 'big')
104PRE_LOAD_HDR_SIZE = 0x00001000.to_bytes(4, 'big')
Neha Malcom Francis3b788942023-07-22 00:14:24 +0530105TI_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 +0530106TI_UNSECURE_DATA = b'unsecuredata'
Simon Glassa435cd12020-09-01 05:13:59 -0600107
108# Subdirectory of the input dir to use to put test FDTs
109TEST_FDT_SUBDIR = 'fdts'
Simon Glassdb168d42018-07-17 13:25:39 -0600110
Simon Glass2c6adba2019-07-20 12:23:47 -0600111# The expected size for the device tree in some tests
Simon Glass4c613bf2019-07-08 14:25:50 -0600112EXTRACT_DTB_SIZE = 0x3c9
113
Simon Glass2c6adba2019-07-20 12:23:47 -0600114# Properties expected to be in the device tree when update_dtb is used
115BASE_DTB_PROPS = ['offset', 'size', 'image-pos']
116
Simon Glassfb30e292019-07-20 12:23:51 -0600117# Extra properties expected to be in the device tree when allow-repack is used
118REPACK_DTB_PROPS = ['orig-offset', 'orig-size']
119
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +0200120# Supported compression bintools
Stefan Herbrechtsmeiera5e4dcb2022-08-19 16:25:38 +0200121COMP_BINTOOLS = ['bzip2', 'gzip', 'lz4', 'lzma_alone', 'lzop', 'xz', 'zstd']
Simon Glass57454f42016-11-25 20:15:52 -0700122
Simon Glassad5cfe12023-01-07 14:07:14 -0700123TEE_ADDR = 0x5678
124
Sughosh Ganu269ee6d2023-08-22 23:09:59 +0530125# Firmware Management Protocol(FMP) GUID
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +0530126FW_MGMT_GUID = '6dcbd5ed-e82d-4c44-bda1-7194199ad92a'
Sughosh Ganu269ee6d2023-08-22 23:09:59 +0530127# Image GUID specified in the DTS
Caleb Connolly4bdc9602024-08-30 13:34:35 +0100128CAPSULE_IMAGE_GUID = '985F2937-7C2E-5E9A-8A5E-8E063312964B'
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +0530129# Windows cert GUID
130WIN_CERT_TYPE_EFI_GUID = '4aafd29d-68df-49ee-8aa9-347d375665a7'
Sughosh Ganu6b2d18a2023-10-10 14:40:59 +0530131# Empty capsule GUIDs
132EMPTY_CAPSULE_ACCEPT_GUID = '0c996046-bcc0-4d04-85ec-e1fcedf1c6f8'
133EMPTY_CAPSULE_REVERT_GUID = 'acd58b4b-c0e8-475f-99b5-6b3f7e07aaf0'
Sughosh Ganu269ee6d2023-08-22 23:09:59 +0530134
Simon Glass57454f42016-11-25 20:15:52 -0700135class TestFunctional(unittest.TestCase):
136 """Functional tests for binman
137
138 Most of these use a sample .dts file to build an image and then check
139 that it looks correct. The sample files are in the test/ subdirectory
140 and are numbered.
141
142 For each entry type a very small test file is created using fixed
143 string contents. This makes it easy to test that things look right, and
144 debug problems.
145
146 In some cases a 'real' file must be used - these are also supplied in
147 the test/ diurectory.
148 """
149 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600150 def setUpClass(cls):
Simon Glassb3393262017-11-12 21:52:20 -0700151 global entry
Simon Glassc585dd42020-04-17 18:09:03 -0600152 from binman import entry
Simon Glassb3393262017-11-12 21:52:20 -0700153
Simon Glass57454f42016-11-25 20:15:52 -0700154 # Handle the case where argv[0] is 'python'
Simon Glass862f8e22019-08-24 07:22:43 -0600155 cls._binman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
156 cls._binman_pathname = os.path.join(cls._binman_dir, 'binman')
Simon Glass57454f42016-11-25 20:15:52 -0700157
158 # Create a temporary directory for input files
Simon Glass862f8e22019-08-24 07:22:43 -0600159 cls._indir = tempfile.mkdtemp(prefix='binmant.')
Simon Glass57454f42016-11-25 20:15:52 -0700160
161 # Create some test files
162 TestFunctional._MakeInputFile('u-boot.bin', U_BOOT_DATA)
163 TestFunctional._MakeInputFile('u-boot.img', U_BOOT_IMG_DATA)
164 TestFunctional._MakeInputFile('spl/u-boot-spl.bin', U_BOOT_SPL_DATA)
Simon Glass8425a1f2018-07-17 13:25:48 -0600165 TestFunctional._MakeInputFile('tpl/u-boot-tpl.bin', U_BOOT_TPL_DATA)
Simon Glass56d05412022-02-28 07:16:54 -0700166 TestFunctional._MakeInputFile('vpl/u-boot-vpl.bin', U_BOOT_VPL_DATA)
Simon Glass57454f42016-11-25 20:15:52 -0700167 TestFunctional._MakeInputFile('blobfile', BLOB_DATA)
Simon Glass72232452016-11-25 20:15:53 -0700168 TestFunctional._MakeInputFile('me.bin', ME_DATA)
169 TestFunctional._MakeInputFile('vga.bin', VGA_DATA)
Simon Glass862f8e22019-08-24 07:22:43 -0600170 cls._ResetDtbs()
Simon Glass0b074d62019-08-24 07:22:48 -0600171
Jagdish Gediya311d4842018-09-03 21:35:08 +0530172 TestFunctional._MakeInputFile('u-boot-br.bin', PPC_MPC85XX_BR_DATA)
Simon Glass0b074d62019-08-24 07:22:48 -0600173
Simon Glassabab18c2019-08-24 07:22:49 -0600174 TestFunctional._MakeInputFile('u-boot-x86-start16.bin', X86_START16_DATA)
175 TestFunctional._MakeInputFile('spl/u-boot-x86-start16-spl.bin',
Simon Glasse83679d2017-11-12 21:52:26 -0700176 X86_START16_SPL_DATA)
Simon Glassabab18c2019-08-24 07:22:49 -0600177 TestFunctional._MakeInputFile('tpl/u-boot-x86-start16-tpl.bin',
Simon Glassed40e962018-09-14 04:57:10 -0600178 X86_START16_TPL_DATA)
Simon Glass0b074d62019-08-24 07:22:48 -0600179
180 TestFunctional._MakeInputFile('u-boot-x86-reset16.bin',
181 X86_RESET16_DATA)
182 TestFunctional._MakeInputFile('spl/u-boot-x86-reset16-spl.bin',
183 X86_RESET16_SPL_DATA)
184 TestFunctional._MakeInputFile('tpl/u-boot-x86-reset16-tpl.bin',
185 X86_RESET16_TPL_DATA)
186
Simon Glass57454f42016-11-25 20:15:52 -0700187 TestFunctional._MakeInputFile('u-boot-nodtb.bin', U_BOOT_NODTB_DATA)
Simon Glass3d274232017-11-12 21:52:27 -0700188 TestFunctional._MakeInputFile('spl/u-boot-spl-nodtb.bin',
189 U_BOOT_SPL_NODTB_DATA)
Simon Glass3fb4f422018-09-14 04:57:32 -0600190 TestFunctional._MakeInputFile('tpl/u-boot-tpl-nodtb.bin',
191 U_BOOT_TPL_NODTB_DATA)
Simon Glass56d05412022-02-28 07:16:54 -0700192 TestFunctional._MakeInputFile('vpl/u-boot-vpl-nodtb.bin',
193 U_BOOT_VPL_NODTB_DATA)
Simon Glassb4176d42016-11-25 20:15:56 -0700194 TestFunctional._MakeInputFile('fsp.bin', FSP_DATA)
195 TestFunctional._MakeInputFile('cmc.bin', CMC_DATA)
Bin Mengd7bcdf52017-08-15 22:41:54 -0700196 TestFunctional._MakeInputFile('vbt.bin', VBT_DATA)
Simon Glassa409c932017-11-12 21:52:28 -0700197 TestFunctional._MakeInputFile('mrc.bin', MRC_DATA)
Simon Glassdb168d42018-07-17 13:25:39 -0600198 TestFunctional._MakeInputFile('ecrw.bin', CROS_EC_RW_DATA)
Simon Glassc1ae83c2018-07-17 13:25:44 -0600199 TestFunctional._MakeInputDir('devkeys')
200 TestFunctional._MakeInputFile('bmpblk.bin', BMPBLK_DATA)
Simon Glass41902e42018-10-01 12:22:31 -0600201 TestFunctional._MakeInputFile('refcode.bin', REFCODE_DATA)
Simon Glassba7985d2019-08-24 07:23:07 -0600202 TestFunctional._MakeInputFile('fsp_m.bin', FSP_M_DATA)
Simon Glass4d9086d2019-10-20 21:31:35 -0600203 TestFunctional._MakeInputFile('fsp_s.bin', FSP_S_DATA)
Simon Glass9ea87b22019-10-20 21:31:36 -0600204 TestFunctional._MakeInputFile('fsp_t.bin', FSP_T_DATA)
Simon Glass57454f42016-11-25 20:15:52 -0700205
Simon Glassf6290892019-08-24 07:22:53 -0600206 cls._elf_testdir = os.path.join(cls._indir, 'elftest')
207 elf_test.BuildElfTestFiles(cls._elf_testdir)
208
Simon Glass72232452016-11-25 20:15:53 -0700209 # ELF file with a '_dt_ucode_base_size' symbol
Simon Glass4affd4b2019-08-24 07:22:54 -0600210 TestFunctional._MakeInputFile('u-boot',
Simon Glass80025522022-01-29 14:14:04 -0700211 tools.read_file(cls.ElfTestFile('u_boot_ucode_ptr')))
Simon Glass72232452016-11-25 20:15:53 -0700212
213 # Intel flash descriptor file
Simon Glasse88cef92020-07-09 18:39:41 -0600214 cls._SetupDescriptor()
Simon Glass72232452016-11-25 20:15:53 -0700215
Simon Glass862f8e22019-08-24 07:22:43 -0600216 shutil.copytree(cls.TestFile('files'),
217 os.path.join(cls._indir, 'files'))
Simon Glassac6328c2018-09-14 04:57:28 -0600218
Neha Malcom Francis3b788942023-07-22 00:14:24 +0530219 shutil.copytree(cls.TestFile('yaml'),
220 os.path.join(cls._indir, 'yaml'))
221
Simon Glass7ba33592018-09-14 04:57:26 -0600222 TestFunctional._MakeInputFile('compress', COMPRESS_DATA)
Simon Glassd92c8362020-10-26 17:40:25 -0600223 TestFunctional._MakeInputFile('compress_big', COMPRESS_DATA_BIG)
Simon Glass559c4de2020-09-01 05:13:58 -0600224 TestFunctional._MakeInputFile('bl31.bin', ATF_BL31_DATA)
Roger Quadros5cdcea02022-02-19 20:50:04 +0200225 TestFunctional._MakeInputFile('tee-pager.bin', TEE_OS_DATA)
Neha Malcom Francis59be2552023-12-05 15:12:18 +0530226 TestFunctional._MakeInputFile('dm.bin', TI_DM_DATA)
Simon Glass3efb2972021-11-23 21:08:59 -0700227 TestFunctional._MakeInputFile('bl2u.bin', ATF_BL2U_DATA)
Bin Mengc0b15742021-05-10 20:23:33 +0800228 TestFunctional._MakeInputFile('fw_dynamic.bin', OPENSBI_DATA)
Samuel Holland9d8cc632020-10-21 21:12:15 -0500229 TestFunctional._MakeInputFile('scp.bin', SCP_DATA)
Jonas Karlman35305492023-02-25 19:01:33 +0000230 TestFunctional._MakeInputFile('rockchip-tpl.bin', ROCKCHIP_TPL_DATA)
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +0530231 TestFunctional._MakeInputFile('ti_unsecure.bin', TI_UNSECURE_DATA)
Sughosh Ganu269ee6d2023-08-22 23:09:59 +0530232 TestFunctional._MakeInputFile('capsule_input.bin', EFI_CAPSULE_DATA)
Simon Glass7ba33592018-09-14 04:57:26 -0600233
Simon Glassa435cd12020-09-01 05:13:59 -0600234 # Add a few .dtb files for testing
235 TestFunctional._MakeInputFile('%s/test-fdt1.dtb' % TEST_FDT_SUBDIR,
236 TEST_FDT1_DATA)
237 TestFunctional._MakeInputFile('%s/test-fdt2.dtb' % TEST_FDT_SUBDIR,
238 TEST_FDT2_DATA)
239
Simon Glassa0729502020-09-06 10:35:33 -0600240 TestFunctional._MakeInputFile('env.txt', ENV_DATA)
241
Simon Glass5f423422022-03-05 20:19:12 -0700242 # ELF file with two sections in different parts of memory, used for both
243 # ATF and OP_TEE
244 TestFunctional._MakeInputFile('bl31.elf',
245 tools.read_file(cls.ElfTestFile('elf_sections')))
246 TestFunctional._MakeInputFile('tee.elf',
247 tools.read_file(cls.ElfTestFile('elf_sections')))
248
Simon Glassad5cfe12023-01-07 14:07:14 -0700249 # Newer OP_TEE file in v1 binary format
250 cls.make_tee_bin('tee.bin')
251
Christian Taedcke62ac29a2023-07-17 09:05:54 +0200252 # test files for encrypted tests
253 TestFunctional._MakeInputFile('encrypted-file.iv', ENCRYPTED_IV_DATA)
254 TestFunctional._MakeInputFile('encrypted-file.key', ENCRYPTED_KEY_DATA)
255
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +0200256 cls.comp_bintools = {}
257 for name in COMP_BINTOOLS:
258 cls.comp_bintools[name] = bintool.Bintool.create(name)
Simon Glass1de34482019-07-08 13:18:53 -0600259
Simon Glass57454f42016-11-25 20:15:52 -0700260 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600261 def tearDownClass(cls):
Simon Glass57454f42016-11-25 20:15:52 -0700262 """Remove the temporary input directory and its contents"""
Simon Glass862f8e22019-08-24 07:22:43 -0600263 if cls.preserve_indir:
264 print('Preserving input dir: %s' % cls._indir)
Simon Glass1c420c92019-07-08 13:18:49 -0600265 else:
Simon Glass862f8e22019-08-24 07:22:43 -0600266 if cls._indir:
267 shutil.rmtree(cls._indir)
268 cls._indir = None
Simon Glass57454f42016-11-25 20:15:52 -0700269
Simon Glass1c420c92019-07-08 13:18:49 -0600270 @classmethod
Simon Glasscebfab22019-07-08 13:18:50 -0600271 def setup_test_args(cls, preserve_indir=False, preserve_outdirs=False,
Simon Glassf46732a2019-07-08 14:25:29 -0600272 toolpath=None, verbosity=None):
Simon Glass1c420c92019-07-08 13:18:49 -0600273 """Accept arguments controlling test execution
274
275 Args:
276 preserve_indir: Preserve the shared input directory used by all
277 tests in this class.
278 preserve_outdir: Preserve the output directories used by tests. Each
279 test has its own, so this is normally only useful when running a
280 single test.
Simon Glasscebfab22019-07-08 13:18:50 -0600281 toolpath: ist of paths to use for tools
Simon Glass1c420c92019-07-08 13:18:49 -0600282 """
283 cls.preserve_indir = preserve_indir
284 cls.preserve_outdirs = preserve_outdirs
Simon Glasscebfab22019-07-08 13:18:50 -0600285 cls.toolpath = toolpath
Simon Glassf46732a2019-07-08 14:25:29 -0600286 cls.verbosity = verbosity
Simon Glass1c420c92019-07-08 13:18:49 -0600287
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +0200288 def _CheckBintool(self, bintool):
289 if not bintool.is_present():
290 self.skipTest('%s not available' % bintool.name)
291
Simon Glass1de34482019-07-08 13:18:53 -0600292 def _CheckLz4(self):
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +0200293 bintool = self.comp_bintools['lz4']
294 self._CheckBintool(bintool)
Simon Glass1de34482019-07-08 13:18:53 -0600295
Simon Glassee9d10d2019-07-20 12:24:09 -0600296 def _CleanupOutputDir(self):
297 """Remove the temporary output directory"""
298 if self.preserve_outdirs:
299 print('Preserving output dir: %s' % tools.outdir)
300 else:
Simon Glass80025522022-01-29 14:14:04 -0700301 tools._finalise_for_test()
Simon Glassee9d10d2019-07-20 12:24:09 -0600302
Simon Glass57454f42016-11-25 20:15:52 -0700303 def setUp(self):
304 # Enable this to turn on debugging output
Simon Glass011f1b32022-01-29 14:14:15 -0700305 # tout.init(tout.DEBUG)
Simon Glass57454f42016-11-25 20:15:52 -0700306 command.test_result = None
307
308 def tearDown(self):
309 """Remove the temporary output directory"""
Simon Glassee9d10d2019-07-20 12:24:09 -0600310 self._CleanupOutputDir()
Simon Glass57454f42016-11-25 20:15:52 -0700311
Simon Glassb3d6fc72019-07-20 12:24:10 -0600312 def _SetupImageInTmpdir(self):
313 """Set up the output image in a new temporary directory
314
315 This is used when an image has been generated in the output directory,
316 but we want to run binman again. This will create a new output
317 directory and fail to delete the original one.
318
319 This creates a new temporary directory, copies the image to it (with a
320 new name) and removes the old output directory.
321
322 Returns:
323 Tuple:
324 Temporary directory to use
325 New image filename
326 """
Simon Glass80025522022-01-29 14:14:04 -0700327 image_fname = tools.get_output_filename('image.bin')
Simon Glassb3d6fc72019-07-20 12:24:10 -0600328 tmpdir = tempfile.mkdtemp(prefix='binman.')
329 updated_fname = os.path.join(tmpdir, 'image-updated.bin')
Simon Glass80025522022-01-29 14:14:04 -0700330 tools.write_file(updated_fname, tools.read_file(image_fname))
Simon Glassb3d6fc72019-07-20 12:24:10 -0600331 self._CleanupOutputDir()
332 return tmpdir, updated_fname
333
Simon Glass8425a1f2018-07-17 13:25:48 -0600334 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600335 def _ResetDtbs(cls):
Simon Glass8425a1f2018-07-17 13:25:48 -0600336 TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA)
337 TestFunctional._MakeInputFile('spl/u-boot-spl.dtb', U_BOOT_SPL_DTB_DATA)
338 TestFunctional._MakeInputFile('tpl/u-boot-tpl.dtb', U_BOOT_TPL_DTB_DATA)
Simon Glass56d05412022-02-28 07:16:54 -0700339 TestFunctional._MakeInputFile('vpl/u-boot-vpl.dtb', U_BOOT_VPL_DTB_DATA)
Simon Glass8425a1f2018-07-17 13:25:48 -0600340
Simon Glass57454f42016-11-25 20:15:52 -0700341 def _RunBinman(self, *args, **kwargs):
342 """Run binman using the command line
343
344 Args:
345 Arguments to pass, as a list of strings
346 kwargs: Arguments to pass to Command.RunPipe()
347 """
Simon Glass840be732022-01-29 14:14:05 -0700348 result = command.run_pipe([[self._binman_pathname] + list(args)],
Simon Glass57454f42016-11-25 20:15:52 -0700349 capture=True, capture_stderr=True, raise_on_error=False)
350 if result.return_code and kwargs.get('raise_on_error', True):
351 raise Exception("Error running '%s': %s" % (' '.join(args),
352 result.stdout + result.stderr))
353 return result
354
Simon Glassf46732a2019-07-08 14:25:29 -0600355 def _DoBinman(self, *argv):
Simon Glass57454f42016-11-25 20:15:52 -0700356 """Run binman using directly (in the same process)
357
358 Args:
359 Arguments to pass, as a list of strings
360 Returns:
361 Return value (0 for success)
362 """
Simon Glassf46732a2019-07-08 14:25:29 -0600363 argv = list(argv)
364 args = cmdline.ParseArgs(argv)
365 args.pager = 'binman-invalid-pager'
366 args.build_dir = self._indir
Simon Glass57454f42016-11-25 20:15:52 -0700367
368 # For testing, you can force an increase in verbosity here
Simon Glassf46732a2019-07-08 14:25:29 -0600369 # args.verbosity = tout.DEBUG
370 return control.Binman(args)
Simon Glass57454f42016-11-25 20:15:52 -0700371
Simon Glass91710b32018-07-17 13:25:32 -0600372 def _DoTestFile(self, fname, debug=False, map=False, update_dtb=False,
Simon Glassb4595d82019-04-25 21:58:34 -0600373 entry_args=None, images=None, use_real_dtb=False,
Simon Glassed930672021-03-18 20:25:05 +1300374 use_expanded=False, verbosity=None, allow_missing=False,
Heiko Thiery6d451362022-01-06 11:49:41 +0100375 allow_fake_blobs=False, extra_indirs=None, threads=None,
Simon Glass66152ce2022-01-09 20:14:09 -0700376 test_section_timeout=False, update_fdt_in_elf=None,
Andrew Davis6b463da2023-07-22 00:14:44 +0530377 force_missing_bintools='', ignore_missing=False, output_dir=None):
Simon Glass57454f42016-11-25 20:15:52 -0700378 """Run binman with a given test file
379
380 Args:
Simon Glass511f6582018-10-01 12:22:30 -0600381 fname: Device-tree source filename to use (e.g. 005_simple.dts)
Simon Glass1e324002018-06-01 09:38:19 -0600382 debug: True to enable debugging output
Simon Glass30732662018-06-01 09:38:20 -0600383 map: True to output map files for the images
Simon Glasse8561af2018-08-01 15:22:37 -0600384 update_dtb: Update the offset and size of each entry in the device
Simon Glassa87014e2018-07-06 10:27:42 -0600385 tree before packing it into the image
Simon Glass3b376c32018-09-14 04:57:12 -0600386 entry_args: Dict of entry args to supply to binman
387 key: arg name
388 value: value of that arg
389 images: List of image names to build
Simon Glass31ee50f2020-09-01 05:13:55 -0600390 use_real_dtb: True to use the test file as the contents of
391 the u-boot-dtb entry. Normally this is not needed and the
392 test contents (the U_BOOT_DTB_DATA string) can be used.
393 But in some test we need the real contents.
Simon Glassed930672021-03-18 20:25:05 +1300394 use_expanded: True to use expanded entries where available, e.g.
395 'u-boot-expanded' instead of 'u-boot'
Simon Glass31ee50f2020-09-01 05:13:55 -0600396 verbosity: Verbosity level to use (0-3, None=don't set it)
397 allow_missing: Set the '--allow-missing' flag so that missing
398 external binaries just produce a warning instead of an error
Heiko Thiery6d451362022-01-06 11:49:41 +0100399 allow_fake_blobs: Set the '--fake-ext-blobs' flag
Simon Glassa435cd12020-09-01 05:13:59 -0600400 extra_indirs: Extra input directories to add using -I
Simon Glass76f496d2021-07-06 10:36:37 -0600401 threads: Number of threads to use (None for default, 0 for
402 single-threaded)
Simon Glass9a798402021-11-03 21:09:17 -0600403 test_section_timeout: True to force the first time to timeout, as
404 used in testThreadTimeout()
Simon Glassadfb8492021-11-03 21:09:18 -0600405 update_fdt_in_elf: Value to pass with --update-fdt-in-elf=xxx
Simon Glassb553e8a2024-08-26 13:11:29 -0600406 force_missing_bintools (str): comma-separated list of bintools to
Simon Glass66152ce2022-01-09 20:14:09 -0700407 regard as missing
Simon Glassb553e8a2024-08-26 13:11:29 -0600408 ignore_missing (bool): True to return success even if there are
409 missing blobs or bintools
Andrew Davis6b463da2023-07-22 00:14:44 +0530410 output_dir: Specific output directory to use for image using -O
Simon Glass9a798402021-11-03 21:09:17 -0600411
412 Returns:
413 int return code, 0 on success
Simon Glass57454f42016-11-25 20:15:52 -0700414 """
Simon Glassf46732a2019-07-08 14:25:29 -0600415 args = []
Simon Glass075a45c2017-11-13 18:55:00 -0700416 if debug:
417 args.append('-D')
Simon Glassf46732a2019-07-08 14:25:29 -0600418 if verbosity is not None:
419 args.append('-v%d' % verbosity)
420 elif self.verbosity:
421 args.append('-v%d' % self.verbosity)
422 if self.toolpath:
423 for path in self.toolpath:
424 args += ['--toolpath', path]
Simon Glass76f496d2021-07-06 10:36:37 -0600425 if threads is not None:
426 args.append('-T%d' % threads)
427 if test_section_timeout:
428 args.append('--test-section-timeout')
Simon Glassf46732a2019-07-08 14:25:29 -0600429 args += ['build', '-p', '-I', self._indir, '-d', self.TestFile(fname)]
Simon Glass30732662018-06-01 09:38:20 -0600430 if map:
431 args.append('-m')
Simon Glassa87014e2018-07-06 10:27:42 -0600432 if update_dtb:
Simon Glass38a411c2019-07-08 13:18:47 -0600433 args.append('-u')
Simon Glass31402012018-09-14 04:57:23 -0600434 if not use_real_dtb:
435 args.append('--fake-dtb')
Simon Glassed930672021-03-18 20:25:05 +1300436 if not use_expanded:
437 args.append('--no-expanded')
Simon Glass91710b32018-07-17 13:25:32 -0600438 if entry_args:
Simon Glass5f3645b2019-05-14 15:53:41 -0600439 for arg, value in entry_args.items():
Simon Glass91710b32018-07-17 13:25:32 -0600440 args.append('-a%s=%s' % (arg, value))
Simon Glass5d94cc62020-07-09 18:39:38 -0600441 if allow_missing:
442 args.append('-M')
Simon Glass6bce5dc2022-11-09 19:14:42 -0700443 if ignore_missing:
444 args.append('-W')
Heiko Thiery6d451362022-01-06 11:49:41 +0100445 if allow_fake_blobs:
446 args.append('--fake-ext-blobs')
Simon Glass66152ce2022-01-09 20:14:09 -0700447 if force_missing_bintools:
448 args += ['--force-missing-bintools', force_missing_bintools]
Simon Glassadfb8492021-11-03 21:09:18 -0600449 if update_fdt_in_elf:
450 args += ['--update-fdt-in-elf', update_fdt_in_elf]
Simon Glass3b376c32018-09-14 04:57:12 -0600451 if images:
452 for image in images:
453 args += ['-i', image]
Simon Glassa435cd12020-09-01 05:13:59 -0600454 if extra_indirs:
455 for indir in extra_indirs:
456 args += ['-I', indir]
Andrew Davis6b463da2023-07-22 00:14:44 +0530457 if output_dir:
458 args += ['-O', output_dir]
Simon Glass075a45c2017-11-13 18:55:00 -0700459 return self._DoBinman(*args)
Simon Glass57454f42016-11-25 20:15:52 -0700460
461 def _SetupDtb(self, fname, outfile='u-boot.dtb'):
Simon Glass72232452016-11-25 20:15:53 -0700462 """Set up a new test device-tree file
463
464 The given file is compiled and set up as the device tree to be used
465 for ths test.
466
467 Args:
468 fname: Filename of .dts file to read
Simon Glass1e324002018-06-01 09:38:19 -0600469 outfile: Output filename for compiled device-tree binary
Simon Glass72232452016-11-25 20:15:53 -0700470
471 Returns:
Simon Glass1e324002018-06-01 09:38:19 -0600472 Contents of device-tree binary
Simon Glass72232452016-11-25 20:15:53 -0700473 """
Simon Glassb8d2daa2019-07-20 12:23:49 -0600474 tmpdir = tempfile.mkdtemp(prefix='binmant.')
475 dtb = fdt_util.EnsureCompiled(self.TestFile(fname), tmpdir)
Simon Glass33486662019-05-14 15:53:42 -0600476 with open(dtb, 'rb') as fd:
Simon Glass57454f42016-11-25 20:15:52 -0700477 data = fd.read()
478 TestFunctional._MakeInputFile(outfile, data)
Simon Glassb8d2daa2019-07-20 12:23:49 -0600479 shutil.rmtree(tmpdir)
Simon Glass752e7552018-10-01 21:12:41 -0600480 return data
Simon Glass57454f42016-11-25 20:15:52 -0700481
Simon Glass56d05412022-02-28 07:16:54 -0700482 def _GetDtbContentsForSpls(self, dtb_data, name):
483 """Create a version of the main DTB for SPL / TPL / VPL
Simon Glasse219aa42018-09-14 04:57:24 -0600484
485 For testing we don't actually have different versions of the DTB. With
486 U-Boot we normally run fdtgrep to remove unwanted nodes, but for tests
487 we don't normally have any unwanted nodes.
488
489 We still want the DTBs for SPL and TPL to be different though, since
490 otherwise it is confusing to know which one we are looking at. So add
491 an 'spl' or 'tpl' property to the top-level node.
Simon Glass31ee50f2020-09-01 05:13:55 -0600492
493 Args:
494 dtb_data: dtb data to modify (this should be a value devicetree)
495 name: Name of a new property to add
496
497 Returns:
498 New dtb data with the property added
Simon Glasse219aa42018-09-14 04:57:24 -0600499 """
500 dtb = fdt.Fdt.FromData(dtb_data)
501 dtb.Scan()
502 dtb.GetNode('/binman').AddZeroProp(name)
503 dtb.Sync(auto_resize=True)
504 dtb.Pack()
505 return dtb.GetContents()
506
Simon Glassed930672021-03-18 20:25:05 +1300507 def _DoReadFileDtb(self, fname, use_real_dtb=False, use_expanded=False,
508 map=False, update_dtb=False, entry_args=None,
Simon Glass76f496d2021-07-06 10:36:37 -0600509 reset_dtbs=True, extra_indirs=None, threads=None):
Simon Glass57454f42016-11-25 20:15:52 -0700510 """Run binman and return the resulting image
511
512 This runs binman with a given test file and then reads the resulting
513 output file. It is a shortcut function since most tests need to do
514 these steps.
515
516 Raises an assertion failure if binman returns a non-zero exit code.
517
518 Args:
Simon Glass511f6582018-10-01 12:22:30 -0600519 fname: Device-tree source filename to use (e.g. 005_simple.dts)
Simon Glass57454f42016-11-25 20:15:52 -0700520 use_real_dtb: True to use the test file as the contents of
521 the u-boot-dtb entry. Normally this is not needed and the
522 test contents (the U_BOOT_DTB_DATA string) can be used.
523 But in some test we need the real contents.
Simon Glassed930672021-03-18 20:25:05 +1300524 use_expanded: True to use expanded entries where available, e.g.
525 'u-boot-expanded' instead of 'u-boot'
Simon Glass30732662018-06-01 09:38:20 -0600526 map: True to output map files for the images
Simon Glasse8561af2018-08-01 15:22:37 -0600527 update_dtb: Update the offset and size of each entry in the device
Simon Glassa87014e2018-07-06 10:27:42 -0600528 tree before packing it into the image
Simon Glass31ee50f2020-09-01 05:13:55 -0600529 entry_args: Dict of entry args to supply to binman
530 key: arg name
531 value: value of that arg
532 reset_dtbs: With use_real_dtb the test dtb is overwritten by this
533 function. If reset_dtbs is True, then the original test dtb
534 is written back before this function finishes
Simon Glassa435cd12020-09-01 05:13:59 -0600535 extra_indirs: Extra input directories to add using -I
Simon Glass76f496d2021-07-06 10:36:37 -0600536 threads: Number of threads to use (None for default, 0 for
537 single-threaded)
Simon Glass72232452016-11-25 20:15:53 -0700538
539 Returns:
540 Tuple:
541 Resulting image contents
542 Device tree contents
Simon Glass30732662018-06-01 09:38:20 -0600543 Map data showing contents of image (or None if none)
Simon Glassdef77b52018-07-17 13:25:27 -0600544 Output device tree binary filename ('u-boot.dtb' path)
Simon Glass57454f42016-11-25 20:15:52 -0700545 """
Simon Glass72232452016-11-25 20:15:53 -0700546 dtb_data = None
Simon Glass57454f42016-11-25 20:15:52 -0700547 # Use the compiled test file as the u-boot-dtb input
548 if use_real_dtb:
Simon Glass72232452016-11-25 20:15:53 -0700549 dtb_data = self._SetupDtb(fname)
Simon Glasse219aa42018-09-14 04:57:24 -0600550
551 # For testing purposes, make a copy of the DT for SPL and TPL. Add
Simon Glassd9e01d22024-07-20 11:49:40 +0100552 # a node indicating which it is, to aid verification.
Simon Glass56d05412022-02-28 07:16:54 -0700553 for name in ['spl', 'tpl', 'vpl']:
Simon Glasse219aa42018-09-14 04:57:24 -0600554 dtb_fname = '%s/u-boot-%s.dtb' % (name, name)
555 outfile = os.path.join(self._indir, dtb_fname)
556 TestFunctional._MakeInputFile(dtb_fname,
Simon Glass56d05412022-02-28 07:16:54 -0700557 self._GetDtbContentsForSpls(dtb_data, name))
Simon Glass57454f42016-11-25 20:15:52 -0700558
559 try:
Simon Glass91710b32018-07-17 13:25:32 -0600560 retcode = self._DoTestFile(fname, map=map, update_dtb=update_dtb,
Simon Glassa435cd12020-09-01 05:13:59 -0600561 entry_args=entry_args, use_real_dtb=use_real_dtb,
Simon Glass76f496d2021-07-06 10:36:37 -0600562 use_expanded=use_expanded, extra_indirs=extra_indirs,
563 threads=threads)
Simon Glass57454f42016-11-25 20:15:52 -0700564 self.assertEqual(0, retcode)
Simon Glass80025522022-01-29 14:14:04 -0700565 out_dtb_fname = tools.get_output_filename('u-boot.dtb.out')
Simon Glass57454f42016-11-25 20:15:52 -0700566
567 # Find the (only) image, read it and return its contents
568 image = control.images['image']
Simon Glass80025522022-01-29 14:14:04 -0700569 image_fname = tools.get_output_filename('image.bin')
Simon Glassa87014e2018-07-06 10:27:42 -0600570 self.assertTrue(os.path.exists(image_fname))
Simon Glass30732662018-06-01 09:38:20 -0600571 if map:
Simon Glass80025522022-01-29 14:14:04 -0700572 map_fname = tools.get_output_filename('image.map')
Simon Glass30732662018-06-01 09:38:20 -0600573 with open(map_fname) as fd:
574 map_data = fd.read()
575 else:
576 map_data = None
Simon Glass33486662019-05-14 15:53:42 -0600577 with open(image_fname, 'rb') as fd:
Simon Glassa87014e2018-07-06 10:27:42 -0600578 return fd.read(), dtb_data, map_data, out_dtb_fname
Simon Glass57454f42016-11-25 20:15:52 -0700579 finally:
580 # Put the test file back
Simon Glasse219aa42018-09-14 04:57:24 -0600581 if reset_dtbs and use_real_dtb:
Simon Glass8425a1f2018-07-17 13:25:48 -0600582 self._ResetDtbs()
Simon Glass57454f42016-11-25 20:15:52 -0700583
Simon Glass5b4bce32019-07-08 14:25:26 -0600584 def _DoReadFileRealDtb(self, fname):
585 """Run binman with a real .dtb file and return the resulting data
586
587 Args:
588 fname: DT source filename to use (e.g. 082_fdt_update_all.dts)
589
590 Returns:
591 Resulting image contents
592 """
593 return self._DoReadFileDtb(fname, use_real_dtb=True, update_dtb=True)[0]
594
Simon Glass72232452016-11-25 20:15:53 -0700595 def _DoReadFile(self, fname, use_real_dtb=False):
Simon Glass1e324002018-06-01 09:38:19 -0600596 """Helper function which discards the device-tree binary
597
598 Args:
Simon Glass511f6582018-10-01 12:22:30 -0600599 fname: Device-tree source filename to use (e.g. 005_simple.dts)
Simon Glass1e324002018-06-01 09:38:19 -0600600 use_real_dtb: True to use the test file as the contents of
601 the u-boot-dtb entry. Normally this is not needed and the
602 test contents (the U_BOOT_DTB_DATA string) can be used.
603 But in some test we need the real contents.
Simon Glassdef77b52018-07-17 13:25:27 -0600604
605 Returns:
606 Resulting image contents
Simon Glass1e324002018-06-01 09:38:19 -0600607 """
Simon Glass72232452016-11-25 20:15:53 -0700608 return self._DoReadFileDtb(fname, use_real_dtb)[0]
609
Simon Glass57454f42016-11-25 20:15:52 -0700610 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600611 def _MakeInputFile(cls, fname, contents):
Simon Glass57454f42016-11-25 20:15:52 -0700612 """Create a new test input file, creating directories as needed
613
614 Args:
Simon Glasse8561af2018-08-01 15:22:37 -0600615 fname: Filename to create
Simon Glass57454f42016-11-25 20:15:52 -0700616 contents: File contents to write in to the file
617 Returns:
618 Full pathname of file created
619 """
Simon Glass862f8e22019-08-24 07:22:43 -0600620 pathname = os.path.join(cls._indir, fname)
Simon Glass57454f42016-11-25 20:15:52 -0700621 dirname = os.path.dirname(pathname)
622 if dirname and not os.path.exists(dirname):
623 os.makedirs(dirname)
624 with open(pathname, 'wb') as fd:
625 fd.write(contents)
626 return pathname
627
628 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600629 def _MakeInputDir(cls, dirname):
Simon Glassc1ae83c2018-07-17 13:25:44 -0600630 """Create a new test input directory, creating directories as needed
631
632 Args:
633 dirname: Directory name to create
634
635 Returns:
636 Full pathname of directory created
637 """
Simon Glass862f8e22019-08-24 07:22:43 -0600638 pathname = os.path.join(cls._indir, dirname)
Simon Glassc1ae83c2018-07-17 13:25:44 -0600639 if not os.path.exists(pathname):
640 os.makedirs(pathname)
641 return pathname
642
643 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600644 def _SetupSplElf(cls, src_fname='bss_data'):
Simon Glass7057d022018-10-01 21:12:47 -0600645 """Set up an ELF file with a '_dt_ucode_base_size' symbol
646
647 Args:
648 Filename of ELF file to use as SPL
649 """
Simon Glass93a806f2019-08-24 07:22:59 -0600650 TestFunctional._MakeInputFile('spl/u-boot-spl',
Simon Glass80025522022-01-29 14:14:04 -0700651 tools.read_file(cls.ElfTestFile(src_fname)))
Simon Glass7057d022018-10-01 21:12:47 -0600652
653 @classmethod
Simon Glass3eb5b202019-08-24 07:23:00 -0600654 def _SetupTplElf(cls, src_fname='bss_data'):
655 """Set up an ELF file with a '_dt_ucode_base_size' symbol
656
657 Args:
658 Filename of ELF file to use as TPL
659 """
660 TestFunctional._MakeInputFile('tpl/u-boot-tpl',
Simon Glass80025522022-01-29 14:14:04 -0700661 tools.read_file(cls.ElfTestFile(src_fname)))
Simon Glass3eb5b202019-08-24 07:23:00 -0600662
663 @classmethod
Simon Glass56d05412022-02-28 07:16:54 -0700664 def _SetupVplElf(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 VPL
669 """
670 TestFunctional._MakeInputFile('vpl/u-boot-vpl',
671 tools.read_file(cls.ElfTestFile(src_fname)))
672
673 @classmethod
Lukas Funkee901faf2023-07-18 13:53:13 +0200674 def _SetupPmuFwlElf(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('pmu-firmware.elf',
681 tools.read_file(cls.ElfTestFile(src_fname)))
682
683 @classmethod
Simon Glasse88cef92020-07-09 18:39:41 -0600684 def _SetupDescriptor(cls):
685 with open(cls.TestFile('descriptor.bin'), 'rb') as fd:
686 TestFunctional._MakeInputFile('descriptor.bin', fd.read())
687
688 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600689 def TestFile(cls, fname):
690 return os.path.join(cls._binman_dir, 'test', fname)
Simon Glass57454f42016-11-25 20:15:52 -0700691
Simon Glassf6290892019-08-24 07:22:53 -0600692 @classmethod
693 def ElfTestFile(cls, fname):
694 return os.path.join(cls._elf_testdir, fname)
695
Simon Glassad5cfe12023-01-07 14:07:14 -0700696 @classmethod
697 def make_tee_bin(cls, fname, paged_sz=0, extra_data=b''):
698 init_sz, start_hi, start_lo, dummy = (len(U_BOOT_DATA), 0, TEE_ADDR, 0)
699 data = b'OPTE\x01xxx' + struct.pack('<5I', init_sz, start_hi, start_lo,
700 dummy, paged_sz) + U_BOOT_DATA
701 data += extra_data
702 TestFunctional._MakeInputFile(fname, data)
703
Simon Glass57454f42016-11-25 20:15:52 -0700704 def AssertInList(self, grep_list, target):
705 """Assert that at least one of a list of things is in a target
706
707 Args:
708 grep_list: List of strings to check
709 target: Target string
710 """
711 for grep in grep_list:
712 if grep in target:
713 return
Simon Glass848cdb52019-05-17 22:00:50 -0600714 self.fail("Error: '%s' not found in '%s'" % (grep_list, target))
Simon Glass57454f42016-11-25 20:15:52 -0700715
716 def CheckNoGaps(self, entries):
717 """Check that all entries fit together without gaps
718
719 Args:
720 entries: List of entries to check
721 """
Simon Glasse8561af2018-08-01 15:22:37 -0600722 offset = 0
Simon Glass57454f42016-11-25 20:15:52 -0700723 for entry in entries.values():
Simon Glasse8561af2018-08-01 15:22:37 -0600724 self.assertEqual(offset, entry.offset)
725 offset += entry.size
Simon Glass57454f42016-11-25 20:15:52 -0700726
Simon Glass72232452016-11-25 20:15:53 -0700727 def GetFdtLen(self, dtb):
Simon Glass1e324002018-06-01 09:38:19 -0600728 """Get the totalsize field from a device-tree binary
Simon Glass72232452016-11-25 20:15:53 -0700729
730 Args:
Simon Glass1e324002018-06-01 09:38:19 -0600731 dtb: Device-tree binary contents
Simon Glass72232452016-11-25 20:15:53 -0700732
733 Returns:
Simon Glass1e324002018-06-01 09:38:19 -0600734 Total size of device-tree binary, from the header
Simon Glass72232452016-11-25 20:15:53 -0700735 """
736 return struct.unpack('>L', dtb[4:8])[0]
737
Simon Glass0f621332019-07-08 14:25:27 -0600738 def _GetPropTree(self, dtb, prop_names, prefix='/binman/'):
Simon Glassa87014e2018-07-06 10:27:42 -0600739 def AddNode(node, path):
740 if node.name != '/':
741 path += '/' + node.name
Simon Glass0f621332019-07-08 14:25:27 -0600742 for prop in node.props.values():
743 if prop.name in prop_names:
744 prop_path = path + ':' + prop.name
745 tree[prop_path[len(prefix):]] = fdt_util.fdt32_to_cpu(
746 prop.value)
Simon Glassa87014e2018-07-06 10:27:42 -0600747 for subnode in node.subnodes:
Simon Glassa87014e2018-07-06 10:27:42 -0600748 AddNode(subnode, path)
749
750 tree = {}
Simon Glassa87014e2018-07-06 10:27:42 -0600751 AddNode(dtb.GetRoot(), '')
752 return tree
753
Ivan Mikhaylov3cfcaa4d2023-03-08 01:13:40 +0000754 def _CheckSign(self, fit, key):
755 try:
756 tools.run('fit_check_sign', '-k', key, '-f', fit)
757 except:
758 self.fail('Expected signed FIT container')
759 return False
760 return True
761
Simon Glass57454f42016-11-25 20:15:52 -0700762 def testRun(self):
763 """Test a basic run with valid args"""
764 result = self._RunBinman('-h')
765
766 def testFullHelp(self):
767 """Test that the full help is displayed with -H"""
768 result = self._RunBinman('-H')
Simon Glass75ead662021-03-18 20:25:13 +1300769 help_file = os.path.join(self._binman_dir, 'README.rst')
Tom Rinic3c0b6d2018-01-16 15:29:50 -0500770 # Remove possible extraneous strings
771 extra = '::::::::::::::\n' + help_file + '\n::::::::::::::\n'
772 gothelp = result.stdout.replace(extra, '')
773 self.assertEqual(len(gothelp), os.path.getsize(help_file))
Simon Glass57454f42016-11-25 20:15:52 -0700774 self.assertEqual(0, len(result.stderr))
775 self.assertEqual(0, result.return_code)
776
777 def testFullHelpInternal(self):
778 """Test that the full help is displayed with -H"""
779 try:
780 command.test_result = command.CommandResult()
781 result = self._DoBinman('-H')
Simon Glass75ead662021-03-18 20:25:13 +1300782 help_file = os.path.join(self._binman_dir, 'README.rst')
Simon Glass57454f42016-11-25 20:15:52 -0700783 finally:
784 command.test_result = None
785
786 def testHelp(self):
787 """Test that the basic help is displayed with -h"""
788 result = self._RunBinman('-h')
789 self.assertTrue(len(result.stdout) > 200)
790 self.assertEqual(0, len(result.stderr))
791 self.assertEqual(0, result.return_code)
792
Simon Glass57454f42016-11-25 20:15:52 -0700793 def testBoard(self):
794 """Test that we can run it with a specific board"""
Simon Glass511f6582018-10-01 12:22:30 -0600795 self._SetupDtb('005_simple.dts', 'sandbox/u-boot.dtb')
Simon Glass57454f42016-11-25 20:15:52 -0700796 TestFunctional._MakeInputFile('sandbox/u-boot.bin', U_BOOT_DATA)
Simon Glassed930672021-03-18 20:25:05 +1300797 result = self._DoBinman('build', '-n', '-b', 'sandbox')
Simon Glass57454f42016-11-25 20:15:52 -0700798 self.assertEqual(0, result)
799
800 def testNeedBoard(self):
801 """Test that we get an error when no board ius supplied"""
802 with self.assertRaises(ValueError) as e:
Simon Glassf46732a2019-07-08 14:25:29 -0600803 result = self._DoBinman('build')
Simon Glass57454f42016-11-25 20:15:52 -0700804 self.assertIn("Must provide a board to process (use -b <board>)",
805 str(e.exception))
806
807 def testMissingDt(self):
Simon Glass1e324002018-06-01 09:38:19 -0600808 """Test that an invalid device-tree file generates an error"""
Simon Glass57454f42016-11-25 20:15:52 -0700809 with self.assertRaises(Exception) as e:
Simon Glassf46732a2019-07-08 14:25:29 -0600810 self._RunBinman('build', '-d', 'missing_file')
Simon Glass57454f42016-11-25 20:15:52 -0700811 # We get one error from libfdt, and a different one from fdtget.
812 self.AssertInList(["Couldn't open blob from 'missing_file'",
813 'No such file or directory'], str(e.exception))
814
815 def testBrokenDt(self):
Simon Glass1e324002018-06-01 09:38:19 -0600816 """Test that an invalid device-tree source file generates an error
Simon Glass57454f42016-11-25 20:15:52 -0700817
818 Since this is a source file it should be compiled and the error
819 will come from the device-tree compiler (dtc).
820 """
821 with self.assertRaises(Exception) as e:
Simon Glassf46732a2019-07-08 14:25:29 -0600822 self._RunBinman('build', '-d', self.TestFile('001_invalid.dts'))
Simon Glass57454f42016-11-25 20:15:52 -0700823 self.assertIn("FATAL ERROR: Unable to parse input tree",
824 str(e.exception))
825
826 def testMissingNode(self):
827 """Test that a device tree without a 'binman' node generates an error"""
828 with self.assertRaises(Exception) as e:
Simon Glassf46732a2019-07-08 14:25:29 -0600829 self._DoBinman('build', '-d', self.TestFile('002_missing_node.dts'))
Simon Glass57454f42016-11-25 20:15:52 -0700830 self.assertIn("does not have a 'binman' node", str(e.exception))
831
832 def testEmpty(self):
833 """Test that an empty binman node works OK (i.e. does nothing)"""
Simon Glassf46732a2019-07-08 14:25:29 -0600834 result = self._RunBinman('build', '-d', self.TestFile('003_empty.dts'))
Simon Glass57454f42016-11-25 20:15:52 -0700835 self.assertEqual(0, len(result.stderr))
836 self.assertEqual(0, result.return_code)
837
838 def testInvalidEntry(self):
839 """Test that an invalid entry is flagged"""
840 with self.assertRaises(Exception) as e:
Simon Glassf46732a2019-07-08 14:25:29 -0600841 result = self._RunBinman('build', '-d',
Simon Glass511f6582018-10-01 12:22:30 -0600842 self.TestFile('004_invalid_entry.dts'))
Simon Glass57454f42016-11-25 20:15:52 -0700843 self.assertIn("Unknown entry type 'not-a-valid-type' in node "
844 "'/binman/not-a-valid-type'", str(e.exception))
845
846 def testSimple(self):
847 """Test a simple binman with a single file"""
Simon Glass511f6582018-10-01 12:22:30 -0600848 data = self._DoReadFile('005_simple.dts')
Simon Glass57454f42016-11-25 20:15:52 -0700849 self.assertEqual(U_BOOT_DATA, data)
850
Simon Glass075a45c2017-11-13 18:55:00 -0700851 def testSimpleDebug(self):
852 """Test a simple binman run with debugging enabled"""
Simon Glass52d06212019-07-08 14:25:53 -0600853 self._DoTestFile('005_simple.dts', debug=True)
Simon Glass075a45c2017-11-13 18:55:00 -0700854
Simon Glass57454f42016-11-25 20:15:52 -0700855 def testDual(self):
856 """Test that we can handle creating two images
857
858 This also tests image padding.
859 """
Simon Glass511f6582018-10-01 12:22:30 -0600860 retcode = self._DoTestFile('006_dual_image.dts')
Simon Glass57454f42016-11-25 20:15:52 -0700861 self.assertEqual(0, retcode)
862
863 image = control.images['image1']
Simon Glass39dd2152019-07-08 14:25:47 -0600864 self.assertEqual(len(U_BOOT_DATA), image.size)
Simon Glass80025522022-01-29 14:14:04 -0700865 fname = tools.get_output_filename('image1.bin')
Simon Glass57454f42016-11-25 20:15:52 -0700866 self.assertTrue(os.path.exists(fname))
Simon Glass33486662019-05-14 15:53:42 -0600867 with open(fname, 'rb') as fd:
Simon Glass57454f42016-11-25 20:15:52 -0700868 data = fd.read()
869 self.assertEqual(U_BOOT_DATA, data)
870
871 image = control.images['image2']
Simon Glass39dd2152019-07-08 14:25:47 -0600872 self.assertEqual(3 + len(U_BOOT_DATA) + 5, image.size)
Simon Glass80025522022-01-29 14:14:04 -0700873 fname = tools.get_output_filename('image2.bin')
Simon Glass57454f42016-11-25 20:15:52 -0700874 self.assertTrue(os.path.exists(fname))
Simon Glass33486662019-05-14 15:53:42 -0600875 with open(fname, 'rb') as fd:
Simon Glass57454f42016-11-25 20:15:52 -0700876 data = fd.read()
877 self.assertEqual(U_BOOT_DATA, data[3:7])
Simon Glass80025522022-01-29 14:14:04 -0700878 self.assertEqual(tools.get_bytes(0, 3), data[:3])
879 self.assertEqual(tools.get_bytes(0, 5), data[7:])
Simon Glass57454f42016-11-25 20:15:52 -0700880
881 def testBadAlign(self):
882 """Test that an invalid alignment value is detected"""
883 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -0600884 self._DoTestFile('007_bad_align.dts')
Simon Glass57454f42016-11-25 20:15:52 -0700885 self.assertIn("Node '/binman/u-boot': Alignment 23 must be a power "
886 "of two", str(e.exception))
887
888 def testPackSimple(self):
889 """Test that packing works as expected"""
Simon Glass511f6582018-10-01 12:22:30 -0600890 retcode = self._DoTestFile('008_pack.dts')
Simon Glass57454f42016-11-25 20:15:52 -0700891 self.assertEqual(0, retcode)
892 self.assertIn('image', control.images)
893 image = control.images['image']
Simon Glasseca32212018-06-01 09:38:12 -0600894 entries = image.GetEntries()
Simon Glass57454f42016-11-25 20:15:52 -0700895 self.assertEqual(5, len(entries))
896
897 # First u-boot
898 self.assertIn('u-boot', entries)
899 entry = entries['u-boot']
Simon Glasse8561af2018-08-01 15:22:37 -0600900 self.assertEqual(0, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700901 self.assertEqual(len(U_BOOT_DATA), entry.size)
902
903 # Second u-boot, aligned to 16-byte boundary
904 self.assertIn('u-boot-align', entries)
905 entry = entries['u-boot-align']
Simon Glasse8561af2018-08-01 15:22:37 -0600906 self.assertEqual(16, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700907 self.assertEqual(len(U_BOOT_DATA), entry.size)
908
909 # Third u-boot, size 23 bytes
910 self.assertIn('u-boot-size', entries)
911 entry = entries['u-boot-size']
Simon Glasse8561af2018-08-01 15:22:37 -0600912 self.assertEqual(20, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700913 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
914 self.assertEqual(23, entry.size)
915
916 # Fourth u-boot, placed immediate after the above
917 self.assertIn('u-boot-next', entries)
918 entry = entries['u-boot-next']
Simon Glasse8561af2018-08-01 15:22:37 -0600919 self.assertEqual(43, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700920 self.assertEqual(len(U_BOOT_DATA), entry.size)
921
Simon Glasse8561af2018-08-01 15:22:37 -0600922 # Fifth u-boot, placed at a fixed offset
Simon Glass57454f42016-11-25 20:15:52 -0700923 self.assertIn('u-boot-fixed', entries)
924 entry = entries['u-boot-fixed']
Simon Glasse8561af2018-08-01 15:22:37 -0600925 self.assertEqual(61, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700926 self.assertEqual(len(U_BOOT_DATA), entry.size)
927
Simon Glass39dd2152019-07-08 14:25:47 -0600928 self.assertEqual(65, image.size)
Simon Glass57454f42016-11-25 20:15:52 -0700929
930 def testPackExtra(self):
931 """Test that extra packing feature works as expected"""
Simon Glassafb9caa2020-10-26 17:40:10 -0600932 data, _, _, out_dtb_fname = self._DoReadFileDtb('009_pack_extra.dts',
933 update_dtb=True)
Simon Glass57454f42016-11-25 20:15:52 -0700934
Simon Glass57454f42016-11-25 20:15:52 -0700935 self.assertIn('image', control.images)
936 image = control.images['image']
Simon Glasseca32212018-06-01 09:38:12 -0600937 entries = image.GetEntries()
Samuel Hollande2574022023-01-21 17:25:16 -0600938 self.assertEqual(6, len(entries))
Simon Glass57454f42016-11-25 20:15:52 -0700939
Samuel Hollande2574022023-01-21 17:25:16 -0600940 # First u-boot with padding before and after (included in minimum size)
Simon Glass57454f42016-11-25 20:15:52 -0700941 self.assertIn('u-boot', entries)
942 entry = entries['u-boot']
Simon Glasse8561af2018-08-01 15:22:37 -0600943 self.assertEqual(0, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700944 self.assertEqual(3, entry.pad_before)
945 self.assertEqual(3 + 5 + len(U_BOOT_DATA), entry.size)
Simon Glass187202f2020-10-26 17:40:08 -0600946 self.assertEqual(U_BOOT_DATA, entry.data)
Simon Glass80025522022-01-29 14:14:04 -0700947 self.assertEqual(tools.get_bytes(0, 3) + U_BOOT_DATA +
948 tools.get_bytes(0, 5), data[:entry.size])
Simon Glass187202f2020-10-26 17:40:08 -0600949 pos = entry.size
Simon Glass57454f42016-11-25 20:15:52 -0700950
951 # Second u-boot has an aligned size, but it has no effect
952 self.assertIn('u-boot-align-size-nop', entries)
953 entry = entries['u-boot-align-size-nop']
Simon Glass187202f2020-10-26 17:40:08 -0600954 self.assertEqual(pos, entry.offset)
955 self.assertEqual(len(U_BOOT_DATA), entry.size)
956 self.assertEqual(U_BOOT_DATA, entry.data)
957 self.assertEqual(U_BOOT_DATA, data[pos:pos + entry.size])
958 pos += entry.size
Simon Glass57454f42016-11-25 20:15:52 -0700959
960 # Third u-boot has an aligned size too
961 self.assertIn('u-boot-align-size', entries)
962 entry = entries['u-boot-align-size']
Simon Glass187202f2020-10-26 17:40:08 -0600963 self.assertEqual(pos, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700964 self.assertEqual(32, entry.size)
Simon Glass187202f2020-10-26 17:40:08 -0600965 self.assertEqual(U_BOOT_DATA, entry.data)
Simon Glass80025522022-01-29 14:14:04 -0700966 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 32 - len(U_BOOT_DATA)),
Simon Glass187202f2020-10-26 17:40:08 -0600967 data[pos:pos + entry.size])
968 pos += entry.size
Simon Glass57454f42016-11-25 20:15:52 -0700969
970 # Fourth u-boot has an aligned end
971 self.assertIn('u-boot-align-end', entries)
972 entry = entries['u-boot-align-end']
Simon Glasse8561af2018-08-01 15:22:37 -0600973 self.assertEqual(48, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700974 self.assertEqual(16, entry.size)
Simon Glass187202f2020-10-26 17:40:08 -0600975 self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)])
Simon Glass80025522022-01-29 14:14:04 -0700976 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 16 - len(U_BOOT_DATA)),
Simon Glass187202f2020-10-26 17:40:08 -0600977 data[pos:pos + entry.size])
978 pos += entry.size
Simon Glass57454f42016-11-25 20:15:52 -0700979
980 # Fifth u-boot immediately afterwards
981 self.assertIn('u-boot-align-both', entries)
982 entry = entries['u-boot-align-both']
Simon Glasse8561af2018-08-01 15:22:37 -0600983 self.assertEqual(64, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700984 self.assertEqual(64, entry.size)
Simon Glass187202f2020-10-26 17:40:08 -0600985 self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)])
Simon Glass80025522022-01-29 14:14:04 -0700986 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 64 - len(U_BOOT_DATA)),
Simon Glass187202f2020-10-26 17:40:08 -0600987 data[pos:pos + entry.size])
Simon Glass57454f42016-11-25 20:15:52 -0700988
Samuel Hollande2574022023-01-21 17:25:16 -0600989 # Sixth u-boot with both minimum size and aligned size
990 self.assertIn('u-boot-min-size', entries)
991 entry = entries['u-boot-min-size']
992 self.assertEqual(128, entry.offset)
993 self.assertEqual(32, entry.size)
994 self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)])
995 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 32 - len(U_BOOT_DATA)),
996 data[pos:pos + entry.size])
997
Simon Glass57454f42016-11-25 20:15:52 -0700998 self.CheckNoGaps(entries)
Samuel Hollande2574022023-01-21 17:25:16 -0600999 self.assertEqual(160, image.size)
Simon Glass57454f42016-11-25 20:15:52 -07001000
Simon Glassafb9caa2020-10-26 17:40:10 -06001001 dtb = fdt.Fdt(out_dtb_fname)
1002 dtb.Scan()
1003 props = self._GetPropTree(dtb, ['size', 'offset', 'image-pos'])
1004 expected = {
1005 'image-pos': 0,
1006 'offset': 0,
Samuel Hollande2574022023-01-21 17:25:16 -06001007 'size': 160,
Simon Glassafb9caa2020-10-26 17:40:10 -06001008
1009 'u-boot:image-pos': 0,
1010 'u-boot:offset': 0,
1011 'u-boot:size': 3 + 5 + len(U_BOOT_DATA),
1012
1013 'u-boot-align-size-nop:image-pos': 12,
1014 'u-boot-align-size-nop:offset': 12,
1015 'u-boot-align-size-nop:size': 4,
1016
1017 'u-boot-align-size:image-pos': 16,
1018 'u-boot-align-size:offset': 16,
1019 'u-boot-align-size:size': 32,
1020
1021 'u-boot-align-end:image-pos': 48,
1022 'u-boot-align-end:offset': 48,
1023 'u-boot-align-end:size': 16,
1024
1025 'u-boot-align-both:image-pos': 64,
1026 'u-boot-align-both:offset': 64,
1027 'u-boot-align-both:size': 64,
Samuel Hollande2574022023-01-21 17:25:16 -06001028
1029 'u-boot-min-size:image-pos': 128,
1030 'u-boot-min-size:offset': 128,
1031 'u-boot-min-size:size': 32,
Simon Glassafb9caa2020-10-26 17:40:10 -06001032 }
1033 self.assertEqual(expected, props)
1034
Simon Glass57454f42016-11-25 20:15:52 -07001035 def testPackAlignPowerOf2(self):
1036 """Test that invalid entry alignment is detected"""
1037 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001038 self._DoTestFile('010_pack_align_power2.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001039 self.assertIn("Node '/binman/u-boot': Alignment 5 must be a power "
1040 "of two", str(e.exception))
1041
1042 def testPackAlignSizePowerOf2(self):
1043 """Test that invalid entry size alignment is detected"""
1044 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001045 self._DoTestFile('011_pack_align_size_power2.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001046 self.assertIn("Node '/binman/u-boot': Alignment size 55 must be a "
1047 "power of two", str(e.exception))
1048
1049 def testPackInvalidAlign(self):
Simon Glasse8561af2018-08-01 15:22:37 -06001050 """Test detection of an offset that does not match its alignment"""
Simon Glass57454f42016-11-25 20:15:52 -07001051 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001052 self._DoTestFile('012_pack_inv_align.dts')
Simon Glasse8561af2018-08-01 15:22:37 -06001053 self.assertIn("Node '/binman/u-boot': Offset 0x5 (5) does not match "
Simon Glass57454f42016-11-25 20:15:52 -07001054 "align 0x4 (4)", str(e.exception))
1055
1056 def testPackInvalidSizeAlign(self):
1057 """Test that invalid entry size alignment is detected"""
1058 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001059 self._DoTestFile('013_pack_inv_size_align.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001060 self.assertIn("Node '/binman/u-boot': Size 0x5 (5) does not match "
1061 "align-size 0x4 (4)", str(e.exception))
1062
1063 def testPackOverlap(self):
1064 """Test that overlapping regions are detected"""
1065 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001066 self._DoTestFile('014_pack_overlap.dts')
Simon Glasse8561af2018-08-01 15:22:37 -06001067 self.assertIn("Node '/binman/u-boot-align': Offset 0x3 (3) overlaps "
Simon Glass57454f42016-11-25 20:15:52 -07001068 "with previous entry '/binman/u-boot' ending at 0x4 (4)",
1069 str(e.exception))
1070
1071 def testPackEntryOverflow(self):
1072 """Test that entries that overflow their size are detected"""
1073 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001074 self._DoTestFile('015_pack_overflow.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001075 self.assertIn("Node '/binman/u-boot': Entry contents size is 0x4 (4) "
1076 "but entry size is 0x3 (3)", str(e.exception))
1077
1078 def testPackImageOverflow(self):
1079 """Test that entries which overflow the image size are detected"""
1080 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001081 self._DoTestFile('016_pack_image_overflow.dts')
Simon Glasseca32212018-06-01 09:38:12 -06001082 self.assertIn("Section '/binman': contents size 0x4 (4) exceeds section "
Simon Glass57454f42016-11-25 20:15:52 -07001083 "size 0x3 (3)", str(e.exception))
1084
1085 def testPackImageSize(self):
1086 """Test that the image size can be set"""
Simon Glass511f6582018-10-01 12:22:30 -06001087 retcode = self._DoTestFile('017_pack_image_size.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001088 self.assertEqual(0, retcode)
1089 self.assertIn('image', control.images)
1090 image = control.images['image']
Simon Glass39dd2152019-07-08 14:25:47 -06001091 self.assertEqual(7, image.size)
Simon Glass57454f42016-11-25 20:15:52 -07001092
1093 def testPackImageSizeAlign(self):
1094 """Test that image size alignemnt works as expected"""
Simon Glass511f6582018-10-01 12:22:30 -06001095 retcode = self._DoTestFile('018_pack_image_align.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001096 self.assertEqual(0, retcode)
1097 self.assertIn('image', control.images)
1098 image = control.images['image']
Simon Glass39dd2152019-07-08 14:25:47 -06001099 self.assertEqual(16, image.size)
Simon Glass57454f42016-11-25 20:15:52 -07001100
1101 def testPackInvalidImageAlign(self):
1102 """Test that invalid image alignment is detected"""
1103 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001104 self._DoTestFile('019_pack_inv_image_align.dts')
Simon Glasseca32212018-06-01 09:38:12 -06001105 self.assertIn("Section '/binman': Size 0x7 (7) does not match "
Simon Glass57454f42016-11-25 20:15:52 -07001106 "align-size 0x8 (8)", str(e.exception))
1107
Simon Glass2a0fa982022-02-11 13:23:21 -07001108 def testPackAlignPowerOf2Inv(self):
Simon Glass57454f42016-11-25 20:15:52 -07001109 """Test that invalid image alignment is detected"""
1110 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001111 self._DoTestFile('020_pack_inv_image_align_power2.dts')
Simon Glass39dd2152019-07-08 14:25:47 -06001112 self.assertIn("Image '/binman': Alignment size 131 must be a power of "
Simon Glass57454f42016-11-25 20:15:52 -07001113 "two", str(e.exception))
1114
1115 def testImagePadByte(self):
1116 """Test that the image pad byte can be specified"""
Simon Glass7057d022018-10-01 21:12:47 -06001117 self._SetupSplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001118 data = self._DoReadFile('021_image_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07001119 self.assertEqual(U_BOOT_SPL_DATA + tools.get_bytes(0xff, 1) +
Simon Glassac0d4952019-05-14 15:53:47 -06001120 U_BOOT_DATA, data)
Simon Glass57454f42016-11-25 20:15:52 -07001121
1122 def testImageName(self):
1123 """Test that image files can be named"""
Simon Glass511f6582018-10-01 12:22:30 -06001124 retcode = self._DoTestFile('022_image_name.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001125 self.assertEqual(0, retcode)
1126 image = control.images['image1']
Simon Glass80025522022-01-29 14:14:04 -07001127 fname = tools.get_output_filename('test-name')
Simon Glass57454f42016-11-25 20:15:52 -07001128 self.assertTrue(os.path.exists(fname))
1129
1130 image = control.images['image2']
Simon Glass80025522022-01-29 14:14:04 -07001131 fname = tools.get_output_filename('test-name.xx')
Simon Glass57454f42016-11-25 20:15:52 -07001132 self.assertTrue(os.path.exists(fname))
1133
1134 def testBlobFilename(self):
1135 """Test that generic blobs can be provided by filename"""
Simon Glass511f6582018-10-01 12:22:30 -06001136 data = self._DoReadFile('023_blob.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001137 self.assertEqual(BLOB_DATA, data)
1138
1139 def testPackSorted(self):
1140 """Test that entries can be sorted"""
Simon Glass7057d022018-10-01 21:12:47 -06001141 self._SetupSplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001142 data = self._DoReadFile('024_sorted.dts')
Simon Glass80025522022-01-29 14:14:04 -07001143 self.assertEqual(tools.get_bytes(0, 1) + U_BOOT_SPL_DATA +
1144 tools.get_bytes(0, 2) + U_BOOT_DATA, data)
Simon Glass57454f42016-11-25 20:15:52 -07001145
Simon Glasse8561af2018-08-01 15:22:37 -06001146 def testPackZeroOffset(self):
1147 """Test that an entry at offset 0 is not given a new offset"""
Marek Vasutf7413f02023-07-18 07:23:58 -06001148 self._SetupSplElf()
Simon Glass57454f42016-11-25 20:15:52 -07001149 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001150 self._DoTestFile('025_pack_zero_size.dts')
Simon Glasse8561af2018-08-01 15:22:37 -06001151 self.assertIn("Node '/binman/u-boot-spl': Offset 0x0 (0) overlaps "
Simon Glass57454f42016-11-25 20:15:52 -07001152 "with previous entry '/binman/u-boot' ending at 0x4 (4)",
1153 str(e.exception))
1154
1155 def testPackUbootDtb(self):
1156 """Test that a device tree can be added to U-Boot"""
Simon Glass511f6582018-10-01 12:22:30 -06001157 data = self._DoReadFile('026_pack_u_boot_dtb.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001158 self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA, data)
Simon Glass72232452016-11-25 20:15:53 -07001159
1160 def testPackX86RomNoSize(self):
1161 """Test that the end-at-4gb property requires a size property"""
Marek Vasutf7413f02023-07-18 07:23:58 -06001162 self._SetupSplElf()
Simon Glass72232452016-11-25 20:15:53 -07001163 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001164 self._DoTestFile('027_pack_4gb_no_size.dts')
Simon Glass39dd2152019-07-08 14:25:47 -06001165 self.assertIn("Image '/binman': Section size must be provided when "
Simon Glass72232452016-11-25 20:15:53 -07001166 "using end-at-4gb", str(e.exception))
1167
Jagdish Gediya0fb978c2018-09-03 21:35:07 +05301168 def test4gbAndSkipAtStartTogether(self):
1169 """Test that the end-at-4gb and skip-at-size property can't be used
1170 together"""
Marek Vasutf7413f02023-07-18 07:23:58 -06001171 self._SetupSplElf()
Jagdish Gediya0fb978c2018-09-03 21:35:07 +05301172 with self.assertRaises(ValueError) as e:
Simon Glass11f2bd02019-08-24 07:23:02 -06001173 self._DoTestFile('098_4gb_and_skip_at_start_together.dts')
Simon Glass39dd2152019-07-08 14:25:47 -06001174 self.assertIn("Image '/binman': Provide either 'end-at-4gb' or "
Jagdish Gediya0fb978c2018-09-03 21:35:07 +05301175 "'skip-at-start'", str(e.exception))
1176
Simon Glass72232452016-11-25 20:15:53 -07001177 def testPackX86RomOutside(self):
Simon Glasse8561af2018-08-01 15:22:37 -06001178 """Test that the end-at-4gb property checks for offset boundaries"""
Marek Vasutf7413f02023-07-18 07:23:58 -06001179 self._SetupSplElf()
Simon Glass72232452016-11-25 20:15:53 -07001180 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001181 self._DoTestFile('028_pack_4gb_outside.dts')
Simon Glassd6179862020-10-26 17:40:05 -06001182 self.assertIn("Node '/binman/u-boot': Offset 0x0 (0) size 0x4 (4) "
1183 "is outside the section '/binman' starting at "
1184 '0xffffffe0 (4294967264) of size 0x20 (32)',
Simon Glass72232452016-11-25 20:15:53 -07001185 str(e.exception))
1186
1187 def testPackX86Rom(self):
1188 """Test that a basic x86 ROM can be created"""
Simon Glass7057d022018-10-01 21:12:47 -06001189 self._SetupSplElf()
Simon Glass1d167762019-08-24 07:23:01 -06001190 data = self._DoReadFile('029_x86_rom.dts')
Simon Glass80025522022-01-29 14:14:04 -07001191 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 3) + U_BOOT_SPL_DATA +
1192 tools.get_bytes(0, 2), data)
Simon Glass72232452016-11-25 20:15:53 -07001193
1194 def testPackX86RomMeNoDesc(self):
1195 """Test that an invalid Intel descriptor entry is detected"""
Simon Glasse88cef92020-07-09 18:39:41 -06001196 try:
Simon Glass14c596c2020-07-25 15:11:19 -06001197 TestFunctional._MakeInputFile('descriptor-empty.bin', b'')
Simon Glasse88cef92020-07-09 18:39:41 -06001198 with self.assertRaises(ValueError) as e:
Simon Glass14c596c2020-07-25 15:11:19 -06001199 self._DoTestFile('163_x86_rom_me_empty.dts')
Simon Glasse88cef92020-07-09 18:39:41 -06001200 self.assertIn("Node '/binman/intel-descriptor': Cannot find Intel Flash Descriptor (FD) signature",
1201 str(e.exception))
1202 finally:
1203 self._SetupDescriptor()
Simon Glass72232452016-11-25 20:15:53 -07001204
1205 def testPackX86RomBadDesc(self):
1206 """Test that the Intel requires a descriptor entry"""
1207 with self.assertRaises(ValueError) as e:
Simon Glass1d167762019-08-24 07:23:01 -06001208 self._DoTestFile('030_x86_rom_me_no_desc.dts')
Simon Glasse8561af2018-08-01 15:22:37 -06001209 self.assertIn("Node '/binman/intel-me': No offset set with "
1210 "offset-unset: should another entry provide this correct "
1211 "offset?", str(e.exception))
Simon Glass72232452016-11-25 20:15:53 -07001212
1213 def testPackX86RomMe(self):
1214 """Test that an x86 ROM with an ME region can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001215 data = self._DoReadFile('031_x86_rom_me.dts')
Simon Glass80025522022-01-29 14:14:04 -07001216 expected_desc = tools.read_file(self.TestFile('descriptor.bin'))
Simon Glass759af872019-07-08 13:18:54 -06001217 if data[:0x1000] != expected_desc:
1218 self.fail('Expected descriptor binary at start of image')
Simon Glass72232452016-11-25 20:15:53 -07001219 self.assertEqual(ME_DATA, data[0x1000:0x1000 + len(ME_DATA)])
1220
1221 def testPackVga(self):
1222 """Test that an image with a VGA binary can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001223 data = self._DoReadFile('032_intel_vga.dts')
Simon Glass72232452016-11-25 20:15:53 -07001224 self.assertEqual(VGA_DATA, data[:len(VGA_DATA)])
1225
1226 def testPackStart16(self):
1227 """Test that an image with an x86 start16 region can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001228 data = self._DoReadFile('033_x86_start16.dts')
Simon Glass72232452016-11-25 20:15:53 -07001229 self.assertEqual(X86_START16_DATA, data[:len(X86_START16_DATA)])
1230
Jagdish Gediya311d4842018-09-03 21:35:08 +05301231 def testPackPowerpcMpc85xxBootpgResetvec(self):
1232 """Test that an image with powerpc-mpc85xx-bootpg-resetvec can be
1233 created"""
Simon Glass11f2bd02019-08-24 07:23:02 -06001234 data = self._DoReadFile('150_powerpc_mpc85xx_bootpg_resetvec.dts')
Jagdish Gediya311d4842018-09-03 21:35:08 +05301235 self.assertEqual(PPC_MPC85XX_BR_DATA, data[:len(PPC_MPC85XX_BR_DATA)])
1236
Simon Glass6ba679c2018-07-06 10:27:17 -06001237 def _RunMicrocodeTest(self, dts_fname, nodtb_data, ucode_second=False):
Simon Glass820af1d2018-07-06 10:27:16 -06001238 """Handle running a test for insertion of microcode
1239
1240 Args:
1241 dts_fname: Name of test .dts file
1242 nodtb_data: Data that we expect in the first section
Simon Glass6ba679c2018-07-06 10:27:17 -06001243 ucode_second: True if the microsecond entry is second instead of
1244 third
Simon Glass820af1d2018-07-06 10:27:16 -06001245
1246 Returns:
1247 Tuple:
1248 Contents of first region (U-Boot or SPL)
Simon Glasse8561af2018-08-01 15:22:37 -06001249 Offset and size components of microcode pointer, as inserted
Simon Glass820af1d2018-07-06 10:27:16 -06001250 in the above (two 4-byte words)
1251 """
Simon Glass3d274232017-11-12 21:52:27 -07001252 data = self._DoReadFile(dts_fname, True)
Simon Glass72232452016-11-25 20:15:53 -07001253
1254 # Now check the device tree has no microcode
Simon Glass6ba679c2018-07-06 10:27:17 -06001255 if ucode_second:
1256 ucode_content = data[len(nodtb_data):]
1257 ucode_pos = len(nodtb_data)
1258 dtb_with_ucode = ucode_content[16:]
1259 fdt_len = self.GetFdtLen(dtb_with_ucode)
1260 else:
1261 dtb_with_ucode = data[len(nodtb_data):]
1262 fdt_len = self.GetFdtLen(dtb_with_ucode)
1263 ucode_content = dtb_with_ucode[fdt_len:]
1264 ucode_pos = len(nodtb_data) + fdt_len
Simon Glass80025522022-01-29 14:14:04 -07001265 fname = tools.get_output_filename('test.dtb')
Simon Glass72232452016-11-25 20:15:53 -07001266 with open(fname, 'wb') as fd:
Simon Glass820af1d2018-07-06 10:27:16 -06001267 fd.write(dtb_with_ucode)
Simon Glass22c92ca2017-05-27 07:38:29 -06001268 dtb = fdt.FdtScan(fname)
1269 ucode = dtb.GetNode('/microcode')
Simon Glass72232452016-11-25 20:15:53 -07001270 self.assertTrue(ucode)
1271 for node in ucode.subnodes:
1272 self.assertFalse(node.props.get('data'))
1273
Simon Glass72232452016-11-25 20:15:53 -07001274 # Check that the microcode appears immediately after the Fdt
1275 # This matches the concatenation of the data properties in
Simon Glasse83679d2017-11-12 21:52:26 -07001276 # the /microcode/update@xxx nodes in 34_x86_ucode.dts.
Simon Glass72232452016-11-25 20:15:53 -07001277 ucode_data = struct.pack('>4L', 0x12345678, 0x12345679, 0xabcd0000,
1278 0x78235609)
Simon Glass820af1d2018-07-06 10:27:16 -06001279 self.assertEqual(ucode_data, ucode_content[:len(ucode_data)])
Simon Glass72232452016-11-25 20:15:53 -07001280
1281 # Check that the microcode pointer was inserted. It should match the
Simon Glasse8561af2018-08-01 15:22:37 -06001282 # expected offset and size
Simon Glass72232452016-11-25 20:15:53 -07001283 pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
1284 len(ucode_data))
Simon Glass6ba679c2018-07-06 10:27:17 -06001285 u_boot = data[:len(nodtb_data)]
1286 return u_boot, pos_and_size
Simon Glass3d274232017-11-12 21:52:27 -07001287
1288 def testPackUbootMicrocode(self):
1289 """Test that x86 microcode can be handled correctly
1290
1291 We expect to see the following in the image, in order:
1292 u-boot-nodtb.bin with a microcode pointer inserted at the correct
1293 place
1294 u-boot.dtb with the microcode removed
1295 the microcode
1296 """
Simon Glass511f6582018-10-01 12:22:30 -06001297 first, pos_and_size = self._RunMicrocodeTest('034_x86_ucode.dts',
Simon Glass3d274232017-11-12 21:52:27 -07001298 U_BOOT_NODTB_DATA)
Simon Glass303f62f2019-05-17 22:00:46 -06001299 self.assertEqual(b'nodtb with microcode' + pos_and_size +
1300 b' somewhere in here', first)
Simon Glass72232452016-11-25 20:15:53 -07001301
Simon Glassbac25c82017-05-27 07:38:26 -06001302 def _RunPackUbootSingleMicrocode(self):
Simon Glass72232452016-11-25 20:15:53 -07001303 """Test that x86 microcode can be handled correctly
1304
1305 We expect to see the following in the image, in order:
1306 u-boot-nodtb.bin with a microcode pointer inserted at the correct
1307 place
1308 u-boot.dtb with the microcode
1309 an empty microcode region
1310 """
1311 # We need the libfdt library to run this test since only that allows
1312 # finding the offset of a property. This is required by
1313 # Entry_u_boot_dtb_with_ucode.ObtainContents().
Simon Glass511f6582018-10-01 12:22:30 -06001314 data = self._DoReadFile('035_x86_single_ucode.dts', True)
Simon Glass72232452016-11-25 20:15:53 -07001315
1316 second = data[len(U_BOOT_NODTB_DATA):]
1317
1318 fdt_len = self.GetFdtLen(second)
1319 third = second[fdt_len:]
1320 second = second[:fdt_len]
1321
Simon Glassbac25c82017-05-27 07:38:26 -06001322 ucode_data = struct.pack('>2L', 0x12345678, 0x12345679)
1323 self.assertIn(ucode_data, second)
1324 ucode_pos = second.find(ucode_data) + len(U_BOOT_NODTB_DATA)
Simon Glass72232452016-11-25 20:15:53 -07001325
Simon Glassbac25c82017-05-27 07:38:26 -06001326 # Check that the microcode pointer was inserted. It should match the
Simon Glasse8561af2018-08-01 15:22:37 -06001327 # expected offset and size
Simon Glassbac25c82017-05-27 07:38:26 -06001328 pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
1329 len(ucode_data))
1330 first = data[:len(U_BOOT_NODTB_DATA)]
Simon Glass303f62f2019-05-17 22:00:46 -06001331 self.assertEqual(b'nodtb with microcode' + pos_and_size +
1332 b' somewhere in here', first)
Simon Glass996021e2016-11-25 20:15:54 -07001333
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001334 def testPackUbootSingleMicrocode(self):
1335 """Test that x86 microcode can be handled correctly with fdt_normal.
1336 """
Simon Glassbac25c82017-05-27 07:38:26 -06001337 self._RunPackUbootSingleMicrocode()
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001338
Simon Glass996021e2016-11-25 20:15:54 -07001339 def testUBootImg(self):
1340 """Test that u-boot.img can be put in a file"""
Simon Glass511f6582018-10-01 12:22:30 -06001341 data = self._DoReadFile('036_u_boot_img.dts')
Simon Glass996021e2016-11-25 20:15:54 -07001342 self.assertEqual(U_BOOT_IMG_DATA, data)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001343
1344 def testNoMicrocode(self):
1345 """Test that a missing microcode region is detected"""
1346 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001347 self._DoReadFile('037_x86_no_ucode.dts', True)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001348 self.assertIn("Node '/binman/u-boot-dtb-with-ucode': No /microcode "
1349 "node found in ", str(e.exception))
1350
1351 def testMicrocodeWithoutNode(self):
1352 """Test that a missing u-boot-dtb-with-ucode node is detected"""
1353 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001354 self._DoReadFile('038_x86_ucode_missing_node.dts', True)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001355 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
1356 "microcode region u-boot-dtb-with-ucode", str(e.exception))
1357
1358 def testMicrocodeWithoutNode2(self):
1359 """Test that a missing u-boot-ucode node is detected"""
1360 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001361 self._DoReadFile('039_x86_ucode_missing_node2.dts', True)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001362 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
1363 "microcode region u-boot-ucode", str(e.exception))
1364
1365 def testMicrocodeWithoutPtrInElf(self):
1366 """Test that a U-Boot binary without the microcode symbol is detected"""
1367 # ELF file without a '_dt_ucode_base_size' symbol
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001368 try:
Simon Glassfaaaa162019-08-24 07:22:55 -06001369 TestFunctional._MakeInputFile('u-boot',
Simon Glass80025522022-01-29 14:14:04 -07001370 tools.read_file(self.ElfTestFile('u_boot_no_ucode_ptr')))
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001371
1372 with self.assertRaises(ValueError) as e:
Simon Glassbac25c82017-05-27 07:38:26 -06001373 self._RunPackUbootSingleMicrocode()
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001374 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot locate "
1375 "_dt_ucode_base_size symbol in u-boot", str(e.exception))
1376
1377 finally:
1378 # Put the original file back
Simon Glass4affd4b2019-08-24 07:22:54 -06001379 TestFunctional._MakeInputFile('u-boot',
Simon Glass80025522022-01-29 14:14:04 -07001380 tools.read_file(self.ElfTestFile('u_boot_ucode_ptr')))
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001381
1382 def testMicrocodeNotInImage(self):
1383 """Test that microcode must be placed within the image"""
1384 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001385 self._DoReadFile('040_x86_ucode_not_in_image.dts', True)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001386 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Microcode "
1387 "pointer _dt_ucode_base_size at fffffe14 is outside the "
Simon Glassad5a7712018-06-01 09:38:14 -06001388 "section ranging from 00000000 to 0000002e", str(e.exception))
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001389
1390 def testWithoutMicrocode(self):
1391 """Test that we can cope with an image without microcode (e.g. qemu)"""
Simon Glassfaaaa162019-08-24 07:22:55 -06001392 TestFunctional._MakeInputFile('u-boot',
Simon Glass80025522022-01-29 14:14:04 -07001393 tools.read_file(self.ElfTestFile('u_boot_no_ucode_ptr')))
Simon Glass511f6582018-10-01 12:22:30 -06001394 data, dtb, _, _ = self._DoReadFileDtb('044_x86_optional_ucode.dts', True)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001395
1396 # Now check the device tree has no microcode
1397 self.assertEqual(U_BOOT_NODTB_DATA, data[:len(U_BOOT_NODTB_DATA)])
1398 second = data[len(U_BOOT_NODTB_DATA):]
1399
1400 fdt_len = self.GetFdtLen(second)
1401 self.assertEqual(dtb, second[:fdt_len])
1402
1403 used_len = len(U_BOOT_NODTB_DATA) + fdt_len
1404 third = data[used_len:]
Simon Glass80025522022-01-29 14:14:04 -07001405 self.assertEqual(tools.get_bytes(0, 0x200 - used_len), third)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001406
1407 def testUnknownPosSize(self):
1408 """Test that microcode must be placed within the image"""
1409 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001410 self._DoReadFile('041_unknown_pos_size.dts', True)
Simon Glasse8561af2018-08-01 15:22:37 -06001411 self.assertIn("Section '/binman': Unable to set offset/size for unknown "
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001412 "entry 'invalid-entry'", str(e.exception))
Simon Glassb4176d42016-11-25 20:15:56 -07001413
1414 def testPackFsp(self):
1415 """Test that an image with a FSP binary can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001416 data = self._DoReadFile('042_intel_fsp.dts')
Simon Glassb4176d42016-11-25 20:15:56 -07001417 self.assertEqual(FSP_DATA, data[:len(FSP_DATA)])
1418
1419 def testPackCmc(self):
Bin Mengd7bcdf52017-08-15 22:41:54 -07001420 """Test that an image with a CMC binary can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001421 data = self._DoReadFile('043_intel_cmc.dts')
Simon Glassb4176d42016-11-25 20:15:56 -07001422 self.assertEqual(CMC_DATA, data[:len(CMC_DATA)])
Bin Mengd7bcdf52017-08-15 22:41:54 -07001423
1424 def testPackVbt(self):
1425 """Test that an image with a VBT binary can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001426 data = self._DoReadFile('046_intel_vbt.dts')
Bin Mengd7bcdf52017-08-15 22:41:54 -07001427 self.assertEqual(VBT_DATA, data[:len(VBT_DATA)])
Simon Glassac599912017-11-12 21:52:22 -07001428
Simon Glass7f94e832017-11-12 21:52:25 -07001429 def testSplBssPad(self):
1430 """Test that we can pad SPL's BSS with zeros"""
Simon Glass3d274232017-11-12 21:52:27 -07001431 # ELF file with a '__bss_size' symbol
Simon Glass7057d022018-10-01 21:12:47 -06001432 self._SetupSplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001433 data = self._DoReadFile('047_spl_bss_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07001434 self.assertEqual(U_BOOT_SPL_DATA + tools.get_bytes(0, 10) + U_BOOT_DATA,
Simon Glassac0d4952019-05-14 15:53:47 -06001435 data)
Simon Glass7f94e832017-11-12 21:52:25 -07001436
Simon Glass04cda032018-10-01 21:12:42 -06001437 def testSplBssPadMissing(self):
1438 """Test that a missing symbol is detected"""
Simon Glass7057d022018-10-01 21:12:47 -06001439 self._SetupSplElf('u_boot_ucode_ptr')
Simon Glass24ad3652017-11-13 18:54:54 -07001440 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001441 self._DoReadFile('047_spl_bss_pad.dts')
Simon Glass24ad3652017-11-13 18:54:54 -07001442 self.assertIn('Expected __bss_size symbol in spl/u-boot-spl',
1443 str(e.exception))
1444
Simon Glasse83679d2017-11-12 21:52:26 -07001445 def testPackStart16Spl(self):
Simon Glassed40e962018-09-14 04:57:10 -06001446 """Test that an image with an x86 start16 SPL region can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001447 data = self._DoReadFile('048_x86_start16_spl.dts')
Simon Glasse83679d2017-11-12 21:52:26 -07001448 self.assertEqual(X86_START16_SPL_DATA, data[:len(X86_START16_SPL_DATA)])
1449
Simon Glass6ba679c2018-07-06 10:27:17 -06001450 def _PackUbootSplMicrocode(self, dts, ucode_second=False):
1451 """Helper function for microcode tests
Simon Glass3d274232017-11-12 21:52:27 -07001452
1453 We expect to see the following in the image, in order:
1454 u-boot-spl-nodtb.bin with a microcode pointer inserted at the
1455 correct place
1456 u-boot.dtb with the microcode removed
1457 the microcode
Simon Glass6ba679c2018-07-06 10:27:17 -06001458
1459 Args:
1460 dts: Device tree file to use for test
1461 ucode_second: True if the microsecond entry is second instead of
1462 third
Simon Glass3d274232017-11-12 21:52:27 -07001463 """
Simon Glass7057d022018-10-01 21:12:47 -06001464 self._SetupSplElf('u_boot_ucode_ptr')
Simon Glass6ba679c2018-07-06 10:27:17 -06001465 first, pos_and_size = self._RunMicrocodeTest(dts, U_BOOT_SPL_NODTB_DATA,
1466 ucode_second=ucode_second)
Simon Glass303f62f2019-05-17 22:00:46 -06001467 self.assertEqual(b'splnodtb with microc' + pos_and_size +
1468 b'ter somewhere in here', first)
Simon Glass3d274232017-11-12 21:52:27 -07001469
Simon Glass6ba679c2018-07-06 10:27:17 -06001470 def testPackUbootSplMicrocode(self):
1471 """Test that x86 microcode can be handled correctly in SPL"""
Marek Vasutf7413f02023-07-18 07:23:58 -06001472 self._SetupSplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001473 self._PackUbootSplMicrocode('049_x86_ucode_spl.dts')
Simon Glass6ba679c2018-07-06 10:27:17 -06001474
1475 def testPackUbootSplMicrocodeReorder(self):
1476 """Test that order doesn't matter for microcode entries
1477
1478 This is the same as testPackUbootSplMicrocode but when we process the
1479 u-boot-ucode entry we have not yet seen the u-boot-dtb-with-ucode
1480 entry, so we reply on binman to try later.
1481 """
Simon Glass511f6582018-10-01 12:22:30 -06001482 self._PackUbootSplMicrocode('058_x86_ucode_spl_needs_retry.dts',
Simon Glass6ba679c2018-07-06 10:27:17 -06001483 ucode_second=True)
1484
Simon Glassa409c932017-11-12 21:52:28 -07001485 def testPackMrc(self):
1486 """Test that an image with an MRC binary can be created"""
Simon Glass511f6582018-10-01 12:22:30 -06001487 data = self._DoReadFile('050_intel_mrc.dts')
Simon Glassa409c932017-11-12 21:52:28 -07001488 self.assertEqual(MRC_DATA, data[:len(MRC_DATA)])
1489
Simon Glass9aa6a6f2017-11-13 18:54:55 -07001490 def testSplDtb(self):
1491 """Test that an image with spl/u-boot-spl.dtb can be created"""
Marek Vasutf7413f02023-07-18 07:23:58 -06001492 self._SetupSplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001493 data = self._DoReadFile('051_u_boot_spl_dtb.dts')
Simon Glass9aa6a6f2017-11-13 18:54:55 -07001494 self.assertEqual(U_BOOT_SPL_DTB_DATA, data[:len(U_BOOT_SPL_DTB_DATA)])
1495
Simon Glass0a6da312017-11-13 18:54:56 -07001496 def testSplNoDtb(self):
1497 """Test that an image with spl/u-boot-spl-nodtb.bin can be created"""
Simon Glass13089cc2021-04-25 08:39:32 +12001498 self._SetupSplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001499 data = self._DoReadFile('052_u_boot_spl_nodtb.dts')
Simon Glass0a6da312017-11-13 18:54:56 -07001500 self.assertEqual(U_BOOT_SPL_NODTB_DATA, data[:len(U_BOOT_SPL_NODTB_DATA)])
1501
Simon Glass7098b7f2021-03-21 18:24:30 +13001502 def checkSymbols(self, dts, base_data, u_boot_offset, entry_args=None,
Simon Glass4abf7842023-07-18 07:23:54 -06001503 use_expanded=False, no_write_symbols=False):
Simon Glass31e04cb2021-03-18 20:24:56 +13001504 """Check the image contains the expected symbol values
1505
1506 Args:
1507 dts: Device tree file to use for test
1508 base_data: Data before and after 'u-boot' section
1509 u_boot_offset: Offset of 'u-boot' section in image
Simon Glass7098b7f2021-03-21 18:24:30 +13001510 entry_args: Dict of entry args to supply to binman
1511 key: arg name
1512 value: value of that arg
1513 use_expanded: True to use expanded entries where available, e.g.
1514 'u-boot-expanded' instead of 'u-boot'
Simon Glass31e04cb2021-03-18 20:24:56 +13001515 """
Simon Glass5d0c0262019-08-24 07:22:56 -06001516 elf_fname = self.ElfTestFile('u_boot_binman_syms')
Simon Glass4ca8e042017-11-13 18:55:01 -07001517 syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
1518 addr = elf.GetSymbolAddress(elf_fname, '__image_copy_start')
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03001519 self.assertEqual(syms['_binman_sym_magic'].address, addr)
Simon Glass31e04cb2021-03-18 20:24:56 +13001520 self.assertEqual(syms['_binman_u_boot_spl_any_prop_offset'].address,
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03001521 addr + 4)
Simon Glass4ca8e042017-11-13 18:55:01 -07001522
Simon Glass7057d022018-10-01 21:12:47 -06001523 self._SetupSplElf('u_boot_binman_syms')
Simon Glass7098b7f2021-03-21 18:24:30 +13001524 data = self._DoReadFileDtb(dts, entry_args=entry_args,
1525 use_expanded=use_expanded)[0]
Simon Glass31e04cb2021-03-18 20:24:56 +13001526 # The image should contain the symbols from u_boot_binman_syms.c
1527 # Note that image_pos is adjusted by the base address of the image,
1528 # which is 0x10 in our test image
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03001529 sym_values = struct.pack('<LLQLL', elf.BINMAN_SYM_MAGIC_VALUE,
1530 0x00, u_boot_offset + len(U_BOOT_DATA),
Simon Glass31e04cb2021-03-18 20:24:56 +13001531 0x10 + u_boot_offset, 0x04)
Simon Glass4abf7842023-07-18 07:23:54 -06001532 if no_write_symbols:
1533 expected = (base_data +
1534 tools.get_bytes(0xff, 0x38 - len(base_data)) +
1535 U_BOOT_DATA + base_data)
1536 else:
1537 expected = (sym_values + base_data[24:] +
1538 tools.get_bytes(0xff, 1) + U_BOOT_DATA + sym_values +
1539 base_data[24:])
Simon Glass4ca8e042017-11-13 18:55:01 -07001540 self.assertEqual(expected, data)
1541
Simon Glass31e04cb2021-03-18 20:24:56 +13001542 def testSymbols(self):
1543 """Test binman can assign symbols embedded in U-Boot"""
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03001544 self.checkSymbols('053_symbols.dts', U_BOOT_SPL_DATA, 0x1c)
Simon Glass31e04cb2021-03-18 20:24:56 +13001545
1546 def testSymbolsNoDtb(self):
1547 """Test binman can assign symbols embedded in U-Boot SPL"""
Simon Glass3bbc9932021-03-21 18:24:29 +13001548 self.checkSymbols('196_symbols_nodtb.dts',
Simon Glass31e04cb2021-03-18 20:24:56 +13001549 U_BOOT_SPL_NODTB_DATA + U_BOOT_SPL_DTB_DATA,
1550 0x38)
1551
Simon Glasse76a3e62018-06-01 09:38:11 -06001552 def testPackUnitAddress(self):
1553 """Test that we support multiple binaries with the same name"""
Simon Glass511f6582018-10-01 12:22:30 -06001554 data = self._DoReadFile('054_unit_address.dts')
Simon Glasse76a3e62018-06-01 09:38:11 -06001555 self.assertEqual(U_BOOT_DATA + U_BOOT_DATA, data)
1556
Simon Glassa91e1152018-06-01 09:38:16 -06001557 def testSections(self):
1558 """Basic test of sections"""
Simon Glass511f6582018-10-01 12:22:30 -06001559 data = self._DoReadFile('055_sections.dts')
Simon Glass80025522022-01-29 14:14:04 -07001560 expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) +
1561 U_BOOT_DATA + tools.get_bytes(ord('a'), 12) +
1562 U_BOOT_DATA + tools.get_bytes(ord('&'), 4))
Simon Glassa91e1152018-06-01 09:38:16 -06001563 self.assertEqual(expected, data)
Simon Glassac599912017-11-12 21:52:22 -07001564
Simon Glass30732662018-06-01 09:38:20 -06001565 def testMap(self):
1566 """Tests outputting a map of the images"""
Simon Glass511f6582018-10-01 12:22:30 -06001567 _, _, map_data, _ = self._DoReadFileDtb('055_sections.dts', map=True)
Simon Glass7eca7922018-07-17 13:25:49 -06001568 self.assertEqual('''ImagePos Offset Size Name
Simon Glass49cd2b32023-02-07 14:34:18 -0700156900000000 00000000 00000028 image
Simon Glass7eca7922018-07-17 13:25:49 -0600157000000000 00000000 00000010 section@0
157100000000 00000000 00000004 u-boot
157200000010 00000010 00000010 section@1
157300000010 00000000 00000004 u-boot
157400000020 00000020 00000004 section@2
157500000020 00000000 00000004 u-boot
Simon Glass30732662018-06-01 09:38:20 -06001576''', map_data)
1577
Simon Glass3b78d532018-06-01 09:38:21 -06001578 def testNamePrefix(self):
1579 """Tests that name prefixes are used"""
Simon Glass511f6582018-10-01 12:22:30 -06001580 _, _, map_data, _ = self._DoReadFileDtb('056_name_prefix.dts', map=True)
Simon Glass7eca7922018-07-17 13:25:49 -06001581 self.assertEqual('''ImagePos Offset Size Name
Simon Glass49cd2b32023-02-07 14:34:18 -0700158200000000 00000000 00000028 image
Simon Glass7eca7922018-07-17 13:25:49 -0600158300000000 00000000 00000010 section@0
158400000000 00000000 00000004 ro-u-boot
158500000010 00000010 00000010 section@1
158600000010 00000000 00000004 rw-u-boot
Simon Glass3b78d532018-06-01 09:38:21 -06001587''', map_data)
1588
Simon Glass6ba679c2018-07-06 10:27:17 -06001589 def testUnknownContents(self):
1590 """Test that obtaining the contents works as expected"""
1591 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001592 self._DoReadFile('057_unknown_contents.dts', True)
Simon Glass39dd2152019-07-08 14:25:47 -06001593 self.assertIn("Image '/binman': Internal error: Could not complete "
Simon Glassc585dd42020-04-17 18:09:03 -06001594 "processing of contents: remaining ["
1595 "<binman.etype._testing.Entry__testing ", str(e.exception))
Simon Glass6ba679c2018-07-06 10:27:17 -06001596
Simon Glass2e1169f2018-07-06 10:27:19 -06001597 def testBadChangeSize(self):
1598 """Test that trying to change the size of an entry fails"""
Simon Glasse61b6f62019-07-08 14:25:37 -06001599 try:
1600 state.SetAllowEntryExpansion(False)
1601 with self.assertRaises(ValueError) as e:
1602 self._DoReadFile('059_change_size.dts', True)
Simon Glass8c702fb2019-07-20 12:23:57 -06001603 self.assertIn("Node '/binman/_testing': Cannot update entry size from 2 to 3",
Simon Glasse61b6f62019-07-08 14:25:37 -06001604 str(e.exception))
1605 finally:
1606 state.SetAllowEntryExpansion(True)
Simon Glass2e1169f2018-07-06 10:27:19 -06001607
Simon Glassa87014e2018-07-06 10:27:42 -06001608 def testUpdateFdt(self):
Simon Glasse8561af2018-08-01 15:22:37 -06001609 """Test that we can update the device tree with offset/size info"""
Simon Glass511f6582018-10-01 12:22:30 -06001610 _, _, _, out_dtb_fname = self._DoReadFileDtb('060_fdt_update.dts',
Simon Glassa87014e2018-07-06 10:27:42 -06001611 update_dtb=True)
Simon Glass5463a6a2018-07-17 13:25:52 -06001612 dtb = fdt.Fdt(out_dtb_fname)
1613 dtb.Scan()
Simon Glassfb30e292019-07-20 12:23:51 -06001614 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
Simon Glassa87014e2018-07-06 10:27:42 -06001615 self.assertEqual({
Simon Glass9dcc8612018-08-01 15:22:42 -06001616 'image-pos': 0,
Simon Glass3a9a2b82018-07-17 13:25:28 -06001617 'offset': 0,
Simon Glasse8561af2018-08-01 15:22:37 -06001618 '_testing:offset': 32,
Simon Glass8c702fb2019-07-20 12:23:57 -06001619 '_testing:size': 2,
Simon Glass9dcc8612018-08-01 15:22:42 -06001620 '_testing:image-pos': 32,
Simon Glasse8561af2018-08-01 15:22:37 -06001621 'section@0/u-boot:offset': 0,
Simon Glassa87014e2018-07-06 10:27:42 -06001622 'section@0/u-boot:size': len(U_BOOT_DATA),
Simon Glass9dcc8612018-08-01 15:22:42 -06001623 'section@0/u-boot:image-pos': 0,
Simon Glasse8561af2018-08-01 15:22:37 -06001624 'section@0:offset': 0,
Simon Glassa87014e2018-07-06 10:27:42 -06001625 'section@0:size': 16,
Simon Glass9dcc8612018-08-01 15:22:42 -06001626 'section@0:image-pos': 0,
Simon Glassa87014e2018-07-06 10:27:42 -06001627
Simon Glasse8561af2018-08-01 15:22:37 -06001628 'section@1/u-boot:offset': 0,
Simon Glassa87014e2018-07-06 10:27:42 -06001629 'section@1/u-boot:size': len(U_BOOT_DATA),
Simon Glass9dcc8612018-08-01 15:22:42 -06001630 'section@1/u-boot:image-pos': 16,
Simon Glasse8561af2018-08-01 15:22:37 -06001631 'section@1:offset': 16,
Simon Glassa87014e2018-07-06 10:27:42 -06001632 'section@1:size': 16,
Simon Glass9dcc8612018-08-01 15:22:42 -06001633 'section@1:image-pos': 16,
Simon Glassa87014e2018-07-06 10:27:42 -06001634 'size': 40
1635 }, props)
1636
1637 def testUpdateFdtBad(self):
1638 """Test that we detect when ProcessFdt never completes"""
1639 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001640 self._DoReadFileDtb('061_fdt_update_bad.dts', update_dtb=True)
Simon Glassa87014e2018-07-06 10:27:42 -06001641 self.assertIn('Could not complete processing of Fdt: remaining '
Simon Glassc585dd42020-04-17 18:09:03 -06001642 '[<binman.etype._testing.Entry__testing',
1643 str(e.exception))
Simon Glass2e1169f2018-07-06 10:27:19 -06001644
Simon Glass91710b32018-07-17 13:25:32 -06001645 def testEntryArgs(self):
1646 """Test passing arguments to entries from the command line"""
1647 entry_args = {
1648 'test-str-arg': 'test1',
1649 'test-int-arg': '456',
1650 }
Simon Glass511f6582018-10-01 12:22:30 -06001651 self._DoReadFileDtb('062_entry_args.dts', entry_args=entry_args)
Simon Glass91710b32018-07-17 13:25:32 -06001652 self.assertIn('image', control.images)
1653 entry = control.images['image'].GetEntries()['_testing']
1654 self.assertEqual('test0', entry.test_str_fdt)
1655 self.assertEqual('test1', entry.test_str_arg)
1656 self.assertEqual(123, entry.test_int_fdt)
1657 self.assertEqual(456, entry.test_int_arg)
1658
1659 def testEntryArgsMissing(self):
1660 """Test missing arguments and properties"""
1661 entry_args = {
1662 'test-int-arg': '456',
1663 }
Simon Glass511f6582018-10-01 12:22:30 -06001664 self._DoReadFileDtb('063_entry_args_missing.dts', entry_args=entry_args)
Simon Glass91710b32018-07-17 13:25:32 -06001665 entry = control.images['image'].GetEntries()['_testing']
1666 self.assertEqual('test0', entry.test_str_fdt)
1667 self.assertEqual(None, entry.test_str_arg)
1668 self.assertEqual(None, entry.test_int_fdt)
1669 self.assertEqual(456, entry.test_int_arg)
1670
1671 def testEntryArgsRequired(self):
1672 """Test missing arguments and properties"""
1673 entry_args = {
1674 'test-int-arg': '456',
1675 }
1676 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001677 self._DoReadFileDtb('064_entry_args_required.dts')
Simon Glass21db0ff2020-09-01 05:13:54 -06001678 self.assertIn("Node '/binman/_testing': "
1679 'Missing required properties/entry args: test-str-arg, '
1680 'test-int-fdt, test-int-arg',
Simon Glass91710b32018-07-17 13:25:32 -06001681 str(e.exception))
1682
1683 def testEntryArgsInvalidFormat(self):
1684 """Test that an invalid entry-argument format is detected"""
Simon Glassf46732a2019-07-08 14:25:29 -06001685 args = ['build', '-d', self.TestFile('064_entry_args_required.dts'),
1686 '-ano-value']
Simon Glass91710b32018-07-17 13:25:32 -06001687 with self.assertRaises(ValueError) as e:
1688 self._DoBinman(*args)
1689 self.assertIn("Invalid entry arguemnt 'no-value'", str(e.exception))
1690
1691 def testEntryArgsInvalidInteger(self):
1692 """Test that an invalid entry-argument integer is detected"""
1693 entry_args = {
1694 'test-int-arg': 'abc',
1695 }
1696 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001697 self._DoReadFileDtb('062_entry_args.dts', entry_args=entry_args)
Simon Glass91710b32018-07-17 13:25:32 -06001698 self.assertIn("Node '/binman/_testing': Cannot convert entry arg "
1699 "'test-int-arg' (value 'abc') to integer",
1700 str(e.exception))
1701
1702 def testEntryArgsInvalidDatatype(self):
1703 """Test that an invalid entry-argument datatype is detected
1704
1705 This test could be written in entry_test.py except that it needs
1706 access to control.entry_args, which seems more than that module should
1707 be able to see.
1708 """
1709 entry_args = {
1710 'test-bad-datatype-arg': '12',
1711 }
1712 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001713 self._DoReadFileDtb('065_entry_args_unknown_datatype.dts',
Simon Glass91710b32018-07-17 13:25:32 -06001714 entry_args=entry_args)
1715 self.assertIn('GetArg() internal error: Unknown data type ',
1716 str(e.exception))
1717
Simon Glass2ca52032018-07-17 13:25:33 -06001718 def testText(self):
1719 """Test for a text entry type"""
1720 entry_args = {
1721 'test-id': TEXT_DATA,
1722 'test-id2': TEXT_DATA2,
1723 'test-id3': TEXT_DATA3,
1724 }
Simon Glass511f6582018-10-01 12:22:30 -06001725 data, _, _, _ = self._DoReadFileDtb('066_text.dts',
Simon Glass2ca52032018-07-17 13:25:33 -06001726 entry_args=entry_args)
Simon Glass80025522022-01-29 14:14:04 -07001727 expected = (tools.to_bytes(TEXT_DATA) +
1728 tools.get_bytes(0, 8 - len(TEXT_DATA)) +
1729 tools.to_bytes(TEXT_DATA2) + tools.to_bytes(TEXT_DATA3) +
Simon Glass47f6a622019-07-08 13:18:40 -06001730 b'some text' + b'more text')
Simon Glass2ca52032018-07-17 13:25:33 -06001731 self.assertEqual(expected, data)
1732
Simon Glass969616c2018-07-17 13:25:36 -06001733 def testEntryDocs(self):
1734 """Test for creation of entry documentation"""
1735 with test_util.capture_sys_output() as (stdout, stderr):
Simon Glass220ff5f2020-08-05 13:27:46 -06001736 control.WriteEntryDocs(control.GetEntryModules())
Simon Glass969616c2018-07-17 13:25:36 -06001737 self.assertTrue(len(stdout.getvalue()) > 0)
1738
1739 def testEntryDocsMissing(self):
1740 """Test handling of missing entry documentation"""
1741 with self.assertRaises(ValueError) as e:
1742 with test_util.capture_sys_output() as (stdout, stderr):
Simon Glass220ff5f2020-08-05 13:27:46 -06001743 control.WriteEntryDocs(control.GetEntryModules(), 'u_boot')
Simon Glass969616c2018-07-17 13:25:36 -06001744 self.assertIn('Documentation is missing for modules: u_boot',
1745 str(e.exception))
1746
Simon Glass704784b2018-07-17 13:25:38 -06001747 def testFmap(self):
1748 """Basic test of generation of a flashrom fmap"""
Simon Glass511f6582018-10-01 12:22:30 -06001749 data = self._DoReadFile('067_fmap.dts')
Simon Glass704784b2018-07-17 13:25:38 -06001750 fhdr, fentries = fmap_util.DecodeFmap(data[32:])
Simon Glass80025522022-01-29 14:14:04 -07001751 expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) +
1752 U_BOOT_DATA + tools.get_bytes(ord('a'), 12))
Simon Glass704784b2018-07-17 13:25:38 -06001753 self.assertEqual(expected, data[:32])
Simon Glass303f62f2019-05-17 22:00:46 -06001754 self.assertEqual(b'__FMAP__', fhdr.signature)
Simon Glass704784b2018-07-17 13:25:38 -06001755 self.assertEqual(1, fhdr.ver_major)
1756 self.assertEqual(0, fhdr.ver_minor)
1757 self.assertEqual(0, fhdr.base)
Simon Glassb1d414c2021-04-03 11:05:10 +13001758 expect_size = fmap_util.FMAP_HEADER_LEN + fmap_util.FMAP_AREA_LEN * 5
Simon Glass82059c22021-04-03 11:05:09 +13001759 self.assertEqual(16 + 16 + expect_size, fhdr.image_size)
Simon Glass303f62f2019-05-17 22:00:46 -06001760 self.assertEqual(b'FMAP', fhdr.name)
Simon Glassb1d414c2021-04-03 11:05:10 +13001761 self.assertEqual(5, fhdr.nareas)
Simon Glass82059c22021-04-03 11:05:09 +13001762 fiter = iter(fentries)
Simon Glass704784b2018-07-17 13:25:38 -06001763
Simon Glass82059c22021-04-03 11:05:09 +13001764 fentry = next(fiter)
Simon Glassb1d414c2021-04-03 11:05:10 +13001765 self.assertEqual(b'SECTION0', fentry.name)
1766 self.assertEqual(0, fentry.offset)
1767 self.assertEqual(16, fentry.size)
Simon Glasscda991e2023-02-12 17:11:15 -07001768 self.assertEqual(fmap_util.FMAP_AREA_PRESERVE, fentry.flags)
Simon Glassb1d414c2021-04-03 11:05:10 +13001769
1770 fentry = next(fiter)
Simon Glass82059c22021-04-03 11:05:09 +13001771 self.assertEqual(b'RO_U_BOOT', fentry.name)
1772 self.assertEqual(0, fentry.offset)
1773 self.assertEqual(4, fentry.size)
1774 self.assertEqual(0, fentry.flags)
Simon Glass704784b2018-07-17 13:25:38 -06001775
Simon Glass82059c22021-04-03 11:05:09 +13001776 fentry = next(fiter)
Simon Glassb1d414c2021-04-03 11:05:10 +13001777 self.assertEqual(b'SECTION1', fentry.name)
1778 self.assertEqual(16, fentry.offset)
1779 self.assertEqual(16, fentry.size)
1780 self.assertEqual(0, fentry.flags)
1781
1782 fentry = next(fiter)
Simon Glass82059c22021-04-03 11:05:09 +13001783 self.assertEqual(b'RW_U_BOOT', fentry.name)
1784 self.assertEqual(16, fentry.offset)
1785 self.assertEqual(4, fentry.size)
1786 self.assertEqual(0, fentry.flags)
Simon Glass704784b2018-07-17 13:25:38 -06001787
Simon Glass82059c22021-04-03 11:05:09 +13001788 fentry = next(fiter)
1789 self.assertEqual(b'FMAP', fentry.name)
1790 self.assertEqual(32, fentry.offset)
1791 self.assertEqual(expect_size, fentry.size)
1792 self.assertEqual(0, fentry.flags)
Simon Glass704784b2018-07-17 13:25:38 -06001793
Simon Glassdb168d42018-07-17 13:25:39 -06001794 def testBlobNamedByArg(self):
1795 """Test we can add a blob with the filename coming from an entry arg"""
1796 entry_args = {
1797 'cros-ec-rw-path': 'ecrw.bin',
1798 }
Simon Glass21db0ff2020-09-01 05:13:54 -06001799 self._DoReadFileDtb('068_blob_named_by_arg.dts', entry_args=entry_args)
Simon Glassdb168d42018-07-17 13:25:39 -06001800
Simon Glass53f53992018-07-17 13:25:40 -06001801 def testFill(self):
1802 """Test for an fill entry type"""
Simon Glass511f6582018-10-01 12:22:30 -06001803 data = self._DoReadFile('069_fill.dts')
Simon Glass80025522022-01-29 14:14:04 -07001804 expected = tools.get_bytes(0xff, 8) + tools.get_bytes(0, 8)
Simon Glass53f53992018-07-17 13:25:40 -06001805 self.assertEqual(expected, data)
1806
1807 def testFillNoSize(self):
1808 """Test for an fill entry type with no size"""
1809 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001810 self._DoReadFile('070_fill_no_size.dts')
Simon Glass0cf5bce2022-08-13 11:40:44 -06001811 self.assertIn("'fill' entry is missing properties: size",
Simon Glass53f53992018-07-17 13:25:40 -06001812 str(e.exception))
1813
Simon Glassc1ae83c2018-07-17 13:25:44 -06001814 def _HandleGbbCommand(self, pipe_list):
1815 """Fake calls to the futility utility"""
Simon Glass9a1c7262023-02-22 12:14:49 -07001816 if 'futility' in pipe_list[0][0]:
Simon Glassc1ae83c2018-07-17 13:25:44 -06001817 fname = pipe_list[0][-1]
1818 # Append our GBB data to the file, which will happen every time the
1819 # futility command is called.
Simon Glass33486662019-05-14 15:53:42 -06001820 with open(fname, 'ab') as fd:
Simon Glassc1ae83c2018-07-17 13:25:44 -06001821 fd.write(GBB_DATA)
1822 return command.CommandResult()
1823
1824 def testGbb(self):
1825 """Test for the Chromium OS Google Binary Block"""
1826 command.test_result = self._HandleGbbCommand
1827 entry_args = {
1828 'keydir': 'devkeys',
1829 'bmpblk': 'bmpblk.bin',
1830 }
Simon Glass511f6582018-10-01 12:22:30 -06001831 data, _, _, _ = self._DoReadFileDtb('071_gbb.dts', entry_args=entry_args)
Simon Glassc1ae83c2018-07-17 13:25:44 -06001832
1833 # Since futility
Simon Glass80025522022-01-29 14:14:04 -07001834 expected = (GBB_DATA + GBB_DATA + tools.get_bytes(0, 8) +
1835 tools.get_bytes(0, 0x2180 - 16))
Simon Glassc1ae83c2018-07-17 13:25:44 -06001836 self.assertEqual(expected, data)
1837
1838 def testGbbTooSmall(self):
1839 """Test for the Chromium OS Google Binary Block being large enough"""
1840 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001841 self._DoReadFileDtb('072_gbb_too_small.dts')
Simon Glassc1ae83c2018-07-17 13:25:44 -06001842 self.assertIn("Node '/binman/gbb': GBB is too small",
1843 str(e.exception))
1844
1845 def testGbbNoSize(self):
1846 """Test for the Chromium OS Google Binary Block having a size"""
1847 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001848 self._DoReadFileDtb('073_gbb_no_size.dts')
Simon Glassc1ae83c2018-07-17 13:25:44 -06001849 self.assertIn("Node '/binman/gbb': GBB must have a fixed size",
1850 str(e.exception))
1851
Simon Glass66152ce2022-01-09 20:14:09 -07001852 def testGbbMissing(self):
1853 """Test that binman still produces an image if futility is missing"""
1854 entry_args = {
1855 'keydir': 'devkeys',
1856 }
1857 with test_util.capture_sys_output() as (_, stderr):
1858 self._DoTestFile('071_gbb.dts', force_missing_bintools='futility',
1859 entry_args=entry_args)
1860 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07001861 self.assertRegex(err, "Image 'image'.*missing bintools.*: futility")
Simon Glass66152ce2022-01-09 20:14:09 -07001862
Simon Glass5c350162018-07-17 13:25:47 -06001863 def _HandleVblockCommand(self, pipe_list):
Simon Glass220c6222021-01-06 21:35:17 -07001864 """Fake calls to the futility utility
1865
1866 The expected pipe is:
1867
1868 [('futility', 'vbutil_firmware', '--vblock',
1869 'vblock.vblock', '--keyblock', 'devkeys/firmware.keyblock',
1870 '--signprivate', 'devkeys/firmware_data_key.vbprivk',
1871 '--version', '1', '--fv', 'input.vblock', '--kernelkey',
1872 'devkeys/kernel_subkey.vbpubk', '--flags', '1')]
1873
1874 This writes to the output file (here, 'vblock.vblock'). If
1875 self._hash_data is False, it writes VBLOCK_DATA, else it writes a hash
1876 of the input data (here, 'input.vblock').
1877 """
Simon Glass9a1c7262023-02-22 12:14:49 -07001878 if 'futility' in pipe_list[0][0]:
Simon Glass5c350162018-07-17 13:25:47 -06001879 fname = pipe_list[0][3]
Simon Glass639505b2018-09-14 04:57:11 -06001880 with open(fname, 'wb') as fd:
Simon Glass220c6222021-01-06 21:35:17 -07001881 if self._hash_data:
1882 infile = pipe_list[0][11]
1883 m = hashlib.sha256()
Simon Glass80025522022-01-29 14:14:04 -07001884 data = tools.read_file(infile)
Simon Glass220c6222021-01-06 21:35:17 -07001885 m.update(data)
1886 fd.write(m.digest())
1887 else:
1888 fd.write(VBLOCK_DATA)
1889
Simon Glass5c350162018-07-17 13:25:47 -06001890 return command.CommandResult()
1891
1892 def testVblock(self):
1893 """Test for the Chromium OS Verified Boot Block"""
Simon Glass220c6222021-01-06 21:35:17 -07001894 self._hash_data = False
Simon Glass5c350162018-07-17 13:25:47 -06001895 command.test_result = self._HandleVblockCommand
1896 entry_args = {
1897 'keydir': 'devkeys',
1898 }
Simon Glass511f6582018-10-01 12:22:30 -06001899 data, _, _, _ = self._DoReadFileDtb('074_vblock.dts',
Simon Glass5c350162018-07-17 13:25:47 -06001900 entry_args=entry_args)
1901 expected = U_BOOT_DATA + VBLOCK_DATA + U_BOOT_DTB_DATA
1902 self.assertEqual(expected, data)
1903
1904 def testVblockNoContent(self):
1905 """Test we detect a vblock which has no content to sign"""
1906 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001907 self._DoReadFile('075_vblock_no_content.dts')
Simon Glasse1915782021-03-21 18:24:31 +13001908 self.assertIn("Node '/binman/vblock': Collection must have a 'content' "
Simon Glass5c350162018-07-17 13:25:47 -06001909 'property', str(e.exception))
1910
1911 def testVblockBadPhandle(self):
1912 """Test that we detect a vblock with an invalid phandle in contents"""
1913 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001914 self._DoReadFile('076_vblock_bad_phandle.dts')
Simon Glass5c350162018-07-17 13:25:47 -06001915 self.assertIn("Node '/binman/vblock': Cannot find node for phandle "
1916 '1000', str(e.exception))
1917
1918 def testVblockBadEntry(self):
1919 """Test that we detect an entry that points to a non-entry"""
1920 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001921 self._DoReadFile('077_vblock_bad_entry.dts')
Simon Glass5c350162018-07-17 13:25:47 -06001922 self.assertIn("Node '/binman/vblock': Cannot find entry for node "
1923 "'other'", str(e.exception))
1924
Simon Glass220c6222021-01-06 21:35:17 -07001925 def testVblockContent(self):
1926 """Test that the vblock signs the right data"""
1927 self._hash_data = True
1928 command.test_result = self._HandleVblockCommand
1929 entry_args = {
1930 'keydir': 'devkeys',
1931 }
1932 data = self._DoReadFileDtb(
1933 '189_vblock_content.dts', use_real_dtb=True, update_dtb=True,
1934 entry_args=entry_args)[0]
1935 hashlen = 32 # SHA256 hash is 32 bytes
1936 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
1937 hashval = data[-hashlen:]
1938 dtb = data[len(U_BOOT_DATA):-hashlen]
1939
1940 expected_data = U_BOOT_DATA + dtb
1941
1942 # The hashval should be a hash of the dtb
1943 m = hashlib.sha256()
1944 m.update(expected_data)
1945 expected_hashval = m.digest()
1946 self.assertEqual(expected_hashval, hashval)
1947
Simon Glass66152ce2022-01-09 20:14:09 -07001948 def testVblockMissing(self):
1949 """Test that binman still produces an image if futility is missing"""
1950 entry_args = {
1951 'keydir': 'devkeys',
1952 }
1953 with test_util.capture_sys_output() as (_, stderr):
1954 self._DoTestFile('074_vblock.dts',
1955 force_missing_bintools='futility',
1956 entry_args=entry_args)
1957 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07001958 self.assertRegex(err, "Image 'image'.*missing bintools.*: futility")
Simon Glass66152ce2022-01-09 20:14:09 -07001959
Simon Glass8425a1f2018-07-17 13:25:48 -06001960 def testTpl(self):
Simon Glass3eb5b202019-08-24 07:23:00 -06001961 """Test that an image with TPL and its device tree can be created"""
Simon Glass8425a1f2018-07-17 13:25:48 -06001962 # ELF file with a '__bss_size' symbol
Simon Glass3eb5b202019-08-24 07:23:00 -06001963 self._SetupTplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001964 data = self._DoReadFile('078_u_boot_tpl.dts')
Simon Glass8425a1f2018-07-17 13:25:48 -06001965 self.assertEqual(U_BOOT_TPL_DATA + U_BOOT_TPL_DTB_DATA, data)
1966
Simon Glass24b97442018-07-17 13:25:51 -06001967 def testUsesPos(self):
1968 """Test that the 'pos' property cannot be used anymore"""
1969 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001970 data = self._DoReadFile('079_uses_pos.dts')
Simon Glass24b97442018-07-17 13:25:51 -06001971 self.assertIn("Node '/binman/u-boot': Please use 'offset' instead of "
1972 "'pos'", str(e.exception))
1973
Simon Glass274bf092018-09-14 04:57:08 -06001974 def testFillZero(self):
1975 """Test for an fill entry type with a size of 0"""
Simon Glass511f6582018-10-01 12:22:30 -06001976 data = self._DoReadFile('080_fill_empty.dts')
Simon Glass80025522022-01-29 14:14:04 -07001977 self.assertEqual(tools.get_bytes(0, 16), data)
Simon Glass274bf092018-09-14 04:57:08 -06001978
Simon Glass267de432018-09-14 04:57:09 -06001979 def testTextMissing(self):
1980 """Test for a text entry type where there is no text"""
1981 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001982 self._DoReadFileDtb('066_text.dts',)
Simon Glass267de432018-09-14 04:57:09 -06001983 self.assertIn("Node '/binman/text': No value provided for text label "
1984 "'test-id'", str(e.exception))
1985
Simon Glassed40e962018-09-14 04:57:10 -06001986 def testPackStart16Tpl(self):
1987 """Test that an image with an x86 start16 TPL region can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001988 data = self._DoReadFile('081_x86_start16_tpl.dts')
Simon Glassed40e962018-09-14 04:57:10 -06001989 self.assertEqual(X86_START16_TPL_DATA, data[:len(X86_START16_TPL_DATA)])
1990
Simon Glass3b376c32018-09-14 04:57:12 -06001991 def testSelectImage(self):
1992 """Test that we can select which images to build"""
Simon Glassb4595d82019-04-25 21:58:34 -06001993 expected = 'Skipping images: image1'
1994
1995 # We should only get the expected message in verbose mode
Simon Glass8a50b4a2019-07-08 13:18:48 -06001996 for verbosity in (0, 2):
Simon Glassb4595d82019-04-25 21:58:34 -06001997 with test_util.capture_sys_output() as (stdout, stderr):
1998 retcode = self._DoTestFile('006_dual_image.dts',
1999 verbosity=verbosity,
2000 images=['image2'])
2001 self.assertEqual(0, retcode)
2002 if verbosity:
2003 self.assertIn(expected, stdout.getvalue())
2004 else:
2005 self.assertNotIn(expected, stdout.getvalue())
Simon Glass3b376c32018-09-14 04:57:12 -06002006
Simon Glass80025522022-01-29 14:14:04 -07002007 self.assertFalse(os.path.exists(tools.get_output_filename('image1.bin')))
2008 self.assertTrue(os.path.exists(tools.get_output_filename('image2.bin')))
Simon Glassb3d6fc72019-07-20 12:24:10 -06002009 self._CleanupOutputDir()
Simon Glass3b376c32018-09-14 04:57:12 -06002010
Simon Glasse219aa42018-09-14 04:57:24 -06002011 def testUpdateFdtAll(self):
2012 """Test that all device trees are updated with offset/size info"""
Marek Vasutf7413f02023-07-18 07:23:58 -06002013 self._SetupSplElf()
2014 self._SetupTplElf()
Simon Glass5b4bce32019-07-08 14:25:26 -06002015 data = self._DoReadFileRealDtb('082_fdt_update_all.dts')
Simon Glasse219aa42018-09-14 04:57:24 -06002016
2017 base_expected = {
Simon Glasse219aa42018-09-14 04:57:24 -06002018 'offset': 0,
Simon Glass56d05412022-02-28 07:16:54 -07002019 'image-pos': 0,
2020 'size': 2320,
Simon Glasse219aa42018-09-14 04:57:24 -06002021 'section:offset': 0,
Simon Glass56d05412022-02-28 07:16:54 -07002022 'section:image-pos': 0,
2023 'section:size': 565,
2024 'section/u-boot-dtb:offset': 0,
2025 'section/u-boot-dtb:image-pos': 0,
2026 'section/u-boot-dtb:size': 565,
2027 'u-boot-spl-dtb:offset': 565,
2028 'u-boot-spl-dtb:image-pos': 565,
2029 'u-boot-spl-dtb:size': 585,
2030 'u-boot-tpl-dtb:offset': 1150,
2031 'u-boot-tpl-dtb:image-pos': 1150,
2032 'u-boot-tpl-dtb:size': 585,
2033 'u-boot-vpl-dtb:image-pos': 1735,
2034 'u-boot-vpl-dtb:offset': 1735,
2035 'u-boot-vpl-dtb:size': 585,
Simon Glasse219aa42018-09-14 04:57:24 -06002036 }
2037
2038 # We expect three device-tree files in the output, one after the other.
2039 # Read them in sequence. We look for an 'spl' property in the SPL tree,
2040 # and 'tpl' in the TPL tree, to make sure they are distinct from the
2041 # main U-Boot tree. All three should have the same postions and offset.
2042 start = 0
Simon Glass56d05412022-02-28 07:16:54 -07002043 self.maxDiff = None
2044 for item in ['', 'spl', 'tpl', 'vpl']:
Simon Glasse219aa42018-09-14 04:57:24 -06002045 dtb = fdt.Fdt.FromData(data[start:])
2046 dtb.Scan()
Simon Glassfb30e292019-07-20 12:23:51 -06002047 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS +
Simon Glass56d05412022-02-28 07:16:54 -07002048 ['spl', 'tpl', 'vpl'])
Simon Glasse219aa42018-09-14 04:57:24 -06002049 expected = dict(base_expected)
2050 if item:
2051 expected[item] = 0
2052 self.assertEqual(expected, props)
2053 start += dtb._fdt_obj.totalsize()
2054
2055 def testUpdateFdtOutput(self):
2056 """Test that output DTB files are updated"""
2057 try:
Simon Glass511f6582018-10-01 12:22:30 -06002058 data, dtb_data, _, _ = self._DoReadFileDtb('082_fdt_update_all.dts',
Simon Glasse219aa42018-09-14 04:57:24 -06002059 use_real_dtb=True, update_dtb=True, reset_dtbs=False)
2060
2061 # Unfortunately, compiling a source file always results in a file
2062 # called source.dtb (see fdt_util.EnsureCompiled()). The test
Simon Glass511f6582018-10-01 12:22:30 -06002063 # source file (e.g. test/075_fdt_update_all.dts) thus does not enter
Simon Glasse219aa42018-09-14 04:57:24 -06002064 # binman as a file called u-boot.dtb. To fix this, copy the file
2065 # over to the expected place.
Simon Glasse219aa42018-09-14 04:57:24 -06002066 start = 0
2067 for fname in ['u-boot.dtb.out', 'spl/u-boot-spl.dtb.out',
Simon Glass56d05412022-02-28 07:16:54 -07002068 'tpl/u-boot-tpl.dtb.out', 'vpl/u-boot-vpl.dtb.out']:
Simon Glasse219aa42018-09-14 04:57:24 -06002069 dtb = fdt.Fdt.FromData(data[start:])
2070 size = dtb._fdt_obj.totalsize()
Simon Glass80025522022-01-29 14:14:04 -07002071 pathname = tools.get_output_filename(os.path.split(fname)[1])
2072 outdata = tools.read_file(pathname)
Simon Glasse219aa42018-09-14 04:57:24 -06002073 name = os.path.split(fname)[0]
2074
2075 if name:
Simon Glass56d05412022-02-28 07:16:54 -07002076 orig_indata = self._GetDtbContentsForSpls(dtb_data, name)
Simon Glasse219aa42018-09-14 04:57:24 -06002077 else:
2078 orig_indata = dtb_data
2079 self.assertNotEqual(outdata, orig_indata,
2080 "Expected output file '%s' be updated" % pathname)
2081 self.assertEqual(outdata, data[start:start + size],
2082 "Expected output file '%s' to match output image" %
2083 pathname)
2084 start += size
2085 finally:
2086 self._ResetDtbs()
2087
Simon Glass7ba33592018-09-14 04:57:26 -06002088 def _decompress(self, data):
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +02002089 bintool = self.comp_bintools['lz4']
2090 return bintool.decompress(data)
Simon Glass7ba33592018-09-14 04:57:26 -06002091
2092 def testCompress(self):
2093 """Test compression of blobs"""
Simon Glass1de34482019-07-08 13:18:53 -06002094 self._CheckLz4()
Simon Glass511f6582018-10-01 12:22:30 -06002095 data, _, _, out_dtb_fname = self._DoReadFileDtb('083_compress.dts',
Simon Glass7ba33592018-09-14 04:57:26 -06002096 use_real_dtb=True, update_dtb=True)
2097 dtb = fdt.Fdt(out_dtb_fname)
2098 dtb.Scan()
2099 props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
2100 orig = self._decompress(data)
Brandon Maiera657bc62024-06-04 16:16:05 +00002101 self.assertEqual(COMPRESS_DATA, orig)
Simon Glass789b34402020-10-26 17:40:15 -06002102
2103 # Do a sanity check on various fields
2104 image = control.images['image']
2105 entries = image.GetEntries()
2106 self.assertEqual(1, len(entries))
2107
2108 entry = entries['blob']
2109 self.assertEqual(COMPRESS_DATA, entry.uncomp_data)
2110 self.assertEqual(len(COMPRESS_DATA), entry.uncomp_size)
2111 orig = self._decompress(entry.data)
2112 self.assertEqual(orig, entry.uncomp_data)
2113
Simon Glass72eeff12020-10-26 17:40:16 -06002114 self.assertEqual(image.data, entry.data)
2115
Simon Glass7ba33592018-09-14 04:57:26 -06002116 expected = {
2117 'blob:uncomp-size': len(COMPRESS_DATA),
2118 'blob:size': len(data),
2119 'size': len(data),
2120 }
2121 self.assertEqual(expected, props)
2122
Simon Glassac6328c2018-09-14 04:57:28 -06002123 def testFiles(self):
2124 """Test bringing in multiple files"""
Simon Glass511f6582018-10-01 12:22:30 -06002125 data = self._DoReadFile('084_files.dts')
Simon Glassac6328c2018-09-14 04:57:28 -06002126 self.assertEqual(FILES_DATA, data)
2127
2128 def testFilesCompress(self):
2129 """Test bringing in multiple files and compressing them"""
Simon Glass1de34482019-07-08 13:18:53 -06002130 self._CheckLz4()
Simon Glass511f6582018-10-01 12:22:30 -06002131 data = self._DoReadFile('085_files_compress.dts')
Simon Glassac6328c2018-09-14 04:57:28 -06002132
2133 image = control.images['image']
2134 entries = image.GetEntries()
2135 files = entries['files']
Simon Glass39dd2152019-07-08 14:25:47 -06002136 entries = files._entries
Simon Glassac6328c2018-09-14 04:57:28 -06002137
Simon Glass303f62f2019-05-17 22:00:46 -06002138 orig = b''
Simon Glassac6328c2018-09-14 04:57:28 -06002139 for i in range(1, 3):
2140 key = '%d.dat' % i
2141 start = entries[key].image_pos
2142 len = entries[key].size
2143 chunk = data[start:start + len]
2144 orig += self._decompress(chunk)
2145
2146 self.assertEqual(FILES_DATA, orig)
2147
2148 def testFilesMissing(self):
2149 """Test missing files"""
2150 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002151 data = self._DoReadFile('086_files_none.dts')
Simon Glassac6328c2018-09-14 04:57:28 -06002152 self.assertIn("Node '/binman/files': Pattern \'files/*.none\' matched "
2153 'no files', str(e.exception))
2154
2155 def testFilesNoPattern(self):
2156 """Test missing files"""
2157 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002158 data = self._DoReadFile('087_files_no_pattern.dts')
Simon Glassac6328c2018-09-14 04:57:28 -06002159 self.assertIn("Node '/binman/files': Missing 'pattern' property",
2160 str(e.exception))
2161
Simon Glassdd156a42022-03-05 20:18:59 -07002162 def testExtendSize(self):
2163 """Test an extending entry"""
2164 data, _, map_data, _ = self._DoReadFileDtb('088_extend_size.dts',
Simon Glassfa79a812018-09-14 04:57:29 -06002165 map=True)
Simon Glass80025522022-01-29 14:14:04 -07002166 expect = (tools.get_bytes(ord('a'), 8) + U_BOOT_DATA +
2167 MRC_DATA + tools.get_bytes(ord('b'), 1) + U_BOOT_DATA +
2168 tools.get_bytes(ord('c'), 8) + U_BOOT_DATA +
2169 tools.get_bytes(ord('d'), 8))
Simon Glassfa79a812018-09-14 04:57:29 -06002170 self.assertEqual(expect, data)
2171 self.assertEqual('''ImagePos Offset Size Name
Simon Glass49cd2b32023-02-07 14:34:18 -0700217200000000 00000000 00000028 image
Simon Glassfa79a812018-09-14 04:57:29 -0600217300000000 00000000 00000008 fill
217400000008 00000008 00000004 u-boot
21750000000c 0000000c 00000004 section
21760000000c 00000000 00000003 intel-mrc
217700000010 00000010 00000004 u-boot2
217800000014 00000014 0000000c section2
217900000014 00000000 00000008 fill
21800000001c 00000008 00000004 u-boot
218100000020 00000020 00000008 fill2
2182''', map_data)
2183
Simon Glassdd156a42022-03-05 20:18:59 -07002184 def testExtendSizeBad(self):
2185 """Test an extending entry which fails to provide contents"""
Simon Glasscd817d52018-09-14 04:57:36 -06002186 with test_util.capture_sys_output() as (stdout, stderr):
2187 with self.assertRaises(ValueError) as e:
Simon Glassdd156a42022-03-05 20:18:59 -07002188 self._DoReadFileDtb('089_extend_size_bad.dts', map=True)
Simon Glassfa79a812018-09-14 04:57:29 -06002189 self.assertIn("Node '/binman/_testing': Cannot obtain contents when "
2190 'expanding entry', str(e.exception))
2191
Simon Glassae7cf032018-09-14 04:57:31 -06002192 def testHash(self):
2193 """Test hashing of the contents of an entry"""
Simon Glass511f6582018-10-01 12:22:30 -06002194 _, _, _, out_dtb_fname = self._DoReadFileDtb('090_hash.dts',
Simon Glassae7cf032018-09-14 04:57:31 -06002195 use_real_dtb=True, update_dtb=True)
2196 dtb = fdt.Fdt(out_dtb_fname)
2197 dtb.Scan()
2198 hash_node = dtb.GetNode('/binman/u-boot/hash').props['value']
2199 m = hashlib.sha256()
2200 m.update(U_BOOT_DATA)
Simon Glass303f62f2019-05-17 22:00:46 -06002201 self.assertEqual(m.digest(), b''.join(hash_node.value))
Simon Glassae7cf032018-09-14 04:57:31 -06002202
2203 def testHashNoAlgo(self):
2204 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002205 self._DoReadFileDtb('091_hash_no_algo.dts', update_dtb=True)
Simon Glassae7cf032018-09-14 04:57:31 -06002206 self.assertIn("Node \'/binman/u-boot\': Missing \'algo\' property for "
2207 'hash node', str(e.exception))
2208
2209 def testHashBadAlgo(self):
2210 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002211 self._DoReadFileDtb('092_hash_bad_algo.dts', update_dtb=True)
Simon Glass64af7c22022-02-08 10:59:44 -07002212 self.assertIn("Node '/binman/u-boot': Unknown hash algorithm 'invalid'",
Simon Glassae7cf032018-09-14 04:57:31 -06002213 str(e.exception))
2214
2215 def testHashSection(self):
2216 """Test hashing of the contents of an entry"""
Simon Glass511f6582018-10-01 12:22:30 -06002217 _, _, _, out_dtb_fname = self._DoReadFileDtb('099_hash_section.dts',
Simon Glassae7cf032018-09-14 04:57:31 -06002218 use_real_dtb=True, update_dtb=True)
2219 dtb = fdt.Fdt(out_dtb_fname)
2220 dtb.Scan()
2221 hash_node = dtb.GetNode('/binman/section/hash').props['value']
2222 m = hashlib.sha256()
2223 m.update(U_BOOT_DATA)
Simon Glass80025522022-01-29 14:14:04 -07002224 m.update(tools.get_bytes(ord('a'), 16))
Simon Glass303f62f2019-05-17 22:00:46 -06002225 self.assertEqual(m.digest(), b''.join(hash_node.value))
Simon Glassae7cf032018-09-14 04:57:31 -06002226
Simon Glass3fb4f422018-09-14 04:57:32 -06002227 def testPackUBootTplMicrocode(self):
2228 """Test that x86 microcode can be handled correctly in TPL
2229
2230 We expect to see the following in the image, in order:
2231 u-boot-tpl-nodtb.bin with a microcode pointer inserted at the correct
2232 place
2233 u-boot-tpl.dtb with the microcode removed
2234 the microcode
2235 """
Simon Glass3eb5b202019-08-24 07:23:00 -06002236 self._SetupTplElf('u_boot_ucode_ptr')
Simon Glass511f6582018-10-01 12:22:30 -06002237 first, pos_and_size = self._RunMicrocodeTest('093_x86_tpl_ucode.dts',
Simon Glass3fb4f422018-09-14 04:57:32 -06002238 U_BOOT_TPL_NODTB_DATA)
Simon Glass303f62f2019-05-17 22:00:46 -06002239 self.assertEqual(b'tplnodtb with microc' + pos_and_size +
2240 b'ter somewhere in here', first)
Simon Glass3fb4f422018-09-14 04:57:32 -06002241
Simon Glassc64aea52018-09-14 04:57:34 -06002242 def testFmapX86(self):
2243 """Basic test of generation of a flashrom fmap"""
Simon Glass511f6582018-10-01 12:22:30 -06002244 data = self._DoReadFile('094_fmap_x86.dts')
Simon Glassc64aea52018-09-14 04:57:34 -06002245 fhdr, fentries = fmap_util.DecodeFmap(data[32:])
Simon Glass80025522022-01-29 14:14:04 -07002246 expected = U_BOOT_DATA + MRC_DATA + tools.get_bytes(ord('a'), 32 - 7)
Simon Glassc64aea52018-09-14 04:57:34 -06002247 self.assertEqual(expected, data[:32])
2248 fhdr, fentries = fmap_util.DecodeFmap(data[32:])
2249
2250 self.assertEqual(0x100, fhdr.image_size)
2251
2252 self.assertEqual(0, fentries[0].offset)
2253 self.assertEqual(4, fentries[0].size)
Simon Glass303f62f2019-05-17 22:00:46 -06002254 self.assertEqual(b'U_BOOT', fentries[0].name)
Simon Glassc64aea52018-09-14 04:57:34 -06002255
2256 self.assertEqual(4, fentries[1].offset)
2257 self.assertEqual(3, fentries[1].size)
Simon Glass303f62f2019-05-17 22:00:46 -06002258 self.assertEqual(b'INTEL_MRC', fentries[1].name)
Simon Glassc64aea52018-09-14 04:57:34 -06002259
2260 self.assertEqual(32, fentries[2].offset)
2261 self.assertEqual(fmap_util.FMAP_HEADER_LEN +
2262 fmap_util.FMAP_AREA_LEN * 3, fentries[2].size)
Simon Glass303f62f2019-05-17 22:00:46 -06002263 self.assertEqual(b'FMAP', fentries[2].name)
Simon Glassc64aea52018-09-14 04:57:34 -06002264
2265 def testFmapX86Section(self):
2266 """Basic test of generation of a flashrom fmap"""
Simon Glass511f6582018-10-01 12:22:30 -06002267 data = self._DoReadFile('095_fmap_x86_section.dts')
Simon Glass80025522022-01-29 14:14:04 -07002268 expected = U_BOOT_DATA + MRC_DATA + tools.get_bytes(ord('b'), 32 - 7)
Simon Glassc64aea52018-09-14 04:57:34 -06002269 self.assertEqual(expected, data[:32])
2270 fhdr, fentries = fmap_util.DecodeFmap(data[36:])
2271
Simon Glassb1d414c2021-04-03 11:05:10 +13002272 self.assertEqual(0x180, fhdr.image_size)
2273 expect_size = fmap_util.FMAP_HEADER_LEN + fmap_util.FMAP_AREA_LEN * 4
Simon Glass82059c22021-04-03 11:05:09 +13002274 fiter = iter(fentries)
Simon Glassc64aea52018-09-14 04:57:34 -06002275
Simon Glass82059c22021-04-03 11:05:09 +13002276 fentry = next(fiter)
2277 self.assertEqual(b'U_BOOT', fentry.name)
2278 self.assertEqual(0, fentry.offset)
2279 self.assertEqual(4, fentry.size)
Simon Glassc64aea52018-09-14 04:57:34 -06002280
Simon Glass82059c22021-04-03 11:05:09 +13002281 fentry = next(fiter)
Simon Glassb1d414c2021-04-03 11:05:10 +13002282 self.assertEqual(b'SECTION', fentry.name)
2283 self.assertEqual(4, fentry.offset)
2284 self.assertEqual(0x20 + expect_size, fentry.size)
2285
2286 fentry = next(fiter)
Simon Glass82059c22021-04-03 11:05:09 +13002287 self.assertEqual(b'INTEL_MRC', fentry.name)
2288 self.assertEqual(4, fentry.offset)
2289 self.assertEqual(3, fentry.size)
Simon Glassc64aea52018-09-14 04:57:34 -06002290
Simon Glass82059c22021-04-03 11:05:09 +13002291 fentry = next(fiter)
2292 self.assertEqual(b'FMAP', fentry.name)
2293 self.assertEqual(36, fentry.offset)
2294 self.assertEqual(expect_size, fentry.size)
Simon Glassc64aea52018-09-14 04:57:34 -06002295
Simon Glassb1714232018-09-14 04:57:35 -06002296 def testElf(self):
2297 """Basic test of ELF entries"""
Simon Glass7057d022018-10-01 21:12:47 -06002298 self._SetupSplElf()
Simon Glass3eb5b202019-08-24 07:23:00 -06002299 self._SetupTplElf()
Simon Glassf6290892019-08-24 07:22:53 -06002300 with open(self.ElfTestFile('bss_data'), 'rb') as fd:
Simon Glassb1714232018-09-14 04:57:35 -06002301 TestFunctional._MakeInputFile('-boot', fd.read())
Simon Glass511f6582018-10-01 12:22:30 -06002302 data = self._DoReadFile('096_elf.dts')
Simon Glassb1714232018-09-14 04:57:35 -06002303
Simon Glass0d673792019-07-08 13:18:25 -06002304 def testElfStrip(self):
Simon Glassb1714232018-09-14 04:57:35 -06002305 """Basic test of ELF entries"""
Simon Glass7057d022018-10-01 21:12:47 -06002306 self._SetupSplElf()
Simon Glassf6290892019-08-24 07:22:53 -06002307 with open(self.ElfTestFile('bss_data'), 'rb') as fd:
Simon Glassb1714232018-09-14 04:57:35 -06002308 TestFunctional._MakeInputFile('-boot', fd.read())
Simon Glass511f6582018-10-01 12:22:30 -06002309 data = self._DoReadFile('097_elf_strip.dts')
Simon Glassb1714232018-09-14 04:57:35 -06002310
Simon Glasscd817d52018-09-14 04:57:36 -06002311 def testPackOverlapMap(self):
2312 """Test that overlapping regions are detected"""
2313 with test_util.capture_sys_output() as (stdout, stderr):
2314 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002315 self._DoTestFile('014_pack_overlap.dts', map=True)
Simon Glass80025522022-01-29 14:14:04 -07002316 map_fname = tools.get_output_filename('image.map')
Simon Glasscd817d52018-09-14 04:57:36 -06002317 self.assertEqual("Wrote map file '%s' to show errors\n" % map_fname,
2318 stdout.getvalue())
2319
2320 # We should not get an inmage, but there should be a map file
Simon Glass80025522022-01-29 14:14:04 -07002321 self.assertFalse(os.path.exists(tools.get_output_filename('image.bin')))
Simon Glasscd817d52018-09-14 04:57:36 -06002322 self.assertTrue(os.path.exists(map_fname))
Simon Glass80025522022-01-29 14:14:04 -07002323 map_data = tools.read_file(map_fname, binary=False)
Simon Glasscd817d52018-09-14 04:57:36 -06002324 self.assertEqual('''ImagePos Offset Size Name
Simon Glass49cd2b32023-02-07 14:34:18 -07002325<none> 00000000 00000008 image
Simon Glasscd817d52018-09-14 04:57:36 -06002326<none> 00000000 00000004 u-boot
2327<none> 00000003 00000004 u-boot-align
2328''', map_data)
2329
Simon Glass0d673792019-07-08 13:18:25 -06002330 def testPackRefCode(self):
Simon Glass41902e42018-10-01 12:22:31 -06002331 """Test that an image with an Intel Reference code binary works"""
2332 data = self._DoReadFile('100_intel_refcode.dts')
2333 self.assertEqual(REFCODE_DATA, data[:len(REFCODE_DATA)])
2334
Simon Glasseb023b32019-04-25 21:58:39 -06002335 def testSectionOffset(self):
2336 """Tests use of a section with an offset"""
2337 data, _, map_data, _ = self._DoReadFileDtb('101_sections_offset.dts',
2338 map=True)
2339 self.assertEqual('''ImagePos Offset Size Name
Simon Glass49cd2b32023-02-07 14:34:18 -0700234000000000 00000000 00000038 image
Simon Glasseb023b32019-04-25 21:58:39 -0600234100000004 00000004 00000010 section@0
234200000004 00000000 00000004 u-boot
234300000018 00000018 00000010 section@1
234400000018 00000000 00000004 u-boot
23450000002c 0000002c 00000004 section@2
23460000002c 00000000 00000004 u-boot
2347''', map_data)
2348 self.assertEqual(data,
Simon Glass80025522022-01-29 14:14:04 -07002349 tools.get_bytes(0x26, 4) + U_BOOT_DATA +
2350 tools.get_bytes(0x21, 12) +
2351 tools.get_bytes(0x26, 4) + U_BOOT_DATA +
2352 tools.get_bytes(0x61, 12) +
2353 tools.get_bytes(0x26, 4) + U_BOOT_DATA +
2354 tools.get_bytes(0x26, 8))
Simon Glasseb023b32019-04-25 21:58:39 -06002355
Simon Glass1de34482019-07-08 13:18:53 -06002356 def testCbfsRaw(self):
2357 """Test base handling of a Coreboot Filesystem (CBFS)
2358
2359 The exact contents of the CBFS is verified by similar tests in
2360 cbfs_util_test.py. The tests here merely check that the files added to
2361 the CBFS can be found in the final image.
2362 """
2363 data = self._DoReadFile('102_cbfs_raw.dts')
2364 size = 0xb0
2365
2366 cbfs = cbfs_util.CbfsReader(data)
2367 self.assertEqual(size, cbfs.rom_size)
2368
2369 self.assertIn('u-boot-dtb', cbfs.files)
2370 cfile = cbfs.files['u-boot-dtb']
2371 self.assertEqual(U_BOOT_DTB_DATA, cfile.data)
2372
2373 def testCbfsArch(self):
2374 """Test on non-x86 architecture"""
2375 data = self._DoReadFile('103_cbfs_raw_ppc.dts')
2376 size = 0x100
2377
2378 cbfs = cbfs_util.CbfsReader(data)
2379 self.assertEqual(size, cbfs.rom_size)
2380
2381 self.assertIn('u-boot-dtb', cbfs.files)
2382 cfile = cbfs.files['u-boot-dtb']
2383 self.assertEqual(U_BOOT_DTB_DATA, cfile.data)
2384
2385 def testCbfsStage(self):
2386 """Tests handling of a Coreboot Filesystem (CBFS)"""
2387 if not elf.ELF_TOOLS:
2388 self.skipTest('Python elftools not available')
2389 elf_fname = os.path.join(self._indir, 'cbfs-stage.elf')
2390 elf.MakeElf(elf_fname, U_BOOT_DATA, U_BOOT_DTB_DATA)
2391 size = 0xb0
2392
2393 data = self._DoReadFile('104_cbfs_stage.dts')
2394 cbfs = cbfs_util.CbfsReader(data)
2395 self.assertEqual(size, cbfs.rom_size)
2396
2397 self.assertIn('u-boot', cbfs.files)
2398 cfile = cbfs.files['u-boot']
2399 self.assertEqual(U_BOOT_DATA + U_BOOT_DTB_DATA, cfile.data)
2400
2401 def testCbfsRawCompress(self):
2402 """Test handling of compressing raw files"""
2403 self._CheckLz4()
2404 data = self._DoReadFile('105_cbfs_raw_compress.dts')
2405 size = 0x140
2406
2407 cbfs = cbfs_util.CbfsReader(data)
2408 self.assertIn('u-boot', cbfs.files)
2409 cfile = cbfs.files['u-boot']
2410 self.assertEqual(COMPRESS_DATA, cfile.data)
2411
2412 def testCbfsBadArch(self):
2413 """Test handling of a bad architecture"""
2414 with self.assertRaises(ValueError) as e:
2415 self._DoReadFile('106_cbfs_bad_arch.dts')
2416 self.assertIn("Invalid architecture 'bad-arch'", str(e.exception))
2417
2418 def testCbfsNoSize(self):
2419 """Test handling of a missing size property"""
2420 with self.assertRaises(ValueError) as e:
2421 self._DoReadFile('107_cbfs_no_size.dts')
2422 self.assertIn('entry must have a size property', str(e.exception))
2423
Simon Glass3e28f4f2021-11-23 11:03:54 -07002424 def testCbfsNoContents(self):
Simon Glass1de34482019-07-08 13:18:53 -06002425 """Test handling of a CBFS entry which does not provide contentsy"""
2426 with self.assertRaises(ValueError) as e:
2427 self._DoReadFile('108_cbfs_no_contents.dts')
2428 self.assertIn('Could not complete processing of contents',
2429 str(e.exception))
2430
2431 def testCbfsBadCompress(self):
2432 """Test handling of a bad architecture"""
2433 with self.assertRaises(ValueError) as e:
2434 self._DoReadFile('109_cbfs_bad_compress.dts')
2435 self.assertIn("Invalid compression in 'u-boot': 'invalid-algo'",
2436 str(e.exception))
2437
2438 def testCbfsNamedEntries(self):
2439 """Test handling of named entries"""
2440 data = self._DoReadFile('110_cbfs_name.dts')
2441
2442 cbfs = cbfs_util.CbfsReader(data)
2443 self.assertIn('FRED', cbfs.files)
2444 cfile1 = cbfs.files['FRED']
2445 self.assertEqual(U_BOOT_DATA, cfile1.data)
2446
2447 self.assertIn('hello', cbfs.files)
2448 cfile2 = cbfs.files['hello']
2449 self.assertEqual(U_BOOT_DTB_DATA, cfile2.data)
2450
Simon Glass759af872019-07-08 13:18:54 -06002451 def _SetupIfwi(self, fname):
2452 """Set up to run an IFWI test
2453
2454 Args:
2455 fname: Filename of input file to provide (fitimage.bin or ifwi.bin)
2456 """
2457 self._SetupSplElf()
Simon Glass3eb5b202019-08-24 07:23:00 -06002458 self._SetupTplElf()
Simon Glass759af872019-07-08 13:18:54 -06002459
2460 # Intel Integrated Firmware Image (IFWI) file
2461 with gzip.open(self.TestFile('%s.gz' % fname), 'rb') as fd:
2462 data = fd.read()
2463 TestFunctional._MakeInputFile(fname,data)
2464
2465 def _CheckIfwi(self, data):
2466 """Check that an image with an IFWI contains the correct output
2467
2468 Args:
2469 data: Conents of output file
2470 """
Simon Glass80025522022-01-29 14:14:04 -07002471 expected_desc = tools.read_file(self.TestFile('descriptor.bin'))
Simon Glass759af872019-07-08 13:18:54 -06002472 if data[:0x1000] != expected_desc:
2473 self.fail('Expected descriptor binary at start of image')
2474
2475 # We expect to find the TPL wil in subpart IBBP entry IBBL
Simon Glass80025522022-01-29 14:14:04 -07002476 image_fname = tools.get_output_filename('image.bin')
2477 tpl_fname = tools.get_output_filename('tpl.out')
Simon Glass57c7a482022-01-09 20:14:01 -07002478 ifwitool = bintool.Bintool.create('ifwitool')
2479 ifwitool.extract(image_fname, 'IBBP', 'IBBL', tpl_fname)
Simon Glass759af872019-07-08 13:18:54 -06002480
Simon Glass80025522022-01-29 14:14:04 -07002481 tpl_data = tools.read_file(tpl_fname)
Simon Glassf55bd692019-08-24 07:22:51 -06002482 self.assertEqual(U_BOOT_TPL_DATA, tpl_data[:len(U_BOOT_TPL_DATA)])
Simon Glass759af872019-07-08 13:18:54 -06002483
2484 def testPackX86RomIfwi(self):
2485 """Test that an x86 ROM with Integrated Firmware Image can be created"""
2486 self._SetupIfwi('fitimage.bin')
Simon Glass1d167762019-08-24 07:23:01 -06002487 data = self._DoReadFile('111_x86_rom_ifwi.dts')
Simon Glass759af872019-07-08 13:18:54 -06002488 self._CheckIfwi(data)
2489
2490 def testPackX86RomIfwiNoDesc(self):
2491 """Test that an x86 ROM with IFWI can be created from an ifwi.bin file"""
2492 self._SetupIfwi('ifwi.bin')
Simon Glass1d167762019-08-24 07:23:01 -06002493 data = self._DoReadFile('112_x86_rom_ifwi_nodesc.dts')
Simon Glass759af872019-07-08 13:18:54 -06002494 self._CheckIfwi(data)
2495
2496 def testPackX86RomIfwiNoData(self):
2497 """Test that an x86 ROM with IFWI handles missing data"""
2498 self._SetupIfwi('ifwi.bin')
2499 with self.assertRaises(ValueError) as e:
Simon Glass1d167762019-08-24 07:23:01 -06002500 data = self._DoReadFile('113_x86_rom_ifwi_nodata.dts')
Simon Glass759af872019-07-08 13:18:54 -06002501 self.assertIn('Could not complete processing of contents',
2502 str(e.exception))
Simon Glass91710b32018-07-17 13:25:32 -06002503
Simon Glass66152ce2022-01-09 20:14:09 -07002504 def testIfwiMissing(self):
2505 """Test that binman still produces an image if ifwitool is missing"""
2506 self._SetupIfwi('fitimage.bin')
2507 with test_util.capture_sys_output() as (_, stderr):
2508 self._DoTestFile('111_x86_rom_ifwi.dts',
2509 force_missing_bintools='ifwitool')
2510 err = stderr.getvalue()
2511 self.assertRegex(err,
Simon Glass49cd2b32023-02-07 14:34:18 -07002512 "Image 'image'.*missing bintools.*: ifwitool")
Simon Glass66152ce2022-01-09 20:14:09 -07002513
Simon Glassc2f1aed2019-07-08 13:18:56 -06002514 def testCbfsOffset(self):
2515 """Test a CBFS with files at particular offsets
2516
2517 Like all CFBS tests, this is just checking the logic that calls
2518 cbfs_util. See cbfs_util_test for fully tests (e.g. test_cbfs_offset()).
2519 """
2520 data = self._DoReadFile('114_cbfs_offset.dts')
2521 size = 0x200
2522
2523 cbfs = cbfs_util.CbfsReader(data)
2524 self.assertEqual(size, cbfs.rom_size)
2525
2526 self.assertIn('u-boot', cbfs.files)
2527 cfile = cbfs.files['u-boot']
2528 self.assertEqual(U_BOOT_DATA, cfile.data)
2529 self.assertEqual(0x40, cfile.cbfs_offset)
2530
2531 self.assertIn('u-boot-dtb', cbfs.files)
2532 cfile2 = cbfs.files['u-boot-dtb']
2533 self.assertEqual(U_BOOT_DTB_DATA, cfile2.data)
2534 self.assertEqual(0x140, cfile2.cbfs_offset)
2535
Simon Glass0f621332019-07-08 14:25:27 -06002536 def testFdtmap(self):
2537 """Test an FDT map can be inserted in the image"""
2538 data = self.data = self._DoReadFileRealDtb('115_fdtmap.dts')
2539 fdtmap_data = data[len(U_BOOT_DATA):]
2540 magic = fdtmap_data[:8]
Simon Glassc5fd10a2019-10-31 07:43:03 -06002541 self.assertEqual(b'_FDTMAP_', magic)
Simon Glass80025522022-01-29 14:14:04 -07002542 self.assertEqual(tools.get_bytes(0, 8), fdtmap_data[8:16])
Simon Glass0f621332019-07-08 14:25:27 -06002543
2544 fdt_data = fdtmap_data[16:]
2545 dtb = fdt.Fdt.FromData(fdt_data)
2546 dtb.Scan()
Simon Glass2c6adba2019-07-20 12:23:47 -06002547 props = self._GetPropTree(dtb, BASE_DTB_PROPS, prefix='/')
Simon Glass0f621332019-07-08 14:25:27 -06002548 self.assertEqual({
2549 'image-pos': 0,
2550 'offset': 0,
2551 'u-boot:offset': 0,
2552 'u-boot:size': len(U_BOOT_DATA),
2553 'u-boot:image-pos': 0,
2554 'fdtmap:image-pos': 4,
2555 'fdtmap:offset': 4,
2556 'fdtmap:size': len(fdtmap_data),
2557 'size': len(data),
2558 }, props)
2559
2560 def testFdtmapNoMatch(self):
2561 """Check handling of an FDT map when the section cannot be found"""
2562 self.data = self._DoReadFileRealDtb('115_fdtmap.dts')
2563
2564 # Mangle the section name, which should cause a mismatch between the
2565 # correct FDT path and the one expected by the section
2566 image = control.images['image']
Simon Glasscec34ba2019-07-08 14:25:28 -06002567 image._node.path += '-suffix'
Simon Glass0f621332019-07-08 14:25:27 -06002568 entries = image.GetEntries()
2569 fdtmap = entries['fdtmap']
2570 with self.assertRaises(ValueError) as e:
2571 fdtmap._GetFdtmap()
2572 self.assertIn("Cannot locate node for path '/binman-suffix'",
2573 str(e.exception))
2574
Simon Glasscec34ba2019-07-08 14:25:28 -06002575 def testFdtmapHeader(self):
2576 """Test an FDT map and image header can be inserted in the image"""
2577 data = self.data = self._DoReadFileRealDtb('116_fdtmap_hdr.dts')
2578 fdtmap_pos = len(U_BOOT_DATA)
2579 fdtmap_data = data[fdtmap_pos:]
2580 fdt_data = fdtmap_data[16:]
2581 dtb = fdt.Fdt.FromData(fdt_data)
2582 fdt_size = dtb.GetFdtObj().totalsize()
2583 hdr_data = data[-8:]
Simon Glassc5fd10a2019-10-31 07:43:03 -06002584 self.assertEqual(b'BinM', hdr_data[:4])
Simon Glasscec34ba2019-07-08 14:25:28 -06002585 offset = struct.unpack('<I', hdr_data[4:])[0] & 0xffffffff
2586 self.assertEqual(fdtmap_pos - 0x400, offset - (1 << 32))
2587
2588 def testFdtmapHeaderStart(self):
2589 """Test an image header can be inserted at the image start"""
2590 data = self.data = self._DoReadFileRealDtb('117_fdtmap_hdr_start.dts')
2591 fdtmap_pos = 0x100 + len(U_BOOT_DATA)
2592 hdr_data = data[:8]
Simon Glassc5fd10a2019-10-31 07:43:03 -06002593 self.assertEqual(b'BinM', hdr_data[:4])
Simon Glasscec34ba2019-07-08 14:25:28 -06002594 offset = struct.unpack('<I', hdr_data[4:])[0]
2595 self.assertEqual(fdtmap_pos, offset)
2596
2597 def testFdtmapHeaderPos(self):
2598 """Test an image header can be inserted at a chosen position"""
2599 data = self.data = self._DoReadFileRealDtb('118_fdtmap_hdr_pos.dts')
2600 fdtmap_pos = 0x100 + len(U_BOOT_DATA)
2601 hdr_data = data[0x80:0x88]
Simon Glassc5fd10a2019-10-31 07:43:03 -06002602 self.assertEqual(b'BinM', hdr_data[:4])
Simon Glasscec34ba2019-07-08 14:25:28 -06002603 offset = struct.unpack('<I', hdr_data[4:])[0]
2604 self.assertEqual(fdtmap_pos, offset)
2605
2606 def testHeaderMissingFdtmap(self):
2607 """Test an image header requires an fdtmap"""
2608 with self.assertRaises(ValueError) as e:
2609 self.data = self._DoReadFileRealDtb('119_fdtmap_hdr_missing.dts')
2610 self.assertIn("'image_header' section must have an 'fdtmap' sibling",
2611 str(e.exception))
2612
2613 def testHeaderNoLocation(self):
2614 """Test an image header with a no specified location is detected"""
2615 with self.assertRaises(ValueError) as e:
2616 self.data = self._DoReadFileRealDtb('120_hdr_no_location.dts')
2617 self.assertIn("Invalid location 'None', expected 'start' or 'end'",
2618 str(e.exception))
2619
Simon Glasse61b6f62019-07-08 14:25:37 -06002620 def testEntryExpand(self):
Simon Glassdd156a42022-03-05 20:18:59 -07002621 """Test extending an entry after it is packed"""
2622 data = self._DoReadFile('121_entry_extend.dts')
Simon Glass8c702fb2019-07-20 12:23:57 -06002623 self.assertEqual(b'aaa', data[:3])
2624 self.assertEqual(U_BOOT_DATA, data[3:3 + len(U_BOOT_DATA)])
2625 self.assertEqual(b'aaa', data[-3:])
Simon Glasse61b6f62019-07-08 14:25:37 -06002626
Simon Glassdd156a42022-03-05 20:18:59 -07002627 def testEntryExtendBad(self):
2628 """Test extending an entry after it is packed, twice"""
Simon Glasse61b6f62019-07-08 14:25:37 -06002629 with self.assertRaises(ValueError) as e:
Simon Glassdd156a42022-03-05 20:18:59 -07002630 self._DoReadFile('122_entry_extend_twice.dts')
Simon Glass9d8ee322019-07-20 12:23:58 -06002631 self.assertIn("Image '/binman': Entries changed size after packing",
Simon Glasse61b6f62019-07-08 14:25:37 -06002632 str(e.exception))
2633
Simon Glassdd156a42022-03-05 20:18:59 -07002634 def testEntryExtendSection(self):
2635 """Test extending an entry within a section after it is packed"""
2636 data = self._DoReadFile('123_entry_extend_section.dts')
Simon Glass8c702fb2019-07-20 12:23:57 -06002637 self.assertEqual(b'aaa', data[:3])
2638 self.assertEqual(U_BOOT_DATA, data[3:3 + len(U_BOOT_DATA)])
2639 self.assertEqual(b'aaa', data[-3:])
Simon Glasse61b6f62019-07-08 14:25:37 -06002640
Simon Glass90d29682019-07-08 14:25:38 -06002641 def testCompressDtb(self):
2642 """Test that compress of device-tree files is supported"""
2643 self._CheckLz4()
2644 data = self.data = self._DoReadFileRealDtb('124_compress_dtb.dts')
2645 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
2646 comp_data = data[len(U_BOOT_DATA):]
2647 orig = self._decompress(comp_data)
2648 dtb = fdt.Fdt.FromData(orig)
2649 dtb.Scan()
2650 props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
2651 expected = {
2652 'u-boot:size': len(U_BOOT_DATA),
2653 'u-boot-dtb:uncomp-size': len(orig),
2654 'u-boot-dtb:size': len(comp_data),
2655 'size': len(data),
2656 }
2657 self.assertEqual(expected, props)
2658
Simon Glass151bbbf2019-07-08 14:25:41 -06002659 def testCbfsUpdateFdt(self):
2660 """Test that we can update the device tree with CBFS offset/size info"""
2661 self._CheckLz4()
2662 data, _, _, out_dtb_fname = self._DoReadFileDtb('125_cbfs_update.dts',
2663 update_dtb=True)
2664 dtb = fdt.Fdt(out_dtb_fname)
2665 dtb.Scan()
Simon Glass2c6adba2019-07-20 12:23:47 -06002666 props = self._GetPropTree(dtb, BASE_DTB_PROPS + ['uncomp-size'])
Simon Glass151bbbf2019-07-08 14:25:41 -06002667 del props['cbfs/u-boot:size']
2668 self.assertEqual({
2669 'offset': 0,
2670 'size': len(data),
2671 'image-pos': 0,
2672 'cbfs:offset': 0,
2673 'cbfs:size': len(data),
2674 'cbfs:image-pos': 0,
Simon Glassfa144222023-10-14 14:40:28 -06002675 'cbfs/u-boot:offset': 0x30,
Simon Glass151bbbf2019-07-08 14:25:41 -06002676 'cbfs/u-boot:uncomp-size': len(U_BOOT_DATA),
Simon Glassfa144222023-10-14 14:40:28 -06002677 'cbfs/u-boot:image-pos': 0x30,
2678 'cbfs/u-boot-dtb:offset': 0xa4,
Simon Glass151bbbf2019-07-08 14:25:41 -06002679 'cbfs/u-boot-dtb:size': len(U_BOOT_DATA),
Simon Glassfa144222023-10-14 14:40:28 -06002680 'cbfs/u-boot-dtb:image-pos': 0xa4,
Simon Glass151bbbf2019-07-08 14:25:41 -06002681 }, props)
2682
Simon Glass3c9b4f22019-07-08 14:25:42 -06002683 def testCbfsBadType(self):
2684 """Test an image header with a no specified location is detected"""
2685 with self.assertRaises(ValueError) as e:
2686 self._DoReadFile('126_cbfs_bad_type.dts')
2687 self.assertIn("Unknown cbfs-type 'badtype'", str(e.exception))
2688
Simon Glass6b156f82019-07-08 14:25:43 -06002689 def testList(self):
2690 """Test listing the files in an image"""
2691 self._CheckLz4()
2692 data = self._DoReadFile('127_list.dts')
2693 image = control.images['image']
2694 entries = image.BuildEntryList()
2695 self.assertEqual(7, len(entries))
2696
2697 ent = entries[0]
2698 self.assertEqual(0, ent.indent)
Simon Glass49cd2b32023-02-07 14:34:18 -07002699 self.assertEqual('image', ent.name)
Simon Glass6b156f82019-07-08 14:25:43 -06002700 self.assertEqual('section', ent.etype)
2701 self.assertEqual(len(data), ent.size)
2702 self.assertEqual(0, ent.image_pos)
2703 self.assertEqual(None, ent.uncomp_size)
Simon Glass39dd2152019-07-08 14:25:47 -06002704 self.assertEqual(0, ent.offset)
Simon Glass6b156f82019-07-08 14:25:43 -06002705
2706 ent = entries[1]
2707 self.assertEqual(1, ent.indent)
2708 self.assertEqual('u-boot', ent.name)
2709 self.assertEqual('u-boot', ent.etype)
2710 self.assertEqual(len(U_BOOT_DATA), ent.size)
2711 self.assertEqual(0, ent.image_pos)
2712 self.assertEqual(None, ent.uncomp_size)
2713 self.assertEqual(0, ent.offset)
2714
2715 ent = entries[2]
2716 self.assertEqual(1, ent.indent)
2717 self.assertEqual('section', ent.name)
2718 self.assertEqual('section', ent.etype)
2719 section_size = ent.size
2720 self.assertEqual(0x100, ent.image_pos)
2721 self.assertEqual(None, ent.uncomp_size)
Simon Glass39dd2152019-07-08 14:25:47 -06002722 self.assertEqual(0x100, ent.offset)
Simon Glass6b156f82019-07-08 14:25:43 -06002723
2724 ent = entries[3]
2725 self.assertEqual(2, ent.indent)
2726 self.assertEqual('cbfs', ent.name)
2727 self.assertEqual('cbfs', ent.etype)
2728 self.assertEqual(0x400, ent.size)
2729 self.assertEqual(0x100, ent.image_pos)
2730 self.assertEqual(None, ent.uncomp_size)
2731 self.assertEqual(0, ent.offset)
2732
2733 ent = entries[4]
2734 self.assertEqual(3, ent.indent)
2735 self.assertEqual('u-boot', ent.name)
2736 self.assertEqual('u-boot', ent.etype)
2737 self.assertEqual(len(U_BOOT_DATA), ent.size)
2738 self.assertEqual(0x138, ent.image_pos)
2739 self.assertEqual(None, ent.uncomp_size)
2740 self.assertEqual(0x38, ent.offset)
2741
2742 ent = entries[5]
2743 self.assertEqual(3, ent.indent)
2744 self.assertEqual('u-boot-dtb', ent.name)
2745 self.assertEqual('text', ent.etype)
2746 self.assertGreater(len(COMPRESS_DATA), ent.size)
2747 self.assertEqual(0x178, ent.image_pos)
2748 self.assertEqual(len(COMPRESS_DATA), ent.uncomp_size)
2749 self.assertEqual(0x78, ent.offset)
2750
2751 ent = entries[6]
2752 self.assertEqual(2, ent.indent)
2753 self.assertEqual('u-boot-dtb', ent.name)
2754 self.assertEqual('u-boot-dtb', ent.etype)
2755 self.assertEqual(0x500, ent.image_pos)
2756 self.assertEqual(len(U_BOOT_DTB_DATA), ent.uncomp_size)
2757 dtb_size = ent.size
2758 # Compressing this data expands it since headers are added
2759 self.assertGreater(dtb_size, len(U_BOOT_DTB_DATA))
2760 self.assertEqual(0x400, ent.offset)
2761
2762 self.assertEqual(len(data), 0x100 + section_size)
2763 self.assertEqual(section_size, 0x400 + dtb_size)
2764
Simon Glass8d8bf4e2019-07-08 14:25:44 -06002765 def testFindFdtmap(self):
2766 """Test locating an FDT map in an image"""
2767 self._CheckLz4()
2768 data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
2769 image = control.images['image']
2770 entries = image.GetEntries()
2771 entry = entries['fdtmap']
2772 self.assertEqual(entry.image_pos, fdtmap.LocateFdtmap(data))
2773
2774 def testFindFdtmapMissing(self):
2775 """Test failing to locate an FDP map"""
2776 data = self._DoReadFile('005_simple.dts')
2777 self.assertEqual(None, fdtmap.LocateFdtmap(data))
2778
Simon Glassed39a3c2019-07-08 14:25:45 -06002779 def testFindImageHeader(self):
2780 """Test locating a image header"""
2781 self._CheckLz4()
Simon Glassb8424fa2019-07-08 14:25:46 -06002782 data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
Simon Glassed39a3c2019-07-08 14:25:45 -06002783 image = control.images['image']
2784 entries = image.GetEntries()
2785 entry = entries['fdtmap']
2786 # The header should point to the FDT map
2787 self.assertEqual(entry.image_pos, image_header.LocateHeaderOffset(data))
2788
2789 def testFindImageHeaderStart(self):
2790 """Test locating a image header located at the start of an image"""
Simon Glassb8424fa2019-07-08 14:25:46 -06002791 data = self.data = self._DoReadFileRealDtb('117_fdtmap_hdr_start.dts')
Simon Glassed39a3c2019-07-08 14:25:45 -06002792 image = control.images['image']
2793 entries = image.GetEntries()
2794 entry = entries['fdtmap']
2795 # The header should point to the FDT map
2796 self.assertEqual(entry.image_pos, image_header.LocateHeaderOffset(data))
2797
2798 def testFindImageHeaderMissing(self):
2799 """Test failing to locate an image header"""
2800 data = self._DoReadFile('005_simple.dts')
2801 self.assertEqual(None, image_header.LocateHeaderOffset(data))
2802
Simon Glassb8424fa2019-07-08 14:25:46 -06002803 def testReadImage(self):
2804 """Test reading an image and accessing its FDT map"""
2805 self._CheckLz4()
2806 data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
Simon Glass80025522022-01-29 14:14:04 -07002807 image_fname = tools.get_output_filename('image.bin')
Simon Glassb8424fa2019-07-08 14:25:46 -06002808 orig_image = control.images['image']
2809 image = Image.FromFile(image_fname)
2810 self.assertEqual(orig_image.GetEntries().keys(),
2811 image.GetEntries().keys())
2812
2813 orig_entry = orig_image.GetEntries()['fdtmap']
2814 entry = image.GetEntries()['fdtmap']
Brandon Maiera657bc62024-06-04 16:16:05 +00002815 self.assertEqual(orig_entry.offset, entry.offset)
2816 self.assertEqual(orig_entry.size, entry.size)
2817 self.assertEqual(orig_entry.image_pos, entry.image_pos)
Simon Glassb8424fa2019-07-08 14:25:46 -06002818
2819 def testReadImageNoHeader(self):
2820 """Test accessing an image's FDT map without an image header"""
2821 self._CheckLz4()
2822 data = self._DoReadFileRealDtb('129_decode_image_nohdr.dts')
Simon Glass80025522022-01-29 14:14:04 -07002823 image_fname = tools.get_output_filename('image.bin')
Simon Glassb8424fa2019-07-08 14:25:46 -06002824 image = Image.FromFile(image_fname)
2825 self.assertTrue(isinstance(image, Image))
Simon Glass072959a2019-07-20 12:23:50 -06002826 self.assertEqual('image', image.image_name[-5:])
Simon Glassb8424fa2019-07-08 14:25:46 -06002827
2828 def testReadImageFail(self):
2829 """Test failing to read an image image's FDT map"""
2830 self._DoReadFile('005_simple.dts')
Simon Glass80025522022-01-29 14:14:04 -07002831 image_fname = tools.get_output_filename('image.bin')
Simon Glassb8424fa2019-07-08 14:25:46 -06002832 with self.assertRaises(ValueError) as e:
2833 image = Image.FromFile(image_fname)
2834 self.assertIn("Cannot find FDT map in image", str(e.exception))
Simon Glassc2f1aed2019-07-08 13:18:56 -06002835
Simon Glassb2fd11d2019-07-08 14:25:48 -06002836 def testListCmd(self):
2837 """Test listing the files in an image using an Fdtmap"""
2838 self._CheckLz4()
2839 data = self._DoReadFileRealDtb('130_list_fdtmap.dts')
2840
2841 # lz4 compression size differs depending on the version
2842 image = control.images['image']
2843 entries = image.GetEntries()
2844 section_size = entries['section'].size
2845 fdt_size = entries['section'].GetEntries()['u-boot-dtb'].size
2846 fdtmap_offset = entries['fdtmap'].offset
2847
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01002848 tmpdir = None
Simon Glassb3d6fc72019-07-20 12:24:10 -06002849 try:
2850 tmpdir, updated_fname = self._SetupImageInTmpdir()
2851 with test_util.capture_sys_output() as (stdout, stderr):
2852 self._DoBinman('ls', '-i', updated_fname)
2853 finally:
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01002854 if tmpdir:
2855 shutil.rmtree(tmpdir)
Simon Glassb2fd11d2019-07-08 14:25:48 -06002856 lines = stdout.getvalue().splitlines()
2857 expected = [
2858'Name Image-pos Size Entry-type Offset Uncomp-size',
2859'----------------------------------------------------------------------',
Simon Glass49cd2b32023-02-07 14:34:18 -07002860'image 0 c00 section 0',
Simon Glassb2fd11d2019-07-08 14:25:48 -06002861' u-boot 0 4 u-boot 0',
2862' section 100 %x section 100' % section_size,
2863' cbfs 100 400 cbfs 0',
Simon Glassfa144222023-10-14 14:40:28 -06002864' u-boot 120 4 u-boot 20',
Simon Glassc5fd10a2019-10-31 07:43:03 -06002865' u-boot-dtb 180 105 u-boot-dtb 80 3c9',
Simon Glassb2fd11d2019-07-08 14:25:48 -06002866' u-boot-dtb 500 %x u-boot-dtb 400 3c9' % fdt_size,
Simon Glassc5fd10a2019-10-31 07:43:03 -06002867' fdtmap %x 3bd fdtmap %x' %
Simon Glassb2fd11d2019-07-08 14:25:48 -06002868 (fdtmap_offset, fdtmap_offset),
2869' image-header bf8 8 image-header bf8',
2870 ]
2871 self.assertEqual(expected, lines)
2872
2873 def testListCmdFail(self):
2874 """Test failing to list an image"""
2875 self._DoReadFile('005_simple.dts')
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01002876 tmpdir = None
Simon Glassb3d6fc72019-07-20 12:24:10 -06002877 try:
2878 tmpdir, updated_fname = self._SetupImageInTmpdir()
2879 with self.assertRaises(ValueError) as e:
2880 self._DoBinman('ls', '-i', updated_fname)
2881 finally:
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01002882 if tmpdir:
2883 shutil.rmtree(tmpdir)
Simon Glassb2fd11d2019-07-08 14:25:48 -06002884 self.assertIn("Cannot find FDT map in image", str(e.exception))
2885
2886 def _RunListCmd(self, paths, expected):
2887 """List out entries and check the result
2888
2889 Args:
2890 paths: List of paths to pass to the list command
2891 expected: Expected list of filenames to be returned, in order
2892 """
2893 self._CheckLz4()
2894 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07002895 image_fname = tools.get_output_filename('image.bin')
Simon Glassb2fd11d2019-07-08 14:25:48 -06002896 image = Image.FromFile(image_fname)
2897 lines = image.GetListEntries(paths)[1]
2898 files = [line[0].strip() for line in lines[1:]]
2899 self.assertEqual(expected, files)
2900
2901 def testListCmdSection(self):
2902 """Test listing the files in a section"""
2903 self._RunListCmd(['section'],
2904 ['section', 'cbfs', 'u-boot', 'u-boot-dtb', 'u-boot-dtb'])
2905
2906 def testListCmdFile(self):
2907 """Test listing a particular file"""
2908 self._RunListCmd(['*u-boot-dtb'], ['u-boot-dtb', 'u-boot-dtb'])
2909
2910 def testListCmdWildcard(self):
2911 """Test listing a wildcarded file"""
2912 self._RunListCmd(['*boot*'],
2913 ['u-boot', 'u-boot', 'u-boot-dtb', 'u-boot-dtb'])
2914
2915 def testListCmdWildcardMulti(self):
2916 """Test listing a wildcarded file"""
2917 self._RunListCmd(['*cb*', '*head*'],
2918 ['cbfs', 'u-boot', 'u-boot-dtb', 'image-header'])
2919
2920 def testListCmdEmpty(self):
2921 """Test listing a wildcarded file"""
2922 self._RunListCmd(['nothing'], [])
2923
2924 def testListCmdPath(self):
2925 """Test listing the files in a sub-entry of a section"""
2926 self._RunListCmd(['section/cbfs'], ['cbfs', 'u-boot', 'u-boot-dtb'])
2927
Simon Glass4c613bf2019-07-08 14:25:50 -06002928 def _RunExtractCmd(self, entry_name, decomp=True):
2929 """Extract an entry from an image
2930
2931 Args:
2932 entry_name: Entry name to extract
2933 decomp: True to decompress the data if compressed, False to leave
2934 it in its raw uncompressed format
2935
2936 Returns:
2937 data from entry
2938 """
2939 self._CheckLz4()
2940 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07002941 image_fname = tools.get_output_filename('image.bin')
Simon Glass4c613bf2019-07-08 14:25:50 -06002942 return control.ReadEntry(image_fname, entry_name, decomp)
2943
2944 def testExtractSimple(self):
2945 """Test extracting a single file"""
2946 data = self._RunExtractCmd('u-boot')
2947 self.assertEqual(U_BOOT_DATA, data)
2948
Simon Glass980a2842019-07-08 14:25:52 -06002949 def testExtractSection(self):
2950 """Test extracting the files in a section"""
2951 data = self._RunExtractCmd('section')
2952 cbfs_data = data[:0x400]
2953 cbfs = cbfs_util.CbfsReader(cbfs_data)
Simon Glassc5fd10a2019-10-31 07:43:03 -06002954 self.assertEqual(['u-boot', 'u-boot-dtb', ''], list(cbfs.files.keys()))
Simon Glass980a2842019-07-08 14:25:52 -06002955 dtb_data = data[0x400:]
2956 dtb = self._decompress(dtb_data)
2957 self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
2958
2959 def testExtractCompressed(self):
2960 """Test extracting compressed data"""
2961 data = self._RunExtractCmd('section/u-boot-dtb')
2962 self.assertEqual(EXTRACT_DTB_SIZE, len(data))
2963
2964 def testExtractRaw(self):
2965 """Test extracting compressed data without decompressing it"""
2966 data = self._RunExtractCmd('section/u-boot-dtb', decomp=False)
2967 dtb = self._decompress(data)
2968 self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
2969
2970 def testExtractCbfs(self):
2971 """Test extracting CBFS data"""
2972 data = self._RunExtractCmd('section/cbfs/u-boot')
2973 self.assertEqual(U_BOOT_DATA, data)
2974
2975 def testExtractCbfsCompressed(self):
2976 """Test extracting CBFS compressed data"""
2977 data = self._RunExtractCmd('section/cbfs/u-boot-dtb')
2978 self.assertEqual(EXTRACT_DTB_SIZE, len(data))
2979
2980 def testExtractCbfsRaw(self):
2981 """Test extracting CBFS compressed data without decompressing it"""
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +02002982 bintool = self.comp_bintools['lzma_alone']
2983 self._CheckBintool(bintool)
Simon Glass980a2842019-07-08 14:25:52 -06002984 data = self._RunExtractCmd('section/cbfs/u-boot-dtb', decomp=False)
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +02002985 dtb = bintool.decompress(data)
Simon Glass980a2842019-07-08 14:25:52 -06002986 self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
2987
Simon Glass4c613bf2019-07-08 14:25:50 -06002988 def testExtractBadEntry(self):
2989 """Test extracting a bad section path"""
2990 with self.assertRaises(ValueError) as e:
2991 self._RunExtractCmd('section/does-not-exist')
2992 self.assertIn("Entry 'does-not-exist' not found in '/section'",
2993 str(e.exception))
2994
2995 def testExtractMissingFile(self):
2996 """Test extracting file that does not exist"""
2997 with self.assertRaises(IOError) as e:
2998 control.ReadEntry('missing-file', 'name')
2999
3000 def testExtractBadFile(self):
3001 """Test extracting an invalid file"""
3002 fname = os.path.join(self._indir, 'badfile')
Simon Glass80025522022-01-29 14:14:04 -07003003 tools.write_file(fname, b'')
Simon Glass4c613bf2019-07-08 14:25:50 -06003004 with self.assertRaises(ValueError) as e:
3005 control.ReadEntry(fname, 'name')
3006
Simon Glass980a2842019-07-08 14:25:52 -06003007 def testExtractCmd(self):
3008 """Test extracting a file fron an image on the command line"""
3009 self._CheckLz4()
3010 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass980a2842019-07-08 14:25:52 -06003011 fname = os.path.join(self._indir, 'output.extact')
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01003012 tmpdir = None
Simon Glassb3d6fc72019-07-20 12:24:10 -06003013 try:
3014 tmpdir, updated_fname = self._SetupImageInTmpdir()
3015 with test_util.capture_sys_output() as (stdout, stderr):
3016 self._DoBinman('extract', '-i', updated_fname, 'u-boot',
3017 '-f', fname)
3018 finally:
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01003019 if tmpdir:
3020 shutil.rmtree(tmpdir)
Simon Glass80025522022-01-29 14:14:04 -07003021 data = tools.read_file(fname)
Simon Glass980a2842019-07-08 14:25:52 -06003022 self.assertEqual(U_BOOT_DATA, data)
3023
3024 def testExtractOneEntry(self):
3025 """Test extracting a single entry fron an image """
3026 self._CheckLz4()
3027 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07003028 image_fname = tools.get_output_filename('image.bin')
Simon Glass980a2842019-07-08 14:25:52 -06003029 fname = os.path.join(self._indir, 'output.extact')
3030 control.ExtractEntries(image_fname, fname, None, ['u-boot'])
Simon Glass80025522022-01-29 14:14:04 -07003031 data = tools.read_file(fname)
Simon Glass980a2842019-07-08 14:25:52 -06003032 self.assertEqual(U_BOOT_DATA, data)
3033
3034 def _CheckExtractOutput(self, decomp):
3035 """Helper to test file output with and without decompression
3036
3037 Args:
3038 decomp: True to decompress entry data, False to output it raw
3039 """
3040 def _CheckPresent(entry_path, expect_data, expect_size=None):
3041 """Check and remove expected file
3042
3043 This checks the data/size of a file and removes the file both from
3044 the outfiles set and from the output directory. Once all files are
3045 processed, both the set and directory should be empty.
3046
3047 Args:
3048 entry_path: Entry path
3049 expect_data: Data to expect in file, or None to skip check
3050 expect_size: Size of data to expect in file, or None to skip
3051 """
3052 path = os.path.join(outdir, entry_path)
Simon Glass80025522022-01-29 14:14:04 -07003053 data = tools.read_file(path)
Simon Glass980a2842019-07-08 14:25:52 -06003054 os.remove(path)
3055 if expect_data:
3056 self.assertEqual(expect_data, data)
3057 elif expect_size:
3058 self.assertEqual(expect_size, len(data))
3059 outfiles.remove(path)
3060
3061 def _CheckDirPresent(name):
3062 """Remove expected directory
3063
3064 This gives an error if the directory does not exist as expected
3065
3066 Args:
3067 name: Name of directory to remove
3068 """
3069 path = os.path.join(outdir, name)
3070 os.rmdir(path)
3071
3072 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07003073 image_fname = tools.get_output_filename('image.bin')
Simon Glass980a2842019-07-08 14:25:52 -06003074 outdir = os.path.join(self._indir, 'extract')
3075 einfos = control.ExtractEntries(image_fname, None, outdir, [], decomp)
3076
3077 # Create a set of all file that were output (should be 9)
3078 outfiles = set()
3079 for root, dirs, files in os.walk(outdir):
3080 outfiles |= set([os.path.join(root, fname) for fname in files])
3081 self.assertEqual(9, len(outfiles))
3082 self.assertEqual(9, len(einfos))
3083
3084 image = control.images['image']
3085 entries = image.GetEntries()
3086
3087 # Check the 9 files in various ways
3088 section = entries['section']
3089 section_entries = section.GetEntries()
3090 cbfs_entries = section_entries['cbfs'].GetEntries()
3091 _CheckPresent('u-boot', U_BOOT_DATA)
3092 _CheckPresent('section/cbfs/u-boot', U_BOOT_DATA)
3093 dtb_len = EXTRACT_DTB_SIZE
3094 if not decomp:
3095 dtb_len = cbfs_entries['u-boot-dtb'].size
3096 _CheckPresent('section/cbfs/u-boot-dtb', None, dtb_len)
3097 if not decomp:
3098 dtb_len = section_entries['u-boot-dtb'].size
3099 _CheckPresent('section/u-boot-dtb', None, dtb_len)
3100
3101 fdtmap = entries['fdtmap']
3102 _CheckPresent('fdtmap', fdtmap.data)
3103 hdr = entries['image-header']
3104 _CheckPresent('image-header', hdr.data)
3105
3106 _CheckPresent('section/root', section.data)
3107 cbfs = section_entries['cbfs']
3108 _CheckPresent('section/cbfs/root', cbfs.data)
Simon Glass80025522022-01-29 14:14:04 -07003109 data = tools.read_file(image_fname)
Simon Glass980a2842019-07-08 14:25:52 -06003110 _CheckPresent('root', data)
3111
3112 # There should be no files left. Remove all the directories to check.
3113 # If there are any files/dirs remaining, one of these checks will fail.
3114 self.assertEqual(0, len(outfiles))
3115 _CheckDirPresent('section/cbfs')
3116 _CheckDirPresent('section')
3117 _CheckDirPresent('')
3118 self.assertFalse(os.path.exists(outdir))
3119
3120 def testExtractAllEntries(self):
3121 """Test extracting all entries"""
3122 self._CheckLz4()
3123 self._CheckExtractOutput(decomp=True)
3124
3125 def testExtractAllEntriesRaw(self):
3126 """Test extracting all entries without decompressing them"""
3127 self._CheckLz4()
3128 self._CheckExtractOutput(decomp=False)
3129
3130 def testExtractSelectedEntries(self):
3131 """Test extracting some entries"""
3132 self._CheckLz4()
3133 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07003134 image_fname = tools.get_output_filename('image.bin')
Simon Glass980a2842019-07-08 14:25:52 -06003135 outdir = os.path.join(self._indir, 'extract')
3136 einfos = control.ExtractEntries(image_fname, None, outdir,
3137 ['*cb*', '*head*'])
3138
3139 # File output is tested by testExtractAllEntries(), so just check that
3140 # the expected entries are selected
3141 names = [einfo.name for einfo in einfos]
3142 self.assertEqual(names,
3143 ['cbfs', 'u-boot', 'u-boot-dtb', 'image-header'])
3144
3145 def testExtractNoEntryPaths(self):
3146 """Test extracting some entries"""
3147 self._CheckLz4()
3148 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07003149 image_fname = tools.get_output_filename('image.bin')
Simon Glass980a2842019-07-08 14:25:52 -06003150 with self.assertRaises(ValueError) as e:
3151 control.ExtractEntries(image_fname, 'fname', None, [])
Simon Glassa772d3f2019-07-20 12:24:14 -06003152 self.assertIn('Must specify an entry path to write with -f',
Simon Glass980a2842019-07-08 14:25:52 -06003153 str(e.exception))
3154
3155 def testExtractTooManyEntryPaths(self):
3156 """Test extracting some entries"""
3157 self._CheckLz4()
3158 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07003159 image_fname = tools.get_output_filename('image.bin')
Simon Glass980a2842019-07-08 14:25:52 -06003160 with self.assertRaises(ValueError) as e:
3161 control.ExtractEntries(image_fname, 'fname', None, ['a', 'b'])
Simon Glassa772d3f2019-07-20 12:24:14 -06003162 self.assertIn('Must specify exactly one entry path to write with -f',
Simon Glass980a2842019-07-08 14:25:52 -06003163 str(e.exception))
3164
Simon Glass52d06212019-07-08 14:25:53 -06003165 def testPackAlignSection(self):
3166 """Test that sections can have alignment"""
3167 self._DoReadFile('131_pack_align_section.dts')
3168
3169 self.assertIn('image', control.images)
3170 image = control.images['image']
3171 entries = image.GetEntries()
3172 self.assertEqual(3, len(entries))
3173
3174 # First u-boot
3175 self.assertIn('u-boot', entries)
3176 entry = entries['u-boot']
3177 self.assertEqual(0, entry.offset)
3178 self.assertEqual(0, entry.image_pos)
3179 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3180 self.assertEqual(len(U_BOOT_DATA), entry.size)
3181
3182 # Section0
3183 self.assertIn('section0', entries)
3184 section0 = entries['section0']
3185 self.assertEqual(0x10, section0.offset)
3186 self.assertEqual(0x10, section0.image_pos)
3187 self.assertEqual(len(U_BOOT_DATA), section0.size)
3188
3189 # Second u-boot
3190 section_entries = section0.GetEntries()
3191 self.assertIn('u-boot', section_entries)
3192 entry = section_entries['u-boot']
3193 self.assertEqual(0, entry.offset)
3194 self.assertEqual(0x10, entry.image_pos)
3195 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3196 self.assertEqual(len(U_BOOT_DATA), entry.size)
3197
3198 # Section1
3199 self.assertIn('section1', entries)
3200 section1 = entries['section1']
3201 self.assertEqual(0x14, section1.offset)
3202 self.assertEqual(0x14, section1.image_pos)
3203 self.assertEqual(0x20, section1.size)
3204
3205 # Second u-boot
3206 section_entries = section1.GetEntries()
3207 self.assertIn('u-boot', section_entries)
3208 entry = section_entries['u-boot']
3209 self.assertEqual(0, entry.offset)
3210 self.assertEqual(0x14, entry.image_pos)
3211 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3212 self.assertEqual(len(U_BOOT_DATA), entry.size)
3213
3214 # Section2
3215 self.assertIn('section2', section_entries)
3216 section2 = section_entries['section2']
3217 self.assertEqual(0x4, section2.offset)
3218 self.assertEqual(0x18, section2.image_pos)
3219 self.assertEqual(4, section2.size)
3220
3221 # Third u-boot
3222 section_entries = section2.GetEntries()
3223 self.assertIn('u-boot', section_entries)
3224 entry = section_entries['u-boot']
3225 self.assertEqual(0, entry.offset)
3226 self.assertEqual(0x18, entry.image_pos)
3227 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3228 self.assertEqual(len(U_BOOT_DATA), entry.size)
3229
Simon Glassf8a54bc2019-07-20 12:23:56 -06003230 def _RunReplaceCmd(self, entry_name, data, decomp=True, allow_resize=True,
3231 dts='132_replace.dts'):
Simon Glass072959a2019-07-20 12:23:50 -06003232 """Replace an entry in an image
3233
3234 This writes the entry data to update it, then opens the updated file and
3235 returns the value that it now finds there.
3236
3237 Args:
3238 entry_name: Entry name to replace
3239 data: Data to replace it with
3240 decomp: True to compress the data if needed, False if data is
3241 already compressed so should be used as is
Simon Glassf8a54bc2019-07-20 12:23:56 -06003242 allow_resize: True to allow entries to change size, False to raise
3243 an exception
Simon Glass072959a2019-07-20 12:23:50 -06003244
3245 Returns:
3246 Tuple:
3247 data from entry
3248 data from fdtmap (excluding header)
Simon Glassf8a54bc2019-07-20 12:23:56 -06003249 Image object that was modified
Simon Glass072959a2019-07-20 12:23:50 -06003250 """
Simon Glassf8a54bc2019-07-20 12:23:56 -06003251 dtb_data = self._DoReadFileDtb(dts, use_real_dtb=True,
Simon Glass072959a2019-07-20 12:23:50 -06003252 update_dtb=True)[1]
3253
3254 self.assertIn('image', control.images)
3255 image = control.images['image']
3256 entries = image.GetEntries()
3257 orig_dtb_data = entries['u-boot-dtb'].data
3258 orig_fdtmap_data = entries['fdtmap'].data
3259
Simon Glass80025522022-01-29 14:14:04 -07003260 image_fname = tools.get_output_filename('image.bin')
3261 updated_fname = tools.get_output_filename('image-updated.bin')
3262 tools.write_file(updated_fname, tools.read_file(image_fname))
Simon Glassf8a54bc2019-07-20 12:23:56 -06003263 image = control.WriteEntry(updated_fname, entry_name, data, decomp,
3264 allow_resize)
Simon Glass072959a2019-07-20 12:23:50 -06003265 data = control.ReadEntry(updated_fname, entry_name, decomp)
3266
Simon Glassf8a54bc2019-07-20 12:23:56 -06003267 # The DT data should not change unless resized:
3268 if not allow_resize:
3269 new_dtb_data = entries['u-boot-dtb'].data
3270 self.assertEqual(new_dtb_data, orig_dtb_data)
3271 new_fdtmap_data = entries['fdtmap'].data
3272 self.assertEqual(new_fdtmap_data, orig_fdtmap_data)
Simon Glass072959a2019-07-20 12:23:50 -06003273
Simon Glassf8a54bc2019-07-20 12:23:56 -06003274 return data, orig_fdtmap_data[fdtmap.FDTMAP_HDR_LEN:], image
Simon Glass072959a2019-07-20 12:23:50 -06003275
3276 def testReplaceSimple(self):
3277 """Test replacing a single file"""
3278 expected = b'x' * len(U_BOOT_DATA)
Simon Glassf8a54bc2019-07-20 12:23:56 -06003279 data, expected_fdtmap, _ = self._RunReplaceCmd('u-boot', expected,
3280 allow_resize=False)
Simon Glass072959a2019-07-20 12:23:50 -06003281 self.assertEqual(expected, data)
3282
3283 # Test that the state looks right. There should be an FDT for the fdtmap
3284 # that we jsut read back in, and it should match what we find in the
3285 # 'control' tables. Checking for an FDT that does not exist should
3286 # return None.
3287 path, fdtmap = state.GetFdtContents('fdtmap')
Simon Glassf8a54bc2019-07-20 12:23:56 -06003288 self.assertIsNotNone(path)
Simon Glass072959a2019-07-20 12:23:50 -06003289 self.assertEqual(expected_fdtmap, fdtmap)
3290
3291 dtb = state.GetFdtForEtype('fdtmap')
3292 self.assertEqual(dtb.GetContents(), fdtmap)
3293
3294 missing_path, missing_fdtmap = state.GetFdtContents('missing')
3295 self.assertIsNone(missing_path)
3296 self.assertIsNone(missing_fdtmap)
3297
3298 missing_dtb = state.GetFdtForEtype('missing')
3299 self.assertIsNone(missing_dtb)
3300
3301 self.assertEqual('/binman', state.fdt_path_prefix)
3302
3303 def testReplaceResizeFail(self):
3304 """Test replacing a file by something larger"""
3305 expected = U_BOOT_DATA + b'x'
3306 with self.assertRaises(ValueError) as e:
Simon Glassf8a54bc2019-07-20 12:23:56 -06003307 self._RunReplaceCmd('u-boot', expected, allow_resize=False,
3308 dts='139_replace_repack.dts')
Simon Glass072959a2019-07-20 12:23:50 -06003309 self.assertIn("Node '/u-boot': Entry data size does not match, but resize is disabled",
3310 str(e.exception))
3311
3312 def testReplaceMulti(self):
3313 """Test replacing entry data where multiple images are generated"""
3314 data = self._DoReadFileDtb('133_replace_multi.dts', use_real_dtb=True,
3315 update_dtb=True)[0]
3316 expected = b'x' * len(U_BOOT_DATA)
Simon Glass80025522022-01-29 14:14:04 -07003317 updated_fname = tools.get_output_filename('image-updated.bin')
3318 tools.write_file(updated_fname, data)
Simon Glass072959a2019-07-20 12:23:50 -06003319 entry_name = 'u-boot'
Simon Glassf8a54bc2019-07-20 12:23:56 -06003320 control.WriteEntry(updated_fname, entry_name, expected,
3321 allow_resize=False)
Simon Glass072959a2019-07-20 12:23:50 -06003322 data = control.ReadEntry(updated_fname, entry_name)
3323 self.assertEqual(expected, data)
3324
3325 # Check the state looks right.
3326 self.assertEqual('/binman/image', state.fdt_path_prefix)
3327
3328 # Now check we can write the first image
Simon Glass80025522022-01-29 14:14:04 -07003329 image_fname = tools.get_output_filename('first-image.bin')
3330 updated_fname = tools.get_output_filename('first-updated.bin')
3331 tools.write_file(updated_fname, tools.read_file(image_fname))
Simon Glass072959a2019-07-20 12:23:50 -06003332 entry_name = 'u-boot'
Simon Glassf8a54bc2019-07-20 12:23:56 -06003333 control.WriteEntry(updated_fname, entry_name, expected,
3334 allow_resize=False)
Simon Glass072959a2019-07-20 12:23:50 -06003335 data = control.ReadEntry(updated_fname, entry_name)
3336 self.assertEqual(expected, data)
3337
3338 # Check the state looks right.
3339 self.assertEqual('/binman/first-image', state.fdt_path_prefix)
Simon Glass39dd2152019-07-08 14:25:47 -06003340
Simon Glassfb30e292019-07-20 12:23:51 -06003341 def testUpdateFdtAllRepack(self):
3342 """Test that all device trees are updated with offset/size info"""
Marek Vasutf7413f02023-07-18 07:23:58 -06003343 self._SetupSplElf()
3344 self._SetupTplElf()
Simon Glassfb30e292019-07-20 12:23:51 -06003345 data = self._DoReadFileRealDtb('134_fdt_update_all_repack.dts')
3346 SECTION_SIZE = 0x300
3347 DTB_SIZE = 602
3348 FDTMAP_SIZE = 608
3349 base_expected = {
3350 'offset': 0,
3351 'size': SECTION_SIZE + DTB_SIZE * 2 + FDTMAP_SIZE,
3352 'image-pos': 0,
3353 'section:offset': 0,
3354 'section:size': SECTION_SIZE,
3355 'section:image-pos': 0,
3356 'section/u-boot-dtb:offset': 4,
3357 'section/u-boot-dtb:size': 636,
3358 'section/u-boot-dtb:image-pos': 4,
3359 'u-boot-spl-dtb:offset': SECTION_SIZE,
3360 'u-boot-spl-dtb:size': DTB_SIZE,
3361 'u-boot-spl-dtb:image-pos': SECTION_SIZE,
3362 'u-boot-tpl-dtb:offset': SECTION_SIZE + DTB_SIZE,
3363 'u-boot-tpl-dtb:image-pos': SECTION_SIZE + DTB_SIZE,
3364 'u-boot-tpl-dtb:size': DTB_SIZE,
3365 'fdtmap:offset': SECTION_SIZE + DTB_SIZE * 2,
3366 'fdtmap:size': FDTMAP_SIZE,
3367 'fdtmap:image-pos': SECTION_SIZE + DTB_SIZE * 2,
3368 }
3369 main_expected = {
3370 'section:orig-size': SECTION_SIZE,
3371 'section/u-boot-dtb:orig-offset': 4,
3372 }
3373
3374 # We expect three device-tree files in the output, with the first one
3375 # within a fixed-size section.
3376 # Read them in sequence. We look for an 'spl' property in the SPL tree,
3377 # and 'tpl' in the TPL tree, to make sure they are distinct from the
3378 # main U-Boot tree. All three should have the same positions and offset
3379 # except that the main tree should include the main_expected properties
3380 start = 4
3381 for item in ['', 'spl', 'tpl', None]:
3382 if item is None:
3383 start += 16 # Move past fdtmap header
3384 dtb = fdt.Fdt.FromData(data[start:])
3385 dtb.Scan()
3386 props = self._GetPropTree(dtb,
3387 BASE_DTB_PROPS + REPACK_DTB_PROPS + ['spl', 'tpl'],
3388 prefix='/' if item is None else '/binman/')
3389 expected = dict(base_expected)
3390 if item:
3391 expected[item] = 0
3392 else:
3393 # Main DTB and fdtdec should include the 'orig-' properties
3394 expected.update(main_expected)
3395 # Helpful for debugging:
3396 #for prop in sorted(props):
3397 #print('prop %s %s %s' % (prop, props[prop], expected[prop]))
3398 self.assertEqual(expected, props)
3399 if item == '':
3400 start = SECTION_SIZE
3401 else:
3402 start += dtb._fdt_obj.totalsize()
3403
Simon Glass11453762019-07-20 12:23:55 -06003404 def testFdtmapHeaderMiddle(self):
3405 """Test an FDT map in the middle of an image when it should be at end"""
3406 with self.assertRaises(ValueError) as e:
3407 self._DoReadFileRealDtb('135_fdtmap_hdr_middle.dts')
3408 self.assertIn("Invalid sibling order 'middle' for image-header: Must be at 'end' to match location",
3409 str(e.exception))
3410
3411 def testFdtmapHeaderStartBad(self):
3412 """Test an FDT map in middle of an image when it should be at start"""
3413 with self.assertRaises(ValueError) as e:
3414 self._DoReadFileRealDtb('136_fdtmap_hdr_startbad.dts')
3415 self.assertIn("Invalid sibling order 'end' for image-header: Must be at 'start' to match location",
3416 str(e.exception))
3417
3418 def testFdtmapHeaderEndBad(self):
3419 """Test an FDT map at the start of an image when it should be at end"""
3420 with self.assertRaises(ValueError) as e:
3421 self._DoReadFileRealDtb('137_fdtmap_hdr_endbad.dts')
3422 self.assertIn("Invalid sibling order 'start' for image-header: Must be at 'end' to match location",
3423 str(e.exception))
3424
3425 def testFdtmapHeaderNoSize(self):
3426 """Test an image header at the end of an image with undefined size"""
3427 self._DoReadFileRealDtb('138_fdtmap_hdr_nosize.dts')
3428
Simon Glassf8a54bc2019-07-20 12:23:56 -06003429 def testReplaceResize(self):
3430 """Test replacing a single file in an entry with a larger file"""
3431 expected = U_BOOT_DATA + b'x'
3432 data, _, image = self._RunReplaceCmd('u-boot', expected,
3433 dts='139_replace_repack.dts')
3434 self.assertEqual(expected, data)
3435
3436 entries = image.GetEntries()
3437 dtb_data = entries['u-boot-dtb'].data
3438 dtb = fdt.Fdt.FromData(dtb_data)
3439 dtb.Scan()
3440
3441 # The u-boot section should now be larger in the dtb
3442 node = dtb.GetNode('/binman/u-boot')
3443 self.assertEqual(len(expected), fdt_util.GetInt(node, 'size'))
3444
3445 # Same for the fdtmap
3446 fdata = entries['fdtmap'].data
3447 fdtb = fdt.Fdt.FromData(fdata[fdtmap.FDTMAP_HDR_LEN:])
3448 fdtb.Scan()
3449 fnode = fdtb.GetNode('/u-boot')
3450 self.assertEqual(len(expected), fdt_util.GetInt(fnode, 'size'))
3451
3452 def testReplaceResizeNoRepack(self):
3453 """Test replacing an entry with a larger file when not allowed"""
3454 expected = U_BOOT_DATA + b'x'
3455 with self.assertRaises(ValueError) as e:
3456 self._RunReplaceCmd('u-boot', expected)
3457 self.assertIn('Entry data size does not match, but allow-repack is not present for this image',
3458 str(e.exception))
3459
Simon Glass9d8ee322019-07-20 12:23:58 -06003460 def testEntryShrink(self):
3461 """Test contracting an entry after it is packed"""
3462 try:
3463 state.SetAllowEntryContraction(True)
3464 data = self._DoReadFileDtb('140_entry_shrink.dts',
3465 update_dtb=True)[0]
3466 finally:
3467 state.SetAllowEntryContraction(False)
3468 self.assertEqual(b'a', data[:1])
3469 self.assertEqual(U_BOOT_DATA, data[1:1 + len(U_BOOT_DATA)])
3470 self.assertEqual(b'a', data[-1:])
3471
3472 def testEntryShrinkFail(self):
3473 """Test not being allowed to contract an entry after it is packed"""
3474 data = self._DoReadFileDtb('140_entry_shrink.dts', update_dtb=True)[0]
3475
3476 # In this case there is a spare byte at the end of the data. The size of
3477 # the contents is only 1 byte but we still have the size before it
3478 # shrunk.
3479 self.assertEqual(b'a\0', data[:2])
3480 self.assertEqual(U_BOOT_DATA, data[2:2 + len(U_BOOT_DATA)])
3481 self.assertEqual(b'a\0', data[-2:])
3482
Simon Glass70e32982019-07-20 12:24:01 -06003483 def testDescriptorOffset(self):
3484 """Test that the Intel descriptor is always placed at at the start"""
3485 data = self._DoReadFileDtb('141_descriptor_offset.dts')
3486 image = control.images['image']
3487 entries = image.GetEntries()
3488 desc = entries['intel-descriptor']
3489 self.assertEqual(0xff800000, desc.offset);
3490 self.assertEqual(0xff800000, desc.image_pos);
3491
Simon Glass37fdd142019-07-20 12:24:06 -06003492 def testReplaceCbfs(self):
3493 """Test replacing a single file in CBFS without changing the size"""
3494 self._CheckLz4()
3495 expected = b'x' * len(U_BOOT_DATA)
3496 data = self._DoReadFileRealDtb('142_replace_cbfs.dts')
Simon Glass80025522022-01-29 14:14:04 -07003497 updated_fname = tools.get_output_filename('image-updated.bin')
3498 tools.write_file(updated_fname, data)
Simon Glass37fdd142019-07-20 12:24:06 -06003499 entry_name = 'section/cbfs/u-boot'
3500 control.WriteEntry(updated_fname, entry_name, expected,
3501 allow_resize=True)
3502 data = control.ReadEntry(updated_fname, entry_name)
3503 self.assertEqual(expected, data)
3504
3505 def testReplaceResizeCbfs(self):
3506 """Test replacing a single file in CBFS with one of a different size"""
3507 self._CheckLz4()
3508 expected = U_BOOT_DATA + b'x'
3509 data = self._DoReadFileRealDtb('142_replace_cbfs.dts')
Simon Glass80025522022-01-29 14:14:04 -07003510 updated_fname = tools.get_output_filename('image-updated.bin')
3511 tools.write_file(updated_fname, data)
Simon Glass37fdd142019-07-20 12:24:06 -06003512 entry_name = 'section/cbfs/u-boot'
3513 control.WriteEntry(updated_fname, entry_name, expected,
3514 allow_resize=True)
3515 data = control.ReadEntry(updated_fname, entry_name)
3516 self.assertEqual(expected, data)
3517
Simon Glass30033c22019-07-20 12:24:15 -06003518 def _SetupForReplace(self):
3519 """Set up some files to use to replace entries
3520
3521 This generates an image, copies it to a new file, extracts all the files
3522 in it and updates some of them
3523
3524 Returns:
3525 List
3526 Image filename
3527 Output directory
3528 Expected values for updated entries, each a string
3529 """
3530 data = self._DoReadFileRealDtb('143_replace_all.dts')
3531
Simon Glass80025522022-01-29 14:14:04 -07003532 updated_fname = tools.get_output_filename('image-updated.bin')
3533 tools.write_file(updated_fname, data)
Simon Glass30033c22019-07-20 12:24:15 -06003534
3535 outdir = os.path.join(self._indir, 'extract')
3536 einfos = control.ExtractEntries(updated_fname, None, outdir, [])
3537
3538 expected1 = b'x' + U_BOOT_DATA + b'y'
3539 u_boot_fname1 = os.path.join(outdir, 'u-boot')
Simon Glass80025522022-01-29 14:14:04 -07003540 tools.write_file(u_boot_fname1, expected1)
Simon Glass30033c22019-07-20 12:24:15 -06003541
3542 expected2 = b'a' + U_BOOT_DATA + b'b'
3543 u_boot_fname2 = os.path.join(outdir, 'u-boot2')
Simon Glass80025522022-01-29 14:14:04 -07003544 tools.write_file(u_boot_fname2, expected2)
Simon Glass30033c22019-07-20 12:24:15 -06003545
3546 expected_text = b'not the same text'
3547 text_fname = os.path.join(outdir, 'text')
Simon Glass80025522022-01-29 14:14:04 -07003548 tools.write_file(text_fname, expected_text)
Simon Glass30033c22019-07-20 12:24:15 -06003549
3550 dtb_fname = os.path.join(outdir, 'u-boot-dtb')
3551 dtb = fdt.FdtScan(dtb_fname)
3552 node = dtb.GetNode('/binman/text')
3553 node.AddString('my-property', 'the value')
3554 dtb.Sync(auto_resize=True)
3555 dtb.Flush()
3556
3557 return updated_fname, outdir, expected1, expected2, expected_text
3558
3559 def _CheckReplaceMultiple(self, entry_paths):
3560 """Handle replacing the contents of multiple entries
3561
3562 Args:
3563 entry_paths: List of entry paths to replace
3564
3565 Returns:
3566 List
3567 Dict of entries in the image:
3568 key: Entry name
3569 Value: Entry object
3570 Expected values for updated entries, each a string
3571 """
3572 updated_fname, outdir, expected1, expected2, expected_text = (
3573 self._SetupForReplace())
3574 control.ReplaceEntries(updated_fname, None, outdir, entry_paths)
3575
3576 image = Image.FromFile(updated_fname)
3577 image.LoadData()
3578 return image.GetEntries(), expected1, expected2, expected_text
3579
3580 def testReplaceAll(self):
3581 """Test replacing the contents of all entries"""
3582 entries, expected1, expected2, expected_text = (
3583 self._CheckReplaceMultiple([]))
3584 data = entries['u-boot'].data
3585 self.assertEqual(expected1, data)
3586
3587 data = entries['u-boot2'].data
3588 self.assertEqual(expected2, data)
3589
3590 data = entries['text'].data
3591 self.assertEqual(expected_text, data)
3592
3593 # Check that the device tree is updated
3594 data = entries['u-boot-dtb'].data
3595 dtb = fdt.Fdt.FromData(data)
3596 dtb.Scan()
3597 node = dtb.GetNode('/binman/text')
3598 self.assertEqual('the value', node.props['my-property'].value)
3599
3600 def testReplaceSome(self):
3601 """Test replacing the contents of a few entries"""
3602 entries, expected1, expected2, expected_text = (
3603 self._CheckReplaceMultiple(['u-boot2', 'text']))
3604
3605 # This one should not change
3606 data = entries['u-boot'].data
3607 self.assertEqual(U_BOOT_DATA, data)
3608
3609 data = entries['u-boot2'].data
3610 self.assertEqual(expected2, data)
3611
3612 data = entries['text'].data
3613 self.assertEqual(expected_text, data)
3614
3615 def testReplaceCmd(self):
3616 """Test replacing a file fron an image on the command line"""
3617 self._DoReadFileRealDtb('143_replace_all.dts')
3618
3619 try:
3620 tmpdir, updated_fname = self._SetupImageInTmpdir()
3621
3622 fname = os.path.join(tmpdir, 'update-u-boot.bin')
3623 expected = b'x' * len(U_BOOT_DATA)
Simon Glass80025522022-01-29 14:14:04 -07003624 tools.write_file(fname, expected)
Simon Glass30033c22019-07-20 12:24:15 -06003625
3626 self._DoBinman('replace', '-i', updated_fname, 'u-boot', '-f', fname)
Simon Glass80025522022-01-29 14:14:04 -07003627 data = tools.read_file(updated_fname)
Simon Glass30033c22019-07-20 12:24:15 -06003628 self.assertEqual(expected, data[:len(expected)])
3629 map_fname = os.path.join(tmpdir, 'image-updated.map')
3630 self.assertFalse(os.path.exists(map_fname))
3631 finally:
3632 shutil.rmtree(tmpdir)
3633
3634 def testReplaceCmdSome(self):
3635 """Test replacing some files fron an image on the command line"""
3636 updated_fname, outdir, expected1, expected2, expected_text = (
3637 self._SetupForReplace())
3638
3639 self._DoBinman('replace', '-i', updated_fname, '-I', outdir,
3640 'u-boot2', 'text')
3641
Simon Glass80025522022-01-29 14:14:04 -07003642 tools.prepare_output_dir(None)
Simon Glass30033c22019-07-20 12:24:15 -06003643 image = Image.FromFile(updated_fname)
3644 image.LoadData()
3645 entries = image.GetEntries()
3646
3647 # This one should not change
3648 data = entries['u-boot'].data
3649 self.assertEqual(U_BOOT_DATA, data)
3650
3651 data = entries['u-boot2'].data
3652 self.assertEqual(expected2, data)
3653
3654 data = entries['text'].data
3655 self.assertEqual(expected_text, data)
3656
3657 def testReplaceMissing(self):
3658 """Test replacing entries where the file is missing"""
3659 updated_fname, outdir, expected1, expected2, expected_text = (
3660 self._SetupForReplace())
3661
3662 # Remove one of the files, to generate a warning
3663 u_boot_fname1 = os.path.join(outdir, 'u-boot')
3664 os.remove(u_boot_fname1)
3665
3666 with test_util.capture_sys_output() as (stdout, stderr):
3667 control.ReplaceEntries(updated_fname, None, outdir, [])
3668 self.assertIn("Skipping entry '/u-boot' from missing file",
Simon Glass6e02f7c2020-07-09 18:39:39 -06003669 stderr.getvalue())
Simon Glass30033c22019-07-20 12:24:15 -06003670
3671 def testReplaceCmdMap(self):
3672 """Test replacing a file fron an image on the command line"""
3673 self._DoReadFileRealDtb('143_replace_all.dts')
3674
3675 try:
3676 tmpdir, updated_fname = self._SetupImageInTmpdir()
3677
3678 fname = os.path.join(self._indir, 'update-u-boot.bin')
3679 expected = b'x' * len(U_BOOT_DATA)
Simon Glass80025522022-01-29 14:14:04 -07003680 tools.write_file(fname, expected)
Simon Glass30033c22019-07-20 12:24:15 -06003681
3682 self._DoBinman('replace', '-i', updated_fname, 'u-boot',
3683 '-f', fname, '-m')
3684 map_fname = os.path.join(tmpdir, 'image-updated.map')
3685 self.assertTrue(os.path.exists(map_fname))
3686 finally:
3687 shutil.rmtree(tmpdir)
3688
3689 def testReplaceNoEntryPaths(self):
3690 """Test replacing an entry without an entry path"""
3691 self._DoReadFileRealDtb('143_replace_all.dts')
Simon Glass80025522022-01-29 14:14:04 -07003692 image_fname = tools.get_output_filename('image.bin')
Simon Glass30033c22019-07-20 12:24:15 -06003693 with self.assertRaises(ValueError) as e:
3694 control.ReplaceEntries(image_fname, 'fname', None, [])
3695 self.assertIn('Must specify an entry path to read with -f',
3696 str(e.exception))
3697
3698 def testReplaceTooManyEntryPaths(self):
3699 """Test extracting some entries"""
3700 self._DoReadFileRealDtb('143_replace_all.dts')
Simon Glass80025522022-01-29 14:14:04 -07003701 image_fname = tools.get_output_filename('image.bin')
Simon Glass30033c22019-07-20 12:24:15 -06003702 with self.assertRaises(ValueError) as e:
3703 control.ReplaceEntries(image_fname, 'fname', None, ['a', 'b'])
3704 self.assertIn('Must specify exactly one entry path to write with -f',
3705 str(e.exception))
3706
Simon Glass0b074d62019-08-24 07:22:48 -06003707 def testPackReset16(self):
3708 """Test that an image with an x86 reset16 region can be created"""
3709 data = self._DoReadFile('144_x86_reset16.dts')
3710 self.assertEqual(X86_RESET16_DATA, data[:len(X86_RESET16_DATA)])
3711
3712 def testPackReset16Spl(self):
3713 """Test that an image with an x86 reset16-spl region can be created"""
3714 data = self._DoReadFile('145_x86_reset16_spl.dts')
3715 self.assertEqual(X86_RESET16_SPL_DATA, data[:len(X86_RESET16_SPL_DATA)])
3716
3717 def testPackReset16Tpl(self):
3718 """Test that an image with an x86 reset16-tpl region can be created"""
3719 data = self._DoReadFile('146_x86_reset16_tpl.dts')
3720 self.assertEqual(X86_RESET16_TPL_DATA, data[:len(X86_RESET16_TPL_DATA)])
3721
Simon Glass232f90c2019-08-24 07:22:50 -06003722 def testPackIntelFit(self):
3723 """Test that an image with an Intel FIT and pointer can be created"""
3724 data = self._DoReadFile('147_intel_fit.dts')
3725 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
3726 fit = data[16:32];
3727 self.assertEqual(b'_FIT_ \x01\x00\x00\x00\x00\x01\x80}' , fit)
3728 ptr = struct.unpack('<i', data[0x40:0x44])[0]
3729
3730 image = control.images['image']
3731 entries = image.GetEntries()
3732 expected_ptr = entries['intel-fit'].image_pos - (1 << 32)
3733 self.assertEqual(expected_ptr, ptr)
3734
3735 def testPackIntelFitMissing(self):
3736 """Test detection of a FIT pointer with not FIT region"""
3737 with self.assertRaises(ValueError) as e:
3738 self._DoReadFile('148_intel_fit_missing.dts')
3739 self.assertIn("'intel-fit-ptr' section must have an 'intel-fit' sibling",
3740 str(e.exception))
3741
Simon Glass72555fa2019-11-06 17:22:44 -07003742 def _CheckSymbolsTplSection(self, dts, expected_vals):
3743 data = self._DoReadFile(dts)
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003744 sym_values = struct.pack('<LLQLL', elf.BINMAN_SYM_MAGIC_VALUE, *expected_vals)
Simon Glass3eb5b202019-08-24 07:23:00 -06003745 upto1 = 4 + len(U_BOOT_SPL_DATA)
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003746 expected1 = tools.get_bytes(0xff, 4) + sym_values + U_BOOT_SPL_DATA[24:]
Simon Glass3eb5b202019-08-24 07:23:00 -06003747 self.assertEqual(expected1, data[:upto1])
3748
3749 upto2 = upto1 + 1 + len(U_BOOT_SPL_DATA)
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003750 expected2 = tools.get_bytes(0xff, 1) + sym_values + U_BOOT_SPL_DATA[24:]
Simon Glass3eb5b202019-08-24 07:23:00 -06003751 self.assertEqual(expected2, data[upto1:upto2])
3752
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003753 upto3 = 0x3c + len(U_BOOT_DATA)
Simon Glass80025522022-01-29 14:14:04 -07003754 expected3 = tools.get_bytes(0xff, 1) + U_BOOT_DATA
Simon Glass3eb5b202019-08-24 07:23:00 -06003755 self.assertEqual(expected3, data[upto2:upto3])
3756
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003757 expected4 = sym_values + U_BOOT_TPL_DATA[24:]
Simon Glass72555fa2019-11-06 17:22:44 -07003758 self.assertEqual(expected4, data[upto3:upto3 + len(U_BOOT_TPL_DATA)])
3759
3760 def testSymbolsTplSection(self):
3761 """Test binman can assign symbols embedded in U-Boot TPL in a section"""
3762 self._SetupSplElf('u_boot_binman_syms')
3763 self._SetupTplElf('u_boot_binman_syms')
3764 self._CheckSymbolsTplSection('149_symbols_tpl.dts',
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003765 [0x04, 0x20, 0x10 + 0x3c, 0x04])
Simon Glass72555fa2019-11-06 17:22:44 -07003766
3767 def testSymbolsTplSectionX86(self):
3768 """Test binman can assign symbols in a section with end-at-4gb"""
3769 self._SetupSplElf('u_boot_binman_syms_x86')
3770 self._SetupTplElf('u_boot_binman_syms_x86')
3771 self._CheckSymbolsTplSection('155_symbols_tpl_x86.dts',
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003772 [0xffffff04, 0xffffff20, 0xffffff3c,
Simon Glass72555fa2019-11-06 17:22:44 -07003773 0x04])
Simon Glass3eb5b202019-08-24 07:23:00 -06003774
Simon Glass98c59572019-08-24 07:23:03 -06003775 def testPackX86RomIfwiSectiom(self):
3776 """Test that a section can be placed in an IFWI region"""
3777 self._SetupIfwi('fitimage.bin')
3778 data = self._DoReadFile('151_x86_rom_ifwi_section.dts')
3779 self._CheckIfwi(data)
3780
Simon Glassba7985d2019-08-24 07:23:07 -06003781 def testPackFspM(self):
3782 """Test that an image with a FSP memory-init binary can be created"""
3783 data = self._DoReadFile('152_intel_fsp_m.dts')
3784 self.assertEqual(FSP_M_DATA, data[:len(FSP_M_DATA)])
3785
Simon Glass4d9086d2019-10-20 21:31:35 -06003786 def testPackFspS(self):
3787 """Test that an image with a FSP silicon-init binary can be created"""
3788 data = self._DoReadFile('153_intel_fsp_s.dts')
3789 self.assertEqual(FSP_S_DATA, data[:len(FSP_S_DATA)])
Simon Glassba7985d2019-08-24 07:23:07 -06003790
Simon Glass9ea87b22019-10-20 21:31:36 -06003791 def testPackFspT(self):
3792 """Test that an image with a FSP temp-ram-init binary can be created"""
3793 data = self._DoReadFile('154_intel_fsp_t.dts')
3794 self.assertEqual(FSP_T_DATA, data[:len(FSP_T_DATA)])
3795
Simon Glass48f3aad2020-07-09 18:39:31 -06003796 def testMkimage(self):
3797 """Test using mkimage to build an image"""
Marek Vasutf7413f02023-07-18 07:23:58 -06003798 self._SetupSplElf()
Simon Glass48f3aad2020-07-09 18:39:31 -06003799 data = self._DoReadFile('156_mkimage.dts')
3800
3801 # Just check that the data appears in the file somewhere
3802 self.assertIn(U_BOOT_SPL_DATA, data)
3803
Simon Glass66152ce2022-01-09 20:14:09 -07003804 def testMkimageMissing(self):
3805 """Test that binman still produces an image if mkimage is missing"""
Marek Vasutf7413f02023-07-18 07:23:58 -06003806 self._SetupSplElf()
Simon Glass66152ce2022-01-09 20:14:09 -07003807 with test_util.capture_sys_output() as (_, stderr):
3808 self._DoTestFile('156_mkimage.dts',
3809 force_missing_bintools='mkimage')
3810 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07003811 self.assertRegex(err, "Image 'image'.*missing bintools.*: mkimage")
Simon Glass66152ce2022-01-09 20:14:09 -07003812
Simon Glass5e560182020-07-09 18:39:36 -06003813 def testExtblob(self):
3814 """Test an image with an external blob"""
3815 data = self._DoReadFile('157_blob_ext.dts')
3816 self.assertEqual(REFCODE_DATA, data)
3817
3818 def testExtblobMissing(self):
3819 """Test an image with a missing external blob"""
3820 with self.assertRaises(ValueError) as e:
3821 self._DoReadFile('158_blob_ext_missing.dts')
3822 self.assertIn("Filename 'missing-file' not found in input path",
3823 str(e.exception))
3824
Simon Glass5d94cc62020-07-09 18:39:38 -06003825 def testExtblobMissingOk(self):
3826 """Test an image with an missing external blob that is allowed"""
Simon Glassa003cd32020-07-09 18:39:40 -06003827 with test_util.capture_sys_output() as (stdout, stderr):
Simon Glass6bce5dc2022-11-09 19:14:42 -07003828 ret = self._DoTestFile('158_blob_ext_missing.dts',
3829 allow_missing=True)
3830 self.assertEqual(103, ret)
Simon Glassa003cd32020-07-09 18:39:40 -06003831 err = stderr.getvalue()
Jonas Karlmanda423fc2023-07-18 20:34:39 +00003832 self.assertIn('(missing-file)', err)
Simon Glass49cd2b32023-02-07 14:34:18 -07003833 self.assertRegex(err, "Image 'image'.*missing.*: blob-ext")
Simon Glass6bce5dc2022-11-09 19:14:42 -07003834 self.assertIn('Some images are invalid', err)
3835
3836 def testExtblobMissingOkFlag(self):
3837 """Test an image with an missing external blob allowed with -W"""
3838 with test_util.capture_sys_output() as (stdout, stderr):
3839 ret = self._DoTestFile('158_blob_ext_missing.dts',
3840 allow_missing=True, ignore_missing=True)
3841 self.assertEqual(0, ret)
3842 err = stderr.getvalue()
Jonas Karlmanda423fc2023-07-18 20:34:39 +00003843 self.assertIn('(missing-file)', err)
Simon Glass49cd2b32023-02-07 14:34:18 -07003844 self.assertRegex(err, "Image 'image'.*missing.*: blob-ext")
Simon Glass6bce5dc2022-11-09 19:14:42 -07003845 self.assertIn('Some images are invalid', err)
Simon Glassa003cd32020-07-09 18:39:40 -06003846
3847 def testExtblobMissingOkSect(self):
3848 """Test an image with an missing external blob that is allowed"""
3849 with test_util.capture_sys_output() as (stdout, stderr):
3850 self._DoTestFile('159_blob_ext_missing_sect.dts',
3851 allow_missing=True)
3852 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07003853 self.assertRegex(err, "Image 'image'.*missing.*: blob-ext blob-ext2")
Simon Glass5d94cc62020-07-09 18:39:38 -06003854
Simon Glasse88cef92020-07-09 18:39:41 -06003855 def testPackX86RomMeMissingDesc(self):
3856 """Test that an missing Intel descriptor entry is allowed"""
Simon Glasse88cef92020-07-09 18:39:41 -06003857 with test_util.capture_sys_output() as (stdout, stderr):
Simon Glass14c596c2020-07-25 15:11:19 -06003858 self._DoTestFile('164_x86_rom_me_missing.dts', allow_missing=True)
Simon Glasse88cef92020-07-09 18:39:41 -06003859 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07003860 self.assertRegex(err, "Image 'image'.*missing.*: intel-descriptor")
Simon Glasse88cef92020-07-09 18:39:41 -06003861
3862 def testPackX86RomMissingIfwi(self):
3863 """Test that an x86 ROM with Integrated Firmware Image can be created"""
3864 self._SetupIfwi('fitimage.bin')
3865 pathname = os.path.join(self._indir, 'fitimage.bin')
3866 os.remove(pathname)
3867 with test_util.capture_sys_output() as (stdout, stderr):
3868 self._DoTestFile('111_x86_rom_ifwi.dts', allow_missing=True)
3869 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07003870 self.assertRegex(err, "Image 'image'.*missing.*: intel-ifwi")
Simon Glasse88cef92020-07-09 18:39:41 -06003871
Simon Glass2a0fa982022-02-11 13:23:21 -07003872 def testPackOverlapZero(self):
Simon Glassd70829a2020-07-09 18:39:42 -06003873 """Test that zero-size overlapping regions are ignored"""
3874 self._DoTestFile('160_pack_overlap_zero.dts')
3875
Alper Nebi Yasak09fb0612022-02-08 01:08:04 +03003876 def _CheckSimpleFitData(self, fit_data, kernel_data, fdt1_data):
Simon Glass45d556d2020-07-09 18:39:45 -06003877 # The data should be inside the FIT
3878 dtb = fdt.Fdt.FromData(fit_data)
3879 dtb.Scan()
3880 fnode = dtb.GetNode('/images/kernel')
3881 self.assertIn('data', fnode.props)
3882
3883 fname = os.path.join(self._indir, 'fit_data.fit')
Simon Glass80025522022-01-29 14:14:04 -07003884 tools.write_file(fname, fit_data)
3885 out = tools.run('dumpimage', '-l', fname)
Simon Glass45d556d2020-07-09 18:39:45 -06003886
3887 # Check a few features to make sure the plumbing works. We don't need
3888 # to test the operation of mkimage or dumpimage here. First convert the
3889 # output into a dict where the keys are the fields printed by dumpimage
3890 # and the values are a list of values for each field
3891 lines = out.splitlines()
3892
3893 # Converts "Compression: gzip compressed" into two groups:
3894 # 'Compression' and 'gzip compressed'
3895 re_line = re.compile(r'^ *([^:]*)(?:: *(.*))?$')
3896 vals = collections.defaultdict(list)
3897 for line in lines:
3898 mat = re_line.match(line)
3899 vals[mat.group(1)].append(mat.group(2))
3900
Brandon Maiera657bc62024-06-04 16:16:05 +00003901 self.assertEqual('FIT description: test-desc', lines[0])
Simon Glass45d556d2020-07-09 18:39:45 -06003902 self.assertIn('Created:', lines[1])
3903 self.assertIn('Image 0 (kernel)', vals)
3904 self.assertIn('Hash value', vals)
3905 data_sizes = vals.get('Data Size')
3906 self.assertIsNotNone(data_sizes)
3907 self.assertEqual(2, len(data_sizes))
3908 # Format is "4 Bytes = 0.00 KiB = 0.00 MiB" so take the first word
Alper Nebi Yasak09fb0612022-02-08 01:08:04 +03003909 self.assertEqual(len(kernel_data), int(data_sizes[0].split()[0]))
3910 self.assertEqual(len(fdt1_data), int(data_sizes[1].split()[0]))
3911
Alper Nebi Yasak1a0ee0f2022-03-27 18:31:47 +03003912 # Check if entry listing correctly omits /images/
3913 image = control.images['image']
3914 fit_entry = image.GetEntries()['fit']
3915 subentries = list(fit_entry.GetEntries().keys())
3916 expected = ['kernel', 'fdt-1']
3917 self.assertEqual(expected, subentries)
3918
Alper Nebi Yasak09fb0612022-02-08 01:08:04 +03003919 def testSimpleFit(self):
3920 """Test an image with a FIT inside"""
Marek Vasutf7413f02023-07-18 07:23:58 -06003921 self._SetupSplElf()
Alper Nebi Yasak09fb0612022-02-08 01:08:04 +03003922 data = self._DoReadFile('161_fit.dts')
3923 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
3924 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
3925 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
3926
3927 self._CheckSimpleFitData(fit_data, U_BOOT_DATA, U_BOOT_SPL_DTB_DATA)
3928
3929 def testSimpleFitExpandsSubentries(self):
3930 """Test that FIT images expand their subentries"""
3931 data = self._DoReadFileDtb('161_fit.dts', use_expanded=True)[0]
3932 self.assertEqual(U_BOOT_EXP_DATA, data[:len(U_BOOT_EXP_DATA)])
3933 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
3934 fit_data = data[len(U_BOOT_EXP_DATA):-len(U_BOOT_NODTB_DATA)]
3935
3936 self._CheckSimpleFitData(fit_data, U_BOOT_EXP_DATA, U_BOOT_SPL_DTB_DATA)
Simon Glass45d556d2020-07-09 18:39:45 -06003937
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03003938 def testSimpleFitImagePos(self):
3939 """Test that we have correct image-pos for FIT subentries"""
3940 data, _, _, out_dtb_fname = self._DoReadFileDtb('161_fit.dts',
3941 update_dtb=True)
3942 dtb = fdt.Fdt(out_dtb_fname)
3943 dtb.Scan()
3944 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
3945
Simon Glassb7bad182022-03-05 20:19:01 -07003946 self.maxDiff = None
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03003947 self.assertEqual({
3948 'image-pos': 0,
3949 'offset': 0,
3950 'size': 1890,
3951
3952 'u-boot:image-pos': 0,
3953 'u-boot:offset': 0,
3954 'u-boot:size': 4,
3955
3956 'fit:image-pos': 4,
3957 'fit:offset': 4,
3958 'fit:size': 1840,
3959
Simon Glassb7bad182022-03-05 20:19:01 -07003960 'fit/images/kernel:image-pos': 304,
3961 'fit/images/kernel:offset': 300,
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03003962 'fit/images/kernel:size': 4,
3963
Simon Glassb7bad182022-03-05 20:19:01 -07003964 'fit/images/kernel/u-boot:image-pos': 304,
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03003965 'fit/images/kernel/u-boot:offset': 0,
3966 'fit/images/kernel/u-boot:size': 4,
3967
Simon Glassb7bad182022-03-05 20:19:01 -07003968 'fit/images/fdt-1:image-pos': 552,
3969 'fit/images/fdt-1:offset': 548,
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03003970 'fit/images/fdt-1:size': 6,
3971
Simon Glassb7bad182022-03-05 20:19:01 -07003972 'fit/images/fdt-1/u-boot-spl-dtb:image-pos': 552,
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03003973 'fit/images/fdt-1/u-boot-spl-dtb:offset': 0,
3974 'fit/images/fdt-1/u-boot-spl-dtb:size': 6,
3975
3976 'u-boot-nodtb:image-pos': 1844,
3977 'u-boot-nodtb:offset': 1844,
3978 'u-boot-nodtb:size': 46,
3979 }, props)
3980
3981 # Actually check the data is where we think it is
3982 for node, expected in [
3983 ("u-boot", U_BOOT_DATA),
3984 ("fit/images/kernel", U_BOOT_DATA),
3985 ("fit/images/kernel/u-boot", U_BOOT_DATA),
3986 ("fit/images/fdt-1", U_BOOT_SPL_DTB_DATA),
3987 ("fit/images/fdt-1/u-boot-spl-dtb", U_BOOT_SPL_DTB_DATA),
3988 ("u-boot-nodtb", U_BOOT_NODTB_DATA),
3989 ]:
3990 image_pos = props[f"{node}:image-pos"]
3991 size = props[f"{node}:size"]
3992 self.assertEqual(len(expected), size)
3993 self.assertEqual(expected, data[image_pos:image_pos+size])
3994
Simon Glass45d556d2020-07-09 18:39:45 -06003995 def testFitExternal(self):
Simon Glass31ee50f2020-09-01 05:13:55 -06003996 """Test an image with an FIT with external images"""
Simon Glass45d556d2020-07-09 18:39:45 -06003997 data = self._DoReadFile('162_fit_external.dts')
3998 fit_data = data[len(U_BOOT_DATA):-2] # _testing is 2 bytes
3999
Simon Glass7932c882022-01-09 20:13:39 -07004000 # Size of the external-data region as set up by mkimage
4001 external_data_size = len(U_BOOT_DATA) + 2
4002 expected_size = (len(U_BOOT_DATA) + 0x400 +
Simon Glass80025522022-01-29 14:14:04 -07004003 tools.align(external_data_size, 4) +
Simon Glass7932c882022-01-09 20:13:39 -07004004 len(U_BOOT_NODTB_DATA))
4005
Simon Glass45d556d2020-07-09 18:39:45 -06004006 # The data should be outside the FIT
4007 dtb = fdt.Fdt.FromData(fit_data)
4008 dtb.Scan()
4009 fnode = dtb.GetNode('/images/kernel')
4010 self.assertNotIn('data', fnode.props)
Simon Glass7932c882022-01-09 20:13:39 -07004011 self.assertEqual(len(U_BOOT_DATA),
4012 fdt_util.fdt32_to_cpu(fnode.props['data-size'].value))
4013 fit_pos = 0x400;
4014 self.assertEqual(
4015 fit_pos,
4016 fdt_util.fdt32_to_cpu(fnode.props['data-position'].value))
4017
Brandon Maiera657bc62024-06-04 16:16:05 +00004018 self.assertEqual(expected_size, len(data))
Simon Glass7932c882022-01-09 20:13:39 -07004019 actual_pos = len(U_BOOT_DATA) + fit_pos
4020 self.assertEqual(U_BOOT_DATA + b'aa',
4021 data[actual_pos:actual_pos + external_data_size])
Simon Glassfb30e292019-07-20 12:23:51 -06004022
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03004023 def testFitExternalImagePos(self):
4024 """Test that we have correct image-pos for external FIT subentries"""
4025 data, _, _, out_dtb_fname = self._DoReadFileDtb('162_fit_external.dts',
4026 update_dtb=True)
4027 dtb = fdt.Fdt(out_dtb_fname)
4028 dtb.Scan()
4029 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
4030
4031 self.assertEqual({
4032 'image-pos': 0,
4033 'offset': 0,
4034 'size': 1082,
4035
4036 'u-boot:image-pos': 0,
4037 'u-boot:offset': 0,
4038 'u-boot:size': 4,
4039
4040 'fit:size': 1032,
4041 'fit:offset': 4,
4042 'fit:image-pos': 4,
4043
4044 'fit/images/kernel:size': 4,
4045 'fit/images/kernel:offset': 1024,
4046 'fit/images/kernel:image-pos': 1028,
4047
4048 'fit/images/kernel/u-boot:size': 4,
4049 'fit/images/kernel/u-boot:offset': 0,
4050 'fit/images/kernel/u-boot:image-pos': 1028,
4051
4052 'fit/images/fdt-1:size': 2,
4053 'fit/images/fdt-1:offset': 1028,
4054 'fit/images/fdt-1:image-pos': 1032,
4055
4056 'fit/images/fdt-1/_testing:size': 2,
4057 'fit/images/fdt-1/_testing:offset': 0,
4058 'fit/images/fdt-1/_testing:image-pos': 1032,
4059
4060 'u-boot-nodtb:image-pos': 1036,
4061 'u-boot-nodtb:offset': 1036,
4062 'u-boot-nodtb:size': 46,
4063 }, props)
4064
4065 # Actually check the data is where we think it is
4066 for node, expected in [
4067 ("u-boot", U_BOOT_DATA),
4068 ("fit/images/kernel", U_BOOT_DATA),
4069 ("fit/images/kernel/u-boot", U_BOOT_DATA),
4070 ("fit/images/fdt-1", b'aa'),
4071 ("fit/images/fdt-1/_testing", b'aa'),
4072 ("u-boot-nodtb", U_BOOT_NODTB_DATA),
4073 ]:
4074 image_pos = props[f"{node}:image-pos"]
4075 size = props[f"{node}:size"]
4076 self.assertEqual(len(expected), size)
4077 self.assertEqual(expected, data[image_pos:image_pos+size])
4078
Simon Glass66152ce2022-01-09 20:14:09 -07004079 def testFitMissing(self):
Simon Glass039d65f2023-03-02 17:02:43 -07004080 """Test that binman complains if mkimage is missing"""
4081 with self.assertRaises(ValueError) as e:
4082 self._DoTestFile('162_fit_external.dts',
4083 force_missing_bintools='mkimage')
4084 self.assertIn("Node '/binman/fit': Missing tool: 'mkimage'",
4085 str(e.exception))
4086
4087 def testFitMissingOK(self):
Simon Glass66152ce2022-01-09 20:14:09 -07004088 """Test that binman still produces a FIT image if mkimage is missing"""
4089 with test_util.capture_sys_output() as (_, stderr):
Simon Glass039d65f2023-03-02 17:02:43 -07004090 self._DoTestFile('162_fit_external.dts', allow_missing=True,
Simon Glass66152ce2022-01-09 20:14:09 -07004091 force_missing_bintools='mkimage')
4092 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07004093 self.assertRegex(err, "Image 'image'.*missing bintools.*: mkimage")
Simon Glass66152ce2022-01-09 20:14:09 -07004094
Alper Nebi Yasak6aae2392020-08-31 12:58:18 +03004095 def testSectionIgnoreHashSignature(self):
4096 """Test that sections ignore hash, signature nodes for its data"""
4097 data = self._DoReadFile('165_section_ignore_hash_signature.dts')
4098 expected = (U_BOOT_DATA + U_BOOT_DATA)
4099 self.assertEqual(expected, data)
4100
Alper Nebi Yasakc5030602020-08-31 12:58:19 +03004101 def testPadInSections(self):
4102 """Test pad-before, pad-after for entries in sections"""
Simon Glassd12599d2020-10-26 17:40:09 -06004103 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4104 '166_pad_in_sections.dts', update_dtb=True)
Simon Glass80025522022-01-29 14:14:04 -07004105 expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) +
4106 U_BOOT_DATA + tools.get_bytes(ord('!'), 6) +
Alper Nebi Yasakc5030602020-08-31 12:58:19 +03004107 U_BOOT_DATA)
4108 self.assertEqual(expected, data)
4109
Simon Glassd12599d2020-10-26 17:40:09 -06004110 dtb = fdt.Fdt(out_dtb_fname)
4111 dtb.Scan()
4112 props = self._GetPropTree(dtb, ['size', 'image-pos', 'offset'])
4113 expected = {
4114 'image-pos': 0,
4115 'offset': 0,
4116 'size': 12 + 6 + 3 * len(U_BOOT_DATA),
4117
4118 'section:image-pos': 0,
4119 'section:offset': 0,
4120 'section:size': 12 + 6 + 3 * len(U_BOOT_DATA),
4121
4122 'section/before:image-pos': 0,
4123 'section/before:offset': 0,
4124 'section/before:size': len(U_BOOT_DATA),
4125
4126 'section/u-boot:image-pos': 4,
4127 'section/u-boot:offset': 4,
4128 'section/u-boot:size': 12 + len(U_BOOT_DATA) + 6,
4129
4130 'section/after:image-pos': 26,
4131 'section/after:offset': 26,
4132 'section/after:size': len(U_BOOT_DATA),
4133 }
4134 self.assertEqual(expected, props)
4135
Alper Nebi Yasakc261ec12020-08-31 12:58:20 +03004136 def testFitImageSubentryAlignment(self):
4137 """Test relative alignability of FIT image subentries"""
Alper Nebi Yasakd4553262022-02-08 01:08:07 +03004138 self._SetupSplElf()
Alper Nebi Yasakc261ec12020-08-31 12:58:20 +03004139 entry_args = {
4140 'test-id': TEXT_DATA,
4141 }
4142 data, _, _, _ = self._DoReadFileDtb('167_fit_image_subentry_alignment.dts',
4143 entry_args=entry_args)
4144 dtb = fdt.Fdt.FromData(data)
4145 dtb.Scan()
4146
4147 node = dtb.GetNode('/images/kernel')
4148 data = dtb.GetProps(node)["data"].bytes
4149 align_pad = 0x10 - (len(U_BOOT_SPL_DATA) % 0x10)
Simon Glass80025522022-01-29 14:14:04 -07004150 expected = (tools.get_bytes(0, 0x20) + U_BOOT_SPL_DATA +
4151 tools.get_bytes(0, align_pad) + U_BOOT_DATA)
Alper Nebi Yasakc261ec12020-08-31 12:58:20 +03004152 self.assertEqual(expected, data)
4153
4154 node = dtb.GetNode('/images/fdt-1')
4155 data = dtb.GetProps(node)["data"].bytes
Simon Glass80025522022-01-29 14:14:04 -07004156 expected = (U_BOOT_SPL_DTB_DATA + tools.get_bytes(0, 20) +
4157 tools.to_bytes(TEXT_DATA) + tools.get_bytes(0, 30) +
Alper Nebi Yasakc261ec12020-08-31 12:58:20 +03004158 U_BOOT_DTB_DATA)
4159 self.assertEqual(expected, data)
4160
4161 def testFitExtblobMissingOk(self):
4162 """Test a FIT with a missing external blob that is allowed"""
4163 with test_util.capture_sys_output() as (stdout, stderr):
4164 self._DoTestFile('168_fit_missing_blob.dts',
4165 allow_missing=True)
4166 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07004167 self.assertRegex(err, "Image 'image'.*missing.*: atf-bl31")
Alper Nebi Yasakc261ec12020-08-31 12:58:20 +03004168
Simon Glass21db0ff2020-09-01 05:13:54 -06004169 def testBlobNamedByArgMissing(self):
4170 """Test handling of a missing entry arg"""
4171 with self.assertRaises(ValueError) as e:
4172 self._DoReadFile('068_blob_named_by_arg.dts')
4173 self.assertIn("Missing required properties/entry args: cros-ec-rw-path",
4174 str(e.exception))
4175
Simon Glass559c4de2020-09-01 05:13:58 -06004176 def testPackBl31(self):
4177 """Test that an image with an ATF BL31 binary can be created"""
4178 data = self._DoReadFile('169_atf_bl31.dts')
4179 self.assertEqual(ATF_BL31_DATA, data[:len(ATF_BL31_DATA)])
4180
Samuel Holland9d8cc632020-10-21 21:12:15 -05004181 def testPackScp(self):
4182 """Test that an image with an SCP binary can be created"""
4183 data = self._DoReadFile('172_scp.dts')
4184 self.assertEqual(SCP_DATA, data[:len(SCP_DATA)])
4185
Simon Glassd2a9d6e2024-08-26 13:11:37 -06004186 def CheckFitFdt(self, dts='170_fit_fdt.dts', use_fdt_list=True,
4187 default_dt=None):
Simon Glasscd2783e2024-07-20 11:49:46 +01004188 """Check an image with an FIT with multiple FDT images"""
Simon Glassa435cd12020-09-01 05:13:59 -06004189 def _CheckFdt(seq, expected_data):
4190 """Check the FDT nodes
4191
4192 Args:
4193 seq: Sequence number to check (0 or 1)
4194 expected_data: Expected contents of 'data' property
4195 """
4196 name = 'fdt-%d' % seq
4197 fnode = dtb.GetNode('/images/%s' % name)
4198 self.assertIsNotNone(fnode)
4199 self.assertEqual({'description','type', 'compression', 'data'},
4200 set(fnode.props.keys()))
4201 self.assertEqual(expected_data, fnode.props['data'].bytes)
4202 self.assertEqual('fdt-test-fdt%d.dtb' % seq,
4203 fnode.props['description'].value)
Jan Kiszkaa1419df2022-02-28 17:06:20 +01004204 self.assertEqual(fnode.subnodes[0].name, 'hash')
Simon Glassa435cd12020-09-01 05:13:59 -06004205
4206 def _CheckConfig(seq, expected_data):
4207 """Check the configuration nodes
4208
4209 Args:
4210 seq: Sequence number to check (0 or 1)
4211 expected_data: Expected contents of 'data' property
4212 """
4213 cnode = dtb.GetNode('/configurations')
4214 self.assertIn('default', cnode.props)
Simon Glass1032acc2020-09-06 10:39:08 -06004215 self.assertEqual('config-2', cnode.props['default'].value)
Simon Glassa435cd12020-09-01 05:13:59 -06004216
4217 name = 'config-%d' % seq
4218 fnode = dtb.GetNode('/configurations/%s' % name)
4219 self.assertIsNotNone(fnode)
4220 self.assertEqual({'description','firmware', 'loadables', 'fdt'},
4221 set(fnode.props.keys()))
4222 self.assertEqual('conf-test-fdt%d.dtb' % seq,
4223 fnode.props['description'].value)
4224 self.assertEqual('fdt-%d' % seq, fnode.props['fdt'].value)
4225
4226 entry_args = {
Simon Glass1032acc2020-09-06 10:39:08 -06004227 'default-dt': 'test-fdt2',
Simon Glassa435cd12020-09-01 05:13:59 -06004228 }
Simon Glasscd2783e2024-07-20 11:49:46 +01004229 if use_fdt_list:
4230 entry_args['of-list'] = 'test-fdt1 test-fdt2'
Simon Glassd2a9d6e2024-08-26 13:11:37 -06004231 if default_dt:
4232 entry_args['default-dt'] = default_dt
Simon Glassa435cd12020-09-01 05:13:59 -06004233 data = self._DoReadFileDtb(
Simon Glasscd2783e2024-07-20 11:49:46 +01004234 dts,
Simon Glassa435cd12020-09-01 05:13:59 -06004235 entry_args=entry_args,
4236 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
4237 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
4238 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
4239
4240 dtb = fdt.Fdt.FromData(fit_data)
4241 dtb.Scan()
4242 fnode = dtb.GetNode('/images/kernel')
4243 self.assertIn('data', fnode.props)
4244
4245 # Check all the properties in fdt-1 and fdt-2
4246 _CheckFdt(1, TEST_FDT1_DATA)
4247 _CheckFdt(2, TEST_FDT2_DATA)
4248
4249 # Check configurations
4250 _CheckConfig(1, TEST_FDT1_DATA)
4251 _CheckConfig(2, TEST_FDT2_DATA)
4252
Simon Glasscd2783e2024-07-20 11:49:46 +01004253 def testFitFdt(self):
4254 """Test an image with an FIT with multiple FDT images"""
4255 self.CheckFitFdt()
4256
Simon Glassa435cd12020-09-01 05:13:59 -06004257 def testFitFdtMissingList(self):
4258 """Test handling of a missing 'of-list' entry arg"""
4259 with self.assertRaises(ValueError) as e:
Bin Meng16cf5662021-05-10 20:23:32 +08004260 self._DoReadFile('170_fit_fdt.dts')
Simon Glassa435cd12020-09-01 05:13:59 -06004261 self.assertIn("Generator node requires 'of-list' entry argument",
4262 str(e.exception))
4263
4264 def testFitFdtEmptyList(self):
4265 """Test handling of an empty 'of-list' entry arg"""
4266 entry_args = {
4267 'of-list': '',
4268 }
4269 data = self._DoReadFileDtb('170_fit_fdt.dts', entry_args=entry_args)[0]
4270
4271 def testFitFdtMissingProp(self):
4272 """Test handling of a missing 'fit,fdt-list' property"""
4273 with self.assertRaises(ValueError) as e:
4274 self._DoReadFile('171_fit_fdt_missing_prop.dts')
4275 self.assertIn("Generator node requires 'fit,fdt-list' property",
4276 str(e.exception))
Simon Glass559c4de2020-09-01 05:13:58 -06004277
Simon Glass1032acc2020-09-06 10:39:08 -06004278 def testFitFdtMissing(self):
4279 """Test handling of a missing 'default-dt' entry arg"""
4280 entry_args = {
4281 'of-list': 'test-fdt1 test-fdt2',
4282 }
4283 with self.assertRaises(ValueError) as e:
4284 self._DoReadFileDtb(
Bin Meng16cf5662021-05-10 20:23:32 +08004285 '170_fit_fdt.dts',
Simon Glass1032acc2020-09-06 10:39:08 -06004286 entry_args=entry_args,
4287 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
4288 self.assertIn("Generated 'default' node requires default-dt entry argument",
4289 str(e.exception))
4290
4291 def testFitFdtNotInList(self):
4292 """Test handling of a default-dt that is not in the of-list"""
4293 entry_args = {
4294 'of-list': 'test-fdt1 test-fdt2',
4295 'default-dt': 'test-fdt3',
4296 }
4297 with self.assertRaises(ValueError) as e:
4298 self._DoReadFileDtb(
Bin Meng16cf5662021-05-10 20:23:32 +08004299 '170_fit_fdt.dts',
Simon Glass1032acc2020-09-06 10:39:08 -06004300 entry_args=entry_args,
4301 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
4302 self.assertIn("default-dt entry argument 'test-fdt3' not found in fdt list: test-fdt1, test-fdt2",
4303 str(e.exception))
4304
Simon Glassa820af72020-09-06 10:39:09 -06004305 def testFitExtblobMissingHelp(self):
4306 """Test display of help messages when an external blob is missing"""
4307 control.missing_blob_help = control._ReadMissingBlobHelp()
4308 control.missing_blob_help['wibble'] = 'Wibble test'
4309 control.missing_blob_help['another'] = 'Another test'
4310 with test_util.capture_sys_output() as (stdout, stderr):
4311 self._DoTestFile('168_fit_missing_blob.dts',
4312 allow_missing=True)
4313 err = stderr.getvalue()
4314
4315 # We can get the tag from the name, the type or the missing-msg
4316 # property. Check all three.
4317 self.assertIn('You may need to build ARM Trusted', err)
4318 self.assertIn('Wibble test', err)
4319 self.assertIn('Another test', err)
4320
Simon Glass6f1f4d42020-09-06 10:35:32 -06004321 def testMissingBlob(self):
4322 """Test handling of a blob containing a missing file"""
4323 with self.assertRaises(ValueError) as e:
4324 self._DoTestFile('173_missing_blob.dts', allow_missing=True)
4325 self.assertIn("Filename 'missing' not found in input path",
4326 str(e.exception))
4327
Simon Glassa0729502020-09-06 10:35:33 -06004328 def testEnvironment(self):
4329 """Test adding a U-Boot environment"""
4330 data = self._DoReadFile('174_env.dts')
4331 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
4332 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
4333 env = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
4334 self.assertEqual(b'\x1b\x97\x22\x7c\x01var1=1\0var2="2"\0\0\xff\xff',
4335 env)
4336
4337 def testEnvironmentNoSize(self):
4338 """Test that a missing 'size' property is detected"""
4339 with self.assertRaises(ValueError) as e:
Simon Glass8cdc08a2020-10-26 17:40:00 -06004340 self._DoTestFile('175_env_no_size.dts')
Simon Glassa0729502020-09-06 10:35:33 -06004341 self.assertIn("'u-boot-env' entry must have a size property",
4342 str(e.exception))
4343
4344 def testEnvironmentTooSmall(self):
4345 """Test handling of an environment that does not fit"""
4346 with self.assertRaises(ValueError) as e:
Simon Glass8cdc08a2020-10-26 17:40:00 -06004347 self._DoTestFile('176_env_too_small.dts')
Simon Glassa0729502020-09-06 10:35:33 -06004348
4349 # checksum, start byte, environment with \0 terminator, final \0
4350 need = 4 + 1 + len(ENV_DATA) + 1 + 1
4351 short = need - 0x8
4352 self.assertIn("too small to hold data (need %#x more bytes)" % short,
4353 str(e.exception))
4354
Simon Glassd1fdf752020-10-26 17:40:01 -06004355 def testSkipAtStart(self):
4356 """Test handling of skip-at-start section"""
4357 data = self._DoReadFile('177_skip_at_start.dts')
4358 self.assertEqual(U_BOOT_DATA, data)
4359
4360 image = control.images['image']
4361 entries = image.GetEntries()
4362 section = entries['section']
4363 self.assertEqual(0, section.offset)
4364 self.assertEqual(len(U_BOOT_DATA), section.size)
4365 self.assertEqual(U_BOOT_DATA, section.GetData())
4366
4367 entry = section.GetEntries()['u-boot']
4368 self.assertEqual(16, entry.offset)
4369 self.assertEqual(len(U_BOOT_DATA), entry.size)
4370 self.assertEqual(U_BOOT_DATA, entry.data)
4371
4372 def testSkipAtStartPad(self):
4373 """Test handling of skip-at-start section with padded entry"""
4374 data = self._DoReadFile('178_skip_at_start_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07004375 before = tools.get_bytes(0, 8)
4376 after = tools.get_bytes(0, 4)
Simon Glassd1fdf752020-10-26 17:40:01 -06004377 all = before + U_BOOT_DATA + after
4378 self.assertEqual(all, data)
4379
4380 image = control.images['image']
4381 entries = image.GetEntries()
4382 section = entries['section']
4383 self.assertEqual(0, section.offset)
4384 self.assertEqual(len(all), section.size)
4385 self.assertEqual(all, section.GetData())
4386
4387 entry = section.GetEntries()['u-boot']
4388 self.assertEqual(16, entry.offset)
4389 self.assertEqual(len(all), entry.size)
4390 self.assertEqual(U_BOOT_DATA, entry.data)
4391
4392 def testSkipAtStartSectionPad(self):
4393 """Test handling of skip-at-start section with padding"""
4394 data = self._DoReadFile('179_skip_at_start_section_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07004395 before = tools.get_bytes(0, 8)
4396 after = tools.get_bytes(0, 4)
Simon Glassd1fdf752020-10-26 17:40:01 -06004397 all = before + U_BOOT_DATA + after
Simon Glass510ef0f2020-10-26 17:40:13 -06004398 self.assertEqual(all, data)
Simon Glassd1fdf752020-10-26 17:40:01 -06004399
4400 image = control.images['image']
4401 entries = image.GetEntries()
4402 section = entries['section']
4403 self.assertEqual(0, section.offset)
4404 self.assertEqual(len(all), section.size)
Simon Glass72eeff12020-10-26 17:40:16 -06004405 self.assertEqual(U_BOOT_DATA, section.data)
Simon Glass510ef0f2020-10-26 17:40:13 -06004406 self.assertEqual(all, section.GetPaddedData())
Simon Glassd1fdf752020-10-26 17:40:01 -06004407
4408 entry = section.GetEntries()['u-boot']
4409 self.assertEqual(16, entry.offset)
4410 self.assertEqual(len(U_BOOT_DATA), entry.size)
4411 self.assertEqual(U_BOOT_DATA, entry.data)
Simon Glassa0729502020-09-06 10:35:33 -06004412
Simon Glassbb395742020-10-26 17:40:14 -06004413 def testSectionPad(self):
4414 """Testing padding with sections"""
4415 data = self._DoReadFile('180_section_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07004416 expected = (tools.get_bytes(ord('&'), 3) +
4417 tools.get_bytes(ord('!'), 5) +
Simon Glassbb395742020-10-26 17:40:14 -06004418 U_BOOT_DATA +
Simon Glass80025522022-01-29 14:14:04 -07004419 tools.get_bytes(ord('!'), 1) +
4420 tools.get_bytes(ord('&'), 2))
Simon Glassbb395742020-10-26 17:40:14 -06004421 self.assertEqual(expected, data)
4422
4423 def testSectionAlign(self):
4424 """Testing alignment with sections"""
4425 data = self._DoReadFileDtb('181_section_align.dts', map=True)[0]
4426 expected = (b'\0' + # fill section
Simon Glass80025522022-01-29 14:14:04 -07004427 tools.get_bytes(ord('&'), 1) + # padding to section align
Simon Glassbb395742020-10-26 17:40:14 -06004428 b'\0' + # fill section
Simon Glass80025522022-01-29 14:14:04 -07004429 tools.get_bytes(ord('!'), 3) + # padding to u-boot align
Simon Glassbb395742020-10-26 17:40:14 -06004430 U_BOOT_DATA +
Simon Glass80025522022-01-29 14:14:04 -07004431 tools.get_bytes(ord('!'), 4) + # padding to u-boot size
4432 tools.get_bytes(ord('!'), 4)) # padding to section size
Simon Glassbb395742020-10-26 17:40:14 -06004433 self.assertEqual(expected, data)
4434
Simon Glassd92c8362020-10-26 17:40:25 -06004435 def testCompressImage(self):
4436 """Test compression of the entire image"""
4437 self._CheckLz4()
4438 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4439 '182_compress_image.dts', use_real_dtb=True, update_dtb=True)
4440 dtb = fdt.Fdt(out_dtb_fname)
4441 dtb.Scan()
4442 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4443 'uncomp-size'])
4444 orig = self._decompress(data)
Brandon Maiera657bc62024-06-04 16:16:05 +00004445 self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, orig)
Simon Glassd92c8362020-10-26 17:40:25 -06004446
4447 # Do a sanity check on various fields
4448 image = control.images['image']
4449 entries = image.GetEntries()
4450 self.assertEqual(2, len(entries))
4451
4452 entry = entries['blob']
4453 self.assertEqual(COMPRESS_DATA, entry.data)
4454 self.assertEqual(len(COMPRESS_DATA), entry.size)
4455
4456 entry = entries['u-boot']
4457 self.assertEqual(U_BOOT_DATA, entry.data)
4458 self.assertEqual(len(U_BOOT_DATA), entry.size)
4459
4460 self.assertEqual(len(data), image.size)
4461 self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, image.uncomp_data)
4462 self.assertEqual(len(COMPRESS_DATA + U_BOOT_DATA), image.uncomp_size)
4463 orig = self._decompress(image.data)
4464 self.assertEqual(orig, image.uncomp_data)
4465
4466 expected = {
4467 'blob:offset': 0,
4468 'blob:size': len(COMPRESS_DATA),
4469 'u-boot:offset': len(COMPRESS_DATA),
4470 'u-boot:size': len(U_BOOT_DATA),
4471 'uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4472 'offset': 0,
4473 'image-pos': 0,
4474 'size': len(data),
4475 }
4476 self.assertEqual(expected, props)
4477
4478 def testCompressImageLess(self):
4479 """Test compression where compression reduces the image size"""
4480 self._CheckLz4()
4481 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4482 '183_compress_image_less.dts', use_real_dtb=True, update_dtb=True)
4483 dtb = fdt.Fdt(out_dtb_fname)
4484 dtb.Scan()
4485 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4486 'uncomp-size'])
4487 orig = self._decompress(data)
4488
Brandon Maiera657bc62024-06-04 16:16:05 +00004489 self.assertEqual(COMPRESS_DATA + COMPRESS_DATA + U_BOOT_DATA, orig)
Simon Glassd92c8362020-10-26 17:40:25 -06004490
4491 # Do a sanity check on various fields
4492 image = control.images['image']
4493 entries = image.GetEntries()
4494 self.assertEqual(2, len(entries))
4495
4496 entry = entries['blob']
4497 self.assertEqual(COMPRESS_DATA_BIG, entry.data)
4498 self.assertEqual(len(COMPRESS_DATA_BIG), entry.size)
4499
4500 entry = entries['u-boot']
4501 self.assertEqual(U_BOOT_DATA, entry.data)
4502 self.assertEqual(len(U_BOOT_DATA), entry.size)
4503
4504 self.assertEqual(len(data), image.size)
4505 self.assertEqual(COMPRESS_DATA_BIG + U_BOOT_DATA, image.uncomp_data)
4506 self.assertEqual(len(COMPRESS_DATA_BIG + U_BOOT_DATA),
4507 image.uncomp_size)
4508 orig = self._decompress(image.data)
4509 self.assertEqual(orig, image.uncomp_data)
4510
4511 expected = {
4512 'blob:offset': 0,
4513 'blob:size': len(COMPRESS_DATA_BIG),
4514 'u-boot:offset': len(COMPRESS_DATA_BIG),
4515 'u-boot:size': len(U_BOOT_DATA),
4516 'uncomp-size': len(COMPRESS_DATA_BIG + U_BOOT_DATA),
4517 'offset': 0,
4518 'image-pos': 0,
4519 'size': len(data),
4520 }
4521 self.assertEqual(expected, props)
4522
4523 def testCompressSectionSize(self):
4524 """Test compression of a section with a fixed size"""
4525 self._CheckLz4()
4526 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4527 '184_compress_section_size.dts', use_real_dtb=True, update_dtb=True)
4528 dtb = fdt.Fdt(out_dtb_fname)
4529 dtb.Scan()
4530 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4531 'uncomp-size'])
4532 orig = self._decompress(data)
Brandon Maiera657bc62024-06-04 16:16:05 +00004533 self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, orig)
Simon Glassd92c8362020-10-26 17:40:25 -06004534 expected = {
4535 'section/blob:offset': 0,
4536 'section/blob:size': len(COMPRESS_DATA),
4537 'section/u-boot:offset': len(COMPRESS_DATA),
4538 'section/u-boot:size': len(U_BOOT_DATA),
4539 'section:offset': 0,
4540 'section:image-pos': 0,
4541 'section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4542 'section:size': 0x30,
4543 'offset': 0,
4544 'image-pos': 0,
4545 'size': 0x30,
4546 }
4547 self.assertEqual(expected, props)
4548
4549 def testCompressSection(self):
4550 """Test compression of a section with no fixed size"""
4551 self._CheckLz4()
4552 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4553 '185_compress_section.dts', use_real_dtb=True, update_dtb=True)
4554 dtb = fdt.Fdt(out_dtb_fname)
4555 dtb.Scan()
4556 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4557 'uncomp-size'])
4558 orig = self._decompress(data)
Brandon Maiera657bc62024-06-04 16:16:05 +00004559 self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, orig)
Simon Glassd92c8362020-10-26 17:40:25 -06004560 expected = {
4561 'section/blob:offset': 0,
4562 'section/blob:size': len(COMPRESS_DATA),
4563 'section/u-boot:offset': len(COMPRESS_DATA),
4564 'section/u-boot:size': len(U_BOOT_DATA),
4565 'section:offset': 0,
4566 'section:image-pos': 0,
4567 'section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4568 'section:size': len(data),
4569 'offset': 0,
4570 'image-pos': 0,
4571 'size': len(data),
4572 }
4573 self.assertEqual(expected, props)
4574
Stefan Herbrechtsmeier94813a02022-08-19 16:25:31 +02004575 def testLz4Missing(self):
4576 """Test that binman still produces an image if lz4 is missing"""
4577 with test_util.capture_sys_output() as (_, stderr):
4578 self._DoTestFile('185_compress_section.dts',
4579 force_missing_bintools='lz4')
4580 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07004581 self.assertRegex(err, "Image 'image'.*missing bintools.*: lz4")
Stefan Herbrechtsmeier94813a02022-08-19 16:25:31 +02004582
Simon Glassd92c8362020-10-26 17:40:25 -06004583 def testCompressExtra(self):
4584 """Test compression of a section with no fixed size"""
4585 self._CheckLz4()
4586 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4587 '186_compress_extra.dts', use_real_dtb=True, update_dtb=True)
4588 dtb = fdt.Fdt(out_dtb_fname)
4589 dtb.Scan()
4590 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4591 'uncomp-size'])
4592
4593 base = data[len(U_BOOT_DATA):]
Brandon Maiera657bc62024-06-04 16:16:05 +00004594 self.assertEqual(U_BOOT_DATA, base[:len(U_BOOT_DATA)])
Simon Glassd92c8362020-10-26 17:40:25 -06004595 rest = base[len(U_BOOT_DATA):]
4596
4597 # Check compressed data
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +02004598 bintool = self.comp_bintools['lz4']
4599 expect1 = bintool.compress(COMPRESS_DATA + U_BOOT_DATA)
Stefan Herbrechtsmeier86f1fc02022-08-19 16:25:23 +02004600 data1 = rest[:len(expect1)]
4601 section1 = self._decompress(data1)
Brandon Maiera657bc62024-06-04 16:16:05 +00004602 self.assertEqual(expect1, data1)
4603 self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, section1)
Simon Glassd92c8362020-10-26 17:40:25 -06004604 rest1 = rest[len(expect1):]
4605
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +02004606 expect2 = bintool.compress(COMPRESS_DATA + COMPRESS_DATA)
Stefan Herbrechtsmeier86f1fc02022-08-19 16:25:23 +02004607 data2 = rest1[:len(expect2)]
4608 section2 = self._decompress(data2)
Brandon Maiera657bc62024-06-04 16:16:05 +00004609 self.assertEqual(expect2, data2)
4610 self.assertEqual(COMPRESS_DATA + COMPRESS_DATA, section2)
Simon Glassd92c8362020-10-26 17:40:25 -06004611 rest2 = rest1[len(expect2):]
4612
4613 expect_size = (len(U_BOOT_DATA) + len(U_BOOT_DATA) + len(expect1) +
4614 len(expect2) + len(U_BOOT_DATA))
Brandon Maiera657bc62024-06-04 16:16:05 +00004615 #self.assertEqual(expect_size, len(data))
Simon Glassd92c8362020-10-26 17:40:25 -06004616
Brandon Maiera657bc62024-06-04 16:16:05 +00004617 #self.assertEqual(U_BOOT_DATA, rest2)
Simon Glassd92c8362020-10-26 17:40:25 -06004618
4619 self.maxDiff = None
4620 expected = {
4621 'u-boot:offset': 0,
4622 'u-boot:image-pos': 0,
4623 'u-boot:size': len(U_BOOT_DATA),
4624
4625 'base:offset': len(U_BOOT_DATA),
4626 'base:image-pos': len(U_BOOT_DATA),
4627 'base:size': len(data) - len(U_BOOT_DATA),
4628 'base/u-boot:offset': 0,
4629 'base/u-boot:image-pos': len(U_BOOT_DATA),
4630 'base/u-boot:size': len(U_BOOT_DATA),
4631 'base/u-boot2:offset': len(U_BOOT_DATA) + len(expect1) +
4632 len(expect2),
4633 'base/u-boot2:image-pos': len(U_BOOT_DATA) * 2 + len(expect1) +
4634 len(expect2),
4635 'base/u-boot2:size': len(U_BOOT_DATA),
4636
4637 'base/section:offset': len(U_BOOT_DATA),
4638 'base/section:image-pos': len(U_BOOT_DATA) * 2,
4639 'base/section:size': len(expect1),
4640 'base/section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4641 'base/section/blob:offset': 0,
4642 'base/section/blob:size': len(COMPRESS_DATA),
4643 'base/section/u-boot:offset': len(COMPRESS_DATA),
4644 'base/section/u-boot:size': len(U_BOOT_DATA),
4645
4646 'base/section2:offset': len(U_BOOT_DATA) + len(expect1),
4647 'base/section2:image-pos': len(U_BOOT_DATA) * 2 + len(expect1),
4648 'base/section2:size': len(expect2),
4649 'base/section2:uncomp-size': len(COMPRESS_DATA + COMPRESS_DATA),
4650 'base/section2/blob:offset': 0,
4651 'base/section2/blob:size': len(COMPRESS_DATA),
4652 'base/section2/blob2:offset': len(COMPRESS_DATA),
4653 'base/section2/blob2:size': len(COMPRESS_DATA),
4654
4655 'offset': 0,
4656 'image-pos': 0,
4657 'size': len(data),
4658 }
4659 self.assertEqual(expected, props)
4660
Simon Glassecbe4732021-01-06 21:35:15 -07004661 def testSymbolsSubsection(self):
4662 """Test binman can assign symbols from a subsection"""
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03004663 self.checkSymbols('187_symbols_sub.dts', U_BOOT_SPL_DATA, 0x1c)
Simon Glassecbe4732021-01-06 21:35:15 -07004664
Simon Glass3fb25402021-01-06 21:35:16 -07004665 def testReadImageEntryArg(self):
4666 """Test reading an image that would need an entry arg to generate"""
4667 entry_args = {
4668 'cros-ec-rw-path': 'ecrw.bin',
4669 }
4670 data = self.data = self._DoReadFileDtb(
4671 '188_image_entryarg.dts',use_real_dtb=True, update_dtb=True,
4672 entry_args=entry_args)
4673
Simon Glass80025522022-01-29 14:14:04 -07004674 image_fname = tools.get_output_filename('image.bin')
Simon Glass3fb25402021-01-06 21:35:16 -07004675 orig_image = control.images['image']
4676
4677 # This should not generate an error about the missing 'cros-ec-rw-path'
4678 # since we are reading the image from a file. Compare with
4679 # testEntryArgsRequired()
4680 image = Image.FromFile(image_fname)
4681 self.assertEqual(orig_image.GetEntries().keys(),
4682 image.GetEntries().keys())
4683
Simon Glassa2af7302021-01-06 21:35:18 -07004684 def testFilesAlign(self):
4685 """Test alignment with files"""
4686 data = self._DoReadFile('190_files_align.dts')
4687
4688 # The first string is 15 bytes so will align to 16
4689 expect = FILES_DATA[:15] + b'\0' + FILES_DATA[15:]
4690 self.assertEqual(expect, data)
4691
Simon Glassdb84b562021-01-06 21:35:19 -07004692 def testReadImageSkip(self):
4693 """Test reading an image and accessing its FDT map"""
4694 data = self.data = self._DoReadFileRealDtb('191_read_image_skip.dts')
Simon Glass80025522022-01-29 14:14:04 -07004695 image_fname = tools.get_output_filename('image.bin')
Simon Glassdb84b562021-01-06 21:35:19 -07004696 orig_image = control.images['image']
4697 image = Image.FromFile(image_fname)
4698 self.assertEqual(orig_image.GetEntries().keys(),
4699 image.GetEntries().keys())
4700
4701 orig_entry = orig_image.GetEntries()['fdtmap']
4702 entry = image.GetEntries()['fdtmap']
4703 self.assertEqual(orig_entry.offset, entry.offset)
4704 self.assertEqual(orig_entry.size, entry.size)
4705 self.assertEqual(16, entry.image_pos)
4706
4707 u_boot = image.GetEntries()['section'].GetEntries()['u-boot']
4708
Brandon Maiera657bc62024-06-04 16:16:05 +00004709 self.assertEqual(U_BOOT_DATA, u_boot.ReadData())
Simon Glassdb84b562021-01-06 21:35:19 -07004710
Simon Glassc98de972021-03-18 20:24:57 +13004711 def testTplNoDtb(self):
4712 """Test that an image with tpl/u-boot-tpl-nodtb.bin can be created"""
Simon Glass13089cc2021-04-25 08:39:32 +12004713 self._SetupTplElf()
Simon Glassc98de972021-03-18 20:24:57 +13004714 data = self._DoReadFile('192_u_boot_tpl_nodtb.dts')
4715 self.assertEqual(U_BOOT_TPL_NODTB_DATA,
4716 data[:len(U_BOOT_TPL_NODTB_DATA)])
4717
Simon Glass63f41d42021-03-18 20:24:58 +13004718 def testTplBssPad(self):
4719 """Test that we can pad TPL's BSS with zeros"""
4720 # ELF file with a '__bss_size' symbol
4721 self._SetupTplElf()
4722 data = self._DoReadFile('193_tpl_bss_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07004723 self.assertEqual(U_BOOT_TPL_DATA + tools.get_bytes(0, 10) + U_BOOT_DATA,
Simon Glass63f41d42021-03-18 20:24:58 +13004724 data)
4725
4726 def testTplBssPadMissing(self):
4727 """Test that a missing symbol is detected"""
4728 self._SetupTplElf('u_boot_ucode_ptr')
4729 with self.assertRaises(ValueError) as e:
4730 self._DoReadFile('193_tpl_bss_pad.dts')
4731 self.assertIn('Expected __bss_size symbol in tpl/u-boot-tpl',
4732 str(e.exception))
4733
Simon Glass718b5292021-03-18 20:25:07 +13004734 def checkDtbSizes(self, data, pad_len, start):
4735 """Check the size arguments in a dtb embedded in an image
4736
4737 Args:
4738 data: The image data
4739 pad_len: Length of the pad section in the image, in bytes
4740 start: Start offset of the devicetree to examine, within the image
4741
4742 Returns:
4743 Size of the devicetree in bytes
4744 """
4745 dtb_data = data[start:]
4746 dtb = fdt.Fdt.FromData(dtb_data)
4747 fdt_size = dtb.GetFdtObj().totalsize()
4748 dtb.Scan()
4749 props = self._GetPropTree(dtb, 'size')
4750 self.assertEqual({
4751 'size': len(data),
4752 'u-boot-spl/u-boot-spl-bss-pad:size': pad_len,
4753 'u-boot-spl/u-boot-spl-dtb:size': 801,
4754 'u-boot-spl/u-boot-spl-nodtb:size': len(U_BOOT_SPL_NODTB_DATA),
4755 'u-boot-spl:size': 860,
4756 'u-boot-tpl:size': len(U_BOOT_TPL_DATA),
4757 'u-boot/u-boot-dtb:size': 781,
4758 'u-boot/u-boot-nodtb:size': len(U_BOOT_NODTB_DATA),
4759 'u-boot:size': 827,
4760 }, props)
4761 return fdt_size
4762
4763 def testExpanded(self):
4764 """Test that an expanded entry type is selected when needed"""
4765 self._SetupSplElf()
4766 self._SetupTplElf()
4767
4768 # SPL has a devicetree, TPL does not
4769 entry_args = {
4770 'spl-dtb': '1',
4771 'spl-bss-pad': 'y',
4772 'tpl-dtb': '',
4773 }
4774 self._DoReadFileDtb('194_fdt_incl.dts', use_expanded=True,
4775 entry_args=entry_args)
4776 image = control.images['image']
4777 entries = image.GetEntries()
4778 self.assertEqual(3, len(entries))
4779
4780 # First, u-boot, which should be expanded into u-boot-nodtb and dtb
4781 self.assertIn('u-boot', entries)
4782 entry = entries['u-boot']
4783 self.assertEqual('u-boot-expanded', entry.etype)
4784 subent = entry.GetEntries()
4785 self.assertEqual(2, len(subent))
4786 self.assertIn('u-boot-nodtb', subent)
4787 self.assertIn('u-boot-dtb', subent)
4788
4789 # Second, u-boot-spl, which should be expanded into three parts
4790 self.assertIn('u-boot-spl', entries)
4791 entry = entries['u-boot-spl']
4792 self.assertEqual('u-boot-spl-expanded', entry.etype)
4793 subent = entry.GetEntries()
4794 self.assertEqual(3, len(subent))
4795 self.assertIn('u-boot-spl-nodtb', subent)
4796 self.assertIn('u-boot-spl-bss-pad', subent)
4797 self.assertIn('u-boot-spl-dtb', subent)
4798
4799 # Third, u-boot-tpl, which should be not be expanded, since TPL has no
4800 # devicetree
4801 self.assertIn('u-boot-tpl', entries)
4802 entry = entries['u-boot-tpl']
4803 self.assertEqual('u-boot-tpl', entry.etype)
4804 self.assertEqual(None, entry.GetEntries())
4805
4806 def testExpandedTpl(self):
4807 """Test that an expanded entry type is selected for TPL when needed"""
4808 self._SetupTplElf()
4809
4810 entry_args = {
4811 'tpl-bss-pad': 'y',
4812 'tpl-dtb': 'y',
4813 }
4814 self._DoReadFileDtb('195_fdt_incl_tpl.dts', use_expanded=True,
4815 entry_args=entry_args)
4816 image = control.images['image']
4817 entries = image.GetEntries()
4818 self.assertEqual(1, len(entries))
4819
4820 # We only have u-boot-tpl, which be expanded
4821 self.assertIn('u-boot-tpl', entries)
4822 entry = entries['u-boot-tpl']
4823 self.assertEqual('u-boot-tpl-expanded', entry.etype)
4824 subent = entry.GetEntries()
4825 self.assertEqual(3, len(subent))
4826 self.assertIn('u-boot-tpl-nodtb', subent)
4827 self.assertIn('u-boot-tpl-bss-pad', subent)
4828 self.assertIn('u-boot-tpl-dtb', subent)
4829
4830 def testExpandedNoPad(self):
4831 """Test an expanded entry without BSS pad enabled"""
4832 self._SetupSplElf()
4833 self._SetupTplElf()
4834
4835 # SPL has a devicetree, TPL does not
4836 entry_args = {
4837 'spl-dtb': 'something',
4838 'spl-bss-pad': 'n',
4839 'tpl-dtb': '',
4840 }
4841 self._DoReadFileDtb('194_fdt_incl.dts', use_expanded=True,
4842 entry_args=entry_args)
4843 image = control.images['image']
4844 entries = image.GetEntries()
4845
4846 # Just check u-boot-spl, which should be expanded into two parts
4847 self.assertIn('u-boot-spl', entries)
4848 entry = entries['u-boot-spl']
4849 self.assertEqual('u-boot-spl-expanded', entry.etype)
4850 subent = entry.GetEntries()
4851 self.assertEqual(2, len(subent))
4852 self.assertIn('u-boot-spl-nodtb', subent)
4853 self.assertIn('u-boot-spl-dtb', subent)
4854
4855 def testExpandedTplNoPad(self):
4856 """Test that an expanded entry type with padding disabled in TPL"""
4857 self._SetupTplElf()
4858
4859 entry_args = {
4860 'tpl-bss-pad': '',
4861 'tpl-dtb': 'y',
4862 }
4863 self._DoReadFileDtb('195_fdt_incl_tpl.dts', use_expanded=True,
4864 entry_args=entry_args)
4865 image = control.images['image']
4866 entries = image.GetEntries()
4867 self.assertEqual(1, len(entries))
4868
4869 # We only have u-boot-tpl, which be expanded
4870 self.assertIn('u-boot-tpl', entries)
4871 entry = entries['u-boot-tpl']
4872 self.assertEqual('u-boot-tpl-expanded', entry.etype)
4873 subent = entry.GetEntries()
4874 self.assertEqual(2, len(subent))
4875 self.assertIn('u-boot-tpl-nodtb', subent)
4876 self.assertIn('u-boot-tpl-dtb', subent)
4877
4878 def testFdtInclude(self):
4879 """Test that an Fdt is update within all binaries"""
4880 self._SetupSplElf()
4881 self._SetupTplElf()
4882
4883 # SPL has a devicetree, TPL does not
4884 self.maxDiff = None
4885 entry_args = {
4886 'spl-dtb': '1',
4887 'spl-bss-pad': 'y',
4888 'tpl-dtb': '',
4889 }
4890 # Build the image. It includes two separate devicetree binaries, each
4891 # with their own contents, but all contain the binman definition.
4892 data = self._DoReadFileDtb(
4893 '194_fdt_incl.dts', use_real_dtb=True, use_expanded=True,
4894 update_dtb=True, entry_args=entry_args)[0]
4895 pad_len = 10
4896
4897 # Check the U-Boot dtb
4898 start = len(U_BOOT_NODTB_DATA)
4899 fdt_size = self.checkDtbSizes(data, pad_len, start)
4900
4901 # Now check SPL
4902 start += fdt_size + len(U_BOOT_SPL_NODTB_DATA) + pad_len
4903 fdt_size = self.checkDtbSizes(data, pad_len, start)
4904
4905 # TPL has no devicetree
4906 start += fdt_size + len(U_BOOT_TPL_DATA)
4907 self.assertEqual(len(data), start)
Simon Glassbb395742020-10-26 17:40:14 -06004908
Simon Glass7098b7f2021-03-21 18:24:30 +13004909 def testSymbolsExpanded(self):
4910 """Test binman can assign symbols in expanded entries"""
4911 entry_args = {
4912 'spl-dtb': '1',
4913 }
4914 self.checkSymbols('197_symbols_expand.dts', U_BOOT_SPL_NODTB_DATA +
4915 U_BOOT_SPL_DTB_DATA, 0x38,
4916 entry_args=entry_args, use_expanded=True)
4917
Simon Glasse1915782021-03-21 18:24:31 +13004918 def testCollection(self):
4919 """Test a collection"""
4920 data = self._DoReadFile('198_collection.dts')
4921 self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA +
Simon Glass80025522022-01-29 14:14:04 -07004922 tools.get_bytes(0xff, 2) + U_BOOT_NODTB_DATA +
4923 tools.get_bytes(0xfe, 3) + U_BOOT_DTB_DATA,
Simon Glasse1915782021-03-21 18:24:31 +13004924 data)
4925
Simon Glass27a7f772021-03-21 18:24:32 +13004926 def testCollectionSection(self):
4927 """Test a collection where a section must be built first"""
4928 # Sections never have their contents when GetData() is called, but when
Simon Glass7e3f89f2021-11-23 11:03:47 -07004929 # BuildSectionData() is called with required=True, a section will force
Simon Glass27a7f772021-03-21 18:24:32 +13004930 # building the contents, producing an error is anything is still
4931 # missing.
4932 data = self._DoReadFile('199_collection_section.dts')
4933 section = U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA
Simon Glass80025522022-01-29 14:14:04 -07004934 self.assertEqual(section + U_BOOT_DATA + tools.get_bytes(0xff, 2) +
4935 section + tools.get_bytes(0xfe, 3) + U_BOOT_DATA,
Simon Glass27a7f772021-03-21 18:24:32 +13004936 data)
4937
Simon Glassf427c5f2021-03-21 18:24:33 +13004938 def testAlignDefault(self):
4939 """Test that default alignment works on sections"""
4940 data = self._DoReadFile('200_align_default.dts')
Simon Glass80025522022-01-29 14:14:04 -07004941 expected = (U_BOOT_DATA + tools.get_bytes(0, 8 - len(U_BOOT_DATA)) +
Simon Glassf427c5f2021-03-21 18:24:33 +13004942 U_BOOT_DATA)
4943 # Special alignment for section
Simon Glass80025522022-01-29 14:14:04 -07004944 expected += tools.get_bytes(0, 32 - len(expected))
Simon Glassf427c5f2021-03-21 18:24:33 +13004945 # No alignment within the nested section
4946 expected += U_BOOT_DATA + U_BOOT_NODTB_DATA;
4947 # Now the final piece, which should be default-aligned
Simon Glass80025522022-01-29 14:14:04 -07004948 expected += tools.get_bytes(0, 88 - len(expected)) + U_BOOT_NODTB_DATA
Simon Glassf427c5f2021-03-21 18:24:33 +13004949 self.assertEqual(expected, data)
Simon Glass27a7f772021-03-21 18:24:32 +13004950
Bin Mengc0b15742021-05-10 20:23:33 +08004951 def testPackOpenSBI(self):
4952 """Test that an image with an OpenSBI binary can be created"""
4953 data = self._DoReadFile('201_opensbi.dts')
4954 self.assertEqual(OPENSBI_DATA, data[:len(OPENSBI_DATA)])
4955
Simon Glass76f496d2021-07-06 10:36:37 -06004956 def testSectionsSingleThread(self):
4957 """Test sections without multithreading"""
4958 data = self._DoReadFileDtb('055_sections.dts', threads=0)[0]
Simon Glass80025522022-01-29 14:14:04 -07004959 expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) +
4960 U_BOOT_DATA + tools.get_bytes(ord('a'), 12) +
4961 U_BOOT_DATA + tools.get_bytes(ord('&'), 4))
Simon Glass76f496d2021-07-06 10:36:37 -06004962 self.assertEqual(expected, data)
4963
4964 def testThreadTimeout(self):
4965 """Test handling a thread that takes too long"""
4966 with self.assertRaises(ValueError) as e:
4967 self._DoTestFile('202_section_timeout.dts',
4968 test_section_timeout=True)
Simon Glass2d59d152021-10-18 12:13:15 -06004969 self.assertIn("Timed out obtaining contents", str(e.exception))
Simon Glass76f496d2021-07-06 10:36:37 -06004970
Simon Glass748a1d42021-07-06 10:36:41 -06004971 def testTiming(self):
4972 """Test output of timing information"""
4973 data = self._DoReadFile('055_sections.dts')
4974 with test_util.capture_sys_output() as (stdout, stderr):
4975 state.TimingShow()
4976 self.assertIn('read:', stdout.getvalue())
4977 self.assertIn('compress:', stdout.getvalue())
4978
Simon Glassadfb8492021-11-03 21:09:18 -06004979 def testUpdateFdtInElf(self):
4980 """Test that we can update the devicetree in an ELF file"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02004981 if not elf.ELF_TOOLS:
4982 self.skipTest('Python elftools not available')
Simon Glassadfb8492021-11-03 21:09:18 -06004983 infile = elf_fname = self.ElfTestFile('u_boot_binman_embed')
4984 outfile = os.path.join(self._indir, 'u-boot.out')
4985 begin_sym = 'dtb_embed_begin'
4986 end_sym = 'dtb_embed_end'
4987 retcode = self._DoTestFile(
4988 '060_fdt_update.dts', update_dtb=True,
4989 update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym]))
4990 self.assertEqual(0, retcode)
4991
4992 # Check that the output file does in fact contact a dtb with the binman
4993 # definition in the correct place
4994 syms = elf.GetSymbolFileOffset(infile,
4995 ['dtb_embed_begin', 'dtb_embed_end'])
Simon Glass80025522022-01-29 14:14:04 -07004996 data = tools.read_file(outfile)
Simon Glassadfb8492021-11-03 21:09:18 -06004997 dtb_data = data[syms['dtb_embed_begin'].offset:
4998 syms['dtb_embed_end'].offset]
4999
5000 dtb = fdt.Fdt.FromData(dtb_data)
5001 dtb.Scan()
5002 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
5003 self.assertEqual({
5004 'image-pos': 0,
5005 'offset': 0,
5006 '_testing:offset': 32,
5007 '_testing:size': 2,
5008 '_testing:image-pos': 32,
5009 'section@0/u-boot:offset': 0,
5010 'section@0/u-boot:size': len(U_BOOT_DATA),
5011 'section@0/u-boot:image-pos': 0,
5012 'section@0:offset': 0,
5013 'section@0:size': 16,
5014 'section@0:image-pos': 0,
5015
5016 'section@1/u-boot:offset': 0,
5017 'section@1/u-boot:size': len(U_BOOT_DATA),
5018 'section@1/u-boot:image-pos': 16,
5019 'section@1:offset': 16,
5020 'section@1:size': 16,
5021 'section@1:image-pos': 16,
5022 'size': 40
5023 }, props)
5024
5025 def testUpdateFdtInElfInvalid(self):
5026 """Test that invalid args are detected with --update-fdt-in-elf"""
5027 with self.assertRaises(ValueError) as e:
5028 self._DoTestFile('060_fdt_update.dts', update_fdt_in_elf='fred')
5029 self.assertIn("Invalid args ['fred'] to --update-fdt-in-elf",
5030 str(e.exception))
5031
5032 def testUpdateFdtInElfNoSyms(self):
5033 """Test that missing symbols are detected with --update-fdt-in-elf"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005034 if not elf.ELF_TOOLS:
5035 self.skipTest('Python elftools not available')
Simon Glassadfb8492021-11-03 21:09:18 -06005036 infile = elf_fname = self.ElfTestFile('u_boot_binman_embed')
5037 outfile = ''
5038 begin_sym = 'wrong_begin'
5039 end_sym = 'wrong_end'
5040 with self.assertRaises(ValueError) as e:
5041 self._DoTestFile(
5042 '060_fdt_update.dts',
5043 update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym]))
5044 self.assertIn("Expected two symbols 'wrong_begin' and 'wrong_end': got 0:",
5045 str(e.exception))
5046
5047 def testUpdateFdtInElfTooSmall(self):
5048 """Test that an over-large dtb is detected with --update-fdt-in-elf"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005049 if not elf.ELF_TOOLS:
5050 self.skipTest('Python elftools not available')
Simon Glassadfb8492021-11-03 21:09:18 -06005051 infile = elf_fname = self.ElfTestFile('u_boot_binman_embed_sm')
5052 outfile = os.path.join(self._indir, 'u-boot.out')
5053 begin_sym = 'dtb_embed_begin'
5054 end_sym = 'dtb_embed_end'
5055 with self.assertRaises(ValueError) as e:
5056 self._DoTestFile(
5057 '060_fdt_update.dts', update_dtb=True,
5058 update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym]))
5059 self.assertRegex(
5060 str(e.exception),
5061 "Not enough space in '.*u_boot_binman_embed_sm' for data length.*")
5062
Simon Glass88e04da2021-11-23 11:03:42 -07005063 def testVersion(self):
5064 """Test we can get the binman version"""
5065 version = '(unreleased)'
5066 self.assertEqual(version, state.GetVersion(self._indir))
5067
5068 with self.assertRaises(SystemExit):
5069 with test_util.capture_sys_output() as (_, stderr):
5070 self._DoBinman('-V')
5071 self.assertEqual('Binman %s\n' % version, stderr.getvalue())
5072
5073 # Try running the tool too, just to be safe
5074 result = self._RunBinman('-V')
5075 self.assertEqual('Binman %s\n' % version, result.stderr)
5076
5077 # Set up a version file to make sure that works
5078 version = 'v2025.01-rc2'
Simon Glass80025522022-01-29 14:14:04 -07005079 tools.write_file(os.path.join(self._indir, 'version'), version,
Simon Glass88e04da2021-11-23 11:03:42 -07005080 binary=False)
5081 self.assertEqual(version, state.GetVersion(self._indir))
5082
Simon Glass637958f2021-11-23 21:09:50 -07005083 def testAltFormat(self):
5084 """Test that alternative formats can be used to extract"""
5085 self._DoReadFileRealDtb('213_fdtmap_alt_format.dts')
5086
5087 try:
5088 tmpdir, updated_fname = self._SetupImageInTmpdir()
5089 with test_util.capture_sys_output() as (stdout, _):
5090 self._DoBinman('extract', '-i', updated_fname, '-F', 'list')
5091 self.assertEqual(
5092 '''Flag (-F) Entry type Description
5093fdt fdtmap Extract the devicetree blob from the fdtmap
5094''',
5095 stdout.getvalue())
5096
5097 dtb = os.path.join(tmpdir, 'fdt.dtb')
5098 self._DoBinman('extract', '-i', updated_fname, '-F', 'fdt', '-f',
5099 dtb, 'fdtmap')
5100
5101 # Check that we can read it and it can be scanning, meaning it does
5102 # not have a 16-byte fdtmap header
Simon Glass80025522022-01-29 14:14:04 -07005103 data = tools.read_file(dtb)
Simon Glass637958f2021-11-23 21:09:50 -07005104 dtb = fdt.Fdt.FromData(data)
5105 dtb.Scan()
5106
5107 # Now check u-boot which has no alt_format
5108 fname = os.path.join(tmpdir, 'fdt.dtb')
5109 self._DoBinman('extract', '-i', updated_fname, '-F', 'dummy',
5110 '-f', fname, 'u-boot')
Simon Glass80025522022-01-29 14:14:04 -07005111 data = tools.read_file(fname)
Simon Glass637958f2021-11-23 21:09:50 -07005112 self.assertEqual(U_BOOT_DATA, data)
5113
5114 finally:
5115 shutil.rmtree(tmpdir)
5116
Simon Glass0b00ae62021-11-23 21:09:52 -07005117 def testExtblobList(self):
5118 """Test an image with an external blob list"""
5119 data = self._DoReadFile('215_blob_ext_list.dts')
5120 self.assertEqual(REFCODE_DATA + FSP_M_DATA, data)
5121
5122 def testExtblobListMissing(self):
5123 """Test an image with a missing external blob"""
5124 with self.assertRaises(ValueError) as e:
5125 self._DoReadFile('216_blob_ext_list_missing.dts')
5126 self.assertIn("Filename 'missing-file' not found in input path",
5127 str(e.exception))
5128
5129 def testExtblobListMissingOk(self):
5130 """Test an image with an missing external blob that is allowed"""
5131 with test_util.capture_sys_output() as (stdout, stderr):
5132 self._DoTestFile('216_blob_ext_list_missing.dts',
5133 allow_missing=True)
5134 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07005135 self.assertRegex(err, "Image 'image'.*missing.*: blob-ext")
Simon Glass0b00ae62021-11-23 21:09:52 -07005136
Simon Glass3efb2972021-11-23 21:08:59 -07005137 def testFip(self):
5138 """Basic test of generation of an ARM Firmware Image Package (FIP)"""
5139 data = self._DoReadFile('203_fip.dts')
5140 hdr, fents = fip_util.decode_fip(data)
5141 self.assertEqual(fip_util.HEADER_MAGIC, hdr.name)
5142 self.assertEqual(fip_util.HEADER_SERIAL, hdr.serial)
5143 self.assertEqual(0x123, hdr.flags)
5144
5145 self.assertEqual(2, len(fents))
5146
5147 fent = fents[0]
5148 self.assertEqual(
5149 bytes([0x47, 0xd4, 0x08, 0x6d, 0x4c, 0xfe, 0x98, 0x46,
5150 0x9b, 0x95, 0x29, 0x50, 0xcb, 0xbd, 0x5a, 0x0]), fent.uuid)
5151 self.assertEqual('soc-fw', fent.fip_type)
5152 self.assertEqual(0x88, fent.offset)
5153 self.assertEqual(len(ATF_BL31_DATA), fent.size)
5154 self.assertEqual(0x123456789abcdef, fent.flags)
5155 self.assertEqual(ATF_BL31_DATA, fent.data)
5156 self.assertEqual(True, fent.valid)
5157
5158 fent = fents[1]
5159 self.assertEqual(
5160 bytes([0x65, 0x92, 0x27, 0x03, 0x2f, 0x74, 0xe6, 0x44,
5161 0x8d, 0xff, 0x57, 0x9a, 0xc1, 0xff, 0x06, 0x10]), fent.uuid)
5162 self.assertEqual('scp-fwu-cfg', fent.fip_type)
5163 self.assertEqual(0x8c, fent.offset)
5164 self.assertEqual(len(ATF_BL31_DATA), fent.size)
5165 self.assertEqual(0, fent.flags)
5166 self.assertEqual(ATF_BL2U_DATA, fent.data)
5167 self.assertEqual(True, fent.valid)
5168
5169 def testFipOther(self):
5170 """Basic FIP with something that isn't a external blob"""
5171 data = self._DoReadFile('204_fip_other.dts')
5172 hdr, fents = fip_util.decode_fip(data)
5173
5174 self.assertEqual(2, len(fents))
5175 fent = fents[1]
5176 self.assertEqual('rot-cert', fent.fip_type)
5177 self.assertEqual(b'aa', fent.data)
5178
Simon Glass3efb2972021-11-23 21:08:59 -07005179 def testFipNoType(self):
5180 """FIP with an entry of an unknown type"""
5181 with self.assertRaises(ValueError) as e:
5182 self._DoReadFile('205_fip_no_type.dts')
5183 self.assertIn("Must provide a fip-type (node name 'u-boot' is not a known FIP type)",
5184 str(e.exception))
5185
5186 def testFipUuid(self):
5187 """Basic FIP with a manual uuid"""
5188 data = self._DoReadFile('206_fip_uuid.dts')
5189 hdr, fents = fip_util.decode_fip(data)
5190
5191 self.assertEqual(2, len(fents))
5192 fent = fents[1]
5193 self.assertEqual(None, fent.fip_type)
5194 self.assertEqual(
5195 bytes([0xfc, 0x65, 0x13, 0x92, 0x4a, 0x5b, 0x11, 0xec,
5196 0x94, 0x35, 0xff, 0x2d, 0x1c, 0xfc, 0x79, 0x9c]),
5197 fent.uuid)
5198 self.assertEqual(U_BOOT_DATA, fent.data)
5199
5200 def testFipLs(self):
5201 """Test listing a FIP"""
5202 data = self._DoReadFileRealDtb('207_fip_ls.dts')
5203 hdr, fents = fip_util.decode_fip(data)
5204
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01005205 tmpdir = None
Simon Glass3efb2972021-11-23 21:08:59 -07005206 try:
5207 tmpdir, updated_fname = self._SetupImageInTmpdir()
5208 with test_util.capture_sys_output() as (stdout, stderr):
5209 self._DoBinman('ls', '-i', updated_fname)
5210 finally:
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01005211 if tmpdir:
5212 shutil.rmtree(tmpdir)
Simon Glass3efb2972021-11-23 21:08:59 -07005213 lines = stdout.getvalue().splitlines()
5214 expected = [
Simon Glass49cd2b32023-02-07 14:34:18 -07005215'Name Image-pos Size Entry-type Offset Uncomp-size',
5216'--------------------------------------------------------------',
5217'image 0 2d3 section 0',
5218' atf-fip 0 90 atf-fip 0',
5219' soc-fw 88 4 blob-ext 88',
5220' u-boot 8c 4 u-boot 8c',
5221' fdtmap 90 243 fdtmap 90',
Simon Glass3efb2972021-11-23 21:08:59 -07005222]
5223 self.assertEqual(expected, lines)
5224
5225 image = control.images['image']
5226 entries = image.GetEntries()
5227 fdtmap = entries['fdtmap']
5228
5229 fdtmap_data = data[fdtmap.image_pos:fdtmap.image_pos + fdtmap.size]
5230 magic = fdtmap_data[:8]
5231 self.assertEqual(b'_FDTMAP_', magic)
Simon Glass80025522022-01-29 14:14:04 -07005232 self.assertEqual(tools.get_bytes(0, 8), fdtmap_data[8:16])
Simon Glass3efb2972021-11-23 21:08:59 -07005233
5234 fdt_data = fdtmap_data[16:]
5235 dtb = fdt.Fdt.FromData(fdt_data)
5236 dtb.Scan()
5237 props = self._GetPropTree(dtb, BASE_DTB_PROPS, prefix='/')
5238 self.assertEqual({
5239 'atf-fip/soc-fw:image-pos': 136,
5240 'atf-fip/soc-fw:offset': 136,
5241 'atf-fip/soc-fw:size': 4,
5242 'atf-fip/u-boot:image-pos': 140,
5243 'atf-fip/u-boot:offset': 140,
5244 'atf-fip/u-boot:size': 4,
5245 'atf-fip:image-pos': 0,
5246 'atf-fip:offset': 0,
5247 'atf-fip:size': 144,
5248 'image-pos': 0,
5249 'offset': 0,
5250 'fdtmap:image-pos': fdtmap.image_pos,
5251 'fdtmap:offset': fdtmap.offset,
5252 'fdtmap:size': len(fdtmap_data),
5253 'size': len(data),
5254 }, props)
5255
5256 def testFipExtractOneEntry(self):
5257 """Test extracting a single entry fron an FIP"""
5258 self._DoReadFileRealDtb('207_fip_ls.dts')
Simon Glass80025522022-01-29 14:14:04 -07005259 image_fname = tools.get_output_filename('image.bin')
Simon Glass3efb2972021-11-23 21:08:59 -07005260 fname = os.path.join(self._indir, 'output.extact')
5261 control.ExtractEntries(image_fname, fname, None, ['atf-fip/u-boot'])
Simon Glass80025522022-01-29 14:14:04 -07005262 data = tools.read_file(fname)
Simon Glass3efb2972021-11-23 21:08:59 -07005263 self.assertEqual(U_BOOT_DATA, data)
5264
5265 def testFipReplace(self):
5266 """Test replacing a single file in a FIP"""
Simon Glass80025522022-01-29 14:14:04 -07005267 expected = U_BOOT_DATA + tools.get_bytes(0x78, 50)
Simon Glass3efb2972021-11-23 21:08:59 -07005268 data = self._DoReadFileRealDtb('208_fip_replace.dts')
Simon Glass80025522022-01-29 14:14:04 -07005269 updated_fname = tools.get_output_filename('image-updated.bin')
5270 tools.write_file(updated_fname, data)
Simon Glass3efb2972021-11-23 21:08:59 -07005271 entry_name = 'atf-fip/u-boot'
5272 control.WriteEntry(updated_fname, entry_name, expected,
5273 allow_resize=True)
5274 actual = control.ReadEntry(updated_fname, entry_name)
5275 self.assertEqual(expected, actual)
5276
Simon Glass80025522022-01-29 14:14:04 -07005277 new_data = tools.read_file(updated_fname)
Simon Glass3efb2972021-11-23 21:08:59 -07005278 hdr, fents = fip_util.decode_fip(new_data)
5279
5280 self.assertEqual(2, len(fents))
5281
5282 # Check that the FIP entry is updated
5283 fent = fents[1]
5284 self.assertEqual(0x8c, fent.offset)
5285 self.assertEqual(len(expected), fent.size)
5286 self.assertEqual(0, fent.flags)
5287 self.assertEqual(expected, fent.data)
5288 self.assertEqual(True, fent.valid)
5289
5290 def testFipMissing(self):
5291 with test_util.capture_sys_output() as (stdout, stderr):
5292 self._DoTestFile('209_fip_missing.dts', allow_missing=True)
5293 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07005294 self.assertRegex(err, "Image 'image'.*missing.*: rmm-fw")
Simon Glass3efb2972021-11-23 21:08:59 -07005295
5296 def testFipSize(self):
5297 """Test a FIP with a size property"""
5298 data = self._DoReadFile('210_fip_size.dts')
5299 self.assertEqual(0x100 + len(U_BOOT_DATA), len(data))
5300 hdr, fents = fip_util.decode_fip(data)
5301 self.assertEqual(fip_util.HEADER_MAGIC, hdr.name)
5302 self.assertEqual(fip_util.HEADER_SERIAL, hdr.serial)
5303
5304 self.assertEqual(1, len(fents))
5305
5306 fent = fents[0]
5307 self.assertEqual('soc-fw', fent.fip_type)
5308 self.assertEqual(0x60, fent.offset)
5309 self.assertEqual(len(ATF_BL31_DATA), fent.size)
5310 self.assertEqual(ATF_BL31_DATA, fent.data)
5311 self.assertEqual(True, fent.valid)
5312
5313 rest = data[0x60 + len(ATF_BL31_DATA):0x100]
Simon Glass80025522022-01-29 14:14:04 -07005314 self.assertEqual(tools.get_bytes(0xff, len(rest)), rest)
Simon Glass3efb2972021-11-23 21:08:59 -07005315
5316 def testFipBadAlign(self):
5317 """Test that an invalid alignment value in a FIP is detected"""
5318 with self.assertRaises(ValueError) as e:
5319 self._DoTestFile('211_fip_bad_align.dts')
5320 self.assertIn(
5321 "Node \'/binman/atf-fip\': FIP alignment 31 must be a power of two",
5322 str(e.exception))
5323
5324 def testFipCollection(self):
5325 """Test using a FIP in a collection"""
5326 data = self._DoReadFile('212_fip_collection.dts')
5327 entry1 = control.images['image'].GetEntries()['collection']
5328 data1 = data[:entry1.size]
5329 hdr1, fents2 = fip_util.decode_fip(data1)
5330
5331 entry2 = control.images['image'].GetEntries()['atf-fip']
5332 data2 = data[entry2.offset:entry2.offset + entry2.size]
5333 hdr1, fents2 = fip_util.decode_fip(data2)
5334
5335 # The 'collection' entry should have U-Boot included at the end
5336 self.assertEqual(entry1.size - len(U_BOOT_DATA), entry2.size)
5337 self.assertEqual(data1, data2 + U_BOOT_DATA)
5338 self.assertEqual(U_BOOT_DATA, data1[-4:])
5339
5340 # There should be a U-Boot after the final FIP
5341 self.assertEqual(U_BOOT_DATA, data[-4:])
Simon Glass76f496d2021-07-06 10:36:37 -06005342
Simon Glassccae6862022-01-12 13:10:35 -07005343 def testFakeBlob(self):
5344 """Test handling of faking an external blob"""
5345 with test_util.capture_sys_output() as (stdout, stderr):
5346 self._DoTestFile('217_fake_blob.dts', allow_missing=True,
5347 allow_fake_blobs=True)
5348 err = stderr.getvalue()
5349 self.assertRegex(
5350 err,
5351 "Image '.*' has faked external blobs and is non-functional: .*")
Simon Glassccae6862022-01-12 13:10:35 -07005352
Simon Glassceb5f912022-01-09 20:13:46 -07005353 def testExtblobListFaked(self):
5354 """Test an extblob with missing external blob that are faked"""
5355 with test_util.capture_sys_output() as (stdout, stderr):
5356 self._DoTestFile('216_blob_ext_list_missing.dts',
5357 allow_fake_blobs=True)
5358 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07005359 self.assertRegex(err, "Image 'image'.*faked.*: blob-ext-list")
Simon Glassceb5f912022-01-09 20:13:46 -07005360
Simon Glass162017b2022-01-09 20:13:57 -07005361 def testListBintools(self):
5362 args = ['tool', '--list']
5363 with test_util.capture_sys_output() as (stdout, _):
5364 self._DoBinman(*args)
5365 out = stdout.getvalue().splitlines()
5366 self.assertTrue(len(out) >= 2)
5367
5368 def testFetchBintools(self):
5369 def fail_download(url):
Simon Glass80025522022-01-29 14:14:04 -07005370 """Take the tools.download() function by raising an exception"""
Simon Glass162017b2022-01-09 20:13:57 -07005371 raise urllib.error.URLError('my error')
5372
5373 args = ['tool']
5374 with self.assertRaises(ValueError) as e:
5375 self._DoBinman(*args)
5376 self.assertIn("Invalid arguments to 'tool' subcommand",
5377 str(e.exception))
5378
5379 args = ['tool', '--fetch']
5380 with self.assertRaises(ValueError) as e:
5381 self._DoBinman(*args)
5382 self.assertIn('Please specify bintools to fetch', str(e.exception))
5383
5384 args = ['tool', '--fetch', '_testing']
Simon Glass80025522022-01-29 14:14:04 -07005385 with unittest.mock.patch.object(tools, 'download',
Simon Glass162017b2022-01-09 20:13:57 -07005386 side_effect=fail_download):
5387 with test_util.capture_sys_output() as (stdout, _):
5388 self._DoBinman(*args)
5389 self.assertIn('failed to fetch with all methods', stdout.getvalue())
5390
Simon Glass620c4462022-01-09 20:14:11 -07005391 def testBintoolDocs(self):
5392 """Test for creation of bintool documentation"""
5393 with test_util.capture_sys_output() as (stdout, stderr):
5394 control.write_bintool_docs(control.bintool.Bintool.get_tool_list())
5395 self.assertTrue(len(stdout.getvalue()) > 0)
5396
5397 def testBintoolDocsMissing(self):
5398 """Test handling of missing bintool documentation"""
5399 with self.assertRaises(ValueError) as e:
5400 with test_util.capture_sys_output() as (stdout, stderr):
5401 control.write_bintool_docs(
5402 control.bintool.Bintool.get_tool_list(), 'mkimage')
5403 self.assertIn('Documentation is missing for modules: mkimage',
5404 str(e.exception))
5405
Jan Kiszka58c407f2022-01-28 20:37:53 +01005406 def testListWithGenNode(self):
5407 """Check handling of an FDT map when the section cannot be found"""
5408 entry_args = {
5409 'of-list': 'test-fdt1 test-fdt2',
5410 }
5411 data = self._DoReadFileDtb(
5412 '219_fit_gennode.dts',
5413 entry_args=entry_args,
5414 use_real_dtb=True,
5415 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])
5416
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01005417 tmpdir = None
Jan Kiszka58c407f2022-01-28 20:37:53 +01005418 try:
5419 tmpdir, updated_fname = self._SetupImageInTmpdir()
5420 with test_util.capture_sys_output() as (stdout, stderr):
5421 self._RunBinman('ls', '-i', updated_fname)
5422 finally:
Heinrich Schuchardt53f6f592023-12-16 00:26:04 +01005423 if tmpdir:
5424 shutil.rmtree(tmpdir)
Jan Kiszka58c407f2022-01-28 20:37:53 +01005425
Alper Nebi Yasake706d872022-02-08 01:08:05 +03005426 def testFitSubentryUsesBintool(self):
5427 """Test that binman FIT subentries can use bintools"""
5428 command.test_result = self._HandleGbbCommand
5429 entry_args = {
5430 'keydir': 'devkeys',
5431 'bmpblk': 'bmpblk.bin',
5432 }
5433 data, _, _, _ = self._DoReadFileDtb('220_fit_subentry_bintool.dts',
5434 entry_args=entry_args)
5435
Alper Nebi Yasakd4553262022-02-08 01:08:07 +03005436 expected = (GBB_DATA + GBB_DATA + tools.get_bytes(0, 8) +
5437 tools.get_bytes(0, 0x2180 - 16))
Alper Nebi Yasake706d872022-02-08 01:08:05 +03005438 self.assertIn(expected, data)
5439
5440 def testFitSubentryMissingBintool(self):
5441 """Test that binman reports missing bintools for FIT subentries"""
5442 entry_args = {
5443 'keydir': 'devkeys',
5444 }
5445 with test_util.capture_sys_output() as (_, stderr):
5446 self._DoTestFile('220_fit_subentry_bintool.dts',
5447 force_missing_bintools='futility', entry_args=entry_args)
5448 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07005449 self.assertRegex(err, "Image 'image'.*missing bintools.*: futility")
Simon Glassccae6862022-01-12 13:10:35 -07005450
Alper Nebi Yasak1e4ffd82022-02-09 22:02:35 +03005451 def testFitSubentryHashSubnode(self):
5452 """Test an image with a FIT inside"""
Marek Vasutf7413f02023-07-18 07:23:58 -06005453 self._SetupSplElf()
Alper Nebi Yasak1e4ffd82022-02-09 22:02:35 +03005454 data, _, _, out_dtb_name = self._DoReadFileDtb(
5455 '221_fit_subentry_hash.dts', use_real_dtb=True, update_dtb=True)
5456
5457 mkimage_dtb = fdt.Fdt.FromData(data)
5458 mkimage_dtb.Scan()
5459 binman_dtb = fdt.Fdt(out_dtb_name)
5460 binman_dtb.Scan()
5461
5462 # Check that binman didn't add hash values
5463 fnode = binman_dtb.GetNode('/binman/fit/images/kernel/hash')
5464 self.assertNotIn('value', fnode.props)
5465
5466 fnode = binman_dtb.GetNode('/binman/fit/images/fdt-1/hash')
5467 self.assertNotIn('value', fnode.props)
5468
5469 # Check that mkimage added hash values
5470 fnode = mkimage_dtb.GetNode('/images/kernel/hash')
5471 self.assertIn('value', fnode.props)
5472
5473 fnode = mkimage_dtb.GetNode('/images/fdt-1/hash')
5474 self.assertIn('value', fnode.props)
5475
Roger Quadros5cdcea02022-02-19 20:50:04 +02005476 def testPackTeeOs(self):
5477 """Test that an image with an TEE binary can be created"""
5478 data = self._DoReadFile('222_tee_os.dts')
5479 self.assertEqual(TEE_OS_DATA, data[:len(TEE_OS_DATA)])
5480
Neha Malcom Francis59be2552023-12-05 15:12:18 +05305481 def testPackTiDm(self):
5482 """Test that an image with a TI DM binary can be created"""
5483 data = self._DoReadFile('225_ti_dm.dts')
5484 self.assertEqual(TI_DM_DATA, data[:len(TI_DM_DATA)])
5485
Simon Glass912339f2022-02-08 11:50:03 -07005486 def testFitFdtOper(self):
5487 """Check handling of a specified FIT operation"""
5488 entry_args = {
5489 'of-list': 'test-fdt1 test-fdt2',
5490 'default-dt': 'test-fdt2',
5491 }
5492 self._DoReadFileDtb(
5493 '223_fit_fdt_oper.dts',
5494 entry_args=entry_args,
5495 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
5496
5497 def testFitFdtBadOper(self):
5498 """Check handling of an FDT map when the section cannot be found"""
5499 with self.assertRaises(ValueError) as exc:
5500 self._DoReadFileDtb('224_fit_bad_oper.dts')
Simon Glass05f71dc2022-03-05 20:19:09 -07005501 self.assertIn("Node '/binman/fit': subnode 'images/@fdt-SEQ': Unknown operation 'unknown'",
Simon Glass912339f2022-02-08 11:50:03 -07005502 str(exc.exception))
5503
Simon Glassdd156a42022-03-05 20:18:59 -07005504 def test_uses_expand_size(self):
5505 """Test that the 'expand-size' property cannot be used anymore"""
5506 with self.assertRaises(ValueError) as e:
5507 data = self._DoReadFile('225_expand_size_bad.dts')
5508 self.assertIn(
5509 "Node '/binman/u-boot': Please use 'extend-size' instead of 'expand-size'",
5510 str(e.exception))
5511
Simon Glass5f423422022-03-05 20:19:12 -07005512 def testFitSplitElf(self):
5513 """Test an image with an FIT with an split-elf operation"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005514 if not elf.ELF_TOOLS:
5515 self.skipTest('Python elftools not available')
Simon Glass5f423422022-03-05 20:19:12 -07005516 entry_args = {
5517 'of-list': 'test-fdt1 test-fdt2',
5518 'default-dt': 'test-fdt2',
5519 'atf-bl31-path': 'bl31.elf',
5520 'tee-os-path': 'tee.elf',
5521 }
5522 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
5523 data = self._DoReadFileDtb(
5524 '226_fit_split_elf.dts',
5525 entry_args=entry_args,
5526 extra_indirs=[test_subdir])[0]
5527
5528 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
5529 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
5530
5531 base_keys = {'description', 'type', 'arch', 'os', 'compression',
5532 'data', 'load'}
5533 dtb = fdt.Fdt.FromData(fit_data)
5534 dtb.Scan()
5535
5536 elf_data = tools.read_file(os.path.join(self._indir, 'bl31.elf'))
5537 segments, entry = elf.read_loadable_segments(elf_data)
5538
5539 # We assume there are two segments
Brandon Maiera657bc62024-06-04 16:16:05 +00005540 self.assertEqual(2, len(segments))
Simon Glass5f423422022-03-05 20:19:12 -07005541
5542 atf1 = dtb.GetNode('/images/atf-1')
5543 _, start, data = segments[0]
5544 self.assertEqual(base_keys | {'entry'}, atf1.props.keys())
5545 self.assertEqual(entry,
5546 fdt_util.fdt32_to_cpu(atf1.props['entry'].value))
5547 self.assertEqual(start,
5548 fdt_util.fdt32_to_cpu(atf1.props['load'].value))
5549 self.assertEqual(data, atf1.props['data'].bytes)
5550
Jonas Karlmand2c7d902023-01-21 19:01:48 +00005551 hash_node = atf1.FindNode('hash')
5552 self.assertIsNotNone(hash_node)
5553 self.assertEqual({'algo', 'value'}, hash_node.props.keys())
5554
Simon Glass5f423422022-03-05 20:19:12 -07005555 atf2 = dtb.GetNode('/images/atf-2')
5556 self.assertEqual(base_keys, atf2.props.keys())
5557 _, start, data = segments[1]
5558 self.assertEqual(start,
5559 fdt_util.fdt32_to_cpu(atf2.props['load'].value))
5560 self.assertEqual(data, atf2.props['data'].bytes)
5561
Jonas Karlmand2c7d902023-01-21 19:01:48 +00005562 hash_node = atf2.FindNode('hash')
5563 self.assertIsNotNone(hash_node)
5564 self.assertEqual({'algo', 'value'}, hash_node.props.keys())
5565
5566 hash_node = dtb.GetNode('/images/tee-1/hash-1')
5567 self.assertIsNotNone(hash_node)
5568 self.assertEqual({'algo', 'value'}, hash_node.props.keys())
5569
Simon Glass5f423422022-03-05 20:19:12 -07005570 conf = dtb.GetNode('/configurations')
5571 self.assertEqual({'default'}, conf.props.keys())
5572
5573 for subnode in conf.subnodes:
5574 self.assertEqual({'description', 'fdt', 'loadables'},
5575 subnode.props.keys())
5576 self.assertEqual(
5577 ['atf-1', 'atf-2', 'tee-1', 'tee-2'],
5578 fdt_util.GetStringList(subnode, 'loadables'))
5579
5580 def _check_bad_fit(self, dts):
5581 """Check a bad FIT
5582
5583 This runs with the given dts and returns the assertion raised
5584
5585 Args:
5586 dts (str): dts filename to use
5587
5588 Returns:
5589 str: Assertion string raised
5590 """
5591 entry_args = {
5592 'of-list': 'test-fdt1 test-fdt2',
5593 'default-dt': 'test-fdt2',
5594 'atf-bl31-path': 'bl31.elf',
5595 'tee-os-path': 'tee.elf',
5596 }
5597 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
5598 with self.assertRaises(ValueError) as exc:
5599 self._DoReadFileDtb(dts, entry_args=entry_args,
5600 extra_indirs=[test_subdir])[0]
5601 return str(exc.exception)
5602
5603 def testFitSplitElfBadElf(self):
5604 """Test a FIT split-elf operation with an invalid ELF file"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005605 if not elf.ELF_TOOLS:
5606 self.skipTest('Python elftools not available')
Simon Glass5f423422022-03-05 20:19:12 -07005607 TestFunctional._MakeInputFile('bad.elf', tools.get_bytes(100, 100))
5608 entry_args = {
5609 'of-list': 'test-fdt1 test-fdt2',
5610 'default-dt': 'test-fdt2',
5611 'atf-bl31-path': 'bad.elf',
5612 'tee-os-path': 'tee.elf',
5613 }
5614 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
5615 with self.assertRaises(ValueError) as exc:
5616 self._DoReadFileDtb(
5617 '226_fit_split_elf.dts',
5618 entry_args=entry_args,
5619 extra_indirs=[test_subdir])[0]
5620 self.assertIn(
5621 "Node '/binman/fit': subnode 'images/@atf-SEQ': Failed to read ELF file: Magic number does not match",
5622 str(exc.exception))
5623
Simon Glass5f423422022-03-05 20:19:12 -07005624 def checkFitSplitElf(self, **kwargs):
Simon Glass7d3e4072022-08-07 09:46:46 -06005625 """Test an split-elf FIT with a missing ELF file
5626
5627 Args:
5628 kwargs (dict of str): Arguments to pass to _DoTestFile()
5629
5630 Returns:
5631 tuple:
5632 str: stdout result
5633 str: stderr result
5634 """
Simon Glass5f423422022-03-05 20:19:12 -07005635 entry_args = {
5636 'of-list': 'test-fdt1 test-fdt2',
5637 'default-dt': 'test-fdt2',
5638 'atf-bl31-path': 'bl31.elf',
5639 'tee-os-path': 'missing.elf',
5640 }
5641 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
5642 with test_util.capture_sys_output() as (stdout, stderr):
5643 self._DoTestFile(
5644 '226_fit_split_elf.dts', entry_args=entry_args,
Simon Glass7d3e4072022-08-07 09:46:46 -06005645 extra_indirs=[test_subdir], verbosity=3, **kwargs)
5646 out = stdout.getvalue()
5647 err = stderr.getvalue()
5648 return out, err
Simon Glass5f423422022-03-05 20:19:12 -07005649
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005650 def testFitSplitElfBadDirective(self):
5651 """Test a FIT split-elf invalid fit,xxx directive in an image node"""
5652 if not elf.ELF_TOOLS:
5653 self.skipTest('Python elftools not available')
5654 err = self._check_bad_fit('227_fit_bad_dir.dts')
5655 self.assertIn(
5656 "Node '/binman/fit': subnode 'images/@atf-SEQ': Unknown directive 'fit,something'",
5657 err)
5658
5659 def testFitSplitElfBadDirectiveConfig(self):
5660 """Test a FIT split-elf with invalid fit,xxx directive in config"""
5661 if not elf.ELF_TOOLS:
5662 self.skipTest('Python elftools not available')
5663 err = self._check_bad_fit('228_fit_bad_dir_config.dts')
5664 self.assertEqual(
5665 "Node '/binman/fit': subnode 'configurations/@config-SEQ': Unknown directive 'fit,config'",
5666 err)
5667
5668
Simon Glass5f423422022-03-05 20:19:12 -07005669 def testFitSplitElfMissing(self):
5670 """Test an split-elf FIT with a missing ELF file"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005671 if not elf.ELF_TOOLS:
5672 self.skipTest('Python elftools not available')
Simon Glass7d3e4072022-08-07 09:46:46 -06005673 out, err = self.checkFitSplitElf(allow_missing=True)
Simon Glass5f423422022-03-05 20:19:12 -07005674 self.assertRegex(
5675 err,
5676 "Image '.*' is missing external blobs and is non-functional: .*")
Simon Glass7d3e4072022-08-07 09:46:46 -06005677 self.assertNotRegex(out, '.*Faked blob.*')
5678 fname = tools.get_output_filename('binman-fake/missing.elf')
5679 self.assertFalse(os.path.exists(fname))
Simon Glass5f423422022-03-05 20:19:12 -07005680
5681 def testFitSplitElfFaked(self):
5682 """Test an split-elf FIT with faked ELF file"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005683 if not elf.ELF_TOOLS:
5684 self.skipTest('Python elftools not available')
Simon Glass7d3e4072022-08-07 09:46:46 -06005685 out, err = self.checkFitSplitElf(allow_missing=True, allow_fake_blobs=True)
Simon Glass5f423422022-03-05 20:19:12 -07005686 self.assertRegex(
5687 err,
5688 "Image '.*' is missing external blobs and is non-functional: .*")
Simon Glass7d3e4072022-08-07 09:46:46 -06005689 self.assertRegex(
5690 out,
5691 "Entry '/binman/fit/images/@tee-SEQ/tee-os': Faked blob '.*binman-fake/missing.elf")
5692 fname = tools.get_output_filename('binman-fake/missing.elf')
5693 self.assertTrue(os.path.exists(fname))
Simon Glass5f423422022-03-05 20:19:12 -07005694
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005695 def testMkimageMissingBlob(self):
5696 """Test using mkimage to build an image"""
5697 with test_util.capture_sys_output() as (stdout, stderr):
5698 self._DoTestFile('229_mkimage_missing.dts', allow_missing=True,
5699 allow_fake_blobs=True)
5700 err = stderr.getvalue()
5701 self.assertRegex(
5702 err,
5703 "Image '.*' has faked external blobs and is non-functional: .*")
5704
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005705 def testPreLoad(self):
5706 """Test an image with a pre-load header"""
5707 entry_args = {
Simon Glasse2dfb962023-07-24 09:19:57 -06005708 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005709 }
Simon Glasse2dfb962023-07-24 09:19:57 -06005710 data = self._DoReadFileDtb(
5711 '230_pre_load.dts', entry_args=entry_args,
5712 extra_indirs=[os.path.join(self._binman_dir, 'test')])[0]
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005713 self.assertEqual(PRE_LOAD_MAGIC, data[:len(PRE_LOAD_MAGIC)])
5714 self.assertEqual(PRE_LOAD_VERSION, data[4:4 + len(PRE_LOAD_VERSION)])
5715 self.assertEqual(PRE_LOAD_HDR_SIZE, data[8:8 + len(PRE_LOAD_HDR_SIZE)])
Simon Glasse2dfb962023-07-24 09:19:57 -06005716
5717 def testPreLoadNoKey(self):
5718 """Test an image with a pre-load heade0r with missing key"""
5719 with self.assertRaises(FileNotFoundError) as exc:
5720 self._DoReadFile('230_pre_load.dts')
5721 self.assertIn("No such file or directory: 'dev.key'",
5722 str(exc.exception))
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005723
5724 def testPreLoadPkcs(self):
5725 """Test an image with a pre-load header with padding pkcs"""
Simon Glasse2dfb962023-07-24 09:19:57 -06005726 entry_args = {
5727 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5728 }
5729 data = self._DoReadFileDtb('231_pre_load_pkcs.dts',
5730 entry_args=entry_args)[0]
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005731 self.assertEqual(PRE_LOAD_MAGIC, data[:len(PRE_LOAD_MAGIC)])
5732 self.assertEqual(PRE_LOAD_VERSION, data[4:4 + len(PRE_LOAD_VERSION)])
5733 self.assertEqual(PRE_LOAD_HDR_SIZE, data[8:8 + len(PRE_LOAD_HDR_SIZE)])
5734
5735 def testPreLoadPss(self):
5736 """Test an image with a pre-load header with padding pss"""
Simon Glasse2dfb962023-07-24 09:19:57 -06005737 entry_args = {
5738 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5739 }
5740 data = self._DoReadFileDtb('232_pre_load_pss.dts',
5741 entry_args=entry_args)[0]
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005742 self.assertEqual(PRE_LOAD_MAGIC, data[:len(PRE_LOAD_MAGIC)])
5743 self.assertEqual(PRE_LOAD_VERSION, data[4:4 + len(PRE_LOAD_VERSION)])
5744 self.assertEqual(PRE_LOAD_HDR_SIZE, data[8:8 + len(PRE_LOAD_HDR_SIZE)])
5745
5746 def testPreLoadInvalidPadding(self):
5747 """Test an image with a pre-load header with an invalid padding"""
Simon Glasse2dfb962023-07-24 09:19:57 -06005748 entry_args = {
5749 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5750 }
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005751 with self.assertRaises(ValueError) as e:
Simon Glasse2dfb962023-07-24 09:19:57 -06005752 self._DoReadFileDtb('233_pre_load_invalid_padding.dts',
5753 entry_args=entry_args)
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005754
5755 def testPreLoadInvalidSha(self):
5756 """Test an image with a pre-load header with an invalid hash"""
Simon Glasse2dfb962023-07-24 09:19:57 -06005757 entry_args = {
5758 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5759 }
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005760 with self.assertRaises(ValueError) as e:
Simon Glasse2dfb962023-07-24 09:19:57 -06005761 self._DoReadFileDtb('234_pre_load_invalid_sha.dts',
5762 entry_args=entry_args)
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005763
5764 def testPreLoadInvalidAlgo(self):
5765 """Test an image with a pre-load header with an invalid algo"""
5766 with self.assertRaises(ValueError) as e:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005767 data = self._DoReadFile('235_pre_load_invalid_algo.dts')
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005768
5769 def testPreLoadInvalidKey(self):
5770 """Test an image with a pre-load header with an invalid key"""
Simon Glasse2dfb962023-07-24 09:19:57 -06005771 entry_args = {
5772 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5773 }
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005774 with self.assertRaises(ValueError) as e:
Simon Glasse2dfb962023-07-24 09:19:57 -06005775 data = self._DoReadFileDtb('236_pre_load_invalid_key.dts',
5776 entry_args=entry_args)
Roger Quadros5cdcea02022-02-19 20:50:04 +02005777
Alper Nebi Yasak5cff63f2022-03-27 18:31:44 +03005778 def _CheckSafeUniqueNames(self, *images):
5779 """Check all entries of given images for unsafe unique names"""
5780 for image in images:
5781 entries = {}
5782 image._CollectEntries(entries, {}, image)
5783 for entry in entries.values():
5784 uniq = entry.GetUniqueName()
5785
5786 # Used as part of a filename, so must not be absolute paths.
5787 self.assertFalse(os.path.isabs(uniq))
5788
5789 def testSafeUniqueNames(self):
5790 """Test entry unique names are safe in single image configuration"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005791 data = self._DoReadFileRealDtb('237_unique_names.dts')
Alper Nebi Yasak5cff63f2022-03-27 18:31:44 +03005792
5793 orig_image = control.images['image']
5794 image_fname = tools.get_output_filename('image.bin')
5795 image = Image.FromFile(image_fname)
5796
5797 self._CheckSafeUniqueNames(orig_image, image)
5798
5799 def testSafeUniqueNamesMulti(self):
5800 """Test entry unique names are safe with multiple images"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005801 data = self._DoReadFileRealDtb('238_unique_names_multi.dts')
Alper Nebi Yasak5cff63f2022-03-27 18:31:44 +03005802
5803 orig_image = control.images['image']
5804 image_fname = tools.get_output_filename('image.bin')
5805 image = Image.FromFile(image_fname)
5806
5807 self._CheckSafeUniqueNames(orig_image, image)
5808
Alper Nebi Yasake63ca5a2022-03-27 18:31:45 +03005809 def testReplaceCmdWithBintool(self):
5810 """Test replacing an entry that needs a bintool to pack"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005811 data = self._DoReadFileRealDtb('239_replace_with_bintool.dts')
Alper Nebi Yasake63ca5a2022-03-27 18:31:45 +03005812 expected = U_BOOT_DATA + b'aa'
5813 self.assertEqual(expected, data[:len(expected)])
5814
5815 try:
5816 tmpdir, updated_fname = self._SetupImageInTmpdir()
5817 fname = os.path.join(tmpdir, 'update-testing.bin')
5818 tools.write_file(fname, b'zz')
5819 self._DoBinman('replace', '-i', updated_fname,
5820 '_testing', '-f', fname)
5821
5822 data = tools.read_file(updated_fname)
5823 expected = U_BOOT_DATA + b'zz'
5824 self.assertEqual(expected, data[:len(expected)])
5825 finally:
5826 shutil.rmtree(tmpdir)
5827
5828 def testReplaceCmdOtherWithBintool(self):
5829 """Test replacing an entry when another needs a bintool to pack"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005830 data = self._DoReadFileRealDtb('239_replace_with_bintool.dts')
Alper Nebi Yasake63ca5a2022-03-27 18:31:45 +03005831 expected = U_BOOT_DATA + b'aa'
5832 self.assertEqual(expected, data[:len(expected)])
5833
5834 try:
5835 tmpdir, updated_fname = self._SetupImageInTmpdir()
5836 fname = os.path.join(tmpdir, 'update-u-boot.bin')
5837 tools.write_file(fname, b'x' * len(U_BOOT_DATA))
5838 self._DoBinman('replace', '-i', updated_fname,
5839 'u-boot', '-f', fname)
5840
5841 data = tools.read_file(updated_fname)
5842 expected = b'x' * len(U_BOOT_DATA) + b'aa'
5843 self.assertEqual(expected, data[:len(expected)])
5844 finally:
5845 shutil.rmtree(tmpdir)
5846
Alper Nebi Yasak00c68f12022-03-27 18:31:46 +03005847 def testReplaceResizeNoRepackSameSize(self):
5848 """Test replacing entries with same-size data without repacking"""
5849 expected = b'x' * len(U_BOOT_DATA)
5850 data, expected_fdtmap, _ = self._RunReplaceCmd('u-boot', expected)
5851 self.assertEqual(expected, data)
5852
5853 path, fdtmap = state.GetFdtContents('fdtmap')
5854 self.assertIsNotNone(path)
5855 self.assertEqual(expected_fdtmap, fdtmap)
5856
5857 def testReplaceResizeNoRepackSmallerSize(self):
5858 """Test replacing entries with smaller-size data without repacking"""
5859 new_data = b'x'
5860 data, expected_fdtmap, _ = self._RunReplaceCmd('u-boot', new_data)
5861 expected = new_data.ljust(len(U_BOOT_DATA), b'\0')
5862 self.assertEqual(expected, data)
5863
5864 path, fdtmap = state.GetFdtContents('fdtmap')
5865 self.assertIsNotNone(path)
5866 self.assertEqual(expected_fdtmap, fdtmap)
5867
Alper Nebi Yasak6cadc502022-03-27 18:31:48 +03005868 def testExtractFit(self):
5869 """Test extracting a FIT section"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005870 self._DoReadFileRealDtb('240_fit_extract_replace.dts')
Alper Nebi Yasak6cadc502022-03-27 18:31:48 +03005871 image_fname = tools.get_output_filename('image.bin')
5872
5873 fit_data = control.ReadEntry(image_fname, 'fit')
5874 fit = fdt.Fdt.FromData(fit_data)
5875 fit.Scan()
5876
5877 # Check subentry data inside the extracted fit
5878 for node_path, expected in [
5879 ('/images/kernel', U_BOOT_DATA),
5880 ('/images/fdt-1', U_BOOT_NODTB_DATA),
5881 ('/images/scr-1', COMPRESS_DATA),
5882 ]:
5883 node = fit.GetNode(node_path)
5884 data = fit.GetProps(node)['data'].bytes
5885 self.assertEqual(expected, data)
5886
5887 def testExtractFitSubentries(self):
5888 """Test extracting FIT section subentries"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005889 self._DoReadFileRealDtb('240_fit_extract_replace.dts')
Alper Nebi Yasak6cadc502022-03-27 18:31:48 +03005890 image_fname = tools.get_output_filename('image.bin')
5891
5892 for entry_path, expected in [
5893 ('fit/kernel', U_BOOT_DATA),
5894 ('fit/kernel/u-boot', U_BOOT_DATA),
5895 ('fit/fdt-1', U_BOOT_NODTB_DATA),
5896 ('fit/fdt-1/u-boot-nodtb', U_BOOT_NODTB_DATA),
5897 ('fit/scr-1', COMPRESS_DATA),
5898 ('fit/scr-1/blob', COMPRESS_DATA),
5899 ]:
5900 data = control.ReadEntry(image_fname, entry_path)
5901 self.assertEqual(expected, data)
5902
Alper Nebi Yasak49892642022-03-27 18:31:49 +03005903 def testReplaceFitSubentryLeafSameSize(self):
5904 """Test replacing a FIT leaf subentry with same-size data"""
5905 new_data = b'x' * len(U_BOOT_DATA)
5906 data, expected_fdtmap, _ = self._RunReplaceCmd(
5907 'fit/kernel/u-boot', new_data,
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005908 dts='240_fit_extract_replace.dts')
Alper Nebi Yasak49892642022-03-27 18:31:49 +03005909 self.assertEqual(new_data, data)
5910
5911 path, fdtmap = state.GetFdtContents('fdtmap')
5912 self.assertIsNotNone(path)
5913 self.assertEqual(expected_fdtmap, fdtmap)
5914
5915 def testReplaceFitSubentryLeafBiggerSize(self):
5916 """Test replacing a FIT leaf subentry with bigger-size data"""
5917 new_data = b'ub' * len(U_BOOT_NODTB_DATA)
5918 data, expected_fdtmap, _ = self._RunReplaceCmd(
5919 'fit/fdt-1/u-boot-nodtb', new_data,
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005920 dts='240_fit_extract_replace.dts')
Alper Nebi Yasak49892642022-03-27 18:31:49 +03005921 self.assertEqual(new_data, data)
5922
5923 # Will be repacked, so fdtmap must change
5924 path, fdtmap = state.GetFdtContents('fdtmap')
5925 self.assertIsNotNone(path)
5926 self.assertNotEqual(expected_fdtmap, fdtmap)
5927
5928 def testReplaceFitSubentryLeafSmallerSize(self):
5929 """Test replacing a FIT leaf subentry with smaller-size data"""
5930 new_data = b'x'
5931 expected = new_data.ljust(len(U_BOOT_NODTB_DATA), b'\0')
5932 data, expected_fdtmap, _ = self._RunReplaceCmd(
5933 'fit/fdt-1/u-boot-nodtb', new_data,
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005934 dts='240_fit_extract_replace.dts')
Alper Nebi Yasak49892642022-03-27 18:31:49 +03005935 self.assertEqual(expected, data)
5936
5937 path, fdtmap = state.GetFdtContents('fdtmap')
5938 self.assertIsNotNone(path)
5939 self.assertEqual(expected_fdtmap, fdtmap)
5940
Alper Nebi Yasak1d44c8e2022-03-27 18:31:50 +03005941 def testReplaceSectionSimple(self):
Simon Glass49b77e82023-03-02 17:02:44 -07005942 """Test replacing a simple section with same-sized data"""
Alper Nebi Yasak1d44c8e2022-03-27 18:31:50 +03005943 new_data = b'w' * len(COMPRESS_DATA + U_BOOT_DATA)
Simon Glass49b77e82023-03-02 17:02:44 -07005944 data, expected_fdtmap, image = self._RunReplaceCmd('section',
5945 new_data, dts='241_replace_section_simple.dts')
5946 self.assertEqual(new_data, data)
5947
5948 entries = image.GetEntries()
5949 self.assertIn('section', entries)
5950 entry = entries['section']
5951 self.assertEqual(len(new_data), entry.size)
5952
5953 def testReplaceSectionLarger(self):
5954 """Test replacing a simple section with larger data"""
5955 new_data = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) + 1)
5956 data, expected_fdtmap, image = self._RunReplaceCmd('section',
5957 new_data, dts='241_replace_section_simple.dts')
5958 self.assertEqual(new_data, data)
5959
5960 entries = image.GetEntries()
5961 self.assertIn('section', entries)
5962 entry = entries['section']
5963 self.assertEqual(len(new_data), entry.size)
5964 fentry = entries['fdtmap']
5965 self.assertEqual(entry.offset + entry.size, fentry.offset)
5966
5967 def testReplaceSectionSmaller(self):
5968 """Test replacing a simple section with smaller data"""
5969 new_data = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) - 1) + b'\0'
5970 data, expected_fdtmap, image = self._RunReplaceCmd('section',
5971 new_data, dts='241_replace_section_simple.dts')
5972 self.assertEqual(new_data, data)
5973
5974 # The new size is the same as the old, just with a pad byte at the end
5975 entries = image.GetEntries()
5976 self.assertIn('section', entries)
5977 entry = entries['section']
5978 self.assertEqual(len(new_data), entry.size)
5979
5980 def testReplaceSectionSmallerAllow(self):
5981 """Test failing to replace a simple section with smaller data"""
5982 new_data = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) - 1)
5983 try:
5984 state.SetAllowEntryContraction(True)
5985 with self.assertRaises(ValueError) as exc:
5986 self._RunReplaceCmd('section', new_data,
5987 dts='241_replace_section_simple.dts')
5988 finally:
5989 state.SetAllowEntryContraction(False)
5990
5991 # Since we have no information about the position of things within the
5992 # section, we cannot adjust the position of /section-u-boot so it ends
5993 # up outside the section
Simon Glassc6b283f2022-08-13 11:40:46 -06005994 self.assertIn(
Simon Glass49b77e82023-03-02 17:02:44 -07005995 "Node '/section/u-boot': Offset 0x24 (36) size 0x4 (4) is outside "
5996 "the section '/section' starting at 0x0 (0) of size 0x27 (39)",
Simon Glassc6b283f2022-08-13 11:40:46 -06005997 str(exc.exception))
Alper Nebi Yasak1d44c8e2022-03-27 18:31:50 +03005998
Simon Glass8fbca772022-08-13 11:40:48 -06005999 def testMkimageImagename(self):
6000 """Test using mkimage with -n holding the data too"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006001 self._SetupSplElf()
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006002 data = self._DoReadFile('242_mkimage_name.dts')
Simon Glass8fbca772022-08-13 11:40:48 -06006003
6004 # Check that the data appears in the file somewhere
6005 self.assertIn(U_BOOT_SPL_DATA, data)
6006
Simon Glassbb7d3bb2022-09-06 20:26:52 -06006007 # Get struct legacy_img_hdr -> ih_name
Simon Glass8fbca772022-08-13 11:40:48 -06006008 name = data[0x20:0x40]
6009
6010 # Build the filename that we expect to be placed in there, by virtue of
6011 # the -n paraameter
6012 expect = os.path.join(tools.get_output_dir(), 'mkimage.mkimage')
6013
6014 # Check that the image name is set to the temporary filename used
6015 self.assertEqual(expect.encode('utf-8')[:0x20], name)
6016
Simon Glassb1669752022-08-13 11:40:49 -06006017 def testMkimageImage(self):
6018 """Test using mkimage with -n holding the data too"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006019 self._SetupSplElf()
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006020 data = self._DoReadFile('243_mkimage_image.dts')
Simon Glassb1669752022-08-13 11:40:49 -06006021
6022 # Check that the data appears in the file somewhere
6023 self.assertIn(U_BOOT_SPL_DATA, data)
6024
Simon Glassbb7d3bb2022-09-06 20:26:52 -06006025 # Get struct legacy_img_hdr -> ih_name
Simon Glassb1669752022-08-13 11:40:49 -06006026 name = data[0x20:0x40]
6027
6028 # Build the filename that we expect to be placed in there, by virtue of
6029 # the -n paraameter
6030 expect = os.path.join(tools.get_output_dir(), 'mkimage-n.mkimage')
6031
6032 # Check that the image name is set to the temporary filename used
6033 self.assertEqual(expect.encode('utf-8')[:0x20], name)
6034
6035 # Check the corect data is in the imagename file
6036 self.assertEqual(U_BOOT_DATA, tools.read_file(expect))
6037
6038 def testMkimageImageNoContent(self):
6039 """Test using mkimage with -n and no data"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006040 self._SetupSplElf()
Simon Glassb1669752022-08-13 11:40:49 -06006041 with self.assertRaises(ValueError) as exc:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006042 self._DoReadFile('244_mkimage_image_no_content.dts')
Simon Glassb1669752022-08-13 11:40:49 -06006043 self.assertIn('Could not complete processing of contents',
6044 str(exc.exception))
6045
6046 def testMkimageImageBad(self):
6047 """Test using mkimage with imagename node and data-to-imagename"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006048 self._SetupSplElf()
Simon Glassb1669752022-08-13 11:40:49 -06006049 with self.assertRaises(ValueError) as exc:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006050 self._DoReadFile('245_mkimage_image_bad.dts')
Simon Glassb1669752022-08-13 11:40:49 -06006051 self.assertIn('Cannot use both imagename node and data-to-imagename',
6052 str(exc.exception))
6053
Simon Glassbd5cd882022-08-13 11:40:50 -06006054 def testCollectionOther(self):
6055 """Test a collection where the data comes from another section"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006056 data = self._DoReadFile('246_collection_other.dts')
Simon Glassbd5cd882022-08-13 11:40:50 -06006057 self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA +
6058 tools.get_bytes(0xff, 2) + U_BOOT_NODTB_DATA +
6059 tools.get_bytes(0xfe, 3) + U_BOOT_DTB_DATA,
6060 data)
6061
6062 def testMkimageCollection(self):
6063 """Test using a collection referring to an entry in a mkimage entry"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006064 self._SetupSplElf()
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006065 data = self._DoReadFile('247_mkimage_coll.dts')
Simon Glassbd5cd882022-08-13 11:40:50 -06006066 expect = U_BOOT_SPL_DATA + U_BOOT_DATA
6067 self.assertEqual(expect, data[:len(expect)])
6068
Stefan Herbrechtsmeier11121d32022-08-19 16:25:25 +02006069 def testCompressDtbPrependInvalid(self):
6070 """Test that invalid header is detected"""
6071 with self.assertRaises(ValueError) as e:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006072 self._DoReadFileDtb('248_compress_dtb_prepend_invalid.dts')
Stefan Herbrechtsmeier11121d32022-08-19 16:25:25 +02006073 self.assertIn("Node '/binman/u-boot-dtb': Invalid prepend in "
6074 "'u-boot-dtb': 'invalid'", str(e.exception))
6075
6076 def testCompressDtbPrependLength(self):
6077 """Test that compress with length header works as expected"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006078 data = self._DoReadFileRealDtb('249_compress_dtb_prepend_length.dts')
Stefan Herbrechtsmeier11121d32022-08-19 16:25:25 +02006079 image = control.images['image']
6080 entries = image.GetEntries()
6081 self.assertIn('u-boot-dtb', entries)
6082 u_boot_dtb = entries['u-boot-dtb']
6083 self.assertIn('fdtmap', entries)
6084 fdtmap = entries['fdtmap']
6085
6086 image_fname = tools.get_output_filename('image.bin')
6087 orig = control.ReadEntry(image_fname, 'u-boot-dtb')
6088 dtb = fdt.Fdt.FromData(orig)
6089 dtb.Scan()
6090 props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
6091 expected = {
6092 'u-boot:size': len(U_BOOT_DATA),
6093 'u-boot-dtb:uncomp-size': len(orig),
6094 'u-boot-dtb:size': u_boot_dtb.size,
6095 'fdtmap:size': fdtmap.size,
6096 'size': len(data),
6097 }
6098 self.assertEqual(expected, props)
6099
6100 # Check implementation
6101 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
6102 rest = data[len(U_BOOT_DATA):]
6103 comp_data_len = struct.unpack('<I', rest[:4])[0]
6104 comp_data = rest[4:4 + comp_data_len]
6105 orig2 = self._decompress(comp_data)
6106 self.assertEqual(orig, orig2)
6107
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +02006108 def testInvalidCompress(self):
6109 """Test that invalid compress algorithm is detected"""
6110 with self.assertRaises(ValueError) as e:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006111 self._DoTestFile('250_compress_dtb_invalid.dts')
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +02006112 self.assertIn("Unknown algorithm 'invalid'", str(e.exception))
6113
Stefan Herbrechtsmeiera14bee02022-08-19 16:25:32 +02006114 def testCompUtilCompressions(self):
6115 """Test compression algorithms"""
6116 for bintool in self.comp_bintools.values():
6117 self._CheckBintool(bintool)
6118 data = bintool.compress(COMPRESS_DATA)
6119 self.assertNotEqual(COMPRESS_DATA, data)
6120 orig = bintool.decompress(data)
Brandon Maiera657bc62024-06-04 16:16:05 +00006121 self.assertEqual(COMPRESS_DATA, orig)
Stefan Herbrechtsmeiera14bee02022-08-19 16:25:32 +02006122
6123 def testCompUtilVersions(self):
6124 """Test tool version of compression algorithms"""
6125 for bintool in self.comp_bintools.values():
6126 self._CheckBintool(bintool)
6127 version = bintool.version()
6128 self.assertRegex(version, '^v?[0-9]+[0-9.]*')
6129
6130 def testCompUtilPadding(self):
6131 """Test padding of compression algorithms"""
Stefan Herbrechtsmeiera5e4dcb2022-08-19 16:25:38 +02006132 # Skip zstd because it doesn't support padding
6133 for bintool in [v for k,v in self.comp_bintools.items() if k != 'zstd']:
Stefan Herbrechtsmeiera14bee02022-08-19 16:25:32 +02006134 self._CheckBintool(bintool)
6135 data = bintool.compress(COMPRESS_DATA)
6136 self.assertNotEqual(COMPRESS_DATA, data)
6137 data += tools.get_bytes(0, 64)
6138 orig = bintool.decompress(data)
Brandon Maiera657bc62024-06-04 16:16:05 +00006139 self.assertEqual(COMPRESS_DATA, orig)
Stefan Herbrechtsmeiera14bee02022-08-19 16:25:32 +02006140
Stefan Herbrechtsmeiera5e4dcb2022-08-19 16:25:38 +02006141 def testCompressDtbZstd(self):
6142 """Test that zstd compress of device-tree files failed"""
6143 with self.assertRaises(ValueError) as e:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006144 self._DoTestFile('251_compress_dtb_zstd.dts')
Stefan Herbrechtsmeiera5e4dcb2022-08-19 16:25:38 +02006145 self.assertIn("Node '/binman/u-boot-dtb': The zstd compression "
6146 "requires a length header", str(e.exception))
6147
Quentin Schulz9b5c6482022-09-02 15:10:48 +02006148 def testMkimageMultipleDataFiles(self):
6149 """Test passing multiple files to mkimage in a mkimage entry"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006150 self._SetupSplElf()
6151 self._SetupTplElf()
Quentin Schulz9b5c6482022-09-02 15:10:48 +02006152 data = self._DoReadFile('252_mkimage_mult_data.dts')
6153 # Size of files are packed in their 4B big-endian format
6154 expect = struct.pack('>I', len(U_BOOT_TPL_DATA))
6155 expect += struct.pack('>I', len(U_BOOT_SPL_DATA))
6156 # Size info is always followed by a 4B zero value.
6157 expect += tools.get_bytes(0, 4)
6158 expect += U_BOOT_TPL_DATA
6159 # All but last files are 4B-aligned
6160 align_pad = len(U_BOOT_TPL_DATA) % 4
6161 if align_pad:
6162 expect += tools.get_bytes(0, align_pad)
6163 expect += U_BOOT_SPL_DATA
6164 self.assertEqual(expect, data[-len(expect):])
6165
Marek Vasutf7413f02023-07-18 07:23:58 -06006166 def testMkimageMultipleExpanded(self):
6167 """Test passing multiple files to mkimage in a mkimage entry"""
6168 self._SetupSplElf()
6169 self._SetupTplElf()
6170 entry_args = {
6171 'spl-bss-pad': 'y',
6172 'spl-dtb': 'y',
6173 }
6174 data = self._DoReadFileDtb('252_mkimage_mult_data.dts',
6175 use_expanded=True, entry_args=entry_args)[0]
6176 pad_len = 10
6177 tpl_expect = U_BOOT_TPL_DATA
6178 spl_expect = U_BOOT_SPL_NODTB_DATA + tools.get_bytes(0, pad_len)
6179 spl_expect += U_BOOT_SPL_DTB_DATA
6180
6181 content = data[0x40:]
6182 lens = struct.unpack('>III', content[:12])
6183
6184 # Size of files are packed in their 4B big-endian format
6185 # Size info is always followed by a 4B zero value.
6186 self.assertEqual(len(tpl_expect), lens[0])
6187 self.assertEqual(len(spl_expect), lens[1])
6188 self.assertEqual(0, lens[2])
6189
6190 rest = content[12:]
6191 self.assertEqual(tpl_expect, rest[:len(tpl_expect)])
6192
6193 rest = rest[len(tpl_expect):]
6194 align_pad = len(tpl_expect) % 4
6195 self.assertEqual(tools.get_bytes(0, align_pad), rest[:align_pad])
6196 rest = rest[align_pad:]
6197 self.assertEqual(spl_expect, rest)
6198
Quentin Schulz9b5c6482022-09-02 15:10:48 +02006199 def testMkimageMultipleNoContent(self):
6200 """Test passing multiple data files to mkimage with one data file having no content"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006201 self._SetupSplElf()
Quentin Schulz9b5c6482022-09-02 15:10:48 +02006202 with self.assertRaises(ValueError) as exc:
6203 self._DoReadFile('253_mkimage_mult_no_content.dts')
6204 self.assertIn('Could not complete processing of contents',
6205 str(exc.exception))
6206
Quentin Schulz0d3a9262022-09-02 15:10:49 +02006207 def testMkimageFilename(self):
6208 """Test using mkimage to build a binary with a filename"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006209 self._SetupSplElf()
Quentin Schulz0d3a9262022-09-02 15:10:49 +02006210 retcode = self._DoTestFile('254_mkimage_filename.dts')
6211 self.assertEqual(0, retcode)
6212 fname = tools.get_output_filename('mkimage-test.bin')
6213 self.assertTrue(os.path.exists(fname))
6214
Simon Glass56d05412022-02-28 07:16:54 -07006215 def testVpl(self):
6216 """Test that an image with VPL and its device tree can be created"""
6217 # ELF file with a '__bss_size' symbol
6218 self._SetupVplElf()
6219 data = self._DoReadFile('255_u_boot_vpl.dts')
6220 self.assertEqual(U_BOOT_VPL_DATA + U_BOOT_VPL_DTB_DATA, data)
6221
6222 def testVplNoDtb(self):
6223 """Test that an image with vpl/u-boot-vpl-nodtb.bin can be created"""
6224 self._SetupVplElf()
6225 data = self._DoReadFile('256_u_boot_vpl_nodtb.dts')
6226 self.assertEqual(U_BOOT_VPL_NODTB_DATA,
6227 data[:len(U_BOOT_VPL_NODTB_DATA)])
6228
6229 def testExpandedVpl(self):
6230 """Test that an expanded entry type is selected for TPL when needed"""
6231 self._SetupVplElf()
6232
6233 entry_args = {
6234 'vpl-bss-pad': 'y',
6235 'vpl-dtb': 'y',
6236 }
6237 self._DoReadFileDtb('257_fdt_incl_vpl.dts', use_expanded=True,
6238 entry_args=entry_args)
6239 image = control.images['image']
6240 entries = image.GetEntries()
6241 self.assertEqual(1, len(entries))
6242
6243 # We only have u-boot-vpl, which be expanded
6244 self.assertIn('u-boot-vpl', entries)
6245 entry = entries['u-boot-vpl']
6246 self.assertEqual('u-boot-vpl-expanded', entry.etype)
6247 subent = entry.GetEntries()
6248 self.assertEqual(3, len(subent))
6249 self.assertIn('u-boot-vpl-nodtb', subent)
6250 self.assertIn('u-boot-vpl-bss-pad', subent)
6251 self.assertIn('u-boot-vpl-dtb', subent)
6252
6253 def testVplBssPadMissing(self):
6254 """Test that a missing symbol is detected"""
6255 self._SetupVplElf('u_boot_ucode_ptr')
6256 with self.assertRaises(ValueError) as e:
6257 self._DoReadFile('258_vpl_bss_pad.dts')
6258 self.assertIn('Expected __bss_size symbol in vpl/u-boot-vpl',
6259 str(e.exception))
6260
Neha Malcom Francis3eb4be32022-10-17 16:36:25 +05306261 def testSymlink(self):
Andrew Davis6b463da2023-07-22 00:14:44 +05306262 """Test that image files can be symlinked"""
Neha Malcom Francis3eb4be32022-10-17 16:36:25 +05306263 retcode = self._DoTestFile('259_symlink.dts', debug=True, map=True)
6264 self.assertEqual(0, retcode)
6265 image = control.images['test_image']
6266 fname = tools.get_output_filename('test_image.bin')
6267 sname = tools.get_output_filename('symlink_to_test.bin')
6268 self.assertTrue(os.path.islink(sname))
6269 self.assertEqual(os.readlink(sname), fname)
Alper Nebi Yasake63ca5a2022-03-27 18:31:45 +03006270
Andrew Davis6b463da2023-07-22 00:14:44 +05306271 def testSymlinkOverwrite(self):
6272 """Test that symlinked images can be overwritten"""
6273 testdir = TestFunctional._MakeInputDir('symlinktest')
6274 self._DoTestFile('259_symlink.dts', debug=True, map=True, output_dir=testdir)
6275 # build the same image again in the same directory so that existing symlink is present
6276 self._DoTestFile('259_symlink.dts', debug=True, map=True, output_dir=testdir)
6277 fname = tools.get_output_filename('test_image.bin')
6278 sname = tools.get_output_filename('symlink_to_test.bin')
6279 self.assertTrue(os.path.islink(sname))
6280 self.assertEqual(os.readlink(sname), fname)
6281
Simon Glass37f85de2022-10-20 18:22:47 -06006282 def testSymbolsElf(self):
6283 """Test binman can assign symbols embedded in an ELF file"""
6284 if not elf.ELF_TOOLS:
6285 self.skipTest('Python elftools not available')
6286 self._SetupTplElf('u_boot_binman_syms')
6287 self._SetupVplElf('u_boot_binman_syms')
6288 self._SetupSplElf('u_boot_binman_syms')
6289 data = self._DoReadFileDtb('260_symbols_elf.dts')[0]
6290 image_fname = tools.get_output_filename('image.bin')
6291
6292 image = control.images['image']
6293 entries = image.GetEntries()
6294
6295 for entry in entries.values():
6296 # No symbols in u-boot and it has faked contents anyway
6297 if entry.name == 'u-boot':
6298 continue
6299 edata = data[entry.image_pos:entry.image_pos + entry.size]
6300 efname = tools.get_output_filename(f'edata-{entry.name}')
6301 tools.write_file(efname, edata)
6302
6303 syms = elf.GetSymbolFileOffset(efname, ['_binman_u_boot'])
6304 re_name = re.compile('_binman_(u_boot_(.*))_prop_(.*)')
6305 for name, sym in syms.items():
6306 msg = 'test'
6307 val = elf.GetSymbolValue(sym, edata, msg)
6308 entry_m = re_name.match(name)
6309 if entry_m:
6310 ename, prop = entry_m.group(1), entry_m.group(3)
6311 entry, entry_name, prop_name = image.LookupEntry(entries,
6312 name, msg)
6313 if prop_name == 'offset':
6314 expect_val = entry.offset
6315 elif prop_name == 'image_pos':
6316 expect_val = entry.image_pos
6317 elif prop_name == 'size':
6318 expect_val = entry.size
6319 self.assertEqual(expect_val, val)
6320
6321 def testSymbolsElfBad(self):
6322 """Check error when trying to write symbols without the elftools lib"""
6323 if not elf.ELF_TOOLS:
6324 self.skipTest('Python elftools not available')
6325 self._SetupTplElf('u_boot_binman_syms')
6326 self._SetupVplElf('u_boot_binman_syms')
6327 self._SetupSplElf('u_boot_binman_syms')
6328 try:
6329 elf.ELF_TOOLS = False
6330 with self.assertRaises(ValueError) as exc:
6331 self._DoReadFileDtb('260_symbols_elf.dts')
6332 finally:
6333 elf.ELF_TOOLS = True
6334 self.assertIn(
6335 "Section '/binman': entry '/binman/u-boot-spl-elf': "
6336 'Cannot write symbols to an ELF file without Python elftools',
6337 str(exc.exception))
6338
Simon Glassde244162023-01-07 14:07:08 -07006339 def testSectionFilename(self):
6340 """Check writing of section contents to a file"""
6341 data = self._DoReadFile('261_section_fname.dts')
6342 expected = (b'&&' + U_BOOT_DATA + b'&&&' +
6343 tools.get_bytes(ord('!'), 7) +
6344 U_BOOT_DATA + tools.get_bytes(ord('&'), 12))
6345 self.assertEqual(expected, data)
6346
6347 sect_fname = tools.get_output_filename('outfile.bin')
6348 self.assertTrue(os.path.exists(sect_fname))
6349 sect_data = tools.read_file(sect_fname)
6350 self.assertEqual(U_BOOT_DATA, sect_data)
6351
Simon Glass1e9e61c2023-01-07 14:07:12 -07006352 def testAbsent(self):
6353 """Check handling of absent entries"""
6354 data = self._DoReadFile('262_absent.dts')
6355 self.assertEqual(U_BOOT_DATA + U_BOOT_IMG_DATA, data)
6356
Simon Glassad5cfe12023-01-07 14:07:14 -07006357 def testPackTeeOsOptional(self):
6358 """Test that an image with an optional TEE binary can be created"""
6359 entry_args = {
6360 'tee-os-path': 'tee.elf',
6361 }
6362 data = self._DoReadFileDtb('263_tee_os_opt.dts',
6363 entry_args=entry_args)[0]
6364 self.assertEqual(U_BOOT_DATA + U_BOOT_IMG_DATA, data)
6365
6366 def checkFitTee(self, dts, tee_fname):
6367 """Check that a tee-os entry works and returns data
6368
6369 Args:
6370 dts (str): Device tree filename to use
6371 tee_fname (str): filename containing tee-os
6372
6373 Returns:
6374 bytes: Image contents
6375 """
6376 if not elf.ELF_TOOLS:
6377 self.skipTest('Python elftools not available')
6378 entry_args = {
6379 'of-list': 'test-fdt1 test-fdt2',
6380 'default-dt': 'test-fdt2',
6381 'tee-os-path': tee_fname,
6382 }
6383 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
6384 data = self._DoReadFileDtb(dts, entry_args=entry_args,
6385 extra_indirs=[test_subdir])[0]
6386 return data
6387
6388 def testFitTeeOsOptionalFit(self):
6389 """Test an image with a FIT with an optional OP-TEE binary"""
6390 data = self.checkFitTee('264_tee_os_opt_fit.dts', 'tee.bin')
6391
6392 # There should be only one node, holding the data set up in SetUpClass()
6393 # for tee.bin
6394 dtb = fdt.Fdt.FromData(data)
6395 dtb.Scan()
6396 node = dtb.GetNode('/images/tee-1')
6397 self.assertEqual(TEE_ADDR,
6398 fdt_util.fdt32_to_cpu(node.props['load'].value))
6399 self.assertEqual(TEE_ADDR,
6400 fdt_util.fdt32_to_cpu(node.props['entry'].value))
6401 self.assertEqual(U_BOOT_DATA, node.props['data'].bytes)
6402
Jonas Karlmanb2be3e42023-07-18 20:34:36 +00006403 with test_util.capture_sys_output() as (stdout, stderr):
6404 self.checkFitTee('264_tee_os_opt_fit.dts', '')
6405 err = stderr.getvalue()
6406 self.assertRegex(
6407 err,
6408 "Image '.*' is missing optional external blobs but is still functional: tee-os")
6409
Simon Glassad5cfe12023-01-07 14:07:14 -07006410 def testFitTeeOsOptionalFitBad(self):
6411 """Test an image with a FIT with an optional OP-TEE binary"""
6412 with self.assertRaises(ValueError) as exc:
6413 self.checkFitTee('265_tee_os_opt_fit_bad.dts', 'tee.bin')
6414 self.assertIn(
6415 "Node '/binman/fit': subnode 'images/@tee-SEQ': Failed to read ELF file: Magic number does not match",
6416 str(exc.exception))
6417
6418 def testFitTeeOsBad(self):
6419 """Test an OP-TEE binary with wrong formats"""
6420 self.make_tee_bin('tee.bad1', 123)
6421 with self.assertRaises(ValueError) as exc:
6422 self.checkFitTee('264_tee_os_opt_fit.dts', 'tee.bad1')
6423 self.assertIn(
6424 "Node '/binman/fit/images/@tee-SEQ/tee-os': OP-TEE paged mode not supported",
6425 str(exc.exception))
6426
6427 self.make_tee_bin('tee.bad2', 0, b'extra data')
6428 with self.assertRaises(ValueError) as exc:
6429 self.checkFitTee('264_tee_os_opt_fit.dts', 'tee.bad2')
6430 self.assertIn(
6431 "Node '/binman/fit/images/@tee-SEQ/tee-os': Invalid OP-TEE file: size mismatch (expected 0x4, have 0xe)",
6432 str(exc.exception))
6433
Simon Glass63328f12023-01-07 14:07:15 -07006434 def testExtblobOptional(self):
6435 """Test an image with an external blob that is optional"""
6436 with test_util.capture_sys_output() as (stdout, stderr):
6437 data = self._DoReadFile('266_blob_ext_opt.dts')
6438 self.assertEqual(REFCODE_DATA, data)
6439 err = stderr.getvalue()
6440 self.assertRegex(
6441 err,
Jonas Karlman9f96b812023-07-18 20:34:34 +00006442 "Image '.*' is missing optional external blobs but is still functional: missing")
Simon Glass63328f12023-01-07 14:07:15 -07006443
Simon Glass7447a9d2023-01-11 16:10:12 -07006444 def testSectionInner(self):
6445 """Test an inner section with a size"""
6446 data = self._DoReadFile('267_section_inner.dts')
6447 expected = U_BOOT_DATA + tools.get_bytes(0, 12)
6448 self.assertEqual(expected, data)
6449
Simon Glassa4948b22023-01-11 16:10:14 -07006450 def testNull(self):
6451 """Test an image with a null entry"""
6452 data = self._DoReadFile('268_null.dts')
6453 self.assertEqual(U_BOOT_DATA + b'\xff\xff\xff\xff' + U_BOOT_IMG_DATA, data)
6454
Simon Glassf1ee03b2023-01-11 16:10:16 -07006455 def testOverlap(self):
6456 """Test an image with a overlapping entry"""
6457 data = self._DoReadFile('269_overlap.dts')
6458 self.assertEqual(U_BOOT_DATA[:1] + b'aa' + U_BOOT_DATA[3:], data)
6459
6460 image = control.images['image']
6461 entries = image.GetEntries()
6462
6463 self.assertIn('inset', entries)
6464 inset = entries['inset']
6465 self.assertEqual(1, inset.offset);
6466 self.assertEqual(1, inset.image_pos);
6467 self.assertEqual(2, inset.size);
6468
6469 def testOverlapNull(self):
6470 """Test an image with a null overlap"""
6471 data = self._DoReadFile('270_overlap_null.dts')
6472 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
6473
6474 # Check the FMAP
6475 fhdr, fentries = fmap_util.DecodeFmap(data[len(U_BOOT_DATA):])
6476 self.assertEqual(4, fhdr.nareas)
6477 fiter = iter(fentries)
6478
6479 fentry = next(fiter)
6480 self.assertEqual(b'SECTION', fentry.name)
6481 self.assertEqual(0, fentry.offset)
6482 self.assertEqual(len(U_BOOT_DATA), fentry.size)
6483 self.assertEqual(0, fentry.flags)
6484
6485 fentry = next(fiter)
6486 self.assertEqual(b'U_BOOT', fentry.name)
6487 self.assertEqual(0, fentry.offset)
6488 self.assertEqual(len(U_BOOT_DATA), fentry.size)
6489 self.assertEqual(0, fentry.flags)
6490
6491 # Make sure that the NULL entry appears in the FMAP
6492 fentry = next(fiter)
6493 self.assertEqual(b'NULL', fentry.name)
6494 self.assertEqual(1, fentry.offset)
6495 self.assertEqual(2, fentry.size)
6496 self.assertEqual(0, fentry.flags)
6497
6498 fentry = next(fiter)
6499 self.assertEqual(b'FMAP', fentry.name)
6500 self.assertEqual(len(U_BOOT_DATA), fentry.offset)
6501
6502 def testOverlapBad(self):
6503 """Test an image with a bad overlapping entry"""
6504 with self.assertRaises(ValueError) as exc:
6505 self._DoReadFile('271_overlap_bad.dts')
6506 self.assertIn(
6507 "Node '/binman/inset': Offset 0x10 (16) ending at 0x12 (18) must overlap with existing entries",
6508 str(exc.exception))
6509
6510 def testOverlapNoOffset(self):
6511 """Test an image with a bad overlapping entry"""
6512 with self.assertRaises(ValueError) as exc:
6513 self._DoReadFile('272_overlap_no_size.dts')
6514 self.assertIn(
6515 "Node '/binman/inset': 'fill' entry is missing properties: size",
6516 str(exc.exception))
6517
Simon Glasse0035c92023-01-11 16:10:17 -07006518 def testBlobSymbol(self):
6519 """Test a blob with symbols read from an ELF file"""
6520 elf_fname = self.ElfTestFile('blob_syms')
6521 TestFunctional._MakeInputFile('blob_syms', tools.read_file(elf_fname))
6522 TestFunctional._MakeInputFile('blob_syms.bin',
6523 tools.read_file(self.ElfTestFile('blob_syms.bin')))
6524
6525 data = self._DoReadFile('273_blob_symbol.dts')
6526
6527 syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
6528 addr = elf.GetSymbolAddress(elf_fname, '__my_start_sym')
6529 self.assertEqual(syms['_binman_sym_magic'].address, addr)
6530 self.assertEqual(syms['_binman_inset_prop_offset'].address, addr + 4)
6531 self.assertEqual(syms['_binman_inset_prop_size'].address, addr + 8)
6532
6533 sym_values = struct.pack('<LLL', elf.BINMAN_SYM_MAGIC_VALUE, 4, 8)
6534 expected = sym_values
6535 self.assertEqual(expected, data[:len(expected)])
6536
Simon Glass49e9c002023-01-11 16:10:19 -07006537 def testOffsetFromElf(self):
6538 """Test a blob with symbols read from an ELF file"""
6539 elf_fname = self.ElfTestFile('blob_syms')
6540 TestFunctional._MakeInputFile('blob_syms', tools.read_file(elf_fname))
6541 TestFunctional._MakeInputFile('blob_syms.bin',
6542 tools.read_file(self.ElfTestFile('blob_syms.bin')))
6543
6544 data = self._DoReadFile('274_offset_from_elf.dts')
6545
6546 syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
6547 base = elf.GetSymbolAddress(elf_fname, '__my_start_sym')
6548
6549 image = control.images['image']
6550 entries = image.GetEntries()
6551
6552 self.assertIn('inset', entries)
6553 inset = entries['inset']
6554
6555 self.assertEqual(base + 4, inset.offset);
6556 self.assertEqual(base + 4, inset.image_pos);
6557 self.assertEqual(4, inset.size);
6558
6559 self.assertIn('inset2', entries)
6560 inset = entries['inset2']
6561 self.assertEqual(base + 8, inset.offset);
6562 self.assertEqual(base + 8, inset.image_pos);
6563 self.assertEqual(4, inset.size);
6564
Jonas Karlmanc59ea892023-01-21 19:01:39 +00006565 def testFitAlign(self):
6566 """Test an image with an FIT with aligned external data"""
6567 data = self._DoReadFile('275_fit_align.dts')
6568 self.assertEqual(4096, len(data))
6569
6570 dtb = fdt.Fdt.FromData(data)
6571 dtb.Scan()
6572
6573 props = self._GetPropTree(dtb, ['data-position'])
6574 expected = {
6575 'u-boot:data-position': 1024,
6576 'fdt-1:data-position': 2048,
6577 'fdt-2:data-position': 3072,
6578 }
6579 self.assertEqual(expected, props)
6580
Jonas Karlman490f73c2023-01-21 19:02:12 +00006581 def testFitFirmwareLoadables(self):
6582 """Test an image with an FIT that use fit,firmware"""
6583 if not elf.ELF_TOOLS:
6584 self.skipTest('Python elftools not available')
6585 entry_args = {
6586 'of-list': 'test-fdt1',
6587 'default-dt': 'test-fdt1',
6588 'atf-bl31-path': 'bl31.elf',
6589 'tee-os-path': 'missing.bin',
6590 }
6591 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
Simon Glass62f85902023-02-23 18:18:01 -07006592 with test_util.capture_sys_output() as (stdout, stderr):
6593 data = self._DoReadFileDtb(
6594 '276_fit_firmware_loadables.dts',
6595 entry_args=entry_args,
6596 extra_indirs=[test_subdir])[0]
Jonas Karlman490f73c2023-01-21 19:02:12 +00006597
6598 dtb = fdt.Fdt.FromData(data)
6599 dtb.Scan()
6600
6601 node = dtb.GetNode('/configurations/conf-uboot-1')
6602 self.assertEqual('u-boot', node.props['firmware'].value)
6603 self.assertEqual(['atf-1', 'atf-2'],
6604 fdt_util.GetStringList(node, 'loadables'))
6605
6606 node = dtb.GetNode('/configurations/conf-atf-1')
6607 self.assertEqual('atf-1', node.props['firmware'].value)
6608 self.assertEqual(['u-boot', 'atf-2'],
6609 fdt_util.GetStringList(node, 'loadables'))
6610
6611 node = dtb.GetNode('/configurations/conf-missing-uboot-1')
6612 self.assertEqual('u-boot', node.props['firmware'].value)
6613 self.assertEqual(['atf-1', 'atf-2'],
6614 fdt_util.GetStringList(node, 'loadables'))
6615
6616 node = dtb.GetNode('/configurations/conf-missing-atf-1')
6617 self.assertEqual('atf-1', node.props['firmware'].value)
6618 self.assertEqual(['u-boot', 'atf-2'],
6619 fdt_util.GetStringList(node, 'loadables'))
6620
6621 node = dtb.GetNode('/configurations/conf-missing-tee-1')
6622 self.assertEqual('atf-1', node.props['firmware'].value)
6623 self.assertEqual(['u-boot', 'atf-2'],
6624 fdt_util.GetStringList(node, 'loadables'))
6625
Simon Glass9a1c7262023-02-22 12:14:49 -07006626 def testTooldir(self):
6627 """Test that we can specify the tooldir"""
6628 with test_util.capture_sys_output() as (stdout, stderr):
6629 self.assertEqual(0, self._DoBinman('--tooldir', 'fred',
6630 'tool', '-l'))
6631 self.assertEqual('fred', bintool.Bintool.tooldir)
6632
6633 # Check that the toolpath is updated correctly
6634 self.assertEqual(['fred'], tools.tool_search_paths)
6635
6636 # Try with a few toolpaths; the tooldir should be at the end
6637 with test_util.capture_sys_output() as (stdout, stderr):
6638 self.assertEqual(0, self._DoBinman(
6639 '--toolpath', 'mary', '--toolpath', 'anna', '--tooldir', 'fred',
6640 'tool', '-l'))
6641 self.assertEqual(['mary', 'anna', 'fred'], tools.tool_search_paths)
6642
Simon Glass49b77e82023-03-02 17:02:44 -07006643 def testReplaceSectionEntry(self):
6644 """Test replacing an entry in a section"""
6645 expect_data = b'w' * len(U_BOOT_DATA + COMPRESS_DATA)
6646 entry_data, expected_fdtmap, image = self._RunReplaceCmd('section/blob',
6647 expect_data, dts='241_replace_section_simple.dts')
6648 self.assertEqual(expect_data, entry_data)
6649
6650 entries = image.GetEntries()
6651 self.assertIn('section', entries)
6652 section = entries['section']
6653
6654 sect_entries = section.GetEntries()
6655 self.assertIn('blob', sect_entries)
6656 entry = sect_entries['blob']
6657 self.assertEqual(len(expect_data), entry.size)
6658
6659 fname = tools.get_output_filename('image-updated.bin')
6660 data = tools.read_file(fname)
6661
6662 new_blob_data = data[entry.image_pos:entry.image_pos + len(expect_data)]
6663 self.assertEqual(expect_data, new_blob_data)
6664
6665 self.assertEqual(U_BOOT_DATA,
6666 data[entry.image_pos + len(expect_data):]
6667 [:len(U_BOOT_DATA)])
6668
6669 def testReplaceSectionDeep(self):
6670 """Test replacing an entry in two levels of sections"""
6671 expect_data = b'w' * len(U_BOOT_DATA + COMPRESS_DATA)
6672 entry_data, expected_fdtmap, image = self._RunReplaceCmd(
6673 'section/section/blob', expect_data,
6674 dts='278_replace_section_deep.dts')
6675 self.assertEqual(expect_data, entry_data)
6676
6677 entries = image.GetEntries()
6678 self.assertIn('section', entries)
6679 section = entries['section']
6680
6681 subentries = section.GetEntries()
6682 self.assertIn('section', subentries)
6683 section = subentries['section']
6684
6685 sect_entries = section.GetEntries()
6686 self.assertIn('blob', sect_entries)
6687 entry = sect_entries['blob']
6688 self.assertEqual(len(expect_data), entry.size)
6689
6690 fname = tools.get_output_filename('image-updated.bin')
6691 data = tools.read_file(fname)
6692
6693 new_blob_data = data[entry.image_pos:entry.image_pos + len(expect_data)]
6694 self.assertEqual(expect_data, new_blob_data)
6695
6696 self.assertEqual(U_BOOT_DATA,
6697 data[entry.image_pos + len(expect_data):]
6698 [:len(U_BOOT_DATA)])
6699
6700 def testReplaceFitSibling(self):
6701 """Test an image with a FIT inside where we replace its sibling"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006702 self._SetupSplElf()
Simon Glass49b77e82023-03-02 17:02:44 -07006703 fname = TestFunctional._MakeInputFile('once', b'available once')
6704 self._DoReadFileRealDtb('277_replace_fit_sibling.dts')
6705 os.remove(fname)
6706
6707 try:
6708 tmpdir, updated_fname = self._SetupImageInTmpdir()
6709
6710 fname = os.path.join(tmpdir, 'update-blob')
6711 expected = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) + 1)
6712 tools.write_file(fname, expected)
6713
6714 self._DoBinman('replace', '-i', updated_fname, 'blob', '-f', fname)
6715 data = tools.read_file(updated_fname)
6716 start = len(U_BOOT_DTB_DATA)
6717 self.assertEqual(expected, data[start:start + len(expected)])
6718 map_fname = os.path.join(tmpdir, 'image-updated.map')
6719 self.assertFalse(os.path.exists(map_fname))
6720 finally:
6721 shutil.rmtree(tmpdir)
6722
Simon Glassc3fe97f2023-03-02 17:02:45 -07006723 def testX509Cert(self):
6724 """Test creating an X509 certificate"""
6725 keyfile = self.TestFile('key.key')
6726 entry_args = {
6727 'keyfile': keyfile,
6728 }
6729 data = self._DoReadFileDtb('279_x509_cert.dts',
6730 entry_args=entry_args)[0]
6731 cert = data[:-4]
6732 self.assertEqual(U_BOOT_DATA, data[-4:])
6733
6734 # TODO: verify the signature
6735
6736 def testX509CertMissing(self):
6737 """Test that binman still produces an image if openssl is missing"""
6738 keyfile = self.TestFile('key.key')
6739 entry_args = {
6740 'keyfile': 'keyfile',
6741 }
6742 with test_util.capture_sys_output() as (_, stderr):
6743 self._DoTestFile('279_x509_cert.dts',
6744 force_missing_bintools='openssl',
6745 entry_args=entry_args)
6746 err = stderr.getvalue()
6747 self.assertRegex(err, "Image 'image'.*missing bintools.*: openssl")
6748
Jonas Karlman35305492023-02-25 19:01:33 +00006749 def testPackRockchipTpl(self):
6750 """Test that an image with a Rockchip TPL binary can be created"""
Simon Glasse3ef5ed2023-07-24 09:19:58 -06006751 data = self._DoReadFile('291_rockchip_tpl.dts')
Jonas Karlman35305492023-02-25 19:01:33 +00006752 self.assertEqual(ROCKCHIP_TPL_DATA, data[:len(ROCKCHIP_TPL_DATA)])
6753
Jonas Karlman1016ec72023-02-25 19:01:35 +00006754 def testMkimageMissingBlobMultiple(self):
6755 """Test missing blob with mkimage entry and multiple-data-files"""
6756 with test_util.capture_sys_output() as (stdout, stderr):
Simon Glasse3ef5ed2023-07-24 09:19:58 -06006757 self._DoTestFile('292_mkimage_missing_multiple.dts', allow_missing=True)
Jonas Karlman1016ec72023-02-25 19:01:35 +00006758 err = stderr.getvalue()
6759 self.assertIn("is missing external blobs and is non-functional", err)
6760
6761 with self.assertRaises(ValueError) as e:
Simon Glasse3ef5ed2023-07-24 09:19:58 -06006762 self._DoTestFile('292_mkimage_missing_multiple.dts', allow_missing=False)
Jonas Karlman1016ec72023-02-25 19:01:35 +00006763 self.assertIn("not found in input path", str(e.exception))
6764
Ivan Mikhaylov3cfcaa4d2023-03-08 01:13:40 +00006765 def _PrepareSignEnv(self, dts='280_fit_sign.dts'):
6766 """Prepare sign environment
6767
6768 Create private and public keys, add pubkey into dtb.
6769
6770 Returns:
6771 Tuple:
6772 FIT container
6773 Image name
6774 Private key
6775 DTB
6776 """
Marek Vasutf7413f02023-07-18 07:23:58 -06006777 self._SetupSplElf()
Ivan Mikhaylov3cfcaa4d2023-03-08 01:13:40 +00006778 data = self._DoReadFileRealDtb(dts)
6779 updated_fname = tools.get_output_filename('image-updated.bin')
6780 tools.write_file(updated_fname, data)
6781 dtb = tools.get_output_filename('source.dtb')
6782 private_key = tools.get_output_filename('test_key.key')
6783 public_key = tools.get_output_filename('test_key.crt')
6784 fit = tools.get_output_filename('fit.fit')
6785 key_dir = tools.get_output_dir()
6786
6787 tools.run('openssl', 'req', '-batch' , '-newkey', 'rsa:4096',
6788 '-sha256', '-new', '-nodes', '-x509', '-keyout',
6789 private_key, '-out', public_key)
6790 tools.run('fdt_add_pubkey', '-a', 'sha256,rsa4096', '-k', key_dir,
6791 '-n', 'test_key', '-r', 'conf', dtb)
6792
6793 return fit, updated_fname, private_key, dtb
6794
6795 def testSignSimple(self):
6796 """Test that a FIT container can be signed in image"""
6797 is_signed = False
6798 fit, fname, private_key, dtb = self._PrepareSignEnv()
6799
6800 # do sign with private key
6801 control.SignEntries(fname, None, private_key, 'sha256,rsa4096',
6802 ['fit'])
6803 is_signed = self._CheckSign(fit, dtb)
6804
6805 self.assertEqual(is_signed, True)
6806
6807 def testSignExactFIT(self):
6808 """Test that a FIT container can be signed and replaced in image"""
6809 is_signed = False
6810 fit, fname, private_key, dtb = self._PrepareSignEnv()
6811
6812 # Make sure we propagate the toolpath, since mkimage may not be on PATH
6813 args = []
6814 if self.toolpath:
6815 for path in self.toolpath:
6816 args += ['--toolpath', path]
6817
6818 # do sign with private key
6819 self._DoBinman(*args, 'sign', '-i', fname, '-k', private_key, '-a',
6820 'sha256,rsa4096', '-f', fit, 'fit')
6821 is_signed = self._CheckSign(fit, dtb)
6822
6823 self.assertEqual(is_signed, True)
6824
6825 def testSignNonFit(self):
6826 """Test a non-FIT entry cannot be signed"""
6827 is_signed = False
6828 fit, fname, private_key, _ = self._PrepareSignEnv(
6829 '281_sign_non_fit.dts')
6830
6831 # do sign with private key
6832 with self.assertRaises(ValueError) as e:
6833 self._DoBinman('sign', '-i', fname, '-k', private_key, '-a',
6834 'sha256,rsa4096', '-f', fit, 'u-boot')
6835 self.assertIn(
6836 "Node '/u-boot': Updating signatures is not supported with this entry type",
6837 str(e.exception))
6838
6839 def testSignMissingMkimage(self):
6840 """Test that FIT signing handles a missing mkimage tool"""
6841 fit, fname, private_key, _ = self._PrepareSignEnv()
6842
6843 # try to sign with a missing mkimage tool
6844 bintool.Bintool.set_missing_list(['mkimage'])
6845 with self.assertRaises(ValueError) as e:
6846 control.SignEntries(fname, None, private_key, 'sha256,rsa4096',
6847 ['fit'])
6848 self.assertIn("Node '/fit': Missing tool: 'mkimage'", str(e.exception))
6849
Simon Glass4abf7842023-07-18 07:23:54 -06006850 def testSymbolNoWrite(self):
6851 """Test disabling of symbol writing"""
Marek Vasutf7413f02023-07-18 07:23:58 -06006852 self._SetupSplElf()
Simon Glass4abf7842023-07-18 07:23:54 -06006853 self.checkSymbols('282_symbols_disable.dts', U_BOOT_SPL_DATA, 0x1c,
6854 no_write_symbols=True)
6855
6856 def testSymbolNoWriteExpanded(self):
6857 """Test disabling of symbol writing in expanded entries"""
6858 entry_args = {
6859 'spl-dtb': '1',
6860 }
6861 self.checkSymbols('282_symbols_disable.dts', U_BOOT_SPL_NODTB_DATA +
6862 U_BOOT_SPL_DTB_DATA, 0x38,
6863 entry_args=entry_args, use_expanded=True,
6864 no_write_symbols=True)
6865
Marek Vasutf7413f02023-07-18 07:23:58 -06006866 def testMkimageSpecial(self):
6867 """Test mkimage ignores special hash-1 node"""
6868 data = self._DoReadFile('283_mkimage_special.dts')
6869
6870 # Just check that the data appears in the file somewhere
6871 self.assertIn(U_BOOT_DATA, data)
6872
Simon Glass2d94c422023-07-18 07:23:59 -06006873 def testFitFdtList(self):
6874 """Test an image with an FIT with the fit,fdt-list-val option"""
6875 entry_args = {
6876 'default-dt': 'test-fdt2',
6877 }
6878 data = self._DoReadFileDtb(
6879 '284_fit_fdt_list.dts',
6880 entry_args=entry_args,
6881 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
6882 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
6883 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
6884
Simon Glass83b8bfe2023-07-18 07:24:01 -06006885 def testSplEmptyBss(self):
6886 """Test an expanded SPL with a zero-size BSS"""
6887 # ELF file with a '__bss_size' symbol
6888 self._SetupSplElf(src_fname='bss_data_zero')
6889
6890 entry_args = {
6891 'spl-bss-pad': 'y',
6892 'spl-dtb': 'y',
6893 }
6894 data = self._DoReadFileDtb('285_spl_expand.dts',
6895 use_expanded=True, entry_args=entry_args)[0]
6896
Simon Glassfc792842023-07-18 07:24:04 -06006897 def testTemplate(self):
6898 """Test using a template"""
6899 TestFunctional._MakeInputFile('vga2.bin', b'#' + VGA_DATA)
6900 data = self._DoReadFile('286_template.dts')
6901 first = U_BOOT_DATA + VGA_DATA + U_BOOT_DTB_DATA
6902 second = U_BOOT_DATA + b'#' + VGA_DATA + U_BOOT_DTB_DATA
6903 self.assertEqual(U_BOOT_IMG_DATA + first + second, data)
6904
Simon Glass09490b02023-07-22 21:43:52 -06006905 dtb_fname1 = tools.get_output_filename('u-boot.dtb.tmpl1')
6906 self.assertTrue(os.path.exists(dtb_fname1))
6907 dtb = fdt.Fdt.FromData(tools.read_file(dtb_fname1))
6908 dtb.Scan()
6909 node1 = dtb.GetNode('/binman/template')
6910 self.assertTrue(node1)
6911 vga = dtb.GetNode('/binman/first/intel-vga')
6912 self.assertTrue(vga)
6913
Simon Glass54825e12023-07-22 21:43:56 -06006914 dtb_fname2 = tools.get_output_filename('u-boot.dtb.tmpl2')
6915 self.assertTrue(os.path.exists(dtb_fname2))
6916 dtb2 = fdt.Fdt.FromData(tools.read_file(dtb_fname2))
6917 dtb2.Scan()
6918 node2 = dtb2.GetNode('/binman/template')
6919 self.assertFalse(node2)
6920
Simon Glass9909c112023-07-18 07:24:05 -06006921 def testTemplateBlobMulti(self):
6922 """Test using a template with 'multiple-images' enabled"""
6923 TestFunctional._MakeInputFile('my-blob.bin', b'blob')
6924 TestFunctional._MakeInputFile('my-blob2.bin', b'other')
6925 retcode = self._DoTestFile('287_template_multi.dts')
6926
6927 self.assertEqual(0, retcode)
6928 image = control.images['image']
6929 image_fname = tools.get_output_filename('my-image.bin')
6930 data = tools.read_file(image_fname)
6931 self.assertEqual(b'blob@@@@other', data)
6932
Simon Glass5dc511b2023-07-18 07:24:06 -06006933 def testTemplateFit(self):
6934 """Test using a template in a FIT"""
6935 fit_data = self._DoReadFile('288_template_fit.dts')
6936 fname = os.path.join(self._indir, 'fit_data.fit')
6937 tools.write_file(fname, fit_data)
6938 out = tools.run('dumpimage', '-l', fname)
6939
Simon Glassaa6e0552023-07-18 07:24:07 -06006940 def testTemplateSection(self):
6941 """Test using a template in a section (not at top level)"""
6942 TestFunctional._MakeInputFile('vga2.bin', b'#' + VGA_DATA)
6943 data = self._DoReadFile('289_template_section.dts')
6944 first = U_BOOT_DATA + VGA_DATA + U_BOOT_DTB_DATA
6945 second = U_BOOT_DATA + b'#' + VGA_DATA + U_BOOT_DTB_DATA
6946 self.assertEqual(U_BOOT_IMG_DATA + first + second + first, data)
6947
Simon Glassf53a7bc2023-07-18 07:24:08 -06006948 def testMkimageSymbols(self):
6949 """Test using mkimage to build an image with symbols in it"""
6950 self._SetupSplElf('u_boot_binman_syms')
6951 data = self._DoReadFile('290_mkimage_sym.dts')
6952
6953 image = control.images['image']
6954 entries = image.GetEntries()
6955 self.assertIn('u-boot', entries)
6956 u_boot = entries['u-boot']
6957
6958 mkim = entries['mkimage']
6959 mkim_entries = mkim.GetEntries()
6960 self.assertIn('u-boot-spl', mkim_entries)
6961 spl = mkim_entries['u-boot-spl']
6962 self.assertIn('u-boot-spl2', mkim_entries)
6963 spl2 = mkim_entries['u-boot-spl2']
6964
6965 # skip the mkimage header and the area sizes
6966 mk_data = data[mkim.offset + 0x40:]
6967 size, term = struct.unpack('>LL', mk_data[:8])
6968
6969 # There should be only one image, so check that the zero terminator is
6970 # present
6971 self.assertEqual(0, term)
6972
6973 content = mk_data[8:8 + size]
6974
6975 # The image should contain the symbols from u_boot_binman_syms.c
6976 # Note that image_pos is adjusted by the base address of the image,
6977 # which is 0x10 in our test image
6978 spl_data = content[:0x18]
6979 content = content[0x1b:]
6980
6981 # After the header is a table of offsets for each image. There should
6982 # only be one image, then a 0 terminator, so figure out the real start
6983 # of the image data
6984 base = 0x40 + 8
6985
6986 # Check symbols in both u-boot-spl and u-boot-spl2
6987 for i in range(2):
6988 vals = struct.unpack('<LLQLL', spl_data)
6989
6990 # The image should contain the symbols from u_boot_binman_syms.c
6991 # Note that image_pos is adjusted by the base address of the image,
6992 # which is 0x10 in our 'u_boot_binman_syms' test image
6993 self.assertEqual(elf.BINMAN_SYM_MAGIC_VALUE, vals[0])
6994 self.assertEqual(base, vals[1])
6995 self.assertEqual(spl2.offset, vals[2])
6996 # figure out the internal positions of its components
6997 self.assertEqual(0x10 + u_boot.image_pos, vals[3])
6998
6999 # Check that spl and spl2 are actually at the indicated positions
7000 self.assertEqual(
7001 elf.BINMAN_SYM_MAGIC_VALUE,
7002 struct.unpack('<I', data[spl.image_pos:spl.image_pos + 4])[0])
7003 self.assertEqual(
7004 elf.BINMAN_SYM_MAGIC_VALUE,
7005 struct.unpack('<I', data[spl2.image_pos:spl2.image_pos + 4])[0])
7006
7007 self.assertEqual(len(U_BOOT_DATA), vals[4])
7008
7009 # Move to next
7010 spl_data = content[:0x18]
7011
Simon Glass86b3e472023-07-22 21:43:57 -06007012 def testTemplatePhandle(self):
7013 """Test using a template in a node containing a phandle"""
7014 entry_args = {
7015 'atf-bl31-path': 'bl31.elf',
7016 }
Simon Glass76ee0ca2023-08-03 17:23:58 -06007017 data = self._DoReadFileDtb('309_template_phandle.dts',
Simon Glass86b3e472023-07-22 21:43:57 -06007018 entry_args=entry_args)
7019 fname = tools.get_output_filename('image.bin')
7020 out = tools.run('dumpimage', '-l', fname)
7021
7022 # We should see the FIT description and one for each of the two images
7023 lines = out.splitlines()
7024 descs = [line.split()[-1] for line in lines if 'escription' in line]
7025 self.assertEqual(['test-desc', 'atf', 'fdt'], descs)
7026
7027 def testTemplatePhandleDup(self):
7028 """Test using a template in a node containing a phandle"""
7029 entry_args = {
7030 'atf-bl31-path': 'bl31.elf',
7031 }
7032 with self.assertRaises(ValueError) as e:
Simon Glass76ee0ca2023-08-03 17:23:58 -06007033 self._DoReadFileDtb('310_template_phandle_dup.dts',
Simon Glass86b3e472023-07-22 21:43:57 -06007034 entry_args=entry_args)
7035 self.assertIn(
7036 'Duplicate phandle 1 in nodes /binman/image/fit/images/atf/atf-bl31 and /binman/image-2/fit/images/atf/atf-bl31',
7037 str(e.exception))
7038
Neha Malcom Francis3b788942023-07-22 00:14:24 +05307039 def testTIBoardConfig(self):
7040 """Test that a schema validated board config file can be generated"""
Simon Glassf1264ba2023-07-24 09:19:59 -06007041 data = self._DoReadFile('293_ti_board_cfg.dts')
Neha Malcom Francis3b788942023-07-22 00:14:24 +05307042 self.assertEqual(TI_BOARD_CONFIG_DATA, data)
7043
Neha Malcom Francis8cd04512024-01-05 17:09:17 +05307044 def testTIBoardConfigLint(self):
7045 """Test that an incorrectly linted config file would generate error"""
7046 with self.assertRaises(ValueError) as e:
7047 data = self._DoReadFile('323_ti_board_cfg_phony.dts')
7048 self.assertIn("Yamllint error", str(e.exception))
7049
Neha Malcom Francis3b788942023-07-22 00:14:24 +05307050 def testTIBoardConfigCombined(self):
7051 """Test that a schema validated combined board config file can be generated"""
Simon Glassf1264ba2023-07-24 09:19:59 -06007052 data = self._DoReadFile('294_ti_board_cfg_combined.dts')
Neha Malcom Francis3b788942023-07-22 00:14:24 +05307053 configlen_noheader = TI_BOARD_CONFIG_DATA * 4
7054 self.assertGreater(data, configlen_noheader)
7055
7056 def testTIBoardConfigNoDataType(self):
7057 """Test that error is thrown when data type is not supported"""
7058 with self.assertRaises(ValueError) as e:
Simon Glassf1264ba2023-07-24 09:19:59 -06007059 data = self._DoReadFile('295_ti_board_cfg_no_type.dts')
Neha Malcom Francis3b788942023-07-22 00:14:24 +05307060 self.assertIn("Schema validation error", str(e.exception))
Simon Glassde244162023-01-07 14:07:08 -07007061
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307062 def testPackTiSecure(self):
7063 """Test that an image with a TI secured binary can be created"""
7064 keyfile = self.TestFile('key.key')
7065 entry_args = {
7066 'keyfile': keyfile,
7067 }
Simon Glassf1264ba2023-07-24 09:19:59 -06007068 data = self._DoReadFileDtb('296_ti_secure.dts',
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307069 entry_args=entry_args)[0]
7070 self.assertGreater(len(data), len(TI_UNSECURE_DATA))
7071
Manorit Chawdhry2e523b02023-12-29 16:16:27 +05307072 def testPackTiSecureFirewall(self):
7073 """Test that an image with a TI secured binary can be created"""
7074 keyfile = self.TestFile('key.key')
7075 entry_args = {
7076 'keyfile': keyfile,
7077 }
7078 data_no_firewall = self._DoReadFileDtb('296_ti_secure.dts',
7079 entry_args=entry_args)[0]
7080 data_firewall = self._DoReadFileDtb('324_ti_secure_firewall.dts',
7081 entry_args=entry_args)[0]
7082 self.assertGreater(len(data_firewall),len(data_no_firewall))
7083
7084 def testPackTiSecureFirewallMissingProperty(self):
7085 """Test that an image with a TI secured binary can be created"""
7086 keyfile = self.TestFile('key.key')
7087 entry_args = {
7088 'keyfile': keyfile,
7089 }
7090 with self.assertRaises(ValueError) as e:
7091 data_firewall = self._DoReadFileDtb('325_ti_secure_firewall_missing_property.dts',
7092 entry_args=entry_args)[0]
7093 self.assertRegex(str(e.exception), "Node '/binman/ti-secure': Subnode 'firewall-0-2' is missing properties: id,region")
7094
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307095 def testPackTiSecureMissingTool(self):
7096 """Test that an image with a TI secured binary (non-functional) can be created
7097 when openssl is missing"""
7098 keyfile = self.TestFile('key.key')
7099 entry_args = {
7100 'keyfile': keyfile,
7101 }
7102 with test_util.capture_sys_output() as (_, stderr):
Simon Glassf1264ba2023-07-24 09:19:59 -06007103 self._DoTestFile('296_ti_secure.dts',
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307104 force_missing_bintools='openssl',
7105 entry_args=entry_args)
7106 err = stderr.getvalue()
7107 self.assertRegex(err, "Image 'image'.*missing bintools.*: openssl")
7108
7109 def testPackTiSecureROM(self):
7110 """Test that a ROM image with a TI secured binary can be created"""
7111 keyfile = self.TestFile('key.key')
7112 entry_args = {
7113 'keyfile': keyfile,
7114 }
Simon Glassf1264ba2023-07-24 09:19:59 -06007115 data = self._DoReadFileDtb('297_ti_secure_rom.dts',
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307116 entry_args=entry_args)[0]
Simon Glassf1264ba2023-07-24 09:19:59 -06007117 data_a = self._DoReadFileDtb('299_ti_secure_rom_a.dts',
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307118 entry_args=entry_args)[0]
Simon Glassf1264ba2023-07-24 09:19:59 -06007119 data_b = self._DoReadFileDtb('300_ti_secure_rom_b.dts',
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307120 entry_args=entry_args)[0]
7121 self.assertGreater(len(data), len(TI_UNSECURE_DATA))
7122 self.assertGreater(len(data_a), len(TI_UNSECURE_DATA))
7123 self.assertGreater(len(data_b), len(TI_UNSECURE_DATA))
7124
7125 def testPackTiSecureROMCombined(self):
7126 """Test that a ROM image with a TI secured binary can be created"""
7127 keyfile = self.TestFile('key.key')
7128 entry_args = {
7129 'keyfile': keyfile,
7130 }
Simon Glassf1264ba2023-07-24 09:19:59 -06007131 data = self._DoReadFileDtb('298_ti_secure_rom_combined.dts',
Neha Malcom Francis5f5f0a62023-07-22 00:14:25 +05307132 entry_args=entry_args)[0]
7133 self.assertGreater(len(data), len(TI_UNSECURE_DATA))
7134
Christian Taedcke62ac29a2023-07-17 09:05:54 +02007135 def testEncryptedNoAlgo(self):
7136 """Test encrypted node with missing required properties"""
7137 with self.assertRaises(ValueError) as e:
7138 self._DoReadFileDtb('301_encrypted_no_algo.dts')
7139 self.assertIn(
7140 "Node '/binman/fit/images/u-boot/encrypted': 'encrypted' entry is missing properties: algo iv-filename",
7141 str(e.exception))
7142
7143 def testEncryptedInvalidIvfile(self):
7144 """Test encrypted node with invalid iv file"""
7145 with self.assertRaises(ValueError) as e:
7146 self._DoReadFileDtb('302_encrypted_invalid_iv_file.dts')
7147 self.assertIn("Filename 'invalid-iv-file' not found in input path",
7148 str(e.exception))
7149
7150 def testEncryptedMissingKey(self):
7151 """Test encrypted node with missing key properties"""
7152 with self.assertRaises(ValueError) as e:
7153 self._DoReadFileDtb('303_encrypted_missing_key.dts')
7154 self.assertIn(
7155 "Node '/binman/fit/images/u-boot/encrypted': Provide either 'key-filename' or 'key-source'",
7156 str(e.exception))
7157
7158 def testEncryptedKeySource(self):
7159 """Test encrypted node with key-source property"""
7160 data = self._DoReadFileDtb('304_encrypted_key_source.dts')[0]
7161
7162 dtb = fdt.Fdt.FromData(data)
7163 dtb.Scan()
7164
7165 node = dtb.GetNode('/images/u-boot/cipher')
7166 self.assertEqual('algo-name', node.props['algo'].value)
7167 self.assertEqual('key-source-value', node.props['key-source'].value)
7168 self.assertEqual(ENCRYPTED_IV_DATA,
7169 tools.to_bytes(''.join(node.props['iv'].value)))
7170 self.assertNotIn('key', node.props)
7171
7172 def testEncryptedKeyFile(self):
7173 """Test encrypted node with key-filename property"""
7174 data = self._DoReadFileDtb('305_encrypted_key_file.dts')[0]
7175
7176 dtb = fdt.Fdt.FromData(data)
7177 dtb.Scan()
7178
7179 node = dtb.GetNode('/images/u-boot/cipher')
7180 self.assertEqual('algo-name', node.props['algo'].value)
7181 self.assertEqual(ENCRYPTED_IV_DATA,
7182 tools.to_bytes(''.join(node.props['iv'].value)))
7183 self.assertEqual(ENCRYPTED_KEY_DATA,
7184 tools.to_bytes(''.join(node.props['key'].value)))
7185 self.assertNotIn('key-source', node.props)
7186
Lukas Funkee901faf2023-07-18 13:53:13 +02007187
7188 def testSplPubkeyDtb(self):
Simon Glass4b861272024-07-20 11:49:41 +01007189 """Test u_boot_spl_pubkey_dtb etype"""
7190 data = tools.read_file(self.TestFile("key.pem"))
7191 self._MakeInputFile("key.crt", data)
7192 self._DoReadFileRealDtb('306_spl_pubkey_dtb.dts')
7193 image = control.images['image']
7194 entries = image.GetEntries()
7195 dtb_entry = entries['u-boot-spl-pubkey-dtb']
7196 dtb_data = dtb_entry.GetData()
7197 dtb = fdt.Fdt.FromData(dtb_data)
7198 dtb.Scan()
Lukas Funkee901faf2023-07-18 13:53:13 +02007199
Simon Glass4b861272024-07-20 11:49:41 +01007200 signature_node = dtb.GetNode('/signature')
7201 self.assertIsNotNone(signature_node)
7202 key_node = signature_node.FindNode("key-key")
7203 self.assertIsNotNone(key_node)
7204 self.assertEqual(fdt_util.GetString(key_node, "required"), "conf")
7205 self.assertEqual(fdt_util.GetString(key_node, "algo"), "sha384,rsa4096")
7206 self.assertEqual(fdt_util.GetString(key_node, "key-name-hint"), "key")
Christian Taedcke62ac29a2023-07-17 09:05:54 +02007207
Lukas Funke712e1062023-08-03 17:22:14 +02007208 def testXilinxBootgenSigning(self):
7209 """Test xilinx-bootgen etype"""
7210 bootgen = bintool.Bintool.create('bootgen')
7211 self._CheckBintool(bootgen)
7212 data = tools.read_file(self.TestFile("key.key"))
7213 self._MakeInputFile("psk.pem", data)
7214 self._MakeInputFile("ssk.pem", data)
7215 self._SetupPmuFwlElf()
7216 self._SetupSplElf()
7217 self._DoReadFileRealDtb('307_xilinx_bootgen_sign.dts')
7218 image_fname = tools.get_output_filename('image.bin')
7219
7220 # Read partition header table and check if authentication is enabled
7221 bootgen_out = bootgen.run_cmd("-arch", "zynqmp",
7222 "-read", image_fname, "pht").splitlines()
7223 attributes = {"authentication": None,
7224 "core": None,
7225 "encryption": None}
7226
7227 for l in bootgen_out:
7228 for a in attributes.keys():
7229 if a in l:
7230 m = re.match(fr".*{a} \[([^]]+)\]", l)
7231 attributes[a] = m.group(1)
7232
7233 self.assertTrue(attributes['authentication'] == "rsa")
7234 self.assertTrue(attributes['core'] == "a53-0")
7235 self.assertTrue(attributes['encryption'] == "no")
7236
7237 def testXilinxBootgenSigningEncryption(self):
7238 """Test xilinx-bootgen etype"""
7239 bootgen = bintool.Bintool.create('bootgen')
7240 self._CheckBintool(bootgen)
7241 data = tools.read_file(self.TestFile("key.key"))
7242 self._MakeInputFile("psk.pem", data)
7243 self._MakeInputFile("ssk.pem", data)
7244 self._SetupPmuFwlElf()
7245 self._SetupSplElf()
7246 self._DoReadFileRealDtb('308_xilinx_bootgen_sign_enc.dts')
7247 image_fname = tools.get_output_filename('image.bin')
7248
7249 # Read boot header in order to verify encryption source and
7250 # encryption parameter
7251 bootgen_out = bootgen.run_cmd("-arch", "zynqmp",
7252 "-read", image_fname, "bh").splitlines()
7253 attributes = {"auth_only":
7254 {"re": r".*auth_only \[([^]]+)\]", "value": None},
7255 "encryption_keystore":
7256 {"re": r" *encryption_keystore \(0x28\) : (.*)",
7257 "value": None},
7258 }
7259
7260 for l in bootgen_out:
7261 for a in attributes.keys():
7262 if a in l:
7263 m = re.match(attributes[a]['re'], l)
7264 attributes[a] = m.group(1)
7265
7266 # Check if fsbl-attribute is set correctly
7267 self.assertTrue(attributes['auth_only'] == "true")
7268 # Check if key is stored in efuse
7269 self.assertTrue(attributes['encryption_keystore'] == "0xa5c3c5a3")
7270
7271 def testXilinxBootgenMissing(self):
7272 """Test that binman still produces an image if bootgen is missing"""
7273 data = tools.read_file(self.TestFile("key.key"))
7274 self._MakeInputFile("psk.pem", data)
7275 self._MakeInputFile("ssk.pem", data)
7276 self._SetupPmuFwlElf()
7277 self._SetupSplElf()
7278 with test_util.capture_sys_output() as (_, stderr):
7279 self._DoTestFile('307_xilinx_bootgen_sign.dts',
7280 force_missing_bintools='bootgen')
7281 err = stderr.getvalue()
7282 self.assertRegex(err,
7283 "Image 'image'.*missing bintools.*: bootgen")
7284
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +05307285 def _GetCapsuleHeaders(self, data):
7286 """Get the capsule header contents
7287
7288 Args:
7289 data: Capsule file contents
7290
7291 Returns:
7292 Dict:
7293 key: Capsule Header name (str)
7294 value: Header field value (str)
7295 """
7296 capsule_file = os.path.join(self._indir, 'test.capsule')
7297 tools.write_file(capsule_file, data)
7298
7299 out = tools.run('mkeficapsule', '--dump-capsule', capsule_file)
7300 lines = out.splitlines()
7301
7302 re_line = re.compile(r'^([^:\-\t]*)(?:\t*\s*:\s*(.*))?$')
7303 vals = {}
7304 for line in lines:
7305 mat = re_line.match(line)
7306 if mat:
7307 vals[mat.group(1)] = mat.group(2)
7308
7309 return vals
7310
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307311 def _CheckCapsule(self, data, signed_capsule=False, version_check=False,
7312 capoemflags=False):
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +05307313 fmp_signature = "3153534D" # 'M', 'S', 'S', '1'
7314 fmp_size = "00000010"
7315 fmp_fw_version = "00000002"
7316 capsule_image_index = "00000001"
7317 oemflag = "00018000"
7318 auth_hdr_revision = "00000200"
7319 auth_hdr_cert_type = "00000EF1"
7320
7321 payload_data_len = len(EFI_CAPSULE_DATA)
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307322
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +05307323 hdr = self._GetCapsuleHeaders(data)
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307324
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +05307325 self.assertEqual(FW_MGMT_GUID.upper(), hdr['EFI_CAPSULE_HDR.CAPSULE_GUID'])
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307326
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +05307327 self.assertEqual(CAPSULE_IMAGE_GUID.upper(),
7328 hdr['FMP_CAPSULE_IMAGE_HDR.UPDATE_IMAGE_TYPE_ID'])
7329 self.assertEqual(capsule_image_index,
7330 hdr['FMP_CAPSULE_IMAGE_HDR.UPDATE_IMAGE_INDEX'])
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307331
7332 if capoemflags:
Sughosh Ganu5ffce2b2023-10-10 14:40:57 +05307333 self.assertEqual(oemflag, hdr['EFI_CAPSULE_HDR.FLAGS'])
7334
7335 if signed_capsule:
7336 self.assertEqual(auth_hdr_revision,
7337 hdr['EFI_FIRMWARE_IMAGE_AUTH.AUTH_INFO.HDR.wREVISION'])
7338 self.assertEqual(auth_hdr_cert_type,
7339 hdr['EFI_FIRMWARE_IMAGE_AUTH.AUTH_INFO.HDR.wCERTTYPE'])
7340 self.assertEqual(WIN_CERT_TYPE_EFI_GUID.upper(),
7341 hdr['EFI_FIRMWARE_IMAGE_AUTH.AUTH_INFO.CERT_TYPE'])
7342
7343 if version_check:
7344 self.assertEqual(fmp_signature,
7345 hdr['FMP_PAYLOAD_HDR.SIGNATURE'])
7346 self.assertEqual(fmp_size,
7347 hdr['FMP_PAYLOAD_HDR.HEADER_SIZE'])
7348 self.assertEqual(fmp_fw_version,
7349 hdr['FMP_PAYLOAD_HDR.FW_VERSION'])
7350
7351 self.assertEqual(payload_data_len, int(hdr['Payload Image Size']))
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307352
Sughosh Ganu6b2d18a2023-10-10 14:40:59 +05307353 def _CheckEmptyCapsule(self, data, accept_capsule=False):
7354 if accept_capsule:
7355 capsule_hdr_guid = EMPTY_CAPSULE_ACCEPT_GUID
7356 else:
7357 capsule_hdr_guid = EMPTY_CAPSULE_REVERT_GUID
7358
7359 hdr = self._GetCapsuleHeaders(data)
7360
7361 self.assertEqual(capsule_hdr_guid.upper(),
7362 hdr['EFI_CAPSULE_HDR.CAPSULE_GUID'])
7363
7364 if accept_capsule:
7365 capsule_size = "0000002C"
7366 else:
7367 capsule_size = "0000001C"
7368 self.assertEqual(capsule_size,
7369 hdr['EFI_CAPSULE_HDR.CAPSULE_IMAGE_SIZE'])
7370
7371 if accept_capsule:
7372 self.assertEqual(CAPSULE_IMAGE_GUID.upper(), hdr['ACCEPT_IMAGE_GUID'])
7373
Sughosh Ganu269ee6d2023-08-22 23:09:59 +05307374 def testCapsuleGen(self):
7375 """Test generation of EFI capsule"""
7376 data = self._DoReadFile('311_capsule.dts')
7377
7378 self._CheckCapsule(data)
7379
7380 def testSignedCapsuleGen(self):
7381 """Test generation of EFI capsule"""
7382 data = tools.read_file(self.TestFile("key.key"))
7383 self._MakeInputFile("key.key", data)
7384 data = tools.read_file(self.TestFile("key.pem"))
7385 self._MakeInputFile("key.crt", data)
7386
7387 data = self._DoReadFile('312_capsule_signed.dts')
7388
7389 self._CheckCapsule(data, signed_capsule=True)
7390
7391 def testCapsuleGenVersionSupport(self):
7392 """Test generation of EFI capsule with version support"""
7393 data = self._DoReadFile('313_capsule_version.dts')
7394
7395 self._CheckCapsule(data, version_check=True)
7396
7397 def testCapsuleGenSignedVer(self):
7398 """Test generation of signed EFI capsule with version information"""
7399 data = tools.read_file(self.TestFile("key.key"))
7400 self._MakeInputFile("key.key", data)
7401 data = tools.read_file(self.TestFile("key.pem"))
7402 self._MakeInputFile("key.crt", data)
7403
7404 data = self._DoReadFile('314_capsule_signed_ver.dts')
7405
7406 self._CheckCapsule(data, signed_capsule=True, version_check=True)
7407
7408 def testCapsuleGenCapOemFlags(self):
7409 """Test generation of EFI capsule with OEM Flags set"""
7410 data = self._DoReadFile('315_capsule_oemflags.dts')
7411
7412 self._CheckCapsule(data, capoemflags=True)
7413
7414 def testCapsuleGenKeyMissing(self):
7415 """Test that binman errors out on missing key"""
7416 with self.assertRaises(ValueError) as e:
7417 self._DoReadFile('316_capsule_missing_key.dts')
7418
7419 self.assertIn("Both private key and public key certificate need to be provided",
7420 str(e.exception))
7421
7422 def testCapsuleGenIndexMissing(self):
7423 """Test that binman errors out on missing image index"""
7424 with self.assertRaises(ValueError) as e:
7425 self._DoReadFile('317_capsule_missing_index.dts')
7426
7427 self.assertIn("entry is missing properties: image-index",
7428 str(e.exception))
7429
7430 def testCapsuleGenGuidMissing(self):
7431 """Test that binman errors out on missing image GUID"""
7432 with self.assertRaises(ValueError) as e:
7433 self._DoReadFile('318_capsule_missing_guid.dts')
7434
7435 self.assertIn("entry is missing properties: image-guid",
7436 str(e.exception))
7437
Sughosh Ganu6b2d18a2023-10-10 14:40:59 +05307438 def testCapsuleGenAcceptCapsule(self):
7439 """Test generationg of accept EFI capsule"""
7440 data = self._DoReadFile('319_capsule_accept.dts')
7441
7442 self._CheckEmptyCapsule(data, accept_capsule=True)
7443
7444 def testCapsuleGenRevertCapsule(self):
7445 """Test generationg of revert EFI capsule"""
7446 data = self._DoReadFile('320_capsule_revert.dts')
7447
7448 self._CheckEmptyCapsule(data)
7449
7450 def testCapsuleGenAcceptGuidMissing(self):
7451 """Test that binman errors out on missing image GUID for accept capsule"""
7452 with self.assertRaises(ValueError) as e:
7453 self._DoReadFile('321_capsule_accept_missing_guid.dts')
7454
7455 self.assertIn("Image GUID needed for generating accept capsule",
7456 str(e.exception))
7457
7458 def testCapsuleGenEmptyCapsuleTypeMissing(self):
7459 """Test that capsule-type is specified"""
7460 with self.assertRaises(ValueError) as e:
7461 self._DoReadFile('322_empty_capsule_type_missing.dts')
7462
7463 self.assertIn("entry is missing properties: capsule-type",
7464 str(e.exception))
7465
7466 def testCapsuleGenAcceptOrRevertMissing(self):
7467 """Test that both accept and revert capsule are not specified"""
7468 with self.assertRaises(ValueError) as e:
7469 self._DoReadFile('323_capsule_accept_revert_missing.dts')
7470
Simon Glassa360b8f2024-06-23 11:55:06 -06007471 def test_assume_size(self):
7472 """Test handling of the assume-size property for external blob"""
7473 with self.assertRaises(ValueError) as e:
7474 self._DoTestFile('326_assume_size.dts', allow_missing=True,
7475 allow_fake_blobs=True)
7476 self.assertIn("contents size 0xa (10) exceeds section size 0x9 (9)",
7477 str(e.exception))
7478
7479 def test_assume_size_ok(self):
7480 """Test handling of the assume-size where it fits OK"""
7481 with test_util.capture_sys_output() as (stdout, stderr):
7482 self._DoTestFile('327_assume_size_ok.dts', allow_missing=True,
7483 allow_fake_blobs=True)
7484 err = stderr.getvalue()
7485 self.assertRegex(
7486 err,
7487 "Image '.*' has faked external blobs and is non-functional: .*")
7488
7489 def test_assume_size_no_fake(self):
7490 """Test handling of the assume-size where it fits OK"""
7491 with test_util.capture_sys_output() as (stdout, stderr):
7492 self._DoTestFile('327_assume_size_ok.dts', allow_missing=True)
7493 err = stderr.getvalue()
7494 self.assertRegex(
7495 err,
7496 "Image '.*' is missing external blobs and is non-functional: .*")
7497
Simon Glass5f7aadf2024-07-20 11:49:47 +01007498 def SetupAlternateDts(self):
7499 """Compile the .dts test files for alternative-fdt
7500
7501 Returns:
7502 tuple:
7503 str: Test directory created
7504 list of str: '.bin' files which we expect Binman to create
7505 """
7506 testdir = TestFunctional._MakeInputDir('dtb')
7507 dtb_list = []
7508 for fname in glob.glob(f'{self.TestFile("alt_dts")}/*.dts'):
7509 tmp_fname = fdt_util.EnsureCompiled(fname, testdir)
7510 base = os.path.splitext(os.path.basename(fname))[0]
7511 dtb_list.append(base + '.bin')
7512 shutil.move(tmp_fname, os.path.join(testdir, base + '.dtb'))
7513
7514 return testdir, dtb_list
7515
Simon Glassf3598922024-07-20 11:49:45 +01007516 def CheckAlternates(self, dts, phase, xpl_data):
7517 """Run the test for the alterative-fdt etype
7518
7519 Args:
7520 dts (str): Devicetree file to process
7521 phase (str): Phase to process ('spl', 'tpl' or 'vpl')
7522 xpl_data (bytes): Expected data for the phase's binary
7523
7524 Returns:
7525 dict of .dtb files produced
7526 key: str filename
7527 value: Fdt object
7528 """
Simon Glass5f7aadf2024-07-20 11:49:47 +01007529 dtb_list = self.SetupAlternateDts()[1]
Simon Glassf3598922024-07-20 11:49:45 +01007530
7531 entry_args = {
7532 f'{phase}-dtb': '1',
7533 f'{phase}-bss-pad': 'y',
7534 'of-spl-remove-props': 'prop-to-remove another-prop-to-get-rid-of',
7535 }
7536 data = self._DoReadFileDtb(dts, use_real_dtb=True, update_dtb=True,
7537 use_expanded=True, entry_args=entry_args)[0]
7538 self.assertEqual(xpl_data, data[:len(xpl_data)])
7539 rest = data[len(xpl_data):]
7540 pad_len = 10
7541 self.assertEqual(tools.get_bytes(0, pad_len), rest[:pad_len])
7542
7543 # Check the dtb is using the test file
7544 dtb_data = rest[pad_len:]
7545 dtb = fdt.Fdt.FromData(dtb_data)
7546 dtb.Scan()
7547 fdt_size = dtb.GetFdtObj().totalsize()
7548 self.assertEqual('model-not-set',
7549 fdt_util.GetString(dtb.GetRoot(), 'compatible'))
7550
7551 pad_len = 10
7552
7553 # Check the other output files
7554 dtbs = {}
7555 for fname in dtb_list:
7556 pathname = tools.get_output_filename(fname)
7557 self.assertTrue(os.path.exists(pathname))
7558
7559 data = tools.read_file(pathname)
7560 self.assertEqual(xpl_data, data[:len(xpl_data)])
7561 rest = data[len(xpl_data):]
7562
7563 self.assertEqual(tools.get_bytes(0, pad_len), rest[:pad_len])
7564 rest = rest[pad_len:]
7565
7566 dtb = fdt.Fdt.FromData(rest)
7567 dtb.Scan()
7568 dtbs[fname] = dtb
7569
7570 expected = 'one' if '1' in fname else 'two'
7571 self.assertEqual(f'u-boot,model-{expected}',
7572 fdt_util.GetString(dtb.GetRoot(), 'compatible'))
7573
7574 # Make sure the FDT is the same size as the 'main' one
7575 rest = rest[fdt_size:]
7576
7577 self.assertEqual(b'', rest)
7578 return dtbs
7579
7580 def testAlternatesFdt(self):
7581 """Test handling of alternates-fdt etype"""
7582 self._SetupTplElf()
7583 dtbs = self.CheckAlternates('328_alternates_fdt.dts', 'tpl',
7584 U_BOOT_TPL_NODTB_DATA)
7585 for dtb in dtbs.values():
7586 # Check for the node with the tag
7587 node = dtb.GetNode('/node')
7588 self.assertIsNotNone(node)
7589 self.assertEqual(5, len(node.props.keys()))
7590
7591 # Make sure the other node is still there
7592 self.assertIsNotNone(dtb.GetNode('/node/other-node'))
7593
7594 def testAlternatesFdtgrep(self):
7595 """Test handling of alternates-fdt etype using fdtgrep"""
7596 self._SetupTplElf()
7597 dtbs = self.CheckAlternates('329_alternates_fdtgrep.dts', 'tpl',
7598 U_BOOT_TPL_NODTB_DATA)
7599 for dtb in dtbs.values():
7600 # Check for the node with the tag
7601 node = dtb.GetNode('/node')
7602 self.assertIsNotNone(node)
7603 self.assertEqual({'some-prop', 'not-a-prop-to-remove'},
7604 node.props.keys())
7605
7606 # Make sure the other node is gone
7607 self.assertIsNone(dtb.GetNode('/node/other-node'))
7608
7609 def testAlternatesFdtgrepVpl(self):
7610 """Test handling of alternates-fdt etype using fdtgrep with vpl"""
7611 self._SetupVplElf()
7612 dtbs = self.CheckAlternates('330_alternates_vpl.dts', 'vpl',
7613 U_BOOT_VPL_NODTB_DATA)
7614
7615 def testAlternatesFdtgrepSpl(self):
7616 """Test handling of alternates-fdt etype using fdtgrep with spl"""
7617 self._SetupSplElf()
7618 dtbs = self.CheckAlternates('331_alternates_spl.dts', 'spl',
7619 U_BOOT_SPL_NODTB_DATA)
7620
7621 def testAlternatesFdtgrepInval(self):
7622 """Test alternates-fdt etype using fdtgrep with invalid phase"""
7623 self._SetupSplElf()
7624 with self.assertRaises(ValueError) as e:
7625 dtbs = self.CheckAlternates('332_alternates_inval.dts', 'spl',
7626 U_BOOT_SPL_NODTB_DATA)
7627 self.assertIn("Invalid U-Boot phase 'bad-phase': Use tpl/vpl/spl",
7628 str(e.exception))
7629
Simon Glasscd2783e2024-07-20 11:49:46 +01007630 def testFitFdtListDir(self):
7631 """Test an image with an FIT with FDT images using fit,fdt-list-dir"""
Simon Glass1bba8942024-08-26 13:11:34 -06007632 old_dir = os.getcwd()
7633 try:
7634 os.chdir(self._indir)
7635 self.CheckFitFdt('333_fit_fdt_dir.dts', False)
7636 finally:
7637 os.chdir(old_dir)
Simon Glasscd2783e2024-07-20 11:49:46 +01007638
Simon Glassd2a9d6e2024-08-26 13:11:37 -06007639 def testFitFdtListDirDefault(self):
7640 """Test an FIT fit,fdt-list-dir where the default DT in is a subdir"""
7641 old_dir = os.getcwd()
7642 try:
7643 os.chdir(self._indir)
7644 self.CheckFitFdt('333_fit_fdt_dir.dts', False,
7645 default_dt='rockchip/test-fdt2')
7646 finally:
7647 os.chdir(old_dir)
7648
Simon Glass5f7aadf2024-07-20 11:49:47 +01007649 def testFitFdtCompat(self):
7650 """Test an image with an FIT with compatible in the config nodes"""
7651 entry_args = {
7652 'of-list': 'model1 model2',
7653 'default-dt': 'model2',
7654 }
7655 testdir, dtb_list = self.SetupAlternateDts()
7656 data = self._DoReadFileDtb(
7657 '334_fit_fdt_compat.dts', use_real_dtb=True, update_dtb=True,
7658 entry_args=entry_args, extra_indirs=[testdir])[0]
7659
7660 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
7661
7662 fit = fdt.Fdt.FromData(fit_data)
7663 fit.Scan()
7664
7665 cnode = fit.GetNode('/configurations')
7666 self.assertIn('default', cnode.props)
7667 self.assertEqual('config-2', cnode.props['default'].value)
7668
7669 for seq in range(1, 2):
7670 name = f'config-{seq}'
7671 fnode = fit.GetNode('/configurations/%s' % name)
7672 self.assertIsNotNone(fnode)
7673 self.assertIn('compatible', fnode.props.keys())
7674 expected = 'one' if seq == 1 else 'two'
7675 self.assertEqual(f'u-boot,model-{expected}',
7676 fnode.props['compatible'].value)
7677
Simon Glassa04b9942024-07-20 11:49:48 +01007678 def testFitFdtPhase(self):
7679 """Test an image with an FIT with fdt-phase in the fdt nodes"""
7680 phase = 'tpl'
7681 entry_args = {
7682 f'{phase}-dtb': '1',
7683 f'{phase}-bss-pad': 'y',
7684 'of-spl-remove-props': 'prop-to-remove another-prop-to-get-rid-of',
7685 'of-list': 'model1 model2',
7686 'default-dt': 'model2',
7687 }
7688 testdir, dtb_list = self.SetupAlternateDts()
7689 data = self._DoReadFileDtb(
7690 '335_fit_fdt_phase.dts', use_real_dtb=True, update_dtb=True,
7691 entry_args=entry_args, extra_indirs=[testdir])[0]
7692 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
7693 fit = fdt.Fdt.FromData(fit_data)
7694 fit.Scan()
7695
7696 # Check that each FDT has only the expected properties for the phase
7697 for seq in range(1, 2):
7698 fnode = fit.GetNode(f'/images/fdt-{seq}')
7699 self.assertIsNotNone(fnode)
7700 dtb = fdt.Fdt.FromData(fnode.props['data'].bytes)
7701 dtb.Scan()
7702
7703 # Make sure that the 'bootph-pre-sram' tag in /node protects it from
7704 # removal
7705 node = dtb.GetNode('/node')
7706 self.assertIsNotNone(node)
7707 self.assertEqual({'some-prop', 'not-a-prop-to-remove'},
7708 node.props.keys())
7709
7710 # Make sure the other node is gone
7711 self.assertIsNone(dtb.GetNode('/node/other-node'))
7712
Simon Glassb553e8a2024-08-26 13:11:29 -06007713 def testMkeficapsuleMissing(self):
7714 """Test that binman complains if mkeficapsule is missing"""
7715 with self.assertRaises(ValueError) as e:
7716 self._DoTestFile('311_capsule.dts',
7717 force_missing_bintools='mkeficapsule')
7718 self.assertIn("Node '/binman/efi-capsule': Missing tool: 'mkeficapsule'",
7719 str(e.exception))
7720
7721 def testMkeficapsuleMissingOk(self):
7722 """Test that binman deals with mkeficapsule being missing"""
7723 with test_util.capture_sys_output() as (stdout, stderr):
7724 ret = self._DoTestFile('311_capsule.dts',
7725 force_missing_bintools='mkeficapsule',
7726 allow_missing=True)
7727 self.assertEqual(103, ret)
7728 err = stderr.getvalue()
7729 self.assertRegex(err, "Image 'image'.*missing bintools.*: mkeficapsule")
7730
Simon Glassa360b8f2024-06-23 11:55:06 -06007731
Simon Glassac599912017-11-12 21:52:22 -07007732if __name__ == "__main__":
7733 unittest.main()