預備文章:
//-----------------------------------------------------------------------//
By: cnblogs-HJB
//-----------------------------------------------------------------------//
By:Gooogleman
//-----------------------------------------------------------------------//
By:gliethttp
//-----------------------------------------------------------------------//
By:CSDN-fan227
//-----------------------------------------------------------------------//
By:kernel
//-----------------------------------------------------------------------//
《2440從NANDFlash啓動之bootloader運行以前 》
By:rightsoft
//-----------------------------------------------------------------------//
By:驅動開發網-fishly_0
//-----------------------------------------------------------------------//
《Nboot程序詳細分析 》
By:昔日之ID:formerman
//-----------------------------------------------------------------------//
By:Mercury
//-----------------------------------------------------------------------//
好了,下面進入正文。昨天我們一起先看了下config.bib。於是我們就想更進一步。來在仔細研究下到底bootloader是有哪些事兒呢?
於是我們就找了上面的一些文章來看看。首先我們看HJB大牛的文章。文章很清晰的告訴了我們wince6.0下如何操作bootloader,下面我們再說一次,本人非常笨,不多說兩邊記不住,所以大家就忍受吧。首先系統要運行,需要一個引導程序,在以前有NOR flash,友善系列的開發板就是帶NOR這個東東,然後伴隨的是有一個vivisuper的loader,這裏我們不去細談這個,現在主流是nandflash,所以,對應的有一個nandflash的boot,我們叫他nboot,那我們再來看ARM,我們拿Samsung這個arm來做例子,在HJB大牛的文章中提及了,ARM自身有一個4K的空間來存儲應到程序,所以在上電之後通過一系列的配置,將nboot加載到ARM 4k的空間裏去執行,nboot就開始對系統的nandflash,內存,USB等等只要是你想初始化的設備進行一個初始化,這裏的初始化程度就看你自己寫的初始化程序了,按需所設計。接下來是nboot的重要使命,去叫eboot或者直接叫nk,然後結束自己的生命週期。
這裏要特別說的是,在上一篇文章完結後,有個朋友問我nboot怎麼去找到nk或者eboot在nandflash裏的地址啊?這個問題一下也把我問懵了。百思不得其解,看nboot的程序裏面到處都是什麼loadaddress,或者是jumpaddress之類的東西,以爲那個就是,後來根據以上一些文章的閱讀發現,其實那些地址只是SDRAM和ARM直接一個虛擬和實際物理地址。根本不是nandflash裏的地址。
那如何去從nandflash裏面找到我們要的eboot或者image呢?這個時候被我們忽略的TOC蹦出來了,大叫道那些東西都是我扒的。這個時候我們就仔細來看看gliethttp的文章,關於wince TOC啓動的文章,這裏gliethttp給我們詳細介紹了兩個結構體:TOC和_IMAGE_DESCRIPTOR,具體這兩個結構體的分析我們這裏也貼出來,給這兩扒東西的留個紀念,並給出一些註釋,註釋來自gliethttp,具體如下:
1:
typedef struct
_TOC
2:
{
3:
//和CSW中的認證域類似,只用來驗證接下去內容的合法
4:
DWORD dwSignature;
5:
//包含image的索引(我的是1)、啓動delay時間、ip地址、MAC地址和掩碼等
6:
BOOT_CFG BootCfg;
7:
//用來描述3個之多的ce內核image數組,我用的是id[1]
8:
IMAGE_DESCRIPTOR id[MAX_TOC_DESCRIPTORS];
9:
CHAININFO chainInfo;
10:
} TOC, *PTOC; // 512 字節
11:
12:
typedef struct
_IMAGE_DESCRIPTOR
13:
{
14:
DWORD dwVersion; //編譯時的版本號
15:
DWORD dwSignature; //“EBOOT”或“CFSH”等
16:
UCHAR ucString[IMAGE_STRING_LEN];
//描述字符串:如"eboot.nb0"之類
17:
DWORD dwImageType; //image的類型nk.nb0爲0x04
18:
DWORD dwTtlSectors; //image文件用到的NAND的扇區總數
19:
DWORD dwLoadAddress; //image加載時的虛擬地址
20:
DWORD dwJumpAddress; //image加載完成後的跳轉地址
21:
SG_SECTOR sgList[MAX_SG_SECTORS];
//image的段描述,包括起始扇區號和所需扇區數目
22:
ULONG dwStoreOffset;
} IMAGE_DESCRIPTOR, *PIMAGE_DESCRIPTOR;
這兩個相依爲命的結構體是扒eboot和image的罪魁禍首。他們到底是怎麼扒的呢,在gliethttp文中介紹道:並不需要一次性將所有nk.nb0數據都加載到內存,應該按需加載,那就是ce的鏡像文件image“按需加載”[“段式加載”]方式,TOC就是用來描述ce內核鏡像文件image的xipkernel段核心結構體,我們只需加載核文件xipkernel就能正常啓動進入wince界面。
好了,到此爲止我們搞清楚了原理了,那內存SDRAM和ARM之間的地址又回到了重要的位置,我們怎麼去設定這個地址呢?首先我們可以看下最新的mini2440 nboot的寫法,首先是給出幾個固定的地址,也就是原始的物理地址和起始地址,一般的情況下大家可以在oemaddrtab_cfg.inc中的對照表中找到SDRAM的地址映射,一般情況下是類似如下的描述:
1:
DCD 0x80000000, 0x30000000, 64
2:
; 64 MB DRAM BANK 6
好了,這完成了第一步,第二步我們來寫個比較簡單的小程序,如何把虛擬地址轉換成實際物理地址呢,這裏又回到mini2440的最新5.0代碼或者6.0代碼中,有一句這樣的話,寫的很好:
1:
#define
VIRTUAL_TO_PHYSICAL(va) ( ( (va>0x8c000000) ? (va-0xc000000) : (va) ) - VA_BASE + _RAM_STARTADDRESS )
這裏作出了一個判斷,保證了地址的大小。同時也轉換了地址。
這樣一來我們知道如何在兩個地址之間轉換了,不過這個事情其實是在內存管理單元沒有工作的情況下要用一下。當MMU工作了這個事情按照常理是交給MMU處理了.這些都是後話,我們繼續看,有了TOC有了地址轉換的工具,那地址到底在哪裏呢?
現在我們在來看看gooogleman的文章,做wince開發驅動的應該都會用pb自帶的一個命令行,這個是很好用的,有的時候makeimg啊,notepad一個什麼文件什麼的,很方便,這裏gooogleman帶我們回憶了一個非常重要,但是又往往被我們忽略的一個命令:viewbin,有了這個東東,我們可以仔細的看看我們要引導的eboot.bin或者nk.bin的起始地址是多少了。方法很簡單,直接viewbin nk.bin或者viewbin eboot.bin就可以了。起始地址,長度以及開始位置都列的很清晰,而這些個信息我們在TOC扒的時候已經全然送到他們該送的地方去了,系統自己該JUMP的也就JUMP過去,該Lunch的也就去Lunch過去,最新的會自己Run過去,呵呵這個完全看自己寫的了。都是一樣,無非是用一個指針指去那個地址,下面給個參考例子,我們就RUN過去,首先定義下怎麼個run法:
首先初始化一下我們的run
1:
#define
DOWNLOAD_ADDRESS 0x30138000
2:
void
(*run)(void
)=(void
(*)(void
))(DOWNLOAD_ADDRESS);
然後我們通過TOC給run加速,一般是在char ReadImageFromNand(unsigned int dwEntry)這個函數中
1:
run = (void
(*)(void
))(pToc->id[dwEntry].dwJumpAddress ? VIRTUAL_TO_PHYSICAL(pToc->id[dwEntry].dwJumpAddress) :
最後我們要在main函數裏run起來
1:
if
(run) run();
這句話說的很好聽,如果想跑你就跑吧。
好了。到此爲止該run到eboot了就去run eboot了,該run image就去run image了。
這裏補充下,其實eboot也是一樣,eboot起來也就是去run image,這個看設計的要求來定,理論是一樣的。
其他的文章對nboot+eboot+image模式進行了詳細的闡述,大家可以自己閱讀,這裏不多說了。
祝賀大家run的愉快。