uboot

***********************************
*******day:2014/10/14**************
************uboot******************
***********************************
1.爲何要有uboot?
2.uboot是用來幹嗎的?
3.uboot是怎麼工做的?
4.uboot結束後的結果是怎麼樣的?
爲了回答以上的問題,或許問題還不止這些,根據我我的的理解來談談,請觀看者注意版權問題哈。
起初龍芯的工程師跟我講uboot的時候,我還搞不明白什麼是uboot的,當時不懂事,學習以後我才明白什麼是uboot的,我就舉個比喻吧:就比如如你要吃西瓜,那你吃西瓜那好歹得有把西瓜刀菜刀之類的工具吧?難道你直接用啃的嗎?這個西瓜刀就是你在吃到西瓜肉以前的uboot。因此對於爲何要有uboot,你應該懂了吧,只不過這個uboot不是用來切西瓜的,而是用來加載和啓動咱們傳說中的內核的。至於什麼是內核?等我寫完uboot再睡會,而後再寫關於內核這巨人。

那到底uboot是怎麼樣來啓動和加載內核的呢??你看,這裏又有新問題了吧?別緊張,在此以前,必需要先了解下uboot的工做原理,天啊,又是工做原理,別暈,其實當初我也暈了。其實,soc一上電,uboot瞬間就開始工做了,IROM裏的固化代碼會把nandflash裏的前16K代碼拷貝到IRAM裏來執行,在這裏實現重定位,這個IRAM的0x340000地址就是運行地址,記住這個前16K哈。爲何呢?拿個西瓜啃,慢慢往下看。

在拿到uboot的原始源碼以後,記住,你要看看這個源碼到底有沒有支持你的開發板,若是沒有就得從新找找其餘版本的源碼,因爲uboot這種小玩意是跟開發板緊密相連的,因此你要根據你的開發板來作必定的移植,也就是針對開發板弄一個特定的uboot,對了,這個uboot的意思個人理解就是:u(you) 啓動。
拿到源碼直接定位到startS,你能夠看到異常向量表之類的彙編代碼,其實第一階段就是用匯編寫的,因此總結下第一階段的過程:
1.設置異常向量表 一開始就跳到了reset那裏了
2.關閉中斷,主要是關閉F和I位,而後進入SVC32模式,記住,必定要在這種模式下 set the cpu to SVC32 mode
3.cpu初始化 bl cpu_init_crit 在這裏主要是關閉緩存和MMU,至於爲何要關閉緩存,你想一想,若是不關閉的話,CPU是從緩存裏去指令的,這樣作的緣由就是由於匹配CPU的速度,爲了乖乖的讓CPU從內存裏取指令,只好先關閉咯
4.關閉開門狗,關閉VIC,其實這裏關閉了VIC的話,上面的F I 就沒什麼必要了,你想一想,老大都被抓了,小弟還有勇氣活嗎?還有開門狗,其實這個狗真的很厲害,在你程序跑飛的時候能夠幫你挽救系統。但在這時,你若是不關的話,它隨時會讓你的CPU一直復位的,除非你喂狗,可是你想一想,有可能喂狗嗎?
5.串口初始化,這裏是爲了調試在終端看信息用的,總不能用望遠鏡吧。
6.時鐘初始化,這個時鐘初始化不是說調如今幾點幾點的,而是爲整個開發板提供工做頻率的。
7.內存初始化。這個內存初始化通常都是跟原廠要的代碼,比較複雜,主要是爲了後面的重定位用的。
//注意注意:廣大的中國市民:6 和 7 在源碼裏默認是沒有幫你初始化的,因此你要作的移植就是:是這樣的:把#if 後面的宏定義改爲1就好了。也就是說,這個是沒有被編譯進去的,因此你這時候要作的就是改Makefile,添加:SOBJS := lowlevel_init.o mem_setup.o 但前提是你要修改你的內存初始化的代碼---->跟原廠要。必定要記住要確保這段代碼在nandFlash的前16K,怎麼確保呢?這個問題問得好:你能夠打開uboot.dis能夠看到段的分區:以下:只要把代碼放在text段裏就好了
.text :
{
cpu/arm_cortexa8/start.o (.text)
board/samsung/fs210/lowlevel_init.o (.text)
board/samsung/fs210/mem_setup.o (.text)
board/samsung/fs210/nand_cp.o (.text)
*(.text)
}
8.棧的初始化。必定要注意:在進入C語言代碼以前必定要確保棧已經初始化好了,不然,你後果自負。
9.重定位(這個指的是實現uboot的自重定位--->nandflash--->DDRII)這裏面有點深奧,扯到Flash和DDRII的內容,在這裏你只要先知道有這個步驟就好了,咦?什麼,uboot是在Flash裏的?沒錯,uboot在製做好的時候就是燒在Flash裏的:這裏給出平時燒錄的步驟
燒錄uboot://說明:這個是用tftp協議來下載的,因此,你的uboot必需要移植好,使其能支持tftp和nand erase 和nand write等命令,固然,這是比較後的知識了。其實也不是很後啦,就是在第二階段作的事而已。
cp u-boot.bin /tftpboot/
uboot1.3.4:
tftp 0x20008000 u-boot.bin
nand erase 0x0 0x40000
nand write 0x20008000 0x0 0x40000
//當實現重定位以後,(把整個u-boot拷貝考DDRII裏)在DDRII裏的第一階段的u-boot代碼就不會被從新執行,
//此時的pc指針自動指向第二階段的代碼,而後執行
//以前的在上電啓動的時候,u-boot被拷貝到nandflash裏,因爲nandflash不具有執行指令的功能,但能保證掉電後不丟失數據,因此在第一階段裏要實現u-boot把本身整個拷貝到DDRII裏來執行。DDRII具備執行指令的功能
10.清bss段。這個嘛,順應潮流,清吧。就當作是清垃圾吧,人人愛衛生,生活更美好
在進行清bss段以前,其實還有事情要作,也就是在DDRII裏分配棧內存,也是預留一些內存,好比預留128K給環境變量
啊,等等。這些預留的空間會在之後用到,在這隻要知道就好了。
11.跳轉到第二階段(c語言代碼),//因此在以前說的,必定要在跳轉到C語言代碼以前初始化好棧。否則,、、、
ldr pc, _start_armboot @ jump to C code
_start_armboot: .word start_armboot
*******************************************************************************************************
第二階段:從source insight裏跳轉,便可以看到start_armboot是在board.c裏面的,那麼第二階段作了什麼呢?
1.初始化一個全局變量指針 gd = (gd_t*)(_armboot_start - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t));
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));//這兩個指針就是指向了第一階段預留的空間的某一個位置

