mini2440 nboot 源碼分析+TOC框架圖

2014年4月25日

廢話少說,先上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






發佈了45 篇原創文章 · 獲贊 6 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章