Bootloader 的主控函数bootloadermain函数分析

startup.s对底层进行初始化后,调用main()此函数一般位于main.c下面

main函数调用BLCOMMON.c下的BootloaderMain()函数,bootloader的主控权丧失。

学习何老师书的内容摘抄如下:

 //目录是%/WINCE500/PUBLIC/COMMON/OAK/DRIVERS/ETHDBG/BLCOMMON下的BLCOMMON.c文件中的内容

void BootloaderMain (void)
{
    DWORD dwAction;  
    DWORD dwpToc = 0;
    DWORD dwImageStart = 0, dwImageLength = 0, dwLaunchAddr = 0;
    BOOL bDownloaded = FALSE;

    // relocate globals to RAM// 把全局变量定位到ram
    if (!KernelRelocate (pTOC))
    {
        // spin forever     //出错处理
        HALT (BLERR_KERNELRELOCATE);
    }

    // (1) Init debug support. We can use OEMWriteDebugString afterward.
 //初始化调试端口,我们就可以使用OEMWriteDebugString 函数
    if (!OEMDebugInit ())
    {
        // spin forever
        HALT (BLERR_DBGINIT);
    }

    // output banner
    EdbgOutputDebugString (NKSignon, CURRENT_VERSION_MAJOR, CURRENT_VERSION_MINOR);

    // (3) initialize platform (clock, drivers, transports, etc)
 //初始化平台(时钟,驱动,和传输)
    if (!OEMPlatformInit ())
    {
        // spin forever
        HALT (BLERR_PLATINIT);
    }

    // system ready, preparing for download
    EdbgOutputDebugString ("System ready!/r/nPreparing for download.../r/n");

    // (4) call OEM specific pre-download function
 //调用OEM编写的OEMPreDownload函数决定是否下载
    switch (dwAction = OEMPreDownload ())
    {
    case BL_DOWNLOAD:
        // (5) download image
 //下载镜像
        if (!DownloadImage (&dwImageStart, &dwImageLength, &dwLaunchAddr))
        {
            // error already reported in DownloadImage
            SPIN_FOREVER;
        }
        bDownloaded = TRUE;

        // Check for pTOC signature ("CECE") here, after image in place
 //检测下载镜像的签名
        if (*(LPDWORD) OEMMapMemAddr (dwImageStart, dwImageStart + ROM_SIGNATURE_OFFSET) == ROM_SIGNATURE)
        {
            dwpToc = *(LPDWORD) OEMMapMemAddr (dwImageStart, dwImageStart + ROM_SIGNATURE_OFFSET + sizeof(ULONG));
            // need to map the content again since the pointer is going to be in a fixup address
     //再一次映射内容
            dwpToc = (DWORD) OEMMapMemAddr (dwImageStart, dwpToc + g_dwROMOffset);

            EdbgOutputDebugString ("ROMHDR at Address %Xh/r/n", dwImageStart + ROM_SIGNATURE_OFFSET + sizeof (DWORD)); //

right after signature
        }

        // fall through
 //没有break,下载后直接继续执行
    case BL_JUMP:
        // Before jumping to the image, optionally check the image signature.
        // NOTE: if we haven't downloaded the image by now, we assume that it'll be loaded from local storage in OEMLaunch

(or it
        // already resides in RAM from an earlier download), and in this case, the image start address might be 0.  This

means
        // that the image signature routine will need to find the image in storage or in RAM to validate it.  Since the OEM"s
        // OEMLaunch function will need to do this anyways, we trust that it's within their abilities to do it here.
        //
 //在启动操作系统之前,再次检查签名,如果没有下载,那么OEMLauch函数应该有能力
 //启动存在本地存储中的镜像
        if (g_bBINDownload && g_pOEMCheckSignature)
        {
            if (!g_pOEMCheckSignature(dwImageStart, g_dwROMOffset, dwLaunchAddr, bDownloaded))
                HALT(BLERR_CAT_SIGNATURE);
        }
        // (5) final call to launch the image. never returned
 //最终启动操作系统镜像,此函数不会返回
        OEMLaunch (dwImageStart, dwImageLength, dwLaunchAddr, (const ROMHDR *)dwpToc);
        // should never return
 //不应该执行到这里
        // fall through
    default:
        // ERROR! spin forever
 //出错
        HALT (BLERR_INVALIDCMD);
    }
}

/*

重定位函数KernelRelocate ()
重定位的目的是:确保全局变量可被写
(主要防止在只读介质上运行BootLoader的时候出错,重新将全局变量定位到RAM中就实现全局变量的可写)
pTOC定义在BLCommon.c文件中: ROMHDR * volatile const pTOC=(ROMHDR *)-1;
ROMHDR指针描述整个ROM的几乎所有信息,但代码本身不带有ROM信息
那么pTOC什么时候被初始化的呢?
全局变量pTOC使用RomImage.exe把EBOOT打包为ROM文件的时候初始化的
*/
static BOOL KernelRelocate (ROMHDR *const pTOC)
{
    ULONG loop;
    COPYentry *cptr;
    //如果pTOC为-1,则重定位失败
    if (pTOC == (ROMHDR *const) -1)
    {
        return (FALSE); // spin forever!
    }
    // This is where the data sections become valid... don't read globals until after this
    //根据pTOC结构体的信息,获得要复制的源地址、目标地址和长度
    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);
    //把多余的地址置0
        if (cptr->ulCopyLen != cptr->ulDestLen)
            memset((LPVOID)(cptr->ulDest+cptr->ulCopyLen),0,cptr->ulDestLen-cptr->ulCopyLen);
    }
    return (TRUE);
}

