日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

uboot的环境变量

發布時間:2023/12/15 编程问答 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 uboot的环境变量 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
注:本文是學習朱老師課程整理的筆記,基于uboot-1.3.4和s5pc11x分析。

環境變量的作用

可以不用修改uboot的源代碼,而是通過修改環境變量來影響uboot運行時的一些數據和特性。譬如說通過修改bootdelay環境變量就可以更改系統開機自動啟動時倒數的秒數。

環境變量的優先級

如果環境變量為空則使用代碼中的值;如果環境變量不為空則優先使用環境變量對應的值。

譬如machid(機器碼)。uboot中在x210_sd.h中定義了一個機器碼2456,如果要修改uboot中配置的機器碼,可以修改x210_sd.h中的機器碼,但是修改源代碼后需要重新編譯燒錄,很麻煩;比較簡單的方法就是使用環境變量machid。如:set machid 0x998,有了machid環境變量后,系統啟動時會優先使用machid對應的環境變量,這就是優先級問題。

環境變量的工作方式

默認環境變量,在uboot/common/env_common.c中default_environment:

uchar default_environment[CFG_ENV_SIZE] = {"bootargs=" CONFIG_BOOTARGS "\0""bootcmd=" CONFIG_BOOTCOMMAND "\0""mtdpart=" CONFIG_MTDPARTITION "\0""nfsboot=" CONFIG_NFSBOOTCOMMAND "\0""bootdelay=" MK_STR(CONFIG_BOOTDELAY) "\0""baudrate=" MK_STR(CONFIG_BAUDRATE) "\0""ethaddr=" MK_STR(CONFIG_ETHADDR) "\0""ipaddr=" MK_STR(CONFIG_IPADDR) "\0""serverip=" MK_STR(CONFIG_SERVERIP) "\0""gatewayip=" MK_STR(CONFIG_GATEWAYIP) "\0""netmask=" MK_STR(CONFIG_NETMASK) "\0""\0" };

環境變量在內存中的存儲大體如下:

這東西本質是一個字符數組,大小為CFG_ENV_SIZE(16kb),里面內容就是很多個環境變量連續分布組成的,每個環境變量最末端以’\0’結束。

SD卡中環境變量分區,在uboot的raw分區中。存儲時是把DDR中的環境變量整體的寫入SD卡中分區里。所以當我們saveenv時其實整個所有的環境變量都被保存了一遍,而不是只保存更改了的。

DDR中環境變量就是default_environment字符數組,在uboot中其實是一個全局變量,鏈接時在數據段,重定位時default_environment就被重定位到DDR中一個內存地址處了。

剛燒錄的SD卡中環境變量分區是空白的,uboot第一次運行時加載的是uboot代碼中自帶的一份環境變量,叫默認環境變量。我們在saveenv時DDR中的環境變量會被更新到SD卡中的環境變量中,就可以被保存下來,下次開機會將環境變量從SD卡中relocate到DDR中去。

default_environment中的內容雖然被uboot源代碼初始化為一定的值(這個值就是我們的默認環境變量),但是在uboot啟動的第二階段,env_relocate時代碼會去判斷SD卡中的env分區的crc是否通過。如果crc校驗通過說明SD卡中有正確的環境變量存儲,則relocate函數會從SD卡中讀取環境變量來覆蓋default_environment字符數組,從而每次開機可以保持上一次更改過的環境變量。

環境變量相關命令源碼解析

  • printenv
int do_printenv (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) {int i, j, k, nxt;int rcode = 0;if (argc == 1) { /* Print all env variables */for (i=0; env_get_char(i) != '\0'; i=nxt+1) {for (nxt=i; env_get_char(nxt) != '\0'; ++nxt);for (k=i; k<nxt; ++k)putc(env_get_char(k));putc ('\n');if (ctrlc()) {puts ("\n ** Abort\n");return 1;}}printf("\nEnvironment size: %d/%ld bytes\n",i, (ulong)ENV_SIZE);return 0;}for (i=1; i<argc; ++i) { /* print single env variables */char *name = argv[i];k = -1;for (j=0; env_get_char(j) != '\0'; j=nxt+1) {for (nxt=j; env_get_char(nxt) != '\0'; ++nxt);k = envmatch((uchar *)name, j);if (k < 0) {continue;}puts (name);putc ('=');while (k < nxt)putc(env_get_char(k++));putc ('\n');break;}if (k < 0) {printf ("## Error: \"%s\" not defined\n", name);rcode ++;}}return rcode; }

找到printenv命令所對應的函數。通過printenv的help可以看出,這個命令有2種使用方法。第一種直接使用不加參數則打印所有的環境變量;第二種是printenv name則只打印出name這個環境變量的值。

do_printenv函數首先判斷argc是否等于1,若argc=1那么就循環打印所有的環境變量出來;如果argc不等于1,則后面的參數就是要打印的環境變量,給哪個環境變量就打印哪個。

argc=1時用雙重for循環來依次打印所有的環境變量。第一重for循環就是處理各個環境變量。所以有多少個環境變量則第一重就執行循環多少圈。

env_get_char函數中又調用了 env_get_char_memory:

uchar env_get_char_memory (int index) {if (gd->env_valid) {return ( *((uchar *)(gd->env_addr + index)) );} else {return ( default_environment[index] );} }