gd->bd->bi_arch_number = MACH_TYPE_SMDKC100; //開發板的id號碼 1826---》在內核中要用到
//內核裏其實也存在一個ID,它會跟你這個id進行比較,若是發現不對,而此時你又沒有經過set machid 來給內核的話,內核就會找不到你的這個開發板,這時候內核就會啓動不起來。而這個的做用主要是確保你的開發板跟內核是保持一致的。
gd->bd->bi_boot_params = PHYS_SDRAM_1 + 0x100; //傳遞給內核的數據所存放的位置
//這個變量就是你的uboot傳遞給內核的參數所在的內存位置,這個就是一個約定,你放在這個位置,內核也知道
//這個位置去取,若是你存放參數的位置不對,這時候內核取不到數據,你想他會怎麼樣,它不哭不鬧不上吊,直
//接就死給你看,你拿他沒辦法的。
//這時候的參數又分爲兩種狀況:(在本節來講,有點扯遠了)
一、默認的參數設置:當內核發現,uboot並無經過set bootargs給它傳遞參數時,內核這時它不哭,它就去找
看它自己有沒有默認的參數,好比serveriP 等,若是有,謝天謝地,它直接拿來用。這個默認參數通常都設置在
開發板的配置文件好比smdkc100.h裏,那怎麼找呢?咦,跟讀代碼你能夠發如今初始化序列裏(下面)有這樣的一
個函數:env_init, /* initialize environment */
//gd->env_addr = (ulong)&default_environment[0];在這裏就是使用了默認的參數設置
//gd->env_valid = 1; 這時候你就要問了?這個參數不是存放在第一階段預留的DDRII裏嗎?那一掉電不就是
//丟失了數據了嗎?爲何在下次啓動的時候在沒有傳參的時候還會加載??問得好,其實這個默認參數時在
//uboot的.data段,當nandflash中沒有有效數據時就會優先使用默認的
2.你經過set bootargs給內核傳遞參數
set bootargs console=ttySAC0,115200 root=/dev/nfs nfsroot=192.168.7.99:/opt/filesystem ip=192.168.7.189 init=/linuxrc
參數說明:
console=xxx : 告訴內核printk的調試信息是經過什麼設備打印出來
console=ttySAC0,115200
root=xxx: 告訴根文件系統在哪裏了
root=/dev/nfs : 根文件系統存在網絡遠端
nfsroot=ip:path : 詳細地址 這個ip地址就是你Ubuntu的地址
ip=192.168.7.189 : 系統啓動以後,靜態分配ip
root=/dev/nfs + nfsroot=xxx + ip=xxx
init=xxx: 告訴內核祖先進程的可執行代碼是什麼
init=/linuxrc
//廣大的中國市民:這裏參數的設置,其實目的是爲了讓你在網絡遠端下載的,其實都是要經過網絡的,但有沒有直接一上電就不須要從網絡下載呢?是有的,其實就是把內核,文件系統直接都燒錄在nandflash裏,固然這裏只是稍微提下
在跟讀代碼的時候能夠看到這樣一個循環:
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
這個循環實際上就是初始化序列,而能夠看到其實init_sequence是一個函數指針數組(這個有點饒吧:其實就是把指向函數的指針存放在數組裏,也就是這個數組存放的都是指向函數的指針)
init_fnc_t *init_sequence[] = {
#if defined(CONFIG_ARCH_CPU_INIT) //這個宏定義顯得頗有意思,這種能夠一次性判斷多個宏是否被定義,因此要使用該初始化函數,就必需要定義這個宏
arch_cpu_init, /* basic arch cpu dependent setup */
#endif
board_init, /* basic board dependent setup */
#if defined(CONFIG_USE_IRQ)
interrupt_init, /* set up exceptions */
#endif
timer_init, /* initialize timer */
#ifdef CONFIG_FSL_ESDHC
get_clocks,
#endif
env_init, /* initialize environment */
init_baudrate, /* initialze baudrate settings */
serial_init, /* serial communications setup */值得注意的是:在此函數以前不容許有任何的打印
#endif
dram_init, /* configure available RAM banks */
...
};linux

