blob: c46a065382703d5546f06834b7a1b07755a9b9dd [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 Glasse0e5df92018-09-14 04:57:31 -06009import hashlib
Simon Glass4f443042016-11-25 20:15:52 -070010from optparse import OptionParser
11import os
12import shutil
13import struct
14import sys
15import tempfile
16import unittest
17
18import binman
19import cmdline
20import command
21import control
Simon Glass19790632017-11-13 18:55:01 -070022import elf
Simon Glass99ed4a22017-05-27 07:38:30 -060023import fdt
Simon Glass4f443042016-11-25 20:15:52 -070024import fdt_util
Simon Glass11e36cc2018-07-17 13:25:38 -060025import fmap_util
Simon Glassfd8d1f72018-07-17 13:25:36 -060026import test_util
Simon Glassc55a50f2018-09-14 04:57:19 -060027import state
Simon Glass4f443042016-11-25 20:15:52 -070028import tools
29import tout
30
31# Contents of test files, corresponding to different entry types
Simon Glass6b187df2017-11-12 21:52:27 -070032U_BOOT_DATA = '1234'
33U_BOOT_IMG_DATA = 'img'
Simon Glassf6898902017-11-13 18:54:59 -070034U_BOOT_SPL_DATA = '56780123456789abcde'
Simon Glassb8ef5b62018-07-17 13:25:48 -060035U_BOOT_TPL_DATA = 'tpl'
Simon Glass6b187df2017-11-12 21:52:27 -070036BLOB_DATA = '89'
37ME_DATA = '0abcd'
38VGA_DATA = 'vga'
39U_BOOT_DTB_DATA = 'udtb'
Simon Glass47419ea2017-11-13 18:54:55 -070040U_BOOT_SPL_DTB_DATA = 'spldtb'
Simon Glassb8ef5b62018-07-17 13:25:48 -060041U_BOOT_TPL_DTB_DATA = 'tpldtb'
Simon Glass6b187df2017-11-12 21:52:27 -070042X86_START16_DATA = 'start16'
43X86_START16_SPL_DATA = 'start16spl'
Simon Glass35b384c2018-09-14 04:57:10 -060044X86_START16_TPL_DATA = 'start16tpl'
Simon Glass6b187df2017-11-12 21:52:27 -070045U_BOOT_NODTB_DATA = 'nodtb with microcode pointer somewhere in here'
46U_BOOT_SPL_NODTB_DATA = 'splnodtb with microcode pointer somewhere in here'
47FSP_DATA = 'fsp'
48CMC_DATA = 'cmc'
49VBT_DATA = 'vbt'
Simon Glassca4f4ff2017-11-12 21:52:28 -070050MRC_DATA = 'mrc'
Simon Glassbb748372018-07-17 13:25:33 -060051TEXT_DATA = 'text'
52TEXT_DATA2 = 'text2'
53TEXT_DATA3 = 'text3'
Simon Glassec127af2018-07-17 13:25:39 -060054CROS_EC_RW_DATA = 'ecrw'
Simon Glass0ef87aa2018-07-17 13:25:44 -060055GBB_DATA = 'gbbd'
56BMPBLK_DATA = 'bmp'
Simon Glass24d0d3c2018-07-17 13:25:47 -060057VBLOCK_DATA = 'vblk'
Simon Glass0a98b282018-09-14 04:57:28 -060058FILES_DATA = ("sorry I'm late\nOh, don't bother apologising, I'm " +
59 "sorry you're alive\n")
Simon Glass83d73c22018-09-14 04:57:26 -060060COMPRESS_DATA = 'data to compress'
Simon Glassec127af2018-07-17 13:25:39 -060061
Simon Glass4f443042016-11-25 20:15:52 -070062
63class TestFunctional(unittest.TestCase):
64 """Functional tests for binman
65
66 Most of these use a sample .dts file to build an image and then check
67 that it looks correct. The sample files are in the test/ subdirectory
68 and are numbered.
69
70 For each entry type a very small test file is created using fixed
71 string contents. This makes it easy to test that things look right, and
72 debug problems.
73
74 In some cases a 'real' file must be used - these are also supplied in
75 the test/ diurectory.
76 """
77 @classmethod
78 def setUpClass(self):
Simon Glass4d5994f2017-11-12 21:52:20 -070079 global entry
80 import entry
81
Simon Glass4f443042016-11-25 20:15:52 -070082 # Handle the case where argv[0] is 'python'
83 self._binman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
84 self._binman_pathname = os.path.join(self._binman_dir, 'binman')
85
86 # Create a temporary directory for input files
87 self._indir = tempfile.mkdtemp(prefix='binmant.')
88
89 # Create some test files
90 TestFunctional._MakeInputFile('u-boot.bin', U_BOOT_DATA)
91 TestFunctional._MakeInputFile('u-boot.img', U_BOOT_IMG_DATA)
92 TestFunctional._MakeInputFile('spl/u-boot-spl.bin', U_BOOT_SPL_DATA)
Simon Glassb8ef5b62018-07-17 13:25:48 -060093 TestFunctional._MakeInputFile('tpl/u-boot-tpl.bin', U_BOOT_TPL_DATA)
Simon Glass4f443042016-11-25 20:15:52 -070094 TestFunctional._MakeInputFile('blobfile', BLOB_DATA)
Simon Glasse0ff8552016-11-25 20:15:53 -070095 TestFunctional._MakeInputFile('me.bin', ME_DATA)
96 TestFunctional._MakeInputFile('vga.bin', VGA_DATA)
Simon Glassb8ef5b62018-07-17 13:25:48 -060097 self._ResetDtbs()
Simon Glasse0ff8552016-11-25 20:15:53 -070098 TestFunctional._MakeInputFile('u-boot-x86-16bit.bin', X86_START16_DATA)
Simon Glass87722132017-11-12 21:52:26 -070099 TestFunctional._MakeInputFile('spl/u-boot-x86-16bit-spl.bin',
100 X86_START16_SPL_DATA)
Simon Glass35b384c2018-09-14 04:57:10 -0600101 TestFunctional._MakeInputFile('tpl/u-boot-x86-16bit-tpl.bin',
102 X86_START16_TPL_DATA)
Simon Glass4f443042016-11-25 20:15:52 -0700103 TestFunctional._MakeInputFile('u-boot-nodtb.bin', U_BOOT_NODTB_DATA)
Simon Glass6b187df2017-11-12 21:52:27 -0700104 TestFunctional._MakeInputFile('spl/u-boot-spl-nodtb.bin',
105 U_BOOT_SPL_NODTB_DATA)
Simon Glassda229092016-11-25 20:15:56 -0700106 TestFunctional._MakeInputFile('fsp.bin', FSP_DATA)
107 TestFunctional._MakeInputFile('cmc.bin', CMC_DATA)
Bin Meng59ea8c22017-08-15 22:41:54 -0700108 TestFunctional._MakeInputFile('vbt.bin', VBT_DATA)
Simon Glassca4f4ff2017-11-12 21:52:28 -0700109 TestFunctional._MakeInputFile('mrc.bin', MRC_DATA)
Simon Glassec127af2018-07-17 13:25:39 -0600110 TestFunctional._MakeInputFile('ecrw.bin', CROS_EC_RW_DATA)
Simon Glass0ef87aa2018-07-17 13:25:44 -0600111 TestFunctional._MakeInputDir('devkeys')
112 TestFunctional._MakeInputFile('bmpblk.bin', BMPBLK_DATA)
Simon Glass4f443042016-11-25 20:15:52 -0700113 self._output_setup = False
114
Simon Glasse0ff8552016-11-25 20:15:53 -0700115 # ELF file with a '_dt_ucode_base_size' symbol
116 with open(self.TestFile('u_boot_ucode_ptr')) as fd:
117 TestFunctional._MakeInputFile('u-boot', fd.read())
118
119 # Intel flash descriptor file
120 with open(self.TestFile('descriptor.bin')) as fd:
121 TestFunctional._MakeInputFile('descriptor.bin', fd.read())
122
Simon Glass0a98b282018-09-14 04:57:28 -0600123 shutil.copytree(self.TestFile('files'),
124 os.path.join(self._indir, 'files'))
125
Simon Glass83d73c22018-09-14 04:57:26 -0600126 TestFunctional._MakeInputFile('compress', COMPRESS_DATA)
127
Simon Glass4f443042016-11-25 20:15:52 -0700128 @classmethod
129 def tearDownClass(self):
130 """Remove the temporary input directory and its contents"""
131 if self._indir:
132 shutil.rmtree(self._indir)
133 self._indir = None
134
135 def setUp(self):
136 # Enable this to turn on debugging output
137 # tout.Init(tout.DEBUG)
138 command.test_result = None
139
140 def tearDown(self):
141 """Remove the temporary output directory"""
142 tools._FinaliseForTest()
143
Simon Glassb8ef5b62018-07-17 13:25:48 -0600144 @classmethod
145 def _ResetDtbs(self):
146 TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA)
147 TestFunctional._MakeInputFile('spl/u-boot-spl.dtb', U_BOOT_SPL_DTB_DATA)
148 TestFunctional._MakeInputFile('tpl/u-boot-tpl.dtb', U_BOOT_TPL_DTB_DATA)
149
Simon Glass4f443042016-11-25 20:15:52 -0700150 def _RunBinman(self, *args, **kwargs):
151 """Run binman using the command line
152
153 Args:
154 Arguments to pass, as a list of strings
155 kwargs: Arguments to pass to Command.RunPipe()
156 """
157 result = command.RunPipe([[self._binman_pathname] + list(args)],
158 capture=True, capture_stderr=True, raise_on_error=False)
159 if result.return_code and kwargs.get('raise_on_error', True):
160 raise Exception("Error running '%s': %s" % (' '.join(args),
161 result.stdout + result.stderr))
162 return result
163
164 def _DoBinman(self, *args):
165 """Run binman using directly (in the same process)
166
167 Args:
168 Arguments to pass, as a list of strings
169 Returns:
170 Return value (0 for success)
171 """
Simon Glass7fe91732017-11-13 18:55:00 -0700172 args = list(args)
173 if '-D' in sys.argv:
174 args = args + ['-D']
175 (options, args) = cmdline.ParseArgs(args)
Simon Glass4f443042016-11-25 20:15:52 -0700176 options.pager = 'binman-invalid-pager'
177 options.build_dir = self._indir
178
179 # For testing, you can force an increase in verbosity here
180 # options.verbosity = tout.DEBUG
181 return control.Binman(options, args)
182
Simon Glass53af22a2018-07-17 13:25:32 -0600183 def _DoTestFile(self, fname, debug=False, map=False, update_dtb=False,
Simon Glass93d17412018-09-14 04:57:23 -0600184 entry_args=None, images=None, use_real_dtb=False):
Simon Glass4f443042016-11-25 20:15:52 -0700185 """Run binman with a given test file
186
187 Args:
Simon Glass7ae5f312018-06-01 09:38:19 -0600188 fname: Device-tree source filename to use (e.g. 05_simple.dts)
189 debug: True to enable debugging output
Simon Glass3b0c3822018-06-01 09:38:20 -0600190 map: True to output map files for the images
Simon Glass3ab95982018-08-01 15:22:37 -0600191 update_dtb: Update the offset and size of each entry in the device
Simon Glass16b8d6b2018-07-06 10:27:42 -0600192 tree before packing it into the image
Simon Glass0bfa7b02018-09-14 04:57:12 -0600193 entry_args: Dict of entry args to supply to binman
194 key: arg name
195 value: value of that arg
196 images: List of image names to build
Simon Glass4f443042016-11-25 20:15:52 -0700197 """
Simon Glass7fe91732017-11-13 18:55:00 -0700198 args = ['-p', '-I', self._indir, '-d', self.TestFile(fname)]
199 if debug:
200 args.append('-D')
Simon Glass3b0c3822018-06-01 09:38:20 -0600201 if map:
202 args.append('-m')
Simon Glass16b8d6b2018-07-06 10:27:42 -0600203 if update_dtb:
204 args.append('-up')
Simon Glass93d17412018-09-14 04:57:23 -0600205 if not use_real_dtb:
206 args.append('--fake-dtb')
Simon Glass53af22a2018-07-17 13:25:32 -0600207 if entry_args:
208 for arg, value in entry_args.iteritems():
209 args.append('-a%s=%s' % (arg, value))
Simon Glass0bfa7b02018-09-14 04:57:12 -0600210 if images:
211 for image in images:
212 args += ['-i', image]
Simon Glass7fe91732017-11-13 18:55:00 -0700213 return self._DoBinman(*args)
Simon Glass4f443042016-11-25 20:15:52 -0700214
215 def _SetupDtb(self, fname, outfile='u-boot.dtb'):
Simon Glasse0ff8552016-11-25 20:15:53 -0700216 """Set up a new test device-tree file
217
218 The given file is compiled and set up as the device tree to be used
219 for ths test.
220
221 Args:
222 fname: Filename of .dts file to read
Simon Glass7ae5f312018-06-01 09:38:19 -0600223 outfile: Output filename for compiled device-tree binary
Simon Glasse0ff8552016-11-25 20:15:53 -0700224
225 Returns:
Simon Glass7ae5f312018-06-01 09:38:19 -0600226 Contents of device-tree binary
Simon Glasse0ff8552016-11-25 20:15:53 -0700227 """
Simon Glass4f443042016-11-25 20:15:52 -0700228 if not self._output_setup:
229 tools.PrepareOutputDir(self._indir, True)
230 self._output_setup = True
231 dtb = fdt_util.EnsureCompiled(self.TestFile(fname))
232 with open(dtb) as fd:
233 data = fd.read()
234 TestFunctional._MakeInputFile(outfile, data)
Simon Glasse0ff8552016-11-25 20:15:53 -0700235 return data
Simon Glass4f443042016-11-25 20:15:52 -0700236
Simon Glass6ed45ba2018-09-14 04:57:24 -0600237 def _GetDtbContentsForSplTpl(self, dtb_data, name):
238 """Create a version of the main DTB for SPL or SPL
239
240 For testing we don't actually have different versions of the DTB. With
241 U-Boot we normally run fdtgrep to remove unwanted nodes, but for tests
242 we don't normally have any unwanted nodes.
243
244 We still want the DTBs for SPL and TPL to be different though, since
245 otherwise it is confusing to know which one we are looking at. So add
246 an 'spl' or 'tpl' property to the top-level node.
247 """
248 dtb = fdt.Fdt.FromData(dtb_data)
249 dtb.Scan()
250 dtb.GetNode('/binman').AddZeroProp(name)
251 dtb.Sync(auto_resize=True)
252 dtb.Pack()
253 return dtb.GetContents()
254
Simon Glass16b8d6b2018-07-06 10:27:42 -0600255 def _DoReadFileDtb(self, fname, use_real_dtb=False, map=False,
Simon Glass6ed45ba2018-09-14 04:57:24 -0600256 update_dtb=False, entry_args=None, reset_dtbs=True):
Simon Glass4f443042016-11-25 20:15:52 -0700257 """Run binman and return the resulting image
258
259 This runs binman with a given test file and then reads the resulting
260 output file. It is a shortcut function since most tests need to do
261 these steps.
262
263 Raises an assertion failure if binman returns a non-zero exit code.
264
265 Args:
Simon Glass7ae5f312018-06-01 09:38:19 -0600266 fname: Device-tree source filename to use (e.g. 05_simple.dts)
Simon Glass4f443042016-11-25 20:15:52 -0700267 use_real_dtb: True to use the test file as the contents of
268 the u-boot-dtb entry. Normally this is not needed and the
269 test contents (the U_BOOT_DTB_DATA string) can be used.
270 But in some test we need the real contents.
Simon Glass3b0c3822018-06-01 09:38:20 -0600271 map: True to output map files for the images
Simon Glass3ab95982018-08-01 15:22:37 -0600272 update_dtb: Update the offset and size of each entry in the device
Simon Glass16b8d6b2018-07-06 10:27:42 -0600273 tree before packing it into the image
Simon Glasse0ff8552016-11-25 20:15:53 -0700274
275 Returns:
276 Tuple:
277 Resulting image contents
278 Device tree contents
Simon Glass3b0c3822018-06-01 09:38:20 -0600279 Map data showing contents of image (or None if none)
Simon Glassea6922e2018-07-17 13:25:27 -0600280 Output device tree binary filename ('u-boot.dtb' path)
Simon Glass4f443042016-11-25 20:15:52 -0700281 """
Simon Glasse0ff8552016-11-25 20:15:53 -0700282 dtb_data = None
Simon Glass4f443042016-11-25 20:15:52 -0700283 # Use the compiled test file as the u-boot-dtb input
284 if use_real_dtb:
Simon Glasse0ff8552016-11-25 20:15:53 -0700285 dtb_data = self._SetupDtb(fname)
Simon Glass6ed45ba2018-09-14 04:57:24 -0600286 infile = os.path.join(self._indir, 'u-boot.dtb')
287
288 # For testing purposes, make a copy of the DT for SPL and TPL. Add
289 # a node indicating which it is, so aid verification.
290 for name in ['spl', 'tpl']:
291 dtb_fname = '%s/u-boot-%s.dtb' % (name, name)
292 outfile = os.path.join(self._indir, dtb_fname)
293 TestFunctional._MakeInputFile(dtb_fname,
294 self._GetDtbContentsForSplTpl(dtb_data, name))
Simon Glass4f443042016-11-25 20:15:52 -0700295
296 try:
Simon Glass53af22a2018-07-17 13:25:32 -0600297 retcode = self._DoTestFile(fname, map=map, update_dtb=update_dtb,
Simon Glass6ed45ba2018-09-14 04:57:24 -0600298 entry_args=entry_args, use_real_dtb=use_real_dtb)
Simon Glass4f443042016-11-25 20:15:52 -0700299 self.assertEqual(0, retcode)
Simon Glass6ed45ba2018-09-14 04:57:24 -0600300 out_dtb_fname = tools.GetOutputFilename('u-boot.dtb.out')
Simon Glass4f443042016-11-25 20:15:52 -0700301
302 # Find the (only) image, read it and return its contents
303 image = control.images['image']
Simon Glass16b8d6b2018-07-06 10:27:42 -0600304 image_fname = tools.GetOutputFilename('image.bin')
305 self.assertTrue(os.path.exists(image_fname))
Simon Glass3b0c3822018-06-01 09:38:20 -0600306 if map:
307 map_fname = tools.GetOutputFilename('image.map')
308 with open(map_fname) as fd:
309 map_data = fd.read()
310 else:
311 map_data = None
Simon Glass16b8d6b2018-07-06 10:27:42 -0600312 with open(image_fname) as fd:
313 return fd.read(), dtb_data, map_data, out_dtb_fname
Simon Glass4f443042016-11-25 20:15:52 -0700314 finally:
315 # Put the test file back
Simon Glass6ed45ba2018-09-14 04:57:24 -0600316 if reset_dtbs and use_real_dtb:
Simon Glassb8ef5b62018-07-17 13:25:48 -0600317 self._ResetDtbs()
Simon Glass4f443042016-11-25 20:15:52 -0700318
Simon Glasse0ff8552016-11-25 20:15:53 -0700319 def _DoReadFile(self, fname, use_real_dtb=False):
Simon Glass7ae5f312018-06-01 09:38:19 -0600320 """Helper function which discards the device-tree binary
321
322 Args:
323 fname: Device-tree source filename to use (e.g. 05_simple.dts)
324 use_real_dtb: True to use the test file as the contents of
325 the u-boot-dtb entry. Normally this is not needed and the
326 test contents (the U_BOOT_DTB_DATA string) can be used.
327 But in some test we need the real contents.
Simon Glassea6922e2018-07-17 13:25:27 -0600328
329 Returns:
330 Resulting image contents
Simon Glass7ae5f312018-06-01 09:38:19 -0600331 """
Simon Glasse0ff8552016-11-25 20:15:53 -0700332 return self._DoReadFileDtb(fname, use_real_dtb)[0]
333
Simon Glass4f443042016-11-25 20:15:52 -0700334 @classmethod
335 def _MakeInputFile(self, fname, contents):
336 """Create a new test input file, creating directories as needed
337
338 Args:
Simon Glass3ab95982018-08-01 15:22:37 -0600339 fname: Filename to create
Simon Glass4f443042016-11-25 20:15:52 -0700340 contents: File contents to write in to the file
341 Returns:
342 Full pathname of file created
343 """
344 pathname = os.path.join(self._indir, fname)
345 dirname = os.path.dirname(pathname)
346 if dirname and not os.path.exists(dirname):
347 os.makedirs(dirname)
348 with open(pathname, 'wb') as fd:
349 fd.write(contents)
350 return pathname
351
352 @classmethod
Simon Glass0ef87aa2018-07-17 13:25:44 -0600353 def _MakeInputDir(self, dirname):
354 """Create a new test input directory, creating directories as needed
355
356 Args:
357 dirname: Directory name to create
358
359 Returns:
360 Full pathname of directory created
361 """
362 pathname = os.path.join(self._indir, dirname)
363 if not os.path.exists(pathname):
364 os.makedirs(pathname)
365 return pathname
366
367 @classmethod
Simon Glass4f443042016-11-25 20:15:52 -0700368 def TestFile(self, fname):
369 return os.path.join(self._binman_dir, 'test', fname)
370
371 def AssertInList(self, grep_list, target):
372 """Assert that at least one of a list of things is in a target
373
374 Args:
375 grep_list: List of strings to check
376 target: Target string
377 """
378 for grep in grep_list:
379 if grep in target:
380 return
381 self.fail("Error: '%' not found in '%s'" % (grep_list, target))
382
383 def CheckNoGaps(self, entries):
384 """Check that all entries fit together without gaps
385
386 Args:
387 entries: List of entries to check
388 """
Simon Glass3ab95982018-08-01 15:22:37 -0600389 offset = 0
Simon Glass4f443042016-11-25 20:15:52 -0700390 for entry in entries.values():
Simon Glass3ab95982018-08-01 15:22:37 -0600391 self.assertEqual(offset, entry.offset)
392 offset += entry.size
Simon Glass4f443042016-11-25 20:15:52 -0700393
Simon Glasse0ff8552016-11-25 20:15:53 -0700394 def GetFdtLen(self, dtb):
Simon Glass7ae5f312018-06-01 09:38:19 -0600395 """Get the totalsize field from a device-tree binary
Simon Glasse0ff8552016-11-25 20:15:53 -0700396
397 Args:
Simon Glass7ae5f312018-06-01 09:38:19 -0600398 dtb: Device-tree binary contents
Simon Glasse0ff8552016-11-25 20:15:53 -0700399
400 Returns:
Simon Glass7ae5f312018-06-01 09:38:19 -0600401 Total size of device-tree binary, from the header
Simon Glasse0ff8552016-11-25 20:15:53 -0700402 """
403 return struct.unpack('>L', dtb[4:8])[0]
404
Simon Glasscee02e62018-07-17 13:25:52 -0600405 def _GetPropTree(self, dtb, prop_names):
Simon Glass16b8d6b2018-07-06 10:27:42 -0600406 def AddNode(node, path):
407 if node.name != '/':
408 path += '/' + node.name
Simon Glass16b8d6b2018-07-06 10:27:42 -0600409 for subnode in node.subnodes:
410 for prop in subnode.props.values():
Simon Glasscee02e62018-07-17 13:25:52 -0600411 if prop.name in prop_names:
Simon Glass16b8d6b2018-07-06 10:27:42 -0600412 prop_path = path + '/' + subnode.name + ':' + prop.name
413 tree[prop_path[len('/binman/'):]] = fdt_util.fdt32_to_cpu(
414 prop.value)
Simon Glass16b8d6b2018-07-06 10:27:42 -0600415 AddNode(subnode, path)
416
417 tree = {}
Simon Glass16b8d6b2018-07-06 10:27:42 -0600418 AddNode(dtb.GetRoot(), '')
419 return tree
420
Simon Glass4f443042016-11-25 20:15:52 -0700421 def testRun(self):
422 """Test a basic run with valid args"""
423 result = self._RunBinman('-h')
424
425 def testFullHelp(self):
426 """Test that the full help is displayed with -H"""
427 result = self._RunBinman('-H')
428 help_file = os.path.join(self._binman_dir, 'README')
Tom Rini3759df02018-01-16 15:29:50 -0500429 # Remove possible extraneous strings
430 extra = '::::::::::::::\n' + help_file + '\n::::::::::::::\n'
431 gothelp = result.stdout.replace(extra, '')
432 self.assertEqual(len(gothelp), os.path.getsize(help_file))
Simon Glass4f443042016-11-25 20:15:52 -0700433 self.assertEqual(0, len(result.stderr))
434 self.assertEqual(0, result.return_code)
435
436 def testFullHelpInternal(self):
437 """Test that the full help is displayed with -H"""
438 try:
439 command.test_result = command.CommandResult()
440 result = self._DoBinman('-H')
441 help_file = os.path.join(self._binman_dir, 'README')
442 finally:
443 command.test_result = None
444
445 def testHelp(self):
446 """Test that the basic help is displayed with -h"""
447 result = self._RunBinman('-h')
448 self.assertTrue(len(result.stdout) > 200)
449 self.assertEqual(0, len(result.stderr))
450 self.assertEqual(0, result.return_code)
451
Simon Glass4f443042016-11-25 20:15:52 -0700452 def testBoard(self):
453 """Test that we can run it with a specific board"""
454 self._SetupDtb('05_simple.dts', 'sandbox/u-boot.dtb')
455 TestFunctional._MakeInputFile('sandbox/u-boot.bin', U_BOOT_DATA)
456 result = self._DoBinman('-b', 'sandbox')
457 self.assertEqual(0, result)
458
459 def testNeedBoard(self):
460 """Test that we get an error when no board ius supplied"""
461 with self.assertRaises(ValueError) as e:
462 result = self._DoBinman()
463 self.assertIn("Must provide a board to process (use -b <board>)",
464 str(e.exception))
465
466 def testMissingDt(self):
Simon Glass7ae5f312018-06-01 09:38:19 -0600467 """Test that an invalid device-tree file generates an error"""
Simon Glass4f443042016-11-25 20:15:52 -0700468 with self.assertRaises(Exception) as e:
469 self._RunBinman('-d', 'missing_file')
470 # We get one error from libfdt, and a different one from fdtget.
471 self.AssertInList(["Couldn't open blob from 'missing_file'",
472 'No such file or directory'], str(e.exception))
473
474 def testBrokenDt(self):
Simon Glass7ae5f312018-06-01 09:38:19 -0600475 """Test that an invalid device-tree source file generates an error
Simon Glass4f443042016-11-25 20:15:52 -0700476
477 Since this is a source file it should be compiled and the error
478 will come from the device-tree compiler (dtc).
479 """
480 with self.assertRaises(Exception) as e:
481 self._RunBinman('-d', self.TestFile('01_invalid.dts'))
482 self.assertIn("FATAL ERROR: Unable to parse input tree",
483 str(e.exception))
484
485 def testMissingNode(self):
486 """Test that a device tree without a 'binman' node generates an error"""
487 with self.assertRaises(Exception) as e:
488 self._DoBinman('-d', self.TestFile('02_missing_node.dts'))
489 self.assertIn("does not have a 'binman' node", str(e.exception))
490
491 def testEmpty(self):
492 """Test that an empty binman node works OK (i.e. does nothing)"""
493 result = self._RunBinman('-d', self.TestFile('03_empty.dts'))
494 self.assertEqual(0, len(result.stderr))
495 self.assertEqual(0, result.return_code)
496
497 def testInvalidEntry(self):
498 """Test that an invalid entry is flagged"""
499 with self.assertRaises(Exception) as e:
500 result = self._RunBinman('-d',
501 self.TestFile('04_invalid_entry.dts'))
Simon Glass4f443042016-11-25 20:15:52 -0700502 self.assertIn("Unknown entry type 'not-a-valid-type' in node "
503 "'/binman/not-a-valid-type'", str(e.exception))
504
505 def testSimple(self):
506 """Test a simple binman with a single file"""
507 data = self._DoReadFile('05_simple.dts')
508 self.assertEqual(U_BOOT_DATA, data)
509
Simon Glass7fe91732017-11-13 18:55:00 -0700510 def testSimpleDebug(self):
511 """Test a simple binman run with debugging enabled"""
512 data = self._DoTestFile('05_simple.dts', debug=True)
513
Simon Glass4f443042016-11-25 20:15:52 -0700514 def testDual(self):
515 """Test that we can handle creating two images
516
517 This also tests image padding.
518 """
519 retcode = self._DoTestFile('06_dual_image.dts')
520 self.assertEqual(0, retcode)
521
522 image = control.images['image1']
523 self.assertEqual(len(U_BOOT_DATA), image._size)
524 fname = tools.GetOutputFilename('image1.bin')
525 self.assertTrue(os.path.exists(fname))
526 with open(fname) as fd:
527 data = fd.read()
528 self.assertEqual(U_BOOT_DATA, data)
529
530 image = control.images['image2']
531 self.assertEqual(3 + len(U_BOOT_DATA) + 5, image._size)
532 fname = tools.GetOutputFilename('image2.bin')
533 self.assertTrue(os.path.exists(fname))
534 with open(fname) as fd:
535 data = fd.read()
536 self.assertEqual(U_BOOT_DATA, data[3:7])
537 self.assertEqual(chr(0) * 3, data[:3])
538 self.assertEqual(chr(0) * 5, data[7:])
539
540 def testBadAlign(self):
541 """Test that an invalid alignment value is detected"""
542 with self.assertRaises(ValueError) as e:
543 self._DoTestFile('07_bad_align.dts')
544 self.assertIn("Node '/binman/u-boot': Alignment 23 must be a power "
545 "of two", str(e.exception))
546
547 def testPackSimple(self):
548 """Test that packing works as expected"""
549 retcode = self._DoTestFile('08_pack.dts')
550 self.assertEqual(0, retcode)
551 self.assertIn('image', control.images)
552 image = control.images['image']
Simon Glass8f1da502018-06-01 09:38:12 -0600553 entries = image.GetEntries()
Simon Glass4f443042016-11-25 20:15:52 -0700554 self.assertEqual(5, len(entries))
555
556 # First u-boot
557 self.assertIn('u-boot', entries)
558 entry = entries['u-boot']
Simon Glass3ab95982018-08-01 15:22:37 -0600559 self.assertEqual(0, entry.offset)
Simon Glass4f443042016-11-25 20:15:52 -0700560 self.assertEqual(len(U_BOOT_DATA), entry.size)
561
562 # Second u-boot, aligned to 16-byte boundary
563 self.assertIn('u-boot-align', entries)
564 entry = entries['u-boot-align']
Simon Glass3ab95982018-08-01 15:22:37 -0600565 self.assertEqual(16, entry.offset)
Simon Glass4f443042016-11-25 20:15:52 -0700566 self.assertEqual(len(U_BOOT_DATA), entry.size)
567
568 # Third u-boot, size 23 bytes
569 self.assertIn('u-boot-size', entries)
570 entry = entries['u-boot-size']
Simon Glass3ab95982018-08-01 15:22:37 -0600571 self.assertEqual(20, entry.offset)
Simon Glass4f443042016-11-25 20:15:52 -0700572 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
573 self.assertEqual(23, entry.size)
574
575 # Fourth u-boot, placed immediate after the above
576 self.assertIn('u-boot-next', entries)
577 entry = entries['u-boot-next']
Simon Glass3ab95982018-08-01 15:22:37 -0600578 self.assertEqual(43, entry.offset)
Simon Glass4f443042016-11-25 20:15:52 -0700579 self.assertEqual(len(U_BOOT_DATA), entry.size)
580
Simon Glass3ab95982018-08-01 15:22:37 -0600581 # Fifth u-boot, placed at a fixed offset
Simon Glass4f443042016-11-25 20:15:52 -0700582 self.assertIn('u-boot-fixed', entries)
583 entry = entries['u-boot-fixed']
Simon Glass3ab95982018-08-01 15:22:37 -0600584 self.assertEqual(61, entry.offset)
Simon Glass4f443042016-11-25 20:15:52 -0700585 self.assertEqual(len(U_BOOT_DATA), entry.size)
586
587 self.assertEqual(65, image._size)
588
589 def testPackExtra(self):
590 """Test that extra packing feature works as expected"""
591 retcode = self._DoTestFile('09_pack_extra.dts')
592
593 self.assertEqual(0, retcode)
594 self.assertIn('image', control.images)
595 image = control.images['image']
Simon Glass8f1da502018-06-01 09:38:12 -0600596 entries = image.GetEntries()
Simon Glass4f443042016-11-25 20:15:52 -0700597 self.assertEqual(5, len(entries))
598
599 # First u-boot with padding before and after
600 self.assertIn('u-boot', entries)
601 entry = entries['u-boot']
Simon Glass3ab95982018-08-01 15:22:37 -0600602 self.assertEqual(0, entry.offset)
Simon Glass4f443042016-11-25 20:15:52 -0700603 self.assertEqual(3, entry.pad_before)
604 self.assertEqual(3 + 5 + len(U_BOOT_DATA), entry.size)
605
606 # Second u-boot has an aligned size, but it has no effect
607 self.assertIn('u-boot-align-size-nop', entries)
608 entry = entries['u-boot-align-size-nop']
Simon Glass3ab95982018-08-01 15:22:37 -0600609 self.assertEqual(12, entry.offset)
Simon Glass4f443042016-11-25 20:15:52 -0700610 self.assertEqual(4, entry.size)
611
612 # Third u-boot has an aligned size too
613 self.assertIn('u-boot-align-size', entries)
614 entry = entries['u-boot-align-size']
Simon Glass3ab95982018-08-01 15:22:37 -0600615 self.assertEqual(16, entry.offset)
Simon Glass4f443042016-11-25 20:15:52 -0700616 self.assertEqual(32, entry.size)
617
618 # Fourth u-boot has an aligned end
619 self.assertIn('u-boot-align-end', entries)
620 entry = entries['u-boot-align-end']
Simon Glass3ab95982018-08-01 15:22:37 -0600621 self.assertEqual(48, entry.offset)
Simon Glass4f443042016-11-25 20:15:52 -0700622 self.assertEqual(16, entry.size)
623
624 # Fifth u-boot immediately afterwards
625 self.assertIn('u-boot-align-both', entries)
626 entry = entries['u-boot-align-both']
Simon Glass3ab95982018-08-01 15:22:37 -0600627 self.assertEqual(64, entry.offset)
Simon Glass4f443042016-11-25 20:15:52 -0700628 self.assertEqual(64, entry.size)
629
630 self.CheckNoGaps(entries)
631 self.assertEqual(128, image._size)
632
633 def testPackAlignPowerOf2(self):
634 """Test that invalid entry alignment is detected"""
635 with self.assertRaises(ValueError) as e:
636 self._DoTestFile('10_pack_align_power2.dts')
637 self.assertIn("Node '/binman/u-boot': Alignment 5 must be a power "
638 "of two", str(e.exception))
639
640 def testPackAlignSizePowerOf2(self):
641 """Test that invalid entry size alignment is detected"""
642 with self.assertRaises(ValueError) as e:
643 self._DoTestFile('11_pack_align_size_power2.dts')
644 self.assertIn("Node '/binman/u-boot': Alignment size 55 must be a "
645 "power of two", str(e.exception))
646
647 def testPackInvalidAlign(self):
Simon Glass3ab95982018-08-01 15:22:37 -0600648 """Test detection of an offset that does not match its alignment"""
Simon Glass4f443042016-11-25 20:15:52 -0700649 with self.assertRaises(ValueError) as e:
650 self._DoTestFile('12_pack_inv_align.dts')
Simon Glass3ab95982018-08-01 15:22:37 -0600651 self.assertIn("Node '/binman/u-boot': Offset 0x5 (5) does not match "
Simon Glass4f443042016-11-25 20:15:52 -0700652 "align 0x4 (4)", str(e.exception))
653
654 def testPackInvalidSizeAlign(self):
655 """Test that invalid entry size alignment is detected"""
656 with self.assertRaises(ValueError) as e:
657 self._DoTestFile('13_pack_inv_size_align.dts')
658 self.assertIn("Node '/binman/u-boot': Size 0x5 (5) does not match "
659 "align-size 0x4 (4)", str(e.exception))
660
661 def testPackOverlap(self):
662 """Test that overlapping regions are detected"""
663 with self.assertRaises(ValueError) as e:
664 self._DoTestFile('14_pack_overlap.dts')
Simon Glass3ab95982018-08-01 15:22:37 -0600665 self.assertIn("Node '/binman/u-boot-align': Offset 0x3 (3) overlaps "
Simon Glass4f443042016-11-25 20:15:52 -0700666 "with previous entry '/binman/u-boot' ending at 0x4 (4)",
667 str(e.exception))
668
669 def testPackEntryOverflow(self):
670 """Test that entries that overflow their size are detected"""
671 with self.assertRaises(ValueError) as e:
672 self._DoTestFile('15_pack_overflow.dts')
673 self.assertIn("Node '/binman/u-boot': Entry contents size is 0x4 (4) "
674 "but entry size is 0x3 (3)", str(e.exception))
675
676 def testPackImageOverflow(self):
677 """Test that entries which overflow the image size are detected"""
678 with self.assertRaises(ValueError) as e:
679 self._DoTestFile('16_pack_image_overflow.dts')
Simon Glass8f1da502018-06-01 09:38:12 -0600680 self.assertIn("Section '/binman': contents size 0x4 (4) exceeds section "
Simon Glass4f443042016-11-25 20:15:52 -0700681 "size 0x3 (3)", str(e.exception))
682
683 def testPackImageSize(self):
684 """Test that the image size can be set"""
685 retcode = self._DoTestFile('17_pack_image_size.dts')
686 self.assertEqual(0, retcode)
687 self.assertIn('image', control.images)
688 image = control.images['image']
689 self.assertEqual(7, image._size)
690
691 def testPackImageSizeAlign(self):
692 """Test that image size alignemnt works as expected"""
693 retcode = self._DoTestFile('18_pack_image_align.dts')
694 self.assertEqual(0, retcode)
695 self.assertIn('image', control.images)
696 image = control.images['image']
697 self.assertEqual(16, image._size)
698
699 def testPackInvalidImageAlign(self):
700 """Test that invalid image alignment is detected"""
701 with self.assertRaises(ValueError) as e:
702 self._DoTestFile('19_pack_inv_image_align.dts')
Simon Glass8f1da502018-06-01 09:38:12 -0600703 self.assertIn("Section '/binman': Size 0x7 (7) does not match "
Simon Glass4f443042016-11-25 20:15:52 -0700704 "align-size 0x8 (8)", str(e.exception))
705
706 def testPackAlignPowerOf2(self):
707 """Test that invalid image alignment is detected"""
708 with self.assertRaises(ValueError) as e:
709 self._DoTestFile('20_pack_inv_image_align_power2.dts')
Simon Glass8f1da502018-06-01 09:38:12 -0600710 self.assertIn("Section '/binman': Alignment size 131 must be a power of "
Simon Glass4f443042016-11-25 20:15:52 -0700711 "two", str(e.exception))
712
713 def testImagePadByte(self):
714 """Test that the image pad byte can be specified"""
Simon Glass19790632017-11-13 18:55:01 -0700715 with open(self.TestFile('bss_data')) as fd:
716 TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
Simon Glass4f443042016-11-25 20:15:52 -0700717 data = self._DoReadFile('21_image_pad.dts')
Simon Glassf6898902017-11-13 18:54:59 -0700718 self.assertEqual(U_BOOT_SPL_DATA + (chr(0xff) * 1) + U_BOOT_DATA, data)
Simon Glass4f443042016-11-25 20:15:52 -0700719
720 def testImageName(self):
721 """Test that image files can be named"""
722 retcode = self._DoTestFile('22_image_name.dts')
723 self.assertEqual(0, retcode)
724 image = control.images['image1']
725 fname = tools.GetOutputFilename('test-name')
726 self.assertTrue(os.path.exists(fname))
727
728 image = control.images['image2']
729 fname = tools.GetOutputFilename('test-name.xx')
730 self.assertTrue(os.path.exists(fname))
731
732 def testBlobFilename(self):
733 """Test that generic blobs can be provided by filename"""
734 data = self._DoReadFile('23_blob.dts')
735 self.assertEqual(BLOB_DATA, data)
736
737 def testPackSorted(self):
738 """Test that entries can be sorted"""
739 data = self._DoReadFile('24_sorted.dts')
Simon Glassf6898902017-11-13 18:54:59 -0700740 self.assertEqual(chr(0) * 1 + U_BOOT_SPL_DATA + chr(0) * 2 +
Simon Glass4f443042016-11-25 20:15:52 -0700741 U_BOOT_DATA, data)
742
Simon Glass3ab95982018-08-01 15:22:37 -0600743 def testPackZeroOffset(self):
744 """Test that an entry at offset 0 is not given a new offset"""
Simon Glass4f443042016-11-25 20:15:52 -0700745 with self.assertRaises(ValueError) as e:
746 self._DoTestFile('25_pack_zero_size.dts')
Simon Glass3ab95982018-08-01 15:22:37 -0600747 self.assertIn("Node '/binman/u-boot-spl': Offset 0x0 (0) overlaps "
Simon Glass4f443042016-11-25 20:15:52 -0700748 "with previous entry '/binman/u-boot' ending at 0x4 (4)",
749 str(e.exception))
750
751 def testPackUbootDtb(self):
752 """Test that a device tree can be added to U-Boot"""
753 data = self._DoReadFile('26_pack_u_boot_dtb.dts')
754 self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA, data)
Simon Glasse0ff8552016-11-25 20:15:53 -0700755
756 def testPackX86RomNoSize(self):
757 """Test that the end-at-4gb property requires a size property"""
758 with self.assertRaises(ValueError) as e:
759 self._DoTestFile('27_pack_4gb_no_size.dts')
Simon Glass8f1da502018-06-01 09:38:12 -0600760 self.assertIn("Section '/binman': Section size must be provided when "
Simon Glasse0ff8552016-11-25 20:15:53 -0700761 "using end-at-4gb", str(e.exception))
762
763 def testPackX86RomOutside(self):
Simon Glass3ab95982018-08-01 15:22:37 -0600764 """Test that the end-at-4gb property checks for offset boundaries"""
Simon Glasse0ff8552016-11-25 20:15:53 -0700765 with self.assertRaises(ValueError) as e:
766 self._DoTestFile('28_pack_4gb_outside.dts')
Simon Glass3ab95982018-08-01 15:22:37 -0600767 self.assertIn("Node '/binman/u-boot': Offset 0x0 (0) is outside "
Simon Glass8f1da502018-06-01 09:38:12 -0600768 "the section starting at 0xffffffe0 (4294967264)",
Simon Glasse0ff8552016-11-25 20:15:53 -0700769 str(e.exception))
770
771 def testPackX86Rom(self):
772 """Test that a basic x86 ROM can be created"""
773 data = self._DoReadFile('29_x86-rom.dts')
Simon Glassf6898902017-11-13 18:54:59 -0700774 self.assertEqual(U_BOOT_DATA + chr(0) * 7 + U_BOOT_SPL_DATA +
775 chr(0) * 2, data)
Simon Glasse0ff8552016-11-25 20:15:53 -0700776
777 def testPackX86RomMeNoDesc(self):
778 """Test that an invalid Intel descriptor entry is detected"""
779 TestFunctional._MakeInputFile('descriptor.bin', '')
780 with self.assertRaises(ValueError) as e:
781 self._DoTestFile('31_x86-rom-me.dts')
782 self.assertIn("Node '/binman/intel-descriptor': Cannot find FD "
783 "signature", str(e.exception))
784
785 def testPackX86RomBadDesc(self):
786 """Test that the Intel requires a descriptor entry"""
787 with self.assertRaises(ValueError) as e:
788 self._DoTestFile('30_x86-rom-me-no-desc.dts')
Simon Glass3ab95982018-08-01 15:22:37 -0600789 self.assertIn("Node '/binman/intel-me': No offset set with "
790 "offset-unset: should another entry provide this correct "
791 "offset?", str(e.exception))
Simon Glasse0ff8552016-11-25 20:15:53 -0700792
793 def testPackX86RomMe(self):
794 """Test that an x86 ROM with an ME region can be created"""
795 data = self._DoReadFile('31_x86-rom-me.dts')
796 self.assertEqual(ME_DATA, data[0x1000:0x1000 + len(ME_DATA)])
797
798 def testPackVga(self):
799 """Test that an image with a VGA binary can be created"""
800 data = self._DoReadFile('32_intel-vga.dts')
801 self.assertEqual(VGA_DATA, data[:len(VGA_DATA)])
802
803 def testPackStart16(self):
804 """Test that an image with an x86 start16 region can be created"""
805 data = self._DoReadFile('33_x86-start16.dts')
806 self.assertEqual(X86_START16_DATA, data[:len(X86_START16_DATA)])
807
Simon Glass736bb0a2018-07-06 10:27:17 -0600808 def _RunMicrocodeTest(self, dts_fname, nodtb_data, ucode_second=False):
Simon Glassadc57012018-07-06 10:27:16 -0600809 """Handle running a test for insertion of microcode
810
811 Args:
812 dts_fname: Name of test .dts file
813 nodtb_data: Data that we expect in the first section
Simon Glass736bb0a2018-07-06 10:27:17 -0600814 ucode_second: True if the microsecond entry is second instead of
815 third
Simon Glassadc57012018-07-06 10:27:16 -0600816
817 Returns:
818 Tuple:
819 Contents of first region (U-Boot or SPL)
Simon Glass3ab95982018-08-01 15:22:37 -0600820 Offset and size components of microcode pointer, as inserted
Simon Glassadc57012018-07-06 10:27:16 -0600821 in the above (two 4-byte words)
822 """
Simon Glass6b187df2017-11-12 21:52:27 -0700823 data = self._DoReadFile(dts_fname, True)
Simon Glasse0ff8552016-11-25 20:15:53 -0700824
825 # Now check the device tree has no microcode
Simon Glass736bb0a2018-07-06 10:27:17 -0600826 if ucode_second:
827 ucode_content = data[len(nodtb_data):]
828 ucode_pos = len(nodtb_data)
829 dtb_with_ucode = ucode_content[16:]
830 fdt_len = self.GetFdtLen(dtb_with_ucode)
831 else:
832 dtb_with_ucode = data[len(nodtb_data):]
833 fdt_len = self.GetFdtLen(dtb_with_ucode)
834 ucode_content = dtb_with_ucode[fdt_len:]
835 ucode_pos = len(nodtb_data) + fdt_len
Simon Glasse0ff8552016-11-25 20:15:53 -0700836 fname = tools.GetOutputFilename('test.dtb')
837 with open(fname, 'wb') as fd:
Simon Glassadc57012018-07-06 10:27:16 -0600838 fd.write(dtb_with_ucode)
Simon Glassec3f3782017-05-27 07:38:29 -0600839 dtb = fdt.FdtScan(fname)
840 ucode = dtb.GetNode('/microcode')
Simon Glasse0ff8552016-11-25 20:15:53 -0700841 self.assertTrue(ucode)
842 for node in ucode.subnodes:
843 self.assertFalse(node.props.get('data'))
844
Simon Glasse0ff8552016-11-25 20:15:53 -0700845 # Check that the microcode appears immediately after the Fdt
846 # This matches the concatenation of the data properties in
Simon Glass87722132017-11-12 21:52:26 -0700847 # the /microcode/update@xxx nodes in 34_x86_ucode.dts.
Simon Glasse0ff8552016-11-25 20:15:53 -0700848 ucode_data = struct.pack('>4L', 0x12345678, 0x12345679, 0xabcd0000,
849 0x78235609)
Simon Glassadc57012018-07-06 10:27:16 -0600850 self.assertEqual(ucode_data, ucode_content[:len(ucode_data)])
Simon Glasse0ff8552016-11-25 20:15:53 -0700851
852 # Check that the microcode pointer was inserted. It should match the
Simon Glass3ab95982018-08-01 15:22:37 -0600853 # expected offset and size
Simon Glasse0ff8552016-11-25 20:15:53 -0700854 pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
855 len(ucode_data))
Simon Glass736bb0a2018-07-06 10:27:17 -0600856 u_boot = data[:len(nodtb_data)]
857 return u_boot, pos_and_size
Simon Glass6b187df2017-11-12 21:52:27 -0700858
859 def testPackUbootMicrocode(self):
860 """Test that x86 microcode can be handled correctly
861
862 We expect to see the following in the image, in order:
863 u-boot-nodtb.bin with a microcode pointer inserted at the correct
864 place
865 u-boot.dtb with the microcode removed
866 the microcode
867 """
868 first, pos_and_size = self._RunMicrocodeTest('34_x86_ucode.dts',
869 U_BOOT_NODTB_DATA)
Simon Glasse0ff8552016-11-25 20:15:53 -0700870 self.assertEqual('nodtb with microcode' + pos_and_size +
871 ' somewhere in here', first)
872
Simon Glass160a7662017-05-27 07:38:26 -0600873 def _RunPackUbootSingleMicrocode(self):
Simon Glasse0ff8552016-11-25 20:15:53 -0700874 """Test that x86 microcode can be handled correctly
875
876 We expect to see the following in the image, in order:
877 u-boot-nodtb.bin with a microcode pointer inserted at the correct
878 place
879 u-boot.dtb with the microcode
880 an empty microcode region
881 """
882 # We need the libfdt library to run this test since only that allows
883 # finding the offset of a property. This is required by
884 # Entry_u_boot_dtb_with_ucode.ObtainContents().
Simon Glasse0ff8552016-11-25 20:15:53 -0700885 data = self._DoReadFile('35_x86_single_ucode.dts', True)
886
887 second = data[len(U_BOOT_NODTB_DATA):]
888
889 fdt_len = self.GetFdtLen(second)
890 third = second[fdt_len:]
891 second = second[:fdt_len]
892
Simon Glass160a7662017-05-27 07:38:26 -0600893 ucode_data = struct.pack('>2L', 0x12345678, 0x12345679)
894 self.assertIn(ucode_data, second)
895 ucode_pos = second.find(ucode_data) + len(U_BOOT_NODTB_DATA)
Simon Glasse0ff8552016-11-25 20:15:53 -0700896
Simon Glass160a7662017-05-27 07:38:26 -0600897 # Check that the microcode pointer was inserted. It should match the
Simon Glass3ab95982018-08-01 15:22:37 -0600898 # expected offset and size
Simon Glass160a7662017-05-27 07:38:26 -0600899 pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
900 len(ucode_data))
901 first = data[:len(U_BOOT_NODTB_DATA)]
902 self.assertEqual('nodtb with microcode' + pos_and_size +
903 ' somewhere in here', first)
Simon Glassc49deb82016-11-25 20:15:54 -0700904
Simon Glass75db0862016-11-25 20:15:55 -0700905 def testPackUbootSingleMicrocode(self):
906 """Test that x86 microcode can be handled correctly with fdt_normal.
907 """
Simon Glass160a7662017-05-27 07:38:26 -0600908 self._RunPackUbootSingleMicrocode()
Simon Glass75db0862016-11-25 20:15:55 -0700909
Simon Glassc49deb82016-11-25 20:15:54 -0700910 def testUBootImg(self):
911 """Test that u-boot.img can be put in a file"""
912 data = self._DoReadFile('36_u_boot_img.dts')
913 self.assertEqual(U_BOOT_IMG_DATA, data)
Simon Glass75db0862016-11-25 20:15:55 -0700914
915 def testNoMicrocode(self):
916 """Test that a missing microcode region is detected"""
917 with self.assertRaises(ValueError) as e:
918 self._DoReadFile('37_x86_no_ucode.dts', True)
919 self.assertIn("Node '/binman/u-boot-dtb-with-ucode': No /microcode "
920 "node found in ", str(e.exception))
921
922 def testMicrocodeWithoutNode(self):
923 """Test that a missing u-boot-dtb-with-ucode node is detected"""
924 with self.assertRaises(ValueError) as e:
925 self._DoReadFile('38_x86_ucode_missing_node.dts', True)
926 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
927 "microcode region u-boot-dtb-with-ucode", str(e.exception))
928
929 def testMicrocodeWithoutNode2(self):
930 """Test that a missing u-boot-ucode node is detected"""
931 with self.assertRaises(ValueError) as e:
932 self._DoReadFile('39_x86_ucode_missing_node2.dts', True)
933 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
934 "microcode region u-boot-ucode", str(e.exception))
935
936 def testMicrocodeWithoutPtrInElf(self):
937 """Test that a U-Boot binary without the microcode symbol is detected"""
938 # ELF file without a '_dt_ucode_base_size' symbol
Simon Glass75db0862016-11-25 20:15:55 -0700939 try:
940 with open(self.TestFile('u_boot_no_ucode_ptr')) as fd:
941 TestFunctional._MakeInputFile('u-boot', fd.read())
942
943 with self.assertRaises(ValueError) as e:
Simon Glass160a7662017-05-27 07:38:26 -0600944 self._RunPackUbootSingleMicrocode()
Simon Glass75db0862016-11-25 20:15:55 -0700945 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot locate "
946 "_dt_ucode_base_size symbol in u-boot", str(e.exception))
947
948 finally:
949 # Put the original file back
950 with open(self.TestFile('u_boot_ucode_ptr')) as fd:
951 TestFunctional._MakeInputFile('u-boot', fd.read())
952
953 def testMicrocodeNotInImage(self):
954 """Test that microcode must be placed within the image"""
955 with self.assertRaises(ValueError) as e:
956 self._DoReadFile('40_x86_ucode_not_in_image.dts', True)
957 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Microcode "
958 "pointer _dt_ucode_base_size at fffffe14 is outside the "
Simon Glass25ac0e62018-06-01 09:38:14 -0600959 "section ranging from 00000000 to 0000002e", str(e.exception))
Simon Glass75db0862016-11-25 20:15:55 -0700960
961 def testWithoutMicrocode(self):
962 """Test that we can cope with an image without microcode (e.g. qemu)"""
963 with open(self.TestFile('u_boot_no_ucode_ptr')) as fd:
964 TestFunctional._MakeInputFile('u-boot', fd.read())
Simon Glass16b8d6b2018-07-06 10:27:42 -0600965 data, dtb, _, _ = self._DoReadFileDtb('44_x86_optional_ucode.dts', True)
Simon Glass75db0862016-11-25 20:15:55 -0700966
967 # Now check the device tree has no microcode
968 self.assertEqual(U_BOOT_NODTB_DATA, data[:len(U_BOOT_NODTB_DATA)])
969 second = data[len(U_BOOT_NODTB_DATA):]
970
971 fdt_len = self.GetFdtLen(second)
972 self.assertEqual(dtb, second[:fdt_len])
973
974 used_len = len(U_BOOT_NODTB_DATA) + fdt_len
975 third = data[used_len:]
976 self.assertEqual(chr(0) * (0x200 - used_len), third)
977
978 def testUnknownPosSize(self):
979 """Test that microcode must be placed within the image"""
980 with self.assertRaises(ValueError) as e:
981 self._DoReadFile('41_unknown_pos_size.dts', True)
Simon Glass3ab95982018-08-01 15:22:37 -0600982 self.assertIn("Section '/binman': Unable to set offset/size for unknown "
Simon Glass75db0862016-11-25 20:15:55 -0700983 "entry 'invalid-entry'", str(e.exception))
Simon Glassda229092016-11-25 20:15:56 -0700984
985 def testPackFsp(self):
986 """Test that an image with a FSP binary can be created"""
987 data = self._DoReadFile('42_intel-fsp.dts')
988 self.assertEqual(FSP_DATA, data[:len(FSP_DATA)])
989
990 def testPackCmc(self):
Bin Meng59ea8c22017-08-15 22:41:54 -0700991 """Test that an image with a CMC binary can be created"""
Simon Glassda229092016-11-25 20:15:56 -0700992 data = self._DoReadFile('43_intel-cmc.dts')
993 self.assertEqual(CMC_DATA, data[:len(CMC_DATA)])
Bin Meng59ea8c22017-08-15 22:41:54 -0700994
995 def testPackVbt(self):
996 """Test that an image with a VBT binary can be created"""
997 data = self._DoReadFile('46_intel-vbt.dts')
998 self.assertEqual(VBT_DATA, data[:len(VBT_DATA)])
Simon Glass9fc60b42017-11-12 21:52:22 -0700999
Simon Glass56509842017-11-12 21:52:25 -07001000 def testSplBssPad(self):
1001 """Test that we can pad SPL's BSS with zeros"""
Simon Glass6b187df2017-11-12 21:52:27 -07001002 # ELF file with a '__bss_size' symbol
1003 with open(self.TestFile('bss_data')) as fd:
1004 TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
Simon Glass56509842017-11-12 21:52:25 -07001005 data = self._DoReadFile('47_spl_bss_pad.dts')
1006 self.assertEqual(U_BOOT_SPL_DATA + (chr(0) * 10) + U_BOOT_DATA, data)
1007
Simon Glassb50e5612017-11-13 18:54:54 -07001008 with open(self.TestFile('u_boot_ucode_ptr')) as fd:
1009 TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
1010 with self.assertRaises(ValueError) as e:
1011 data = self._DoReadFile('47_spl_bss_pad.dts')
1012 self.assertIn('Expected __bss_size symbol in spl/u-boot-spl',
1013 str(e.exception))
1014
Simon Glass87722132017-11-12 21:52:26 -07001015 def testPackStart16Spl(self):
Simon Glass35b384c2018-09-14 04:57:10 -06001016 """Test that an image with an x86 start16 SPL region can be created"""
Simon Glass87722132017-11-12 21:52:26 -07001017 data = self._DoReadFile('48_x86-start16-spl.dts')
1018 self.assertEqual(X86_START16_SPL_DATA, data[:len(X86_START16_SPL_DATA)])
1019
Simon Glass736bb0a2018-07-06 10:27:17 -06001020 def _PackUbootSplMicrocode(self, dts, ucode_second=False):
1021 """Helper function for microcode tests
Simon Glass6b187df2017-11-12 21:52:27 -07001022
1023 We expect to see the following in the image, in order:
1024 u-boot-spl-nodtb.bin with a microcode pointer inserted at the
1025 correct place
1026 u-boot.dtb with the microcode removed
1027 the microcode
Simon Glass736bb0a2018-07-06 10:27:17 -06001028
1029 Args:
1030 dts: Device tree file to use for test
1031 ucode_second: True if the microsecond entry is second instead of
1032 third
Simon Glass6b187df2017-11-12 21:52:27 -07001033 """
1034 # ELF file with a '_dt_ucode_base_size' symbol
1035 with open(self.TestFile('u_boot_ucode_ptr')) as fd:
1036 TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
Simon Glass736bb0a2018-07-06 10:27:17 -06001037 first, pos_and_size = self._RunMicrocodeTest(dts, U_BOOT_SPL_NODTB_DATA,
1038 ucode_second=ucode_second)
Simon Glass6b187df2017-11-12 21:52:27 -07001039 self.assertEqual('splnodtb with microc' + pos_and_size +
1040 'ter somewhere in here', first)
1041
Simon Glass736bb0a2018-07-06 10:27:17 -06001042 def testPackUbootSplMicrocode(self):
1043 """Test that x86 microcode can be handled correctly in SPL"""
1044 self._PackUbootSplMicrocode('49_x86_ucode_spl.dts')
1045
1046 def testPackUbootSplMicrocodeReorder(self):
1047 """Test that order doesn't matter for microcode entries
1048
1049 This is the same as testPackUbootSplMicrocode but when we process the
1050 u-boot-ucode entry we have not yet seen the u-boot-dtb-with-ucode
1051 entry, so we reply on binman to try later.
1052 """
1053 self._PackUbootSplMicrocode('58_x86_ucode_spl_needs_retry.dts',
1054 ucode_second=True)
1055
Simon Glassca4f4ff2017-11-12 21:52:28 -07001056 def testPackMrc(self):
1057 """Test that an image with an MRC binary can be created"""
1058 data = self._DoReadFile('50_intel_mrc.dts')
1059 self.assertEqual(MRC_DATA, data[:len(MRC_DATA)])
1060
Simon Glass47419ea2017-11-13 18:54:55 -07001061 def testSplDtb(self):
1062 """Test that an image with spl/u-boot-spl.dtb can be created"""
1063 data = self._DoReadFile('51_u_boot_spl_dtb.dts')
1064 self.assertEqual(U_BOOT_SPL_DTB_DATA, data[:len(U_BOOT_SPL_DTB_DATA)])
1065
Simon Glass4e6fdbe2017-11-13 18:54:56 -07001066 def testSplNoDtb(self):
1067 """Test that an image with spl/u-boot-spl-nodtb.bin can be created"""
1068 data = self._DoReadFile('52_u_boot_spl_nodtb.dts')
1069 self.assertEqual(U_BOOT_SPL_NODTB_DATA, data[:len(U_BOOT_SPL_NODTB_DATA)])
1070
Simon Glass19790632017-11-13 18:55:01 -07001071 def testSymbols(self):
1072 """Test binman can assign symbols embedded in U-Boot"""
1073 elf_fname = self.TestFile('u_boot_binman_syms')
1074 syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
1075 addr = elf.GetSymbolAddress(elf_fname, '__image_copy_start')
Simon Glass3ab95982018-08-01 15:22:37 -06001076 self.assertEqual(syms['_binman_u_boot_spl_prop_offset'].address, addr)
Simon Glass19790632017-11-13 18:55:01 -07001077
1078 with open(self.TestFile('u_boot_binman_syms')) as fd:
1079 TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
1080 data = self._DoReadFile('53_symbols.dts')
1081 sym_values = struct.pack('<LQL', 0x24 + 0, 0x24 + 24, 0x24 + 20)
1082 expected = (sym_values + U_BOOT_SPL_DATA[16:] + chr(0xff) +
1083 U_BOOT_DATA +
1084 sym_values + U_BOOT_SPL_DATA[16:])
1085 self.assertEqual(expected, data)
1086
Simon Glassdd57c132018-06-01 09:38:11 -06001087 def testPackUnitAddress(self):
1088 """Test that we support multiple binaries with the same name"""
1089 data = self._DoReadFile('54_unit_address.dts')
1090 self.assertEqual(U_BOOT_DATA + U_BOOT_DATA, data)
1091
Simon Glass18546952018-06-01 09:38:16 -06001092 def testSections(self):
1093 """Basic test of sections"""
1094 data = self._DoReadFile('55_sections.dts')
Simon Glass8122f392018-07-17 13:25:28 -06001095 expected = (U_BOOT_DATA + '!' * 12 + U_BOOT_DATA + 'a' * 12 +
1096 U_BOOT_DATA + '&' * 4)
Simon Glass18546952018-06-01 09:38:16 -06001097 self.assertEqual(expected, data)
Simon Glass9fc60b42017-11-12 21:52:22 -07001098
Simon Glass3b0c3822018-06-01 09:38:20 -06001099 def testMap(self):
1100 """Tests outputting a map of the images"""
Simon Glass16b8d6b2018-07-06 10:27:42 -06001101 _, _, map_data, _ = self._DoReadFileDtb('55_sections.dts', map=True)
Simon Glass1be70d22018-07-17 13:25:49 -06001102 self.assertEqual('''ImagePos Offset Size Name
110300000000 00000000 00000028 main-section
110400000000 00000000 00000010 section@0
110500000000 00000000 00000004 u-boot
110600000010 00000010 00000010 section@1
110700000010 00000000 00000004 u-boot
110800000020 00000020 00000004 section@2
110900000020 00000000 00000004 u-boot
Simon Glass3b0c3822018-06-01 09:38:20 -06001110''', map_data)
1111
Simon Glassc8d48ef2018-06-01 09:38:21 -06001112 def testNamePrefix(self):
1113 """Tests that name prefixes are used"""
Simon Glass16b8d6b2018-07-06 10:27:42 -06001114 _, _, map_data, _ = self._DoReadFileDtb('56_name_prefix.dts', map=True)
Simon Glass1be70d22018-07-17 13:25:49 -06001115 self.assertEqual('''ImagePos Offset Size Name
111600000000 00000000 00000028 main-section
111700000000 00000000 00000010 section@0
111800000000 00000000 00000004 ro-u-boot
111900000010 00000010 00000010 section@1
112000000010 00000000 00000004 rw-u-boot
Simon Glassc8d48ef2018-06-01 09:38:21 -06001121''', map_data)
1122
Simon Glass736bb0a2018-07-06 10:27:17 -06001123 def testUnknownContents(self):
1124 """Test that obtaining the contents works as expected"""
1125 with self.assertRaises(ValueError) as e:
1126 self._DoReadFile('57_unknown_contents.dts', True)
1127 self.assertIn("Section '/binman': Internal error: Could not complete "
1128 "processing of contents: remaining [<_testing.Entry__testing ",
1129 str(e.exception))
1130
Simon Glass5c890232018-07-06 10:27:19 -06001131 def testBadChangeSize(self):
1132 """Test that trying to change the size of an entry fails"""
1133 with self.assertRaises(ValueError) as e:
1134 self._DoReadFile('59_change_size.dts', True)
1135 self.assertIn("Node '/binman/_testing': Cannot update entry size from "
1136 '2 to 1', str(e.exception))
1137
Simon Glass16b8d6b2018-07-06 10:27:42 -06001138 def testUpdateFdt(self):
Simon Glass3ab95982018-08-01 15:22:37 -06001139 """Test that we can update the device tree with offset/size info"""
Simon Glass16b8d6b2018-07-06 10:27:42 -06001140 _, _, _, out_dtb_fname = self._DoReadFileDtb('60_fdt_update.dts',
1141 update_dtb=True)
Simon Glasscee02e62018-07-17 13:25:52 -06001142 dtb = fdt.Fdt(out_dtb_fname)
1143 dtb.Scan()
1144 props = self._GetPropTree(dtb, ['offset', 'size', 'image-pos'])
Simon Glass16b8d6b2018-07-06 10:27:42 -06001145 self.assertEqual({
Simon Glassdbf6be92018-08-01 15:22:42 -06001146 'image-pos': 0,
Simon Glass8122f392018-07-17 13:25:28 -06001147 'offset': 0,
Simon Glass3ab95982018-08-01 15:22:37 -06001148 '_testing:offset': 32,
Simon Glass16b8d6b2018-07-06 10:27:42 -06001149 '_testing:size': 1,
Simon Glassdbf6be92018-08-01 15:22:42 -06001150 '_testing:image-pos': 32,
Simon Glass3ab95982018-08-01 15:22:37 -06001151 'section@0/u-boot:offset': 0,
Simon Glass16b8d6b2018-07-06 10:27:42 -06001152 'section@0/u-boot:size': len(U_BOOT_DATA),
Simon Glassdbf6be92018-08-01 15:22:42 -06001153 'section@0/u-boot:image-pos': 0,
Simon Glass3ab95982018-08-01 15:22:37 -06001154 'section@0:offset': 0,
Simon Glass16b8d6b2018-07-06 10:27:42 -06001155 'section@0:size': 16,
Simon Glassdbf6be92018-08-01 15:22:42 -06001156 'section@0:image-pos': 0,
Simon Glass16b8d6b2018-07-06 10:27:42 -06001157
Simon Glass3ab95982018-08-01 15:22:37 -06001158 'section@1/u-boot:offset': 0,
Simon Glass16b8d6b2018-07-06 10:27:42 -06001159 'section@1/u-boot:size': len(U_BOOT_DATA),
Simon Glassdbf6be92018-08-01 15:22:42 -06001160 'section@1/u-boot:image-pos': 16,
Simon Glass3ab95982018-08-01 15:22:37 -06001161 'section@1:offset': 16,
Simon Glass16b8d6b2018-07-06 10:27:42 -06001162 'section@1:size': 16,
Simon Glassdbf6be92018-08-01 15:22:42 -06001163 'section@1:image-pos': 16,
Simon Glass16b8d6b2018-07-06 10:27:42 -06001164 'size': 40
1165 }, props)
1166
1167 def testUpdateFdtBad(self):
1168 """Test that we detect when ProcessFdt never completes"""
1169 with self.assertRaises(ValueError) as e:
1170 self._DoReadFileDtb('61_fdt_update_bad.dts', update_dtb=True)
1171 self.assertIn('Could not complete processing of Fdt: remaining '
1172 '[<_testing.Entry__testing', str(e.exception))
Simon Glass5c890232018-07-06 10:27:19 -06001173
Simon Glass53af22a2018-07-17 13:25:32 -06001174 def testEntryArgs(self):
1175 """Test passing arguments to entries from the command line"""
1176 entry_args = {
1177 'test-str-arg': 'test1',
1178 'test-int-arg': '456',
1179 }
1180 self._DoReadFileDtb('62_entry_args.dts', entry_args=entry_args)
1181 self.assertIn('image', control.images)
1182 entry = control.images['image'].GetEntries()['_testing']
1183 self.assertEqual('test0', entry.test_str_fdt)
1184 self.assertEqual('test1', entry.test_str_arg)
1185 self.assertEqual(123, entry.test_int_fdt)
1186 self.assertEqual(456, entry.test_int_arg)
1187
1188 def testEntryArgsMissing(self):
1189 """Test missing arguments and properties"""
1190 entry_args = {
1191 'test-int-arg': '456',
1192 }
1193 self._DoReadFileDtb('63_entry_args_missing.dts', entry_args=entry_args)
1194 entry = control.images['image'].GetEntries()['_testing']
1195 self.assertEqual('test0', entry.test_str_fdt)
1196 self.assertEqual(None, entry.test_str_arg)
1197 self.assertEqual(None, entry.test_int_fdt)
1198 self.assertEqual(456, entry.test_int_arg)
1199
1200 def testEntryArgsRequired(self):
1201 """Test missing arguments and properties"""
1202 entry_args = {
1203 'test-int-arg': '456',
1204 }
1205 with self.assertRaises(ValueError) as e:
1206 self._DoReadFileDtb('64_entry_args_required.dts')
1207 self.assertIn("Node '/binman/_testing': Missing required "
1208 'properties/entry args: test-str-arg, test-int-fdt, test-int-arg',
1209 str(e.exception))
1210
1211 def testEntryArgsInvalidFormat(self):
1212 """Test that an invalid entry-argument format is detected"""
1213 args = ['-d', self.TestFile('64_entry_args_required.dts'), '-ano-value']
1214 with self.assertRaises(ValueError) as e:
1215 self._DoBinman(*args)
1216 self.assertIn("Invalid entry arguemnt 'no-value'", str(e.exception))
1217
1218 def testEntryArgsInvalidInteger(self):
1219 """Test that an invalid entry-argument integer is detected"""
1220 entry_args = {
1221 'test-int-arg': 'abc',
1222 }
1223 with self.assertRaises(ValueError) as e:
1224 self._DoReadFileDtb('62_entry_args.dts', entry_args=entry_args)
1225 self.assertIn("Node '/binman/_testing': Cannot convert entry arg "
1226 "'test-int-arg' (value 'abc') to integer",
1227 str(e.exception))
1228
1229 def testEntryArgsInvalidDatatype(self):
1230 """Test that an invalid entry-argument datatype is detected
1231
1232 This test could be written in entry_test.py except that it needs
1233 access to control.entry_args, which seems more than that module should
1234 be able to see.
1235 """
1236 entry_args = {
1237 'test-bad-datatype-arg': '12',
1238 }
1239 with self.assertRaises(ValueError) as e:
1240 self._DoReadFileDtb('65_entry_args_unknown_datatype.dts',
1241 entry_args=entry_args)
1242 self.assertIn('GetArg() internal error: Unknown data type ',
1243 str(e.exception))
1244
Simon Glassbb748372018-07-17 13:25:33 -06001245 def testText(self):
1246 """Test for a text entry type"""
1247 entry_args = {
1248 'test-id': TEXT_DATA,
1249 'test-id2': TEXT_DATA2,
1250 'test-id3': TEXT_DATA3,
1251 }
1252 data, _, _, _ = self._DoReadFileDtb('66_text.dts',
1253 entry_args=entry_args)
1254 expected = (TEXT_DATA + chr(0) * (8 - len(TEXT_DATA)) + TEXT_DATA2 +
1255 TEXT_DATA3 + 'some text')
1256 self.assertEqual(expected, data)
1257
Simon Glassfd8d1f72018-07-17 13:25:36 -06001258 def testEntryDocs(self):
1259 """Test for creation of entry documentation"""
1260 with test_util.capture_sys_output() as (stdout, stderr):
1261 control.WriteEntryDocs(binman.GetEntryModules())
1262 self.assertTrue(len(stdout.getvalue()) > 0)
1263
1264 def testEntryDocsMissing(self):
1265 """Test handling of missing entry documentation"""
1266 with self.assertRaises(ValueError) as e:
1267 with test_util.capture_sys_output() as (stdout, stderr):
1268 control.WriteEntryDocs(binman.GetEntryModules(), 'u_boot')
1269 self.assertIn('Documentation is missing for modules: u_boot',
1270 str(e.exception))
1271
Simon Glass11e36cc2018-07-17 13:25:38 -06001272 def testFmap(self):
1273 """Basic test of generation of a flashrom fmap"""
1274 data = self._DoReadFile('67_fmap.dts')
1275 fhdr, fentries = fmap_util.DecodeFmap(data[32:])
1276 expected = U_BOOT_DATA + '!' * 12 + U_BOOT_DATA + 'a' * 12
1277 self.assertEqual(expected, data[:32])
1278 self.assertEqual('__FMAP__', fhdr.signature)
1279 self.assertEqual(1, fhdr.ver_major)
1280 self.assertEqual(0, fhdr.ver_minor)
1281 self.assertEqual(0, fhdr.base)
1282 self.assertEqual(16 + 16 +
1283 fmap_util.FMAP_HEADER_LEN +
1284 fmap_util.FMAP_AREA_LEN * 3, fhdr.image_size)
1285 self.assertEqual('FMAP', fhdr.name)
1286 self.assertEqual(3, fhdr.nareas)
1287 for fentry in fentries:
1288 self.assertEqual(0, fentry.flags)
1289
1290 self.assertEqual(0, fentries[0].offset)
1291 self.assertEqual(4, fentries[0].size)
1292 self.assertEqual('RO_U_BOOT', fentries[0].name)
1293
1294 self.assertEqual(16, fentries[1].offset)
1295 self.assertEqual(4, fentries[1].size)
1296 self.assertEqual('RW_U_BOOT', fentries[1].name)
1297
1298 self.assertEqual(32, fentries[2].offset)
1299 self.assertEqual(fmap_util.FMAP_HEADER_LEN +
1300 fmap_util.FMAP_AREA_LEN * 3, fentries[2].size)
1301 self.assertEqual('FMAP', fentries[2].name)
1302
Simon Glassec127af2018-07-17 13:25:39 -06001303 def testBlobNamedByArg(self):
1304 """Test we can add a blob with the filename coming from an entry arg"""
1305 entry_args = {
1306 'cros-ec-rw-path': 'ecrw.bin',
1307 }
1308 data, _, _, _ = self._DoReadFileDtb('68_blob_named_by_arg.dts',
1309 entry_args=entry_args)
1310
Simon Glass3af8e492018-07-17 13:25:40 -06001311 def testFill(self):
1312 """Test for an fill entry type"""
1313 data = self._DoReadFile('69_fill.dts')
1314 expected = 8 * chr(0xff) + 8 * chr(0)
1315 self.assertEqual(expected, data)
1316
1317 def testFillNoSize(self):
1318 """Test for an fill entry type with no size"""
1319 with self.assertRaises(ValueError) as e:
1320 self._DoReadFile('70_fill_no_size.dts')
1321 self.assertIn("'fill' entry must have a size property",
1322 str(e.exception))
1323
Simon Glass0ef87aa2018-07-17 13:25:44 -06001324 def _HandleGbbCommand(self, pipe_list):
1325 """Fake calls to the futility utility"""
1326 if pipe_list[0][0] == 'futility':
1327 fname = pipe_list[0][-1]
1328 # Append our GBB data to the file, which will happen every time the
1329 # futility command is called.
1330 with open(fname, 'a') as fd:
1331 fd.write(GBB_DATA)
1332 return command.CommandResult()
1333
1334 def testGbb(self):
1335 """Test for the Chromium OS Google Binary Block"""
1336 command.test_result = self._HandleGbbCommand
1337 entry_args = {
1338 'keydir': 'devkeys',
1339 'bmpblk': 'bmpblk.bin',
1340 }
1341 data, _, _, _ = self._DoReadFileDtb('71_gbb.dts', entry_args=entry_args)
1342
1343 # Since futility
1344 expected = GBB_DATA + GBB_DATA + 8 * chr(0) + (0x2180 - 16) * chr(0)
1345 self.assertEqual(expected, data)
1346
1347 def testGbbTooSmall(self):
1348 """Test for the Chromium OS Google Binary Block being large enough"""
1349 with self.assertRaises(ValueError) as e:
1350 self._DoReadFileDtb('72_gbb_too_small.dts')
1351 self.assertIn("Node '/binman/gbb': GBB is too small",
1352 str(e.exception))
1353
1354 def testGbbNoSize(self):
1355 """Test for the Chromium OS Google Binary Block having a size"""
1356 with self.assertRaises(ValueError) as e:
1357 self._DoReadFileDtb('73_gbb_no_size.dts')
1358 self.assertIn("Node '/binman/gbb': GBB must have a fixed size",
1359 str(e.exception))
1360
Simon Glass24d0d3c2018-07-17 13:25:47 -06001361 def _HandleVblockCommand(self, pipe_list):
1362 """Fake calls to the futility utility"""
1363 if pipe_list[0][0] == 'futility':
1364 fname = pipe_list[0][3]
Simon Glassa326b492018-09-14 04:57:11 -06001365 with open(fname, 'wb') as fd:
Simon Glass24d0d3c2018-07-17 13:25:47 -06001366 fd.write(VBLOCK_DATA)
1367 return command.CommandResult()
1368
1369 def testVblock(self):
1370 """Test for the Chromium OS Verified Boot Block"""
1371 command.test_result = self._HandleVblockCommand
1372 entry_args = {
1373 'keydir': 'devkeys',
1374 }
1375 data, _, _, _ = self._DoReadFileDtb('74_vblock.dts',
1376 entry_args=entry_args)
1377 expected = U_BOOT_DATA + VBLOCK_DATA + U_BOOT_DTB_DATA
1378 self.assertEqual(expected, data)
1379
1380 def testVblockNoContent(self):
1381 """Test we detect a vblock which has no content to sign"""
1382 with self.assertRaises(ValueError) as e:
1383 self._DoReadFile('75_vblock_no_content.dts')
1384 self.assertIn("Node '/binman/vblock': Vblock must have a 'content' "
1385 'property', str(e.exception))
1386
1387 def testVblockBadPhandle(self):
1388 """Test that we detect a vblock with an invalid phandle in contents"""
1389 with self.assertRaises(ValueError) as e:
1390 self._DoReadFile('76_vblock_bad_phandle.dts')
1391 self.assertIn("Node '/binman/vblock': Cannot find node for phandle "
1392 '1000', str(e.exception))
1393
1394 def testVblockBadEntry(self):
1395 """Test that we detect an entry that points to a non-entry"""
1396 with self.assertRaises(ValueError) as e:
1397 self._DoReadFile('77_vblock_bad_entry.dts')
1398 self.assertIn("Node '/binman/vblock': Cannot find entry for node "
1399 "'other'", str(e.exception))
1400
Simon Glassb8ef5b62018-07-17 13:25:48 -06001401 def testTpl(self):
1402 """Test that an image with TPL and ots device tree can be created"""
1403 # ELF file with a '__bss_size' symbol
1404 with open(self.TestFile('bss_data')) as fd:
1405 TestFunctional._MakeInputFile('tpl/u-boot-tpl', fd.read())
1406 data = self._DoReadFile('78_u_boot_tpl.dts')
1407 self.assertEqual(U_BOOT_TPL_DATA + U_BOOT_TPL_DTB_DATA, data)
1408
Simon Glass15a587c2018-07-17 13:25:51 -06001409 def testUsesPos(self):
1410 """Test that the 'pos' property cannot be used anymore"""
1411 with self.assertRaises(ValueError) as e:
1412 data = self._DoReadFile('79_uses_pos.dts')
1413 self.assertIn("Node '/binman/u-boot': Please use 'offset' instead of "
1414 "'pos'", str(e.exception))
1415
Simon Glassd178eab2018-09-14 04:57:08 -06001416 def testFillZero(self):
1417 """Test for an fill entry type with a size of 0"""
1418 data = self._DoReadFile('80_fill_empty.dts')
1419 self.assertEqual(chr(0) * 16, data)
1420
Simon Glass0b489362018-09-14 04:57:09 -06001421 def testTextMissing(self):
1422 """Test for a text entry type where there is no text"""
1423 with self.assertRaises(ValueError) as e:
1424 self._DoReadFileDtb('66_text.dts',)
1425 self.assertIn("Node '/binman/text': No value provided for text label "
1426 "'test-id'", str(e.exception))
1427
Simon Glass35b384c2018-09-14 04:57:10 -06001428 def testPackStart16Tpl(self):
1429 """Test that an image with an x86 start16 TPL region can be created"""
1430 data = self._DoReadFile('81_x86-start16-tpl.dts')
1431 self.assertEqual(X86_START16_TPL_DATA, data[:len(X86_START16_TPL_DATA)])
1432
Simon Glass0bfa7b02018-09-14 04:57:12 -06001433 def testSelectImage(self):
1434 """Test that we can select which images to build"""
1435 with test_util.capture_sys_output() as (stdout, stderr):
1436 retcode = self._DoTestFile('06_dual_image.dts', images=['image2'])
1437 self.assertEqual(0, retcode)
1438 self.assertIn('Skipping images: image1', stdout.getvalue())
1439
1440 self.assertFalse(os.path.exists(tools.GetOutputFilename('image1.bin')))
1441 self.assertTrue(os.path.exists(tools.GetOutputFilename('image2.bin')))
1442
Simon Glass6ed45ba2018-09-14 04:57:24 -06001443 def testUpdateFdtAll(self):
1444 """Test that all device trees are updated with offset/size info"""
1445 data, _, _, _ = self._DoReadFileDtb('82_fdt_update_all.dts',
1446 use_real_dtb=True, update_dtb=True)
1447
1448 base_expected = {
1449 'section:image-pos': 0,
1450 'u-boot-tpl-dtb:size': 513,
1451 'u-boot-spl-dtb:size': 513,
1452 'u-boot-spl-dtb:offset': 493,
1453 'image-pos': 0,
1454 'section/u-boot-dtb:image-pos': 0,
1455 'u-boot-spl-dtb:image-pos': 493,
1456 'section/u-boot-dtb:size': 493,
1457 'u-boot-tpl-dtb:image-pos': 1006,
1458 'section/u-boot-dtb:offset': 0,
1459 'section:size': 493,
1460 'offset': 0,
1461 'section:offset': 0,
1462 'u-boot-tpl-dtb:offset': 1006,
1463 'size': 1519
1464 }
1465
1466 # We expect three device-tree files in the output, one after the other.
1467 # Read them in sequence. We look for an 'spl' property in the SPL tree,
1468 # and 'tpl' in the TPL tree, to make sure they are distinct from the
1469 # main U-Boot tree. All three should have the same postions and offset.
1470 start = 0
1471 for item in ['', 'spl', 'tpl']:
1472 dtb = fdt.Fdt.FromData(data[start:])
1473 dtb.Scan()
1474 props = self._GetPropTree(dtb, ['offset', 'size', 'image-pos',
1475 'spl', 'tpl'])
1476 expected = dict(base_expected)
1477 if item:
1478 expected[item] = 0
1479 self.assertEqual(expected, props)
1480 start += dtb._fdt_obj.totalsize()
1481
1482 def testUpdateFdtOutput(self):
1483 """Test that output DTB files are updated"""
1484 try:
1485 data, dtb_data, _, _ = self._DoReadFileDtb('82_fdt_update_all.dts',
1486 use_real_dtb=True, update_dtb=True, reset_dtbs=False)
1487
1488 # Unfortunately, compiling a source file always results in a file
1489 # called source.dtb (see fdt_util.EnsureCompiled()). The test
1490 # source file (e.g. test/75_fdt_update_all.dts) thus does not enter
1491 # binman as a file called u-boot.dtb. To fix this, copy the file
1492 # over to the expected place.
1493 #tools.WriteFile(os.path.join(self._indir, 'u-boot.dtb'),
1494 #tools.ReadFile(tools.GetOutputFilename('source.dtb')))
1495 start = 0
1496 for fname in ['u-boot.dtb.out', 'spl/u-boot-spl.dtb.out',
1497 'tpl/u-boot-tpl.dtb.out']:
1498 dtb = fdt.Fdt.FromData(data[start:])
1499 size = dtb._fdt_obj.totalsize()
1500 pathname = tools.GetOutputFilename(os.path.split(fname)[1])
1501 outdata = tools.ReadFile(pathname)
1502 name = os.path.split(fname)[0]
1503
1504 if name:
1505 orig_indata = self._GetDtbContentsForSplTpl(dtb_data, name)
1506 else:
1507 orig_indata = dtb_data
1508 self.assertNotEqual(outdata, orig_indata,
1509 "Expected output file '%s' be updated" % pathname)
1510 self.assertEqual(outdata, data[start:start + size],
1511 "Expected output file '%s' to match output image" %
1512 pathname)
1513 start += size
1514 finally:
1515 self._ResetDtbs()
1516
Simon Glass83d73c22018-09-14 04:57:26 -06001517 def _decompress(self, data):
1518 out = os.path.join(self._indir, 'lz4.tmp')
1519 with open(out, 'wb') as fd:
1520 fd.write(data)
1521 return tools.Run('lz4', '-dc', out)
1522 '''
1523 try:
1524 orig = lz4.frame.decompress(data)
1525 except AttributeError:
1526 orig = lz4.decompress(data)
1527 '''
1528
1529 def testCompress(self):
1530 """Test compression of blobs"""
1531 data, _, _, out_dtb_fname = self._DoReadFileDtb('83_compress.dts',
1532 use_real_dtb=True, update_dtb=True)
1533 dtb = fdt.Fdt(out_dtb_fname)
1534 dtb.Scan()
1535 props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
1536 orig = self._decompress(data)
1537 self.assertEquals(COMPRESS_DATA, orig)
1538 expected = {
1539 'blob:uncomp-size': len(COMPRESS_DATA),
1540 'blob:size': len(data),
1541 'size': len(data),
1542 }
1543 self.assertEqual(expected, props)
1544
Simon Glass0a98b282018-09-14 04:57:28 -06001545 def testFiles(self):
1546 """Test bringing in multiple files"""
1547 data = self._DoReadFile('84_files.dts')
1548 self.assertEqual(FILES_DATA, data)
1549
1550 def testFilesCompress(self):
1551 """Test bringing in multiple files and compressing them"""
1552 data = self._DoReadFile('85_files_compress.dts')
1553
1554 image = control.images['image']
1555 entries = image.GetEntries()
1556 files = entries['files']
1557 entries = files._section._entries
1558
1559 orig = ''
1560 for i in range(1, 3):
1561 key = '%d.dat' % i
1562 start = entries[key].image_pos
1563 len = entries[key].size
1564 chunk = data[start:start + len]
1565 orig += self._decompress(chunk)
1566
1567 self.assertEqual(FILES_DATA, orig)
1568
1569 def testFilesMissing(self):
1570 """Test missing files"""
1571 with self.assertRaises(ValueError) as e:
1572 data = self._DoReadFile('86_files_none.dts')
1573 self.assertIn("Node '/binman/files': Pattern \'files/*.none\' matched "
1574 'no files', str(e.exception))
1575
1576 def testFilesNoPattern(self):
1577 """Test missing files"""
1578 with self.assertRaises(ValueError) as e:
1579 data = self._DoReadFile('87_files_no_pattern.dts')
1580 self.assertIn("Node '/binman/files': Missing 'pattern' property",
1581 str(e.exception))
1582
Simon Glassba64a0b2018-09-14 04:57:29 -06001583 def testExpandSize(self):
1584 """Test an expanding entry"""
1585 data, _, map_data, _ = self._DoReadFileDtb('88_expand_size.dts',
1586 map=True)
1587 expect = ('a' * 8 + U_BOOT_DATA +
1588 MRC_DATA + 'b' * 1 + U_BOOT_DATA +
1589 'c' * 8 + U_BOOT_DATA +
1590 'd' * 8)
1591 self.assertEqual(expect, data)
1592 self.assertEqual('''ImagePos Offset Size Name
159300000000 00000000 00000028 main-section
159400000000 00000000 00000008 fill
159500000008 00000008 00000004 u-boot
15960000000c 0000000c 00000004 section
15970000000c 00000000 00000003 intel-mrc
159800000010 00000010 00000004 u-boot2
159900000014 00000014 0000000c section2
160000000014 00000000 00000008 fill
16010000001c 00000008 00000004 u-boot
160200000020 00000020 00000008 fill2
1603''', map_data)
1604
1605 def testExpandSizeBad(self):
1606 """Test an expanding entry which fails to provide contents"""
1607 with self.assertRaises(ValueError) as e:
1608 self._DoReadFileDtb('89_expand_size_bad.dts', map=True)
1609 self.assertIn("Node '/binman/_testing': Cannot obtain contents when "
1610 'expanding entry', str(e.exception))
1611
Simon Glasse0e5df92018-09-14 04:57:31 -06001612 def testHash(self):
1613 """Test hashing of the contents of an entry"""
1614 _, _, _, out_dtb_fname = self._DoReadFileDtb('90_hash.dts',
1615 use_real_dtb=True, update_dtb=True)
1616 dtb = fdt.Fdt(out_dtb_fname)
1617 dtb.Scan()
1618 hash_node = dtb.GetNode('/binman/u-boot/hash').props['value']
1619 m = hashlib.sha256()
1620 m.update(U_BOOT_DATA)
1621 self.assertEqual(m.digest(), ''.join(hash_node.value))
1622
1623 def testHashNoAlgo(self):
1624 with self.assertRaises(ValueError) as e:
1625 self._DoReadFileDtb('91_hash_no_algo.dts', update_dtb=True)
1626 self.assertIn("Node \'/binman/u-boot\': Missing \'algo\' property for "
1627 'hash node', str(e.exception))
1628
1629 def testHashBadAlgo(self):
1630 with self.assertRaises(ValueError) as e:
1631 self._DoReadFileDtb('92_hash_bad_algo.dts', update_dtb=True)
1632 self.assertIn("Node '/binman/u-boot': Unknown hash algorithm",
1633 str(e.exception))
1634
1635 def testHashSection(self):
1636 """Test hashing of the contents of an entry"""
1637 _, _, _, out_dtb_fname = self._DoReadFileDtb('99_hash_section.dts',
1638 use_real_dtb=True, update_dtb=True)
1639 dtb = fdt.Fdt(out_dtb_fname)
1640 dtb.Scan()
1641 hash_node = dtb.GetNode('/binman/section/hash').props['value']
1642 m = hashlib.sha256()
1643 m.update(U_BOOT_DATA)
1644 m.update(16 * 'a')
1645 self.assertEqual(m.digest(), ''.join(hash_node.value))
1646
Simon Glass53af22a2018-07-17 13:25:32 -06001647
Simon Glass9fc60b42017-11-12 21:52:22 -07001648if __name__ == "__main__":
1649 unittest.main()