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

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

1KernelRelocate()

第一个函数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中不能直接进行写操作)。

2OEMDebugInit ()

第二个函数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,同时在加载内核的时候务必不要让同样的资源进行冲突,不然系统会报错的。

3OEMPlatformInit ()

这里初始化串口后会在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。(因为系统的reset0地址开始)。再结合  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_pbBlockg_pSectorInfoBuf 进行初始化,分配内存后g_pbMBRSector是最开始的地址,占用一个sector512B),接下来是一个BLOCK的缓存,开始地址为g_pbBlock,最后是g_pSectorInfoBuf,占用10sector地址。所以在这里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指向的1sector的结构体进行一下描述和说明,从下面的图中可以看到对于这一个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;

}

上面这个地方的初始化其实也很简单,主要是对EBOOTNK的一些磁盘中起始地址和大小,以及移动到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);

}

 

(待续。。。。)

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值