日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

嵌入式BootLoader技术内幕(二)

發(fā)布時(shí)間:2025/3/8 编程问答 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 嵌入式BootLoader技术内幕(二) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
三、Boot Loader 的主要任務(wù)與典型結(jié)構(gòu)框架

在繼續(xù)本節(jié)的討論之前,首先我們做一個(gè)假定,那就是:假定內(nèi)核映像與根文件系統(tǒng)映像
都被加載到 RAM 中運(yùn)行。之所以提出這樣一個(gè)假設(shè)前提是因?yàn)?#xff0c;在嵌入式系統(tǒng)中內(nèi)核映像
與根文件系統(tǒng)映像也可以直接在 ROM 或 Flash 這樣的固態(tài)存儲(chǔ)設(shè)備中直接運(yùn)行。但這種
做法無疑是以運(yùn)行速度的犧牲為代價(jià)的。從操作系統(tǒng)的角度看,Boot Loader 的總目標(biāo)就
是正確地調(diào)用內(nèi)核來執(zhí)行。

另外,由于 Boot Loader 的實(shí)現(xiàn)依賴于 CPU 的體系結(jié)構(gòu),因此大多數(shù) Boot Loader 都分
為 stage1 和 stage2 兩大部分。依賴于 CPU 體系結(jié)構(gòu)的代碼,比如設(shè)備初始化代碼等,
通常都放在 stage1 中,而且通常都用匯編語言來實(shí)現(xiàn),以達(dá)到短小精悍的目的。而 sta
ge2 則通常用C語言來實(shí)現(xiàn),這樣可以實(shí)現(xiàn)給復(fù)雜的功能,而且代碼會(huì)具有更好的可讀性和
可移植性。

Boot Loader 的 stage1 通常包括以下步驟(以執(zhí)行的先后順序):
·硬件設(shè)備初始化。
·為加載 Boot Loader 的 stage2 準(zhǔn)備 RAM 空間。
·拷貝 Boot Loader 的 stage2 到 RAM 空間中。
·設(shè)置好堆棧。
·跳轉(zhuǎn)到 stage2 的 C 入口點(diǎn)。
Boot Loader 的 stage2 通常包括以下步驟(以執(zhí)行的先后順序):
·初始化本階段要使用到的硬件設(shè)備。
·檢測系統(tǒng)內(nèi)存映射(memory map)。
·將 kernel 映像和根文件系統(tǒng)映像從 flash 上讀到 RAM 空間中。
·為內(nèi)核設(shè)置啟動(dòng)參數(shù)。
·調(diào)用內(nèi)核。

3.1 Boot Loader 的 stage1

3.1.1 基本的硬件初始化

這是 Boot Loader 一開始就執(zhí)行的操作,其目的是為 stage2 的執(zhí)行以及隨后的 kernel
的執(zhí)行準(zhǔn)備好一些基本的硬件環(huán)境。它通常包括以下步驟(以執(zhí)行的先后順序):

1.屏蔽所有的中斷。為中斷提供服務(wù)通常是 OS 設(shè)備驅(qū)動(dòng)程序的責(zé)任,因此在 Boot Loa
der 的執(zhí)行全過程中可以不必響應(yīng)任何中斷。中斷屏蔽可以通過寫 CPU 的中斷屏蔽寄存器
或狀態(tài)寄存器(比如 ARM 的 CPSR 寄存器)來完成。

2.設(shè)置 CPU 的速度和時(shí)鐘頻率。

3.RAM 初始化。包括正確地設(shè)置系統(tǒng)的內(nèi)存控制器的功能寄存器以及各內(nèi)存庫控制寄存器
等。

4.初始化 LED。典型地,通過 GPIO 來驅(qū)動(dòng) LED,其目的是表明系統(tǒng)的狀態(tài)是 OK 還是
Error。如果板子上沒有 LED,那么也可以通過初始化 UART 向串口打印 Boot Loader 的
Logo 字符信息來完成這一點(diǎn)。

5. 關(guān)閉 CPU 內(nèi)部指令/數(shù)據(jù) cache。

3.1.2 為加載 stage2 準(zhǔn)備 RAM 空間

