blob: 4616a29deb6896e9ad63054ad0d0f375773a39e0 [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001# SPDX-License-Identifier: GPL-2.0+
Simon Glass4f443042016-11-25 20:15:52 -07002# Copyright (c) 2016 Google, Inc
3# Written by Simon Glass <sjg@chromium.org>
4#
Simon Glass4f443042016-11-25 20:15:52 -07005# To run a single test, change to this directory, and:
6#
7# python -m unittest func_test.TestFunctional.testHelp
8
Simon Glassfdc34362020-07-09 18:39:45 -06009import collections
Simon Glass16287932020-04-17 18:09:03 -060010import gzip
Simon Glasse0e5df92018-09-14 04:57:31 -060011import hashlib
Simon Glass4f443042016-11-25 20:15:52 -070012from optparse import OptionParser
13import os
Simon Glassfdc34362020-07-09 18:39:45 -060014import re
Simon Glass4f443042016-11-25 20:15:52 -070015import shutil
16import struct
17import sys
18import tempfile
19import unittest
Simon Glass56ee85e2022-01-09 20:13:57 -070020import unittest.mock
21import urllib.error
Simon Glass4f443042016-11-25 20:15:52 -070022
Simon Glass386c63c2022-01-09 20:13:50 -070023from binman import bintool
Simon Glass16287932020-04-17 18:09:03 -060024from binman import cbfs_util
25from binman import cmdline
Simon Glassad35ce52022-01-09 20:14:03 -070026from binman import comp_util
Simon Glass16287932020-04-17 18:09:03 -060027from binman import control
28from binman import elf
29from binman import elf_test
Simon Glass75989722021-11-23 21:08:59 -070030from binman import fip_util
Simon Glass16287932020-04-17 18:09:03 -060031from binman import fmap_util
Simon Glass16287932020-04-17 18:09:03 -060032from binman import state
33from dtoc import fdt
34from dtoc import fdt_util
35from binman.etype import fdtmap
36from binman.etype import image_header
Simon Glass07237982020-08-05 13:27:47 -060037from binman.image import Image
Simon Glassbf776672020-04-17 18:09:04 -060038from patman import command
39from patman import test_util
40from patman import tools
41from patman import tout
Simon Glass4f443042016-11-25 20:15:52 -070042
43# Contents of test files, corresponding to different entry types
Simon Glassc6c10e72019-05-17 22:00:46 -060044U_BOOT_DATA = b'1234'
45U_BOOT_IMG_DATA = b'img'
Simon Glasseb0086f2019-08-24 07:23:04 -060046U_BOOT_SPL_DATA = b'56780123456789abcdefghi'
47U_BOOT_TPL_DATA = b'tpl9876543210fedcbazyw'
Simon Glassc6c10e72019-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'
54X86_START16_DATA = b'start16'
55X86_START16_SPL_DATA = b'start16spl'
56X86_START16_TPL_DATA = b'start16tpl'
Simon Glass2250ee62019-08-24 07:22:48 -060057X86_RESET16_DATA = b'reset16'
58X86_RESET16_SPL_DATA = b'reset16spl'
59X86_RESET16_TPL_DATA = b'reset16tpl'
Simon Glassc6c10e72019-05-17 22:00:46 -060060PPC_MPC85XX_BR_DATA = b'ppcmpc85xxbr'
61U_BOOT_NODTB_DATA = b'nodtb with microcode pointer somewhere in here'
62U_BOOT_SPL_NODTB_DATA = b'splnodtb with microcode pointer somewhere in here'
63U_BOOT_TPL_NODTB_DATA = b'tplnodtb with microcode pointer somewhere in here'
Alper Nebi Yasak21353312022-02-08 01:08:04 +030064U_BOOT_EXP_DATA = U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA
65U_BOOT_SPL_EXP_DATA = U_BOOT_SPL_NODTB_DATA + U_BOOT_SPL_DTB_DATA
66U_BOOT_TPL_EXP_DATA = U_BOOT_TPL_NODTB_DATA + U_BOOT_TPL_DTB_DATA
Simon Glassc6c10e72019-05-17 22:00:46 -060067FSP_DATA = b'fsp'
68CMC_DATA = b'cmc'
69VBT_DATA = b'vbt'
70MRC_DATA = b'mrc'
Simon Glassbb748372018-07-17 13:25:33 -060071TEXT_DATA = 'text'
72TEXT_DATA2 = 'text2'
73TEXT_DATA3 = 'text3'
Simon Glassc6c10e72019-05-17 22:00:46 -060074CROS_EC_RW_DATA = b'ecrw'
75GBB_DATA = b'gbbd'
76BMPBLK_DATA = b'bmp'
77VBLOCK_DATA = b'vblk'
78FILES_DATA = (b"sorry I'm late\nOh, don't bother apologising, I'm " +
79 b"sorry you're alive\n")
Simon Glassff5c7e32019-07-08 13:18:42 -060080COMPRESS_DATA = b'compress xxxxxxxxxxxxxxxxxxxxxx data'
Simon Glass8f5ef892020-10-26 17:40:25 -060081COMPRESS_DATA_BIG = COMPRESS_DATA * 2
Simon Glassc6c10e72019-05-17 22:00:46 -060082REFCODE_DATA = b'refcode'
Simon Glassea0fff92019-08-24 07:23:07 -060083FSP_M_DATA = b'fsp_m'
Simon Glassbc6a88f2019-10-20 21:31:35 -060084FSP_S_DATA = b'fsp_s'
Simon Glass998d1482019-10-20 21:31:36 -060085FSP_T_DATA = b'fsp_t'
Simon Glassdc2f81a2020-09-01 05:13:58 -060086ATF_BL31_DATA = b'bl31'
Simon Glass75989722021-11-23 21:08:59 -070087ATF_BL2U_DATA = b'bl2u'
Bin Meng4c4d6072021-05-10 20:23:33 +080088OPENSBI_DATA = b'opensbi'
Samuel Holland18bd4552020-10-21 21:12:15 -050089SCP_DATA = b'scp'
Simon Glass6cf99532020-09-01 05:13:59 -060090TEST_FDT1_DATA = b'fdt1'
91TEST_FDT2_DATA = b'test-fdt2'
Simon Glassfb91d562020-09-06 10:35:33 -060092ENV_DATA = b'var1=1\nvar2="2"'
Simon Glass6cf99532020-09-01 05:13:59 -060093
94# Subdirectory of the input dir to use to put test FDTs
95TEST_FDT_SUBDIR = 'fdts'
Simon Glassec127af2018-07-17 13:25:39 -060096
Simon Glass6ccbfcd2019-07-20 12:23:47 -060097# The expected size for the device tree in some tests
Simon Glassf667e452019-07-08 14:25:50 -060098EXTRACT_DTB_SIZE = 0x3c9
99
Simon Glass6ccbfcd2019-07-20 12:23:47 -0600100# Properties expected to be in the device tree when update_dtb is used
101BASE_DTB_PROPS = ['offset', 'size', 'image-pos']
102
Simon Glass12bb1a92019-07-20 12:23:51 -0600103# Extra properties expected to be in the device tree when allow-repack is used
104REPACK_DTB_PROPS = ['orig-offset', 'orig-size']
105
Simon Glass4f443042016-11-25 20:15:52 -0700106
107class TestFunctional(unittest.TestCase):
108 """Functional tests for binman
109
110 Most of these use a sample .dts file to build an image and then check
111 that it looks correct. The sample files are in the test/ subdirectory
112 and are numbered.
113
114 For each entry type a very small test file is created using fixed
115 string contents. This makes it easy to test that things look right, and
116 debug problems.
117
118 In some cases a 'real' file must be used - these are also supplied in
119 the test/ diurectory.
120 """
121 @classmethod
Simon Glassb986b3b2019-08-24 07:22:43 -0600122 def setUpClass(cls):
Simon Glass4d5994f2017-11-12 21:52:20 -0700123 global entry
Simon Glass16287932020-04-17 18:09:03 -0600124 from binman import entry
Simon Glass4d5994f2017-11-12 21:52:20 -0700125
Simon Glass4f443042016-11-25 20:15:52 -0700126 # Handle the case where argv[0] is 'python'
Simon Glassb986b3b2019-08-24 07:22:43 -0600127 cls._binman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
128 cls._binman_pathname = os.path.join(cls._binman_dir, 'binman')
Simon Glass4f443042016-11-25 20:15:52 -0700129
130 # Create a temporary directory for input files
Simon Glassb986b3b2019-08-24 07:22:43 -0600131 cls._indir = tempfile.mkdtemp(prefix='binmant.')
Simon Glass4f443042016-11-25 20:15:52 -0700132
133 # Create some test files
134 TestFunctional._MakeInputFile('u-boot.bin', U_BOOT_DATA)
135 TestFunctional._MakeInputFile('u-boot.img', U_BOOT_IMG_DATA)
136 TestFunctional._MakeInputFile('spl/u-boot-spl.bin', U_BOOT_SPL_DATA)
Simon Glassb8ef5b62018-07-17 13:25:48 -0600137 TestFunctional._MakeInputFile('tpl/u-boot-tpl.bin', U_BOOT_TPL_DATA)
Simon Glass4f443042016-11-25 20:15:52 -0700138 TestFunctional._MakeInputFile('blobfile', BLOB_DATA)
Simon Glasse0ff8552016-11-25 20:15:53 -0700139 TestFunctional._MakeInputFile('me.bin', ME_DATA)
140 TestFunctional._MakeInputFile('vga.bin', VGA_DATA)
Simon Glassb986b3b2019-08-24 07:22:43 -0600141 cls._ResetDtbs()
Simon Glass2250ee62019-08-24 07:22:48 -0600142
Jagdish Gediya9d368f32018-09-03 21:35:08 +0530143 TestFunctional._MakeInputFile('u-boot-br.bin', PPC_MPC85XX_BR_DATA)
Simon Glass2250ee62019-08-24 07:22:48 -0600144
Simon Glass5e239182019-08-24 07:22:49 -0600145 TestFunctional._MakeInputFile('u-boot-x86-start16.bin', X86_START16_DATA)
146 TestFunctional._MakeInputFile('spl/u-boot-x86-start16-spl.bin',
Simon Glass87722132017-11-12 21:52:26 -0700147 X86_START16_SPL_DATA)
Simon Glass5e239182019-08-24 07:22:49 -0600148 TestFunctional._MakeInputFile('tpl/u-boot-x86-start16-tpl.bin',
Simon Glass35b384c2018-09-14 04:57:10 -0600149 X86_START16_TPL_DATA)
Simon Glass2250ee62019-08-24 07:22:48 -0600150
151 TestFunctional._MakeInputFile('u-boot-x86-reset16.bin',
152 X86_RESET16_DATA)
153 TestFunctional._MakeInputFile('spl/u-boot-x86-reset16-spl.bin',
154 X86_RESET16_SPL_DATA)
155 TestFunctional._MakeInputFile('tpl/u-boot-x86-reset16-tpl.bin',
156 X86_RESET16_TPL_DATA)
157
Simon Glass4f443042016-11-25 20:15:52 -0700158 TestFunctional._MakeInputFile('u-boot-nodtb.bin', U_BOOT_NODTB_DATA)
Simon Glass6b187df2017-11-12 21:52:27 -0700159 TestFunctional._MakeInputFile('spl/u-boot-spl-nodtb.bin',
160 U_BOOT_SPL_NODTB_DATA)
Simon Glassf0253632018-09-14 04:57:32 -0600161 TestFunctional._MakeInputFile('tpl/u-boot-tpl-nodtb.bin',
162 U_BOOT_TPL_NODTB_DATA)
Simon Glassda229092016-11-25 20:15:56 -0700163 TestFunctional._MakeInputFile('fsp.bin', FSP_DATA)
164 TestFunctional._MakeInputFile('cmc.bin', CMC_DATA)
Bin Meng59ea8c22017-08-15 22:41:54 -0700165 TestFunctional._MakeInputFile('vbt.bin', VBT_DATA)
Simon Glassca4f4ff2017-11-12 21:52:28 -0700166 TestFunctional._MakeInputFile('mrc.bin', MRC_DATA)
Simon Glassec127af2018-07-17 13:25:39 -0600167 TestFunctional._MakeInputFile('ecrw.bin', CROS_EC_RW_DATA)
Simon Glass0ef87aa2018-07-17 13:25:44 -0600168 TestFunctional._MakeInputDir('devkeys')
169 TestFunctional._MakeInputFile('bmpblk.bin', BMPBLK_DATA)
Simon Glass3ae192c2018-10-01 12:22:31 -0600170 TestFunctional._MakeInputFile('refcode.bin', REFCODE_DATA)
Simon Glassea0fff92019-08-24 07:23:07 -0600171 TestFunctional._MakeInputFile('fsp_m.bin', FSP_M_DATA)
Simon Glassbc6a88f2019-10-20 21:31:35 -0600172 TestFunctional._MakeInputFile('fsp_s.bin', FSP_S_DATA)
Simon Glass998d1482019-10-20 21:31:36 -0600173 TestFunctional._MakeInputFile('fsp_t.bin', FSP_T_DATA)
Simon Glass4f443042016-11-25 20:15:52 -0700174
Simon Glass53e22bf2019-08-24 07:22:53 -0600175 cls._elf_testdir = os.path.join(cls._indir, 'elftest')
176 elf_test.BuildElfTestFiles(cls._elf_testdir)
177
Simon Glasse0ff8552016-11-25 20:15:53 -0700178 # ELF file with a '_dt_ucode_base_size' symbol
Simon Glassf514d8f2019-08-24 07:22:54 -0600179 TestFunctional._MakeInputFile('u-boot',
Simon Glassc1aa66e2022-01-29 14:14:04 -0700180 tools.read_file(cls.ElfTestFile('u_boot_ucode_ptr')))
Simon Glasse0ff8552016-11-25 20:15:53 -0700181
182 # Intel flash descriptor file
Simon Glass0ba4b3d2020-07-09 18:39:41 -0600183 cls._SetupDescriptor()
Simon Glasse0ff8552016-11-25 20:15:53 -0700184
Simon Glassb986b3b2019-08-24 07:22:43 -0600185 shutil.copytree(cls.TestFile('files'),
186 os.path.join(cls._indir, 'files'))
Simon Glass0a98b282018-09-14 04:57:28 -0600187
Simon Glass83d73c22018-09-14 04:57:26 -0600188 TestFunctional._MakeInputFile('compress', COMPRESS_DATA)
Simon Glass8f5ef892020-10-26 17:40:25 -0600189 TestFunctional._MakeInputFile('compress_big', COMPRESS_DATA_BIG)
Simon Glassdc2f81a2020-09-01 05:13:58 -0600190 TestFunctional._MakeInputFile('bl31.bin', ATF_BL31_DATA)
Simon Glass75989722021-11-23 21:08:59 -0700191 TestFunctional._MakeInputFile('bl2u.bin', ATF_BL2U_DATA)
Bin Meng4c4d6072021-05-10 20:23:33 +0800192 TestFunctional._MakeInputFile('fw_dynamic.bin', OPENSBI_DATA)
Samuel Holland18bd4552020-10-21 21:12:15 -0500193 TestFunctional._MakeInputFile('scp.bin', SCP_DATA)
Simon Glass83d73c22018-09-14 04:57:26 -0600194
Simon Glass6cf99532020-09-01 05:13:59 -0600195 # Add a few .dtb files for testing
196 TestFunctional._MakeInputFile('%s/test-fdt1.dtb' % TEST_FDT_SUBDIR,
197 TEST_FDT1_DATA)
198 TestFunctional._MakeInputFile('%s/test-fdt2.dtb' % TEST_FDT_SUBDIR,
199 TEST_FDT2_DATA)
200
Simon Glassfb91d562020-09-06 10:35:33 -0600201 TestFunctional._MakeInputFile('env.txt', ENV_DATA)
202
Simon Glass33ce3512022-01-09 20:14:06 -0700203 cls.have_lz4 = comp_util.HAVE_LZ4
Simon Glassac62fba2019-07-08 13:18:53 -0600204
Simon Glass4f443042016-11-25 20:15:52 -0700205 @classmethod
Simon Glassb986b3b2019-08-24 07:22:43 -0600206 def tearDownClass(cls):
Simon Glass4f443042016-11-25 20:15:52 -0700207 """Remove the temporary input directory and its contents"""
Simon Glassb986b3b2019-08-24 07:22:43 -0600208 if cls.preserve_indir:
209 print('Preserving input dir: %s' % cls._indir)
Simon Glassd5164a72019-07-08 13:18:49 -0600210 else:
Simon Glassb986b3b2019-08-24 07:22:43 -0600211 if cls._indir:
212 shutil.rmtree(cls._indir)
213 cls._indir = None
Simon Glass4f443042016-11-25 20:15:52 -0700214
Simon Glassd5164a72019-07-08 13:18:49 -0600215 @classmethod
Simon Glass8acce602019-07-08 13:18:50 -0600216 def setup_test_args(cls, preserve_indir=False, preserve_outdirs=False,
Simon Glass53cd5d92019-07-08 14:25:29 -0600217 toolpath=None, verbosity=None):
Simon Glassd5164a72019-07-08 13:18:49 -0600218 """Accept arguments controlling test execution
219
220 Args:
221 preserve_indir: Preserve the shared input directory used by all
222 tests in this class.
223 preserve_outdir: Preserve the output directories used by tests. Each
224 test has its own, so this is normally only useful when running a
225 single test.
Simon Glass8acce602019-07-08 13:18:50 -0600226 toolpath: ist of paths to use for tools
Simon Glassd5164a72019-07-08 13:18:49 -0600227 """
228 cls.preserve_indir = preserve_indir
229 cls.preserve_outdirs = preserve_outdirs
Simon Glass8acce602019-07-08 13:18:50 -0600230 cls.toolpath = toolpath
Simon Glass53cd5d92019-07-08 14:25:29 -0600231 cls.verbosity = verbosity
Simon Glassd5164a72019-07-08 13:18:49 -0600232
Simon Glassac62fba2019-07-08 13:18:53 -0600233 def _CheckLz4(self):
234 if not self.have_lz4:
235 self.skipTest('lz4 --no-frame-crc not available')
236
Simon Glassbf574f12019-07-20 12:24:09 -0600237 def _CleanupOutputDir(self):
238 """Remove the temporary output directory"""
239 if self.preserve_outdirs:
240 print('Preserving output dir: %s' % tools.outdir)
241 else:
Simon Glassc1aa66e2022-01-29 14:14:04 -0700242 tools._finalise_for_test()
Simon Glassbf574f12019-07-20 12:24:09 -0600243
Simon Glass4f443042016-11-25 20:15:52 -0700244 def setUp(self):
245 # Enable this to turn on debugging output
Simon Glassf3385a52022-01-29 14:14:15 -0700246 # tout.init(tout.DEBUG)
Simon Glass4f443042016-11-25 20:15:52 -0700247 command.test_result = None
248
249 def tearDown(self):
250 """Remove the temporary output directory"""
Simon Glassbf574f12019-07-20 12:24:09 -0600251 self._CleanupOutputDir()
Simon Glass4f443042016-11-25 20:15:52 -0700252
Simon Glassf86a7362019-07-20 12:24:10 -0600253 def _SetupImageInTmpdir(self):
254 """Set up the output image in a new temporary directory
255
256 This is used when an image has been generated in the output directory,
257 but we want to run binman again. This will create a new output
258 directory and fail to delete the original one.
259
260 This creates a new temporary directory, copies the image to it (with a
261 new name) and removes the old output directory.
262
263 Returns:
264 Tuple:
265 Temporary directory to use
266 New image filename
267 """
Simon Glassc1aa66e2022-01-29 14:14:04 -0700268 image_fname = tools.get_output_filename('image.bin')
Simon Glassf86a7362019-07-20 12:24:10 -0600269 tmpdir = tempfile.mkdtemp(prefix='binman.')
270 updated_fname = os.path.join(tmpdir, 'image-updated.bin')
Simon Glassc1aa66e2022-01-29 14:14:04 -0700271 tools.write_file(updated_fname, tools.read_file(image_fname))
Simon Glassf86a7362019-07-20 12:24:10 -0600272 self._CleanupOutputDir()
273 return tmpdir, updated_fname
274
Simon Glassb8ef5b62018-07-17 13:25:48 -0600275 @classmethod
Simon Glassb986b3b2019-08-24 07:22:43 -0600276 def _ResetDtbs(cls):
Simon Glassb8ef5b62018-07-17 13:25:48 -0600277 TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA)
278 TestFunctional._MakeInputFile('spl/u-boot-spl.dtb', U_BOOT_SPL_DTB_DATA)
279 TestFunctional._MakeInputFile('tpl/u-boot-tpl.dtb', U_BOOT_TPL_DTB_DATA)
280
Simon Glass4f443042016-11-25 20:15:52 -0700281 def _RunBinman(self, *args, **kwargs):
282 """Run binman using the command line
283
284 Args:
285 Arguments to pass, as a list of strings
286 kwargs: Arguments to pass to Command.RunPipe()
287 """
Simon Glassd9800692022-01-29 14:14:05 -0700288 result = command.run_pipe([[self._binman_pathname] + list(args)],
Simon Glass4f443042016-11-25 20:15:52 -0700289 capture=True, capture_stderr=True, raise_on_error=False)
290 if result.return_code and kwargs.get('raise_on_error', True):
291 raise Exception("Error running '%s': %s" % (' '.join(args),
292 result.stdout + result.stderr))
293 return result
294
Simon Glass53cd5d92019-07-08 14:25:29 -0600295 def _DoBinman(self, *argv):
Simon Glass4f443042016-11-25 20:15:52 -0700296 """Run binman using directly (in the same process)
297
298 Args:
299 Arguments to pass, as a list of strings
300 Returns:
301 Return value (0 for success)
302 """
Simon Glass53cd5d92019-07-08 14:25:29 -0600303 argv = list(argv)
304 args = cmdline.ParseArgs(argv)
305 args.pager = 'binman-invalid-pager'
306 args.build_dir = self._indir
Simon Glass4f443042016-11-25 20:15:52 -0700307
308 # For testing, you can force an increase in verbosity here
Simon Glass53cd5d92019-07-08 14:25:29 -0600309 # args.verbosity = tout.DEBUG
310 return control.Binman(args)
Simon Glass4f443042016-11-25 20:15:52 -0700311
Simon Glass53af22a2018-07-17 13:25:32 -0600312 def _DoTestFile(self, fname, debug=False, map=False, update_dtb=False,
Simon Glasseb833d82019-04-25 21:58:34 -0600313 entry_args=None, images=None, use_real_dtb=False,
Simon Glass63aeaeb2021-03-18 20:25:05 +1300314 use_expanded=False, verbosity=None, allow_missing=False,
Heiko Thierya89c8f22022-01-06 11:49:41 +0100315 allow_fake_blobs=False, extra_indirs=None, threads=None,
Simon Glass4f9ee832022-01-09 20:14:09 -0700316 test_section_timeout=False, update_fdt_in_elf=None,
317 force_missing_bintools=''):
Simon Glass4f443042016-11-25 20:15:52 -0700318 """Run binman with a given test file
319
320 Args:
Simon Glass741f2d62018-10-01 12:22:30 -0600321 fname: Device-tree source filename to use (e.g. 005_simple.dts)
Simon Glass7ae5f312018-06-01 09:38:19 -0600322 debug: True to enable debugging output
Simon Glass3b0c3822018-06-01 09:38:20 -0600323 map: True to output map files for the images
Simon Glass3ab95982018-08-01 15:22:37 -0600324 update_dtb: Update the offset and size of each entry in the device
Simon Glass16b8d6b2018-07-06 10:27:42 -0600325 tree before packing it into the image
Simon Glass0bfa7b02018-09-14 04:57:12 -0600326 entry_args: Dict of entry args to supply to binman
327 key: arg name
328 value: value of that arg
329 images: List of image names to build
Simon Glasse9d336d2020-09-01 05:13:55 -0600330 use_real_dtb: True to use the test file as the contents of
331 the u-boot-dtb entry. Normally this is not needed and the
332 test contents (the U_BOOT_DTB_DATA string) can be used.
333 But in some test we need the real contents.
Simon Glass63aeaeb2021-03-18 20:25:05 +1300334 use_expanded: True to use expanded entries where available, e.g.
335 'u-boot-expanded' instead of 'u-boot'
Simon Glasse9d336d2020-09-01 05:13:55 -0600336 verbosity: Verbosity level to use (0-3, None=don't set it)
337 allow_missing: Set the '--allow-missing' flag so that missing
338 external binaries just produce a warning instead of an error
Heiko Thierya89c8f22022-01-06 11:49:41 +0100339 allow_fake_blobs: Set the '--fake-ext-blobs' flag
Simon Glass6cf99532020-09-01 05:13:59 -0600340 extra_indirs: Extra input directories to add using -I
Simon Glassc69d19c2021-07-06 10:36:37 -0600341 threads: Number of threads to use (None for default, 0 for
342 single-threaded)
Simon Glass7115f002021-11-03 21:09:17 -0600343 test_section_timeout: True to force the first time to timeout, as
344 used in testThreadTimeout()
Simon Glass0427bed2021-11-03 21:09:18 -0600345 update_fdt_in_elf: Value to pass with --update-fdt-in-elf=xxx
Simon Glass4f9ee832022-01-09 20:14:09 -0700346 force_missing_tools (str): comma-separated list of bintools to
347 regard as missing
Simon Glass7115f002021-11-03 21:09:17 -0600348
349 Returns:
350 int return code, 0 on success
Simon Glass4f443042016-11-25 20:15:52 -0700351 """
Simon Glass53cd5d92019-07-08 14:25:29 -0600352 args = []
Simon Glass7fe91732017-11-13 18:55:00 -0700353 if debug:
354 args.append('-D')
Simon Glass53cd5d92019-07-08 14:25:29 -0600355 if verbosity is not None:
356 args.append('-v%d' % verbosity)
357 elif self.verbosity:
358 args.append('-v%d' % self.verbosity)
359 if self.toolpath:
360 for path in self.toolpath:
361 args += ['--toolpath', path]
Simon Glassc69d19c2021-07-06 10:36:37 -0600362 if threads is not None:
363 args.append('-T%d' % threads)
364 if test_section_timeout:
365 args.append('--test-section-timeout')
Simon Glass53cd5d92019-07-08 14:25:29 -0600366 args += ['build', '-p', '-I', self._indir, '-d', self.TestFile(fname)]
Simon Glass3b0c3822018-06-01 09:38:20 -0600367 if map:
368 args.append('-m')
Simon Glass16b8d6b2018-07-06 10:27:42 -0600369 if update_dtb:
Simon Glass2569e102019-07-08 13:18:47 -0600370 args.append('-u')
Simon Glass93d17412018-09-14 04:57:23 -0600371 if not use_real_dtb:
372 args.append('--fake-dtb')
Simon Glass63aeaeb2021-03-18 20:25:05 +1300373 if not use_expanded:
374 args.append('--no-expanded')
Simon Glass53af22a2018-07-17 13:25:32 -0600375 if entry_args:
Simon Glass50979152019-05-14 15:53:41 -0600376 for arg, value in entry_args.items():
Simon Glass53af22a2018-07-17 13:25:32 -0600377 args.append('-a%s=%s' % (arg, value))
Simon Glass4f9f1052020-07-09 18:39:38 -0600378 if allow_missing:
379 args.append('-M')
Heiko Thierya89c8f22022-01-06 11:49:41 +0100380 if allow_fake_blobs:
381 args.append('--fake-ext-blobs')
Simon Glass4f9ee832022-01-09 20:14:09 -0700382 if force_missing_bintools:
383 args += ['--force-missing-bintools', force_missing_bintools]
Simon Glass0427bed2021-11-03 21:09:18 -0600384 if update_fdt_in_elf:
385 args += ['--update-fdt-in-elf', update_fdt_in_elf]
Simon Glass0bfa7b02018-09-14 04:57:12 -0600386 if images:
387 for image in images:
388 args += ['-i', image]
Simon Glass6cf99532020-09-01 05:13:59 -0600389 if extra_indirs:
390 for indir in extra_indirs:
391 args += ['-I', indir]
Simon Glass7fe91732017-11-13 18:55:00 -0700392 return self._DoBinman(*args)
Simon Glass4f443042016-11-25 20:15:52 -0700393
394 def _SetupDtb(self, fname, outfile='u-boot.dtb'):
Simon Glasse0ff8552016-11-25 20:15:53 -0700395 """Set up a new test device-tree file
396
397 The given file is compiled and set up as the device tree to be used
398 for ths test.
399
400 Args:
401 fname: Filename of .dts file to read
Simon Glass7ae5f312018-06-01 09:38:19 -0600402 outfile: Output filename for compiled device-tree binary
Simon Glasse0ff8552016-11-25 20:15:53 -0700403
404 Returns:
Simon Glass7ae5f312018-06-01 09:38:19 -0600405 Contents of device-tree binary
Simon Glasse0ff8552016-11-25 20:15:53 -0700406 """
Simon Glassa004f292019-07-20 12:23:49 -0600407 tmpdir = tempfile.mkdtemp(prefix='binmant.')
408 dtb = fdt_util.EnsureCompiled(self.TestFile(fname), tmpdir)
Simon Glass1d0ebf72019-05-14 15:53:42 -0600409 with open(dtb, 'rb') as fd:
Simon Glass4f443042016-11-25 20:15:52 -0700410 data = fd.read()
411 TestFunctional._MakeInputFile(outfile, data)
Simon Glassa004f292019-07-20 12:23:49 -0600412 shutil.rmtree(tmpdir)
Simon Glasse0e62752018-10-01 21:12:41 -0600413 return data
Simon Glass4f443042016-11-25 20:15:52 -0700414
Simon Glass6ed45ba2018-09-14 04:57:24 -0600415 def _GetDtbContentsForSplTpl(self, dtb_data, name):
416 """Create a version of the main DTB for SPL or SPL
417
418 For testing we don't actually have different versions of the DTB. With
419 U-Boot we normally run fdtgrep to remove unwanted nodes, but for tests
420 we don't normally have any unwanted nodes.
421
422 We still want the DTBs for SPL and TPL to be different though, since
423 otherwise it is confusing to know which one we are looking at. So add
424 an 'spl' or 'tpl' property to the top-level node.
Simon Glasse9d336d2020-09-01 05:13:55 -0600425
426 Args:
427 dtb_data: dtb data to modify (this should be a value devicetree)
428 name: Name of a new property to add
429
430 Returns:
431 New dtb data with the property added
Simon Glass6ed45ba2018-09-14 04:57:24 -0600432 """
433 dtb = fdt.Fdt.FromData(dtb_data)
434 dtb.Scan()
435 dtb.GetNode('/binman').AddZeroProp(name)
436 dtb.Sync(auto_resize=True)
437 dtb.Pack()
438 return dtb.GetContents()
439
Simon Glass63aeaeb2021-03-18 20:25:05 +1300440 def _DoReadFileDtb(self, fname, use_real_dtb=False, use_expanded=False,
441 map=False, update_dtb=False, entry_args=None,
Simon Glassc69d19c2021-07-06 10:36:37 -0600442 reset_dtbs=True, extra_indirs=None, threads=None):
Simon Glass4f443042016-11-25 20:15:52 -0700443 """Run binman and return the resulting image
444
445 This runs binman with a given test file and then reads the resulting
446 output file. It is a shortcut function since most tests need to do
447 these steps.
448
449 Raises an assertion failure if binman returns a non-zero exit code.
450
451 Args:
Simon Glass741f2d62018-10-01 12:22:30 -0600452 fname: Device-tree source filename to use (e.g. 005_simple.dts)
Simon Glass4f443042016-11-25 20:15:52 -0700453 use_real_dtb: True to use the test file as the contents of
454 the u-boot-dtb entry. Normally this is not needed and the
455 test contents (the U_BOOT_DTB_DATA string) can be used.
456 But in some test we need the real contents.
Simon Glass63aeaeb2021-03-18 20:25:05 +1300457 use_expanded: True to use expanded entries where available, e.g.
458 'u-boot-expanded' instead of 'u-boot'
Simon Glass3b0c3822018-06-01 09:38:20 -0600459 map: True to output map files for the images
Simon Glass3ab95982018-08-01 15:22:37 -0600460 update_dtb: Update the offset and size of each entry in the device
Simon Glass16b8d6b2018-07-06 10:27:42 -0600461 tree before packing it into the image
Simon Glasse9d336d2020-09-01 05:13:55 -0600462 entry_args: Dict of entry args to supply to binman
463 key: arg name
464 value: value of that arg
465 reset_dtbs: With use_real_dtb the test dtb is overwritten by this
466 function. If reset_dtbs is True, then the original test dtb
467 is written back before this function finishes
Simon Glass6cf99532020-09-01 05:13:59 -0600468 extra_indirs: Extra input directories to add using -I
Simon Glassc69d19c2021-07-06 10:36:37 -0600469 threads: Number of threads to use (None for default, 0 for
470 single-threaded)
Simon Glasse0ff8552016-11-25 20:15:53 -0700471
472 Returns:
473 Tuple:
474 Resulting image contents
475 Device tree contents
Simon Glass3b0c3822018-06-01 09:38:20 -0600476 Map data showing contents of image (or None if none)
Simon Glassea6922e2018-07-17 13:25:27 -0600477 Output device tree binary filename ('u-boot.dtb' path)
Simon Glass4f443042016-11-25 20:15:52 -0700478 """
Simon Glasse0ff8552016-11-25 20:15:53 -0700479 dtb_data = None
Simon Glass4f443042016-11-25 20:15:52 -0700480 # Use the compiled test file as the u-boot-dtb input
481 if use_real_dtb:
Simon Glasse0ff8552016-11-25 20:15:53 -0700482 dtb_data = self._SetupDtb(fname)
Simon Glass6ed45ba2018-09-14 04:57:24 -0600483
484 # For testing purposes, make a copy of the DT for SPL and TPL. Add
485 # a node indicating which it is, so aid verification.
486 for name in ['spl', 'tpl']:
487 dtb_fname = '%s/u-boot-%s.dtb' % (name, name)
488 outfile = os.path.join(self._indir, dtb_fname)
489 TestFunctional._MakeInputFile(dtb_fname,
490 self._GetDtbContentsForSplTpl(dtb_data, name))
Simon Glass4f443042016-11-25 20:15:52 -0700491
492 try:
Simon Glass53af22a2018-07-17 13:25:32 -0600493 retcode = self._DoTestFile(fname, map=map, update_dtb=update_dtb,
Simon Glass6cf99532020-09-01 05:13:59 -0600494 entry_args=entry_args, use_real_dtb=use_real_dtb,
Simon Glassc69d19c2021-07-06 10:36:37 -0600495 use_expanded=use_expanded, extra_indirs=extra_indirs,
496 threads=threads)
Simon Glass4f443042016-11-25 20:15:52 -0700497 self.assertEqual(0, retcode)
Simon Glassc1aa66e2022-01-29 14:14:04 -0700498 out_dtb_fname = tools.get_output_filename('u-boot.dtb.out')
Simon Glass4f443042016-11-25 20:15:52 -0700499
500 # Find the (only) image, read it and return its contents
501 image = control.images['image']
Simon Glassc1aa66e2022-01-29 14:14:04 -0700502 image_fname = tools.get_output_filename('image.bin')
Simon Glass16b8d6b2018-07-06 10:27:42 -0600503 self.assertTrue(os.path.exists(image_fname))
Simon Glass3b0c3822018-06-01 09:38:20 -0600504 if map:
Simon Glassc1aa66e2022-01-29 14:14:04 -0700505 map_fname = tools.get_output_filename('image.map')
Simon Glass3b0c3822018-06-01 09:38:20 -0600506 with open(map_fname) as fd:
507 map_data = fd.read()
508 else:
509 map_data = None
Simon Glass1d0ebf72019-05-14 15:53:42 -0600510 with open(image_fname, 'rb') as fd:
Simon Glass16b8d6b2018-07-06 10:27:42 -0600511 return fd.read(), dtb_data, map_data, out_dtb_fname
Simon Glass4f443042016-11-25 20:15:52 -0700512 finally:
513 # Put the test file back
Simon Glass6ed45ba2018-09-14 04:57:24 -0600514 if reset_dtbs and use_real_dtb:
Simon Glassb8ef5b62018-07-17 13:25:48 -0600515 self._ResetDtbs()
Simon Glass4f443042016-11-25 20:15:52 -0700516
Simon Glass3c081312019-07-08 14:25:26 -0600517 def _DoReadFileRealDtb(self, fname):
518 """Run binman with a real .dtb file and return the resulting data
519
520 Args:
521 fname: DT source filename to use (e.g. 082_fdt_update_all.dts)
522
523 Returns:
524 Resulting image contents
525 """
526 return self._DoReadFileDtb(fname, use_real_dtb=True, update_dtb=True)[0]
527
Simon Glasse0ff8552016-11-25 20:15:53 -0700528 def _DoReadFile(self, fname, use_real_dtb=False):
Simon Glass7ae5f312018-06-01 09:38:19 -0600529 """Helper function which discards the device-tree binary
530
531 Args:
Simon Glass741f2d62018-10-01 12:22:30 -0600532 fname: Device-tree source filename to use (e.g. 005_simple.dts)
Simon Glass7ae5f312018-06-01 09:38:19 -0600533 use_real_dtb: True to use the test file as the contents of
534 the u-boot-dtb entry. Normally this is not needed and the
535 test contents (the U_BOOT_DTB_DATA string) can be used.
536 But in some test we need the real contents.
Simon Glassea6922e2018-07-17 13:25:27 -0600537
538 Returns:
539 Resulting image contents
Simon Glass7ae5f312018-06-01 09:38:19 -0600540 """
Simon Glasse0ff8552016-11-25 20:15:53 -0700541 return self._DoReadFileDtb(fname, use_real_dtb)[0]
542
Simon Glass4f443042016-11-25 20:15:52 -0700543 @classmethod
Simon Glassb986b3b2019-08-24 07:22:43 -0600544 def _MakeInputFile(cls, fname, contents):
Simon Glass4f443042016-11-25 20:15:52 -0700545 """Create a new test input file, creating directories as needed
546
547 Args:
Simon Glass3ab95982018-08-01 15:22:37 -0600548 fname: Filename to create
Simon Glass4f443042016-11-25 20:15:52 -0700549 contents: File contents to write in to the file
550 Returns:
551 Full pathname of file created
552 """
Simon Glassb986b3b2019-08-24 07:22:43 -0600553 pathname = os.path.join(cls._indir, fname)
Simon Glass4f443042016-11-25 20:15:52 -0700554 dirname = os.path.dirname(pathname)
555 if dirname and not os.path.exists(dirname):
556 os.makedirs(dirname)
557 with open(pathname, 'wb') as fd:
558 fd.write(contents)
559 return pathname
560
561 @classmethod
Simon Glassb986b3b2019-08-24 07:22:43 -0600562 def _MakeInputDir(cls, dirname):
Simon Glass0ef87aa2018-07-17 13:25:44 -0600563 """Create a new test input directory, creating directories as needed
564
565 Args:
566 dirname: Directory name to create
567
568 Returns:
569 Full pathname of directory created
570 """
Simon Glassb986b3b2019-08-24 07:22:43 -0600571 pathname = os.path.join(cls._indir, dirname)
Simon Glass0ef87aa2018-07-17 13:25:44 -0600572 if not os.path.exists(pathname):
573 os.makedirs(pathname)
574 return pathname
575
576 @classmethod
Simon Glassb986b3b2019-08-24 07:22:43 -0600577 def _SetupSplElf(cls, src_fname='bss_data'):
Simon Glass11ae93e2018-10-01 21:12:47 -0600578 """Set up an ELF file with a '_dt_ucode_base_size' symbol
579
580 Args:
581 Filename of ELF file to use as SPL
582 """
Simon Glassc9a0b272019-08-24 07:22:59 -0600583 TestFunctional._MakeInputFile('spl/u-boot-spl',
Simon Glassc1aa66e2022-01-29 14:14:04 -0700584 tools.read_file(cls.ElfTestFile(src_fname)))
Simon Glass11ae93e2018-10-01 21:12:47 -0600585
586 @classmethod
Simon Glass2090f1e2019-08-24 07:23:00 -0600587 def _SetupTplElf(cls, src_fname='bss_data'):
588 """Set up an ELF file with a '_dt_ucode_base_size' symbol
589
590 Args:
591 Filename of ELF file to use as TPL
592 """
593 TestFunctional._MakeInputFile('tpl/u-boot-tpl',
Simon Glassc1aa66e2022-01-29 14:14:04 -0700594 tools.read_file(cls.ElfTestFile(src_fname)))
Simon Glass2090f1e2019-08-24 07:23:00 -0600595
596 @classmethod
Simon Glass0ba4b3d2020-07-09 18:39:41 -0600597 def _SetupDescriptor(cls):
598 with open(cls.TestFile('descriptor.bin'), 'rb') as fd:
599 TestFunctional._MakeInputFile('descriptor.bin', fd.read())
600
601 @classmethod
Simon Glassb986b3b2019-08-24 07:22:43 -0600602 def TestFile(cls, fname):
603 return os.path.join(cls._binman_dir, 'test', fname)
Simon Glass4f443042016-11-25 20:15:52 -0700604
Simon Glass53e22bf2019-08-24 07:22:53 -0600605 @classmethod
606 def ElfTestFile(cls, fname):
607 return os.path.join(cls._elf_testdir, fname)
608
Simon Glass4f443042016-11-25 20:15:52 -0700609 def AssertInList(self, grep_list, target):
610 """Assert that at least one of a list of things is in a target
611
612 Args:
613 grep_list: List of strings to check
614 target: Target string
615 """
616 for grep in grep_list:
617 if grep in target:
618 return
Simon Glass1fc62de2019-05-17 22:00:50 -0600619 self.fail("Error: '%s' not found in '%s'" % (grep_list, target))
Simon Glass4f443042016-11-25 20:15:52 -0700620
621 def CheckNoGaps(self, entries):
622 """Check that all entries fit together without gaps
623
624 Args:
625 entries: List of entries to check
626 """
Simon Glass3ab95982018-08-01 15:22:37 -0600627 offset = 0
Simon Glass4f443042016-11-25 20:15:52 -0700628 for entry in entries.values():
Simon Glass3ab95982018-08-01 15:22:37 -0600629 self.assertEqual(offset, entry.offset)
630 offset += entry.size
Simon Glass4f443042016-11-25 20:15:52 -0700631
Simon Glasse0ff8552016-11-25 20:15:53 -0700632 def GetFdtLen(self, dtb):
Simon Glass7ae5f312018-06-01 09:38:19 -0600633 """Get the totalsize field from a device-tree binary
Simon Glasse0ff8552016-11-25 20:15:53 -0700634
635 Args:
Simon Glass7ae5f312018-06-01 09:38:19 -0600636 dtb: Device-tree binary contents
Simon Glasse0ff8552016-11-25 20:15:53 -0700637
638 Returns:
Simon Glass7ae5f312018-06-01 09:38:19 -0600639 Total size of device-tree binary, from the header
Simon Glasse0ff8552016-11-25 20:15:53 -0700640 """
641 return struct.unpack('>L', dtb[4:8])[0]
642
Simon Glass086cec92019-07-08 14:25:27 -0600643 def _GetPropTree(self, dtb, prop_names, prefix='/binman/'):
Simon Glass16b8d6b2018-07-06 10:27:42 -0600644 def AddNode(node, path):
645 if node.name != '/':
646 path += '/' + node.name
Simon Glass086cec92019-07-08 14:25:27 -0600647 for prop in node.props.values():
648 if prop.name in prop_names:
649 prop_path = path + ':' + prop.name
650 tree[prop_path[len(prefix):]] = fdt_util.fdt32_to_cpu(
651 prop.value)
Simon Glass16b8d6b2018-07-06 10:27:42 -0600652 for subnode in node.subnodes:
Simon Glass16b8d6b2018-07-06 10:27:42 -0600653 AddNode(subnode, path)
654
655 tree = {}
Simon Glass16b8d6b2018-07-06 10:27:42 -0600656 AddNode(dtb.GetRoot(), '')
657 return tree
658
Simon Glass4f443042016-11-25 20:15:52 -0700659 def testRun(self):
660 """Test a basic run with valid args"""
661 result = self._RunBinman('-h')
662
663 def testFullHelp(self):
664 """Test that the full help is displayed with -H"""
665 result = self._RunBinman('-H')
Simon Glass61adb2d2021-03-18 20:25:13 +1300666 help_file = os.path.join(self._binman_dir, 'README.rst')
Tom Rini3759df02018-01-16 15:29:50 -0500667 # Remove possible extraneous strings
668 extra = '::::::::::::::\n' + help_file + '\n::::::::::::::\n'
669 gothelp = result.stdout.replace(extra, '')
670 self.assertEqual(len(gothelp), os.path.getsize(help_file))
Simon Glass4f443042016-11-25 20:15:52 -0700671 self.assertEqual(0, len(result.stderr))
672 self.assertEqual(0, result.return_code)
673
674 def testFullHelpInternal(self):
675 """Test that the full help is displayed with -H"""
676 try:
677 command.test_result = command.CommandResult()
678 result = self._DoBinman('-H')
Simon Glass61adb2d2021-03-18 20:25:13 +1300679 help_file = os.path.join(self._binman_dir, 'README.rst')
Simon Glass4f443042016-11-25 20:15:52 -0700680 finally:
681 command.test_result = None
682
683 def testHelp(self):
684 """Test that the basic help is displayed with -h"""
685 result = self._RunBinman('-h')
686 self.assertTrue(len(result.stdout) > 200)
687 self.assertEqual(0, len(result.stderr))
688 self.assertEqual(0, result.return_code)
689
Simon Glass4f443042016-11-25 20:15:52 -0700690 def testBoard(self):
691 """Test that we can run it with a specific board"""
Simon Glass741f2d62018-10-01 12:22:30 -0600692 self._SetupDtb('005_simple.dts', 'sandbox/u-boot.dtb')
Simon Glass4f443042016-11-25 20:15:52 -0700693 TestFunctional._MakeInputFile('sandbox/u-boot.bin', U_BOOT_DATA)
Simon Glass63aeaeb2021-03-18 20:25:05 +1300694 result = self._DoBinman('build', '-n', '-b', 'sandbox')
Simon Glass4f443042016-11-25 20:15:52 -0700695 self.assertEqual(0, result)
696
697 def testNeedBoard(self):
698 """Test that we get an error when no board ius supplied"""
699 with self.assertRaises(ValueError) as e:
Simon Glass53cd5d92019-07-08 14:25:29 -0600700 result = self._DoBinman('build')
Simon Glass4f443042016-11-25 20:15:52 -0700701 self.assertIn("Must provide a board to process (use -b <board>)",
702 str(e.exception))
703
704 def testMissingDt(self):
Simon Glass7ae5f312018-06-01 09:38:19 -0600705 """Test that an invalid device-tree file generates an error"""
Simon Glass4f443042016-11-25 20:15:52 -0700706 with self.assertRaises(Exception) as e:
Simon Glass53cd5d92019-07-08 14:25:29 -0600707 self._RunBinman('build', '-d', 'missing_file')
Simon Glass4f443042016-11-25 20:15:52 -0700708 # We get one error from libfdt, and a different one from fdtget.
709 self.AssertInList(["Couldn't open blob from 'missing_file'",
710 'No such file or directory'], str(e.exception))
711
712 def testBrokenDt(self):
Simon Glass7ae5f312018-06-01 09:38:19 -0600713 """Test that an invalid device-tree source file generates an error
Simon Glass4f443042016-11-25 20:15:52 -0700714
715 Since this is a source file it should be compiled and the error
716 will come from the device-tree compiler (dtc).
717 """
718 with self.assertRaises(Exception) as e:
Simon Glass53cd5d92019-07-08 14:25:29 -0600719 self._RunBinman('build', '-d', self.TestFile('001_invalid.dts'))
Simon Glass4f443042016-11-25 20:15:52 -0700720 self.assertIn("FATAL ERROR: Unable to parse input tree",
721 str(e.exception))
722
723 def testMissingNode(self):
724 """Test that a device tree without a 'binman' node generates an error"""
725 with self.assertRaises(Exception) as e:
Simon Glass53cd5d92019-07-08 14:25:29 -0600726 self._DoBinman('build', '-d', self.TestFile('002_missing_node.dts'))
Simon Glass4f443042016-11-25 20:15:52 -0700727 self.assertIn("does not have a 'binman' node", str(e.exception))
728
729 def testEmpty(self):
730 """Test that an empty binman node works OK (i.e. does nothing)"""
Simon Glass53cd5d92019-07-08 14:25:29 -0600731 result = self._RunBinman('build', '-d', self.TestFile('003_empty.dts'))
Simon Glass4f443042016-11-25 20:15:52 -0700732 self.assertEqual(0, len(result.stderr))
733 self.assertEqual(0, result.return_code)
734
735 def testInvalidEntry(self):
736 """Test that an invalid entry is flagged"""
737 with self.assertRaises(Exception) as e:
Simon Glass53cd5d92019-07-08 14:25:29 -0600738 result = self._RunBinman('build', '-d',
Simon Glass741f2d62018-10-01 12:22:30 -0600739 self.TestFile('004_invalid_entry.dts'))
Simon Glass4f443042016-11-25 20:15:52 -0700740 self.assertIn("Unknown entry type 'not-a-valid-type' in node "
741 "'/binman/not-a-valid-type'", str(e.exception))
742
743 def testSimple(self):
744 """Test a simple binman with a single file"""
Simon Glass741f2d62018-10-01 12:22:30 -0600745 data = self._DoReadFile('005_simple.dts')
Simon Glass4f443042016-11-25 20:15:52 -0700746 self.assertEqual(U_BOOT_DATA, data)
747
Simon Glass7fe91732017-11-13 18:55:00 -0700748 def testSimpleDebug(self):
749 """Test a simple binman run with debugging enabled"""
Simon Glasse2705fa2019-07-08 14:25:53 -0600750 self._DoTestFile('005_simple.dts', debug=True)
Simon Glass7fe91732017-11-13 18:55:00 -0700751
Simon Glass4f443042016-11-25 20:15:52 -0700752 def testDual(self):
753 """Test that we can handle creating two images
754
755 This also tests image padding.
756 """
Simon Glass741f2d62018-10-01 12:22:30 -0600757 retcode = self._DoTestFile('006_dual_image.dts')
Simon Glass4f443042016-11-25 20:15:52 -0700758 self.assertEqual(0, retcode)
759
760 image = control.images['image1']
Simon Glass8beb11e2019-07-08 14:25:47 -0600761 self.assertEqual(len(U_BOOT_DATA), image.size)
Simon Glassc1aa66e2022-01-29 14:14:04 -0700762 fname = tools.get_output_filename('image1.bin')
Simon Glass4f443042016-11-25 20:15:52 -0700763 self.assertTrue(os.path.exists(fname))
Simon Glass1d0ebf72019-05-14 15:53:42 -0600764 with open(fname, 'rb') as fd:
Simon Glass4f443042016-11-25 20:15:52 -0700765 data = fd.read()
766 self.assertEqual(U_BOOT_DATA, data)
767
768 image = control.images['image2']
Simon Glass8beb11e2019-07-08 14:25:47 -0600769 self.assertEqual(3 + len(U_BOOT_DATA) + 5, image.size)
Simon Glassc1aa66e2022-01-29 14:14:04 -0700770 fname = tools.get_output_filename('image2.bin')
Simon Glass4f443042016-11-25 20:15:52 -0700771 self.assertTrue(os.path.exists(fname))
Simon Glass1d0ebf72019-05-14 15:53:42 -0600772 with open(fname, 'rb') as fd:
Simon Glass4f443042016-11-25 20:15:52 -0700773 data = fd.read()
774 self.assertEqual(U_BOOT_DATA, data[3:7])
Simon Glassc1aa66e2022-01-29 14:14:04 -0700775 self.assertEqual(tools.get_bytes(0, 3), data[:3])
776 self.assertEqual(tools.get_bytes(0, 5), data[7:])
Simon Glass4f443042016-11-25 20:15:52 -0700777
778 def testBadAlign(self):
779 """Test that an invalid alignment value is detected"""
780 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -0600781 self._DoTestFile('007_bad_align.dts')
Simon Glass4f443042016-11-25 20:15:52 -0700782 self.assertIn("Node '/binman/u-boot': Alignment 23 must be a power "
783 "of two", str(e.exception))
784
785 def testPackSimple(self):
786 """Test that packing works as expected"""
Simon Glass741f2d62018-10-01 12:22:30 -0600787 retcode = self._DoTestFile('008_pack.dts')
Simon Glass4f443042016-11-25 20:15:52 -0700788 self.assertEqual(0, retcode)
789 self.assertIn('image', control.images)
790 image = control.images['image']
Simon Glass8f1da502018-06-01 09:38:12 -0600791 entries = image.GetEntries()
Simon Glass4f443042016-11-25 20:15:52 -0700792 self.assertEqual(5, len(entries))
793
794 # First u-boot
795 self.assertIn('u-boot', entries)
796 entry = entries['u-boot']
Simon Glass3ab95982018-08-01 15:22:37 -0600797 self.assertEqual(0, entry.offset)
Simon Glass4f443042016-11-25 20:15:52 -0700798 self.assertEqual(len(U_BOOT_DATA), entry.size)
799
800 # Second u-boot, aligned to 16-byte boundary
801 self.assertIn('u-boot-align', entries)
802 entry = entries['u-boot-align']
Simon Glass3ab95982018-08-01 15:22:37 -0600803 self.assertEqual(16, entry.offset)
Simon Glass4f443042016-11-25 20:15:52 -0700804 self.assertEqual(len(U_BOOT_DATA), entry.size)
805
806 # Third u-boot, size 23 bytes
807 self.assertIn('u-boot-size', entries)
808 entry = entries['u-boot-size']
Simon Glass3ab95982018-08-01 15:22:37 -0600809 self.assertEqual(20, entry.offset)
Simon Glass4f443042016-11-25 20:15:52 -0700810 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
811 self.assertEqual(23, entry.size)
812
813 # Fourth u-boot, placed immediate after the above
814 self.assertIn('u-boot-next', entries)
815 entry = entries['u-boot-next']
Simon Glass3ab95982018-08-01 15:22:37 -0600816 self.assertEqual(43, entry.offset)
Simon Glass4f443042016-11-25 20:15:52 -0700817 self.assertEqual(len(U_BOOT_DATA), entry.size)
818
Simon Glass3ab95982018-08-01 15:22:37 -0600819 # Fifth u-boot, placed at a fixed offset
Simon Glass4f443042016-11-25 20:15:52 -0700820 self.assertIn('u-boot-fixed', entries)
821 entry = entries['u-boot-fixed']
Simon Glass3ab95982018-08-01 15:22:37 -0600822 self.assertEqual(61, entry.offset)
Simon Glass4f443042016-11-25 20:15:52 -0700823 self.assertEqual(len(U_BOOT_DATA), entry.size)
824
Simon Glass8beb11e2019-07-08 14:25:47 -0600825 self.assertEqual(65, image.size)
Simon Glass4f443042016-11-25 20:15:52 -0700826
827 def testPackExtra(self):
828 """Test that extra packing feature works as expected"""
Simon Glass4eec34c2020-10-26 17:40:10 -0600829 data, _, _, out_dtb_fname = self._DoReadFileDtb('009_pack_extra.dts',
830 update_dtb=True)
Simon Glass4f443042016-11-25 20:15:52 -0700831
Simon Glass4f443042016-11-25 20:15:52 -0700832 self.assertIn('image', control.images)
833 image = control.images['image']
Simon Glass8f1da502018-06-01 09:38:12 -0600834 entries = image.GetEntries()
Simon Glass4f443042016-11-25 20:15:52 -0700835 self.assertEqual(5, len(entries))
836
837 # First u-boot with padding before and after
838 self.assertIn('u-boot', entries)
839 entry = entries['u-boot']
Simon Glass3ab95982018-08-01 15:22:37 -0600840 self.assertEqual(0, entry.offset)
Simon Glass4f443042016-11-25 20:15:52 -0700841 self.assertEqual(3, entry.pad_before)
842 self.assertEqual(3 + 5 + len(U_BOOT_DATA), entry.size)
Simon Glassef439ed2020-10-26 17:40:08 -0600843 self.assertEqual(U_BOOT_DATA, entry.data)
Simon Glassc1aa66e2022-01-29 14:14:04 -0700844 self.assertEqual(tools.get_bytes(0, 3) + U_BOOT_DATA +
845 tools.get_bytes(0, 5), data[:entry.size])
Simon Glassef439ed2020-10-26 17:40:08 -0600846 pos = entry.size
Simon Glass4f443042016-11-25 20:15:52 -0700847
848 # Second u-boot has an aligned size, but it has no effect
849 self.assertIn('u-boot-align-size-nop', entries)
850 entry = entries['u-boot-align-size-nop']
Simon Glassef439ed2020-10-26 17:40:08 -0600851 self.assertEqual(pos, entry.offset)
852 self.assertEqual(len(U_BOOT_DATA), entry.size)
853 self.assertEqual(U_BOOT_DATA, entry.data)
854 self.assertEqual(U_BOOT_DATA, data[pos:pos + entry.size])
855 pos += entry.size
Simon Glass4f443042016-11-25 20:15:52 -0700856
857 # Third u-boot has an aligned size too
858 self.assertIn('u-boot-align-size', entries)
859 entry = entries['u-boot-align-size']
Simon Glassef439ed2020-10-26 17:40:08 -0600860 self.assertEqual(pos, entry.offset)
Simon Glass4f443042016-11-25 20:15:52 -0700861 self.assertEqual(32, entry.size)
Simon Glassef439ed2020-10-26 17:40:08 -0600862 self.assertEqual(U_BOOT_DATA, entry.data)
Simon Glassc1aa66e2022-01-29 14:14:04 -0700863 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 32 - len(U_BOOT_DATA)),
Simon Glassef439ed2020-10-26 17:40:08 -0600864 data[pos:pos + entry.size])
865 pos += entry.size
Simon Glass4f443042016-11-25 20:15:52 -0700866
867 # Fourth u-boot has an aligned end
868 self.assertIn('u-boot-align-end', entries)
869 entry = entries['u-boot-align-end']
Simon Glass3ab95982018-08-01 15:22:37 -0600870 self.assertEqual(48, entry.offset)
Simon Glass4f443042016-11-25 20:15:52 -0700871 self.assertEqual(16, entry.size)
Simon Glassef439ed2020-10-26 17:40:08 -0600872 self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)])
Simon Glassc1aa66e2022-01-29 14:14:04 -0700873 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 16 - len(U_BOOT_DATA)),
Simon Glassef439ed2020-10-26 17:40:08 -0600874 data[pos:pos + entry.size])
875 pos += entry.size
Simon Glass4f443042016-11-25 20:15:52 -0700876
877 # Fifth u-boot immediately afterwards
878 self.assertIn('u-boot-align-both', entries)
879 entry = entries['u-boot-align-both']
Simon Glass3ab95982018-08-01 15:22:37 -0600880 self.assertEqual(64, entry.offset)
Simon Glass4f443042016-11-25 20:15:52 -0700881 self.assertEqual(64, entry.size)
Simon Glassef439ed2020-10-26 17:40:08 -0600882 self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)])
Simon Glassc1aa66e2022-01-29 14:14:04 -0700883 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 64 - len(U_BOOT_DATA)),
Simon Glassef439ed2020-10-26 17:40:08 -0600884 data[pos:pos + entry.size])
Simon Glass4f443042016-11-25 20:15:52 -0700885
886 self.CheckNoGaps(entries)
Simon Glass8beb11e2019-07-08 14:25:47 -0600887 self.assertEqual(128, image.size)
Simon Glass4f443042016-11-25 20:15:52 -0700888
Simon Glass4eec34c2020-10-26 17:40:10 -0600889 dtb = fdt.Fdt(out_dtb_fname)
890 dtb.Scan()
891 props = self._GetPropTree(dtb, ['size', 'offset', 'image-pos'])
892 expected = {
893 'image-pos': 0,
894 'offset': 0,
895 'size': 128,
896
897 'u-boot:image-pos': 0,
898 'u-boot:offset': 0,
899 'u-boot:size': 3 + 5 + len(U_BOOT_DATA),
900
901 'u-boot-align-size-nop:image-pos': 12,
902 'u-boot-align-size-nop:offset': 12,
903 'u-boot-align-size-nop:size': 4,
904
905 'u-boot-align-size:image-pos': 16,
906 'u-boot-align-size:offset': 16,
907 'u-boot-align-size:size': 32,
908
909 'u-boot-align-end:image-pos': 48,
910 'u-boot-align-end:offset': 48,
911 'u-boot-align-end:size': 16,
912
913 'u-boot-align-both:image-pos': 64,
914 'u-boot-align-both:offset': 64,
915 'u-boot-align-both:size': 64,
916 }
917 self.assertEqual(expected, props)
918
Simon Glass4f443042016-11-25 20:15:52 -0700919 def testPackAlignPowerOf2(self):
920 """Test that invalid entry alignment is detected"""
921 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -0600922 self._DoTestFile('010_pack_align_power2.dts')
Simon Glass4f443042016-11-25 20:15:52 -0700923 self.assertIn("Node '/binman/u-boot': Alignment 5 must be a power "
924 "of two", str(e.exception))
925
926 def testPackAlignSizePowerOf2(self):
927 """Test that invalid entry size alignment is detected"""
928 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -0600929 self._DoTestFile('011_pack_align_size_power2.dts')
Simon Glass4f443042016-11-25 20:15:52 -0700930 self.assertIn("Node '/binman/u-boot': Alignment size 55 must be a "
931 "power of two", str(e.exception))
932
933 def testPackInvalidAlign(self):
Simon Glass3ab95982018-08-01 15:22:37 -0600934 """Test detection of an offset that does not match its alignment"""
Simon Glass4f443042016-11-25 20:15:52 -0700935 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -0600936 self._DoTestFile('012_pack_inv_align.dts')
Simon Glass3ab95982018-08-01 15:22:37 -0600937 self.assertIn("Node '/binman/u-boot': Offset 0x5 (5) does not match "
Simon Glass4f443042016-11-25 20:15:52 -0700938 "align 0x4 (4)", str(e.exception))
939
940 def testPackInvalidSizeAlign(self):
941 """Test that invalid entry size alignment is detected"""
942 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -0600943 self._DoTestFile('013_pack_inv_size_align.dts')
Simon Glass4f443042016-11-25 20:15:52 -0700944 self.assertIn("Node '/binman/u-boot': Size 0x5 (5) does not match "
945 "align-size 0x4 (4)", str(e.exception))
946
947 def testPackOverlap(self):
948 """Test that overlapping regions are detected"""
949 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -0600950 self._DoTestFile('014_pack_overlap.dts')
Simon Glass3ab95982018-08-01 15:22:37 -0600951 self.assertIn("Node '/binman/u-boot-align': Offset 0x3 (3) overlaps "
Simon Glass4f443042016-11-25 20:15:52 -0700952 "with previous entry '/binman/u-boot' ending at 0x4 (4)",
953 str(e.exception))
954
955 def testPackEntryOverflow(self):
956 """Test that entries that overflow their size are detected"""
957 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -0600958 self._DoTestFile('015_pack_overflow.dts')
Simon Glass4f443042016-11-25 20:15:52 -0700959 self.assertIn("Node '/binman/u-boot': Entry contents size is 0x4 (4) "
960 "but entry size is 0x3 (3)", str(e.exception))
961
962 def testPackImageOverflow(self):
963 """Test that entries which overflow the image size are detected"""
964 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -0600965 self._DoTestFile('016_pack_image_overflow.dts')
Simon Glass8f1da502018-06-01 09:38:12 -0600966 self.assertIn("Section '/binman': contents size 0x4 (4) exceeds section "
Simon Glass4f443042016-11-25 20:15:52 -0700967 "size 0x3 (3)", str(e.exception))
968
969 def testPackImageSize(self):
970 """Test that the image size can be set"""
Simon Glass741f2d62018-10-01 12:22:30 -0600971 retcode = self._DoTestFile('017_pack_image_size.dts')
Simon Glass4f443042016-11-25 20:15:52 -0700972 self.assertEqual(0, retcode)
973 self.assertIn('image', control.images)
974 image = control.images['image']
Simon Glass8beb11e2019-07-08 14:25:47 -0600975 self.assertEqual(7, image.size)
Simon Glass4f443042016-11-25 20:15:52 -0700976
977 def testPackImageSizeAlign(self):
978 """Test that image size alignemnt works as expected"""
Simon Glass741f2d62018-10-01 12:22:30 -0600979 retcode = self._DoTestFile('018_pack_image_align.dts')
Simon Glass4f443042016-11-25 20:15:52 -0700980 self.assertEqual(0, retcode)
981 self.assertIn('image', control.images)
982 image = control.images['image']
Simon Glass8beb11e2019-07-08 14:25:47 -0600983 self.assertEqual(16, image.size)
Simon Glass4f443042016-11-25 20:15:52 -0700984
985 def testPackInvalidImageAlign(self):
986 """Test that invalid image alignment is detected"""
987 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -0600988 self._DoTestFile('019_pack_inv_image_align.dts')
Simon Glass8f1da502018-06-01 09:38:12 -0600989 self.assertIn("Section '/binman': Size 0x7 (7) does not match "
Simon Glass4f443042016-11-25 20:15:52 -0700990 "align-size 0x8 (8)", str(e.exception))
991
992 def testPackAlignPowerOf2(self):
993 """Test that invalid image alignment is detected"""
994 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -0600995 self._DoTestFile('020_pack_inv_image_align_power2.dts')
Simon Glass8beb11e2019-07-08 14:25:47 -0600996 self.assertIn("Image '/binman': Alignment size 131 must be a power of "
Simon Glass4f443042016-11-25 20:15:52 -0700997 "two", str(e.exception))
998
999 def testImagePadByte(self):
1000 """Test that the image pad byte can be specified"""
Simon Glass11ae93e2018-10-01 21:12:47 -06001001 self._SetupSplElf()
Simon Glass741f2d62018-10-01 12:22:30 -06001002 data = self._DoReadFile('021_image_pad.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07001003 self.assertEqual(U_BOOT_SPL_DATA + tools.get_bytes(0xff, 1) +
Simon Glasse6d85ff2019-05-14 15:53:47 -06001004 U_BOOT_DATA, data)
Simon Glass4f443042016-11-25 20:15:52 -07001005
1006 def testImageName(self):
1007 """Test that image files can be named"""
Simon Glass741f2d62018-10-01 12:22:30 -06001008 retcode = self._DoTestFile('022_image_name.dts')
Simon Glass4f443042016-11-25 20:15:52 -07001009 self.assertEqual(0, retcode)
1010 image = control.images['image1']
Simon Glassc1aa66e2022-01-29 14:14:04 -07001011 fname = tools.get_output_filename('test-name')
Simon Glass4f443042016-11-25 20:15:52 -07001012 self.assertTrue(os.path.exists(fname))
1013
1014 image = control.images['image2']
Simon Glassc1aa66e2022-01-29 14:14:04 -07001015 fname = tools.get_output_filename('test-name.xx')
Simon Glass4f443042016-11-25 20:15:52 -07001016 self.assertTrue(os.path.exists(fname))
1017
1018 def testBlobFilename(self):
1019 """Test that generic blobs can be provided by filename"""
Simon Glass741f2d62018-10-01 12:22:30 -06001020 data = self._DoReadFile('023_blob.dts')
Simon Glass4f443042016-11-25 20:15:52 -07001021 self.assertEqual(BLOB_DATA, data)
1022
1023 def testPackSorted(self):
1024 """Test that entries can be sorted"""
Simon Glass11ae93e2018-10-01 21:12:47 -06001025 self._SetupSplElf()
Simon Glass741f2d62018-10-01 12:22:30 -06001026 data = self._DoReadFile('024_sorted.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07001027 self.assertEqual(tools.get_bytes(0, 1) + U_BOOT_SPL_DATA +
1028 tools.get_bytes(0, 2) + U_BOOT_DATA, data)
Simon Glass4f443042016-11-25 20:15:52 -07001029
Simon Glass3ab95982018-08-01 15:22:37 -06001030 def testPackZeroOffset(self):
1031 """Test that an entry at offset 0 is not given a new offset"""
Simon Glass4f443042016-11-25 20:15:52 -07001032 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001033 self._DoTestFile('025_pack_zero_size.dts')
Simon Glass3ab95982018-08-01 15:22:37 -06001034 self.assertIn("Node '/binman/u-boot-spl': Offset 0x0 (0) overlaps "
Simon Glass4f443042016-11-25 20:15:52 -07001035 "with previous entry '/binman/u-boot' ending at 0x4 (4)",
1036 str(e.exception))
1037
1038 def testPackUbootDtb(self):
1039 """Test that a device tree can be added to U-Boot"""
Simon Glass741f2d62018-10-01 12:22:30 -06001040 data = self._DoReadFile('026_pack_u_boot_dtb.dts')
Simon Glass4f443042016-11-25 20:15:52 -07001041 self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA, data)
Simon Glasse0ff8552016-11-25 20:15:53 -07001042
1043 def testPackX86RomNoSize(self):
1044 """Test that the end-at-4gb property requires a size property"""
1045 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001046 self._DoTestFile('027_pack_4gb_no_size.dts')
Simon Glass8beb11e2019-07-08 14:25:47 -06001047 self.assertIn("Image '/binman': Section size must be provided when "
Simon Glasse0ff8552016-11-25 20:15:53 -07001048 "using end-at-4gb", str(e.exception))
1049
Jagdish Gediya94b57db2018-09-03 21:35:07 +05301050 def test4gbAndSkipAtStartTogether(self):
1051 """Test that the end-at-4gb and skip-at-size property can't be used
1052 together"""
1053 with self.assertRaises(ValueError) as e:
Simon Glassdfdd2b62019-08-24 07:23:02 -06001054 self._DoTestFile('098_4gb_and_skip_at_start_together.dts')
Simon Glass8beb11e2019-07-08 14:25:47 -06001055 self.assertIn("Image '/binman': Provide either 'end-at-4gb' or "
Jagdish Gediya94b57db2018-09-03 21:35:07 +05301056 "'skip-at-start'", str(e.exception))
1057
Simon Glasse0ff8552016-11-25 20:15:53 -07001058 def testPackX86RomOutside(self):
Simon Glass3ab95982018-08-01 15:22:37 -06001059 """Test that the end-at-4gb property checks for offset boundaries"""
Simon Glasse0ff8552016-11-25 20:15:53 -07001060 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001061 self._DoTestFile('028_pack_4gb_outside.dts')
Simon Glasse6bed4f2020-10-26 17:40:05 -06001062 self.assertIn("Node '/binman/u-boot': Offset 0x0 (0) size 0x4 (4) "
1063 "is outside the section '/binman' starting at "
1064 '0xffffffe0 (4294967264) of size 0x20 (32)',
Simon Glasse0ff8552016-11-25 20:15:53 -07001065 str(e.exception))
1066
1067 def testPackX86Rom(self):
1068 """Test that a basic x86 ROM can be created"""
Simon Glass11ae93e2018-10-01 21:12:47 -06001069 self._SetupSplElf()
Simon Glass9255f3c2019-08-24 07:23:01 -06001070 data = self._DoReadFile('029_x86_rom.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07001071 self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 3) + U_BOOT_SPL_DATA +
1072 tools.get_bytes(0, 2), data)
Simon Glasse0ff8552016-11-25 20:15:53 -07001073
1074 def testPackX86RomMeNoDesc(self):
1075 """Test that an invalid Intel descriptor entry is detected"""
Simon Glass0ba4b3d2020-07-09 18:39:41 -06001076 try:
Simon Glass52b10dd2020-07-25 15:11:19 -06001077 TestFunctional._MakeInputFile('descriptor-empty.bin', b'')
Simon Glass0ba4b3d2020-07-09 18:39:41 -06001078 with self.assertRaises(ValueError) as e:
Simon Glass52b10dd2020-07-25 15:11:19 -06001079 self._DoTestFile('163_x86_rom_me_empty.dts')
Simon Glass0ba4b3d2020-07-09 18:39:41 -06001080 self.assertIn("Node '/binman/intel-descriptor': Cannot find Intel Flash Descriptor (FD) signature",
1081 str(e.exception))
1082 finally:
1083 self._SetupDescriptor()
Simon Glasse0ff8552016-11-25 20:15:53 -07001084
1085 def testPackX86RomBadDesc(self):
1086 """Test that the Intel requires a descriptor entry"""
1087 with self.assertRaises(ValueError) as e:
Simon Glass9255f3c2019-08-24 07:23:01 -06001088 self._DoTestFile('030_x86_rom_me_no_desc.dts')
Simon Glass3ab95982018-08-01 15:22:37 -06001089 self.assertIn("Node '/binman/intel-me': No offset set with "
1090 "offset-unset: should another entry provide this correct "
1091 "offset?", str(e.exception))
Simon Glasse0ff8552016-11-25 20:15:53 -07001092
1093 def testPackX86RomMe(self):
1094 """Test that an x86 ROM with an ME region can be created"""
Simon Glass9255f3c2019-08-24 07:23:01 -06001095 data = self._DoReadFile('031_x86_rom_me.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07001096 expected_desc = tools.read_file(self.TestFile('descriptor.bin'))
Simon Glassc5ac1382019-07-08 13:18:54 -06001097 if data[:0x1000] != expected_desc:
1098 self.fail('Expected descriptor binary at start of image')
Simon Glasse0ff8552016-11-25 20:15:53 -07001099 self.assertEqual(ME_DATA, data[0x1000:0x1000 + len(ME_DATA)])
1100
1101 def testPackVga(self):
1102 """Test that an image with a VGA binary can be created"""
Simon Glass9255f3c2019-08-24 07:23:01 -06001103 data = self._DoReadFile('032_intel_vga.dts')
Simon Glasse0ff8552016-11-25 20:15:53 -07001104 self.assertEqual(VGA_DATA, data[:len(VGA_DATA)])
1105
1106 def testPackStart16(self):
1107 """Test that an image with an x86 start16 region can be created"""
Simon Glass9255f3c2019-08-24 07:23:01 -06001108 data = self._DoReadFile('033_x86_start16.dts')
Simon Glasse0ff8552016-11-25 20:15:53 -07001109 self.assertEqual(X86_START16_DATA, data[:len(X86_START16_DATA)])
1110
Jagdish Gediya9d368f32018-09-03 21:35:08 +05301111 def testPackPowerpcMpc85xxBootpgResetvec(self):
1112 """Test that an image with powerpc-mpc85xx-bootpg-resetvec can be
1113 created"""
Simon Glassdfdd2b62019-08-24 07:23:02 -06001114 data = self._DoReadFile('150_powerpc_mpc85xx_bootpg_resetvec.dts')
Jagdish Gediya9d368f32018-09-03 21:35:08 +05301115 self.assertEqual(PPC_MPC85XX_BR_DATA, data[:len(PPC_MPC85XX_BR_DATA)])
1116
Simon Glass736bb0a2018-07-06 10:27:17 -06001117 def _RunMicrocodeTest(self, dts_fname, nodtb_data, ucode_second=False):
Simon Glassadc57012018-07-06 10:27:16 -06001118 """Handle running a test for insertion of microcode
1119
1120 Args:
1121 dts_fname: Name of test .dts file
1122 nodtb_data: Data that we expect in the first section
Simon Glass736bb0a2018-07-06 10:27:17 -06001123 ucode_second: True if the microsecond entry is second instead of
1124 third
Simon Glassadc57012018-07-06 10:27:16 -06001125
1126 Returns:
1127 Tuple:
1128 Contents of first region (U-Boot or SPL)
Simon Glass3ab95982018-08-01 15:22:37 -06001129 Offset and size components of microcode pointer, as inserted
Simon Glassadc57012018-07-06 10:27:16 -06001130 in the above (two 4-byte words)
1131 """
Simon Glass6b187df2017-11-12 21:52:27 -07001132 data = self._DoReadFile(dts_fname, True)
Simon Glasse0ff8552016-11-25 20:15:53 -07001133
1134 # Now check the device tree has no microcode
Simon Glass736bb0a2018-07-06 10:27:17 -06001135 if ucode_second:
1136 ucode_content = data[len(nodtb_data):]
1137 ucode_pos = len(nodtb_data)
1138 dtb_with_ucode = ucode_content[16:]
1139 fdt_len = self.GetFdtLen(dtb_with_ucode)
1140 else:
1141 dtb_with_ucode = data[len(nodtb_data):]
1142 fdt_len = self.GetFdtLen(dtb_with_ucode)
1143 ucode_content = dtb_with_ucode[fdt_len:]
1144 ucode_pos = len(nodtb_data) + fdt_len
Simon Glassc1aa66e2022-01-29 14:14:04 -07001145 fname = tools.get_output_filename('test.dtb')
Simon Glasse0ff8552016-11-25 20:15:53 -07001146 with open(fname, 'wb') as fd:
Simon Glassadc57012018-07-06 10:27:16 -06001147 fd.write(dtb_with_ucode)
Simon Glassec3f3782017-05-27 07:38:29 -06001148 dtb = fdt.FdtScan(fname)
1149 ucode = dtb.GetNode('/microcode')
Simon Glasse0ff8552016-11-25 20:15:53 -07001150 self.assertTrue(ucode)
1151 for node in ucode.subnodes:
1152 self.assertFalse(node.props.get('data'))
1153
Simon Glasse0ff8552016-11-25 20:15:53 -07001154 # Check that the microcode appears immediately after the Fdt
1155 # This matches the concatenation of the data properties in
Simon Glass87722132017-11-12 21:52:26 -07001156 # the /microcode/update@xxx nodes in 34_x86_ucode.dts.
Simon Glasse0ff8552016-11-25 20:15:53 -07001157 ucode_data = struct.pack('>4L', 0x12345678, 0x12345679, 0xabcd0000,
1158 0x78235609)
Simon Glassadc57012018-07-06 10:27:16 -06001159 self.assertEqual(ucode_data, ucode_content[:len(ucode_data)])
Simon Glasse0ff8552016-11-25 20:15:53 -07001160
1161 # Check that the microcode pointer was inserted. It should match the
Simon Glass3ab95982018-08-01 15:22:37 -06001162 # expected offset and size
Simon Glasse0ff8552016-11-25 20:15:53 -07001163 pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
1164 len(ucode_data))
Simon Glass736bb0a2018-07-06 10:27:17 -06001165 u_boot = data[:len(nodtb_data)]
1166 return u_boot, pos_and_size
Simon Glass6b187df2017-11-12 21:52:27 -07001167
1168 def testPackUbootMicrocode(self):
1169 """Test that x86 microcode can be handled correctly
1170
1171 We expect to see the following in the image, in order:
1172 u-boot-nodtb.bin with a microcode pointer inserted at the correct
1173 place
1174 u-boot.dtb with the microcode removed
1175 the microcode
1176 """
Simon Glass741f2d62018-10-01 12:22:30 -06001177 first, pos_and_size = self._RunMicrocodeTest('034_x86_ucode.dts',
Simon Glass6b187df2017-11-12 21:52:27 -07001178 U_BOOT_NODTB_DATA)
Simon Glassc6c10e72019-05-17 22:00:46 -06001179 self.assertEqual(b'nodtb with microcode' + pos_and_size +
1180 b' somewhere in here', first)
Simon Glasse0ff8552016-11-25 20:15:53 -07001181
Simon Glass160a7662017-05-27 07:38:26 -06001182 def _RunPackUbootSingleMicrocode(self):
Simon Glasse0ff8552016-11-25 20:15:53 -07001183 """Test that x86 microcode can be handled correctly
1184
1185 We expect to see the following in the image, in order:
1186 u-boot-nodtb.bin with a microcode pointer inserted at the correct
1187 place
1188 u-boot.dtb with the microcode
1189 an empty microcode region
1190 """
1191 # We need the libfdt library to run this test since only that allows
1192 # finding the offset of a property. This is required by
1193 # Entry_u_boot_dtb_with_ucode.ObtainContents().
Simon Glass741f2d62018-10-01 12:22:30 -06001194 data = self._DoReadFile('035_x86_single_ucode.dts', True)
Simon Glasse0ff8552016-11-25 20:15:53 -07001195
1196 second = data[len(U_BOOT_NODTB_DATA):]
1197
1198 fdt_len = self.GetFdtLen(second)
1199 third = second[fdt_len:]
1200 second = second[:fdt_len]
1201
Simon Glass160a7662017-05-27 07:38:26 -06001202 ucode_data = struct.pack('>2L', 0x12345678, 0x12345679)
1203 self.assertIn(ucode_data, second)
1204 ucode_pos = second.find(ucode_data) + len(U_BOOT_NODTB_DATA)
Simon Glasse0ff8552016-11-25 20:15:53 -07001205
Simon Glass160a7662017-05-27 07:38:26 -06001206 # Check that the microcode pointer was inserted. It should match the
Simon Glass3ab95982018-08-01 15:22:37 -06001207 # expected offset and size
Simon Glass160a7662017-05-27 07:38:26 -06001208 pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
1209 len(ucode_data))
1210 first = data[:len(U_BOOT_NODTB_DATA)]
Simon Glassc6c10e72019-05-17 22:00:46 -06001211 self.assertEqual(b'nodtb with microcode' + pos_and_size +
1212 b' somewhere in here', first)
Simon Glassc49deb82016-11-25 20:15:54 -07001213
Simon Glass75db0862016-11-25 20:15:55 -07001214 def testPackUbootSingleMicrocode(self):
1215 """Test that x86 microcode can be handled correctly with fdt_normal.
1216 """
Simon Glass160a7662017-05-27 07:38:26 -06001217 self._RunPackUbootSingleMicrocode()
Simon Glass75db0862016-11-25 20:15:55 -07001218
Simon Glassc49deb82016-11-25 20:15:54 -07001219 def testUBootImg(self):
1220 """Test that u-boot.img can be put in a file"""
Simon Glass741f2d62018-10-01 12:22:30 -06001221 data = self._DoReadFile('036_u_boot_img.dts')
Simon Glassc49deb82016-11-25 20:15:54 -07001222 self.assertEqual(U_BOOT_IMG_DATA, data)
Simon Glass75db0862016-11-25 20:15:55 -07001223
1224 def testNoMicrocode(self):
1225 """Test that a missing microcode region is detected"""
1226 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001227 self._DoReadFile('037_x86_no_ucode.dts', True)
Simon Glass75db0862016-11-25 20:15:55 -07001228 self.assertIn("Node '/binman/u-boot-dtb-with-ucode': No /microcode "
1229 "node found in ", str(e.exception))
1230
1231 def testMicrocodeWithoutNode(self):
1232 """Test that a missing u-boot-dtb-with-ucode node is detected"""
1233 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001234 self._DoReadFile('038_x86_ucode_missing_node.dts', True)
Simon Glass75db0862016-11-25 20:15:55 -07001235 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
1236 "microcode region u-boot-dtb-with-ucode", str(e.exception))
1237
1238 def testMicrocodeWithoutNode2(self):
1239 """Test that a missing u-boot-ucode node is detected"""
1240 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001241 self._DoReadFile('039_x86_ucode_missing_node2.dts', True)
Simon Glass75db0862016-11-25 20:15:55 -07001242 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
1243 "microcode region u-boot-ucode", str(e.exception))
1244
1245 def testMicrocodeWithoutPtrInElf(self):
1246 """Test that a U-Boot binary without the microcode symbol is detected"""
1247 # ELF file without a '_dt_ucode_base_size' symbol
Simon Glass75db0862016-11-25 20:15:55 -07001248 try:
Simon Glassbccd91d2019-08-24 07:22:55 -06001249 TestFunctional._MakeInputFile('u-boot',
Simon Glassc1aa66e2022-01-29 14:14:04 -07001250 tools.read_file(self.ElfTestFile('u_boot_no_ucode_ptr')))
Simon Glass75db0862016-11-25 20:15:55 -07001251
1252 with self.assertRaises(ValueError) as e:
Simon Glass160a7662017-05-27 07:38:26 -06001253 self._RunPackUbootSingleMicrocode()
Simon Glass75db0862016-11-25 20:15:55 -07001254 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot locate "
1255 "_dt_ucode_base_size symbol in u-boot", str(e.exception))
1256
1257 finally:
1258 # Put the original file back
Simon Glassf514d8f2019-08-24 07:22:54 -06001259 TestFunctional._MakeInputFile('u-boot',
Simon Glassc1aa66e2022-01-29 14:14:04 -07001260 tools.read_file(self.ElfTestFile('u_boot_ucode_ptr')))
Simon Glass75db0862016-11-25 20:15:55 -07001261
1262 def testMicrocodeNotInImage(self):
1263 """Test that microcode must be placed within the image"""
1264 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001265 self._DoReadFile('040_x86_ucode_not_in_image.dts', True)
Simon Glass75db0862016-11-25 20:15:55 -07001266 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Microcode "
1267 "pointer _dt_ucode_base_size at fffffe14 is outside the "
Simon Glass25ac0e62018-06-01 09:38:14 -06001268 "section ranging from 00000000 to 0000002e", str(e.exception))
Simon Glass75db0862016-11-25 20:15:55 -07001269
1270 def testWithoutMicrocode(self):
1271 """Test that we can cope with an image without microcode (e.g. qemu)"""
Simon Glassbccd91d2019-08-24 07:22:55 -06001272 TestFunctional._MakeInputFile('u-boot',
Simon Glassc1aa66e2022-01-29 14:14:04 -07001273 tools.read_file(self.ElfTestFile('u_boot_no_ucode_ptr')))
Simon Glass741f2d62018-10-01 12:22:30 -06001274 data, dtb, _, _ = self._DoReadFileDtb('044_x86_optional_ucode.dts', True)
Simon Glass75db0862016-11-25 20:15:55 -07001275
1276 # Now check the device tree has no microcode
1277 self.assertEqual(U_BOOT_NODTB_DATA, data[:len(U_BOOT_NODTB_DATA)])
1278 second = data[len(U_BOOT_NODTB_DATA):]
1279
1280 fdt_len = self.GetFdtLen(second)
1281 self.assertEqual(dtb, second[:fdt_len])
1282
1283 used_len = len(U_BOOT_NODTB_DATA) + fdt_len
1284 third = data[used_len:]
Simon Glassc1aa66e2022-01-29 14:14:04 -07001285 self.assertEqual(tools.get_bytes(0, 0x200 - used_len), third)
Simon Glass75db0862016-11-25 20:15:55 -07001286
1287 def testUnknownPosSize(self):
1288 """Test that microcode must be placed within the image"""
1289 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001290 self._DoReadFile('041_unknown_pos_size.dts', True)
Simon Glass3ab95982018-08-01 15:22:37 -06001291 self.assertIn("Section '/binman': Unable to set offset/size for unknown "
Simon Glass75db0862016-11-25 20:15:55 -07001292 "entry 'invalid-entry'", str(e.exception))
Simon Glassda229092016-11-25 20:15:56 -07001293
1294 def testPackFsp(self):
1295 """Test that an image with a FSP binary can be created"""
Simon Glass9255f3c2019-08-24 07:23:01 -06001296 data = self._DoReadFile('042_intel_fsp.dts')
Simon Glassda229092016-11-25 20:15:56 -07001297 self.assertEqual(FSP_DATA, data[:len(FSP_DATA)])
1298
1299 def testPackCmc(self):
Bin Meng59ea8c22017-08-15 22:41:54 -07001300 """Test that an image with a CMC binary can be created"""
Simon Glass9255f3c2019-08-24 07:23:01 -06001301 data = self._DoReadFile('043_intel_cmc.dts')
Simon Glassda229092016-11-25 20:15:56 -07001302 self.assertEqual(CMC_DATA, data[:len(CMC_DATA)])
Bin Meng59ea8c22017-08-15 22:41:54 -07001303
1304 def testPackVbt(self):
1305 """Test that an image with a VBT binary can be created"""
Simon Glass9255f3c2019-08-24 07:23:01 -06001306 data = self._DoReadFile('046_intel_vbt.dts')
Bin Meng59ea8c22017-08-15 22:41:54 -07001307 self.assertEqual(VBT_DATA, data[:len(VBT_DATA)])
Simon Glass9fc60b42017-11-12 21:52:22 -07001308
Simon Glass56509842017-11-12 21:52:25 -07001309 def testSplBssPad(self):
1310 """Test that we can pad SPL's BSS with zeros"""
Simon Glass6b187df2017-11-12 21:52:27 -07001311 # ELF file with a '__bss_size' symbol
Simon Glass11ae93e2018-10-01 21:12:47 -06001312 self._SetupSplElf()
Simon Glass741f2d62018-10-01 12:22:30 -06001313 data = self._DoReadFile('047_spl_bss_pad.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07001314 self.assertEqual(U_BOOT_SPL_DATA + tools.get_bytes(0, 10) + U_BOOT_DATA,
Simon Glasse6d85ff2019-05-14 15:53:47 -06001315 data)
Simon Glass56509842017-11-12 21:52:25 -07001316
Simon Glass86af5112018-10-01 21:12:42 -06001317 def testSplBssPadMissing(self):
1318 """Test that a missing symbol is detected"""
Simon Glass11ae93e2018-10-01 21:12:47 -06001319 self._SetupSplElf('u_boot_ucode_ptr')
Simon Glassb50e5612017-11-13 18:54:54 -07001320 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001321 self._DoReadFile('047_spl_bss_pad.dts')
Simon Glassb50e5612017-11-13 18:54:54 -07001322 self.assertIn('Expected __bss_size symbol in spl/u-boot-spl',
1323 str(e.exception))
1324
Simon Glass87722132017-11-12 21:52:26 -07001325 def testPackStart16Spl(self):
Simon Glass35b384c2018-09-14 04:57:10 -06001326 """Test that an image with an x86 start16 SPL region can be created"""
Simon Glass9255f3c2019-08-24 07:23:01 -06001327 data = self._DoReadFile('048_x86_start16_spl.dts')
Simon Glass87722132017-11-12 21:52:26 -07001328 self.assertEqual(X86_START16_SPL_DATA, data[:len(X86_START16_SPL_DATA)])
1329
Simon Glass736bb0a2018-07-06 10:27:17 -06001330 def _PackUbootSplMicrocode(self, dts, ucode_second=False):
1331 """Helper function for microcode tests
Simon Glass6b187df2017-11-12 21:52:27 -07001332
1333 We expect to see the following in the image, in order:
1334 u-boot-spl-nodtb.bin with a microcode pointer inserted at the
1335 correct place
1336 u-boot.dtb with the microcode removed
1337 the microcode
Simon Glass736bb0a2018-07-06 10:27:17 -06001338
1339 Args:
1340 dts: Device tree file to use for test
1341 ucode_second: True if the microsecond entry is second instead of
1342 third
Simon Glass6b187df2017-11-12 21:52:27 -07001343 """
Simon Glass11ae93e2018-10-01 21:12:47 -06001344 self._SetupSplElf('u_boot_ucode_ptr')
Simon Glass736bb0a2018-07-06 10:27:17 -06001345 first, pos_and_size = self._RunMicrocodeTest(dts, U_BOOT_SPL_NODTB_DATA,
1346 ucode_second=ucode_second)
Simon Glassc6c10e72019-05-17 22:00:46 -06001347 self.assertEqual(b'splnodtb with microc' + pos_and_size +
1348 b'ter somewhere in here', first)
Simon Glass6b187df2017-11-12 21:52:27 -07001349
Simon Glass736bb0a2018-07-06 10:27:17 -06001350 def testPackUbootSplMicrocode(self):
1351 """Test that x86 microcode can be handled correctly in SPL"""
Simon Glass741f2d62018-10-01 12:22:30 -06001352 self._PackUbootSplMicrocode('049_x86_ucode_spl.dts')
Simon Glass736bb0a2018-07-06 10:27:17 -06001353
1354 def testPackUbootSplMicrocodeReorder(self):
1355 """Test that order doesn't matter for microcode entries
1356
1357 This is the same as testPackUbootSplMicrocode but when we process the
1358 u-boot-ucode entry we have not yet seen the u-boot-dtb-with-ucode
1359 entry, so we reply on binman to try later.
1360 """
Simon Glass741f2d62018-10-01 12:22:30 -06001361 self._PackUbootSplMicrocode('058_x86_ucode_spl_needs_retry.dts',
Simon Glass736bb0a2018-07-06 10:27:17 -06001362 ucode_second=True)
1363
Simon Glassca4f4ff2017-11-12 21:52:28 -07001364 def testPackMrc(self):
1365 """Test that an image with an MRC binary can be created"""
Simon Glass741f2d62018-10-01 12:22:30 -06001366 data = self._DoReadFile('050_intel_mrc.dts')
Simon Glassca4f4ff2017-11-12 21:52:28 -07001367 self.assertEqual(MRC_DATA, data[:len(MRC_DATA)])
1368
Simon Glass47419ea2017-11-13 18:54:55 -07001369 def testSplDtb(self):
1370 """Test that an image with spl/u-boot-spl.dtb can be created"""
Simon Glass741f2d62018-10-01 12:22:30 -06001371 data = self._DoReadFile('051_u_boot_spl_dtb.dts')
Simon Glass47419ea2017-11-13 18:54:55 -07001372 self.assertEqual(U_BOOT_SPL_DTB_DATA, data[:len(U_BOOT_SPL_DTB_DATA)])
1373
Simon Glass4e6fdbe2017-11-13 18:54:56 -07001374 def testSplNoDtb(self):
1375 """Test that an image with spl/u-boot-spl-nodtb.bin can be created"""
Simon Glass0fe44dc2021-04-25 08:39:32 +12001376 self._SetupSplElf()
Simon Glass741f2d62018-10-01 12:22:30 -06001377 data = self._DoReadFile('052_u_boot_spl_nodtb.dts')
Simon Glass4e6fdbe2017-11-13 18:54:56 -07001378 self.assertEqual(U_BOOT_SPL_NODTB_DATA, data[:len(U_BOOT_SPL_NODTB_DATA)])
1379
Simon Glass3d433382021-03-21 18:24:30 +13001380 def checkSymbols(self, dts, base_data, u_boot_offset, entry_args=None,
1381 use_expanded=False):
Simon Glassf5898822021-03-18 20:24:56 +13001382 """Check the image contains the expected symbol values
1383
1384 Args:
1385 dts: Device tree file to use for test
1386 base_data: Data before and after 'u-boot' section
1387 u_boot_offset: Offset of 'u-boot' section in image
Simon Glass3d433382021-03-21 18:24:30 +13001388 entry_args: Dict of entry args to supply to binman
1389 key: arg name
1390 value: value of that arg
1391 use_expanded: True to use expanded entries where available, e.g.
1392 'u-boot-expanded' instead of 'u-boot'
Simon Glassf5898822021-03-18 20:24:56 +13001393 """
Simon Glass1542c8b2019-08-24 07:22:56 -06001394 elf_fname = self.ElfTestFile('u_boot_binman_syms')
Simon Glass19790632017-11-13 18:55:01 -07001395 syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
1396 addr = elf.GetSymbolAddress(elf_fname, '__image_copy_start')
Simon Glassf5898822021-03-18 20:24:56 +13001397 self.assertEqual(syms['_binman_u_boot_spl_any_prop_offset'].address,
1398 addr)
Simon Glass19790632017-11-13 18:55:01 -07001399
Simon Glass11ae93e2018-10-01 21:12:47 -06001400 self._SetupSplElf('u_boot_binman_syms')
Simon Glass3d433382021-03-21 18:24:30 +13001401 data = self._DoReadFileDtb(dts, entry_args=entry_args,
1402 use_expanded=use_expanded)[0]
Simon Glassf5898822021-03-18 20:24:56 +13001403 # The image should contain the symbols from u_boot_binman_syms.c
1404 # Note that image_pos is adjusted by the base address of the image,
1405 # which is 0x10 in our test image
1406 sym_values = struct.pack('<LQLL', 0x00,
1407 u_boot_offset + len(U_BOOT_DATA),
1408 0x10 + u_boot_offset, 0x04)
1409 expected = (sym_values + base_data[20:] +
Simon Glassc1aa66e2022-01-29 14:14:04 -07001410 tools.get_bytes(0xff, 1) + U_BOOT_DATA + sym_values +
Simon Glassf5898822021-03-18 20:24:56 +13001411 base_data[20:])
Simon Glass19790632017-11-13 18:55:01 -07001412 self.assertEqual(expected, data)
1413
Simon Glassf5898822021-03-18 20:24:56 +13001414 def testSymbols(self):
1415 """Test binman can assign symbols embedded in U-Boot"""
1416 self.checkSymbols('053_symbols.dts', U_BOOT_SPL_DATA, 0x18)
1417
1418 def testSymbolsNoDtb(self):
1419 """Test binman can assign symbols embedded in U-Boot SPL"""
Simon Glasse9e0db82021-03-21 18:24:29 +13001420 self.checkSymbols('196_symbols_nodtb.dts',
Simon Glassf5898822021-03-18 20:24:56 +13001421 U_BOOT_SPL_NODTB_DATA + U_BOOT_SPL_DTB_DATA,
1422 0x38)
1423
Simon Glassdd57c132018-06-01 09:38:11 -06001424 def testPackUnitAddress(self):
1425 """Test that we support multiple binaries with the same name"""
Simon Glass741f2d62018-10-01 12:22:30 -06001426 data = self._DoReadFile('054_unit_address.dts')
Simon Glassdd57c132018-06-01 09:38:11 -06001427 self.assertEqual(U_BOOT_DATA + U_BOOT_DATA, data)
1428
Simon Glass18546952018-06-01 09:38:16 -06001429 def testSections(self):
1430 """Basic test of sections"""
Simon Glass741f2d62018-10-01 12:22:30 -06001431 data = self._DoReadFile('055_sections.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07001432 expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) +
1433 U_BOOT_DATA + tools.get_bytes(ord('a'), 12) +
1434 U_BOOT_DATA + tools.get_bytes(ord('&'), 4))
Simon Glass18546952018-06-01 09:38:16 -06001435 self.assertEqual(expected, data)
Simon Glass9fc60b42017-11-12 21:52:22 -07001436
Simon Glass3b0c3822018-06-01 09:38:20 -06001437 def testMap(self):
1438 """Tests outputting a map of the images"""
Simon Glass741f2d62018-10-01 12:22:30 -06001439 _, _, map_data, _ = self._DoReadFileDtb('055_sections.dts', map=True)
Simon Glass1be70d22018-07-17 13:25:49 -06001440 self.assertEqual('''ImagePos Offset Size Name
144100000000 00000000 00000028 main-section
144200000000 00000000 00000010 section@0
144300000000 00000000 00000004 u-boot
144400000010 00000010 00000010 section@1
144500000010 00000000 00000004 u-boot
144600000020 00000020 00000004 section@2
144700000020 00000000 00000004 u-boot
Simon Glass3b0c3822018-06-01 09:38:20 -06001448''', map_data)
1449
Simon Glassc8d48ef2018-06-01 09:38:21 -06001450 def testNamePrefix(self):
1451 """Tests that name prefixes are used"""
Simon Glass741f2d62018-10-01 12:22:30 -06001452 _, _, map_data, _ = self._DoReadFileDtb('056_name_prefix.dts', map=True)
Simon Glass1be70d22018-07-17 13:25:49 -06001453 self.assertEqual('''ImagePos Offset Size Name
145400000000 00000000 00000028 main-section
145500000000 00000000 00000010 section@0
145600000000 00000000 00000004 ro-u-boot
145700000010 00000010 00000010 section@1
145800000010 00000000 00000004 rw-u-boot
Simon Glassc8d48ef2018-06-01 09:38:21 -06001459''', map_data)
1460
Simon Glass736bb0a2018-07-06 10:27:17 -06001461 def testUnknownContents(self):
1462 """Test that obtaining the contents works as expected"""
1463 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001464 self._DoReadFile('057_unknown_contents.dts', True)
Simon Glass8beb11e2019-07-08 14:25:47 -06001465 self.assertIn("Image '/binman': Internal error: Could not complete "
Simon Glass16287932020-04-17 18:09:03 -06001466 "processing of contents: remaining ["
1467 "<binman.etype._testing.Entry__testing ", str(e.exception))
Simon Glass736bb0a2018-07-06 10:27:17 -06001468
Simon Glass5c890232018-07-06 10:27:19 -06001469 def testBadChangeSize(self):
1470 """Test that trying to change the size of an entry fails"""
Simon Glassc52c9e72019-07-08 14:25:37 -06001471 try:
1472 state.SetAllowEntryExpansion(False)
1473 with self.assertRaises(ValueError) as e:
1474 self._DoReadFile('059_change_size.dts', True)
Simon Glass79d3c582019-07-20 12:23:57 -06001475 self.assertIn("Node '/binman/_testing': Cannot update entry size from 2 to 3",
Simon Glassc52c9e72019-07-08 14:25:37 -06001476 str(e.exception))
1477 finally:
1478 state.SetAllowEntryExpansion(True)
Simon Glass5c890232018-07-06 10:27:19 -06001479
Simon Glass16b8d6b2018-07-06 10:27:42 -06001480 def testUpdateFdt(self):
Simon Glass3ab95982018-08-01 15:22:37 -06001481 """Test that we can update the device tree with offset/size info"""
Simon Glass741f2d62018-10-01 12:22:30 -06001482 _, _, _, out_dtb_fname = self._DoReadFileDtb('060_fdt_update.dts',
Simon Glass16b8d6b2018-07-06 10:27:42 -06001483 update_dtb=True)
Simon Glasscee02e62018-07-17 13:25:52 -06001484 dtb = fdt.Fdt(out_dtb_fname)
1485 dtb.Scan()
Simon Glass12bb1a92019-07-20 12:23:51 -06001486 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
Simon Glass16b8d6b2018-07-06 10:27:42 -06001487 self.assertEqual({
Simon Glassdbf6be92018-08-01 15:22:42 -06001488 'image-pos': 0,
Simon Glass8122f392018-07-17 13:25:28 -06001489 'offset': 0,
Simon Glass3ab95982018-08-01 15:22:37 -06001490 '_testing:offset': 32,
Simon Glass79d3c582019-07-20 12:23:57 -06001491 '_testing:size': 2,
Simon Glassdbf6be92018-08-01 15:22:42 -06001492 '_testing:image-pos': 32,
Simon Glass3ab95982018-08-01 15:22:37 -06001493 'section@0/u-boot:offset': 0,
Simon Glass16b8d6b2018-07-06 10:27:42 -06001494 'section@0/u-boot:size': len(U_BOOT_DATA),
Simon Glassdbf6be92018-08-01 15:22:42 -06001495 'section@0/u-boot:image-pos': 0,
Simon Glass3ab95982018-08-01 15:22:37 -06001496 'section@0:offset': 0,
Simon Glass16b8d6b2018-07-06 10:27:42 -06001497 'section@0:size': 16,
Simon Glassdbf6be92018-08-01 15:22:42 -06001498 'section@0:image-pos': 0,
Simon Glass16b8d6b2018-07-06 10:27:42 -06001499
Simon Glass3ab95982018-08-01 15:22:37 -06001500 'section@1/u-boot:offset': 0,
Simon Glass16b8d6b2018-07-06 10:27:42 -06001501 'section@1/u-boot:size': len(U_BOOT_DATA),
Simon Glassdbf6be92018-08-01 15:22:42 -06001502 'section@1/u-boot:image-pos': 16,
Simon Glass3ab95982018-08-01 15:22:37 -06001503 'section@1:offset': 16,
Simon Glass16b8d6b2018-07-06 10:27:42 -06001504 'section@1:size': 16,
Simon Glassdbf6be92018-08-01 15:22:42 -06001505 'section@1:image-pos': 16,
Simon Glass16b8d6b2018-07-06 10:27:42 -06001506 'size': 40
1507 }, props)
1508
1509 def testUpdateFdtBad(self):
1510 """Test that we detect when ProcessFdt never completes"""
1511 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001512 self._DoReadFileDtb('061_fdt_update_bad.dts', update_dtb=True)
Simon Glass16b8d6b2018-07-06 10:27:42 -06001513 self.assertIn('Could not complete processing of Fdt: remaining '
Simon Glass16287932020-04-17 18:09:03 -06001514 '[<binman.etype._testing.Entry__testing',
1515 str(e.exception))
Simon Glass5c890232018-07-06 10:27:19 -06001516
Simon Glass53af22a2018-07-17 13:25:32 -06001517 def testEntryArgs(self):
1518 """Test passing arguments to entries from the command line"""
1519 entry_args = {
1520 'test-str-arg': 'test1',
1521 'test-int-arg': '456',
1522 }
Simon Glass741f2d62018-10-01 12:22:30 -06001523 self._DoReadFileDtb('062_entry_args.dts', entry_args=entry_args)
Simon Glass53af22a2018-07-17 13:25:32 -06001524 self.assertIn('image', control.images)
1525 entry = control.images['image'].GetEntries()['_testing']
1526 self.assertEqual('test0', entry.test_str_fdt)
1527 self.assertEqual('test1', entry.test_str_arg)
1528 self.assertEqual(123, entry.test_int_fdt)
1529 self.assertEqual(456, entry.test_int_arg)
1530
1531 def testEntryArgsMissing(self):
1532 """Test missing arguments and properties"""
1533 entry_args = {
1534 'test-int-arg': '456',
1535 }
Simon Glass741f2d62018-10-01 12:22:30 -06001536 self._DoReadFileDtb('063_entry_args_missing.dts', entry_args=entry_args)
Simon Glass53af22a2018-07-17 13:25:32 -06001537 entry = control.images['image'].GetEntries()['_testing']
1538 self.assertEqual('test0', entry.test_str_fdt)
1539 self.assertEqual(None, entry.test_str_arg)
1540 self.assertEqual(None, entry.test_int_fdt)
1541 self.assertEqual(456, entry.test_int_arg)
1542
1543 def testEntryArgsRequired(self):
1544 """Test missing arguments and properties"""
1545 entry_args = {
1546 'test-int-arg': '456',
1547 }
1548 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001549 self._DoReadFileDtb('064_entry_args_required.dts')
Simon Glass3decfa32020-09-01 05:13:54 -06001550 self.assertIn("Node '/binman/_testing': "
1551 'Missing required properties/entry args: test-str-arg, '
1552 'test-int-fdt, test-int-arg',
Simon Glass53af22a2018-07-17 13:25:32 -06001553 str(e.exception))
1554
1555 def testEntryArgsInvalidFormat(self):
1556 """Test that an invalid entry-argument format is detected"""
Simon Glass53cd5d92019-07-08 14:25:29 -06001557 args = ['build', '-d', self.TestFile('064_entry_args_required.dts'),
1558 '-ano-value']
Simon Glass53af22a2018-07-17 13:25:32 -06001559 with self.assertRaises(ValueError) as e:
1560 self._DoBinman(*args)
1561 self.assertIn("Invalid entry arguemnt 'no-value'", str(e.exception))
1562
1563 def testEntryArgsInvalidInteger(self):
1564 """Test that an invalid entry-argument integer is detected"""
1565 entry_args = {
1566 'test-int-arg': 'abc',
1567 }
1568 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001569 self._DoReadFileDtb('062_entry_args.dts', entry_args=entry_args)
Simon Glass53af22a2018-07-17 13:25:32 -06001570 self.assertIn("Node '/binman/_testing': Cannot convert entry arg "
1571 "'test-int-arg' (value 'abc') to integer",
1572 str(e.exception))
1573
1574 def testEntryArgsInvalidDatatype(self):
1575 """Test that an invalid entry-argument datatype is detected
1576
1577 This test could be written in entry_test.py except that it needs
1578 access to control.entry_args, which seems more than that module should
1579 be able to see.
1580 """
1581 entry_args = {
1582 'test-bad-datatype-arg': '12',
1583 }
1584 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001585 self._DoReadFileDtb('065_entry_args_unknown_datatype.dts',
Simon Glass53af22a2018-07-17 13:25:32 -06001586 entry_args=entry_args)
1587 self.assertIn('GetArg() internal error: Unknown data type ',
1588 str(e.exception))
1589
Simon Glassbb748372018-07-17 13:25:33 -06001590 def testText(self):
1591 """Test for a text entry type"""
1592 entry_args = {
1593 'test-id': TEXT_DATA,
1594 'test-id2': TEXT_DATA2,
1595 'test-id3': TEXT_DATA3,
1596 }
Simon Glass741f2d62018-10-01 12:22:30 -06001597 data, _, _, _ = self._DoReadFileDtb('066_text.dts',
Simon Glassbb748372018-07-17 13:25:33 -06001598 entry_args=entry_args)
Simon Glassc1aa66e2022-01-29 14:14:04 -07001599 expected = (tools.to_bytes(TEXT_DATA) +
1600 tools.get_bytes(0, 8 - len(TEXT_DATA)) +
1601 tools.to_bytes(TEXT_DATA2) + tools.to_bytes(TEXT_DATA3) +
Simon Glassaa88b502019-07-08 13:18:40 -06001602 b'some text' + b'more text')
Simon Glassbb748372018-07-17 13:25:33 -06001603 self.assertEqual(expected, data)
1604
Simon Glassfd8d1f72018-07-17 13:25:36 -06001605 def testEntryDocs(self):
1606 """Test for creation of entry documentation"""
1607 with test_util.capture_sys_output() as (stdout, stderr):
Simon Glass87d43322020-08-05 13:27:46 -06001608 control.WriteEntryDocs(control.GetEntryModules())
Simon Glassfd8d1f72018-07-17 13:25:36 -06001609 self.assertTrue(len(stdout.getvalue()) > 0)
1610
1611 def testEntryDocsMissing(self):
1612 """Test handling of missing entry documentation"""
1613 with self.assertRaises(ValueError) as e:
1614 with test_util.capture_sys_output() as (stdout, stderr):
Simon Glass87d43322020-08-05 13:27:46 -06001615 control.WriteEntryDocs(control.GetEntryModules(), 'u_boot')
Simon Glassfd8d1f72018-07-17 13:25:36 -06001616 self.assertIn('Documentation is missing for modules: u_boot',
1617 str(e.exception))
1618
Simon Glass11e36cc2018-07-17 13:25:38 -06001619 def testFmap(self):
1620 """Basic test of generation of a flashrom fmap"""
Simon Glass741f2d62018-10-01 12:22:30 -06001621 data = self._DoReadFile('067_fmap.dts')
Simon Glass11e36cc2018-07-17 13:25:38 -06001622 fhdr, fentries = fmap_util.DecodeFmap(data[32:])
Simon Glassc1aa66e2022-01-29 14:14:04 -07001623 expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) +
1624 U_BOOT_DATA + tools.get_bytes(ord('a'), 12))
Simon Glass11e36cc2018-07-17 13:25:38 -06001625 self.assertEqual(expected, data[:32])
Simon Glassc6c10e72019-05-17 22:00:46 -06001626 self.assertEqual(b'__FMAP__', fhdr.signature)
Simon Glass11e36cc2018-07-17 13:25:38 -06001627 self.assertEqual(1, fhdr.ver_major)
1628 self.assertEqual(0, fhdr.ver_minor)
1629 self.assertEqual(0, fhdr.base)
Simon Glass17365752021-04-03 11:05:10 +13001630 expect_size = fmap_util.FMAP_HEADER_LEN + fmap_util.FMAP_AREA_LEN * 5
Simon Glassc7722e82021-04-03 11:05:09 +13001631 self.assertEqual(16 + 16 + expect_size, fhdr.image_size)
Simon Glassc6c10e72019-05-17 22:00:46 -06001632 self.assertEqual(b'FMAP', fhdr.name)
Simon Glass17365752021-04-03 11:05:10 +13001633 self.assertEqual(5, fhdr.nareas)
Simon Glassc7722e82021-04-03 11:05:09 +13001634 fiter = iter(fentries)
Simon Glass11e36cc2018-07-17 13:25:38 -06001635
Simon Glassc7722e82021-04-03 11:05:09 +13001636 fentry = next(fiter)
Simon Glass17365752021-04-03 11:05:10 +13001637 self.assertEqual(b'SECTION0', fentry.name)
1638 self.assertEqual(0, fentry.offset)
1639 self.assertEqual(16, fentry.size)
1640 self.assertEqual(0, fentry.flags)
1641
1642 fentry = next(fiter)
Simon Glassc7722e82021-04-03 11:05:09 +13001643 self.assertEqual(b'RO_U_BOOT', fentry.name)
1644 self.assertEqual(0, fentry.offset)
1645 self.assertEqual(4, fentry.size)
1646 self.assertEqual(0, fentry.flags)
Simon Glass11e36cc2018-07-17 13:25:38 -06001647
Simon Glassc7722e82021-04-03 11:05:09 +13001648 fentry = next(fiter)
Simon Glass17365752021-04-03 11:05:10 +13001649 self.assertEqual(b'SECTION1', fentry.name)
1650 self.assertEqual(16, fentry.offset)
1651 self.assertEqual(16, fentry.size)
1652 self.assertEqual(0, fentry.flags)
1653
1654 fentry = next(fiter)
Simon Glassc7722e82021-04-03 11:05:09 +13001655 self.assertEqual(b'RW_U_BOOT', fentry.name)
1656 self.assertEqual(16, fentry.offset)
1657 self.assertEqual(4, fentry.size)
1658 self.assertEqual(0, fentry.flags)
Simon Glass11e36cc2018-07-17 13:25:38 -06001659
Simon Glassc7722e82021-04-03 11:05:09 +13001660 fentry = next(fiter)
1661 self.assertEqual(b'FMAP', fentry.name)
1662 self.assertEqual(32, fentry.offset)
1663 self.assertEqual(expect_size, fentry.size)
1664 self.assertEqual(0, fentry.flags)
Simon Glass11e36cc2018-07-17 13:25:38 -06001665
Simon Glassec127af2018-07-17 13:25:39 -06001666 def testBlobNamedByArg(self):
1667 """Test we can add a blob with the filename coming from an entry arg"""
1668 entry_args = {
1669 'cros-ec-rw-path': 'ecrw.bin',
1670 }
Simon Glass3decfa32020-09-01 05:13:54 -06001671 self._DoReadFileDtb('068_blob_named_by_arg.dts', entry_args=entry_args)
Simon Glassec127af2018-07-17 13:25:39 -06001672
Simon Glass3af8e492018-07-17 13:25:40 -06001673 def testFill(self):
1674 """Test for an fill entry type"""
Simon Glass741f2d62018-10-01 12:22:30 -06001675 data = self._DoReadFile('069_fill.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07001676 expected = tools.get_bytes(0xff, 8) + tools.get_bytes(0, 8)
Simon Glass3af8e492018-07-17 13:25:40 -06001677 self.assertEqual(expected, data)
1678
1679 def testFillNoSize(self):
1680 """Test for an fill entry type with no size"""
1681 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001682 self._DoReadFile('070_fill_no_size.dts')
Simon Glass3af8e492018-07-17 13:25:40 -06001683 self.assertIn("'fill' entry must have a size property",
1684 str(e.exception))
1685
Simon Glass0ef87aa2018-07-17 13:25:44 -06001686 def _HandleGbbCommand(self, pipe_list):
1687 """Fake calls to the futility utility"""
1688 if pipe_list[0][0] == 'futility':
1689 fname = pipe_list[0][-1]
1690 # Append our GBB data to the file, which will happen every time the
1691 # futility command is called.
Simon Glass1d0ebf72019-05-14 15:53:42 -06001692 with open(fname, 'ab') as fd:
Simon Glass0ef87aa2018-07-17 13:25:44 -06001693 fd.write(GBB_DATA)
1694 return command.CommandResult()
1695
1696 def testGbb(self):
1697 """Test for the Chromium OS Google Binary Block"""
1698 command.test_result = self._HandleGbbCommand
1699 entry_args = {
1700 'keydir': 'devkeys',
1701 'bmpblk': 'bmpblk.bin',
1702 }
Simon Glass741f2d62018-10-01 12:22:30 -06001703 data, _, _, _ = self._DoReadFileDtb('071_gbb.dts', entry_args=entry_args)
Simon Glass0ef87aa2018-07-17 13:25:44 -06001704
1705 # Since futility
Simon Glassc1aa66e2022-01-29 14:14:04 -07001706 expected = (GBB_DATA + GBB_DATA + tools.get_bytes(0, 8) +
1707 tools.get_bytes(0, 0x2180 - 16))
Simon Glass0ef87aa2018-07-17 13:25:44 -06001708 self.assertEqual(expected, data)
1709
1710 def testGbbTooSmall(self):
1711 """Test for the Chromium OS Google Binary Block being large enough"""
1712 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001713 self._DoReadFileDtb('072_gbb_too_small.dts')
Simon Glass0ef87aa2018-07-17 13:25:44 -06001714 self.assertIn("Node '/binman/gbb': GBB is too small",
1715 str(e.exception))
1716
1717 def testGbbNoSize(self):
1718 """Test for the Chromium OS Google Binary Block having a size"""
1719 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001720 self._DoReadFileDtb('073_gbb_no_size.dts')
Simon Glass0ef87aa2018-07-17 13:25:44 -06001721 self.assertIn("Node '/binman/gbb': GBB must have a fixed size",
1722 str(e.exception))
1723
Simon Glass4f9ee832022-01-09 20:14:09 -07001724 def testGbbMissing(self):
1725 """Test that binman still produces an image if futility is missing"""
1726 entry_args = {
1727 'keydir': 'devkeys',
1728 }
1729 with test_util.capture_sys_output() as (_, stderr):
1730 self._DoTestFile('071_gbb.dts', force_missing_bintools='futility',
1731 entry_args=entry_args)
1732 err = stderr.getvalue()
1733 self.assertRegex(err,
1734 "Image 'main-section'.*missing bintools.*: futility")
1735
Simon Glass24d0d3c2018-07-17 13:25:47 -06001736 def _HandleVblockCommand(self, pipe_list):
Simon Glass5af9ebc2021-01-06 21:35:17 -07001737 """Fake calls to the futility utility
1738
1739 The expected pipe is:
1740
1741 [('futility', 'vbutil_firmware', '--vblock',
1742 'vblock.vblock', '--keyblock', 'devkeys/firmware.keyblock',
1743 '--signprivate', 'devkeys/firmware_data_key.vbprivk',
1744 '--version', '1', '--fv', 'input.vblock', '--kernelkey',
1745 'devkeys/kernel_subkey.vbpubk', '--flags', '1')]
1746
1747 This writes to the output file (here, 'vblock.vblock'). If
1748 self._hash_data is False, it writes VBLOCK_DATA, else it writes a hash
1749 of the input data (here, 'input.vblock').
1750 """
Simon Glass24d0d3c2018-07-17 13:25:47 -06001751 if pipe_list[0][0] == 'futility':
1752 fname = pipe_list[0][3]
Simon Glassa326b492018-09-14 04:57:11 -06001753 with open(fname, 'wb') as fd:
Simon Glass5af9ebc2021-01-06 21:35:17 -07001754 if self._hash_data:
1755 infile = pipe_list[0][11]
1756 m = hashlib.sha256()
Simon Glassc1aa66e2022-01-29 14:14:04 -07001757 data = tools.read_file(infile)
Simon Glass5af9ebc2021-01-06 21:35:17 -07001758 m.update(data)
1759 fd.write(m.digest())
1760 else:
1761 fd.write(VBLOCK_DATA)
1762
Simon Glass24d0d3c2018-07-17 13:25:47 -06001763 return command.CommandResult()
1764
1765 def testVblock(self):
1766 """Test for the Chromium OS Verified Boot Block"""
Simon Glass5af9ebc2021-01-06 21:35:17 -07001767 self._hash_data = False
Simon Glass24d0d3c2018-07-17 13:25:47 -06001768 command.test_result = self._HandleVblockCommand
1769 entry_args = {
1770 'keydir': 'devkeys',
1771 }
Simon Glass741f2d62018-10-01 12:22:30 -06001772 data, _, _, _ = self._DoReadFileDtb('074_vblock.dts',
Simon Glass24d0d3c2018-07-17 13:25:47 -06001773 entry_args=entry_args)
1774 expected = U_BOOT_DATA + VBLOCK_DATA + U_BOOT_DTB_DATA
1775 self.assertEqual(expected, data)
1776
1777 def testVblockNoContent(self):
1778 """Test we detect a vblock which has no content to sign"""
1779 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001780 self._DoReadFile('075_vblock_no_content.dts')
Simon Glass189f2912021-03-21 18:24:31 +13001781 self.assertIn("Node '/binman/vblock': Collection must have a 'content' "
Simon Glass24d0d3c2018-07-17 13:25:47 -06001782 'property', str(e.exception))
1783
1784 def testVblockBadPhandle(self):
1785 """Test that we detect a vblock with an invalid phandle in contents"""
1786 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001787 self._DoReadFile('076_vblock_bad_phandle.dts')
Simon Glass24d0d3c2018-07-17 13:25:47 -06001788 self.assertIn("Node '/binman/vblock': Cannot find node for phandle "
1789 '1000', str(e.exception))
1790
1791 def testVblockBadEntry(self):
1792 """Test that we detect an entry that points to a non-entry"""
1793 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001794 self._DoReadFile('077_vblock_bad_entry.dts')
Simon Glass24d0d3c2018-07-17 13:25:47 -06001795 self.assertIn("Node '/binman/vblock': Cannot find entry for node "
1796 "'other'", str(e.exception))
1797
Simon Glass5af9ebc2021-01-06 21:35:17 -07001798 def testVblockContent(self):
1799 """Test that the vblock signs the right data"""
1800 self._hash_data = True
1801 command.test_result = self._HandleVblockCommand
1802 entry_args = {
1803 'keydir': 'devkeys',
1804 }
1805 data = self._DoReadFileDtb(
1806 '189_vblock_content.dts', use_real_dtb=True, update_dtb=True,
1807 entry_args=entry_args)[0]
1808 hashlen = 32 # SHA256 hash is 32 bytes
1809 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
1810 hashval = data[-hashlen:]
1811 dtb = data[len(U_BOOT_DATA):-hashlen]
1812
1813 expected_data = U_BOOT_DATA + dtb
1814
1815 # The hashval should be a hash of the dtb
1816 m = hashlib.sha256()
1817 m.update(expected_data)
1818 expected_hashval = m.digest()
1819 self.assertEqual(expected_hashval, hashval)
1820
Simon Glass4f9ee832022-01-09 20:14:09 -07001821 def testVblockMissing(self):
1822 """Test that binman still produces an image if futility is missing"""
1823 entry_args = {
1824 'keydir': 'devkeys',
1825 }
1826 with test_util.capture_sys_output() as (_, stderr):
1827 self._DoTestFile('074_vblock.dts',
1828 force_missing_bintools='futility',
1829 entry_args=entry_args)
1830 err = stderr.getvalue()
1831 self.assertRegex(err,
1832 "Image 'main-section'.*missing bintools.*: futility")
1833
Simon Glassb8ef5b62018-07-17 13:25:48 -06001834 def testTpl(self):
Simon Glass2090f1e2019-08-24 07:23:00 -06001835 """Test that an image with TPL and its device tree can be created"""
Simon Glassb8ef5b62018-07-17 13:25:48 -06001836 # ELF file with a '__bss_size' symbol
Simon Glass2090f1e2019-08-24 07:23:00 -06001837 self._SetupTplElf()
Simon Glass741f2d62018-10-01 12:22:30 -06001838 data = self._DoReadFile('078_u_boot_tpl.dts')
Simon Glassb8ef5b62018-07-17 13:25:48 -06001839 self.assertEqual(U_BOOT_TPL_DATA + U_BOOT_TPL_DTB_DATA, data)
1840
Simon Glass15a587c2018-07-17 13:25:51 -06001841 def testUsesPos(self):
1842 """Test that the 'pos' property cannot be used anymore"""
1843 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001844 data = self._DoReadFile('079_uses_pos.dts')
Simon Glass15a587c2018-07-17 13:25:51 -06001845 self.assertIn("Node '/binman/u-boot': Please use 'offset' instead of "
1846 "'pos'", str(e.exception))
1847
Simon Glassd178eab2018-09-14 04:57:08 -06001848 def testFillZero(self):
1849 """Test for an fill entry type with a size of 0"""
Simon Glass741f2d62018-10-01 12:22:30 -06001850 data = self._DoReadFile('080_fill_empty.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07001851 self.assertEqual(tools.get_bytes(0, 16), data)
Simon Glassd178eab2018-09-14 04:57:08 -06001852
Simon Glass0b489362018-09-14 04:57:09 -06001853 def testTextMissing(self):
1854 """Test for a text entry type where there is no text"""
1855 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001856 self._DoReadFileDtb('066_text.dts',)
Simon Glass0b489362018-09-14 04:57:09 -06001857 self.assertIn("Node '/binman/text': No value provided for text label "
1858 "'test-id'", str(e.exception))
1859
Simon Glass35b384c2018-09-14 04:57:10 -06001860 def testPackStart16Tpl(self):
1861 """Test that an image with an x86 start16 TPL region can be created"""
Simon Glass9255f3c2019-08-24 07:23:01 -06001862 data = self._DoReadFile('081_x86_start16_tpl.dts')
Simon Glass35b384c2018-09-14 04:57:10 -06001863 self.assertEqual(X86_START16_TPL_DATA, data[:len(X86_START16_TPL_DATA)])
1864
Simon Glass0bfa7b02018-09-14 04:57:12 -06001865 def testSelectImage(self):
1866 """Test that we can select which images to build"""
Simon Glasseb833d82019-04-25 21:58:34 -06001867 expected = 'Skipping images: image1'
Simon Glass0bfa7b02018-09-14 04:57:12 -06001868
Simon Glasseb833d82019-04-25 21:58:34 -06001869 # We should only get the expected message in verbose mode
Simon Glassee0c9a72019-07-08 13:18:48 -06001870 for verbosity in (0, 2):
Simon Glasseb833d82019-04-25 21:58:34 -06001871 with test_util.capture_sys_output() as (stdout, stderr):
1872 retcode = self._DoTestFile('006_dual_image.dts',
1873 verbosity=verbosity,
1874 images=['image2'])
1875 self.assertEqual(0, retcode)
1876 if verbosity:
1877 self.assertIn(expected, stdout.getvalue())
1878 else:
1879 self.assertNotIn(expected, stdout.getvalue())
1880
Simon Glassc1aa66e2022-01-29 14:14:04 -07001881 self.assertFalse(os.path.exists(tools.get_output_filename('image1.bin')))
1882 self.assertTrue(os.path.exists(tools.get_output_filename('image2.bin')))
Simon Glassf86a7362019-07-20 12:24:10 -06001883 self._CleanupOutputDir()
Simon Glass0bfa7b02018-09-14 04:57:12 -06001884
Simon Glass6ed45ba2018-09-14 04:57:24 -06001885 def testUpdateFdtAll(self):
1886 """Test that all device trees are updated with offset/size info"""
Simon Glass3c081312019-07-08 14:25:26 -06001887 data = self._DoReadFileRealDtb('082_fdt_update_all.dts')
Simon Glass6ed45ba2018-09-14 04:57:24 -06001888
1889 base_expected = {
1890 'section:image-pos': 0,
1891 'u-boot-tpl-dtb:size': 513,
1892 'u-boot-spl-dtb:size': 513,
1893 'u-boot-spl-dtb:offset': 493,
1894 'image-pos': 0,
1895 'section/u-boot-dtb:image-pos': 0,
1896 'u-boot-spl-dtb:image-pos': 493,
1897 'section/u-boot-dtb:size': 493,
1898 'u-boot-tpl-dtb:image-pos': 1006,
1899 'section/u-boot-dtb:offset': 0,
1900 'section:size': 493,
1901 'offset': 0,
1902 'section:offset': 0,
1903 'u-boot-tpl-dtb:offset': 1006,
1904 'size': 1519
1905 }
1906
1907 # We expect three device-tree files in the output, one after the other.
1908 # Read them in sequence. We look for an 'spl' property in the SPL tree,
1909 # and 'tpl' in the TPL tree, to make sure they are distinct from the
1910 # main U-Boot tree. All three should have the same postions and offset.
1911 start = 0
1912 for item in ['', 'spl', 'tpl']:
1913 dtb = fdt.Fdt.FromData(data[start:])
1914 dtb.Scan()
Simon Glass12bb1a92019-07-20 12:23:51 -06001915 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS +
1916 ['spl', 'tpl'])
Simon Glass6ed45ba2018-09-14 04:57:24 -06001917 expected = dict(base_expected)
1918 if item:
1919 expected[item] = 0
1920 self.assertEqual(expected, props)
1921 start += dtb._fdt_obj.totalsize()
1922
1923 def testUpdateFdtOutput(self):
1924 """Test that output DTB files are updated"""
1925 try:
Simon Glass741f2d62018-10-01 12:22:30 -06001926 data, dtb_data, _, _ = self._DoReadFileDtb('082_fdt_update_all.dts',
Simon Glass6ed45ba2018-09-14 04:57:24 -06001927 use_real_dtb=True, update_dtb=True, reset_dtbs=False)
1928
1929 # Unfortunately, compiling a source file always results in a file
1930 # called source.dtb (see fdt_util.EnsureCompiled()). The test
Simon Glass741f2d62018-10-01 12:22:30 -06001931 # source file (e.g. test/075_fdt_update_all.dts) thus does not enter
Simon Glass6ed45ba2018-09-14 04:57:24 -06001932 # binman as a file called u-boot.dtb. To fix this, copy the file
1933 # over to the expected place.
Simon Glass6ed45ba2018-09-14 04:57:24 -06001934 start = 0
1935 for fname in ['u-boot.dtb.out', 'spl/u-boot-spl.dtb.out',
1936 'tpl/u-boot-tpl.dtb.out']:
1937 dtb = fdt.Fdt.FromData(data[start:])
1938 size = dtb._fdt_obj.totalsize()
Simon Glassc1aa66e2022-01-29 14:14:04 -07001939 pathname = tools.get_output_filename(os.path.split(fname)[1])
1940 outdata = tools.read_file(pathname)
Simon Glass6ed45ba2018-09-14 04:57:24 -06001941 name = os.path.split(fname)[0]
1942
1943 if name:
1944 orig_indata = self._GetDtbContentsForSplTpl(dtb_data, name)
1945 else:
1946 orig_indata = dtb_data
1947 self.assertNotEqual(outdata, orig_indata,
1948 "Expected output file '%s' be updated" % pathname)
1949 self.assertEqual(outdata, data[start:start + size],
1950 "Expected output file '%s' to match output image" %
1951 pathname)
1952 start += size
1953 finally:
1954 self._ResetDtbs()
1955
Simon Glass83d73c22018-09-14 04:57:26 -06001956 def _decompress(self, data):
Simon Glass0d1e95a2022-01-09 20:14:04 -07001957 return comp_util.decompress(data, 'lz4')
Simon Glass83d73c22018-09-14 04:57:26 -06001958
1959 def testCompress(self):
1960 """Test compression of blobs"""
Simon Glassac62fba2019-07-08 13:18:53 -06001961 self._CheckLz4()
Simon Glass741f2d62018-10-01 12:22:30 -06001962 data, _, _, out_dtb_fname = self._DoReadFileDtb('083_compress.dts',
Simon Glass83d73c22018-09-14 04:57:26 -06001963 use_real_dtb=True, update_dtb=True)
1964 dtb = fdt.Fdt(out_dtb_fname)
1965 dtb.Scan()
1966 props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
1967 orig = self._decompress(data)
1968 self.assertEquals(COMPRESS_DATA, orig)
Simon Glass97c3e9a2020-10-26 17:40:15 -06001969
1970 # Do a sanity check on various fields
1971 image = control.images['image']
1972 entries = image.GetEntries()
1973 self.assertEqual(1, len(entries))
1974
1975 entry = entries['blob']
1976 self.assertEqual(COMPRESS_DATA, entry.uncomp_data)
1977 self.assertEqual(len(COMPRESS_DATA), entry.uncomp_size)
1978 orig = self._decompress(entry.data)
1979 self.assertEqual(orig, entry.uncomp_data)
1980
Simon Glass63e7ba62020-10-26 17:40:16 -06001981 self.assertEqual(image.data, entry.data)
1982
Simon Glass83d73c22018-09-14 04:57:26 -06001983 expected = {
1984 'blob:uncomp-size': len(COMPRESS_DATA),
1985 'blob:size': len(data),
1986 'size': len(data),
1987 }
1988 self.assertEqual(expected, props)
1989
Simon Glass0a98b282018-09-14 04:57:28 -06001990 def testFiles(self):
1991 """Test bringing in multiple files"""
Simon Glass741f2d62018-10-01 12:22:30 -06001992 data = self._DoReadFile('084_files.dts')
Simon Glass0a98b282018-09-14 04:57:28 -06001993 self.assertEqual(FILES_DATA, data)
1994
1995 def testFilesCompress(self):
1996 """Test bringing in multiple files and compressing them"""
Simon Glassac62fba2019-07-08 13:18:53 -06001997 self._CheckLz4()
Simon Glass741f2d62018-10-01 12:22:30 -06001998 data = self._DoReadFile('085_files_compress.dts')
Simon Glass0a98b282018-09-14 04:57:28 -06001999
2000 image = control.images['image']
2001 entries = image.GetEntries()
2002 files = entries['files']
Simon Glass8beb11e2019-07-08 14:25:47 -06002003 entries = files._entries
Simon Glass0a98b282018-09-14 04:57:28 -06002004
Simon Glassc6c10e72019-05-17 22:00:46 -06002005 orig = b''
Simon Glass0a98b282018-09-14 04:57:28 -06002006 for i in range(1, 3):
2007 key = '%d.dat' % i
2008 start = entries[key].image_pos
2009 len = entries[key].size
2010 chunk = data[start:start + len]
2011 orig += self._decompress(chunk)
2012
2013 self.assertEqual(FILES_DATA, orig)
2014
2015 def testFilesMissing(self):
2016 """Test missing files"""
2017 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06002018 data = self._DoReadFile('086_files_none.dts')
Simon Glass0a98b282018-09-14 04:57:28 -06002019 self.assertIn("Node '/binman/files': Pattern \'files/*.none\' matched "
2020 'no files', str(e.exception))
2021
2022 def testFilesNoPattern(self):
2023 """Test missing files"""
2024 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06002025 data = self._DoReadFile('087_files_no_pattern.dts')
Simon Glass0a98b282018-09-14 04:57:28 -06002026 self.assertIn("Node '/binman/files': Missing 'pattern' property",
2027 str(e.exception))
2028
Simon Glassba64a0b2018-09-14 04:57:29 -06002029 def testExpandSize(self):
2030 """Test an expanding entry"""
Simon Glass741f2d62018-10-01 12:22:30 -06002031 data, _, map_data, _ = self._DoReadFileDtb('088_expand_size.dts',
Simon Glassba64a0b2018-09-14 04:57:29 -06002032 map=True)
Simon Glassc1aa66e2022-01-29 14:14:04 -07002033 expect = (tools.get_bytes(ord('a'), 8) + U_BOOT_DATA +
2034 MRC_DATA + tools.get_bytes(ord('b'), 1) + U_BOOT_DATA +
2035 tools.get_bytes(ord('c'), 8) + U_BOOT_DATA +
2036 tools.get_bytes(ord('d'), 8))
Simon Glassba64a0b2018-09-14 04:57:29 -06002037 self.assertEqual(expect, data)
2038 self.assertEqual('''ImagePos Offset Size Name
203900000000 00000000 00000028 main-section
204000000000 00000000 00000008 fill
204100000008 00000008 00000004 u-boot
20420000000c 0000000c 00000004 section
20430000000c 00000000 00000003 intel-mrc
204400000010 00000010 00000004 u-boot2
204500000014 00000014 0000000c section2
204600000014 00000000 00000008 fill
20470000001c 00000008 00000004 u-boot
204800000020 00000020 00000008 fill2
2049''', map_data)
2050
2051 def testExpandSizeBad(self):
2052 """Test an expanding entry which fails to provide contents"""
Simon Glass163ed6c2018-09-14 04:57:36 -06002053 with test_util.capture_sys_output() as (stdout, stderr):
2054 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06002055 self._DoReadFileDtb('089_expand_size_bad.dts', map=True)
Simon Glassba64a0b2018-09-14 04:57:29 -06002056 self.assertIn("Node '/binman/_testing': Cannot obtain contents when "
2057 'expanding entry', str(e.exception))
2058
Simon Glasse0e5df92018-09-14 04:57:31 -06002059 def testHash(self):
2060 """Test hashing of the contents of an entry"""
Simon Glass741f2d62018-10-01 12:22:30 -06002061 _, _, _, out_dtb_fname = self._DoReadFileDtb('090_hash.dts',
Simon Glasse0e5df92018-09-14 04:57:31 -06002062 use_real_dtb=True, update_dtb=True)
2063 dtb = fdt.Fdt(out_dtb_fname)
2064 dtb.Scan()
2065 hash_node = dtb.GetNode('/binman/u-boot/hash').props['value']
2066 m = hashlib.sha256()
2067 m.update(U_BOOT_DATA)
Simon Glassc6c10e72019-05-17 22:00:46 -06002068 self.assertEqual(m.digest(), b''.join(hash_node.value))
Simon Glasse0e5df92018-09-14 04:57:31 -06002069
2070 def testHashNoAlgo(self):
2071 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06002072 self._DoReadFileDtb('091_hash_no_algo.dts', update_dtb=True)
Simon Glasse0e5df92018-09-14 04:57:31 -06002073 self.assertIn("Node \'/binman/u-boot\': Missing \'algo\' property for "
2074 'hash node', str(e.exception))
2075
2076 def testHashBadAlgo(self):
2077 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06002078 self._DoReadFileDtb('092_hash_bad_algo.dts', update_dtb=True)
Simon Glass8db1f992022-02-08 10:59:44 -07002079 self.assertIn("Node '/binman/u-boot': Unknown hash algorithm 'invalid'",
Simon Glasse0e5df92018-09-14 04:57:31 -06002080 str(e.exception))
2081
2082 def testHashSection(self):
2083 """Test hashing of the contents of an entry"""
Simon Glass741f2d62018-10-01 12:22:30 -06002084 _, _, _, out_dtb_fname = self._DoReadFileDtb('099_hash_section.dts',
Simon Glasse0e5df92018-09-14 04:57:31 -06002085 use_real_dtb=True, update_dtb=True)
2086 dtb = fdt.Fdt(out_dtb_fname)
2087 dtb.Scan()
2088 hash_node = dtb.GetNode('/binman/section/hash').props['value']
2089 m = hashlib.sha256()
2090 m.update(U_BOOT_DATA)
Simon Glassc1aa66e2022-01-29 14:14:04 -07002091 m.update(tools.get_bytes(ord('a'), 16))
Simon Glassc6c10e72019-05-17 22:00:46 -06002092 self.assertEqual(m.digest(), b''.join(hash_node.value))
Simon Glasse0e5df92018-09-14 04:57:31 -06002093
Simon Glassf0253632018-09-14 04:57:32 -06002094 def testPackUBootTplMicrocode(self):
2095 """Test that x86 microcode can be handled correctly in TPL
2096
2097 We expect to see the following in the image, in order:
2098 u-boot-tpl-nodtb.bin with a microcode pointer inserted at the correct
2099 place
2100 u-boot-tpl.dtb with the microcode removed
2101 the microcode
2102 """
Simon Glass2090f1e2019-08-24 07:23:00 -06002103 self._SetupTplElf('u_boot_ucode_ptr')
Simon Glass741f2d62018-10-01 12:22:30 -06002104 first, pos_and_size = self._RunMicrocodeTest('093_x86_tpl_ucode.dts',
Simon Glassf0253632018-09-14 04:57:32 -06002105 U_BOOT_TPL_NODTB_DATA)
Simon Glassc6c10e72019-05-17 22:00:46 -06002106 self.assertEqual(b'tplnodtb with microc' + pos_and_size +
2107 b'ter somewhere in here', first)
Simon Glassf0253632018-09-14 04:57:32 -06002108
Simon Glassf8f8df62018-09-14 04:57:34 -06002109 def testFmapX86(self):
2110 """Basic test of generation of a flashrom fmap"""
Simon Glass741f2d62018-10-01 12:22:30 -06002111 data = self._DoReadFile('094_fmap_x86.dts')
Simon Glassf8f8df62018-09-14 04:57:34 -06002112 fhdr, fentries = fmap_util.DecodeFmap(data[32:])
Simon Glassc1aa66e2022-01-29 14:14:04 -07002113 expected = U_BOOT_DATA + MRC_DATA + tools.get_bytes(ord('a'), 32 - 7)
Simon Glassf8f8df62018-09-14 04:57:34 -06002114 self.assertEqual(expected, data[:32])
2115 fhdr, fentries = fmap_util.DecodeFmap(data[32:])
2116
2117 self.assertEqual(0x100, fhdr.image_size)
2118
2119 self.assertEqual(0, fentries[0].offset)
2120 self.assertEqual(4, fentries[0].size)
Simon Glassc6c10e72019-05-17 22:00:46 -06002121 self.assertEqual(b'U_BOOT', fentries[0].name)
Simon Glassf8f8df62018-09-14 04:57:34 -06002122
2123 self.assertEqual(4, fentries[1].offset)
2124 self.assertEqual(3, fentries[1].size)
Simon Glassc6c10e72019-05-17 22:00:46 -06002125 self.assertEqual(b'INTEL_MRC', fentries[1].name)
Simon Glassf8f8df62018-09-14 04:57:34 -06002126
2127 self.assertEqual(32, fentries[2].offset)
2128 self.assertEqual(fmap_util.FMAP_HEADER_LEN +
2129 fmap_util.FMAP_AREA_LEN * 3, fentries[2].size)
Simon Glassc6c10e72019-05-17 22:00:46 -06002130 self.assertEqual(b'FMAP', fentries[2].name)
Simon Glassf8f8df62018-09-14 04:57:34 -06002131
2132 def testFmapX86Section(self):
2133 """Basic test of generation of a flashrom fmap"""
Simon Glass741f2d62018-10-01 12:22:30 -06002134 data = self._DoReadFile('095_fmap_x86_section.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07002135 expected = U_BOOT_DATA + MRC_DATA + tools.get_bytes(ord('b'), 32 - 7)
Simon Glassf8f8df62018-09-14 04:57:34 -06002136 self.assertEqual(expected, data[:32])
2137 fhdr, fentries = fmap_util.DecodeFmap(data[36:])
2138
Simon Glass17365752021-04-03 11:05:10 +13002139 self.assertEqual(0x180, fhdr.image_size)
2140 expect_size = fmap_util.FMAP_HEADER_LEN + fmap_util.FMAP_AREA_LEN * 4
Simon Glassc7722e82021-04-03 11:05:09 +13002141 fiter = iter(fentries)
Simon Glassf8f8df62018-09-14 04:57:34 -06002142
Simon Glassc7722e82021-04-03 11:05:09 +13002143 fentry = next(fiter)
2144 self.assertEqual(b'U_BOOT', fentry.name)
2145 self.assertEqual(0, fentry.offset)
2146 self.assertEqual(4, fentry.size)
Simon Glassf8f8df62018-09-14 04:57:34 -06002147
Simon Glassc7722e82021-04-03 11:05:09 +13002148 fentry = next(fiter)
Simon Glass17365752021-04-03 11:05:10 +13002149 self.assertEqual(b'SECTION', fentry.name)
2150 self.assertEqual(4, fentry.offset)
2151 self.assertEqual(0x20 + expect_size, fentry.size)
2152
2153 fentry = next(fiter)
Simon Glassc7722e82021-04-03 11:05:09 +13002154 self.assertEqual(b'INTEL_MRC', fentry.name)
2155 self.assertEqual(4, fentry.offset)
2156 self.assertEqual(3, fentry.size)
Simon Glassf8f8df62018-09-14 04:57:34 -06002157
Simon Glassc7722e82021-04-03 11:05:09 +13002158 fentry = next(fiter)
2159 self.assertEqual(b'FMAP', fentry.name)
2160 self.assertEqual(36, fentry.offset)
2161 self.assertEqual(expect_size, fentry.size)
Simon Glassf8f8df62018-09-14 04:57:34 -06002162
Simon Glassfe1ae3e2018-09-14 04:57:35 -06002163 def testElf(self):
2164 """Basic test of ELF entries"""
Simon Glass11ae93e2018-10-01 21:12:47 -06002165 self._SetupSplElf()
Simon Glass2090f1e2019-08-24 07:23:00 -06002166 self._SetupTplElf()
Simon Glass53e22bf2019-08-24 07:22:53 -06002167 with open(self.ElfTestFile('bss_data'), 'rb') as fd:
Simon Glassfe1ae3e2018-09-14 04:57:35 -06002168 TestFunctional._MakeInputFile('-boot', fd.read())
Simon Glass741f2d62018-10-01 12:22:30 -06002169 data = self._DoReadFile('096_elf.dts')
Simon Glassfe1ae3e2018-09-14 04:57:35 -06002170
Simon Glass093d1682019-07-08 13:18:25 -06002171 def testElfStrip(self):
Simon Glassfe1ae3e2018-09-14 04:57:35 -06002172 """Basic test of ELF entries"""
Simon Glass11ae93e2018-10-01 21:12:47 -06002173 self._SetupSplElf()
Simon Glass53e22bf2019-08-24 07:22:53 -06002174 with open(self.ElfTestFile('bss_data'), 'rb') as fd:
Simon Glassfe1ae3e2018-09-14 04:57:35 -06002175 TestFunctional._MakeInputFile('-boot', fd.read())
Simon Glass741f2d62018-10-01 12:22:30 -06002176 data = self._DoReadFile('097_elf_strip.dts')
Simon Glassfe1ae3e2018-09-14 04:57:35 -06002177
Simon Glass163ed6c2018-09-14 04:57:36 -06002178 def testPackOverlapMap(self):
2179 """Test that overlapping regions are detected"""
2180 with test_util.capture_sys_output() as (stdout, stderr):
2181 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06002182 self._DoTestFile('014_pack_overlap.dts', map=True)
Simon Glassc1aa66e2022-01-29 14:14:04 -07002183 map_fname = tools.get_output_filename('image.map')
Simon Glass163ed6c2018-09-14 04:57:36 -06002184 self.assertEqual("Wrote map file '%s' to show errors\n" % map_fname,
2185 stdout.getvalue())
2186
2187 # We should not get an inmage, but there should be a map file
Simon Glassc1aa66e2022-01-29 14:14:04 -07002188 self.assertFalse(os.path.exists(tools.get_output_filename('image.bin')))
Simon Glass163ed6c2018-09-14 04:57:36 -06002189 self.assertTrue(os.path.exists(map_fname))
Simon Glassc1aa66e2022-01-29 14:14:04 -07002190 map_data = tools.read_file(map_fname, binary=False)
Simon Glass163ed6c2018-09-14 04:57:36 -06002191 self.assertEqual('''ImagePos Offset Size Name
Simon Glass0ff83da2020-10-26 17:40:24 -06002192<none> 00000000 00000008 main-section
Simon Glass163ed6c2018-09-14 04:57:36 -06002193<none> 00000000 00000004 u-boot
2194<none> 00000003 00000004 u-boot-align
2195''', map_data)
2196
Simon Glass093d1682019-07-08 13:18:25 -06002197 def testPackRefCode(self):
Simon Glass3ae192c2018-10-01 12:22:31 -06002198 """Test that an image with an Intel Reference code binary works"""
2199 data = self._DoReadFile('100_intel_refcode.dts')
2200 self.assertEqual(REFCODE_DATA, data[:len(REFCODE_DATA)])
2201
Simon Glass9481c802019-04-25 21:58:39 -06002202 def testSectionOffset(self):
2203 """Tests use of a section with an offset"""
2204 data, _, map_data, _ = self._DoReadFileDtb('101_sections_offset.dts',
2205 map=True)
2206 self.assertEqual('''ImagePos Offset Size Name
220700000000 00000000 00000038 main-section
220800000004 00000004 00000010 section@0
220900000004 00000000 00000004 u-boot
221000000018 00000018 00000010 section@1
221100000018 00000000 00000004 u-boot
22120000002c 0000002c 00000004 section@2
22130000002c 00000000 00000004 u-boot
2214''', map_data)
2215 self.assertEqual(data,
Simon Glassc1aa66e2022-01-29 14:14:04 -07002216 tools.get_bytes(0x26, 4) + U_BOOT_DATA +
2217 tools.get_bytes(0x21, 12) +
2218 tools.get_bytes(0x26, 4) + U_BOOT_DATA +
2219 tools.get_bytes(0x61, 12) +
2220 tools.get_bytes(0x26, 4) + U_BOOT_DATA +
2221 tools.get_bytes(0x26, 8))
Simon Glass9481c802019-04-25 21:58:39 -06002222
Simon Glassac62fba2019-07-08 13:18:53 -06002223 def testCbfsRaw(self):
2224 """Test base handling of a Coreboot Filesystem (CBFS)
2225
2226 The exact contents of the CBFS is verified by similar tests in
2227 cbfs_util_test.py. The tests here merely check that the files added to
2228 the CBFS can be found in the final image.
2229 """
2230 data = self._DoReadFile('102_cbfs_raw.dts')
2231 size = 0xb0
2232
2233 cbfs = cbfs_util.CbfsReader(data)
2234 self.assertEqual(size, cbfs.rom_size)
2235
2236 self.assertIn('u-boot-dtb', cbfs.files)
2237 cfile = cbfs.files['u-boot-dtb']
2238 self.assertEqual(U_BOOT_DTB_DATA, cfile.data)
2239
2240 def testCbfsArch(self):
2241 """Test on non-x86 architecture"""
2242 data = self._DoReadFile('103_cbfs_raw_ppc.dts')
2243 size = 0x100
2244
2245 cbfs = cbfs_util.CbfsReader(data)
2246 self.assertEqual(size, cbfs.rom_size)
2247
2248 self.assertIn('u-boot-dtb', cbfs.files)
2249 cfile = cbfs.files['u-boot-dtb']
2250 self.assertEqual(U_BOOT_DTB_DATA, cfile.data)
2251
2252 def testCbfsStage(self):
2253 """Tests handling of a Coreboot Filesystem (CBFS)"""
2254 if not elf.ELF_TOOLS:
2255 self.skipTest('Python elftools not available')
2256 elf_fname = os.path.join(self._indir, 'cbfs-stage.elf')
2257 elf.MakeElf(elf_fname, U_BOOT_DATA, U_BOOT_DTB_DATA)
2258 size = 0xb0
2259
2260 data = self._DoReadFile('104_cbfs_stage.dts')
2261 cbfs = cbfs_util.CbfsReader(data)
2262 self.assertEqual(size, cbfs.rom_size)
2263
2264 self.assertIn('u-boot', cbfs.files)
2265 cfile = cbfs.files['u-boot']
2266 self.assertEqual(U_BOOT_DATA + U_BOOT_DTB_DATA, cfile.data)
2267
2268 def testCbfsRawCompress(self):
2269 """Test handling of compressing raw files"""
2270 self._CheckLz4()
2271 data = self._DoReadFile('105_cbfs_raw_compress.dts')
2272 size = 0x140
2273
2274 cbfs = cbfs_util.CbfsReader(data)
2275 self.assertIn('u-boot', cbfs.files)
2276 cfile = cbfs.files['u-boot']
2277 self.assertEqual(COMPRESS_DATA, cfile.data)
2278
2279 def testCbfsBadArch(self):
2280 """Test handling of a bad architecture"""
2281 with self.assertRaises(ValueError) as e:
2282 self._DoReadFile('106_cbfs_bad_arch.dts')
2283 self.assertIn("Invalid architecture 'bad-arch'", str(e.exception))
2284
2285 def testCbfsNoSize(self):
2286 """Test handling of a missing size property"""
2287 with self.assertRaises(ValueError) as e:
2288 self._DoReadFile('107_cbfs_no_size.dts')
2289 self.assertIn('entry must have a size property', str(e.exception))
2290
Simon Glasse2f04742021-11-23 11:03:54 -07002291 def testCbfsNoContents(self):
Simon Glassac62fba2019-07-08 13:18:53 -06002292 """Test handling of a CBFS entry which does not provide contentsy"""
2293 with self.assertRaises(ValueError) as e:
2294 self._DoReadFile('108_cbfs_no_contents.dts')
2295 self.assertIn('Could not complete processing of contents',
2296 str(e.exception))
2297
2298 def testCbfsBadCompress(self):
2299 """Test handling of a bad architecture"""
2300 with self.assertRaises(ValueError) as e:
2301 self._DoReadFile('109_cbfs_bad_compress.dts')
2302 self.assertIn("Invalid compression in 'u-boot': 'invalid-algo'",
2303 str(e.exception))
2304
2305 def testCbfsNamedEntries(self):
2306 """Test handling of named entries"""
2307 data = self._DoReadFile('110_cbfs_name.dts')
2308
2309 cbfs = cbfs_util.CbfsReader(data)
2310 self.assertIn('FRED', cbfs.files)
2311 cfile1 = cbfs.files['FRED']
2312 self.assertEqual(U_BOOT_DATA, cfile1.data)
2313
2314 self.assertIn('hello', cbfs.files)
2315 cfile2 = cbfs.files['hello']
2316 self.assertEqual(U_BOOT_DTB_DATA, cfile2.data)
2317
Simon Glassc5ac1382019-07-08 13:18:54 -06002318 def _SetupIfwi(self, fname):
2319 """Set up to run an IFWI test
2320
2321 Args:
2322 fname: Filename of input file to provide (fitimage.bin or ifwi.bin)
2323 """
2324 self._SetupSplElf()
Simon Glass2090f1e2019-08-24 07:23:00 -06002325 self._SetupTplElf()
Simon Glassc5ac1382019-07-08 13:18:54 -06002326
2327 # Intel Integrated Firmware Image (IFWI) file
2328 with gzip.open(self.TestFile('%s.gz' % fname), 'rb') as fd:
2329 data = fd.read()
2330 TestFunctional._MakeInputFile(fname,data)
2331
2332 def _CheckIfwi(self, data):
2333 """Check that an image with an IFWI contains the correct output
2334
2335 Args:
2336 data: Conents of output file
2337 """
Simon Glassc1aa66e2022-01-29 14:14:04 -07002338 expected_desc = tools.read_file(self.TestFile('descriptor.bin'))
Simon Glassc5ac1382019-07-08 13:18:54 -06002339 if data[:0x1000] != expected_desc:
2340 self.fail('Expected descriptor binary at start of image')
2341
2342 # We expect to find the TPL wil in subpart IBBP entry IBBL
Simon Glassc1aa66e2022-01-29 14:14:04 -07002343 image_fname = tools.get_output_filename('image.bin')
2344 tpl_fname = tools.get_output_filename('tpl.out')
Simon Glass532ae702022-01-09 20:14:01 -07002345 ifwitool = bintool.Bintool.create('ifwitool')
2346 ifwitool.extract(image_fname, 'IBBP', 'IBBL', tpl_fname)
Simon Glassc5ac1382019-07-08 13:18:54 -06002347
Simon Glassc1aa66e2022-01-29 14:14:04 -07002348 tpl_data = tools.read_file(tpl_fname)
Simon Glasse95be632019-08-24 07:22:51 -06002349 self.assertEqual(U_BOOT_TPL_DATA, tpl_data[:len(U_BOOT_TPL_DATA)])
Simon Glassc5ac1382019-07-08 13:18:54 -06002350
2351 def testPackX86RomIfwi(self):
2352 """Test that an x86 ROM with Integrated Firmware Image can be created"""
2353 self._SetupIfwi('fitimage.bin')
Simon Glass9255f3c2019-08-24 07:23:01 -06002354 data = self._DoReadFile('111_x86_rom_ifwi.dts')
Simon Glassc5ac1382019-07-08 13:18:54 -06002355 self._CheckIfwi(data)
2356
2357 def testPackX86RomIfwiNoDesc(self):
2358 """Test that an x86 ROM with IFWI can be created from an ifwi.bin file"""
2359 self._SetupIfwi('ifwi.bin')
Simon Glass9255f3c2019-08-24 07:23:01 -06002360 data = self._DoReadFile('112_x86_rom_ifwi_nodesc.dts')
Simon Glassc5ac1382019-07-08 13:18:54 -06002361 self._CheckIfwi(data)
2362
2363 def testPackX86RomIfwiNoData(self):
2364 """Test that an x86 ROM with IFWI handles missing data"""
2365 self._SetupIfwi('ifwi.bin')
2366 with self.assertRaises(ValueError) as e:
Simon Glass9255f3c2019-08-24 07:23:01 -06002367 data = self._DoReadFile('113_x86_rom_ifwi_nodata.dts')
Simon Glassc5ac1382019-07-08 13:18:54 -06002368 self.assertIn('Could not complete processing of contents',
2369 str(e.exception))
Simon Glass53af22a2018-07-17 13:25:32 -06002370
Simon Glass4f9ee832022-01-09 20:14:09 -07002371 def testIfwiMissing(self):
2372 """Test that binman still produces an image if ifwitool is missing"""
2373 self._SetupIfwi('fitimage.bin')
2374 with test_util.capture_sys_output() as (_, stderr):
2375 self._DoTestFile('111_x86_rom_ifwi.dts',
2376 force_missing_bintools='ifwitool')
2377 err = stderr.getvalue()
2378 self.assertRegex(err,
2379 "Image 'main-section'.*missing bintools.*: ifwitool")
2380
Simon Glasse073d4e2019-07-08 13:18:56 -06002381 def testCbfsOffset(self):
2382 """Test a CBFS with files at particular offsets
2383
2384 Like all CFBS tests, this is just checking the logic that calls
2385 cbfs_util. See cbfs_util_test for fully tests (e.g. test_cbfs_offset()).
2386 """
2387 data = self._DoReadFile('114_cbfs_offset.dts')
2388 size = 0x200
2389
2390 cbfs = cbfs_util.CbfsReader(data)
2391 self.assertEqual(size, cbfs.rom_size)
2392
2393 self.assertIn('u-boot', cbfs.files)
2394 cfile = cbfs.files['u-boot']
2395 self.assertEqual(U_BOOT_DATA, cfile.data)
2396 self.assertEqual(0x40, cfile.cbfs_offset)
2397
2398 self.assertIn('u-boot-dtb', cbfs.files)
2399 cfile2 = cbfs.files['u-boot-dtb']
2400 self.assertEqual(U_BOOT_DTB_DATA, cfile2.data)
2401 self.assertEqual(0x140, cfile2.cbfs_offset)
2402
Simon Glass086cec92019-07-08 14:25:27 -06002403 def testFdtmap(self):
2404 """Test an FDT map can be inserted in the image"""
2405 data = self.data = self._DoReadFileRealDtb('115_fdtmap.dts')
2406 fdtmap_data = data[len(U_BOOT_DATA):]
2407 magic = fdtmap_data[:8]
Simon Glassb6ee0cf2019-10-31 07:43:03 -06002408 self.assertEqual(b'_FDTMAP_', magic)
Simon Glassc1aa66e2022-01-29 14:14:04 -07002409 self.assertEqual(tools.get_bytes(0, 8), fdtmap_data[8:16])
Simon Glass086cec92019-07-08 14:25:27 -06002410
2411 fdt_data = fdtmap_data[16:]
2412 dtb = fdt.Fdt.FromData(fdt_data)
2413 dtb.Scan()
Simon Glass6ccbfcd2019-07-20 12:23:47 -06002414 props = self._GetPropTree(dtb, BASE_DTB_PROPS, prefix='/')
Simon Glass086cec92019-07-08 14:25:27 -06002415 self.assertEqual({
2416 'image-pos': 0,
2417 'offset': 0,
2418 'u-boot:offset': 0,
2419 'u-boot:size': len(U_BOOT_DATA),
2420 'u-boot:image-pos': 0,
2421 'fdtmap:image-pos': 4,
2422 'fdtmap:offset': 4,
2423 'fdtmap:size': len(fdtmap_data),
2424 'size': len(data),
2425 }, props)
2426
2427 def testFdtmapNoMatch(self):
2428 """Check handling of an FDT map when the section cannot be found"""
2429 self.data = self._DoReadFileRealDtb('115_fdtmap.dts')
2430
2431 # Mangle the section name, which should cause a mismatch between the
2432 # correct FDT path and the one expected by the section
2433 image = control.images['image']
Simon Glasscf228942019-07-08 14:25:28 -06002434 image._node.path += '-suffix'
Simon Glass086cec92019-07-08 14:25:27 -06002435 entries = image.GetEntries()
2436 fdtmap = entries['fdtmap']
2437 with self.assertRaises(ValueError) as e:
2438 fdtmap._GetFdtmap()
2439 self.assertIn("Cannot locate node for path '/binman-suffix'",
2440 str(e.exception))
2441
Simon Glasscf228942019-07-08 14:25:28 -06002442 def testFdtmapHeader(self):
2443 """Test an FDT map and image header can be inserted in the image"""
2444 data = self.data = self._DoReadFileRealDtb('116_fdtmap_hdr.dts')
2445 fdtmap_pos = len(U_BOOT_DATA)
2446 fdtmap_data = data[fdtmap_pos:]
2447 fdt_data = fdtmap_data[16:]
2448 dtb = fdt.Fdt.FromData(fdt_data)
2449 fdt_size = dtb.GetFdtObj().totalsize()
2450 hdr_data = data[-8:]
Simon Glassb6ee0cf2019-10-31 07:43:03 -06002451 self.assertEqual(b'BinM', hdr_data[:4])
Simon Glasscf228942019-07-08 14:25:28 -06002452 offset = struct.unpack('<I', hdr_data[4:])[0] & 0xffffffff
2453 self.assertEqual(fdtmap_pos - 0x400, offset - (1 << 32))
2454
2455 def testFdtmapHeaderStart(self):
2456 """Test an image header can be inserted at the image start"""
2457 data = self.data = self._DoReadFileRealDtb('117_fdtmap_hdr_start.dts')
2458 fdtmap_pos = 0x100 + len(U_BOOT_DATA)
2459 hdr_data = data[:8]
Simon Glassb6ee0cf2019-10-31 07:43:03 -06002460 self.assertEqual(b'BinM', hdr_data[:4])
Simon Glasscf228942019-07-08 14:25:28 -06002461 offset = struct.unpack('<I', hdr_data[4:])[0]
2462 self.assertEqual(fdtmap_pos, offset)
2463
2464 def testFdtmapHeaderPos(self):
2465 """Test an image header can be inserted at a chosen position"""
2466 data = self.data = self._DoReadFileRealDtb('118_fdtmap_hdr_pos.dts')
2467 fdtmap_pos = 0x100 + len(U_BOOT_DATA)
2468 hdr_data = data[0x80:0x88]
Simon Glassb6ee0cf2019-10-31 07:43:03 -06002469 self.assertEqual(b'BinM', hdr_data[:4])
Simon Glasscf228942019-07-08 14:25:28 -06002470 offset = struct.unpack('<I', hdr_data[4:])[0]
2471 self.assertEqual(fdtmap_pos, offset)
2472
2473 def testHeaderMissingFdtmap(self):
2474 """Test an image header requires an fdtmap"""
2475 with self.assertRaises(ValueError) as e:
2476 self.data = self._DoReadFileRealDtb('119_fdtmap_hdr_missing.dts')
2477 self.assertIn("'image_header' section must have an 'fdtmap' sibling",
2478 str(e.exception))
2479
2480 def testHeaderNoLocation(self):
2481 """Test an image header with a no specified location is detected"""
2482 with self.assertRaises(ValueError) as e:
2483 self.data = self._DoReadFileRealDtb('120_hdr_no_location.dts')
2484 self.assertIn("Invalid location 'None', expected 'start' or 'end'",
2485 str(e.exception))
2486
Simon Glassc52c9e72019-07-08 14:25:37 -06002487 def testEntryExpand(self):
2488 """Test expanding an entry after it is packed"""
2489 data = self._DoReadFile('121_entry_expand.dts')
Simon Glass79d3c582019-07-20 12:23:57 -06002490 self.assertEqual(b'aaa', data[:3])
2491 self.assertEqual(U_BOOT_DATA, data[3:3 + len(U_BOOT_DATA)])
2492 self.assertEqual(b'aaa', data[-3:])
Simon Glassc52c9e72019-07-08 14:25:37 -06002493
2494 def testEntryExpandBad(self):
2495 """Test expanding an entry after it is packed, twice"""
2496 with self.assertRaises(ValueError) as e:
2497 self._DoReadFile('122_entry_expand_twice.dts')
Simon Glass61ec04f2019-07-20 12:23:58 -06002498 self.assertIn("Image '/binman': Entries changed size after packing",
Simon Glassc52c9e72019-07-08 14:25:37 -06002499 str(e.exception))
2500
2501 def testEntryExpandSection(self):
2502 """Test expanding an entry within a section after it is packed"""
2503 data = self._DoReadFile('123_entry_expand_section.dts')
Simon Glass79d3c582019-07-20 12:23:57 -06002504 self.assertEqual(b'aaa', data[:3])
2505 self.assertEqual(U_BOOT_DATA, data[3:3 + len(U_BOOT_DATA)])
2506 self.assertEqual(b'aaa', data[-3:])
Simon Glassc52c9e72019-07-08 14:25:37 -06002507
Simon Glass6c223fd2019-07-08 14:25:38 -06002508 def testCompressDtb(self):
2509 """Test that compress of device-tree files is supported"""
2510 self._CheckLz4()
2511 data = self.data = self._DoReadFileRealDtb('124_compress_dtb.dts')
2512 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
2513 comp_data = data[len(U_BOOT_DATA):]
2514 orig = self._decompress(comp_data)
2515 dtb = fdt.Fdt.FromData(orig)
2516 dtb.Scan()
2517 props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
2518 expected = {
2519 'u-boot:size': len(U_BOOT_DATA),
2520 'u-boot-dtb:uncomp-size': len(orig),
2521 'u-boot-dtb:size': len(comp_data),
2522 'size': len(data),
2523 }
2524 self.assertEqual(expected, props)
2525
Simon Glass69f7cb32019-07-08 14:25:41 -06002526 def testCbfsUpdateFdt(self):
2527 """Test that we can update the device tree with CBFS offset/size info"""
2528 self._CheckLz4()
2529 data, _, _, out_dtb_fname = self._DoReadFileDtb('125_cbfs_update.dts',
2530 update_dtb=True)
2531 dtb = fdt.Fdt(out_dtb_fname)
2532 dtb.Scan()
Simon Glass6ccbfcd2019-07-20 12:23:47 -06002533 props = self._GetPropTree(dtb, BASE_DTB_PROPS + ['uncomp-size'])
Simon Glass69f7cb32019-07-08 14:25:41 -06002534 del props['cbfs/u-boot:size']
2535 self.assertEqual({
2536 'offset': 0,
2537 'size': len(data),
2538 'image-pos': 0,
2539 'cbfs:offset': 0,
2540 'cbfs:size': len(data),
2541 'cbfs:image-pos': 0,
2542 'cbfs/u-boot:offset': 0x38,
2543 'cbfs/u-boot:uncomp-size': len(U_BOOT_DATA),
2544 'cbfs/u-boot:image-pos': 0x38,
2545 'cbfs/u-boot-dtb:offset': 0xb8,
2546 'cbfs/u-boot-dtb:size': len(U_BOOT_DATA),
2547 'cbfs/u-boot-dtb:image-pos': 0xb8,
2548 }, props)
2549
Simon Glass8a1ad062019-07-08 14:25:42 -06002550 def testCbfsBadType(self):
2551 """Test an image header with a no specified location is detected"""
2552 with self.assertRaises(ValueError) as e:
2553 self._DoReadFile('126_cbfs_bad_type.dts')
2554 self.assertIn("Unknown cbfs-type 'badtype'", str(e.exception))
2555
Simon Glass41b8ba02019-07-08 14:25:43 -06002556 def testList(self):
2557 """Test listing the files in an image"""
2558 self._CheckLz4()
2559 data = self._DoReadFile('127_list.dts')
2560 image = control.images['image']
2561 entries = image.BuildEntryList()
2562 self.assertEqual(7, len(entries))
2563
2564 ent = entries[0]
2565 self.assertEqual(0, ent.indent)
2566 self.assertEqual('main-section', ent.name)
2567 self.assertEqual('section', ent.etype)
2568 self.assertEqual(len(data), ent.size)
2569 self.assertEqual(0, ent.image_pos)
2570 self.assertEqual(None, ent.uncomp_size)
Simon Glass8beb11e2019-07-08 14:25:47 -06002571 self.assertEqual(0, ent.offset)
Simon Glass41b8ba02019-07-08 14:25:43 -06002572
2573 ent = entries[1]
2574 self.assertEqual(1, ent.indent)
2575 self.assertEqual('u-boot', ent.name)
2576 self.assertEqual('u-boot', ent.etype)
2577 self.assertEqual(len(U_BOOT_DATA), ent.size)
2578 self.assertEqual(0, ent.image_pos)
2579 self.assertEqual(None, ent.uncomp_size)
2580 self.assertEqual(0, ent.offset)
2581
2582 ent = entries[2]
2583 self.assertEqual(1, ent.indent)
2584 self.assertEqual('section', ent.name)
2585 self.assertEqual('section', ent.etype)
2586 section_size = ent.size
2587 self.assertEqual(0x100, ent.image_pos)
2588 self.assertEqual(None, ent.uncomp_size)
Simon Glass8beb11e2019-07-08 14:25:47 -06002589 self.assertEqual(0x100, ent.offset)
Simon Glass41b8ba02019-07-08 14:25:43 -06002590
2591 ent = entries[3]
2592 self.assertEqual(2, ent.indent)
2593 self.assertEqual('cbfs', ent.name)
2594 self.assertEqual('cbfs', ent.etype)
2595 self.assertEqual(0x400, ent.size)
2596 self.assertEqual(0x100, ent.image_pos)
2597 self.assertEqual(None, ent.uncomp_size)
2598 self.assertEqual(0, ent.offset)
2599
2600 ent = entries[4]
2601 self.assertEqual(3, ent.indent)
2602 self.assertEqual('u-boot', ent.name)
2603 self.assertEqual('u-boot', ent.etype)
2604 self.assertEqual(len(U_BOOT_DATA), ent.size)
2605 self.assertEqual(0x138, ent.image_pos)
2606 self.assertEqual(None, ent.uncomp_size)
2607 self.assertEqual(0x38, ent.offset)
2608
2609 ent = entries[5]
2610 self.assertEqual(3, ent.indent)
2611 self.assertEqual('u-boot-dtb', ent.name)
2612 self.assertEqual('text', ent.etype)
2613 self.assertGreater(len(COMPRESS_DATA), ent.size)
2614 self.assertEqual(0x178, ent.image_pos)
2615 self.assertEqual(len(COMPRESS_DATA), ent.uncomp_size)
2616 self.assertEqual(0x78, ent.offset)
2617
2618 ent = entries[6]
2619 self.assertEqual(2, ent.indent)
2620 self.assertEqual('u-boot-dtb', ent.name)
2621 self.assertEqual('u-boot-dtb', ent.etype)
2622 self.assertEqual(0x500, ent.image_pos)
2623 self.assertEqual(len(U_BOOT_DTB_DATA), ent.uncomp_size)
2624 dtb_size = ent.size
2625 # Compressing this data expands it since headers are added
2626 self.assertGreater(dtb_size, len(U_BOOT_DTB_DATA))
2627 self.assertEqual(0x400, ent.offset)
2628
2629 self.assertEqual(len(data), 0x100 + section_size)
2630 self.assertEqual(section_size, 0x400 + dtb_size)
2631
Simon Glasse1925fa2019-07-08 14:25:44 -06002632 def testFindFdtmap(self):
2633 """Test locating an FDT map in an image"""
2634 self._CheckLz4()
2635 data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
2636 image = control.images['image']
2637 entries = image.GetEntries()
2638 entry = entries['fdtmap']
2639 self.assertEqual(entry.image_pos, fdtmap.LocateFdtmap(data))
2640
2641 def testFindFdtmapMissing(self):
2642 """Test failing to locate an FDP map"""
2643 data = self._DoReadFile('005_simple.dts')
2644 self.assertEqual(None, fdtmap.LocateFdtmap(data))
2645
Simon Glass2d260032019-07-08 14:25:45 -06002646 def testFindImageHeader(self):
2647 """Test locating a image header"""
2648 self._CheckLz4()
Simon Glassffded752019-07-08 14:25:46 -06002649 data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
Simon Glass2d260032019-07-08 14:25:45 -06002650 image = control.images['image']
2651 entries = image.GetEntries()
2652 entry = entries['fdtmap']
2653 # The header should point to the FDT map
2654 self.assertEqual(entry.image_pos, image_header.LocateHeaderOffset(data))
2655
2656 def testFindImageHeaderStart(self):
2657 """Test locating a image header located at the start of an image"""
Simon Glassffded752019-07-08 14:25:46 -06002658 data = self.data = self._DoReadFileRealDtb('117_fdtmap_hdr_start.dts')
Simon Glass2d260032019-07-08 14:25:45 -06002659 image = control.images['image']
2660 entries = image.GetEntries()
2661 entry = entries['fdtmap']
2662 # The header should point to the FDT map
2663 self.assertEqual(entry.image_pos, image_header.LocateHeaderOffset(data))
2664
2665 def testFindImageHeaderMissing(self):
2666 """Test failing to locate an image header"""
2667 data = self._DoReadFile('005_simple.dts')
2668 self.assertEqual(None, image_header.LocateHeaderOffset(data))
2669
Simon Glassffded752019-07-08 14:25:46 -06002670 def testReadImage(self):
2671 """Test reading an image and accessing its FDT map"""
2672 self._CheckLz4()
2673 data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07002674 image_fname = tools.get_output_filename('image.bin')
Simon Glassffded752019-07-08 14:25:46 -06002675 orig_image = control.images['image']
2676 image = Image.FromFile(image_fname)
2677 self.assertEqual(orig_image.GetEntries().keys(),
2678 image.GetEntries().keys())
2679
2680 orig_entry = orig_image.GetEntries()['fdtmap']
2681 entry = image.GetEntries()['fdtmap']
2682 self.assertEquals(orig_entry.offset, entry.offset)
2683 self.assertEquals(orig_entry.size, entry.size)
2684 self.assertEquals(orig_entry.image_pos, entry.image_pos)
2685
2686 def testReadImageNoHeader(self):
2687 """Test accessing an image's FDT map without an image header"""
2688 self._CheckLz4()
2689 data = self._DoReadFileRealDtb('129_decode_image_nohdr.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07002690 image_fname = tools.get_output_filename('image.bin')
Simon Glassffded752019-07-08 14:25:46 -06002691 image = Image.FromFile(image_fname)
2692 self.assertTrue(isinstance(image, Image))
Simon Glass10f9d002019-07-20 12:23:50 -06002693 self.assertEqual('image', image.image_name[-5:])
Simon Glassffded752019-07-08 14:25:46 -06002694
2695 def testReadImageFail(self):
2696 """Test failing to read an image image's FDT map"""
2697 self._DoReadFile('005_simple.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07002698 image_fname = tools.get_output_filename('image.bin')
Simon Glassffded752019-07-08 14:25:46 -06002699 with self.assertRaises(ValueError) as e:
2700 image = Image.FromFile(image_fname)
2701 self.assertIn("Cannot find FDT map in image", str(e.exception))
Simon Glasse073d4e2019-07-08 13:18:56 -06002702
Simon Glass61f564d2019-07-08 14:25:48 -06002703 def testListCmd(self):
2704 """Test listing the files in an image using an Fdtmap"""
2705 self._CheckLz4()
2706 data = self._DoReadFileRealDtb('130_list_fdtmap.dts')
2707
2708 # lz4 compression size differs depending on the version
2709 image = control.images['image']
2710 entries = image.GetEntries()
2711 section_size = entries['section'].size
2712 fdt_size = entries['section'].GetEntries()['u-boot-dtb'].size
2713 fdtmap_offset = entries['fdtmap'].offset
2714
Simon Glassf86a7362019-07-20 12:24:10 -06002715 try:
2716 tmpdir, updated_fname = self._SetupImageInTmpdir()
2717 with test_util.capture_sys_output() as (stdout, stderr):
2718 self._DoBinman('ls', '-i', updated_fname)
2719 finally:
2720 shutil.rmtree(tmpdir)
Simon Glass61f564d2019-07-08 14:25:48 -06002721 lines = stdout.getvalue().splitlines()
2722 expected = [
2723'Name Image-pos Size Entry-type Offset Uncomp-size',
2724'----------------------------------------------------------------------',
2725'main-section 0 c00 section 0',
2726' u-boot 0 4 u-boot 0',
2727' section 100 %x section 100' % section_size,
2728' cbfs 100 400 cbfs 0',
2729' u-boot 138 4 u-boot 38',
Simon Glassb6ee0cf2019-10-31 07:43:03 -06002730' u-boot-dtb 180 105 u-boot-dtb 80 3c9',
Simon Glass61f564d2019-07-08 14:25:48 -06002731' u-boot-dtb 500 %x u-boot-dtb 400 3c9' % fdt_size,
Simon Glassb6ee0cf2019-10-31 07:43:03 -06002732' fdtmap %x 3bd fdtmap %x' %
Simon Glass61f564d2019-07-08 14:25:48 -06002733 (fdtmap_offset, fdtmap_offset),
2734' image-header bf8 8 image-header bf8',
2735 ]
2736 self.assertEqual(expected, lines)
2737
2738 def testListCmdFail(self):
2739 """Test failing to list an image"""
2740 self._DoReadFile('005_simple.dts')
Simon Glassf86a7362019-07-20 12:24:10 -06002741 try:
2742 tmpdir, updated_fname = self._SetupImageInTmpdir()
2743 with self.assertRaises(ValueError) as e:
2744 self._DoBinman('ls', '-i', updated_fname)
2745 finally:
2746 shutil.rmtree(tmpdir)
Simon Glass61f564d2019-07-08 14:25:48 -06002747 self.assertIn("Cannot find FDT map in image", str(e.exception))
2748
2749 def _RunListCmd(self, paths, expected):
2750 """List out entries and check the result
2751
2752 Args:
2753 paths: List of paths to pass to the list command
2754 expected: Expected list of filenames to be returned, in order
2755 """
2756 self._CheckLz4()
2757 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07002758 image_fname = tools.get_output_filename('image.bin')
Simon Glass61f564d2019-07-08 14:25:48 -06002759 image = Image.FromFile(image_fname)
2760 lines = image.GetListEntries(paths)[1]
2761 files = [line[0].strip() for line in lines[1:]]
2762 self.assertEqual(expected, files)
2763
2764 def testListCmdSection(self):
2765 """Test listing the files in a section"""
2766 self._RunListCmd(['section'],
2767 ['section', 'cbfs', 'u-boot', 'u-boot-dtb', 'u-boot-dtb'])
2768
2769 def testListCmdFile(self):
2770 """Test listing a particular file"""
2771 self._RunListCmd(['*u-boot-dtb'], ['u-boot-dtb', 'u-boot-dtb'])
2772
2773 def testListCmdWildcard(self):
2774 """Test listing a wildcarded file"""
2775 self._RunListCmd(['*boot*'],
2776 ['u-boot', 'u-boot', 'u-boot-dtb', 'u-boot-dtb'])
2777
2778 def testListCmdWildcardMulti(self):
2779 """Test listing a wildcarded file"""
2780 self._RunListCmd(['*cb*', '*head*'],
2781 ['cbfs', 'u-boot', 'u-boot-dtb', 'image-header'])
2782
2783 def testListCmdEmpty(self):
2784 """Test listing a wildcarded file"""
2785 self._RunListCmd(['nothing'], [])
2786
2787 def testListCmdPath(self):
2788 """Test listing the files in a sub-entry of a section"""
2789 self._RunListCmd(['section/cbfs'], ['cbfs', 'u-boot', 'u-boot-dtb'])
2790
Simon Glassf667e452019-07-08 14:25:50 -06002791 def _RunExtractCmd(self, entry_name, decomp=True):
2792 """Extract an entry from an image
2793
2794 Args:
2795 entry_name: Entry name to extract
2796 decomp: True to decompress the data if compressed, False to leave
2797 it in its raw uncompressed format
2798
2799 Returns:
2800 data from entry
2801 """
2802 self._CheckLz4()
2803 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07002804 image_fname = tools.get_output_filename('image.bin')
Simon Glassf667e452019-07-08 14:25:50 -06002805 return control.ReadEntry(image_fname, entry_name, decomp)
2806
2807 def testExtractSimple(self):
2808 """Test extracting a single file"""
2809 data = self._RunExtractCmd('u-boot')
2810 self.assertEqual(U_BOOT_DATA, data)
2811
Simon Glass71ce0ba2019-07-08 14:25:52 -06002812 def testExtractSection(self):
2813 """Test extracting the files in a section"""
2814 data = self._RunExtractCmd('section')
2815 cbfs_data = data[:0x400]
2816 cbfs = cbfs_util.CbfsReader(cbfs_data)
Simon Glassb6ee0cf2019-10-31 07:43:03 -06002817 self.assertEqual(['u-boot', 'u-boot-dtb', ''], list(cbfs.files.keys()))
Simon Glass71ce0ba2019-07-08 14:25:52 -06002818 dtb_data = data[0x400:]
2819 dtb = self._decompress(dtb_data)
2820 self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
2821
2822 def testExtractCompressed(self):
2823 """Test extracting compressed data"""
2824 data = self._RunExtractCmd('section/u-boot-dtb')
2825 self.assertEqual(EXTRACT_DTB_SIZE, len(data))
2826
2827 def testExtractRaw(self):
2828 """Test extracting compressed data without decompressing it"""
2829 data = self._RunExtractCmd('section/u-boot-dtb', decomp=False)
2830 dtb = self._decompress(data)
2831 self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
2832
2833 def testExtractCbfs(self):
2834 """Test extracting CBFS data"""
2835 data = self._RunExtractCmd('section/cbfs/u-boot')
2836 self.assertEqual(U_BOOT_DATA, data)
2837
2838 def testExtractCbfsCompressed(self):
2839 """Test extracting CBFS compressed data"""
2840 data = self._RunExtractCmd('section/cbfs/u-boot-dtb')
2841 self.assertEqual(EXTRACT_DTB_SIZE, len(data))
2842
2843 def testExtractCbfsRaw(self):
2844 """Test extracting CBFS compressed data without decompressing it"""
2845 data = self._RunExtractCmd('section/cbfs/u-boot-dtb', decomp=False)
Simon Glass0d1e95a2022-01-09 20:14:04 -07002846 dtb = comp_util.decompress(data, 'lzma', with_header=False)
Simon Glass71ce0ba2019-07-08 14:25:52 -06002847 self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
2848
Simon Glassf667e452019-07-08 14:25:50 -06002849 def testExtractBadEntry(self):
2850 """Test extracting a bad section path"""
2851 with self.assertRaises(ValueError) as e:
2852 self._RunExtractCmd('section/does-not-exist')
2853 self.assertIn("Entry 'does-not-exist' not found in '/section'",
2854 str(e.exception))
2855
2856 def testExtractMissingFile(self):
2857 """Test extracting file that does not exist"""
2858 with self.assertRaises(IOError) as e:
2859 control.ReadEntry('missing-file', 'name')
2860
2861 def testExtractBadFile(self):
2862 """Test extracting an invalid file"""
2863 fname = os.path.join(self._indir, 'badfile')
Simon Glassc1aa66e2022-01-29 14:14:04 -07002864 tools.write_file(fname, b'')
Simon Glassf667e452019-07-08 14:25:50 -06002865 with self.assertRaises(ValueError) as e:
2866 control.ReadEntry(fname, 'name')
2867
Simon Glass71ce0ba2019-07-08 14:25:52 -06002868 def testExtractCmd(self):
2869 """Test extracting a file fron an image on the command line"""
2870 self._CheckLz4()
2871 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass71ce0ba2019-07-08 14:25:52 -06002872 fname = os.path.join(self._indir, 'output.extact')
Simon Glassf86a7362019-07-20 12:24:10 -06002873 try:
2874 tmpdir, updated_fname = self._SetupImageInTmpdir()
2875 with test_util.capture_sys_output() as (stdout, stderr):
2876 self._DoBinman('extract', '-i', updated_fname, 'u-boot',
2877 '-f', fname)
2878 finally:
2879 shutil.rmtree(tmpdir)
Simon Glassc1aa66e2022-01-29 14:14:04 -07002880 data = tools.read_file(fname)
Simon Glass71ce0ba2019-07-08 14:25:52 -06002881 self.assertEqual(U_BOOT_DATA, data)
2882
2883 def testExtractOneEntry(self):
2884 """Test extracting a single entry fron an image """
2885 self._CheckLz4()
2886 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07002887 image_fname = tools.get_output_filename('image.bin')
Simon Glass71ce0ba2019-07-08 14:25:52 -06002888 fname = os.path.join(self._indir, 'output.extact')
2889 control.ExtractEntries(image_fname, fname, None, ['u-boot'])
Simon Glassc1aa66e2022-01-29 14:14:04 -07002890 data = tools.read_file(fname)
Simon Glass71ce0ba2019-07-08 14:25:52 -06002891 self.assertEqual(U_BOOT_DATA, data)
2892
2893 def _CheckExtractOutput(self, decomp):
2894 """Helper to test file output with and without decompression
2895
2896 Args:
2897 decomp: True to decompress entry data, False to output it raw
2898 """
2899 def _CheckPresent(entry_path, expect_data, expect_size=None):
2900 """Check and remove expected file
2901
2902 This checks the data/size of a file and removes the file both from
2903 the outfiles set and from the output directory. Once all files are
2904 processed, both the set and directory should be empty.
2905
2906 Args:
2907 entry_path: Entry path
2908 expect_data: Data to expect in file, or None to skip check
2909 expect_size: Size of data to expect in file, or None to skip
2910 """
2911 path = os.path.join(outdir, entry_path)
Simon Glassc1aa66e2022-01-29 14:14:04 -07002912 data = tools.read_file(path)
Simon Glass71ce0ba2019-07-08 14:25:52 -06002913 os.remove(path)
2914 if expect_data:
2915 self.assertEqual(expect_data, data)
2916 elif expect_size:
2917 self.assertEqual(expect_size, len(data))
2918 outfiles.remove(path)
2919
2920 def _CheckDirPresent(name):
2921 """Remove expected directory
2922
2923 This gives an error if the directory does not exist as expected
2924
2925 Args:
2926 name: Name of directory to remove
2927 """
2928 path = os.path.join(outdir, name)
2929 os.rmdir(path)
2930
2931 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07002932 image_fname = tools.get_output_filename('image.bin')
Simon Glass71ce0ba2019-07-08 14:25:52 -06002933 outdir = os.path.join(self._indir, 'extract')
2934 einfos = control.ExtractEntries(image_fname, None, outdir, [], decomp)
2935
2936 # Create a set of all file that were output (should be 9)
2937 outfiles = set()
2938 for root, dirs, files in os.walk(outdir):
2939 outfiles |= set([os.path.join(root, fname) for fname in files])
2940 self.assertEqual(9, len(outfiles))
2941 self.assertEqual(9, len(einfos))
2942
2943 image = control.images['image']
2944 entries = image.GetEntries()
2945
2946 # Check the 9 files in various ways
2947 section = entries['section']
2948 section_entries = section.GetEntries()
2949 cbfs_entries = section_entries['cbfs'].GetEntries()
2950 _CheckPresent('u-boot', U_BOOT_DATA)
2951 _CheckPresent('section/cbfs/u-boot', U_BOOT_DATA)
2952 dtb_len = EXTRACT_DTB_SIZE
2953 if not decomp:
2954 dtb_len = cbfs_entries['u-boot-dtb'].size
2955 _CheckPresent('section/cbfs/u-boot-dtb', None, dtb_len)
2956 if not decomp:
2957 dtb_len = section_entries['u-boot-dtb'].size
2958 _CheckPresent('section/u-boot-dtb', None, dtb_len)
2959
2960 fdtmap = entries['fdtmap']
2961 _CheckPresent('fdtmap', fdtmap.data)
2962 hdr = entries['image-header']
2963 _CheckPresent('image-header', hdr.data)
2964
2965 _CheckPresent('section/root', section.data)
2966 cbfs = section_entries['cbfs']
2967 _CheckPresent('section/cbfs/root', cbfs.data)
Simon Glassc1aa66e2022-01-29 14:14:04 -07002968 data = tools.read_file(image_fname)
Simon Glass71ce0ba2019-07-08 14:25:52 -06002969 _CheckPresent('root', data)
2970
2971 # There should be no files left. Remove all the directories to check.
2972 # If there are any files/dirs remaining, one of these checks will fail.
2973 self.assertEqual(0, len(outfiles))
2974 _CheckDirPresent('section/cbfs')
2975 _CheckDirPresent('section')
2976 _CheckDirPresent('')
2977 self.assertFalse(os.path.exists(outdir))
2978
2979 def testExtractAllEntries(self):
2980 """Test extracting all entries"""
2981 self._CheckLz4()
2982 self._CheckExtractOutput(decomp=True)
2983
2984 def testExtractAllEntriesRaw(self):
2985 """Test extracting all entries without decompressing them"""
2986 self._CheckLz4()
2987 self._CheckExtractOutput(decomp=False)
2988
2989 def testExtractSelectedEntries(self):
2990 """Test extracting some entries"""
2991 self._CheckLz4()
2992 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07002993 image_fname = tools.get_output_filename('image.bin')
Simon Glass71ce0ba2019-07-08 14:25:52 -06002994 outdir = os.path.join(self._indir, 'extract')
2995 einfos = control.ExtractEntries(image_fname, None, outdir,
2996 ['*cb*', '*head*'])
2997
2998 # File output is tested by testExtractAllEntries(), so just check that
2999 # the expected entries are selected
3000 names = [einfo.name for einfo in einfos]
3001 self.assertEqual(names,
3002 ['cbfs', 'u-boot', 'u-boot-dtb', 'image-header'])
3003
3004 def testExtractNoEntryPaths(self):
3005 """Test extracting some entries"""
3006 self._CheckLz4()
3007 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07003008 image_fname = tools.get_output_filename('image.bin')
Simon Glass71ce0ba2019-07-08 14:25:52 -06003009 with self.assertRaises(ValueError) as e:
3010 control.ExtractEntries(image_fname, 'fname', None, [])
Simon Glassbb5edc12019-07-20 12:24:14 -06003011 self.assertIn('Must specify an entry path to write with -f',
Simon Glass71ce0ba2019-07-08 14:25:52 -06003012 str(e.exception))
3013
3014 def testExtractTooManyEntryPaths(self):
3015 """Test extracting some entries"""
3016 self._CheckLz4()
3017 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07003018 image_fname = tools.get_output_filename('image.bin')
Simon Glass71ce0ba2019-07-08 14:25:52 -06003019 with self.assertRaises(ValueError) as e:
3020 control.ExtractEntries(image_fname, 'fname', None, ['a', 'b'])
Simon Glassbb5edc12019-07-20 12:24:14 -06003021 self.assertIn('Must specify exactly one entry path to write with -f',
Simon Glass71ce0ba2019-07-08 14:25:52 -06003022 str(e.exception))
3023
Simon Glasse2705fa2019-07-08 14:25:53 -06003024 def testPackAlignSection(self):
3025 """Test that sections can have alignment"""
3026 self._DoReadFile('131_pack_align_section.dts')
3027
3028 self.assertIn('image', control.images)
3029 image = control.images['image']
3030 entries = image.GetEntries()
3031 self.assertEqual(3, len(entries))
3032
3033 # First u-boot
3034 self.assertIn('u-boot', entries)
3035 entry = entries['u-boot']
3036 self.assertEqual(0, entry.offset)
3037 self.assertEqual(0, entry.image_pos)
3038 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3039 self.assertEqual(len(U_BOOT_DATA), entry.size)
3040
3041 # Section0
3042 self.assertIn('section0', entries)
3043 section0 = entries['section0']
3044 self.assertEqual(0x10, section0.offset)
3045 self.assertEqual(0x10, section0.image_pos)
3046 self.assertEqual(len(U_BOOT_DATA), section0.size)
3047
3048 # Second u-boot
3049 section_entries = section0.GetEntries()
3050 self.assertIn('u-boot', section_entries)
3051 entry = section_entries['u-boot']
3052 self.assertEqual(0, entry.offset)
3053 self.assertEqual(0x10, entry.image_pos)
3054 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3055 self.assertEqual(len(U_BOOT_DATA), entry.size)
3056
3057 # Section1
3058 self.assertIn('section1', entries)
3059 section1 = entries['section1']
3060 self.assertEqual(0x14, section1.offset)
3061 self.assertEqual(0x14, section1.image_pos)
3062 self.assertEqual(0x20, section1.size)
3063
3064 # Second u-boot
3065 section_entries = section1.GetEntries()
3066 self.assertIn('u-boot', section_entries)
3067 entry = section_entries['u-boot']
3068 self.assertEqual(0, entry.offset)
3069 self.assertEqual(0x14, entry.image_pos)
3070 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3071 self.assertEqual(len(U_BOOT_DATA), entry.size)
3072
3073 # Section2
3074 self.assertIn('section2', section_entries)
3075 section2 = section_entries['section2']
3076 self.assertEqual(0x4, section2.offset)
3077 self.assertEqual(0x18, section2.image_pos)
3078 self.assertEqual(4, section2.size)
3079
3080 # Third u-boot
3081 section_entries = section2.GetEntries()
3082 self.assertIn('u-boot', section_entries)
3083 entry = section_entries['u-boot']
3084 self.assertEqual(0, entry.offset)
3085 self.assertEqual(0x18, entry.image_pos)
3086 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3087 self.assertEqual(len(U_BOOT_DATA), entry.size)
3088
Simon Glass51014aa2019-07-20 12:23:56 -06003089 def _RunReplaceCmd(self, entry_name, data, decomp=True, allow_resize=True,
3090 dts='132_replace.dts'):
Simon Glass10f9d002019-07-20 12:23:50 -06003091 """Replace an entry in an image
3092
3093 This writes the entry data to update it, then opens the updated file and
3094 returns the value that it now finds there.
3095
3096 Args:
3097 entry_name: Entry name to replace
3098 data: Data to replace it with
3099 decomp: True to compress the data if needed, False if data is
3100 already compressed so should be used as is
Simon Glass51014aa2019-07-20 12:23:56 -06003101 allow_resize: True to allow entries to change size, False to raise
3102 an exception
Simon Glass10f9d002019-07-20 12:23:50 -06003103
3104 Returns:
3105 Tuple:
3106 data from entry
3107 data from fdtmap (excluding header)
Simon Glass51014aa2019-07-20 12:23:56 -06003108 Image object that was modified
Simon Glass10f9d002019-07-20 12:23:50 -06003109 """
Simon Glass51014aa2019-07-20 12:23:56 -06003110 dtb_data = self._DoReadFileDtb(dts, use_real_dtb=True,
Simon Glass10f9d002019-07-20 12:23:50 -06003111 update_dtb=True)[1]
3112
3113 self.assertIn('image', control.images)
3114 image = control.images['image']
3115 entries = image.GetEntries()
3116 orig_dtb_data = entries['u-boot-dtb'].data
3117 orig_fdtmap_data = entries['fdtmap'].data
3118
Simon Glassc1aa66e2022-01-29 14:14:04 -07003119 image_fname = tools.get_output_filename('image.bin')
3120 updated_fname = tools.get_output_filename('image-updated.bin')
3121 tools.write_file(updated_fname, tools.read_file(image_fname))
Simon Glass51014aa2019-07-20 12:23:56 -06003122 image = control.WriteEntry(updated_fname, entry_name, data, decomp,
3123 allow_resize)
Simon Glass10f9d002019-07-20 12:23:50 -06003124 data = control.ReadEntry(updated_fname, entry_name, decomp)
3125
Simon Glass51014aa2019-07-20 12:23:56 -06003126 # The DT data should not change unless resized:
3127 if not allow_resize:
3128 new_dtb_data = entries['u-boot-dtb'].data
3129 self.assertEqual(new_dtb_data, orig_dtb_data)
3130 new_fdtmap_data = entries['fdtmap'].data
3131 self.assertEqual(new_fdtmap_data, orig_fdtmap_data)
Simon Glass10f9d002019-07-20 12:23:50 -06003132
Simon Glass51014aa2019-07-20 12:23:56 -06003133 return data, orig_fdtmap_data[fdtmap.FDTMAP_HDR_LEN:], image
Simon Glass10f9d002019-07-20 12:23:50 -06003134
3135 def testReplaceSimple(self):
3136 """Test replacing a single file"""
3137 expected = b'x' * len(U_BOOT_DATA)
Simon Glass51014aa2019-07-20 12:23:56 -06003138 data, expected_fdtmap, _ = self._RunReplaceCmd('u-boot', expected,
3139 allow_resize=False)
Simon Glass10f9d002019-07-20 12:23:50 -06003140 self.assertEqual(expected, data)
3141
3142 # Test that the state looks right. There should be an FDT for the fdtmap
3143 # that we jsut read back in, and it should match what we find in the
3144 # 'control' tables. Checking for an FDT that does not exist should
3145 # return None.
3146 path, fdtmap = state.GetFdtContents('fdtmap')
Simon Glass51014aa2019-07-20 12:23:56 -06003147 self.assertIsNotNone(path)
Simon Glass10f9d002019-07-20 12:23:50 -06003148 self.assertEqual(expected_fdtmap, fdtmap)
3149
3150 dtb = state.GetFdtForEtype('fdtmap')
3151 self.assertEqual(dtb.GetContents(), fdtmap)
3152
3153 missing_path, missing_fdtmap = state.GetFdtContents('missing')
3154 self.assertIsNone(missing_path)
3155 self.assertIsNone(missing_fdtmap)
3156
3157 missing_dtb = state.GetFdtForEtype('missing')
3158 self.assertIsNone(missing_dtb)
3159
3160 self.assertEqual('/binman', state.fdt_path_prefix)
3161
3162 def testReplaceResizeFail(self):
3163 """Test replacing a file by something larger"""
3164 expected = U_BOOT_DATA + b'x'
3165 with self.assertRaises(ValueError) as e:
Simon Glass51014aa2019-07-20 12:23:56 -06003166 self._RunReplaceCmd('u-boot', expected, allow_resize=False,
3167 dts='139_replace_repack.dts')
Simon Glass10f9d002019-07-20 12:23:50 -06003168 self.assertIn("Node '/u-boot': Entry data size does not match, but resize is disabled",
3169 str(e.exception))
3170
3171 def testReplaceMulti(self):
3172 """Test replacing entry data where multiple images are generated"""
3173 data = self._DoReadFileDtb('133_replace_multi.dts', use_real_dtb=True,
3174 update_dtb=True)[0]
3175 expected = b'x' * len(U_BOOT_DATA)
Simon Glassc1aa66e2022-01-29 14:14:04 -07003176 updated_fname = tools.get_output_filename('image-updated.bin')
3177 tools.write_file(updated_fname, data)
Simon Glass10f9d002019-07-20 12:23:50 -06003178 entry_name = 'u-boot'
Simon Glass51014aa2019-07-20 12:23:56 -06003179 control.WriteEntry(updated_fname, entry_name, expected,
3180 allow_resize=False)
Simon Glass10f9d002019-07-20 12:23:50 -06003181 data = control.ReadEntry(updated_fname, entry_name)
3182 self.assertEqual(expected, data)
3183
3184 # Check the state looks right.
3185 self.assertEqual('/binman/image', state.fdt_path_prefix)
3186
3187 # Now check we can write the first image
Simon Glassc1aa66e2022-01-29 14:14:04 -07003188 image_fname = tools.get_output_filename('first-image.bin')
3189 updated_fname = tools.get_output_filename('first-updated.bin')
3190 tools.write_file(updated_fname, tools.read_file(image_fname))
Simon Glass10f9d002019-07-20 12:23:50 -06003191 entry_name = 'u-boot'
Simon Glass51014aa2019-07-20 12:23:56 -06003192 control.WriteEntry(updated_fname, entry_name, expected,
3193 allow_resize=False)
Simon Glass10f9d002019-07-20 12:23:50 -06003194 data = control.ReadEntry(updated_fname, entry_name)
3195 self.assertEqual(expected, data)
3196
3197 # Check the state looks right.
3198 self.assertEqual('/binman/first-image', state.fdt_path_prefix)
Simon Glass8beb11e2019-07-08 14:25:47 -06003199
Simon Glass12bb1a92019-07-20 12:23:51 -06003200 def testUpdateFdtAllRepack(self):
3201 """Test that all device trees are updated with offset/size info"""
3202 data = self._DoReadFileRealDtb('134_fdt_update_all_repack.dts')
3203 SECTION_SIZE = 0x300
3204 DTB_SIZE = 602
3205 FDTMAP_SIZE = 608
3206 base_expected = {
3207 'offset': 0,
3208 'size': SECTION_SIZE + DTB_SIZE * 2 + FDTMAP_SIZE,
3209 'image-pos': 0,
3210 'section:offset': 0,
3211 'section:size': SECTION_SIZE,
3212 'section:image-pos': 0,
3213 'section/u-boot-dtb:offset': 4,
3214 'section/u-boot-dtb:size': 636,
3215 'section/u-boot-dtb:image-pos': 4,
3216 'u-boot-spl-dtb:offset': SECTION_SIZE,
3217 'u-boot-spl-dtb:size': DTB_SIZE,
3218 'u-boot-spl-dtb:image-pos': SECTION_SIZE,
3219 'u-boot-tpl-dtb:offset': SECTION_SIZE + DTB_SIZE,
3220 'u-boot-tpl-dtb:image-pos': SECTION_SIZE + DTB_SIZE,
3221 'u-boot-tpl-dtb:size': DTB_SIZE,
3222 'fdtmap:offset': SECTION_SIZE + DTB_SIZE * 2,
3223 'fdtmap:size': FDTMAP_SIZE,
3224 'fdtmap:image-pos': SECTION_SIZE + DTB_SIZE * 2,
3225 }
3226 main_expected = {
3227 'section:orig-size': SECTION_SIZE,
3228 'section/u-boot-dtb:orig-offset': 4,
3229 }
3230
3231 # We expect three device-tree files in the output, with the first one
3232 # within a fixed-size section.
3233 # Read them in sequence. We look for an 'spl' property in the SPL tree,
3234 # and 'tpl' in the TPL tree, to make sure they are distinct from the
3235 # main U-Boot tree. All three should have the same positions and offset
3236 # except that the main tree should include the main_expected properties
3237 start = 4
3238 for item in ['', 'spl', 'tpl', None]:
3239 if item is None:
3240 start += 16 # Move past fdtmap header
3241 dtb = fdt.Fdt.FromData(data[start:])
3242 dtb.Scan()
3243 props = self._GetPropTree(dtb,
3244 BASE_DTB_PROPS + REPACK_DTB_PROPS + ['spl', 'tpl'],
3245 prefix='/' if item is None else '/binman/')
3246 expected = dict(base_expected)
3247 if item:
3248 expected[item] = 0
3249 else:
3250 # Main DTB and fdtdec should include the 'orig-' properties
3251 expected.update(main_expected)
3252 # Helpful for debugging:
3253 #for prop in sorted(props):
3254 #print('prop %s %s %s' % (prop, props[prop], expected[prop]))
3255 self.assertEqual(expected, props)
3256 if item == '':
3257 start = SECTION_SIZE
3258 else:
3259 start += dtb._fdt_obj.totalsize()
3260
Simon Glasseba1f0c2019-07-20 12:23:55 -06003261 def testFdtmapHeaderMiddle(self):
3262 """Test an FDT map in the middle of an image when it should be at end"""
3263 with self.assertRaises(ValueError) as e:
3264 self._DoReadFileRealDtb('135_fdtmap_hdr_middle.dts')
3265 self.assertIn("Invalid sibling order 'middle' for image-header: Must be at 'end' to match location",
3266 str(e.exception))
3267
3268 def testFdtmapHeaderStartBad(self):
3269 """Test an FDT map in middle of an image when it should be at start"""
3270 with self.assertRaises(ValueError) as e:
3271 self._DoReadFileRealDtb('136_fdtmap_hdr_startbad.dts')
3272 self.assertIn("Invalid sibling order 'end' for image-header: Must be at 'start' to match location",
3273 str(e.exception))
3274
3275 def testFdtmapHeaderEndBad(self):
3276 """Test an FDT map at the start of an image when it should be at end"""
3277 with self.assertRaises(ValueError) as e:
3278 self._DoReadFileRealDtb('137_fdtmap_hdr_endbad.dts')
3279 self.assertIn("Invalid sibling order 'start' for image-header: Must be at 'end' to match location",
3280 str(e.exception))
3281
3282 def testFdtmapHeaderNoSize(self):
3283 """Test an image header at the end of an image with undefined size"""
3284 self._DoReadFileRealDtb('138_fdtmap_hdr_nosize.dts')
3285
Simon Glass51014aa2019-07-20 12:23:56 -06003286 def testReplaceResize(self):
3287 """Test replacing a single file in an entry with a larger file"""
3288 expected = U_BOOT_DATA + b'x'
3289 data, _, image = self._RunReplaceCmd('u-boot', expected,
3290 dts='139_replace_repack.dts')
3291 self.assertEqual(expected, data)
3292
3293 entries = image.GetEntries()
3294 dtb_data = entries['u-boot-dtb'].data
3295 dtb = fdt.Fdt.FromData(dtb_data)
3296 dtb.Scan()
3297
3298 # The u-boot section should now be larger in the dtb
3299 node = dtb.GetNode('/binman/u-boot')
3300 self.assertEqual(len(expected), fdt_util.GetInt(node, 'size'))
3301
3302 # Same for the fdtmap
3303 fdata = entries['fdtmap'].data
3304 fdtb = fdt.Fdt.FromData(fdata[fdtmap.FDTMAP_HDR_LEN:])
3305 fdtb.Scan()
3306 fnode = fdtb.GetNode('/u-boot')
3307 self.assertEqual(len(expected), fdt_util.GetInt(fnode, 'size'))
3308
3309 def testReplaceResizeNoRepack(self):
3310 """Test replacing an entry with a larger file when not allowed"""
3311 expected = U_BOOT_DATA + b'x'
3312 with self.assertRaises(ValueError) as e:
3313 self._RunReplaceCmd('u-boot', expected)
3314 self.assertIn('Entry data size does not match, but allow-repack is not present for this image',
3315 str(e.exception))
3316
Simon Glass61ec04f2019-07-20 12:23:58 -06003317 def testEntryShrink(self):
3318 """Test contracting an entry after it is packed"""
3319 try:
3320 state.SetAllowEntryContraction(True)
3321 data = self._DoReadFileDtb('140_entry_shrink.dts',
3322 update_dtb=True)[0]
3323 finally:
3324 state.SetAllowEntryContraction(False)
3325 self.assertEqual(b'a', data[:1])
3326 self.assertEqual(U_BOOT_DATA, data[1:1 + len(U_BOOT_DATA)])
3327 self.assertEqual(b'a', data[-1:])
3328
3329 def testEntryShrinkFail(self):
3330 """Test not being allowed to contract an entry after it is packed"""
3331 data = self._DoReadFileDtb('140_entry_shrink.dts', update_dtb=True)[0]
3332
3333 # In this case there is a spare byte at the end of the data. The size of
3334 # the contents is only 1 byte but we still have the size before it
3335 # shrunk.
3336 self.assertEqual(b'a\0', data[:2])
3337 self.assertEqual(U_BOOT_DATA, data[2:2 + len(U_BOOT_DATA)])
3338 self.assertEqual(b'a\0', data[-2:])
3339
Simon Glass27145fd2019-07-20 12:24:01 -06003340 def testDescriptorOffset(self):
3341 """Test that the Intel descriptor is always placed at at the start"""
3342 data = self._DoReadFileDtb('141_descriptor_offset.dts')
3343 image = control.images['image']
3344 entries = image.GetEntries()
3345 desc = entries['intel-descriptor']
3346 self.assertEqual(0xff800000, desc.offset);
3347 self.assertEqual(0xff800000, desc.image_pos);
3348
Simon Glasseb0f4a42019-07-20 12:24:06 -06003349 def testReplaceCbfs(self):
3350 """Test replacing a single file in CBFS without changing the size"""
3351 self._CheckLz4()
3352 expected = b'x' * len(U_BOOT_DATA)
3353 data = self._DoReadFileRealDtb('142_replace_cbfs.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07003354 updated_fname = tools.get_output_filename('image-updated.bin')
3355 tools.write_file(updated_fname, data)
Simon Glasseb0f4a42019-07-20 12:24:06 -06003356 entry_name = 'section/cbfs/u-boot'
3357 control.WriteEntry(updated_fname, entry_name, expected,
3358 allow_resize=True)
3359 data = control.ReadEntry(updated_fname, entry_name)
3360 self.assertEqual(expected, data)
3361
3362 def testReplaceResizeCbfs(self):
3363 """Test replacing a single file in CBFS with one of a different size"""
3364 self._CheckLz4()
3365 expected = U_BOOT_DATA + b'x'
3366 data = self._DoReadFileRealDtb('142_replace_cbfs.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07003367 updated_fname = tools.get_output_filename('image-updated.bin')
3368 tools.write_file(updated_fname, data)
Simon Glasseb0f4a42019-07-20 12:24:06 -06003369 entry_name = 'section/cbfs/u-boot'
3370 control.WriteEntry(updated_fname, entry_name, expected,
3371 allow_resize=True)
3372 data = control.ReadEntry(updated_fname, entry_name)
3373 self.assertEqual(expected, data)
3374
Simon Glassa6cb9952019-07-20 12:24:15 -06003375 def _SetupForReplace(self):
3376 """Set up some files to use to replace entries
3377
3378 This generates an image, copies it to a new file, extracts all the files
3379 in it and updates some of them
3380
3381 Returns:
3382 List
3383 Image filename
3384 Output directory
3385 Expected values for updated entries, each a string
3386 """
3387 data = self._DoReadFileRealDtb('143_replace_all.dts')
3388
Simon Glassc1aa66e2022-01-29 14:14:04 -07003389 updated_fname = tools.get_output_filename('image-updated.bin')
3390 tools.write_file(updated_fname, data)
Simon Glassa6cb9952019-07-20 12:24:15 -06003391
3392 outdir = os.path.join(self._indir, 'extract')
3393 einfos = control.ExtractEntries(updated_fname, None, outdir, [])
3394
3395 expected1 = b'x' + U_BOOT_DATA + b'y'
3396 u_boot_fname1 = os.path.join(outdir, 'u-boot')
Simon Glassc1aa66e2022-01-29 14:14:04 -07003397 tools.write_file(u_boot_fname1, expected1)
Simon Glassa6cb9952019-07-20 12:24:15 -06003398
3399 expected2 = b'a' + U_BOOT_DATA + b'b'
3400 u_boot_fname2 = os.path.join(outdir, 'u-boot2')
Simon Glassc1aa66e2022-01-29 14:14:04 -07003401 tools.write_file(u_boot_fname2, expected2)
Simon Glassa6cb9952019-07-20 12:24:15 -06003402
3403 expected_text = b'not the same text'
3404 text_fname = os.path.join(outdir, 'text')
Simon Glassc1aa66e2022-01-29 14:14:04 -07003405 tools.write_file(text_fname, expected_text)
Simon Glassa6cb9952019-07-20 12:24:15 -06003406
3407 dtb_fname = os.path.join(outdir, 'u-boot-dtb')
3408 dtb = fdt.FdtScan(dtb_fname)
3409 node = dtb.GetNode('/binman/text')
3410 node.AddString('my-property', 'the value')
3411 dtb.Sync(auto_resize=True)
3412 dtb.Flush()
3413
3414 return updated_fname, outdir, expected1, expected2, expected_text
3415
3416 def _CheckReplaceMultiple(self, entry_paths):
3417 """Handle replacing the contents of multiple entries
3418
3419 Args:
3420 entry_paths: List of entry paths to replace
3421
3422 Returns:
3423 List
3424 Dict of entries in the image:
3425 key: Entry name
3426 Value: Entry object
3427 Expected values for updated entries, each a string
3428 """
3429 updated_fname, outdir, expected1, expected2, expected_text = (
3430 self._SetupForReplace())
3431 control.ReplaceEntries(updated_fname, None, outdir, entry_paths)
3432
3433 image = Image.FromFile(updated_fname)
3434 image.LoadData()
3435 return image.GetEntries(), expected1, expected2, expected_text
3436
3437 def testReplaceAll(self):
3438 """Test replacing the contents of all entries"""
3439 entries, expected1, expected2, expected_text = (
3440 self._CheckReplaceMultiple([]))
3441 data = entries['u-boot'].data
3442 self.assertEqual(expected1, data)
3443
3444 data = entries['u-boot2'].data
3445 self.assertEqual(expected2, data)
3446
3447 data = entries['text'].data
3448 self.assertEqual(expected_text, data)
3449
3450 # Check that the device tree is updated
3451 data = entries['u-boot-dtb'].data
3452 dtb = fdt.Fdt.FromData(data)
3453 dtb.Scan()
3454 node = dtb.GetNode('/binman/text')
3455 self.assertEqual('the value', node.props['my-property'].value)
3456
3457 def testReplaceSome(self):
3458 """Test replacing the contents of a few entries"""
3459 entries, expected1, expected2, expected_text = (
3460 self._CheckReplaceMultiple(['u-boot2', 'text']))
3461
3462 # This one should not change
3463 data = entries['u-boot'].data
3464 self.assertEqual(U_BOOT_DATA, data)
3465
3466 data = entries['u-boot2'].data
3467 self.assertEqual(expected2, data)
3468
3469 data = entries['text'].data
3470 self.assertEqual(expected_text, data)
3471
3472 def testReplaceCmd(self):
3473 """Test replacing a file fron an image on the command line"""
3474 self._DoReadFileRealDtb('143_replace_all.dts')
3475
3476 try:
3477 tmpdir, updated_fname = self._SetupImageInTmpdir()
3478
3479 fname = os.path.join(tmpdir, 'update-u-boot.bin')
3480 expected = b'x' * len(U_BOOT_DATA)
Simon Glassc1aa66e2022-01-29 14:14:04 -07003481 tools.write_file(fname, expected)
Simon Glassa6cb9952019-07-20 12:24:15 -06003482
3483 self._DoBinman('replace', '-i', updated_fname, 'u-boot', '-f', fname)
Simon Glassc1aa66e2022-01-29 14:14:04 -07003484 data = tools.read_file(updated_fname)
Simon Glassa6cb9952019-07-20 12:24:15 -06003485 self.assertEqual(expected, data[:len(expected)])
3486 map_fname = os.path.join(tmpdir, 'image-updated.map')
3487 self.assertFalse(os.path.exists(map_fname))
3488 finally:
3489 shutil.rmtree(tmpdir)
3490
3491 def testReplaceCmdSome(self):
3492 """Test replacing some files fron an image on the command line"""
3493 updated_fname, outdir, expected1, expected2, expected_text = (
3494 self._SetupForReplace())
3495
3496 self._DoBinman('replace', '-i', updated_fname, '-I', outdir,
3497 'u-boot2', 'text')
3498
Simon Glassc1aa66e2022-01-29 14:14:04 -07003499 tools.prepare_output_dir(None)
Simon Glassa6cb9952019-07-20 12:24:15 -06003500 image = Image.FromFile(updated_fname)
3501 image.LoadData()
3502 entries = image.GetEntries()
3503
3504 # This one should not change
3505 data = entries['u-boot'].data
3506 self.assertEqual(U_BOOT_DATA, data)
3507
3508 data = entries['u-boot2'].data
3509 self.assertEqual(expected2, data)
3510
3511 data = entries['text'].data
3512 self.assertEqual(expected_text, data)
3513
3514 def testReplaceMissing(self):
3515 """Test replacing entries where the file is missing"""
3516 updated_fname, outdir, expected1, expected2, expected_text = (
3517 self._SetupForReplace())
3518
3519 # Remove one of the files, to generate a warning
3520 u_boot_fname1 = os.path.join(outdir, 'u-boot')
3521 os.remove(u_boot_fname1)
3522
3523 with test_util.capture_sys_output() as (stdout, stderr):
3524 control.ReplaceEntries(updated_fname, None, outdir, [])
3525 self.assertIn("Skipping entry '/u-boot' from missing file",
Simon Glass38fdb4c2020-07-09 18:39:39 -06003526 stderr.getvalue())
Simon Glassa6cb9952019-07-20 12:24:15 -06003527
3528 def testReplaceCmdMap(self):
3529 """Test replacing a file fron an image on the command line"""
3530 self._DoReadFileRealDtb('143_replace_all.dts')
3531
3532 try:
3533 tmpdir, updated_fname = self._SetupImageInTmpdir()
3534
3535 fname = os.path.join(self._indir, 'update-u-boot.bin')
3536 expected = b'x' * len(U_BOOT_DATA)
Simon Glassc1aa66e2022-01-29 14:14:04 -07003537 tools.write_file(fname, expected)
Simon Glassa6cb9952019-07-20 12:24:15 -06003538
3539 self._DoBinman('replace', '-i', updated_fname, 'u-boot',
3540 '-f', fname, '-m')
3541 map_fname = os.path.join(tmpdir, 'image-updated.map')
3542 self.assertTrue(os.path.exists(map_fname))
3543 finally:
3544 shutil.rmtree(tmpdir)
3545
3546 def testReplaceNoEntryPaths(self):
3547 """Test replacing an entry without an entry path"""
3548 self._DoReadFileRealDtb('143_replace_all.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07003549 image_fname = tools.get_output_filename('image.bin')
Simon Glassa6cb9952019-07-20 12:24:15 -06003550 with self.assertRaises(ValueError) as e:
3551 control.ReplaceEntries(image_fname, 'fname', None, [])
3552 self.assertIn('Must specify an entry path to read with -f',
3553 str(e.exception))
3554
3555 def testReplaceTooManyEntryPaths(self):
3556 """Test extracting some entries"""
3557 self._DoReadFileRealDtb('143_replace_all.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07003558 image_fname = tools.get_output_filename('image.bin')
Simon Glassa6cb9952019-07-20 12:24:15 -06003559 with self.assertRaises(ValueError) as e:
3560 control.ReplaceEntries(image_fname, 'fname', None, ['a', 'b'])
3561 self.assertIn('Must specify exactly one entry path to write with -f',
3562 str(e.exception))
3563
Simon Glass2250ee62019-08-24 07:22:48 -06003564 def testPackReset16(self):
3565 """Test that an image with an x86 reset16 region can be created"""
3566 data = self._DoReadFile('144_x86_reset16.dts')
3567 self.assertEqual(X86_RESET16_DATA, data[:len(X86_RESET16_DATA)])
3568
3569 def testPackReset16Spl(self):
3570 """Test that an image with an x86 reset16-spl region can be created"""
3571 data = self._DoReadFile('145_x86_reset16_spl.dts')
3572 self.assertEqual(X86_RESET16_SPL_DATA, data[:len(X86_RESET16_SPL_DATA)])
3573
3574 def testPackReset16Tpl(self):
3575 """Test that an image with an x86 reset16-tpl region can be created"""
3576 data = self._DoReadFile('146_x86_reset16_tpl.dts')
3577 self.assertEqual(X86_RESET16_TPL_DATA, data[:len(X86_RESET16_TPL_DATA)])
3578
Simon Glass5af12072019-08-24 07:22:50 -06003579 def testPackIntelFit(self):
3580 """Test that an image with an Intel FIT and pointer can be created"""
3581 data = self._DoReadFile('147_intel_fit.dts')
3582 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
3583 fit = data[16:32];
3584 self.assertEqual(b'_FIT_ \x01\x00\x00\x00\x00\x01\x80}' , fit)
3585 ptr = struct.unpack('<i', data[0x40:0x44])[0]
3586
3587 image = control.images['image']
3588 entries = image.GetEntries()
3589 expected_ptr = entries['intel-fit'].image_pos - (1 << 32)
3590 self.assertEqual(expected_ptr, ptr)
3591
3592 def testPackIntelFitMissing(self):
3593 """Test detection of a FIT pointer with not FIT region"""
3594 with self.assertRaises(ValueError) as e:
3595 self._DoReadFile('148_intel_fit_missing.dts')
3596 self.assertIn("'intel-fit-ptr' section must have an 'intel-fit' sibling",
3597 str(e.exception))
3598
Simon Glass7c150132019-11-06 17:22:44 -07003599 def _CheckSymbolsTplSection(self, dts, expected_vals):
3600 data = self._DoReadFile(dts)
3601 sym_values = struct.pack('<LQLL', *expected_vals)
Simon Glass2090f1e2019-08-24 07:23:00 -06003602 upto1 = 4 + len(U_BOOT_SPL_DATA)
Simon Glassc1aa66e2022-01-29 14:14:04 -07003603 expected1 = tools.get_bytes(0xff, 4) + sym_values + U_BOOT_SPL_DATA[20:]
Simon Glass2090f1e2019-08-24 07:23:00 -06003604 self.assertEqual(expected1, data[:upto1])
3605
3606 upto2 = upto1 + 1 + len(U_BOOT_SPL_DATA)
Simon Glassc1aa66e2022-01-29 14:14:04 -07003607 expected2 = tools.get_bytes(0xff, 1) + sym_values + U_BOOT_SPL_DATA[20:]
Simon Glass2090f1e2019-08-24 07:23:00 -06003608 self.assertEqual(expected2, data[upto1:upto2])
3609
Simon Glasseb0086f2019-08-24 07:23:04 -06003610 upto3 = 0x34 + len(U_BOOT_DATA)
Simon Glassc1aa66e2022-01-29 14:14:04 -07003611 expected3 = tools.get_bytes(0xff, 1) + U_BOOT_DATA
Simon Glass2090f1e2019-08-24 07:23:00 -06003612 self.assertEqual(expected3, data[upto2:upto3])
3613
Simon Glassb87064c2019-08-24 07:23:05 -06003614 expected4 = sym_values + U_BOOT_TPL_DATA[20:]
Simon Glass7c150132019-11-06 17:22:44 -07003615 self.assertEqual(expected4, data[upto3:upto3 + len(U_BOOT_TPL_DATA)])
3616
3617 def testSymbolsTplSection(self):
3618 """Test binman can assign symbols embedded in U-Boot TPL in a section"""
3619 self._SetupSplElf('u_boot_binman_syms')
3620 self._SetupTplElf('u_boot_binman_syms')
3621 self._CheckSymbolsTplSection('149_symbols_tpl.dts',
3622 [0x04, 0x1c, 0x10 + 0x34, 0x04])
3623
3624 def testSymbolsTplSectionX86(self):
3625 """Test binman can assign symbols in a section with end-at-4gb"""
3626 self._SetupSplElf('u_boot_binman_syms_x86')
3627 self._SetupTplElf('u_boot_binman_syms_x86')
3628 self._CheckSymbolsTplSection('155_symbols_tpl_x86.dts',
3629 [0xffffff04, 0xffffff1c, 0xffffff34,
3630 0x04])
Simon Glass2090f1e2019-08-24 07:23:00 -06003631
Simon Glassbf4d0e22019-08-24 07:23:03 -06003632 def testPackX86RomIfwiSectiom(self):
3633 """Test that a section can be placed in an IFWI region"""
3634 self._SetupIfwi('fitimage.bin')
3635 data = self._DoReadFile('151_x86_rom_ifwi_section.dts')
3636 self._CheckIfwi(data)
3637
Simon Glassea0fff92019-08-24 07:23:07 -06003638 def testPackFspM(self):
3639 """Test that an image with a FSP memory-init binary can be created"""
3640 data = self._DoReadFile('152_intel_fsp_m.dts')
3641 self.assertEqual(FSP_M_DATA, data[:len(FSP_M_DATA)])
3642
Simon Glassbc6a88f2019-10-20 21:31:35 -06003643 def testPackFspS(self):
3644 """Test that an image with a FSP silicon-init binary can be created"""
3645 data = self._DoReadFile('153_intel_fsp_s.dts')
3646 self.assertEqual(FSP_S_DATA, data[:len(FSP_S_DATA)])
Simon Glassea0fff92019-08-24 07:23:07 -06003647
Simon Glass998d1482019-10-20 21:31:36 -06003648 def testPackFspT(self):
3649 """Test that an image with a FSP temp-ram-init binary can be created"""
3650 data = self._DoReadFile('154_intel_fsp_t.dts')
3651 self.assertEqual(FSP_T_DATA, data[:len(FSP_T_DATA)])
3652
Simon Glass0dc706f2020-07-09 18:39:31 -06003653 def testMkimage(self):
3654 """Test using mkimage to build an image"""
3655 data = self._DoReadFile('156_mkimage.dts')
3656
3657 # Just check that the data appears in the file somewhere
3658 self.assertIn(U_BOOT_SPL_DATA, data)
3659
Simon Glass4f9ee832022-01-09 20:14:09 -07003660 def testMkimageMissing(self):
3661 """Test that binman still produces an image if mkimage is missing"""
3662 with test_util.capture_sys_output() as (_, stderr):
3663 self._DoTestFile('156_mkimage.dts',
3664 force_missing_bintools='mkimage')
3665 err = stderr.getvalue()
3666 self.assertRegex(err,
3667 "Image 'main-section'.*missing bintools.*: mkimage")
3668
Simon Glassce867ad2020-07-09 18:39:36 -06003669 def testExtblob(self):
3670 """Test an image with an external blob"""
3671 data = self._DoReadFile('157_blob_ext.dts')
3672 self.assertEqual(REFCODE_DATA, data)
3673
3674 def testExtblobMissing(self):
3675 """Test an image with a missing external blob"""
3676 with self.assertRaises(ValueError) as e:
3677 self._DoReadFile('158_blob_ext_missing.dts')
3678 self.assertIn("Filename 'missing-file' not found in input path",
3679 str(e.exception))
3680
Simon Glass4f9f1052020-07-09 18:39:38 -06003681 def testExtblobMissingOk(self):
3682 """Test an image with an missing external blob that is allowed"""
Simon Glassb1cca952020-07-09 18:39:40 -06003683 with test_util.capture_sys_output() as (stdout, stderr):
3684 self._DoTestFile('158_blob_ext_missing.dts', allow_missing=True)
3685 err = stderr.getvalue()
3686 self.assertRegex(err, "Image 'main-section'.*missing.*: blob-ext")
3687
3688 def testExtblobMissingOkSect(self):
3689 """Test an image with an missing external blob that is allowed"""
3690 with test_util.capture_sys_output() as (stdout, stderr):
3691 self._DoTestFile('159_blob_ext_missing_sect.dts',
3692 allow_missing=True)
3693 err = stderr.getvalue()
3694 self.assertRegex(err, "Image 'main-section'.*missing.*: "
3695 "blob-ext blob-ext2")
Simon Glass4f9f1052020-07-09 18:39:38 -06003696
Simon Glass0ba4b3d2020-07-09 18:39:41 -06003697 def testPackX86RomMeMissingDesc(self):
3698 """Test that an missing Intel descriptor entry is allowed"""
Simon Glass0ba4b3d2020-07-09 18:39:41 -06003699 with test_util.capture_sys_output() as (stdout, stderr):
Simon Glass52b10dd2020-07-25 15:11:19 -06003700 self._DoTestFile('164_x86_rom_me_missing.dts', allow_missing=True)
Simon Glass0ba4b3d2020-07-09 18:39:41 -06003701 err = stderr.getvalue()
3702 self.assertRegex(err,
3703 "Image 'main-section'.*missing.*: intel-descriptor")
3704
3705 def testPackX86RomMissingIfwi(self):
3706 """Test that an x86 ROM with Integrated Firmware Image can be created"""
3707 self._SetupIfwi('fitimage.bin')
3708 pathname = os.path.join(self._indir, 'fitimage.bin')
3709 os.remove(pathname)
3710 with test_util.capture_sys_output() as (stdout, stderr):
3711 self._DoTestFile('111_x86_rom_ifwi.dts', allow_missing=True)
3712 err = stderr.getvalue()
3713 self.assertRegex(err, "Image 'main-section'.*missing.*: intel-ifwi")
3714
Simon Glassb3295fd2020-07-09 18:39:42 -06003715 def testPackOverlap(self):
3716 """Test that zero-size overlapping regions are ignored"""
3717 self._DoTestFile('160_pack_overlap_zero.dts')
3718
Alper Nebi Yasak21353312022-02-08 01:08:04 +03003719 def _CheckSimpleFitData(self, fit_data, kernel_data, fdt1_data):
Simon Glassfdc34362020-07-09 18:39:45 -06003720 # The data should be inside the FIT
3721 dtb = fdt.Fdt.FromData(fit_data)
3722 dtb.Scan()
3723 fnode = dtb.GetNode('/images/kernel')
3724 self.assertIn('data', fnode.props)
3725
3726 fname = os.path.join(self._indir, 'fit_data.fit')
Simon Glassc1aa66e2022-01-29 14:14:04 -07003727 tools.write_file(fname, fit_data)
3728 out = tools.run('dumpimage', '-l', fname)
Simon Glassfdc34362020-07-09 18:39:45 -06003729
3730 # Check a few features to make sure the plumbing works. We don't need
3731 # to test the operation of mkimage or dumpimage here. First convert the
3732 # output into a dict where the keys are the fields printed by dumpimage
3733 # and the values are a list of values for each field
3734 lines = out.splitlines()
3735
3736 # Converts "Compression: gzip compressed" into two groups:
3737 # 'Compression' and 'gzip compressed'
3738 re_line = re.compile(r'^ *([^:]*)(?:: *(.*))?$')
3739 vals = collections.defaultdict(list)
3740 for line in lines:
3741 mat = re_line.match(line)
3742 vals[mat.group(1)].append(mat.group(2))
3743
3744 self.assertEquals('FIT description: test-desc', lines[0])
3745 self.assertIn('Created:', lines[1])
3746 self.assertIn('Image 0 (kernel)', vals)
3747 self.assertIn('Hash value', vals)
3748 data_sizes = vals.get('Data Size')
3749 self.assertIsNotNone(data_sizes)
3750 self.assertEqual(2, len(data_sizes))
3751 # Format is "4 Bytes = 0.00 KiB = 0.00 MiB" so take the first word
Alper Nebi Yasak21353312022-02-08 01:08:04 +03003752 self.assertEqual(len(kernel_data), int(data_sizes[0].split()[0]))
3753 self.assertEqual(len(fdt1_data), int(data_sizes[1].split()[0]))
3754
3755 def testSimpleFit(self):
3756 """Test an image with a FIT inside"""
3757 data = self._DoReadFile('161_fit.dts')
3758 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
3759 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
3760 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
3761
3762 self._CheckSimpleFitData(fit_data, U_BOOT_DATA, U_BOOT_SPL_DTB_DATA)
3763
3764 def testSimpleFitExpandsSubentries(self):
3765 """Test that FIT images expand their subentries"""
3766 data = self._DoReadFileDtb('161_fit.dts', use_expanded=True)[0]
3767 self.assertEqual(U_BOOT_EXP_DATA, data[:len(U_BOOT_EXP_DATA)])
3768 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
3769 fit_data = data[len(U_BOOT_EXP_DATA):-len(U_BOOT_NODTB_DATA)]
3770
3771 self._CheckSimpleFitData(fit_data, U_BOOT_EXP_DATA, U_BOOT_SPL_DTB_DATA)
Simon Glassfdc34362020-07-09 18:39:45 -06003772
Alper Nebi Yasak73092222022-02-08 01:08:08 +03003773 def testSimpleFitImagePos(self):
3774 """Test that we have correct image-pos for FIT subentries"""
3775 data, _, _, out_dtb_fname = self._DoReadFileDtb('161_fit.dts',
3776 update_dtb=True)
3777 dtb = fdt.Fdt(out_dtb_fname)
3778 dtb.Scan()
3779 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
3780
3781 self.assertEqual({
3782 'image-pos': 0,
3783 'offset': 0,
3784 'size': 1890,
3785
3786 'u-boot:image-pos': 0,
3787 'u-boot:offset': 0,
3788 'u-boot:size': 4,
3789
3790 'fit:image-pos': 4,
3791 'fit:offset': 4,
3792 'fit:size': 1840,
3793
3794 'fit/images/kernel:image-pos': 160,
3795 'fit/images/kernel:offset': 156,
3796 'fit/images/kernel:size': 4,
3797
3798 'fit/images/kernel/u-boot:image-pos': 160,
3799 'fit/images/kernel/u-boot:offset': 0,
3800 'fit/images/kernel/u-boot:size': 4,
3801
3802 'fit/images/fdt-1:image-pos': 456,
3803 'fit/images/fdt-1:offset': 452,
3804 'fit/images/fdt-1:size': 6,
3805
3806 'fit/images/fdt-1/u-boot-spl-dtb:image-pos': 456,
3807 'fit/images/fdt-1/u-boot-spl-dtb:offset': 0,
3808 'fit/images/fdt-1/u-boot-spl-dtb:size': 6,
3809
3810 'u-boot-nodtb:image-pos': 1844,
3811 'u-boot-nodtb:offset': 1844,
3812 'u-boot-nodtb:size': 46,
3813 }, props)
3814
3815 # Actually check the data is where we think it is
3816 for node, expected in [
3817 ("u-boot", U_BOOT_DATA),
3818 ("fit/images/kernel", U_BOOT_DATA),
3819 ("fit/images/kernel/u-boot", U_BOOT_DATA),
3820 ("fit/images/fdt-1", U_BOOT_SPL_DTB_DATA),
3821 ("fit/images/fdt-1/u-boot-spl-dtb", U_BOOT_SPL_DTB_DATA),
3822 ("u-boot-nodtb", U_BOOT_NODTB_DATA),
3823 ]:
3824 image_pos = props[f"{node}:image-pos"]
3825 size = props[f"{node}:size"]
3826 self.assertEqual(len(expected), size)
3827 self.assertEqual(expected, data[image_pos:image_pos+size])
3828
Simon Glassfdc34362020-07-09 18:39:45 -06003829 def testFitExternal(self):
Simon Glasse9d336d2020-09-01 05:13:55 -06003830 """Test an image with an FIT with external images"""
Simon Glassfdc34362020-07-09 18:39:45 -06003831 data = self._DoReadFile('162_fit_external.dts')
3832 fit_data = data[len(U_BOOT_DATA):-2] # _testing is 2 bytes
3833
Simon Glass8bc78b72022-01-09 20:13:39 -07003834 # Size of the external-data region as set up by mkimage
3835 external_data_size = len(U_BOOT_DATA) + 2
3836 expected_size = (len(U_BOOT_DATA) + 0x400 +
Simon Glassc1aa66e2022-01-29 14:14:04 -07003837 tools.align(external_data_size, 4) +
Simon Glass8bc78b72022-01-09 20:13:39 -07003838 len(U_BOOT_NODTB_DATA))
3839
Simon Glassfdc34362020-07-09 18:39:45 -06003840 # The data should be outside the FIT
3841 dtb = fdt.Fdt.FromData(fit_data)
3842 dtb.Scan()
3843 fnode = dtb.GetNode('/images/kernel')
3844 self.assertNotIn('data', fnode.props)
Simon Glass8bc78b72022-01-09 20:13:39 -07003845 self.assertEqual(len(U_BOOT_DATA),
3846 fdt_util.fdt32_to_cpu(fnode.props['data-size'].value))
3847 fit_pos = 0x400;
3848 self.assertEqual(
3849 fit_pos,
3850 fdt_util.fdt32_to_cpu(fnode.props['data-position'].value))
3851
3852 self.assertEquals(expected_size, len(data))
3853 actual_pos = len(U_BOOT_DATA) + fit_pos
3854 self.assertEqual(U_BOOT_DATA + b'aa',
3855 data[actual_pos:actual_pos + external_data_size])
Simon Glass12bb1a92019-07-20 12:23:51 -06003856
Alper Nebi Yasak73092222022-02-08 01:08:08 +03003857 def testFitExternalImagePos(self):
3858 """Test that we have correct image-pos for external FIT subentries"""
3859 data, _, _, out_dtb_fname = self._DoReadFileDtb('162_fit_external.dts',
3860 update_dtb=True)
3861 dtb = fdt.Fdt(out_dtb_fname)
3862 dtb.Scan()
3863 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
3864
3865 self.assertEqual({
3866 'image-pos': 0,
3867 'offset': 0,
3868 'size': 1082,
3869
3870 'u-boot:image-pos': 0,
3871 'u-boot:offset': 0,
3872 'u-boot:size': 4,
3873
3874 'fit:size': 1032,
3875 'fit:offset': 4,
3876 'fit:image-pos': 4,
3877
3878 'fit/images/kernel:size': 4,
3879 'fit/images/kernel:offset': 1024,
3880 'fit/images/kernel:image-pos': 1028,
3881
3882 'fit/images/kernel/u-boot:size': 4,
3883 'fit/images/kernel/u-boot:offset': 0,
3884 'fit/images/kernel/u-boot:image-pos': 1028,
3885
3886 'fit/images/fdt-1:size': 2,
3887 'fit/images/fdt-1:offset': 1028,
3888 'fit/images/fdt-1:image-pos': 1032,
3889
3890 'fit/images/fdt-1/_testing:size': 2,
3891 'fit/images/fdt-1/_testing:offset': 0,
3892 'fit/images/fdt-1/_testing:image-pos': 1032,
3893
3894 'u-boot-nodtb:image-pos': 1036,
3895 'u-boot-nodtb:offset': 1036,
3896 'u-boot-nodtb:size': 46,
3897 }, props)
3898
3899 # Actually check the data is where we think it is
3900 for node, expected in [
3901 ("u-boot", U_BOOT_DATA),
3902 ("fit/images/kernel", U_BOOT_DATA),
3903 ("fit/images/kernel/u-boot", U_BOOT_DATA),
3904 ("fit/images/fdt-1", b'aa'),
3905 ("fit/images/fdt-1/_testing", b'aa'),
3906 ("u-boot-nodtb", U_BOOT_NODTB_DATA),
3907 ]:
3908 image_pos = props[f"{node}:image-pos"]
3909 size = props[f"{node}:size"]
3910 self.assertEqual(len(expected), size)
3911 self.assertEqual(expected, data[image_pos:image_pos+size])
3912
Simon Glass4f9ee832022-01-09 20:14:09 -07003913 def testFitMissing(self):
3914 """Test that binman still produces a FIT image if mkimage is missing"""
3915 with test_util.capture_sys_output() as (_, stderr):
3916 self._DoTestFile('162_fit_external.dts',
3917 force_missing_bintools='mkimage')
3918 err = stderr.getvalue()
3919 self.assertRegex(err,
3920 "Image 'main-section'.*missing bintools.*: mkimage")
3921
Alper Nebi Yasak8001d0b2020-08-31 12:58:18 +03003922 def testSectionIgnoreHashSignature(self):
3923 """Test that sections ignore hash, signature nodes for its data"""
3924 data = self._DoReadFile('165_section_ignore_hash_signature.dts')
3925 expected = (U_BOOT_DATA + U_BOOT_DATA)
3926 self.assertEqual(expected, data)
3927
Alper Nebi Yasak3fdeb142020-08-31 12:58:19 +03003928 def testPadInSections(self):
3929 """Test pad-before, pad-after for entries in sections"""
Simon Glassf90d9062020-10-26 17:40:09 -06003930 data, _, _, out_dtb_fname = self._DoReadFileDtb(
3931 '166_pad_in_sections.dts', update_dtb=True)
Simon Glassc1aa66e2022-01-29 14:14:04 -07003932 expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) +
3933 U_BOOT_DATA + tools.get_bytes(ord('!'), 6) +
Alper Nebi Yasak3fdeb142020-08-31 12:58:19 +03003934 U_BOOT_DATA)
3935 self.assertEqual(expected, data)
3936
Simon Glassf90d9062020-10-26 17:40:09 -06003937 dtb = fdt.Fdt(out_dtb_fname)
3938 dtb.Scan()
3939 props = self._GetPropTree(dtb, ['size', 'image-pos', 'offset'])
3940 expected = {
3941 'image-pos': 0,
3942 'offset': 0,
3943 'size': 12 + 6 + 3 * len(U_BOOT_DATA),
3944
3945 'section:image-pos': 0,
3946 'section:offset': 0,
3947 'section:size': 12 + 6 + 3 * len(U_BOOT_DATA),
3948
3949 'section/before:image-pos': 0,
3950 'section/before:offset': 0,
3951 'section/before:size': len(U_BOOT_DATA),
3952
3953 'section/u-boot:image-pos': 4,
3954 'section/u-boot:offset': 4,
3955 'section/u-boot:size': 12 + len(U_BOOT_DATA) + 6,
3956
3957 'section/after:image-pos': 26,
3958 'section/after:offset': 26,
3959 'section/after:size': len(U_BOOT_DATA),
3960 }
3961 self.assertEqual(expected, props)
3962
Alper Nebi Yasakfe057012020-08-31 12:58:20 +03003963 def testFitImageSubentryAlignment(self):
3964 """Test relative alignability of FIT image subentries"""
Alper Nebi Yasakf3078d42022-02-08 01:08:07 +03003965 self._SetupSplElf()
Alper Nebi Yasakfe057012020-08-31 12:58:20 +03003966 entry_args = {
3967 'test-id': TEXT_DATA,
3968 }
3969 data, _, _, _ = self._DoReadFileDtb('167_fit_image_subentry_alignment.dts',
3970 entry_args=entry_args)
3971 dtb = fdt.Fdt.FromData(data)
3972 dtb.Scan()
3973
3974 node = dtb.GetNode('/images/kernel')
3975 data = dtb.GetProps(node)["data"].bytes
3976 align_pad = 0x10 - (len(U_BOOT_SPL_DATA) % 0x10)
Simon Glassc1aa66e2022-01-29 14:14:04 -07003977 expected = (tools.get_bytes(0, 0x20) + U_BOOT_SPL_DATA +
3978 tools.get_bytes(0, align_pad) + U_BOOT_DATA)
Alper Nebi Yasakfe057012020-08-31 12:58:20 +03003979 self.assertEqual(expected, data)
3980
3981 node = dtb.GetNode('/images/fdt-1')
3982 data = dtb.GetProps(node)["data"].bytes
Simon Glassc1aa66e2022-01-29 14:14:04 -07003983 expected = (U_BOOT_SPL_DTB_DATA + tools.get_bytes(0, 20) +
3984 tools.to_bytes(TEXT_DATA) + tools.get_bytes(0, 30) +
Alper Nebi Yasakfe057012020-08-31 12:58:20 +03003985 U_BOOT_DTB_DATA)
3986 self.assertEqual(expected, data)
3987
3988 def testFitExtblobMissingOk(self):
3989 """Test a FIT with a missing external blob that is allowed"""
3990 with test_util.capture_sys_output() as (stdout, stderr):
3991 self._DoTestFile('168_fit_missing_blob.dts',
3992 allow_missing=True)
3993 err = stderr.getvalue()
Simon Glassb2381432020-09-06 10:39:09 -06003994 self.assertRegex(err, "Image 'main-section'.*missing.*: atf-bl31")
Alper Nebi Yasakfe057012020-08-31 12:58:20 +03003995
Simon Glass3decfa32020-09-01 05:13:54 -06003996 def testBlobNamedByArgMissing(self):
3997 """Test handling of a missing entry arg"""
3998 with self.assertRaises(ValueError) as e:
3999 self._DoReadFile('068_blob_named_by_arg.dts')
4000 self.assertIn("Missing required properties/entry args: cros-ec-rw-path",
4001 str(e.exception))
4002
Simon Glassdc2f81a2020-09-01 05:13:58 -06004003 def testPackBl31(self):
4004 """Test that an image with an ATF BL31 binary can be created"""
4005 data = self._DoReadFile('169_atf_bl31.dts')
4006 self.assertEqual(ATF_BL31_DATA, data[:len(ATF_BL31_DATA)])
4007
Samuel Holland18bd4552020-10-21 21:12:15 -05004008 def testPackScp(self):
4009 """Test that an image with an SCP binary can be created"""
4010 data = self._DoReadFile('172_scp.dts')
4011 self.assertEqual(SCP_DATA, data[:len(SCP_DATA)])
4012
Simon Glass6cf99532020-09-01 05:13:59 -06004013 def testFitFdt(self):
4014 """Test an image with an FIT with multiple FDT images"""
4015 def _CheckFdt(seq, expected_data):
4016 """Check the FDT nodes
4017
4018 Args:
4019 seq: Sequence number to check (0 or 1)
4020 expected_data: Expected contents of 'data' property
4021 """
4022 name = 'fdt-%d' % seq
4023 fnode = dtb.GetNode('/images/%s' % name)
4024 self.assertIsNotNone(fnode)
4025 self.assertEqual({'description','type', 'compression', 'data'},
4026 set(fnode.props.keys()))
4027 self.assertEqual(expected_data, fnode.props['data'].bytes)
4028 self.assertEqual('fdt-test-fdt%d.dtb' % seq,
4029 fnode.props['description'].value)
4030
4031 def _CheckConfig(seq, expected_data):
4032 """Check the configuration nodes
4033
4034 Args:
4035 seq: Sequence number to check (0 or 1)
4036 expected_data: Expected contents of 'data' property
4037 """
4038 cnode = dtb.GetNode('/configurations')
4039 self.assertIn('default', cnode.props)
Simon Glassc0f1ebe2020-09-06 10:39:08 -06004040 self.assertEqual('config-2', cnode.props['default'].value)
Simon Glass6cf99532020-09-01 05:13:59 -06004041
4042 name = 'config-%d' % seq
4043 fnode = dtb.GetNode('/configurations/%s' % name)
4044 self.assertIsNotNone(fnode)
4045 self.assertEqual({'description','firmware', 'loadables', 'fdt'},
4046 set(fnode.props.keys()))
4047 self.assertEqual('conf-test-fdt%d.dtb' % seq,
4048 fnode.props['description'].value)
4049 self.assertEqual('fdt-%d' % seq, fnode.props['fdt'].value)
4050
4051 entry_args = {
4052 'of-list': 'test-fdt1 test-fdt2',
Simon Glassc0f1ebe2020-09-06 10:39:08 -06004053 'default-dt': 'test-fdt2',
Simon Glass6cf99532020-09-01 05:13:59 -06004054 }
4055 data = self._DoReadFileDtb(
Bin Mengaa75ce92021-05-10 20:23:32 +08004056 '170_fit_fdt.dts',
Simon Glass6cf99532020-09-01 05:13:59 -06004057 entry_args=entry_args,
4058 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
4059 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
4060 fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
4061
4062 dtb = fdt.Fdt.FromData(fit_data)
4063 dtb.Scan()
4064 fnode = dtb.GetNode('/images/kernel')
4065 self.assertIn('data', fnode.props)
4066
4067 # Check all the properties in fdt-1 and fdt-2
4068 _CheckFdt(1, TEST_FDT1_DATA)
4069 _CheckFdt(2, TEST_FDT2_DATA)
4070
4071 # Check configurations
4072 _CheckConfig(1, TEST_FDT1_DATA)
4073 _CheckConfig(2, TEST_FDT2_DATA)
4074
4075 def testFitFdtMissingList(self):
4076 """Test handling of a missing 'of-list' entry arg"""
4077 with self.assertRaises(ValueError) as e:
Bin Mengaa75ce92021-05-10 20:23:32 +08004078 self._DoReadFile('170_fit_fdt.dts')
Simon Glass6cf99532020-09-01 05:13:59 -06004079 self.assertIn("Generator node requires 'of-list' entry argument",
4080 str(e.exception))
4081
4082 def testFitFdtEmptyList(self):
4083 """Test handling of an empty 'of-list' entry arg"""
4084 entry_args = {
4085 'of-list': '',
4086 }
4087 data = self._DoReadFileDtb('170_fit_fdt.dts', entry_args=entry_args)[0]
4088
4089 def testFitFdtMissingProp(self):
4090 """Test handling of a missing 'fit,fdt-list' property"""
4091 with self.assertRaises(ValueError) as e:
4092 self._DoReadFile('171_fit_fdt_missing_prop.dts')
4093 self.assertIn("Generator node requires 'fit,fdt-list' property",
4094 str(e.exception))
Simon Glassdc2f81a2020-09-01 05:13:58 -06004095
Simon Glassc0f1ebe2020-09-06 10:39:08 -06004096 def testFitFdtEmptyList(self):
4097 """Test handling of an empty 'of-list' entry arg"""
4098 entry_args = {
4099 'of-list': '',
4100 }
Bin Mengaa75ce92021-05-10 20:23:32 +08004101 data = self._DoReadFileDtb('170_fit_fdt.dts', entry_args=entry_args)[0]
Simon Glassc0f1ebe2020-09-06 10:39:08 -06004102
4103 def testFitFdtMissing(self):
4104 """Test handling of a missing 'default-dt' entry arg"""
4105 entry_args = {
4106 'of-list': 'test-fdt1 test-fdt2',
4107 }
4108 with self.assertRaises(ValueError) as e:
4109 self._DoReadFileDtb(
Bin Mengaa75ce92021-05-10 20:23:32 +08004110 '170_fit_fdt.dts',
Simon Glassc0f1ebe2020-09-06 10:39:08 -06004111 entry_args=entry_args,
4112 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
4113 self.assertIn("Generated 'default' node requires default-dt entry argument",
4114 str(e.exception))
4115
4116 def testFitFdtNotInList(self):
4117 """Test handling of a default-dt that is not in the of-list"""
4118 entry_args = {
4119 'of-list': 'test-fdt1 test-fdt2',
4120 'default-dt': 'test-fdt3',
4121 }
4122 with self.assertRaises(ValueError) as e:
4123 self._DoReadFileDtb(
Bin Mengaa75ce92021-05-10 20:23:32 +08004124 '170_fit_fdt.dts',
Simon Glassc0f1ebe2020-09-06 10:39:08 -06004125 entry_args=entry_args,
4126 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
4127 self.assertIn("default-dt entry argument 'test-fdt3' not found in fdt list: test-fdt1, test-fdt2",
4128 str(e.exception))
4129
Simon Glassb2381432020-09-06 10:39:09 -06004130 def testFitExtblobMissingHelp(self):
4131 """Test display of help messages when an external blob is missing"""
4132 control.missing_blob_help = control._ReadMissingBlobHelp()
4133 control.missing_blob_help['wibble'] = 'Wibble test'
4134 control.missing_blob_help['another'] = 'Another test'
4135 with test_util.capture_sys_output() as (stdout, stderr):
4136 self._DoTestFile('168_fit_missing_blob.dts',
4137 allow_missing=True)
4138 err = stderr.getvalue()
4139
4140 # We can get the tag from the name, the type or the missing-msg
4141 # property. Check all three.
4142 self.assertIn('You may need to build ARM Trusted', err)
4143 self.assertIn('Wibble test', err)
4144 self.assertIn('Another test', err)
4145
Simon Glass204aa782020-09-06 10:35:32 -06004146 def testMissingBlob(self):
4147 """Test handling of a blob containing a missing file"""
4148 with self.assertRaises(ValueError) as e:
4149 self._DoTestFile('173_missing_blob.dts', allow_missing=True)
4150 self.assertIn("Filename 'missing' not found in input path",
4151 str(e.exception))
4152
Simon Glassfb91d562020-09-06 10:35:33 -06004153 def testEnvironment(self):
4154 """Test adding a U-Boot environment"""
4155 data = self._DoReadFile('174_env.dts')
4156 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
4157 self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
4158 env = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
4159 self.assertEqual(b'\x1b\x97\x22\x7c\x01var1=1\0var2="2"\0\0\xff\xff',
4160 env)
4161
4162 def testEnvironmentNoSize(self):
4163 """Test that a missing 'size' property is detected"""
4164 with self.assertRaises(ValueError) as e:
Simon Glassa4dfe3e2020-10-26 17:40:00 -06004165 self._DoTestFile('175_env_no_size.dts')
Simon Glassfb91d562020-09-06 10:35:33 -06004166 self.assertIn("'u-boot-env' entry must have a size property",
4167 str(e.exception))
4168
4169 def testEnvironmentTooSmall(self):
4170 """Test handling of an environment that does not fit"""
4171 with self.assertRaises(ValueError) as e:
Simon Glassa4dfe3e2020-10-26 17:40:00 -06004172 self._DoTestFile('176_env_too_small.dts')
Simon Glassfb91d562020-09-06 10:35:33 -06004173
4174 # checksum, start byte, environment with \0 terminator, final \0
4175 need = 4 + 1 + len(ENV_DATA) + 1 + 1
4176 short = need - 0x8
4177 self.assertIn("too small to hold data (need %#x more bytes)" % short,
4178 str(e.exception))
4179
Simon Glassf2c0dd82020-10-26 17:40:01 -06004180 def testSkipAtStart(self):
4181 """Test handling of skip-at-start section"""
4182 data = self._DoReadFile('177_skip_at_start.dts')
4183 self.assertEqual(U_BOOT_DATA, data)
4184
4185 image = control.images['image']
4186 entries = image.GetEntries()
4187 section = entries['section']
4188 self.assertEqual(0, section.offset)
4189 self.assertEqual(len(U_BOOT_DATA), section.size)
4190 self.assertEqual(U_BOOT_DATA, section.GetData())
4191
4192 entry = section.GetEntries()['u-boot']
4193 self.assertEqual(16, entry.offset)
4194 self.assertEqual(len(U_BOOT_DATA), entry.size)
4195 self.assertEqual(U_BOOT_DATA, entry.data)
4196
4197 def testSkipAtStartPad(self):
4198 """Test handling of skip-at-start section with padded entry"""
4199 data = self._DoReadFile('178_skip_at_start_pad.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07004200 before = tools.get_bytes(0, 8)
4201 after = tools.get_bytes(0, 4)
Simon Glassf2c0dd82020-10-26 17:40:01 -06004202 all = before + U_BOOT_DATA + after
4203 self.assertEqual(all, data)
4204
4205 image = control.images['image']
4206 entries = image.GetEntries()
4207 section = entries['section']
4208 self.assertEqual(0, section.offset)
4209 self.assertEqual(len(all), section.size)
4210 self.assertEqual(all, section.GetData())
4211
4212 entry = section.GetEntries()['u-boot']
4213 self.assertEqual(16, entry.offset)
4214 self.assertEqual(len(all), entry.size)
4215 self.assertEqual(U_BOOT_DATA, entry.data)
4216
4217 def testSkipAtStartSectionPad(self):
4218 """Test handling of skip-at-start section with padding"""
4219 data = self._DoReadFile('179_skip_at_start_section_pad.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07004220 before = tools.get_bytes(0, 8)
4221 after = tools.get_bytes(0, 4)
Simon Glassf2c0dd82020-10-26 17:40:01 -06004222 all = before + U_BOOT_DATA + after
Simon Glassd1d3ad72020-10-26 17:40:13 -06004223 self.assertEqual(all, data)
Simon Glassf2c0dd82020-10-26 17:40:01 -06004224
4225 image = control.images['image']
4226 entries = image.GetEntries()
4227 section = entries['section']
4228 self.assertEqual(0, section.offset)
4229 self.assertEqual(len(all), section.size)
Simon Glass63e7ba62020-10-26 17:40:16 -06004230 self.assertEqual(U_BOOT_DATA, section.data)
Simon Glassd1d3ad72020-10-26 17:40:13 -06004231 self.assertEqual(all, section.GetPaddedData())
Simon Glassf2c0dd82020-10-26 17:40:01 -06004232
4233 entry = section.GetEntries()['u-boot']
4234 self.assertEqual(16, entry.offset)
4235 self.assertEqual(len(U_BOOT_DATA), entry.size)
4236 self.assertEqual(U_BOOT_DATA, entry.data)
Simon Glassfb91d562020-09-06 10:35:33 -06004237
Simon Glass7d398bb2020-10-26 17:40:14 -06004238 def testSectionPad(self):
4239 """Testing padding with sections"""
4240 data = self._DoReadFile('180_section_pad.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07004241 expected = (tools.get_bytes(ord('&'), 3) +
4242 tools.get_bytes(ord('!'), 5) +
Simon Glass7d398bb2020-10-26 17:40:14 -06004243 U_BOOT_DATA +
Simon Glassc1aa66e2022-01-29 14:14:04 -07004244 tools.get_bytes(ord('!'), 1) +
4245 tools.get_bytes(ord('&'), 2))
Simon Glass7d398bb2020-10-26 17:40:14 -06004246 self.assertEqual(expected, data)
4247
4248 def testSectionAlign(self):
4249 """Testing alignment with sections"""
4250 data = self._DoReadFileDtb('181_section_align.dts', map=True)[0]
4251 expected = (b'\0' + # fill section
Simon Glassc1aa66e2022-01-29 14:14:04 -07004252 tools.get_bytes(ord('&'), 1) + # padding to section align
Simon Glass7d398bb2020-10-26 17:40:14 -06004253 b'\0' + # fill section
Simon Glassc1aa66e2022-01-29 14:14:04 -07004254 tools.get_bytes(ord('!'), 3) + # padding to u-boot align
Simon Glass7d398bb2020-10-26 17:40:14 -06004255 U_BOOT_DATA +
Simon Glassc1aa66e2022-01-29 14:14:04 -07004256 tools.get_bytes(ord('!'), 4) + # padding to u-boot size
4257 tools.get_bytes(ord('!'), 4)) # padding to section size
Simon Glass7d398bb2020-10-26 17:40:14 -06004258 self.assertEqual(expected, data)
4259
Simon Glass8f5ef892020-10-26 17:40:25 -06004260 def testCompressImage(self):
4261 """Test compression of the entire image"""
4262 self._CheckLz4()
4263 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4264 '182_compress_image.dts', use_real_dtb=True, update_dtb=True)
4265 dtb = fdt.Fdt(out_dtb_fname)
4266 dtb.Scan()
4267 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4268 'uncomp-size'])
4269 orig = self._decompress(data)
4270 self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, orig)
4271
4272 # Do a sanity check on various fields
4273 image = control.images['image']
4274 entries = image.GetEntries()
4275 self.assertEqual(2, len(entries))
4276
4277 entry = entries['blob']
4278 self.assertEqual(COMPRESS_DATA, entry.data)
4279 self.assertEqual(len(COMPRESS_DATA), entry.size)
4280
4281 entry = entries['u-boot']
4282 self.assertEqual(U_BOOT_DATA, entry.data)
4283 self.assertEqual(len(U_BOOT_DATA), entry.size)
4284
4285 self.assertEqual(len(data), image.size)
4286 self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, image.uncomp_data)
4287 self.assertEqual(len(COMPRESS_DATA + U_BOOT_DATA), image.uncomp_size)
4288 orig = self._decompress(image.data)
4289 self.assertEqual(orig, image.uncomp_data)
4290
4291 expected = {
4292 'blob:offset': 0,
4293 'blob:size': len(COMPRESS_DATA),
4294 'u-boot:offset': len(COMPRESS_DATA),
4295 'u-boot:size': len(U_BOOT_DATA),
4296 'uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4297 'offset': 0,
4298 'image-pos': 0,
4299 'size': len(data),
4300 }
4301 self.assertEqual(expected, props)
4302
4303 def testCompressImageLess(self):
4304 """Test compression where compression reduces the image size"""
4305 self._CheckLz4()
4306 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4307 '183_compress_image_less.dts', use_real_dtb=True, update_dtb=True)
4308 dtb = fdt.Fdt(out_dtb_fname)
4309 dtb.Scan()
4310 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4311 'uncomp-size'])
4312 orig = self._decompress(data)
4313
4314 self.assertEquals(COMPRESS_DATA + COMPRESS_DATA + U_BOOT_DATA, orig)
4315
4316 # Do a sanity check on various fields
4317 image = control.images['image']
4318 entries = image.GetEntries()
4319 self.assertEqual(2, len(entries))
4320
4321 entry = entries['blob']
4322 self.assertEqual(COMPRESS_DATA_BIG, entry.data)
4323 self.assertEqual(len(COMPRESS_DATA_BIG), entry.size)
4324
4325 entry = entries['u-boot']
4326 self.assertEqual(U_BOOT_DATA, entry.data)
4327 self.assertEqual(len(U_BOOT_DATA), entry.size)
4328
4329 self.assertEqual(len(data), image.size)
4330 self.assertEqual(COMPRESS_DATA_BIG + U_BOOT_DATA, image.uncomp_data)
4331 self.assertEqual(len(COMPRESS_DATA_BIG + U_BOOT_DATA),
4332 image.uncomp_size)
4333 orig = self._decompress(image.data)
4334 self.assertEqual(orig, image.uncomp_data)
4335
4336 expected = {
4337 'blob:offset': 0,
4338 'blob:size': len(COMPRESS_DATA_BIG),
4339 'u-boot:offset': len(COMPRESS_DATA_BIG),
4340 'u-boot:size': len(U_BOOT_DATA),
4341 'uncomp-size': len(COMPRESS_DATA_BIG + U_BOOT_DATA),
4342 'offset': 0,
4343 'image-pos': 0,
4344 'size': len(data),
4345 }
4346 self.assertEqual(expected, props)
4347
4348 def testCompressSectionSize(self):
4349 """Test compression of a section with a fixed size"""
4350 self._CheckLz4()
4351 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4352 '184_compress_section_size.dts', use_real_dtb=True, update_dtb=True)
4353 dtb = fdt.Fdt(out_dtb_fname)
4354 dtb.Scan()
4355 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4356 'uncomp-size'])
4357 orig = self._decompress(data)
4358 self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, orig)
4359 expected = {
4360 'section/blob:offset': 0,
4361 'section/blob:size': len(COMPRESS_DATA),
4362 'section/u-boot:offset': len(COMPRESS_DATA),
4363 'section/u-boot:size': len(U_BOOT_DATA),
4364 'section:offset': 0,
4365 'section:image-pos': 0,
4366 'section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4367 'section:size': 0x30,
4368 'offset': 0,
4369 'image-pos': 0,
4370 'size': 0x30,
4371 }
4372 self.assertEqual(expected, props)
4373
4374 def testCompressSection(self):
4375 """Test compression of a section with no fixed size"""
4376 self._CheckLz4()
4377 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4378 '185_compress_section.dts', use_real_dtb=True, update_dtb=True)
4379 dtb = fdt.Fdt(out_dtb_fname)
4380 dtb.Scan()
4381 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4382 'uncomp-size'])
4383 orig = self._decompress(data)
4384 self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, orig)
4385 expected = {
4386 'section/blob:offset': 0,
4387 'section/blob:size': len(COMPRESS_DATA),
4388 'section/u-boot:offset': len(COMPRESS_DATA),
4389 'section/u-boot:size': len(U_BOOT_DATA),
4390 'section:offset': 0,
4391 'section:image-pos': 0,
4392 'section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4393 'section:size': len(data),
4394 'offset': 0,
4395 'image-pos': 0,
4396 'size': len(data),
4397 }
4398 self.assertEqual(expected, props)
4399
4400 def testCompressExtra(self):
4401 """Test compression of a section with no fixed size"""
4402 self._CheckLz4()
4403 data, _, _, out_dtb_fname = self._DoReadFileDtb(
4404 '186_compress_extra.dts', use_real_dtb=True, update_dtb=True)
4405 dtb = fdt.Fdt(out_dtb_fname)
4406 dtb.Scan()
4407 props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4408 'uncomp-size'])
4409
4410 base = data[len(U_BOOT_DATA):]
4411 self.assertEquals(U_BOOT_DATA, base[:len(U_BOOT_DATA)])
4412 rest = base[len(U_BOOT_DATA):]
4413
4414 # Check compressed data
4415 section1 = self._decompress(rest)
Simon Glass0d1e95a2022-01-09 20:14:04 -07004416 expect1 = comp_util.compress(COMPRESS_DATA + U_BOOT_DATA, 'lz4')
Simon Glass8f5ef892020-10-26 17:40:25 -06004417 self.assertEquals(expect1, rest[:len(expect1)])
4418 self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, section1)
4419 rest1 = rest[len(expect1):]
4420
4421 section2 = self._decompress(rest1)
Simon Glass0d1e95a2022-01-09 20:14:04 -07004422 expect2 = comp_util.compress(COMPRESS_DATA + COMPRESS_DATA, 'lz4')
Simon Glass8f5ef892020-10-26 17:40:25 -06004423 self.assertEquals(expect2, rest1[:len(expect2)])
4424 self.assertEquals(COMPRESS_DATA + COMPRESS_DATA, section2)
4425 rest2 = rest1[len(expect2):]
4426
4427 expect_size = (len(U_BOOT_DATA) + len(U_BOOT_DATA) + len(expect1) +
4428 len(expect2) + len(U_BOOT_DATA))
4429 #self.assertEquals(expect_size, len(data))
4430
4431 #self.assertEquals(U_BOOT_DATA, rest2)
4432
4433 self.maxDiff = None
4434 expected = {
4435 'u-boot:offset': 0,
4436 'u-boot:image-pos': 0,
4437 'u-boot:size': len(U_BOOT_DATA),
4438
4439 'base:offset': len(U_BOOT_DATA),
4440 'base:image-pos': len(U_BOOT_DATA),
4441 'base:size': len(data) - len(U_BOOT_DATA),
4442 'base/u-boot:offset': 0,
4443 'base/u-boot:image-pos': len(U_BOOT_DATA),
4444 'base/u-boot:size': len(U_BOOT_DATA),
4445 'base/u-boot2:offset': len(U_BOOT_DATA) + len(expect1) +
4446 len(expect2),
4447 'base/u-boot2:image-pos': len(U_BOOT_DATA) * 2 + len(expect1) +
4448 len(expect2),
4449 'base/u-boot2:size': len(U_BOOT_DATA),
4450
4451 'base/section:offset': len(U_BOOT_DATA),
4452 'base/section:image-pos': len(U_BOOT_DATA) * 2,
4453 'base/section:size': len(expect1),
4454 'base/section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4455 'base/section/blob:offset': 0,
4456 'base/section/blob:size': len(COMPRESS_DATA),
4457 'base/section/u-boot:offset': len(COMPRESS_DATA),
4458 'base/section/u-boot:size': len(U_BOOT_DATA),
4459
4460 'base/section2:offset': len(U_BOOT_DATA) + len(expect1),
4461 'base/section2:image-pos': len(U_BOOT_DATA) * 2 + len(expect1),
4462 'base/section2:size': len(expect2),
4463 'base/section2:uncomp-size': len(COMPRESS_DATA + COMPRESS_DATA),
4464 'base/section2/blob:offset': 0,
4465 'base/section2/blob:size': len(COMPRESS_DATA),
4466 'base/section2/blob2:offset': len(COMPRESS_DATA),
4467 'base/section2/blob2:size': len(COMPRESS_DATA),
4468
4469 'offset': 0,
4470 'image-pos': 0,
4471 'size': len(data),
4472 }
4473 self.assertEqual(expected, props)
4474
Simon Glass870a9ea2021-01-06 21:35:15 -07004475 def testSymbolsSubsection(self):
4476 """Test binman can assign symbols from a subsection"""
Simon Glassf5898822021-03-18 20:24:56 +13004477 self.checkSymbols('187_symbols_sub.dts', U_BOOT_SPL_DATA, 0x18)
Simon Glass870a9ea2021-01-06 21:35:15 -07004478
Simon Glass939d1062021-01-06 21:35:16 -07004479 def testReadImageEntryArg(self):
4480 """Test reading an image that would need an entry arg to generate"""
4481 entry_args = {
4482 'cros-ec-rw-path': 'ecrw.bin',
4483 }
4484 data = self.data = self._DoReadFileDtb(
4485 '188_image_entryarg.dts',use_real_dtb=True, update_dtb=True,
4486 entry_args=entry_args)
4487
Simon Glassc1aa66e2022-01-29 14:14:04 -07004488 image_fname = tools.get_output_filename('image.bin')
Simon Glass939d1062021-01-06 21:35:16 -07004489 orig_image = control.images['image']
4490
4491 # This should not generate an error about the missing 'cros-ec-rw-path'
4492 # since we are reading the image from a file. Compare with
4493 # testEntryArgsRequired()
4494 image = Image.FromFile(image_fname)
4495 self.assertEqual(orig_image.GetEntries().keys(),
4496 image.GetEntries().keys())
4497
Simon Glass6eb99322021-01-06 21:35:18 -07004498 def testFilesAlign(self):
4499 """Test alignment with files"""
4500 data = self._DoReadFile('190_files_align.dts')
4501
4502 # The first string is 15 bytes so will align to 16
4503 expect = FILES_DATA[:15] + b'\0' + FILES_DATA[15:]
4504 self.assertEqual(expect, data)
4505
Simon Glass5c6ba712021-01-06 21:35:19 -07004506 def testReadImageSkip(self):
4507 """Test reading an image and accessing its FDT map"""
4508 data = self.data = self._DoReadFileRealDtb('191_read_image_skip.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07004509 image_fname = tools.get_output_filename('image.bin')
Simon Glass5c6ba712021-01-06 21:35:19 -07004510 orig_image = control.images['image']
4511 image = Image.FromFile(image_fname)
4512 self.assertEqual(orig_image.GetEntries().keys(),
4513 image.GetEntries().keys())
4514
4515 orig_entry = orig_image.GetEntries()['fdtmap']
4516 entry = image.GetEntries()['fdtmap']
4517 self.assertEqual(orig_entry.offset, entry.offset)
4518 self.assertEqual(orig_entry.size, entry.size)
4519 self.assertEqual(16, entry.image_pos)
4520
4521 u_boot = image.GetEntries()['section'].GetEntries()['u-boot']
4522
4523 self.assertEquals(U_BOOT_DATA, u_boot.ReadData())
4524
Simon Glass77a64e02021-03-18 20:24:57 +13004525 def testTplNoDtb(self):
4526 """Test that an image with tpl/u-boot-tpl-nodtb.bin can be created"""
Simon Glass0fe44dc2021-04-25 08:39:32 +12004527 self._SetupTplElf()
Simon Glass77a64e02021-03-18 20:24:57 +13004528 data = self._DoReadFile('192_u_boot_tpl_nodtb.dts')
4529 self.assertEqual(U_BOOT_TPL_NODTB_DATA,
4530 data[:len(U_BOOT_TPL_NODTB_DATA)])
4531
Simon Glassd26efc82021-03-18 20:24:58 +13004532 def testTplBssPad(self):
4533 """Test that we can pad TPL's BSS with zeros"""
4534 # ELF file with a '__bss_size' symbol
4535 self._SetupTplElf()
4536 data = self._DoReadFile('193_tpl_bss_pad.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07004537 self.assertEqual(U_BOOT_TPL_DATA + tools.get_bytes(0, 10) + U_BOOT_DATA,
Simon Glassd26efc82021-03-18 20:24:58 +13004538 data)
4539
4540 def testTplBssPadMissing(self):
4541 """Test that a missing symbol is detected"""
4542 self._SetupTplElf('u_boot_ucode_ptr')
4543 with self.assertRaises(ValueError) as e:
4544 self._DoReadFile('193_tpl_bss_pad.dts')
4545 self.assertIn('Expected __bss_size symbol in tpl/u-boot-tpl',
4546 str(e.exception))
4547
Simon Glass06684922021-03-18 20:25:07 +13004548 def checkDtbSizes(self, data, pad_len, start):
4549 """Check the size arguments in a dtb embedded in an image
4550
4551 Args:
4552 data: The image data
4553 pad_len: Length of the pad section in the image, in bytes
4554 start: Start offset of the devicetree to examine, within the image
4555
4556 Returns:
4557 Size of the devicetree in bytes
4558 """
4559 dtb_data = data[start:]
4560 dtb = fdt.Fdt.FromData(dtb_data)
4561 fdt_size = dtb.GetFdtObj().totalsize()
4562 dtb.Scan()
4563 props = self._GetPropTree(dtb, 'size')
4564 self.assertEqual({
4565 'size': len(data),
4566 'u-boot-spl/u-boot-spl-bss-pad:size': pad_len,
4567 'u-boot-spl/u-boot-spl-dtb:size': 801,
4568 'u-boot-spl/u-boot-spl-nodtb:size': len(U_BOOT_SPL_NODTB_DATA),
4569 'u-boot-spl:size': 860,
4570 'u-boot-tpl:size': len(U_BOOT_TPL_DATA),
4571 'u-boot/u-boot-dtb:size': 781,
4572 'u-boot/u-boot-nodtb:size': len(U_BOOT_NODTB_DATA),
4573 'u-boot:size': 827,
4574 }, props)
4575 return fdt_size
4576
4577 def testExpanded(self):
4578 """Test that an expanded entry type is selected when needed"""
4579 self._SetupSplElf()
4580 self._SetupTplElf()
4581
4582 # SPL has a devicetree, TPL does not
4583 entry_args = {
4584 'spl-dtb': '1',
4585 'spl-bss-pad': 'y',
4586 'tpl-dtb': '',
4587 }
4588 self._DoReadFileDtb('194_fdt_incl.dts', use_expanded=True,
4589 entry_args=entry_args)
4590 image = control.images['image']
4591 entries = image.GetEntries()
4592 self.assertEqual(3, len(entries))
4593
4594 # First, u-boot, which should be expanded into u-boot-nodtb and dtb
4595 self.assertIn('u-boot', entries)
4596 entry = entries['u-boot']
4597 self.assertEqual('u-boot-expanded', entry.etype)
4598 subent = entry.GetEntries()
4599 self.assertEqual(2, len(subent))
4600 self.assertIn('u-boot-nodtb', subent)
4601 self.assertIn('u-boot-dtb', subent)
4602
4603 # Second, u-boot-spl, which should be expanded into three parts
4604 self.assertIn('u-boot-spl', entries)
4605 entry = entries['u-boot-spl']
4606 self.assertEqual('u-boot-spl-expanded', entry.etype)
4607 subent = entry.GetEntries()
4608 self.assertEqual(3, len(subent))
4609 self.assertIn('u-boot-spl-nodtb', subent)
4610 self.assertIn('u-boot-spl-bss-pad', subent)
4611 self.assertIn('u-boot-spl-dtb', subent)
4612
4613 # Third, u-boot-tpl, which should be not be expanded, since TPL has no
4614 # devicetree
4615 self.assertIn('u-boot-tpl', entries)
4616 entry = entries['u-boot-tpl']
4617 self.assertEqual('u-boot-tpl', entry.etype)
4618 self.assertEqual(None, entry.GetEntries())
4619
4620 def testExpandedTpl(self):
4621 """Test that an expanded entry type is selected for TPL when needed"""
4622 self._SetupTplElf()
4623
4624 entry_args = {
4625 'tpl-bss-pad': 'y',
4626 'tpl-dtb': 'y',
4627 }
4628 self._DoReadFileDtb('195_fdt_incl_tpl.dts', use_expanded=True,
4629 entry_args=entry_args)
4630 image = control.images['image']
4631 entries = image.GetEntries()
4632 self.assertEqual(1, len(entries))
4633
4634 # We only have u-boot-tpl, which be expanded
4635 self.assertIn('u-boot-tpl', entries)
4636 entry = entries['u-boot-tpl']
4637 self.assertEqual('u-boot-tpl-expanded', entry.etype)
4638 subent = entry.GetEntries()
4639 self.assertEqual(3, len(subent))
4640 self.assertIn('u-boot-tpl-nodtb', subent)
4641 self.assertIn('u-boot-tpl-bss-pad', subent)
4642 self.assertIn('u-boot-tpl-dtb', subent)
4643
4644 def testExpandedNoPad(self):
4645 """Test an expanded entry without BSS pad enabled"""
4646 self._SetupSplElf()
4647 self._SetupTplElf()
4648
4649 # SPL has a devicetree, TPL does not
4650 entry_args = {
4651 'spl-dtb': 'something',
4652 'spl-bss-pad': 'n',
4653 'tpl-dtb': '',
4654 }
4655 self._DoReadFileDtb('194_fdt_incl.dts', use_expanded=True,
4656 entry_args=entry_args)
4657 image = control.images['image']
4658 entries = image.GetEntries()
4659
4660 # Just check u-boot-spl, which should be expanded into two parts
4661 self.assertIn('u-boot-spl', entries)
4662 entry = entries['u-boot-spl']
4663 self.assertEqual('u-boot-spl-expanded', entry.etype)
4664 subent = entry.GetEntries()
4665 self.assertEqual(2, len(subent))
4666 self.assertIn('u-boot-spl-nodtb', subent)
4667 self.assertIn('u-boot-spl-dtb', subent)
4668
4669 def testExpandedTplNoPad(self):
4670 """Test that an expanded entry type with padding disabled in TPL"""
4671 self._SetupTplElf()
4672
4673 entry_args = {
4674 'tpl-bss-pad': '',
4675 'tpl-dtb': 'y',
4676 }
4677 self._DoReadFileDtb('195_fdt_incl_tpl.dts', use_expanded=True,
4678 entry_args=entry_args)
4679 image = control.images['image']
4680 entries = image.GetEntries()
4681 self.assertEqual(1, len(entries))
4682
4683 # We only have u-boot-tpl, which be expanded
4684 self.assertIn('u-boot-tpl', entries)
4685 entry = entries['u-boot-tpl']
4686 self.assertEqual('u-boot-tpl-expanded', entry.etype)
4687 subent = entry.GetEntries()
4688 self.assertEqual(2, len(subent))
4689 self.assertIn('u-boot-tpl-nodtb', subent)
4690 self.assertIn('u-boot-tpl-dtb', subent)
4691
4692 def testFdtInclude(self):
4693 """Test that an Fdt is update within all binaries"""
4694 self._SetupSplElf()
4695 self._SetupTplElf()
4696
4697 # SPL has a devicetree, TPL does not
4698 self.maxDiff = None
4699 entry_args = {
4700 'spl-dtb': '1',
4701 'spl-bss-pad': 'y',
4702 'tpl-dtb': '',
4703 }
4704 # Build the image. It includes two separate devicetree binaries, each
4705 # with their own contents, but all contain the binman definition.
4706 data = self._DoReadFileDtb(
4707 '194_fdt_incl.dts', use_real_dtb=True, use_expanded=True,
4708 update_dtb=True, entry_args=entry_args)[0]
4709 pad_len = 10
4710
4711 # Check the U-Boot dtb
4712 start = len(U_BOOT_NODTB_DATA)
4713 fdt_size = self.checkDtbSizes(data, pad_len, start)
4714
4715 # Now check SPL
4716 start += fdt_size + len(U_BOOT_SPL_NODTB_DATA) + pad_len
4717 fdt_size = self.checkDtbSizes(data, pad_len, start)
4718
4719 # TPL has no devicetree
4720 start += fdt_size + len(U_BOOT_TPL_DATA)
4721 self.assertEqual(len(data), start)
Simon Glass7d398bb2020-10-26 17:40:14 -06004722
Simon Glass3d433382021-03-21 18:24:30 +13004723 def testSymbolsExpanded(self):
4724 """Test binman can assign symbols in expanded entries"""
4725 entry_args = {
4726 'spl-dtb': '1',
4727 }
4728 self.checkSymbols('197_symbols_expand.dts', U_BOOT_SPL_NODTB_DATA +
4729 U_BOOT_SPL_DTB_DATA, 0x38,
4730 entry_args=entry_args, use_expanded=True)
4731
Simon Glass189f2912021-03-21 18:24:31 +13004732 def testCollection(self):
4733 """Test a collection"""
4734 data = self._DoReadFile('198_collection.dts')
4735 self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA +
Simon Glassc1aa66e2022-01-29 14:14:04 -07004736 tools.get_bytes(0xff, 2) + U_BOOT_NODTB_DATA +
4737 tools.get_bytes(0xfe, 3) + U_BOOT_DTB_DATA,
Simon Glass189f2912021-03-21 18:24:31 +13004738 data)
4739
Simon Glass631f7522021-03-21 18:24:32 +13004740 def testCollectionSection(self):
4741 """Test a collection where a section must be built first"""
4742 # Sections never have their contents when GetData() is called, but when
Simon Glassd34bcdd2021-11-23 11:03:47 -07004743 # BuildSectionData() is called with required=True, a section will force
Simon Glass631f7522021-03-21 18:24:32 +13004744 # building the contents, producing an error is anything is still
4745 # missing.
4746 data = self._DoReadFile('199_collection_section.dts')
4747 section = U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA
Simon Glassc1aa66e2022-01-29 14:14:04 -07004748 self.assertEqual(section + U_BOOT_DATA + tools.get_bytes(0xff, 2) +
4749 section + tools.get_bytes(0xfe, 3) + U_BOOT_DATA,
Simon Glass631f7522021-03-21 18:24:32 +13004750 data)
4751
Simon Glass5ff9fed2021-03-21 18:24:33 +13004752 def testAlignDefault(self):
4753 """Test that default alignment works on sections"""
4754 data = self._DoReadFile('200_align_default.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07004755 expected = (U_BOOT_DATA + tools.get_bytes(0, 8 - len(U_BOOT_DATA)) +
Simon Glass5ff9fed2021-03-21 18:24:33 +13004756 U_BOOT_DATA)
4757 # Special alignment for section
Simon Glassc1aa66e2022-01-29 14:14:04 -07004758 expected += tools.get_bytes(0, 32 - len(expected))
Simon Glass5ff9fed2021-03-21 18:24:33 +13004759 # No alignment within the nested section
4760 expected += U_BOOT_DATA + U_BOOT_NODTB_DATA;
4761 # Now the final piece, which should be default-aligned
Simon Glassc1aa66e2022-01-29 14:14:04 -07004762 expected += tools.get_bytes(0, 88 - len(expected)) + U_BOOT_NODTB_DATA
Simon Glass5ff9fed2021-03-21 18:24:33 +13004763 self.assertEqual(expected, data)
Simon Glass631f7522021-03-21 18:24:32 +13004764
Bin Meng4c4d6072021-05-10 20:23:33 +08004765 def testPackOpenSBI(self):
4766 """Test that an image with an OpenSBI binary can be created"""
4767 data = self._DoReadFile('201_opensbi.dts')
4768 self.assertEqual(OPENSBI_DATA, data[:len(OPENSBI_DATA)])
4769
Simon Glassc69d19c2021-07-06 10:36:37 -06004770 def testSectionsSingleThread(self):
4771 """Test sections without multithreading"""
4772 data = self._DoReadFileDtb('055_sections.dts', threads=0)[0]
Simon Glassc1aa66e2022-01-29 14:14:04 -07004773 expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) +
4774 U_BOOT_DATA + tools.get_bytes(ord('a'), 12) +
4775 U_BOOT_DATA + tools.get_bytes(ord('&'), 4))
Simon Glassc69d19c2021-07-06 10:36:37 -06004776 self.assertEqual(expected, data)
4777
4778 def testThreadTimeout(self):
4779 """Test handling a thread that takes too long"""
4780 with self.assertRaises(ValueError) as e:
4781 self._DoTestFile('202_section_timeout.dts',
4782 test_section_timeout=True)
Simon Glassb2dfe832021-10-18 12:13:15 -06004783 self.assertIn("Timed out obtaining contents", str(e.exception))
Simon Glassc69d19c2021-07-06 10:36:37 -06004784
Simon Glass03ebc202021-07-06 10:36:41 -06004785 def testTiming(self):
4786 """Test output of timing information"""
4787 data = self._DoReadFile('055_sections.dts')
4788 with test_util.capture_sys_output() as (stdout, stderr):
4789 state.TimingShow()
4790 self.assertIn('read:', stdout.getvalue())
4791 self.assertIn('compress:', stdout.getvalue())
4792
Simon Glass0427bed2021-11-03 21:09:18 -06004793 def testUpdateFdtInElf(self):
4794 """Test that we can update the devicetree in an ELF file"""
4795 infile = elf_fname = self.ElfTestFile('u_boot_binman_embed')
4796 outfile = os.path.join(self._indir, 'u-boot.out')
4797 begin_sym = 'dtb_embed_begin'
4798 end_sym = 'dtb_embed_end'
4799 retcode = self._DoTestFile(
4800 '060_fdt_update.dts', update_dtb=True,
4801 update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym]))
4802 self.assertEqual(0, retcode)
4803
4804 # Check that the output file does in fact contact a dtb with the binman
4805 # definition in the correct place
4806 syms = elf.GetSymbolFileOffset(infile,
4807 ['dtb_embed_begin', 'dtb_embed_end'])
Simon Glassc1aa66e2022-01-29 14:14:04 -07004808 data = tools.read_file(outfile)
Simon Glass0427bed2021-11-03 21:09:18 -06004809 dtb_data = data[syms['dtb_embed_begin'].offset:
4810 syms['dtb_embed_end'].offset]
4811
4812 dtb = fdt.Fdt.FromData(dtb_data)
4813 dtb.Scan()
4814 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
4815 self.assertEqual({
4816 'image-pos': 0,
4817 'offset': 0,
4818 '_testing:offset': 32,
4819 '_testing:size': 2,
4820 '_testing:image-pos': 32,
4821 'section@0/u-boot:offset': 0,
4822 'section@0/u-boot:size': len(U_BOOT_DATA),
4823 'section@0/u-boot:image-pos': 0,
4824 'section@0:offset': 0,
4825 'section@0:size': 16,
4826 'section@0:image-pos': 0,
4827
4828 'section@1/u-boot:offset': 0,
4829 'section@1/u-boot:size': len(U_BOOT_DATA),
4830 'section@1/u-boot:image-pos': 16,
4831 'section@1:offset': 16,
4832 'section@1:size': 16,
4833 'section@1:image-pos': 16,
4834 'size': 40
4835 }, props)
4836
4837 def testUpdateFdtInElfInvalid(self):
4838 """Test that invalid args are detected with --update-fdt-in-elf"""
4839 with self.assertRaises(ValueError) as e:
4840 self._DoTestFile('060_fdt_update.dts', update_fdt_in_elf='fred')
4841 self.assertIn("Invalid args ['fred'] to --update-fdt-in-elf",
4842 str(e.exception))
4843
4844 def testUpdateFdtInElfNoSyms(self):
4845 """Test that missing symbols are detected with --update-fdt-in-elf"""
4846 infile = elf_fname = self.ElfTestFile('u_boot_binman_embed')
4847 outfile = ''
4848 begin_sym = 'wrong_begin'
4849 end_sym = 'wrong_end'
4850 with self.assertRaises(ValueError) as e:
4851 self._DoTestFile(
4852 '060_fdt_update.dts',
4853 update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym]))
4854 self.assertIn("Expected two symbols 'wrong_begin' and 'wrong_end': got 0:",
4855 str(e.exception))
4856
4857 def testUpdateFdtInElfTooSmall(self):
4858 """Test that an over-large dtb is detected with --update-fdt-in-elf"""
4859 infile = elf_fname = self.ElfTestFile('u_boot_binman_embed_sm')
4860 outfile = os.path.join(self._indir, 'u-boot.out')
4861 begin_sym = 'dtb_embed_begin'
4862 end_sym = 'dtb_embed_end'
4863 with self.assertRaises(ValueError) as e:
4864 self._DoTestFile(
4865 '060_fdt_update.dts', update_dtb=True,
4866 update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym]))
4867 self.assertRegex(
4868 str(e.exception),
4869 "Not enough space in '.*u_boot_binman_embed_sm' for data length.*")
4870
Simon Glassc475dec2021-11-23 11:03:42 -07004871 def testVersion(self):
4872 """Test we can get the binman version"""
4873 version = '(unreleased)'
4874 self.assertEqual(version, state.GetVersion(self._indir))
4875
4876 with self.assertRaises(SystemExit):
4877 with test_util.capture_sys_output() as (_, stderr):
4878 self._DoBinman('-V')
4879 self.assertEqual('Binman %s\n' % version, stderr.getvalue())
4880
4881 # Try running the tool too, just to be safe
4882 result = self._RunBinman('-V')
4883 self.assertEqual('Binman %s\n' % version, result.stderr)
4884
4885 # Set up a version file to make sure that works
4886 version = 'v2025.01-rc2'
Simon Glassc1aa66e2022-01-29 14:14:04 -07004887 tools.write_file(os.path.join(self._indir, 'version'), version,
Simon Glassc475dec2021-11-23 11:03:42 -07004888 binary=False)
4889 self.assertEqual(version, state.GetVersion(self._indir))
4890
Simon Glass943bf782021-11-23 21:09:50 -07004891 def testAltFormat(self):
4892 """Test that alternative formats can be used to extract"""
4893 self._DoReadFileRealDtb('213_fdtmap_alt_format.dts')
4894
4895 try:
4896 tmpdir, updated_fname = self._SetupImageInTmpdir()
4897 with test_util.capture_sys_output() as (stdout, _):
4898 self._DoBinman('extract', '-i', updated_fname, '-F', 'list')
4899 self.assertEqual(
4900 '''Flag (-F) Entry type Description
4901fdt fdtmap Extract the devicetree blob from the fdtmap
4902''',
4903 stdout.getvalue())
4904
4905 dtb = os.path.join(tmpdir, 'fdt.dtb')
4906 self._DoBinman('extract', '-i', updated_fname, '-F', 'fdt', '-f',
4907 dtb, 'fdtmap')
4908
4909 # Check that we can read it and it can be scanning, meaning it does
4910 # not have a 16-byte fdtmap header
Simon Glassc1aa66e2022-01-29 14:14:04 -07004911 data = tools.read_file(dtb)
Simon Glass943bf782021-11-23 21:09:50 -07004912 dtb = fdt.Fdt.FromData(data)
4913 dtb.Scan()
4914
4915 # Now check u-boot which has no alt_format
4916 fname = os.path.join(tmpdir, 'fdt.dtb')
4917 self._DoBinman('extract', '-i', updated_fname, '-F', 'dummy',
4918 '-f', fname, 'u-boot')
Simon Glassc1aa66e2022-01-29 14:14:04 -07004919 data = tools.read_file(fname)
Simon Glass943bf782021-11-23 21:09:50 -07004920 self.assertEqual(U_BOOT_DATA, data)
4921
4922 finally:
4923 shutil.rmtree(tmpdir)
4924
Simon Glasscc2c5002021-11-23 21:09:52 -07004925 def testExtblobList(self):
4926 """Test an image with an external blob list"""
4927 data = self._DoReadFile('215_blob_ext_list.dts')
4928 self.assertEqual(REFCODE_DATA + FSP_M_DATA, data)
4929
4930 def testExtblobListMissing(self):
4931 """Test an image with a missing external blob"""
4932 with self.assertRaises(ValueError) as e:
4933 self._DoReadFile('216_blob_ext_list_missing.dts')
4934 self.assertIn("Filename 'missing-file' not found in input path",
4935 str(e.exception))
4936
4937 def testExtblobListMissingOk(self):
4938 """Test an image with an missing external blob that is allowed"""
4939 with test_util.capture_sys_output() as (stdout, stderr):
4940 self._DoTestFile('216_blob_ext_list_missing.dts',
4941 allow_missing=True)
4942 err = stderr.getvalue()
4943 self.assertRegex(err, "Image 'main-section'.*missing.*: blob-ext")
4944
Simon Glass75989722021-11-23 21:08:59 -07004945 def testFip(self):
4946 """Basic test of generation of an ARM Firmware Image Package (FIP)"""
4947 data = self._DoReadFile('203_fip.dts')
4948 hdr, fents = fip_util.decode_fip(data)
4949 self.assertEqual(fip_util.HEADER_MAGIC, hdr.name)
4950 self.assertEqual(fip_util.HEADER_SERIAL, hdr.serial)
4951 self.assertEqual(0x123, hdr.flags)
4952
4953 self.assertEqual(2, len(fents))
4954
4955 fent = fents[0]
4956 self.assertEqual(
4957 bytes([0x47, 0xd4, 0x08, 0x6d, 0x4c, 0xfe, 0x98, 0x46,
4958 0x9b, 0x95, 0x29, 0x50, 0xcb, 0xbd, 0x5a, 0x0]), fent.uuid)
4959 self.assertEqual('soc-fw', fent.fip_type)
4960 self.assertEqual(0x88, fent.offset)
4961 self.assertEqual(len(ATF_BL31_DATA), fent.size)
4962 self.assertEqual(0x123456789abcdef, fent.flags)
4963 self.assertEqual(ATF_BL31_DATA, fent.data)
4964 self.assertEqual(True, fent.valid)
4965
4966 fent = fents[1]
4967 self.assertEqual(
4968 bytes([0x65, 0x92, 0x27, 0x03, 0x2f, 0x74, 0xe6, 0x44,
4969 0x8d, 0xff, 0x57, 0x9a, 0xc1, 0xff, 0x06, 0x10]), fent.uuid)
4970 self.assertEqual('scp-fwu-cfg', fent.fip_type)
4971 self.assertEqual(0x8c, fent.offset)
4972 self.assertEqual(len(ATF_BL31_DATA), fent.size)
4973 self.assertEqual(0, fent.flags)
4974 self.assertEqual(ATF_BL2U_DATA, fent.data)
4975 self.assertEqual(True, fent.valid)
4976
4977 def testFipOther(self):
4978 """Basic FIP with something that isn't a external blob"""
4979 data = self._DoReadFile('204_fip_other.dts')
4980 hdr, fents = fip_util.decode_fip(data)
4981
4982 self.assertEqual(2, len(fents))
4983 fent = fents[1]
4984 self.assertEqual('rot-cert', fent.fip_type)
4985 self.assertEqual(b'aa', fent.data)
4986
4987 def testFipOther(self):
4988 """Basic FIP with something that isn't a external blob"""
4989 data = self._DoReadFile('204_fip_other.dts')
4990 hdr, fents = fip_util.decode_fip(data)
4991
4992 self.assertEqual(2, len(fents))
4993 fent = fents[1]
4994 self.assertEqual('rot-cert', fent.fip_type)
4995 self.assertEqual(b'aa', fent.data)
4996
4997 def testFipNoType(self):
4998 """FIP with an entry of an unknown type"""
4999 with self.assertRaises(ValueError) as e:
5000 self._DoReadFile('205_fip_no_type.dts')
5001 self.assertIn("Must provide a fip-type (node name 'u-boot' is not a known FIP type)",
5002 str(e.exception))
5003
5004 def testFipUuid(self):
5005 """Basic FIP with a manual uuid"""
5006 data = self._DoReadFile('206_fip_uuid.dts')
5007 hdr, fents = fip_util.decode_fip(data)
5008
5009 self.assertEqual(2, len(fents))
5010 fent = fents[1]
5011 self.assertEqual(None, fent.fip_type)
5012 self.assertEqual(
5013 bytes([0xfc, 0x65, 0x13, 0x92, 0x4a, 0x5b, 0x11, 0xec,
5014 0x94, 0x35, 0xff, 0x2d, 0x1c, 0xfc, 0x79, 0x9c]),
5015 fent.uuid)
5016 self.assertEqual(U_BOOT_DATA, fent.data)
5017
5018 def testFipLs(self):
5019 """Test listing a FIP"""
5020 data = self._DoReadFileRealDtb('207_fip_ls.dts')
5021 hdr, fents = fip_util.decode_fip(data)
5022
5023 try:
5024 tmpdir, updated_fname = self._SetupImageInTmpdir()
5025 with test_util.capture_sys_output() as (stdout, stderr):
5026 self._DoBinman('ls', '-i', updated_fname)
5027 finally:
5028 shutil.rmtree(tmpdir)
5029 lines = stdout.getvalue().splitlines()
5030 expected = [
5031'Name Image-pos Size Entry-type Offset Uncomp-size',
5032'----------------------------------------------------------------',
5033'main-section 0 2d3 section 0',
5034' atf-fip 0 90 atf-fip 0',
5035' soc-fw 88 4 blob-ext 88',
5036' u-boot 8c 4 u-boot 8c',
5037' fdtmap 90 243 fdtmap 90',
5038]
5039 self.assertEqual(expected, lines)
5040
5041 image = control.images['image']
5042 entries = image.GetEntries()
5043 fdtmap = entries['fdtmap']
5044
5045 fdtmap_data = data[fdtmap.image_pos:fdtmap.image_pos + fdtmap.size]
5046 magic = fdtmap_data[:8]
5047 self.assertEqual(b'_FDTMAP_', magic)
Simon Glassc1aa66e2022-01-29 14:14:04 -07005048 self.assertEqual(tools.get_bytes(0, 8), fdtmap_data[8:16])
Simon Glass75989722021-11-23 21:08:59 -07005049
5050 fdt_data = fdtmap_data[16:]
5051 dtb = fdt.Fdt.FromData(fdt_data)
5052 dtb.Scan()
5053 props = self._GetPropTree(dtb, BASE_DTB_PROPS, prefix='/')
5054 self.assertEqual({
5055 'atf-fip/soc-fw:image-pos': 136,
5056 'atf-fip/soc-fw:offset': 136,
5057 'atf-fip/soc-fw:size': 4,
5058 'atf-fip/u-boot:image-pos': 140,
5059 'atf-fip/u-boot:offset': 140,
5060 'atf-fip/u-boot:size': 4,
5061 'atf-fip:image-pos': 0,
5062 'atf-fip:offset': 0,
5063 'atf-fip:size': 144,
5064 'image-pos': 0,
5065 'offset': 0,
5066 'fdtmap:image-pos': fdtmap.image_pos,
5067 'fdtmap:offset': fdtmap.offset,
5068 'fdtmap:size': len(fdtmap_data),
5069 'size': len(data),
5070 }, props)
5071
5072 def testFipExtractOneEntry(self):
5073 """Test extracting a single entry fron an FIP"""
5074 self._DoReadFileRealDtb('207_fip_ls.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07005075 image_fname = tools.get_output_filename('image.bin')
Simon Glass75989722021-11-23 21:08:59 -07005076 fname = os.path.join(self._indir, 'output.extact')
5077 control.ExtractEntries(image_fname, fname, None, ['atf-fip/u-boot'])
Simon Glassc1aa66e2022-01-29 14:14:04 -07005078 data = tools.read_file(fname)
Simon Glass75989722021-11-23 21:08:59 -07005079 self.assertEqual(U_BOOT_DATA, data)
5080
5081 def testFipReplace(self):
5082 """Test replacing a single file in a FIP"""
Simon Glassc1aa66e2022-01-29 14:14:04 -07005083 expected = U_BOOT_DATA + tools.get_bytes(0x78, 50)
Simon Glass75989722021-11-23 21:08:59 -07005084 data = self._DoReadFileRealDtb('208_fip_replace.dts')
Simon Glassc1aa66e2022-01-29 14:14:04 -07005085 updated_fname = tools.get_output_filename('image-updated.bin')
5086 tools.write_file(updated_fname, data)
Simon Glass75989722021-11-23 21:08:59 -07005087 entry_name = 'atf-fip/u-boot'
5088 control.WriteEntry(updated_fname, entry_name, expected,
5089 allow_resize=True)
5090 actual = control.ReadEntry(updated_fname, entry_name)
5091 self.assertEqual(expected, actual)
5092
Simon Glassc1aa66e2022-01-29 14:14:04 -07005093 new_data = tools.read_file(updated_fname)
Simon Glass75989722021-11-23 21:08:59 -07005094 hdr, fents = fip_util.decode_fip(new_data)
5095
5096 self.assertEqual(2, len(fents))
5097
5098 # Check that the FIP entry is updated
5099 fent = fents[1]
5100 self.assertEqual(0x8c, fent.offset)
5101 self.assertEqual(len(expected), fent.size)
5102 self.assertEqual(0, fent.flags)
5103 self.assertEqual(expected, fent.data)
5104 self.assertEqual(True, fent.valid)
5105
5106 def testFipMissing(self):
5107 with test_util.capture_sys_output() as (stdout, stderr):
5108 self._DoTestFile('209_fip_missing.dts', allow_missing=True)
5109 err = stderr.getvalue()
5110 self.assertRegex(err, "Image 'main-section'.*missing.*: rmm-fw")
5111
5112 def testFipSize(self):
5113 """Test a FIP with a size property"""
5114 data = self._DoReadFile('210_fip_size.dts')
5115 self.assertEqual(0x100 + len(U_BOOT_DATA), len(data))
5116 hdr, fents = fip_util.decode_fip(data)
5117 self.assertEqual(fip_util.HEADER_MAGIC, hdr.name)
5118 self.assertEqual(fip_util.HEADER_SERIAL, hdr.serial)
5119
5120 self.assertEqual(1, len(fents))
5121
5122 fent = fents[0]
5123 self.assertEqual('soc-fw', fent.fip_type)
5124 self.assertEqual(0x60, fent.offset)
5125 self.assertEqual(len(ATF_BL31_DATA), fent.size)
5126 self.assertEqual(ATF_BL31_DATA, fent.data)
5127 self.assertEqual(True, fent.valid)
5128
5129 rest = data[0x60 + len(ATF_BL31_DATA):0x100]
Simon Glassc1aa66e2022-01-29 14:14:04 -07005130 self.assertEqual(tools.get_bytes(0xff, len(rest)), rest)
Simon Glass75989722021-11-23 21:08:59 -07005131
5132 def testFipBadAlign(self):
5133 """Test that an invalid alignment value in a FIP is detected"""
5134 with self.assertRaises(ValueError) as e:
5135 self._DoTestFile('211_fip_bad_align.dts')
5136 self.assertIn(
5137 "Node \'/binman/atf-fip\': FIP alignment 31 must be a power of two",
5138 str(e.exception))
5139
5140 def testFipCollection(self):
5141 """Test using a FIP in a collection"""
5142 data = self._DoReadFile('212_fip_collection.dts')
5143 entry1 = control.images['image'].GetEntries()['collection']
5144 data1 = data[:entry1.size]
5145 hdr1, fents2 = fip_util.decode_fip(data1)
5146
5147 entry2 = control.images['image'].GetEntries()['atf-fip']
5148 data2 = data[entry2.offset:entry2.offset + entry2.size]
5149 hdr1, fents2 = fip_util.decode_fip(data2)
5150
5151 # The 'collection' entry should have U-Boot included at the end
5152 self.assertEqual(entry1.size - len(U_BOOT_DATA), entry2.size)
5153 self.assertEqual(data1, data2 + U_BOOT_DATA)
5154 self.assertEqual(U_BOOT_DATA, data1[-4:])
5155
5156 # There should be a U-Boot after the final FIP
5157 self.assertEqual(U_BOOT_DATA, data[-4:])
Simon Glassc69d19c2021-07-06 10:36:37 -06005158
Simon Glass32d4f102022-01-12 13:10:35 -07005159 def testFakeBlob(self):
5160 """Test handling of faking an external blob"""
5161 with test_util.capture_sys_output() as (stdout, stderr):
5162 self._DoTestFile('217_fake_blob.dts', allow_missing=True,
5163 allow_fake_blobs=True)
5164 err = stderr.getvalue()
5165 self.assertRegex(
5166 err,
5167 "Image '.*' has faked external blobs and is non-functional: .*")
Simon Glass32d4f102022-01-12 13:10:35 -07005168
Simon Glassf4590e02022-01-09 20:13:46 -07005169 def testExtblobListFaked(self):
5170 """Test an extblob with missing external blob that are faked"""
5171 with test_util.capture_sys_output() as (stdout, stderr):
5172 self._DoTestFile('216_blob_ext_list_missing.dts',
5173 allow_fake_blobs=True)
5174 err = stderr.getvalue()
5175 self.assertRegex(err, "Image 'main-section'.*faked.*: blob-ext-list")
5176
Simon Glass56ee85e2022-01-09 20:13:57 -07005177 def testListBintools(self):
5178 args = ['tool', '--list']
5179 with test_util.capture_sys_output() as (stdout, _):
5180 self._DoBinman(*args)
5181 out = stdout.getvalue().splitlines()
5182 self.assertTrue(len(out) >= 2)
5183
5184 def testFetchBintools(self):
5185 def fail_download(url):
Simon Glassc1aa66e2022-01-29 14:14:04 -07005186 """Take the tools.download() function by raising an exception"""
Simon Glass56ee85e2022-01-09 20:13:57 -07005187 raise urllib.error.URLError('my error')
5188
5189 args = ['tool']
5190 with self.assertRaises(ValueError) as e:
5191 self._DoBinman(*args)
5192 self.assertIn("Invalid arguments to 'tool' subcommand",
5193 str(e.exception))
5194
5195 args = ['tool', '--fetch']
5196 with self.assertRaises(ValueError) as e:
5197 self._DoBinman(*args)
5198 self.assertIn('Please specify bintools to fetch', str(e.exception))
5199
5200 args = ['tool', '--fetch', '_testing']
Simon Glassc1aa66e2022-01-29 14:14:04 -07005201 with unittest.mock.patch.object(tools, 'download',
Simon Glass56ee85e2022-01-09 20:13:57 -07005202 side_effect=fail_download):
5203 with test_util.capture_sys_output() as (stdout, _):
5204 self._DoBinman(*args)
5205 self.assertIn('failed to fetch with all methods', stdout.getvalue())
5206
Simon Glassa00d9712022-01-09 20:14:10 -07005207 def testInvalidCompress(self):
5208 with self.assertRaises(ValueError) as e:
5209 comp_util.compress(b'', 'invalid')
5210 self.assertIn("Unknown algorithm 'invalid'", str(e.exception))
5211
5212 with self.assertRaises(ValueError) as e:
5213 comp_util.decompress(b'1234', 'invalid')
5214 self.assertIn("Unknown algorithm 'invalid'", str(e.exception))
5215
Simon Glassbc570642022-01-09 20:14:11 -07005216 def testBintoolDocs(self):
5217 """Test for creation of bintool documentation"""
5218 with test_util.capture_sys_output() as (stdout, stderr):
5219 control.write_bintool_docs(control.bintool.Bintool.get_tool_list())
5220 self.assertTrue(len(stdout.getvalue()) > 0)
5221
5222 def testBintoolDocsMissing(self):
5223 """Test handling of missing bintool documentation"""
5224 with self.assertRaises(ValueError) as e:
5225 with test_util.capture_sys_output() as (stdout, stderr):
5226 control.write_bintool_docs(
5227 control.bintool.Bintool.get_tool_list(), 'mkimage')
5228 self.assertIn('Documentation is missing for modules: mkimage',
5229 str(e.exception))
5230
Jan Kiszkafcc87ef2022-01-28 20:37:53 +01005231 def testListWithGenNode(self):
5232 """Check handling of an FDT map when the section cannot be found"""
5233 entry_args = {
5234 'of-list': 'test-fdt1 test-fdt2',
5235 }
5236 data = self._DoReadFileDtb(
5237 '219_fit_gennode.dts',
5238 entry_args=entry_args,
5239 use_real_dtb=True,
5240 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])
5241
5242 try:
5243 tmpdir, updated_fname = self._SetupImageInTmpdir()
5244 with test_util.capture_sys_output() as (stdout, stderr):
5245 self._RunBinman('ls', '-i', updated_fname)
5246 finally:
5247 shutil.rmtree(tmpdir)
5248
Alper Nebi Yasaked293c32022-02-08 01:08:05 +03005249 def testFitSubentryUsesBintool(self):
5250 """Test that binman FIT subentries can use bintools"""
5251 command.test_result = self._HandleGbbCommand
5252 entry_args = {
5253 'keydir': 'devkeys',
5254 'bmpblk': 'bmpblk.bin',
5255 }
5256 data, _, _, _ = self._DoReadFileDtb('220_fit_subentry_bintool.dts',
5257 entry_args=entry_args)
5258
Alper Nebi Yasakf3078d42022-02-08 01:08:07 +03005259 expected = (GBB_DATA + GBB_DATA + tools.get_bytes(0, 8) +
5260 tools.get_bytes(0, 0x2180 - 16))
Alper Nebi Yasaked293c32022-02-08 01:08:05 +03005261 self.assertIn(expected, data)
5262
5263 def testFitSubentryMissingBintool(self):
5264 """Test that binman reports missing bintools for FIT subentries"""
5265 entry_args = {
5266 'keydir': 'devkeys',
5267 }
5268 with test_util.capture_sys_output() as (_, stderr):
5269 self._DoTestFile('220_fit_subentry_bintool.dts',
5270 force_missing_bintools='futility', entry_args=entry_args)
5271 err = stderr.getvalue()
5272 self.assertRegex(err,
5273 "Image 'main-section'.*missing bintools.*: futility")
Simon Glass32d4f102022-01-12 13:10:35 -07005274
Alper Nebi Yasakee813c82022-02-09 22:02:35 +03005275 def testFitSubentryHashSubnode(self):
5276 """Test an image with a FIT inside"""
5277 data, _, _, out_dtb_name = self._DoReadFileDtb(
5278 '221_fit_subentry_hash.dts', use_real_dtb=True, update_dtb=True)
5279
5280 mkimage_dtb = fdt.Fdt.FromData(data)
5281 mkimage_dtb.Scan()
5282 binman_dtb = fdt.Fdt(out_dtb_name)
5283 binman_dtb.Scan()
5284
5285 # Check that binman didn't add hash values
5286 fnode = binman_dtb.GetNode('/binman/fit/images/kernel/hash')
5287 self.assertNotIn('value', fnode.props)
5288
5289 fnode = binman_dtb.GetNode('/binman/fit/images/fdt-1/hash')
5290 self.assertNotIn('value', fnode.props)
5291
5292 # Check that mkimage added hash values
5293 fnode = mkimage_dtb.GetNode('/images/kernel/hash')
5294 self.assertIn('value', fnode.props)
5295
5296 fnode = mkimage_dtb.GetNode('/images/fdt-1/hash')
5297 self.assertIn('value', fnode.props)
5298
Simon Glass9fc60b42017-11-12 21:52:22 -07005299if __name__ == "__main__":
5300 unittest.main()