blob: ee951d10cf3bfb3651fcd13c241141ebeca57380 [file] [log] [blame]
Simon Glass96a62962019-07-08 13:18:52 -06001#!/usr/bin/env python
2# SPDX-License-Identifier: GPL-2.0+
3# Copyright 2019 Google LLC
4# Written by Simon Glass <sjg@chromium.org>
5
6"""Tests for cbfs_util
7
8These create and read various CBFSs and compare the results with expected
9values and with cbfstool
10"""
11
Simon Glass96a62962019-07-08 13:18:52 -060012import io
13import os
14import shutil
15import struct
16import tempfile
17import unittest
18
Simon Glassa7c941e2022-01-09 20:13:58 -070019from binman import bintool
Simon Glassc585dd42020-04-17 18:09:03 -060020from binman import cbfs_util
21from binman.cbfs_util import CbfsWriter
22from binman import elf
Simon Glass131444f2023-02-23 18:18:04 -070023from u_boot_pylib import test_util
24from u_boot_pylib import tools
Simon Glass96a62962019-07-08 13:18:52 -060025
26U_BOOT_DATA = b'1234'
27U_BOOT_DTB_DATA = b'udtb'
28COMPRESS_DATA = b'compress xxxxxxxxxxxxxxxxxxxxxx data'
29
30
31class TestCbfs(unittest.TestCase):
32 """Test of cbfs_util classes"""
33 #pylint: disable=W0212
34 @classmethod
35 def setUpClass(cls):
36 # Create a temporary directory for test files
37 cls._indir = tempfile.mkdtemp(prefix='cbfs_util.')
Simon Glass80025522022-01-29 14:14:04 -070038 tools.set_input_dirs([cls._indir])
Simon Glass96a62962019-07-08 13:18:52 -060039
40 # Set up some useful data files
41 TestCbfs._make_input_file('u-boot.bin', U_BOOT_DATA)
42 TestCbfs._make_input_file('u-boot.dtb', U_BOOT_DTB_DATA)
43 TestCbfs._make_input_file('compress', COMPRESS_DATA)
44
45 # Set up a temporary output directory, used by the tools library when
46 # compressing files
Simon Glass80025522022-01-29 14:14:04 -070047 tools.prepare_output_dir(None)
Simon Glass96a62962019-07-08 13:18:52 -060048
Simon Glassa7c941e2022-01-09 20:13:58 -070049 cls.cbfstool = bintool.Bintool.create('cbfstool')
50 cls.have_cbfstool = cls.cbfstool.is_present()
Simon Glass96a62962019-07-08 13:18:52 -060051
Stefan Herbrechtsmeier80c62772022-08-19 16:25:29 +020052 lz4 = bintool.Bintool.create('lz4')
53 cls.have_lz4 = lz4.is_present()
Simon Glass96a62962019-07-08 13:18:52 -060054
55 @classmethod
56 def tearDownClass(cls):
57 """Remove the temporary input directory and its contents"""
58 if cls._indir:
59 shutil.rmtree(cls._indir)
60 cls._indir = None
Simon Glass80025522022-01-29 14:14:04 -070061 tools.finalise_output_dir()
Simon Glass96a62962019-07-08 13:18:52 -060062
63 @classmethod
64 def _make_input_file(cls, fname, contents):
65 """Create a new test input file, creating directories as needed
66
67 Args:
68 fname: Filename to create
69 contents: File contents to write in to the file
70 Returns:
71 Full pathname of file created
72 """
73 pathname = os.path.join(cls._indir, fname)
Simon Glass80025522022-01-29 14:14:04 -070074 tools.write_file(pathname, contents)
Simon Glass96a62962019-07-08 13:18:52 -060075 return pathname
76
77 def _check_hdr(self, data, size, offset=0, arch=cbfs_util.ARCHITECTURE_X86):
78 """Check that the CBFS has the expected header
79
80 Args:
81 data: Data to check
82 size: Expected ROM size
83 offset: Expected offset to first CBFS file
84 arch: Expected architecture
85
86 Returns:
87 CbfsReader object containing the CBFS
88 """
89 cbfs = cbfs_util.CbfsReader(data)
90 self.assertEqual(cbfs_util.HEADER_MAGIC, cbfs.magic)
91 self.assertEqual(cbfs_util.HEADER_VERSION2, cbfs.version)
92 self.assertEqual(size, cbfs.rom_size)
93 self.assertEqual(0, cbfs.boot_block_size)
94 self.assertEqual(cbfs_util.ENTRY_ALIGN, cbfs.align)
95 self.assertEqual(offset, cbfs.cbfs_offset)
96 self.assertEqual(arch, cbfs.arch)
97 return cbfs
98
99 def _check_uboot(self, cbfs, ftype=cbfs_util.TYPE_RAW, offset=0x38,
Simon Glassc2f1aed2019-07-08 13:18:56 -0600100 data=U_BOOT_DATA, cbfs_offset=None):
Simon Glass96a62962019-07-08 13:18:52 -0600101 """Check that the U-Boot file is as expected
102
103 Args:
104 cbfs: CbfsReader object to check
105 ftype: Expected file type
106 offset: Expected offset of file
107 data: Expected data in file
Simon Glassc2f1aed2019-07-08 13:18:56 -0600108 cbfs_offset: Expected CBFS offset for file's data
Simon Glass96a62962019-07-08 13:18:52 -0600109
110 Returns:
111 CbfsFile object containing the file
112 """
113 self.assertIn('u-boot', cbfs.files)
114 cfile = cbfs.files['u-boot']
115 self.assertEqual('u-boot', cfile.name)
116 self.assertEqual(offset, cfile.offset)
Simon Glassc2f1aed2019-07-08 13:18:56 -0600117 if cbfs_offset is not None:
118 self.assertEqual(cbfs_offset, cfile.cbfs_offset)
Simon Glass96a62962019-07-08 13:18:52 -0600119 self.assertEqual(data, cfile.data)
120 self.assertEqual(ftype, cfile.ftype)
121 self.assertEqual(cbfs_util.COMPRESS_NONE, cfile.compress)
122 self.assertEqual(len(data), cfile.memlen)
123 return cfile
124
Simon Glassc2f1aed2019-07-08 13:18:56 -0600125 def _check_dtb(self, cbfs, offset=0x38, data=U_BOOT_DTB_DATA,
126 cbfs_offset=None):
Simon Glass96a62962019-07-08 13:18:52 -0600127 """Check that the U-Boot dtb file is as expected
128
129 Args:
130 cbfs: CbfsReader object to check
131 offset: Expected offset of file
132 data: Expected data in file
Simon Glassc2f1aed2019-07-08 13:18:56 -0600133 cbfs_offset: Expected CBFS offset for file's data
Simon Glass96a62962019-07-08 13:18:52 -0600134 """
135 self.assertIn('u-boot-dtb', cbfs.files)
136 cfile = cbfs.files['u-boot-dtb']
137 self.assertEqual('u-boot-dtb', cfile.name)
138 self.assertEqual(offset, cfile.offset)
Simon Glassc2f1aed2019-07-08 13:18:56 -0600139 if cbfs_offset is not None:
140 self.assertEqual(cbfs_offset, cfile.cbfs_offset)
Simon Glass96a62962019-07-08 13:18:52 -0600141 self.assertEqual(U_BOOT_DTB_DATA, cfile.data)
142 self.assertEqual(cbfs_util.TYPE_RAW, cfile.ftype)
143 self.assertEqual(cbfs_util.COMPRESS_NONE, cfile.compress)
144 self.assertEqual(len(U_BOOT_DTB_DATA), cfile.memlen)
145
146 def _check_raw(self, data, size, offset=0, arch=cbfs_util.ARCHITECTURE_X86):
147 """Check that two raw files are added as expected
148
149 Args:
150 data: Data to check
151 size: Expected ROM size
152 offset: Expected offset to first CBFS file
153 arch: Expected architecture
154 """
155 cbfs = self._check_hdr(data, size, offset=offset, arch=arch)
156 self._check_uboot(cbfs)
157 self._check_dtb(cbfs)
158
Simon Glassc2f1aed2019-07-08 13:18:56 -0600159 def _get_expected_cbfs(self, size, arch='x86', compress=None, base=None):
Simon Glass96a62962019-07-08 13:18:52 -0600160 """Get the file created by cbfstool for a particular scenario
161
162 Args:
163 size: Size of the CBFS in bytes
164 arch: Architecture of the CBFS, as a string
165 compress: Compression to use, e.g. cbfs_util.COMPRESS_LZMA
Simon Glassc2f1aed2019-07-08 13:18:56 -0600166 base: Base address of file, or None to put it anywhere
Simon Glass96a62962019-07-08 13:18:52 -0600167
168 Returns:
169 Resulting CBFS file, or None if cbfstool is not available
170 """
171 if not self.have_cbfstool or not self.have_lz4:
172 return None
173 cbfs_fname = os.path.join(self._indir, 'test.cbfs')
Simon Glassa7c941e2022-01-09 20:13:58 -0700174 self.cbfstool.create_new(cbfs_fname, size, arch)
Simon Glassc2f1aed2019-07-08 13:18:56 -0600175 if base:
176 base = [(1 << 32) - size + b for b in base]
Simon Glassa7c941e2022-01-09 20:13:58 -0700177 self.cbfstool.add_raw(
178 cbfs_fname, 'u-boot',
Simon Glass80025522022-01-29 14:14:04 -0700179 tools.get_input_filename(compress and 'compress' or 'u-boot.bin'),
Simon Glassa7c941e2022-01-09 20:13:58 -0700180 compress[0] if compress else None,
181 base[0] if base else None)
182 self.cbfstool.add_raw(
183 cbfs_fname, 'u-boot-dtb',
Simon Glass80025522022-01-29 14:14:04 -0700184 tools.get_input_filename(compress and 'compress' or 'u-boot.dtb'),
Simon Glassa7c941e2022-01-09 20:13:58 -0700185 compress[1] if compress else None,
186 base[1] if base else None)
Simon Glass96a62962019-07-08 13:18:52 -0600187 return cbfs_fname
188
189 def _compare_expected_cbfs(self, data, cbfstool_fname):
190 """Compare against what cbfstool creates
191
192 This compares what binman creates with what cbfstool creates for what
193 is proportedly the same thing.
194
195 Args:
196 data: CBFS created by binman
197 cbfstool_fname: CBFS created by cbfstool
198 """
199 if not self.have_cbfstool or not self.have_lz4:
200 return
Simon Glass80025522022-01-29 14:14:04 -0700201 expect = tools.read_file(cbfstool_fname)
Simon Glass96a62962019-07-08 13:18:52 -0600202 if expect != data:
Simon Glass80025522022-01-29 14:14:04 -0700203 tools.write_file('/tmp/expect', expect)
204 tools.write_file('/tmp/actual', data)
Simon Glass96a62962019-07-08 13:18:52 -0600205 print('diff -y <(xxd -g1 /tmp/expect) <(xxd -g1 /tmp/actual) | colordiff')
206 self.fail('cbfstool produced a different result')
207
208 def test_cbfs_functions(self):
209 """Test global functions of cbfs_util"""
210 self.assertEqual(cbfs_util.ARCHITECTURE_X86, cbfs_util.find_arch('x86'))
211 self.assertIsNone(cbfs_util.find_arch('bad-arch'))
212
213 self.assertEqual(cbfs_util.COMPRESS_LZMA, cbfs_util.find_compress('lzma'))
214 self.assertIsNone(cbfs_util.find_compress('bad-comp'))
215
216 def test_cbfstool_failure(self):
217 """Test failure to run cbfstool"""
218 if not self.have_cbfstool:
219 self.skipTest('No cbfstool available')
Simon Glassa7c941e2022-01-09 20:13:58 -0700220 with self.assertRaises(ValueError) as exc:
221 out = self.cbfstool.fail()
222 self.assertIn('cbfstool missing-file bad-command', str(exc.exception))
Simon Glass96a62962019-07-08 13:18:52 -0600223
224 def test_cbfs_raw(self):
225 """Test base handling of a Coreboot Filesystem (CBFS)"""
226 size = 0xb0
227 cbw = CbfsWriter(size)
228 cbw.add_file_raw('u-boot', U_BOOT_DATA)
229 cbw.add_file_raw('u-boot-dtb', U_BOOT_DTB_DATA)
230 data = cbw.get_data()
231 self._check_raw(data, size)
232 cbfs_fname = self._get_expected_cbfs(size=size)
233 self._compare_expected_cbfs(data, cbfs_fname)
234
235 def test_cbfs_invalid_file_type(self):
236 """Check handling of an invalid file type when outputiing a CBFS"""
237 size = 0xb0
238 cbw = CbfsWriter(size)
239 cfile = cbw.add_file_raw('u-boot', U_BOOT_DATA)
240
241 # Change the type manually before generating the CBFS, and make sure
242 # that the generator complains
243 cfile.ftype = 0xff
244 with self.assertRaises(ValueError) as e:
245 cbw.get_data()
246 self.assertIn('Unknown type 0xff when writing', str(e.exception))
247
248 def test_cbfs_invalid_file_type_on_read(self):
249 """Check handling of an invalid file type when reading the CBFS"""
250 size = 0xb0
251 cbw = CbfsWriter(size)
252 cbw.add_file_raw('u-boot', U_BOOT_DATA)
253
254 data = cbw.get_data()
255
256 # Read in the first file header
257 cbr = cbfs_util.CbfsReader(data, read=False)
258 with io.BytesIO(data) as fd:
259 self.assertTrue(cbr._find_and_read_header(fd, len(data)))
260 pos = fd.tell()
261 hdr_data = fd.read(cbfs_util.FILE_HEADER_LEN)
262 magic, size, ftype, attr, offset = struct.unpack(
263 cbfs_util.FILE_HEADER_FORMAT, hdr_data)
264
265 # Create a new CBFS with a change to the file type
266 ftype = 0xff
267 newdata = data[:pos]
268 newdata += struct.pack(cbfs_util.FILE_HEADER_FORMAT, magic, size, ftype,
269 attr, offset)
270 newdata += data[pos + cbfs_util.FILE_HEADER_LEN:]
271
272 # Read in this CBFS and make sure that the reader complains
273 with self.assertRaises(ValueError) as e:
274 cbfs_util.CbfsReader(newdata)
275 self.assertIn('Unknown type 0xff when reading', str(e.exception))
276
277 def test_cbfs_no_space(self):
278 """Check handling of running out of space in the CBFS"""
279 size = 0x60
280 cbw = CbfsWriter(size)
281 cbw.add_file_raw('u-boot', U_BOOT_DATA)
282 with self.assertRaises(ValueError) as e:
283 cbw.get_data()
284 self.assertIn('No space for header', str(e.exception))
285
286 def test_cbfs_no_space_skip(self):
287 """Check handling of running out of space in CBFS with file header"""
Simon Glassa61e6fe2019-07-08 13:18:55 -0600288 size = 0x5c
289 cbw = CbfsWriter(size, arch=cbfs_util.ARCHITECTURE_PPC64)
290 cbw._add_fileheader = True
291 cbw.add_file_raw('u-boot', U_BOOT_DATA)
292 with self.assertRaises(ValueError) as e:
293 cbw.get_data()
294 self.assertIn('No space for data before offset', str(e.exception))
295
296 def test_cbfs_no_space_pad(self):
297 """Check handling of running out of space in CBFS with file header"""
Simon Glass96a62962019-07-08 13:18:52 -0600298 size = 0x70
299 cbw = CbfsWriter(size)
300 cbw._add_fileheader = True
301 cbw.add_file_raw('u-boot', U_BOOT_DATA)
302 with self.assertRaises(ValueError) as e:
303 cbw.get_data()
Simon Glassa61e6fe2019-07-08 13:18:55 -0600304 self.assertIn('No space for data before pad offset', str(e.exception))
Simon Glass96a62962019-07-08 13:18:52 -0600305
306 def test_cbfs_bad_header_ptr(self):
307 """Check handling of a bad master-header pointer"""
308 size = 0x70
309 cbw = CbfsWriter(size)
310 cbw.add_file_raw('u-boot', U_BOOT_DATA)
311 data = cbw.get_data()
312
313 # Add one to the pointer to make it invalid
314 newdata = data[:-4] + struct.pack('<I', cbw._header_offset + 1)
315
316 # We should still be able to find the master header by searching
317 with test_util.capture_sys_output() as (stdout, _stderr):
318 cbfs = cbfs_util.CbfsReader(newdata)
319 self.assertIn('Relative offset seems wrong', stdout.getvalue())
320 self.assertIn('u-boot', cbfs.files)
321 self.assertEqual(size, cbfs.rom_size)
322
323 def test_cbfs_bad_header(self):
324 """Check handling of a bad master header"""
325 size = 0x70
326 cbw = CbfsWriter(size)
327 cbw.add_file_raw('u-boot', U_BOOT_DATA)
328 data = cbw.get_data()
329
330 # Drop most of the header and try reading the modified CBFS
331 newdata = data[:cbw._header_offset + 4]
332
333 with test_util.capture_sys_output() as (stdout, _stderr):
334 with self.assertRaises(ValueError) as e:
335 cbfs_util.CbfsReader(newdata)
336 self.assertIn('Relative offset seems wrong', stdout.getvalue())
337 self.assertIn('Cannot find master header', str(e.exception))
338
339 def test_cbfs_bad_file_header(self):
340 """Check handling of a bad file header"""
341 size = 0x70
342 cbw = CbfsWriter(size)
343 cbw.add_file_raw('u-boot', U_BOOT_DATA)
344 data = cbw.get_data()
345
346 # Read in the CBFS master header (only), then stop
347 cbr = cbfs_util.CbfsReader(data, read=False)
348 with io.BytesIO(data) as fd:
349 self.assertTrue(cbr._find_and_read_header(fd, len(data)))
350 pos = fd.tell()
351
352 # Remove all but 4 bytes of the file headerm and try to read the file
353 newdata = data[:pos + 4]
354 with test_util.capture_sys_output() as (stdout, _stderr):
355 with io.BytesIO(newdata) as fd:
356 fd.seek(pos)
357 self.assertEqual(False, cbr._read_next_file(fd))
Simon Glassd4ed3b02019-07-20 12:24:03 -0600358 self.assertIn('File header at 0x0 ran out of data', stdout.getvalue())
Simon Glass96a62962019-07-08 13:18:52 -0600359
360 def test_cbfs_bad_file_string(self):
361 """Check handling of an incomplete filename string"""
362 size = 0x70
363 cbw = CbfsWriter(size)
364 cbw.add_file_raw('16-characters xx', U_BOOT_DATA)
365 data = cbw.get_data()
366
367 # Read in the CBFS master header (only), then stop
368 cbr = cbfs_util.CbfsReader(data, read=False)
369 with io.BytesIO(data) as fd:
370 self.assertTrue(cbr._find_and_read_header(fd, len(data)))
371 pos = fd.tell()
372
373 # Create a new CBFS with only the first 16 bytes of the file name, then
374 # try to read the file
375 newdata = data[:pos + cbfs_util.FILE_HEADER_LEN + 16]
376 with test_util.capture_sys_output() as (stdout, _stderr):
377 with io.BytesIO(newdata) as fd:
378 fd.seek(pos)
379 self.assertEqual(False, cbr._read_next_file(fd))
Simon Glassd4ed3b02019-07-20 12:24:03 -0600380 self.assertIn('String at %#x ran out of data' %
Simon Glass96a62962019-07-08 13:18:52 -0600381 cbfs_util.FILE_HEADER_LEN, stdout.getvalue())
382
383 def test_cbfs_debug(self):
384 """Check debug output"""
385 size = 0x70
386 cbw = CbfsWriter(size)
387 cbw.add_file_raw('u-boot', U_BOOT_DATA)
388 data = cbw.get_data()
389
390 try:
391 cbfs_util.DEBUG = True
392 with test_util.capture_sys_output() as (stdout, _stderr):
393 cbfs_util.CbfsReader(data)
394 self.assertEqual('name u-boot\ndata %s\n' % U_BOOT_DATA,
395 stdout.getvalue())
396 finally:
397 cbfs_util.DEBUG = False
398
399 def test_cbfs_bad_attribute(self):
400 """Check handling of bad attribute tag"""
401 if not self.have_lz4:
402 self.skipTest('lz4 --no-frame-crc not available')
403 size = 0x140
404 cbw = CbfsWriter(size)
Simon Glassc2f1aed2019-07-08 13:18:56 -0600405 cbw.add_file_raw('u-boot', COMPRESS_DATA, None,
Simon Glass96a62962019-07-08 13:18:52 -0600406 compress=cbfs_util.COMPRESS_LZ4)
407 data = cbw.get_data()
408
409 # Search the CBFS for the expected compression tag
410 with io.BytesIO(data) as fd:
411 while True:
412 pos = fd.tell()
413 tag, = struct.unpack('>I', fd.read(4))
414 if tag == cbfs_util.FILE_ATTR_TAG_COMPRESSION:
415 break
416
417 # Create a new CBFS with the tag changed to something invalid
418 newdata = data[:pos] + struct.pack('>I', 0x123) + data[pos + 4:]
419 with test_util.capture_sys_output() as (stdout, _stderr):
420 cbfs_util.CbfsReader(newdata)
421 self.assertEqual('Unknown attribute tag 123\n', stdout.getvalue())
422
423 def test_cbfs_missing_attribute(self):
424 """Check handling of an incomplete attribute tag"""
425 if not self.have_lz4:
426 self.skipTest('lz4 --no-frame-crc not available')
427 size = 0x140
428 cbw = CbfsWriter(size)
Simon Glassc2f1aed2019-07-08 13:18:56 -0600429 cbw.add_file_raw('u-boot', COMPRESS_DATA, None,
Simon Glass96a62962019-07-08 13:18:52 -0600430 compress=cbfs_util.COMPRESS_LZ4)
431 data = cbw.get_data()
432
433 # Read in the CBFS master header (only), then stop
434 cbr = cbfs_util.CbfsReader(data, read=False)
435 with io.BytesIO(data) as fd:
436 self.assertTrue(cbr._find_and_read_header(fd, len(data)))
437 pos = fd.tell()
438
439 # Create a new CBFS with only the first 4 bytes of the compression tag,
440 # then try to read the file
441 tag_pos = pos + cbfs_util.FILE_HEADER_LEN + cbfs_util.FILENAME_ALIGN
442 newdata = data[:tag_pos + 4]
443 with test_util.capture_sys_output() as (stdout, _stderr):
444 with io.BytesIO(newdata) as fd:
445 fd.seek(pos)
446 self.assertEqual(False, cbr._read_next_file(fd))
447 self.assertIn('Attribute tag at %x ran out of data' % tag_pos,
448 stdout.getvalue())
449
450 def test_cbfs_file_master_header(self):
451 """Check handling of a file containing a master header"""
452 size = 0x100
453 cbw = CbfsWriter(size)
454 cbw._add_fileheader = True
455 cbw.add_file_raw('u-boot', U_BOOT_DATA)
456 data = cbw.get_data()
457
458 cbr = cbfs_util.CbfsReader(data)
459 self.assertIn('u-boot', cbr.files)
460 self.assertEqual(size, cbr.rom_size)
461
462 def test_cbfs_arch(self):
463 """Test on non-x86 architecture"""
464 size = 0x100
465 cbw = CbfsWriter(size, arch=cbfs_util.ARCHITECTURE_PPC64)
466 cbw.add_file_raw('u-boot', U_BOOT_DATA)
467 cbw.add_file_raw('u-boot-dtb', U_BOOT_DTB_DATA)
468 data = cbw.get_data()
469 self._check_raw(data, size, offset=0x40,
470 arch=cbfs_util.ARCHITECTURE_PPC64)
471
472 # Compare against what cbfstool creates
473 cbfs_fname = self._get_expected_cbfs(size=size, arch='ppc64')
474 self._compare_expected_cbfs(data, cbfs_fname)
475
476 def test_cbfs_stage(self):
477 """Tests handling of a Coreboot Filesystem (CBFS)"""
478 if not elf.ELF_TOOLS:
479 self.skipTest('Python elftools not available')
480 elf_fname = os.path.join(self._indir, 'cbfs-stage.elf')
481 elf.MakeElf(elf_fname, U_BOOT_DATA, U_BOOT_DTB_DATA)
482
483 size = 0xb0
484 cbw = CbfsWriter(size)
Simon Glass80025522022-01-29 14:14:04 -0700485 cbw.add_file_stage('u-boot', tools.read_file(elf_fname))
Simon Glass96a62962019-07-08 13:18:52 -0600486
487 data = cbw.get_data()
488 cbfs = self._check_hdr(data, size)
489 load = 0xfef20000
490 entry = load + 2
491
492 cfile = self._check_uboot(cbfs, cbfs_util.TYPE_STAGE, offset=0x28,
493 data=U_BOOT_DATA + U_BOOT_DTB_DATA)
494
495 self.assertEqual(entry, cfile.entry)
496 self.assertEqual(load, cfile.load)
497 self.assertEqual(len(U_BOOT_DATA) + len(U_BOOT_DTB_DATA),
498 cfile.data_len)
499
500 # Compare against what cbfstool creates
501 if self.have_cbfstool:
502 cbfs_fname = os.path.join(self._indir, 'test.cbfs')
Simon Glassa7c941e2022-01-09 20:13:58 -0700503 self.cbfstool.create_new(cbfs_fname, size)
504 self.cbfstool.add_stage(cbfs_fname, 'u-boot', elf_fname)
Simon Glass96a62962019-07-08 13:18:52 -0600505 self._compare_expected_cbfs(data, cbfs_fname)
506
507 def test_cbfs_raw_compress(self):
508 """Test base handling of compressing raw files"""
509 if not self.have_lz4:
510 self.skipTest('lz4 --no-frame-crc not available')
511 size = 0x140
512 cbw = CbfsWriter(size)
Simon Glassc2f1aed2019-07-08 13:18:56 -0600513 cbw.add_file_raw('u-boot', COMPRESS_DATA, None,
Simon Glass96a62962019-07-08 13:18:52 -0600514 compress=cbfs_util.COMPRESS_LZ4)
Simon Glassc2f1aed2019-07-08 13:18:56 -0600515 cbw.add_file_raw('u-boot-dtb', COMPRESS_DATA, None,
Simon Glass96a62962019-07-08 13:18:52 -0600516 compress=cbfs_util.COMPRESS_LZMA)
517 data = cbw.get_data()
518
519 cbfs = self._check_hdr(data, size)
520 self.assertIn('u-boot', cbfs.files)
521 cfile = cbfs.files['u-boot']
522 self.assertEqual(cfile.name, 'u-boot')
523 self.assertEqual(cfile.offset, 56)
524 self.assertEqual(cfile.data, COMPRESS_DATA)
525 self.assertEqual(cfile.ftype, cbfs_util.TYPE_RAW)
526 self.assertEqual(cfile.compress, cbfs_util.COMPRESS_LZ4)
527 self.assertEqual(cfile.memlen, len(COMPRESS_DATA))
528
529 self.assertIn('u-boot-dtb', cbfs.files)
530 cfile = cbfs.files['u-boot-dtb']
531 self.assertEqual(cfile.name, 'u-boot-dtb')
532 self.assertEqual(cfile.offset, 56)
533 self.assertEqual(cfile.data, COMPRESS_DATA)
534 self.assertEqual(cfile.ftype, cbfs_util.TYPE_RAW)
535 self.assertEqual(cfile.compress, cbfs_util.COMPRESS_LZMA)
536 self.assertEqual(cfile.memlen, len(COMPRESS_DATA))
537
538 cbfs_fname = self._get_expected_cbfs(size=size, compress=['lz4', 'lzma'])
539 self._compare_expected_cbfs(data, cbfs_fname)
540
Simon Glassa61e6fe2019-07-08 13:18:55 -0600541 def test_cbfs_raw_space(self):
542 """Test files with unused space in the CBFS"""
543 size = 0xf0
544 cbw = CbfsWriter(size)
545 cbw.add_file_raw('u-boot', U_BOOT_DATA)
546 cbw.add_file_raw('u-boot-dtb', U_BOOT_DTB_DATA)
547 data = cbw.get_data()
548 self._check_raw(data, size)
549 cbfs_fname = self._get_expected_cbfs(size=size)
550 self._compare_expected_cbfs(data, cbfs_fname)
551
Simon Glassc2f1aed2019-07-08 13:18:56 -0600552 def test_cbfs_offset(self):
553 """Test a CBFS with files at particular offsets"""
554 size = 0x200
555 cbw = CbfsWriter(size)
556 cbw.add_file_raw('u-boot', U_BOOT_DATA, 0x40)
557 cbw.add_file_raw('u-boot-dtb', U_BOOT_DTB_DATA, 0x140)
558
559 data = cbw.get_data()
560 cbfs = self._check_hdr(data, size)
561 self._check_uboot(cbfs, ftype=cbfs_util.TYPE_RAW, offset=0x40,
562 cbfs_offset=0x40)
563 self._check_dtb(cbfs, offset=0x40, cbfs_offset=0x140)
564
565 cbfs_fname = self._get_expected_cbfs(size=size, base=(0x40, 0x140))
566 self._compare_expected_cbfs(data, cbfs_fname)
567
568 def test_cbfs_invalid_file_type_header(self):
569 """Check handling of an invalid file type when outputting a header"""
570 size = 0xb0
571 cbw = CbfsWriter(size)
572 cfile = cbw.add_file_raw('u-boot', U_BOOT_DATA, 0)
573
574 # Change the type manually before generating the CBFS, and make sure
575 # that the generator complains
576 cfile.ftype = 0xff
577 with self.assertRaises(ValueError) as e:
578 cbw.get_data()
579 self.assertIn('Unknown file type 0xff', str(e.exception))
580
581 def test_cbfs_offset_conflict(self):
582 """Test a CBFS with files that want to overlap"""
583 size = 0x200
584 cbw = CbfsWriter(size)
585 cbw.add_file_raw('u-boot', U_BOOT_DATA, 0x40)
586 cbw.add_file_raw('u-boot-dtb', U_BOOT_DTB_DATA, 0x80)
587
588 with self.assertRaises(ValueError) as e:
589 cbw.get_data()
590 self.assertIn('No space for data before pad offset', str(e.exception))
591
592 def test_cbfs_check_offset(self):
593 """Test that we can discover the offset of a file after writing it"""
594 size = 0xb0
595 cbw = CbfsWriter(size)
596 cbw.add_file_raw('u-boot', U_BOOT_DATA)
597 cbw.add_file_raw('u-boot-dtb', U_BOOT_DTB_DATA)
598 data = cbw.get_data()
599
600 cbfs = cbfs_util.CbfsReader(data)
601 self.assertEqual(0x38, cbfs.files['u-boot'].cbfs_offset)
602 self.assertEqual(0x78, cbfs.files['u-boot-dtb'].cbfs_offset)
603
Simon Glass96a62962019-07-08 13:18:52 -0600604
605if __name__ == '__main__':
606 unittest.main()