bootload启动流程(三)--Eboot每个函数的详细说明

2025-05-10 08:17:03

由于eboot虽小但是各个功能都具有,所以也是一个比较复杂的过程,下面将对它下面的主要函数进行说明,这里面好多函数都是与nk共用的,所以大多代码并不在eboot下面,而是在public下面。

1)KernelRelocate()

第一个函数KernelRelocate (pTOC)这个函数实际是将片上一些变量移动到可读写的区域:他的原型就在blcommon.c下面:

static BOOL KernelRelocate (ROMHDR *const pTOC)

{

ULONG loop;

COPYentry *cptr;

if (pTOC == (ROMHDR *const) -1) {

return FALSE; // spin forever!

}

// This is where the data sections become valid... don't read globals until after this

for (loop = 0; loop < pTOC->ulCopyEntries; loop++) {

cptr = (COPYentry *)(pTOC->ulCopyOffset + loop*sizeof(COPYentry));

if (cptr->ulCopyLen)

memcpy((LPVOID)cptr->ulDest,(LPVOID)cptr->ulSource,cptr->ulCopyLen);

if (cptr->ulCopyLen != cptr->ulDestLen)

memset((LPVOID)(cptr->ulDest+cptr->ulCopyLen),0,cptr->ulDestLen-cptr->ulCopyLen);

}

return TRUE;

}

它的主要参数pToc是在制作镜像的时候编译生成的,指向一个ROMHDR的结构体,关于这个结构体在这里就不再详细叙述,函数的主要目的是将一些变量搬移到RAM中,这是为了防止对变量的写操作(FLASH中不能直接进行写操作)。

2)OEMDebugInit ()

第二个函数OEMDebugInit ()实际是为了调试而需要初始化的,如果是产品这块东西可以去掉,他的原型在main.c里面。在这里调用和内核共用的串口代码:

BOOL OEMDebugInit()

{

// Assign callback functions to be usec by blcommon.

//

g_pOEMReportError = OEMReportError;

g_pOEMVerifyMemory = OEMVerifyMemory; // Verify memory to be used by downloaded image...

g_pOEMMultiBINNotify = OEMMultiBINNotify; // Notified of all the BIN files to be downloaded...

OEMInitDebugSerial();

return TRUE;

}

关于OEMInitDebugSerial()在内核代码中,关于串口的配置和使用比较简单,这里不再多讲,顺便让大家注意一下就是检查你的串口是UART0还是UART1,同时在加载内核的时候务必不要让同样的资源进行冲突,不然系统会报错的。

3)OEMPlatformInit ()

这里初始化串口后会在PC机上打印出一些调试和版本信息。接下来的一个函数是EBOOT里面比较重要的: OEMPlatformInit (),这个函数在下载之前做了大量的硬件和资源方面的准备工作,其原型也在main.c里面,其作用由于相当的重要,所以我进行了详细的注释,如下:

BOOL OEMPlatformInit()

