[coreboot-gerrit] New patch to review for coreboot: libpayload: cbfs: Add cbfs_handle API for more fine-grained accesses

Julius Werner (jwerner@chromium.org) gerrit at coreboot.org
Fri May 13 22:56:40 CEST 2016


Julius Werner (jwerner at chromium.org) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/14810

-gerrit

commit 8f2f917a9fd77f6df3738ce18ceb8229b6a82628
Author: Julius Werner <jwerner at chromium.org>
Date:   Thu May 12 14:40:57 2016 -0700

    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 (with dependent depthcharge change).
    
    Change-Id: I269f3979e77ae691ee9d4e1ab564eff6d45b7cbe
    Signed-off-by: Julius Werner <jwerner at chromium.org>
---
 payloads/libpayload/include/cbfs_core.h |  42 +++----
 payloads/libpayload/libcbfs/cbfs.c      |  17 ++-
 payloads/libpayload/libcbfs/cbfs_core.c | 190 +++++++++++++++-----------------
 3 files changed, 129 insertions(+), 120 deletions(-)

diff --git a/payloads/libpayload/include/cbfs_core.h b/payloads/libpayload/include/cbfs_core.h
index 4cbc4c0..f94056d 100644
--- a/payloads/libpayload/include/cbfs_core.h
+++ b/payloads/libpayload/include/cbfs_core.h
@@ -168,19 +168,6 @@ struct cbfs_file_attr_hash {
 	uint8_t  hash_data[];
 } __PACKED;
 
-/* Given a cbfs_file, return the first file attribute, or NULL. */
-struct cbfs_file_attribute *cbfs_file_first_attr(struct cbfs_file *file);
-
-/* Given a cbfs_file and a cbfs_file_attribute, return the attribute that
- * follows it, or NULL. */
-struct cbfs_file_attribute *cbfs_file_next_attr(struct cbfs_file *file,
-	struct cbfs_file_attribute *attr);
-
-/* Given a cbfs_file and an attribute tag, return the first instance of the
- * attribute or NULL if none found. */
-struct cbfs_file_attribute *cbfs_file_find_attr(struct cbfs_file *file,
-	uint32_t tag);
-
 /*** Component sub-headers ***/
 
 /* Following are component sub-headers for the "standard"
@@ -224,9 +211,6 @@ struct cbfs_optionrom {
 	uint32_t len;
 } __attribute__((packed));
 
-#define CBFS_NAME(_c) (((char *) (_c)) + sizeof(struct cbfs_file))
-#define CBFS_SUBHEADER(_p) ( (void *) ((((uint8_t *) (_p)) + ntohl((_p)->offset))) )
-
 #define CBFS_MEDIA_INVALID_MAP_ADDRESS	((void*)(0xffffffff))
 #define CBFS_DEFAULT_MEDIA		((void*)(0x0))
 
@@ -258,9 +242,6 @@ struct cbfs_media {
 	int (*close)(struct cbfs_media *media);
 };
 
-/* returns pointer to a file entry inside CBFS or NULL on error */
-struct cbfs_file *cbfs_get_file(struct cbfs_media *media, const char *name);
-
 /*
  * Returns pointer to a copy of the file content or NULL on error.
  * If the file is compressed, data will be decompressed.
@@ -276,4 +257,27 @@ int cbfs_decompress(int algo, void *src, void *dst, int len);
  *  on failure */
 const struct cbfs_header *cbfs_get_header(struct cbfs_media *media);
 
+/* Persistent handle to a CBFS file that has not yet been fully mapped. */
+struct cbfs_handle {
+	struct cbfs_media media;	/* copy of original media object */
+	u32 type;			/* CBFS file type */
+	u32 media_offset;		/* offset from beginning of media */
+	u32 attribute_offset;		/* relative offset of attributes */
+	u32 content_offset;		/* relative offset of contents */
+	u32 content_size;		/* length of file contents in bytes */
+};
+
+/* Returns handle to CBFS file, or NULL on error. Does not yet map contents.
+ * Caller is responsible to free() returned handle after use. */
+struct cbfs_handle *cbfs_get_handle(struct cbfs_media *media, const char *name);
+
+/* Given a cbfs_handle and an attribute tag, return a mapping for the first
+ * instance of the attribute or NULL if none found. */
+void *cbfs_get_attr(struct cbfs_handle *handle, uint32_t tag);
+
+/* Given a cbfs_handle, returns the (decompressed) file contents in a buffer,
+ * or NULL on error. If |size| is passed, will store amount of bytes read there.
+ * If |limit| is not 0, will only return up to that many bytes. */
+void *cbfs_get_contents(struct cbfs_handle *handle, size_t *size, size_t limit);
+
 #endif
diff --git a/payloads/libpayload/libcbfs/cbfs.c b/payloads/libpayload/libcbfs/cbfs.c
index a1cc7e4..38b1ff8 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 c32d262..602fb4f 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,99 @@ 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| */
+	} else if (limit != 0 && limit < on_media_size) {
+		*size = limit;
+		on_media_size = limit;
+	} else {
+		*size = on_media_size;
 	}
 
-	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;
-	}
+	void *data = m->map(m, handle->media_offset + handle->content_offset,
+			    on_media_size);
+	if (data == CBFS_MEDIA_INVALID_MAP_ADDRESS)
+		goto unmap;
 
-	void *file_content = (void *)CBFS_SUBHEADER(file);
+	ret = malloc(*size);
+	if (ret != NULL && !cbfs_decompress(algo, data, ret, *size))
+		free(ret);
 
-	struct cbfs_file_attribute *attr =
-		cbfs_file_find_attr(file, CBFS_FILE_ATTR_TAG_COMPRESSION);
-
-	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);
-	}
-
-	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;
+unmap:
+	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)



More information about the coreboot-gerrit mailing list