diff options
-rw-r--r-- | board/cr50/board_id.c | 269 | ||||
-rw-r--r-- | board/cr50/board_id.h | 31 | ||||
-rw-r--r-- | board/cr50/build.mk | 1 | ||||
-rw-r--r-- | chip/g/flash_info.h | 23 | ||||
-rw-r--r-- | chip/g/signed_header.h | 17 | ||||
-rw-r--r-- | include/tpm_vendor_cmds.h | 4 |
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, }; |