bootloader(3)15.1.2 Bootloader的結構和啟動過程 1. 概述 在移植之前先瞭解Bootloader的一些通用概念,對理解它的代碼會有所幫助。 在一個嵌入式Linux系統中,從軟件的角度通常可以分為4個層次: (1)引導加載程序,包括固化在固件(firmware)中的 boot 代碼(可選)和Bootloader兩大部分。 有些CPU在運行Bootloader之前先運行一段固化的程序(固件,firmware),比如x86結構的CPU就是先運行BIOS中的固件,然後才運行硬盤第一個分區(MBR)中的Bootloader。 在大多嵌入式系統中並沒有固件,Bootloader是上電後執行的第一個程序。 (2)Linux內核。 特定於嵌入式板子的定制內核以及內核的啟動參數。內核的啟動參數可以是內核默認的,或是由Bootloader傳遞給它的。 (3)文件系統。 包括根文件系統和建機車借款立於Flash內存設備之上的文件系統。裡面包含了Linux系統能夠運行所必需的應用程序、庫等,比如可以給用戶提供操作Linux的控制界面的shell程序,動態連接的程序運行時需要的glibc或uClibc庫,等等。 (4)用戶應用程序。 特定於用戶的應用程序,它們也存儲在文件系統中。有時在用戶應用程序和內核層之間可能還會包括一個嵌入式圖形用戶界面。常用的嵌入式 GUI 有:Qtopia 和 MiniGUI 等。 顯然,在嵌入系統的固態存儲設備上有相應的分區來存儲它們,圖15.1是一個典型的分區結構。 圖15.1 嵌入式Linux系統中的典型分區結構 「Boot parameters」分區中存放一些可設置的參數,比如IP地址、串口波特率、要傳遞給內核的命令行參數等。正常啟動過程中,Bootloader首先 運行,然後它將內核覆制到內存中(房地產也有些內核可以在固態存儲設備上直接運行),並且在內存某個固定的地址設置好要傳遞給內核的參數,最後運行內核。內核啟 動之後,它會掛接(mount)根文件系統(「Root filesystem」),啟動文件系統中的應用程序。 2. Bootloader的兩個階段 Bootloader的啟動過程啟動過程可以分為單階段(Single Stage)、多階段(Multi-Stage)兩種。通常多階段的Bootloader能提供更為複雜的功能,以及更好的可移植性。從固態存儲設備上啟 動的Bootloader大多都是 2 階段的啟動過程。這從前面的硬件實驗可以很好地理解這點:第一階段使用彙編來實現,它完成一些依賴於 CPU 體系結構的初始化,並調用第二階段的代碼。第二階段則通常使用C語言來實現,這樣可以實現更複雜的功能,而且代碼會有更好的可讀性和可移植性。 一太平洋房屋般而言,這兩個階段完成的功能可以如下分類,但這不是絕對的: (1)Bootloader第一階段的功能。硬件設備初始化。 為加載Bootloader的第二階段代碼準備RAM空間。 拷貝Bootloader的第二階段代碼到 RAM 空間中。 設置好棧。 跳轉到第二階段代碼的C入口點。 在第一階段進行的硬件初始化一般包括:關閉WATCHDOG、關中斷、設置CPU的速度和時鐘頻率、RAM初始化等。這些並不都是必需的,比如S3C2410/S3C2440的開發板所使用的U-Boot中,就將CPU的速度和時鐘頻率的設置放在第二階段。 甚至,將第二階段的代碼複製到RAM空間中也不是必需的,對於NOR Flash等存儲設備,完全可以在上面直接執行代碼,只不過這相比在RAM中執行效率大為降低。 (2)Bootloader第二階段的功能。初始化本階段要使用到的硬件設備。融資 檢測系統內存映射(memory map)。 將內核映像和根文件系統映像從Flash上讀到RAM空間中。 為內核設置啟動參數。 調用內核。 為了方便開發,至少要初始化一個串口以便程序員與Bootloader進行交互。 所謂檢測內存映射,就是確定板上使用了多少內存,它們的地址空間是什麼。由於嵌入式開發中,Bootloader多是針對某類板子進行編寫,所以可以根據板子的情況直接設置,不需要考慮可以適用於各類情況的複雜算法。 Flash上的內核映像有可能是經過壓縮的,在讀到RAM之後,還需要進行解壓。當然,對於有自解壓功能的內核,不需要Bootloader來解壓。 將根文件系統映像複製到RAM中,這不是必需的。這取決於是什麼類型的根文件系統,以及內核訪問它的方法。 為內核設置啟動參數將在下一小節介紹。 將內核存放吳哥窟在適當的位置後,直接跳到到它的入口點即可調用內核。調用內核之前,下列條件要滿足: (1)CPU 寄存器的設置。R0=0 R1=機器類型ID;對於ARM結構的CPU,其機器類型ID可以參見 linux/arch/arm/tools/mach-types。 R2=啟動參數標記列表在 RAM 中起始基地址 (2)CPU工作模式。必須禁止中斷(IRQs和FIQs) CPU 必須 SVC 模式 (3)Cache 和 MMU 的設置。MMU 必須關閉 指令 Cache 可以打開也可以關閉 數據 Cache 必須關閉 如果用C語言,可以像下列示例代碼一樣來調用內核: void (*theKernel)(int zero, int arch, u32 params_addr) = (void (*)(int, int, u32))KERNEL_RAM_BASE; …… theKernel(0, ARCH_NUMBER, (u32) kernel_params_start); 3. Bootloader與內核的交互 Bootloader與內核的交互關鍵字廣告是單向的,Bootloader將各類參數傳給內核。由於它們不能同時運行,傳遞辦法只有一個:Bootloader將參數放在某個約定的地方之後,再啟動內核,內核啟動後從這個地方獲得參數。 除了約定好參數存放的地址外,還要規定參數的結構。Linux 2.4.x 以後的內核都期望以標記列表(tagged list)的形式來傳遞啟動參數。標記,就是一種數據結構;標記列表,就是挨著存放的多個標記。標記列表以標記ATAG_CORE 開始,以標記ATAG_NONE 結束。標記的數據結構為tag,它由一個tag_header結構和一個聯合(union)組成。tag_header結構表示標記的類型及長度,比如是 表示內存還是表示命令行參數等。對於不同類型的標記使用不同的聯合(union),比如表示內存時使用tag_mem32,表示命令行時使用 tag_cmdline。數據結構tag和辦公室出租tag_header定義在Linux內核源碼的include/asm/setup.h頭文件中: struct tag_header { u32 size; u32 tag; struct tag { struct tag_header hdr; union { struct tag_corecore; struct tag_mem32mem; struct tag_videotextvideotext; struct tag_ramdiskramdisk; struct tag_initrdinitrd; struct tag_serialnrserialnr; struct tag_revisionrevision; struct tag_videolfbvideolfb; struct tag_cmdlinecmdline; * Acorn specificstruct tag_acornacorn; * DC21285 specificstruct tag_memclkmemclk; } u; 下面以設置內存標記、命令行標記為例說明參數的傳遞: (1)設置標記 ATAG_CORE。 標記列表以標記 ATAG_CORE開始,假設Bootloader與內核約定的參數存放地址為0x30000100,則可以以如下代汽車借款碼設置標記 ATAG_CORE: params = (struct tag *) 0x30000100; params->hdr.tag = ATAG_CORE; params->hdr.size = tag_size (tag_core); params->u.core.flags = 0; params->u.core.pagesize = 0; params->u.core.rootdev = 0; params = tag_next (params); 其中,tag_next定義如下,它指向當前標記的末尾: #define tag_next(t)((struct tag *)((u32 *)(t) + (t)->hdr.size)) (2)設置內存標記。 假設開發板使用的內存起始地址為0x30000000,大小為0x4000000,則內存標記可以如下設置: params->hdr.tag = ATAG_MEM; params->hdr.size = tag_size (tag_mem32); params->u.mem.start = 0x30000000; params->u.mem.size = 0x4000000; params = tag_next 個人信貸(params); (3)設置命令行標記。 命令行就是一個字符串,它被用來控制內核的一些行為。比如"root=/dev/mtdblock2 init="/linuxrc" console="ttySAC0""表示根文件系統在MTD2分區上,系統啟動後執行的第一個程序為/linuxrc,控制台為ttySAC0(即第一 個串口)。 命令行可以在Bootloader中通過命令設置好,然後如下構造標記傳給內核: char *p = "root=/dev/mtdblock2 init="/linuxrc" console="ttySAC0""; params->hdr.tag = ATAG_CMDLINE; params->hdr.size = (sizeof (struct tag_header) + strlen (p) + 1 + 4) >> 2; strcpy (params->u.cmdline.cmdline, p); params = tag_next (params); (4)設置標記ATAG_NONE。 標記列表以標記ATAG_NONE結束,如下設置: 結婚params->hdr.tag = ATAG_NONE;
創作者介紹

rk63rkwsuh 發表在 痞客邦 PIXNET 留言(0) 人氣()