summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMary Ruthven <mruthven@chromium.org>2022-09-21 15:48:30 -0500
committerChromeos LUCI <chromeos-scoped@luci-project-accounts.iam.gserviceaccount.com>2022-11-07 18:07:20 +0000
commit52b9c34f7a054b8a0de9146b5ffb771480c8e611 (patch)
treedc289f8ba86f1c4bf075dca6ebf868e8f152a6ed
parentb08b1eeca736388709381471cebbd80b6e969921 (diff)
downloadchrome-ec-52b9c34f7a054b8a0de9146b5ffb771480c8e611.tar.gz
apro: save the gbb descriptor after verification passes
Save the GBBD in AP RO flash after verification passes. It takes a while to cycle through all of the factory flags. If Cr50 successfully matches the saved AP RO hash with injected factory flags, save the flags to save for future runs. The gbb descriptor data is saved 512 bytes after the start of the AP RO check data. The max v1 size is currently 296 bytes, so there's extra room if we need to increase the AP RO check data size. The entire AP RO data space is 2048, so there's a lot of extra space after the gbb descriptor if we need to add more stuff. BUG=b:236844541 TEST=manual # erase hash > ap_ro_info erase # set the GBB flags to 0x239 /usr/share/vboot/bin/set_gbb_flags.sh 0x239 # add test key to RO_VPD vpd -i RO_VPD -s "apro_test=original" # save hash ap_ro_hash.py WP_RO # trigger verification. Make sure it fails because the flags # are 0x239 [200.425891 RO Validation triggered] ... [200.481670 AP RO FAILED!] # set the GBB flags to 0 /usr/share/vboot/bin/set_gbb_flags.sh 0 # change test RO_VPD key. make sure verification fails. vpd -i RO_VPD -s "apro_test=wrong" [3.822818 RO Validation triggered] ... [61.407680 spi_hash_disable] [61.407955 AP RO FAILED!] [61.418949 AP off] # restore test RO_VPD key. vpd -i RO_VPD -s "apro_test=original" # trigger verification. Make sure it passes and saves the gbbd. [3.822818 RO Validation triggered] [3.825035 enable_spi_pinmux: AP] ... [25.695068 spi_hash_disable] [25.696224 AP RO PASS!] # check saved gbbd shows 0x239 > ap result : 6 gbb : saved (0x239) supported : yes ... # Trigger verification. Verify Cr50 just uses 0x239 [356.968860 RO Validation triggered] [356.969795 enable_spi_pinmux: AP] ... [364.289047 AP RO PASS!] # change test RO_VPD key make sure verification fails. vpd -i RO_VPD -s "apro_test=wrong" [213.868492 RO Validation triggered] ... [221.192661 AP RO FAILED!] # erase the AP RO data. Verify gbbd gets cleared > ap_ro_info erase result : 6 [400.206562 ap_ro_check_unsupported: RO verification not programmed] supported : no > ap result : 6 [403.772743 ap_ro_check_unsupported: RO verification not programmed] supported : no > Change-Id: Iad8cfd4a448c2e5798a94aa8b4e3a735281eb849 Signed-off-by: Mary Ruthven <mruthven@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3915000 Reviewed-by: Vadim Bendebury <vbendeb@chromium.org>
-rw-r--r--common/ap_ro_integrity_check.c312
-rw-r--r--include/flash_log.h2
2 files changed, 268 insertions, 46 deletions
diff --git a/common/ap_ro_integrity_check.c b/common/ap_ro_integrity_check.c
index d4b11d3f06..725ab4f5cd 100644
--- a/common/ap_ro_integrity_check.c
+++ b/common/ap_ro_integrity_check.c
@@ -54,11 +54,14 @@
/* Version of the AP RO check information saved in the H1 flash page. */
#define AP_RO_HASH_LAYOUT_VERSION_0 0
#define AP_RO_HASH_LAYOUT_VERSION_1 1
+#define AP_RO_GBBD_LAYOUT_VERSION_2 2 /* Used to store the gbb descriptor */
/* Verification scheme V1. */
#define AP_RO_HASH_TYPE_FACTORY 0
/* Verification scheme V2. */
#define AP_RO_HASH_TYPE_GSCVD 1
+/* Use the factory gbb flags to generate the V1 hash */
+#define AP_RO_HASH_TYPE_GBBD 2
/* A flash range included in hash calculations. */
struct ro_range {
@@ -90,20 +93,6 @@ struct ap_ro_check_header {
uint32_t checksum;
};
-/*
- * Saved AP RO data includes the ap ro check header, the sha digest of the
- * firmware and the RO ranges. Make sure the header, digest, and maximum number
- * of ranges fit in the AP RO space.
- */
-BUILD_ASSERT(AP_RO_DATA_SPACE_SIZE >=
- sizeof(struct ap_ro_check_header) + SHA256_DIGEST_SIZE +
- AP_RO_MAX_NUM_RANGES * sizeof(struct ro_range));
-/* Format of the AP RO check information saved in the H1 flash page. */
-struct ap_ro_check {
- struct ap_ro_check_header header;
- struct ap_ro_check_payload payload;
-};
-
/*****************************************************************************/
/* FMAP structures borrowed from host/lib/include/fmap.h in vboot_reference. */
@@ -178,13 +167,21 @@ BUILD_ASSERT(sizeof(struct vb2_gbb_header) == VB2_GBB_HEADER_SIZE);
BUILD_ASSERT(offsetof(struct vb2_gbb_header, flags) ==
VB2_GBB_HEADER_FLAG_OFFSET);
+/* One of the AP RO verification outcomes, internal representation. */
+enum gbbd_status {
+ GS_INJECT_FLAGS = BIT(0), /* Generate the hash using a factory */
+ /* flag value. */
+ GS_FLAGS_IN_HASH = BIT(1), /* The gbb flags are covered by the */
+ /* hash. */
+};
+
struct gbb_descriptor {
/*
* If validate_flags is true, verify the gbb flags are set to 0 during
* verification. If the gbb flags are in the hash, AP RO verification
* will need to validate them.
*/
- bool validate_flags;
+ enum gbbd_status status;
/*
* The FMAP range information. The FMAP must be in the hash ranges for
* verification to pass.
@@ -200,11 +197,30 @@ struct gbb_descriptor {
/* Flags used to generate the hash */
uint32_t injected_flags;
-} __packed;
+};
/*****************************************************************************/
/* V1 Factory Support (AP_RO_HASH_TYPE_FACTORY) */
+/* Format of the AP RO check information saved in the H1 flash page. */
+struct ap_ro_check {
+ /* AP_RO_HASH_TYPE_FACTORY data */
+ struct ap_ro_check_header header;
+ /* Used by the V1 scheme. */
+ struct ap_ro_check_payload payload;
+
+ /* Optional GBB flag data. */
+ struct ap_ro_check_header gbbd_header;
+ /* Used to save the injected gbb flags. */
+ struct gbb_descriptor gbbd;
+};
+/*
+ * Make sure all saved AP RO data can fit in the AP RO space. This includes
+ * two ap ro check headers, the sha digest of the firmware, the maximum number
+ * of AP RO ranges, and a gbb descriptor.
+ */
+BUILD_ASSERT(sizeof(struct ap_ro_check) <= AP_RO_DATA_SPACE_SIZE);
+
/* One of the AP RO verification outcomes, internal representation. */
enum ap_ro_check_result {
ROV_NOT_FOUND = 1, /* Control structures not found. */
@@ -215,6 +231,24 @@ enum ap_ro_check_result {
/* Page offset for H1 flash operations. */
static const uint32_t h1_flash_offset_ =
AP_RO_DATA_SPACE_ADDR - CONFIG_PROGRAM_MEMORY_BASE;
+static const uint32_t h1_apro_gbbd_data_flash_offset_ =
+ h1_flash_offset_ + offsetof(struct ap_ro_check, gbbd_header);
+
+/*
+ * Enforce flash_write checks at build to ensure the chip can write the AP RO
+ * data to flash. Check all structs.
+ */
+/* Verify the chip supports writing the struct sizes. */
+BUILD_ASSERT(sizeof(struct ap_ro_check_payload) % CONFIG_FLASH_WRITE_SIZE == 0);
+BUILD_ASSERT(sizeof(struct ap_ro_check_header) % CONFIG_FLASH_WRITE_SIZE == 0);
+BUILD_ASSERT(sizeof(struct gbb_descriptor) % CONFIG_FLASH_WRITE_SIZE == 0);
+BUILD_ASSERT(sizeof(struct ap_ro_check) % CONFIG_FLASH_WRITE_SIZE == 0);
+/* Verify the chip will be able to write to the header offsets. */
+BUILD_ASSERT((AP_RO_DATA_SPACE_ADDR - CONFIG_PROGRAM_MEMORY_BASE) %
+ CONFIG_FLASH_WRITE_SIZE == 0);
+BUILD_ASSERT((AP_RO_DATA_SPACE_ADDR - CONFIG_PROGRAM_MEMORY_BASE +
+ offsetof(struct ap_ro_check, gbbd_header)) %
+ CONFIG_FLASH_WRITE_SIZE == 0);
/* Fixed pointer at the H1 flash page storing the AP RO check information. */
static const struct ap_ro_check *p_chk =
@@ -572,7 +606,7 @@ enum ap_ro_check_result validate_ranges_sha(const struct ro_range *ranges,
* the injected gbb flag value and the actual data from before
* and after the gbb flags to calculate the hash.
*/
- if (gbbd->validate_flags &&
+ if (gbbd->status & GS_INJECT_FLAGS &&
is_in_range(gbbd->gbb_flags, ranges[i])) {
update_sha_with_gbb_range(&ctx, ranges[i], gbbd);
continue;
@@ -635,8 +669,24 @@ static enum ap_ro_check_result validate_ranges_sha_with_factory_flags(
expected_digest,
gbbd) == ROV_SUCCEEDED) {
CPRINTS("matched gbb %x", gbbd->injected_flags);
+ /*
+ * If the hash was generated with 0 GBB flags, then
+ * there's no need for special handling.
+ */
+ if (!gbbd->injected_flags)
+ gbbd->status &= ~GS_INJECT_FLAGS;
+
return ROV_SUCCEEDED;
}
+ /*
+ * If the flags aren't getting injected, then there's no point
+ * in trying other flags. Nothing is getting substituted, so the
+ * calculated hash will not change with different flag values.
+ * If it failed, then it will keep failing. Go ahead and fail
+ * now.
+ */
+ if (!(gbbd->status & GS_INJECT_FLAGS))
+ return ROV_FAILED;
}
return ROV_FAILED;
}
@@ -672,6 +722,68 @@ static int read_ap_spi(void *buf, uint32_t offset, size_t size, int code_line)
return 0;
}
+static int verify_ap_ro_gbb_space(void)
+{
+ uint32_t checksum;
+
+ if ((p_chk->gbbd_header.type != AP_RO_HASH_TYPE_GBBD) ||
+ (p_chk->gbbd_header.version != AP_RO_GBBD_LAYOUT_VERSION_2))
+ return EC_ERROR_CRC;
+ /* The GBB descriptor is only valid for FACTORY hashes. */
+ if (p_chk->header.type != AP_RO_HASH_TYPE_FACTORY)
+ return EC_ERROR_CRC;
+
+ /* The V1 header saved too many ranges. The stored GBBD is invalid */
+ if (p_chk->header.num_ranges > AP_RO_MAX_NUM_RANGES) {
+ CPRINTS("%s: V1 stored too many ranges", __func__);
+ return EC_ERROR_CRC;
+ }
+ if (p_chk->gbbd_header.num_ranges != p_chk->header.num_ranges) {
+ CPRINTS("%s: gbbd doesn't match v1 header", __func__);
+ return EC_ERROR_CRC;
+ }
+
+ app_compute_hash(&p_chk->gbbd, sizeof(struct gbb_descriptor),
+ &checksum, sizeof(checksum));
+
+ if (memcmp(&checksum, &p_chk->gbbd_header.checksum, sizeof(checksum))) {
+ CPRINTS("%s: AP RO GBB Checksum corrupted", __func__);
+ return EC_ERROR_CRC;
+ }
+
+ return EC_SUCCESS;
+}
+
+/**
+ * Load the saved gbb descriptor.
+ *
+ * @param gbbd pointer to the gbb descriptor with the fmap, gbb, gbb_flags,
+ * and status.
+ *
+ * @return ROV_SUCCEEDED on success, ROV_NOT_FOUND if there's no saved gbbd
+ * data, ROV_FAILED if reading the saved gbbd failed.
+ */
+static enum ap_ro_check_result get_saved_gbbd(struct gbb_descriptor *gbbd)
+{
+ /*
+ * It's not possible to save the gbbd with 0xffff num ranges. If it's
+ * set to 0xffff, then the gbbd is probably erased. This is the same
+ * check ap_ro_check_unsupported uses to verify the v1 header.
+ */
+ if (p_chk->gbbd_header.num_ranges == (uint16_t)~0) {
+ CPRINTS("%s: not programmed", __func__);
+ return ROV_NOT_FOUND;
+ }
+
+ if (verify_ap_ro_gbb_space() != EC_SUCCESS)
+ return ROV_FAILED;
+
+ memcpy(gbbd, &p_chk->gbbd, sizeof(*gbbd));
+ CPRINTS("%s (0x%x): flags 0x%x", __func__, gbbd->status,
+ gbbd->injected_flags);
+ return ROV_SUCCEEDED;
+}
+
/**
* Find GBB FMAP area in the FMAP table.
*
@@ -729,12 +841,51 @@ static int range_is_in_hash(const struct ro_range range)
}
return EC_ERROR_INVAL;
}
+/*
+ * Verify the GBB flags are set to 0.
+ *
+ * @param gbbd pointer to the gbb descriptor with the fmap, gbb, gbb_flags,
+ * and status information.
+ *
+ * Returns:
+ * ROV_SUCCEEDED if the flags are outside of the hash or if the flags
+ * are in the hash and set to 0.
+ * ROV_FAILED if the gbb format is invalid.
+ */
+static enum ap_ro_check_result validate_gbb_flags(struct gbb_descriptor *gbbd)
+{
+ uint32_t gbb_flags;
+ /*
+ * There's no special GBB handling, so there's no need to validate the
+ * GBB outside of the standard AP RO verification.
+ */
+ if (!(gbbd->status & GS_INJECT_FLAGS)) {
+ CPRINTS("%s: skip GBB check", __func__);
+ return ROV_SUCCEEDED;
+ }
+
+ if (gbbd->gbb_flags.range_size != sizeof(gbb_flags)) {
+ CPRINTS("%s: invalid size", __func__);
+ return ROV_FAILED;
+ }
+
+ if (read_ap_spi(&gbb_flags, gbbd->gbb_flags.flash_offset,
+ sizeof(gbb_flags), __LINE__))
+ return ROV_FAILED;
+
+ if (gbb_flags) {
+ CPRINTS("%s: invalid flags %x", __func__, gbb_flags);
+ return ROV_FAILED;
+ }
+ CPRINTS("%s: ok", __func__);
+ return ROV_SUCCEEDED;
+}
/*
* Verify the GBB contents and validate the GBB flags are set to 0.
*
* @param gbbd pointer to the gbb descriptor with the fmap, gbb, gbb_flags,
- * and validate_flags information from init_gbbd.
+ * and status information.
*
* Returns:
* ROV_SUCCEEDED if the flags are outside of the hash or if the flags
@@ -746,13 +897,11 @@ static enum ap_ro_check_result validate_gbbd(struct gbb_descriptor *gbbd)
struct vb2_gbb_header gbb;
/*
- * The GBB flags are outside of the hash, so there's no need to validate
- * them.
- * The location was found in the FMAP which will be verified. If it's
- * pointing to the wrong place, AP RO verification will fail.
+ * There's no special GBB handling, so there's no need to validate the
+ * GBB outside of the standard AP RO verification.
*/
- if (!gbbd->validate_flags) {
- CPRINTS("%s: GBB flags not in hash", __func__);
+ if (!(gbbd->status & GS_INJECT_FLAGS)) {
+ CPRINTS("%s: skip GBB check", __func__);
return ROV_SUCCEEDED;
}
@@ -787,12 +936,9 @@ static enum ap_ro_check_result validate_gbbd(struct gbb_descriptor *gbbd)
}
/* Verify the GBB flags are set to 0. */
- if (gbb.flags) {
- CPRINTS("%s: invalid flags %x", __func__, gbb.flags);
+ if (validate_gbb_flags(gbbd) != ROV_SUCCEEDED)
return ROV_FAILED;
- }
- CPRINTS("%s: ok", __func__);
return ROV_SUCCEEDED;
}
/*
@@ -810,11 +956,13 @@ static enum ap_ro_check_result validate_gbbd(struct gbb_descriptor *gbbd)
* ROV_FAILED if no FMAP section was found in the AP RO hash or if the
* GBB wasn't found in FMAP.
*/
-static enum ap_ro_check_result init_gbbd(struct gbb_descriptor *gbbd)
+static enum ap_ro_check_result find_gbb_with_fmap(struct gbb_descriptor *gbbd)
{
uint32_t offset;
int rv;
+ /* Set entire gbbd contents to zero. */
+ memset(gbbd, 0, sizeof(gbbd));
for (offset = 0; offset < MAX_SUPPORTED_FLASH_SIZE;
offset += LOWEST_FMAP_ALIGNMENT) {
struct fmap_header fmh;
@@ -867,15 +1015,39 @@ static enum ap_ro_check_result init_gbbd(struct gbb_descriptor *gbbd)
gbbd->gbb_flags.flash_offset = gbbah.area_offset +
VB2_GBB_HEADER_FLAG_OFFSET;
gbbd->gbb_flags.range_size = VB2_GBB_HEADER_FLAG_SIZE;
- gbbd->validate_flags = range_is_in_hash(gbbd->gbb_flags) ==
- EC_SUCCESS;
+ /* Verify the flags if they're in the hash. */
+ if (range_is_in_hash(gbbd->gbb_flags) == EC_SUCCESS)
+ gbbd->status = GS_INJECT_FLAGS | GS_FLAGS_IN_HASH;
return ROV_SUCCEEDED;
}
return ROV_FAILED;
}
+/*
+ * Initialize the gbb descriptor using the FMAP and GBB found in RO flash.
+ *
+ * Iterate through AP flash at 4K intervals looking for FMAP. Once FMAP is
+ * found call find the FMAP GBB section. Populate the FMAP and GBB information
+ * in the gbb descriptor.
+ *
+ * @param gbbd pointer to the gbb descriptor.
+ *
+ * Returns:
+ * ROV_SUCCEEDED if a valid GBB was found using FMAP information from a
+ * section covered by the AP RO hash.
+ * ROV_FAILED if no FMAP section was found in the AP RO hash or if the
+ * GBB wasn't found in FMAP.
+ */
+static enum ap_ro_check_result init_gbbd(struct gbb_descriptor *gbbd)
+{
+ enum ap_ro_check_result rv = find_gbb_with_fmap(gbbd);
+
+ if (rv != ROV_SUCCEEDED)
+ return rv;
+ return validate_gbbd(gbbd);
+}
/*
* A hook used to keep the EC in reset, no matter what keys the user presses,
* the only way out is the Cr50 reboot, most likely through power cycle by
@@ -922,7 +1094,6 @@ int ec_rst_override(void)
return !apro_fail_status_cleared && apro_result == AP_RO_FAIL;
}
-
static uint8_t do_ap_ro_check(void)
{
enum ap_ro_check_result rv;
@@ -939,23 +1110,61 @@ static uint8_t do_ap_ro_check(void)
enable_ap_spi_hash_shortcut();
- /* Find the GBB and FMAP locations. */
- rv = init_gbbd(&gbbd);
- /* Check the GBB flags are 0 before validating any AP RO ranges. */
- if (rv == ROV_SUCCEEDED)
- rv = validate_gbbd(&gbbd);
- if (rv == ROV_SUCCEEDED) {
- if (gbbd.validate_flags)
+ /* Try and load the GBB and FMAP locations from flash. */
+ rv = get_saved_gbbd(&gbbd);
+ switch (rv) {
+ case ROV_SUCCEEDED:
+ /* Use the saved gbbd to verify RO */
+ rv = validate_gbb_flags(&gbbd);
+ if (rv == ROV_SUCCEEDED)
+ rv = validate_ranges_sha(p_chk->payload.ranges,
+ p_chk->header.num_ranges,
+ p_chk->payload.digest,
+ &gbbd);
+ break;
+ case ROV_NOT_FOUND:
+ /* Initialize the gbbd if there's no saved data. */
+ rv = init_gbbd(&gbbd);
+
+ /*
+ * Run AP RO verification with all of the factory flags.
+ *
+ * The saved hash could have been generated with non-zero
+ * factory flags. Try recalculating the hash with common
+ * factory flags to see if any of them pass.
+ *
+ * If it passes, save the gbbd, so future runs won't need
+ * to retry all of them.
+ */
+ if (rv == ROV_SUCCEEDED)
rv = validate_ranges_sha_with_factory_flags(
p_chk->payload.ranges,
p_chk->header.num_ranges,
p_chk->payload.digest,
&gbbd);
- else
- rv = validate_ranges_sha(p_chk->payload.ranges,
- p_chk->header.num_ranges,
- p_chk->payload.digest,
- &gbbd);
+
+ /* If verification succeeded, try to save the gbbd. */
+ if (rv == ROV_SUCCEEDED) {
+ if (write_ap_ro_check_data(
+ AP_RO_GBBD_LAYOUT_VERSION_2,
+ AP_RO_HASH_TYPE_GBBD,
+ p_chk->header.num_ranges,
+ h1_apro_gbbd_data_flash_offset_,
+ (uint32_t *)(&p_chk->gbbd_header),
+ &gbbd,
+ sizeof(gbbd)) == ARCVE_OK) {
+ CPRINTS("%s: saved gbbd", __func__);
+ ap_ro_add_flash_event(APROF_SAVED_GBBD);
+ } else {
+ CPRINTS("%s: save gbbd failed", __func__);
+ ap_ro_add_flash_event(
+ APROF_FAILED_TO_SAVE_GBBD);
+ }
+ }
+ break;
+ default:
+ CPRINTS("%s: FAIL corrupted GBBD", __func__);
+ break;
}
disable_ap_spi_hash_shortcut();
@@ -977,7 +1186,7 @@ static uint8_t do_ap_ro_check(void)
*/
return EC_ERROR_CRC;
}
- /* TODO(b/236844541): save gbbd */
+
apro_result = AP_RO_PASS;
ap_ro_add_flash_event(APROF_CHECK_SUCCEEDED);
CPRINTS("AP RO PASS!");
@@ -1085,7 +1294,18 @@ static int ap_ro_info_cmd(int argc, char **argv)
/* All other AP RO verificaiton unsupported reasons are fine */
if (rv)
return EC_SUCCESS;
-
+ rv = verify_ap_ro_gbb_space();
+ ccprintf("gbbd : ");
+ if (rv == EC_SUCCESS) {
+ ccprintf("ok (%d)\n", p_chk->gbbd.status);
+ ccprintf("flags : ");
+ if (p_chk->gbbd.status & GS_FLAGS_IN_HASH)
+ ccprintf("0x%x\n", p_chk->gbbd.injected_flags);
+ else
+ ccprintf("na\n");
+ } else {
+ ccprintf("na (%d)\n", rv);
+ }
ccprintf("sha256 hash %ph\n",
HEX_BUF(p_chk->payload.digest, sizeof(p_chk->payload.digest)));
ccprintf("Covered ranges:\n");
diff --git a/include/flash_log.h b/include/flash_log.h
index 05f3e3319e..407a184e2f 100644
--- a/include/flash_log.h
+++ b/include/flash_log.h
@@ -102,6 +102,8 @@ enum ap_ro_verification_ev {
APROF_CHECK_SUCCEEDED = 7,
APROF_CHECK_UNSUPPORTED = 8,
APROF_FAIL_CLEARED = 9,
+ APROF_SAVED_GBBD = 10,
+ APROF_FAILED_TO_SAVE_GBBD = 11,
};
struct ap_ro_entry_payload {