/*

OEMDebugInit()初始化调试端口函数一般在BSP中的“SRC/BOOTLOADER/EBOOT”Main.c文件里面。

OEMDebugInit()一般调用OEMInitDebugSerial()函数来初始化串口,对UART1端口进行初始化,

这样我们就可以通过超级终端看打印信息咯~

*/


/*OEMPlatformInit()函数
    @func   BOOL | OEMPlatformInit | Initialize the Samsung SMD2410 platform hardware.
    @rdesc  TRUE = Success, FALSE = Failure.
    @comm   
    @xref 
初始化目标板上的设备函数OEMPlatformInit()函数,主要使得EBoot成功使用一些设备
如:    1、实时钟(用于网络下载及用户输入判断);
        2、显示屏(用于操作系统启动时,在屏幕上显示的logo等信息)
 3、Flash(镜像下载后须烧录到Flash中或者其他方式要使用Flash。此外,FMD驱动程序也可以在此初始化)
 4、网卡(EBoot通过网卡下载镜像。在这里可以初始化网卡,包括MAC地址,I/O方式等)
 5、BSP的共享参数(OAL与EBoot会共享一些参数,即EBoot会将一些参数传给OAL使用,在此可以给参数初始化)
 6、此外,要在EBoot中输出菜单的超级终端中与界面交互,也可以在此使用
*/
BOOL OEMPlatformInit(void)
{
    UINT8 BootDelay;
    UINT8 KeySelect;
    UINT32 dwStartTime, dwPrevTime, dwCurrTime;
    PCI_REG_INFO NANDInfo;
    BOOLEAN bResult = FALSE;

    OALMSG(OAL_FUNC, (TEXT("+OEMPlatformInit./r/n")));

    EdbgOutputDebugString("Microsoft Windows CE Bootloader for the Samsung SMDK2410 Version %d.%d Built %s/r/n/r/n",
                          EBOOT_VERSION_MAJOR, EBOOT_VERSION_MINOR, __DATE__);

    // Initialize the display.
    //
 //初始化显示设备
    InitDisplay();
 //可以在InitDisplay()完成之后,用memcpy把定义在bitmap.c中的数组复制到帧缓冲内,从而实现开机启动画面

    // Initialize the RealTimeClock
 //初始化实时时钟,因为后面许多判断超时需要用到,所以这一步骤是必须的
    InitRealTimeClock();

    // Initialize the BSP args structure.
    //初始化BSP的共享参数,在2410 EBoot中定义了一个叫pBSPArgs的区域指针(loader.h文件中)
    //#define IMAGE_SHARE_ARGS_UA_START 0xAC020000
    //#define pBSPArgs ((BSP_ARGS *)IMAGE_SHARE_ARGS_US_START)
    //BSP_ARGS结构体有3个数据域:OAL_ARGS_HEADER描述版本信息和签名;
     deviceID设备的描述符
     OAL_LITL_ARGS是KITL的一些信息,系统启动时候可以从共享中读取一些信息
    //Bootloader把信息写入这个内存区域,在OAL中就可以直接读取
    memset(pBSPArgs, 0, sizeof(BSP_ARGS));
    pBSPArgs->header.signature       = OAL_ARGS_SIGNATURE;
    pBSPArgs->header.oalVersion      = OAL_ARGS_VERSION;
    pBSPArgs->header.bspVersion      = BSP_ARGS_VERSION;
    pBSPArgs->kitl.flags             = OAL_KITL_FLAGS_ENABLED | OAL_KITL_FLAGS_VMINI;
    pBSPArgs->kitl.devLoc.IfcType    = Internal;
    pBSPArgs->kitl.devLoc.BusNumber  = 0;
    pBSPArgs->kitl.devLoc.LogicalLoc = BSP_BASE_REG_PA_CS8900A_IOBASE;

    // Initialize the AMD AM29LV800 flash code.
    //初始化AM29LV800芯片和Flash驱动
    //AM29LV800芯片用于保存启动信息
    //Flash驱动初始化,直接调用FMD_Init()函数
    if (!AM29LV800_Init((UINT32)AMD_FLASH_START))
    {
        OALMSG(OAL_ERROR, (TEXT("ERROR: OEMPlatformInit: Flash initialization failed./r/n")));
        goto CleanUp;
    }

    // Initialize the Smart Media flash driver (and partitioning code).
    //
    memset(&NANDInfo, 0, sizeof(PCI_REG_INFO));
    NANDInfo.MemBase.Num    = 1;
    NANDInfo.MemBase.Reg[0] = (DWORD)OALPAtoVA(S3C2410X_BASE_REG_PA_NAND, FALSE);
    if (!FMD_Init(NULL, &NANDInfo, NULL))
    {
        OALMSG(OAL_WARN, (TEXT("WARNING: OEMPlatformInit: Failed to initialize Smart Media./r/n")));
        g_bSmartMediaExist = FALSE;
    }
    else
    {
        g_bSmartMediaExist = TRUE;
    }

    // Retrieve eboot settings from AMD flash.
    // EBoot从AM29LV800芯片中读取启动信息
    if (!ReadBootConfig(&g_BootConfig))
    {
        OALMSG(OAL_ERROR, (TEXT("ERROR: OEMPlatformInit: Failed to retrieve bootloader settings from flash./r/n")));
        goto CleanUp;
    }

    // Display boot message - user can halt the autoboot by pressing any key on the serial terminal emulator.
    //检测用户在规定的时间内、在开发终端上,有无按键按下;有则,调用MainMenu函数打印启动菜单
    BootDelay = g_BootConfig.BootDelay;

    EdbgOutputDebugString ( "Press [ENTER] to download now or [SPACE] to cancel./r/n");
    EdbgOutputDebugString ( "/r/nInitiating image download in %d seconds. ", BootDelay--);
    //得到当前时间
    dwStartTime = OEMEthGetSecs();
    dwPrevTime  = dwStartTime;
    dwCurrTime  = dwStartTime;
    KeySelect   = 0;

    // Allow the user to break into the bootloader menu.
    //在BootDelay指定的时间内等待用户按键
    while((dwCurrTime - dwStartTime) < g_BootConfig.BootDelay)
    {
        KeySelect = OEMReadDebugByte();//读取用户按键
        if ((KeySelect == 0x20) || (KeySelect == 0x0d))
            break;
        dwCurrTime = OEMEthGetSecs();//获得当前时间
 //向用户回显剩余的等待时间
        if (dwCurrTime > dwPrevTime)
        {
            int i, j;

            // 1 Second has elapsed - update the countdown timer.
            dwPrevTime = dwCurrTime;
            if (BootDelay < 9)
                i = 11;
            else if (BootDelay < 99)
                i = 12;
            else if (BootDelay < 999)
                i = 13;

            for(j = 0; j < i; j++)
                OEMWriteDebugByte((BYTE)0x08); // print back space
            EdbgOutputDebugString ( "%d seconds. ", BootDelay--);
        }
    }
    EdbgOutputDebugString ( "/r/n");

    // Boot or enter bootloader menu.
    //判断用户按键情况
    switch(KeySelect)
    {
    case 0x20: // Boot menu.打印BootMenu
        MainMenu(&g_BootConfig);
        break;
    case 0x00: // Fall through if no keys were pressed -or-直接启动
    case 0x0d: // the user cancelled the countdown.
    default:
        EdbgOutputDebugString ( "/r/nStarting auto-download ... /r/n");
        break;
    }

    // Configure Ethernet controller.
    //调用InitEthDevice()初始化以太网
    if (!InitEthDevice(&g_BootConfig))
    {
        OALMSG(OAL_ERROR, (TEXT("ERROR: OEMPlatformInit: Failed to initialize Ethernet controller./r/n")));
        goto CleanUp;
    }

    bResult = TRUE;

CleanUp:

    OALMSG(OAL_FUNC, (TEXT("_OEMPlatformInit./r/n")));
    return(bResult);
}

