]>
git.ipfire.org Git - thirdparty/u-boot.git/blob - test/py/tests/test_fit.py
1 # SPDX-License-Identifier: GPL-2.0+
2 # Copyright (c) 2013, Google Inc.
4 # Sanity check of the FIT handling in U-Boot
9 import u_boot_utils
as util
12 # Define a base ITS which we can adjust using % and a dictionary
17 description = "Chrome OS kernel image with one or more FDT blobs";
22 data = /incbin/("%(kernel)s");
26 compression = "%(compression)s";
31 data = /incbin/("%(loadables1)s");
41 data = /incbin/("%(fdt)s");
45 compression = "%(compression)s";
47 algo = "sha1,rsa2048";
48 key-name-hint = "dev";
53 data = /incbin/("%(ramdisk)s");
58 compression = "%(compression)s";
62 data = /incbin/("%(loadables2)s");
82 # Define a base FDT - currently we don't use anything in this
90 model = "Sandbox Verified Boot Test";
91 compatible = "sandbox";
97 compatible = "sandbox,reset";
103 # This is the U-Boot script that is run for each test. First load the FIT,
104 # then run the 'bootm' command, then save out memory from the places where
105 # we expect 'bootm' to write things. Then quit.
107 host load hostfs 0 %(fit_addr)x %(fit)s
108 fdt addr %(fit_addr)x
109 bootm start %(fit_addr)x
111 host save hostfs 0 %(kernel_addr)x %(kernel_out)s %(kernel_size)x
112 host save hostfs 0 %(fdt_addr)x %(fdt_out)s %(fdt_size)x
113 host save hostfs 0 %(ramdisk_addr)x %(ramdisk_out)s %(ramdisk_size)x
114 host save hostfs 0 %(loadables1_addr)x %(loadables1_out)s %(loadables1_size)x
115 host save hostfs 0 %(loadables2_addr)x %(loadables2_out)s %(loadables2_size)x
118 @pytest.mark
.boardspec('sandbox')
119 @pytest.mark
.buildconfigspec('fit_signature')
120 @pytest.mark
.requiredtool('dtc')
121 def test_fit(u_boot_console
):
122 def make_fname(leaf
):
123 """Make a temporary filename
126 leaf: Leaf name of file to create (within temporary directory)
130 return os
.path
.join(cons
.config
.build_dir
, leaf
)
133 """Get the size of a file
136 fname: Filename to check
138 Size of file in bytes
140 return os
.stat(fname
).st_size
142 def read_file(fname
):
143 """Read the contents of a file
146 fname: Filename to read
148 Contents of file as a string
150 with
open(fname
, 'rb') as fd
:
153 def make_ramdisk(filename
, text
):
154 """Make a sample ramdisk with test data
157 Filename of ramdisk created
159 fname
= make_fname(filename
)
162 data
+= '%s %d was seldom used in the middle ages\n' % (text
, i
)
163 with
open(fname
, 'w') as fd
:
167 def make_compressed(filename
):
168 util
.run_and_log(cons
, ['gzip', '-f', '-k', filename
])
169 return filename
+ '.gz'
171 def find_matching(text
, match
):
172 """Find a match in a line of text, and return the unmatched line portion
174 This is used to extract a part of a line from some text. The match string
175 is used to locate the line - we use the first line that contains that
178 Once we find a match, we discard the match string itself from the line,
179 and return what remains.
181 TODO: If this function becomes more generally useful, we could change it
182 to use regex and return groups.
185 text: Text to check (list of strings, one for each command issued)
186 match: String to search for
188 String containing unmatched portion of line
190 ValueError: If match is not found
192 >>> find_matching(['first line:10', 'second_line:20'], 'first line:')
194 >>> find_matching(['first line:10', 'second_line:20'], 'second line')
195 Traceback (most recent call last):
197 ValueError: Test aborted
198 >>> find_matching('first line:10\', 'second_line:20'], 'second_line:')
200 >>> find_matching('first line:10\', 'second_line:20\nthird_line:30'],
204 __tracebackhide__
= True
205 for line
in '\n'.join(text
).splitlines():
206 pos
= line
.find(match
)
208 return line
[:pos
] + line
[pos
+ len(match
):]
210 pytest
.fail("Expected '%s' but not found in output")
212 def check_equal(expected_fname
, actual_fname
, failure_msg
):
213 """Check that a file matches its expected contents
215 This is always used on out-buffers whose size is decided by the test
216 script anyway, which in some cases may be larger than what we're
217 actually looking for. So it's safe to truncate it to the size of the
221 expected_fname: Filename containing expected contents
222 actual_fname: Filename containing actual contents
223 failure_msg: Message to print on failure
225 expected_data
= read_file(expected_fname
)
226 actual_data
= read_file(actual_fname
)
227 if len(expected_data
) < len(actual_data
):
228 actual_data
= actual_data
[:len(expected_data
)]
229 assert expected_data
== actual_data
, failure_msg
231 def check_not_equal(expected_fname
, actual_fname
, failure_msg
):
232 """Check that a file does not match its expected contents
235 expected_fname: Filename containing expected contents
236 actual_fname: Filename containing actual contents
237 failure_msg: Message to print on failure
239 expected_data
= read_file(expected_fname
)
240 actual_data
= read_file(actual_fname
)
241 assert expected_data
!= actual_data
, failure_msg
243 def run_fit_test(mkimage
):
244 """Basic sanity check of FIT loading in U-Boot
246 TODO: Almost everything:
247 - hash algorithms - invalid hash/contents should be detected
248 - signature algorithms - invalid sig/contents should be detected
250 - checking that errors are detected like:
253 - invalid configurations
254 - incorrect os/arch/type fields
256 - images too large/small
257 - invalid FDT (e.g. putting a random binary in instead)
258 - default configuration selection
259 - bootm command line parameters should have desired effect
260 - run code coverage to make sure we are testing all the code
262 # Set up invariant files
263 control_dtb
= fit_util
.make_dtb(cons
, base_fdt
, 'u-boot')
264 kernel
= fit_util
.make_kernel(cons
, 'test-kernel.bin', 'kernel')
265 ramdisk
= make_ramdisk('test-ramdisk.bin', 'ramdisk')
266 loadables1
= fit_util
.make_kernel(cons
, 'test-loadables1.bin', 'lenrek')
267 loadables2
= make_ramdisk('test-loadables2.bin', 'ksidmar')
268 kernel_out
= make_fname('kernel-out.bin')
269 fdt
= make_fname('u-boot.dtb')
270 fdt_out
= make_fname('fdt-out.dtb')
271 ramdisk_out
= make_fname('ramdisk-out.bin')
272 loadables1_out
= make_fname('loadables1-out.bin')
273 loadables2_out
= make_fname('loadables2-out.bin')
275 # Set up basic parameters with default values
280 'kernel_out' : kernel_out
,
281 'kernel_addr' : 0x40000,
282 'kernel_size' : filesize(kernel
),
286 'fdt_addr' : 0x80000,
287 'fdt_size' : filesize(control_dtb
),
291 'ramdisk_out' : ramdisk_out
,
292 'ramdisk_addr' : 0xc0000,
293 'ramdisk_size' : filesize(ramdisk
),
295 'ramdisk_config' : '',
297 'loadables1' : loadables1
,
298 'loadables1_out' : loadables1_out
,
299 'loadables1_addr' : 0x100000,
300 'loadables1_size' : filesize(loadables1
),
301 'loadables1_load' : '',
303 'loadables2' : loadables2
,
304 'loadables2_out' : loadables2_out
,
305 'loadables2_addr' : 0x140000,
306 'loadables2_size' : filesize(loadables2
),
307 'loadables2_load' : '',
309 'loadables_config' : '',
310 'compression' : 'none',
313 # Make a basic FIT and a script to load it
314 fit
= fit_util
.make_fit(cons
, mkimage
, base_its
, params
)
316 cmd
= base_script
% params
318 # First check that we can load a kernel
319 # We could perhaps reduce duplication with some loss of readability
320 cons
.config
.dtb
= control_dtb
322 with cons
.log
.section('Kernel load'):
323 output
= cons
.run_command_list(cmd
.splitlines())
324 check_equal(kernel
, kernel_out
, 'Kernel not loaded')
325 check_not_equal(control_dtb
, fdt_out
,
326 'FDT loaded but should be ignored')
327 check_not_equal(ramdisk
, ramdisk_out
,
328 'Ramdisk loaded but should not be')
330 # Find out the offset in the FIT where U-Boot has found the FDT
331 line
= find_matching(output
, 'Booting using the fdt blob at ')
332 fit_offset
= int(line
, 16) - params
['fit_addr']
333 fdt_magic
= struct
.pack('>L', 0xd00dfeed)
334 data
= read_file(fit
)
336 # Now find where it actually is in the FIT (skip the first word)
337 real_fit_offset
= data
.find(fdt_magic
, 4)
338 assert fit_offset
== real_fit_offset
, (
339 'U-Boot loaded FDT from offset %#x, FDT is actually at %#x' %
340 (fit_offset
, real_fit_offset
))
342 # Check if bootargs strings substitution works
343 output
= cons
.run_command_list([
344 'env set bootargs \\\"\'my_boot_var=${foo}\'\\\"',
347 'env print bootargs'])
348 assert 'bootargs="my_boot_var=bar"' in output
, "Bootargs strings not substituted"
350 # Now a kernel and an FDT
351 with cons
.log
.section('Kernel + FDT load'):
352 params
['fdt_load'] = 'load = <%#x>;' % params
['fdt_addr']
353 fit
= fit_util
.make_fit(cons
, mkimage
, base_its
, params
)
355 output
= cons
.run_command_list(cmd
.splitlines())
356 check_equal(kernel
, kernel_out
, 'Kernel not loaded')
357 check_equal(control_dtb
, fdt_out
, 'FDT not loaded')
358 check_not_equal(ramdisk
, ramdisk_out
,
359 'Ramdisk loaded but should not be')
362 with cons
.log
.section('Kernel + FDT + Ramdisk load'):
363 params
['ramdisk_config'] = 'ramdisk = "ramdisk-1";'
364 params
['ramdisk_load'] = 'load = <%#x>;' % params
['ramdisk_addr']
365 fit
= fit_util
.make_fit(cons
, mkimage
, base_its
, params
)
367 output
= cons
.run_command_list(cmd
.splitlines())
368 check_equal(ramdisk
, ramdisk_out
, 'Ramdisk not loaded')
370 # Configuration with some Loadables
371 with cons
.log
.section('Kernel + FDT + Ramdisk load + Loadables'):
372 params
['loadables_config'] = 'loadables = "kernel-2", "ramdisk-2";'
373 params
['loadables1_load'] = ('load = <%#x>;' %
374 params
['loadables1_addr'])
375 params
['loadables2_load'] = ('load = <%#x>;' %
376 params
['loadables2_addr'])
377 fit
= fit_util
.make_fit(cons
, mkimage
, base_its
, params
)
379 output
= cons
.run_command_list(cmd
.splitlines())
380 check_equal(loadables1
, loadables1_out
,
381 'Loadables1 (kernel) not loaded')
382 check_equal(loadables2
, loadables2_out
,
383 'Loadables2 (ramdisk) not loaded')
385 # Kernel, FDT and Ramdisk all compressed
386 with cons
.log
.section('(Kernel + FDT + Ramdisk) compressed'):
387 params
['compression'] = 'gzip'
388 params
['kernel'] = make_compressed(kernel
)
389 params
['fdt'] = make_compressed(fdt
)
390 params
['ramdisk'] = make_compressed(ramdisk
)
391 fit
= fit_util
.make_fit(cons
, mkimage
, base_its
, params
)
393 output
= cons
.run_command_list(cmd
.splitlines())
394 check_equal(kernel
, kernel_out
, 'Kernel not loaded')
395 check_equal(control_dtb
, fdt_out
, 'FDT not loaded')
396 check_not_equal(ramdisk
, ramdisk_out
, 'Ramdisk got decompressed?')
397 check_equal(ramdisk
+ '.gz', ramdisk_out
, 'Ramdist not loaded')
400 cons
= u_boot_console
401 # We need to use our own device tree file. Remember to restore it
403 old_dtb
= cons
.config
.dtb
405 mkimage
= cons
.config
.build_dir
+ '/tools/mkimage'
406 run_fit_test(mkimage
)
408 # Go back to the original U-Boot with the correct dtb.
409 cons
.config
.dtb
= old_dtb