xref: /aosp_15_r20/external/coreboot/src/commonlib/bsd/cbfs_mcache.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
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 
build_walker(cbfs_dev_t dev,size_t offset,const union cbfs_mdata * mdata,size_t already_read,void * arg)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 
cbfs_mcache_build(cbfs_dev_t dev,void * mcache,size_t size,struct vb2_hash * metadata_hash)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 
cbfs_mcache_lookup(const void * mcache,size_t mcache_size,const char * name,union cbfs_mdata * mdata_out,size_t * data_offset_out)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 
cbfs_mcache_real_size(const void * mcache,size_t mcache_size)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 }
145