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

 

本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/ymzhou117/archive/2010/04/16/5492313.aspx

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章