{

SYSTEMTIME st;

SYSTEMTIME defst = {2002, 1, 0, 1, 12, 0, 0, 0};

DWORD dwStartTime, dwPrevTime, dwCurrTime;

int cKeySelect = 0;

DWORD dwBootDelay = 10; // seconds. N.B: change for retail device!

#ifdef SIMULATOR

EdbgOutputDebugString("Microsoft Windows CE SMDK2440 Bootloader *** SIMULATOR *** /r/n");

#else

EdbgOutputDebugString("Microsoft Windows CE SMDK2440 Bootloader Version %d.%d Built %s %s /r/n",

EBOOT_VERSION_MAJOR, EBOOT_VERSION_MINOR, __DATE__, __TIME__);

#endif

// Initialize the globals

//

memset((LPVOID) &(pDriverGlobals->eth), 0, DBG_ETH_GLOBALS_SIZE);

memset((LPVOID) &g_TOC, 0, sizeof(g_TOC));

//对全局变量清零初始化

// This should not change unless reserved blocks are added/removed;

// made global to do the calc only once.

g_dwImageStartBlock = IMAGE_START_BLOCK;

// Check real time clock, initialize if necessary (used for polling in net routines)

//

OEMGetRealTime(&st);

if ((st.wYear < 2000) ||

(st.wMonth < 1) ||

(st.wMonth > 12) ||

(st.wDay < 1) ||

(st.wDay > 31) ||

(st.wHour > 23) ||

(st.wMinute > 59) ||

(st.wSecond > 59) ||

(st.wMilliseconds > 999)) {

OEMSetRealTime(&defst);

}

else {

OEMSetRealTime(&st);

}

//获取并且初始化实时时钟

if (!InitUSB())

{

DEBUGMSG(1, (TEXT("OEMPlatformInit: Failed to initialize USB./r/n")));

return(FALSE);

}

//这块相当重要,虽然EBOOT是基于网络下载内核的,但是这里是基于USB,所以在这里初始化USB的参数,后面还将详细的说明

Isr_Init();

//中断初始化,这里是为USB下载做准备工作

// Try to initialize the boot media block driver and BinFS partition.

//

if ( !BP_Init((LPBYTE)BINFS_RAM_START, BINFS_RAM_LENGTH, NULL, NULL, NULL) )

{

//这里是对磁盘初始化,为后期将内核下载后存入FLASH做准备

EdbgOutputDebugString("WARNING: OEMPlatformInit failed to initialize Boot Media./r/n/r/n");

g_bBootMediaExist = FALSE;

}

else

g_bBootMediaExist = TRUE;

// Try to retrieve TOC (and Boot config) from boot media

//

if ( !TOC_Read( ) ) {

// use default settings

TOC_Init(DEFAULT_IMAGE_DESCRIPTOR, (IMAGE_TYPE_RAMIMAGE|IMAGE_TYPE_BINFS), 0, 0, 0);

}

//TOC的初始化

// Start boot monitor prompt

//

dwBootDelay = g_pBootCfg->BootDelay;

if (g_pBootCfg->ConfigFlags & BOOT_TYPE_DIRECT)

{

EdbgOutputDebugString ( "Press [ENTER] to launch image stored on boot media, or [SPACE] to enter boot monitor./r/n");

EdbgOutputDebugString ( "/r/nInitiating image launch in %d seconds. ", dwBootDelay--);

} else

{

EdbgOutputDebugString ( "Press [ENTER] to download image now, or [SPACE] to enter boot monitor./r/n");

EdbgOutputDebugString ( "/r/nInitiating image download in %d seconds. ", dwBootDelay--);

}

dwStartTime = OEMEthGetSecs();

dwPrevTime = dwStartTime;

dwCurrTime = dwStartTime;

// allow the user to break into boot monitor

while((dwCurrTime - dwStartTime) < dwBootDelay)

{

cKeySelect = OEMReadDebugByte();

if ((cKeySelect == 0x20) || (cKeySelect == 0x0d))

break;

dwCurrTime = OEMEthGetSecs();

if (dwCurrTime > dwPrevTime)

{

int i=0, j;

// 1 Second has elapsed - update the countdown timer.

dwPrevTime = dwCurrTime;

if (dwBootDelay < 9)

i = 11;

else if (dwBootDelay < 99)

i = 12;

else if (dwBootDelay < 999)

i = 13;

for(j = 0; j < i; j++)

OEMWriteDebugByte((BYTE)0x08); // print back space

EdbgOutputDebugString ( "%d seconds. ", dwBootDelay--);

}

}

EdbgOutputDebugString ( "/r/n");

//上面的代码虽然多,但是功能简单,就是等待用户选择是从USB下载还是从硬盘读取

switch(cKeySelect)

{

case 0x20: // Boot monitor.

g_bDownloadImage = BootMonitor( );

//如果是下载,给出菜单选择,否则从硬盘读取

break;

case 0x00: // Fall through if no keys were pressed -or-

case 0x0d: // the user cancelled the countdown.

default:

EdbgOutputDebugString ("/r/n direct_g_pBootCfg->ConfigFlags =%x/r/n",g_pBootCfg->ConfigFlags);

if (g_pBootCfg->ConfigFlags & BOOT_TYPE_DIRECT)

{

EdbgOutputDebugString ( "/r/nLaunching image from boot media ... /r/n");

g_bDownloadImage = FALSE;

}

else

{

EdbgOutputDebugString ( "/r/nStarting auto-download ... /r/n");

g_bDownloadImage = TRUE;

}

break;

}

// NOTE - right now, we assume that if we're downloading, it's done over Ethernet.

// In the future, this may include other transports (USB, etc.).

//

if ( !g_bDownloadImage )

{

// User doesn't want to download image - load it from the boot media.

// We could read an entire nk.bin or nk.nb0 into ram and jump.

if ( !VALID_TOC(g_pTOC) ) {

EdbgOutputDebugString("OEMPlatformInit: ERROR_INVALID_TOC, can not autoboot./r/n");

return FALSE;

}

EdbgOutputDebugString ( "/r/ng_ImageType_from =%x/r/n",g_ImageType);

switch (g_ImageType) {

//开始从硬盘读取内核,检验内核格式,在这里是

case IMAGE_TYPE_LOADER:

EdbgOutputDebugString("OEMPlatformInit: IMAGE_TYPE_LOADER/r/n");

break;

case IMAGE_TYPE_RAMIMAGE:

EdbgOutputDebugString("OEMPlatformInit: IMAGE_TYPE_RAMIMAGE/r/n");

if ( !ReadRamImageFromBootMedia( ) ) {

RETAILMSG(1, (TEXT("OEMPlatformInit ERROR: Failed to load kernel region into RAM./r/n")));

return FALSE;

}

break;

case (IMAGE_TYPE_RAMIMAGE|IMAGE_TYPE_BINFS):

case (IMAGE_TYPE_RAMIMAGE|IMAGE_TYPE_BINFS|IMAGE_TYPE_MXIP):

EdbgOutputDebugString("OEMPlatformInit: IMAGE_TYPE_RAMIMAGE|IMAGE_TYPE_BINFS|IMAGE_TYPE_MXIP/r/n");

// N.B: this assumes the image is setup as multi-bin for BinFS.

if ( !ReadKernelRegionFromBootMedia( ) ) {

//从硬盘加载内核

RETAILMSG(1, (TEXT("OEMPlatformInit ERROR: Failed to load kernel region into RAM./r/n")));

return FALSE;

}

break;

default:

EdbgOutputDebugString("OEMPlatformInit ERROR: unknown image type: 0x%x /r/n", g_ImageType );

return FALSE;

}

}

// If user specified a static IP address, use it (don't use DHCP).

//

if (g_bDownloadImage && !(g_pBootCfg->ConfigFlags & CONFIG_FLAGS_DHCP))

{

pDriverGlobals->eth.TargetAddr.dwIP = g_pBootCfg->EdbgAddr.dwIP;

pDriverGlobals->eth.SubnetMask = g_pBootCfg->SubnetMask;

}

// Configure Ethernet controller.

//

if ( g_pBootCfg->ConfigFlags & CONFIG_FLAGS_DEBUGGER )

{

if ( g_bUSBDownload == FALSE )

{

/* if (!InitEthDevice(g_pBootCfg))

{

DEBUGMSG(1, (TEXT("OEMPlatformInit: Failed to initialize Ethernet controller./r/n")));

return(FALSE);

}*/

}

}

return TRUE;

}

