blob: 999c35e52a0a97cb24295965dce1e5b4aaa099a0 [file] [log] [blame]
Angel Ponsebda03e2020-04-02 23:48:05 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Aaron Durbin295d58b2015-12-15 13:33:51 -06002
3#include <console/console.h>
4#include <commonlib/cbfs.h>
5#include <commonlib/endian.h>
6#include <commonlib/helpers.h>
7#include <string.h>
Joel Kitching2eb89c82019-04-25 17:45:12 +08008#include <vb2_sha.h>
Aaron Durbin295d58b2015-12-15 13:33:51 -06009
Aaron Durbin295d58b2015-12-15 13:33:51 -060010static size_t cbfs_next_offset(const struct region_device *cbfs,
11 const struct cbfsf *f)
12{
13 size_t offset;
14
15 if (f == NULL)
16 return 0;
17
Elyes HAOUAS23e3f9d2020-02-03 21:07:19 +010018 /* The region_device objects store absolute offsets over the whole
Aaron Durbin295d58b2015-12-15 13:33:51 -060019 * region. Therefore a relative offset needs to be calculated. */
20 offset = rdev_relative_offset(cbfs, &f->data);
21 offset += region_device_sz(&f->data);
22
23 return ALIGN_UP(offset, CBFS_ALIGNMENT);
24}
25
26static int cbfs_end(const struct region_device *cbfs, size_t offset)
27{
28 if (offset >= region_device_sz(cbfs))
29 return 1;
30
31 return 0;
32}
33
34int cbfs_for_each_file(const struct region_device *cbfs,
35 const struct cbfsf *prev, struct cbfsf *fh)
36{
37 size_t offset;
38
39 offset = cbfs_next_offset(cbfs, prev);
40
41 /* Try to scan the entire cbfs region looking for file name. */
42 while (1) {
43 struct cbfs_file file;
44 const size_t fsz = sizeof(file);
45
46 DEBUG("Checking offset %zx\n", offset);
47
48 /* End of region. */
49 if (cbfs_end(cbfs, offset))
50 return 1;
51
52 /* Can't read file. Nothing else to do but bail out. */
53 if (rdev_readat(cbfs, &file, offset, fsz) != fsz)
54 break;
55
56 if (memcmp(file.magic, CBFS_FILE_MAGIC, sizeof(file.magic))) {
57 offset++;
58 offset = ALIGN_UP(offset, CBFS_ALIGNMENT);
59 continue;
60 }
61
62 file.len = read_be32(&file.len);
63 file.offset = read_be32(&file.offset);
64
65 DEBUG("File @ offset %zx size %x\n", offset, file.len);
66
67 /* Keep track of both the metadata and the data for the file. */
68 if (rdev_chain(&fh->metadata, cbfs, offset, file.offset))
69 break;
70
71 if (rdev_chain(&fh->data, cbfs, offset + file.offset, file.len))
72 break;
73
74 /* Success. */
75 return 0;
76 }
77
78 return -1;
79}
80
Julius Werner71885a42016-08-19 15:24:54 -070081size_t cbfs_for_each_attr(void *metadata, size_t metadata_size,
82 size_t last_offset)
83{
84 struct cbfs_file_attribute *attr;
85
86 if (!last_offset) {
87 struct cbfs_file *file = metadata;
88 size_t start_offset = read_be32(&file->attributes_offset);
89 if (start_offset <= sizeof(struct cbfs_file) ||
90 start_offset + sizeof(*attr) > metadata_size)
91 return 0;
92 return start_offset;
93 }
94
95 attr = metadata + last_offset;
96 size_t next_offset = last_offset + read_be32(&attr->len);
97
98 if (next_offset + sizeof(*attr) > metadata_size)
99 return 0;
100 return next_offset;
101}
102
103int cbfsf_decompression_info(struct cbfsf *fh, uint32_t *algo, size_t *size)
104{
105 size_t metadata_size = region_device_sz(&fh->metadata);
106 void *metadata = rdev_mmap_full(&fh->metadata);
107 size_t offs = 0;
108
109 if (!metadata)
110 return -1;
111
112 while ((offs = cbfs_for_each_attr(metadata, metadata_size, offs))) {
113 struct cbfs_file_attr_compression *attr = metadata + offs;
114 if (read_be32(&attr->tag) != CBFS_FILE_ATTR_TAG_COMPRESSION)
115 continue;
116
117 *algo = read_be32(&attr->compression);
118 *size = read_be32(&attr->decompressed_size);
119 rdev_munmap(&fh->metadata, metadata);
120 return 0;
121 }
122
123 *algo = CBFS_COMPRESS_NONE;
124 *size = region_device_sz(&fh->data);
125 rdev_munmap(&fh->metadata, metadata);
126 return 0;
127}
128
Patrick Rudolpheeb4e202018-04-26 10:26:13 +0200129int cbfsf_file_type(struct cbfsf *fh, uint32_t *ftype)
Aaron Durbincbb6c752015-12-15 15:57:11 -0600130{
131 const size_t sz = sizeof(*ftype);
132
133 if (rdev_readat(&fh->metadata, ftype,
134 offsetof(struct cbfs_file, type), sz) != sz)
135 return -1;
136
137 *ftype = read_be32(ftype);
138
139 return 0;
140}
141
Aaron Durbin295d58b2015-12-15 13:33:51 -0600142int cbfs_locate(struct cbfsf *fh, const struct region_device *cbfs,
143 const char *name, uint32_t *type)
144{
145 struct cbfsf *prev;
146
147 LOG("Locating '%s'\n", name);
148
149 prev = NULL;
150
151 while (1) {
152 int ret;
153 char *fname;
154 int name_match;
155 const size_t fsz = sizeof(struct cbfs_file);
156
157 ret = cbfs_for_each_file(cbfs, prev, fh);
158 prev = fh;
159
160 /* Either failed to read or hit the end of the region. */
161 if (ret < 0 || ret > 0)
162 break;
163
164 fname = rdev_mmap(&fh->metadata, fsz,
165 region_device_sz(&fh->metadata) - fsz);
166
167 if (fname == NULL)
168 break;
169
170 name_match = !strcmp(fname, name);
171 rdev_munmap(&fh->metadata, fname);
172
173 if (!name_match) {
174 DEBUG(" Unmatched '%s' at %zx\n", fname,
175 rdev_relative_offset(cbfs, &fh->metadata));
176 continue;
177 }
178
179 if (type != NULL) {
180 uint32_t ftype;
181
Aaron Durbincbb6c752015-12-15 15:57:11 -0600182 if (cbfsf_file_type(fh, &ftype))
Aaron Durbin295d58b2015-12-15 13:33:51 -0600183 break;
184
Ronald G. Minnich60fd6842018-05-14 13:27:07 -0700185 if (*type != 0 && *type != ftype) {
Aaron Durbin295d58b2015-12-15 13:33:51 -0600186 DEBUG(" Unmatched type %x at %zx\n", ftype,
187 rdev_relative_offset(cbfs,
188 &fh->metadata));
189 continue;
190 }
Ronald G. Minnich60fd6842018-05-14 13:27:07 -0700191 // *type being 0 means we want to know ftype.
192 // We could just do a blind assignment but
193 // if type is pointing to read-only memory
194 // that might be bad.
195 if (*type == 0)
196 *type = ftype;
Aaron Durbin295d58b2015-12-15 13:33:51 -0600197 }
198
199 LOG("Found @ offset %zx size %zx\n",
200 rdev_relative_offset(cbfs, &fh->metadata),
201 region_device_sz(&fh->data));
202
203 /* Success. */
204 return 0;
205 }
206
207 LOG("'%s' not found.\n", name);
208 return -1;
209}
Aaron Durbincbb6c752015-12-15 15:57:11 -0600210
211static int cbfs_extend_hash_buffer(struct vb2_digest_context *ctx,
212 void *buf, size_t sz)
213{
214 return vb2_digest_extend(ctx, buf, sz);
215}
216
217static int cbfs_extend_hash(struct vb2_digest_context *ctx,
218 const struct region_device *rdev)
219{
220 uint8_t buffer[1024];
221 size_t sz_left;
222 size_t offset;
223
224 sz_left = region_device_sz(rdev);
225 offset = 0;
226
227 while (sz_left) {
228 int rv;
229 size_t block_sz = MIN(sz_left, sizeof(buffer));
230
231 if (rdev_readat(rdev, buffer, offset, block_sz) != block_sz)
232 return VB2_ERROR_UNKNOWN;
233
234 rv = cbfs_extend_hash_buffer(ctx, buffer, block_sz);
235
236 if (rv)
237 return rv;
238
239 sz_left -= block_sz;
240 offset += block_sz;
241 }
242
243 return VB2_SUCCESS;
244}
245
246/* Include offsets of child regions within the parent into the hash. */
247static int cbfs_extend_hash_with_offset(struct vb2_digest_context *ctx,
248 const struct region_device *p,
249 const struct region_device *c)
250{
251 int32_t soffset;
252 int rv;
253
254 soffset = rdev_relative_offset(p, c);
255
256 if (soffset < 0)
257 return VB2_ERROR_UNKNOWN;
258
259 /* All offsets in big endian format. */
260 write_be32(&soffset, soffset);
261
262 rv = cbfs_extend_hash_buffer(ctx, &soffset, sizeof(soffset));
263
264 if (rv)
265 return rv;
266
267 return cbfs_extend_hash(ctx, c);
268}
269
270/* Hash in the potential CBFS header sitting at the beginning of the CBFS
271 * region as well as relative offset at the end. */
272static int cbfs_extend_hash_master_header(struct vb2_digest_context *ctx,
273 const struct region_device *cbfs)
274{
275 struct region_device rdev;
276 int rv;
277
278 if (rdev_chain(&rdev, cbfs, 0, sizeof(struct cbfs_header)))
279 return VB2_ERROR_UNKNOWN;
280
281 rv = cbfs_extend_hash_with_offset(ctx, cbfs, &rdev);
282
283 if (rv)
284 return rv;
285
286 /* Include potential relative offset at end of region. */
287 if (rdev_chain(&rdev, cbfs, region_device_sz(cbfs) - sizeof(int32_t),
288 sizeof(int32_t)))
289 return VB2_ERROR_UNKNOWN;
290
291 return cbfs_extend_hash_with_offset(ctx, cbfs, &rdev);
292}
293
294int cbfs_vb2_hash_contents(const struct region_device *cbfs,
295 enum vb2_hash_algorithm hash_alg, void *digest,
296 size_t digest_sz)
297{
298 struct vb2_digest_context ctx;
299 int rv;
300 struct cbfsf f;
301 struct cbfsf *prev;
302 struct cbfsf *fh;
303
304 rv = vb2_digest_init(&ctx, hash_alg);
305
306 if (rv)
307 return rv;
308
309 rv = cbfs_extend_hash_master_header(&ctx, cbfs);
310 if (rv)
311 return rv;
312
313 prev = NULL;
314 fh = &f;
315
316 while (1) {
317 uint32_t ftype;
318
319 rv = cbfs_for_each_file(cbfs, prev, fh);
320 prev = fh;
321
322 if (rv < 0)
323 return VB2_ERROR_UNKNOWN;
324
325 /* End of CBFS. */
326 if (rv > 0)
327 break;
328
329 rv = cbfs_extend_hash_with_offset(&ctx, cbfs, &fh->metadata);
330
331 if (rv)
332 return rv;
333
334 /* Include data contents in hash if file is non-empty. */
335 if (cbfsf_file_type(fh, &ftype))
336 return VB2_ERROR_UNKNOWN;
337
338 if (ftype == CBFS_TYPE_DELETED || ftype == CBFS_TYPE_DELETED2)
339 continue;
340
341 rv = cbfs_extend_hash_with_offset(&ctx, cbfs, &fh->data);
342
343 if (rv)
344 return rv;
345 }
346
347 return vb2_digest_finalize(&ctx, digest, digest_sz);
348}