為了獲得更快的執(zhí)行速度,通常把 stage2 加載到 RAM 空間中來執(zhí)行,因此必須為加載
Boot Loader 的 stage2 準(zhǔn)備好一段可用的 RAM 空間范圍。

由于 stage2 通常是 C 語言執(zhí)行代碼,因此在考慮空間大小時(shí),除了 stage2 可執(zhí)行映象
的大小外,還必須把堆棧空間也考慮進(jìn)來。此外,空間大小最好是 memory page 大小(通
常是 4KB)的倍數(shù)。一般而言,1M 的 RAM 空間已經(jīng)足夠了。具體的地址范圍可以任意安排
,比如 blob 就將它的 stage2 可執(zhí)行映像安排到從系統(tǒng) RAM 起始地址 0xc0200000 開始
的 1M 空間內(nèi)執(zhí)行。但是,將 stage2 安排到整個(gè) RAM 空間的最頂 1MB(也即(RamEnd-1M
B) - RamEnd)是一種值得推薦的方法。

為了后面的敘述方便,這里把所安排的 RAM 空間范圍的大小記為:stage2_size(字節(jié)),
把起始地址和終止地址分別記為:stage2_start 和 stage2_end(這兩個(gè)地址均以 4 字節(jié)
邊界對(duì)齊)。因此:

stage2_end=stage2_start+stage2_size



另外,還必須確保所安排的地址范圍的的確確是可讀寫的 RAM 空間,因此,必須對(duì)你所安
排的地址范圍進(jìn)行測試。具體的測試方法可以采用類似于 blob 的方法,也即:以 memor
y page 為被測試單位,測試每個(gè) memory page 開始的兩個(gè)字是否是可讀寫的。為了后面
敘述的方便,我們記這個(gè)檢測算法為:test_mempage,其具體步驟如下:

1.先保存 memory page 一開始兩個(gè)字的內(nèi)容。

2.向這兩個(gè)字中寫入任意的數(shù)字。比如:向第一個(gè)字寫入 0x55,第 2 個(gè)字寫入 0xaa。


3.然后,立即將這兩個(gè)字的內(nèi)容讀回。顯然,我們讀到的內(nèi)容應(yīng)該分別是 0x55 和 0xaa
。如果不是,則說明這個(gè) memory page 所占據(jù)的地址范圍不是一段有效的 RAM 空間。


4.再向這兩個(gè)字中寫入任意的數(shù)字。比如:向第一個(gè)字寫入 0xaa,第 2 個(gè)字中寫入 0x
55。

5.然后,立即將這兩個(gè)字的內(nèi)容立即讀回。顯然,我們讀到的內(nèi)容應(yīng)該分別是 0xaa 和
0x55。如果不是,則說明這個(gè) memory page 所占據(jù)的地址范圍不是一段有效的 RAM 空間


6.恢復(fù)這兩個(gè)字的原始內(nèi)容。測試完畢。

為了得到一段干凈的 RAM 空間范圍,我們也可以將所安排的 RAM 空間范圍進(jìn)行清零操作


3.1.3 拷貝 stage2 到 RAM 中

拷貝時(shí)要確定兩點(diǎn):(1) stage2 的可執(zhí)行映象在固態(tài)存儲(chǔ)設(shè)備的存放起始地址和終止地址
;(2) RAM 空間的起始地址。

3.1.4 設(shè)置堆棧指針 sp

堆棧指針的設(shè)置是為了執(zhí)行 C 語言代碼作好準(zhǔn)備。通常我們可以把 sp 的值設(shè)置為(stag
e2_end-4),也即在 3.1.2 節(jié)所安排的那個(gè) 1MB 的 RAM 空間的最頂端(堆棧向下生長)。
此外,在設(shè)置堆棧指針 sp 之前,也可以關(guān)閉 led 燈,以提示用戶我們準(zhǔn)備跳轉(zhuǎn)到 stag
e2。經(jīng)過上述這些執(zhí)行步驟后,系統(tǒng)的物理內(nèi)存布局應(yīng)該如下圖2所示。

3.1.5 跳轉(zhuǎn)到 stage2 的 C 入口點(diǎn)

在上述一切都就緒后,就可以跳轉(zhuǎn)到 Boot Loader 的 stage2 去執(zhí)行了。比如,在 ARM
系統(tǒng)中,這可以通過修改 PC 寄存器為合適的地址來實(shí)現(xiàn)。


