summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/commonlib/bsd/include/commonlib/bsd/cbfs_mdata.h27
-rw-r--r--src/commonlib/bsd/include/commonlib/bsd/cbfs_private.h19
-rw-r--r--src/commonlib/bsd/include/commonlib/bsd/cbfs_serialized.h3
-rw-r--r--src/include/cbfs.h196
-rw-r--r--src/lib/cbfs.c117
5 files changed, 270 insertions, 92 deletions
diff --git a/src/commonlib/bsd/include/commonlib/bsd/cbfs_mdata.h b/src/commonlib/bsd/include/commonlib/bsd/cbfs_mdata.h
new file mode 100644
index 0000000000..df13427c5f
--- /dev/null
+++ b/src/commonlib/bsd/include/commonlib/bsd/cbfs_mdata.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later */
+
+#ifndef _COMMONLIB_BSD_CBFS_MDATA_H_
+#define _COMMONLIB_BSD_CBFS_MDATA_H_
+
+#include <commonlib/bsd/cbfs_serialized.h>
+#include <stddef.h>
+#include <stdint.h>
+
+/*
+ * Helper structure to allocate space for a blob of metadata on the stack.
+ * NOTE: The fields in any union cbfs_mdata or any of its substructures from cbfs_serialized.h
+ * should always remain in the same byte order as they are stored on flash (= big endian). To
+ * avoid byte-order confusion, fields should always and only be converted to host byte order at
+ * exactly the time they are read from one of these structures into their own separate variable.
+ */
+union cbfs_mdata {
+ struct cbfs_file h;
+ uint8_t raw[CBFS_METADATA_MAX_SIZE];
+};
+
+/* Finds a CBFS attribute in a metadata block. Attribute returned as-is (still big-endian).
+ If |size| is not 0, will check that it matches the length of the attribute (if found)...
+ else caller is responsible for checking the |len| field to avoid reading out-of-bounds. */
+const void *cbfs_find_attr(const union cbfs_mdata *mdata, uint32_t attr_tag, size_t size_check);
+
+#endif /* _COMMONLIB_BSD_CBFS_MDATA_H_ */
diff --git a/src/commonlib/bsd/include/commonlib/bsd/cbfs_private.h b/src/commonlib/bsd/include/commonlib/bsd/cbfs_private.h
index df5014355c..fc2d0d0457 100644
--- a/src/commonlib/bsd/include/commonlib/bsd/cbfs_private.h
+++ b/src/commonlib/bsd/include/commonlib/bsd/cbfs_private.h
@@ -5,7 +5,7 @@
#include <commonlib/bsd/cb_err.h>
-#include <commonlib/bsd/cbfs_serialized.h>
+#include <commonlib/bsd/cbfs_mdata.h>
#include <commonlib/bsd/sysincludes.h>
#include <stdbool.h>
#include <stdint.h>
@@ -41,18 +41,6 @@
*/
#include <cbfs_glue.h>
-/*
- * Helper structure to allocate space for a blob of metadata on the stack.
- * NOTE: The fields in any union cbfs_mdata or any of its substructures from cbfs_serialized.h
- * should always remain in the same byte order as they are stored on flash (= big endian). To
- * avoid byte-order confusion, fields should always and only be converted to host byte order at
- * exactly the time they are read from one of these structures into their own separate variable.
- */
-union cbfs_mdata {
- struct cbfs_file h;
- uint8_t raw[CBFS_METADATA_MAX_SIZE];
-};
-
/* Flags that modify behavior of cbfs_walk(). */
enum cbfs_walk_flags {
/* Write the calculated hash back out to |metadata_hash->hash| rather than comparing it.
@@ -130,9 +118,4 @@ cb_err_t cbfs_mcache_lookup(const void *mcache, size_t mcache_size, const char *
/* Returns the amount of bytes actually used by the CBFS metadata cache in |mcache|. */
size_t cbfs_mcache_real_size(const void *mcache, size_t mcache_size);
-/* Finds a CBFS attribute in a metadata block. Attribute returned as-is (still big-endian).
- If |size| is not 0, will check that it matches the length of the attribute (if found)...
- else caller is responsible for checking the |len| field to avoid reading out-of-bounds. */
-const void *cbfs_find_attr(const union cbfs_mdata *mdata, uint32_t attr_tag, size_t size_check);
-
#endif /* _COMMONLIB_BSD_CBFS_PRIVATE_H_ */
diff --git a/src/commonlib/bsd/include/commonlib/bsd/cbfs_serialized.h b/src/commonlib/bsd/include/commonlib/bsd/cbfs_serialized.h
index dd504695b3..dc14cd5f49 100644
--- a/src/commonlib/bsd/include/commonlib/bsd/cbfs_serialized.h
+++ b/src/commonlib/bsd/include/commonlib/bsd/cbfs_serialized.h
@@ -13,6 +13,9 @@ enum cbfs_compression {
};
enum cbfs_type {
+ /* QUERY is an alias for DELETED that can be passed to CBFS APIs to
+ inquire about the type of a file, rather than constrain it. */
+ CBFS_TYPE_QUERY = 0,
CBFS_TYPE_DELETED = 0x00000000,
CBFS_TYPE_NULL = 0xffffffff,
CBFS_TYPE_BOOTBLOCK = 0x01,
diff --git a/src/include/cbfs.h b/src/include/cbfs.h
index 426757d3f5..b89a13ddd3 100644
--- a/src/include/cbfs.h
+++ b/src/include/cbfs.h
@@ -4,6 +4,7 @@
#define _CBFS_H_
#include <cbmem.h>
+#include <commonlib/bsd/cbfs_mdata.h>
#include <commonlib/cbfs.h>
#include <commonlib/mem_pool.h>
#include <program_loading.h>
@@ -15,27 +16,91 @@
* CBFS FILE ACCESS APIs *
**********************************************************************************************/
-/* Map file into memory, returning a pointer to the mapping or NULL on error. If |size_out| is
- not NULL, it will pass out the size of the mapped file.
- NOTE: Since this may return a direct pointer to memory-mapped hardware, compressed files are
- NOT transparently decompressed (unlike cbfs_load()). */
-static inline void *cbfs_map(const char *name, size_t *size_out);
+/*
+ * These are the APIs used to access files in CBFS. In order to keep the calls simple and free
+ * of clutter in the common cases, but still offer all advanced functionality when needed, there
+ * are many different variations that are implemented by wrapping the same underlying API with
+ * static inlines. All accessors have in common that they look up files by name, and will
+ * transparently decompress files that are compressed.
+ *
+ * There are three main flavors of CBFS accessors:
+ *
+ * size_t cbfs_load(char *name, void *buf, size_t size): Loads the contents of a CBFS file into
+ * a buffer provided by the caller (by providing pointer and size to it). Will return the
+ * amount of bytes loaded on success, or 0 on error.
+ *
+ * void *cbfs_map(char *name, size_t *size_out): Maps a file into the address space. If the file
+ * is not compressed and the platform supports direct memory-mapping for the boot medium,
+ * a pointer to the platform mapping is returned directly. In all other cases, memory will
+ * be allocated from the cbfs_cache and file data will be loaded into there. Returns a
+ * pointer to the mapping on success, or NULL on error. If an optional size_out parameter
+ * is passed in, it will be filled out with the size of the mapped data. Caller should call
+ * cbfs_unmap() after it is done using the mapping to free up the cbfs_cache if possible.
+ *
+ * void *cbfs_alloc(char *name, cbfs_allocator_t allocator, void *arg, size_t *size_out): Loads
+ * file data into memory provided by a custom allocator function that the caller passes in.
+ * The caller may pass an argument that is passed through verbatim to the allocator.
+ * Returns the pointer returned by the allocator (where the file data was loaded to) on
+ * success, or NULL on error. If an optional size_out parameter is passed in, it will be
+ * filled out with the size of the loaded data.
+ *
+ * void *cbfs_cbmem_alloc(char *name, uint32_t cbmem_id, size_t *size_out): Wrapper around
+ * cbfs_alloc() that will provide an allocator function for allocating space for the file
+ * data in CBMEM, with the provided CBMEM ID.
+ *
+ * All of these flavors have variations with any of the following optional parameters added:
+ *
+ * ..._ro_...: Will force looking up the CBFS file in the read-only CBFS (the "COREBOOT" FMAP
+ * section), even when running in an RW stage from one of the RW CBFSs. Only relevant if
+ * CONFIG(VBOOT) is set.
+ *
+ * ..._type_...: May pass in an extra enum cbfs_type *type parameter. If the value it points to
+ * is CBFS_TYPE_QUERY, it will be replaced with the actual CBFS type of the found file. If
+ * it is anything else, the type will be compared with the actually found type, and the
+ * operation will fail if they don't match.
+ */
+
+/*
+ * An allocator function for passing to cbfs_alloc(). Takes the argument that was originally
+ * passed to cbfs_alloc(), the size of the file to be loaded, and a pointer to the already
+ * loaded and verified file metadata (for rare cases where the allocator needs to check custom
+ * attributes). Must return a pointer to space of the requested size where the file data should
+ * be loaded, or NULL to make the operation fail.
+ */
+typedef void *(*cbfs_allocator_t)(void *arg, size_t size, union cbfs_mdata *mdata);
+
+static inline size_t cbfs_load(const char *name, void *buf, size_t size);
+static inline size_t cbfs_ro_load(const char *name, void *buf, size_t size);
+static inline size_t cbfs_type_load(const char *name, void *buf, size_t size,
+ enum cbfs_type *type);
+static inline size_t cbfs_ro_type_load(const char *name, void *buf, size_t size,
+ enum cbfs_type *type);
-/* Like cbfs_map(), except that it will always read from the read-only CBFS (the "COREBOOT" FMAP
- region), even when CONFIG(VBOOT) is enabled. */
+static inline void *cbfs_map(const char *name, size_t *size_out);
static inline void *cbfs_ro_map(const char *name, size_t *size_out);
+static inline void *cbfs_type_map(const char *name, size_t *size_out, enum cbfs_type *type);
+static inline void *cbfs_ro_type_map(const char *name, size_t *size_out, enum cbfs_type *type);
+
+static inline void *cbfs_alloc(const char *name, cbfs_allocator_t allocator, void *arg,
+ size_t *size_out);
+static inline void *cbfs_ro_alloc(const char *name, cbfs_allocator_t allocator, void *arg,
+ size_t *size_out);
+static inline void *cbfs_type_alloc(const char *name, cbfs_allocator_t allocator, void *arg,
+ size_t *size_out, enum cbfs_type *type);
+static inline void *cbfs_ro_type_alloc(const char *name, cbfs_allocator_t allocator, void *arg,
+ size_t *size_out, enum cbfs_type *type);
+
+static inline void *cbfs_cbmem_alloc(const char *name, uint32_t cbmem_id, size_t *size_out);
+static inline void *cbfs_ro_cbmem_alloc(const char *name, uint32_t cbmem_id, size_t *size_out);
+static inline void *cbfs_type_cbmem_alloc(const char *name, uint32_t cbmem_id, size_t *size_out,
+ enum cbfs_type *type);
+static inline void *cbfs_ro_type_cbmem_alloc(const char *name, uint32_t cbmem_id,
+ size_t *size_out, enum cbfs_type *type);
/* Removes a previously allocated CBFS mapping. Should try to unmap mappings in strict LIFO
order where possible, since mapping backends often don't support more complicated cases. */
void cbfs_unmap(void *mapping);
-/* Load a file from CBFS into a buffer. Returns amount of loaded bytes on success or 0 on error.
- File will get decompressed as necessary. */
-static inline size_t cbfs_load(const char *name, void *buf, size_t buf_size);
-/* Like cbfs_load(), except that it will always read from the read-only CBFS (the "COREBOOT"
- FMAP region), even when CONFIG(VBOOT) is enabled. */
-static inline size_t cbfs_ro_load(const char *name, void *buf, size_t buf_size);
-
/* Load stage into memory filling in prog. Return 0 on success. < 0 on error. */
int cbfs_prog_stage_load(struct prog *prog);
@@ -111,31 +176,118 @@ size_t cbfs_load_and_decompress(const struct region_device *rdev, size_t offset,
/**********************************************************************************************
* INTERNAL HELPERS FOR INLINES, DO NOT USE. *
**********************************************************************************************/
-size_t _cbfs_load(const char *name, void *buf, size_t buf_size, bool force_ro);
-void *_cbfs_map(const char *name, size_t *size_out, bool force_ro);
+void *_cbfs_alloc(const char *name, cbfs_allocator_t allocator, void *arg,
+ size_t *size_out, bool force_ro, enum cbfs_type *type);
+
+struct _cbfs_default_allocator_arg {
+ void *buf;
+ size_t buf_size;
+};
+void *_cbfs_default_allocator(void *arg, size_t size, union cbfs_mdata *unused);
+void *_cbfs_cbmem_allocator(void *arg, size_t size, union cbfs_mdata *unused);
/**********************************************************************************************
* INLINE IMPLEMENTATIONS *
**********************************************************************************************/
+static inline void *cbfs_alloc(const char *name, cbfs_allocator_t allocator, void *arg,
+ size_t *size_out)
+{
+ return cbfs_type_alloc(name, allocator, arg, size_out, NULL);
+}
+
+static inline void *cbfs_ro_alloc(const char *name, cbfs_allocator_t allocator, void *arg,
+ size_t *size_out)
+{
+ return cbfs_ro_type_alloc(name, allocator, arg, size_out, NULL);
+}
+
+static inline void *cbfs_type_alloc(const char *name, cbfs_allocator_t allocator, void *arg,
+ size_t *size_out, enum cbfs_type *type)
+{
+ return _cbfs_alloc(name, allocator, arg, size_out, false, type);
+}
+
+static inline void *cbfs_ro_type_alloc(const char *name, cbfs_allocator_t allocator, void *arg,
+ size_t *size_out, enum cbfs_type *type)
+{
+ return _cbfs_alloc(name, allocator, arg, size_out, true, type);
+}
+
static inline void *cbfs_map(const char *name, size_t *size_out)
{
- return _cbfs_map(name, size_out, false);
+ return cbfs_type_map(name, size_out, NULL);
}
static inline void *cbfs_ro_map(const char *name, size_t *size_out)
{
- return _cbfs_map(name, size_out, true);
+ return cbfs_ro_type_map(name, size_out, NULL);
+}
+
+static inline void *cbfs_type_map(const char *name, size_t *size_out, enum cbfs_type *type)
+{
+ return cbfs_type_alloc(name, NULL, NULL, size_out, type);
+}
+
+static inline void *cbfs_ro_type_map(const char *name, size_t *size_out, enum cbfs_type *type)
+{
+ return cbfs_ro_type_alloc(name, NULL, NULL, size_out, type);
+}
+
+static inline size_t _cbfs_load(const char *name, void *buf, size_t size, bool force_ro,
+ enum cbfs_type *type)
+{
+ struct _cbfs_default_allocator_arg arg = { .buf = buf, .buf_size = size };
+ if (_cbfs_alloc(name, _cbfs_default_allocator, &arg, &size, force_ro, type))
+ return size;
+ else
+ return 0;
+}
+
+static inline size_t cbfs_load(const char *name, void *buf, size_t size)
+{
+ return cbfs_type_load(name, buf, size, NULL);
+}
+
+static inline size_t cbfs_type_load(const char *name, void *buf, size_t size,
+ enum cbfs_type *type)
+{
+ return _cbfs_load(name, buf, size, false, type);
+}
+
+static inline size_t cbfs_ro_load(const char *name, void *buf, size_t size)
+{
+ return cbfs_ro_type_load(name, buf, size, NULL);
+}
+
+static inline size_t cbfs_ro_type_load(const char *name, void *buf, size_t size,
+ enum cbfs_type *type)
+{
+ return _cbfs_load(name, buf, size, true, type);
+}
+
+static inline void *cbfs_cbmem_alloc(const char *name, uint32_t cbmem_id, size_t *size_out)
+{
+ return cbfs_type_cbmem_alloc(name, cbmem_id, size_out, NULL);
+}
+
+static inline void *cbfs_ro_cbmem_alloc(const char *name, uint32_t cbmem_id, size_t *size_out)
+{
+ return cbfs_ro_type_cbmem_alloc(name, cbmem_id, size_out, NULL);
}
-static inline size_t cbfs_load(const char *name, void *buf, size_t buf_size)
+static inline void *cbfs_type_cbmem_alloc(const char *name, uint32_t cbmem_id, size_t *size_out,
+ enum cbfs_type *type)
{
- return _cbfs_load(name, buf, buf_size, false);
+ return cbfs_type_alloc(name, _cbfs_cbmem_allocator, (void *)(uintptr_t)cbmem_id,
+ size_out, type);
}
-static inline size_t cbfs_ro_load(const char *name, void *buf, size_t buf_size)
+static inline void *cbfs_ro_type_cbmem_alloc(const char *name, uint32_t cbmem_id,
+ size_t *size_out, enum cbfs_type *type)
{
- return _cbfs_load(name, buf, buf_size, true);
+ return cbfs_ro_type_alloc(name, _cbfs_cbmem_allocator, (void *)(uintptr_t)cbmem_id,
+ size_out, type);
}
#endif
diff --git a/src/lib/cbfs.c b/src/lib/cbfs.c
index 93dbb681aa..7df9dc671e 100644
--- a/src/lib/cbfs.c
+++ b/src/lib/cbfs.c
@@ -102,48 +102,6 @@ int cbfs_boot_locate(struct cbfsf *fh, const char *name, uint32_t *type)
return 0;
}
-void *_cbfs_map(const char *name, size_t *size_out, bool force_ro)
-{
- struct region_device rdev;
- union cbfs_mdata mdata;
-
- if (cbfs_boot_lookup(name, force_ro, &mdata, &rdev))
- return NULL;
-
- if (size_out != NULL)
- *size_out = region_device_sz(&rdev);
-
- uint32_t compression = CBFS_COMPRESS_NONE;
- const struct cbfs_file_attr_compression *attr = cbfs_find_attr(&mdata,
- CBFS_FILE_ATTR_TAG_COMPRESSION, sizeof(*attr));
- if (attr)
- compression = be32toh(attr->compression);
-
- if (compression == CBFS_COMPRESS_NONE)
- return rdev_mmap_full(&rdev);
-
- if (!CBFS_CACHE_AVAILABLE) {
- ERROR("Cannot map compressed file %s on x86\n", mdata.h.filename);
- return NULL;
- }
-
- size_t size = be32toh(attr->decompressed_size);
- void *buf = mem_pool_alloc(&cbfs_cache, size);
- if (!buf) {
- ERROR("CBFS cache out of memory when mapping %s\n", mdata.h.filename);
- return NULL;
- }
-
- size = cbfs_load_and_decompress(&rdev, 0, region_device_sz(&rdev), buf, size,
- compression);
- if (!size)
- return NULL;
-
- if (size_out != NULL)
- *size_out = size;
- return buf;
-}
-
void cbfs_unmap(void *mapping)
{
/*
@@ -234,6 +192,8 @@ size_t cbfs_load_and_decompress(const struct region_device *rdev, size_t offset,
size_t out_size;
void *map;
+ DEBUG("Decompressing %zu bytes to %p with algo %d\n", buffer_size, buffer, compression);
+
switch (compression) {
case CBFS_COMPRESS_NONE:
if (buffer_size < in_size)
@@ -348,25 +308,78 @@ void *cbfs_boot_map_optionrom_revision(uint16_t vendor, uint16_t device, uint8_t
return cbfs_map(name, NULL);
}
-size_t _cbfs_load(const char *name, void *buf, size_t buf_size, bool force_ro)
+void *_cbfs_alloc(const char *name, cbfs_allocator_t allocator, void *arg,
+ size_t *size_out, bool force_ro, enum cbfs_type *type)
{
struct region_device rdev;
union cbfs_mdata mdata;
+ void *loc;
+
+ DEBUG("%s(name='%s', alloc=%p(%p), force_ro=%s, type=%d)\n", __func__, name, allocator,
+ arg, force_ro ? "true" : "false", type ? *type : -1);
if (cbfs_boot_lookup(name, force_ro, &mdata, &rdev))
- return 0;
+ return NULL;
+ if (type) {
+ const enum cbfs_type real_type = be32toh(mdata.h.type);
+ if (*type == CBFS_TYPE_QUERY)
+ *type = real_type;
+ else if (*type != real_type) {
+ ERROR("'%s' type mismatch (is %u, expected %u)\n",
+ mdata.h.filename, real_type, *type);
+ return NULL;
+ }
+ }
+
+ size_t size = region_device_sz(&rdev);
uint32_t compression = CBFS_COMPRESS_NONE;
- const struct cbfs_file_attr_compression *attr = cbfs_find_attr(&mdata,
- CBFS_FILE_ATTR_TAG_COMPRESSION, sizeof(*attr));
- if (attr) {
- compression = be32toh(attr->compression);
- if (buf_size < be32toh(attr->decompressed_size))
- return 0;
+ const struct cbfs_file_attr_compression *cattr = cbfs_find_attr(&mdata,
+ CBFS_FILE_ATTR_TAG_COMPRESSION, sizeof(*cattr));
+ if (cattr) {
+ compression = be32toh(cattr->compression);
+ size = be32toh(cattr->decompressed_size);
+ }
+
+ if (size_out)
+ *size_out = size;
+
+ /* allocator == NULL means do a cbfs_map() */
+ if (allocator) {
+ loc = allocator(arg, size, &mdata);
+ } else if (compression == CBFS_COMPRESS_NONE) {
+ return rdev_mmap_full(&rdev);
+ } else if (!CBFS_CACHE_AVAILABLE) {
+ ERROR("Cannot map compressed file %s on x86\n", mdata.h.filename);
+ return NULL;
+ } else {
+ loc = mem_pool_alloc(&cbfs_cache, size);
}
- return cbfs_load_and_decompress(&rdev, 0, region_device_sz(&rdev),
- buf, buf_size, compression);
+ if (!loc) {
+ ERROR("'%s' allocation failure\n", mdata.h.filename);
+ return NULL;
+ }
+
+ size = cbfs_load_and_decompress(&rdev, 0, region_device_sz(&rdev),
+ loc, size, compression);
+ if (!size)
+ return NULL;
+
+ return loc;
+}
+
+void *_cbfs_default_allocator(void *arg, size_t size, union cbfs_mdata *unused)
+{
+ struct _cbfs_default_allocator_arg *darg = arg;
+ if (size > darg->buf_size)
+ return NULL;
+ return darg->buf;
+}
+
+void *_cbfs_cbmem_allocator(void *arg, size_t size, union cbfs_mdata *unused)
+{
+ return cbmem_add((uintptr_t)arg, size);
}
int cbfs_prog_stage_load(struct prog *pstage)