这个函数初始化了USB、中断、BIFS硬盘格式以及TOC,然后与用户交互选择是在USB下载还是直接从硬盘直接读取。由于涉及东西比较关键,下面会分析这些东西:

首先是InitUSB(),这个函数在$(_TARGETPLATROOT)/EBOOT/USB.C下面,首先进行了USB描述符的初始化,后然进行各个端点的配置:

BOOL InitUSB()

{

BYTE index;

UCHAR cRxChar;

volatile INTreg *s2440INT = (INTreg *)INT_BASE;

pUSBCtrlAddr = (PUSHORT)(USB_BASE);

InitDescriptorTable();//USB标准描述符初始化,有兴趣的可以仔细研究一下,函数在同一个文件下面

//

// Initialize the USBD Controller

//这里USB使用的slave模式,ep0为控制端点,ep1/2/3/4为数据端点

index = pUSBCtrlAddr->INDEX.index;

// suspend mode disable

pUSBCtrlAddr->PMR.sus_en = 0x0;

// setup endpoint 0

pUSBCtrlAddr->INDEX.index = 0;

pUSBCtrlAddr->MAXP.maxp = 0x1; // 8 BYTE

pUSBCtrlAddr->EP0ICSR1.sopr_cdt = 1; //清除 OUT_PKT_RDY

pUSBCtrlAddr->EP0ICSR1.sse_ = 1; // 清除SETUP_END

// setup endpoint 1

pUSBCtrlAddr->INDEX.index = 1;

pUSBCtrlAddr->MAXP.maxp = 0x8; // 64 BYTE

pUSBCtrlAddr->EP0ICSR1.de_ff = 1;

pUSBCtrlAddr->EP0ICSR1.sopr_cdt = 1;

pUSBCtrlAddr->ICSR2.mode_in = 1; // IN

pUSBCtrlAddr->ICSR2.iso = 0; // BULK

// setup endpoint 3

pUSBCtrlAddr->INDEX.index = 3;

pUSBCtrlAddr->MAXP.maxp = 0x8; // 64 BYTE

pUSBCtrlAddr->EP0ICSR1.de_ff = 1;

pUSBCtrlAddr->EP0ICSR1.sopr_cdt = 1;

pUSBCtrlAddr->ICSR2.mode_in = 0; // OUT

pUSBCtrlAddr->OCSR2.iso = 0; // BULK

// clear all EP interrupts

pUSBCtrlAddr->EIR.ep0_int = 0x1;

pUSBCtrlAddr->EIR.ep1_int = 0x1;

pUSBCtrlAddr->EIR.ep2_int = 0x1;

pUSBCtrlAddr->EIR.ep3_int = 0x1;

pUSBCtrlAddr->EIR.ep4_int = 0x1;

// clear reset int

pUSBCtrlAddr->UIR.reset_int = 0x1;

// EP0, 1, & 3 Enabled, EP2, 4 Disabled

pUSBCtrlAddr->EIER.ep0_int_en = 0x1;

pUSBCtrlAddr->EIER.ep2_int_en = 0x0;

pUSBCtrlAddr->EIER.ep4_int_en = 0x0;

pUSBCtrlAddr->EIER.ep1_int_en = 0x1;

pUSBCtrlAddr->EIER.ep3_int_en = 0x1;

// enable reset int

pUSBCtrlAddr->UIER.reset_int_en = 0x1;

return TRUE;

}

