Julius Werner | 1e37c9c | 2019-12-11 17:09:39 -0800 | [diff] [blame] | 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> |
Thomas Heijligen | 3f9613b | 2022-11-28 11:46:48 +0100 | [diff] [blame] | 5 | #include <commonlib/bsd/helpers.h> |
Julius Werner | 1e37c9c | 2019-12-11 17:09:39 -0800 | [diff] [blame] | 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 | |
Julius Werner | 69cc557 | 2022-03-04 17:49:56 -0800 | [diff] [blame] | 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) |
Julius Werner | 1e37c9c | 2019-12-11 17:09:39 -0800 | [diff] [blame] | 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 | |
Julius Werner | 69cc557 | 2022-03-04 17:49:56 -0800 | [diff] [blame] | 66 | enum cb_err cbfs_mcache_build(cbfs_dev_t dev, void *mcache, size_t size, |
| 67 | struct vb2_hash *metadata_hash) |
Julius Werner | 1e37c9c | 2019-12-11 17:09:39 -0800 | [diff] [blame] | 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)); |
Julius Werner | 69cc557 | 2022-03-04 17:49:56 -0800 | [diff] [blame] | 77 | enum cb_err ret = cbfs_walk(dev, build_walker, &args, metadata_hash, 0); |
Julius Werner | 1e37c9c | 2019-12-11 17:09:39 -0800 | [diff] [blame] | 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 | |
Julius Werner | 69cc557 | 2022-03-04 17:49:56 -0800 | [diff] [blame] | 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) |
Julius Werner | 1e37c9c | 2019-12-11 17:09:39 -0800 | [diff] [blame] | 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 | |
Julius Werner | 20f5dce | 2020-12-03 12:25:11 -0800 | [diff] [blame] | 99 | while (current + sizeof(uint32_t) <= end) { |
Julius Werner | 1e37c9c | 2019-12-11 17:09:39 -0800 | [diff] [blame] | 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); |
Julius Werner | d477565 | 2020-03-13 16:43:34 -0700 | [diff] [blame] | 110 | if (namesize <= data_offset - offsetof(union cbfs_mdata, h.filename) && |
| 111 | memcmp(name, entry->file.h.filename, namesize) == 0) { |
Julius Werner | 1e37c9c | 2019-12-11 17:09:39 -0800 | [diff] [blame] | 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 | |
Julius Werner | b571846 | 2021-04-08 17:08:28 -0700 | [diff] [blame] | 122 | ERROR("CBFS mcache is not terminated!\n"); /* should never happen */ |
Julius Werner | 1e37c9c | 2019-12-11 17:09:39 -0800 | [diff] [blame] | 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 | |
Julius Werner | b571846 | 2021-04-08 17:08:28 -0700 | [diff] [blame] | 131 | while (current + sizeof(uint32_t) <= end) { |
Julius Werner | 1e37c9c | 2019-12-11 17:09:39 -0800 | [diff] [blame] | 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 | } |