您的位置:首页 > 其它

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

2010-10-10 18:05 387 查看
由于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);
}


(待续。。。。)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: