廢話少說,先上main函數:
void Main(void)
{
char result;
MMU_EnableICache();
MMU_EnableDCache();
Port_Init();
Uart_Init();
Nand_Init();
//Memset((char*)0x30000000, 0, 3U * 512 * 1024);
//Memset((char*)0x30000000 + 32U * 1024 * 1024, 0, 128U * 1024U);
Memset((char*)pBSPArgs, 0, 1024);
Uart_SendString("load Windows CE Image..\r\n");
//for(;;);
InitLCD();
result = ReadImageFromNand(1);
if (run) run();
// shouldn't be here.
for (;;);
}
main()函數很簡單,使能MMU的指令和數據cache,端口初始化,串口初始化,nand初始化,然後調用Memset函數將pBSPArgs指針指向的1024大小的內存空間全部清零,清零後串口打印加載WinCE鏡像文件的消息:
Uart_SendString("load Windows CE Image..\r\n");
然後初始化LCD顯示器,然後調用
result = ReadImageFromNand(1);
函數將鏡像文件從nand flash中讀取至內存中,最後調用
if (run) run();
啓動內核。整體流程還是比較清晰明瞭的。
初始化什麼的內部函數可以暫時不管,關鍵關注的重點在於
result = ReadImageFromNand(1);
好了,我們來看一看這個函數,ReadImageFromNand()的定義在nand.c中,裏面的內容還是很豐富的:
// Loading
char ReadImageFromNand(unsigned int dwEntry)
{
volatile unsigned int dwSectorsNeeded;
volatile unsigned int dwSector, dwLength; // Start Sector & Length
volatile unsigned int dwRAM, i;
NF_ReadSector(TOC_BLOCK*SECTOR_PER_BLOCK, (U8*)pToc);
if (!VALID_TOC(pToc))
return 0x32;
if ( !(pToc->id[dwEntry].dwImageType & IMAGE_TYPE_RAMIMAGE) ) {
return 0x33;
}
dwSectorsNeeded = pToc->id[dwEntry].dwTtlSectors;
dwRAM = VIRTUAL_TO_PHYSICAL(pToc->id[dwEntry].dwLoadAddress);
run = (void (*)(void))(pToc->id[dwEntry].dwJumpAddress ? VIRTUAL_TO_PHYSICAL(pToc->id[dwEntry].dwJumpAddress) :
VIRTUAL_TO_PHYSICAL(pToc->id[dwEntry].dwLoadAddress));
i = 0;
while (dwSectorsNeeded && i < MAX_SG_SECTORS)
{
dwSector = pToc->id[dwEntry].sgList[i].dwSector;
dwLength = pToc->id[dwEntry].sgList[i].dwLength;
ProgressBarInit(dwLength);
// read each sg segment
while (dwLength) {
if (dwSector % SECTOR_PER_BLOCK == 0) {
if (! NF_IsBadBlock(dwSector/SECTOR_PER_BLOCK)) {
dwSector += SECTOR_PER_BLOCK;
continue;
}
}
if(!NF_ReadSector(dwSector, (unsigned char*)dwRAM)){
Uart_SendString("\nCannot load Windows CE\n");
while (1);
}
dwSector++;
dwLength--;
dwRAM += SECTOR_SIZE;
ProgressBarStep();
}
dwSectorsNeeded -= pToc->id[dwEntry].sgList[i].dwLength;
i++;
}
return 0;
首先是
NF_ReadSector(TOC_BLOCK*SECTOR_PER_BLOCK, (U8*)pToc);
將nand flash的TOC_BLOCK*SECTOR_PER_BLOCK區域的一個扇區大小的數據搬運至pToc所指向的內存區域
接着判斷此區域數據內容
if (!VALID_TOC(pToc))
return 0x32;
if ( !(pToc->id[dwEntry].dwImageType & IMAGE_TYPE_RAMIMAGE) ) {
return 0x33;
}
如果不是有效的TOC,則返回0x32,若有效,讀取pToc指針指向的結構體的內容,此結構體內容如下:
//
// TOC: Table Of Contents, OEM on disk structure.
// sizeof(TOC) = SECTOR_SIZE.
// Consider the TOC_BLOCK to contain an array of PAGES_PER_BLOCK
// TOC entries, since the entire block is reserved.
//
typedef struct _TOC {
DWORD dwSignature;
// How to boot the images in this TOC.
// This could be moved into the image descriptor if desired,
// but I prefer to conserve space.
BOOT_CFG BootCfg;
// Array of Image Descriptors.
IMAGE_DESCRIPTOR id[MAX_TOC_DESCRIPTORS];
CHAININFO chainInfo;
} TOC, *PTOC; // 512 bytes
TOC包含了OEM代碼在磁盤上的存儲結構,TOC的大小呢等於一個扇區的面積,這也就和從磁盤(nand flash)的指定位置讀取一個扇區的面積的數據相對應了。TOC_BLOCK存放了PAGES_PER_BLOCK TOC入口信息隊列,因此這個整個block是被保留的。TOC結構體包含了四個成員,其中id成員爲 IMAGE_DESCRIPTOR類型的結構體,包含了鏡像的各種描述,查看id成員:
// IMAGE_DESCRIPTOR: describes the image to load.
// The image can be anything: bootloaders, RAMIMAGE, MXIP, ...
// Note: Our NAND uses H/W ECC, so no checksum needed.
//
typedef struct _IMAGE_DESCRIPTOR {
// File version info
DWORD dwVersion; // e.g: build number
DWORD dwSignature; // e.g: "EBOT", "CFSH", etc
UCHAR ucString[IMAGE_STRING_LEN]; // e.g: "PocketPC_2002"
DWORD dwImageType; // IMAGE_TYPE_ flags
DWORD dwTtlSectors; // TTL image size in sectors.
// We store size in sectors instead of bytes
// to simplify sector reads in Nboot.
DWORD dwLoadAddress; // Virtual address to load image (ImageStart)
DWORD dwJumpAddress; // Virtual address to jump (StartAddress/LaunchAddr)
// This array equates to a sector-based MXIP MultiBINInfo in blcommon.
// Unused entries are zeroed.
// You could chain image descriptors if needed.
SG_SECTOR sgList[MAX_SG_SECTORS];
// BinFS support to load nk region only
//struct
//{
ULONG dwStoreOffset; // byte offset - not needed - remove!
//ULONG RunAddress; // nk dwRegionStart address
//ULONG Length; // nk dwRegionLength in bytes
//ULONG LaunchAddress; // nk dwLaunchAddr
//} NKRegion;
} IMAGE_DESCRIPTOR, *PIMAGE_DESCRIPTOR;
在 ReadImageFromNand()的前部分,主要任務是校驗TOC合法性、驗證image類型、image總扇區數、image加載至內存的地址dwRAM、跳轉地址run,然後通過while循環讀取flash中的image信息至內存中。
while (dwSectorsNeeded && i < MAX_SG_SECTORS)
{
dwSector = pToc->id[dwEntry].sgList[i].dwSector;//image段第i個list的開始扇區
dwLength = pToc->id[dwEntry].sgList[i].dwLength;//鏡像段的第i個list的長度(多少個扇區)
ProgressBarInit(dwLength);//
// read each sg segment
while (dwLength) {
if (dwSector % SECTOR_PER_BLOCK == 0) {//若dwSector爲block的開頭
if (! NF_IsBadBlock(dwSector/SECTOR_PER_BLOCK)) {//則檢查block是否是壞塊
dwSector += SECTOR_PER_BLOCK;//壞塊則跳過此壞塊
continue;
}
}
if(!NF_ReadSector(dwSector, (unsigned char*)dwRAM)){//讀取dwSector的一個扇區至dwRAM
Uart_SendString("\nCannot load Windows CE\n");
while (1);
}
dwSector++;//扇區頭地址往後挪一個位置
dwLength--;//鏡像長度減一
dwRAM += SECTOR_SIZE;//ram中的目的地址往後挪一,增加的長度爲一個扇區的長度
ProgressBarStep();
}
dwSectorsNeeded -= pToc->id[dwEntry].sgList[i].dwLength;//需要讀取的總扇區數=總扇區數-已經讀取數據的扇區數
i++;
}
此段代碼負責將flash中的內容複製至RAM中,包含兩個while循環while (dwSectorsNeeded && i < MAX_SG_SECTORS)、while (dwLength) ,其中while (dwLength) 負責將一個list的數據,從dwSector開始的dwLength個扇區數據搬運至dwRAM中,while (dwSectorsNeeded && i < MAX_SG_SECTORS)負責遍歷所有的list,list數據共有MAX_SG_SECTORS個,通過查看這個宏可以知道,MAX_SG_SECTORS爲14個,dwSectorsNeeded是鏡像所有list的扇區總數,while循環裏的判斷語句可謂雙保險,通過判定:1.複製的扇區總數是否足額2.複製的list數目是否足額,來停止image的複製。
通過list的數據結構可以很清晰的知道,所有的list應該是連續的,而這MAX_SG_SECTORS個的各個list可以使不連續的。
將nand裏面的內容讀取完成之後,就可以執行:
if (run) run();
跳轉至內核,啓動。
run = (void (*)(void))(pToc->id[dwEntry].dwJumpAddress ? VIRTUAL_TO_PHYSICAL(pToc->id[dwEntry].dwJumpAddress) :
VIRTUAL_TO_PHYSICAL(pToc->id[dwEntry].dwLoadAddress));
其中函數run的聲明也特別有意思:
void (*run)(void)=(void (*)(void))(DOWNLOAD_ADDRESS);
void (*run)(void)定義了一個名稱爲run指向參數爲void,返回值也爲void的函數的指針,可以看做:
run爲指向出入參數和返回值均爲void的函數的指針,void (*run)(void)=(void (*)(void))(DOWNLOAD_ADDRESS);表示將地址DOWNLOAD_ADDRESS賦予run指針,也就是跳轉執行了。(void (*)(void))表示將DOWNLOAD_ADDRESS地址強制轉換爲和run指針類型一致的地址值,也就是所謂的強制轉換。
void (*run)(void) = (void (*)(void))0x0;
void (*run)(void) = (void (*)(void))0x0;
定義了一個不帶參數和返回值的函數指針run ,同時對它進行初始化。指向了內存的0號單元。
run是一個指向返回值和參數都爲空的函數的指針,0x0就是0,這個賦值就是將地址0強制轉換爲返回值和參數都爲空的函數的地址賦給run。
(void (*)(void))進行強制轉換.就好象(char *)str一樣
void (*run)(void)與void (*)(void)有什麼區別呢 ?
只是差個名稱
對於類型來說本身叫什麼就沒必要
void (*)(void) 就是函數指針,它是個類型說明
void (*)(void)
(*) --我是函數指針
(void)-不需要傳給我參數
void --我的返回類型爲空
------------------
void (*run)(void)
(*run) --我是函數指針 ,我叫run
(void)-不需要傳給我參數
void --我的返回類型爲空
你可以這麼看typedef void (*run)(void) 然後run a = (run)0x00; 這樣總能理解了吧
typedef struct _TOC {
DWORD |
dwSignature; |
BOOT_CFG |
BootCfg; |
IMAGE_DESCRIPTOR |
id[MAX_TOC_DESCRIPTORS]; |
CHAININFO |
chainInfo; |
} TOC, *PTOC; // 512 bytes
typedef struct _BOOTCFG {
ULONG |
ImageIndex; |
ULONG |
ConfigFlags; |
ULONG |
BootDelay; |
EDBG_ADDR |
EdbgAddr; |
ULONG |
SubnetMask; |
} BOOT_CFG, *PBOOT_CFG;
typedef struct _EDBG_ADDR {
DWORD |
dwIP; |
USHORT |
wMAC[3]; |
USHORT |
wPort; |
} EDBG_ADDR;
typedef struct _IMAGE_DESCRIPTOR {
DWORD |
dwVersion; |
DWORD |
dwSignature; |
UCHAR |
ucString[IMAGE_STRING_LEN]; |
DWORD |
dwImageType; |
DWORD |
dwTtlSectors; |
DWORD |
dwLoadAddress; |
DWORD |
dwJumpAddress; |
SG_SECTOR |
sgList[MAX_SG_SECTORS]; |
} IMAGE_DESCRIPTOR, *PIMAGE_DESCRIPTOR;
typedef struct _SG_SECTOR {
DWORD |
dwSector; |
DWORD |
dwLength; |
} SG_SECTOR, *PSG_SECTOR;
typedef struct _CHAININFO {
DWORD |
dwLoadAddress; |
DWORD |
dwFlashAddress; |
DWORD |
dwLength; |
} CHAININFO, *PCHAININFO;
TOC分析見http://blog.csdn.net/formerman/article/details/4347905