diff options
Diffstat (limited to 'src/commonlib')
-rw-r--r-- | src/commonlib/Makefile.inc | 7 | ||||
-rw-r--r-- | src/commonlib/bsd/cbfs_mcache.c | 143 | ||||
-rw-r--r-- | src/commonlib/bsd/include/commonlib/bsd/cb_err.h | 1 | ||||
-rw-r--r-- | src/commonlib/bsd/include/commonlib/bsd/cbfs_private.h | 21 | ||||
-rw-r--r-- | src/commonlib/include/commonlib/cbmem_id.h | 6 |
5 files changed, 177 insertions, 1 deletions
diff --git a/src/commonlib/Makefile.inc b/src/commonlib/Makefile.inc index b2225cb114..1a38e4a89f 100644 --- a/src/commonlib/Makefile.inc +++ b/src/commonlib/Makefile.inc @@ -37,6 +37,13 @@ postcar-y += bsd/cbfs_private.c ramstage-y += bsd/cbfs_private.c smm-y += bsd/cbfs_private.c +bootblock-y += bsd/cbfs_mcache.c +verstage-y += bsd/cbfs_mcache.c +romstage-y += bsd/cbfs_mcache.c +postcar-y += bsd/cbfs_mcache.c +ramstage-y += bsd/cbfs_mcache.c +smm-y += bsd/cbfs_mcache.c + decompressor-y += bsd/lz4_wrapper.c bootblock-y += bsd/lz4_wrapper.c verstage-y += bsd/lz4_wrapper.c diff --git a/src/commonlib/bsd/cbfs_mcache.c b/src/commonlib/bsd/cbfs_mcache.c new file mode 100644 index 0000000000..f54b6631af --- /dev/null +++ b/src/commonlib/bsd/cbfs_mcache.c @@ -0,0 +1,143 @@ +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later */ + +#include <assert.h> +#include <commonlib/bsd/cbfs_private.h> + +/* + * A CBFS metadata cache is an in memory data structure storing CBFS file headers (= metadata). + * It is defined by its start pointer and size. It contains a sequence of variable-length + * union mcache_entry entries. There is no overall header structure for the cache. + * + * Each mcache_entry is the raw metadata for a CBFS file (including attributes) in the same form + * as stored on flash (i.e. values in big-endian), except that the CBFS magic signature in the + * first 8 bytes ('LARCHIVE') is overwritten with mcache-internal bookkeeping data. The first 4 + * bytes are a magic number (MCACHE_MAGIC_FILE) and the next 4 bytes are the absolute offset in + * bytes on the cbfs_dev_t that this metadata blob was found at. (Note that depending on the + * implementation of cbfs_dev_t, this offset may still be relative to the start of a subregion + * of the underlying storage device.) + * + * The length of an mcache_entry (i.e. length of the underlying metadata blob) is encoded in the + * metadata (entry->file.h.offset). The next mcache_entry begins at the next + * CBFS_MCACHE_ALIGNMENT boundary after that. The cache is terminated by a special 4-byte + * mcache_entry that consists only of a magic number (MCACHE_MAGIC_END or MCACHE_MAGIC_FULL). + */ + +#define MCACHE_MAGIC_FILE 0x454c4946 /* 'FILE' */ +#define MCACHE_MAGIC_FULL 0x4c4c5546 /* 'FULL' */ +#define MCACHE_MAGIC_END 0x444e4524 /* '$END' */ + +union mcache_entry { + union cbfs_mdata file; + struct { /* These fields exactly overlap file.h.magic */ + uint32_t magic; + uint32_t offset; + }; +}; + +struct cbfs_mcache_build_args { + void *mcache; + void *end; + int count; +}; + +static cb_err_t build_walker(cbfs_dev_t dev, size_t offset, const union cbfs_mdata *mdata, + size_t already_read, void *arg) +{ + struct cbfs_mcache_build_args *args = arg; + union mcache_entry *entry = args->mcache; + const uint32_t data_offset = be32toh(mdata->h.offset); + + if (args->end - args->mcache < data_offset) + return CB_CBFS_CACHE_FULL; + + if (cbfs_copy_fill_metadata(args->mcache, mdata, already_read, dev, offset)) + return CB_CBFS_IO; + + entry->magic = MCACHE_MAGIC_FILE; + entry->offset = offset; + + args->mcache += ALIGN_UP(data_offset, CBFS_MCACHE_ALIGNMENT); + args->count++; + + return CB_CBFS_NOT_FOUND; +} + +cb_err_t cbfs_mcache_build(cbfs_dev_t dev, void *mcache, size_t size, + struct vb2_hash *metadata_hash) +{ + struct cbfs_mcache_build_args args = { + .mcache = mcache, + .end = mcache + ALIGN_DOWN(size, CBFS_MCACHE_ALIGNMENT) + - sizeof(uint32_t), /* leave space for terminating magic */ + .count = 0, + }; + + assert(size > sizeof(uint32_t) && IS_ALIGNED((uintptr_t)mcache, CBFS_MCACHE_ALIGNMENT)); + cb_err_t ret = cbfs_walk(dev, build_walker, &args, metadata_hash, 0); + union mcache_entry *entry = args.mcache; + if (ret == CB_CBFS_NOT_FOUND) { + ret = CB_SUCCESS; + entry->magic = MCACHE_MAGIC_END; + } else if (ret == CB_CBFS_CACHE_FULL) { + ERROR("mcache overflow, should increase CBFS_MCACHE size!\n"); + entry->magic = MCACHE_MAGIC_FULL; + } + + LOG("mcache @%p built for %d files, used %#zx of %#zx bytes\n", mcache, + args.count, args.mcache + sizeof(entry->magic) - mcache, size); + return ret; +} + +cb_err_t cbfs_mcache_lookup(const void *mcache, size_t mcache_size, const char *name, + union cbfs_mdata *mdata_out, size_t *data_offset_out) +{ + const size_t namesize = strlen(name) + 1; /* Count trailing \0 so we can memcmp() it. */ + const void *end = mcache + mcache_size; + const void *current = mcache; + + while (current + sizeof(uint32_t) < end) { + const union mcache_entry *entry = current; + + if (entry->magic == MCACHE_MAGIC_END) + return CB_CBFS_NOT_FOUND; + if (entry->magic == MCACHE_MAGIC_FULL) + return CB_CBFS_CACHE_FULL; + + assert(entry->magic == MCACHE_MAGIC_FILE); + const uint32_t data_offset = be32toh(entry->file.h.offset); + const uint32_t data_length = be32toh(entry->file.h.len); + if (namesize <= data_offset - offsetof(union cbfs_mdata, filename) && + memcmp(name, entry->file.filename, namesize) == 0) { + LOG("Found '%s' @%#x size %#x in mcache @%p\n", + name, entry->offset, data_length, current); + *data_offset_out = entry->offset + data_offset; + memcpy(mdata_out, &entry->file, data_offset); + return CB_SUCCESS; + } + + current += ALIGN_UP(data_offset, CBFS_MCACHE_ALIGNMENT); + } + + ERROR("CBFS mcache overflow!\n"); + return CB_ERR; +} + +size_t cbfs_mcache_real_size(const void *mcache, size_t mcache_size) +{ + const void *end = mcache + mcache_size; + const void *current = mcache; + + while (current + sizeof(uint32_t) < end) { + const union mcache_entry *entry = current; + + if (entry->magic == MCACHE_MAGIC_FULL || entry->magic == MCACHE_MAGIC_END) { + current += sizeof(entry->magic); + break; + } + + assert(entry->magic == MCACHE_MAGIC_FILE); + current += ALIGN_UP(be32toh(entry->file.h.offset), CBFS_MCACHE_ALIGNMENT); + } + + return current - mcache; +} diff --git a/src/commonlib/bsd/include/commonlib/bsd/cb_err.h b/src/commonlib/bsd/include/commonlib/bsd/cb_err.h index e5aa852617..b61b956524 100644 --- a/src/commonlib/bsd/include/commonlib/bsd/cb_err.h +++ b/src/commonlib/bsd/include/commonlib/bsd/cb_err.h @@ -39,6 +39,7 @@ enum cb_err { CB_CBFS_IO = -400, /**< Underlying I/O error */ CB_CBFS_NOT_FOUND = -401, /**< File not found in directory */ CB_CBFS_HASH_MISMATCH = -402, /**< Master hash validation failed */ + CB_CBFS_CACHE_FULL = -403, /**< Metadata cache overflowed */ }; /* Don't typedef the enum directly, so the size is unambiguous for serialization. */ diff --git a/src/commonlib/bsd/include/commonlib/bsd/cbfs_private.h b/src/commonlib/bsd/include/commonlib/bsd/cbfs_private.h index aaee62f4c3..64dcf9f5ba 100644 --- a/src/commonlib/bsd/include/commonlib/bsd/cbfs_private.h +++ b/src/commonlib/bsd/include/commonlib/bsd/cbfs_private.h @@ -113,4 +113,25 @@ cb_err_t cbfs_copy_fill_metadata(union cbfs_mdata *dst, const union cbfs_mdata * cb_err_t cbfs_lookup(cbfs_dev_t dev, const char *name, union cbfs_mdata *mdata_out, size_t *data_offset_out, struct vb2_hash *metadata_hash); +/* Both base address and size of CBFS mcaches must be aligned to this value! */ +#define CBFS_MCACHE_ALIGNMENT sizeof(uint32_t) /* Largest data type used in CBFS */ + +/* Build an in-memory CBFS metadata cache out of the CBFS on |dev| into a |mcache_size| bytes + * memory area at |mcache|. Also verify |metadata_hash| unless it is NULL. If this returns + * CB_CBFS_CACHE_FULL, the mcache is still valid and can be used, but lookups may return + * CB_CBFS_CACHE_FULL for files that didn't fit to indicate that the caller needs to fall back + * to cbfs_lookup(). */ +cb_err_t cbfs_mcache_build(cbfs_dev_t dev, void *mcache, size_t mcache_size, + struct vb2_hash *metadata_hash); + +/* + * Find a file named |name| in a CBFS metadata cache and copy its metadata into |mdata_out|. + * Pass out offset to the file data (on the original CBFS device used for cbfs_mcache_build()). + */ +cb_err_t cbfs_mcache_lookup(const void *mcache, size_t mcache_size, const char *name, + union cbfs_mdata *mdata_out, size_t *data_offset_out); + +/* 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); + #endif /* _COMMONLIB_BSD_CBFS_PRIVATE_H_ */ diff --git a/src/commonlib/include/commonlib/cbmem_id.h b/src/commonlib/include/commonlib/cbmem_id.h index 6b4d60469e..ab7cf63843 100644 --- a/src/commonlib/include/commonlib/cbmem_id.h +++ b/src/commonlib/include/commonlib/cbmem_id.h @@ -67,6 +67,8 @@ #define CBMEM_ID_ROM2 0x524f4d32 #define CBMEM_ID_ROM3 0x524f4d33 #define CBMEM_ID_FMAP 0x464d4150 +#define CBMEM_ID_CBFS_RO_MCACHE 0x524d5346 +#define CBMEM_ID_CBFS_RW_MCACHE 0x574d5346 #define CBMEM_ID_FSP_LOGO 0x4c4f474f #define CBMEM_ID_SMM_COMBUFFER 0x53534d32 @@ -129,5 +131,7 @@ { CBMEM_ID_ROM1, "VGA ROM #1 "}, \ { CBMEM_ID_ROM2, "VGA ROM #2 "}, \ { CBMEM_ID_ROM3, "VGA ROM #3 "}, \ - { CBMEM_ID_FMAP, "FMAP "}, + { CBMEM_ID_FMAP, "FMAP "}, \ + { CBMEM_ID_CBFS_RO_MCACHE, "RO MCACHE "}, \ + { CBMEM_ID_CBFS_RW_MCACHE, "RW MCACHE "} #endif /* _CBMEM_ID_H_ */ |