mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-12-03 18:10:13 +00:00
1e94b46f73
This old patch was marked as deferred. Bring it back to life, to continue towards the removal of common.h Move this out of the common header and include it only where needed. Signed-off-by: Simon Glass <sjg@chromium.org>
435 lines
9.7 KiB
C
435 lines
9.7 KiB
C
// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
|
|
/*
|
|
* Copyright (C) 2019, STMicroelectronics - All Rights Reserved
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <command.h>
|
|
#include <console.h>
|
|
#include <log.h>
|
|
#include <misc.h>
|
|
#include <asm/arch/bsec.h>
|
|
#include <dm/device.h>
|
|
#include <dm/uclass.h>
|
|
#include <linux/printk.h>
|
|
|
|
/*
|
|
* Closed device: OTP0
|
|
* STM32MP15x: bit 6 of OPT0
|
|
* STM32MP13x: 0b111111 = 0x3F for OTP_SECURED closed device
|
|
*/
|
|
#define STM32_OTP_CLOSE_ID 0
|
|
#define STM32_OTP_STM32MP13x_CLOSE_MASK 0x3F
|
|
#define STM32_OTP_STM32MP15x_CLOSE_MASK BIT(6)
|
|
|
|
/* PKH is the first element of the key list */
|
|
#define STM32KEY_PKH 0
|
|
|
|
struct stm32key {
|
|
char *name;
|
|
char *desc;
|
|
u8 start;
|
|
u8 size;
|
|
};
|
|
|
|
const struct stm32key stm32mp13_list[] = {
|
|
[STM32KEY_PKH] = {
|
|
.name = "PKHTH",
|
|
.desc = "Hash of the 8 ECC Public Keys Hashes Table (ECDSA is the authentication algorithm)",
|
|
.start = 24,
|
|
.size = 8,
|
|
},
|
|
{
|
|
.name = "EDMK",
|
|
.desc = "Encryption/Decryption Master Key",
|
|
.start = 92,
|
|
.size = 4,
|
|
}
|
|
};
|
|
|
|
const struct stm32key stm32mp15_list[] = {
|
|
[STM32KEY_PKH] = {
|
|
.name = "PKH",
|
|
.desc = "Hash of the ECC Public Key (ECDSA is the authentication algorithm)",
|
|
.start = 24,
|
|
.size = 8,
|
|
}
|
|
};
|
|
|
|
/* index of current selected key in stm32key list, 0 = PKH by default */
|
|
static u8 stm32key_index;
|
|
|
|
static u8 get_key_nb(void)
|
|
{
|
|
if (IS_ENABLED(CONFIG_STM32MP13x))
|
|
return ARRAY_SIZE(stm32mp13_list);
|
|
|
|
if (IS_ENABLED(CONFIG_STM32MP15x))
|
|
return ARRAY_SIZE(stm32mp15_list);
|
|
}
|
|
|
|
static const struct stm32key *get_key(u8 index)
|
|
{
|
|
if (IS_ENABLED(CONFIG_STM32MP13x))
|
|
return &stm32mp13_list[index];
|
|
|
|
if (IS_ENABLED(CONFIG_STM32MP15x))
|
|
return &stm32mp15_list[index];
|
|
}
|
|
|
|
static u32 get_otp_close_mask(void)
|
|
{
|
|
if (IS_ENABLED(CONFIG_STM32MP13x))
|
|
return STM32_OTP_STM32MP13x_CLOSE_MASK;
|
|
|
|
if (IS_ENABLED(CONFIG_STM32MP15x))
|
|
return STM32_OTP_STM32MP15x_CLOSE_MASK;
|
|
}
|
|
|
|
static int get_misc_dev(struct udevice **dev)
|
|
{
|
|
int ret;
|
|
|
|
ret = uclass_get_device_by_driver(UCLASS_MISC, DM_DRIVER_GET(stm32mp_bsec), dev);
|
|
if (ret)
|
|
log_err("Can't find stm32mp_bsec driver\n");
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void read_key_value(const struct stm32key *key, u32 addr)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < key->size; i++) {
|
|
printf("%s OTP %i: [%08x] %08x\n", key->name, key->start + i,
|
|
addr, __be32_to_cpu(*(u32 *)addr));
|
|
addr += 4;
|
|
}
|
|
}
|
|
|
|
static int read_key_otp(struct udevice *dev, const struct stm32key *key, bool print, bool *locked)
|
|
{
|
|
int i, word, ret;
|
|
int nb_invalid = 0, nb_zero = 0, nb_lock = 0, nb_lock_err = 0;
|
|
u32 val, lock;
|
|
bool status;
|
|
|
|
for (i = 0, word = key->start; i < key->size; i++, word++) {
|
|
ret = misc_read(dev, STM32_BSEC_OTP(word), &val, 4);
|
|
if (ret != 4)
|
|
val = ~0x0;
|
|
ret = misc_read(dev, STM32_BSEC_LOCK(word), &lock, 4);
|
|
if (ret != 4)
|
|
lock = BSEC_LOCK_ERROR;
|
|
if (print)
|
|
printf("%s OTP %i: %08x lock : %08x\n", key->name, word, val, lock);
|
|
if (val == ~0x0)
|
|
nb_invalid++;
|
|
else if (val == 0x0)
|
|
nb_zero++;
|
|
if (lock & BSEC_LOCK_PERM)
|
|
nb_lock++;
|
|
if (lock & BSEC_LOCK_ERROR)
|
|
nb_lock_err++;
|
|
}
|
|
|
|
status = nb_lock_err || (nb_lock == key->size);
|
|
if (locked)
|
|
*locked = status;
|
|
if (nb_lock_err && print)
|
|
printf("%s lock is invalid!\n", key->name);
|
|
else if (!status && print)
|
|
printf("%s is not locked!\n", key->name);
|
|
|
|
if (nb_invalid == key->size) {
|
|
if (print)
|
|
printf("%s is invalid!\n", key->name);
|
|
return -EINVAL;
|
|
}
|
|
if (nb_zero == key->size) {
|
|
if (print)
|
|
printf("%s is free!\n", key->name);
|
|
return -ENOENT;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int read_close_status(struct udevice *dev, bool print, bool *closed)
|
|
{
|
|
int word, ret, result;
|
|
u32 val, lock, mask;
|
|
bool status;
|
|
|
|
result = 0;
|
|
word = STM32_OTP_CLOSE_ID;
|
|
ret = misc_read(dev, STM32_BSEC_OTP(word), &val, 4);
|
|
if (ret < 0)
|
|
result = ret;
|
|
if (ret != 4)
|
|
val = 0x0;
|
|
|
|
ret = misc_read(dev, STM32_BSEC_LOCK(word), &lock, 4);
|
|
if (ret < 0)
|
|
result = ret;
|
|
if (ret != 4)
|
|
lock = BSEC_LOCK_ERROR;
|
|
|
|
mask = get_otp_close_mask();
|
|
status = (val & mask) == mask;
|
|
if (closed)
|
|
*closed = status;
|
|
if (print)
|
|
printf("OTP %d: closed status: %d lock : %08x\n", word, status, lock);
|
|
|
|
return result;
|
|
}
|
|
|
|
static int fuse_key_value(struct udevice *dev, const struct stm32key *key, u32 addr, bool print)
|
|
{
|
|
u32 word, val;
|
|
int i, ret;
|
|
|
|
for (i = 0, word = key->start; i < key->size; i++, word++, addr += 4) {
|
|
val = __be32_to_cpu(*(u32 *)addr);
|
|
if (print)
|
|
printf("Fuse %s OTP %i : %08x\n", key->name, word, val);
|
|
|
|
ret = misc_write(dev, STM32_BSEC_OTP(word), &val, 4);
|
|
if (ret != 4) {
|
|
log_err("Fuse %s OTP %i failed\n", key->name, word);
|
|
return ret;
|
|
}
|
|
/* on success, lock the OTP for the key */
|
|
val = BSEC_LOCK_PERM;
|
|
ret = misc_write(dev, STM32_BSEC_LOCK(word), &val, 4);
|
|
if (ret != 4) {
|
|
log_err("Lock %s OTP %i failed\n", key->name, word);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int confirm_prog(void)
|
|
{
|
|
puts("Warning: Programming fuses is an irreversible operation!\n"
|
|
" This may brick your system.\n"
|
|
" Use this command only if you are sure of what you are doing!\n"
|
|
"\nReally perform this fuse programming? <y/N>\n");
|
|
|
|
if (confirm_yesno())
|
|
return 1;
|
|
|
|
puts("Fuse programming aborted\n");
|
|
return 0;
|
|
}
|
|
|
|
static void display_key_info(const struct stm32key *key)
|
|
{
|
|
printf("%s : %s\n", key->name, key->desc);
|
|
printf("\tOTP%d..%d\n", key->start, key->start + key->size);
|
|
}
|
|
|
|
static int do_stm32key_list(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < get_key_nb(); i++)
|
|
display_key_info(get_key(i));
|
|
|
|
return CMD_RET_SUCCESS;
|
|
}
|
|
|
|
static int do_stm32key_select(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
|
|
{
|
|
const struct stm32key *key;
|
|
int i;
|
|
|
|
if (argc == 1) {
|
|
printf("Selected key:\n");
|
|
key = get_key(stm32key_index);
|
|
display_key_info(key);
|
|
return CMD_RET_SUCCESS;
|
|
}
|
|
|
|
for (i = 0; i < get_key_nb(); i++) {
|
|
key = get_key(i);
|
|
if (!strcmp(key->name, argv[1])) {
|
|
printf("%s selected\n", key->name);
|
|
stm32key_index = i;
|
|
return CMD_RET_SUCCESS;
|
|
}
|
|
}
|
|
|
|
printf("Unknown key %s\n", argv[1]);
|
|
|
|
return CMD_RET_FAILURE;
|
|
}
|
|
|
|
static int do_stm32key_read(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
|
|
{
|
|
const struct stm32key *key;
|
|
struct udevice *dev;
|
|
u32 addr;
|
|
int ret, i;
|
|
int result;
|
|
|
|
ret = get_misc_dev(&dev);
|
|
|
|
if (argc == 1) {
|
|
if (ret)
|
|
return CMD_RET_FAILURE;
|
|
key = get_key(stm32key_index);
|
|
ret = read_key_otp(dev, key, true, NULL);
|
|
if (ret != -ENOENT)
|
|
return CMD_RET_FAILURE;
|
|
return CMD_RET_SUCCESS;
|
|
}
|
|
|
|
if (!strcmp("-a", argv[1])) {
|
|
if (ret)
|
|
return CMD_RET_FAILURE;
|
|
result = CMD_RET_SUCCESS;
|
|
for (i = 0; i < get_key_nb(); i++) {
|
|
key = get_key(i);
|
|
ret = read_key_otp(dev, key, true, NULL);
|
|
if (ret != -ENOENT)
|
|
result = CMD_RET_FAILURE;
|
|
}
|
|
ret = read_close_status(dev, true, NULL);
|
|
if (ret)
|
|
result = CMD_RET_FAILURE;
|
|
|
|
return result;
|
|
}
|
|
|
|
addr = hextoul(argv[1], NULL);
|
|
if (!addr)
|
|
return CMD_RET_USAGE;
|
|
|
|
key = get_key(stm32key_index);
|
|
printf("Read %s at 0x%08x\n", key->name, addr);
|
|
read_key_value(key, addr);
|
|
|
|
return CMD_RET_SUCCESS;
|
|
}
|
|
|
|
static int do_stm32key_fuse(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
|
|
{
|
|
const struct stm32key *key = get_key(stm32key_index);
|
|
struct udevice *dev;
|
|
u32 addr;
|
|
int ret;
|
|
bool yes = false, lock;
|
|
|
|
if (argc < 2)
|
|
return CMD_RET_USAGE;
|
|
|
|
if (argc == 3) {
|
|
if (strcmp(argv[1], "-y"))
|
|
return CMD_RET_USAGE;
|
|
yes = true;
|
|
}
|
|
|
|
addr = hextoul(argv[argc - 1], NULL);
|
|
if (!addr)
|
|
return CMD_RET_USAGE;
|
|
|
|
ret = get_misc_dev(&dev);
|
|
if (ret)
|
|
return CMD_RET_FAILURE;
|
|
|
|
if (read_key_otp(dev, key, !yes, &lock) != -ENOENT) {
|
|
printf("Error: can't fuse again the OTP\n");
|
|
return CMD_RET_FAILURE;
|
|
}
|
|
if (lock) {
|
|
printf("Error: %s is locked\n", key->name);
|
|
return CMD_RET_FAILURE;
|
|
}
|
|
|
|
if (!yes) {
|
|
printf("Writing %s with\n", key->name);
|
|
read_key_value(key, addr);
|
|
}
|
|
|
|
if (!yes && !confirm_prog())
|
|
return CMD_RET_FAILURE;
|
|
|
|
if (fuse_key_value(dev, key, addr, !yes))
|
|
return CMD_RET_FAILURE;
|
|
|
|
printf("%s updated !\n", key->name);
|
|
|
|
return CMD_RET_SUCCESS;
|
|
}
|
|
|
|
static int do_stm32key_close(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
|
|
{
|
|
const struct stm32key *key;
|
|
bool yes, lock, closed;
|
|
struct udevice *dev;
|
|
u32 val;
|
|
int ret;
|
|
|
|
yes = false;
|
|
if (argc == 2) {
|
|
if (strcmp(argv[1], "-y"))
|
|
return CMD_RET_USAGE;
|
|
yes = true;
|
|
}
|
|
|
|
ret = get_misc_dev(&dev);
|
|
if (ret)
|
|
return CMD_RET_FAILURE;
|
|
|
|
if (read_close_status(dev, !yes, &closed))
|
|
return CMD_RET_FAILURE;
|
|
|
|
if (closed) {
|
|
printf("Error: already closed!\n");
|
|
return CMD_RET_FAILURE;
|
|
}
|
|
|
|
/* check PKH status before to close */
|
|
key = get_key(STM32KEY_PKH);
|
|
ret = read_key_otp(dev, key, !yes, &lock);
|
|
if (ret) {
|
|
if (ret == -ENOENT)
|
|
printf("Error: %s not programmed!\n", key->name);
|
|
return CMD_RET_FAILURE;
|
|
}
|
|
if (!lock)
|
|
printf("Warning: %s not locked!\n", key->name);
|
|
|
|
if (!yes && !confirm_prog())
|
|
return CMD_RET_FAILURE;
|
|
|
|
val = get_otp_close_mask();
|
|
ret = misc_write(dev, STM32_BSEC_OTP(STM32_OTP_CLOSE_ID), &val, 4);
|
|
if (ret != 4) {
|
|
printf("Error: can't update OTP %d\n", STM32_OTP_CLOSE_ID);
|
|
return CMD_RET_FAILURE;
|
|
}
|
|
|
|
printf("Device is closed !\n");
|
|
|
|
return CMD_RET_SUCCESS;
|
|
}
|
|
|
|
static char stm32key_help_text[] =
|
|
"list : list the supported key with description\n"
|
|
"stm32key select [<key>] : Select the key identified by <key> or display the key used for read/fuse command\n"
|
|
"stm32key read [<addr> | -a ] : Read the curent key at <addr> or current / all (-a) key in OTP\n"
|
|
"stm32key fuse [-y] <addr> : Fuse the current key at addr in OTP\n"
|
|
"stm32key close [-y] : Close the device\n";
|
|
|
|
U_BOOT_CMD_WITH_SUBCMDS(stm32key, "Manage key on STM32", stm32key_help_text,
|
|
U_BOOT_SUBCMD_MKENT(list, 1, 0, do_stm32key_list),
|
|
U_BOOT_SUBCMD_MKENT(select, 2, 0, do_stm32key_select),
|
|
U_BOOT_SUBCMD_MKENT(read, 2, 0, do_stm32key_read),
|
|
U_BOOT_SUBCMD_MKENT(fuse, 3, 0, do_stm32key_fuse),
|
|
U_BOOT_SUBCMD_MKENT(close, 2, 0, do_stm32key_close));
|