之后的Isr_Init()其实也是针对USB的一些东西,我们查看原型:

void Isr_Init(void)

{

volatile INTreg *s2440INT = (INTreg *)INT_BASE;

s2440INT->rINTMOD=0x0; // All=IRQ mode

s2440INT->rINTMSK=BIT_ALLMSK; // All interrupt is masked.

// make value to assemble code "b IsrHandler"

pISR =(unsigned)(0xEA000000)+(((unsigned)IsrHandler - (0x8c000000 + 0x18 + 0x8) )>>2);

//跳转到isrHandler进行中断访问

s2440INT->rSRCPND = BIT_USBD;

if (s2440INT->rINTPND & BIT_USBD) s2440INT->rINTPND = BIT_USBD;

s2440INT->rINTMSK &= ~BIT_USBD; // USB Interrupt enable.

s2440INT->rSRCPND = BIT_DMA2;

if (s2440INT->rINTPND & BIT_DMA2) s2440INT->rINTPND = BIT_DMA2;

s2440INT->rINTMSK |= BIT_DMA2;

//s2440INT->rINTMSK &= ~BIT_DMA2; // DMA Interrupt enable.

}

这里面由于没有中断机制的建立,所以用了一种很特别的方式实现了中断:

#define pISR (*(unsigned *)(0x30000000+0x18)),这里0x30000000可以换为0x80000000,也可以换为0x00000000,这是因为将虚拟地址0 map到了0x30000000,同时映射到了0x80000000。(因为系统的reset为0地址开始)。再结合 pISR =(unsigned)(0xEA000000)+(((unsigned)IsrHandler - (0x8c000000 + 0x18 + 0x8) )>>2),不难发现当发生中断的时候会自动跳转到isrHandler,关于上面这句的翻译有很多资料已经详细说明,这里就不再多说了。有兴趣的朋友可以给我发邮件咨询。

同时我们可以查看到下面一段汇编:

LEAF_ENTRY IsrHandler

; sub sp,sp,#4 ;decrement sp(to store jump address)

sub lr, lr, #4

stmfd sp!, {r0-r12,lr}

mov r0, lr

bl IsrUsbd

ldmfd sp!, {r0-r12,lr}

movs pc, lr

ENDP ; |IsrHandler|

END

这个是IsrHandler,实际上它将现场保护之后进入了IsrUsbd。通过上面的详细叙述,我们最终发现做了这么大工作的目的就是为了进行中断跳转到IsrUsbd()。也就是开启USB中断。

接下来我们看看BP_Init((LPBYTE)BINFS_RAM_START, BINFS_RAM_LENGTH, NULL, NULL, NULL),WINCE500/PUBLIC/COMMON/OAK/DRIVERS/ETHDBG/BOOTPART/bootpart.cpp下面有这个函数。这个函数主要是对分区的一些全局变量初始化:

