coreboot
coreboot is an Open Source project aimed at replacing the proprietary BIOS found in most computers.
cbfs_private.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later */
2 
4 #include <assert.h>
5 
6 static enum cb_err read_next_header(cbfs_dev_t dev, size_t *offset, struct cbfs_file *buffer,
7  const size_t devsize)
8 {
9  DEBUG("Looking for next file @%#zx...\n", *offset);
11  while (*offset + sizeof(*buffer) < devsize) {
12  if (cbfs_dev_read(dev, buffer, *offset, sizeof(*buffer)) != sizeof(*buffer))
13  return CB_CBFS_IO;
14 
15  if (memcmp(buffer->magic, CBFS_FILE_MAGIC, sizeof(buffer->magic)) == 0)
16  return CB_SUCCESS;
17 
19  }
20 
21  DEBUG("End of CBFS reached\n");
22  return CB_CBFS_NOT_FOUND;
23 }
24 
25 enum cb_err cbfs_walk(cbfs_dev_t dev, enum cb_err (*walker)(cbfs_dev_t dev, size_t offset,
26  const union cbfs_mdata *mdata,
27  size_t already_read, void *arg),
28  void *arg, struct vb2_hash *metadata_hash, enum cbfs_walk_flags flags)
29 {
30  const bool do_hash = CBFS_ENABLE_HASHING && metadata_hash;
31  const size_t devsize = cbfs_dev_size(dev);
32  struct vb2_digest_context dc;
33  vb2_error_t vbrv;
34 
35  assert(CBFS_ENABLE_HASHING || (!metadata_hash && !(flags & CBFS_WALK_WRITEBACK_HASH)));
36  if (do_hash && (vbrv = vb2_digest_init(&dc, metadata_hash->algo))) {
37  ERROR("Metadata hash digest (%d) init error: %#x\n", metadata_hash->algo, vbrv);
38  return CB_ERR_ARG;
39  }
40 
41  size_t offset = 0;
42  enum cb_err ret_header;
43  enum cb_err ret_walker = CB_CBFS_NOT_FOUND;
44  union cbfs_mdata mdata;
45  while ((ret_header = read_next_header(dev, &offset, &mdata.h, devsize)) == CB_SUCCESS) {
46  const uint32_t attr_offset = be32toh(mdata.h.attributes_offset);
47  const uint32_t data_offset = be32toh(mdata.h.offset);
48  const uint32_t data_length = be32toh(mdata.h.len);
49  const uint32_t type = be32toh(mdata.h.type);
50  const bool empty = (type == CBFS_TYPE_DELETED || type == CBFS_TYPE_NULL);
51 
52  DEBUG("Found CBFS header @%#zx (type %d, attr +%#x, data +%#x, length %#x)\n",
53  offset, type, attr_offset, data_offset, data_length);
54  if (data_offset > sizeof(mdata) || data_length > devsize ||
55  offset + data_offset + data_length > devsize) {
56  ERROR("File @%#zx too large\n", offset);
58  continue;
59  }
60 
61  if (empty && !(flags & CBFS_WALK_INCLUDE_EMPTY))
62  goto next_file;
63 
64  /* When hashing we need to read everything. Otherwise skip the attributes.
65  attr_offset may be 0, which means there are no attributes. */
66  ssize_t todo;
67  if (do_hash || attr_offset == 0)
68  todo = data_offset - sizeof(mdata.h);
69  else
70  todo = attr_offset - sizeof(mdata.h);
71  if (todo <= 0 || data_offset < attr_offset) {
72  ERROR("Corrupt file header @%#zx\n", offset);
73  goto next_file;
74  }
75 
76  /* Read the rest of the metadata (filename, and possibly attributes). */
77  assert(todo > 0 && todo <= sizeof(mdata) - sizeof(mdata.h));
78  if (cbfs_dev_read(dev, mdata.raw + sizeof(mdata.h),
79  offset + sizeof(mdata.h), todo) != todo)
80  return CB_CBFS_IO;
81  /* Force filename null-termination, just in case. */
82  mdata.raw[attr_offset ? attr_offset - 1 : data_offset - 1] = '\0';
83  DEBUG("File name: '%s'\n", mdata.h.filename);
84 
85  if (do_hash && !empty && vb2_digest_extend(&dc, mdata.raw, data_offset))
86  return CB_ERR;
87 
88  if (walker && ret_walker == CB_CBFS_NOT_FOUND)
89  ret_walker = walker(dev, offset, &mdata, sizeof(mdata.h) + todo, arg);
90 
91  /* Return IO errors immediately. For others, finish the hash first if needed. */
92  if (ret_walker == CB_CBFS_IO || (ret_walker != CB_CBFS_NOT_FOUND && !do_hash))
93  return ret_walker;
94 
95 next_file:
96  offset += data_offset + data_length;
97  }
98 
99  if (ret_header != CB_CBFS_NOT_FOUND)
100  return ret_header;
101 
102  if (do_hash) {
103  uint8_t real_hash[VB2_MAX_DIGEST_SIZE];
104  size_t hash_size = vb2_digest_size(metadata_hash->algo);
105  if (vb2_digest_finalize(&dc, real_hash, hash_size))
106  return CB_ERR;
107  if (flags & CBFS_WALK_WRITEBACK_HASH)
108  memcpy(metadata_hash->raw, real_hash, hash_size);
109  else if (memcmp(metadata_hash->raw, real_hash, hash_size) != 0)
110  return CB_CBFS_HASH_MISMATCH;
111  }
112 
113  return ret_walker;
114 }
115 
116 enum cb_err cbfs_copy_fill_metadata(union cbfs_mdata *dst, const union cbfs_mdata *src,
117  size_t already_read, cbfs_dev_t dev, size_t offset)
118 {
119  /* First, copy the stuff that cbfs_walk() already read for us. */
120  memcpy(dst, src, already_read);
121 
122  /* Then read in whatever metadata may be left (will only happen in non-hashing case). */
123  const size_t todo = be32toh(src->h.offset) - already_read;
124  assert(todo <= sizeof(*dst) - already_read);
125  if (todo && cbfs_dev_read(dev, dst->raw + already_read, offset + already_read,
126  todo) != todo)
127  return CB_CBFS_IO;
128  return CB_SUCCESS;
129 }
130 
133  const char *name;
134  size_t namesize;
136 };
137 
138 static enum cb_err lookup_walker(cbfs_dev_t dev, size_t offset, const union cbfs_mdata *mdata,
139  size_t already_read, void *arg)
140 {
141  struct cbfs_lookup_args *args = arg;
142  /* Check if the name we're looking for could fit, then we can safely memcmp() it. */
143  if (args->namesize > already_read - offsetof(union cbfs_mdata, h.filename) ||
144  memcmp(args->name, mdata->h.filename, args->namesize) != 0)
145  return CB_CBFS_NOT_FOUND;
146 
147  LOG("Found '%s' @%#zx size %#x\n", args->name, offset, be32toh(mdata->h.len));
148  if (cbfs_copy_fill_metadata(args->mdata_out, mdata, already_read, dev, offset))
149  return CB_CBFS_IO;
150 
151  *args->data_offset_out = offset + be32toh(mdata->h.offset);
152  return CB_SUCCESS;
153 }
154 
155 enum cb_err cbfs_lookup(cbfs_dev_t dev, const char *name, union cbfs_mdata *mdata_out,
156  size_t *data_offset_out, struct vb2_hash *metadata_hash)
157 {
158  struct cbfs_lookup_args args = {
159  .mdata_out = mdata_out,
160  .name = name,
161  .namesize = strlen(name) + 1, /* Count trailing \0 so we can memcmp() it. */
162  .data_offset_out = data_offset_out,
163  };
164  return cbfs_walk(dev, lookup_walker, &args, metadata_hash, 0);
165 }
166 
167 const void *cbfs_find_attr(const union cbfs_mdata *mdata, uint32_t attr_tag, size_t size_check)
168 {
170  uint32_t end = be32toh(mdata->h.offset);
171 
172  if (!offset)
173  return NULL;
174 
175  while (offset + sizeof(struct cbfs_file_attribute) <= end) {
176  const struct cbfs_file_attribute *attr = (const void *)mdata->raw + offset;
177  const uint32_t tag = be32toh(attr->tag);
178  const uint32_t len = be32toh(attr->len);
179 
180  if (len < sizeof(struct cbfs_file_attribute) || len > end - offset) {
181  ERROR("Attribute %s[%x] invalid length: %u\n",
182  mdata->h.filename, tag, len);
183  return NULL;
184  }
185  if (tag == attr_tag) {
186  if (size_check && len != size_check) {
187  ERROR("Attribute %s[%x] size mismatch: %u != %zu\n",
188  mdata->h.filename, tag, len, size_check);
189  return NULL;
190  }
191  return attr;
192  }
193  offset += len;
194  }
195 
196  return NULL;
197 }
198 
199 const struct vb2_hash *cbfs_file_hash(const union cbfs_mdata *mdata)
200 {
201  /* Hashes are variable-length attributes, so need to manually check the length. */
202  const struct cbfs_file_attr_hash *attr =
204  if (!attr)
205  return NULL; /* no hash */
206  const size_t asize = be32toh(attr->len);
207 
208  const struct vb2_hash *hash = &attr->hash;
209  const size_t hsize = vb2_digest_size(hash->algo);
210  if (!hsize) {
211  ERROR("Hash algo %u for '%s' unsupported.\n", hash->algo, mdata->h.filename);
212  return NULL;
213  }
214  if (hsize != asize - offsetof(struct cbfs_file_attr_hash, hash.raw)) {
215  ERROR("Hash attribute size for '%s' (%zu) incorrect for algo %u.\n",
216  mdata->h.filename, asize, hash->algo);
217  return NULL;
218  }
219  return hash;
220 }
const char * name
Definition: mmu.c:92
void * memcpy(void *dest, const void *src, size_t n)
Definition: memcpy.c:7
#define assert(statement)
Definition: assert.h:74
#define offsetof(TYPE, MEMBER)
Definition: helpers.h:84
#define ALIGN_UP(x, a)
Definition: helpers.h:17
cb_err
coreboot error codes
Definition: cb_err.h:15
@ CB_ERR
Generic error code.
Definition: cb_err.h:17
@ CB_ERR_ARG
Invalid argument.
Definition: cb_err.h:18
@ CB_SUCCESS
Call completed successfully.
Definition: cb_err.h:16
@ CB_CBFS_IO
Underlying I/O error.
Definition: cb_err.h:39
@ CB_CBFS_NOT_FOUND
File not found in directory.
Definition: cb_err.h:40
@ CB_CBFS_HASH_MISMATCH
Master hash validation failed.
Definition: cb_err.h:41
#define ERROR(...)
Definition: cbfs_glue.h:22
static ssize_t cbfs_dev_read(cbfs_dev_t dev, void *buffer, size_t offset, size_t size)
Definition: cbfs_glue.h:31
#define LOG(...)
Definition: cbfs_glue.h:23
static size_t cbfs_dev_size(cbfs_dev_t dev)
Definition: cbfs_glue.h:36
#define CBFS_ENABLE_HASHING
Definition: cbfs_glue.h:19
static enum cb_err read_next_header(cbfs_dev_t dev, size_t *offset, struct cbfs_file *buffer, const size_t devsize)
Definition: cbfs_private.c:6
const struct vb2_hash * cbfs_file_hash(const union cbfs_mdata *mdata)
Definition: cbfs_private.c:199
enum cb_err cbfs_walk(cbfs_dev_t dev, enum cb_err(*walker)(cbfs_dev_t dev, size_t offset, const union cbfs_mdata *mdata, size_t already_read, void *arg), void *arg, struct vb2_hash *metadata_hash, enum cbfs_walk_flags flags)
Definition: cbfs_private.c:25
static enum cb_err lookup_walker(cbfs_dev_t dev, size_t offset, const union cbfs_mdata *mdata, size_t already_read, void *arg)
Definition: cbfs_private.c:138
enum cb_err cbfs_lookup(cbfs_dev_t dev, const char *name, union cbfs_mdata *mdata_out, size_t *data_offset_out, struct vb2_hash *metadata_hash)
Definition: cbfs_private.c:155
const void * cbfs_find_attr(const union cbfs_mdata *mdata, uint32_t attr_tag, size_t size_check)
Definition: cbfs_private.c:167
enum cb_err cbfs_copy_fill_metadata(union cbfs_mdata *dst, const union cbfs_mdata *src, size_t already_read, cbfs_dev_t dev, size_t offset)
Definition: cbfs_private.c:116
cbfs_walk_flags
Definition: cbfs_private.h:45
@ CBFS_WALK_INCLUDE_EMPTY
Definition: cbfs_private.h:52
@ CBFS_WALK_WRITEBACK_HASH
Definition: cbfs_private.h:48
@ CBFS_TYPE_DELETED
@ CBFS_TYPE_NULL
#define CBFS_FILE_MAGIC
This is a component header - every entry in the CBFS will have this header.
#define CBFS_ALIGNMENT
@ CBFS_FILE_ATTR_TAG_HASH
struct @413::@414 args
#define DEBUG
Definition: software_i2c.c:12
static size_t offset
Definition: flashconsole.c:16
static uint32_t be32toh(uint32_t big_endian_32bits)
Definition: endian.h:130
struct bootblock_arg arg
Definition: decompressor.c:22
unsigned int type
Definition: edid.c:57
u8 buffer[C2P_BUFFER_MAXSIZE]
Definition: psp_smm.c:18
#define NULL
Definition: stddef.h:19
__SIZE_TYPE__ ssize_t
Definition: stddef.h:13
unsigned int uint32_t
Definition: stdint.h:14
unsigned char uint8_t
Definition: stdint.h:8
int memcmp(const void *s1, const void *s2, size_t n)
Definition: memcmp.c:3
size_t strlen(const char *src)
Definition: string.c:42
struct vb2_hash hash
uint32_t len
uint32_t offset
uint32_t type
char filename[0]
uint32_t attributes_offset
union cbfs_mdata * mdata_out
Definition: cbfs_private.c:132
const char * name
Definition: cbfs_private.c:133
size_t * data_offset_out
Definition: cbfs_private.c:135
struct cbfs_file h
Definition: cbfs_mdata.h:21
uint8_t raw[CBFS_METADATA_MAX_SIZE]
Definition: cbfs_mdata.h:22