mirror of
				https://xff.cz/git/u-boot/
				synced 2025-10-31 10:26:10 +01:00 
			
		
		
		
	binman: Add support for fixed-offset files in CBFS
A feature of CBFS is that it allows files to be positioned at particular offset (as with binman in general). This is useful to support execute-in-place (XIP) code, since this may not be relocatable. Add a new cbfs-offset property to control this. Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
		| @@ -100,6 +100,7 @@ with the second subnode below: | |||||||
|             filename = "u-boot.dtb"; |             filename = "u-boot.dtb"; | ||||||
|             cbfs-type = "raw"; |             cbfs-type = "raw"; | ||||||
|             cbfs-compress = "lz4"; |             cbfs-compress = "lz4"; | ||||||
|  |             cbfs-offset = <0x100000>; | ||||||
|         }; |         }; | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
| @@ -158,6 +159,15 @@ cbfs-type: | |||||||
|         to add a flat binary with a load/start address, similar to the |         to add a flat binary with a load/start address, similar to the | ||||||
|         'add-flat-binary' option in cbfstool. |         'add-flat-binary' option in cbfstool. | ||||||
|  |  | ||||||
|  | cbfs-offset: | ||||||
|  |     This is the offset of the file's data within the CBFS. It is used to | ||||||
|  |     specify where the file should be placed in cases where a fixed position | ||||||
|  |     is needed. Typical uses are for code which is not relocatable and must | ||||||
|  |     execute in-place from a particular address. This works because SPI flash | ||||||
|  |     is generally mapped into memory on x86 devices. The file header is | ||||||
|  |     placed before this offset so that the data start lines up exactly with | ||||||
|  |     the chosen offset. If this property is not provided, then the file is | ||||||
|  |     placed in the next available spot. | ||||||
|  |  | ||||||
| The current implementation supports only a subset of CBFS features. It does | The current implementation supports only a subset of CBFS features. It does | ||||||
| not support other file types (e.g. payload), adding multiple files (like the | not support other file types (e.g. payload), adding multiple files (like the | ||||||
| @@ -334,6 +344,34 @@ See README.x86 for information about x86 binary blobs. | |||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Entry: intel-ifwi: Entry containing an Intel Integrated Firmware Image (IFWI) file | ||||||
|  | ---------------------------------------------------------------------------------- | ||||||
|  |  | ||||||
|  | Properties / Entry arguments: | ||||||
|  |     - filename: Filename of file to read into entry. This is either the | ||||||
|  |         IFWI file itself, or a file that can be converted into one using a | ||||||
|  |         tool | ||||||
|  |     - convert-fit: If present this indicates that the ifwitool should be | ||||||
|  |         used to convert the provided file into a IFWI. | ||||||
|  |  | ||||||
|  | This file contains code and data used by the SoC that is required to make | ||||||
|  | it work. It includes U-Boot TPL, microcode, things related to the CSE | ||||||
|  | (Converged Security Engine, the microcontroller that loads all the firmware) | ||||||
|  | and other items beyond the wit of man. | ||||||
|  |  | ||||||
|  | A typical filename is 'ifwi.bin' for an IFWI file, or 'fitimage.bin' for a | ||||||
|  | file that will be converted to an IFWI. | ||||||
|  |  | ||||||
|  | The position of this entry is generally set by the intel-descriptor entry. | ||||||
|  |  | ||||||
|  | The contents of the IFWI are specified by the subnodes of the IFWI node. | ||||||
|  | Each subnode describes an entry which is placed into the IFWFI with a given | ||||||
|  | sub-partition (and optional entry name). | ||||||
|  |  | ||||||
|  | See README.x86 for information about x86 binary blobs. | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| Entry: intel-me: Entry containing an Intel Management Engine (ME) file | Entry: intel-me: Entry containing an Intel Management Engine (ME) file | ||||||
| ---------------------------------------------------------------------- | ---------------------------------------------------------------------- | ||||||
|  |  | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ it is necessary to rely on the C structures and source code (mostly cbfstool) | |||||||
| to fully understand it. | to fully understand it. | ||||||
|  |  | ||||||
| Currently supported: raw and stage types with compression, padding empty areas | Currently supported: raw and stage types with compression, padding empty areas | ||||||
|     with empty files |     with empty files, fixed-offset files | ||||||
| """ | """ | ||||||
|  |  | ||||||
| from __future__ import print_function | from __future__ import print_function | ||||||
| @@ -190,6 +190,8 @@ class CbfsFile(object): | |||||||
|     Properties: |     Properties: | ||||||
|         name: Name of file |         name: Name of file | ||||||
|         offset: Offset of file data from start of file header |         offset: Offset of file data from start of file header | ||||||
|  |         cbfs_offset: Offset of file data in bytes from start of CBFS, or None to | ||||||
|  |             place this file anyway | ||||||
|         data: Contents of file, uncompressed |         data: Contents of file, uncompressed | ||||||
|         data_len: Length of (possibly compressed) data in bytes |         data_len: Length of (possibly compressed) data in bytes | ||||||
|         ftype: File type (TYPE_...) |         ftype: File type (TYPE_...) | ||||||
| @@ -203,9 +205,10 @@ class CbfsFile(object): | |||||||
|             contents (used for empty files) |             contents (used for empty files) | ||||||
|         size: Size of the file in bytes (used for empty files) |         size: Size of the file in bytes (used for empty files) | ||||||
|     """ |     """ | ||||||
|     def __init__(self, name, ftype, data, compress=COMPRESS_NONE): |     def __init__(self, name, ftype, data, cbfs_offset, compress=COMPRESS_NONE): | ||||||
|         self.name = name |         self.name = name | ||||||
|         self.offset = None |         self.offset = None | ||||||
|  |         self.cbfs_offset = cbfs_offset | ||||||
|         self.data = data |         self.data = data | ||||||
|         self.ftype = ftype |         self.ftype = ftype | ||||||
|         self.compress = compress |         self.compress = compress | ||||||
| @@ -231,7 +234,7 @@ class CbfsFile(object): | |||||||
|         self.data_len = len(indata) |         self.data_len = len(indata) | ||||||
|  |  | ||||||
|     @classmethod |     @classmethod | ||||||
|     def stage(cls, base_address, name, data): |     def stage(cls, base_address, name, data, cbfs_offset): | ||||||
|         """Create a new stage file |         """Create a new stage file | ||||||
|  |  | ||||||
|         Args: |         Args: | ||||||
| @@ -239,28 +242,32 @@ class CbfsFile(object): | |||||||
|             name: String file name to put in CBFS (does not need to correspond |             name: String file name to put in CBFS (does not need to correspond | ||||||
|                 to the name that the file originally came from) |                 to the name that the file originally came from) | ||||||
|             data: Contents of file |             data: Contents of file | ||||||
|  |             cbfs_offset: Offset of file data in bytes from start of CBFS, or | ||||||
|  |                 None to place this file anyway | ||||||
|  |  | ||||||
|         Returns: |         Returns: | ||||||
|             CbfsFile object containing the file information |             CbfsFile object containing the file information | ||||||
|         """ |         """ | ||||||
|         cfile = CbfsFile(name, TYPE_STAGE, data) |         cfile = CbfsFile(name, TYPE_STAGE, data, cbfs_offset) | ||||||
|         cfile.base_address = base_address |         cfile.base_address = base_address | ||||||
|         return cfile |         return cfile | ||||||
|  |  | ||||||
|     @classmethod |     @classmethod | ||||||
|     def raw(cls, name, data, compress): |     def raw(cls, name, data, cbfs_offset, compress): | ||||||
|         """Create a new raw file |         """Create a new raw file | ||||||
|  |  | ||||||
|         Args: |         Args: | ||||||
|             name: String file name to put in CBFS (does not need to correspond |             name: String file name to put in CBFS (does not need to correspond | ||||||
|                 to the name that the file originally came from) |                 to the name that the file originally came from) | ||||||
|             data: Contents of file |             data: Contents of file | ||||||
|  |             cbfs_offset: Offset of file data in bytes from start of CBFS, or | ||||||
|  |                 None to place this file anyway | ||||||
|             compress: Compression algorithm to use (COMPRESS_...) |             compress: Compression algorithm to use (COMPRESS_...) | ||||||
|  |  | ||||||
|         Returns: |         Returns: | ||||||
|             CbfsFile object containing the file information |             CbfsFile object containing the file information | ||||||
|         """ |         """ | ||||||
|         return CbfsFile(name, TYPE_RAW, data, compress) |         return CbfsFile(name, TYPE_RAW, data, cbfs_offset, compress) | ||||||
|  |  | ||||||
|     @classmethod |     @classmethod | ||||||
|     def empty(cls, space_to_use, erase_byte): |     def empty(cls, space_to_use, erase_byte): | ||||||
| @@ -275,12 +282,44 @@ class CbfsFile(object): | |||||||
|         Returns: |         Returns: | ||||||
|             CbfsFile object containing the file information |             CbfsFile object containing the file information | ||||||
|         """ |         """ | ||||||
|         cfile = CbfsFile('', TYPE_EMPTY, b'') |         cfile = CbfsFile('', TYPE_EMPTY, b'', None) | ||||||
|         cfile.size = space_to_use - FILE_HEADER_LEN - FILENAME_ALIGN |         cfile.size = space_to_use - FILE_HEADER_LEN - FILENAME_ALIGN | ||||||
|         cfile.erase_byte = erase_byte |         cfile.erase_byte = erase_byte | ||||||
|         return cfile |         return cfile | ||||||
|  |  | ||||||
|     def get_data(self): |     def calc_start_offset(self): | ||||||
|  |         """Check if this file needs to start at a particular offset in CBFS | ||||||
|  |  | ||||||
|  |         Returns: | ||||||
|  |             None if the file can be placed anywhere, or | ||||||
|  |             the largest offset where the file could start (integer) | ||||||
|  |         """ | ||||||
|  |         if self.cbfs_offset is None: | ||||||
|  |             return None | ||||||
|  |         return self.cbfs_offset - self.get_header_len() | ||||||
|  |  | ||||||
|  |     def get_header_len(self): | ||||||
|  |         """Get the length of headers required for a file | ||||||
|  |  | ||||||
|  |         This is the minimum length required before the actual data for this file | ||||||
|  |         could start. It might start later if there is padding. | ||||||
|  |  | ||||||
|  |         Returns: | ||||||
|  |             Total length of all non-data fields, in bytes | ||||||
|  |         """ | ||||||
|  |         name = _pack_string(self.name) | ||||||
|  |         hdr_len = len(name) + FILE_HEADER_LEN | ||||||
|  |         if self.ftype == TYPE_STAGE: | ||||||
|  |             pass | ||||||
|  |         elif self.ftype == TYPE_RAW: | ||||||
|  |             hdr_len += ATTR_COMPRESSION_LEN | ||||||
|  |         elif self.ftype == TYPE_EMPTY: | ||||||
|  |             pass | ||||||
|  |         else: | ||||||
|  |             raise ValueError('Unknown file type %#x\n' % self.ftype) | ||||||
|  |         return hdr_len | ||||||
|  |  | ||||||
|  |     def get_data(self, offset=None, pad_byte=None): | ||||||
|         """Obtain the contents of the file, in CBFS format |         """Obtain the contents of the file, in CBFS format | ||||||
|  |  | ||||||
|         Returns: |         Returns: | ||||||
| @@ -292,6 +331,7 @@ class CbfsFile(object): | |||||||
|         attr_pos = 0 |         attr_pos = 0 | ||||||
|         content = b'' |         content = b'' | ||||||
|         attr = b'' |         attr = b'' | ||||||
|  |         pad = b'' | ||||||
|         data = self.data |         data = self.data | ||||||
|         if self.ftype == TYPE_STAGE: |         if self.ftype == TYPE_STAGE: | ||||||
|             elf_data = elf.DecodeElf(data, self.base_address) |             elf_data = elf.DecodeElf(data, self.base_address) | ||||||
| @@ -315,10 +355,33 @@ class CbfsFile(object): | |||||||
|         if attr: |         if attr: | ||||||
|             attr_pos = hdr_len |             attr_pos = hdr_len | ||||||
|             hdr_len += len(attr) |             hdr_len += len(attr) | ||||||
|         hdr = struct.pack(FILE_HEADER_FORMAT, FILE_MAGIC, |         if self.cbfs_offset is not None: | ||||||
|                           len(content) + len(data), |             pad_len = self.cbfs_offset - offset - hdr_len | ||||||
|  |             if pad_len < 0:  # pragma: no cover | ||||||
|  |                 # Test coverage of this is not available since this should never | ||||||
|  |                 # happen. It indicates that get_header_len() provided an | ||||||
|  |                 # incorrect value (too small) so that we decided that we could | ||||||
|  |                 # put this file at the requested place, but in fact a previous | ||||||
|  |                 # file extends far enough into the CBFS that this is not | ||||||
|  |                 # possible. | ||||||
|  |                 raise ValueError("Internal error: CBFS file '%s': Requested offset %#x but current output position is %#x" % | ||||||
|  |                                  (self.name, self.cbfs_offset, offset)) | ||||||
|  |             pad = tools.GetBytes(pad_byte, pad_len) | ||||||
|  |             hdr_len += pad_len | ||||||
|  |         self.offset = len(content) + len(data) | ||||||
|  |         hdr = struct.pack(FILE_HEADER_FORMAT, FILE_MAGIC, self.offset, | ||||||
|                           self.ftype, attr_pos, hdr_len) |                           self.ftype, attr_pos, hdr_len) | ||||||
|         return hdr + name + attr + content + data |  | ||||||
|  |         # Do a sanity check of the get_header_len() function, to ensure that it | ||||||
|  |         # stays in lockstep with this function | ||||||
|  |         expected_len = self.get_header_len() | ||||||
|  |         actual_len = len(hdr + name + attr) | ||||||
|  |         if expected_len != actual_len:  # pragma: no cover | ||||||
|  |             # Test coverage of this is not available since this should never | ||||||
|  |             # happen. It probably indicates that get_header_len() is broken. | ||||||
|  |             raise ValueError("Internal error: CBFS file '%s': Expected headers of %#x bytes, got %#d" % | ||||||
|  |                              (self.name, expected_len, actual_len)) | ||||||
|  |         return hdr + name + attr + pad + content + data | ||||||
|  |  | ||||||
|  |  | ||||||
| class CbfsWriter(object): | class CbfsWriter(object): | ||||||
| @@ -431,34 +494,39 @@ class CbfsWriter(object): | |||||||
|         if offset < self._size: |         if offset < self._size: | ||||||
|             self._skip_to(fd, offset) |             self._skip_to(fd, offset) | ||||||
|  |  | ||||||
|     def add_file_stage(self, name, data): |     def add_file_stage(self, name, data, cbfs_offset=None): | ||||||
|         """Add a new stage file to the CBFS |         """Add a new stage file to the CBFS | ||||||
|  |  | ||||||
|         Args: |         Args: | ||||||
|             name: String file name to put in CBFS (does not need to correspond |             name: String file name to put in CBFS (does not need to correspond | ||||||
|                 to the name that the file originally came from) |                 to the name that the file originally came from) | ||||||
|             data: Contents of file |             data: Contents of file | ||||||
|  |             cbfs_offset: Offset of this file's data within the CBFS, in bytes, | ||||||
|  |                 or None to place this file anywhere | ||||||
|  |  | ||||||
|         Returns: |         Returns: | ||||||
|             CbfsFile object created |             CbfsFile object created | ||||||
|         """ |         """ | ||||||
|         cfile = CbfsFile.stage(self._base_address, name, data) |         cfile = CbfsFile.stage(self._base_address, name, data, cbfs_offset) | ||||||
|         self._files[name] = cfile |         self._files[name] = cfile | ||||||
|         return cfile |         return cfile | ||||||
|  |  | ||||||
|     def add_file_raw(self, name, data, compress=COMPRESS_NONE): |     def add_file_raw(self, name, data, cbfs_offset=None, | ||||||
|  |                      compress=COMPRESS_NONE): | ||||||
|         """Create a new raw file |         """Create a new raw file | ||||||
|  |  | ||||||
|         Args: |         Args: | ||||||
|             name: String file name to put in CBFS (does not need to correspond |             name: String file name to put in CBFS (does not need to correspond | ||||||
|                 to the name that the file originally came from) |                 to the name that the file originally came from) | ||||||
|             data: Contents of file |             data: Contents of file | ||||||
|  |             cbfs_offset: Offset of this file's data within the CBFS, in bytes, | ||||||
|  |                 or None to place this file anywhere | ||||||
|             compress: Compression algorithm to use (COMPRESS_...) |             compress: Compression algorithm to use (COMPRESS_...) | ||||||
|  |  | ||||||
|         Returns: |         Returns: | ||||||
|             CbfsFile object created |             CbfsFile object created | ||||||
|         """ |         """ | ||||||
|         cfile = CbfsFile.raw(name, data, compress) |         cfile = CbfsFile.raw(name, data, cbfs_offset, compress) | ||||||
|         self._files[name] = cfile |         self._files[name] = cfile | ||||||
|         return cfile |         return cfile | ||||||
|  |  | ||||||
| @@ -507,7 +575,11 @@ class CbfsWriter(object): | |||||||
|  |  | ||||||
|         # Write out each file |         # Write out each file | ||||||
|         for cbf in self._files.values(): |         for cbf in self._files.values(): | ||||||
|             fd.write(cbf.get_data()) |             # Place the file at its requested place, if any | ||||||
|  |             offset = cbf.calc_start_offset() | ||||||
|  |             if offset is not None: | ||||||
|  |                 self._pad_to(fd, align_int_down(offset, self._align)) | ||||||
|  |             fd.write(cbf.get_data(fd.tell(), self._erase_byte)) | ||||||
|             self._align_to(fd, self._align) |             self._align_to(fd, self._align) | ||||||
|         if not self._hdr_at_start: |         if not self._hdr_at_start: | ||||||
|             self._write_header(fd, add_fileheader=self._add_fileheader) |             self._write_header(fd, add_fileheader=self._add_fileheader) | ||||||
| @@ -639,25 +711,27 @@ class CbfsReader(object): | |||||||
|  |  | ||||||
|         # Create the correct CbfsFile object depending on the type |         # Create the correct CbfsFile object depending on the type | ||||||
|         cfile = None |         cfile = None | ||||||
|         fd.seek(file_pos + offset, io.SEEK_SET) |         cbfs_offset = file_pos + offset | ||||||
|  |         fd.seek(cbfs_offset, io.SEEK_SET) | ||||||
|         if ftype == TYPE_CBFSHEADER: |         if ftype == TYPE_CBFSHEADER: | ||||||
|             self._read_header(fd) |             self._read_header(fd) | ||||||
|         elif ftype == TYPE_STAGE: |         elif ftype == TYPE_STAGE: | ||||||
|             data = fd.read(STAGE_LEN) |             data = fd.read(STAGE_LEN) | ||||||
|             cfile = CbfsFile.stage(self.stage_base_address, name, b'') |             cfile = CbfsFile.stage(self.stage_base_address, name, b'', | ||||||
|  |                                    cbfs_offset) | ||||||
|             (cfile.compress, cfile.entry, cfile.load, cfile.data_len, |             (cfile.compress, cfile.entry, cfile.load, cfile.data_len, | ||||||
|              cfile.memlen) = struct.unpack(STAGE_FORMAT, data) |              cfile.memlen) = struct.unpack(STAGE_FORMAT, data) | ||||||
|             cfile.data = fd.read(cfile.data_len) |             cfile.data = fd.read(cfile.data_len) | ||||||
|         elif ftype == TYPE_RAW: |         elif ftype == TYPE_RAW: | ||||||
|             data = fd.read(size) |             data = fd.read(size) | ||||||
|             cfile = CbfsFile.raw(name, data, compress) |             cfile = CbfsFile.raw(name, data, cbfs_offset, compress) | ||||||
|             cfile.decompress() |             cfile.decompress() | ||||||
|             if DEBUG: |             if DEBUG: | ||||||
|                 print('data', data) |                 print('data', data) | ||||||
|         elif ftype == TYPE_EMPTY: |         elif ftype == TYPE_EMPTY: | ||||||
|             # Just read the data and discard it, since it is only padding |             # Just read the data and discard it, since it is only padding | ||||||
|             fd.read(size) |             fd.read(size) | ||||||
|             cfile = CbfsFile('', TYPE_EMPTY, b'') |             cfile = CbfsFile('', TYPE_EMPTY, b'', cbfs_offset) | ||||||
|         else: |         else: | ||||||
|             raise ValueError('Unknown type %#x when reading\n' % ftype) |             raise ValueError('Unknown type %#x when reading\n' % ftype) | ||||||
|         if cfile: |         if cfile: | ||||||
| @@ -674,7 +748,8 @@ class CbfsReader(object): | |||||||
|         """Read attributes from the file |         """Read attributes from the file | ||||||
|  |  | ||||||
|         CBFS files can have attributes which are things that cannot fit into the |         CBFS files can have attributes which are things that cannot fit into the | ||||||
|         header. The only attribute currently supported is compression. |         header. The only attributes currently supported are compression and the | ||||||
|  |         unused tag. | ||||||
|  |  | ||||||
|         Args: |         Args: | ||||||
|             fd: File to read from |             fd: File to read from | ||||||
| @@ -703,6 +778,8 @@ class CbfsReader(object): | |||||||
|                 # We don't currently use this information |                 # We don't currently use this information | ||||||
|                 atag, alen, compress, _decomp_size = struct.unpack( |                 atag, alen, compress, _decomp_size = struct.unpack( | ||||||
|                     ATTR_COMPRESSION_FORMAT, data) |                     ATTR_COMPRESSION_FORMAT, data) | ||||||
|  |             elif atag == FILE_ATTR_TAG_UNUSED2: | ||||||
|  |                 break | ||||||
|             else: |             else: | ||||||
|                 print('Unknown attribute tag %x' % atag) |                 print('Unknown attribute tag %x' % atag) | ||||||
|             attr_size -= len(data) |             attr_size -= len(data) | ||||||
| @@ -760,7 +837,7 @@ class CbfsReader(object): | |||||||
|         return val.decode('utf-8') |         return val.decode('utf-8') | ||||||
|  |  | ||||||
|  |  | ||||||
| def cbfstool(fname, *cbfs_args): | def cbfstool(fname, *cbfs_args, **kwargs): | ||||||
|     """Run cbfstool with provided arguments |     """Run cbfstool with provided arguments | ||||||
|  |  | ||||||
|     If the tool fails then this function raises an exception and prints out the |     If the tool fails then this function raises an exception and prints out the | ||||||
| @@ -773,7 +850,9 @@ def cbfstool(fname, *cbfs_args): | |||||||
|     Returns: |     Returns: | ||||||
|         CommandResult object containing the results |         CommandResult object containing the results | ||||||
|     """ |     """ | ||||||
|     args = ('cbfstool', fname) + cbfs_args |     args = ['cbfstool', fname] + list(cbfs_args) | ||||||
|  |     if kwargs.get('base') is not None: | ||||||
|  |         args += ['-b', '%#x' % kwargs['base']] | ||||||
|     result = command.RunPipe([args], capture=not VERBOSE, |     result = command.RunPipe([args], capture=not VERBOSE, | ||||||
|                              capture_stderr=not VERBOSE, raise_on_error=False) |                              capture_stderr=not VERBOSE, raise_on_error=False) | ||||||
|     if result.return_code: |     if result.return_code: | ||||||
|   | |||||||
| @@ -105,7 +105,7 @@ class TestCbfs(unittest.TestCase): | |||||||
|         return cbfs |         return cbfs | ||||||
|  |  | ||||||
|     def _check_uboot(self, cbfs, ftype=cbfs_util.TYPE_RAW, offset=0x38, |     def _check_uboot(self, cbfs, ftype=cbfs_util.TYPE_RAW, offset=0x38, | ||||||
|                      data=U_BOOT_DATA): |                      data=U_BOOT_DATA, cbfs_offset=None): | ||||||
|         """Check that the U-Boot file is as expected |         """Check that the U-Boot file is as expected | ||||||
|  |  | ||||||
|         Args: |         Args: | ||||||
| @@ -113,6 +113,7 @@ class TestCbfs(unittest.TestCase): | |||||||
|             ftype: Expected file type |             ftype: Expected file type | ||||||
|             offset: Expected offset of file |             offset: Expected offset of file | ||||||
|             data: Expected data in file |             data: Expected data in file | ||||||
|  |             cbfs_offset: Expected CBFS offset for file's data | ||||||
|  |  | ||||||
|         Returns: |         Returns: | ||||||
|             CbfsFile object containing the file |             CbfsFile object containing the file | ||||||
| @@ -121,24 +122,30 @@ class TestCbfs(unittest.TestCase): | |||||||
|         cfile = cbfs.files['u-boot'] |         cfile = cbfs.files['u-boot'] | ||||||
|         self.assertEqual('u-boot', cfile.name) |         self.assertEqual('u-boot', cfile.name) | ||||||
|         self.assertEqual(offset, cfile.offset) |         self.assertEqual(offset, cfile.offset) | ||||||
|  |         if cbfs_offset is not None: | ||||||
|  |             self.assertEqual(cbfs_offset, cfile.cbfs_offset) | ||||||
|         self.assertEqual(data, cfile.data) |         self.assertEqual(data, cfile.data) | ||||||
|         self.assertEqual(ftype, cfile.ftype) |         self.assertEqual(ftype, cfile.ftype) | ||||||
|         self.assertEqual(cbfs_util.COMPRESS_NONE, cfile.compress) |         self.assertEqual(cbfs_util.COMPRESS_NONE, cfile.compress) | ||||||
|         self.assertEqual(len(data), cfile.memlen) |         self.assertEqual(len(data), cfile.memlen) | ||||||
|         return cfile |         return cfile | ||||||
|  |  | ||||||
|     def _check_dtb(self, cbfs, offset=0x38, data=U_BOOT_DTB_DATA): |     def _check_dtb(self, cbfs, offset=0x38, data=U_BOOT_DTB_DATA, | ||||||
|  |                    cbfs_offset=None): | ||||||
|         """Check that the U-Boot dtb file is as expected |         """Check that the U-Boot dtb file is as expected | ||||||
|  |  | ||||||
|         Args: |         Args: | ||||||
|             cbfs: CbfsReader object to check |             cbfs: CbfsReader object to check | ||||||
|             offset: Expected offset of file |             offset: Expected offset of file | ||||||
|             data: Expected data in file |             data: Expected data in file | ||||||
|  |             cbfs_offset: Expected CBFS offset for file's data | ||||||
|         """ |         """ | ||||||
|         self.assertIn('u-boot-dtb', cbfs.files) |         self.assertIn('u-boot-dtb', cbfs.files) | ||||||
|         cfile = cbfs.files['u-boot-dtb'] |         cfile = cbfs.files['u-boot-dtb'] | ||||||
|         self.assertEqual('u-boot-dtb', cfile.name) |         self.assertEqual('u-boot-dtb', cfile.name) | ||||||
|         self.assertEqual(offset, cfile.offset) |         self.assertEqual(offset, cfile.offset) | ||||||
|  |         if cbfs_offset is not None: | ||||||
|  |             self.assertEqual(cbfs_offset, cfile.cbfs_offset) | ||||||
|         self.assertEqual(U_BOOT_DTB_DATA, cfile.data) |         self.assertEqual(U_BOOT_DTB_DATA, cfile.data) | ||||||
|         self.assertEqual(cbfs_util.TYPE_RAW, cfile.ftype) |         self.assertEqual(cbfs_util.TYPE_RAW, cfile.ftype) | ||||||
|         self.assertEqual(cbfs_util.COMPRESS_NONE, cfile.compress) |         self.assertEqual(cbfs_util.COMPRESS_NONE, cfile.compress) | ||||||
| @@ -157,13 +164,14 @@ class TestCbfs(unittest.TestCase): | |||||||
|         self._check_uboot(cbfs) |         self._check_uboot(cbfs) | ||||||
|         self._check_dtb(cbfs) |         self._check_dtb(cbfs) | ||||||
|  |  | ||||||
|     def _get_expected_cbfs(self, size, arch='x86', compress=None): |     def _get_expected_cbfs(self, size, arch='x86', compress=None, base=None): | ||||||
|         """Get the file created by cbfstool for a particular scenario |         """Get the file created by cbfstool for a particular scenario | ||||||
|  |  | ||||||
|         Args: |         Args: | ||||||
|             size: Size of the CBFS in bytes |             size: Size of the CBFS in bytes | ||||||
|             arch: Architecture of the CBFS, as a string |             arch: Architecture of the CBFS, as a string | ||||||
|             compress: Compression to use, e.g. cbfs_util.COMPRESS_LZMA |             compress: Compression to use, e.g. cbfs_util.COMPRESS_LZMA | ||||||
|  |             base: Base address of file, or None to put it anywhere | ||||||
|  |  | ||||||
|         Returns: |         Returns: | ||||||
|             Resulting CBFS file, or None if cbfstool is not available |             Resulting CBFS file, or None if cbfstool is not available | ||||||
| @@ -172,14 +180,18 @@ class TestCbfs(unittest.TestCase): | |||||||
|             return None |             return None | ||||||
|         cbfs_fname = os.path.join(self._indir, 'test.cbfs') |         cbfs_fname = os.path.join(self._indir, 'test.cbfs') | ||||||
|         cbfs_util.cbfstool(cbfs_fname, 'create', '-m', arch, '-s', '%#x' % size) |         cbfs_util.cbfstool(cbfs_fname, 'create', '-m', arch, '-s', '%#x' % size) | ||||||
|  |         if base: | ||||||
|  |             base = [(1 << 32) - size + b for b in base] | ||||||
|         cbfs_util.cbfstool(cbfs_fname, 'add', '-n', 'u-boot', '-t', 'raw', |         cbfs_util.cbfstool(cbfs_fname, 'add', '-n', 'u-boot', '-t', 'raw', | ||||||
|                            '-c', compress and compress[0] or 'none', |                            '-c', compress and compress[0] or 'none', | ||||||
|                            '-f', tools.GetInputFilename( |                            '-f', tools.GetInputFilename( | ||||||
|                                compress and 'compress' or 'u-boot.bin')) |                                compress and 'compress' or 'u-boot.bin'), | ||||||
|  |                            base=base[0] if base else None) | ||||||
|         cbfs_util.cbfstool(cbfs_fname, 'add', '-n', 'u-boot-dtb', '-t', 'raw', |         cbfs_util.cbfstool(cbfs_fname, 'add', '-n', 'u-boot-dtb', '-t', 'raw', | ||||||
|                            '-c', compress and compress[1] or 'none', |                            '-c', compress and compress[1] or 'none', | ||||||
|                            '-f', tools.GetInputFilename( |                            '-f', tools.GetInputFilename( | ||||||
|                                compress and 'compress' or 'u-boot.dtb')) |                                compress and 'compress' or 'u-boot.dtb'), | ||||||
|  |                            base=base[1] if base else None) | ||||||
|         return cbfs_fname |         return cbfs_fname | ||||||
|  |  | ||||||
|     def _compare_expected_cbfs(self, data, cbfstool_fname): |     def _compare_expected_cbfs(self, data, cbfstool_fname): | ||||||
| @@ -407,7 +419,7 @@ class TestCbfs(unittest.TestCase): | |||||||
|             self.skipTest('lz4 --no-frame-crc not available') |             self.skipTest('lz4 --no-frame-crc not available') | ||||||
|         size = 0x140 |         size = 0x140 | ||||||
|         cbw = CbfsWriter(size) |         cbw = CbfsWriter(size) | ||||||
|         cbw.add_file_raw('u-boot', COMPRESS_DATA, |         cbw.add_file_raw('u-boot', COMPRESS_DATA, None, | ||||||
|                          compress=cbfs_util.COMPRESS_LZ4) |                          compress=cbfs_util.COMPRESS_LZ4) | ||||||
|         data = cbw.get_data() |         data = cbw.get_data() | ||||||
|  |  | ||||||
| @@ -431,7 +443,7 @@ class TestCbfs(unittest.TestCase): | |||||||
|             self.skipTest('lz4 --no-frame-crc not available') |             self.skipTest('lz4 --no-frame-crc not available') | ||||||
|         size = 0x140 |         size = 0x140 | ||||||
|         cbw = CbfsWriter(size) |         cbw = CbfsWriter(size) | ||||||
|         cbw.add_file_raw('u-boot', COMPRESS_DATA, |         cbw.add_file_raw('u-boot', COMPRESS_DATA, None, | ||||||
|                          compress=cbfs_util.COMPRESS_LZ4) |                          compress=cbfs_util.COMPRESS_LZ4) | ||||||
|         data = cbw.get_data() |         data = cbw.get_data() | ||||||
|  |  | ||||||
| @@ -517,9 +529,9 @@ class TestCbfs(unittest.TestCase): | |||||||
|             self.skipTest('lz4 --no-frame-crc not available') |             self.skipTest('lz4 --no-frame-crc not available') | ||||||
|         size = 0x140 |         size = 0x140 | ||||||
|         cbw = CbfsWriter(size) |         cbw = CbfsWriter(size) | ||||||
|         cbw.add_file_raw('u-boot', COMPRESS_DATA, |         cbw.add_file_raw('u-boot', COMPRESS_DATA, None, | ||||||
|                          compress=cbfs_util.COMPRESS_LZ4) |                          compress=cbfs_util.COMPRESS_LZ4) | ||||||
|         cbw.add_file_raw('u-boot-dtb', COMPRESS_DATA, |         cbw.add_file_raw('u-boot-dtb', COMPRESS_DATA, None, | ||||||
|                          compress=cbfs_util.COMPRESS_LZMA) |                          compress=cbfs_util.COMPRESS_LZMA) | ||||||
|         data = cbw.get_data() |         data = cbw.get_data() | ||||||
|  |  | ||||||
| @@ -556,6 +568,58 @@ class TestCbfs(unittest.TestCase): | |||||||
|         cbfs_fname = self._get_expected_cbfs(size=size) |         cbfs_fname = self._get_expected_cbfs(size=size) | ||||||
|         self._compare_expected_cbfs(data, cbfs_fname) |         self._compare_expected_cbfs(data, cbfs_fname) | ||||||
|  |  | ||||||
|  |     def test_cbfs_offset(self): | ||||||
|  |         """Test a CBFS with files at particular offsets""" | ||||||
|  |         size = 0x200 | ||||||
|  |         cbw = CbfsWriter(size) | ||||||
|  |         cbw.add_file_raw('u-boot', U_BOOT_DATA, 0x40) | ||||||
|  |         cbw.add_file_raw('u-boot-dtb', U_BOOT_DTB_DATA, 0x140) | ||||||
|  |  | ||||||
|  |         data = cbw.get_data() | ||||||
|  |         cbfs = self._check_hdr(data, size) | ||||||
|  |         self._check_uboot(cbfs, ftype=cbfs_util.TYPE_RAW, offset=0x40, | ||||||
|  |                           cbfs_offset=0x40) | ||||||
|  |         self._check_dtb(cbfs, offset=0x40, cbfs_offset=0x140) | ||||||
|  |  | ||||||
|  |         cbfs_fname = self._get_expected_cbfs(size=size, base=(0x40, 0x140)) | ||||||
|  |         self._compare_expected_cbfs(data, cbfs_fname) | ||||||
|  |  | ||||||
|  |     def test_cbfs_invalid_file_type_header(self): | ||||||
|  |         """Check handling of an invalid file type when outputting a header""" | ||||||
|  |         size = 0xb0 | ||||||
|  |         cbw = CbfsWriter(size) | ||||||
|  |         cfile = cbw.add_file_raw('u-boot', U_BOOT_DATA, 0) | ||||||
|  |  | ||||||
|  |         # Change the type manually before generating the CBFS, and make sure | ||||||
|  |         # that the generator complains | ||||||
|  |         cfile.ftype = 0xff | ||||||
|  |         with self.assertRaises(ValueError) as e: | ||||||
|  |             cbw.get_data() | ||||||
|  |         self.assertIn('Unknown file type 0xff', str(e.exception)) | ||||||
|  |  | ||||||
|  |     def test_cbfs_offset_conflict(self): | ||||||
|  |         """Test a CBFS with files that want to overlap""" | ||||||
|  |         size = 0x200 | ||||||
|  |         cbw = CbfsWriter(size) | ||||||
|  |         cbw.add_file_raw('u-boot', U_BOOT_DATA, 0x40) | ||||||
|  |         cbw.add_file_raw('u-boot-dtb', U_BOOT_DTB_DATA, 0x80) | ||||||
|  |  | ||||||
|  |         with self.assertRaises(ValueError) as e: | ||||||
|  |             cbw.get_data() | ||||||
|  |         self.assertIn('No space for data before pad offset', str(e.exception)) | ||||||
|  |  | ||||||
|  |     def test_cbfs_check_offset(self): | ||||||
|  |         """Test that we can discover the offset of a file after writing it""" | ||||||
|  |         size = 0xb0 | ||||||
|  |         cbw = CbfsWriter(size) | ||||||
|  |         cbw.add_file_raw('u-boot', U_BOOT_DATA) | ||||||
|  |         cbw.add_file_raw('u-boot-dtb', U_BOOT_DTB_DATA) | ||||||
|  |         data = cbw.get_data() | ||||||
|  |  | ||||||
|  |         cbfs = cbfs_util.CbfsReader(data) | ||||||
|  |         self.assertEqual(0x38, cbfs.files['u-boot'].cbfs_offset) | ||||||
|  |         self.assertEqual(0x78, cbfs.files['u-boot-dtb'].cbfs_offset) | ||||||
|  |  | ||||||
|  |  | ||||||
| if __name__ == '__main__': | if __name__ == '__main__': | ||||||
|     unittest.main() |     unittest.main() | ||||||
|   | |||||||
| @@ -52,6 +52,7 @@ class Entry_cbfs(Entry): | |||||||
|                 filename = "u-boot.dtb"; |                 filename = "u-boot.dtb"; | ||||||
|                 cbfs-type = "raw"; |                 cbfs-type = "raw"; | ||||||
|                 cbfs-compress = "lz4"; |                 cbfs-compress = "lz4"; | ||||||
|  |                 cbfs-offset = <0x100000>; | ||||||
|             }; |             }; | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
| @@ -110,6 +111,15 @@ class Entry_cbfs(Entry): | |||||||
|             to add a flat binary with a load/start address, similar to the |             to add a flat binary with a load/start address, similar to the | ||||||
|             'add-flat-binary' option in cbfstool. |             'add-flat-binary' option in cbfstool. | ||||||
|  |  | ||||||
|  |     cbfs-offset: | ||||||
|  |         This is the offset of the file's data within the CBFS. It is used to | ||||||
|  |         specify where the file should be placed in cases where a fixed position | ||||||
|  |         is needed. Typical uses are for code which is not relocatable and must | ||||||
|  |         execute in-place from a particular address. This works because SPI flash | ||||||
|  |         is generally mapped into memory on x86 devices. The file header is | ||||||
|  |         placed before this offset so that the data start lines up exactly with | ||||||
|  |         the chosen offset. If this property is not provided, then the file is | ||||||
|  |         placed in the next available spot. | ||||||
|  |  | ||||||
|     The current implementation supports only a subset of CBFS features. It does |     The current implementation supports only a subset of CBFS features. It does | ||||||
|     not support other file types (e.g. payload), adding multiple files (like the |     not support other file types (e.g. payload), adding multiple files (like the | ||||||
| @@ -172,9 +182,10 @@ class Entry_cbfs(Entry): | |||||||
|                 return False |                 return False | ||||||
|             data = entry.GetData() |             data = entry.GetData() | ||||||
|             if entry._type == 'raw': |             if entry._type == 'raw': | ||||||
|                 cbfs.add_file_raw(entry._cbfs_name, data, entry._cbfs_compress) |                 cbfs.add_file_raw(entry._cbfs_name, data, entry._cbfs_offset, | ||||||
|  |                                   entry._cbfs_compress) | ||||||
|             elif entry._type == 'stage': |             elif entry._type == 'stage': | ||||||
|                 cbfs.add_file_stage(entry._cbfs_name, data) |                 cbfs.add_file_stage(entry._cbfs_name, data, entry._cbfs_offset) | ||||||
|         data = cbfs.get_data() |         data = cbfs.get_data() | ||||||
|         self.SetContents(data) |         self.SetContents(data) | ||||||
|         return True |         return True | ||||||
| @@ -186,6 +197,7 @@ class Entry_cbfs(Entry): | |||||||
|             entry._cbfs_name = fdt_util.GetString(node, 'cbfs-name', entry.name) |             entry._cbfs_name = fdt_util.GetString(node, 'cbfs-name', entry.name) | ||||||
|             entry._type = fdt_util.GetString(node, 'cbfs-type') |             entry._type = fdt_util.GetString(node, 'cbfs-type') | ||||||
|             compress = fdt_util.GetString(node, 'cbfs-compress', 'none') |             compress = fdt_util.GetString(node, 'cbfs-compress', 'none') | ||||||
|  |             entry._cbfs_offset = fdt_util.GetInt(node, 'cbfs-offset') | ||||||
|             entry._cbfs_compress = cbfs_util.find_compress(compress) |             entry._cbfs_compress = cbfs_util.find_compress(compress) | ||||||
|             if entry._cbfs_compress is None: |             if entry._cbfs_compress is None: | ||||||
|                 self.Raise("Invalid compression in '%s': '%s'" % |                 self.Raise("Invalid compression in '%s': '%s'" % | ||||||
|   | |||||||
| @@ -2012,5 +2012,28 @@ class TestFunctional(unittest.TestCase): | |||||||
|         self.assertIn('Could not complete processing of contents', |         self.assertIn('Could not complete processing of contents', | ||||||
|                       str(e.exception)) |                       str(e.exception)) | ||||||
|  |  | ||||||
|  |     def testCbfsOffset(self): | ||||||
|  |         """Test a CBFS with files at particular offsets | ||||||
|  |  | ||||||
|  |         Like all CFBS tests, this is just checking the logic that calls | ||||||
|  |         cbfs_util. See cbfs_util_test for fully tests (e.g. test_cbfs_offset()). | ||||||
|  |         """ | ||||||
|  |         data = self._DoReadFile('114_cbfs_offset.dts') | ||||||
|  |         size = 0x200 | ||||||
|  |  | ||||||
|  |         cbfs = cbfs_util.CbfsReader(data) | ||||||
|  |         self.assertEqual(size, cbfs.rom_size) | ||||||
|  |  | ||||||
|  |         self.assertIn('u-boot', cbfs.files) | ||||||
|  |         cfile = cbfs.files['u-boot'] | ||||||
|  |         self.assertEqual(U_BOOT_DATA, cfile.data) | ||||||
|  |         self.assertEqual(0x40, cfile.cbfs_offset) | ||||||
|  |  | ||||||
|  |         self.assertIn('u-boot-dtb', cbfs.files) | ||||||
|  |         cfile2 = cbfs.files['u-boot-dtb'] | ||||||
|  |         self.assertEqual(U_BOOT_DTB_DATA, cfile2.data) | ||||||
|  |         self.assertEqual(0x140, cfile2.cbfs_offset) | ||||||
|  |  | ||||||
|  |  | ||||||
| if __name__ == "__main__": | if __name__ == "__main__": | ||||||
|     unittest.main() |     unittest.main() | ||||||
|   | |||||||
							
								
								
									
										26
									
								
								tools/binman/test/114_cbfs_offset.dts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								tools/binman/test/114_cbfs_offset.dts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | |||||||
|  | // SPDX-License-Identifier: GPL-2.0+ | ||||||
|  |  | ||||||
|  | /dts-v1/; | ||||||
|  |  | ||||||
|  | / { | ||||||
|  | 	#address-cells = <1>; | ||||||
|  | 	#size-cells = <1>; | ||||||
|  |  | ||||||
|  | 	binman { | ||||||
|  | 		sort-by-offset; | ||||||
|  | 		end-at-4gb; | ||||||
|  | 		size = <0x200>; | ||||||
|  | 		cbfs { | ||||||
|  | 			size = <0x200>; | ||||||
|  | 			offset = <0xfffffe00>; | ||||||
|  | 			u-boot { | ||||||
|  | 				cbfs-offset = <0x40>; | ||||||
|  | 				cbfs-type = "raw"; | ||||||
|  | 			}; | ||||||
|  | 			u-boot-dtb { | ||||||
|  | 				cbfs-offset = <0x140>; | ||||||
|  | 				cbfs-type = "raw"; | ||||||
|  | 			}; | ||||||
|  | 		}; | ||||||
|  | 	}; | ||||||
|  | }; | ||||||
		Reference in New Issue
	
	Block a user