diff options
Diffstat (limited to 'chip/g/sn_bits.c')
-rw-r--r-- | chip/g/sn_bits.c | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/chip/g/sn_bits.c b/chip/g/sn_bits.c new file mode 100644 index 0000000000..2e12db832f --- /dev/null +++ b/chip/g/sn_bits.c @@ -0,0 +1,269 @@ +/* Copyright 2018 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 "board_space.h" +#include "console.h" +#include "extension.h" +#include "flash_info.h" +#include "util.h" +#include "wp.h" + +#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ## args) +#define CPRINTF(format, args...) cprintf(CC_SYSTEM, format, ## args) + +int read_sn_data(struct sn_data *sn) +{ + uint32_t *id_p; + int i; + + /* + * SN Bits structure size is guaranteed to be divisible by 4, and it + * is guaranteed to be aligned at 4 bytes. + */ + + id_p = (uint32_t *)sn; + + /* Make sure INFO1 sn bits space is readable */ + if (flash_info_read_enable(INFO_SN_DATA_OFFSET, + INFO_SN_DATA_PROTECT_SIZE) != + EC_SUCCESS) { + CPRINTS("%s: failed to enable read access to info", __func__); + return EC_ERROR_ACCESS_DENIED; + } + + for (i = 0; i < sizeof(*sn); i += sizeof(uint32_t)) { + int rv; + + rv = flash_physical_info_read_word + (INFO_SN_DATA_OFFSET + i, id_p); + if (rv != EC_SUCCESS) { + CPRINTF("%s: failed to read word %d, error %d\n", + __func__, i, rv); + return rv; + } + id_p++; + } + return EC_SUCCESS; +} + +static int write_sn_data(struct sn_data *sn_data, int header_only) +{ + int rv = EC_SUCCESS; + + /* Enable write access */ + if (flash_info_write_enable(INFO_SN_DATA_OFFSET, + INFO_SN_DATA_PROTECT_SIZE) != + EC_SUCCESS) { + CPRINTS("%s: failed to enable write access", __func__); + return EC_ERROR_ACCESS_DENIED; + } + + /* Write sn bits */ + rv = flash_info_physical_write(INFO_SN_DATA_OFFSET, + header_only ? + SN_HEADER_SIZE : sizeof(*sn_data), + (const char *)sn_data); + if (rv != EC_SUCCESS) + CPRINTS("%s: write failed", __func__); + + /* Disable write access */ + flash_info_write_disable(); + + return rv; +} +/** + * Initialize SN data space in flash INFO1, and write sn hash. This can only + * be called once per device; subsequent calls on a device that has already + * had the sn hash written will fail. + * + * @param id Pointer to a SN 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_sn_hash(const uint32_t sn_hash[3]) +{ + int rv = EC_ERROR_PARAM_COUNT; + int i; + struct sn_data sn_data; + + rv = read_sn_data(&sn_data); + if (rv != EC_SUCCESS) + return rv; + + /* Check the sn data space is currently uninitialized */ + for (i = 0; i < (sizeof(sn_data) / sizeof(uint32_t)); i++) + if (((uint32_t *) &sn_data)[i] != 0xffffffff) + return EC_ERROR_INVALID_CONFIG; + + sn_data.version = SN_DATA_VERSION; + memcpy(sn_data.sn_hash, sn_hash, sizeof(sn_data.sn_hash)); + + rv = write_sn_data(&sn_data, 0); + + return rv; +} + +static int increment_rma_count(uint8_t inc) +{ + int rv = EC_ERROR_PARAM_COUNT; + struct sn_data sn_data; + + rv = read_sn_data(&sn_data); + if (rv != EC_SUCCESS) + return rv; + + /* Make sure we know how to update this data */ + if (sn_data.version != SN_DATA_VERSION) + return EC_ERROR_INVALID_CONFIG; + + /* Don't allow incrementing more than the number of bits */ + if (inc > RMA_COUNT_BITS) + return EC_ERROR_INVAL; + + /* + * The RMA status is initially set to 0xff. We set bit 7 + * to 0 to indicate the device has been RMA'd at least once, + * and use the remaining bits as a count of how many times + * the device has been RMA'd. The number of 0s represents + * the number of RMAs. As there are only 7 bits available + * for the count, a value of 0x00 means the device has + * been RMA'd at least 7 times (but we do not know how many). + * + * We allow incrementing by 0 or n (rather than 0 or 1) so + * that a device in any state can be put into the RMA'd with + * unknown count (0x00) state with a single call to this + * function. + */ + sn_data.rma_status <<= inc; + sn_data.rma_status &= RMA_INDICATOR; + + rv = write_sn_data(&sn_data, 1); + + return rv; +} + +static enum vendor_cmd_rc vc_sn_set_hash(enum vendor_cmd_cc code, + void *buf, + size_t input_size, + size_t *response_size) +{ + uint32_t sn_hash[3]; + uint8_t *pbuf = buf; + + *response_size = 1; + + if (input_size != sizeof(sn_hash)) { + *pbuf = VENDOR_RC_BOGUS_ARGS; + return VENDOR_RC_BOGUS_ARGS; + } + + memcpy(&sn_hash, pbuf, sizeof(sn_hash)); + + /* We care about the LSB only. */ + *pbuf = (uint8_t) write_sn_hash(sn_hash); + + return *pbuf; +} +DECLARE_VENDOR_COMMAND(VENDOR_CC_SN_SET_HASH, vc_sn_set_hash); + +static enum vendor_cmd_rc vc_sn_inc_rma(enum vendor_cmd_cc code, + void *buf, + size_t input_size, + size_t *response_size) +{ + uint8_t *pbuf = buf; + + if (wp_is_asserted()) + return EC_ERROR_ACCESS_DENIED; + + *response_size = 1; + + if (input_size != sizeof(*pbuf)) { + *pbuf = VENDOR_RC_BOGUS_ARGS; + return VENDOR_RC_BOGUS_ARGS; + } + + /* We care about the LSB only. */ + *pbuf = (uint8_t) increment_rma_count(*pbuf); + + return *pbuf; +} +DECLARE_VENDOR_COMMAND(VENDOR_CC_SN_INC_RMA, vc_sn_inc_rma); + +static int command_sn(int argc, char **argv) +{ + int rv = EC_ERROR_PARAM_COUNT; + struct sn_data sn; + + switch (argc) { +#ifdef CR50_DEV + case 4: + { + char *e; + + sn.sn_hash[0] = strtoi(argv[1], &e, 0); + if (*e) + return EC_ERROR_PARAM1; + + sn.sn_hash[1] = strtoi(argv[2], &e, 0); + if (*e) + return EC_ERROR_PARAM2; + + sn.sn_hash[2] = strtoi(argv[3], &e, 0); + if (*e) + return EC_ERROR_PARAM3; + + rv = write_sn_hash(sn.sn_hash); + if (rv != EC_SUCCESS) + return rv; + + goto print_sn_data; + } + case 3: + { + int count; + char *e; + + if (strcasecmp(argv[1], "rmainc") != 0) + return EC_ERROR_PARAM1; + + count = strtoi(argv[2], &e, 0); + if (*e || count > 7) + return EC_ERROR_PARAM2; + + rv = increment_rma_count(count); + if (rv != EC_SUCCESS) + return rv; + } + /* fall through */ +print_sn_data: +#endif + case 1: + rv = read_sn_data(&sn); + if (rv == EC_SUCCESS) + CPRINTF("Version: %02x\n" + "RMA: %02x\n" + "SN: %08x %08x %08x\n", + sn.version, sn.rma_status, + sn.sn_hash[0], sn.sn_hash[1], sn.sn_hash[2]); + + break; + default: + rv = EC_ERROR_PARAM_COUNT; + } + + return rv; +} +DECLARE_SAFE_CONSOLE_COMMAND(sn, + command_sn, "" +#ifdef CR50_DEV + "[(sn0 sn1 sn2) | (rmainc n)]" +#endif + , "Get" +#ifdef CR50_DEV + "/Set" +#endif + " Serial Number Data"); |