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
8These create and read various CBFSs and compare the results with expected
9values and with cbfstool
10"""
11
12import io
13import os
14import shutil
15import struct
16import tempfile
17import unittest
18
19from binman import bintool
20from binman import cbfs_util
21from binman.cbfs_util import CbfsWriter
22from binman import elf
23from u_boot_pylib import test_util
24from u_boot_pylib import tools
25
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.')
38        tools.set_input_dirs([cls._indir])
39
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
47        tools.prepare_output_dir(None)
48
49        cls.cbfstool = bintool.Bintool.create('cbfstool')
50        cls.have_cbfstool = cls.cbfstool.is_present()
51
52        lz4 = bintool.Bintool.create('lz4')
53        cls.have_lz4 = lz4.is_present()
54
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
61        tools.finalise_output_dir()
62
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)
74        tools.write_file(pathname, contents)
75        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=0x20,
100                     data=U_BOOT_DATA, cbfs_offset=None):
101        """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
108            cbfs_offset: Expected CBFS offset for file's data
109
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)
117        if cbfs_offset is not None:
118            self.assertEqual(cbfs_offset, cfile.cbfs_offset)
119        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
125    def _check_dtb(self, cbfs, offset=0x24, data=U_BOOT_DTB_DATA,
126                   cbfs_offset=None):
127        """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
133            cbfs_offset: Expected CBFS offset for file's data
134        """
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)
139        if cbfs_offset is not None:
140            self.assertEqual(cbfs_offset, cfile.cbfs_offset)
141        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
159    def _get_expected_cbfs(self, size, arch='x86', compress=None, base=None):
160        """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
166            base: Base address of file, or None to put it anywhere
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        self.cbfstool.create_new(cbfs_fname, size, arch)
175        if base:
176            base = [(1 << 32) - size + b for b in base]
177        self.cbfstool.add_raw(
178            cbfs_fname, 'u-boot',
179            tools.get_input_filename(compress and 'compress' or 'u-boot.bin'),
180            compress[0] if compress else None,
181            base[0] if base else None)
182        self.cbfstool.add_raw(
183            cbfs_fname, 'u-boot-dtb',
184            tools.get_input_filename(compress and 'compress' or 'u-boot.dtb'),
185            compress[1] if compress else None,
186            base[1] if base else None)
187        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
201        expect = tools.read_file(cbfstool_fname)
202        if expect != data:
203            tools.write_file('/tmp/expect', expect)
204            tools.write_file('/tmp/actual', data)
205            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')
220        with self.assertRaises(ValueError) as exc:
221            out = self.cbfstool.fail()
222        self.assertIn('cbfstool missing-file bad-command', str(exc.exception))
223
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"""
288        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"""
298        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()
304        self.assertIn('No space for data before pad offset', str(e.exception))
305
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))
358        self.assertIn('File header at 0x0 ran out of data', stdout.getvalue())
359
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))
380        self.assertIn('String at %#x ran out of data' %
381                      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\nftype 50\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)
405        cbw.add_file_raw('u-boot', COMPRESS_DATA, None,
406                         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)
429        cbw.add_file_raw('u-boot', COMPRESS_DATA, None,
430                         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. Note that the tag gets pushed out 4 bytes
441        tag_pos = (4 + pos + cbfs_util.FILE_HEADER_LEN +
442                   cbfs_util.ATTRIBUTE_ALIGN)
443        newdata = data[:tag_pos + 4]
444        with test_util.capture_sys_output() as (stdout, _stderr):
445            with io.BytesIO(newdata) as fd:
446                fd.seek(pos)
447                self.assertEqual(False, cbr._read_next_file(fd))
448        self.assertIn('Attribute tag at %x ran out of data' % tag_pos,
449                      stdout.getvalue())
450
451    def test_cbfs_file_master_header(self):
452        """Check handling of a file containing a master header"""
453        size = 0x100
454        cbw = CbfsWriter(size)
455        cbw._add_fileheader = True
456        cbw.add_file_raw('u-boot', U_BOOT_DATA)
457        data = cbw.get_data()
458
459        cbr = cbfs_util.CbfsReader(data)
460        self.assertIn('u-boot', cbr.files)
461        self.assertEqual(size, cbr.rom_size)
462
463    def test_cbfs_arch(self):
464        """Test on non-x86 architecture"""
465        size = 0x100
466        cbw = CbfsWriter(size, arch=cbfs_util.ARCHITECTURE_PPC64)
467        cbw.add_file_raw('u-boot', U_BOOT_DATA)
468        cbw.add_file_raw('u-boot-dtb', U_BOOT_DTB_DATA)
469        data = cbw.get_data()
470        self._check_raw(data, size, offset=0x40,
471                        arch=cbfs_util.ARCHITECTURE_PPC64)
472
473        # Compare against what cbfstool creates
474        cbfs_fname = self._get_expected_cbfs(size=size, arch='ppc64')
475        self._compare_expected_cbfs(data, cbfs_fname)
476
477    def test_cbfs_stage(self):
478        """Tests handling of a CBFS stage"""
479        if not elf.ELF_TOOLS:
480            self.skipTest('Python elftools not available')
481        elf_fname = os.path.join(self._indir, 'cbfs-stage.elf')
482        elf.MakeElf(elf_fname, U_BOOT_DATA, U_BOOT_DTB_DATA)
483
484        size = 0xb0
485        cbw = CbfsWriter(size)
486        cbw.add_file_stage('u-boot', tools.read_file(elf_fname))
487
488        data = cbw.get_data()
489        cbfs = self._check_hdr(data, size)
490        load = 0xfef20000
491        entry = load + 2
492
493        cfile = self._check_uboot(cbfs, cbfs_util.TYPE_STAGE, offset=0x38,
494                                  data=U_BOOT_DATA + U_BOOT_DTB_DATA)
495
496        self.assertEqual(entry, cfile.entry)
497        self.assertEqual(load, cfile.load)
498        self.assertEqual(len(U_BOOT_DATA) + len(U_BOOT_DTB_DATA),
499                         cfile.data_len)
500
501        # Compare against what cbfstool creates
502        if self.have_cbfstool:
503            cbfs_fname = os.path.join(self._indir, 'test.cbfs')
504            self.cbfstool.create_new(cbfs_fname, size)
505            self.cbfstool.add_stage(cbfs_fname, 'u-boot', elf_fname)
506            self._compare_expected_cbfs(data, cbfs_fname)
507
508    def test_cbfs_raw_compress(self):
509        """Test base handling of compressing raw files"""
510        if not self.have_lz4:
511            self.skipTest('lz4 --no-frame-crc not available')
512        size = 0x140
513        cbw = CbfsWriter(size)
514        cbw.add_file_raw('u-boot', COMPRESS_DATA, None,
515                         compress=cbfs_util.COMPRESS_LZ4)
516        cbw.add_file_raw('u-boot-dtb', COMPRESS_DATA, None,
517                         compress=cbfs_util.COMPRESS_LZMA)
518        data = cbw.get_data()
519
520        cbfs = self._check_hdr(data, size)
521        self.assertIn('u-boot', cbfs.files)
522        cfile = cbfs.files['u-boot']
523        self.assertEqual(cfile.name, 'u-boot')
524        self.assertEqual(cfile.offset, 0x30)
525        self.assertEqual(cfile.data, COMPRESS_DATA)
526        self.assertEqual(cfile.ftype, cbfs_util.TYPE_RAW)
527        self.assertEqual(cfile.compress, cbfs_util.COMPRESS_LZ4)
528        self.assertEqual(cfile.memlen, len(COMPRESS_DATA))
529
530        self.assertIn('u-boot-dtb', cbfs.files)
531        cfile = cbfs.files['u-boot-dtb']
532        self.assertEqual(cfile.name, 'u-boot-dtb')
533        self.assertEqual(cfile.offset, 0x34)
534        self.assertEqual(cfile.data, COMPRESS_DATA)
535        self.assertEqual(cfile.ftype, cbfs_util.TYPE_RAW)
536        self.assertEqual(cfile.compress, cbfs_util.COMPRESS_LZMA)
537        self.assertEqual(cfile.memlen, len(COMPRESS_DATA))
538
539        cbfs_fname = self._get_expected_cbfs(size=size, compress=['lz4', 'lzma'])
540        self._compare_expected_cbfs(data, cbfs_fname)
541
542    def test_cbfs_raw_space(self):
543        """Test files with unused space in the CBFS"""
544        size = 0xf0
545        cbw = CbfsWriter(size)
546        cbw.add_file_raw('u-boot', U_BOOT_DATA)
547        cbw.add_file_raw('u-boot-dtb', U_BOOT_DTB_DATA)
548        data = cbw.get_data()
549        self._check_raw(data, size)
550        cbfs_fname = self._get_expected_cbfs(size=size)
551        self._compare_expected_cbfs(data, cbfs_fname)
552
553    def test_cbfs_offset(self):
554        """Test a CBFS with files at particular offsets"""
555        size = 0x200
556        cbw = CbfsWriter(size)
557        cbw.add_file_raw('u-boot', U_BOOT_DATA, 0x40)
558        cbw.add_file_raw('u-boot-dtb', U_BOOT_DTB_DATA, 0x140)
559
560        data = cbw.get_data()
561        cbfs = self._check_hdr(data, size)
562        self._check_uboot(cbfs, ftype=cbfs_util.TYPE_RAW, offset=0x40,
563                          cbfs_offset=0x40)
564        self._check_dtb(cbfs, offset=0x40, cbfs_offset=0x140)
565
566        cbfs_fname = self._get_expected_cbfs(size=size, base=(0x40, 0x140))
567        self._compare_expected_cbfs(data, cbfs_fname)
568
569    def test_cbfs_invalid_file_type_header(self):
570        """Check handling of an invalid file type when outputting a header"""
571        size = 0xb0
572        cbw = CbfsWriter(size)
573        cfile = cbw.add_file_raw('u-boot', U_BOOT_DATA, 0)
574
575        # Change the type manually before generating the CBFS, and make sure
576        # that the generator complains
577        cfile.ftype = 0xff
578        with self.assertRaises(ValueError) as e:
579            cbw.get_data()
580        self.assertIn('Unknown file type 0xff', str(e.exception))
581
582    def test_cbfs_offset_conflict(self):
583        """Test a CBFS with files that want to overlap"""
584        size = 0x200
585        cbw = CbfsWriter(size)
586        cbw.add_file_raw('u-boot', U_BOOT_DATA, 0x40)
587        cbw.add_file_raw('u-boot-dtb', U_BOOT_DTB_DATA, 0x80)
588
589        with self.assertRaises(ValueError) as e:
590            cbw.get_data()
591        self.assertIn('No space for data before pad offset', str(e.exception))
592
593    def test_cbfs_check_offset(self):
594        """Test that we can discover the offset of a file after writing it"""
595        size = 0xb0
596        cbw = CbfsWriter(size)
597        cbw.add_file_raw('u-boot', U_BOOT_DATA)
598        cbw.add_file_raw('u-boot-dtb', U_BOOT_DTB_DATA)
599        data = cbw.get_data()
600
601        cbfs = cbfs_util.CbfsReader(data)
602        self.assertEqual(0x20, cbfs.files['u-boot'].cbfs_offset)
603        self.assertEqual(0x64, cbfs.files['u-boot-dtb'].cbfs_offset)
604
605
606if __name__ == '__main__':
607    unittest.main()
608