diff options
author | Philip Chen <philipchen@google.com> | 2017-05-12 15:17:32 -0700 |
---|---|---|
committer | ChromeOS Commit Bot <chromeos-commit-bot@chromium.org> | 2017-06-30 01:24:50 +0000 |
commit | 4df96eee9f810e8265b026c30cd29f56960533c1 (patch) | |
tree | 7839f165172252fb79a6340800e883badbe975d5 | |
parent | 976c99634430026b6d6f38ee89aba01545ce1b14 (diff) | |
download | chrome-ec-stabilize-9592.67.B.tar.gz |
cr50: Add console and TPM vendor commands to get/set board IDstabilize-9592.82.Bstabilize-9592.67.Bstabilize-9592.55.Brelease-R60-9592.B
This patch adds vendor and console commands to read and write the
board ID space in the INFO1 block.
Current image's board ID settings are saved in the image header by the
latest codesigner.
Board ID write attempts are rejected if the board ID space is already
initialized, or if the currently running image will not be allowed to
run with the new board ID space settings.
Error codes are returned to the caller as a single byte value.
Successful read command returns 12 bytes of the board ID space
contents.
The console command always allows to read the board ID value, and
allows to write it if the image was built with debug enabled.
BUG=b:35586335
BRANCH=cr50
TEST=as follows:
- verified that board ID can be read by any image and set by debug
images.
- with the upcoming patches verified the ability to set and read
board ID values using vendor commands.
Change-Id: I35a3e2db92175a29de8011172b80091065b27414
Signed-off-by: Philip Chen <philipchen@google.com>
Signed-off-by: Vadim Bendebury <vbendeb@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/522234
Reviewed-by: Aseda Aboagye <aaboagye@chromium.org>
Reviewed-by: Mary Ruthven <mruthven@chromium.org>
(cherry picked from commit ee545922389739b39cc0ac7e0f0d1dd8c2c67607)
Reviewed-on: https://chromium-review.googlesource.com/557504
-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, }; |