blob: 04127faa6ffe087bbd710657dda2c1d22c0b58b6 [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 Glassd5164a72019-07-08 13:18:49 -06009from __future__ import print_function
10
Simon Glasse0e5df92018-09-14 04:57:31 -060011import hashlib
Simon Glass4f443042016-11-25 20:15:52 -070012from optparse import OptionParser
13import os
14import shutil
15import struct
16import sys
17import tempfile
18import unittest
19
20import binman
Simon Glassac62fba2019-07-08 13:18:53 -060021import cbfs_util
Simon Glass4f443042016-11-25 20:15:52 -070022import cmdline
23import command
24import control
Simon Glass19790632017-11-13 18:55:01 -070025import elf
Simon Glass99ed4a22017-05-27 07:38:30 -060026import fdt
Simon Glasse1925fa2019-07-08 14:25:44 -060027from etype import fdtmap
Simon Glass2d260032019-07-08 14:25:45 -060028from etype import image_header
Simon Glass4f443042016-11-25 20:15:52 -070029import fdt_util
Simon Glass11e36cc2018-07-17 13:25:38 -060030import fmap_util
Simon Glassfd8d1f72018-07-17 13:25:36 -060031import test_util
Simon Glassc5ac1382019-07-08 13:18:54 -060032import gzip
Simon Glassffded752019-07-08 14:25:46 -060033from image import Image
Simon Glassc55a50f2018-09-14 04:57:19 -060034import state
Simon Glass4f443042016-11-25 20:15:52 -070035import tools
36import tout
37
38# Contents of test files, corresponding to different entry types
Simon Glassc6c10e72019-05-17 22:00:46 -060039U_BOOT_DATA = b'1234'
40U_BOOT_IMG_DATA = b'img'
41U_BOOT_SPL_DATA = b'56780123456789abcde'
42U_BOOT_TPL_DATA = b'tpl'
43BLOB_DATA = b'89'
44ME_DATA = b'0abcd'
45VGA_DATA = b'vga'
46U_BOOT_DTB_DATA = b'udtb'
47U_BOOT_SPL_DTB_DATA = b'spldtb'
48U_BOOT_TPL_DTB_DATA = b'tpldtb'
49X86_START16_DATA = b'start16'
50X86_START16_SPL_DATA = b'start16spl'
51X86_START16_TPL_DATA = b'start16tpl'
Simon Glass2250ee62019-08-24 07:22:48 -060052X86_RESET16_DATA = b'reset16'
53X86_RESET16_SPL_DATA = b'reset16spl'
54X86_RESET16_TPL_DATA = b'reset16tpl'
Simon Glassc6c10e72019-05-17 22:00:46 -060055PPC_MPC85XX_BR_DATA = b'ppcmpc85xxbr'
56U_BOOT_NODTB_DATA = b'nodtb with microcode pointer somewhere in here'
57U_BOOT_SPL_NODTB_DATA = b'splnodtb with microcode pointer somewhere in here'
58U_BOOT_TPL_NODTB_DATA = b'tplnodtb with microcode pointer somewhere in here'
59FSP_DATA = b'fsp'
60CMC_DATA = b'cmc'
61VBT_DATA = b'vbt'
62MRC_DATA = b'mrc'
Simon Glassbb748372018-07-17 13:25:33 -060063TEXT_DATA = 'text'
64TEXT_DATA2 = 'text2'
65TEXT_DATA3 = 'text3'
Simon Glassc6c10e72019-05-17 22:00:46 -060066CROS_EC_RW_DATA = b'ecrw'
67GBB_DATA = b'gbbd'
68BMPBLK_DATA = b'bmp'
69VBLOCK_DATA = b'vblk'
70FILES_DATA = (b"sorry I'm late\nOh, don't bother apologising, I'm " +
71 b"sorry you're alive\n")
Simon Glassff5c7e32019-07-08 13:18:42 -060072COMPRESS_DATA = b'compress xxxxxxxxxxxxxxxxxxxxxx data'
Simon Glassc6c10e72019-05-17 22:00:46 -060073REFCODE_DATA = b'refcode'
Simon Glassec127af2018-07-17 13:25:39 -060074
Simon Glass6ccbfcd2019-07-20 12:23:47 -060075# The expected size for the device tree in some tests
Simon Glassf667e452019-07-08 14:25:50 -060076EXTRACT_DTB_SIZE = 0x3c9
77
Simon Glass6ccbfcd2019-07-20 12:23:47 -060078# Properties expected to be in the device tree when update_dtb is used
79BASE_DTB_PROPS = ['offset', 'size', 'image-pos']
80
Simon Glass12bb1a92019-07-20 12:23:51 -060081# Extra properties expected to be in the device tree when allow-repack is used
82REPACK_DTB_PROPS = ['orig-offset', 'orig-size']
83
Simon Glass4f443042016-11-25 20:15:52 -070084
85class TestFunctional(unittest.TestCase):
86 """Functional tests for binman
87
88 Most of these use a sample .dts file to build an image and then check
89 that it looks correct. The sample files are in the test/ subdirectory
90 and are numbered.
91
92 For each entry type a very small test file is created using fixed
93 string contents. This makes it easy to test that things look right, and
94 debug problems.
95
96 In some cases a 'real' file must be used - these are also supplied in
97 the test/ diurectory.
98 """
99 @classmethod
Simon Glassb986b3b2019-08-24 07:22:43 -0600100 def setUpClass(cls):
Simon Glass4d5994f2017-11-12 21:52:20 -0700101 global entry
102 import entry
103
Simon Glass4f443042016-11-25 20:15:52 -0700104 # Handle the case where argv[0] is 'python'
Simon Glassb986b3b2019-08-24 07:22:43 -0600105 cls._binman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
106 cls._binman_pathname = os.path.join(cls._binman_dir, 'binman')
Simon Glass4f443042016-11-25 20:15:52 -0700107
108 # Create a temporary directory for input files
Simon Glassb986b3b2019-08-24 07:22:43 -0600109 cls._indir = tempfile.mkdtemp(prefix='binmant.')
Simon Glass4f443042016-11-25 20:15:52 -0700110
111 # Create some test files
112 TestFunctional._MakeInputFile('u-boot.bin', U_BOOT_DATA)
113 TestFunctional._MakeInputFile('u-boot.img', U_BOOT_IMG_DATA)
114 TestFunctional._MakeInputFile('spl/u-boot-spl.bin', U_BOOT_SPL_DATA)
Simon Glassb8ef5b62018-07-17 13:25:48 -0600115 TestFunctional._MakeInputFile('tpl/u-boot-tpl.bin', U_BOOT_TPL_DATA)
Simon Glass4f443042016-11-25 20:15:52 -0700116 TestFunctional._MakeInputFile('blobfile', BLOB_DATA)
Simon Glasse0ff8552016-11-25 20:15:53 -0700117 TestFunctional._MakeInputFile('me.bin', ME_DATA)
118 TestFunctional._MakeInputFile('vga.bin', VGA_DATA)
Simon Glassb986b3b2019-08-24 07:22:43 -0600119 cls._ResetDtbs()
Simon Glass2250ee62019-08-24 07:22:48 -0600120
Jagdish Gediya9d368f32018-09-03 21:35:08 +0530121 TestFunctional._MakeInputFile('u-boot-br.bin', PPC_MPC85XX_BR_DATA)
Simon Glass2250ee62019-08-24 07:22:48 -0600122
Simon Glass5e239182019-08-24 07:22:49 -0600123 TestFunctional._MakeInputFile('u-boot-x86-start16.bin', X86_START16_DATA)
124 TestFunctional._MakeInputFile('spl/u-boot-x86-start16-spl.bin',
Simon Glass87722132017-11-12 21:52:26 -0700125 X86_START16_SPL_DATA)
Simon Glass5e239182019-08-24 07:22:49 -0600126 TestFunctional._MakeInputFile('tpl/u-boot-x86-start16-tpl.bin',
Simon Glass35b384c2018-09-14 04:57:10 -0600127 X86_START16_TPL_DATA)
Simon Glass2250ee62019-08-24 07:22:48 -0600128
129 TestFunctional._MakeInputFile('u-boot-x86-reset16.bin',
130 X86_RESET16_DATA)
131 TestFunctional._MakeInputFile('spl/u-boot-x86-reset16-spl.bin',
132 X86_RESET16_SPL_DATA)
133 TestFunctional._MakeInputFile('tpl/u-boot-x86-reset16-tpl.bin',
134 X86_RESET16_TPL_DATA)
135
Simon Glass4f443042016-11-25 20:15:52 -0700136 TestFunctional._MakeInputFile('u-boot-nodtb.bin', U_BOOT_NODTB_DATA)
Simon Glass6b187df2017-11-12 21:52:27 -0700137 TestFunctional._MakeInputFile('spl/u-boot-spl-nodtb.bin',
138 U_BOOT_SPL_NODTB_DATA)
Simon Glassf0253632018-09-14 04:57:32 -0600139 TestFunctional._MakeInputFile('tpl/u-boot-tpl-nodtb.bin',
140 U_BOOT_TPL_NODTB_DATA)
Simon Glassda229092016-11-25 20:15:56 -0700141 TestFunctional._MakeInputFile('fsp.bin', FSP_DATA)
142 TestFunctional._MakeInputFile('cmc.bin', CMC_DATA)
Bin Meng59ea8c22017-08-15 22:41:54 -0700143 TestFunctional._MakeInputFile('vbt.bin', VBT_DATA)
Simon Glassca4f4ff2017-11-12 21:52:28 -0700144 TestFunctional._MakeInputFile('mrc.bin', MRC_DATA)
Simon Glassec127af2018-07-17 13:25:39 -0600145 TestFunctional._MakeInputFile('ecrw.bin', CROS_EC_RW_DATA)
Simon Glass0ef87aa2018-07-17 13:25:44 -0600146 TestFunctional._MakeInputDir('devkeys')
147 TestFunctional._MakeInputFile('bmpblk.bin', BMPBLK_DATA)
Simon Glass3ae192c2018-10-01 12:22:31 -0600148 TestFunctional._MakeInputFile('refcode.bin', REFCODE_DATA)
Simon Glass4f443042016-11-25 20:15:52 -0700149
Simon Glasse0ff8552016-11-25 20:15:53 -0700150 # ELF file with a '_dt_ucode_base_size' symbol
Simon Glassb986b3b2019-08-24 07:22:43 -0600151 with open(cls.TestFile('u_boot_ucode_ptr'), 'rb') as fd:
Simon Glasse0ff8552016-11-25 20:15:53 -0700152 TestFunctional._MakeInputFile('u-boot', fd.read())
153
154 # Intel flash descriptor file
Simon Glassb986b3b2019-08-24 07:22:43 -0600155 with open(cls.TestFile('descriptor.bin'), 'rb') as fd:
Simon Glasse0ff8552016-11-25 20:15:53 -0700156 TestFunctional._MakeInputFile('descriptor.bin', fd.read())
157
Simon Glassb986b3b2019-08-24 07:22:43 -0600158 shutil.copytree(cls.TestFile('files'),
159 os.path.join(cls._indir, 'files'))
Simon Glass0a98b282018-09-14 04:57:28 -0600160
Simon Glass83d73c22018-09-14 04:57:26 -0600161 TestFunctional._MakeInputFile('compress', COMPRESS_DATA)
162
Simon Glassac62fba2019-07-08 13:18:53 -0600163 # Travis-CI may have an old lz4
Simon Glassb986b3b2019-08-24 07:22:43 -0600164 cls.have_lz4 = True
Simon Glassac62fba2019-07-08 13:18:53 -0600165 try:
166 tools.Run('lz4', '--no-frame-crc', '-c',
Simon Glassb986b3b2019-08-24 07:22:43 -0600167 os.path.join(cls._indir, 'u-boot.bin'))
Simon Glassac62fba2019-07-08 13:18:53 -0600168 except:
Simon Glassb986b3b2019-08-24 07:22:43 -0600169 cls.have_lz4 = False
Simon Glassac62fba2019-07-08 13:18:53 -0600170
Simon Glass4f443042016-11-25 20:15:52 -0700171 @classmethod
Simon Glassb986b3b2019-08-24 07:22:43 -0600172 def tearDownClass(cls):
Simon Glass4f443042016-11-25 20:15:52 -0700173 """Remove the temporary input directory and its contents"""
Simon Glassb986b3b2019-08-24 07:22:43 -0600174 if cls.preserve_indir:
175 print('Preserving input dir: %s' % cls._indir)
Simon Glassd5164a72019-07-08 13:18:49 -0600176 else:
Simon Glassb986b3b2019-08-24 07:22:43 -0600177 if cls._indir:
178 shutil.rmtree(cls._indir)
179 cls._indir = None
Simon Glass4f443042016-11-25 20:15:52 -0700180
Simon Glassd5164a72019-07-08 13:18:49 -0600181 @classmethod
Simon Glass8acce602019-07-08 13:18:50 -0600182 def setup_test_args(cls, preserve_indir=False, preserve_outdirs=False,
Simon Glass53cd5d92019-07-08 14:25:29 -0600183 toolpath=None, verbosity=None):
Simon Glassd5164a72019-07-08 13:18:49 -0600184 """Accept arguments controlling test execution
185
186 Args:
187 preserve_indir: Preserve the shared input directory used by all
188 tests in this class.
189 preserve_outdir: Preserve the output directories used by tests. Each
190 test has its own, so this is normally only useful when running a
191 single test.
Simon Glass8acce602019-07-08 13:18:50 -0600192 toolpath: ist of paths to use for tools
Simon Glassd5164a72019-07-08 13:18:49 -0600193 """
194 cls.preserve_indir = preserve_indir
195 cls.preserve_outdirs = preserve_outdirs
Simon Glass8acce602019-07-08 13:18:50 -0600196 cls.toolpath = toolpath
Simon Glass53cd5d92019-07-08 14:25:29 -0600197 cls.verbosity = verbosity
Simon Glassd5164a72019-07-08 13:18:49 -0600198
Simon Glassac62fba2019-07-08 13:18:53 -0600199 def _CheckLz4(self):
200 if not self.have_lz4:
201 self.skipTest('lz4 --no-frame-crc not available')
202
Simon Glassbf574f12019-07-20 12:24:09 -0600203 def _CleanupOutputDir(self):
204 """Remove the temporary output directory"""
205 if self.preserve_outdirs:
206 print('Preserving output dir: %s' % tools.outdir)
207 else:
208 tools._FinaliseForTest()
209
Simon Glass4f443042016-11-25 20:15:52 -0700210 def setUp(self):
211 # Enable this to turn on debugging output
212 # tout.Init(tout.DEBUG)
213 command.test_result = None
214
215 def tearDown(self):
216 """Remove the temporary output directory"""
Simon Glassbf574f12019-07-20 12:24:09 -0600217 self._CleanupOutputDir()
Simon Glass4f443042016-11-25 20:15:52 -0700218
Simon Glassf86a7362019-07-20 12:24:10 -0600219 def _SetupImageInTmpdir(self):
220 """Set up the output image in a new temporary directory
221
222 This is used when an image has been generated in the output directory,
223 but we want to run binman again. This will create a new output
224 directory and fail to delete the original one.
225
226 This creates a new temporary directory, copies the image to it (with a
227 new name) and removes the old output directory.
228
229 Returns:
230 Tuple:
231 Temporary directory to use
232 New image filename
233 """
234 image_fname = tools.GetOutputFilename('image.bin')
235 tmpdir = tempfile.mkdtemp(prefix='binman.')
236 updated_fname = os.path.join(tmpdir, 'image-updated.bin')
237 tools.WriteFile(updated_fname, tools.ReadFile(image_fname))
238 self._CleanupOutputDir()
239 return tmpdir, updated_fname
240
Simon Glassb8ef5b62018-07-17 13:25:48 -0600241 @classmethod
Simon Glassb986b3b2019-08-24 07:22:43 -0600242 def _ResetDtbs(cls):
Simon Glassb8ef5b62018-07-17 13:25:48 -0600243 TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA)
244 TestFunctional._MakeInputFile('spl/u-boot-spl.dtb', U_BOOT_SPL_DTB_DATA)
245 TestFunctional._MakeInputFile('tpl/u-boot-tpl.dtb', U_BOOT_TPL_DTB_DATA)
246
Simon Glass4f443042016-11-25 20:15:52 -0700247 def _RunBinman(self, *args, **kwargs):
248 """Run binman using the command line
249
250 Args:
251 Arguments to pass, as a list of strings
252 kwargs: Arguments to pass to Command.RunPipe()
253 """
254 result = command.RunPipe([[self._binman_pathname] + list(args)],
255 capture=True, capture_stderr=True, raise_on_error=False)
256 if result.return_code and kwargs.get('raise_on_error', True):
257 raise Exception("Error running '%s': %s" % (' '.join(args),
258 result.stdout + result.stderr))
259 return result
260
Simon Glass53cd5d92019-07-08 14:25:29 -0600261 def _DoBinman(self, *argv):
Simon Glass4f443042016-11-25 20:15:52 -0700262 """Run binman using directly (in the same process)
263
264 Args:
265 Arguments to pass, as a list of strings
266 Returns:
267 Return value (0 for success)
268 """
Simon Glass53cd5d92019-07-08 14:25:29 -0600269 argv = list(argv)
270 args = cmdline.ParseArgs(argv)
271 args.pager = 'binman-invalid-pager'
272 args.build_dir = self._indir
Simon Glass4f443042016-11-25 20:15:52 -0700273
274 # For testing, you can force an increase in verbosity here
Simon Glass53cd5d92019-07-08 14:25:29 -0600275 # args.verbosity = tout.DEBUG
276 return control.Binman(args)
Simon Glass4f443042016-11-25 20:15:52 -0700277
Simon Glass53af22a2018-07-17 13:25:32 -0600278 def _DoTestFile(self, fname, debug=False, map=False, update_dtb=False,
Simon Glasseb833d82019-04-25 21:58:34 -0600279 entry_args=None, images=None, use_real_dtb=False,
280 verbosity=None):
Simon Glass4f443042016-11-25 20:15:52 -0700281 """Run binman with a given test file
282
283 Args:
Simon Glass741f2d62018-10-01 12:22:30 -0600284 fname: Device-tree source filename to use (e.g. 005_simple.dts)
Simon Glass7ae5f312018-06-01 09:38:19 -0600285 debug: True to enable debugging output
Simon Glass3b0c3822018-06-01 09:38:20 -0600286 map: True to output map files for the images
Simon Glass3ab95982018-08-01 15:22:37 -0600287 update_dtb: Update the offset and size of each entry in the device
Simon Glass16b8d6b2018-07-06 10:27:42 -0600288 tree before packing it into the image
Simon Glass0bfa7b02018-09-14 04:57:12 -0600289 entry_args: Dict of entry args to supply to binman
290 key: arg name
291 value: value of that arg
292 images: List of image names to build
Simon Glass4f443042016-11-25 20:15:52 -0700293 """
Simon Glass53cd5d92019-07-08 14:25:29 -0600294 args = []
Simon Glass7fe91732017-11-13 18:55:00 -0700295 if debug:
296 args.append('-D')
Simon Glass53cd5d92019-07-08 14:25:29 -0600297 if verbosity is not None:
298 args.append('-v%d' % verbosity)
299 elif self.verbosity:
300 args.append('-v%d' % self.verbosity)
301 if self.toolpath:
302 for path in self.toolpath:
303 args += ['--toolpath', path]
304 args += ['build', '-p', '-I', self._indir, '-d', self.TestFile(fname)]
Simon Glass3b0c3822018-06-01 09:38:20 -0600305 if map:
306 args.append('-m')
Simon Glass16b8d6b2018-07-06 10:27:42 -0600307 if update_dtb:
Simon Glass2569e102019-07-08 13:18:47 -0600308 args.append('-u')
Simon Glass93d17412018-09-14 04:57:23 -0600309 if not use_real_dtb:
310 args.append('--fake-dtb')
Simon Glass53af22a2018-07-17 13:25:32 -0600311 if entry_args:
Simon Glass50979152019-05-14 15:53:41 -0600312 for arg, value in entry_args.items():
Simon Glass53af22a2018-07-17 13:25:32 -0600313 args.append('-a%s=%s' % (arg, value))
Simon Glass0bfa7b02018-09-14 04:57:12 -0600314 if images:
315 for image in images:
316 args += ['-i', image]
Simon Glass7fe91732017-11-13 18:55:00 -0700317 return self._DoBinman(*args)
Simon Glass4f443042016-11-25 20:15:52 -0700318
319 def _SetupDtb(self, fname, outfile='u-boot.dtb'):
Simon Glasse0ff8552016-11-25 20:15:53 -0700320 """Set up a new test device-tree file
321
322 The given file is compiled and set up as the device tree to be used
323 for ths test.
324
325 Args:
326 fname: Filename of .dts file to read
Simon Glass7ae5f312018-06-01 09:38:19 -0600327 outfile: Output filename for compiled device-tree binary
Simon Glasse0ff8552016-11-25 20:15:53 -0700328
329 Returns:
Simon Glass7ae5f312018-06-01 09:38:19 -0600330 Contents of device-tree binary
Simon Glasse0ff8552016-11-25 20:15:53 -0700331 """
Simon Glassa004f292019-07-20 12:23:49 -0600332 tmpdir = tempfile.mkdtemp(prefix='binmant.')
333 dtb = fdt_util.EnsureCompiled(self.TestFile(fname), tmpdir)
Simon Glass1d0ebf72019-05-14 15:53:42 -0600334 with open(dtb, 'rb') as fd:
Simon Glass4f443042016-11-25 20:15:52 -0700335 data = fd.read()
336 TestFunctional._MakeInputFile(outfile, data)
Simon Glassa004f292019-07-20 12:23:49 -0600337 shutil.rmtree(tmpdir)
Simon Glasse0e62752018-10-01 21:12:41 -0600338 return data
Simon Glass4f443042016-11-25 20:15:52 -0700339
Simon Glass6ed45ba2018-09-14 04:57:24 -0600340 def _GetDtbContentsForSplTpl(self, dtb_data, name):
341 """Create a version of the main DTB for SPL or SPL
342
343 For testing we don't actually have different versions of the DTB. With
344 U-Boot we normally run fdtgrep to remove unwanted nodes, but for tests
345 we don't normally have any unwanted nodes.
346
347 We still want the DTBs for SPL and TPL to be different though, since
348 otherwise it is confusing to know which one we are looking at. So add
349 an 'spl' or 'tpl' property to the top-level node.
350 """
351 dtb = fdt.Fdt.FromData(dtb_data)
352 dtb.Scan()
353 dtb.GetNode('/binman').AddZeroProp(name)
354 dtb.Sync(auto_resize=True)
355 dtb.Pack()
356 return dtb.GetContents()
357
Simon Glass16b8d6b2018-07-06 10:27:42 -0600358 def _DoReadFileDtb(self, fname, use_real_dtb=False, map=False,
Simon Glass6ed45ba2018-09-14 04:57:24 -0600359 update_dtb=False, entry_args=None, reset_dtbs=True):
Simon Glass4f443042016-11-25 20:15:52 -0700360 """Run binman and return the resulting image
361
362 This runs binman with a given test file and then reads the resulting
363 output file. It is a shortcut function since most tests need to do
364 these steps.
365
366 Raises an assertion failure if binman returns a non-zero exit code.
367
368 Args:
Simon Glass741f2d62018-10-01 12:22:30 -0600369 fname: Device-tree source filename to use (e.g. 005_simple.dts)
Simon Glass4f443042016-11-25 20:15:52 -0700370 use_real_dtb: True to use the test file as the contents of
371 the u-boot-dtb entry. Normally this is not needed and the
372 test contents (the U_BOOT_DTB_DATA string) can be used.
373 But in some test we need the real contents.
Simon Glass3b0c3822018-06-01 09:38:20 -0600374 map: True to output map files for the images
Simon Glass3ab95982018-08-01 15:22:37 -0600375 update_dtb: Update the offset and size of each entry in the device
Simon Glass16b8d6b2018-07-06 10:27:42 -0600376 tree before packing it into the image
Simon Glasse0ff8552016-11-25 20:15:53 -0700377
378 Returns:
379 Tuple:
380 Resulting image contents
381 Device tree contents
Simon Glass3b0c3822018-06-01 09:38:20 -0600382 Map data showing contents of image (or None if none)
Simon Glassea6922e2018-07-17 13:25:27 -0600383 Output device tree binary filename ('u-boot.dtb' path)
Simon Glass4f443042016-11-25 20:15:52 -0700384 """
Simon Glasse0ff8552016-11-25 20:15:53 -0700385 dtb_data = None
Simon Glass4f443042016-11-25 20:15:52 -0700386 # Use the compiled test file as the u-boot-dtb input
387 if use_real_dtb:
Simon Glasse0ff8552016-11-25 20:15:53 -0700388 dtb_data = self._SetupDtb(fname)
Simon Glass6ed45ba2018-09-14 04:57:24 -0600389
390 # For testing purposes, make a copy of the DT for SPL and TPL. Add
391 # a node indicating which it is, so aid verification.
392 for name in ['spl', 'tpl']:
393 dtb_fname = '%s/u-boot-%s.dtb' % (name, name)
394 outfile = os.path.join(self._indir, dtb_fname)
395 TestFunctional._MakeInputFile(dtb_fname,
396 self._GetDtbContentsForSplTpl(dtb_data, name))
Simon Glass4f443042016-11-25 20:15:52 -0700397
398 try:
Simon Glass53af22a2018-07-17 13:25:32 -0600399 retcode = self._DoTestFile(fname, map=map, update_dtb=update_dtb,
Simon Glass6ed45ba2018-09-14 04:57:24 -0600400 entry_args=entry_args, use_real_dtb=use_real_dtb)
Simon Glass4f443042016-11-25 20:15:52 -0700401 self.assertEqual(0, retcode)
Simon Glass6ed45ba2018-09-14 04:57:24 -0600402 out_dtb_fname = tools.GetOutputFilename('u-boot.dtb.out')
Simon Glass4f443042016-11-25 20:15:52 -0700403
404 # Find the (only) image, read it and return its contents
405 image = control.images['image']
Simon Glass16b8d6b2018-07-06 10:27:42 -0600406 image_fname = tools.GetOutputFilename('image.bin')
407 self.assertTrue(os.path.exists(image_fname))
Simon Glass3b0c3822018-06-01 09:38:20 -0600408 if map:
409 map_fname = tools.GetOutputFilename('image.map')
410 with open(map_fname) as fd:
411 map_data = fd.read()
412 else:
413 map_data = None
Simon Glass1d0ebf72019-05-14 15:53:42 -0600414 with open(image_fname, 'rb') as fd:
Simon Glass16b8d6b2018-07-06 10:27:42 -0600415 return fd.read(), dtb_data, map_data, out_dtb_fname
Simon Glass4f443042016-11-25 20:15:52 -0700416 finally:
417 # Put the test file back
Simon Glass6ed45ba2018-09-14 04:57:24 -0600418 if reset_dtbs and use_real_dtb:
Simon Glassb8ef5b62018-07-17 13:25:48 -0600419 self._ResetDtbs()
Simon Glass4f443042016-11-25 20:15:52 -0700420
Simon Glass3c081312019-07-08 14:25:26 -0600421 def _DoReadFileRealDtb(self, fname):
422 """Run binman with a real .dtb file and return the resulting data
423
424 Args:
425 fname: DT source filename to use (e.g. 082_fdt_update_all.dts)
426
427 Returns:
428 Resulting image contents
429 """
430 return self._DoReadFileDtb(fname, use_real_dtb=True, update_dtb=True)[0]
431
Simon Glasse0ff8552016-11-25 20:15:53 -0700432 def _DoReadFile(self, fname, use_real_dtb=False):
Simon Glass7ae5f312018-06-01 09:38:19 -0600433 """Helper function which discards the device-tree binary
434
435 Args:
Simon Glass741f2d62018-10-01 12:22:30 -0600436 fname: Device-tree source filename to use (e.g. 005_simple.dts)
Simon Glass7ae5f312018-06-01 09:38:19 -0600437 use_real_dtb: True to use the test file as the contents of
438 the u-boot-dtb entry. Normally this is not needed and the
439 test contents (the U_BOOT_DTB_DATA string) can be used.
440 But in some test we need the real contents.
Simon Glassea6922e2018-07-17 13:25:27 -0600441
442 Returns:
443 Resulting image contents
Simon Glass7ae5f312018-06-01 09:38:19 -0600444 """
Simon Glasse0ff8552016-11-25 20:15:53 -0700445 return self._DoReadFileDtb(fname, use_real_dtb)[0]
446
Simon Glass4f443042016-11-25 20:15:52 -0700447 @classmethod
Simon Glassb986b3b2019-08-24 07:22:43 -0600448 def _MakeInputFile(cls, fname, contents):
Simon Glass4f443042016-11-25 20:15:52 -0700449 """Create a new test input file, creating directories as needed
450
451 Args:
Simon Glass3ab95982018-08-01 15:22:37 -0600452 fname: Filename to create
Simon Glass4f443042016-11-25 20:15:52 -0700453 contents: File contents to write in to the file
454 Returns:
455 Full pathname of file created
456 """
Simon Glassb986b3b2019-08-24 07:22:43 -0600457 pathname = os.path.join(cls._indir, fname)
Simon Glass4f443042016-11-25 20:15:52 -0700458 dirname = os.path.dirname(pathname)
459 if dirname and not os.path.exists(dirname):
460 os.makedirs(dirname)
461 with open(pathname, 'wb') as fd:
462 fd.write(contents)
463 return pathname
464
465 @classmethod
Simon Glassb986b3b2019-08-24 07:22:43 -0600466 def _MakeInputDir(cls, dirname):
Simon Glass0ef87aa2018-07-17 13:25:44 -0600467 """Create a new test input directory, creating directories as needed
468
469 Args:
470 dirname: Directory name to create
471
472 Returns:
473 Full pathname of directory created
474 """
Simon Glassb986b3b2019-08-24 07:22:43 -0600475 pathname = os.path.join(cls._indir, dirname)
Simon Glass0ef87aa2018-07-17 13:25:44 -0600476 if not os.path.exists(pathname):
477 os.makedirs(pathname)
478 return pathname
479
480 @classmethod
Simon Glassb986b3b2019-08-24 07:22:43 -0600481 def _SetupSplElf(cls, src_fname='bss_data'):
Simon Glass11ae93e2018-10-01 21:12:47 -0600482 """Set up an ELF file with a '_dt_ucode_base_size' symbol
483
484 Args:
485 Filename of ELF file to use as SPL
486 """
Simon Glassb986b3b2019-08-24 07:22:43 -0600487 with open(cls.TestFile(src_fname), 'rb') as fd:
Simon Glass11ae93e2018-10-01 21:12:47 -0600488 TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
489
490 @classmethod
Simon Glassb986b3b2019-08-24 07:22:43 -0600491 def TestFile(cls, fname):
492 return os.path.join(cls._binman_dir, 'test', fname)
Simon Glass4f443042016-11-25 20:15:52 -0700493
494 def AssertInList(self, grep_list, target):
495 """Assert that at least one of a list of things is in a target
496
497 Args:
498 grep_list: List of strings to check
499 target: Target string
500 """
501 for grep in grep_list:
502 if grep in target:
503 return
Simon Glass1fc62de2019-05-17 22:00:50 -0600504 self.fail("Error: '%s' not found in '%s'" % (grep_list, target))
Simon Glass4f443042016-11-25 20:15:52 -0700505
506 def CheckNoGaps(self, entries):
507 """Check that all entries fit together without gaps
508
509 Args:
510 entries: List of entries to check
511 """
Simon Glass3ab95982018-08-01 15:22:37 -0600512 offset = 0
Simon Glass4f443042016-11-25 20:15:52 -0700513 for entry in entries.values():
Simon Glass3ab95982018-08-01 15:22:37 -0600514 self.assertEqual(offset, entry.offset)
515 offset += entry.size
Simon Glass4f443042016-11-25 20:15:52 -0700516
Simon Glasse0ff8552016-11-25 20:15:53 -0700517 def GetFdtLen(self, dtb):
Simon Glass7ae5f312018-06-01 09:38:19 -0600518 """Get the totalsize field from a device-tree binary
Simon Glasse0ff8552016-11-25 20:15:53 -0700519
520 Args:
Simon Glass7ae5f312018-06-01 09:38:19 -0600521 dtb: Device-tree binary contents
Simon Glasse0ff8552016-11-25 20:15:53 -0700522
523 Returns:
Simon Glass7ae5f312018-06-01 09:38:19 -0600524 Total size of device-tree binary, from the header
Simon Glasse0ff8552016-11-25 20:15:53 -0700525 """
526 return struct.unpack('>L', dtb[4:8])[0]
527
Simon Glass086cec92019-07-08 14:25:27 -0600528 def _GetPropTree(self, dtb, prop_names, prefix='/binman/'):
Simon Glass16b8d6b2018-07-06 10:27:42 -0600529 def AddNode(node, path):
530 if node.name != '/':
531 path += '/' + node.name
Simon Glass086cec92019-07-08 14:25:27 -0600532 for prop in node.props.values():
533 if prop.name in prop_names:
534 prop_path = path + ':' + prop.name
535 tree[prop_path[len(prefix):]] = fdt_util.fdt32_to_cpu(
536 prop.value)
Simon Glass16b8d6b2018-07-06 10:27:42 -0600537 for subnode in node.subnodes:
Simon Glass16b8d6b2018-07-06 10:27:42 -0600538 AddNode(subnode, path)
539
540 tree = {}
Simon Glass16b8d6b2018-07-06 10:27:42 -0600541 AddNode(dtb.GetRoot(), '')
542 return tree
543
Simon Glass4f443042016-11-25 20:15:52 -0700544 def testRun(self):
545 """Test a basic run with valid args"""
546 result = self._RunBinman('-h')
547
548 def testFullHelp(self):
549 """Test that the full help is displayed with -H"""
550 result = self._RunBinman('-H')
551 help_file = os.path.join(self._binman_dir, 'README')
Tom Rini3759df02018-01-16 15:29:50 -0500552 # Remove possible extraneous strings
553 extra = '::::::::::::::\n' + help_file + '\n::::::::::::::\n'
554 gothelp = result.stdout.replace(extra, '')
555 self.assertEqual(len(gothelp), os.path.getsize(help_file))
Simon Glass4f443042016-11-25 20:15:52 -0700556 self.assertEqual(0, len(result.stderr))
557 self.assertEqual(0, result.return_code)
558
559 def testFullHelpInternal(self):
560 """Test that the full help is displayed with -H"""
561 try:
562 command.test_result = command.CommandResult()
563 result = self._DoBinman('-H')
564 help_file = os.path.join(self._binman_dir, 'README')
565 finally:
566 command.test_result = None
567
568 def testHelp(self):
569 """Test that the basic help is displayed with -h"""
570 result = self._RunBinman('-h')
571 self.assertTrue(len(result.stdout) > 200)
572 self.assertEqual(0, len(result.stderr))
573 self.assertEqual(0, result.return_code)
574
Simon Glass4f443042016-11-25 20:15:52 -0700575 def testBoard(self):
576 """Test that we can run it with a specific board"""
Simon Glass741f2d62018-10-01 12:22:30 -0600577 self._SetupDtb('005_simple.dts', 'sandbox/u-boot.dtb')
Simon Glass4f443042016-11-25 20:15:52 -0700578 TestFunctional._MakeInputFile('sandbox/u-boot.bin', U_BOOT_DATA)
Simon Glass53cd5d92019-07-08 14:25:29 -0600579 result = self._DoBinman('build', '-b', 'sandbox')
Simon Glass4f443042016-11-25 20:15:52 -0700580 self.assertEqual(0, result)
581
582 def testNeedBoard(self):
583 """Test that we get an error when no board ius supplied"""
584 with self.assertRaises(ValueError) as e:
Simon Glass53cd5d92019-07-08 14:25:29 -0600585 result = self._DoBinman('build')
Simon Glass4f443042016-11-25 20:15:52 -0700586 self.assertIn("Must provide a board to process (use -b <board>)",
587 str(e.exception))
588
589 def testMissingDt(self):
Simon Glass7ae5f312018-06-01 09:38:19 -0600590 """Test that an invalid device-tree file generates an error"""
Simon Glass4f443042016-11-25 20:15:52 -0700591 with self.assertRaises(Exception) as e:
Simon Glass53cd5d92019-07-08 14:25:29 -0600592 self._RunBinman('build', '-d', 'missing_file')
Simon Glass4f443042016-11-25 20:15:52 -0700593 # We get one error from libfdt, and a different one from fdtget.
594 self.AssertInList(["Couldn't open blob from 'missing_file'",
595 'No such file or directory'], str(e.exception))
596
597 def testBrokenDt(self):
Simon Glass7ae5f312018-06-01 09:38:19 -0600598 """Test that an invalid device-tree source file generates an error
Simon Glass4f443042016-11-25 20:15:52 -0700599
600 Since this is a source file it should be compiled and the error
601 will come from the device-tree compiler (dtc).
602 """
603 with self.assertRaises(Exception) as e:
Simon Glass53cd5d92019-07-08 14:25:29 -0600604 self._RunBinman('build', '-d', self.TestFile('001_invalid.dts'))
Simon Glass4f443042016-11-25 20:15:52 -0700605 self.assertIn("FATAL ERROR: Unable to parse input tree",
606 str(e.exception))
607
608 def testMissingNode(self):
609 """Test that a device tree without a 'binman' node generates an error"""
610 with self.assertRaises(Exception) as e:
Simon Glass53cd5d92019-07-08 14:25:29 -0600611 self._DoBinman('build', '-d', self.TestFile('002_missing_node.dts'))
Simon Glass4f443042016-11-25 20:15:52 -0700612 self.assertIn("does not have a 'binman' node", str(e.exception))
613
614 def testEmpty(self):
615 """Test that an empty binman node works OK (i.e. does nothing)"""
Simon Glass53cd5d92019-07-08 14:25:29 -0600616 result = self._RunBinman('build', '-d', self.TestFile('003_empty.dts'))
Simon Glass4f443042016-11-25 20:15:52 -0700617 self.assertEqual(0, len(result.stderr))
618 self.assertEqual(0, result.return_code)
619
620 def testInvalidEntry(self):
621 """Test that an invalid entry is flagged"""
622 with self.assertRaises(Exception) as e:
Simon Glass53cd5d92019-07-08 14:25:29 -0600623 result = self._RunBinman('build', '-d',
Simon Glass741f2d62018-10-01 12:22:30 -0600624 self.TestFile('004_invalid_entry.dts'))
Simon Glass4f443042016-11-25 20:15:52 -0700625 self.assertIn("Unknown entry type 'not-a-valid-type' in node "
626 "'/binman/not-a-valid-type'", str(e.exception))
627
628 def testSimple(self):
629 """Test a simple binman with a single file"""
Simon Glass741f2d62018-10-01 12:22:30 -0600630 data = self._DoReadFile('005_simple.dts')
Simon Glass4f443042016-11-25 20:15:52 -0700631 self.assertEqual(U_BOOT_DATA, data)
632
Simon Glass7fe91732017-11-13 18:55:00 -0700633 def testSimpleDebug(self):
634 """Test a simple binman run with debugging enabled"""
Simon Glasse2705fa2019-07-08 14:25:53 -0600635 self._DoTestFile('005_simple.dts', debug=True)
Simon Glass7fe91732017-11-13 18:55:00 -0700636
Simon Glass4f443042016-11-25 20:15:52 -0700637 def testDual(self):
638 """Test that we can handle creating two images
639
640 This also tests image padding.
641 """
Simon Glass741f2d62018-10-01 12:22:30 -0600642 retcode = self._DoTestFile('006_dual_image.dts')
Simon Glass4f443042016-11-25 20:15:52 -0700643 self.assertEqual(0, retcode)
644
645 image = control.images['image1']
Simon Glass8beb11e2019-07-08 14:25:47 -0600646 self.assertEqual(len(U_BOOT_DATA), image.size)
Simon Glass4f443042016-11-25 20:15:52 -0700647 fname = tools.GetOutputFilename('image1.bin')
648 self.assertTrue(os.path.exists(fname))
Simon Glass1d0ebf72019-05-14 15:53:42 -0600649 with open(fname, 'rb') as fd:
Simon Glass4f443042016-11-25 20:15:52 -0700650 data = fd.read()
651 self.assertEqual(U_BOOT_DATA, data)
652
653 image = control.images['image2']
Simon Glass8beb11e2019-07-08 14:25:47 -0600654 self.assertEqual(3 + len(U_BOOT_DATA) + 5, image.size)
Simon Glass4f443042016-11-25 20:15:52 -0700655 fname = tools.GetOutputFilename('image2.bin')
656 self.assertTrue(os.path.exists(fname))
Simon Glass1d0ebf72019-05-14 15:53:42 -0600657 with open(fname, 'rb') as fd:
Simon Glass4f443042016-11-25 20:15:52 -0700658 data = fd.read()
659 self.assertEqual(U_BOOT_DATA, data[3:7])
Simon Glasse6d85ff2019-05-14 15:53:47 -0600660 self.assertEqual(tools.GetBytes(0, 3), data[:3])
661 self.assertEqual(tools.GetBytes(0, 5), data[7:])
Simon Glass4f443042016-11-25 20:15:52 -0700662
663 def testBadAlign(self):
664 """Test that an invalid alignment value is detected"""
665 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -0600666 self._DoTestFile('007_bad_align.dts')
Simon Glass4f443042016-11-25 20:15:52 -0700667 self.assertIn("Node '/binman/u-boot': Alignment 23 must be a power "
668 "of two", str(e.exception))
669
670 def testPackSimple(self):
671 """Test that packing works as expected"""
Simon Glass741f2d62018-10-01 12:22:30 -0600672 retcode = self._DoTestFile('008_pack.dts')
Simon Glass4f443042016-11-25 20:15:52 -0700673 self.assertEqual(0, retcode)
674 self.assertIn('image', control.images)
675 image = control.images['image']
Simon Glass8f1da502018-06-01 09:38:12 -0600676 entries = image.GetEntries()
Simon Glass4f443042016-11-25 20:15:52 -0700677 self.assertEqual(5, len(entries))
678
679 # First u-boot
680 self.assertIn('u-boot', entries)
681 entry = entries['u-boot']
Simon Glass3ab95982018-08-01 15:22:37 -0600682 self.assertEqual(0, entry.offset)
Simon Glass4f443042016-11-25 20:15:52 -0700683 self.assertEqual(len(U_BOOT_DATA), entry.size)
684
685 # Second u-boot, aligned to 16-byte boundary
686 self.assertIn('u-boot-align', entries)
687 entry = entries['u-boot-align']
Simon Glass3ab95982018-08-01 15:22:37 -0600688 self.assertEqual(16, entry.offset)
Simon Glass4f443042016-11-25 20:15:52 -0700689 self.assertEqual(len(U_BOOT_DATA), entry.size)
690
691 # Third u-boot, size 23 bytes
692 self.assertIn('u-boot-size', entries)
693 entry = entries['u-boot-size']
Simon Glass3ab95982018-08-01 15:22:37 -0600694 self.assertEqual(20, entry.offset)
Simon Glass4f443042016-11-25 20:15:52 -0700695 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
696 self.assertEqual(23, entry.size)
697
698 # Fourth u-boot, placed immediate after the above
699 self.assertIn('u-boot-next', entries)
700 entry = entries['u-boot-next']
Simon Glass3ab95982018-08-01 15:22:37 -0600701 self.assertEqual(43, entry.offset)
Simon Glass4f443042016-11-25 20:15:52 -0700702 self.assertEqual(len(U_BOOT_DATA), entry.size)
703
Simon Glass3ab95982018-08-01 15:22:37 -0600704 # Fifth u-boot, placed at a fixed offset
Simon Glass4f443042016-11-25 20:15:52 -0700705 self.assertIn('u-boot-fixed', entries)
706 entry = entries['u-boot-fixed']
Simon Glass3ab95982018-08-01 15:22:37 -0600707 self.assertEqual(61, entry.offset)
Simon Glass4f443042016-11-25 20:15:52 -0700708 self.assertEqual(len(U_BOOT_DATA), entry.size)
709
Simon Glass8beb11e2019-07-08 14:25:47 -0600710 self.assertEqual(65, image.size)
Simon Glass4f443042016-11-25 20:15:52 -0700711
712 def testPackExtra(self):
713 """Test that extra packing feature works as expected"""
Simon Glass741f2d62018-10-01 12:22:30 -0600714 retcode = self._DoTestFile('009_pack_extra.dts')
Simon Glass4f443042016-11-25 20:15:52 -0700715
716 self.assertEqual(0, retcode)
717 self.assertIn('image', control.images)
718 image = control.images['image']
Simon Glass8f1da502018-06-01 09:38:12 -0600719 entries = image.GetEntries()
Simon Glass4f443042016-11-25 20:15:52 -0700720 self.assertEqual(5, len(entries))
721
722 # First u-boot with padding before and after
723 self.assertIn('u-boot', entries)
724 entry = entries['u-boot']
Simon Glass3ab95982018-08-01 15:22:37 -0600725 self.assertEqual(0, entry.offset)
Simon Glass4f443042016-11-25 20:15:52 -0700726 self.assertEqual(3, entry.pad_before)
727 self.assertEqual(3 + 5 + len(U_BOOT_DATA), entry.size)
728
729 # Second u-boot has an aligned size, but it has no effect
730 self.assertIn('u-boot-align-size-nop', entries)
731 entry = entries['u-boot-align-size-nop']
Simon Glass3ab95982018-08-01 15:22:37 -0600732 self.assertEqual(12, entry.offset)
Simon Glass4f443042016-11-25 20:15:52 -0700733 self.assertEqual(4, entry.size)
734
735 # Third u-boot has an aligned size too
736 self.assertIn('u-boot-align-size', entries)
737 entry = entries['u-boot-align-size']
Simon Glass3ab95982018-08-01 15:22:37 -0600738 self.assertEqual(16, entry.offset)
Simon Glass4f443042016-11-25 20:15:52 -0700739 self.assertEqual(32, entry.size)
740
741 # Fourth u-boot has an aligned end
742 self.assertIn('u-boot-align-end', entries)
743 entry = entries['u-boot-align-end']
Simon Glass3ab95982018-08-01 15:22:37 -0600744 self.assertEqual(48, entry.offset)
Simon Glass4f443042016-11-25 20:15:52 -0700745 self.assertEqual(16, entry.size)
746
747 # Fifth u-boot immediately afterwards
748 self.assertIn('u-boot-align-both', entries)
749 entry = entries['u-boot-align-both']
Simon Glass3ab95982018-08-01 15:22:37 -0600750 self.assertEqual(64, entry.offset)
Simon Glass4f443042016-11-25 20:15:52 -0700751 self.assertEqual(64, entry.size)
752
753 self.CheckNoGaps(entries)
Simon Glass8beb11e2019-07-08 14:25:47 -0600754 self.assertEqual(128, image.size)
Simon Glass4f443042016-11-25 20:15:52 -0700755
756 def testPackAlignPowerOf2(self):
757 """Test that invalid entry alignment is detected"""
758 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -0600759 self._DoTestFile('010_pack_align_power2.dts')
Simon Glass4f443042016-11-25 20:15:52 -0700760 self.assertIn("Node '/binman/u-boot': Alignment 5 must be a power "
761 "of two", str(e.exception))
762
763 def testPackAlignSizePowerOf2(self):
764 """Test that invalid entry size alignment is detected"""
765 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -0600766 self._DoTestFile('011_pack_align_size_power2.dts')
Simon Glass4f443042016-11-25 20:15:52 -0700767 self.assertIn("Node '/binman/u-boot': Alignment size 55 must be a "
768 "power of two", str(e.exception))
769
770 def testPackInvalidAlign(self):
Simon Glass3ab95982018-08-01 15:22:37 -0600771 """Test detection of an offset that does not match its alignment"""
Simon Glass4f443042016-11-25 20:15:52 -0700772 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -0600773 self._DoTestFile('012_pack_inv_align.dts')
Simon Glass3ab95982018-08-01 15:22:37 -0600774 self.assertIn("Node '/binman/u-boot': Offset 0x5 (5) does not match "
Simon Glass4f443042016-11-25 20:15:52 -0700775 "align 0x4 (4)", str(e.exception))
776
777 def testPackInvalidSizeAlign(self):
778 """Test that invalid entry size alignment is detected"""
779 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -0600780 self._DoTestFile('013_pack_inv_size_align.dts')
Simon Glass4f443042016-11-25 20:15:52 -0700781 self.assertIn("Node '/binman/u-boot': Size 0x5 (5) does not match "
782 "align-size 0x4 (4)", str(e.exception))
783
784 def testPackOverlap(self):
785 """Test that overlapping regions are detected"""
786 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -0600787 self._DoTestFile('014_pack_overlap.dts')
Simon Glass3ab95982018-08-01 15:22:37 -0600788 self.assertIn("Node '/binman/u-boot-align': Offset 0x3 (3) overlaps "
Simon Glass4f443042016-11-25 20:15:52 -0700789 "with previous entry '/binman/u-boot' ending at 0x4 (4)",
790 str(e.exception))
791
792 def testPackEntryOverflow(self):
793 """Test that entries that overflow their size are detected"""
794 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -0600795 self._DoTestFile('015_pack_overflow.dts')
Simon Glass4f443042016-11-25 20:15:52 -0700796 self.assertIn("Node '/binman/u-boot': Entry contents size is 0x4 (4) "
797 "but entry size is 0x3 (3)", str(e.exception))
798
799 def testPackImageOverflow(self):
800 """Test that entries which overflow the image size are detected"""
801 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -0600802 self._DoTestFile('016_pack_image_overflow.dts')
Simon Glass8f1da502018-06-01 09:38:12 -0600803 self.assertIn("Section '/binman': contents size 0x4 (4) exceeds section "
Simon Glass4f443042016-11-25 20:15:52 -0700804 "size 0x3 (3)", str(e.exception))
805
806 def testPackImageSize(self):
807 """Test that the image size can be set"""
Simon Glass741f2d62018-10-01 12:22:30 -0600808 retcode = self._DoTestFile('017_pack_image_size.dts')
Simon Glass4f443042016-11-25 20:15:52 -0700809 self.assertEqual(0, retcode)
810 self.assertIn('image', control.images)
811 image = control.images['image']
Simon Glass8beb11e2019-07-08 14:25:47 -0600812 self.assertEqual(7, image.size)
Simon Glass4f443042016-11-25 20:15:52 -0700813
814 def testPackImageSizeAlign(self):
815 """Test that image size alignemnt works as expected"""
Simon Glass741f2d62018-10-01 12:22:30 -0600816 retcode = self._DoTestFile('018_pack_image_align.dts')
Simon Glass4f443042016-11-25 20:15:52 -0700817 self.assertEqual(0, retcode)
818 self.assertIn('image', control.images)
819 image = control.images['image']
Simon Glass8beb11e2019-07-08 14:25:47 -0600820 self.assertEqual(16, image.size)
Simon Glass4f443042016-11-25 20:15:52 -0700821
822 def testPackInvalidImageAlign(self):
823 """Test that invalid image alignment is detected"""
824 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -0600825 self._DoTestFile('019_pack_inv_image_align.dts')
Simon Glass8f1da502018-06-01 09:38:12 -0600826 self.assertIn("Section '/binman': Size 0x7 (7) does not match "
Simon Glass4f443042016-11-25 20:15:52 -0700827 "align-size 0x8 (8)", str(e.exception))
828
829 def testPackAlignPowerOf2(self):
830 """Test that invalid image alignment is detected"""
831 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -0600832 self._DoTestFile('020_pack_inv_image_align_power2.dts')
Simon Glass8beb11e2019-07-08 14:25:47 -0600833 self.assertIn("Image '/binman': Alignment size 131 must be a power of "
Simon Glass4f443042016-11-25 20:15:52 -0700834 "two", str(e.exception))
835
836 def testImagePadByte(self):
837 """Test that the image pad byte can be specified"""
Simon Glass11ae93e2018-10-01 21:12:47 -0600838 self._SetupSplElf()
Simon Glass741f2d62018-10-01 12:22:30 -0600839 data = self._DoReadFile('021_image_pad.dts')
Simon Glasse6d85ff2019-05-14 15:53:47 -0600840 self.assertEqual(U_BOOT_SPL_DATA + tools.GetBytes(0xff, 1) +
841 U_BOOT_DATA, data)
Simon Glass4f443042016-11-25 20:15:52 -0700842
843 def testImageName(self):
844 """Test that image files can be named"""
Simon Glass741f2d62018-10-01 12:22:30 -0600845 retcode = self._DoTestFile('022_image_name.dts')
Simon Glass4f443042016-11-25 20:15:52 -0700846 self.assertEqual(0, retcode)
847 image = control.images['image1']
848 fname = tools.GetOutputFilename('test-name')
849 self.assertTrue(os.path.exists(fname))
850
851 image = control.images['image2']
852 fname = tools.GetOutputFilename('test-name.xx')
853 self.assertTrue(os.path.exists(fname))
854
855 def testBlobFilename(self):
856 """Test that generic blobs can be provided by filename"""
Simon Glass741f2d62018-10-01 12:22:30 -0600857 data = self._DoReadFile('023_blob.dts')
Simon Glass4f443042016-11-25 20:15:52 -0700858 self.assertEqual(BLOB_DATA, data)
859
860 def testPackSorted(self):
861 """Test that entries can be sorted"""
Simon Glass11ae93e2018-10-01 21:12:47 -0600862 self._SetupSplElf()
Simon Glass741f2d62018-10-01 12:22:30 -0600863 data = self._DoReadFile('024_sorted.dts')
Simon Glasse6d85ff2019-05-14 15:53:47 -0600864 self.assertEqual(tools.GetBytes(0, 1) + U_BOOT_SPL_DATA +
865 tools.GetBytes(0, 2) + U_BOOT_DATA, data)
Simon Glass4f443042016-11-25 20:15:52 -0700866
Simon Glass3ab95982018-08-01 15:22:37 -0600867 def testPackZeroOffset(self):
868 """Test that an entry at offset 0 is not given a new offset"""
Simon Glass4f443042016-11-25 20:15:52 -0700869 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -0600870 self._DoTestFile('025_pack_zero_size.dts')
Simon Glass3ab95982018-08-01 15:22:37 -0600871 self.assertIn("Node '/binman/u-boot-spl': Offset 0x0 (0) overlaps "
Simon Glass4f443042016-11-25 20:15:52 -0700872 "with previous entry '/binman/u-boot' ending at 0x4 (4)",
873 str(e.exception))
874
875 def testPackUbootDtb(self):
876 """Test that a device tree can be added to U-Boot"""
Simon Glass741f2d62018-10-01 12:22:30 -0600877 data = self._DoReadFile('026_pack_u_boot_dtb.dts')
Simon Glass4f443042016-11-25 20:15:52 -0700878 self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA, data)
Simon Glasse0ff8552016-11-25 20:15:53 -0700879
880 def testPackX86RomNoSize(self):
881 """Test that the end-at-4gb property requires a size property"""
882 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -0600883 self._DoTestFile('027_pack_4gb_no_size.dts')
Simon Glass8beb11e2019-07-08 14:25:47 -0600884 self.assertIn("Image '/binman': Section size must be provided when "
Simon Glasse0ff8552016-11-25 20:15:53 -0700885 "using end-at-4gb", str(e.exception))
886
Jagdish Gediya94b57db2018-09-03 21:35:07 +0530887 def test4gbAndSkipAtStartTogether(self):
888 """Test that the end-at-4gb and skip-at-size property can't be used
889 together"""
890 with self.assertRaises(ValueError) as e:
891 self._DoTestFile('80_4gb_and_skip_at_start_together.dts')
Simon Glass8beb11e2019-07-08 14:25:47 -0600892 self.assertIn("Image '/binman': Provide either 'end-at-4gb' or "
Jagdish Gediya94b57db2018-09-03 21:35:07 +0530893 "'skip-at-start'", str(e.exception))
894
Simon Glasse0ff8552016-11-25 20:15:53 -0700895 def testPackX86RomOutside(self):
Simon Glass3ab95982018-08-01 15:22:37 -0600896 """Test that the end-at-4gb property checks for offset boundaries"""
Simon Glasse0ff8552016-11-25 20:15:53 -0700897 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -0600898 self._DoTestFile('028_pack_4gb_outside.dts')
Simon Glass3ab95982018-08-01 15:22:37 -0600899 self.assertIn("Node '/binman/u-boot': Offset 0x0 (0) is outside "
Simon Glass8f1da502018-06-01 09:38:12 -0600900 "the section starting at 0xffffffe0 (4294967264)",
Simon Glasse0ff8552016-11-25 20:15:53 -0700901 str(e.exception))
902
903 def testPackX86Rom(self):
904 """Test that a basic x86 ROM can be created"""
Simon Glass11ae93e2018-10-01 21:12:47 -0600905 self._SetupSplElf()
Simon Glass741f2d62018-10-01 12:22:30 -0600906 data = self._DoReadFile('029_x86-rom.dts')
Simon Glasse6d85ff2019-05-14 15:53:47 -0600907 self.assertEqual(U_BOOT_DATA + tools.GetBytes(0, 7) + U_BOOT_SPL_DATA +
908 tools.GetBytes(0, 2), data)
Simon Glasse0ff8552016-11-25 20:15:53 -0700909
910 def testPackX86RomMeNoDesc(self):
911 """Test that an invalid Intel descriptor entry is detected"""
Simon Glassc6c10e72019-05-17 22:00:46 -0600912 TestFunctional._MakeInputFile('descriptor.bin', b'')
Simon Glasse0ff8552016-11-25 20:15:53 -0700913 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -0600914 self._DoTestFile('031_x86-rom-me.dts')
Simon Glass458be452019-07-08 13:18:32 -0600915 self.assertIn("Node '/binman/intel-descriptor': Cannot find Intel Flash Descriptor (FD) signature",
916 str(e.exception))
Simon Glasse0ff8552016-11-25 20:15:53 -0700917
918 def testPackX86RomBadDesc(self):
919 """Test that the Intel requires a descriptor entry"""
920 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -0600921 self._DoTestFile('030_x86-rom-me-no-desc.dts')
Simon Glass3ab95982018-08-01 15:22:37 -0600922 self.assertIn("Node '/binman/intel-me': No offset set with "
923 "offset-unset: should another entry provide this correct "
924 "offset?", str(e.exception))
Simon Glasse0ff8552016-11-25 20:15:53 -0700925
926 def testPackX86RomMe(self):
927 """Test that an x86 ROM with an ME region can be created"""
Simon Glass741f2d62018-10-01 12:22:30 -0600928 data = self._DoReadFile('031_x86-rom-me.dts')
Simon Glassc5ac1382019-07-08 13:18:54 -0600929 expected_desc = tools.ReadFile(self.TestFile('descriptor.bin'))
930 if data[:0x1000] != expected_desc:
931 self.fail('Expected descriptor binary at start of image')
Simon Glasse0ff8552016-11-25 20:15:53 -0700932 self.assertEqual(ME_DATA, data[0x1000:0x1000 + len(ME_DATA)])
933
934 def testPackVga(self):
935 """Test that an image with a VGA binary can be created"""
Simon Glass741f2d62018-10-01 12:22:30 -0600936 data = self._DoReadFile('032_intel-vga.dts')
Simon Glasse0ff8552016-11-25 20:15:53 -0700937 self.assertEqual(VGA_DATA, data[:len(VGA_DATA)])
938
939 def testPackStart16(self):
940 """Test that an image with an x86 start16 region can be created"""
Simon Glass741f2d62018-10-01 12:22:30 -0600941 data = self._DoReadFile('033_x86-start16.dts')
Simon Glasse0ff8552016-11-25 20:15:53 -0700942 self.assertEqual(X86_START16_DATA, data[:len(X86_START16_DATA)])
943
Jagdish Gediya9d368f32018-09-03 21:35:08 +0530944 def testPackPowerpcMpc85xxBootpgResetvec(self):
945 """Test that an image with powerpc-mpc85xx-bootpg-resetvec can be
946 created"""
947 data = self._DoReadFile('81_powerpc_mpc85xx_bootpg_resetvec.dts')
948 self.assertEqual(PPC_MPC85XX_BR_DATA, data[:len(PPC_MPC85XX_BR_DATA)])
949
Simon Glass736bb0a2018-07-06 10:27:17 -0600950 def _RunMicrocodeTest(self, dts_fname, nodtb_data, ucode_second=False):
Simon Glassadc57012018-07-06 10:27:16 -0600951 """Handle running a test for insertion of microcode
952
953 Args:
954 dts_fname: Name of test .dts file
955 nodtb_data: Data that we expect in the first section
Simon Glass736bb0a2018-07-06 10:27:17 -0600956 ucode_second: True if the microsecond entry is second instead of
957 third
Simon Glassadc57012018-07-06 10:27:16 -0600958
959 Returns:
960 Tuple:
961 Contents of first region (U-Boot or SPL)
Simon Glass3ab95982018-08-01 15:22:37 -0600962 Offset and size components of microcode pointer, as inserted
Simon Glassadc57012018-07-06 10:27:16 -0600963 in the above (two 4-byte words)
964 """
Simon Glass6b187df2017-11-12 21:52:27 -0700965 data = self._DoReadFile(dts_fname, True)
Simon Glasse0ff8552016-11-25 20:15:53 -0700966
967 # Now check the device tree has no microcode
Simon Glass736bb0a2018-07-06 10:27:17 -0600968 if ucode_second:
969 ucode_content = data[len(nodtb_data):]
970 ucode_pos = len(nodtb_data)
971 dtb_with_ucode = ucode_content[16:]
972 fdt_len = self.GetFdtLen(dtb_with_ucode)
973 else:
974 dtb_with_ucode = data[len(nodtb_data):]
975 fdt_len = self.GetFdtLen(dtb_with_ucode)
976 ucode_content = dtb_with_ucode[fdt_len:]
977 ucode_pos = len(nodtb_data) + fdt_len
Simon Glasse0ff8552016-11-25 20:15:53 -0700978 fname = tools.GetOutputFilename('test.dtb')
979 with open(fname, 'wb') as fd:
Simon Glassadc57012018-07-06 10:27:16 -0600980 fd.write(dtb_with_ucode)
Simon Glassec3f3782017-05-27 07:38:29 -0600981 dtb = fdt.FdtScan(fname)
982 ucode = dtb.GetNode('/microcode')
Simon Glasse0ff8552016-11-25 20:15:53 -0700983 self.assertTrue(ucode)
984 for node in ucode.subnodes:
985 self.assertFalse(node.props.get('data'))
986
Simon Glasse0ff8552016-11-25 20:15:53 -0700987 # Check that the microcode appears immediately after the Fdt
988 # This matches the concatenation of the data properties in
Simon Glass87722132017-11-12 21:52:26 -0700989 # the /microcode/update@xxx nodes in 34_x86_ucode.dts.
Simon Glasse0ff8552016-11-25 20:15:53 -0700990 ucode_data = struct.pack('>4L', 0x12345678, 0x12345679, 0xabcd0000,
991 0x78235609)
Simon Glassadc57012018-07-06 10:27:16 -0600992 self.assertEqual(ucode_data, ucode_content[:len(ucode_data)])
Simon Glasse0ff8552016-11-25 20:15:53 -0700993
994 # Check that the microcode pointer was inserted. It should match the
Simon Glass3ab95982018-08-01 15:22:37 -0600995 # expected offset and size
Simon Glasse0ff8552016-11-25 20:15:53 -0700996 pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
997 len(ucode_data))
Simon Glass736bb0a2018-07-06 10:27:17 -0600998 u_boot = data[:len(nodtb_data)]
999 return u_boot, pos_and_size
Simon Glass6b187df2017-11-12 21:52:27 -07001000
1001 def testPackUbootMicrocode(self):
1002 """Test that x86 microcode can be handled correctly
1003
1004 We expect to see the following in the image, in order:
1005 u-boot-nodtb.bin with a microcode pointer inserted at the correct
1006 place
1007 u-boot.dtb with the microcode removed
1008 the microcode
1009 """
Simon Glass741f2d62018-10-01 12:22:30 -06001010 first, pos_and_size = self._RunMicrocodeTest('034_x86_ucode.dts',
Simon Glass6b187df2017-11-12 21:52:27 -07001011 U_BOOT_NODTB_DATA)
Simon Glassc6c10e72019-05-17 22:00:46 -06001012 self.assertEqual(b'nodtb with microcode' + pos_and_size +
1013 b' somewhere in here', first)
Simon Glasse0ff8552016-11-25 20:15:53 -07001014
Simon Glass160a7662017-05-27 07:38:26 -06001015 def _RunPackUbootSingleMicrocode(self):
Simon Glasse0ff8552016-11-25 20:15:53 -07001016 """Test that x86 microcode can be handled correctly
1017
1018 We expect to see the following in the image, in order:
1019 u-boot-nodtb.bin with a microcode pointer inserted at the correct
1020 place
1021 u-boot.dtb with the microcode
1022 an empty microcode region
1023 """
1024 # We need the libfdt library to run this test since only that allows
1025 # finding the offset of a property. This is required by
1026 # Entry_u_boot_dtb_with_ucode.ObtainContents().
Simon Glass741f2d62018-10-01 12:22:30 -06001027 data = self._DoReadFile('035_x86_single_ucode.dts', True)
Simon Glasse0ff8552016-11-25 20:15:53 -07001028
1029 second = data[len(U_BOOT_NODTB_DATA):]
1030
1031 fdt_len = self.GetFdtLen(second)
1032 third = second[fdt_len:]
1033 second = second[:fdt_len]
1034
Simon Glass160a7662017-05-27 07:38:26 -06001035 ucode_data = struct.pack('>2L', 0x12345678, 0x12345679)
1036 self.assertIn(ucode_data, second)
1037 ucode_pos = second.find(ucode_data) + len(U_BOOT_NODTB_DATA)
Simon Glasse0ff8552016-11-25 20:15:53 -07001038
Simon Glass160a7662017-05-27 07:38:26 -06001039 # Check that the microcode pointer was inserted. It should match the
Simon Glass3ab95982018-08-01 15:22:37 -06001040 # expected offset and size
Simon Glass160a7662017-05-27 07:38:26 -06001041 pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
1042 len(ucode_data))
1043 first = data[:len(U_BOOT_NODTB_DATA)]
Simon Glassc6c10e72019-05-17 22:00:46 -06001044 self.assertEqual(b'nodtb with microcode' + pos_and_size +
1045 b' somewhere in here', first)
Simon Glassc49deb82016-11-25 20:15:54 -07001046
Simon Glass75db0862016-11-25 20:15:55 -07001047 def testPackUbootSingleMicrocode(self):
1048 """Test that x86 microcode can be handled correctly with fdt_normal.
1049 """
Simon Glass160a7662017-05-27 07:38:26 -06001050 self._RunPackUbootSingleMicrocode()
Simon Glass75db0862016-11-25 20:15:55 -07001051
Simon Glassc49deb82016-11-25 20:15:54 -07001052 def testUBootImg(self):
1053 """Test that u-boot.img can be put in a file"""
Simon Glass741f2d62018-10-01 12:22:30 -06001054 data = self._DoReadFile('036_u_boot_img.dts')
Simon Glassc49deb82016-11-25 20:15:54 -07001055 self.assertEqual(U_BOOT_IMG_DATA, data)
Simon Glass75db0862016-11-25 20:15:55 -07001056
1057 def testNoMicrocode(self):
1058 """Test that a missing microcode region is detected"""
1059 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001060 self._DoReadFile('037_x86_no_ucode.dts', True)
Simon Glass75db0862016-11-25 20:15:55 -07001061 self.assertIn("Node '/binman/u-boot-dtb-with-ucode': No /microcode "
1062 "node found in ", str(e.exception))
1063
1064 def testMicrocodeWithoutNode(self):
1065 """Test that a missing u-boot-dtb-with-ucode node is detected"""
1066 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001067 self._DoReadFile('038_x86_ucode_missing_node.dts', True)
Simon Glass75db0862016-11-25 20:15:55 -07001068 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
1069 "microcode region u-boot-dtb-with-ucode", str(e.exception))
1070
1071 def testMicrocodeWithoutNode2(self):
1072 """Test that a missing u-boot-ucode node is detected"""
1073 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001074 self._DoReadFile('039_x86_ucode_missing_node2.dts', True)
Simon Glass75db0862016-11-25 20:15:55 -07001075 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
1076 "microcode region u-boot-ucode", str(e.exception))
1077
1078 def testMicrocodeWithoutPtrInElf(self):
1079 """Test that a U-Boot binary without the microcode symbol is detected"""
1080 # ELF file without a '_dt_ucode_base_size' symbol
Simon Glass75db0862016-11-25 20:15:55 -07001081 try:
Simon Glass1d0ebf72019-05-14 15:53:42 -06001082 with open(self.TestFile('u_boot_no_ucode_ptr'), 'rb') as fd:
Simon Glass75db0862016-11-25 20:15:55 -07001083 TestFunctional._MakeInputFile('u-boot', fd.read())
1084
1085 with self.assertRaises(ValueError) as e:
Simon Glass160a7662017-05-27 07:38:26 -06001086 self._RunPackUbootSingleMicrocode()
Simon Glass75db0862016-11-25 20:15:55 -07001087 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot locate "
1088 "_dt_ucode_base_size symbol in u-boot", str(e.exception))
1089
1090 finally:
1091 # Put the original file back
Simon Glass1d0ebf72019-05-14 15:53:42 -06001092 with open(self.TestFile('u_boot_ucode_ptr'), 'rb') as fd:
Simon Glass75db0862016-11-25 20:15:55 -07001093 TestFunctional._MakeInputFile('u-boot', fd.read())
1094
1095 def testMicrocodeNotInImage(self):
1096 """Test that microcode must be placed within the image"""
1097 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001098 self._DoReadFile('040_x86_ucode_not_in_image.dts', True)
Simon Glass75db0862016-11-25 20:15:55 -07001099 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Microcode "
1100 "pointer _dt_ucode_base_size at fffffe14 is outside the "
Simon Glass25ac0e62018-06-01 09:38:14 -06001101 "section ranging from 00000000 to 0000002e", str(e.exception))
Simon Glass75db0862016-11-25 20:15:55 -07001102
1103 def testWithoutMicrocode(self):
1104 """Test that we can cope with an image without microcode (e.g. qemu)"""
Simon Glass1d0ebf72019-05-14 15:53:42 -06001105 with open(self.TestFile('u_boot_no_ucode_ptr'), 'rb') as fd:
Simon Glass75db0862016-11-25 20:15:55 -07001106 TestFunctional._MakeInputFile('u-boot', fd.read())
Simon Glass741f2d62018-10-01 12:22:30 -06001107 data, dtb, _, _ = self._DoReadFileDtb('044_x86_optional_ucode.dts', True)
Simon Glass75db0862016-11-25 20:15:55 -07001108
1109 # Now check the device tree has no microcode
1110 self.assertEqual(U_BOOT_NODTB_DATA, data[:len(U_BOOT_NODTB_DATA)])
1111 second = data[len(U_BOOT_NODTB_DATA):]
1112
1113 fdt_len = self.GetFdtLen(second)
1114 self.assertEqual(dtb, second[:fdt_len])
1115
1116 used_len = len(U_BOOT_NODTB_DATA) + fdt_len
1117 third = data[used_len:]
Simon Glasse6d85ff2019-05-14 15:53:47 -06001118 self.assertEqual(tools.GetBytes(0, 0x200 - used_len), third)
Simon Glass75db0862016-11-25 20:15:55 -07001119
1120 def testUnknownPosSize(self):
1121 """Test that microcode must be placed within the image"""
1122 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001123 self._DoReadFile('041_unknown_pos_size.dts', True)
Simon Glass3ab95982018-08-01 15:22:37 -06001124 self.assertIn("Section '/binman': Unable to set offset/size for unknown "
Simon Glass75db0862016-11-25 20:15:55 -07001125 "entry 'invalid-entry'", str(e.exception))
Simon Glassda229092016-11-25 20:15:56 -07001126
1127 def testPackFsp(self):
1128 """Test that an image with a FSP binary can be created"""
Simon Glass741f2d62018-10-01 12:22:30 -06001129 data = self._DoReadFile('042_intel-fsp.dts')
Simon Glassda229092016-11-25 20:15:56 -07001130 self.assertEqual(FSP_DATA, data[:len(FSP_DATA)])
1131
1132 def testPackCmc(self):
Bin Meng59ea8c22017-08-15 22:41:54 -07001133 """Test that an image with a CMC binary can be created"""
Simon Glass741f2d62018-10-01 12:22:30 -06001134 data = self._DoReadFile('043_intel-cmc.dts')
Simon Glassda229092016-11-25 20:15:56 -07001135 self.assertEqual(CMC_DATA, data[:len(CMC_DATA)])
Bin Meng59ea8c22017-08-15 22:41:54 -07001136
1137 def testPackVbt(self):
1138 """Test that an image with a VBT binary can be created"""
Simon Glass741f2d62018-10-01 12:22:30 -06001139 data = self._DoReadFile('046_intel-vbt.dts')
Bin Meng59ea8c22017-08-15 22:41:54 -07001140 self.assertEqual(VBT_DATA, data[:len(VBT_DATA)])
Simon Glass9fc60b42017-11-12 21:52:22 -07001141
Simon Glass56509842017-11-12 21:52:25 -07001142 def testSplBssPad(self):
1143 """Test that we can pad SPL's BSS with zeros"""
Simon Glass6b187df2017-11-12 21:52:27 -07001144 # ELF file with a '__bss_size' symbol
Simon Glass11ae93e2018-10-01 21:12:47 -06001145 self._SetupSplElf()
Simon Glass741f2d62018-10-01 12:22:30 -06001146 data = self._DoReadFile('047_spl_bss_pad.dts')
Simon Glasse6d85ff2019-05-14 15:53:47 -06001147 self.assertEqual(U_BOOT_SPL_DATA + tools.GetBytes(0, 10) + U_BOOT_DATA,
1148 data)
Simon Glass56509842017-11-12 21:52:25 -07001149
Simon Glass86af5112018-10-01 21:12:42 -06001150 def testSplBssPadMissing(self):
1151 """Test that a missing symbol is detected"""
Simon Glass11ae93e2018-10-01 21:12:47 -06001152 self._SetupSplElf('u_boot_ucode_ptr')
Simon Glassb50e5612017-11-13 18:54:54 -07001153 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001154 self._DoReadFile('047_spl_bss_pad.dts')
Simon Glassb50e5612017-11-13 18:54:54 -07001155 self.assertIn('Expected __bss_size symbol in spl/u-boot-spl',
1156 str(e.exception))
1157
Simon Glass87722132017-11-12 21:52:26 -07001158 def testPackStart16Spl(self):
Simon Glass35b384c2018-09-14 04:57:10 -06001159 """Test that an image with an x86 start16 SPL region can be created"""
Simon Glass741f2d62018-10-01 12:22:30 -06001160 data = self._DoReadFile('048_x86-start16-spl.dts')
Simon Glass87722132017-11-12 21:52:26 -07001161 self.assertEqual(X86_START16_SPL_DATA, data[:len(X86_START16_SPL_DATA)])
1162
Simon Glass736bb0a2018-07-06 10:27:17 -06001163 def _PackUbootSplMicrocode(self, dts, ucode_second=False):
1164 """Helper function for microcode tests
Simon Glass6b187df2017-11-12 21:52:27 -07001165
1166 We expect to see the following in the image, in order:
1167 u-boot-spl-nodtb.bin with a microcode pointer inserted at the
1168 correct place
1169 u-boot.dtb with the microcode removed
1170 the microcode
Simon Glass736bb0a2018-07-06 10:27:17 -06001171
1172 Args:
1173 dts: Device tree file to use for test
1174 ucode_second: True if the microsecond entry is second instead of
1175 third
Simon Glass6b187df2017-11-12 21:52:27 -07001176 """
Simon Glass11ae93e2018-10-01 21:12:47 -06001177 self._SetupSplElf('u_boot_ucode_ptr')
Simon Glass736bb0a2018-07-06 10:27:17 -06001178 first, pos_and_size = self._RunMicrocodeTest(dts, U_BOOT_SPL_NODTB_DATA,
1179 ucode_second=ucode_second)
Simon Glassc6c10e72019-05-17 22:00:46 -06001180 self.assertEqual(b'splnodtb with microc' + pos_and_size +
1181 b'ter somewhere in here', first)
Simon Glass6b187df2017-11-12 21:52:27 -07001182
Simon Glass736bb0a2018-07-06 10:27:17 -06001183 def testPackUbootSplMicrocode(self):
1184 """Test that x86 microcode can be handled correctly in SPL"""
Simon Glass741f2d62018-10-01 12:22:30 -06001185 self._PackUbootSplMicrocode('049_x86_ucode_spl.dts')
Simon Glass736bb0a2018-07-06 10:27:17 -06001186
1187 def testPackUbootSplMicrocodeReorder(self):
1188 """Test that order doesn't matter for microcode entries
1189
1190 This is the same as testPackUbootSplMicrocode but when we process the
1191 u-boot-ucode entry we have not yet seen the u-boot-dtb-with-ucode
1192 entry, so we reply on binman to try later.
1193 """
Simon Glass741f2d62018-10-01 12:22:30 -06001194 self._PackUbootSplMicrocode('058_x86_ucode_spl_needs_retry.dts',
Simon Glass736bb0a2018-07-06 10:27:17 -06001195 ucode_second=True)
1196
Simon Glassca4f4ff2017-11-12 21:52:28 -07001197 def testPackMrc(self):
1198 """Test that an image with an MRC binary can be created"""
Simon Glass741f2d62018-10-01 12:22:30 -06001199 data = self._DoReadFile('050_intel_mrc.dts')
Simon Glassca4f4ff2017-11-12 21:52:28 -07001200 self.assertEqual(MRC_DATA, data[:len(MRC_DATA)])
1201
Simon Glass47419ea2017-11-13 18:54:55 -07001202 def testSplDtb(self):
1203 """Test that an image with spl/u-boot-spl.dtb can be created"""
Simon Glass741f2d62018-10-01 12:22:30 -06001204 data = self._DoReadFile('051_u_boot_spl_dtb.dts')
Simon Glass47419ea2017-11-13 18:54:55 -07001205 self.assertEqual(U_BOOT_SPL_DTB_DATA, data[:len(U_BOOT_SPL_DTB_DATA)])
1206
Simon Glass4e6fdbe2017-11-13 18:54:56 -07001207 def testSplNoDtb(self):
1208 """Test that an image with spl/u-boot-spl-nodtb.bin can be created"""
Simon Glass741f2d62018-10-01 12:22:30 -06001209 data = self._DoReadFile('052_u_boot_spl_nodtb.dts')
Simon Glass4e6fdbe2017-11-13 18:54:56 -07001210 self.assertEqual(U_BOOT_SPL_NODTB_DATA, data[:len(U_BOOT_SPL_NODTB_DATA)])
1211
Simon Glass19790632017-11-13 18:55:01 -07001212 def testSymbols(self):
1213 """Test binman can assign symbols embedded in U-Boot"""
1214 elf_fname = self.TestFile('u_boot_binman_syms')
1215 syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
1216 addr = elf.GetSymbolAddress(elf_fname, '__image_copy_start')
Simon Glass3ab95982018-08-01 15:22:37 -06001217 self.assertEqual(syms['_binman_u_boot_spl_prop_offset'].address, addr)
Simon Glass19790632017-11-13 18:55:01 -07001218
Simon Glass11ae93e2018-10-01 21:12:47 -06001219 self._SetupSplElf('u_boot_binman_syms')
Simon Glass741f2d62018-10-01 12:22:30 -06001220 data = self._DoReadFile('053_symbols.dts')
Simon Glass19790632017-11-13 18:55:01 -07001221 sym_values = struct.pack('<LQL', 0x24 + 0, 0x24 + 24, 0x24 + 20)
Simon Glasse6d85ff2019-05-14 15:53:47 -06001222 expected = (sym_values + U_BOOT_SPL_DATA[16:] +
1223 tools.GetBytes(0xff, 1) + U_BOOT_DATA + sym_values +
1224 U_BOOT_SPL_DATA[16:])
Simon Glass19790632017-11-13 18:55:01 -07001225 self.assertEqual(expected, data)
1226
Simon Glassdd57c132018-06-01 09:38:11 -06001227 def testPackUnitAddress(self):
1228 """Test that we support multiple binaries with the same name"""
Simon Glass741f2d62018-10-01 12:22:30 -06001229 data = self._DoReadFile('054_unit_address.dts')
Simon Glassdd57c132018-06-01 09:38:11 -06001230 self.assertEqual(U_BOOT_DATA + U_BOOT_DATA, data)
1231
Simon Glass18546952018-06-01 09:38:16 -06001232 def testSections(self):
1233 """Basic test of sections"""
Simon Glass741f2d62018-10-01 12:22:30 -06001234 data = self._DoReadFile('055_sections.dts')
Simon Glassc6c10e72019-05-17 22:00:46 -06001235 expected = (U_BOOT_DATA + tools.GetBytes(ord('!'), 12) +
1236 U_BOOT_DATA + tools.GetBytes(ord('a'), 12) +
1237 U_BOOT_DATA + tools.GetBytes(ord('&'), 4))
Simon Glass18546952018-06-01 09:38:16 -06001238 self.assertEqual(expected, data)
Simon Glass9fc60b42017-11-12 21:52:22 -07001239
Simon Glass3b0c3822018-06-01 09:38:20 -06001240 def testMap(self):
1241 """Tests outputting a map of the images"""
Simon Glass741f2d62018-10-01 12:22:30 -06001242 _, _, map_data, _ = self._DoReadFileDtb('055_sections.dts', map=True)
Simon Glass1be70d22018-07-17 13:25:49 -06001243 self.assertEqual('''ImagePos Offset Size Name
124400000000 00000000 00000028 main-section
124500000000 00000000 00000010 section@0
124600000000 00000000 00000004 u-boot
124700000010 00000010 00000010 section@1
124800000010 00000000 00000004 u-boot
124900000020 00000020 00000004 section@2
125000000020 00000000 00000004 u-boot
Simon Glass3b0c3822018-06-01 09:38:20 -06001251''', map_data)
1252
Simon Glassc8d48ef2018-06-01 09:38:21 -06001253 def testNamePrefix(self):
1254 """Tests that name prefixes are used"""
Simon Glass741f2d62018-10-01 12:22:30 -06001255 _, _, map_data, _ = self._DoReadFileDtb('056_name_prefix.dts', map=True)
Simon Glass1be70d22018-07-17 13:25:49 -06001256 self.assertEqual('''ImagePos Offset Size Name
125700000000 00000000 00000028 main-section
125800000000 00000000 00000010 section@0
125900000000 00000000 00000004 ro-u-boot
126000000010 00000010 00000010 section@1
126100000010 00000000 00000004 rw-u-boot
Simon Glassc8d48ef2018-06-01 09:38:21 -06001262''', map_data)
1263
Simon Glass736bb0a2018-07-06 10:27:17 -06001264 def testUnknownContents(self):
1265 """Test that obtaining the contents works as expected"""
1266 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001267 self._DoReadFile('057_unknown_contents.dts', True)
Simon Glass8beb11e2019-07-08 14:25:47 -06001268 self.assertIn("Image '/binman': Internal error: Could not complete "
Simon Glass736bb0a2018-07-06 10:27:17 -06001269 "processing of contents: remaining [<_testing.Entry__testing ",
1270 str(e.exception))
1271
Simon Glass5c890232018-07-06 10:27:19 -06001272 def testBadChangeSize(self):
1273 """Test that trying to change the size of an entry fails"""
Simon Glassc52c9e72019-07-08 14:25:37 -06001274 try:
1275 state.SetAllowEntryExpansion(False)
1276 with self.assertRaises(ValueError) as e:
1277 self._DoReadFile('059_change_size.dts', True)
Simon Glass79d3c582019-07-20 12:23:57 -06001278 self.assertIn("Node '/binman/_testing': Cannot update entry size from 2 to 3",
Simon Glassc52c9e72019-07-08 14:25:37 -06001279 str(e.exception))
1280 finally:
1281 state.SetAllowEntryExpansion(True)
Simon Glass5c890232018-07-06 10:27:19 -06001282
Simon Glass16b8d6b2018-07-06 10:27:42 -06001283 def testUpdateFdt(self):
Simon Glass3ab95982018-08-01 15:22:37 -06001284 """Test that we can update the device tree with offset/size info"""
Simon Glass741f2d62018-10-01 12:22:30 -06001285 _, _, _, out_dtb_fname = self._DoReadFileDtb('060_fdt_update.dts',
Simon Glass16b8d6b2018-07-06 10:27:42 -06001286 update_dtb=True)
Simon Glasscee02e62018-07-17 13:25:52 -06001287 dtb = fdt.Fdt(out_dtb_fname)
1288 dtb.Scan()
Simon Glass12bb1a92019-07-20 12:23:51 -06001289 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
Simon Glass16b8d6b2018-07-06 10:27:42 -06001290 self.assertEqual({
Simon Glassdbf6be92018-08-01 15:22:42 -06001291 'image-pos': 0,
Simon Glass8122f392018-07-17 13:25:28 -06001292 'offset': 0,
Simon Glass3ab95982018-08-01 15:22:37 -06001293 '_testing:offset': 32,
Simon Glass79d3c582019-07-20 12:23:57 -06001294 '_testing:size': 2,
Simon Glassdbf6be92018-08-01 15:22:42 -06001295 '_testing:image-pos': 32,
Simon Glass3ab95982018-08-01 15:22:37 -06001296 'section@0/u-boot:offset': 0,
Simon Glass16b8d6b2018-07-06 10:27:42 -06001297 'section@0/u-boot:size': len(U_BOOT_DATA),
Simon Glassdbf6be92018-08-01 15:22:42 -06001298 'section@0/u-boot:image-pos': 0,
Simon Glass3ab95982018-08-01 15:22:37 -06001299 'section@0:offset': 0,
Simon Glass16b8d6b2018-07-06 10:27:42 -06001300 'section@0:size': 16,
Simon Glassdbf6be92018-08-01 15:22:42 -06001301 'section@0:image-pos': 0,
Simon Glass16b8d6b2018-07-06 10:27:42 -06001302
Simon Glass3ab95982018-08-01 15:22:37 -06001303 'section@1/u-boot:offset': 0,
Simon Glass16b8d6b2018-07-06 10:27:42 -06001304 'section@1/u-boot:size': len(U_BOOT_DATA),
Simon Glassdbf6be92018-08-01 15:22:42 -06001305 'section@1/u-boot:image-pos': 16,
Simon Glass3ab95982018-08-01 15:22:37 -06001306 'section@1:offset': 16,
Simon Glass16b8d6b2018-07-06 10:27:42 -06001307 'section@1:size': 16,
Simon Glassdbf6be92018-08-01 15:22:42 -06001308 'section@1:image-pos': 16,
Simon Glass16b8d6b2018-07-06 10:27:42 -06001309 'size': 40
1310 }, props)
1311
1312 def testUpdateFdtBad(self):
1313 """Test that we detect when ProcessFdt never completes"""
1314 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001315 self._DoReadFileDtb('061_fdt_update_bad.dts', update_dtb=True)
Simon Glass16b8d6b2018-07-06 10:27:42 -06001316 self.assertIn('Could not complete processing of Fdt: remaining '
1317 '[<_testing.Entry__testing', str(e.exception))
Simon Glass5c890232018-07-06 10:27:19 -06001318
Simon Glass53af22a2018-07-17 13:25:32 -06001319 def testEntryArgs(self):
1320 """Test passing arguments to entries from the command line"""
1321 entry_args = {
1322 'test-str-arg': 'test1',
1323 'test-int-arg': '456',
1324 }
Simon Glass741f2d62018-10-01 12:22:30 -06001325 self._DoReadFileDtb('062_entry_args.dts', entry_args=entry_args)
Simon Glass53af22a2018-07-17 13:25:32 -06001326 self.assertIn('image', control.images)
1327 entry = control.images['image'].GetEntries()['_testing']
1328 self.assertEqual('test0', entry.test_str_fdt)
1329 self.assertEqual('test1', entry.test_str_arg)
1330 self.assertEqual(123, entry.test_int_fdt)
1331 self.assertEqual(456, entry.test_int_arg)
1332
1333 def testEntryArgsMissing(self):
1334 """Test missing arguments and properties"""
1335 entry_args = {
1336 'test-int-arg': '456',
1337 }
Simon Glass741f2d62018-10-01 12:22:30 -06001338 self._DoReadFileDtb('063_entry_args_missing.dts', entry_args=entry_args)
Simon Glass53af22a2018-07-17 13:25:32 -06001339 entry = control.images['image'].GetEntries()['_testing']
1340 self.assertEqual('test0', entry.test_str_fdt)
1341 self.assertEqual(None, entry.test_str_arg)
1342 self.assertEqual(None, entry.test_int_fdt)
1343 self.assertEqual(456, entry.test_int_arg)
1344
1345 def testEntryArgsRequired(self):
1346 """Test missing arguments and properties"""
1347 entry_args = {
1348 'test-int-arg': '456',
1349 }
1350 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001351 self._DoReadFileDtb('064_entry_args_required.dts')
Simon Glass53af22a2018-07-17 13:25:32 -06001352 self.assertIn("Node '/binman/_testing': Missing required "
1353 'properties/entry args: test-str-arg, test-int-fdt, test-int-arg',
1354 str(e.exception))
1355
1356 def testEntryArgsInvalidFormat(self):
1357 """Test that an invalid entry-argument format is detected"""
Simon Glass53cd5d92019-07-08 14:25:29 -06001358 args = ['build', '-d', self.TestFile('064_entry_args_required.dts'),
1359 '-ano-value']
Simon Glass53af22a2018-07-17 13:25:32 -06001360 with self.assertRaises(ValueError) as e:
1361 self._DoBinman(*args)
1362 self.assertIn("Invalid entry arguemnt 'no-value'", str(e.exception))
1363
1364 def testEntryArgsInvalidInteger(self):
1365 """Test that an invalid entry-argument integer is detected"""
1366 entry_args = {
1367 'test-int-arg': 'abc',
1368 }
1369 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001370 self._DoReadFileDtb('062_entry_args.dts', entry_args=entry_args)
Simon Glass53af22a2018-07-17 13:25:32 -06001371 self.assertIn("Node '/binman/_testing': Cannot convert entry arg "
1372 "'test-int-arg' (value 'abc') to integer",
1373 str(e.exception))
1374
1375 def testEntryArgsInvalidDatatype(self):
1376 """Test that an invalid entry-argument datatype is detected
1377
1378 This test could be written in entry_test.py except that it needs
1379 access to control.entry_args, which seems more than that module should
1380 be able to see.
1381 """
1382 entry_args = {
1383 'test-bad-datatype-arg': '12',
1384 }
1385 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001386 self._DoReadFileDtb('065_entry_args_unknown_datatype.dts',
Simon Glass53af22a2018-07-17 13:25:32 -06001387 entry_args=entry_args)
1388 self.assertIn('GetArg() internal error: Unknown data type ',
1389 str(e.exception))
1390
Simon Glassbb748372018-07-17 13:25:33 -06001391 def testText(self):
1392 """Test for a text entry type"""
1393 entry_args = {
1394 'test-id': TEXT_DATA,
1395 'test-id2': TEXT_DATA2,
1396 'test-id3': TEXT_DATA3,
1397 }
Simon Glass741f2d62018-10-01 12:22:30 -06001398 data, _, _, _ = self._DoReadFileDtb('066_text.dts',
Simon Glassbb748372018-07-17 13:25:33 -06001399 entry_args=entry_args)
Simon Glassc6c10e72019-05-17 22:00:46 -06001400 expected = (tools.ToBytes(TEXT_DATA) +
1401 tools.GetBytes(0, 8 - len(TEXT_DATA)) +
1402 tools.ToBytes(TEXT_DATA2) + tools.ToBytes(TEXT_DATA3) +
Simon Glassaa88b502019-07-08 13:18:40 -06001403 b'some text' + b'more text')
Simon Glassbb748372018-07-17 13:25:33 -06001404 self.assertEqual(expected, data)
1405
Simon Glassfd8d1f72018-07-17 13:25:36 -06001406 def testEntryDocs(self):
1407 """Test for creation of entry documentation"""
1408 with test_util.capture_sys_output() as (stdout, stderr):
1409 control.WriteEntryDocs(binman.GetEntryModules())
1410 self.assertTrue(len(stdout.getvalue()) > 0)
1411
1412 def testEntryDocsMissing(self):
1413 """Test handling of missing entry documentation"""
1414 with self.assertRaises(ValueError) as e:
1415 with test_util.capture_sys_output() as (stdout, stderr):
1416 control.WriteEntryDocs(binman.GetEntryModules(), 'u_boot')
1417 self.assertIn('Documentation is missing for modules: u_boot',
1418 str(e.exception))
1419
Simon Glass11e36cc2018-07-17 13:25:38 -06001420 def testFmap(self):
1421 """Basic test of generation of a flashrom fmap"""
Simon Glass741f2d62018-10-01 12:22:30 -06001422 data = self._DoReadFile('067_fmap.dts')
Simon Glass11e36cc2018-07-17 13:25:38 -06001423 fhdr, fentries = fmap_util.DecodeFmap(data[32:])
Simon Glassc6c10e72019-05-17 22:00:46 -06001424 expected = (U_BOOT_DATA + tools.GetBytes(ord('!'), 12) +
1425 U_BOOT_DATA + tools.GetBytes(ord('a'), 12))
Simon Glass11e36cc2018-07-17 13:25:38 -06001426 self.assertEqual(expected, data[:32])
Simon Glassc6c10e72019-05-17 22:00:46 -06001427 self.assertEqual(b'__FMAP__', fhdr.signature)
Simon Glass11e36cc2018-07-17 13:25:38 -06001428 self.assertEqual(1, fhdr.ver_major)
1429 self.assertEqual(0, fhdr.ver_minor)
1430 self.assertEqual(0, fhdr.base)
1431 self.assertEqual(16 + 16 +
1432 fmap_util.FMAP_HEADER_LEN +
1433 fmap_util.FMAP_AREA_LEN * 3, fhdr.image_size)
Simon Glassc6c10e72019-05-17 22:00:46 -06001434 self.assertEqual(b'FMAP', fhdr.name)
Simon Glass11e36cc2018-07-17 13:25:38 -06001435 self.assertEqual(3, fhdr.nareas)
1436 for fentry in fentries:
1437 self.assertEqual(0, fentry.flags)
1438
1439 self.assertEqual(0, fentries[0].offset)
1440 self.assertEqual(4, fentries[0].size)
Simon Glassc6c10e72019-05-17 22:00:46 -06001441 self.assertEqual(b'RO_U_BOOT', fentries[0].name)
Simon Glass11e36cc2018-07-17 13:25:38 -06001442
1443 self.assertEqual(16, fentries[1].offset)
1444 self.assertEqual(4, fentries[1].size)
Simon Glassc6c10e72019-05-17 22:00:46 -06001445 self.assertEqual(b'RW_U_BOOT', fentries[1].name)
Simon Glass11e36cc2018-07-17 13:25:38 -06001446
1447 self.assertEqual(32, fentries[2].offset)
1448 self.assertEqual(fmap_util.FMAP_HEADER_LEN +
1449 fmap_util.FMAP_AREA_LEN * 3, fentries[2].size)
Simon Glassc6c10e72019-05-17 22:00:46 -06001450 self.assertEqual(b'FMAP', fentries[2].name)
Simon Glass11e36cc2018-07-17 13:25:38 -06001451
Simon Glassec127af2018-07-17 13:25:39 -06001452 def testBlobNamedByArg(self):
1453 """Test we can add a blob with the filename coming from an entry arg"""
1454 entry_args = {
1455 'cros-ec-rw-path': 'ecrw.bin',
1456 }
Simon Glass741f2d62018-10-01 12:22:30 -06001457 data, _, _, _ = self._DoReadFileDtb('068_blob_named_by_arg.dts',
Simon Glassec127af2018-07-17 13:25:39 -06001458 entry_args=entry_args)
1459
Simon Glass3af8e492018-07-17 13:25:40 -06001460 def testFill(self):
1461 """Test for an fill entry type"""
Simon Glass741f2d62018-10-01 12:22:30 -06001462 data = self._DoReadFile('069_fill.dts')
Simon Glasse6d85ff2019-05-14 15:53:47 -06001463 expected = tools.GetBytes(0xff, 8) + tools.GetBytes(0, 8)
Simon Glass3af8e492018-07-17 13:25:40 -06001464 self.assertEqual(expected, data)
1465
1466 def testFillNoSize(self):
1467 """Test for an fill entry type with no size"""
1468 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001469 self._DoReadFile('070_fill_no_size.dts')
Simon Glass3af8e492018-07-17 13:25:40 -06001470 self.assertIn("'fill' entry must have a size property",
1471 str(e.exception))
1472
Simon Glass0ef87aa2018-07-17 13:25:44 -06001473 def _HandleGbbCommand(self, pipe_list):
1474 """Fake calls to the futility utility"""
1475 if pipe_list[0][0] == 'futility':
1476 fname = pipe_list[0][-1]
1477 # Append our GBB data to the file, which will happen every time the
1478 # futility command is called.
Simon Glass1d0ebf72019-05-14 15:53:42 -06001479 with open(fname, 'ab') as fd:
Simon Glass0ef87aa2018-07-17 13:25:44 -06001480 fd.write(GBB_DATA)
1481 return command.CommandResult()
1482
1483 def testGbb(self):
1484 """Test for the Chromium OS Google Binary Block"""
1485 command.test_result = self._HandleGbbCommand
1486 entry_args = {
1487 'keydir': 'devkeys',
1488 'bmpblk': 'bmpblk.bin',
1489 }
Simon Glass741f2d62018-10-01 12:22:30 -06001490 data, _, _, _ = self._DoReadFileDtb('071_gbb.dts', entry_args=entry_args)
Simon Glass0ef87aa2018-07-17 13:25:44 -06001491
1492 # Since futility
Simon Glasse6d85ff2019-05-14 15:53:47 -06001493 expected = (GBB_DATA + GBB_DATA + tools.GetBytes(0, 8) +
1494 tools.GetBytes(0, 0x2180 - 16))
Simon Glass0ef87aa2018-07-17 13:25:44 -06001495 self.assertEqual(expected, data)
1496
1497 def testGbbTooSmall(self):
1498 """Test for the Chromium OS Google Binary Block being large enough"""
1499 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001500 self._DoReadFileDtb('072_gbb_too_small.dts')
Simon Glass0ef87aa2018-07-17 13:25:44 -06001501 self.assertIn("Node '/binman/gbb': GBB is too small",
1502 str(e.exception))
1503
1504 def testGbbNoSize(self):
1505 """Test for the Chromium OS Google Binary Block having a size"""
1506 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001507 self._DoReadFileDtb('073_gbb_no_size.dts')
Simon Glass0ef87aa2018-07-17 13:25:44 -06001508 self.assertIn("Node '/binman/gbb': GBB must have a fixed size",
1509 str(e.exception))
1510
Simon Glass24d0d3c2018-07-17 13:25:47 -06001511 def _HandleVblockCommand(self, pipe_list):
1512 """Fake calls to the futility utility"""
1513 if pipe_list[0][0] == 'futility':
1514 fname = pipe_list[0][3]
Simon Glassa326b492018-09-14 04:57:11 -06001515 with open(fname, 'wb') as fd:
Simon Glass24d0d3c2018-07-17 13:25:47 -06001516 fd.write(VBLOCK_DATA)
1517 return command.CommandResult()
1518
1519 def testVblock(self):
1520 """Test for the Chromium OS Verified Boot Block"""
1521 command.test_result = self._HandleVblockCommand
1522 entry_args = {
1523 'keydir': 'devkeys',
1524 }
Simon Glass741f2d62018-10-01 12:22:30 -06001525 data, _, _, _ = self._DoReadFileDtb('074_vblock.dts',
Simon Glass24d0d3c2018-07-17 13:25:47 -06001526 entry_args=entry_args)
1527 expected = U_BOOT_DATA + VBLOCK_DATA + U_BOOT_DTB_DATA
1528 self.assertEqual(expected, data)
1529
1530 def testVblockNoContent(self):
1531 """Test we detect a vblock which has no content to sign"""
1532 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001533 self._DoReadFile('075_vblock_no_content.dts')
Simon Glass24d0d3c2018-07-17 13:25:47 -06001534 self.assertIn("Node '/binman/vblock': Vblock must have a 'content' "
1535 'property', str(e.exception))
1536
1537 def testVblockBadPhandle(self):
1538 """Test that we detect a vblock with an invalid phandle in contents"""
1539 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001540 self._DoReadFile('076_vblock_bad_phandle.dts')
Simon Glass24d0d3c2018-07-17 13:25:47 -06001541 self.assertIn("Node '/binman/vblock': Cannot find node for phandle "
1542 '1000', str(e.exception))
1543
1544 def testVblockBadEntry(self):
1545 """Test that we detect an entry that points to a non-entry"""
1546 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001547 self._DoReadFile('077_vblock_bad_entry.dts')
Simon Glass24d0d3c2018-07-17 13:25:47 -06001548 self.assertIn("Node '/binman/vblock': Cannot find entry for node "
1549 "'other'", str(e.exception))
1550
Simon Glassb8ef5b62018-07-17 13:25:48 -06001551 def testTpl(self):
1552 """Test that an image with TPL and ots device tree can be created"""
1553 # ELF file with a '__bss_size' symbol
Simon Glass1d0ebf72019-05-14 15:53:42 -06001554 with open(self.TestFile('bss_data'), 'rb') as fd:
Simon Glassb8ef5b62018-07-17 13:25:48 -06001555 TestFunctional._MakeInputFile('tpl/u-boot-tpl', fd.read())
Simon Glass741f2d62018-10-01 12:22:30 -06001556 data = self._DoReadFile('078_u_boot_tpl.dts')
Simon Glassb8ef5b62018-07-17 13:25:48 -06001557 self.assertEqual(U_BOOT_TPL_DATA + U_BOOT_TPL_DTB_DATA, data)
1558
Simon Glass15a587c2018-07-17 13:25:51 -06001559 def testUsesPos(self):
1560 """Test that the 'pos' property cannot be used anymore"""
1561 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001562 data = self._DoReadFile('079_uses_pos.dts')
Simon Glass15a587c2018-07-17 13:25:51 -06001563 self.assertIn("Node '/binman/u-boot': Please use 'offset' instead of "
1564 "'pos'", str(e.exception))
1565
Simon Glassd178eab2018-09-14 04:57:08 -06001566 def testFillZero(self):
1567 """Test for an fill entry type with a size of 0"""
Simon Glass741f2d62018-10-01 12:22:30 -06001568 data = self._DoReadFile('080_fill_empty.dts')
Simon Glasse6d85ff2019-05-14 15:53:47 -06001569 self.assertEqual(tools.GetBytes(0, 16), data)
Simon Glassd178eab2018-09-14 04:57:08 -06001570
Simon Glass0b489362018-09-14 04:57:09 -06001571 def testTextMissing(self):
1572 """Test for a text entry type where there is no text"""
1573 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001574 self._DoReadFileDtb('066_text.dts',)
Simon Glass0b489362018-09-14 04:57:09 -06001575 self.assertIn("Node '/binman/text': No value provided for text label "
1576 "'test-id'", str(e.exception))
1577
Simon Glass35b384c2018-09-14 04:57:10 -06001578 def testPackStart16Tpl(self):
1579 """Test that an image with an x86 start16 TPL region can be created"""
Simon Glass741f2d62018-10-01 12:22:30 -06001580 data = self._DoReadFile('081_x86-start16-tpl.dts')
Simon Glass35b384c2018-09-14 04:57:10 -06001581 self.assertEqual(X86_START16_TPL_DATA, data[:len(X86_START16_TPL_DATA)])
1582
Simon Glass0bfa7b02018-09-14 04:57:12 -06001583 def testSelectImage(self):
1584 """Test that we can select which images to build"""
Simon Glasseb833d82019-04-25 21:58:34 -06001585 expected = 'Skipping images: image1'
Simon Glass0bfa7b02018-09-14 04:57:12 -06001586
Simon Glasseb833d82019-04-25 21:58:34 -06001587 # We should only get the expected message in verbose mode
Simon Glassee0c9a72019-07-08 13:18:48 -06001588 for verbosity in (0, 2):
Simon Glasseb833d82019-04-25 21:58:34 -06001589 with test_util.capture_sys_output() as (stdout, stderr):
1590 retcode = self._DoTestFile('006_dual_image.dts',
1591 verbosity=verbosity,
1592 images=['image2'])
1593 self.assertEqual(0, retcode)
1594 if verbosity:
1595 self.assertIn(expected, stdout.getvalue())
1596 else:
1597 self.assertNotIn(expected, stdout.getvalue())
1598
1599 self.assertFalse(os.path.exists(tools.GetOutputFilename('image1.bin')))
1600 self.assertTrue(os.path.exists(tools.GetOutputFilename('image2.bin')))
Simon Glassf86a7362019-07-20 12:24:10 -06001601 self._CleanupOutputDir()
Simon Glass0bfa7b02018-09-14 04:57:12 -06001602
Simon Glass6ed45ba2018-09-14 04:57:24 -06001603 def testUpdateFdtAll(self):
1604 """Test that all device trees are updated with offset/size info"""
Simon Glass3c081312019-07-08 14:25:26 -06001605 data = self._DoReadFileRealDtb('082_fdt_update_all.dts')
Simon Glass6ed45ba2018-09-14 04:57:24 -06001606
1607 base_expected = {
1608 'section:image-pos': 0,
1609 'u-boot-tpl-dtb:size': 513,
1610 'u-boot-spl-dtb:size': 513,
1611 'u-boot-spl-dtb:offset': 493,
1612 'image-pos': 0,
1613 'section/u-boot-dtb:image-pos': 0,
1614 'u-boot-spl-dtb:image-pos': 493,
1615 'section/u-boot-dtb:size': 493,
1616 'u-boot-tpl-dtb:image-pos': 1006,
1617 'section/u-boot-dtb:offset': 0,
1618 'section:size': 493,
1619 'offset': 0,
1620 'section:offset': 0,
1621 'u-boot-tpl-dtb:offset': 1006,
1622 'size': 1519
1623 }
1624
1625 # We expect three device-tree files in the output, one after the other.
1626 # Read them in sequence. We look for an 'spl' property in the SPL tree,
1627 # and 'tpl' in the TPL tree, to make sure they are distinct from the
1628 # main U-Boot tree. All three should have the same postions and offset.
1629 start = 0
1630 for item in ['', 'spl', 'tpl']:
1631 dtb = fdt.Fdt.FromData(data[start:])
1632 dtb.Scan()
Simon Glass12bb1a92019-07-20 12:23:51 -06001633 props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS +
1634 ['spl', 'tpl'])
Simon Glass6ed45ba2018-09-14 04:57:24 -06001635 expected = dict(base_expected)
1636 if item:
1637 expected[item] = 0
1638 self.assertEqual(expected, props)
1639 start += dtb._fdt_obj.totalsize()
1640
1641 def testUpdateFdtOutput(self):
1642 """Test that output DTB files are updated"""
1643 try:
Simon Glass741f2d62018-10-01 12:22:30 -06001644 data, dtb_data, _, _ = self._DoReadFileDtb('082_fdt_update_all.dts',
Simon Glass6ed45ba2018-09-14 04:57:24 -06001645 use_real_dtb=True, update_dtb=True, reset_dtbs=False)
1646
1647 # Unfortunately, compiling a source file always results in a file
1648 # called source.dtb (see fdt_util.EnsureCompiled()). The test
Simon Glass741f2d62018-10-01 12:22:30 -06001649 # source file (e.g. test/075_fdt_update_all.dts) thus does not enter
Simon Glass6ed45ba2018-09-14 04:57:24 -06001650 # binman as a file called u-boot.dtb. To fix this, copy the file
1651 # over to the expected place.
1652 #tools.WriteFile(os.path.join(self._indir, 'u-boot.dtb'),
1653 #tools.ReadFile(tools.GetOutputFilename('source.dtb')))
1654 start = 0
1655 for fname in ['u-boot.dtb.out', 'spl/u-boot-spl.dtb.out',
1656 'tpl/u-boot-tpl.dtb.out']:
1657 dtb = fdt.Fdt.FromData(data[start:])
1658 size = dtb._fdt_obj.totalsize()
1659 pathname = tools.GetOutputFilename(os.path.split(fname)[1])
1660 outdata = tools.ReadFile(pathname)
1661 name = os.path.split(fname)[0]
1662
1663 if name:
1664 orig_indata = self._GetDtbContentsForSplTpl(dtb_data, name)
1665 else:
1666 orig_indata = dtb_data
1667 self.assertNotEqual(outdata, orig_indata,
1668 "Expected output file '%s' be updated" % pathname)
1669 self.assertEqual(outdata, data[start:start + size],
1670 "Expected output file '%s' to match output image" %
1671 pathname)
1672 start += size
1673 finally:
1674 self._ResetDtbs()
1675
Simon Glass83d73c22018-09-14 04:57:26 -06001676 def _decompress(self, data):
Simon Glassff5c7e32019-07-08 13:18:42 -06001677 return tools.Decompress(data, 'lz4')
Simon Glass83d73c22018-09-14 04:57:26 -06001678
1679 def testCompress(self):
1680 """Test compression of blobs"""
Simon Glassac62fba2019-07-08 13:18:53 -06001681 self._CheckLz4()
Simon Glass741f2d62018-10-01 12:22:30 -06001682 data, _, _, out_dtb_fname = self._DoReadFileDtb('083_compress.dts',
Simon Glass83d73c22018-09-14 04:57:26 -06001683 use_real_dtb=True, update_dtb=True)
1684 dtb = fdt.Fdt(out_dtb_fname)
1685 dtb.Scan()
1686 props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
1687 orig = self._decompress(data)
1688 self.assertEquals(COMPRESS_DATA, orig)
1689 expected = {
1690 'blob:uncomp-size': len(COMPRESS_DATA),
1691 'blob:size': len(data),
1692 'size': len(data),
1693 }
1694 self.assertEqual(expected, props)
1695
Simon Glass0a98b282018-09-14 04:57:28 -06001696 def testFiles(self):
1697 """Test bringing in multiple files"""
Simon Glass741f2d62018-10-01 12:22:30 -06001698 data = self._DoReadFile('084_files.dts')
Simon Glass0a98b282018-09-14 04:57:28 -06001699 self.assertEqual(FILES_DATA, data)
1700
1701 def testFilesCompress(self):
1702 """Test bringing in multiple files and compressing them"""
Simon Glassac62fba2019-07-08 13:18:53 -06001703 self._CheckLz4()
Simon Glass741f2d62018-10-01 12:22:30 -06001704 data = self._DoReadFile('085_files_compress.dts')
Simon Glass0a98b282018-09-14 04:57:28 -06001705
1706 image = control.images['image']
1707 entries = image.GetEntries()
1708 files = entries['files']
Simon Glass8beb11e2019-07-08 14:25:47 -06001709 entries = files._entries
Simon Glass0a98b282018-09-14 04:57:28 -06001710
Simon Glassc6c10e72019-05-17 22:00:46 -06001711 orig = b''
Simon Glass0a98b282018-09-14 04:57:28 -06001712 for i in range(1, 3):
1713 key = '%d.dat' % i
1714 start = entries[key].image_pos
1715 len = entries[key].size
1716 chunk = data[start:start + len]
1717 orig += self._decompress(chunk)
1718
1719 self.assertEqual(FILES_DATA, orig)
1720
1721 def testFilesMissing(self):
1722 """Test missing files"""
1723 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001724 data = self._DoReadFile('086_files_none.dts')
Simon Glass0a98b282018-09-14 04:57:28 -06001725 self.assertIn("Node '/binman/files': Pattern \'files/*.none\' matched "
1726 'no files', str(e.exception))
1727
1728 def testFilesNoPattern(self):
1729 """Test missing files"""
1730 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001731 data = self._DoReadFile('087_files_no_pattern.dts')
Simon Glass0a98b282018-09-14 04:57:28 -06001732 self.assertIn("Node '/binman/files': Missing 'pattern' property",
1733 str(e.exception))
1734
Simon Glassba64a0b2018-09-14 04:57:29 -06001735 def testExpandSize(self):
1736 """Test an expanding entry"""
Simon Glass741f2d62018-10-01 12:22:30 -06001737 data, _, map_data, _ = self._DoReadFileDtb('088_expand_size.dts',
Simon Glassba64a0b2018-09-14 04:57:29 -06001738 map=True)
Simon Glassc6c10e72019-05-17 22:00:46 -06001739 expect = (tools.GetBytes(ord('a'), 8) + U_BOOT_DATA +
1740 MRC_DATA + tools.GetBytes(ord('b'), 1) + U_BOOT_DATA +
1741 tools.GetBytes(ord('c'), 8) + U_BOOT_DATA +
1742 tools.GetBytes(ord('d'), 8))
Simon Glassba64a0b2018-09-14 04:57:29 -06001743 self.assertEqual(expect, data)
1744 self.assertEqual('''ImagePos Offset Size Name
174500000000 00000000 00000028 main-section
174600000000 00000000 00000008 fill
174700000008 00000008 00000004 u-boot
17480000000c 0000000c 00000004 section
17490000000c 00000000 00000003 intel-mrc
175000000010 00000010 00000004 u-boot2
175100000014 00000014 0000000c section2
175200000014 00000000 00000008 fill
17530000001c 00000008 00000004 u-boot
175400000020 00000020 00000008 fill2
1755''', map_data)
1756
1757 def testExpandSizeBad(self):
1758 """Test an expanding entry which fails to provide contents"""
Simon Glass163ed6c2018-09-14 04:57:36 -06001759 with test_util.capture_sys_output() as (stdout, stderr):
1760 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001761 self._DoReadFileDtb('089_expand_size_bad.dts', map=True)
Simon Glassba64a0b2018-09-14 04:57:29 -06001762 self.assertIn("Node '/binman/_testing': Cannot obtain contents when "
1763 'expanding entry', str(e.exception))
1764
Simon Glasse0e5df92018-09-14 04:57:31 -06001765 def testHash(self):
1766 """Test hashing of the contents of an entry"""
Simon Glass741f2d62018-10-01 12:22:30 -06001767 _, _, _, out_dtb_fname = self._DoReadFileDtb('090_hash.dts',
Simon Glasse0e5df92018-09-14 04:57:31 -06001768 use_real_dtb=True, update_dtb=True)
1769 dtb = fdt.Fdt(out_dtb_fname)
1770 dtb.Scan()
1771 hash_node = dtb.GetNode('/binman/u-boot/hash').props['value']
1772 m = hashlib.sha256()
1773 m.update(U_BOOT_DATA)
Simon Glassc6c10e72019-05-17 22:00:46 -06001774 self.assertEqual(m.digest(), b''.join(hash_node.value))
Simon Glasse0e5df92018-09-14 04:57:31 -06001775
1776 def testHashNoAlgo(self):
1777 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001778 self._DoReadFileDtb('091_hash_no_algo.dts', update_dtb=True)
Simon Glasse0e5df92018-09-14 04:57:31 -06001779 self.assertIn("Node \'/binman/u-boot\': Missing \'algo\' property for "
1780 'hash node', str(e.exception))
1781
1782 def testHashBadAlgo(self):
1783 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001784 self._DoReadFileDtb('092_hash_bad_algo.dts', update_dtb=True)
Simon Glasse0e5df92018-09-14 04:57:31 -06001785 self.assertIn("Node '/binman/u-boot': Unknown hash algorithm",
1786 str(e.exception))
1787
1788 def testHashSection(self):
1789 """Test hashing of the contents of an entry"""
Simon Glass741f2d62018-10-01 12:22:30 -06001790 _, _, _, out_dtb_fname = self._DoReadFileDtb('099_hash_section.dts',
Simon Glasse0e5df92018-09-14 04:57:31 -06001791 use_real_dtb=True, update_dtb=True)
1792 dtb = fdt.Fdt(out_dtb_fname)
1793 dtb.Scan()
1794 hash_node = dtb.GetNode('/binman/section/hash').props['value']
1795 m = hashlib.sha256()
1796 m.update(U_BOOT_DATA)
Simon Glassc6c10e72019-05-17 22:00:46 -06001797 m.update(tools.GetBytes(ord('a'), 16))
1798 self.assertEqual(m.digest(), b''.join(hash_node.value))
Simon Glasse0e5df92018-09-14 04:57:31 -06001799
Simon Glassf0253632018-09-14 04:57:32 -06001800 def testPackUBootTplMicrocode(self):
1801 """Test that x86 microcode can be handled correctly in TPL
1802
1803 We expect to see the following in the image, in order:
1804 u-boot-tpl-nodtb.bin with a microcode pointer inserted at the correct
1805 place
1806 u-boot-tpl.dtb with the microcode removed
1807 the microcode
1808 """
Simon Glass1d0ebf72019-05-14 15:53:42 -06001809 with open(self.TestFile('u_boot_ucode_ptr'), 'rb') as fd:
Simon Glassf0253632018-09-14 04:57:32 -06001810 TestFunctional._MakeInputFile('tpl/u-boot-tpl', fd.read())
Simon Glass741f2d62018-10-01 12:22:30 -06001811 first, pos_and_size = self._RunMicrocodeTest('093_x86_tpl_ucode.dts',
Simon Glassf0253632018-09-14 04:57:32 -06001812 U_BOOT_TPL_NODTB_DATA)
Simon Glassc6c10e72019-05-17 22:00:46 -06001813 self.assertEqual(b'tplnodtb with microc' + pos_and_size +
1814 b'ter somewhere in here', first)
Simon Glassf0253632018-09-14 04:57:32 -06001815
Simon Glassf8f8df62018-09-14 04:57:34 -06001816 def testFmapX86(self):
1817 """Basic test of generation of a flashrom fmap"""
Simon Glass741f2d62018-10-01 12:22:30 -06001818 data = self._DoReadFile('094_fmap_x86.dts')
Simon Glassf8f8df62018-09-14 04:57:34 -06001819 fhdr, fentries = fmap_util.DecodeFmap(data[32:])
Simon Glassc6c10e72019-05-17 22:00:46 -06001820 expected = U_BOOT_DATA + MRC_DATA + tools.GetBytes(ord('a'), 32 - 7)
Simon Glassf8f8df62018-09-14 04:57:34 -06001821 self.assertEqual(expected, data[:32])
1822 fhdr, fentries = fmap_util.DecodeFmap(data[32:])
1823
1824 self.assertEqual(0x100, fhdr.image_size)
1825
1826 self.assertEqual(0, fentries[0].offset)
1827 self.assertEqual(4, fentries[0].size)
Simon Glassc6c10e72019-05-17 22:00:46 -06001828 self.assertEqual(b'U_BOOT', fentries[0].name)
Simon Glassf8f8df62018-09-14 04:57:34 -06001829
1830 self.assertEqual(4, fentries[1].offset)
1831 self.assertEqual(3, fentries[1].size)
Simon Glassc6c10e72019-05-17 22:00:46 -06001832 self.assertEqual(b'INTEL_MRC', fentries[1].name)
Simon Glassf8f8df62018-09-14 04:57:34 -06001833
1834 self.assertEqual(32, fentries[2].offset)
1835 self.assertEqual(fmap_util.FMAP_HEADER_LEN +
1836 fmap_util.FMAP_AREA_LEN * 3, fentries[2].size)
Simon Glassc6c10e72019-05-17 22:00:46 -06001837 self.assertEqual(b'FMAP', fentries[2].name)
Simon Glassf8f8df62018-09-14 04:57:34 -06001838
1839 def testFmapX86Section(self):
1840 """Basic test of generation of a flashrom fmap"""
Simon Glass741f2d62018-10-01 12:22:30 -06001841 data = self._DoReadFile('095_fmap_x86_section.dts')
Simon Glassc6c10e72019-05-17 22:00:46 -06001842 expected = U_BOOT_DATA + MRC_DATA + tools.GetBytes(ord('b'), 32 - 7)
Simon Glassf8f8df62018-09-14 04:57:34 -06001843 self.assertEqual(expected, data[:32])
1844 fhdr, fentries = fmap_util.DecodeFmap(data[36:])
1845
1846 self.assertEqual(0x100, fhdr.image_size)
1847
1848 self.assertEqual(0, fentries[0].offset)
1849 self.assertEqual(4, fentries[0].size)
Simon Glassc6c10e72019-05-17 22:00:46 -06001850 self.assertEqual(b'U_BOOT', fentries[0].name)
Simon Glassf8f8df62018-09-14 04:57:34 -06001851
1852 self.assertEqual(4, fentries[1].offset)
1853 self.assertEqual(3, fentries[1].size)
Simon Glassc6c10e72019-05-17 22:00:46 -06001854 self.assertEqual(b'INTEL_MRC', fentries[1].name)
Simon Glassf8f8df62018-09-14 04:57:34 -06001855
1856 self.assertEqual(36, fentries[2].offset)
1857 self.assertEqual(fmap_util.FMAP_HEADER_LEN +
1858 fmap_util.FMAP_AREA_LEN * 3, fentries[2].size)
Simon Glassc6c10e72019-05-17 22:00:46 -06001859 self.assertEqual(b'FMAP', fentries[2].name)
Simon Glassf8f8df62018-09-14 04:57:34 -06001860
Simon Glassfe1ae3e2018-09-14 04:57:35 -06001861 def testElf(self):
1862 """Basic test of ELF entries"""
Simon Glass11ae93e2018-10-01 21:12:47 -06001863 self._SetupSplElf()
Simon Glass1d0ebf72019-05-14 15:53:42 -06001864 with open(self.TestFile('bss_data'), 'rb') as fd:
Simon Glass4c650252019-07-08 13:18:46 -06001865 TestFunctional._MakeInputFile('tpl/u-boot-tpl', fd.read())
1866 with open(self.TestFile('bss_data'), 'rb') as fd:
Simon Glassfe1ae3e2018-09-14 04:57:35 -06001867 TestFunctional._MakeInputFile('-boot', fd.read())
Simon Glass741f2d62018-10-01 12:22:30 -06001868 data = self._DoReadFile('096_elf.dts')
Simon Glassfe1ae3e2018-09-14 04:57:35 -06001869
Simon Glass093d1682019-07-08 13:18:25 -06001870 def testElfStrip(self):
Simon Glassfe1ae3e2018-09-14 04:57:35 -06001871 """Basic test of ELF entries"""
Simon Glass11ae93e2018-10-01 21:12:47 -06001872 self._SetupSplElf()
Simon Glass1d0ebf72019-05-14 15:53:42 -06001873 with open(self.TestFile('bss_data'), 'rb') as fd:
Simon Glassfe1ae3e2018-09-14 04:57:35 -06001874 TestFunctional._MakeInputFile('-boot', fd.read())
Simon Glass741f2d62018-10-01 12:22:30 -06001875 data = self._DoReadFile('097_elf_strip.dts')
Simon Glassfe1ae3e2018-09-14 04:57:35 -06001876
Simon Glass163ed6c2018-09-14 04:57:36 -06001877 def testPackOverlapMap(self):
1878 """Test that overlapping regions are detected"""
1879 with test_util.capture_sys_output() as (stdout, stderr):
1880 with self.assertRaises(ValueError) as e:
Simon Glass741f2d62018-10-01 12:22:30 -06001881 self._DoTestFile('014_pack_overlap.dts', map=True)
Simon Glass163ed6c2018-09-14 04:57:36 -06001882 map_fname = tools.GetOutputFilename('image.map')
1883 self.assertEqual("Wrote map file '%s' to show errors\n" % map_fname,
1884 stdout.getvalue())
1885
1886 # We should not get an inmage, but there should be a map file
1887 self.assertFalse(os.path.exists(tools.GetOutputFilename('image.bin')))
1888 self.assertTrue(os.path.exists(map_fname))
Simon Glasseb546ac2019-05-17 22:00:51 -06001889 map_data = tools.ReadFile(map_fname, binary=False)
Simon Glass163ed6c2018-09-14 04:57:36 -06001890 self.assertEqual('''ImagePos Offset Size Name
1891<none> 00000000 00000007 main-section
1892<none> 00000000 00000004 u-boot
1893<none> 00000003 00000004 u-boot-align
1894''', map_data)
1895
Simon Glass093d1682019-07-08 13:18:25 -06001896 def testPackRefCode(self):
Simon Glass3ae192c2018-10-01 12:22:31 -06001897 """Test that an image with an Intel Reference code binary works"""
1898 data = self._DoReadFile('100_intel_refcode.dts')
1899 self.assertEqual(REFCODE_DATA, data[:len(REFCODE_DATA)])
1900
Simon Glass9481c802019-04-25 21:58:39 -06001901 def testSectionOffset(self):
1902 """Tests use of a section with an offset"""
1903 data, _, map_data, _ = self._DoReadFileDtb('101_sections_offset.dts',
1904 map=True)
1905 self.assertEqual('''ImagePos Offset Size Name
190600000000 00000000 00000038 main-section
190700000004 00000004 00000010 section@0
190800000004 00000000 00000004 u-boot
190900000018 00000018 00000010 section@1
191000000018 00000000 00000004 u-boot
19110000002c 0000002c 00000004 section@2
19120000002c 00000000 00000004 u-boot
1913''', map_data)
1914 self.assertEqual(data,
Simon Glasse6d85ff2019-05-14 15:53:47 -06001915 tools.GetBytes(0x26, 4) + U_BOOT_DATA +
1916 tools.GetBytes(0x21, 12) +
1917 tools.GetBytes(0x26, 4) + U_BOOT_DATA +
1918 tools.GetBytes(0x61, 12) +
1919 tools.GetBytes(0x26, 4) + U_BOOT_DATA +
1920 tools.GetBytes(0x26, 8))
Simon Glass9481c802019-04-25 21:58:39 -06001921
Simon Glassac62fba2019-07-08 13:18:53 -06001922 def testCbfsRaw(self):
1923 """Test base handling of a Coreboot Filesystem (CBFS)
1924
1925 The exact contents of the CBFS is verified by similar tests in
1926 cbfs_util_test.py. The tests here merely check that the files added to
1927 the CBFS can be found in the final image.
1928 """
1929 data = self._DoReadFile('102_cbfs_raw.dts')
1930 size = 0xb0
1931
1932 cbfs = cbfs_util.CbfsReader(data)
1933 self.assertEqual(size, cbfs.rom_size)
1934
1935 self.assertIn('u-boot-dtb', cbfs.files)
1936 cfile = cbfs.files['u-boot-dtb']
1937 self.assertEqual(U_BOOT_DTB_DATA, cfile.data)
1938
1939 def testCbfsArch(self):
1940 """Test on non-x86 architecture"""
1941 data = self._DoReadFile('103_cbfs_raw_ppc.dts')
1942 size = 0x100
1943
1944 cbfs = cbfs_util.CbfsReader(data)
1945 self.assertEqual(size, cbfs.rom_size)
1946
1947 self.assertIn('u-boot-dtb', cbfs.files)
1948 cfile = cbfs.files['u-boot-dtb']
1949 self.assertEqual(U_BOOT_DTB_DATA, cfile.data)
1950
1951 def testCbfsStage(self):
1952 """Tests handling of a Coreboot Filesystem (CBFS)"""
1953 if not elf.ELF_TOOLS:
1954 self.skipTest('Python elftools not available')
1955 elf_fname = os.path.join(self._indir, 'cbfs-stage.elf')
1956 elf.MakeElf(elf_fname, U_BOOT_DATA, U_BOOT_DTB_DATA)
1957 size = 0xb0
1958
1959 data = self._DoReadFile('104_cbfs_stage.dts')
1960 cbfs = cbfs_util.CbfsReader(data)
1961 self.assertEqual(size, cbfs.rom_size)
1962
1963 self.assertIn('u-boot', cbfs.files)
1964 cfile = cbfs.files['u-boot']
1965 self.assertEqual(U_BOOT_DATA + U_BOOT_DTB_DATA, cfile.data)
1966
1967 def testCbfsRawCompress(self):
1968 """Test handling of compressing raw files"""
1969 self._CheckLz4()
1970 data = self._DoReadFile('105_cbfs_raw_compress.dts')
1971 size = 0x140
1972
1973 cbfs = cbfs_util.CbfsReader(data)
1974 self.assertIn('u-boot', cbfs.files)
1975 cfile = cbfs.files['u-boot']
1976 self.assertEqual(COMPRESS_DATA, cfile.data)
1977
1978 def testCbfsBadArch(self):
1979 """Test handling of a bad architecture"""
1980 with self.assertRaises(ValueError) as e:
1981 self._DoReadFile('106_cbfs_bad_arch.dts')
1982 self.assertIn("Invalid architecture 'bad-arch'", str(e.exception))
1983
1984 def testCbfsNoSize(self):
1985 """Test handling of a missing size property"""
1986 with self.assertRaises(ValueError) as e:
1987 self._DoReadFile('107_cbfs_no_size.dts')
1988 self.assertIn('entry must have a size property', str(e.exception))
1989
1990 def testCbfsNoCOntents(self):
1991 """Test handling of a CBFS entry which does not provide contentsy"""
1992 with self.assertRaises(ValueError) as e:
1993 self._DoReadFile('108_cbfs_no_contents.dts')
1994 self.assertIn('Could not complete processing of contents',
1995 str(e.exception))
1996
1997 def testCbfsBadCompress(self):
1998 """Test handling of a bad architecture"""
1999 with self.assertRaises(ValueError) as e:
2000 self._DoReadFile('109_cbfs_bad_compress.dts')
2001 self.assertIn("Invalid compression in 'u-boot': 'invalid-algo'",
2002 str(e.exception))
2003
2004 def testCbfsNamedEntries(self):
2005 """Test handling of named entries"""
2006 data = self._DoReadFile('110_cbfs_name.dts')
2007
2008 cbfs = cbfs_util.CbfsReader(data)
2009 self.assertIn('FRED', cbfs.files)
2010 cfile1 = cbfs.files['FRED']
2011 self.assertEqual(U_BOOT_DATA, cfile1.data)
2012
2013 self.assertIn('hello', cbfs.files)
2014 cfile2 = cbfs.files['hello']
2015 self.assertEqual(U_BOOT_DTB_DATA, cfile2.data)
2016
Simon Glassc5ac1382019-07-08 13:18:54 -06002017 def _SetupIfwi(self, fname):
2018 """Set up to run an IFWI test
2019
2020 Args:
2021 fname: Filename of input file to provide (fitimage.bin or ifwi.bin)
2022 """
2023 self._SetupSplElf()
2024
2025 # Intel Integrated Firmware Image (IFWI) file
2026 with gzip.open(self.TestFile('%s.gz' % fname), 'rb') as fd:
2027 data = fd.read()
2028 TestFunctional._MakeInputFile(fname,data)
2029
2030 def _CheckIfwi(self, data):
2031 """Check that an image with an IFWI contains the correct output
2032
2033 Args:
2034 data: Conents of output file
2035 """
2036 expected_desc = tools.ReadFile(self.TestFile('descriptor.bin'))
2037 if data[:0x1000] != expected_desc:
2038 self.fail('Expected descriptor binary at start of image')
2039
2040 # We expect to find the TPL wil in subpart IBBP entry IBBL
2041 image_fname = tools.GetOutputFilename('image.bin')
2042 tpl_fname = tools.GetOutputFilename('tpl.out')
2043 tools.RunIfwiTool(image_fname, tools.CMD_EXTRACT, fname=tpl_fname,
2044 subpart='IBBP', entry_name='IBBL')
2045
2046 tpl_data = tools.ReadFile(tpl_fname)
2047 self.assertEqual(tpl_data[:len(U_BOOT_TPL_DATA)], U_BOOT_TPL_DATA)
2048
2049 def testPackX86RomIfwi(self):
2050 """Test that an x86 ROM with Integrated Firmware Image can be created"""
2051 self._SetupIfwi('fitimage.bin')
2052 data = self._DoReadFile('111_x86-rom-ifwi.dts')
2053 self._CheckIfwi(data)
2054
2055 def testPackX86RomIfwiNoDesc(self):
2056 """Test that an x86 ROM with IFWI can be created from an ifwi.bin file"""
2057 self._SetupIfwi('ifwi.bin')
2058 data = self._DoReadFile('112_x86-rom-ifwi-nodesc.dts')
2059 self._CheckIfwi(data)
2060
2061 def testPackX86RomIfwiNoData(self):
2062 """Test that an x86 ROM with IFWI handles missing data"""
2063 self._SetupIfwi('ifwi.bin')
2064 with self.assertRaises(ValueError) as e:
2065 data = self._DoReadFile('113_x86-rom-ifwi-nodata.dts')
2066 self.assertIn('Could not complete processing of contents',
2067 str(e.exception))
Simon Glass53af22a2018-07-17 13:25:32 -06002068
Simon Glasse073d4e2019-07-08 13:18:56 -06002069 def testCbfsOffset(self):
2070 """Test a CBFS with files at particular offsets
2071
2072 Like all CFBS tests, this is just checking the logic that calls
2073 cbfs_util. See cbfs_util_test for fully tests (e.g. test_cbfs_offset()).
2074 """
2075 data = self._DoReadFile('114_cbfs_offset.dts')
2076 size = 0x200
2077
2078 cbfs = cbfs_util.CbfsReader(data)
2079 self.assertEqual(size, cbfs.rom_size)
2080
2081 self.assertIn('u-boot', cbfs.files)
2082 cfile = cbfs.files['u-boot']
2083 self.assertEqual(U_BOOT_DATA, cfile.data)
2084 self.assertEqual(0x40, cfile.cbfs_offset)
2085
2086 self.assertIn('u-boot-dtb', cbfs.files)
2087 cfile2 = cbfs.files['u-boot-dtb']
2088 self.assertEqual(U_BOOT_DTB_DATA, cfile2.data)
2089 self.assertEqual(0x140, cfile2.cbfs_offset)
2090
Simon Glass086cec92019-07-08 14:25:27 -06002091 def testFdtmap(self):
2092 """Test an FDT map can be inserted in the image"""
2093 data = self.data = self._DoReadFileRealDtb('115_fdtmap.dts')
2094 fdtmap_data = data[len(U_BOOT_DATA):]
2095 magic = fdtmap_data[:8]
2096 self.assertEqual('_FDTMAP_', magic)
2097 self.assertEqual(tools.GetBytes(0, 8), fdtmap_data[8:16])
2098
2099 fdt_data = fdtmap_data[16:]
2100 dtb = fdt.Fdt.FromData(fdt_data)
2101 dtb.Scan()
Simon Glass6ccbfcd2019-07-20 12:23:47 -06002102 props = self._GetPropTree(dtb, BASE_DTB_PROPS, prefix='/')
Simon Glass086cec92019-07-08 14:25:27 -06002103 self.assertEqual({
2104 'image-pos': 0,
2105 'offset': 0,
2106 'u-boot:offset': 0,
2107 'u-boot:size': len(U_BOOT_DATA),
2108 'u-boot:image-pos': 0,
2109 'fdtmap:image-pos': 4,
2110 'fdtmap:offset': 4,
2111 'fdtmap:size': len(fdtmap_data),
2112 'size': len(data),
2113 }, props)
2114
2115 def testFdtmapNoMatch(self):
2116 """Check handling of an FDT map when the section cannot be found"""
2117 self.data = self._DoReadFileRealDtb('115_fdtmap.dts')
2118
2119 # Mangle the section name, which should cause a mismatch between the
2120 # correct FDT path and the one expected by the section
2121 image = control.images['image']
Simon Glasscf228942019-07-08 14:25:28 -06002122 image._node.path += '-suffix'
Simon Glass086cec92019-07-08 14:25:27 -06002123 entries = image.GetEntries()
2124 fdtmap = entries['fdtmap']
2125 with self.assertRaises(ValueError) as e:
2126 fdtmap._GetFdtmap()
2127 self.assertIn("Cannot locate node for path '/binman-suffix'",
2128 str(e.exception))
2129
Simon Glasscf228942019-07-08 14:25:28 -06002130 def testFdtmapHeader(self):
2131 """Test an FDT map and image header can be inserted in the image"""
2132 data = self.data = self._DoReadFileRealDtb('116_fdtmap_hdr.dts')
2133 fdtmap_pos = len(U_BOOT_DATA)
2134 fdtmap_data = data[fdtmap_pos:]
2135 fdt_data = fdtmap_data[16:]
2136 dtb = fdt.Fdt.FromData(fdt_data)
2137 fdt_size = dtb.GetFdtObj().totalsize()
2138 hdr_data = data[-8:]
2139 self.assertEqual('BinM', hdr_data[:4])
2140 offset = struct.unpack('<I', hdr_data[4:])[0] & 0xffffffff
2141 self.assertEqual(fdtmap_pos - 0x400, offset - (1 << 32))
2142
2143 def testFdtmapHeaderStart(self):
2144 """Test an image header can be inserted at the image start"""
2145 data = self.data = self._DoReadFileRealDtb('117_fdtmap_hdr_start.dts')
2146 fdtmap_pos = 0x100 + len(U_BOOT_DATA)
2147 hdr_data = data[:8]
2148 self.assertEqual('BinM', hdr_data[:4])
2149 offset = struct.unpack('<I', hdr_data[4:])[0]
2150 self.assertEqual(fdtmap_pos, offset)
2151
2152 def testFdtmapHeaderPos(self):
2153 """Test an image header can be inserted at a chosen position"""
2154 data = self.data = self._DoReadFileRealDtb('118_fdtmap_hdr_pos.dts')
2155 fdtmap_pos = 0x100 + len(U_BOOT_DATA)
2156 hdr_data = data[0x80:0x88]
2157 self.assertEqual('BinM', hdr_data[:4])
2158 offset = struct.unpack('<I', hdr_data[4:])[0]
2159 self.assertEqual(fdtmap_pos, offset)
2160
2161 def testHeaderMissingFdtmap(self):
2162 """Test an image header requires an fdtmap"""
2163 with self.assertRaises(ValueError) as e:
2164 self.data = self._DoReadFileRealDtb('119_fdtmap_hdr_missing.dts')
2165 self.assertIn("'image_header' section must have an 'fdtmap' sibling",
2166 str(e.exception))
2167
2168 def testHeaderNoLocation(self):
2169 """Test an image header with a no specified location is detected"""
2170 with self.assertRaises(ValueError) as e:
2171 self.data = self._DoReadFileRealDtb('120_hdr_no_location.dts')
2172 self.assertIn("Invalid location 'None', expected 'start' or 'end'",
2173 str(e.exception))
2174
Simon Glassc52c9e72019-07-08 14:25:37 -06002175 def testEntryExpand(self):
2176 """Test expanding an entry after it is packed"""
2177 data = self._DoReadFile('121_entry_expand.dts')
Simon Glass79d3c582019-07-20 12:23:57 -06002178 self.assertEqual(b'aaa', data[:3])
2179 self.assertEqual(U_BOOT_DATA, data[3:3 + len(U_BOOT_DATA)])
2180 self.assertEqual(b'aaa', data[-3:])
Simon Glassc52c9e72019-07-08 14:25:37 -06002181
2182 def testEntryExpandBad(self):
2183 """Test expanding an entry after it is packed, twice"""
2184 with self.assertRaises(ValueError) as e:
2185 self._DoReadFile('122_entry_expand_twice.dts')
Simon Glass61ec04f2019-07-20 12:23:58 -06002186 self.assertIn("Image '/binman': Entries changed size after packing",
Simon Glassc52c9e72019-07-08 14:25:37 -06002187 str(e.exception))
2188
2189 def testEntryExpandSection(self):
2190 """Test expanding an entry within a section after it is packed"""
2191 data = self._DoReadFile('123_entry_expand_section.dts')
Simon Glass79d3c582019-07-20 12:23:57 -06002192 self.assertEqual(b'aaa', data[:3])
2193 self.assertEqual(U_BOOT_DATA, data[3:3 + len(U_BOOT_DATA)])
2194 self.assertEqual(b'aaa', data[-3:])
Simon Glassc52c9e72019-07-08 14:25:37 -06002195
Simon Glass6c223fd2019-07-08 14:25:38 -06002196 def testCompressDtb(self):
2197 """Test that compress of device-tree files is supported"""
2198 self._CheckLz4()
2199 data = self.data = self._DoReadFileRealDtb('124_compress_dtb.dts')
2200 self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
2201 comp_data = data[len(U_BOOT_DATA):]
2202 orig = self._decompress(comp_data)
2203 dtb = fdt.Fdt.FromData(orig)
2204 dtb.Scan()
2205 props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
2206 expected = {
2207 'u-boot:size': len(U_BOOT_DATA),
2208 'u-boot-dtb:uncomp-size': len(orig),
2209 'u-boot-dtb:size': len(comp_data),
2210 'size': len(data),
2211 }
2212 self.assertEqual(expected, props)
2213
Simon Glass69f7cb32019-07-08 14:25:41 -06002214 def testCbfsUpdateFdt(self):
2215 """Test that we can update the device tree with CBFS offset/size info"""
2216 self._CheckLz4()
2217 data, _, _, out_dtb_fname = self._DoReadFileDtb('125_cbfs_update.dts',
2218 update_dtb=True)
2219 dtb = fdt.Fdt(out_dtb_fname)
2220 dtb.Scan()
Simon Glass6ccbfcd2019-07-20 12:23:47 -06002221 props = self._GetPropTree(dtb, BASE_DTB_PROPS + ['uncomp-size'])
Simon Glass69f7cb32019-07-08 14:25:41 -06002222 del props['cbfs/u-boot:size']
2223 self.assertEqual({
2224 'offset': 0,
2225 'size': len(data),
2226 'image-pos': 0,
2227 'cbfs:offset': 0,
2228 'cbfs:size': len(data),
2229 'cbfs:image-pos': 0,
2230 'cbfs/u-boot:offset': 0x38,
2231 'cbfs/u-boot:uncomp-size': len(U_BOOT_DATA),
2232 'cbfs/u-boot:image-pos': 0x38,
2233 'cbfs/u-boot-dtb:offset': 0xb8,
2234 'cbfs/u-boot-dtb:size': len(U_BOOT_DATA),
2235 'cbfs/u-boot-dtb:image-pos': 0xb8,
2236 }, props)
2237
Simon Glass8a1ad062019-07-08 14:25:42 -06002238 def testCbfsBadType(self):
2239 """Test an image header with a no specified location is detected"""
2240 with self.assertRaises(ValueError) as e:
2241 self._DoReadFile('126_cbfs_bad_type.dts')
2242 self.assertIn("Unknown cbfs-type 'badtype'", str(e.exception))
2243
Simon Glass41b8ba02019-07-08 14:25:43 -06002244 def testList(self):
2245 """Test listing the files in an image"""
2246 self._CheckLz4()
2247 data = self._DoReadFile('127_list.dts')
2248 image = control.images['image']
2249 entries = image.BuildEntryList()
2250 self.assertEqual(7, len(entries))
2251
2252 ent = entries[0]
2253 self.assertEqual(0, ent.indent)
2254 self.assertEqual('main-section', ent.name)
2255 self.assertEqual('section', ent.etype)
2256 self.assertEqual(len(data), ent.size)
2257 self.assertEqual(0, ent.image_pos)
2258 self.assertEqual(None, ent.uncomp_size)
Simon Glass8beb11e2019-07-08 14:25:47 -06002259 self.assertEqual(0, ent.offset)
Simon Glass41b8ba02019-07-08 14:25:43 -06002260
2261 ent = entries[1]
2262 self.assertEqual(1, ent.indent)
2263 self.assertEqual('u-boot', ent.name)
2264 self.assertEqual('u-boot', ent.etype)
2265 self.assertEqual(len(U_BOOT_DATA), ent.size)
2266 self.assertEqual(0, ent.image_pos)
2267 self.assertEqual(None, ent.uncomp_size)
2268 self.assertEqual(0, ent.offset)
2269
2270 ent = entries[2]
2271 self.assertEqual(1, ent.indent)
2272 self.assertEqual('section', ent.name)
2273 self.assertEqual('section', ent.etype)
2274 section_size = ent.size
2275 self.assertEqual(0x100, ent.image_pos)
2276 self.assertEqual(None, ent.uncomp_size)
Simon Glass8beb11e2019-07-08 14:25:47 -06002277 self.assertEqual(0x100, ent.offset)
Simon Glass41b8ba02019-07-08 14:25:43 -06002278
2279 ent = entries[3]
2280 self.assertEqual(2, ent.indent)
2281 self.assertEqual('cbfs', ent.name)
2282 self.assertEqual('cbfs', ent.etype)
2283 self.assertEqual(0x400, ent.size)
2284 self.assertEqual(0x100, ent.image_pos)
2285 self.assertEqual(None, ent.uncomp_size)
2286 self.assertEqual(0, ent.offset)
2287
2288 ent = entries[4]
2289 self.assertEqual(3, ent.indent)
2290 self.assertEqual('u-boot', ent.name)
2291 self.assertEqual('u-boot', ent.etype)
2292 self.assertEqual(len(U_BOOT_DATA), ent.size)
2293 self.assertEqual(0x138, ent.image_pos)
2294 self.assertEqual(None, ent.uncomp_size)
2295 self.assertEqual(0x38, ent.offset)
2296
2297 ent = entries[5]
2298 self.assertEqual(3, ent.indent)
2299 self.assertEqual('u-boot-dtb', ent.name)
2300 self.assertEqual('text', ent.etype)
2301 self.assertGreater(len(COMPRESS_DATA), ent.size)
2302 self.assertEqual(0x178, ent.image_pos)
2303 self.assertEqual(len(COMPRESS_DATA), ent.uncomp_size)
2304 self.assertEqual(0x78, ent.offset)
2305
2306 ent = entries[6]
2307 self.assertEqual(2, ent.indent)
2308 self.assertEqual('u-boot-dtb', ent.name)
2309 self.assertEqual('u-boot-dtb', ent.etype)
2310 self.assertEqual(0x500, ent.image_pos)
2311 self.assertEqual(len(U_BOOT_DTB_DATA), ent.uncomp_size)
2312 dtb_size = ent.size
2313 # Compressing this data expands it since headers are added
2314 self.assertGreater(dtb_size, len(U_BOOT_DTB_DATA))
2315 self.assertEqual(0x400, ent.offset)
2316
2317 self.assertEqual(len(data), 0x100 + section_size)
2318 self.assertEqual(section_size, 0x400 + dtb_size)
2319
Simon Glasse1925fa2019-07-08 14:25:44 -06002320 def testFindFdtmap(self):
2321 """Test locating an FDT map in an image"""
2322 self._CheckLz4()
2323 data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
2324 image = control.images['image']
2325 entries = image.GetEntries()
2326 entry = entries['fdtmap']
2327 self.assertEqual(entry.image_pos, fdtmap.LocateFdtmap(data))
2328
2329 def testFindFdtmapMissing(self):
2330 """Test failing to locate an FDP map"""
2331 data = self._DoReadFile('005_simple.dts')
2332 self.assertEqual(None, fdtmap.LocateFdtmap(data))
2333
Simon Glass2d260032019-07-08 14:25:45 -06002334 def testFindImageHeader(self):
2335 """Test locating a image header"""
2336 self._CheckLz4()
Simon Glassffded752019-07-08 14:25:46 -06002337 data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
Simon Glass2d260032019-07-08 14:25:45 -06002338 image = control.images['image']
2339 entries = image.GetEntries()
2340 entry = entries['fdtmap']
2341 # The header should point to the FDT map
2342 self.assertEqual(entry.image_pos, image_header.LocateHeaderOffset(data))
2343
2344 def testFindImageHeaderStart(self):
2345 """Test locating a image header located at the start of an image"""
Simon Glassffded752019-07-08 14:25:46 -06002346 data = self.data = self._DoReadFileRealDtb('117_fdtmap_hdr_start.dts')
Simon Glass2d260032019-07-08 14:25:45 -06002347 image = control.images['image']
2348 entries = image.GetEntries()
2349 entry = entries['fdtmap']
2350 # The header should point to the FDT map
2351 self.assertEqual(entry.image_pos, image_header.LocateHeaderOffset(data))
2352
2353 def testFindImageHeaderMissing(self):
2354 """Test failing to locate an image header"""
2355 data = self._DoReadFile('005_simple.dts')
2356 self.assertEqual(None, image_header.LocateHeaderOffset(data))
2357
Simon Glassffded752019-07-08 14:25:46 -06002358 def testReadImage(self):
2359 """Test reading an image and accessing its FDT map"""
2360 self._CheckLz4()
2361 data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
2362 image_fname = tools.GetOutputFilename('image.bin')
2363 orig_image = control.images['image']
2364 image = Image.FromFile(image_fname)
2365 self.assertEqual(orig_image.GetEntries().keys(),
2366 image.GetEntries().keys())
2367
2368 orig_entry = orig_image.GetEntries()['fdtmap']
2369 entry = image.GetEntries()['fdtmap']
2370 self.assertEquals(orig_entry.offset, entry.offset)
2371 self.assertEquals(orig_entry.size, entry.size)
2372 self.assertEquals(orig_entry.image_pos, entry.image_pos)
2373
2374 def testReadImageNoHeader(self):
2375 """Test accessing an image's FDT map without an image header"""
2376 self._CheckLz4()
2377 data = self._DoReadFileRealDtb('129_decode_image_nohdr.dts')
2378 image_fname = tools.GetOutputFilename('image.bin')
2379 image = Image.FromFile(image_fname)
2380 self.assertTrue(isinstance(image, Image))
Simon Glass10f9d002019-07-20 12:23:50 -06002381 self.assertEqual('image', image.image_name[-5:])
Simon Glassffded752019-07-08 14:25:46 -06002382
2383 def testReadImageFail(self):
2384 """Test failing to read an image image's FDT map"""
2385 self._DoReadFile('005_simple.dts')
2386 image_fname = tools.GetOutputFilename('image.bin')
2387 with self.assertRaises(ValueError) as e:
2388 image = Image.FromFile(image_fname)
2389 self.assertIn("Cannot find FDT map in image", str(e.exception))
Simon Glasse073d4e2019-07-08 13:18:56 -06002390
Simon Glass61f564d2019-07-08 14:25:48 -06002391 def testListCmd(self):
2392 """Test listing the files in an image using an Fdtmap"""
2393 self._CheckLz4()
2394 data = self._DoReadFileRealDtb('130_list_fdtmap.dts')
2395
2396 # lz4 compression size differs depending on the version
2397 image = control.images['image']
2398 entries = image.GetEntries()
2399 section_size = entries['section'].size
2400 fdt_size = entries['section'].GetEntries()['u-boot-dtb'].size
2401 fdtmap_offset = entries['fdtmap'].offset
2402
Simon Glassf86a7362019-07-20 12:24:10 -06002403 try:
2404 tmpdir, updated_fname = self._SetupImageInTmpdir()
2405 with test_util.capture_sys_output() as (stdout, stderr):
2406 self._DoBinman('ls', '-i', updated_fname)
2407 finally:
2408 shutil.rmtree(tmpdir)
Simon Glass61f564d2019-07-08 14:25:48 -06002409 lines = stdout.getvalue().splitlines()
2410 expected = [
2411'Name Image-pos Size Entry-type Offset Uncomp-size',
2412'----------------------------------------------------------------------',
2413'main-section 0 c00 section 0',
2414' u-boot 0 4 u-boot 0',
2415' section 100 %x section 100' % section_size,
2416' cbfs 100 400 cbfs 0',
2417' u-boot 138 4 u-boot 38',
2418' u-boot-dtb 180 10f u-boot-dtb 80 3c9',
2419' u-boot-dtb 500 %x u-boot-dtb 400 3c9' % fdt_size,
Simon Glass1411ac82019-07-20 12:23:44 -06002420' fdtmap %x 3b4 fdtmap %x' %
Simon Glass61f564d2019-07-08 14:25:48 -06002421 (fdtmap_offset, fdtmap_offset),
2422' image-header bf8 8 image-header bf8',
2423 ]
2424 self.assertEqual(expected, lines)
2425
2426 def testListCmdFail(self):
2427 """Test failing to list an image"""
2428 self._DoReadFile('005_simple.dts')
Simon Glassf86a7362019-07-20 12:24:10 -06002429 try:
2430 tmpdir, updated_fname = self._SetupImageInTmpdir()
2431 with self.assertRaises(ValueError) as e:
2432 self._DoBinman('ls', '-i', updated_fname)
2433 finally:
2434 shutil.rmtree(tmpdir)
Simon Glass61f564d2019-07-08 14:25:48 -06002435 self.assertIn("Cannot find FDT map in image", str(e.exception))
2436
2437 def _RunListCmd(self, paths, expected):
2438 """List out entries and check the result
2439
2440 Args:
2441 paths: List of paths to pass to the list command
2442 expected: Expected list of filenames to be returned, in order
2443 """
2444 self._CheckLz4()
2445 self._DoReadFileRealDtb('130_list_fdtmap.dts')
2446 image_fname = tools.GetOutputFilename('image.bin')
2447 image = Image.FromFile(image_fname)
2448 lines = image.GetListEntries(paths)[1]
2449 files = [line[0].strip() for line in lines[1:]]
2450 self.assertEqual(expected, files)
2451
2452 def testListCmdSection(self):
2453 """Test listing the files in a section"""
2454 self._RunListCmd(['section'],
2455 ['section', 'cbfs', 'u-boot', 'u-boot-dtb', 'u-boot-dtb'])
2456
2457 def testListCmdFile(self):
2458 """Test listing a particular file"""
2459 self._RunListCmd(['*u-boot-dtb'], ['u-boot-dtb', 'u-boot-dtb'])
2460
2461 def testListCmdWildcard(self):
2462 """Test listing a wildcarded file"""
2463 self._RunListCmd(['*boot*'],
2464 ['u-boot', 'u-boot', 'u-boot-dtb', 'u-boot-dtb'])
2465
2466 def testListCmdWildcardMulti(self):
2467 """Test listing a wildcarded file"""
2468 self._RunListCmd(['*cb*', '*head*'],
2469 ['cbfs', 'u-boot', 'u-boot-dtb', 'image-header'])
2470
2471 def testListCmdEmpty(self):
2472 """Test listing a wildcarded file"""
2473 self._RunListCmd(['nothing'], [])
2474
2475 def testListCmdPath(self):
2476 """Test listing the files in a sub-entry of a section"""
2477 self._RunListCmd(['section/cbfs'], ['cbfs', 'u-boot', 'u-boot-dtb'])
2478
Simon Glassf667e452019-07-08 14:25:50 -06002479 def _RunExtractCmd(self, entry_name, decomp=True):
2480 """Extract an entry from an image
2481
2482 Args:
2483 entry_name: Entry name to extract
2484 decomp: True to decompress the data if compressed, False to leave
2485 it in its raw uncompressed format
2486
2487 Returns:
2488 data from entry
2489 """
2490 self._CheckLz4()
2491 self._DoReadFileRealDtb('130_list_fdtmap.dts')
2492 image_fname = tools.GetOutputFilename('image.bin')
2493 return control.ReadEntry(image_fname, entry_name, decomp)
2494
2495 def testExtractSimple(self):
2496 """Test extracting a single file"""
2497 data = self._RunExtractCmd('u-boot')
2498 self.assertEqual(U_BOOT_DATA, data)
2499
Simon Glass71ce0ba2019-07-08 14:25:52 -06002500 def testExtractSection(self):
2501 """Test extracting the files in a section"""
2502 data = self._RunExtractCmd('section')
2503 cbfs_data = data[:0x400]
2504 cbfs = cbfs_util.CbfsReader(cbfs_data)
2505 self.assertEqual(['u-boot', 'u-boot-dtb', ''], cbfs.files.keys())
2506 dtb_data = data[0x400:]
2507 dtb = self._decompress(dtb_data)
2508 self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
2509
2510 def testExtractCompressed(self):
2511 """Test extracting compressed data"""
2512 data = self._RunExtractCmd('section/u-boot-dtb')
2513 self.assertEqual(EXTRACT_DTB_SIZE, len(data))
2514
2515 def testExtractRaw(self):
2516 """Test extracting compressed data without decompressing it"""
2517 data = self._RunExtractCmd('section/u-boot-dtb', decomp=False)
2518 dtb = self._decompress(data)
2519 self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
2520
2521 def testExtractCbfs(self):
2522 """Test extracting CBFS data"""
2523 data = self._RunExtractCmd('section/cbfs/u-boot')
2524 self.assertEqual(U_BOOT_DATA, data)
2525
2526 def testExtractCbfsCompressed(self):
2527 """Test extracting CBFS compressed data"""
2528 data = self._RunExtractCmd('section/cbfs/u-boot-dtb')
2529 self.assertEqual(EXTRACT_DTB_SIZE, len(data))
2530
2531 def testExtractCbfsRaw(self):
2532 """Test extracting CBFS compressed data without decompressing it"""
2533 data = self._RunExtractCmd('section/cbfs/u-boot-dtb', decomp=False)
Simon Glasseb0f4a42019-07-20 12:24:06 -06002534 dtb = tools.Decompress(data, 'lzma', with_header=False)
Simon Glass71ce0ba2019-07-08 14:25:52 -06002535 self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
2536
Simon Glassf667e452019-07-08 14:25:50 -06002537 def testExtractBadEntry(self):
2538 """Test extracting a bad section path"""
2539 with self.assertRaises(ValueError) as e:
2540 self._RunExtractCmd('section/does-not-exist')
2541 self.assertIn("Entry 'does-not-exist' not found in '/section'",
2542 str(e.exception))
2543
2544 def testExtractMissingFile(self):
2545 """Test extracting file that does not exist"""
2546 with self.assertRaises(IOError) as e:
2547 control.ReadEntry('missing-file', 'name')
2548
2549 def testExtractBadFile(self):
2550 """Test extracting an invalid file"""
2551 fname = os.path.join(self._indir, 'badfile')
2552 tools.WriteFile(fname, b'')
2553 with self.assertRaises(ValueError) as e:
2554 control.ReadEntry(fname, 'name')
2555
Simon Glass71ce0ba2019-07-08 14:25:52 -06002556 def testExtractCmd(self):
2557 """Test extracting a file fron an image on the command line"""
2558 self._CheckLz4()
2559 self._DoReadFileRealDtb('130_list_fdtmap.dts')
Simon Glass71ce0ba2019-07-08 14:25:52 -06002560 fname = os.path.join(self._indir, 'output.extact')
Simon Glassf86a7362019-07-20 12:24:10 -06002561 try:
2562 tmpdir, updated_fname = self._SetupImageInTmpdir()
2563 with test_util.capture_sys_output() as (stdout, stderr):
2564 self._DoBinman('extract', '-i', updated_fname, 'u-boot',
2565 '-f', fname)
2566 finally:
2567 shutil.rmtree(tmpdir)
Simon Glass71ce0ba2019-07-08 14:25:52 -06002568 data = tools.ReadFile(fname)
2569 self.assertEqual(U_BOOT_DATA, data)
2570
2571 def testExtractOneEntry(self):
2572 """Test extracting a single entry fron an image """
2573 self._CheckLz4()
2574 self._DoReadFileRealDtb('130_list_fdtmap.dts')
2575 image_fname = tools.GetOutputFilename('image.bin')
2576 fname = os.path.join(self._indir, 'output.extact')
2577 control.ExtractEntries(image_fname, fname, None, ['u-boot'])
2578 data = tools.ReadFile(fname)
2579 self.assertEqual(U_BOOT_DATA, data)
2580
2581 def _CheckExtractOutput(self, decomp):
2582 """Helper to test file output with and without decompression
2583
2584 Args:
2585 decomp: True to decompress entry data, False to output it raw
2586 """
2587 def _CheckPresent(entry_path, expect_data, expect_size=None):
2588 """Check and remove expected file
2589
2590 This checks the data/size of a file and removes the file both from
2591 the outfiles set and from the output directory. Once all files are
2592 processed, both the set and directory should be empty.
2593
2594 Args:
2595 entry_path: Entry path
2596 expect_data: Data to expect in file, or None to skip check
2597 expect_size: Size of data to expect in file, or None to skip
2598 """
2599 path = os.path.join(outdir, entry_path)
2600 data = tools.ReadFile(path)
2601 os.remove(path)
2602 if expect_data:
2603 self.assertEqual(expect_data, data)
2604 elif expect_size:
2605 self.assertEqual(expect_size, len(data))
2606 outfiles.remove(path)
2607
2608 def _CheckDirPresent(name):
2609 """Remove expected directory
2610
2611 This gives an error if the directory does not exist as expected
2612
2613 Args:
2614 name: Name of directory to remove
2615 """
2616 path = os.path.join(outdir, name)
2617 os.rmdir(path)
2618
2619 self._DoReadFileRealDtb('130_list_fdtmap.dts')
2620 image_fname = tools.GetOutputFilename('image.bin')
2621 outdir = os.path.join(self._indir, 'extract')
2622 einfos = control.ExtractEntries(image_fname, None, outdir, [], decomp)
2623
2624 # Create a set of all file that were output (should be 9)
2625 outfiles = set()
2626 for root, dirs, files in os.walk(outdir):
2627 outfiles |= set([os.path.join(root, fname) for fname in files])
2628 self.assertEqual(9, len(outfiles))
2629 self.assertEqual(9, len(einfos))
2630
2631 image = control.images['image']
2632 entries = image.GetEntries()
2633
2634 # Check the 9 files in various ways
2635 section = entries['section']
2636 section_entries = section.GetEntries()
2637 cbfs_entries = section_entries['cbfs'].GetEntries()
2638 _CheckPresent('u-boot', U_BOOT_DATA)
2639 _CheckPresent('section/cbfs/u-boot', U_BOOT_DATA)
2640 dtb_len = EXTRACT_DTB_SIZE
2641 if not decomp:
2642 dtb_len = cbfs_entries['u-boot-dtb'].size
2643 _CheckPresent('section/cbfs/u-boot-dtb', None, dtb_len)
2644 if not decomp:
2645 dtb_len = section_entries['u-boot-dtb'].size
2646 _CheckPresent('section/u-boot-dtb', None, dtb_len)
2647
2648 fdtmap = entries['fdtmap']
2649 _CheckPresent('fdtmap', fdtmap.data)
2650 hdr = entries['image-header']
2651 _CheckPresent('image-header', hdr.data)
2652
2653 _CheckPresent('section/root', section.data)
2654 cbfs = section_entries['cbfs']
2655 _CheckPresent('section/cbfs/root', cbfs.data)
2656 data = tools.ReadFile(image_fname)
2657 _CheckPresent('root', data)
2658
2659 # There should be no files left. Remove all the directories to check.
2660 # If there are any files/dirs remaining, one of these checks will fail.
2661 self.assertEqual(0, len(outfiles))
2662 _CheckDirPresent('section/cbfs')
2663 _CheckDirPresent('section')
2664 _CheckDirPresent('')
2665 self.assertFalse(os.path.exists(outdir))
2666
2667 def testExtractAllEntries(self):
2668 """Test extracting all entries"""
2669 self._CheckLz4()
2670 self._CheckExtractOutput(decomp=True)
2671
2672 def testExtractAllEntriesRaw(self):
2673 """Test extracting all entries without decompressing them"""
2674 self._CheckLz4()
2675 self._CheckExtractOutput(decomp=False)
2676
2677 def testExtractSelectedEntries(self):
2678 """Test extracting some entries"""
2679 self._CheckLz4()
2680 self._DoReadFileRealDtb('130_list_fdtmap.dts')
2681 image_fname = tools.GetOutputFilename('image.bin')
2682 outdir = os.path.join(self._indir, 'extract')
2683 einfos = control.ExtractEntries(image_fname, None, outdir,
2684 ['*cb*', '*head*'])
2685
2686 # File output is tested by testExtractAllEntries(), so just check that
2687 # the expected entries are selected
2688 names = [einfo.name for einfo in einfos]
2689 self.assertEqual(names,
2690 ['cbfs', 'u-boot', 'u-boot-dtb', 'image-header'])
2691
2692 def testExtractNoEntryPaths(self):
2693 """Test extracting some entries"""
2694 self._CheckLz4()
2695 self._DoReadFileRealDtb('130_list_fdtmap.dts')
2696 image_fname = tools.GetOutputFilename('image.bin')
2697 with self.assertRaises(ValueError) as e:
2698 control.ExtractEntries(image_fname, 'fname', None, [])
Simon Glassbb5edc12019-07-20 12:24:14 -06002699 self.assertIn('Must specify an entry path to write with -f',
Simon Glass71ce0ba2019-07-08 14:25:52 -06002700 str(e.exception))
2701
2702 def testExtractTooManyEntryPaths(self):
2703 """Test extracting some entries"""
2704 self._CheckLz4()
2705 self._DoReadFileRealDtb('130_list_fdtmap.dts')
2706 image_fname = tools.GetOutputFilename('image.bin')
2707 with self.assertRaises(ValueError) as e:
2708 control.ExtractEntries(image_fname, 'fname', None, ['a', 'b'])
Simon Glassbb5edc12019-07-20 12:24:14 -06002709 self.assertIn('Must specify exactly one entry path to write with -f',
Simon Glass71ce0ba2019-07-08 14:25:52 -06002710 str(e.exception))
2711
Simon Glasse2705fa2019-07-08 14:25:53 -06002712 def testPackAlignSection(self):
2713 """Test that sections can have alignment"""
2714 self._DoReadFile('131_pack_align_section.dts')
2715
2716 self.assertIn('image', control.images)
2717 image = control.images['image']
2718 entries = image.GetEntries()
2719 self.assertEqual(3, len(entries))
2720
2721 # First u-boot
2722 self.assertIn('u-boot', entries)
2723 entry = entries['u-boot']
2724 self.assertEqual(0, entry.offset)
2725 self.assertEqual(0, entry.image_pos)
2726 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
2727 self.assertEqual(len(U_BOOT_DATA), entry.size)
2728
2729 # Section0
2730 self.assertIn('section0', entries)
2731 section0 = entries['section0']
2732 self.assertEqual(0x10, section0.offset)
2733 self.assertEqual(0x10, section0.image_pos)
2734 self.assertEqual(len(U_BOOT_DATA), section0.size)
2735
2736 # Second u-boot
2737 section_entries = section0.GetEntries()
2738 self.assertIn('u-boot', section_entries)
2739 entry = section_entries['u-boot']
2740 self.assertEqual(0, entry.offset)
2741 self.assertEqual(0x10, entry.image_pos)
2742 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
2743 self.assertEqual(len(U_BOOT_DATA), entry.size)
2744
2745 # Section1
2746 self.assertIn('section1', entries)
2747 section1 = entries['section1']
2748 self.assertEqual(0x14, section1.offset)
2749 self.assertEqual(0x14, section1.image_pos)
2750 self.assertEqual(0x20, section1.size)
2751
2752 # Second u-boot
2753 section_entries = section1.GetEntries()
2754 self.assertIn('u-boot', section_entries)
2755 entry = section_entries['u-boot']
2756 self.assertEqual(0, entry.offset)
2757 self.assertEqual(0x14, entry.image_pos)
2758 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
2759 self.assertEqual(len(U_BOOT_DATA), entry.size)
2760
2761 # Section2
2762 self.assertIn('section2', section_entries)
2763 section2 = section_entries['section2']
2764 self.assertEqual(0x4, section2.offset)
2765 self.assertEqual(0x18, section2.image_pos)
2766 self.assertEqual(4, section2.size)
2767
2768 # Third u-boot
2769 section_entries = section2.GetEntries()
2770 self.assertIn('u-boot', section_entries)
2771 entry = section_entries['u-boot']
2772 self.assertEqual(0, entry.offset)
2773 self.assertEqual(0x18, entry.image_pos)
2774 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
2775 self.assertEqual(len(U_BOOT_DATA), entry.size)
2776
Simon Glass51014aa2019-07-20 12:23:56 -06002777 def _RunReplaceCmd(self, entry_name, data, decomp=True, allow_resize=True,
2778 dts='132_replace.dts'):
Simon Glass10f9d002019-07-20 12:23:50 -06002779 """Replace an entry in an image
2780
2781 This writes the entry data to update it, then opens the updated file and
2782 returns the value that it now finds there.
2783
2784 Args:
2785 entry_name: Entry name to replace
2786 data: Data to replace it with
2787 decomp: True to compress the data if needed, False if data is
2788 already compressed so should be used as is
Simon Glass51014aa2019-07-20 12:23:56 -06002789 allow_resize: True to allow entries to change size, False to raise
2790 an exception
Simon Glass10f9d002019-07-20 12:23:50 -06002791
2792 Returns:
2793 Tuple:
2794 data from entry
2795 data from fdtmap (excluding header)
Simon Glass51014aa2019-07-20 12:23:56 -06002796 Image object that was modified
Simon Glass10f9d002019-07-20 12:23:50 -06002797 """
Simon Glass51014aa2019-07-20 12:23:56 -06002798 dtb_data = self._DoReadFileDtb(dts, use_real_dtb=True,
Simon Glass10f9d002019-07-20 12:23:50 -06002799 update_dtb=True)[1]
2800
2801 self.assertIn('image', control.images)
2802 image = control.images['image']
2803 entries = image.GetEntries()
2804 orig_dtb_data = entries['u-boot-dtb'].data
2805 orig_fdtmap_data = entries['fdtmap'].data
2806
2807 image_fname = tools.GetOutputFilename('image.bin')
2808 updated_fname = tools.GetOutputFilename('image-updated.bin')
2809 tools.WriteFile(updated_fname, tools.ReadFile(image_fname))
Simon Glass51014aa2019-07-20 12:23:56 -06002810 image = control.WriteEntry(updated_fname, entry_name, data, decomp,
2811 allow_resize)
Simon Glass10f9d002019-07-20 12:23:50 -06002812 data = control.ReadEntry(updated_fname, entry_name, decomp)
2813
Simon Glass51014aa2019-07-20 12:23:56 -06002814 # The DT data should not change unless resized:
2815 if not allow_resize:
2816 new_dtb_data = entries['u-boot-dtb'].data
2817 self.assertEqual(new_dtb_data, orig_dtb_data)
2818 new_fdtmap_data = entries['fdtmap'].data
2819 self.assertEqual(new_fdtmap_data, orig_fdtmap_data)
Simon Glass10f9d002019-07-20 12:23:50 -06002820
Simon Glass51014aa2019-07-20 12:23:56 -06002821 return data, orig_fdtmap_data[fdtmap.FDTMAP_HDR_LEN:], image
Simon Glass10f9d002019-07-20 12:23:50 -06002822
2823 def testReplaceSimple(self):
2824 """Test replacing a single file"""
2825 expected = b'x' * len(U_BOOT_DATA)
Simon Glass51014aa2019-07-20 12:23:56 -06002826 data, expected_fdtmap, _ = self._RunReplaceCmd('u-boot', expected,
2827 allow_resize=False)
Simon Glass10f9d002019-07-20 12:23:50 -06002828 self.assertEqual(expected, data)
2829
2830 # Test that the state looks right. There should be an FDT for the fdtmap
2831 # that we jsut read back in, and it should match what we find in the
2832 # 'control' tables. Checking for an FDT that does not exist should
2833 # return None.
2834 path, fdtmap = state.GetFdtContents('fdtmap')
Simon Glass51014aa2019-07-20 12:23:56 -06002835 self.assertIsNotNone(path)
Simon Glass10f9d002019-07-20 12:23:50 -06002836 self.assertEqual(expected_fdtmap, fdtmap)
2837
2838 dtb = state.GetFdtForEtype('fdtmap')
2839 self.assertEqual(dtb.GetContents(), fdtmap)
2840
2841 missing_path, missing_fdtmap = state.GetFdtContents('missing')
2842 self.assertIsNone(missing_path)
2843 self.assertIsNone(missing_fdtmap)
2844
2845 missing_dtb = state.GetFdtForEtype('missing')
2846 self.assertIsNone(missing_dtb)
2847
2848 self.assertEqual('/binman', state.fdt_path_prefix)
2849
2850 def testReplaceResizeFail(self):
2851 """Test replacing a file by something larger"""
2852 expected = U_BOOT_DATA + b'x'
2853 with self.assertRaises(ValueError) as e:
Simon Glass51014aa2019-07-20 12:23:56 -06002854 self._RunReplaceCmd('u-boot', expected, allow_resize=False,
2855 dts='139_replace_repack.dts')
Simon Glass10f9d002019-07-20 12:23:50 -06002856 self.assertIn("Node '/u-boot': Entry data size does not match, but resize is disabled",
2857 str(e.exception))
2858
2859 def testReplaceMulti(self):
2860 """Test replacing entry data where multiple images are generated"""
2861 data = self._DoReadFileDtb('133_replace_multi.dts', use_real_dtb=True,
2862 update_dtb=True)[0]
2863 expected = b'x' * len(U_BOOT_DATA)
2864 updated_fname = tools.GetOutputFilename('image-updated.bin')
2865 tools.WriteFile(updated_fname, data)
2866 entry_name = 'u-boot'
Simon Glass51014aa2019-07-20 12:23:56 -06002867 control.WriteEntry(updated_fname, entry_name, expected,
2868 allow_resize=False)
Simon Glass10f9d002019-07-20 12:23:50 -06002869 data = control.ReadEntry(updated_fname, entry_name)
2870 self.assertEqual(expected, data)
2871
2872 # Check the state looks right.
2873 self.assertEqual('/binman/image', state.fdt_path_prefix)
2874
2875 # Now check we can write the first image
2876 image_fname = tools.GetOutputFilename('first-image.bin')
2877 updated_fname = tools.GetOutputFilename('first-updated.bin')
2878 tools.WriteFile(updated_fname, tools.ReadFile(image_fname))
2879 entry_name = 'u-boot'
Simon Glass51014aa2019-07-20 12:23:56 -06002880 control.WriteEntry(updated_fname, entry_name, expected,
2881 allow_resize=False)
Simon Glass10f9d002019-07-20 12:23:50 -06002882 data = control.ReadEntry(updated_fname, entry_name)
2883 self.assertEqual(expected, data)
2884
2885 # Check the state looks right.
2886 self.assertEqual('/binman/first-image', state.fdt_path_prefix)
Simon Glass8beb11e2019-07-08 14:25:47 -06002887
Simon Glass12bb1a92019-07-20 12:23:51 -06002888 def testUpdateFdtAllRepack(self):
2889 """Test that all device trees are updated with offset/size info"""
2890 data = self._DoReadFileRealDtb('134_fdt_update_all_repack.dts')
2891 SECTION_SIZE = 0x300
2892 DTB_SIZE = 602
2893 FDTMAP_SIZE = 608
2894 base_expected = {
2895 'offset': 0,
2896 'size': SECTION_SIZE + DTB_SIZE * 2 + FDTMAP_SIZE,
2897 'image-pos': 0,
2898 'section:offset': 0,
2899 'section:size': SECTION_SIZE,
2900 'section:image-pos': 0,
2901 'section/u-boot-dtb:offset': 4,
2902 'section/u-boot-dtb:size': 636,
2903 'section/u-boot-dtb:image-pos': 4,
2904 'u-boot-spl-dtb:offset': SECTION_SIZE,
2905 'u-boot-spl-dtb:size': DTB_SIZE,
2906 'u-boot-spl-dtb:image-pos': SECTION_SIZE,
2907 'u-boot-tpl-dtb:offset': SECTION_SIZE + DTB_SIZE,
2908 'u-boot-tpl-dtb:image-pos': SECTION_SIZE + DTB_SIZE,
2909 'u-boot-tpl-dtb:size': DTB_SIZE,
2910 'fdtmap:offset': SECTION_SIZE + DTB_SIZE * 2,
2911 'fdtmap:size': FDTMAP_SIZE,
2912 'fdtmap:image-pos': SECTION_SIZE + DTB_SIZE * 2,
2913 }
2914 main_expected = {
2915 'section:orig-size': SECTION_SIZE,
2916 'section/u-boot-dtb:orig-offset': 4,
2917 }
2918
2919 # We expect three device-tree files in the output, with the first one
2920 # within a fixed-size section.
2921 # Read them in sequence. We look for an 'spl' property in the SPL tree,
2922 # and 'tpl' in the TPL tree, to make sure they are distinct from the
2923 # main U-Boot tree. All three should have the same positions and offset
2924 # except that the main tree should include the main_expected properties
2925 start = 4
2926 for item in ['', 'spl', 'tpl', None]:
2927 if item is None:
2928 start += 16 # Move past fdtmap header
2929 dtb = fdt.Fdt.FromData(data[start:])
2930 dtb.Scan()
2931 props = self._GetPropTree(dtb,
2932 BASE_DTB_PROPS + REPACK_DTB_PROPS + ['spl', 'tpl'],
2933 prefix='/' if item is None else '/binman/')
2934 expected = dict(base_expected)
2935 if item:
2936 expected[item] = 0
2937 else:
2938 # Main DTB and fdtdec should include the 'orig-' properties
2939 expected.update(main_expected)
2940 # Helpful for debugging:
2941 #for prop in sorted(props):
2942 #print('prop %s %s %s' % (prop, props[prop], expected[prop]))
2943 self.assertEqual(expected, props)
2944 if item == '':
2945 start = SECTION_SIZE
2946 else:
2947 start += dtb._fdt_obj.totalsize()
2948
Simon Glasseba1f0c2019-07-20 12:23:55 -06002949 def testFdtmapHeaderMiddle(self):
2950 """Test an FDT map in the middle of an image when it should be at end"""
2951 with self.assertRaises(ValueError) as e:
2952 self._DoReadFileRealDtb('135_fdtmap_hdr_middle.dts')
2953 self.assertIn("Invalid sibling order 'middle' for image-header: Must be at 'end' to match location",
2954 str(e.exception))
2955
2956 def testFdtmapHeaderStartBad(self):
2957 """Test an FDT map in middle of an image when it should be at start"""
2958 with self.assertRaises(ValueError) as e:
2959 self._DoReadFileRealDtb('136_fdtmap_hdr_startbad.dts')
2960 self.assertIn("Invalid sibling order 'end' for image-header: Must be at 'start' to match location",
2961 str(e.exception))
2962
2963 def testFdtmapHeaderEndBad(self):
2964 """Test an FDT map at the start of an image when it should be at end"""
2965 with self.assertRaises(ValueError) as e:
2966 self._DoReadFileRealDtb('137_fdtmap_hdr_endbad.dts')
2967 self.assertIn("Invalid sibling order 'start' for image-header: Must be at 'end' to match location",
2968 str(e.exception))
2969
2970 def testFdtmapHeaderNoSize(self):
2971 """Test an image header at the end of an image with undefined size"""
2972 self._DoReadFileRealDtb('138_fdtmap_hdr_nosize.dts')
2973
Simon Glass51014aa2019-07-20 12:23:56 -06002974 def testReplaceResize(self):
2975 """Test replacing a single file in an entry with a larger file"""
2976 expected = U_BOOT_DATA + b'x'
2977 data, _, image = self._RunReplaceCmd('u-boot', expected,
2978 dts='139_replace_repack.dts')
2979 self.assertEqual(expected, data)
2980
2981 entries = image.GetEntries()
2982 dtb_data = entries['u-boot-dtb'].data
2983 dtb = fdt.Fdt.FromData(dtb_data)
2984 dtb.Scan()
2985
2986 # The u-boot section should now be larger in the dtb
2987 node = dtb.GetNode('/binman/u-boot')
2988 self.assertEqual(len(expected), fdt_util.GetInt(node, 'size'))
2989
2990 # Same for the fdtmap
2991 fdata = entries['fdtmap'].data
2992 fdtb = fdt.Fdt.FromData(fdata[fdtmap.FDTMAP_HDR_LEN:])
2993 fdtb.Scan()
2994 fnode = fdtb.GetNode('/u-boot')
2995 self.assertEqual(len(expected), fdt_util.GetInt(fnode, 'size'))
2996
2997 def testReplaceResizeNoRepack(self):
2998 """Test replacing an entry with a larger file when not allowed"""
2999 expected = U_BOOT_DATA + b'x'
3000 with self.assertRaises(ValueError) as e:
3001 self._RunReplaceCmd('u-boot', expected)
3002 self.assertIn('Entry data size does not match, but allow-repack is not present for this image',
3003 str(e.exception))
3004
Simon Glass61ec04f2019-07-20 12:23:58 -06003005 def testEntryShrink(self):
3006 """Test contracting an entry after it is packed"""
3007 try:
3008 state.SetAllowEntryContraction(True)
3009 data = self._DoReadFileDtb('140_entry_shrink.dts',
3010 update_dtb=True)[0]
3011 finally:
3012 state.SetAllowEntryContraction(False)
3013 self.assertEqual(b'a', data[:1])
3014 self.assertEqual(U_BOOT_DATA, data[1:1 + len(U_BOOT_DATA)])
3015 self.assertEqual(b'a', data[-1:])
3016
3017 def testEntryShrinkFail(self):
3018 """Test not being allowed to contract an entry after it is packed"""
3019 data = self._DoReadFileDtb('140_entry_shrink.dts', update_dtb=True)[0]
3020
3021 # In this case there is a spare byte at the end of the data. The size of
3022 # the contents is only 1 byte but we still have the size before it
3023 # shrunk.
3024 self.assertEqual(b'a\0', data[:2])
3025 self.assertEqual(U_BOOT_DATA, data[2:2 + len(U_BOOT_DATA)])
3026 self.assertEqual(b'a\0', data[-2:])
3027
Simon Glass27145fd2019-07-20 12:24:01 -06003028 def testDescriptorOffset(self):
3029 """Test that the Intel descriptor is always placed at at the start"""
3030 data = self._DoReadFileDtb('141_descriptor_offset.dts')
3031 image = control.images['image']
3032 entries = image.GetEntries()
3033 desc = entries['intel-descriptor']
3034 self.assertEqual(0xff800000, desc.offset);
3035 self.assertEqual(0xff800000, desc.image_pos);
3036
Simon Glasseb0f4a42019-07-20 12:24:06 -06003037 def testReplaceCbfs(self):
3038 """Test replacing a single file in CBFS without changing the size"""
3039 self._CheckLz4()
3040 expected = b'x' * len(U_BOOT_DATA)
3041 data = self._DoReadFileRealDtb('142_replace_cbfs.dts')
3042 updated_fname = tools.GetOutputFilename('image-updated.bin')
3043 tools.WriteFile(updated_fname, data)
3044 entry_name = 'section/cbfs/u-boot'
3045 control.WriteEntry(updated_fname, entry_name, expected,
3046 allow_resize=True)
3047 data = control.ReadEntry(updated_fname, entry_name)
3048 self.assertEqual(expected, data)
3049
3050 def testReplaceResizeCbfs(self):
3051 """Test replacing a single file in CBFS with one of a different size"""
3052 self._CheckLz4()
3053 expected = U_BOOT_DATA + b'x'
3054 data = self._DoReadFileRealDtb('142_replace_cbfs.dts')
3055 updated_fname = tools.GetOutputFilename('image-updated.bin')
3056 tools.WriteFile(updated_fname, data)
3057 entry_name = 'section/cbfs/u-boot'
3058 control.WriteEntry(updated_fname, entry_name, expected,
3059 allow_resize=True)
3060 data = control.ReadEntry(updated_fname, entry_name)
3061 self.assertEqual(expected, data)
3062
Simon Glassa6cb9952019-07-20 12:24:15 -06003063 def _SetupForReplace(self):
3064 """Set up some files to use to replace entries
3065
3066 This generates an image, copies it to a new file, extracts all the files
3067 in it and updates some of them
3068
3069 Returns:
3070 List
3071 Image filename
3072 Output directory
3073 Expected values for updated entries, each a string
3074 """
3075 data = self._DoReadFileRealDtb('143_replace_all.dts')
3076
3077 updated_fname = tools.GetOutputFilename('image-updated.bin')
3078 tools.WriteFile(updated_fname, data)
3079
3080 outdir = os.path.join(self._indir, 'extract')
3081 einfos = control.ExtractEntries(updated_fname, None, outdir, [])
3082
3083 expected1 = b'x' + U_BOOT_DATA + b'y'
3084 u_boot_fname1 = os.path.join(outdir, 'u-boot')
3085 tools.WriteFile(u_boot_fname1, expected1)
3086
3087 expected2 = b'a' + U_BOOT_DATA + b'b'
3088 u_boot_fname2 = os.path.join(outdir, 'u-boot2')
3089 tools.WriteFile(u_boot_fname2, expected2)
3090
3091 expected_text = b'not the same text'
3092 text_fname = os.path.join(outdir, 'text')
3093 tools.WriteFile(text_fname, expected_text)
3094
3095 dtb_fname = os.path.join(outdir, 'u-boot-dtb')
3096 dtb = fdt.FdtScan(dtb_fname)
3097 node = dtb.GetNode('/binman/text')
3098 node.AddString('my-property', 'the value')
3099 dtb.Sync(auto_resize=True)
3100 dtb.Flush()
3101
3102 return updated_fname, outdir, expected1, expected2, expected_text
3103
3104 def _CheckReplaceMultiple(self, entry_paths):
3105 """Handle replacing the contents of multiple entries
3106
3107 Args:
3108 entry_paths: List of entry paths to replace
3109
3110 Returns:
3111 List
3112 Dict of entries in the image:
3113 key: Entry name
3114 Value: Entry object
3115 Expected values for updated entries, each a string
3116 """
3117 updated_fname, outdir, expected1, expected2, expected_text = (
3118 self._SetupForReplace())
3119 control.ReplaceEntries(updated_fname, None, outdir, entry_paths)
3120
3121 image = Image.FromFile(updated_fname)
3122 image.LoadData()
3123 return image.GetEntries(), expected1, expected2, expected_text
3124
3125 def testReplaceAll(self):
3126 """Test replacing the contents of all entries"""
3127 entries, expected1, expected2, expected_text = (
3128 self._CheckReplaceMultiple([]))
3129 data = entries['u-boot'].data
3130 self.assertEqual(expected1, data)
3131
3132 data = entries['u-boot2'].data
3133 self.assertEqual(expected2, data)
3134
3135 data = entries['text'].data
3136 self.assertEqual(expected_text, data)
3137
3138 # Check that the device tree is updated
3139 data = entries['u-boot-dtb'].data
3140 dtb = fdt.Fdt.FromData(data)
3141 dtb.Scan()
3142 node = dtb.GetNode('/binman/text')
3143 self.assertEqual('the value', node.props['my-property'].value)
3144
3145 def testReplaceSome(self):
3146 """Test replacing the contents of a few entries"""
3147 entries, expected1, expected2, expected_text = (
3148 self._CheckReplaceMultiple(['u-boot2', 'text']))
3149
3150 # This one should not change
3151 data = entries['u-boot'].data
3152 self.assertEqual(U_BOOT_DATA, data)
3153
3154 data = entries['u-boot2'].data
3155 self.assertEqual(expected2, data)
3156
3157 data = entries['text'].data
3158 self.assertEqual(expected_text, data)
3159
3160 def testReplaceCmd(self):
3161 """Test replacing a file fron an image on the command line"""
3162 self._DoReadFileRealDtb('143_replace_all.dts')
3163
3164 try:
3165 tmpdir, updated_fname = self._SetupImageInTmpdir()
3166
3167 fname = os.path.join(tmpdir, 'update-u-boot.bin')
3168 expected = b'x' * len(U_BOOT_DATA)
3169 tools.WriteFile(fname, expected)
3170
3171 self._DoBinman('replace', '-i', updated_fname, 'u-boot', '-f', fname)
3172 data = tools.ReadFile(updated_fname)
3173 self.assertEqual(expected, data[:len(expected)])
3174 map_fname = os.path.join(tmpdir, 'image-updated.map')
3175 self.assertFalse(os.path.exists(map_fname))
3176 finally:
3177 shutil.rmtree(tmpdir)
3178
3179 def testReplaceCmdSome(self):
3180 """Test replacing some files fron an image on the command line"""
3181 updated_fname, outdir, expected1, expected2, expected_text = (
3182 self._SetupForReplace())
3183
3184 self._DoBinman('replace', '-i', updated_fname, '-I', outdir,
3185 'u-boot2', 'text')
3186
3187 tools.PrepareOutputDir(None)
3188 image = Image.FromFile(updated_fname)
3189 image.LoadData()
3190 entries = image.GetEntries()
3191
3192 # This one should not change
3193 data = entries['u-boot'].data
3194 self.assertEqual(U_BOOT_DATA, data)
3195
3196 data = entries['u-boot2'].data
3197 self.assertEqual(expected2, data)
3198
3199 data = entries['text'].data
3200 self.assertEqual(expected_text, data)
3201
3202 def testReplaceMissing(self):
3203 """Test replacing entries where the file is missing"""
3204 updated_fname, outdir, expected1, expected2, expected_text = (
3205 self._SetupForReplace())
3206
3207 # Remove one of the files, to generate a warning
3208 u_boot_fname1 = os.path.join(outdir, 'u-boot')
3209 os.remove(u_boot_fname1)
3210
3211 with test_util.capture_sys_output() as (stdout, stderr):
3212 control.ReplaceEntries(updated_fname, None, outdir, [])
3213 self.assertIn("Skipping entry '/u-boot' from missing file",
3214 stdout.getvalue())
3215
3216 def testReplaceCmdMap(self):
3217 """Test replacing a file fron an image on the command line"""
3218 self._DoReadFileRealDtb('143_replace_all.dts')
3219
3220 try:
3221 tmpdir, updated_fname = self._SetupImageInTmpdir()
3222
3223 fname = os.path.join(self._indir, 'update-u-boot.bin')
3224 expected = b'x' * len(U_BOOT_DATA)
3225 tools.WriteFile(fname, expected)
3226
3227 self._DoBinman('replace', '-i', updated_fname, 'u-boot',
3228 '-f', fname, '-m')
3229 map_fname = os.path.join(tmpdir, 'image-updated.map')
3230 self.assertTrue(os.path.exists(map_fname))
3231 finally:
3232 shutil.rmtree(tmpdir)
3233
3234 def testReplaceNoEntryPaths(self):
3235 """Test replacing an entry without an entry path"""
3236 self._DoReadFileRealDtb('143_replace_all.dts')
3237 image_fname = tools.GetOutputFilename('image.bin')
3238 with self.assertRaises(ValueError) as e:
3239 control.ReplaceEntries(image_fname, 'fname', None, [])
3240 self.assertIn('Must specify an entry path to read with -f',
3241 str(e.exception))
3242
3243 def testReplaceTooManyEntryPaths(self):
3244 """Test extracting some entries"""
3245 self._DoReadFileRealDtb('143_replace_all.dts')
3246 image_fname = tools.GetOutputFilename('image.bin')
3247 with self.assertRaises(ValueError) as e:
3248 control.ReplaceEntries(image_fname, 'fname', None, ['a', 'b'])
3249 self.assertIn('Must specify exactly one entry path to write with -f',
3250 str(e.exception))
3251
Simon Glass2250ee62019-08-24 07:22:48 -06003252 def testPackReset16(self):
3253 """Test that an image with an x86 reset16 region can be created"""
3254 data = self._DoReadFile('144_x86_reset16.dts')
3255 self.assertEqual(X86_RESET16_DATA, data[:len(X86_RESET16_DATA)])
3256
3257 def testPackReset16Spl(self):
3258 """Test that an image with an x86 reset16-spl region can be created"""
3259 data = self._DoReadFile('145_x86_reset16_spl.dts')
3260 self.assertEqual(X86_RESET16_SPL_DATA, data[:len(X86_RESET16_SPL_DATA)])
3261
3262 def testPackReset16Tpl(self):
3263 """Test that an image with an x86 reset16-tpl region can be created"""
3264 data = self._DoReadFile('146_x86_reset16_tpl.dts')
3265 self.assertEqual(X86_RESET16_TPL_DATA, data[:len(X86_RESET16_TPL_DATA)])
3266
Simon Glass12bb1a92019-07-20 12:23:51 -06003267
Simon Glass9fc60b42017-11-12 21:52:22 -07003268if __name__ == "__main__":
3269 unittest.main()