/* 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 "system.h" #include "util.h" #define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ## args) #define CPRINTF(format, args...) cprintf(CC_SYSTEM, format, ## args) #define BLANK_FIELD 0xffffffff /** * 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()); } int board_id_type_is_blank(const struct board_id *id) { return (id->type & id->type_inv) == BLANK_FIELD; } static int board_id_flags_are_blank(const struct board_id *id) { return id->flags == BLANK_FIELD; } int board_id_is_blank(const struct board_id *id) { return board_id_type_is_blank(id) && board_id_flags_are_blank(id); } int board_id_is_erased(void) { struct board_id id; /* * If we can't read the board id for some reason, return 0 just to be * safe */ if (read_board_id(&id) != EC_SUCCESS) { CPRINTS("%s: BID read error", __func__); return 0; } if (board_id_is_blank(&id)) { CPRINTS("BID erased"); return 1; } return 0; } 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 (board_id_is_blank(id)) 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; /* * 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); /* * Masked bits in header Board ID type must match type and inverse from * flash. */ if (!mismatch && !board_id_type_is_blank(id)) { mismatch = header_board_id_type ^ id->type; mismatch |= header_board_id_type ^ ~id->type_inv; mismatch &= header_board_id_mask; } return mismatch; } 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; for (i = 0; i < sizeof(*id); i += sizeof(uint32_t)) { int rv; rv = flash_physical_info_read_word (INFO_BOARD_ID_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; } uint32_t board_id_mismatch(const struct SignedHeader *sh) { struct board_id id; if (!sh) /* Get header of the currently running image. */ sh = get_current_image_header(); /* Get Board ID from INFO1. */ if (read_board_id(&id) != EC_SUCCESS) { /* * On failure, set id fields to 0. This will only match an * unrestricted image header (board_id_mask=board_id_flags=0), * which would run on any Board ID. * * Don't return error, because that would prevent all images * from running. */ id.type = id.type_inv = id.flags = 0; } return check_board_id_vs_header(&id, sh); } /** * 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(struct board_id *id) { struct board_id id_test; uint32_t rv; /* 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 (!board_id_is_blank(&id_test)) { if (!board_id_type_is_blank(&id_test)) { CPRINTS("%s: Board ID already programmed", __func__); return EC_ERROR_ACCESS_DENIED; } CPRINTS("%s: using old flags.", __func__); id->flags = id_test.flags; } /* * 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; } flash_info_write_enable(); /* Write Board ID */ rv = flash_info_physical_write(INFO_BOARD_ID_OFFSET + offsetof(struct info1_board_space, bid), sizeof(*id), (const char *)id); if (rv != EC_SUCCESS) CPRINTS("%s: write failed", __func__); 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); if (id.type == BLANK_FIELD) id.type_inv = BLANK_FIELD; else 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:%08x, flags %08x\n", id.type, id.type_inv, id.flags); if (board_id_is_blank(&id)) return rv; /* The space is not initialized. */ if (!board_id_type_is_blank(&id) && 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); } #endif return rv; } DECLARE_SAFE_CONSOLE_COMMAND(bid, command_board_id, #ifdef CR50_DEV "[bid flags]", #else NULL, #endif "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);