Simon Glass | 96a6296 | 2019-07-08 13:18:52 -0600 | [diff] [blame] | 1 | #!/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 | |
| 8 | These create and read various CBFSs and compare the results with expected |
| 9 | values and with cbfstool |
| 10 | """ |
| 11 | |
| 12 | from __future__ import print_function |
| 13 | |
| 14 | import io |
| 15 | import os |
| 16 | import shutil |
| 17 | import struct |
| 18 | import tempfile |
| 19 | import unittest |
| 20 | |
| 21 | import cbfs_util |
| 22 | from cbfs_util import CbfsWriter |
| 23 | import elf |
| 24 | import test_util |
| 25 | import tools |
| 26 | |
| 27 | U_BOOT_DATA = b'1234' |
| 28 | U_BOOT_DTB_DATA = b'udtb' |
| 29 | COMPRESS_DATA = b'compress xxxxxxxxxxxxxxxxxxxxxx data' |
| 30 | |
| 31 | |
| 32 | class TestCbfs(unittest.TestCase): |
| 33 | """Test of cbfs_util classes""" |
| 34 | #pylint: disable=W0212 |
| 35 | @classmethod |
| 36 | def setUpClass(cls): |
| 37 | # Create a temporary directory for test files |
| 38 | cls._indir = tempfile.mkdtemp(prefix='cbfs_util.') |
| 39 | tools.SetInputDirs([cls._indir]) |
| 40 | |
| 41 | # Set up some useful data files |
| 42 | TestCbfs._make_input_file('u-boot.bin', U_BOOT_DATA) |
| 43 | TestCbfs._make_input_file('u-boot.dtb', U_BOOT_DTB_DATA) |
| 44 | TestCbfs._make_input_file('compress', COMPRESS_DATA) |
| 45 | |
| 46 | # Set up a temporary output directory, used by the tools library when |
| 47 | # compressing files |
| 48 | tools.PrepareOutputDir(None) |
| 49 | |
| 50 | cls.have_cbfstool = True |
| 51 | try: |
| 52 | tools.Run('which', 'cbfstool') |
| 53 | except: |
| 54 | cls.have_cbfstool = False |
| 55 | |
| 56 | cls.have_lz4 = True |
| 57 | try: |
| 58 | tools.Run('lz4', '--no-frame-crc', '-c', |
| 59 | tools.GetInputFilename('u-boot.bin')) |
| 60 | except: |
| 61 | cls.have_lz4 = False |
| 62 | |
| 63 | @classmethod |
| 64 | def tearDownClass(cls): |
| 65 | """Remove the temporary input directory and its contents""" |
| 66 | if cls._indir: |
| 67 | shutil.rmtree(cls._indir) |
| 68 | cls._indir = None |
| 69 | tools.FinaliseOutputDir() |
| 70 | |
| 71 | @classmethod |
| 72 | def _make_input_file(cls, fname, contents): |
| 73 | """Create a new test input file, creating directories as needed |
| 74 | |
| 75 | Args: |
| 76 | fname: Filename to create |
| 77 | contents: File contents to write in to the file |
| 78 | Returns: |
| 79 | Full pathname of file created |
| 80 | """ |
| 81 | pathname = os.path.join(cls._indir, fname) |
| 82 | tools.WriteFile(pathname, contents) |
| 83 | return pathname |
| 84 | |
| 85 | def _check_hdr(self, data, size, offset=0, arch=cbfs_util.ARCHITECTURE_X86): |
| 86 | """Check that the CBFS has the expected header |
| 87 | |
| 88 | Args: |
| 89 | data: Data to check |
| 90 | size: Expected ROM size |
| 91 | offset: Expected offset to first CBFS file |
| 92 | arch: Expected architecture |
| 93 | |
| 94 | Returns: |
| 95 | CbfsReader object containing the CBFS |
| 96 | """ |
| 97 | cbfs = cbfs_util.CbfsReader(data) |
| 98 | self.assertEqual(cbfs_util.HEADER_MAGIC, cbfs.magic) |
| 99 | self.assertEqual(cbfs_util.HEADER_VERSION2, cbfs.version) |
| 100 | self.assertEqual(size, cbfs.rom_size) |
| 101 | self.assertEqual(0, cbfs.boot_block_size) |
| 102 | self.assertEqual(cbfs_util.ENTRY_ALIGN, cbfs.align) |
| 103 | self.assertEqual(offset, cbfs.cbfs_offset) |
| 104 | self.assertEqual(arch, cbfs.arch) |
| 105 | return cbfs |
| 106 | |
| 107 | def _check_uboot(self, cbfs, ftype=cbfs_util.TYPE_RAW, offset=0x38, |
| 108 | data=U_BOOT_DATA): |
| 109 | """Check that the U-Boot file is as expected |
| 110 | |
| 111 | Args: |
| 112 | cbfs: CbfsReader object to check |
| 113 | ftype: Expected file type |
| 114 | offset: Expected offset of file |
| 115 | data: Expected data in file |
| 116 | |
| 117 | Returns: |
| 118 | CbfsFile object containing the file |
| 119 | """ |
| 120 | self.assertIn('u-boot', cbfs.files) |
| 121 | cfile = cbfs.files['u-boot'] |
| 122 | self.assertEqual('u-boot', cfile.name) |
| 123 | self.assertEqual(offset, cfile.offset) |
| 124 | self.assertEqual(data, cfile.data) |
| 125 | self.assertEqual(ftype, cfile.ftype) |
| 126 | self.assertEqual(cbfs_util.COMPRESS_NONE, cfile.compress) |
| 127 | self.assertEqual(len(data), cfile.memlen) |
| 128 | return cfile |
| 129 | |
| 130 | def _check_dtb(self, cbfs, offset=0x38, data=U_BOOT_DTB_DATA): |
| 131 | """Check that the U-Boot dtb file is as expected |
| 132 | |
| 133 | Args: |
| 134 | cbfs: CbfsReader object to check |
| 135 | offset: Expected offset of file |
| 136 | data: Expected data in file |
| 137 | """ |
| 138 | self.assertIn('u-boot-dtb', cbfs.files) |
| 139 | cfile = cbfs.files['u-boot-dtb'] |
| 140 | self.assertEqual('u-boot-dtb', cfile.name) |
| 141 | self.assertEqual(offset, cfile.offset) |
| 142 | self.assertEqual(U_BOOT_DTB_DATA, cfile.data) |
| 143 | self.assertEqual(cbfs_util.TYPE_RAW, cfile.ftype) |
| 144 | self.assertEqual(cbfs_util.COMPRESS_NONE, cfile.compress) |
| 145 | self.assertEqual(len(U_BOOT_DTB_DATA), cfile.memlen) |
| 146 | |
| 147 | def _check_raw(self, data, size, offset=0, arch=cbfs_util.ARCHITECTURE_X86): |
| 148 | """Check that two raw files are added as expected |
| 149 | |
| 150 | Args: |
| 151 | data: Data to check |
| 152 | size: Expected ROM size |
| 153 | offset: Expected offset to first CBFS file |
| 154 | arch: Expected architecture |
| 155 | """ |
| 156 | cbfs = self._check_hdr(data, size, offset=offset, arch=arch) |
| 157 | self._check_uboot(cbfs) |
| 158 | self._check_dtb(cbfs) |
| 159 | |
| 160 | def _get_expected_cbfs(self, size, arch='x86', compress=None): |
| 161 | """Get the file created by cbfstool for a particular scenario |
| 162 | |
| 163 | Args: |
| 164 | size: Size of the CBFS in bytes |
| 165 | arch: Architecture of the CBFS, as a string |
| 166 | compress: Compression to use, e.g. cbfs_util.COMPRESS_LZMA |
| 167 | |
| 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') |
| 174 | cbfs_util.cbfstool(cbfs_fname, 'create', '-m', arch, '-s', '%#x' % size) |
| 175 | cbfs_util.cbfstool(cbfs_fname, 'add', '-n', 'u-boot', '-t', 'raw', |
| 176 | '-c', compress and compress[0] or 'none', |
| 177 | '-f', tools.GetInputFilename( |
| 178 | compress and 'compress' or 'u-boot.bin')) |
| 179 | cbfs_util.cbfstool(cbfs_fname, 'add', '-n', 'u-boot-dtb', '-t', 'raw', |
| 180 | '-c', compress and compress[1] or 'none', |
| 181 | '-f', tools.GetInputFilename( |
| 182 | compress and 'compress' or 'u-boot.dtb')) |
| 183 | return cbfs_fname |
| 184 | |
| 185 | def _compare_expected_cbfs(self, data, cbfstool_fname): |
| 186 | """Compare against what cbfstool creates |
| 187 | |
| 188 | This compares what binman creates with what cbfstool creates for what |
| 189 | is proportedly the same thing. |
| 190 | |
| 191 | Args: |
| 192 | data: CBFS created by binman |
| 193 | cbfstool_fname: CBFS created by cbfstool |
| 194 | """ |
| 195 | if not self.have_cbfstool or not self.have_lz4: |
| 196 | return |
| 197 | expect = tools.ReadFile(cbfstool_fname) |
| 198 | if expect != data: |
| 199 | tools.WriteFile('/tmp/expect', expect) |
| 200 | tools.WriteFile('/tmp/actual', data) |
| 201 | print('diff -y <(xxd -g1 /tmp/expect) <(xxd -g1 /tmp/actual) | colordiff') |
| 202 | self.fail('cbfstool produced a different result') |
| 203 | |
| 204 | def test_cbfs_functions(self): |
| 205 | """Test global functions of cbfs_util""" |
| 206 | self.assertEqual(cbfs_util.ARCHITECTURE_X86, cbfs_util.find_arch('x86')) |
| 207 | self.assertIsNone(cbfs_util.find_arch('bad-arch')) |
| 208 | |
| 209 | self.assertEqual(cbfs_util.COMPRESS_LZMA, cbfs_util.find_compress('lzma')) |
| 210 | self.assertIsNone(cbfs_util.find_compress('bad-comp')) |
| 211 | |
| 212 | def test_cbfstool_failure(self): |
| 213 | """Test failure to run cbfstool""" |
| 214 | if not self.have_cbfstool: |
| 215 | self.skipTest('No cbfstool available') |
| 216 | try: |
| 217 | # In verbose mode this test fails since stderr is not captured. Fix |
| 218 | # this by turning off verbosity. |
| 219 | old_verbose = cbfs_util.VERBOSE |
| 220 | cbfs_util.VERBOSE = False |
| 221 | with test_util.capture_sys_output() as (_stdout, stderr): |
| 222 | with self.assertRaises(Exception) as e: |
| 223 | cbfs_util.cbfstool('missing-file', 'bad-command') |
| 224 | finally: |
| 225 | cbfs_util.VERBOSE = old_verbose |
| 226 | self.assertIn('Unknown command', stderr.getvalue()) |
| 227 | self.assertIn('Failed to run', str(e.exception)) |
| 228 | |
| 229 | def test_cbfs_raw(self): |
| 230 | """Test base handling of a Coreboot Filesystem (CBFS)""" |
| 231 | size = 0xb0 |
| 232 | cbw = CbfsWriter(size) |
| 233 | cbw.add_file_raw('u-boot', U_BOOT_DATA) |
| 234 | cbw.add_file_raw('u-boot-dtb', U_BOOT_DTB_DATA) |
| 235 | data = cbw.get_data() |
| 236 | self._check_raw(data, size) |
| 237 | cbfs_fname = self._get_expected_cbfs(size=size) |
| 238 | self._compare_expected_cbfs(data, cbfs_fname) |
| 239 | |
| 240 | def test_cbfs_invalid_file_type(self): |
| 241 | """Check handling of an invalid file type when outputiing a CBFS""" |
| 242 | size = 0xb0 |
| 243 | cbw = CbfsWriter(size) |
| 244 | cfile = cbw.add_file_raw('u-boot', U_BOOT_DATA) |
| 245 | |
| 246 | # Change the type manually before generating the CBFS, and make sure |
| 247 | # that the generator complains |
| 248 | cfile.ftype = 0xff |
| 249 | with self.assertRaises(ValueError) as e: |
| 250 | cbw.get_data() |
| 251 | self.assertIn('Unknown type 0xff when writing', str(e.exception)) |
| 252 | |
| 253 | def test_cbfs_invalid_file_type_on_read(self): |
| 254 | """Check handling of an invalid file type when reading the CBFS""" |
| 255 | size = 0xb0 |
| 256 | cbw = CbfsWriter(size) |
| 257 | cbw.add_file_raw('u-boot', U_BOOT_DATA) |
| 258 | |
| 259 | data = cbw.get_data() |
| 260 | |
| 261 | # Read in the first file header |
| 262 | cbr = cbfs_util.CbfsReader(data, read=False) |
| 263 | with io.BytesIO(data) as fd: |
| 264 | self.assertTrue(cbr._find_and_read_header(fd, len(data))) |
| 265 | pos = fd.tell() |
| 266 | hdr_data = fd.read(cbfs_util.FILE_HEADER_LEN) |
| 267 | magic, size, ftype, attr, offset = struct.unpack( |
| 268 | cbfs_util.FILE_HEADER_FORMAT, hdr_data) |
| 269 | |
| 270 | # Create a new CBFS with a change to the file type |
| 271 | ftype = 0xff |
| 272 | newdata = data[:pos] |
| 273 | newdata += struct.pack(cbfs_util.FILE_HEADER_FORMAT, magic, size, ftype, |
| 274 | attr, offset) |
| 275 | newdata += data[pos + cbfs_util.FILE_HEADER_LEN:] |
| 276 | |
| 277 | # Read in this CBFS and make sure that the reader complains |
| 278 | with self.assertRaises(ValueError) as e: |
| 279 | cbfs_util.CbfsReader(newdata) |
| 280 | self.assertIn('Unknown type 0xff when reading', str(e.exception)) |
| 281 | |
| 282 | def test_cbfs_no_space(self): |
| 283 | """Check handling of running out of space in the CBFS""" |
| 284 | size = 0x60 |
| 285 | cbw = CbfsWriter(size) |
| 286 | cbw.add_file_raw('u-boot', U_BOOT_DATA) |
| 287 | with self.assertRaises(ValueError) as e: |
| 288 | cbw.get_data() |
| 289 | self.assertIn('No space for header', str(e.exception)) |
| 290 | |
| 291 | def test_cbfs_no_space_skip(self): |
| 292 | """Check handling of running out of space in CBFS with file header""" |
Simon Glass | a61e6fe | 2019-07-08 13:18:55 -0600 | [diff] [blame^] | 293 | size = 0x5c |
| 294 | cbw = CbfsWriter(size, arch=cbfs_util.ARCHITECTURE_PPC64) |
| 295 | cbw._add_fileheader = True |
| 296 | cbw.add_file_raw('u-boot', U_BOOT_DATA) |
| 297 | with self.assertRaises(ValueError) as e: |
| 298 | cbw.get_data() |
| 299 | self.assertIn('No space for data before offset', str(e.exception)) |
| 300 | |
| 301 | def test_cbfs_no_space_pad(self): |
| 302 | """Check handling of running out of space in CBFS with file header""" |
Simon Glass | 96a6296 | 2019-07-08 13:18:52 -0600 | [diff] [blame] | 303 | size = 0x70 |
| 304 | cbw = CbfsWriter(size) |
| 305 | cbw._add_fileheader = True |
| 306 | cbw.add_file_raw('u-boot', U_BOOT_DATA) |
| 307 | with self.assertRaises(ValueError) as e: |
| 308 | cbw.get_data() |
Simon Glass | a61e6fe | 2019-07-08 13:18:55 -0600 | [diff] [blame^] | 309 | self.assertIn('No space for data before pad offset', str(e.exception)) |
Simon Glass | 96a6296 | 2019-07-08 13:18:52 -0600 | [diff] [blame] | 310 | |
| 311 | def test_cbfs_bad_header_ptr(self): |
| 312 | """Check handling of a bad master-header pointer""" |
| 313 | size = 0x70 |
| 314 | cbw = CbfsWriter(size) |
| 315 | cbw.add_file_raw('u-boot', U_BOOT_DATA) |
| 316 | data = cbw.get_data() |
| 317 | |
| 318 | # Add one to the pointer to make it invalid |
| 319 | newdata = data[:-4] + struct.pack('<I', cbw._header_offset + 1) |
| 320 | |
| 321 | # We should still be able to find the master header by searching |
| 322 | with test_util.capture_sys_output() as (stdout, _stderr): |
| 323 | cbfs = cbfs_util.CbfsReader(newdata) |
| 324 | self.assertIn('Relative offset seems wrong', stdout.getvalue()) |
| 325 | self.assertIn('u-boot', cbfs.files) |
| 326 | self.assertEqual(size, cbfs.rom_size) |
| 327 | |
| 328 | def test_cbfs_bad_header(self): |
| 329 | """Check handling of a bad master header""" |
| 330 | size = 0x70 |
| 331 | cbw = CbfsWriter(size) |
| 332 | cbw.add_file_raw('u-boot', U_BOOT_DATA) |
| 333 | data = cbw.get_data() |
| 334 | |
| 335 | # Drop most of the header and try reading the modified CBFS |
| 336 | newdata = data[:cbw._header_offset + 4] |
| 337 | |
| 338 | with test_util.capture_sys_output() as (stdout, _stderr): |
| 339 | with self.assertRaises(ValueError) as e: |
| 340 | cbfs_util.CbfsReader(newdata) |
| 341 | self.assertIn('Relative offset seems wrong', stdout.getvalue()) |
| 342 | self.assertIn('Cannot find master header', str(e.exception)) |
| 343 | |
| 344 | def test_cbfs_bad_file_header(self): |
| 345 | """Check handling of a bad file header""" |
| 346 | size = 0x70 |
| 347 | cbw = CbfsWriter(size) |
| 348 | cbw.add_file_raw('u-boot', U_BOOT_DATA) |
| 349 | data = cbw.get_data() |
| 350 | |
| 351 | # Read in the CBFS master header (only), then stop |
| 352 | cbr = cbfs_util.CbfsReader(data, read=False) |
| 353 | with io.BytesIO(data) as fd: |
| 354 | self.assertTrue(cbr._find_and_read_header(fd, len(data))) |
| 355 | pos = fd.tell() |
| 356 | |
| 357 | # Remove all but 4 bytes of the file headerm and try to read the file |
| 358 | newdata = data[:pos + 4] |
| 359 | with test_util.capture_sys_output() as (stdout, _stderr): |
| 360 | with io.BytesIO(newdata) as fd: |
| 361 | fd.seek(pos) |
| 362 | self.assertEqual(False, cbr._read_next_file(fd)) |
| 363 | self.assertIn('File header at 0 ran out of data', stdout.getvalue()) |
| 364 | |
| 365 | def test_cbfs_bad_file_string(self): |
| 366 | """Check handling of an incomplete filename string""" |
| 367 | size = 0x70 |
| 368 | cbw = CbfsWriter(size) |
| 369 | cbw.add_file_raw('16-characters xx', U_BOOT_DATA) |
| 370 | data = cbw.get_data() |
| 371 | |
| 372 | # Read in the CBFS master header (only), then stop |
| 373 | cbr = cbfs_util.CbfsReader(data, read=False) |
| 374 | with io.BytesIO(data) as fd: |
| 375 | self.assertTrue(cbr._find_and_read_header(fd, len(data))) |
| 376 | pos = fd.tell() |
| 377 | |
| 378 | # Create a new CBFS with only the first 16 bytes of the file name, then |
| 379 | # try to read the file |
| 380 | newdata = data[:pos + cbfs_util.FILE_HEADER_LEN + 16] |
| 381 | with test_util.capture_sys_output() as (stdout, _stderr): |
| 382 | with io.BytesIO(newdata) as fd: |
| 383 | fd.seek(pos) |
| 384 | self.assertEqual(False, cbr._read_next_file(fd)) |
| 385 | self.assertIn('String at %x ran out of data' % |
| 386 | cbfs_util.FILE_HEADER_LEN, stdout.getvalue()) |
| 387 | |
| 388 | def test_cbfs_debug(self): |
| 389 | """Check debug output""" |
| 390 | size = 0x70 |
| 391 | cbw = CbfsWriter(size) |
| 392 | cbw.add_file_raw('u-boot', U_BOOT_DATA) |
| 393 | data = cbw.get_data() |
| 394 | |
| 395 | try: |
| 396 | cbfs_util.DEBUG = True |
| 397 | with test_util.capture_sys_output() as (stdout, _stderr): |
| 398 | cbfs_util.CbfsReader(data) |
| 399 | self.assertEqual('name u-boot\ndata %s\n' % U_BOOT_DATA, |
| 400 | stdout.getvalue()) |
| 401 | finally: |
| 402 | cbfs_util.DEBUG = False |
| 403 | |
| 404 | def test_cbfs_bad_attribute(self): |
| 405 | """Check handling of bad attribute tag""" |
| 406 | if not self.have_lz4: |
| 407 | self.skipTest('lz4 --no-frame-crc not available') |
| 408 | size = 0x140 |
| 409 | cbw = CbfsWriter(size) |
| 410 | cbw.add_file_raw('u-boot', COMPRESS_DATA, |
| 411 | compress=cbfs_util.COMPRESS_LZ4) |
| 412 | data = cbw.get_data() |
| 413 | |
| 414 | # Search the CBFS for the expected compression tag |
| 415 | with io.BytesIO(data) as fd: |
| 416 | while True: |
| 417 | pos = fd.tell() |
| 418 | tag, = struct.unpack('>I', fd.read(4)) |
| 419 | if tag == cbfs_util.FILE_ATTR_TAG_COMPRESSION: |
| 420 | break |
| 421 | |
| 422 | # Create a new CBFS with the tag changed to something invalid |
| 423 | newdata = data[:pos] + struct.pack('>I', 0x123) + data[pos + 4:] |
| 424 | with test_util.capture_sys_output() as (stdout, _stderr): |
| 425 | cbfs_util.CbfsReader(newdata) |
| 426 | self.assertEqual('Unknown attribute tag 123\n', stdout.getvalue()) |
| 427 | |
| 428 | def test_cbfs_missing_attribute(self): |
| 429 | """Check handling of an incomplete attribute tag""" |
| 430 | if not self.have_lz4: |
| 431 | self.skipTest('lz4 --no-frame-crc not available') |
| 432 | size = 0x140 |
| 433 | cbw = CbfsWriter(size) |
| 434 | cbw.add_file_raw('u-boot', COMPRESS_DATA, |
| 435 | compress=cbfs_util.COMPRESS_LZ4) |
| 436 | data = cbw.get_data() |
| 437 | |
| 438 | # Read in the CBFS master header (only), then stop |
| 439 | cbr = cbfs_util.CbfsReader(data, read=False) |
| 440 | with io.BytesIO(data) as fd: |
| 441 | self.assertTrue(cbr._find_and_read_header(fd, len(data))) |
| 442 | pos = fd.tell() |
| 443 | |
| 444 | # Create a new CBFS with only the first 4 bytes of the compression tag, |
| 445 | # then try to read the file |
| 446 | tag_pos = pos + cbfs_util.FILE_HEADER_LEN + cbfs_util.FILENAME_ALIGN |
| 447 | newdata = data[:tag_pos + 4] |
| 448 | with test_util.capture_sys_output() as (stdout, _stderr): |
| 449 | with io.BytesIO(newdata) as fd: |
| 450 | fd.seek(pos) |
| 451 | self.assertEqual(False, cbr._read_next_file(fd)) |
| 452 | self.assertIn('Attribute tag at %x ran out of data' % tag_pos, |
| 453 | stdout.getvalue()) |
| 454 | |
| 455 | def test_cbfs_file_master_header(self): |
| 456 | """Check handling of a file containing a master header""" |
| 457 | size = 0x100 |
| 458 | cbw = CbfsWriter(size) |
| 459 | cbw._add_fileheader = True |
| 460 | cbw.add_file_raw('u-boot', U_BOOT_DATA) |
| 461 | data = cbw.get_data() |
| 462 | |
| 463 | cbr = cbfs_util.CbfsReader(data) |
| 464 | self.assertIn('u-boot', cbr.files) |
| 465 | self.assertEqual(size, cbr.rom_size) |
| 466 | |
| 467 | def test_cbfs_arch(self): |
| 468 | """Test on non-x86 architecture""" |
| 469 | size = 0x100 |
| 470 | cbw = CbfsWriter(size, arch=cbfs_util.ARCHITECTURE_PPC64) |
| 471 | cbw.add_file_raw('u-boot', U_BOOT_DATA) |
| 472 | cbw.add_file_raw('u-boot-dtb', U_BOOT_DTB_DATA) |
| 473 | data = cbw.get_data() |
| 474 | self._check_raw(data, size, offset=0x40, |
| 475 | arch=cbfs_util.ARCHITECTURE_PPC64) |
| 476 | |
| 477 | # Compare against what cbfstool creates |
| 478 | cbfs_fname = self._get_expected_cbfs(size=size, arch='ppc64') |
| 479 | self._compare_expected_cbfs(data, cbfs_fname) |
| 480 | |
| 481 | def test_cbfs_stage(self): |
| 482 | """Tests handling of a Coreboot Filesystem (CBFS)""" |
| 483 | if not elf.ELF_TOOLS: |
| 484 | self.skipTest('Python elftools not available') |
| 485 | elf_fname = os.path.join(self._indir, 'cbfs-stage.elf') |
| 486 | elf.MakeElf(elf_fname, U_BOOT_DATA, U_BOOT_DTB_DATA) |
| 487 | |
| 488 | size = 0xb0 |
| 489 | cbw = CbfsWriter(size) |
| 490 | cbw.add_file_stage('u-boot', tools.ReadFile(elf_fname)) |
| 491 | |
| 492 | data = cbw.get_data() |
| 493 | cbfs = self._check_hdr(data, size) |
| 494 | load = 0xfef20000 |
| 495 | entry = load + 2 |
| 496 | |
| 497 | cfile = self._check_uboot(cbfs, cbfs_util.TYPE_STAGE, offset=0x28, |
| 498 | data=U_BOOT_DATA + U_BOOT_DTB_DATA) |
| 499 | |
| 500 | self.assertEqual(entry, cfile.entry) |
| 501 | self.assertEqual(load, cfile.load) |
| 502 | self.assertEqual(len(U_BOOT_DATA) + len(U_BOOT_DTB_DATA), |
| 503 | cfile.data_len) |
| 504 | |
| 505 | # Compare against what cbfstool creates |
| 506 | if self.have_cbfstool: |
| 507 | cbfs_fname = os.path.join(self._indir, 'test.cbfs') |
| 508 | cbfs_util.cbfstool(cbfs_fname, 'create', '-m', 'x86', '-s', |
| 509 | '%#x' % size) |
| 510 | cbfs_util.cbfstool(cbfs_fname, 'add-stage', '-n', 'u-boot', |
| 511 | '-f', elf_fname) |
| 512 | self._compare_expected_cbfs(data, cbfs_fname) |
| 513 | |
| 514 | def test_cbfs_raw_compress(self): |
| 515 | """Test base handling of compressing raw files""" |
| 516 | if not self.have_lz4: |
| 517 | self.skipTest('lz4 --no-frame-crc not available') |
| 518 | size = 0x140 |
| 519 | cbw = CbfsWriter(size) |
| 520 | cbw.add_file_raw('u-boot', COMPRESS_DATA, |
| 521 | compress=cbfs_util.COMPRESS_LZ4) |
| 522 | cbw.add_file_raw('u-boot-dtb', COMPRESS_DATA, |
| 523 | compress=cbfs_util.COMPRESS_LZMA) |
| 524 | data = cbw.get_data() |
| 525 | |
| 526 | cbfs = self._check_hdr(data, size) |
| 527 | self.assertIn('u-boot', cbfs.files) |
| 528 | cfile = cbfs.files['u-boot'] |
| 529 | self.assertEqual(cfile.name, 'u-boot') |
| 530 | self.assertEqual(cfile.offset, 56) |
| 531 | self.assertEqual(cfile.data, COMPRESS_DATA) |
| 532 | self.assertEqual(cfile.ftype, cbfs_util.TYPE_RAW) |
| 533 | self.assertEqual(cfile.compress, cbfs_util.COMPRESS_LZ4) |
| 534 | self.assertEqual(cfile.memlen, len(COMPRESS_DATA)) |
| 535 | |
| 536 | self.assertIn('u-boot-dtb', cbfs.files) |
| 537 | cfile = cbfs.files['u-boot-dtb'] |
| 538 | self.assertEqual(cfile.name, 'u-boot-dtb') |
| 539 | self.assertEqual(cfile.offset, 56) |
| 540 | self.assertEqual(cfile.data, COMPRESS_DATA) |
| 541 | self.assertEqual(cfile.ftype, cbfs_util.TYPE_RAW) |
| 542 | self.assertEqual(cfile.compress, cbfs_util.COMPRESS_LZMA) |
| 543 | self.assertEqual(cfile.memlen, len(COMPRESS_DATA)) |
| 544 | |
| 545 | cbfs_fname = self._get_expected_cbfs(size=size, compress=['lz4', 'lzma']) |
| 546 | self._compare_expected_cbfs(data, cbfs_fname) |
| 547 | |
Simon Glass | a61e6fe | 2019-07-08 13:18:55 -0600 | [diff] [blame^] | 548 | def test_cbfs_raw_space(self): |
| 549 | """Test files with unused space in the CBFS""" |
| 550 | size = 0xf0 |
| 551 | cbw = CbfsWriter(size) |
| 552 | cbw.add_file_raw('u-boot', U_BOOT_DATA) |
| 553 | cbw.add_file_raw('u-boot-dtb', U_BOOT_DTB_DATA) |
| 554 | data = cbw.get_data() |
| 555 | self._check_raw(data, size) |
| 556 | cbfs_fname = self._get_expected_cbfs(size=size) |
| 557 | self._compare_expected_cbfs(data, cbfs_fname) |
| 558 | |
Simon Glass | 96a6296 | 2019-07-08 13:18:52 -0600 | [diff] [blame] | 559 | |
| 560 | if __name__ == '__main__': |
| 561 | unittest.main() |