標籤:blog code http tar ext com
原文:http://blog.sina.com.cn/s/blog_6b94d5680100nd48.html
文章結構
結構順序有變化
-1:燒寫uboot
0: bootargs bootcmd 命令參數的設定
1:製作yaffs2的過程
2:燒寫yaffs2的過程
3:製作uimage 的過程
4: 燒寫uiamge的過程
5:uimage zimage vmlinux 的區別
6: uboot傳遞給核心的參數結構 tag
7:bootm go 的 啟動過程 以及區別
8:載入地址 入口地址 等
///////////////////通過uboot燒寫uboot到nandflash/////////////
//////////////
nand erase 0 50000
//////////////
tftpboot 30008000 u-boot.bin
/////
Loading: T ##################
done
Bytes transferred = 257164 (3ec8c hex)
[[email protected]]# nand write 0x30008000 0 3f000
//////////////
nand write 0x30008000 0 3f000( 3ec8c最靠近2K整數倍的值 )
///////////////////通過uboot燒寫核心到nandflash/////////////
先查看你自己板子的nandflash分區表
通過執行個體分析:下面的燒寫核心出錯
[[email protected]]# tftpboot 30008000 uImage
dm9000 i/o: 0x20000300, id: 0x90000a46
DM9000: running in 16 bit mode
MAC: 08:08:11:18:12:27
operating at 100M full duplex mode
Using dm9000 device
TFTP from server 192.168.0.100; our IP address is 192.168.0.10
Filename ‘uImage‘.
Load address: 0x30008000
Loading: T #################################################################
#################################################################
##########################
done
Bytes transferred = 2287128 (22e618 hex)
[[email protected]]# nand erase 0x60000 0x500000
NAND erase: device 0 offset 0x60000, size 0x500000
Erasing at 0x54000002800000 -- 0% complete.
OK
[[email protected]]# nand write 0x30008000 0x60000 22e618
NAND write: device 0 offset 0x60000, size 0x22e618
Attempt to write non page aligned data
2287128 bytes written: ERROR
下面的燒寫正確:
NAND erase: device 0 offset 0x80000, size 0x500000
Erasing at 0x54000002800000 -- 0% complete.
OK
[[email protected]440]# nand write 0x30008000 0x80000 22e800
NAND write: device 0 offset 0x80000, size 22e800
改了兩個地方:
1:0x60000 ->0x80000
2:0x22e618 ->22e800
修改的原因是:1:從0x60000檫除 會把bootargs bootcmd 也給檫除了 所以選擇從0x80000開始檫除
2:0x22e618 雖然是通過tftpboot下載的核心的實際大小,但是這裡是write指令 後面的參數是指明要檫寫的長度 檫寫長度要求是2k(2048)的整數倍(也就是整頁 整頁的檫除) 而0x22e618 不是2K的整數倍 ,出現錯誤Attempt to write non page aligned data 2287128 bytes written: ERROR ,於是取一個最靠近的數 就選擇了22e800 就OK樂
/////////////////////設定自啟動參數bootcmd///////////////////
我剛開始時 是這樣寫的:
setenv bootcmd nand read 30008000 80000 22e800 ; bootm 30008000
按確定後 直接執行了bootm這個命令 於是改了:
setenv bootcmd nand read 30008000 80000 22e800 \; bootm 30008000 就OK了
/////////////////////下載yaffs2映像到nandflash中/////////////////////////
生產yaffs2 以及工具請可以參考Tekkaman Ninja 的檔案 uboot 燒寫yaffs2檔案系統
方法是類似於核心的燒寫
先查看你自己板子的nandflash分區表( 比如128M的板子 128M ->8000000 檫寫地址是從0x580000開始 所以 兩個相減 等於 7a80000 而我的是256M的 前面的核心是從0x80000開始的 檫除了0x500000 長度 所以這裡檔案系統 從0x580000 開始 結束到 nandflash的結束 )
128M
先察除:nand erase 0x580000 7a80000
再寫 :nand write.yaffs 30008000 0x580000 dbb040(檔案系統的大小,而這裡檔案系統 不像核心一樣 燒寫長度 不是2K的整數倍 我不確定我從網路上找到的答案是否是對的,自己也沒有去查證:用mkyaffs2image
產生的檔案系統 在2K資料對齊的問題上 已經採取了對齊措施 使用者不用在考慮資料對齊的問題,而mkimage等工具沒有採用對齊措施 所以是要注意)
256M:
先察除:nand erase 0x580000 FA80000
再寫 :nand write.yaffs 30008000 0x580000 dbb040
///////////設定bootargs參數////////////////////////////////////
1:如果用yaffs2檔案系統
setenv bootargs noinitrd root=/dev/mtdblock3 rootfstype=yaffs2 rw console=ttySAC0,115200 init=/linuxrc mem=64M
2:如果用NFS檔案系統
setenv bootargs noinitrd
root=/dev/nfs rw nfsroot= 192.168.0.100 :/ home/lq/rootfs ip=192.168.0.10 console=ttySAC0 mem=64M
///////////////bootatgs 參數解析/////////////////////////
root
用來指定rootfs的位置, 常見的情況有:
root=/dev/ram rw
root=/dev/ram0 rw
請 注意上面的這兩種設定情況是通用的,我做過測試甚至root=/dev/ram1 rw和root=/dev/ram2 rw也是可以的,網上有人說在某些情況下是不通用的,即必須設定成ram或者ram0,但是目前還沒有遇到,還需要進一步確認,遇到不行的時候可以逐一嘗 試。
root=/dev/mtdx rw
root=/dev/mtdblockx rw
root=/dev/mtdblock/x rw
root=31:0x
上 面的這幾個在一定情況下是通用的,當然這要看你當前的系統是否支援,不過mtd是字元裝置,而mtdblock是塊裝置,有時候你的挨個的試到底當前的系 統支援上面那種情況下,不過root=/dev/mtdblockx rw比較通用。此外,如果直接指定裝置名稱可以的話,那麼使用此裝置的裝置號也是可以的。
root=/dev/nfs
在檔案系統為基於nfs的檔案系統的時候使用。當然指定root=/dev/nfs之後,還需要指定nfsroot=serverip:nfs_dir,即指明檔案系統存在那個主機的那個目錄下面。
B. rootfstype
這個選項需要跟root一起配合使用,一般如果根檔案系統是ext2的話,有沒有這個選項是無所謂的,但是如果是jffs2,squashfs等檔案系統的話,就需要rootfstype指明檔案系統的類型,不然會無法掛載根分區.
C. console
console=tty 使用虛擬串口終端裝置 .
console=ttyS[,options] 使用特定的串口,options可以是這樣的形式bbbbpnx,這裡bbbb是指串口的傳輸速率,p是奇偶位(從來沒有看過使用過),n是指的bits。
console=ttySAC[,options] 同上面。
看 你當前的環境,有時用ttyS,有時用ttySAC,網上有人說,這是跟核心的版本有關,2.4用ttyS,2.6用ttySAC,但實際情況是官方文檔 中也是使用ttyS,所以應該是跟核心版本沒有關聯的。可以查看Documentation/serial-console.txt找到相關描述。
D. mem
mem=xxM 指定記憶體的大小,不是必須的
E. ramdisk_size
ramdisk=xxxxx 不推薦
ramdisk_size=xxxxx 推薦
上 面這兩個都可以告訴ramdisk 驅動,建立的ramdisk的size,預設情況下是4m(s390預設8M),你可以查看Documentation/ramdisk.txt找到相關 的描述,不過ramdisk=xxxxx在新版的核心都已經沒有提了,不推薦使用。
F. initrd, noinitrd
當你沒有使用ramdisk啟動系統的時候,你需要使用noinitrd這個參數,但是如果使用了的話,就需要指定initrd=r_addr,size, r_addr表示initrd在記憶體中的位置,size表示initrd的大小。
G. init
init 指定的是核心啟起來後,進入系統中啟動並執行第一個指令碼,一般init=/linuxrc, 或者init=/etc/preinit,preinit的內容一般是建立console,null裝置節點,運行init程式,掛載一些檔案系統等等操 作。請注意,很多初學者以為init=/linuxrc是固定寫法,其實不然,/linuxrc指的是/目錄下面的linuxrc指令碼,一般是一個串連罷 了。
H. mtdparts
mtdparts=fc000000.nor_flash:1920k(linux),128k(fdt),20M(ramdisk),4M(jffs2),38272k(user),256k(env),384k(uboot)
要 想這個參數起作用,核心中的mtd驅動必須要支援,即核心配置時需要選上Device Drivers ---> Memory Technology Device (MTD) support ---> Command line partition table parsing
mtdparts的格式如下:
mtdparts=[;
:= :[,]
:= [@offset][][ro]
:= unique id used in mapping driver/device
:= standard linux memsize OR "-" to denote all remaining space
:= (NAME)
因此你在使用的時候需要按照下面的格式來設定:
mtdparts=mtd-id:@(),@()
這裡面有幾個必須要注意的:
a. mtd-id 必須要跟你當前平台的flash的mtd-id一致,不然整個mtdparts會失效
b. size在設定的時候可以為實際的size(xxM,xxk,xx),也可以為‘-‘這表示剩餘的所有空間。
舉例:
假設flash 的mtd-id是sa1100,那麼你可以使用下面的方式來設定:
mtdparts=sa1100:- → 只有一個分區
mtdparts=sa1100:256k(ARMboot)ro,-(root) → 有兩個分區
可以查看drivers/mtd/cmdlinepart.c中的注釋找到相關描述。
I. ip
指定系統啟動之後網卡的ip地址,如果你使用基於nfs的檔案系統,那麼必須要有這個參數,其他的情況下就看你自己的喜好了。設定ip有兩種方法:
ip = ip addr
ip=ip addr:server ip addr:gateway:netmask::which netcard:off
這兩種方法可以用,不過很明顯第二種要詳細很多,請注意第二種中which netcard 是指開發板上的網卡,而不是主機上的網卡。
說完常見的幾種bootargs,那麼我們來討論平常我經常使用的幾種組合:
1). 假設檔案系統是ramdisk,且直接就在記憶體中,bootargs的設定應該如下:
setenv bootargs ‘initrd=0x32000000,0xa00000 root=/dev/ram0 console=ttySAC0 mem=64M init=/linuxrc’
2). 假設檔案系統是ramdisk,且在flash中,bootargs的設定應該如下:
setenv bootargs ‘mem=32M console=ttyS0,115200 root=/dev/ram rw init=/linuxrc’
注意這種情況下你應該要在bootm命令中指定ramdisk在flash中的地址,如bootm kernel_addr ramdisk_addr (fdt_addr)
3). 假設檔案系統是jffs2類型的,且在flash中,bootargs的設定應該如下
setenv bootargs ‘mem=32M console=ttyS0,115200 noinitrd root=/dev/mtdblock2 rw rootfstype=jffs2 init=/linuxrc’
4). 假設檔案系統是基於nfs的,bootargs的設定應該如下
setenv bootargs ‘noinitrd mem=64M console=ttySAC0 root=/dev/nfs nfsroot=192.168.0.3:/nfs ip=192.168.0.5:192.168.0.3:192.168.0.3:255.255.255.0::eth0:off’
或者
setenv bootargs ‘noinitrd mem=64M console=ttySAC0 root=/dev/nfs nfsroot=192.168.0.3:/nfs ip=192.168.0.5’
//////////bootm go 的 啟動過程 以及區別/////////////////////
Bootm命令在/common/cmd_bootm.c中do_bootm函數中
//////////////////
通過mkimage這個tool可以給zImage添加一個header:
typedef struct image_header
{
uint32_t ih_magic;
uint32_t ih_hcrc;
uint32_t ih_time;
uint32_t ih_size;
uint32_t ih_load;
uint32_t ih_ep;
uint32_t ih_dcrc;
uint8_t ih_os;
uint8_t ih_arch;
uint8_t ih_type;
uint8_t ih_comp;
uint8_t ih_name[IH_NMLEN];
} image_header_t;
這個頭的大小是 0x40 64個位元組,64byte的資訊,供建立tag之用,通過tag 傳遞參數給核心
mkimage 參數說明:
對於ARM linux核心映象用法:
-A arm -------- 架構是arm
-O linux -------- 作業系統是linux
-T kernel -------- 類型是kernel
-C none/bzip/gzip -------- 壓縮類型
-a 30008000 ---- image的載入地址(hex),通常為0xX00008000
-e 300080XX ---- 核心的入口地址(hex),XX為0x40或者0x00
-n linux-XXX --- image的名字,任意
-d nameXXX ---- 無頭資訊的image檔案名稱,你的源核心檔案
uImageXXX ---- 加了頭資訊之後的image檔案名稱,任意取
mkimage -n ‘liquan_kernel‘ -A arm -O linux -T kernel -C none -a 0x30008000 -e 0x30008040 -d zImage A_zImage
mkimage -n ‘liquan_kernel‘ -A arm -O linux -T kernel -C none -a 0x30008000 -e 0x30008000 -d zImage B_zImage
上面通過mkimage命令建立了兩個帶有bootm命令用的頭的uImage核心映像
做過這樣的一個實驗:
1:
tftpboot 30008000 A_zImage
bootm 30008000
2:
tftpboot 31000000 B_zImage
bootm 31000000
都可以成功啟動
對應第二個實驗 如果在生產B_zImage時改為-a 0x30008000 -e 0x30008040 也就是說 核心的載入地址 與 核心的入口地址不一樣 這樣再tftpboot 31000000 B_zImage;bootm 31000000 這樣就啟動不了
tftpboot xxxx 核心映像
bootm xxxx
對於非gzip壓縮的核心映像( 非gzip壓縮 請看下面 這裡 -C none 沒有壓縮的 )
bootm命令會首先判斷bootm xxxx 中xxxx是否與 mkimage 中 -a指定的載入地址相同。
1:相同
如果相同的話那就讓其原封不同的放在那,但-e指定的入口地址會推後64byte,以跳過這64byte的頭部,再啟動
2:不同
如果不同的話會從這個地址開始提取出這個64byte的頭部,對其進行分析,然後把去迴轉部的核心複製到-a指定的load地址中去運行之 所以這個情況下 是要求 -a 0x30008000 -e 0x30008000 也就是要求 核心入口地址與載入地址要相同就是這樣要求 因為 已經沒有頭部了
對於gzip壓縮過的核心:(解壓核心到-a指定的地址 這個地址 這時也就是核心入口地址了)
因為u-boot要對其解壓,因此tftpboot是不能等於-a指定的地址的,且必須有一定的間隔,否則解壓到-a的核心會覆蓋當前啟動並執行程式 。此時要求-a等於-e指定的地址
///////////////////////各種壓縮檔的區別//////////////////////////////////////////
通過核心的編譯 會生產zImage zImage是vmlinux經過gzip壓縮後的檔案,它們不僅是一個壓縮檔,而且在這個檔案的開頭部分內嵌有gzip解壓縮代碼。
通過mkimage -C none/bzip2/gzip 可以決定是否生產的uImage 進行壓縮(所以上面的uImage 是gzip壓縮的)
如果mkimage -C bzip2/gzip 對zImage 進行了進一步的壓縮 那麼下載內sdram裡面後 首先是bootm 實現針對mkimage -C bzip2/gzip 對uImage進行第一步解壓(這部可以叫做u-boot解壓) 然後再進行zImage的自解壓(kernel自解壓)
////////////////
zImage在前面已經被壓縮了一次(這次的解壓是自解壓的 不是uboot壓縮的 所以對於uboot來說 這個不是壓縮檔),所以在用mkimage時 如果 -c 指定要壓縮 那麼U-boot 對核心添加頭部時,可以對原來的zImage/Image添加了U-boot的壓縮方式,使得產生的uImage變小。此時-c gzip 若不對zImage/Image用gzip命令壓縮,則-c none。綜合上面分析,mkimage的影響因子為:
-e :核心的入口地址是否與-a相同
Tftpaddr:即將核心載入到RAM中啟動並執行地址,決定是否搬運或解壓核心,Tftpaddr 與-a指定的載入地址相同,如果 如果Tftpaddr 與-a 不同 則去迴轉部後搬運至-a處運行,如果-c 壓縮過 tftpboot 地址 還不能與-a 相同 以防覆蓋
-c :核心是否經過gzip壓縮過,決定了是搬運還是解壓 壓縮的 就解壓到-a的地址處 沒有壓縮的 如果Tftpaddr 與-a 不同 則去迴轉部後搬運至-a處運行
/////////////////
vmlinux:
編譯出來的最原始的核心檔案,未壓縮。
zImage :
vmlinux經過gzip壓縮後的檔案,它們不僅是一個壓縮檔,而且在這兩個檔案的開頭部分內嵌有gzip解 壓縮代碼。所以你不能用gunzip 或 gzip –dc解包vmlinuz。
bzImage:
bz表示“bigzImage”,不是用bzip2壓縮的。兩者的不同之處在於,zImage解壓縮核心到低端記憶體(第一個640K),bzImage解壓縮核心到高端記憶體(1M以上)。如果核心比較小,那麼採用zImage或bzImage都行,如果比較大應該用bzImage。
uImageU-boot:
專用的映像檔案,它是在zImage之前加上一個長度為0x40的tag。
vmlinuz:
bzImage/zImage檔案的拷貝或指向bzImage/zImage的連結。核心檔案中包含一個微型的gzip用於解壓縮核心並引導它。兩者的不同之處在於,老的zImage解壓縮核心到低端記憶體(第一個640K),bzImage解壓縮核心到高端記憶體(1M以上)。如果核心比較小,那麼可以採用zImage 或bzImage之一,兩種方式引導的系統運行時是相同的。大的核心採用bzImage,不能採用zImage。
////////////////////////bootm go 啟動//////////////////////////////////////////////
通過模擬器可以很清晰的看到bootm先後完成了2部分工作。可以分析bootm源碼
1.set bootargs 傳遞參數
2.set pc ,r0 ,r1
通過模擬器可以很清晰的看到go先後完成了2部分工作。可以分析bootm源碼
go命令本質就是改變當前pc值
通過模擬器對go命令加以改造
a.將通用寄存器值改成
b.通過模擬器修改 0x08000100 地址的值
然後讓程式執行,這樣通過uboot也可以讓zImage得以執行。
可見go和bootm差異就是 go只是改寫pc值,而bootm 除了改PC外 傳遞r0,r1,r2還有bootargs
/////////////////bootm啟動過程///////////////////////////////////////////////
Bootm命令在/common/cmd_bootm.c中do_bootm函數
》》》》》》》》》》》擷取當前核心的地址,預設地址或者bootm的第一個參數
預設的載入地址或傳遞給bootm命令(優先)與實際的核心存放地址需要一致
if (argc < 2) {
addr = load_addr; // load_addr = CFG_LOAD_ADDR;
} else {
addr = simple_strtoul(argv[1], NULL, 16);
}
printf ("## Booting image at lx ...\n", addr);
》》》》》》》》》》》》獲得image頭,沒有mkimage的就返回了
memmove (&header, (char *)addr, sizeof(image_header_t));
》》》》》》》》》》》》列印頭部資訊
print_image_hdr ((image_header_t *)addr);
執行個體:
Image Name: dd-kernel-2.4.19
Image Type: ARM Linux Kernel Image (gzip compressed)
Data Size: 869574 Bytes = 849.2 kB
Load Address: 20008000
Entry Point: 20008000
》》》》》》》》》》》》校正image頭部
printf (" Verifying Checksum ... "); printf ("OK\n");
》》》》》》》》》》》》檢查image支援的體繫結構即—A 選項是否為arm或者ppc等
》》》》》》》》》》》》檢查image的類型
TYPE_MULTI 是否指核心與檔案系統一起,核心後面有個分界線
switch (hdr->ih_type)
case IH_TYPE_KERNEL:
name = "Kernel Image";
break;
case IH_TYPE_MULTI:
》》》》》》》》》》判斷核心的壓縮類型
此處的核心是否壓縮非zImage和Image的概念,而是指核心在被mkimage處理前是否用gunzip等壓縮過
switch (hdr->ih_comp) {
case IH_COMP_NONE: // 非壓縮核心
if(ntohl(hdr->ih_load) == addr) {
// 當前核心存放的地址與-a指定的一致,則不搬動,-e必須必-a大0x40
printf (" XIP %s ... ", name);
} else {
//當前核心存放的地址與-a指定的不一致,則將核心搬到-a地址,此時-a與-e必相同
memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);
。。。。
case IH_COMP_GZIP:
printf (" Uncompressing %s ... ", name);
if (gunzip ((void *)ntohl(hdr->ih_load), unc_len,
//壓縮核心,將除去頭部的核心解壓到-a 指定的地址了,要求-a與-e相同
// 為防止解壓縮時覆蓋,對於壓縮核心,核心存放地址最好在—a後面
(uchar *)data, (int *)&len) != 0) {
do_reset (cmdtp, flag, argc, argv);
}
break;
》》》》》》》》》》》》》》》》判斷作業系統類型
switch (hdr->ih_os) {
default:
case IH_OS_LINUX:
do_bootm_linux (cmdtp, flag, argc, argv, addr, len_ptr, verify);
//前四個為傳給bootm的,addr為核心最初的存放地址,沒有用處
break;
#ifdef CONFIG_PPC
static boot_os_Fcn do_bootm_linux;
#else
extern boot_os_Fcn do_bootm_linux;
由上可知,對於ppc和其他體繫結構的do_bootm_linux函數實現是不一樣的
》》》》》》》》》》》》》》啟動Linux核心
do_bootm_linux (cmd_tbl_t *cmdtp, int flag,
int argc, char *argv[],
ulong addr,
ulong *len_ptr,
int verify)
》》》》》》》》》》》》擷取命令列參數
if ((s = getenv("bootargs")) == NULL)
s = "";
strcpy (cmdline, s);
》》》》》》》》》》》》賦核心啟動地址
kernel = (void (*)(bd_t *, ulong, ulong, ulong, ulong))hdr->ih_ep;
注意,對於壓縮過的核心,會將核心解壓到-a指定的地址,此時-a 與-e 地址必須相同
》》》》》》》》》》》判斷bootm的命令參數中是否有initrd
if (argc >= 3) {
addr = simple_strtoul(argv[2], NULL, 16);
printf ("## Loading RAMDisk Image at lx ...\n", addr);
若有initrd則賦值,否則為0
》》》》》》》》》》》》》》》啟動Linux核心
//*kbd = *(gd->bd); 在上面賦值的
(*kernel) (kbd, initrd _start, initrd_end, cmd_start, cmd_end);