2.nandflash的初始化 前面提到過,你要燒錄uboot,就必需要用nand erase等命令,而這些命令使用的前提就必須
要初始化nandflash ,先看下nandflash的命令用法:
nand命令:
nand read[.jffs2] - addr off|partition size 這個[.jffs2]能夠先不用理他 這是一種文件系統。
nand read 0x20008000 0x0 0x100000
ARM地址 nandflash的地址 大小 從nandflash的地址讀大小爲0x100000(1M)的代碼量到0x20008000這個地址裏
nand erase 0x100000 0x400000
nand write[.jffs2] - addr off|partiton size
nand write 0x20008000 0x100000 0x400000
同nand read
那怎麼樣才能初始化呢?跟讀代碼能夠發現:
#if defined(CONFIG_CMD_NAND)//必須先定義這個宏
puts ("NAND: ");
nand_init(); /* go init the NAND */
#endif
這樣算初始化了??有個init??那你就錯了,其實這個代碼跟進去,它裏面作的事情就是幫你計算內存是多大而已,
printf("%u MiB\n", size / 1024);
因此,nandflash的代碼和實現uboot重定位的代碼須要本身寫的,由於每一個開發板可能寄存器什麼的有可能不同,因此在這就不列出代碼了,在這裏只是要提一點://你重定位的地址必定要跟你連接地址要一致,否則內核找不到數組

在上面提到:那個環境變量爲何在下次啓動的時候在沒有傳參的時候還會加載??
首先你要明確的一點就是:nandflash自己存放的數據在掉電的狀況下是不會丟失數據的,
再者,nandflash存放數據和取數據也很方便,只要三個命令便可,即nand erase / nand write /nand read
通過上面的nandflash的移植,就可使用啦,
因此,這個涉及到環境變量的重定位問題,跟讀代碼咱們能夠發現這樣的一個函數:
/* initialize environment */
env_relocate ();//環境變量的重定位
|
env_relocate_spec ();
|
if(gd->env_valid == 1) {
env_ptr = tmp_env1;
}
能夠看出,其實在DDRII裏預留的128K的環境變量等參數實際上是在nandflash的CONFIG_ENV_OFFSET爲起始地址的128K重定
位到DDRII來執行的,因此每次在掉電後開機,都能執行到環境變量參數,而每次set 什麼的都是在DDRII裏修改的
這時候經過save就能夠保存到nandflash裏了,方便吧。
至於nandflash要用到的宏定義我在這裏就直接給出了,僅供參考:
#define CONFIG_ENV_IS_IN_NAND 1
#define CONFIG_CMD_NAND 1
#define CONFIG_ENV_SIZE (256 << 10) /* 128KiB, 0x20000 */
#define CONFIG_ENV_OFFSET (512 << 10) /* 512KiB, 0x80000 */
#define CONFIG_SYS_MAX_NAND_DEVICE 1 //nand設備數量
#define CONFIG_SYS_NAND_MAX_CHIPS 1 //芯片的數量
#define CONFIG_SYS_NAND_BASE 0xB0E00000 // nandflash控制器的基地址
#define CONFIG_NAND_S5PV210 1 // mtd支持s5pv210的nand操做,用於控制s5pv210.c的編譯
#define CONFIG_NAND_BL1_8BIT_ECC 1
#define CFG_NAND_HWECC 1
#define CONFIG_NAND_PAGES_IN_BLOCK 64 // 每一個block有多少個頁

