460 lines
12 KiB
C
460 lines
12 KiB
C
/*
|
|
* (C) Copyright 2018-2020
|
|
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
|
|
* ouyangkun <ouyangkun@allwinnertech.com>
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0+
|
|
*/
|
|
#include <common.h>
|
|
#include <asm/io.h>
|
|
#include <smc.h>
|
|
#include <fdt_support.h>
|
|
#include <sys_partition.h>
|
|
#include <memalign.h>
|
|
#include <sunxi_image_verifier.h>
|
|
#include <openssl_ext.h>
|
|
#include <securestorage.h>
|
|
#include "usb.h"
|
|
#include <asm/arch/clock.h>
|
|
#include "sunxi_challenge.h"
|
|
|
|
#define DEBUG_PRINT 0
|
|
|
|
enum MIPS_DATA_TYPE_t {
|
|
MIPS_CODE,
|
|
/*
|
|
* order db->db_table->db_project if critical
|
|
* since *end of first one if *start of next one
|
|
*/
|
|
MIPS_DATABASE,
|
|
MIPS_PROJECT_TABLE,
|
|
MIPS_DB_PROJECT,
|
|
MIPS_XML
|
|
};
|
|
const char *mips_names[] = { [MIPS_CODE] = "mips_code",
|
|
[MIPS_PROJECT_TABLE] = "mips_project_table",
|
|
[MIPS_DATABASE] = "mips_database",
|
|
[MIPS_DB_PROJECT] = "mips_db_project",
|
|
[MIPS_XML] = "mips_xml" };
|
|
struct mips_data_info_t {
|
|
char file_name[64];
|
|
uint32_t file_len;
|
|
char cert_name[64];
|
|
uint32_t cert_len;
|
|
uint32_t load_addr;
|
|
char str_addr[64];
|
|
uint8_t flag;
|
|
};
|
|
#define FILE_NAME_NOT_FOUNT (1 << 0)
|
|
#define CERT_NAME_NOT_FOUNT (1 << 1)
|
|
#define LOAD_ADDR_NOT_FOUNT (1 << 2)
|
|
extern int do_sunxi_flash(cmd_tbl_t *cmdtp, int flag, int argc,
|
|
char *const argv[]);
|
|
static int usb_debug_enabled;
|
|
|
|
static void load_setting_from_fdt(struct mips_data_info_t *info, int index)
|
|
{
|
|
char *pstr_tmp;
|
|
int node;
|
|
char node_name[64];
|
|
sprintf(node_name, "/firmware/mips/%s", mips_names[index]);
|
|
node = fdt_path_offset(working_fdt, node_name);
|
|
if (fdt_getprop_string(working_fdt, node, "file_name", &pstr_tmp) < 0)
|
|
info->flag |= FILE_NAME_NOT_FOUNT;
|
|
else
|
|
strcpy(info->file_name, pstr_tmp);
|
|
|
|
if (fdt_getprop_string(working_fdt, node, "cert_name", &pstr_tmp) < 0)
|
|
info->flag |= CERT_NAME_NOT_FOUNT;
|
|
else
|
|
strcpy(info->cert_name, pstr_tmp);
|
|
|
|
if (fdt_getprop_u32(working_fdt, node, "load_addr", &info->load_addr) <
|
|
0)
|
|
info->flag |= LOAD_ADDR_NOT_FOUNT;
|
|
else
|
|
sprintf(info->str_addr, "%x", info->load_addr);
|
|
|
|
if (index == MIPS_DB_PROJECT) {
|
|
char projectID_name[64];
|
|
int projectID_len;
|
|
char tmp_name[64];
|
|
/*project database file name build dynamically*/
|
|
if (sunxi_secure_object_read("mips_projectID", projectID_name,
|
|
64, &projectID_len)) {
|
|
strcpy(projectID_name, "0x0001");
|
|
}
|
|
strcpy(tmp_name, info->file_name);
|
|
|
|
snprintf(info->cert_name, sizeof(info->cert_name), "%s%s%s",
|
|
tmp_name, projectID_name, ".der");
|
|
info->flag &= ~CERT_NAME_NOT_FOUNT;
|
|
|
|
snprintf(info->file_name, sizeof(info->file_name), "%s%s%s",
|
|
tmp_name, projectID_name, ".TSE");
|
|
info->flag &= ~FILE_NAME_NOT_FOUNT;
|
|
}
|
|
|
|
if (index == MIPS_DB_PROJECT || index == MIPS_PROJECT_TABLE) {
|
|
/*addr decided by prev item *end, not need to load from fdt*/
|
|
info->flag &= ~LOAD_ADDR_NOT_FOUNT;
|
|
}
|
|
#if DEBUG_PRINT == 1
|
|
pr_err(" file_name:%s\n", info->file_name);
|
|
pr_err(" cert_name:%s\n", info->cert_name);
|
|
pr_err(" load_addr:%x\n", info->load_addr);
|
|
pr_err(" flag :%x\n", info->flag);
|
|
#endif
|
|
}
|
|
|
|
enum USB_VERIFY_ST_t {
|
|
NOT_DONE,
|
|
/*
|
|
* order db->db_table->db_project if critical
|
|
* since *end of first one if *start of next one
|
|
*/
|
|
PASS,
|
|
FAILED
|
|
};
|
|
static enum USB_VERIFY_ST_t usb_key_valid;
|
|
|
|
enum read_source_en {
|
|
FROM_PART,
|
|
FROM_USB,
|
|
};
|
|
struct read_param_t {
|
|
char *cmd;
|
|
char interface[16];
|
|
char dev[16];
|
|
char *addr;
|
|
char *file_name;
|
|
char *null;
|
|
};
|
|
static int prepare_read_argv(struct read_param_t *read_argv)
|
|
{
|
|
int partno = -1;
|
|
|
|
memset(&read_argv[FROM_PART], 0, sizeof(struct read_param_t));
|
|
partno = sunxi_partition_get_partno_byname("mips"); /*android*/
|
|
if (partno >= 0) {
|
|
read_argv[FROM_PART].cmd = "fatload";
|
|
strcpy(read_argv[FROM_PART].interface, "sunxi_flash");
|
|
snprintf(read_argv[FROM_PART].dev, 16, "0:%x", partno);
|
|
} else {
|
|
pr_err("Get mips partition number fail!\n");
|
|
return -1;
|
|
}
|
|
|
|
memset(&read_argv[FROM_USB], 0, sizeof(struct read_param_t));
|
|
|
|
#ifdef CONFIG_USB_STORAGE
|
|
char *str = env_get("load_mips_from_usb");
|
|
if ((str) && (strcmp(str, "1") == 0)) {
|
|
/*todo: get a better place to init usb_debug_enabled*/
|
|
usb_debug_enabled = 1;
|
|
}
|
|
if (usb_debug_enabled) {
|
|
run_command("usb start", CMD_FLAG_ENV);
|
|
if (!usb_stor_info()) {
|
|
read_argv[FROM_USB].cmd = "fatload";
|
|
strcpy(read_argv[FROM_USB].interface, "usb");
|
|
snprintf(read_argv[FROM_USB].dev, 16, "0");
|
|
usb_key_valid = NOT_DONE;
|
|
} else {
|
|
/*no usb storage found, disable usb_debug to skip useless try*/
|
|
usb_debug_enabled = 0;
|
|
}
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static void verify_usb_key(struct read_param_t *read_argv)
|
|
{
|
|
char *tmp_argv[6];
|
|
unsigned char *key_buf = memalign(CACHE_LINE_SIZE, 1024 * 16);
|
|
char key_buf_str[24];
|
|
if (!key_buf) {
|
|
usb_key_valid = FAILED;
|
|
}
|
|
snprintf(key_buf_str, 24, "%x", (uint32_t)key_buf);
|
|
tmp_argv[0] = read_argv[FROM_USB].cmd;
|
|
tmp_argv[1] = read_argv[FROM_USB].interface;
|
|
tmp_argv[2] = read_argv[FROM_USB].dev;
|
|
tmp_argv[3] = key_buf_str;
|
|
tmp_argv[4] = "usb_debug_key.der";
|
|
if (do_fat_fsload(0, 0, 5, tmp_argv)) {
|
|
pr_msg("unable to open %s\n from usb", tmp_argv[4]);
|
|
usb_key_valid = FAILED;
|
|
} else {
|
|
unsigned char *salted_challenge =
|
|
memalign(CACHE_LINE_SIZE, 512);
|
|
memset(salted_challenge, 0, 512);
|
|
strcpy((char *)salted_challenge,
|
|
"salt to derived challenge into mips key challenge");
|
|
memcpy(&salted_challenge[128], sunxi_challenge,
|
|
sunxi_challenge_len);
|
|
if (sunxi_verify_mips(salted_challenge,
|
|
128 + sunxi_challenge_len, key_buf,
|
|
1024 * 16)) {
|
|
pr_err("verify usb debug key failed\n");
|
|
usb_key_valid = FAILED;
|
|
} else {
|
|
usb_key_valid = PASS;
|
|
}
|
|
}
|
|
free(key_buf);
|
|
return;
|
|
}
|
|
|
|
static int try_fat_fload(struct read_param_t *read_argv,
|
|
struct mips_data_info_t *mips_data_info,
|
|
char *cert_addr)
|
|
{
|
|
char *tmp_argv[6];
|
|
uint32_t *file_len;
|
|
int read_src = -1;
|
|
|
|
if (usb_debug_enabled) {
|
|
if (usb_key_valid == NOT_DONE)
|
|
verify_usb_key(read_argv);
|
|
|
|
if (usb_key_valid == PASS) {
|
|
tmp_argv[0] = read_argv[FROM_USB].cmd;
|
|
tmp_argv[1] = read_argv[FROM_USB].interface;
|
|
tmp_argv[2] = read_argv[FROM_USB].dev;
|
|
if (!cert_addr) {
|
|
tmp_argv[3] = mips_data_info->str_addr;
|
|
tmp_argv[4] = mips_data_info->file_name;
|
|
file_len = &mips_data_info->file_len;
|
|
} else {
|
|
tmp_argv[3] = cert_addr;
|
|
tmp_argv[4] = mips_data_info->cert_name;
|
|
file_len = &mips_data_info->cert_len;
|
|
}
|
|
tmp_argv[5] = NULL;
|
|
if (do_fat_fsload(0, 0, 5, tmp_argv)) {
|
|
pr_msg("unable to open %s\n from usb",
|
|
tmp_argv[4]);
|
|
} else {
|
|
read_src = FROM_USB;
|
|
goto read_done;
|
|
}
|
|
}
|
|
}
|
|
|
|
tmp_argv[0] = read_argv[FROM_PART].cmd;
|
|
tmp_argv[1] = read_argv[FROM_PART].interface;
|
|
tmp_argv[2] = read_argv[FROM_PART].dev;
|
|
if (!cert_addr) {
|
|
tmp_argv[3] = mips_data_info->str_addr;
|
|
tmp_argv[4] = mips_data_info->file_name;
|
|
file_len = &mips_data_info->file_len;
|
|
} else {
|
|
tmp_argv[3] = cert_addr;
|
|
tmp_argv[4] = mips_data_info->cert_name;
|
|
file_len = &mips_data_info->cert_len;
|
|
}
|
|
tmp_argv[5] = NULL;
|
|
if (do_fat_fsload(0, 0, 5, tmp_argv)) {
|
|
pr_err("unable to open %s\n", tmp_argv[4]);
|
|
|
|
return -1;
|
|
}
|
|
read_src = FROM_PART;
|
|
|
|
read_done:
|
|
*file_len = simple_strtoul(env_get("filesize"), NULL, 16);
|
|
#if DEBUG_PRINT == 1
|
|
if (!cert_addr)
|
|
pr_err(" file_len :%x\n", mips_data_info->file_len);
|
|
else
|
|
pr_err(" cert_len :%x\n", mips_data_info->cert_len);
|
|
#endif
|
|
pr_msg("load %s from %s\n", tmp_argv[4],
|
|
read_src == FROM_USB ? "usb" : "part");
|
|
|
|
return 0;
|
|
}
|
|
|
|
__maybe_unused static void
|
|
dump_mips_data_info(struct mips_data_info_t *mips_data_info)
|
|
{
|
|
printf("file_name:%s\n"
|
|
"file_len:%d\n"
|
|
"cert_name:%s\n"
|
|
"cert_len:%d\n"
|
|
"load_addr:%d\n"
|
|
"str_addr:%s\n"
|
|
"flag:%d\n",
|
|
mips_data_info->file_name, mips_data_info->file_len,
|
|
mips_data_info->cert_name, mips_data_info->cert_len,
|
|
mips_data_info->load_addr, mips_data_info->str_addr,
|
|
mips_data_info->flag);
|
|
}
|
|
|
|
static int load_mips_data(uint32_t *run_addr)
|
|
{
|
|
int i;
|
|
int ret = -1;
|
|
char cert_addr[32];
|
|
uint8_t *cert_tmp = NULL;
|
|
struct mips_data_info_t mips_data_info[ARRAY_SIZE(mips_names)];
|
|
|
|
struct read_param_t read_argv[2];
|
|
if (prepare_read_argv(read_argv)) {
|
|
return -1;
|
|
}
|
|
|
|
/*prepare buffer for cert verify*/
|
|
if (sunxi_get_secureboard()) {
|
|
cert_tmp = memalign(CACHE_LINE_SIZE, 8192);
|
|
if (!cert_tmp) {
|
|
pr_err("no memory for cert tmp\n");
|
|
goto err;
|
|
} else {
|
|
sprintf(cert_addr, "%x", (uint32_t)cert_tmp);
|
|
#if DEBUG_PRINT == 1
|
|
pr_err(" cert_addr :%s\n", cert_addr);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
memset(&mips_data_info, 0, sizeof(mips_data_info));
|
|
for (i = 0; i < ARRAY_SIZE(mips_names); i++) {
|
|
load_setting_from_fdt(&mips_data_info[i], i);
|
|
|
|
if (mips_data_info[i].flag &
|
|
(FILE_NAME_NOT_FOUNT | LOAD_ADDR_NOT_FOUNT)) {
|
|
pr_err("no info for %s\n", mips_names[i]);
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(mips_names); i++) {
|
|
if (try_fat_fload(read_argv, &mips_data_info[i], NULL)) {
|
|
goto err;
|
|
}
|
|
/*
|
|
* db,projecttable,db_project should cat toghter,
|
|
* use prev *end as next *start
|
|
*/
|
|
switch (i) {
|
|
case MIPS_DATABASE:
|
|
case MIPS_PROJECT_TABLE:
|
|
mips_data_info[i + 1].load_addr =
|
|
mips_data_info[i].load_addr +
|
|
mips_data_info[i].file_len;
|
|
sprintf(mips_data_info[i + 1].str_addr, "%x",
|
|
mips_data_info[i + 1].load_addr);
|
|
break;
|
|
}
|
|
|
|
/*load cert and verify, xml do not need verify*/
|
|
if (sunxi_get_secureboard() && i != MIPS_XML) {
|
|
if (mips_data_info[i].flag & CERT_NAME_NOT_FOUNT)
|
|
pr_err("no cert for %s\n", mips_names[i]);
|
|
if (try_fat_fload(read_argv, &mips_data_info[i],
|
|
cert_addr))
|
|
goto err;
|
|
|
|
if (sunxi_verify_mips(
|
|
(uint8_t *)mips_data_info[i].load_addr,
|
|
mips_data_info[i].file_len, cert_tmp,
|
|
mips_data_info[i].cert_len)) {
|
|
pr_err("verify %s failed\n",
|
|
mips_data_info[i].file_name);
|
|
goto err;
|
|
}
|
|
}
|
|
}
|
|
|
|
ret = 0;
|
|
*run_addr = mips_data_info[MIPS_CODE].load_addr;
|
|
err:
|
|
if (cert_tmp)
|
|
free(cert_tmp);
|
|
return ret;
|
|
}
|
|
|
|
__maybe_unused static void release_mips(uint32_t img_addr)
|
|
{
|
|
#define MIPS_CFG_BASE 0x3061000
|
|
/* set mips clock to 400M*/
|
|
uint32_t src_clk = clock_get_pll6() * 2;
|
|
uint32_t div = src_clk / 400;
|
|
uint32_t reg = readl(SUNXI_CCM_BASE + 0x600);
|
|
reg &= ~(0x7);
|
|
reg |= div - 1;
|
|
writel(reg, SUNXI_CCM_BASE + 0x600);
|
|
|
|
/*enable mips clock, but dont release mips, set mips table addr first*/
|
|
reg |= 1 << 31;
|
|
writel(reg, SUNXI_CCM_BASE + 0x600);
|
|
writel(0x00030001, SUNXI_CCM_BASE + 0x60C);
|
|
udelay(100); /*wait module ready for configuration*/
|
|
writel(img_addr, MIPS_CFG_BASE + 0x30);
|
|
|
|
/*mips table addr set up done, now release mips*/
|
|
writel(0x00070001, SUNXI_CCM_BASE + 0x60C);
|
|
}
|
|
|
|
int do_boot_mips(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
|
|
{
|
|
uint32_t img_addr;
|
|
int ret = -1;
|
|
|
|
if (strcmp("start", argv[1]) == 0) {
|
|
if (load_mips_data(&img_addr) == 0) {
|
|
uint32_t mips_only_size, shm_size;
|
|
#if defined(FPGA_PLATFORM)
|
|
/*init uart gpio*/
|
|
writel(0x00000200, SUNXI_PIO_BASE + 0x34);
|
|
#endif
|
|
int node;
|
|
/*read memory layout setting*/
|
|
mips_only_size = 0;
|
|
shm_size = 0;
|
|
node = fdt_path_offset(working_fdt,
|
|
"/firmware/mips/mips_memory");
|
|
if (node >= 0) {
|
|
fdt_getprop_u32(working_fdt, node,
|
|
"mips_only_size",
|
|
&mips_only_size);
|
|
fdt_getprop_u32(working_fdt, node, "shm_size",
|
|
&shm_size);
|
|
#if DEBUG_PRINT == 1
|
|
pr_err("mips_only_size %x\n", mips_only_size);
|
|
pr_err("shm_size %x\n", shm_size);
|
|
#endif
|
|
}
|
|
if (!mips_only_size && !shm_size) {
|
|
pr_err("no info for mips memory\n");
|
|
ret = -1;
|
|
} else {
|
|
#if 0 //do not release mips for now
|
|
if (sunxi_get_secureboard()) {
|
|
smc_tee_setup_mips(img_addr,
|
|
mips_only_size);
|
|
} else {
|
|
release_mips(img_addr);
|
|
}
|
|
#endif
|
|
ret = fdt_add_mem_rsv(working_fdt, img_addr,
|
|
mips_only_size +
|
|
shm_size);
|
|
if (ret) {
|
|
pr_err("##add mem rsv error: %s : %s\n",
|
|
__func__, fdt_strerror(ret));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static char boot_mips_help[] = "start - boot mips application\n";
|
|
|
|
U_BOOT_CMD(sunxi_mips, CONFIG_SYS_MAXARGS, 1, do_boot_mips,
|
|
"boot application image from memory", boot_mips_help);
|