http://tech.ccidnet.com/pub/attachme.../12/268047.gif

圖2 bootloader 的 stage2 可執(zhí)行映象剛被拷貝到 RAM 空間時(shí)的系統(tǒng)內(nèi)存布局


3.2 Boot Loader 的 stage2

正如前面所說,stage2 的代碼通常用 C 語言來實(shí)現(xiàn),以便于實(shí)現(xiàn)更復(fù)雜的功能和取得更
好的代碼可讀性和可移植性。但是與普通 C 語言應(yīng)用程序不同的是,在編譯和鏈接 boot
loader 這樣的程序時(shí),我們不能使用 glibc 庫中的任何支持函數(shù)。其原因是顯而易見的
。這就給我們帶來一個(gè)問題,那就是從那里跳轉(zhuǎn)進(jìn) main() 函數(shù)呢?直接把 main() 函數(shù)
的起始地址作為整個(gè) stage2 執(zhí)行映像的入口點(diǎn)或許是最直接的想法。但是這樣做有兩個(gè)
缺點(diǎn):1)無法通過main() 函數(shù)傳遞函數(shù)參數(shù);2)無法處理 main() 函數(shù)返回的情況。一種
更為巧妙的方法是利用 trampoline(*簧床)的概念。也即,用匯編語言寫一段trampolin
e 小程序,并將這段 trampoline 小程序來作為 stage2 可執(zhí)行映象的執(zhí)行入口點(diǎn)。然后
我們可以在 trampoline 匯編小程序中用 CPU 跳轉(zhuǎn)指令跳入 main() 函數(shù)中去執(zhí)行;而當(dāng)
main() 函數(shù)返回時(shí),CPU 執(zhí)行路徑顯然再次回到我們的 trampoline 程序。簡而言之,
這種方法的思想就是:用這段 trampoline 小程序來作為 main() 函數(shù)的外部包裹(exter
nal wrapper)。

下面給出一個(gè)簡單的 trampoline 程序示例(來自blob):

.text

.globl _trampoline
_trampoline:
bl main
/* if main ever returns we just call it again */
b _trampoline



可以看出,當(dāng) main() 函數(shù)返回后,我們又用一條跳轉(zhuǎn)指令重新執(zhí)行 trampoline 程序―
―當(dāng)然也就重新執(zhí)行 main() 函數(shù),這也就是 trampoline(*簧床)一詞的意思所在。


3.2.1初始化本階段要使用到的硬件設(shè)備

這通常包括:(1)初始化至少一個(gè)串口,以便和終端用戶進(jìn)行 I/O 輸出信息;(2)初始
化計(jì)時(shí)器等。在初始化這些設(shè)備之前,也可以重新把 LED 燈點(diǎn)亮,以表明我們已經(jīng)進(jìn)入
main() 函數(shù)執(zhí)行。

設(shè)備初始化完成后,可以輸出一些打印信息,程序名字字符串、版本號(hào)等。

3.2.2 檢測系統(tǒng)的內(nèi)存映射(memory map)

所謂內(nèi)存映射就是指在整個(gè) 4GB 物理地址空間中有哪些地址范圍被分配用來尋址系統(tǒng)的
RAM 單元。比如,在 SA-1100 CPU 中,從 0xC000,0000 開始的 512M 地址空間被用作系
統(tǒng)的 RAM 地址空間,而在 Samsung S3C44B0X CPU 中,從 0x0c00,0000 到 0x1000,0000
之間的 64M 地址空間被用作系統(tǒng)的 RAM 地址空間。雖然 CPU 通常預(yù)留出一大段足夠的
地址空間給系統(tǒng) RAM,但是在搭建具體的嵌入式系統(tǒng)時(shí)卻不一定會(huì)實(shí)現(xiàn) CPU 預(yù)留的全部
RAM 地址空間。也就是說,具體的嵌入式系統(tǒng)往往只把 CPU 預(yù)留的全部 RAM 地址空間中
的一部分映射到 RAM 單元上,而讓剩下的那部分預(yù)留 RAM 地址空間處于未使用狀態(tài)。由
于上述這個(gè)事實(shí),因此 Boot Loader 的 stage2 必須在它想干點(diǎn)什么 (比如,將存儲(chǔ)在
flash 上的內(nèi)核映像讀到 RAM 空間中) 之前檢測整個(gè)系統(tǒng)的內(nèi)存映射情況,也即它必須知
道 CPU 預(yù)留的全部 RAM 地址空間中的哪些被真正映射到 RAM 地址單元,哪些是處于 "u
nused" 狀態(tài)的。