3.網卡的初始化,使其支持tftp協議來在網絡遠端(pc機Ubuntu的/tftpboot)下載uboot,uIamge,filesystem等
這個板子用的是S5PV210是帶網卡的,DM9000內核也支持,因此只要稍微移植就好了。爲何呢?這是由於在人羣
中多看了一眼??開玩笑,跟代碼能夠發現這樣的貓膩:
static void dm9000_get_enetaddr(struct eth_device *dev)
{
#if !defined(CONFIG_DM9000_NO_SROM)//其實這個宏定義是有定義的,因此默認狀況下DM9000是沒有獲取到mac地址的
int i;
for (i = 0; i < 3; i++)
dm9000_read_srom_word(i, dev->enetaddr + (2 * i));
#else //因此在這裏須要本身手動的增長如下代碼
eth_getenv_enetaddr("ethaddr", dev->enetaddr); //從環境變量中獲取mac地址
#endif
}
若是你以爲你只要作到這一關鍵的一步,那你就錯了,其實這只是DM9000的驅動代碼而已,而後以太網呢
?在board.c中調用的eth_initialize中其實調用了board_eth_init,但實際上這個board_eth_init是沒
有的,因此須要咱們來移植。在這個函數裏來調用dm9000的驅動代碼
int board_eth_init(bd_t *bis)
{
int rc = 0;
#ifdef CONFIG_DRIVER_DM9000
rc = dm9000_initialize(bis);//調用dm9000的驅動
|
/* Load MAC address from EEPROM */
dm9000_get_enetaddr(dev);
#endif
return rc;
}
可是你想,這樣夠了嗎?eth_initialize你跟進去其實能夠發現這個函數實際上是用來調用dm9000的,但實際上以太網的初始化仍是沒有調用的,因此在board.c裏須要添加以太網的初始化代碼eth_init(gd->bd);
對於以太網和dm9000,那些宏定義,實際上是根據跟源碼來得知要定義哪些宏定義,在此,我就直接給出來啦:
//for dm9000,添加以下內容
#define CONFIG_CMD_NET 1 //支持網絡命令
#define CONFIG_NET_MULTI 1
#define CONFIG_CMD_PING 1 // 支持ping命令
#define CONFIG_DRIVER_DM9000 1 //須要編譯dm9000的驅動
#define CONFIG_DM9000_BASE 0x88000000 // dm9000的基地址 這個基地址是根據不一樣硬件來肯定的
#define DM9000_IO CONFIG_DM9000_BASE //dm9000的IO地址 這個也是同上
#define DM9000_DATA (CONFIG_DM9000_BASE + 4) //dm9000的數據地址 這個也是同上
#define CONFIG_DM9000_USE_16BIT //數據位寬
#define CONFIG_DM9000_NO_SROM 1 // 網卡中不會自帶rom用於存放mac地址緩存

4.進入一個死循環,倒計時,這個倒計時是由系統定時器提供的,提供10ms的基準,
for (;;) {
main_loop ();
}
這個代碼就不跟下去了,太噁心了,有興趣的就跟吧,其實表現出來的就是五秒倒計時,在五秒內按任何鍵停止倒計時,不然,,,,就加載內核啦,固然此時要有內核放在/tftpboot裏或者以前燒錄 了內核鏡像在nandflash的分區裏面,否則,呵呵、、網絡

至此,我我的以爲已經解決了以上提出來的問題,至於uboot結束後的結果是什麼?我的以爲成功引導內核啓動後接下來就沒他的事了,只要啓動了內核或者能夠中斷五秒進入能夠設置的時候,uboot就應該say ヾ( ̄▽ ̄)Bye~Bye~了
要否則,你的uboot估計要從新檢查了,沒事,醫療費幾十萬而已啦、、、函數

相關文章
相關標籤/搜索