BOOL BP_Init (LPBYTE pMemory, DWORD dwSize, LPCTSTR lpActiveReg, PPCI_REG_INFO pRegIn, PPCI_REG_INFO pRegOut)

{

DWORD dwBufferSize;

if (!pMemory) {

RETAILMSG(1,(TEXT("BP_Init Fails No memory fails!!!/r/n")));

return FALSE;

}

if (!FMD_Init (lpActiveReg, pRegIn, pRegOut))

return FALSE;

if (!FMD_GetInfo (&g_FlashInfo)) {

RETAILMSG(1,(TEXT("BP_Init Fails FMD_GetInfo fails!!!/r/n")));

return FALSE;

}

// Check to make sure size is enough for one sector, one block, and sectorinfo buffer for one block

g_dwDataBytesPerBlock = g_FlashInfo.wDataBytesPerSector * g_FlashInfo.wSectorsPerBlock;

dwBufferSize = g_FlashInfo.wDataBytesPerSector + g_dwDataBytesPerBlock +

g_FlashInfo.wSectorsPerBlock * sizeof(SectorInfo);

if (dwSize < dwBufferSize) {

RETAILMSG(1,(TEXT("BP_Init Fails buffer size = %x < required = %x!!!/r/n"),dwSize,dwBufferSize));

return FALSE;

}

for (int i = 0; i < NUM_PARTS; i++) {

g_partStateTable[i].pPartEntry= NULL;

g_partStateTable[i].dwDataPointer = 0;

}

g_pbMBRSector = pMemory; //size = g_FlashInfo.wDataBytesPerSector;

g_pbBlock = pMemory + g_FlashInfo.wDataBytesPerSector; //size = g_dwDataBytesPerBlock;

g_pSectorInfoBuf = (PSectorInfo)(g_pbBlock + g_dwDataBytesPerBlock); //size = g_FlashInfo.wSectorsPerBlock * sizeof(SectorInfo);

g_dwLastLogSector = 0;

return TRUE;

}

主要是对全局变量g_pbMBRSector 、g_pbBlock和g_pSectorInfoBuf 进行初始化,分配内存后g_pbMBRSector是最开始的地址,占用一个sector(512B),接下来是一个BLOCK的缓存,开始地址为g_pbBlock,最后是g_pSectorInfoBuf,占用10个sector地址。所以在这里g_pbMBRSector开始地址是(BIN_FS 8c021000 00005000 RESERVED)的开始地址,由于是不能缓存的,所以是在0xAc021000。大小为5000是因为size >= 1 sector(g_pbMBRSector所用) + 1 block(g_pbBlock所用) + 32 个sector信息(16B)大小(g_pSectorInfoBuf所用) = 512B + 512B X 32 + 32 X 16B = 0x4400B。故这里大小选为5000,如果flash型号不一样这些参数都需要改变。

下面主要是对g_pbMBRSector指向的1个sector的结构体进行一下描述和说明,从下面的图中可以看到对于这一个sector(512B)的资源分配:

其中:

· 主引导程序(偏移地址0000H--0088H ),它负责从活动分区中装载,并运行系统引导程序。

· 出错信息数据区,偏移地址0089H--00E1H 为出错信息,00E2H--01BDH 全为0 字节。

· 分区表(DPT,Disk Partition Table )含4 个分区项,偏移地址01BEH--01FDH, 每个分区表项长16 个字节,共64 字节为分区项1 、分区项2 、分区项3 、分区项4 。

