这个函数首先初始化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系统。
|