blob: 5cb46ab9fdbf96a99f0866214a237eb51e083557 [file] [log] [blame]
/*
* otp_key - manages OTP codes for IBR secure boot
*
* (C) Copyright 2013 Google Inc.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <c2k_otp.h>
#include <command.h>
#include <common.h>
#include <errno.h>
#include <fcntl.h>
#include <fs.h>
#include <malloc.h>
#include <board_id.h>
#define OTP_KEY_SIZE_BYTES 256
#define OTP_KEY_SIZE_BITS (OTP_KEY_SIZE_BYTES * 8)
#define OTP_HEADER_OFFSET_BITS 32
/*
* Converts an RSA public key read in from a file to a bit array suitable
* for otp_write.
*
* Note that this inverts each byte in the key, which is what we think
* the hardware expects, based on a Mindspeed sample. Yet to be tested.
*/
static void _convert_to_bit_array(uint8_t *key, unsigned int key_size,
uint8_t *bit_array) {
int i, j, offset;
uint8_t key_byte;
for (i = 0, offset = 0; i < key_size; ++i) {
key_byte = key[i];
for (j = 0; j < 8; ++j, ++offset) {
bit_array[offset] = (key_byte >> j) & 1;
}
}
}
/*
* Dumps a key to stdout, for debugging.
*/
static void _dump_key(char *key_name, uint8_t *key, unsigned int key_size) {
int i;
printf("%s:\n", key_name);
for (i = 0; i < key_size; ++i) {
if ((i % 16) == 0) {
printf("\n");
}
printf(" %.2x", key[i]);
}
printf("\n");
}
static int do_write_key(struct command *cmdtp, int argc, char *argv[])
{
int fd, rv = 0;
uint8_t *provided_key, *provided_key_bit_arr, *verify_buf;
long offset = 0;
if (argc < 2)
return COMMAND_ERROR_USAGE;
provided_key = xmalloc(OTP_KEY_SIZE_BYTES);
provided_key_bit_arr = xmalloc(OTP_KEY_SIZE_BITS);
verify_buf = xmalloc(OTP_KEY_SIZE_BYTES);
fd = open(argv[1], O_RDONLY);
if (fd < 0) {
perror(argv[1]);
rv = 1;
goto end;
}
if (read(fd, provided_key, OTP_KEY_SIZE_BYTES) != OTP_KEY_SIZE_BYTES) {
perror("file read");
close(fd);
rv = 1;
goto end;
}
close(fd);
if (argc > 2) {
offset = simple_strtol(argv[2], NULL, 10);
if (offset <= 0 || (offset & 0xFFFFFFFF) != offset) {
printf("Error: invalid offset %s\n", argv[2]);
rv = 1;
goto end;
}
printf("For debug purposes, using an offset of %ld bytes\n", offset);
offset *= 8; // Convert from bytes to bits;
} else {
offset = OTP_HEADER_OFFSET_BITS;
}
/* Write the key to the OTP. */
_convert_to_bit_array(provided_key, OTP_KEY_SIZE_BYTES, provided_key_bit_arr);
otp_write(offset, provided_key_bit_arr, OTP_KEY_SIZE_BITS);
/* Verify the write. */
if (otp_read(offset, verify_buf, OTP_KEY_SIZE_BYTES) != 0) {
printf("Error: otp_read failed, unable to verify write\n");
rv = 1;
goto end;
}
if (memcmp(provided_key, verify_buf, OTP_KEY_SIZE_BYTES) != 0) {
printf("Key writing failed:\n\n");
_dump_key("Provided key:", provided_key, OTP_KEY_SIZE_BYTES);
printf("\n");
_dump_key("Written key:", verify_buf, OTP_KEY_SIZE_BYTES);
rv = 1;
goto end;
}
printf("Key written and verified.\n");
end:
free(provided_key);
free(provided_key_bit_arr);
free(verify_buf);
return rv;
}
static const __maybe_unused char cmd_write_key_help[] =
"Usage: write_key <key_file> [offset]\n"
"Writes the given RSA public key to the OTP.\n"
"An optional offset can be used for debugging.\n";
BAREBOX_CMD_START(write_key)
.cmd = do_write_key,
.usage = "Write key to OTP",
BAREBOX_CMD_HELP(cmd_write_key_help)
BAREBOX_CMD_END
static int do_verify_key(struct command *cmdtp, int argc, char *argv[])
{
int fd, rv = 0;
uint8_t *provided_key, *otp_key;
long offset = 0;
if (argc < 2)
return COMMAND_ERROR_USAGE;
provided_key = xmalloc(OTP_KEY_SIZE_BYTES);
otp_key = xmalloc(OTP_KEY_SIZE_BYTES);
fd = open(argv[1], O_RDONLY);
if (fd < 0) {
perror(argv[1]);
rv = 1;
goto end;
}
if (read(fd, provided_key, OTP_KEY_SIZE_BYTES) != OTP_KEY_SIZE_BYTES) {
perror("file read");
close(fd);
rv = 1;
goto end;
}
close(fd);
if (argc > 2) {
offset = simple_strtol(argv[2], NULL, 10);
if (offset <= 0 || (offset & 0xFFFFFFFF) != offset) {
printf("Error: invalid offset %s\n", argv[2]);
rv = 1;
goto end;
}
printf("For debug purposes, using an offset of %ld bytes\n", offset);
offset *= 8; // Convert from bytes to bits;
} else {
offset = OTP_HEADER_OFFSET_BITS;
}
if (otp_read(offset, otp_key, OTP_KEY_SIZE_BYTES) != 0) {
printf("Error: otp_read failed!\n");
rv = 1;
goto end;
}
if (memcmp(provided_key, otp_key, OTP_KEY_SIZE_BYTES) != 0) {
printf("OTP key mismatch!\n\n");
_dump_key("provided_key", provided_key, OTP_KEY_SIZE_BYTES);
printf("\n");
_dump_key("otp_key", otp_key, OTP_KEY_SIZE_BYTES);
rv = 1;
goto end;
}
printf("Verified: OTP key matches provided key.\n");
end:
free(provided_key);
free(otp_key);
return rv;
}
static const __maybe_unused char cmd_verify_key_help[] =
"Usage: verify_key <key_file> [offset]\n"
"Verifies that the key in key_file matches the one written to the OTP.\n"
"An optional offset can be used for debugging\n";
BAREBOX_CMD_START(verify_key)
.cmd = do_verify_key,
.usage = "verify key written to OTP",
BAREBOX_CMD_HELP(cmd_verify_key_help)
BAREBOX_CMD_END
static int do_lock_otp(struct command *cmdtp, int argc, char *argv[])
{
if (otp_lock() != 0) {
printf("Error: Locking the OTP failed. OTP MAY or MAY NOT be locked!\n");
return -1;
}
printf("OTP is now write-locked\n");
return 0;
}
static const __maybe_unused char cmd_lock_otp_help[] =
"Usage: lock_otp\n"
"Locks the OTP against further writes.\n"
"WARNING: This cannot be undone!\n";
BAREBOX_CMD_START(lock_otp)
.cmd = do_lock_otp,
.usage = "lock otp against future writes",
BAREBOX_CMD_HELP(cmd_lock_otp_help)
BAREBOX_CMD_END
static int do_enable_auth(struct command *cmdtp, int argc, char *argv[])
{
uint8_t bytes[2], one = 1;
/* Disable JTAG */
otp_write(0, &one, 1);
/* Disable debug mode */
otp_write(8, &one, 1);
/* Enable authentication */
otp_write(9, &one, 1);
/* Set the key size. This is actually a combination of writing 0 to offset 10
* and 1 to offset 11, but there's no need to write 0s. */
otp_write(11, &one, 1);
/* The other item to be set is the package type, to offset 12, but for us this
* is 0, so there's no need to write it. */
/* Now verify that the correct bytes were written */
if (otp_read(0, bytes, 2) != 0) {
printf("Error: otp_read failed, unable to verify authentication enabled\n");
return 1;
}
if (bytes[0] != 0x1 || bytes[1] != 0xB) {
printf("Error: Verification failed!\n");
printf("Byte 0 was %.2x (expected %.2x)\n", bytes[0], 0x1);
printf("Byte 1 was %.2x (expected %.2x)\n", bytes[1], 0xB);
return 1;
}
printf("Verification suceeded. Authentication enabled.\n");
return 0;
}
static const __maybe_unused char cmd_enable_auth_help[] =
"Usage: enable_auth\n"
"Flips the authentication bit in the OTP.\n"
"WARNING: After running this, device will only boot securely.\n";
BAREBOX_CMD_START(enable_auth)
.cmd = do_enable_auth,
.usage = "flip auth bit in OTP",
BAREBOX_CMD_HELP(cmd_enable_auth_help)
BAREBOX_CMD_END
static int do_enable_auth_jtag_on(struct command *cmdtp, int argc, char *argv[])
{
uint8_t bytes[2], one = 1;
/* Disable debug mode */
otp_write(8, &one, 1);
/* Enable authentication */
otp_write(9, &one, 1);
/* Set the key size. This is actually a combination of writing 0 to offset 10
* and 1 to offset 11, but there's no need to write 0s. */
otp_write(11, &one, 1);
/* The other item to be set is the package type, to offset 12, but for us this
* is 0, so there's no need to write it. */
/* Now verify that the correct bytes were written */
if (otp_read(0, bytes, 2) != 0) {
printf("Error: otp_read failed, unable to verify authentication enabled\n");
return 1;
}
if ((bytes[1] & 0xB) != 0xB) {
printf("Error: Verification failed!\n");
printf("Byte 0 was %.2x\n", bytes[0]);
printf("Byte 1 was %.2x\n", bytes[1]);
return 1;
}
printf("Verification suceeded. Authentication enabled.\n");
return 0;
}
static const __maybe_unused char cmd_enable_auth_jtag_on_help[] =
"Usage: enable_auth_jtag_on\n"
"Flips the authentication bit in the OTP but leaves JTAG on.\n"
"WARNING: After running this, device will only boot securely.\n";
BAREBOX_CMD_START(enable_auth_jtag_on)
.cmd = do_enable_auth_jtag_on,
.usage = "set auth bit in OTP, leave JTAG enabled",
BAREBOX_CMD_HELP(cmd_enable_auth_jtag_on_help)
BAREBOX_CMD_END
static int do_print_otp_config(struct command *cmdtp, int argc, char *argv[])
{
uint8_t bytes[2];
/* Now verify that the correct bytes were written */
if (otp_read(0, bytes, 2) != 0) {
printf("Error: otp_read failed\n");
return 1;
}
printf("0x%.2x 0x%.2x\n", bytes[0], bytes[1]);
return 0;
}
static const __maybe_unused char cmd_print_otp_config_help[] =
"Usage: print_otp_config\n";
BAREBOX_CMD_START(print_otp_config)
.cmd = do_print_otp_config,
.usage = "print OTP config bytes",
BAREBOX_CMD_HELP(cmd_print_otp_config_help)
BAREBOX_CMD_END
static int do_set_otp_board_id(struct command *cmdtp, int argc,
char *argv[]) {
int board_id;
uint32_t board_id_otp;
uint8_t bit_array[32];
if (argc != 2)
return COMMAND_ERROR_USAGE;
board_id = simple_strtoul(argv[1], NULL, 0);
/* Writing the board ID is irreversible, verify the provided board ID
matches the GPIO configuration */
if (board_id != get_board_id_gpio()) {
printf("Given board ID does not match GPIO board ID: %d\n",
get_board_id_gpio());
return 1;
}
if (board_id == OPTIMUS_BOARD_ID) {
board_id_otp = OPTIMUS_BOARD_ID_OTP;
} else if (board_id == SIDESWIPE_BOARD_ID) {
board_id_otp = SIDESWIPE_BOARD_ID_OTP;
} else if (board_id == SPACECAST_BOARD_ID) {
board_id_otp = SPACECAST_BOARD_ID_OTP;
} else if (board_id == OPTIMUS_PRIME_BOARD_ID) {
board_id_otp = OPTIMUS_PRIME_BOARD_ID_OTP;
} else if (board_id == SIDESWIPE_PRIME_BOARD_ID) {
board_id_otp = SIDESWIPE_PRIME_BOARD_ID_OTP;
} else {
printf("Unsupported board: %d\n", board_id);
return 1;
}
/* Write the key id to the OTP. */
_convert_to_bit_array((uint8_t *)&board_id_otp, 4, bit_array);
otp_write(OTP_OFFSET_BOARD_ID, bit_array, 32);
return 0;
}
static const __maybe_unused char cmd_set_otp_board_id_help[] =
"Usage: set_otp_board_id <id>\n"
"Set the board id in the OTP.\n"
"WARNING: This cannot be undone!\n";
BAREBOX_CMD_START(set_board_id)
.cmd = do_set_otp_board_id,
.usage = "set the device id in the OTP",
BAREBOX_CMD_HELP(cmd_set_otp_board_id_help)
BAREBOX_CMD_END