diff --git a/src/commonlib/cbfs.c b/src/commonlib/cbfs.c index 56faf286ef..1db8d31acf 100644 --- a/src/commonlib/cbfs.c +++ b/src/commonlib/cbfs.c @@ -108,6 +108,54 @@ int cbfs_for_each_file(const struct region_device *cbfs, return -1; } +size_t cbfs_for_each_attr(void *metadata, size_t metadata_size, + size_t last_offset) +{ + struct cbfs_file_attribute *attr; + + if (!last_offset) { + struct cbfs_file *file = metadata; + size_t start_offset = read_be32(&file->attributes_offset); + if (start_offset <= sizeof(struct cbfs_file) || + start_offset + sizeof(*attr) > metadata_size) + return 0; + return start_offset; + } + + attr = metadata + last_offset; + size_t next_offset = last_offset + read_be32(&attr->len); + + if (next_offset + sizeof(*attr) > metadata_size) + return 0; + return next_offset; +} + +int cbfsf_decompression_info(struct cbfsf *fh, uint32_t *algo, size_t *size) +{ + size_t metadata_size = region_device_sz(&fh->metadata); + void *metadata = rdev_mmap_full(&fh->metadata); + size_t offs = 0; + + if (!metadata) + return -1; + + while ((offs = cbfs_for_each_attr(metadata, metadata_size, offs))) { + struct cbfs_file_attr_compression *attr = metadata + offs; + if (read_be32(&attr->tag) != CBFS_FILE_ATTR_TAG_COMPRESSION) + continue; + + *algo = read_be32(&attr->compression); + *size = read_be32(&attr->decompressed_size); + rdev_munmap(&fh->metadata, metadata); + return 0; + } + + *algo = CBFS_COMPRESS_NONE; + *size = region_device_sz(&fh->data); + rdev_munmap(&fh->metadata, metadata); + return 0; +} + static int cbfsf_file_type(struct cbfsf *fh, uint32_t *ftype) { const size_t sz = sizeof(*ftype); diff --git a/src/commonlib/include/commonlib/cbfs.h b/src/commonlib/include/commonlib/cbfs.h index c74dd708e3..5e511a7aea 100644 --- a/src/commonlib/include/commonlib/cbfs.h +++ b/src/commonlib/include/commonlib/cbfs.h @@ -55,6 +55,23 @@ static inline void cbfs_file_metadata(struct region_device *metadata, int cbfs_for_each_file(const struct region_device *cbfs, const struct cbfsf *prev, struct cbfsf *fh); +/* + * Return the offset for each CBFS attribute in a CBFS file metadata region. + * The metadata must already be fully mapped by the caller. Will return the + * offset (relative to the start of the metadata) or 0 when there are no + * further attributes. Should be called with 0 to begin, then always with + * the previously returned value until it returns 0. + */ +size_t cbfs_for_each_attr(void *metadata, size_t metadata_size, + size_t last_offset); + +/* + * Find out the decompression algorithm and decompressed size of a non-stage + * CBFS file (by parsing its metadata attributes), and return them with + * out-parameters. Returns 0 on success and < 0 on error. + */ +int cbfsf_decompression_info(struct cbfsf *fh, uint32_t *algo, size_t *size); + /* * Perform the vb2 hash over the CBFS region skipping empty file contents. * Caller is responsible for providing the hash algorithm as well as storage diff --git a/src/commonlib/include/commonlib/cbfs_serialized.h b/src/commonlib/include/commonlib/cbfs_serialized.h index c01ba1a020..1e394d280f 100644 --- a/src/commonlib/include/commonlib/cbfs_serialized.h +++ b/src/commonlib/include/commonlib/cbfs_serialized.h @@ -133,10 +133,57 @@ struct cbfs_file { char magic[8]; uint32_t len; uint32_t type; - uint32_t checksum; + uint32_t attributes_offset; uint32_t offset; } __attribute__((packed)); +/* The common fields of extended cbfs file attributes. + Attributes are expected to start with tag/len, then append their + specific fields. */ +struct cbfs_file_attribute { + uint32_t tag; + /* len covers the whole structure, incl. tag and len */ + uint32_t len; + uint8_t data[0]; +} __attribute__((packed)); + +/* Depending on how the header was initialized, it may be backed with 0x00 or + * 0xff. Support both. */ +#define CBFS_FILE_ATTR_TAG_UNUSED 0 +#define CBFS_FILE_ATTR_TAG_UNUSED2 0xffffffff +#define CBFS_FILE_ATTR_TAG_COMPRESSION 0x42435a4c +#define CBFS_FILE_ATTR_TAG_HASH 0x68736148 +#define CBFS_FILE_ATTR_TAG_POSITION 0x42435350 /* PSCB */ +#define CBFS_FILE_ATTR_TAG_ALIGNMENT 0x42434c41 /* ALCB */ + +struct cbfs_file_attr_compression { + uint32_t tag; + uint32_t len; + /* whole file compression format. 0 if no compression. */ + uint32_t compression; + uint32_t decompressed_size; +} __attribute__((packed)); + +struct cbfs_file_attr_hash { + uint32_t tag; + uint32_t len; + uint32_t hash_type; + /* hash_data is len - sizeof(struct) bytes */ + uint8_t hash_data[]; +} __attribute__((packed)); + +struct cbfs_file_attr_position { + uint32_t tag; + uint32_t len; + uint32_t position; +} __attribute__((packed)); + +struct cbfs_file_attr_align { + uint32_t tag; + uint32_t len; + uint32_t alignment; +} __attribute__((packed)); + /* * ROMCC does not understand uint64_t, so we hide future definitions as they are * unlikely to be ever needed from ROMCC