(1) 內(nèi)存映射的描述

可以用如下數(shù)據(jù)結(jié)構(gòu)來描述 RAM 地址空間中的一段連續(xù)(continuous)的地址范圍:

typedef struct memory_area_struct {
u32 start; /* the base address of the memory region */
u32 size; /* the byte number of the memory region */
int used;
} memory_area_t;



這段 RAM 地址空間中的連續(xù)地址范圍可以處于兩種狀態(tài)之一:(1)used=1,則說明這段連
續(xù)的地址范圍已被實(shí)現(xiàn),也即真正地被映射到 RAM 單元上。(2)used=0,則說明這段連續(xù)
的地址范圍并未被系統(tǒng)所實(shí)現(xiàn),而是處于未使用狀態(tài)。

基于上述 memory_area_t 數(shù)據(jù)結(jié)構(gòu),整個(gè) CPU 預(yù)留的 RAM 地址空間可以用一個(gè) memory
_area_t 類型的數(shù)組來表示,如下所示:

memory_area_t memory_map[NUM_MEM_AREAS] = {
[0 ... (NUM_MEM_AREAS - 1)] = {
.start = 0,
.size = 0,
.used = 0
},
};



(2) 內(nèi)存映射的檢測

下面我們給出一個(gè)可用來檢測整個(gè) RAM 地址空間內(nèi)存映射情況的簡單而有效的算法:


/* 數(shù)組初始化 */
for(i = 0; i < NUM_MEM_AREAS; i++)
memory_map[i].used = 0;

/* first write a 0 to all memory locations */
for(addr = MEM_START; addr < MEM_END; addr += PAGE_SIZE)
* (u32 *)addr = 0;

for(i = 0, addr = MEM_START; addr < MEM_END; addr += PAGE_SIZE) {
/*
* 檢測從基地址 MEM_START+i*PAGE_SIZE 開始,大小為
* PAGE_SIZE 的地址空間是否是有效的RAM地址空間。
*/
調(diào)用3.1.2節(jié)中的算法test_mempage();
if ( current memory page isnot a valid ram page) {
/* no RAM here */
if(memory_map[i].used )
i++;
continue;
}

/*
* 當(dāng)前頁已經(jīng)是一個(gè)被映射到 RAM 的有效地址范圍
* 但是還要看看當(dāng)前頁是否只是 4GB 地址空間中某個(gè)地址頁的別名?
*/
if(* (u32 *)addr != 0) { /* alias? */
/* 這個(gè)內(nèi)存頁是 4GB 地址空間中某個(gè)地址頁的別名 */
if ( memory_map[i].used )
i++;
continue;
}

/*
* 當(dāng)前頁已經(jīng)是一個(gè)被映射到 RAM 的有效地址范圍
* 而且它也不是 4GB 地址空間中某個(gè)地址頁的別名。
*/
if (memory_map[i].used == 0) {
memory_map[i].start = addr;
memory_map[i].size = PAGE_SIZE;
memory_map[i].used = 1;
} else {
memory_map[i].size += PAGE_SIZE;
}
} /* end of for (…) */



在用上述算法檢測完系統(tǒng)的內(nèi)存映射情況后,Boot Loader 也可以將內(nèi)存映射的詳細(xì)信息
打印到串口。

3.2.3 加載內(nèi)核映像和根文件系統(tǒng)映像

(1) 規(guī)劃內(nèi)存占用的布局

這里包括兩個(gè)方面:(1)內(nèi)核映像所占用的內(nèi)存范圍;(2)根文件系統(tǒng)所占用的內(nèi)存范圍
。在規(guī)劃內(nèi)存占用的布局時(shí),主要考慮基地址和映像的大小兩個(gè)方面。