· 结束标志字,偏移地址01FE--01FF 的2 个字节值为结束标志55AA, 如果该标志错误系统就不能启动。(这里参考http://blog.csdn.net/wu_ye_zhou/archive/2010/06/12/5667136.aspx)

而对于每个分区其结构体定义如下表所示,其中在bootpart.h中也定义了这个结构体:

在这里所谓的磁头就是block,扇区就是sector,柱面在这里没有意思。结构体如下:

typedef struct _PARTENTRY {

BYTE Part_BootInd; // If 80h means this is boot partition

BYTE Part_FirstHead; // Partition starting head based 0

BYTE Part_FirstSector; // Partition starting sector based 1

BYTE Part_FirstTrack; // Partition starting track based 0

BYTE Part_FileSystem; // Partition type signature field

BYTE Part_LastHead; // Partition ending head based 0

BYTE Part_LastSector; // Partition ending sector based 1

BYTE Part_LastTrack; // Partition ending track based 0

DWORD Part_StartSector; // Logical starting sector based 0

DWORD Part_TotalSectors; // Total logical sectors in partition

} PARTENTRY;

typedef PARTENTRY UNALIGNED *PPARTENTRY;

下面是TOC_Read( )与TOC_Init()函数,这两个函数是对TOC结构体进行的操作,主要用于数据从硬盘读到RAM中的引导操作。其原型都在$(_TARGETPLATROOT)/eboot/fmd.cpp中。其中对TOC结构体的定义在$(_TARGETPLATROOT)/inc/loader.h中。

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

// UCHAR Pad[12]; // align on SECTOR_SIZE

CHAININFO chainInfo;

} TOC, *PTOC; // 512 bytes

从上面可以看出这里主要是IMAGE_DESCRIPTOR这个结构体,关于它的定义也在同一个目录下面:

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];//描述在FLASH的存储地址和大小

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

typedef struct _SG_SECTOR {

DWORD dwSector; // Starting sector of the image segment

DWORD dwLength; // Image length of this segment, in contigious sectors.

} SG_SECTOR, *PSG_SECTOR;

对这个结构体了解之后我们先来看

BOOL TOC_Read(void)

{

SectorInfo si;

// EdbgOutputDebugString("TOC_Read/r/n");

if ( !g_bBootMediaExist ) {

EdbgOutputDebugString("TOC_Read ERROR: no boot media/r/n");

return FALSE;

}

if ( !FMD_ReadSector(TOC_SECTOR, (PUCHAR)g_pTOC, &si, 1) ) {

EdbgOutputDebugString("TOC_Read ERROR: Unable to read TOC/r/n");

return FALSE;

}//读取硬盘中的TOC参数

// is it a valid TOC?

if ( !VALID_TOC(g_pTOC) ) {

EdbgOutputDebugString("TOC_Read ERROR: INVALID_TOC Signature: 0x%x/r/n", g_pTOC->dwSignature);

return FALSE;

}//判断是否有效

// is it an OEM block?

if ( (si.bBadBlock != BADBLOCKMARK) || !(si.bOEMReserved & (OEM_BLOCK_RESERVED | OEM_BLOCK_READONLY)) ) {

EdbgOutputDebugString("TOC_Read ERROR: SectorInfo verify failed: %x %x %x %x/r/n",

si.dwReserved1, si.bOEMReserved, si.bBadBlock, si.wReserved2);

return FALSE;

}

// update our boot config

g_pBootCfg = &g_pTOC->BootCfg;

// update our index

g_dwTocEntry = g_pBootCfg->ImageIndex;

// debugger enabled?

g_bWaitForConnect = (g_pBootCfg->ConfigFlags & CONFIG_FLAGS_DEBUGGER) ? TRUE : FALSE;

// cache image type

g_ImageType = g_pTOC->id[g_dwTocEntry].dwImageType;

//更新关于TOC的全局变量

//TOC_Print( );

// EdbgOutputDebugString("-TOC_Read/r/n");

return TRUE;

}

实际在刚开始硬盘中没东西的时候这个函数返回是错误的(TOC存在block1中),然后就开始初始化TOC,函数如下:

// init the TOC to defaults

BOOL TOC_Init(DWORD dwEntry, DWORD dwImageType, DWORD dwImageStart, DWORD dwImageLength, DWORD dwLaunchAddr)

