| /* |
| * 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 |