blob: 481a6dbfb7e39954373e4a0e724738dc88ac8a24 [file] [log] [blame]
/* Copyright 2017 The ChromiumOS Authors
* 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,
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);