九鼎创展论坛中文版English
登录 | 立即注册 设为首页收藏本站

S3C2440 eboot详细流程分析[本站原创]

查看数: 11844 | 评论数: 2 | 收藏 0
关灯 | 提示:支持键盘翻页<-左 右->
    组图打开中,请稍候......
发布时间: 2012-7-19 11:31

正文摘要:

模块三星官方给出的Bootloader就包括这两个部分:NBoot和EBoot。针对S3C2440处理器,由于内部只有4K的SRAM,当硬件配置成Nand启动时,固化在处理器内部的IROM程序会自动将Nand的Block0的前4K字节的程序拷备到SRAM中 ...

回复

armeasy 发表于 2012-7-19 11:35:26
这个函数首先初始化LCD显示,初始化USB,中断,初始化flash,然后从nand中读取TOC,读取成功后再进入5秒倒计时,随后根据用户操作或是缺省配置进入交互界面或是引导系统。
值得注意的是,TOC这个参数相当的重要。从字面上讲,TOC即table of context,它里面存放了Eboot和NK的版本号,标识号,字符串标识号,映像类型,所占用的页长度,引导地址,跳转地址,起始页地址等信息,给个打印信息就一清二楚了:
TOC {
dwSignature:0x434F544E
BootCfg {
  ConfigFlags: 0x830
  BootDelay: 0x5
  ImageIndex: 1
  IP: 192.168.1.115
  MAC Address: 00:11:22:33:44:55
  Port: 0.0.0.0
  SubnetMask: 255.255.255.0
}
ID[0] {
  dwVersion: 0x20004
  dwSignature: 0x45424F54
  String: 'eboot.nb0'
  dwImageType: 0x2
  dwTtlSectors: 0x200
  dwLoadAddress: 0x80038000
  dwJumpAddress: 0x80038000
  dwStoreOffset: 0x0
  sgList[0].dwSector: 0xA0
  sgList[0].dwLength: 0x200
}
ID[1] {
  dwVersion: 0x1
  dwSignature: 0x43465348
  String: ''
  dwImageType: 0x2
  dwTtlSectors: 0x1119D
  dwLoadAddress: 0x80200000
  dwJumpAddress: 0x8022C4C8
  dwStoreOffset: 0x0
  sgList[0].dwSector: 0x360
  sgList[0].dwLength: 0x1119D
}
chainInfo.dwLoadAddress:0X00000000
chainInfo.dwFlashAddress:0X00000000
chainInfo.dwLength:0X00000000
}
如果TOC出错,则无法正常引导系统。从上面的程序可以看出,在switch语句里面,如果检测到0x20,即空格键,MainMenu()函数会被调用,这个函数会打印主交互界面的信息,具体执行什么任务,需要用户指派。打印信息如下:
Ethernet BootLoader Configuration:

0) IP address:192.168.1.115
1) Subnet mask:255.255.255.0
2) DHCP: Disabled
3) Boot delay: 5seconds
4) Reset tofactory default configuration
5) Startup image:LAUNCH EXISTING
6) Program diskimage into SmartMedia card: Enabled
7) Program CS8900MAC address (00:11:22:33:44:55)
8) KernelDebugger: DISABLED
9) Format BootMedia for BinFS
F) Low-levelformat the Smart Media card
D) Download imagenow
L) LAUNCH existingBoot Media image
U) DOWNLOAD imagenow(USB)
R) ReadConfiguration
W) WriteConfiguration Right Now

Enter yourselection:
3):配置启动延时时间;
4):复位TOC里面信息到出厂设置;
5):运行系统和下载系统选择;
6):下载系统到nand和SDRAM选择;
9):格式化nand为BinFS格式;
F):低格nand flash;
L):运行nand中存在的系统;
U):通过USB下载映像;
R):读取TOC信息;
W):保存配置的参数信息。
这里我们比较关心一块新的flash,我们需要对他的操作。在Uboot中,我们保留了Uboot,TOC,Eboot,MBR,BootParam,pic,NK七段,其中在烧写Uboot,Eboot,pic以及BootParam时,已经对相应段擦写过一遍了,而TOC段,MBR段,NK段还没有任何操作。这些都会在这里下载NK时进行。下面我们逐步分析。
在上面打印信息的配置下,按U,将会通过USB下载NK到nand。查看代码,当按U后,程序给bConfigChanged ,g_bUSBDownload,bDownload赋1,然后执行TOC_Write()函数,进入这个函数可以发现,先擦除TOC所存放的块,这里存放TOC的位置要严格和Uboot里面预定义的相同,我们把它放在block4。之后将TOC信息写入块。g_bDownloadImage通过获取MainMenu()的返回值,即bDownload的值为1,这时OEMPlatformInit()函数已经运行完毕,再回过来看BootloaderMain()函数,该函数在执行完OEMPlatformInit()函数后,通过调用OEMPreDownload()函数决定下载映像或是引导映像。我们看看OEMPreDownload()函数:
DWORDOEMPreDownload(void)
{
    BOOL bGotJump = FALSE;
    DWORDdwDHCPLeaseTime = 0;
    PDWORD pdwDHCPLeaseTime =&dwDHCPLeaseTime;
    DWORD dwBootFlags = 0;

    OALMSG(OAL_FUNC,(TEXT("+OEMPreDownload.\r\n")));

    // Create device name based on Ethernetaddress (this is how Platform Builder identifies this device).
    //
    OALKitlCreateName(BSP_DEVICE_PREFIX,pBSPArgs->kitl.mac, pBSPArgs->deviceId);
    OALMSG(OAL_INFO, (L"INFO: *** DeviceName '%hs' ***\r\n", pBSPArgs->deviceId));

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

            if ( !g_bDownloadImage)
            {
                     return(BL_JUMP);
            }

            // Initialize the the TFTPtransport.
            //
            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 Ethernetconnection.\r\n")));
                     return(BL_ERROR);
            }


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

            OALMSG(OAL_FUNC,(TEXT("_OEMPreDownload.\r\n")));
   }
   else
   {
            OALMSG(TRUE, (TEXT("Please sendthe Image through USB.\r\n")));
   }

    return(bGotJump ? BL_JUMP : BL_DOWNLOAD);
}
由于之前在OEMPlatformInit()函数中,g_bUSBDownload的值已经被赋为1,所以上面程序执行到最后,bGotJump=0,也就是return BL_DOWNLOAD。那么在BootloaderMain()函数中,将会调用DownloadImage()函数下载映像。
在DownloadImage()函数中,首先从USB获得的BIN文件中读取BIN文件的前7个字节的数据,对于标准的BIN文件,前7个字节的数据一定是“B000FF\x0A”,接下来再读BIN文件的起始地址和长度,这两个数据很重要,读出来的数据会被复制到结构体g_DownloadManifest中,然后就是将BIN文件剩下来的一条条记录读到DRAM中。整个DownloadImage()函数需要对NK.bin或XIP.bin的结构相当了解,程序完全按照其结构将里面的数据一条条的解析出来。一个bin 文件在存储上是按下面的结构存储的:
     组成:标记(7)+Image开始地址(1)+Image长度(1)  
           记录0地址+记录0长+记录0校验和+记录0内容(文件内容)
           记录1地址+记录1长+记录1校验和+记录1内容(文件内容)
           ......
     最后一条记录是表示结束。
     bin 文件的头部(不包括记录)可以用下面的结构表示
  structBinFile
{
                 BYTE signature[7]; // = { ''B'', ''0'', ''0'', ''0'', ''F'', ''F'',''\a'' }
                 DWORD ImageStart
                 DWORD ImageLength
          };
