xref: /aosp_15_r20/external/coreboot/payloads/libpayload/libcbfs/cbfs.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: BSD-3-Clause */
2 
3 #include <libpayload-config.h>
4 #include <arch/virtual.h>
5 #include <assert.h>
6 #include <cbfs.h>
7 #include <cbfs_glue.h>
8 #include <commonlib/bsd/cbfs_private.h>
9 #include <commonlib/bsd/fmap_serialized.h>
10 #include <libpayload.h>
11 #include <lp_vboot.h>
12 #include <lz4.h>
13 #include <lzma.h>
14 #include <string.h>
15 #include <sysinfo.h>
16 
17 
cbfs_get_boot_device(bool force_ro)18 static const struct cbfs_boot_device *cbfs_get_boot_device(bool force_ro)
19 {
20 	static struct cbfs_boot_device ro;
21 	static struct cbfs_boot_device rw;
22 
23 	if (!force_ro) {
24 		if (!rw.dev.size) {
25 			rw.dev.offset = lib_sysinfo.cbfs_offset;
26 			rw.dev.size = lib_sysinfo.cbfs_size;
27 			rw.mcache = phys_to_virt(lib_sysinfo.cbfs_rw_mcache_offset);
28 			rw.mcache_size = lib_sysinfo.cbfs_rw_mcache_size;
29 		}
30 		return &rw;
31 	}
32 
33 	if (ro.dev.size)
34 		return &ro;
35 
36 	if (fmap_locate_area("COREBOOT", &ro.dev.offset, &ro.dev.size))
37 		return NULL;
38 
39 	ro.mcache = phys_to_virt(lib_sysinfo.cbfs_ro_mcache_offset);
40 	ro.mcache_size = lib_sysinfo.cbfs_ro_mcache_size;
41 
42 	return &ro;
43 }
44 
_cbfs_boot_lookup(const char * name,bool force_ro,union cbfs_mdata * mdata)45 ssize_t _cbfs_boot_lookup(const char *name, bool force_ro, union cbfs_mdata *mdata)
46 {
47 	const struct cbfs_boot_device *cbd = cbfs_get_boot_device(force_ro);
48 	if (!cbd)
49 		return CB_ERR;
50 
51 	size_t data_offset;
52 	enum cb_err err = CB_CBFS_CACHE_FULL;
53 	if (cbd->mcache_size)
54 		err = cbfs_mcache_lookup(cbd->mcache, cbd->mcache_size, name, mdata,
55 					 &data_offset);
56 
57 	if (err == CB_CBFS_CACHE_FULL)
58 		err = cbfs_lookup(&cbd->dev, name, mdata, &data_offset, NULL);
59 
60 	/* Fallback to RO if possible. */
61 	if (CONFIG(LP_ENABLE_CBFS_FALLBACK) && !force_ro && err == CB_CBFS_NOT_FOUND) {
62 		LOG("Fall back to RO region for '%s'\n", name);
63 		return _cbfs_boot_lookup(name, true, mdata);
64 	}
65 
66 	if (err) {
67 		if (err == CB_CBFS_NOT_FOUND)
68 			LOG("'%s' not found.\n", name);
69 		else
70 			ERROR("Error %d when looking up '%s'\n", err, name);
71 		return err;
72 	}
73 
74 	return cbd->dev.offset + data_offset;
75 }
76 
cbfs_unmap(void * mapping)77 void cbfs_unmap(void *mapping)
78 {
79 	free(mapping);
80 }
81 
cbfs_file_hash_mismatch(const void * buffer,size_t size,const union cbfs_mdata * mdata,bool skip_verification)82 static bool cbfs_file_hash_mismatch(const void *buffer, size_t size,
83 				    const union cbfs_mdata *mdata, bool skip_verification)
84 {
85 	if (!CONFIG(LP_CBFS_VERIFICATION) || skip_verification)
86 		return false;
87 
88 	const struct vb2_hash *hash = cbfs_file_hash(mdata);
89 	if (!hash) {
90 		ERROR("'%s' does not have a file hash!\n", mdata->h.filename);
91 		return true;
92 	}
93 	vb2_error_t rv = vb2_hash_verify(cbfs_hwcrypto_allowed(), buffer, size, hash);
94 	if (rv != VB2_SUCCESS) {
95 		ERROR("'%s' file hash mismatch!\n", mdata->h.filename);
96 		if (CONFIG(LP_VBOOT_CBFS_INTEGRATION) && !vboot_recovery_mode_enabled())
97 			vboot_fail_and_reboot(vboot_get_context(), VB2_RECOVERY_FW_BODY, rv);
98 		return true;
99 	}
100 
101 	return false;
102 }
103 
cbfs_load_and_decompress(size_t offset,size_t in_size,void * buffer,size_t buffer_size,uint32_t compression,const union cbfs_mdata * mdata,bool skip_verification)104 static size_t cbfs_load_and_decompress(size_t offset, size_t in_size, void *buffer,
105 				       size_t buffer_size, uint32_t compression,
106 				       const union cbfs_mdata *mdata, bool skip_verification)
107 {
108 	void *load = buffer;
109 	size_t out_size = 0;
110 
111 	DEBUG("Decompressing %zu bytes from '%s' to %p with algo %d\n", in_size,
112 	      mdata->h.filename, buffer, compression);
113 
114 	if (compression != CBFS_COMPRESS_NONE) {
115 		load = malloc(in_size);
116 		if (!load) {
117 			ERROR("'%s' buffer allocation failed\n", mdata->h.filename);
118 			return 0;
119 		}
120 	}
121 
122 	if (boot_device_read(load, offset, in_size) != in_size) {
123 		ERROR("'%s' failed to read contents of file\n", mdata->h.filename);
124 		goto out;
125 	}
126 
127 	if (cbfs_file_hash_mismatch(load, in_size, mdata, skip_verification))
128 		goto out;
129 
130 	switch (compression) {
131 	case CBFS_COMPRESS_NONE:
132 		out_size = in_size;
133 		break;
134 	case CBFS_COMPRESS_LZ4:
135 		if (!CONFIG(LP_LZ4))
136 			goto out;
137 		out_size = ulz4fn(load, in_size, buffer, buffer_size);
138 		break;
139 	case CBFS_COMPRESS_LZMA:
140 		if (!CONFIG(LP_LZMA))
141 			goto out;
142 		out_size = ulzman(load, in_size, buffer, buffer_size);
143 		break;
144 	default:
145 		ERROR("'%s' decompression algo %d not supported\n", mdata->h.filename,
146 		      compression);
147 	}
148 out:
149 	if (load != buffer)
150 		free(load);
151 	return out_size;
152 }
153 
do_load(union cbfs_mdata * mdata,ssize_t offset,void * buf,size_t * size_inout,bool skip_verification)154 static void *do_load(union cbfs_mdata *mdata, ssize_t offset, void *buf, size_t *size_inout,
155 		     bool skip_verification)
156 {
157 	bool malloced = false;
158 	size_t buf_size = 0;
159 	size_t out_size;
160 	uint32_t compression = CBFS_COMPRESS_NONE;
161 	const struct cbfs_file_attr_compression *cattr =
162 		cbfs_find_attr(mdata, CBFS_FILE_ATTR_TAG_COMPRESSION, sizeof(*cattr));
163 	if (cattr) {
164 		compression = be32toh(cattr->compression);
165 		out_size = be32toh(cattr->decompressed_size);
166 	} else {
167 		out_size = be32toh(mdata->h.len);
168 	}
169 
170 	if (size_inout) {
171 		buf_size = *size_inout;
172 		*size_inout = out_size;
173 	}
174 
175 	if (buf) {
176 		if (!size_inout || buf_size < out_size) {
177 			ERROR("'%s' buffer too small\n", mdata->h.filename);
178 			return NULL;
179 		}
180 	} else {
181 		buf = malloc(out_size);
182 		if (!buf) {
183 			ERROR("'%s' allocation failure\n", mdata->h.filename);
184 			return NULL;
185 		}
186 		malloced = true;
187 	}
188 
189 	if (cbfs_load_and_decompress(offset, be32toh(mdata->h.len), buf, out_size, compression,
190 				     mdata, skip_verification)
191 	    != out_size) {
192 		if (malloced)
193 			free(buf);
194 		return NULL;
195 	}
196 
197 	return buf;
198 }
199 
_cbfs_load(const char * name,void * buf,size_t * size_inout,bool force_ro)200 void *_cbfs_load(const char *name, void *buf, size_t *size_inout, bool force_ro)
201 {
202 	ssize_t offset;
203 	union cbfs_mdata mdata;
204 
205 	DEBUG("%s(name='%s', buf=%p, force_ro=%s)\n", __func__, name, buf,
206 	      force_ro ? "true" : "false");
207 
208 	offset = _cbfs_boot_lookup(name, force_ro, &mdata);
209 	if (offset < 0)
210 		return NULL;
211 
212 	return do_load(&mdata, offset, buf, size_inout, false);
213 }
214 
_cbfs_unverified_area_load(const char * area,const char * name,void * buf,size_t * size_inout)215 void *_cbfs_unverified_area_load(const char *area, const char *name, void *buf,
216 				 size_t *size_inout)
217 {
218 	struct cbfs_dev dev;
219 	union cbfs_mdata mdata;
220 	size_t data_offset;
221 
222 	DEBUG("%s(area='%s', name='%s', buf=%p)\n", __func__, area, name, buf);
223 
224 	if (fmap_locate_area(area, &dev.offset, &dev.size) != CB_SUCCESS)
225 		return NULL;
226 
227 	if (cbfs_lookup(&dev, name, &mdata, &data_offset, NULL)) {
228 		ERROR("'%s' not found in '%s'\n", name, area);
229 		return NULL;
230 	}
231 
232 	return do_load(&mdata, dev.offset + data_offset, buf, size_inout, true);
233 }
234 
235 /* This should be overridden by payloads that want to enforce more explicit
236    policy on using HW crypto. */
cbfs_hwcrypto_allowed(void)237 __weak bool cbfs_hwcrypto_allowed(void)
238 {
239 	/* Avoid compiling vboot calls to prevent linker errors. */
240 	if (!CONFIG(LP_CBFS_VERIFICATION))
241 		return true;
242 
243 	return vb2api_hwcrypto_allowed(vboot_get_context());
244 }
245