// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (c) 2019 Amlogic, Inc. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_AMLOGIC_ENV_ENCRYPT #include #endif static const char* const temp_for_compile[] = {"__test1","__test2","__test3",NULL}; extern const char * const _env_args_reserve_[0] __attribute__((weak, alias("temp_for_compile"))); extern const char * const _aml_env_reserv_array[0] __attribute__((weak, alias("temp_for_compile"))); extern const char * const _aml_env_reserv_array1[0] __attribute__((weak, alias("temp_for_compile"))); extern const char * const _board_env_reserv_array0[0] __attribute__((weak, alias("temp_for_compile"))); extern const char * const _board_env_reserv_array1[0] __attribute__((weak, alias("temp_for_compile"))); extern const char * const _board_env_reserv_array2[0] __attribute__((weak, alias("temp_for_compile"))); #define debugP(fmt...) //printf("dbg[ENV]" fmt) #define errorP(fmt...) do {printf("ERR[ENV]L%d:", __LINE__); printf(fmt); } while (0) #define wrnP(fmt...) printf("WRN[ENV]" fmt) #define MsgP(fmt...) printf("MSG[ENV]" fmt) enum DEF_ENV_RESERV_ARRAY { BIT_ENV_RESERV_ARRAY_AML_COMMON, //-c, common aml env array_aml_env_reserv_array BIT_ENV_RESERV_ARRAY_AML_COMMON1, //-c0, common aml env array_aml_env_reserv_array BIT_ENV_RESERV_ARRAY_USER_INPUT, //input env1/env2/env3... BIT_ENV_RESERV_ARRAY_BOARD_DEFINE0, //-b0, board env array _board_env_reserv_array0 BIT_ENV_RESERV_ARRAY_BOARD_DEFINE1, //-b1, board env array _board_env_reserv_array1 BIT_ENV_RESERV_ARRAY_BOARD_DEFINE2, //-b2, board env array _board_env_reserv_array2 DEF_ENV_RESERV_ARRAY_COUNT, }; static int _reserve_env_list_after_defenv(const int reservNum, const char* const reservNameList[]) { int ret = 0; int index = 0; unsigned sumOfEnvVal = 0;//sum of strlen(getenv(env_i)) const int MaxReservNum = CONFIG_SYS_MAXARGS - 1; const char *valListBuf[MaxReservNum];//store at most 64 envs char *tmpEnvBuf = NULL; if (reservNum > MaxReservNum) { errorP("max reserved env list num %d < wanted %d\n", MaxReservNum, reservNum); return __LINE__; } //1, cal the total buf size needed to save the envs for (index = 0; index < reservNum; ++index) { const char *cfgEnvKey = reservNameList[index]; const char *cfgEnvVal = env_get(cfgEnvKey); MsgP("to rsv %s=%s\n", cfgEnvKey, cfgEnvVal ? cfgEnvVal : "null"); if (cfgEnvVal) sumOfEnvVal += strlen(cfgEnvVal) + 1; valListBuf[index] = cfgEnvVal; } //2, transfer the env values to buffer if (sumOfEnvVal) { tmpEnvBuf = (char *)malloc(sumOfEnvVal); if (!tmpEnvBuf) { errorP("Fail in malloc(%d)\n", sumOfEnvVal); return __LINE__; } memset(tmpEnvBuf, 0, sumOfEnvVal); char *tmpbuf = tmpEnvBuf; for (index = 0; index < reservNum; ++index) { const char *valBeforeDef = valListBuf[index]; if (!valBeforeDef) continue; const unsigned thisValLen = strlen(valBeforeDef) + 1; memcpy(tmpbuf, valBeforeDef, thisValLen); valListBuf[index] = tmpbuf; tmpbuf += thisValLen; debugP("tmpEnvBuf=%p, tmpbuf=%p, thisValLen=%d\n", tmpEnvBuf, tmpbuf, thisValLen); debugP("cp:k[%s]%s-->%s\n", reservNameList[index], valBeforeDef, tmpEnvBuf); } } set_default_env("## defenv_reserve ##", 0); if (sumOfEnvVal) { for (index = 0; index < reservNum; ++index) { const char *cfgEnvKey = reservNameList[index]; const char *valAftDef = valListBuf[index]; if (valAftDef) { env_set(cfgEnvKey, valAftDef); debugP("set[%s=%s]\n", cfgEnvKey, valAftDef); } } } if (tmpEnvBuf) free(tmpEnvBuf); return ret; } static int _do_defenv_reserv(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { int envListNum = argc - 1; const char **envListArr = (const char **)(argv + 1); unsigned int rsv_flags = 0; const int MAX_RSV_NUM = CONFIG_SYS_MAXARGS - 1; const char *rsv_env_list[MAX_RSV_NUM];//store at most 64 envs int rsv_env_num = 0; int ret = 0, i = 0, k = 0; const char *tmp = NULL; if (!envListNum) { envListArr = (const char **)_env_args_reserve_; const char **pArr = (const char **)envListArr; while (*pArr++) ++envListNum; } else if (*argv[1] == '-') { char *arg = NULL; const char * const*_aml_env_reserv_[DEF_ENV_RESERV_ARRAY_COUNT]; _aml_env_reserv_[BIT_ENV_RESERV_ARRAY_AML_COMMON] = _aml_env_reserv_array; _aml_env_reserv_[BIT_ENV_RESERV_ARRAY_AML_COMMON1] = _aml_env_reserv_array1; _aml_env_reserv_[BIT_ENV_RESERV_ARRAY_USER_INPUT] = temp_for_compile; _aml_env_reserv_[BIT_ENV_RESERV_ARRAY_BOARD_DEFINE0] = _board_env_reserv_array0; _aml_env_reserv_[BIT_ENV_RESERV_ARRAY_BOARD_DEFINE1] = _board_env_reserv_array1; _aml_env_reserv_[BIT_ENV_RESERV_ARRAY_BOARD_DEFINE2] = _board_env_reserv_array2; debugP("argc %d\n", argc); while (argc > 1 && **(argv + 1) == '-') { arg = *++argv; --argc; if (*++arg) { switch (*arg) { case 'b':{//board env const int bd = *++arg - '0'; if (bd > 2 || bd < 0) { errorP("Invalid option -%s\n", arg - 1); return CMD_RET_USAGE; } debugP("board reserv _board_env_reserv_array%d\n", bd); rsv_flags |= 1 << (BIT_ENV_RESERV_ARRAY_BOARD_DEFINE0 + bd); } break; case 'c':{//common env int icmn = arg[1]; if (icmn > '0') { errorP("invalid cmn para %s\n", arg); return CMD_RET_FAILURE; } if (icmn) icmn = 1; MsgP("common reserv para: %s\n", arg); rsv_flags |= 1 << (BIT_ENV_RESERV_ARRAY_AML_COMMON + icmn); } break; default: errorP("Invalid para -%s\n", arg); return CMD_RET_USAGE; } } } MsgP("argc %d, rsv_flags 0x%x\n", argc, rsv_flags); if (argc > 1) { MsgP("rsv usr env: "); for (i = 1; i < argc; ++i) { printf("%s, ", argv[i]); rsv_env_list[i - 1] = argv[i]; } rsv_env_num = argc - 1; printf("\n"); } for (i = 0; i < DEF_ENV_RESERV_ARRAY_COUNT; ++i) { if (!(rsv_flags & (1 << i))) continue; debugP("rsv %d\n", i); envListArr = (const char **)_aml_env_reserv_[i]; for (k = 0, tmp = envListArr[k]; tmp && *tmp; tmp = envListArr[++k]) { debugP("rsv[%d][%d] = %s\n", i, k, tmp); if (rsv_env_num >= MAX_RSV_NUM) { errorP("too long rsv num %d, max %d\n", rsv_env_num, MAX_RSV_NUM - 1); return CMD_RET_FAILURE; } rsv_env_list[rsv_env_num++] = tmp; } } debugP("rsv_env_num %d\n", rsv_env_num); rsv_env_list[rsv_env_num] = NULL; envListNum = rsv_env_num; envListArr = rsv_env_list; } ret = _reserve_env_list_after_defenv(envListNum, envListArr); return ret ? CMD_RET_FAILURE : CMD_RET_SUCCESS; } U_BOOT_CMD(_aml_defenv_reserve, //command name CONFIG_SYS_MAXARGS, //maxargs 0, //repeatable _do_defenv_reserv, //command function "reserve some specified envs after defaulting env", //description " see help defenv_reserve for usage\n" //usage ); static int do_defenv_reserv(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { if (argc > 1) return _do_defenv_reserv(cmdtp, flag, argc, argv); //no para return run_command("echo defenv_para $defenv_para;" "_aml_defenv_reserve ${defenv_para}", flag); } U_BOOT_CMD_COMPLETE(defenv_reserve, //command name CONFIG_SYS_MAXARGS, //maxargs 0, //repeatable do_defenv_reserv, //command function "reserve some specified envs after defaulting env", //description " argv: defenv_reserve <-c> <-b1> <-b2> <-b3> \n" //usage " - e.g. \n" " defenv_reserve :\n" //usage " old mode, NO env list , reserv cfg array '_env_args_reserve_' in .c\n" " defenv_reserve reserv_en0, reserv_env1, ...\n" //usage " old mode, reserve user env list\n" //usage " defenv_reserve <-c> <-b1> <-b2> <-b3> ...\n" //usage " new mode, default all env except \n" " -c, amlogic Common env array _aml_env_reserv_array\n" " -b0, .c env array _board_env_reserv_array0\n" " -b1, .c env array _board_env_reserv_array1\n" " -b2, .c env array _board_env_reserv_array2\n" " \n", var_complete ); /* * update_env_part, update env in flash * usage: update_env_part env1 env2 env3 ... * Just add/update/delete env in flash, not replace all env like saveenv * updaete include any of add/update/delete * Reasons to replace saveenv with update_env_part * >>Usually only save env u need, not include others like bootdelay * >>Most cases, we need update env iff changed, and need speed up as save env to flash cost time * todo: check not arg duplicated in argv */ #if CONFIG_IS_ENABLED(AML_UPDATE_ENV) static int do_update_env_part(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { int i = 0; int ret = CMD_RET_SUCCESS; int silent = 0;//don't tell empty env, 0 will print which env isnot exist int force = 0;//when 1, even no all env list exist will update env part int print = 0;//print env name/value after update env part const int BUF_SZ = CONFIG_ENV_SIZE * 2; char *env_part_buf = NULL; char *new_env_buf = NULL; env_t *ep = NULL; unsigned char *pdata = NULL; uint32_t crc; uint32_t n_env_in_flash = 0;//0 if all env not in flash, argc -1 if all env in flash int need_update_env = 0; const char *env_end = NULL;//current last kv's \0 unsigned long env_len = 0; unsigned int save_buf = 0;//1 if need save new_env_buf if (argc < 2) { MsgP("Need at least one env specify to update\n"); return CMD_RET_USAGE; } while (argc > 1 && **(argv + 1) == '-') { char *arg = *++argv; --argc; while (*++arg) { switch (*arg) { case 'p':/* print */ print = true; break; case 's':/* silent */ silent = true; break; case 'f':/* force */ force = true; break; default: return CMD_RET_USAGE; } } } for (i = 1; i < argc; ++i) { if (env_get(argv[i])) { continue; } if (!force) {//force mode allow update env part even some env not exist MsgP("env %s NOT exist and not -f, so cannot update flash env\n", argv[i]); return CMD_RET_FAILURE; } } env_part_buf = malloc(BUF_SZ); if (!env_part_buf) { errorP("Fail malloc buf sz 0x%x\n", BUF_SZ); return CMD_RET_FAILURE; } new_env_buf = env_part_buf + CONFIG_ENV_SIZE; //#1> record env list need to update debugP("argc %d, argv[1] %s\n", argc, argv[1]); env_set("_update_env_list", NULL); for (i = 1; i < argc; ++i) { env_set("_temp_env_", argv[i]); run_command("env set _update_env_list ${_update_env_list} ${_temp_env_}", 0); } env_set("_temp_env_", NULL); if (!silent) //print env list which updated in part env if not same MsgP("_update_env_list: %s\n", env_get("_update_env_list")); //#2> read env and check if valid if (store_get_type() == BOOT_NONE) { errorP("env_storage: must init before load\n"); ret = CMD_RET_FAILURE; goto _update_env_part_err; } if (store_rsv_read(RSV_ENV, BUF_SZ, env_part_buf)) { errorP("fail read env from storage\n"); ret = CMD_RET_FAILURE; goto _update_env_part_err; } else { ep = (env_t *)env_part_buf; pdata = ep->data; memcpy(&crc, &ep->crc, sizeof(crc)); #ifdef CONFIG_AMLOGIC_ENV_ENCRYPT if (crc32(0, ep->iv, ENV_SIZE) != crc) { #else if (crc32(0, ep->data, ENV_SIZE) != crc) { #endif errorP("bad CRC in storage, so directly save all env to storage\n"); ret = env_save(); goto _update_env_part_err; } #ifdef CONFIG_AMLOGIC_ENV_ENCRYPT /* Decrypt the env*/ ret = aml_env_aes_crypt(ep, 0); if (ret) { errorP("Failed to decrypt env, so directly save all env to storage\n"); ret = env_save(); goto _update_env_part_err; } #endif } //#3> parse storage env and check if need update, most cases true //compare if all env vars are same in flash //case 1: usr env and store env both exist, but value not same (include usr env val empty) //case 2: usr env not exist on store, but usr env val not empty if (!pdata) { errorP("err pdata\n"); ret = CMD_RET_FAILURE; goto _update_env_part_err; } else { const char *current_kv = (char *)pdata;//env1=val\0 unsigned int n_same_env = 0; const char *kvsep = "="; //3.1>get the true end postion for (env_end = (char *)pdata; env_end < (char *)pdata + sizeof(ep->data);) { const char *p = env_end + strnlen(env_end, (char *)pdata + sizeof(ep->data) - env_end); if (*p != '\0') { errorP("env need end with 0 but %c\n", *p); ret = CMD_RET_FAILURE; goto _update_env_part_err; } if (++p - (char *)pdata >= sizeof(ep->data)) { env_end = --p; break; } if (*p == '\0') { env_end = --p; break; } env_end = p; } env_len = env_end - (char *)pdata; if (*env_end || env_len < 1024) { errorP("too short env or env end %c err\n", *env_end); ret = CMD_RET_FAILURE; goto _update_env_part_err; } debugP("part env addr 0x%p, end 0x%p, sz %lx\n", env_part_buf, env_end, env_end - env_part_buf); //3.2> check if NOT need update flash, which is most used case for (; current_kv < env_end; current_kv += strlen(current_kv) + 1) { const char *s_env_v = strpbrk(current_kv, kvsep);//storage env value int sz_flash_env = 0; if (!s_env_v) { errorP("err env in storage, not k=v fmt\n%s\n", current_kv); ret = CMD_RET_FAILURE; goto _update_env_part_err; } ++s_env_v;//skip '=' sz_flash_env = s_env_v - current_kv - 1; //case 1, usr val == storage env val, skip //case 2, usr val empty, del it if exist in storage //case 2, usr val not empty, del it if exist in storage, append new to end for (i = 1; i < argc; ++i) { const char *usr_env = argv[i]; const char *usr_env_val = env_get(usr_env); const int sz_usr_env = strlen(usr_env); if (sz_flash_env != sz_usr_env) continue; if (strncmp(current_kv, usr_env, sz_usr_env)) continue; //Found key ++n_env_in_flash; if (!usr_env_val) {//need delete env in flash MsgP("store env %s need remove\n", usr_env); need_update_env = 1; break; } if (strcmp(usr_env_val, s_env_v)) {//need modify env in flash MsgP("store env %s need modify\n", usr_env); need_update_env = 1; break; } debugP("store env %s NOT need update\n", usr_env); ++n_same_env; } } if (!need_update_env) {//old user env not changed need_update_env = n_same_env < argc - 1; debugP("usr env num %d, not need update %d\n", argc - 1, n_same_env); } //if all env not in flash, not need update if all env not exist if (n_env_in_flash == 0) {//all env not in flash int all_env_empty = 1;//all env not exist in flash, and in memory for (i = 1; i < argc && all_env_empty; ++i) if (env_get(argv[i])) all_env_empty = 0; if (all_env_empty) if (!silent) MsgP("all env not exist in env and flash\n"); need_update_env = !all_env_empty; } } if (print) run_command("printenv ${_update_env_list}", 0); env_set("_update_env_list", NULL); if (!need_update_env) { MsgP("all env NOT need update\n"); ret = CMD_RET_SUCCESS; goto _update_env_part_err; } if (!n_env_in_flash) {//append all env to last as all not in env part char *new_end = (char *)env_end; unsigned int left_len = CONFIG_ENV_SIZE - env_len; for (i = 1; i < argc && left_len > 0; ++i) { char *usr_env = argv[i]; char *val = env_get(usr_env); int cp_len = 0; if (!val) continue; if (!silent) MsgP("append new env %s\n", usr_env); cp_len = strlcpy(++new_end, usr_env, left_len);//cp key new_end += cp_len, left_len -= cp_len; *new_end = '=', --left_len;//cpy '=' cp_len = strlcpy(++new_end, val, left_len) + 1;//cp val new_end += cp_len, left_len -= cp_len; } if (left_len == CONFIG_ENV_SIZE - env_len) { errorP("exception\n"); ret = CMD_RET_FAILURE; goto _update_env_part_err; } env_len = CONFIG_ENV_SIZE - left_len; env_end = env_part_buf + env_len; save_buf = 0; goto _update_env_save_; } else {//not all env in flash //copy flash env to new buf, and skip env need modify/delete const char *current_kv = (char *)pdata;//env1=val\0 const char *kvsep = "="; char *new_kv = new_env_buf + 4; //skip crc unsigned int new_env_len = 0; int env_need_update = 0; memset(new_env_buf, 0, CONFIG_ENV_SIZE); save_buf = 1; /* 1, if current k=v\0 not in usr input list, just copy it to new buffer * 2, else if in input list but no value, skip it * 3, else if value not change, just copy it to new buffer * 4, else if in the usr input list but value changed, use input key value instead */ for (; current_kv < env_end; current_kv += strlen(current_kv) + 1) { const char *s_env_v = strpbrk(current_kv, kvsep);//storage env value const char *next = NULL;//next k=v int s_env_v_len = 1; unsigned int kv_len = 0; int sz_flash_env = 0; if (!s_env_v) { errorP("err env in flash, not k=v fmt\n%s\n", current_kv); ret = CMD_RET_FAILURE; goto _update_env_part_err; } ++s_env_v;//skip '=' sz_flash_env = s_env_v - current_kv - 1; s_env_v_len = strnlen(s_env_v, env_end - s_env_v); next = s_env_v + s_env_v_len + 1;//next k=v\0 for (i = 1; i < argc; ++i) { const char *usr_env = argv[i]; int sz_usr_env = 0; if (!usr_env) continue;//disposed sz_usr_env = strlen(usr_env); if (sz_flash_env != sz_usr_env) continue; if (!strncmp(current_kv, usr_env, sz_usr_env)) break; } kv_len = (unsigned long)(next - current_kv); if (i == argc) {//this store env not in user input, so not need change memcpy(new_kv, current_kv, kv_len); new_kv += kv_len; new_env_len += kv_len; } else { const char *usr_env = argv[i]; const char *usr_env_val = env_get(usr_env); if (!usr_env_val) {//2, in the input list but no value MsgP("store env %s will removed\n", usr_env); env_need_update = 1; continue; } else if (!strcmp(usr_env_val, s_env_v)) {//in input && not change MsgP("store env %s not need changed\n", usr_env); memcpy(new_kv, current_kv, kv_len); new_kv += kv_len; new_env_len += kv_len; } else {//4, in the list and value changed unsigned int cp_len = kv_len - 1 - s_env_v_len; MsgP("store env %s DO need changed\n", usr_env); env_need_update = 1; memcpy(new_kv, current_kv, cp_len);//copy k= new_kv += cp_len; new_env_len += cp_len; cp_len = strlen(usr_env_val) + 1; memcpy(new_kv, usr_env_val, cp_len);//copy k= new_kv += cp_len; new_env_len += cp_len; } //argv[i] = NULL;//mark as disposed *((char **)argv + i) = NULL;//mark as disposed } } //end to traverse all flash env for (i = 1; i < argc; ++i) { const char *usr_env = argv[i]; char *usr_env_val = usr_env ? env_get(usr_env) : NULL; unsigned int left_len = 0; if (!usr_env) continue; if (!usr_env_val) { MsgP("new env %s NULL so skip\n", usr_env); } else { env_need_update = 1; left_len = CONFIG_ENV_SIZE - new_env_len - 1; snprintf(new_kv, left_len, "%s=%s", usr_env, usr_env_val); MsgP("new store env %s\n", new_kv); new_kv += strnlen(new_kv, left_len) + 1; } } if (!env_need_update) { MsgP("store env same, new env NULL, so not need update\n"); ret = CMD_RET_SUCCESS; goto _update_env_part_err; } } _update_env_save_: ep = (env_t *)(env_part_buf + save_buf * CONFIG_ENV_SIZE); #ifdef CONFIG_AMLOGIC_ENV_ENCRYPT /* Encrypt the env*/ ret = aml_env_aes_crypt(ep, 1); if (ret) { errorP("Fail to encrypt env\n"); ret = CMD_RET_FAILURE; goto _update_env_part_err; } #endif pdata = ep->data; debugP("crc before update 0x%x\n", ep->crc); //ep->crc = crc32(0, pdata, ENV_SIZE);//not work... #ifdef CONFIG_AMLOGIC_ENV_ENCRYPT crc = crc32(0, ep->iv, ENV_SIZE); #else crc = crc32(0, ep->data, ENV_SIZE); #endif memcpy(&ep->crc, &crc, sizeof(crc)); if (!silent) MsgP("new env part crc 0x%x\n", ep->crc); if (store_rsv_write(RSV_ENV, CONFIG_ENV_SIZE, ep)) { errorP("Fail to update env part\n"); ret = CMD_RET_FAILURE; goto _update_env_part_err; } debugP("ok update env addr 0x%p\n", env_part_buf); _update_env_part_err: free(env_part_buf); return ret; } U_BOOT_CMD_COMPLETE(update_env_part, //command name CONFIG_SYS_MAXARGS, //maxargs 0, //repeatable do_update_env_part, //command function "update env part with argv list", //usage " argv: update_env_part <-p> <-f> <-s> env0 env1 env2 ...\n" " -p print, will print env value\n" " -f force, will allow to update env part even some specified env vars not exist\n" " -s silent, least log if specify -s\n" " - e.g.\n" " update_env_part -p lock:\n" " update env var lock if it's diff in flash\n" " update_env_part -f -p lock lock1:\n" " update env var lock if it's diff and even lock1 is not exist\n", var_complete ); #endif //#if CONFIG_IS_ENABLED(AML_UPDATE_ENV)