uboot的环境变量
環(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
找到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ù)中可以看出:
總結(jié):這個函數(shù)要看懂,首先要明白整個環(huán)境變量在內(nèi)存中如何存儲的。
- setenv
命令定義對應(yīng)的函數(shù)在uboot/common/cmd_nvedit.c中,對應(yīng)的函數(shù)為do_setenv。
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)境變量
本來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)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python 利用opencv给白底照片
- 下一篇: 错误集(大概会持续更新)