summaryrefslogtreecommitdiff
path: root/common/env_otp.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/env_otp.c')
-rw-r--r--common/env_otp.c425
1 files changed, 425 insertions, 0 deletions
diff --git a/common/env_otp.c b/common/env_otp.c
new file mode 100644
index 0000000..98d7c25
--- /dev/null
+++ b/common/env_otp.c
@@ -0,0 +1,425 @@
+#include <common.h>
+#include <command.h>
+#include <environment.h>
+#include <serial.h>
+#include <linux/stddef.h>
+#include <asm/byteorder.h>
+#include <linux/aes.h>
+#include <linux/rsa/base64.h>
+#include <search.h>
+#include <errno.h>
+#include "../common/wmt_efuse/wmt_efuse.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define CIPHER_IV \
+{ \
+ 'W', 'O', 'N', 'D', \
+ 'E', 'R', 'M', 'E', \
+ 'D', 'I', 'A', 3, \
+ 7, 6, 'A', 'F' \
+}
+
+
+#define THE_KEY \
+{ \
+ 'W', 'M', 'T', 'K', \
+ 'E', 'Y', 1, 7, \
+ 4, 4, 0, 3, \
+ 7, 6, 'A', 'F' \
+}
+
+#define MAX_OTP_VAL_SIZE 260
+#define MAX_OTP_VAL_BASE64_SIZE 512
+#define MAX_NAME_SIZE (256)
+#define MAX_VALUE_SIZE (4*1024)
+
+extern env_t *env_ptr;
+extern env_t *flash_addr;
+
+extern env_t *env_ptr2 ;
+extern env_t *flash_addr2 ;
+extern struct hsearch_data env_htab ;
+
+int encdec(int mode, unsigned char *key, size_t keybytes, unsigned char * data, size_t databytes, unsigned char *output){
+ aes_context ctx;
+ unsigned char iv[16]=CIPHER_IV;
+
+ if(mode != AES_ENCRYPT && mode != AES_DECRYPT)
+ return -1;
+
+ if(mode == AES_ENCRYPT){
+ if(aes_setkey_enc( &ctx, key, keybytes * 8) != 0){
+ printf("invalid key size\n");
+ return -1;
+ }
+ }else{
+ if(aes_setkey_dec( &ctx, key, keybytes * 8) != 0){
+ printf("invalid key size\n");
+ return -1;
+ }
+ }
+
+ if(aes_crypt_cbc(&ctx, mode, databytes, iv, data, output) != 0){
+ return -1;
+ }
+
+ return 0;
+}
+
+//aes128
+int genkey(unsigned char key[16]){
+ //unsigned char *chipid = (unsigned char*)otp_getenv("otp.chipid");
+ unsigned char chipid[8]={0};
+ unsigned char chipidstr[17]={0};
+ unsigned char id[16]={0};
+ unsigned char thekey[16]=THE_KEY;
+ int i = 0;
+
+ efuse_read_otp(OTP_CPUID, chipid, sizeof(chipid));
+ for(i = 0; i < sizeof(chipid); i++){
+ sprintf(chipidstr+i*2, "%02X", chipid[i]);
+ }
+ printf("chipidstr is %s\n", chipidstr);
+
+ //if(chipid == NULL) return -1;
+
+ int len = strlen((char*)chipidstr);
+ if(len > sizeof(id)) len = sizeof(id);
+ strncpy((char*)id, (char*)chipidstr, len);
+ return encdec(AES_ENCRYPT, thekey, sizeof(thekey), id, sizeof(id), key);
+}
+
+
+int otp_sec_encode(uchar *in, int ilen, uchar *out, size_t *olen)
+{
+ //aes128
+ unsigned char key[16]={0};
+ unsigned char val[MAX_OTP_VAL_SIZE]={0};
+ unsigned char cypval[MAX_OTP_VAL_SIZE]={0};
+ unsigned char *p,*s ;
+ size_t vallen=0;
+
+ if(genkey(key) < 0){
+ printf("fail to gen key\n");
+ return 1;
+ }
+
+ if(ilen > MAX_OTP_VAL_SIZE-1){
+ printf("env value is too long\n");
+ return 1;
+ }
+
+ p = in;
+ s = val;
+ while((*s++ = *p++) != '\0')
+ ;
+
+ vallen = ilen;
+ //16 bytes aligned
+ vallen = ((vallen + 15) / 16) * 16;
+
+ printf("val is %s, vallen is %d\n",in, vallen);
+ if(encdec(AES_ENCRYPT, key, sizeof(key), val, vallen, cypval) < 0){
+ printf("encode value fail\n");
+ return 1;
+ }
+
+ if(base64_encode(out, olen, cypval, vallen) != 0){
+ printf("base64 encode fail\n");
+ return 1;
+ }
+
+ return 0;
+
+}
+
+int otp_sec_decode(const char *val, uchar * theval)
+{
+ if( val != NULL && theval != NULL){
+ unsigned char key[16]={0};
+ unsigned char cypval[MAX_OTP_VAL_SIZE]={0};
+ size_t vallen = MAX_OTP_VAL_SIZE;
+
+ if(base64_decode(cypval, &vallen, val, strlen(val)) != 0){
+ printf("base64 decode fail\n");
+ return -1;
+ }
+
+ if(genkey(key) < 0){
+ printf("fail to gen key\n");
+ return -1;
+ }
+
+ if(vallen % 16){
+ printf("invalid value len\n");
+ return -1;
+ }
+
+ if(encdec(AES_DECRYPT, key, sizeof(key), cypval, vallen, theval) < 0){
+ printf("encode value fail\n");
+ return -1;
+ }
+ return 0;
+ }
+ return -1;
+}
+
+static int write_env(int index, uchar* buf, int size)
+{
+ int rc;
+ ulong end_addr,flash_sect_addr;
+ ulong start,end;
+ int rcode = 0;
+
+ if(size > CFG_ENV_SIZE)
+ size = CFG_ENV_SIZE;
+
+ flash_sect_addr = (ulong)flash_addr;
+ end_addr =(flash_sect_addr+0x20000-1);
+
+ //protect 2 sectors
+ if (flash_sect_protect (0, flash_sect_addr, end_addr))
+ return 1;
+
+ start = (ulong)flash_addr+(index*CFG_ENV_SIZE);
+ end = (ulong)flash_addr+(index*CFG_ENV_SIZE)+CFG_ENV_SIZE-1;
+
+ printf ("Erasing env%d...",index+1);
+ if (flash_sect_erase (start, end)){
+ rcode = 1;
+ goto Done;
+ }
+
+ printf ("Writing to env%d... ",index+1);
+
+ if (rc = flash_write(buf,start, size))
+ {
+ flash_perror (rc);
+ rcode = 1;
+ }else{
+ printf("done\n");
+ }
+
+Done:
+ /* try to re-protect */
+ (void) flash_sect_protect (1, flash_sect_addr, end_addr);
+ return rcode;
+}
+
+static int is_persist(char *name)
+{
+ int i, len;
+ char *persistlist[]={"otp.", "ethaddr", "wmt.ethaddr.persist", "androidboot.serialno",
+ "btaddr", "wmt.btaddr.persist","pcba.serialno","serialnum","persist.", NULL};
+
+ for(i=0; persistlist[i] != NULL; i++){
+ len = strlen(persistlist[i]);
+ if(!strncmp(name, persistlist[i], len))
+ return 0;
+ }
+
+ return -1;
+}
+
+static int sync_persist_env(struct hsearch_data *htab, uchar *env2)
+{
+ int len,i;
+ int updated=0;
+ uchar name[MAX_NAME_SIZE]={0};
+ uchar *val=NULL,*valbuf=NULL;
+ uchar *s,*res;
+ env_t env_new;
+ ENTRY e, *ep;
+
+ if(!htab)
+ return 1;
+
+ valbuf = malloc(MAX_VALUE_SIZE);
+
+ for(s=env2; s < (env2+ENV_SIZE) && *s!='\0'; ){
+
+ if(is_persist(s) == 0){
+ i=0;
+ while(*s != '=' && *s != '\0' && i < (sizeof(name)-1))
+ name[i++] = *s++;
+
+ name[i] = '\0';
+
+ i=0;
+ s++;//skip '='
+ val = valbuf;
+ while(*s != '\0' )
+ val[i++] = *s++;
+
+ val[i] = '\0';
+ s++;
+ //printf("env2:%s=%s\n",name,val);
+
+ e.key = (char*)name;
+ e.data = (char*)NULL;
+ hsearch_r(e, FIND, &ep, htab);
+ //if(ep) printf("env1:%s-%s\n",ep->key,ep->data);
+ /* otp.xx exist in env2, but not exist in env1,copy it to env1 */
+ if(!ep){
+ e.key = (char*)name;
+ e.data = (char*)val;
+ printf("insert %s=%s to env1\n",e.key,e.data);
+ hsearch_r(e, ENTER, &ep, htab);
+ if (!ep) {
+ printf("## Error inserting \"%s\" variable\n",name);
+ free(valbuf);
+ return 1;
+ }
+ updated ++;
+ }
+ }
+ else{
+ len = strlen(s)+1;
+ s += len;
+ }
+ }
+
+ //printf("sync %d otps to env1\n",updated);
+ res = (char *)&(env_new.data);
+ len = hexport_r(htab, '\0', &res, ENV_SIZE, 0, NULL);
+ if (len < 0) {
+ printf("Cannot export environment\n");
+ free(valbuf);
+ return 1;
+ }
+ env_new.crc = crc32(0, env_new.data, ENV_SIZE);
+ write_env(0,(uchar*)&env_new, CFG_ENV_SIZE);
+ write_env(1,(uchar*)&env_new, CFG_ENV_SIZE);
+ free(valbuf);
+
+ return 0;
+}
+
+
+/* buf shloud to be clean*/
+static int cpyenv(uchar *buf, uchar *addr, int len)
+{
+ int blk=0;
+ uchar *src,*dest;
+
+ src = addr;
+ dest = buf;
+
+ memcpy(dest, src, 0x400);//1K
+ while( (dest[0x3fe]|dest[0x3ff]) != '\0' && blk++ < 63){
+ src += 0x400;
+ dest += 0x400;
+ memcpy(dest, src, 0x400);
+ }
+
+ return 0;
+}
+
+int save_env( int idx)
+{
+ int len;
+ uchar *res;
+ env_t env_new;
+
+ if (idx ==1||idx ==2){
+ res = (char *)&(env_new.data);
+ len = hexport_r(&env_htab, '\0', &res, ENV_SIZE, 0, NULL);
+ if (len < 0) {
+ printf("Cannot export environment\n");
+ return 1;
+ }
+ env_new.crc = crc32(0, env_new.data, ENV_SIZE);
+
+ write_env(idx-1, (uchar*)&env_new, CFG_ENV_SIZE);
+ }else{
+ uchar env2_buf[CFG_ENV_SIZE]={0};
+ cpyenv(env2_buf, (uchar*)env_ptr2, CFG_ENV_SIZE);
+ sync_persist_env(&env_htab,&env2_buf[4]);
+ }
+
+ return 0;
+}
+
+
+int esync(void)
+{
+ int ret;
+ u32 crc1,crc2;
+ uchar env1_buf[CFG_ENV_SIZE]={0};
+ uchar env2_buf[CFG_ENV_SIZE]={0};
+
+ /* sync SF env partitions */
+
+ cpyenv(env1_buf, (uchar*)env_ptr, CFG_ENV_SIZE);
+ cpyenv(env2_buf, (uchar*)env_ptr2, CFG_ENV_SIZE);
+
+ crc1 = crc32(0, &env1_buf[4], ENV_SIZE);
+ crc2 = crc32(0, &env2_buf[4], ENV_SIZE);
+
+ //printf("crc1:%08lx,%08lx; crc2:%08lx,%08lx\n", env_ptr->crc,crc1,env_ptr2->crc,crc2);
+ /* env1 and env2 ok,and env1==env2 */
+ if (crc1 == env_ptr->crc && crc2 == env_ptr2->crc && crc1 == crc2) {
+ printf("env1==env2\n");
+ }
+ /* env1 is invalid,env2 is ok */
+ else if(crc1 != env_ptr->crc && crc2 == env_ptr2->crc){
+ printf("env2->env1\n");
+ //memcpy(env_buf,env_ptr2,CFG_ENV_SIZE);
+ write_env(0, env2_buf, CFG_ENV_SIZE);
+ }
+ /* env2 is invalid, env1 is ok */
+ else if(crc2 != env_ptr2->crc && crc1 == env_ptr->crc){
+ printf("env1->env2\n");
+ //memcpy(env_buf,env_ptr,CFG_ENV_SIZE);
+ write_env(1, env1_buf, CFG_ENV_SIZE);
+ }
+ /* env2 env1 ok,but env1!=env2 */
+ else if(crc2 == env_ptr2->crc && crc1 == env_ptr->crc && crc1 != crc2){
+ printf("env1<-> env2\n");
+ struct hsearch_data env_htab1 ={0};
+ if (himport_r(&env_htab1, (char *)&env1_buf[4], ENV_SIZE, '\0', 0)==0) {
+ printf("env1 hash table error!\n");
+ return 1;
+ }
+ sync_persist_env(&env_htab1, &env2_buf[4]);
+ hdestroy_r(&env_htab1);
+ }
+ /* env2 is invalid, env1 is invalid */
+ else{
+ printf("crc1:%08lx,%08lx; crc2:%08lx,%08lx\n", env_ptr->crc,crc1,env_ptr2->crc,crc2);
+ printf("both env invalid\n");
+
+ return 1;
+ }
+
+ return 0;
+
+}
+
+int do_esync(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
+{
+ int i=0;
+
+ if(argc < 2){
+ esync();
+ }else{
+ i = simple_strtoul(argv[1], NULL, 10);
+ if(i==1){
+ /* save cache to env1 */
+ save_env(1);
+ }else if(i==2){
+ /* save cache to env2 */
+ save_env(2);
+ }else{
+ ;
+ }
+ }
+ return 0;
+}
+
+U_BOOT_CMD(
+ esync, 3, 1, do_esync,
+ "esync - sync uboot env\n",
+ "esync \n"
+);