四极管 EBOOT最后一个函数OEMLaunch详解
在下载了内核镜像之后,进入到最后一个阶段OEMLaunch,在这里将完成eboot的最后一部分任务,并将跳转到OAL.exe开始启动内核。还是先给出该函数的源码如下:
可见OEMLaunch的几个参数都是从DownloadImage函数获得的,dwImageStart是镜像的起始地址。DwImageLength是镜像的大小,dwLaunchAddr是内核的启动地址,pRomHdr指向TOC数据。
本文原始出处:http://jazka.blog.51cto.com/809003/612722
void OEMLaunch( DWORD dwImageStart, DWORD dwImageLength, DWORD dwLaunchAddr, const ROMHDR *pRomHdr )
{
DWORD dwPhysLaunchAddr;
EDBG_ADDR EshellHostAddr;
EDBG_OS_CONFIG_DATA *pCfgData;
volatile S3C2416_IOPORT_REG *s2416IO_regs = (S3C2416_IOPORT_REG *)OALPAtoVA(S3C2416_BASE_REG_PA_IOPORT, FALSE);
OALMSG(OAL_FUNC, (TEXT("+OEMLaunch.\r\n")));
//以下程序的功能是判断是否需要将镜像文件写入到Nand Flash中,当前镜像只是下载到了RAM中,一般都会写到FLASH中,否则掉电重启后,RAM的镜像就不存在了,又要重新下载镜像实现启动。
if (g_bDownloadImage && (g_pBootCfg->ConfigFlags & TARGET_TYPE_NAND))
{
// Since this platform only supports RAM images, the image cache address is the same as the image RAM address.
//
//以下程序的功能是根据镜像的数据类型stepldr、bootloader还是nk分别写入对应的NandFLASH位置。
如果镜像类型是Stepldr或者bootloader,通过函数writeRawImageToBootMedia完成实现的flash写操作,主要是完成nand Flash的坏块检测、块擦除、写块、读块、设置TOC等操作。该函数位于文件WINCEROOT\SMDK2416\SRC\BOOTLOADER\EBOOT\Nand.cpp中。
如果镜像类型是nk,则通过函数WriteOSImageToBootMedia写到flash中,该函数放到后面详细解释。
由于TOC记录的数据与BOOTLOADER、NK都有关系,所以如果是这两种镜像类型,在写入NAND以后还需要更新TOC中的数据信息,如TOC_Write函数的调用。而Stepldr则与TOC无关,无需更新。
switch (g_ImageType)
{
case IMAGE_TYPE_STEPLDR:
if(!WriteRawImageToBootMedia(dwImageStart, dwImageLength, dwLaunchAddr))
{
OALMSG(OAL_ERROR, (TEXT("ERROR: OEMLaunch: Failed to store image to Smart Media.\r\n")));
goto CleanUp;
}
OALMSG(TRUE, (TEXT("Step loader image stored to Nandflash.will auto Shutdown\r\n")));
Disp_String(1,DownInfo1,0,ITEM3_LINE+90,pDispMem,ASCII);
Delayms(800);
// ClearDispBuffer();
PowerOff();
while(1);
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 0 // don't write TOC after download Eboot
if ( !TOC_Write() ) {
EdbgOutputDebugString("*** OEMLaunch ERROR: TOC_Write failed! Next boot may not load from disk *** \r\n");
}
TOC_Print();
#endif
}
OALMSG(TRUE, (TEXT("Eboot image stored to Nandflash.Will auto Shutdown\r\n")));
Disp_String(1,DownInfo1,0,ITEM3_LINE+90,pDispMem,ASCII);
Delayms(800);
//ClearDispBuffer();
PowerOff();
while(1);
break;
case IMAGE_TYPE_RAMIMAGE:
OALMSG(1, (TEXT("+IMAGE_TYPE_RAMIMAGE dwImageStart:%08x.,dwLaunchAddr : %08x\r\n"),dwImageStart,dwLaunchAddr));
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 Smart Media.\r\n")));
goto CleanUp;
}
EdbgOutputDebugString("INFO1:dwLaunchAdd : %08x g_pTOC->id[0].dwJumpAddress :%08x, g_pTOC->id[1].dwJumpAddress :%08x\r \r\n", dwLaunchAddr, g_pTOC->id[0].dwJumpAddress,g_pTOC->id[1].dwJumpAddress);
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("INFO2: using TOC[%d] dwJumpAddress: 0x%x\r\n", g_dwTocEntry, dwLaunchAddr);
}
//OALMSG(TRUE, (TEXT("NK.bin image stored to Nandflash.please manual Shutdown\r\n")));
Delayms(800);
// ClearDispBuffer();
break;
}
//ClearDispBuffer();
if ( !ReadOSImageFromBootMedia( ) )
{
OALMSG(OAL_ERROR, (TEXT("OEMPlatformInit ERROR: Failed to load kernel region into RAM.\r\n")));
SpinForever();
}
}
//以下程序的功能是判断为不需要将镜像文件写入Nand Flash时的处理情况,从代码中可以看出,如果镜像是从stepldr或者bootloader,则必须要写入到flash中,否则提示无法从RAM启动。
else if(g_bDownloadImage)
{
switch (g_ImageType)
{
case IMAGE_TYPE_STEPLDR:
OALMSG(TRUE, (TEXT("Stepldr image can't launch from ram.\r\n")));
OALMSG(TRUE, (TEXT("You should program it into flash.\r\n")));
SpinForever();
break;
case IMAGE_TYPE_LOADER:
OALMSG(TRUE, (TEXT("Eboot image can't launch from ram.\r\n")));
OALMSG(TRUE, (TEXT("You should program 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 port settings for service
// connections - also sends us KITL flags. This information is used later by the OS (KITL).
//以下程序功能是当采用网络下载镜像时的处理函数,还包括了关于KITL的处理,由于本平台是从USB下载,这部分不会执行,所以不做详细的分析。
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: 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;
}
}
SaveEthernetAddress();
// 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.
//以下程序的功能是将内核的启动地址记录到TOC数据中,这样子在下一次跳转到内核镜像的时候就不需要重新获取跳转地址,而是直接按TOC记录的进行跳转。
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 the MMU off)…
//以下程序的功能是dwPhysLaunchAddr便是启动内核的物理地址,作为Launch函数的输入参数完成跳转到内核启动。Launch函数是汇编实现的。在Startup.s中实现。
到此为止,EBOOT的全部任务就完成了。
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();
}
接下来详细解析WriteOSImageToBootMedia函数的源码,该函数位于文件WINCEROOT/SMDK2416\SRC\BOOTLOADER\EBOOT\Nand.cpp中,实现源码为:
/*
@func BOOL | WriteOSImageToBootMedia | Stores the image cached in RAM to the Boot Media.
The image may be comprised of one or more BIN regions.
@rdesc TRUE = Success, FALSE = Failure.
@comm
@xref
*/
BOOL WriteOSImageToBootMedia(DWORD dwImageStart, DWORD dwImageLength, DWORD dwLaunchAddr)
{
BYTE nCount;
DWORD dwNumExts;
PXIPCHAIN_SUMMARY pChainInfo = NULL;
EXTENSION *pExt = NULL;
DWORD dwBINFSPartLength = 0;
HANDLE hPart;
DWORD dwStoreOffset;
DWORD dwMaxRegionLength[BL_MAX_BIN_REGIONS] = {0};
DWORD dwChainStart, dwChainLength;
DWORD dwBlock;
DWORD nBlockNum;
// DWORD checksum = 0;
// Initialize the variables
dwChainStart = dwChainLength = 0;
OALMSG(OAL_FUNC, (TEXT("+WriteOSImageToBootMedia\r\n")));
OALMSG(TRUE, (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: device doesn't exist.\r\n")));
return(FALSE);
}
if (g_ImageType == IMAGE_TYPE_RAMIMAGE)
{
g_dwMBRSectorNum = BLOCK_TO_SECTOR(IMAGE_START_BLOCK);
OALMSG(TRUE, (TEXT("g_dwMBRSectorNum = 0x%x\r\n"), g_dwMBRSectorNum));
dwBlock = IMAGE_START_BLOCK;
// dwImageStart += dwLaunchAddr;
// dwImageLength = dwImageLength; //step loader can support 4k bytes only. NO SuperIPL case is 16K....... 3 Block
}
RETAILMSG(1,(TEXT("Erase Block from 0x%x, to 0x%x \n"), dwBlock, SPECIAL_AREA_START + SPECIAL_AREA_SIZE - 1));
for (nBlockNum = dwBlock ; nBlockNum < SPECIAL_AREA_START + SPECIAL_AREA_SIZE; nBlockNum++)
{
if (!FMD_EraseBlock(nBlockNum))
{
return(FALSE);
}
}
//以下函数程序的功能是判断TOC数据是否有效,这种代码在EBOOT中会经常用到。
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 extension descriptor.
// This region, if found, details the number, start, and size of each BIN region.
//以下程序的功能是用来检测当前下载的镜像中是否包含NK.exe,并返回其扩展指针。主要通过GetKernelExtPointer函数来完成此任务。
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.
//以下部分程序的功能是负责检查用来的创建BINFS分区的空间是否足够。
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));
}
OALMSG(TRUE, (TEXT("dwBlock = %d \n"), dwBlock));
// 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.
//以下部分程序的功能是通过函数BP_OpenPartition来在Nand Flash设备上创建分区。该函数在文件WINCEROOT\SMDK2416\SRC\BOOTLOADER\EBOOT\Bootpart1.cpp中。第一个参数dwStartSector为起始逻辑扇区。(IMAGE_START_BLOCK+1)*PAGES_PER_BLOCK即使OSImage所在的Block,加1是因为MBR保存在IMAGE_START_BLOCK开始的第一个block中;第二个参数dwPartType表示分区的类型,PART_BINFS表示要创建分区还是打开分区,PART_OPEN_ALWAYS表示如果不存在这个分区则创建,如果存在则打开。最后返回给分区的句柄。
hPart = BP_OpenPartition( (dwBlock)*(PAGES_PER_SUBLK)*(SECTORS_PER_SUPAGE),
//SECTOR_TO_BLOCK_SIZE(FILE_TO_SECTOR_SIZE(dwBINFSPartLength))*PAGES_PER_BLOCK, // align to block
//(dwBINFSPartLength/SECTOR_SIZE)+1,
SECTOR_TO_BLOCK_SIZE(FILE_TO_SECTOR_SIZE(dwBINFSPartLength))*SECTORS_PER_SUBLK + (IMAGE_START_BLOCK+1)*SECTORS_PER_SUBLK, // hmseo for whimory... +1 is for MBR
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);
}
//以下部分程序的功能是BP_SetDataPointer设置该分区的数据指针实际上数据指针是在该分区中下一次被读或者被写的位置,该函数位于文件WINCEROOT\SMDK2416\SRC\BOOTLOADER\EBOOT\Bootpart1.cpp中,第一个参数hParttion为被创建分区的句柄,第二个参数dwAdderss为数据指针的新位置,一般会配合BP_ReadData()和BP_WriteData()两个函数来用。
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;
// No concern about Multiple XIP
// nCount = 0;
OALMSG(TRUE, (TEXT("nCount = %d \n"), nCount));
// Media byte offset where image region is stored.
dwStoreOffset += nCount ? dwMaxRegionLength[nCount-1] : 0;
// dwStoreOffset = 0;
// Set the file pointer (byte indexing) to the correct offset for this particular region.
//
if ( !BP_SetDataPointer(hPart, dwStoreOffset + (dwBlock+1)*BYTES_PER_SECTOR*SECTORS_PER_SUBLK) )
{
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.
//以下部分程序的功能是调用函数BP_WriteData向该分区写入数据,即将OSImage写入BINFS分区中。该函数位于WINCEROOT\SMDK2416\SRC\BOOTLOADER\EBOOT\Bootpart1.cpp文件中。第一个参数hPartition被创建的分区的句柄,第二个参数pbBuffer为要写入的数据的Buffer,第三个参数dwLength为要写入数据的长度。
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?
//以下部分程序的功能是完成更新TOC并把内核拷贝到SDRAM中,为下一步的跳转执行做准备。
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 to OEMLaunch
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 Chain region
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));
OALMSG(TRUE, (TEXT(" memcpy : g_pTOC->chainInfo.dwLoadAddress = 0x%X; dwRegionStart = 0x%X; dwRegionLength = 0x%X\n"),
g_pTOC->chainInfo.dwLoadAddress,
dwRegionStart,
dwRegionLength));
// Now copy it to the SDRAM
//memcpy((void *)g_pTOC->chainInfo.dwLoadAddress, (void *)dwRegionStart, dwRegionLength);
}
}
OALMSG(TRUE, (TEXT("-WriteOSImageToBootMedia\r\n")));
return(TRUE);
}