summaryrefslogtreecommitdiff
path: root/payloads/libpayload/libcbfs
diff options
context:
space:
mode:
authorJulius Werner <jwerner@chromium.org>2016-05-12 14:40:57 -0700
committerJulius Werner <jwerner@chromium.org>2016-05-17 22:48:28 +0200
commit2296479dfd886d4cf4c145f1d3ed1ec64dc20630 (patch)
tree15b93696b4470064fb080efcbc04a260bddb8129 /payloads/libpayload/libcbfs
parent4bab6e79b078c76d0a42883c4b4c9c68615d5a1e (diff)
downloadcoreboot-2296479dfd886d4cf4c145f1d3ed1ec64dc20630.tar.xz
libpayload: cbfs: Add cbfs_handle API for more fine-grained accesses
The libpayload CBFS APIs are pretty old and clunky, primarily because of the way the cbfs_media struct may or may not be passed in and may be initialized inside the API calls in a way that cannot be passed back out again. Due to this, the only real CBFS access function we have always reads a whole file with all metadata, and everything else has to build on top of that. This makes certain tasks like reading just a file attribute very inefficient on non-memory-mapped platforms (because you always have to map the whole file). This patch isn't going to fix the world, but will allow a bit more flexibility by bolting a new API on top which uses a struct cbfs_handle to represent a found but not yet read file. A cbfs_handle contains a copy of the cbfs_media needed to read the file, so it can be kept and passed around to read individual parts of it after the initial lookup. The existing (non-media) legacy API is retained for backwards compatibility, as is cbfs_file_get_contents() (which is most likely what more recent payloads would have used, and also a good convenience wrapper for the most simple use case), but they are now implemented on top of the new API. TEST=Booted Oak, made sure that firmware screens and software sync worked okay. Change-Id: I269f3979e77ae691ee9d4e1ab564eff6d45b7cbe Signed-off-by: Julius Werner <jwerner@chromium.org> Reviewed-on: https://review.coreboot.org/14810 Tested-by: build bot (Jenkins) Reviewed-by: Aaron Durbin <adurbin@chromium.org>
Diffstat (limited to 'payloads/libpayload/libcbfs')
-rw-r--r--payloads/libpayload/libcbfs/cbfs.c17
-rw-r--r--payloads/libpayload/libcbfs/cbfs_core.c189
2 files changed, 108 insertions, 98 deletions
diff --git a/payloads/libpayload/libcbfs/cbfs.c b/payloads/libpayload/libcbfs/cbfs.c
index a1cc7e443c..38b1ff8c71 100644
--- a/payloads/libpayload/libcbfs/cbfs.c
+++ b/payloads/libpayload/libcbfs/cbfs.c
@@ -159,7 +159,22 @@ void *cbfs_load_payload(struct cbfs_media *media, const char *name)
}
struct cbfs_file *cbfs_find(const char *name) {
- return cbfs_get_file(CBFS_DEFAULT_MEDIA, name);
+ struct cbfs_handle *handle = cbfs_get_handle(CBFS_DEFAULT_MEDIA, name);
+ struct cbfs_media *m = &handle->media;
+ void *ret;
+
+ if (!handle)
+ return NULL;
+
+ ret = m->map(m, handle->media_offset,
+ handle->content_offset + handle->content_size);
+ if (ret == CBFS_MEDIA_INVALID_MAP_ADDRESS) {
+ free(handle);
+ return NULL;
+ }
+
+ free(handle);
+ return ret;
}
void *cbfs_find_file(const char *name, int type) {
diff --git a/payloads/libpayload/libcbfs/cbfs_core.c b/payloads/libpayload/libcbfs/cbfs_core.c
index c32d262b33..7e660ce83c 100644
--- a/payloads/libpayload/libcbfs/cbfs_core.c
+++ b/payloads/libpayload/libcbfs/cbfs_core.c
@@ -136,12 +136,15 @@ static int get_cbfs_range(uint32_t *offset, uint32_t *cbfs_end,
}
/* public API starts here*/
-struct cbfs_file *cbfs_get_file(struct cbfs_media *media, const char *name)
+struct cbfs_handle *cbfs_get_handle(struct cbfs_media *media, const char *name)
{
const char *vardata;
uint32_t offset, cbfs_end, vardata_len;
- struct cbfs_file file, *file_ptr;
- struct cbfs_media default_media;
+ struct cbfs_file file;
+ struct cbfs_handle *handle = malloc(sizeof(*handle));
+
+ if (!handle)
+ return NULL;
if (get_cbfs_range(&offset, &cbfs_end, media)) {
ERROR("Failed to find cbfs range\n");
@@ -149,11 +152,13 @@ struct cbfs_file *cbfs_get_file(struct cbfs_media *media, const char *name)
}
if (media == CBFS_DEFAULT_MEDIA) {
- media = &default_media;
+ media = &handle->media;
if (init_default_cbfs_media(media) != 0) {
ERROR("Failed to initialize default media.\n");
return NULL;
}
+ } else {
+ memcpy(&handle->media, media, sizeof(*media));
}
DEBUG("CBFS location: 0x%x~0x%x\n", offset, cbfs_end);
@@ -189,10 +194,14 @@ struct cbfs_file *cbfs_get_file(struct cbfs_media *media, const char *name)
DEBUG("Found file (offset=0x%x, len=%d).\n",
offset + file_offset, file_len);
media->unmap(media, vardata);
- file_ptr = media->map(media, offset,
- file_offset + file_len);
media->close(media);
- return file_ptr;
+ handle->type = ntohl(file.type);
+ handle->media_offset = offset;
+ handle->content_offset = file_offset;
+ handle->content_size = file_len;
+ handle->attribute_offset =
+ ntohl(file.attributes_offset);
+ return handle;
} else {
DEBUG(" (unmatched file @0x%x: %s)\n", offset,
vardata);
@@ -209,118 +218,104 @@ struct cbfs_file *cbfs_get_file(struct cbfs_media *media, const char *name)
return NULL;
}
-void *cbfs_get_file_content(struct cbfs_media *media, const char *name,
- int type, size_t *sz)
+void *cbfs_get_contents(struct cbfs_handle *handle, size_t *size, size_t limit)
{
- /*
- * get file (possibly compressed) data. we pass through 'media' to
- * cbfs_get_file (don't call init_default_cbfs_media) here so that
- * cbfs_get_file can see whether the call is for CBFS_DEFAULT_MEDIA
- * or not. Therefore, we don't (can't) unmap the file but it's caused
- * by a pre-existing inherent problem with cbfs_get_file (and all
- * callers are suffering from it).
- */
- struct cbfs_file *file = cbfs_get_file(media, name);
-
- if (file == NULL) {
- ERROR("Could not find file '%s'.\n", name);
- return NULL;
+ struct cbfs_media *m = &handle->media;
+ size_t on_media_size = handle->content_size;
+ int algo = CBFS_COMPRESS_NONE;
+ void *ret = NULL;
+ size_t dummy_size;
+
+ if (!size)
+ size = &dummy_size;
+
+ struct cbfs_file_attr_compression *comp =
+ cbfs_get_attr(handle, CBFS_FILE_ATTR_TAG_COMPRESSION);
+ if (comp) {
+ algo = ntohl(comp->compression);
+ DEBUG("File '%s' is compressed (alg=%d)\n", name, algo);
+ *size = ntohl(comp->decompressed_size);
+ /* TODO: Implement partial decompression with |limit| */
}
- if (sz)
- *sz = 0;
-
- if (ntohl(file->type) != type) {
- ERROR("File '%s' is of type %x, but we requested %x.\n", name,
- ntohl(file->type), type);
- return NULL;
+ if (algo == CBFS_COMPRESS_NONE) {
+ if (limit != 0 && limit < on_media_size) {
+ *size = limit;
+ on_media_size = limit;
+ } else {
+ *size = on_media_size;
+ }
}
- void *file_content = (void *)CBFS_SUBHEADER(file);
-
- struct cbfs_file_attribute *attr =
- cbfs_file_find_attr(file, CBFS_FILE_ATTR_TAG_COMPRESSION);
+ void *data = m->map(m, handle->media_offset + handle->content_offset,
+ on_media_size);
+ if (data == CBFS_MEDIA_INVALID_MAP_ADDRESS)
+ return NULL;
- size_t final_size = ntohl(file->len);
- int compression_algo = CBFS_COMPRESS_NONE;
- if (attr) {
- struct cbfs_file_attr_compression *comp =
- (struct cbfs_file_attr_compression *)attr;
- compression_algo = ntohl(comp->compression);
- DEBUG("File '%s' is compressed (alg=%d)\n",
- name, compression_algo);
- final_size = ntohl(comp->decompressed_size);
+ ret = malloc(*size);
+ if (ret != NULL && !cbfs_decompress(algo, data, ret, *size)) {
+ free(ret);
+ ret = NULL;
}
- void *dst = malloc(final_size);
- if (dst == NULL)
- goto err;
-
- if (!cbfs_decompress(compression_algo, file_content, dst, final_size))
- goto err;
-
- if (sz)
- *sz = final_size;
-
- return dst;
-
-err:
- free(dst);
- return NULL;
+ m->unmap(m, data);
+ return ret;
}
-struct cbfs_file_attribute *cbfs_file_first_attr(struct cbfs_file *file)
+void *cbfs_get_file_content(struct cbfs_media *media, const char *name,
+ int type, size_t *sz)
{
- /* attributes_offset should be 0 when there is no attribute, but all
- * values that point into the cbfs_file header are invalid, too. */
- if (ntohl(file->attributes_offset) <= sizeof(*file))
- return NULL;
+ void *ret = NULL;
+ struct cbfs_handle *handle = cbfs_get_handle(media, name);
- /* There needs to be enough space for the file header and one
- * attribute header for this to make sense. */
- if (ntohl(file->offset) <=
- sizeof(*file) + sizeof(struct cbfs_file_attribute))
+ if (!handle)
return NULL;
- return (struct cbfs_file_attribute *)
- (((uint8_t *)file) + ntohl(file->attributes_offset));
+ if (handle->type == type)
+ ret = cbfs_get_contents(handle, sz, 0);
+ else
+ ERROR("File '%s' is of type %x, but we requested %x.\n", name,
+ handle->type, type);
+
+ free(handle);
+ return ret;
}
-struct cbfs_file_attribute *cbfs_file_next_attr(struct cbfs_file *file,
- struct cbfs_file_attribute *attr)
+void *cbfs_get_attr(struct cbfs_handle *handle, uint32_t tag)
{
- /* ex falso sequitur quodlibet */
- if (attr == NULL)
- return NULL;
-
- /* Is there enough space for another attribute? */
- if ((uint8_t *)attr + ntohl(attr->len) +
- sizeof(struct cbfs_file_attribute) >=
- (uint8_t *)file + ntohl(file->offset))
- return NULL;
+ struct cbfs_media *m = &handle->media;
+ uint32_t offset = handle->media_offset + handle->attribute_offset;
+ uint32_t end = handle->media_offset + handle->content_offset;
+ struct cbfs_file_attribute attr;
+ void *ret;
- struct cbfs_file_attribute *next = (struct cbfs_file_attribute *)
- (((uint8_t *)attr) + ntohl(attr->len));
- /* If any, "unused" attributes must come last. */
- if (ntohl(next->tag) == CBFS_FILE_ATTR_TAG_UNUSED)
- return NULL;
- if (ntohl(next->tag) == CBFS_FILE_ATTR_TAG_UNUSED2)
+ /* attribute_offset should be 0 when there is no attribute, but all
+ * values that point into the cbfs_file header are invalid, too. */
+ if (handle->attribute_offset <= sizeof(struct cbfs_file))
return NULL;
- return next;
-}
-
-struct cbfs_file_attribute *cbfs_file_find_attr(struct cbfs_file *file,
- uint32_t tag)
-{
- struct cbfs_file_attribute *attr = cbfs_file_first_attr(file);
- while (attr) {
- if (ntohl(attr->tag) == tag)
- break;
- attr = cbfs_file_next_attr(file, attr);
+ m->open(m);
+ while (offset + sizeof(attr) <= end) {
+ if (m->read(m, &attr, offset, sizeof(attr)) != sizeof(attr)) {
+ ERROR("Failed to read attribute header %#x\n", offset);
+ m->close(m);
+ return NULL;
+ }
+ if (ntohl(attr.tag) != tag) {
+ offset += ntohl(attr.len);
+ continue;
+ }
+ ret = m->map(m, offset, ntohl(attr.len));
+ if (ret == CBFS_MEDIA_INVALID_MAP_ADDRESS) {
+ ERROR("Failed to map attribute at %#x\n", offset);
+ m->close(m);
+ return NULL;
+ }
+ return ret;
}
- return attr;
+ m->close(m);
+ return NULL;
}
int cbfs_decompress(int algo, void *src, void *dst, int len)