aboutsummaryrefslogtreecommitdiff
path: root/util/cbfstool/cbfs_image.c
diff options
context:
space:
mode:
authorSol Boucher <solb@chromium.org>2015-03-18 12:36:27 -0700
committerStefan Reinauer <stefan.reinauer@coreboot.org>2015-05-13 22:19:59 +0200
commit67a0a864be0e8b81f22ebb9d6f090d77d1da2924 (patch)
tree927c59c4696c7193f590458f5c2e092c8c0ecac0 /util/cbfstool/cbfs_image.c
parent2591e937e76ccb797d77adbcb667939ca9dfdae5 (diff)
cbfstool: New image format w/ required FMAP and w/o CBFS master header
These new-style firmware images use the FMAP of the root of knowledge about their layout, which allows them to have sections containing raw data whose offset and size can easily be determined at runtime or when modifying or flashing the image. Furthermore, they can even have multiple CBFSes, each of which occupies a different FMAP region. It is assumed that the first entry of each CBFS, including the primary one, will be located right at the start of its region. This means that the bootblock needs to be moved into its own FMAP region, but makes the CBFS master header obsolete because, with the exception of the version and alignment, all its fields are redundant once its CBFS has an entry in the FMAP. The version code will be addressed in a future commit before the new format comes into use, while the alignment will just be defined to 64 bytes in both cbfstool and coreboot itself, since there's almost no reason to ever change it in practice. The version code field and all necessary coreboot changes will come separately. BUG=chromium:470407 TEST=Build panther and nyan_big coreboot.rom and image.bin images with and without this patch, diff their hexdumps, and note that no locations differ except for those that do between subsequent builds of the same codebase. Try working with new-style images: use fmaptool to produce an FMAP section from an fmd file having raw sections and multiple CBFSes, pass the resulting file to cbfstool create -M -F, then try printing its layout and CBFSes' contents, add and remove CBFS files, and read and write raw sections. BRANCH=None Change-Id: I7dd2578d2143d0cedd652fdba5b22221fcc2184a Signed-off-by: Sol Boucher <solb@chromium.org> Original-Commit-Id: 8a670322297f83135b929a5b20ff2bd0e7d2abd3 Original-Change-Id: Ib86fb50edc66632f4e6f717909bbe4efb6c874e5 Original-Signed-off-by: Sol Boucher <solb@chromium.org> Original-Reviewed-on: https://chromium-review.googlesource.com/265863 Original-Reviewed-by: Aaron Durbin <adurbin@chromium.org> Reviewed-on: http://review.coreboot.org/10135 Tested-by: build bot (Jenkins)
Diffstat (limited to 'util/cbfstool/cbfs_image.c')
-rw-r--r--util/cbfstool/cbfs_image.c154
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"