一、移植環境
- 主 ?機:VMWare--Fedora 9
- 開發板:Mini2440--64MB Nand,Kernel:2.6.30.4
- 編譯器:arm-linux-gcc-4.3.2.tgz
- u-boot:u-boot-2009.08.tar.bz2
二、移植步驟
?? 我們知道使用tftp下載內核和使用nfs掛載文件系統的好處是,當我們重新編譯內核或文件系統后不用重新把這些鏡像文件再燒錄到flash上,而是把這些鏡像文件放到開發主機的tftp或nfs服務的主目錄下,通過網絡來加載他們,不用頻繁的往flash上燒,這樣一可以保護flash的使用壽命,二可以方便的調試內核或文件系統,提高開發效率。可見,讓u-boot實現這個功能是一件很有意義的事情。
?? 實現這樣的功能很簡單,網上也有很多資料。但有很多細節的東西如果稍不注意就導致失敗,這里就結合本人實現的過程進行講述和一些問題的分析。
- tftp服務的安裝與配置及測試
???要使用tftp服務及測試它要安裝兩個軟件包,一個就是tftp服務器,另外一個就是tftp客戶端,這里安裝客戶端只是用于在主機本地測試tftp服務器是否正常運行的,來確保u-boot能夠訪問tftp服務(u-boot中已有tftp客戶端的功能,其實在前面幾篇中都已經使用了tftp下載內核或文件系統到開發板上,如果那里都做到了,這里就可以直接跳過)。
?? 首先使用rpm命令查看你的主機上是否已經安裝了tftp服務器和客戶端,如果沒有安裝就去下載這兩個軟件包進行安裝或者可以使用yum命令進行在線安裝,yum會自動的去搜索適合你主機平臺的最新軟件包進行下載安裝,如果主機已經安裝了,則會提示軟件包已經安裝了最新的版本。如下圖所示:
???配置tftp服務器,主要是配置tftp的主目錄及訪問權限。因tftp服務依賴于xinetd服務,所以一般tftp服務安裝好后其配置文件一般會在/etc/xinetd.d/目錄下:?
?
- nfs服務的安裝與配置及測試
?? 以root的身份在控制臺輸入setup,在系統服務選項中選中nfs服務,如下圖:
?? 配置NFS服務器的共享主目錄,也要注意權限問題:
- u-boot到kernel的參數傳遞??
?? 我們知道,在kernel配置選項Boot options中有一個Default kernel command string參數項,而在u-boot參數中也有一個bootargs參數項,他們都是供內核啟動用的,那他們又有什么區別呢,內核啟動時到底是用哪一個呢?兩種參數項分別如下圖所示(kernel中的參數指定是從開發板Flash分區上掛載文件系統,u-boot中的參數指定的是從NFS掛載文件系統):
?? 那么,u-boot是如果將參數信息傳遞到內核中的呢?而內核又是怎么接收u-boot傳遞過來的參數呢?這就涉及到一點點ARM寄存器的知識了。
?? 我們知道,ARM有7種工作模式和37個寄存器(31個通用寄存器和6個狀態寄存器),如下圖:
?? ARM工作模式之間的轉換就是利用這些寄存器進行,而u-boot參數的傳遞也利用了三個通用寄存器R0、R1和R2。關于ARM工作模式和寄存器在這里就不做講敘了,以后再講,這里你就理解成u-boot在啟動的時候把參數存放到這三個寄存器中,到內核啟動時再把寄存器中的參數取出,當然,他們并不是就這樣簡單的操作。下面我們看代碼一一分析。
?? 首先,我們來分析一下u-boot是怎樣處理和發送要傳遞的參數,而u-boot要傳遞的參數又有哪些呢?除了我們最容易知道的bootargs(即內核commandline)參數項外,要傳遞的參數還有MACH_TYPE(即我們所說的機器碼)、系統根設備信息(標志,頁面大小)、內存信息(起始地址,大小)、RAMDISK信息(起始地址,大小)、壓縮的RAMDISK根文件系統信息(起始地址,大小)。由此可見要傳遞的參數很多,這時候,u-boot就提供一種叫做參數鏈表(tagged list)的方式把這些參數組織起來,鏈表結構體定義在:include/asm-arm/setup.h中,而實現鏈表的組織在lib_arm/bootm.c中:
?? ?
我們可以看到,鏈表的組織是由一系列函數實現,u-boot規定,鏈表必須以ATAG_CORE標記開始,以ATAG_NONE標記結束,中間就是一些參數標記項,這點從代碼中可以體現出來。那么在這些函數中有一個bd的參數是至關重要的,它是一個bd_info類型的結構體,定義在include/asm-arm/u-boot.h中,而這個結構體又被一個global_data類型的結構體所引用,定義在include/asm-arm/global_data.h中,如下:
???
那么,那個bd參數到底是做什么用的呢?從定義中可以得知,bd記錄了機器碼、u-boot參數鏈表在內存中的地址等信息,那又問,它在什么地方進行記錄的呢?它就在我們自己開發板初始化代碼中記錄的,如:board/samsung/my2440/my2440.c中
注意:bd_t被gd_t所引用,而在global_data.h中我們可以看到,u-boot定義了一個gd_t的全局指針變量*gd,所以在這里就可以直接使用gd來設置bd了。
好了,我們還是接著分析這個參數鏈表是如何被傳遞的,組織參數鏈表的系列函數在一個叫do_bootm_linux的函數中被調用的,還是定義在lib_arm/bootm.c中
?
mkimage [-x] -A arch -O os -T type -C comp -a addr -e ep -n name -d data_file[:data_file...] image
選項:
-A:set architecture to 'arch'?????? //用于指定CPU類型,比如ARM
-O:set operating system to 'os'???? //用于指定操作系統,比如Linux
-T:set image type to 'type'???????? //用于指定image類型,比如Kernel
-C:set compression type 'comp'????? //指定壓縮類型
-a:set load address to 'addr' (hex) //指定image的載入地址
-e:set entry point to 'ep' (hex)??? //內核的入口地址,一般為image的載入地址+0x40(信息頭的大小)
-n:set image name to 'name'???????? //image在頭結構中的命名
-d:use image data from 'datafile'???//無頭信息的image文件名
-x:set XIP (execute in place)?????? //設置執行位置
例如:
mkimage -n 'linux-2.6.30.4' -A arm -O linux -T kernel -C none -a 0x30008000 -e 0x30008000 -d zImage uImage.img
呵呵,相信此時的你撥云見日,茅塞頓開了吧!這個入口地址就是0x30008000,這也正是為什么u-boot一定要使用uImage的格式來啟動內核的原因之一。注意:這里有個kernel入口地址0x30008000,在上面還提到一個u-boot參數鏈表在內存中的地址0x30000100,試想如果這里指定的kernel入口地址覆蓋了參數鏈表的地址會怎么樣?
好了,把上面每個步驟從下往上看就可以知道u-boot參數項在u-boot端的傳遞的整個流程了,那么,接下來再分析u-boot參數項在kernel端是怎樣接收的。
start:
??......??
? .word?0x016f2818??@ Magic numbers to help the loader
??.word?start???@ absolute load/run zImage address
??.word?_edata??@ zImage end address
1:mov?r7, r1??? @ save architecture ID
??mov?r8, r2??? @ save atags pointer
? ......
wont_overwrite:?mov?r0, r4
??mov?r3, r7
??bl?decompress_kernel
??b?call_kernel
......
call_kernel:?bl?cache_clean_flush
??bl?cache_off
??mov?r0, #0???@ must be zero
??mov?r1, r7???@ restore architecture number
??mov?r2, r8???@ restore atags pointer
??mov?pc, r4???@ call kernel
......
首先,將u-boot傳遞過來的r1(機器碼)、r2(參數鏈表在內在中的物理地址)分別保存到ARM寄存器r7、r8中,再將r7作為參數傳遞給解壓函數decompress_kernel(),在這個解壓函數中再將r7傳遞給全局變量__machine_arch_type,然后在跳轉到vmlinux入口之前再將r7、r8還原到r1、r2中。
在arch/arm/kernel/head.S文件中,內核vmlinux入口的部分代碼如下:
ENTRY(stext)
????setmode????PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ ensure svc mode? @ and irqs disabled
????mrc????p15, 0, r9, c0, c0????????@ get processor id
????bl???? __lookup_processor_type???@ r5=procinfo r9=cpuid
????movs???r10, r5?????????????????? @ invalid processor (r5=0)?
????beq????__error_p???????????????? @ yes, error 'p'
????bl???? __lookup_machine_type?????@ r5=machinfo
????movs???r8, r5??????????????????? @ invalid machine (r5=0)?
????beq????__error_a???????????????? @ yes, error 'a'
????bl???? __vet_atags
????bl???? __create_page_tables
......
首先從ARM特殊寄存器(CP15)中獲得ARM內核的類型,從處理器內核描述符(proc_info_list)表(__proc_info_begin—__proc_info_end)中查詢有無此ARM 內核的類型,如果無就出錯退出。處理器內核描述符定義在include/asm-arm/procinfo.h中,具體的函數實現在 arch/arm/mm/proc-xxx.S中,在編譯連接過程中將各種處理器內核描述符組合成表。接著從機器描述(machine_desc)表(__mach_info_begin—__mach_info_end)中查詢有無r1寄存器指定的機器碼,如果沒有就出錯退出,所以這也說明了為什么在u-boot中指定的機器碼一定要與內核中指定的一致,否則內核就無法啟動。機器編號mach_type_xxx在arch/arm/tools/mach-types文件中說明,每個機器描述符中包括一個唯一的機器編號,機器描述符的定義在 include/asm-arm/mach/arch.h中,具體實現在arch/arm/mach-xxxx文件夾中,在編譯連接過程中將基于同一種處理器的不同機器描述符組合成表。例如,S3C2440處理器的機器碼為1008的機器描述符如下所示:
MACHINE_START(SMDK2440, "SMDK2440")
????/* Maintainer: Ben Dooks
????.phys_io?????? = S3C2410_PA_UART,
????.io_pg_offst???= (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
????.boot_params???= S3C2410_SDRAM_PA + 0x100,//注意:這個地址就是與u-boot中參數鏈表在內存中的物理地址相對應
????.init_irq????? = s3c24xx_init_irq,
????.map_io????????= smdk2440_map_io,
????.init_machine??= smdk2440_machine_init,
????.timer???????? = &s3c24xx_timer,
MACHINE_END
最后就打開MMU,并跳轉到 init/main.c的start_kernel()初始化系統。函數start_kernel()的部分代碼如下:
asmlinkage void __init start_kernel(void)
{
???......
???setup_arch(&command_line);
???......
}
?
函數setup_arch在arch/arm/kernel/setup.c中實現,部分代碼如下:
void __init setup_arch(char **cmdline_p)
{
???......
???setup_processor();
???mdesc = setup_machine(machine_arch_type);
???......
???parse_tags(tags);
???......
}
setup_processor()函數從處理器內核描述符表中找到匹配的描述符,并初始化一些處理器
變量。setup_machine()用機器編號(在解壓函數decompress_kernel 中被賦值)作為參數返回機器描述符。從機器描述符中獲得內核參數的物理地址,賦值給tags 變量。然后調用parse_tags()函數分析內核參數鏈表,把各個參數值傳遞給全局變量。這樣內核就收到了u-boot傳遞的參數。?
?
- tftp下載內核和nfs掛載文件系統
好了,上面tftp服務和nfs服務都已經準備好了,u-boot到kernel的參數傳遞也沒問題了,接下來就設置一下u-boot環境變量中的參數項和kernel的配置選項使之能使用tftp自動下載kernal和通過網絡自動掛載nfs文件系統。u-boot環境變量設置如下:
bootcmd參數項就是使用tftp把主機tftp主目錄下的uImage下載到開發板SDRAM中的0x31000000位置,接著使用bootm命令執行引導內核啟動。
而bootargs參數項就是內核啟動的命令行參數,u-boot就是把這個參數項傳遞給了內核,通過nfs掛載文件系統。這里一定要注意serverip和ipaddr的設置(即服務器IP或者開發主機IP和開發板的IP)。另外要注意,內核要能使用nfs也要配置相應的選項,如下:
File systems --->
????????Network File Systems --->
????????????<*> NFS file system support ## 必選
????????????????[*] Provide NFSv3 client support ## 可選
????????????[*] Root file system on NFS ## 必選
Networking --->
????????[*] Networking support
????????????Networking options --->
????????????????[*] IP: kernel level autoconfiguration ## 必選
?
運行結果如下:
a. tftp下載內核,并引導內核啟動:
?
b. u-boot傳遞的命令行參數被內核所接收:
c. 內核通過nfs掛載文件系統:
d.?查看掛載的nfs文件系統,發現完全與主機nfs服務器主目錄中的文件系統一致,說明成功!
?
?
[root@localhost home]# vi /etc/exports? //如果沒有這個文件就創建它,添加下面一行配置信息,注意格式一定要正確,否則導致服務不正常
/home/filesystem *(rw,no_root_squash,sync)
注釋:“/home/filesystem”是NFS服務器的主目錄,注意目錄的權限
?????“*”表示所有的IP都可以訪問NFS主目錄
?????“rw”表示可讀可寫
?????”no_root_squash“表示登入到NFS主機的用戶如果是ROOT用戶,他就擁有ROOT的權限
???? “sync”表示同步
?
[root@localhost home]# service nfs restart //重新啟動NFS服務,使配置文件生效
?
?? 測試NFS服務是否正常。將事先準備好的文件系統放到NFS主目錄下,如下:
[root@localhost home]# ls /home/filesystem/
bin??? dev? home????? lib????? mnt?? root? sum100? tmp? var
debug? etc? hostname? linuxrc? proc? sbin? sys???? usr
[root@localhost home]#
//在主機本地測試NFS服務,將NFS主目錄下的文件系統掛載到/mnt目錄下,192.168.1.101是主機的IP
[root@localhost home]#mount -o nolock -t nfs 192.168.1.101:/home/filesystem /mnt
?
?? 可以看到/mnt目錄下的內容和NFS主目錄/home/filesystem下的內容完全一致,說明NFS服務正常:
?
[root@localhost home]# vi /etc/xinetd.d/tftp
service tftp
{
????????disable???? = no
????????socket_type = dgram
????????protocol??? = udp
????????wait??????? = yes
????????user??????? = root
????????server????? = /usr/sbin/in.tftpd
????????server_args = -s /home/tftp-root -c??//主要是修改這里,指定tftp服務器的主目錄,-c選項是指可以創建文件
????????per_source? = 11
????????cps???????? = 100 2
????????flags?????? = IPv4
}
?
?? 創建剛才指定的tftp服務器主目錄,也要注意主目錄的可讀可寫的權限:
[root@localhost home]#mkdir /home/tftp-root
[root@localhost home]#chmod 777 /home/tftp-root
?
?? 啟動和測試tftp服務:
[root@localhost home]#service xinetd restart //重啟xinetd服務就會啟動其下的所有服務,也包括tftp服務
[root@localhost home]#service iptables stop //關閉防火墻
[root@localhost home]#tftp 主機IP地址?
?tftp>get?要下載的文件?
?tftp>put?要上傳的文件?
?tftp>q
[root@localhost home]#
評論
查看更多