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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

uboot的环境变量

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

環(huán)境變量的作用

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

環(huán)境變量的優(yōu)先級

如果環(huán)境變量為空則使用代碼中的值;如果環(huán)境變量不為空則優(yōu)先使用環(huán)境變量對應(yīng)的值。

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

環(huán)境變量的工作方式

默認環(huán)境變量,在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" };

環(huán)境變量在內(nèi)存中的存儲大體如下:

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

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

DDR中環(huán)境變量就是default_environment字符數(shù)組,在uboot中其實是一個全局變量,鏈接時在數(shù)據(jù)段,重定位時default_environment就被重定位到DDR中一個內(nèi)存地址處了。

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

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

環(huán)境變量相關(guān)命令源碼解析

  • 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命令所對應(yīng)的函數(shù)。通過printenv的help可以看出,這個命令有2種使用方法。第一種直接使用不加參數(shù)則打印所有的環(huán)境變量;第二種是printenv name則只打印出name這個環(huán)境變量的值。

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

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

env_get_char函數(shù)中又調(diào)用了 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函數(shù)中可以看出:

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

總結(jié):這個函數(shù)要看懂,首先要明白整個環(huán)境變量在內(nèi)存中如何存儲的。

  • setenv
    命令定義對應(yīng)的函數(shù)在uboot/common/cmd_nvedit.c中,對應(yīng)的函數(shù)為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中又調(diào)用了_do_setenv,_do_setenv的思路就是:先去DDR中的環(huán)境變量處尋找原來有沒有這個環(huán)境變量,如果原來就有則需要覆蓋原來的環(huán)境變量,如果原來沒有則在最后新增一個環(huán)境變量即可。

第1步:遍歷DDR中環(huán)境變量的數(shù)組,找到原來就有的那個環(huán)境變量對應(yīng)的地址。

/** 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步:擦除原來的環(huán)境變量
第3步:寫入新的環(huán)境變量

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

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

  • saveenv

在uboot/common/cmd_nvedit.c中,對應(yīng)函數(shù)為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實際執(zhí)行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內(nèi)部對應(yīng)的寄存器)從而知道我們的啟動介質(zhì),然后調(diào)用這種啟動介質(zhì)對應(yīng)的函數(shù)來操作。這里我們的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,在芯片數(shù)據(jù)手冊中查到該寄存器是用戶自定義數(shù)據(jù)。我們在start.S中判斷啟動介質(zhì)后將#BOOT_MMCSD(就是3,定義在x210_sd.h)寫入了這個寄存器,所以這里讀出的肯定是3,經(jīng)過判斷就是movinand。所以實際執(zhí)行的函數(shù)是:saveenv_movinand。

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

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

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中規(guī)劃iNnad/SD卡的原始分區(qū)表,這個里面記錄了我們對iNand的分區(qū),env分區(qū)也在這里,下標是2。追到這一層就夠了,再里面就是調(diào)用驅(qū)動部分的寫SD卡/iNand的底層函數(shù)了。

  • getenv和getenv_r

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

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

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

總結(jié)

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

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。