cbfs.c
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 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 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 77 void cbfs_unmap(void *mapping) 78 { 79 free(mapping); 80 } 81 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 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 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 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 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. */ 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 }