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