blob: 43b4f850a691a113542a098051da7bfc13648ec5 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001# SPDX-License-Identifier: GPL-2.0+
Simon Glass57454f42016-11-25 20:15:52 -07002# Copyright (c) 2016 Google, Inc
3# Written by Simon Glass <sjg@chromium.org>
4#
Simon Glass57454f42016-11-25 20:15:52 -07005# To run a single test, change to this directory, and:
6#
7# python -m unittest func_test.TestFunctional.testHelp
8
Simon Glass45d556d2020-07-09 18:39:45 -06009import collections
Simon Glassc585dd42020-04-17 18:09:03 -060010import gzip
Simon Glassae7cf032018-09-14 04:57:31 -060011import hashlib
Simon Glass57454f42016-11-25 20:15:52 -070012from optparse import OptionParser
13import os
Simon Glass45d556d2020-07-09 18:39:45 -060014import re
Simon Glass57454f42016-11-25 20:15:52 -070015import shutil
16import struct
17import sys
18import tempfile
19import unittest
Simon Glass162017b2022-01-09 20:13:57 -070020import unittest.mock
21import urllib.error
Simon Glass57454f42016-11-25 20:15:52 -070022
Simon Glass4eae9252022-01-09 20:13:50 -070023from binman import bintool
Simon Glassc585dd42020-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 Glass3efb2972021-11-23 21:08:59 -070029from binman import fip_util
Simon Glassc585dd42020-04-17 18:09:03 -060030from binman import fmap_util
Simon Glassc585dd42020-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 Glass90cd6f02020-08-05 13:27:47 -060036from binman.image import Image
Simon Glass131444f2023-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 Glass57454f42016-11-25 20:15:52 -070041
42# Contents of test files, corresponding to different entry types
Simon Glass303f62f2019-05-17 22:00:46 -060043U_BOOT_DATA = b'1234'
44U_BOOT_IMG_DATA = b'img'
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +030045U_BOOT_SPL_DATA = b'56780123456789abcdefghijklm'
46U_BOOT_TPL_DATA = b'tpl9876543210fedcbazywvuts'
Simon Glass56d05412022-02-28 07:16:54 -070047U_BOOT_VPL_DATA = b'vpl76543210fedcbazywxyz_'
Simon Glass303f62f2019-05-17 22:00:46 -060048BLOB_DATA = b'89'
49ME_DATA = b'0abcd'
50VGA_DATA = b'vga'
51U_BOOT_DTB_DATA = b'udtb'
52U_BOOT_SPL_DTB_DATA = b'spldtb'
53U_BOOT_TPL_DTB_DATA = b'tpldtb'
Simon Glass56d05412022-02-28 07:16:54 -070054U_BOOT_VPL_DTB_DATA = b'vpldtb'
Simon Glass303f62f2019-05-17 22:00:46 -060055X86_START16_DATA = b'start16'
56X86_START16_SPL_DATA = b'start16spl'
57X86_START16_TPL_DATA = b'start16tpl'
Simon Glass0b074d62019-08-24 07:22:48 -060058X86_RESET16_DATA = b'reset16'
59X86_RESET16_SPL_DATA = b'reset16spl'
60X86_RESET16_TPL_DATA = b'reset16tpl'
Simon Glass303f62f2019-05-17 22:00:46 -060061PPC_MPC85XX_BR_DATA = b'ppcmpc85xxbr'
62U_BOOT_NODTB_DATA = b'nodtb with microcode pointer somewhere in here'
63U_BOOT_SPL_NODTB_DATA = b'splnodtb with microcode pointer somewhere in here'
64U_BOOT_TPL_NODTB_DATA = b'tplnodtb with microcode pointer somewhere in here'
Simon Glass56d05412022-02-28 07:16:54 -070065U_BOOT_VPL_NODTB_DATA = b'vplnodtb'
Alper Nebi Yasak09fb0612022-02-08 01:08:04 +030066U_BOOT_EXP_DATA = U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA
67U_BOOT_SPL_EXP_DATA = U_BOOT_SPL_NODTB_DATA + U_BOOT_SPL_DTB_DATA
68U_BOOT_TPL_EXP_DATA = U_BOOT_TPL_NODTB_DATA + U_BOOT_TPL_DTB_DATA
Simon Glass303f62f2019-05-17 22:00:46 -060069FSP_DATA = b'fsp'
70CMC_DATA = b'cmc'
71VBT_DATA = b'vbt'
72MRC_DATA = b'mrc'
Simon Glass2ca52032018-07-17 13:25:33 -060073TEXT_DATA = 'text'
74TEXT_DATA2 = 'text2'
75TEXT_DATA3 = 'text3'
Simon Glass303f62f2019-05-17 22:00:46 -060076CROS_EC_RW_DATA = b'ecrw'
77GBB_DATA = b'gbbd'
78BMPBLK_DATA = b'bmp'
79VBLOCK_DATA = b'vblk'
80FILES_DATA = (b"sorry I'm late\nOh, don't bother apologising, I'm " +
81 b"sorry you're alive\n")
Simon Glassccec0262019-07-08 13:18:42 -060082COMPRESS_DATA = b'compress xxxxxxxxxxxxxxxxxxxxxx data'
Simon Glassd92c8362020-10-26 17:40:25 -060083COMPRESS_DATA_BIG = COMPRESS_DATA * 2
Simon Glass303f62f2019-05-17 22:00:46 -060084REFCODE_DATA = b'refcode'
Simon Glassba7985d2019-08-24 07:23:07 -060085FSP_M_DATA = b'fsp_m'
Simon Glass4d9086d2019-10-20 21:31:35 -060086FSP_S_DATA = b'fsp_s'
Simon Glass9ea87b22019-10-20 21:31:36 -060087FSP_T_DATA = b'fsp_t'
Simon Glass559c4de2020-09-01 05:13:58 -060088ATF_BL31_DATA = b'bl31'
Roger Quadros5cdcea02022-02-19 20:50:04 +020089TEE_OS_DATA = b'this is some tee OS data'
Simon Glass3efb2972021-11-23 21:08:59 -070090ATF_BL2U_DATA = b'bl2u'
Bin Mengc0b15742021-05-10 20:23:33 +080091OPENSBI_DATA = b'opensbi'
Samuel Holland9d8cc632020-10-21 21:12:15 -050092SCP_DATA = b'scp'
Jonas Karlman35305492023-02-25 19:01:33 +000093ROCKCHIP_TPL_DATA = b'rockchip-tpl'
Simon Glassa435cd12020-09-01 05:13:59 -060094TEST_FDT1_DATA = b'fdt1'
95TEST_FDT2_DATA = b'test-fdt2'
Simon Glassa0729502020-09-06 10:35:33 -060096ENV_DATA = b'var1=1\nvar2="2"'
Philippe Reynesebe96cb2022-03-28 22:57:04 +020097PRE_LOAD_MAGIC = b'UBSH'
98PRE_LOAD_VERSION = 0x11223344.to_bytes(4, 'big')
99PRE_LOAD_HDR_SIZE = 0x00001000.to_bytes(4, 'big')
Simon Glassa435cd12020-09-01 05:13:59 -0600100
101# Subdirectory of the input dir to use to put test FDTs
102TEST_FDT_SUBDIR = 'fdts'
Simon Glassdb168d42018-07-17 13:25:39 -0600103
Simon Glass2c6adba2019-07-20 12:23:47 -0600104# The expected size for the device tree in some tests
Simon Glass4c613bf2019-07-08 14:25:50 -0600105EXTRACT_DTB_SIZE = 0x3c9
106
Simon Glass2c6adba2019-07-20 12:23:47 -0600107# Properties expected to be in the device tree when update_dtb is used
108BASE_DTB_PROPS = ['offset', 'size', 'image-pos']
109
Simon Glassfb30e292019-07-20 12:23:51 -0600110# Extra properties expected to be in the device tree when allow-repack is used
111REPACK_DTB_PROPS = ['orig-offset', 'orig-size']
112
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +0200113# Supported compression bintools
Stefan Herbrechtsmeiera5e4dcb2022-08-19 16:25:38 +0200114COMP_BINTOOLS = ['bzip2', 'gzip', 'lz4', 'lzma_alone', 'lzop', 'xz', 'zstd']
Simon Glass57454f42016-11-25 20:15:52 -0700115
Simon Glassad5cfe12023-01-07 14:07:14 -0700116TEE_ADDR = 0x5678
117
Simon Glass57454f42016-11-25 20:15:52 -0700118class TestFunctional(unittest.TestCase):
119 """Functional tests for binman
120
121 Most of these use a sample .dts file to build an image and then check
122 that it looks correct. The sample files are in the test/ subdirectory
123 and are numbered.
124
125 For each entry type a very small test file is created using fixed
126 string contents. This makes it easy to test that things look right, and
127 debug problems.
128
129 In some cases a 'real' file must be used - these are also supplied in
130 the test/ diurectory.
131 """
132 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600133 def setUpClass(cls):
Simon Glassb3393262017-11-12 21:52:20 -0700134 global entry
Simon Glassc585dd42020-04-17 18:09:03 -0600135 from binman import entry
Simon Glassb3393262017-11-12 21:52:20 -0700136
Simon Glass57454f42016-11-25 20:15:52 -0700137 # Handle the case where argv[0] is 'python'
Simon Glass862f8e22019-08-24 07:22:43 -0600138 cls._binman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
139 cls._binman_pathname = os.path.join(cls._binman_dir, 'binman')
Simon Glass57454f42016-11-25 20:15:52 -0700140
141 # Create a temporary directory for input files
Simon Glass862f8e22019-08-24 07:22:43 -0600142 cls._indir = tempfile.mkdtemp(prefix='binmant.')
Simon Glass57454f42016-11-25 20:15:52 -0700143
144 # Create some test files
145 TestFunctional._MakeInputFile('u-boot.bin', U_BOOT_DATA)
146 TestFunctional._MakeInputFile('u-boot.img', U_BOOT_IMG_DATA)
147 TestFunctional._MakeInputFile('spl/u-boot-spl.bin', U_BOOT_SPL_DATA)
Simon Glass8425a1f2018-07-17 13:25:48 -0600148 TestFunctional._MakeInputFile('tpl/u-boot-tpl.bin', U_BOOT_TPL_DATA)
Simon Glass56d05412022-02-28 07:16:54 -0700149 TestFunctional._MakeInputFile('vpl/u-boot-vpl.bin', U_BOOT_VPL_DATA)
Simon Glass57454f42016-11-25 20:15:52 -0700150 TestFunctional._MakeInputFile('blobfile', BLOB_DATA)
Simon Glass72232452016-11-25 20:15:53 -0700151 TestFunctional._MakeInputFile('me.bin', ME_DATA)
152 TestFunctional._MakeInputFile('vga.bin', VGA_DATA)
Simon Glass862f8e22019-08-24 07:22:43 -0600153 cls._ResetDtbs()
Simon Glass0b074d62019-08-24 07:22:48 -0600154
Jagdish Gediya311d4842018-09-03 21:35:08 +0530155 TestFunctional._MakeInputFile('u-boot-br.bin', PPC_MPC85XX_BR_DATA)
Simon Glass0b074d62019-08-24 07:22:48 -0600156
Simon Glassabab18c2019-08-24 07:22:49 -0600157 TestFunctional._MakeInputFile('u-boot-x86-start16.bin', X86_START16_DATA)
158 TestFunctional._MakeInputFile('spl/u-boot-x86-start16-spl.bin',
Simon Glasse83679d2017-11-12 21:52:26 -0700159 X86_START16_SPL_DATA)
Simon Glassabab18c2019-08-24 07:22:49 -0600160 TestFunctional._MakeInputFile('tpl/u-boot-x86-start16-tpl.bin',
Simon Glassed40e962018-09-14 04:57:10 -0600161 X86_START16_TPL_DATA)
Simon Glass0b074d62019-08-24 07:22:48 -0600162
163 TestFunctional._MakeInputFile('u-boot-x86-reset16.bin',
164 X86_RESET16_DATA)
165 TestFunctional._MakeInputFile('spl/u-boot-x86-reset16-spl.bin',
166 X86_RESET16_SPL_DATA)
167 TestFunctional._MakeInputFile('tpl/u-boot-x86-reset16-tpl.bin',
168 X86_RESET16_TPL_DATA)
169
Simon Glass57454f42016-11-25 20:15:52 -0700170 TestFunctional._MakeInputFile('u-boot-nodtb.bin', U_BOOT_NODTB_DATA)
Simon Glass3d274232017-11-12 21:52:27 -0700171 TestFunctional._MakeInputFile('spl/u-boot-spl-nodtb.bin',
172 U_BOOT_SPL_NODTB_DATA)
Simon Glass3fb4f422018-09-14 04:57:32 -0600173 TestFunctional._MakeInputFile('tpl/u-boot-tpl-nodtb.bin',
174 U_BOOT_TPL_NODTB_DATA)
Simon Glass56d05412022-02-28 07:16:54 -0700175 TestFunctional._MakeInputFile('vpl/u-boot-vpl-nodtb.bin',
176 U_BOOT_VPL_NODTB_DATA)
Simon Glassb4176d42016-11-25 20:15:56 -0700177 TestFunctional._MakeInputFile('fsp.bin', FSP_DATA)
178 TestFunctional._MakeInputFile('cmc.bin', CMC_DATA)
Bin Mengd7bcdf52017-08-15 22:41:54 -0700179 TestFunctional._MakeInputFile('vbt.bin', VBT_DATA)
Simon Glassa409c932017-11-12 21:52:28 -0700180 TestFunctional._MakeInputFile('mrc.bin', MRC_DATA)
Simon Glassdb168d42018-07-17 13:25:39 -0600181 TestFunctional._MakeInputFile('ecrw.bin', CROS_EC_RW_DATA)
Simon Glassc1ae83c2018-07-17 13:25:44 -0600182 TestFunctional._MakeInputDir('devkeys')
183 TestFunctional._MakeInputFile('bmpblk.bin', BMPBLK_DATA)
Simon Glass41902e42018-10-01 12:22:31 -0600184 TestFunctional._MakeInputFile('refcode.bin', REFCODE_DATA)
Simon Glassba7985d2019-08-24 07:23:07 -0600185 TestFunctional._MakeInputFile('fsp_m.bin', FSP_M_DATA)
Simon Glass4d9086d2019-10-20 21:31:35 -0600186 TestFunctional._MakeInputFile('fsp_s.bin', FSP_S_DATA)
Simon Glass9ea87b22019-10-20 21:31:36 -0600187 TestFunctional._MakeInputFile('fsp_t.bin', FSP_T_DATA)
Simon Glass57454f42016-11-25 20:15:52 -0700188
Simon Glassf6290892019-08-24 07:22:53 -0600189 cls._elf_testdir = os.path.join(cls._indir, 'elftest')
190 elf_test.BuildElfTestFiles(cls._elf_testdir)
191
Simon Glass72232452016-11-25 20:15:53 -0700192 # ELF file with a '_dt_ucode_base_size' symbol
Simon Glass4affd4b2019-08-24 07:22:54 -0600193 TestFunctional._MakeInputFile('u-boot',
Simon Glass80025522022-01-29 14:14:04 -0700194 tools.read_file(cls.ElfTestFile('u_boot_ucode_ptr')))
Simon Glass72232452016-11-25 20:15:53 -0700195
196 # Intel flash descriptor file
Simon Glasse88cef92020-07-09 18:39:41 -0600197 cls._SetupDescriptor()
Simon Glass72232452016-11-25 20:15:53 -0700198
Simon Glass862f8e22019-08-24 07:22:43 -0600199 shutil.copytree(cls.TestFile('files'),
200 os.path.join(cls._indir, 'files'))
Simon Glassac6328c2018-09-14 04:57:28 -0600201
Simon Glass7ba33592018-09-14 04:57:26 -0600202 TestFunctional._MakeInputFile('compress', COMPRESS_DATA)
Simon Glassd92c8362020-10-26 17:40:25 -0600203 TestFunctional._MakeInputFile('compress_big', COMPRESS_DATA_BIG)
Simon Glass559c4de2020-09-01 05:13:58 -0600204 TestFunctional._MakeInputFile('bl31.bin', ATF_BL31_DATA)
Roger Quadros5cdcea02022-02-19 20:50:04 +0200205 TestFunctional._MakeInputFile('tee-pager.bin', TEE_OS_DATA)
Simon Glass3efb2972021-11-23 21:08:59 -0700206 TestFunctional._MakeInputFile('bl2u.bin', ATF_BL2U_DATA)
Bin Mengc0b15742021-05-10 20:23:33 +0800207 TestFunctional._MakeInputFile('fw_dynamic.bin', OPENSBI_DATA)
Samuel Holland9d8cc632020-10-21 21:12:15 -0500208 TestFunctional._MakeInputFile('scp.bin', SCP_DATA)
Jonas Karlman35305492023-02-25 19:01:33 +0000209 TestFunctional._MakeInputFile('rockchip-tpl.bin', ROCKCHIP_TPL_DATA)
Simon Glass7ba33592018-09-14 04:57:26 -0600210
Simon Glassa435cd12020-09-01 05:13:59 -0600211 # Add a few .dtb files for testing
212 TestFunctional._MakeInputFile('%s/test-fdt1.dtb' % TEST_FDT_SUBDIR,
213 TEST_FDT1_DATA)
214 TestFunctional._MakeInputFile('%s/test-fdt2.dtb' % TEST_FDT_SUBDIR,
215 TEST_FDT2_DATA)
216
Simon Glassa0729502020-09-06 10:35:33 -0600217 TestFunctional._MakeInputFile('env.txt', ENV_DATA)
218
Simon Glass5f423422022-03-05 20:19:12 -0700219 # ELF file with two sections in different parts of memory, used for both
220 # ATF and OP_TEE
221 TestFunctional._MakeInputFile('bl31.elf',
222 tools.read_file(cls.ElfTestFile('elf_sections')))
223 TestFunctional._MakeInputFile('tee.elf',
224 tools.read_file(cls.ElfTestFile('elf_sections')))
225
Simon Glassad5cfe12023-01-07 14:07:14 -0700226 # Newer OP_TEE file in v1 binary format
227 cls.make_tee_bin('tee.bin')
228
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +0200229 cls.comp_bintools = {}
230 for name in COMP_BINTOOLS:
231 cls.comp_bintools[name] = bintool.Bintool.create(name)
Simon Glass1de34482019-07-08 13:18:53 -0600232
Simon Glass57454f42016-11-25 20:15:52 -0700233 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600234 def tearDownClass(cls):
Simon Glass57454f42016-11-25 20:15:52 -0700235 """Remove the temporary input directory and its contents"""
Simon Glass862f8e22019-08-24 07:22:43 -0600236 if cls.preserve_indir:
237 print('Preserving input dir: %s' % cls._indir)
Simon Glass1c420c92019-07-08 13:18:49 -0600238 else:
Simon Glass862f8e22019-08-24 07:22:43 -0600239 if cls._indir:
240 shutil.rmtree(cls._indir)
241 cls._indir = None
Simon Glass57454f42016-11-25 20:15:52 -0700242
Simon Glass1c420c92019-07-08 13:18:49 -0600243 @classmethod
Simon Glasscebfab22019-07-08 13:18:50 -0600244 def setup_test_args(cls, preserve_indir=False, preserve_outdirs=False,
Simon Glassf46732a2019-07-08 14:25:29 -0600245 toolpath=None, verbosity=None):
Simon Glass1c420c92019-07-08 13:18:49 -0600246 """Accept arguments controlling test execution
247
248 Args:
249 preserve_indir: Preserve the shared input directory used by all
250 tests in this class.
251 preserve_outdir: Preserve the output directories used by tests. Each
252 test has its own, so this is normally only useful when running a
253 single test.
Simon Glasscebfab22019-07-08 13:18:50 -0600254 toolpath: ist of paths to use for tools
Simon Glass1c420c92019-07-08 13:18:49 -0600255 """
256 cls.preserve_indir = preserve_indir
257 cls.preserve_outdirs = preserve_outdirs
Simon Glasscebfab22019-07-08 13:18:50 -0600258 cls.toolpath = toolpath
Simon Glassf46732a2019-07-08 14:25:29 -0600259 cls.verbosity = verbosity
Simon Glass1c420c92019-07-08 13:18:49 -0600260
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +0200261 def _CheckBintool(self, bintool):
262 if not bintool.is_present():
263 self.skipTest('%s not available' % bintool.name)
264
Simon Glass1de34482019-07-08 13:18:53 -0600265 def _CheckLz4(self):
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +0200266 bintool = self.comp_bintools['lz4']
267 self._CheckBintool(bintool)
Simon Glass1de34482019-07-08 13:18:53 -0600268
Simon Glassee9d10d2019-07-20 12:24:09 -0600269 def _CleanupOutputDir(self):
270 """Remove the temporary output directory"""
271 if self.preserve_outdirs:
272 print('Preserving output dir: %s' % tools.outdir)
273 else:
Simon Glass80025522022-01-29 14:14:04 -0700274 tools._finalise_for_test()
Simon Glassee9d10d2019-07-20 12:24:09 -0600275
Simon Glass57454f42016-11-25 20:15:52 -0700276 def setUp(self):
277 # Enable this to turn on debugging output
Simon Glass011f1b32022-01-29 14:14:15 -0700278 # tout.init(tout.DEBUG)
Simon Glass57454f42016-11-25 20:15:52 -0700279 command.test_result = None
280
281 def tearDown(self):
282 """Remove the temporary output directory"""
Simon Glassee9d10d2019-07-20 12:24:09 -0600283 self._CleanupOutputDir()
Simon Glass57454f42016-11-25 20:15:52 -0700284
Simon Glassb3d6fc72019-07-20 12:24:10 -0600285 def _SetupImageInTmpdir(self):
286 """Set up the output image in a new temporary directory
287
288 This is used when an image has been generated in the output directory,
289 but we want to run binman again. This will create a new output
290 directory and fail to delete the original one.
291
292 This creates a new temporary directory, copies the image to it (with a
293 new name) and removes the old output directory.
294
295 Returns:
296 Tuple:
297 Temporary directory to use
298 New image filename
299 """
Simon Glass80025522022-01-29 14:14:04 -0700300 image_fname = tools.get_output_filename('image.bin')
Simon Glassb3d6fc72019-07-20 12:24:10 -0600301 tmpdir = tempfile.mkdtemp(prefix='binman.')
302 updated_fname = os.path.join(tmpdir, 'image-updated.bin')
Simon Glass80025522022-01-29 14:14:04 -0700303 tools.write_file(updated_fname, tools.read_file(image_fname))
Simon Glassb3d6fc72019-07-20 12:24:10 -0600304 self._CleanupOutputDir()
305 return tmpdir, updated_fname
306
Simon Glass8425a1f2018-07-17 13:25:48 -0600307 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600308 def _ResetDtbs(cls):
Simon Glass8425a1f2018-07-17 13:25:48 -0600309 TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA)
310 TestFunctional._MakeInputFile('spl/u-boot-spl.dtb', U_BOOT_SPL_DTB_DATA)
311 TestFunctional._MakeInputFile('tpl/u-boot-tpl.dtb', U_BOOT_TPL_DTB_DATA)
Simon Glass56d05412022-02-28 07:16:54 -0700312 TestFunctional._MakeInputFile('vpl/u-boot-vpl.dtb', U_BOOT_VPL_DTB_DATA)
Simon Glass8425a1f2018-07-17 13:25:48 -0600313
Simon Glass57454f42016-11-25 20:15:52 -0700314 def _RunBinman(self, *args, **kwargs):
315 """Run binman using the command line
316
317 Args:
318 Arguments to pass, as a list of strings
319 kwargs: Arguments to pass to Command.RunPipe()
320 """
Simon Glass840be732022-01-29 14:14:05 -0700321 result = command.run_pipe([[self._binman_pathname] + list(args)],
Simon Glass57454f42016-11-25 20:15:52 -0700322 capture=True, capture_stderr=True, raise_on_error=False)
323 if result.return_code and kwargs.get('raise_on_error', True):
324 raise Exception("Error running '%s': %s" % (' '.join(args),
325 result.stdout + result.stderr))
326 return result
327
Simon Glassf46732a2019-07-08 14:25:29 -0600328 def _DoBinman(self, *argv):
Simon Glass57454f42016-11-25 20:15:52 -0700329 """Run binman using directly (in the same process)
330
331 Args:
332 Arguments to pass, as a list of strings
333 Returns:
334 Return value (0 for success)
335 """
Simon Glassf46732a2019-07-08 14:25:29 -0600336 argv = list(argv)
337 args = cmdline.ParseArgs(argv)
338 args.pager = 'binman-invalid-pager'
339 args.build_dir = self._indir
Simon Glass57454f42016-11-25 20:15:52 -0700340
341 # For testing, you can force an increase in verbosity here
Simon Glassf46732a2019-07-08 14:25:29 -0600342 # args.verbosity = tout.DEBUG
343 return control.Binman(args)
Simon Glass57454f42016-11-25 20:15:52 -0700344
Simon Glass91710b32018-07-17 13:25:32 -0600345 def _DoTestFile(self, fname, debug=False, map=False, update_dtb=False,
Simon Glassb4595d82019-04-25 21:58:34 -0600346 entry_args=None, images=None, use_real_dtb=False,
Simon Glassed930672021-03-18 20:25:05 +1300347 use_expanded=False, verbosity=None, allow_missing=False,
Heiko Thiery6d451362022-01-06 11:49:41 +0100348 allow_fake_blobs=False, extra_indirs=None, threads=None,
Simon Glass66152ce2022-01-09 20:14:09 -0700349 test_section_timeout=False, update_fdt_in_elf=None,
Simon Glass6bce5dc2022-11-09 19:14:42 -0700350 force_missing_bintools='', ignore_missing=False):
Simon Glass57454f42016-11-25 20:15:52 -0700351 """Run binman with a given test file
352
353 Args:
Simon Glass511f6582018-10-01 12:22:30 -0600354 fname: Device-tree source filename to use (e.g. 005_simple.dts)
Simon Glass1e324002018-06-01 09:38:19 -0600355 debug: True to enable debugging output
Simon Glass30732662018-06-01 09:38:20 -0600356 map: True to output map files for the images
Simon Glasse8561af2018-08-01 15:22:37 -0600357 update_dtb: Update the offset and size of each entry in the device
Simon Glassa87014e2018-07-06 10:27:42 -0600358 tree before packing it into the image
Simon Glass3b376c32018-09-14 04:57:12 -0600359 entry_args: Dict of entry args to supply to binman
360 key: arg name
361 value: value of that arg
362 images: List of image names to build
Simon Glass31ee50f2020-09-01 05:13:55 -0600363 use_real_dtb: True to use the test file as the contents of
364 the u-boot-dtb entry. Normally this is not needed and the
365 test contents (the U_BOOT_DTB_DATA string) can be used.
366 But in some test we need the real contents.
Simon Glassed930672021-03-18 20:25:05 +1300367 use_expanded: True to use expanded entries where available, e.g.
368 'u-boot-expanded' instead of 'u-boot'
Simon Glass31ee50f2020-09-01 05:13:55 -0600369 verbosity: Verbosity level to use (0-3, None=don't set it)
370 allow_missing: Set the '--allow-missing' flag so that missing
371 external binaries just produce a warning instead of an error
Heiko Thiery6d451362022-01-06 11:49:41 +0100372 allow_fake_blobs: Set the '--fake-ext-blobs' flag
Simon Glassa435cd12020-09-01 05:13:59 -0600373 extra_indirs: Extra input directories to add using -I
Simon Glass76f496d2021-07-06 10:36:37 -0600374 threads: Number of threads to use (None for default, 0 for
375 single-threaded)
Simon Glass9a798402021-11-03 21:09:17 -0600376 test_section_timeout: True to force the first time to timeout, as
377 used in testThreadTimeout()
Simon Glassadfb8492021-11-03 21:09:18 -0600378 update_fdt_in_elf: Value to pass with --update-fdt-in-elf=xxx
Simon Glass66152ce2022-01-09 20:14:09 -0700379 force_missing_tools (str): comma-separated list of bintools to
380 regard as missing
Simon Glass9a798402021-11-03 21:09:17 -0600381
382 Returns:
383 int return code, 0 on success
Simon Glass57454f42016-11-25 20:15:52 -0700384 """
Simon Glassf46732a2019-07-08 14:25:29 -0600385 args = []
Simon Glass075a45c2017-11-13 18:55:00 -0700386 if debug:
387 args.append('-D')
Simon Glassf46732a2019-07-08 14:25:29 -0600388 if verbosity is not None:
389 args.append('-v%d' % verbosity)
390 elif self.verbosity:
391 args.append('-v%d' % self.verbosity)
392 if self.toolpath:
393 for path in self.toolpath:
394 args += ['--toolpath', path]
Simon Glass76f496d2021-07-06 10:36:37 -0600395 if threads is not None:
396 args.append('-T%d' % threads)
397 if test_section_timeout:
398 args.append('--test-section-timeout')
Simon Glassf46732a2019-07-08 14:25:29 -0600399 args += ['build', '-p', '-I', self._indir, '-d', self.TestFile(fname)]
Simon Glass30732662018-06-01 09:38:20 -0600400 if map:
401 args.append('-m')
Simon Glassa87014e2018-07-06 10:27:42 -0600402 if update_dtb:
Simon Glass38a411c2019-07-08 13:18:47 -0600403 args.append('-u')
Simon Glass31402012018-09-14 04:57:23 -0600404 if not use_real_dtb:
405 args.append('--fake-dtb')
Simon Glassed930672021-03-18 20:25:05 +1300406 if not use_expanded:
407 args.append('--no-expanded')
Simon Glass91710b32018-07-17 13:25:32 -0600408 if entry_args:
Simon Glass5f3645b2019-05-14 15:53:41 -0600409 for arg, value in entry_args.items():
Simon Glass91710b32018-07-17 13:25:32 -0600410 args.append('-a%s=%s' % (arg, value))
Simon Glass5d94cc62020-07-09 18:39:38 -0600411 if allow_missing:
412 args.append('-M')
Simon Glass6bce5dc2022-11-09 19:14:42 -0700413 if ignore_missing:
414 args.append('-W')
Heiko Thiery6d451362022-01-06 11:49:41 +0100415 if allow_fake_blobs:
416 args.append('--fake-ext-blobs')
Simon Glass66152ce2022-01-09 20:14:09 -0700417 if force_missing_bintools:
418 args += ['--force-missing-bintools', force_missing_bintools]
Simon Glassadfb8492021-11-03 21:09:18 -0600419 if update_fdt_in_elf:
420 args += ['--update-fdt-in-elf', update_fdt_in_elf]
Simon Glass3b376c32018-09-14 04:57:12 -0600421 if images:
422 for image in images:
423 args += ['-i', image]
Simon Glassa435cd12020-09-01 05:13:59 -0600424 if extra_indirs:
425 for indir in extra_indirs:
426 args += ['-I', indir]
Simon Glass075a45c2017-11-13 18:55:00 -0700427 return self._DoBinman(*args)
Simon Glass57454f42016-11-25 20:15:52 -0700428
429 def _SetupDtb(self, fname, outfile='u-boot.dtb'):
Simon Glass72232452016-11-25 20:15:53 -0700430 """Set up a new test device-tree file
431
432 The given file is compiled and set up as the device tree to be used
433 for ths test.
434
435 Args:
436 fname: Filename of .dts file to read
Simon Glass1e324002018-06-01 09:38:19 -0600437 outfile: Output filename for compiled device-tree binary
Simon Glass72232452016-11-25 20:15:53 -0700438
439 Returns:
Simon Glass1e324002018-06-01 09:38:19 -0600440 Contents of device-tree binary
Simon Glass72232452016-11-25 20:15:53 -0700441 """
Simon Glassb8d2daa2019-07-20 12:23:49 -0600442 tmpdir = tempfile.mkdtemp(prefix='binmant.')
443 dtb = fdt_util.EnsureCompiled(self.TestFile(fname), tmpdir)
Simon Glass33486662019-05-14 15:53:42 -0600444 with open(dtb, 'rb') as fd:
Simon Glass57454f42016-11-25 20:15:52 -0700445 data = fd.read()
446 TestFunctional._MakeInputFile(outfile, data)
Simon Glassb8d2daa2019-07-20 12:23:49 -0600447 shutil.rmtree(tmpdir)
Simon Glass752e7552018-10-01 21:12:41 -0600448 return data
Simon Glass57454f42016-11-25 20:15:52 -0700449
Simon Glass56d05412022-02-28 07:16:54 -0700450 def _GetDtbContentsForSpls(self, dtb_data, name):
451 """Create a version of the main DTB for SPL / TPL / VPL
Simon Glasse219aa42018-09-14 04:57:24 -0600452
453 For testing we don't actually have different versions of the DTB. With
454 U-Boot we normally run fdtgrep to remove unwanted nodes, but for tests
455 we don't normally have any unwanted nodes.
456
457 We still want the DTBs for SPL and TPL to be different though, since
458 otherwise it is confusing to know which one we are looking at. So add
459 an 'spl' or 'tpl' property to the top-level node.
Simon Glass31ee50f2020-09-01 05:13:55 -0600460
461 Args:
462 dtb_data: dtb data to modify (this should be a value devicetree)
463 name: Name of a new property to add
464
465 Returns:
466 New dtb data with the property added
Simon Glasse219aa42018-09-14 04:57:24 -0600467 """
468 dtb = fdt.Fdt.FromData(dtb_data)
469 dtb.Scan()
470 dtb.GetNode('/binman').AddZeroProp(name)
471 dtb.Sync(auto_resize=True)
472 dtb.Pack()
473 return dtb.GetContents()
474
Simon Glassed930672021-03-18 20:25:05 +1300475 def _DoReadFileDtb(self, fname, use_real_dtb=False, use_expanded=False,
476 map=False, update_dtb=False, entry_args=None,
Simon Glass76f496d2021-07-06 10:36:37 -0600477 reset_dtbs=True, extra_indirs=None, threads=None):
Simon Glass57454f42016-11-25 20:15:52 -0700478 """Run binman and return the resulting image
479
480 This runs binman with a given test file and then reads the resulting
481 output file. It is a shortcut function since most tests need to do
482 these steps.
483
484 Raises an assertion failure if binman returns a non-zero exit code.
485
486 Args:
Simon Glass511f6582018-10-01 12:22:30 -0600487 fname: Device-tree source filename to use (e.g. 005_simple.dts)
Simon Glass57454f42016-11-25 20:15:52 -0700488 use_real_dtb: True to use the test file as the contents of
489 the u-boot-dtb entry. Normally this is not needed and the
490 test contents (the U_BOOT_DTB_DATA string) can be used.
491 But in some test we need the real contents.
Simon Glassed930672021-03-18 20:25:05 +1300492 use_expanded: True to use expanded entries where available, e.g.
493 'u-boot-expanded' instead of 'u-boot'
Simon Glass30732662018-06-01 09:38:20 -0600494 map: True to output map files for the images
Simon Glasse8561af2018-08-01 15:22:37 -0600495 update_dtb: Update the offset and size of each entry in the device
Simon Glassa87014e2018-07-06 10:27:42 -0600496 tree before packing it into the image
Simon Glass31ee50f2020-09-01 05:13:55 -0600497 entry_args: Dict of entry args to supply to binman
498 key: arg name
499 value: value of that arg
500 reset_dtbs: With use_real_dtb the test dtb is overwritten by this
501 function. If reset_dtbs is True, then the original test dtb
502 is written back before this function finishes
Simon Glassa435cd12020-09-01 05:13:59 -0600503 extra_indirs: Extra input directories to add using -I
Simon Glass76f496d2021-07-06 10:36:37 -0600504 threads: Number of threads to use (None for default, 0 for
505 single-threaded)
Simon Glass72232452016-11-25 20:15:53 -0700506
507 Returns:
508 Tuple:
509 Resulting image contents
510 Device tree contents
Simon Glass30732662018-06-01 09:38:20 -0600511 Map data showing contents of image (or None if none)
Simon Glassdef77b52018-07-17 13:25:27 -0600512 Output device tree binary filename ('u-boot.dtb' path)
Simon Glass57454f42016-11-25 20:15:52 -0700513 """
Simon Glass72232452016-11-25 20:15:53 -0700514 dtb_data = None
Simon Glass57454f42016-11-25 20:15:52 -0700515 # Use the compiled test file as the u-boot-dtb input
516 if use_real_dtb:
Simon Glass72232452016-11-25 20:15:53 -0700517 dtb_data = self._SetupDtb(fname)
Simon Glasse219aa42018-09-14 04:57:24 -0600518
519 # For testing purposes, make a copy of the DT for SPL and TPL. Add
520 # a node indicating which it is, so aid verification.
Simon Glass56d05412022-02-28 07:16:54 -0700521 for name in ['spl', 'tpl', 'vpl']:
Simon Glasse219aa42018-09-14 04:57:24 -0600522 dtb_fname = '%s/u-boot-%s.dtb' % (name, name)
523 outfile = os.path.join(self._indir, dtb_fname)
524 TestFunctional._MakeInputFile(dtb_fname,
Simon Glass56d05412022-02-28 07:16:54 -0700525 self._GetDtbContentsForSpls(dtb_data, name))
Simon Glass57454f42016-11-25 20:15:52 -0700526
527 try:
Simon Glass91710b32018-07-17 13:25:32 -0600528 retcode = self._DoTestFile(fname, map=map, update_dtb=update_dtb,
Simon Glassa435cd12020-09-01 05:13:59 -0600529 entry_args=entry_args, use_real_dtb=use_real_dtb,
Simon Glass76f496d2021-07-06 10:36:37 -0600530 use_expanded=use_expanded, extra_indirs=extra_indirs,
531 threads=threads)
Simon Glass57454f42016-11-25 20:15:52 -0700532 self.assertEqual(0, retcode)
Simon Glass80025522022-01-29 14:14:04 -0700533 out_dtb_fname = tools.get_output_filename('u-boot.dtb.out')
Simon Glass57454f42016-11-25 20:15:52 -0700534
535 # Find the (only) image, read it and return its contents
536 image = control.images['image']
Simon Glass80025522022-01-29 14:14:04 -0700537 image_fname = tools.get_output_filename('image.bin')
Simon Glassa87014e2018-07-06 10:27:42 -0600538 self.assertTrue(os.path.exists(image_fname))
Simon Glass30732662018-06-01 09:38:20 -0600539 if map:
Simon Glass80025522022-01-29 14:14:04 -0700540 map_fname = tools.get_output_filename('image.map')
Simon Glass30732662018-06-01 09:38:20 -0600541 with open(map_fname) as fd:
542 map_data = fd.read()
543 else:
544 map_data = None
Simon Glass33486662019-05-14 15:53:42 -0600545 with open(image_fname, 'rb') as fd:
Simon Glassa87014e2018-07-06 10:27:42 -0600546 return fd.read(), dtb_data, map_data, out_dtb_fname
Simon Glass57454f42016-11-25 20:15:52 -0700547 finally:
548 # Put the test file back
Simon Glasse219aa42018-09-14 04:57:24 -0600549 if reset_dtbs and use_real_dtb:
Simon Glass8425a1f2018-07-17 13:25:48 -0600550 self._ResetDtbs()
Simon Glass57454f42016-11-25 20:15:52 -0700551
Simon Glass5b4bce32019-07-08 14:25:26 -0600552 def _DoReadFileRealDtb(self, fname):
553 """Run binman with a real .dtb file and return the resulting data
554
555 Args:
556 fname: DT source filename to use (e.g. 082_fdt_update_all.dts)
557
558 Returns:
559 Resulting image contents
560 """
561 return self._DoReadFileDtb(fname, use_real_dtb=True, update_dtb=True)[0]
562
Simon Glass72232452016-11-25 20:15:53 -0700563 def _DoReadFile(self, fname, use_real_dtb=False):
Simon Glass1e324002018-06-01 09:38:19 -0600564 """Helper function which discards the device-tree binary
565
566 Args:
Simon Glass511f6582018-10-01 12:22:30 -0600567 fname: Device-tree source filename to use (e.g. 005_simple.dts)
Simon Glass1e324002018-06-01 09:38:19 -0600568 use_real_dtb: True to use the test file as the contents of
569 the u-boot-dtb entry. Normally this is not needed and the
570 test contents (the U_BOOT_DTB_DATA string) can be used.
571 But in some test we need the real contents.
Simon Glassdef77b52018-07-17 13:25:27 -0600572
573 Returns:
574 Resulting image contents
Simon Glass1e324002018-06-01 09:38:19 -0600575 """
Simon Glass72232452016-11-25 20:15:53 -0700576 return self._DoReadFileDtb(fname, use_real_dtb)[0]
577
Simon Glass57454f42016-11-25 20:15:52 -0700578 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600579 def _MakeInputFile(cls, fname, contents):
Simon Glass57454f42016-11-25 20:15:52 -0700580 """Create a new test input file, creating directories as needed
581
582 Args:
Simon Glasse8561af2018-08-01 15:22:37 -0600583 fname: Filename to create
Simon Glass57454f42016-11-25 20:15:52 -0700584 contents: File contents to write in to the file
585 Returns:
586 Full pathname of file created
587 """
Simon Glass862f8e22019-08-24 07:22:43 -0600588 pathname = os.path.join(cls._indir, fname)
Simon Glass57454f42016-11-25 20:15:52 -0700589 dirname = os.path.dirname(pathname)
590 if dirname and not os.path.exists(dirname):
591 os.makedirs(dirname)
592 with open(pathname, 'wb') as fd:
593 fd.write(contents)
594 return pathname
595
596 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600597 def _MakeInputDir(cls, dirname):
Simon Glassc1ae83c2018-07-17 13:25:44 -0600598 """Create a new test input directory, creating directories as needed
599
600 Args:
601 dirname: Directory name to create
602
603 Returns:
604 Full pathname of directory created
605 """
Simon Glass862f8e22019-08-24 07:22:43 -0600606 pathname = os.path.join(cls._indir, dirname)
Simon Glassc1ae83c2018-07-17 13:25:44 -0600607 if not os.path.exists(pathname):
608 os.makedirs(pathname)
609 return pathname
610
611 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600612 def _SetupSplElf(cls, src_fname='bss_data'):
Simon Glass7057d022018-10-01 21:12:47 -0600613 """Set up an ELF file with a '_dt_ucode_base_size' symbol
614
615 Args:
616 Filename of ELF file to use as SPL
617 """
Simon Glass93a806f2019-08-24 07:22:59 -0600618 TestFunctional._MakeInputFile('spl/u-boot-spl',
Simon Glass80025522022-01-29 14:14:04 -0700619 tools.read_file(cls.ElfTestFile(src_fname)))
Simon Glass7057d022018-10-01 21:12:47 -0600620
621 @classmethod
Simon Glass3eb5b202019-08-24 07:23:00 -0600622 def _SetupTplElf(cls, src_fname='bss_data'):
623 """Set up an ELF file with a '_dt_ucode_base_size' symbol
624
625 Args:
626 Filename of ELF file to use as TPL
627 """
628 TestFunctional._MakeInputFile('tpl/u-boot-tpl',
Simon Glass80025522022-01-29 14:14:04 -0700629 tools.read_file(cls.ElfTestFile(src_fname)))
Simon Glass3eb5b202019-08-24 07:23:00 -0600630
631 @classmethod
Simon Glass56d05412022-02-28 07:16:54 -0700632 def _SetupVplElf(cls, src_fname='bss_data'):
633 """Set up an ELF file with a '_dt_ucode_base_size' symbol
634
635 Args:
636 Filename of ELF file to use as VPL
637 """
638 TestFunctional._MakeInputFile('vpl/u-boot-vpl',
639 tools.read_file(cls.ElfTestFile(src_fname)))
640
641 @classmethod
Simon Glasse88cef92020-07-09 18:39:41 -0600642 def _SetupDescriptor(cls):
643 with open(cls.TestFile('descriptor.bin'), 'rb') as fd:
644 TestFunctional._MakeInputFile('descriptor.bin', fd.read())
645
646 @classmethod
Simon Glass862f8e22019-08-24 07:22:43 -0600647 def TestFile(cls, fname):
648 return os.path.join(cls._binman_dir, 'test', fname)
Simon Glass57454f42016-11-25 20:15:52 -0700649
Simon Glassf6290892019-08-24 07:22:53 -0600650 @classmethod
651 def ElfTestFile(cls, fname):
652 return os.path.join(cls._elf_testdir, fname)
653
Simon Glassad5cfe12023-01-07 14:07:14 -0700654 @classmethod
655 def make_tee_bin(cls, fname, paged_sz=0, extra_data=b''):
656 init_sz, start_hi, start_lo, dummy = (len(U_BOOT_DATA), 0, TEE_ADDR, 0)
657 data = b'OPTE\x01xxx' + struct.pack('<5I', init_sz, start_hi, start_lo,
658 dummy, paged_sz) + U_BOOT_DATA
659 data += extra_data
660 TestFunctional._MakeInputFile(fname, data)
661
Simon Glass57454f42016-11-25 20:15:52 -0700662 def AssertInList(self, grep_list, target):
663 """Assert that at least one of a list of things is in a target
664
665 Args:
666 grep_list: List of strings to check
667 target: Target string
668 """
669 for grep in grep_list:
670 if grep in target:
671 return
Simon Glass848cdb52019-05-17 22:00:50 -0600672 self.fail("Error: '%s' not found in '%s'" % (grep_list, target))
Simon Glass57454f42016-11-25 20:15:52 -0700673
674 def CheckNoGaps(self, entries):
675 """Check that all entries fit together without gaps
676
677 Args:
678 entries: List of entries to check
679 """
Simon Glasse8561af2018-08-01 15:22:37 -0600680 offset = 0
Simon Glass57454f42016-11-25 20:15:52 -0700681 for entry in entries.values():
Simon Glasse8561af2018-08-01 15:22:37 -0600682 self.assertEqual(offset, entry.offset)
683 offset += entry.size
Simon Glass57454f42016-11-25 20:15:52 -0700684
Simon Glass72232452016-11-25 20:15:53 -0700685 def GetFdtLen(self, dtb):
Simon Glass1e324002018-06-01 09:38:19 -0600686 """Get the totalsize field from a device-tree binary
Simon Glass72232452016-11-25 20:15:53 -0700687
688 Args:
Simon Glass1e324002018-06-01 09:38:19 -0600689 dtb: Device-tree binary contents
Simon Glass72232452016-11-25 20:15:53 -0700690
691 Returns:
Simon Glass1e324002018-06-01 09:38:19 -0600692 Total size of device-tree binary, from the header
Simon Glass72232452016-11-25 20:15:53 -0700693 """
694 return struct.unpack('>L', dtb[4:8])[0]
695
Simon Glass0f621332019-07-08 14:25:27 -0600696 def _GetPropTree(self, dtb, prop_names, prefix='/binman/'):
Simon Glassa87014e2018-07-06 10:27:42 -0600697 def AddNode(node, path):
698 if node.name != '/':
699 path += '/' + node.name
Simon Glass0f621332019-07-08 14:25:27 -0600700 for prop in node.props.values():
701 if prop.name in prop_names:
702 prop_path = path + ':' + prop.name
703 tree[prop_path[len(prefix):]] = fdt_util.fdt32_to_cpu(
704 prop.value)
Simon Glassa87014e2018-07-06 10:27:42 -0600705 for subnode in node.subnodes:
Simon Glassa87014e2018-07-06 10:27:42 -0600706 AddNode(subnode, path)
707
708 tree = {}
Simon Glassa87014e2018-07-06 10:27:42 -0600709 AddNode(dtb.GetRoot(), '')
710 return tree
711
Ivan Mikhaylov3cfcaa4d2023-03-08 01:13:40 +0000712 def _CheckSign(self, fit, key):
713 try:
714 tools.run('fit_check_sign', '-k', key, '-f', fit)
715 except:
716 self.fail('Expected signed FIT container')
717 return False
718 return True
719
Simon Glass57454f42016-11-25 20:15:52 -0700720 def testRun(self):
721 """Test a basic run with valid args"""
722 result = self._RunBinman('-h')
723
724 def testFullHelp(self):
725 """Test that the full help is displayed with -H"""
726 result = self._RunBinman('-H')
Simon Glass75ead662021-03-18 20:25:13 +1300727 help_file = os.path.join(self._binman_dir, 'README.rst')
Tom Rinic3c0b6d2018-01-16 15:29:50 -0500728 # Remove possible extraneous strings
729 extra = '::::::::::::::\n' + help_file + '\n::::::::::::::\n'
730 gothelp = result.stdout.replace(extra, '')
731 self.assertEqual(len(gothelp), os.path.getsize(help_file))
Simon Glass57454f42016-11-25 20:15:52 -0700732 self.assertEqual(0, len(result.stderr))
733 self.assertEqual(0, result.return_code)
734
735 def testFullHelpInternal(self):
736 """Test that the full help is displayed with -H"""
737 try:
738 command.test_result = command.CommandResult()
739 result = self._DoBinman('-H')
Simon Glass75ead662021-03-18 20:25:13 +1300740 help_file = os.path.join(self._binman_dir, 'README.rst')
Simon Glass57454f42016-11-25 20:15:52 -0700741 finally:
742 command.test_result = None
743
744 def testHelp(self):
745 """Test that the basic help is displayed with -h"""
746 result = self._RunBinman('-h')
747 self.assertTrue(len(result.stdout) > 200)
748 self.assertEqual(0, len(result.stderr))
749 self.assertEqual(0, result.return_code)
750
Simon Glass57454f42016-11-25 20:15:52 -0700751 def testBoard(self):
752 """Test that we can run it with a specific board"""
Simon Glass511f6582018-10-01 12:22:30 -0600753 self._SetupDtb('005_simple.dts', 'sandbox/u-boot.dtb')
Simon Glass57454f42016-11-25 20:15:52 -0700754 TestFunctional._MakeInputFile('sandbox/u-boot.bin', U_BOOT_DATA)
Simon Glassed930672021-03-18 20:25:05 +1300755 result = self._DoBinman('build', '-n', '-b', 'sandbox')
Simon Glass57454f42016-11-25 20:15:52 -0700756 self.assertEqual(0, result)
757
758 def testNeedBoard(self):
759 """Test that we get an error when no board ius supplied"""
760 with self.assertRaises(ValueError) as e:
Simon Glassf46732a2019-07-08 14:25:29 -0600761 result = self._DoBinman('build')
Simon Glass57454f42016-11-25 20:15:52 -0700762 self.assertIn("Must provide a board to process (use -b <board>)",
763 str(e.exception))
764
765 def testMissingDt(self):
Simon Glass1e324002018-06-01 09:38:19 -0600766 """Test that an invalid device-tree file generates an error"""
Simon Glass57454f42016-11-25 20:15:52 -0700767 with self.assertRaises(Exception) as e:
Simon Glassf46732a2019-07-08 14:25:29 -0600768 self._RunBinman('build', '-d', 'missing_file')
Simon Glass57454f42016-11-25 20:15:52 -0700769 # We get one error from libfdt, and a different one from fdtget.
770 self.AssertInList(["Couldn't open blob from 'missing_file'",
771 'No such file or directory'], str(e.exception))
772
773 def testBrokenDt(self):
Simon Glass1e324002018-06-01 09:38:19 -0600774 """Test that an invalid device-tree source file generates an error
Simon Glass57454f42016-11-25 20:15:52 -0700775
776 Since this is a source file it should be compiled and the error
777 will come from the device-tree compiler (dtc).
778 """
779 with self.assertRaises(Exception) as e:
Simon Glassf46732a2019-07-08 14:25:29 -0600780 self._RunBinman('build', '-d', self.TestFile('001_invalid.dts'))
Simon Glass57454f42016-11-25 20:15:52 -0700781 self.assertIn("FATAL ERROR: Unable to parse input tree",
782 str(e.exception))
783
784 def testMissingNode(self):
785 """Test that a device tree without a 'binman' node generates an error"""
786 with self.assertRaises(Exception) as e:
Simon Glassf46732a2019-07-08 14:25:29 -0600787 self._DoBinman('build', '-d', self.TestFile('002_missing_node.dts'))
Simon Glass57454f42016-11-25 20:15:52 -0700788 self.assertIn("does not have a 'binman' node", str(e.exception))
789
790 def testEmpty(self):
791 """Test that an empty binman node works OK (i.e. does nothing)"""
Simon Glassf46732a2019-07-08 14:25:29 -0600792 result = self._RunBinman('build', '-d', self.TestFile('003_empty.dts'))
Simon Glass57454f42016-11-25 20:15:52 -0700793 self.assertEqual(0, len(result.stderr))
794 self.assertEqual(0, result.return_code)
795
796 def testInvalidEntry(self):
797 """Test that an invalid entry is flagged"""
798 with self.assertRaises(Exception) as e:
Simon Glassf46732a2019-07-08 14:25:29 -0600799 result = self._RunBinman('build', '-d',
Simon Glass511f6582018-10-01 12:22:30 -0600800 self.TestFile('004_invalid_entry.dts'))
Simon Glass57454f42016-11-25 20:15:52 -0700801 self.assertIn("Unknown entry type 'not-a-valid-type' in node "
802 "'/binman/not-a-valid-type'", str(e.exception))
803
804 def testSimple(self):
805 """Test a simple binman with a single file"""
Simon Glass511f6582018-10-01 12:22:30 -0600806 data = self._DoReadFile('005_simple.dts')
Simon Glass57454f42016-11-25 20:15:52 -0700807 self.assertEqual(U_BOOT_DATA, data)
808
Simon Glass075a45c2017-11-13 18:55:00 -0700809 def testSimpleDebug(self):
810 """Test a simple binman run with debugging enabled"""
Simon Glass52d06212019-07-08 14:25:53 -0600811 self._DoTestFile('005_simple.dts', debug=True)
Simon Glass075a45c2017-11-13 18:55:00 -0700812
Simon Glass57454f42016-11-25 20:15:52 -0700813 def testDual(self):
814 """Test that we can handle creating two images
815
816 This also tests image padding.
817 """
Simon Glass511f6582018-10-01 12:22:30 -0600818 retcode = self._DoTestFile('006_dual_image.dts')
Simon Glass57454f42016-11-25 20:15:52 -0700819 self.assertEqual(0, retcode)
820
821 image = control.images['image1']
Simon Glass39dd2152019-07-08 14:25:47 -0600822 self.assertEqual(len(U_BOOT_DATA), image.size)
Simon Glass80025522022-01-29 14:14:04 -0700823 fname = tools.get_output_filename('image1.bin')
Simon Glass57454f42016-11-25 20:15:52 -0700824 self.assertTrue(os.path.exists(fname))
Simon Glass33486662019-05-14 15:53:42 -0600825 with open(fname, 'rb') as fd:
Simon Glass57454f42016-11-25 20:15:52 -0700826 data = fd.read()
827 self.assertEqual(U_BOOT_DATA, data)
828
829 image = control.images['image2']
Simon Glass39dd2152019-07-08 14:25:47 -0600830 self.assertEqual(3 + len(U_BOOT_DATA) + 5, image.size)
Simon Glass80025522022-01-29 14:14:04 -0700831 fname = tools.get_output_filename('image2.bin')
Simon Glass57454f42016-11-25 20:15:52 -0700832 self.assertTrue(os.path.exists(fname))
Simon Glass33486662019-05-14 15:53:42 -0600833 with open(fname, 'rb') as fd:
Simon Glass57454f42016-11-25 20:15:52 -0700834 data = fd.read()
835 self.assertEqual(U_BOOT_DATA, data[3:7])
Simon Glass80025522022-01-29 14:14:04 -0700836 self.assertEqual(tools.get_bytes(0, 3), data[:3])
837 self.assertEqual(tools.get_bytes(0, 5), data[7:])
Simon Glass57454f42016-11-25 20:15:52 -0700838
839 def testBadAlign(self):
840 """Test that an invalid alignment value is detected"""
841 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -0600842 self._DoTestFile('007_bad_align.dts')
Simon Glass57454f42016-11-25 20:15:52 -0700843 self.assertIn("Node '/binman/u-boot': Alignment 23 must be a power "
844 "of two", str(e.exception))
845
846 def testPackSimple(self):
847 """Test that packing works as expected"""
Simon Glass511f6582018-10-01 12:22:30 -0600848 retcode = self._DoTestFile('008_pack.dts')
Simon Glass57454f42016-11-25 20:15:52 -0700849 self.assertEqual(0, retcode)
850 self.assertIn('image', control.images)
851 image = control.images['image']
Simon Glasseca32212018-06-01 09:38:12 -0600852 entries = image.GetEntries()
Simon Glass57454f42016-11-25 20:15:52 -0700853 self.assertEqual(5, len(entries))
854
855 # First u-boot
856 self.assertIn('u-boot', entries)
857 entry = entries['u-boot']
Simon Glasse8561af2018-08-01 15:22:37 -0600858 self.assertEqual(0, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700859 self.assertEqual(len(U_BOOT_DATA), entry.size)
860
861 # Second u-boot, aligned to 16-byte boundary
862 self.assertIn('u-boot-align', entries)
863 entry = entries['u-boot-align']
Simon Glasse8561af2018-08-01 15:22:37 -0600864 self.assertEqual(16, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700865 self.assertEqual(len(U_BOOT_DATA), entry.size)
866
867 # Third u-boot, size 23 bytes
868 self.assertIn('u-boot-size', entries)
869 entry = entries['u-boot-size']
Simon Glasse8561af2018-08-01 15:22:37 -0600870 self.assertEqual(20, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700871 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
872 self.assertEqual(23, entry.size)
873
874 # Fourth u-boot, placed immediate after the above
875 self.assertIn('u-boot-next', entries)
876 entry = entries['u-boot-next']
Simon Glasse8561af2018-08-01 15:22:37 -0600877 self.assertEqual(43, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700878 self.assertEqual(len(U_BOOT_DATA), entry.size)
879
Simon Glasse8561af2018-08-01 15:22:37 -0600880 # Fifth u-boot, placed at a fixed offset
Simon Glass57454f42016-11-25 20:15:52 -0700881 self.assertIn('u-boot-fixed', entries)
882 entry = entries['u-boot-fixed']
Simon Glasse8561af2018-08-01 15:22:37 -0600883 self.assertEqual(61, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700884 self.assertEqual(len(U_BOOT_DATA), entry.size)
885
Simon Glass39dd2152019-07-08 14:25:47 -0600886 self.assertEqual(65, image.size)
Simon Glass57454f42016-11-25 20:15:52 -0700887
888 def testPackExtra(self):
889 """Test that extra packing feature works as expected"""
Simon Glassafb9caa2020-10-26 17:40:10 -0600890 data, _, _, out_dtb_fname = self._DoReadFileDtb('009_pack_extra.dts',
891 update_dtb=True)
Simon Glass57454f42016-11-25 20:15:52 -0700892
Simon Glass57454f42016-11-25 20:15:52 -0700893 self.assertIn('image', control.images)
894 image = control.images['image']
Simon Glasseca32212018-06-01 09:38:12 -0600895 entries = image.GetEntries()
Samuel Hollande2574022023-01-21 17:25:16 -0600896 self.assertEqual(6, len(entries))
Simon Glass57454f42016-11-25 20:15:52 -0700897
Samuel Hollande2574022023-01-21 17:25:16 -0600898 # First u-boot with padding before and after (included in minimum size)
Simon Glass57454f42016-11-25 20:15:52 -0700899 self.assertIn('u-boot', entries)
900 entry = entries['u-boot']
Simon Glasse8561af2018-08-01 15:22:37 -0600901 self.assertEqual(0, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700902 self.assertEqual(3, entry.pad_before)
903 self.assertEqual(3 + 5 + len(U_BOOT_DATA), entry.size)
Simon Glass187202f2020-10-26 17:40:08 -0600904 self.assertEqual(U_BOOT_DATA, entry.data)
Simon Glass80025522022-01-29 14:14:04 -0700905 self.assertEqual(tools.get_bytes(0, 3) + U_BOOT_DATA +
906 tools.get_bytes(0, 5), data[:entry.size])
Simon Glass187202f2020-10-26 17:40:08 -0600907 pos = entry.size
Simon Glass57454f42016-11-25 20:15:52 -0700908
909 # Second u-boot has an aligned size, but it has no effect
910 self.assertIn('u-boot-align-size-nop', entries)
911 entry = entries['u-boot-align-size-nop']
Simon Glass187202f2020-10-26 17:40:08 -0600912 self.assertEqual(pos, entry.offset)
913 self.assertEqual(len(U_BOOT_DATA), entry.size)
914 self.assertEqual(U_BOOT_DATA, entry.data)
915 self.assertEqual(U_BOOT_DATA, data[pos:pos + entry.size])
916 pos += entry.size
Simon Glass57454f42016-11-25 20:15:52 -0700917
918 # Third u-boot has an aligned size too
919 self.assertIn('u-boot-align-size', entries)
920 entry = entries['u-boot-align-size']
Simon Glass187202f2020-10-26 17:40:08 -0600921 self.assertEqual(pos, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700922 self.assertEqual(32, entry.size)
Simon Glass187202f2020-10-26 17:40:08 -0600923 self.assertEqual(U_BOOT_DATA, entry.data)
Simon Glass80025522022-01-29 14:14:04 -0700924 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 32 - len(U_BOOT_DATA)),
Simon Glass187202f2020-10-26 17:40:08 -0600925 data[pos:pos + entry.size])
926 pos += entry.size
Simon Glass57454f42016-11-25 20:15:52 -0700927
928 # Fourth u-boot has an aligned end
929 self.assertIn('u-boot-align-end', entries)
930 entry = entries['u-boot-align-end']
Simon Glasse8561af2018-08-01 15:22:37 -0600931 self.assertEqual(48, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700932 self.assertEqual(16, entry.size)
Simon Glass187202f2020-10-26 17:40:08 -0600933 self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)])
Simon Glass80025522022-01-29 14:14:04 -0700934 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 16 - len(U_BOOT_DATA)),
Simon Glass187202f2020-10-26 17:40:08 -0600935 data[pos:pos + entry.size])
936 pos += entry.size
Simon Glass57454f42016-11-25 20:15:52 -0700937
938 # Fifth u-boot immediately afterwards
939 self.assertIn('u-boot-align-both', entries)
940 entry = entries['u-boot-align-both']
Simon Glasse8561af2018-08-01 15:22:37 -0600941 self.assertEqual(64, entry.offset)
Simon Glass57454f42016-11-25 20:15:52 -0700942 self.assertEqual(64, entry.size)
Simon Glass187202f2020-10-26 17:40:08 -0600943 self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)])
Simon Glass80025522022-01-29 14:14:04 -0700944 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 64 - len(U_BOOT_DATA)),
Simon Glass187202f2020-10-26 17:40:08 -0600945 data[pos:pos + entry.size])
Simon Glass57454f42016-11-25 20:15:52 -0700946
Samuel Hollande2574022023-01-21 17:25:16 -0600947 # Sixth u-boot with both minimum size and aligned size
948 self.assertIn('u-boot-min-size', entries)
949 entry = entries['u-boot-min-size']
950 self.assertEqual(128, entry.offset)
951 self.assertEqual(32, entry.size)
952 self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)])
953 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 32 - len(U_BOOT_DATA)),
954 data[pos:pos + entry.size])
955
Simon Glass57454f42016-11-25 20:15:52 -0700956 self.CheckNoGaps(entries)
Samuel Hollande2574022023-01-21 17:25:16 -0600957 self.assertEqual(160, image.size)
Simon Glass57454f42016-11-25 20:15:52 -0700958
Simon Glassafb9caa2020-10-26 17:40:10 -0600959 dtb = fdt.Fdt(out_dtb_fname)
960 dtb.Scan()
961 props = self._GetPropTree(dtb, ['size', 'offset', 'image-pos'])
962 expected = {
963 'image-pos': 0,
964 'offset': 0,
Samuel Hollande2574022023-01-21 17:25:16 -0600965 'size': 160,
Simon Glassafb9caa2020-10-26 17:40:10 -0600966
967 'u-boot:image-pos': 0,
968 'u-boot:offset': 0,
969 'u-boot:size': 3 + 5 + len(U_BOOT_DATA),
970
971 'u-boot-align-size-nop:image-pos': 12,
972 'u-boot-align-size-nop:offset': 12,
973 'u-boot-align-size-nop:size': 4,
974
975 'u-boot-align-size:image-pos': 16,
976 'u-boot-align-size:offset': 16,
977 'u-boot-align-size:size': 32,
978
979 'u-boot-align-end:image-pos': 48,
980 'u-boot-align-end:offset': 48,
981 'u-boot-align-end:size': 16,
982
983 'u-boot-align-both:image-pos': 64,
984 'u-boot-align-both:offset': 64,
985 'u-boot-align-both:size': 64,
Samuel Hollande2574022023-01-21 17:25:16 -0600986
987 'u-boot-min-size:image-pos': 128,
988 'u-boot-min-size:offset': 128,
989 'u-boot-min-size:size': 32,
Simon Glassafb9caa2020-10-26 17:40:10 -0600990 }
991 self.assertEqual(expected, props)
992
Simon Glass57454f42016-11-25 20:15:52 -0700993 def testPackAlignPowerOf2(self):
994 """Test that invalid entry alignment is detected"""
995 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -0600996 self._DoTestFile('010_pack_align_power2.dts')
Simon Glass57454f42016-11-25 20:15:52 -0700997 self.assertIn("Node '/binman/u-boot': Alignment 5 must be a power "
998 "of two", str(e.exception))
999
1000 def testPackAlignSizePowerOf2(self):
1001 """Test that invalid entry size alignment is detected"""
1002 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001003 self._DoTestFile('011_pack_align_size_power2.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001004 self.assertIn("Node '/binman/u-boot': Alignment size 55 must be a "
1005 "power of two", str(e.exception))
1006
1007 def testPackInvalidAlign(self):
Simon Glasse8561af2018-08-01 15:22:37 -06001008 """Test detection of an offset that does not match its alignment"""
Simon Glass57454f42016-11-25 20:15:52 -07001009 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001010 self._DoTestFile('012_pack_inv_align.dts')
Simon Glasse8561af2018-08-01 15:22:37 -06001011 self.assertIn("Node '/binman/u-boot': Offset 0x5 (5) does not match "
Simon Glass57454f42016-11-25 20:15:52 -07001012 "align 0x4 (4)", str(e.exception))
1013
1014 def testPackInvalidSizeAlign(self):
1015 """Test that invalid entry size alignment is detected"""
1016 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001017 self._DoTestFile('013_pack_inv_size_align.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001018 self.assertIn("Node '/binman/u-boot': Size 0x5 (5) does not match "
1019 "align-size 0x4 (4)", str(e.exception))
1020
1021 def testPackOverlap(self):
1022 """Test that overlapping regions are detected"""
1023 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001024 self._DoTestFile('014_pack_overlap.dts')
Simon Glasse8561af2018-08-01 15:22:37 -06001025 self.assertIn("Node '/binman/u-boot-align': Offset 0x3 (3) overlaps "
Simon Glass57454f42016-11-25 20:15:52 -07001026 "with previous entry '/binman/u-boot' ending at 0x4 (4)",
1027 str(e.exception))
1028
1029 def testPackEntryOverflow(self):
1030 """Test that entries that overflow their size are detected"""
1031 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001032 self._DoTestFile('015_pack_overflow.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001033 self.assertIn("Node '/binman/u-boot': Entry contents size is 0x4 (4) "
1034 "but entry size is 0x3 (3)", str(e.exception))
1035
1036 def testPackImageOverflow(self):
1037 """Test that entries which overflow the image size are detected"""
1038 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001039 self._DoTestFile('016_pack_image_overflow.dts')
Simon Glasseca32212018-06-01 09:38:12 -06001040 self.assertIn("Section '/binman': contents size 0x4 (4) exceeds section "
Simon Glass57454f42016-11-25 20:15:52 -07001041 "size 0x3 (3)", str(e.exception))
1042
1043 def testPackImageSize(self):
1044 """Test that the image size can be set"""
Simon Glass511f6582018-10-01 12:22:30 -06001045 retcode = self._DoTestFile('017_pack_image_size.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001046 self.assertEqual(0, retcode)
1047 self.assertIn('image', control.images)
1048 image = control.images['image']
Simon Glass39dd2152019-07-08 14:25:47 -06001049 self.assertEqual(7, image.size)
Simon Glass57454f42016-11-25 20:15:52 -07001050
1051 def testPackImageSizeAlign(self):
1052 """Test that image size alignemnt works as expected"""
Simon Glass511f6582018-10-01 12:22:30 -06001053 retcode = self._DoTestFile('018_pack_image_align.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001054 self.assertEqual(0, retcode)
1055 self.assertIn('image', control.images)
1056 image = control.images['image']
Simon Glass39dd2152019-07-08 14:25:47 -06001057 self.assertEqual(16, image.size)
Simon Glass57454f42016-11-25 20:15:52 -07001058
1059 def testPackInvalidImageAlign(self):
1060 """Test that invalid image alignment is detected"""
1061 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001062 self._DoTestFile('019_pack_inv_image_align.dts')
Simon Glasseca32212018-06-01 09:38:12 -06001063 self.assertIn("Section '/binman': Size 0x7 (7) does not match "
Simon Glass57454f42016-11-25 20:15:52 -07001064 "align-size 0x8 (8)", str(e.exception))
1065
Simon Glass2a0fa982022-02-11 13:23:21 -07001066 def testPackAlignPowerOf2Inv(self):
Simon Glass57454f42016-11-25 20:15:52 -07001067 """Test that invalid image alignment is detected"""
1068 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001069 self._DoTestFile('020_pack_inv_image_align_power2.dts')
Simon Glass39dd2152019-07-08 14:25:47 -06001070 self.assertIn("Image '/binman': Alignment size 131 must be a power of "
Simon Glass57454f42016-11-25 20:15:52 -07001071 "two", str(e.exception))
1072
1073 def testImagePadByte(self):
1074 """Test that the image pad byte can be specified"""
Simon Glass7057d022018-10-01 21:12:47 -06001075 self._SetupSplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001076 data = self._DoReadFile('021_image_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07001077 self.assertEqual(U_BOOT_SPL_DATA + tools.get_bytes(0xff, 1) +
Simon Glassac0d4952019-05-14 15:53:47 -06001078 U_BOOT_DATA, data)
Simon Glass57454f42016-11-25 20:15:52 -07001079
1080 def testImageName(self):
1081 """Test that image files can be named"""
Simon Glass511f6582018-10-01 12:22:30 -06001082 retcode = self._DoTestFile('022_image_name.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001083 self.assertEqual(0, retcode)
1084 image = control.images['image1']
Simon Glass80025522022-01-29 14:14:04 -07001085 fname = tools.get_output_filename('test-name')
Simon Glass57454f42016-11-25 20:15:52 -07001086 self.assertTrue(os.path.exists(fname))
1087
1088 image = control.images['image2']
Simon Glass80025522022-01-29 14:14:04 -07001089 fname = tools.get_output_filename('test-name.xx')
Simon Glass57454f42016-11-25 20:15:52 -07001090 self.assertTrue(os.path.exists(fname))
1091
1092 def testBlobFilename(self):
1093 """Test that generic blobs can be provided by filename"""
Simon Glass511f6582018-10-01 12:22:30 -06001094 data = self._DoReadFile('023_blob.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001095 self.assertEqual(BLOB_DATA, data)
1096
1097 def testPackSorted(self):
1098 """Test that entries can be sorted"""
Simon Glass7057d022018-10-01 21:12:47 -06001099 self._SetupSplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001100 data = self._DoReadFile('024_sorted.dts')
Simon Glass80025522022-01-29 14:14:04 -07001101 self.assertEqual(tools.get_bytes(0, 1) + U_BOOT_SPL_DATA +
1102 tools.get_bytes(0, 2) + U_BOOT_DATA, data)
Simon Glass57454f42016-11-25 20:15:52 -07001103
Simon Glasse8561af2018-08-01 15:22:37 -06001104 def testPackZeroOffset(self):
1105 """Test that an entry at offset 0 is not given a new offset"""
Simon Glass57454f42016-11-25 20:15:52 -07001106 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001107 self._DoTestFile('025_pack_zero_size.dts')
Simon Glasse8561af2018-08-01 15:22:37 -06001108 self.assertIn("Node '/binman/u-boot-spl': Offset 0x0 (0) overlaps "
Simon Glass57454f42016-11-25 20:15:52 -07001109 "with previous entry '/binman/u-boot' ending at 0x4 (4)",
1110 str(e.exception))
1111
1112 def testPackUbootDtb(self):
1113 """Test that a device tree can be added to U-Boot"""
Simon Glass511f6582018-10-01 12:22:30 -06001114 data = self._DoReadFile('026_pack_u_boot_dtb.dts')
Simon Glass57454f42016-11-25 20:15:52 -07001115 self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA, data)
Simon Glass72232452016-11-25 20:15:53 -07001116
1117 def testPackX86RomNoSize(self):
1118 """Test that the end-at-4gb property requires a size property"""
1119 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001120 self._DoTestFile('027_pack_4gb_no_size.dts')
Simon Glass39dd2152019-07-08 14:25:47 -06001121 self.assertIn("Image '/binman': Section size must be provided when "
Simon Glass72232452016-11-25 20:15:53 -07001122 "using end-at-4gb", str(e.exception))
1123
Jagdish Gediya0fb978c2018-09-03 21:35:07 +05301124 def test4gbAndSkipAtStartTogether(self):
1125 """Test that the end-at-4gb and skip-at-size property can't be used
1126 together"""
1127 with self.assertRaises(ValueError) as e:
Simon Glass11f2bd02019-08-24 07:23:02 -06001128 self._DoTestFile('098_4gb_and_skip_at_start_together.dts')
Simon Glass39dd2152019-07-08 14:25:47 -06001129 self.assertIn("Image '/binman': Provide either 'end-at-4gb' or "
Jagdish Gediya0fb978c2018-09-03 21:35:07 +05301130 "'skip-at-start'", str(e.exception))
1131
Simon Glass72232452016-11-25 20:15:53 -07001132 def testPackX86RomOutside(self):
Simon Glasse8561af2018-08-01 15:22:37 -06001133 """Test that the end-at-4gb property checks for offset boundaries"""
Simon Glass72232452016-11-25 20:15:53 -07001134 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001135 self._DoTestFile('028_pack_4gb_outside.dts')
Simon Glassd6179862020-10-26 17:40:05 -06001136 self.assertIn("Node '/binman/u-boot': Offset 0x0 (0) size 0x4 (4) "
1137 "is outside the section '/binman' starting at "
1138 '0xffffffe0 (4294967264) of size 0x20 (32)',
Simon Glass72232452016-11-25 20:15:53 -07001139 str(e.exception))
1140
1141 def testPackX86Rom(self):
1142 """Test that a basic x86 ROM can be created"""
Simon Glass7057d022018-10-01 21:12:47 -06001143 self._SetupSplElf()
Simon Glass1d167762019-08-24 07:23:01 -06001144 data = self._DoReadFile('029_x86_rom.dts')
Simon Glass80025522022-01-29 14:14:04 -07001145 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 3) + U_BOOT_SPL_DATA +
1146 tools.get_bytes(0, 2), data)
Simon Glass72232452016-11-25 20:15:53 -07001147
1148 def testPackX86RomMeNoDesc(self):
1149 """Test that an invalid Intel descriptor entry is detected"""
Simon Glasse88cef92020-07-09 18:39:41 -06001150 try:
Simon Glass14c596c2020-07-25 15:11:19 -06001151 TestFunctional._MakeInputFile('descriptor-empty.bin', b'')
Simon Glasse88cef92020-07-09 18:39:41 -06001152 with self.assertRaises(ValueError) as e:
Simon Glass14c596c2020-07-25 15:11:19 -06001153 self._DoTestFile('163_x86_rom_me_empty.dts')
Simon Glasse88cef92020-07-09 18:39:41 -06001154 self.assertIn("Node '/binman/intel-descriptor': Cannot find Intel Flash Descriptor (FD) signature",
1155 str(e.exception))
1156 finally:
1157 self._SetupDescriptor()
Simon Glass72232452016-11-25 20:15:53 -07001158
1159 def testPackX86RomBadDesc(self):
1160 """Test that the Intel requires a descriptor entry"""
1161 with self.assertRaises(ValueError) as e:
Simon Glass1d167762019-08-24 07:23:01 -06001162 self._DoTestFile('030_x86_rom_me_no_desc.dts')
Simon Glasse8561af2018-08-01 15:22:37 -06001163 self.assertIn("Node '/binman/intel-me': No offset set with "
1164 "offset-unset: should another entry provide this correct "
1165 "offset?", str(e.exception))
Simon Glass72232452016-11-25 20:15:53 -07001166
1167 def testPackX86RomMe(self):
1168 """Test that an x86 ROM with an ME region can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001169 data = self._DoReadFile('031_x86_rom_me.dts')
Simon Glass80025522022-01-29 14:14:04 -07001170 expected_desc = tools.read_file(self.TestFile('descriptor.bin'))
Simon Glass759af872019-07-08 13:18:54 -06001171 if data[:0x1000] != expected_desc:
1172 self.fail('Expected descriptor binary at start of image')
Simon Glass72232452016-11-25 20:15:53 -07001173 self.assertEqual(ME_DATA, data[0x1000:0x1000 + len(ME_DATA)])
1174
1175 def testPackVga(self):
1176 """Test that an image with a VGA binary can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001177 data = self._DoReadFile('032_intel_vga.dts')
Simon Glass72232452016-11-25 20:15:53 -07001178 self.assertEqual(VGA_DATA, data[:len(VGA_DATA)])
1179
1180 def testPackStart16(self):
1181 """Test that an image with an x86 start16 region can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001182 data = self._DoReadFile('033_x86_start16.dts')
Simon Glass72232452016-11-25 20:15:53 -07001183 self.assertEqual(X86_START16_DATA, data[:len(X86_START16_DATA)])
1184
Jagdish Gediya311d4842018-09-03 21:35:08 +05301185 def testPackPowerpcMpc85xxBootpgResetvec(self):
1186 """Test that an image with powerpc-mpc85xx-bootpg-resetvec can be
1187 created"""
Simon Glass11f2bd02019-08-24 07:23:02 -06001188 data = self._DoReadFile('150_powerpc_mpc85xx_bootpg_resetvec.dts')
Jagdish Gediya311d4842018-09-03 21:35:08 +05301189 self.assertEqual(PPC_MPC85XX_BR_DATA, data[:len(PPC_MPC85XX_BR_DATA)])
1190
Simon Glass6ba679c2018-07-06 10:27:17 -06001191 def _RunMicrocodeTest(self, dts_fname, nodtb_data, ucode_second=False):
Simon Glass820af1d2018-07-06 10:27:16 -06001192 """Handle running a test for insertion of microcode
1193
1194 Args:
1195 dts_fname: Name of test .dts file
1196 nodtb_data: Data that we expect in the first section
Simon Glass6ba679c2018-07-06 10:27:17 -06001197 ucode_second: True if the microsecond entry is second instead of
1198 third
Simon Glass820af1d2018-07-06 10:27:16 -06001199
1200 Returns:
1201 Tuple:
1202 Contents of first region (U-Boot or SPL)
Simon Glasse8561af2018-08-01 15:22:37 -06001203 Offset and size components of microcode pointer, as inserted
Simon Glass820af1d2018-07-06 10:27:16 -06001204 in the above (two 4-byte words)
1205 """
Simon Glass3d274232017-11-12 21:52:27 -07001206 data = self._DoReadFile(dts_fname, True)
Simon Glass72232452016-11-25 20:15:53 -07001207
1208 # Now check the device tree has no microcode
Simon Glass6ba679c2018-07-06 10:27:17 -06001209 if ucode_second:
1210 ucode_content = data[len(nodtb_data):]
1211 ucode_pos = len(nodtb_data)
1212 dtb_with_ucode = ucode_content[16:]
1213 fdt_len = self.GetFdtLen(dtb_with_ucode)
1214 else:
1215 dtb_with_ucode = data[len(nodtb_data):]
1216 fdt_len = self.GetFdtLen(dtb_with_ucode)
1217 ucode_content = dtb_with_ucode[fdt_len:]
1218 ucode_pos = len(nodtb_data) + fdt_len
Simon Glass80025522022-01-29 14:14:04 -07001219 fname = tools.get_output_filename('test.dtb')
Simon Glass72232452016-11-25 20:15:53 -07001220 with open(fname, 'wb') as fd:
Simon Glass820af1d2018-07-06 10:27:16 -06001221 fd.write(dtb_with_ucode)
Simon Glass22c92ca2017-05-27 07:38:29 -06001222 dtb = fdt.FdtScan(fname)
1223 ucode = dtb.GetNode('/microcode')
Simon Glass72232452016-11-25 20:15:53 -07001224 self.assertTrue(ucode)
1225 for node in ucode.subnodes:
1226 self.assertFalse(node.props.get('data'))
1227
Simon Glass72232452016-11-25 20:15:53 -07001228 # Check that the microcode appears immediately after the Fdt
1229 # This matches the concatenation of the data properties in
Simon Glasse83679d2017-11-12 21:52:26 -07001230 # the /microcode/update@xxx nodes in 34_x86_ucode.dts.
Simon Glass72232452016-11-25 20:15:53 -07001231 ucode_data = struct.pack('>4L', 0x12345678, 0x12345679, 0xabcd0000,
1232 0x78235609)
Simon Glass820af1d2018-07-06 10:27:16 -06001233 self.assertEqual(ucode_data, ucode_content[:len(ucode_data)])
Simon Glass72232452016-11-25 20:15:53 -07001234
1235 # Check that the microcode pointer was inserted. It should match the
Simon Glasse8561af2018-08-01 15:22:37 -06001236 # expected offset and size
Simon Glass72232452016-11-25 20:15:53 -07001237 pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
1238 len(ucode_data))
Simon Glass6ba679c2018-07-06 10:27:17 -06001239 u_boot = data[:len(nodtb_data)]
1240 return u_boot, pos_and_size
Simon Glass3d274232017-11-12 21:52:27 -07001241
1242 def testPackUbootMicrocode(self):
1243 """Test that x86 microcode can be handled correctly
1244
1245 We expect to see the following in the image, in order:
1246 u-boot-nodtb.bin with a microcode pointer inserted at the correct
1247 place
1248 u-boot.dtb with the microcode removed
1249 the microcode
1250 """
Simon Glass511f6582018-10-01 12:22:30 -06001251 first, pos_and_size = self._RunMicrocodeTest('034_x86_ucode.dts',
Simon Glass3d274232017-11-12 21:52:27 -07001252 U_BOOT_NODTB_DATA)
Simon Glass303f62f2019-05-17 22:00:46 -06001253 self.assertEqual(b'nodtb with microcode' + pos_and_size +
1254 b' somewhere in here', first)
Simon Glass72232452016-11-25 20:15:53 -07001255
Simon Glassbac25c82017-05-27 07:38:26 -06001256 def _RunPackUbootSingleMicrocode(self):
Simon Glass72232452016-11-25 20:15:53 -07001257 """Test that x86 microcode can be handled correctly
1258
1259 We expect to see the following in the image, in order:
1260 u-boot-nodtb.bin with a microcode pointer inserted at the correct
1261 place
1262 u-boot.dtb with the microcode
1263 an empty microcode region
1264 """
1265 # We need the libfdt library to run this test since only that allows
1266 # finding the offset of a property. This is required by
1267 # Entry_u_boot_dtb_with_ucode.ObtainContents().
Simon Glass511f6582018-10-01 12:22:30 -06001268 data = self._DoReadFile('035_x86_single_ucode.dts', True)
Simon Glass72232452016-11-25 20:15:53 -07001269
1270 second = data[len(U_BOOT_NODTB_DATA):]
1271
1272 fdt_len = self.GetFdtLen(second)
1273 third = second[fdt_len:]
1274 second = second[:fdt_len]
1275
Simon Glassbac25c82017-05-27 07:38:26 -06001276 ucode_data = struct.pack('>2L', 0x12345678, 0x12345679)
1277 self.assertIn(ucode_data, second)
1278 ucode_pos = second.find(ucode_data) + len(U_BOOT_NODTB_DATA)
Simon Glass72232452016-11-25 20:15:53 -07001279
Simon Glassbac25c82017-05-27 07:38:26 -06001280 # Check that the microcode pointer was inserted. It should match the
Simon Glasse8561af2018-08-01 15:22:37 -06001281 # expected offset and size
Simon Glassbac25c82017-05-27 07:38:26 -06001282 pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
1283 len(ucode_data))
1284 first = data[:len(U_BOOT_NODTB_DATA)]
Simon Glass303f62f2019-05-17 22:00:46 -06001285 self.assertEqual(b'nodtb with microcode' + pos_and_size +
1286 b' somewhere in here', first)
Simon Glass996021e2016-11-25 20:15:54 -07001287
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001288 def testPackUbootSingleMicrocode(self):
1289 """Test that x86 microcode can be handled correctly with fdt_normal.
1290 """
Simon Glassbac25c82017-05-27 07:38:26 -06001291 self._RunPackUbootSingleMicrocode()
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001292
Simon Glass996021e2016-11-25 20:15:54 -07001293 def testUBootImg(self):
1294 """Test that u-boot.img can be put in a file"""
Simon Glass511f6582018-10-01 12:22:30 -06001295 data = self._DoReadFile('036_u_boot_img.dts')
Simon Glass996021e2016-11-25 20:15:54 -07001296 self.assertEqual(U_BOOT_IMG_DATA, data)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001297
1298 def testNoMicrocode(self):
1299 """Test that a missing microcode region is detected"""
1300 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001301 self._DoReadFile('037_x86_no_ucode.dts', True)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001302 self.assertIn("Node '/binman/u-boot-dtb-with-ucode': No /microcode "
1303 "node found in ", str(e.exception))
1304
1305 def testMicrocodeWithoutNode(self):
1306 """Test that a missing u-boot-dtb-with-ucode node is detected"""
1307 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001308 self._DoReadFile('038_x86_ucode_missing_node.dts', True)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001309 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
1310 "microcode region u-boot-dtb-with-ucode", str(e.exception))
1311
1312 def testMicrocodeWithoutNode2(self):
1313 """Test that a missing u-boot-ucode node is detected"""
1314 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001315 self._DoReadFile('039_x86_ucode_missing_node2.dts', True)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001316 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
1317 "microcode region u-boot-ucode", str(e.exception))
1318
1319 def testMicrocodeWithoutPtrInElf(self):
1320 """Test that a U-Boot binary without the microcode symbol is detected"""
1321 # ELF file without a '_dt_ucode_base_size' symbol
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001322 try:
Simon Glassfaaaa162019-08-24 07:22:55 -06001323 TestFunctional._MakeInputFile('u-boot',
Simon Glass80025522022-01-29 14:14:04 -07001324 tools.read_file(self.ElfTestFile('u_boot_no_ucode_ptr')))
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001325
1326 with self.assertRaises(ValueError) as e:
Simon Glassbac25c82017-05-27 07:38:26 -06001327 self._RunPackUbootSingleMicrocode()
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001328 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot locate "
1329 "_dt_ucode_base_size symbol in u-boot", str(e.exception))
1330
1331 finally:
1332 # Put the original file back
Simon Glass4affd4b2019-08-24 07:22:54 -06001333 TestFunctional._MakeInputFile('u-boot',
Simon Glass80025522022-01-29 14:14:04 -07001334 tools.read_file(self.ElfTestFile('u_boot_ucode_ptr')))
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001335
1336 def testMicrocodeNotInImage(self):
1337 """Test that microcode must be placed within the image"""
1338 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001339 self._DoReadFile('040_x86_ucode_not_in_image.dts', True)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001340 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Microcode "
1341 "pointer _dt_ucode_base_size at fffffe14 is outside the "
Simon Glassad5a7712018-06-01 09:38:14 -06001342 "section ranging from 00000000 to 0000002e", str(e.exception))
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001343
1344 def testWithoutMicrocode(self):
1345 """Test that we can cope with an image without microcode (e.g. qemu)"""
Simon Glassfaaaa162019-08-24 07:22:55 -06001346 TestFunctional._MakeInputFile('u-boot',
Simon Glass80025522022-01-29 14:14:04 -07001347 tools.read_file(self.ElfTestFile('u_boot_no_ucode_ptr')))
Simon Glass511f6582018-10-01 12:22:30 -06001348 data, dtb, _, _ = self._DoReadFileDtb('044_x86_optional_ucode.dts', True)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001349
1350 # Now check the device tree has no microcode
1351 self.assertEqual(U_BOOT_NODTB_DATA, data[:len(U_BOOT_NODTB_DATA)])
1352 second = data[len(U_BOOT_NODTB_DATA):]
1353
1354 fdt_len = self.GetFdtLen(second)
1355 self.assertEqual(dtb, second[:fdt_len])
1356
1357 used_len = len(U_BOOT_NODTB_DATA) + fdt_len
1358 third = data[used_len:]
Simon Glass80025522022-01-29 14:14:04 -07001359 self.assertEqual(tools.get_bytes(0, 0x200 - used_len), third)
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001360
1361 def testUnknownPosSize(self):
1362 """Test that microcode must be placed within the image"""
1363 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001364 self._DoReadFile('041_unknown_pos_size.dts', True)
Simon Glasse8561af2018-08-01 15:22:37 -06001365 self.assertIn("Section '/binman': Unable to set offset/size for unknown "
Simon Glassd2dfb5f2016-11-25 20:15:55 -07001366 "entry 'invalid-entry'", str(e.exception))
Simon Glassb4176d42016-11-25 20:15:56 -07001367
1368 def testPackFsp(self):
1369 """Test that an image with a FSP binary can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001370 data = self._DoReadFile('042_intel_fsp.dts')
Simon Glassb4176d42016-11-25 20:15:56 -07001371 self.assertEqual(FSP_DATA, data[:len(FSP_DATA)])
1372
1373 def testPackCmc(self):
Bin Mengd7bcdf52017-08-15 22:41:54 -07001374 """Test that an image with a CMC binary can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001375 data = self._DoReadFile('043_intel_cmc.dts')
Simon Glassb4176d42016-11-25 20:15:56 -07001376 self.assertEqual(CMC_DATA, data[:len(CMC_DATA)])
Bin Mengd7bcdf52017-08-15 22:41:54 -07001377
1378 def testPackVbt(self):
1379 """Test that an image with a VBT binary can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001380 data = self._DoReadFile('046_intel_vbt.dts')
Bin Mengd7bcdf52017-08-15 22:41:54 -07001381 self.assertEqual(VBT_DATA, data[:len(VBT_DATA)])
Simon Glassac599912017-11-12 21:52:22 -07001382
Simon Glass7f94e832017-11-12 21:52:25 -07001383 def testSplBssPad(self):
1384 """Test that we can pad SPL's BSS with zeros"""
Simon Glass3d274232017-11-12 21:52:27 -07001385 # ELF file with a '__bss_size' symbol
Simon Glass7057d022018-10-01 21:12:47 -06001386 self._SetupSplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001387 data = self._DoReadFile('047_spl_bss_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07001388 self.assertEqual(U_BOOT_SPL_DATA + tools.get_bytes(0, 10) + U_BOOT_DATA,
Simon Glassac0d4952019-05-14 15:53:47 -06001389 data)
Simon Glass7f94e832017-11-12 21:52:25 -07001390
Simon Glass04cda032018-10-01 21:12:42 -06001391 def testSplBssPadMissing(self):
1392 """Test that a missing symbol is detected"""
Simon Glass7057d022018-10-01 21:12:47 -06001393 self._SetupSplElf('u_boot_ucode_ptr')
Simon Glass24ad3652017-11-13 18:54:54 -07001394 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001395 self._DoReadFile('047_spl_bss_pad.dts')
Simon Glass24ad3652017-11-13 18:54:54 -07001396 self.assertIn('Expected __bss_size symbol in spl/u-boot-spl',
1397 str(e.exception))
1398
Simon Glasse83679d2017-11-12 21:52:26 -07001399 def testPackStart16Spl(self):
Simon Glassed40e962018-09-14 04:57:10 -06001400 """Test that an image with an x86 start16 SPL region can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001401 data = self._DoReadFile('048_x86_start16_spl.dts')
Simon Glasse83679d2017-11-12 21:52:26 -07001402 self.assertEqual(X86_START16_SPL_DATA, data[:len(X86_START16_SPL_DATA)])
1403
Simon Glass6ba679c2018-07-06 10:27:17 -06001404 def _PackUbootSplMicrocode(self, dts, ucode_second=False):
1405 """Helper function for microcode tests
Simon Glass3d274232017-11-12 21:52:27 -07001406
1407 We expect to see the following in the image, in order:
1408 u-boot-spl-nodtb.bin with a microcode pointer inserted at the
1409 correct place
1410 u-boot.dtb with the microcode removed
1411 the microcode
Simon Glass6ba679c2018-07-06 10:27:17 -06001412
1413 Args:
1414 dts: Device tree file to use for test
1415 ucode_second: True if the microsecond entry is second instead of
1416 third
Simon Glass3d274232017-11-12 21:52:27 -07001417 """
Simon Glass7057d022018-10-01 21:12:47 -06001418 self._SetupSplElf('u_boot_ucode_ptr')
Simon Glass6ba679c2018-07-06 10:27:17 -06001419 first, pos_and_size = self._RunMicrocodeTest(dts, U_BOOT_SPL_NODTB_DATA,
1420 ucode_second=ucode_second)
Simon Glass303f62f2019-05-17 22:00:46 -06001421 self.assertEqual(b'splnodtb with microc' + pos_and_size +
1422 b'ter somewhere in here', first)
Simon Glass3d274232017-11-12 21:52:27 -07001423
Simon Glass6ba679c2018-07-06 10:27:17 -06001424 def testPackUbootSplMicrocode(self):
1425 """Test that x86 microcode can be handled correctly in SPL"""
Simon Glass511f6582018-10-01 12:22:30 -06001426 self._PackUbootSplMicrocode('049_x86_ucode_spl.dts')
Simon Glass6ba679c2018-07-06 10:27:17 -06001427
1428 def testPackUbootSplMicrocodeReorder(self):
1429 """Test that order doesn't matter for microcode entries
1430
1431 This is the same as testPackUbootSplMicrocode but when we process the
1432 u-boot-ucode entry we have not yet seen the u-boot-dtb-with-ucode
1433 entry, so we reply on binman to try later.
1434 """
Simon Glass511f6582018-10-01 12:22:30 -06001435 self._PackUbootSplMicrocode('058_x86_ucode_spl_needs_retry.dts',
Simon Glass6ba679c2018-07-06 10:27:17 -06001436 ucode_second=True)
1437
Simon Glassa409c932017-11-12 21:52:28 -07001438 def testPackMrc(self):
1439 """Test that an image with an MRC binary can be created"""
Simon Glass511f6582018-10-01 12:22:30 -06001440 data = self._DoReadFile('050_intel_mrc.dts')
Simon Glassa409c932017-11-12 21:52:28 -07001441 self.assertEqual(MRC_DATA, data[:len(MRC_DATA)])
1442
Simon Glass9aa6a6f2017-11-13 18:54:55 -07001443 def testSplDtb(self):
1444 """Test that an image with spl/u-boot-spl.dtb can be created"""
Simon Glass511f6582018-10-01 12:22:30 -06001445 data = self._DoReadFile('051_u_boot_spl_dtb.dts')
Simon Glass9aa6a6f2017-11-13 18:54:55 -07001446 self.assertEqual(U_BOOT_SPL_DTB_DATA, data[:len(U_BOOT_SPL_DTB_DATA)])
1447
Simon Glass0a6da312017-11-13 18:54:56 -07001448 def testSplNoDtb(self):
1449 """Test that an image with spl/u-boot-spl-nodtb.bin can be created"""
Simon Glass13089cc2021-04-25 08:39:32 +12001450 self._SetupSplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001451 data = self._DoReadFile('052_u_boot_spl_nodtb.dts')
Simon Glass0a6da312017-11-13 18:54:56 -07001452 self.assertEqual(U_BOOT_SPL_NODTB_DATA, data[:len(U_BOOT_SPL_NODTB_DATA)])
1453
Simon Glass7098b7f2021-03-21 18:24:30 +13001454 def checkSymbols(self, dts, base_data, u_boot_offset, entry_args=None,
1455 use_expanded=False):
Simon Glass31e04cb2021-03-18 20:24:56 +13001456 """Check the image contains the expected symbol values
1457
1458 Args:
1459 dts: Device tree file to use for test
1460 base_data: Data before and after 'u-boot' section
1461 u_boot_offset: Offset of 'u-boot' section in image
Simon Glass7098b7f2021-03-21 18:24:30 +13001462 entry_args: Dict of entry args to supply to binman
1463 key: arg name
1464 value: value of that arg
1465 use_expanded: True to use expanded entries where available, e.g.
1466 'u-boot-expanded' instead of 'u-boot'
Simon Glass31e04cb2021-03-18 20:24:56 +13001467 """
Simon Glass5d0c0262019-08-24 07:22:56 -06001468 elf_fname = self.ElfTestFile('u_boot_binman_syms')
Simon Glass4ca8e042017-11-13 18:55:01 -07001469 syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
1470 addr = elf.GetSymbolAddress(elf_fname, '__image_copy_start')
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03001471 self.assertEqual(syms['_binman_sym_magic'].address, addr)
Simon Glass31e04cb2021-03-18 20:24:56 +13001472 self.assertEqual(syms['_binman_u_boot_spl_any_prop_offset'].address,
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03001473 addr + 4)
Simon Glass4ca8e042017-11-13 18:55:01 -07001474
Simon Glass7057d022018-10-01 21:12:47 -06001475 self._SetupSplElf('u_boot_binman_syms')
Simon Glass7098b7f2021-03-21 18:24:30 +13001476 data = self._DoReadFileDtb(dts, entry_args=entry_args,
1477 use_expanded=use_expanded)[0]
Simon Glass31e04cb2021-03-18 20:24:56 +13001478 # The image should contain the symbols from u_boot_binman_syms.c
1479 # Note that image_pos is adjusted by the base address of the image,
1480 # which is 0x10 in our test image
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03001481 sym_values = struct.pack('<LLQLL', elf.BINMAN_SYM_MAGIC_VALUE,
1482 0x00, u_boot_offset + len(U_BOOT_DATA),
Simon Glass31e04cb2021-03-18 20:24:56 +13001483 0x10 + u_boot_offset, 0x04)
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03001484 expected = (sym_values + base_data[24:] +
Simon Glass80025522022-01-29 14:14:04 -07001485 tools.get_bytes(0xff, 1) + U_BOOT_DATA + sym_values +
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03001486 base_data[24:])
Simon Glass4ca8e042017-11-13 18:55:01 -07001487 self.assertEqual(expected, data)
1488
Simon Glass31e04cb2021-03-18 20:24:56 +13001489 def testSymbols(self):
1490 """Test binman can assign symbols embedded in U-Boot"""
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03001491 self.checkSymbols('053_symbols.dts', U_BOOT_SPL_DATA, 0x1c)
Simon Glass31e04cb2021-03-18 20:24:56 +13001492
1493 def testSymbolsNoDtb(self):
1494 """Test binman can assign symbols embedded in U-Boot SPL"""
Simon Glass3bbc9932021-03-21 18:24:29 +13001495 self.checkSymbols('196_symbols_nodtb.dts',
Simon Glass31e04cb2021-03-18 20:24:56 +13001496 U_BOOT_SPL_NODTB_DATA + U_BOOT_SPL_DTB_DATA,
1497 0x38)
1498
Simon Glasse76a3e62018-06-01 09:38:11 -06001499 def testPackUnitAddress(self):
1500 """Test that we support multiple binaries with the same name"""
Simon Glass511f6582018-10-01 12:22:30 -06001501 data = self._DoReadFile('054_unit_address.dts')
Simon Glasse76a3e62018-06-01 09:38:11 -06001502 self.assertEqual(U_BOOT_DATA + U_BOOT_DATA, data)
1503
Simon Glassa91e1152018-06-01 09:38:16 -06001504 def testSections(self):
1505 """Basic test of sections"""
Simon Glass511f6582018-10-01 12:22:30 -06001506 data = self._DoReadFile('055_sections.dts')
Simon Glass80025522022-01-29 14:14:04 -07001507 expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) +
1508 U_BOOT_DATA + tools.get_bytes(ord('a'), 12) +
1509 U_BOOT_DATA + tools.get_bytes(ord('&'), 4))
Simon Glassa91e1152018-06-01 09:38:16 -06001510 self.assertEqual(expected, data)
Simon Glassac599912017-11-12 21:52:22 -07001511
Simon Glass30732662018-06-01 09:38:20 -06001512 def testMap(self):
1513 """Tests outputting a map of the images"""
Simon Glass511f6582018-10-01 12:22:30 -06001514 _, _, map_data, _ = self._DoReadFileDtb('055_sections.dts', map=True)
Simon Glass7eca7922018-07-17 13:25:49 -06001515 self.assertEqual('''ImagePos Offset Size Name
Simon Glass49cd2b32023-02-07 14:34:18 -0700151600000000 00000000 00000028 image
Simon Glass7eca7922018-07-17 13:25:49 -0600151700000000 00000000 00000010 section@0
151800000000 00000000 00000004 u-boot
151900000010 00000010 00000010 section@1
152000000010 00000000 00000004 u-boot
152100000020 00000020 00000004 section@2
152200000020 00000000 00000004 u-boot
Simon Glass30732662018-06-01 09:38:20 -06001523''', map_data)
1524
Simon Glass3b78d532018-06-01 09:38:21 -06001525 def testNamePrefix(self):
1526 """Tests that name prefixes are used"""
Simon Glass511f6582018-10-01 12:22:30 -06001527 _, _, map_data, _ = self._DoReadFileDtb('056_name_prefix.dts', map=True)
Simon Glass7eca7922018-07-17 13:25:49 -06001528 self.assertEqual('''ImagePos Offset Size Name
Simon Glass49cd2b32023-02-07 14:34:18 -0700152900000000 00000000 00000028 image
Simon Glass7eca7922018-07-17 13:25:49 -0600153000000000 00000000 00000010 section@0
153100000000 00000000 00000004 ro-u-boot
153200000010 00000010 00000010 section@1
153300000010 00000000 00000004 rw-u-boot
Simon Glass3b78d532018-06-01 09:38:21 -06001534''', map_data)
1535
Simon Glass6ba679c2018-07-06 10:27:17 -06001536 def testUnknownContents(self):
1537 """Test that obtaining the contents works as expected"""
1538 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001539 self._DoReadFile('057_unknown_contents.dts', True)
Simon Glass39dd2152019-07-08 14:25:47 -06001540 self.assertIn("Image '/binman': Internal error: Could not complete "
Simon Glassc585dd42020-04-17 18:09:03 -06001541 "processing of contents: remaining ["
1542 "<binman.etype._testing.Entry__testing ", str(e.exception))
Simon Glass6ba679c2018-07-06 10:27:17 -06001543
Simon Glass2e1169f2018-07-06 10:27:19 -06001544 def testBadChangeSize(self):
1545 """Test that trying to change the size of an entry fails"""
Simon Glasse61b6f62019-07-08 14:25:37 -06001546 try:
1547 state.SetAllowEntryExpansion(False)
1548 with self.assertRaises(ValueError) as e:
1549 self._DoReadFile('059_change_size.dts', True)
Simon Glass8c702fb2019-07-20 12:23:57 -06001550 self.assertIn("Node '/binman/_testing': Cannot update entry size from 2 to 3",
Simon Glasse61b6f62019-07-08 14:25:37 -06001551 str(e.exception))
1552 finally:
1553 state.SetAllowEntryExpansion(True)
Simon Glass2e1169f2018-07-06 10:27:19 -06001554
Simon Glassa87014e2018-07-06 10:27:42 -06001555 def testUpdateFdt(self):
Simon Glasse8561af2018-08-01 15:22:37 -06001556 """Test that we can update the device tree with offset/size info"""
Simon Glass511f6582018-10-01 12:22:30 -06001557 _, _, _, out_dtb_fname = self._DoReadFileDtb('060_fdt_update.dts',
Simon Glassa87014e2018-07-06 10:27:42 -06001558 update_dtb=True)
Simon Glass5463a6a2018-07-17 13:25:52 -06001559 dtb = fdt.Fdt(out_dtb_fname)
1560 dtb.Scan()
Simon Glassfb30e292019-07-20 12:23:51 -06001561 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
Simon Glassa87014e2018-07-06 10:27:42 -06001562 self.assertEqual({
Simon Glass9dcc8612018-08-01 15:22:42 -06001563 'image-pos': 0,
Simon Glass3a9a2b82018-07-17 13:25:28 -06001564 'offset': 0,
Simon Glasse8561af2018-08-01 15:22:37 -06001565 '_testing:offset': 32,
Simon Glass8c702fb2019-07-20 12:23:57 -06001566 '_testing:size': 2,
Simon Glass9dcc8612018-08-01 15:22:42 -06001567 '_testing:image-pos': 32,
Simon Glasse8561af2018-08-01 15:22:37 -06001568 'section@0/u-boot:offset': 0,
Simon Glassa87014e2018-07-06 10:27:42 -06001569 'section@0/u-boot:size': len(U_BOOT_DATA),
Simon Glass9dcc8612018-08-01 15:22:42 -06001570 'section@0/u-boot:image-pos': 0,
Simon Glasse8561af2018-08-01 15:22:37 -06001571 'section@0:offset': 0,
Simon Glassa87014e2018-07-06 10:27:42 -06001572 'section@0:size': 16,
Simon Glass9dcc8612018-08-01 15:22:42 -06001573 'section@0:image-pos': 0,
Simon Glassa87014e2018-07-06 10:27:42 -06001574
Simon Glasse8561af2018-08-01 15:22:37 -06001575 'section@1/u-boot:offset': 0,
Simon Glassa87014e2018-07-06 10:27:42 -06001576 'section@1/u-boot:size': len(U_BOOT_DATA),
Simon Glass9dcc8612018-08-01 15:22:42 -06001577 'section@1/u-boot:image-pos': 16,
Simon Glasse8561af2018-08-01 15:22:37 -06001578 'section@1:offset': 16,
Simon Glassa87014e2018-07-06 10:27:42 -06001579 'section@1:size': 16,
Simon Glass9dcc8612018-08-01 15:22:42 -06001580 'section@1:image-pos': 16,
Simon Glassa87014e2018-07-06 10:27:42 -06001581 'size': 40
1582 }, props)
1583
1584 def testUpdateFdtBad(self):
1585 """Test that we detect when ProcessFdt never completes"""
1586 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001587 self._DoReadFileDtb('061_fdt_update_bad.dts', update_dtb=True)
Simon Glassa87014e2018-07-06 10:27:42 -06001588 self.assertIn('Could not complete processing of Fdt: remaining '
Simon Glassc585dd42020-04-17 18:09:03 -06001589 '[<binman.etype._testing.Entry__testing',
1590 str(e.exception))
Simon Glass2e1169f2018-07-06 10:27:19 -06001591
Simon Glass91710b32018-07-17 13:25:32 -06001592 def testEntryArgs(self):
1593 """Test passing arguments to entries from the command line"""
1594 entry_args = {
1595 'test-str-arg': 'test1',
1596 'test-int-arg': '456',
1597 }
Simon Glass511f6582018-10-01 12:22:30 -06001598 self._DoReadFileDtb('062_entry_args.dts', entry_args=entry_args)
Simon Glass91710b32018-07-17 13:25:32 -06001599 self.assertIn('image', control.images)
1600 entry = control.images['image'].GetEntries()['_testing']
1601 self.assertEqual('test0', entry.test_str_fdt)
1602 self.assertEqual('test1', entry.test_str_arg)
1603 self.assertEqual(123, entry.test_int_fdt)
1604 self.assertEqual(456, entry.test_int_arg)
1605
1606 def testEntryArgsMissing(self):
1607 """Test missing arguments and properties"""
1608 entry_args = {
1609 'test-int-arg': '456',
1610 }
Simon Glass511f6582018-10-01 12:22:30 -06001611 self._DoReadFileDtb('063_entry_args_missing.dts', entry_args=entry_args)
Simon Glass91710b32018-07-17 13:25:32 -06001612 entry = control.images['image'].GetEntries()['_testing']
1613 self.assertEqual('test0', entry.test_str_fdt)
1614 self.assertEqual(None, entry.test_str_arg)
1615 self.assertEqual(None, entry.test_int_fdt)
1616 self.assertEqual(456, entry.test_int_arg)
1617
1618 def testEntryArgsRequired(self):
1619 """Test missing arguments and properties"""
1620 entry_args = {
1621 'test-int-arg': '456',
1622 }
1623 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001624 self._DoReadFileDtb('064_entry_args_required.dts')
Simon Glass21db0ff2020-09-01 05:13:54 -06001625 self.assertIn("Node '/binman/_testing': "
1626 'Missing required properties/entry args: test-str-arg, '
1627 'test-int-fdt, test-int-arg',
Simon Glass91710b32018-07-17 13:25:32 -06001628 str(e.exception))
1629
1630 def testEntryArgsInvalidFormat(self):
1631 """Test that an invalid entry-argument format is detected"""
Simon Glassf46732a2019-07-08 14:25:29 -06001632 args = ['build', '-d', self.TestFile('064_entry_args_required.dts'),
1633 '-ano-value']
Simon Glass91710b32018-07-17 13:25:32 -06001634 with self.assertRaises(ValueError) as e:
1635 self._DoBinman(*args)
1636 self.assertIn("Invalid entry arguemnt 'no-value'", str(e.exception))
1637
1638 def testEntryArgsInvalidInteger(self):
1639 """Test that an invalid entry-argument integer is detected"""
1640 entry_args = {
1641 'test-int-arg': 'abc',
1642 }
1643 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001644 self._DoReadFileDtb('062_entry_args.dts', entry_args=entry_args)
Simon Glass91710b32018-07-17 13:25:32 -06001645 self.assertIn("Node '/binman/_testing': Cannot convert entry arg "
1646 "'test-int-arg' (value 'abc') to integer",
1647 str(e.exception))
1648
1649 def testEntryArgsInvalidDatatype(self):
1650 """Test that an invalid entry-argument datatype is detected
1651
1652 This test could be written in entry_test.py except that it needs
1653 access to control.entry_args, which seems more than that module should
1654 be able to see.
1655 """
1656 entry_args = {
1657 'test-bad-datatype-arg': '12',
1658 }
1659 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001660 self._DoReadFileDtb('065_entry_args_unknown_datatype.dts',
Simon Glass91710b32018-07-17 13:25:32 -06001661 entry_args=entry_args)
1662 self.assertIn('GetArg() internal error: Unknown data type ',
1663 str(e.exception))
1664
Simon Glass2ca52032018-07-17 13:25:33 -06001665 def testText(self):
1666 """Test for a text entry type"""
1667 entry_args = {
1668 'test-id': TEXT_DATA,
1669 'test-id2': TEXT_DATA2,
1670 'test-id3': TEXT_DATA3,
1671 }
Simon Glass511f6582018-10-01 12:22:30 -06001672 data, _, _, _ = self._DoReadFileDtb('066_text.dts',
Simon Glass2ca52032018-07-17 13:25:33 -06001673 entry_args=entry_args)
Simon Glass80025522022-01-29 14:14:04 -07001674 expected = (tools.to_bytes(TEXT_DATA) +
1675 tools.get_bytes(0, 8 - len(TEXT_DATA)) +
1676 tools.to_bytes(TEXT_DATA2) + tools.to_bytes(TEXT_DATA3) +
Simon Glass47f6a622019-07-08 13:18:40 -06001677 b'some text' + b'more text')
Simon Glass2ca52032018-07-17 13:25:33 -06001678 self.assertEqual(expected, data)
1679
Simon Glass969616c2018-07-17 13:25:36 -06001680 def testEntryDocs(self):
1681 """Test for creation of entry documentation"""
1682 with test_util.capture_sys_output() as (stdout, stderr):
Simon Glass220ff5f2020-08-05 13:27:46 -06001683 control.WriteEntryDocs(control.GetEntryModules())
Simon Glass969616c2018-07-17 13:25:36 -06001684 self.assertTrue(len(stdout.getvalue()) > 0)
1685
1686 def testEntryDocsMissing(self):
1687 """Test handling of missing entry documentation"""
1688 with self.assertRaises(ValueError) as e:
1689 with test_util.capture_sys_output() as (stdout, stderr):
Simon Glass220ff5f2020-08-05 13:27:46 -06001690 control.WriteEntryDocs(control.GetEntryModules(), 'u_boot')
Simon Glass969616c2018-07-17 13:25:36 -06001691 self.assertIn('Documentation is missing for modules: u_boot',
1692 str(e.exception))
1693
Simon Glass704784b2018-07-17 13:25:38 -06001694 def testFmap(self):
1695 """Basic test of generation of a flashrom fmap"""
Simon Glass511f6582018-10-01 12:22:30 -06001696 data = self._DoReadFile('067_fmap.dts')
Simon Glass704784b2018-07-17 13:25:38 -06001697 fhdr, fentries = fmap_util.DecodeFmap(data[32:])
Simon Glass80025522022-01-29 14:14:04 -07001698 expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) +
1699 U_BOOT_DATA + tools.get_bytes(ord('a'), 12))
Simon Glass704784b2018-07-17 13:25:38 -06001700 self.assertEqual(expected, data[:32])
Simon Glass303f62f2019-05-17 22:00:46 -06001701 self.assertEqual(b'__FMAP__', fhdr.signature)
Simon Glass704784b2018-07-17 13:25:38 -06001702 self.assertEqual(1, fhdr.ver_major)
1703 self.assertEqual(0, fhdr.ver_minor)
1704 self.assertEqual(0, fhdr.base)
Simon Glassb1d414c2021-04-03 11:05:10 +13001705 expect_size = fmap_util.FMAP_HEADER_LEN + fmap_util.FMAP_AREA_LEN * 5
Simon Glass82059c22021-04-03 11:05:09 +13001706 self.assertEqual(16 + 16 + expect_size, fhdr.image_size)
Simon Glass303f62f2019-05-17 22:00:46 -06001707 self.assertEqual(b'FMAP', fhdr.name)
Simon Glassb1d414c2021-04-03 11:05:10 +13001708 self.assertEqual(5, fhdr.nareas)
Simon Glass82059c22021-04-03 11:05:09 +13001709 fiter = iter(fentries)
Simon Glass704784b2018-07-17 13:25:38 -06001710
Simon Glass82059c22021-04-03 11:05:09 +13001711 fentry = next(fiter)
Simon Glassb1d414c2021-04-03 11:05:10 +13001712 self.assertEqual(b'SECTION0', fentry.name)
1713 self.assertEqual(0, fentry.offset)
1714 self.assertEqual(16, fentry.size)
Simon Glasscda991e2023-02-12 17:11:15 -07001715 self.assertEqual(fmap_util.FMAP_AREA_PRESERVE, fentry.flags)
Simon Glassb1d414c2021-04-03 11:05:10 +13001716
1717 fentry = next(fiter)
Simon Glass82059c22021-04-03 11:05:09 +13001718 self.assertEqual(b'RO_U_BOOT', fentry.name)
1719 self.assertEqual(0, fentry.offset)
1720 self.assertEqual(4, fentry.size)
1721 self.assertEqual(0, fentry.flags)
Simon Glass704784b2018-07-17 13:25:38 -06001722
Simon Glass82059c22021-04-03 11:05:09 +13001723 fentry = next(fiter)
Simon Glassb1d414c2021-04-03 11:05:10 +13001724 self.assertEqual(b'SECTION1', fentry.name)
1725 self.assertEqual(16, fentry.offset)
1726 self.assertEqual(16, fentry.size)
1727 self.assertEqual(0, fentry.flags)
1728
1729 fentry = next(fiter)
Simon Glass82059c22021-04-03 11:05:09 +13001730 self.assertEqual(b'RW_U_BOOT', fentry.name)
1731 self.assertEqual(16, fentry.offset)
1732 self.assertEqual(4, fentry.size)
1733 self.assertEqual(0, fentry.flags)
Simon Glass704784b2018-07-17 13:25:38 -06001734
Simon Glass82059c22021-04-03 11:05:09 +13001735 fentry = next(fiter)
1736 self.assertEqual(b'FMAP', fentry.name)
1737 self.assertEqual(32, fentry.offset)
1738 self.assertEqual(expect_size, fentry.size)
1739 self.assertEqual(0, fentry.flags)
Simon Glass704784b2018-07-17 13:25:38 -06001740
Simon Glassdb168d42018-07-17 13:25:39 -06001741 def testBlobNamedByArg(self):
1742 """Test we can add a blob with the filename coming from an entry arg"""
1743 entry_args = {
1744 'cros-ec-rw-path': 'ecrw.bin',
1745 }
Simon Glass21db0ff2020-09-01 05:13:54 -06001746 self._DoReadFileDtb('068_blob_named_by_arg.dts', entry_args=entry_args)
Simon Glassdb168d42018-07-17 13:25:39 -06001747
Simon Glass53f53992018-07-17 13:25:40 -06001748 def testFill(self):
1749 """Test for an fill entry type"""
Simon Glass511f6582018-10-01 12:22:30 -06001750 data = self._DoReadFile('069_fill.dts')
Simon Glass80025522022-01-29 14:14:04 -07001751 expected = tools.get_bytes(0xff, 8) + tools.get_bytes(0, 8)
Simon Glass53f53992018-07-17 13:25:40 -06001752 self.assertEqual(expected, data)
1753
1754 def testFillNoSize(self):
1755 """Test for an fill entry type with no size"""
1756 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001757 self._DoReadFile('070_fill_no_size.dts')
Simon Glass0cf5bce2022-08-13 11:40:44 -06001758 self.assertIn("'fill' entry is missing properties: size",
Simon Glass53f53992018-07-17 13:25:40 -06001759 str(e.exception))
1760
Simon Glassc1ae83c2018-07-17 13:25:44 -06001761 def _HandleGbbCommand(self, pipe_list):
1762 """Fake calls to the futility utility"""
Simon Glass9a1c7262023-02-22 12:14:49 -07001763 if 'futility' in pipe_list[0][0]:
Simon Glassc1ae83c2018-07-17 13:25:44 -06001764 fname = pipe_list[0][-1]
1765 # Append our GBB data to the file, which will happen every time the
1766 # futility command is called.
Simon Glass33486662019-05-14 15:53:42 -06001767 with open(fname, 'ab') as fd:
Simon Glassc1ae83c2018-07-17 13:25:44 -06001768 fd.write(GBB_DATA)
1769 return command.CommandResult()
1770
1771 def testGbb(self):
1772 """Test for the Chromium OS Google Binary Block"""
1773 command.test_result = self._HandleGbbCommand
1774 entry_args = {
1775 'keydir': 'devkeys',
1776 'bmpblk': 'bmpblk.bin',
1777 }
Simon Glass511f6582018-10-01 12:22:30 -06001778 data, _, _, _ = self._DoReadFileDtb('071_gbb.dts', entry_args=entry_args)
Simon Glassc1ae83c2018-07-17 13:25:44 -06001779
1780 # Since futility
Simon Glass80025522022-01-29 14:14:04 -07001781 expected = (GBB_DATA + GBB_DATA + tools.get_bytes(0, 8) +
1782 tools.get_bytes(0, 0x2180 - 16))
Simon Glassc1ae83c2018-07-17 13:25:44 -06001783 self.assertEqual(expected, data)
1784
1785 def testGbbTooSmall(self):
1786 """Test for the Chromium OS Google Binary Block being large enough"""
1787 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001788 self._DoReadFileDtb('072_gbb_too_small.dts')
Simon Glassc1ae83c2018-07-17 13:25:44 -06001789 self.assertIn("Node '/binman/gbb': GBB is too small",
1790 str(e.exception))
1791
1792 def testGbbNoSize(self):
1793 """Test for the Chromium OS Google Binary Block having a size"""
1794 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001795 self._DoReadFileDtb('073_gbb_no_size.dts')
Simon Glassc1ae83c2018-07-17 13:25:44 -06001796 self.assertIn("Node '/binman/gbb': GBB must have a fixed size",
1797 str(e.exception))
1798
Simon Glass66152ce2022-01-09 20:14:09 -07001799 def testGbbMissing(self):
1800 """Test that binman still produces an image if futility is missing"""
1801 entry_args = {
1802 'keydir': 'devkeys',
1803 }
1804 with test_util.capture_sys_output() as (_, stderr):
1805 self._DoTestFile('071_gbb.dts', force_missing_bintools='futility',
1806 entry_args=entry_args)
1807 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07001808 self.assertRegex(err, "Image 'image'.*missing bintools.*: futility")
Simon Glass66152ce2022-01-09 20:14:09 -07001809
Simon Glass5c350162018-07-17 13:25:47 -06001810 def _HandleVblockCommand(self, pipe_list):
Simon Glass220c6222021-01-06 21:35:17 -07001811 """Fake calls to the futility utility
1812
1813 The expected pipe is:
1814
1815 [('futility', 'vbutil_firmware', '--vblock',
1816 'vblock.vblock', '--keyblock', 'devkeys/firmware.keyblock',
1817 '--signprivate', 'devkeys/firmware_data_key.vbprivk',
1818 '--version', '1', '--fv', 'input.vblock', '--kernelkey',
1819 'devkeys/kernel_subkey.vbpubk', '--flags', '1')]
1820
1821 This writes to the output file (here, 'vblock.vblock'). If
1822 self._hash_data is False, it writes VBLOCK_DATA, else it writes a hash
1823 of the input data (here, 'input.vblock').
1824 """
Simon Glass9a1c7262023-02-22 12:14:49 -07001825 if 'futility' in pipe_list[0][0]:
Simon Glass5c350162018-07-17 13:25:47 -06001826 fname = pipe_list[0][3]
Simon Glass639505b2018-09-14 04:57:11 -06001827 with open(fname, 'wb') as fd:
Simon Glass220c6222021-01-06 21:35:17 -07001828 if self._hash_data:
1829 infile = pipe_list[0][11]
1830 m = hashlib.sha256()
Simon Glass80025522022-01-29 14:14:04 -07001831 data = tools.read_file(infile)
Simon Glass220c6222021-01-06 21:35:17 -07001832 m.update(data)
1833 fd.write(m.digest())
1834 else:
1835 fd.write(VBLOCK_DATA)
1836
Simon Glass5c350162018-07-17 13:25:47 -06001837 return command.CommandResult()
1838
1839 def testVblock(self):
1840 """Test for the Chromium OS Verified Boot Block"""
Simon Glass220c6222021-01-06 21:35:17 -07001841 self._hash_data = False
Simon Glass5c350162018-07-17 13:25:47 -06001842 command.test_result = self._HandleVblockCommand
1843 entry_args = {
1844 'keydir': 'devkeys',
1845 }
Simon Glass511f6582018-10-01 12:22:30 -06001846 data, _, _, _ = self._DoReadFileDtb('074_vblock.dts',
Simon Glass5c350162018-07-17 13:25:47 -06001847 entry_args=entry_args)
1848 expected = U_BOOT_DATA + VBLOCK_DATA + U_BOOT_DTB_DATA
1849 self.assertEqual(expected, data)
1850
1851 def testVblockNoContent(self):
1852 """Test we detect a vblock which has no content to sign"""
1853 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001854 self._DoReadFile('075_vblock_no_content.dts')
Simon Glasse1915782021-03-21 18:24:31 +13001855 self.assertIn("Node '/binman/vblock': Collection must have a 'content' "
Simon Glass5c350162018-07-17 13:25:47 -06001856 'property', str(e.exception))
1857
1858 def testVblockBadPhandle(self):
1859 """Test that we detect a vblock with an invalid phandle in contents"""
1860 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001861 self._DoReadFile('076_vblock_bad_phandle.dts')
Simon Glass5c350162018-07-17 13:25:47 -06001862 self.assertIn("Node '/binman/vblock': Cannot find node for phandle "
1863 '1000', str(e.exception))
1864
1865 def testVblockBadEntry(self):
1866 """Test that we detect an entry that points to a non-entry"""
1867 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001868 self._DoReadFile('077_vblock_bad_entry.dts')
Simon Glass5c350162018-07-17 13:25:47 -06001869 self.assertIn("Node '/binman/vblock': Cannot find entry for node "
1870 "'other'", str(e.exception))
1871
Simon Glass220c6222021-01-06 21:35:17 -07001872 def testVblockContent(self):
1873 """Test that the vblock signs the right data"""
1874 self._hash_data = True
1875 command.test_result = self._HandleVblockCommand
1876 entry_args = {
1877 'keydir': 'devkeys',
1878 }
1879 data = self._DoReadFileDtb(
1880 '189_vblock_content.dts', use_real_dtb=True, update_dtb=True,
1881 entry_args=entry_args)[0]
1882 hashlen = 32 # SHA256 hash is 32 bytes
1883 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
1884 hashval = data[-hashlen:]
1885 dtb = data[len(U_BOOT_DATA):-hashlen]
1886
1887 expected_data = U_BOOT_DATA + dtb
1888
1889 # The hashval should be a hash of the dtb
1890 m = hashlib.sha256()
1891 m.update(expected_data)
1892 expected_hashval = m.digest()
1893 self.assertEqual(expected_hashval, hashval)
1894
Simon Glass66152ce2022-01-09 20:14:09 -07001895 def testVblockMissing(self):
1896 """Test that binman still produces an image if futility is missing"""
1897 entry_args = {
1898 'keydir': 'devkeys',
1899 }
1900 with test_util.capture_sys_output() as (_, stderr):
1901 self._DoTestFile('074_vblock.dts',
1902 force_missing_bintools='futility',
1903 entry_args=entry_args)
1904 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07001905 self.assertRegex(err, "Image 'image'.*missing bintools.*: futility")
Simon Glass66152ce2022-01-09 20:14:09 -07001906
Simon Glass8425a1f2018-07-17 13:25:48 -06001907 def testTpl(self):
Simon Glass3eb5b202019-08-24 07:23:00 -06001908 """Test that an image with TPL and its device tree can be created"""
Simon Glass8425a1f2018-07-17 13:25:48 -06001909 # ELF file with a '__bss_size' symbol
Simon Glass3eb5b202019-08-24 07:23:00 -06001910 self._SetupTplElf()
Simon Glass511f6582018-10-01 12:22:30 -06001911 data = self._DoReadFile('078_u_boot_tpl.dts')
Simon Glass8425a1f2018-07-17 13:25:48 -06001912 self.assertEqual(U_BOOT_TPL_DATA + U_BOOT_TPL_DTB_DATA, data)
1913
Simon Glass24b97442018-07-17 13:25:51 -06001914 def testUsesPos(self):
1915 """Test that the 'pos' property cannot be used anymore"""
1916 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001917 data = self._DoReadFile('079_uses_pos.dts')
Simon Glass24b97442018-07-17 13:25:51 -06001918 self.assertIn("Node '/binman/u-boot': Please use 'offset' instead of "
1919 "'pos'", str(e.exception))
1920
Simon Glass274bf092018-09-14 04:57:08 -06001921 def testFillZero(self):
1922 """Test for an fill entry type with a size of 0"""
Simon Glass511f6582018-10-01 12:22:30 -06001923 data = self._DoReadFile('080_fill_empty.dts')
Simon Glass80025522022-01-29 14:14:04 -07001924 self.assertEqual(tools.get_bytes(0, 16), data)
Simon Glass274bf092018-09-14 04:57:08 -06001925
Simon Glass267de432018-09-14 04:57:09 -06001926 def testTextMissing(self):
1927 """Test for a text entry type where there is no text"""
1928 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06001929 self._DoReadFileDtb('066_text.dts',)
Simon Glass267de432018-09-14 04:57:09 -06001930 self.assertIn("Node '/binman/text': No value provided for text label "
1931 "'test-id'", str(e.exception))
1932
Simon Glassed40e962018-09-14 04:57:10 -06001933 def testPackStart16Tpl(self):
1934 """Test that an image with an x86 start16 TPL region can be created"""
Simon Glass1d167762019-08-24 07:23:01 -06001935 data = self._DoReadFile('081_x86_start16_tpl.dts')
Simon Glassed40e962018-09-14 04:57:10 -06001936 self.assertEqual(X86_START16_TPL_DATA, data[:len(X86_START16_TPL_DATA)])
1937
Simon Glass3b376c32018-09-14 04:57:12 -06001938 def testSelectImage(self):
1939 """Test that we can select which images to build"""
Simon Glassb4595d82019-04-25 21:58:34 -06001940 expected = 'Skipping images: image1'
1941
1942 # We should only get the expected message in verbose mode
Simon Glass8a50b4a2019-07-08 13:18:48 -06001943 for verbosity in (0, 2):
Simon Glassb4595d82019-04-25 21:58:34 -06001944 with test_util.capture_sys_output() as (stdout, stderr):
1945 retcode = self._DoTestFile('006_dual_image.dts',
1946 verbosity=verbosity,
1947 images=['image2'])
1948 self.assertEqual(0, retcode)
1949 if verbosity:
1950 self.assertIn(expected, stdout.getvalue())
1951 else:
1952 self.assertNotIn(expected, stdout.getvalue())
Simon Glass3b376c32018-09-14 04:57:12 -06001953
Simon Glass80025522022-01-29 14:14:04 -07001954 self.assertFalse(os.path.exists(tools.get_output_filename('image1.bin')))
1955 self.assertTrue(os.path.exists(tools.get_output_filename('image2.bin')))
Simon Glassb3d6fc72019-07-20 12:24:10 -06001956 self._CleanupOutputDir()
Simon Glass3b376c32018-09-14 04:57:12 -06001957
Simon Glasse219aa42018-09-14 04:57:24 -06001958 def testUpdateFdtAll(self):
1959 """Test that all device trees are updated with offset/size info"""
Simon Glass5b4bce32019-07-08 14:25:26 -06001960 data = self._DoReadFileRealDtb('082_fdt_update_all.dts')
Simon Glasse219aa42018-09-14 04:57:24 -06001961
1962 base_expected = {
Simon Glasse219aa42018-09-14 04:57:24 -06001963 'offset': 0,
Simon Glass56d05412022-02-28 07:16:54 -07001964 'image-pos': 0,
1965 'size': 2320,
Simon Glasse219aa42018-09-14 04:57:24 -06001966 'section:offset': 0,
Simon Glass56d05412022-02-28 07:16:54 -07001967 'section:image-pos': 0,
1968 'section:size': 565,
1969 'section/u-boot-dtb:offset': 0,
1970 'section/u-boot-dtb:image-pos': 0,
1971 'section/u-boot-dtb:size': 565,
1972 'u-boot-spl-dtb:offset': 565,
1973 'u-boot-spl-dtb:image-pos': 565,
1974 'u-boot-spl-dtb:size': 585,
1975 'u-boot-tpl-dtb:offset': 1150,
1976 'u-boot-tpl-dtb:image-pos': 1150,
1977 'u-boot-tpl-dtb:size': 585,
1978 'u-boot-vpl-dtb:image-pos': 1735,
1979 'u-boot-vpl-dtb:offset': 1735,
1980 'u-boot-vpl-dtb:size': 585,
Simon Glasse219aa42018-09-14 04:57:24 -06001981 }
1982
1983 # We expect three device-tree files in the output, one after the other.
1984 # Read them in sequence. We look for an 'spl' property in the SPL tree,
1985 # and 'tpl' in the TPL tree, to make sure they are distinct from the
1986 # main U-Boot tree. All three should have the same postions and offset.
1987 start = 0
Simon Glass56d05412022-02-28 07:16:54 -07001988 self.maxDiff = None
1989 for item in ['', 'spl', 'tpl', 'vpl']:
Simon Glasse219aa42018-09-14 04:57:24 -06001990 dtb = fdt.Fdt.FromData(data[start:])
1991 dtb.Scan()
Simon Glassfb30e292019-07-20 12:23:51 -06001992 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS +
Simon Glass56d05412022-02-28 07:16:54 -07001993 ['spl', 'tpl', 'vpl'])
Simon Glasse219aa42018-09-14 04:57:24 -06001994 expected = dict(base_expected)
1995 if item:
1996 expected[item] = 0
1997 self.assertEqual(expected, props)
1998 start += dtb._fdt_obj.totalsize()
1999
2000 def testUpdateFdtOutput(self):
2001 """Test that output DTB files are updated"""
2002 try:
Simon Glass511f6582018-10-01 12:22:30 -06002003 data, dtb_data, _, _ = self._DoReadFileDtb('082_fdt_update_all.dts',
Simon Glasse219aa42018-09-14 04:57:24 -06002004 use_real_dtb=True, update_dtb=True, reset_dtbs=False)
2005
2006 # Unfortunately, compiling a source file always results in a file
2007 # called source.dtb (see fdt_util.EnsureCompiled()). The test
Simon Glass511f6582018-10-01 12:22:30 -06002008 # source file (e.g. test/075_fdt_update_all.dts) thus does not enter
Simon Glasse219aa42018-09-14 04:57:24 -06002009 # binman as a file called u-boot.dtb. To fix this, copy the file
2010 # over to the expected place.
Simon Glasse219aa42018-09-14 04:57:24 -06002011 start = 0
2012 for fname in ['u-boot.dtb.out', 'spl/u-boot-spl.dtb.out',
Simon Glass56d05412022-02-28 07:16:54 -07002013 'tpl/u-boot-tpl.dtb.out', 'vpl/u-boot-vpl.dtb.out']:
Simon Glasse219aa42018-09-14 04:57:24 -06002014 dtb = fdt.Fdt.FromData(data[start:])
2015 size = dtb._fdt_obj.totalsize()
Simon Glass80025522022-01-29 14:14:04 -07002016 pathname = tools.get_output_filename(os.path.split(fname)[1])
2017 outdata = tools.read_file(pathname)
Simon Glasse219aa42018-09-14 04:57:24 -06002018 name = os.path.split(fname)[0]
2019
2020 if name:
Simon Glass56d05412022-02-28 07:16:54 -07002021 orig_indata = self._GetDtbContentsForSpls(dtb_data, name)
Simon Glasse219aa42018-09-14 04:57:24 -06002022 else:
2023 orig_indata = dtb_data
2024 self.assertNotEqual(outdata, orig_indata,
2025 "Expected output file '%s' be updated" % pathname)
2026 self.assertEqual(outdata, data[start:start + size],
2027 "Expected output file '%s' to match output image" %
2028 pathname)
2029 start += size
2030 finally:
2031 self._ResetDtbs()
2032
Simon Glass7ba33592018-09-14 04:57:26 -06002033 def _decompress(self, data):
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +02002034 bintool = self.comp_bintools['lz4']
2035 return bintool.decompress(data)
Simon Glass7ba33592018-09-14 04:57:26 -06002036
2037 def testCompress(self):
2038 """Test compression of blobs"""
Simon Glass1de34482019-07-08 13:18:53 -06002039 self._CheckLz4()
Simon Glass511f6582018-10-01 12:22:30 -06002040 data, _, _, out_dtb_fname = self._DoReadFileDtb('083_compress.dts',
Simon Glass7ba33592018-09-14 04:57:26 -06002041 use_real_dtb=True, update_dtb=True)
2042 dtb = fdt.Fdt(out_dtb_fname)
2043 dtb.Scan()
2044 props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
2045 orig = self._decompress(data)
2046 self.assertEquals(COMPRESS_DATA, orig)
Simon Glass789b34402020-10-26 17:40:15 -06002047
2048 # Do a sanity check on various fields
2049 image = control.images['image']
2050 entries = image.GetEntries()
2051 self.assertEqual(1, len(entries))
2052
2053 entry = entries['blob']
2054 self.assertEqual(COMPRESS_DATA, entry.uncomp_data)
2055 self.assertEqual(len(COMPRESS_DATA), entry.uncomp_size)
2056 orig = self._decompress(entry.data)
2057 self.assertEqual(orig, entry.uncomp_data)
2058
Simon Glass72eeff12020-10-26 17:40:16 -06002059 self.assertEqual(image.data, entry.data)
2060
Simon Glass7ba33592018-09-14 04:57:26 -06002061 expected = {
2062 'blob:uncomp-size': len(COMPRESS_DATA),
2063 'blob:size': len(data),
2064 'size': len(data),
2065 }
2066 self.assertEqual(expected, props)
2067
Simon Glassac6328c2018-09-14 04:57:28 -06002068 def testFiles(self):
2069 """Test bringing in multiple files"""
Simon Glass511f6582018-10-01 12:22:30 -06002070 data = self._DoReadFile('084_files.dts')
Simon Glassac6328c2018-09-14 04:57:28 -06002071 self.assertEqual(FILES_DATA, data)
2072
2073 def testFilesCompress(self):
2074 """Test bringing in multiple files and compressing them"""
Simon Glass1de34482019-07-08 13:18:53 -06002075 self._CheckLz4()
Simon Glass511f6582018-10-01 12:22:30 -06002076 data = self._DoReadFile('085_files_compress.dts')
Simon Glassac6328c2018-09-14 04:57:28 -06002077
2078 image = control.images['image']
2079 entries = image.GetEntries()
2080 files = entries['files']
Simon Glass39dd2152019-07-08 14:25:47 -06002081 entries = files._entries
Simon Glassac6328c2018-09-14 04:57:28 -06002082
Simon Glass303f62f2019-05-17 22:00:46 -06002083 orig = b''
Simon Glassac6328c2018-09-14 04:57:28 -06002084 for i in range(1, 3):
2085 key = '%d.dat' % i
2086 start = entries[key].image_pos
2087 len = entries[key].size
2088 chunk = data[start:start + len]
2089 orig += self._decompress(chunk)
2090
2091 self.assertEqual(FILES_DATA, orig)
2092
2093 def testFilesMissing(self):
2094 """Test missing files"""
2095 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002096 data = self._DoReadFile('086_files_none.dts')
Simon Glassac6328c2018-09-14 04:57:28 -06002097 self.assertIn("Node '/binman/files': Pattern \'files/*.none\' matched "
2098 'no files', str(e.exception))
2099
2100 def testFilesNoPattern(self):
2101 """Test missing files"""
2102 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002103 data = self._DoReadFile('087_files_no_pattern.dts')
Simon Glassac6328c2018-09-14 04:57:28 -06002104 self.assertIn("Node '/binman/files': Missing 'pattern' property",
2105 str(e.exception))
2106
Simon Glassdd156a42022-03-05 20:18:59 -07002107 def testExtendSize(self):
2108 """Test an extending entry"""
2109 data, _, map_data, _ = self._DoReadFileDtb('088_extend_size.dts',
Simon Glassfa79a812018-09-14 04:57:29 -06002110 map=True)
Simon Glass80025522022-01-29 14:14:04 -07002111 expect = (tools.get_bytes(ord('a'), 8) + U_BOOT_DATA +
2112 MRC_DATA + tools.get_bytes(ord('b'), 1) + U_BOOT_DATA +
2113 tools.get_bytes(ord('c'), 8) + U_BOOT_DATA +
2114 tools.get_bytes(ord('d'), 8))
Simon Glassfa79a812018-09-14 04:57:29 -06002115 self.assertEqual(expect, data)
2116 self.assertEqual('''ImagePos Offset Size Name
Simon Glass49cd2b32023-02-07 14:34:18 -0700211700000000 00000000 00000028 image
Simon Glassfa79a812018-09-14 04:57:29 -0600211800000000 00000000 00000008 fill
211900000008 00000008 00000004 u-boot
21200000000c 0000000c 00000004 section
21210000000c 00000000 00000003 intel-mrc
212200000010 00000010 00000004 u-boot2
212300000014 00000014 0000000c section2
212400000014 00000000 00000008 fill
21250000001c 00000008 00000004 u-boot
212600000020 00000020 00000008 fill2
2127''', map_data)
2128
Simon Glassdd156a42022-03-05 20:18:59 -07002129 def testExtendSizeBad(self):
2130 """Test an extending entry which fails to provide contents"""
Simon Glasscd817d52018-09-14 04:57:36 -06002131 with test_util.capture_sys_output() as (stdout, stderr):
2132 with self.assertRaises(ValueError) as e:
Simon Glassdd156a42022-03-05 20:18:59 -07002133 self._DoReadFileDtb('089_extend_size_bad.dts', map=True)
Simon Glassfa79a812018-09-14 04:57:29 -06002134 self.assertIn("Node '/binman/_testing': Cannot obtain contents when "
2135 'expanding entry', str(e.exception))
2136
Simon Glassae7cf032018-09-14 04:57:31 -06002137 def testHash(self):
2138 """Test hashing of the contents of an entry"""
Simon Glass511f6582018-10-01 12:22:30 -06002139 _, _, _, out_dtb_fname = self._DoReadFileDtb('090_hash.dts',
Simon Glassae7cf032018-09-14 04:57:31 -06002140 use_real_dtb=True, update_dtb=True)
2141 dtb = fdt.Fdt(out_dtb_fname)
2142 dtb.Scan()
2143 hash_node = dtb.GetNode('/binman/u-boot/hash').props['value']
2144 m = hashlib.sha256()
2145 m.update(U_BOOT_DATA)
Simon Glass303f62f2019-05-17 22:00:46 -06002146 self.assertEqual(m.digest(), b''.join(hash_node.value))
Simon Glassae7cf032018-09-14 04:57:31 -06002147
2148 def testHashNoAlgo(self):
2149 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002150 self._DoReadFileDtb('091_hash_no_algo.dts', update_dtb=True)
Simon Glassae7cf032018-09-14 04:57:31 -06002151 self.assertIn("Node \'/binman/u-boot\': Missing \'algo\' property for "
2152 'hash node', str(e.exception))
2153
2154 def testHashBadAlgo(self):
2155 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002156 self._DoReadFileDtb('092_hash_bad_algo.dts', update_dtb=True)
Simon Glass64af7c22022-02-08 10:59:44 -07002157 self.assertIn("Node '/binman/u-boot': Unknown hash algorithm 'invalid'",
Simon Glassae7cf032018-09-14 04:57:31 -06002158 str(e.exception))
2159
2160 def testHashSection(self):
2161 """Test hashing of the contents of an entry"""
Simon Glass511f6582018-10-01 12:22:30 -06002162 _, _, _, out_dtb_fname = self._DoReadFileDtb('099_hash_section.dts',
Simon Glassae7cf032018-09-14 04:57:31 -06002163 use_real_dtb=True, update_dtb=True)
2164 dtb = fdt.Fdt(out_dtb_fname)
2165 dtb.Scan()
2166 hash_node = dtb.GetNode('/binman/section/hash').props['value']
2167 m = hashlib.sha256()
2168 m.update(U_BOOT_DATA)
Simon Glass80025522022-01-29 14:14:04 -07002169 m.update(tools.get_bytes(ord('a'), 16))
Simon Glass303f62f2019-05-17 22:00:46 -06002170 self.assertEqual(m.digest(), b''.join(hash_node.value))
Simon Glassae7cf032018-09-14 04:57:31 -06002171
Simon Glass3fb4f422018-09-14 04:57:32 -06002172 def testPackUBootTplMicrocode(self):
2173 """Test that x86 microcode can be handled correctly in TPL
2174
2175 We expect to see the following in the image, in order:
2176 u-boot-tpl-nodtb.bin with a microcode pointer inserted at the correct
2177 place
2178 u-boot-tpl.dtb with the microcode removed
2179 the microcode
2180 """
Simon Glass3eb5b202019-08-24 07:23:00 -06002181 self._SetupTplElf('u_boot_ucode_ptr')
Simon Glass511f6582018-10-01 12:22:30 -06002182 first, pos_and_size = self._RunMicrocodeTest('093_x86_tpl_ucode.dts',
Simon Glass3fb4f422018-09-14 04:57:32 -06002183 U_BOOT_TPL_NODTB_DATA)
Simon Glass303f62f2019-05-17 22:00:46 -06002184 self.assertEqual(b'tplnodtb with microc' + pos_and_size +
2185 b'ter somewhere in here', first)
Simon Glass3fb4f422018-09-14 04:57:32 -06002186
Simon Glassc64aea52018-09-14 04:57:34 -06002187 def testFmapX86(self):
2188 """Basic test of generation of a flashrom fmap"""
Simon Glass511f6582018-10-01 12:22:30 -06002189 data = self._DoReadFile('094_fmap_x86.dts')
Simon Glassc64aea52018-09-14 04:57:34 -06002190 fhdr, fentries = fmap_util.DecodeFmap(data[32:])
Simon Glass80025522022-01-29 14:14:04 -07002191 expected = U_BOOT_DATA + MRC_DATA + tools.get_bytes(ord('a'), 32 - 7)
Simon Glassc64aea52018-09-14 04:57:34 -06002192 self.assertEqual(expected, data[:32])
2193 fhdr, fentries = fmap_util.DecodeFmap(data[32:])
2194
2195 self.assertEqual(0x100, fhdr.image_size)
2196
2197 self.assertEqual(0, fentries[0].offset)
2198 self.assertEqual(4, fentries[0].size)
Simon Glass303f62f2019-05-17 22:00:46 -06002199 self.assertEqual(b'U_BOOT', fentries[0].name)
Simon Glassc64aea52018-09-14 04:57:34 -06002200
2201 self.assertEqual(4, fentries[1].offset)
2202 self.assertEqual(3, fentries[1].size)
Simon Glass303f62f2019-05-17 22:00:46 -06002203 self.assertEqual(b'INTEL_MRC', fentries[1].name)
Simon Glassc64aea52018-09-14 04:57:34 -06002204
2205 self.assertEqual(32, fentries[2].offset)
2206 self.assertEqual(fmap_util.FMAP_HEADER_LEN +
2207 fmap_util.FMAP_AREA_LEN * 3, fentries[2].size)
Simon Glass303f62f2019-05-17 22:00:46 -06002208 self.assertEqual(b'FMAP', fentries[2].name)
Simon Glassc64aea52018-09-14 04:57:34 -06002209
2210 def testFmapX86Section(self):
2211 """Basic test of generation of a flashrom fmap"""
Simon Glass511f6582018-10-01 12:22:30 -06002212 data = self._DoReadFile('095_fmap_x86_section.dts')
Simon Glass80025522022-01-29 14:14:04 -07002213 expected = U_BOOT_DATA + MRC_DATA + tools.get_bytes(ord('b'), 32 - 7)
Simon Glassc64aea52018-09-14 04:57:34 -06002214 self.assertEqual(expected, data[:32])
2215 fhdr, fentries = fmap_util.DecodeFmap(data[36:])
2216
Simon Glassb1d414c2021-04-03 11:05:10 +13002217 self.assertEqual(0x180, fhdr.image_size)
2218 expect_size = fmap_util.FMAP_HEADER_LEN + fmap_util.FMAP_AREA_LEN * 4
Simon Glass82059c22021-04-03 11:05:09 +13002219 fiter = iter(fentries)
Simon Glassc64aea52018-09-14 04:57:34 -06002220
Simon Glass82059c22021-04-03 11:05:09 +13002221 fentry = next(fiter)
2222 self.assertEqual(b'U_BOOT', fentry.name)
2223 self.assertEqual(0, fentry.offset)
2224 self.assertEqual(4, fentry.size)
Simon Glassc64aea52018-09-14 04:57:34 -06002225
Simon Glass82059c22021-04-03 11:05:09 +13002226 fentry = next(fiter)
Simon Glassb1d414c2021-04-03 11:05:10 +13002227 self.assertEqual(b'SECTION', fentry.name)
2228 self.assertEqual(4, fentry.offset)
2229 self.assertEqual(0x20 + expect_size, fentry.size)
2230
2231 fentry = next(fiter)
Simon Glass82059c22021-04-03 11:05:09 +13002232 self.assertEqual(b'INTEL_MRC', fentry.name)
2233 self.assertEqual(4, fentry.offset)
2234 self.assertEqual(3, fentry.size)
Simon Glassc64aea52018-09-14 04:57:34 -06002235
Simon Glass82059c22021-04-03 11:05:09 +13002236 fentry = next(fiter)
2237 self.assertEqual(b'FMAP', fentry.name)
2238 self.assertEqual(36, fentry.offset)
2239 self.assertEqual(expect_size, fentry.size)
Simon Glassc64aea52018-09-14 04:57:34 -06002240
Simon Glassb1714232018-09-14 04:57:35 -06002241 def testElf(self):
2242 """Basic test of ELF entries"""
Simon Glass7057d022018-10-01 21:12:47 -06002243 self._SetupSplElf()
Simon Glass3eb5b202019-08-24 07:23:00 -06002244 self._SetupTplElf()
Simon Glassf6290892019-08-24 07:22:53 -06002245 with open(self.ElfTestFile('bss_data'), 'rb') as fd:
Simon Glassb1714232018-09-14 04:57:35 -06002246 TestFunctional._MakeInputFile('-boot', fd.read())
Simon Glass511f6582018-10-01 12:22:30 -06002247 data = self._DoReadFile('096_elf.dts')
Simon Glassb1714232018-09-14 04:57:35 -06002248
Simon Glass0d673792019-07-08 13:18:25 -06002249 def testElfStrip(self):
Simon Glassb1714232018-09-14 04:57:35 -06002250 """Basic test of ELF entries"""
Simon Glass7057d022018-10-01 21:12:47 -06002251 self._SetupSplElf()
Simon Glassf6290892019-08-24 07:22:53 -06002252 with open(self.ElfTestFile('bss_data'), 'rb') as fd:
Simon Glassb1714232018-09-14 04:57:35 -06002253 TestFunctional._MakeInputFile('-boot', fd.read())
Simon Glass511f6582018-10-01 12:22:30 -06002254 data = self._DoReadFile('097_elf_strip.dts')
Simon Glassb1714232018-09-14 04:57:35 -06002255
Simon Glasscd817d52018-09-14 04:57:36 -06002256 def testPackOverlapMap(self):
2257 """Test that overlapping regions are detected"""
2258 with test_util.capture_sys_output() as (stdout, stderr):
2259 with self.assertRaises(ValueError) as e:
Simon Glass511f6582018-10-01 12:22:30 -06002260 self._DoTestFile('014_pack_overlap.dts', map=True)
Simon Glass80025522022-01-29 14:14:04 -07002261 map_fname = tools.get_output_filename('image.map')
Simon Glasscd817d52018-09-14 04:57:36 -06002262 self.assertEqual("Wrote map file '%s' to show errors\n" % map_fname,
2263 stdout.getvalue())
2264
2265 # We should not get an inmage, but there should be a map file
Simon Glass80025522022-01-29 14:14:04 -07002266 self.assertFalse(os.path.exists(tools.get_output_filename('image.bin')))
Simon Glasscd817d52018-09-14 04:57:36 -06002267 self.assertTrue(os.path.exists(map_fname))
Simon Glass80025522022-01-29 14:14:04 -07002268 map_data = tools.read_file(map_fname, binary=False)
Simon Glasscd817d52018-09-14 04:57:36 -06002269 self.assertEqual('''ImagePos Offset Size Name
Simon Glass49cd2b32023-02-07 14:34:18 -07002270<none> 00000000 00000008 image
Simon Glasscd817d52018-09-14 04:57:36 -06002271<none> 00000000 00000004 u-boot
2272<none> 00000003 00000004 u-boot-align
2273''', map_data)
2274
Simon Glass0d673792019-07-08 13:18:25 -06002275 def testPackRefCode(self):
Simon Glass41902e42018-10-01 12:22:31 -06002276 """Test that an image with an Intel Reference code binary works"""
2277 data = self._DoReadFile('100_intel_refcode.dts')
2278 self.assertEqual(REFCODE_DATA, data[:len(REFCODE_DATA)])
2279
Simon Glasseb023b32019-04-25 21:58:39 -06002280 def testSectionOffset(self):
2281 """Tests use of a section with an offset"""
2282 data, _, map_data, _ = self._DoReadFileDtb('101_sections_offset.dts',
2283 map=True)
2284 self.assertEqual('''ImagePos Offset Size Name
Simon Glass49cd2b32023-02-07 14:34:18 -0700228500000000 00000000 00000038 image
Simon Glasseb023b32019-04-25 21:58:39 -0600228600000004 00000004 00000010 section@0
228700000004 00000000 00000004 u-boot
228800000018 00000018 00000010 section@1
228900000018 00000000 00000004 u-boot
22900000002c 0000002c 00000004 section@2
22910000002c 00000000 00000004 u-boot
2292''', map_data)
2293 self.assertEqual(data,
Simon Glass80025522022-01-29 14:14:04 -07002294 tools.get_bytes(0x26, 4) + U_BOOT_DATA +
2295 tools.get_bytes(0x21, 12) +
2296 tools.get_bytes(0x26, 4) + U_BOOT_DATA +
2297 tools.get_bytes(0x61, 12) +
2298 tools.get_bytes(0x26, 4) + U_BOOT_DATA +
2299 tools.get_bytes(0x26, 8))
Simon Glasseb023b32019-04-25 21:58:39 -06002300
Simon Glass1de34482019-07-08 13:18:53 -06002301 def testCbfsRaw(self):
2302 """Test base handling of a Coreboot Filesystem (CBFS)
2303
2304 The exact contents of the CBFS is verified by similar tests in
2305 cbfs_util_test.py. The tests here merely check that the files added to
2306 the CBFS can be found in the final image.
2307 """
2308 data = self._DoReadFile('102_cbfs_raw.dts')
2309 size = 0xb0
2310
2311 cbfs = cbfs_util.CbfsReader(data)
2312 self.assertEqual(size, cbfs.rom_size)
2313
2314 self.assertIn('u-boot-dtb', cbfs.files)
2315 cfile = cbfs.files['u-boot-dtb']
2316 self.assertEqual(U_BOOT_DTB_DATA, cfile.data)
2317
2318 def testCbfsArch(self):
2319 """Test on non-x86 architecture"""
2320 data = self._DoReadFile('103_cbfs_raw_ppc.dts')
2321 size = 0x100
2322
2323 cbfs = cbfs_util.CbfsReader(data)
2324 self.assertEqual(size, cbfs.rom_size)
2325
2326 self.assertIn('u-boot-dtb', cbfs.files)
2327 cfile = cbfs.files['u-boot-dtb']
2328 self.assertEqual(U_BOOT_DTB_DATA, cfile.data)
2329
2330 def testCbfsStage(self):
2331 """Tests handling of a Coreboot Filesystem (CBFS)"""
2332 if not elf.ELF_TOOLS:
2333 self.skipTest('Python elftools not available')
2334 elf_fname = os.path.join(self._indir, 'cbfs-stage.elf')
2335 elf.MakeElf(elf_fname, U_BOOT_DATA, U_BOOT_DTB_DATA)
2336 size = 0xb0
2337
2338 data = self._DoReadFile('104_cbfs_stage.dts')
2339 cbfs = cbfs_util.CbfsReader(data)
2340 self.assertEqual(size, cbfs.rom_size)
2341
2342 self.assertIn('u-boot', cbfs.files)
2343 cfile = cbfs.files['u-boot']
2344 self.assertEqual(U_BOOT_DATA + U_BOOT_DTB_DATA, cfile.data)
2345
2346 def testCbfsRawCompress(self):
2347 """Test handling of compressing raw files"""
2348 self._CheckLz4()
2349 data = self._DoReadFile('105_cbfs_raw_compress.dts')
2350 size = 0x140
2351
2352 cbfs = cbfs_util.CbfsReader(data)
2353 self.assertIn('u-boot', cbfs.files)
2354 cfile = cbfs.files['u-boot']
2355 self.assertEqual(COMPRESS_DATA, cfile.data)
2356
2357 def testCbfsBadArch(self):
2358 """Test handling of a bad architecture"""
2359 with self.assertRaises(ValueError) as e:
2360 self._DoReadFile('106_cbfs_bad_arch.dts')
2361 self.assertIn("Invalid architecture 'bad-arch'", str(e.exception))
2362
2363 def testCbfsNoSize(self):
2364 """Test handling of a missing size property"""
2365 with self.assertRaises(ValueError) as e:
2366 self._DoReadFile('107_cbfs_no_size.dts')
2367 self.assertIn('entry must have a size property', str(e.exception))
2368
Simon Glass3e28f4f2021-11-23 11:03:54 -07002369 def testCbfsNoContents(self):
Simon Glass1de34482019-07-08 13:18:53 -06002370 """Test handling of a CBFS entry which does not provide contentsy"""
2371 with self.assertRaises(ValueError) as e:
2372 self._DoReadFile('108_cbfs_no_contents.dts')
2373 self.assertIn('Could not complete processing of contents',
2374 str(e.exception))
2375
2376 def testCbfsBadCompress(self):
2377 """Test handling of a bad architecture"""
2378 with self.assertRaises(ValueError) as e:
2379 self._DoReadFile('109_cbfs_bad_compress.dts')
2380 self.assertIn("Invalid compression in 'u-boot': 'invalid-algo'",
2381 str(e.exception))
2382
2383 def testCbfsNamedEntries(self):
2384 """Test handling of named entries"""
2385 data = self._DoReadFile('110_cbfs_name.dts')
2386
2387 cbfs = cbfs_util.CbfsReader(data)
2388 self.assertIn('FRED', cbfs.files)
2389 cfile1 = cbfs.files['FRED']
2390 self.assertEqual(U_BOOT_DATA, cfile1.data)
2391
2392 self.assertIn('hello', cbfs.files)
2393 cfile2 = cbfs.files['hello']
2394 self.assertEqual(U_BOOT_DTB_DATA, cfile2.data)
2395
Simon Glass759af872019-07-08 13:18:54 -06002396 def _SetupIfwi(self, fname):
2397 """Set up to run an IFWI test
2398
2399 Args:
2400 fname: Filename of input file to provide (fitimage.bin or ifwi.bin)
2401 """
2402 self._SetupSplElf()
Simon Glass3eb5b202019-08-24 07:23:00 -06002403 self._SetupTplElf()
Simon Glass759af872019-07-08 13:18:54 -06002404
2405 # Intel Integrated Firmware Image (IFWI) file
2406 with gzip.open(self.TestFile('%s.gz' % fname), 'rb') as fd:
2407 data = fd.read()
2408 TestFunctional._MakeInputFile(fname,data)
2409
2410 def _CheckIfwi(self, data):
2411 """Check that an image with an IFWI contains the correct output
2412
2413 Args:
2414 data: Conents of output file
2415 """
Simon Glass80025522022-01-29 14:14:04 -07002416 expected_desc = tools.read_file(self.TestFile('descriptor.bin'))
Simon Glass759af872019-07-08 13:18:54 -06002417 if data[:0x1000] != expected_desc:
2418 self.fail('Expected descriptor binary at start of image')
2419
2420 # We expect to find the TPL wil in subpart IBBP entry IBBL
Simon Glass80025522022-01-29 14:14:04 -07002421 image_fname = tools.get_output_filename('image.bin')
2422 tpl_fname = tools.get_output_filename('tpl.out')
Simon Glass57c7a482022-01-09 20:14:01 -07002423 ifwitool = bintool.Bintool.create('ifwitool')
2424 ifwitool.extract(image_fname, 'IBBP', 'IBBL', tpl_fname)
Simon Glass759af872019-07-08 13:18:54 -06002425
Simon Glass80025522022-01-29 14:14:04 -07002426 tpl_data = tools.read_file(tpl_fname)
Simon Glassf55bd692019-08-24 07:22:51 -06002427 self.assertEqual(U_BOOT_TPL_DATA, tpl_data[:len(U_BOOT_TPL_DATA)])
Simon Glass759af872019-07-08 13:18:54 -06002428
2429 def testPackX86RomIfwi(self):
2430 """Test that an x86 ROM with Integrated Firmware Image can be created"""
2431 self._SetupIfwi('fitimage.bin')
Simon Glass1d167762019-08-24 07:23:01 -06002432 data = self._DoReadFile('111_x86_rom_ifwi.dts')
Simon Glass759af872019-07-08 13:18:54 -06002433 self._CheckIfwi(data)
2434
2435 def testPackX86RomIfwiNoDesc(self):
2436 """Test that an x86 ROM with IFWI can be created from an ifwi.bin file"""
2437 self._SetupIfwi('ifwi.bin')
Simon Glass1d167762019-08-24 07:23:01 -06002438 data = self._DoReadFile('112_x86_rom_ifwi_nodesc.dts')
Simon Glass759af872019-07-08 13:18:54 -06002439 self._CheckIfwi(data)
2440
2441 def testPackX86RomIfwiNoData(self):
2442 """Test that an x86 ROM with IFWI handles missing data"""
2443 self._SetupIfwi('ifwi.bin')
2444 with self.assertRaises(ValueError) as e:
Simon Glass1d167762019-08-24 07:23:01 -06002445 data = self._DoReadFile('113_x86_rom_ifwi_nodata.dts')
Simon Glass759af872019-07-08 13:18:54 -06002446 self.assertIn('Could not complete processing of contents',
2447 str(e.exception))
Simon Glass91710b32018-07-17 13:25:32 -06002448
Simon Glass66152ce2022-01-09 20:14:09 -07002449 def testIfwiMissing(self):
2450 """Test that binman still produces an image if ifwitool is missing"""
2451 self._SetupIfwi('fitimage.bin')
2452 with test_util.capture_sys_output() as (_, stderr):
2453 self._DoTestFile('111_x86_rom_ifwi.dts',
2454 force_missing_bintools='ifwitool')
2455 err = stderr.getvalue()
2456 self.assertRegex(err,
Simon Glass49cd2b32023-02-07 14:34:18 -07002457 "Image 'image'.*missing bintools.*: ifwitool")
Simon Glass66152ce2022-01-09 20:14:09 -07002458
Simon Glassc2f1aed2019-07-08 13:18:56 -06002459 def testCbfsOffset(self):
2460 """Test a CBFS with files at particular offsets
2461
2462 Like all CFBS tests, this is just checking the logic that calls
2463 cbfs_util. See cbfs_util_test for fully tests (e.g. test_cbfs_offset()).
2464 """
2465 data = self._DoReadFile('114_cbfs_offset.dts')
2466 size = 0x200
2467
2468 cbfs = cbfs_util.CbfsReader(data)
2469 self.assertEqual(size, cbfs.rom_size)
2470
2471 self.assertIn('u-boot', cbfs.files)
2472 cfile = cbfs.files['u-boot']
2473 self.assertEqual(U_BOOT_DATA, cfile.data)
2474 self.assertEqual(0x40, cfile.cbfs_offset)
2475
2476 self.assertIn('u-boot-dtb', cbfs.files)
2477 cfile2 = cbfs.files['u-boot-dtb']
2478 self.assertEqual(U_BOOT_DTB_DATA, cfile2.data)
2479 self.assertEqual(0x140, cfile2.cbfs_offset)
2480
Simon Glass0f621332019-07-08 14:25:27 -06002481 def testFdtmap(self):
2482 """Test an FDT map can be inserted in the image"""
2483 data = self.data = self._DoReadFileRealDtb('115_fdtmap.dts')
2484 fdtmap_data = data[len(U_BOOT_DATA):]
2485 magic = fdtmap_data[:8]
Simon Glassc5fd10a2019-10-31 07:43:03 -06002486 self.assertEqual(b'_FDTMAP_', magic)
Simon Glass80025522022-01-29 14:14:04 -07002487 self.assertEqual(tools.get_bytes(0, 8), fdtmap_data[8:16])
Simon Glass0f621332019-07-08 14:25:27 -06002488
2489 fdt_data = fdtmap_data[16:]
2490 dtb = fdt.Fdt.FromData(fdt_data)
2491 dtb.Scan()
Simon Glass2c6adba2019-07-20 12:23:47 -06002492 props = self._GetPropTree(dtb, BASE_DTB_PROPS, prefix='/')
Simon Glass0f621332019-07-08 14:25:27 -06002493 self.assertEqual({
2494 'image-pos': 0,
2495 'offset': 0,
2496 'u-boot:offset': 0,
2497 'u-boot:size': len(U_BOOT_DATA),
2498 'u-boot:image-pos': 0,
2499 'fdtmap:image-pos': 4,
2500 'fdtmap:offset': 4,
2501 'fdtmap:size': len(fdtmap_data),
2502 'size': len(data),
2503 }, props)
2504
2505 def testFdtmapNoMatch(self):
2506 """Check handling of an FDT map when the section cannot be found"""
2507 self.data = self._DoReadFileRealDtb('115_fdtmap.dts')
2508
2509 # Mangle the section name, which should cause a mismatch between the
2510 # correct FDT path and the one expected by the section
2511 image = control.images['image']
Simon Glasscec34ba2019-07-08 14:25:28 -06002512 image._node.path += '-suffix'
Simon Glass0f621332019-07-08 14:25:27 -06002513 entries = image.GetEntries()
2514 fdtmap = entries['fdtmap']
2515 with self.assertRaises(ValueError) as e:
2516 fdtmap._GetFdtmap()
2517 self.assertIn("Cannot locate node for path '/binman-suffix'",
2518 str(e.exception))
2519
Simon Glasscec34ba2019-07-08 14:25:28 -06002520 def testFdtmapHeader(self):
2521 """Test an FDT map and image header can be inserted in the image"""
2522 data = self.data = self._DoReadFileRealDtb('116_fdtmap_hdr.dts')
2523 fdtmap_pos = len(U_BOOT_DATA)
2524 fdtmap_data = data[fdtmap_pos:]
2525 fdt_data = fdtmap_data[16:]
2526 dtb = fdt.Fdt.FromData(fdt_data)
2527 fdt_size = dtb.GetFdtObj().totalsize()
2528 hdr_data = data[-8:]
Simon Glassc5fd10a2019-10-31 07:43:03 -06002529 self.assertEqual(b'BinM', hdr_data[:4])
Simon Glasscec34ba2019-07-08 14:25:28 -06002530 offset = struct.unpack('<I', hdr_data[4:])[0] & 0xffffffff
2531 self.assertEqual(fdtmap_pos - 0x400, offset - (1 << 32))
2532
2533 def testFdtmapHeaderStart(self):
2534 """Test an image header can be inserted at the image start"""
2535 data = self.data = self._DoReadFileRealDtb('117_fdtmap_hdr_start.dts')
2536 fdtmap_pos = 0x100 + len(U_BOOT_DATA)
2537 hdr_data = data[:8]
Simon Glassc5fd10a2019-10-31 07:43:03 -06002538 self.assertEqual(b'BinM', hdr_data[:4])
Simon Glasscec34ba2019-07-08 14:25:28 -06002539 offset = struct.unpack('<I', hdr_data[4:])[0]
2540 self.assertEqual(fdtmap_pos, offset)
2541
2542 def testFdtmapHeaderPos(self):
2543 """Test an image header can be inserted at a chosen position"""
2544 data = self.data = self._DoReadFileRealDtb('118_fdtmap_hdr_pos.dts')
2545 fdtmap_pos = 0x100 + len(U_BOOT_DATA)
2546 hdr_data = data[0x80:0x88]
Simon Glassc5fd10a2019-10-31 07:43:03 -06002547 self.assertEqual(b'BinM', hdr_data[:4])
Simon Glasscec34ba2019-07-08 14:25:28 -06002548 offset = struct.unpack('<I', hdr_data[4:])[0]
2549 self.assertEqual(fdtmap_pos, offset)
2550
2551 def testHeaderMissingFdtmap(self):
2552 """Test an image header requires an fdtmap"""
2553 with self.assertRaises(ValueError) as e:
2554 self.data = self._DoReadFileRealDtb('119_fdtmap_hdr_missing.dts')
2555 self.assertIn("'image_header' section must have an 'fdtmap' sibling",
2556 str(e.exception))
2557
2558 def testHeaderNoLocation(self):
2559 """Test an image header with a no specified location is detected"""
2560 with self.assertRaises(ValueError) as e:
2561 self.data = self._DoReadFileRealDtb('120_hdr_no_location.dts')
2562 self.assertIn("Invalid location 'None', expected 'start' or 'end'",
2563 str(e.exception))
2564
Simon Glasse61b6f62019-07-08 14:25:37 -06002565 def testEntryExpand(self):
Simon Glassdd156a42022-03-05 20:18:59 -07002566 """Test extending an entry after it is packed"""
2567 data = self._DoReadFile('121_entry_extend.dts')
Simon Glass8c702fb2019-07-20 12:23:57 -06002568 self.assertEqual(b'aaa', data[:3])
2569 self.assertEqual(U_BOOT_DATA, data[3:3 + len(U_BOOT_DATA)])
2570 self.assertEqual(b'aaa', data[-3:])
Simon Glasse61b6f62019-07-08 14:25:37 -06002571
Simon Glassdd156a42022-03-05 20:18:59 -07002572 def testEntryExtendBad(self):
2573 """Test extending an entry after it is packed, twice"""
Simon Glasse61b6f62019-07-08 14:25:37 -06002574 with self.assertRaises(ValueError) as e:
Simon Glassdd156a42022-03-05 20:18:59 -07002575 self._DoReadFile('122_entry_extend_twice.dts')
Simon Glass9d8ee322019-07-20 12:23:58 -06002576 self.assertIn("Image '/binman': Entries changed size after packing",
Simon Glasse61b6f62019-07-08 14:25:37 -06002577 str(e.exception))
2578
Simon Glassdd156a42022-03-05 20:18:59 -07002579 def testEntryExtendSection(self):
2580 """Test extending an entry within a section after it is packed"""
2581 data = self._DoReadFile('123_entry_extend_section.dts')
Simon Glass8c702fb2019-07-20 12:23:57 -06002582 self.assertEqual(b'aaa', data[:3])
2583 self.assertEqual(U_BOOT_DATA, data[3:3 + len(U_BOOT_DATA)])
2584 self.assertEqual(b'aaa', data[-3:])
Simon Glasse61b6f62019-07-08 14:25:37 -06002585
Simon Glass90d29682019-07-08 14:25:38 -06002586 def testCompressDtb(self):
2587 """Test that compress of device-tree files is supported"""
2588 self._CheckLz4()
2589 data = self.data = self._DoReadFileRealDtb('124_compress_dtb.dts')
2590 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
2591 comp_data = data[len(U_BOOT_DATA):]
2592 orig = self._decompress(comp_data)
2593 dtb = fdt.Fdt.FromData(orig)
2594 dtb.Scan()
2595 props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
2596 expected = {
2597 'u-boot:size': len(U_BOOT_DATA),
2598 'u-boot-dtb:uncomp-size': len(orig),
2599 'u-boot-dtb:size': len(comp_data),
2600 'size': len(data),
2601 }
2602 self.assertEqual(expected, props)
2603
Simon Glass151bbbf2019-07-08 14:25:41 -06002604 def testCbfsUpdateFdt(self):
2605 """Test that we can update the device tree with CBFS offset/size info"""
2606 self._CheckLz4()
2607 data, _, _, out_dtb_fname = self._DoReadFileDtb('125_cbfs_update.dts',
2608 update_dtb=True)
2609 dtb = fdt.Fdt(out_dtb_fname)
2610 dtb.Scan()
Simon Glass2c6adba2019-07-20 12:23:47 -06002611 props = self._GetPropTree(dtb, BASE_DTB_PROPS + ['uncomp-size'])
Simon Glass151bbbf2019-07-08 14:25:41 -06002612 del props['cbfs/u-boot:size']
2613 self.assertEqual({
2614 'offset': 0,
2615 'size': len(data),
2616 'image-pos': 0,
2617 'cbfs:offset': 0,
2618 'cbfs:size': len(data),
2619 'cbfs:image-pos': 0,
2620 'cbfs/u-boot:offset': 0x38,
2621 'cbfs/u-boot:uncomp-size': len(U_BOOT_DATA),
2622 'cbfs/u-boot:image-pos': 0x38,
2623 'cbfs/u-boot-dtb:offset': 0xb8,
2624 'cbfs/u-boot-dtb:size': len(U_BOOT_DATA),
2625 'cbfs/u-boot-dtb:image-pos': 0xb8,
2626 }, props)
2627
Simon Glass3c9b4f22019-07-08 14:25:42 -06002628 def testCbfsBadType(self):
2629 """Test an image header with a no specified location is detected"""
2630 with self.assertRaises(ValueError) as e:
2631 self._DoReadFile('126_cbfs_bad_type.dts')
2632 self.assertIn("Unknown cbfs-type 'badtype'", str(e.exception))
2633
Simon Glass6b156f82019-07-08 14:25:43 -06002634 def testList(self):
2635 """Test listing the files in an image"""
2636 self._CheckLz4()
2637 data = self._DoReadFile('127_list.dts')
2638 image = control.images['image']
2639 entries = image.BuildEntryList()
2640 self.assertEqual(7, len(entries))
2641
2642 ent = entries[0]
2643 self.assertEqual(0, ent.indent)
Simon Glass49cd2b32023-02-07 14:34:18 -07002644 self.assertEqual('image', ent.name)
Simon Glass6b156f82019-07-08 14:25:43 -06002645 self.assertEqual('section', ent.etype)
2646 self.assertEqual(len(data), ent.size)
2647 self.assertEqual(0, ent.image_pos)
2648 self.assertEqual(None, ent.uncomp_size)
Simon Glass39dd2152019-07-08 14:25:47 -06002649 self.assertEqual(0, ent.offset)
Simon Glass6b156f82019-07-08 14:25:43 -06002650
2651 ent = entries[1]
2652 self.assertEqual(1, ent.indent)
2653 self.assertEqual('u-boot', ent.name)
2654 self.assertEqual('u-boot', ent.etype)
2655 self.assertEqual(len(U_BOOT_DATA), ent.size)
2656 self.assertEqual(0, ent.image_pos)
2657 self.assertEqual(None, ent.uncomp_size)
2658 self.assertEqual(0, ent.offset)
2659
2660 ent = entries[2]
2661 self.assertEqual(1, ent.indent)
2662 self.assertEqual('section', ent.name)
2663 self.assertEqual('section', ent.etype)
2664 section_size = ent.size
2665 self.assertEqual(0x100, ent.image_pos)
2666 self.assertEqual(None, ent.uncomp_size)
Simon Glass39dd2152019-07-08 14:25:47 -06002667 self.assertEqual(0x100, ent.offset)
Simon Glass6b156f82019-07-08 14:25:43 -06002668
2669 ent = entries[3]
2670 self.assertEqual(2, ent.indent)
2671 self.assertEqual('cbfs', ent.name)
2672 self.assertEqual('cbfs', ent.etype)
2673 self.assertEqual(0x400, ent.size)
2674 self.assertEqual(0x100, ent.image_pos)
2675 self.assertEqual(None, ent.uncomp_size)
2676 self.assertEqual(0, ent.offset)
2677
2678 ent = entries[4]
2679 self.assertEqual(3, ent.indent)
2680 self.assertEqual('u-boot', ent.name)
2681 self.assertEqual('u-boot', ent.etype)
2682 self.assertEqual(len(U_BOOT_DATA), ent.size)
2683 self.assertEqual(0x138, ent.image_pos)
2684 self.assertEqual(None, ent.uncomp_size)
2685 self.assertEqual(0x38, ent.offset)
2686
2687 ent = entries[5]
2688 self.assertEqual(3, ent.indent)
2689 self.assertEqual('u-boot-dtb', ent.name)
2690 self.assertEqual('text', ent.etype)
2691 self.assertGreater(len(COMPRESS_DATA), ent.size)
2692 self.assertEqual(0x178, ent.image_pos)
2693 self.assertEqual(len(COMPRESS_DATA), ent.uncomp_size)
2694 self.assertEqual(0x78, ent.offset)
2695
2696 ent = entries[6]
2697 self.assertEqual(2, ent.indent)
2698 self.assertEqual('u-boot-dtb', ent.name)
2699 self.assertEqual('u-boot-dtb', ent.etype)
2700 self.assertEqual(0x500, ent.image_pos)
2701 self.assertEqual(len(U_BOOT_DTB_DATA), ent.uncomp_size)
2702 dtb_size = ent.size
2703 # Compressing this data expands it since headers are added
2704 self.assertGreater(dtb_size, len(U_BOOT_DTB_DATA))
2705 self.assertEqual(0x400, ent.offset)
2706
2707 self.assertEqual(len(data), 0x100 + section_size)
2708 self.assertEqual(section_size, 0x400 + dtb_size)
2709
Simon Glass8d8bf4e2019-07-08 14:25:44 -06002710 def testFindFdtmap(self):
2711 """Test locating an FDT map in an image"""
2712 self._CheckLz4()
2713 data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
2714 image = control.images['image']
2715 entries = image.GetEntries()
2716 entry = entries['fdtmap']
2717 self.assertEqual(entry.image_pos, fdtmap.LocateFdtmap(data))
2718
2719 def testFindFdtmapMissing(self):
2720 """Test failing to locate an FDP map"""
2721 data = self._DoReadFile('005_simple.dts')
2722 self.assertEqual(None, fdtmap.LocateFdtmap(data))
2723
Simon Glassed39a3c2019-07-08 14:25:45 -06002724 def testFindImageHeader(self):
2725 """Test locating a image header"""
2726 self._CheckLz4()
Simon Glassb8424fa2019-07-08 14:25:46 -06002727 data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
Simon Glassed39a3c2019-07-08 14:25:45 -06002728 image = control.images['image']
2729 entries = image.GetEntries()
2730 entry = entries['fdtmap']
2731 # The header should point to the FDT map
2732 self.assertEqual(entry.image_pos, image_header.LocateHeaderOffset(data))
2733
2734 def testFindImageHeaderStart(self):
2735 """Test locating a image header located at the start of an image"""
Simon Glassb8424fa2019-07-08 14:25:46 -06002736 data = self.data = self._DoReadFileRealDtb('117_fdtmap_hdr_start.dts')
Simon Glassed39a3c2019-07-08 14:25:45 -06002737 image = control.images['image']
2738 entries = image.GetEntries()
2739 entry = entries['fdtmap']
2740 # The header should point to the FDT map
2741 self.assertEqual(entry.image_pos, image_header.LocateHeaderOffset(data))
2742
2743 def testFindImageHeaderMissing(self):
2744 """Test failing to locate an image header"""
2745 data = self._DoReadFile('005_simple.dts')
2746 self.assertEqual(None, image_header.LocateHeaderOffset(data))
2747
Simon Glassb8424fa2019-07-08 14:25:46 -06002748 def testReadImage(self):
2749 """Test reading an image and accessing its FDT map"""
2750 self._CheckLz4()
2751 data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
Simon Glass80025522022-01-29 14:14:04 -07002752 image_fname = tools.get_output_filename('image.bin')
Simon Glassb8424fa2019-07-08 14:25:46 -06002753 orig_image = control.images['image']
2754 image = Image.FromFile(image_fname)
2755 self.assertEqual(orig_image.GetEntries().keys(),
2756 image.GetEntries().keys())
2757
2758 orig_entry = orig_image.GetEntries()['fdtmap']
2759 entry = image.GetEntries()['fdtmap']
2760 self.assertEquals(orig_entry.offset, entry.offset)
2761 self.assertEquals(orig_entry.size, entry.size)
2762 self.assertEquals(orig_entry.image_pos, entry.image_pos)
2763
2764 def testReadImageNoHeader(self):
2765 """Test accessing an image's FDT map without an image header"""
2766 self._CheckLz4()
2767 data = self._DoReadFileRealDtb('129_decode_image_nohdr.dts')
Simon Glass80025522022-01-29 14:14:04 -07002768 image_fname = tools.get_output_filename('image.bin')
Simon Glassb8424fa2019-07-08 14:25:46 -06002769 image = Image.FromFile(image_fname)
2770 self.assertTrue(isinstance(image, Image))
Simon Glass072959a2019-07-20 12:23:50 -06002771 self.assertEqual('image', image.image_name[-5:])
Simon Glassb8424fa2019-07-08 14:25:46 -06002772
2773 def testReadImageFail(self):
2774 """Test failing to read an image image's FDT map"""
2775 self._DoReadFile('005_simple.dts')
Simon Glass80025522022-01-29 14:14:04 -07002776 image_fname = tools.get_output_filename('image.bin')
Simon Glassb8424fa2019-07-08 14:25:46 -06002777 with self.assertRaises(ValueError) as e:
2778 image = Image.FromFile(image_fname)
2779 self.assertIn("Cannot find FDT map in image", str(e.exception))
Simon Glassc2f1aed2019-07-08 13:18:56 -06002780
Simon Glassb2fd11d2019-07-08 14:25:48 -06002781 def testListCmd(self):
2782 """Test listing the files in an image using an Fdtmap"""
2783 self._CheckLz4()
2784 data = self._DoReadFileRealDtb('130_list_fdtmap.dts')
2785
2786 # lz4 compression size differs depending on the version
2787 image = control.images['image']
2788 entries = image.GetEntries()
2789 section_size = entries['section'].size
2790 fdt_size = entries['section'].GetEntries()['u-boot-dtb'].size
2791 fdtmap_offset = entries['fdtmap'].offset
2792
Simon Glassb3d6fc72019-07-20 12:24:10 -06002793 try:
2794 tmpdir, updated_fname = self._SetupImageInTmpdir()
2795 with test_util.capture_sys_output() as (stdout, stderr):
2796 self._DoBinman('ls', '-i', updated_fname)
2797 finally:
2798 shutil.rmtree(tmpdir)
Simon Glassb2fd11d2019-07-08 14:25:48 -06002799 lines = stdout.getvalue().splitlines()
2800 expected = [
2801'Name Image-pos Size Entry-type Offset Uncomp-size',
2802'----------------------------------------------------------------------',
Simon Glass49cd2b32023-02-07 14:34:18 -07002803'image 0 c00 section 0',
Simon Glassb2fd11d2019-07-08 14:25:48 -06002804' u-boot 0 4 u-boot 0',
2805' section 100 %x section 100' % section_size,
2806' cbfs 100 400 cbfs 0',
2807' u-boot 138 4 u-boot 38',
Simon Glassc5fd10a2019-10-31 07:43:03 -06002808' u-boot-dtb 180 105 u-boot-dtb 80 3c9',
Simon Glassb2fd11d2019-07-08 14:25:48 -06002809' u-boot-dtb 500 %x u-boot-dtb 400 3c9' % fdt_size,
Simon Glassc5fd10a2019-10-31 07:43:03 -06002810' fdtmap %x 3bd fdtmap %x' %
Simon Glassb2fd11d2019-07-08 14:25:48 -06002811 (fdtmap_offset, fdtmap_offset),
2812' image-header bf8 8 image-header bf8',
2813 ]
2814 self.assertEqual(expected, lines)
2815
2816 def testListCmdFail(self):
2817 """Test failing to list an image"""
2818 self._DoReadFile('005_simple.dts')
Simon Glassb3d6fc72019-07-20 12:24:10 -06002819 try:
2820 tmpdir, updated_fname = self._SetupImageInTmpdir()
2821 with self.assertRaises(ValueError) as e:
2822 self._DoBinman('ls', '-i', updated_fname)
2823 finally:
2824 shutil.rmtree(tmpdir)
Simon Glassb2fd11d2019-07-08 14:25:48 -06002825 self.assertIn("Cannot find FDT map in image", str(e.exception))
2826
2827 def _RunListCmd(self, paths, expected):
2828 """List out entries and check the result
2829
2830 Args:
2831 paths: List of paths to pass to the list command
2832 expected: Expected list of filenames to be returned, in order
2833 """
2834 self._CheckLz4()
2835 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07002836 image_fname = tools.get_output_filename('image.bin')
Simon Glassb2fd11d2019-07-08 14:25:48 -06002837 image = Image.FromFile(image_fname)
2838 lines = image.GetListEntries(paths)[1]
2839 files = [line[0].strip() for line in lines[1:]]
2840 self.assertEqual(expected, files)
2841
2842 def testListCmdSection(self):
2843 """Test listing the files in a section"""
2844 self._RunListCmd(['section'],
2845 ['section', 'cbfs', 'u-boot', 'u-boot-dtb', 'u-boot-dtb'])
2846
2847 def testListCmdFile(self):
2848 """Test listing a particular file"""
2849 self._RunListCmd(['*u-boot-dtb'], ['u-boot-dtb', 'u-boot-dtb'])
2850
2851 def testListCmdWildcard(self):
2852 """Test listing a wildcarded file"""
2853 self._RunListCmd(['*boot*'],
2854 ['u-boot', 'u-boot', 'u-boot-dtb', 'u-boot-dtb'])
2855
2856 def testListCmdWildcardMulti(self):
2857 """Test listing a wildcarded file"""
2858 self._RunListCmd(['*cb*', '*head*'],
2859 ['cbfs', 'u-boot', 'u-boot-dtb', 'image-header'])
2860
2861 def testListCmdEmpty(self):
2862 """Test listing a wildcarded file"""
2863 self._RunListCmd(['nothing'], [])
2864
2865 def testListCmdPath(self):
2866 """Test listing the files in a sub-entry of a section"""
2867 self._RunListCmd(['section/cbfs'], ['cbfs', 'u-boot', 'u-boot-dtb'])
2868
Simon Glass4c613bf2019-07-08 14:25:50 -06002869 def _RunExtractCmd(self, entry_name, decomp=True):
2870 """Extract an entry from an image
2871
2872 Args:
2873 entry_name: Entry name to extract
2874 decomp: True to decompress the data if compressed, False to leave
2875 it in its raw uncompressed format
2876
2877 Returns:
2878 data from entry
2879 """
2880 self._CheckLz4()
2881 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07002882 image_fname = tools.get_output_filename('image.bin')
Simon Glass4c613bf2019-07-08 14:25:50 -06002883 return control.ReadEntry(image_fname, entry_name, decomp)
2884
2885 def testExtractSimple(self):
2886 """Test extracting a single file"""
2887 data = self._RunExtractCmd('u-boot')
2888 self.assertEqual(U_BOOT_DATA, data)
2889
Simon Glass980a2842019-07-08 14:25:52 -06002890 def testExtractSection(self):
2891 """Test extracting the files in a section"""
2892 data = self._RunExtractCmd('section')
2893 cbfs_data = data[:0x400]
2894 cbfs = cbfs_util.CbfsReader(cbfs_data)
Simon Glassc5fd10a2019-10-31 07:43:03 -06002895 self.assertEqual(['u-boot', 'u-boot-dtb', ''], list(cbfs.files.keys()))
Simon Glass980a2842019-07-08 14:25:52 -06002896 dtb_data = data[0x400:]
2897 dtb = self._decompress(dtb_data)
2898 self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
2899
2900 def testExtractCompressed(self):
2901 """Test extracting compressed data"""
2902 data = self._RunExtractCmd('section/u-boot-dtb')
2903 self.assertEqual(EXTRACT_DTB_SIZE, len(data))
2904
2905 def testExtractRaw(self):
2906 """Test extracting compressed data without decompressing it"""
2907 data = self._RunExtractCmd('section/u-boot-dtb', decomp=False)
2908 dtb = self._decompress(data)
2909 self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
2910
2911 def testExtractCbfs(self):
2912 """Test extracting CBFS data"""
2913 data = self._RunExtractCmd('section/cbfs/u-boot')
2914 self.assertEqual(U_BOOT_DATA, data)
2915
2916 def testExtractCbfsCompressed(self):
2917 """Test extracting CBFS compressed data"""
2918 data = self._RunExtractCmd('section/cbfs/u-boot-dtb')
2919 self.assertEqual(EXTRACT_DTB_SIZE, len(data))
2920
2921 def testExtractCbfsRaw(self):
2922 """Test extracting CBFS compressed data without decompressing it"""
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +02002923 bintool = self.comp_bintools['lzma_alone']
2924 self._CheckBintool(bintool)
Simon Glass980a2842019-07-08 14:25:52 -06002925 data = self._RunExtractCmd('section/cbfs/u-boot-dtb', decomp=False)
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +02002926 dtb = bintool.decompress(data)
Simon Glass980a2842019-07-08 14:25:52 -06002927 self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
2928
Simon Glass4c613bf2019-07-08 14:25:50 -06002929 def testExtractBadEntry(self):
2930 """Test extracting a bad section path"""
2931 with self.assertRaises(ValueError) as e:
2932 self._RunExtractCmd('section/does-not-exist')
2933 self.assertIn("Entry 'does-not-exist' not found in '/section'",
2934 str(e.exception))
2935
2936 def testExtractMissingFile(self):
2937 """Test extracting file that does not exist"""
2938 with self.assertRaises(IOError) as e:
2939 control.ReadEntry('missing-file', 'name')
2940
2941 def testExtractBadFile(self):
2942 """Test extracting an invalid file"""
2943 fname = os.path.join(self._indir, 'badfile')
Simon Glass80025522022-01-29 14:14:04 -07002944 tools.write_file(fname, b'')
Simon Glass4c613bf2019-07-08 14:25:50 -06002945 with self.assertRaises(ValueError) as e:
2946 control.ReadEntry(fname, 'name')
2947
Simon Glass980a2842019-07-08 14:25:52 -06002948 def testExtractCmd(self):
2949 """Test extracting a file fron an image on the command line"""
2950 self._CheckLz4()
2951 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass980a2842019-07-08 14:25:52 -06002952 fname = os.path.join(self._indir, 'output.extact')
Simon Glassb3d6fc72019-07-20 12:24:10 -06002953 try:
2954 tmpdir, updated_fname = self._SetupImageInTmpdir()
2955 with test_util.capture_sys_output() as (stdout, stderr):
2956 self._DoBinman('extract', '-i', updated_fname, 'u-boot',
2957 '-f', fname)
2958 finally:
2959 shutil.rmtree(tmpdir)
Simon Glass80025522022-01-29 14:14:04 -07002960 data = tools.read_file(fname)
Simon Glass980a2842019-07-08 14:25:52 -06002961 self.assertEqual(U_BOOT_DATA, data)
2962
2963 def testExtractOneEntry(self):
2964 """Test extracting a single entry fron an image """
2965 self._CheckLz4()
2966 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07002967 image_fname = tools.get_output_filename('image.bin')
Simon Glass980a2842019-07-08 14:25:52 -06002968 fname = os.path.join(self._indir, 'output.extact')
2969 control.ExtractEntries(image_fname, fname, None, ['u-boot'])
Simon Glass80025522022-01-29 14:14:04 -07002970 data = tools.read_file(fname)
Simon Glass980a2842019-07-08 14:25:52 -06002971 self.assertEqual(U_BOOT_DATA, data)
2972
2973 def _CheckExtractOutput(self, decomp):
2974 """Helper to test file output with and without decompression
2975
2976 Args:
2977 decomp: True to decompress entry data, False to output it raw
2978 """
2979 def _CheckPresent(entry_path, expect_data, expect_size=None):
2980 """Check and remove expected file
2981
2982 This checks the data/size of a file and removes the file both from
2983 the outfiles set and from the output directory. Once all files are
2984 processed, both the set and directory should be empty.
2985
2986 Args:
2987 entry_path: Entry path
2988 expect_data: Data to expect in file, or None to skip check
2989 expect_size: Size of data to expect in file, or None to skip
2990 """
2991 path = os.path.join(outdir, entry_path)
Simon Glass80025522022-01-29 14:14:04 -07002992 data = tools.read_file(path)
Simon Glass980a2842019-07-08 14:25:52 -06002993 os.remove(path)
2994 if expect_data:
2995 self.assertEqual(expect_data, data)
2996 elif expect_size:
2997 self.assertEqual(expect_size, len(data))
2998 outfiles.remove(path)
2999
3000 def _CheckDirPresent(name):
3001 """Remove expected directory
3002
3003 This gives an error if the directory does not exist as expected
3004
3005 Args:
3006 name: Name of directory to remove
3007 """
3008 path = os.path.join(outdir, name)
3009 os.rmdir(path)
3010
3011 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07003012 image_fname = tools.get_output_filename('image.bin')
Simon Glass980a2842019-07-08 14:25:52 -06003013 outdir = os.path.join(self._indir, 'extract')
3014 einfos = control.ExtractEntries(image_fname, None, outdir, [], decomp)
3015
3016 # Create a set of all file that were output (should be 9)
3017 outfiles = set()
3018 for root, dirs, files in os.walk(outdir):
3019 outfiles |= set([os.path.join(root, fname) for fname in files])
3020 self.assertEqual(9, len(outfiles))
3021 self.assertEqual(9, len(einfos))
3022
3023 image = control.images['image']
3024 entries = image.GetEntries()
3025
3026 # Check the 9 files in various ways
3027 section = entries['section']
3028 section_entries = section.GetEntries()
3029 cbfs_entries = section_entries['cbfs'].GetEntries()
3030 _CheckPresent('u-boot', U_BOOT_DATA)
3031 _CheckPresent('section/cbfs/u-boot', U_BOOT_DATA)
3032 dtb_len = EXTRACT_DTB_SIZE
3033 if not decomp:
3034 dtb_len = cbfs_entries['u-boot-dtb'].size
3035 _CheckPresent('section/cbfs/u-boot-dtb', None, dtb_len)
3036 if not decomp:
3037 dtb_len = section_entries['u-boot-dtb'].size
3038 _CheckPresent('section/u-boot-dtb', None, dtb_len)
3039
3040 fdtmap = entries['fdtmap']
3041 _CheckPresent('fdtmap', fdtmap.data)
3042 hdr = entries['image-header']
3043 _CheckPresent('image-header', hdr.data)
3044
3045 _CheckPresent('section/root', section.data)
3046 cbfs = section_entries['cbfs']
3047 _CheckPresent('section/cbfs/root', cbfs.data)
Simon Glass80025522022-01-29 14:14:04 -07003048 data = tools.read_file(image_fname)
Simon Glass980a2842019-07-08 14:25:52 -06003049 _CheckPresent('root', data)
3050
3051 # There should be no files left. Remove all the directories to check.
3052 # If there are any files/dirs remaining, one of these checks will fail.
3053 self.assertEqual(0, len(outfiles))
3054 _CheckDirPresent('section/cbfs')
3055 _CheckDirPresent('section')
3056 _CheckDirPresent('')
3057 self.assertFalse(os.path.exists(outdir))
3058
3059 def testExtractAllEntries(self):
3060 """Test extracting all entries"""
3061 self._CheckLz4()
3062 self._CheckExtractOutput(decomp=True)
3063
3064 def testExtractAllEntriesRaw(self):
3065 """Test extracting all entries without decompressing them"""
3066 self._CheckLz4()
3067 self._CheckExtractOutput(decomp=False)
3068
3069 def testExtractSelectedEntries(self):
3070 """Test extracting some entries"""
3071 self._CheckLz4()
3072 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07003073 image_fname = tools.get_output_filename('image.bin')
Simon Glass980a2842019-07-08 14:25:52 -06003074 outdir = os.path.join(self._indir, 'extract')
3075 einfos = control.ExtractEntries(image_fname, None, outdir,
3076 ['*cb*', '*head*'])
3077
3078 # File output is tested by testExtractAllEntries(), so just check that
3079 # the expected entries are selected
3080 names = [einfo.name for einfo in einfos]
3081 self.assertEqual(names,
3082 ['cbfs', 'u-boot', 'u-boot-dtb', 'image-header'])
3083
3084 def testExtractNoEntryPaths(self):
3085 """Test extracting some entries"""
3086 self._CheckLz4()
3087 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07003088 image_fname = tools.get_output_filename('image.bin')
Simon Glass980a2842019-07-08 14:25:52 -06003089 with self.assertRaises(ValueError) as e:
3090 control.ExtractEntries(image_fname, 'fname', None, [])
Simon Glassa772d3f2019-07-20 12:24:14 -06003091 self.assertIn('Must specify an entry path to write with -f',
Simon Glass980a2842019-07-08 14:25:52 -06003092 str(e.exception))
3093
3094 def testExtractTooManyEntryPaths(self):
3095 """Test extracting some entries"""
3096 self._CheckLz4()
3097 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass80025522022-01-29 14:14:04 -07003098 image_fname = tools.get_output_filename('image.bin')
Simon Glass980a2842019-07-08 14:25:52 -06003099 with self.assertRaises(ValueError) as e:
3100 control.ExtractEntries(image_fname, 'fname', None, ['a', 'b'])
Simon Glassa772d3f2019-07-20 12:24:14 -06003101 self.assertIn('Must specify exactly one entry path to write with -f',
Simon Glass980a2842019-07-08 14:25:52 -06003102 str(e.exception))
3103
Simon Glass52d06212019-07-08 14:25:53 -06003104 def testPackAlignSection(self):
3105 """Test that sections can have alignment"""
3106 self._DoReadFile('131_pack_align_section.dts')
3107
3108 self.assertIn('image', control.images)
3109 image = control.images['image']
3110 entries = image.GetEntries()
3111 self.assertEqual(3, len(entries))
3112
3113 # First u-boot
3114 self.assertIn('u-boot', entries)
3115 entry = entries['u-boot']
3116 self.assertEqual(0, entry.offset)
3117 self.assertEqual(0, entry.image_pos)
3118 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3119 self.assertEqual(len(U_BOOT_DATA), entry.size)
3120
3121 # Section0
3122 self.assertIn('section0', entries)
3123 section0 = entries['section0']
3124 self.assertEqual(0x10, section0.offset)
3125 self.assertEqual(0x10, section0.image_pos)
3126 self.assertEqual(len(U_BOOT_DATA), section0.size)
3127
3128 # Second u-boot
3129 section_entries = section0.GetEntries()
3130 self.assertIn('u-boot', section_entries)
3131 entry = section_entries['u-boot']
3132 self.assertEqual(0, entry.offset)
3133 self.assertEqual(0x10, entry.image_pos)
3134 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3135 self.assertEqual(len(U_BOOT_DATA), entry.size)
3136
3137 # Section1
3138 self.assertIn('section1', entries)
3139 section1 = entries['section1']
3140 self.assertEqual(0x14, section1.offset)
3141 self.assertEqual(0x14, section1.image_pos)
3142 self.assertEqual(0x20, section1.size)
3143
3144 # Second u-boot
3145 section_entries = section1.GetEntries()
3146 self.assertIn('u-boot', section_entries)
3147 entry = section_entries['u-boot']
3148 self.assertEqual(0, entry.offset)
3149 self.assertEqual(0x14, entry.image_pos)
3150 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3151 self.assertEqual(len(U_BOOT_DATA), entry.size)
3152
3153 # Section2
3154 self.assertIn('section2', section_entries)
3155 section2 = section_entries['section2']
3156 self.assertEqual(0x4, section2.offset)
3157 self.assertEqual(0x18, section2.image_pos)
3158 self.assertEqual(4, section2.size)
3159
3160 # Third u-boot
3161 section_entries = section2.GetEntries()
3162 self.assertIn('u-boot', section_entries)
3163 entry = section_entries['u-boot']
3164 self.assertEqual(0, entry.offset)
3165 self.assertEqual(0x18, entry.image_pos)
3166 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3167 self.assertEqual(len(U_BOOT_DATA), entry.size)
3168
Simon Glassf8a54bc2019-07-20 12:23:56 -06003169 def _RunReplaceCmd(self, entry_name, data, decomp=True, allow_resize=True,
3170 dts='132_replace.dts'):
Simon Glass072959a2019-07-20 12:23:50 -06003171 """Replace an entry in an image
3172
3173 This writes the entry data to update it, then opens the updated file and
3174 returns the value that it now finds there.
3175
3176 Args:
3177 entry_name: Entry name to replace
3178 data: Data to replace it with
3179 decomp: True to compress the data if needed, False if data is
3180 already compressed so should be used as is
Simon Glassf8a54bc2019-07-20 12:23:56 -06003181 allow_resize: True to allow entries to change size, False to raise
3182 an exception
Simon Glass072959a2019-07-20 12:23:50 -06003183
3184 Returns:
3185 Tuple:
3186 data from entry
3187 data from fdtmap (excluding header)
Simon Glassf8a54bc2019-07-20 12:23:56 -06003188 Image object that was modified
Simon Glass072959a2019-07-20 12:23:50 -06003189 """
Simon Glassf8a54bc2019-07-20 12:23:56 -06003190 dtb_data = self._DoReadFileDtb(dts, use_real_dtb=True,
Simon Glass072959a2019-07-20 12:23:50 -06003191 update_dtb=True)[1]
3192
3193 self.assertIn('image', control.images)
3194 image = control.images['image']
3195 entries = image.GetEntries()
3196 orig_dtb_data = entries['u-boot-dtb'].data
3197 orig_fdtmap_data = entries['fdtmap'].data
3198
Simon Glass80025522022-01-29 14:14:04 -07003199 image_fname = tools.get_output_filename('image.bin')
3200 updated_fname = tools.get_output_filename('image-updated.bin')
3201 tools.write_file(updated_fname, tools.read_file(image_fname))
Simon Glassf8a54bc2019-07-20 12:23:56 -06003202 image = control.WriteEntry(updated_fname, entry_name, data, decomp,
3203 allow_resize)
Simon Glass072959a2019-07-20 12:23:50 -06003204 data = control.ReadEntry(updated_fname, entry_name, decomp)
3205
Simon Glassf8a54bc2019-07-20 12:23:56 -06003206 # The DT data should not change unless resized:
3207 if not allow_resize:
3208 new_dtb_data = entries['u-boot-dtb'].data
3209 self.assertEqual(new_dtb_data, orig_dtb_data)
3210 new_fdtmap_data = entries['fdtmap'].data
3211 self.assertEqual(new_fdtmap_data, orig_fdtmap_data)
Simon Glass072959a2019-07-20 12:23:50 -06003212
Simon Glassf8a54bc2019-07-20 12:23:56 -06003213 return data, orig_fdtmap_data[fdtmap.FDTMAP_HDR_LEN:], image
Simon Glass072959a2019-07-20 12:23:50 -06003214
3215 def testReplaceSimple(self):
3216 """Test replacing a single file"""
3217 expected = b'x' * len(U_BOOT_DATA)
Simon Glassf8a54bc2019-07-20 12:23:56 -06003218 data, expected_fdtmap, _ = self._RunReplaceCmd('u-boot', expected,
3219 allow_resize=False)
Simon Glass072959a2019-07-20 12:23:50 -06003220 self.assertEqual(expected, data)
3221
3222 # Test that the state looks right. There should be an FDT for the fdtmap
3223 # that we jsut read back in, and it should match what we find in the
3224 # 'control' tables. Checking for an FDT that does not exist should
3225 # return None.
3226 path, fdtmap = state.GetFdtContents('fdtmap')
Simon Glassf8a54bc2019-07-20 12:23:56 -06003227 self.assertIsNotNone(path)
Simon Glass072959a2019-07-20 12:23:50 -06003228 self.assertEqual(expected_fdtmap, fdtmap)
3229
3230 dtb = state.GetFdtForEtype('fdtmap')
3231 self.assertEqual(dtb.GetContents(), fdtmap)
3232
3233 missing_path, missing_fdtmap = state.GetFdtContents('missing')
3234 self.assertIsNone(missing_path)
3235 self.assertIsNone(missing_fdtmap)
3236
3237 missing_dtb = state.GetFdtForEtype('missing')
3238 self.assertIsNone(missing_dtb)
3239
3240 self.assertEqual('/binman', state.fdt_path_prefix)
3241
3242 def testReplaceResizeFail(self):
3243 """Test replacing a file by something larger"""
3244 expected = U_BOOT_DATA + b'x'
3245 with self.assertRaises(ValueError) as e:
Simon Glassf8a54bc2019-07-20 12:23:56 -06003246 self._RunReplaceCmd('u-boot', expected, allow_resize=False,
3247 dts='139_replace_repack.dts')
Simon Glass072959a2019-07-20 12:23:50 -06003248 self.assertIn("Node '/u-boot': Entry data size does not match, but resize is disabled",
3249 str(e.exception))
3250
3251 def testReplaceMulti(self):
3252 """Test replacing entry data where multiple images are generated"""
3253 data = self._DoReadFileDtb('133_replace_multi.dts', use_real_dtb=True,
3254 update_dtb=True)[0]
3255 expected = b'x' * len(U_BOOT_DATA)
Simon Glass80025522022-01-29 14:14:04 -07003256 updated_fname = tools.get_output_filename('image-updated.bin')
3257 tools.write_file(updated_fname, data)
Simon Glass072959a2019-07-20 12:23:50 -06003258 entry_name = 'u-boot'
Simon Glassf8a54bc2019-07-20 12:23:56 -06003259 control.WriteEntry(updated_fname, entry_name, expected,
3260 allow_resize=False)
Simon Glass072959a2019-07-20 12:23:50 -06003261 data = control.ReadEntry(updated_fname, entry_name)
3262 self.assertEqual(expected, data)
3263
3264 # Check the state looks right.
3265 self.assertEqual('/binman/image', state.fdt_path_prefix)
3266
3267 # Now check we can write the first image
Simon Glass80025522022-01-29 14:14:04 -07003268 image_fname = tools.get_output_filename('first-image.bin')
3269 updated_fname = tools.get_output_filename('first-updated.bin')
3270 tools.write_file(updated_fname, tools.read_file(image_fname))
Simon Glass072959a2019-07-20 12:23:50 -06003271 entry_name = 'u-boot'
Simon Glassf8a54bc2019-07-20 12:23:56 -06003272 control.WriteEntry(updated_fname, entry_name, expected,
3273 allow_resize=False)
Simon Glass072959a2019-07-20 12:23:50 -06003274 data = control.ReadEntry(updated_fname, entry_name)
3275 self.assertEqual(expected, data)
3276
3277 # Check the state looks right.
3278 self.assertEqual('/binman/first-image', state.fdt_path_prefix)
Simon Glass39dd2152019-07-08 14:25:47 -06003279
Simon Glassfb30e292019-07-20 12:23:51 -06003280 def testUpdateFdtAllRepack(self):
3281 """Test that all device trees are updated with offset/size info"""
3282 data = self._DoReadFileRealDtb('134_fdt_update_all_repack.dts')
3283 SECTION_SIZE = 0x300
3284 DTB_SIZE = 602
3285 FDTMAP_SIZE = 608
3286 base_expected = {
3287 'offset': 0,
3288 'size': SECTION_SIZE + DTB_SIZE * 2 + FDTMAP_SIZE,
3289 'image-pos': 0,
3290 'section:offset': 0,
3291 'section:size': SECTION_SIZE,
3292 'section:image-pos': 0,
3293 'section/u-boot-dtb:offset': 4,
3294 'section/u-boot-dtb:size': 636,
3295 'section/u-boot-dtb:image-pos': 4,
3296 'u-boot-spl-dtb:offset': SECTION_SIZE,
3297 'u-boot-spl-dtb:size': DTB_SIZE,
3298 'u-boot-spl-dtb:image-pos': SECTION_SIZE,
3299 'u-boot-tpl-dtb:offset': SECTION_SIZE + DTB_SIZE,
3300 'u-boot-tpl-dtb:image-pos': SECTION_SIZE + DTB_SIZE,
3301 'u-boot-tpl-dtb:size': DTB_SIZE,
3302 'fdtmap:offset': SECTION_SIZE + DTB_SIZE * 2,
3303 'fdtmap:size': FDTMAP_SIZE,
3304 'fdtmap:image-pos': SECTION_SIZE + DTB_SIZE * 2,
3305 }
3306 main_expected = {
3307 'section:orig-size': SECTION_SIZE,
3308 'section/u-boot-dtb:orig-offset': 4,
3309 }
3310
3311 # We expect three device-tree files in the output, with the first one
3312 # within a fixed-size section.
3313 # Read them in sequence. We look for an 'spl' property in the SPL tree,
3314 # and 'tpl' in the TPL tree, to make sure they are distinct from the
3315 # main U-Boot tree. All three should have the same positions and offset
3316 # except that the main tree should include the main_expected properties
3317 start = 4
3318 for item in ['', 'spl', 'tpl', None]:
3319 if item is None:
3320 start += 16 # Move past fdtmap header
3321 dtb = fdt.Fdt.FromData(data[start:])
3322 dtb.Scan()
3323 props = self._GetPropTree(dtb,
3324 BASE_DTB_PROPS + REPACK_DTB_PROPS + ['spl', 'tpl'],
3325 prefix='/' if item is None else '/binman/')
3326 expected = dict(base_expected)
3327 if item:
3328 expected[item] = 0
3329 else:
3330 # Main DTB and fdtdec should include the 'orig-' properties
3331 expected.update(main_expected)
3332 # Helpful for debugging:
3333 #for prop in sorted(props):
3334 #print('prop %s %s %s' % (prop, props[prop], expected[prop]))
3335 self.assertEqual(expected, props)
3336 if item == '':
3337 start = SECTION_SIZE
3338 else:
3339 start += dtb._fdt_obj.totalsize()
3340
Simon Glass11453762019-07-20 12:23:55 -06003341 def testFdtmapHeaderMiddle(self):
3342 """Test an FDT map in the middle of an image when it should be at end"""
3343 with self.assertRaises(ValueError) as e:
3344 self._DoReadFileRealDtb('135_fdtmap_hdr_middle.dts')
3345 self.assertIn("Invalid sibling order 'middle' for image-header: Must be at 'end' to match location",
3346 str(e.exception))
3347
3348 def testFdtmapHeaderStartBad(self):
3349 """Test an FDT map in middle of an image when it should be at start"""
3350 with self.assertRaises(ValueError) as e:
3351 self._DoReadFileRealDtb('136_fdtmap_hdr_startbad.dts')
3352 self.assertIn("Invalid sibling order 'end' for image-header: Must be at 'start' to match location",
3353 str(e.exception))
3354
3355 def testFdtmapHeaderEndBad(self):
3356 """Test an FDT map at the start of an image when it should be at end"""
3357 with self.assertRaises(ValueError) as e:
3358 self._DoReadFileRealDtb('137_fdtmap_hdr_endbad.dts')
3359 self.assertIn("Invalid sibling order 'start' for image-header: Must be at 'end' to match location",
3360 str(e.exception))
3361
3362 def testFdtmapHeaderNoSize(self):
3363 """Test an image header at the end of an image with undefined size"""
3364 self._DoReadFileRealDtb('138_fdtmap_hdr_nosize.dts')
3365
Simon Glassf8a54bc2019-07-20 12:23:56 -06003366 def testReplaceResize(self):
3367 """Test replacing a single file in an entry with a larger file"""
3368 expected = U_BOOT_DATA + b'x'
3369 data, _, image = self._RunReplaceCmd('u-boot', expected,
3370 dts='139_replace_repack.dts')
3371 self.assertEqual(expected, data)
3372
3373 entries = image.GetEntries()
3374 dtb_data = entries['u-boot-dtb'].data
3375 dtb = fdt.Fdt.FromData(dtb_data)
3376 dtb.Scan()
3377
3378 # The u-boot section should now be larger in the dtb
3379 node = dtb.GetNode('/binman/u-boot')
3380 self.assertEqual(len(expected), fdt_util.GetInt(node, 'size'))
3381
3382 # Same for the fdtmap
3383 fdata = entries['fdtmap'].data
3384 fdtb = fdt.Fdt.FromData(fdata[fdtmap.FDTMAP_HDR_LEN:])
3385 fdtb.Scan()
3386 fnode = fdtb.GetNode('/u-boot')
3387 self.assertEqual(len(expected), fdt_util.GetInt(fnode, 'size'))
3388
3389 def testReplaceResizeNoRepack(self):
3390 """Test replacing an entry with a larger file when not allowed"""
3391 expected = U_BOOT_DATA + b'x'
3392 with self.assertRaises(ValueError) as e:
3393 self._RunReplaceCmd('u-boot', expected)
3394 self.assertIn('Entry data size does not match, but allow-repack is not present for this image',
3395 str(e.exception))
3396
Simon Glass9d8ee322019-07-20 12:23:58 -06003397 def testEntryShrink(self):
3398 """Test contracting an entry after it is packed"""
3399 try:
3400 state.SetAllowEntryContraction(True)
3401 data = self._DoReadFileDtb('140_entry_shrink.dts',
3402 update_dtb=True)[0]
3403 finally:
3404 state.SetAllowEntryContraction(False)
3405 self.assertEqual(b'a', data[:1])
3406 self.assertEqual(U_BOOT_DATA, data[1:1 + len(U_BOOT_DATA)])
3407 self.assertEqual(b'a', data[-1:])
3408
3409 def testEntryShrinkFail(self):
3410 """Test not being allowed to contract an entry after it is packed"""
3411 data = self._DoReadFileDtb('140_entry_shrink.dts', update_dtb=True)[0]
3412
3413 # In this case there is a spare byte at the end of the data. The size of
3414 # the contents is only 1 byte but we still have the size before it
3415 # shrunk.
3416 self.assertEqual(b'a\0', data[:2])
3417 self.assertEqual(U_BOOT_DATA, data[2:2 + len(U_BOOT_DATA)])
3418 self.assertEqual(b'a\0', data[-2:])
3419
Simon Glass70e32982019-07-20 12:24:01 -06003420 def testDescriptorOffset(self):
3421 """Test that the Intel descriptor is always placed at at the start"""
3422 data = self._DoReadFileDtb('141_descriptor_offset.dts')
3423 image = control.images['image']
3424 entries = image.GetEntries()
3425 desc = entries['intel-descriptor']
3426 self.assertEqual(0xff800000, desc.offset);
3427 self.assertEqual(0xff800000, desc.image_pos);
3428
Simon Glass37fdd142019-07-20 12:24:06 -06003429 def testReplaceCbfs(self):
3430 """Test replacing a single file in CBFS without changing the size"""
3431 self._CheckLz4()
3432 expected = b'x' * len(U_BOOT_DATA)
3433 data = self._DoReadFileRealDtb('142_replace_cbfs.dts')
Simon Glass80025522022-01-29 14:14:04 -07003434 updated_fname = tools.get_output_filename('image-updated.bin')
3435 tools.write_file(updated_fname, data)
Simon Glass37fdd142019-07-20 12:24:06 -06003436 entry_name = 'section/cbfs/u-boot'
3437 control.WriteEntry(updated_fname, entry_name, expected,
3438 allow_resize=True)
3439 data = control.ReadEntry(updated_fname, entry_name)
3440 self.assertEqual(expected, data)
3441
3442 def testReplaceResizeCbfs(self):
3443 """Test replacing a single file in CBFS with one of a different size"""
3444 self._CheckLz4()
3445 expected = U_BOOT_DATA + b'x'
3446 data = self._DoReadFileRealDtb('142_replace_cbfs.dts')
Simon Glass80025522022-01-29 14:14:04 -07003447 updated_fname = tools.get_output_filename('image-updated.bin')
3448 tools.write_file(updated_fname, data)
Simon Glass37fdd142019-07-20 12:24:06 -06003449 entry_name = 'section/cbfs/u-boot'
3450 control.WriteEntry(updated_fname, entry_name, expected,
3451 allow_resize=True)
3452 data = control.ReadEntry(updated_fname, entry_name)
3453 self.assertEqual(expected, data)
3454
Simon Glass30033c22019-07-20 12:24:15 -06003455 def _SetupForReplace(self):
3456 """Set up some files to use to replace entries
3457
3458 This generates an image, copies it to a new file, extracts all the files
3459 in it and updates some of them
3460
3461 Returns:
3462 List
3463 Image filename
3464 Output directory
3465 Expected values for updated entries, each a string
3466 """
3467 data = self._DoReadFileRealDtb('143_replace_all.dts')
3468
Simon Glass80025522022-01-29 14:14:04 -07003469 updated_fname = tools.get_output_filename('image-updated.bin')
3470 tools.write_file(updated_fname, data)
Simon Glass30033c22019-07-20 12:24:15 -06003471
3472 outdir = os.path.join(self._indir, 'extract')
3473 einfos = control.ExtractEntries(updated_fname, None, outdir, [])
3474
3475 expected1 = b'x' + U_BOOT_DATA + b'y'
3476 u_boot_fname1 = os.path.join(outdir, 'u-boot')
Simon Glass80025522022-01-29 14:14:04 -07003477 tools.write_file(u_boot_fname1, expected1)
Simon Glass30033c22019-07-20 12:24:15 -06003478
3479 expected2 = b'a' + U_BOOT_DATA + b'b'
3480 u_boot_fname2 = os.path.join(outdir, 'u-boot2')
Simon Glass80025522022-01-29 14:14:04 -07003481 tools.write_file(u_boot_fname2, expected2)
Simon Glass30033c22019-07-20 12:24:15 -06003482
3483 expected_text = b'not the same text'
3484 text_fname = os.path.join(outdir, 'text')
Simon Glass80025522022-01-29 14:14:04 -07003485 tools.write_file(text_fname, expected_text)
Simon Glass30033c22019-07-20 12:24:15 -06003486
3487 dtb_fname = os.path.join(outdir, 'u-boot-dtb')
3488 dtb = fdt.FdtScan(dtb_fname)
3489 node = dtb.GetNode('/binman/text')
3490 node.AddString('my-property', 'the value')
3491 dtb.Sync(auto_resize=True)
3492 dtb.Flush()
3493
3494 return updated_fname, outdir, expected1, expected2, expected_text
3495
3496 def _CheckReplaceMultiple(self, entry_paths):
3497 """Handle replacing the contents of multiple entries
3498
3499 Args:
3500 entry_paths: List of entry paths to replace
3501
3502 Returns:
3503 List
3504 Dict of entries in the image:
3505 key: Entry name
3506 Value: Entry object
3507 Expected values for updated entries, each a string
3508 """
3509 updated_fname, outdir, expected1, expected2, expected_text = (
3510 self._SetupForReplace())
3511 control.ReplaceEntries(updated_fname, None, outdir, entry_paths)
3512
3513 image = Image.FromFile(updated_fname)
3514 image.LoadData()
3515 return image.GetEntries(), expected1, expected2, expected_text
3516
3517 def testReplaceAll(self):
3518 """Test replacing the contents of all entries"""
3519 entries, expected1, expected2, expected_text = (
3520 self._CheckReplaceMultiple([]))
3521 data = entries['u-boot'].data
3522 self.assertEqual(expected1, data)
3523
3524 data = entries['u-boot2'].data
3525 self.assertEqual(expected2, data)
3526
3527 data = entries['text'].data
3528 self.assertEqual(expected_text, data)
3529
3530 # Check that the device tree is updated
3531 data = entries['u-boot-dtb'].data
3532 dtb = fdt.Fdt.FromData(data)
3533 dtb.Scan()
3534 node = dtb.GetNode('/binman/text')
3535 self.assertEqual('the value', node.props['my-property'].value)
3536
3537 def testReplaceSome(self):
3538 """Test replacing the contents of a few entries"""
3539 entries, expected1, expected2, expected_text = (
3540 self._CheckReplaceMultiple(['u-boot2', 'text']))
3541
3542 # This one should not change
3543 data = entries['u-boot'].data
3544 self.assertEqual(U_BOOT_DATA, data)
3545
3546 data = entries['u-boot2'].data
3547 self.assertEqual(expected2, data)
3548
3549 data = entries['text'].data
3550 self.assertEqual(expected_text, data)
3551
3552 def testReplaceCmd(self):
3553 """Test replacing a file fron an image on the command line"""
3554 self._DoReadFileRealDtb('143_replace_all.dts')
3555
3556 try:
3557 tmpdir, updated_fname = self._SetupImageInTmpdir()
3558
3559 fname = os.path.join(tmpdir, 'update-u-boot.bin')
3560 expected = b'x' * len(U_BOOT_DATA)
Simon Glass80025522022-01-29 14:14:04 -07003561 tools.write_file(fname, expected)
Simon Glass30033c22019-07-20 12:24:15 -06003562
3563 self._DoBinman('replace', '-i', updated_fname, 'u-boot', '-f', fname)
Simon Glass80025522022-01-29 14:14:04 -07003564 data = tools.read_file(updated_fname)
Simon Glass30033c22019-07-20 12:24:15 -06003565 self.assertEqual(expected, data[:len(expected)])
3566 map_fname = os.path.join(tmpdir, 'image-updated.map')
3567 self.assertFalse(os.path.exists(map_fname))
3568 finally:
3569 shutil.rmtree(tmpdir)
3570
3571 def testReplaceCmdSome(self):
3572 """Test replacing some files fron an image on the command line"""
3573 updated_fname, outdir, expected1, expected2, expected_text = (
3574 self._SetupForReplace())
3575
3576 self._DoBinman('replace', '-i', updated_fname, '-I', outdir,
3577 'u-boot2', 'text')
3578
Simon Glass80025522022-01-29 14:14:04 -07003579 tools.prepare_output_dir(None)
Simon Glass30033c22019-07-20 12:24:15 -06003580 image = Image.FromFile(updated_fname)
3581 image.LoadData()
3582 entries = image.GetEntries()
3583
3584 # This one should not change
3585 data = entries['u-boot'].data
3586 self.assertEqual(U_BOOT_DATA, data)
3587
3588 data = entries['u-boot2'].data
3589 self.assertEqual(expected2, data)
3590
3591 data = entries['text'].data
3592 self.assertEqual(expected_text, data)
3593
3594 def testReplaceMissing(self):
3595 """Test replacing entries where the file is missing"""
3596 updated_fname, outdir, expected1, expected2, expected_text = (
3597 self._SetupForReplace())
3598
3599 # Remove one of the files, to generate a warning
3600 u_boot_fname1 = os.path.join(outdir, 'u-boot')
3601 os.remove(u_boot_fname1)
3602
3603 with test_util.capture_sys_output() as (stdout, stderr):
3604 control.ReplaceEntries(updated_fname, None, outdir, [])
3605 self.assertIn("Skipping entry '/u-boot' from missing file",
Simon Glass6e02f7c2020-07-09 18:39:39 -06003606 stderr.getvalue())
Simon Glass30033c22019-07-20 12:24:15 -06003607
3608 def testReplaceCmdMap(self):
3609 """Test replacing a file fron an image on the command line"""
3610 self._DoReadFileRealDtb('143_replace_all.dts')
3611
3612 try:
3613 tmpdir, updated_fname = self._SetupImageInTmpdir()
3614
3615 fname = os.path.join(self._indir, 'update-u-boot.bin')
3616 expected = b'x' * len(U_BOOT_DATA)
Simon Glass80025522022-01-29 14:14:04 -07003617 tools.write_file(fname, expected)
Simon Glass30033c22019-07-20 12:24:15 -06003618
3619 self._DoBinman('replace', '-i', updated_fname, 'u-boot',
3620 '-f', fname, '-m')
3621 map_fname = os.path.join(tmpdir, 'image-updated.map')
3622 self.assertTrue(os.path.exists(map_fname))
3623 finally:
3624 shutil.rmtree(tmpdir)
3625
3626 def testReplaceNoEntryPaths(self):
3627 """Test replacing an entry without an entry path"""
3628 self._DoReadFileRealDtb('143_replace_all.dts')
Simon Glass80025522022-01-29 14:14:04 -07003629 image_fname = tools.get_output_filename('image.bin')
Simon Glass30033c22019-07-20 12:24:15 -06003630 with self.assertRaises(ValueError) as e:
3631 control.ReplaceEntries(image_fname, 'fname', None, [])
3632 self.assertIn('Must specify an entry path to read with -f',
3633 str(e.exception))
3634
3635 def testReplaceTooManyEntryPaths(self):
3636 """Test extracting some entries"""
3637 self._DoReadFileRealDtb('143_replace_all.dts')
Simon Glass80025522022-01-29 14:14:04 -07003638 image_fname = tools.get_output_filename('image.bin')
Simon Glass30033c22019-07-20 12:24:15 -06003639 with self.assertRaises(ValueError) as e:
3640 control.ReplaceEntries(image_fname, 'fname', None, ['a', 'b'])
3641 self.assertIn('Must specify exactly one entry path to write with -f',
3642 str(e.exception))
3643
Simon Glass0b074d62019-08-24 07:22:48 -06003644 def testPackReset16(self):
3645 """Test that an image with an x86 reset16 region can be created"""
3646 data = self._DoReadFile('144_x86_reset16.dts')
3647 self.assertEqual(X86_RESET16_DATA, data[:len(X86_RESET16_DATA)])
3648
3649 def testPackReset16Spl(self):
3650 """Test that an image with an x86 reset16-spl region can be created"""
3651 data = self._DoReadFile('145_x86_reset16_spl.dts')
3652 self.assertEqual(X86_RESET16_SPL_DATA, data[:len(X86_RESET16_SPL_DATA)])
3653
3654 def testPackReset16Tpl(self):
3655 """Test that an image with an x86 reset16-tpl region can be created"""
3656 data = self._DoReadFile('146_x86_reset16_tpl.dts')
3657 self.assertEqual(X86_RESET16_TPL_DATA, data[:len(X86_RESET16_TPL_DATA)])
3658
Simon Glass232f90c2019-08-24 07:22:50 -06003659 def testPackIntelFit(self):
3660 """Test that an image with an Intel FIT and pointer can be created"""
3661 data = self._DoReadFile('147_intel_fit.dts')
3662 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
3663 fit = data[16:32];
3664 self.assertEqual(b'_FIT_ \x01\x00\x00\x00\x00\x01\x80}' , fit)
3665 ptr = struct.unpack('<i', data[0x40:0x44])[0]
3666
3667 image = control.images['image']
3668 entries = image.GetEntries()
3669 expected_ptr = entries['intel-fit'].image_pos - (1 << 32)
3670 self.assertEqual(expected_ptr, ptr)
3671
3672 def testPackIntelFitMissing(self):
3673 """Test detection of a FIT pointer with not FIT region"""
3674 with self.assertRaises(ValueError) as e:
3675 self._DoReadFile('148_intel_fit_missing.dts')
3676 self.assertIn("'intel-fit-ptr' section must have an 'intel-fit' sibling",
3677 str(e.exception))
3678
Simon Glass72555fa2019-11-06 17:22:44 -07003679 def _CheckSymbolsTplSection(self, dts, expected_vals):
3680 data = self._DoReadFile(dts)
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003681 sym_values = struct.pack('<LLQLL', elf.BINMAN_SYM_MAGIC_VALUE, *expected_vals)
Simon Glass3eb5b202019-08-24 07:23:00 -06003682 upto1 = 4 + len(U_BOOT_SPL_DATA)
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003683 expected1 = tools.get_bytes(0xff, 4) + sym_values + U_BOOT_SPL_DATA[24:]
Simon Glass3eb5b202019-08-24 07:23:00 -06003684 self.assertEqual(expected1, data[:upto1])
3685
3686 upto2 = upto1 + 1 + len(U_BOOT_SPL_DATA)
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003687 expected2 = tools.get_bytes(0xff, 1) + sym_values + U_BOOT_SPL_DATA[24:]
Simon Glass3eb5b202019-08-24 07:23:00 -06003688 self.assertEqual(expected2, data[upto1:upto2])
3689
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003690 upto3 = 0x3c + len(U_BOOT_DATA)
Simon Glass80025522022-01-29 14:14:04 -07003691 expected3 = tools.get_bytes(0xff, 1) + U_BOOT_DATA
Simon Glass3eb5b202019-08-24 07:23:00 -06003692 self.assertEqual(expected3, data[upto2:upto3])
3693
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003694 expected4 = sym_values + U_BOOT_TPL_DATA[24:]
Simon Glass72555fa2019-11-06 17:22:44 -07003695 self.assertEqual(expected4, data[upto3:upto3 + len(U_BOOT_TPL_DATA)])
3696
3697 def testSymbolsTplSection(self):
3698 """Test binman can assign symbols embedded in U-Boot TPL in a section"""
3699 self._SetupSplElf('u_boot_binman_syms')
3700 self._SetupTplElf('u_boot_binman_syms')
3701 self._CheckSymbolsTplSection('149_symbols_tpl.dts',
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003702 [0x04, 0x20, 0x10 + 0x3c, 0x04])
Simon Glass72555fa2019-11-06 17:22:44 -07003703
3704 def testSymbolsTplSectionX86(self):
3705 """Test binman can assign symbols in a section with end-at-4gb"""
3706 self._SetupSplElf('u_boot_binman_syms_x86')
3707 self._SetupTplElf('u_boot_binman_syms_x86')
3708 self._CheckSymbolsTplSection('155_symbols_tpl_x86.dts',
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03003709 [0xffffff04, 0xffffff20, 0xffffff3c,
Simon Glass72555fa2019-11-06 17:22:44 -07003710 0x04])
Simon Glass3eb5b202019-08-24 07:23:00 -06003711
Simon Glass98c59572019-08-24 07:23:03 -06003712 def testPackX86RomIfwiSectiom(self):
3713 """Test that a section can be placed in an IFWI region"""
3714 self._SetupIfwi('fitimage.bin')
3715 data = self._DoReadFile('151_x86_rom_ifwi_section.dts')
3716 self._CheckIfwi(data)
3717
Simon Glassba7985d2019-08-24 07:23:07 -06003718 def testPackFspM(self):
3719 """Test that an image with a FSP memory-init binary can be created"""
3720 data = self._DoReadFile('152_intel_fsp_m.dts')
3721 self.assertEqual(FSP_M_DATA, data[:len(FSP_M_DATA)])
3722
Simon Glass4d9086d2019-10-20 21:31:35 -06003723 def testPackFspS(self):
3724 """Test that an image with a FSP silicon-init binary can be created"""
3725 data = self._DoReadFile('153_intel_fsp_s.dts')
3726 self.assertEqual(FSP_S_DATA, data[:len(FSP_S_DATA)])
Simon Glassba7985d2019-08-24 07:23:07 -06003727
Simon Glass9ea87b22019-10-20 21:31:36 -06003728 def testPackFspT(self):
3729 """Test that an image with a FSP temp-ram-init binary can be created"""
3730 data = self._DoReadFile('154_intel_fsp_t.dts')
3731 self.assertEqual(FSP_T_DATA, data[:len(FSP_T_DATA)])
3732
Simon Glass48f3aad2020-07-09 18:39:31 -06003733 def testMkimage(self):
3734 """Test using mkimage to build an image"""
3735 data = self._DoReadFile('156_mkimage.dts')
3736
3737 # Just check that the data appears in the file somewhere
3738 self.assertIn(U_BOOT_SPL_DATA, data)
3739
Simon Glass66152ce2022-01-09 20:14:09 -07003740 def testMkimageMissing(self):
3741 """Test that binman still produces an image if mkimage is missing"""
3742 with test_util.capture_sys_output() as (_, stderr):
3743 self._DoTestFile('156_mkimage.dts',
3744 force_missing_bintools='mkimage')
3745 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07003746 self.assertRegex(err, "Image 'image'.*missing bintools.*: mkimage")
Simon Glass66152ce2022-01-09 20:14:09 -07003747
Simon Glass5e560182020-07-09 18:39:36 -06003748 def testExtblob(self):
3749 """Test an image with an external blob"""
3750 data = self._DoReadFile('157_blob_ext.dts')
3751 self.assertEqual(REFCODE_DATA, data)
3752
3753 def testExtblobMissing(self):
3754 """Test an image with a missing external blob"""
3755 with self.assertRaises(ValueError) as e:
3756 self._DoReadFile('158_blob_ext_missing.dts')
3757 self.assertIn("Filename 'missing-file' not found in input path",
3758 str(e.exception))
3759
Simon Glass5d94cc62020-07-09 18:39:38 -06003760 def testExtblobMissingOk(self):
3761 """Test an image with an missing external blob that is allowed"""
Simon Glassa003cd32020-07-09 18:39:40 -06003762 with test_util.capture_sys_output() as (stdout, stderr):
Simon Glass6bce5dc2022-11-09 19:14:42 -07003763 ret = self._DoTestFile('158_blob_ext_missing.dts',
3764 allow_missing=True)
3765 self.assertEqual(103, ret)
Simon Glassa003cd32020-07-09 18:39:40 -06003766 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07003767 self.assertRegex(err, "Image 'image'.*missing.*: blob-ext")
Simon Glass6bce5dc2022-11-09 19:14:42 -07003768 self.assertIn('Some images are invalid', err)
3769
3770 def testExtblobMissingOkFlag(self):
3771 """Test an image with an missing external blob allowed with -W"""
3772 with test_util.capture_sys_output() as (stdout, stderr):
3773 ret = self._DoTestFile('158_blob_ext_missing.dts',
3774 allow_missing=True, ignore_missing=True)
3775 self.assertEqual(0, ret)
3776 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07003777 self.assertRegex(err, "Image 'image'.*missing.*: blob-ext")
Simon Glass6bce5dc2022-11-09 19:14:42 -07003778 self.assertIn('Some images are invalid', err)
Simon Glassa003cd32020-07-09 18:39:40 -06003779
3780 def testExtblobMissingOkSect(self):
3781 """Test an image with an missing external blob that is allowed"""
3782 with test_util.capture_sys_output() as (stdout, stderr):
3783 self._DoTestFile('159_blob_ext_missing_sect.dts',
3784 allow_missing=True)
3785 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07003786 self.assertRegex(err, "Image 'image'.*missing.*: blob-ext blob-ext2")
Simon Glass5d94cc62020-07-09 18:39:38 -06003787
Simon Glasse88cef92020-07-09 18:39:41 -06003788 def testPackX86RomMeMissingDesc(self):
3789 """Test that an missing Intel descriptor entry is allowed"""
Simon Glasse88cef92020-07-09 18:39:41 -06003790 with test_util.capture_sys_output() as (stdout, stderr):
Simon Glass14c596c2020-07-25 15:11:19 -06003791 self._DoTestFile('164_x86_rom_me_missing.dts', allow_missing=True)
Simon Glasse88cef92020-07-09 18:39:41 -06003792 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07003793 self.assertRegex(err, "Image 'image'.*missing.*: intel-descriptor")
Simon Glasse88cef92020-07-09 18:39:41 -06003794
3795 def testPackX86RomMissingIfwi(self):
3796 """Test that an x86 ROM with Integrated Firmware Image can be created"""
3797 self._SetupIfwi('fitimage.bin')
3798 pathname = os.path.join(self._indir, 'fitimage.bin')
3799 os.remove(pathname)
3800 with test_util.capture_sys_output() as (stdout, stderr):
3801 self._DoTestFile('111_x86_rom_ifwi.dts', allow_missing=True)
3802 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07003803 self.assertRegex(err, "Image 'image'.*missing.*: intel-ifwi")
Simon Glasse88cef92020-07-09 18:39:41 -06003804
Simon Glass2a0fa982022-02-11 13:23:21 -07003805 def testPackOverlapZero(self):
Simon Glassd70829a2020-07-09 18:39:42 -06003806 """Test that zero-size overlapping regions are ignored"""
3807 self._DoTestFile('160_pack_overlap_zero.dts')
3808
Alper Nebi Yasak09fb0612022-02-08 01:08:04 +03003809 def _CheckSimpleFitData(self, fit_data, kernel_data, fdt1_data):
Simon Glass45d556d2020-07-09 18:39:45 -06003810 # The data should be inside the FIT
3811 dtb = fdt.Fdt.FromData(fit_data)
3812 dtb.Scan()
3813 fnode = dtb.GetNode('/images/kernel')
3814 self.assertIn('data', fnode.props)
3815
3816 fname = os.path.join(self._indir, 'fit_data.fit')
Simon Glass80025522022-01-29 14:14:04 -07003817 tools.write_file(fname, fit_data)
3818 out = tools.run('dumpimage', '-l', fname)
Simon Glass45d556d2020-07-09 18:39:45 -06003819
3820 # Check a few features to make sure the plumbing works. We don't need
3821 # to test the operation of mkimage or dumpimage here. First convert the
3822 # output into a dict where the keys are the fields printed by dumpimage
3823 # and the values are a list of values for each field
3824 lines = out.splitlines()
3825
3826 # Converts "Compression: gzip compressed" into two groups:
3827 # 'Compression' and 'gzip compressed'
3828 re_line = re.compile(r'^ *([^:]*)(?:: *(.*))?$')
3829 vals = collections.defaultdict(list)
3830 for line in lines:
3831 mat = re_line.match(line)
3832 vals[mat.group(1)].append(mat.group(2))
3833
3834 self.assertEquals('FIT description: test-desc', lines[0])
3835 self.assertIn('Created:', lines[1])
3836 self.assertIn('Image 0 (kernel)', vals)
3837 self.assertIn('Hash value', vals)
3838 data_sizes = vals.get('Data Size')
3839 self.assertIsNotNone(data_sizes)
3840 self.assertEqual(2, len(data_sizes))
3841 # Format is "4 Bytes = 0.00 KiB = 0.00 MiB" so take the first word
Alper Nebi Yasak09fb0612022-02-08 01:08:04 +03003842 self.assertEqual(len(kernel_data), int(data_sizes[0].split()[0]))
3843 self.assertEqual(len(fdt1_data), int(data_sizes[1].split()[0]))
3844
Alper Nebi Yasak1a0ee0f2022-03-27 18:31:47 +03003845 # Check if entry listing correctly omits /images/
3846 image = control.images['image']
3847 fit_entry = image.GetEntries()['fit']
3848 subentries = list(fit_entry.GetEntries().keys())
3849 expected = ['kernel', 'fdt-1']
3850 self.assertEqual(expected, subentries)
3851
Alper Nebi Yasak09fb0612022-02-08 01:08:04 +03003852 def testSimpleFit(self):
3853 """Test an image with a FIT inside"""
3854 data = self._DoReadFile('161_fit.dts')
3855 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
3856 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
3857 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
3858
3859 self._CheckSimpleFitData(fit_data, U_BOOT_DATA, U_BOOT_SPL_DTB_DATA)
3860
3861 def testSimpleFitExpandsSubentries(self):
3862 """Test that FIT images expand their subentries"""
3863 data = self._DoReadFileDtb('161_fit.dts', use_expanded=True)[0]
3864 self.assertEqual(U_BOOT_EXP_DATA, data[:len(U_BOOT_EXP_DATA)])
3865 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
3866 fit_data = data[len(U_BOOT_EXP_DATA):-len(U_BOOT_NODTB_DATA)]
3867
3868 self._CheckSimpleFitData(fit_data, U_BOOT_EXP_DATA, U_BOOT_SPL_DTB_DATA)
Simon Glass45d556d2020-07-09 18:39:45 -06003869
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03003870 def testSimpleFitImagePos(self):
3871 """Test that we have correct image-pos for FIT subentries"""
3872 data, _, _, out_dtb_fname = self._DoReadFileDtb('161_fit.dts',
3873 update_dtb=True)
3874 dtb = fdt.Fdt(out_dtb_fname)
3875 dtb.Scan()
3876 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
3877
Simon Glassb7bad182022-03-05 20:19:01 -07003878 self.maxDiff = None
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03003879 self.assertEqual({
3880 'image-pos': 0,
3881 'offset': 0,
3882 'size': 1890,
3883
3884 'u-boot:image-pos': 0,
3885 'u-boot:offset': 0,
3886 'u-boot:size': 4,
3887
3888 'fit:image-pos': 4,
3889 'fit:offset': 4,
3890 'fit:size': 1840,
3891
Simon Glassb7bad182022-03-05 20:19:01 -07003892 'fit/images/kernel:image-pos': 304,
3893 'fit/images/kernel:offset': 300,
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03003894 'fit/images/kernel:size': 4,
3895
Simon Glassb7bad182022-03-05 20:19:01 -07003896 'fit/images/kernel/u-boot:image-pos': 304,
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03003897 'fit/images/kernel/u-boot:offset': 0,
3898 'fit/images/kernel/u-boot:size': 4,
3899
Simon Glassb7bad182022-03-05 20:19:01 -07003900 'fit/images/fdt-1:image-pos': 552,
3901 'fit/images/fdt-1:offset': 548,
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03003902 'fit/images/fdt-1:size': 6,
3903
Simon Glassb7bad182022-03-05 20:19:01 -07003904 'fit/images/fdt-1/u-boot-spl-dtb:image-pos': 552,
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03003905 'fit/images/fdt-1/u-boot-spl-dtb:offset': 0,
3906 'fit/images/fdt-1/u-boot-spl-dtb:size': 6,
3907
3908 'u-boot-nodtb:image-pos': 1844,
3909 'u-boot-nodtb:offset': 1844,
3910 'u-boot-nodtb:size': 46,
3911 }, props)
3912
3913 # Actually check the data is where we think it is
3914 for node, expected in [
3915 ("u-boot", U_BOOT_DATA),
3916 ("fit/images/kernel", U_BOOT_DATA),
3917 ("fit/images/kernel/u-boot", U_BOOT_DATA),
3918 ("fit/images/fdt-1", U_BOOT_SPL_DTB_DATA),
3919 ("fit/images/fdt-1/u-boot-spl-dtb", U_BOOT_SPL_DTB_DATA),
3920 ("u-boot-nodtb", U_BOOT_NODTB_DATA),
3921 ]:
3922 image_pos = props[f"{node}:image-pos"]
3923 size = props[f"{node}:size"]
3924 self.assertEqual(len(expected), size)
3925 self.assertEqual(expected, data[image_pos:image_pos+size])
3926
Simon Glass45d556d2020-07-09 18:39:45 -06003927 def testFitExternal(self):
Simon Glass31ee50f2020-09-01 05:13:55 -06003928 """Test an image with an FIT with external images"""
Simon Glass45d556d2020-07-09 18:39:45 -06003929 data = self._DoReadFile('162_fit_external.dts')
3930 fit_data = data[len(U_BOOT_DATA):-2] # _testing is 2 bytes
3931
Simon Glass7932c882022-01-09 20:13:39 -07003932 # Size of the external-data region as set up by mkimage
3933 external_data_size = len(U_BOOT_DATA) + 2
3934 expected_size = (len(U_BOOT_DATA) + 0x400 +
Simon Glass80025522022-01-29 14:14:04 -07003935 tools.align(external_data_size, 4) +
Simon Glass7932c882022-01-09 20:13:39 -07003936 len(U_BOOT_NODTB_DATA))
3937
Simon Glass45d556d2020-07-09 18:39:45 -06003938 # The data should be outside the FIT
3939 dtb = fdt.Fdt.FromData(fit_data)
3940 dtb.Scan()
3941 fnode = dtb.GetNode('/images/kernel')
3942 self.assertNotIn('data', fnode.props)
Simon Glass7932c882022-01-09 20:13:39 -07003943 self.assertEqual(len(U_BOOT_DATA),
3944 fdt_util.fdt32_to_cpu(fnode.props['data-size'].value))
3945 fit_pos = 0x400;
3946 self.assertEqual(
3947 fit_pos,
3948 fdt_util.fdt32_to_cpu(fnode.props['data-position'].value))
3949
3950 self.assertEquals(expected_size, len(data))
3951 actual_pos = len(U_BOOT_DATA) + fit_pos
3952 self.assertEqual(U_BOOT_DATA + b'aa',
3953 data[actual_pos:actual_pos + external_data_size])
Simon Glassfb30e292019-07-20 12:23:51 -06003954
Alper Nebi Yasakac873ed2022-02-08 01:08:08 +03003955 def testFitExternalImagePos(self):
3956 """Test that we have correct image-pos for external FIT subentries"""
3957 data, _, _, out_dtb_fname = self._DoReadFileDtb('162_fit_external.dts',
3958 update_dtb=True)
3959 dtb = fdt.Fdt(out_dtb_fname)
3960 dtb.Scan()
3961 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
3962
3963 self.assertEqual({
3964 'image-pos': 0,
3965 'offset': 0,
3966 'size': 1082,
3967
3968 'u-boot:image-pos': 0,
3969 'u-boot:offset': 0,
3970 'u-boot:size': 4,
3971
3972 'fit:size': 1032,
3973 'fit:offset': 4,
3974 'fit:image-pos': 4,
3975
3976 'fit/images/kernel:size': 4,
3977 'fit/images/kernel:offset': 1024,
3978 'fit/images/kernel:image-pos': 1028,
3979
3980 'fit/images/kernel/u-boot:size': 4,
3981 'fit/images/kernel/u-boot:offset': 0,
3982 'fit/images/kernel/u-boot:image-pos': 1028,
3983
3984 'fit/images/fdt-1:size': 2,
3985 'fit/images/fdt-1:offset': 1028,
3986 'fit/images/fdt-1:image-pos': 1032,
3987
3988 'fit/images/fdt-1/_testing:size': 2,
3989 'fit/images/fdt-1/_testing:offset': 0,
3990 'fit/images/fdt-1/_testing:image-pos': 1032,
3991
3992 'u-boot-nodtb:image-pos': 1036,
3993 'u-boot-nodtb:offset': 1036,
3994 'u-boot-nodtb:size': 46,
3995 }, props)
3996
3997 # Actually check the data is where we think it is
3998 for node, expected in [
3999 ("u-boot", U_BOOT_DATA),
4000 ("fit/images/kernel", U_BOOT_DATA),
4001 ("fit/images/kernel/u-boot", U_BOOT_DATA),
4002 ("fit/images/fdt-1", b'aa'),
4003 ("fit/images/fdt-1/_testing", b'aa'),
4004 ("u-boot-nodtb", U_BOOT_NODTB_DATA),
4005 ]:
4006 image_pos = props[f"{node}:image-pos"]
4007 size = props[f"{node}:size"]
4008 self.assertEqual(len(expected), size)
4009 self.assertEqual(expected, data[image_pos:image_pos+size])
4010
Simon Glass66152ce2022-01-09 20:14:09 -07004011 def testFitMissing(self):
Simon Glass039d65f2023-03-02 17:02:43 -07004012 """Test that binman complains if mkimage is missing"""
4013 with self.assertRaises(ValueError) as e:
4014 self._DoTestFile('162_fit_external.dts',
4015 force_missing_bintools='mkimage')
4016 self.assertIn("Node '/binman/fit': Missing tool: 'mkimage'",
4017 str(e.exception))
4018
4019 def testFitMissingOK(self):
Simon Glass66152ce2022-01-09 20:14:09 -07004020 """Test that binman still produces a FIT image if mkimage is missing"""
4021 with test_util.capture_sys_output() as (_, stderr):
Simon Glass039d65f2023-03-02 17:02:43 -07004022 self._DoTestFile('162_fit_external.dts', allow_missing=True,
Simon Glass66152ce2022-01-09 20:14:09 -07004023 force_missing_bintools='mkimage')
4024 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07004025 self.assertRegex(err, "Image 'image'.*missing bintools.*: mkimage")
Simon Glass66152ce2022-01-09 20:14:09 -07004026
Alper Nebi Yasak6aae2392020-08-31 12:58:18 +03004027 def testSectionIgnoreHashSignature(self):
4028 """Test that sections ignore hash, signature nodes for its data"""
4029 data = self._DoReadFile('165_section_ignore_hash_signature.dts')
4030 expected = (U_BOOT_DATA + U_BOOT_DATA)
4031 self.assertEqual(expected, data)
4032
Alper Nebi Yasakc5030602020-08-31 12:58:19 +03004033 def testPadInSections(self):
4034 """Test pad-before, pad-after for entries in sections"""
Simon Glassd12599d2020-10-26 17:40:09 -06004035 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4036 '166_pad_in_sections.dts', update_dtb=True)
Simon Glass80025522022-01-29 14:14:04 -07004037 expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) +
4038 U_BOOT_DATA + tools.get_bytes(ord('!'), 6) +
Alper Nebi Yasakc5030602020-08-31 12:58:19 +03004039 U_BOOT_DATA)
4040 self.assertEqual(expected, data)
4041
Simon Glassd12599d2020-10-26 17:40:09 -06004042 dtb = fdt.Fdt(out_dtb_fname)
4043 dtb.Scan()
4044 props = self._GetPropTree(dtb, ['size', 'image-pos', 'offset'])
4045 expected = {
4046 'image-pos': 0,
4047 'offset': 0,
4048 'size': 12 + 6 + 3 * len(U_BOOT_DATA),
4049
4050 'section:image-pos': 0,
4051 'section:offset': 0,
4052 'section:size': 12 + 6 + 3 * len(U_BOOT_DATA),
4053
4054 'section/before:image-pos': 0,
4055 'section/before:offset': 0,
4056 'section/before:size': len(U_BOOT_DATA),
4057
4058 'section/u-boot:image-pos': 4,
4059 'section/u-boot:offset': 4,
4060 'section/u-boot:size': 12 + len(U_BOOT_DATA) + 6,
4061
4062 'section/after:image-pos': 26,
4063 'section/after:offset': 26,
4064 'section/after:size': len(U_BOOT_DATA),
4065 }
4066 self.assertEqual(expected, props)
4067
Alper Nebi Yasakc261ec12020-08-31 12:58:20 +03004068 def testFitImageSubentryAlignment(self):
4069 """Test relative alignability of FIT image subentries"""
Alper Nebi Yasakd4553262022-02-08 01:08:07 +03004070 self._SetupSplElf()
Alper Nebi Yasakc261ec12020-08-31 12:58:20 +03004071 entry_args = {
4072 'test-id': TEXT_DATA,
4073 }
4074 data, _, _, _ = self._DoReadFileDtb('167_fit_image_subentry_alignment.dts',
4075 entry_args=entry_args)
4076 dtb = fdt.Fdt.FromData(data)
4077 dtb.Scan()
4078
4079 node = dtb.GetNode('/images/kernel')
4080 data = dtb.GetProps(node)["data"].bytes
4081 align_pad = 0x10 - (len(U_BOOT_SPL_DATA) % 0x10)
Simon Glass80025522022-01-29 14:14:04 -07004082 expected = (tools.get_bytes(0, 0x20) + U_BOOT_SPL_DATA +
4083 tools.get_bytes(0, align_pad) + U_BOOT_DATA)
Alper Nebi Yasakc261ec12020-08-31 12:58:20 +03004084 self.assertEqual(expected, data)
4085
4086 node = dtb.GetNode('/images/fdt-1')
4087 data = dtb.GetProps(node)["data"].bytes
Simon Glass80025522022-01-29 14:14:04 -07004088 expected = (U_BOOT_SPL_DTB_DATA + tools.get_bytes(0, 20) +
4089 tools.to_bytes(TEXT_DATA) + tools.get_bytes(0, 30) +
Alper Nebi Yasakc261ec12020-08-31 12:58:20 +03004090 U_BOOT_DTB_DATA)
4091 self.assertEqual(expected, data)
4092
4093 def testFitExtblobMissingOk(self):
4094 """Test a FIT with a missing external blob that is allowed"""
4095 with test_util.capture_sys_output() as (stdout, stderr):
4096 self._DoTestFile('168_fit_missing_blob.dts',
4097 allow_missing=True)
4098 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07004099 self.assertRegex(err, "Image 'image'.*missing.*: atf-bl31")
Alper Nebi Yasakc261ec12020-08-31 12:58:20 +03004100
Simon Glass21db0ff2020-09-01 05:13:54 -06004101 def testBlobNamedByArgMissing(self):
4102 """Test handling of a missing entry arg"""
4103 with self.assertRaises(ValueError) as e:
4104 self._DoReadFile('068_blob_named_by_arg.dts')
4105 self.assertIn("Missing required properties/entry args: cros-ec-rw-path",
4106 str(e.exception))
4107
Simon Glass559c4de2020-09-01 05:13:58 -06004108 def testPackBl31(self):
4109 """Test that an image with an ATF BL31 binary can be created"""
4110 data = self._DoReadFile('169_atf_bl31.dts')
4111 self.assertEqual(ATF_BL31_DATA, data[:len(ATF_BL31_DATA)])
4112
Samuel Holland9d8cc632020-10-21 21:12:15 -05004113 def testPackScp(self):
4114 """Test that an image with an SCP binary can be created"""
4115 data = self._DoReadFile('172_scp.dts')
4116 self.assertEqual(SCP_DATA, data[:len(SCP_DATA)])
4117
Simon Glassa435cd12020-09-01 05:13:59 -06004118 def testFitFdt(self):
4119 """Test an image with an FIT with multiple FDT images"""
4120 def _CheckFdt(seq, expected_data):
4121 """Check the FDT nodes
4122
4123 Args:
4124 seq: Sequence number to check (0 or 1)
4125 expected_data: Expected contents of 'data' property
4126 """
4127 name = 'fdt-%d' % seq
4128 fnode = dtb.GetNode('/images/%s' % name)
4129 self.assertIsNotNone(fnode)
4130 self.assertEqual({'description','type', 'compression', 'data'},
4131 set(fnode.props.keys()))
4132 self.assertEqual(expected_data, fnode.props['data'].bytes)
4133 self.assertEqual('fdt-test-fdt%d.dtb' % seq,
4134 fnode.props['description'].value)
Jan Kiszkaa1419df2022-02-28 17:06:20 +01004135 self.assertEqual(fnode.subnodes[0].name, 'hash')
Simon Glassa435cd12020-09-01 05:13:59 -06004136
4137 def _CheckConfig(seq, expected_data):
4138 """Check the configuration nodes
4139
4140 Args:
4141 seq: Sequence number to check (0 or 1)
4142 expected_data: Expected contents of 'data' property
4143 """
4144 cnode = dtb.GetNode('/configurations')
4145 self.assertIn('default', cnode.props)
Simon Glass1032acc2020-09-06 10:39:08 -06004146 self.assertEqual('config-2', cnode.props['default'].value)
Simon Glassa435cd12020-09-01 05:13:59 -06004147
4148 name = 'config-%d' % seq
4149 fnode = dtb.GetNode('/configurations/%s' % name)
4150 self.assertIsNotNone(fnode)
4151 self.assertEqual({'description','firmware', 'loadables', 'fdt'},
4152 set(fnode.props.keys()))
4153 self.assertEqual('conf-test-fdt%d.dtb' % seq,
4154 fnode.props['description'].value)
4155 self.assertEqual('fdt-%d' % seq, fnode.props['fdt'].value)
4156
4157 entry_args = {
4158 'of-list': 'test-fdt1 test-fdt2',
Simon Glass1032acc2020-09-06 10:39:08 -06004159 'default-dt': 'test-fdt2',
Simon Glassa435cd12020-09-01 05:13:59 -06004160 }
4161 data = self._DoReadFileDtb(
Bin Meng16cf5662021-05-10 20:23:32 +08004162 '170_fit_fdt.dts',
Simon Glassa435cd12020-09-01 05:13:59 -06004163 entry_args=entry_args,
4164 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
4165 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
4166 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
4167
4168 dtb = fdt.Fdt.FromData(fit_data)
4169 dtb.Scan()
4170 fnode = dtb.GetNode('/images/kernel')
4171 self.assertIn('data', fnode.props)
4172
4173 # Check all the properties in fdt-1 and fdt-2
4174 _CheckFdt(1, TEST_FDT1_DATA)
4175 _CheckFdt(2, TEST_FDT2_DATA)
4176
4177 # Check configurations
4178 _CheckConfig(1, TEST_FDT1_DATA)
4179 _CheckConfig(2, TEST_FDT2_DATA)
4180
4181 def testFitFdtMissingList(self):
4182 """Test handling of a missing 'of-list' entry arg"""
4183 with self.assertRaises(ValueError) as e:
Bin Meng16cf5662021-05-10 20:23:32 +08004184 self._DoReadFile('170_fit_fdt.dts')
Simon Glassa435cd12020-09-01 05:13:59 -06004185 self.assertIn("Generator node requires 'of-list' entry argument",
4186 str(e.exception))
4187
4188 def testFitFdtEmptyList(self):
4189 """Test handling of an empty 'of-list' entry arg"""
4190 entry_args = {
4191 'of-list': '',
4192 }
4193 data = self._DoReadFileDtb('170_fit_fdt.dts', entry_args=entry_args)[0]
4194
4195 def testFitFdtMissingProp(self):
4196 """Test handling of a missing 'fit,fdt-list' property"""
4197 with self.assertRaises(ValueError) as e:
4198 self._DoReadFile('171_fit_fdt_missing_prop.dts')
4199 self.assertIn("Generator node requires 'fit,fdt-list' property",
4200 str(e.exception))
Simon Glass559c4de2020-09-01 05:13:58 -06004201
Simon Glass1032acc2020-09-06 10:39:08 -06004202 def testFitFdtMissing(self):
4203 """Test handling of a missing 'default-dt' entry arg"""
4204 entry_args = {
4205 'of-list': 'test-fdt1 test-fdt2',
4206 }
4207 with self.assertRaises(ValueError) as e:
4208 self._DoReadFileDtb(
Bin Meng16cf5662021-05-10 20:23:32 +08004209 '170_fit_fdt.dts',
Simon Glass1032acc2020-09-06 10:39:08 -06004210 entry_args=entry_args,
4211 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
4212 self.assertIn("Generated 'default' node requires default-dt entry argument",
4213 str(e.exception))
4214
4215 def testFitFdtNotInList(self):
4216 """Test handling of a default-dt that is not in the of-list"""
4217 entry_args = {
4218 'of-list': 'test-fdt1 test-fdt2',
4219 'default-dt': 'test-fdt3',
4220 }
4221 with self.assertRaises(ValueError) as e:
4222 self._DoReadFileDtb(
Bin Meng16cf5662021-05-10 20:23:32 +08004223 '170_fit_fdt.dts',
Simon Glass1032acc2020-09-06 10:39:08 -06004224 entry_args=entry_args,
4225 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
4226 self.assertIn("default-dt entry argument 'test-fdt3' not found in fdt list: test-fdt1, test-fdt2",
4227 str(e.exception))
4228
Simon Glassa820af72020-09-06 10:39:09 -06004229 def testFitExtblobMissingHelp(self):
4230 """Test display of help messages when an external blob is missing"""
4231 control.missing_blob_help = control._ReadMissingBlobHelp()
4232 control.missing_blob_help['wibble'] = 'Wibble test'
4233 control.missing_blob_help['another'] = 'Another test'
4234 with test_util.capture_sys_output() as (stdout, stderr):
4235 self._DoTestFile('168_fit_missing_blob.dts',
4236 allow_missing=True)
4237 err = stderr.getvalue()
4238
4239 # We can get the tag from the name, the type or the missing-msg
4240 # property. Check all three.
4241 self.assertIn('You may need to build ARM Trusted', err)
4242 self.assertIn('Wibble test', err)
4243 self.assertIn('Another test', err)
4244
Simon Glass6f1f4d42020-09-06 10:35:32 -06004245 def testMissingBlob(self):
4246 """Test handling of a blob containing a missing file"""
4247 with self.assertRaises(ValueError) as e:
4248 self._DoTestFile('173_missing_blob.dts', allow_missing=True)
4249 self.assertIn("Filename 'missing' not found in input path",
4250 str(e.exception))
4251
Simon Glassa0729502020-09-06 10:35:33 -06004252 def testEnvironment(self):
4253 """Test adding a U-Boot environment"""
4254 data = self._DoReadFile('174_env.dts')
4255 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
4256 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
4257 env = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
4258 self.assertEqual(b'\x1b\x97\x22\x7c\x01var1=1\0var2="2"\0\0\xff\xff',
4259 env)
4260
4261 def testEnvironmentNoSize(self):
4262 """Test that a missing 'size' property is detected"""
4263 with self.assertRaises(ValueError) as e:
Simon Glass8cdc08a2020-10-26 17:40:00 -06004264 self._DoTestFile('175_env_no_size.dts')
Simon Glassa0729502020-09-06 10:35:33 -06004265 self.assertIn("'u-boot-env' entry must have a size property",
4266 str(e.exception))
4267
4268 def testEnvironmentTooSmall(self):
4269 """Test handling of an environment that does not fit"""
4270 with self.assertRaises(ValueError) as e:
Simon Glass8cdc08a2020-10-26 17:40:00 -06004271 self._DoTestFile('176_env_too_small.dts')
Simon Glassa0729502020-09-06 10:35:33 -06004272
4273 # checksum, start byte, environment with \0 terminator, final \0
4274 need = 4 + 1 + len(ENV_DATA) + 1 + 1
4275 short = need - 0x8
4276 self.assertIn("too small to hold data (need %#x more bytes)" % short,
4277 str(e.exception))
4278
Simon Glassd1fdf752020-10-26 17:40:01 -06004279 def testSkipAtStart(self):
4280 """Test handling of skip-at-start section"""
4281 data = self._DoReadFile('177_skip_at_start.dts')
4282 self.assertEqual(U_BOOT_DATA, data)
4283
4284 image = control.images['image']
4285 entries = image.GetEntries()
4286 section = entries['section']
4287 self.assertEqual(0, section.offset)
4288 self.assertEqual(len(U_BOOT_DATA), section.size)
4289 self.assertEqual(U_BOOT_DATA, section.GetData())
4290
4291 entry = section.GetEntries()['u-boot']
4292 self.assertEqual(16, entry.offset)
4293 self.assertEqual(len(U_BOOT_DATA), entry.size)
4294 self.assertEqual(U_BOOT_DATA, entry.data)
4295
4296 def testSkipAtStartPad(self):
4297 """Test handling of skip-at-start section with padded entry"""
4298 data = self._DoReadFile('178_skip_at_start_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07004299 before = tools.get_bytes(0, 8)
4300 after = tools.get_bytes(0, 4)
Simon Glassd1fdf752020-10-26 17:40:01 -06004301 all = before + U_BOOT_DATA + after
4302 self.assertEqual(all, data)
4303
4304 image = control.images['image']
4305 entries = image.GetEntries()
4306 section = entries['section']
4307 self.assertEqual(0, section.offset)
4308 self.assertEqual(len(all), section.size)
4309 self.assertEqual(all, section.GetData())
4310
4311 entry = section.GetEntries()['u-boot']
4312 self.assertEqual(16, entry.offset)
4313 self.assertEqual(len(all), entry.size)
4314 self.assertEqual(U_BOOT_DATA, entry.data)
4315
4316 def testSkipAtStartSectionPad(self):
4317 """Test handling of skip-at-start section with padding"""
4318 data = self._DoReadFile('179_skip_at_start_section_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07004319 before = tools.get_bytes(0, 8)
4320 after = tools.get_bytes(0, 4)
Simon Glassd1fdf752020-10-26 17:40:01 -06004321 all = before + U_BOOT_DATA + after
Simon Glass510ef0f2020-10-26 17:40:13 -06004322 self.assertEqual(all, data)
Simon Glassd1fdf752020-10-26 17:40:01 -06004323
4324 image = control.images['image']
4325 entries = image.GetEntries()
4326 section = entries['section']
4327 self.assertEqual(0, section.offset)
4328 self.assertEqual(len(all), section.size)
Simon Glass72eeff12020-10-26 17:40:16 -06004329 self.assertEqual(U_BOOT_DATA, section.data)
Simon Glass510ef0f2020-10-26 17:40:13 -06004330 self.assertEqual(all, section.GetPaddedData())
Simon Glassd1fdf752020-10-26 17:40:01 -06004331
4332 entry = section.GetEntries()['u-boot']
4333 self.assertEqual(16, entry.offset)
4334 self.assertEqual(len(U_BOOT_DATA), entry.size)
4335 self.assertEqual(U_BOOT_DATA, entry.data)
Simon Glassa0729502020-09-06 10:35:33 -06004336
Simon Glassbb395742020-10-26 17:40:14 -06004337 def testSectionPad(self):
4338 """Testing padding with sections"""
4339 data = self._DoReadFile('180_section_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07004340 expected = (tools.get_bytes(ord('&'), 3) +
4341 tools.get_bytes(ord('!'), 5) +
Simon Glassbb395742020-10-26 17:40:14 -06004342 U_BOOT_DATA +
Simon Glass80025522022-01-29 14:14:04 -07004343 tools.get_bytes(ord('!'), 1) +
4344 tools.get_bytes(ord('&'), 2))
Simon Glassbb395742020-10-26 17:40:14 -06004345 self.assertEqual(expected, data)
4346
4347 def testSectionAlign(self):
4348 """Testing alignment with sections"""
4349 data = self._DoReadFileDtb('181_section_align.dts', map=True)[0]
4350 expected = (b'\0' + # fill section
Simon Glass80025522022-01-29 14:14:04 -07004351 tools.get_bytes(ord('&'), 1) + # padding to section align
Simon Glassbb395742020-10-26 17:40:14 -06004352 b'\0' + # fill section
Simon Glass80025522022-01-29 14:14:04 -07004353 tools.get_bytes(ord('!'), 3) + # padding to u-boot align
Simon Glassbb395742020-10-26 17:40:14 -06004354 U_BOOT_DATA +
Simon Glass80025522022-01-29 14:14:04 -07004355 tools.get_bytes(ord('!'), 4) + # padding to u-boot size
4356 tools.get_bytes(ord('!'), 4)) # padding to section size
Simon Glassbb395742020-10-26 17:40:14 -06004357 self.assertEqual(expected, data)
4358
Simon Glassd92c8362020-10-26 17:40:25 -06004359 def testCompressImage(self):
4360 """Test compression of the entire image"""
4361 self._CheckLz4()
4362 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4363 '182_compress_image.dts', use_real_dtb=True, update_dtb=True)
4364 dtb = fdt.Fdt(out_dtb_fname)
4365 dtb.Scan()
4366 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4367 'uncomp-size'])
4368 orig = self._decompress(data)
4369 self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, orig)
4370
4371 # Do a sanity check on various fields
4372 image = control.images['image']
4373 entries = image.GetEntries()
4374 self.assertEqual(2, len(entries))
4375
4376 entry = entries['blob']
4377 self.assertEqual(COMPRESS_DATA, entry.data)
4378 self.assertEqual(len(COMPRESS_DATA), entry.size)
4379
4380 entry = entries['u-boot']
4381 self.assertEqual(U_BOOT_DATA, entry.data)
4382 self.assertEqual(len(U_BOOT_DATA), entry.size)
4383
4384 self.assertEqual(len(data), image.size)
4385 self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, image.uncomp_data)
4386 self.assertEqual(len(COMPRESS_DATA + U_BOOT_DATA), image.uncomp_size)
4387 orig = self._decompress(image.data)
4388 self.assertEqual(orig, image.uncomp_data)
4389
4390 expected = {
4391 'blob:offset': 0,
4392 'blob:size': len(COMPRESS_DATA),
4393 'u-boot:offset': len(COMPRESS_DATA),
4394 'u-boot:size': len(U_BOOT_DATA),
4395 'uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4396 'offset': 0,
4397 'image-pos': 0,
4398 'size': len(data),
4399 }
4400 self.assertEqual(expected, props)
4401
4402 def testCompressImageLess(self):
4403 """Test compression where compression reduces the image size"""
4404 self._CheckLz4()
4405 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4406 '183_compress_image_less.dts', use_real_dtb=True, update_dtb=True)
4407 dtb = fdt.Fdt(out_dtb_fname)
4408 dtb.Scan()
4409 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4410 'uncomp-size'])
4411 orig = self._decompress(data)
4412
4413 self.assertEquals(COMPRESS_DATA + COMPRESS_DATA + U_BOOT_DATA, orig)
4414
4415 # Do a sanity check on various fields
4416 image = control.images['image']
4417 entries = image.GetEntries()
4418 self.assertEqual(2, len(entries))
4419
4420 entry = entries['blob']
4421 self.assertEqual(COMPRESS_DATA_BIG, entry.data)
4422 self.assertEqual(len(COMPRESS_DATA_BIG), entry.size)
4423
4424 entry = entries['u-boot']
4425 self.assertEqual(U_BOOT_DATA, entry.data)
4426 self.assertEqual(len(U_BOOT_DATA), entry.size)
4427
4428 self.assertEqual(len(data), image.size)
4429 self.assertEqual(COMPRESS_DATA_BIG + U_BOOT_DATA, image.uncomp_data)
4430 self.assertEqual(len(COMPRESS_DATA_BIG + U_BOOT_DATA),
4431 image.uncomp_size)
4432 orig = self._decompress(image.data)
4433 self.assertEqual(orig, image.uncomp_data)
4434
4435 expected = {
4436 'blob:offset': 0,
4437 'blob:size': len(COMPRESS_DATA_BIG),
4438 'u-boot:offset': len(COMPRESS_DATA_BIG),
4439 'u-boot:size': len(U_BOOT_DATA),
4440 'uncomp-size': len(COMPRESS_DATA_BIG + U_BOOT_DATA),
4441 'offset': 0,
4442 'image-pos': 0,
4443 'size': len(data),
4444 }
4445 self.assertEqual(expected, props)
4446
4447 def testCompressSectionSize(self):
4448 """Test compression of a section with a fixed size"""
4449 self._CheckLz4()
4450 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4451 '184_compress_section_size.dts', use_real_dtb=True, update_dtb=True)
4452 dtb = fdt.Fdt(out_dtb_fname)
4453 dtb.Scan()
4454 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4455 'uncomp-size'])
4456 orig = self._decompress(data)
4457 self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, orig)
4458 expected = {
4459 'section/blob:offset': 0,
4460 'section/blob:size': len(COMPRESS_DATA),
4461 'section/u-boot:offset': len(COMPRESS_DATA),
4462 'section/u-boot:size': len(U_BOOT_DATA),
4463 'section:offset': 0,
4464 'section:image-pos': 0,
4465 'section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4466 'section:size': 0x30,
4467 'offset': 0,
4468 'image-pos': 0,
4469 'size': 0x30,
4470 }
4471 self.assertEqual(expected, props)
4472
4473 def testCompressSection(self):
4474 """Test compression of a section with no fixed size"""
4475 self._CheckLz4()
4476 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4477 '185_compress_section.dts', use_real_dtb=True, update_dtb=True)
4478 dtb = fdt.Fdt(out_dtb_fname)
4479 dtb.Scan()
4480 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4481 'uncomp-size'])
4482 orig = self._decompress(data)
4483 self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, orig)
4484 expected = {
4485 'section/blob:offset': 0,
4486 'section/blob:size': len(COMPRESS_DATA),
4487 'section/u-boot:offset': len(COMPRESS_DATA),
4488 'section/u-boot:size': len(U_BOOT_DATA),
4489 'section:offset': 0,
4490 'section:image-pos': 0,
4491 'section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4492 'section:size': len(data),
4493 'offset': 0,
4494 'image-pos': 0,
4495 'size': len(data),
4496 }
4497 self.assertEqual(expected, props)
4498
Stefan Herbrechtsmeier94813a02022-08-19 16:25:31 +02004499 def testLz4Missing(self):
4500 """Test that binman still produces an image if lz4 is missing"""
4501 with test_util.capture_sys_output() as (_, stderr):
4502 self._DoTestFile('185_compress_section.dts',
4503 force_missing_bintools='lz4')
4504 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07004505 self.assertRegex(err, "Image 'image'.*missing bintools.*: lz4")
Stefan Herbrechtsmeier94813a02022-08-19 16:25:31 +02004506
Simon Glassd92c8362020-10-26 17:40:25 -06004507 def testCompressExtra(self):
4508 """Test compression of a section with no fixed size"""
4509 self._CheckLz4()
4510 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4511 '186_compress_extra.dts', use_real_dtb=True, update_dtb=True)
4512 dtb = fdt.Fdt(out_dtb_fname)
4513 dtb.Scan()
4514 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4515 'uncomp-size'])
4516
4517 base = data[len(U_BOOT_DATA):]
4518 self.assertEquals(U_BOOT_DATA, base[:len(U_BOOT_DATA)])
4519 rest = base[len(U_BOOT_DATA):]
4520
4521 # Check compressed data
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +02004522 bintool = self.comp_bintools['lz4']
4523 expect1 = bintool.compress(COMPRESS_DATA + U_BOOT_DATA)
Stefan Herbrechtsmeier86f1fc02022-08-19 16:25:23 +02004524 data1 = rest[:len(expect1)]
4525 section1 = self._decompress(data1)
4526 self.assertEquals(expect1, data1)
Simon Glassd92c8362020-10-26 17:40:25 -06004527 self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, section1)
4528 rest1 = rest[len(expect1):]
4529
Stefan Herbrechtsmeierb4bfbce2022-08-19 16:25:28 +02004530 expect2 = bintool.compress(COMPRESS_DATA + COMPRESS_DATA)
Stefan Herbrechtsmeier86f1fc02022-08-19 16:25:23 +02004531 data2 = rest1[:len(expect2)]
4532 section2 = self._decompress(data2)
4533 self.assertEquals(expect2, data2)
Simon Glassd92c8362020-10-26 17:40:25 -06004534 self.assertEquals(COMPRESS_DATA + COMPRESS_DATA, section2)
4535 rest2 = rest1[len(expect2):]
4536
4537 expect_size = (len(U_BOOT_DATA) + len(U_BOOT_DATA) + len(expect1) +
4538 len(expect2) + len(U_BOOT_DATA))
4539 #self.assertEquals(expect_size, len(data))
4540
4541 #self.assertEquals(U_BOOT_DATA, rest2)
4542
4543 self.maxDiff = None
4544 expected = {
4545 'u-boot:offset': 0,
4546 'u-boot:image-pos': 0,
4547 'u-boot:size': len(U_BOOT_DATA),
4548
4549 'base:offset': len(U_BOOT_DATA),
4550 'base:image-pos': len(U_BOOT_DATA),
4551 'base:size': len(data) - len(U_BOOT_DATA),
4552 'base/u-boot:offset': 0,
4553 'base/u-boot:image-pos': len(U_BOOT_DATA),
4554 'base/u-boot:size': len(U_BOOT_DATA),
4555 'base/u-boot2:offset': len(U_BOOT_DATA) + len(expect1) +
4556 len(expect2),
4557 'base/u-boot2:image-pos': len(U_BOOT_DATA) * 2 + len(expect1) +
4558 len(expect2),
4559 'base/u-boot2:size': len(U_BOOT_DATA),
4560
4561 'base/section:offset': len(U_BOOT_DATA),
4562 'base/section:image-pos': len(U_BOOT_DATA) * 2,
4563 'base/section:size': len(expect1),
4564 'base/section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4565 'base/section/blob:offset': 0,
4566 'base/section/blob:size': len(COMPRESS_DATA),
4567 'base/section/u-boot:offset': len(COMPRESS_DATA),
4568 'base/section/u-boot:size': len(U_BOOT_DATA),
4569
4570 'base/section2:offset': len(U_BOOT_DATA) + len(expect1),
4571 'base/section2:image-pos': len(U_BOOT_DATA) * 2 + len(expect1),
4572 'base/section2:size': len(expect2),
4573 'base/section2:uncomp-size': len(COMPRESS_DATA + COMPRESS_DATA),
4574 'base/section2/blob:offset': 0,
4575 'base/section2/blob:size': len(COMPRESS_DATA),
4576 'base/section2/blob2:offset': len(COMPRESS_DATA),
4577 'base/section2/blob2:size': len(COMPRESS_DATA),
4578
4579 'offset': 0,
4580 'image-pos': 0,
4581 'size': len(data),
4582 }
4583 self.assertEqual(expected, props)
4584
Simon Glassecbe4732021-01-06 21:35:15 -07004585 def testSymbolsSubsection(self):
4586 """Test binman can assign symbols from a subsection"""
Alper Nebi Yasak9634dc92022-06-18 15:13:11 +03004587 self.checkSymbols('187_symbols_sub.dts', U_BOOT_SPL_DATA, 0x1c)
Simon Glassecbe4732021-01-06 21:35:15 -07004588
Simon Glass3fb25402021-01-06 21:35:16 -07004589 def testReadImageEntryArg(self):
4590 """Test reading an image that would need an entry arg to generate"""
4591 entry_args = {
4592 'cros-ec-rw-path': 'ecrw.bin',
4593 }
4594 data = self.data = self._DoReadFileDtb(
4595 '188_image_entryarg.dts',use_real_dtb=True, update_dtb=True,
4596 entry_args=entry_args)
4597
Simon Glass80025522022-01-29 14:14:04 -07004598 image_fname = tools.get_output_filename('image.bin')
Simon Glass3fb25402021-01-06 21:35:16 -07004599 orig_image = control.images['image']
4600
4601 # This should not generate an error about the missing 'cros-ec-rw-path'
4602 # since we are reading the image from a file. Compare with
4603 # testEntryArgsRequired()
4604 image = Image.FromFile(image_fname)
4605 self.assertEqual(orig_image.GetEntries().keys(),
4606 image.GetEntries().keys())
4607
Simon Glassa2af7302021-01-06 21:35:18 -07004608 def testFilesAlign(self):
4609 """Test alignment with files"""
4610 data = self._DoReadFile('190_files_align.dts')
4611
4612 # The first string is 15 bytes so will align to 16
4613 expect = FILES_DATA[:15] + b'\0' + FILES_DATA[15:]
4614 self.assertEqual(expect, data)
4615
Simon Glassdb84b562021-01-06 21:35:19 -07004616 def testReadImageSkip(self):
4617 """Test reading an image and accessing its FDT map"""
4618 data = self.data = self._DoReadFileRealDtb('191_read_image_skip.dts')
Simon Glass80025522022-01-29 14:14:04 -07004619 image_fname = tools.get_output_filename('image.bin')
Simon Glassdb84b562021-01-06 21:35:19 -07004620 orig_image = control.images['image']
4621 image = Image.FromFile(image_fname)
4622 self.assertEqual(orig_image.GetEntries().keys(),
4623 image.GetEntries().keys())
4624
4625 orig_entry = orig_image.GetEntries()['fdtmap']
4626 entry = image.GetEntries()['fdtmap']
4627 self.assertEqual(orig_entry.offset, entry.offset)
4628 self.assertEqual(orig_entry.size, entry.size)
4629 self.assertEqual(16, entry.image_pos)
4630
4631 u_boot = image.GetEntries()['section'].GetEntries()['u-boot']
4632
4633 self.assertEquals(U_BOOT_DATA, u_boot.ReadData())
4634
Simon Glassc98de972021-03-18 20:24:57 +13004635 def testTplNoDtb(self):
4636 """Test that an image with tpl/u-boot-tpl-nodtb.bin can be created"""
Simon Glass13089cc2021-04-25 08:39:32 +12004637 self._SetupTplElf()
Simon Glassc98de972021-03-18 20:24:57 +13004638 data = self._DoReadFile('192_u_boot_tpl_nodtb.dts')
4639 self.assertEqual(U_BOOT_TPL_NODTB_DATA,
4640 data[:len(U_BOOT_TPL_NODTB_DATA)])
4641
Simon Glass63f41d42021-03-18 20:24:58 +13004642 def testTplBssPad(self):
4643 """Test that we can pad TPL's BSS with zeros"""
4644 # ELF file with a '__bss_size' symbol
4645 self._SetupTplElf()
4646 data = self._DoReadFile('193_tpl_bss_pad.dts')
Simon Glass80025522022-01-29 14:14:04 -07004647 self.assertEqual(U_BOOT_TPL_DATA + tools.get_bytes(0, 10) + U_BOOT_DATA,
Simon Glass63f41d42021-03-18 20:24:58 +13004648 data)
4649
4650 def testTplBssPadMissing(self):
4651 """Test that a missing symbol is detected"""
4652 self._SetupTplElf('u_boot_ucode_ptr')
4653 with self.assertRaises(ValueError) as e:
4654 self._DoReadFile('193_tpl_bss_pad.dts')
4655 self.assertIn('Expected __bss_size symbol in tpl/u-boot-tpl',
4656 str(e.exception))
4657
Simon Glass718b5292021-03-18 20:25:07 +13004658 def checkDtbSizes(self, data, pad_len, start):
4659 """Check the size arguments in a dtb embedded in an image
4660
4661 Args:
4662 data: The image data
4663 pad_len: Length of the pad section in the image, in bytes
4664 start: Start offset of the devicetree to examine, within the image
4665
4666 Returns:
4667 Size of the devicetree in bytes
4668 """
4669 dtb_data = data[start:]
4670 dtb = fdt.Fdt.FromData(dtb_data)
4671 fdt_size = dtb.GetFdtObj().totalsize()
4672 dtb.Scan()
4673 props = self._GetPropTree(dtb, 'size')
4674 self.assertEqual({
4675 'size': len(data),
4676 'u-boot-spl/u-boot-spl-bss-pad:size': pad_len,
4677 'u-boot-spl/u-boot-spl-dtb:size': 801,
4678 'u-boot-spl/u-boot-spl-nodtb:size': len(U_BOOT_SPL_NODTB_DATA),
4679 'u-boot-spl:size': 860,
4680 'u-boot-tpl:size': len(U_BOOT_TPL_DATA),
4681 'u-boot/u-boot-dtb:size': 781,
4682 'u-boot/u-boot-nodtb:size': len(U_BOOT_NODTB_DATA),
4683 'u-boot:size': 827,
4684 }, props)
4685 return fdt_size
4686
4687 def testExpanded(self):
4688 """Test that an expanded entry type is selected when needed"""
4689 self._SetupSplElf()
4690 self._SetupTplElf()
4691
4692 # SPL has a devicetree, TPL does not
4693 entry_args = {
4694 'spl-dtb': '1',
4695 'spl-bss-pad': 'y',
4696 'tpl-dtb': '',
4697 }
4698 self._DoReadFileDtb('194_fdt_incl.dts', use_expanded=True,
4699 entry_args=entry_args)
4700 image = control.images['image']
4701 entries = image.GetEntries()
4702 self.assertEqual(3, len(entries))
4703
4704 # First, u-boot, which should be expanded into u-boot-nodtb and dtb
4705 self.assertIn('u-boot', entries)
4706 entry = entries['u-boot']
4707 self.assertEqual('u-boot-expanded', entry.etype)
4708 subent = entry.GetEntries()
4709 self.assertEqual(2, len(subent))
4710 self.assertIn('u-boot-nodtb', subent)
4711 self.assertIn('u-boot-dtb', subent)
4712
4713 # Second, u-boot-spl, which should be expanded into three parts
4714 self.assertIn('u-boot-spl', entries)
4715 entry = entries['u-boot-spl']
4716 self.assertEqual('u-boot-spl-expanded', entry.etype)
4717 subent = entry.GetEntries()
4718 self.assertEqual(3, len(subent))
4719 self.assertIn('u-boot-spl-nodtb', subent)
4720 self.assertIn('u-boot-spl-bss-pad', subent)
4721 self.assertIn('u-boot-spl-dtb', subent)
4722
4723 # Third, u-boot-tpl, which should be not be expanded, since TPL has no
4724 # devicetree
4725 self.assertIn('u-boot-tpl', entries)
4726 entry = entries['u-boot-tpl']
4727 self.assertEqual('u-boot-tpl', entry.etype)
4728 self.assertEqual(None, entry.GetEntries())
4729
4730 def testExpandedTpl(self):
4731 """Test that an expanded entry type is selected for TPL when needed"""
4732 self._SetupTplElf()
4733
4734 entry_args = {
4735 'tpl-bss-pad': 'y',
4736 'tpl-dtb': 'y',
4737 }
4738 self._DoReadFileDtb('195_fdt_incl_tpl.dts', use_expanded=True,
4739 entry_args=entry_args)
4740 image = control.images['image']
4741 entries = image.GetEntries()
4742 self.assertEqual(1, len(entries))
4743
4744 # We only have u-boot-tpl, which be expanded
4745 self.assertIn('u-boot-tpl', entries)
4746 entry = entries['u-boot-tpl']
4747 self.assertEqual('u-boot-tpl-expanded', entry.etype)
4748 subent = entry.GetEntries()
4749 self.assertEqual(3, len(subent))
4750 self.assertIn('u-boot-tpl-nodtb', subent)
4751 self.assertIn('u-boot-tpl-bss-pad', subent)
4752 self.assertIn('u-boot-tpl-dtb', subent)
4753
4754 def testExpandedNoPad(self):
4755 """Test an expanded entry without BSS pad enabled"""
4756 self._SetupSplElf()
4757 self._SetupTplElf()
4758
4759 # SPL has a devicetree, TPL does not
4760 entry_args = {
4761 'spl-dtb': 'something',
4762 'spl-bss-pad': 'n',
4763 'tpl-dtb': '',
4764 }
4765 self._DoReadFileDtb('194_fdt_incl.dts', use_expanded=True,
4766 entry_args=entry_args)
4767 image = control.images['image']
4768 entries = image.GetEntries()
4769
4770 # Just check u-boot-spl, which should be expanded into two parts
4771 self.assertIn('u-boot-spl', entries)
4772 entry = entries['u-boot-spl']
4773 self.assertEqual('u-boot-spl-expanded', entry.etype)
4774 subent = entry.GetEntries()
4775 self.assertEqual(2, len(subent))
4776 self.assertIn('u-boot-spl-nodtb', subent)
4777 self.assertIn('u-boot-spl-dtb', subent)
4778
4779 def testExpandedTplNoPad(self):
4780 """Test that an expanded entry type with padding disabled in TPL"""
4781 self._SetupTplElf()
4782
4783 entry_args = {
4784 'tpl-bss-pad': '',
4785 'tpl-dtb': 'y',
4786 }
4787 self._DoReadFileDtb('195_fdt_incl_tpl.dts', use_expanded=True,
4788 entry_args=entry_args)
4789 image = control.images['image']
4790 entries = image.GetEntries()
4791 self.assertEqual(1, len(entries))
4792
4793 # We only have u-boot-tpl, which be expanded
4794 self.assertIn('u-boot-tpl', entries)
4795 entry = entries['u-boot-tpl']
4796 self.assertEqual('u-boot-tpl-expanded', entry.etype)
4797 subent = entry.GetEntries()
4798 self.assertEqual(2, len(subent))
4799 self.assertIn('u-boot-tpl-nodtb', subent)
4800 self.assertIn('u-boot-tpl-dtb', subent)
4801
4802 def testFdtInclude(self):
4803 """Test that an Fdt is update within all binaries"""
4804 self._SetupSplElf()
4805 self._SetupTplElf()
4806
4807 # SPL has a devicetree, TPL does not
4808 self.maxDiff = None
4809 entry_args = {
4810 'spl-dtb': '1',
4811 'spl-bss-pad': 'y',
4812 'tpl-dtb': '',
4813 }
4814 # Build the image. It includes two separate devicetree binaries, each
4815 # with their own contents, but all contain the binman definition.
4816 data = self._DoReadFileDtb(
4817 '194_fdt_incl.dts', use_real_dtb=True, use_expanded=True,
4818 update_dtb=True, entry_args=entry_args)[0]
4819 pad_len = 10
4820
4821 # Check the U-Boot dtb
4822 start = len(U_BOOT_NODTB_DATA)
4823 fdt_size = self.checkDtbSizes(data, pad_len, start)
4824
4825 # Now check SPL
4826 start += fdt_size + len(U_BOOT_SPL_NODTB_DATA) + pad_len
4827 fdt_size = self.checkDtbSizes(data, pad_len, start)
4828
4829 # TPL has no devicetree
4830 start += fdt_size + len(U_BOOT_TPL_DATA)
4831 self.assertEqual(len(data), start)
Simon Glassbb395742020-10-26 17:40:14 -06004832
Simon Glass7098b7f2021-03-21 18:24:30 +13004833 def testSymbolsExpanded(self):
4834 """Test binman can assign symbols in expanded entries"""
4835 entry_args = {
4836 'spl-dtb': '1',
4837 }
4838 self.checkSymbols('197_symbols_expand.dts', U_BOOT_SPL_NODTB_DATA +
4839 U_BOOT_SPL_DTB_DATA, 0x38,
4840 entry_args=entry_args, use_expanded=True)
4841
Simon Glasse1915782021-03-21 18:24:31 +13004842 def testCollection(self):
4843 """Test a collection"""
4844 data = self._DoReadFile('198_collection.dts')
4845 self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA +
Simon Glass80025522022-01-29 14:14:04 -07004846 tools.get_bytes(0xff, 2) + U_BOOT_NODTB_DATA +
4847 tools.get_bytes(0xfe, 3) + U_BOOT_DTB_DATA,
Simon Glasse1915782021-03-21 18:24:31 +13004848 data)
4849
Simon Glass27a7f772021-03-21 18:24:32 +13004850 def testCollectionSection(self):
4851 """Test a collection where a section must be built first"""
4852 # Sections never have their contents when GetData() is called, but when
Simon Glass7e3f89f2021-11-23 11:03:47 -07004853 # BuildSectionData() is called with required=True, a section will force
Simon Glass27a7f772021-03-21 18:24:32 +13004854 # building the contents, producing an error is anything is still
4855 # missing.
4856 data = self._DoReadFile('199_collection_section.dts')
4857 section = U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA
Simon Glass80025522022-01-29 14:14:04 -07004858 self.assertEqual(section + U_BOOT_DATA + tools.get_bytes(0xff, 2) +
4859 section + tools.get_bytes(0xfe, 3) + U_BOOT_DATA,
Simon Glass27a7f772021-03-21 18:24:32 +13004860 data)
4861
Simon Glassf427c5f2021-03-21 18:24:33 +13004862 def testAlignDefault(self):
4863 """Test that default alignment works on sections"""
4864 data = self._DoReadFile('200_align_default.dts')
Simon Glass80025522022-01-29 14:14:04 -07004865 expected = (U_BOOT_DATA + tools.get_bytes(0, 8 - len(U_BOOT_DATA)) +
Simon Glassf427c5f2021-03-21 18:24:33 +13004866 U_BOOT_DATA)
4867 # Special alignment for section
Simon Glass80025522022-01-29 14:14:04 -07004868 expected += tools.get_bytes(0, 32 - len(expected))
Simon Glassf427c5f2021-03-21 18:24:33 +13004869 # No alignment within the nested section
4870 expected += U_BOOT_DATA + U_BOOT_NODTB_DATA;
4871 # Now the final piece, which should be default-aligned
Simon Glass80025522022-01-29 14:14:04 -07004872 expected += tools.get_bytes(0, 88 - len(expected)) + U_BOOT_NODTB_DATA
Simon Glassf427c5f2021-03-21 18:24:33 +13004873 self.assertEqual(expected, data)
Simon Glass27a7f772021-03-21 18:24:32 +13004874
Bin Mengc0b15742021-05-10 20:23:33 +08004875 def testPackOpenSBI(self):
4876 """Test that an image with an OpenSBI binary can be created"""
4877 data = self._DoReadFile('201_opensbi.dts')
4878 self.assertEqual(OPENSBI_DATA, data[:len(OPENSBI_DATA)])
4879
Simon Glass76f496d2021-07-06 10:36:37 -06004880 def testSectionsSingleThread(self):
4881 """Test sections without multithreading"""
4882 data = self._DoReadFileDtb('055_sections.dts', threads=0)[0]
Simon Glass80025522022-01-29 14:14:04 -07004883 expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) +
4884 U_BOOT_DATA + tools.get_bytes(ord('a'), 12) +
4885 U_BOOT_DATA + tools.get_bytes(ord('&'), 4))
Simon Glass76f496d2021-07-06 10:36:37 -06004886 self.assertEqual(expected, data)
4887
4888 def testThreadTimeout(self):
4889 """Test handling a thread that takes too long"""
4890 with self.assertRaises(ValueError) as e:
4891 self._DoTestFile('202_section_timeout.dts',
4892 test_section_timeout=True)
Simon Glass2d59d152021-10-18 12:13:15 -06004893 self.assertIn("Timed out obtaining contents", str(e.exception))
Simon Glass76f496d2021-07-06 10:36:37 -06004894
Simon Glass748a1d42021-07-06 10:36:41 -06004895 def testTiming(self):
4896 """Test output of timing information"""
4897 data = self._DoReadFile('055_sections.dts')
4898 with test_util.capture_sys_output() as (stdout, stderr):
4899 state.TimingShow()
4900 self.assertIn('read:', stdout.getvalue())
4901 self.assertIn('compress:', stdout.getvalue())
4902
Simon Glassadfb8492021-11-03 21:09:18 -06004903 def testUpdateFdtInElf(self):
4904 """Test that we can update the devicetree in an ELF file"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02004905 if not elf.ELF_TOOLS:
4906 self.skipTest('Python elftools not available')
Simon Glassadfb8492021-11-03 21:09:18 -06004907 infile = elf_fname = self.ElfTestFile('u_boot_binman_embed')
4908 outfile = os.path.join(self._indir, 'u-boot.out')
4909 begin_sym = 'dtb_embed_begin'
4910 end_sym = 'dtb_embed_end'
4911 retcode = self._DoTestFile(
4912 '060_fdt_update.dts', update_dtb=True,
4913 update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym]))
4914 self.assertEqual(0, retcode)
4915
4916 # Check that the output file does in fact contact a dtb with the binman
4917 # definition in the correct place
4918 syms = elf.GetSymbolFileOffset(infile,
4919 ['dtb_embed_begin', 'dtb_embed_end'])
Simon Glass80025522022-01-29 14:14:04 -07004920 data = tools.read_file(outfile)
Simon Glassadfb8492021-11-03 21:09:18 -06004921 dtb_data = data[syms['dtb_embed_begin'].offset:
4922 syms['dtb_embed_end'].offset]
4923
4924 dtb = fdt.Fdt.FromData(dtb_data)
4925 dtb.Scan()
4926 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
4927 self.assertEqual({
4928 'image-pos': 0,
4929 'offset': 0,
4930 '_testing:offset': 32,
4931 '_testing:size': 2,
4932 '_testing:image-pos': 32,
4933 'section@0/u-boot:offset': 0,
4934 'section@0/u-boot:size': len(U_BOOT_DATA),
4935 'section@0/u-boot:image-pos': 0,
4936 'section@0:offset': 0,
4937 'section@0:size': 16,
4938 'section@0:image-pos': 0,
4939
4940 'section@1/u-boot:offset': 0,
4941 'section@1/u-boot:size': len(U_BOOT_DATA),
4942 'section@1/u-boot:image-pos': 16,
4943 'section@1:offset': 16,
4944 'section@1:size': 16,
4945 'section@1:image-pos': 16,
4946 'size': 40
4947 }, props)
4948
4949 def testUpdateFdtInElfInvalid(self):
4950 """Test that invalid args are detected with --update-fdt-in-elf"""
4951 with self.assertRaises(ValueError) as e:
4952 self._DoTestFile('060_fdt_update.dts', update_fdt_in_elf='fred')
4953 self.assertIn("Invalid args ['fred'] to --update-fdt-in-elf",
4954 str(e.exception))
4955
4956 def testUpdateFdtInElfNoSyms(self):
4957 """Test that missing symbols are detected with --update-fdt-in-elf"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02004958 if not elf.ELF_TOOLS:
4959 self.skipTest('Python elftools not available')
Simon Glassadfb8492021-11-03 21:09:18 -06004960 infile = elf_fname = self.ElfTestFile('u_boot_binman_embed')
4961 outfile = ''
4962 begin_sym = 'wrong_begin'
4963 end_sym = 'wrong_end'
4964 with self.assertRaises(ValueError) as e:
4965 self._DoTestFile(
4966 '060_fdt_update.dts',
4967 update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym]))
4968 self.assertIn("Expected two symbols 'wrong_begin' and 'wrong_end': got 0:",
4969 str(e.exception))
4970
4971 def testUpdateFdtInElfTooSmall(self):
4972 """Test that an over-large dtb is detected with --update-fdt-in-elf"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02004973 if not elf.ELF_TOOLS:
4974 self.skipTest('Python elftools not available')
Simon Glassadfb8492021-11-03 21:09:18 -06004975 infile = elf_fname = self.ElfTestFile('u_boot_binman_embed_sm')
4976 outfile = os.path.join(self._indir, 'u-boot.out')
4977 begin_sym = 'dtb_embed_begin'
4978 end_sym = 'dtb_embed_end'
4979 with self.assertRaises(ValueError) as e:
4980 self._DoTestFile(
4981 '060_fdt_update.dts', update_dtb=True,
4982 update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym]))
4983 self.assertRegex(
4984 str(e.exception),
4985 "Not enough space in '.*u_boot_binman_embed_sm' for data length.*")
4986
Simon Glass88e04da2021-11-23 11:03:42 -07004987 def testVersion(self):
4988 """Test we can get the binman version"""
4989 version = '(unreleased)'
4990 self.assertEqual(version, state.GetVersion(self._indir))
4991
4992 with self.assertRaises(SystemExit):
4993 with test_util.capture_sys_output() as (_, stderr):
4994 self._DoBinman('-V')
4995 self.assertEqual('Binman %s\n' % version, stderr.getvalue())
4996
4997 # Try running the tool too, just to be safe
4998 result = self._RunBinman('-V')
4999 self.assertEqual('Binman %s\n' % version, result.stderr)
5000
5001 # Set up a version file to make sure that works
5002 version = 'v2025.01-rc2'
Simon Glass80025522022-01-29 14:14:04 -07005003 tools.write_file(os.path.join(self._indir, 'version'), version,
Simon Glass88e04da2021-11-23 11:03:42 -07005004 binary=False)
5005 self.assertEqual(version, state.GetVersion(self._indir))
5006
Simon Glass637958f2021-11-23 21:09:50 -07005007 def testAltFormat(self):
5008 """Test that alternative formats can be used to extract"""
5009 self._DoReadFileRealDtb('213_fdtmap_alt_format.dts')
5010
5011 try:
5012 tmpdir, updated_fname = self._SetupImageInTmpdir()
5013 with test_util.capture_sys_output() as (stdout, _):
5014 self._DoBinman('extract', '-i', updated_fname, '-F', 'list')
5015 self.assertEqual(
5016 '''Flag (-F) Entry type Description
5017fdt fdtmap Extract the devicetree blob from the fdtmap
5018''',
5019 stdout.getvalue())
5020
5021 dtb = os.path.join(tmpdir, 'fdt.dtb')
5022 self._DoBinman('extract', '-i', updated_fname, '-F', 'fdt', '-f',
5023 dtb, 'fdtmap')
5024
5025 # Check that we can read it and it can be scanning, meaning it does
5026 # not have a 16-byte fdtmap header
Simon Glass80025522022-01-29 14:14:04 -07005027 data = tools.read_file(dtb)
Simon Glass637958f2021-11-23 21:09:50 -07005028 dtb = fdt.Fdt.FromData(data)
5029 dtb.Scan()
5030
5031 # Now check u-boot which has no alt_format
5032 fname = os.path.join(tmpdir, 'fdt.dtb')
5033 self._DoBinman('extract', '-i', updated_fname, '-F', 'dummy',
5034 '-f', fname, 'u-boot')
Simon Glass80025522022-01-29 14:14:04 -07005035 data = tools.read_file(fname)
Simon Glass637958f2021-11-23 21:09:50 -07005036 self.assertEqual(U_BOOT_DATA, data)
5037
5038 finally:
5039 shutil.rmtree(tmpdir)
5040
Simon Glass0b00ae62021-11-23 21:09:52 -07005041 def testExtblobList(self):
5042 """Test an image with an external blob list"""
5043 data = self._DoReadFile('215_blob_ext_list.dts')
5044 self.assertEqual(REFCODE_DATA + FSP_M_DATA, data)
5045
5046 def testExtblobListMissing(self):
5047 """Test an image with a missing external blob"""
5048 with self.assertRaises(ValueError) as e:
5049 self._DoReadFile('216_blob_ext_list_missing.dts')
5050 self.assertIn("Filename 'missing-file' not found in input path",
5051 str(e.exception))
5052
5053 def testExtblobListMissingOk(self):
5054 """Test an image with an missing external blob that is allowed"""
5055 with test_util.capture_sys_output() as (stdout, stderr):
5056 self._DoTestFile('216_blob_ext_list_missing.dts',
5057 allow_missing=True)
5058 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07005059 self.assertRegex(err, "Image 'image'.*missing.*: blob-ext")
Simon Glass0b00ae62021-11-23 21:09:52 -07005060
Simon Glass3efb2972021-11-23 21:08:59 -07005061 def testFip(self):
5062 """Basic test of generation of an ARM Firmware Image Package (FIP)"""
5063 data = self._DoReadFile('203_fip.dts')
5064 hdr, fents = fip_util.decode_fip(data)
5065 self.assertEqual(fip_util.HEADER_MAGIC, hdr.name)
5066 self.assertEqual(fip_util.HEADER_SERIAL, hdr.serial)
5067 self.assertEqual(0x123, hdr.flags)
5068
5069 self.assertEqual(2, len(fents))
5070
5071 fent = fents[0]
5072 self.assertEqual(
5073 bytes([0x47, 0xd4, 0x08, 0x6d, 0x4c, 0xfe, 0x98, 0x46,
5074 0x9b, 0x95, 0x29, 0x50, 0xcb, 0xbd, 0x5a, 0x0]), fent.uuid)
5075 self.assertEqual('soc-fw', fent.fip_type)
5076 self.assertEqual(0x88, fent.offset)
5077 self.assertEqual(len(ATF_BL31_DATA), fent.size)
5078 self.assertEqual(0x123456789abcdef, fent.flags)
5079 self.assertEqual(ATF_BL31_DATA, fent.data)
5080 self.assertEqual(True, fent.valid)
5081
5082 fent = fents[1]
5083 self.assertEqual(
5084 bytes([0x65, 0x92, 0x27, 0x03, 0x2f, 0x74, 0xe6, 0x44,
5085 0x8d, 0xff, 0x57, 0x9a, 0xc1, 0xff, 0x06, 0x10]), fent.uuid)
5086 self.assertEqual('scp-fwu-cfg', fent.fip_type)
5087 self.assertEqual(0x8c, fent.offset)
5088 self.assertEqual(len(ATF_BL31_DATA), fent.size)
5089 self.assertEqual(0, fent.flags)
5090 self.assertEqual(ATF_BL2U_DATA, fent.data)
5091 self.assertEqual(True, fent.valid)
5092
5093 def testFipOther(self):
5094 """Basic FIP with something that isn't a external blob"""
5095 data = self._DoReadFile('204_fip_other.dts')
5096 hdr, fents = fip_util.decode_fip(data)
5097
5098 self.assertEqual(2, len(fents))
5099 fent = fents[1]
5100 self.assertEqual('rot-cert', fent.fip_type)
5101 self.assertEqual(b'aa', fent.data)
5102
Simon Glass3efb2972021-11-23 21:08:59 -07005103 def testFipNoType(self):
5104 """FIP with an entry of an unknown type"""
5105 with self.assertRaises(ValueError) as e:
5106 self._DoReadFile('205_fip_no_type.dts')
5107 self.assertIn("Must provide a fip-type (node name 'u-boot' is not a known FIP type)",
5108 str(e.exception))
5109
5110 def testFipUuid(self):
5111 """Basic FIP with a manual uuid"""
5112 data = self._DoReadFile('206_fip_uuid.dts')
5113 hdr, fents = fip_util.decode_fip(data)
5114
5115 self.assertEqual(2, len(fents))
5116 fent = fents[1]
5117 self.assertEqual(None, fent.fip_type)
5118 self.assertEqual(
5119 bytes([0xfc, 0x65, 0x13, 0x92, 0x4a, 0x5b, 0x11, 0xec,
5120 0x94, 0x35, 0xff, 0x2d, 0x1c, 0xfc, 0x79, 0x9c]),
5121 fent.uuid)
5122 self.assertEqual(U_BOOT_DATA, fent.data)
5123
5124 def testFipLs(self):
5125 """Test listing a FIP"""
5126 data = self._DoReadFileRealDtb('207_fip_ls.dts')
5127 hdr, fents = fip_util.decode_fip(data)
5128
5129 try:
5130 tmpdir, updated_fname = self._SetupImageInTmpdir()
5131 with test_util.capture_sys_output() as (stdout, stderr):
5132 self._DoBinman('ls', '-i', updated_fname)
5133 finally:
5134 shutil.rmtree(tmpdir)
5135 lines = stdout.getvalue().splitlines()
5136 expected = [
Simon Glass49cd2b32023-02-07 14:34:18 -07005137'Name Image-pos Size Entry-type Offset Uncomp-size',
5138'--------------------------------------------------------------',
5139'image 0 2d3 section 0',
5140' atf-fip 0 90 atf-fip 0',
5141' soc-fw 88 4 blob-ext 88',
5142' u-boot 8c 4 u-boot 8c',
5143' fdtmap 90 243 fdtmap 90',
Simon Glass3efb2972021-11-23 21:08:59 -07005144]
5145 self.assertEqual(expected, lines)
5146
5147 image = control.images['image']
5148 entries = image.GetEntries()
5149 fdtmap = entries['fdtmap']
5150
5151 fdtmap_data = data[fdtmap.image_pos:fdtmap.image_pos + fdtmap.size]
5152 magic = fdtmap_data[:8]
5153 self.assertEqual(b'_FDTMAP_', magic)
Simon Glass80025522022-01-29 14:14:04 -07005154 self.assertEqual(tools.get_bytes(0, 8), fdtmap_data[8:16])
Simon Glass3efb2972021-11-23 21:08:59 -07005155
5156 fdt_data = fdtmap_data[16:]
5157 dtb = fdt.Fdt.FromData(fdt_data)
5158 dtb.Scan()
5159 props = self._GetPropTree(dtb, BASE_DTB_PROPS, prefix='/')
5160 self.assertEqual({
5161 'atf-fip/soc-fw:image-pos': 136,
5162 'atf-fip/soc-fw:offset': 136,
5163 'atf-fip/soc-fw:size': 4,
5164 'atf-fip/u-boot:image-pos': 140,
5165 'atf-fip/u-boot:offset': 140,
5166 'atf-fip/u-boot:size': 4,
5167 'atf-fip:image-pos': 0,
5168 'atf-fip:offset': 0,
5169 'atf-fip:size': 144,
5170 'image-pos': 0,
5171 'offset': 0,
5172 'fdtmap:image-pos': fdtmap.image_pos,
5173 'fdtmap:offset': fdtmap.offset,
5174 'fdtmap:size': len(fdtmap_data),
5175 'size': len(data),
5176 }, props)
5177
5178 def testFipExtractOneEntry(self):
5179 """Test extracting a single entry fron an FIP"""
5180 self._DoReadFileRealDtb('207_fip_ls.dts')
Simon Glass80025522022-01-29 14:14:04 -07005181 image_fname = tools.get_output_filename('image.bin')
Simon Glass3efb2972021-11-23 21:08:59 -07005182 fname = os.path.join(self._indir, 'output.extact')
5183 control.ExtractEntries(image_fname, fname, None, ['atf-fip/u-boot'])
Simon Glass80025522022-01-29 14:14:04 -07005184 data = tools.read_file(fname)
Simon Glass3efb2972021-11-23 21:08:59 -07005185 self.assertEqual(U_BOOT_DATA, data)
5186
5187 def testFipReplace(self):
5188 """Test replacing a single file in a FIP"""
Simon Glass80025522022-01-29 14:14:04 -07005189 expected = U_BOOT_DATA + tools.get_bytes(0x78, 50)
Simon Glass3efb2972021-11-23 21:08:59 -07005190 data = self._DoReadFileRealDtb('208_fip_replace.dts')
Simon Glass80025522022-01-29 14:14:04 -07005191 updated_fname = tools.get_output_filename('image-updated.bin')
5192 tools.write_file(updated_fname, data)
Simon Glass3efb2972021-11-23 21:08:59 -07005193 entry_name = 'atf-fip/u-boot'
5194 control.WriteEntry(updated_fname, entry_name, expected,
5195 allow_resize=True)
5196 actual = control.ReadEntry(updated_fname, entry_name)
5197 self.assertEqual(expected, actual)
5198
Simon Glass80025522022-01-29 14:14:04 -07005199 new_data = tools.read_file(updated_fname)
Simon Glass3efb2972021-11-23 21:08:59 -07005200 hdr, fents = fip_util.decode_fip(new_data)
5201
5202 self.assertEqual(2, len(fents))
5203
5204 # Check that the FIP entry is updated
5205 fent = fents[1]
5206 self.assertEqual(0x8c, fent.offset)
5207 self.assertEqual(len(expected), fent.size)
5208 self.assertEqual(0, fent.flags)
5209 self.assertEqual(expected, fent.data)
5210 self.assertEqual(True, fent.valid)
5211
5212 def testFipMissing(self):
5213 with test_util.capture_sys_output() as (stdout, stderr):
5214 self._DoTestFile('209_fip_missing.dts', allow_missing=True)
5215 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07005216 self.assertRegex(err, "Image 'image'.*missing.*: rmm-fw")
Simon Glass3efb2972021-11-23 21:08:59 -07005217
5218 def testFipSize(self):
5219 """Test a FIP with a size property"""
5220 data = self._DoReadFile('210_fip_size.dts')
5221 self.assertEqual(0x100 + len(U_BOOT_DATA), len(data))
5222 hdr, fents = fip_util.decode_fip(data)
5223 self.assertEqual(fip_util.HEADER_MAGIC, hdr.name)
5224 self.assertEqual(fip_util.HEADER_SERIAL, hdr.serial)
5225
5226 self.assertEqual(1, len(fents))
5227
5228 fent = fents[0]
5229 self.assertEqual('soc-fw', fent.fip_type)
5230 self.assertEqual(0x60, fent.offset)
5231 self.assertEqual(len(ATF_BL31_DATA), fent.size)
5232 self.assertEqual(ATF_BL31_DATA, fent.data)
5233 self.assertEqual(True, fent.valid)
5234
5235 rest = data[0x60 + len(ATF_BL31_DATA):0x100]
Simon Glass80025522022-01-29 14:14:04 -07005236 self.assertEqual(tools.get_bytes(0xff, len(rest)), rest)
Simon Glass3efb2972021-11-23 21:08:59 -07005237
5238 def testFipBadAlign(self):
5239 """Test that an invalid alignment value in a FIP is detected"""
5240 with self.assertRaises(ValueError) as e:
5241 self._DoTestFile('211_fip_bad_align.dts')
5242 self.assertIn(
5243 "Node \'/binman/atf-fip\': FIP alignment 31 must be a power of two",
5244 str(e.exception))
5245
5246 def testFipCollection(self):
5247 """Test using a FIP in a collection"""
5248 data = self._DoReadFile('212_fip_collection.dts')
5249 entry1 = control.images['image'].GetEntries()['collection']
5250 data1 = data[:entry1.size]
5251 hdr1, fents2 = fip_util.decode_fip(data1)
5252
5253 entry2 = control.images['image'].GetEntries()['atf-fip']
5254 data2 = data[entry2.offset:entry2.offset + entry2.size]
5255 hdr1, fents2 = fip_util.decode_fip(data2)
5256
5257 # The 'collection' entry should have U-Boot included at the end
5258 self.assertEqual(entry1.size - len(U_BOOT_DATA), entry2.size)
5259 self.assertEqual(data1, data2 + U_BOOT_DATA)
5260 self.assertEqual(U_BOOT_DATA, data1[-4:])
5261
5262 # There should be a U-Boot after the final FIP
5263 self.assertEqual(U_BOOT_DATA, data[-4:])
Simon Glass76f496d2021-07-06 10:36:37 -06005264
Simon Glassccae6862022-01-12 13:10:35 -07005265 def testFakeBlob(self):
5266 """Test handling of faking an external blob"""
5267 with test_util.capture_sys_output() as (stdout, stderr):
5268 self._DoTestFile('217_fake_blob.dts', allow_missing=True,
5269 allow_fake_blobs=True)
5270 err = stderr.getvalue()
5271 self.assertRegex(
5272 err,
5273 "Image '.*' has faked external blobs and is non-functional: .*")
Simon Glassccae6862022-01-12 13:10:35 -07005274
Simon Glassceb5f912022-01-09 20:13:46 -07005275 def testExtblobListFaked(self):
5276 """Test an extblob with missing external blob that are faked"""
5277 with test_util.capture_sys_output() as (stdout, stderr):
5278 self._DoTestFile('216_blob_ext_list_missing.dts',
5279 allow_fake_blobs=True)
5280 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07005281 self.assertRegex(err, "Image 'image'.*faked.*: blob-ext-list")
Simon Glassceb5f912022-01-09 20:13:46 -07005282
Simon Glass162017b2022-01-09 20:13:57 -07005283 def testListBintools(self):
5284 args = ['tool', '--list']
5285 with test_util.capture_sys_output() as (stdout, _):
5286 self._DoBinman(*args)
5287 out = stdout.getvalue().splitlines()
5288 self.assertTrue(len(out) >= 2)
5289
5290 def testFetchBintools(self):
5291 def fail_download(url):
Simon Glass80025522022-01-29 14:14:04 -07005292 """Take the tools.download() function by raising an exception"""
Simon Glass162017b2022-01-09 20:13:57 -07005293 raise urllib.error.URLError('my error')
5294
5295 args = ['tool']
5296 with self.assertRaises(ValueError) as e:
5297 self._DoBinman(*args)
5298 self.assertIn("Invalid arguments to 'tool' subcommand",
5299 str(e.exception))
5300
5301 args = ['tool', '--fetch']
5302 with self.assertRaises(ValueError) as e:
5303 self._DoBinman(*args)
5304 self.assertIn('Please specify bintools to fetch', str(e.exception))
5305
5306 args = ['tool', '--fetch', '_testing']
Simon Glass80025522022-01-29 14:14:04 -07005307 with unittest.mock.patch.object(tools, 'download',
Simon Glass162017b2022-01-09 20:13:57 -07005308 side_effect=fail_download):
5309 with test_util.capture_sys_output() as (stdout, _):
5310 self._DoBinman(*args)
5311 self.assertIn('failed to fetch with all methods', stdout.getvalue())
5312
Simon Glass620c4462022-01-09 20:14:11 -07005313 def testBintoolDocs(self):
5314 """Test for creation of bintool documentation"""
5315 with test_util.capture_sys_output() as (stdout, stderr):
5316 control.write_bintool_docs(control.bintool.Bintool.get_tool_list())
5317 self.assertTrue(len(stdout.getvalue()) > 0)
5318
5319 def testBintoolDocsMissing(self):
5320 """Test handling of missing bintool documentation"""
5321 with self.assertRaises(ValueError) as e:
5322 with test_util.capture_sys_output() as (stdout, stderr):
5323 control.write_bintool_docs(
5324 control.bintool.Bintool.get_tool_list(), 'mkimage')
5325 self.assertIn('Documentation is missing for modules: mkimage',
5326 str(e.exception))
5327
Jan Kiszka58c407f2022-01-28 20:37:53 +01005328 def testListWithGenNode(self):
5329 """Check handling of an FDT map when the section cannot be found"""
5330 entry_args = {
5331 'of-list': 'test-fdt1 test-fdt2',
5332 }
5333 data = self._DoReadFileDtb(
5334 '219_fit_gennode.dts',
5335 entry_args=entry_args,
5336 use_real_dtb=True,
5337 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])
5338
5339 try:
5340 tmpdir, updated_fname = self._SetupImageInTmpdir()
5341 with test_util.capture_sys_output() as (stdout, stderr):
5342 self._RunBinman('ls', '-i', updated_fname)
5343 finally:
5344 shutil.rmtree(tmpdir)
5345
Alper Nebi Yasake706d872022-02-08 01:08:05 +03005346 def testFitSubentryUsesBintool(self):
5347 """Test that binman FIT subentries can use bintools"""
5348 command.test_result = self._HandleGbbCommand
5349 entry_args = {
5350 'keydir': 'devkeys',
5351 'bmpblk': 'bmpblk.bin',
5352 }
5353 data, _, _, _ = self._DoReadFileDtb('220_fit_subentry_bintool.dts',
5354 entry_args=entry_args)
5355
Alper Nebi Yasakd4553262022-02-08 01:08:07 +03005356 expected = (GBB_DATA + GBB_DATA + tools.get_bytes(0, 8) +
5357 tools.get_bytes(0, 0x2180 - 16))
Alper Nebi Yasake706d872022-02-08 01:08:05 +03005358 self.assertIn(expected, data)
5359
5360 def testFitSubentryMissingBintool(self):
5361 """Test that binman reports missing bintools for FIT subentries"""
5362 entry_args = {
5363 'keydir': 'devkeys',
5364 }
5365 with test_util.capture_sys_output() as (_, stderr):
5366 self._DoTestFile('220_fit_subentry_bintool.dts',
5367 force_missing_bintools='futility', entry_args=entry_args)
5368 err = stderr.getvalue()
Simon Glass49cd2b32023-02-07 14:34:18 -07005369 self.assertRegex(err, "Image 'image'.*missing bintools.*: futility")
Simon Glassccae6862022-01-12 13:10:35 -07005370
Alper Nebi Yasak1e4ffd82022-02-09 22:02:35 +03005371 def testFitSubentryHashSubnode(self):
5372 """Test an image with a FIT inside"""
5373 data, _, _, out_dtb_name = self._DoReadFileDtb(
5374 '221_fit_subentry_hash.dts', use_real_dtb=True, update_dtb=True)
5375
5376 mkimage_dtb = fdt.Fdt.FromData(data)
5377 mkimage_dtb.Scan()
5378 binman_dtb = fdt.Fdt(out_dtb_name)
5379 binman_dtb.Scan()
5380
5381 # Check that binman didn't add hash values
5382 fnode = binman_dtb.GetNode('/binman/fit/images/kernel/hash')
5383 self.assertNotIn('value', fnode.props)
5384
5385 fnode = binman_dtb.GetNode('/binman/fit/images/fdt-1/hash')
5386 self.assertNotIn('value', fnode.props)
5387
5388 # Check that mkimage added hash values
5389 fnode = mkimage_dtb.GetNode('/images/kernel/hash')
5390 self.assertIn('value', fnode.props)
5391
5392 fnode = mkimage_dtb.GetNode('/images/fdt-1/hash')
5393 self.assertIn('value', fnode.props)
5394
Roger Quadros5cdcea02022-02-19 20:50:04 +02005395 def testPackTeeOs(self):
5396 """Test that an image with an TEE binary can be created"""
5397 data = self._DoReadFile('222_tee_os.dts')
5398 self.assertEqual(TEE_OS_DATA, data[:len(TEE_OS_DATA)])
5399
Simon Glass912339f2022-02-08 11:50:03 -07005400 def testFitFdtOper(self):
5401 """Check handling of a specified FIT operation"""
5402 entry_args = {
5403 'of-list': 'test-fdt1 test-fdt2',
5404 'default-dt': 'test-fdt2',
5405 }
5406 self._DoReadFileDtb(
5407 '223_fit_fdt_oper.dts',
5408 entry_args=entry_args,
5409 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
5410
5411 def testFitFdtBadOper(self):
5412 """Check handling of an FDT map when the section cannot be found"""
5413 with self.assertRaises(ValueError) as exc:
5414 self._DoReadFileDtb('224_fit_bad_oper.dts')
Simon Glass05f71dc2022-03-05 20:19:09 -07005415 self.assertIn("Node '/binman/fit': subnode 'images/@fdt-SEQ': Unknown operation 'unknown'",
Simon Glass912339f2022-02-08 11:50:03 -07005416 str(exc.exception))
5417
Simon Glassdd156a42022-03-05 20:18:59 -07005418 def test_uses_expand_size(self):
5419 """Test that the 'expand-size' property cannot be used anymore"""
5420 with self.assertRaises(ValueError) as e:
5421 data = self._DoReadFile('225_expand_size_bad.dts')
5422 self.assertIn(
5423 "Node '/binman/u-boot': Please use 'extend-size' instead of 'expand-size'",
5424 str(e.exception))
5425
Simon Glass5f423422022-03-05 20:19:12 -07005426 def testFitSplitElf(self):
5427 """Test an image with an FIT with an split-elf operation"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005428 if not elf.ELF_TOOLS:
5429 self.skipTest('Python elftools not available')
Simon Glass5f423422022-03-05 20:19:12 -07005430 entry_args = {
5431 'of-list': 'test-fdt1 test-fdt2',
5432 'default-dt': 'test-fdt2',
5433 'atf-bl31-path': 'bl31.elf',
5434 'tee-os-path': 'tee.elf',
5435 }
5436 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
5437 data = self._DoReadFileDtb(
5438 '226_fit_split_elf.dts',
5439 entry_args=entry_args,
5440 extra_indirs=[test_subdir])[0]
5441
5442 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
5443 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
5444
5445 base_keys = {'description', 'type', 'arch', 'os', 'compression',
5446 'data', 'load'}
5447 dtb = fdt.Fdt.FromData(fit_data)
5448 dtb.Scan()
5449
5450 elf_data = tools.read_file(os.path.join(self._indir, 'bl31.elf'))
5451 segments, entry = elf.read_loadable_segments(elf_data)
5452
5453 # We assume there are two segments
5454 self.assertEquals(2, len(segments))
5455
5456 atf1 = dtb.GetNode('/images/atf-1')
5457 _, start, data = segments[0]
5458 self.assertEqual(base_keys | {'entry'}, atf1.props.keys())
5459 self.assertEqual(entry,
5460 fdt_util.fdt32_to_cpu(atf1.props['entry'].value))
5461 self.assertEqual(start,
5462 fdt_util.fdt32_to_cpu(atf1.props['load'].value))
5463 self.assertEqual(data, atf1.props['data'].bytes)
5464
Jonas Karlmand2c7d902023-01-21 19:01:48 +00005465 hash_node = atf1.FindNode('hash')
5466 self.assertIsNotNone(hash_node)
5467 self.assertEqual({'algo', 'value'}, hash_node.props.keys())
5468
Simon Glass5f423422022-03-05 20:19:12 -07005469 atf2 = dtb.GetNode('/images/atf-2')
5470 self.assertEqual(base_keys, atf2.props.keys())
5471 _, start, data = segments[1]
5472 self.assertEqual(start,
5473 fdt_util.fdt32_to_cpu(atf2.props['load'].value))
5474 self.assertEqual(data, atf2.props['data'].bytes)
5475
Jonas Karlmand2c7d902023-01-21 19:01:48 +00005476 hash_node = atf2.FindNode('hash')
5477 self.assertIsNotNone(hash_node)
5478 self.assertEqual({'algo', 'value'}, hash_node.props.keys())
5479
5480 hash_node = dtb.GetNode('/images/tee-1/hash-1')
5481 self.assertIsNotNone(hash_node)
5482 self.assertEqual({'algo', 'value'}, hash_node.props.keys())
5483
Simon Glass5f423422022-03-05 20:19:12 -07005484 conf = dtb.GetNode('/configurations')
5485 self.assertEqual({'default'}, conf.props.keys())
5486
5487 for subnode in conf.subnodes:
5488 self.assertEqual({'description', 'fdt', 'loadables'},
5489 subnode.props.keys())
5490 self.assertEqual(
5491 ['atf-1', 'atf-2', 'tee-1', 'tee-2'],
5492 fdt_util.GetStringList(subnode, 'loadables'))
5493
5494 def _check_bad_fit(self, dts):
5495 """Check a bad FIT
5496
5497 This runs with the given dts and returns the assertion raised
5498
5499 Args:
5500 dts (str): dts filename to use
5501
5502 Returns:
5503 str: Assertion string raised
5504 """
5505 entry_args = {
5506 'of-list': 'test-fdt1 test-fdt2',
5507 'default-dt': 'test-fdt2',
5508 'atf-bl31-path': 'bl31.elf',
5509 'tee-os-path': 'tee.elf',
5510 }
5511 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
5512 with self.assertRaises(ValueError) as exc:
5513 self._DoReadFileDtb(dts, entry_args=entry_args,
5514 extra_indirs=[test_subdir])[0]
5515 return str(exc.exception)
5516
5517 def testFitSplitElfBadElf(self):
5518 """Test a FIT split-elf operation with an invalid ELF file"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005519 if not elf.ELF_TOOLS:
5520 self.skipTest('Python elftools not available')
Simon Glass5f423422022-03-05 20:19:12 -07005521 TestFunctional._MakeInputFile('bad.elf', tools.get_bytes(100, 100))
5522 entry_args = {
5523 'of-list': 'test-fdt1 test-fdt2',
5524 'default-dt': 'test-fdt2',
5525 'atf-bl31-path': 'bad.elf',
5526 'tee-os-path': 'tee.elf',
5527 }
5528 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
5529 with self.assertRaises(ValueError) as exc:
5530 self._DoReadFileDtb(
5531 '226_fit_split_elf.dts',
5532 entry_args=entry_args,
5533 extra_indirs=[test_subdir])[0]
5534 self.assertIn(
5535 "Node '/binman/fit': subnode 'images/@atf-SEQ': Failed to read ELF file: Magic number does not match",
5536 str(exc.exception))
5537
Simon Glass5f423422022-03-05 20:19:12 -07005538 def checkFitSplitElf(self, **kwargs):
Simon Glass7d3e4072022-08-07 09:46:46 -06005539 """Test an split-elf FIT with a missing ELF file
5540
5541 Args:
5542 kwargs (dict of str): Arguments to pass to _DoTestFile()
5543
5544 Returns:
5545 tuple:
5546 str: stdout result
5547 str: stderr result
5548 """
Simon Glass5f423422022-03-05 20:19:12 -07005549 entry_args = {
5550 'of-list': 'test-fdt1 test-fdt2',
5551 'default-dt': 'test-fdt2',
5552 'atf-bl31-path': 'bl31.elf',
5553 'tee-os-path': 'missing.elf',
5554 }
5555 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
5556 with test_util.capture_sys_output() as (stdout, stderr):
5557 self._DoTestFile(
5558 '226_fit_split_elf.dts', entry_args=entry_args,
Simon Glass7d3e4072022-08-07 09:46:46 -06005559 extra_indirs=[test_subdir], verbosity=3, **kwargs)
5560 out = stdout.getvalue()
5561 err = stderr.getvalue()
5562 return out, err
Simon Glass5f423422022-03-05 20:19:12 -07005563
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005564 def testFitSplitElfBadDirective(self):
5565 """Test a FIT split-elf invalid fit,xxx directive in an image node"""
5566 if not elf.ELF_TOOLS:
5567 self.skipTest('Python elftools not available')
5568 err = self._check_bad_fit('227_fit_bad_dir.dts')
5569 self.assertIn(
5570 "Node '/binman/fit': subnode 'images/@atf-SEQ': Unknown directive 'fit,something'",
5571 err)
5572
5573 def testFitSplitElfBadDirectiveConfig(self):
5574 """Test a FIT split-elf with invalid fit,xxx directive in config"""
5575 if not elf.ELF_TOOLS:
5576 self.skipTest('Python elftools not available')
5577 err = self._check_bad_fit('228_fit_bad_dir_config.dts')
5578 self.assertEqual(
5579 "Node '/binman/fit': subnode 'configurations/@config-SEQ': Unknown directive 'fit,config'",
5580 err)
5581
5582
Simon Glass5f423422022-03-05 20:19:12 -07005583 def testFitSplitElfMissing(self):
5584 """Test an split-elf FIT with a missing ELF file"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005585 if not elf.ELF_TOOLS:
5586 self.skipTest('Python elftools not available')
Simon Glass7d3e4072022-08-07 09:46:46 -06005587 out, err = self.checkFitSplitElf(allow_missing=True)
Simon Glass5f423422022-03-05 20:19:12 -07005588 self.assertRegex(
5589 err,
5590 "Image '.*' is missing external blobs and is non-functional: .*")
Simon Glass7d3e4072022-08-07 09:46:46 -06005591 self.assertNotRegex(out, '.*Faked blob.*')
5592 fname = tools.get_output_filename('binman-fake/missing.elf')
5593 self.assertFalse(os.path.exists(fname))
Simon Glass5f423422022-03-05 20:19:12 -07005594
5595 def testFitSplitElfFaked(self):
5596 """Test an split-elf FIT with faked ELF file"""
Stefan Herbrechtsmeier732742e2022-08-19 16:25:18 +02005597 if not elf.ELF_TOOLS:
5598 self.skipTest('Python elftools not available')
Simon Glass7d3e4072022-08-07 09:46:46 -06005599 out, err = self.checkFitSplitElf(allow_missing=True, allow_fake_blobs=True)
Simon Glass5f423422022-03-05 20:19:12 -07005600 self.assertRegex(
5601 err,
5602 "Image '.*' is missing external blobs and is non-functional: .*")
Simon Glass7d3e4072022-08-07 09:46:46 -06005603 self.assertRegex(
5604 out,
5605 "Entry '/binman/fit/images/@tee-SEQ/tee-os': Faked blob '.*binman-fake/missing.elf")
5606 fname = tools.get_output_filename('binman-fake/missing.elf')
5607 self.assertTrue(os.path.exists(fname))
Simon Glass5f423422022-03-05 20:19:12 -07005608
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005609 def testMkimageMissingBlob(self):
5610 """Test using mkimage to build an image"""
5611 with test_util.capture_sys_output() as (stdout, stderr):
5612 self._DoTestFile('229_mkimage_missing.dts', allow_missing=True,
5613 allow_fake_blobs=True)
5614 err = stderr.getvalue()
5615 self.assertRegex(
5616 err,
5617 "Image '.*' has faked external blobs and is non-functional: .*")
5618
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005619 def testPreLoad(self):
5620 """Test an image with a pre-load header"""
5621 entry_args = {
5622 'pre-load-key-path': '.',
5623 }
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005624 data, _, _, _ = self._DoReadFileDtb('230_pre_load.dts',
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005625 entry_args=entry_args)
5626 self.assertEqual(PRE_LOAD_MAGIC, data[:len(PRE_LOAD_MAGIC)])
5627 self.assertEqual(PRE_LOAD_VERSION, data[4:4 + len(PRE_LOAD_VERSION)])
5628 self.assertEqual(PRE_LOAD_HDR_SIZE, data[8:8 + len(PRE_LOAD_HDR_SIZE)])
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005629 data = self._DoReadFile('230_pre_load.dts')
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005630 self.assertEqual(PRE_LOAD_MAGIC, data[:len(PRE_LOAD_MAGIC)])
5631 self.assertEqual(PRE_LOAD_VERSION, data[4:4 + len(PRE_LOAD_VERSION)])
5632 self.assertEqual(PRE_LOAD_HDR_SIZE, data[8:8 + len(PRE_LOAD_HDR_SIZE)])
5633
5634 def testPreLoadPkcs(self):
5635 """Test an image with a pre-load header with padding pkcs"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005636 data = self._DoReadFile('231_pre_load_pkcs.dts')
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005637 self.assertEqual(PRE_LOAD_MAGIC, data[:len(PRE_LOAD_MAGIC)])
5638 self.assertEqual(PRE_LOAD_VERSION, data[4:4 + len(PRE_LOAD_VERSION)])
5639 self.assertEqual(PRE_LOAD_HDR_SIZE, data[8:8 + len(PRE_LOAD_HDR_SIZE)])
5640
5641 def testPreLoadPss(self):
5642 """Test an image with a pre-load header with padding pss"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005643 data = self._DoReadFile('232_pre_load_pss.dts')
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005644 self.assertEqual(PRE_LOAD_MAGIC, data[:len(PRE_LOAD_MAGIC)])
5645 self.assertEqual(PRE_LOAD_VERSION, data[4:4 + len(PRE_LOAD_VERSION)])
5646 self.assertEqual(PRE_LOAD_HDR_SIZE, data[8:8 + len(PRE_LOAD_HDR_SIZE)])
5647
5648 def testPreLoadInvalidPadding(self):
5649 """Test an image with a pre-load header with an invalid padding"""
5650 with self.assertRaises(ValueError) as e:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005651 data = self._DoReadFile('233_pre_load_invalid_padding.dts')
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005652
5653 def testPreLoadInvalidSha(self):
5654 """Test an image with a pre-load header with an invalid hash"""
5655 with self.assertRaises(ValueError) as e:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005656 data = self._DoReadFile('234_pre_load_invalid_sha.dts')
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005657
5658 def testPreLoadInvalidAlgo(self):
5659 """Test an image with a pre-load header with an invalid algo"""
5660 with self.assertRaises(ValueError) as e:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005661 data = self._DoReadFile('235_pre_load_invalid_algo.dts')
Philippe Reynesebe96cb2022-03-28 22:57:04 +02005662
5663 def testPreLoadInvalidKey(self):
5664 """Test an image with a pre-load header with an invalid key"""
5665 with self.assertRaises(ValueError) as e:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005666 data = self._DoReadFile('236_pre_load_invalid_key.dts')
Roger Quadros5cdcea02022-02-19 20:50:04 +02005667
Alper Nebi Yasak5cff63f2022-03-27 18:31:44 +03005668 def _CheckSafeUniqueNames(self, *images):
5669 """Check all entries of given images for unsafe unique names"""
5670 for image in images:
5671 entries = {}
5672 image._CollectEntries(entries, {}, image)
5673 for entry in entries.values():
5674 uniq = entry.GetUniqueName()
5675
5676 # Used as part of a filename, so must not be absolute paths.
5677 self.assertFalse(os.path.isabs(uniq))
5678
5679 def testSafeUniqueNames(self):
5680 """Test entry unique names are safe in single image configuration"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005681 data = self._DoReadFileRealDtb('237_unique_names.dts')
Alper Nebi Yasak5cff63f2022-03-27 18:31:44 +03005682
5683 orig_image = control.images['image']
5684 image_fname = tools.get_output_filename('image.bin')
5685 image = Image.FromFile(image_fname)
5686
5687 self._CheckSafeUniqueNames(orig_image, image)
5688
5689 def testSafeUniqueNamesMulti(self):
5690 """Test entry unique names are safe with multiple images"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005691 data = self._DoReadFileRealDtb('238_unique_names_multi.dts')
Alper Nebi Yasak5cff63f2022-03-27 18:31:44 +03005692
5693 orig_image = control.images['image']
5694 image_fname = tools.get_output_filename('image.bin')
5695 image = Image.FromFile(image_fname)
5696
5697 self._CheckSafeUniqueNames(orig_image, image)
5698
Alper Nebi Yasake63ca5a2022-03-27 18:31:45 +03005699 def testReplaceCmdWithBintool(self):
5700 """Test replacing an entry that needs a bintool to pack"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005701 data = self._DoReadFileRealDtb('239_replace_with_bintool.dts')
Alper Nebi Yasake63ca5a2022-03-27 18:31:45 +03005702 expected = U_BOOT_DATA + b'aa'
5703 self.assertEqual(expected, data[:len(expected)])
5704
5705 try:
5706 tmpdir, updated_fname = self._SetupImageInTmpdir()
5707 fname = os.path.join(tmpdir, 'update-testing.bin')
5708 tools.write_file(fname, b'zz')
5709 self._DoBinman('replace', '-i', updated_fname,
5710 '_testing', '-f', fname)
5711
5712 data = tools.read_file(updated_fname)
5713 expected = U_BOOT_DATA + b'zz'
5714 self.assertEqual(expected, data[:len(expected)])
5715 finally:
5716 shutil.rmtree(tmpdir)
5717
5718 def testReplaceCmdOtherWithBintool(self):
5719 """Test replacing an entry when another needs a bintool to pack"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005720 data = self._DoReadFileRealDtb('239_replace_with_bintool.dts')
Alper Nebi Yasake63ca5a2022-03-27 18:31:45 +03005721 expected = U_BOOT_DATA + b'aa'
5722 self.assertEqual(expected, data[:len(expected)])
5723
5724 try:
5725 tmpdir, updated_fname = self._SetupImageInTmpdir()
5726 fname = os.path.join(tmpdir, 'update-u-boot.bin')
5727 tools.write_file(fname, b'x' * len(U_BOOT_DATA))
5728 self._DoBinman('replace', '-i', updated_fname,
5729 'u-boot', '-f', fname)
5730
5731 data = tools.read_file(updated_fname)
5732 expected = b'x' * len(U_BOOT_DATA) + b'aa'
5733 self.assertEqual(expected, data[:len(expected)])
5734 finally:
5735 shutil.rmtree(tmpdir)
5736
Alper Nebi Yasak00c68f12022-03-27 18:31:46 +03005737 def testReplaceResizeNoRepackSameSize(self):
5738 """Test replacing entries with same-size data without repacking"""
5739 expected = b'x' * len(U_BOOT_DATA)
5740 data, expected_fdtmap, _ = self._RunReplaceCmd('u-boot', expected)
5741 self.assertEqual(expected, data)
5742
5743 path, fdtmap = state.GetFdtContents('fdtmap')
5744 self.assertIsNotNone(path)
5745 self.assertEqual(expected_fdtmap, fdtmap)
5746
5747 def testReplaceResizeNoRepackSmallerSize(self):
5748 """Test replacing entries with smaller-size data without repacking"""
5749 new_data = b'x'
5750 data, expected_fdtmap, _ = self._RunReplaceCmd('u-boot', new_data)
5751 expected = new_data.ljust(len(U_BOOT_DATA), b'\0')
5752 self.assertEqual(expected, data)
5753
5754 path, fdtmap = state.GetFdtContents('fdtmap')
5755 self.assertIsNotNone(path)
5756 self.assertEqual(expected_fdtmap, fdtmap)
5757
Alper Nebi Yasak6cadc502022-03-27 18:31:48 +03005758 def testExtractFit(self):
5759 """Test extracting a FIT section"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005760 self._DoReadFileRealDtb('240_fit_extract_replace.dts')
Alper Nebi Yasak6cadc502022-03-27 18:31:48 +03005761 image_fname = tools.get_output_filename('image.bin')
5762
5763 fit_data = control.ReadEntry(image_fname, 'fit')
5764 fit = fdt.Fdt.FromData(fit_data)
5765 fit.Scan()
5766
5767 # Check subentry data inside the extracted fit
5768 for node_path, expected in [
5769 ('/images/kernel', U_BOOT_DATA),
5770 ('/images/fdt-1', U_BOOT_NODTB_DATA),
5771 ('/images/scr-1', COMPRESS_DATA),
5772 ]:
5773 node = fit.GetNode(node_path)
5774 data = fit.GetProps(node)['data'].bytes
5775 self.assertEqual(expected, data)
5776
5777 def testExtractFitSubentries(self):
5778 """Test extracting FIT section subentries"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005779 self._DoReadFileRealDtb('240_fit_extract_replace.dts')
Alper Nebi Yasak6cadc502022-03-27 18:31:48 +03005780 image_fname = tools.get_output_filename('image.bin')
5781
5782 for entry_path, expected in [
5783 ('fit/kernel', U_BOOT_DATA),
5784 ('fit/kernel/u-boot', U_BOOT_DATA),
5785 ('fit/fdt-1', U_BOOT_NODTB_DATA),
5786 ('fit/fdt-1/u-boot-nodtb', U_BOOT_NODTB_DATA),
5787 ('fit/scr-1', COMPRESS_DATA),
5788 ('fit/scr-1/blob', COMPRESS_DATA),
5789 ]:
5790 data = control.ReadEntry(image_fname, entry_path)
5791 self.assertEqual(expected, data)
5792
Alper Nebi Yasak49892642022-03-27 18:31:49 +03005793 def testReplaceFitSubentryLeafSameSize(self):
5794 """Test replacing a FIT leaf subentry with same-size data"""
5795 new_data = b'x' * len(U_BOOT_DATA)
5796 data, expected_fdtmap, _ = self._RunReplaceCmd(
5797 'fit/kernel/u-boot', new_data,
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005798 dts='240_fit_extract_replace.dts')
Alper Nebi Yasak49892642022-03-27 18:31:49 +03005799 self.assertEqual(new_data, data)
5800
5801 path, fdtmap = state.GetFdtContents('fdtmap')
5802 self.assertIsNotNone(path)
5803 self.assertEqual(expected_fdtmap, fdtmap)
5804
5805 def testReplaceFitSubentryLeafBiggerSize(self):
5806 """Test replacing a FIT leaf subentry with bigger-size data"""
5807 new_data = b'ub' * len(U_BOOT_NODTB_DATA)
5808 data, expected_fdtmap, _ = self._RunReplaceCmd(
5809 'fit/fdt-1/u-boot-nodtb', new_data,
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005810 dts='240_fit_extract_replace.dts')
Alper Nebi Yasak49892642022-03-27 18:31:49 +03005811 self.assertEqual(new_data, data)
5812
5813 # Will be repacked, so fdtmap must change
5814 path, fdtmap = state.GetFdtContents('fdtmap')
5815 self.assertIsNotNone(path)
5816 self.assertNotEqual(expected_fdtmap, fdtmap)
5817
5818 def testReplaceFitSubentryLeafSmallerSize(self):
5819 """Test replacing a FIT leaf subentry with smaller-size data"""
5820 new_data = b'x'
5821 expected = new_data.ljust(len(U_BOOT_NODTB_DATA), b'\0')
5822 data, expected_fdtmap, _ = self._RunReplaceCmd(
5823 'fit/fdt-1/u-boot-nodtb', new_data,
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005824 dts='240_fit_extract_replace.dts')
Alper Nebi Yasak49892642022-03-27 18:31:49 +03005825 self.assertEqual(expected, data)
5826
5827 path, fdtmap = state.GetFdtContents('fdtmap')
5828 self.assertIsNotNone(path)
5829 self.assertEqual(expected_fdtmap, fdtmap)
5830
Alper Nebi Yasak1d44c8e2022-03-27 18:31:50 +03005831 def testReplaceSectionSimple(self):
Simon Glass49b77e82023-03-02 17:02:44 -07005832 """Test replacing a simple section with same-sized data"""
Alper Nebi Yasak1d44c8e2022-03-27 18:31:50 +03005833 new_data = b'w' * len(COMPRESS_DATA + U_BOOT_DATA)
Simon Glass49b77e82023-03-02 17:02:44 -07005834 data, expected_fdtmap, image = self._RunReplaceCmd('section',
5835 new_data, dts='241_replace_section_simple.dts')
5836 self.assertEqual(new_data, data)
5837
5838 entries = image.GetEntries()
5839 self.assertIn('section', entries)
5840 entry = entries['section']
5841 self.assertEqual(len(new_data), entry.size)
5842
5843 def testReplaceSectionLarger(self):
5844 """Test replacing a simple section with larger data"""
5845 new_data = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) + 1)
5846 data, expected_fdtmap, image = self._RunReplaceCmd('section',
5847 new_data, dts='241_replace_section_simple.dts')
5848 self.assertEqual(new_data, data)
5849
5850 entries = image.GetEntries()
5851 self.assertIn('section', entries)
5852 entry = entries['section']
5853 self.assertEqual(len(new_data), entry.size)
5854 fentry = entries['fdtmap']
5855 self.assertEqual(entry.offset + entry.size, fentry.offset)
5856
5857 def testReplaceSectionSmaller(self):
5858 """Test replacing a simple section with smaller data"""
5859 new_data = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) - 1) + b'\0'
5860 data, expected_fdtmap, image = self._RunReplaceCmd('section',
5861 new_data, dts='241_replace_section_simple.dts')
5862 self.assertEqual(new_data, data)
5863
5864 # The new size is the same as the old, just with a pad byte at the end
5865 entries = image.GetEntries()
5866 self.assertIn('section', entries)
5867 entry = entries['section']
5868 self.assertEqual(len(new_data), entry.size)
5869
5870 def testReplaceSectionSmallerAllow(self):
5871 """Test failing to replace a simple section with smaller data"""
5872 new_data = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) - 1)
5873 try:
5874 state.SetAllowEntryContraction(True)
5875 with self.assertRaises(ValueError) as exc:
5876 self._RunReplaceCmd('section', new_data,
5877 dts='241_replace_section_simple.dts')
5878 finally:
5879 state.SetAllowEntryContraction(False)
5880
5881 # Since we have no information about the position of things within the
5882 # section, we cannot adjust the position of /section-u-boot so it ends
5883 # up outside the section
Simon Glassc6b283f2022-08-13 11:40:46 -06005884 self.assertIn(
Simon Glass49b77e82023-03-02 17:02:44 -07005885 "Node '/section/u-boot': Offset 0x24 (36) size 0x4 (4) is outside "
5886 "the section '/section' starting at 0x0 (0) of size 0x27 (39)",
Simon Glassc6b283f2022-08-13 11:40:46 -06005887 str(exc.exception))
Alper Nebi Yasak1d44c8e2022-03-27 18:31:50 +03005888
Simon Glass8fbca772022-08-13 11:40:48 -06005889 def testMkimageImagename(self):
5890 """Test using mkimage with -n holding the data too"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005891 data = self._DoReadFile('242_mkimage_name.dts')
Simon Glass8fbca772022-08-13 11:40:48 -06005892
5893 # Check that the data appears in the file somewhere
5894 self.assertIn(U_BOOT_SPL_DATA, data)
5895
Simon Glassbb7d3bb2022-09-06 20:26:52 -06005896 # Get struct legacy_img_hdr -> ih_name
Simon Glass8fbca772022-08-13 11:40:48 -06005897 name = data[0x20:0x40]
5898
5899 # Build the filename that we expect to be placed in there, by virtue of
5900 # the -n paraameter
5901 expect = os.path.join(tools.get_output_dir(), 'mkimage.mkimage')
5902
5903 # Check that the image name is set to the temporary filename used
5904 self.assertEqual(expect.encode('utf-8')[:0x20], name)
5905
Simon Glassb1669752022-08-13 11:40:49 -06005906 def testMkimageImage(self):
5907 """Test using mkimage with -n holding the data too"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005908 data = self._DoReadFile('243_mkimage_image.dts')
Simon Glassb1669752022-08-13 11:40:49 -06005909
5910 # Check that the data appears in the file somewhere
5911 self.assertIn(U_BOOT_SPL_DATA, data)
5912
Simon Glassbb7d3bb2022-09-06 20:26:52 -06005913 # Get struct legacy_img_hdr -> ih_name
Simon Glassb1669752022-08-13 11:40:49 -06005914 name = data[0x20:0x40]
5915
5916 # Build the filename that we expect to be placed in there, by virtue of
5917 # the -n paraameter
5918 expect = os.path.join(tools.get_output_dir(), 'mkimage-n.mkimage')
5919
5920 # Check that the image name is set to the temporary filename used
5921 self.assertEqual(expect.encode('utf-8')[:0x20], name)
5922
5923 # Check the corect data is in the imagename file
5924 self.assertEqual(U_BOOT_DATA, tools.read_file(expect))
5925
5926 def testMkimageImageNoContent(self):
5927 """Test using mkimage with -n and no data"""
5928 with self.assertRaises(ValueError) as exc:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005929 self._DoReadFile('244_mkimage_image_no_content.dts')
Simon Glassb1669752022-08-13 11:40:49 -06005930 self.assertIn('Could not complete processing of contents',
5931 str(exc.exception))
5932
5933 def testMkimageImageBad(self):
5934 """Test using mkimage with imagename node and data-to-imagename"""
5935 with self.assertRaises(ValueError) as exc:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005936 self._DoReadFile('245_mkimage_image_bad.dts')
Simon Glassb1669752022-08-13 11:40:49 -06005937 self.assertIn('Cannot use both imagename node and data-to-imagename',
5938 str(exc.exception))
5939
Simon Glassbd5cd882022-08-13 11:40:50 -06005940 def testCollectionOther(self):
5941 """Test a collection where the data comes from another section"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005942 data = self._DoReadFile('246_collection_other.dts')
Simon Glassbd5cd882022-08-13 11:40:50 -06005943 self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA +
5944 tools.get_bytes(0xff, 2) + U_BOOT_NODTB_DATA +
5945 tools.get_bytes(0xfe, 3) + U_BOOT_DTB_DATA,
5946 data)
5947
5948 def testMkimageCollection(self):
5949 """Test using a collection referring to an entry in a mkimage entry"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005950 data = self._DoReadFile('247_mkimage_coll.dts')
Simon Glassbd5cd882022-08-13 11:40:50 -06005951 expect = U_BOOT_SPL_DATA + U_BOOT_DATA
5952 self.assertEqual(expect, data[:len(expect)])
5953
Stefan Herbrechtsmeier11121d32022-08-19 16:25:25 +02005954 def testCompressDtbPrependInvalid(self):
5955 """Test that invalid header is detected"""
5956 with self.assertRaises(ValueError) as e:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005957 self._DoReadFileDtb('248_compress_dtb_prepend_invalid.dts')
Stefan Herbrechtsmeier11121d32022-08-19 16:25:25 +02005958 self.assertIn("Node '/binman/u-boot-dtb': Invalid prepend in "
5959 "'u-boot-dtb': 'invalid'", str(e.exception))
5960
5961 def testCompressDtbPrependLength(self):
5962 """Test that compress with length header works as expected"""
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005963 data = self._DoReadFileRealDtb('249_compress_dtb_prepend_length.dts')
Stefan Herbrechtsmeier11121d32022-08-19 16:25:25 +02005964 image = control.images['image']
5965 entries = image.GetEntries()
5966 self.assertIn('u-boot-dtb', entries)
5967 u_boot_dtb = entries['u-boot-dtb']
5968 self.assertIn('fdtmap', entries)
5969 fdtmap = entries['fdtmap']
5970
5971 image_fname = tools.get_output_filename('image.bin')
5972 orig = control.ReadEntry(image_fname, 'u-boot-dtb')
5973 dtb = fdt.Fdt.FromData(orig)
5974 dtb.Scan()
5975 props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
5976 expected = {
5977 'u-boot:size': len(U_BOOT_DATA),
5978 'u-boot-dtb:uncomp-size': len(orig),
5979 'u-boot-dtb:size': u_boot_dtb.size,
5980 'fdtmap:size': fdtmap.size,
5981 'size': len(data),
5982 }
5983 self.assertEqual(expected, props)
5984
5985 # Check implementation
5986 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
5987 rest = data[len(U_BOOT_DATA):]
5988 comp_data_len = struct.unpack('<I', rest[:4])[0]
5989 comp_data = rest[4:4 + comp_data_len]
5990 orig2 = self._decompress(comp_data)
5991 self.assertEqual(orig, orig2)
5992
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +02005993 def testInvalidCompress(self):
5994 """Test that invalid compress algorithm is detected"""
5995 with self.assertRaises(ValueError) as e:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02005996 self._DoTestFile('250_compress_dtb_invalid.dts')
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +02005997 self.assertIn("Unknown algorithm 'invalid'", str(e.exception))
5998
Stefan Herbrechtsmeiera14bee02022-08-19 16:25:32 +02005999 def testCompUtilCompressions(self):
6000 """Test compression algorithms"""
6001 for bintool in self.comp_bintools.values():
6002 self._CheckBintool(bintool)
6003 data = bintool.compress(COMPRESS_DATA)
6004 self.assertNotEqual(COMPRESS_DATA, data)
6005 orig = bintool.decompress(data)
6006 self.assertEquals(COMPRESS_DATA, orig)
6007
6008 def testCompUtilVersions(self):
6009 """Test tool version of compression algorithms"""
6010 for bintool in self.comp_bintools.values():
6011 self._CheckBintool(bintool)
6012 version = bintool.version()
6013 self.assertRegex(version, '^v?[0-9]+[0-9.]*')
6014
6015 def testCompUtilPadding(self):
6016 """Test padding of compression algorithms"""
Stefan Herbrechtsmeiera5e4dcb2022-08-19 16:25:38 +02006017 # Skip zstd because it doesn't support padding
6018 for bintool in [v for k,v in self.comp_bintools.items() if k != 'zstd']:
Stefan Herbrechtsmeiera14bee02022-08-19 16:25:32 +02006019 self._CheckBintool(bintool)
6020 data = bintool.compress(COMPRESS_DATA)
6021 self.assertNotEqual(COMPRESS_DATA, data)
6022 data += tools.get_bytes(0, 64)
6023 orig = bintool.decompress(data)
6024 self.assertEquals(COMPRESS_DATA, orig)
6025
Stefan Herbrechtsmeiera5e4dcb2022-08-19 16:25:38 +02006026 def testCompressDtbZstd(self):
6027 """Test that zstd compress of device-tree files failed"""
6028 with self.assertRaises(ValueError) as e:
Stefan Herbrechtsmeier3074bdd2022-08-23 12:46:09 +02006029 self._DoTestFile('251_compress_dtb_zstd.dts')
Stefan Herbrechtsmeiera5e4dcb2022-08-19 16:25:38 +02006030 self.assertIn("Node '/binman/u-boot-dtb': The zstd compression "
6031 "requires a length header", str(e.exception))
6032
Quentin Schulz9b5c6482022-09-02 15:10:48 +02006033 def testMkimageMultipleDataFiles(self):
6034 """Test passing multiple files to mkimage in a mkimage entry"""
6035 data = self._DoReadFile('252_mkimage_mult_data.dts')
6036 # Size of files are packed in their 4B big-endian format
6037 expect = struct.pack('>I', len(U_BOOT_TPL_DATA))
6038 expect += struct.pack('>I', len(U_BOOT_SPL_DATA))
6039 # Size info is always followed by a 4B zero value.
6040 expect += tools.get_bytes(0, 4)
6041 expect += U_BOOT_TPL_DATA
6042 # All but last files are 4B-aligned
6043 align_pad = len(U_BOOT_TPL_DATA) % 4
6044 if align_pad:
6045 expect += tools.get_bytes(0, align_pad)
6046 expect += U_BOOT_SPL_DATA
6047 self.assertEqual(expect, data[-len(expect):])
6048
6049 def testMkimageMultipleNoContent(self):
6050 """Test passing multiple data files to mkimage with one data file having no content"""
6051 with self.assertRaises(ValueError) as exc:
6052 self._DoReadFile('253_mkimage_mult_no_content.dts')
6053 self.assertIn('Could not complete processing of contents',
6054 str(exc.exception))
6055
Quentin Schulz0d3a9262022-09-02 15:10:49 +02006056 def testMkimageFilename(self):
6057 """Test using mkimage to build a binary with a filename"""
6058 retcode = self._DoTestFile('254_mkimage_filename.dts')
6059 self.assertEqual(0, retcode)
6060 fname = tools.get_output_filename('mkimage-test.bin')
6061 self.assertTrue(os.path.exists(fname))
6062
Simon Glass56d05412022-02-28 07:16:54 -07006063 def testVpl(self):
6064 """Test that an image with VPL and its device tree can be created"""
6065 # ELF file with a '__bss_size' symbol
6066 self._SetupVplElf()
6067 data = self._DoReadFile('255_u_boot_vpl.dts')
6068 self.assertEqual(U_BOOT_VPL_DATA + U_BOOT_VPL_DTB_DATA, data)
6069
6070 def testVplNoDtb(self):
6071 """Test that an image with vpl/u-boot-vpl-nodtb.bin can be created"""
6072 self._SetupVplElf()
6073 data = self._DoReadFile('256_u_boot_vpl_nodtb.dts')
6074 self.assertEqual(U_BOOT_VPL_NODTB_DATA,
6075 data[:len(U_BOOT_VPL_NODTB_DATA)])
6076
6077 def testExpandedVpl(self):
6078 """Test that an expanded entry type is selected for TPL when needed"""
6079 self._SetupVplElf()
6080
6081 entry_args = {
6082 'vpl-bss-pad': 'y',
6083 'vpl-dtb': 'y',
6084 }
6085 self._DoReadFileDtb('257_fdt_incl_vpl.dts', use_expanded=True,
6086 entry_args=entry_args)
6087 image = control.images['image']
6088 entries = image.GetEntries()
6089 self.assertEqual(1, len(entries))
6090
6091 # We only have u-boot-vpl, which be expanded
6092 self.assertIn('u-boot-vpl', entries)
6093 entry = entries['u-boot-vpl']
6094 self.assertEqual('u-boot-vpl-expanded', entry.etype)
6095 subent = entry.GetEntries()
6096 self.assertEqual(3, len(subent))
6097 self.assertIn('u-boot-vpl-nodtb', subent)
6098 self.assertIn('u-boot-vpl-bss-pad', subent)
6099 self.assertIn('u-boot-vpl-dtb', subent)
6100
6101 def testVplBssPadMissing(self):
6102 """Test that a missing symbol is detected"""
6103 self._SetupVplElf('u_boot_ucode_ptr')
6104 with self.assertRaises(ValueError) as e:
6105 self._DoReadFile('258_vpl_bss_pad.dts')
6106 self.assertIn('Expected __bss_size symbol in vpl/u-boot-vpl',
6107 str(e.exception))
6108
Neha Malcom Francis3eb4be32022-10-17 16:36:25 +05306109 def testSymlink(self):
6110 """Test that image files can be named"""
6111 retcode = self._DoTestFile('259_symlink.dts', debug=True, map=True)
6112 self.assertEqual(0, retcode)
6113 image = control.images['test_image']
6114 fname = tools.get_output_filename('test_image.bin')
6115 sname = tools.get_output_filename('symlink_to_test.bin')
6116 self.assertTrue(os.path.islink(sname))
6117 self.assertEqual(os.readlink(sname), fname)
Alper Nebi Yasake63ca5a2022-03-27 18:31:45 +03006118
Simon Glass37f85de2022-10-20 18:22:47 -06006119 def testSymbolsElf(self):
6120 """Test binman can assign symbols embedded in an ELF file"""
6121 if not elf.ELF_TOOLS:
6122 self.skipTest('Python elftools not available')
6123 self._SetupTplElf('u_boot_binman_syms')
6124 self._SetupVplElf('u_boot_binman_syms')
6125 self._SetupSplElf('u_boot_binman_syms')
6126 data = self._DoReadFileDtb('260_symbols_elf.dts')[0]
6127 image_fname = tools.get_output_filename('image.bin')
6128
6129 image = control.images['image']
6130 entries = image.GetEntries()
6131
6132 for entry in entries.values():
6133 # No symbols in u-boot and it has faked contents anyway
6134 if entry.name == 'u-boot':
6135 continue
6136 edata = data[entry.image_pos:entry.image_pos + entry.size]
6137 efname = tools.get_output_filename(f'edata-{entry.name}')
6138 tools.write_file(efname, edata)
6139
6140 syms = elf.GetSymbolFileOffset(efname, ['_binman_u_boot'])
6141 re_name = re.compile('_binman_(u_boot_(.*))_prop_(.*)')
6142 for name, sym in syms.items():
6143 msg = 'test'
6144 val = elf.GetSymbolValue(sym, edata, msg)
6145 entry_m = re_name.match(name)
6146 if entry_m:
6147 ename, prop = entry_m.group(1), entry_m.group(3)
6148 entry, entry_name, prop_name = image.LookupEntry(entries,
6149 name, msg)
6150 if prop_name == 'offset':
6151 expect_val = entry.offset
6152 elif prop_name == 'image_pos':
6153 expect_val = entry.image_pos
6154 elif prop_name == 'size':
6155 expect_val = entry.size
6156 self.assertEqual(expect_val, val)
6157
6158 def testSymbolsElfBad(self):
6159 """Check error when trying to write symbols without the elftools lib"""
6160 if not elf.ELF_TOOLS:
6161 self.skipTest('Python elftools not available')
6162 self._SetupTplElf('u_boot_binman_syms')
6163 self._SetupVplElf('u_boot_binman_syms')
6164 self._SetupSplElf('u_boot_binman_syms')
6165 try:
6166 elf.ELF_TOOLS = False
6167 with self.assertRaises(ValueError) as exc:
6168 self._DoReadFileDtb('260_symbols_elf.dts')
6169 finally:
6170 elf.ELF_TOOLS = True
6171 self.assertIn(
6172 "Section '/binman': entry '/binman/u-boot-spl-elf': "
6173 'Cannot write symbols to an ELF file without Python elftools',
6174 str(exc.exception))
6175
Simon Glassde244162023-01-07 14:07:08 -07006176 def testSectionFilename(self):
6177 """Check writing of section contents to a file"""
6178 data = self._DoReadFile('261_section_fname.dts')
6179 expected = (b'&&' + U_BOOT_DATA + b'&&&' +
6180 tools.get_bytes(ord('!'), 7) +
6181 U_BOOT_DATA + tools.get_bytes(ord('&'), 12))
6182 self.assertEqual(expected, data)
6183
6184 sect_fname = tools.get_output_filename('outfile.bin')
6185 self.assertTrue(os.path.exists(sect_fname))
6186 sect_data = tools.read_file(sect_fname)
6187 self.assertEqual(U_BOOT_DATA, sect_data)
6188
Simon Glass1e9e61c2023-01-07 14:07:12 -07006189 def testAbsent(self):
6190 """Check handling of absent entries"""
6191 data = self._DoReadFile('262_absent.dts')
6192 self.assertEqual(U_BOOT_DATA + U_BOOT_IMG_DATA, data)
6193
Simon Glassad5cfe12023-01-07 14:07:14 -07006194 def testPackTeeOsOptional(self):
6195 """Test that an image with an optional TEE binary can be created"""
6196 entry_args = {
6197 'tee-os-path': 'tee.elf',
6198 }
6199 data = self._DoReadFileDtb('263_tee_os_opt.dts',
6200 entry_args=entry_args)[0]
6201 self.assertEqual(U_BOOT_DATA + U_BOOT_IMG_DATA, data)
6202
6203 def checkFitTee(self, dts, tee_fname):
6204 """Check that a tee-os entry works and returns data
6205
6206 Args:
6207 dts (str): Device tree filename to use
6208 tee_fname (str): filename containing tee-os
6209
6210 Returns:
6211 bytes: Image contents
6212 """
6213 if not elf.ELF_TOOLS:
6214 self.skipTest('Python elftools not available')
6215 entry_args = {
6216 'of-list': 'test-fdt1 test-fdt2',
6217 'default-dt': 'test-fdt2',
6218 'tee-os-path': tee_fname,
6219 }
6220 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
6221 data = self._DoReadFileDtb(dts, entry_args=entry_args,
6222 extra_indirs=[test_subdir])[0]
6223 return data
6224
6225 def testFitTeeOsOptionalFit(self):
6226 """Test an image with a FIT with an optional OP-TEE binary"""
6227 data = self.checkFitTee('264_tee_os_opt_fit.dts', 'tee.bin')
6228
6229 # There should be only one node, holding the data set up in SetUpClass()
6230 # for tee.bin
6231 dtb = fdt.Fdt.FromData(data)
6232 dtb.Scan()
6233 node = dtb.GetNode('/images/tee-1')
6234 self.assertEqual(TEE_ADDR,
6235 fdt_util.fdt32_to_cpu(node.props['load'].value))
6236 self.assertEqual(TEE_ADDR,
6237 fdt_util.fdt32_to_cpu(node.props['entry'].value))
6238 self.assertEqual(U_BOOT_DATA, node.props['data'].bytes)
6239
6240 def testFitTeeOsOptionalFitBad(self):
6241 """Test an image with a FIT with an optional OP-TEE binary"""
6242 with self.assertRaises(ValueError) as exc:
6243 self.checkFitTee('265_tee_os_opt_fit_bad.dts', 'tee.bin')
6244 self.assertIn(
6245 "Node '/binman/fit': subnode 'images/@tee-SEQ': Failed to read ELF file: Magic number does not match",
6246 str(exc.exception))
6247
6248 def testFitTeeOsBad(self):
6249 """Test an OP-TEE binary with wrong formats"""
6250 self.make_tee_bin('tee.bad1', 123)
6251 with self.assertRaises(ValueError) as exc:
6252 self.checkFitTee('264_tee_os_opt_fit.dts', 'tee.bad1')
6253 self.assertIn(
6254 "Node '/binman/fit/images/@tee-SEQ/tee-os': OP-TEE paged mode not supported",
6255 str(exc.exception))
6256
6257 self.make_tee_bin('tee.bad2', 0, b'extra data')
6258 with self.assertRaises(ValueError) as exc:
6259 self.checkFitTee('264_tee_os_opt_fit.dts', 'tee.bad2')
6260 self.assertIn(
6261 "Node '/binman/fit/images/@tee-SEQ/tee-os': Invalid OP-TEE file: size mismatch (expected 0x4, have 0xe)",
6262 str(exc.exception))
6263
Simon Glass63328f12023-01-07 14:07:15 -07006264 def testExtblobOptional(self):
6265 """Test an image with an external blob that is optional"""
6266 with test_util.capture_sys_output() as (stdout, stderr):
6267 data = self._DoReadFile('266_blob_ext_opt.dts')
6268 self.assertEqual(REFCODE_DATA, data)
6269 err = stderr.getvalue()
6270 self.assertRegex(
6271 err,
6272 "Image '.*' is missing external blobs but is still functional: missing")
6273
Simon Glass7447a9d2023-01-11 16:10:12 -07006274 def testSectionInner(self):
6275 """Test an inner section with a size"""
6276 data = self._DoReadFile('267_section_inner.dts')
6277 expected = U_BOOT_DATA + tools.get_bytes(0, 12)
6278 self.assertEqual(expected, data)
6279
Simon Glassa4948b22023-01-11 16:10:14 -07006280 def testNull(self):
6281 """Test an image with a null entry"""
6282 data = self._DoReadFile('268_null.dts')
6283 self.assertEqual(U_BOOT_DATA + b'\xff\xff\xff\xff' + U_BOOT_IMG_DATA, data)
6284
Simon Glassf1ee03b2023-01-11 16:10:16 -07006285 def testOverlap(self):
6286 """Test an image with a overlapping entry"""
6287 data = self._DoReadFile('269_overlap.dts')
6288 self.assertEqual(U_BOOT_DATA[:1] + b'aa' + U_BOOT_DATA[3:], data)
6289
6290 image = control.images['image']
6291 entries = image.GetEntries()
6292
6293 self.assertIn('inset', entries)
6294 inset = entries['inset']
6295 self.assertEqual(1, inset.offset);
6296 self.assertEqual(1, inset.image_pos);
6297 self.assertEqual(2, inset.size);
6298
6299 def testOverlapNull(self):
6300 """Test an image with a null overlap"""
6301 data = self._DoReadFile('270_overlap_null.dts')
6302 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
6303
6304 # Check the FMAP
6305 fhdr, fentries = fmap_util.DecodeFmap(data[len(U_BOOT_DATA):])
6306 self.assertEqual(4, fhdr.nareas)
6307 fiter = iter(fentries)
6308
6309 fentry = next(fiter)
6310 self.assertEqual(b'SECTION', fentry.name)
6311 self.assertEqual(0, fentry.offset)
6312 self.assertEqual(len(U_BOOT_DATA), fentry.size)
6313 self.assertEqual(0, fentry.flags)
6314
6315 fentry = next(fiter)
6316 self.assertEqual(b'U_BOOT', fentry.name)
6317 self.assertEqual(0, fentry.offset)
6318 self.assertEqual(len(U_BOOT_DATA), fentry.size)
6319 self.assertEqual(0, fentry.flags)
6320
6321 # Make sure that the NULL entry appears in the FMAP
6322 fentry = next(fiter)
6323 self.assertEqual(b'NULL', fentry.name)
6324 self.assertEqual(1, fentry.offset)
6325 self.assertEqual(2, fentry.size)
6326 self.assertEqual(0, fentry.flags)
6327
6328 fentry = next(fiter)
6329 self.assertEqual(b'FMAP', fentry.name)
6330 self.assertEqual(len(U_BOOT_DATA), fentry.offset)
6331
6332 def testOverlapBad(self):
6333 """Test an image with a bad overlapping entry"""
6334 with self.assertRaises(ValueError) as exc:
6335 self._DoReadFile('271_overlap_bad.dts')
6336 self.assertIn(
6337 "Node '/binman/inset': Offset 0x10 (16) ending at 0x12 (18) must overlap with existing entries",
6338 str(exc.exception))
6339
6340 def testOverlapNoOffset(self):
6341 """Test an image with a bad overlapping entry"""
6342 with self.assertRaises(ValueError) as exc:
6343 self._DoReadFile('272_overlap_no_size.dts')
6344 self.assertIn(
6345 "Node '/binman/inset': 'fill' entry is missing properties: size",
6346 str(exc.exception))
6347
Simon Glasse0035c92023-01-11 16:10:17 -07006348 def testBlobSymbol(self):
6349 """Test a blob with symbols read from an ELF file"""
6350 elf_fname = self.ElfTestFile('blob_syms')
6351 TestFunctional._MakeInputFile('blob_syms', tools.read_file(elf_fname))
6352 TestFunctional._MakeInputFile('blob_syms.bin',
6353 tools.read_file(self.ElfTestFile('blob_syms.bin')))
6354
6355 data = self._DoReadFile('273_blob_symbol.dts')
6356
6357 syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
6358 addr = elf.GetSymbolAddress(elf_fname, '__my_start_sym')
6359 self.assertEqual(syms['_binman_sym_magic'].address, addr)
6360 self.assertEqual(syms['_binman_inset_prop_offset'].address, addr + 4)
6361 self.assertEqual(syms['_binman_inset_prop_size'].address, addr + 8)
6362
6363 sym_values = struct.pack('<LLL', elf.BINMAN_SYM_MAGIC_VALUE, 4, 8)
6364 expected = sym_values
6365 self.assertEqual(expected, data[:len(expected)])
6366
Simon Glass49e9c002023-01-11 16:10:19 -07006367 def testOffsetFromElf(self):
6368 """Test a blob with symbols read from an ELF file"""
6369 elf_fname = self.ElfTestFile('blob_syms')
6370 TestFunctional._MakeInputFile('blob_syms', tools.read_file(elf_fname))
6371 TestFunctional._MakeInputFile('blob_syms.bin',
6372 tools.read_file(self.ElfTestFile('blob_syms.bin')))
6373
6374 data = self._DoReadFile('274_offset_from_elf.dts')
6375
6376 syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
6377 base = elf.GetSymbolAddress(elf_fname, '__my_start_sym')
6378
6379 image = control.images['image']
6380 entries = image.GetEntries()
6381
6382 self.assertIn('inset', entries)
6383 inset = entries['inset']
6384
6385 self.assertEqual(base + 4, inset.offset);
6386 self.assertEqual(base + 4, inset.image_pos);
6387 self.assertEqual(4, inset.size);
6388
6389 self.assertIn('inset2', entries)
6390 inset = entries['inset2']
6391 self.assertEqual(base + 8, inset.offset);
6392 self.assertEqual(base + 8, inset.image_pos);
6393 self.assertEqual(4, inset.size);
6394
Jonas Karlmanc59ea892023-01-21 19:01:39 +00006395 def testFitAlign(self):
6396 """Test an image with an FIT with aligned external data"""
6397 data = self._DoReadFile('275_fit_align.dts')
6398 self.assertEqual(4096, len(data))
6399
6400 dtb = fdt.Fdt.FromData(data)
6401 dtb.Scan()
6402
6403 props = self._GetPropTree(dtb, ['data-position'])
6404 expected = {
6405 'u-boot:data-position': 1024,
6406 'fdt-1:data-position': 2048,
6407 'fdt-2:data-position': 3072,
6408 }
6409 self.assertEqual(expected, props)
6410
Jonas Karlman490f73c2023-01-21 19:02:12 +00006411 def testFitFirmwareLoadables(self):
6412 """Test an image with an FIT that use fit,firmware"""
6413 if not elf.ELF_TOOLS:
6414 self.skipTest('Python elftools not available')
6415 entry_args = {
6416 'of-list': 'test-fdt1',
6417 'default-dt': 'test-fdt1',
6418 'atf-bl31-path': 'bl31.elf',
6419 'tee-os-path': 'missing.bin',
6420 }
6421 test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
Simon Glass62f85902023-02-23 18:18:01 -07006422 with test_util.capture_sys_output() as (stdout, stderr):
6423 data = self._DoReadFileDtb(
6424 '276_fit_firmware_loadables.dts',
6425 entry_args=entry_args,
6426 extra_indirs=[test_subdir])[0]
Jonas Karlman490f73c2023-01-21 19:02:12 +00006427
6428 dtb = fdt.Fdt.FromData(data)
6429 dtb.Scan()
6430
6431 node = dtb.GetNode('/configurations/conf-uboot-1')
6432 self.assertEqual('u-boot', node.props['firmware'].value)
6433 self.assertEqual(['atf-1', 'atf-2'],
6434 fdt_util.GetStringList(node, 'loadables'))
6435
6436 node = dtb.GetNode('/configurations/conf-atf-1')
6437 self.assertEqual('atf-1', node.props['firmware'].value)
6438 self.assertEqual(['u-boot', 'atf-2'],
6439 fdt_util.GetStringList(node, 'loadables'))
6440
6441 node = dtb.GetNode('/configurations/conf-missing-uboot-1')
6442 self.assertEqual('u-boot', node.props['firmware'].value)
6443 self.assertEqual(['atf-1', 'atf-2'],
6444 fdt_util.GetStringList(node, 'loadables'))
6445
6446 node = dtb.GetNode('/configurations/conf-missing-atf-1')
6447 self.assertEqual('atf-1', node.props['firmware'].value)
6448 self.assertEqual(['u-boot', 'atf-2'],
6449 fdt_util.GetStringList(node, 'loadables'))
6450
6451 node = dtb.GetNode('/configurations/conf-missing-tee-1')
6452 self.assertEqual('atf-1', node.props['firmware'].value)
6453 self.assertEqual(['u-boot', 'atf-2'],
6454 fdt_util.GetStringList(node, 'loadables'))
6455
Simon Glass9a1c7262023-02-22 12:14:49 -07006456 def testTooldir(self):
6457 """Test that we can specify the tooldir"""
6458 with test_util.capture_sys_output() as (stdout, stderr):
6459 self.assertEqual(0, self._DoBinman('--tooldir', 'fred',
6460 'tool', '-l'))
6461 self.assertEqual('fred', bintool.Bintool.tooldir)
6462
6463 # Check that the toolpath is updated correctly
6464 self.assertEqual(['fred'], tools.tool_search_paths)
6465
6466 # Try with a few toolpaths; the tooldir should be at the end
6467 with test_util.capture_sys_output() as (stdout, stderr):
6468 self.assertEqual(0, self._DoBinman(
6469 '--toolpath', 'mary', '--toolpath', 'anna', '--tooldir', 'fred',
6470 'tool', '-l'))
6471 self.assertEqual(['mary', 'anna', 'fred'], tools.tool_search_paths)
6472
Simon Glass49b77e82023-03-02 17:02:44 -07006473 def testReplaceSectionEntry(self):
6474 """Test replacing an entry in a section"""
6475 expect_data = b'w' * len(U_BOOT_DATA + COMPRESS_DATA)
6476 entry_data, expected_fdtmap, image = self._RunReplaceCmd('section/blob',
6477 expect_data, dts='241_replace_section_simple.dts')
6478 self.assertEqual(expect_data, entry_data)
6479
6480 entries = image.GetEntries()
6481 self.assertIn('section', entries)
6482 section = entries['section']
6483
6484 sect_entries = section.GetEntries()
6485 self.assertIn('blob', sect_entries)
6486 entry = sect_entries['blob']
6487 self.assertEqual(len(expect_data), entry.size)
6488
6489 fname = tools.get_output_filename('image-updated.bin')
6490 data = tools.read_file(fname)
6491
6492 new_blob_data = data[entry.image_pos:entry.image_pos + len(expect_data)]
6493 self.assertEqual(expect_data, new_blob_data)
6494
6495 self.assertEqual(U_BOOT_DATA,
6496 data[entry.image_pos + len(expect_data):]
6497 [:len(U_BOOT_DATA)])
6498
6499 def testReplaceSectionDeep(self):
6500 """Test replacing an entry in two levels of sections"""
6501 expect_data = b'w' * len(U_BOOT_DATA + COMPRESS_DATA)
6502 entry_data, expected_fdtmap, image = self._RunReplaceCmd(
6503 'section/section/blob', expect_data,
6504 dts='278_replace_section_deep.dts')
6505 self.assertEqual(expect_data, entry_data)
6506
6507 entries = image.GetEntries()
6508 self.assertIn('section', entries)
6509 section = entries['section']
6510
6511 subentries = section.GetEntries()
6512 self.assertIn('section', subentries)
6513 section = subentries['section']
6514
6515 sect_entries = section.GetEntries()
6516 self.assertIn('blob', sect_entries)
6517 entry = sect_entries['blob']
6518 self.assertEqual(len(expect_data), entry.size)
6519
6520 fname = tools.get_output_filename('image-updated.bin')
6521 data = tools.read_file(fname)
6522
6523 new_blob_data = data[entry.image_pos:entry.image_pos + len(expect_data)]
6524 self.assertEqual(expect_data, new_blob_data)
6525
6526 self.assertEqual(U_BOOT_DATA,
6527 data[entry.image_pos + len(expect_data):]
6528 [:len(U_BOOT_DATA)])
6529
6530 def testReplaceFitSibling(self):
6531 """Test an image with a FIT inside where we replace its sibling"""
6532 fname = TestFunctional._MakeInputFile('once', b'available once')
6533 self._DoReadFileRealDtb('277_replace_fit_sibling.dts')
6534 os.remove(fname)
6535
6536 try:
6537 tmpdir, updated_fname = self._SetupImageInTmpdir()
6538
6539 fname = os.path.join(tmpdir, 'update-blob')
6540 expected = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) + 1)
6541 tools.write_file(fname, expected)
6542
6543 self._DoBinman('replace', '-i', updated_fname, 'blob', '-f', fname)
6544 data = tools.read_file(updated_fname)
6545 start = len(U_BOOT_DTB_DATA)
6546 self.assertEqual(expected, data[start:start + len(expected)])
6547 map_fname = os.path.join(tmpdir, 'image-updated.map')
6548 self.assertFalse(os.path.exists(map_fname))
6549 finally:
6550 shutil.rmtree(tmpdir)
6551
Simon Glassc3fe97f2023-03-02 17:02:45 -07006552 def testX509Cert(self):
6553 """Test creating an X509 certificate"""
6554 keyfile = self.TestFile('key.key')
6555 entry_args = {
6556 'keyfile': keyfile,
6557 }
6558 data = self._DoReadFileDtb('279_x509_cert.dts',
6559 entry_args=entry_args)[0]
6560 cert = data[:-4]
6561 self.assertEqual(U_BOOT_DATA, data[-4:])
6562
6563 # TODO: verify the signature
6564
6565 def testX509CertMissing(self):
6566 """Test that binman still produces an image if openssl is missing"""
6567 keyfile = self.TestFile('key.key')
6568 entry_args = {
6569 'keyfile': 'keyfile',
6570 }
6571 with test_util.capture_sys_output() as (_, stderr):
6572 self._DoTestFile('279_x509_cert.dts',
6573 force_missing_bintools='openssl',
6574 entry_args=entry_args)
6575 err = stderr.getvalue()
6576 self.assertRegex(err, "Image 'image'.*missing bintools.*: openssl")
6577
Jonas Karlman35305492023-02-25 19:01:33 +00006578 def testPackRockchipTpl(self):
6579 """Test that an image with a Rockchip TPL binary can be created"""
6580 data = self._DoReadFile('277_rockchip_tpl.dts')
6581 self.assertEqual(ROCKCHIP_TPL_DATA, data[:len(ROCKCHIP_TPL_DATA)])
6582
Jonas Karlman1016ec72023-02-25 19:01:35 +00006583 def testMkimageMissingBlobMultiple(self):
6584 """Test missing blob with mkimage entry and multiple-data-files"""
6585 with test_util.capture_sys_output() as (stdout, stderr):
6586 self._DoTestFile('278_mkimage_missing_multiple.dts', allow_missing=True)
6587 err = stderr.getvalue()
6588 self.assertIn("is missing external blobs and is non-functional", err)
6589
6590 with self.assertRaises(ValueError) as e:
6591 self._DoTestFile('278_mkimage_missing_multiple.dts', allow_missing=False)
6592 self.assertIn("not found in input path", str(e.exception))
6593
Ivan Mikhaylov3cfcaa4d2023-03-08 01:13:40 +00006594 def _PrepareSignEnv(self, dts='280_fit_sign.dts'):
6595 """Prepare sign environment
6596
6597 Create private and public keys, add pubkey into dtb.
6598
6599 Returns:
6600 Tuple:
6601 FIT container
6602 Image name
6603 Private key
6604 DTB
6605 """
6606
6607 data = self._DoReadFileRealDtb(dts)
6608 updated_fname = tools.get_output_filename('image-updated.bin')
6609 tools.write_file(updated_fname, data)
6610 dtb = tools.get_output_filename('source.dtb')
6611 private_key = tools.get_output_filename('test_key.key')
6612 public_key = tools.get_output_filename('test_key.crt')
6613 fit = tools.get_output_filename('fit.fit')
6614 key_dir = tools.get_output_dir()
6615
6616 tools.run('openssl', 'req', '-batch' , '-newkey', 'rsa:4096',
6617 '-sha256', '-new', '-nodes', '-x509', '-keyout',
6618 private_key, '-out', public_key)
6619 tools.run('fdt_add_pubkey', '-a', 'sha256,rsa4096', '-k', key_dir,
6620 '-n', 'test_key', '-r', 'conf', dtb)
6621
6622 return fit, updated_fname, private_key, dtb
6623
6624 def testSignSimple(self):
6625 """Test that a FIT container can be signed in image"""
6626 is_signed = False
6627 fit, fname, private_key, dtb = self._PrepareSignEnv()
6628
6629 # do sign with private key
6630 control.SignEntries(fname, None, private_key, 'sha256,rsa4096',
6631 ['fit'])
6632 is_signed = self._CheckSign(fit, dtb)
6633
6634 self.assertEqual(is_signed, True)
6635
6636 def testSignExactFIT(self):
6637 """Test that a FIT container can be signed and replaced in image"""
6638 is_signed = False
6639 fit, fname, private_key, dtb = self._PrepareSignEnv()
6640
6641 # Make sure we propagate the toolpath, since mkimage may not be on PATH
6642 args = []
6643 if self.toolpath:
6644 for path in self.toolpath:
6645 args += ['--toolpath', path]
6646
6647 # do sign with private key
6648 self._DoBinman(*args, 'sign', '-i', fname, '-k', private_key, '-a',
6649 'sha256,rsa4096', '-f', fit, 'fit')
6650 is_signed = self._CheckSign(fit, dtb)
6651
6652 self.assertEqual(is_signed, True)
6653
6654 def testSignNonFit(self):
6655 """Test a non-FIT entry cannot be signed"""
6656 is_signed = False
6657 fit, fname, private_key, _ = self._PrepareSignEnv(
6658 '281_sign_non_fit.dts')
6659
6660 # do sign with private key
6661 with self.assertRaises(ValueError) as e:
6662 self._DoBinman('sign', '-i', fname, '-k', private_key, '-a',
6663 'sha256,rsa4096', '-f', fit, 'u-boot')
6664 self.assertIn(
6665 "Node '/u-boot': Updating signatures is not supported with this entry type",
6666 str(e.exception))
6667
6668 def testSignMissingMkimage(self):
6669 """Test that FIT signing handles a missing mkimage tool"""
6670 fit, fname, private_key, _ = self._PrepareSignEnv()
6671
6672 # try to sign with a missing mkimage tool
6673 bintool.Bintool.set_missing_list(['mkimage'])
6674 with self.assertRaises(ValueError) as e:
6675 control.SignEntries(fname, None, private_key, 'sha256,rsa4096',
6676 ['fit'])
6677 self.assertIn("Node '/fit': Missing tool: 'mkimage'", str(e.exception))
6678
Simon Glassde244162023-01-07 14:07:08 -07006679
Simon Glassac599912017-11-12 21:52:22 -07006680if __name__ == "__main__":
6681 unittest.main()