blob: d40b58474d007c43cb7865fe44fdeb11b3b64b5c [file] [log] [blame]
/*
* Copyright (C) 2015 Freescale Semiconductor, Inc.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <asm/io.h>
#include <asm/arch/mx6_bee.h>
#include <asm-generic/errno.h>
#include <asm/system.h>
#include <common.h>
#include <command.h>
#include <fuse.h>
DECLARE_GLOBAL_DATA_PTR;
#if (defined(CONFIG_SYS_DCACHE_OFF) || defined(CONFIG_SYS_ICACHE_OFF))
#error "Bee needs Cache Open"
#endif
struct bee_parameters {
int key_method;
int mode;
u32 start1;
u32 size1;
u32 start2;
u32 size2;
};
#define SOFT_KEY 0
#define SNVS_KEY 1
#define ECB_MODE 0
#define CTR_MODE 1
#define AES_REGION0_ADDR 0x10000000
#define AES_REGION1_ADDR 0x30000000
static struct bee_parameters para;
static int bee_inited;
union key_soft {
u8 s_key[16];
u32 b_key[4];
};
union key_soft key_bad;
/* software version */
u8 hw_get_random_byte(void)
{
static u32 lcg_state;
static u32 nb_soft = 9876543;
#define MAX_SOFT_RNG 1024
static const u32 a = 1664525;
static const u32 c = 1013904223;
nb_soft = (nb_soft + 1) % MAX_SOFT_RNG;
lcg_state = (a * lcg_state + c);
return (u8) (lcg_state >> 24);
}
/*
* Lock bee GPR0 bits
* Only reset can release these bits.
*/
static int bee_lock(void)
{
int val;
val = readl(BEE_BASE_ADDR + GPR0);
val |= (GPR0_CTRL_CLK_EN_LOCK | GPR0_CTRL_SFTRST_N_LOCK |
GPR0_CTRL_AES_MODE_LOCK | GPR0_SEC_LEVEL_LOCK |
GPR0_AES_KEY_SEL_LOCK | GPR0_BEE_ENABLE_LOCK);
writel(val, BEE_BASE_ADDR + GPR0);
return 0;
}
/* Only check bee enable lock is enough */
static int bee_locked(void)
{
int val;
val = readl(BEE_BASE_ADDR + GPR0);
return val & GPR0_BEE_ENABLE_LOCK ? 1 : 0;
}
int bee_init(struct bee_parameters *p)
{
int i;
union key_soft *key = &key_bad;
u32 value;
if (bee_locked()) {
printf("BEE already enabled and locked.\n");
return CMD_RET_FAILURE;
}
/* CLKGATE, SFTRST */
writel(GPR0_CTRL_CLK_EN | GPR0_CTRL_SFTRST_N, BEE_BASE_ADDR + GPR0);
/* OFFSET_ADDR0 */
writel(p->start1 >> 16, BEE_BASE_ADDR + GPR1);
/*
* OFFSET_ADDR1
* Default protect IRAM region, if what you want to protect
* bigger that 512M which is the max size that one AES region
* can protect, we need AES region 1 to cover.
*/
writel(p->start2 >> 16, BEE_BASE_ADDR + GPR2);
if (p->key_method == SOFT_KEY) {
for (i = 0; i < 16; i++)
key->s_key[i] = hw_get_random_byte();
/* AES 128 key from software */
/* aes0_key0_w0 */
writel(key->b_key[0], BEE_BASE_ADDR + GPR3);
/* aes0_key0_w1 */
writel(key->b_key[1], BEE_BASE_ADDR + GPR4);
/* aes0_key0_w2 */
writel(key->b_key[2], BEE_BASE_ADDR + GPR5);
/* aes0_key0_w3 */
writel(key->b_key[3], BEE_BASE_ADDR + GPR6);
}
if (p->mode == ECB_MODE) {
value = GPR0_CTRL_CLK_EN | GPR0_CTRL_SFTRST_N |
GPR0_SEC_LEVEL_3 | GPR0_AES_KEY_SEL_SNVS |
GPR0_BEE_ENABLE | GPR0_CTRL_AES_MODE_ECB;
if (p->key_method == SOFT_KEY)
value = GPR0_CTRL_CLK_EN | GPR0_CTRL_SFTRST_N |
GPR0_SEC_LEVEL_3 | GPR0_AES_KEY_SEL_SOFT |
GPR0_BEE_ENABLE | GPR0_CTRL_AES_MODE_ECB;
writel(value, BEE_BASE_ADDR + GPR0);
} else {
for (i = 0; i < 16; i++)
key->s_key[i] = hw_get_random_byte();
/* aes_key1_w0 */
writel(key->b_key[0], BEE_BASE_ADDR + GPR8);
/* aes_key1_w1 */
writel(key->b_key[1], BEE_BASE_ADDR + GPR9);
/* aes_key1_w2 */
writel(key->b_key[2], BEE_BASE_ADDR + GPR10);
/* aes_key1_w3 */
writel(key->b_key[3], BEE_BASE_ADDR + GPR11);
value = GPR0_CTRL_CLK_EN | GPR0_CTRL_SFTRST_N |
GPR0_SEC_LEVEL_3 | GPR0_AES_KEY_SEL_SNVS |
GPR0_BEE_ENABLE | GPR0_CTRL_AES_MODE_CTR;
if (p->key_method == SOFT_KEY)
value = GPR0_CTRL_CLK_EN | GPR0_CTRL_SFTRST_N |
GPR0_SEC_LEVEL_3 | GPR0_AES_KEY_SEL_SOFT |
GPR0_BEE_ENABLE | GPR0_CTRL_AES_MODE_CTR;
writel(value, BEE_BASE_ADDR + GPR0);
}
bee_lock();
printf("BEE is settings as: %s mode, %s %d key\n",
(p->mode == ECB_MODE) ? "ECB" : "CTR",
(p->key_method == SOFT_KEY) ? "SOFT" : "SNVS HW",
(p->mode == ECB_MODE) ? 128 : 256);
return CMD_RET_SUCCESS;
}
int bee_test(struct bee_parameters *p, int region)
{
u32 result = 0, range, address;
int i, val;
/*
* Test instruction running in AES Region:
* int test(void)
* {
* return 0x55aa55aa;
* }
* Assemble:
* 0xe59f0000: ldr r0, [pc]
* 0xe12fff1e: bx lr
* 0x55aa55aa: 0x55aa55aa
*/
u32 inst[3] = {0xe59f0000, 0xe12fff1e, 0x55aa55aa};
/* Cache enabled? */
if ((get_cr() & (CR_I | CR_C)) != (CR_I | CR_C)) {
printf("Enable dcache and icache first!\n");
return CMD_RET_FAILURE;
}
printf("Test Region %d\nBegin Data test: Writing... ", region);
range = (region == 0) ? p->size1 : p->size2;
address = (region == 0) ? AES_REGION0_ADDR : AES_REGION1_ADDR;
for (i = 0; i < range; i = i + 4)
writel(i, address + i);
printf("Finshed Write!\n");
flush_dcache_range(address, address + range);
printf("Reading... ");
for (i = 0; i < range; i = i + 4) {
val = readl(address + i);
if (val != i)
result++;
}
printf("Finshed Read!\n");
if (result > 0)
printf("BEE Data Test check Failed!\n");
else
printf("BEE Data Test Check Passed!\n");
for (i = 0; i < ARRAY_SIZE(inst); i++)
writel(inst[i], address + (i * 4));
flush_dcache_range(address, address + sizeof(inst));
val = ((int (*)(void))address)();
printf("\nBee Instruction test, Program:\n"
"int test(void)\n"
"{\n"
" return 0x55aa55aa;\n"
"}\n"
"Assemble:\n"
"0xe59f0000: ldr r0, [pc]\n"
"0xe12fff1e: bx lr\n"
"0x55aa55aa: 0x55aa55aa\n"
"Runnint at 0x%x\n", address);
if (val == 0x55aa55aa)
printf("Bee Instruction Test Passed!\n");
else
printf("Bee Instruction Test Failed!\n");
return CMD_RET_SUCCESS;
}
static int region_valid(u32 start, u32 size)
{
if ((start < PHYS_SDRAM) || (start >= (start + size - 1)) ||
(start >= (PHYS_SDRAM + PHYS_SDRAM_SIZE - 1))) {
printf("Invalid start 0x%x, size 0x%x\n", start, size);
return -EINVAL;
}
if (size > SZ_512M) {
printf("The region size exceeds SZ_512M\n");
return -EINVAL;
}
if ((start & 0xFFFF) && (size & 0xFFFF)) {
printf("start or size not 64KB aligned!\n");
return -EINVAL;
}
if ((start + size - 1) >= (gd->start_addr_sp - CONFIG_STACKSIZE)) {
printf("Overlap with uboot execution environment!\n"
"Decrease size or start\n");
return -EINVAL;
}
return 0;
}
static int do_bee_init(cmd_tbl_t *cmdtp, int flag, int argc,
char * const argv[])
{
u32 start, size, val;
int ret;
struct bee_parameters *p = &para;
#if defined(CONFIG_SYS_ARM_CACHE_WRITETHROUGH)
enum dcache_option option = DCACHE_WRITETHROUGH;
#else
enum dcache_option option = DCACHE_WRITEBACK;
#endif
if (argc > 5)
return CMD_RET_USAGE;
if (fuse_read(0, 4, &val)) {
puts("Can not get fuse bank 0, word 4\n");
} else {
if (val & (1 << 25)) {
puts("BEE disabed in fuse!\n");
return CMD_RET_FAILURE;
}
}
/* Cache enabled? */
if ((get_cr() & (CR_I | CR_C)) != (CR_I | CR_C)) {
/*
* Here we need icache and dcache both enabled, because
* we may take the protected region for instruction and
* data usage. And icache and dcache both enabled are
* better for performance.
*/
printf("Please enable dcache and icache first!\n");
return CMD_RET_FAILURE;
}
p->key_method = SOFT_KEY;
p->mode = ECB_MODE;
p->start1 = PHYS_SDRAM;
p->size1 = SZ_512M;
p->start2 = IRAM_BASE_ADDR;
p->size2 = IRAM_SIZE;
if (argc == 2) {
p->key_method = (int)simple_strtoul(argv[1], NULL, 16);
p->mode = ECB_MODE;
p->start1 = PHYS_SDRAM;
p->size1 = SZ_512M;
} else if (argc == 3) {
p->key_method = (int)simple_strtoul(argv[1], NULL, 16);
p->mode = (int)simple_strtoul(argv[2], NULL, 10);
p->start1 = PHYS_SDRAM;
p->size1 = SZ_512M;
} else if ((argc == 4) || (argc == 5)) {
p->key_method = (int)simple_strtoul(argv[1], NULL, 16);
p->mode = (int)simple_strtoul(argv[2], NULL, 10);
start = (u32)simple_strtoul(argv[3], NULL, 16);
/* Default size that AES Region0 can protected */
size = SZ_512M;
if (argc == 5)
size = (u32)simple_strtoul(argv[4], NULL, 16);
p->start1 = start;
p->size1 = size;
}
if ((p->key_method != SOFT_KEY) && (p->key_method != SNVS_KEY))
return CMD_RET_USAGE;
if ((p->mode != ECB_MODE) && (p->mode != CTR_MODE))
return CMD_RET_USAGE;
/*
* No need to check region valid for IRAM, since it is fixed.
* Only check DRAM region here.
*/
if (region_valid(p->start1, p->size1))
return CMD_RET_FAILURE;
ret = bee_init(p);
if (ret)
return CMD_RET_FAILURE;
/*
* Set DCACHE OFF to AES REGION0 and AES REGION1 first
* to avoid possible unexcepted cache settings.
*/
mmu_set_region_dcache_behaviour(AES_REGION0_ADDR, SZ_1G, DCACHE_OFF);
mmu_set_region_dcache_behaviour(AES_REGION0_ADDR, p->size1, option);
mmu_set_region_dcache_behaviour(AES_REGION1_ADDR, p->size2, option);
printf("Access Region 0x%x - 0x%x to protect 0x%x - 0x%x\n"
"Do not directly access 0x%x - 0x%x\n"
"Access Region 0x%x - 0x%x to protect 0x%x - 0x%x\n"
"Do not directly access 0x%x - 0x%x\n",
AES_REGION0_ADDR, AES_REGION0_ADDR + p->size1 - 1,
p->start1, p->start1 + p->size1 - 1,
p->start1, p->start1 + p->size1 - 1,
AES_REGION1_ADDR, AES_REGION1_ADDR + p->size2 - 1,
p->start2, p->start2 + p->size2 - 1,
p->start2, p->start2 + p->size2 - 1);
bee_inited = 1;
return CMD_RET_SUCCESS;
}
static int do_bee_test(cmd_tbl_t *cmdtp, int flag, int argc,
char * const argv[])
{
int ret;
int region;
if (bee_inited == 0) {
printf("Bee not initialized, run bee init first!\n");
return CMD_RET_FAILURE;
}
if (argc > 2)
return CMD_RET_USAGE;
region = 0;
if (argc == 2)
region = (int)simple_strtoul(argv[1], NULL, 16);
/* Only two regions are supported, 0 and 1 */
if (region >= 2)
return CMD_RET_USAGE;
ret = bee_test(&para, region);
if (ret)
return CMD_RET_FAILURE;
return CMD_RET_SUCCESS;
}
static cmd_tbl_t cmd_bmp_sub[] = {
U_BOOT_CMD_MKENT(init, 5, 0, do_bee_init, "", ""),
U_BOOT_CMD_MKENT(test, 2, 0, do_bee_test, "", ""),
};
static int do_bee_ops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
cmd_tbl_t *c;
c = find_cmd_tbl(argv[1], &cmd_bmp_sub[0], ARRAY_SIZE(cmd_bmp_sub));
/* Drop off the 'bee' command argument */
argc--;
argv++;
if (c)
return c->cmd(cmdtp, flag, argc, argv);
else
return CMD_RET_USAGE;
}
U_BOOT_CMD(
bee, CONFIG_SYS_MAXARGS, 1, do_bee_ops,
"BEE function test",
"init [key] [mode] [start] [size] - BEE block initial\n"
" key: 0 | 1, 0 means software key, 1 means SNVS random key\n"
" mode: 0 | 1, 0 means ECB mode, 1 means CTR mode\n"
" start: start address that you want to protect\n"
" size: The size of the area that you want to protect\n"
" start and end(start + size) addr both should be 64KB aligned.\n"
"\n"
" After initialization, the mapping:\n"
" 1. [0x10000000 - (0x10000000 + size - 1)] <--->\n"
" [start - (start + size - 1)]\n"
" Here [start - (start + size -1)] is fixed mapping to\n"
" [0x10000000 - (0x10000000 + size - 1)], whatever start is.\n"
" 2. [0x30000000 - (0x30000000 + IRAM_SIZE - 1)] <--->\n"
" [IRAM_BASE_ADDR - (IRAM_BASE_ADDR + IRAM_SIZE - 1)]\n"
"\n"
" Note: Here we only use AES region 0 to protect the DRAM\n"
" area that you specified, max size SZ_512M.\n"
" AES region 1 is used to protect IRAM area.\n"
" Example:\n"
" 1. bee init 1 1 0xa0000000 0x10000\n"
" Access 0x10000000 - 0x10010000 to protect 0xa0000000 - 0xa0010000\n"
" 2. bee init 1 1 0x80000000 0x20000\n"
" Access 0x10000000 - 0x10020000 to protect 0x80000000 - 0x80020000\n"
"\n"
" Default configuration if only `bee init` without any args:\n"
" 1. software key\n"
" 2. ECB mode\n"
" 3. Address protected:\n"
" Remapped Region0: PHYS_SDRAM - PHYS_SDRAM + SZ_512M\n"
" Remapped Region1: IRAM_BASE_ADDR - IRAM_BASE_ADDR + IRAM_SIZE\n"
" 4. Default Mapping for 6UL:\n"
" [0x10000000 - 0x2FFFFFFF] <-> [0x80000000 - 0x9FFFFFFF]\n"
" [0x30000000 - 0x3001FFFF] <-> [0x00900000 - 0x0091FFFF]\n"
"\n"
"bee test [region] - BEE function test\n"
" region: 0 | 1, 0 means region0, 1 means regions1\n"
);