diff options
Diffstat (limited to 'util/cbfstool/cbfs_image.c')
-rw-r--r-- | util/cbfstool/cbfs_image.c | 154 |
1 files changed, 111 insertions, 43 deletions
diff --git a/util/cbfstool/cbfs_image.c b/util/cbfstool/cbfs_image.c index d07f80660f..4ecb461f5e 100644 --- a/util/cbfstool/cbfs_image.c +++ b/util/cbfstool/cbfs_image.c @@ -109,8 +109,11 @@ static size_t cbfs_calculate_file_header_size(const char *name) align_up(strlen(name) + 1, CBFS_FILENAME_ALIGN)); } +/* Only call on legacy CBFSes possessing a master header. */ static int cbfs_fix_legacy_size(struct cbfs_image *image, char *hdr_loc) { + assert(image); + assert(cbfs_is_legacy_cbfs(image)); // A bug in old cbfstool may produce extra few bytes (by alignment) and // cause cbfstool to overwrite things after free space -- which is // usually CBFS header on x86. We need to workaround that. @@ -182,41 +185,78 @@ void cbfs_get_header(struct cbfs_header *header, void *src) header->architecture = xdr_be.get32(&outheader); } -int cbfs_image_create(struct cbfs_image *image, - uint32_t architecture, - uint32_t align, - struct buffer *bootblock, - uint32_t bootblock_offset, - uint32_t header_offset, - uint32_t entries_offset) +int cbfs_image_create(struct cbfs_image *image, size_t entries_size) { assert(image); assert(image->buffer.data); - assert(bootblock); - struct cbfs_file *entry; - int32_t *rel_offset; - uint32_t cbfs_len; - void *header_loc; - size_t empty_header_len; - size_t size = image->buffer.size; + size_t empty_header_len = cbfs_calculate_file_header_size(""); + uint32_t entries_offset = 0; + uint32_t align = CBFS_ENTRY_ALIGNMENT; + if (image->has_header) { + entries_offset = image->header.offset; - DEBUG("cbfs_image_create: bootblock=0x%x+0x%zx, " - "header=0x%x+0x%zx, entries_offset=0x%x\n", - bootblock_offset, bootblock->size, header_offset, - sizeof(image->header), entries_offset); + if (entries_offset > image->buffer.size) { + ERROR("CBFS file entries are located outside CBFS itself\n"); + return -1; + } + + align = image->header.align; + } // This attribute must be given in order to prove that this module // correctly preserves certain CBFS properties. See the block comment // near the top of this file (and the associated commit message). - empty_header_len = cbfs_calculate_file_header_size(""); if (align < empty_header_len) { ERROR("CBFS must be aligned to at least %zu bytes\n", empty_header_len); return -1; } - // Adjust legcay top-aligned address to ROM offset. + if (entries_size > image->buffer.size - entries_offset) { + ERROR("CBFS doesn't have enough space to fit its file entries\n"); + return -1; + } + + if (empty_header_len > entries_size) { + ERROR("CBFS is too small to fit any header\n"); + return -1; + } + struct cbfs_file *entry_header = + (struct cbfs_file *)(image->buffer.data + entries_offset); + // This alignment is necessary in order to prove that this module + // correctly preserves certain CBFS properties. See the block comment + // near the top of this file (and the associated commit message). + entries_size -= entries_size % align; + + size_t capacity = entries_size - empty_header_len; + LOG("Created CBFS (capacity = %zu bytes)\n", capacity); + return cbfs_create_empty_entry(entry_header, capacity, ""); +} + +int cbfs_legacy_image_create(struct cbfs_image *image, + uint32_t architecture, + uint32_t align, + struct buffer *bootblock, + uint32_t bootblock_offset, + uint32_t header_offset, + uint32_t entries_offset) +{ + assert(image); + assert(image->buffer.data); + assert(bootblock); + + int32_t *rel_offset; + uint32_t cbfs_len; + void *header_loc; + size_t size = image->buffer.size; + + DEBUG("cbfs_image_create: bootblock=0x%x+0x%zx, " + "header=0x%x+0x%zx, entries_offset=0x%x\n", + bootblock_offset, bootblock->size, header_offset, + sizeof(image->header), entries_offset); + + // Adjust legacy top-aligned address to ROM offset. if (IS_TOP_ALIGNED_ADDRESS(entries_offset)) entries_offset = size + (int32_t)entries_offset; if (IS_TOP_ALIGNED_ADDRESS(bootblock_offset)) @@ -259,6 +299,7 @@ int cbfs_image_create(struct cbfs_image *image, header_loc = (image->buffer.data + header_offset); cbfs_put_header(header_loc, &image->header); + image->has_header = true; // The last 4 byte of the image contain the relative offset from the end // of the image to the master header as a 32-bit signed integer. x86 @@ -274,12 +315,6 @@ int cbfs_image_create(struct cbfs_image *image, entries_offset, align); return -1; } - if (entries_offset + empty_header_len > size) { - ERROR("Offset (0x%x+0x%zx) exceed ROM size(0x%zx)\n", - entries_offset, empty_header_len, size); - return -1; - } - entry = (struct cbfs_file *)(image->buffer.data + entries_offset); // To calculate available length, find // e = min(bootblock, header, rel_offset) where e > entries_offset. cbfs_len = size - sizeof(int32_t); @@ -287,13 +322,9 @@ int cbfs_image_create(struct cbfs_image *image, cbfs_len = bootblock_offset; if (header_offset > entries_offset && header_offset < cbfs_len) cbfs_len = header_offset; - // This alignment is necessary in order to prove that this module - // correctly preserves certain CBFS properties. See the block comment - // near the top of this file (and the associated commit message). - cbfs_len -= cbfs_len % align; - cbfs_len -= entries_offset + empty_header_len; - cbfs_create_empty_entry(entry, cbfs_len, ""); - LOG("Created CBFS image (capacity = %d bytes)\n", cbfs_len); + + if (cbfs_image_create(image, cbfs_len - entries_offset)) + return -1; return 0; } @@ -305,17 +336,31 @@ int cbfs_image_from_buffer(struct cbfs_image *out, struct buffer *in, assert(in->data); buffer_clone(&out->buffer, in); + out->has_header = false; + void *header_loc = cbfs_find_header(in->data, in->size, offset); if (header_loc) { cbfs_get_header(&out->header, header_loc); + out->has_header = true; cbfs_fix_legacy_size(out, header_loc); + } else if (offset != ~0u) { + ERROR("The -H switch is only valid on legacy images having CBFS master headers.\n"); + return 1; + } else if (!cbfs_is_valid_cbfs(out)) { + ERROR("Selected image region is not a valid CBFS.\n"); + return 1; } - return !header_loc; + + return 0; } int cbfs_copy_instance(struct cbfs_image *image, size_t copy_offset, size_t copy_size) { + assert(image); + if (!cbfs_is_legacy_cbfs(image)) + return -1; + struct cbfs_file *src_entry, *dst_entry; struct cbfs_header *copy_header; size_t align, entry_offset; @@ -411,7 +456,8 @@ static int cbfs_add_entry_at(struct cbfs_image *image, uint32_t header_size = cbfs_calculate_file_header_size(name), min_entry_size = cbfs_calculate_file_header_size(""); uint32_t len, target; - uint32_t align = image->header.align; + uint32_t align = image->has_header ? image->header.align : + CBFS_ENTRY_ALIGNMENT; target = content_offset - header_size; if (target % align) @@ -492,6 +538,11 @@ int cbfs_add_entry(struct cbfs_image *image, struct buffer *buffer, name, content_offset, header_size, buffer->size, need_size); if (IS_TOP_ALIGNED_ADDRESS(content_offset)) { + if (!cbfs_is_legacy_cbfs(image)) { + ERROR("Top-aligned offsets are only supported for legacy CBFSes (with master headers)\n"); + return -1; + } + // legacy cbfstool takes top-aligned address. uint32_t theromsize = image->header.romsize; INFO("Converting top-aligned address 0x%x to offset: 0x%x\n", @@ -798,7 +849,8 @@ int cbfs_print_entry_info(struct cbfs_image *image, struct cbfs_file *entry, int cbfs_print_directory(struct cbfs_image *image) { - cbfs_print_header_info(image); + if (cbfs_is_legacy_cbfs(image)) + cbfs_print_header_info(image); printf("%-30s %-10s %-12s Size\n", "Name", "Offset", "Type"); cbfs_walk(image, cbfs_print_entry_info, NULL); return 0; @@ -923,15 +975,17 @@ struct cbfs_header *cbfs_find_header(char *data, size_t size, struct cbfs_file *cbfs_find_first_entry(struct cbfs_image *image) { assert(image); - return (struct cbfs_file *)(image->buffer.data + - image->header.offset); + return image->has_header ? (struct cbfs_file *)(image->buffer.data + + image->header.offset) : + (struct cbfs_file *)image->buffer.data; } struct cbfs_file *cbfs_find_next_entry(struct cbfs_image *image, struct cbfs_file *entry) { uint32_t addr = cbfs_get_entry_addr(image, entry); - int align = image->header.align; + int align = image->has_header ? image->header.align : + CBFS_ENTRY_ALIGNMENT; assert(entry && cbfs_is_valid_entry(image, entry)); addr += ntohl(entry->offset) + ntohl(entry->len); addr = align_up(addr, align); @@ -944,6 +998,17 @@ uint32_t cbfs_get_entry_addr(struct cbfs_image *image, struct cbfs_file *entry) return (int32_t)((char *)entry - image->buffer.data); } +int cbfs_is_valid_cbfs(struct cbfs_image *image) +{ + return buffer_check_magic(&image->buffer, CBFS_FILE_MAGIC, + strlen(CBFS_FILE_MAGIC)); +} + +int cbfs_is_legacy_cbfs(struct cbfs_image *image) +{ + return image->has_header; +} + int cbfs_is_valid_entry(struct cbfs_image *image, struct cbfs_file *entry) { uint32_t offset = cbfs_get_entry_addr(image, entry); @@ -955,7 +1020,7 @@ int cbfs_is_valid_entry(struct cbfs_image *image, struct cbfs_file *entry) buffer_clone(&entry_data, &image->buffer); buffer_seek(&entry_data, offset); return buffer_check_magic(&entry_data, CBFS_FILE_MAGIC, - strlen(CBFS_FILE_MAGIC)); + strlen(CBFS_FILE_MAGIC)); } int cbfs_create_empty_entry(struct cbfs_file *entry, @@ -999,7 +1064,8 @@ int32_t cbfs_locate_entry(struct cbfs_image *image, const char *name, /* Default values: allow fitting anywhere in ROM. */ if (!page_size) - page_size = image->header.romsize; + page_size = image->has_header ? image->header.romsize : + image->buffer.size; if (!align) align = 1; @@ -1007,9 +1073,11 @@ int32_t cbfs_locate_entry(struct cbfs_image *image, const char *name, ERROR("Input file size (%d) greater than page size (%d).\n", size, page_size); - if (page_size % image->header.align) + uint32_t image_align = image->has_header ? image->header.align : + CBFS_ENTRY_ALIGNMENT; + if (page_size % image_align) WARN("%s: Page size (%#x) not aligned with CBFS image (%#x).\n", - __func__, page_size, image->header.align); + __func__, page_size, image_align); /* TODO Old cbfstool always assume input is a stage file (and adding * sizeof(cbfs_stage) for header. We should fix that by adding "-t" |