uboot 環境變量實現原理:數組
首先咱們先要搞清楚uboot中環境變量的做用,uboot中環境變量的做用就是在不改變源碼、不用從新編譯的狀況下,能夠使咱們經過安全
設置環境變量的值來改變uboot的一些設置,如bootdelay時間、機器碼的值等等。ide
下面咱們來具體看一下uboot中環境變量如何實現函數
首先看一下環境變量的初始化函數:ui
env_init定義在commen/env_movi.c中 函數中實際執行的就是把default_environment的地址賦值給全局變量gd中的env_addr 和env_valid兩個值;this
int env_init(void) { #if defined(ENV_IS_EMBEDDED)
#else /* ENV_IS_EMBEDDED */ gd->env_addr = (ulong)&default_environment[0]; gd->env_valid = 1; #endif /* ENV_IS_EMBEDDED */
return (0); }
來看一下default_environment數組spa
在來看一下env_relocate 這個函數code
重點是一下兩句代碼blog
env_ptr = (env_t *)malloc (CFG_ENV_SIZE); 這句代碼做用是給uboot環境變量開闢一塊16k大小的內存ip
env_relocate_spec (); 這句代碼的做用是把sd卡中的uboot環境變量整個分區複製到開闢的這個內存地址處;
void env_relocate (void) { #ifdef ENV_IS_EMBEDDED #else
/* * We must allocate a buffer for the environment */ env_ptr = (env_t *)malloc (CFG_ENV_SIZE); DEBUGF ("%s[%d] malloced ENV at %p\n", __FUNCTION__,__LINE__,env_ptr); #endif
if (gd->env_valid == 0) { } else { env_relocate_spec (); } gd->env_addr = (ulong)&(env_ptr->data); }
經過movi_read_env函數把sd卡中的環境變量複製到內存中
主藥用到的是 movi_read_env(virt_to_phys((ulong)env_ptr)); 函數
crc32(0, env_ptr->data, ENV_SIZE) != env_ptr->crc
use_default()
三個函數: movi_read_env把sd卡中的uboot的環境變量分區複製到env_ptr中;
crc32 計算內存中環境變量的crc的值,若是不相等則執行
use_default函數
void env_relocate_spec (void) { #if !defined(ENV_IS_EMBEDDED)
uint *magic = (uint*)(PHYS_SDRAM_1); if ((0x24564236 != magic[0]) || (0x20764316 != magic[1])) movi_read_env(virt_to_phys((ulong)env_ptr)); if (crc32(0, env_ptr->data, ENV_SIZE) != env_ptr->crc) return use_default(); #endif /* ! ENV_IS_EMBEDDED */ }
下面看一下use_default函數的代碼:
1:輸出*** Warning - bad CRC or moviNAND, using default environment:
2:清0環境變量內存,把default_environment中的值複製到環境變量內存
3:計算crc,寫入內存中的crc位
4:設置gd中的env_valid爲1;
static void use_default() { puts ("*** Warning - bad CRC or moviNAND, using default environment\n\n"); if (default_environment_size > CFG_ENV_SIZE){ puts ("*** Error - default environment is too large\n\n"); return; } memset (env_ptr, 0, sizeof(env_t)); memcpy (env_ptr->data, default_environment, default_environment_size); env_ptr->crc = crc32(0, env_ptr->data, ENV_SIZE); gd->env_valid = 1; }
這樣環境變量初始化算是完成了;
咱們在回顧一下環境變量初始化都作了哪些工做:
1:在uboot尚未初始化flash設備(nand movinand 等flash)的時候,先進行環境變量初級初始化,即把gd全局變量中的 env_valid = 1; env_addr 等於全局變量default_enviroment數組的首地址
2:初始化完flash設置之後就要進行環境變量的重定位工做了,即把環境變量從flash中copy到內存中,用一個全局變量env_ptr指向這段內存;複製工做時經過movi_read_env這個函數實現的,實際就是把sd卡中
環境變量分區所有複製到env_ptr指向的這段內存中,而後在對這段內存中的環境變量進行crc校驗,若是失敗的話,則把default_enviroment中的環境變量複製到這裏;
(這裏對代碼分析env_relocate函數中用malloc設置了一段內存來存放環境變量,一直沒有用free來釋放這段內存,這裏是否存在安全隱患?)
------------------------------------------------------------------------------------------------------------
接下來咱們在看一下uboot中關於環境變量的一些命令
第一個 printenv 實現函數爲do_printfenv
代碼以下:
1 int do_printenv (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) 2 { 3 int i, j, k, nxt; 4 int rcode = 0; 5
6 if (argc == 1) { /* Print all env variables */
7 for (i=0; env_get_char(i) != '\0'; i=nxt+1) { 8 for (nxt=i; env_get_char(nxt) != '\0'; ++nxt) 9 ; 10 for (k=i; k<nxt; ++k) 11 putc(env_get_char(k)); 12 putc ('\n'); 13
14 if (ctrlc()) { 15 puts ("\n ** Abort\n"); 16 return 1; 17 } 18 } 19
20 printf("\nEnvironment size: %d/%ld bytes\n", 21 i, (ulong)ENV_SIZE); 22
23 return 0; 24 } 25
26 for (i=1; i<argc; ++i) { /* print single env variables */
27 char *name = argv[i]; 28
29 k = -1; 30
31 for (j=0; env_get_char(j) != '\0'; j=nxt+1) { 32
33 for (nxt=j; env_get_char(nxt) != '\0'; ++nxt) 34 ; 35 k = envmatch((uchar *)name, j); 36 if (k < 0) { 37 continue; 38 } 39 puts (name); 40 putc ('='); 41 while (k < nxt) 42 putc(env_get_char(k++)); 43 putc ('\n'); 44 break; 45 } 46 if (k < 0) { 47 printf ("## Error: \"%s\" not defined\n", name); 48 rcode ++; 49 } 50 } 51 return rcode; 52 }
uchar env_get_char_memory (int index) { if (gd->env_valid) { return ( *((uchar *)(gd->env_addr + index)) ); } else { return ( default_environment[index] ); } }
第二個命令:setenv命令對用的函數是do_setenv 命令,實際上調用的是_do_setenv函數
下面咱們來分析一下這個函數
int _do_setenv (int flag, int argc, char *argv[]) { int i, len, oldval; int console = -1; uchar *env, *nxt = NULL; char *name; bd_t *bd = gd->bd; uchar *env_data = env_get_addr(0); if (!env_data) /* need copy in RAM */
return 1; name = argv[1]; if (strchr(name, '=')) { printf ("## Error: illegal character '=' in variable name \"%s\"\n", name); return 1; } /* * search if variable with this name already exists */ oldval = -1; for (env=env_data; *env; env=nxt+1) { for (nxt=env; *nxt; ++nxt) ; if ((oldval = envmatch((uchar *)name, env-env_data)) >= 0) break; } /* * Delete any existing definition */
if (oldval >= 0) {
/* Check for console redirection */
if (strcmp(name,"stdin") == 0) { console = stdin; } else if (strcmp(name,"stdout") == 0) { console = stdout; } else if (strcmp(name,"stderr") == 0) { console = stderr; } if (console != -1) { if (argc < 3) { /* Cannot delete it! */ printf("Can't delete \"%s\"\n", name); return 1; } /* Try assigning specified device */
if (console_assign (console, argv[2]) < 0) return 1; #ifdef CONFIG_SERIAL_MULTI if (serial_assign (argv[2]) < 0) return 1; #endif } /* * Switch to new baudrate if new baudrate is supported */
if (strcmp(argv[1],"baudrate") == 0) { int baudrate = simple_strtoul(argv[2], NULL, 10); int i; for (i=0; i<N_BAUDRATES; ++i) { if (baudrate == baudrate_table[i]) break; } if (i == N_BAUDRATES) { printf ("## Baudrate %d bps not supported\n", baudrate); return 1; } printf ("## Switch baudrate to %d bps and press ENTER ...\n", baudrate); udelay(50000); gd->baudrate = baudrate; serial_setbrg (); udelay(50000); for (;;) { if (getc() == '\r') break; } } if (*++nxt == '\0') { if (env > env_data) { env--; } else { *env = '\0'; } } else { for (;;) { *env = *nxt++; if ((*env == '\0') && (*nxt == '\0')) break; ++env; } } *++env = '\0'; } #ifdef CONFIG_NET_MULTI if (strncmp(name, "eth", 3) == 0) { char *end; int num = simple_strtoul(name+3, &end, 10); if (strcmp(end, "addr") == 0) { eth_set_enetaddr(num, argv[2]); } } #endif
/* Delete only ? */
if ((argc < 3) || argv[2] == NULL) { env_crc_update (); return 0; } /* * Append new definition at the end */
for (env=env_data; *env || *(env+1); ++env) ; if (env > env_data) ++env; /* * Overflow when: * "name" + "=" + "val" +"\0\0" > ENV_SIZE - (env-env_data) */ len = strlen(name) + 2; /* add '=' for first arg, ' ' for all others */
for (i=2; i<argc; ++i) { len += strlen(argv[i]) + 1; } if (len > (&env_data[ENV_SIZE]-env)) { printf ("## Error: environment overflow, \"%s\" deleted\n", name); return 1; } while ((*env = *name++) != '\0') env++; for (i=2; i<argc; ++i) { char *val = argv[i]; *env = (i==2) ? '=' : ' '; while ((*++env = *val++) != '\0') ; } /* end is marked with double '\0' */
*++env = '\0'; /* Update CRC */ env_crc_update (); /* * Some variables should be updated when the corresponding * entry in the enviornment is changed */
if (strcmp(argv[1],"ethaddr") == 0) { char *s = argv[2]; /* always use only one arg */
char *e; for (i=0; i<6; ++i) { bd->bi_enetaddr[i] = s ? simple_strtoul(s, &e, 16) : 0; if (s) s = (*e) ? e+1 : e; } #ifdef CONFIG_NET_MULTI eth_set_enetaddr(0, argv[2]); #endif
return 0; } if (strcmp(argv[1],"ipaddr") == 0) { char *s = argv[2]; /* always use only one arg */
char *e; unsigned long addr; bd->bi_ip_addr = 0; for (addr=0, i=0; i<4; ++i) { ulong val = s ? simple_strtoul(s, &e, 10) : 0; addr <<= 8; addr |= (val & 0xFF); if (s) s = (*e) ? e+1 : e; } bd->bi_ip_addr = htonl(addr); return 0; } if (strcmp(argv[1],"loadaddr") == 0) { load_addr = simple_strtoul(argv[2], NULL, 16); return 0; } #if defined(CONFIG_CMD_NET)
if (strcmp(argv[1],"bootfile") == 0) { copy_filename (BootFile, argv[2], sizeof(BootFile)); return 0; } #endif #ifdef CONFIG_AMIGAONEG3SE if (strcmp(argv[1], "vga_fg_color") == 0 || strcmp(argv[1], "vga_bg_color") == 0 ) { extern void video_set_color(unsigned char attr); extern unsigned char video_get_attr(void); video_set_color(video_get_attr()); return 0; } #endif /* CONFIG_AMIGAONEG3SE */
return 0; }