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