bootm命令只能用來引導經過mkimage構建了鏡像頭的內核鏡像文件以及根文件鏡像,對於沒有用mkimage對內核進行處理的話,那直接把內核下載到連接腳本中指定的加載地址0x30008000再運行就行,內核會自解壓運行(不過內核運行需要一個tag來傳遞參數,而這個tag是由bootloader提供的,在u-boot下默認是由bootm命令建立的)
通過mkimage可以給內核鏡像或根文件系統鏡像加入一個用來記錄鏡像的各種信息的頭.同樣通過mkimage也可以將內核鏡像進行一次壓縮(指定-C none/gzip/bzip2),所以這里也就引申出了兩個階段的解壓縮過程:第一個階段是u-boot里面的解壓縮,也就是將由mkimage壓縮的鏡像解壓縮得到原始的沒加鏡像頭的內核鏡像.第二個階段是內核鏡像的自解壓,u-boot里面的解壓實際上是bootm 實現的,把 mkimage -C bzip2或者gzip 生成的uImage進行解壓;而kernel的自解壓是對zImage進行解壓,發生在bootm解壓之后.
下面通過cmd_bootm.c文件中對bootm命令進行解析以及執行的過程來分析,這三種不同地址的區別:
1 ulong load_addr = CFG_LOAD_ADDR; /* Default Load Address */ 2 3 int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) 4 { 5 ...... 6 if (argc < 2) { 7 addr = load_addr;//當bootm命令后面不帶地址參數時,將默認的加載地址賦值給addr 8 } else { 9 addr = simple_strtoul(argv[1], NULL, 16); //如果bootm命令后面帶了加載地址,則將該地址賦值給addr,所以最終有用的地址還是bootm命令后附帶的地址 10 } 11 ...... 12 13 switch (hdr->ih_comp) { //開始判斷利用mkimage命令后是否對內核鏡像進行了壓縮 14 case IH_COMP_NONE: //如果沒有被壓縮,只是在原有的內核鏡像前加了一個頭 15 if(ntohl(hdr->ih_load) == addr) { //這步很重要,涉及到我們要討論的兩個地址,我們知道在利用mkimage時指定-a選項后面就是指定的內核加載的地址,這個地址會被存放到鏡像頭結構的ih_load成員變量中如在smdk2410中ih_load為0x30008000,在這里開始作地址的判斷,如果指定的加載地址和之前的addr也就是bootm后面附帶的地址相同,則不用將內核鏡像搬到其他地方,就在這個地址上執行,這樣內核的入口地址就要在加載地址之后的64個字節(因為鏡像頭占了64個字節),所以入口地址為0x30008040 16 printf (" XIP %s ... ", name); 17 } else {//如果指定的加載地址和bootm命令后的附加地址不相同,我們看看下面data此時表示的是什么地址:data = addr + sizeof(image_header_t);可以看到如果指定加載地址與bootm命令后地址不相同,則從bootm命令后面地址所在地取出內核鏡像的頭進行檢驗,檢驗完后data指向真正的內核,然后將內核拷貝到指定的加載地址處來進行自解壓運行,這個時候內核的入口地址就和加載地址一樣,不需要加上40個字節,因為內核鏡像前面的40個字節的頭已經被取出來了. 18 memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len); 19 } 20 case IH_COMP_GZIP://如果利用mkimage時對內核鏡像進行了壓縮,則需要在u-boot內進行第一階段的解壓縮,將解壓后的內核鏡像存放到指定的加載地址ih_load,然后內核鏡像自解壓啟動 21 printf (" Uncompressing %s ... ", name); 22 if (gunzip ((void *)ntohl(hdr->ih_load), unc_len,(uchar *)data, &len) != 0) { 23 puts ("GUNZIP ERROR - must RESET board to recover/n"); 24 SHOW_BOOT_PROGRESS (-6); 25 do_reset (cmdtp, flag, argc, argv); 26 } 27 break; 28 } 29 ...... 30 }
所以如果使用mkimage生成內核鏡像文件的話,會在內核的前頭加上了64byte的信息,供建立tag之用.bootm命令會首先判斷bootm xxxx 這個指定的地址xxxx是否與-a指定的加載地址是否相同.
- 如果不同的話會從這個地址開始提取出這個64byte的頭部,對其進行分析,然后把去掉頭部的內核復制到-a指定的load地址中去運行之
- 如果相同的話那就讓其原封不同的放在那,但-e指定的入口地址會推后64byte,以跳過這64byte的頭部.
我們來看看這兩個地址的不同情況:
1) mkimage -A arm -O linux -T kernel -C none -a 0x30008000 -e 0x30008040 -n linux-2.6.18.8 -d zImage uImage
這種情況 ,只能把 uImage download到 30008000的位置上,否則 從0x30008040是啟動不了的.
原因:如果將uImage(加了頭的鏡像文件)下載到不同於指定加載地址的地方,則會進行上面的操作,將去掉頭部的內核拷貝到指定的加載地址,此時加載地址和入口地址需要是相同的,因為已經沒有鏡像頭了,所以此時入口地址也應該為30008000,而不應該再加上64個字節.所以在構建鏡像頭部中的加載地址和入口地址時千萬要考慮下載的地址,否則將會啟動不了.
2) mkimage -A arm -O linux -T kernel -C none -a 30008000 -e 30008000 -n linux-2.6.18.8 -d zImage uImage
這種情況download地址隨便. 還是按上面說的,因為將加載地址和入口地址設置成同樣的地址,在下載到任意地址時,將去掉頭部的內核鏡像拷貝到指定加載地址后,可以直接從加載地址開始啟動.但是要是下載地址和指定加載地址相同呢?也就是下面的:
如果 tftp 下載地址==0x30008000 , 此時因為下載地址和指定加載地址相同,所以就不會搬動,內核直接從指定加載地址自解壓啦,但是因為指定的入口地址也是0x30008000,還是在鏡像頭處,可以看到上面的代碼,如果相同沒有做任何事,只是打印了提示信息,所以還得將入口地址往后推后64個字節還是從 0x30008040 啟動就肯定OK.
所以在制作鏡像頭以及下載地址就有兩種情況:
1) mkimage -n 'linux-2.6.14' -A arm -O linux -T kernel -C none -a 0x30008000 -e 0x30008000 -d zImage uImage
加載地址和入口地址相同
tftp 0x31000000 uImage
bootm 0x31000000
下載地址可以任意放.
2) mkimage -n 'linux-2.6.14' -A arm -O linux -T kernel -C none -a 0x30008000 -e 0x30008040 -d zImage uImage
入口地址在加載地址后面64個字節
tftp 0x30008000 uImage
bootm 0x30008000
下載地址一定要在指定的加載地址上.
本文轉自:http://blog.csdn.net/liangkaiming/article/details/5986680