Files

606 lines
15 KiB
C

// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <config.h>
#include <common.h>
#include <command.h>
#include <malloc.h>
#include <hexdump.h>
#include <mmc.h>
#include <asm/amlogic/arch/efuse.h>
#include <asm/amlogic/arch/bl31_apis.h>
#include <amlogic/aml_efuse.h>
#include <u-boot/crc.h>
#ifdef CONFIG_AML_PROVISION_UBOOT
#define AML_MAX_KEY_NAME 32
#define AML_MAX_EFUSE_KEYS 8
#define AML_MAX_EFUSE_KEY_BLOCKS (1)
#define MMC_BLOCK_SIZE (512)
#if CONFIG_AML_PROVISION_UBOOT_NUM == 1 ||CONFIG_AML_PROVISION_UBOOT_NUM == 2
#define AML_BOOT_BOOT_NUM CONFIG_AML_PROVISION_UBOOT_NUM
#else
#define AML_BOOT_BOOT_NUM 2
#error "CONFIG_AML_PROVISION_UBOOT_NUM is not correct, use 2 as default"
#endif
#if CONFIG_AML_PROVISION_UBOOT_OFFSET < 5 || CONFIG_AML_PROVISION_UBOOT_OFFSET > 504
#error "CONFIG_AML_PROVISION_UBOOT_OFFSET is out of range, just default:300"
#define AML_CUSTOM_OFFSET 300
#else
#define AML_CUSTOM_OFFSET CONFIG_AML_PROVISION_UBOOT_OFFSET
#endif
#define AML_EFUSE_KEY_BLOCK_START (AML_MAX_EFUSE_KEYS*AML_MAX_EFUSE_KEY_BLOCKS + AML_CUSTOM_OFFSET)
#define AML_KEY_HASH_NORMAL_MAGIC (0x484e4452) /*HNDR*/
#define AML_KEY_HASH_DFU_MAGIC (0x48444452) /*HDDR*/
#define AML_KEY_DGPK1_MAGIC (0x44475031) /*DGP1*/
#define AML_KEY_DVGK_MAGIC (0x4456474b) /*DVGK*/
#define AML_KEY_DVUK_MAGIC (0x4456554b) /*DVUK*/
#define AML_KEY_AUDIO_VENDOR_ID_MAGIC (0x41564944) /*AVID*/
static const struct {
const char *name;
u64 offset;
unsigned int magic;
} aml_key_map[AML_MAX_EFUSE_KEYS] = {
[0] = {
.name = "HASH_NORMAL_DEVICE_ROOTCERT",
.offset = (AML_EFUSE_KEY_BLOCK_START - 0*AML_MAX_EFUSE_KEY_BLOCKS) * MMC_BLOCK_SIZE,
.magic = AML_KEY_HASH_NORMAL_MAGIC
},
[1] = {
.name = "HASH_DFU_DEVICE_ROOTCERT",
.offset = (AML_EFUSE_KEY_BLOCK_START - 1*AML_MAX_EFUSE_KEY_BLOCKS) * MMC_BLOCK_SIZE,
.magic = AML_KEY_HASH_DFU_MAGIC
},
[2] = {
.name = "DGPK1",
.offset = (AML_EFUSE_KEY_BLOCK_START - 2*AML_MAX_EFUSE_KEY_BLOCKS) * MMC_BLOCK_SIZE,
.magic = AML_KEY_DGPK1_MAGIC
},
[3] = {
.name = "DVGK",
.offset = (AML_EFUSE_KEY_BLOCK_START - 3*AML_MAX_EFUSE_KEY_BLOCKS) * MMC_BLOCK_SIZE,
.magic = AML_KEY_DVGK_MAGIC
},
[4] = {
.name = "DVUK",
.offset = (AML_EFUSE_KEY_BLOCK_START - 4*AML_MAX_EFUSE_KEY_BLOCKS) * MMC_BLOCK_SIZE,
.magic = AML_KEY_DVUK_MAGIC
},
[5] = {
.name = "AUDIO_VENDOR_ID",
.offset = (AML_EFUSE_KEY_BLOCK_START - 5*AML_MAX_EFUSE_KEY_BLOCKS) * MMC_BLOCK_SIZE,
.magic = AML_KEY_AUDIO_VENDOR_ID_MAGIC
},
[6] = {
.name = "reserved",
.offset = (AML_EFUSE_KEY_BLOCK_START - 6*AML_MAX_EFUSE_KEY_BLOCKS) * MMC_BLOCK_SIZE,
.magic = 0
},
[7] = {
.name = "reserved",
.offset = (AML_EFUSE_KEY_BLOCK_START - 7*AML_MAX_EFUSE_KEY_BLOCKS) * MMC_BLOCK_SIZE,
.magic = 0
},
};
#define AML_MAX_FEAT_ENABLE 6
char aml_enable_efuse[AML_MAX_FEAT_ENABLE][64] = {
"FEAT_ENABLE_DEVICE_ROOT_PUBRSA_PROT",
"FEAT_ENABLE_DEVICE_LVL1_PUBRSA_PROT",
"FEAT_ENABLE_DEVICE_LVLX_PUBRSA_PROT",
"FEAT_ENABLE_DEVICE_VENDOR_SIG",
"FEAT_ENABLE_DEVICE_PROT",
"FEAT_ENABLE_DEVICE_SCS_SIG"
};
#define AML_KEY_FLAG_ENCRYPTED 0x1
#define AML_KEY_FLAG_BURNED 0x2
typedef struct __aml_prov_key {
unsigned int magic;
unsigned short flags;
unsigned short len;
unsigned int crc32;
unsigned char data[0];
} aml_prov_key_t;
static unsigned char s_key_buf[MMC_BLOCK_SIZE * AML_MAX_EFUSE_KEY_BLOCKS];
static int aml_find_key_map(const char *name)
{
int i = 0;
if (NULL == name)
return -1;
for (i = 0; i < AML_MAX_EFUSE_KEYS; i++)
{
if (0 == strncmp(name, aml_key_map[i].name, AML_MAX_KEY_NAME))
return i;
}
return -1;
}
static int aml_read_key_from_boot(unsigned char *pbuf, u64 offset)
{
struct mmc *mmc;
int nread = 0;
u64 block_offset = 0;
printf("%s read data from boot%d\n", __func__, (AML_BOOT_BOOT_NUM - 1));
if (!pbuf) {
printf("Null pbuf used in %s\n", __func__);
return -1;
}
mmc = find_mmc_device(1);
if (!mmc) {
printf("no mmc devices available\n");
return -1;
}
/* switch to boot partition */
if (mmc_switch_part(mmc, AML_BOOT_BOOT_NUM) != 0)
{
printf("ERROR: couldn't switch to boot partition\n");
return -1;
}
block_offset = mmc->capacity - offset;
printf("%s block_offset=%lx, capacity=%lx\n", __func__, (long)block_offset, (long)mmc->capacity);
#ifndef CONFIG_BLK
nread = mmc->block_dev.block_read(AML_BOOT_BOOT_NUM,
block_offset / MMC_BLOCK_SIZE,
AML_MAX_EFUSE_KEY_BLOCKS, pbuf);
#else
nread = blk_dread(mmc_get_blk_desc(mmc),
block_offset / MMC_BLOCK_SIZE,
AML_MAX_EFUSE_KEY_BLOCKS, pbuf);
#endif
/* switch to user partition */
if (mmc_switch_part(mmc, 0) != 0)
{
printf("ERROR: couldn't switch to user partition\n");
return -1;
}
if (nread < 0) {
printf("ERROR: read failure, nread %d\n", nread);
return -1;
}
return 0;
}
static int aml_write_key_to_boot(const unsigned char *pbuf, u64 offset)
{
struct mmc *mmc;
int nwrite = 0;
u64 block_offset = 0;
printf("%s write data into boot%d\n", __func__, (AML_BOOT_BOOT_NUM - 1));
if (!pbuf) {
printf("Null pbuf used in %s\n", __func__);
return -1;
}
mmc = find_mmc_device(1);
if (!mmc) {
printf("no mmc devices available\n");
return -1;
}
/* switch to boot partition */
if (mmc_switch_part(mmc, AML_BOOT_BOOT_NUM) != 0)
{
printf("ERROR: couldn't switch to boot partition\n");
return -1;
}
block_offset = mmc->capacity - offset;
printf("%s block_offset=%lx, capacity=%lx\n", __func__, (long)block_offset, (long)mmc->capacity);
#ifndef CONFIG_BLK
nwrite = mmc->block_dev.block_write(AML_BOOT_BOOT_NUM,
block_offset / MMC_BLOCK_SIZE,
AML_MAX_EFUSE_KEY_BLOCKS, pbuf);
#else
nwrite = blk_dwrite(mmc_get_blk_desc(mmc),
block_offset/MMC_BLOCK_SIZE,
AML_MAX_EFUSE_KEY_BLOCKS, pbuf);
#endif
/* switch to user partition */
if (mmc_switch_part(mmc, 0) != 0)
{
printf("ERROR: couldn't switch to user partition\n");
return -1;
}
if (nwrite < 0) {
printf("ERROR: write failure, nwrite %d\n", nwrite);
return -1;
}
return 0;
}
static int aml_verify_key_data(aml_prov_key_t *key, unsigned int magic)
{
unsigned int data_crc = 0;
if (!key)
return -1;
printf("%s magic=0x%x, crc32=0x%x, len:%d, flags:%d\n", __func__, key->magic, key->crc32, key->len, key->flags);
if (key->magic != magic) {
printf("%s wrong magic=0x%x, should be 0x%x\n", __func__, key->magic, magic);
return -1;
}
if (key->len == 0 || key->crc32 == 0) {
printf("%s len=%d, crc32=0x%x\n", __func__, key->len, key->crc32);
return -1;
}
data_crc = crc32(0, key->data, key->len);
if (key->crc32 != data_crc) {
printf("%s crc=0x%x, data crc=0x%x\n", __func__, key->crc32, data_crc);
return -1;
}
printf("%s verify data successfully\n", __func__);
return 0;
}
static int aml_clear_key_data(int map_id)
{
int ret = 0;
aml_prov_key_t *key = NULL;
char *efuse_debug = env_get("provision_efuse_debug");
if (map_id < 0 || map_id > sizeof(aml_key_map)/sizeof(aml_key_map[0]))
return -1;
printf("%s clear data offset:%lld, name:%s\n", __func__, aml_key_map[map_id].offset, aml_key_map[map_id].name);
if (efuse_debug && strncmp(efuse_debug, "1", 1) == 0 ) {
printf("%s do not clear data in debug mode\n", __func__);
return 0;
}
memset(s_key_buf, 0, MMC_BLOCK_SIZE * AML_MAX_EFUSE_KEY_BLOCKS);
key = (aml_prov_key_t *)s_key_buf;
key->flags = AML_KEY_FLAG_BURNED;
key->len = 0;
key->crc32 = 0;
key->magic = aml_key_map[map_id].magic;
ret = aml_write_key_to_boot(s_key_buf, aml_key_map[map_id].offset);
return ret;
}
#define AML_CMD_BUF_LEN 256
char g_aml_cmd_buf[AML_CMD_BUF_LEN] = { 0 };
static void aml_execute_efuse_cmd(int run)
{
char *efuse_debug = env_get("provision_efuse_debug");
printf("efuse cmd:%s\n",g_aml_cmd_buf);
if (!efuse_debug || strncmp(efuse_debug, "1", 1) || run) {
run_command(g_aml_cmd_buf, 0);
printf("%s run efuse_obj cmd finished!\n", __func__);
} else {
printf("%s run efuse_obj cmd, not execute in debug mode!\n", __func__);
}
}
/*return 0 -non-existent, return 1 --exist*/
static int aml_get_efuse_obj(const char *name, int lock)
{
int val = 0;
memset(g_aml_cmd_buf, 0, sizeof(g_aml_cmd_buf));
if (!name)
return 0;
if (lock)
snprintf(g_aml_cmd_buf, AML_CMD_BUF_LEN, "%s %s\n", "efuse_obj get_lock", name);
else
snprintf(g_aml_cmd_buf, AML_CMD_BUF_LEN, "%s %s\n", "efuse_obj get", name);
aml_execute_efuse_cmd(1);
val = (int)efuse_field.data[0];
printf("%s efuse:%s,%d: %s\n", __func__, name, val, val ? "exist" : "non-existent");
return val;
}
static int aml_set_efuse_obj(const char *name, aml_prov_key_t *key)
{
char *hex_data_p = NULL;
char *hex_data = NULL;
int key_data_len = key->len * 2 + 1;
hex_data = hex_data_p = (char *)malloc(key_data_len);
if (!hex_data)
return -1;
memset(hex_data_p, 0, key_data_len);
hex_data_p = bin2hex(hex_data_p, key->data, key->len);
*hex_data_p = '\0';
printf("%s,hex data len:%lu\n", __func__, strlen(hex_data));
if (key->flags & AML_KEY_FLAG_ENCRYPTED) {
memset(g_aml_cmd_buf, 0, sizeof(g_aml_cmd_buf));
snprintf(g_aml_cmd_buf, AML_CMD_BUF_LEN, "efuse_obj set_enc %s %s", name, hex_data);
aml_execute_efuse_cmd(0);
memset(g_aml_cmd_buf, 0, sizeof(g_aml_cmd_buf));
snprintf(g_aml_cmd_buf, AML_CMD_BUF_LEN, "efuse_obj lock %s", name);
aml_execute_efuse_cmd(0);
} else {
memset(g_aml_cmd_buf, 0, sizeof(g_aml_cmd_buf));
snprintf(g_aml_cmd_buf, AML_CMD_BUF_LEN, "efuse_obj set %s %s", name, hex_data);
aml_execute_efuse_cmd(0);
memset(g_aml_cmd_buf, 0, sizeof(g_aml_cmd_buf));
snprintf(g_aml_cmd_buf, AML_CMD_BUF_LEN, "efuse_obj lock %s", name);
aml_execute_efuse_cmd(0);
}
free(hex_data);
return 0;
}
static int aml_enable_efuse_obj(const char *name)
{
int ret = 0;
ret = aml_get_efuse_obj(name, 0);
if (!ret) {
memset(g_aml_cmd_buf, 0, sizeof(g_aml_cmd_buf));
snprintf(g_aml_cmd_buf, AML_CMD_BUF_LEN, "efuse_obj set %s 01", name);
aml_execute_efuse_cmd(0);
return 0;
} else {
printf("%s %s is already enabled\n", __func__, name);
return 1;
}
}
/*just provision efuse which name is in support list, do not check if the key exists or not*/
int aml_provision_key(const char *name)
{
int ret = -1;
aml_prov_key_t *key = NULL;
int map_id = -1;
printf("%s provision start %s\n", __func__, name);
map_id = aml_find_key_map(name);
if (map_id < 0)
return ret;
memset(s_key_buf, 0, MMC_BLOCK_SIZE * AML_MAX_EFUSE_KEY_BLOCKS);
ret = aml_read_key_from_boot(s_key_buf, aml_key_map[map_id].offset);
/*verify data*/
if (!ret) {
key = (aml_prov_key_t *)s_key_buf;
ret = aml_verify_key_data(key, aml_key_map[map_id].magic);
} else {
printf("%s aml_read_prov_key ret:%d\n", __func__, ret);
}
/*call provision API to burn*/
if (!ret ) {
ret = aml_set_efuse_obj(aml_key_map[map_id].name, key);
if (ret)
printf("%s write key ret:%d\n", __func__, ret);
else
aml_clear_key_data(map_id);
} else {
printf("%s provision key %s,ret:%d\n", __func__, name, ret);
ret = -1;
}
return ret;
}
static int aml_provision_rootcert(void)
{
int need_regen=0;
int ret = 0;
/*query if dgpk1 exists or not, 1 -- existed, 0 --non existed*/
ret = aml_get_efuse_obj("HASH_NORMAL_DEVICE_ROOTCERT", 1);
if (!ret) {
aml_provision_key("HASH_NORMAL_DEVICE_ROOTCERT");
printf("%s provision HASH_NORMAL_DEVICE_ROOTCERT \n", __func__);
need_regen = 1;
}
ret = aml_get_efuse_obj("HASH_DFU_DEVICE_ROOTCERT", 1);
if (!ret) {
aml_provision_key("HASH_DFU_DEVICE_ROOTCERT");
printf("%s provision HASH_DFU_DEVICE_ROOTCERT \n", __func__);
need_regen = 1;
}
if (need_regen)
run_command("ampk regen_enc_key", 0);
return 0;
}
void aml_provision_dgpk1(int reboot)
{
int ret = 0;
int need_reboot = 0;
aml_provision_rootcert();
ret = aml_get_efuse_obj("DGPK1", 1);
if (!ret) {
aml_provision_key("DGPK1");
printf("%s provision DGPK1 \n", __func__);
need_reboot = 1;
}
if (need_reboot && reboot)
run_command("reboot", 0);
}
void aml_enable_secureboot(int reboot)
{
int ret = 0;
int i = 0;
int need_reboot = 0;
aml_provision_rootcert();
ret = aml_get_efuse_obj("DVGK", 1);
if (!ret) {
aml_provision_key("DVGK");
printf("%s provision DVGK \n", __func__);
need_reboot = 1;
}
ret = aml_get_efuse_obj("DVUK", 1);
if (!ret) {
aml_provision_key("DVUK");
printf("%s provision DVUK \n", __func__);
need_reboot = 1;
}
for (i = 0; i < AML_MAX_FEAT_ENABLE; i++) {
ret = aml_enable_efuse_obj(aml_enable_efuse[i]);
if (!ret)
need_reboot = 1;
}
if (need_reboot && reboot)
run_command("reboot", 0);
}
int do_provision_efuse(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
uint32_t rc = CMD_RET_FAILURE;
if (argc != 2) {
printf("Invalid number of arguments %d\n", argc);
return CMD_RET_USAGE;
}
rc = aml_provision_key(argv[1]);
if (!rc)
rc = CMD_RET_SUCCESS;
return rc;
}
static char provision_efuse_help_text[] =
"<provision_efuse key_name>\n"
"Support root key name including:HASH_NORMAL_DEVICE_ROOTCERT, \n"
"HASH_DFU_DEVICE_ROOTCERT, DGPK1, DVGK, DVUK, AUDIO_VENDOR_ID\n";
U_BOOT_CMD(provision_efuse, 2, 0, do_provision_efuse,
"provision_efuse root keys stored in boot, do not check if the key exist or not",
provision_efuse_help_text
);
int do_provision_dgpk1(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
uint32_t rc = CMD_RET_FAILURE;
int reboot_flag = 0;
if (argc != 2) {
printf("Invalid number of arguments %d\n", argc);
return CMD_RET_USAGE;
}
reboot_flag = simple_strtol(argv[1], NULL, 10);
aml_provision_dgpk1(reboot_flag);
rc = CMD_RET_SUCCESS;
return rc;
}
static char provision_dgpk1_help_text[] =
"<provision_dgpk1 reboot>\n"
"will provision HASH_NORMAL_DEVICE_ROOTCERT and HASH_DFU_DEVICE_ROOTCERT first if they do not exist, \n"
"and then provision DGPK1\n";
U_BOOT_CMD(provision_dgpk1, 2, 0, do_provision_dgpk1,
"provision_dgpk1 will provision dgp1 and root certs if it needed ",
provision_dgpk1_help_text
);
int do_secureboot_enable(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
uint32_t rc = CMD_RET_FAILURE;
int reboot_flag = 0;
if (argc != 2) {
printf("Invalid number of arguments %d\n", argc);
return CMD_RET_USAGE;
}
reboot_flag = simple_strtol(argv[1], NULL, 10);
aml_enable_secureboot(reboot_flag);
rc = CMD_RET_SUCCESS;
return rc;
}
static char secureboot_enable_help_text[] =
"<secureboot_enable reboot>\n"
"will provision HASH_NORMAL_DEVICE_ROOTCERT and HASH_DFU_DEVICE_ROOTCERT first if they do not exist, \n"
"and then enable secure boot\n";
U_BOOT_CMD(secureboot_enable, 2, 0, do_secureboot_enable,
"secureboot_enable will provision and enable efuses related to secure boot and root certs if it needed ",
secureboot_enable_help_text
);
int do_provision_clear(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
uint32_t rc = CMD_RET_FAILURE;
int map_id = -1;
if (argc != 2) {
printf("Invalid number of arguments %d\n", argc);
return CMD_RET_USAGE;
}
map_id = aml_find_key_map(argv[1]);
if (map_id < 0)
return rc;
rc = aml_clear_key_data(map_id);
if (!rc)
rc = CMD_RET_SUCCESS;
return rc;
}
static char provision_clear_help_text[] =
"<provision_clear key_name>\n"
"Support root key name including:HASH_NORMAL_DEVICE_ROOTCERT, \n"
"HASH_DFU_DEVICE_ROOTCERT, DGPK1, DVGK, DVUK, AUDIO_VENDOR_ID\n";
U_BOOT_CMD(provision_clear, 2, 0, do_provision_clear,
"provision_clear root keys stored in boot, do not check if the key exist or not",
provision_clear_help_text
);
#endif /* CONFIG_AML_PROVISION_UBOOT */