之前的调式运行都是PB通过以太网下载到板子的RAM中运行,也就是CE直接运行在内存中.但是最终CE都是在设备上电后自动运行.这就需要将CE的OS镜像存储在非易失性设备中,然后bootloader将其复制到RAM中并运行.
板子上有64M的Nand Flash,我们就将系统镜像存储在Nand Flash中.PB首先通过以太网下载OS镜像到RAM,然后eboot再将其烧写到Nand Flash中.系统上电eboot启动后再读取Nand Flash中的系统镜像,将其加载到RAM中运行.
nk.bin是基于记录的OS镜像文件,需要通过一个类似解压的工作才能运行,而nk.nb0则是系统运行的二进制镜像,直接load到RAM中就可以运行了.BINFS的功能实现可以分3步来:
第一步:将OS镜像写入到Nand Flash中的BINFS文件系统,然后可以在eboot中进行读取并加载到RAM中运行
第二步:将NandFlash分成BINFS和FAT两个区,BINFS用来保存nk.bin,FAT用来作存储区
第三步:实现诸如Multibin等更高级的BINFS应用.
这次先实现第一步,实现OS镜像在BINFS中的存储和加载.如有错误,欢迎指正.
BINFS是Binary ROM Image File System,CE5.0的联机文档是这么说的:
The binary ROM Image File System (BINFS) is a file system that reads the binary image (.bin) file format generated by Romimage.exe. The .bin file format organizes data into specific sections. Each section contains a section header that specifies the starting address, length, and checksum values for that section. Romimage.exe writes data organized by logical sections, such as an application's text or .data region, to the .bin file.
PB下载到开发板的是nk.bin文件,在eboot中的Blcommon.c中的DownloadImage函数将其解压为nk.nb0,然后我们烧写nk.nb0到flash中.系统启动时eboot从flash中读取nk.nb0,然后加载到内存中运行.
1.增加eboot对BINFS的支持
(1)添加BOOTPART库
该库位于/PUBLIC/COMMON/OAK/DRIVERS/ETHDBG/BOOTPART,我们将其复制到BSP目录下,如/PLATFORM/GEC2410/SRC/COMMON/BOOTPART,该库实现了BINFS操作的各种函数.Flash驱动在SMDK已经实现了,在FMD库中.
(2)在eboot的source文件的TARGETLIBS中增加对该库的引用:
$(_TARGETPLATROOT)/lib/$(_CPUINDPATH)/bootpart.lib
(3)也可以直接引用bootpart.lib,由于Flash配置的关系需要做些修改
//因为Flash前18个block被保留,而GetMBRSectorNum得到的是第一个空闲的Block,因此这里手动设定MBR的Block //g_dwMBRSectorNum = GetMBRSectorNum(); g_dwMBRSectorNum = (IMAGE_START_BLOCK) * g_FlashInfo.wSectorsPerBlock;
同样在BP_OpenPartition中手动设置格式化的起始Block
BP_LowLevelFormat (18, g_FlashInfo.dwNumBlocks, dwFlags);
另外我们的nand.c中会调用变量dwLastWrittenLoc,在bootpart.cpp中需要extern "C"的声明
extern "C" {DWORD g_dwLastWrittenLoc;} // Stores the byte address of the last physical flash address written to
重新编译该库.
(4)在OEMPlatformInit函数添加BINFS的初始化,
g_dwImageStartBlock = IMAGE_START_BLOCK; //Try to initialize the boot media block driver and BinFS partition. if ( !BP_Init((LPBYTE)BINFS_RAM_START, BINFS_RAM_LENGTH, NULL, NULL, NULL) ) { OALMSG(OAL_WARN, (TEXT("WARNING: OEMPlatformInit failed to initialize Boot Media./r/n"))); g_bSmartMediaExist = FALSE; } else { g_bSmartMediaExist = TRUE; }
这里IMAGE_START_BLOCK就是BINFS在NandFlash起始的block bumber,实际值为0x12,即BINFS从block 18开始,之前的block的用来保存Nboot,eboot等(虽然现在eboot实现在nor flash中,并未用到,但为了可能用到因而暂时保留一个区).实际上block 18是MBR,block 19才开始是OS镜像的区域
#define RESERVED_BOOT_BLOCKS (NBOOT_BLOCK_SIZE + TOC_BLOCK_SIZE + EBOOT_BLOCK_SIZE)
#define IMAGE_START_BLOCK RESERVED_BOOT_BLOCKS
BP_Init实际上调用了FMD_Init和FMD_GetInfo进行NandFlash的初始化和获取NandFlash的信息,保留了BINFS_RAM_START地址的一段内存将来用来处理Flash的相关信息.
(4) OEMMultiBINNotify
在原来SMDK2410的BSP中g_pOEMMultiBINNotify函数只能下载.nb0,我们需要修改来获取nk.bin的信息
在OEMDebugInit中创建一个新的函数指针OEMMultiBINNotify
//g_pOEMMultiBINNotify = OEMDownloadFileNotify;
g_pOEMMultiBINNotify = OEMMultiBINNotify;
OEMMultiBINNotify代码:
void OEMMultiBINNotify(const PMultiBINInfo pInfo) { BYTE nCount; DWORD g_dwMinImageStart; OALMSG(OAL_FUNC, (TEXT("+OEMMultiBINNotify./r/n"))); if (!pInfo || !pInfo->dwNumRegions) { OALMSG(OAL_WARN, (TEXT("WARNING: OEMMultiBINNotify: Invalid BIN region descriptor(s)./r/n"))); return; } if (!pInfo->Region[0].dwRegionStart && !pInfo->Region[0].dwRegionLength) { return; } g_dwMinImageStart = pInfo->Region[0].dwRegionStart; OALMSG(TRUE, (TEXT("/r/nDownload BIN file information:/r/n"))); OALMSG(TRUE, (TEXT("-----------------------------------------------------/r/n"))); for (nCount = 0 ; nCount < pInfo->dwNumRegions ; nCount++) { OALMSG(TRUE, (TEXT("[%d]: Base Address=0x%x Length=0x%x/r/n"), nCount, pInfo->Region[nCount].dwRegionStart, pInfo->Region[nCount].dwRegionLength)); if (pInfo->Region[nCount].dwRegionStart < g_dwMinImageStart) { g_dwMinImageStart = pInfo->Region[nCount].dwRegionStart; if (g_dwMinImageStart == 0) { OALMSG(OAL_WARN, (TEXT("WARNING: OEMMultiBINNotify: Bad start address for region (%d)./r/n"), nCount)); return; } } } memcpy((LPBYTE)&g_BINRegionInfo, (LPBYTE)pInfo, sizeof(MultiBINInfo)); OALMSG(TRUE, (TEXT("-----------------------------------------------------/r/n"))); OALMSG(OAL_FUNC, (TEXT("_OEMMultiBINNotify./r/n"))); }
OEMMultiBINNotify获取PB将要下载的nk.bin信息,并保存在MultiBINInfo型的结构g_BINRegionInfo中.
(5)在nand.c中实现读写OS镜像函数:WriteDiskImageToSmartMedia和ReadRamImageFromBootMedia
[1]WriteDiskImageToSmartMedia
目前只支持一个nb0文件,即nk.nb0的保存,要支持multi-bin还需要在增加代码,第一步先支持单个镜像文件,然后再来支持multi-bin,有些代码是用来支持multi-bin的,这部分我没有修改,目前不会用到.
BOOL WriteDiskImageToSmartMedia(DWORD dwImageStart, DWORD dwImageLength, BOOT_CFG *pBootCfg) { 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; dwChainStart = dwChainLength = 0; OALMSG(OAL_FUNC, (TEXT("+WriteOSImageToBootMedia/r/n"))); OALMSG(OAL_INFO, (TEXT("+WriteOSImageToBootMedia: ImageStart: 0x%x, ImageLength: 0x%x, LaunchAddr:0x%x/r/n"), dwImageStart, dwImageLength, pBootCfg->dwLaunchAddr)); //在eboot的BootCfg结构中添加几个参数用来保存nk.bin的相关信息,如内存加载地址,Image大小等 /* typedef struct { ULONG Signature; // Config signature (used to validate). USHORT VerMajor; // Config major version. USHORT VerMinor; // Config minor version. ULONG ConfigFlags; // General bootloader flags. ULONG IPAddr; // Device static IP address. ULONG SubnetMask; // Device subnet mask. BYTE BootDelay; // Bootloader count-down delay. BYTE LoadDeviceOrder; // Search order for download devices. USHORT CS8900MAC[3]; // Crystal CS8900A MAC address. BOOL bLoadNandOSImage; //launch existing OS image from Nand Flash? DWORD dwLaunchAddr; // Kernel region launch address. DWORD dwLoadAddress; //OS RAM Image load address DWORD dwTtlSectors; //OS RAM Image sectors DWORD dwStoreOffset; //OS RAM Image offset DWORD dwJumpAddress; //OS RAM Image Jump Address DWORD dwImageType; //OS RAM Image ImageType CHAININFO chainInfo; } BOOT_CFG, *PBOOT_CFG; */ pBootCfg->dwLoadAddress = dwImageStart; pBootCfg->dwStoreOffset = 0; pBootCfg->dwTtlSectors = FILE_TO_SECTOR_SIZE(dwImageLength); // If there isn't a SmartMedia card in the system, it'll be kind of hard to write an image to it... // if (!g_bSmartMediaExist) { EdbgOutputDebugString("WARNING: Smart Media device doesn't exist - unable to store image./r/n"); return(FALSE); } // Look in the kernel region's extension area for a multi-BIN extension descriptor. // 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. Chain region 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("Found the 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 a multi-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 pChainInfo is not in any particular order. // So, walk our MultiBINInfo matching up pChainInfo to find each regions MAX Length for (i = 0; i < dwNumExts; i++) { if ( g_BINRegionInfo.Region[i].dwRegionStart == (DWORD)((pChainInfo + nCount)->pvAddr) ) { dwMaxRegionLength[i] = (pChainInfo + nCount)->dwMaxLength; OALMSG(TRUE, (TEXT("dwMaxRegionLength[%u]: 0x%x /r/n"), i, dwMaxRegionLength[i])); break; } } } } else // A single BIN file or potentially a multi-region update (but the partition's already been created in this 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 the MBR on the Boot Media and its length is // determined by the maximum image size (or sum of all maximum sizes in a multi-region design). // Parameters are LOGICAL sectors. // EdbgOutputDebugString("Partiotion block numer: 0x%d/r/n",(IMAGE_START_BLOCK+1)*PAGES_PER_BLOCK); 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 to open/create partition./r/n"))); return(FALSE); } // Are there multiple BIN files in RAM (we may just be updating one in a multi-BIN solution)? // 实际上目前我们只下载一个bin文件,即nk.bin,也就是g_BINRegionInfo.dwNumRegions=1 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 this particular region. // if ( !BP_SetDataPointer(hPart, dwStoreOffset) ) { OALMSG(OAL_ERROR, (TEXT("ERROR: StoreImageToBootMedia: Failed to set 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 to write region to BINFS partition (start=0x%x, length=0x%x)./r/n", dwRegionStart, dwRegionLength); return(FALSE); } // update our TOC? // if ((pBootCfg->dwLoadAddress == g_BINRegionInfo.Region[nCount].dwRegionStart) && pBootCfg->dwTtlSectors == FILE_TO_SECTOR_SIZE(dwRegionLength) ) { pBootCfg->dwStoreOffset = dwStoreOffset; pBootCfg->dwJumpAddress = 0; // Filled upon return to OEMLaunch pBootCfg->dwImageType = g_ImageType; // copy Kernel Region to SDRAM for jump memcpy((void*)pBootCfg->dwLoadAddress, (void*)dwRegionStart, dwRegionLength); OALMSG(TRUE, (TEXT("Updateded TOC!/r/n"))); } //这个if用来保存multi-bin信息,目前不会进入 else if( (dwChainStart == g_BINRegionInfo.Region[nCount].dwRegionStart) && (dwChainLength == g_BINRegionInfo.Region[nCount].dwRegionLength)) { // Update our TOC for Chain region pBootCfg->chainInfo.dwLoadAddress = dwChainStart; pBootCfg->chainInfo.dwFlashAddress = FILE_TO_SECTOR_SIZE(g_dwLastWrittenLoc); pBootCfg->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"), pBootCfg->chainInfo.dwLoadAddress, pBootCfg->chainInfo.dwFlashAddress, pBootCfg->chainInfo.dwLength)); // Now copy it to the SDRAM memcpy((void *)pBootCfg->chainInfo.dwLoadAddress, (void *)dwRegionStart, dwRegionLength); } } 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 to open/create Extended partition ***/r/n"))); } OALMSG(OAL_FUNC, (TEXT("-WriteOSImageToBootMedia/r/n"))); return TRUE; }
[2]ReadRamImageFromBootMedia
读就比较简单了,通过bootpart库的API去读取BINFS分区的文件(nk.nb0),放入指定的内存区域,然后就可以在OEMLaunch中跳转运行CE OS了.
BOOL ReadRamImageFromBootMedia( ) { HANDLE hPart; SectorInfo si; DWORD chainaddr,flashaddr; DWORD i; OALMSG(OAL_INFO, (TEXT("+ReadOSImageFromBootMedia/r/n"))); if (!g_bSmartMediaExist) { OALMSG(OAL_ERROR, (TEXT("ERROR: WriteRawImageToBootMedia: device doesn't exist./r/n"))); return(FALSE); } if ( !OEMVerifyMemory(g_BootConfig.dwLoadAddress, sizeof(DWORD)) || !OEMVerifyMemory(g_BootConfig.dwJumpAddress, sizeof(DWORD)) || !g_BootConfig.dwTtlSectors ) { OALMSG(OAL_ERROR, (TEXT("ReadOSImageFromBootMedia: ERROR_INVALID_ADDRESS: (address=0x%x, sectors=0x%x, launch address=0x%x).../r/n"), g_BootConfig.dwLoadAddress, g_BootConfig.dwTtlSectors, g_BootConfig.dwJumpAddress)); return FALSE; } // Open the BINFS partition (it must exist). // hPart = BP_OpenPartition( NEXT_FREE_LOC, USE_REMAINING_SPACE, PART_BINFS, TRUE, PART_OPEN_EXISTING); if (hPart == INVALID_HANDLE_VALUE ) { OALMSG(OAL_ERROR, (TEXT("ERROR: ReadOSImageFromBootMedia: Failed to open existing partition./r/n"))); return(FALSE); } // Set the partition file pointer to the correct offset for the kernel region. // if ( !BP_SetDataPointer(hPart, g_BootConfig.dwStoreOffset) ) { OALMSG(OAL_ERROR, (TEXT("ERROR: ReadOSImageFromBootMedia: Failed to set data pointer in partition (offset=0x%x)./r/n"), g_BootConfig.dwStoreOffset)); return(FALSE); } // Read the kernel region from the Boot Media into RAM. // if ( !BP_ReadData( hPart, (LPBYTE)(g_BootConfig.dwLoadAddress), SECTOR_TO_FILE_SIZE(g_BootConfig.dwTtlSectors)) ) { OALMSG(OAL_ERROR, (TEXT("ERROR: ReadOSImageFromBootMedia: Failed to read kernel region from partition./r/n"))); return(FALSE); } if (!g_BootConfig.chainInfo.dwLoadAddress) { chainaddr = g_BootConfig.chainInfo.dwLoadAddress; flashaddr = g_BootConfig.chainInfo.dwFlashAddress; for ( i = 0; i < (g_BootConfig.chainInfo.dwLength); i++ ) { OALMSG(TRUE, (TEXT("chainaddr=0x%x, flashaddr=0x%x/r/n"), chainaddr, flashaddr+i)); if ( !FMD_ReadSector(flashaddr+i, (PUCHAR)(chainaddr), &si, 1) ) { OALMSG(OAL_ERROR, (TEXT("TOC_Write ERROR: Unable to read/verify TOC/r/n"))); return FALSE; } chainaddr += 512; } } OALMSG(OAL_FUNC, (TEXT("_ReadOSImageFromBootMedia/r/n"))); return(TRUE); }
(6)在OEMLaunch调用ReadRamImageFromBootMedia和WriteDiskImageToSmartMedia
在OEMLaunch添加以下代码:
//g_bDownloadImage为是否从PB下载nk.bin的标志,如果为0则直接从Flash中读取OS镜像 if (!g_bDownloadImage) { if ( !ReadRamImageFromBootMedia( ) ) { OALMSG(OAL_ERROR, (TEXT("OEMPlatformInit ERROR: Failed to load kernel region into RAM./r/n"))); // return FALSE; } } // If the user requested that a disk image (stored in RAM now) be written to the SmartMedia card, so it now. // //将下载的nk.bin烧写到flash中 if (g_bDownloadImage && (g_BootConfig.ConfigFlags & TARGET_TYPE_NAND)) { WriteDiskImageToSmartMedia(dwImageStart,dwImageLength,&g_BootConfig); //保存新的加载信息到BOOT_CFG结构中. WriteBootConfig(&g_BootConfig); }
设置加载地址:
// 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.dwJumpAddress != dwLaunchAddr)) { g_BootConfig.dwJumpAddress = dwLaunchAddr; } else { dwLaunchAddr= g_BootConfig.dwJumpAddress; OALMSG(OAL_INFO, (TEXT("INFO: using TOC[%d] dwJumpAddress: 0x%x/r/n"), g_dwTocEntry, dwLaunchAddr)); }
(7)在eboot菜单中设置,这样就可以选择是连接PB下载nk.bin进行调试还是直接从Nand加载OS并运行了.
主要工作就是设置一些标志位,这样在OEMLaunch,OEMPreDownload中就可以进行判断就相应的处理了.
eboot菜单中添加:
EdbgOutputDebugString ( "5) Program disk image into SmartMedia card: %s/r/n", (pBootCfg->ConfigFlags & TARGET_TYPE_NAND)?"Enabled":"Disabled"); EdbgOutputDebugString ( "L) Lauch From Existing Flash Image By Default:%s/r/n",(pBootCfg->bLoadNandOSImage)?"Enabled":"Disabled");
菜单对应的执行代码:
case '5': // Toggle image storage to Smart Media. pBootCfg->ConfigFlags = (pBootCfg->ConfigFlags ^ TARGET_TYPE_NAND); bConfigChanged = TRUE; break; case 'L': case 'l': pBootCfg->bLoadNandOSImage = ~pBootCfg->bLoadNandOSImage; bConfigChanged = TRUE; break;
OEMPreDownload的修改,在函数开始添加,这样如果从Flash启动就可以跳过bootme
if (!g_bDownloadImage) { return(BL_JUMP); }