为了保护硬件flash上的数据,防止被HACKED,我们产品大都需要一个加密芯片。而海思本身就有OTP功能,我们完全可以把它利用起来。此外, 我们可以在OTP里保存一些特定的信息,例如设备信息、秘钥等信息。
目录
本文代码实现参考《Hi35xx Camera Soc 用户指南.pdf》
1 相关寄存器定义
主要有otp时钟和otp_ctrl寄存器组
#define HAL_SET_BIT(src, bit) ((src) |= (1<<bit))
#define HAL_CLEAR_BIT(src,bit) ((src) &= ~(1<<bit))
#define REG_SYS_OTP_CLK_ADDR_PHY (0x120101BC)
#define OTP_CRG_CLOCK_BIT (0x01 << 1)
#define OTP_REG_BASE_ADDR_PHY (0x100B0000)
#define OTP_USER_IF_BASE g_pOtpRegBase
#define OTP_USER_WORK_MODE (OTP_USER_IF_BASE+0x0000)
#define OTP_USER_OP_START (OTP_USER_IF_BASE+0x0004)
#define OTP_USER_KEY_INDEX (OTP_USER_IF_BASE+0x0008)
#define OTP_USER_KEY_DATA0 (OTP_USER_IF_BASE+0x000c)
#define OTP_USER_KEY_DATA1 (OTP_USER_IF_BASE+0x0010)
#define OTP_USER_KEY_DATA2 (OTP_USER_IF_BASE+0x0014)
#define OTP_USER_KEY_DATA3 (OTP_USER_IF_BASE+0x0018)
#define OTP_USER_KEY_DATA4 (OTP_USER_IF_BASE+0x001c)
#define OTP_USER_KEY_DATA5 (OTP_USER_IF_BASE+0x0020)
#define OTP_USER_KEY_DATA6 (OTP_USER_IF_BASE+0x0024)
#define OTP_USER_KEY_DATA7 (OTP_USER_IF_BASE+0x0028)
#define OTP_USER_KEY_DATA8 (OTP_USER_IF_BASE+0x002c)
#define OTP_USER_FLAG_VALUE (OTP_USER_IF_BASE+0x0030)
#define OTP_USER_FLAG_INDEX (OTP_USER_IF_BASE+0x0034)
#define OTP_USER_REV_ADDR (OTP_USER_IF_BASE+0x0038)
#define OTP_USER_REV_WDATA (OTP_USER_IF_BASE+0x003c)
#define OTP_USER_REV_RDATA (OTP_USER_IF_BASE+0x0040)
#define OTP_USER_LOCK_STA0 (OTP_USER_IF_BASE+0x0044)
#define OTP_USER_LOCK_STA1 (OTP_USER_IF_BASE+0x0048)
#define OTP_USER_CTRL_STA (OTP_USER_IF_BASE+0x004c)
2 寄存器读写函数
#define PAGE_SIZE_MASK (~(0xfff))
#define PAGE_SIZE 0x1000
#define HAL_CIPHER_ReadReg(addr, result) (*(result) = *(volatile unsigned int *)(addr))
#define HAL_CIPHER_WriteReg(addr,result) (*(volatile unsigned int *)(addr) = (result))
HI_VOID *g_pOtpRegBase = NULL;
static const char dev[]="/dev/mem";
HI_VOID * COMM_MMAP(HI_U32 u32RetAddr)
{
HI_S32 fd = open (dev, O_RDWR | O_SYNC);
if (fd < 0)
{
printf("open %s error!\n", dev);
return NULL;
}
/* addr align in page_size(4K) */
HI_U32 phy_addr_in_page;
HI_U32 page_diff;
phy_addr_in_page = u32RetAddr & PAGE_SIZE_MASK;
page_diff = u32RetAddr - phy_addr_in_page;
/* size in page_size */
HI_U32 size_in_page;
HI_U32 size = PAGE_SIZE;
size_in_page =((size + page_diff - 1) & PAGE_SIZE_MASK) + PAGE_SIZE;
HI_VOID *addr = mmap((void *)0, size_in_page, PROT_READ|PROT_WRITE, MAP_SHARED, fd, phy_addr_in_page);
if (addr == MAP_FAILED)
{
printf("mmap @ 0x%x error!\n", phy_addr_in_page);
close(fd);
return NULL;
}
return addr+page_diff;
}
HI_VOID COMM_MUNMAP(HI_U32 u32RegAddr)
{
munmap((HI_VOID*)u32RegAddr, PAGE_SIZE);
}
3 otp初始化实现
/* OTP init */
HI_S32 HI_OTPMNG_Init(HI_VOID)
{
HI_U32 CrgValue = 0;
HI_U32 *pu32SysAddr = HI_NULL;
pu32SysAddr = COMM_MMAP(REG_SYS_OTP_CLK_ADDR_PHY);
if (pu32SysAddr == HI_NULL)
{
printf("[ERROR] u32SysAddr ioremap with nocache failed!!\n");
return HI_FAILURE;
}
HAL_CIPHER_ReadReg(pu32SysAddr, &CrgValue);
//printf("clk 0x%x\n", CrgValue);
CrgValue |= OTP_CRG_CLOCK_BIT; /* set the bit 0, clock opened */
HAL_CIPHER_WriteReg(pu32SysAddr, CrgValue);
COMM_MUNMAP((HI_U32)pu32SysAddr);
g_pOtpRegBase = COMM_MMAP(OTP_REG_BASE_ADDR_PHY);
if (g_pOtpRegBase == HI_NULL)
{
printf("[ERROR] osal_ioremap_nocache for OTP failed!!\n");
return HI_FAILURE;
}
return HI_SUCCESS;
}
HI_VOID HI_OTPMNG_Uninit(HI_VOID)
{
if(g_pOtpRegBase)
{
COMM_MUNMAP((HI_U32)g_pOtpRegBase);
g_pOtpRegBase = NULL;
}
}
4 读取otp的lock状态
HI_S32 HI_OTPMNG_GetLockStat(HI_U32 *pu32LockSta)
{
if(HI_FAILURE == HI_OTPMNG_WaitFree())
{
return HI_FAILURE;
}
if(HI_OTPMNG_SetMode(OTP_READ_LOCK_STA_MODE))
{
return HI_FAILURE;
}
HI_OTPMNG_OP_Start();
if(HI_OTPMNG_Wait_OP_done())
{
return HI_FAILURE;
}
HAL_CIPHER_ReadReg(OTP_USER_LOCK_STA0, pu32LockSta);
return HI_SUCCESS;
}
5 加在otp的key到加密模块
HI_S32 HI_OTPMNG_LoadCipherKey(HI_U32 opt_id)
{
if(opt_id > OTP_USER_KEY3)
{
opt_id = OTP_USER_KEY0;
}
if(HI_FAILURE == HI_OTPMNG_WaitFree())
{
return HI_FAILURE;
}
HI_OTPMNG_CHOOSE_OTP_key(opt_id);
if(HI_OTPMNG_SetMode(OTP_LOCK_CIPHER_KEY_MODE))
{
return HI_FAILURE;
}
HI_OTPMNG_OP_Start();
if(HI_FAILURE == HI_OTPMNG_Wait_OP_done())
{
return HI_FAILURE;
}
return HI_SUCCESS;
}
6 烧写KEY、JTAGID、PASSWORD到OTP
HI_S32 HI_OTPMNG_WriteKey(HI_U32 enWhichKey, HI_U32 *pu32key, HI_U32 u32KeyArrayLen)
{
HI_U32 u32LockStatus;
HI_S32 i;
if(HI_OTPMNG_GetLockStat(&u32LockStatus))
{
return HI_FAILURE;
}
if(HI_OTPMNG_Is_Locked(enWhichKey,u32LockStatus))
{
return HI_FAILURE;
}
if(HI_OTPMNG_WaitFree())
{
return HI_FAILURE;
}
HI_OTPMNG_CHOOSE_OTP_key(enWhichKey);
for(i = 0; i < u32KeyArrayLen; i++)
{
HAL_CIPHER_WriteReg(OTP_USER_KEY_DATA0+i*4, pu32key[i]);
}
if(HI_OTPMNG_SetMode(OTP_WRITE_KEY_ID_OR_PASSWD_MODE))
{
return HI_FAILURE;
}
HI_OTPMNG_OP_Start();
return HI_OTPMNG_Wait_OP_done();
}
7 KEY、JTAGID、PASSWORD的CRC校验
HI_BOOL HI_OTPMNG_CRC16(HI_U32 enWhichKey, HI_U16 u16CRC)
{
if(HI_OTPMNG_WaitFree())
{
return HI_FALSE;
}
HI_OTPMNG_CHOOSE_OTP_key(enWhichKey);
if(HI_OTPMNG_SetMode(OTP_KEY_ID_OR_PASSWD_CRC_MODE))
{
return HI_FALSE;
}
HI_OTPMNG_OP_Start();
if(HI_OTPMNG_Wait_OP_done())
{
return HI_FALSE;
}
return HI_OTPMNG_Wait_Is_CRC_ok();
}
8 使能标志位烧写模式
我对这部分文档中的理解还没到位,暂不实现。
9 用户预留空间烧写模式
8Kbit大小供开发者使用,也就是1KBytes,足够用。1K分为256块,每块32bit,每块可独立烧写。
产品型号、sensor、厂家、主板等信息可以烧入此空间。
// return -1 failed, 0 success, 1 has locked
HI_S32 HI_OTPMNG_WriteUserData(HI_U32 u32UserRegIdx, HI_U32 u32UserRegData)
{
if(HI_OTPMNG_WaitFree())
{
return HI_FAILURE;
}
HAL_CIPHER_WriteReg(OTP_USER_REV_ADDR, u32UserRegIdx);
HAL_CIPHER_WriteReg(OTP_USER_REV_WDATA, u32UserRegData);
if(HI_OTPMNG_SetMode(OTP_WRITE_USER_ROOM_MODE))
{
return HI_FAILURE;
}
HI_OTPMNG_OP_Start();
if(HI_OTPMNG_Wait_OP_done())
{
return HI_FAILURE;
}
return HI_OTPMNG_Wait_Is_Userlocked();
}
10 用户预留空间回读模式
HI_S32 HI_OTPMNG_ReadUserData(HI_U32 u32UserRegIdx, HI_U32 *pu32UserRegData)
{
if(HI_OTPMNG_WaitFree())
{
return HI_FAILURE;
}
HAL_CIPHER_WriteReg(OTP_USER_REV_ADDR, u32UserRegIdx);
if(HI_OTPMNG_SetMode(OTP_Read_USER_ROOM_MODE))
{
return HI_FAILURE;
}
HI_OTPMNG_OP_Start();
if(HI_OTPMNG_Wait_OP_done())
{
return HI_FAILURE;
}
HAL_CIPHER_ReadReg(OTP_USER_REV_RDATA, pu32UserRegData);
return HI_SUCCESS;
}
11 样例及源码传送门
static HI_VOID VIO_TestOTP()
{
printf("=========VIO_TestOTP=============\n");
if(HI_OTPMNG_Init() == HI_SUCCESS)
{
// 注意用户空间有256个块,每块大小为32bit
// 每块只可以烧写一次,下次烧写会打印已锁定
HI_OTPMNG_WriteUserData(0, 0x12345678);
HI_S32 i;
HI_U32 u32UserRegData;
// 打印所有user
for(i = 0; i <= 255; i++)
{
HI_OTPMNG_ReadUserData(i, &u32UserRegData);
printf("\t %0*x: %0*x\n", 2, i, 8, u32UserRegData);
}
HI_OTPMNG_Uninit();
}
}