{

DWORD dwSig = 0;

EdbgOutputDebugString("TOC_Init: dwEntry:%u, dwImageType: 0x%x, dwImageStart: 0x%x, dwImageLength: 0x%x, dwLaunchAddr: 0x%x/r/n",

dwEntry, dwImageType, dwImageStart, dwImageLength, dwLaunchAddr);

if (0 == dwEntry) {

EdbgOutputDebugString("/r/n*** WARNING: TOC_Init blasting Eboot ***/r/n");

TEST_TRAP;

}

switch (dwImageType) {

case IMAGE_TYPE_LOADER:

dwSig = IMAGE_EBOOT_SIG;

break;

case IMAGE_TYPE_RAMIMAGE:

dwSig = IMAGE_RAM_SIG;

break;

case (IMAGE_TYPE_RAMIMAGE|IMAGE_TYPE_BINFS):

dwSig = IMAGE_BINFS_SIG;

break;

default:

EdbgOutputDebugString("ERROR: OEMLaunch: unknown image type: 0x%x /r/n", dwImageType);

return FALSE;

}//确认dwSig为哪一个,对于我们硬盘启动方式为IMAGE_BINFS_SIG

memset(g_pTOC, 0, sizeof(g_TOC));

// init boof cfg

BootConfigInit(dwEntry);//这里索引为block索引,为1

// update our index

g_dwTocEntry = dwEntry;

// debugger enabled?

g_bWaitForConnect = (g_pBootCfg->ConfigFlags & CONFIG_FLAGS_DEBUGGER) ? TRUE : FALSE;

// init TOC...

g_pTOC->dwSignature = TOC_SIGNATURE;

// init TOC entry for Eboot

// Those are hard coded numbers from boot.bib

g_pTOC->id[0].dwVersion = (EBOOT_VERSION_MAJOR << 16) | EBOOT_VERSION_MINOR;

g_pTOC->id[0].dwSignature = IMAGE_EBOOT_SIG;

memcpy(g_pTOC->id[0].ucString, "eboot.nb0", sizeof("eboot.nb0")+1); // NUll terminate

g_pTOC->id[0].dwImageType = IMAGE_TYPE_RAMIMAGE;

g_pTOC->id[0].dwLoadAddress = EBOOT_RAM_IMAGE_BASE;

g_pTOC->id[0].dwJumpAddress = EBOOT_RAM_IMAGE_BASE;

g_pTOC->id[0].dwTtlSectors = FILE_TO_SECTOR_SIZE(EBOOT_RAM_IMAGE_SIZE);

// 1 contigious segment

g_pTOC->id[0].sgList[0].dwSector = BLOCK_TO_SECTOR(EBOOT_BLOCK);

g_pTOC->id[0].sgList[0].dwLength = g_pTOC->id[0].dwTtlSectors;

//这部分是id[0]存储EBOOT的调用,当EBOOT烧写在block2的时候,nboot将用TOC引导其运行在RAM中

// init the TOC entry

g_pTOC->id[dwEntry].dwVersion = 0x001;

g_pTOC->id[dwEntry].dwSignature = dwSig;

memset(g_pTOC->id[dwEntry].ucString, 0, IMAGE_STRING_LEN);

g_pTOC->id[dwEntry].dwImageType = dwImageType;

g_pTOC->id[dwEntry].dwLoadAddress = dwImageStart;

g_pTOC->id[dwEntry].dwJumpAddress = dwLaunchAddr;

g_pTOC->id[dwEntry].dwStoreOffset = 0;

g_pTOC->id[dwEntry].dwTtlSectors = FILE_TO_SECTOR_SIZE(dwImageLength);

// 1 contigious segment

g_pTOC->id[dwEntry].sgList[0].dwSector = BLOCK_TO_SECTOR(g_dwImageStartBlock);

g_pTOC->id[dwEntry].sgList[0].dwLength = g_pTOC->id[dwEntry].dwTtlSectors;

//这部分在第一次初始化的时候仅仅是填充了一些0,而在真正下载内核到RAM后会填写上内核的一些基本信息,包括启动地址和大小

TOC_Print();

return TRUE;

}

上面这个地方的初始化其实也很简单,主要是对EBOOT和NK的一些磁盘中起始地址和大小,以及移动到RAM中的运行地址进行了描述,在启动系统的时候索引调用。

如果是从磁盘启动,我们首先来看一下ReadKernelRegionFromBootMedia( )这个函数,这个函数的意义是将内核代码移动到内存中。原型也在同一个目录下的fmd.cpp中。(注意这些函数由于内核也会用到,所以都属于eboot和内核共用代码),实际这个函数是很复杂的,里面涉及了好多关于磁盘的知识,后面我将专门设置一节来分析,所以关于这一块的详细东西可以查看附录。

BOOL ReadKernelRegionFromBootMedia( )

