/* * (C) Copyright 2000-2999 * Allwinner Technology Co., Ltd. * Author: qinjian * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include #ifdef CONFIG_ARCH_SUN8IW12P1 #define SECURE_BIT_OFFSET 31 #else #define SECURE_BIT_OFFSET 11 #endif #define SBROM_ACCELERATION_ENABLE_BIT 29 #define EFUSE_DBG_E 0 #if EFUSE_DBG_E static void efuse_dump(char *str, unsigned char *data, int len, int align) { int i = 0; if (str) printf("\n%s: ", str); for (i = 0; i < len; i++) { if ((i % align) == 0) { printf("\n"); } printf("%x", *(data++)); } printf("\n"); } #define EFUSE_DBG printf #define EFUSE_DBG_DUMP efuse_dump #define EFUSE_DUMP_LEN 16 #else #define EFUSE_DBG_DUMP(...) \ do { \ } while (0); #define EFUSE_DBG(...) \ do { \ } while (0); #endif #ifndef EFUSE_READ_PROTECT #define EFUSE_READ_PROTECT EFUSE_CHIPCONFIG #define EFUSE_WRITE_PROTECT EFUSE_CHIPCONFIG #endif /* internal struct */ typedef struct efuse_key_map_new { char name[SUNXI_KEY_NAME_LEN]; int offset; int size; /* unit: bit */ int rd_fbd_offset; /*read protect*/ int burned_flg_offset; /*write protect*/ int sw_rule; } efuse_key_map_new_t; /*It can not be seen.*/ #define EFUSE_PRIVATE (0) /*After burned ,cpu can not access.*/ #define EFUSE_NACCESS (1 << 1) #define EFUSE_RW (2 << 1) #define EFUSE_RO (3 << 1) #define EFUSE_ACL_SET_BRUN_BIT (1 << 29) #define EFUSE_ACL_SET_RD_FORBID_BIT (1 << 30) #define EFUSE_BRUN_RD_OFFSET_MASK (0xFFFFFF) #define EFUSE_DEF_ITEM(name, offset, size_bits, rd_offset, burn_offset, acl) \ { \ name, offset, size_bits, rd_offset, burn_offset, acl \ } #if defined(CONFIG_MRCH_SUN50IW11P1) static efuse_key_map_new_t g_key_info[] = { EFUSE_DEF_ITEM(EFUSE_ROTPK_NAME, 0x38, 256, -1, -1, EFUSE_RO), EFUSE_DEF_ITEM("", 0, 0, 0, 0, EFUSE_PRIVATE), }; #elif defined(CONFIG_ARCH_SUN8IW17P1) static efuse_key_map_new_t g_key_info[] = { EFUSE_DEF_ITEM(EFUSE_THM_SENSOR_NAME, 0x14, 96, 2, 2, EFUSE_RO), EFUSE_DEF_ITEM(EFUSE_ROTPK_NAME, 0x70, 256, -1, -1, EFUSE_RO), EFUSE_DEF_ITEM("sensor", 0x14, 96, 2, 2, EFUSE_RO), EFUSE_DEF_ITEM(EFUSE_FT_ZONE_NAME, 0x20, 128, 3, 3, EFUSE_RW), EFUSE_DEF_ITEM(EFUSE_TV_OUT_NAME, 0x30, 32, 4, 4, EFUSE_RO), EFUSE_DEF_ITEM(EFUSE_OEM_NAME, 0x34, 96, 4, 4, EFUSE_RO), EFUSE_DEF_ITEM("", 0, 0, 0, 0, EFUSE_PRIVATE), }; #elif defined(CONFIG_MRCH_SUN8IW19P1) static efuse_key_map_new_t g_key_info[] = { EFUSE_DEF_ITEM(EFUSE_CHIPID_NAME, 0x0, 128, -1, 0, EFUSE_RO), EFUSE_DEF_ITEM(EFUSE_THM_SENSOR_NAME, EFUSE_THERMAL_SENSOR, 64, -1, 0, EFUSE_RO), EFUSE_DEF_ITEM(EFUSE_FT_ZONE_NAME, EFUSE_TF_ZONE, 128, -1, 0, EFUSE_RO), EFUSE_DEF_ITEM("psensor", EFUSE_PSENSOR, 32, -1, 15, EFUSE_RO), EFUSE_DEF_ITEM("anti_blushing", EFUSE_ANTI_BLUSHING, 32, -1, 15, EFUSE_RO), EFUSE_DEF_ITEM(EFUSE_RESERVED_NAME, EFUSE_CLIENT_RESERVE, 256, -1, 12, EFUSE_RO), EFUSE_DEF_ITEM("", 0, 0, 0, 0, EFUSE_PRIVATE), }; #elif defined(CONFIG_MRCH_SUN8IW7P1) static efuse_key_map_new_t g_key_info[] = { EFUSE_DEF_ITEM(EFUSE_ROTPK_NAME, 0x64, 256, -1, -1, EFUSE_RO), EFUSE_DEF_ITEM("", 0, 0, 0, 0, EFUSE_PRIVATE), }; #elif defined(CONFIG_MRCH_SUN8IW18P1) static efuse_key_map_new_t g_key_info[] = { EFUSE_DEF_ITEM(EFUSE_ROTPK_NAME, 0x40, 256, -1, -1, EFUSE_RO), EFUSE_DEF_ITEM("", 0, 0, 0, 0, EFUSE_PRIVATE), }; #else /*Please extend key_maps for new arch here*/ static efuse_key_map_new_t g_key_info[] = { EFUSE_DEF_ITEM(EFUSE_ROTPK_NAME, 0x70, 256, -1, -1, EFUSE_RO), EFUSE_DEF_ITEM("", 0, 0, 0, 0, EFUSE_PRIVATE), }; #endif __weak int set_efuse_voltage(int vol) { return 0; } /*Please reference 1728 spec page11 to know why to add this function *burn efuse :efuse sram can not get the latest value *unless via sid read or reboot. */ static uint __sid_reg_read_key(uint key_index) { uint reg_val; reg_val = readl(SID_PRCTL); reg_val &= ~((0x1ff << 16) | 0x3); reg_val |= key_index << 16; writel(reg_val, SID_PRCTL); reg_val &= ~((0xff << 8) | 0x3); reg_val |= (SID_OP_LOCK << 8) | 0x2; writel(reg_val, SID_PRCTL); while (readl(SID_PRCTL) & 0x2) { ; } reg_val &= ~((0x1ff << 16) | (0xff << 8) | 0x3); writel(reg_val, SID_PRCTL); reg_val = readl(SID_RDKEY); return reg_val; } uint sid_read_key(uint key_index) { return __sid_reg_read_key(key_index); } void sid_program_key(uint key_index, uint key_value) { uint reg_val; set_efuse_voltage(1900); #ifdef EFUSE_HV_SWITCH writel(1, EFUSE_HV_SWITCH); #endif writel(key_value, SID_PRKEY); reg_val = readl(SID_PRCTL); reg_val &= ~((0x1ff << 16) | 0x3); reg_val |= key_index << 16; writel(reg_val, SID_PRCTL); reg_val &= ~((0xff << 8) | 0x3); reg_val |= (SID_OP_LOCK << 8) | 0x1; writel(reg_val, SID_PRCTL); while (readl(SID_PRCTL) & 0x1) { ; } reg_val &= ~((0x1ff << 16) | (0xff << 8) | 0x3); writel(reg_val, SID_PRCTL); set_efuse_voltage(1800); #ifdef EFUSE_HV_SWITCH writel(0, EFUSE_HV_SWITCH); #endif return; } #define EFUSE_BURN_MAX_TRY_CNT 3 static int uni_burn_key(uint key_index, uint key_value) { uint key_burn_bitmask = ~(sid_read_key(key_index)) & key_value; int fail = 0; while (key_burn_bitmask) { sid_program_key(key_index, key_burn_bitmask); if (fail > EFUSE_BURN_MAX_TRY_CNT) { EFUSE_DBG("[efuse] warn: **uni_burn_key failed **"); return -1; } key_burn_bitmask &= (~(__sid_reg_read_key(key_index))); fail++; } return 0; } int sid_set_security_mode(void) { #ifdef EFUSE_LCJS #ifdef CONFIG_ARCH_SUN50IW1P1 return uni_burn_key(EFUSE_LCJS, ((0x1 << SECURE_BIT_OFFSET) | (0x1 << SBROM_ACCELERATION_ENABLE_BIT))); #else return uni_burn_key(EFUSE_LCJS, (0x1 << SECURE_BIT_OFFSET)); #endif #endif return 0; } int sid_probe_security_mode(void) { #ifdef EFUSE_LCJS return (sid_read_key(EFUSE_LCJS) >> SECURE_BIT_OFFSET) & 1; #else return 0; #endif } #ifdef SID_SECURE_MODE int sid_get_security_status(void) { return readl(SID_SECURE_MODE) & 0x1; } #else int sid_get_security_status(void) { return sid_probe_security_mode(); } #endif static void _set_cfg_flg(int efuse_cfg_base, int bit_offset) { uni_burn_key(efuse_cfg_base, (1 << bit_offset)); return; } static int _get_burned_flag(efuse_key_map_new_t *key_map) { if (key_map->burned_flg_offset < 0) { return 0; } else { return (sid_read_key(EFUSE_WRITE_PROTECT) >> (key_map->burned_flg_offset & EFUSE_BRUN_RD_OFFSET_MASK)) & 1; } } static int __sw_acl_ck(efuse_key_map_new_t *key_map, int burn) { if (key_map->sw_rule == EFUSE_PRIVATE) { EFUSE_DBG("\n[efuse]%s: PRIVATE\n", key_map->name); return EFUSE_ERR_PRIVATE; } if (burn == 0) { if (key_map->sw_rule == EFUSE_NACCESS) { EFUSE_DBG("\n[efuse]%s:NACCESS\n", key_map->name); return EFUSE_ERR_NO_ACCESS; } } else { /*If already burned:*/ if (_get_burned_flag(key_map)) { if ((key_map->sw_rule == EFUSE_NACCESS) || (key_map->sw_rule == EFUSE_RO)) { EFUSE_DBG("\n[efuse]%s: already burned\n", key_map->name); return EFUSE_ERR_ALREADY_BURNED; } } if (key_map->sw_rule == EFUSE_RW) { key_map->burned_flg_offset |= EFUSE_ACL_SET_BRUN_BIT; key_map->rd_fbd_offset |= EFUSE_ACL_SET_RD_FORBID_BIT; } } return 0; } /*Efuse access control rule check.*/ static int __efuse_acl_ck(efuse_key_map_new_t *key_map, int burn) { /*For normal solution only check EFUSE_PRIVATE,other will be seemed as EFUSE_RW */ if (sid_get_security_status() == 0) { if (key_map->sw_rule == EFUSE_PRIVATE) { return EFUSE_ERR_PRIVATE; } return 0; } int ret = __sw_acl_ck(key_map, burn); if (ret) { return ret; } if (burn) { if (_get_burned_flag(key_map)) { /*already burned*/ EFUSE_DBG("[efuse]%s:already burned\n", key_map->name); EFUSE_DBG("config reg:0x%x\n", sid_read_key(EFUSE_WRITE_PROTECT)); return EFUSE_ERR_ALREADY_BURNED; } } else { if ((key_map->rd_fbd_offset >= 0) && ((sid_read_key(EFUSE_READ_PROTECT) >> key_map->rd_fbd_offset) & 1)) { /*Read is not allowed because of the read forbidden bit was set*/ EFUSE_DBG("[efuse]%s forbid bit set\n", key_map->name); EFUSE_DBG("config reg:0x%x\n", sid_read_key(EFUSE_READ_PROTECT)); return EFUSE_ERR_READ_FORBID; } } return 0; } /*get a uint value from unsigned char *k_src*/ static unsigned int _get_uint_val(unsigned char *k_src) { unsigned int test = 0x12345678; if ((unsigned int)k_src & 0x3) { /*big endian*/ if (*(unsigned char *)(&test) == 0x12) { memcpy((void *)&test, (void *)k_src, 4); return test; } else { test = ((*(k_src + 3)) << 24) | ((*(k_src + 2)) << 16) | ((*(k_src + 1)) << 8) | (*k_src); return test; } } else { return *(unsigned int *)k_src; } } int sunxi_efuse_write(void *key_inf) { efuse_key_info_t *list = (efuse_key_info_t *)key_inf; unsigned char *k_src = NULL; unsigned int niddle = 0, tmp_data = 0, k_d_lft = 0; efuse_key_map_new_t *key_map = g_key_info; if (list == NULL) { EFUSE_DBG("[efuse] error: key_inf is null\n"); return EFUSE_ERR_ARG; } /* search key via name*/ for (; key_map->size != 0; key_map++) { if (!memcmp(list->name, key_map->name, strlen(key_map->name))) { EFUSE_DBG("key name = %s\n", key_map->name); EFUSE_DBG("key offset = 0x%x\n", key_map->offset); /* check if there is enough space to store the key*/ if ((key_map->size >> 3) < list->len) { EFUSE_DBG("key name = %s\n", key_map->name); EFUSE_DBG("[efuse] error: no enough space\ , dst size(%d), src size(%d)\n", key_map->size >> 3, list->len); return EFUSE_ERR_KEY_SIZE_TOO_BIG; } break; } } if (key_map->size == 0) { EFUSE_DBG("[sunxi_efuse_write] error: unknow key\n"); return EFUSE_ERR_KEY_NAME_WRONG; } int ret = __efuse_acl_ck(key_map, 1); if (ret) { EFUSE_DBG("[sunxi_efuse_write] error: NO ACCESS\n"); return ret; } EFUSE_DBG_DUMP(list->name, list->key_data, list->len, EFUSE_DUMP_LEN); niddle = key_map->offset; k_d_lft = list->len; k_src = list->key_data; while (k_d_lft >= 4) { tmp_data = _get_uint_val(k_src); EFUSE_DBG("offset:0x%x val:0x%x\n", niddle, tmp_data); if (tmp_data) { if (uni_burn_key(niddle, tmp_data)) { return EFUSE_ERR_BURN_TIMING; } } k_d_lft -= 4; niddle += 4; k_src += 4; } if (k_d_lft) { uint mask = (1UL << (k_d_lft << 3)) - 1; tmp_data = _get_uint_val(k_src); mask &= tmp_data; EFUSE_DBG("offset:0x%x val:0x%x\n", niddle, mask); if (mask) { if (uni_burn_key(niddle, mask)) { return EFUSE_ERR_BURN_TIMING; } } } /*Already burned bit: Set this bit to indicate it is already burned.*/ if ((key_map->burned_flg_offset >= 0) && (key_map->burned_flg_offset <= EFUSE_BRUN_RD_OFFSET_MASK) && sid_get_security_status()) { _set_cfg_flg(EFUSE_WRITE_PROTECT, key_map->burned_flg_offset); } /*Read forbidden bit: Set to indicate cpu can not access this key again.*/ if ((key_map->rd_fbd_offset >= 0) && (key_map->rd_fbd_offset <= EFUSE_BRUN_RD_OFFSET_MASK) && sid_get_security_status()) { _set_cfg_flg(EFUSE_READ_PROTECT, key_map->rd_fbd_offset); } return 0; } /*This API assume the caller already *prepared enough buffer to receive data. *Because the lenth of key is exported as MACRO*/ #define EFUSE_ROUND_UP(x, y) ((((x) + ((y)-1)) / (y)) * (y)) int sunxi_efuse_read(void *key_name, void *rd_buf, int *len) { efuse_key_map_new_t *key_map = g_key_info; uint tmp = 0, i = 0, k_u32_l = 0, bit_lft = 0; int offset = 0, tmp_sz = 0; __attribute__((unused)) int show_status = 0; unsigned int *u32_p = (unsigned int *)rd_buf; unsigned char *u8_p = (unsigned char *)rd_buf; *len = 0; if (!(key_name && rd_buf)) { EFUSE_DBG("[efuse] error arg check fail\n"); return EFUSE_ERR_ARG; } /* search key via name*/ for (; key_map->size != 0; key_map++) { if (!memcmp(key_name, key_map->name, strlen(key_map->name))) { break; } } if (key_map->size == 0) { EFUSE_DBG("[efuse] error: unknow key name\n"); return EFUSE_ERR_KEY_NAME_WRONG; } int ret = __efuse_acl_ck(key_map, 0); if (ret) { EFUSE_DBG("[sunxi_efuse_write] error: acl check fail\n"); return ret; } EFUSE_DBG("key name:%s key size:%d key offset:0x%X\n", key_map->name, key_map->size, key_map->offset); k_u32_l = key_map->size / 32; bit_lft = key_map->size % 32; offset = key_map->offset; for (i = 0; i < k_u32_l; i++) { tmp = sid_read_key(offset); if (((unsigned int)rd_buf & 0x3) == 0) { u32_p[i] = tmp; } else { memcpy((void *)(u8_p + i * 4), (void *)(&tmp), 4); } offset += 4; tmp_sz += 4; } if (bit_lft) { EFUSE_DBG("bit lft is %d\n", bit_lft); tmp = sid_read_key(offset); memcpy((void *)(u8_p + k_u32_l * 4), (void *)(&tmp), EFUSE_ROUND_UP(bit_lft, 8)); tmp_sz += EFUSE_ROUND_UP(bit_lft, 8); } *len = tmp_sz; return 0; } #ifndef SID_ROTPK_CTRL int sunxi_efuse_get_rotpk_status(void) { return -1; } #else int sunxi_efuse_get_rotpk_status(void) { int ret; ret = (readl(SID_ROTPK_CTRL) & (1 << SID_ROTPK_EFUSED_BIT)) == (1 << SID_ROTPK_EFUSED_BIT); return ret; } #endif