summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVadim Bendebury <vbendeb@chromium.org>2020-04-24 18:41:17 -0700
committerCommit Bot <commit-bot@chromium.org>2020-05-22 03:35:06 +0000
commit907629953aaf6a03fe2cf73de1748e40e580a073 (patch)
treed6ded95d24b4308faf14d1600845206a195d1cf2
parent70c81db54fff163b7b4bfe2b4a02e2b10e906bed (diff)
downloadchrome-ec-907629953aaf6a03fe2cf73de1748e40e580a073.tar.gz
Add AP RO integrity check implementation.
This patch adds code which accepts the vendor command communicating the list of the AP firmware sections to verify and the expected cumulative sha256 sum value of the sections. The vendor command payload is checked for sanity: each range offset is not expected to exceed 32M bytes (the largest possible SPI flash size) and each size is not expected to exceed 4M bytes. If any inconsistencies are found in the payload, or the flash integrity space is already programmed, an error is returned to the AP. It the command validity check succeeds, the payload of the vendor command is prepended by a header including the number of the flash regions to check and a 4 byte checksum of the stored information. This combined information is stored in the dedicated H1 flash space, specifically the RO_B region, at offset of 0x3000, 2K bytes page below the region used for the flash log. The valid RO range in upgrade_fw.c:set_valid_sections() is modified to prevent erasing of the AP RO hash value during Cr50 RO updates. The new file also introduces a function used to verify the AP flash when requested. The returned value indicates one of three conditions: - valid verification information not found - AP flash integrity verification failed - AP flash integrity verification succeeded A new console command allows to examine the contents of the space where the list of ranges and the sum are stored. CR50_DEV builds also allow to erase the page. BUG=b:153764696 TEST=with the rest of the patches applied verified successful execution of the AP RO verification sequence. Signed-off-by: Vadim Bendebury <vbendeb@chromium.org> Change-Id: I1894ef897a86e9d60b9f5bcff3a680f632239e1b Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2171398 Reviewed-by: Andrey Pronin <apronin@chromium.org>
-rw-r--r--chip/g/config_chip.h8
-rw-r--r--chip/g/upgrade_fw.c3
-rw-r--r--common/ap_ro_integrity_check.c267
-rw-r--r--include/ap_ro_integrity_check.h22
-rw-r--r--include/tpm_vendor_cmds.h2
5 files changed, 300 insertions, 2 deletions
diff --git a/chip/g/config_chip.h b/chip/g/config_chip.h
index 7c60567dfc..dcd85c86d1 100644
--- a/chip/g/config_chip.h
+++ b/chip/g/config_chip.h
@@ -160,6 +160,14 @@
(CONFIG_PROGRAM_MEMORY_BASE + CHIP_RO_B_MEM_OFF + CONFIG_RO_SIZE - \
CONFIG_FLASH_LOG_SPACE)
+/* Space reserved for RO hashes */
+#define AP_RO_DATA_SPACE_SIZE CONFIG_FLASH_BANK_SIZE
+#define AP_RO_DATA_SPACE_ADDR (CONFIG_FLASH_LOG_BASE - AP_RO_DATA_SPACE_SIZE)
+
+/* Maximum space available for the RO image */
+#define MAX_RO_CODE_SIZE (CONFIG_RO_SIZE - CONFIG_FLASH_LOG_SPACE - \
+ AP_RO_DATA_SPACE_SIZE)
+
/* Use software crypto (libcryptoc). */
#define CONFIG_LIBCRYPTOC
#endif /* __CROS_EC_CONFIG_CHIP_H */
diff --git a/chip/g/upgrade_fw.c b/chip/g/upgrade_fw.c
index 727af4b7d9..f400b4f317 100644
--- a/chip/g/upgrade_fw.c
+++ b/chip/g/upgrade_fw.c
@@ -63,8 +63,7 @@ static void set_valid_sections(void)
}
valid_sections.ro_top_offset = valid_sections.ro_base_offset +
- CONFIG_RO_SIZE - 0x800; /* 2K for certs! */
-
+ MAX_RO_CODE_SIZE;
valid_sections.rw_top_offset = valid_sections.rw_base_offset +
CONFIG_RW_SIZE;
}
diff --git a/common/ap_ro_integrity_check.c b/common/ap_ro_integrity_check.c
new file mode 100644
index 0000000000..bb02f306db
--- /dev/null
+++ b/common/ap_ro_integrity_check.c
@@ -0,0 +1,267 @@
+/* Copyright 2020 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Code supporting AP RO verification.
+ */
+
+#include "console.h"
+#include "crypto_api.h"
+#include "extension.h"
+#include "flash.h"
+#include "flash_info.h"
+#include "cryptoc/sha256.h"
+#include "stddef.h"
+#include "stdint.h"
+#include "timer.h"
+#include "usb_spi.h"
+
+#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ##args)
+#define CPRINTF(format, args...) cprintf(CC_SYSTEM, format, ##args)
+
+/* A flash range included in hash calculations. */
+struct flash_range {
+ uint32_t flash_offset;
+ uint32_t range_size;
+} __packed;
+
+/* Values used for sanity check of the flash_range structure fields. */
+#define MAX_SUPPORTED_FLASH_SIZE (32 * 1024 * 1024)
+#define MAX_SUPPORTED_RANGE_SIZE (4 * 1024 * 1024)
+
+/* Page offset for H1 flash operations. */
+static const uint32_t h1_flash_offset_ =
+ AP_RO_DATA_SPACE_ADDR - CONFIG_PROGRAM_MEMORY_BASE;
+
+/*
+ * Payload of the vendor command communicating a variable number of flash
+ * ranges to be checked and the total sha256.
+ *
+ * The actual number of ranges is determined based on the actual payload size.
+ */
+struct ap_ro_check_payload {
+ uint8_t digest[SHA256_DIGEST_SIZE];
+ struct flash_range ranges[0];
+} __packed;
+
+/* Version of the AP RO check information saved in the H1 flash page. */
+#define AP_RO_HASH_LAYOUT_VERSION 0
+
+/*
+ * Header added for storing of the AP RO check information in the H1 flash
+ * page. The checksum is a 4 byte truncated sha256 of the saved payload, just
+ * a sanity check.
+ */
+struct ap_ro_check_header {
+ uint16_t version;
+ uint16_t num_ranges;
+ uint32_t checksum;
+};
+
+/* 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;
+};
+
+/* Fixed pointer at the H1 flash page storing the AP RO check information. */
+static const struct ap_ro_check *p_chk =
+ (const struct ap_ro_check *)AP_RO_DATA_SPACE_ADDR;
+
+/* Errors recognized and returned by the vendor command handler. */
+enum ap_ro_check_vc_errors {
+ ARCVE_TOO_SHORT = 1,
+ ARCVE_BAD_PAYLOAD_SIZE = 2,
+ ARCVE_BAD_OFFSET = 3,
+ ARCVE_BAD_RANGE_SIZE = 4,
+ ARCVE_ALREADY_PROGRAMMED = 5,
+};
+
+static enum vendor_cmd_rc vc_seed_ap_ro_check(enum vendor_cmd_cc code,
+ void *buf, size_t input_size,
+ size_t *response_size)
+{
+ struct ap_ro_check_header check_header;
+ const struct ap_ro_check_payload *vc_payload = buf;
+ uint32_t vc_num_of_ranges;
+ uint32_t i;
+ uint8_t *response = buf;
+ size_t prog_size;
+
+ *response_size = 1; /* Just in case there is an error. */
+
+ /* There should be at least one range and the hash. */
+ if (input_size < (SHA256_DIGEST_SIZE + sizeof(struct flash_range))) {
+ *response = ARCVE_TOO_SHORT;
+ return VENDOR_RC_BOGUS_ARGS;
+ }
+
+ /* There should be an integer number of ranges. */
+ if (((input_size - SHA256_DIGEST_SIZE) % sizeof(struct flash_range)) !=
+ 0) {
+ *response = ARCVE_BAD_PAYLOAD_SIZE;
+ return VENDOR_RC_BOGUS_ARGS;
+ }
+
+ vc_num_of_ranges =
+ (input_size - SHA256_DIGEST_SIZE) / sizeof(struct flash_range);
+
+ for (i = 0; i < vc_num_of_ranges; i++) {
+ if (vc_payload->ranges[i].range_size >
+ MAX_SUPPORTED_RANGE_SIZE) {
+ *response = ARCVE_BAD_RANGE_SIZE;
+ return VENDOR_RC_BOGUS_ARGS;
+ }
+ if ((vc_payload->ranges[i].flash_offset +
+ vc_payload->ranges[i].range_size) >
+ MAX_SUPPORTED_FLASH_SIZE) {
+ *response = ARCVE_BAD_OFFSET;
+ return VENDOR_RC_BOGUS_ARGS;
+ }
+ }
+
+ prog_size = sizeof(struct ap_ro_check_header) + input_size;
+ for (i = 0; i < (prog_size / sizeof(uint32_t)); i++)
+ if (((uint32_t *)p_chk)[i] != ~0) {
+ *response = ARCVE_ALREADY_PROGRAMMED;
+ return VENDOR_RC_NOT_ALLOWED;
+ }
+
+ check_header.version = AP_RO_HASH_LAYOUT_VERSION;
+ check_header.num_ranges = vc_num_of_ranges;
+ app_compute_hash(buf, input_size, &check_header.checksum,
+ sizeof(check_header.checksum));
+
+ flash_open_ro_window(h1_flash_offset_, prog_size);
+ flash_physical_write(h1_flash_offset_, sizeof(check_header),
+ (char *)&check_header);
+ flash_physical_write(h1_flash_offset_ + sizeof(check_header),
+ input_size, buf);
+
+ *response_size = 0;
+ return VENDOR_RC_SUCCESS;
+}
+DECLARE_VENDOR_COMMAND(VENDOR_CC_SEED_AP_RO_CHECK, vc_seed_ap_ro_check);
+
+static int verify_ap_ro_check_space(void)
+{
+ uint32_t checksum;
+ size_t data_size;
+
+ data_size = p_chk->header.num_ranges * sizeof(struct flash_range) +
+ sizeof(struct ap_ro_check_payload);
+ if (data_size > CONFIG_FLASH_BANK_SIZE) {
+ CPRINTS("%s: bogus number of ranges %d", __func__,
+ p_chk->header.num_ranges);
+ return EC_ERROR_CRC;
+ }
+
+ app_compute_hash(&p_chk->payload, data_size, &checksum,
+ sizeof(checksum));
+
+ if (memcmp(&checksum, &p_chk->header.checksum, sizeof(checksum))) {
+ CPRINTS("%s: AP RO Checksum corrupted", __func__);
+ return EC_ERROR_CRC;
+ }
+
+ return EC_SUCCESS;
+}
+
+int validate_ap_ro(void)
+{
+ uint32_t i;
+ HASH_CTX ctx;
+ uint8_t digest[SHA256_DIGEST_SIZE];
+ int rv;
+
+ if (p_chk->header.num_ranges == (uint16_t)~0) {
+ CPRINTS("%s: RO verification not programmed", __func__);
+ return EC_ERROR_INVAL;
+ }
+
+ /* Is the contents intact? */
+ if (verify_ap_ro_check_space() != EC_SUCCESS)
+ return EC_ERROR_INVAL; /* No verification possible. */
+
+ enable_ap_spi_hash_shortcut();
+ usb_spi_sha256_start(&ctx);
+ for (i = 0; i < p_chk->header.num_ranges; i++) {
+ CPRINTS("%s: %x:%x", __func__,
+ p_chk->payload.ranges[i].flash_offset,
+ p_chk->payload.ranges[i].range_size);
+ /* Make sure the message gets out before verification starts. */
+ cflush();
+ usb_spi_sha256_update(&ctx,
+ p_chk->payload.ranges[i].flash_offset,
+ p_chk->payload.ranges[i].range_size);
+ }
+
+ usb_spi_sha256_final(&ctx, digest, sizeof(digest));
+ if (memcmp(digest, p_chk->payload.digest, sizeof(digest))) {
+ CPRINTS("AP RO verification FAILED!");
+ CPRINTS("Calculated digest %ph",
+ HEX_BUF(digest, sizeof(digest)));
+ CPRINTS("Stored digest %ph",
+ HEX_BUF(p_chk->payload.digest,
+ sizeof(p_chk->payload.digest)));
+ rv = EC_ERROR_CRC;
+ } else {
+ rv = EC_SUCCESS;
+ CPRINTS("AP RO verification SUCCEEDED!");
+ }
+ disable_ap_spi_hash_shortcut();
+
+ return rv;
+}
+
+static int ap_ro_info_cmd(int argc, char **argv)
+{
+ int rv;
+ int i;
+#ifdef CR50_DEV
+ int const max_args = 2;
+#else
+ int const max_args = 1;
+#endif
+
+ if (argc > max_args)
+ return EC_ERROR_PARAM_COUNT;
+#ifdef CR50_DEV
+ if (argc == max_args) {
+ if (strcasecmp(argv[1], "erase"))
+ return EC_ERROR_PARAM1;
+ /*
+ * TODO(vbendeb): Make this a partial erase, use refactored
+ * Board ID space partial erase.
+ */
+ flash_open_ro_window(h1_flash_offset_, AP_RO_DATA_SPACE_SIZE);
+ flash_physical_erase(h1_flash_offset_, AP_RO_DATA_SPACE_SIZE);
+ }
+#endif
+ if ((p_chk->header.num_ranges == (uint16_t)~0) &&
+ (p_chk->header.checksum == ~0)) {
+ ccprintf("AP RO check space is not programmed\n");
+ return EC_SUCCESS;
+ }
+
+ rv = verify_ap_ro_check_space();
+ if (rv != EC_SUCCESS)
+ return rv; /* No verification possible. */
+
+ ccprintf("sha256 hash %ph\n",
+ HEX_BUF(p_chk->payload.digest, sizeof(p_chk->payload.digest)));
+ ccprintf("Covered ranges:\n");
+ for (i = 0; i < p_chk->header.num_ranges; i++)
+ ccprintf("%08x...%08x\n", p_chk->payload.ranges[i].flash_offset,
+ p_chk->payload.ranges[i].flash_offset +
+ p_chk->payload.ranges[i].range_size - 1);
+
+ return EC_SUCCESS;
+}
+DECLARE_SAFE_CONSOLE_COMMAND(ap_ro_info, ap_ro_info_cmd,
+#ifdef CR50_DEV
+ "[erase]", "Display or erase AP RO check space"
+#else
+ "", "Display AP RO check space"
+#endif
+);
diff --git a/include/ap_ro_integrity_check.h b/include/ap_ro_integrity_check.h
new file mode 100644
index 0000000000..365bccbe8e
--- /dev/null
+++ b/include/ap_ro_integrity_check.h
@@ -0,0 +1,22 @@
+/* Copyright 2020 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ */
+#ifndef __CR50_INCLUDE_AP_RO_INTEGRITY_CHECK_H
+#define __CR50_INCLUDE_AP_RO_INTEGRITY_CHECK_H
+
+/*
+ * validate_ap_ro: based on information saved in an H1 RO flash page verify
+ * contents of the AP flash.
+ *
+ * Returns:
+ *
+ * EC_SUCCESS if valid integrity check information was found and the AP flash
+ * check succeeded.
+ * EC_ERROR_INVAL in valid integrity check information was not found.
+ * EC_ERROR_CRC if information was found, but the AP flash check failed.
+ */
+int validate_ap_ro(void);
+
+#endif /* ! __CR50_INCLUDE_AP_RO_INTEGRITY_CHECK_H */
diff --git a/include/tpm_vendor_cmds.h b/include/tpm_vendor_cmds.h
index 4cb3683d49..637ec05fe9 100644
--- a/include/tpm_vendor_cmds.h
+++ b/include/tpm_vendor_cmds.h
@@ -147,6 +147,8 @@ enum vendor_cmd_cc {
VENDOR_CC_GET_BOOT_MODE = 52,
VENDOR_CC_RESET_EC = 53,
+ VENDOR_CC_SEED_AP_RO_CHECK = 54,
+
LAST_VENDOR_COMMAND = 65535,
};