summaryrefslogtreecommitdiff
path: root/board/cr50/board_id.c
diff options
context:
space:
mode:
Diffstat (limited to 'board/cr50/board_id.c')
-rw-r--r--board/cr50/board_id.c269
1 files changed, 269 insertions, 0 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);