上面兩條return的語句其實可以相等。
在env_init函數中可以看出:

gd->env_addr = (ulong)&default_environment[0]; gd->env_valid = 1;

總結:這個函數要看懂,首先要明白整個環境變量在內存中如何存儲的。

  • setenv
    命令定義對應的函數在uboot/common/cmd_nvedit.c中,對應的函數為do_setenv。
int do_setenv (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) {if (argc < 2) {printf ("Usage:\n%s\n", cmdtp->usage);return 1;}return _do_setenv (flag, argc, argv); }

do_setenv中又調用了_do_setenv,_do_setenv的思路就是:先去DDR中的環境變量處尋找原來有沒有這個環境變量,如果原來就有則需要覆蓋原來的環境變量,如果原來沒有則在最后新增一個環境變量即可。

第1步:遍歷DDR中環境變量的數組,找到原來就有的那個環境變量對應的地址。

/** 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;}

第2步:擦除原來的環境變量
第3步:寫入新的環境變量

if (*++nxt == '\0') { /* 擦除原來的環境變量 */if (env > env_data) {env--;} else {*env = '\0';}} else { for (;;) { /* 寫入新的環境變量 */*env = *nxt++;if ((*env == '\0') && (*nxt == '\0'))break;++env;}}*++env = '\0';}

本來setenv做完上面的就完了,但是還要考慮一些附加的問題。
問題一:環境變量太多超出DDR中的字符數組,溢出的解決方法。
問題二:有些環境變量如baudrate、ipaddr等,在gd中有對應的全局變量。這種環境變量在set更新的時候要同時去更新對應的全局變量,否則就會出現在本次運行中環境變量和全局變量不一致的情況。

  • saveenv

在uboot/common/cmd_nvedit.c中,對應函數為do_saveenv

int do_saveenv (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) {extern char * env_name_spec;printf ("Saving Environment to %s...\n", env_name_spec);return (saveenv() ? 1 : 0); }

從uboot實際執行saveenv命令的輸出,可以知道env_name_spec的定義在env_auto.c中。接著saveenv()就是定義在env_auto.c中:

int saveenv(void) {#if defined(CONFIG_S5PC100) || defined(CONFIG_S5PC110) || defined(CONFIG_S5P6442)if (INF_REG3_REG == 2)saveenv_nand();else if (INF_REG3_REG == 3)saveenv_movinand();else if (INF_REG3_REG == 1)saveenv_onenand();else if (INF_REG3_REG == 4)saveenv_nor();elseprintf("Unknown boot device\n");return 0; }

使用宏定義的方式去條件編譯了各種常見的flash芯片(如movinand、norflash、nand等)。然后在程序中讀取INF_REG(OMpin內部對應的寄存器)從而知道我們的啟動介質,然后調用這種啟動介質對應的函數來操作。這里我們的INF_REG3_REG =3,它的賦值在start.s中:

/* SD/MMC BOOT */cmp r2, #0xcmoveq r3, #BOOT_MMCSD /* #define BOOT_MMCSD 0x3 */……ldr r0, =INF_REG_BASEstr r3, [r0, #INF_REG3_OFFSET]

INF_REG3_REG 寄存器地址:E010F000+0C=E010_F00C,在芯片數據手冊中查到該寄存器是用戶自定義數據。我們在start.S中判斷啟動介質后將#BOOT_MMCSD(就是3,定義在x210_sd.h)寫入了這個寄存器,所以這里讀出的肯定是3,經過判斷就是movinand。所以實際執行的函數是:saveenv_movinand。

int saveenv_movinand(void) {movi_write_env(virt_to_phys((ulong)env_ptr));puts("done\n");return 1; }

真正執行保存環境變量操作的是:cpu/s5pc11x/movi.c中的movi_write_env函數,這個函數肯定是寫sd卡,將DDR中的環境變量數組(其實就是default_environment這個數組,大小16kb,剛好32個扇區)寫入iNand中的ENV分區中。

void movi_write_env(ulong addr) {movi_write(raw_area_control.image[2].start_blk,raw_area_control.image[2].used_blk, addr); }

raw_area_control是uboot中規劃iNnad/SD卡的原始分區表,這個里面記錄了我們對iNand的分區,env分區也在這里,下標是2。追到這一層就夠了,再里面就是調用驅動部分的寫SD卡/iNand的底層函數了。

  • getenv和getenv_r

getenv是不可重入函數(關于函數的可重入性分析見函數的可重入性理解)。實現方式就是去遍歷default_environment數組,挨個拿出所有的環境變量比對name,找到相等的直接返回這個環境變量的首地址即可。

getenv_r是可重入函數。getenv函數是直接返回這個找到的環境變量在DDR中環境變量處的地址,而getenv_r函數的做法是找到了DDR中環境變量地址后,將這個環境變量復制一份到提供的buf中,而不動原來DDR中環境變量。

所以差別就是:getenv中返回的地址只能讀不能隨便亂寫,而getenv_r中返回的環境變量是在自己提供的buf中,是可以隨便改寫加工的。兩者功能是一樣的,但是可重入版本會比較安全一些,建議使用。

總結

以上是生活随笔為你收集整理的uboot的环境变量的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。