/* * (C) Copyright 2000-2999 * Allwinner Technology Co., Ltd. * Author: qinjian * SPDX-License-Identifier: GPL-2.0+ */ #include "common.h" #include #include #ifdef CONFIG_ARCH_SUN8IW12P1 #define SECURE_BIT_OFFSET 31 #else #define SECURE_BIT_OFFSET 11 #endif /*Globel config area begin*/ #define EFUSE_DBG_E 0 #define NORMAL_IC_FOLLOW_SEC_RULE 0 /*Globel config area end*/ #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 EFUSE_BURN_MAX_TRY_CNT) { EFUSE_DBG("[efuse] %s %d fatal err: **uni_burn_key failed **", __FILE__,__LINE__); #if EFUSE_DBG_E g_err_flg++; #endif return -1; } key_burn_bitmask &= (~(__sid_reg_read_key(key_index))); fail++; } return 0; } void sid_set_security_mode(void) { #ifdef EFUSE_LCJS if(uni_burn_key(EFUSE_LCJS, (0x1 << SECURE_BIT_OFFSET))) { EFUSE_DBG("[efuse] %s %d fatal err: **sid_set_security_mode failed **", __FILE__,__LINE__); } #endif return; } /*This is an obseleted api*/ 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) { if(uni_burn_key(efuse_cfg_base,(1<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)) { EFUSE_DBG("\n[efuse]%s: already burned\n",key_map->name); EFUSE_DBG("config reg:0x%x\n",sid_read_key(EFUSE_WRITE_PROTECT)); if(key_map->sw_rule == EFUSE_NACCESS) return EFUSE_ERR_NO_ACCESS; return EFUSE_ERR_ALREADY_BURNED; } if(key_map->sw_rule == EFUSE_RW) { /*modify burned_flg_offset&&rd_fbd_offset in case of the config bits been burned*/ key_map->burned_flg_offset |= EFUSE_ACL_SET_BRUN_BIT; key_map->rd_fbd_offset |= EFUSE_ACL_SET_RD_FORBID_BIT; } else if(key_map->sw_rule == EFUSE_RO) { /*modify rd_fbd_offset in case of the config bit been burned*/ 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 */ #ifdef NORMAL_IC_FOLLOW_SEC_RULE #else if(sid_get_security_status() == 0) { if(key_map->sw_rule == EFUSE_PRIVATE) { return EFUSE_ERR_PRIVATE; } return 0; } #endif 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)||(list->len == 0)) { EFUSE_DBG("[efuse] error: key_inf is null or len is 0\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))) { /* 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] __efuse_acl_ck check failed\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) #ifndef NORMAL_IC_FOLLOW_SEC_RULE &&sid_get_security_status() #endif ) { _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) #ifndef NORMAL_IC_FOLLOW_SEC_RULE &&sid_get_security_status() #endif ) { _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) { 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; /*if rd_buf not aligned ,u32_p will not be accessed*/ unsigned int *u32_p = (unsigned int *)rd_buf; unsigned char *u8_p = (unsigned char *)rd_buf; 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:%d\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;isize != 0; key_map++) { printf("%s : [size:%d Byte][sw_rule:%d]\n", key_map->name,key_map->size/8,key_map->sw_rule); } }