| /* | |
| * \file efuse_usr_space_api.c | |
| * \brief support read/write user space using keyname mode | |
| * mapping keyname to offset by looking /efusekey in dtb | |
| * | |
| * \version 1.0.0 | |
| * \date 15/07/14 | |
| * \author Sam.Wu <yihui.wu@amlgic.com> | |
| * | |
| * Copyright (c) 2015 Amlogic. All Rights Reserved. | |
| * | |
| */ | |
| #include <config.h> | |
| #include <common.h> | |
| #include <command.h> | |
| #include <malloc.h> | |
| #include <linux/ctype.h> | |
| #define EFUSE_DBG(fmt...) //printf("[EFUSE_DBG]"fmt) | |
| #define EFUSE_MSG(fmt...) printf("[EFUSE_MSG]"fmt) | |
| #define EFUSE_ERR(fmt...) printf("[EFUSE_ERR]f(%s)L%d:", __func__, __LINE__),printf(fmt) | |
| extern uint32_t efuse_get_max(void); | |
| extern int efuse_read_usr(char *buf, size_t count, loff_t *ppos); | |
| extern int efuse_write_usr(char *buf, size_t count, loff_t *ppos); | |
| struct efusekey_info{ | |
| char keyname[32]; | |
| unsigned offset; | |
| unsigned size; | |
| }; | |
| static struct _efuseCfgInf{ | |
| unsigned initMaigc;//magic to indicate whether inited | |
| unsigned totalCfgKeyNums; | |
| struct efusekey_info * pKeyInf; | |
| } | |
| _efuseKeyInfos = {.totalCfgKeyNums = 0, .pKeyInf = NULL}; | |
| int efuse_usr_api_init_dtb(const char* dt_addr) | |
| { | |
| int nodeoffset, poffset = 0; | |
| char propname[32]; | |
| const void* phandle; | |
| int ret; | |
| int index; | |
| uint32_t max_size; | |
| unsigned efusekeynum = 0; | |
| struct efusekey_info * efusekey_infos = NULL; | |
| ret = fdt_check_header(dt_addr); | |
| if (ret < 0) { | |
| EFUSE_ERR("fdt check failed [%s]\n", fdt_strerror(ret)); | |
| return __LINE__; | |
| } | |
| _efuseKeyInfos.initMaigc = 0; | |
| nodeoffset = fdt_path_offset(dt_addr, "/efusekey"); | |
| if (nodeoffset < 0) { | |
| EFUSE_ERR("not find /efusekey node [%s].\n", fdt_strerror(nodeoffset)); | |
| return __LINE__; | |
| } | |
| phandle = fdt_getprop(dt_addr, nodeoffset, "keynum", NULL); | |
| efusekeynum = be32_to_cpup((u32 *)phandle); | |
| EFUSE_MSG("keynum is %x\n", efusekeynum); | |
| if (efusekey_infos) free(efusekey_infos) ; | |
| efusekey_infos = (struct efusekey_info *)malloc(sizeof (struct efusekey_info) *efusekeynum); | |
| if (!efusekey_infos) { | |
| EFUSE_ERR("malloc err\n"); | |
| return __LINE__; | |
| } | |
| max_size = efuse_get_max(); | |
| for (index = 0; index < efusekeynum; index++) | |
| { | |
| struct efusekey_info* theKeyInf = efusekey_infos + index; | |
| sprintf(propname, "key%d", index); | |
| /* printf("%s: propname: %s\n",__func__,propname); */ | |
| phandle = fdt_getprop(dt_addr, nodeoffset, propname, NULL); | |
| if (!phandle) { | |
| EFUSE_ERR("don't find match %s\n", propname); | |
| goto err; | |
| } | |
| poffset = fdt_node_offset_by_phandle(dt_addr, | |
| be32_to_cpup((u32 *)phandle)); | |
| if (!poffset) { | |
| EFUSE_ERR("can't find device node for key[%s]\n", propname); | |
| goto err; | |
| } | |
| phandle = fdt_getprop(dt_addr, poffset, "keyname", NULL); | |
| if (!phandle) { | |
| EFUSE_ERR("Can't find keyname for key[%d]\n", index); | |
| goto err; | |
| } | |
| strcpy(theKeyInf->keyname, phandle); | |
| phandle = fdt_getprop(dt_addr, poffset, "offset", NULL); | |
| if (!phandle) { | |
| EFUSE_ERR("Can't find offset for key[%s]\n", theKeyInf->keyname); | |
| goto err; | |
| } | |
| theKeyInf->offset = be32_to_cpup((u32 *)phandle); | |
| phandle = fdt_getprop(dt_addr, poffset, "size", NULL); | |
| if (!phandle) { | |
| EFUSE_ERR("Can't find size for key[%s]\n", theKeyInf->keyname); | |
| goto err; | |
| } | |
| theKeyInf->size = be32_to_cpup((u32 *)phandle); | |
| EFUSE_DBG("key[%02d] name=%12s, offset=0x%04x, size=0x%04x\n", | |
| index, theKeyInf->keyname, theKeyInf->offset, theKeyInf->size); | |
| if (theKeyInf->offset + theKeyInf->size > max_size) { | |
| EFUSE_ERR("\n offset (0x%x) + size (0x%x) > max [0x%x]!\n", theKeyInf->offset, theKeyInf->size, max_size); | |
| return __LINE__; | |
| } | |
| } | |
| _efuseKeyInfos.totalCfgKeyNums = efusekeynum; | |
| _efuseKeyInfos.pKeyInf = efusekey_infos; | |
| EFUSE_DBG("%s success!\n", __func__); | |
| _efuseKeyInfos.initMaigc = 0xee; | |
| return 0; | |
| err: | |
| free(efusekey_infos); | |
| EFUSE_ERR("%s error!\n", __func__); | |
| return -1; | |
| } | |
| static int _get_cfg_key_inf_byname(const char* keyname, const struct efusekey_info ** pKeyInf) | |
| { | |
| int index = 0; | |
| struct efusekey_info* theKeyInf = _efuseKeyInfos.pKeyInf; | |
| int ret = 0; | |
| if (0xee != _efuseKeyInfos.initMaigc) { | |
| EFUSE_ERR("Pls init first.\n"); | |
| return __LINE__; | |
| } | |
| for (; index < _efuseKeyInfos.totalCfgKeyNums; ++index, ++theKeyInf) | |
| { | |
| const char* theKeyname = theKeyInf->keyname; | |
| ret = strcmp(theKeyname, keyname); | |
| if (ret) continue; | |
| *pKeyInf = theKeyInf; | |
| return 0; | |
| } | |
| EFUSE_ERR("efuse keyname(%s) not configured\n", keyname); | |
| return __LINE__;//Not found the matched name | |
| } | |
| int efuse_usr_api_get_cfg_key_size(const char* keyname, unsigned* pSz) | |
| { | |
| int ret = 0; | |
| const struct efusekey_info* theCfgKeyInf = NULL; | |
| ret = _get_cfg_key_inf_byname(keyname, &theCfgKeyInf); | |
| if (ret) { | |
| EFUSE_ERR("not name cfg in dts.\n"); | |
| return __LINE__; | |
| } | |
| *pSz = theCfgKeyInf->size; | |
| return 0; | |
| } | |
| int efuse_usr_api_write_key(const char* keyname, const void* keydata, const unsigned dataSz) | |
| { | |
| int ret = 0; | |
| const struct efusekey_info* theCfgKeyInf = NULL; | |
| ret = _get_cfg_key_inf_byname(keyname, &theCfgKeyInf); | |
| if (ret) { | |
| EFUSE_ERR("not name cfg in dts.\n"); | |
| return __LINE__; | |
| } | |
| if (dataSz != theCfgKeyInf->size) { | |
| EFUSE_ERR("dataSz 0x%x != cfg size 0x%x\n", dataSz, theCfgKeyInf->size); | |
| return __LINE__; | |
| } | |
| ret = efuse_write_usr((char*)keydata, theCfgKeyInf->size, (loff_t*)&theCfgKeyInf->offset); | |
| if (ret < 0) { | |
| EFUSE_ERR("error: efuse write fail.\n"); | |
| return __LINE__; | |
| } | |
| return 0; | |
| } | |
| //usrefuse read mac $loadaddr (size) | |
| int efuse_usr_api_read_key(const char* keyname, void* databuf, const unsigned bufSz) | |
| { | |
| int ret = 0; | |
| const struct efusekey_info* theCfgKeyInf = NULL; | |
| loff_t offset = 0; | |
| ret = _get_cfg_key_inf_byname(keyname, &theCfgKeyInf); | |
| if (ret) { | |
| EFUSE_ERR("not name cfg in dts.\n"); | |
| return __LINE__; | |
| } | |
| const unsigned cfgCnt = theCfgKeyInf->size; | |
| if (cfgCnt > bufSz && bufSz) { | |
| EFUSE_ERR("cfg size 0x%x > bufsz 0x%x\n", cfgCnt, bufSz); | |
| return __LINE__; | |
| } | |
| EFUSE_DBG("keyname=%s, databuf=%p, bufSz=%d, cfgCnt=%u\n", keyname, databuf, bufSz, cfgCnt); | |
| offset = theCfgKeyInf->offset; | |
| memset(databuf, cfgCnt, 0); | |
| ret = efuse_read_usr((char*)databuf, cfgCnt, &offset); | |
| if (ret == -1) { | |
| EFUSE_ERR("ERROR: efuse read user data fail!, size=%u, offset=%llu\n", cfgCnt, offset); | |
| return __LINE__; | |
| } | |
| if (ret != cfgCnt) { | |
| EFUSE_ERR("ERROR: read %d byte(s) not wanted %d byte(s) data\n", ret, cfgCnt); | |
| return __LINE__; | |
| } | |
| return 0; | |
| } | |
| static int hex_ascii_to_buf(const char* input, uint8_t* buf, const unsigned bufSz) | |
| { | |
| int ret = 0; | |
| const char* tmpStr = input; | |
| const unsigned inputLen = strlen(input); | |
| int i = 0; | |
| if (!inputLen) { | |
| EFUSE_ERR("err input len 0\n"); | |
| return __LINE__; | |
| } | |
| if ( ((inputLen>>1)<<1) != inputLen ) { | |
| EFUSE_ERR("inputLen %d not even\n", inputLen); | |
| return __LINE__; | |
| } | |
| if (bufSz * 2 > inputLen) { | |
| EFUSE_ERR("bufSz %d not enough\n", bufSz); | |
| return __LINE__; | |
| } | |
| for (tmpStr = input; *tmpStr; ++tmpStr) | |
| { | |
| char c = *tmpStr; | |
| ret = isxdigit(c); | |
| if (!ret) { | |
| EFUSE_ERR("input(%s) contain non xdigit, c=%c\n", input, c); | |
| return __LINE__; | |
| } | |
| } | |
| for (i = 0; i < inputLen; i += 2) | |
| { | |
| char tmpByte[8]; | |
| tmpByte[2] = '\0'; | |
| tmpByte[0] = input[i]; | |
| tmpByte[1] = input[i + 1]; | |
| const unsigned val = simple_strtoul(tmpByte, NULL, 16); | |
| if (val > 0xff) { | |
| EFUSE_ERR("Exception: val 0x%x > 0xff\n", val); | |
| return __LINE__; | |
| } | |
| buf[i>>1] = val; | |
| } | |
| return 0; | |
| } | |
| static int do_usr_efuse_api(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) | |
| { | |
| int ret = 0; | |
| const char* subcmd = argv[1]; | |
| if (argc < 2) return CMD_RET_USAGE; | |
| if (!strcmp("init", subcmd)) | |
| { | |
| const char* dtbLoadAddr = NULL; | |
| if (argc > 2) | |
| { | |
| dtbLoadAddr = (char*)simple_strtoul(argv[2], NULL, 0); | |
| } | |
| else | |
| { | |
| dtbLoadAddr = getenv("dtb_mem_addr"); | |
| if (!dtbLoadAddr) { | |
| setenv("dtb_mem_addr", simple_itoa(CONFIG_SYS_SDRAM_BASE + (16U<<20))); | |
| } | |
| dtbLoadAddr = (char*)simple_strtoul(dtbLoadAddr, NULL, 0); | |
| } | |
| ret = efuse_usr_api_init_dtb(dtbLoadAddr); | |
| } | |
| else if(!strcmp("size", subcmd)) | |
| { | |
| const char* keyname = argv[2]; | |
| unsigned keysize = argc > 3 ? simple_strtoul(argv[3], NULL, 0) : 0; | |
| ret = efuse_usr_api_get_cfg_key_size(keyname, &keysize); | |
| EFUSE_MSG("keysize=%d\n", keysize); | |
| } | |
| else if(!strcmp("read", subcmd)) | |
| { | |
| if (argc < 4) return CMD_RET_USAGE; | |
| const char* keyname = argv[2]; | |
| char* databuf = (char*)simple_strtoul(argv[3], NULL, 16); | |
| unsigned bufSz = argc > 4 ? simple_strtoul(argv[4], NULL, 0) : 0; | |
| if (!bufSz) | |
| { | |
| ret = efuse_usr_api_get_cfg_key_size(keyname, &bufSz); | |
| if (ret) { | |
| EFUSE_ERR("Fail in get sz for key[%s]\n", keyname); | |
| return __LINE__; | |
| } | |
| } | |
| ret = efuse_usr_api_read_key(keyname, databuf, bufSz); | |
| if (!ret) | |
| { | |
| int i = 0; | |
| printf("efuse read data"); | |
| for (i = 0; i < bufSz; i++) { | |
| if (i%8 == 0) printf("\n[0x%02x]:", i); | |
| printf("%02x ", databuf[i]); | |
| } | |
| printf("\n"); | |
| } | |
| } | |
| else if(!strcmp("write", subcmd)) | |
| { | |
| if (argc < 4) return CMD_RET_USAGE; | |
| const char* keyname = argv[2]; | |
| char* keydata = (char*)simple_strtoul(argv[3], NULL, 16); | |
| unsigned bufSz = argc > 4 ? simple_strtoul(argv[4], NULL, 0) : 0; | |
| uint8_t* tmpBuf = NULL; | |
| if (!bufSz) | |
| { | |
| const char* input = argv[3]; | |
| bufSz = ( strlen(input) >> 1 ); | |
| tmpBuf = malloc(bufSz); | |
| ret = hex_ascii_to_buf(input, tmpBuf, bufSz); | |
| if (ret) { | |
| EFUSE_ERR("Failed in change hex ascii to buf\n"); | |
| return __LINE__; | |
| } | |
| keydata = (char*)tmpBuf; | |
| } | |
| ret = efuse_usr_api_write_key(keyname, keydata, bufSz); | |
| if (tmpBuf) free(tmpBuf) ; | |
| } | |
| return ret ? CMD_RET_FAILURE : CMD_RET_SUCCESS; | |
| } | |
| U_BOOT_CMD( | |
| efuse_user, | |
| 5, //max argc | |
| 1, //repeatable | |
| do_usr_efuse_api, | |
| "efuse user space read write ops", | |
| "init <dtbAddr> --- init efuse user space\n" | |
| "size keyname <addr> --- get key size configured in dts\n" | |
| "read keyname addr <size> --- read key value to mem addr \n" | |
| "write --- write key value\n" | |
| " efuse_user write keyname hexstring --- write key value in hex string\n" | |
| " efuse_user write keyname addr size --- write key value, U need load you key value into mem addr first\n" | |
| ); | |