summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--board/cr50/board_id.c269
-rw-r--r--board/cr50/board_id.h31
-rw-r--r--board/cr50/build.mk1
-rw-r--r--chip/g/flash_info.h23
-rw-r--r--chip/g/signed_header.h17
-rw-r--r--include/tpm_vendor_cmds.h4
6 files changed, 335 insertions, 10 deletions
diff --git a/board/cr50/board_id.c b/board/cr50/board_id.c
new file mode 100644
index 0000000000..f6f3202a96
--- /dev/null
+++ b/board/cr50/board_id.c
@@ -0,0 +1,269 @@
+/* Copyright 2017 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.
+ */
+
+#include "common.h"
+#include "board_id.h"
+#include "endian.h"
+#include "extension.h"
+#include "flash_info.h"
+#include "signed_header.h"
+#include "system.h"
+#include "util.h"
+
+#define CPRINTS(format, args...) cprints(CC_RBOX, format, ## args)
+#define CPRINTF(format, args...) cprintf(CC_RBOX, format, ## args)
+
+/**
+ * Return the image header for the current image copy
+ */
+const struct SignedHeader *get_current_image_header(void)
+{
+ return (const struct SignedHeader *)
+ get_program_memory_addr(system_get_image_copy());
+}
+
+/**
+ * Check the current header vs. the supplied Board ID
+ *
+ * @param board_id Pointer to a Board ID structure to check
+ * @param h Pointer to the currently running image's header
+ *
+ * @return 0 if no mismatch, non-zero if mismatch
+ */
+static uint32_t check_board_id_vs_header(const struct board_id *id,
+ const struct SignedHeader *h)
+{
+ uint32_t mismatch;
+ uint32_t header_board_id_type;
+ uint32_t header_board_id_mask;
+ uint32_t header_board_id_flags;
+
+ /* Blank Board ID matches all headers */
+ if (~(id->type & id->type_inv & id->flags) == 0)
+ return 0;
+
+ header_board_id_type = SIGNED_HEADER_PADDING ^ h->board_id_type;
+ header_board_id_mask = SIGNED_HEADER_PADDING ^ h->board_id_type_mask;
+ header_board_id_flags = SIGNED_HEADER_PADDING ^ h->board_id_flags;
+
+ /* Blank header means this is a common image, can run on any device. */
+ if ((header_board_id_type |
+ header_board_id_mask |
+ header_board_id_flags) == 0)
+ return 0;
+
+ /*
+ * Masked bits in header Board ID type must match type and inverse from
+ * flash.
+ */
+ mismatch = header_board_id_type ^ id->type;
+ mismatch |= header_board_id_type ^ ~id->type_inv;
+ mismatch &= header_board_id_mask;
+
+ /*
+ * All 1-bits in header Board ID flags must be present in flags from
+ * flash
+ */
+ mismatch |=
+ ((header_board_id_flags & id->flags) != header_board_id_flags);
+
+ return mismatch;
+}
+
+/**
+ * Check board ID from the flash INFO1 space.
+ *
+ * @param id Pointer to a Board ID structure to fill
+ *
+ * @return EC_SUCCESS of an error code in cases of vairous failures to read.
+ */
+static int read_board_id(struct board_id *id)
+{
+ uint32_t *id_p;
+ int i;
+
+ /*
+ * Board ID structure size is guaranteed to be divisible by 4, and it
+ * is guaranteed to be aligned at 4 bytes.
+ */
+
+ id_p = (uint32_t *)id;
+
+ /* Make sure INFO1 board ID space is readable */
+ if (flash_info_read_enable(INFO_BOARD_SPACE_OFFSET,
+ INFO_BOARD_SPACE_PROTECT_SIZE) !=
+ EC_SUCCESS) {
+ CPRINTS("%s: failed to enable read access to info", __func__);
+ return EC_ERROR_ACCESS_DENIED;
+ }
+
+ for (i = 0; i < sizeof(*id); i += sizeof(uint32_t)) {
+ int rv;
+
+ rv = flash_physical_info_read_word
+ (INFO_BOARD_SPACE_OFFSET +
+ offsetof(struct info1_board_space, bid) + i,
+ id_p);
+ if (rv != EC_SUCCESS) {
+ CPRINTF("%s: failed to read word %d, error %d\n",
+ i, rv);
+ return rv;
+ }
+ id_p++;
+ }
+ return EC_SUCCESS;
+}
+
+/**
+ * Write board ID into the flash INFO1 space.
+ *
+ * @param id Pointer to a Board ID structure to copy into INFO1
+ *
+ * @return EC_SUCCESS or an error code in cases of various failures to read or
+ * if the space has been already initialized.
+ */
+static int write_board_id(const struct board_id *id)
+{
+ struct board_id id_test;
+ uint32_t rv;
+
+ /*
+ * Make sure the current header will still validate against the
+ * proposed values. If it doesn't, then programming these values
+ * would cause the next boot to fail.
+ */
+ if (check_board_id_vs_header(id, get_current_image_header()) != 0) {
+ CPRINTS("%s: Board ID wouldn't allow current header", __func__);
+ return EC_ERROR_INVAL;
+ }
+
+ /* Fail if Board ID is already programmed */
+ rv = read_board_id(&id_test);
+ if (rv != EC_SUCCESS) {
+ CPRINTS("%s: error reading Board ID", __func__);
+ return rv;
+ }
+
+ if (~(id_test.type & id_test.type_inv & id_test.flags) != 0) {
+ CPRINTS("%s: Board ID already programmed", __func__);
+ return EC_ERROR_ACCESS_DENIED;
+ }
+
+ /* Enable write access */
+ if (flash_info_write_enable(INFO_BOARD_SPACE_OFFSET,
+ INFO_BOARD_SPACE_PROTECT_SIZE) !=
+ EC_SUCCESS) {
+ CPRINTS("%s: failed to enable write access", __func__);
+ return EC_ERROR_ACCESS_DENIED;
+ }
+
+ /* Write Board ID */
+ rv = flash_info_physical_write(INFO_BOARD_SPACE_OFFSET +
+ offsetof(struct info1_board_space, bid),
+ sizeof(*id), (const char *)id);
+ if (rv != EC_SUCCESS)
+ CPRINTS("%s: write failed", __func__);
+
+ /* Disable write access */
+ flash_info_write_disable();
+
+ return rv;
+}
+
+static enum vendor_cmd_rc vc_set_board_id(enum vendor_cmd_cc code,
+ void *buf,
+ size_t input_size,
+ size_t *response_size)
+{
+ struct board_id id;
+ uint8_t *pbuf = buf;
+
+ *response_size = 1;
+
+ /* Exactly two fields are expected. */
+ if (input_size != sizeof(id.type) + sizeof(id.flags)) {
+ *pbuf = VENDOR_RC_BOGUS_ARGS;
+ return VENDOR_RC_BOGUS_ARGS;
+ }
+
+ memcpy(&id.type, pbuf, sizeof(id.type));
+ id.type = be32toh(id.type);
+ id.type_inv = ~id.type;
+
+ memcpy(&id.flags, pbuf + sizeof(id.type), sizeof(id.flags));
+ id.flags = be32toh(id.flags);
+
+ /* We care about the LSB only. */
+ *pbuf = write_board_id(&id);
+
+ return *pbuf;
+}
+DECLARE_VENDOR_COMMAND(VENDOR_CC_SET_BOARD_ID, vc_set_board_id);
+
+static int command_board_id(int argc, char **argv)
+{
+ struct board_id id;
+ int rv = EC_ERROR_PARAM_COUNT;
+
+ if (argc == 1) {
+ rv = read_board_id(&id);
+
+ if (rv != EC_SUCCESS) {
+ ccprintf("Failed to read board ID space\n");
+ return rv;
+ }
+ ccprintf("Board ID: %08x, flags %08x\n", id.type, id.flags);
+
+ if ((~id.type | ~id.type_inv | ~id.flags) == 0)
+ return rv; /* The space is not initialized. */
+
+ if (id.type != ~id.type_inv)
+ ccprintf("Inv Type Mismatch (%08x instead of %08x)!\n",
+ id.type_inv, ~id.type);
+ }
+#ifdef CR50_DEV
+ else if (argc == 3) {
+ char *e;
+
+ id.type = strtoi(argv[1], &e, 0);
+ if (*e)
+ return EC_ERROR_PARAM1;
+ id.type_inv = ~id.type;
+ id.flags = strtoi(argv[2], &e, 0);
+ if (*e)
+ return EC_ERROR_PARAM2;
+
+ rv = write_board_id(&id);
+ } else {
+ ccprintf("specify board type and flags\n");
+ rv = EC_ERROR_PARAM_COUNT;
+ }
+#endif
+ return rv;
+}
+DECLARE_SAFE_CONSOLE_COMMAND(bid,
+ command_board_id, NULL, "Set/Get Board ID");
+
+static enum vendor_cmd_rc vc_get_board_id(enum vendor_cmd_cc code,
+ void *buf,
+ size_t input_size,
+ size_t *response_size)
+{
+ struct board_id id;
+
+ if (read_board_id(&id))
+ return VENDOR_RC_READ_FLASH_FAIL;
+
+ /* Convert to line representation. */
+ id.type = htobe32(id.type);
+ id.type_inv = htobe32(id.type_inv);
+ id.flags = htobe32(id.flags);
+
+ memcpy(buf, &id, sizeof(id));
+ *response_size = sizeof(id);
+
+ return VENDOR_RC_SUCCESS;
+}
+DECLARE_VENDOR_COMMAND(VENDOR_CC_GET_BOARD_ID, vc_get_board_id);
diff --git a/board/cr50/board_id.h b/board/cr50/board_id.h
new file mode 100644
index 0000000000..fefcd4c36b
--- /dev/null
+++ b/board/cr50/board_id.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2017 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 __EC_BOARD_CR50_BOARD_ID__H
+#define __EC_BOARD_CR50_BOARD_ID__H
+
+#include "util.h"
+
+/* Structure holding Board ID */
+struct board_id {
+ uint32_t type; /* Board type */
+ uint32_t type_inv; /* Board type (inverted) */
+ uint32_t flags; /* Flags */
+};
+
+/* Info1 Board space contents. */
+struct info1_board_space {
+ struct board_id bid;
+};
+
+#define INFO_BOARD_ID_SIZE sizeof(struct board_id)
+#define INFO_BOARD_SPACE_PROTECT_SIZE 16
+
+BUILD_ASSERT((offsetof(struct info1_board_space, bid) & 3) == 0);
+BUILD_ASSERT((INFO_BOARD_ID_SIZE & 3) == 0);
+BUILD_ASSERT(sizeof(struct info1_board_space) <= INFO_BOARD_SPACE_PROTECT_SIZE);
+
+#endif /* ! __EC_BOARD_CR50_BOARD_ID_H */
diff --git a/board/cr50/build.mk b/board/cr50/build.mk
index 688f36882f..e625a1f93c 100644
--- a/board/cr50/build.mk
+++ b/board/cr50/build.mk
@@ -30,6 +30,7 @@ dirs-y += $(BDIR)/tpm2
# Objects that we need to build
board-y = board.o
+board-y += board_id.o
board-${CONFIG_RDD} += rdd.o
board-${CONFIG_USB_SPI} += usb_spi.o
board-${CONFIG_USB_I2C} += usb_i2c.o
diff --git a/chip/g/flash_info.h b/chip/g/flash_info.h
index 9ff730b39e..e07fac1ed7 100644
--- a/chip/g/flash_info.h
+++ b/chip/g/flash_info.h
@@ -11,17 +11,24 @@
#include "signed_header.h"
/*
- * Info1 space available to the app firmware is split in several areas. Of
- * interest are the two spaces used for rollback prevention of RO and RW image
- * versions.
+ * Info1 space available to the app firmware is split in four equal size
+ * areas, used as follows:
*
- * Each bit in the image infomap header section is mapped into a 4 byte word
- * in the Info1 space.
+ * Area 0 - RO rollback prevention
+ * Area 1 - RW rollback prevention
+ * Area 2 - Board specific stuff
+ * Area 3 - Crypto scratch
*/
+#define INFO_AREA_SIZE (INFO_MAX * 4)
+#define INFO_TOTAL_SIZE (INFO_AREA_SIZE * 4)
+
#define INFO_RO_MAP_OFFSET 0
-#define INFO_RO_MAP_SIZE (INFO_MAX * 4)
-#define INFO_RW_MAP_OFFSET INFO_RO_MAP_SIZE
-#define INFO_RW_MAP_SIZE (INFO_MAX * 4)
+#define INFO_RO_MAP_SIZE INFO_AREA_SIZE
+
+#define INFO_RW_MAP_OFFSET (INFO_RO_MAP_OFFSET + INFO_RO_MAP_SIZE)
+#define INFO_RW_MAP_SIZE INFO_AREA_SIZE
+
+#define INFO_BOARD_SPACE_OFFSET (INFO_RW_MAP_OFFSET + INFO_RW_MAP_SIZE)
int flash_info_read_enable(uint32_t offset, size_t size);
/* This in fact enables both read and write. */
diff --git a/chip/g/signed_header.h b/chip/g/signed_header.h
index c4e6726c40..cafe858718 100644
--- a/chip/g/signed_header.h
+++ b/chip/g/signed_header.h
@@ -14,6 +14,9 @@
#define INFO_MAX 128 /* baked in rom! */
#define INFO_IGNORE 0xaa3c55c3 /* baked in rom! */
+/* Default value for _pad[] words */
+#define SIGNED_HEADER_PADDING 0x33333333
+
struct SignedHeader {
uint32_t magic; /* -1 (thanks, boot_sys!) */
uint32_t signature[96];
@@ -42,8 +45,18 @@ struct SignedHeader {
uint32_t err_response_;
/* action to take when expectation is violated */
uint32_t expect_response_;
- uint32_t _pad[256 - 1 - 96 - 1 - 7 - 1 - 96 -
- 5*1 - 4 - 4 - 9*1 - 2 - 1];
+ /*
+ * Padding to bring the total structure size to 1K. Note: First 17
+ * words of _pad[] may be used by a second FIPS-compliant signature,
+ * so don't put anything there.
+ */
+ uint32_t _pad[24];
+ /* Board ID type, mask, flags (stored ^SIGNED_HEADER_PADDING) */
+ uint32_t board_id_type;
+ uint32_t board_id_type_mask;
+ uint32_t board_id_flags;
+ uint32_t dev_id0_; /* node id, if locked */
+ uint32_t dev_id1_;
uint32_t fuses_chk_; /* top 32 bit of expected fuses hash */
uint32_t info_chk_; /* top 32 bit of expected info hash */
};
diff --git a/include/tpm_vendor_cmds.h b/include/tpm_vendor_cmds.h
index c6a04f17f2..4861457d36 100644
--- a/include/tpm_vendor_cmds.h
+++ b/include/tpm_vendor_cmds.h
@@ -38,6 +38,8 @@ enum vendor_cmd_cc {
VENDOR_CC_REPORT_TPM_STATE = 23,
VENDOR_CC_TURN_UPDATE_ON = 24,
+ VENDOR_CC_GET_BOARD_ID = 25,
+ VENDOR_CC_SET_BOARD_ID = 26,
LAST_VENDOR_COMMAND = 65535,
};
@@ -54,6 +56,8 @@ enum vendor_cmd_rc {
/* Our TPMv2 vendor-specific response codes. */
VENDOR_RC_SUCCESS = 0,
VENDOR_RC_BOGUS_ARGS = 1,
+ VENDOR_RC_READ_FLASH_FAIL = 2,
+ VENDOR_RC_WRITE_FLASH_FAIL = 3,
/* Only 7 bits available; max is 127 */
VENDOR_RC_NO_SUCH_COMMAND = 127,
};