1 /* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later */
4 #include <commonlib/bsd/cbfs_private.h>
5 #include <commonlib/bsd/helpers.h>
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.
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.)
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).
26 #define MCACHE_MAGIC_FILE 0x454c4946 /* 'FILE' */
27 #define MCACHE_MAGIC_FULL 0x4c4c5546 /* 'FULL' */
28 #define MCACHE_MAGIC_END 0x444e4524 /* '$END' */
31 union cbfs_mdata file
;
32 struct { /* These fields exactly overlap file.h.magic */
38 struct cbfs_mcache_build_args
{
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
)
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
);
51 if (args
->end
- args
->mcache
< data_offset
)
52 return CB_CBFS_CACHE_FULL
;
54 if (cbfs_copy_fill_metadata(args
->mcache
, mdata
, already_read
, dev
, offset
))
57 entry
->magic
= MCACHE_MAGIC_FILE
;
58 entry
->offset
= offset
;
60 args
->mcache
+= ALIGN_UP(data_offset
, CBFS_MCACHE_ALIGNMENT
);
63 return CB_CBFS_NOT_FOUND
;
66 enum cb_err
cbfs_mcache_build(cbfs_dev_t dev
, void *mcache
, size_t size
,
67 struct vb2_hash
*metadata_hash
)
69 struct cbfs_mcache_build_args args
= {
71 .end
= mcache
+ ALIGN_DOWN(size
, CBFS_MCACHE_ALIGNMENT
)
72 - sizeof(uint32_t), /* leave space for terminating magic */
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
) {
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
;
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
);
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
)
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
;
99 while (current
+ sizeof(uint32_t) <= end
) {
100 const union mcache_entry
*entry
= current
;
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
;
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
);
119 current
+= ALIGN_UP(data_offset
, CBFS_MCACHE_ALIGNMENT
);
122 ERROR("CBFS mcache is not terminated!\n"); /* should never happen */
126 size_t cbfs_mcache_real_size(const void *mcache
, size_t mcache_size
)
128 const void *end
= mcache
+ mcache_size
;
129 const void *current
= mcache
;
131 while (current
+ sizeof(uint32_t) <= end
) {
132 const union mcache_entry
*entry
= current
;
134 if (entry
->magic
== MCACHE_MAGIC_FULL
|| entry
->magic
== MCACHE_MAGIC_END
) {
135 current
+= sizeof(entry
->magic
);
139 assert(entry
->magic
== MCACHE_MAGIC_FILE
);
140 current
+= ALIGN_UP(be32toh(entry
->file
.h
.offset
), CBFS_MCACHE_ALIGNMENT
);
143 return current
- mcache
;