.text
.global _start
_start:
b Reset @ 0x00: 發生復位異常時從地址零處開始運行
b HandleUndef @ 0x04: 未定義指令停止模式的向量地址
b HandleSWI @ 0x08: 管理模式的向量地址,經過SWI指令進入此模式
b HandlePrefetchAbort @ 0x0C: 指令預取終止致使的異常的向量地址
b HandleDataAbort @ 0x10: 數據訪問終止致使的異常的向量地址
b HandleNotUsed @ 0x14: 保留
b HandleIRQ @ 0x18: 中斷模式的向量地址
b HandleFIQ @ 0x1C: 快中斷模式的向量地址
Reset:
|
ldr r0, =0x53000000
|
ldr r0, =0x4C000014 @CLKDIVN register
|
@ SDRAM Init
|
ldr sp, =0x32FFF000 @設置堆棧
|
以開發板上使用的K9F1208爲例,每一個頁(page)爲512Byte數據和16Byte校驗,每一個塊(Block)爲32個頁,即16KByte數據和512Byte校驗。linux
void nand_init(void)
NFCONF = 0x300;
|
for(i=start_addr; i < (start_addr + size);)
NFCMMD = 0; //發出READ0命令
|
|
這些都是TAG的類型,注意這些整數跟地址沒有關係,只是一個用來識別標記類型的符號而已。數組
每一個Tag都用結構體表示,包含TagHeader 頭結構體以及隨後的參數值數據結構。如 ATAG_CORE:緩存
struct Atag {服務器
struct TagHeader stHdr;數據結構
struct TagCore stCore;async
};ide
其中包含兩個結構體。第一個結構體TagHeader含兩個整型變量,用以表示本結構體的長度、標記類型;nSzie賦值爲頭部TagHeader和數據TagCore的大小之和,注意是以字(即4字節)爲單位;ulTag 就賦值爲先前定義的宏ATAG_CORE。第二個結構體就是實際的數據了。函數
|
|
其中涉及到的全部數據結構都可在 Linux 內核源碼的include/asm/setup.h 頭文件找到,咱們把這些定義放在Bootloader的頭文件atag.h中
啓動參數標記列表以標記 ATAG_CORE 開始,以標記 ATAG_NONE 結束。每一個標記由標識被傳遞參數的 tag_header 結構以及隨後的參數值數據結構來組成。數據結構 tag 和 tag_header 定義在 Linux 內核源碼的include/asm/setup.h 頭文件中,在咱們的S-Boot中對應的頭文件爲 atag.h。
在嵌入式 Linux 系統中,一般須要由 Boot Loader 設置的常見啓動參數有:ATAG_CORE、ATAG_MEM、ATAG_CMDLINE、ATAG_RAMDISK、ATAG_INITRD等。
向內核傳遞參數的方法,先在內存中某個起始地址開始,連續存放多個Tag, 組成Tag列表。列表中的每一個Tag包括頭部TagHeader和數據結構體。按規定,第一個Tag必須是ATAG_CORE, 最末一個Tag必須是ATAG_NONE,並且中間必須包含至少一個ATAG_MEM。 注意的是末尾的ATAG_NONE只包括頭部,沒有數據內容。如圖所示。
在編程時先定義好起始地址,而後用一個指針,每設置完畢一個Tag的內容就向後移動相應的長度,而後設置下一個Tag內容,以保證各個Tag的連續存放。
下面具體說明幾個關鍵Tag的數據區域內容的設置。struct TagCore結構體已經在前面列出,它包含三個整型變量,ulFlags通常設爲零,nPageSize表示分頁內存管理中每一頁的大小,通常爲4096字節,ulRootDev是系統啓動的設備號,設爲零便可,由於一般在後面的命令行參數Cmdline中覆蓋這個設置。Struct TagMem用來描述系統的物理內存地址空間,定義以下:
|
其中nSzie表示內存的總大小,ulStart爲內存的起始物理地址,兩者結合告訴內核系統可用的物理內存空間是哪些。Struct TagCmdline結構體的定義就更簡單了,只是一個字符數組,初始長度爲1,以下所示:
|
實際上命令行參數不可能只有一個字節,咱們一般使用strcpy函數把命令行參數拷貝到cCmdLine地址處,在結尾附加一個字符串結束符’\0’,而後用strlen函數得到cCmdLine數組的實際長度(包括字符串結束符)。常見的命令行參數如:root=/dev/mtdblock2 init=/linuxrc console=ttySAC0,115200 mem=65536。咱們知道的是,Bootloader以標記列表的形式向內核傳遞的參數,大概有10種不一樣類型的Tag,而命令行參數只是其中的一種。其它須要設置的Tag包括ATAG_RAMDISK、ATAG_INITRD等,此處再也不詳細介紹。
在咱們的S-Boot中設置了ATAG_CORE,ATAG_MEM,ATAG_CMDLINE,ATAG_NONE 四項。其中CmdLine 使用的是:
const char *CmdLine = "root=/dev/nfs nfsroot=192.168.1.249:/home/hongwang/mkrootfs/rootfs ip=192.168.1.252:192.168.1.249:192.168.1.1:255.255.255.0:hwlee.net:eth0:off console=ttySAC0,115200 init=/linuxrc mem=65536K console=tty1 fbcon=rotate:2";
這裏root=/dev/nfs表示使用NFS作根文件系統,注意並不真的存在/dev/nfs這個設備,它只是一個符號而已,告訴內核使用NFS而不是使用真正的設備作根文件系統。
nfsroot=[<server-ip>:]<root-dir>[,<nfs-options>]
nfsroot=192.168.1.249:/home/hongwang/mkrootfs/rootfs是NFS服務器地址及要掛載的目錄。
ip=<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<autoconf>
ip=192.168.1.252:192.168.1.249:192.168.1.1:255.255.255.0:hwlee.net:eth0:off
只說明一下autoconf,這一個選項指明開發板使用的自動配置IP地址的方法,有時開發板能夠設置成經過DHCP或者BOOTP等協議從服務器獲取IP地址。off 或 none 表示不使用自動配置,使用指定的靜態IP地址信息。
console=ttySAC0,115200 串口控制檯
console=tty1 fbcon=rotate:2 液晶屏Framebuffer控制檯,若是內核支持,能夠在LCD屏幕上顯示Linux內核啓動過程,起點結束後在LCD屏幕上進入Shell控制檯供用 戶操做。fbcon=rotate:2表示控制檯旋轉180度,若爲1表示旋轉90度,3旋轉270度,0不旋轉。
zImage 二進制文件包含兩部份內容,起始部分是解壓縮程序,後面是壓縮的內核。解壓縮程序是最早運行的,內核中文件是:arch/arm/boot /compressed/head.S,它負責把壓縮的內核解壓到0x30008000處。所以zImage能夠下載到RAM任意位置處,由解壓縮程序負 責搬移到正確的運行地址。
因此 Bootloader啓動Linux內核的方法就是直接跳轉到內核的第一條指令處,也就是跳轉到內存中存放內核映像的開始地址,內核映像具備自解壓功能, 會把本身釋放到正確的運行地址。Tag列表怎樣傳給內核呢?使用的方法是把Tag列表的起始地址傳給內核。首先,定義一個指向函數的指針:
typedef void (*LINUX_KERNEL_ENTRY)(int, int, UINT32);
LINUX_KERNEL_ENTRY pfExecKernel;
這樣pfExecKernel就是一個函數指針,函數具備三個整型變量。而後,讓pfExecKernel指向內核映像的起始地址處,這裏使用強制類型轉換把地址轉換成函數指針類型:
pfExecKernel = (LINUX_KERNEL_ENTRY)pKernelStartAddr;
最後,以三個參數調用pfExecKernel函數:
pfExecKernel(0, MACH_ID, ATAG_BASE);
其中第一個參數默認爲零,能夠沒必要理會。第二個參數是機器ID號,不一樣的CPU有不一樣的號碼與之對應,能夠在內核源代碼的linux/arch/arm/tools/mach-types 文件中查到,S3C2440 對應的MACH_ID 爲362。第三個參數ATAG_BASE就是上文講到的Tag列表的首地址。
綜上所述,包含最基本功能的S-Boot運行流程已經很清楚了。下圖對此做了一個總結。