一、Bootloader 的引入
1.1 Bootloader 的引入
Linux 內(nèi)核的啟動是需要一定的必要條件的,但在 CPU 剛上電啟動時(shí),一般連內(nèi)存控制 器都沒有配置過,根本無法在內(nèi)存中運(yùn)行程序,更不可能處在 Linux 內(nèi)核的啟動環(huán)境中。為 了初始化 CPU 及其他外設(shè),使得 Linux 內(nèi)核可以在系統(tǒng)主存中跑起來,并讓系統(tǒng)符合 Linux 內(nèi)核啟動的必備條件,必須要有一個先于內(nèi)核運(yùn)行的程序,他就是所謂的引導(dǎo)加載程序: Bootloader。
Bootloader并不是只有Linux才需要,是幾乎所有的運(yùn)行操作系統(tǒng)的設(shè)備都必須具備的。 PC電腦的BIOS就是bootloader的一部分,對于Linux PC來說:Bootloader = BIOS + GRUB/LILO。
1.2 嵌入式 Linux 系統(tǒng)軟件結(jié)構(gòu)與分布
一般情況下嵌入式 Linux 系統(tǒng)中軟件主要由以下幾個部分組成:
1.引導(dǎo)加載程序:其中包括內(nèi)部ROM中的固化啟動代碼和bootloader兩部分。固化ROM 是廠家在芯片生產(chǎn)時(shí)固化,用于引導(dǎo) bootloader。
2.Linux Kernel 和Drivers。
3.文件系統(tǒng):包括根文件系統(tǒng)和建立于Flash 內(nèi)存設(shè)備之上的文件系統(tǒng)(ext4、UBI、 CRAMFS 等)。它是提供管理系統(tǒng)的各種配置文件以及系統(tǒng)執(zhí)行用于應(yīng)用程序的良好運(yùn)行環(huán) 境的載體。
4.應(yīng)用程序:用于自定義的應(yīng)用程序,存放于文件系統(tǒng)之中。 在Flash 存儲器中,上面四個部分的分布如圖 1 所示:
圖1:嵌入式Linux的軟件分布
但是以上只是大部分情況下的分布,也有一些根文件系統(tǒng)可能是 initramfs,被一起壓縮到了內(nèi)核映像里,或者沒有 bootloader 參數(shù)區(qū)等。
二、Bootloader 介紹
2.1 Bootloader 的功能
Bootloader是在操作系統(tǒng)內(nèi)核運(yùn)行之前運(yùn)行的一段小程序,通過它我們可以初始化硬件設(shè)備,從而將系統(tǒng)的軟硬件環(huán)境帶到一個合適的狀態(tài),以便為最終調(diào)用操作系統(tǒng)內(nèi)核準(zhǔn)備好 正確的環(huán)境,最后從別處(Flash、以太網(wǎng)、UART 等)載入內(nèi)核映像并跳到入口地址運(yùn)行。
簡單的說,Bootloader 就是這么一小段程序,它在系統(tǒng)上電時(shí)開始執(zhí)行,初始化硬件設(shè)備、準(zhǔn)備好軟件環(huán)境,最后調(diào)用操作系統(tǒng)內(nèi)核。
可以增強(qiáng) Bootloader 的功能,比如增加網(wǎng)絡(luò)功能、從 PC 上通過串口或網(wǎng)絡(luò)下載文件、 燒寫文件、將 Flash 上壓縮的文件解壓后再運(yùn)行等,這就是一個功能更為強(qiáng)大的 Bootloader, 也稱為 Monitor。實(shí)際上,在最終產(chǎn)品中用戶并不需要這些功能,他們只是為了方便開發(fā)。
2.2 Bootloader 的特點(diǎn)
由于 Bootloader 直接操作硬件,因此它嚴(yán)重依賴于硬件,且依據(jù)所引導(dǎo)的操作系統(tǒng)的不同而不同。在嵌入式世界中建立一個通用的 Bootloader 幾乎是不可能的,而有可能的是 讓一個 Bootloader 代碼支持多種不同的架構(gòu)和操作系統(tǒng),并讓他方便移植。Bootloader的啟動過程通常是多階段的,這樣既能夠提供復(fù)雜的功能,又具有更好的可移植性。
大多數(shù) Bootloader 都包含兩種不同的操作模式:本地加載模式和遠(yuǎn)程下載模式。遠(yuǎn)程下載模式只對開發(fā)人員才有意義。
Bootloader 都映射在 CPU復(fù)位后運(yùn)行的第一條指令的地址處,以保證系統(tǒng)上電或復(fù)位 后首先執(zhí)行 Bootloader。
2.3 Bootloader 的分類
首先區(qū)分一下“Bootloader”和“Monitor”的概念:嚴(yán)格的講,“Bootloader”只是引 導(dǎo)設(shè)備并執(zhí)行主程序的固件;而“Monitor”還提供更多的命令行接口,可進(jìn)行調(diào)試、讀寫內(nèi)存、燒寫 Flash、配置環(huán)境變量等?!癕onitor”在嵌入式系統(tǒng)開發(fā)過程中可以提供更好的調(diào)試功能,開發(fā)完成后,就完全設(shè)置成一個“Bootloader”了。所以習(xí)慣上將他們統(tǒng)稱為 Bootloader。表 2.1 為常見的開放源碼的 Linux 引導(dǎo)程序。
表2.1 常見的開放源碼 Linux 引導(dǎo)程序
其中 U-Boot 支持大多 CPU,可以燒寫 EXT2、JFFS2 文件系統(tǒng)映像,支持串口下載、網(wǎng)絡(luò) 下載,并提供了大量的命令。
2.4 Bootloader 的啟動模式
Bootloader 的主要功能是引導(dǎo)操作系統(tǒng),但在開發(fā)時(shí),通常需要使用各種命令操作 Bootloader,一般通過串口來連接 PC 和開發(fā)板,可以在串口上輸入各種命令、觀察運(yùn)行結(jié)果等。這也只是對開發(fā)人員才有意義,用戶使用產(chǎn)品時(shí)是不用接串口來控制 Bootloader 的。 從這個角度可以將 Bootloader 分為啟動加載(Boot loading)模式和下載(Down loading)模 式。
2.4.1 啟動加載(Bootloading)模式
這種模式也稱為"自主"(Autonomous)模式。也即 Boot Loader 從目標(biāo)機(jī)上的某個固態(tài)存儲設(shè)備上將操作系統(tǒng)加載到 RAM 中運(yùn)行,整個過程并沒有用戶的介入。這種模式是 Boot Loader 的正常工作模式,因此在嵌入式產(chǎn)品發(fā)布的時(shí)侯,Bootloader顯然必須工作在這種模 式下。
2.4.2 下載(Downloading)模式
在這種模式下,目標(biāo)機(jī)上的 Boot Loader 將通過串口連接或網(wǎng)絡(luò)連接等通信手段從主機(jī)(Host)下載文件,比如:下載內(nèi)核映像和根文件系統(tǒng)映像等。從主機(jī)下載的文件通常首先 被 Boot Loader 保存到目標(biāo)機(jī)的 RAM 中,然后再被 Boot Loader 寫到目標(biāo)機(jī)上的 FLASH 類固 態(tài)存儲設(shè)備中。Boot Loader 的這種模式通常在第一次安裝內(nèi)核與根文件系統(tǒng)時(shí)被使用;此 外,以后的系統(tǒng)更新也會使用Boot Loader的這種工作模式。工作于這種模式下的Boot Loader 通常都會向它的終端用戶提供一個簡單的命令行接口。
像 Blob 或 U-Boot 等這樣功能強(qiáng)大的 Boot Loader 通常同時(shí)支持這兩種工作模式,而且 允許用戶在這兩種工作模式之間進(jìn)行切換。比如,Blob 在啟動時(shí)處于正常的啟動加載模式, 但是它會延時(shí) 10 秒等待終端用戶按下任意鍵而將 blob 切換到下載模式。如果在 10 秒內(nèi)沒 有用戶按鍵,則 blob 繼續(xù)啟動 Linux 內(nèi)核。
2.4.3 下載模式之網(wǎng)絡(luò)啟動方式
這種方式開發(fā)板不需要配置較大的存儲介質(zhì)(跟無盤工作站有點(diǎn)類似),但是使用這種 啟動方式之前,需要把 Bootloader 安裝到板上的 EPPROM 或者 Flash 中。Bootloader 通過以 太網(wǎng)接口遠(yuǎn)程下載 Linux 內(nèi)核映像或者文件系統(tǒng)到 RAM 中運(yùn)行。這種方式對于嵌入式系統(tǒng)開發(fā)來說非常重要。
使用這種方式的前提條件,就是目標(biāo)板有串口、以太網(wǎng)接口、USB 接口或者其他鏈接方式:串口一般作為控制臺,同時(shí)可以用來下載內(nèi)核映像和 RAMDISK 文件系統(tǒng);用網(wǎng)絡(luò)接口 來掛載 NFS 文件系統(tǒng);也可以使用 USB 接口虛擬成以太網(wǎng)口來通訊。
使用網(wǎng)絡(luò)啟動方式,還需要在服務(wù)器上配置啟動相關(guān)網(wǎng)絡(luò)服務(wù):使用 TFTP 服務(wù)為 Bootloader 客戶端提供文件下載功能;DHCP 服務(wù)動態(tài)為 Bootloader 設(shè)置 IP 地址,配置網(wǎng)絡(luò) 參數(shù)。網(wǎng)絡(luò)啟動方式如圖 2.1 所示。
圖2.1 網(wǎng)絡(luò)方式啟動系統(tǒng)
2.5 Bootloader 的啟動流程
Bootloader 的啟動過程可以分為單階段(Single Stage)、多階段(Multi-Stage)兩種。通 常多階段的 Bootloader 能提供更為復(fù)雜的功能以及更好的可移植性。從固態(tài)存儲設(shè)備上啟動 的Bootloader 大多都是兩個階段的啟動過程。第一階段使用匯編來實(shí)現(xiàn),它完成一些依賴于 CPU 體系結(jié)構(gòu)的初始化,并調(diào)用第二階段的代碼;第二階段則通常使用 C 語言來實(shí)現(xiàn),這樣 可以實(shí)現(xiàn)更復(fù)雜的功能,而且代碼會有更好的可讀性和可移植性。
一般而言,這兩個階段完成的功能可以如下分類:
2.5.1 Bootloader 第一階段的功能
Bootloader 在第一階段主要完成以下功能:
硬件設(shè)備初始化;
為加載 Bootloader 的第二階段代碼準(zhǔn)備 RAM 空間;
復(fù)制 Bootloader 的第二階段代碼到 RAM 空間中;
設(shè)置好棧;
跳轉(zhuǎn)到第二階段代碼的 C 入口點(diǎn);
在第一階段進(jìn)行的硬件初始化一般包括:關(guān)閉 WATCHDOG、關(guān)中斷、設(shè)置 CPU 的速度 和時(shí)鐘頻率、RAM 初始化等。這些并不都是必須的,比如 S3C2410/S3C2440 的開發(fā)板所使 用的 U-Boot 中,就將 CPU 的速度和時(shí)鐘頻率放在第二階段進(jìn)行設(shè)置。
甚至,將第二階段的代碼復(fù)制到 RAM 空間也不是必須的,對于 Nor Flash 等支持 XIP 的 存儲設(shè)備,完全可以在上面直接執(zhí)行代碼,只不過相比在 RAM 中執(zhí)行效率大為降低。
2.5.2 Bootloader 第二階段的功能
Bootloader 在第二階段主要完成以下功能:
初始化本階段要使用到的硬件設(shè)備;
檢測系統(tǒng)內(nèi)存映射(Memory map);
將內(nèi)核映像和根文件系統(tǒng)映像從 Flash 上讀到 RAM 空間中;
為內(nèi)核設(shè)置啟動參數(shù);
調(diào)用內(nèi)核;
為了方便開發(fā),只要要初始化一個串口以便程序員與 Bootloader 進(jìn)行交互。
所謂檢測內(nèi)存映射,就是確定板上使用了多少內(nèi)存、他們的地址空間是什么。由于嵌入 式開發(fā)中的 Bootloader 多是針對某類板子進(jìn)行編寫,所以可以根據(jù)板子的情況直接設(shè)置,不 需要考慮可以適用于各類情況的復(fù)雜算法。
Flash 上的內(nèi)核映像有可能是經(jīng)過壓縮的,在讀到 RAM 之后,還需要進(jìn)行解壓。當(dāng)然, 對于有自解壓功能的內(nèi)核,不需要Bootloader 來解壓。
將根文件系統(tǒng)映像復(fù)制到 RAM 中并不是必須的,這取決于是什么類型的根文件系統(tǒng),以及內(nèi)核訪問它的方法。
將內(nèi)核放在適當(dāng)?shù)奈恢煤?,在跳入?zhí)行內(nèi)核之前,需要根據(jù)內(nèi)核啟動的需求,配置相應(yīng) 的啟動參數(shù)。如 Linux 內(nèi)核的啟動要求如表 2.2 所示。
表2.2 Linux 內(nèi)核啟動條件
在 C 語言中,可以像下列示例代碼一樣來調(diào)用內(nèi)核:
void (*theKernel)(int zero, int arch, u32params_addr) = (void (*)(int, int, u32))KERNEL_RAM_BASE;
theKernel(0,ARCH_NUMBER, (u32)kernel_params_start);
2.6 Bootloader 與內(nèi)核的交互
U-Boot 與內(nèi)核之間的交互是單向的,U-Boot 將各類參數(shù)傳遞給內(nèi)核。由于他們(U-Boot 與內(nèi)核)不能同時(shí)運(yùn)行,傳遞的辦法只有一個:U-Boot 將參數(shù)放在放在某個約定的地方之 后,再啟動內(nèi)核,內(nèi)核啟動后從這個地方獲得參數(shù)。
除了約定好參數(shù)存放的地址外,還要規(guī)定參數(shù)的結(jié)構(gòu)。Linux 2.4.x 以后的內(nèi)核都期望以標(biāo)記列表(tagged list)的形式來傳遞啟動參數(shù)。標(biāo)記,就是一種數(shù)據(jù)結(jié)構(gòu);標(biāo)記列表,就 是挨著存放的多個標(biāo)記。標(biāo)記列表以標(biāo)記 ATAG_CORE 開始,以標(biāo)記 ATAG_NONE 結(jié)束。
標(biāo)記的數(shù)據(jù)結(jié)構(gòu)為tag,它由一個tag_header結(jié)構(gòu)和一個聯(lián)合體(union)組成。tag_header 結(jié)構(gòu)標(biāo)示標(biāo)記的類型及長度,比如是表示內(nèi)存還是表示命令行參數(shù)等。對于不同類型的標(biāo)記使用不同的聯(lián)合體(union),比如表示內(nèi)存時(shí)使用tag_mem32,表示命令時(shí)使用tag_cmdline。
數(shù)據(jù)結(jié)構(gòu) tag 和tag_header 定義在 Linux 內(nèi)核源碼的include/asm/setup.h 頭文件中(在 U-Boot 的 include/asm-arm/目錄下的 setup.h 中也有定義),如下所示:
struct tag_header {
u32 size;
u32 tag;
};
struct tag {
struct tag_header hdr;
union {
struct tag_core core;
struct tag_mem32 mem;
struct tag_videotext videotext;
struct tag_ramdisk ramdisk;
struct tag_initrd initrd;
struct tag_serialnr serialnr;
struct tag_revision revision;
struct tag_videolfb videolfb;
struct tag_cmdline cmdline;
/*
* Acorn specific
*/
struct tag_acorn acorn;
/*
* DC21285 specific
*/
struct tag_memclk memclk;
} u;
};
下面以設(shè)置內(nèi)存標(biāo)記、命令標(biāo)記為例說明參數(shù)的傳遞。
2.6.1 設(shè)置標(biāo)記ATAG_CORE
標(biāo)記列表以標(biāo)記 ATAG_CORE 開始,其結(jié)構(gòu)體定義如下:
/* The list must start with an ATAG_COREnode */
#define ATAG_CORE 0x54410001
struct tag_core {
u32 flags; /* bit 0 =read-only */
u32 pagesize;
u32 rootdev;
};
假設(shè) Bootloader與內(nèi)核約定的參數(shù)存放地址為 0x30000100,則可以以如下代碼設(shè)置標(biāo) 記 ATAG_CORE:
#define tag_next(t) ((struct tag *)((u32*)(t) + (t)->hdr.size))
static struct tag *params; //定義全局變量 params,為 struct tag 結(jié)構(gòu)
static void setup_start_tag (bd_t *bd)
{
params = (struct tag *) bd->bi_boot_params; //bi_boot_params =0x30000100
params->hdr.tag = ATAG_CORE;// ATAG_CORE 在前面定義,指定標(biāo)記類型
params->hdr.size = tag_size (tag_core); //計(jì)算標(biāo)記大小
// 設(shè)置 ATAG_CORE 標(biāo)記的內(nèi)容
params->u.core.flags = 0;
params->u.core.pagesize = 0;
params->u.core.rootdev = 0;
params = tag_next (params);
}
其中,tag_next定義為指向當(dāng)前標(biāo)記的末尾。
2.6.2 設(shè)置內(nèi)存標(biāo)記
內(nèi)存標(biāo)記 tag_mem32 的定義如下:
struct tag_mem32 {
u32 size; // 內(nèi)存的大小
u32 start; /* physical start address */
};
假設(shè)開發(fā)板使用的內(nèi)存起始地址為 0x30000000,大小為 0x4000000,則內(nèi)存標(biāo)記可以如 下設(shè)置:
static void setup_memory_tags (bd_t *bd)
{
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);
}
2.6.3 設(shè)置命令行標(biāo)記
命令行就是一個字符串,它被用來控制內(nèi)核的一些行為。比如“root=/dev/mtdlock2 init=/linuxrc console=ttySAC0”表示根文件系統(tǒng)在 MTD2 分區(qū)上,系統(tǒng)啟動后執(zhí)行的第一個程序?yàn)?linuxrc,控制臺為 ttySAC0。
命令行可以在 Bootloader 中通過命令設(shè)置好,然后按如下構(gòu)造標(biāo)記傳給內(nèi)核。
char *p = ”root=/dev/mtdlock 2init=/linuxrc console=ttySAC0”;
params->hdr.tag = ATAG_CMDLINE;
params->hdr.size = (sizeof (structtag_header) + strlen (p) + 1 + 4) >> 2;
strcpy (params->u.cmdline.cmdline,p);
params = tag_next (params);
2.6.4 設(shè)置標(biāo)記ATAG_NONE
標(biāo)記列表以標(biāo)記 ATAG_NONE 結(jié)束,如下設(shè)置:
static void setup_end_tag (bd_t *bd)
{
params->hdr.tag = ATAG_NONE;
params->hdr.size = 0;
}
-
嵌入式
+關(guān)注
關(guān)注
5145文章
19597瀏覽量
316141 -
Linux
+關(guān)注
關(guān)注
87文章
11496瀏覽量
213225 -
bootloader
+關(guān)注
關(guān)注
2文章
238瀏覽量
46557
原文標(biāo)題:詳談嵌入式之Bootloader
文章出處:【微信號:gh_c472c2199c88,微信公眾號:嵌入式微處理器】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
嵌入式Linux啟動時(shí)間優(yōu)化的秘密之五-Bootloader


基于網(wǎng)絡(luò)加載的嵌入式BootLoader的設(shè)計(jì)與實(shí)現(xiàn)
基于網(wǎng)絡(luò)加載的嵌入式BootLoader有什么優(yōu)點(diǎn)?
嵌入式系統(tǒng)BootLoader 移植
基于MIPS64的嵌入式Linux Bootloader的移
嵌入式考試筆記之嵌入式系統(tǒng)基礎(chǔ)知識
基于Xilinx FPGA特點(diǎn)的嵌入式Bootloader設(shè)計(jì)與實(shí)現(xiàn)

基于嵌入式Linux系統(tǒng)的Bootloader模型在MIPS64上的移植設(shè)計(jì)淺析
VScode嵌入式開發(fā)之入門教程

評論