/*位于main.c文件中
    @func   BOOL | MainMenu | Manages the Samsung bootloader main menu.
    @rdesc  TRUE == Success and FALSE == Failure.
    @comm   
    @xref  
*/
//实现用于与用户交互的菜单
static BOOL MainMenu(PBOOT_CFG pBootCfg)
{
    BYTE KeySelect = 0;
    BOOL bConfigChanged = FALSE;

    while(TRUE)
    {
        KeySelect = 0;
 //打印菜单
        EdbgOutputDebugString ( "/r/nEthernet Boot Loader Configuration:/r/n/r/n");
        EdbgOutputDebugString ( "0) IP address: %s/r/n",inet_ntoa(pBootCfg->IPAddr));
        EdbgOutputDebugString ( "1) Subnet mask: %s/r/n", inet_ntoa(pBootCfg->SubnetMask));
        EdbgOutputDebugString ( "2) DHCP: %s/r/n", (pBootCfg->ConfigFlags & CONFIG_FLAGS_DHCP)?"Enabled":"Disabled");
        EdbgOutputDebugString ( "3) Boot delay: %d seconds/r/n", pBootCfg->BootDelay);
        EdbgOutputDebugString ( "4) Reset to factory default configuration/r/n");
        EdbgOutputDebugString ( "5) Program disk image into SmartMedia card: %s/r/n", (pBootCfg->ConfigFlags &

CONFIG_FLAGS_SAVETOFLASH)?"Enabled":"Disabled");
        EdbgOutputDebugString ( "6) Program CS8900 MAC address/r/n");
        EdbgOutputDebugString ( "7) Low-level format the Smart Media card/r/n");
        EdbgOutputDebugString ( "D) Download image now/r/n");
        EdbgOutputDebugString ( "/r/nEnter your selection: ");

        while (! ( ( (KeySelect >= '0') && (KeySelect <= '7') ) ||
                   ( (KeySelect == 'D') || (KeySelect == 'd') ) ))
        {
            KeySelect = OEMReadDebugByte();//读取用户按键
        }

        EdbgOutputDebugString ( "%c/r/n", KeySelect);

        switch(KeySelect) //根据用户按键进行不同操作
        {
        case '0':           // Change IP address.
            SetIP(pBootCfg);
            bConfigChanged = TRUE;
            break;
        case '1':           // Change subnet mask.
            SetMask(pBootCfg);
            bConfigChanged = TRUE;
            break;
        case '2':           // Toggle static/DHCP mode.
            pBootCfg->ConfigFlags = (pBootCfg->ConfigFlags ^ CONFIG_FLAGS_DHCP);
            bConfigChanged = TRUE;
            break;
        case '3':           // Change autoboot delay.
            SetDelay(pBootCfg);
            bConfigChanged = TRUE;
            break;
        case '4':           // Reset the bootloader configuration to defaults.
            ResetBootConfig(pBootCfg);
            bConfigChanged = TRUE;
            break;
        case '5':           // Toggle image storage to Smart Media.
            pBootCfg->ConfigFlags = (pBootCfg->ConfigFlags ^ CONFIG_FLAGS_SAVETOFLASH);
            bConfigChanged = TRUE;
            break;
        case '6':           // Configure Crystal CS8900 MAC address.
            SetCS8900MACAddress(pBootCfg);
            bConfigChanged = TRUE;
            break;
        case '7':           // Format the Smart Media card.
            if (g_bSmartMediaExist && !FormatSmartMedia())
            {
                RETAILMSG(1, (TEXT("ERROR: Failed to perform low-level format of SmartMedia card./r/n")));
            }
            break;
        case 'D':           // Download? Yes.
        case 'd':
            goto MENU_DONE;
            break;
        default:
            break;
        }
    }

MENU_DONE:

    // If eboot settings were changed by user, save them to flash.
    //把用户的修改重新写回AM29LV800 Flash中
    if (bConfigChanged && !WriteBootConfig(pBootCfg))
    {
        OALMSG(OAL_WARN, (TEXT("WARNING: MainMenu: Failed to store updated bootloader configuration to flash./r/n")));
    }

    return(TRUE);
}

