Bootloader 的主控函数bootloadermain函数分析

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

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

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

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

        实际调用的是目录 下的 /WINCE500/PLATFORM/COMMON/SRC/COMMON/BOOT/BLCOMMON
  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,c ptr->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_Downloa dManifest);

  }

  // 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_Downloa dManifest);

  }

  }

  // Locate the current download manifest entry (current download file).

  //定位到当前下载清单的入口

  pCurDownloadFile = &g_DownloadManifest.Region[g_DownloadManifest.dwNu mRegions - 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->dwRegionS tart,

  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->dwRegionSt art, 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].dwRe gionStart, 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 = http://blog.soso.com/qz.q/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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值