diff --git a/Makefile.mk b/Makefile.mk index 3a20cffd62..a823a115a4 100644 --- a/Makefile.mk +++ b/Makefile.mk @@ -984,6 +984,8 @@ CBFS_REGIONS := COREBOOT,COREBOOT_TS endif endif +CBFS_REGION_COUNT := $(words $(subst $(comma),$(spc),$(CBFS_REGIONS))) + # regions-for-file - Returns a cbfstool regions parameter # $(call regions-for-file,$(filename)) # returns "REGION1,REGION2,..." @@ -1356,8 +1358,8 @@ endif # CONFIG_CPU_INTEL_FIRMWARE_INTERFACE_TABLE $(CBFSTOOL) $@ layout @printf " CBFSPRINT $(subst $(obj)/,,$(@))\n\n" ifeq ($(CONFIG_CBFS_VERIFICATION),y) - line=$$($(CBFSTOOL) $@ print -kv 2>/dev/null | grep -F '[CBFS VERIFICATION (COREBOOT)]') ;\ - if ! printf "$$line" | grep -q 'fully valid'; then \ + line=$$($(CBFSTOOL) $@ print -kv -r $(CBFS_REGIONS) 2>/dev/null | grep -F '[CBFS VERIFICATION') ;\ + if [ "$$(printf "$$line" | grep -c 'fully valid')" -ne $(CBFS_REGION_COUNT) ]; then \ echo "CBFS verification error: $$line" ;\ exit 1 ;\ fi diff --git a/src/include/cbfs.h b/src/include/cbfs.h index 27a285e51d..f7067f9009 100644 --- a/src/include/cbfs.h +++ b/src/include/cbfs.h @@ -201,7 +201,8 @@ void *_cbfs_alloc(const char *name, cbfs_allocator_t allocator, void *arg, size_t *size_out, bool force_ro, enum cbfs_type *type); void *_cbfs_unverified_area_alloc(const char *area, const char *name, - cbfs_allocator_t allocator, void *arg, size_t *size_out); + cbfs_allocator_t allocator, void *arg, size_t *size_out, + enum cbfs_type *type); struct _cbfs_default_allocator_arg { void *buf; @@ -242,7 +243,14 @@ static inline void *cbfs_unverified_area_alloc(const char *area, const char *nam cbfs_allocator_t allocator, void *arg, size_t *size_out) { - return _cbfs_unverified_area_alloc(area, name, allocator, arg, size_out); + return _cbfs_unverified_area_alloc(area, name, allocator, arg, size_out, NULL); +} + +static inline void *cbfs_unverified_area_type_alloc(const char *area, const char *name, + cbfs_allocator_t allocator, void *arg, + size_t *size_out, enum cbfs_type *type) +{ + return _cbfs_unverified_area_alloc(area, name, allocator, arg, size_out, type); } static inline void *cbfs_map(const char *name, size_t *size_out) @@ -268,7 +276,7 @@ static inline void *cbfs_ro_type_map(const char *name, size_t *size_out, enum cb static inline void *cbfs_unverified_area_map(const char *area, const char *name, size_t *size_out) { - return _cbfs_unverified_area_alloc(area, name, NULL, NULL, size_out); + return _cbfs_unverified_area_alloc(area, name, NULL, NULL, size_out, NULL); } static inline size_t _cbfs_load(const char *name, void *buf, size_t size, bool force_ro, @@ -307,7 +315,7 @@ static inline size_t cbfs_unverified_area_load(const char *area, const char *nam void *buf, size_t size) { struct _cbfs_default_allocator_arg arg = { .buf = buf, .buf_size = size }; - if (_cbfs_unverified_area_alloc(area, name, _cbfs_default_allocator, &arg, &size)) + if (_cbfs_unverified_area_alloc(area, name, _cbfs_default_allocator, &arg, &size, NULL)) return size; else return 0; @@ -341,7 +349,7 @@ static inline void *cbfs_unverified_area_cbmem_alloc(const char *area, const cha uint32_t cbmem_id, size_t *size_out) { return _cbfs_unverified_area_alloc(area, name, _cbfs_cbmem_allocator, - (void *)(uintptr_t)cbmem_id, size_out); + (void *)(uintptr_t)cbmem_id, size_out, NULL); } static inline size_t cbfs_get_size(const char *name) diff --git a/src/lib/cbfs.c b/src/lib/cbfs.c index 1dd9288bff..0c602474b3 100644 --- a/src/lib/cbfs.c +++ b/src/lib/cbfs.c @@ -173,7 +173,12 @@ static bool cbfs_file_hash_mismatch(const void *buffer, size_t size, const struct vb2_hash *hash = NULL; - if (CONFIG(CBFS_VERIFICATION) && !skip_verification) { + /* + * Skipping this block in SMM because vboot library isn't linked to SMM stage. This is + * an issue only if using a CMOS options backend, then SMM refers to an option and tries + * to verify cmos.layout here. + */ + if (CONFIG(CBFS_VERIFICATION) && !ENV_SMM && !skip_verification) { hash = cbfs_file_hash(mdata); if (!hash) { ERROR("'%s' does not have a file hash!\n", mdata->h.filename); @@ -546,7 +551,8 @@ void *_cbfs_alloc(const char *name, cbfs_allocator_t allocator, void *arg, } void *_cbfs_unverified_area_alloc(const char *area, const char *name, - cbfs_allocator_t allocator, void *arg, size_t *size_out) + cbfs_allocator_t allocator, void *arg, size_t *size_out, + enum cbfs_type *type) { struct region_device area_rdev, file_rdev; union cbfs_mdata mdata; @@ -562,6 +568,17 @@ void *_cbfs_unverified_area_alloc(const char *area, const char *name, return NULL; } + if (type) { + const enum cbfs_type real_type = be32toh(mdata.h.type); + if (*type == CBFS_TYPE_QUERY) + *type = real_type; + else if (*type != real_type) { + ERROR("'%s' type mismatch (is %u, expected %u)\n", + mdata.h.filename, real_type, *type); + return NULL; + } + } + if (rdev_chain(&file_rdev, &area_rdev, data_offset, be32toh(mdata.h.len))) return NULL; diff --git a/src/security/tpm/tspi/crtm.c b/src/security/tpm/tspi/crtm.c index 4e0a9b727a..def2fc3513 100644 --- a/src/security/tpm/tspi/crtm.c +++ b/src/security/tpm/tspi/crtm.c @@ -9,6 +9,10 @@ #include #include +/* This include is available as only if CONFIG_SOC_INTEL_COMMON_BLOCK is + set, which is not guaranteed for this file. */ +#include + static int tpm_log_initialized; static inline int tpm_log_available(void) { @@ -72,7 +76,28 @@ static tpm_result_t tspi_init_crtm(void) /* Mapping measures the file. We know we can safely map here because bootblock-as-a-file is only used on x86, where we don't need cache to map. */ enum cbfs_type type = CBFS_TYPE_BOOTBLOCK; - void *mapping = cbfs_ro_type_map("bootblock", NULL, &type); + void *mapping = NULL; + + if (CONFIG(INTEL_TOP_SWAP_SEPARATE_REGIONS)) { + enum ts_config top_swap = get_rtc_buc_top_swap_status(); + if (top_swap == TS_ENABLE) + printk(BIOS_INFO, + "CRTM Top Swap: Measuring bootblock in TOPSWAP (will be logged as BOOTBLOCK).\n"); + else + printk(BIOS_INFO, + "CRTM Top Swap: Measuring bootblock in BOOTBLOCK.\n"); + + /* + * Whether Top Swap is active or not, FMAP always refers to the same + * memory ranges but the contents of BOOTBLOCK and TOPSWAP are swapped. + * Hence always using BOOTBLOCK region to access the active bootblock. + */ + mapping = cbfs_unverified_area_type_alloc("BOOTBLOCK", "bootblock", + NULL, NULL, NULL, &type); + } else { + mapping = cbfs_ro_type_map("bootblock", NULL, &type); + } + if (!mapping) { printk(BIOS_INFO, "TSPI: Couldn't measure bootblock into CRTM!\n"); diff --git a/util/cbfstool/cbfs_sections.h b/util/cbfstool/cbfs_sections.h index 99a4761b64..c3479bbfd2 100644 --- a/util/cbfstool/cbfs_sections.h +++ b/util/cbfstool/cbfs_sections.h @@ -10,7 +10,9 @@ #define SECTION_NAME_FMAP "FMAP" #define SECTION_NAME_PRIMARY_CBFS "COREBOOT" +#define SECTION_NAME_TOPSWAP_CBFS "COREBOOT_TS" #define SECTION_NAME_BOOTBLOCK "BOOTBLOCK" +#define SECTION_NAME_TOPSWAP "TOPSWAP" #define SECTION_ANNOTATION_CBFS "CBFS" diff --git a/util/cbfstool/cbfstool.c b/util/cbfstool/cbfstool.c index 5555b57364..68dbd32984 100644 --- a/util/cbfstool/cbfstool.c +++ b/util/cbfstool/cbfstool.c @@ -135,6 +135,18 @@ struct mh_cache { bool initialized; }; +static bool is_main_cbfs_region(const char *region_name) +{ + return strcmp(region_name, SECTION_NAME_PRIMARY_CBFS) == 0 || + strcmp(region_name, SECTION_NAME_TOPSWAP_CBFS) == 0; +} + +static bool is_topswap_region(const char *region_name) +{ + return strcmp(region_name, SECTION_NAME_TOPSWAP_CBFS) == 0 || + strcmp(region_name, SECTION_NAME_TOPSWAP) == 0; +} + static struct mh_cache *get_mh_cache(void) { static struct mh_cache mhc; @@ -148,22 +160,24 @@ static struct mh_cache *get_mh_cache(void) if (!fmap) goto no_metadata_hash; + const char *bootblock_region = SECTION_NAME_BOOTBLOCK; + if (is_topswap_region(param.region_name)) + bootblock_region = SECTION_NAME_TOPSWAP; + /* Find the metadata_hash container. If there is a "BOOTBLOCK" FMAP section, it's there. If not, it's a normal file in the primary CBFS section. */ size_t offset, size; struct buffer buffer; - if (fmap_find_area(fmap, SECTION_NAME_BOOTBLOCK)) { - if (!partitioned_file_read_region(&buffer, param.image_file, - SECTION_NAME_BOOTBLOCK)) + if (fmap_find_area(fmap, bootblock_region)) { + if (!partitioned_file_read_region(&buffer, param.image_file, bootblock_region)) goto no_metadata_hash; - mhc.region = SECTION_NAME_BOOTBLOCK; + mhc.region = bootblock_region; offset = 0; size = buffer.size; } else { struct cbfs_image cbfs; struct cbfs_file *mh_container; - if (!partitioned_file_read_region(&buffer, param.image_file, - SECTION_NAME_PRIMARY_CBFS)) + if (!partitioned_file_read_region(&buffer, param.image_file, SECTION_NAME_PRIMARY_CBFS)) goto no_metadata_hash; mhc.region = SECTION_NAME_PRIMARY_CBFS; if (cbfs_image_from_buffer(&cbfs, &buffer, param.headeroffset)) @@ -245,7 +259,7 @@ static int update_anchor(struct mh_cache *mhc, uint8_t *fmap_hash) will recalculate and update the metadata hash in the bootblock if needed. */ static int maybe_update_metadata_hash(struct cbfs_image *cbfs) { - if (strcmp(param.region_name, SECTION_NAME_PRIMARY_CBFS)) + if (!is_main_cbfs_region(param.region_name)) return 0; /* Metadata hash only embedded in primary CBFS. */ struct mh_cache *mhc = get_mh_cache(); @@ -268,6 +282,7 @@ static int maybe_update_metadata_hash(struct cbfs_image *cbfs) static int maybe_update_fmap_hash(void) { if (strcmp(param.region_name, SECTION_NAME_BOOTBLOCK) && + strcmp(param.region_name, SECTION_NAME_TOPSWAP) && strcmp(param.region_name, SECTION_NAME_FMAP) && param.type != CBFS_TYPE_BOOTBLOCK && param.type != CBFS_TYPE_AMDFW) @@ -1603,7 +1618,7 @@ static int cbfs_print(void) vb2_digest_size(real_hash.algo)); printf("[METADATA HASH]\t%s:%s", vb2_get_hash_algorithm_name(real_hash.algo), hash_str); - if (!strcmp(param.region_name, SECTION_NAME_PRIMARY_CBFS)) { + if (is_main_cbfs_region(param.region_name)) { if (!memcmp(mhc->cbfs_hash.raw, real_hash.raw, vb2_digest_size(real_hash.algo))) { printf(":valid"); @@ -2364,6 +2379,10 @@ int main(int argc, char **argv) strcpy(region_name_scratch, param.region_name); param.region_name = strtok(region_name_scratch, ","); for (unsigned region = 0; region < num_regions; ++region) { + // Reset metadata cache in case region uses anchor from a different + // location. + get_mh_cache()->initialized = false; + if (!param.region_name) { ERROR("Encountered illegal degenerate region name in -r list\n"); ERROR("The image will be left unmodified.\n"); @@ -2371,8 +2390,7 @@ int main(int argc, char **argv) return 1; } - if (strcmp(param.region_name, SECTION_NAME_PRIMARY_CBFS) - == 0) + if (is_main_cbfs_region(param.region_name)) seen_primary_cbfs = true; param.image_region = image_regions + region; diff --git a/util/cbfstool/platform_fixups.c b/util/cbfstool/platform_fixups.c index 7e29cd7e2b..97fdd2e15b 100644 --- a/util/cbfstool/platform_fixups.c +++ b/util/cbfstool/platform_fixups.c @@ -312,12 +312,14 @@ static int mediatek_fixup(struct buffer *buffer, unused size_t offset) platform_fixup_func platform_fixups_probe(struct buffer *buffer, size_t offset, const char *region_name) { - if (!strcmp(region_name, SECTION_NAME_BOOTBLOCK)) { + if (!strcmp(region_name, SECTION_NAME_BOOTBLOCK) || + !strcmp(region_name, SECTION_NAME_TOPSWAP)) { if (qualcomm_probe(buffer, offset)) return qualcomm_fixup; else if (mediatek_probe(buffer)) return mediatek_fixup; - } else if (!strcmp(region_name, SECTION_NAME_PRIMARY_CBFS)) { + } else if (!strcmp(region_name, SECTION_NAME_PRIMARY_CBFS) || + !strcmp(region_name, SECTION_NAME_TOPSWAP_CBFS)) { /* TODO: add fixups for primary CBFS bootblock platforms, if needed */ } else { ERROR("%s called for unexpected FMAP region %s!\n", __func__, region_name);