久久久久久久麻豆,中日韩av在线,麻豆av在线免费,中文字幕二区三区,欧美日韩视频一区二区,亚洲欧美日韩在线一区,国产无毒不卡

嵌入式系統(tǒng) Boot Loader 技術(shù)內(nèi)幕


blob 的方法,也即:以 memory page 為被測(cè)試單位,測(cè)試每個(gè) memory page 開始的兩個(gè)字是否是可讀寫的。為了后面敘述的方便,我們記這個(gè)檢測(cè)算法為: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。如果不是,則說(shuō)明這個(gè) memory page 所占據(jù)的地址范圍不是一段有效的 ram 空間。

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

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

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

為了得到一段干凈的 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 語(yǔ)言代碼作好準(zhǔn)備。通常我們可以把 sp 的值設(shè)置為(stage2_end-4),也即在 3.1.2 節(jié)所安排的那個(gè) 1mb 的 ram 空間的最頂端(堆棧向下生長(zhǎng))。

此外,在設(shè)置堆棧指針 sp 之前,也可以關(guān)閉 led 燈,以提示用戶我們準(zhǔn)備跳轉(zhuǎn)到 stage2。

經(jīng)過(guò)上述這些執(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)中,這可以通過(guò)修改 pc 寄存器為合適的地址來(lái)實(shí)現(xiàn)。


圖2 bootloader 的 stage2 可執(zhí)行映象剛被拷貝到 ram 空間時(shí)的系統(tǒng)內(nèi)存布局
嵌入式系統(tǒng) Boot Loader 技術(shù)內(nèi)幕

3.2 boot loader 的 stage2

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

下面給出一個(gè)簡(jiǎn)單的 trampoline 程序示例(來(lái)自blob):

.text.globl _trampoline_trampoline:blmain/* 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 檢測(cè)系統(tǒng)的內(nèi)存映射(memory map)

所謂內(nèi)存映射就是指在整個(gè) 4gb 物理地址空間中有哪些地址范圍被分配用來(lái)尋址系統(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 地址空間。也就是說(shuō),具體的嵌入式系統(tǒng)往往只把 cpu 預(yù)留的全部 ram 地址空間中的一部分映射到 ram 單元上,而讓剩下的那部分預(yù)留 ram 地址空間處于未使用狀態(tài)。 由于上述這個(gè)事實(shí),因此 boot loader 的 stage2 必須在它想干點(diǎn)什么 (比如,將存儲(chǔ)在 flash 上的內(nèi)核映像讀到 ram 空間中) 之前檢測(cè)整個(gè)系統(tǒng)的內(nèi)存映射情況,也即它必須知道 cpu 預(yù)留的全部 ram 地址空間中的哪些被真正映射到 ram 地址單元,哪些是處于 "unused" 狀態(tài)的。

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

可以用如下數(shù)據(jù)結(jié)構(gòu)來(lái)描述 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,則說(shuō)明這段連續(xù)的地址范圍已被實(shí)現(xiàn),也即真正地被映射到 ram 單元上。(2)used=0,則說(shuō)明這段連續(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ù)組來(lái)表示,如下所示:

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

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

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

/* 數(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) {     /*      * 檢測(cè)從基地址 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)前頁(yè)已經(jīng)是一個(gè)被映射到 ram 的有效地址范圍 * 但是還要看看當(dāng)前頁(yè)是否只是 4gb 地址空間中某個(gè)地址頁(yè)的別名? */if(* (u32 *)addr != 0) { /* alias? *//* 這個(gè)內(nèi)存頁(yè)是 4gb 地址空間中某個(gè)地址頁(yè)的別名 */if ( memory_map[i].used )i++;continue;}/* * 當(dāng)前頁(yè)已經(jīng)是一個(gè)被映射到 ram 的有效地址范圍 * 而且它也不是 4gb 地址空間中某個(gè)地址頁(yè)的別名。 */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 (…) */

在用上述算法檢測(cè)完系統(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)核一般都不操過(guò) 1mb)。為什么要把從 mem_start 到 mem_start+0x8000 這段 32kb 大小的內(nèi)存空出來(lái)呢?這是因?yàn)?linux 內(nèi)核要在這段內(nèi)存中放置一些全局?jǐn)?shù)據(jù)結(jié)構(gòu),如:?jiǎn)?dòng)參數(shù)和內(nèi)核頁(yè)表等信息。

而對(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è)簡(jiǎn)單的循環(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)該說(shuō),在將內(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)的形式來(lái)傳遞啟動(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)來(lái)組成。數(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_none0x00000000struct tag_header {u32 size; /* 注意,這里size是字?jǐn)?shù)為單位的 */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 specific */struct tag_acornacorn;/* * dc21285 specific */struct tag_memclkmemclk;} u;};

COPYRIGHT(C) 2011 廈門永宏亞得機(jī)電科技有限公司版權(quán)所有(閩ICP備05025945號(hào)) ALL RIGHTS RESERVED?

電話: 0592-5190891 傳真: 0592-5190720 E-Mail: E-mail:yade8895@163.com
地址: 廈門市海滄區(qū)興港六里17號(hào)2607室 郵編:361009 聯(lián)系人:翟先生