天天看點

Uboot和系統移植(10)------- uboot環境變量的源碼分析一,uboot的環境變量基礎二,環境變量相關指令源碼解析三,uboot内部擷取環境變量

目錄

  • 一,uboot的環境變量基礎
  • 二,環境變量相關指令源碼解析
  • 三,uboot内部擷取環境變量

一,uboot的環境變量基礎

1、環境變量的作用

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

2、環境變量的優先級

(1)uboot代碼當中有一個值,環境變量中也有一個值。uboot程式實際運作時規則是:如果環境變量為空則使用代碼中的值;如果環境變量不為空則優先使用環境變量對應的值。

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

3、環境變量在uboot中工作方式

(1)預設環境變量,在uboot/common/env_common.c中default_environment,這東西本質是一個字元數組,大小為CFG_ENV_SIZE(16kb),裡面内容就是很多個環境變量連續分布組成的,每個環境變量最末端以’\0’結束。

Uboot和系統移植(10)------- uboot環境變量的源碼分析一,uboot的環境變量基礎二,環境變量相關指令源碼解析三,uboot内部擷取環境變量

(2)SD卡中環境變量分區,在uboot的raw分區中。SD卡中其實就是給了個分區,專門用來存儲而已。存儲時其實是把DDR中的環境變量整體的寫入SD卡中分區裡。是以當我們saveenv時其實整個所有的環境變量都被儲存了一遍,而不是隻儲存更改了的。

(3)DDR中環境變量,在default_environment中,實質是字元數組。在uboot中其實是一個全局變量,連結時在資料段,重定位時default_environment就被重定位到DDR中一個記憶體位址處了。這個位址處的這個全局字元數組就是我們uboot運作時的DDR中的環境變量了。

總結:剛燒錄的系統中環境變量分區是空白的,uboot第一次運作時加載的是uboot代碼中自帶的一份環境變量,叫預設環境變量。我們在saveenv時DDR中的環境變量會被更新到SD卡中的環境變量中,就可以被儲存下來,下次開機會在環境變量relocate時會SD卡中的環境變量會被加載到DDR中去。

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

二,環境變量相關指令源碼解析

1、printenv

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

Uboot和系統移植(10)------- uboot環境變量的源碼分析一,uboot的環境變量基礎二,環境變量相關指令源碼解析三,uboot内部擷取環境變量

(2)分析do_printenv函數。

這個函數要看懂,首先要明白整個字元數組default_environment在記憶體中如何存儲的問題。

Uboot和系統移植(10)------- uboot環境變量的源碼分析一,uboot的環境變量基礎二,環境變量相關指令源碼解析三,uboot内部擷取環境變量

(3)do_printenv函數首先區分argc=1還是不等于1的情況,若argc=1那麼就循環列印所有的環境變量出來;如果argc不等于1,則後面的參數就是要列印的環境變量,給哪個就列印哪個。

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

Uboot和系統移植(10)------- uboot環境變量的源碼分析一,uboot的環境變量基礎二,環境變量相關指令源碼解析三,uboot内部擷取環境變量

2、setenv

(1)指令定義和對應的函數在uboot/common/cmd_nvedit.c中,對應的函數為do_setenv。

(2)setenv的思路就是:先去DDR中的環境變量處尋找原來有沒有這個環境變量,如果原來就有則需要覆寫原來的環境變量,如果原來沒有則在最後新增一個環境變量即可。

第1步:周遊DDR中環境變量的數組,找到原來就有的那個環境變量對應的位址。168-174行。

第2步:擦除原來的環境變量,259-265行

第3步:寫入新的環境變量,266-273行。

(3)本來setenv做完上面的就完了,但是還要考慮一些附加的問題。

問題一:環境變量太多超出DDR中的字元數組,溢出的解決方法。

問題二:有些環境變量如baudrate、ipaddr等,在gd中有對應的全局變量。這種環境變量在set更新的時候要同時去更新對應的全局變量,否則就會出現在本次運作中環境變量和全局變量不一緻的情況。

3、saveenv

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

(2)從uboot實際執行saveenv指令的輸出,和x210_sd.h中的配置(#define CFG_ENV_IS_IN_AUTO)可以分析出:我們實際使用的是env_auto.c中相關的内容。沒有一種晶片叫auto的,env_auto.c中是使用宏定義的方式去條件編譯了各種常見的flash晶片(如movinand、norflash、nand等)。然後在程式中讀取INF_REG(OMpin内部對應的寄存器)進而知道我們的啟動媒體,然後調用這種啟動媒體對應的操作函數來操作。

(3)do_saveenv内部調用env_auto.c中的saveenv函數來執行實際的環境變量儲存操作。

(4)寄存器位址:E010F000+0C=E010_F00C,含義是使用者自定義資料。我們在**start.S中判斷啟動媒體後将#BOOT_MMCSD(就是3,定義在x210_sd.h)寫入了這個寄存器,**是以這裡讀出的肯定是3,經過判斷就是movinand。是以實際執行的函數是:saveenv_movinand

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

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

三,uboot内部擷取環境變量

1、getenv

(1)應該是不可重入的。

(2)實作方式就是去周遊default_environment數組,挨個拿出所有的環境變量比對name,找到相等的直接傳回這個環境變量的首位址即可。

2、getenv_r

(1)可重入版本。(可自行搜尋補充可重入函數的概念)

(2)getenv函數是直接傳回這個找到的環境變量在DDR中環境變量處的位址,而getenv_r函數的做法是找到了DDR中環境變量位址後,将這個環境變量複制一份到提供的buf中,而不動原來DDR中環境變量。

是以差别就是:getenv中傳回的位址隻能讀不能随便亂寫,而getenv_r中傳回的環境變量是在自己提供的buf中,是可以随便改寫加工的。

3、總結

(1)功能是一樣的,但是可重入版本會比較安全一些,建議使用。

(2)有關于環境變量的所有操作,主要了解了環境變量在DDR中的存儲方法,了解了環境變量和gd全局變量的關聯和優先級,了解了環境變量在存儲媒體中的存儲方式(專用raw分區),整個環境變量相關的都清楚了。

繼續閱讀