Hello Aaron Durbin,
I'd like you to do a code review. Please visit
https://review.coreboot.org/c/coreboot/+/38423
to review the following change.
Change subject: cbfs: Add metadata cache ......................................................................
cbfs: Add metadata cache
This patch adds a new CBFS "mcache" (metadata cache) -- a memory buffer that stores the headers of all CBFS files. Similar to the existing FMAP cache, this cache should reduce the amount of SPI accesses we need to do every boot: rather than having to re-read all CBFS headers from SPI flash every time we're looking for a file, we can just walk the same list in this in-memory copy and finally use it to directly access the flash at the right position for the file data.
This patch adds the code to support the cache but doesn't enable it on any platform. The next one will turn it on by default.
Change-Id: I5b1084bfdad1c6ab0ee1b143ed8dd796827f4c65 Signed-off-by: Julius Werner jwerner@chromium.org --- M src/commonlib/Makefile.inc A src/commonlib/bsd/cbfs_mcache.c M src/commonlib/bsd/include/commonlib/bsd/cb_err.h M src/commonlib/bsd/include/commonlib/bsd/cbfs_private.h M src/commonlib/include/commonlib/cbmem_id.h M src/include/cbfs.h M src/include/memlayout.h M src/include/symbols.h M src/lib/Kconfig M src/lib/cbfs.c M src/lib/coreboot_table.c M src/security/vboot/vboot_common.h M src/security/vboot/vboot_loader.c 13 files changed, 346 insertions(+), 31 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/23/38423/1
diff --git a/src/commonlib/Makefile.inc b/src/commonlib/Makefile.inc index b2225cb..1a38e4a 100644 --- a/src/commonlib/Makefile.inc +++ b/src/commonlib/Makefile.inc @@ -37,6 +37,13 @@ ramstage-y += bsd/cbfs_private.c smm-y += bsd/cbfs_private.c
+bootblock-y += bsd/cbfs_mcache.c +verstage-y += bsd/cbfs_mcache.c +romstage-y += bsd/cbfs_mcache.c +postcar-y += bsd/cbfs_mcache.c +ramstage-y += bsd/cbfs_mcache.c +smm-y += bsd/cbfs_mcache.c + decompressor-y += bsd/lz4_wrapper.c bootblock-y += bsd/lz4_wrapper.c verstage-y += bsd/lz4_wrapper.c diff --git a/src/commonlib/bsd/cbfs_mcache.c b/src/commonlib/bsd/cbfs_mcache.c new file mode 100644 index 0000000..d97dcce --- /dev/null +++ b/src/commonlib/bsd/cbfs_mcache.c @@ -0,0 +1,147 @@ +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later */ + +#include <commonlib/bsd/cbfs_private.h> + +#define MCACHE_TOKEN_MAGIC 0xd +#define MCACHE_OFFSET_FULL 0 +#define MCACHE_OFFSET_END 1 + +struct mcache_token { + uint32_t offset : 28; + uint32_t magic : 4; +}; + +struct cbfs_mcache_build_args { + void *mcache; + void *end; + size_t offset; + int count; +}; + +static void make_token(struct cbfs_mcache_build_args *args, size_t new_offset) +{ + struct mcache_token *token = args->mcache; + + /* If there's no space for the token, lookup() will know the cache is full anyway. */ + if (args->end - args->mcache < sizeof(*token)) + return; + + token->magic = MCACHE_TOKEN_MAGIC; + token->offset = new_offset; + + args->mcache += sizeof(*token); +} + +static cb_err_t build_walker(cbfs_dev_t dev, size_t offset, union cbfs_mdata *mdata, + bool do_hash, void *arg) +{ + struct cbfs_mcache_build_args *args = arg; + const uint32_t data_offset = be32toh(mdata->h.offset); + const uint32_t data_length = be32toh(mdata->h.len); + + if (offset != args->offset) + make_token(args, offset); + + if (args->end - args->mcache < data_offset) { + make_token(args, MCACHE_OFFSET_FULL); + return CB_CBFS_CACHE_FULL; + } + + if (cbfs_copy_fill_metadata(args->mcache, mdata, dev, offset, do_hash) != CB_SUCCESS) + return CB_CBFS_IO; + + args->mcache += data_offset; + args->offset = ALIGN_UP(offset + data_offset + data_length, CBFS_ALIGNMENT); + args->count++; + + return CB_CBFS_NOT_FOUND; +} + +cb_err_t cbfs_mcache_build(cbfs_dev_t dev, void *mcache, size_t size, + struct vb2_hash *master_hash) +{ + struct cbfs_mcache_build_args args = { + .mcache = mcache, + .end = mcache + size, + .offset = 0, + .count = 0, + }; + + cb_err_t ret = cbfs_walk(dev, build_walker, &args, master_hash, 0); + if (ret == CB_CBFS_CACHE_FULL) + ERROR("mcache overflow, should increase CBFS_MCACHE size!\n"); + else if (ret != CB_CBFS_NOT_FOUND) + return ret; + + make_token(&args, MCACHE_OFFSET_END); + LOG("mcache @%p built for %d files, used %#zx bytes\n", + mcache, args.count, args.mcache - mcache); + return CB_SUCCESS; +} + +cb_err_t cbfs_mcache_lookup(cbfs_dev_t dev, void *mcache, size_t mcache_size, const char *name, + union cbfs_mdata *mdata_out, size_t *data_offset_out, + struct vb2_hash *master_hash) +{ + size_t namesize = strlen(name) + 1; /* Count trailing \0 so we can memcmp() it. */ + void *end = mcache + mcache_size; + void *current = mcache; + + size_t offset = 0; + while (end - current >= sizeof(struct mcache_token)) { + union cbfs_mdata *mdata = current; + + if (memcmp(mdata->h.magic, CBFS_FILE_MAGIC, sizeof(mdata->h.magic)) != 0) { + struct mcache_token *token = current; + if (token->magic != MCACHE_TOKEN_MAGIC) { + ERROR("CBFS mcache corrupted at %p\n", token); + return CB_ERR; + } + if (token->offset == MCACHE_OFFSET_END) + return CB_CBFS_NOT_FOUND; + if (token->offset == MCACHE_OFFSET_FULL) + break; + offset = token->offset; + current += sizeof(*token); + continue; + } + + const uint32_t data_offset = be32toh(mdata->h.offset); + const uint32_t data_length = be32toh(mdata->h.len); + if (namesize <= cbfs_filename_size(&mdata->h) && + memcmp(name, mdata->filename, namesize) == 0) { + LOG("Found '%s' @%#zx size %#x in mcache @%p\n", + name, offset, data_length, current); + *data_offset_out = offset + data_offset; + memcpy(mdata_out, mdata, data_offset); + return CB_SUCCESS; + } + + offset = ALIGN_UP(offset + data_offset + data_length, CBFS_ALIGNMENT); + current += data_offset; + } + + return cbfs_lookup(dev, name, mdata_out, data_offset_out, master_hash); +} + +size_t cbfs_mcache_real_size(void *mcache, size_t mcache_size) +{ + void *end = mcache + mcache_size; + void *current = mcache; + + while (end - current >= sizeof(struct mcache_token)) { + union cbfs_mdata *mdata = current; + + if (memcmp(mdata->h.magic, CBFS_FILE_MAGIC, sizeof(mdata->h.magic)) != 0) { + struct mcache_token *token = current; + current += sizeof(*token); + if (token->offset == MCACHE_OFFSET_END || + token->offset == MCACHE_OFFSET_FULL) + break; + } else { + current += be32toh(mdata->h.offset); + } + } + + return current - mcache; +} diff --git a/src/commonlib/bsd/include/commonlib/bsd/cb_err.h b/src/commonlib/bsd/include/commonlib/bsd/cb_err.h index ab422e1..18aa4d7 100644 --- a/src/commonlib/bsd/include/commonlib/bsd/cb_err.h +++ b/src/commonlib/bsd/include/commonlib/bsd/cb_err.h @@ -39,6 +39,7 @@ CB_CBFS_IO = -400, /**< Underlying I/O error */ CB_CBFS_NOT_FOUND = -401, /**< File not found in directory */ CB_CBFS_HASH = -402, /**< Master hash validation failed */ + CB_CBFS_CACHE_FULL = -403, /**< Metadata cache overflowed */ };
/* Don't typedef the enum directly, so the size is unambiguous for serialization. */ diff --git a/src/commonlib/bsd/include/commonlib/bsd/cbfs_private.h b/src/commonlib/bsd/include/commonlib/bsd/cbfs_private.h index b4fc1c7..374a460 100644 --- a/src/commonlib/bsd/include/commonlib/bsd/cbfs_private.h +++ b/src/commonlib/bsd/include/commonlib/bsd/cbfs_private.h @@ -92,4 +92,22 @@ cb_err_t cbfs_lookup(cbfs_dev_t dev, const char *name, union cbfs_mdata *mdata_out, size_t *data_offset_out, struct vb2_hash *master_hash);
+/* Build an in-memory CBFS metadata cache out of the CBFS on |dev| into a |mcache_size| bytes + * memory area at |mcache|. Also verify |master_hash| unless it is NULL. */ +cb_err_t cbfs_mcache_build(cbfs_dev_t dev, void *mcache, size_t mcache_size, + struct vb2_hash *master_hash); + +/* + * Find a file named |name| in a CBFS metadata cache and copy its metadata into |mdata_out|. + * Pass out offset to the file data (on the storage backend). May fall back to cbfs_lookup() if + * metadata cache was too small for CBFS. |dev| and |master_hash| must have the same values as + * originally passed to cbfs_mcache_build(). + */ +cb_err_t cbfs_mcache_lookup(cbfs_dev_t dev, void *mcache, size_t mcache_size, const char *name, + union cbfs_mdata *mdata_out, size_t *data_offset_out, + struct vb2_hash *master_hash); + +/* Returns the amount of bytes actually used by the CBFS metadata cache in |mcache|. */ +size_t cbfs_mcache_real_size(void *mcache, size_t mcache_size); + #endif /* _COMMONLIB_BSD_CBFS_PRIVATE_H_ */ diff --git a/src/commonlib/include/commonlib/cbmem_id.h b/src/commonlib/include/commonlib/cbmem_id.h index b063cd1..8f4f7f3 100644 --- a/src/commonlib/include/commonlib/cbmem_id.h +++ b/src/commonlib/include/commonlib/cbmem_id.h @@ -79,6 +79,8 @@ #define CBMEM_ID_ROM2 0x524f4d32 #define CBMEM_ID_ROM3 0x524f4d33 #define CBMEM_ID_FMAP 0x464d4150 +#define CBMEM_ID_CBFS_RO_MCACHE 0x524d5346 +#define CBMEM_ID_CBFS_RW_MCACHE 0x574d5346 #define CBMEM_ID_FSP_LOGO 0x4c4f474f
#define CBMEM_ID_TO_NAME_TABLE \ @@ -141,5 +143,7 @@ { CBMEM_ID_ROM1, "VGA ROM #1 "}, \ { CBMEM_ID_ROM2, "VGA ROM #2 "}, \ { CBMEM_ID_ROM3, "VGA ROM #3 "}, \ - { CBMEM_ID_FMAP, "FMAP "}, + { CBMEM_ID_FMAP, "FMAP "}, \ + { CBMEM_ID_CBFS_RO_MCACHE, "RO MCACHE "}, \ + { CBMEM_ID_CBFS_RW_MCACHE, "RW MCACHE "} #endif /* _CBMEM_ID_H_ */ diff --git a/src/include/cbfs.h b/src/include/cbfs.h index 2d16aa7..82c92d7 100644 --- a/src/include/cbfs.h +++ b/src/include/cbfs.h @@ -18,6 +18,7 @@
#include <commonlib/cbfs.h> #include <program_loading.h> +#include <types.h>
/*********************************************** * Perform CBFS operations on the boot device. * @@ -59,8 +60,18 @@ /* Load stage into memory filling in prog. Return 0 on success. < 0 on error. */ int cbfs_prog_stage_load(struct prog *prog);
-/* Returns the region device of the currently active CBFS. - Return < 0 on error, 0 on success. */ -int cbfs_boot_region_device(struct region_device *rdev); +struct cbfs_boot_device { + struct region_device rdev; + void *mcache; + size_t mcache_size; +}; + +/* + * Retrieves the currently active CBFS boot device. If |force_ro| is set, will + * always return the read-only CBFS instead (this only makes a difference when + * CONFIG_VBOOT is enabled). May perform certain CBFS initialization tasks. + * Returns NULL on error (e.g. boot device IO error). + */ +const struct cbfs_boot_device *cbfs_get_boot_device(bool force_ro);
#endif diff --git a/src/include/memlayout.h b/src/include/memlayout.h index e3aeec6..0d430dee 100644 --- a/src/include/memlayout.h +++ b/src/include/memlayout.h @@ -81,6 +81,9 @@ _ = ASSERT(sz >= FMAP_SIZE, \ STR(FMAP does not fit in FMAP_CACHE! (sz < FMAP_SIZE)));
+#define CBFS_MCACHE(addr, sz) \ + REGION(cbfs_mcache, addr, sz, 8) + #if ENV_ROMSTAGE_OR_BEFORE #define PRERAM_CBFS_CACHE(addr, size) \ REGION(preram_cbfs_cache, addr, size, 4) \ diff --git a/src/include/symbols.h b/src/include/symbols.h index eec4701..a8e1a82 100644 --- a/src/include/symbols.h +++ b/src/include/symbols.h @@ -34,6 +34,7 @@ DECLARE_REGION(preram_cbfs_cache) DECLARE_REGION(postram_cbfs_cache) DECLARE_REGION(cbfs_cache) +DECLARE_REGION(cbfs_mcache) DECLARE_REGION(fmap_cache) DECLARE_REGION(payload)
diff --git a/src/lib/Kconfig b/src/lib/Kconfig index dd9974a..26bf0be 100644 --- a/src/lib/Kconfig +++ b/src/lib/Kconfig @@ -75,3 +75,24 @@ If your platform really doesn't want to use an FMAP cache (e.g. due to space constraints), you can select this to disable warnings and save a bit more code. + +config NO_CBFS_MCACHE + bool + default y + help + Disables the CBFS metadata cache. This means that your platform does + not need to provide a CBFS_MCACHE section in memlayout and can save + the associated CAR/SRAM size. In that case every single CBFS file + lookup must re-read the same CBFS directory entries from flash to find + the respective file. + +config CBFS_MCACHE_RW_PERCENTAGE + int + depends on VBOOT + default 25 if CHROMEOS # Chrome OS stores many L10n files in RO only + default 50 + help + The amount of the CBFS_MCACHE area that's used for the RW CBFS, in + percent from 0 to 100. The remaining area will be used for the RO + CBFS. Default is an even 50/50 split. When VBOOT is disabled, this + will automatically be 0 (meaning the whole MCACHE is used for RO). diff --git a/src/lib/cbfs.c b/src/lib/cbfs.c index d6fb99c..40290fd 100644 --- a/src/lib/cbfs.c +++ b/src/lib/cbfs.c @@ -15,6 +15,7 @@ */
#include <assert.h> +#include <cbmem.h> #include <console/console.h> #include <string.h> #include <stdlib.h> @@ -30,30 +31,40 @@ #include <security/vboot/vboot_crtm.h> #include <security/vboot/vboot_common.h>
+static cb_err_t cbfs_boot_lookup(const struct cbfs_boot_device *cbd, + const char *name, union cbfs_mdata *mdata, size_t *data_offset) +{ + if (CONFIG(NO_CBFS_MCACHE)) + return cbfs_lookup(&cbd->rdev, name, mdata, data_offset, NULL); + else + return cbfs_mcache_lookup(&cbd->rdev, + cbd->mcache, cbd->mcache_size, + name, mdata, data_offset, NULL); +} + int cbfs_boot_locate(struct cbfsf *fh, const char *name, uint32_t *type) { - struct region_device rdev; - - if (cbfs_boot_region_device(&rdev)) + const struct cbfs_boot_device *cbd = cbfs_get_boot_device(false); + if (!cbd) return -1;
union cbfs_mdata mdata; size_t data_offset; - cb_err_t err = cbfs_lookup(&rdev, name, &mdata, &data_offset, NULL); + cb_err_t err = cbfs_boot_lookup(cbd, name, &mdata, &data_offset);
if (CONFIG(VBOOT_ENABLE_CBFS_FALLBACK) && err == CB_CBFS_NOT_FOUND) { printk(BIOS_INFO, "CBFS: Fall back to RO region for %s\n", name); - if (fmap_locate_area_as_rdev("COREBOOT", &rdev)) + if (!(cbd = cbfs_get_boot_device(true))) return -1; - err = cbfs_lookup(&rdev, name, &mdata, &data_offset, NULL); + err = cbfs_boot_lookup(cbd, name, &mdata, &data_offset); } if (err) return -1;
size_t msize = be32toh(mdata.h.offset); - if (rdev_chain(&fh->metadata, &rdev, data_offset - msize, msize) || - rdev_chain(&fh->data, &rdev, data_offset, be32toh(mdata.h.len))) + if (rdev_chain(&fh->metadata, &cbd->rdev, data_offset - msize, msize) || + rdev_chain(&fh->data, &cbd->rdev, data_offset, be32toh(mdata.h.len))) return -1; if (type) { if (!*type) @@ -280,9 +291,66 @@ return 0; }
-int cbfs_boot_region_device(struct region_device *rdev) +const struct cbfs_boot_device *cbfs_get_boot_device(bool force_ro) { + static struct cbfs_boot_device ro; + + if (!force_ro) { + const struct cbfs_boot_device *rw = vboot_get_cbfs_boot_device(); + if (rw) + return rw; + } + + if (region_device_sz(&ro.rdev)) + return &ro; + + if (!CONFIG(NO_CBFS_MCACHE)) { + const struct cbmem_entry *entry; + if (cbmem_possibly_online() && + (entry = cbmem_entry_find(CBMEM_ID_CBFS_RO_MCACHE))) { + ro.mcache = cbmem_entry_start(entry); + ro.mcache_size = cbmem_entry_size(entry); + } else if (ENV_ROMSTAGE_OR_BEFORE) { + ro.mcache = _cbfs_mcache; + ro.mcache_size = REGION_SIZE(cbfs_mcache) * + (100 - CONFIG_CBFS_MCACHE_RW_PERCENTAGE) / 100; + } + } + boot_device_init(); - return vboot_locate_cbfs(rdev) && - fmap_locate_area_as_rdev("COREBOOT", rdev); + if (fmap_locate_area_as_rdev("COREBOOT", &ro.rdev)) + return NULL; + + if (!CONFIG(NO_CBFS_MCACHE) && ENV_BOOTBLOCK) { + static bool mcache_done = false; + if (!mcache_done && ro.mcache_size > 0 && cbfs_mcache_build( + &ro.rdev, ro.mcache, ro.mcache_size, NULL)) + return NULL; + mcache_done = true; + } + return &ro; } + +#if !CONFIG(NO_CBFS_MCACHE) +static void mcache_to_cbmem(const struct cbfs_boot_device *cbd, u32 cbmem_id) +{ + if (!cbd) + return; + + size_t real_size = cbfs_mcache_real_size(cbd->mcache, cbd->mcache_size); + void *cbmem_mcache = cbmem_add(cbmem_id, real_size); + if (!cbmem_mcache) { + printk(BIOS_ERR, "ERROR: Cannot allocate CBMEM mcache %#x (%#zx bytes)!\n", + cbmem_id, real_size); + return; + } + memcpy(cbmem_mcache, cbd->mcache, real_size); +} + +static void cbfs_mcache_migrate(int unused) +{ + mcache_to_cbmem(vboot_get_cbfs_boot_device(), CBMEM_ID_CBFS_RW_MCACHE); + mcache_to_cbmem(cbfs_get_boot_device(true), CBMEM_ID_CBFS_RO_MCACHE); +} +ROMSTAGE_CBMEM_INIT_HOOK(cbfs_mcache_migrate) +#endif diff --git a/src/lib/coreboot_table.c b/src/lib/coreboot_table.c index e42cb3b..b46ca57 100644 --- a/src/lib/coreboot_table.c +++ b/src/lib/coreboot_table.c @@ -243,11 +243,8 @@ { struct lb_boot_media_params *bmp; const struct region_device *boot_dev; - struct region_device cbfs_dev; - - boot_device_init(); - - if (cbfs_boot_region_device(&cbfs_dev)) + const struct cbfs_boot_device *cbd = cbfs_get_boot_device(false); + if (!cbd) return;
boot_dev = boot_device_ro(); @@ -258,8 +255,8 @@ bmp->tag = LB_TAG_BOOT_MEDIA_PARAMS; bmp->size = sizeof(*bmp);
- bmp->cbfs_offset = region_device_offset(&cbfs_dev); - bmp->cbfs_size = region_device_sz(&cbfs_dev); + bmp->cbfs_offset = region_device_offset(&cbd->rdev); + bmp->cbfs_size = region_device_sz(&cbd->rdev); bmp->boot_media_size = region_device_sz(boot_dev);
bmp->fmap_offset = get_fmap_flash_offset(); diff --git a/src/security/vboot/vboot_common.h b/src/security/vboot/vboot_common.h index 976c26a..0ebefb6 100644 --- a/src/security/vboot/vboot_common.h +++ b/src/security/vboot/vboot_common.h @@ -16,6 +16,7 @@ #define __VBOOT_VBOOT_COMMON_H__
#include <commonlib/region.h> +#include <cbfs.h> #include <stdint.h> #include <vboot_api.h> #include <vboot_struct.h> @@ -71,7 +72,7 @@ int vboot_recovery_mode_memory_retrain(void); int vboot_can_enable_udc(void); void vboot_run_logic(void); -int vboot_locate_cbfs(struct region_device *rdev); +const struct cbfs_boot_device *vboot_get_cbfs_boot_device(void); #else /* !CONFIG_VBOOT */ static inline int vboot_developer_mode_enabled(void) { return 0; } static inline int vboot_recovery_mode_enabled(void) { return 0; } @@ -79,7 +80,7 @@ /* If VBOOT is not enabled, we are okay enabling USB device controller (UDC). */ static inline int vboot_can_enable_udc(void) { return 1; } static inline void vboot_run_logic(void) {} -static inline int vboot_locate_cbfs(struct region_device *rdev) { return -1; } +const struct cbfs_boot_device *vboot_get_cbfs_boot_device(void) { return NULL; } #endif
void vboot_save_nvdata_only(struct vb2_context *ctx); diff --git a/src/security/vboot/vboot_loader.c b/src/security/vboot/vboot_loader.c index 7e63775..a8db7a3 100644 --- a/src/security/vboot/vboot_loader.c +++ b/src/security/vboot/vboot_loader.c @@ -13,7 +13,10 @@ * GNU General Public License for more details. */
+#include <boot_device.h> #include <cbfs.h> +#include <cbmem.h> +#include <commonlib/bsd/cbfs_private.h> #include <console/console.h> #include <ec/google/chromeec/ec.h> #include <rmodule.h> @@ -34,12 +37,26 @@
int vboot_executed;
+static void build_rw_mcache(void) +{ + if (CONFIG(NO_CBFS_MCACHE)) + return; + + if (vboot_get_context()->flags & VB2_CONTEXT_RECOVERY_MODE) + return; /* No RW mcache if we're staying in RO. */ + + const struct cbfs_boot_device *cbd = vboot_get_cbfs_boot_device(); + if (cbfs_mcache_build(&cbd->rdev, cbd->mcache, cbd->mcache_size, NULL)) + die("Failed to build RW mcache."); /* TODO: -> recovery? */ +} + void vboot_run_logic(void) { if (verification_should_run()) { /* Note: this path is not used for VBOOT_RETURN_FROM_VERSTAGE */ verstage_main(); vboot_executed = 1; + build_rw_mcache(); } else if (verstage_should_load()) { struct cbfsf file; struct prog verstage = @@ -67,21 +84,40 @@ return;
vboot_executed = 1; + build_rw_mcache(); } }
-int vboot_locate_cbfs(struct region_device *rdev) +const struct cbfs_boot_device *vboot_get_cbfs_boot_device(void) { - struct vb2_context *ctx; - /* Don't honor vboot results until the vboot logic has run. */ if (!vboot_logic_executed()) - return -1; + return NULL;
- ctx = vboot_get_context(); + static struct cbfs_boot_device cbd; + if (region_device_sz(&cbd.rdev)) + return &cbd;
+ struct vb2_context *ctx = vboot_get_context(); if (ctx->flags & VB2_CONTEXT_RECOVERY_MODE) - return -1; + return NULL;
- return vboot_locate_firmware(ctx, rdev); + boot_device_init(); + if (vboot_locate_firmware(ctx, &cbd.rdev)) + return NULL; + + if (!CONFIG(NO_CBFS_MCACHE)) { + const struct cbmem_entry *entry; + if (cbmem_possibly_online() && + (entry = cbmem_entry_find(CBMEM_ID_CBFS_RW_MCACHE))) { + cbd.mcache = cbmem_entry_start(entry); + cbd.mcache_size = cbmem_entry_size(entry); + } else if (ENV_ROMSTAGE_OR_BEFORE) { + cbd.mcache_size = REGION_SIZE(cbfs_mcache) * + CONFIG_CBFS_MCACHE_RW_PERCENTAGE / 100; + cbd.mcache = _ecbfs_mcache - cbd.mcache_size; + } + } + + return &cbd; }
build bot (Jenkins) has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 1:
(3 comments)
https://review.coreboot.org/c/coreboot/+/38423/1/src/lib/cbfs.c File src/lib/cbfs.c:
https://review.coreboot.org/c/coreboot/+/38423/1/src/lib/cbfs.c@58 PS1, Line 58: if (!(cbd = cbfs_get_boot_device(true))) do not use assignment in if condition
https://review.coreboot.org/c/coreboot/+/38423/1/src/lib/cbfs.c@309 PS1, Line 309: if (cbmem_possibly_online() && do not use assignment in if condition
https://review.coreboot.org/c/coreboot/+/38423/1/src/security/vboot/vboot_lo... File src/security/vboot/vboot_loader.c:
https://review.coreboot.org/c/coreboot/+/38423/1/src/security/vboot/vboot_lo... PS1, Line 111: if (cbmem_possibly_online() && do not use assignment in if condition
build bot (Jenkins) has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 2:
(3 comments)
https://review.coreboot.org/c/coreboot/+/38423/2/src/lib/cbfs.c File src/lib/cbfs.c:
https://review.coreboot.org/c/coreboot/+/38423/2/src/lib/cbfs.c@58 PS2, Line 58: if (!(cbd = cbfs_get_boot_device(true))) do not use assignment in if condition
https://review.coreboot.org/c/coreboot/+/38423/2/src/lib/cbfs.c@309 PS2, Line 309: if (cbmem_possibly_online() && do not use assignment in if condition
https://review.coreboot.org/c/coreboot/+/38423/2/src/security/vboot/vboot_lo... File src/security/vboot/vboot_loader.c:
https://review.coreboot.org/c/coreboot/+/38423/2/src/security/vboot/vboot_lo... PS2, Line 111: if (cbmem_possibly_online() && do not use assignment in if condition
Hello Aaron Durbin, build bot (Jenkins), Patrick Georgi, Martin Roth,
I'd like you to reexamine a change. Please visit
https://review.coreboot.org/c/coreboot/+/38423
to look at the new patch set (#3).
Change subject: cbfs: Add metadata cache ......................................................................
cbfs: Add metadata cache
This patch adds a new CBFS "mcache" (metadata cache) -- a memory buffer that stores the headers of all CBFS files. Similar to the existing FMAP cache, this cache should reduce the amount of SPI accesses we need to do every boot: rather than having to re-read all CBFS headers from SPI flash every time we're looking for a file, we can just walk the same list in this in-memory copy and finally use it to directly access the flash at the right position for the file data.
This patch adds the code to support the cache but doesn't enable it on any platform. The next one will turn it on by default.
Change-Id: I5b1084bfdad1c6ab0ee1b143ed8dd796827f4c65 Signed-off-by: Julius Werner jwerner@chromium.org --- M src/commonlib/Makefile.inc A src/commonlib/bsd/cbfs_mcache.c M src/commonlib/bsd/include/commonlib/bsd/cb_err.h M src/commonlib/bsd/include/commonlib/bsd/cbfs_private.h M src/commonlib/include/commonlib/cbmem_id.h M src/include/cbfs.h M src/include/memlayout.h M src/include/symbols.h M src/lib/Kconfig M src/lib/cbfs.c M src/lib/coreboot_table.c M src/security/vboot/vboot_common.h M src/security/vboot/vboot_loader.c 13 files changed, 349 insertions(+), 31 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/23/38423/3
build bot (Jenkins) has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 3:
(3 comments)
https://review.coreboot.org/c/coreboot/+/38423/3/src/lib/cbfs.c File src/lib/cbfs.c:
https://review.coreboot.org/c/coreboot/+/38423/3/src/lib/cbfs.c@58 PS3, Line 58: if (!(cbd = cbfs_get_boot_device(true))) do not use assignment in if condition
https://review.coreboot.org/c/coreboot/+/38423/3/src/lib/cbfs.c@309 PS3, Line 309: if (cbmem_possibly_online() && do not use assignment in if condition
https://review.coreboot.org/c/coreboot/+/38423/3/src/security/vboot/vboot_lo... File src/security/vboot/vboot_loader.c:
https://review.coreboot.org/c/coreboot/+/38423/3/src/security/vboot/vboot_lo... PS3, Line 111: if (cbmem_possibly_online() && do not use assignment in if condition
Rob Barnes has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 3:
Is there a bug tracking this work?
Julius Werner has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 3:
Is there a bug tracking this work?
No, but I have a sort of design doc (https://osfc.io/uploads/talk/paper/47/The_future_of_firmware_verification_in..., or docs.google.com/document/d/1mPuTYtz_j-TG9COg7iuwLQp03KZTXLcx-ZtSD1YyKR0 for the commentable version) and a conference talk (https://www.youtube.com/watch?v=Hs_EhewBgtM) about it.
build bot (Jenkins) has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 4:
(3 comments)
https://review.coreboot.org/c/coreboot/+/38423/4/src/lib/cbfs.c File src/lib/cbfs.c:
https://review.coreboot.org/c/coreboot/+/38423/4/src/lib/cbfs.c@58 PS4, Line 58: if (!(cbd = cbfs_get_boot_device(true))) do not use assignment in if condition
https://review.coreboot.org/c/coreboot/+/38423/4/src/lib/cbfs.c@309 PS4, Line 309: if (cbmem_possibly_online() && do not use assignment in if condition
https://review.coreboot.org/c/coreboot/+/38423/4/src/security/vboot/vboot_lo... File src/security/vboot/vboot_loader.c:
https://review.coreboot.org/c/coreboot/+/38423/4/src/security/vboot/vboot_lo... PS4, Line 111: if (cbmem_possibly_online() && do not use assignment in if condition
Aaron Durbin has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 4:
(12 comments)
https://review.coreboot.org/c/coreboot/+/38423/4/src/commonlib/bsd/cbfs_mcac... File src/commonlib/bsd/cbfs_mcache.c:
https://review.coreboot.org/c/coreboot/+/38423/4/src/commonlib/bsd/cbfs_mcac... PS4, Line 8: There should be a format description of this cache.
https://review.coreboot.org/c/coreboot/+/38423/4/src/commonlib/bsd/cbfs_mcac... PS4, Line 26: if (args->end - args->mcache < sizeof(*token)) This seems like we're dangling lots of conditions on the state of teh cache. Why can't we be more explicit that every file entry needs a token as well as needing space for the entry indicating termination of the cbfs? If those things aren't met we have failed entirely -- and propagate that situation up the stack?
https://review.coreboot.org/c/coreboot/+/38423/4/src/commonlib/bsd/cbfs_mcac... PS4, Line 42: if (offset != args->offset) Why is this conditional? I feel like we should always keep a token marker for every file.
https://review.coreboot.org/c/coreboot/+/38423/4/src/commonlib/bsd/cbfs_mcac... PS4, Line 54: args->offset = ALIGN_UP(offset + data_offset + data_length, CBFS_ALIGNMENT); I'm not clear why we would be keeping track of this at all. The walker should just spit offsets out that are consumed and logged.
https://review.coreboot.org/c/coreboot/+/38423/4/src/commonlib/bsd/cbfs_mcac... PS4, Line 72: ERROR("mcache overflow, should increase CBFS_MCACHE size!\n"); This should be a major error.
https://review.coreboot.org/c/coreboot/+/38423/4/src/commonlib/bsd/cbfs_mcac... PS4, Line 123: By default we are falling back to the boot media? Why? Shouldn't we have an error when this occurs? Likewise there isn't a comment nor a console output indicating this explicit case was hit.
https://review.coreboot.org/c/coreboot/+/38423/4/src/commonlib/bsd/cbfs_mcac... PS4, Line 135: if (memcmp(mdata->h.magic, CBFS_FILE_MAGIC, sizeof(mdata->h.magic)) != 0) { This needs some commentary. From what I can tell there's a token if there's no cbfs metadata, correct?
https://review.coreboot.org/c/coreboot/+/38423/4/src/commonlib/bsd/include/c... File src/commonlib/bsd/include/commonlib/bsd/cbfs_private.h:
https://review.coreboot.org/c/coreboot/+/38423/4/src/commonlib/bsd/include/c... PS4, Line 98: struct vb2_hash *master_hash); Why isn't master_hash const?
https://review.coreboot.org/c/coreboot/+/38423/4/src/lib/cbfs.c File src/lib/cbfs.c:
https://review.coreboot.org/c/coreboot/+/38423/4/src/lib/cbfs.c@299 PS4, Line 299: const struct cbfs_boot_device *rw = vboot_get_cbfs_boot_device(); When this returns NULL what semantics is it indicating?
- recovery? - vboot isn't enabled?
What else?
https://review.coreboot.org/c/coreboot/+/38423/4/src/lib/cbfs.c@310 PS4, Line 310: cbmem_entry_find This failing is a broader problem. I don't think we should fall back into ENV_ROMSTAGE_OR_BEFORE case.
https://review.coreboot.org/c/coreboot/+/38423/4/src/lib/cbfs.c@324 PS4, Line 324: ENV_BOOTBLOCK How do you envision this stuff working with separate verstage and NO_RETURN_FROM_VERSTAGE?
https://review.coreboot.org/c/coreboot/+/38423/4/src/security/vboot/vboot_lo... File src/security/vboot/vboot_loader.c:
https://review.coreboot.org/c/coreboot/+/38423/4/src/security/vboot/vboot_lo... PS4, Line 46: return; /* No RW mcache if we're staying in RO. */ Dont' we already have this covered with vboot_get_cbfs_boot_device() returning NULL?
Hello Aaron Durbin, build bot (Jenkins), Patrick Georgi, Martin Roth,
I'd like you to reexamine a change. Please visit
https://review.coreboot.org/c/coreboot/+/38423
to look at the new patch set (#5).
Change subject: cbfs: Add metadata cache ......................................................................
cbfs: Add metadata cache
This patch adds a new CBFS "mcache" (metadata cache) -- a memory buffer that stores the headers of all CBFS files. Similar to the existing FMAP cache, this cache should reduce the amount of SPI accesses we need to do every boot: rather than having to re-read all CBFS headers from SPI flash every time we're looking for a file, we can just walk the same list in this in-memory copy and finally use it to directly access the flash at the right position for the file data.
This patch adds the code to support the cache but doesn't enable it on any platform. The next one will turn it on by default.
Change-Id: I5b1084bfdad1c6ab0ee1b143ed8dd796827f4c65 Signed-off-by: Julius Werner jwerner@chromium.org --- M src/commonlib/Makefile.inc A src/commonlib/bsd/cbfs_mcache.c M src/commonlib/bsd/include/commonlib/bsd/cb_err.h M src/commonlib/bsd/include/commonlib/bsd/cbfs_private.h M src/commonlib/include/commonlib/cbmem_id.h M src/include/cbfs.h M src/include/memlayout.h M src/include/symbols.h M src/lib/Kconfig M src/lib/cbfs.c M src/lib/coreboot_table.c M src/security/vboot/vboot_common.h M src/security/vboot/vboot_loader.c 13 files changed, 350 insertions(+), 32 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/23/38423/5
build bot (Jenkins) has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 5:
(3 comments)
https://review.coreboot.org/c/coreboot/+/38423/5/src/lib/cbfs.c File src/lib/cbfs.c:
https://review.coreboot.org/c/coreboot/+/38423/5/src/lib/cbfs.c@58 PS5, Line 58: if (!(cbd = cbfs_get_boot_device(true))) do not use assignment in if condition
https://review.coreboot.org/c/coreboot/+/38423/5/src/lib/cbfs.c@301 PS5, Line 301: if (cbmem_possibly_online() && do not use assignment in if condition
https://review.coreboot.org/c/coreboot/+/38423/5/src/security/vboot/vboot_lo... File src/security/vboot/vboot_loader.c:
https://review.coreboot.org/c/coreboot/+/38423/5/src/security/vboot/vboot_lo... PS5, Line 110: if (cbmem_possibly_online() && do not use assignment in if condition
Julius Werner has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 5:
(11 comments)
https://review.coreboot.org/c/coreboot/+/38423/4/src/commonlib/bsd/cbfs_mcac... File src/commonlib/bsd/cbfs_mcache.c:
https://review.coreboot.org/c/coreboot/+/38423/4/src/commonlib/bsd/cbfs_mcac... PS4, Line 8:
There should be a format description of this cache.
Yeah, I think I had planned to document this somewhere and must have forgotten. Added a description.
https://review.coreboot.org/c/coreboot/+/38423/4/src/commonlib/bsd/cbfs_mcac... PS4, Line 26: if (args->end - args->mcache < sizeof(*token))
This seems like we're dangling lots of conditions on the state of teh cache. […]
I'm trying to be really thrifty with space in this cache because CBFS headers can be very small and there can be hundreds of files, so every bit of per-file overhead quickly multiplies into a notable percentage. The tokens are really an edge case for unusual situations in this design, for a tightly packed CBFS without any holes you won't need any (other than the terminating one). See the new description I'm adding at the top for details.
I tried writing it so that it would always leave space for the final token at first but what I came up with somehow ended up more complicated than this. You probably want to check that you're not running over the end of the cache when walking it anyway, and at that point you might as well make use of that check by keeping things simple here.
https://review.coreboot.org/c/coreboot/+/38423/4/src/commonlib/bsd/cbfs_mcac... PS4, Line 42: if (offset != args->offset)
Why is this conditional? I feel like we should always keep a token marker for every file.
See above, I feel we might as well save the 4 bytes if we can.
https://review.coreboot.org/c/coreboot/+/38423/4/src/commonlib/bsd/cbfs_mcac... PS4, Line 72: ERROR("mcache overflow, should increase CBFS_MCACHE size!\n");
This should be a major error.
The goal of the design is that things are still usable when the cache is too small. This could particularly be relevant for a vboot RW update -- the RW mcache is (necessarily) built by RO, so if you want to add extra files with your update and end up overflowing the cache size hardcoded in RO you would be SOL without this. With this, you're just making accesses to a few files a little slower. (Note that cbfs_mcache_lookup() still has access to the master_hash for this reason, so the fallback path is still just as secure.)
https://review.coreboot.org/c/coreboot/+/38423/4/src/commonlib/bsd/cbfs_mcac... PS4, Line 123:
By default we are falling back to the boot media? Why? Shouldn't we have an error when this occurs? […]
See above... this is necessary to support the cache full case. (I'm not printing an error again because I already printed one after building the cache, I thought once was enough.)
https://review.coreboot.org/c/coreboot/+/38423/4/src/commonlib/bsd/cbfs_mcac... PS4, Line 135: if (memcmp(mdata->h.magic, CBFS_FILE_MAGIC, sizeof(mdata->h.magic)) != 0) {
This needs some commentary. […]
Yes, this is basically the same walking code as in cbfs_mcache_lookup() above. Every "slot" in the mcache is either a CBFS file header or a token. The new documentation I'm adding to the top of the file will explain this.
https://review.coreboot.org/c/coreboot/+/38423/4/src/commonlib/bsd/include/c... File src/commonlib/bsd/include/commonlib/bsd/cbfs_private.h:
https://review.coreboot.org/c/coreboot/+/38423/4/src/commonlib/bsd/include/c... PS4, Line 98: struct vb2_hash *master_hash);
Why isn't master_hash const?
It's just a consequence of my cbfs_walk() design. I want it (eventually) to be usable by cbfstool for generating the master_hash as well, so I have the CBFS_WALK_WRITEBACK_HASH option. That's why the master_hash argument to cbfs_walk() cannot be const, and since this is passed through to there this cannot be const either (although this function will not set CBFS_WALK_WRITEBACK_HASH and so the master_hash argument here will never be written to). I could also make it const here and cast the const away explicitly in the code but I think it's easier to just leave it like this.
https://review.coreboot.org/c/coreboot/+/38423/4/src/lib/cbfs.c File src/lib/cbfs.c:
https://review.coreboot.org/c/coreboot/+/38423/4/src/lib/cbfs.c@299 PS4, Line 299: const struct cbfs_boot_device *rw = vboot_get_cbfs_boot_device();
When this returns NULL what semantics is it indicating? […]
Those two or that vboot hasn't executed yet. Technically it would also return NULL when vboot_locate_firmware() returns NULL (e.g. FMAP slot not found), but in that case vboot should've rebooted into recovery mode before we get here (or actually, now that I look at it it just does a die() in that case).
https://review.coreboot.org/c/coreboot/+/38423/4/src/lib/cbfs.c@310 PS4, Line 310: cbmem_entry_find
This failing is a broader problem. […]
Well... we're not exactly. There's still a check for ROMSTAGE_OR_BEFORE below, so if we're in ramstage that path won't be taken either. In that case ro.mcache_size would stay 0, cbfs_mcache_lookup() would immediately act as if the cache was full and fall back to reading from SPI. (This isn't really a case that's ever meant to happen since that CBMEM entry should always be there in ramstage, but I think it would do the right thing if it did.)
Basically, this code is trying to handle 4 cases:
1. bootblock/verstage: always use memlayout mcache 2. romstage before CBMEM init: use memlayout mcache 3. romstage after CBMEM init: use CBMEM mcache (in this case I guess it could fall back to memlayout if the CBMEM entry was broken for some reason, but in this case that would also not be a problem) 4. postcar/ramstage: always use CBMEM (should always exist here)
https://review.coreboot.org/c/coreboot/+/38423/4/src/lib/cbfs.c@324 PS4, Line 324: ENV_BOOTBLOCK
How do you envision this stuff working with separate verstage and NO_RETURN_FROM_VERSTAGE?
I don't think there's a problem? When SEPARATE_VERSTAGE is true, the bootblock loads the verstage -- so it will build the RO mcache at that point (at the latest), before any vboot stuff runs. Doesn't matter if the verstage returns after that or not, both stages will access the valid cache in SRAM/CAR.
But there might actually be a problem with SEPARATE_VERSTAGE=n now that I think of it, because then I guess it's possible that the very first file we're loading is already an RW file and we've never initialized the RO mcache. I'll move this to the top to prevent that.
https://review.coreboot.org/c/coreboot/+/38423/4/src/security/vboot/vboot_lo... File src/security/vboot/vboot_loader.c:
https://review.coreboot.org/c/coreboot/+/38423/4/src/security/vboot/vboot_lo... PS4, Line 46: return; /* No RW mcache if we're staying in RO. */
Dont' we already have this covered with vboot_get_cbfs_boot_device() returning NULL?
Well, we would if I added a NULL check below. Okay, I can do that.
Julius Werner has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 5:
(5 comments)
https://review.coreboot.org/c/coreboot/+/38423/4/src/commonlib/bsd/cbfs_mcac... File src/commonlib/bsd/cbfs_mcache.c:
https://review.coreboot.org/c/coreboot/+/38423/4/src/commonlib/bsd/cbfs_mcac... PS4, Line 8:
Yeah, I think I had planned to document this somewhere and must have forgotten. Added a description.
Done
https://review.coreboot.org/c/coreboot/+/38423/4/src/commonlib/bsd/cbfs_mcac... PS4, Line 26: if (args->end - args->mcache < sizeof(*token))
I'm trying to be really thrifty with space in this cache because CBFS headers can be very small and […]
edit: Sorry, forgot I still had to update these comments before pressing publish. I found a way to follow your suggestion without wasting extra space by overwriting the CBFS header magic (which we don't need anymore once we get here) with the information I used to store in these tokens. So every entry stores its offset explicitly now, and I also made sure there's always space for the terminator.
https://review.coreboot.org/c/coreboot/+/38423/4/src/commonlib/bsd/cbfs_mcac... PS4, Line 42: if (offset != args->offset)
See above, I feel we might as well save the 4 bytes if we can.
edit: disregard other comment, done.
https://review.coreboot.org/c/coreboot/+/38423/4/src/commonlib/bsd/cbfs_mcac... PS4, Line 54: args->offset = ALIGN_UP(offset + data_offset + data_length, CBFS_ALIGNMENT);
I'm not clear why we would be keeping track of this at all. […]
Done
https://review.coreboot.org/c/coreboot/+/38423/4/src/commonlib/bsd/cbfs_mcac... PS4, Line 135: if (memcmp(mdata->h.magic, CBFS_FILE_MAGIC, sizeof(mdata->h.magic)) != 0) {
Yes, this is basically the same walking code as in cbfs_mcache_lookup() above. […]
edit: New struct mcache_entry should make this clearer now.
build bot (Jenkins) has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 6:
(3 comments)
https://review.coreboot.org/c/coreboot/+/38423/6/src/lib/cbfs.c File src/lib/cbfs.c:
https://review.coreboot.org/c/coreboot/+/38423/6/src/lib/cbfs.c@58 PS6, Line 58: if (!(cbd = cbfs_get_boot_device(true))) do not use assignment in if condition
https://review.coreboot.org/c/coreboot/+/38423/6/src/lib/cbfs.c@301 PS6, Line 301: if (cbmem_possibly_online() && do not use assignment in if condition
https://review.coreboot.org/c/coreboot/+/38423/6/src/security/vboot/vboot_lo... File src/security/vboot/vboot_loader.c:
https://review.coreboot.org/c/coreboot/+/38423/6/src/security/vboot/vboot_lo... PS6, Line 110: if (cbmem_possibly_online() && do not use assignment in if condition
Julius Werner has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 6:
(1 comment)
https://review.coreboot.org/c/coreboot/+/38423/6/src/lib/cbfs.c File src/lib/cbfs.c:
https://review.coreboot.org/c/coreboot/+/38423/6/src/lib/cbfs.c@301 PS6, Line 301: if (cbmem_possibly_online() &&
do not use assignment in if condition
I'm planning to ignore these btw, unless anyone has strong feelings about it. These are kernel warnings and to my knowledge there's no hard rule about assignment expressions in the coreboot style. I feel it helps readability here to keep things compact and avoid further indentation.
Patrick Rudolph has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 6:
(1 comment)
https://review.coreboot.org/c/coreboot/+/38423/6/src/commonlib/bsd/cbfs_mcac... File src/commonlib/bsd/cbfs_mcache.c:
https://review.coreboot.org/c/coreboot/+/38423/6/src/commonlib/bsd/cbfs_mcac... PS6, Line 15: CBFS What's a cbfs device? The SPI flash? The bios region? The FMAP region containing a whole CBFS? Is there a magic at the beginning of the cbfs device?
Julius Werner has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 6:
(1 comment)
https://review.coreboot.org/c/coreboot/+/38423/6/src/commonlib/bsd/cbfs_mcac... File src/commonlib/bsd/cbfs_mcache.c:
https://review.coreboot.org/c/coreboot/+/38423/6/src/commonlib/bsd/cbfs_mcac... PS6, Line 15: CBFS
What's a cbfs device? The SPI flash? The bios region? The FMAP region containing a whole CBFS? Is th […]
It's whatever is represented by cbfs_dev_t. All CBFS stuff in commonlib/bsd is based on that opaque type which can be implemented by the underlying software in a variety of ways (e.g. hooked up to SPI flash for coreboot or an image file by CBFS). It only represents the area actually used by CBFS, so on SPI flash it would only be a specific FMAP region. See the comments in cbfs_private.h (in CB:38421) for a general explanation of the cbfs_dev_t API.
(A CBFS always starts immediately with the first file, so there's always an 'LARCHIVE' file magic at the beginning. See Documentation/cbfs.txt for documentation of the CBFS layout itself which is not changed by any of these patches.)
Julius Werner has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 6:
(1 comment)
https://review.coreboot.org/c/coreboot/+/38423/6/src/commonlib/bsd/cbfs_mcac... File src/commonlib/bsd/cbfs_mcache.c:
https://review.coreboot.org/c/coreboot/+/38423/6/src/commonlib/bsd/cbfs_mcac... PS6, Line 15: CBFS
or an image file by CBFS
Sorry, that was meant to say 'cbfstool'
build bot (Jenkins) has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 7:
(3 comments)
https://review.coreboot.org/c/coreboot/+/38423/7/src/lib/cbfs.c File src/lib/cbfs.c:
https://review.coreboot.org/c/coreboot/+/38423/7/src/lib/cbfs.c@58 PS7, Line 58: if (!(cbd = cbfs_get_boot_device(true))) do not use assignment in if condition
https://review.coreboot.org/c/coreboot/+/38423/7/src/lib/cbfs.c@301 PS7, Line 301: if (cbmem_possibly_online() && do not use assignment in if condition
https://review.coreboot.org/c/coreboot/+/38423/7/src/security/vboot/vboot_lo... File src/security/vboot/vboot_loader.c:
https://review.coreboot.org/c/coreboot/+/38423/7/src/security/vboot/vboot_lo... PS7, Line 110: if (cbmem_possibly_online() && do not use assignment in if condition
Hello build bot (Jenkins), Patrick Georgi, Martin Roth, Aaron Durbin,
I'd like you to reexamine a change. Please visit
https://review.coreboot.org/c/coreboot/+/38423
to look at the new patch set (#8).
Change subject: cbfs: Add metadata cache ......................................................................
cbfs: Add metadata cache
This patch adds a new CBFS "mcache" (metadata cache) -- a memory buffer that stores the headers of all CBFS files. Similar to the existing FMAP cache, this cache should reduce the amount of SPI accesses we need to do every boot: rather than having to re-read all CBFS headers from SPI flash every time we're looking for a file, we can just walk the same list in this in-memory copy and finally use it to directly access the flash at the right position for the file data.
This patch adds the code to support the cache but doesn't enable it on any platform. The next one will turn it on by default.
Change-Id: I5b1084bfdad1c6ab0ee1b143ed8dd796827f4c65 Signed-off-by: Julius Werner jwerner@chromium.org --- M src/commonlib/Makefile.inc A src/commonlib/bsd/cbfs_mcache.c M src/commonlib/bsd/include/commonlib/bsd/cb_err.h M src/commonlib/bsd/include/commonlib/bsd/cbfs_private.h M src/commonlib/include/commonlib/cbmem_id.h M src/include/cbfs.h M src/include/memlayout.h M src/include/symbols.h M src/lib/Kconfig M src/lib/cbfs.c M src/lib/coreboot_table.c M src/security/vboot/vboot_common.h M src/security/vboot/vboot_loader.c 13 files changed, 350 insertions(+), 32 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/23/38423/8
build bot (Jenkins) has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 8:
(3 comments)
https://review.coreboot.org/c/coreboot/+/38423/8/src/lib/cbfs.c File src/lib/cbfs.c:
https://review.coreboot.org/c/coreboot/+/38423/8/src/lib/cbfs.c@58 PS8, Line 58: if (!(cbd = cbfs_get_boot_device(true))) do not use assignment in if condition
https://review.coreboot.org/c/coreboot/+/38423/8/src/lib/cbfs.c@301 PS8, Line 301: if (cbmem_possibly_online() && do not use assignment in if condition
https://review.coreboot.org/c/coreboot/+/38423/8/src/security/vboot/vboot_lo... File src/security/vboot/vboot_loader.c:
https://review.coreboot.org/c/coreboot/+/38423/8/src/security/vboot/vboot_lo... PS8, Line 110: if (cbmem_possibly_online() && do not use assignment in if condition
Hello build bot (Jenkins), Patrick Georgi, Martin Roth, Aaron Durbin,
I'd like you to reexamine a change. Please visit
https://review.coreboot.org/c/coreboot/+/38423
to look at the new patch set (#9).
Change subject: cbfs: Add metadata cache ......................................................................
cbfs: Add metadata cache
This patch adds a new CBFS "mcache" (metadata cache) -- a memory buffer that stores the headers of all CBFS files. Similar to the existing FMAP cache, this cache should reduce the amount of SPI accesses we need to do every boot: rather than having to re-read all CBFS headers from SPI flash every time we're looking for a file, we can just walk the same list in this in-memory copy and finally use it to directly access the flash at the right position for the file data.
This patch adds the code to support the cache but doesn't enable it on any platform. The next one will turn it on by default.
Change-Id: I5b1084bfdad1c6ab0ee1b143ed8dd796827f4c65 Signed-off-by: Julius Werner jwerner@chromium.org --- M src/commonlib/Makefile.inc A src/commonlib/bsd/cbfs_mcache.c M src/commonlib/bsd/include/commonlib/bsd/cb_err.h M src/commonlib/bsd/include/commonlib/bsd/cbfs_private.h M src/commonlib/include/commonlib/cbmem_id.h M src/include/cbfs.h M src/include/memlayout.h M src/include/symbols.h M src/lib/Kconfig M src/lib/cbfs.c M src/lib/coreboot_table.c M src/security/vboot/vboot_common.h M src/security/vboot/vboot_loader.c 13 files changed, 350 insertions(+), 32 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/23/38423/9
build bot (Jenkins) has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 9:
(3 comments)
https://review.coreboot.org/c/coreboot/+/38423/9/src/lib/cbfs.c File src/lib/cbfs.c:
https://review.coreboot.org/c/coreboot/+/38423/9/src/lib/cbfs.c@58 PS9, Line 58: if (!(cbd = cbfs_get_boot_device(true))) do not use assignment in if condition
https://review.coreboot.org/c/coreboot/+/38423/9/src/lib/cbfs.c@271 PS9, Line 271: if (cbmem_possibly_online() && do not use assignment in if condition
https://review.coreboot.org/c/coreboot/+/38423/9/src/security/vboot/vboot_lo... File src/security/vboot/vboot_loader.c:
https://review.coreboot.org/c/coreboot/+/38423/9/src/security/vboot/vboot_lo... PS9, Line 110: if (cbmem_possibly_online() && do not use assignment in if condition
Hello build bot (Jenkins), Patrick Georgi, Martin Roth, Aaron Durbin,
I'd like you to reexamine a change. Please visit
https://review.coreboot.org/c/coreboot/+/38423
to look at the new patch set (#10).
Change subject: cbfs: Add metadata cache ......................................................................
cbfs: Add metadata cache
This patch adds a new CBFS "mcache" (metadata cache) -- a memory buffer that stores the headers of all CBFS files. Similar to the existing FMAP cache, this cache should reduce the amount of SPI accesses we need to do every boot: rather than having to re-read all CBFS headers from SPI flash every time we're looking for a file, we can just walk the same list in this in-memory copy and finally use it to directly access the flash at the right position for the file data.
This patch adds the code to support the cache but doesn't enable it on any platform. The next one will turn it on by default.
Change-Id: I5b1084bfdad1c6ab0ee1b143ed8dd796827f4c65 Signed-off-by: Julius Werner jwerner@chromium.org --- M src/commonlib/Makefile.inc A src/commonlib/bsd/cbfs_mcache.c M src/commonlib/bsd/include/commonlib/bsd/cb_err.h M src/commonlib/bsd/include/commonlib/bsd/cbfs_private.h M src/commonlib/include/commonlib/cbmem_id.h M src/include/cbfs.h M src/include/memlayout.h M src/include/symbols.h M src/lib/Kconfig M src/lib/cbfs.c M src/lib/coreboot_table.c M src/security/vboot/vboot_common.h M src/security/vboot/vboot_loader.c 13 files changed, 353 insertions(+), 32 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/23/38423/10
build bot (Jenkins) has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 10:
(3 comments)
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/cbfs.c File src/lib/cbfs.c:
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/cbfs.c@46 PS10, Line 46: if (!(cbd = cbfs_get_boot_device(true))) do not use assignment in if condition
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/cbfs.c@297 PS10, Line 297: if (cbmem_possibly_online() && do not use assignment in if condition
https://review.coreboot.org/c/coreboot/+/38423/10/src/security/vboot/vboot_l... File src/security/vboot/vboot_loader.c:
https://review.coreboot.org/c/coreboot/+/38423/10/src/security/vboot/vboot_l... PS10, Line 100: if (cbmem_possibly_online() && do not use assignment in if condition
Julius Werner has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 10:
(2 comments)
https://review.coreboot.org/c/coreboot/+/38423/4/src/commonlib/bsd/cbfs_mcac... File src/commonlib/bsd/cbfs_mcache.c:
https://review.coreboot.org/c/coreboot/+/38423/4/src/commonlib/bsd/cbfs_mcac... PS4, Line 72: ERROR("mcache overflow, should increase CBFS_MCACHE size!\n");
The goal of the design is that things are still usable when the cache is too small. […]
I changed my mind a bit about how this works -- I still think we need to support cases with RW updates where the mcache might overflow, but I am passing CB_CBFS_CACHE_FULL out to the caller now so that the decision what to do about it can be made at that level.
https://review.coreboot.org/c/coreboot/+/38423/4/src/commonlib/bsd/cbfs_mcac... PS4, Line 123:
See above... this is necessary to support the cache full case. […]
Changed now as mentioned above. Pushed the responsibility for this to the caller so it can decide whether to die() or fall back to cbfs_lookup() or whatever. Makes the function signature a bit less bulky, too.
Aaron Durbin has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 10:
(11 comments)
https://review.coreboot.org/c/coreboot/+/38423/10/src/commonlib/bsd/cbfs_mca... File src/commonlib/bsd/cbfs_mcache.c:
https://review.coreboot.org/c/coreboot/+/38423/10/src/commonlib/bsd/cbfs_mca... PS10, Line 12: values in big-endian Why not transition these to host endian after the cache is built?
https://review.coreboot.org/c/coreboot/+/38423/10/src/commonlib/bsd/cbfs_mca... PS10, Line 14: absolute offset What's the reason behind storing absolute? I think it'd be good to provide the explanation for the decision. The rdev in cbfs_boot_device object would be a sub region of the full device so those absolute offsets can exceed the sub region size. Or maybe I'm confused by this terminology. The offset is actually relative to the rdev of the cbfs_boot_device object.
https://review.coreboot.org/c/coreboot/+/38423/10/src/commonlib/bsd/cbfs_mca... PS10, Line 70: .end = mcache + size - sizeof(uint32_t), /* leave space for terminating magic */ Shouldn't we be doing ALIGN_DOWN(size - sizeof(uint32_t), MCACHE_ALIGNMENT) to ensure we meet alignment assumptions?
https://review.coreboot.org/c/coreboot/+/38423/10/src/commonlib/bsd/cbfs_mca... PS10, Line 74: assert(size >= sizeof(uint32_t)); also assert mcache is aligned to at least uint32_t as well?
https://review.coreboot.org/c/coreboot/+/38423/10/src/commonlib/bsd/cbfs_mca... PS10, Line 116: entry While the unions are carefully crafted, I think it's clearer to do:
memcpy(mdata_out, &entry->file, data_offset);
https://review.coreboot.org/c/coreboot/+/38423/10/src/commonlib/bsd/cbfs_mca... PS10, Line 116: memcpy(mdata_out, entry, data_offset); Again, how are we ensuring data_offset does not exceed sizeof(cbfs_mdata) ?
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/Kconfig File src/lib/Kconfig:
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/Kconfig@91 PS10, Line 91: depends on VBOOT Shouldn't this also depend on !NO_CBFS_MCACHE?
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/Kconfig@98 PS10, Line 98: automatically be 0 How? Is that Kconfig makes this value 0 with !VBOOT?
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/cbfs.c File src/lib/cbfs.c:
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/cbfs.c@28 PS10, Line 28: if (err == CB_CBFS_CACHE_FULL) Should we print something here indicating one is going back to the boot media for metadata?
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/cbfs.c@277 PS10, Line 277: /* Ensure we always init RO mcache, even if first file is from RW. */ Why? No explanation is provided.
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/cbfs.c@295 PS10, Line 295: if (!CONFIG(NO_CBFS_MCACHE)) { I see we're packing RO and RW into the same area. I feel like we could be more explicit by just having 2 regions instead of open coding the math and pointer arithmetic to try and pack things into a single region. The boundaries are already fixed so why not just be more clear?
Hello build bot (Jenkins), Patrick Georgi, Martin Roth, Aaron Durbin,
I'd like you to reexamine a change. Please visit
https://review.coreboot.org/c/coreboot/+/38423
to look at the new patch set (#11).
Change subject: cbfs: Add metadata cache ......................................................................
cbfs: Add metadata cache
This patch adds a new CBFS "mcache" (metadata cache) -- a memory buffer that stores the headers of all CBFS files. Similar to the existing FMAP cache, this cache should reduce the amount of SPI accesses we need to do every boot: rather than having to re-read all CBFS headers from SPI flash every time we're looking for a file, we can just walk the same list in this in-memory copy and finally use it to directly access the flash at the right position for the file data.
This patch adds the code to support the cache but doesn't enable it on any platform. The next one will turn it on by default.
Change-Id: I5b1084bfdad1c6ab0ee1b143ed8dd796827f4c65 Signed-off-by: Julius Werner jwerner@chromium.org --- M src/commonlib/Makefile.inc A src/commonlib/bsd/cbfs_mcache.c M src/commonlib/bsd/include/commonlib/bsd/cb_err.h M src/commonlib/bsd/include/commonlib/bsd/cbfs_private.h M src/commonlib/include/commonlib/cbmem_id.h M src/include/cbfs.h M src/include/memlayout.h M src/include/symbols.h M src/lib/Kconfig M src/lib/cbfs.c M src/lib/coreboot_table.c M src/security/vboot/vboot_common.h M src/security/vboot/vboot_loader.c 13 files changed, 357 insertions(+), 34 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/23/38423/11
build bot (Jenkins) has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 11:
(3 comments)
https://review.coreboot.org/c/coreboot/+/38423/11/src/lib/cbfs.c File src/lib/cbfs.c:
https://review.coreboot.org/c/coreboot/+/38423/11/src/lib/cbfs.c@45 PS11, Line 45: if (!(cbd = cbfs_get_boot_device(true))) do not use assignment in if condition
https://review.coreboot.org/c/coreboot/+/38423/11/src/lib/cbfs.c@367 PS11, Line 367: if (cbmem_possibly_online() && do not use assignment in if condition
https://review.coreboot.org/c/coreboot/+/38423/11/src/security/vboot/vboot_l... File src/security/vboot/vboot_loader.c:
https://review.coreboot.org/c/coreboot/+/38423/11/src/security/vboot/vboot_l... PS11, Line 100: if (cbmem_possibly_online() && do not use assignment in if condition
Julius Werner has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 11:
(11 comments)
https://review.coreboot.org/c/coreboot/+/38423/10/src/commonlib/bsd/cbfs_mca... File src/commonlib/bsd/cbfs_mcache.c:
https://review.coreboot.org/c/coreboot/+/38423/10/src/commonlib/bsd/cbfs_mca... PS10, Line 12: values in big-endian
Why not transition these to host endian after the cache is built?
Same reason as mentioned in https://review.coreboot.org/c/coreboot/+/38421/11/src/commonlib/bsd/include/..., I think to avoid confusion it's just better to have a hard rule that anything in a union cbfs_mdata always stays in big-endian.
https://review.coreboot.org/c/coreboot/+/38423/10/src/commonlib/bsd/cbfs_mca... PS10, Line 14: absolute offset
What's the reason behind storing absolute? I think it'd be good to provide the explanation for the d […]
Yes, when I say absolute I mean absolute to the cbfs_dev_t (as opposed to say relative to the previous header or something, which I considered but decided absolute is easier to deal with). For coreboot that's equivalent to an rdev for the CBFS FMAP region. This code only knows about cbfs_dev_t, it doesn't know anything about the implementation beyond that (which could be a file in the case of cbfstool, or anything else really). Clarified comment.
https://review.coreboot.org/c/coreboot/+/38423/10/src/commonlib/bsd/cbfs_mca... PS10, Line 70: .end = mcache + size - sizeof(uint32_t), /* leave space for terminating magic */
Shouldn't we be doing ALIGN_DOWN(size - sizeof(uint32_t), MCACHE_ALIGNMENT) to ensure we meet alignm […]
Done
https://review.coreboot.org/c/coreboot/+/38423/10/src/commonlib/bsd/cbfs_mca... PS10, Line 74: assert(size >= sizeof(uint32_t));
also assert mcache is aligned to at least uint32_t as well?
Done
https://review.coreboot.org/c/coreboot/+/38423/10/src/commonlib/bsd/cbfs_mca... PS10, Line 116: memcpy(mdata_out, entry, data_offset);
Again, how are we ensuring data_offset does not exceed sizeof(cbfs_mdata) ?
This also goes back to https://review.coreboot.org/c/coreboot/+/38421/11/src/commonlib/bsd/cbfs_pri...: cbfs_walk() considers any header invalid that doesn't fit within cbfs_mdata and refuses to even parse the rest of it (so in this case build_walker() is never called on it and it doesn't end up in the mcache).
https://review.coreboot.org/c/coreboot/+/38423/10/src/commonlib/bsd/cbfs_mca... PS10, Line 116: entry
While the unions are carefully crafted, I think it's clearer to do: […]
Done
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/Kconfig File src/lib/Kconfig:
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/Kconfig@91 PS10, Line 91: depends on VBOOT
Shouldn't this also depend on !NO_CBFS_MCACHE?
Done
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/Kconfig@98 PS10, Line 98: automatically be 0
How? Is that Kconfig makes this value 0 with !VBOOT?
Yes, Kconfig still outputs macros for configs with unmet dependencies, it just forces them to n or 0 or "" (depending on the type).
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/cbfs.c File src/lib/cbfs.c:
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/cbfs.c@28 PS10, Line 28: if (err == CB_CBFS_CACHE_FULL)
Should we print something here indicating one is going back to the boot media for metadata?
I can add that if you feel it's necessary, but cbfs_mcache_build() already prints an error when building if the cache is too small. I think that's really the important point where it should be reported and then later it's not that useful to say it again and again whenever a file from beyond the cached part is actually read. (Note that the normal log message for a found file is still different between mcache and normal lookup, so it's still possible to tell from the log where a specific file came from, there's just not an explicit error message on a separate line again for each file.)
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/cbfs.c@277 PS10, Line 277: /* Ensure we always init RO mcache, even if first file is from RW. */
Why? No explanation is provided.
Because otherwise the RO mcache might never get initialized. I don't want to link the mcache building code into every stage just in case that stage is the first where it's needed. Expanded the comment a little, let me know if that helps.
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/cbfs.c@295 PS10, Line 295: if (!CONFIG(NO_CBFS_MCACHE)) {
I see we're packing RO and RW into the same area. […]
I came up with this design to allow users more flexibility for how to split up the two caches. The CBFS_MCACHE region is declared in SoC memlayouts, but different boards using that SoC (or different images running on that board) may prefer different splits. For example, on Chrome OS we have all our UI assets in the RO CBFS, so there tend to be a lot more files in our RO CBFS than in our RW. For other users a 50:50 split probably makes more sense, or they may even want to give more weight to RW (e.g. the Facebook guys who don't use recovery mode and thus don't have the post-verstage stages in RO). That's why I set the default for CBFS_MCACHE_RW_PERCENTAGE to 25 for CONFIG_CHROMEOS and to 50 otherwise.
This is the only code that deals with those raw percentages and everything else is just operating on the cbfs_boot_device structures set up here, so I'd say it's about as encapsulated as it can be already.
Hello build bot (Jenkins), Patrick Georgi, Martin Roth, Aaron Durbin,
I'd like you to reexamine a change. Please visit
https://review.coreboot.org/c/coreboot/+/38423
to look at the new patch set (#12).
Change subject: cbfs: Add metadata cache ......................................................................
cbfs: Add metadata cache
This patch adds a new CBFS "mcache" (metadata cache) -- a memory buffer that stores the headers of all CBFS files. Similar to the existing FMAP cache, this cache should reduce the amount of SPI accesses we need to do every boot: rather than having to re-read all CBFS headers from SPI flash every time we're looking for a file, we can just walk the same list in this in-memory copy and finally use it to directly access the flash at the right position for the file data.
This patch adds the code to support the cache but doesn't enable it on any platform. The next one will turn it on by default.
Change-Id: I5b1084bfdad1c6ab0ee1b143ed8dd796827f4c65 Signed-off-by: Julius Werner jwerner@chromium.org --- M src/commonlib/Makefile.inc A src/commonlib/bsd/cbfs_mcache.c M src/commonlib/bsd/include/commonlib/bsd/cb_err.h M src/commonlib/bsd/include/commonlib/bsd/cbfs_private.h M src/commonlib/include/commonlib/cbmem_id.h M src/include/cbfs.h M src/include/memlayout.h M src/include/symbols.h M src/lib/Kconfig M src/lib/cbfs.c M src/lib/coreboot_table.c M src/security/vboot/vboot_common.h M src/security/vboot/vboot_loader.c 13 files changed, 357 insertions(+), 34 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/23/38423/12
build bot (Jenkins) has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 12:
(3 comments)
https://review.coreboot.org/c/coreboot/+/38423/12/src/lib/cbfs.c File src/lib/cbfs.c:
https://review.coreboot.org/c/coreboot/+/38423/12/src/lib/cbfs.c@45 PS12, Line 45: if (!(cbd = cbfs_get_boot_device(true))) do not use assignment in if condition
https://review.coreboot.org/c/coreboot/+/38423/12/src/lib/cbfs.c@367 PS12, Line 367: if (cbmem_possibly_online() && do not use assignment in if condition
https://review.coreboot.org/c/coreboot/+/38423/12/src/security/vboot/vboot_l... File src/security/vboot/vboot_loader.c:
https://review.coreboot.org/c/coreboot/+/38423/12/src/security/vboot/vboot_l... PS12, Line 100: if (cbmem_possibly_online() && do not use assignment in if condition
Aaron Durbin has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 12:
(3 comments)
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/cbfs.c File src/lib/cbfs.c:
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/cbfs.c@28 PS10, Line 28: if (err == CB_CBFS_CACHE_FULL)
I can add that if you feel it's necessary, but cbfs_mcache_build() already prints an error when buil […]
I think we should have a Kconfig to fail so we don't fall back at all. For our deployments I don't think we should ever fall back on the boot media for the walk. It's way too subtle, and I think we'll be setting ourselves up for someone missing the hints and shipping something we shouldn't.
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/cbfs.c@295 PS10, Line 295: if (!CONFIG(NO_CBFS_MCACHE)) {
I came up with this design to allow users more flexibility for how to split up the two caches. […]
fwiw, vboot_loader has the similar calculation dance but for the RW piece. I was actually thrown off in thinking we had duplicated things. So it's not just in one place. We have implementation details in multiple files related to the same logic. I wonder if a helper could be provided for dealing with the similar logic.
https://review.coreboot.org/c/coreboot/+/38423/12/src/security/vboot/vboot_l... File src/security/vboot/vboot_loader.c:
https://review.coreboot.org/c/coreboot/+/38423/12/src/security/vboot/vboot_l... PS12, Line 39: die("Failed to build RW mcache."); /* TODO: -> recovery? */ I would think we would want die or go to recovery if the cache is full.
Hello build bot (Jenkins), Patrick Georgi, Martin Roth, Aaron Durbin,
I'd like you to reexamine a change. Please visit
https://review.coreboot.org/c/coreboot/+/38423
to look at the new patch set (#13).
Change subject: cbfs: Add metadata cache ......................................................................
cbfs: Add metadata cache
This patch adds a new CBFS "mcache" (metadata cache) -- a memory buffer that stores the headers of all CBFS files. Similar to the existing FMAP cache, this cache should reduce the amount of SPI accesses we need to do every boot: rather than having to re-read all CBFS headers from SPI flash every time we're looking for a file, we can just walk the same list in this in-memory copy and finally use it to directly access the flash at the right position for the file data.
This patch adds the code to support the cache but doesn't enable it on any platform. The next one will turn it on by default.
Change-Id: I5b1084bfdad1c6ab0ee1b143ed8dd796827f4c65 Signed-off-by: Julius Werner jwerner@chromium.org --- M src/commonlib/Makefile.inc A src/commonlib/bsd/cbfs_mcache.c M src/commonlib/bsd/include/commonlib/bsd/cb_err.h M src/commonlib/bsd/include/commonlib/bsd/cbfs_private.h M src/commonlib/include/commonlib/cbmem_id.h M src/include/cbfs.h M src/include/memlayout.h M src/include/symbols.h M src/lib/Kconfig M src/lib/cbfs.c M src/lib/coreboot_table.c M src/security/vboot/vboot_common.h M src/security/vboot/vboot_loader.c 13 files changed, 366 insertions(+), 34 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/23/38423/13
build bot (Jenkins) has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 13:
(2 comments)
https://review.coreboot.org/c/coreboot/+/38423/13/src/lib/cbfs.c File src/lib/cbfs.c:
https://review.coreboot.org/c/coreboot/+/38423/13/src/lib/cbfs.c@45 PS13, Line 45: if (!(cbd = cbfs_get_boot_device(true))) do not use assignment in if condition
https://review.coreboot.org/c/coreboot/+/38423/13/src/lib/cbfs.c@348 PS13, Line 348: if (cbmem_possibly_online() && do not use assignment in if condition
Julius Werner has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 13:
(3 comments)
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/cbfs.c File src/lib/cbfs.c:
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/cbfs.c@28 PS10, Line 28: if (err == CB_CBFS_CACHE_FULL)
I think we should have a Kconfig to fail so we don't fall back at all. […]
I think the fallback is critical for RW updates though? If we ever get into a situation where we need to add more files in an RW update, and that starts overflowing the cache, we'd be screwed otherwise. The cache size has to be hardcoded in RO because verstage already needs to build the cache to load the RW romstage.
Note that the cache full error message is "CBFS ERROR: mcache overflow, ...", and any firmware log string with the string "ERROR" in it will make our suspend_stress_test script complain. (This, sadly, is the most reliable method I've found over the years to mark a non-fatal error condition in a way that bring-up teams will actually notice, and that's why I always tell people to start the really bad error messages with "ERROR". I agree that's terrible and we could really use a more reliable mechanism to report non-fatal but problematic errors, like including the log-level in the CBMEM console so we could at least scan for CB_ERR or something... but it was never something I had time to do myself.) If we want something more robust we can of course also add a FAFT test that checks for that specific message, and that also adds a bunch of dummy files to trigger it intentionally and confirm the device can still boot in an overflow scenario. But I think we'll need to implement that sort of policy in our release testing flow, so that we still have the option to manually waive it for a later RW update if that's the only way to solve a critical issue.
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/cbfs.c@295 PS10, Line 295: if (!CONFIG(NO_CBFS_MCACHE)) {
fwiw, vboot_loader has the similar calculation dance but for the RW piece. […]
Oh... okay, yeah, that's fair. Factored it out into a function.
https://review.coreboot.org/c/coreboot/+/38423/12/src/security/vboot/vboot_l... File src/security/vboot/vboot_loader.c:
https://review.coreboot.org/c/coreboot/+/38423/12/src/security/vboot/vboot_l... PS12, Line 39: die("Failed to build RW mcache."); /* TODO: -> recovery? */
I would think we would want die or go to recovery if the cache is full.
See other comment, I think this would too dangerously restrict our ability to change things in RW updates.
Hello build bot (Jenkins), Patrick Georgi, Martin Roth, Aaron Durbin,
I'd like you to reexamine a change. Please visit
https://review.coreboot.org/c/coreboot/+/38423
to look at the new patch set (#14).
Change subject: cbfs: Add metadata cache ......................................................................
cbfs: Add metadata cache
This patch adds a new CBFS "mcache" (metadata cache) -- a memory buffer that stores the headers of all CBFS files. Similar to the existing FMAP cache, this cache should reduce the amount of SPI accesses we need to do every boot: rather than having to re-read all CBFS headers from SPI flash every time we're looking for a file, we can just walk the same list in this in-memory copy and finally use it to directly access the flash at the right position for the file data.
This patch adds the code to support the cache but doesn't enable it on any platform. The next one will turn it on by default.
Change-Id: I5b1084bfdad1c6ab0ee1b143ed8dd796827f4c65 Signed-off-by: Julius Werner jwerner@chromium.org --- M src/commonlib/Makefile.inc A src/commonlib/bsd/cbfs_mcache.c M src/commonlib/bsd/include/commonlib/bsd/cb_err.h M src/commonlib/bsd/include/commonlib/bsd/cbfs_private.h M src/commonlib/include/commonlib/cbmem_id.h M src/include/cbfs.h M src/include/memlayout.h M src/include/symbols.h M src/lib/Kconfig M src/lib/cbfs.c M src/lib/coreboot_table.c M src/security/vboot/vboot_common.h M src/security/vboot/vboot_loader.c 13 files changed, 366 insertions(+), 33 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/23/38423/14
build bot (Jenkins) has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 14:
(2 comments)
https://review.coreboot.org/c/coreboot/+/38423/14/src/lib/cbfs.c File src/lib/cbfs.c:
https://review.coreboot.org/c/coreboot/+/38423/14/src/lib/cbfs.c@44 PS14, Line 44: if (!(cbd = cbfs_get_boot_device(true))) do not use assignment in if condition
https://review.coreboot.org/c/coreboot/+/38423/14/src/lib/cbfs.c@349 PS14, Line 349: if (cbmem_possibly_online() && do not use assignment in if condition
Hello build bot (Jenkins), Patrick Georgi, Martin Roth, Aaron Durbin,
I'd like you to reexamine a change. Please visit
https://review.coreboot.org/c/coreboot/+/38423
to look at the new patch set (#15).
Change subject: cbfs: Add metadata cache ......................................................................
cbfs: Add metadata cache
This patch adds a new CBFS "mcache" (metadata cache) -- a memory buffer that stores the headers of all CBFS files. Similar to the existing FMAP cache, this cache should reduce the amount of SPI accesses we need to do every boot: rather than having to re-read all CBFS headers from SPI flash every time we're looking for a file, we can just walk the same list in this in-memory copy and finally use it to directly access the flash at the right position for the file data.
This patch adds the code to support the cache but doesn't enable it on any platform. The next one will turn it on by default.
Change-Id: I5b1084bfdad1c6ab0ee1b143ed8dd796827f4c65 Signed-off-by: Julius Werner jwerner@chromium.org --- M src/commonlib/Makefile.inc A src/commonlib/bsd/cbfs_mcache.c M src/commonlib/bsd/include/commonlib/bsd/cb_err.h M src/commonlib/bsd/include/commonlib/bsd/cbfs_private.h M src/commonlib/include/commonlib/cbmem_id.h M src/include/cbfs.h M src/include/memlayout.h M src/include/symbols.h M src/lib/Kconfig M src/lib/cbfs.c M src/lib/coreboot_table.c M src/security/vboot/vboot_common.h M src/security/vboot/vboot_loader.c 13 files changed, 366 insertions(+), 33 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/23/38423/15
build bot (Jenkins) has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 15:
(2 comments)
https://review.coreboot.org/c/coreboot/+/38423/15/src/lib/cbfs.c File src/lib/cbfs.c:
https://review.coreboot.org/c/coreboot/+/38423/15/src/lib/cbfs.c@44 PS15, Line 44: if (!(cbd = cbfs_get_boot_device(true))) do not use assignment in if condition
https://review.coreboot.org/c/coreboot/+/38423/15/src/lib/cbfs.c@355 PS15, Line 355: if (cbmem_possibly_online() && do not use assignment in if condition
build bot (Jenkins) has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 16:
(2 comments)
https://review.coreboot.org/c/coreboot/+/38423/16/src/lib/cbfs.c File src/lib/cbfs.c:
https://review.coreboot.org/c/coreboot/+/38423/16/src/lib/cbfs.c@44 PS16, Line 44: if (!(cbd = cbfs_get_boot_device(true))) do not use assignment in if condition
https://review.coreboot.org/c/coreboot/+/38423/16/src/lib/cbfs.c@355 PS16, Line 355: if (cbmem_possibly_online() && do not use assignment in if condition
Aaron Durbin has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 16:
(1 comment)
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/cbfs.c File src/lib/cbfs.c:
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/cbfs.c@28 PS10, Line 28: if (err == CB_CBFS_CACHE_FULL)
I think the fallback is critical for RW updates though? If we ever get into a situation where we need to add more files in an RW update, and that starts overflowing the cache, we'd be screwed otherwise. The cache size has to be hardcoded in RO because verstage already needs to build the cache to load the RW romstage.
Or move the cache once we're in RW? I don't feel like it's a good policy to count on not hitting the cache. That seems not ideal to me. I understand where you are coming from for future proofing, but it seems like not a great crutch. I'll think on it some more, but I don't think we should count on that situation being our 1st and only option.
Note that the cache full error message is "CBFS ERROR: mcache overflow, ...", and any firmware log string with the string "ERROR" in it will make our suspend_stress_test script complain. (This, sadly, is the most reliable method I've found over the years to mark a non-fatal error condition in a way that bring-up teams will actually notice, and that's why I always tell people to start the really bad error messages with "ERROR". I agree that's terrible and we could really use a more reliable mechanism to report non-fatal but problematic errors, like including the log-level in the CBMEM console so we could at least scan for CB_ERR or something... but it was never something I had time to do myself.) If we want something more robust we can of course also add a FAFT test that checks for that specific message, and that also adds a bunch of dummy files to trigger it intentionally and confirm the device can still boot in an overflow scenario. But I think we'll need to implement that sort of policy in our release testing flow, so that we still have the option to manually waive it for a later RW update if that's the only way to solve a critical issue.
I wonder if we should funnel more information into eventlog or some other way to get structured messages/events through for the reasons you've stated. That's obviously for the future, though.
Aaron Durbin has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 16: Code-Review+2
(2 comments)
https://review.coreboot.org/c/coreboot/+/38423/16/src/commonlib/bsd/cbfs_mca... File src/commonlib/bsd/cbfs_mcache.c:
https://review.coreboot.org/c/coreboot/+/38423/16/src/commonlib/bsd/cbfs_mca... PS16, Line 136: if (entry->magic != MCACHE_MAGIC_FILE) { We check for corrupted mcache above. Any reason not to do similar checks here? Let it fail on subsequent lookup?
https://review.coreboot.org/c/coreboot/+/38423/16/src/include/cbfs.h File src/include/cbfs.h:
https://review.coreboot.org/c/coreboot/+/38423/16/src/include/cbfs.h@52 PS16, Line 52: find fill?
Hello build bot (Jenkins), Patrick Georgi, Martin Roth, Aaron Durbin,
I'd like you to reexamine a change. Please visit
https://review.coreboot.org/c/coreboot/+/38423
to look at the new patch set (#17).
Change subject: cbfs: Add metadata cache ......................................................................
cbfs: Add metadata cache
This patch adds a new CBFS "mcache" (metadata cache) -- a memory buffer that stores the headers of all CBFS files. Similar to the existing FMAP cache, this cache should reduce the amount of SPI accesses we need to do every boot: rather than having to re-read all CBFS headers from SPI flash every time we're looking for a file, we can just walk the same list in this in-memory copy and finally use it to directly access the flash at the right position for the file data.
This patch adds the code to support the cache but doesn't enable it on any platform. The next one will turn it on by default.
Change-Id: I5b1084bfdad1c6ab0ee1b143ed8dd796827f4c65 Signed-off-by: Julius Werner jwerner@chromium.org --- M src/commonlib/Makefile.inc A src/commonlib/bsd/cbfs_mcache.c M src/commonlib/bsd/include/commonlib/bsd/cb_err.h M src/commonlib/bsd/include/commonlib/bsd/cbfs_private.h M src/commonlib/include/commonlib/cbmem_id.h M src/include/cbfs.h M src/include/memlayout.h M src/include/symbols.h M src/lib/Kconfig M src/lib/cbfs.c M src/lib/coreboot_table.c M src/security/vboot/vboot_common.h M src/security/vboot/vboot_loader.c 13 files changed, 364 insertions(+), 33 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/23/38423/17
Julius Werner has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 17:
(10 comments)
https://review.coreboot.org/c/coreboot/+/38423/4/src/commonlib/bsd/cbfs_mcac... File src/commonlib/bsd/cbfs_mcache.c:
https://review.coreboot.org/c/coreboot/+/38423/4/src/commonlib/bsd/cbfs_mcac... PS4, Line 72: ERROR("mcache overflow, should increase CBFS_MCACHE size!\n");
I changed my mind a bit about how this works -- I still think we need to support cases with RW updat […]
(Continued in https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/cbfs.c#28)
https://review.coreboot.org/c/coreboot/+/38423/4/src/commonlib/bsd/cbfs_mcac... PS4, Line 123:
Changed now as mentioned above. […]
Done
https://review.coreboot.org/c/coreboot/+/38423/16/src/commonlib/bsd/cbfs_mca... File src/commonlib/bsd/cbfs_mcache.c:
https://review.coreboot.org/c/coreboot/+/38423/16/src/commonlib/bsd/cbfs_mca... PS16, Line 136: if (entry->magic != MCACHE_MAGIC_FILE) {
We check for corrupted mcache above. […]
Mostly because I'm not sure what to return tbh, I didn't want to blow up this function to use cb_err_t and an out-parameter just for this. The mcache is in memory and unless there's memory corruption it shouldn't be possible for it to get corrupted, so the check above is basically just an assert.
Actually, let me just change that to an assert and then I can do that here as well.
https://review.coreboot.org/c/coreboot/+/38423/4/src/commonlib/bsd/include/c... File src/commonlib/bsd/include/commonlib/bsd/cbfs_private.h:
https://review.coreboot.org/c/coreboot/+/38423/4/src/commonlib/bsd/include/c... PS4, Line 98: struct vb2_hash *master_hash);
It's just a consequence of my cbfs_walk() design. […]
Ack
https://review.coreboot.org/c/coreboot/+/38423/16/src/include/cbfs.h File src/include/cbfs.h:
https://review.coreboot.org/c/coreboot/+/38423/16/src/include/cbfs.h@52 PS16, Line 52: find
fill?
Well it's finding the mcache, I think that's the most important thing about what it does, that it is using that result to fill out the member fields in the passed struct is more of an implementation detail. If I called it cbfs_boot_device_fill_mcache() I think it would sound like it was filling the actual mcache (which it doesn't), I want to avoid that impression.
https://review.coreboot.org/c/coreboot/+/38423/4/src/lib/cbfs.c File src/lib/cbfs.c:
https://review.coreboot.org/c/coreboot/+/38423/4/src/lib/cbfs.c@299 PS4, Line 299: const struct cbfs_boot_device *rw = vboot_get_cbfs_boot_device();
Those two or that vboot hasn't executed yet. […]
Ack
https://review.coreboot.org/c/coreboot/+/38423/4/src/lib/cbfs.c@310 PS4, Line 310: cbmem_entry_find
Well... we're not exactly. […]
Ack
https://review.coreboot.org/c/coreboot/+/38423/4/src/lib/cbfs.c@324 PS4, Line 324: ENV_BOOTBLOCK
I don't think there's a problem? When SEPARATE_VERSTAGE is true, the bootblock loads the verstage -- […]
Done
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/cbfs.c File src/lib/cbfs.c:
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/cbfs.c@28 PS10, Line 28: if (err == CB_CBFS_CACHE_FULL)
Or move the cache once we're in RW? I don't feel like it's a good policy to count on not hitting the cache. That seems not ideal to me. I understand where you are coming from for future proofing, but it seems like not a great crutch. I'll think on it some more, but I don't think we should count on that situation being our 1st and only option.
Well, again, verstage already needs to have the RW cache but its memory layout is defined by RO code, so if it doesn't fit there it doesn't fit. The only way to have the RW cache defined by RW code is to make verstage explicitly avoid using the cache and look up the romstage directly on flash, which both wastes time for an extra lookup on flash that we normally wouldn't need and would be very hard to work into the CBFS APIs where I'd really like to make the cache transparent to higher layers. I don't think this is enough a problem in practice to make that worth it. I also think leaving the fallback option available is nice even if we will try to make sure we'll never hit it in Chrome OS, because other coreboot users may have other requirements. For some, there may be a need to store such a large amount of files that they cannot reasonably fit a full cache anywhere, but they may still benefit from a partial cache (especially since those files would likely be payload-specific stuff that gets added at the end, and they can order their cbfstool add invocations to put the stuff that's not needed in performance-critical paths in the region that would overflow the cache).
https://review.coreboot.org/c/coreboot/+/38423/4/src/security/vboot/vboot_lo... File src/security/vboot/vboot_loader.c:
https://review.coreboot.org/c/coreboot/+/38423/4/src/security/vboot/vboot_lo... PS4, Line 46: return; /* No RW mcache if we're staying in RO. */
Well, we would if I added a NULL check below. Okay, I can do that.
Done
build bot (Jenkins) has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 17:
(2 comments)
https://review.coreboot.org/c/coreboot/+/38423/17/src/lib/cbfs.c File src/lib/cbfs.c:
https://review.coreboot.org/c/coreboot/+/38423/17/src/lib/cbfs.c@44 PS17, Line 44: if (!(cbd = cbfs_get_boot_device(true))) do not use assignment in if condition
https://review.coreboot.org/c/coreboot/+/38423/17/src/lib/cbfs.c@355 PS17, Line 355: if (cbmem_possibly_online() && do not use assignment in if condition
Aaron Durbin has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 17:
(1 comment)
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/cbfs.c File src/lib/cbfs.c:
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/cbfs.c@28 PS10, Line 28: if (err == CB_CBFS_CACHE_FULL)
Well, again, verstage already needs to have the RW cache but its memory layout is defined by RO code, so if it doesn't fit there it doesn't fit. The only way to have the RW cache defined by RW code is to make verstage explicitly avoid using the cache and look up the romstage directly on flash, which both wastes time for an extra lookup on flash that we normally wouldn't need and would be very hard to work into the CBFS APIs where I'd really like to make the cache transparent to higher layers. I don't think this is enough a problem in practice to make that worth it. I also think leaving the fallback option available is nice even if we will try to make sure we'll never hit it in Chrome OS, because other coreboot users may have other requirements. For some, there may be a need to store such a large amount of files that they cannot reasonably fit a full cache anywhere, but they may still benefit from a partial cache (especially since those files would likely be payload-specific stuff that gets added at the end, and they can order their cbfstool add invocations to put the stuff that's not needed in performance-critical paths in the region that would overflow the cache).
There are other ways such that RW_SECTION_A/B only has the next stage in its cbfs with another cbfs with the rest of the files.
RO -> RW_SECTION_A -> RW_SECTION_FULL
That the first RW is a small trampoline w.r.t. metadata (one file) which is signed w/ the signature in vblock. You would be extending the concept of the hash anchor to the first stage of RW like you have for bootblock in the current patchset (or just throw it in another file of that smaller cbfs that is vblock covered).
I find the argument about wasting time hard to swallow since you are explicitly allowing CACHE_FULL to be a 1st class citizen on chrome os which inherently requires a re-walk of the entire metadata of the cbfs to re-validate the metadata for anything that falls out of the cache.
I think it's important to have as little brittleness from the RO binding to RW, and we're now enforcing a tight coupling that is forcing some not so optimal decisions.
FWIW, I'm fine w/ fallback for different users of coreboot, but I don't think it's the right answer for Chrome OS. I believe there are other ways to optimize the dependencies and bindings. I think a lot of it starts from understanding the access patterns of files (which you eluded to) and chaining everything correspondingly. I agree it's simpler to throw everything into one big cbfs, but it's forcing us down particular paths where the trade-offs are not optimal when you introduce this hard coupling of RO->RW, especially fixing the size of the metadata cache when adding to cbmem when we have memory to play with to handle the full cache size.
Julius Werner has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 17:
(1 comment)
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/cbfs.c File src/lib/cbfs.c:
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/cbfs.c@28 PS10, Line 28: if (err == CB_CBFS_CACHE_FULL)
There are other ways such that RW_SECTION_A/B only has the next stage in its cbfs with another cbfs with the rest of the files.
RO -> RW_SECTION_A -> RW_SECTION_FULL
Yeah, but... do we want that? I feel like this would add a grotesque overcomplication to our otherwise neat and clean RO/RW CBFS concept to solve what I really think is a non-issue. I mean part of the whole reason I'm doing all of this is to maintain backwards-compatibility to old tooling, old payloads, etc. If we suddenly start changing CBFS section names and splitting things up into multiple pieces I might as well throw all of this away and design a new file system from scratch that's more suited for verification.
I find the argument about wasting time hard to swallow since you are explicitly allowing CACHE_FULL to be a 1st class citizen on chrome os which inherently requires a re-walk of the entire metadata of the cbfs to re-validate the metadata for anything that falls out of the cache.
But you're comparing a common case time waste to an extremely rare case here. I mean in how many RW updates in Chrome OS history did we actually add a new file that didn't exist in the old version? And even then it would most likely fit because there's most likely a bunch of slack in the cache space anyway.
I think it's important to have as little brittleness from the RO binding to RW, and we're now enforcing a tight coupling that is forcing some not so optimal decisions.
I'm confused... isn't the point of having the fallback exactly that there's no tight coupling (the RW will still always work even if it exceeds the bounds that were presumed in the RO)?
Also, I don't think there's anything "brittle" about the fallback path -- it is not unsafe or insecure or in any way worse than using the mcache other than a tiny speed hit, and I would continue to sleep perfectly sound if we actually did end up hitting that very unlikely set of circumstances to trigger it on a board. That's the reason I implemented this fallback because I *don't* want people to have to worry about what happens if their update overflows the cache. And the speed hit is really just the cost of a normal CBFS lookup (without CONFIG_TOCTOU_SAFETY, which we're not planning to enable, the metadata hash doesn't get checked again in that path), the very thing we have always been doing all the time before this patch anyway, and only for the few files that actually exceed the cache (which on Chrome OS we could for example order to be files like ecrw that aren't needed on every boot).
So basically, the way I see this is that I uploaded an optimization patch here that can make booting maybe ~20ms faster, and what we're discussing right now is "but what if the stars align and something happens that probably never actually happened in practice in the past 5 years and cause this to only make the boot 19ms faster on one board?". From that point of view, I'm kinda surprised how concerned you are about this case.
especially fixing the size of the metadata cache when adding to cbmem when we have memory to play with to handle the full cache size.
I mean, if this would help allay your concerns I'm happy to add code to the CBMEM hook that would rebuild the full cache in DRAM if the pre-RAM cache had overflowed (although I think that the cumulated extra boot time from loading all the mcache building code into romstage on every board would far, far exceed the time saved in these very rare cases).
Also, FWIW we can really enforce requirements as high as we want on the Chrome OS side. The amount of real cache used is printed in the log so if you want us to always have at least XX% free space in our RW mcache or room for at least Y files, I can write you a FAFT test to guarantee that.
Hello build bot (Jenkins), Patrick Georgi, Martin Roth, Aaron Durbin,
I'd like you to reexamine a change. Please visit
https://review.coreboot.org/c/coreboot/+/38423
to look at the new patch set (#18).
Change subject: cbfs: Add metadata cache ......................................................................
cbfs: Add metadata cache
This patch adds a new CBFS "mcache" (metadata cache) -- a memory buffer that stores the headers of all CBFS files. Similar to the existing FMAP cache, this cache should reduce the amount of SPI accesses we need to do every boot: rather than having to re-read all CBFS headers from SPI flash every time we're looking for a file, we can just walk the same list in this in-memory copy and finally use it to directly access the flash at the right position for the file data.
This patch adds the code to support the cache but doesn't enable it on any platform. The next one will turn it on by default.
Change-Id: I5b1084bfdad1c6ab0ee1b143ed8dd796827f4c65 Signed-off-by: Julius Werner jwerner@chromium.org --- M src/commonlib/Makefile.inc A src/commonlib/bsd/cbfs_mcache.c M src/commonlib/bsd/include/commonlib/bsd/cb_err.h M src/commonlib/bsd/include/commonlib/bsd/cbfs_private.h M src/commonlib/include/commonlib/cbmem_id.h M src/include/cbfs.h M src/include/memlayout.h M src/include/symbols.h M src/lib/Kconfig M src/lib/cbfs.c M src/lib/coreboot_table.c M src/security/vboot/vboot_common.h M src/security/vboot/vboot_loader.c 13 files changed, 364 insertions(+), 33 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/23/38423/18
build bot (Jenkins) has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 18:
(2 comments)
https://review.coreboot.org/c/coreboot/+/38423/18/src/lib/cbfs.c File src/lib/cbfs.c:
https://review.coreboot.org/c/coreboot/+/38423/18/src/lib/cbfs.c@44 PS18, Line 44: if (!(cbd = cbfs_get_boot_device(true))) do not use assignment in if condition
https://review.coreboot.org/c/coreboot/+/38423/18/src/lib/cbfs.c@355 PS18, Line 355: if (cbmem_possibly_online() && do not use assignment in if condition
Aaron Durbin has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 18:
i386-elf-ld.bfd: section .id LMA [00000000ffffff4c,00000000ffffff7f] overlaps section .text LMA [00000000ffffc000,00000000ffffff4f] i386-elf-ld.bfd: /cb-build/coreboot-gerrit.0/default/INTEL_D945GCLF/cbfs/fallback/bootblock.debug: section .id lma 0xffffff4c adjusted to 0xffffff50 make[1]: *** [src/arch/x86/Makefile.inc:110: /cb-build/coreboot-gerrit.0/default/INTEL_D945GCLF/cbfs/fallback/bootblock.debug] Error 1
Aaron Durbin has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 18: Code-Review+2
Aaron Durbin has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 18:
(1 comment)
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/cbfs.c File src/lib/cbfs.c:
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/cbfs.c@28 PS10, Line 28: if (err == CB_CBFS_CACHE_FULL)
There are other ways such that RW_SECTION_A/B only has the next stage in its cbfs with another cbfs with the rest of the files.
RO -> RW_SECTION_A -> RW_SECTION_FULL
Yeah, but... do we want that? I feel like this would add a grotesque overcomplication to our otherwise neat and clean RO/RW CBFS concept to solve what I really think is a non-issue. I mean part of the whole reason I'm doing all of this is to maintain backwards-compatibility to old tooling, old payloads, etc. If we suddenly start changing CBFS section names and splitting things up into multiple pieces I might as well throw all of this away and design a new file system from scratch that's more suited for verification.
I don't follow your assertion of needing to throw all of this away. I don't think what I suggested should be applied to every user of coreboot. But we do have a real need in Chrome OS with RO/RW split. The surface area exposure of RW from RO should be minimized as much as possible. And the suggestion I made is about minimizing that exposure. I do not subscribe to the notion that falling back to boot media for lookups because of an increase in number of files in RW is a 'non-issue'. I actually think it goes against the larger intent and push to having TOCTOU capable system. In the end, we're just steering to the cbfs at well-defined boundaries. I don't think that is a major change that necessitates throwing everything out.
We have realities that pre-RAM environments are resource constrained. That environment necessitates making decisions to optimize for those constraints. Having mutliple cbfs that are walked through is not a huge leap to handle that. I will admit that our tooling around building cbfses is not great at all. I think we probably want to invest in better tools for helping aid in building all these things up.
This can be implemented over time, but I don't see why we would disregard a solution to a problem you're already expecting to happen. We should plug that gap, imo, if we think this is going to be common.
I think we need flexibility in chaining cbfses, though, because not everything fits into a simple solution space. Furquan brought up the CSE issue in the doc. It's a problem we do need to solve and not ignore because it doesn't fit into the cleanliness bucket. Having that chaining capability allows for flexibility in designing solutions to problems we need to tackle.
I find the argument about wasting time hard to swallow since you are explicitly allowing CACHE_FULL to be a 1st class citizen on chrome os which inherently requires a re-walk of the entire metadata of the cbfs to re-validate the metadata for anything that falls out of the cache.
But you're comparing a common case time waste to an extremely rare case here. I mean in how many RW updates in Chrome OS history did we actually add a new file that didn't exist in the old version? And even then it would most likely fit because there's most likely a bunch of slack in the cache space anyway.
I was going off of your assertion you expect the fallback to be the answer. My assumption is that we should never hit that fallback because it's not appropriate for our product. That is exactly why I suggested we add an option to fail in that case because things went terribly bad.
Historical updates aren't very helpful guidance since I anticipate updates to be happening more frequently along with features added to assist in product improvements and consistency across the fleet.
I think it's important to have as little brittleness from the RO binding to RW, and we're now enforcing a tight coupling that is forcing some not so optimal decisions.
I'm confused... isn't the point of having the fallback exactly that there's no tight coupling (the RW will still always work even if it exceeds the bounds that were presumed in the RO)?
I think that depends on definition of "work", though fallback definitely works in the sense of being load and use files not hit in metadata cache. That is a policy decision, though, that I don't believe we should employ. I don't think its appropriate to go back to boot media for lookups. We should be striving for doing those operations in-core.
Also, I don't think there's anything "brittle" about the fallback path -- it is not unsafe or insecure or in any way worse than using the mcache other than a tiny speed hit, and I would continue to sleep perfectly sound if we actually did end up hitting that very unlikely set of circumstances to trigger it on a board. That's the reason I implemented this fallback because I *don't* want people to have to worry about what happens if their update overflows the cache. And the speed hit is really just the cost of a normal CBFS lookup (without CONFIG_TOCTOU_SAFETY, which we're not planning to enable, the metadata hash doesn't get checked again in that path), the very thing we have always been doing all the time before this patch anyway, and only for the few files that actually exceed the cache (which on Chrome OS we could for example order to be files like ecrw that aren't needed on every boot).
I was referring to the coupling of RO and RW. Right now RO takes an offset/size and signature checks the whole thing. Data published by RO is the vboot result containing those attributes. That's not a ton of surface area. However, when building out RW cache and assuming a single cbfs, we now are running up against constraints of the pre-RAM environment and forcing those decisions leads to more limitations in the constraints. I think we should optimize for keeping dependencies on RO from RW as minimal as possible.
Our threat model in Chrome OS is changing and needs to change for business purposes. We should not just accept that since we did something in the past we shouldn't improve for the future. We can design solutions that allow people not to worry about pre-RAM mcache overflows. In order to order files one needs to characterize temporal access patterns and have the tools place files accordingly. That's the same type of work needed for arranging files in multiple cbfses based on access/use as well, fwiw.
So basically, the way I see this is that I uploaded an optimization patch here that can make booting maybe ~20ms faster, and what we're discussing right now is "but what if the stars align and something happens that probably never actually happened in practice in the past 5 years and cause this to only make the boot 19ms faster on one board?". From that point of view, I'm kinda surprised how concerned you are about this case.
Again, that was based on your indication you wanted to allow for it which I think is not what we should be doing. I was not basing my stance on performance what so ever.
Moreover, anything not hitting in the mcache needs to have a complete re-walk and recalculation of the metadata to determine it's legit metadata. Or were you wanting to leave the window of opportunity open from when the anchor was checked and when every file is accessed (and assumed the metadata is still correct)? I feel like it's not that much more effort to achieve that situation from ever happening. That can definitely be added later, but I don't think we should balk at achieving that goal.
especially fixing the size of the metadata cache when adding to cbmem when we have memory to play with to handle the full cache size.
I mean, if this would help allay your concerns I'm happy to add code to the CBMEM hook that would rebuild the full cache in DRAM if the pre-RAM cache had overflowed (although I think that the cumulated extra boot time from loading all the mcache building code into romstage on every board would far, far exceed the time saved in these very rare cases).
I don't think that's a great solution either when we can plug the gap completely.
Also, FWIW we can really enforce requirements as high as we want on the Chrome OS side. The amount of real cache used is printed in the log so if you want us to always have at least XX% free space in our RW mcache or room for at least Y files, I can write you a FAFT test to guarantee that.
Yes, and die() or whatever when we hit fallback path for Chrome OS. We should always be hitting the mcache. And making that happen will require a little more work/effort, but I don't think it's impossible to achieve.
Julius Werner has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 18:
(1 comment)
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/cbfs.c File src/lib/cbfs.c:
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/cbfs.c@28 PS10, Line 28: if (err == CB_CBFS_CACHE_FULL)
There are other ways such that RW_SECTION_A/B only has the next stage in its cbfs with another c […]
Sorry, let me just take a step back here because I feel like we're talking past each other a bit. I want to make sure that we're on the same page on a few fundamental things:
1. I am implementing two possible "levels" of security here: the one where only CONFIG_CBFS_VERIFICATION is enabled (every file and every bit of metadata is verified at least once per boot, before it is used), and the one where in addition to that CONFIG_TOCTOU_SAFETY is also enabled (every file and every bit of metadata is verified every time it is loaded, before those loaded bytes are used). It is up to the user which one to select, and selecting only the first one makes certain things faster, but both of those options are supposed to provide exactly the kinds of security guarantees they promise, with no holes, gaps or left-open windows. I am not planning to leave any security guarantee to be "added later" here (I mean, the code isn't all written yet but I have a plan of how it should look and I'm not going to mark those options as menuconfig-available and ready for use until it is), and if you think there's actually a guarantee that my design promises but doesn't quite keep in some edge case, please let me know.
2. Chrome OS is currently planning to *not* enable CONFIG_TOCTOU_SAFETY in the near-term (next couple of years). Our verification anchor scheme using Dauntless is not TOCTOU-safe, so there's no point trying to provide that guarantee in coreboot if there were already wide-open TOCTOU attack opportunities earlier in the boot flow. This was agreed upon with the security team when we designed RO verification. That's why when I'm giving examples specific to the Chrome OS case, I refer to how the code behaves with CONFIG_TOCTOU_SAFETY disabled. (In the case of an RW lookup falling back to flash because the mcache is full, you *only* need to verify all the metadata again when CONFIG_TOCTOU_SAFETY is enabled, because it was already verified once when the RW mcache was built. In Chrome OS the option would be disabled so it wouldn't have to do that and it would do a simpler, faster lookup with early termination. The code for this is sketched out in https://review.coreboot.org/c/coreboot/+/41120/10/src/lib/cbfs.c line 43 -- there's only a TODO comment there right now because the vboot integration isn't written yet, but the intention is that the metadata_hash variable passed to cbfs_lookup() will only not be NULL if CONFIG_TOCTOU_SAFETY is enabled.)
3. The whole point of the mcache is *only* to be a performance improvement, *nothing* else. None of the security guarantees provided by this design fundamentally rely on the mcache in any way. The system is just as secure when CONFIG_NO_CBFS_MCACHE is enabled, and lookups are just as secure when they fall back to flash because the RW mcache is full. The only thing you pay is extra time for reloading or reverifying metadata again. (CONFIG_TOCTOU_SAFETY currently depends on the mcache in Kconfig, and requires the RO mcache to be big enough to fit all files. That is a practical simplification for me, not a fundamental requirement. I could work around that requirement but to do that I would have to introduce a separate memlayout section to pass the RO metadata hash anchor forward to later stages. I don't expect there to really be a practical need for that specific configuration to work, so I decided to simplify things and save some implementation/maintenance effort by making the RO mcache a requirement for CONFIG_TOCTOU_SAFETY. This is *only* the RO mcache, you can set the RW mcache size to zero and still have a perfectly secure, perfectly TOCTOU-safe system that simply reverifies all the metadata on every RW file access.)
4. I agree with you that RO/RW coupling should be minimized in the sense that we should always strive to avoid limiting our options of what we can do with a future RW update. That some coupling must exist is, of course, a necessity, and I think you're underestimating it if you only think about the data persisted by vboot. We have a ton of memlayout sections that are shared between RO and RW and cannot be independently resized by the latter (the existing CBFS_CACHE, timestamps, CBMEM console, FMAP cache, the ROMSTAGE area, stack, page tables, etc.), so the mcache would just become one more out of many there. The important part, I think, is to make sure that on the rare chance where we find ourselves wanting to increase that area with an RW update but can't, this won't be catastrophic for us (i.e. prevent us from shipping that update or create some sort of security risk). I think my fallback path which still provides all the same security guarantees (with basically no extra maintenance overhead because it's reusing all the same code) for only a small performance penalty is the right solution to provide that certainty, and we have the ability (by enforcing minimum requirements through FAFT) to tune the risk of that performance penalty ever even coming into play to basically be as small as we want it to be.
Following these considerations I still believe that the model I'm presenting here is the best solution. I think it optimizes for low complexity and maintenance burden and for being fast in the common case, while guaranteeing both safety (i.e. not getting locked out of the ability to update) and security for all edge cases. I think the cost of causing a cache overflow is low (a small performance impact, nothing else), and the risk of getting there in the first place is tiny and can be individually adjusted by each user/platform depending on how worried they are about that performance impact and how much free SRAM/CAR space they can spare. (It should also be noted, of course, that platform constraints still put other restrictions on what an RW update can do, because there's only so much SRAM/CAR space to go around. Generally we try to make good use of the available space we have anyway, so on an SoC with a lot of SRAM we'd probably allocate more spare space to the CBFS mcache in the first place because there's no point in wasting it if we have some left over, decreasing the chance that mcache space would ever become a problem with an update. If a platform defines its mcache to be very tight to begin with there's probably a good reason for that, and even if the RW update was able to freely resize the cache it might not be able to free up enough space to fit what it needs to (even if there is another section that could be cannibalized a bit, that might not help if it's not adjacent to the mcache). In that scenario I'd much rather be able to take a small performance hit and keep on booting without mcache than to have a system that cannot be made to work at all if I can't find enough space.)
I do not like the approach of adding separate CBFS sections for the romstage because I think it would add a lot of complexity where I don't believe it is necessary, and it would break some of the backwards-compatibility I'm carefully trying to maintain here. I think those are serious problems and I don't see them standing in any relation to what I still really believe to be a non-issue (a rare chance at a small performance penalty in edge cases). The issue Furquan mentioned is a perfect example of a problem that will magically go away when this design is fully implemented, and I think we agreed on the doc now that they will just implement a stopgap solution for that which can later be discarded. (Fundamentally I'm not going to preclude the ability to use special CBFS sections and I'll make sure there are APIs for that -- e.g. I think the SMMSTORE code needs it, and on the payload side we have RW_LEGACY -- but doing that always adds complexity and that extra complexity needs to be justified somehow, it's only warranted when there is no simpler way to achieve the same goals. I really don't see that justification here.)
If you really think this needs to go in a different direction let's maybe set up a VC to talk it through, that might be easier than discussing it here?
Aaron Durbin has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 18:
(1 comment)
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/cbfs.c File src/lib/cbfs.c:
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/cbfs.c@28 PS10, Line 28: if (err == CB_CBFS_CACHE_FULL)
- Chrome OS is currently planning to *not* enable CONFIG_TOCTOU_SAFETY in the near-term (next couple of years). Our verification anchor scheme using Dauntless is not TOCTOU-safe, so there's no point trying to provide that guarantee in coreboot if there were already wide-open TOCTOU attack opportunities earlier in the boot flow. This was agreed upon with the security team when we designed RO verification. That's why when I'm giving examples specific to the Chrome OS case, I refer to how the code behaves with CONFIG_TOCTOU_SAFETY disabled. (In the case of an RW lookup falling back to flash because the mcache is full, you *only* need to verify all the metadata again when CONFIG_TOCTOU_SAFETY is enabled, because it was already verified once when the RW mcache was built. In Chrome OS the option would be disabled so it wouldn't have to do that and it would do a simpler, faster lookup with early termination. The code for this is sketched out in https://review.coreboot.org/c/coreboot/+/41120/10/src/lib/cbfs.c line 43 -- there's only a TODO comment there right now because the vboot integration isn't written yet, but the intention is that the metadata_hash variable passed to cbfs_lookup() will only not be NULL if CONFIG_TOCTOU_SAFETY is enabled.)
The TOCTOU window of exploit is between dauntless check and the chipset pulling code and anchor in-core. I agree there. After that, with the anchor maintained in-core, one can continue to implement whatever policy necessary.
- The whole point of the mcache is *only* to be a performance improvement, *nothing* else. None of the security guarantees provided by this design fundamentally rely on the mcache in any way. The system is just as secure when CONFIG_NO_CBFS_MCACHE is enabled, and lookups are just as secure when they fall back to flash because the RW mcache is full. The only thing you pay is extra time for reloading or reverifying metadata again. (CONFIG_TOCTOU_SAFETY currently depends on the mcache in Kconfig, and requires the RO mcache to be big enough to fit all files. That is a practical simplification for me, not a fundamental requirement. I could work around that requirement but to do that I would have to introduce a separate memlayout section to pass the RO metadata hash anchor forward to later stages. I don't expect there to really be a practical need for that specific configuration to work, so I decided to simplify things and save some implementation/maintenance effort by making the RO mcache a requirement for CONFIG_TOCTOU_SAFETY. This is *only* the RO mcache, you can set the RW mcache size to zero and still have a perfectly secure, perfectly TOCTOU-safe system that simply reverifies all the metadata on every RW file access.)
It was not clear to me at all that we were incrementally only doing this for RO verification. Yes, I agree if you don't want to maintain the bits necessary for closing known-but-closeable TOCTOU windows then you don't need to worry about the things you mentioned above. To me, it seemed we have that ability, but we're choosing not to which I don't understand why. I get that one can argue that the first window is not closed so don't close any others, but the building blocks are there -- especially if one wanted to use the chipset proprietary implementations to anchor root of trust as well. It would work without much more effort. But we're purposefully not going down that path.
- I agree with you that RO/RW coupling should be minimized in the sense that we should always strive to avoid limiting our options of what we can do with a future RW update. That some coupling must exist is, of course, a necessity, and I think you're underestimating it if you only think about the data persisted by vboot. We have a ton of memlayout sections that are shared between RO and RW and cannot be independently resized by the latter (the existing CBFS_CACHE, timestamps, CBMEM console, FMAP cache, the ROMSTAGE area, stack, page tables, etc.), so the mcache would just become one more out of many there. The important part, I think, is to make sure that on the rare chance where we find ourselves wanting to increase that area with an RW update but can't, this won't be catastrophic for us (i.e. prevent us from shipping that update or create some sort of security risk). I think my fallback path which still provides all the same security guarantees (with basically no extra maintenance overhead because it's reusing all the same code) for only a small performance penalty is the right solution to provide that certainty, and we have the ability (by enforcing minimum requirements through FAFT) to tune the risk of that performance penalty ever even coming into play to basically be as small as we want it to be.
I'm not ignoring the existing things where we do have coupling. But in this design we are adding more coupling and choosing to make the surface area larger. With the premise that we're not trying to improve aside from RO verification, then that approach perfectly makes sense. I was looking at things from a loftier standpoint and setting us up where we can obtain TOCTOU safety. Those resource constrained environments inherently require one to chain things through instead of doing everything. It allows for the most flexibility while not having differences across platforms. That work will definitely have to be done in the future since it can't be ignored.
Following these considerations I still believe that the model I'm presenting here is the best solution. I think it optimizes for low complexity and maintenance burden and for being fast in the common case, while guaranteeing both safety (i.e. not getting locked out of the ability to update) and security for all edge cases. I think the cost of causing a cache overflow is low (a small performance impact, nothing else), and the risk of getting there in the first place is tiny and can be individually adjusted by each user/platform depending on how worried they are about that performance impact and how much free SRAM/CAR space they can spare. (It should also be noted, of course, that platform constraints still put other restrictions on what an RW update can do, because there's only so much SRAM/CAR space to go around. Generally we try to make good use of the available space we have anyway, so on an SoC with a lot of SRAM we'd probably allocate more spare space to the CBFS mcache in the first place because there's no point in wasting it if we have some left over, decreasing the chance that mcache space would ever become a problem with an update. If a platform defines its mcache to be very tight to begin with there's probably a good reason for that, and even if the RW update was able to freely resize the cache it might not be able to free up enough space to fit what it needs to (even if there is another section that could be cannibalized a bit, that might not help if it's not adjacent to the mcache). In that scenario I'd much rather be able to take a small performance hit and keep on booting without mcache than to have a system that cannot be made to work at all if I can't find enough space.)
I don't think we should accept so many differences across platforms when we know we can solve for consistency. I laid out a way to not hit those resource constrained barriers related to RW cbfs, and we will absolutely need to do that in the future.
I do not like the approach of adding separate CBFS sections for the romstage because I think it would add a lot of complexity where I don't believe it is necessary, and it would break some of the backwards-compatibility I'm carefully trying to maintain here. I think those are serious problems and I don't see them standing in any relation to what I still really believe to be a non-issue (a rare chance at a small performance penalty in edge cases). The issue Furquan mentioned is a perfect example of a problem that will magically go away when this design is fully implemented, and I think we agreed on the doc now that they will just implement a stopgap solution for that which can later be discarded. (Fundamentally I'm not going to preclude the ability to use special CBFS sections and I'll make sure there are APIs for that -- e.g. I think the SMMSTORE code needs it, and on the payload side we have RW_LEGACY -- but doing that always adds complexity and that extra complexity needs to be justified somehow, it's only warranted when there is no simpler way to achieve the same goals. I really don't see that justification here.)
I don't understand the complexity assertion nor backwards compatibility. It's re-steering to another cbfs in time assuming a linear bootflow. That fits just fine in the existing code by updating rdev of the cbfs in the cbfs boot device thing. Anyway it'll need to be addressed eventually, and all this code can definitely be used for whatever solution needed in the future.
If you really think this needs to go in a different direction let's maybe set up a VC to talk it through, that might be easier than discussing it here?
Sure. I'll follow up on email.
Julius Werner has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 18:
(1 comment)
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/cbfs.c File src/lib/cbfs.c:
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/cbfs.c@28 PS10, Line 28: if (err == CB_CBFS_CACHE_FULL)
- The whole point of the mcache is *only* to be a performance improvement, *nothing* else. None of the security guarantees provided by this design fundamentally rely on the mcache in any way. The system is just as secure when CONFIG_NO_CBFS_MCACHE is enabled, and lookups are just as secure when they fall back to flash because the RW mcache is full. The only thing you pay is extra time for reloading or reverifying metadata again. (CONFIG_TOCTOU_SAFETY currently depends on the mcache in Kconfig, and requires the RO mcache to be big enough to fit all files. That is a practical simplification for me, not a fundamental requirement. I could work around that requirement but to do that I would have to introduce a separate memlayout section to pass the RO metadata hash anchor forward to later stages. I don't expect there to really be a practical need for that specific configuration to work, so I decided to simplify things and save some implementation/maintenance effort by making the RO mcache a requirement for CONFIG_TOCTOU_SAFETY. This is *only* the RO mcache, you can set the RW mcache size to zero and still have a perfectly secure, perfectly TOCTOU-safe system that simply reverifies all the metadata on every RW file access.)
It was not clear to me at all that we were incrementally only doing this for RO verification. Yes, I agree if you don't want to maintain the bits necessary for closing known-but-closeable TOCTOU windows then you don't need to worry about the things you mentioned above. To me, it seemed we have that ability, but we're choosing not to which I don't understand why. I get that one can argue that the first window is not closed so don't close any others, but the building blocks are there -- especially if one wanted to use the chipset proprietary implementations to anchor root of trust as well. It would work without much more effort. But we're purposefully not going down that path.
Sorry, I really don't see the relation between what I wrote and what you wrote here, I don't get the impression you understood what I was trying to say. Again, I am *not leaving any gaps, holes or known-but-closeable windows open* in my design. If you enable the CONFIG_TOCTOU_SAFETY option in Kconfig, the design *will be TOCTOU-safe*! Period. This is not a temporary incremental RO-only thing, this will hold true for the final design for both the RO and RW CBFS. If you verify the anchor in a TOCTOU-safe way (e.g. using fused SoC hashing) and you run this on a non-XIP platform, you will have an entirely TOCTOU-safe boot flow start to end, RO and RW, no other open windows remaining. Even when accessing a file that did not fit in the mcache, this implementation will do so in an entirely TOCTOU-safe manner. The mcache overflow issue does *not* threaten TOCTOU-safety or any other security guarantee in *any* way. I keep saying that but I don't get the impression you hear me to be honest.
I don't understand the complexity assertion nor backwards compatibility. It's re-steering to another cbfs in time assuming a linear bootflow. That fits just fine in the existing code by updating rdev of the cbfs in the cbfs boot device thing. Anyway it'll need to be addressed eventually, and all this code can definitely be used for whatever solution needed in the future.
What you're proposing requires every platform to carefully separate its CBFS files based on what stage they are needed in. Some files are needed in both pre-RAM and post-RAM stages, so then what -- either you have to store them twice or you have to provide extra APIs to allow one side to access the CBFS of the other, and place the cognitive burden of figuring out what to use where onto each platform maintainer. It also requires a bunch extra plumbing to set up yet another mcache instance and reserve space for that in memlayout and...
I disagree that this "needs to be addressed eventually", because my solution doesn't need this, not now and not in the future. My solution can solve this problem and still provide all the same security guarantees you want without adding any of this stuff. That is why I think it is better.
Aaron Durbin has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 18:
(1 comment)
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/cbfs.c File src/lib/cbfs.c:
https://review.coreboot.org/c/coreboot/+/38423/10/src/lib/cbfs.c@28 PS10, Line 28: if (err == CB_CBFS_CACHE_FULL)
Sorry, I really don't see the relation between what I wrote and what you wrote here, I don't get the impression you understood what I was trying to say. Again, I am *not leaving any gaps, holes or known-but-closeable windows open* in my design. If you enable the CONFIG_TOCTOU_SAFETY option in Kconfig, the design *will be TOCTOU-safe*! Period. This is not a temporary incremental RO-only thing, this will hold true for the final design for both the RO and RW CBFS. If you verify the anchor in a TOCTOU-safe way (e.g. using fused SoC hashing) and you run this on a non-XIP platform, you will have an entirely TOCTOU-safe boot flow start to end, RO and RW, no other open windows remaining. Even when accessing a file that did not fit in the mcache, this implementation will do so in an entirely TOCTOU-safe manner. The mcache overflow issue does *not* threaten TOCTOU-safety or any other security guarantee in *any* way. I keep saying that but I don't get the impression you hear me to be honest.
I might be confused. I was under the impression that we weren't going to be enabling CONFIG_TOCTOU_SAFETY on Chrome OS. And I think we should. I'm probably misunderstanding.
What you're proposing requires every platform to carefully separate its CBFS files based on what stage they are needed in. Some files are needed in both pre-RAM and post-RAM stages, so then what -- either you have to store them twice or you have to provide extra APIs to allow one side to access the CBFS of the other, and place the cognitive burden of figuring out what to use where onto each platform maintainer. It also requires a bunch extra plumbing to set up yet another mcache instance and reserve space for that in memlayout and...
I don't think the implementation would need to be what you mentioned.
I disagree that this "needs to be addressed eventually", because my solution doesn't need this, not now and not in the future. My solution can solve this problem and still provide all the same security guarantees you want without adding any of this stuff. That is why I think it is better.
I think it depends on what we would be optimizing for on specific platforms in a pre-ram environment. Those resources are limited which inherently makes one need to optimize for something. If we're reliant upon the seeding full RW CBFS metadata in RO (and dependent on number RW files) then we're forcing our hand w.r.t. tradeoffs when one could fill mcache partially. I don't believe every platform is the same, and I'm worried we're not dealing with the limited resource problem up front. That's where I'm coming from, at least.
build bot (Jenkins) has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 19:
(2 comments)
https://review.coreboot.org/c/coreboot/+/38423/19/src/lib/cbfs.c File src/lib/cbfs.c:
https://review.coreboot.org/c/coreboot/+/38423/19/src/lib/cbfs.c@44 PS19, Line 44: if (!(cbd = cbfs_get_boot_device(true))) do not use assignment in if condition
https://review.coreboot.org/c/coreboot/+/38423/19/src/lib/cbfs.c@355 PS19, Line 355: if (cbmem_possibly_online() && do not use assignment in if condition
build bot (Jenkins) has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 20:
(2 comments)
https://review.coreboot.org/c/coreboot/+/38423/20/src/lib/cbfs.c File src/lib/cbfs.c:
https://review.coreboot.org/c/coreboot/+/38423/20/src/lib/cbfs.c@44 PS20, Line 44: if (!(cbd = cbfs_get_boot_device(true))) do not use assignment in if condition
https://review.coreboot.org/c/coreboot/+/38423/20/src/lib/cbfs.c@355 PS20, Line 355: if (cbmem_possibly_online() && do not use assignment in if condition
build bot (Jenkins) has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
Patch Set 21:
(2 comments)
https://review.coreboot.org/c/coreboot/+/38423/21/src/lib/cbfs.c File src/lib/cbfs.c:
https://review.coreboot.org/c/coreboot/+/38423/21/src/lib/cbfs.c@44 PS21, Line 44: if (!(cbd = cbfs_get_boot_device(true))) do not use assignment in if condition
https://review.coreboot.org/c/coreboot/+/38423/21/src/lib/cbfs.c@355 PS21, Line 355: if (cbmem_possibly_online() && do not use assignment in if condition
Philipp Deppenwiese has submitted this change. ( https://review.coreboot.org/c/coreboot/+/38423 )
Change subject: cbfs: Add metadata cache ......................................................................
cbfs: Add metadata cache
This patch adds a new CBFS "mcache" (metadata cache) -- a memory buffer that stores the headers of all CBFS files. Similar to the existing FMAP cache, this cache should reduce the amount of SPI accesses we need to do every boot: rather than having to re-read all CBFS headers from SPI flash every time we're looking for a file, we can just walk the same list in this in-memory copy and finally use it to directly access the flash at the right position for the file data.
This patch adds the code to support the cache but doesn't enable it on any platform. The next one will turn it on by default.
Change-Id: I5b1084bfdad1c6ab0ee1b143ed8dd796827f4c65 Signed-off-by: Julius Werner jwerner@chromium.org Reviewed-on: https://review.coreboot.org/c/coreboot/+/38423 Tested-by: build bot (Jenkins) no-reply@coreboot.org Reviewed-by: Aaron Durbin adurbin@chromium.org --- M src/commonlib/Makefile.inc A src/commonlib/bsd/cbfs_mcache.c M src/commonlib/bsd/include/commonlib/bsd/cb_err.h M src/commonlib/bsd/include/commonlib/bsd/cbfs_private.h M src/commonlib/include/commonlib/cbmem_id.h M src/include/cbfs.h M src/include/memlayout.h M src/include/symbols.h M src/lib/Kconfig M src/lib/cbfs.c M src/lib/coreboot_table.c M src/security/vboot/vboot_common.h M src/security/vboot/vboot_loader.c 13 files changed, 364 insertions(+), 33 deletions(-)
Approvals: build bot (Jenkins): Verified Aaron Durbin: Looks good to me, approved
diff --git a/src/commonlib/Makefile.inc b/src/commonlib/Makefile.inc index b2225cb..1a38e4a 100644 --- a/src/commonlib/Makefile.inc +++ b/src/commonlib/Makefile.inc @@ -37,6 +37,13 @@ ramstage-y += bsd/cbfs_private.c smm-y += bsd/cbfs_private.c
+bootblock-y += bsd/cbfs_mcache.c +verstage-y += bsd/cbfs_mcache.c +romstage-y += bsd/cbfs_mcache.c +postcar-y += bsd/cbfs_mcache.c +ramstage-y += bsd/cbfs_mcache.c +smm-y += bsd/cbfs_mcache.c + decompressor-y += bsd/lz4_wrapper.c bootblock-y += bsd/lz4_wrapper.c verstage-y += bsd/lz4_wrapper.c diff --git a/src/commonlib/bsd/cbfs_mcache.c b/src/commonlib/bsd/cbfs_mcache.c new file mode 100644 index 0000000..f54b663 --- /dev/null +++ b/src/commonlib/bsd/cbfs_mcache.c @@ -0,0 +1,143 @@ +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later */ + +#include <assert.h> +#include <commonlib/bsd/cbfs_private.h> + +/* + * A CBFS metadata cache is an in memory data structure storing CBFS file headers (= metadata). + * It is defined by its start pointer and size. It contains a sequence of variable-length + * union mcache_entry entries. There is no overall header structure for the cache. + * + * Each mcache_entry is the raw metadata for a CBFS file (including attributes) in the same form + * as stored on flash (i.e. values in big-endian), except that the CBFS magic signature in the + * first 8 bytes ('LARCHIVE') is overwritten with mcache-internal bookkeeping data. The first 4 + * bytes are a magic number (MCACHE_MAGIC_FILE) and the next 4 bytes are the absolute offset in + * bytes on the cbfs_dev_t that this metadata blob was found at. (Note that depending on the + * implementation of cbfs_dev_t, this offset may still be relative to the start of a subregion + * of the underlying storage device.) + * + * The length of an mcache_entry (i.e. length of the underlying metadata blob) is encoded in the + * metadata (entry->file.h.offset). The next mcache_entry begins at the next + * CBFS_MCACHE_ALIGNMENT boundary after that. The cache is terminated by a special 4-byte + * mcache_entry that consists only of a magic number (MCACHE_MAGIC_END or MCACHE_MAGIC_FULL). + */ + +#define MCACHE_MAGIC_FILE 0x454c4946 /* 'FILE' */ +#define MCACHE_MAGIC_FULL 0x4c4c5546 /* 'FULL' */ +#define MCACHE_MAGIC_END 0x444e4524 /* '$END' */ + +union mcache_entry { + union cbfs_mdata file; + struct { /* These fields exactly overlap file.h.magic */ + uint32_t magic; + uint32_t offset; + }; +}; + +struct cbfs_mcache_build_args { + void *mcache; + void *end; + int count; +}; + +static cb_err_t build_walker(cbfs_dev_t dev, size_t offset, const union cbfs_mdata *mdata, + size_t already_read, void *arg) +{ + struct cbfs_mcache_build_args *args = arg; + union mcache_entry *entry = args->mcache; + const uint32_t data_offset = be32toh(mdata->h.offset); + + if (args->end - args->mcache < data_offset) + return CB_CBFS_CACHE_FULL; + + if (cbfs_copy_fill_metadata(args->mcache, mdata, already_read, dev, offset)) + return CB_CBFS_IO; + + entry->magic = MCACHE_MAGIC_FILE; + entry->offset = offset; + + args->mcache += ALIGN_UP(data_offset, CBFS_MCACHE_ALIGNMENT); + args->count++; + + return CB_CBFS_NOT_FOUND; +} + +cb_err_t cbfs_mcache_build(cbfs_dev_t dev, void *mcache, size_t size, + struct vb2_hash *metadata_hash) +{ + struct cbfs_mcache_build_args args = { + .mcache = mcache, + .end = mcache + ALIGN_DOWN(size, CBFS_MCACHE_ALIGNMENT) + - sizeof(uint32_t), /* leave space for terminating magic */ + .count = 0, + }; + + assert(size > sizeof(uint32_t) && IS_ALIGNED((uintptr_t)mcache, CBFS_MCACHE_ALIGNMENT)); + cb_err_t ret = cbfs_walk(dev, build_walker, &args, metadata_hash, 0); + union mcache_entry *entry = args.mcache; + if (ret == CB_CBFS_NOT_FOUND) { + ret = CB_SUCCESS; + entry->magic = MCACHE_MAGIC_END; + } else if (ret == CB_CBFS_CACHE_FULL) { + ERROR("mcache overflow, should increase CBFS_MCACHE size!\n"); + entry->magic = MCACHE_MAGIC_FULL; + } + + LOG("mcache @%p built for %d files, used %#zx of %#zx bytes\n", mcache, + args.count, args.mcache + sizeof(entry->magic) - mcache, size); + return ret; +} + +cb_err_t cbfs_mcache_lookup(const void *mcache, size_t mcache_size, const char *name, + union cbfs_mdata *mdata_out, size_t *data_offset_out) +{ + const size_t namesize = strlen(name) + 1; /* Count trailing \0 so we can memcmp() it. */ + const void *end = mcache + mcache_size; + const void *current = mcache; + + while (current + sizeof(uint32_t) < end) { + const union mcache_entry *entry = current; + + if (entry->magic == MCACHE_MAGIC_END) + return CB_CBFS_NOT_FOUND; + if (entry->magic == MCACHE_MAGIC_FULL) + return CB_CBFS_CACHE_FULL; + + assert(entry->magic == MCACHE_MAGIC_FILE); + const uint32_t data_offset = be32toh(entry->file.h.offset); + const uint32_t data_length = be32toh(entry->file.h.len); + if (namesize <= data_offset - offsetof(union cbfs_mdata, filename) && + memcmp(name, entry->file.filename, namesize) == 0) { + LOG("Found '%s' @%#x size %#x in mcache @%p\n", + name, entry->offset, data_length, current); + *data_offset_out = entry->offset + data_offset; + memcpy(mdata_out, &entry->file, data_offset); + return CB_SUCCESS; + } + + current += ALIGN_UP(data_offset, CBFS_MCACHE_ALIGNMENT); + } + + ERROR("CBFS mcache overflow!\n"); + return CB_ERR; +} + +size_t cbfs_mcache_real_size(const void *mcache, size_t mcache_size) +{ + const void *end = mcache + mcache_size; + const void *current = mcache; + + while (current + sizeof(uint32_t) < end) { + const union mcache_entry *entry = current; + + if (entry->magic == MCACHE_MAGIC_FULL || entry->magic == MCACHE_MAGIC_END) { + current += sizeof(entry->magic); + break; + } + + assert(entry->magic == MCACHE_MAGIC_FILE); + current += ALIGN_UP(be32toh(entry->file.h.offset), CBFS_MCACHE_ALIGNMENT); + } + + return current - mcache; +} diff --git a/src/commonlib/bsd/include/commonlib/bsd/cb_err.h b/src/commonlib/bsd/include/commonlib/bsd/cb_err.h index e5aa852..b61b956 100644 --- a/src/commonlib/bsd/include/commonlib/bsd/cb_err.h +++ b/src/commonlib/bsd/include/commonlib/bsd/cb_err.h @@ -39,6 +39,7 @@ CB_CBFS_IO = -400, /**< Underlying I/O error */ CB_CBFS_NOT_FOUND = -401, /**< File not found in directory */ CB_CBFS_HASH_MISMATCH = -402, /**< Master hash validation failed */ + CB_CBFS_CACHE_FULL = -403, /**< Metadata cache overflowed */ };
/* Don't typedef the enum directly, so the size is unambiguous for serialization. */ diff --git a/src/commonlib/bsd/include/commonlib/bsd/cbfs_private.h b/src/commonlib/bsd/include/commonlib/bsd/cbfs_private.h index aaee62f..64dcf9f 100644 --- a/src/commonlib/bsd/include/commonlib/bsd/cbfs_private.h +++ b/src/commonlib/bsd/include/commonlib/bsd/cbfs_private.h @@ -113,4 +113,25 @@ cb_err_t cbfs_lookup(cbfs_dev_t dev, const char *name, union cbfs_mdata *mdata_out, size_t *data_offset_out, struct vb2_hash *metadata_hash);
+/* Both base address and size of CBFS mcaches must be aligned to this value! */ +#define CBFS_MCACHE_ALIGNMENT sizeof(uint32_t) /* Largest data type used in CBFS */ + +/* Build an in-memory CBFS metadata cache out of the CBFS on |dev| into a |mcache_size| bytes + * memory area at |mcache|. Also verify |metadata_hash| unless it is NULL. If this returns + * CB_CBFS_CACHE_FULL, the mcache is still valid and can be used, but lookups may return + * CB_CBFS_CACHE_FULL for files that didn't fit to indicate that the caller needs to fall back + * to cbfs_lookup(). */ +cb_err_t cbfs_mcache_build(cbfs_dev_t dev, void *mcache, size_t mcache_size, + struct vb2_hash *metadata_hash); + +/* + * Find a file named |name| in a CBFS metadata cache and copy its metadata into |mdata_out|. + * Pass out offset to the file data (on the original CBFS device used for cbfs_mcache_build()). + */ +cb_err_t cbfs_mcache_lookup(const void *mcache, size_t mcache_size, const char *name, + union cbfs_mdata *mdata_out, size_t *data_offset_out); + +/* Returns the amount of bytes actually used by the CBFS metadata cache in |mcache|. */ +size_t cbfs_mcache_real_size(const void *mcache, size_t mcache_size); + #endif /* _COMMONLIB_BSD_CBFS_PRIVATE_H_ */ diff --git a/src/commonlib/include/commonlib/cbmem_id.h b/src/commonlib/include/commonlib/cbmem_id.h index 6b4d604..ab7cf63 100644 --- a/src/commonlib/include/commonlib/cbmem_id.h +++ b/src/commonlib/include/commonlib/cbmem_id.h @@ -67,6 +67,8 @@ #define CBMEM_ID_ROM2 0x524f4d32 #define CBMEM_ID_ROM3 0x524f4d33 #define CBMEM_ID_FMAP 0x464d4150 +#define CBMEM_ID_CBFS_RO_MCACHE 0x524d5346 +#define CBMEM_ID_CBFS_RW_MCACHE 0x574d5346 #define CBMEM_ID_FSP_LOGO 0x4c4f474f #define CBMEM_ID_SMM_COMBUFFER 0x53534d32
@@ -129,5 +131,7 @@ { CBMEM_ID_ROM1, "VGA ROM #1 "}, \ { CBMEM_ID_ROM2, "VGA ROM #2 "}, \ { CBMEM_ID_ROM3, "VGA ROM #3 "}, \ - { CBMEM_ID_FMAP, "FMAP "}, + { CBMEM_ID_FMAP, "FMAP "}, \ + { CBMEM_ID_CBFS_RO_MCACHE, "RO MCACHE "}, \ + { CBMEM_ID_CBFS_RW_MCACHE, "RW MCACHE "} #endif /* _CBMEM_ID_H_ */ diff --git a/src/include/cbfs.h b/src/include/cbfs.h index a35597d..32ed7f8 100644 --- a/src/include/cbfs.h +++ b/src/include/cbfs.h @@ -3,10 +3,10 @@ #ifndef _CBFS_H_ #define _CBFS_H_
+#include <cbmem.h> #include <commonlib/cbfs.h> #include <program_loading.h> -#include <stddef.h> -#include <stdint.h> +#include <types.h>
/*********************************************** * Perform CBFS operations on the boot device. * @@ -42,8 +42,21 @@ /* Load stage into memory filling in prog. Return 0 on success. < 0 on error. */ int cbfs_prog_stage_load(struct prog *prog);
-/* Returns the region device of the currently active CBFS. - Return < 0 on error, 0 on success. */ -int cbfs_boot_region_device(struct region_device *rdev); +struct cbfs_boot_device { + struct region_device rdev; + void *mcache; + size_t mcache_size; +}; + +/* Helper to fill out |mcache| and |mcache_size| in a cbfs_boot_device. */ +void cbfs_boot_device_find_mcache(struct cbfs_boot_device *cbd, uint32_t id); + +/* + * Retrieves the currently active CBFS boot device. If |force_ro| is set, will + * always return the read-only CBFS instead (this only makes a difference when + * CONFIG(VBOOT) is enabled). May perform certain CBFS initialization tasks. + * Returns NULL on error (e.g. boot device IO error). + */ +const struct cbfs_boot_device *cbfs_get_boot_device(bool force_ro);
#endif diff --git a/src/include/memlayout.h b/src/include/memlayout.h index bd1d684..bf830b7 100644 --- a/src/include/memlayout.h +++ b/src/include/memlayout.h @@ -71,6 +71,9 @@ _ = ASSERT(sz >= FMAP_SIZE, \ STR(FMAP does not fit in FMAP_CACHE! (sz < FMAP_SIZE)));
+#define CBFS_MCACHE(addr, sz) \ + REGION(cbfs_mcache, addr, sz, 4) + #if ENV_ROMSTAGE_OR_BEFORE #define PRERAM_CBFS_CACHE(addr, size) \ REGION(preram_cbfs_cache, addr, size, 4) \ diff --git a/src/include/symbols.h b/src/include/symbols.h index 371d84b..6fe24f5 100644 --- a/src/include/symbols.h +++ b/src/include/symbols.h @@ -33,6 +33,7 @@ DECLARE_REGION(preram_cbfs_cache) DECLARE_REGION(postram_cbfs_cache) DECLARE_REGION(cbfs_cache) +DECLARE_REGION(cbfs_mcache) DECLARE_REGION(fmap_cache) DECLARE_REGION(tpm_tcpa_log)
diff --git a/src/lib/Kconfig b/src/lib/Kconfig index d6e7e51..ab2b9c5 100644 --- a/src/lib/Kconfig +++ b/src/lib/Kconfig @@ -80,3 +80,24 @@ help This option enables eSPI library helper functions for displaying debug information. + +config NO_CBFS_MCACHE + bool + default y + help + Disables the CBFS metadata cache. This means that your platform does + not need to provide a CBFS_MCACHE section in memlayout and can save + the associated CAR/SRAM size. In that case every single CBFS file + lookup must re-read the same CBFS directory entries from flash to find + the respective file. + +config CBFS_MCACHE_RW_PERCENTAGE + int + depends on VBOOT && !NO_CBFS_MCACHE + default 25 if CHROMEOS # Chrome OS stores many L10n files in RO only + default 50 + help + The amount of the CBFS_MCACHE area that's used for the RW CBFS, in + percent from 0 to 100. The remaining area will be used for the RO + CBFS. Default is an even 50/50 split. When VBOOT is disabled, this + will automatically be 0 (meaning the whole MCACHE is used for RO). diff --git a/src/lib/cbfs.c b/src/lib/cbfs.c index 447d91f..3b7d429 100644 --- a/src/lib/cbfs.c +++ b/src/lib/cbfs.c @@ -3,6 +3,7 @@ #include <assert.h> #include <boot_device.h> #include <cbfs.h> +#include <cbmem.h> #include <commonlib/bsd/cbfs_private.h> #include <commonlib/bsd/compression.h> #include <commonlib/endian.h> @@ -16,22 +17,33 @@ #include <symbols.h> #include <timestamp.h>
+static cb_err_t cbfs_boot_lookup(const struct cbfs_boot_device *cbd, + const char *name, union cbfs_mdata *mdata, size_t *data_offset) +{ + cb_err_t err = CB_CBFS_CACHE_FULL; + if (!CONFIG(NO_CBFS_MCACHE) && !ENV_SMM) + err = cbfs_mcache_lookup(cbd->mcache, cbd->mcache_size, + name, mdata, data_offset); + if (err == CB_CBFS_CACHE_FULL) + err = cbfs_lookup(&cbd->rdev, name, mdata, data_offset, NULL); + return err; +} + int cbfs_boot_locate(struct cbfsf *fh, const char *name, uint32_t *type) { - struct region_device rdev; - - if (cbfs_boot_region_device(&rdev)) + const struct cbfs_boot_device *cbd = cbfs_get_boot_device(false); + if (!cbd) return -1;
size_t data_offset; - cb_err_t err = cbfs_lookup(&rdev, name, &fh->mdata, &data_offset, NULL); + cb_err_t err = cbfs_boot_lookup(cbd, name, &fh->mdata, &data_offset);
if (CONFIG(VBOOT_ENABLE_CBFS_FALLBACK) && err == CB_CBFS_NOT_FOUND) { printk(BIOS_INFO, "CBFS: Fall back to RO region for %s\n", name); - if (fmap_locate_area_as_rdev("COREBOOT", &rdev)) + if (!(cbd = cbfs_get_boot_device(true))) return -1; - err = cbfs_lookup(&rdev, name, &fh->mdata, &data_offset, NULL); + err = cbfs_boot_lookup(cbd, name, &fh->mdata, &data_offset); } if (err) return -1; @@ -39,7 +51,8 @@ size_t msize = be32toh(fh->mdata.h.offset); if (rdev_chain(&fh->metadata, &addrspace_32bit.rdev, (uintptr_t)&fh->mdata, msize) || - rdev_chain(&fh->data, &rdev, data_offset, be32toh(fh->mdata.h.len))) + rdev_chain(&fh->data, &cbd->rdev, data_offset, + be32toh(fh->mdata.h.len))) return -1; if (type) { if (!*type) @@ -333,9 +346,86 @@ return 0; }
-int cbfs_boot_region_device(struct region_device *rdev) +void cbfs_boot_device_find_mcache(struct cbfs_boot_device *cbd, uint32_t id) { - boot_device_init(); - return vboot_locate_cbfs(rdev) && - fmap_locate_area_as_rdev("COREBOOT", rdev); + if (CONFIG(NO_CBFS_MCACHE) || ENV_SMM) + return; + + const struct cbmem_entry *entry; + if (cbmem_possibly_online() && + (entry = cbmem_entry_find(id))) { + cbd->mcache = cbmem_entry_start(entry); + cbd->mcache_size = cbmem_entry_size(entry); + } else if (ENV_ROMSTAGE_OR_BEFORE) { + u8 *boundary = _ecbfs_mcache - REGION_SIZE(cbfs_mcache) * + CONFIG_CBFS_MCACHE_RW_PERCENTAGE / 100; + boundary = (u8 *)ALIGN_DOWN((uintptr_t)boundary, + CBFS_MCACHE_ALIGNMENT); + if (id == CBMEM_ID_CBFS_RO_MCACHE) { + cbd->mcache = _cbfs_mcache; + cbd->mcache_size = boundary - _cbfs_mcache; + } else if (id == CBMEM_ID_CBFS_RW_MCACHE) { + cbd->mcache = boundary; + cbd->mcache_size = _ecbfs_mcache - boundary; + } + } } + +const struct cbfs_boot_device *cbfs_get_boot_device(bool force_ro) +{ + static struct cbfs_boot_device ro; + + /* Ensure we always init RO mcache, even if first file is from RW. + Otherwise it may not be available when needed in later stages. */ + if (ENV_INITIAL_STAGE && !force_ro && !region_device_sz(&ro.rdev)) + cbfs_get_boot_device(true); + + if (!force_ro) { + const struct cbfs_boot_device *rw = vboot_get_cbfs_boot_device(); + /* This will return NULL if vboot isn't enabled, didn't run yet + or decided to boot into recovery mode. */ + if (rw) + return rw; + } + + if (region_device_sz(&ro.rdev)) + return &ro; + + if (fmap_locate_area_as_rdev("COREBOOT", &ro.rdev)) + return NULL; + + cbfs_boot_device_find_mcache(&ro, CBMEM_ID_CBFS_RO_MCACHE); + + if (ENV_INITIAL_STAGE && !CONFIG(NO_CBFS_MCACHE)) { + cb_err_t err = cbfs_mcache_build(&ro.rdev, ro.mcache, + ro.mcache_size, NULL); + if (err && err != CB_CBFS_CACHE_FULL) + die("Failed to build RO mcache"); + } + + return &ro; +} + +#if !CONFIG(NO_CBFS_MCACHE) +static void mcache_to_cbmem(const struct cbfs_boot_device *cbd, u32 cbmem_id) +{ + if (!cbd) + return; + + size_t real_size = cbfs_mcache_real_size(cbd->mcache, cbd->mcache_size); + void *cbmem_mcache = cbmem_add(cbmem_id, real_size); + if (!cbmem_mcache) { + printk(BIOS_ERR, "ERROR: Cannot allocate CBMEM mcache %#x (%#zx bytes)!\n", + cbmem_id, real_size); + return; + } + memcpy(cbmem_mcache, cbd->mcache, real_size); +} + +static void cbfs_mcache_migrate(int unused) +{ + mcache_to_cbmem(vboot_get_cbfs_boot_device(), CBMEM_ID_CBFS_RW_MCACHE); + mcache_to_cbmem(cbfs_get_boot_device(true), CBMEM_ID_CBFS_RO_MCACHE); +} +ROMSTAGE_CBMEM_INIT_HOOK(cbfs_mcache_migrate) +#endif diff --git a/src/lib/coreboot_table.c b/src/lib/coreboot_table.c index 69ded3c..4cbf3c7 100644 --- a/src/lib/coreboot_table.c +++ b/src/lib/coreboot_table.c @@ -220,11 +220,8 @@ { struct lb_boot_media_params *bmp; const struct region_device *boot_dev; - struct region_device cbfs_dev; - - boot_device_init(); - - if (cbfs_boot_region_device(&cbfs_dev)) + const struct cbfs_boot_device *cbd = cbfs_get_boot_device(false); + if (!cbd) return;
boot_dev = boot_device_ro(); @@ -235,8 +232,8 @@ bmp->tag = LB_TAG_BOOT_MEDIA_PARAMS; bmp->size = sizeof(*bmp);
- bmp->cbfs_offset = region_device_offset(&cbfs_dev); - bmp->cbfs_size = region_device_sz(&cbfs_dev); + bmp->cbfs_offset = region_device_offset(&cbd->rdev); + bmp->cbfs_size = region_device_sz(&cbd->rdev); bmp->boot_media_size = region_device_sz(boot_dev);
bmp->fmap_offset = get_fmap_flash_offset(); diff --git a/src/security/vboot/vboot_common.h b/src/security/vboot/vboot_common.h index e64f663..512da0e 100644 --- a/src/security/vboot/vboot_common.h +++ b/src/security/vboot/vboot_common.h @@ -3,6 +3,7 @@ #define __VBOOT_VBOOT_COMMON_H__
#include <commonlib/region.h> +#include <cbfs.h> #include <vb2_api.h>
/* @@ -50,14 +51,17 @@ int vboot_recovery_mode_enabled(void); int vboot_can_enable_udc(void); void vboot_run_logic(void); -int vboot_locate_cbfs(struct region_device *rdev); +const struct cbfs_boot_device *vboot_get_cbfs_boot_device(void); #else /* !CONFIG_VBOOT */ static inline int vboot_developer_mode_enabled(void) { return 0; } static inline int vboot_recovery_mode_enabled(void) { return 0; } /* If VBOOT is not enabled, we are okay enabling USB device controller (UDC). */ static inline int vboot_can_enable_udc(void) { return 1; } static inline void vboot_run_logic(void) {} -static inline int vboot_locate_cbfs(struct region_device *rdev) { return -1; } +static inline const struct cbfs_boot_device *vboot_get_cbfs_boot_device(void) +{ + return NULL; +} #endif
void vboot_save_data(struct vb2_context *ctx); diff --git a/src/security/vboot/vboot_loader.c b/src/security/vboot/vboot_loader.c index bca4c3e..9c6e56e 100644 --- a/src/security/vboot/vboot_loader.c +++ b/src/security/vboot/vboot_loader.c @@ -1,6 +1,9 @@ /* SPDX-License-Identifier: GPL-2.0-only */
+#include <boot_device.h> #include <cbfs.h> +#include <cbmem.h> +#include <commonlib/bsd/cbfs_private.h> #include <console/console.h> #include <ec/google/chromeec/ec.h> #include <rmodule.h> @@ -22,12 +25,27 @@
int vboot_executed;
+static void build_rw_mcache(void) +{ + if (CONFIG(NO_CBFS_MCACHE)) + return; + + const struct cbfs_boot_device *cbd = vboot_get_cbfs_boot_device(); + if (!cbd) /* Don't build RW mcache in recovery mode. */ + return; + cb_err_t err = cbfs_mcache_build(&cbd->rdev, cbd->mcache, + cbd->mcache_size, NULL); + if (err && err != CB_CBFS_CACHE_FULL) + die("Failed to build RW mcache."); /* TODO: -> recovery? */ +} + void vboot_run_logic(void) { if (verification_should_run()) { /* Note: this path is not used for VBOOT_RETURN_FROM_VERSTAGE */ verstage_main(); vboot_executed = 1; + build_rw_mcache(); } else if (verstage_should_load()) { struct cbfsf file; struct prog verstage = @@ -55,21 +73,29 @@ return;
vboot_executed = 1; + build_rw_mcache(); } }
-int vboot_locate_cbfs(struct region_device *rdev) +const struct cbfs_boot_device *vboot_get_cbfs_boot_device(void) { - struct vb2_context *ctx; - /* Don't honor vboot results until the vboot logic has run. */ if (!vboot_logic_executed()) - return -1; + return NULL;
- ctx = vboot_get_context(); + static struct cbfs_boot_device cbd; + if (region_device_sz(&cbd.rdev)) + return &cbd;
+ struct vb2_context *ctx = vboot_get_context(); if (ctx->flags & VB2_CONTEXT_RECOVERY_MODE) - return -1; + return NULL;
- return vboot_locate_firmware(ctx, rdev); + boot_device_init(); + if (vboot_locate_firmware(ctx, &cbd.rdev)) + return NULL; + + cbfs_boot_device_find_mcache(&cbd, CBMEM_ID_CBFS_RW_MCACHE); + + return &cbd; }