有了上面的具体结构分析,DownloadImage()函数就不难理解了。这个函数执行完之后,我们再回到BootloaderMain()函数中,我们细心一点,会发现原来那个switch语句中,BL_DOWNLOAD条件的case语句执行完之后,并没有break,也就是说执行完DownloadImage()函数之后,并没有退出,而是继续执行BL_JUMP条件的case语句。该语句调用了OEMLaunch()函数,函数传入了上面读出来的映像起始地址,映像长度以及运行地址。
真正将nk.bin或XIP.bin数据写到nand中,并不是在DownloadImage()中,而是在OEMLaunch()中。其代码如下:
void OEMLaunch( DWORD dwImageStart, DWORDdwImageLength, DWORD dwLaunchAddr, const ROMHDR *pRomHdr )
{
   DWORD dwPhysLaunchAddr;
   EDBG_ADDR EshellHostAddr;
   EDBG_OS_CONFIG_DATA *pCfgData;   

   OALMSG(OAL_FUNC, (TEXT("+OEMLaunch.\r\n")));

    //If the user requested that a disk image (stored in RAM now) be written to theSmartMedia card, so it now.
    //
    if(g_bDownloadImage && (g_pBootCfg->ConfigFlags &TARGET_TYPE_NAND))
    {
       // Since this platform only supports RAM images, the image cache addressis the same as the image RAM address.
       //

       switch (g_ImageType)
       {
           case IMAGE_TYPE_STEPLDR:
                         if (!WriteRawImageToBootMedia(dwImageStart,dwImageLength, dwLaunchAddr))
                         {
                           OALMSG(OAL_ERROR,(TEXT("ERROR: OEMLaunch: Failed to store image to SmartMedia.\r\n")));
                           goto CleanUp;
                    }
                         OALMSG(TRUE, (TEXT("INFO: Step loaderimage stored to Smart Media.  PleaseReboot.  Halting...\r\n")));
                     while(1)
                     {
                           // Wait...
                     }
                    break;

           case IMAGE_TYPE_LOADER:
                                    g_pTOC->id[0].dwLoadAddress= dwImageStart;
                                    g_pTOC->id[0].dwTtlSectors= FILE_TO_SECTOR_SIZE(dwImageLength);
                         if(!WriteRawImageToBootMedia(dwImageStart, dwImageLength, dwLaunchAddr))
                         {
                           OALMSG(OAL_ERROR,(TEXT("ERROR: OEMLaunch: Failed to store image to Smart Media.\r\n")));
                           goto CleanUp;
                    }
                                    if(dwLaunchAddr && (g_pTOC->id[0].dwJumpAddress != dwLaunchAddr))
                                    {
                                             g_pTOC->id[0].dwJumpAddress= dwLaunchAddr;
                                             if( !TOC_Write() ) {
                                     EdbgOutputDebugString("***OEMLaunch ERROR: TOC_Write failed! Next boot may not load from disk ***\r\n");
                                             }
                               TOC_Print();
                                    }
                         OALMSG(TRUE, (TEXT("INFO: Ebootimage stored to Smart Media.  PleaseReboot.  Halting...\r\n")));
                         while(1)
                         {
                           // Wait...
                    }                     

                break;

           case IMAGE_TYPE_RAMIMAGE:
                                    g_pTOC->id[g_dwTocEntry].dwLoadAddress= dwImageStart;
                                    g_pTOC->id[g_dwTocEntry].dwTtlSectors= FILE_TO_SECTOR_SIZE(dwImageLength);
                         if(!WriteOSImageToBootMedia(dwImageStart, dwImageLength, dwLaunchAddr))
                         {
                           OALMSG(OAL_ERROR,(TEXT("ERROR: OEMLaunch: Failed to store image to SmartMedia.\r\n")));
                           goto CleanUp;
                    }

                                    if(dwLaunchAddr && (g_pTOC->id[g_dwTocEntry].dwJumpAddress !=dwLaunchAddr))
                                    {
                                             g_pTOC->id[g_dwTocEntry].dwJumpAddress= dwLaunchAddr;
                                             if( !TOC_Write() ) {
                                     EdbgOutputDebugString("***OEMLaunch ERROR: TOC_Write failed! Next boot may not load from disk ***\r\n");
                                             }
                               TOC_Print();
                                    }
                                    else
                                    {
                                             dwLaunchAddr=g_pTOC->id[g_dwTocEntry].dwJumpAddress;
                                             EdbgOutputDebugString("INFO:using TOC[%d] dwJumpAddress: 0x%x\r\n", g_dwTocEntry, dwLaunchAddr);
                                    }

                break;
       }
    }
   else if(g_bDownloadImage)
    {
       switch (g_ImageType)
       {
           case IMAGE_TYPE_STEPLDR:
                         OALMSG(TRUE, (TEXT("Stepldr imagecan't launch from ram.\r\n")));
                         OALMSG(TRUE, (TEXT("You shouldprogram it into flash.\r\n")));
                         SpinForever();
                                    break;
           case IMAGE_TYPE_LOADER:
                         OALMSG(TRUE, (TEXT("Eboot imagecan't launch from ram.\r\n")));
                         OALMSG(TRUE, (TEXT("You shouldprogram it into flash.\r\n")));
                         SpinForever();
                         break;
           default:
                  break;
     }
    }

   OALMSG(1, (TEXT("waitforconnect\r\n")));
    //Wait for Platform Builder to connect after the download and send us IP and portsettings for service
    //connections - also sends us KITL flags. This information is used later by the OS (KITL).
    //
    if(~g_bUSBDownload & g_bDownloadImage & g_bWaitForConnect)
    {
       memset(&EshellHostAddr, 0, sizeof(EDBG_ADDR));

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

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

       // If the user selected "passive" KITL (i.e., don't connect tothe target at boot time), set the
       // flag in the args structure so the OS image can honor it when itboots.
       //
       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 theaddress in case we
    //want to jump to this image next time.  Ifno launch address was provided, retrieve the last one.
    //
        if(dwLaunchAddr && (g_pTOC->id[g_dwTocEntry].dwJumpAddress !=dwLaunchAddr))
        {
                 g_pTOC->id[g_dwTocEntry].dwJumpAddress= dwLaunchAddr;
        }
        else
        {
                 dwLaunchAddr=g_pTOC->id[g_dwTocEntry].dwJumpAddress;
                 OALMSG(OAL_INFO,(TEXT("INFO: using TOC[%d] dwJumpAddress: 0x%x\r\n"), g_dwTocEntry,dwLaunchAddr));
        }

    //Jump to downloaded image (use the physical address since we'll be turning theMMU off)...
    //
   dwPhysLaunchAddr = (DWORD)OALVAtoPA((void *)dwLaunchAddr);
   OALMSG(TRUE, (TEXT("INFO: OEMLaunch: Jumping to Physical Address0x%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();
}

在这个函数中,我们又看到了熟悉的变量g_bDownloadImage。程序通过这个变量判断是运行nand中存在的映像还是写映像到nand。当然这里是下载映像到nand了。函数中又出现了一个switch语句,里面有三个case变量,IMAGE_TYPE_STEPLDR,IMAGE_TYPE_LOADER以及IMAGE_TYPE_RAMIMAGE,分别对应steploader,eboot以及nk。前面两个我们通过uboot烧到nand了,当然也可以通过这里烧写。因此我们只关心IMAGE_TYPE_RAMIMAGE。该case语句里面,调用了WriteOSImageToBootMedia()函数,这个函数就是真正实现将系统映像文件写到nandflash的源函数!在将数据写到nand之前,该函数会进行TOC,MBR校验,分区。其代码如下:

BOOL WriteOSImageToBootMedia(DWORDdwImageStart, DWORD dwImageLength, DWORD dwLaunchAddr)
{
   BYTE nCount;
   DWORD dwNumExts;
   PXIPCHAIN_SUMMARY pChainInfo = NULL;
   EXTENSION *pExt = NULL;
   DWORD dwBINFSPartLength = 0;
   HANDLE hPart, hPartEx;
   DWORD dwStoreOffset;
   DWORD dwMaxRegionLength[BL_MAX_BIN_REGIONS] = {0};
   DWORD dwChainStart, dwChainLength;

   //  Initialize the variables
   dwChainStart = dwChainLength = 0;

   OALMSG(OAL_FUNC, (TEXT("+WriteOSImageToBootMedia\r\n")));
   OALMSG(OAL_INFO, (TEXT("+WriteOSImageToBootMedia: g_dwTocEntry =%d,ImageStart: 0x%x, ImageLength: 0x%x, LaunchAddr:0x%x\r\n"),
                            g_dwTocEntry,dwImageStart, dwImageLength, dwLaunchAddr));

    if( !g_bBootMediaExist )
    {
       OALMSG(OAL_ERROR, (TEXT("ERROR: WriteOSImageToBootMedia: devicedoesn't exist.\r\n")));
       return(FALSE);
    }

    if( !VALID_TOC(g_pTOC) )
    {
       OALMSG(OAL_WARN, (TEXT("WARN: WriteOSImageToBootMedia:INVALID_TOC\r\n")));
       if ( !TOC_Init(g_dwTocEntry, g_ImageType, dwImageStart, dwImageLength,dwLaunchAddr) )
       {
           OALMSG(OAL_ERROR, (TEXT("ERROR: INVALID_TOC\r\n")));
           return(FALSE);
       }
    }

    //Look in the kernel region's extension area for a multi-BIN extensiondescriptor.
    //This region, if found, details the number, start, and size of each BIN region.
    //
    for(nCount = 0, dwNumExts = 0 ; (nCount < g_BINRegionInfo.dwNumRegions);nCount++)
    {
       // Does this region contain nk.exe and an extension pointer?
       //
       pExt = (EXTENSION*)GetKernelExtPointer(g_BINRegionInfo.Region[nCount].dwRegionStart,
                                               g_BINRegionInfo.Region[nCount].dwRegionLength );
       if ( pExt != NULL)
       {
           // If there is an extension pointer region, walk it until the end.
           //
           while (pExt)
           {
                DWORD dwBaseAddr =g_BINRegionInfo.Region[nCount].dwRegionStart;
                pExt = (EXTENSION*)OEMMapMemAddr(dwBaseAddr, (DWORD)pExt);
                OALMSG(OAL_INFO,(TEXT("INFO: OEMLaunch: Found chain extenstion: '%s' @ 0x%x\r\n"),pExt->name, dwBaseAddr));
                if ((pExt->type == 0)&& !strcmp(pExt->name, "chain information"))
                {
                    pChainInfo =(PXIPCHAIN_SUMMARY) OEMMapMemAddr(dwBaseAddr, (DWORD)pExt->pdata);
                    dwNumExts =(pExt->length / sizeof(XIPCHAIN_SUMMARY));
                    OALMSG(OAL_INFO,(TEXT("INFO: OEMLaunch: Found 'chain information' (pChainInfo=0x%x  Extensions=0x%x).\r\n"),(DWORD)pChainInfo, dwNumExts));
                    break;
                }
                pExt = (EXTENSION*)pExt->pNextExt;
           }
       }
       else {
           //  Search for Chain region. Chainregion doesn't have the ROMSIGNATURE set
           DWORD   dwRegionStart =g_BINRegionInfo.Region[nCount].dwRegionStart;
           DWORD   dwSig = *(LPDWORD)OEMMapMemAddr(dwRegionStart, dwRegionStart + ROM_SIGNATURE_OFFSET);

           if ( dwSig != ROM_SIGNATURE) {
                // It is the chain
                dwChainStart = dwRegionStart;
                dwChainLength =g_BINRegionInfo.Region[nCount].dwRegionLength;
                OALMSG(TRUE, (TEXT("Foundthe Chain region: StartAddress: 0x%X; Length: 0x%X\n"), dwChainStart,dwChainLength));
           }
       }
    }

    //Determine how big the Total BINFS partition needs to be to store all of this.
    //
    if(pChainInfo && dwNumExts == g_BINRegionInfo.dwNumRegions)    // We're downloading all the regions in amulti-region image...
    {
       DWORD i;
       OALMSG(TRUE, (TEXT("Writing multi-regions\r\n")));

       for (nCount = 0, dwBINFSPartLength = 0 ; nCount < dwNumExts ;nCount++)
       {
           dwBINFSPartLength += (pChainInfo + nCount)->dwMaxLength;
           OALMSG(OAL_ERROR, (TEXT("BINFSPartMaxLength[%u]: 0x%x,TtlBINFSPartLength: 0x%x \r\n"),
                nCount, (pChainInfo +nCount)->dwMaxLength, dwBINFSPartLength));

           // MultiBINInfo does not store each Regions MAX length, and pChainInfois not in any particular order.
           // So, walk our MultiBINInfo matching up pChainInfo to find each regionsMAX Length
           for (i = 0; i < dwNumExts; i++) {
                if (g_BINRegionInfo.Region.dwRegionStart == (DWORD)((pChainInfo +nCount)->pvAddr) ) {
                    dwMaxRegionLength =(pChainInfo + nCount)->dwMaxLength;
                    OALMSG(TRUE,(TEXT("dwMaxRegionLength[%u]: 0x%x \r\n"), i, dwMaxRegionLength));
                    break;
                }
           }
       }
    }
   else    // A single BIN file orpotentially a multi-region update (but the partition's already been created inthis latter case).
    {
       dwBINFSPartLength = g_BINRegionInfo.Region[0].dwRegionLength;
       OALMSG(TRUE, (TEXT("Writing single region/multi-region update,dwBINFSPartLength: %u \r\n"), dwBINFSPartLength));
    }
    //Open/Create the BINFS partition where images are stored.  This partition starts immediately after theMBR on the Boot Media and its length is
    //determined by the maximum image size (or sum of all maximum sizes in amulti-region design).
    //Parameters are LOGICAL sectors.
    //
   hPart = BP_OpenPartition( (IMAGE_START_BLOCK+1)*PAGES_PER_BLOCK, // next block of MBR
                             SECTOR_TO_BLOCK_SIZE(FILE_TO_SECTOR_SIZE(dwBINFSPartLength))*PAGES_PER_BLOCK,// align to block
                              PART_BINFS,
                              TRUE,
                              PART_OPEN_ALWAYS);
    if(hPart == INVALID_HANDLE_VALUE )
    {
       OALMSG(OAL_ERROR, (TEXT("ERROR: WriteOSImageToBootMedia: Failed toopen/create partition.\r\n")));
       return(FALSE);
    }
    //Are there multiple BIN files in RAM (we may just be updating one in a multi-BINsolution)?
    //
   for (nCount = 0, dwStoreOffset = 0; nCount <g_BINRegionInfo.dwNumRegions ; nCount++)
    {
       DWORD dwRegionStart  =(DWORD)OEMMapMemAddr(0, g_BINRegionInfo.Region[nCount].dwRegionStart);
       DWORD dwRegionLength = g_BINRegionInfo.Region[nCount].dwRegionLength;
       // Media byte offset where image region is stored.
       dwStoreOffset += nCount ? dwMaxRegionLength[nCount-1] : 0;
       // Set the file pointer (byte indexing) to the correct offset for thisparticular region.
       //
       if ( !BP_SetDataPointer(hPart, dwStoreOffset) )//设置分区数据指针?指向第0个分区???
       {
           OALMSG(OAL_ERROR, (TEXT("ERROR: StoreImageToBootMedia: Failed toset data pointer in partition (offset=0x%x).\r\n"), dwStoreOffset));
           return(FALSE);
       }
       // Write the region to the BINFS partition.
       //
       if ( !BP_WriteData(hPart, (LPBYTE)dwRegionStart, dwRegionLength) )
       {
           EdbgOutputDebugString("ERROR: StoreImageToBootMedia: Failed towrite region to BINFS partition (start=0x%x, length=0x%x).\r\n",dwRegionStart, dwRegionLength);
           return(FALSE);
       }
       // update our TOC?
       //
       if ((g_pTOC->id[g_dwTocEntry].dwLoadAddress ==g_BINRegionInfo.Region[nCount].dwRegionStart) &&
            g_pTOC->id[g_dwTocEntry].dwTtlSectors ==FILE_TO_SECTOR_SIZE(dwRegionLength) )
       {
           g_pTOC->id[g_dwTocEntry].dwStoreOffset = dwStoreOffset;
           g_pTOC->id[g_dwTocEntry].dwJumpAddress = 0; // Filled upon return toOEMLaunch
           g_pTOC->id[g_dwTocEntry].dwImageType = g_ImageType;
           g_pTOC->id[g_dwTocEntry].sgList[0].dwSector =FILE_TO_SECTOR_SIZE(g_dwLastWrittenLoc);
           g_pTOC->id[g_dwTocEntry].sgList[0].dwLength = g_pTOC->id[g_dwTocEntry].dwTtlSectors;
           // copy Kernel Region to SDRAM for jump
           memcpy((void*)g_pTOC->id[g_dwTocEntry].dwLoadAddress,(void*)dwRegionStart, dwRegionLength);
           OALMSG(TRUE, (TEXT("Updateded TOC!\r\n")));
       }
       else if( (dwChainStart == g_BINRegionInfo.Region[nCount].dwRegionStart)&&
                 (dwChainLength ==g_BINRegionInfo.Region[nCount].dwRegionLength))
       {
           //  Update our TOC for Chainregion
           g_pTOC->chainInfo.dwLoadAddress = dwChainStart;
           g_pTOC->chainInfo.dwFlashAddress =FILE_TO_SECTOR_SIZE(g_dwLastWrittenLoc);
           g_pTOC->chainInfo.dwLength =FILE_TO_SECTOR_SIZE(dwMaxRegionLength[nCount]);
           OALMSG(TRUE, (TEXT("Written Chain Region to the Flash\n")));
           OALMSG(TRUE, (TEXT("LoadAddress = 0x%X; FlashAddress = 0x%X; Length= 0x%X\n"),
                                 g_pTOC->chainInfo.dwLoadAddress,
                                 g_pTOC->chainInfo.dwFlashAddress,
                                 g_pTOC->chainInfo.dwLength));
           // Now copy it to the SDRAM
           memcpy((void *)g_pTOC->chainInfo.dwLoadAddress, (void*)dwRegionStart, dwRegionLength);
       }
    }
    //create extended partition in whatever is left
    //
   hPartEx = BP_OpenPartition( NEXT_FREE_LOC,
                               USE_REMAINING_SPACE,
                                PART_DOS32,
                                TRUE,
                               PART_OPEN_ALWAYS);
    if(hPartEx == INVALID_HANDLE_VALUE )
    {
       OALMSG(OAL_WARN, (TEXT("*** WARN: StoreImageToBootMedia: Failed toopen/create Extended partition ***\r\n")));
    }
   OALMSG(OAL_FUNC, (TEXT("-WriteOSImageToBootMedia\r\n")));
   return(TRUE);
}
可以看到,程序首先调用VALID_TOC()函数校验TOC数据,再调用BP_OpenPartition()函数进行分区。这个分区函数非常重要,其代码如下:
HANDLE BP_OpenPartition(DWORD dwStartSector,DWORD dwNumSectors, DWORD dwPartType, BOOL fActive, DWORD dwCreationFlags)
{
       DWORD dwPartIndex;
       BOOL fExists;
       ASSERT (g_pbMBRSector);
       if (!IsValidMBR()) // 检测MBR的存放位置,如果没有检测到,则低格flash,再创建分区
                 {
           DWORD dwFlags = 0;
           if (dwCreationFlags == PART_OPEN_EXISTING)
                           {
                RETAILMSG(1,(TEXT("OpenPartition: Invalid MBR. Cannot open existing partition 0x%x.\r\n"), dwPartType));
                return INVALID_HANDLE_VALUE;
           }
           RETAILMSG(1, (TEXT("OpenPartition: Invalid MBR.  Formatting flash.\r\n")));
           if (g_FlashInfo.flashType == NOR)
                           {
                dwFlags |=FORMAT_SKIP_BLOCK_CHECK;
           }
           //BP_LowLevelFormat (0, g_FlashInfo.dwNumBlocks, dwFlags);// 没有检测到MBR,则先低格flash.这里不能从block0开始!!!
           // lqm changed.10-04-13
           BP_LowLevelFormat (13, g_FlashInfo.dwNumBlocks-13, dwFlags);
           dwPartIndex = 0;
           fExists = FALSE;
       }
       else
                 {
           fExists = GetPartitionTableIndex(dwPartType, fActive,&dwPartIndex);        
       }
       RETAILMSG(1, (TEXT("OpenPartition: Partition Exists=0x%x for part0x%x.\r\n"), fExists, dwPartType));
       if (fExists)
                 {
           // Partition was found.  
           if (dwCreationFlags == PART_CREATE_NEW)
                return INVALID_HANDLE_VALUE;
           if (g_partStateTable[dwPartIndex].pPartEntry == NULL)
                           {
                // Open partition.  If this is the boot section partition, thenfile pointer starts after MBR
               g_partStateTable[dwPartIndex].pPartEntry = (PPARTENTRY)(g_pbMBRSector +PARTTABLE_OFFSET + sizeof(PARTENTRY)*dwPartIndex);
               g_partStateTable[dwPartIndex].dwDataPointer = 0;
           }            
           return (HANDLE)&g_partStateTable[dwPartIndex];            
       }
       else
                 {
           // If there are already 4 partitions, or creation flag specifiedOPEN_EXISTING, fail.
           if ((dwPartIndex == NUM_PARTS) || (dwCreationFlags ==PART_OPEN_EXISTING))
                return INVALID_HANDLE_VALUE;
           // Create new partition
           return CreatePartition (dwStartSector, dwNumSectors, dwPartType,fActive, dwPartIndex);// 没有检测到MBR,低格flash后,再创建分区
       }
       return INVALID_HANDLE_VALUE;
}
该函数有五个传入参数,其功能如下:
dwStartSector:分区的起始页
dwNumSectors:分区页数
dwPartType:分区格式
fActive:true -> 指示该分区为活动分区
dwCreationFlags:PART_OPEN_ALWAYS指示如果分区不存在就创建该分区,存在就OPEN该分区,返回分区句柄。
再看刚才WriteOSImageToBootMedia()函数中传进来的参数:
dwStartSector:(IMAGE_START_BLOCK+1)*PAGES_PER_BLOCK=(1+8+4+1)*32=448;
dwNumSectors: 由USB下载得到的映像大小计算出最终需要的页数;
dwPartType: 0x21,表示BinFS格式;
fActive: 1;
dwCreationFlags: PART_OPEN_ALWAYS。
有了入口参数,我们再详细分析函数所实现的功能。
程序第一句通过调用IsValidMBR()函数,判断MBR是否存在。该函数代码如下:
static BOOL IsValidMBR()
{
    //Check to see if the MBR is valid
    //MBR block is always located at logical sector 0
   g_dwMBRSectorNum = GetMBRSectorNum();        
   RETAILMSG (1, (TEXT("IsValidMBR: MBR sector = 0x%x\r\n"), g_dwMBRSectorNum));
    if((g_dwMBRSectorNum == INVALID_ADDR) || !FMD_ReadSector (g_dwMBRSectorNum,g_pbMBRSector, NULL, 1))
        {
       return FALSE;  
    }
        //lqm added for test.10-04-12
        RETAILMSG(1, (TEXT("g_pbMBRSector[0] = 0x%x\r\n"), g_pbMBRSector[0]));
        RETAILMSG(1, (TEXT("g_pbMBRSector[1] = 0x%x\r\n"), g_pbMBRSector[1]));
        RETAILMSG(1, (TEXT("g_pbMBRSector[2] = 0x%x\r\n"), g_pbMBRSector[2]));
        RETAILMSG(1, (TEXT("g_pbMBRSector[SECTOR_SIZE-2] = 0x%x\r\n"),g_pbMBRSector[SECTOR_SIZE-2]));
        RETAILMSG(1, (TEXT("g_pbMBRSector[SECTOR_SIZE-1] = 0x%x\r\n"),g_pbMBRSector[SECTOR_SIZE-1]));
        //end added.
   return ((g_pbMBRSector[0] == 0xE9) &&
        (g_pbMBRSector[1] == 0xfd) &&
        (g_pbMBRSector[2] == 0xff) &&
        (g_pbMBRSector[SECTOR_SIZE-2] == 0x55) &&
        (g_pbMBRSector[SECTOR_SIZE-1] == 0xAA));
}
该函数首先通过GetMBRSectorNum()函数获得MBR存放的段,其代码如下:
// 获得保存MBR的那个页
static DWORD GetMBRSectorNum ()
{
   //DWORD dwBlockNum = 0;       //K9F1G08
   DWORD dwBlockNum = 13;//K9F1208.lqm changed.must matched with uboot.
   while (dwBlockNum < g_FlashInfo.dwNumBlocks)
        {
       if (!IS_BLOCK_UNUSABLE (dwBlockNum)) //判断当前block是否可用
                 {
           return (dwBlockNum * g_FlashInfo.wSectorsPerBlock);
                 }
       dwBlockNum++;
    }
   return INVALID_ADDR;
}
这里dwBlockNum变量定义了MBR存放的block。结合UBoot分区表的设置,MBR被保存到了block13。因此,这里直接赋予dwBlockNum=13。这里必须严格和Uboot里面的分区表对应。该函数返回block13对应的起始段。
关于MBR:NANDFLASH由BLOCK和Sector组成,所以NANDFLASH的第0 BLOCK,第1 Sector为主引导扇区,FDISK程序写到该扇区的内容称为主引导记录(MBR)。该记录占用512个字节,它用于硬盘启动时将系统控制权交给用户指定的,并在分区表中登记了的某个操作系统区。
一个扇区的硬盘主引导记录MBR由如图6-15所示的4个部分组成。
·主引导程序(偏移地址0000H--0088H),它负责从活动分区中装载,并运行系统引导程序。
·出错信息数据区,偏移地址0089H--00E1H为出错信息,00E2H--01BDH全为0字节。
·分区表(DPT,Disk Partition Table)含4个分区项,偏移地址01BEH--01FDH,每个分区表项长16个字节,共64字节为分区项1、分区项2、分区项3、分区项4。
·结束标志字,偏移地址01FE--01FF的2个字节值为结束标志55AA,如果该标志错误系统就不能启动。

  
0000-0088
  
  
  
Master Boot Record
  
主引导程序
  
  
主引导
  
程序
  
  
0089-01BD
  
  
出错信息数据区
  
  
数据区
  
  
01BE-01CD
  
  
分区项1(16字节)
  
  
  
分区表
  
  
  
01CE-01DD
  
  
分区项2(16字节)
  
  
01DE-01ED
  
  
分区项3(16字节)
  
  
01EE-01FD
  
  
分区项4(16字节)
  
  
01FE
  
  
55
  
  
结束标志
  
  
01FF
  
  
AA
  
结合这些,我们再分析IsValidMBR()函数的代码,通过调用FMD_ReadSector()函数,读取所在page的数据,然后判断其帧头是否为0xE9,0xFd,0xFF,帧尾是否为0x55,0xAA。如果没有,则表示对应页没有保存MBR信息,这时程序调用BP_LowLevelFormat()函数,格式化对应block,并创建MBR。BP_LowLevelFormat()函数代码如下:
BOOL BP_LowLevelFormat(DWORD dwStartBlock,DWORD dwNumBlocks, DWORD dwFlags)
{
   dwNumBlocks = min (dwNumBlocks, g_FlashInfo.dwNumBlocks);// 要格式化的block不能超过flash总的block数

   RETAILMSG(1,(TEXT("Enter LowLevelFormat [0x%x, 0x%x].\r\n"),dwStartBlock, dwStartBlock + dwNumBlocks - 1));

    //Erase all the flash blocks.
    //检测每一个Sector,每个BLOCK只要有一个Sector不能读写这个块都会被处理成坏块,这样才能保证系统的稳定性。
    if(!EraseBlocks(dwStartBlock, dwNumBlocks, dwFlags))//这里有问题,dwNumBlocks应为dwStartBlock+ dwNumBlocks - 1!lqm.10-04-14
       return(FALSE);

    //Determine first good starting block
   while (IS_BLOCK_UNUSABLE (dwStartBlock) && dwStartBlock <g_FlashInfo.dwNumBlocks)
        {
       dwStartBlock++;
    }

    if(dwStartBlock >= g_FlashInfo.dwNumBlocks)
        {
       RETAILMSG(1,(TEXT("BP_LowLevelFormat: no goodblocks\r\n")));        
       return FALSE;
    }

    //MBR goes in the first sector of the starting block.  This will be logical sector 0.
   g_dwMBRSectorNum = dwStartBlock * g_FlashInfo.wSectorsPerBlock;

        //Create an MBR.
   CreateMBR();// 创建MBR,这时创建的MBR里面的分区表信息是空的。

   RETAILMSG (1, (TEXT("Done.\r\n\r\n")));
   return(TRUE);
}
值得注意的是,这里的CreateMBR()函数创建的是一个空MBR,只是放了帧头和帧尾,具体里面的分区信息并没有填充。这些信息是在后面填充的。CreateMBR()函数代码如下:
static BOOL CreateMBR()
{
    //This, plus a valid partition table, is all the CE partition manager needs torecognize
    //the MBR as valid. It does not contain boot code.

   memset (g_pbMBRSector, 0xff, g_FlashInfo.wDataBytesPerSector);
   g_pbMBRSector[0] = 0xE9;
   g_pbMBRSector[1] = 0xfd;
   g_pbMBRSector[2] = 0xff;
   g_pbMBRSector[SECTOR_SIZE-2] = 0x55;
   g_pbMBRSector[SECTOR_SIZE-1] = 0xAA;

    //Zero out partition table so that mspart treats entries as empty.
    //因为这里还没有分区,故先将分区表清零
   memset (g_pbMBRSector+PARTTABLE_OFFSET, 0, sizeof(PARTENTRY) *NUM_PARTS);

   return WriteMBR();//将上面的信息写到指定的block
}  
可以看出,这里面只是填入了一个帧头和帧尾的信息。
格式化完成后,再调用CreatePartition()函数进行分区,代码如下:
static HANDLE CreatePartition (DWORD dwStartSector,DWORD dwNumSectors, DWORD dwPartType, BOOL fActive, DWORD dwPartIndex)
{
   DWORD dwBootInd = 0;

   RETAILMSG(1, (TEXT("CreatePartition: Enter CreatePartition for0x%x.\r\n"), dwPartType));

    if(fActive)
       dwBootInd |= PART_IND_ACTIVE;
    if(dwPartType == PART_BOOTSECTION || dwPartType == PART_BINFS || dwPartType ==PART_XIP)//NK映像只读!
       dwBootInd |= PART_IND_READ_ONLY;   

    // If start sector is invalid, it means find next free sector
    if(dwStartSector == NEXT_FREE_LOC)
        {        

       dwStartSector = FindFreeSector();
       if (dwStartSector == INVALID_ADDR)
                 {
           RETAILMSG(1, (TEXT("CreatePartition: can't find freesector.\r\n")));
           return INVALID_HANDLE_VALUE;
       }

        // Start partitions on the next block ifthey are currently on the wrong block type.
       if (dwStartSector % g_FlashInfo.wSectorsPerBlock)
                 {            
           DWORD dwBlock = dwStartSector / g_FlashInfo.wSectorsPerBlock;
           if (IS_PART_READONLY(dwBootInd) != IS_BLOCK_READONLY(dwBlock))
                           {
                dwStartSector = (dwBlock+1) *g_FlashInfo.wSectorsPerBlock;
           }
       }
    }

    if(IS_PART_READONLY(dwBootInd)) // 分区是否只读
        {

       // Allow read-only partitions to go to the end of disk, if requested.
       if (dwNumSectors == USE_REMAINING_SPACE)
                 {

           DWORD dwLastLogSector = LastLogSector();
           if (dwLastLogSector == INVALID_ADDR)
                return INVALID_HANDLE_VALUE;

           dwNumSectors = dwLastLogSector- dwStartSector + 1;
       }
    }
   else
        {

       DWORD dwLastLogSector = LastLogSector();
       if (dwLastLogSector == INVALID_ADDR)
           return INVALID_HANDLE_VALUE;

       // Determine the number of blocks to reserve for the FAL compaction whencreating an extended partition.
       DWORD dwReservedBlocks = g_FlashInfo.dwNumBlocks /PERCENTAGE_OF_MEDIA_TO_RESERVE;
       if((dwReservedBlocks = g_FlashInfo.dwNumBlocks /PERCENTAGE_OF_MEDIA_TO_RESERVE) < MINIMUM_FLASH_BLOCKS_TO_RESERVE)
                 {
           dwReservedBlocks = MINIMUM_FLASH_BLOCKS_TO_RESERVE;
       }

       DWORD dwNumMaxSectors = dwLastLogSector - dwStartSector + 1 -dwReservedBlocks * g_FlashInfo.wSectorsPerBlock;

       // If dwNumSectors was provided, validate it isn't past the max.
       // If dwNumSectors is USE_REMAINING_SPACE, fill disk with max sectors.
       if ((dwNumSectors == USE_REMAINING_SPACE)  || (dwNumMaxSectors <  dwNumSectors)) {
           RETAILMSG(1, (TEXT("CreatePartition: Num sectors set to 0x%x toallow for compaction blocks.\r\n"), dwNumMaxSectors));
           dwNumSectors = dwNumMaxSectors ;   
       }
    }


    if(!AreSectorsFree (dwStartSector, dwNumSectors))
        {
       RETAILMSG (1, (TEXT("CreatePartition: sectors [0x%x, 0x%x]requested are out of range or taken by another partition\r\n"),dwStartSector, dwNumSectors));
       return INVALID_HANDLE_VALUE;
    }

   RETAILMSG(1, (TEXT("CreatePartition: Start = 0x%x, Num = 0x%x.\r\n"),dwStartSector, dwNumSectors));
    //准备分区信息写入分区表
   AddPartitionTableEntry (dwPartIndex, dwStartSector, dwNumSectors,(BYTE)dwPartType, (BYTE)dwBootInd);

    if(IS_PART_READONLY(dwBootInd))
        {
       if (!WriteLogicalNumbers (dwStartSector, dwNumSectors, TRUE))
                 {
           RETAILMSG(1, (TEXT("CreatePartition: can't mark sectorinfo.\r\n")));
           return INVALID_HANDLE_VALUE;
       }
    }

    if(!WriteMBR())// 这里写MBR的分区表信息???
       return INVALID_HANDLE_VALUE;

   g_partStateTable[dwPartIndex].pPartEntry = (PPARTENTRY)(g_pbMBRSector +PARTTABLE_OFFSET + sizeof(PARTENTRY)*dwPartIndex);
   g_partStateTable[dwPartIndex].dwDataPointer = 0;

   return (HANDLE)&g_partStateTable[dwPartIndex];            
}
该分区的传入参数作用如下:
dwStartSector:起始分区的逻辑页
dwNumSectors:分区占用的页数.如果为-1则视为将余下的所有空间划为一个分区。
dwPartType:分区类型
fActive: TRUE则创建活动分区
dwPartIndex:MBR的分区入口索引
调用该函数实现BinFS系统分区,写入MBR信息。
再回到WriteOSImageToBootMedia()函数,前面创建完BinFS系统分区之后,再调用BP_SetDataPointer()函数设置分区数据指针,调用BP_WriteData()函数将数据写到nandflash。可见,完成写系统映像到nand的函数,最终是调用了BP_WriteData()函数。
烧到nand之后,再更新TOC数据,之后再调用BP_OpenPartition()函数给剩下的flash分区,格式为PART_DOS32,即FAT32格式。
给剩下的flash分区之后,程序就会跳转到映像下载地址,加载系统并运行。到此,Eboot完成使命,将控制权交给WINCE系统。

QQ|手机版|小黑屋|深圳市九鼎创展科技官方论坛 ( 粤ICP备11028681号-2  

GMT+8, 2024-5-21 23:52 , Processed in 0.046514 second(s), 23 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表