Jakub Czapiga | 63e5427 | 2021-11-15 08:36:07 +0000 | [diff] [blame] | 1 | /* SPDX-License-Identifier: BSD-3-Clause */ |
Patrick Georgi | 6de1ee4a | 2011-07-21 15:43:14 +0200 | [diff] [blame] | 2 | |
Jakub Czapiga | 63e5427 | 2021-11-15 08:36:07 +0000 | [diff] [blame] | 3 | #include <libpayload-config.h> |
| 4 | #include <arch/virtual.h> |
| 5 | #include <assert.h> |
Hung-Te Lin | d01d036 | 2013-01-25 12:42:40 +0800 | [diff] [blame] | 6 | #include <cbfs.h> |
Jakub Czapiga | ad45f68 | 2022-01-03 15:06:21 +0000 | [diff] [blame] | 7 | #include <cbfs_glue.h> |
Jakub Czapiga | 63e5427 | 2021-11-15 08:36:07 +0000 | [diff] [blame] | 8 | #include <commonlib/bsd/cbfs_private.h> |
| 9 | #include <commonlib/bsd/fmap_serialized.h> |
| 10 | #include <libpayload.h> |
| 11 | #include <lz4.h> |
| 12 | #include <lzma.h> |
Hung-Te Lin | d01d036 | 2013-01-25 12:42:40 +0800 | [diff] [blame] | 13 | #include <string.h> |
Jakub Czapiga | 63e5427 | 2021-11-15 08:36:07 +0000 | [diff] [blame] | 14 | #include <sysinfo.h> |
Patrick Georgi | 6de1ee4a | 2011-07-21 15:43:14 +0200 | [diff] [blame] | 15 | |
Patrick Georgi | 6de1ee4a | 2011-07-21 15:43:14 +0200 | [diff] [blame] | 16 | |
Jakub Czapiga | 63e5427 | 2021-11-15 08:36:07 +0000 | [diff] [blame] | 17 | static const struct cbfs_boot_device *cbfs_get_boot_device(bool force_ro) |
Stefan Reinauer | 09e16dc | 2013-01-14 10:20:15 -0800 | [diff] [blame] | 18 | { |
Jakub Czapiga | 63e5427 | 2021-11-15 08:36:07 +0000 | [diff] [blame] | 19 | static struct cbfs_boot_device ro; |
| 20 | static struct cbfs_boot_device rw; |
| 21 | |
| 22 | if (!force_ro) { |
| 23 | if (!rw.dev.size) { |
| 24 | rw.dev.offset = lib_sysinfo.cbfs_offset; |
| 25 | rw.dev.size = lib_sysinfo.cbfs_size; |
| 26 | rw.mcache = phys_to_virt(lib_sysinfo.cbfs_rw_mcache_offset); |
| 27 | rw.mcache_size = lib_sysinfo.cbfs_rw_mcache_size; |
| 28 | } |
| 29 | return &rw; |
| 30 | } |
| 31 | |
| 32 | if (ro.dev.size) |
| 33 | return &ro; |
| 34 | |
| 35 | if (fmap_locate_area("COREBOOT", &ro.dev.offset, &ro.dev.size)) |
| 36 | return NULL; |
| 37 | |
| 38 | ro.mcache = phys_to_virt(lib_sysinfo.cbfs_ro_mcache_offset); |
| 39 | ro.mcache_size = lib_sysinfo.cbfs_ro_mcache_size; |
| 40 | |
| 41 | return &ro; |
Patrick Georgi | 409d17d | 2012-01-17 15:52:05 +0100 | [diff] [blame] | 42 | } |
| 43 | |
Jakub Czapiga | 63e5427 | 2021-11-15 08:36:07 +0000 | [diff] [blame] | 44 | ssize_t _cbfs_boot_lookup(const char *name, bool force_ro, union cbfs_mdata *mdata) |
Stefan Reinauer | 09e16dc | 2013-01-14 10:20:15 -0800 | [diff] [blame] | 45 | { |
Jakub Czapiga | 63e5427 | 2021-11-15 08:36:07 +0000 | [diff] [blame] | 46 | const struct cbfs_boot_device *cbd = cbfs_get_boot_device(force_ro); |
| 47 | if (!cbd) |
| 48 | return CB_ERR; |
| 49 | |
| 50 | size_t data_offset; |
Julius Werner | 69cc557 | 2022-03-04 17:49:56 -0800 | [diff] [blame] | 51 | enum cb_err err = CB_CBFS_CACHE_FULL; |
Jakub Czapiga | 63e5427 | 2021-11-15 08:36:07 +0000 | [diff] [blame] | 52 | if (cbd->mcache_size) |
| 53 | err = cbfs_mcache_lookup(cbd->mcache, cbd->mcache_size, name, mdata, |
| 54 | &data_offset); |
| 55 | |
| 56 | if (err == CB_CBFS_CACHE_FULL) |
| 57 | err = cbfs_lookup(&cbd->dev, name, mdata, &data_offset, NULL); |
| 58 | |
| 59 | /* Fallback to RO if possible. */ |
| 60 | if (CONFIG(LP_ENABLE_CBFS_FALLBACK) && !force_ro && err == CB_CBFS_NOT_FOUND) { |
| 61 | LOG("Fall back to RO region for '%s'\n", name); |
| 62 | return _cbfs_boot_lookup(name, true, mdata); |
| 63 | } |
| 64 | |
| 65 | if (err) { |
| 66 | if (err == CB_CBFS_NOT_FOUND) |
| 67 | LOG("'%s' not found.\n", name); |
| 68 | else |
| 69 | ERROR("Error %d when looking up '%s'\n", err, name); |
| 70 | return err; |
| 71 | } |
| 72 | |
| 73 | return cbd->dev.offset + data_offset; |
Patrick Georgi | 409d17d | 2012-01-17 15:52:05 +0100 | [diff] [blame] | 74 | } |
| 75 | |
Jakub Czapiga | 63e5427 | 2021-11-15 08:36:07 +0000 | [diff] [blame] | 76 | void cbfs_unmap(void *mapping) |
Patrick Georgi | 409d17d | 2012-01-17 15:52:05 +0100 | [diff] [blame] | 77 | { |
Jakub Czapiga | 63e5427 | 2021-11-15 08:36:07 +0000 | [diff] [blame] | 78 | free(mapping); |
Patrick Georgi | 409d17d | 2012-01-17 15:52:05 +0100 | [diff] [blame] | 79 | } |
| 80 | |
Jakub Czapiga | 63e5427 | 2021-11-15 08:36:07 +0000 | [diff] [blame] | 81 | static bool cbfs_file_hash_mismatch(const void *buffer, size_t size, |
Jakub Czapiga | ad45f68 | 2022-01-03 15:06:21 +0000 | [diff] [blame] | 82 | const union cbfs_mdata *mdata, bool skip_verification) |
Patrick Georgi | 409d17d | 2012-01-17 15:52:05 +0100 | [diff] [blame] | 83 | { |
Jakub Czapiga | ad45f68 | 2022-01-03 15:06:21 +0000 | [diff] [blame] | 84 | if (!CONFIG(LP_CBFS_VERIFICATION) || skip_verification) |
Jakub Czapiga | 63e5427 | 2021-11-15 08:36:07 +0000 | [diff] [blame] | 85 | return false; |
Hung-Te Lin | d01d036 | 2013-01-25 12:42:40 +0800 | [diff] [blame] | 86 | |
Jakub Czapiga | 63e5427 | 2021-11-15 08:36:07 +0000 | [diff] [blame] | 87 | const struct vb2_hash *hash = cbfs_file_hash(mdata); |
| 88 | if (!hash) { |
| 89 | ERROR("'%s' does not have a file hash!\n", mdata->h.filename); |
| 90 | return true; |
| 91 | } |
| 92 | if (vb2_hash_verify(buffer, size, hash) != VB2_SUCCESS) { |
| 93 | ERROR("'%s' file hash mismatch!\n", mdata->h.filename); |
| 94 | return true; |
| 95 | } |
Hung-Te Lin | d01d036 | 2013-01-25 12:42:40 +0800 | [diff] [blame] | 96 | |
Jakub Czapiga | 63e5427 | 2021-11-15 08:36:07 +0000 | [diff] [blame] | 97 | return false; |
| 98 | } |
Hung-Te Lin | d01d036 | 2013-01-25 12:42:40 +0800 | [diff] [blame] | 99 | |
Jakub Czapiga | 63e5427 | 2021-11-15 08:36:07 +0000 | [diff] [blame] | 100 | static size_t cbfs_load_and_decompress(size_t offset, size_t in_size, void *buffer, |
| 101 | size_t buffer_size, uint32_t compression, |
Jakub Czapiga | ad45f68 | 2022-01-03 15:06:21 +0000 | [diff] [blame] | 102 | const union cbfs_mdata *mdata, bool skip_verification) |
Jakub Czapiga | 63e5427 | 2021-11-15 08:36:07 +0000 | [diff] [blame] | 103 | { |
| 104 | void *load = buffer; |
| 105 | size_t out_size = 0; |
| 106 | |
| 107 | DEBUG("Decompressing %zu bytes from '%s' to %p with algo %d\n", in_size, |
| 108 | mdata->h.filename, buffer, compression); |
| 109 | |
| 110 | if (compression != CBFS_COMPRESS_NONE) { |
| 111 | load = malloc(in_size); |
| 112 | if (!load) { |
| 113 | ERROR("'%s' buffer allocation failed\n", mdata->h.filename); |
| 114 | return 0; |
| 115 | } |
| 116 | } |
| 117 | |
| 118 | if (boot_device_read(load, offset, in_size) != in_size) { |
| 119 | ERROR("'%s' failed to read contents of file\n", mdata->h.filename); |
Patrick Georgi | 33ab4fe | 2016-07-29 16:36:23 +0200 | [diff] [blame] | 120 | goto out; |
| 121 | } |
Hung-Te Lin | d01d036 | 2013-01-25 12:42:40 +0800 | [diff] [blame] | 122 | |
Yu-Ping Wu | c1d7d89 | 2022-06-15 15:03:04 +0800 | [diff] [blame^] | 123 | if (cbfs_file_hash_mismatch(load, in_size, mdata, skip_verification)) |
Jakub Czapiga | 63e5427 | 2021-11-15 08:36:07 +0000 | [diff] [blame] | 124 | goto out; |
Gabe Black | ec3a462 | 2013-07-01 04:34:29 -0700 | [diff] [blame] | 125 | |
Jakub Czapiga | 63e5427 | 2021-11-15 08:36:07 +0000 | [diff] [blame] | 126 | switch (compression) { |
| 127 | case CBFS_COMPRESS_NONE: |
| 128 | out_size = in_size; |
| 129 | break; |
| 130 | case CBFS_COMPRESS_LZ4: |
| 131 | if (!CONFIG(LP_LZ4)) |
| 132 | goto out; |
| 133 | out_size = ulz4fn(load, in_size, buffer, buffer_size); |
| 134 | break; |
| 135 | case CBFS_COMPRESS_LZMA: |
| 136 | if (!CONFIG(LP_LZMA)) |
| 137 | goto out; |
| 138 | out_size = ulzman(load, in_size, buffer, buffer_size); |
| 139 | break; |
| 140 | default: |
| 141 | ERROR("'%s' decompression algo %d not supported\n", mdata->h.filename, |
| 142 | compression); |
| 143 | } |
Patrick Georgi | 33ab4fe | 2016-07-29 16:36:23 +0200 | [diff] [blame] | 144 | out: |
Jakub Czapiga | 63e5427 | 2021-11-15 08:36:07 +0000 | [diff] [blame] | 145 | if (load != buffer) |
| 146 | free(load); |
| 147 | return out_size; |
Patrick Georgi | 409d17d | 2012-01-17 15:52:05 +0100 | [diff] [blame] | 148 | } |
Hung-Te Lin | d01d036 | 2013-01-25 12:42:40 +0800 | [diff] [blame] | 149 | |
Jakub Czapiga | ad45f68 | 2022-01-03 15:06:21 +0000 | [diff] [blame] | 150 | static void *do_load(union cbfs_mdata *mdata, ssize_t offset, void *buf, size_t *size_inout, |
| 151 | bool skip_verification) |
Hung-Te Lin | d01d036 | 2013-01-25 12:42:40 +0800 | [diff] [blame] | 152 | { |
Jakub Czapiga | 63e5427 | 2021-11-15 08:36:07 +0000 | [diff] [blame] | 153 | bool malloced = false; |
Jakub Czapiga | ad45f68 | 2022-01-03 15:06:21 +0000 | [diff] [blame] | 154 | size_t out_size; |
Jakub Czapiga | 63e5427 | 2021-11-15 08:36:07 +0000 | [diff] [blame] | 155 | uint32_t compression = CBFS_COMPRESS_NONE; |
| 156 | const struct cbfs_file_attr_compression *cattr = |
Jakub Czapiga | ad45f68 | 2022-01-03 15:06:21 +0000 | [diff] [blame] | 157 | cbfs_find_attr(mdata, CBFS_FILE_ATTR_TAG_COMPRESSION, sizeof(*cattr)); |
Jakub Czapiga | 63e5427 | 2021-11-15 08:36:07 +0000 | [diff] [blame] | 158 | if (cattr) { |
| 159 | compression = be32toh(cattr->compression); |
| 160 | out_size = be32toh(cattr->decompressed_size); |
| 161 | } else { |
Jakub Czapiga | ad45f68 | 2022-01-03 15:06:21 +0000 | [diff] [blame] | 162 | out_size = be32toh(mdata->h.len); |
Jakub Czapiga | 63e5427 | 2021-11-15 08:36:07 +0000 | [diff] [blame] | 163 | } |
| 164 | |
| 165 | if (buf) { |
| 166 | if (!size_inout || *size_inout < out_size) { |
Jakub Czapiga | e904d9a | 2022-02-17 15:03:25 +0100 | [diff] [blame] | 167 | ERROR("'%s' buffer too small\n", mdata->h.filename); |
Jakub Czapiga | 63e5427 | 2021-11-15 08:36:07 +0000 | [diff] [blame] | 168 | return NULL; |
| 169 | } |
| 170 | } else { |
| 171 | buf = malloc(out_size); |
| 172 | if (!buf) { |
Jakub Czapiga | e904d9a | 2022-02-17 15:03:25 +0100 | [diff] [blame] | 173 | ERROR("'%s' allocation failure\n", mdata->h.filename); |
Jakub Czapiga | 63e5427 | 2021-11-15 08:36:07 +0000 | [diff] [blame] | 174 | return NULL; |
| 175 | } |
| 176 | malloced = true; |
| 177 | } |
| 178 | |
Jakub Czapiga | ad45f68 | 2022-01-03 15:06:21 +0000 | [diff] [blame] | 179 | if (cbfs_load_and_decompress(offset, be32toh(mdata->h.len), buf, out_size, compression, |
| 180 | mdata, skip_verification) |
Jakub Czapiga | 63e5427 | 2021-11-15 08:36:07 +0000 | [diff] [blame] | 181 | != out_size) { |
| 182 | if (malloced) |
| 183 | free(buf); |
Julius Werner | 2296479 | 2016-05-12 14:40:57 -0700 | [diff] [blame] | 184 | return NULL; |
| 185 | } |
Jakub Czapiga | 63e5427 | 2021-11-15 08:36:07 +0000 | [diff] [blame] | 186 | if (size_inout) |
| 187 | *size_inout = out_size; |
Julius Werner | 2296479 | 2016-05-12 14:40:57 -0700 | [diff] [blame] | 188 | |
Jakub Czapiga | 63e5427 | 2021-11-15 08:36:07 +0000 | [diff] [blame] | 189 | return buf; |
Hung-Te Lin | d01d036 | 2013-01-25 12:42:40 +0800 | [diff] [blame] | 190 | } |
Jakub Czapiga | ad45f68 | 2022-01-03 15:06:21 +0000 | [diff] [blame] | 191 | |
| 192 | void *_cbfs_load(const char *name, void *buf, size_t *size_inout, bool force_ro) |
| 193 | { |
| 194 | ssize_t offset; |
| 195 | union cbfs_mdata mdata; |
| 196 | |
| 197 | DEBUG("%s(name='%s', buf=%p, force_ro=%s)\n", __func__, name, buf, |
| 198 | force_ro ? "true" : "false"); |
| 199 | |
| 200 | offset = _cbfs_boot_lookup(name, force_ro, &mdata); |
| 201 | if (offset < 0) |
| 202 | return NULL; |
| 203 | |
| 204 | return do_load(&mdata, offset, buf, size_inout, false); |
| 205 | } |
| 206 | |
| 207 | void *_cbfs_unverified_area_load(const char *area, const char *name, void *buf, |
| 208 | size_t *size_inout) |
| 209 | { |
| 210 | struct cbfs_dev dev; |
| 211 | union cbfs_mdata mdata; |
| 212 | size_t data_offset; |
| 213 | |
| 214 | DEBUG("%s(area='%s', name='%s', buf=%p)\n", __func__, area, name, buf); |
| 215 | |
| 216 | if (fmap_locate_area(area, &dev.offset, &dev.size) != CB_SUCCESS) |
| 217 | return NULL; |
| 218 | |
| 219 | if (cbfs_lookup(&dev, name, &mdata, &data_offset, NULL)) { |
| 220 | ERROR("'%s' not found in '%s'\n", name, area); |
| 221 | return NULL; |
| 222 | } |
| 223 | |
| 224 | return do_load(&mdata, dev.offset + data_offset, buf, size_inout, true); |
| 225 | } |