對(duì)于內(nèi)核映像,一般將其拷貝到從(MEM_START+0x8000) 這個(gè)基地址開始的大約1MB大小的
內(nèi)存范圍內(nèi)(嵌入式 Linux 的內(nèi)核一般都不操過 1MB)。為什么要把從 MEM_START 到 MEM
_START+0x8000 這段 32KB 大小的內(nèi)存空出來呢?這是因?yàn)?Linux 內(nèi)核要在這段內(nèi)存中
放置一些全局?jǐn)?shù)據(jù)結(jié)構(gòu),如:啟動(dòng)參數(shù)和內(nèi)核頁表等信息。

而對(duì)于根文件系統(tǒng)映像,則一般將其拷貝到 MEM_START+0x0010,0000 開始的地方。如果用
Ramdisk 作為根文件系統(tǒng)映像,則其解壓后的大小一般是1MB。

(2)從 Flash 上拷貝

由于像 ARM 這樣的嵌入式 CPU 通常都是在統(tǒng)一的內(nèi)存地址空間中尋址 Flash 等固態(tài)存儲(chǔ)
設(shè)備的,因此從 Flash 上讀取數(shù)據(jù)與從 RAM 單元中讀取數(shù)據(jù)并沒有什么不同。用一個(gè)簡
單的循環(huán)就可以完成從 Flash 設(shè)備上拷貝映像的工作:

while(count) {
*dest++ = *src++; /* they are all aligned with word boundary */
count -= 4; /* byte number */
};



3.2.4 設(shè)置內(nèi)核的啟動(dòng)參數(shù)

應(yīng)該說,在將內(nèi)核映像和根文件系統(tǒng)映像拷貝到 RAM 空間中后,就可以準(zhǔn)備啟動(dòng) Linux
內(nèi)核了。但是在調(diào)用內(nèi)核之前,應(yīng)該作一步準(zhǔn)備工作,即:設(shè)置 Linux 內(nèi)核的啟動(dòng)參數(shù)。


Linux 2.4.x 以后的內(nèi)核都期望以標(biāo)記列表(tagged list)的形式來傳遞啟動(dòng)參數(shù)。啟動(dòng)參
數(shù)標(biāo)記列表以標(biāo)記 ATAG_CORE 開始,以標(biāo)記 ATAG_NONE 結(jié)束。每個(gè)標(biāo)記由標(biāo)識(shí)被傳遞參
數(shù)的 tag_header 結(jié)構(gòu)以及隨后的參數(shù)值數(shù)據(jù)結(jié)構(gòu)來組成。數(shù)據(jù)結(jié)構(gòu) tag 和 tag_header
定義在 Linux 內(nèi)核源碼的include/asm/setup.h 頭文件中:

/* The list ends with an ATAG_NONE node. */
#define ATAG_NONE 0x00000000

struct tag_header {
u32 size; /* 注意,這里size是字?jǐn)?shù)為單位的 */
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;
};



在嵌入式 Linux 系統(tǒng)中,通常需要由 Boot Loader 設(shè)置的常見啟動(dòng)參數(shù)有:ATAG_CORE、
ATAG_MEM、ATAG_CMDLINE、ATAG_RAMDISK、ATAG_INITRD等。比如,設(shè)置 ATAG_CORE 的代
碼如下:

params = (struct tag *)BOOT_PARAMS;

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);



其中,BOOT_PARAMS 表示內(nèi)核啟動(dòng)參數(shù)在內(nèi)存中的起始基地址,指針 params 是一個(gè) str
uct tag 類型的指針。宏 tag_next() 將以指向當(dāng)前標(biāo)記的指針為參數(shù),計(jì)算緊臨當(dāng)前標(biāo)
記的下一個(gè)標(biāo)記的起始地址。注意,內(nèi)核的根文件系統(tǒng)所在的設(shè)備ID就是在這里設(shè)置的。


下面是設(shè)置內(nèi)存映射情況的示例代碼:

for(i = 0; i < NUM_MEM_AREAS; i++) {
if(memory_map[i].used) {
params->hdr.tag = ATAG_MEM;
params->hdr.size = tag_size(tag_mem32);

params->u.mem.start = memory_map[i].start;
params->u.mem.size = memory_map[i].size;

params = tag_next(params);
}
}



