fmap.c
1 /* SPDX-License-Identifier: GPL-2.0-only */ 2 3 #include <boot_device.h> 4 #include <cbmem.h> 5 #include <console/console.h> 6 #include <fmap.h> 7 #include <metadata_hash.h> 8 #include <stddef.h> 9 #include <string.h> 10 #include <symbols.h> 11 #include <endian.h> 12 13 #include "fmap_config.h" 14 15 /* 16 * See http://code.google.com/p/flashmap/ for more information on FMAP. 17 */ 18 19 static int fmap_print_once; 20 static struct region_device fmap_cache; 21 22 #define print_once(...) do { \ 23 if (!fmap_print_once) \ 24 printk(__VA_ARGS__); \ 25 } while (0) 26 27 uint64_t get_fmap_flash_offset(void) 28 { 29 return FMAP_OFFSET; 30 } 31 32 static int verify_fmap(const struct fmap *fmap) 33 { 34 if (memcmp(fmap->signature, FMAP_SIGNATURE, sizeof(fmap->signature))) { 35 if (ENV_INITIAL_STAGE) 36 printk(BIOS_ERR, "Invalid FMAP at %#x\n", FMAP_OFFSET); 37 return -1; 38 } 39 40 static bool done = false; 41 if (!CONFIG(CBFS_VERIFICATION) || !ENV_INITIAL_STAGE || done) 42 return 0; /* Only need to check hash in first stage. */ 43 44 /* On error we need to die right here, lest we risk a TOCTOU attack where the cache is 45 filled with a tampered FMAP but the later fallback path is fed a valid one. */ 46 if (metadata_hash_verify_fmap(fmap, FMAP_SIZE) != VB2_SUCCESS) 47 die("FMAP verification failure"); 48 49 done = true; 50 return 0; 51 } 52 53 static void report(const struct fmap *fmap) 54 { 55 print_once(BIOS_DEBUG, "FMAP: Found \"%s\" version %d.%d at %#x.\n", 56 fmap->name, fmap->ver_major, fmap->ver_minor, FMAP_OFFSET); 57 print_once(BIOS_DEBUG, "FMAP: base = %#llx size = %#x #areas = %d\n", 58 (long long)le64toh(fmap->base), le32toh(fmap->size), 59 le16toh(fmap->nareas)); 60 fmap_print_once = 1; 61 } 62 63 static void setup_preram_cache(struct region_device *cache_rdev) 64 { 65 if (CONFIG(NO_FMAP_CACHE)) 66 return; 67 68 /* No need to use FMAP cache in SMM */ 69 if (ENV_SMM) 70 return; 71 72 if (!ENV_ROMSTAGE_OR_BEFORE) { 73 /* We get here if ramstage makes an FMAP access before calling 74 cbmem_initialize(). We should avoid letting it come to that, 75 so print a warning. */ 76 print_once(BIOS_WARNING, 77 "WARNING: Post-RAM FMAP access too early for cache!\n"); 78 return; 79 } 80 81 struct fmap *fmap = (struct fmap *)_fmap_cache; 82 if (!(ENV_INITIAL_STAGE)) { 83 /* NOTE: This assumes that the first stage will make 84 at least one FMAP access (usually from finding CBFS). */ 85 if (!verify_fmap(fmap)) 86 goto register_cache; 87 88 /* This shouldn't happen, so no point providing a fallback path here. */ 89 die("FMAP cache corrupted?!\n"); 90 } 91 92 /* In case we fail below, make sure the cache is invalid. */ 93 memset(fmap->signature, 0, sizeof(fmap->signature)); 94 95 boot_device_init(); 96 const struct region_device *boot_rdev = boot_device_ro(); 97 if (!boot_rdev) 98 return; 99 100 /* memlayout statically guarantees that the FMAP_CACHE is big enough. */ 101 if (rdev_readat(boot_rdev, fmap, FMAP_OFFSET, FMAP_SIZE) != FMAP_SIZE) 102 return; 103 if (verify_fmap(fmap)) 104 return; 105 report(fmap); 106 107 register_cache: 108 rdev_chain_mem(cache_rdev, fmap, FMAP_SIZE); 109 } 110 111 static int find_fmap_directory(struct region_device *fmrd) 112 { 113 const struct region_device *boot; 114 struct fmap *fmap; 115 size_t offset = FMAP_OFFSET; 116 117 /* Try FMAP cache first */ 118 if (!region_device_sz(&fmap_cache)) 119 setup_preram_cache(&fmap_cache); 120 if (region_device_sz(&fmap_cache)) 121 return rdev_chain_full(fmrd, &fmap_cache); 122 123 /* Cache setup in pre-RAM stages can't fail, unless flash I/O in general failed. */ 124 if (!CONFIG(NO_FMAP_CACHE) && ENV_ROMSTAGE_OR_BEFORE) 125 return -1; 126 127 boot_device_init(); 128 boot = boot_device_ro(); 129 130 if (boot == NULL) 131 return -1; 132 133 fmap = rdev_mmap(boot, offset, 134 CONFIG(CBFS_VERIFICATION) ? FMAP_SIZE : sizeof(struct fmap)); 135 136 if (fmap == NULL) 137 return -1; 138 139 if (verify_fmap(fmap)) { 140 rdev_munmap(boot, fmap); 141 return -1; 142 } 143 144 report(fmap); 145 146 rdev_munmap(boot, fmap); 147 148 return rdev_chain(fmrd, boot, offset, FMAP_SIZE); 149 } 150 151 int fmap_locate_area_as_rdev(const char *name, struct region_device *area) 152 { 153 struct region ar; 154 155 if (fmap_locate_area(name, &ar)) 156 return -1; 157 158 return boot_device_ro_subregion(&ar, area); 159 } 160 161 int fmap_locate_area_as_rdev_rw(const char *name, struct region_device *area) 162 { 163 struct region ar; 164 165 if (fmap_locate_area(name, &ar)) 166 return -1; 167 168 return boot_device_rw_subregion(&ar, area); 169 } 170 171 int fmap_locate_area(const char *name, struct region *ar) 172 { 173 struct region_device fmrd; 174 size_t offset; 175 176 if (name == NULL || ar == NULL) 177 return -1; 178 179 if (find_fmap_directory(&fmrd)) 180 return -1; 181 182 /* Start reading the areas just after fmap header. */ 183 offset = sizeof(struct fmap); 184 185 while (1) { 186 struct fmap_area *area; 187 188 area = rdev_mmap(&fmrd, offset, sizeof(*area)); 189 190 if (area == NULL) 191 return -1; 192 193 if (strcmp((const char *)area->name, name)) { 194 rdev_munmap(&fmrd, area); 195 offset += sizeof(struct fmap_area); 196 continue; 197 } 198 199 printk(BIOS_DEBUG, "FMAP: area %s found @ %x (%d bytes)\n", 200 name, le32toh(area->offset), le32toh(area->size)); 201 202 *ar = region_create(le32toh(area->offset), le32toh(area->size)); 203 204 rdev_munmap(&fmrd, area); 205 206 return 0; 207 } 208 209 printk(BIOS_DEBUG, "FMAP: area %s not found\n", name); 210 211 return -1; 212 } 213 214 int fmap_find_region_name(const struct region * const ar, 215 char name[FMAP_STRLEN]) 216 { 217 struct region_device fmrd; 218 size_t offset; 219 220 if (name == NULL || ar == NULL) 221 return -1; 222 223 if (find_fmap_directory(&fmrd)) 224 return -1; 225 226 /* Start reading the areas just after fmap header. */ 227 offset = sizeof(struct fmap); 228 229 while (1) { 230 struct fmap_area *area; 231 232 area = rdev_mmap(&fmrd, offset, sizeof(*area)); 233 234 if (area == NULL) 235 return -1; 236 237 if (region_offset(ar) != le32toh(area->offset) || 238 region_sz(ar) != le32toh(area->size)) { 239 rdev_munmap(&fmrd, area); 240 offset += sizeof(struct fmap_area); 241 continue; 242 } 243 244 printk(BIOS_DEBUG, "FMAP: area (%zx, %zx) found, named %s\n", 245 region_offset(ar), region_sz(ar), area->name); 246 247 memcpy(name, area->name, FMAP_STRLEN); 248 249 rdev_munmap(&fmrd, area); 250 251 return 0; 252 } 253 254 printk(BIOS_DEBUG, "FMAP: area (%zx, %zx) not found\n", 255 region_offset(ar), region_sz(ar)); 256 257 return -1; 258 } 259 260 ssize_t fmap_read_area(const char *name, void *buffer, size_t size) 261 { 262 struct region_device rdev; 263 if (fmap_locate_area_as_rdev(name, &rdev)) 264 return -1; 265 return rdev_readat(&rdev, buffer, 0, 266 MIN(size, region_device_sz(&rdev))); 267 } 268 269 ssize_t fmap_overwrite_area(const char *name, const void *buffer, size_t size) 270 { 271 struct region_device rdev; 272 273 if (fmap_locate_area_as_rdev_rw(name, &rdev)) 274 return -1; 275 if (size > region_device_sz(&rdev)) 276 return -1; 277 if (rdev_eraseat(&rdev, 0, region_device_sz(&rdev)) < 0) 278 return -1; 279 return rdev_writeat(&rdev, buffer, 0, size); 280 } 281 282 static void fmap_register_cbmem_cache(void) 283 { 284 const struct cbmem_entry *e; 285 286 /* Find the FMAP cache installed by previous stage */ 287 e = cbmem_entry_find(CBMEM_ID_FMAP); 288 /* Don't set fmap_cache so that find_fmap_directory will use regular path */ 289 if (!e) 290 return; 291 292 rdev_chain_mem(&fmap_cache, cbmem_entry_start(e), cbmem_entry_size(e)); 293 } 294 295 /* 296 * The main reason to copy the FMAP into CBMEM is to make it available to the 297 * OS on every architecture. As side effect use the CBMEM copy as cache. 298 */ 299 static void fmap_add_cbmem_cache(void) 300 { 301 struct region_device fmrd; 302 303 if (find_fmap_directory(&fmrd)) 304 return; 305 306 /* Reloads the FMAP even on ACPI S3 resume */ 307 const size_t s = region_device_sz(&fmrd); 308 struct fmap *fmap = cbmem_add(CBMEM_ID_FMAP, s); 309 if (!fmap) { 310 printk(BIOS_ERR, "Failed to allocate CBMEM\n"); 311 return; 312 } 313 314 const ssize_t ret = rdev_readat(&fmrd, fmap, 0, s); 315 if (ret != s) { 316 printk(BIOS_ERR, "Failed to read FMAP into CBMEM\n"); 317 cbmem_entry_remove(cbmem_entry_find(CBMEM_ID_FMAP)); 318 return; 319 } 320 } 321 322 static void fmap_setup_cbmem_cache(int unused) 323 { 324 if (ENV_CREATES_CBMEM) 325 fmap_add_cbmem_cache(); 326 327 /* Finally advertise the cache for the current stage */ 328 fmap_register_cbmem_cache(); 329 } 330 331 CBMEM_READY_HOOK(fmap_setup_cbmem_cache);