//位于Ethernet.c文件中

BOOL InitEthDevice(PBOOT_CFG pBootCfg)
{
    PBYTE  pBaseIOAddress = NULL;
    UINT32 MemoryBase = 0; 
    BOOL bResult = FALSE;

    OALMSG(OAL_FUNC, (TEXT("+InitEthDevice./r/n")));

    // Use the MAC address programmed into flash by the user.
    //
    memcpy(pBSPArgs->kitl.mac, pBootCfg->CS8900MAC, 6);

    // Use the CS8900A Ethernet controller for download.
    //第一步,注册Eboot用到的以太网回调函数
    pfnEDbgInit      = CS8900DBG_Init;
    pfnEDbgGetFrame  = CS8900DBG_GetFrame;
    pfnEDbgSendFrame = CS8900DBG_SendFrame;
    //取得网卡的IO的基地址与内存基地址
    pBaseIOAddress   = (PBYTE)OALPAtoVA(pBSPArgs->kitl.devLoc.LogicalLoc, FALSE);
    MemoryBase       = (UINT32)OALPAtoVA(BSP_BASE_REG_PA_CS8900A_MEMBASE, FALSE);
   
    // Initialize the Ethernet controller.
    //第二步,调用Init()函数
    if (!pfnEDbgInit((PBYTE)pBaseIOAddress, MemoryBase, pBSPArgs->kitl.mac))
    {
        OALMSG(OAL_ERROR, (TEXT("ERROR: InitEthDevice: Failed to initialize Ethernet

controller./r/n")));
        goto CleanUp;
    }

    // Make sure MAC address has been programmed.
    //
    if (!pBSPArgs->kitl.mac[0] && !pBSPArgs->kitl.mac[1] && !pBSPArgs->kitl.mac[2])
    {
        OALMSG(OAL_ERROR, (TEXT("ERROR: InitEthDevice: Invalid MAC address./r/n")));
        goto CleanUp;
    }

    bResult = TRUE;

CleanUp:

    OALMSG(OAL_FUNC, (TEXT("-InitEthDevice./r/n")));
    return(bResult);
}


/*OEMPreDownload(void)  Main.c文件
    @func   DWORD | OEMPreDownload | Complete pre-download tasks - get IP address, initialize TFTP, etc.
    @rdesc  BL_DOWNLOAD = Platform Builder is asking us to download an image, BL_JUMP = Platform Builder is requesting we

jump to an existing image, BL_ERROR = Failure.
    @comm   
    @xref  
*/
DWORD OEMPreDownload(void)
{
    BOOL  bGotJump = FALSE;
    DWORD dwDHCPLeaseTime = 0;
    PDWORD pdwDHCPLeaseTime = &dwDHCPLeaseTime;
    DWORD dwBootFlags = 0;


    // Create device name based on Ethernet address (this is how Platform Builder identifies this device).
    //调用OALKitlCreateNam()为设备创建一个独一无二的名字
    OALKitlCreateName(BSP_DEVICE_PREFIX, pBSPArgs->kitl.mac, pBSPArgs->deviceId);
    OALMSG(TRUE, (L"INFO: *** Device Name '%hs' ***/r/n", pBSPArgs->deviceId));

    // If the user wants to use a static IP address, don't request an address
    // from a DHCP server.  This is done by passing in a NULL for the DHCP
    // lease time variable.  If user specified a static IP address, use it (don't use DHCP).
    //
    if (!(g_BootConfig.ConfigFlags & CONFIG_FLAGS_DHCP))
    {
        // Static IP address.
        pBSPArgs->kitl.ipAddress  = g_BootConfig.IPAddr;
        pBSPArgs->kitl.ipMask     = g_BootConfig.SubnetMask;
        pBSPArgs->kitl.flags     &= ~OAL_KITL_FLAGS_DHCP;
        pdwDHCPLeaseTime = NULL;
        OALMSG(TRUE, (TEXT("INFO: Using static IP address %s./r/n"), inet_ntoa(pBSPArgs->kitl.ipAddress)));
        OALMSG(TRUE, (TEXT("INFO: Using subnet mask %s./r/n"),       inet_ntoa(pBSPArgs->kitl.ipMask)));
    }
    else
    {
        pBSPArgs->kitl.ipAddress = 0;
        pBSPArgs->kitl.ipMask    = 0;
    }

    // Initialize the the TFTP transport.
    //调用EbootInitEtherTransport()函数,完成网络大多数的初始化任务
    /*
 1)EbootInitEtherTransport()根据参数判断是否采用DHCP动态获取IP地址;
          YES,则通过EbootGetDHCPAddr()从DHCP服务器获取新IP地址;
          NO,则使用用户配置的静态IP;
 2)通过EbootCheckIP()检测当前IP是否可用(通过发送ARP包,判断是否有冲突。没有,则表示IP可用;
 3)通过EbootInitTftpSimple()函数在目标机端初始化Tftp服务器(扮演Tftp协议中的服务器角色的是开发板,客户端角色的是pc机);
 4)建立服务器与客户端之间的握手;
  开发板Eboot(a)      PC机 PB_IDEb)
 (a)向整个网段发送BOOTME消息(告诉PC机已经做好接受镜像的准备好了) 
 (b)接收到BOOTME消息后,PC端向开发板Eboot发送ACK信号,并根据用户的配置进行相应操作(告诉Eboot是下载或者进行Tftp连接)
    */
    g_DeviceAddr.dwIP = pBSPArgs->kitl.ipAddress;
    memcpy(g_DeviceAddr.wMAC, pBSPArgs->kitl.mac, (3 * sizeof(UINT16)));
    g_DeviceAddr.wPort = 0;

    if (!EbootInitEtherTransport(&g_DeviceAddr,
                                 &pBSPArgs->kitl.ipMask,
                                 &bGotJump,
                                 pdwDHCPLeaseTime,
                                 EBOOT_VERSION_MAJOR,
                                 EBOOT_VERSION_MINOR,
                                 BSP_DEVICE_PREFIX,
                                 pBSPArgs->deviceId,
                                 EDBG_CPU_ARM720,
                                 dwBootFlags))
    {
        OALMSG(OAL_ERROR, (TEXT("ERROR: OEMPreDownload: Failed to initialize Ethernet connection./r/n")));
        return(BL_ERROR);
    }


    // If the user wanted a DHCP address, we presumably have it now - save it for the OS to use.
    //
    if (g_BootConfig.ConfigFlags & CONFIG_FLAGS_DHCP)
    {
        // DHCP address.
        pBSPArgs->kitl.ipAddress  = g_DeviceAddr.dwIP;
        pBSPArgs->kitl.flags     |= OAL_KITL_FLAGS_DHCP;
    }

    return(bGotJump ? BL_JUMP : BL_DOWNLOAD);
}

//downloadimage函数blcommon.c

#define BL_HDRSIG_SIZE  7
static BOOL DownloadImage (LPDWORD pdwImageStart, LPDWORD pdwImageLength, LPDWORD pdwLaunchAddr)
{
    BYTE hdr[BL_HDRSIG_SIZE];
    DWORD dwRecLen, dwRecChk, dwRecAddr;
    BOOL fIsFlash = FALSE;
    LPBYTE lpDest = NULL;
    int nPkgNum = 0;
    BYTE nNumDownloadFiles = 1;
    DWORD dwImageStart = 0;
    DWORD dwImageLength = 0;
    RegionInfo *pCurDownloadFile;

    *pdwImageStart = *pdwImageLength = *pdwLaunchAddr = 0;

    do
    {
        // read the 7 byte "magic number"
        //读取7字节的魔术码
        if (!OEMReadData (BL_HDRSIG_SIZE, hdr))
        {
            EdbgOutputDebugString ("/r/nUnable to read image signature./r/n");
            HALT (BLERR_MAGIC);
            return (FALSE);
        }

        // An N000FF packet is manufactured by Platform Builder when we're
        // downloading multiple files or when we're downloading a .nb0 file.
        //N000FF包,由PB发出(当我们下载多文件或者下载.NB0文件)
 //N000FF表示要下载多个BIN
        if (!memcmp (hdr, "N000FF/x0A", BL_HDRSIG_SIZE))
        {
            // read the packet checksum.
            //读取包记号
            if (!OEMReadData (sizeof (DWORD), (LPBYTE) &dwRecChk))
            {
                EdbgOutputDebugString("/r/nUnable to read download manifest checksum./r/n");
                HALT (BLERR_MAGIC);
                return (FALSE);
            }

            // read BIN region descriptions (start address and length).
            //读取BIN种类
            if (!OEMReadData (sizeof (DWORD), (LPBYTE) &g_DownloadManifest.dwNumRegions) ||
                !OEMReadData ((g_DownloadManifest.dwNumRegions * sizeof(RegionInfo)), (LPBYTE) &g_DownloadManifest.Region

[0]))
            {
                EdbgOutputDebugString("/r/nUnable to read download manifest information./r/n");
                HALT (BLERR_MAGIC);
                return (FALSE);
            }

            // verify the packet checksum.
            //检验包的记号
            if (!VerifyChecksum((g_DownloadManifest.dwNumRegions * sizeof(RegionInfo)), (LPBYTE) &g_DownloadManifest.Region

[0], dwRecChk))
            {
                EdbgOutputDebugString ("/r/nDownload manifest packet failed checksum verification./r/n");
                HALT (BLERR_CHECKSUM);
                return (FALSE);
            }

            // Provide the download manifest to the OEM.  This gives the OEM the
            // opportunity to provide start addresses for the .nb0 files (which
            // don't contain placement information like .bin files do).
            //
            if (g_pOEMMultiBINNotify)
            {
                g_pOEMMultiBINNotify((PDownloadManifest)&g_DownloadManifest);
            }

            // look for next download...
            nNumDownloadFiles = (BYTE)(g_DownloadManifest.dwNumRegions + 1);      // +1 to account for this packet.
            continue;
        }
        // Is this an old X000FF multi-bin packet header?  It's no longer supported.
        //不支持X000FF的multi-bin包
        else if (!memcmp (hdr, "X000FF/x0A", BL_HDRSIG_SIZE))
        {
            EdbgOutputDebugString ("ERROR: The X000FF packet is an old-style multi-bin download manifest and it's no longer

supported. /
                                   /r/nPlease update your Platform Builder installation in you want to download multiple

files./r/n");
            HALT (BLERR_MAGIC);
            return (FALSE);
        }
        // Is this a standard bin image?  Check for the usual bin file signature.
        //标准的BIN镜像,检查正常的bin文件名
        else if (!memcmp (hdr, "B000FF/x0A", BL_HDRSIG_SIZE))
        {
            g_bBINDownload = TRUE;

            if (!OEMReadData (sizeof (DWORD), (LPBYTE) &dwImageStart)
                || !OEMReadData (sizeof (DWORD), (LPBYTE) &dwImageLength))
            {
                EdbgOutputDebugString ("Unable to read image start/length/r/n");
                HALT (BLERR_MAGIC);
                return (FALSE);
            }
        }
        // If the header signature isn't recognized, we'll assume the
        // download file is a raw .nb0 file.
        //如果头文件头无法识别,就假设该下载的文件为生疏的.NB0文件
        else
        {
            g_bBINDownload = FALSE;
        }

        // If Platform Builder didn't provide a download manifest (i.e., we're
        // only downloading a single .bin file), manufacture a manifest so we
        // can notify the OEM.
        //
        if (!g_DownloadManifest.dwNumRegions)
        {
            g_DownloadManifest.dwNumRegions             = 1;
            g_DownloadManifest.Region[0].dwRegionStart  = dwImageStart;
            g_DownloadManifest.Region[0].dwRegionLength = dwImageLength;

            // Provide the download manifest to the OEM.
            //提供下载清单给OEM
            if (g_pOEMMultiBINNotify)
            {
                g_pOEMMultiBINNotify((PDownloadManifest)&g_DownloadManifest);
            }
        }

        // Locate the current download manifest entry (current download file).
        //定位到当前下载清单的入口
        pCurDownloadFile = &g_DownloadManifest.Region[g_DownloadManifest.dwNumRegions - nNumDownloadFiles];

        // give the OEM a chance to verify memory
 //校验内存
        if (g_pOEMVerifyMemory && !g_pOEMVerifyMemory (pCurDownloadFile->dwRegionStart, pCurDownloadFile->dwRegionLength))
        {
            EdbgOutputDebugString ("!OEMVERIFYMEMORY: Invalid image/r/n");
            HALT (BLERR_OEMVERIFY);
            return (FALSE);
        }

        // check for flash image. Start erasing if it is.
 //检查是否为flash镜像,如果是则开始擦除
        if ((fIsFlash = OEMIsFlashAddr (pCurDownloadFile->dwRegionStart))
            && !OEMStartEraseFlash (pCurDownloadFile->dwRegionStart, pCurDownloadFile->dwRegionLength))
        {
            EdbgOutputDebugString ("Invalid Flash Address/Length/r/n");
            HALT (BLERR_FLASHADDR);
            return (FALSE);
        }

        // if we're downloading a binary file, we've already downloaded part of the image when searching
        // for a file header.  copy what we've read so far to the destination buffer, then finish downloading.
 //下载一个二进制文件,在搜索文件头的时候我们已经下载了一部分。在完成下载的时候,复制到当前读到的内容到目的缓冲器
        if (!g_bBINDownload)
        {

            lpDest = OEMMapMemAddr (pCurDownloadFile->dwRegionStart, pCurDownloadFile->dwRegionStart);
            memcpy(lpDest, hdr, BL_HDRSIG_SIZE);

            // complete the file download...
            // read data block
            if (!OEMReadData ((pCurDownloadFile->dwRegionLength - BL_HDRSIG_SIZE), (lpDest + BL_HDRSIG_SIZE)))
            {
                EdbgOutputDebugString ("ERROR: failed when reading raw binary file./r/n");
                HALT (BLERR_CORRUPTED_DATA);
                return (FALSE);
            }
        }
        // we're downloading a .bin file - download each .bin record in turn...
        else
        {
            while (OEMReadData (sizeof (DWORD), (LPBYTE) &dwRecAddr) &&
                   OEMReadData (sizeof (DWORD), (LPBYTE) &dwRecLen)  &&
                   OEMReadData (sizeof (DWORD), (LPBYTE) &dwRecChk))
            {
                // last record of .bin file uses sentinel values for address and checksum.
                if (!dwRecAddr && !dwRecChk)
                {
                    break;
                }

                // map the record address (FLASH data is cached, for example)
                lpDest = OEMMapMemAddr (pCurDownloadFile->dwRegionStart, dwRecAddr);

                // read data block
                if (!OEMReadData (dwRecLen, lpDest))
                {
                    EdbgOutputDebugString ("****** Data record %d corrupted, ABORT!!! ******/r/n", nPkgNum);
                    HALT (BLERR_CORRUPTED_DATA);
                    return (FALSE);
                }

                if (!VerifyChecksum (dwRecLen, lpDest, dwRecChk))
                {
                    EdbgOutputDebugString ("****** Checksum failure on record %d, ABORT!!! ******/r/n", nPkgNum);
                    HALT (BLERR_CHECKSUM);
                    return (FALSE);
                }

                // Look for ROMHDR to compute ROM offset.  NOTE: romimage guarantees that the record containing
                // the TOC signature and pointer will always come before the record that contains the ROMHDR contents.
                //
                if (dwRecLen == sizeof(ROMHDR) && (*(LPDWORD) OEMMapMemAddr(pCurDownloadFile->dwRegionStart,

pCurDownloadFile->dwRegionStart + ROM_SIGNATURE_OFFSET) == ROM_SIGNATURE))
                {
                    DWORD dwTempOffset = (dwRecAddr - *(LPDWORD)OEMMapMemAddr(pCurDownloadFile->dwRegionStart,

pCurDownloadFile->dwRegionStart + ROM_SIGNATURE_OFFSET + sizeof(ULONG)));
                    ROMHDR *pROMHdr = (ROMHDR *)lpDest;

                    // Check to make sure this record really contains the ROMHDR.
                    //
                    if ((pROMHdr->physfirst == (pCurDownloadFile->dwRegionStart - dwTempOffset)) &&
                        (pROMHdr->physlast  == (pCurDownloadFile->dwRegionStart - dwTempOffset + pCurDownloadFile-

>dwRegionLength)) &&
                        (DWORD)(HIWORD(pROMHdr->dllfirst << 16) <= pROMHdr->dlllast) &&
                        (DWORD)(LOWORD(pROMHdr->dllfirst << 16) <= pROMHdr->dlllast))
                    {
                        g_dwROMOffset = dwTempOffset;
                        EdbgOutputDebugString("rom_offset=0x%x./r/n", g_dwROMOffset);
                    }
                }

                // verify partial checksum
                OEMShowProgress (nPkgNum ++);

                if (fIsFlash)
                {
                    OEMContinueEraseFlash ();
                }
            }
        }

        // The image start address and length are passed back to the OEM code (OEMLaunch)
        // in the following circumstances:
        // 1. The file is a raw .nb0 file.
        // 2. The file is a .bin file with a TOC that contains the kernel executable.
        // 3. The file is a .bin file without a TOC.
        //
        // If the image is a .bin file with a TOC that doesn't contain the kernel exectuable,
        // then it's a multi-xip/mulit-bin image for a non-kernel region and we don't pass
        // the start address and length back to the OEM code.  OEMLaunch can then save the
        // start address and length with the assurance that if the values are non-zero, they
        // represent the values for the NK region.
        //
        if (g_bBINDownload)
        {
            // Does this .bin file contain a TOC?
            if (*(UINT32 *)(pCurDownloadFile->dwRegionStart + ROM_SIGNATURE_OFFSET) == ROM_SIGNATURE)
            {
                // Contain the kernel?
                if (IsKernelRegion(pCurDownloadFile->dwRegionStart, pCurDownloadFile->dwRegionLength))
                {
                    *pdwImageStart  = pCurDownloadFile->dwRegionStart;
                    *pdwImageLength = pCurDownloadFile->dwRegionLength;
                    *pdwLaunchAddr  = dwRecLen;
                }
            }
            // No TOC - not made by romimage.  However, if we're downloading more than one
            // .bin file, it's probably chain.bin which doesn't have a TOC (and which isn't
            // going to be downloaded on its own) and we should ignore it.
            //
            else if (g_DownloadManifest.dwNumRegions == 1)
                                       
            {
                *pdwImageStart  = pCurDownloadFile->dwRegionStart;
                *pdwImageLength = pCurDownloadFile->dwRegionLength;
                *pdwLaunchAddr  = dwRecLen;
            }
        }
        else    // Raw binary file.
        {
            *pdwImageStart  = pCurDownloadFile->dwRegionStart;
            *pdwLaunchAddr  = pCurDownloadFile->dwRegionStart;
            *pdwImageLength = pCurDownloadFile->dwRegionLength;
        }

        // write to flash if it's flash image
        if (fIsFlash)
        {
            // finish the flash erase
            if (!OEMFinishEraseFlash ())
            {
                HALT (BLERR_FLASH_ERASE);
                return (FALSE);
            }
            // Before writing the image to flash, optionally check the image signature.
            if (g_bBINDownload && g_pOEMCheckSignature)
            {
                if (!g_pOEMCheckSignature(pCurDownloadFile->dwRegionStart, g_dwROMOffset, *pdwLaunchAddr, TRUE))
                    HALT(BLERR_CAT_SIGNATURE);
            }
        }
    }
    while (--nNumDownloadFiles);


    if (fIsFlash)
    {
        nNumDownloadFiles = (BYTE)g_DownloadManifest.dwNumRegions;
        while (nNumDownloadFiles--)
        {
            if (!OEMWriteFlash (g_DownloadManifest.Region[nNumDownloadFiles].dwRegionStart, g_DownloadManifest.Region

[nNumDownloadFiles].dwRegionLength))
            {
                HALT (BLERR_FLASH_WRITE);
                return (FALSE);
            }
        }
    }

    return (TRUE);
}


/*OEMLaunch()函数 main.c文件
    @func   void | OEMLaunch | Executes the stored/downloaded image.
    @rdesc  N/A.
    @comm   
    @xref  
*/

void OEMLaunch( DWORD dwImageStart, DWORD dwImageLength, DWORD dwLaunchAddr, const ROMHDR *pRomHdr )
{
    DWORD dwPhysLaunchAddr;
    EDBG_OS_CONFIG_DATA *pCfgData;   
    EDBG_ADDR EshellHostAddr;
    EDBG_ADDR DeviceAddr;

    //是否把镜像写入NANDFlash
    // If the user requested that a disk image (stored in RAM now) be written to the SmartMedia card, so it now.
    //
    if (g_BootConfig.ConfigFlags & CONFIG_FLAGS_SAVETOFLASH)
    {
        // Since this platform only supports RAM images, the image cache address is the same as the image RAM address.
        //
        if (!WriteDiskImageToSmartMedia(dwImageStart, dwImageLength, &g_BootConfig))
        {
            OALMSG(OAL_ERROR, (TEXT("ERROR: OEMLaunch: Failed to store image to Smart Media./r/n")));
            goto CleanUp;
        }

        // Store the bootloader settings to flash.
        //
        // TODO: minimize flash writes.
        //
        if (!WriteBootConfig(&g_BootConfig))
        {
            OALMSG(OAL_ERROR, (TEXT("ERROR: OEMLaunch: Failed to store bootloader settings to flash./r/n")));
            goto CleanUp;
        }

        OALMSG(TRUE, (TEXT("INFO: Disk image stored to Smart Media.  Please Reboot.  Halting.../r/n")));
        while(1)
        {
            // Wait...
        }
    }

    // Wait for Platform Builder to connect after the download and send us IP and port settings for service
    // connections - also sends us KITL flags.  This information is used later by the OS (KITL).
    //调用EbootWaitForHostConnect得到PB设置
    if (g_bWaitForConnect)
    {
        memset(&EshellHostAddr, 0, sizeof(EDBG_ADDR));

        DeviceAddr.dwIP  = pBSPArgs->kitl.ipAddress;
        memcpy(DeviceAddr.wMAC, pBSPArgs->kitl.mac, (3 * sizeof(UINT16)));
        DeviceAddr.wPort = 0;

        if (!(pCfgData = EbootWaitForHostConnect(&DeviceAddr, &EshellHostAddr)))
        {
            OALMSG(OAL_ERROR, (TEXT("ERROR: OEMLaunch: EbootWaitForHostConnect failed./r/n")));
            goto CleanUp;
        }

        // If the user selected "passive" KITL (i.e., don't connect to the target at boot time), set the
        // flag in the args structure so the OS image can honor it when it boots.
        //
        if (pCfgData->KitlTransport & KTS_PASSIVE_MODE)
        {
            pBSPArgs->kitl.flags |= OAL_KITL_FLAGS_PASSIVE;
        }
    }

    // If a launch address was provided, we must have downloaded the image, save the address in case we
    // want to jump to this image next time.  If no launch address was provided, retrieve the last one.
    //
    if (dwLaunchAddr)
    {
        g_BootConfig.LaunchAddress = dwLaunchAddr;
    }
    else
    {
        dwLaunchAddr = g_BootConfig.LaunchAddress;
    }

    // Save bootloader settings in flash.
    //把配置信息写好Flash
    // TODO: minimize flash writes.
    //
    if (!WriteBootConfig(&g_BootConfig))
    {
        OALMSG(OAL_ERROR, (TEXT("ERROR: OEMLaunch: Failed to store bootloader settings in flash./r/n")));
        goto CleanUp;
    }

    // Jump to downloaded image (use the physical address since we'll be turning the MMU off)...
    //关闭MMU
    dwPhysLaunchAddr = (DWORD)OALVAtoPA((void *)dwLaunchAddr);
    OALMSG(TRUE, (TEXT("INFO: OEMLaunch: Jumping to Physical Address 0x%Xh (Virtual Address 0x%Xh).../r/n/r/n/r/n"),

dwPhysLaunchAddr, dwLaunchAddr));
 
    // Jump...
    //
  //跳转到物理地址
    Launch(dwPhysLaunchAddr);


CleanUp:

    OALMSG(TRUE, (TEXT("ERROR: OEMLaunch: Halting.../r/n")));
    SpinForever();
}

//Launch()位于util.s文件中
 LEAF_ENTRY Launch

 ldr r2, = PhysicalStart
 ldr     r3, = (VIR_RAM_START - PHY_RAM_START)

 sub     r2, r2, r3

 mov     r1, #0x0070             ; Disable MMU 禁止MMU
 mcr     p15, 0, r1, c1, c0, 0
 nop
 mov     pc, r2                  ; Jump to PStart 跳转  因为跳转地址是物理地址,所以在跳转前必须关闭MMU
 nop

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值