可以看出,在 memory_map[]數(shù)組中,每一個(gè)有效的內(nèi)存段都對(duì)應(yīng)一個(gè) ATAG_MEM 參數(shù)標(biāo)
記。

Linux 內(nèi)核在啟動(dòng)時(shí)可以以命令行參數(shù)的形式來接收信息,利用這一點(diǎn)我們可以向內(nèi)核提
供那些內(nèi)核不能自己檢測的硬件參數(shù)信息,或者重載(override)內(nèi)核自己檢測到的信息。
比如,我們用這樣一個(gè)命令行參數(shù)字符串"console=ttyS0,115200n8"來通知內(nèi)核以 ttyS0
作為控制臺(tái),且串口采用 "115200bps、無奇偶校驗(yàn)、8位數(shù)據(jù)位"這樣的設(shè)置。下面是一
段設(shè)置調(diào)用內(nèi)核命令行參數(shù)字符串的示例代碼:

char *p;

/* eat leading white space */
for(p = commandline; *p == ' '; p++)
;

/* skip non-existent command lines so the kernel will still
* use its default command line.
*/
if(*p == '\0')
return;

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);



請(qǐng)注意在上述代碼中,設(shè)置 tag_header 的大小時(shí),必須包括字符串的終止符'\0',此外
還要將字節(jié)數(shù)向上圓整4個(gè)字節(jié),因?yàn)?tag_header 結(jié)構(gòu)中的size 成員表示的是字?jǐn)?shù)。


下面是設(shè)置 ATAG_INITRD 的示例代碼,它告訴內(nèi)核在 RAM 中的什么地方可以找到 initr
d 映象(壓縮格式)以及它的大小:

params->hdr.tag = ATAG_INITRD2;
params->hdr.size = tag_size(tag_initrd);

params->u.initrd.start = RAMDISK_RAM_BASE;
params->u.initrd.size = INITRD_LEN;

params = tag_next(params);



下面是設(shè)置 ATAG_RAMDISK 的示例代碼,它告訴內(nèi)核解壓后的 Ramdisk 有多大(單位是K
B):

params->hdr.tag = ATAG_RAMDISK;
params->hdr.size = tag_size(tag_ramdisk);

params->u.ramdisk.start = 0;
params->u.ramdisk.size = RAMDISK_SIZE; /* 請(qǐng)注意,單位是KB */
params->u.ramdisk.flags = 1; /* automatically load ramdisk */

params = tag_next(params);



最后,設(shè)置 ATAG_NONE 標(biāo)記,結(jié)束整個(gè)啟動(dòng)參數(shù)列表:

static void setup_end_tag(void)
{
params->hdr.tag = ATAG_NONE;
params->hdr.size = 0;
}



3.2.5 調(diào)用內(nèi)核

Boot Loader 調(diào)用 Linux 內(nèi)核的方法是直接跳轉(zhuǎn)到內(nèi)核的第一條指令處,也即直接跳轉(zhuǎn)到
MEM_START+0x8000 地址處。在跳轉(zhuǎn)時(shí),下列條件要滿足:

1. CPU 寄存器的設(shè)置:
·R0=0;
@R1=機(jī)器類型 ID;關(guān)于 Machine Type Number,可以參見 linux/arch/arm/tools/mach
-types。
@R2=啟動(dòng)參數(shù)標(biāo)記列表在 RAM 中起始基地址;

2. CPU 模式:
·必須禁止中斷(IRQs和FIQs);
·CPU 必須 SVC 模式;

3. Cache 和 MMU 的設(shè)置:
·MMU 必須關(guān)閉;
·指令 Cache 可以打開也可以關(guān)閉;
·數(shù)據(jù) Cache 必須關(guān)閉;

如果用 C 語言,可以像下列示例代碼這樣來調(diào)用內(nèi)核:

void (*theKernel)(int zero, int arch, u32 params_addr)
= (void (*)(int, int, u32))KERNEL_RAM_BASE;
……
theKernel(0, ARCH_NUMBER, (u32) kernel_params_start);



注意,theKernel()函數(shù)調(diào)用應(yīng)該永遠(yuǎn)不返回的。如果這個(gè)調(diào)用返回,則說明出錯(cuò)。

總結(jié)

以上是生活随笔為你收集整理的嵌入式BootLoader技术内幕(二)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。