blob: 8e419645a6d564b13e457fca692916eecedaacf3 [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001# SPDX-License-Identifier: GPL-2.0+
Simon Glass4f443042016-11-25 20:15:52 -07002# Copyright (c) 2016 Google, Inc
3# Written by Simon Glass <sjg@chromium.org>
4#
Simon Glass4f443042016-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 Glassfdc34362020-07-09 18:39:45 -06009import collections
Simon Glass16287932020-04-17 18:09:03 -060010import gzip
Simon Glasse0e5df92018-09-14 04:57:31 -060011import hashlib
Simon Glass4f443042016-11-25 20:15:52 -070012from optparse import OptionParser
13import os
Simon Glassfdc34362020-07-09 18:39:45 -060014import re
Simon Glass4f443042016-11-25 20:15:52 -070015import shutil
16import struct
17import sys
18import tempfile
19import unittest
Simon Glass56ee85e2022-01-09 20:13:57 -070020import unittest.mock
21import urllib.error
Simon Glass4f443042016-11-25 20:15:52 -070022
Simon Glass386c63c2022-01-09 20:13:50 -070023from binman import bintool
Simon Glass16287932020-04-17 18:09:03 -060024from binman import cbfs_util
25from binman import cmdline
26from binman import control
27from binman import elf
28from binman import elf_test
Simon Glass75989722021-11-23 21:08:59 -070029from binman import fip_util
Simon Glass16287932020-04-17 18:09:03 -060030from binman import fmap_util
Simon Glass16287932020-04-17 18:09:03 -060031from binman import state
32from dtoc import fdt
33from dtoc import fdt_util
34from binman.etype import fdtmap
35from binman.etype import image_header
Simon Glass07237982020-08-05 13:27:47 -060036from binman.image import Image
Simon Glass4583c002023-02-23 18:18:04 -070037from u_boot_pylib import command
38from u_boot_pylib import test_util
39from u_boot_pylib import tools
40from u_boot_pylib import tout
Simon Glass4f443042016-11-25 20:15:52 -070041
42# Contents of test files, corresponding to different entry types
Simon Glassc6c10e72019-05-17 22:00:46 -060043U_BOOT_DATA = b'1234'
44U_BOOT_IMG_DATA = b'img'
Alper Nebi Yasak367ecbf2022-06-18 15:13:11 +030045U_BOOT_SPL_DATA = b'56780123456789abcdefghijklm'
46U_BOOT_TPL_DATA = b'tpl9876543210fedcbazywvuts'
Simon Glass6ad24522022-02-28 07:16:54 -070047U_BOOT_VPL_DATA = b'vpl76543210fedcbazywxyz_'
Simon Glassc6c10e72019-05-17 22:00:46 -060048BLOB_DATA = b'89'
49ME_DATA = b'0abcd'
50VGA_DATA = b'vga'
Sughosh Ganub6176112023-08-22 23:09:59 +053051EFI_CAPSULE_DATA = b'efi'
Simon Glassc6c10e72019-05-17 22:00:46 -060052U_BOOT_DTB_DATA = b'udtb'
53U_BOOT_SPL_DTB_DATA = b'spldtb'
54U_BOOT_TPL_DTB_DATA = b'tpldtb'
Simon Glass6ad24522022-02-28 07:16:54 -070055U_BOOT_VPL_DTB_DATA = b'vpldtb'
Simon Glassc6c10e72019-05-17 22:00:46 -060056X86_START16_DATA = b'start16'
57X86_START16_SPL_DATA = b'start16spl'
58X86_START16_TPL_DATA = b'start16tpl'
Simon Glass2250ee62019-08-24 07:22:48 -060059X86_RESET16_DATA = b'reset16'
60X86_RESET16_SPL_DATA = b'reset16spl'
61X86_RESET16_TPL_DATA = b'reset16tpl'
Simon Glassc6c10e72019-05-17 22:00:46 -060062PPC_MPC85XX_BR_DATA = b'ppcmpc85xxbr'
63U_BOOT_NODTB_DATA = b'nodtb with microcode pointer somewhere in here'
64U_BOOT_SPL_NODTB_DATA = b'splnodtb with microcode pointer somewhere in here'
65U_BOOT_TPL_NODTB_DATA = b'tplnodtb with microcode pointer somewhere in here'
Simon Glass6ad24522022-02-28 07:16:54 -070066U_BOOT_VPL_NODTB_DATA = b'vplnodtb'
Alper Nebi Yasak21353312022-02-08 01:08:04 +030067U_BOOT_EXP_DATA = U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA
68U_BOOT_SPL_EXP_DATA = U_BOOT_SPL_NODTB_DATA + U_BOOT_SPL_DTB_DATA
69U_BOOT_TPL_EXP_DATA = U_BOOT_TPL_NODTB_DATA + U_BOOT_TPL_DTB_DATA
Simon Glassc6c10e72019-05-17 22:00:46 -060070FSP_DATA = b'fsp'
71CMC_DATA = b'cmc'
72VBT_DATA = b'vbt'
73MRC_DATA = b'mrc'
Simon Glassbb748372018-07-17 13:25:33 -060074TEXT_DATA = 'text'
75TEXT_DATA2 = 'text2'
76TEXT_DATA3 = 'text3'
Simon Glassc6c10e72019-05-17 22:00:46 -060077CROS_EC_RW_DATA = b'ecrw'
78GBB_DATA = b'gbbd'
79BMPBLK_DATA = b'bmp'
80VBLOCK_DATA = b'vblk'
81FILES_DATA = (b"sorry I'm late\nOh, don't bother apologising, I'm " +
82 b"sorry you're alive\n")
Simon Glassff5c7e32019-07-08 13:18:42 -060083COMPRESS_DATA = b'compress xxxxxxxxxxxxxxxxxxxxxx data'
Simon Glass8f5ef892020-10-26 17:40:25 -060084COMPRESS_DATA_BIG = COMPRESS_DATA * 2
Simon Glassc6c10e72019-05-17 22:00:46 -060085REFCODE_DATA = b'refcode'
Simon Glassea0fff92019-08-24 07:23:07 -060086FSP_M_DATA = b'fsp_m'
Simon Glassbc6a88f2019-10-20 21:31:35 -060087FSP_S_DATA = b'fsp_s'
Simon Glass998d1482019-10-20 21:31:36 -060088FSP_T_DATA = b'fsp_t'
Simon Glassdc2f81a2020-09-01 05:13:58 -060089ATF_BL31_DATA = b'bl31'
Roger Quadros47f420a2022-02-19 20:50:04 +020090TEE_OS_DATA = b'this is some tee OS data'
Simon Glass75989722021-11-23 21:08:59 -070091ATF_BL2U_DATA = b'bl2u'
Bin Meng4c4d6072021-05-10 20:23:33 +080092OPENSBI_DATA = b'opensbi'
Samuel Holland18bd4552020-10-21 21:12:15 -050093SCP_DATA = b'scp'
Jonas Karlman05b978b2023-02-25 19:01:33 +000094ROCKCHIP_TPL_DATA = b'rockchip-tpl'
Simon Glass6cf99532020-09-01 05:13:59 -060095TEST_FDT1_DATA = b'fdt1'
96TEST_FDT2_DATA = b'test-fdt2'
Simon Glassfb91d562020-09-06 10:35:33 -060097ENV_DATA = b'var1=1\nvar2="2"'
Christian Taedcke289e6002023-07-17 09:05:54 +020098ENCRYPTED_IV_DATA = b'123456'
99ENCRYPTED_KEY_DATA = b'abcde'
Philippe Reynesb1c50932022-03-28 22:57:04 +0200100PRE_LOAD_MAGIC = b'UBSH'
101PRE_LOAD_VERSION = 0x11223344.to_bytes(4, 'big')
102PRE_LOAD_HDR_SIZE = 0x00001000.to_bytes(4, 'big')
Neha Malcom Francis6c66ccf2023-07-22 00:14:24 +0530103TI_BOARD_CONFIG_DATA = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
Neha Malcom Francis78144822023-07-22 00:14:25 +0530104TI_UNSECURE_DATA = b'unsecuredata'
Simon Glass6cf99532020-09-01 05:13:59 -0600105
106# Subdirectory of the input dir to use to put test FDTs
107TEST_FDT_SUBDIR = 'fdts'
Simon Glassec127af2018-07-17 13:25:39 -0600108
Simon Glass6ccbfcd2019-07-20 12:23:47 -0600109# The expected size for the device tree in some tests
Simon Glassf667e452019-07-08 14:25:50 -0600110EXTRACT_DTB_SIZE = 0x3c9
111
Simon Glass6ccbfcd2019-07-20 12:23:47 -0600112# Properties expected to be in the device tree when update_dtb is used
113BASE_DTB_PROPS = ['offset', 'size', 'image-pos']
114
Simon Glass12bb1a92019-07-20 12:23:51 -0600115# Extra properties expected to be in the device tree when allow-repack is used
116REPACK_DTB_PROPS = ['orig-offset', 'orig-size']
117
Stefan Herbrechtsmeiercbe2e752022-08-19 16:25:28 +0200118# Supported compression bintools
Stefan Herbrechtsmeiercd15b642022-08-19 16:25:38 +0200119COMP_BINTOOLS = ['bzip2', 'gzip', 'lz4', 'lzma_alone', 'lzop', 'xz', 'zstd']
Simon Glass4f443042016-11-25 20:15:52 -0700120
Simon Glass2f80c5e2023-01-07 14:07:14 -0700121TEE_ADDR = 0x5678
122
Sughosh Ganub6176112023-08-22 23:09:59 +0530123# Firmware Management Protocol(FMP) GUID
124FW_MGMT_GUID = 'edd5cb6d2de8444cbda17194199ad92a'
125# Image GUID specified in the DTS
126CAPSULE_IMAGE_GUID = '52cfd7092007104791d108469b7fe9c8'
127
Simon Glass4f443042016-11-25 20:15:52 -0700128class TestFunctional(unittest.TestCase):
129 """Functional tests for binman
130
131 Most of these use a sample .dts file to build an image and then check
132 that it looks correct. The sample files are in the test/ subdirectory
133 and are numbered.
134
135 For each entry type a very small test file is created using fixed
136 string contents. This makes it easy to test that things look right, and
137 debug problems.
138
139 In some cases a 'real' file must be used - these are also supplied in
140 the test/ diurectory.
141 """
142 @classmethod
Simon Glassb986b3b2019-08-24 07:22:43 -0600143 def setUpClass(cls):
Simon Glass4d5994f2017-11-12 21:52:20 -0700144 global entry
Simon Glass16287932020-04-17 18:09:03 -0600145 from binman import entry
Simon Glass4d5994f2017-11-12 21:52:20 -0700146
Simon Glass4f443042016-11-25 20:15:52 -0700147 # Handle the case where argv[0] is 'python'
Simon Glassb986b3b2019-08-24 07:22:43 -0600148 cls._binman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
149 cls._binman_pathname = os.path.join(cls._binman_dir, 'binman')
Simon Glass4f443042016-11-25 20:15:52 -0700150
151 # Create a temporary directory for input files
Simon Glassb986b3b2019-08-24 07:22:43 -0600152 cls._indir = tempfile.mkdtemp(prefix='binmant.')
Simon Glass4f443042016-11-25 20:15:52 -0700153
154 # Create some test files
155 TestFunctional._MakeInputFile('u-boot.bin', U_BOOT_DATA)
156 TestFunctional._MakeInputFile('u-boot.img', U_BOOT_IMG_DATA)
157 TestFunctional._MakeInputFile('spl/u-boot-spl.bin', U_BOOT_SPL_DATA)
Simon Glassb8ef5b62018-07-17 13:25:48 -0600158 TestFunctional._MakeInputFile('tpl/u-boot-tpl.bin', U_BOOT_TPL_DATA)
Simon Glass6ad24522022-02-28 07:16:54 -0700159 TestFunctional._MakeInputFile('vpl/u-boot-vpl.bin', U_BOOT_VPL_DATA)
Simon Glass4f443042016-11-25 20:15:52 -0700160 TestFunctional._MakeInputFile('blobfile', BLOB_DATA)
Simon Glasse0ff8552016-11-25 20:15:53 -0700161 TestFunctional._MakeInputFile('me.bin', ME_DATA)
162 TestFunctional._MakeInputFile('vga.bin', VGA_DATA)
Simon Glassb986b3b2019-08-24 07:22:43 -0600163 cls._ResetDtbs()
Simon Glass2250ee62019-08-24 07:22:48 -0600164
Jagdish Gediya9d368f32018-09-03 21:35:08 +0530165 TestFunctional._MakeInputFile('u-boot-br.bin', PPC_MPC85XX_BR_DATA)
Simon Glass2250ee62019-08-24 07:22:48 -0600166
Simon Glass5e239182019-08-24 07:22:49 -0600167 TestFunctional._MakeInputFile('u-boot-x86-start16.bin', X86_START16_DATA)
168 TestFunctional._MakeInputFile('spl/u-boot-x86-start16-spl.bin',
Simon Glass87722132017-11-12 21:52:26 -0700169 X86_START16_SPL_DATA)
Simon Glass5e239182019-08-24 07:22:49 -0600170 TestFunctional._MakeInputFile('tpl/u-boot-x86-start16-tpl.bin',
Simon Glass35b384c2018-09-14 04:57:10 -0600171 X86_START16_TPL_DATA)
Simon Glass2250ee62019-08-24 07:22:48 -0600172
173 TestFunctional._MakeInputFile('u-boot-x86-reset16.bin',
174 X86_RESET16_DATA)
175 TestFunctional._MakeInputFile('spl/u-boot-x86-reset16-spl.bin',
176 X86_RESET16_SPL_DATA)
177 TestFunctional._MakeInputFile('tpl/u-boot-x86-reset16-tpl.bin',
178 X86_RESET16_TPL_DATA)
179
Simon Glass4f443042016-11-25 20:15:52 -0700180 TestFunctional._MakeInputFile('u-boot-nodtb.bin', U_BOOT_NODTB_DATA)
Simon Glass6b187df2017-11-12 21:52:27 -0700181 TestFunctional._MakeInputFile('spl/u-boot-spl-nodtb.bin',
182 U_BOOT_SPL_NODTB_DATA)
Simon Glassf0253632018-09-14 04:57:32 -0600183 TestFunctional._MakeInputFile('tpl/u-boot-tpl-nodtb.bin',
184 U_BOOT_TPL_NODTB_DATA)
Simon Glass6ad24522022-02-28 07:16:54 -0700185 TestFunctional._MakeInputFile('vpl/u-boot-vpl-nodtb.bin',
186 U_BOOT_VPL_NODTB_DATA)
Simon Glassda229092016-11-25 20:15:56 -0700187 TestFunctional._MakeInputFile('fsp.bin', FSP_DATA)
188 TestFunctional._MakeInputFile('cmc.bin', CMC_DATA)
Bin Meng59ea8c22017-08-15 22:41:54 -0700189 TestFunctional._MakeInputFile('vbt.bin', VBT_DATA)
Simon Glassca4f4ff2017-11-12 21:52:28 -0700190 TestFunctional._MakeInputFile('mrc.bin', MRC_DATA)
Simon Glassec127af2018-07-17 13:25:39 -0600191 TestFunctional._MakeInputFile('ecrw.bin', CROS_EC_RW_DATA)
Simon Glass0ef87aa2018-07-17 13:25:44 -0600192 TestFunctional._MakeInputDir('devkeys')
193 TestFunctional._MakeInputFile('bmpblk.bin', BMPBLK_DATA)
Simon Glass3ae192c2018-10-01 12:22:31 -0600194 TestFunctional._MakeInputFile('refcode.bin', REFCODE_DATA)
Simon Glassea0fff92019-08-24 07:23:07 -0600195 TestFunctional._MakeInputFile('fsp_m.bin', FSP_M_DATA)
Simon Glassbc6a88f2019-10-20 21:31:35 -0600196 TestFunctional._MakeInputFile('fsp_s.bin', FSP_S_DATA)
Simon Glass998d1482019-10-20 21:31:36 -0600197 TestFunctional._MakeInputFile('fsp_t.bin', FSP_T_DATA)
Simon Glass4f443042016-11-25 20:15:52 -0700198
Simon Glass53e22bf2019-08-24 07:22:53 -0600199 cls._elf_testdir = os.path.join(cls._indir, 'elftest')
200 elf_test.BuildElfTestFiles(cls._elf_testdir)
201
Simon Glasse0ff8552016-11-25 20:15:53 -0700202 # ELF file with a '_dt_ucode_base_size' symbol
Simon Glassf514d8f2019-08-24 07:22:54 -0600203 TestFunctional._MakeInputFile('u-boot',
Simon Glassc1aa66e2022-01-29 14:14:04 -0700204 tools.read_file(cls.ElfTestFile('u_boot_ucode_ptr')))
Simon Glasse0ff8552016-11-25 20:15:53 -0700205
206 # Intel flash descriptor file
Simon Glass0ba4b3d2020-07-09 18:39:41 -0600207 cls._SetupDescriptor()
Simon Glasse0ff8552016-11-25 20:15:53 -0700208
Simon Glassb986b3b2019-08-24 07:22:43 -0600209 shutil.copytree(cls.TestFile('files'),
210 os.path.join(cls._indir, 'files'))
Simon Glass0a98b282018-09-14 04:57:28 -0600211
Neha Malcom Francis6c66ccf2023-07-22 00:14:24 +0530212 shutil.copytree(cls.TestFile('yaml'),
213 os.path.join(cls._indir, 'yaml'))
214
Simon Glass83d73c22018-09-14 04:57:26 -0600215 TestFunctional._MakeInputFile('compress', COMPRESS_DATA)
Simon Glass8f5ef892020-10-26 17:40:25 -0600216 TestFunctional._MakeInputFile('compress_big', COMPRESS_DATA_BIG)
Simon Glassdc2f81a2020-09-01 05:13:58 -0600217 TestFunctional._MakeInputFile('bl31.bin', ATF_BL31_DATA)
Roger Quadros47f420a2022-02-19 20:50:04 +0200218 TestFunctional._MakeInputFile('tee-pager.bin', TEE_OS_DATA)
Simon Glass75989722021-11-23 21:08:59 -0700219 TestFunctional._MakeInputFile('bl2u.bin', ATF_BL2U_DATA)
Bin Meng4c4d6072021-05-10 20:23:33 +0800220 TestFunctional._MakeInputFile('fw_dynamic.bin', OPENSBI_DATA)
Samuel Holland18bd4552020-10-21 21:12:15 -0500221 TestFunctional._MakeInputFile('scp.bin', SCP_DATA)
Jonas Karlman05b978b2023-02-25 19:01:33 +0000222 TestFunctional._MakeInputFile('rockchip-tpl.bin', ROCKCHIP_TPL_DATA)
Neha Malcom Francis78144822023-07-22 00:14:25 +0530223 TestFunctional._MakeInputFile('ti_unsecure.bin', TI_UNSECURE_DATA)
Sughosh Ganub6176112023-08-22 23:09:59 +0530224 TestFunctional._MakeInputFile('capsule_input.bin', EFI_CAPSULE_DATA)
Simon Glass83d73c22018-09-14 04:57:26 -0600225
Simon Glass6cf99532020-09-01 05:13:59 -0600226 # Add a few .dtb files for testing
227 TestFunctional._MakeInputFile('%s/test-fdt1.dtb' % TEST_FDT_SUBDIR,
228 TEST_FDT1_DATA)
229 TestFunctional._MakeInputFile('%s/test-fdt2.dtb' % TEST_FDT_SUBDIR,
230 TEST_FDT2_DATA)
231
Simon Glassfb91d562020-09-06 10:35:33 -0600232 TestFunctional._MakeInputFile('env.txt', ENV_DATA)
233
Simon Glass40c8bdd2022-03-05 20:19:12 -0700234 # ELF file with two sections in different parts of memory, used for both
235 # ATF and OP_TEE
236 TestFunctional._MakeInputFile('bl31.elf',
237 tools.read_file(cls.ElfTestFile('elf_sections')))
238 TestFunctional._MakeInputFile('tee.elf',
239 tools.read_file(cls.ElfTestFile('elf_sections')))
240
Simon Glass2f80c5e2023-01-07 14:07:14 -0700241 # Newer OP_TEE file in v1 binary format
242 cls.make_tee_bin('tee.bin')
243
Christian Taedcke289e6002023-07-17 09:05:54 +0200244 # test files for encrypted tests
245 TestFunctional._MakeInputFile('encrypted-file.iv', ENCRYPTED_IV_DATA)
246 TestFunctional._MakeInputFile('encrypted-file.key', ENCRYPTED_KEY_DATA)
247
Stefan Herbrechtsmeiercbe2e752022-08-19 16:25:28 +0200248 cls.comp_bintools = {}
249 for name in COMP_BINTOOLS:
250 cls.comp_bintools[name] = bintool.Bintool.create(name)
Simon Glassac62fba2019-07-08 13:18:53 -0600251
Simon Glass4f443042016-11-25 20:15:52 -0700252 @classmethod
Simon Glassb986b3b2019-08-24 07:22:43 -0600253 def tearDownClass(cls):
Simon Glass4f443042016-11-25 20:15:52 -0700254 """Remove the temporary input directory and its contents"""
Simon Glassb986b3b2019-08-24 07:22:43 -0600255 if cls.preserve_indir:
256 print('Preserving input dir: %s' % cls._indir)
Simon Glassd5164a72019-07-08 13:18:49 -0600257 else:
Simon Glassb986b3b2019-08-24 07:22:43 -0600258 if cls._indir:
259 shutil.rmtree(cls._indir)
260 cls._indir = None
Simon Glass4f443042016-11-25 20:15:52 -0700261
Simon Glassd5164a72019-07-08 13:18:49 -0600262 @classmethod
Simon Glass8acce602019-07-08 13:18:50 -0600263 def setup_test_args(cls, preserve_indir=False, preserve_outdirs=False,
Simon Glass53cd5d92019-07-08 14:25:29 -0600264 toolpath=None, verbosity=None):
Simon Glassd5164a72019-07-08 13:18:49 -0600265 """Accept arguments controlling test execution
266
267 Args:
268 preserve_indir: Preserve the shared input directory used by all
269 tests in this class.
270 preserve_outdir: Preserve the output directories used by tests. Each
271 test has its own, so this is normally only useful when running a
272 single test.
Simon Glass8acce602019-07-08 13:18:50 -0600273 toolpath: ist of paths to use for tools
Simon Glassd5164a72019-07-08 13:18:49 -0600274 """
275 cls.preserve_indir = preserve_indir
276 cls.preserve_outdirs = preserve_outdirs
Simon Glass8acce602019-07-08 13:18:50 -0600277 cls.toolpath = toolpath
Simon Glass53cd5d92019-07-08 14:25:29 -0600278 cls.verbosity = verbosity
Simon Glassd5164a72019-07-08 13:18:49 -0600279
Stefan Herbrechtsmeiercbe2e752022-08-19 16:25:28 +0200280 def _CheckBintool(self, bintool):
281 if not bintool.is_present():
282 self.skipTest('%s not available' % bintool.name)
283
Simon Glassac62fba2019-07-08 13:18:53 -0600284 def _CheckLz4(self):
Stefan Herbrechtsmeiercbe2e752022-08-19 16:25:28 +0200285 bintool = self.comp_bintools['lz4']
286 self._CheckBintool(bintool)
Simon Glassac62fba2019-07-08 13:18:53 -0600287
Simon Glassbf574f12019-07-20 12:24:09 -0600288 def _CleanupOutputDir(self):
289 """Remove the temporary output directory"""
290 if self.preserve_outdirs:
291 print('Preserving output dir: %s' % tools.outdir)
292 else:
Simon Glassc1aa66e2022-01-29 14:14:04 -0700293 tools._finalise_for_test()
Simon Glassbf574f12019-07-20 12:24:09 -0600294
Simon Glass4f443042016-11-25 20:15:52 -0700295 def setUp(self):
296 # Enable this to turn on debugging output
Simon Glassf3385a52022-01-29 14:14:15 -0700297 # tout.init(tout.DEBUG)
Simon Glass4f443042016-11-25 20:15:52 -0700298 command.test_result = None
299
300 def tearDown(self):
301 """Remove the temporary output directory"""
Simon Glassbf574f12019-07-20 12:24:09 -0600302 self._CleanupOutputDir()
Simon Glass4f443042016-11-25 20:15:52 -0700303
Simon Glassf86a7362019-07-20 12:24:10 -0600304 def _SetupImageInTmpdir(self):
305 """Set up the output image in a new temporary directory
306
307 This is used when an image has been generated in the output directory,
308 but we want to run binman again. This will create a new output
309 directory and fail to delete the original one.
310
311 This creates a new temporary directory, copies the image to it (with a
312 new name) and removes the old output directory.
313
314 Returns:
315 Tuple:
316 Temporary directory to use
317 New image filename
318 """
Simon Glassc1aa66e2022-01-29 14:14:04 -0700319 image_fname = tools.get_output_filename('image.bin')
Simon Glassf86a7362019-07-20 12:24:10 -0600320 tmpdir = tempfile.mkdtemp(prefix='binman.')
321 updated_fname = os.path.join(tmpdir, 'image-updated.bin')
Simon Glassc1aa66e2022-01-29 14:14:04 -0700322 tools.write_file(updated_fname, tools.read_file(image_fname))
Simon Glassf86a7362019-07-20 12:24:10 -0600323 self._CleanupOutputDir()
324 return tmpdir, updated_fname
325
Simon Glassb8ef5b62018-07-17 13:25:48 -0600326 @classmethod
Simon Glassb986b3b2019-08-24 07:22:43 -0600327 def _ResetDtbs(cls):
Simon Glassb8ef5b62018-07-17 13:25:48 -0600328 TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA)
329 TestFunctional._MakeInputFile('spl/u-boot-spl.dtb', U_BOOT_SPL_DTB_DATA)
330 TestFunctional._MakeInputFile('tpl/u-boot-tpl.dtb', U_BOOT_TPL_DTB_DATA)
Simon Glass6ad24522022-02-28 07:16:54 -0700331 TestFunctional._MakeInputFile('vpl/u-boot-vpl.dtb', U_BOOT_VPL_DTB_DATA)
Simon Glassb8ef5b62018-07-17 13:25:48 -0600332
Simon Glass4f443042016-11-25 20:15:52 -0700333 def _RunBinman(self, *args, **kwargs):
334 """Run binman using the command line
335
336 Args:
337 Arguments to pass, as a list of strings
338 kwargs: Arguments to pass to Command.RunPipe()
339 """
Simon Glassd9800692022-01-29 14:14:05 -0700340 result = command.run_pipe([[self._binman_pathname] + list(args)],
Simon Glass4f443042016-11-25 20:15:52 -0700341 capture=True, capture_stderr=True, raise_on_error=False)
342 if result.return_code and kwargs.get('raise_on_error', True):
343 raise Exception("Error running '%s': %s" % (' '.join(args),
344 result.stdout + result.stderr))
345 return result
346
Simon Glass53cd5d92019-07-08 14:25:29 -0600347 def _DoBinman(self, *argv):
Simon Glass4f443042016-11-25 20:15:52 -0700348 """Run binman using directly (in the same process)
349
350 Args:
351 Arguments to pass, as a list of strings
352 Returns:
353 Return value (0 for success)
354 """
Simon Glass53cd5d92019-07-08 14:25:29 -0600355 argv = list(argv)
356 args = cmdline.ParseArgs(argv)
357 args.pager = 'binman-invalid-pager'
358 args.build_dir = self._indir
Simon Glass4f443042016-11-25 20:15:52 -0700359
360 # For testing, you can force an increase in verbosity here
Simon Glass53cd5d92019-07-08 14:25:29 -0600361 # args.verbosity = tout.DEBUG
362 return control.Binman(args)
Simon Glass4f443042016-11-25 20:15:52 -0700363
Simon Glass53af22a2018-07-17 13:25:32 -0600364 def _DoTestFile(self, fname, debug=False, map=False, update_dtb=False,
Simon Glasseb833d82019-04-25 21:58:34 -0600365 entry_args=None, images=None, use_real_dtb=False,
Simon Glass63aeaeb2021-03-18 20:25:05 +1300366 use_expanded=False, verbosity=None, allow_missing=False,
Heiko Thierya89c8f22022-01-06 11:49:41 +0100367 allow_fake_blobs=False, extra_indirs=None, threads=None,
Simon Glass4f9ee832022-01-09 20:14:09 -0700368 test_section_timeout=False, update_fdt_in_elf=None,
Andrew Davis15432ea2023-07-22 00:14:44 +0530369 force_missing_bintools='', ignore_missing=False, output_dir=None):
Simon Glass4f443042016-11-25 20:15:52 -0700370 """Run binman with a given test file
371
372 Args:
Simon Glass741f2d62018-10-01 12:22:30 -0600373 fname: Device-tree source filename to use (e.g. 005_simple.dts)
Simon Glass7ae5f312018-06-01 09:38:19 -0600374 debug: True to enable debugging output
Simon Glass3b0c3822018-06-01 09:38:20 -0600375 map: True to output map files for the images
Simon Glass3ab95982018-08-01 15:22:37 -0600376 update_dtb: Update the offset and size of each entry in the device
Simon Glass16b8d6b2018-07-06 10:27:42 -0600377 tree before packing it into the image
Simon Glass0bfa7b02018-09-14 04:57:12 -0600378 entry_args: Dict of entry args to supply to binman
379 key: arg name
380 value: value of that arg
381 images: List of image names to build
Simon Glasse9d336d2020-09-01 05:13:55 -0600382 use_real_dtb: True to use the test file as the contents of
383 the u-boot-dtb entry. Normally this is not needed and the
384 test contents (the U_BOOT_DTB_DATA string) can be used.
385 But in some test we need the real contents.
Simon Glass63aeaeb2021-03-18 20:25:05 +1300386 use_expanded: True to use expanded entries where available, e.g.
387 'u-boot-expanded' instead of 'u-boot'
Simon Glasse9d336d2020-09-01 05:13:55 -0600388 verbosity: Verbosity level to use (0-3, None=don't set it)
389 allow_missing: Set the '--allow-missing' flag so that missing
390 external binaries just produce a warning instead of an error
Heiko Thierya89c8f22022-01-06 11:49:41 +0100391 allow_fake_blobs: Set the '--fake-ext-blobs' flag
Simon Glass6cf99532020-09-01 05:13:59 -0600392 extra_indirs: Extra input directories to add using -I
Simon Glassc69d19c2021-07-06 10:36:37 -0600393 threads: Number of threads to use (None for default, 0 for
394 single-threaded)
Simon Glass7115f002021-11-03 21:09:17 -0600395 test_section_timeout: True to force the first time to timeout, as
396 used in testThreadTimeout()
Simon Glass0427bed2021-11-03 21:09:18 -0600397 update_fdt_in_elf: Value to pass with --update-fdt-in-elf=xxx
Simon Glass4f9ee832022-01-09 20:14:09 -0700398 force_missing_tools (str): comma-separated list of bintools to
399 regard as missing
Andrew Davis15432ea2023-07-22 00:14:44 +0530400 output_dir: Specific output directory to use for image using -O
Simon Glass7115f002021-11-03 21:09:17 -0600401
402 Returns:
403 int return code, 0 on success
Simon Glass4f443042016-11-25 20:15:52 -0700404 """
Simon Glass53cd5d92019-07-08 14:25:29 -0600405 args = []
Simon Glass7fe91732017-11-13 18:55:00 -0700406 if debug:
407 args.append('-D')
Simon Glass53cd5d92019-07-08 14:25:29 -0600408 if verbosity is not None:
409 args.append('-v%d' % verbosity)
410 elif self.verbosity:
411 args.append('-v%d' % self.verbosity)
412 if self.toolpath:
413 for path in self.toolpath:
414 args += ['--toolpath', path]
Simon Glassc69d19c2021-07-06 10:36:37 -0600415 if threads is not None:
416 args.append('-T%d' % threads)
417 if test_section_timeout:
418 args.append('--test-section-timeout')
Simon Glass53cd5d92019-07-08 14:25:29 -0600419 args += ['build', '-p', '-I', self._indir, '-d', self.TestFile(fname)]
Simon Glass3b0c3822018-06-01 09:38:20 -0600420 if map:
421 args.append('-m')
Simon Glass16b8d6b2018-07-06 10:27:42 -0600422 if update_dtb:
Simon Glass2569e102019-07-08 13:18:47 -0600423 args.append('-u')
Simon Glass93d17412018-09-14 04:57:23 -0600424 if not use_real_dtb:
425 args.append('--fake-dtb')
Simon Glass63aeaeb2021-03-18 20:25:05 +1300426 if not use_expanded:
427 args.append('--no-expanded')
Simon Glass53af22a2018-07-17 13:25:32 -0600428 if entry_args:
Simon Glass50979152019-05-14 15:53:41 -0600429 for arg, value in entry_args.items():
Simon Glass53af22a2018-07-17 13:25:32 -0600430 args.append('-a%s=%s' % (arg, value))
Simon Glass4f9f1052020-07-09 18:39:38 -0600431 if allow_missing:
432 args.append('-M')
Simon Glassb38da152022-11-09 19:14:42 -0700433 if ignore_missing:
434 args.append('-W')
Heiko Thierya89c8f22022-01-06 11:49:41 +0100435 if allow_fake_blobs:
436 args.append('--fake-ext-blobs')
Simon Glass4f9ee832022-01-09 20:14:09 -0700437 if force_missing_bintools:
438 args += ['--force-missing-bintools', force_missing_bintools]
Simon Glass0427bed2021-11-03 21:09:18 -0600439 if update_fdt_in_elf:
440 args += ['--update-fdt-in-elf', update_fdt_in_elf]
Simon Glass0bfa7b02018-09-14 04:57:12 -0600441 if images:
442 for image in images:
443 args += ['-i', image]
Simon Glass6cf99532020-09-01 05:13:59 -0600444 if extra_indirs:
445 for indir in extra_indirs:
446 args += ['-I', indir]
Andrew Davis15432ea2023-07-22 00:14:44 +0530447 if output_dir:
448 args += ['-O', output_dir]
Simon Glass7fe91732017-11-13 18:55:00 -0700449 return self._DoBinman(*args)
Simon Glass4f443042016-11-25 20:15:52 -0700450
451 def _SetupDtb(self, fname, outfile='u-boot.dtb'):
Simon Glasse0ff8552016-11-25 20:15:53 -0700452 """Set up a new test device-tree file
453
454 The given file is compiled and set up as the device tree to be used
455 for ths test.
456
457 Args:
458 fname: Filename of .dts file to read
Simon Glass7ae5f312018-06-01 09:38:19 -0600459 outfile: Output filename for compiled device-tree binary
Simon Glasse0ff8552016-11-25 20:15:53 -0700460
461 Returns:
Simon Glass7ae5f312018-06-01 09:38:19 -0600462 Contents of device-tree binary
Simon Glasse0ff8552016-11-25 20:15:53 -0700463 """
Simon Glassa004f292019-07-20 12:23:49 -0600464 tmpdir = tempfile.mkdtemp(prefix='binmant.')
465 dtb = fdt_util.EnsureCompiled(self.TestFile(fname), tmpdir)
Simon Glass1d0ebf72019-05-14 15:53:42 -0600466 with open(dtb, 'rb') as fd:
Simon Glass4f443042016-11-25 20:15:52 -0700467 data = fd.read()
468 TestFunctional._MakeInputFile(outfile, data)
Simon Glassa004f292019-07-20 12:23:49 -0600469 shutil.rmtree(tmpdir)
Simon Glasse0e62752018-10-01 21:12:41 -0600470 return data
Simon Glass4f443042016-11-25 20:15:52 -0700471
Simon Glass6ad24522022-02-28 07:16:54 -0700472 def _GetDtbContentsForSpls(self, dtb_data, name):
473 """Create a version of the main DTB for SPL / TPL / VPL
Simon Glass6ed45ba2018-09-14 04:57:24 -0600474
475 For testing we don't actually have different versions of the DTB. With
476 U-Boot we normally run fdtgrep to remove unwanted nodes, but for tests
477 we don't normally have any unwanted nodes.
478
479 We still want the DTBs for SPL and TPL to be different though, since
480 otherwise it is confusing to know which one we are looking at. So add
481 an 'spl' or 'tpl' property to the top-level node.
Simon Glasse9d336d2020-09-01 05:13:55 -0600482
483 Args:
484 dtb_data: dtb data to modify (this should be a value devicetree)
485 name: Name of a new property to add
486
487 Returns:
488 New dtb data with the property added
Simon Glass6ed45ba2018-09-14 04:57:24 -0600489 """
490 dtb = fdt.Fdt.FromData(dtb_data)
491 dtb.Scan()
492 dtb.GetNode('/binman').AddZeroProp(name)
493 dtb.Sync(auto_resize=True)
494 dtb.Pack()
495 return dtb.GetContents()
496
Simon Glass63aeaeb2021-03-18 20:25:05 +1300497 def _DoReadFileDtb(self, fname, use_real_dtb=False, use_expanded=False,
498 map=False, update_dtb=False, entry_args=None,
Simon Glassc69d19c2021-07-06 10:36:37 -0600499 reset_dtbs=True, extra_indirs=None, threads=None):
Simon Glass4f443042016-11-25 20:15:52 -0700500 """Run binman and return the resulting image
501
502 This runs binman with a given test file and then reads the resulting
503 output file. It is a shortcut function since most tests need to do
504 these steps.
505
506 Raises an assertion failure if binman returns a non-zero exit code.
507
508 Args:
Simon Glass741f2d62018-10-01 12:22:30 -0600509 fname: Device-tree source filename to use (e.g. 005_simple.dts)
Simon Glass4f443042016-11-25 20:15:52 -0700510 use_real_dtb: True to use the test file as the contents of
511 the u-boot-dtb entry. Normally this is not needed and the
512 test contents (the U_BOOT_DTB_DATA string) can be used.
513 But in some test we need the real contents.
Simon Glass63aeaeb2021-03-18 20:25:05 +1300514 use_expanded: True to use expanded entries where available, e.g.
515 'u-boot-expanded' instead of 'u-boot'
Simon Glass3b0c3822018-06-01 09:38:20 -0600516 map: True to output map files for the images
Simon Glass3ab95982018-08-01 15:22:37 -0600517 update_dtb: Update the offset and size of each entry in the device
Simon Glass16b8d6b2018-07-06 10:27:42 -0600518 tree before packing it into the image
Simon Glasse9d336d2020-09-01 05:13:55 -0600519 entry_args: Dict of entry args to supply to binman
520 key: arg name
521 value: value of that arg
522 reset_dtbs: With use_real_dtb the test dtb is overwritten by this
523 function. If reset_dtbs is True, then the original test dtb
524 is written back before this function finishes
Simon Glass6cf99532020-09-01 05:13:59 -0600525 extra_indirs: Extra input directories to add using -I
Simon Glassc69d19c2021-07-06 10:36:37 -0600526 threads: Number of threads to use (None for default, 0 for
527 single-threaded)
Simon Glasse0ff8552016-11-25 20:15:53 -0700528
529 Returns:
530 Tuple:
531 Resulting image contents
532 Device tree contents
Simon Glass3b0c3822018-06-01 09:38:20 -0600533 Map data showing contents of image (or None if none)
Simon Glassea6922e2018-07-17 13:25:27 -0600534 Output device tree binary filename ('u-boot.dtb' path)
Simon Glass4f443042016-11-25 20:15:52 -0700535 """
Simon Glasse0ff8552016-11-25 20:15:53 -0700536 dtb_data = None
Simon Glass4f443042016-11-25 20:15:52 -0700537 # Use the compiled test file as the u-boot-dtb input
538 if use_real_dtb:
Simon Glasse0ff8552016-11-25 20:15:53 -0700539 dtb_data = self._SetupDtb(fname)
Simon Glass6ed45ba2018-09-14 04:57:24 -0600540
541 # For testing purposes, make a copy of the DT for SPL and TPL. Add
542 # a node indicating which it is, so aid verification.
Simon Glass6ad24522022-02-28 07:16:54 -0700543 for name in ['spl', 'tpl', 'vpl']:
Simon Glass6ed45ba2018-09-14 04:57:24 -0600544 dtb_fname = '%s/u-boot-%s.dtb' % (name, name)
545 outfile = os.path.join(self._indir, dtb_fname)
546 TestFunctional._MakeInputFile(dtb_fname,
Simon Glass6ad24522022-02-28 07:16:54 -0700547 self._GetDtbContentsForSpls(dtb_data, name))
Simon Glass4f443042016-11-25 20:15:52 -0700548
549 try:
Simon Glass53af22a2018-07-17 13:25:32 -0600550 retcode = self._DoTestFile(fname, map=map, update_dtb=update_dtb,
Simon Glass6cf99532020-09-01 05:13:59 -0600551 entry_args=entry_args, use_real_dtb=use_real_dtb,
Simon Glassc69d19c2021-07-06 10:36:37 -0600552 use_expanded=use_expanded, extra_indirs=extra_indirs,
553 threads=threads)
Simon Glass4f443042016-11-25 20:15:52 -0700554 self.assertEqual(0, retcode)
Simon Glassc1aa66e2022-01-29 14:14:04 -0700555 out_dtb_fname = tools.get_output_filename('u-boot.dtb.out')
Simon Glass4f443042016-11-25 20:15:52 -0700556
557 # Find the (only) image, read it and return its contents
558 image = control.images['image']
Simon Glassc1aa66e2022-01-29 14:14:04 -0700559 image_fname = tools.get_output_filename('image.bin')
Simon Glass16b8d6b2018-07-06 10:27:42 -0600560 self.assertTrue(os.path.exists(image_fname))
Simon Glass3b0c3822018-06-01 09:38:20 -0600561 if map:
Simon Glassc1aa66e2022-01-29 14:14:04 -0700562 map_fname = tools.get_output_filename('image.map')
Simon Glass3b0c3822018-06-01 09:38:20 -0600563 with open(map_fname) as fd:
564 map_data = fd.read()
565 else:
566 map_data = None
Simon Glass1d0ebf72019-05-14 15:53:42 -0600567 with open(image_fname, 'rb') as fd:
Simon Glass16b8d6b2018-07-06 10:27:42 -0600568 return fd.read(), dtb_data, map_data, out_dtb_fname
Simon Glass4f443042016-11-25 20:15:52 -0700569 finally:
570 # Put the test file back
Simon Glass6ed45ba2018-09-14 04:57:24 -0600571 if reset_dtbs and use_real_dtb:
Simon Glassb8ef5b62018-07-17 13:25:48 -0600572 self._ResetDtbs()
Simon Glass4f443042016-11-25 20:15:52 -0700573
Simon Glass3c081312019-07-08 14:25:26 -0600574 def _DoReadFileRealDtb(self, fname):
575 """Run binman with a real .dtb file and return the resulting data
576
577 Args:
578 fname: DT source filename to use (e.g. 082_fdt_update_all.dts)
579
580 Returns:
581 Resulting image contents
582 """
583 return self._DoReadFileDtb(fname, use_real_dtb=True, update_dtb=True)[0]
584
Simon Glasse0ff8552016-11-25 20:15:53 -0700585 def _DoReadFile(self, fname, use_real_dtb=False):
Simon Glass7ae5f312018-06-01 09:38:19 -0600586 """Helper function which discards the device-tree binary
587
588 Args:
Simon Glass741f2d62018-10-01 12:22:30 -0600589 fname: Device-tree source filename to use (e.g. 005_simple.dts)
Simon Glass7ae5f312018-06-01 09:38:19 -0600590 use_real_dtb: True to use the test file as the contents of
591 the u-boot-dtb entry. Normally this is not needed and the
592 test contents (the U_BOOT_DTB_DATA string) can be used.
593 But in some test we need the real contents.
Simon Glassea6922e2018-07-17 13:25:27 -0600594
595 Returns:
596 Resulting image contents
Simon Glass7ae5f312018-06-01 09:38:19 -0600597 """
Simon Glasse0ff8552016-11-25 20:15:53 -0700598 return self._DoReadFileDtb(fname, use_real_dtb)[0]
599
Simon Glass4f443042016-11-25 20:15:52 -0700600 @classmethod
Simon Glassb986b3b2019-08-24 07:22:43 -0600601 def _MakeInputFile(cls, fname, contents):
Simon Glass4f443042016-11-25 20:15:52 -0700602 """Create a new test input file, creating directories as needed
603
604 Args:
Simon Glass3ab95982018-08-01 15:22:37 -0600605 fname: Filename to create
Simon Glass4f443042016-11-25 20:15:52 -0700606 contents: File contents to write in to the file
607 Returns:
608 Full pathname of file created
609 """
Simon Glassb986b3b2019-08-24 07:22:43 -0600610 pathname = os.path.join(cls._indir, fname)
Simon Glass4f443042016-11-25 20:15:52 -0700611 dirname = os.path.dirname(pathname)
612 if dirname and not os.path.exists(dirname):
613 os.makedirs(dirname)
614 with open(pathname, 'wb') as fd:
615 fd.write(contents)
616 return pathname
617
618 @classmethod
Simon Glassb986b3b2019-08-24 07:22:43 -0600619 def _MakeInputDir(cls, dirname):
Simon Glass0ef87aa2018-07-17 13:25:44 -0600620 """Create a new test input directory, creating directories as needed
621
622 Args:
623 dirname: Directory name to create
624
625 Returns:
626 Full pathname of directory created
627 """
Simon Glassb986b3b2019-08-24 07:22:43 -0600628 pathname = os.path.join(cls._indir, dirname)
Simon Glass0ef87aa2018-07-17 13:25:44 -0600629 if not os.path.exists(pathname):
630 os.makedirs(pathname)
631 return pathname
632
633 @classmethod
Simon Glassb986b3b2019-08-24 07:22:43 -0600634 def _SetupSplElf(cls, src_fname='bss_data'):
Simon Glass11ae93e2018-10-01 21:12:47 -0600635 """Set up an ELF file with a '_dt_ucode_base_size' symbol
636
637 Args:
638 Filename of ELF file to use as SPL
639 """
Simon Glassc9a0b272019-08-24 07:22:59 -0600640 TestFunctional._MakeInputFile('spl/u-boot-spl',
Simon Glassc1aa66e2022-01-29 14:14:04 -0700641 tools.read_file(cls.ElfTestFile(src_fname)))
Simon Glass11ae93e2018-10-01 21:12:47 -0600642
643 @classmethod
Simon Glass2090f1e2019-08-24 07:23:00 -0600644 def _SetupTplElf(cls, src_fname='bss_data'):
645 """Set up an ELF file with a '_dt_ucode_base_size' symbol
646
647 Args:
648 Filename of ELF file to use as TPL
649 """
650 TestFunctional._MakeInputFile('tpl/u-boot-tpl',
Simon Glassc1aa66e2022-01-29 14:14:04 -0700651 tools.read_file(cls.ElfTestFile(src_fname)))
Simon Glass2090f1e2019-08-24 07:23:00 -0600652
653 @classmethod
Simon Glass6ad24522022-02-28 07:16:54 -0700654 def _SetupVplElf(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 VPL
659 """
660 TestFunctional._MakeInputFile('vpl/u-boot-vpl',
661 tools.read_file(cls.ElfTestFile(src_fname)))
662
663 @classmethod
Lukas Funke8c1fbd12023-07-18 13:53:13 +0200664 def _SetupPmuFwlElf(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('pmu-firmware.elf',
671 tools.read_file(cls.ElfTestFile(src_fname)))
672
673 @classmethod
Simon Glass0ba4b3d2020-07-09 18:39:41 -0600674 def _SetupDescriptor(cls):
675 with open(cls.TestFile('descriptor.bin'), 'rb') as fd:
676 TestFunctional._MakeInputFile('descriptor.bin', fd.read())
677
678 @classmethod
Simon Glassb986b3b2019-08-24 07:22:43 -0600679 def TestFile(cls, fname):
680 return os.path.join(cls._binman_dir, 'test', fname)
Simon Glass4f443042016-11-25 20:15:52 -0700681
Simon Glass53e22bf2019-08-24 07:22:53 -0600682 @classmethod
683 def ElfTestFile(cls, fname):
684 return os.path.join(cls._elf_testdir, fname)
685
Simon Glass2f80c5e2023-01-07 14:07:14 -0700686 @classmethod
687 def make_tee_bin(cls, fname, paged_sz=0, extra_data=b''):
688 init_sz, start_hi, start_lo, dummy = (len(U_BOOT_DATA), 0, TEE_ADDR, 0)
689 data = b'OPTE\x01xxx' + struct.pack('<5I', init_sz, start_hi, start_lo,
690 dummy, paged_sz) + U_BOOT_DATA
691 data += extra_data
692 TestFunctional._MakeInputFile(fname, data)
693
Simon Glass4f443042016-11-25 20:15:52 -0700694 def AssertInList(self, grep_list, target):
695 """Assert that at least one of a list of things is in a target
696
697 Args:
698 grep_list: List of strings to check
699 target: Target string
700 """
701 for grep in grep_list:
702 if grep in target:
703 return
Simon Glass1fc62de2019-05-17 22:00:50 -0600704 self.fail("Error: '%s' not found in '%s'" % (grep_list, target))
Simon Glass4f443042016-11-25 20:15:52 -0700705
706 def CheckNoGaps(self, entries):
707 """Check that all entries fit together without gaps
708
709 Args:
710 entries: List of entries to check
711 """
Simon Glass3ab95982018-08-01 15:22:37 -0600712 offset = 0
Simon Glass4f443042016-11-25 20:15:52 -0700713 for entry in entries.values():
Simon Glass3ab95982018-08-01 15:22:37 -0600714 self.assertEqual(offset, entry.offset)
715 offset += entry.size
Simon Glass4f443042016-11-25 20:15:52 -0700716
Simon Glasse0ff8552016-11-25 20:15:53 -0700717 def GetFdtLen(self, dtb):
Simon Glass7ae5f312018-06-01 09:38:19 -0600718 """Get the totalsize field from a device-tree binary
Simon Glasse0ff8552016-11-25 20:15:53 -0700719
720 Args:
Simon Glass7ae5f312018-06-01 09:38:19 -0600721 dtb: Device-tree binary contents
Simon Glasse0ff8552016-11-25 20:15:53 -0700722
723 Returns:
Simon Glass7ae5f312018-06-01 09:38:19 -0600724 Total size of device-tree binary, from the header
Simon Glasse0ff8552016-11-25 20:15:53 -0700725 """
726 return struct.unpack('>L', dtb[4:8])[0]
727
Simon Glass086cec92019-07-08 14:25:27 -0600728 def _GetPropTree(self, dtb, prop_names, prefix='/binman/'):
Simon Glass16b8d6b2018-07-06 10:27:42 -0600729 def AddNode(node, path):
730 if node.name != '/':
731 path += '/' + node.name
Simon Glass086cec92019-07-08 14:25:27 -0600732 for prop in node.props.values():
733 if prop.name in prop_names:
734 prop_path = path + ':' + prop.name
735 tree[prop_path[len(prefix):]] = fdt_util.fdt32_to_cpu(
736 prop.value)
Simon Glass16b8d6b2018-07-06 10:27:42 -0600737 for subnode in node.subnodes:
Simon Glass16b8d6b2018-07-06 10:27:42 -0600738 AddNode(subnode, path)
739
740 tree = {}
Simon Glass16b8d6b2018-07-06 10:27:42 -0600741 AddNode(dtb.GetRoot(), '')
742 return tree
743
Ivan Mikhaylov5b34efe2023-03-08 01:13:40 +0000744 def _CheckSign(self, fit, key):
745 try:
746 tools.run('fit_check_sign', '-k', key, '-f', fit)
747 except:
748 self.fail('Expected signed FIT container')
749 return False
750 return True
751
Simon Glass4f443042016-11-25 20:15:52 -0700752 def testRun(self):
753 """Test a basic run with valid args"""
754 result = self._RunBinman('-h')
755
756 def testFullHelp(self):
757 """Test that the full help is displayed with -H"""
758 result = self._RunBinman('-H')
Simon Glass61adb2d2021-03-18 20:25:13 +1300759 help_file = os.path.join(self._binman_dir, 'README.rst')
Tom Rini3759df02018-01-16 15:29:50 -0500760 # Remove possible extraneous strings
761 extra = '::::::::::::::\n' + help_file + '\n::::::::::::::\n'
762 gothelp = result.stdout.replace(extra, '')
763 self.assertEqual(len(gothelp), os.path.getsize(help_file))
Simon Glass4f443042016-11-25 20:15:52 -0700764 self.assertEqual(0, len(result.stderr))
765 self.assertEqual(0, result.return_code)
766
767 def testFullHelpInternal(self):
768 """Test that the full help is displayed with -H"""
769 try:
770 command.test_result = command.CommandResult()
771 result = self._DoBinman('-H')
Simon Glass61adb2d2021-03-18 20:25:13 +1300772 help_file = os.path.join(self._binman_dir, 'README.rst')
Simon Glass4f443042016-11-25 20:15:52 -0700773 finally:
774 command.test_result = None
775
776 def testHelp(self):
777 """Test that the basic help is displayed with -h"""
778 result = self._RunBinman('-h')
779 self.assertTrue(len(result.stdout) > 200)
780 self.assertEqual(0, len(result.stderr))
781 self.assertEqual(0, result.return_code)
782
Simon Glass4f443042016-11-25 20:15:52 -0700783 def testBoard(self):
784 """Test that we can run it with a specific board"""
Simon Glass741f2d62018-10-01 12:22:30 -0600785 self._SetupDtb('005_simple.dts', 'sandbox/u-boot.dtb')
Simon Glass4f443042016-11-25 20:15:52 -0700786 TestFunctional._MakeInputFile('sandbox/u-boot.bin', U_BOOT_DATA)
Simon Glass63aeaeb2021-03-18 20:25:05 +1300787 result = self._DoBinman('build', '-n', '-b', 'sandbox')
Simon Glass4f443042016-11-25 20:15:52 -0700788 self.assertEqual(0, result)
789
790 def testNeedBoard(self):
791 """Test that we get an error when no board ius supplied"""
792 with self.assertRaises(ValueError) as e:
Simon Glass53cd5d92019-07-08 14:25:29 -0600793 result = self._DoBinman('build')
Simon Glass4f443042016-11-25 20:15:52 -0700794 self.assertIn("Must provide a board to process (use -b <board>)",
795 str(e.exception))
796
797 def testMissingDt(self):
Simon Glass7ae5f312018-06-01 09:38:19 -0600798 """Test that an invalid device-tree file generates an error"""
Simon Glass4f443042016-11-25 20:15:52 -0700799 with self.assertRaises(Exception) as e:
Simon Glass53cd5d92019-07-08 14:25:29 -0600800 self._RunBinman('build', '-d', 'missing_file')
Simon Glass4f443042016-11-25 20:15:52 -0700801 # We get one error from libfdt, and a different one from fdtget.
802 self.AssertInList(["Couldn't open blob from 'missing_file'",
803 'No such file or directory'], str(e.exception))
804
805 def testBrokenDt(self):
Simon Glass7ae5f312018-06-01 09:38:19 -0600806 """Test that an invalid device-tree source file generates an error
Simon Glass4f443042016-11-25 20:15:52 -0700807
808 Since this is a source file it should be compiled and the error
809 will come from the device-tree compiler (dtc).
810 """
811 with self.assertRaises(Exception) as e:
Simon Glass53cd5d92019-07-08 14:25:29 -0600812 self._RunBinman('build', '-d', self.TestFile('001_invalid.dts'))
Simon Glass4f443042016-11-25 20:15:52 -0700813 self.assertIn("FATAL ERROR: Unable to parse input tree",
814 str(e.exception))
815
816 def testMissingNode(self):
817 """Test that a device tree without a 'binman' node generates an error"""
818 with self.assertRaises(Exception) as e:
Simon Glass53cd5d92019-07-08 14:25:29 -0600819 self._DoBinman('build', '-d', self.TestFile('002_missing_node.dts'))
Simon Glass4f443042016-11-25 20:15:52 -0700820 self.assertIn("does not have a 'binman' node", str(e.exception))
821
822 def testEmpty(self):
823 """Test that an empty binman node works OK (i.e. does nothing)"""
Simon Glass53cd5d92019-07-08 14:25:29 -0600824 result = self._RunBinman('build', '-d', self.TestFile('003_empty.dts'))
Simon Glass4f443042016-11-25 20:15:52 -0700825 self.assertEqual(0, len(result.stderr))
826 self.assertEqual(0, result.return_code)
827
828 def testInvalidEntry(self):
829 """Test that an invalid entry is flagged"""
830 with self.assertRaises(Exception) as e:
Simon Glass53cd5d92019-07-08 14:25:29 -0600831 result = self._RunBinman('build', '-d',
Simon Glass741f2d62018-10-01 12:22:30 -0600832 self.TestFile('004_invalid_entry.dts'))
Simon Glass4f443042016-11-25 20:15:52 -0700833 self.assertIn("Unknown entry type 'not-a-valid-type' in node "
834 "'/binman/not-a-valid-type'", str(e.exception))
835
836 def testSimple(self):
837 """Test a simple binman with a single file"""
Simon Glass741f2d62018-10-01 12:22:30 -0600838 data = self._DoReadFile('005_simple.dts')
Simon Glass4f443042016-11-25 20:15:52 -0700839 self.assertEqual(U_BOOT_DATA, data)
840
Simon Glass7fe91732017-11-13 18:55:00 -0700841 def testSimpleDebug(self):
842 """Test a simple binman run with debugging enabled"""
Simon Glasse2705fa2019-07-08 14:25:53 -0600843 self._DoTestFile('005_simple.dts', debug=True)
Simon Glass7fe91732017-11-13 18:55:00 -0700844
Simon Glass4f443042016-11-25 20:15:52 -0700845 def testDual(self):
846 """Test that we can handle creating two images
847
848 This also tests image padding.
849 """
Simon Glass741f2d62018-10-01 12:22:30 -0600850 retcode = self._DoTestFile('006_dual_image.dts')
Simon Glass4f443042016-11-25 20:15:52 -0700851 self.assertEqual(0, retcode)
852
853 image = control.images['image1']
Simon Glass8beb11e2019-07-08 14:25:47 -0600854 self.assertEqual(len(U_BOOT_DATA), image.size)
Simon Glassc1aa66e2022-01-29 14:14:04 -0700855 fname = tools.get_output_filename('image1.bin')
Simon Glass4f443042016-11-25 20:15:52 -0700856 self.assertTrue(os.path.exists(fname))
Simon Glass1d0ebf72019-05-14 15:53:42 -0600857 with open(fname, 'rb') as fd:
Simon Glass4f443042016-11-25 20:15:52 -0700858 data = fd.read()
859 self.assertEqual(U_BOOT_DATA, data)
860
861 image = control.images['image2']
Simon Glass8beb11e2019-07-08 14:25:47 -0600862 self.assertEqual(3 + len(U_BOOT_DATA) + 5, image.size)
Simon Glassc1aa66e2022-01-29 14:14:04 -0700863 fname = tools.get_output_filename('image2.bin')
Simon Glass4f443042016-11-25 20:15:52 -0700864 self.assertTrue(os.path.exists(fname))
Simon Glass1d0ebf72019-05-14 15:53:42 -0600865 with open(fname, 'rb') as fd:
Simon Glass4f443042016-11-25 20:15:52 -0700866 data = fd.read()
867 self.assertEqual(U_BOOT_DATA, data[3:7])
Simon Glassc1aa66e2022-01-29 14:14:04 -0700868 self.assertEqual(tools.get_bytes(0, 3), data[:3])
869 self.assertEqual(tools.get_bytes(0, 5), data[7:])
Simon Glass4f443042016-11-25 20:15:52 -0700870
871 def testBadAlign(self):
872 """Test that an invalid alignment value is detected"""
873 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -0600874 self._DoTestFile('007_bad_align.dts')
Simon Glass4f443042016-11-25 20:15:52 -0700875 self.assertIn("Node '/binman/u-boot': Alignment 23 must be a power "
876 "of two", str(e.exception))
877
878 def testPackSimple(self):
879 """Test that packing works as expected"""
Simon Glass741f2d62018-10-01 12:22:30 -0600880 retcode = self._DoTestFile('008_pack.dts')
Simon Glass4f443042016-11-25 20:15:52 -0700881 self.assertEqual(0, retcode)
882 self.assertIn('image', control.images)
883 image = control.images['image']
Simon Glass8f1da502018-06-01 09:38:12 -0600884 entries = image.GetEntries()
Simon Glass4f443042016-11-25 20:15:52 -0700885 self.assertEqual(5, len(entries))
886
887 # First u-boot
888 self.assertIn('u-boot', entries)
889 entry = entries['u-boot']
Simon Glass3ab95982018-08-01 15:22:37 -0600890 self.assertEqual(0, entry.offset)
Simon Glass4f443042016-11-25 20:15:52 -0700891 self.assertEqual(len(U_BOOT_DATA), entry.size)
892
893 # Second u-boot, aligned to 16-byte boundary
894 self.assertIn('u-boot-align', entries)
895 entry = entries['u-boot-align']
Simon Glass3ab95982018-08-01 15:22:37 -0600896 self.assertEqual(16, entry.offset)
Simon Glass4f443042016-11-25 20:15:52 -0700897 self.assertEqual(len(U_BOOT_DATA), entry.size)
898
899 # Third u-boot, size 23 bytes
900 self.assertIn('u-boot-size', entries)
901 entry = entries['u-boot-size']
Simon Glass3ab95982018-08-01 15:22:37 -0600902 self.assertEqual(20, entry.offset)
Simon Glass4f443042016-11-25 20:15:52 -0700903 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
904 self.assertEqual(23, entry.size)
905
906 # Fourth u-boot, placed immediate after the above
907 self.assertIn('u-boot-next', entries)
908 entry = entries['u-boot-next']
Simon Glass3ab95982018-08-01 15:22:37 -0600909 self.assertEqual(43, entry.offset)
Simon Glass4f443042016-11-25 20:15:52 -0700910 self.assertEqual(len(U_BOOT_DATA), entry.size)
911
Simon Glass3ab95982018-08-01 15:22:37 -0600912 # Fifth u-boot, placed at a fixed offset
Simon Glass4f443042016-11-25 20:15:52 -0700913 self.assertIn('u-boot-fixed', entries)
914 entry = entries['u-boot-fixed']
Simon Glass3ab95982018-08-01 15:22:37 -0600915 self.assertEqual(61, entry.offset)
Simon Glass4f443042016-11-25 20:15:52 -0700916 self.assertEqual(len(U_BOOT_DATA), entry.size)
917
Simon Glass8beb11e2019-07-08 14:25:47 -0600918 self.assertEqual(65, image.size)
Simon Glass4f443042016-11-25 20:15:52 -0700919
920 def testPackExtra(self):
921 """Test that extra packing feature works as expected"""
Simon Glass4eec34c2020-10-26 17:40:10 -0600922 data, _, _, out_dtb_fname = self._DoReadFileDtb('009_pack_extra.dts',
923 update_dtb=True)
Simon Glass4f443042016-11-25 20:15:52 -0700924
Simon Glass4f443042016-11-25 20:15:52 -0700925 self.assertIn('image', control.images)
926 image = control.images['image']
Simon Glass8f1da502018-06-01 09:38:12 -0600927 entries = image.GetEntries()
Samuel Hollandb01ae032023-01-21 17:25:16 -0600928 self.assertEqual(6, len(entries))
Simon Glass4f443042016-11-25 20:15:52 -0700929
Samuel Hollandb01ae032023-01-21 17:25:16 -0600930 # First u-boot with padding before and after (included in minimum size)
Simon Glass4f443042016-11-25 20:15:52 -0700931 self.assertIn('u-boot', entries)
932 entry = entries['u-boot']
Simon Glass3ab95982018-08-01 15:22:37 -0600933 self.assertEqual(0, entry.offset)
Simon Glass4f443042016-11-25 20:15:52 -0700934 self.assertEqual(3, entry.pad_before)
935 self.assertEqual(3 + 5 + len(U_BOOT_DATA), entry.size)
Simon Glassef439ed2020-10-26 17:40:08 -0600936 self.assertEqual(U_BOOT_DATA, entry.data)
Simon Glassc1aa66e2022-01-29 14:14:04 -0700937 self.assertEqual(tools.get_bytes(0, 3) + U_BOOT_DATA +
938 tools.get_bytes(0, 5), data[:entry.size])
Simon Glassef439ed2020-10-26 17:40:08 -0600939 pos = entry.size
Simon Glass4f443042016-11-25 20:15:52 -0700940
941 # Second u-boot has an aligned size, but it has no effect
942 self.assertIn('u-boot-align-size-nop', entries)
943 entry = entries['u-boot-align-size-nop']
Simon Glassef439ed2020-10-26 17:40:08 -0600944 self.assertEqual(pos, entry.offset)
945 self.assertEqual(len(U_BOOT_DATA), entry.size)
946 self.assertEqual(U_BOOT_DATA, entry.data)
947 self.assertEqual(U_BOOT_DATA, data[pos:pos + entry.size])
948 pos += entry.size
Simon Glass4f443042016-11-25 20:15:52 -0700949
950 # Third u-boot has an aligned size too
951 self.assertIn('u-boot-align-size', entries)
952 entry = entries['u-boot-align-size']
Simon Glassef439ed2020-10-26 17:40:08 -0600953 self.assertEqual(pos, entry.offset)
Simon Glass4f443042016-11-25 20:15:52 -0700954 self.assertEqual(32, entry.size)
Simon Glassef439ed2020-10-26 17:40:08 -0600955 self.assertEqual(U_BOOT_DATA, entry.data)
Simon Glassc1aa66e2022-01-29 14:14:04 -0700956 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 32 - len(U_BOOT_DATA)),
Simon Glassef439ed2020-10-26 17:40:08 -0600957 data[pos:pos + entry.size])
958 pos += entry.size
Simon Glass4f443042016-11-25 20:15:52 -0700959
960 # Fourth u-boot has an aligned end
961 self.assertIn('u-boot-align-end', entries)
962 entry = entries['u-boot-align-end']
Simon Glass3ab95982018-08-01 15:22:37 -0600963 self.assertEqual(48, entry.offset)
Simon Glass4f443042016-11-25 20:15:52 -0700964 self.assertEqual(16, entry.size)
Simon Glassef439ed2020-10-26 17:40:08 -0600965 self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)])
Simon Glassc1aa66e2022-01-29 14:14:04 -0700966 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 16 - len(U_BOOT_DATA)),
Simon Glassef439ed2020-10-26 17:40:08 -0600967 data[pos:pos + entry.size])
968 pos += entry.size
Simon Glass4f443042016-11-25 20:15:52 -0700969
970 # Fifth u-boot immediately afterwards
971 self.assertIn('u-boot-align-both', entries)
972 entry = entries['u-boot-align-both']
Simon Glass3ab95982018-08-01 15:22:37 -0600973 self.assertEqual(64, entry.offset)
Simon Glass4f443042016-11-25 20:15:52 -0700974 self.assertEqual(64, entry.size)
Simon Glassef439ed2020-10-26 17:40:08 -0600975 self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)])
Simon Glassc1aa66e2022-01-29 14:14:04 -0700976 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 64 - len(U_BOOT_DATA)),
Simon Glassef439ed2020-10-26 17:40:08 -0600977 data[pos:pos + entry.size])
Simon Glass4f443042016-11-25 20:15:52 -0700978
Samuel Hollandb01ae032023-01-21 17:25:16 -0600979 # Sixth u-boot with both minimum size and aligned size
980 self.assertIn('u-boot-min-size', entries)
981 entry = entries['u-boot-min-size']
982 self.assertEqual(128, entry.offset)
983 self.assertEqual(32, entry.size)
984 self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)])
985 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 32 - len(U_BOOT_DATA)),
986 data[pos:pos + entry.size])
987
Simon Glass4f443042016-11-25 20:15:52 -0700988 self.CheckNoGaps(entries)
Samuel Hollandb01ae032023-01-21 17:25:16 -0600989 self.assertEqual(160, image.size)
Simon Glass4f443042016-11-25 20:15:52 -0700990
Simon Glass4eec34c2020-10-26 17:40:10 -0600991 dtb = fdt.Fdt(out_dtb_fname)
992 dtb.Scan()
993 props = self._GetPropTree(dtb, ['size', 'offset', 'image-pos'])
994 expected = {
995 'image-pos': 0,
996 'offset': 0,
Samuel Hollandb01ae032023-01-21 17:25:16 -0600997 'size': 160,
Simon Glass4eec34c2020-10-26 17:40:10 -0600998
999 'u-boot:image-pos': 0,
1000 'u-boot:offset': 0,
1001 'u-boot:size': 3 + 5 + len(U_BOOT_DATA),
1002
1003 'u-boot-align-size-nop:image-pos': 12,
1004 'u-boot-align-size-nop:offset': 12,
1005 'u-boot-align-size-nop:size': 4,
1006
1007 'u-boot-align-size:image-pos': 16,
1008 'u-boot-align-size:offset': 16,
1009 'u-boot-align-size:size': 32,
1010
1011 'u-boot-align-end:image-pos': 48,
1012 'u-boot-align-end:offset': 48,
1013 'u-boot-align-end:size': 16,
1014
1015 'u-boot-align-both:image-pos': 64,
1016 'u-boot-align-both:offset': 64,
1017 'u-boot-align-both:size': 64,
Samuel Hollandb01ae032023-01-21 17:25:16 -06001018
1019 'u-boot-min-size:image-pos': 128,
1020 'u-boot-min-size:offset': 128,
1021 'u-boot-min-size:size': 32,
Simon Glass4eec34c2020-10-26 17:40:10 -06001022 }
1023 self.assertEqual(expected, props)
1024
Simon Glass4f443042016-11-25 20:15:52 -07001025 def testPackAlignPowerOf2(self):
1026 """Test that invalid entry alignment is detected"""
1027 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001028 self._DoTestFile('010_pack_align_power2.dts')
Simon Glass4f443042016-11-25 20:15:52 -07001029 self.assertIn("Node '/binman/u-boot': Alignment 5 must be a power "
1030 "of two", str(e.exception))
1031
1032 def testPackAlignSizePowerOf2(self):
1033 """Test that invalid entry size alignment is detected"""
1034 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001035 self._DoTestFile('011_pack_align_size_power2.dts')
Simon Glass4f443042016-11-25 20:15:52 -07001036 self.assertIn("Node '/binman/u-boot': Alignment size 55 must be a "
1037 "power of two", str(e.exception))
1038
1039 def testPackInvalidAlign(self):
Simon Glass3ab95982018-08-01 15:22:37 -06001040 """Test detection of an offset that does not match its alignment"""
Simon Glass4f443042016-11-25 20:15:52 -07001041 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001042 self._DoTestFile('012_pack_inv_align.dts')
Simon Glass3ab95982018-08-01 15:22:37 -06001043 self.assertIn("Node '/binman/u-boot': Offset 0x5 (5) does not match "
Simon Glass4f443042016-11-25 20:15:52 -07001044 "align 0x4 (4)", str(e.exception))
1045
1046 def testPackInvalidSizeAlign(self):
1047 """Test that invalid entry size alignment is detected"""
1048 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001049 self._DoTestFile('013_pack_inv_size_align.dts')
Simon Glass4f443042016-11-25 20:15:52 -07001050 self.assertIn("Node '/binman/u-boot': Size 0x5 (5) does not match "
1051 "align-size 0x4 (4)", str(e.exception))
1052
1053 def testPackOverlap(self):
1054 """Test that overlapping regions are detected"""
1055 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001056 self._DoTestFile('014_pack_overlap.dts')
Simon Glass3ab95982018-08-01 15:22:37 -06001057 self.assertIn("Node '/binman/u-boot-align': Offset 0x3 (3) overlaps "
Simon Glass4f443042016-11-25 20:15:52 -07001058 "with previous entry '/binman/u-boot' ending at 0x4 (4)",
1059 str(e.exception))
1060
1061 def testPackEntryOverflow(self):
1062 """Test that entries that overflow their size are detected"""
1063 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001064 self._DoTestFile('015_pack_overflow.dts')
Simon Glass4f443042016-11-25 20:15:52 -07001065 self.assertIn("Node '/binman/u-boot': Entry contents size is 0x4 (4) "
1066 "but entry size is 0x3 (3)", str(e.exception))
1067
1068 def testPackImageOverflow(self):
1069 """Test that entries which overflow the image size are detected"""
1070 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001071 self._DoTestFile('016_pack_image_overflow.dts')
Simon Glass8f1da502018-06-01 09:38:12 -06001072 self.assertIn("Section '/binman': contents size 0x4 (4) exceeds section "
Simon Glass4f443042016-11-25 20:15:52 -07001073 "size 0x3 (3)", str(e.exception))
1074
1075 def testPackImageSize(self):
1076 """Test that the image size can be set"""
Simon Glass741f2d62018-10-01 12:22:30 -06001077 retcode = self._DoTestFile('017_pack_image_size.dts')
Simon Glass4f443042016-11-25 20:15:52 -07001078 self.assertEqual(0, retcode)
1079 self.assertIn('image', control.images)
1080 image = control.images['image']
Simon Glass8beb11e2019-07-08 14:25:47 -06001081 self.assertEqual(7, image.size)
Simon Glass4f443042016-11-25 20:15:52 -07001082
1083 def testPackImageSizeAlign(self):
1084 """Test that image size alignemnt works as expected"""
Simon Glass741f2d62018-10-01 12:22:30 -06001085 retcode = self._DoTestFile('018_pack_image_align.dts')
Simon Glass4f443042016-11-25 20:15:52 -07001086 self.assertEqual(0, retcode)
1087 self.assertIn('image', control.images)
1088 image = control.images['image']
Simon Glass8beb11e2019-07-08 14:25:47 -06001089 self.assertEqual(16, image.size)
Simon Glass4f443042016-11-25 20:15:52 -07001090
1091 def testPackInvalidImageAlign(self):
1092 """Test that invalid image alignment is detected"""
1093 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001094 self._DoTestFile('019_pack_inv_image_align.dts')
Simon Glass8f1da502018-06-01 09:38:12 -06001095 self.assertIn("Section '/binman': Size 0x7 (7) does not match "
Simon Glass4f443042016-11-25 20:15:52 -07001096 "align-size 0x8 (8)", str(e.exception))
1097
Simon Glass8d2ef3e2022-02-11 13:23:21 -07001098 def testPackAlignPowerOf2Inv(self):
Simon Glass4f443042016-11-25 20:15:52 -07001099 """Test that invalid image alignment is detected"""
1100 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001101 self._DoTestFile('020_pack_inv_image_align_power2.dts')
Simon Glass8beb11e2019-07-08 14:25:47 -06001102 self.assertIn("Image '/binman': Alignment size 131 must be a power of "
Simon Glass4f443042016-11-25 20:15:52 -07001103 "two", str(e.exception))
1104
1105 def testImagePadByte(self):
1106 """Test that the image pad byte can be specified"""
Simon Glass11ae93e2018-10-01 21:12:47 -06001107 self._SetupSplElf()
Simon Glass741f2d62018-10-01 12:22:30 -06001108 data = self._DoReadFile('021_image_pad.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07001109 self.assertEqual(U_BOOT_SPL_DATA + tools.get_bytes(0xff, 1) +
Simon Glasse6d85ff2019-05-14 15:53:47 -06001110 U_BOOT_DATA, data)
Simon Glass4f443042016-11-25 20:15:52 -07001111
1112 def testImageName(self):
1113 """Test that image files can be named"""
Simon Glass741f2d62018-10-01 12:22:30 -06001114 retcode = self._DoTestFile('022_image_name.dts')
Simon Glass4f443042016-11-25 20:15:52 -07001115 self.assertEqual(0, retcode)
1116 image = control.images['image1']
Simon Glassc1aa66e2022-01-29 14:14:04 -07001117 fname = tools.get_output_filename('test-name')
Simon Glass4f443042016-11-25 20:15:52 -07001118 self.assertTrue(os.path.exists(fname))
1119
1120 image = control.images['image2']
Simon Glassc1aa66e2022-01-29 14:14:04 -07001121 fname = tools.get_output_filename('test-name.xx')
Simon Glass4f443042016-11-25 20:15:52 -07001122 self.assertTrue(os.path.exists(fname))
1123
1124 def testBlobFilename(self):
1125 """Test that generic blobs can be provided by filename"""
Simon Glass741f2d62018-10-01 12:22:30 -06001126 data = self._DoReadFile('023_blob.dts')
Simon Glass4f443042016-11-25 20:15:52 -07001127 self.assertEqual(BLOB_DATA, data)
1128
1129 def testPackSorted(self):
1130 """Test that entries can be sorted"""
Simon Glass11ae93e2018-10-01 21:12:47 -06001131 self._SetupSplElf()
Simon Glass741f2d62018-10-01 12:22:30 -06001132 data = self._DoReadFile('024_sorted.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07001133 self.assertEqual(tools.get_bytes(0, 1) + U_BOOT_SPL_DATA +
1134 tools.get_bytes(0, 2) + U_BOOT_DATA, data)
Simon Glass4f443042016-11-25 20:15:52 -07001135
Simon Glass3ab95982018-08-01 15:22:37 -06001136 def testPackZeroOffset(self):
1137 """Test that an entry at offset 0 is not given a new offset"""
Marek Vasutfadad3a2023-07-18 07:23:58 -06001138 self._SetupSplElf()
Simon Glass4f443042016-11-25 20:15:52 -07001139 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001140 self._DoTestFile('025_pack_zero_size.dts')
Simon Glass3ab95982018-08-01 15:22:37 -06001141 self.assertIn("Node '/binman/u-boot-spl': Offset 0x0 (0) overlaps "
Simon Glass4f443042016-11-25 20:15:52 -07001142 "with previous entry '/binman/u-boot' ending at 0x4 (4)",
1143 str(e.exception))
1144
1145 def testPackUbootDtb(self):
1146 """Test that a device tree can be added to U-Boot"""
Simon Glass741f2d62018-10-01 12:22:30 -06001147 data = self._DoReadFile('026_pack_u_boot_dtb.dts')
Simon Glass4f443042016-11-25 20:15:52 -07001148 self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA, data)
Simon Glasse0ff8552016-11-25 20:15:53 -07001149
1150 def testPackX86RomNoSize(self):
1151 """Test that the end-at-4gb property requires a size property"""
Marek Vasutfadad3a2023-07-18 07:23:58 -06001152 self._SetupSplElf()
Simon Glasse0ff8552016-11-25 20:15:53 -07001153 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001154 self._DoTestFile('027_pack_4gb_no_size.dts')
Simon Glass8beb11e2019-07-08 14:25:47 -06001155 self.assertIn("Image '/binman': Section size must be provided when "
Simon Glasse0ff8552016-11-25 20:15:53 -07001156 "using end-at-4gb", str(e.exception))
1157
Jagdish Gediya94b57db2018-09-03 21:35:07 +05301158 def test4gbAndSkipAtStartTogether(self):
1159 """Test that the end-at-4gb and skip-at-size property can't be used
1160 together"""
Marek Vasutfadad3a2023-07-18 07:23:58 -06001161 self._SetupSplElf()
Jagdish Gediya94b57db2018-09-03 21:35:07 +05301162 with self.assertRaises(ValueError) as e:
Simon Glassdfdd2b62019-08-24 07:23:02 -06001163 self._DoTestFile('098_4gb_and_skip_at_start_together.dts')
Simon Glass8beb11e2019-07-08 14:25:47 -06001164 self.assertIn("Image '/binman': Provide either 'end-at-4gb' or "
Jagdish Gediya94b57db2018-09-03 21:35:07 +05301165 "'skip-at-start'", str(e.exception))
1166
Simon Glasse0ff8552016-11-25 20:15:53 -07001167 def testPackX86RomOutside(self):
Simon Glass3ab95982018-08-01 15:22:37 -06001168 """Test that the end-at-4gb property checks for offset boundaries"""
Marek Vasutfadad3a2023-07-18 07:23:58 -06001169 self._SetupSplElf()
Simon Glasse0ff8552016-11-25 20:15:53 -07001170 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001171 self._DoTestFile('028_pack_4gb_outside.dts')
Simon Glasse6bed4f2020-10-26 17:40:05 -06001172 self.assertIn("Node '/binman/u-boot': Offset 0x0 (0) size 0x4 (4) "
1173 "is outside the section '/binman' starting at "
1174 '0xffffffe0 (4294967264) of size 0x20 (32)',
Simon Glasse0ff8552016-11-25 20:15:53 -07001175 str(e.exception))
1176
1177 def testPackX86Rom(self):
1178 """Test that a basic x86 ROM can be created"""
Simon Glass11ae93e2018-10-01 21:12:47 -06001179 self._SetupSplElf()
Simon Glass9255f3c2019-08-24 07:23:01 -06001180 data = self._DoReadFile('029_x86_rom.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07001181 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 3) + U_BOOT_SPL_DATA +
1182 tools.get_bytes(0, 2), data)
Simon Glasse0ff8552016-11-25 20:15:53 -07001183
1184 def testPackX86RomMeNoDesc(self):
1185 """Test that an invalid Intel descriptor entry is detected"""
Simon Glass0ba4b3d2020-07-09 18:39:41 -06001186 try:
Simon Glass52b10dd2020-07-25 15:11:19 -06001187 TestFunctional._MakeInputFile('descriptor-empty.bin', b'')
Simon Glass0ba4b3d2020-07-09 18:39:41 -06001188 with self.assertRaises(ValueError) as e:
Simon Glass52b10dd2020-07-25 15:11:19 -06001189 self._DoTestFile('163_x86_rom_me_empty.dts')
Simon Glass0ba4b3d2020-07-09 18:39:41 -06001190 self.assertIn("Node '/binman/intel-descriptor': Cannot find Intel Flash Descriptor (FD) signature",
1191 str(e.exception))
1192 finally:
1193 self._SetupDescriptor()
Simon Glasse0ff8552016-11-25 20:15:53 -07001194
1195 def testPackX86RomBadDesc(self):
1196 """Test that the Intel requires a descriptor entry"""
1197 with self.assertRaises(ValueError) as e:
Simon Glass9255f3c2019-08-24 07:23:01 -06001198 self._DoTestFile('030_x86_rom_me_no_desc.dts')
Simon Glass3ab95982018-08-01 15:22:37 -06001199 self.assertIn("Node '/binman/intel-me': No offset set with "
1200 "offset-unset: should another entry provide this correct "
1201 "offset?", str(e.exception))
Simon Glasse0ff8552016-11-25 20:15:53 -07001202
1203 def testPackX86RomMe(self):
1204 """Test that an x86 ROM with an ME region can be created"""
Simon Glass9255f3c2019-08-24 07:23:01 -06001205 data = self._DoReadFile('031_x86_rom_me.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07001206 expected_desc = tools.read_file(self.TestFile('descriptor.bin'))
Simon Glassc5ac1382019-07-08 13:18:54 -06001207 if data[:0x1000] != expected_desc:
1208 self.fail('Expected descriptor binary at start of image')
Simon Glasse0ff8552016-11-25 20:15:53 -07001209 self.assertEqual(ME_DATA, data[0x1000:0x1000 + len(ME_DATA)])
1210
1211 def testPackVga(self):
1212 """Test that an image with a VGA binary can be created"""
Simon Glass9255f3c2019-08-24 07:23:01 -06001213 data = self._DoReadFile('032_intel_vga.dts')
Simon Glasse0ff8552016-11-25 20:15:53 -07001214 self.assertEqual(VGA_DATA, data[:len(VGA_DATA)])
1215
1216 def testPackStart16(self):
1217 """Test that an image with an x86 start16 region can be created"""
Simon Glass9255f3c2019-08-24 07:23:01 -06001218 data = self._DoReadFile('033_x86_start16.dts')
Simon Glasse0ff8552016-11-25 20:15:53 -07001219 self.assertEqual(X86_START16_DATA, data[:len(X86_START16_DATA)])
1220
Jagdish Gediya9d368f32018-09-03 21:35:08 +05301221 def testPackPowerpcMpc85xxBootpgResetvec(self):
1222 """Test that an image with powerpc-mpc85xx-bootpg-resetvec can be
1223 created"""
Simon Glassdfdd2b62019-08-24 07:23:02 -06001224 data = self._DoReadFile('150_powerpc_mpc85xx_bootpg_resetvec.dts')
Jagdish Gediya9d368f32018-09-03 21:35:08 +05301225 self.assertEqual(PPC_MPC85XX_BR_DATA, data[:len(PPC_MPC85XX_BR_DATA)])
1226
Simon Glass736bb0a2018-07-06 10:27:17 -06001227 def _RunMicrocodeTest(self, dts_fname, nodtb_data, ucode_second=False):
Simon Glassadc57012018-07-06 10:27:16 -06001228 """Handle running a test for insertion of microcode
1229
1230 Args:
1231 dts_fname: Name of test .dts file
1232 nodtb_data: Data that we expect in the first section
Simon Glass736bb0a2018-07-06 10:27:17 -06001233 ucode_second: True if the microsecond entry is second instead of
1234 third
Simon Glassadc57012018-07-06 10:27:16 -06001235
1236 Returns:
1237 Tuple:
1238 Contents of first region (U-Boot or SPL)
Simon Glass3ab95982018-08-01 15:22:37 -06001239 Offset and size components of microcode pointer, as inserted
Simon Glassadc57012018-07-06 10:27:16 -06001240 in the above (two 4-byte words)
1241 """
Simon Glass6b187df2017-11-12 21:52:27 -07001242 data = self._DoReadFile(dts_fname, True)
Simon Glasse0ff8552016-11-25 20:15:53 -07001243
1244 # Now check the device tree has no microcode
Simon Glass736bb0a2018-07-06 10:27:17 -06001245 if ucode_second:
1246 ucode_content = data[len(nodtb_data):]
1247 ucode_pos = len(nodtb_data)
1248 dtb_with_ucode = ucode_content[16:]
1249 fdt_len = self.GetFdtLen(dtb_with_ucode)
1250 else:
1251 dtb_with_ucode = data[len(nodtb_data):]
1252 fdt_len = self.GetFdtLen(dtb_with_ucode)
1253 ucode_content = dtb_with_ucode[fdt_len:]
1254 ucode_pos = len(nodtb_data) + fdt_len
Simon Glassc1aa66e2022-01-29 14:14:04 -07001255 fname = tools.get_output_filename('test.dtb')
Simon Glasse0ff8552016-11-25 20:15:53 -07001256 with open(fname, 'wb') as fd:
Simon Glassadc57012018-07-06 10:27:16 -06001257 fd.write(dtb_with_ucode)
Simon Glassec3f3782017-05-27 07:38:29 -06001258 dtb = fdt.FdtScan(fname)
1259 ucode = dtb.GetNode('/microcode')
Simon Glasse0ff8552016-11-25 20:15:53 -07001260 self.assertTrue(ucode)
1261 for node in ucode.subnodes:
1262 self.assertFalse(node.props.get('data'))
1263
Simon Glasse0ff8552016-11-25 20:15:53 -07001264 # Check that the microcode appears immediately after the Fdt
1265 # This matches the concatenation of the data properties in
Simon Glass87722132017-11-12 21:52:26 -07001266 # the /microcode/update@xxx nodes in 34_x86_ucode.dts.
Simon Glasse0ff8552016-11-25 20:15:53 -07001267 ucode_data = struct.pack('>4L', 0x12345678, 0x12345679, 0xabcd0000,
1268 0x78235609)
Simon Glassadc57012018-07-06 10:27:16 -06001269 self.assertEqual(ucode_data, ucode_content[:len(ucode_data)])
Simon Glasse0ff8552016-11-25 20:15:53 -07001270
1271 # Check that the microcode pointer was inserted. It should match the
Simon Glass3ab95982018-08-01 15:22:37 -06001272 # expected offset and size
Simon Glasse0ff8552016-11-25 20:15:53 -07001273 pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
1274 len(ucode_data))
Simon Glass736bb0a2018-07-06 10:27:17 -06001275 u_boot = data[:len(nodtb_data)]
1276 return u_boot, pos_and_size
Simon Glass6b187df2017-11-12 21:52:27 -07001277
1278 def testPackUbootMicrocode(self):
1279 """Test that x86 microcode can be handled correctly
1280
1281 We expect to see the following in the image, in order:
1282 u-boot-nodtb.bin with a microcode pointer inserted at the correct
1283 place
1284 u-boot.dtb with the microcode removed
1285 the microcode
1286 """
Simon Glass741f2d62018-10-01 12:22:30 -06001287 first, pos_and_size = self._RunMicrocodeTest('034_x86_ucode.dts',
Simon Glass6b187df2017-11-12 21:52:27 -07001288 U_BOOT_NODTB_DATA)
Simon Glassc6c10e72019-05-17 22:00:46 -06001289 self.assertEqual(b'nodtb with microcode' + pos_and_size +
1290 b' somewhere in here', first)
Simon Glasse0ff8552016-11-25 20:15:53 -07001291
Simon Glass160a7662017-05-27 07:38:26 -06001292 def _RunPackUbootSingleMicrocode(self):
Simon Glasse0ff8552016-11-25 20:15:53 -07001293 """Test that x86 microcode can be handled correctly
1294
1295 We expect to see the following in the image, in order:
1296 u-boot-nodtb.bin with a microcode pointer inserted at the correct
1297 place
1298 u-boot.dtb with the microcode
1299 an empty microcode region
1300 """
1301 # We need the libfdt library to run this test since only that allows
1302 # finding the offset of a property. This is required by
1303 # Entry_u_boot_dtb_with_ucode.ObtainContents().
Simon Glass741f2d62018-10-01 12:22:30 -06001304 data = self._DoReadFile('035_x86_single_ucode.dts', True)
Simon Glasse0ff8552016-11-25 20:15:53 -07001305
1306 second = data[len(U_BOOT_NODTB_DATA):]
1307
1308 fdt_len = self.GetFdtLen(second)
1309 third = second[fdt_len:]
1310 second = second[:fdt_len]
1311
Simon Glass160a7662017-05-27 07:38:26 -06001312 ucode_data = struct.pack('>2L', 0x12345678, 0x12345679)
1313 self.assertIn(ucode_data, second)
1314 ucode_pos = second.find(ucode_data) + len(U_BOOT_NODTB_DATA)
Simon Glasse0ff8552016-11-25 20:15:53 -07001315
Simon Glass160a7662017-05-27 07:38:26 -06001316 # Check that the microcode pointer was inserted. It should match the
Simon Glass3ab95982018-08-01 15:22:37 -06001317 # expected offset and size
Simon Glass160a7662017-05-27 07:38:26 -06001318 pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
1319 len(ucode_data))
1320 first = data[:len(U_BOOT_NODTB_DATA)]
Simon Glassc6c10e72019-05-17 22:00:46 -06001321 self.assertEqual(b'nodtb with microcode' + pos_and_size +
1322 b' somewhere in here', first)
Simon Glassc49deb82016-11-25 20:15:54 -07001323
Simon Glass75db0862016-11-25 20:15:55 -07001324 def testPackUbootSingleMicrocode(self):
1325 """Test that x86 microcode can be handled correctly with fdt_normal.
1326 """
Simon Glass160a7662017-05-27 07:38:26 -06001327 self._RunPackUbootSingleMicrocode()
Simon Glass75db0862016-11-25 20:15:55 -07001328
Simon Glassc49deb82016-11-25 20:15:54 -07001329 def testUBootImg(self):
1330 """Test that u-boot.img can be put in a file"""
Simon Glass741f2d62018-10-01 12:22:30 -06001331 data = self._DoReadFile('036_u_boot_img.dts')
Simon Glassc49deb82016-11-25 20:15:54 -07001332 self.assertEqual(U_BOOT_IMG_DATA, data)
Simon Glass75db0862016-11-25 20:15:55 -07001333
1334 def testNoMicrocode(self):
1335 """Test that a missing microcode region is detected"""
1336 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001337 self._DoReadFile('037_x86_no_ucode.dts', True)
Simon Glass75db0862016-11-25 20:15:55 -07001338 self.assertIn("Node '/binman/u-boot-dtb-with-ucode': No /microcode "
1339 "node found in ", str(e.exception))
1340
1341 def testMicrocodeWithoutNode(self):
1342 """Test that a missing u-boot-dtb-with-ucode node is detected"""
1343 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001344 self._DoReadFile('038_x86_ucode_missing_node.dts', True)
Simon Glass75db0862016-11-25 20:15:55 -07001345 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
1346 "microcode region u-boot-dtb-with-ucode", str(e.exception))
1347
1348 def testMicrocodeWithoutNode2(self):
1349 """Test that a missing u-boot-ucode node is detected"""
1350 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001351 self._DoReadFile('039_x86_ucode_missing_node2.dts', True)
Simon Glass75db0862016-11-25 20:15:55 -07001352 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
1353 "microcode region u-boot-ucode", str(e.exception))
1354
1355 def testMicrocodeWithoutPtrInElf(self):
1356 """Test that a U-Boot binary without the microcode symbol is detected"""
1357 # ELF file without a '_dt_ucode_base_size' symbol
Simon Glass75db0862016-11-25 20:15:55 -07001358 try:
Simon Glassbccd91d2019-08-24 07:22:55 -06001359 TestFunctional._MakeInputFile('u-boot',
Simon Glassc1aa66e2022-01-29 14:14:04 -07001360 tools.read_file(self.ElfTestFile('u_boot_no_ucode_ptr')))
Simon Glass75db0862016-11-25 20:15:55 -07001361
1362 with self.assertRaises(ValueError) as e:
Simon Glass160a7662017-05-27 07:38:26 -06001363 self._RunPackUbootSingleMicrocode()
Simon Glass75db0862016-11-25 20:15:55 -07001364 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot locate "
1365 "_dt_ucode_base_size symbol in u-boot", str(e.exception))
1366
1367 finally:
1368 # Put the original file back
Simon Glassf514d8f2019-08-24 07:22:54 -06001369 TestFunctional._MakeInputFile('u-boot',
Simon Glassc1aa66e2022-01-29 14:14:04 -07001370 tools.read_file(self.ElfTestFile('u_boot_ucode_ptr')))
Simon Glass75db0862016-11-25 20:15:55 -07001371
1372 def testMicrocodeNotInImage(self):
1373 """Test that microcode must be placed within the image"""
1374 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001375 self._DoReadFile('040_x86_ucode_not_in_image.dts', True)
Simon Glass75db0862016-11-25 20:15:55 -07001376 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Microcode "
1377 "pointer _dt_ucode_base_size at fffffe14 is outside the "
Simon Glass25ac0e62018-06-01 09:38:14 -06001378 "section ranging from 00000000 to 0000002e", str(e.exception))
Simon Glass75db0862016-11-25 20:15:55 -07001379
1380 def testWithoutMicrocode(self):
1381 """Test that we can cope with an image without microcode (e.g. qemu)"""
Simon Glassbccd91d2019-08-24 07:22:55 -06001382 TestFunctional._MakeInputFile('u-boot',
Simon Glassc1aa66e2022-01-29 14:14:04 -07001383 tools.read_file(self.ElfTestFile('u_boot_no_ucode_ptr')))
Simon Glass741f2d62018-10-01 12:22:30 -06001384 data, dtb, _, _ = self._DoReadFileDtb('044_x86_optional_ucode.dts', True)
Simon Glass75db0862016-11-25 20:15:55 -07001385
1386 # Now check the device tree has no microcode
1387 self.assertEqual(U_BOOT_NODTB_DATA, data[:len(U_BOOT_NODTB_DATA)])
1388 second = data[len(U_BOOT_NODTB_DATA):]
1389
1390 fdt_len = self.GetFdtLen(second)
1391 self.assertEqual(dtb, second[:fdt_len])
1392
1393 used_len = len(U_BOOT_NODTB_DATA) + fdt_len
1394 third = data[used_len:]
Simon Glassc1aa66e2022-01-29 14:14:04 -07001395 self.assertEqual(tools.get_bytes(0, 0x200 - used_len), third)
Simon Glass75db0862016-11-25 20:15:55 -07001396
1397 def testUnknownPosSize(self):
1398 """Test that microcode must be placed within the image"""
1399 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001400 self._DoReadFile('041_unknown_pos_size.dts', True)
Simon Glass3ab95982018-08-01 15:22:37 -06001401 self.assertIn("Section '/binman': Unable to set offset/size for unknown "
Simon Glass75db0862016-11-25 20:15:55 -07001402 "entry 'invalid-entry'", str(e.exception))
Simon Glassda229092016-11-25 20:15:56 -07001403
1404 def testPackFsp(self):
1405 """Test that an image with a FSP binary can be created"""
Simon Glass9255f3c2019-08-24 07:23:01 -06001406 data = self._DoReadFile('042_intel_fsp.dts')
Simon Glassda229092016-11-25 20:15:56 -07001407 self.assertEqual(FSP_DATA, data[:len(FSP_DATA)])
1408
1409 def testPackCmc(self):
Bin Meng59ea8c22017-08-15 22:41:54 -07001410 """Test that an image with a CMC binary can be created"""
Simon Glass9255f3c2019-08-24 07:23:01 -06001411 data = self._DoReadFile('043_intel_cmc.dts')
Simon Glassda229092016-11-25 20:15:56 -07001412 self.assertEqual(CMC_DATA, data[:len(CMC_DATA)])
Bin Meng59ea8c22017-08-15 22:41:54 -07001413
1414 def testPackVbt(self):
1415 """Test that an image with a VBT binary can be created"""
Simon Glass9255f3c2019-08-24 07:23:01 -06001416 data = self._DoReadFile('046_intel_vbt.dts')
Bin Meng59ea8c22017-08-15 22:41:54 -07001417 self.assertEqual(VBT_DATA, data[:len(VBT_DATA)])
Simon Glass9fc60b42017-11-12 21:52:22 -07001418
Simon Glass56509842017-11-12 21:52:25 -07001419 def testSplBssPad(self):
1420 """Test that we can pad SPL's BSS with zeros"""
Simon Glass6b187df2017-11-12 21:52:27 -07001421 # ELF file with a '__bss_size' symbol
Simon Glass11ae93e2018-10-01 21:12:47 -06001422 self._SetupSplElf()
Simon Glass741f2d62018-10-01 12:22:30 -06001423 data = self._DoReadFile('047_spl_bss_pad.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07001424 self.assertEqual(U_BOOT_SPL_DATA + tools.get_bytes(0, 10) + U_BOOT_DATA,
Simon Glasse6d85ff2019-05-14 15:53:47 -06001425 data)
Simon Glass56509842017-11-12 21:52:25 -07001426
Simon Glass86af5112018-10-01 21:12:42 -06001427 def testSplBssPadMissing(self):
1428 """Test that a missing symbol is detected"""
Simon Glass11ae93e2018-10-01 21:12:47 -06001429 self._SetupSplElf('u_boot_ucode_ptr')
Simon Glassb50e5612017-11-13 18:54:54 -07001430 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001431 self._DoReadFile('047_spl_bss_pad.dts')
Simon Glassb50e5612017-11-13 18:54:54 -07001432 self.assertIn('Expected __bss_size symbol in spl/u-boot-spl',
1433 str(e.exception))
1434
Simon Glass87722132017-11-12 21:52:26 -07001435 def testPackStart16Spl(self):
Simon Glass35b384c2018-09-14 04:57:10 -06001436 """Test that an image with an x86 start16 SPL region can be created"""
Simon Glass9255f3c2019-08-24 07:23:01 -06001437 data = self._DoReadFile('048_x86_start16_spl.dts')
Simon Glass87722132017-11-12 21:52:26 -07001438 self.assertEqual(X86_START16_SPL_DATA, data[:len(X86_START16_SPL_DATA)])
1439
Simon Glass736bb0a2018-07-06 10:27:17 -06001440 def _PackUbootSplMicrocode(self, dts, ucode_second=False):
1441 """Helper function for microcode tests
Simon Glass6b187df2017-11-12 21:52:27 -07001442
1443 We expect to see the following in the image, in order:
1444 u-boot-spl-nodtb.bin with a microcode pointer inserted at the
1445 correct place
1446 u-boot.dtb with the microcode removed
1447 the microcode
Simon Glass736bb0a2018-07-06 10:27:17 -06001448
1449 Args:
1450 dts: Device tree file to use for test
1451 ucode_second: True if the microsecond entry is second instead of
1452 third
Simon Glass6b187df2017-11-12 21:52:27 -07001453 """
Simon Glass11ae93e2018-10-01 21:12:47 -06001454 self._SetupSplElf('u_boot_ucode_ptr')
Simon Glass736bb0a2018-07-06 10:27:17 -06001455 first, pos_and_size = self._RunMicrocodeTest(dts, U_BOOT_SPL_NODTB_DATA,
1456 ucode_second=ucode_second)
Simon Glassc6c10e72019-05-17 22:00:46 -06001457 self.assertEqual(b'splnodtb with microc' + pos_and_size +
1458 b'ter somewhere in here', first)
Simon Glass6b187df2017-11-12 21:52:27 -07001459
Simon Glass736bb0a2018-07-06 10:27:17 -06001460 def testPackUbootSplMicrocode(self):
1461 """Test that x86 microcode can be handled correctly in SPL"""
Marek Vasutfadad3a2023-07-18 07:23:58 -06001462 self._SetupSplElf()
Simon Glass741f2d62018-10-01 12:22:30 -06001463 self._PackUbootSplMicrocode('049_x86_ucode_spl.dts')
Simon Glass736bb0a2018-07-06 10:27:17 -06001464
1465 def testPackUbootSplMicrocodeReorder(self):
1466 """Test that order doesn't matter for microcode entries
1467
1468 This is the same as testPackUbootSplMicrocode but when we process the
1469 u-boot-ucode entry we have not yet seen the u-boot-dtb-with-ucode
1470 entry, so we reply on binman to try later.
1471 """
Simon Glass741f2d62018-10-01 12:22:30 -06001472 self._PackUbootSplMicrocode('058_x86_ucode_spl_needs_retry.dts',
Simon Glass736bb0a2018-07-06 10:27:17 -06001473 ucode_second=True)
1474
Simon Glassca4f4ff2017-11-12 21:52:28 -07001475 def testPackMrc(self):
1476 """Test that an image with an MRC binary can be created"""
Simon Glass741f2d62018-10-01 12:22:30 -06001477 data = self._DoReadFile('050_intel_mrc.dts')
Simon Glassca4f4ff2017-11-12 21:52:28 -07001478 self.assertEqual(MRC_DATA, data[:len(MRC_DATA)])
1479
Simon Glass47419ea2017-11-13 18:54:55 -07001480 def testSplDtb(self):
1481 """Test that an image with spl/u-boot-spl.dtb can be created"""
Marek Vasutfadad3a2023-07-18 07:23:58 -06001482 self._SetupSplElf()
Simon Glass741f2d62018-10-01 12:22:30 -06001483 data = self._DoReadFile('051_u_boot_spl_dtb.dts')
Simon Glass47419ea2017-11-13 18:54:55 -07001484 self.assertEqual(U_BOOT_SPL_DTB_DATA, data[:len(U_BOOT_SPL_DTB_DATA)])
1485
Simon Glass4e6fdbe2017-11-13 18:54:56 -07001486 def testSplNoDtb(self):
1487 """Test that an image with spl/u-boot-spl-nodtb.bin can be created"""
Simon Glass0fe44dc2021-04-25 08:39:32 +12001488 self._SetupSplElf()
Simon Glass741f2d62018-10-01 12:22:30 -06001489 data = self._DoReadFile('052_u_boot_spl_nodtb.dts')
Simon Glass4e6fdbe2017-11-13 18:54:56 -07001490 self.assertEqual(U_BOOT_SPL_NODTB_DATA, data[:len(U_BOOT_SPL_NODTB_DATA)])
1491
Simon Glass3d433382021-03-21 18:24:30 +13001492 def checkSymbols(self, dts, base_data, u_boot_offset, entry_args=None,
Simon Glass4649bea2023-07-18 07:23:54 -06001493 use_expanded=False, no_write_symbols=False):
Simon Glassf5898822021-03-18 20:24:56 +13001494 """Check the image contains the expected symbol values
1495
1496 Args:
1497 dts: Device tree file to use for test
1498 base_data: Data before and after 'u-boot' section
1499 u_boot_offset: Offset of 'u-boot' section in image
Simon Glass3d433382021-03-21 18:24:30 +13001500 entry_args: Dict of entry args to supply to binman
1501 key: arg name
1502 value: value of that arg
1503 use_expanded: True to use expanded entries where available, e.g.
1504 'u-boot-expanded' instead of 'u-boot'
Simon Glassf5898822021-03-18 20:24:56 +13001505 """
Simon Glass1542c8b2019-08-24 07:22:56 -06001506 elf_fname = self.ElfTestFile('u_boot_binman_syms')
Simon Glass19790632017-11-13 18:55:01 -07001507 syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
1508 addr = elf.GetSymbolAddress(elf_fname, '__image_copy_start')
Alper Nebi Yasak367ecbf2022-06-18 15:13:11 +03001509 self.assertEqual(syms['_binman_sym_magic'].address, addr)
Simon Glassf5898822021-03-18 20:24:56 +13001510 self.assertEqual(syms['_binman_u_boot_spl_any_prop_offset'].address,
Alper Nebi Yasak367ecbf2022-06-18 15:13:11 +03001511 addr + 4)
Simon Glass19790632017-11-13 18:55:01 -07001512
Simon Glass11ae93e2018-10-01 21:12:47 -06001513 self._SetupSplElf('u_boot_binman_syms')
Simon Glass3d433382021-03-21 18:24:30 +13001514 data = self._DoReadFileDtb(dts, entry_args=entry_args,
1515 use_expanded=use_expanded)[0]
Simon Glassf5898822021-03-18 20:24:56 +13001516 # The image should contain the symbols from u_boot_binman_syms.c
1517 # Note that image_pos is adjusted by the base address of the image,
1518 # which is 0x10 in our test image
Alper Nebi Yasak367ecbf2022-06-18 15:13:11 +03001519 sym_values = struct.pack('<LLQLL', elf.BINMAN_SYM_MAGIC_VALUE,
1520 0x00, u_boot_offset + len(U_BOOT_DATA),
Simon Glassf5898822021-03-18 20:24:56 +13001521 0x10 + u_boot_offset, 0x04)
Simon Glass4649bea2023-07-18 07:23:54 -06001522 if no_write_symbols:
1523 expected = (base_data +
1524 tools.get_bytes(0xff, 0x38 - len(base_data)) +
1525 U_BOOT_DATA + base_data)
1526 else:
1527 expected = (sym_values + base_data[24:] +
1528 tools.get_bytes(0xff, 1) + U_BOOT_DATA + sym_values +
1529 base_data[24:])
Simon Glass19790632017-11-13 18:55:01 -07001530 self.assertEqual(expected, data)
1531
Simon Glassf5898822021-03-18 20:24:56 +13001532 def testSymbols(self):
1533 """Test binman can assign symbols embedded in U-Boot"""
Alper Nebi Yasak367ecbf2022-06-18 15:13:11 +03001534 self.checkSymbols('053_symbols.dts', U_BOOT_SPL_DATA, 0x1c)
Simon Glassf5898822021-03-18 20:24:56 +13001535
1536 def testSymbolsNoDtb(self):
1537 """Test binman can assign symbols embedded in U-Boot SPL"""
Simon Glasse9e0db82021-03-21 18:24:29 +13001538 self.checkSymbols('196_symbols_nodtb.dts',
Simon Glassf5898822021-03-18 20:24:56 +13001539 U_BOOT_SPL_NODTB_DATA + U_BOOT_SPL_DTB_DATA,
1540 0x38)
1541
Simon Glassdd57c132018-06-01 09:38:11 -06001542 def testPackUnitAddress(self):
1543 """Test that we support multiple binaries with the same name"""
Simon Glass741f2d62018-10-01 12:22:30 -06001544 data = self._DoReadFile('054_unit_address.dts')
Simon Glassdd57c132018-06-01 09:38:11 -06001545 self.assertEqual(U_BOOT_DATA + U_BOOT_DATA, data)
1546
Simon Glass18546952018-06-01 09:38:16 -06001547 def testSections(self):
1548 """Basic test of sections"""
Simon Glass741f2d62018-10-01 12:22:30 -06001549 data = self._DoReadFile('055_sections.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07001550 expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) +
1551 U_BOOT_DATA + tools.get_bytes(ord('a'), 12) +
1552 U_BOOT_DATA + tools.get_bytes(ord('&'), 4))
Simon Glass18546952018-06-01 09:38:16 -06001553 self.assertEqual(expected, data)
Simon Glass9fc60b42017-11-12 21:52:22 -07001554
Simon Glass3b0c3822018-06-01 09:38:20 -06001555 def testMap(self):
1556 """Tests outputting a map of the images"""
Simon Glass741f2d62018-10-01 12:22:30 -06001557 _, _, map_data, _ = self._DoReadFileDtb('055_sections.dts', map=True)
Simon Glass1be70d22018-07-17 13:25:49 -06001558 self.assertEqual('''ImagePos Offset Size Name
Simon Glass193d3db2023-02-07 14:34:18 -0700155900000000 00000000 00000028 image
Simon Glass1be70d22018-07-17 13:25:49 -0600156000000000 00000000 00000010 section@0
156100000000 00000000 00000004 u-boot
156200000010 00000010 00000010 section@1
156300000010 00000000 00000004 u-boot
156400000020 00000020 00000004 section@2
156500000020 00000000 00000004 u-boot
Simon Glass3b0c3822018-06-01 09:38:20 -06001566''', map_data)
1567
Simon Glassc8d48ef2018-06-01 09:38:21 -06001568 def testNamePrefix(self):
1569 """Tests that name prefixes are used"""
Simon Glass741f2d62018-10-01 12:22:30 -06001570 _, _, map_data, _ = self._DoReadFileDtb('056_name_prefix.dts', map=True)
Simon Glass1be70d22018-07-17 13:25:49 -06001571 self.assertEqual('''ImagePos Offset Size Name
Simon Glass193d3db2023-02-07 14:34:18 -0700157200000000 00000000 00000028 image
Simon Glass1be70d22018-07-17 13:25:49 -0600157300000000 00000000 00000010 section@0
157400000000 00000000 00000004 ro-u-boot
157500000010 00000010 00000010 section@1
157600000010 00000000 00000004 rw-u-boot
Simon Glassc8d48ef2018-06-01 09:38:21 -06001577''', map_data)
1578
Simon Glass736bb0a2018-07-06 10:27:17 -06001579 def testUnknownContents(self):
1580 """Test that obtaining the contents works as expected"""
1581 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001582 self._DoReadFile('057_unknown_contents.dts', True)
Simon Glass8beb11e2019-07-08 14:25:47 -06001583 self.assertIn("Image '/binman': Internal error: Could not complete "
Simon Glass16287932020-04-17 18:09:03 -06001584 "processing of contents: remaining ["
1585 "<binman.etype._testing.Entry__testing ", str(e.exception))
Simon Glass736bb0a2018-07-06 10:27:17 -06001586
Simon Glass5c890232018-07-06 10:27:19 -06001587 def testBadChangeSize(self):
1588 """Test that trying to change the size of an entry fails"""
Simon Glassc52c9e72019-07-08 14:25:37 -06001589 try:
1590 state.SetAllowEntryExpansion(False)
1591 with self.assertRaises(ValueError) as e:
1592 self._DoReadFile('059_change_size.dts', True)
Simon Glass79d3c582019-07-20 12:23:57 -06001593 self.assertIn("Node '/binman/_testing': Cannot update entry size from 2 to 3",
Simon Glassc52c9e72019-07-08 14:25:37 -06001594 str(e.exception))
1595 finally:
1596 state.SetAllowEntryExpansion(True)
Simon Glass5c890232018-07-06 10:27:19 -06001597
Simon Glass16b8d6b2018-07-06 10:27:42 -06001598 def testUpdateFdt(self):
Simon Glass3ab95982018-08-01 15:22:37 -06001599 """Test that we can update the device tree with offset/size info"""
Simon Glass741f2d62018-10-01 12:22:30 -06001600 _, _, _, out_dtb_fname = self._DoReadFileDtb('060_fdt_update.dts',
Simon Glass16b8d6b2018-07-06 10:27:42 -06001601 update_dtb=True)
Simon Glasscee02e62018-07-17 13:25:52 -06001602 dtb = fdt.Fdt(out_dtb_fname)
1603 dtb.Scan()
Simon Glass12bb1a92019-07-20 12:23:51 -06001604 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
Simon Glass16b8d6b2018-07-06 10:27:42 -06001605 self.assertEqual({
Simon Glassdbf6be92018-08-01 15:22:42 -06001606 'image-pos': 0,
Simon Glass8122f392018-07-17 13:25:28 -06001607 'offset': 0,
Simon Glass3ab95982018-08-01 15:22:37 -06001608 '_testing:offset': 32,
Simon Glass79d3c582019-07-20 12:23:57 -06001609 '_testing:size': 2,
Simon Glassdbf6be92018-08-01 15:22:42 -06001610 '_testing:image-pos': 32,
Simon Glass3ab95982018-08-01 15:22:37 -06001611 'section@0/u-boot:offset': 0,
Simon Glass16b8d6b2018-07-06 10:27:42 -06001612 'section@0/u-boot:size': len(U_BOOT_DATA),
Simon Glassdbf6be92018-08-01 15:22:42 -06001613 'section@0/u-boot:image-pos': 0,
Simon Glass3ab95982018-08-01 15:22:37 -06001614 'section@0:offset': 0,
Simon Glass16b8d6b2018-07-06 10:27:42 -06001615 'section@0:size': 16,
Simon Glassdbf6be92018-08-01 15:22:42 -06001616 'section@0:image-pos': 0,
Simon Glass16b8d6b2018-07-06 10:27:42 -06001617
Simon Glass3ab95982018-08-01 15:22:37 -06001618 'section@1/u-boot:offset': 0,
Simon Glass16b8d6b2018-07-06 10:27:42 -06001619 'section@1/u-boot:size': len(U_BOOT_DATA),
Simon Glassdbf6be92018-08-01 15:22:42 -06001620 'section@1/u-boot:image-pos': 16,
Simon Glass3ab95982018-08-01 15:22:37 -06001621 'section@1:offset': 16,
Simon Glass16b8d6b2018-07-06 10:27:42 -06001622 'section@1:size': 16,
Simon Glassdbf6be92018-08-01 15:22:42 -06001623 'section@1:image-pos': 16,
Simon Glass16b8d6b2018-07-06 10:27:42 -06001624 'size': 40
1625 }, props)
1626
1627 def testUpdateFdtBad(self):
1628 """Test that we detect when ProcessFdt never completes"""
1629 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001630 self._DoReadFileDtb('061_fdt_update_bad.dts', update_dtb=True)
Simon Glass16b8d6b2018-07-06 10:27:42 -06001631 self.assertIn('Could not complete processing of Fdt: remaining '
Simon Glass16287932020-04-17 18:09:03 -06001632 '[<binman.etype._testing.Entry__testing',
1633 str(e.exception))
Simon Glass5c890232018-07-06 10:27:19 -06001634
Simon Glass53af22a2018-07-17 13:25:32 -06001635 def testEntryArgs(self):
1636 """Test passing arguments to entries from the command line"""
1637 entry_args = {
1638 'test-str-arg': 'test1',
1639 'test-int-arg': '456',
1640 }
Simon Glass741f2d62018-10-01 12:22:30 -06001641 self._DoReadFileDtb('062_entry_args.dts', entry_args=entry_args)
Simon Glass53af22a2018-07-17 13:25:32 -06001642 self.assertIn('image', control.images)
1643 entry = control.images['image'].GetEntries()['_testing']
1644 self.assertEqual('test0', entry.test_str_fdt)
1645 self.assertEqual('test1', entry.test_str_arg)
1646 self.assertEqual(123, entry.test_int_fdt)
1647 self.assertEqual(456, entry.test_int_arg)
1648
1649 def testEntryArgsMissing(self):
1650 """Test missing arguments and properties"""
1651 entry_args = {
1652 'test-int-arg': '456',
1653 }
Simon Glass741f2d62018-10-01 12:22:30 -06001654 self._DoReadFileDtb('063_entry_args_missing.dts', entry_args=entry_args)
Simon Glass53af22a2018-07-17 13:25:32 -06001655 entry = control.images['image'].GetEntries()['_testing']
1656 self.assertEqual('test0', entry.test_str_fdt)
1657 self.assertEqual(None, entry.test_str_arg)
1658 self.assertEqual(None, entry.test_int_fdt)
1659 self.assertEqual(456, entry.test_int_arg)
1660
1661 def testEntryArgsRequired(self):
1662 """Test missing arguments and properties"""
1663 entry_args = {
1664 'test-int-arg': '456',
1665 }
1666 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001667 self._DoReadFileDtb('064_entry_args_required.dts')
Simon Glass3decfa32020-09-01 05:13:54 -06001668 self.assertIn("Node '/binman/_testing': "
1669 'Missing required properties/entry args: test-str-arg, '
1670 'test-int-fdt, test-int-arg',
Simon Glass53af22a2018-07-17 13:25:32 -06001671 str(e.exception))
1672
1673 def testEntryArgsInvalidFormat(self):
1674 """Test that an invalid entry-argument format is detected"""
Simon Glass53cd5d92019-07-08 14:25:29 -06001675 args = ['build', '-d', self.TestFile('064_entry_args_required.dts'),
1676 '-ano-value']
Simon Glass53af22a2018-07-17 13:25:32 -06001677 with self.assertRaises(ValueError) as e:
1678 self._DoBinman(*args)
1679 self.assertIn("Invalid entry arguemnt 'no-value'", str(e.exception))
1680
1681 def testEntryArgsInvalidInteger(self):
1682 """Test that an invalid entry-argument integer is detected"""
1683 entry_args = {
1684 'test-int-arg': 'abc',
1685 }
1686 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001687 self._DoReadFileDtb('062_entry_args.dts', entry_args=entry_args)
Simon Glass53af22a2018-07-17 13:25:32 -06001688 self.assertIn("Node '/binman/_testing': Cannot convert entry arg "
1689 "'test-int-arg' (value 'abc') to integer",
1690 str(e.exception))
1691
1692 def testEntryArgsInvalidDatatype(self):
1693 """Test that an invalid entry-argument datatype is detected
1694
1695 This test could be written in entry_test.py except that it needs
1696 access to control.entry_args, which seems more than that module should
1697 be able to see.
1698 """
1699 entry_args = {
1700 'test-bad-datatype-arg': '12',
1701 }
1702 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001703 self._DoReadFileDtb('065_entry_args_unknown_datatype.dts',
Simon Glass53af22a2018-07-17 13:25:32 -06001704 entry_args=entry_args)
1705 self.assertIn('GetArg() internal error: Unknown data type ',
1706 str(e.exception))
1707
Simon Glassbb748372018-07-17 13:25:33 -06001708 def testText(self):
1709 """Test for a text entry type"""
1710 entry_args = {
1711 'test-id': TEXT_DATA,
1712 'test-id2': TEXT_DATA2,
1713 'test-id3': TEXT_DATA3,
1714 }
Simon Glass741f2d62018-10-01 12:22:30 -06001715 data, _, _, _ = self._DoReadFileDtb('066_text.dts',
Simon Glassbb748372018-07-17 13:25:33 -06001716 entry_args=entry_args)
Simon Glassc1aa66e2022-01-29 14:14:04 -07001717 expected = (tools.to_bytes(TEXT_DATA) +
1718 tools.get_bytes(0, 8 - len(TEXT_DATA)) +
1719 tools.to_bytes(TEXT_DATA2) + tools.to_bytes(TEXT_DATA3) +
Simon Glassaa88b502019-07-08 13:18:40 -06001720 b'some text' + b'more text')
Simon Glassbb748372018-07-17 13:25:33 -06001721 self.assertEqual(expected, data)
1722
Simon Glassfd8d1f72018-07-17 13:25:36 -06001723 def testEntryDocs(self):
1724 """Test for creation of entry documentation"""
1725 with test_util.capture_sys_output() as (stdout, stderr):
Simon Glass87d43322020-08-05 13:27:46 -06001726 control.WriteEntryDocs(control.GetEntryModules())
Simon Glassfd8d1f72018-07-17 13:25:36 -06001727 self.assertTrue(len(stdout.getvalue()) > 0)
1728
1729 def testEntryDocsMissing(self):
1730 """Test handling of missing entry documentation"""
1731 with self.assertRaises(ValueError) as e:
1732 with test_util.capture_sys_output() as (stdout, stderr):
Simon Glass87d43322020-08-05 13:27:46 -06001733 control.WriteEntryDocs(control.GetEntryModules(), 'u_boot')
Simon Glassfd8d1f72018-07-17 13:25:36 -06001734 self.assertIn('Documentation is missing for modules: u_boot',
1735 str(e.exception))
1736
Simon Glass11e36cc2018-07-17 13:25:38 -06001737 def testFmap(self):
1738 """Basic test of generation of a flashrom fmap"""
Simon Glass741f2d62018-10-01 12:22:30 -06001739 data = self._DoReadFile('067_fmap.dts')
Simon Glass11e36cc2018-07-17 13:25:38 -06001740 fhdr, fentries = fmap_util.DecodeFmap(data[32:])
Simon Glassc1aa66e2022-01-29 14:14:04 -07001741 expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) +
1742 U_BOOT_DATA + tools.get_bytes(ord('a'), 12))
Simon Glass11e36cc2018-07-17 13:25:38 -06001743 self.assertEqual(expected, data[:32])
Simon Glassc6c10e72019-05-17 22:00:46 -06001744 self.assertEqual(b'__FMAP__', fhdr.signature)
Simon Glass11e36cc2018-07-17 13:25:38 -06001745 self.assertEqual(1, fhdr.ver_major)
1746 self.assertEqual(0, fhdr.ver_minor)
1747 self.assertEqual(0, fhdr.base)
Simon Glass17365752021-04-03 11:05:10 +13001748 expect_size = fmap_util.FMAP_HEADER_LEN + fmap_util.FMAP_AREA_LEN * 5
Simon Glassc7722e82021-04-03 11:05:09 +13001749 self.assertEqual(16 + 16 + expect_size, fhdr.image_size)
Simon Glassc6c10e72019-05-17 22:00:46 -06001750 self.assertEqual(b'FMAP', fhdr.name)
Simon Glass17365752021-04-03 11:05:10 +13001751 self.assertEqual(5, fhdr.nareas)
Simon Glassc7722e82021-04-03 11:05:09 +13001752 fiter = iter(fentries)
Simon Glass11e36cc2018-07-17 13:25:38 -06001753
Simon Glassc7722e82021-04-03 11:05:09 +13001754 fentry = next(fiter)
Simon Glass17365752021-04-03 11:05:10 +13001755 self.assertEqual(b'SECTION0', fentry.name)
1756 self.assertEqual(0, fentry.offset)
1757 self.assertEqual(16, fentry.size)
Simon Glass9dbb02b2023-02-12 17:11:15 -07001758 self.assertEqual(fmap_util.FMAP_AREA_PRESERVE, fentry.flags)
Simon Glass17365752021-04-03 11:05:10 +13001759
1760 fentry = next(fiter)
Simon Glassc7722e82021-04-03 11:05:09 +13001761 self.assertEqual(b'RO_U_BOOT', fentry.name)
1762 self.assertEqual(0, fentry.offset)
1763 self.assertEqual(4, fentry.size)
1764 self.assertEqual(0, fentry.flags)
Simon Glass11e36cc2018-07-17 13:25:38 -06001765
Simon Glassc7722e82021-04-03 11:05:09 +13001766 fentry = next(fiter)
Simon Glass17365752021-04-03 11:05:10 +13001767 self.assertEqual(b'SECTION1', fentry.name)
1768 self.assertEqual(16, fentry.offset)
1769 self.assertEqual(16, fentry.size)
1770 self.assertEqual(0, fentry.flags)
1771
1772 fentry = next(fiter)
Simon Glassc7722e82021-04-03 11:05:09 +13001773 self.assertEqual(b'RW_U_BOOT', fentry.name)
1774 self.assertEqual(16, fentry.offset)
1775 self.assertEqual(4, fentry.size)
1776 self.assertEqual(0, fentry.flags)
Simon Glass11e36cc2018-07-17 13:25:38 -06001777
Simon Glassc7722e82021-04-03 11:05:09 +13001778 fentry = next(fiter)
1779 self.assertEqual(b'FMAP', fentry.name)
1780 self.assertEqual(32, fentry.offset)
1781 self.assertEqual(expect_size, fentry.size)
1782 self.assertEqual(0, fentry.flags)
Simon Glass11e36cc2018-07-17 13:25:38 -06001783
Simon Glassec127af2018-07-17 13:25:39 -06001784 def testBlobNamedByArg(self):
1785 """Test we can add a blob with the filename coming from an entry arg"""
1786 entry_args = {
1787 'cros-ec-rw-path': 'ecrw.bin',
1788 }
Simon Glass3decfa32020-09-01 05:13:54 -06001789 self._DoReadFileDtb('068_blob_named_by_arg.dts', entry_args=entry_args)
Simon Glassec127af2018-07-17 13:25:39 -06001790
Simon Glass3af8e492018-07-17 13:25:40 -06001791 def testFill(self):
1792 """Test for an fill entry type"""
Simon Glass741f2d62018-10-01 12:22:30 -06001793 data = self._DoReadFile('069_fill.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07001794 expected = tools.get_bytes(0xff, 8) + tools.get_bytes(0, 8)
Simon Glass3af8e492018-07-17 13:25:40 -06001795 self.assertEqual(expected, data)
1796
1797 def testFillNoSize(self):
1798 """Test for an fill entry type with no size"""
1799 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001800 self._DoReadFile('070_fill_no_size.dts')
Simon Glasscdadada2022-08-13 11:40:44 -06001801 self.assertIn("'fill' entry is missing properties: size",
Simon Glass3af8e492018-07-17 13:25:40 -06001802 str(e.exception))
1803
Simon Glass0ef87aa2018-07-17 13:25:44 -06001804 def _HandleGbbCommand(self, pipe_list):
1805 """Fake calls to the futility utility"""
Simon Glassfe7e9242023-02-22 12:14:49 -07001806 if 'futility' in pipe_list[0][0]:
Simon Glass0ef87aa2018-07-17 13:25:44 -06001807 fname = pipe_list[0][-1]
1808 # Append our GBB data to the file, which will happen every time the
1809 # futility command is called.
Simon Glass1d0ebf72019-05-14 15:53:42 -06001810 with open(fname, 'ab') as fd:
Simon Glass0ef87aa2018-07-17 13:25:44 -06001811 fd.write(GBB_DATA)
1812 return command.CommandResult()
1813
1814 def testGbb(self):
1815 """Test for the Chromium OS Google Binary Block"""
1816 command.test_result = self._HandleGbbCommand
1817 entry_args = {
1818 'keydir': 'devkeys',
1819 'bmpblk': 'bmpblk.bin',
1820 }
Simon Glass741f2d62018-10-01 12:22:30 -06001821 data, _, _, _ = self._DoReadFileDtb('071_gbb.dts', entry_args=entry_args)
Simon Glass0ef87aa2018-07-17 13:25:44 -06001822
1823 # Since futility
Simon Glassc1aa66e2022-01-29 14:14:04 -07001824 expected = (GBB_DATA + GBB_DATA + tools.get_bytes(0, 8) +
1825 tools.get_bytes(0, 0x2180 - 16))
Simon Glass0ef87aa2018-07-17 13:25:44 -06001826 self.assertEqual(expected, data)
1827
1828 def testGbbTooSmall(self):
1829 """Test for the Chromium OS Google Binary Block being large enough"""
1830 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001831 self._DoReadFileDtb('072_gbb_too_small.dts')
Simon Glass0ef87aa2018-07-17 13:25:44 -06001832 self.assertIn("Node '/binman/gbb': GBB is too small",
1833 str(e.exception))
1834
1835 def testGbbNoSize(self):
1836 """Test for the Chromium OS Google Binary Block having a size"""
1837 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001838 self._DoReadFileDtb('073_gbb_no_size.dts')
Simon Glass0ef87aa2018-07-17 13:25:44 -06001839 self.assertIn("Node '/binman/gbb': GBB must have a fixed size",
1840 str(e.exception))
1841
Simon Glass4f9ee832022-01-09 20:14:09 -07001842 def testGbbMissing(self):
1843 """Test that binman still produces an image if futility is missing"""
1844 entry_args = {
1845 'keydir': 'devkeys',
1846 }
1847 with test_util.capture_sys_output() as (_, stderr):
1848 self._DoTestFile('071_gbb.dts', force_missing_bintools='futility',
1849 entry_args=entry_args)
1850 err = stderr.getvalue()
Simon Glass193d3db2023-02-07 14:34:18 -07001851 self.assertRegex(err, "Image 'image'.*missing bintools.*: futility")
Simon Glass4f9ee832022-01-09 20:14:09 -07001852
Simon Glass24d0d3c2018-07-17 13:25:47 -06001853 def _HandleVblockCommand(self, pipe_list):
Simon Glass5af9ebc2021-01-06 21:35:17 -07001854 """Fake calls to the futility utility
1855
1856 The expected pipe is:
1857
1858 [('futility', 'vbutil_firmware', '--vblock',
1859 'vblock.vblock', '--keyblock', 'devkeys/firmware.keyblock',
1860 '--signprivate', 'devkeys/firmware_data_key.vbprivk',
1861 '--version', '1', '--fv', 'input.vblock', '--kernelkey',
1862 'devkeys/kernel_subkey.vbpubk', '--flags', '1')]
1863
1864 This writes to the output file (here, 'vblock.vblock'). If
1865 self._hash_data is False, it writes VBLOCK_DATA, else it writes a hash
1866 of the input data (here, 'input.vblock').
1867 """
Simon Glassfe7e9242023-02-22 12:14:49 -07001868 if 'futility' in pipe_list[0][0]:
Simon Glass24d0d3c2018-07-17 13:25:47 -06001869 fname = pipe_list[0][3]
Simon Glassa326b492018-09-14 04:57:11 -06001870 with open(fname, 'wb') as fd:
Simon Glass5af9ebc2021-01-06 21:35:17 -07001871 if self._hash_data:
1872 infile = pipe_list[0][11]
1873 m = hashlib.sha256()
Simon Glassc1aa66e2022-01-29 14:14:04 -07001874 data = tools.read_file(infile)
Simon Glass5af9ebc2021-01-06 21:35:17 -07001875 m.update(data)
1876 fd.write(m.digest())
1877 else:
1878 fd.write(VBLOCK_DATA)
1879
Simon Glass24d0d3c2018-07-17 13:25:47 -06001880 return command.CommandResult()
1881
1882 def testVblock(self):
1883 """Test for the Chromium OS Verified Boot Block"""
Simon Glass5af9ebc2021-01-06 21:35:17 -07001884 self._hash_data = False
Simon Glass24d0d3c2018-07-17 13:25:47 -06001885 command.test_result = self._HandleVblockCommand
1886 entry_args = {
1887 'keydir': 'devkeys',
1888 }
Simon Glass741f2d62018-10-01 12:22:30 -06001889 data, _, _, _ = self._DoReadFileDtb('074_vblock.dts',
Simon Glass24d0d3c2018-07-17 13:25:47 -06001890 entry_args=entry_args)
1891 expected = U_BOOT_DATA + VBLOCK_DATA + U_BOOT_DTB_DATA
1892 self.assertEqual(expected, data)
1893
1894 def testVblockNoContent(self):
1895 """Test we detect a vblock which has no content to sign"""
1896 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001897 self._DoReadFile('075_vblock_no_content.dts')
Simon Glass189f2912021-03-21 18:24:31 +13001898 self.assertIn("Node '/binman/vblock': Collection must have a 'content' "
Simon Glass24d0d3c2018-07-17 13:25:47 -06001899 'property', str(e.exception))
1900
1901 def testVblockBadPhandle(self):
1902 """Test that we detect a vblock with an invalid phandle in contents"""
1903 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001904 self._DoReadFile('076_vblock_bad_phandle.dts')
Simon Glass24d0d3c2018-07-17 13:25:47 -06001905 self.assertIn("Node '/binman/vblock': Cannot find node for phandle "
1906 '1000', str(e.exception))
1907
1908 def testVblockBadEntry(self):
1909 """Test that we detect an entry that points to a non-entry"""
1910 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001911 self._DoReadFile('077_vblock_bad_entry.dts')
Simon Glass24d0d3c2018-07-17 13:25:47 -06001912 self.assertIn("Node '/binman/vblock': Cannot find entry for node "
1913 "'other'", str(e.exception))
1914
Simon Glass5af9ebc2021-01-06 21:35:17 -07001915 def testVblockContent(self):
1916 """Test that the vblock signs the right data"""
1917 self._hash_data = True
1918 command.test_result = self._HandleVblockCommand
1919 entry_args = {
1920 'keydir': 'devkeys',
1921 }
1922 data = self._DoReadFileDtb(
1923 '189_vblock_content.dts', use_real_dtb=True, update_dtb=True,
1924 entry_args=entry_args)[0]
1925 hashlen = 32 # SHA256 hash is 32 bytes
1926 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
1927 hashval = data[-hashlen:]
1928 dtb = data[len(U_BOOT_DATA):-hashlen]
1929
1930 expected_data = U_BOOT_DATA + dtb
1931
1932 # The hashval should be a hash of the dtb
1933 m = hashlib.sha256()
1934 m.update(expected_data)
1935 expected_hashval = m.digest()
1936 self.assertEqual(expected_hashval, hashval)
1937
Simon Glass4f9ee832022-01-09 20:14:09 -07001938 def testVblockMissing(self):
1939 """Test that binman still produces an image if futility is missing"""
1940 entry_args = {
1941 'keydir': 'devkeys',
1942 }
1943 with test_util.capture_sys_output() as (_, stderr):
1944 self._DoTestFile('074_vblock.dts',
1945 force_missing_bintools='futility',
1946 entry_args=entry_args)
1947 err = stderr.getvalue()
Simon Glass193d3db2023-02-07 14:34:18 -07001948 self.assertRegex(err, "Image 'image'.*missing bintools.*: futility")
Simon Glass4f9ee832022-01-09 20:14:09 -07001949
Simon Glassb8ef5b62018-07-17 13:25:48 -06001950 def testTpl(self):
Simon Glass2090f1e2019-08-24 07:23:00 -06001951 """Test that an image with TPL and its device tree can be created"""
Simon Glassb8ef5b62018-07-17 13:25:48 -06001952 # ELF file with a '__bss_size' symbol
Simon Glass2090f1e2019-08-24 07:23:00 -06001953 self._SetupTplElf()
Simon Glass741f2d62018-10-01 12:22:30 -06001954 data = self._DoReadFile('078_u_boot_tpl.dts')
Simon Glassb8ef5b62018-07-17 13:25:48 -06001955 self.assertEqual(U_BOOT_TPL_DATA + U_BOOT_TPL_DTB_DATA, data)
1956
Simon Glass15a587c2018-07-17 13:25:51 -06001957 def testUsesPos(self):
1958 """Test that the 'pos' property cannot be used anymore"""
1959 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001960 data = self._DoReadFile('079_uses_pos.dts')
Simon Glass15a587c2018-07-17 13:25:51 -06001961 self.assertIn("Node '/binman/u-boot': Please use 'offset' instead of "
1962 "'pos'", str(e.exception))
1963
Simon Glassd178eab2018-09-14 04:57:08 -06001964 def testFillZero(self):
1965 """Test for an fill entry type with a size of 0"""
Simon Glass741f2d62018-10-01 12:22:30 -06001966 data = self._DoReadFile('080_fill_empty.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07001967 self.assertEqual(tools.get_bytes(0, 16), data)
Simon Glassd178eab2018-09-14 04:57:08 -06001968
Simon Glass0b489362018-09-14 04:57:09 -06001969 def testTextMissing(self):
1970 """Test for a text entry type where there is no text"""
1971 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001972 self._DoReadFileDtb('066_text.dts',)
Simon Glass0b489362018-09-14 04:57:09 -06001973 self.assertIn("Node '/binman/text': No value provided for text label "
1974 "'test-id'", str(e.exception))
1975
Simon Glass35b384c2018-09-14 04:57:10 -06001976 def testPackStart16Tpl(self):
1977 """Test that an image with an x86 start16 TPL region can be created"""
Simon Glass9255f3c2019-08-24 07:23:01 -06001978 data = self._DoReadFile('081_x86_start16_tpl.dts')
Simon Glass35b384c2018-09-14 04:57:10 -06001979 self.assertEqual(X86_START16_TPL_DATA, data[:len(X86_START16_TPL_DATA)])
1980
Simon Glass0bfa7b02018-09-14 04:57:12 -06001981 def testSelectImage(self):
1982 """Test that we can select which images to build"""
Simon Glasseb833d82019-04-25 21:58:34 -06001983 expected = 'Skipping images: image1'
Simon Glass0bfa7b02018-09-14 04:57:12 -06001984
Simon Glasseb833d82019-04-25 21:58:34 -06001985 # We should only get the expected message in verbose mode
Simon Glassee0c9a72019-07-08 13:18:48 -06001986 for verbosity in (0, 2):
Simon Glasseb833d82019-04-25 21:58:34 -06001987 with test_util.capture_sys_output() as (stdout, stderr):
1988 retcode = self._DoTestFile('006_dual_image.dts',
1989 verbosity=verbosity,
1990 images=['image2'])
1991 self.assertEqual(0, retcode)
1992 if verbosity:
1993 self.assertIn(expected, stdout.getvalue())
1994 else:
1995 self.assertNotIn(expected, stdout.getvalue())
1996
Simon Glassc1aa66e2022-01-29 14:14:04 -07001997 self.assertFalse(os.path.exists(tools.get_output_filename('image1.bin')))
1998 self.assertTrue(os.path.exists(tools.get_output_filename('image2.bin')))
Simon Glassf86a7362019-07-20 12:24:10 -06001999 self._CleanupOutputDir()
Simon Glass0bfa7b02018-09-14 04:57:12 -06002000
Simon Glass6ed45ba2018-09-14 04:57:24 -06002001 def testUpdateFdtAll(self):
2002 """Test that all device trees are updated with offset/size info"""
Marek Vasutfadad3a2023-07-18 07:23:58 -06002003 self._SetupSplElf()
2004 self._SetupTplElf()
Simon Glass3c081312019-07-08 14:25:26 -06002005 data = self._DoReadFileRealDtb('082_fdt_update_all.dts')
Simon Glass6ed45ba2018-09-14 04:57:24 -06002006
2007 base_expected = {
Simon Glass6ed45ba2018-09-14 04:57:24 -06002008 'offset': 0,
Simon Glass6ad24522022-02-28 07:16:54 -07002009 'image-pos': 0,
2010 'size': 2320,
Simon Glass6ed45ba2018-09-14 04:57:24 -06002011 'section:offset': 0,
Simon Glass6ad24522022-02-28 07:16:54 -07002012 'section:image-pos': 0,
2013 'section:size': 565,
2014 'section/u-boot-dtb:offset': 0,
2015 'section/u-boot-dtb:image-pos': 0,
2016 'section/u-boot-dtb:size': 565,
2017 'u-boot-spl-dtb:offset': 565,
2018 'u-boot-spl-dtb:image-pos': 565,
2019 'u-boot-spl-dtb:size': 585,
2020 'u-boot-tpl-dtb:offset': 1150,
2021 'u-boot-tpl-dtb:image-pos': 1150,
2022 'u-boot-tpl-dtb:size': 585,
2023 'u-boot-vpl-dtb:image-pos': 1735,
2024 'u-boot-vpl-dtb:offset': 1735,
2025 'u-boot-vpl-dtb:size': 585,
Simon Glass6ed45ba2018-09-14 04:57:24 -06002026 }
2027
2028 # We expect three device-tree files in the output, one after the other.
2029 # Read them in sequence. We look for an 'spl' property in the SPL tree,
2030 # and 'tpl' in the TPL tree, to make sure they are distinct from the
2031 # main U-Boot tree. All three should have the same postions and offset.
2032 start = 0
Simon Glass6ad24522022-02-28 07:16:54 -07002033 self.maxDiff = None
2034 for item in ['', 'spl', 'tpl', 'vpl']:
Simon Glass6ed45ba2018-09-14 04:57:24 -06002035 dtb = fdt.Fdt.FromData(data[start:])
2036 dtb.Scan()
Simon Glass12bb1a92019-07-20 12:23:51 -06002037 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS +
Simon Glass6ad24522022-02-28 07:16:54 -07002038 ['spl', 'tpl', 'vpl'])
Simon Glass6ed45ba2018-09-14 04:57:24 -06002039 expected = dict(base_expected)
2040 if item:
2041 expected[item] = 0
2042 self.assertEqual(expected, props)
2043 start += dtb._fdt_obj.totalsize()
2044
2045 def testUpdateFdtOutput(self):
2046 """Test that output DTB files are updated"""
2047 try:
Simon Glass741f2d62018-10-01 12:22:30 -06002048 data, dtb_data, _, _ = self._DoReadFileDtb('082_fdt_update_all.dts',
Simon Glass6ed45ba2018-09-14 04:57:24 -06002049 use_real_dtb=True, update_dtb=True, reset_dtbs=False)
2050
2051 # Unfortunately, compiling a source file always results in a file
2052 # called source.dtb (see fdt_util.EnsureCompiled()). The test
Simon Glass741f2d62018-10-01 12:22:30 -06002053 # source file (e.g. test/075_fdt_update_all.dts) thus does not enter
Simon Glass6ed45ba2018-09-14 04:57:24 -06002054 # binman as a file called u-boot.dtb. To fix this, copy the file
2055 # over to the expected place.
Simon Glass6ed45ba2018-09-14 04:57:24 -06002056 start = 0
2057 for fname in ['u-boot.dtb.out', 'spl/u-boot-spl.dtb.out',
Simon Glass6ad24522022-02-28 07:16:54 -07002058 'tpl/u-boot-tpl.dtb.out', 'vpl/u-boot-vpl.dtb.out']:
Simon Glass6ed45ba2018-09-14 04:57:24 -06002059 dtb = fdt.Fdt.FromData(data[start:])
2060 size = dtb._fdt_obj.totalsize()
Simon Glassc1aa66e2022-01-29 14:14:04 -07002061 pathname = tools.get_output_filename(os.path.split(fname)[1])
2062 outdata = tools.read_file(pathname)
Simon Glass6ed45ba2018-09-14 04:57:24 -06002063 name = os.path.split(fname)[0]
2064
2065 if name:
Simon Glass6ad24522022-02-28 07:16:54 -07002066 orig_indata = self._GetDtbContentsForSpls(dtb_data, name)
Simon Glass6ed45ba2018-09-14 04:57:24 -06002067 else:
2068 orig_indata = dtb_data
2069 self.assertNotEqual(outdata, orig_indata,
2070 "Expected output file '%s' be updated" % pathname)
2071 self.assertEqual(outdata, data[start:start + size],
2072 "Expected output file '%s' to match output image" %
2073 pathname)
2074 start += size
2075 finally:
2076 self._ResetDtbs()
2077
Simon Glass83d73c22018-09-14 04:57:26 -06002078 def _decompress(self, data):
Stefan Herbrechtsmeiercbe2e752022-08-19 16:25:28 +02002079 bintool = self.comp_bintools['lz4']
2080 return bintool.decompress(data)
Simon Glass83d73c22018-09-14 04:57:26 -06002081
2082 def testCompress(self):
2083 """Test compression of blobs"""
Simon Glassac62fba2019-07-08 13:18:53 -06002084 self._CheckLz4()
Simon Glass741f2d62018-10-01 12:22:30 -06002085 data, _, _, out_dtb_fname = self._DoReadFileDtb('083_compress.dts',
Simon Glass83d73c22018-09-14 04:57:26 -06002086 use_real_dtb=True, update_dtb=True)
2087 dtb = fdt.Fdt(out_dtb_fname)
2088 dtb.Scan()
2089 props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
2090 orig = self._decompress(data)
2091 self.assertEquals(COMPRESS_DATA, orig)
Simon Glass97c3e9a2020-10-26 17:40:15 -06002092
2093 # Do a sanity check on various fields
2094 image = control.images['image']
2095 entries = image.GetEntries()
2096 self.assertEqual(1, len(entries))
2097
2098 entry = entries['blob']
2099 self.assertEqual(COMPRESS_DATA, entry.uncomp_data)
2100 self.assertEqual(len(COMPRESS_DATA), entry.uncomp_size)
2101 orig = self._decompress(entry.data)
2102 self.assertEqual(orig, entry.uncomp_data)
2103
Simon Glass63e7ba62020-10-26 17:40:16 -06002104 self.assertEqual(image.data, entry.data)
2105
Simon Glass83d73c22018-09-14 04:57:26 -06002106 expected = {
2107 'blob:uncomp-size': len(COMPRESS_DATA),
2108 'blob:size': len(data),
2109 'size': len(data),
2110 }
2111 self.assertEqual(expected, props)
2112
Simon Glass0a98b282018-09-14 04:57:28 -06002113 def testFiles(self):
2114 """Test bringing in multiple files"""
Simon Glass741f2d62018-10-01 12:22:30 -06002115 data = self._DoReadFile('084_files.dts')
Simon Glass0a98b282018-09-14 04:57:28 -06002116 self.assertEqual(FILES_DATA, data)
2117
2118 def testFilesCompress(self):
2119 """Test bringing in multiple files and compressing them"""
Simon Glassac62fba2019-07-08 13:18:53 -06002120 self._CheckLz4()
Simon Glass741f2d62018-10-01 12:22:30 -06002121 data = self._DoReadFile('085_files_compress.dts')
Simon Glass0a98b282018-09-14 04:57:28 -06002122
2123 image = control.images['image']
2124 entries = image.GetEntries()
2125 files = entries['files']
Simon Glass8beb11e2019-07-08 14:25:47 -06002126 entries = files._entries
Simon Glass0a98b282018-09-14 04:57:28 -06002127
Simon Glassc6c10e72019-05-17 22:00:46 -06002128 orig = b''
Simon Glass0a98b282018-09-14 04:57:28 -06002129 for i in range(1, 3):
2130 key = '%d.dat' % i
2131 start = entries[key].image_pos
2132 len = entries[key].size
2133 chunk = data[start:start + len]
2134 orig += self._decompress(chunk)
2135
2136 self.assertEqual(FILES_DATA, orig)
2137
2138 def testFilesMissing(self):
2139 """Test missing files"""
2140 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06002141 data = self._DoReadFile('086_files_none.dts')
Simon Glass0a98b282018-09-14 04:57:28 -06002142 self.assertIn("Node '/binman/files': Pattern \'files/*.none\' matched "
2143 'no files', str(e.exception))
2144
2145 def testFilesNoPattern(self):
2146 """Test missing files"""
2147 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06002148 data = self._DoReadFile('087_files_no_pattern.dts')
Simon Glass0a98b282018-09-14 04:57:28 -06002149 self.assertIn("Node '/binman/files': Missing 'pattern' property",
2150 str(e.exception))
2151
Simon Glass80a66ae2022-03-05 20:18:59 -07002152 def testExtendSize(self):
2153 """Test an extending entry"""
2154 data, _, map_data, _ = self._DoReadFileDtb('088_extend_size.dts',
Simon Glassba64a0b2018-09-14 04:57:29 -06002155 map=True)
Simon Glassc1aa66e2022-01-29 14:14:04 -07002156 expect = (tools.get_bytes(ord('a'), 8) + U_BOOT_DATA +
2157 MRC_DATA + tools.get_bytes(ord('b'), 1) + U_BOOT_DATA +
2158 tools.get_bytes(ord('c'), 8) + U_BOOT_DATA +
2159 tools.get_bytes(ord('d'), 8))
Simon Glassba64a0b2018-09-14 04:57:29 -06002160 self.assertEqual(expect, data)
2161 self.assertEqual('''ImagePos Offset Size Name
Simon Glass193d3db2023-02-07 14:34:18 -0700216200000000 00000000 00000028 image
Simon Glassba64a0b2018-09-14 04:57:29 -0600216300000000 00000000 00000008 fill
216400000008 00000008 00000004 u-boot
21650000000c 0000000c 00000004 section
21660000000c 00000000 00000003 intel-mrc
216700000010 00000010 00000004 u-boot2
216800000014 00000014 0000000c section2
216900000014 00000000 00000008 fill
21700000001c 00000008 00000004 u-boot
217100000020 00000020 00000008 fill2
2172''', map_data)
2173
Simon Glass80a66ae2022-03-05 20:18:59 -07002174 def testExtendSizeBad(self):
2175 """Test an extending entry which fails to provide contents"""
Simon Glass163ed6c2018-09-14 04:57:36 -06002176 with test_util.capture_sys_output() as (stdout, stderr):
2177 with self.assertRaises(ValueError) as e:
Simon Glass80a66ae2022-03-05 20:18:59 -07002178 self._DoReadFileDtb('089_extend_size_bad.dts', map=True)
Simon Glassba64a0b2018-09-14 04:57:29 -06002179 self.assertIn("Node '/binman/_testing': Cannot obtain contents when "
2180 'expanding entry', str(e.exception))
2181
Simon Glasse0e5df92018-09-14 04:57:31 -06002182 def testHash(self):
2183 """Test hashing of the contents of an entry"""
Simon Glass741f2d62018-10-01 12:22:30 -06002184 _, _, _, out_dtb_fname = self._DoReadFileDtb('090_hash.dts',
Simon Glasse0e5df92018-09-14 04:57:31 -06002185 use_real_dtb=True, update_dtb=True)
2186 dtb = fdt.Fdt(out_dtb_fname)
2187 dtb.Scan()
2188 hash_node = dtb.GetNode('/binman/u-boot/hash').props['value']
2189 m = hashlib.sha256()
2190 m.update(U_BOOT_DATA)
Simon Glassc6c10e72019-05-17 22:00:46 -06002191 self.assertEqual(m.digest(), b''.join(hash_node.value))
Simon Glasse0e5df92018-09-14 04:57:31 -06002192
2193 def testHashNoAlgo(self):
2194 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06002195 self._DoReadFileDtb('091_hash_no_algo.dts', update_dtb=True)
Simon Glasse0e5df92018-09-14 04:57:31 -06002196 self.assertIn("Node \'/binman/u-boot\': Missing \'algo\' property for "
2197 'hash node', str(e.exception))
2198
2199 def testHashBadAlgo(self):
2200 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06002201 self._DoReadFileDtb('092_hash_bad_algo.dts', update_dtb=True)
Simon Glass8db1f992022-02-08 10:59:44 -07002202 self.assertIn("Node '/binman/u-boot': Unknown hash algorithm 'invalid'",
Simon Glasse0e5df92018-09-14 04:57:31 -06002203 str(e.exception))
2204
2205 def testHashSection(self):
2206 """Test hashing of the contents of an entry"""
Simon Glass741f2d62018-10-01 12:22:30 -06002207 _, _, _, out_dtb_fname = self._DoReadFileDtb('099_hash_section.dts',
Simon Glasse0e5df92018-09-14 04:57:31 -06002208 use_real_dtb=True, update_dtb=True)
2209 dtb = fdt.Fdt(out_dtb_fname)
2210 dtb.Scan()
2211 hash_node = dtb.GetNode('/binman/section/hash').props['value']
2212 m = hashlib.sha256()
2213 m.update(U_BOOT_DATA)
Simon Glassc1aa66e2022-01-29 14:14:04 -07002214 m.update(tools.get_bytes(ord('a'), 16))
Simon Glassc6c10e72019-05-17 22:00:46 -06002215 self.assertEqual(m.digest(), b''.join(hash_node.value))
Simon Glasse0e5df92018-09-14 04:57:31 -06002216
Simon Glassf0253632018-09-14 04:57:32 -06002217 def testPackUBootTplMicrocode(self):
2218 """Test that x86 microcode can be handled correctly in TPL
2219
2220 We expect to see the following in the image, in order:
2221 u-boot-tpl-nodtb.bin with a microcode pointer inserted at the correct
2222 place
2223 u-boot-tpl.dtb with the microcode removed
2224 the microcode
2225 """
Simon Glass2090f1e2019-08-24 07:23:00 -06002226 self._SetupTplElf('u_boot_ucode_ptr')
Simon Glass741f2d62018-10-01 12:22:30 -06002227 first, pos_and_size = self._RunMicrocodeTest('093_x86_tpl_ucode.dts',
Simon Glassf0253632018-09-14 04:57:32 -06002228 U_BOOT_TPL_NODTB_DATA)
Simon Glassc6c10e72019-05-17 22:00:46 -06002229 self.assertEqual(b'tplnodtb with microc' + pos_and_size +
2230 b'ter somewhere in here', first)
Simon Glassf0253632018-09-14 04:57:32 -06002231
Simon Glassf8f8df62018-09-14 04:57:34 -06002232 def testFmapX86(self):
2233 """Basic test of generation of a flashrom fmap"""
Simon Glass741f2d62018-10-01 12:22:30 -06002234 data = self._DoReadFile('094_fmap_x86.dts')
Simon Glassf8f8df62018-09-14 04:57:34 -06002235 fhdr, fentries = fmap_util.DecodeFmap(data[32:])
Simon Glassc1aa66e2022-01-29 14:14:04 -07002236 expected = U_BOOT_DATA + MRC_DATA + tools.get_bytes(ord('a'), 32 - 7)
Simon Glassf8f8df62018-09-14 04:57:34 -06002237 self.assertEqual(expected, data[:32])
2238 fhdr, fentries = fmap_util.DecodeFmap(data[32:])
2239
2240 self.assertEqual(0x100, fhdr.image_size)
2241
2242 self.assertEqual(0, fentries[0].offset)
2243 self.assertEqual(4, fentries[0].size)
Simon Glassc6c10e72019-05-17 22:00:46 -06002244 self.assertEqual(b'U_BOOT', fentries[0].name)
Simon Glassf8f8df62018-09-14 04:57:34 -06002245
2246 self.assertEqual(4, fentries[1].offset)
2247 self.assertEqual(3, fentries[1].size)
Simon Glassc6c10e72019-05-17 22:00:46 -06002248 self.assertEqual(b'INTEL_MRC', fentries[1].name)
Simon Glassf8f8df62018-09-14 04:57:34 -06002249
2250 self.assertEqual(32, fentries[2].offset)
2251 self.assertEqual(fmap_util.FMAP_HEADER_LEN +
2252 fmap_util.FMAP_AREA_LEN * 3, fentries[2].size)
Simon Glassc6c10e72019-05-17 22:00:46 -06002253 self.assertEqual(b'FMAP', fentries[2].name)
Simon Glassf8f8df62018-09-14 04:57:34 -06002254
2255 def testFmapX86Section(self):
2256 """Basic test of generation of a flashrom fmap"""
Simon Glass741f2d62018-10-01 12:22:30 -06002257 data = self._DoReadFile('095_fmap_x86_section.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07002258 expected = U_BOOT_DATA + MRC_DATA + tools.get_bytes(ord('b'), 32 - 7)
Simon Glassf8f8df62018-09-14 04:57:34 -06002259 self.assertEqual(expected, data[:32])
2260 fhdr, fentries = fmap_util.DecodeFmap(data[36:])
2261
Simon Glass17365752021-04-03 11:05:10 +13002262 self.assertEqual(0x180, fhdr.image_size)
2263 expect_size = fmap_util.FMAP_HEADER_LEN + fmap_util.FMAP_AREA_LEN * 4
Simon Glassc7722e82021-04-03 11:05:09 +13002264 fiter = iter(fentries)
Simon Glassf8f8df62018-09-14 04:57:34 -06002265
Simon Glassc7722e82021-04-03 11:05:09 +13002266 fentry = next(fiter)
2267 self.assertEqual(b'U_BOOT', fentry.name)
2268 self.assertEqual(0, fentry.offset)
2269 self.assertEqual(4, fentry.size)
Simon Glassf8f8df62018-09-14 04:57:34 -06002270
Simon Glassc7722e82021-04-03 11:05:09 +13002271 fentry = next(fiter)
Simon Glass17365752021-04-03 11:05:10 +13002272 self.assertEqual(b'SECTION', fentry.name)
2273 self.assertEqual(4, fentry.offset)
2274 self.assertEqual(0x20 + expect_size, fentry.size)
2275
2276 fentry = next(fiter)
Simon Glassc7722e82021-04-03 11:05:09 +13002277 self.assertEqual(b'INTEL_MRC', fentry.name)
2278 self.assertEqual(4, fentry.offset)
2279 self.assertEqual(3, fentry.size)
Simon Glassf8f8df62018-09-14 04:57:34 -06002280
Simon Glassc7722e82021-04-03 11:05:09 +13002281 fentry = next(fiter)
2282 self.assertEqual(b'FMAP', fentry.name)
2283 self.assertEqual(36, fentry.offset)
2284 self.assertEqual(expect_size, fentry.size)
Simon Glassf8f8df62018-09-14 04:57:34 -06002285
Simon Glassfe1ae3e2018-09-14 04:57:35 -06002286 def testElf(self):
2287 """Basic test of ELF entries"""
Simon Glass11ae93e2018-10-01 21:12:47 -06002288 self._SetupSplElf()
Simon Glass2090f1e2019-08-24 07:23:00 -06002289 self._SetupTplElf()
Simon Glass53e22bf2019-08-24 07:22:53 -06002290 with open(self.ElfTestFile('bss_data'), 'rb') as fd:
Simon Glassfe1ae3e2018-09-14 04:57:35 -06002291 TestFunctional._MakeInputFile('-boot', fd.read())
Simon Glass741f2d62018-10-01 12:22:30 -06002292 data = self._DoReadFile('096_elf.dts')
Simon Glassfe1ae3e2018-09-14 04:57:35 -06002293
Simon Glass093d1682019-07-08 13:18:25 -06002294 def testElfStrip(self):
Simon Glassfe1ae3e2018-09-14 04:57:35 -06002295 """Basic test of ELF entries"""
Simon Glass11ae93e2018-10-01 21:12:47 -06002296 self._SetupSplElf()
Simon Glass53e22bf2019-08-24 07:22:53 -06002297 with open(self.ElfTestFile('bss_data'), 'rb') as fd:
Simon Glassfe1ae3e2018-09-14 04:57:35 -06002298 TestFunctional._MakeInputFile('-boot', fd.read())
Simon Glass741f2d62018-10-01 12:22:30 -06002299 data = self._DoReadFile('097_elf_strip.dts')
Simon Glassfe1ae3e2018-09-14 04:57:35 -06002300
Simon Glass163ed6c2018-09-14 04:57:36 -06002301 def testPackOverlapMap(self):
2302 """Test that overlapping regions are detected"""
2303 with test_util.capture_sys_output() as (stdout, stderr):
2304 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06002305 self._DoTestFile('014_pack_overlap.dts', map=True)
Simon Glassc1aa66e2022-01-29 14:14:04 -07002306 map_fname = tools.get_output_filename('image.map')
Simon Glass163ed6c2018-09-14 04:57:36 -06002307 self.assertEqual("Wrote map file '%s' to show errors\n" % map_fname,
2308 stdout.getvalue())
2309
2310 # We should not get an inmage, but there should be a map file
Simon Glassc1aa66e2022-01-29 14:14:04 -07002311 self.assertFalse(os.path.exists(tools.get_output_filename('image.bin')))
Simon Glass163ed6c2018-09-14 04:57:36 -06002312 self.assertTrue(os.path.exists(map_fname))
Simon Glassc1aa66e2022-01-29 14:14:04 -07002313 map_data = tools.read_file(map_fname, binary=False)
Simon Glass163ed6c2018-09-14 04:57:36 -06002314 self.assertEqual('''ImagePos Offset Size Name
Simon Glass193d3db2023-02-07 14:34:18 -07002315<none> 00000000 00000008 image
Simon Glass163ed6c2018-09-14 04:57:36 -06002316<none> 00000000 00000004 u-boot
2317<none> 00000003 00000004 u-boot-align
2318''', map_data)
2319
Simon Glass093d1682019-07-08 13:18:25 -06002320 def testPackRefCode(self):
Simon Glass3ae192c2018-10-01 12:22:31 -06002321 """Test that an image with an Intel Reference code binary works"""
2322 data = self._DoReadFile('100_intel_refcode.dts')
2323 self.assertEqual(REFCODE_DATA, data[:len(REFCODE_DATA)])
2324
Simon Glass9481c802019-04-25 21:58:39 -06002325 def testSectionOffset(self):
2326 """Tests use of a section with an offset"""
2327 data, _, map_data, _ = self._DoReadFileDtb('101_sections_offset.dts',
2328 map=True)
2329 self.assertEqual('''ImagePos Offset Size Name
Simon Glass193d3db2023-02-07 14:34:18 -0700233000000000 00000000 00000038 image
Simon Glass9481c802019-04-25 21:58:39 -0600233100000004 00000004 00000010 section@0
233200000004 00000000 00000004 u-boot
233300000018 00000018 00000010 section@1
233400000018 00000000 00000004 u-boot
23350000002c 0000002c 00000004 section@2
23360000002c 00000000 00000004 u-boot
2337''', map_data)
2338 self.assertEqual(data,
Simon Glassc1aa66e2022-01-29 14:14:04 -07002339 tools.get_bytes(0x26, 4) + U_BOOT_DATA +
2340 tools.get_bytes(0x21, 12) +
2341 tools.get_bytes(0x26, 4) + U_BOOT_DATA +
2342 tools.get_bytes(0x61, 12) +
2343 tools.get_bytes(0x26, 4) + U_BOOT_DATA +
2344 tools.get_bytes(0x26, 8))
Simon Glass9481c802019-04-25 21:58:39 -06002345
Simon Glassac62fba2019-07-08 13:18:53 -06002346 def testCbfsRaw(self):
2347 """Test base handling of a Coreboot Filesystem (CBFS)
2348
2349 The exact contents of the CBFS is verified by similar tests in
2350 cbfs_util_test.py. The tests here merely check that the files added to
2351 the CBFS can be found in the final image.
2352 """
2353 data = self._DoReadFile('102_cbfs_raw.dts')
2354 size = 0xb0
2355
2356 cbfs = cbfs_util.CbfsReader(data)
2357 self.assertEqual(size, cbfs.rom_size)
2358
2359 self.assertIn('u-boot-dtb', cbfs.files)
2360 cfile = cbfs.files['u-boot-dtb']
2361 self.assertEqual(U_BOOT_DTB_DATA, cfile.data)
2362
2363 def testCbfsArch(self):
2364 """Test on non-x86 architecture"""
2365 data = self._DoReadFile('103_cbfs_raw_ppc.dts')
2366 size = 0x100
2367
2368 cbfs = cbfs_util.CbfsReader(data)
2369 self.assertEqual(size, cbfs.rom_size)
2370
2371 self.assertIn('u-boot-dtb', cbfs.files)
2372 cfile = cbfs.files['u-boot-dtb']
2373 self.assertEqual(U_BOOT_DTB_DATA, cfile.data)
2374
2375 def testCbfsStage(self):
2376 """Tests handling of a Coreboot Filesystem (CBFS)"""
2377 if not elf.ELF_TOOLS:
2378 self.skipTest('Python elftools not available')
2379 elf_fname = os.path.join(self._indir, 'cbfs-stage.elf')
2380 elf.MakeElf(elf_fname, U_BOOT_DATA, U_BOOT_DTB_DATA)
2381 size = 0xb0
2382
2383 data = self._DoReadFile('104_cbfs_stage.dts')
2384 cbfs = cbfs_util.CbfsReader(data)
2385 self.assertEqual(size, cbfs.rom_size)
2386
2387 self.assertIn('u-boot', cbfs.files)
2388 cfile = cbfs.files['u-boot']
2389 self.assertEqual(U_BOOT_DATA + U_BOOT_DTB_DATA, cfile.data)
2390
2391 def testCbfsRawCompress(self):
2392 """Test handling of compressing raw files"""
2393 self._CheckLz4()
2394 data = self._DoReadFile('105_cbfs_raw_compress.dts')
2395 size = 0x140
2396
2397 cbfs = cbfs_util.CbfsReader(data)
2398 self.assertIn('u-boot', cbfs.files)
2399 cfile = cbfs.files['u-boot']
2400 self.assertEqual(COMPRESS_DATA, cfile.data)
2401
2402 def testCbfsBadArch(self):
2403 """Test handling of a bad architecture"""
2404 with self.assertRaises(ValueError) as e:
2405 self._DoReadFile('106_cbfs_bad_arch.dts')
2406 self.assertIn("Invalid architecture 'bad-arch'", str(e.exception))
2407
2408 def testCbfsNoSize(self):
2409 """Test handling of a missing size property"""
2410 with self.assertRaises(ValueError) as e:
2411 self._DoReadFile('107_cbfs_no_size.dts')
2412 self.assertIn('entry must have a size property', str(e.exception))
2413
Simon Glasse2f04742021-11-23 11:03:54 -07002414 def testCbfsNoContents(self):
Simon Glassac62fba2019-07-08 13:18:53 -06002415 """Test handling of a CBFS entry which does not provide contentsy"""
2416 with self.assertRaises(ValueError) as e:
2417 self._DoReadFile('108_cbfs_no_contents.dts')
2418 self.assertIn('Could not complete processing of contents',
2419 str(e.exception))
2420
2421 def testCbfsBadCompress(self):
2422 """Test handling of a bad architecture"""
2423 with self.assertRaises(ValueError) as e:
2424 self._DoReadFile('109_cbfs_bad_compress.dts')
2425 self.assertIn("Invalid compression in 'u-boot': 'invalid-algo'",
2426 str(e.exception))
2427
2428 def testCbfsNamedEntries(self):
2429 """Test handling of named entries"""
2430 data = self._DoReadFile('110_cbfs_name.dts')
2431
2432 cbfs = cbfs_util.CbfsReader(data)
2433 self.assertIn('FRED', cbfs.files)
2434 cfile1 = cbfs.files['FRED']
2435 self.assertEqual(U_BOOT_DATA, cfile1.data)
2436
2437 self.assertIn('hello', cbfs.files)
2438 cfile2 = cbfs.files['hello']
2439 self.assertEqual(U_BOOT_DTB_DATA, cfile2.data)
2440
Simon Glassc5ac1382019-07-08 13:18:54 -06002441 def _SetupIfwi(self, fname):
2442 """Set up to run an IFWI test
2443
2444 Args:
2445 fname: Filename of input file to provide (fitimage.bin or ifwi.bin)
2446 """
2447 self._SetupSplElf()
Simon Glass2090f1e2019-08-24 07:23:00 -06002448 self._SetupTplElf()
Simon Glassc5ac1382019-07-08 13:18:54 -06002449
2450 # Intel Integrated Firmware Image (IFWI) file
2451 with gzip.open(self.TestFile('%s.gz' % fname), 'rb') as fd:
2452 data = fd.read()
2453 TestFunctional._MakeInputFile(fname,data)
2454
2455 def _CheckIfwi(self, data):
2456 """Check that an image with an IFWI contains the correct output
2457
2458 Args:
2459 data: Conents of output file
2460 """
Simon Glassc1aa66e2022-01-29 14:14:04 -07002461 expected_desc = tools.read_file(self.TestFile('descriptor.bin'))
Simon Glassc5ac1382019-07-08 13:18:54 -06002462 if data[:0x1000] != expected_desc:
2463 self.fail('Expected descriptor binary at start of image')
2464
2465 # We expect to find the TPL wil in subpart IBBP entry IBBL
Simon Glassc1aa66e2022-01-29 14:14:04 -07002466 image_fname = tools.get_output_filename('image.bin')
2467 tpl_fname = tools.get_output_filename('tpl.out')
Simon Glass532ae702022-01-09 20:14:01 -07002468 ifwitool = bintool.Bintool.create('ifwitool')
2469 ifwitool.extract(image_fname, 'IBBP', 'IBBL', tpl_fname)
Simon Glassc5ac1382019-07-08 13:18:54 -06002470
Simon Glassc1aa66e2022-01-29 14:14:04 -07002471 tpl_data = tools.read_file(tpl_fname)
Simon Glasse95be632019-08-24 07:22:51 -06002472 self.assertEqual(U_BOOT_TPL_DATA, tpl_data[:len(U_BOOT_TPL_DATA)])
Simon Glassc5ac1382019-07-08 13:18:54 -06002473
2474 def testPackX86RomIfwi(self):
2475 """Test that an x86 ROM with Integrated Firmware Image can be created"""
2476 self._SetupIfwi('fitimage.bin')
Simon Glass9255f3c2019-08-24 07:23:01 -06002477 data = self._DoReadFile('111_x86_rom_ifwi.dts')
Simon Glassc5ac1382019-07-08 13:18:54 -06002478 self._CheckIfwi(data)
2479
2480 def testPackX86RomIfwiNoDesc(self):
2481 """Test that an x86 ROM with IFWI can be created from an ifwi.bin file"""
2482 self._SetupIfwi('ifwi.bin')
Simon Glass9255f3c2019-08-24 07:23:01 -06002483 data = self._DoReadFile('112_x86_rom_ifwi_nodesc.dts')
Simon Glassc5ac1382019-07-08 13:18:54 -06002484 self._CheckIfwi(data)
2485
2486 def testPackX86RomIfwiNoData(self):
2487 """Test that an x86 ROM with IFWI handles missing data"""
2488 self._SetupIfwi('ifwi.bin')
2489 with self.assertRaises(ValueError) as e:
Simon Glass9255f3c2019-08-24 07:23:01 -06002490 data = self._DoReadFile('113_x86_rom_ifwi_nodata.dts')
Simon Glassc5ac1382019-07-08 13:18:54 -06002491 self.assertIn('Could not complete processing of contents',
2492 str(e.exception))
Simon Glass53af22a2018-07-17 13:25:32 -06002493
Simon Glass4f9ee832022-01-09 20:14:09 -07002494 def testIfwiMissing(self):
2495 """Test that binman still produces an image if ifwitool is missing"""
2496 self._SetupIfwi('fitimage.bin')
2497 with test_util.capture_sys_output() as (_, stderr):
2498 self._DoTestFile('111_x86_rom_ifwi.dts',
2499 force_missing_bintools='ifwitool')
2500 err = stderr.getvalue()
2501 self.assertRegex(err,
Simon Glass193d3db2023-02-07 14:34:18 -07002502 "Image 'image'.*missing bintools.*: ifwitool")
Simon Glass4f9ee832022-01-09 20:14:09 -07002503
Simon Glasse073d4e2019-07-08 13:18:56 -06002504 def testCbfsOffset(self):
2505 """Test a CBFS with files at particular offsets
2506
2507 Like all CFBS tests, this is just checking the logic that calls
2508 cbfs_util. See cbfs_util_test for fully tests (e.g. test_cbfs_offset()).
2509 """
2510 data = self._DoReadFile('114_cbfs_offset.dts')
2511 size = 0x200
2512
2513 cbfs = cbfs_util.CbfsReader(data)
2514 self.assertEqual(size, cbfs.rom_size)
2515
2516 self.assertIn('u-boot', cbfs.files)
2517 cfile = cbfs.files['u-boot']
2518 self.assertEqual(U_BOOT_DATA, cfile.data)
2519 self.assertEqual(0x40, cfile.cbfs_offset)
2520
2521 self.assertIn('u-boot-dtb', cbfs.files)
2522 cfile2 = cbfs.files['u-boot-dtb']
2523 self.assertEqual(U_BOOT_DTB_DATA, cfile2.data)
2524 self.assertEqual(0x140, cfile2.cbfs_offset)
2525
Simon Glass086cec92019-07-08 14:25:27 -06002526 def testFdtmap(self):
2527 """Test an FDT map can be inserted in the image"""
2528 data = self.data = self._DoReadFileRealDtb('115_fdtmap.dts')
2529 fdtmap_data = data[len(U_BOOT_DATA):]
2530 magic = fdtmap_data[:8]
Simon Glassb6ee0cf2019-10-31 07:43:03 -06002531 self.assertEqual(b'_FDTMAP_', magic)
Simon Glassc1aa66e2022-01-29 14:14:04 -07002532 self.assertEqual(tools.get_bytes(0, 8), fdtmap_data[8:16])
Simon Glass086cec92019-07-08 14:25:27 -06002533
2534 fdt_data = fdtmap_data[16:]
2535 dtb = fdt.Fdt.FromData(fdt_data)
2536 dtb.Scan()
Simon Glass6ccbfcd2019-07-20 12:23:47 -06002537 props = self._GetPropTree(dtb, BASE_DTB_PROPS, prefix='/')
Simon Glass086cec92019-07-08 14:25:27 -06002538 self.assertEqual({
2539 'image-pos': 0,
2540 'offset': 0,
2541 'u-boot:offset': 0,
2542 'u-boot:size': len(U_BOOT_DATA),
2543 'u-boot:image-pos': 0,
2544 'fdtmap:image-pos': 4,
2545 'fdtmap:offset': 4,
2546 'fdtmap:size': len(fdtmap_data),
2547 'size': len(data),
2548 }, props)
2549
2550 def testFdtmapNoMatch(self):
2551 """Check handling of an FDT map when the section cannot be found"""
2552 self.data = self._DoReadFileRealDtb('115_fdtmap.dts')
2553
2554 # Mangle the section name, which should cause a mismatch between the
2555 # correct FDT path and the one expected by the section
2556 image = control.images['image']
Simon Glasscf228942019-07-08 14:25:28 -06002557 image._node.path += '-suffix'
Simon Glass086cec92019-07-08 14:25:27 -06002558 entries = image.GetEntries()
2559 fdtmap = entries['fdtmap']
2560 with self.assertRaises(ValueError) as e:
2561 fdtmap._GetFdtmap()
2562 self.assertIn("Cannot locate node for path '/binman-suffix'",
2563 str(e.exception))
2564
Simon Glasscf228942019-07-08 14:25:28 -06002565 def testFdtmapHeader(self):
2566 """Test an FDT map and image header can be inserted in the image"""
2567 data = self.data = self._DoReadFileRealDtb('116_fdtmap_hdr.dts')
2568 fdtmap_pos = len(U_BOOT_DATA)
2569 fdtmap_data = data[fdtmap_pos:]
2570 fdt_data = fdtmap_data[16:]
2571 dtb = fdt.Fdt.FromData(fdt_data)
2572 fdt_size = dtb.GetFdtObj().totalsize()
2573 hdr_data = data[-8:]
Simon Glassb6ee0cf2019-10-31 07:43:03 -06002574 self.assertEqual(b'BinM', hdr_data[:4])
Simon Glasscf228942019-07-08 14:25:28 -06002575 offset = struct.unpack('<I', hdr_data[4:])[0] & 0xffffffff
2576 self.assertEqual(fdtmap_pos - 0x400, offset - (1 << 32))
2577
2578 def testFdtmapHeaderStart(self):
2579 """Test an image header can be inserted at the image start"""
2580 data = self.data = self._DoReadFileRealDtb('117_fdtmap_hdr_start.dts')
2581 fdtmap_pos = 0x100 + len(U_BOOT_DATA)
2582 hdr_data = data[:8]
Simon Glassb6ee0cf2019-10-31 07:43:03 -06002583 self.assertEqual(b'BinM', hdr_data[:4])
Simon Glasscf228942019-07-08 14:25:28 -06002584 offset = struct.unpack('<I', hdr_data[4:])[0]
2585 self.assertEqual(fdtmap_pos, offset)
2586
2587 def testFdtmapHeaderPos(self):
2588 """Test an image header can be inserted at a chosen position"""
2589 data = self.data = self._DoReadFileRealDtb('118_fdtmap_hdr_pos.dts')
2590 fdtmap_pos = 0x100 + len(U_BOOT_DATA)
2591 hdr_data = data[0x80:0x88]
Simon Glassb6ee0cf2019-10-31 07:43:03 -06002592 self.assertEqual(b'BinM', hdr_data[:4])
Simon Glasscf228942019-07-08 14:25:28 -06002593 offset = struct.unpack('<I', hdr_data[4:])[0]
2594 self.assertEqual(fdtmap_pos, offset)
2595
2596 def testHeaderMissingFdtmap(self):
2597 """Test an image header requires an fdtmap"""
2598 with self.assertRaises(ValueError) as e:
2599 self.data = self._DoReadFileRealDtb('119_fdtmap_hdr_missing.dts')
2600 self.assertIn("'image_header' section must have an 'fdtmap' sibling",
2601 str(e.exception))
2602
2603 def testHeaderNoLocation(self):
2604 """Test an image header with a no specified location is detected"""
2605 with self.assertRaises(ValueError) as e:
2606 self.data = self._DoReadFileRealDtb('120_hdr_no_location.dts')
2607 self.assertIn("Invalid location 'None', expected 'start' or 'end'",
2608 str(e.exception))
2609
Simon Glassc52c9e72019-07-08 14:25:37 -06002610 def testEntryExpand(self):
Simon Glass80a66ae2022-03-05 20:18:59 -07002611 """Test extending an entry after it is packed"""
2612 data = self._DoReadFile('121_entry_extend.dts')
Simon Glass79d3c582019-07-20 12:23:57 -06002613 self.assertEqual(b'aaa', data[:3])
2614 self.assertEqual(U_BOOT_DATA, data[3:3 + len(U_BOOT_DATA)])
2615 self.assertEqual(b'aaa', data[-3:])
Simon Glassc52c9e72019-07-08 14:25:37 -06002616
Simon Glass80a66ae2022-03-05 20:18:59 -07002617 def testEntryExtendBad(self):
2618 """Test extending an entry after it is packed, twice"""
Simon Glassc52c9e72019-07-08 14:25:37 -06002619 with self.assertRaises(ValueError) as e:
Simon Glass80a66ae2022-03-05 20:18:59 -07002620 self._DoReadFile('122_entry_extend_twice.dts')
Simon Glass61ec04f2019-07-20 12:23:58 -06002621 self.assertIn("Image '/binman': Entries changed size after packing",
Simon Glassc52c9e72019-07-08 14:25:37 -06002622 str(e.exception))
2623
Simon Glass80a66ae2022-03-05 20:18:59 -07002624 def testEntryExtendSection(self):
2625 """Test extending an entry within a section after it is packed"""
2626 data = self._DoReadFile('123_entry_extend_section.dts')
Simon Glass79d3c582019-07-20 12:23:57 -06002627 self.assertEqual(b'aaa', data[:3])
2628 self.assertEqual(U_BOOT_DATA, data[3:3 + len(U_BOOT_DATA)])
2629 self.assertEqual(b'aaa', data[-3:])
Simon Glassc52c9e72019-07-08 14:25:37 -06002630
Simon Glass6c223fd2019-07-08 14:25:38 -06002631 def testCompressDtb(self):
2632 """Test that compress of device-tree files is supported"""
2633 self._CheckLz4()
2634 data = self.data = self._DoReadFileRealDtb('124_compress_dtb.dts')
2635 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
2636 comp_data = data[len(U_BOOT_DATA):]
2637 orig = self._decompress(comp_data)
2638 dtb = fdt.Fdt.FromData(orig)
2639 dtb.Scan()
2640 props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
2641 expected = {
2642 'u-boot:size': len(U_BOOT_DATA),
2643 'u-boot-dtb:uncomp-size': len(orig),
2644 'u-boot-dtb:size': len(comp_data),
2645 'size': len(data),
2646 }
2647 self.assertEqual(expected, props)
2648
Simon Glass69f7cb32019-07-08 14:25:41 -06002649 def testCbfsUpdateFdt(self):
2650 """Test that we can update the device tree with CBFS offset/size info"""
2651 self._CheckLz4()
2652 data, _, _, out_dtb_fname = self._DoReadFileDtb('125_cbfs_update.dts',
2653 update_dtb=True)
2654 dtb = fdt.Fdt(out_dtb_fname)
2655 dtb.Scan()
Simon Glass6ccbfcd2019-07-20 12:23:47 -06002656 props = self._GetPropTree(dtb, BASE_DTB_PROPS + ['uncomp-size'])
Simon Glass69f7cb32019-07-08 14:25:41 -06002657 del props['cbfs/u-boot:size']
2658 self.assertEqual({
2659 'offset': 0,
2660 'size': len(data),
2661 'image-pos': 0,
2662 'cbfs:offset': 0,
2663 'cbfs:size': len(data),
2664 'cbfs:image-pos': 0,
2665 'cbfs/u-boot:offset': 0x38,
2666 'cbfs/u-boot:uncomp-size': len(U_BOOT_DATA),
2667 'cbfs/u-boot:image-pos': 0x38,
2668 'cbfs/u-boot-dtb:offset': 0xb8,
2669 'cbfs/u-boot-dtb:size': len(U_BOOT_DATA),
2670 'cbfs/u-boot-dtb:image-pos': 0xb8,
2671 }, props)
2672
Simon Glass8a1ad062019-07-08 14:25:42 -06002673 def testCbfsBadType(self):
2674 """Test an image header with a no specified location is detected"""
2675 with self.assertRaises(ValueError) as e:
2676 self._DoReadFile('126_cbfs_bad_type.dts')
2677 self.assertIn("Unknown cbfs-type 'badtype'", str(e.exception))
2678
Simon Glass41b8ba02019-07-08 14:25:43 -06002679 def testList(self):
2680 """Test listing the files in an image"""
2681 self._CheckLz4()
2682 data = self._DoReadFile('127_list.dts')
2683 image = control.images['image']
2684 entries = image.BuildEntryList()
2685 self.assertEqual(7, len(entries))
2686
2687 ent = entries[0]
2688 self.assertEqual(0, ent.indent)
Simon Glass193d3db2023-02-07 14:34:18 -07002689 self.assertEqual('image', ent.name)
Simon Glass41b8ba02019-07-08 14:25:43 -06002690 self.assertEqual('section', ent.etype)
2691 self.assertEqual(len(data), ent.size)
2692 self.assertEqual(0, ent.image_pos)
2693 self.assertEqual(None, ent.uncomp_size)
Simon Glass8beb11e2019-07-08 14:25:47 -06002694 self.assertEqual(0, ent.offset)
Simon Glass41b8ba02019-07-08 14:25:43 -06002695
2696 ent = entries[1]
2697 self.assertEqual(1, ent.indent)
2698 self.assertEqual('u-boot', ent.name)
2699 self.assertEqual('u-boot', ent.etype)
2700 self.assertEqual(len(U_BOOT_DATA), ent.size)
2701 self.assertEqual(0, ent.image_pos)
2702 self.assertEqual(None, ent.uncomp_size)
2703 self.assertEqual(0, ent.offset)
2704
2705 ent = entries[2]
2706 self.assertEqual(1, ent.indent)
2707 self.assertEqual('section', ent.name)
2708 self.assertEqual('section', ent.etype)
2709 section_size = ent.size
2710 self.assertEqual(0x100, ent.image_pos)
2711 self.assertEqual(None, ent.uncomp_size)
Simon Glass8beb11e2019-07-08 14:25:47 -06002712 self.assertEqual(0x100, ent.offset)
Simon Glass41b8ba02019-07-08 14:25:43 -06002713
2714 ent = entries[3]
2715 self.assertEqual(2, ent.indent)
2716 self.assertEqual('cbfs', ent.name)
2717 self.assertEqual('cbfs', ent.etype)
2718 self.assertEqual(0x400, ent.size)
2719 self.assertEqual(0x100, ent.image_pos)
2720 self.assertEqual(None, ent.uncomp_size)
2721 self.assertEqual(0, ent.offset)
2722
2723 ent = entries[4]
2724 self.assertEqual(3, ent.indent)
2725 self.assertEqual('u-boot', ent.name)
2726 self.assertEqual('u-boot', ent.etype)
2727 self.assertEqual(len(U_BOOT_DATA), ent.size)
2728 self.assertEqual(0x138, ent.image_pos)
2729 self.assertEqual(None, ent.uncomp_size)
2730 self.assertEqual(0x38, ent.offset)
2731
2732 ent = entries[5]
2733 self.assertEqual(3, ent.indent)
2734 self.assertEqual('u-boot-dtb', ent.name)
2735 self.assertEqual('text', ent.etype)
2736 self.assertGreater(len(COMPRESS_DATA), ent.size)
2737 self.assertEqual(0x178, ent.image_pos)
2738 self.assertEqual(len(COMPRESS_DATA), ent.uncomp_size)
2739 self.assertEqual(0x78, ent.offset)
2740
2741 ent = entries[6]
2742 self.assertEqual(2, ent.indent)
2743 self.assertEqual('u-boot-dtb', ent.name)
2744 self.assertEqual('u-boot-dtb', ent.etype)
2745 self.assertEqual(0x500, ent.image_pos)
2746 self.assertEqual(len(U_BOOT_DTB_DATA), ent.uncomp_size)
2747 dtb_size = ent.size
2748 # Compressing this data expands it since headers are added
2749 self.assertGreater(dtb_size, len(U_BOOT_DTB_DATA))
2750 self.assertEqual(0x400, ent.offset)
2751
2752 self.assertEqual(len(data), 0x100 + section_size)
2753 self.assertEqual(section_size, 0x400 + dtb_size)
2754
Simon Glasse1925fa2019-07-08 14:25:44 -06002755 def testFindFdtmap(self):
2756 """Test locating an FDT map in an image"""
2757 self._CheckLz4()
2758 data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
2759 image = control.images['image']
2760 entries = image.GetEntries()
2761 entry = entries['fdtmap']
2762 self.assertEqual(entry.image_pos, fdtmap.LocateFdtmap(data))
2763
2764 def testFindFdtmapMissing(self):
2765 """Test failing to locate an FDP map"""
2766 data = self._DoReadFile('005_simple.dts')
2767 self.assertEqual(None, fdtmap.LocateFdtmap(data))
2768
Simon Glass2d260032019-07-08 14:25:45 -06002769 def testFindImageHeader(self):
2770 """Test locating a image header"""
2771 self._CheckLz4()
Simon Glassffded752019-07-08 14:25:46 -06002772 data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
Simon Glass2d260032019-07-08 14:25:45 -06002773 image = control.images['image']
2774 entries = image.GetEntries()
2775 entry = entries['fdtmap']
2776 # The header should point to the FDT map
2777 self.assertEqual(entry.image_pos, image_header.LocateHeaderOffset(data))
2778
2779 def testFindImageHeaderStart(self):
2780 """Test locating a image header located at the start of an image"""
Simon Glassffded752019-07-08 14:25:46 -06002781 data = self.data = self._DoReadFileRealDtb('117_fdtmap_hdr_start.dts')
Simon Glass2d260032019-07-08 14:25:45 -06002782 image = control.images['image']
2783 entries = image.GetEntries()
2784 entry = entries['fdtmap']
2785 # The header should point to the FDT map
2786 self.assertEqual(entry.image_pos, image_header.LocateHeaderOffset(data))
2787
2788 def testFindImageHeaderMissing(self):
2789 """Test failing to locate an image header"""
2790 data = self._DoReadFile('005_simple.dts')
2791 self.assertEqual(None, image_header.LocateHeaderOffset(data))
2792
Simon Glassffded752019-07-08 14:25:46 -06002793 def testReadImage(self):
2794 """Test reading an image and accessing its FDT map"""
2795 self._CheckLz4()
2796 data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07002797 image_fname = tools.get_output_filename('image.bin')
Simon Glassffded752019-07-08 14:25:46 -06002798 orig_image = control.images['image']
2799 image = Image.FromFile(image_fname)
2800 self.assertEqual(orig_image.GetEntries().keys(),
2801 image.GetEntries().keys())
2802
2803 orig_entry = orig_image.GetEntries()['fdtmap']
2804 entry = image.GetEntries()['fdtmap']
2805 self.assertEquals(orig_entry.offset, entry.offset)
2806 self.assertEquals(orig_entry.size, entry.size)
2807 self.assertEquals(orig_entry.image_pos, entry.image_pos)
2808
2809 def testReadImageNoHeader(self):
2810 """Test accessing an image's FDT map without an image header"""
2811 self._CheckLz4()
2812 data = self._DoReadFileRealDtb('129_decode_image_nohdr.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07002813 image_fname = tools.get_output_filename('image.bin')
Simon Glassffded752019-07-08 14:25:46 -06002814 image = Image.FromFile(image_fname)
2815 self.assertTrue(isinstance(image, Image))
Simon Glass10f9d002019-07-20 12:23:50 -06002816 self.assertEqual('image', image.image_name[-5:])
Simon Glassffded752019-07-08 14:25:46 -06002817
2818 def testReadImageFail(self):
2819 """Test failing to read an image image's FDT map"""
2820 self._DoReadFile('005_simple.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07002821 image_fname = tools.get_output_filename('image.bin')
Simon Glassffded752019-07-08 14:25:46 -06002822 with self.assertRaises(ValueError) as e:
2823 image = Image.FromFile(image_fname)
2824 self.assertIn("Cannot find FDT map in image", str(e.exception))
Simon Glasse073d4e2019-07-08 13:18:56 -06002825
Simon Glass61f564d2019-07-08 14:25:48 -06002826 def testListCmd(self):
2827 """Test listing the files in an image using an Fdtmap"""
2828 self._CheckLz4()
2829 data = self._DoReadFileRealDtb('130_list_fdtmap.dts')
2830
2831 # lz4 compression size differs depending on the version
2832 image = control.images['image']
2833 entries = image.GetEntries()
2834 section_size = entries['section'].size
2835 fdt_size = entries['section'].GetEntries()['u-boot-dtb'].size
2836 fdtmap_offset = entries['fdtmap'].offset
2837
Simon Glassf86a7362019-07-20 12:24:10 -06002838 try:
2839 tmpdir, updated_fname = self._SetupImageInTmpdir()
2840 with test_util.capture_sys_output() as (stdout, stderr):
2841 self._DoBinman('ls', '-i', updated_fname)
2842 finally:
2843 shutil.rmtree(tmpdir)
Simon Glass61f564d2019-07-08 14:25:48 -06002844 lines = stdout.getvalue().splitlines()
2845 expected = [
2846'Name Image-pos Size Entry-type Offset Uncomp-size',
2847'----------------------------------------------------------------------',
Simon Glass193d3db2023-02-07 14:34:18 -07002848'image 0 c00 section 0',
Simon Glass61f564d2019-07-08 14:25:48 -06002849' u-boot 0 4 u-boot 0',
2850' section 100 %x section 100' % section_size,
2851' cbfs 100 400 cbfs 0',
2852' u-boot 138 4 u-boot 38',
Simon Glassb6ee0cf2019-10-31 07:43:03 -06002853' u-boot-dtb 180 105 u-boot-dtb 80 3c9',
Simon Glass61f564d2019-07-08 14:25:48 -06002854' u-boot-dtb 500 %x u-boot-dtb 400 3c9' % fdt_size,
Simon Glassb6ee0cf2019-10-31 07:43:03 -06002855' fdtmap %x 3bd fdtmap %x' %
Simon Glass61f564d2019-07-08 14:25:48 -06002856 (fdtmap_offset, fdtmap_offset),
2857' image-header bf8 8 image-header bf8',
2858 ]
2859 self.assertEqual(expected, lines)
2860
2861 def testListCmdFail(self):
2862 """Test failing to list an image"""
2863 self._DoReadFile('005_simple.dts')
Simon Glassf86a7362019-07-20 12:24:10 -06002864 try:
2865 tmpdir, updated_fname = self._SetupImageInTmpdir()
2866 with self.assertRaises(ValueError) as e:
2867 self._DoBinman('ls', '-i', updated_fname)
2868 finally:
2869 shutil.rmtree(tmpdir)
Simon Glass61f564d2019-07-08 14:25:48 -06002870 self.assertIn("Cannot find FDT map in image", str(e.exception))
2871
2872 def _RunListCmd(self, paths, expected):
2873 """List out entries and check the result
2874
2875 Args:
2876 paths: List of paths to pass to the list command
2877 expected: Expected list of filenames to be returned, in order
2878 """
2879 self._CheckLz4()
2880 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07002881 image_fname = tools.get_output_filename('image.bin')
Simon Glass61f564d2019-07-08 14:25:48 -06002882 image = Image.FromFile(image_fname)
2883 lines = image.GetListEntries(paths)[1]
2884 files = [line[0].strip() for line in lines[1:]]
2885 self.assertEqual(expected, files)
2886
2887 def testListCmdSection(self):
2888 """Test listing the files in a section"""
2889 self._RunListCmd(['section'],
2890 ['section', 'cbfs', 'u-boot', 'u-boot-dtb', 'u-boot-dtb'])
2891
2892 def testListCmdFile(self):
2893 """Test listing a particular file"""
2894 self._RunListCmd(['*u-boot-dtb'], ['u-boot-dtb', 'u-boot-dtb'])
2895
2896 def testListCmdWildcard(self):
2897 """Test listing a wildcarded file"""
2898 self._RunListCmd(['*boot*'],
2899 ['u-boot', 'u-boot', 'u-boot-dtb', 'u-boot-dtb'])
2900
2901 def testListCmdWildcardMulti(self):
2902 """Test listing a wildcarded file"""
2903 self._RunListCmd(['*cb*', '*head*'],
2904 ['cbfs', 'u-boot', 'u-boot-dtb', 'image-header'])
2905
2906 def testListCmdEmpty(self):
2907 """Test listing a wildcarded file"""
2908 self._RunListCmd(['nothing'], [])
2909
2910 def testListCmdPath(self):
2911 """Test listing the files in a sub-entry of a section"""
2912 self._RunListCmd(['section/cbfs'], ['cbfs', 'u-boot', 'u-boot-dtb'])
2913
Simon Glassf667e452019-07-08 14:25:50 -06002914 def _RunExtractCmd(self, entry_name, decomp=True):
2915 """Extract an entry from an image
2916
2917 Args:
2918 entry_name: Entry name to extract
2919 decomp: True to decompress the data if compressed, False to leave
2920 it in its raw uncompressed format
2921
2922 Returns:
2923 data from entry
2924 """
2925 self._CheckLz4()
2926 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07002927 image_fname = tools.get_output_filename('image.bin')
Simon Glassf667e452019-07-08 14:25:50 -06002928 return control.ReadEntry(image_fname, entry_name, decomp)
2929
2930 def testExtractSimple(self):
2931 """Test extracting a single file"""
2932 data = self._RunExtractCmd('u-boot')
2933 self.assertEqual(U_BOOT_DATA, data)
2934
Simon Glass71ce0ba2019-07-08 14:25:52 -06002935 def testExtractSection(self):
2936 """Test extracting the files in a section"""
2937 data = self._RunExtractCmd('section')
2938 cbfs_data = data[:0x400]
2939 cbfs = cbfs_util.CbfsReader(cbfs_data)
Simon Glassb6ee0cf2019-10-31 07:43:03 -06002940 self.assertEqual(['u-boot', 'u-boot-dtb', ''], list(cbfs.files.keys()))
Simon Glass71ce0ba2019-07-08 14:25:52 -06002941 dtb_data = data[0x400:]
2942 dtb = self._decompress(dtb_data)
2943 self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
2944
2945 def testExtractCompressed(self):
2946 """Test extracting compressed data"""
2947 data = self._RunExtractCmd('section/u-boot-dtb')
2948 self.assertEqual(EXTRACT_DTB_SIZE, len(data))
2949
2950 def testExtractRaw(self):
2951 """Test extracting compressed data without decompressing it"""
2952 data = self._RunExtractCmd('section/u-boot-dtb', decomp=False)
2953 dtb = self._decompress(data)
2954 self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
2955
2956 def testExtractCbfs(self):
2957 """Test extracting CBFS data"""
2958 data = self._RunExtractCmd('section/cbfs/u-boot')
2959 self.assertEqual(U_BOOT_DATA, data)
2960
2961 def testExtractCbfsCompressed(self):
2962 """Test extracting CBFS compressed data"""
2963 data = self._RunExtractCmd('section/cbfs/u-boot-dtb')
2964 self.assertEqual(EXTRACT_DTB_SIZE, len(data))
2965
2966 def testExtractCbfsRaw(self):
2967 """Test extracting CBFS compressed data without decompressing it"""
Stefan Herbrechtsmeiercbe2e752022-08-19 16:25:28 +02002968 bintool = self.comp_bintools['lzma_alone']
2969 self._CheckBintool(bintool)
Simon Glass71ce0ba2019-07-08 14:25:52 -06002970 data = self._RunExtractCmd('section/cbfs/u-boot-dtb', decomp=False)
Stefan Herbrechtsmeiercbe2e752022-08-19 16:25:28 +02002971 dtb = bintool.decompress(data)
Simon Glass71ce0ba2019-07-08 14:25:52 -06002972 self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
2973
Simon Glassf667e452019-07-08 14:25:50 -06002974 def testExtractBadEntry(self):
2975 """Test extracting a bad section path"""
2976 with self.assertRaises(ValueError) as e:
2977 self._RunExtractCmd('section/does-not-exist')
2978 self.assertIn("Entry 'does-not-exist' not found in '/section'",
2979 str(e.exception))
2980
2981 def testExtractMissingFile(self):
2982 """Test extracting file that does not exist"""
2983 with self.assertRaises(IOError) as e:
2984 control.ReadEntry('missing-file', 'name')
2985
2986 def testExtractBadFile(self):
2987 """Test extracting an invalid file"""
2988 fname = os.path.join(self._indir, 'badfile')
Simon Glassc1aa66e2022-01-29 14:14:04 -07002989 tools.write_file(fname, b'')
Simon Glassf667e452019-07-08 14:25:50 -06002990 with self.assertRaises(ValueError) as e:
2991 control.ReadEntry(fname, 'name')
2992
Simon Glass71ce0ba2019-07-08 14:25:52 -06002993 def testExtractCmd(self):
2994 """Test extracting a file fron an image on the command line"""
2995 self._CheckLz4()
2996 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass71ce0ba2019-07-08 14:25:52 -06002997 fname = os.path.join(self._indir, 'output.extact')
Simon Glassf86a7362019-07-20 12:24:10 -06002998 try:
2999 tmpdir, updated_fname = self._SetupImageInTmpdir()
3000 with test_util.capture_sys_output() as (stdout, stderr):
3001 self._DoBinman('extract', '-i', updated_fname, 'u-boot',
3002 '-f', fname)
3003 finally:
3004 shutil.rmtree(tmpdir)
Simon Glassc1aa66e2022-01-29 14:14:04 -07003005 data = tools.read_file(fname)
Simon Glass71ce0ba2019-07-08 14:25:52 -06003006 self.assertEqual(U_BOOT_DATA, data)
3007
3008 def testExtractOneEntry(self):
3009 """Test extracting a single entry fron an image """
3010 self._CheckLz4()
3011 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07003012 image_fname = tools.get_output_filename('image.bin')
Simon Glass71ce0ba2019-07-08 14:25:52 -06003013 fname = os.path.join(self._indir, 'output.extact')
3014 control.ExtractEntries(image_fname, fname, None, ['u-boot'])
Simon Glassc1aa66e2022-01-29 14:14:04 -07003015 data = tools.read_file(fname)
Simon Glass71ce0ba2019-07-08 14:25:52 -06003016 self.assertEqual(U_BOOT_DATA, data)
3017
3018 def _CheckExtractOutput(self, decomp):
3019 """Helper to test file output with and without decompression
3020
3021 Args:
3022 decomp: True to decompress entry data, False to output it raw
3023 """
3024 def _CheckPresent(entry_path, expect_data, expect_size=None):
3025 """Check and remove expected file
3026
3027 This checks the data/size of a file and removes the file both from
3028 the outfiles set and from the output directory. Once all files are
3029 processed, both the set and directory should be empty.
3030
3031 Args:
3032 entry_path: Entry path
3033 expect_data: Data to expect in file, or None to skip check
3034 expect_size: Size of data to expect in file, or None to skip
3035 """
3036 path = os.path.join(outdir, entry_path)
Simon Glassc1aa66e2022-01-29 14:14:04 -07003037 data = tools.read_file(path)
Simon Glass71ce0ba2019-07-08 14:25:52 -06003038 os.remove(path)
3039 if expect_data:
3040 self.assertEqual(expect_data, data)
3041 elif expect_size:
3042 self.assertEqual(expect_size, len(data))
3043 outfiles.remove(path)
3044
3045 def _CheckDirPresent(name):
3046 """Remove expected directory
3047
3048 This gives an error if the directory does not exist as expected
3049
3050 Args:
3051 name: Name of directory to remove
3052 """
3053 path = os.path.join(outdir, name)
3054 os.rmdir(path)
3055
3056 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07003057 image_fname = tools.get_output_filename('image.bin')
Simon Glass71ce0ba2019-07-08 14:25:52 -06003058 outdir = os.path.join(self._indir, 'extract')
3059 einfos = control.ExtractEntries(image_fname, None, outdir, [], decomp)
3060
3061 # Create a set of all file that were output (should be 9)
3062 outfiles = set()
3063 for root, dirs, files in os.walk(outdir):
3064 outfiles |= set([os.path.join(root, fname) for fname in files])
3065 self.assertEqual(9, len(outfiles))
3066 self.assertEqual(9, len(einfos))
3067
3068 image = control.images['image']
3069 entries = image.GetEntries()
3070
3071 # Check the 9 files in various ways
3072 section = entries['section']
3073 section_entries = section.GetEntries()
3074 cbfs_entries = section_entries['cbfs'].GetEntries()
3075 _CheckPresent('u-boot', U_BOOT_DATA)
3076 _CheckPresent('section/cbfs/u-boot', U_BOOT_DATA)
3077 dtb_len = EXTRACT_DTB_SIZE
3078 if not decomp:
3079 dtb_len = cbfs_entries['u-boot-dtb'].size
3080 _CheckPresent('section/cbfs/u-boot-dtb', None, dtb_len)
3081 if not decomp:
3082 dtb_len = section_entries['u-boot-dtb'].size
3083 _CheckPresent('section/u-boot-dtb', None, dtb_len)
3084
3085 fdtmap = entries['fdtmap']
3086 _CheckPresent('fdtmap', fdtmap.data)
3087 hdr = entries['image-header']
3088 _CheckPresent('image-header', hdr.data)
3089
3090 _CheckPresent('section/root', section.data)
3091 cbfs = section_entries['cbfs']
3092 _CheckPresent('section/cbfs/root', cbfs.data)
Simon Glassc1aa66e2022-01-29 14:14:04 -07003093 data = tools.read_file(image_fname)
Simon Glass71ce0ba2019-07-08 14:25:52 -06003094 _CheckPresent('root', data)
3095
3096 # There should be no files left. Remove all the directories to check.
3097 # If there are any files/dirs remaining, one of these checks will fail.
3098 self.assertEqual(0, len(outfiles))
3099 _CheckDirPresent('section/cbfs')
3100 _CheckDirPresent('section')
3101 _CheckDirPresent('')
3102 self.assertFalse(os.path.exists(outdir))
3103
3104 def testExtractAllEntries(self):
3105 """Test extracting all entries"""
3106 self._CheckLz4()
3107 self._CheckExtractOutput(decomp=True)
3108
3109 def testExtractAllEntriesRaw(self):
3110 """Test extracting all entries without decompressing them"""
3111 self._CheckLz4()
3112 self._CheckExtractOutput(decomp=False)
3113
3114 def testExtractSelectedEntries(self):
3115 """Test extracting some entries"""
3116 self._CheckLz4()
3117 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07003118 image_fname = tools.get_output_filename('image.bin')
Simon Glass71ce0ba2019-07-08 14:25:52 -06003119 outdir = os.path.join(self._indir, 'extract')
3120 einfos = control.ExtractEntries(image_fname, None, outdir,
3121 ['*cb*', '*head*'])
3122
3123 # File output is tested by testExtractAllEntries(), so just check that
3124 # the expected entries are selected
3125 names = [einfo.name for einfo in einfos]
3126 self.assertEqual(names,
3127 ['cbfs', 'u-boot', 'u-boot-dtb', 'image-header'])
3128
3129 def testExtractNoEntryPaths(self):
3130 """Test extracting some entries"""
3131 self._CheckLz4()
3132 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07003133 image_fname = tools.get_output_filename('image.bin')
Simon Glass71ce0ba2019-07-08 14:25:52 -06003134 with self.assertRaises(ValueError) as e:
3135 control.ExtractEntries(image_fname, 'fname', None, [])
Simon Glassbb5edc12019-07-20 12:24:14 -06003136 self.assertIn('Must specify an entry path to write with -f',
Simon Glass71ce0ba2019-07-08 14:25:52 -06003137 str(e.exception))
3138
3139 def testExtractTooManyEntryPaths(self):
3140 """Test extracting some entries"""
3141 self._CheckLz4()
3142 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07003143 image_fname = tools.get_output_filename('image.bin')
Simon Glass71ce0ba2019-07-08 14:25:52 -06003144 with self.assertRaises(ValueError) as e:
3145 control.ExtractEntries(image_fname, 'fname', None, ['a', 'b'])
Simon Glassbb5edc12019-07-20 12:24:14 -06003146 self.assertIn('Must specify exactly one entry path to write with -f',
Simon Glass71ce0ba2019-07-08 14:25:52 -06003147 str(e.exception))
3148
Simon Glasse2705fa2019-07-08 14:25:53 -06003149 def testPackAlignSection(self):
3150 """Test that sections can have alignment"""
3151 self._DoReadFile('131_pack_align_section.dts')
3152
3153 self.assertIn('image', control.images)
3154 image = control.images['image']
3155 entries = image.GetEntries()
3156 self.assertEqual(3, len(entries))
3157
3158 # First u-boot
3159 self.assertIn('u-boot', entries)
3160 entry = entries['u-boot']
3161 self.assertEqual(0, entry.offset)
3162 self.assertEqual(0, entry.image_pos)
3163 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3164 self.assertEqual(len(U_BOOT_DATA), entry.size)
3165
3166 # Section0
3167 self.assertIn('section0', entries)
3168 section0 = entries['section0']
3169 self.assertEqual(0x10, section0.offset)
3170 self.assertEqual(0x10, section0.image_pos)
3171 self.assertEqual(len(U_BOOT_DATA), section0.size)
3172
3173 # Second u-boot
3174 section_entries = section0.GetEntries()
3175 self.assertIn('u-boot', section_entries)
3176 entry = section_entries['u-boot']
3177 self.assertEqual(0, entry.offset)
3178 self.assertEqual(0x10, entry.image_pos)
3179 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3180 self.assertEqual(len(U_BOOT_DATA), entry.size)
3181
3182 # Section1
3183 self.assertIn('section1', entries)
3184 section1 = entries['section1']
3185 self.assertEqual(0x14, section1.offset)
3186 self.assertEqual(0x14, section1.image_pos)
3187 self.assertEqual(0x20, section1.size)
3188
3189 # Second u-boot
3190 section_entries = section1.GetEntries()
3191 self.assertIn('u-boot', section_entries)
3192 entry = section_entries['u-boot']
3193 self.assertEqual(0, entry.offset)
3194 self.assertEqual(0x14, entry.image_pos)
3195 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3196 self.assertEqual(len(U_BOOT_DATA), entry.size)
3197
3198 # Section2
3199 self.assertIn('section2', section_entries)
3200 section2 = section_entries['section2']
3201 self.assertEqual(0x4, section2.offset)
3202 self.assertEqual(0x18, section2.image_pos)
3203 self.assertEqual(4, section2.size)
3204
3205 # Third u-boot
3206 section_entries = section2.GetEntries()
3207 self.assertIn('u-boot', section_entries)
3208 entry = section_entries['u-boot']
3209 self.assertEqual(0, entry.offset)
3210 self.assertEqual(0x18, entry.image_pos)
3211 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3212 self.assertEqual(len(U_BOOT_DATA), entry.size)
3213
Simon Glass51014aa2019-07-20 12:23:56 -06003214 def _RunReplaceCmd(self, entry_name, data, decomp=True, allow_resize=True,
3215 dts='132_replace.dts'):
Simon Glass10f9d002019-07-20 12:23:50 -06003216 """Replace an entry in an image
3217
3218 This writes the entry data to update it, then opens the updated file and
3219 returns the value that it now finds there.
3220
3221 Args:
3222 entry_name: Entry name to replace
3223 data: Data to replace it with
3224 decomp: True to compress the data if needed, False if data is
3225 already compressed so should be used as is
Simon Glass51014aa2019-07-20 12:23:56 -06003226 allow_resize: True to allow entries to change size, False to raise
3227 an exception
Simon Glass10f9d002019-07-20 12:23:50 -06003228
3229 Returns:
3230 Tuple:
3231 data from entry
3232 data from fdtmap (excluding header)
Simon Glass51014aa2019-07-20 12:23:56 -06003233 Image object that was modified
Simon Glass10f9d002019-07-20 12:23:50 -06003234 """
Simon Glass51014aa2019-07-20 12:23:56 -06003235 dtb_data = self._DoReadFileDtb(dts, use_real_dtb=True,
Simon Glass10f9d002019-07-20 12:23:50 -06003236 update_dtb=True)[1]
3237
3238 self.assertIn('image', control.images)
3239 image = control.images['image']
3240 entries = image.GetEntries()
3241 orig_dtb_data = entries['u-boot-dtb'].data
3242 orig_fdtmap_data = entries['fdtmap'].data
3243
Simon Glassc1aa66e2022-01-29 14:14:04 -07003244 image_fname = tools.get_output_filename('image.bin')
3245 updated_fname = tools.get_output_filename('image-updated.bin')
3246 tools.write_file(updated_fname, tools.read_file(image_fname))
Simon Glass51014aa2019-07-20 12:23:56 -06003247 image = control.WriteEntry(updated_fname, entry_name, data, decomp,
3248 allow_resize)
Simon Glass10f9d002019-07-20 12:23:50 -06003249 data = control.ReadEntry(updated_fname, entry_name, decomp)
3250
Simon Glass51014aa2019-07-20 12:23:56 -06003251 # The DT data should not change unless resized:
3252 if not allow_resize:
3253 new_dtb_data = entries['u-boot-dtb'].data
3254 self.assertEqual(new_dtb_data, orig_dtb_data)
3255 new_fdtmap_data = entries['fdtmap'].data
3256 self.assertEqual(new_fdtmap_data, orig_fdtmap_data)
Simon Glass10f9d002019-07-20 12:23:50 -06003257
Simon Glass51014aa2019-07-20 12:23:56 -06003258 return data, orig_fdtmap_data[fdtmap.FDTMAP_HDR_LEN:], image
Simon Glass10f9d002019-07-20 12:23:50 -06003259
3260 def testReplaceSimple(self):
3261 """Test replacing a single file"""
3262 expected = b'x' * len(U_BOOT_DATA)
Simon Glass51014aa2019-07-20 12:23:56 -06003263 data, expected_fdtmap, _ = self._RunReplaceCmd('u-boot', expected,
3264 allow_resize=False)
Simon Glass10f9d002019-07-20 12:23:50 -06003265 self.assertEqual(expected, data)
3266
3267 # Test that the state looks right. There should be an FDT for the fdtmap
3268 # that we jsut read back in, and it should match what we find in the
3269 # 'control' tables. Checking for an FDT that does not exist should
3270 # return None.
3271 path, fdtmap = state.GetFdtContents('fdtmap')
Simon Glass51014aa2019-07-20 12:23:56 -06003272 self.assertIsNotNone(path)
Simon Glass10f9d002019-07-20 12:23:50 -06003273 self.assertEqual(expected_fdtmap, fdtmap)
3274
3275 dtb = state.GetFdtForEtype('fdtmap')
3276 self.assertEqual(dtb.GetContents(), fdtmap)
3277
3278 missing_path, missing_fdtmap = state.GetFdtContents('missing')
3279 self.assertIsNone(missing_path)
3280 self.assertIsNone(missing_fdtmap)
3281
3282 missing_dtb = state.GetFdtForEtype('missing')
3283 self.assertIsNone(missing_dtb)
3284
3285 self.assertEqual('/binman', state.fdt_path_prefix)
3286
3287 def testReplaceResizeFail(self):
3288 """Test replacing a file by something larger"""
3289 expected = U_BOOT_DATA + b'x'
3290 with self.assertRaises(ValueError) as e:
Simon Glass51014aa2019-07-20 12:23:56 -06003291 self._RunReplaceCmd('u-boot', expected, allow_resize=False,
3292 dts='139_replace_repack.dts')
Simon Glass10f9d002019-07-20 12:23:50 -06003293 self.assertIn("Node '/u-boot': Entry data size does not match, but resize is disabled",
3294 str(e.exception))
3295
3296 def testReplaceMulti(self):
3297 """Test replacing entry data where multiple images are generated"""
3298 data = self._DoReadFileDtb('133_replace_multi.dts', use_real_dtb=True,
3299 update_dtb=True)[0]
3300 expected = b'x' * len(U_BOOT_DATA)
Simon Glassc1aa66e2022-01-29 14:14:04 -07003301 updated_fname = tools.get_output_filename('image-updated.bin')
3302 tools.write_file(updated_fname, data)
Simon Glass10f9d002019-07-20 12:23:50 -06003303 entry_name = 'u-boot'
Simon Glass51014aa2019-07-20 12:23:56 -06003304 control.WriteEntry(updated_fname, entry_name, expected,
3305 allow_resize=False)
Simon Glass10f9d002019-07-20 12:23:50 -06003306 data = control.ReadEntry(updated_fname, entry_name)
3307 self.assertEqual(expected, data)
3308
3309 # Check the state looks right.
3310 self.assertEqual('/binman/image', state.fdt_path_prefix)
3311
3312 # Now check we can write the first image
Simon Glassc1aa66e2022-01-29 14:14:04 -07003313 image_fname = tools.get_output_filename('first-image.bin')
3314 updated_fname = tools.get_output_filename('first-updated.bin')
3315 tools.write_file(updated_fname, tools.read_file(image_fname))
Simon Glass10f9d002019-07-20 12:23:50 -06003316 entry_name = 'u-boot'
Simon Glass51014aa2019-07-20 12:23:56 -06003317 control.WriteEntry(updated_fname, entry_name, expected,
3318 allow_resize=False)
Simon Glass10f9d002019-07-20 12:23:50 -06003319 data = control.ReadEntry(updated_fname, entry_name)
3320 self.assertEqual(expected, data)
3321
3322 # Check the state looks right.
3323 self.assertEqual('/binman/first-image', state.fdt_path_prefix)
Simon Glass8beb11e2019-07-08 14:25:47 -06003324
Simon Glass12bb1a92019-07-20 12:23:51 -06003325 def testUpdateFdtAllRepack(self):
3326 """Test that all device trees are updated with offset/size info"""
Marek Vasutfadad3a2023-07-18 07:23:58 -06003327 self._SetupSplElf()
3328 self._SetupTplElf()
Simon Glass12bb1a92019-07-20 12:23:51 -06003329 data = self._DoReadFileRealDtb('134_fdt_update_all_repack.dts')
3330 SECTION_SIZE = 0x300
3331 DTB_SIZE = 602
3332 FDTMAP_SIZE = 608
3333 base_expected = {
3334 'offset': 0,
3335 'size': SECTION_SIZE + DTB_SIZE * 2 + FDTMAP_SIZE,
3336 'image-pos': 0,
3337 'section:offset': 0,
3338 'section:size': SECTION_SIZE,
3339 'section:image-pos': 0,
3340 'section/u-boot-dtb:offset': 4,
3341 'section/u-boot-dtb:size': 636,
3342 'section/u-boot-dtb:image-pos': 4,
3343 'u-boot-spl-dtb:offset': SECTION_SIZE,
3344 'u-boot-spl-dtb:size': DTB_SIZE,
3345 'u-boot-spl-dtb:image-pos': SECTION_SIZE,
3346 'u-boot-tpl-dtb:offset': SECTION_SIZE + DTB_SIZE,
3347 'u-boot-tpl-dtb:image-pos': SECTION_SIZE + DTB_SIZE,
3348 'u-boot-tpl-dtb:size': DTB_SIZE,
3349 'fdtmap:offset': SECTION_SIZE + DTB_SIZE * 2,
3350 'fdtmap:size': FDTMAP_SIZE,
3351 'fdtmap:image-pos': SECTION_SIZE + DTB_SIZE * 2,
3352 }
3353 main_expected = {
3354 'section:orig-size': SECTION_SIZE,
3355 'section/u-boot-dtb:orig-offset': 4,
3356 }
3357
3358 # We expect three device-tree files in the output, with the first one
3359 # within a fixed-size section.
3360 # Read them in sequence. We look for an 'spl' property in the SPL tree,
3361 # and 'tpl' in the TPL tree, to make sure they are distinct from the
3362 # main U-Boot tree. All three should have the same positions and offset
3363 # except that the main tree should include the main_expected properties
3364 start = 4
3365 for item in ['', 'spl', 'tpl', None]:
3366 if item is None:
3367 start += 16 # Move past fdtmap header
3368 dtb = fdt.Fdt.FromData(data[start:])
3369 dtb.Scan()
3370 props = self._GetPropTree(dtb,
3371 BASE_DTB_PROPS + REPACK_DTB_PROPS + ['spl', 'tpl'],
3372 prefix='/' if item is None else '/binman/')
3373 expected = dict(base_expected)
3374 if item:
3375 expected[item] = 0
3376 else:
3377 # Main DTB and fdtdec should include the 'orig-' properties
3378 expected.update(main_expected)
3379 # Helpful for debugging:
3380 #for prop in sorted(props):
3381 #print('prop %s %s %s' % (prop, props[prop], expected[prop]))
3382 self.assertEqual(expected, props)
3383 if item == '':
3384 start = SECTION_SIZE
3385 else:
3386 start += dtb._fdt_obj.totalsize()
3387
Simon Glasseba1f0c2019-07-20 12:23:55 -06003388 def testFdtmapHeaderMiddle(self):
3389 """Test an FDT map in the middle of an image when it should be at end"""
3390 with self.assertRaises(ValueError) as e:
3391 self._DoReadFileRealDtb('135_fdtmap_hdr_middle.dts')
3392 self.assertIn("Invalid sibling order 'middle' for image-header: Must be at 'end' to match location",
3393 str(e.exception))
3394
3395 def testFdtmapHeaderStartBad(self):
3396 """Test an FDT map in middle of an image when it should be at start"""
3397 with self.assertRaises(ValueError) as e:
3398 self._DoReadFileRealDtb('136_fdtmap_hdr_startbad.dts')
3399 self.assertIn("Invalid sibling order 'end' for image-header: Must be at 'start' to match location",
3400 str(e.exception))
3401
3402 def testFdtmapHeaderEndBad(self):
3403 """Test an FDT map at the start of an image when it should be at end"""
3404 with self.assertRaises(ValueError) as e:
3405 self._DoReadFileRealDtb('137_fdtmap_hdr_endbad.dts')
3406 self.assertIn("Invalid sibling order 'start' for image-header: Must be at 'end' to match location",
3407 str(e.exception))
3408
3409 def testFdtmapHeaderNoSize(self):
3410 """Test an image header at the end of an image with undefined size"""
3411 self._DoReadFileRealDtb('138_fdtmap_hdr_nosize.dts')
3412
Simon Glass51014aa2019-07-20 12:23:56 -06003413 def testReplaceResize(self):
3414 """Test replacing a single file in an entry with a larger file"""
3415 expected = U_BOOT_DATA + b'x'
3416 data, _, image = self._RunReplaceCmd('u-boot', expected,
3417 dts='139_replace_repack.dts')
3418 self.assertEqual(expected, data)
3419
3420 entries = image.GetEntries()
3421 dtb_data = entries['u-boot-dtb'].data
3422 dtb = fdt.Fdt.FromData(dtb_data)
3423 dtb.Scan()
3424
3425 # The u-boot section should now be larger in the dtb
3426 node = dtb.GetNode('/binman/u-boot')
3427 self.assertEqual(len(expected), fdt_util.GetInt(node, 'size'))
3428
3429 # Same for the fdtmap
3430 fdata = entries['fdtmap'].data
3431 fdtb = fdt.Fdt.FromData(fdata[fdtmap.FDTMAP_HDR_LEN:])
3432 fdtb.Scan()
3433 fnode = fdtb.GetNode('/u-boot')
3434 self.assertEqual(len(expected), fdt_util.GetInt(fnode, 'size'))
3435
3436 def testReplaceResizeNoRepack(self):
3437 """Test replacing an entry with a larger file when not allowed"""
3438 expected = U_BOOT_DATA + b'x'
3439 with self.assertRaises(ValueError) as e:
3440 self._RunReplaceCmd('u-boot', expected)
3441 self.assertIn('Entry data size does not match, but allow-repack is not present for this image',
3442 str(e.exception))
3443
Simon Glass61ec04f2019-07-20 12:23:58 -06003444 def testEntryShrink(self):
3445 """Test contracting an entry after it is packed"""
3446 try:
3447 state.SetAllowEntryContraction(True)
3448 data = self._DoReadFileDtb('140_entry_shrink.dts',
3449 update_dtb=True)[0]
3450 finally:
3451 state.SetAllowEntryContraction(False)
3452 self.assertEqual(b'a', data[:1])
3453 self.assertEqual(U_BOOT_DATA, data[1:1 + len(U_BOOT_DATA)])
3454 self.assertEqual(b'a', data[-1:])
3455
3456 def testEntryShrinkFail(self):
3457 """Test not being allowed to contract an entry after it is packed"""
3458 data = self._DoReadFileDtb('140_entry_shrink.dts', update_dtb=True)[0]
3459
3460 # In this case there is a spare byte at the end of the data. The size of
3461 # the contents is only 1 byte but we still have the size before it
3462 # shrunk.
3463 self.assertEqual(b'a\0', data[:2])
3464 self.assertEqual(U_BOOT_DATA, data[2:2 + len(U_BOOT_DATA)])
3465 self.assertEqual(b'a\0', data[-2:])
3466
Simon Glass27145fd2019-07-20 12:24:01 -06003467 def testDescriptorOffset(self):
3468 """Test that the Intel descriptor is always placed at at the start"""
3469 data = self._DoReadFileDtb('141_descriptor_offset.dts')
3470 image = control.images['image']
3471 entries = image.GetEntries()
3472 desc = entries['intel-descriptor']
3473 self.assertEqual(0xff800000, desc.offset);
3474 self.assertEqual(0xff800000, desc.image_pos);
3475
Simon Glasseb0f4a42019-07-20 12:24:06 -06003476 def testReplaceCbfs(self):
3477 """Test replacing a single file in CBFS without changing the size"""
3478 self._CheckLz4()
3479 expected = b'x' * len(U_BOOT_DATA)
3480 data = self._DoReadFileRealDtb('142_replace_cbfs.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07003481 updated_fname = tools.get_output_filename('image-updated.bin')
3482 tools.write_file(updated_fname, data)
Simon Glasseb0f4a42019-07-20 12:24:06 -06003483 entry_name = 'section/cbfs/u-boot'
3484 control.WriteEntry(updated_fname, entry_name, expected,
3485 allow_resize=True)
3486 data = control.ReadEntry(updated_fname, entry_name)
3487 self.assertEqual(expected, data)
3488
3489 def testReplaceResizeCbfs(self):
3490 """Test replacing a single file in CBFS with one of a different size"""
3491 self._CheckLz4()
3492 expected = U_BOOT_DATA + b'x'
3493 data = self._DoReadFileRealDtb('142_replace_cbfs.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07003494 updated_fname = tools.get_output_filename('image-updated.bin')
3495 tools.write_file(updated_fname, data)
Simon Glasseb0f4a42019-07-20 12:24:06 -06003496 entry_name = 'section/cbfs/u-boot'
3497 control.WriteEntry(updated_fname, entry_name, expected,
3498 allow_resize=True)
3499 data = control.ReadEntry(updated_fname, entry_name)
3500 self.assertEqual(expected, data)
3501
Simon Glassa6cb9952019-07-20 12:24:15 -06003502 def _SetupForReplace(self):
3503 """Set up some files to use to replace entries
3504
3505 This generates an image, copies it to a new file, extracts all the files
3506 in it and updates some of them
3507
3508 Returns:
3509 List
3510 Image filename
3511 Output directory
3512 Expected values for updated entries, each a string
3513 """
3514 data = self._DoReadFileRealDtb('143_replace_all.dts')
3515
Simon Glassc1aa66e2022-01-29 14:14:04 -07003516 updated_fname = tools.get_output_filename('image-updated.bin')
3517 tools.write_file(updated_fname, data)
Simon Glassa6cb9952019-07-20 12:24:15 -06003518
3519 outdir = os.path.join(self._indir, 'extract')
3520 einfos = control.ExtractEntries(updated_fname, None, outdir, [])
3521
3522 expected1 = b'x' + U_BOOT_DATA + b'y'
3523 u_boot_fname1 = os.path.join(outdir, 'u-boot')
Simon Glassc1aa66e2022-01-29 14:14:04 -07003524 tools.write_file(u_boot_fname1, expected1)
Simon Glassa6cb9952019-07-20 12:24:15 -06003525
3526 expected2 = b'a' + U_BOOT_DATA + b'b'
3527 u_boot_fname2 = os.path.join(outdir, 'u-boot2')
Simon Glassc1aa66e2022-01-29 14:14:04 -07003528 tools.write_file(u_boot_fname2, expected2)
Simon Glassa6cb9952019-07-20 12:24:15 -06003529
3530 expected_text = b'not the same text'
3531 text_fname = os.path.join(outdir, 'text')
Simon Glassc1aa66e2022-01-29 14:14:04 -07003532 tools.write_file(text_fname, expected_text)
Simon Glassa6cb9952019-07-20 12:24:15 -06003533
3534 dtb_fname = os.path.join(outdir, 'u-boot-dtb')
3535 dtb = fdt.FdtScan(dtb_fname)
3536 node = dtb.GetNode('/binman/text')
3537 node.AddString('my-property', 'the value')
3538 dtb.Sync(auto_resize=True)
3539 dtb.Flush()
3540
3541 return updated_fname, outdir, expected1, expected2, expected_text
3542
3543 def _CheckReplaceMultiple(self, entry_paths):
3544 """Handle replacing the contents of multiple entries
3545
3546 Args:
3547 entry_paths: List of entry paths to replace
3548
3549 Returns:
3550 List
3551 Dict of entries in the image:
3552 key: Entry name
3553 Value: Entry object
3554 Expected values for updated entries, each a string
3555 """
3556 updated_fname, outdir, expected1, expected2, expected_text = (
3557 self._SetupForReplace())
3558 control.ReplaceEntries(updated_fname, None, outdir, entry_paths)
3559
3560 image = Image.FromFile(updated_fname)
3561 image.LoadData()
3562 return image.GetEntries(), expected1, expected2, expected_text
3563
3564 def testReplaceAll(self):
3565 """Test replacing the contents of all entries"""
3566 entries, expected1, expected2, expected_text = (
3567 self._CheckReplaceMultiple([]))
3568 data = entries['u-boot'].data
3569 self.assertEqual(expected1, data)
3570
3571 data = entries['u-boot2'].data
3572 self.assertEqual(expected2, data)
3573
3574 data = entries['text'].data
3575 self.assertEqual(expected_text, data)
3576
3577 # Check that the device tree is updated
3578 data = entries['u-boot-dtb'].data
3579 dtb = fdt.Fdt.FromData(data)
3580 dtb.Scan()
3581 node = dtb.GetNode('/binman/text')
3582 self.assertEqual('the value', node.props['my-property'].value)
3583
3584 def testReplaceSome(self):
3585 """Test replacing the contents of a few entries"""
3586 entries, expected1, expected2, expected_text = (
3587 self._CheckReplaceMultiple(['u-boot2', 'text']))
3588
3589 # This one should not change
3590 data = entries['u-boot'].data
3591 self.assertEqual(U_BOOT_DATA, data)
3592
3593 data = entries['u-boot2'].data
3594 self.assertEqual(expected2, data)
3595
3596 data = entries['text'].data
3597 self.assertEqual(expected_text, data)
3598
3599 def testReplaceCmd(self):
3600 """Test replacing a file fron an image on the command line"""
3601 self._DoReadFileRealDtb('143_replace_all.dts')
3602
3603 try:
3604 tmpdir, updated_fname = self._SetupImageInTmpdir()
3605
3606 fname = os.path.join(tmpdir, 'update-u-boot.bin')
3607 expected = b'x' * len(U_BOOT_DATA)
Simon Glassc1aa66e2022-01-29 14:14:04 -07003608 tools.write_file(fname, expected)
Simon Glassa6cb9952019-07-20 12:24:15 -06003609
3610 self._DoBinman('replace', '-i', updated_fname, 'u-boot', '-f', fname)
Simon Glassc1aa66e2022-01-29 14:14:04 -07003611 data = tools.read_file(updated_fname)
Simon Glassa6cb9952019-07-20 12:24:15 -06003612 self.assertEqual(expected, data[:len(expected)])
3613 map_fname = os.path.join(tmpdir, 'image-updated.map')
3614 self.assertFalse(os.path.exists(map_fname))
3615 finally:
3616 shutil.rmtree(tmpdir)
3617
3618 def testReplaceCmdSome(self):
3619 """Test replacing some files fron an image on the command line"""
3620 updated_fname, outdir, expected1, expected2, expected_text = (
3621 self._SetupForReplace())
3622
3623 self._DoBinman('replace', '-i', updated_fname, '-I', outdir,
3624 'u-boot2', 'text')
3625
Simon Glassc1aa66e2022-01-29 14:14:04 -07003626 tools.prepare_output_dir(None)
Simon Glassa6cb9952019-07-20 12:24:15 -06003627 image = Image.FromFile(updated_fname)
3628 image.LoadData()
3629 entries = image.GetEntries()
3630
3631 # This one should not change
3632 data = entries['u-boot'].data
3633 self.assertEqual(U_BOOT_DATA, data)
3634
3635 data = entries['u-boot2'].data
3636 self.assertEqual(expected2, data)
3637
3638 data = entries['text'].data
3639 self.assertEqual(expected_text, data)
3640
3641 def testReplaceMissing(self):
3642 """Test replacing entries where the file is missing"""
3643 updated_fname, outdir, expected1, expected2, expected_text = (
3644 self._SetupForReplace())
3645
3646 # Remove one of the files, to generate a warning
3647 u_boot_fname1 = os.path.join(outdir, 'u-boot')
3648 os.remove(u_boot_fname1)
3649
3650 with test_util.capture_sys_output() as (stdout, stderr):
3651 control.ReplaceEntries(updated_fname, None, outdir, [])
3652 self.assertIn("Skipping entry '/u-boot' from missing file",
Simon Glass38fdb4c2020-07-09 18:39:39 -06003653 stderr.getvalue())
Simon Glassa6cb9952019-07-20 12:24:15 -06003654
3655 def testReplaceCmdMap(self):
3656 """Test replacing a file fron an image on the command line"""
3657 self._DoReadFileRealDtb('143_replace_all.dts')
3658
3659 try:
3660 tmpdir, updated_fname = self._SetupImageInTmpdir()
3661
3662 fname = os.path.join(self._indir, 'update-u-boot.bin')
3663 expected = b'x' * len(U_BOOT_DATA)
Simon Glassc1aa66e2022-01-29 14:14:04 -07003664 tools.write_file(fname, expected)
Simon Glassa6cb9952019-07-20 12:24:15 -06003665
3666 self._DoBinman('replace', '-i', updated_fname, 'u-boot',
3667 '-f', fname, '-m')
3668 map_fname = os.path.join(tmpdir, 'image-updated.map')
3669 self.assertTrue(os.path.exists(map_fname))
3670 finally:
3671 shutil.rmtree(tmpdir)
3672
3673 def testReplaceNoEntryPaths(self):
3674 """Test replacing an entry without an entry path"""
3675 self._DoReadFileRealDtb('143_replace_all.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07003676 image_fname = tools.get_output_filename('image.bin')
Simon Glassa6cb9952019-07-20 12:24:15 -06003677 with self.assertRaises(ValueError) as e:
3678 control.ReplaceEntries(image_fname, 'fname', None, [])
3679 self.assertIn('Must specify an entry path to read with -f',
3680 str(e.exception))
3681
3682 def testReplaceTooManyEntryPaths(self):
3683 """Test extracting some entries"""
3684 self._DoReadFileRealDtb('143_replace_all.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07003685 image_fname = tools.get_output_filename('image.bin')
Simon Glassa6cb9952019-07-20 12:24:15 -06003686 with self.assertRaises(ValueError) as e:
3687 control.ReplaceEntries(image_fname, 'fname', None, ['a', 'b'])
3688 self.assertIn('Must specify exactly one entry path to write with -f',
3689 str(e.exception))
3690
Simon Glass2250ee62019-08-24 07:22:48 -06003691 def testPackReset16(self):
3692 """Test that an image with an x86 reset16 region can be created"""
3693 data = self._DoReadFile('144_x86_reset16.dts')
3694 self.assertEqual(X86_RESET16_DATA, data[:len(X86_RESET16_DATA)])
3695
3696 def testPackReset16Spl(self):
3697 """Test that an image with an x86 reset16-spl region can be created"""
3698 data = self._DoReadFile('145_x86_reset16_spl.dts')
3699 self.assertEqual(X86_RESET16_SPL_DATA, data[:len(X86_RESET16_SPL_DATA)])
3700
3701 def testPackReset16Tpl(self):
3702 """Test that an image with an x86 reset16-tpl region can be created"""
3703 data = self._DoReadFile('146_x86_reset16_tpl.dts')
3704 self.assertEqual(X86_RESET16_TPL_DATA, data[:len(X86_RESET16_TPL_DATA)])
3705
Simon Glass5af12072019-08-24 07:22:50 -06003706 def testPackIntelFit(self):
3707 """Test that an image with an Intel FIT and pointer can be created"""
3708 data = self._DoReadFile('147_intel_fit.dts')
3709 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
3710 fit = data[16:32];
3711 self.assertEqual(b'_FIT_ \x01\x00\x00\x00\x00\x01\x80}' , fit)
3712 ptr = struct.unpack('<i', data[0x40:0x44])[0]
3713
3714 image = control.images['image']
3715 entries = image.GetEntries()
3716 expected_ptr = entries['intel-fit'].image_pos - (1 << 32)
3717 self.assertEqual(expected_ptr, ptr)
3718
3719 def testPackIntelFitMissing(self):
3720 """Test detection of a FIT pointer with not FIT region"""
3721 with self.assertRaises(ValueError) as e:
3722 self._DoReadFile('148_intel_fit_missing.dts')
3723 self.assertIn("'intel-fit-ptr' section must have an 'intel-fit' sibling",
3724 str(e.exception))
3725
Simon Glass7c150132019-11-06 17:22:44 -07003726 def _CheckSymbolsTplSection(self, dts, expected_vals):
3727 data = self._DoReadFile(dts)
Alper Nebi Yasak367ecbf2022-06-18 15:13:11 +03003728 sym_values = struct.pack('<LLQLL', elf.BINMAN_SYM_MAGIC_VALUE, *expected_vals)
Simon Glass2090f1e2019-08-24 07:23:00 -06003729 upto1 = 4 + len(U_BOOT_SPL_DATA)
Alper Nebi Yasak367ecbf2022-06-18 15:13:11 +03003730 expected1 = tools.get_bytes(0xff, 4) + sym_values + U_BOOT_SPL_DATA[24:]
Simon Glass2090f1e2019-08-24 07:23:00 -06003731 self.assertEqual(expected1, data[:upto1])
3732
3733 upto2 = upto1 + 1 + len(U_BOOT_SPL_DATA)
Alper Nebi Yasak367ecbf2022-06-18 15:13:11 +03003734 expected2 = tools.get_bytes(0xff, 1) + sym_values + U_BOOT_SPL_DATA[24:]
Simon Glass2090f1e2019-08-24 07:23:00 -06003735 self.assertEqual(expected2, data[upto1:upto2])
3736
Alper Nebi Yasak367ecbf2022-06-18 15:13:11 +03003737 upto3 = 0x3c + len(U_BOOT_DATA)
Simon Glassc1aa66e2022-01-29 14:14:04 -07003738 expected3 = tools.get_bytes(0xff, 1) + U_BOOT_DATA
Simon Glass2090f1e2019-08-24 07:23:00 -06003739 self.assertEqual(expected3, data[upto2:upto3])
3740
Alper Nebi Yasak367ecbf2022-06-18 15:13:11 +03003741 expected4 = sym_values + U_BOOT_TPL_DATA[24:]
Simon Glass7c150132019-11-06 17:22:44 -07003742 self.assertEqual(expected4, data[upto3:upto3 + len(U_BOOT_TPL_DATA)])
3743
3744 def testSymbolsTplSection(self):
3745 """Test binman can assign symbols embedded in U-Boot TPL in a section"""
3746 self._SetupSplElf('u_boot_binman_syms')
3747 self._SetupTplElf('u_boot_binman_syms')
3748 self._CheckSymbolsTplSection('149_symbols_tpl.dts',
Alper Nebi Yasak367ecbf2022-06-18 15:13:11 +03003749 [0x04, 0x20, 0x10 + 0x3c, 0x04])
Simon Glass7c150132019-11-06 17:22:44 -07003750
3751 def testSymbolsTplSectionX86(self):
3752 """Test binman can assign symbols in a section with end-at-4gb"""
3753 self._SetupSplElf('u_boot_binman_syms_x86')
3754 self._SetupTplElf('u_boot_binman_syms_x86')
3755 self._CheckSymbolsTplSection('155_symbols_tpl_x86.dts',
Alper Nebi Yasak367ecbf2022-06-18 15:13:11 +03003756 [0xffffff04, 0xffffff20, 0xffffff3c,
Simon Glass7c150132019-11-06 17:22:44 -07003757 0x04])
Simon Glass2090f1e2019-08-24 07:23:00 -06003758
Simon Glassbf4d0e22019-08-24 07:23:03 -06003759 def testPackX86RomIfwiSectiom(self):
3760 """Test that a section can be placed in an IFWI region"""
3761 self._SetupIfwi('fitimage.bin')
3762 data = self._DoReadFile('151_x86_rom_ifwi_section.dts')
3763 self._CheckIfwi(data)
3764
Simon Glassea0fff92019-08-24 07:23:07 -06003765 def testPackFspM(self):
3766 """Test that an image with a FSP memory-init binary can be created"""
3767 data = self._DoReadFile('152_intel_fsp_m.dts')
3768 self.assertEqual(FSP_M_DATA, data[:len(FSP_M_DATA)])
3769
Simon Glassbc6a88f2019-10-20 21:31:35 -06003770 def testPackFspS(self):
3771 """Test that an image with a FSP silicon-init binary can be created"""
3772 data = self._DoReadFile('153_intel_fsp_s.dts')
3773 self.assertEqual(FSP_S_DATA, data[:len(FSP_S_DATA)])
Simon Glassea0fff92019-08-24 07:23:07 -06003774
Simon Glass998d1482019-10-20 21:31:36 -06003775 def testPackFspT(self):
3776 """Test that an image with a FSP temp-ram-init binary can be created"""
3777 data = self._DoReadFile('154_intel_fsp_t.dts')
3778 self.assertEqual(FSP_T_DATA, data[:len(FSP_T_DATA)])
3779
Simon Glass0dc706f2020-07-09 18:39:31 -06003780 def testMkimage(self):
3781 """Test using mkimage to build an image"""
Marek Vasutfadad3a2023-07-18 07:23:58 -06003782 self._SetupSplElf()
Simon Glass0dc706f2020-07-09 18:39:31 -06003783 data = self._DoReadFile('156_mkimage.dts')
3784
3785 # Just check that the data appears in the file somewhere
3786 self.assertIn(U_BOOT_SPL_DATA, data)
3787
Simon Glass4f9ee832022-01-09 20:14:09 -07003788 def testMkimageMissing(self):
3789 """Test that binman still produces an image if mkimage is missing"""
Marek Vasutfadad3a2023-07-18 07:23:58 -06003790 self._SetupSplElf()
Simon Glass4f9ee832022-01-09 20:14:09 -07003791 with test_util.capture_sys_output() as (_, stderr):
3792 self._DoTestFile('156_mkimage.dts',
3793 force_missing_bintools='mkimage')
3794 err = stderr.getvalue()
Simon Glass193d3db2023-02-07 14:34:18 -07003795 self.assertRegex(err, "Image 'image'.*missing bintools.*: mkimage")
Simon Glass4f9ee832022-01-09 20:14:09 -07003796
Simon Glassce867ad2020-07-09 18:39:36 -06003797 def testExtblob(self):
3798 """Test an image with an external blob"""
3799 data = self._DoReadFile('157_blob_ext.dts')
3800 self.assertEqual(REFCODE_DATA, data)
3801
3802 def testExtblobMissing(self):
3803 """Test an image with a missing external blob"""
3804 with self.assertRaises(ValueError) as e:
3805 self._DoReadFile('158_blob_ext_missing.dts')
3806 self.assertIn("Filename 'missing-file' not found in input path",
3807 str(e.exception))
3808
Simon Glass4f9f1052020-07-09 18:39:38 -06003809 def testExtblobMissingOk(self):
3810 """Test an image with an missing external blob that is allowed"""
Simon Glassb1cca952020-07-09 18:39:40 -06003811 with test_util.capture_sys_output() as (stdout, stderr):
Simon Glassb38da152022-11-09 19:14:42 -07003812 ret = self._DoTestFile('158_blob_ext_missing.dts',
3813 allow_missing=True)
3814 self.assertEqual(103, ret)
Simon Glassb1cca952020-07-09 18:39:40 -06003815 err = stderr.getvalue()
Jonas Karlman8f452bc2023-07-18 20:34:39 +00003816 self.assertIn('(missing-file)', err)
Simon Glass193d3db2023-02-07 14:34:18 -07003817 self.assertRegex(err, "Image 'image'.*missing.*: blob-ext")
Simon Glassb38da152022-11-09 19:14:42 -07003818 self.assertIn('Some images are invalid', err)
3819
3820 def testExtblobMissingOkFlag(self):
3821 """Test an image with an missing external blob allowed with -W"""
3822 with test_util.capture_sys_output() as (stdout, stderr):
3823 ret = self._DoTestFile('158_blob_ext_missing.dts',
3824 allow_missing=True, ignore_missing=True)
3825 self.assertEqual(0, ret)
3826 err = stderr.getvalue()
Jonas Karlman8f452bc2023-07-18 20:34:39 +00003827 self.assertIn('(missing-file)', err)
Simon Glass193d3db2023-02-07 14:34:18 -07003828 self.assertRegex(err, "Image 'image'.*missing.*: blob-ext")
Simon Glassb38da152022-11-09 19:14:42 -07003829 self.assertIn('Some images are invalid', err)
Simon Glassb1cca952020-07-09 18:39:40 -06003830
3831 def testExtblobMissingOkSect(self):
3832 """Test an image with an missing external blob that is allowed"""
3833 with test_util.capture_sys_output() as (stdout, stderr):
3834 self._DoTestFile('159_blob_ext_missing_sect.dts',
3835 allow_missing=True)
3836 err = stderr.getvalue()
Simon Glass193d3db2023-02-07 14:34:18 -07003837 self.assertRegex(err, "Image 'image'.*missing.*: blob-ext blob-ext2")
Simon Glass4f9f1052020-07-09 18:39:38 -06003838
Simon Glass0ba4b3d2020-07-09 18:39:41 -06003839 def testPackX86RomMeMissingDesc(self):
3840 """Test that an missing Intel descriptor entry is allowed"""
Simon Glass0ba4b3d2020-07-09 18:39:41 -06003841 with test_util.capture_sys_output() as (stdout, stderr):
Simon Glass52b10dd2020-07-25 15:11:19 -06003842 self._DoTestFile('164_x86_rom_me_missing.dts', allow_missing=True)
Simon Glass0ba4b3d2020-07-09 18:39:41 -06003843 err = stderr.getvalue()
Simon Glass193d3db2023-02-07 14:34:18 -07003844 self.assertRegex(err, "Image 'image'.*missing.*: intel-descriptor")
Simon Glass0ba4b3d2020-07-09 18:39:41 -06003845
3846 def testPackX86RomMissingIfwi(self):
3847 """Test that an x86 ROM with Integrated Firmware Image can be created"""
3848 self._SetupIfwi('fitimage.bin')
3849 pathname = os.path.join(self._indir, 'fitimage.bin')
3850 os.remove(pathname)
3851 with test_util.capture_sys_output() as (stdout, stderr):
3852 self._DoTestFile('111_x86_rom_ifwi.dts', allow_missing=True)
3853 err = stderr.getvalue()
Simon Glass193d3db2023-02-07 14:34:18 -07003854 self.assertRegex(err, "Image 'image'.*missing.*: intel-ifwi")
Simon Glass0ba4b3d2020-07-09 18:39:41 -06003855
Simon Glass8d2ef3e2022-02-11 13:23:21 -07003856 def testPackOverlapZero(self):
Simon Glassb3295fd2020-07-09 18:39:42 -06003857 """Test that zero-size overlapping regions are ignored"""
3858 self._DoTestFile('160_pack_overlap_zero.dts')
3859
Alper Nebi Yasak21353312022-02-08 01:08:04 +03003860 def _CheckSimpleFitData(self, fit_data, kernel_data, fdt1_data):
Simon Glassfdc34362020-07-09 18:39:45 -06003861 # The data should be inside the FIT
3862 dtb = fdt.Fdt.FromData(fit_data)
3863 dtb.Scan()
3864 fnode = dtb.GetNode('/images/kernel')
3865 self.assertIn('data', fnode.props)
3866
3867 fname = os.path.join(self._indir, 'fit_data.fit')
Simon Glassc1aa66e2022-01-29 14:14:04 -07003868 tools.write_file(fname, fit_data)
3869 out = tools.run('dumpimage', '-l', fname)
Simon Glassfdc34362020-07-09 18:39:45 -06003870
3871 # Check a few features to make sure the plumbing works. We don't need
3872 # to test the operation of mkimage or dumpimage here. First convert the
3873 # output into a dict where the keys are the fields printed by dumpimage
3874 # and the values are a list of values for each field
3875 lines = out.splitlines()
3876
3877 # Converts "Compression: gzip compressed" into two groups:
3878 # 'Compression' and 'gzip compressed'
3879 re_line = re.compile(r'^ *([^:]*)(?:: *(.*))?$')
3880 vals = collections.defaultdict(list)
3881 for line in lines:
3882 mat = re_line.match(line)
3883 vals[mat.group(1)].append(mat.group(2))
3884
3885 self.assertEquals('FIT description: test-desc', lines[0])
3886 self.assertIn('Created:', lines[1])
3887 self.assertIn('Image 0 (kernel)', vals)
3888 self.assertIn('Hash value', vals)
3889 data_sizes = vals.get('Data Size')
3890 self.assertIsNotNone(data_sizes)
3891 self.assertEqual(2, len(data_sizes))
3892 # Format is "4 Bytes = 0.00 KiB = 0.00 MiB" so take the first word
Alper Nebi Yasak21353312022-02-08 01:08:04 +03003893 self.assertEqual(len(kernel_data), int(data_sizes[0].split()[0]))
3894 self.assertEqual(len(fdt1_data), int(data_sizes[1].split()[0]))
3895
Alper Nebi Yasake7368782022-03-27 18:31:47 +03003896 # Check if entry listing correctly omits /images/
3897 image = control.images['image']
3898 fit_entry = image.GetEntries()['fit']
3899 subentries = list(fit_entry.GetEntries().keys())
3900 expected = ['kernel', 'fdt-1']
3901 self.assertEqual(expected, subentries)
3902
Alper Nebi Yasak21353312022-02-08 01:08:04 +03003903 def testSimpleFit(self):
3904 """Test an image with a FIT inside"""
Marek Vasutfadad3a2023-07-18 07:23:58 -06003905 self._SetupSplElf()
Alper Nebi Yasak21353312022-02-08 01:08:04 +03003906 data = self._DoReadFile('161_fit.dts')
3907 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
3908 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
3909 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
3910
3911 self._CheckSimpleFitData(fit_data, U_BOOT_DATA, U_BOOT_SPL_DTB_DATA)
3912
3913 def testSimpleFitExpandsSubentries(self):
3914 """Test that FIT images expand their subentries"""
3915 data = self._DoReadFileDtb('161_fit.dts', use_expanded=True)[0]
3916 self.assertEqual(U_BOOT_EXP_DATA, data[:len(U_BOOT_EXP_DATA)])
3917 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
3918 fit_data = data[len(U_BOOT_EXP_DATA):-len(U_BOOT_NODTB_DATA)]
3919
3920 self._CheckSimpleFitData(fit_data, U_BOOT_EXP_DATA, U_BOOT_SPL_DTB_DATA)
Simon Glassfdc34362020-07-09 18:39:45 -06003921
Alper Nebi Yasak73092222022-02-08 01:08:08 +03003922 def testSimpleFitImagePos(self):
3923 """Test that we have correct image-pos for FIT subentries"""
3924 data, _, _, out_dtb_fname = self._DoReadFileDtb('161_fit.dts',
3925 update_dtb=True)
3926 dtb = fdt.Fdt(out_dtb_fname)
3927 dtb.Scan()
3928 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
3929
Simon Glass38397d02022-03-05 20:19:01 -07003930 self.maxDiff = None
Alper Nebi Yasak73092222022-02-08 01:08:08 +03003931 self.assertEqual({
3932 'image-pos': 0,
3933 'offset': 0,
3934 'size': 1890,
3935
3936 'u-boot:image-pos': 0,
3937 'u-boot:offset': 0,
3938 'u-boot:size': 4,
3939
3940 'fit:image-pos': 4,
3941 'fit:offset': 4,
3942 'fit:size': 1840,
3943
Simon Glass38397d02022-03-05 20:19:01 -07003944 'fit/images/kernel:image-pos': 304,
3945 'fit/images/kernel:offset': 300,
Alper Nebi Yasak73092222022-02-08 01:08:08 +03003946 'fit/images/kernel:size': 4,
3947
Simon Glass38397d02022-03-05 20:19:01 -07003948 'fit/images/kernel/u-boot:image-pos': 304,
Alper Nebi Yasak73092222022-02-08 01:08:08 +03003949 'fit/images/kernel/u-boot:offset': 0,
3950 'fit/images/kernel/u-boot:size': 4,
3951
Simon Glass38397d02022-03-05 20:19:01 -07003952 'fit/images/fdt-1:image-pos': 552,
3953 'fit/images/fdt-1:offset': 548,
Alper Nebi Yasak73092222022-02-08 01:08:08 +03003954 'fit/images/fdt-1:size': 6,
3955
Simon Glass38397d02022-03-05 20:19:01 -07003956 'fit/images/fdt-1/u-boot-spl-dtb:image-pos': 552,
Alper Nebi Yasak73092222022-02-08 01:08:08 +03003957 'fit/images/fdt-1/u-boot-spl-dtb:offset': 0,
3958 'fit/images/fdt-1/u-boot-spl-dtb:size': 6,
3959
3960 'u-boot-nodtb:image-pos': 1844,
3961 'u-boot-nodtb:offset': 1844,
3962 'u-boot-nodtb:size': 46,
3963 }, props)
3964
3965 # Actually check the data is where we think it is
3966 for node, expected in [
3967 ("u-boot", U_BOOT_DATA),
3968 ("fit/images/kernel", U_BOOT_DATA),
3969 ("fit/images/kernel/u-boot", U_BOOT_DATA),
3970 ("fit/images/fdt-1", U_BOOT_SPL_DTB_DATA),
3971 ("fit/images/fdt-1/u-boot-spl-dtb", U_BOOT_SPL_DTB_DATA),
3972 ("u-boot-nodtb", U_BOOT_NODTB_DATA),
3973 ]:
3974 image_pos = props[f"{node}:image-pos"]
3975 size = props[f"{node}:size"]
3976 self.assertEqual(len(expected), size)
3977 self.assertEqual(expected, data[image_pos:image_pos+size])
3978
Simon Glassfdc34362020-07-09 18:39:45 -06003979 def testFitExternal(self):
Simon Glasse9d336d2020-09-01 05:13:55 -06003980 """Test an image with an FIT with external images"""
Simon Glassfdc34362020-07-09 18:39:45 -06003981 data = self._DoReadFile('162_fit_external.dts')
3982 fit_data = data[len(U_BOOT_DATA):-2] # _testing is 2 bytes
3983
Simon Glass8bc78b72022-01-09 20:13:39 -07003984 # Size of the external-data region as set up by mkimage
3985 external_data_size = len(U_BOOT_DATA) + 2
3986 expected_size = (len(U_BOOT_DATA) + 0x400 +
Simon Glassc1aa66e2022-01-29 14:14:04 -07003987 tools.align(external_data_size, 4) +
Simon Glass8bc78b72022-01-09 20:13:39 -07003988 len(U_BOOT_NODTB_DATA))
3989
Simon Glassfdc34362020-07-09 18:39:45 -06003990 # The data should be outside the FIT
3991 dtb = fdt.Fdt.FromData(fit_data)
3992 dtb.Scan()
3993 fnode = dtb.GetNode('/images/kernel')
3994 self.assertNotIn('data', fnode.props)
Simon Glass8bc78b72022-01-09 20:13:39 -07003995 self.assertEqual(len(U_BOOT_DATA),
3996 fdt_util.fdt32_to_cpu(fnode.props['data-size'].value))
3997 fit_pos = 0x400;
3998 self.assertEqual(
3999 fit_pos,
4000 fdt_util.fdt32_to_cpu(fnode.props['data-position'].value))
4001
4002 self.assertEquals(expected_size, len(data))
4003 actual_pos = len(U_BOOT_DATA) + fit_pos
4004 self.assertEqual(U_BOOT_DATA + b'aa',
4005 data[actual_pos:actual_pos + external_data_size])
Simon Glass12bb1a92019-07-20 12:23:51 -06004006
Alper Nebi Yasak73092222022-02-08 01:08:08 +03004007 def testFitExternalImagePos(self):
4008 """Test that we have correct image-pos for external FIT subentries"""
4009 data, _, _, out_dtb_fname = self._DoReadFileDtb('162_fit_external.dts',
4010 update_dtb=True)
4011 dtb = fdt.Fdt(out_dtb_fname)
4012 dtb.Scan()
4013 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
4014
4015 self.assertEqual({
4016 'image-pos': 0,
4017 'offset': 0,
4018 'size': 1082,
4019
4020 'u-boot:image-pos': 0,
4021 'u-boot:offset': 0,
4022 'u-boot:size': 4,
4023
4024 'fit:size': 1032,
4025 'fit:offset': 4,
4026 'fit:image-pos': 4,
4027
4028 'fit/images/kernel:size': 4,
4029 'fit/images/kernel:offset': 1024,
4030 'fit/images/kernel:image-pos': 1028,
4031
4032 'fit/images/kernel/u-boot:size': 4,
4033 'fit/images/kernel/u-boot:offset': 0,
4034 'fit/images/kernel/u-boot:image-pos': 1028,
4035
4036 'fit/images/fdt-1:size': 2,
4037 'fit/images/fdt-1:offset': 1028,
4038 'fit/images/fdt-1:image-pos': 1032,
4039
4040 'fit/images/fdt-1/_testing:size': 2,
4041 'fit/images/fdt-1/_testing:offset': 0,
4042 'fit/images/fdt-1/_testing:image-pos': 1032,
4043
4044 'u-boot-nodtb:image-pos': 1036,
4045 'u-boot-nodtb:offset': 1036,
4046 'u-boot-nodtb:size': 46,
4047 }, props)
4048
4049 # Actually check the data is where we think it is
4050 for node, expected in [
4051 ("u-boot", U_BOOT_DATA),
4052 ("fit/images/kernel", U_BOOT_DATA),
4053 ("fit/images/kernel/u-boot", U_BOOT_DATA),
4054 ("fit/images/fdt-1", b'aa'),
4055 ("fit/images/fdt-1/_testing", b'aa'),
4056 ("u-boot-nodtb", U_BOOT_NODTB_DATA),
4057 ]:
4058 image_pos = props[f"{node}:image-pos"]
4059 size = props[f"{node}:size"]
4060 self.assertEqual(len(expected), size)
4061 self.assertEqual(expected, data[image_pos:image_pos+size])
4062
Simon Glass4f9ee832022-01-09 20:14:09 -07004063 def testFitMissing(self):
Simon Glass033828c2023-03-02 17:02:43 -07004064 """Test that binman complains if mkimage is missing"""
4065 with self.assertRaises(ValueError) as e:
4066 self._DoTestFile('162_fit_external.dts',
4067 force_missing_bintools='mkimage')
4068 self.assertIn("Node '/binman/fit': Missing tool: 'mkimage'",
4069 str(e.exception))
4070
4071 def testFitMissingOK(self):
Simon Glass4f9ee832022-01-09 20:14:09 -07004072 """Test that binman still produces a FIT image if mkimage is missing"""
4073 with test_util.capture_sys_output() as (_, stderr):
Simon Glass033828c2023-03-02 17:02:43 -07004074 self._DoTestFile('162_fit_external.dts', allow_missing=True,
Simon Glass4f9ee832022-01-09 20:14:09 -07004075 force_missing_bintools='mkimage')
4076 err = stderr.getvalue()
Simon Glass193d3db2023-02-07 14:34:18 -07004077 self.assertRegex(err, "Image 'image'.*missing bintools.*: mkimage")
Simon Glass4f9ee832022-01-09 20:14:09 -07004078
Alper Nebi Yasak8001d0b2020-08-31 12:58:18 +03004079 def testSectionIgnoreHashSignature(self):
4080 """Test that sections ignore hash, signature nodes for its data"""
4081 data = self._DoReadFile('165_section_ignore_hash_signature.dts')
4082 expected = (U_BOOT_DATA + U_BOOT_DATA)
4083 self.assertEqual(expected, data)
4084
Alper Nebi Yasak3fdeb142020-08-31 12:58:19 +03004085 def testPadInSections(self):
4086 """Test pad-before, pad-after for entries in sections"""
Simon Glassf90d9062020-10-26 17:40:09 -06004087 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4088 '166_pad_in_sections.dts', update_dtb=True)
Simon Glassc1aa66e2022-01-29 14:14:04 -07004089 expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) +
4090 U_BOOT_DATA + tools.get_bytes(ord('!'), 6) +
Alper Nebi Yasak3fdeb142020-08-31 12:58:19 +03004091 U_BOOT_DATA)
4092 self.assertEqual(expected, data)
4093
Simon Glassf90d9062020-10-26 17:40:09 -06004094 dtb = fdt.Fdt(out_dtb_fname)
4095 dtb.Scan()
4096 props = self._GetPropTree(dtb, ['size', 'image-pos', 'offset'])
4097 expected = {
4098 'image-pos': 0,
4099 'offset': 0,
4100 'size': 12 + 6 + 3 * len(U_BOOT_DATA),
4101
4102 'section:image-pos': 0,
4103 'section:offset': 0,
4104 'section:size': 12 + 6 + 3 * len(U_BOOT_DATA),
4105
4106 'section/before:image-pos': 0,
4107 'section/before:offset': 0,
4108 'section/before:size': len(U_BOOT_DATA),
4109
4110 'section/u-boot:image-pos': 4,
4111 'section/u-boot:offset': 4,
4112 'section/u-boot:size': 12 + len(U_BOOT_DATA) + 6,
4113
4114 'section/after:image-pos': 26,
4115 'section/after:offset': 26,
4116 'section/after:size': len(U_BOOT_DATA),
4117 }
4118 self.assertEqual(expected, props)
4119
Alper Nebi Yasakfe057012020-08-31 12:58:20 +03004120 def testFitImageSubentryAlignment(self):
4121 """Test relative alignability of FIT image subentries"""
Alper Nebi Yasakf3078d42022-02-08 01:08:07 +03004122 self._SetupSplElf()
Alper Nebi Yasakfe057012020-08-31 12:58:20 +03004123 entry_args = {
4124 'test-id': TEXT_DATA,
4125 }
4126 data, _, _, _ = self._DoReadFileDtb('167_fit_image_subentry_alignment.dts',
4127 entry_args=entry_args)
4128 dtb = fdt.Fdt.FromData(data)
4129 dtb.Scan()
4130
4131 node = dtb.GetNode('/images/kernel')
4132 data = dtb.GetProps(node)["data"].bytes
4133 align_pad = 0x10 - (len(U_BOOT_SPL_DATA) % 0x10)
Simon Glassc1aa66e2022-01-29 14:14:04 -07004134 expected = (tools.get_bytes(0, 0x20) + U_BOOT_SPL_DATA +
4135 tools.get_bytes(0, align_pad) + U_BOOT_DATA)
Alper Nebi Yasakfe057012020-08-31 12:58:20 +03004136 self.assertEqual(expected, data)
4137
4138 node = dtb.GetNode('/images/fdt-1')
4139 data = dtb.GetProps(node)["data"].bytes
Simon Glassc1aa66e2022-01-29 14:14:04 -07004140 expected = (U_BOOT_SPL_DTB_DATA + tools.get_bytes(0, 20) +
4141 tools.to_bytes(TEXT_DATA) + tools.get_bytes(0, 30) +
Alper Nebi Yasakfe057012020-08-31 12:58:20 +03004142 U_BOOT_DTB_DATA)
4143 self.assertEqual(expected, data)
4144
4145 def testFitExtblobMissingOk(self):
4146 """Test a FIT with a missing external blob that is allowed"""
4147 with test_util.capture_sys_output() as (stdout, stderr):
4148 self._DoTestFile('168_fit_missing_blob.dts',
4149 allow_missing=True)
4150 err = stderr.getvalue()
Simon Glass193d3db2023-02-07 14:34:18 -07004151 self.assertRegex(err, "Image 'image'.*missing.*: atf-bl31")
Alper Nebi Yasakfe057012020-08-31 12:58:20 +03004152
Simon Glass3decfa32020-09-01 05:13:54 -06004153 def testBlobNamedByArgMissing(self):
4154 """Test handling of a missing entry arg"""
4155 with self.assertRaises(ValueError) as e:
4156 self._DoReadFile('068_blob_named_by_arg.dts')
4157 self.assertIn("Missing required properties/entry args: cros-ec-rw-path",
4158 str(e.exception))
4159
Simon Glassdc2f81a2020-09-01 05:13:58 -06004160 def testPackBl31(self):
4161 """Test that an image with an ATF BL31 binary can be created"""
4162 data = self._DoReadFile('169_atf_bl31.dts')
4163 self.assertEqual(ATF_BL31_DATA, data[:len(ATF_BL31_DATA)])
4164
Samuel Holland18bd4552020-10-21 21:12:15 -05004165 def testPackScp(self):
4166 """Test that an image with an SCP binary can be created"""
4167 data = self._DoReadFile('172_scp.dts')
4168 self.assertEqual(SCP_DATA, data[:len(SCP_DATA)])
4169
Simon Glass6cf99532020-09-01 05:13:59 -06004170 def testFitFdt(self):
4171 """Test an image with an FIT with multiple FDT images"""
4172 def _CheckFdt(seq, expected_data):
4173 """Check the FDT nodes
4174
4175 Args:
4176 seq: Sequence number to check (0 or 1)
4177 expected_data: Expected contents of 'data' property
4178 """
4179 name = 'fdt-%d' % seq
4180 fnode = dtb.GetNode('/images/%s' % name)
4181 self.assertIsNotNone(fnode)
4182 self.assertEqual({'description','type', 'compression', 'data'},
4183 set(fnode.props.keys()))
4184 self.assertEqual(expected_data, fnode.props['data'].bytes)
4185 self.assertEqual('fdt-test-fdt%d.dtb' % seq,
4186 fnode.props['description'].value)
Jan Kiszkab2106612022-02-28 17:06:20 +01004187 self.assertEqual(fnode.subnodes[0].name, 'hash')
Simon Glass6cf99532020-09-01 05:13:59 -06004188
4189 def _CheckConfig(seq, expected_data):
4190 """Check the configuration nodes
4191
4192 Args:
4193 seq: Sequence number to check (0 or 1)
4194 expected_data: Expected contents of 'data' property
4195 """
4196 cnode = dtb.GetNode('/configurations')
4197 self.assertIn('default', cnode.props)
Simon Glassc0f1ebe2020-09-06 10:39:08 -06004198 self.assertEqual('config-2', cnode.props['default'].value)
Simon Glass6cf99532020-09-01 05:13:59 -06004199
4200 name = 'config-%d' % seq
4201 fnode = dtb.GetNode('/configurations/%s' % name)
4202 self.assertIsNotNone(fnode)
4203 self.assertEqual({'description','firmware', 'loadables', 'fdt'},
4204 set(fnode.props.keys()))
4205 self.assertEqual('conf-test-fdt%d.dtb' % seq,
4206 fnode.props['description'].value)
4207 self.assertEqual('fdt-%d' % seq, fnode.props['fdt'].value)
4208
4209 entry_args = {
4210 'of-list': 'test-fdt1 test-fdt2',
Simon Glassc0f1ebe2020-09-06 10:39:08 -06004211 'default-dt': 'test-fdt2',
Simon Glass6cf99532020-09-01 05:13:59 -06004212 }
4213 data = self._DoReadFileDtb(
Bin Mengaa75ce92021-05-10 20:23:32 +08004214 '170_fit_fdt.dts',
Simon Glass6cf99532020-09-01 05:13:59 -06004215 entry_args=entry_args,
4216 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
4217 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
4218 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
4219
4220 dtb = fdt.Fdt.FromData(fit_data)
4221 dtb.Scan()
4222 fnode = dtb.GetNode('/images/kernel')
4223 self.assertIn('data', fnode.props)
4224
4225 # Check all the properties in fdt-1 and fdt-2
4226 _CheckFdt(1, TEST_FDT1_DATA)
4227 _CheckFdt(2, TEST_FDT2_DATA)
4228
4229 # Check configurations
4230 _CheckConfig(1, TEST_FDT1_DATA)
4231 _CheckConfig(2, TEST_FDT2_DATA)
4232
4233 def testFitFdtMissingList(self):
4234 """Test handling of a missing 'of-list' entry arg"""
4235 with self.assertRaises(ValueError) as e:
Bin Mengaa75ce92021-05-10 20:23:32 +08004236 self._DoReadFile('170_fit_fdt.dts')
Simon Glass6cf99532020-09-01 05:13:59 -06004237 self.assertIn("Generator node requires 'of-list' entry argument",
4238 str(e.exception))
4239
4240 def testFitFdtEmptyList(self):
4241 """Test handling of an empty 'of-list' entry arg"""
4242 entry_args = {
4243 'of-list': '',
4244 }
4245 data = self._DoReadFileDtb('170_fit_fdt.dts', entry_args=entry_args)[0]
4246
4247 def testFitFdtMissingProp(self):
4248 """Test handling of a missing 'fit,fdt-list' property"""
4249 with self.assertRaises(ValueError) as e:
4250 self._DoReadFile('171_fit_fdt_missing_prop.dts')
4251 self.assertIn("Generator node requires 'fit,fdt-list' property",
4252 str(e.exception))
Simon Glassdc2f81a2020-09-01 05:13:58 -06004253
Simon Glassc0f1ebe2020-09-06 10:39:08 -06004254 def testFitFdtMissing(self):
4255 """Test handling of a missing 'default-dt' entry arg"""
4256 entry_args = {
4257 'of-list': 'test-fdt1 test-fdt2',
4258 }
4259 with self.assertRaises(ValueError) as e:
4260 self._DoReadFileDtb(
Bin Mengaa75ce92021-05-10 20:23:32 +08004261 '170_fit_fdt.dts',
Simon Glassc0f1ebe2020-09-06 10:39:08 -06004262 entry_args=entry_args,
4263 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
4264 self.assertIn("Generated 'default' node requires default-dt entry argument",
4265 str(e.exception))
4266
4267 def testFitFdtNotInList(self):
4268 """Test handling of a default-dt that is not in the of-list"""
4269 entry_args = {
4270 'of-list': 'test-fdt1 test-fdt2',
4271 'default-dt': 'test-fdt3',
4272 }
4273 with self.assertRaises(ValueError) as e:
4274 self._DoReadFileDtb(
Bin Mengaa75ce92021-05-10 20:23:32 +08004275 '170_fit_fdt.dts',
Simon Glassc0f1ebe2020-09-06 10:39:08 -06004276 entry_args=entry_args,
4277 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
4278 self.assertIn("default-dt entry argument 'test-fdt3' not found in fdt list: test-fdt1, test-fdt2",
4279 str(e.exception))
4280
Simon Glassb2381432020-09-06 10:39:09 -06004281 def testFitExtblobMissingHelp(self):
4282 """Test display of help messages when an external blob is missing"""
4283 control.missing_blob_help = control._ReadMissingBlobHelp()
4284 control.missing_blob_help['wibble'] = 'Wibble test'
4285 control.missing_blob_help['another'] = 'Another test'
4286 with test_util.capture_sys_output() as (stdout, stderr):
4287 self._DoTestFile('168_fit_missing_blob.dts',
4288 allow_missing=True)
4289 err = stderr.getvalue()
4290
4291 # We can get the tag from the name, the type or the missing-msg
4292 # property. Check all three.
4293 self.assertIn('You may need to build ARM Trusted', err)
4294 self.assertIn('Wibble test', err)
4295 self.assertIn('Another test', err)
4296
Simon Glass204aa782020-09-06 10:35:32 -06004297 def testMissingBlob(self):
4298 """Test handling of a blob containing a missing file"""
4299 with self.assertRaises(ValueError) as e:
4300 self._DoTestFile('173_missing_blob.dts', allow_missing=True)
4301 self.assertIn("Filename 'missing' not found in input path",
4302 str(e.exception))
4303
Simon Glassfb91d562020-09-06 10:35:33 -06004304 def testEnvironment(self):
4305 """Test adding a U-Boot environment"""
4306 data = self._DoReadFile('174_env.dts')
4307 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
4308 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
4309 env = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
4310 self.assertEqual(b'\x1b\x97\x22\x7c\x01var1=1\0var2="2"\0\0\xff\xff',
4311 env)
4312
4313 def testEnvironmentNoSize(self):
4314 """Test that a missing 'size' property is detected"""
4315 with self.assertRaises(ValueError) as e:
Simon Glassa4dfe3e2020-10-26 17:40:00 -06004316 self._DoTestFile('175_env_no_size.dts')
Simon Glassfb91d562020-09-06 10:35:33 -06004317 self.assertIn("'u-boot-env' entry must have a size property",
4318 str(e.exception))
4319
4320 def testEnvironmentTooSmall(self):
4321 """Test handling of an environment that does not fit"""
4322 with self.assertRaises(ValueError) as e:
Simon Glassa4dfe3e2020-10-26 17:40:00 -06004323 self._DoTestFile('176_env_too_small.dts')
Simon Glassfb91d562020-09-06 10:35:33 -06004324
4325 # checksum, start byte, environment with \0 terminator, final \0
4326 need = 4 + 1 + len(ENV_DATA) + 1 + 1
4327 short = need - 0x8
4328 self.assertIn("too small to hold data (need %#x more bytes)" % short,
4329 str(e.exception))
4330
Simon Glassf2c0dd82020-10-26 17:40:01 -06004331 def testSkipAtStart(self):
4332 """Test handling of skip-at-start section"""
4333 data = self._DoReadFile('177_skip_at_start.dts')
4334 self.assertEqual(U_BOOT_DATA, data)
4335
4336 image = control.images['image']
4337 entries = image.GetEntries()
4338 section = entries['section']
4339 self.assertEqual(0, section.offset)
4340 self.assertEqual(len(U_BOOT_DATA), section.size)
4341 self.assertEqual(U_BOOT_DATA, section.GetData())
4342
4343 entry = section.GetEntries()['u-boot']
4344 self.assertEqual(16, entry.offset)
4345 self.assertEqual(len(U_BOOT_DATA), entry.size)
4346 self.assertEqual(U_BOOT_DATA, entry.data)
4347
4348 def testSkipAtStartPad(self):
4349 """Test handling of skip-at-start section with padded entry"""
4350 data = self._DoReadFile('178_skip_at_start_pad.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07004351 before = tools.get_bytes(0, 8)
4352 after = tools.get_bytes(0, 4)
Simon Glassf2c0dd82020-10-26 17:40:01 -06004353 all = before + U_BOOT_DATA + after
4354 self.assertEqual(all, data)
4355
4356 image = control.images['image']
4357 entries = image.GetEntries()
4358 section = entries['section']
4359 self.assertEqual(0, section.offset)
4360 self.assertEqual(len(all), section.size)
4361 self.assertEqual(all, section.GetData())
4362
4363 entry = section.GetEntries()['u-boot']
4364 self.assertEqual(16, entry.offset)
4365 self.assertEqual(len(all), entry.size)
4366 self.assertEqual(U_BOOT_DATA, entry.data)
4367
4368 def testSkipAtStartSectionPad(self):
4369 """Test handling of skip-at-start section with padding"""
4370 data = self._DoReadFile('179_skip_at_start_section_pad.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07004371 before = tools.get_bytes(0, 8)
4372 after = tools.get_bytes(0, 4)
Simon Glassf2c0dd82020-10-26 17:40:01 -06004373 all = before + U_BOOT_DATA + after
Simon Glassd1d3ad72020-10-26 17:40:13 -06004374 self.assertEqual(all, data)
Simon Glassf2c0dd82020-10-26 17:40:01 -06004375
4376 image = control.images['image']
4377 entries = image.GetEntries()
4378 section = entries['section']
4379 self.assertEqual(0, section.offset)
4380 self.assertEqual(len(all), section.size)
Simon Glass63e7ba62020-10-26 17:40:16 -06004381 self.assertEqual(U_BOOT_DATA, section.data)
Simon Glassd1d3ad72020-10-26 17:40:13 -06004382 self.assertEqual(all, section.GetPaddedData())
Simon Glassf2c0dd82020-10-26 17:40:01 -06004383
4384 entry = section.GetEntries()['u-boot']
4385 self.assertEqual(16, entry.offset)
4386 self.assertEqual(len(U_BOOT_DATA), entry.size)
4387 self.assertEqual(U_BOOT_DATA, entry.data)
Simon Glassfb91d562020-09-06 10:35:33 -06004388
Simon Glass7d398bb2020-10-26 17:40:14 -06004389 def testSectionPad(self):
4390 """Testing padding with sections"""
4391 data = self._DoReadFile('180_section_pad.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07004392 expected = (tools.get_bytes(ord('&'), 3) +
4393 tools.get_bytes(ord('!'), 5) +
Simon Glass7d398bb2020-10-26 17:40:14 -06004394 U_BOOT_DATA +
Simon Glassc1aa66e2022-01-29 14:14:04 -07004395 tools.get_bytes(ord('!'), 1) +
4396 tools.get_bytes(ord('&'), 2))
Simon Glass7d398bb2020-10-26 17:40:14 -06004397 self.assertEqual(expected, data)
4398
4399 def testSectionAlign(self):
4400 """Testing alignment with sections"""
4401 data = self._DoReadFileDtb('181_section_align.dts', map=True)[0]
4402 expected = (b'\0' + # fill section
Simon Glassc1aa66e2022-01-29 14:14:04 -07004403 tools.get_bytes(ord('&'), 1) + # padding to section align
Simon Glass7d398bb2020-10-26 17:40:14 -06004404 b'\0' + # fill section
Simon Glassc1aa66e2022-01-29 14:14:04 -07004405 tools.get_bytes(ord('!'), 3) + # padding to u-boot align
Simon Glass7d398bb2020-10-26 17:40:14 -06004406 U_BOOT_DATA +
Simon Glassc1aa66e2022-01-29 14:14:04 -07004407 tools.get_bytes(ord('!'), 4) + # padding to u-boot size
4408 tools.get_bytes(ord('!'), 4)) # padding to section size
Simon Glass7d398bb2020-10-26 17:40:14 -06004409 self.assertEqual(expected, data)
4410
Simon Glass8f5ef892020-10-26 17:40:25 -06004411 def testCompressImage(self):
4412 """Test compression of the entire image"""
4413 self._CheckLz4()
4414 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4415 '182_compress_image.dts', use_real_dtb=True, update_dtb=True)
4416 dtb = fdt.Fdt(out_dtb_fname)
4417 dtb.Scan()
4418 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4419 'uncomp-size'])
4420 orig = self._decompress(data)
4421 self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, orig)
4422
4423 # Do a sanity check on various fields
4424 image = control.images['image']
4425 entries = image.GetEntries()
4426 self.assertEqual(2, len(entries))
4427
4428 entry = entries['blob']
4429 self.assertEqual(COMPRESS_DATA, entry.data)
4430 self.assertEqual(len(COMPRESS_DATA), entry.size)
4431
4432 entry = entries['u-boot']
4433 self.assertEqual(U_BOOT_DATA, entry.data)
4434 self.assertEqual(len(U_BOOT_DATA), entry.size)
4435
4436 self.assertEqual(len(data), image.size)
4437 self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, image.uncomp_data)
4438 self.assertEqual(len(COMPRESS_DATA + U_BOOT_DATA), image.uncomp_size)
4439 orig = self._decompress(image.data)
4440 self.assertEqual(orig, image.uncomp_data)
4441
4442 expected = {
4443 'blob:offset': 0,
4444 'blob:size': len(COMPRESS_DATA),
4445 'u-boot:offset': len(COMPRESS_DATA),
4446 'u-boot:size': len(U_BOOT_DATA),
4447 'uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4448 'offset': 0,
4449 'image-pos': 0,
4450 'size': len(data),
4451 }
4452 self.assertEqual(expected, props)
4453
4454 def testCompressImageLess(self):
4455 """Test compression where compression reduces the image size"""
4456 self._CheckLz4()
4457 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4458 '183_compress_image_less.dts', use_real_dtb=True, update_dtb=True)
4459 dtb = fdt.Fdt(out_dtb_fname)
4460 dtb.Scan()
4461 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4462 'uncomp-size'])
4463 orig = self._decompress(data)
4464
4465 self.assertEquals(COMPRESS_DATA + COMPRESS_DATA + U_BOOT_DATA, orig)
4466
4467 # Do a sanity check on various fields
4468 image = control.images['image']
4469 entries = image.GetEntries()
4470 self.assertEqual(2, len(entries))
4471
4472 entry = entries['blob']
4473 self.assertEqual(COMPRESS_DATA_BIG, entry.data)
4474 self.assertEqual(len(COMPRESS_DATA_BIG), entry.size)
4475
4476 entry = entries['u-boot']
4477 self.assertEqual(U_BOOT_DATA, entry.data)
4478 self.assertEqual(len(U_BOOT_DATA), entry.size)
4479
4480 self.assertEqual(len(data), image.size)
4481 self.assertEqual(COMPRESS_DATA_BIG + U_BOOT_DATA, image.uncomp_data)
4482 self.assertEqual(len(COMPRESS_DATA_BIG + U_BOOT_DATA),
4483 image.uncomp_size)
4484 orig = self._decompress(image.data)
4485 self.assertEqual(orig, image.uncomp_data)
4486
4487 expected = {
4488 'blob:offset': 0,
4489 'blob:size': len(COMPRESS_DATA_BIG),
4490 'u-boot:offset': len(COMPRESS_DATA_BIG),
4491 'u-boot:size': len(U_BOOT_DATA),
4492 'uncomp-size': len(COMPRESS_DATA_BIG + U_BOOT_DATA),
4493 'offset': 0,
4494 'image-pos': 0,
4495 'size': len(data),
4496 }
4497 self.assertEqual(expected, props)
4498
4499 def testCompressSectionSize(self):
4500 """Test compression of a section with a fixed size"""
4501 self._CheckLz4()
4502 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4503 '184_compress_section_size.dts', use_real_dtb=True, update_dtb=True)
4504 dtb = fdt.Fdt(out_dtb_fname)
4505 dtb.Scan()
4506 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4507 'uncomp-size'])
4508 orig = self._decompress(data)
4509 self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, orig)
4510 expected = {
4511 'section/blob:offset': 0,
4512 'section/blob:size': len(COMPRESS_DATA),
4513 'section/u-boot:offset': len(COMPRESS_DATA),
4514 'section/u-boot:size': len(U_BOOT_DATA),
4515 'section:offset': 0,
4516 'section:image-pos': 0,
4517 'section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4518 'section:size': 0x30,
4519 'offset': 0,
4520 'image-pos': 0,
4521 'size': 0x30,
4522 }
4523 self.assertEqual(expected, props)
4524
4525 def testCompressSection(self):
4526 """Test compression of a section with no fixed size"""
4527 self._CheckLz4()
4528 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4529 '185_compress_section.dts', use_real_dtb=True, update_dtb=True)
4530 dtb = fdt.Fdt(out_dtb_fname)
4531 dtb.Scan()
4532 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4533 'uncomp-size'])
4534 orig = self._decompress(data)
4535 self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, orig)
4536 expected = {
4537 'section/blob:offset': 0,
4538 'section/blob:size': len(COMPRESS_DATA),
4539 'section/u-boot:offset': len(COMPRESS_DATA),
4540 'section/u-boot:size': len(U_BOOT_DATA),
4541 'section:offset': 0,
4542 'section:image-pos': 0,
4543 'section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4544 'section:size': len(data),
4545 'offset': 0,
4546 'image-pos': 0,
4547 'size': len(data),
4548 }
4549 self.assertEqual(expected, props)
4550
Stefan Herbrechtsmeierc3665a82022-08-19 16:25:31 +02004551 def testLz4Missing(self):
4552 """Test that binman still produces an image if lz4 is missing"""
4553 with test_util.capture_sys_output() as (_, stderr):
4554 self._DoTestFile('185_compress_section.dts',
4555 force_missing_bintools='lz4')
4556 err = stderr.getvalue()
Simon Glass193d3db2023-02-07 14:34:18 -07004557 self.assertRegex(err, "Image 'image'.*missing bintools.*: lz4")
Stefan Herbrechtsmeierc3665a82022-08-19 16:25:31 +02004558
Simon Glass8f5ef892020-10-26 17:40:25 -06004559 def testCompressExtra(self):
4560 """Test compression of a section with no fixed size"""
4561 self._CheckLz4()
4562 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4563 '186_compress_extra.dts', use_real_dtb=True, update_dtb=True)
4564 dtb = fdt.Fdt(out_dtb_fname)
4565 dtb.Scan()
4566 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4567 'uncomp-size'])
4568
4569 base = data[len(U_BOOT_DATA):]
4570 self.assertEquals(U_BOOT_DATA, base[:len(U_BOOT_DATA)])
4571 rest = base[len(U_BOOT_DATA):]
4572
4573 # Check compressed data
Stefan Herbrechtsmeiercbe2e752022-08-19 16:25:28 +02004574 bintool = self.comp_bintools['lz4']
4575 expect1 = bintool.compress(COMPRESS_DATA + U_BOOT_DATA)
Stefan Herbrechtsmeierfa24f552022-08-19 16:25:23 +02004576 data1 = rest[:len(expect1)]
4577 section1 = self._decompress(data1)
4578 self.assertEquals(expect1, data1)
Simon Glass8f5ef892020-10-26 17:40:25 -06004579 self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, section1)
4580 rest1 = rest[len(expect1):]
4581
Stefan Herbrechtsmeiercbe2e752022-08-19 16:25:28 +02004582 expect2 = bintool.compress(COMPRESS_DATA + COMPRESS_DATA)
Stefan Herbrechtsmeierfa24f552022-08-19 16:25:23 +02004583 data2 = rest1[:len(expect2)]
4584 section2 = self._decompress(data2)
4585 self.assertEquals(expect2, data2)
Simon Glass8f5ef892020-10-26 17:40:25 -06004586 self.assertEquals(COMPRESS_DATA + COMPRESS_DATA, section2)
4587 rest2 = rest1[len(expect2):]
4588
4589 expect_size = (len(U_BOOT_DATA) + len(U_BOOT_DATA) + len(expect1) +
4590 len(expect2) + len(U_BOOT_DATA))
4591 #self.assertEquals(expect_size, len(data))
4592
4593 #self.assertEquals(U_BOOT_DATA, rest2)
4594
4595 self.maxDiff = None
4596 expected = {
4597 'u-boot:offset': 0,
4598 'u-boot:image-pos': 0,
4599 'u-boot:size': len(U_BOOT_DATA),
4600
4601 'base:offset': len(U_BOOT_DATA),
4602 'base:image-pos': len(U_BOOT_DATA),
4603 'base:size': len(data) - len(U_BOOT_DATA),
4604 'base/u-boot:offset': 0,
4605 'base/u-boot:image-pos': len(U_BOOT_DATA),
4606 'base/u-boot:size': len(U_BOOT_DATA),
4607 'base/u-boot2:offset': len(U_BOOT_DATA) + len(expect1) +
4608 len(expect2),
4609 'base/u-boot2:image-pos': len(U_BOOT_DATA) * 2 + len(expect1) +
4610 len(expect2),
4611 'base/u-boot2:size': len(U_BOOT_DATA),
4612
4613 'base/section:offset': len(U_BOOT_DATA),
4614 'base/section:image-pos': len(U_BOOT_DATA) * 2,
4615 'base/section:size': len(expect1),
4616 'base/section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4617 'base/section/blob:offset': 0,
4618 'base/section/blob:size': len(COMPRESS_DATA),
4619 'base/section/u-boot:offset': len(COMPRESS_DATA),
4620 'base/section/u-boot:size': len(U_BOOT_DATA),
4621
4622 'base/section2:offset': len(U_BOOT_DATA) + len(expect1),
4623 'base/section2:image-pos': len(U_BOOT_DATA) * 2 + len(expect1),
4624 'base/section2:size': len(expect2),
4625 'base/section2:uncomp-size': len(COMPRESS_DATA + COMPRESS_DATA),
4626 'base/section2/blob:offset': 0,
4627 'base/section2/blob:size': len(COMPRESS_DATA),
4628 'base/section2/blob2:offset': len(COMPRESS_DATA),
4629 'base/section2/blob2:size': len(COMPRESS_DATA),
4630
4631 'offset': 0,
4632 'image-pos': 0,
4633 'size': len(data),
4634 }
4635 self.assertEqual(expected, props)
4636
Simon Glass870a9ea2021-01-06 21:35:15 -07004637 def testSymbolsSubsection(self):
4638 """Test binman can assign symbols from a subsection"""
Alper Nebi Yasak367ecbf2022-06-18 15:13:11 +03004639 self.checkSymbols('187_symbols_sub.dts', U_BOOT_SPL_DATA, 0x1c)
Simon Glass870a9ea2021-01-06 21:35:15 -07004640
Simon Glass939d1062021-01-06 21:35:16 -07004641 def testReadImageEntryArg(self):
4642 """Test reading an image that would need an entry arg to generate"""
4643 entry_args = {
4644 'cros-ec-rw-path': 'ecrw.bin',
4645 }
4646 data = self.data = self._DoReadFileDtb(
4647 '188_image_entryarg.dts',use_real_dtb=True, update_dtb=True,
4648 entry_args=entry_args)
4649
Simon Glassc1aa66e2022-01-29 14:14:04 -07004650 image_fname = tools.get_output_filename('image.bin')
Simon Glass939d1062021-01-06 21:35:16 -07004651 orig_image = control.images['image']
4652
4653 # This should not generate an error about the missing 'cros-ec-rw-path'
4654 # since we are reading the image from a file. Compare with
4655 # testEntryArgsRequired()
4656 image = Image.FromFile(image_fname)
4657 self.assertEqual(orig_image.GetEntries().keys(),
4658 image.GetEntries().keys())
4659
Simon Glass6eb99322021-01-06 21:35:18 -07004660 def testFilesAlign(self):
4661 """Test alignment with files"""
4662 data = self._DoReadFile('190_files_align.dts')
4663
4664 # The first string is 15 bytes so will align to 16
4665 expect = FILES_DATA[:15] + b'\0' + FILES_DATA[15:]
4666 self.assertEqual(expect, data)
4667
Simon Glass5c6ba712021-01-06 21:35:19 -07004668 def testReadImageSkip(self):
4669 """Test reading an image and accessing its FDT map"""
4670 data = self.data = self._DoReadFileRealDtb('191_read_image_skip.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07004671 image_fname = tools.get_output_filename('image.bin')
Simon Glass5c6ba712021-01-06 21:35:19 -07004672 orig_image = control.images['image']
4673 image = Image.FromFile(image_fname)
4674 self.assertEqual(orig_image.GetEntries().keys(),
4675 image.GetEntries().keys())
4676
4677 orig_entry = orig_image.GetEntries()['fdtmap']
4678 entry = image.GetEntries()['fdtmap']
4679 self.assertEqual(orig_entry.offset, entry.offset)
4680 self.assertEqual(orig_entry.size, entry.size)
4681 self.assertEqual(16, entry.image_pos)
4682
4683 u_boot = image.GetEntries()['section'].GetEntries()['u-boot']
4684
4685 self.assertEquals(U_BOOT_DATA, u_boot.ReadData())
4686
Simon Glass77a64e02021-03-18 20:24:57 +13004687 def testTplNoDtb(self):
4688 """Test that an image with tpl/u-boot-tpl-nodtb.bin can be created"""
Simon Glass0fe44dc2021-04-25 08:39:32 +12004689 self._SetupTplElf()
Simon Glass77a64e02021-03-18 20:24:57 +13004690 data = self._DoReadFile('192_u_boot_tpl_nodtb.dts')
4691 self.assertEqual(U_BOOT_TPL_NODTB_DATA,
4692 data[:len(U_BOOT_TPL_NODTB_DATA)])
4693
Simon Glassd26efc82021-03-18 20:24:58 +13004694 def testTplBssPad(self):
4695 """Test that we can pad TPL's BSS with zeros"""
4696 # ELF file with a '__bss_size' symbol
4697 self._SetupTplElf()
4698 data = self._DoReadFile('193_tpl_bss_pad.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07004699 self.assertEqual(U_BOOT_TPL_DATA + tools.get_bytes(0, 10) + U_BOOT_DATA,
Simon Glassd26efc82021-03-18 20:24:58 +13004700 data)
4701
4702 def testTplBssPadMissing(self):
4703 """Test that a missing symbol is detected"""
4704 self._SetupTplElf('u_boot_ucode_ptr')
4705 with self.assertRaises(ValueError) as e:
4706 self._DoReadFile('193_tpl_bss_pad.dts')
4707 self.assertIn('Expected __bss_size symbol in tpl/u-boot-tpl',
4708 str(e.exception))
4709
Simon Glass06684922021-03-18 20:25:07 +13004710 def checkDtbSizes(self, data, pad_len, start):
4711 """Check the size arguments in a dtb embedded in an image
4712
4713 Args:
4714 data: The image data
4715 pad_len: Length of the pad section in the image, in bytes
4716 start: Start offset of the devicetree to examine, within the image
4717
4718 Returns:
4719 Size of the devicetree in bytes
4720 """
4721 dtb_data = data[start:]
4722 dtb = fdt.Fdt.FromData(dtb_data)
4723 fdt_size = dtb.GetFdtObj().totalsize()
4724 dtb.Scan()
4725 props = self._GetPropTree(dtb, 'size')
4726 self.assertEqual({
4727 'size': len(data),
4728 'u-boot-spl/u-boot-spl-bss-pad:size': pad_len,
4729 'u-boot-spl/u-boot-spl-dtb:size': 801,
4730 'u-boot-spl/u-boot-spl-nodtb:size': len(U_BOOT_SPL_NODTB_DATA),
4731 'u-boot-spl:size': 860,
4732 'u-boot-tpl:size': len(U_BOOT_TPL_DATA),
4733 'u-boot/u-boot-dtb:size': 781,
4734 'u-boot/u-boot-nodtb:size': len(U_BOOT_NODTB_DATA),
4735 'u-boot:size': 827,
4736 }, props)
4737 return fdt_size
4738
4739 def testExpanded(self):
4740 """Test that an expanded entry type is selected when needed"""
4741 self._SetupSplElf()
4742 self._SetupTplElf()
4743
4744 # SPL has a devicetree, TPL does not
4745 entry_args = {
4746 'spl-dtb': '1',
4747 'spl-bss-pad': 'y',
4748 'tpl-dtb': '',
4749 }
4750 self._DoReadFileDtb('194_fdt_incl.dts', use_expanded=True,
4751 entry_args=entry_args)
4752 image = control.images['image']
4753 entries = image.GetEntries()
4754 self.assertEqual(3, len(entries))
4755
4756 # First, u-boot, which should be expanded into u-boot-nodtb and dtb
4757 self.assertIn('u-boot', entries)
4758 entry = entries['u-boot']
4759 self.assertEqual('u-boot-expanded', entry.etype)
4760 subent = entry.GetEntries()
4761 self.assertEqual(2, len(subent))
4762 self.assertIn('u-boot-nodtb', subent)
4763 self.assertIn('u-boot-dtb', subent)
4764
4765 # Second, u-boot-spl, which should be expanded into three parts
4766 self.assertIn('u-boot-spl', entries)
4767 entry = entries['u-boot-spl']
4768 self.assertEqual('u-boot-spl-expanded', entry.etype)
4769 subent = entry.GetEntries()
4770 self.assertEqual(3, len(subent))
4771 self.assertIn('u-boot-spl-nodtb', subent)
4772 self.assertIn('u-boot-spl-bss-pad', subent)
4773 self.assertIn('u-boot-spl-dtb', subent)
4774
4775 # Third, u-boot-tpl, which should be not be expanded, since TPL has no
4776 # devicetree
4777 self.assertIn('u-boot-tpl', entries)
4778 entry = entries['u-boot-tpl']
4779 self.assertEqual('u-boot-tpl', entry.etype)
4780 self.assertEqual(None, entry.GetEntries())
4781
4782 def testExpandedTpl(self):
4783 """Test that an expanded entry type is selected for TPL when needed"""
4784 self._SetupTplElf()
4785
4786 entry_args = {
4787 'tpl-bss-pad': 'y',
4788 'tpl-dtb': 'y',
4789 }
4790 self._DoReadFileDtb('195_fdt_incl_tpl.dts', use_expanded=True,
4791 entry_args=entry_args)
4792 image = control.images['image']
4793 entries = image.GetEntries()
4794 self.assertEqual(1, len(entries))
4795
4796 # We only have u-boot-tpl, which be expanded
4797 self.assertIn('u-boot-tpl', entries)
4798 entry = entries['u-boot-tpl']
4799 self.assertEqual('u-boot-tpl-expanded', entry.etype)
4800 subent = entry.GetEntries()
4801 self.assertEqual(3, len(subent))
4802 self.assertIn('u-boot-tpl-nodtb', subent)
4803 self.assertIn('u-boot-tpl-bss-pad', subent)
4804 self.assertIn('u-boot-tpl-dtb', subent)
4805
4806 def testExpandedNoPad(self):
4807 """Test an expanded entry without BSS pad enabled"""
4808 self._SetupSplElf()
4809 self._SetupTplElf()
4810
4811 # SPL has a devicetree, TPL does not
4812 entry_args = {
4813 'spl-dtb': 'something',
4814 'spl-bss-pad': 'n',
4815 'tpl-dtb': '',
4816 }
4817 self._DoReadFileDtb('194_fdt_incl.dts', use_expanded=True,
4818 entry_args=entry_args)
4819 image = control.images['image']
4820 entries = image.GetEntries()
4821
4822 # Just check u-boot-spl, which should be expanded into two parts
4823 self.assertIn('u-boot-spl', entries)
4824 entry = entries['u-boot-spl']
4825 self.assertEqual('u-boot-spl-expanded', entry.etype)
4826 subent = entry.GetEntries()
4827 self.assertEqual(2, len(subent))
4828 self.assertIn('u-boot-spl-nodtb', subent)
4829 self.assertIn('u-boot-spl-dtb', subent)
4830
4831 def testExpandedTplNoPad(self):
4832 """Test that an expanded entry type with padding disabled in TPL"""
4833 self._SetupTplElf()
4834
4835 entry_args = {
4836 'tpl-bss-pad': '',
4837 'tpl-dtb': 'y',
4838 }
4839 self._DoReadFileDtb('195_fdt_incl_tpl.dts', use_expanded=True,
4840 entry_args=entry_args)
4841 image = control.images['image']
4842 entries = image.GetEntries()
4843 self.assertEqual(1, len(entries))
4844
4845 # We only have u-boot-tpl, which be expanded
4846 self.assertIn('u-boot-tpl', entries)
4847 entry = entries['u-boot-tpl']
4848 self.assertEqual('u-boot-tpl-expanded', entry.etype)
4849 subent = entry.GetEntries()
4850 self.assertEqual(2, len(subent))
4851 self.assertIn('u-boot-tpl-nodtb', subent)
4852 self.assertIn('u-boot-tpl-dtb', subent)
4853
4854 def testFdtInclude(self):
4855 """Test that an Fdt is update within all binaries"""
4856 self._SetupSplElf()
4857 self._SetupTplElf()
4858
4859 # SPL has a devicetree, TPL does not
4860 self.maxDiff = None
4861 entry_args = {
4862 'spl-dtb': '1',
4863 'spl-bss-pad': 'y',
4864 'tpl-dtb': '',
4865 }
4866 # Build the image. It includes two separate devicetree binaries, each
4867 # with their own contents, but all contain the binman definition.
4868 data = self._DoReadFileDtb(
4869 '194_fdt_incl.dts', use_real_dtb=True, use_expanded=True,
4870 update_dtb=True, entry_args=entry_args)[0]
4871 pad_len = 10
4872
4873 # Check the U-Boot dtb
4874 start = len(U_BOOT_NODTB_DATA)
4875 fdt_size = self.checkDtbSizes(data, pad_len, start)
4876
4877 # Now check SPL
4878 start += fdt_size + len(U_BOOT_SPL_NODTB_DATA) + pad_len
4879 fdt_size = self.checkDtbSizes(data, pad_len, start)
4880
4881 # TPL has no devicetree
4882 start += fdt_size + len(U_BOOT_TPL_DATA)
4883 self.assertEqual(len(data), start)
Simon Glass7d398bb2020-10-26 17:40:14 -06004884
Simon Glass3d433382021-03-21 18:24:30 +13004885 def testSymbolsExpanded(self):
4886 """Test binman can assign symbols in expanded entries"""
4887 entry_args = {
4888 'spl-dtb': '1',
4889 }
4890 self.checkSymbols('197_symbols_expand.dts', U_BOOT_SPL_NODTB_DATA +
4891 U_BOOT_SPL_DTB_DATA, 0x38,
4892 entry_args=entry_args, use_expanded=True)
4893
Simon Glass189f2912021-03-21 18:24:31 +13004894 def testCollection(self):
4895 """Test a collection"""
4896 data = self._DoReadFile('198_collection.dts')
4897 self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA +
Simon Glassc1aa66e2022-01-29 14:14:04 -07004898 tools.get_bytes(0xff, 2) + U_BOOT_NODTB_DATA +
4899 tools.get_bytes(0xfe, 3) + U_BOOT_DTB_DATA,
Simon Glass189f2912021-03-21 18:24:31 +13004900 data)
4901
Simon Glass631f7522021-03-21 18:24:32 +13004902 def testCollectionSection(self):
4903 """Test a collection where a section must be built first"""
4904 # Sections never have their contents when GetData() is called, but when
Simon Glassd34bcdd2021-11-23 11:03:47 -07004905 # BuildSectionData() is called with required=True, a section will force
Simon Glass631f7522021-03-21 18:24:32 +13004906 # building the contents, producing an error is anything is still
4907 # missing.
4908 data = self._DoReadFile('199_collection_section.dts')
4909 section = U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA
Simon Glassc1aa66e2022-01-29 14:14:04 -07004910 self.assertEqual(section + U_BOOT_DATA + tools.get_bytes(0xff, 2) +
4911 section + tools.get_bytes(0xfe, 3) + U_BOOT_DATA,
Simon Glass631f7522021-03-21 18:24:32 +13004912 data)
4913
Simon Glass5ff9fed2021-03-21 18:24:33 +13004914 def testAlignDefault(self):
4915 """Test that default alignment works on sections"""
4916 data = self._DoReadFile('200_align_default.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07004917 expected = (U_BOOT_DATA + tools.get_bytes(0, 8 - len(U_BOOT_DATA)) +
Simon Glass5ff9fed2021-03-21 18:24:33 +13004918 U_BOOT_DATA)
4919 # Special alignment for section
Simon Glassc1aa66e2022-01-29 14:14:04 -07004920 expected += tools.get_bytes(0, 32 - len(expected))
Simon Glass5ff9fed2021-03-21 18:24:33 +13004921 # No alignment within the nested section
4922 expected += U_BOOT_DATA + U_BOOT_NODTB_DATA;
4923 # Now the final piece, which should be default-aligned
Simon Glassc1aa66e2022-01-29 14:14:04 -07004924 expected += tools.get_bytes(0, 88 - len(expected)) + U_BOOT_NODTB_DATA
Simon Glass5ff9fed2021-03-21 18:24:33 +13004925 self.assertEqual(expected, data)
Simon Glass631f7522021-03-21 18:24:32 +13004926
Bin Meng4c4d6072021-05-10 20:23:33 +08004927 def testPackOpenSBI(self):
4928 """Test that an image with an OpenSBI binary can be created"""
4929 data = self._DoReadFile('201_opensbi.dts')
4930 self.assertEqual(OPENSBI_DATA, data[:len(OPENSBI_DATA)])
4931
Simon Glassc69d19c2021-07-06 10:36:37 -06004932 def testSectionsSingleThread(self):
4933 """Test sections without multithreading"""
4934 data = self._DoReadFileDtb('055_sections.dts', threads=0)[0]
Simon Glassc1aa66e2022-01-29 14:14:04 -07004935 expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) +
4936 U_BOOT_DATA + tools.get_bytes(ord('a'), 12) +
4937 U_BOOT_DATA + tools.get_bytes(ord('&'), 4))
Simon Glassc69d19c2021-07-06 10:36:37 -06004938 self.assertEqual(expected, data)
4939
4940 def testThreadTimeout(self):
4941 """Test handling a thread that takes too long"""
4942 with self.assertRaises(ValueError) as e:
4943 self._DoTestFile('202_section_timeout.dts',
4944 test_section_timeout=True)
Simon Glassb2dfe832021-10-18 12:13:15 -06004945 self.assertIn("Timed out obtaining contents", str(e.exception))
Simon Glassc69d19c2021-07-06 10:36:37 -06004946
Simon Glass03ebc202021-07-06 10:36:41 -06004947 def testTiming(self):
4948 """Test output of timing information"""
4949 data = self._DoReadFile('055_sections.dts')
4950 with test_util.capture_sys_output() as (stdout, stderr):
4951 state.TimingShow()
4952 self.assertIn('read:', stdout.getvalue())
4953 self.assertIn('compress:', stdout.getvalue())
4954
Simon Glass0427bed2021-11-03 21:09:18 -06004955 def testUpdateFdtInElf(self):
4956 """Test that we can update the devicetree in an ELF file"""
Stefan Herbrechtsmeier6ac7a832022-08-19 16:25:18 +02004957 if not elf.ELF_TOOLS:
4958 self.skipTest('Python elftools not available')
Simon Glass0427bed2021-11-03 21:09:18 -06004959 infile = elf_fname = self.ElfTestFile('u_boot_binman_embed')
4960 outfile = os.path.join(self._indir, 'u-boot.out')
4961 begin_sym = 'dtb_embed_begin'
4962 end_sym = 'dtb_embed_end'
4963 retcode = self._DoTestFile(
4964 '060_fdt_update.dts', update_dtb=True,
4965 update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym]))
4966 self.assertEqual(0, retcode)
4967
4968 # Check that the output file does in fact contact a dtb with the binman
4969 # definition in the correct place
4970 syms = elf.GetSymbolFileOffset(infile,
4971 ['dtb_embed_begin', 'dtb_embed_end'])
Simon Glassc1aa66e2022-01-29 14:14:04 -07004972 data = tools.read_file(outfile)
Simon Glass0427bed2021-11-03 21:09:18 -06004973 dtb_data = data[syms['dtb_embed_begin'].offset:
4974 syms['dtb_embed_end'].offset]
4975
4976 dtb = fdt.Fdt.FromData(dtb_data)
4977 dtb.Scan()
4978 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
4979 self.assertEqual({
4980 'image-pos': 0,
4981 'offset': 0,
4982 '_testing:offset': 32,
4983 '_testing:size': 2,
4984 '_testing:image-pos': 32,
4985 'section@0/u-boot:offset': 0,
4986 'section@0/u-boot:size': len(U_BOOT_DATA),
4987 'section@0/u-boot:image-pos': 0,
4988 'section@0:offset': 0,
4989 'section@0:size': 16,
4990 'section@0:image-pos': 0,
4991
4992 'section@1/u-boot:offset': 0,
4993 'section@1/u-boot:size': len(U_BOOT_DATA),
4994 'section@1/u-boot:image-pos': 16,
4995 'section@1:offset': 16,
4996 'section@1:size': 16,
4997 'section@1:image-pos': 16,
4998 'size': 40
4999 }, props)
5000
5001 def testUpdateFdtInElfInvalid(self):
5002 """Test that invalid args are detected with --update-fdt-in-elf"""
5003 with self.assertRaises(ValueError) as e:
5004 self._DoTestFile('060_fdt_update.dts', update_fdt_in_elf='fred')
5005 self.assertIn("Invalid args ['fred'] to --update-fdt-in-elf",
5006 str(e.exception))
5007
5008 def testUpdateFdtInElfNoSyms(self):
5009 """Test that missing symbols are detected with --update-fdt-in-elf"""
Stefan Herbrechtsmeier6ac7a832022-08-19 16:25:18 +02005010 if not elf.ELF_TOOLS:
5011 self.skipTest('Python elftools not available')
Simon Glass0427bed2021-11-03 21:09:18 -06005012 infile = elf_fname = self.ElfTestFile('u_boot_binman_embed')
5013 outfile = ''
5014 begin_sym = 'wrong_begin'
5015 end_sym = 'wrong_end'
5016 with self.assertRaises(ValueError) as e:
5017 self._DoTestFile(
5018 '060_fdt_update.dts',
5019 update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym]))
5020 self.assertIn("Expected two symbols 'wrong_begin' and 'wrong_end': got 0:",
5021 str(e.exception))
5022
5023 def testUpdateFdtInElfTooSmall(self):
5024 """Test that an over-large dtb is detected with --update-fdt-in-elf"""
Stefan Herbrechtsmeier6ac7a832022-08-19 16:25:18 +02005025 if not elf.ELF_TOOLS:
5026 self.skipTest('Python elftools not available')
Simon Glass0427bed2021-11-03 21:09:18 -06005027 infile = elf_fname = self.ElfTestFile('u_boot_binman_embed_sm')
5028 outfile = os.path.join(self._indir, 'u-boot.out')
5029 begin_sym = 'dtb_embed_begin'
5030 end_sym = 'dtb_embed_end'
5031 with self.assertRaises(ValueError) as e:
5032 self._DoTestFile(
5033 '060_fdt_update.dts', update_dtb=True,
5034 update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym]))
5035 self.assertRegex(
5036 str(e.exception),
5037 "Not enough space in '.*u_boot_binman_embed_sm' for data length.*")
5038
Simon Glassc475dec2021-11-23 11:03:42 -07005039 def testVersion(self):
5040 """Test we can get the binman version"""
5041 version = '(unreleased)'
5042 self.assertEqual(version, state.GetVersion(self._indir))
5043
5044 with self.assertRaises(SystemExit):
5045 with test_util.capture_sys_output() as (_, stderr):
5046 self._DoBinman('-V')
5047 self.assertEqual('Binman %s\n' % version, stderr.getvalue())
5048
5049 # Try running the tool too, just to be safe
5050 result = self._RunBinman('-V')
5051 self.assertEqual('Binman %s\n' % version, result.stderr)
5052
5053 # Set up a version file to make sure that works
5054 version = 'v2025.01-rc2'
Simon Glassc1aa66e2022-01-29 14:14:04 -07005055 tools.write_file(os.path.join(self._indir, 'version'), version,
Simon Glassc475dec2021-11-23 11:03:42 -07005056 binary=False)
5057 self.assertEqual(version, state.GetVersion(self._indir))
5058
Simon Glass943bf782021-11-23 21:09:50 -07005059 def testAltFormat(self):
5060 """Test that alternative formats can be used to extract"""
5061 self._DoReadFileRealDtb('213_fdtmap_alt_format.dts')
5062
5063 try:
5064 tmpdir, updated_fname = self._SetupImageInTmpdir()
5065 with test_util.capture_sys_output() as (stdout, _):
5066 self._DoBinman('extract', '-i', updated_fname, '-F', 'list')
5067 self.assertEqual(
5068 '''Flag (-F) Entry type Description
5069fdt fdtmap Extract the devicetree blob from the fdtmap
5070''',
5071 stdout.getvalue())
5072
5073 dtb = os.path.join(tmpdir, 'fdt.dtb')
5074 self._DoBinman('extract', '-i', updated_fname, '-F', 'fdt', '-f',
5075 dtb, 'fdtmap')
5076
5077 # Check that we can read it and it can be scanning, meaning it does
5078 # not have a 16-byte fdtmap header
Simon Glassc1aa66e2022-01-29 14:14:04 -07005079 data = tools.read_file(dtb)
Simon Glass943bf782021-11-23 21:09:50 -07005080 dtb = fdt.Fdt.FromData(data)
5081 dtb.Scan()
5082
5083 # Now check u-boot which has no alt_format
5084 fname = os.path.join(tmpdir, 'fdt.dtb')
5085 self._DoBinman('extract', '-i', updated_fname, '-F', 'dummy',
5086 '-f', fname, 'u-boot')
Simon Glassc1aa66e2022-01-29 14:14:04 -07005087 data = tools.read_file(fname)
Simon Glass943bf782021-11-23 21:09:50 -07005088 self.assertEqual(U_BOOT_DATA, data)
5089
5090 finally:
5091 shutil.rmtree(tmpdir)
5092
Simon Glasscc2c5002021-11-23 21:09:52 -07005093 def testExtblobList(self):
5094 """Test an image with an external blob list"""
5095 data = self._DoReadFile('215_blob_ext_list.dts')
5096 self.assertEqual(REFCODE_DATA + FSP_M_DATA, data)
5097
5098 def testExtblobListMissing(self):
5099 """Test an image with a missing external blob"""
5100 with self.assertRaises(ValueError) as e:
5101 self._DoReadFile('216_blob_ext_list_missing.dts')
5102 self.assertIn("Filename 'missing-file' not found in input path",
5103 str(e.exception))
5104
5105 def testExtblobListMissingOk(self):
5106 """Test an image with an missing external blob that is allowed"""
5107 with test_util.capture_sys_output() as (stdout, stderr):
5108 self._DoTestFile('216_blob_ext_list_missing.dts',
5109 allow_missing=True)
5110 err = stderr.getvalue()
Simon Glass193d3db2023-02-07 14:34:18 -07005111 self.assertRegex(err, "Image 'image'.*missing.*: blob-ext")
Simon Glasscc2c5002021-11-23 21:09:52 -07005112
Simon Glass75989722021-11-23 21:08:59 -07005113 def testFip(self):
5114 """Basic test of generation of an ARM Firmware Image Package (FIP)"""
5115 data = self._DoReadFile('203_fip.dts')
5116 hdr, fents = fip_util.decode_fip(data)
5117 self.assertEqual(fip_util.HEADER_MAGIC, hdr.name)
5118 self.assertEqual(fip_util.HEADER_SERIAL, hdr.serial)
5119 self.assertEqual(0x123, hdr.flags)
5120
5121 self.assertEqual(2, len(fents))
5122
5123 fent = fents[0]
5124 self.assertEqual(
5125 bytes([0x47, 0xd4, 0x08, 0x6d, 0x4c, 0xfe, 0x98, 0x46,
5126 0x9b, 0x95, 0x29, 0x50, 0xcb, 0xbd, 0x5a, 0x0]), fent.uuid)
5127 self.assertEqual('soc-fw', fent.fip_type)
5128 self.assertEqual(0x88, fent.offset)
5129 self.assertEqual(len(ATF_BL31_DATA), fent.size)
5130 self.assertEqual(0x123456789abcdef, fent.flags)
5131 self.assertEqual(ATF_BL31_DATA, fent.data)
5132 self.assertEqual(True, fent.valid)
5133
5134 fent = fents[1]
5135 self.assertEqual(
5136 bytes([0x65, 0x92, 0x27, 0x03, 0x2f, 0x74, 0xe6, 0x44,
5137 0x8d, 0xff, 0x57, 0x9a, 0xc1, 0xff, 0x06, 0x10]), fent.uuid)
5138 self.assertEqual('scp-fwu-cfg', fent.fip_type)
5139 self.assertEqual(0x8c, fent.offset)
5140 self.assertEqual(len(ATF_BL31_DATA), fent.size)
5141 self.assertEqual(0, fent.flags)
5142 self.assertEqual(ATF_BL2U_DATA, fent.data)
5143 self.assertEqual(True, fent.valid)
5144
5145 def testFipOther(self):
5146 """Basic FIP with something that isn't a external blob"""
5147 data = self._DoReadFile('204_fip_other.dts')
5148 hdr, fents = fip_util.decode_fip(data)
5149
5150 self.assertEqual(2, len(fents))
5151 fent = fents[1]
5152 self.assertEqual('rot-cert', fent.fip_type)
5153 self.assertEqual(b'aa', fent.data)
5154
Simon Glass75989722021-11-23 21:08:59 -07005155 def testFipNoType(self):
5156 """FIP with an entry of an unknown type"""
5157 with self.assertRaises(ValueError) as e:
5158 self._DoReadFile('205_fip_no_type.dts')
5159 self.assertIn("Must provide a fip-type (node name 'u-boot' is not a known FIP type)",
5160 str(e.exception))
5161
5162 def testFipUuid(self):
5163 """Basic FIP with a manual uuid"""
5164 data = self._DoReadFile('206_fip_uuid.dts')
5165 hdr, fents = fip_util.decode_fip(data)
5166
5167 self.assertEqual(2, len(fents))
5168 fent = fents[1]
5169 self.assertEqual(None, fent.fip_type)
5170 self.assertEqual(
5171 bytes([0xfc, 0x65, 0x13, 0x92, 0x4a, 0x5b, 0x11, 0xec,
5172 0x94, 0x35, 0xff, 0x2d, 0x1c, 0xfc, 0x79, 0x9c]),
5173 fent.uuid)
5174 self.assertEqual(U_BOOT_DATA, fent.data)
5175
5176 def testFipLs(self):
5177 """Test listing a FIP"""
5178 data = self._DoReadFileRealDtb('207_fip_ls.dts')
5179 hdr, fents = fip_util.decode_fip(data)
5180
5181 try:
5182 tmpdir, updated_fname = self._SetupImageInTmpdir()
5183 with test_util.capture_sys_output() as (stdout, stderr):
5184 self._DoBinman('ls', '-i', updated_fname)
5185 finally:
5186 shutil.rmtree(tmpdir)
5187 lines = stdout.getvalue().splitlines()
5188 expected = [
Simon Glass193d3db2023-02-07 14:34:18 -07005189'Name Image-pos Size Entry-type Offset Uncomp-size',
5190'--------------------------------------------------------------',
5191'image 0 2d3 section 0',
5192' atf-fip 0 90 atf-fip 0',
5193' soc-fw 88 4 blob-ext 88',
5194' u-boot 8c 4 u-boot 8c',
5195' fdtmap 90 243 fdtmap 90',
Simon Glass75989722021-11-23 21:08:59 -07005196]
5197 self.assertEqual(expected, lines)
5198
5199 image = control.images['image']
5200 entries = image.GetEntries()
5201 fdtmap = entries['fdtmap']
5202
5203 fdtmap_data = data[fdtmap.image_pos:fdtmap.image_pos + fdtmap.size]
5204 magic = fdtmap_data[:8]
5205 self.assertEqual(b'_FDTMAP_', magic)
Simon Glassc1aa66e2022-01-29 14:14:04 -07005206 self.assertEqual(tools.get_bytes(0, 8), fdtmap_data[8:16])
Simon Glass75989722021-11-23 21:08:59 -07005207
5208 fdt_data = fdtmap_data[16:]
5209 dtb = fdt.Fdt.FromData(fdt_data)
5210 dtb.Scan()
5211 props = self._GetPropTree(dtb, BASE_DTB_PROPS, prefix='/')
5212 self.assertEqual({
5213 'atf-fip/soc-fw:image-pos': 136,
5214 'atf-fip/soc-fw:offset': 136,
5215 'atf-fip/soc-fw:size': 4,
5216 'atf-fip/u-boot:image-pos': 140,
5217 'atf-fip/u-boot:offset': 140,
5218 'atf-fip/u-boot:size': 4,
5219 'atf-fip:image-pos': 0,
5220 'atf-fip:offset': 0,
5221 'atf-fip:size': 144,
5222 'image-pos': 0,
5223 'offset': 0,
5224 'fdtmap:image-pos': fdtmap.image_pos,
5225 'fdtmap:offset': fdtmap.offset,
5226 'fdtmap:size': len(fdtmap_data),
5227 'size': len(data),
5228 }, props)
5229
5230 def testFipExtractOneEntry(self):
5231 """Test extracting a single entry fron an FIP"""
5232 self._DoReadFileRealDtb('207_fip_ls.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07005233 image_fname = tools.get_output_filename('image.bin')
Simon Glass75989722021-11-23 21:08:59 -07005234 fname = os.path.join(self._indir, 'output.extact')
5235 control.ExtractEntries(image_fname, fname, None, ['atf-fip/u-boot'])
Simon Glassc1aa66e2022-01-29 14:14:04 -07005236 data = tools.read_file(fname)
Simon Glass75989722021-11-23 21:08:59 -07005237 self.assertEqual(U_BOOT_DATA, data)
5238
5239 def testFipReplace(self):
5240 """Test replacing a single file in a FIP"""
Simon Glassc1aa66e2022-01-29 14:14:04 -07005241 expected = U_BOOT_DATA + tools.get_bytes(0x78, 50)
Simon Glass75989722021-11-23 21:08:59 -07005242 data = self._DoReadFileRealDtb('208_fip_replace.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07005243 updated_fname = tools.get_output_filename('image-updated.bin')
5244 tools.write_file(updated_fname, data)
Simon Glass75989722021-11-23 21:08:59 -07005245 entry_name = 'atf-fip/u-boot'
5246 control.WriteEntry(updated_fname, entry_name, expected,
5247 allow_resize=True)
5248 actual = control.ReadEntry(updated_fname, entry_name)
5249 self.assertEqual(expected, actual)
5250
Simon Glassc1aa66e2022-01-29 14:14:04 -07005251 new_data = tools.read_file(updated_fname)
Simon Glass75989722021-11-23 21:08:59 -07005252 hdr, fents = fip_util.decode_fip(new_data)
5253
5254 self.assertEqual(2, len(fents))
5255
5256 # Check that the FIP entry is updated
5257 fent = fents[1]
5258 self.assertEqual(0x8c, fent.offset)
5259 self.assertEqual(len(expected), fent.size)
5260 self.assertEqual(0, fent.flags)
5261 self.assertEqual(expected, fent.data)
5262 self.assertEqual(True, fent.valid)
5263
5264 def testFipMissing(self):
5265 with test_util.capture_sys_output() as (stdout, stderr):
5266 self._DoTestFile('209_fip_missing.dts', allow_missing=True)
5267 err = stderr.getvalue()
Simon Glass193d3db2023-02-07 14:34:18 -07005268 self.assertRegex(err, "Image 'image'.*missing.*: rmm-fw")
Simon Glass75989722021-11-23 21:08:59 -07005269
5270 def testFipSize(self):
5271 """Test a FIP with a size property"""
5272 data = self._DoReadFile('210_fip_size.dts')
5273 self.assertEqual(0x100 + len(U_BOOT_DATA), len(data))
5274 hdr, fents = fip_util.decode_fip(data)
5275 self.assertEqual(fip_util.HEADER_MAGIC, hdr.name)
5276 self.assertEqual(fip_util.HEADER_SERIAL, hdr.serial)
5277
5278 self.assertEqual(1, len(fents))
5279
5280 fent = fents[0]
5281 self.assertEqual('soc-fw', fent.fip_type)
5282 self.assertEqual(0x60, fent.offset)
5283 self.assertEqual(len(ATF_BL31_DATA), fent.size)
5284 self.assertEqual(ATF_BL31_DATA, fent.data)
5285 self.assertEqual(True, fent.valid)
5286
5287 rest = data[0x60 + len(ATF_BL31_DATA):0x100]
Simon Glassc1aa66e2022-01-29 14:14:04 -07005288 self.assertEqual(tools.get_bytes(0xff, len(rest)), rest)
Simon Glass75989722021-11-23 21:08:59 -07005289
5290 def testFipBadAlign(self):
5291 """Test that an invalid alignment value in a FIP is detected"""
5292 with self.assertRaises(ValueError) as e:
5293 self._DoTestFile('211_fip_bad_align.dts')
5294 self.assertIn(
5295 "Node \'/binman/atf-fip\': FIP alignment 31 must be a power of two",
5296 str(e.exception))
5297
5298 def testFipCollection(self):
5299 """Test using a FIP in a collection"""
5300 data = self._DoReadFile('212_fip_collection.dts')
5301 entry1 = control.images['image'].GetEntries()['collection']
5302 data1 = data[:entry1.size]
5303 hdr1, fents2 = fip_util.decode_fip(data1)
5304
5305 entry2 = control.images['image'].GetEntries()['atf-fip']
5306 data2 = data[entry2.offset:entry2.offset + entry2.size]
5307 hdr1, fents2 = fip_util.decode_fip(data2)
5308
5309 # The 'collection' entry should have U-Boot included at the end
5310 self.assertEqual(entry1.size - len(U_BOOT_DATA), entry2.size)
5311 self.assertEqual(data1, data2 + U_BOOT_DATA)
5312 self.assertEqual(U_BOOT_DATA, data1[-4:])
5313
5314 # There should be a U-Boot after the final FIP
5315 self.assertEqual(U_BOOT_DATA, data[-4:])
Simon Glassc69d19c2021-07-06 10:36:37 -06005316
Simon Glass32d4f102022-01-12 13:10:35 -07005317 def testFakeBlob(self):
5318 """Test handling of faking an external blob"""
5319 with test_util.capture_sys_output() as (stdout, stderr):
5320 self._DoTestFile('217_fake_blob.dts', allow_missing=True,
5321 allow_fake_blobs=True)
5322 err = stderr.getvalue()
5323 self.assertRegex(
5324 err,
5325 "Image '.*' has faked external blobs and is non-functional: .*")
Simon Glass32d4f102022-01-12 13:10:35 -07005326
Simon Glassf4590e02022-01-09 20:13:46 -07005327 def testExtblobListFaked(self):
5328 """Test an extblob with missing external blob that are faked"""
5329 with test_util.capture_sys_output() as (stdout, stderr):
5330 self._DoTestFile('216_blob_ext_list_missing.dts',
5331 allow_fake_blobs=True)
5332 err = stderr.getvalue()
Simon Glass193d3db2023-02-07 14:34:18 -07005333 self.assertRegex(err, "Image 'image'.*faked.*: blob-ext-list")
Simon Glassf4590e02022-01-09 20:13:46 -07005334
Simon Glass56ee85e2022-01-09 20:13:57 -07005335 def testListBintools(self):
5336 args = ['tool', '--list']
5337 with test_util.capture_sys_output() as (stdout, _):
5338 self._DoBinman(*args)
5339 out = stdout.getvalue().splitlines()
5340 self.assertTrue(len(out) >= 2)
5341
5342 def testFetchBintools(self):
5343 def fail_download(url):
Simon Glassc1aa66e2022-01-29 14:14:04 -07005344 """Take the tools.download() function by raising an exception"""
Simon Glass56ee85e2022-01-09 20:13:57 -07005345 raise urllib.error.URLError('my error')
5346
5347 args = ['tool']
5348 with self.assertRaises(ValueError) as e:
5349 self._DoBinman(*args)
5350 self.assertIn("Invalid arguments to 'tool' subcommand",
5351 str(e.exception))
5352
5353 args = ['tool', '--fetch']
5354 with self.assertRaises(ValueError) as e:
5355 self._DoBinman(*args)
5356 self.assertIn('Please specify bintools to fetch', str(e.exception))
5357
5358 args = ['tool', '--fetch', '_testing']
Simon Glassc1aa66e2022-01-29 14:14:04 -07005359 with unittest.mock.patch.object(tools, 'download',
Simon Glass56ee85e2022-01-09 20:13:57 -07005360 side_effect=fail_download):
5361 with test_util.capture_sys_output() as (stdout, _):
5362 self._DoBinman(*args)
5363 self.assertIn('failed to fetch with all methods', stdout.getvalue())
5364
Simon Glassbc570642022-01-09 20:14:11 -07005365 def testBintoolDocs(self):
5366 """Test for creation of bintool documentation"""
5367 with test_util.capture_sys_output() as (stdout, stderr):
5368 control.write_bintool_docs(control.bintool.Bintool.get_tool_list())
5369 self.assertTrue(len(stdout.getvalue()) > 0)
5370
5371 def testBintoolDocsMissing(self):
5372 """Test handling of missing bintool documentation"""
5373 with self.assertRaises(ValueError) as e:
5374 with test_util.capture_sys_output() as (stdout, stderr):
5375 control.write_bintool_docs(
5376 control.bintool.Bintool.get_tool_list(), 'mkimage')
5377 self.assertIn('Documentation is missing for modules: mkimage',
5378 str(e.exception))
5379
Jan Kiszkafcc87ef2022-01-28 20:37:53 +01005380 def testListWithGenNode(self):
5381 """Check handling of an FDT map when the section cannot be found"""
5382 entry_args = {
5383 'of-list': 'test-fdt1 test-fdt2',
5384 }
5385 data = self._DoReadFileDtb(
5386 '219_fit_gennode.dts',
5387 entry_args=entry_args,
5388 use_real_dtb=True,
5389 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])
5390
5391 try:
5392 tmpdir, updated_fname = self._SetupImageInTmpdir()
5393 with test_util.capture_sys_output() as (stdout, stderr):
5394 self._RunBinman('ls', '-i', updated_fname)
5395 finally:
5396 shutil.rmtree(tmpdir)
5397
Alper Nebi Yasaked293c32022-02-08 01:08:05 +03005398 def testFitSubentryUsesBintool(self):
5399 """Test that binman FIT subentries can use bintools"""
5400 command.test_result = self._HandleGbbCommand
5401 entry_args = {
5402 'keydir': 'devkeys',
5403 'bmpblk': 'bmpblk.bin',
5404 }
5405 data, _, _, _ = self._DoReadFileDtb('220_fit_subentry_bintool.dts',
5406 entry_args=entry_args)
5407
Alper Nebi Yasakf3078d42022-02-08 01:08:07 +03005408 expected = (GBB_DATA + GBB_DATA + tools.get_bytes(0, 8) +
5409 tools.get_bytes(0, 0x2180 - 16))
Alper Nebi Yasaked293c32022-02-08 01:08:05 +03005410 self.assertIn(expected, data)
5411
5412 def testFitSubentryMissingBintool(self):
5413 """Test that binman reports missing bintools for FIT subentries"""
5414 entry_args = {
5415 'keydir': 'devkeys',
5416 }
5417 with test_util.capture_sys_output() as (_, stderr):
5418 self._DoTestFile('220_fit_subentry_bintool.dts',
5419 force_missing_bintools='futility', entry_args=entry_args)
5420 err = stderr.getvalue()
Simon Glass193d3db2023-02-07 14:34:18 -07005421 self.assertRegex(err, "Image 'image'.*missing bintools.*: futility")
Simon Glass32d4f102022-01-12 13:10:35 -07005422
Alper Nebi Yasakee813c82022-02-09 22:02:35 +03005423 def testFitSubentryHashSubnode(self):
5424 """Test an image with a FIT inside"""
Marek Vasutfadad3a2023-07-18 07:23:58 -06005425 self._SetupSplElf()
Alper Nebi Yasakee813c82022-02-09 22:02:35 +03005426 data, _, _, out_dtb_name = self._DoReadFileDtb(
5427 '221_fit_subentry_hash.dts', use_real_dtb=True, update_dtb=True)
5428
5429 mkimage_dtb = fdt.Fdt.FromData(data)
5430 mkimage_dtb.Scan()
5431 binman_dtb = fdt.Fdt(out_dtb_name)
5432 binman_dtb.Scan()
5433
5434 # Check that binman didn't add hash values
5435 fnode = binman_dtb.GetNode('/binman/fit/images/kernel/hash')
5436 self.assertNotIn('value', fnode.props)
5437
5438 fnode = binman_dtb.GetNode('/binman/fit/images/fdt-1/hash')
5439 self.assertNotIn('value', fnode.props)
5440
5441 # Check that mkimage added hash values
5442 fnode = mkimage_dtb.GetNode('/images/kernel/hash')
5443 self.assertIn('value', fnode.props)
5444
5445 fnode = mkimage_dtb.GetNode('/images/fdt-1/hash')
5446 self.assertIn('value', fnode.props)
5447
Roger Quadros47f420a2022-02-19 20:50:04 +02005448 def testPackTeeOs(self):
5449 """Test that an image with an TEE binary can be created"""
5450 data = self._DoReadFile('222_tee_os.dts')
5451 self.assertEqual(TEE_OS_DATA, data[:len(TEE_OS_DATA)])
5452
Simon Glass6a0b5f82022-02-08 11:50:03 -07005453 def testFitFdtOper(self):
5454 """Check handling of a specified FIT operation"""
5455 entry_args = {
5456 'of-list': 'test-fdt1 test-fdt2',
5457 'default-dt': 'test-fdt2',
5458 }
5459 self._DoReadFileDtb(
5460 '223_fit_fdt_oper.dts',
5461 entry_args=entry_args,
5462 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
5463
5464 def testFitFdtBadOper(self):
5465 """Check handling of an FDT map when the section cannot be found"""
5466 with self.assertRaises(ValueError) as exc:
5467 self._DoReadFileDtb('224_fit_bad_oper.dts')
Simon Glassce4e4022022-03-05 20:19:09 -07005468 self.assertIn("Node '/binman/fit': subnode 'images/@fdt-SEQ': Unknown operation 'unknown'",
Simon Glass6a0b5f82022-02-08 11:50:03 -07005469 str(exc.exception))
5470
Simon Glass80a66ae2022-03-05 20:18:59 -07005471 def test_uses_expand_size(self):
5472 """Test that the 'expand-size' property cannot be used anymore"""
5473 with self.assertRaises(ValueError) as e:
5474 data = self._DoReadFile('225_expand_size_bad.dts')
5475 self.assertIn(
5476 "Node '/binman/u-boot': Please use 'extend-size' instead of 'expand-size'",
5477 str(e.exception))
5478
Simon Glass40c8bdd2022-03-05 20:19:12 -07005479 def testFitSplitElf(self):
5480 """Test an image with an FIT with an split-elf operation"""
Stefan Herbrechtsmeier6ac7a832022-08-19 16:25:18 +02005481 if not elf.ELF_TOOLS:
5482 self.skipTest('Python elftools not available')
Simon Glass40c8bdd2022-03-05 20:19:12 -07005483 entry_args = {
5484 'of-list': 'test-fdt1 test-fdt2',
5485 'default-dt': 'test-fdt2',
5486 'atf-bl31-path': 'bl31.elf',
5487 'tee-os-path': 'tee.elf',
5488 }
5489 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
5490 data = self._DoReadFileDtb(
5491 '226_fit_split_elf.dts',
5492 entry_args=entry_args,
5493 extra_indirs=[test_subdir])[0]
5494
5495 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
5496 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
5497
5498 base_keys = {'description', 'type', 'arch', 'os', 'compression',
5499 'data', 'load'}
5500 dtb = fdt.Fdt.FromData(fit_data)
5501 dtb.Scan()
5502
5503 elf_data = tools.read_file(os.path.join(self._indir, 'bl31.elf'))
5504 segments, entry = elf.read_loadable_segments(elf_data)
5505
5506 # We assume there are two segments
5507 self.assertEquals(2, len(segments))
5508
5509 atf1 = dtb.GetNode('/images/atf-1')
5510 _, start, data = segments[0]
5511 self.assertEqual(base_keys | {'entry'}, atf1.props.keys())
5512 self.assertEqual(entry,
5513 fdt_util.fdt32_to_cpu(atf1.props['entry'].value))
5514 self.assertEqual(start,
5515 fdt_util.fdt32_to_cpu(atf1.props['load'].value))
5516 self.assertEqual(data, atf1.props['data'].bytes)
5517
Jonas Karlman00b3d532023-01-21 19:01:48 +00005518 hash_node = atf1.FindNode('hash')
5519 self.assertIsNotNone(hash_node)
5520 self.assertEqual({'algo', 'value'}, hash_node.props.keys())
5521
Simon Glass40c8bdd2022-03-05 20:19:12 -07005522 atf2 = dtb.GetNode('/images/atf-2')
5523 self.assertEqual(base_keys, atf2.props.keys())
5524 _, start, data = segments[1]
5525 self.assertEqual(start,
5526 fdt_util.fdt32_to_cpu(atf2.props['load'].value))
5527 self.assertEqual(data, atf2.props['data'].bytes)
5528
Jonas Karlman00b3d532023-01-21 19:01:48 +00005529 hash_node = atf2.FindNode('hash')
5530 self.assertIsNotNone(hash_node)
5531 self.assertEqual({'algo', 'value'}, hash_node.props.keys())
5532
5533 hash_node = dtb.GetNode('/images/tee-1/hash-1')
5534 self.assertIsNotNone(hash_node)
5535 self.assertEqual({'algo', 'value'}, hash_node.props.keys())
5536
Simon Glass40c8bdd2022-03-05 20:19:12 -07005537 conf = dtb.GetNode('/configurations')
5538 self.assertEqual({'default'}, conf.props.keys())
5539
5540 for subnode in conf.subnodes:
5541 self.assertEqual({'description', 'fdt', 'loadables'},
5542 subnode.props.keys())
5543 self.assertEqual(
5544 ['atf-1', 'atf-2', 'tee-1', 'tee-2'],
5545 fdt_util.GetStringList(subnode, 'loadables'))
5546
5547 def _check_bad_fit(self, dts):
5548 """Check a bad FIT
5549
5550 This runs with the given dts and returns the assertion raised
5551
5552 Args:
5553 dts (str): dts filename to use
5554
5555 Returns:
5556 str: Assertion string raised
5557 """
5558 entry_args = {
5559 'of-list': 'test-fdt1 test-fdt2',
5560 'default-dt': 'test-fdt2',
5561 'atf-bl31-path': 'bl31.elf',
5562 'tee-os-path': 'tee.elf',
5563 }
5564 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
5565 with self.assertRaises(ValueError) as exc:
5566 self._DoReadFileDtb(dts, entry_args=entry_args,
5567 extra_indirs=[test_subdir])[0]
5568 return str(exc.exception)
5569
5570 def testFitSplitElfBadElf(self):
5571 """Test a FIT split-elf operation with an invalid ELF file"""
Stefan Herbrechtsmeier6ac7a832022-08-19 16:25:18 +02005572 if not elf.ELF_TOOLS:
5573 self.skipTest('Python elftools not available')
Simon Glass40c8bdd2022-03-05 20:19:12 -07005574 TestFunctional._MakeInputFile('bad.elf', tools.get_bytes(100, 100))
5575 entry_args = {
5576 'of-list': 'test-fdt1 test-fdt2',
5577 'default-dt': 'test-fdt2',
5578 'atf-bl31-path': 'bad.elf',
5579 'tee-os-path': 'tee.elf',
5580 }
5581 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
5582 with self.assertRaises(ValueError) as exc:
5583 self._DoReadFileDtb(
5584 '226_fit_split_elf.dts',
5585 entry_args=entry_args,
5586 extra_indirs=[test_subdir])[0]
5587 self.assertIn(
5588 "Node '/binman/fit': subnode 'images/@atf-SEQ': Failed to read ELF file: Magic number does not match",
5589 str(exc.exception))
5590
Simon Glass40c8bdd2022-03-05 20:19:12 -07005591 def checkFitSplitElf(self, **kwargs):
Simon Glass7960a0a2022-08-07 09:46:46 -06005592 """Test an split-elf FIT with a missing ELF file
5593
5594 Args:
5595 kwargs (dict of str): Arguments to pass to _DoTestFile()
5596
5597 Returns:
5598 tuple:
5599 str: stdout result
5600 str: stderr result
5601 """
Simon Glass40c8bdd2022-03-05 20:19:12 -07005602 entry_args = {
5603 'of-list': 'test-fdt1 test-fdt2',
5604 'default-dt': 'test-fdt2',
5605 'atf-bl31-path': 'bl31.elf',
5606 'tee-os-path': 'missing.elf',
5607 }
5608 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
5609 with test_util.capture_sys_output() as (stdout, stderr):
5610 self._DoTestFile(
5611 '226_fit_split_elf.dts', entry_args=entry_args,
Simon Glass7960a0a2022-08-07 09:46:46 -06005612 extra_indirs=[test_subdir], verbosity=3, **kwargs)
5613 out = stdout.getvalue()
5614 err = stderr.getvalue()
5615 return out, err
Simon Glass40c8bdd2022-03-05 20:19:12 -07005616
Stefan Herbrechtsmeier5cb0b252022-08-23 12:46:09 +02005617 def testFitSplitElfBadDirective(self):
5618 """Test a FIT split-elf invalid fit,xxx directive in an image node"""
5619 if not elf.ELF_TOOLS:
5620 self.skipTest('Python elftools not available')
5621 err = self._check_bad_fit('227_fit_bad_dir.dts')
5622 self.assertIn(
5623 "Node '/binman/fit': subnode 'images/@atf-SEQ': Unknown directive 'fit,something'",
5624 err)
5625
5626 def testFitSplitElfBadDirectiveConfig(self):
5627 """Test a FIT split-elf with invalid fit,xxx directive in config"""
5628 if not elf.ELF_TOOLS:
5629 self.skipTest('Python elftools not available')
5630 err = self._check_bad_fit('228_fit_bad_dir_config.dts')
5631 self.assertEqual(
5632 "Node '/binman/fit': subnode 'configurations/@config-SEQ': Unknown directive 'fit,config'",
5633 err)
5634
5635
Simon Glass40c8bdd2022-03-05 20:19:12 -07005636 def testFitSplitElfMissing(self):
5637 """Test an split-elf FIT with a missing ELF file"""
Stefan Herbrechtsmeier6ac7a832022-08-19 16:25:18 +02005638 if not elf.ELF_TOOLS:
5639 self.skipTest('Python elftools not available')
Simon Glass7960a0a2022-08-07 09:46:46 -06005640 out, err = self.checkFitSplitElf(allow_missing=True)
Simon Glass40c8bdd2022-03-05 20:19:12 -07005641 self.assertRegex(
5642 err,
5643 "Image '.*' is missing external blobs and is non-functional: .*")
Simon Glass7960a0a2022-08-07 09:46:46 -06005644 self.assertNotRegex(out, '.*Faked blob.*')
5645 fname = tools.get_output_filename('binman-fake/missing.elf')
5646 self.assertFalse(os.path.exists(fname))
Simon Glass40c8bdd2022-03-05 20:19:12 -07005647
5648 def testFitSplitElfFaked(self):
5649 """Test an split-elf FIT with faked ELF file"""
Stefan Herbrechtsmeier6ac7a832022-08-19 16:25:18 +02005650 if not elf.ELF_TOOLS:
5651 self.skipTest('Python elftools not available')
Simon Glass7960a0a2022-08-07 09:46:46 -06005652 out, err = self.checkFitSplitElf(allow_missing=True, allow_fake_blobs=True)
Simon Glass40c8bdd2022-03-05 20:19:12 -07005653 self.assertRegex(
5654 err,
5655 "Image '.*' is missing external blobs and is non-functional: .*")
Simon Glass7960a0a2022-08-07 09:46:46 -06005656 self.assertRegex(
5657 out,
5658 "Entry '/binman/fit/images/@tee-SEQ/tee-os': Faked blob '.*binman-fake/missing.elf")
5659 fname = tools.get_output_filename('binman-fake/missing.elf')
5660 self.assertTrue(os.path.exists(fname))
Simon Glass40c8bdd2022-03-05 20:19:12 -07005661
Stefan Herbrechtsmeier5cb0b252022-08-23 12:46:09 +02005662 def testMkimageMissingBlob(self):
5663 """Test using mkimage to build an image"""
5664 with test_util.capture_sys_output() as (stdout, stderr):
5665 self._DoTestFile('229_mkimage_missing.dts', allow_missing=True,
5666 allow_fake_blobs=True)
5667 err = stderr.getvalue()
5668 self.assertRegex(
5669 err,
5670 "Image '.*' has faked external blobs and is non-functional: .*")
5671
Philippe Reynesb1c50932022-03-28 22:57:04 +02005672 def testPreLoad(self):
5673 """Test an image with a pre-load header"""
5674 entry_args = {
Simon Glassefda8ab2023-07-24 09:19:57 -06005675 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
Philippe Reynesb1c50932022-03-28 22:57:04 +02005676 }
Simon Glassefda8ab2023-07-24 09:19:57 -06005677 data = self._DoReadFileDtb(
5678 '230_pre_load.dts', entry_args=entry_args,
5679 extra_indirs=[os.path.join(self._binman_dir, 'test')])[0]
Philippe Reynesb1c50932022-03-28 22:57:04 +02005680 self.assertEqual(PRE_LOAD_MAGIC, data[:len(PRE_LOAD_MAGIC)])
5681 self.assertEqual(PRE_LOAD_VERSION, data[4:4 + len(PRE_LOAD_VERSION)])
5682 self.assertEqual(PRE_LOAD_HDR_SIZE, data[8:8 + len(PRE_LOAD_HDR_SIZE)])
5683
Simon Glassefda8ab2023-07-24 09:19:57 -06005684 def testPreLoadNoKey(self):
5685 """Test an image with a pre-load heade0r with missing key"""
5686 with self.assertRaises(FileNotFoundError) as exc:
5687 self._DoReadFile('230_pre_load.dts')
5688 self.assertIn("No such file or directory: 'dev.key'",
5689 str(exc.exception))
5690
Philippe Reynesb1c50932022-03-28 22:57:04 +02005691 def testPreLoadPkcs(self):
5692 """Test an image with a pre-load header with padding pkcs"""
Simon Glassefda8ab2023-07-24 09:19:57 -06005693 entry_args = {
5694 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5695 }
5696 data = self._DoReadFileDtb('231_pre_load_pkcs.dts',
5697 entry_args=entry_args)[0]
Philippe Reynesb1c50932022-03-28 22:57:04 +02005698 self.assertEqual(PRE_LOAD_MAGIC, data[:len(PRE_LOAD_MAGIC)])
5699 self.assertEqual(PRE_LOAD_VERSION, data[4:4 + len(PRE_LOAD_VERSION)])
5700 self.assertEqual(PRE_LOAD_HDR_SIZE, data[8:8 + len(PRE_LOAD_HDR_SIZE)])
5701
5702 def testPreLoadPss(self):
5703 """Test an image with a pre-load header with padding pss"""
Simon Glassefda8ab2023-07-24 09:19:57 -06005704 entry_args = {
5705 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5706 }
5707 data = self._DoReadFileDtb('232_pre_load_pss.dts',
5708 entry_args=entry_args)[0]
Philippe Reynesb1c50932022-03-28 22:57:04 +02005709 self.assertEqual(PRE_LOAD_MAGIC, data[:len(PRE_LOAD_MAGIC)])
5710 self.assertEqual(PRE_LOAD_VERSION, data[4:4 + len(PRE_LOAD_VERSION)])
5711 self.assertEqual(PRE_LOAD_HDR_SIZE, data[8:8 + len(PRE_LOAD_HDR_SIZE)])
5712
5713 def testPreLoadInvalidPadding(self):
5714 """Test an image with a pre-load header with an invalid padding"""
Simon Glassefda8ab2023-07-24 09:19:57 -06005715 entry_args = {
5716 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5717 }
Philippe Reynesb1c50932022-03-28 22:57:04 +02005718 with self.assertRaises(ValueError) as e:
Simon Glassefda8ab2023-07-24 09:19:57 -06005719 self._DoReadFileDtb('233_pre_load_invalid_padding.dts',
5720 entry_args=entry_args)
Philippe Reynesb1c50932022-03-28 22:57:04 +02005721
5722 def testPreLoadInvalidSha(self):
5723 """Test an image with a pre-load header with an invalid hash"""
Simon Glassefda8ab2023-07-24 09:19:57 -06005724 entry_args = {
5725 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5726 }
Philippe Reynesb1c50932022-03-28 22:57:04 +02005727 with self.assertRaises(ValueError) as e:
Simon Glassefda8ab2023-07-24 09:19:57 -06005728 self._DoReadFileDtb('234_pre_load_invalid_sha.dts',
5729 entry_args=entry_args)
Philippe Reynesb1c50932022-03-28 22:57:04 +02005730
5731 def testPreLoadInvalidAlgo(self):
5732 """Test an image with a pre-load header with an invalid algo"""
5733 with self.assertRaises(ValueError) as e:
Stefan Herbrechtsmeier5cb0b252022-08-23 12:46:09 +02005734 data = self._DoReadFile('235_pre_load_invalid_algo.dts')
Philippe Reynesb1c50932022-03-28 22:57:04 +02005735
5736 def testPreLoadInvalidKey(self):
5737 """Test an image with a pre-load header with an invalid key"""
Simon Glassefda8ab2023-07-24 09:19:57 -06005738 entry_args = {
5739 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5740 }
Philippe Reynesb1c50932022-03-28 22:57:04 +02005741 with self.assertRaises(ValueError) as e:
Simon Glassefda8ab2023-07-24 09:19:57 -06005742 data = self._DoReadFileDtb('236_pre_load_invalid_key.dts',
5743 entry_args=entry_args)
Roger Quadros47f420a2022-02-19 20:50:04 +02005744
Alper Nebi Yasak67bf2c82022-03-27 18:31:44 +03005745 def _CheckSafeUniqueNames(self, *images):
5746 """Check all entries of given images for unsafe unique names"""
5747 for image in images:
5748 entries = {}
5749 image._CollectEntries(entries, {}, image)
5750 for entry in entries.values():
5751 uniq = entry.GetUniqueName()
5752
5753 # Used as part of a filename, so must not be absolute paths.
5754 self.assertFalse(os.path.isabs(uniq))
5755
5756 def testSafeUniqueNames(self):
5757 """Test entry unique names are safe in single image configuration"""
Stefan Herbrechtsmeier5cb0b252022-08-23 12:46:09 +02005758 data = self._DoReadFileRealDtb('237_unique_names.dts')
Alper Nebi Yasak67bf2c82022-03-27 18:31:44 +03005759
5760 orig_image = control.images['image']
5761 image_fname = tools.get_output_filename('image.bin')
5762 image = Image.FromFile(image_fname)
5763
5764 self._CheckSafeUniqueNames(orig_image, image)
5765
5766 def testSafeUniqueNamesMulti(self):
5767 """Test entry unique names are safe with multiple images"""
Stefan Herbrechtsmeier5cb0b252022-08-23 12:46:09 +02005768 data = self._DoReadFileRealDtb('238_unique_names_multi.dts')
Alper Nebi Yasak67bf2c82022-03-27 18:31:44 +03005769
5770 orig_image = control.images['image']
5771 image_fname = tools.get_output_filename('image.bin')
5772 image = Image.FromFile(image_fname)
5773
5774 self._CheckSafeUniqueNames(orig_image, image)
5775
Alper Nebi Yasak8ee4ec92022-03-27 18:31:45 +03005776 def testReplaceCmdWithBintool(self):
5777 """Test replacing an entry that needs a bintool to pack"""
Stefan Herbrechtsmeier5cb0b252022-08-23 12:46:09 +02005778 data = self._DoReadFileRealDtb('239_replace_with_bintool.dts')
Alper Nebi Yasak8ee4ec92022-03-27 18:31:45 +03005779 expected = U_BOOT_DATA + b'aa'
5780 self.assertEqual(expected, data[:len(expected)])
5781
5782 try:
5783 tmpdir, updated_fname = self._SetupImageInTmpdir()
5784 fname = os.path.join(tmpdir, 'update-testing.bin')
5785 tools.write_file(fname, b'zz')
5786 self._DoBinman('replace', '-i', updated_fname,
5787 '_testing', '-f', fname)
5788
5789 data = tools.read_file(updated_fname)
5790 expected = U_BOOT_DATA + b'zz'
5791 self.assertEqual(expected, data[:len(expected)])
5792 finally:
5793 shutil.rmtree(tmpdir)
5794
5795 def testReplaceCmdOtherWithBintool(self):
5796 """Test replacing an entry when another needs a bintool to pack"""
Stefan Herbrechtsmeier5cb0b252022-08-23 12:46:09 +02005797 data = self._DoReadFileRealDtb('239_replace_with_bintool.dts')
Alper Nebi Yasak8ee4ec92022-03-27 18:31:45 +03005798 expected = U_BOOT_DATA + b'aa'
5799 self.assertEqual(expected, data[:len(expected)])
5800
5801 try:
5802 tmpdir, updated_fname = self._SetupImageInTmpdir()
5803 fname = os.path.join(tmpdir, 'update-u-boot.bin')
5804 tools.write_file(fname, b'x' * len(U_BOOT_DATA))
5805 self._DoBinman('replace', '-i', updated_fname,
5806 'u-boot', '-f', fname)
5807
5808 data = tools.read_file(updated_fname)
5809 expected = b'x' * len(U_BOOT_DATA) + b'aa'
5810 self.assertEqual(expected, data[:len(expected)])
5811 finally:
5812 shutil.rmtree(tmpdir)
5813
Alper Nebi Yasake2ce4fb2022-03-27 18:31:46 +03005814 def testReplaceResizeNoRepackSameSize(self):
5815 """Test replacing entries with same-size data without repacking"""
5816 expected = b'x' * len(U_BOOT_DATA)
5817 data, expected_fdtmap, _ = self._RunReplaceCmd('u-boot', expected)
5818 self.assertEqual(expected, data)
5819
5820 path, fdtmap = state.GetFdtContents('fdtmap')
5821 self.assertIsNotNone(path)
5822 self.assertEqual(expected_fdtmap, fdtmap)
5823
5824 def testReplaceResizeNoRepackSmallerSize(self):
5825 """Test replacing entries with smaller-size data without repacking"""
5826 new_data = b'x'
5827 data, expected_fdtmap, _ = self._RunReplaceCmd('u-boot', new_data)
5828 expected = new_data.ljust(len(U_BOOT_DATA), b'\0')
5829 self.assertEqual(expected, data)
5830
5831 path, fdtmap = state.GetFdtContents('fdtmap')
5832 self.assertIsNotNone(path)
5833 self.assertEqual(expected_fdtmap, fdtmap)
5834
Alper Nebi Yasak74d3b232022-03-27 18:31:48 +03005835 def testExtractFit(self):
5836 """Test extracting a FIT section"""
Stefan Herbrechtsmeier5cb0b252022-08-23 12:46:09 +02005837 self._DoReadFileRealDtb('240_fit_extract_replace.dts')
Alper Nebi Yasak74d3b232022-03-27 18:31:48 +03005838 image_fname = tools.get_output_filename('image.bin')
5839
5840 fit_data = control.ReadEntry(image_fname, 'fit')
5841 fit = fdt.Fdt.FromData(fit_data)
5842 fit.Scan()
5843
5844 # Check subentry data inside the extracted fit
5845 for node_path, expected in [
5846 ('/images/kernel', U_BOOT_DATA),
5847 ('/images/fdt-1', U_BOOT_NODTB_DATA),
5848 ('/images/scr-1', COMPRESS_DATA),
5849 ]:
5850 node = fit.GetNode(node_path)
5851 data = fit.GetProps(node)['data'].bytes
5852 self.assertEqual(expected, data)
5853
5854 def testExtractFitSubentries(self):
5855 """Test extracting FIT section subentries"""
Stefan Herbrechtsmeier5cb0b252022-08-23 12:46:09 +02005856 self._DoReadFileRealDtb('240_fit_extract_replace.dts')
Alper Nebi Yasak74d3b232022-03-27 18:31:48 +03005857 image_fname = tools.get_output_filename('image.bin')
5858
5859 for entry_path, expected in [
5860 ('fit/kernel', U_BOOT_DATA),
5861 ('fit/kernel/u-boot', U_BOOT_DATA),
5862 ('fit/fdt-1', U_BOOT_NODTB_DATA),
5863 ('fit/fdt-1/u-boot-nodtb', U_BOOT_NODTB_DATA),
5864 ('fit/scr-1', COMPRESS_DATA),
5865 ('fit/scr-1/blob', COMPRESS_DATA),
5866 ]:
5867 data = control.ReadEntry(image_fname, entry_path)
5868 self.assertEqual(expected, data)
5869
Alper Nebi Yasak99283e52022-03-27 18:31:49 +03005870 def testReplaceFitSubentryLeafSameSize(self):
5871 """Test replacing a FIT leaf subentry with same-size data"""
5872 new_data = b'x' * len(U_BOOT_DATA)
5873 data, expected_fdtmap, _ = self._RunReplaceCmd(
5874 'fit/kernel/u-boot', new_data,
Stefan Herbrechtsmeier5cb0b252022-08-23 12:46:09 +02005875 dts='240_fit_extract_replace.dts')
Alper Nebi Yasak99283e52022-03-27 18:31:49 +03005876 self.assertEqual(new_data, data)
5877
5878 path, fdtmap = state.GetFdtContents('fdtmap')
5879 self.assertIsNotNone(path)
5880 self.assertEqual(expected_fdtmap, fdtmap)
5881
5882 def testReplaceFitSubentryLeafBiggerSize(self):
5883 """Test replacing a FIT leaf subentry with bigger-size data"""
5884 new_data = b'ub' * len(U_BOOT_NODTB_DATA)
5885 data, expected_fdtmap, _ = self._RunReplaceCmd(
5886 'fit/fdt-1/u-boot-nodtb', new_data,
Stefan Herbrechtsmeier5cb0b252022-08-23 12:46:09 +02005887 dts='240_fit_extract_replace.dts')
Alper Nebi Yasak99283e52022-03-27 18:31:49 +03005888 self.assertEqual(new_data, data)
5889
5890 # Will be repacked, so fdtmap must change
5891 path, fdtmap = state.GetFdtContents('fdtmap')
5892 self.assertIsNotNone(path)
5893 self.assertNotEqual(expected_fdtmap, fdtmap)
5894
5895 def testReplaceFitSubentryLeafSmallerSize(self):
5896 """Test replacing a FIT leaf subentry with smaller-size data"""
5897 new_data = b'x'
5898 expected = new_data.ljust(len(U_BOOT_NODTB_DATA), b'\0')
5899 data, expected_fdtmap, _ = self._RunReplaceCmd(
5900 'fit/fdt-1/u-boot-nodtb', new_data,
Stefan Herbrechtsmeier5cb0b252022-08-23 12:46:09 +02005901 dts='240_fit_extract_replace.dts')
Alper Nebi Yasak99283e52022-03-27 18:31:49 +03005902 self.assertEqual(expected, data)
5903
5904 path, fdtmap = state.GetFdtContents('fdtmap')
5905 self.assertIsNotNone(path)
5906 self.assertEqual(expected_fdtmap, fdtmap)
5907
Alper Nebi Yasak82337bb2022-03-27 18:31:50 +03005908 def testReplaceSectionSimple(self):
Simon Glass7caa3722023-03-02 17:02:44 -07005909 """Test replacing a simple section with same-sized data"""
Alper Nebi Yasak82337bb2022-03-27 18:31:50 +03005910 new_data = b'w' * len(COMPRESS_DATA + U_BOOT_DATA)
Simon Glass7caa3722023-03-02 17:02:44 -07005911 data, expected_fdtmap, image = self._RunReplaceCmd('section',
5912 new_data, dts='241_replace_section_simple.dts')
5913 self.assertEqual(new_data, data)
5914
5915 entries = image.GetEntries()
5916 self.assertIn('section', entries)
5917 entry = entries['section']
5918 self.assertEqual(len(new_data), entry.size)
5919
5920 def testReplaceSectionLarger(self):
5921 """Test replacing a simple section with larger data"""
5922 new_data = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) + 1)
5923 data, expected_fdtmap, image = self._RunReplaceCmd('section',
5924 new_data, dts='241_replace_section_simple.dts')
5925 self.assertEqual(new_data, data)
5926
5927 entries = image.GetEntries()
5928 self.assertIn('section', entries)
5929 entry = entries['section']
5930 self.assertEqual(len(new_data), entry.size)
5931 fentry = entries['fdtmap']
5932 self.assertEqual(entry.offset + entry.size, fentry.offset)
5933
5934 def testReplaceSectionSmaller(self):
5935 """Test replacing a simple section with smaller data"""
5936 new_data = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) - 1) + b'\0'
5937 data, expected_fdtmap, image = self._RunReplaceCmd('section',
5938 new_data, dts='241_replace_section_simple.dts')
5939 self.assertEqual(new_data, data)
5940
5941 # The new size is the same as the old, just with a pad byte at the end
5942 entries = image.GetEntries()
5943 self.assertIn('section', entries)
5944 entry = entries['section']
5945 self.assertEqual(len(new_data), entry.size)
5946
5947 def testReplaceSectionSmallerAllow(self):
5948 """Test failing to replace a simple section with smaller data"""
5949 new_data = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) - 1)
5950 try:
5951 state.SetAllowEntryContraction(True)
5952 with self.assertRaises(ValueError) as exc:
5953 self._RunReplaceCmd('section', new_data,
5954 dts='241_replace_section_simple.dts')
5955 finally:
5956 state.SetAllowEntryContraction(False)
5957
5958 # Since we have no information about the position of things within the
5959 # section, we cannot adjust the position of /section-u-boot so it ends
5960 # up outside the section
Simon Glass73593e42022-08-13 11:40:46 -06005961 self.assertIn(
Simon Glass7caa3722023-03-02 17:02:44 -07005962 "Node '/section/u-boot': Offset 0x24 (36) size 0x4 (4) is outside "
5963 "the section '/section' starting at 0x0 (0) of size 0x27 (39)",
Simon Glass73593e42022-08-13 11:40:46 -06005964 str(exc.exception))
Alper Nebi Yasak82337bb2022-03-27 18:31:50 +03005965
Simon Glassdfe1db42022-08-13 11:40:48 -06005966 def testMkimageImagename(self):
5967 """Test using mkimage with -n holding the data too"""
Marek Vasutfadad3a2023-07-18 07:23:58 -06005968 self._SetupSplElf()
Stefan Herbrechtsmeier5cb0b252022-08-23 12:46:09 +02005969 data = self._DoReadFile('242_mkimage_name.dts')
Simon Glassdfe1db42022-08-13 11:40:48 -06005970
5971 # Check that the data appears in the file somewhere
5972 self.assertIn(U_BOOT_SPL_DATA, data)
5973
Simon Glassf3543e62022-09-06 20:26:52 -06005974 # Get struct legacy_img_hdr -> ih_name
Simon Glassdfe1db42022-08-13 11:40:48 -06005975 name = data[0x20:0x40]
5976
5977 # Build the filename that we expect to be placed in there, by virtue of
5978 # the -n paraameter
5979 expect = os.path.join(tools.get_output_dir(), 'mkimage.mkimage')
5980
5981 # Check that the image name is set to the temporary filename used
5982 self.assertEqual(expect.encode('utf-8')[:0x20], name)
5983
Simon Glass9db9e932022-08-13 11:40:49 -06005984 def testMkimageImage(self):
5985 """Test using mkimage with -n holding the data too"""
Marek Vasutfadad3a2023-07-18 07:23:58 -06005986 self._SetupSplElf()
Stefan Herbrechtsmeier5cb0b252022-08-23 12:46:09 +02005987 data = self._DoReadFile('243_mkimage_image.dts')
Simon Glass9db9e932022-08-13 11:40:49 -06005988
5989 # Check that the data appears in the file somewhere
5990 self.assertIn(U_BOOT_SPL_DATA, data)
5991
Simon Glassf3543e62022-09-06 20:26:52 -06005992 # Get struct legacy_img_hdr -> ih_name
Simon Glass9db9e932022-08-13 11:40:49 -06005993 name = data[0x20:0x40]
5994
5995 # Build the filename that we expect to be placed in there, by virtue of
5996 # the -n paraameter
5997 expect = os.path.join(tools.get_output_dir(), 'mkimage-n.mkimage')
5998
5999 # Check that the image name is set to the temporary filename used
6000 self.assertEqual(expect.encode('utf-8')[:0x20], name)
6001
6002 # Check the corect data is in the imagename file
6003 self.assertEqual(U_BOOT_DATA, tools.read_file(expect))
6004
6005 def testMkimageImageNoContent(self):
6006 """Test using mkimage with -n and no data"""
Marek Vasutfadad3a2023-07-18 07:23:58 -06006007 self._SetupSplElf()
Simon Glass9db9e932022-08-13 11:40:49 -06006008 with self.assertRaises(ValueError) as exc:
Stefan Herbrechtsmeier5cb0b252022-08-23 12:46:09 +02006009 self._DoReadFile('244_mkimage_image_no_content.dts')
Simon Glass9db9e932022-08-13 11:40:49 -06006010 self.assertIn('Could not complete processing of contents',
6011 str(exc.exception))
6012
6013 def testMkimageImageBad(self):
6014 """Test using mkimage with imagename node and data-to-imagename"""
Marek Vasutfadad3a2023-07-18 07:23:58 -06006015 self._SetupSplElf()
Simon Glass9db9e932022-08-13 11:40:49 -06006016 with self.assertRaises(ValueError) as exc:
Stefan Herbrechtsmeier5cb0b252022-08-23 12:46:09 +02006017 self._DoReadFile('245_mkimage_image_bad.dts')
Simon Glass9db9e932022-08-13 11:40:49 -06006018 self.assertIn('Cannot use both imagename node and data-to-imagename',
6019 str(exc.exception))
6020
Simon Glassd626e822022-08-13 11:40:50 -06006021 def testCollectionOther(self):
6022 """Test a collection where the data comes from another section"""
Stefan Herbrechtsmeier5cb0b252022-08-23 12:46:09 +02006023 data = self._DoReadFile('246_collection_other.dts')
Simon Glassd626e822022-08-13 11:40:50 -06006024 self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA +
6025 tools.get_bytes(0xff, 2) + U_BOOT_NODTB_DATA +
6026 tools.get_bytes(0xfe, 3) + U_BOOT_DTB_DATA,
6027 data)
6028
6029 def testMkimageCollection(self):
6030 """Test using a collection referring to an entry in a mkimage entry"""
Marek Vasutfadad3a2023-07-18 07:23:58 -06006031 self._SetupSplElf()
Stefan Herbrechtsmeier5cb0b252022-08-23 12:46:09 +02006032 data = self._DoReadFile('247_mkimage_coll.dts')
Simon Glassd626e822022-08-13 11:40:50 -06006033 expect = U_BOOT_SPL_DATA + U_BOOT_DATA
6034 self.assertEqual(expect, data[:len(expect)])
6035
Stefan Herbrechtsmeier6aa80002022-08-19 16:25:25 +02006036 def testCompressDtbPrependInvalid(self):
6037 """Test that invalid header is detected"""
6038 with self.assertRaises(ValueError) as e:
Stefan Herbrechtsmeier5cb0b252022-08-23 12:46:09 +02006039 self._DoReadFileDtb('248_compress_dtb_prepend_invalid.dts')
Stefan Herbrechtsmeier6aa80002022-08-19 16:25:25 +02006040 self.assertIn("Node '/binman/u-boot-dtb': Invalid prepend in "
6041 "'u-boot-dtb': 'invalid'", str(e.exception))
6042
6043 def testCompressDtbPrependLength(self):
6044 """Test that compress with length header works as expected"""
Stefan Herbrechtsmeier5cb0b252022-08-23 12:46:09 +02006045 data = self._DoReadFileRealDtb('249_compress_dtb_prepend_length.dts')
Stefan Herbrechtsmeier6aa80002022-08-19 16:25:25 +02006046 image = control.images['image']
6047 entries = image.GetEntries()
6048 self.assertIn('u-boot-dtb', entries)
6049 u_boot_dtb = entries['u-boot-dtb']
6050 self.assertIn('fdtmap', entries)
6051 fdtmap = entries['fdtmap']
6052
6053 image_fname = tools.get_output_filename('image.bin')
6054 orig = control.ReadEntry(image_fname, 'u-boot-dtb')
6055 dtb = fdt.Fdt.FromData(orig)
6056 dtb.Scan()
6057 props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
6058 expected = {
6059 'u-boot:size': len(U_BOOT_DATA),
6060 'u-boot-dtb:uncomp-size': len(orig),
6061 'u-boot-dtb:size': u_boot_dtb.size,
6062 'fdtmap:size': fdtmap.size,
6063 'size': len(data),
6064 }
6065 self.assertEqual(expected, props)
6066
6067 # Check implementation
6068 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
6069 rest = data[len(U_BOOT_DATA):]
6070 comp_data_len = struct.unpack('<I', rest[:4])[0]
6071 comp_data = rest[4:4 + comp_data_len]
6072 orig2 = self._decompress(comp_data)
6073 self.assertEqual(orig, orig2)
6074
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +02006075 def testInvalidCompress(self):
6076 """Test that invalid compress algorithm is detected"""
6077 with self.assertRaises(ValueError) as e:
Stefan Herbrechtsmeier5cb0b252022-08-23 12:46:09 +02006078 self._DoTestFile('250_compress_dtb_invalid.dts')
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +02006079 self.assertIn("Unknown algorithm 'invalid'", str(e.exception))
6080
Stefan Herbrechtsmeierda1af352022-08-19 16:25:32 +02006081 def testCompUtilCompressions(self):
6082 """Test compression algorithms"""
6083 for bintool in self.comp_bintools.values():
6084 self._CheckBintool(bintool)
6085 data = bintool.compress(COMPRESS_DATA)
6086 self.assertNotEqual(COMPRESS_DATA, data)
6087 orig = bintool.decompress(data)
6088 self.assertEquals(COMPRESS_DATA, orig)
6089
6090 def testCompUtilVersions(self):
6091 """Test tool version of compression algorithms"""
6092 for bintool in self.comp_bintools.values():
6093 self._CheckBintool(bintool)
6094 version = bintool.version()
6095 self.assertRegex(version, '^v?[0-9]+[0-9.]*')
6096
6097 def testCompUtilPadding(self):
6098 """Test padding of compression algorithms"""
Stefan Herbrechtsmeiercd15b642022-08-19 16:25:38 +02006099 # Skip zstd because it doesn't support padding
6100 for bintool in [v for k,v in self.comp_bintools.items() if k != 'zstd']:
Stefan Herbrechtsmeierda1af352022-08-19 16:25:32 +02006101 self._CheckBintool(bintool)
6102 data = bintool.compress(COMPRESS_DATA)
6103 self.assertNotEqual(COMPRESS_DATA, data)
6104 data += tools.get_bytes(0, 64)
6105 orig = bintool.decompress(data)
6106 self.assertEquals(COMPRESS_DATA, orig)
6107
Stefan Herbrechtsmeiercd15b642022-08-19 16:25:38 +02006108 def testCompressDtbZstd(self):
6109 """Test that zstd compress of device-tree files failed"""
6110 with self.assertRaises(ValueError) as e:
Stefan Herbrechtsmeier5cb0b252022-08-23 12:46:09 +02006111 self._DoTestFile('251_compress_dtb_zstd.dts')
Stefan Herbrechtsmeiercd15b642022-08-19 16:25:38 +02006112 self.assertIn("Node '/binman/u-boot-dtb': The zstd compression "
6113 "requires a length header", str(e.exception))
6114
Quentin Schulz4d91df02022-09-02 15:10:48 +02006115 def testMkimageMultipleDataFiles(self):
6116 """Test passing multiple files to mkimage in a mkimage entry"""
Marek Vasutfadad3a2023-07-18 07:23:58 -06006117 self._SetupSplElf()
6118 self._SetupTplElf()
Quentin Schulz4d91df02022-09-02 15:10:48 +02006119 data = self._DoReadFile('252_mkimage_mult_data.dts')
6120 # Size of files are packed in their 4B big-endian format
6121 expect = struct.pack('>I', len(U_BOOT_TPL_DATA))
6122 expect += struct.pack('>I', len(U_BOOT_SPL_DATA))
6123 # Size info is always followed by a 4B zero value.
6124 expect += tools.get_bytes(0, 4)
6125 expect += U_BOOT_TPL_DATA
6126 # All but last files are 4B-aligned
6127 align_pad = len(U_BOOT_TPL_DATA) % 4
6128 if align_pad:
6129 expect += tools.get_bytes(0, align_pad)
6130 expect += U_BOOT_SPL_DATA
6131 self.assertEqual(expect, data[-len(expect):])
6132
Marek Vasutfadad3a2023-07-18 07:23:58 -06006133 def testMkimageMultipleExpanded(self):
6134 """Test passing multiple files to mkimage in a mkimage entry"""
6135 self._SetupSplElf()
6136 self._SetupTplElf()
6137 entry_args = {
6138 'spl-bss-pad': 'y',
6139 'spl-dtb': 'y',
6140 }
6141 data = self._DoReadFileDtb('252_mkimage_mult_data.dts',
6142 use_expanded=True, entry_args=entry_args)[0]
6143 pad_len = 10
6144 tpl_expect = U_BOOT_TPL_DATA
6145 spl_expect = U_BOOT_SPL_NODTB_DATA + tools.get_bytes(0, pad_len)
6146 spl_expect += U_BOOT_SPL_DTB_DATA
6147
6148 content = data[0x40:]
6149 lens = struct.unpack('>III', content[:12])
6150
6151 # Size of files are packed in their 4B big-endian format
6152 # Size info is always followed by a 4B zero value.
6153 self.assertEqual(len(tpl_expect), lens[0])
6154 self.assertEqual(len(spl_expect), lens[1])
6155 self.assertEqual(0, lens[2])
6156
6157 rest = content[12:]
6158 self.assertEqual(tpl_expect, rest[:len(tpl_expect)])
6159
6160 rest = rest[len(tpl_expect):]
6161 align_pad = len(tpl_expect) % 4
6162 self.assertEqual(tools.get_bytes(0, align_pad), rest[:align_pad])
6163 rest = rest[align_pad:]
6164 self.assertEqual(spl_expect, rest)
6165
Quentin Schulz4d91df02022-09-02 15:10:48 +02006166 def testMkimageMultipleNoContent(self):
6167 """Test passing multiple data files to mkimage with one data file having no content"""
Marek Vasutfadad3a2023-07-18 07:23:58 -06006168 self._SetupSplElf()
Quentin Schulz4d91df02022-09-02 15:10:48 +02006169 with self.assertRaises(ValueError) as exc:
6170 self._DoReadFile('253_mkimage_mult_no_content.dts')
6171 self.assertIn('Could not complete processing of contents',
6172 str(exc.exception))
6173
Quentin Schulz6cc29dc2022-09-02 15:10:49 +02006174 def testMkimageFilename(self):
6175 """Test using mkimage to build a binary with a filename"""
Marek Vasutfadad3a2023-07-18 07:23:58 -06006176 self._SetupSplElf()
Quentin Schulz6cc29dc2022-09-02 15:10:49 +02006177 retcode = self._DoTestFile('254_mkimage_filename.dts')
6178 self.assertEqual(0, retcode)
6179 fname = tools.get_output_filename('mkimage-test.bin')
6180 self.assertTrue(os.path.exists(fname))
6181
Simon Glass6ad24522022-02-28 07:16:54 -07006182 def testVpl(self):
6183 """Test that an image with VPL and its device tree can be created"""
6184 # ELF file with a '__bss_size' symbol
6185 self._SetupVplElf()
6186 data = self._DoReadFile('255_u_boot_vpl.dts')
6187 self.assertEqual(U_BOOT_VPL_DATA + U_BOOT_VPL_DTB_DATA, data)
6188
6189 def testVplNoDtb(self):
6190 """Test that an image with vpl/u-boot-vpl-nodtb.bin can be created"""
6191 self._SetupVplElf()
6192 data = self._DoReadFile('256_u_boot_vpl_nodtb.dts')
6193 self.assertEqual(U_BOOT_VPL_NODTB_DATA,
6194 data[:len(U_BOOT_VPL_NODTB_DATA)])
6195
6196 def testExpandedVpl(self):
6197 """Test that an expanded entry type is selected for TPL when needed"""
6198 self._SetupVplElf()
6199
6200 entry_args = {
6201 'vpl-bss-pad': 'y',
6202 'vpl-dtb': 'y',
6203 }
6204 self._DoReadFileDtb('257_fdt_incl_vpl.dts', use_expanded=True,
6205 entry_args=entry_args)
6206 image = control.images['image']
6207 entries = image.GetEntries()
6208 self.assertEqual(1, len(entries))
6209
6210 # We only have u-boot-vpl, which be expanded
6211 self.assertIn('u-boot-vpl', entries)
6212 entry = entries['u-boot-vpl']
6213 self.assertEqual('u-boot-vpl-expanded', entry.etype)
6214 subent = entry.GetEntries()
6215 self.assertEqual(3, len(subent))
6216 self.assertIn('u-boot-vpl-nodtb', subent)
6217 self.assertIn('u-boot-vpl-bss-pad', subent)
6218 self.assertIn('u-boot-vpl-dtb', subent)
6219
6220 def testVplBssPadMissing(self):
6221 """Test that a missing symbol is detected"""
6222 self._SetupVplElf('u_boot_ucode_ptr')
6223 with self.assertRaises(ValueError) as e:
6224 self._DoReadFile('258_vpl_bss_pad.dts')
6225 self.assertIn('Expected __bss_size symbol in vpl/u-boot-vpl',
6226 str(e.exception))
6227
Neha Malcom Francis3545e852022-10-17 16:36:25 +05306228 def testSymlink(self):
Andrew Davis15432ea2023-07-22 00:14:44 +05306229 """Test that image files can be symlinked"""
Neha Malcom Francis3545e852022-10-17 16:36:25 +05306230 retcode = self._DoTestFile('259_symlink.dts', debug=True, map=True)
6231 self.assertEqual(0, retcode)
6232 image = control.images['test_image']
6233 fname = tools.get_output_filename('test_image.bin')
6234 sname = tools.get_output_filename('symlink_to_test.bin')
6235 self.assertTrue(os.path.islink(sname))
6236 self.assertEqual(os.readlink(sname), fname)
Alper Nebi Yasak8ee4ec92022-03-27 18:31:45 +03006237
Andrew Davis15432ea2023-07-22 00:14:44 +05306238 def testSymlinkOverwrite(self):
6239 """Test that symlinked images can be overwritten"""
6240 testdir = TestFunctional._MakeInputDir('symlinktest')
6241 self._DoTestFile('259_symlink.dts', debug=True, map=True, output_dir=testdir)
6242 # build the same image again in the same directory so that existing symlink is present
6243 self._DoTestFile('259_symlink.dts', debug=True, map=True, output_dir=testdir)
6244 fname = tools.get_output_filename('test_image.bin')
6245 sname = tools.get_output_filename('symlink_to_test.bin')
6246 self.assertTrue(os.path.islink(sname))
6247 self.assertEqual(os.readlink(sname), fname)
6248
Simon Glassd2afb9e2022-10-20 18:22:47 -06006249 def testSymbolsElf(self):
6250 """Test binman can assign symbols embedded in an ELF file"""
6251 if not elf.ELF_TOOLS:
6252 self.skipTest('Python elftools not available')
6253 self._SetupTplElf('u_boot_binman_syms')
6254 self._SetupVplElf('u_boot_binman_syms')
6255 self._SetupSplElf('u_boot_binman_syms')
6256 data = self._DoReadFileDtb('260_symbols_elf.dts')[0]
6257 image_fname = tools.get_output_filename('image.bin')
6258
6259 image = control.images['image']
6260 entries = image.GetEntries()
6261
6262 for entry in entries.values():
6263 # No symbols in u-boot and it has faked contents anyway
6264 if entry.name == 'u-boot':
6265 continue
6266 edata = data[entry.image_pos:entry.image_pos + entry.size]
6267 efname = tools.get_output_filename(f'edata-{entry.name}')
6268 tools.write_file(efname, edata)
6269
6270 syms = elf.GetSymbolFileOffset(efname, ['_binman_u_boot'])
6271 re_name = re.compile('_binman_(u_boot_(.*))_prop_(.*)')
6272 for name, sym in syms.items():
6273 msg = 'test'
6274 val = elf.GetSymbolValue(sym, edata, msg)
6275 entry_m = re_name.match(name)
6276 if entry_m:
6277 ename, prop = entry_m.group(1), entry_m.group(3)
6278 entry, entry_name, prop_name = image.LookupEntry(entries,
6279 name, msg)
6280 if prop_name == 'offset':
6281 expect_val = entry.offset
6282 elif prop_name == 'image_pos':
6283 expect_val = entry.image_pos
6284 elif prop_name == 'size':
6285 expect_val = entry.size
6286 self.assertEqual(expect_val, val)
6287
6288 def testSymbolsElfBad(self):
6289 """Check error when trying to write symbols without the elftools lib"""
6290 if not elf.ELF_TOOLS:
6291 self.skipTest('Python elftools not available')
6292 self._SetupTplElf('u_boot_binman_syms')
6293 self._SetupVplElf('u_boot_binman_syms')
6294 self._SetupSplElf('u_boot_binman_syms')
6295 try:
6296 elf.ELF_TOOLS = False
6297 with self.assertRaises(ValueError) as exc:
6298 self._DoReadFileDtb('260_symbols_elf.dts')
6299 finally:
6300 elf.ELF_TOOLS = True
6301 self.assertIn(
6302 "Section '/binman': entry '/binman/u-boot-spl-elf': "
6303 'Cannot write symbols to an ELF file without Python elftools',
6304 str(exc.exception))
6305
Simon Glassefddab62023-01-07 14:07:08 -07006306 def testSectionFilename(self):
6307 """Check writing of section contents to a file"""
6308 data = self._DoReadFile('261_section_fname.dts')
6309 expected = (b'&&' + U_BOOT_DATA + b'&&&' +
6310 tools.get_bytes(ord('!'), 7) +
6311 U_BOOT_DATA + tools.get_bytes(ord('&'), 12))
6312 self.assertEqual(expected, data)
6313
6314 sect_fname = tools.get_output_filename('outfile.bin')
6315 self.assertTrue(os.path.exists(sect_fname))
6316 sect_data = tools.read_file(sect_fname)
6317 self.assertEqual(U_BOOT_DATA, sect_data)
6318
Simon Glassc8c9f312023-01-07 14:07:12 -07006319 def testAbsent(self):
6320 """Check handling of absent entries"""
6321 data = self._DoReadFile('262_absent.dts')
6322 self.assertEqual(U_BOOT_DATA + U_BOOT_IMG_DATA, data)
6323
Simon Glass2f80c5e2023-01-07 14:07:14 -07006324 def testPackTeeOsOptional(self):
6325 """Test that an image with an optional TEE binary can be created"""
6326 entry_args = {
6327 'tee-os-path': 'tee.elf',
6328 }
6329 data = self._DoReadFileDtb('263_tee_os_opt.dts',
6330 entry_args=entry_args)[0]
6331 self.assertEqual(U_BOOT_DATA + U_BOOT_IMG_DATA, data)
6332
6333 def checkFitTee(self, dts, tee_fname):
6334 """Check that a tee-os entry works and returns data
6335
6336 Args:
6337 dts (str): Device tree filename to use
6338 tee_fname (str): filename containing tee-os
6339
6340 Returns:
6341 bytes: Image contents
6342 """
6343 if not elf.ELF_TOOLS:
6344 self.skipTest('Python elftools not available')
6345 entry_args = {
6346 'of-list': 'test-fdt1 test-fdt2',
6347 'default-dt': 'test-fdt2',
6348 'tee-os-path': tee_fname,
6349 }
6350 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
6351 data = self._DoReadFileDtb(dts, entry_args=entry_args,
6352 extra_indirs=[test_subdir])[0]
6353 return data
6354
6355 def testFitTeeOsOptionalFit(self):
6356 """Test an image with a FIT with an optional OP-TEE binary"""
6357 data = self.checkFitTee('264_tee_os_opt_fit.dts', 'tee.bin')
6358
6359 # There should be only one node, holding the data set up in SetUpClass()
6360 # for tee.bin
6361 dtb = fdt.Fdt.FromData(data)
6362 dtb.Scan()
6363 node = dtb.GetNode('/images/tee-1')
6364 self.assertEqual(TEE_ADDR,
6365 fdt_util.fdt32_to_cpu(node.props['load'].value))
6366 self.assertEqual(TEE_ADDR,
6367 fdt_util.fdt32_to_cpu(node.props['entry'].value))
6368 self.assertEqual(U_BOOT_DATA, node.props['data'].bytes)
6369
Jonas Karlman49dcd1c2023-07-18 20:34:36 +00006370 with test_util.capture_sys_output() as (stdout, stderr):
6371 self.checkFitTee('264_tee_os_opt_fit.dts', '')
6372 err = stderr.getvalue()
6373 self.assertRegex(
6374 err,
6375 "Image '.*' is missing optional external blobs but is still functional: tee-os")
6376
Simon Glass2f80c5e2023-01-07 14:07:14 -07006377 def testFitTeeOsOptionalFitBad(self):
6378 """Test an image with a FIT with an optional OP-TEE binary"""
6379 with self.assertRaises(ValueError) as exc:
6380 self.checkFitTee('265_tee_os_opt_fit_bad.dts', 'tee.bin')
6381 self.assertIn(
6382 "Node '/binman/fit': subnode 'images/@tee-SEQ': Failed to read ELF file: Magic number does not match",
6383 str(exc.exception))
6384
6385 def testFitTeeOsBad(self):
6386 """Test an OP-TEE binary with wrong formats"""
6387 self.make_tee_bin('tee.bad1', 123)
6388 with self.assertRaises(ValueError) as exc:
6389 self.checkFitTee('264_tee_os_opt_fit.dts', 'tee.bad1')
6390 self.assertIn(
6391 "Node '/binman/fit/images/@tee-SEQ/tee-os': OP-TEE paged mode not supported",
6392 str(exc.exception))
6393
6394 self.make_tee_bin('tee.bad2', 0, b'extra data')
6395 with self.assertRaises(ValueError) as exc:
6396 self.checkFitTee('264_tee_os_opt_fit.dts', 'tee.bad2')
6397 self.assertIn(
6398 "Node '/binman/fit/images/@tee-SEQ/tee-os': Invalid OP-TEE file: size mismatch (expected 0x4, have 0xe)",
6399 str(exc.exception))
6400
Simon Glass67a05012023-01-07 14:07:15 -07006401 def testExtblobOptional(self):
6402 """Test an image with an external blob that is optional"""
6403 with test_util.capture_sys_output() as (stdout, stderr):
6404 data = self._DoReadFile('266_blob_ext_opt.dts')
6405 self.assertEqual(REFCODE_DATA, data)
6406 err = stderr.getvalue()
6407 self.assertRegex(
6408 err,
Jonas Karlman05dec372023-07-18 20:34:34 +00006409 "Image '.*' is missing optional external blobs but is still functional: missing")
Simon Glass67a05012023-01-07 14:07:15 -07006410
Simon Glass0b079fc2023-01-11 16:10:12 -07006411 def testSectionInner(self):
6412 """Test an inner section with a size"""
6413 data = self._DoReadFile('267_section_inner.dts')
6414 expected = U_BOOT_DATA + tools.get_bytes(0, 12)
6415 self.assertEqual(expected, data)
6416
Simon Glass62ef2f72023-01-11 16:10:14 -07006417 def testNull(self):
6418 """Test an image with a null entry"""
6419 data = self._DoReadFile('268_null.dts')
6420 self.assertEqual(U_BOOT_DATA + b'\xff\xff\xff\xff' + U_BOOT_IMG_DATA, data)
6421
Simon Glass9766f692023-01-11 16:10:16 -07006422 def testOverlap(self):
6423 """Test an image with a overlapping entry"""
6424 data = self._DoReadFile('269_overlap.dts')
6425 self.assertEqual(U_BOOT_DATA[:1] + b'aa' + U_BOOT_DATA[3:], data)
6426
6427 image = control.images['image']
6428 entries = image.GetEntries()
6429
6430 self.assertIn('inset', entries)
6431 inset = entries['inset']
6432 self.assertEqual(1, inset.offset);
6433 self.assertEqual(1, inset.image_pos);
6434 self.assertEqual(2, inset.size);
6435
6436 def testOverlapNull(self):
6437 """Test an image with a null overlap"""
6438 data = self._DoReadFile('270_overlap_null.dts')
6439 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
6440
6441 # Check the FMAP
6442 fhdr, fentries = fmap_util.DecodeFmap(data[len(U_BOOT_DATA):])
6443 self.assertEqual(4, fhdr.nareas)
6444 fiter = iter(fentries)
6445
6446 fentry = next(fiter)
6447 self.assertEqual(b'SECTION', fentry.name)
6448 self.assertEqual(0, fentry.offset)
6449 self.assertEqual(len(U_BOOT_DATA), fentry.size)
6450 self.assertEqual(0, fentry.flags)
6451
6452 fentry = next(fiter)
6453 self.assertEqual(b'U_BOOT', fentry.name)
6454 self.assertEqual(0, fentry.offset)
6455 self.assertEqual(len(U_BOOT_DATA), fentry.size)
6456 self.assertEqual(0, fentry.flags)
6457
6458 # Make sure that the NULL entry appears in the FMAP
6459 fentry = next(fiter)
6460 self.assertEqual(b'NULL', fentry.name)
6461 self.assertEqual(1, fentry.offset)
6462 self.assertEqual(2, fentry.size)
6463 self.assertEqual(0, fentry.flags)
6464
6465 fentry = next(fiter)
6466 self.assertEqual(b'FMAP', fentry.name)
6467 self.assertEqual(len(U_BOOT_DATA), fentry.offset)
6468
6469 def testOverlapBad(self):
6470 """Test an image with a bad overlapping entry"""
6471 with self.assertRaises(ValueError) as exc:
6472 self._DoReadFile('271_overlap_bad.dts')
6473 self.assertIn(
6474 "Node '/binman/inset': Offset 0x10 (16) ending at 0x12 (18) must overlap with existing entries",
6475 str(exc.exception))
6476
6477 def testOverlapNoOffset(self):
6478 """Test an image with a bad overlapping entry"""
6479 with self.assertRaises(ValueError) as exc:
6480 self._DoReadFile('272_overlap_no_size.dts')
6481 self.assertIn(
6482 "Node '/binman/inset': 'fill' entry is missing properties: size",
6483 str(exc.exception))
6484
Simon Glassc1157862023-01-11 16:10:17 -07006485 def testBlobSymbol(self):
6486 """Test a blob with symbols read from an ELF file"""
6487 elf_fname = self.ElfTestFile('blob_syms')
6488 TestFunctional._MakeInputFile('blob_syms', tools.read_file(elf_fname))
6489 TestFunctional._MakeInputFile('blob_syms.bin',
6490 tools.read_file(self.ElfTestFile('blob_syms.bin')))
6491
6492 data = self._DoReadFile('273_blob_symbol.dts')
6493
6494 syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
6495 addr = elf.GetSymbolAddress(elf_fname, '__my_start_sym')
6496 self.assertEqual(syms['_binman_sym_magic'].address, addr)
6497 self.assertEqual(syms['_binman_inset_prop_offset'].address, addr + 4)
6498 self.assertEqual(syms['_binman_inset_prop_size'].address, addr + 8)
6499
6500 sym_values = struct.pack('<LLL', elf.BINMAN_SYM_MAGIC_VALUE, 4, 8)
6501 expected = sym_values
6502 self.assertEqual(expected, data[:len(expected)])
6503
Simon Glass571bc4e2023-01-11 16:10:19 -07006504 def testOffsetFromElf(self):
6505 """Test a blob with symbols read from an ELF file"""
6506 elf_fname = self.ElfTestFile('blob_syms')
6507 TestFunctional._MakeInputFile('blob_syms', tools.read_file(elf_fname))
6508 TestFunctional._MakeInputFile('blob_syms.bin',
6509 tools.read_file(self.ElfTestFile('blob_syms.bin')))
6510
6511 data = self._DoReadFile('274_offset_from_elf.dts')
6512
6513 syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
6514 base = elf.GetSymbolAddress(elf_fname, '__my_start_sym')
6515
6516 image = control.images['image']
6517 entries = image.GetEntries()
6518
6519 self.assertIn('inset', entries)
6520 inset = entries['inset']
6521
6522 self.assertEqual(base + 4, inset.offset);
6523 self.assertEqual(base + 4, inset.image_pos);
6524 self.assertEqual(4, inset.size);
6525
6526 self.assertIn('inset2', entries)
6527 inset = entries['inset2']
6528 self.assertEqual(base + 8, inset.offset);
6529 self.assertEqual(base + 8, inset.image_pos);
6530 self.assertEqual(4, inset.size);
6531
Jonas Karlman9b2fd2d2023-01-21 19:01:39 +00006532 def testFitAlign(self):
6533 """Test an image with an FIT with aligned external data"""
6534 data = self._DoReadFile('275_fit_align.dts')
6535 self.assertEqual(4096, len(data))
6536
6537 dtb = fdt.Fdt.FromData(data)
6538 dtb.Scan()
6539
6540 props = self._GetPropTree(dtb, ['data-position'])
6541 expected = {
6542 'u-boot:data-position': 1024,
6543 'fdt-1:data-position': 2048,
6544 'fdt-2:data-position': 3072,
6545 }
6546 self.assertEqual(expected, props)
6547
Jonas Karlmanf584d442023-01-21 19:02:12 +00006548 def testFitFirmwareLoadables(self):
6549 """Test an image with an FIT that use fit,firmware"""
6550 if not elf.ELF_TOOLS:
6551 self.skipTest('Python elftools not available')
6552 entry_args = {
6553 'of-list': 'test-fdt1',
6554 'default-dt': 'test-fdt1',
6555 'atf-bl31-path': 'bl31.elf',
6556 'tee-os-path': 'missing.bin',
6557 }
6558 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
Simon Glass7c4027a2023-02-23 18:18:01 -07006559 with test_util.capture_sys_output() as (stdout, stderr):
6560 data = self._DoReadFileDtb(
6561 '276_fit_firmware_loadables.dts',
6562 entry_args=entry_args,
6563 extra_indirs=[test_subdir])[0]
Jonas Karlmanf584d442023-01-21 19:02:12 +00006564
6565 dtb = fdt.Fdt.FromData(data)
6566 dtb.Scan()
6567
6568 node = dtb.GetNode('/configurations/conf-uboot-1')
6569 self.assertEqual('u-boot', node.props['firmware'].value)
6570 self.assertEqual(['atf-1', 'atf-2'],
6571 fdt_util.GetStringList(node, 'loadables'))
6572
6573 node = dtb.GetNode('/configurations/conf-atf-1')
6574 self.assertEqual('atf-1', node.props['firmware'].value)
6575 self.assertEqual(['u-boot', 'atf-2'],
6576 fdt_util.GetStringList(node, 'loadables'))
6577
6578 node = dtb.GetNode('/configurations/conf-missing-uboot-1')
6579 self.assertEqual('u-boot', node.props['firmware'].value)
6580 self.assertEqual(['atf-1', 'atf-2'],
6581 fdt_util.GetStringList(node, 'loadables'))
6582
6583 node = dtb.GetNode('/configurations/conf-missing-atf-1')
6584 self.assertEqual('atf-1', node.props['firmware'].value)
6585 self.assertEqual(['u-boot', 'atf-2'],
6586 fdt_util.GetStringList(node, 'loadables'))
6587
6588 node = dtb.GetNode('/configurations/conf-missing-tee-1')
6589 self.assertEqual('atf-1', node.props['firmware'].value)
6590 self.assertEqual(['u-boot', 'atf-2'],
6591 fdt_util.GetStringList(node, 'loadables'))
6592
Simon Glassfe7e9242023-02-22 12:14:49 -07006593 def testTooldir(self):
6594 """Test that we can specify the tooldir"""
6595 with test_util.capture_sys_output() as (stdout, stderr):
6596 self.assertEqual(0, self._DoBinman('--tooldir', 'fred',
6597 'tool', '-l'))
6598 self.assertEqual('fred', bintool.Bintool.tooldir)
6599
6600 # Check that the toolpath is updated correctly
6601 self.assertEqual(['fred'], tools.tool_search_paths)
6602
6603 # Try with a few toolpaths; the tooldir should be at the end
6604 with test_util.capture_sys_output() as (stdout, stderr):
6605 self.assertEqual(0, self._DoBinman(
6606 '--toolpath', 'mary', '--toolpath', 'anna', '--tooldir', 'fred',
6607 'tool', '-l'))
6608 self.assertEqual(['mary', 'anna', 'fred'], tools.tool_search_paths)
6609
Simon Glass7caa3722023-03-02 17:02:44 -07006610 def testReplaceSectionEntry(self):
6611 """Test replacing an entry in a section"""
6612 expect_data = b'w' * len(U_BOOT_DATA + COMPRESS_DATA)
6613 entry_data, expected_fdtmap, image = self._RunReplaceCmd('section/blob',
6614 expect_data, dts='241_replace_section_simple.dts')
6615 self.assertEqual(expect_data, entry_data)
6616
6617 entries = image.GetEntries()
6618 self.assertIn('section', entries)
6619 section = entries['section']
6620
6621 sect_entries = section.GetEntries()
6622 self.assertIn('blob', sect_entries)
6623 entry = sect_entries['blob']
6624 self.assertEqual(len(expect_data), entry.size)
6625
6626 fname = tools.get_output_filename('image-updated.bin')
6627 data = tools.read_file(fname)
6628
6629 new_blob_data = data[entry.image_pos:entry.image_pos + len(expect_data)]
6630 self.assertEqual(expect_data, new_blob_data)
6631
6632 self.assertEqual(U_BOOT_DATA,
6633 data[entry.image_pos + len(expect_data):]
6634 [:len(U_BOOT_DATA)])
6635
6636 def testReplaceSectionDeep(self):
6637 """Test replacing an entry in two levels of sections"""
6638 expect_data = b'w' * len(U_BOOT_DATA + COMPRESS_DATA)
6639 entry_data, expected_fdtmap, image = self._RunReplaceCmd(
6640 'section/section/blob', expect_data,
6641 dts='278_replace_section_deep.dts')
6642 self.assertEqual(expect_data, entry_data)
6643
6644 entries = image.GetEntries()
6645 self.assertIn('section', entries)
6646 section = entries['section']
6647
6648 subentries = section.GetEntries()
6649 self.assertIn('section', subentries)
6650 section = subentries['section']
6651
6652 sect_entries = section.GetEntries()
6653 self.assertIn('blob', sect_entries)
6654 entry = sect_entries['blob']
6655 self.assertEqual(len(expect_data), entry.size)
6656
6657 fname = tools.get_output_filename('image-updated.bin')
6658 data = tools.read_file(fname)
6659
6660 new_blob_data = data[entry.image_pos:entry.image_pos + len(expect_data)]
6661 self.assertEqual(expect_data, new_blob_data)
6662
6663 self.assertEqual(U_BOOT_DATA,
6664 data[entry.image_pos + len(expect_data):]
6665 [:len(U_BOOT_DATA)])
6666
6667 def testReplaceFitSibling(self):
6668 """Test an image with a FIT inside where we replace its sibling"""
Marek Vasutfadad3a2023-07-18 07:23:58 -06006669 self._SetupSplElf()
Simon Glass7caa3722023-03-02 17:02:44 -07006670 fname = TestFunctional._MakeInputFile('once', b'available once')
6671 self._DoReadFileRealDtb('277_replace_fit_sibling.dts')
6672 os.remove(fname)
6673
6674 try:
6675 tmpdir, updated_fname = self._SetupImageInTmpdir()
6676
6677 fname = os.path.join(tmpdir, 'update-blob')
6678 expected = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) + 1)
6679 tools.write_file(fname, expected)
6680
6681 self._DoBinman('replace', '-i', updated_fname, 'blob', '-f', fname)
6682 data = tools.read_file(updated_fname)
6683 start = len(U_BOOT_DTB_DATA)
6684 self.assertEqual(expected, data[start:start + len(expected)])
6685 map_fname = os.path.join(tmpdir, 'image-updated.map')
6686 self.assertFalse(os.path.exists(map_fname))
6687 finally:
6688 shutil.rmtree(tmpdir)
6689
Simon Glass953d4172023-03-02 17:02:45 -07006690 def testX509Cert(self):
6691 """Test creating an X509 certificate"""
6692 keyfile = self.TestFile('key.key')
6693 entry_args = {
6694 'keyfile': keyfile,
6695 }
6696 data = self._DoReadFileDtb('279_x509_cert.dts',
6697 entry_args=entry_args)[0]
6698 cert = data[:-4]
6699 self.assertEqual(U_BOOT_DATA, data[-4:])
6700
6701 # TODO: verify the signature
6702
6703 def testX509CertMissing(self):
6704 """Test that binman still produces an image if openssl is missing"""
6705 keyfile = self.TestFile('key.key')
6706 entry_args = {
6707 'keyfile': 'keyfile',
6708 }
6709 with test_util.capture_sys_output() as (_, stderr):
6710 self._DoTestFile('279_x509_cert.dts',
6711 force_missing_bintools='openssl',
6712 entry_args=entry_args)
6713 err = stderr.getvalue()
6714 self.assertRegex(err, "Image 'image'.*missing bintools.*: openssl")
6715
Jonas Karlman05b978b2023-02-25 19:01:33 +00006716 def testPackRockchipTpl(self):
6717 """Test that an image with a Rockchip TPL binary can be created"""
Simon Glass176f1f62023-07-24 09:19:58 -06006718 data = self._DoReadFile('291_rockchip_tpl.dts')
Jonas Karlman05b978b2023-02-25 19:01:33 +00006719 self.assertEqual(ROCKCHIP_TPL_DATA, data[:len(ROCKCHIP_TPL_DATA)])
6720
Jonas Karlman40389c22023-02-25 19:01:35 +00006721 def testMkimageMissingBlobMultiple(self):
6722 """Test missing blob with mkimage entry and multiple-data-files"""
6723 with test_util.capture_sys_output() as (stdout, stderr):
Simon Glass176f1f62023-07-24 09:19:58 -06006724 self._DoTestFile('292_mkimage_missing_multiple.dts', allow_missing=True)
Jonas Karlman40389c22023-02-25 19:01:35 +00006725 err = stderr.getvalue()
6726 self.assertIn("is missing external blobs and is non-functional", err)
6727
6728 with self.assertRaises(ValueError) as e:
Simon Glass176f1f62023-07-24 09:19:58 -06006729 self._DoTestFile('292_mkimage_missing_multiple.dts', allow_missing=False)
Jonas Karlman40389c22023-02-25 19:01:35 +00006730 self.assertIn("not found in input path", str(e.exception))
6731
Ivan Mikhaylov5b34efe2023-03-08 01:13:40 +00006732 def _PrepareSignEnv(self, dts='280_fit_sign.dts'):
6733 """Prepare sign environment
6734
6735 Create private and public keys, add pubkey into dtb.
6736
6737 Returns:
6738 Tuple:
6739 FIT container
6740 Image name
6741 Private key
6742 DTB
6743 """
Marek Vasutfadad3a2023-07-18 07:23:58 -06006744 self._SetupSplElf()
Ivan Mikhaylov5b34efe2023-03-08 01:13:40 +00006745 data = self._DoReadFileRealDtb(dts)
6746 updated_fname = tools.get_output_filename('image-updated.bin')
6747 tools.write_file(updated_fname, data)
6748 dtb = tools.get_output_filename('source.dtb')
6749 private_key = tools.get_output_filename('test_key.key')
6750 public_key = tools.get_output_filename('test_key.crt')
6751 fit = tools.get_output_filename('fit.fit')
6752 key_dir = tools.get_output_dir()
6753
6754 tools.run('openssl', 'req', '-batch' , '-newkey', 'rsa:4096',
6755 '-sha256', '-new', '-nodes', '-x509', '-keyout',
6756 private_key, '-out', public_key)
6757 tools.run('fdt_add_pubkey', '-a', 'sha256,rsa4096', '-k', key_dir,
6758 '-n', 'test_key', '-r', 'conf', dtb)
6759
6760 return fit, updated_fname, private_key, dtb
6761
6762 def testSignSimple(self):
6763 """Test that a FIT container can be signed in image"""
6764 is_signed = False
6765 fit, fname, private_key, dtb = self._PrepareSignEnv()
6766
6767 # do sign with private key
6768 control.SignEntries(fname, None, private_key, 'sha256,rsa4096',
6769 ['fit'])
6770 is_signed = self._CheckSign(fit, dtb)
6771
6772 self.assertEqual(is_signed, True)
6773
6774 def testSignExactFIT(self):
6775 """Test that a FIT container can be signed and replaced in image"""
6776 is_signed = False
6777 fit, fname, private_key, dtb = self._PrepareSignEnv()
6778
6779 # Make sure we propagate the toolpath, since mkimage may not be on PATH
6780 args = []
6781 if self.toolpath:
6782 for path in self.toolpath:
6783 args += ['--toolpath', path]
6784
6785 # do sign with private key
6786 self._DoBinman(*args, 'sign', '-i', fname, '-k', private_key, '-a',
6787 'sha256,rsa4096', '-f', fit, 'fit')
6788 is_signed = self._CheckSign(fit, dtb)
6789
6790 self.assertEqual(is_signed, True)
6791
6792 def testSignNonFit(self):
6793 """Test a non-FIT entry cannot be signed"""
6794 is_signed = False
6795 fit, fname, private_key, _ = self._PrepareSignEnv(
6796 '281_sign_non_fit.dts')
6797
6798 # do sign with private key
6799 with self.assertRaises(ValueError) as e:
6800 self._DoBinman('sign', '-i', fname, '-k', private_key, '-a',
6801 'sha256,rsa4096', '-f', fit, 'u-boot')
6802 self.assertIn(
6803 "Node '/u-boot': Updating signatures is not supported with this entry type",
6804 str(e.exception))
6805
6806 def testSignMissingMkimage(self):
6807 """Test that FIT signing handles a missing mkimage tool"""
6808 fit, fname, private_key, _ = self._PrepareSignEnv()
6809
6810 # try to sign with a missing mkimage tool
6811 bintool.Bintool.set_missing_list(['mkimage'])
6812 with self.assertRaises(ValueError) as e:
6813 control.SignEntries(fname, None, private_key, 'sha256,rsa4096',
6814 ['fit'])
6815 self.assertIn("Node '/fit': Missing tool: 'mkimage'", str(e.exception))
6816
Simon Glass4649bea2023-07-18 07:23:54 -06006817 def testSymbolNoWrite(self):
6818 """Test disabling of symbol writing"""
Marek Vasutfadad3a2023-07-18 07:23:58 -06006819 self._SetupSplElf()
Simon Glass4649bea2023-07-18 07:23:54 -06006820 self.checkSymbols('282_symbols_disable.dts', U_BOOT_SPL_DATA, 0x1c,
6821 no_write_symbols=True)
6822
6823 def testSymbolNoWriteExpanded(self):
6824 """Test disabling of symbol writing in expanded entries"""
6825 entry_args = {
6826 'spl-dtb': '1',
6827 }
6828 self.checkSymbols('282_symbols_disable.dts', U_BOOT_SPL_NODTB_DATA +
6829 U_BOOT_SPL_DTB_DATA, 0x38,
6830 entry_args=entry_args, use_expanded=True,
6831 no_write_symbols=True)
6832
Marek Vasutfadad3a2023-07-18 07:23:58 -06006833 def testMkimageSpecial(self):
6834 """Test mkimage ignores special hash-1 node"""
6835 data = self._DoReadFile('283_mkimage_special.dts')
6836
6837 # Just check that the data appears in the file somewhere
6838 self.assertIn(U_BOOT_DATA, data)
6839
Simon Glassb1e40ee2023-07-18 07:23:59 -06006840 def testFitFdtList(self):
6841 """Test an image with an FIT with the fit,fdt-list-val option"""
6842 entry_args = {
6843 'default-dt': 'test-fdt2',
6844 }
6845 data = self._DoReadFileDtb(
6846 '284_fit_fdt_list.dts',
6847 entry_args=entry_args,
6848 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
6849 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
6850 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
6851
Simon Glasse1ad57e2023-07-18 07:24:01 -06006852 def testSplEmptyBss(self):
6853 """Test an expanded SPL with a zero-size BSS"""
6854 # ELF file with a '__bss_size' symbol
6855 self._SetupSplElf(src_fname='bss_data_zero')
6856
6857 entry_args = {
6858 'spl-bss-pad': 'y',
6859 'spl-dtb': 'y',
6860 }
6861 data = self._DoReadFileDtb('285_spl_expand.dts',
6862 use_expanded=True, entry_args=entry_args)[0]
6863
Simon Glassf6abd522023-07-18 07:24:04 -06006864 def testTemplate(self):
6865 """Test using a template"""
6866 TestFunctional._MakeInputFile('vga2.bin', b'#' + VGA_DATA)
6867 data = self._DoReadFile('286_template.dts')
6868 first = U_BOOT_DATA + VGA_DATA + U_BOOT_DTB_DATA
6869 second = U_BOOT_DATA + b'#' + VGA_DATA + U_BOOT_DTB_DATA
6870 self.assertEqual(U_BOOT_IMG_DATA + first + second, data)
6871
Simon Glassb2f47a52023-07-22 21:43:52 -06006872 dtb_fname1 = tools.get_output_filename('u-boot.dtb.tmpl1')
6873 self.assertTrue(os.path.exists(dtb_fname1))
6874 dtb = fdt.Fdt.FromData(tools.read_file(dtb_fname1))
6875 dtb.Scan()
6876 node1 = dtb.GetNode('/binman/template')
6877 self.assertTrue(node1)
6878 vga = dtb.GetNode('/binman/first/intel-vga')
6879 self.assertTrue(vga)
6880
Simon Glassaf41b242023-07-22 21:43:56 -06006881 dtb_fname2 = tools.get_output_filename('u-boot.dtb.tmpl2')
6882 self.assertTrue(os.path.exists(dtb_fname2))
6883 dtb2 = fdt.Fdt.FromData(tools.read_file(dtb_fname2))
6884 dtb2.Scan()
6885 node2 = dtb2.GetNode('/binman/template')
6886 self.assertFalse(node2)
6887
Simon Glass35f72fb2023-07-18 07:24:05 -06006888 def testTemplateBlobMulti(self):
6889 """Test using a template with 'multiple-images' enabled"""
6890 TestFunctional._MakeInputFile('my-blob.bin', b'blob')
6891 TestFunctional._MakeInputFile('my-blob2.bin', b'other')
6892 retcode = self._DoTestFile('287_template_multi.dts')
6893
6894 self.assertEqual(0, retcode)
6895 image = control.images['image']
6896 image_fname = tools.get_output_filename('my-image.bin')
6897 data = tools.read_file(image_fname)
6898 self.assertEqual(b'blob@@@@other', data)
6899
Simon Glassdb0e3f12023-07-18 07:24:06 -06006900 def testTemplateFit(self):
6901 """Test using a template in a FIT"""
6902 fit_data = self._DoReadFile('288_template_fit.dts')
6903 fname = os.path.join(self._indir, 'fit_data.fit')
6904 tools.write_file(fname, fit_data)
6905 out = tools.run('dumpimage', '-l', fname)
6906
Simon Glass696f2b72023-07-18 07:24:07 -06006907 def testTemplateSection(self):
6908 """Test using a template in a section (not at top level)"""
6909 TestFunctional._MakeInputFile('vga2.bin', b'#' + VGA_DATA)
6910 data = self._DoReadFile('289_template_section.dts')
6911 first = U_BOOT_DATA + VGA_DATA + U_BOOT_DTB_DATA
6912 second = U_BOOT_DATA + b'#' + VGA_DATA + U_BOOT_DTB_DATA
6913 self.assertEqual(U_BOOT_IMG_DATA + first + second + first, data)
6914
Simon Glass23b96e92023-07-18 07:24:08 -06006915 def testMkimageSymbols(self):
6916 """Test using mkimage to build an image with symbols in it"""
6917 self._SetupSplElf('u_boot_binman_syms')
6918 data = self._DoReadFile('290_mkimage_sym.dts')
6919
6920 image = control.images['image']
6921 entries = image.GetEntries()
6922 self.assertIn('u-boot', entries)
6923 u_boot = entries['u-boot']
6924
6925 mkim = entries['mkimage']
6926 mkim_entries = mkim.GetEntries()
6927 self.assertIn('u-boot-spl', mkim_entries)
6928 spl = mkim_entries['u-boot-spl']
6929 self.assertIn('u-boot-spl2', mkim_entries)
6930 spl2 = mkim_entries['u-boot-spl2']
6931
6932 # skip the mkimage header and the area sizes
6933 mk_data = data[mkim.offset + 0x40:]
6934 size, term = struct.unpack('>LL', mk_data[:8])
6935
6936 # There should be only one image, so check that the zero terminator is
6937 # present
6938 self.assertEqual(0, term)
6939
6940 content = mk_data[8:8 + size]
6941
6942 # The image should contain the symbols from u_boot_binman_syms.c
6943 # Note that image_pos is adjusted by the base address of the image,
6944 # which is 0x10 in our test image
6945 spl_data = content[:0x18]
6946 content = content[0x1b:]
6947
6948 # After the header is a table of offsets for each image. There should
6949 # only be one image, then a 0 terminator, so figure out the real start
6950 # of the image data
6951 base = 0x40 + 8
6952
6953 # Check symbols in both u-boot-spl and u-boot-spl2
6954 for i in range(2):
6955 vals = struct.unpack('<LLQLL', spl_data)
6956
6957 # The image should contain the symbols from u_boot_binman_syms.c
6958 # Note that image_pos is adjusted by the base address of the image,
6959 # which is 0x10 in our 'u_boot_binman_syms' test image
6960 self.assertEqual(elf.BINMAN_SYM_MAGIC_VALUE, vals[0])
6961 self.assertEqual(base, vals[1])
6962 self.assertEqual(spl2.offset, vals[2])
6963 # figure out the internal positions of its components
6964 self.assertEqual(0x10 + u_boot.image_pos, vals[3])
6965
6966 # Check that spl and spl2 are actually at the indicated positions
6967 self.assertEqual(
6968 elf.BINMAN_SYM_MAGIC_VALUE,
6969 struct.unpack('<I', data[spl.image_pos:spl.image_pos + 4])[0])
6970 self.assertEqual(
6971 elf.BINMAN_SYM_MAGIC_VALUE,
6972 struct.unpack('<I', data[spl2.image_pos:spl2.image_pos + 4])[0])
6973
6974 self.assertEqual(len(U_BOOT_DATA), vals[4])
6975
6976 # Move to next
6977 spl_data = content[:0x18]
6978
Simon Glassd4d97662023-07-22 21:43:57 -06006979 def testTemplatePhandle(self):
6980 """Test using a template in a node containing a phandle"""
6981 entry_args = {
6982 'atf-bl31-path': 'bl31.elf',
6983 }
Simon Glass93a203d2023-08-03 17:23:58 -06006984 data = self._DoReadFileDtb('309_template_phandle.dts',
Simon Glassd4d97662023-07-22 21:43:57 -06006985 entry_args=entry_args)
6986 fname = tools.get_output_filename('image.bin')
6987 out = tools.run('dumpimage', '-l', fname)
6988
6989 # We should see the FIT description and one for each of the two images
6990 lines = out.splitlines()
6991 descs = [line.split()[-1] for line in lines if 'escription' in line]
6992 self.assertEqual(['test-desc', 'atf', 'fdt'], descs)
6993
6994 def testTemplatePhandleDup(self):
6995 """Test using a template in a node containing a phandle"""
6996 entry_args = {
6997 'atf-bl31-path': 'bl31.elf',
6998 }
6999 with self.assertRaises(ValueError) as e:
Simon Glass93a203d2023-08-03 17:23:58 -06007000 self._DoReadFileDtb('310_template_phandle_dup.dts',
Simon Glassd4d97662023-07-22 21:43:57 -06007001 entry_args=entry_args)
7002 self.assertIn(
7003 'Duplicate phandle 1 in nodes /binman/image/fit/images/atf/atf-bl31 and /binman/image-2/fit/images/atf/atf-bl31',
7004 str(e.exception))
7005
Neha Malcom Francis6c66ccf2023-07-22 00:14:24 +05307006 def testTIBoardConfig(self):
7007 """Test that a schema validated board config file can be generated"""
Simon Glassa6b44ac2023-07-24 09:19:59 -06007008 data = self._DoReadFile('293_ti_board_cfg.dts')
Neha Malcom Francis6c66ccf2023-07-22 00:14:24 +05307009 self.assertEqual(TI_BOARD_CONFIG_DATA, data)
7010
7011 def testTIBoardConfigCombined(self):
7012 """Test that a schema validated combined board config file can be generated"""
Simon Glassa6b44ac2023-07-24 09:19:59 -06007013 data = self._DoReadFile('294_ti_board_cfg_combined.dts')
Neha Malcom Francis6c66ccf2023-07-22 00:14:24 +05307014 configlen_noheader = TI_BOARD_CONFIG_DATA * 4
7015 self.assertGreater(data, configlen_noheader)
7016
7017 def testTIBoardConfigNoDataType(self):
7018 """Test that error is thrown when data type is not supported"""
7019 with self.assertRaises(ValueError) as e:
Simon Glassa6b44ac2023-07-24 09:19:59 -06007020 data = self._DoReadFile('295_ti_board_cfg_no_type.dts')
Neha Malcom Francis6c66ccf2023-07-22 00:14:24 +05307021 self.assertIn("Schema validation error", str(e.exception))
Simon Glassefddab62023-01-07 14:07:08 -07007022
Neha Malcom Francis78144822023-07-22 00:14:25 +05307023 def testPackTiSecure(self):
7024 """Test that an image with a TI secured binary can be created"""
7025 keyfile = self.TestFile('key.key')
7026 entry_args = {
7027 'keyfile': keyfile,
7028 }
Simon Glassa6b44ac2023-07-24 09:19:59 -06007029 data = self._DoReadFileDtb('296_ti_secure.dts',
Neha Malcom Francis78144822023-07-22 00:14:25 +05307030 entry_args=entry_args)[0]
7031 self.assertGreater(len(data), len(TI_UNSECURE_DATA))
7032
7033 def testPackTiSecureMissingTool(self):
7034 """Test that an image with a TI secured binary (non-functional) can be created
7035 when openssl is missing"""
7036 keyfile = self.TestFile('key.key')
7037 entry_args = {
7038 'keyfile': keyfile,
7039 }
7040 with test_util.capture_sys_output() as (_, stderr):
Simon Glassa6b44ac2023-07-24 09:19:59 -06007041 self._DoTestFile('296_ti_secure.dts',
Neha Malcom Francis78144822023-07-22 00:14:25 +05307042 force_missing_bintools='openssl',
7043 entry_args=entry_args)
7044 err = stderr.getvalue()
7045 self.assertRegex(err, "Image 'image'.*missing bintools.*: openssl")
7046
7047 def testPackTiSecureROM(self):
7048 """Test that a ROM image with a TI secured binary can be created"""
7049 keyfile = self.TestFile('key.key')
7050 entry_args = {
7051 'keyfile': keyfile,
7052 }
Simon Glassa6b44ac2023-07-24 09:19:59 -06007053 data = self._DoReadFileDtb('297_ti_secure_rom.dts',
Neha Malcom Francis78144822023-07-22 00:14:25 +05307054 entry_args=entry_args)[0]
Simon Glassa6b44ac2023-07-24 09:19:59 -06007055 data_a = self._DoReadFileDtb('299_ti_secure_rom_a.dts',
Neha Malcom Francis78144822023-07-22 00:14:25 +05307056 entry_args=entry_args)[0]
Simon Glassa6b44ac2023-07-24 09:19:59 -06007057 data_b = self._DoReadFileDtb('300_ti_secure_rom_b.dts',
Neha Malcom Francis78144822023-07-22 00:14:25 +05307058 entry_args=entry_args)[0]
7059 self.assertGreater(len(data), len(TI_UNSECURE_DATA))
7060 self.assertGreater(len(data_a), len(TI_UNSECURE_DATA))
7061 self.assertGreater(len(data_b), len(TI_UNSECURE_DATA))
7062
7063 def testPackTiSecureROMCombined(self):
7064 """Test that a ROM image with a TI secured binary can be created"""
7065 keyfile = self.TestFile('key.key')
7066 entry_args = {
7067 'keyfile': keyfile,
7068 }
Simon Glassa6b44ac2023-07-24 09:19:59 -06007069 data = self._DoReadFileDtb('298_ti_secure_rom_combined.dts',
Neha Malcom Francis78144822023-07-22 00:14:25 +05307070 entry_args=entry_args)[0]
7071 self.assertGreater(len(data), len(TI_UNSECURE_DATA))
7072
Christian Taedcke289e6002023-07-17 09:05:54 +02007073 def testEncryptedNoAlgo(self):
7074 """Test encrypted node with missing required properties"""
7075 with self.assertRaises(ValueError) as e:
7076 self._DoReadFileDtb('301_encrypted_no_algo.dts')
7077 self.assertIn(
7078 "Node '/binman/fit/images/u-boot/encrypted': 'encrypted' entry is missing properties: algo iv-filename",
7079 str(e.exception))
7080
7081 def testEncryptedInvalidIvfile(self):
7082 """Test encrypted node with invalid iv file"""
7083 with self.assertRaises(ValueError) as e:
7084 self._DoReadFileDtb('302_encrypted_invalid_iv_file.dts')
7085 self.assertIn("Filename 'invalid-iv-file' not found in input path",
7086 str(e.exception))
7087
7088 def testEncryptedMissingKey(self):
7089 """Test encrypted node with missing key properties"""
7090 with self.assertRaises(ValueError) as e:
7091 self._DoReadFileDtb('303_encrypted_missing_key.dts')
7092 self.assertIn(
7093 "Node '/binman/fit/images/u-boot/encrypted': Provide either 'key-filename' or 'key-source'",
7094 str(e.exception))
7095
7096 def testEncryptedKeySource(self):
7097 """Test encrypted node with key-source property"""
7098 data = self._DoReadFileDtb('304_encrypted_key_source.dts')[0]
7099
7100 dtb = fdt.Fdt.FromData(data)
7101 dtb.Scan()
7102
7103 node = dtb.GetNode('/images/u-boot/cipher')
7104 self.assertEqual('algo-name', node.props['algo'].value)
7105 self.assertEqual('key-source-value', node.props['key-source'].value)
7106 self.assertEqual(ENCRYPTED_IV_DATA,
7107 tools.to_bytes(''.join(node.props['iv'].value)))
7108 self.assertNotIn('key', node.props)
7109
7110 def testEncryptedKeyFile(self):
7111 """Test encrypted node with key-filename property"""
7112 data = self._DoReadFileDtb('305_encrypted_key_file.dts')[0]
7113
7114 dtb = fdt.Fdt.FromData(data)
7115 dtb.Scan()
7116
7117 node = dtb.GetNode('/images/u-boot/cipher')
7118 self.assertEqual('algo-name', node.props['algo'].value)
7119 self.assertEqual(ENCRYPTED_IV_DATA,
7120 tools.to_bytes(''.join(node.props['iv'].value)))
7121 self.assertEqual(ENCRYPTED_KEY_DATA,
7122 tools.to_bytes(''.join(node.props['key'].value)))
7123 self.assertNotIn('key-source', node.props)
7124
7125
Lukas Funke8c1fbd12023-07-18 13:53:13 +02007126 def testSplPubkeyDtb(self):
7127 """Test u_boot_spl_pubkey_dtb etype"""
7128 data = tools.read_file(self.TestFile("key.pem"))
7129 self._MakeInputFile("key.crt", data)
7130 self._DoReadFileRealDtb('306_spl_pubkey_dtb.dts')
7131 image = control.images['image']
7132 entries = image.GetEntries()
7133 dtb_entry = entries['u-boot-spl-pubkey-dtb']
7134 dtb_data = dtb_entry.GetData()
7135 dtb = fdt.Fdt.FromData(dtb_data)
7136 dtb.Scan()
7137
7138 signature_node = dtb.GetNode('/signature')
7139 self.assertIsNotNone(signature_node)
7140 key_node = signature_node.FindNode("key-key")
7141 self.assertIsNotNone(key_node)
7142 self.assertEqual(fdt_util.GetString(key_node, "required"),
7143 "conf")
7144 self.assertEqual(fdt_util.GetString(key_node, "algo"),
7145 "sha384,rsa4096")
7146 self.assertEqual(fdt_util.GetString(key_node, "key-name-hint"),
7147 "key")
7148
Lukas Funked8a2d3b2023-08-03 17:22:14 +02007149 def testXilinxBootgenSigning(self):
7150 """Test xilinx-bootgen etype"""
7151 bootgen = bintool.Bintool.create('bootgen')
7152 self._CheckBintool(bootgen)
7153 data = tools.read_file(self.TestFile("key.key"))
7154 self._MakeInputFile("psk.pem", data)
7155 self._MakeInputFile("ssk.pem", data)
7156 self._SetupPmuFwlElf()
7157 self._SetupSplElf()
7158 self._DoReadFileRealDtb('307_xilinx_bootgen_sign.dts')
7159 image_fname = tools.get_output_filename('image.bin')
7160
7161 # Read partition header table and check if authentication is enabled
7162 bootgen_out = bootgen.run_cmd("-arch", "zynqmp",
7163 "-read", image_fname, "pht").splitlines()
7164 attributes = {"authentication": None,
7165 "core": None,
7166 "encryption": None}
7167
7168 for l in bootgen_out:
7169 for a in attributes.keys():
7170 if a in l:
7171 m = re.match(fr".*{a} \[([^]]+)\]", l)
7172 attributes[a] = m.group(1)
7173
7174 self.assertTrue(attributes['authentication'] == "rsa")
7175 self.assertTrue(attributes['core'] == "a53-0")
7176 self.assertTrue(attributes['encryption'] == "no")
7177
7178 def testXilinxBootgenSigningEncryption(self):
7179 """Test xilinx-bootgen etype"""
7180 bootgen = bintool.Bintool.create('bootgen')
7181 self._CheckBintool(bootgen)
7182 data = tools.read_file(self.TestFile("key.key"))
7183 self._MakeInputFile("psk.pem", data)
7184 self._MakeInputFile("ssk.pem", data)
7185 self._SetupPmuFwlElf()
7186 self._SetupSplElf()
7187 self._DoReadFileRealDtb('308_xilinx_bootgen_sign_enc.dts')
7188 image_fname = tools.get_output_filename('image.bin')
7189
7190 # Read boot header in order to verify encryption source and
7191 # encryption parameter
7192 bootgen_out = bootgen.run_cmd("-arch", "zynqmp",
7193 "-read", image_fname, "bh").splitlines()
7194 attributes = {"auth_only":
7195 {"re": r".*auth_only \[([^]]+)\]", "value": None},
7196 "encryption_keystore":
7197 {"re": r" *encryption_keystore \(0x28\) : (.*)",
7198 "value": None},
7199 }
7200
7201 for l in bootgen_out:
7202 for a in attributes.keys():
7203 if a in l:
7204 m = re.match(attributes[a]['re'], l)
7205 attributes[a] = m.group(1)
7206
7207 # Check if fsbl-attribute is set correctly
7208 self.assertTrue(attributes['auth_only'] == "true")
7209 # Check if key is stored in efuse
7210 self.assertTrue(attributes['encryption_keystore'] == "0xa5c3c5a3")
7211
7212 def testXilinxBootgenMissing(self):
7213 """Test that binman still produces an image if bootgen is missing"""
7214 data = tools.read_file(self.TestFile("key.key"))
7215 self._MakeInputFile("psk.pem", data)
7216 self._MakeInputFile("ssk.pem", data)
7217 self._SetupPmuFwlElf()
7218 self._SetupSplElf()
7219 with test_util.capture_sys_output() as (_, stderr):
7220 self._DoTestFile('307_xilinx_bootgen_sign.dts',
7221 force_missing_bintools='bootgen')
7222 err = stderr.getvalue()
7223 self.assertRegex(err,
7224 "Image 'image'.*missing bintools.*: bootgen")
7225
Sughosh Ganub6176112023-08-22 23:09:59 +05307226 def _CheckCapsule(self, data, signed_capsule=False, version_check=False,
7227 capoemflags=False):
7228 fmp_signature = "4d535331" # 'M', 'S', 'S', '1'
7229 fmp_size = "10"
7230 fmp_fw_version = "02"
7231 oemflag = "0080"
7232
7233 payload_data = EFI_CAPSULE_DATA
7234
7235 # TODO - Currently, these offsets for capsule fields are hardcoded.
7236 # There are plans to add support to the mkeficapsule tool to dump
7237 # the capsule contents which can then be used for capsule
7238 # verification.
7239
7240 # Firmware Management Protocol(FMP) GUID - offset(0 - 32)
7241 self.assertEqual(FW_MGMT_GUID, data.hex()[:32])
7242 # Image GUID - offset(96 - 128)
7243 self.assertEqual(CAPSULE_IMAGE_GUID, data.hex()[96:128])
7244
7245 if capoemflags:
7246 # OEM Flags - offset(40 - 44)
7247 self.assertEqual(oemflag, data.hex()[40:44])
7248 if signed_capsule and version_check:
7249 # FMP header signature - offset(4770 - 4778)
7250 self.assertEqual(fmp_signature, data.hex()[4770:4778])
7251 # FMP header size - offset(4778 - 4780)
7252 self.assertEqual(fmp_size, data.hex()[4778:4780])
7253 # firmware version - offset(4786 - 4788)
7254 self.assertEqual(fmp_fw_version, data.hex()[4786:4788])
7255 # payload offset signed capsule(4802 - 4808)
7256 self.assertEqual(payload_data.hex(), data.hex()[4802:4808])
7257 elif signed_capsule:
7258 # payload offset signed capsule(4770 - 4776)
7259 self.assertEqual(payload_data.hex(), data.hex()[4770:4776])
7260 elif version_check:
7261 # FMP header signature - offset(184 - 192)
7262 self.assertEqual(fmp_signature, data.hex()[184:192])
7263 # FMP header size - offset(192 - 194)
7264 self.assertEqual(fmp_size, data.hex()[192:194])
7265 # firmware version - offset(200 - 202)
7266 self.assertEqual(fmp_fw_version, data.hex()[200:202])
7267 # payload offset for non-signed capsule with version header(216 - 222)
7268 self.assertEqual(payload_data.hex(), data.hex()[216:222])
7269 else:
7270 # payload offset for non-signed capsule with no version header(184 - 190)
7271 self.assertEqual(payload_data.hex(), data.hex()[184:190])
7272
7273 def testCapsuleGen(self):
7274 """Test generation of EFI capsule"""
7275 data = self._DoReadFile('311_capsule.dts')
7276
7277 self._CheckCapsule(data)
7278
7279 def testSignedCapsuleGen(self):
7280 """Test generation of EFI capsule"""
7281 data = tools.read_file(self.TestFile("key.key"))
7282 self._MakeInputFile("key.key", data)
7283 data = tools.read_file(self.TestFile("key.pem"))
7284 self._MakeInputFile("key.crt", data)
7285
7286 data = self._DoReadFile('312_capsule_signed.dts')
7287
7288 self._CheckCapsule(data, signed_capsule=True)
7289
7290 def testCapsuleGenVersionSupport(self):
7291 """Test generation of EFI capsule with version support"""
7292 data = self._DoReadFile('313_capsule_version.dts')
7293
7294 self._CheckCapsule(data, version_check=True)
7295
7296 def testCapsuleGenSignedVer(self):
7297 """Test generation of signed EFI capsule with version information"""
7298 data = tools.read_file(self.TestFile("key.key"))
7299 self._MakeInputFile("key.key", data)
7300 data = tools.read_file(self.TestFile("key.pem"))
7301 self._MakeInputFile("key.crt", data)
7302
7303 data = self._DoReadFile('314_capsule_signed_ver.dts')
7304
7305 self._CheckCapsule(data, signed_capsule=True, version_check=True)
7306
7307 def testCapsuleGenCapOemFlags(self):
7308 """Test generation of EFI capsule with OEM Flags set"""
7309 data = self._DoReadFile('315_capsule_oemflags.dts')
7310
7311 self._CheckCapsule(data, capoemflags=True)
7312
7313 def testCapsuleGenKeyMissing(self):
7314 """Test that binman errors out on missing key"""
7315 with self.assertRaises(ValueError) as e:
7316 self._DoReadFile('316_capsule_missing_key.dts')
7317
7318 self.assertIn("Both private key and public key certificate need to be provided",
7319 str(e.exception))
7320
7321 def testCapsuleGenIndexMissing(self):
7322 """Test that binman errors out on missing image index"""
7323 with self.assertRaises(ValueError) as e:
7324 self._DoReadFile('317_capsule_missing_index.dts')
7325
7326 self.assertIn("entry is missing properties: image-index",
7327 str(e.exception))
7328
7329 def testCapsuleGenGuidMissing(self):
7330 """Test that binman errors out on missing image GUID"""
7331 with self.assertRaises(ValueError) as e:
7332 self._DoReadFile('318_capsule_missing_guid.dts')
7333
7334 self.assertIn("entry is missing properties: image-guid",
7335 str(e.exception))
7336
Simon Glass9fc60b42017-11-12 21:52:22 -07007337if __name__ == "__main__":
7338 unittest.main()