cbfs_mcache.c
1 /* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later */ 2 3 #include <assert.h> 4 #include <commonlib/bsd/cbfs_private.h> 5 #include <commonlib/bsd/helpers.h> 6 7 /* 8 * A CBFS metadata cache is an in memory data structure storing CBFS file headers (= metadata). 9 * It is defined by its start pointer and size. It contains a sequence of variable-length 10 * union mcache_entry entries. There is no overall header structure for the cache. 11 * 12 * Each mcache_entry is the raw metadata for a CBFS file (including attributes) in the same form 13 * as stored on flash (i.e. values in big-endian), except that the CBFS magic signature in the 14 * first 8 bytes ('LARCHIVE') is overwritten with mcache-internal bookkeeping data. The first 4 15 * bytes are a magic number (MCACHE_MAGIC_FILE) and the next 4 bytes are the absolute offset in 16 * bytes on the cbfs_dev_t that this metadata blob was found at. (Note that depending on the 17 * implementation of cbfs_dev_t, this offset may still be relative to the start of a subregion 18 * of the underlying storage device.) 19 * 20 * The length of an mcache_entry (i.e. length of the underlying metadata blob) is encoded in the 21 * metadata (entry->file.h.offset). The next mcache_entry begins at the next 22 * CBFS_MCACHE_ALIGNMENT boundary after that. The cache is terminated by a special 4-byte 23 * mcache_entry that consists only of a magic number (MCACHE_MAGIC_END or MCACHE_MAGIC_FULL). 24 */ 25 26 #define MCACHE_MAGIC_FILE 0x454c4946 /* 'FILE' */ 27 #define MCACHE_MAGIC_FULL 0x4c4c5546 /* 'FULL' */ 28 #define MCACHE_MAGIC_END 0x444e4524 /* '$END' */ 29 30 union mcache_entry { 31 union cbfs_mdata file; 32 struct { /* These fields exactly overlap file.h.magic */ 33 uint32_t magic; 34 uint32_t offset; 35 }; 36 }; 37 38 struct cbfs_mcache_build_args { 39 void *mcache; 40 void *end; 41 int count; 42 }; 43 44 static enum cb_err build_walker(cbfs_dev_t dev, size_t offset, const union cbfs_mdata *mdata, 45 size_t already_read, void *arg) 46 { 47 struct cbfs_mcache_build_args *args = arg; 48 union mcache_entry *entry = args->mcache; 49 const uint32_t data_offset = be32toh(mdata->h.offset); 50 51 if (args->end - args->mcache < data_offset) 52 return CB_CBFS_CACHE_FULL; 53 54 if (cbfs_copy_fill_metadata(args->mcache, mdata, already_read, dev, offset)) 55 return CB_CBFS_IO; 56 57 entry->magic = MCACHE_MAGIC_FILE; 58 entry->offset = offset; 59 60 args->mcache += ALIGN_UP(data_offset, CBFS_MCACHE_ALIGNMENT); 61 args->count++; 62 63 return CB_CBFS_NOT_FOUND; 64 } 65 66 enum cb_err cbfs_mcache_build(cbfs_dev_t dev, void *mcache, size_t size, 67 struct vb2_hash *metadata_hash) 68 { 69 struct cbfs_mcache_build_args args = { 70 .mcache = mcache, 71 .end = mcache + ALIGN_DOWN(size, CBFS_MCACHE_ALIGNMENT) 72 - sizeof(uint32_t), /* leave space for terminating magic */ 73 .count = 0, 74 }; 75 76 assert(size > sizeof(uint32_t) && IS_ALIGNED((uintptr_t)mcache, CBFS_MCACHE_ALIGNMENT)); 77 enum cb_err ret = cbfs_walk(dev, build_walker, &args, metadata_hash, 0); 78 union mcache_entry *entry = args.mcache; 79 if (ret == CB_CBFS_NOT_FOUND) { 80 ret = CB_SUCCESS; 81 entry->magic = MCACHE_MAGIC_END; 82 } else if (ret == CB_CBFS_CACHE_FULL) { 83 ERROR("mcache overflow, should increase CBFS_MCACHE size!\n"); 84 entry->magic = MCACHE_MAGIC_FULL; 85 } 86 87 LOG("mcache @%p built for %d files, used %#zx of %#zx bytes\n", mcache, 88 args.count, args.mcache + sizeof(entry->magic) - mcache, size); 89 return ret; 90 } 91 92 enum cb_err cbfs_mcache_lookup(const void *mcache, size_t mcache_size, const char *name, 93 union cbfs_mdata *mdata_out, size_t *data_offset_out) 94 { 95 const size_t namesize = strlen(name) + 1; /* Count trailing \0 so we can memcmp() it. */ 96 const void *end = mcache + mcache_size; 97 const void *current = mcache; 98 99 while (current + sizeof(uint32_t) <= end) { 100 const union mcache_entry *entry = current; 101 102 if (entry->magic == MCACHE_MAGIC_END) 103 return CB_CBFS_NOT_FOUND; 104 if (entry->magic == MCACHE_MAGIC_FULL) 105 return CB_CBFS_CACHE_FULL; 106 107 assert(entry->magic == MCACHE_MAGIC_FILE); 108 const uint32_t data_offset = be32toh(entry->file.h.offset); 109 const uint32_t data_length = be32toh(entry->file.h.len); 110 if (namesize <= data_offset - offsetof(union cbfs_mdata, h.filename) && 111 memcmp(name, entry->file.h.filename, namesize) == 0) { 112 LOG("Found '%s' @%#x size %#x in mcache @%p\n", 113 name, entry->offset, data_length, current); 114 *data_offset_out = entry->offset + data_offset; 115 memcpy(mdata_out, &entry->file, data_offset); 116 return CB_SUCCESS; 117 } 118 119 current += ALIGN_UP(data_offset, CBFS_MCACHE_ALIGNMENT); 120 } 121 122 ERROR("CBFS mcache is not terminated!\n"); /* should never happen */ 123 return CB_ERR; 124 } 125 126 size_t cbfs_mcache_real_size(const void *mcache, size_t mcache_size) 127 { 128 const void *end = mcache + mcache_size; 129 const void *current = mcache; 130 131 while (current + sizeof(uint32_t) <= end) { 132 const union mcache_entry *entry = current; 133 134 if (entry->magic == MCACHE_MAGIC_FULL || entry->magic == MCACHE_MAGIC_END) { 135 current += sizeof(entry->magic); 136 break; 137 } 138 139 assert(entry->magic == MCACHE_MAGIC_FILE); 140 current += ALIGN_UP(be32toh(entry->file.h.offset), CBFS_MCACHE_ALIGNMENT); 141 } 142 143 return current - mcache; 144 }