| /* |
| * Copyright (C) 2015-2016 Freescale Semiconductor, Inc. |
| * Copyright 2018 NXP |
| * |
| * SPDX-License-Identifier: GPL-2.0+ |
| */ |
| |
| #include <asm/io.h> |
| #include <asm/arch/mx6_bee.h> |
| #include <linux/errno.h> |
| #include <asm/system.h> |
| #include <common.h> |
| #include <command.h> |
| #include <fuse.h> |
| #include <asm/arch/sys_proto.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; |
| } |
| |
| /* 128K for U-Boot Stack */ |
| if ((start + size - 1) >= (gd->start_addr_sp - SZ_128K)) { |
| 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; |
| int ret; |
| struct bee_parameters *p = ¶ |
| |
| #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; |
| |
| #ifdef CONFIG_MX6 |
| if (check_module_fused(MX6_MODULE_BEE)) { |
| printf("BEE is fused, disable it!\n"); |
| return CMD_RET_FAILURE; |
| } |
| #endif |
| |
| /* 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(¶, 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" |
| ); |
| |