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