summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMary Ruthven <mruthven@chromium.org>2022-08-16 14:17:16 -0500
committerChromeos LUCI <chromeos-scoped@luci-project-accounts.iam.gserviceaccount.com>2022-11-02 23:46:46 +0000
commitb9d1609c4c96c592ac9bf2c64b54c784412b5617 (patch)
tree761ad5e6416ce12c61a591ed27189ca00c71841b
parent83be28f6f56c7daa543c03633b28a742e2c462a3 (diff)
downloadchrome-ec-b9d1609c4c96c592ac9bf2c64b54c784412b5617.tar.gz
ap_ro: v1: check the gbb flags are 0
This change verifies the GBB flags are 0. Before running verification find the GBB flags using FMAP. Read the flags and verify they're 0. If they are continue with verification. If verification passes, set the status to AP_RO_PASS instead of AP_RO_PASS_UNVERIFIED_GBB. BUG=b:236844541 TEST=manual # Set GBB flags to 0x42b9 /usr/share/vboot/bin/set_gbb_flags.sh 0x42b9 # save the hash with GBB 0x42b9 ap_ro_hash.py WP_RO # Verify AP RO verification fails because flags are 0x42b9 [72.692916 RO Validation triggered] [72.694034 enable_spi_pinmux: AP] [72.696472 spi_hash_pp_done: AP] [72.747348 validate_gbbd: invalid flags 42b9] [72.748043 spi_hash_disable] [72.748325 AP RO FAILED!] # reboot cr50 to release ec reset > reboot # Set GBB flags to 0 /usr/share/vboot/bin/set_gbb_flags.sh 0 # Verify ap ro verification passes. [11.887981 RO Validation triggered] [11.890193 enable_spi_pinmux: AP] [11.893215 spi_hash_pp_done: AP] [11.944625 validate_gbbd: ok] [11.945545 validate_ranges_sha: 0:400000] [12.001037 AC: -F] [19.201118 spi_hash_disable] [19.202487 AP RO PASS!] [19.212337 AP off] [19.264606 CCD state: UARTEC+TX] # Verify verification fails if the FMAP isn't in the hash. # Set the hash ap_ro_hash.py COREBOOT GBB # Trigger verification. It should fail because the fmap isn't in # the hash. [87.274055 RO Validation triggered] [87.275653 enable_spi_pinmux: AP] [87.278614 spi_hash_pp_done: AP] [87.329715 init_gbbd: FMAP(3c0000:47c) not in hash.] [87.367118 combo0 efs rst] [87.367698 Recovery Requested] [87.388858 AC: -F] [87.544731 init_gbbd: FMAP(13c0000:47c) not in hash.] [87.707511 spi_hash_disable] [87.708415 AP RO FAILED!] Change-Id: I3f53272a9c1aa1e82df16461dd4ac6577e4060c8 Signed-off-by: Mary Ruthven <mruthven@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3840651 Reviewed-by: Vadim Bendebury <vbendeb@chromium.org>
-rw-r--r--common/ap_ro_integrity_check.c277
1 files changed, 262 insertions, 15 deletions
diff --git a/common/ap_ro_integrity_check.c b/common/ap_ro_integrity_check.c
index 16506b96d9..c0303115cf 100644
--- a/common/ap_ro_integrity_check.c
+++ b/common/ap_ro_integrity_check.c
@@ -31,6 +31,7 @@
#define LOWEST_FMAP_ALIGNMENT (4 * 1024)
#define FMAP_SIGNATURE "__FMAP__"
#define FMAP_AREA_NAME "FMAP"
+#define FMAP_GBB_AREA_NAME "GBB"
#define FMAP_SIGNATURE_SIZE (sizeof(FMAP_SIGNATURE) - 1)
#define FMAP_NAMELEN 32
#define FMAP_MAJOR_VERSION 1
@@ -107,7 +108,6 @@ struct ap_ro_check {
struct ap_ro_check_payload payload;
};
-#ifdef FIND_FMAP
/*****************************************************************************/
/* FMAP structures borrowed from host/lib/include/fmap.h in vboot_reference. */
@@ -127,7 +127,81 @@ struct fmap_area_header {
char area_name[FMAP_NAMELEN];
uint16_t area_flags;
} __packed;
-#endif /* FIND_FMAP */
+
+/*****************************************************************************/
+/* GBB information from vboot_reference/firmware/2lib/include/2struct.h */
+/*
+ * Signature at start of the GBB
+ */
+#define VB2_GBB_SIGNATURE "$GBB"
+#define VB2_GBB_SIGNATURE_SIZE (sizeof(VB2_GBB_SIGNATURE) - 1)
+
+#define VB2_GBB_HWID_DIGEST_SIZE 32
+
+/* Supported VB2 GBB struct version */
+#define VB2_GBB_MAJOR_VER 1
+#define VB2_GBB_MINOR_VER 2
+
+#define VB2_GBB_HEADER_SIZE 128
+#define VB2_GBB_HEADER_FLAG_OFFSET 12
+#define VB2_GBB_HEADER_FLAG_SIZE 4
+/*
+ * GBB header saved in AP RO flash.
+ * v1.2 - added fields for sha256 digest of the HWID
+ */
+struct vb2_gbb_header {
+ /* Fields present in version 1.1 */
+ uint8_t signature[VB2_GBB_SIGNATURE_SIZE]; /* VB2_GBB_SIGNATURE */
+ uint16_t major_version; /* See VB2_GBB_MAJOR_VER */
+ uint16_t minor_version; /* See VB2_GBB_MINOR_VER */
+ uint32_t header_size; /* Size of GBB header in bytes */
+
+ /* Flags.
+ * b/236844541 - APRO verification v1 has to confirm the flags are set
+ * to 0.
+ */
+ uint32_t flags;
+
+ /* Offsets (from start of header) and sizes (in bytes) of components */
+ uint32_t hwid_offset; /* HWID */
+ uint32_t hwid_size;
+ uint32_t rootkey_offset; /* Root key */
+ uint32_t rootkey_size;
+ uint32_t bmpfv_offset; /* BMP FV; deprecated in current FW */
+ uint32_t bmpfv_size;
+ uint32_t recovery_key_offset; /* Recovery key */
+ uint32_t recovery_key_size;
+
+ /* Added in version 1.2 */
+ uint8_t hwid_digest[VB2_GBB_HWID_DIGEST_SIZE]; /* SHA-256 of HWID */
+
+ /* Pad to match VB2_GBB_HEADER_SIZE. Initialize to 0. */
+ uint8_t pad[48];
+} __packed;
+BUILD_ASSERT(sizeof(struct vb2_gbb_header) == VB2_GBB_HEADER_SIZE);
+BUILD_ASSERT(offsetof(struct vb2_gbb_header, flags) ==
+ VB2_GBB_HEADER_FLAG_OFFSET);
+
+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;
+ /*
+ * The FMAP range information. The FMAP must be in the hash ranges for
+ * verification to pass.
+ */
+ struct ro_range fmap;
+ /* The full GBB range information. */
+ struct ro_range gbb;
+ /*
+ * The GBB flag range information. The flags are only verified if
+ * they're in the hash.
+ */
+ struct ro_range gbb_flags;
+} __packed;
/*****************************************************************************/
/* V1 Factory Support (AP_RO_HASH_TYPE_FACTORY) */
@@ -359,6 +433,15 @@ static enum ap_ro_check_vc_errors ap_ro_check_unsupported(int add_flash_event)
return ARCVE_OK;
}
+/* Returns true if part_range is fully within full_range. */
+static bool is_in_range(const struct ro_range part_range,
+ const struct ro_range full_range)
+{
+ return (part_range.flash_offset >= full_range.flash_offset) &&
+ (part_range.flash_offset + part_range.range_size <=
+ full_range.flash_offset + full_range.range_size);
+}
+
/**
* Validate hash of AP flash ranges.
*
@@ -404,8 +487,8 @@ enum ap_ro_check_result validate_ranges_sha(const struct ro_range *ranges,
return ROV_SUCCEEDED;
}
-
-#ifdef FIND_FMAP
+/*****************************************************************************/
+/* V1 Factory Verify GBB Support */
/**
* Read AP flash area into provided buffer.
*
@@ -435,22 +518,153 @@ static int read_ap_spi(void *buf, uint32_t offset, size_t size, int code_line)
return 0;
}
+/**
+ * Find GBB FMAP area in the FMAP table.
+ *
+ * @param offset offset of the fmap in the flash
+ * @param nareas number of areas in fmap
+ * @param gbbah fmap area header to save GBB area information in
+ *
+ * @return zero on success, -1 if the GBB is not found.
+ */
+static int find_gbb_fmah(uint32_t offset, uint16_t nareas,
+ struct fmap_area_header *gbbah)
+{
+ uint16_t i;
+ struct fmap_area_header fmah;
+
+ if (nareas > 64) {
+ CPRINTS("%s: too many areas: %d", __func__, nareas);
+ return -1;
+ }
+
+ for (i = 0; i < nareas; i++) {
+ if (read_ap_spi(&fmah, offset, sizeof(fmah), __LINE__))
+ return -1;
+
+ if (!memcmp(fmah.area_name, FMAP_GBB_AREA_NAME,
+ sizeof(FMAP_GBB_AREA_NAME))) {
+ memcpy(gbbah, &fmah, sizeof(*gbbah));
+ return 0;
+ }
+ offset += sizeof(fmah);
+ }
+
+ CPRINTS("Could not find %s area", FMAP_GBB_AREA_NAME);
+
+ return -1;
+}
+
+/*
+ * If the range is covered by one of the AP RO Verification ranges, then the
+ * range is in the hash.
+ *
+ * Returns:
+ * EC_SUCCESS if if the range is in one of the AP RO ranges.
+ * EC_ERROR_CRC if p_chk is invalid.
+ * EC_ERROR_INVAL if p_chk is valid, but range isn't in one of the AP RO
+ * ranges.
+ */
+static int range_is_in_hash(const struct ro_range range)
+{
+ uint16_t i;
+
+ for (i = 0; i < p_chk->header.num_ranges; i++) {
+ if (is_in_range(range, p_chk->payload.ranges[i]))
+ return EC_SUCCESS;
+ }
+ return EC_ERROR_INVAL;
+}
+
+/*
+ * 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.
+ *
+ * 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_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.
+ */
+ if (!gbbd->validate_flags) {
+ CPRINTS("%s: GBB flags not in hash", __func__);
+ return ROV_SUCCEEDED;
+ }
+
+ if (gbbd->gbb.range_size < VB2_GBB_HEADER_SIZE) {
+ CPRINTS("%s: invalid GBB size %u", __func__,
+ gbbd->gbb.range_size);
+ return ROV_FAILED;
+ }
+
+ /* Read and verify the contents of the GBB */
+ if (read_ap_spi(&gbb, gbbd->gbb.flash_offset, sizeof(gbb), __LINE__))
+ return ROV_FAILED;
+
+ /*
+ * Verify the gbb version and signature to make sure it looks
+ * reasonable.
+ */
+ if (gbb.header_size != VB2_GBB_HEADER_SIZE) {
+ CPRINTS("%s: inconsistent contents", __func__);
+ return ROV_FAILED;
+ }
+ if ((gbb.major_version != VB2_GBB_MAJOR_VER) ||
+ (gbb.minor_version != VB2_GBB_MINOR_VER)) {
+ CPRINTS("%s: unsupported ver %d %d", __func__,
+ gbb.major_version, gbb.minor_version);
+ return ROV_FAILED;
+ }
+ if (memcmp(gbb.signature, VB2_GBB_SIGNATURE,
+ VB2_GBB_SIGNATURE_SIZE)) {
+ CPRINTS("%s: invalid signature", __func__);
+ return ROV_FAILED;
+ }
+
+ /* Verify the GBB flags are set to 0. */
+ if (gbb.flags) {
+ CPRINTS("%s: invalid flags %x", __func__, gbb.flags);
+ return ROV_FAILED;
+ }
+
+ CPRINTS("%s: ok", __func__);
+ return ROV_SUCCEEDED;
+}
/*
- * Find the FMAP in RO flash.
+ * Initialize the gbb descriptor using the FMAP and GBB found in RO flash.
*
- * Iterate through AP flash at 4K intervals looking for FMAP.
- * This isn't used right now. It was used as a part of v2 support. It'll
- * get used to find the gbb in a followup cl.
+ * 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.
*
- * Return ROV_SUCCEEDED if a valid FMAP was found, ROV_FAILED otherwise.
+ * @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 find_fmap(void)
+static enum ap_ro_check_result init_gbbd(struct gbb_descriptor *gbbd)
{
uint32_t offset;
+ int rv;
for (offset = 0; offset < MAX_SUPPORTED_FLASH_SIZE;
offset += LOWEST_FMAP_ALIGNMENT) {
struct fmap_header fmh;
+ struct fmap_area_header gbbah;
if (read_ap_spi(fmh.fmap_signature, offset,
sizeof(fmh.fmap_signature), __LINE__))
@@ -475,12 +689,38 @@ static enum ap_ro_check_result find_fmap(void)
continue;
}
+ /* Find the GBB area header in FMAP. */
+ if (find_gbb_fmah(offset + sizeof(struct fmap_header),
+ fmh.fmap_nareas, &gbbah))
+ continue;
+
+ gbbd->fmap.flash_offset = offset;
+ gbbd->fmap.range_size = sizeof(struct fmap_header) +
+ sizeof(struct fmap_area_header) * fmh.fmap_nareas;
+ /*
+ * The FMAP isn't in the AP RO hash, so the GBB location can't
+ * be trusted. Continue searching for a fmap in the hash.
+ */
+ rv = range_is_in_hash(gbbd->fmap);
+ if (rv != EC_SUCCESS) {
+ CPRINTS("%s: FMAP(%x:%x) not in hash.", __func__,
+ gbbd->fmap.flash_offset, gbbd->fmap.range_size);
+ continue;
+ }
+
+ gbbd->gbb.flash_offset = gbbah.area_offset;
+ gbbd->gbb.range_size = gbbah.area_size;
+ 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;
+
return ROV_SUCCEEDED;
}
return ROV_FAILED;
}
-#endif /* FIND_FMAP */
/*
* A hook used to keep the EC in reset, no matter what keys the user presses,
@@ -532,6 +772,7 @@ int ec_rst_override(void)
static uint8_t do_ap_ro_check(void)
{
enum ap_ro_check_result rv;
+ struct gbb_descriptor gbbd;
apro_result = AP_RO_IN_PROGRESS;
apro_fail_status_cleared = 0;
@@ -544,9 +785,15 @@ static uint8_t do_ap_ro_check(void)
enable_ap_spi_hash_shortcut();
- rv = validate_ranges_sha(p_chk->payload.ranges,
- p_chk->header.num_ranges,
- p_chk->payload.digest);
+ /* 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)
+ rv = validate_ranges_sha(p_chk->payload.ranges,
+ p_chk->header.num_ranges,
+ p_chk->payload.digest);
disable_ap_spi_hash_shortcut();
@@ -567,7 +814,7 @@ static uint8_t do_ap_ro_check(void)
*/
return EC_ERROR_CRC;
}
- apro_result = AP_RO_PASS_UNVERIFIED_GBB;
+ apro_result = AP_RO_PASS;
ap_ro_add_flash_event(APROF_CHECK_SUCCEEDED);
CPRINTS("AP RO PASS!");
release_ec_reset_override();