{

HANDLE hPart;

SectorInfo si;

DWORD chainaddr, flashaddr;

int i;

if (!g_bBootMediaExist) {

EdbgOutputDebugString("ERROR: ReadKernelRegionFromBootMedia: device doesn't exist./r/n");

return(FALSE);

}

if ( !VALID_TOC(g_pTOC) ) {

EdbgOutputDebugString("ERROR: ReadKernelRegionFromBootMedia: INVALID_TOC/r/n");

return(FALSE);

}

if ( !(IMAGE_TYPE_BINFS & g_pTOC->id[g_dwTocEntry].dwImageType) ) {

EdbgOutputDebugString("ERROR:ReadKernelRegionFromBootMedia: INVALID_IMAGE_TYPE: 0x%x/r/n",

g_pTOC->id[g_dwTocEntry].dwImageType);

return(FALSE);

}

if ( !VALID_IMAGE_DESCRIPTOR(&g_pTOC->id[g_dwTocEntry]) ) {

EdbgOutputDebugString("OEMPlatformInit: ERROR_INVALID_IMAGE_DESCRIPTOR: 0x%x/r/n",

g_pTOC->id[g_dwTocEntry].dwSignature);

return FALSE;

}

if ( !OEMVerifyMemory(g_pTOC->id[g_dwTocEntry].dwLoadAddress, sizeof(DWORD)) ||

!OEMVerifyMemory(g_pTOC->id[g_dwTocEntry].dwJumpAddress, sizeof(DWORD)) ||

!g_pTOC->id[g_dwTocEntry].dwTtlSectors )

{

EdbgOutputDebugString("OEMPlatformInit: ERROR_INVALID_ADDRESS: (address=0x%x, sectors=0x%x, launchaddress=0x%x).../r/n",g_pTOC->id[g_dwTocEntry].dwLoadAddress,g_pTOC->id[g_dwTocEntry].dwTtlSectors, g_pTOC->id[g_dwTocEntry].dwJumpAddress);

return FALSE;

}

EdbgOutputDebugString("INFO: Loading image from Boot Media to RAM (address=0x%x, sectors=0x%x, launchaddress=0x%x).../r/n",g_pTOC->id[g_dwTocEntry].dwLoadAddress,g_pTOC->id[g_dwTocEntry].dwTtlSectors, g_pTOC->id[g_dwTocEntry].dwJumpAddress);

// Open the BINFS partition (it must exist).

//

hPart = BP_OpenPartition( NEXT_FREE_LOC,

USE_REMAINING_SPACE,

PART_BINFS,

TRUE,

PART_OPEN_EXISTING);

//打开并且找到MBR的索引

if (hPart == INVALID_HANDLE_VALUE )

{EdbgOutputDebugString("ERROR: ReadKernelRegionFromBootMedia: Failed to open existing BINFS partition./r/n");

return(FALSE);

}

// Set the partition file pointer to the correct offset for the kernel region.

if ( !BP_SetDataPointer(hPart, g_pTOC->id[g_dwTocEntry].dwStoreOffset) )

{

EdbgOutputDebugString("ERROR: ReadKernelRegionFromBootMedia: Failed to set data pointer in BINFS partition (offset=0x%x)./r/n", g_pTOC->id[g_dwTocEntry].dwStoreOffset);

return(FALSE);

}//如果数据存储有偏移,设置补偿

// Read the kernel region from the Boot Media into RAM.

if ( !BP_ReadData( hPart,

(LPBYTE)(g_pTOC->id[g_dwTocEntry].dwLoadAddress),

SECTOR_TO_FILE_SIZE(g_pTOC->id[g_dwTocEntry].dwTtlSectors)) )

{

EdbgOutputDebugString("ERROR: ReadKernelRegionFromBootMedia: Failed to read kernel region from BINFS partition./r/n");

return(FALSE);

}

//这里是代码的关键,将内核读取到RAM中

EdbgOutputDebugString("g_pTOC->chainInfo.dwLength=0x%x/r/n", g_pTOC->chainInfo.dwLength);

chainaddr = g_pTOC->chainInfo.dwLoadAddress;

flashaddr = g_pTOC->chainInfo.dwFlashAddress;

for ( i = 0; i < (g_pTOC->chainInfo.dwLength); i++ )

{

EdbgOutputDebugString("chainaddr=0x%x, flashaddr=0x%x/r/n", chainaddr, flashaddr+i);

if ( !FMD_ReadSector(flashaddr+i, (PUCHAR)(chainaddr), &si, 1) ) {

EdbgOutputDebugString("TOC_Write ERROR: Unable to read/verify TOC/r/n");

return FALSE;

}

chainaddr += 512;

}

//读取chain到内存

return(TRUE);

}

(待续。。。。)

英雄数量
最直观血缘路线攻略!!求加精!!