您的位置:首页 > 其它

bootload启动流程(四)--Eboot每个函数的详细说明

2010-10-31 16:42 288 查看

4)OEMPreDownload ()

这个函数也是boot里面实现的功能相对少点,但是也不能忽略,很多人在移植boot的时候实际就在这里容易出问题,关键是要搞清除你的一些flag参数。他的工作主要处理下载前的一些准备工作,我们首先看一下原型,然后对其进行分析:
DWORD OEMPreDownload()
{
char szDeviceName[EDBG_MAX_DEV_NAMELEN];
BOOL fGotJumpImg = FALSE;
DWORD DHCPLeaseTime = 0, *pDHCPLeaseTime = &DHCPLeaseTime;
EdbgOutputDebugString ( "/r/the address of DHCPLeaseTime is %x /r/n",&DHCPLeaseTime);
// If user wants to jump to existing image with no KD - skip download...
if ( !g_bDownloadImage && !g_bWaitForConnect)
{
return(BL_JUMP);
}//如果不需要下载,则可以直接进行启动系统了
if ( !g_bDownloadImage && g_bWaitForConnect )
fGotJumpImg = TRUE;


// EdbgOutputDebugString ( "/r/fgojumpimg is %d /r/n",fGotJumpImg);
// Create device name based on MAC address.
memset(szDeviceName, 0, EDBG_MAX_DEV_NAMELEN);
CreateDeviceName(&g_pBootCfg->EdbgAddr, szDeviceName);
EdbgOutputDebugString("Using device name: '%s'/n", szDeviceName);
// initialize TFTP transport
if ( !(g_pBootCfg->ConfigFlags & CONFIG_FLAGS_DHCP) ) {
pDHCPLeaseTime = NULL; // the pDHCPLeaseTime parameter is overloaded.
// NULL indicates static IP
pDriverGlobals->eth.EdbgFlags = EDBG_FLAGS_STATIC_IP;
}
#ifndef SIMULATOR
if ( g_bUSBDownload == FALSE )
{
if (g_bDownloadImage &&
!EbootInitEtherTransport (&g_pBootCfg->EdbgAddr, &g_pBootCfg->SubnetMask, &fGotJumpImg, pDHCPLeaseTime,
EBOOT_VERSION_MAJOR, EBOOT_VERSION_MINOR, PLATFORM_STRING, szDeviceName, EDBG_CPUID, 0)) {
return BL_ERROR;
}
}
#endif
// SMCSetOptions(OPT_BROADCAST_FILTERING);
// update DHCP lease time
pDriverGlobals->eth.DHCPLeaseTime = DHCPLeaseTime;
if ( g_bUSBDownload == TRUE )
EdbgOutputDebugString("Please send the Image through USB/r/n");
return fGotJumpImg? BL_JUMP : BL_DOWNLOAD;
}
这个函数实际只是在下载前打印了一些下载信息,比较简单,在这里主要确认是USB下载还是网线下载,或者直接从磁盘启动。

5)DownloadImage()

重头戏来了,这个函数在Bootload里面应该算是最重要的,如果是移植别人的boot首先得看懂这个函数,如果是自己编写,首先得完成这里的OEM相关的东西。下面首先看一下原型,然后我们自己分析:
#define BL_HDRSIG_SIZE 7
static BOOL DownloadImage (LPDWORD pdwImageStart, LPDWORD pdwImageLength, LPDWORD pdwLaunchAddr)
{
BYTE hdr[BL_HDRSIG_SIZE];
DWORD dwRecLen, dwRecChk, dwRecAddr;
BOOL fIsFlash;
LPBYTE lpDest;
int nPkgNum = 0;
BYTE nNumRegions = 1;
DWORD dwImageStart, dwImageLength;
*pdwImageStart = *pdwImageLength = *pdwLaunchAddr = 0;
//初始化所需下载参数
EdbgOutputDebugString ("/r/nBegin to read magic number_1/r/n");
do
{
EdbgOutputDebugString ("/r/nBegin to read magic number_2/r/n");
// read the 7 byte "magic number"
if (!OEMReadData (BL_HDRSIG_SIZE, hdr)) {
EdbgOutputDebugString ("/r/nUnable to read image signature./r/n");
HALT (BLERR_MAGIC);
return FALSE;
}//读取BIN文件的标记
// check for multi-bin information packet.
if (!memcmp (hdr, "X000FF/x0A", BL_HDRSIG_SIZE)) {
// determine number of BIN files to be downloaded.
if (!OEMReadData (sizeof (DWORD), (LPBYTE) &dwRecChk)) {
EdbgOutputDebugString("/r/nUnable to read BIN region checksum./r/n");
HALT (BLERR_MAGIC);
return FALSE;
}
// read BIN region descriptions (start address and length).
if (!OEMReadData (sizeof (DWORD), (LPBYTE) &g_MultiBINInfo.dwNumRegions)
|| !OEMReadData ((g_MultiBINInfo.dwNumRegions * sizeof(RegionInfo)), (LPBYTE) &g_MultiBINInfo.Region[0])) {
EdbgOutputDebugString("/r/nUnable to read BIN region descriptors./r/n");
HALT (BLERR_MAGIC);
return FALSE;
}
// verify the packet checksum.
if (!VerifyChecksum((g_MultiBINInfo.dwNumRegions * sizeof(RegionInfo)), (LPBYTE) &g_MultiBINInfo.Region, dwRecChk)) {
EdbgOutputDebugString ("/r/nBIN region descriptor packet failed checksum./r/n");
HALT (BLERR_CHECKSUM);
return FALSE;
}
// provide the region information to the OEM.
if (g_pOEMMultiBINNotify) {
g_pOEMMultiBINNotify((const PMultiBINInfo)&g_MultiBINInfo);
}
// look for next download...
nNumRegions = (BYTE)(g_MultiBINInfo.dwNumRegions + 1); // +1 to account for this packet.
continue;
}
else {
// make sure it is a standard BIN file.
if (memcmp (hdr, "B000FF/x0A", BL_HDRSIG_SIZE)) {
EdbgOutputDebugString ("/r/nThis is not a .BIN file %x %x %x %x %x %x %x/r/n",
hdr[0], hdr[1], hdr[2], hdr[3], hdr[4], hdr[5], hdr[6]);
HALT (BLERR_MAGIC);
return FALSE;
}//确认为BIN文件
}
// read image start/length
if (!OEMReadData (sizeof (DWORD), (LPBYTE) &dwImageStart)
|| !OEMReadData (sizeof (DWORD), (LPBYTE) &dwImageLength)) {
EdbgOutputDebugString ("Unable to read image start/length/r/n");
HALT (BLERR_MAGIC);
return FALSE;
}//读取镜像文件的运行地址和长度
// if this is a single-bin download, record the bin file information and notify the OEM.
if (!g_MultiBINInfo.dwNumRegions) {
g_MultiBINInfo.dwNumRegions = 1;
g_MultiBINInfo.Region[0].dwRegionStart = dwImageStart;
g_MultiBINInfo.Region[0].dwRegionLength = dwImageLength;
// provide the region information to the OEM.
if (g_pOEMMultiBINNotify) {
g_pOEMMultiBINNotify((const PMultiBINInfo)&g_MultiBINInfo);
}//可选模块,与用户交互使用
}
// give the OEM a chance to verify memory
if (g_pOEMVerifyMemory && !g_pOEMVerifyMemory (dwImageStart, dwImageLength)) {
EdbgOutputDebugString ("!OEMVERIFYMEMORY: Invalid image/r/n");
HALT (BLERR_OEMVERIFY);
return FALSE;
}//确认下载类型
// check for flash image. Start erasing if it is.
if ((fIsFlash = OEMIsFlashAddr (dwImageStart))
&& !OEMStartEraseFlash (dwImageStart, dwImageLength)) {
EdbgOutputDebugString ("Invalid Flash Address/Length/r/n");
HALT (BLERR_FLASHADDR);
return FALSE;
}//这断代码可选用在一些用户想要实现的东西上
// read records (start with address, length, and checksum)
while (OEMReadData (sizeof (DWORD), (LPBYTE) &dwRecAddr)
&& OEMReadData (sizeof (DWORD), (LPBYTE) &dwRecLen)
&& OEMReadData (sizeof (DWORD), (LPBYTE) &dwRecChk)) {
//开始读取record
RETAILMSG(1, (_T("DownloadImage RecAddr: 0x%x/r/n"), dwRecAddr));
RETAILMSG(1, (_T("DownloadImage RecLen: 0x%x/r/n"), dwRecLen));
RETAILMSG(1, (_T("DownloadImage RecChk: 0x%x/r/n"), dwRecChk));
// check for last record
if (!dwRecAddr && !dwRecChk) {
// if this is the kernel region, update launch address
if (IsKernelRegion(dwImageStart, dwImageLength)) {
*pdwImageStart = dwImageStart;
*pdwImageLength = dwImageLength;
*pdwLaunchAddr = dwRecLen;
//检测是不是最后一条record,如果是将跳转地址赋给launch
RETAILMSG(1, (_T("dwImageStart : 0x%x/r/n"), dwImageStart));
RETAILMSG(1, (_T("dwImageLength: 0x%x/r/n"), dwImageLength));
RETAILMSG(1, (_T("LaunchAddr : 0x%x/r/n"), dwRecLen));
}
// write to flash if it's flash image
if (fIsFlash) {
// finish the flash erase
if (!OEMFinishEraseFlash ()) {
HALT (BLERR_FLASH_ERASE);
return FALSE;
}
// Before writing the image to flash, optionally check the image signature.
if (g_pOEMCheckSignature)
{
if (!g_pOEMCheckSignature(dwImageStart, g_dwROMOffset, *pdwLaunchAddr, TRUE))
HALT(BLERR_WHQL_SIGNATURE);
}
}
// On to the next (possible) BIN file...
break;
}
// map the record address (FLASH data is cached, for example)
lpDest = OEMMapMemAddr (dwImageStart, dwRecAddr);
//获得目标地址
// read data block
if (!OEMReadData (dwRecLen, lpDest)) {
EdbgOutputDebugString ("****** Data record %d corrupted, ABORT!!! ******/r/n", nPkgNum);
HALT (BLERR_CORRUPTED_DATA);
return FALSE;
}//读取该记录的数据
if (!VerifyChecksum (dwRecLen, lpDest, dwRecChk)) {
EdbgOutputDebugString ("****** Checksum failure on record %d, ABORT!!! ******/r/n", nPkgNum);
HALT (BLERR_CHECKSUM);
return FALSE;
}//CRC和效验
// Look for ROMHDR to compute ROM offset. NOTE: romimage guarantees that the record containing
// the TOC signature and pointer will always come before the record that contains the ROMHDR contents.
if (dwRecLen == sizeof(ROMHDR) && (*(LPDWORD) OEMMapMemAddr(dwImageStart, dwImageStart + ROM_SIGNATURE_OFFSET) == ROM_SIGNATURE))//检测TOC记录
{
DWORD dwTempOffset = (dwRecAddr - *(LPDWORD)OEMMapMemAddr(dwImageStart, dwImageStart + ROM_SIGNATURE_OFFSET + sizeof(ULONG)));
//找到TOC并且标记出偏移
ROMHDR *pROMHdr = (ROMHDR *)lpDest;
// Check to make sure this record really contains the ROMHDR.
if ((pROMHdr->physfirst == (dwImageStart - dwTempOffset)) &&
(pROMHdr->physlast == (dwImageStart - dwTempOffset + dwImageLength)) &&
(DWORD)(HIWORD(pROMHdr->dllfirst << 16) <= pROMHdr->dlllast) &&
(DWORD)(LOWORD(pROMHdr->dllfirst << 16) <= pROMHdr->dlllast))
{
g_dwROMOffset = dwTempOffset;
EdbgOutputDebugString("rom_offset=0x%x./r/n", g_dwROMOffset);
}
}//这块代码实际是验证TOC的一些信息
// verify partial checksum
OEMShowProgress (nPkgNum ++);
if (fIsFlash) {
OEMContinueEraseFlash ();
}
}
}//下载完成
while (--nNumRegions);
if (fIsFlash) {
nNumRegions = (BYTE)g_MultiBINInfo.dwNumRegions;
while (nNumRegions--) {
if (!OEMWriteFlash (g_MultiBINInfo.Region[nNumRegions].dwRegionStart, g_MultiBINInfo.Region[nNumRegions].dwRegionLength)) {
HALT (BLERR_FLASH_WRITE);
return FALSE;
}
}
}
return TRUE;
}
这个函数整体实际也不是很难理解,但是用户可以在里面加入一些自己需要的模块就会使得其变得复杂,还有一个就是里面那个OEMReadData()函数是需要用户自己完成的,这个与你自己的下载硬件以及媒介有关系,在我的BOOT里面我选用的是USB下载方法,下面就将自己的这块代码贴出来与大家共享(我也是在前人的基础上自己修改的)。
BOOL OEMReadData (DWORD cbData, LPBYTE pbData)
{
if ( g_bUSBDownload == FALSE )
{
return EbootEtherReadData(cbData, pbData);
}
else
{
return UbootReadData(cbData, pbData);
}
}
从代码中可以看出实际调用了一个模块,而我选用的是USB,所以看下面的原型:
BOOL UbootReadData(DWORD cbData, LPBYTE pbData)
{
volatile USBD_GLOBALS *usbdShMem = (USBD_GLOBALS *)(DRIVER_GLOBALS_PHYSICAL_MEMORY_START);
volatile INTreg *s2440INT = (INTreg *)INT_BASE;
volatile DMAreg *v_pDMAregs = (DMAreg *)DMA_BASE;
unsigned int temp;
int i;
//初始化USB的一些寄存器地址


Loop:
if ( downPtIndex > readPtIndex + cbData )
{
memcpy(pbData, readPtIndex, cbData);
readPtIndex += cbData;
//下载先于读取的时候开始读取数据
}
else if (downPtIndex == DMABUFFER)
{
while (downPtIndex == DMABUFFER) {}; // first 64 bytes, get interrupt mode.
//前64字节用中断读取,详细看后面的中断处理
if ( readPtIndex == DMABUFFER )
{
readPtIndex += 8;
memcpy(pbData, readPtIndex, cbData);
readPtIndex += cbData;
//读取下载中缓存中的数据,这里首先第一组数据必须小于64字节
}
s2440INT->rSRCPND = BIT_USBD;
if (s2440INT->rINTPND & BIT_USBD) s2440INT->rINTPND = BIT_USBD;
s2440INT->rINTMSK |= BIT_USBD; // USB Interrupt disable.
//USB中断关掉
// read data with DMA operation.
s2440INT->rSRCPND = BIT_DMA2;
if (s2440INT->rINTPND & BIT_DMA2) s2440INT->rINTPND = BIT_DMA2;
s2440INT->rINTMSK &= ~BIT_DMA2; // DMA Interrupt enable.
//启动DMA中断
pUSBCtrlAddr->INDEX.index=3;
CLR_EP3_OUT_PKT_READY();
ConfigEp3DmaMode(downPtIndex,0x80000);
//配置DMA模式
v_pDMAregs->rDIDST2=(downPtIndex+0x80000); //for 1st autoreload.
v_pDMAregs->rDIDSTC2=(1<<2)|(0<<1)|(0<<0);
v_pDMAregs->rDCON2=v_pDMAregs->rDCON2&~(0xfffff)|(0x80000);
//第一次DMA重载
while(rEP3_DMA_TTC<0xfffff)
{
pUSBCtrlAddr->EP3DTL.ep3_ttl_l = 0xff;
pUSBCtrlAddr->EP3DTM.ep3_ttl_m = 0xff;
pUSBCtrlAddr->EP3DTH.ep3_ttl_h = 0x0f;
}
}
else
{
temp = rEP3_DMA_TTC;
for (i = 0; i < 60000; i++ )
{
}
if ( temp == rEP3_DMA_TTC )
{
downPtIndex += ((unsigned int)0xfffff - (unsigned int)rEP3_DMA_TTC);
}
goto Loop;
}
return TRUE;
}
#pragma optimize ("",on)
这断代码整体是很简单,但是也存在几个问题大家需要注意一下。代码首先用中断读取数据,详细过程看下面函数:
void Ep3Handler(void)
{
int fifoCnt;
volatile INTreg *s2440INT = (INTreg *)INT_BASE;
pUSBCtrlAddr->INDEX.index=3;
if(pUSBCtrlAddr->OCSR1.out_pkt_rdy)//中断标记
{
fifoCnt=pUSBCtrlAddr->OFCR1.out_cnt_low;//读取计数值
downPt = (LPBYTE)(downPtIndex);//一定要转换
RdPktEp3((U8 *)downPt,fifoCnt);
downPtIndex += 64;
s2440INT->rINTMSK |= BIT_USBD; // USB Interrupt disable.
return;
}
//I think that EPO_SENT_STALL will not be set to 1.
if(pUSBCtrlAddr->OCSR1.sent_stall)
{
CLR_EP3_SENT_STALL();
return;
}
}
后面配置DMA并且开启DMA中断,当第一次DMA读取数据完成以后(即CURR_TC == 0),进去DMA中断再次配置相关设置。这个过程比较简单,但是细心的读者会发现这段代码在一些很细致的地方写的很精致,如在配置完DMA读取的时候,代码中多写了一次装载信息v_pDMAregs->rDIDST2=(downPtIndex+0x80000); //for 1st autoreload.。这样会使得DMA和CPU同时工作将目标地址提前写进去。然后等第一次DMA结束后中断设置和DMA加载同时进行提高运行效率。
在实际中这里的代码有很多很值得研究的地方,比如细心的读者就会发现这里的DMA和MEMSET函数用的都是物理地址,而有些地方用的却是虚拟地址。这样写是为了代码的可扩展性。但是为什么是正确的呢?这里我就先不说明了,可给你更多自己思考的空间。如果有读者要进行探讨我可以email给他答案,实际只要细心这个问题很简单,顺便提一句,实际这些代码都是经过多次改写和慎密优化过的。所以代码只有精简实现功能强大才能优秀。

6)OEMLaunch()

下面我们来研究一下这里最后一个也很关键的函数,这个函数实现了内核的开始运行以及烧写到FLASH的过程。先来看原型:
void OEMLaunch(DWORD dwImageStart, DWORD dwImageLength, DWORD dwLaunchAddr, const ROMHDR *pRomHdr)
{
EDBG_OS_CONFIG_DATA *pCfgData;
EDBG_ADDR EshellHostAddr = {0,0,0};
EdbgOutputDebugString("::OEMLaunch, ImageStart:0x%x, ImageLength:0x%x, LaunchAddr:0x%x/r/n",
dwImageStart, dwImageLength, dwLaunchAddr);
// If the user requested an image be stored on media, do so now. For multiple RAM BIN files, we need to map
// its RAM address to a flash address - the image base address offset in RAM is maintained in flash.

// Remember kernel launch address or recall stored address if this download didn't provide one
// (i.e., we didn't download the kernel region).
EdbgOutputDebugString ( "/r/nto_g_pBootCfg->ConfigFlags =%x/r/n",g_pBootCfg->ConfigFlags);
if (g_bDownloadImage && (g_pBootCfg->ConfigFlags & TARGET_TYPE_NAND))
{
if (dwImageStart && dwImageLength)//需要写入FLASH的标记,如果设置为0就不用烧写FLASH
{
g_pTOC->id[g_dwTocEntry].dwLoadAddress = dwImageStart;
g_pTOC->id[g_dwTocEntry].dwTtlSectors = FILE_TO_SECTOR_SIZE(dwImageLength);
}//在这里更新TOC(BLOCK),以便在镜像从FLASH启动的时候调用
EdbgOutputDebugString ( "/r/ng_ImageType_to =%x/r/n",g_ImageType);
switch ( g_ImageType ) {
case IMAGE_TYPE_LOADER:
EdbgOutputDebugString("OEMLaunch: IMAGE_TYPE_LOADER/r/n");
if ( !WriteRamImageToBootMedia(g_dwTocEntry) ) {
RETAILMSG(1, (TEXT("OEMLaunch ERROR: Failed to write image to boot media./r/n")));
SPIN_FOREVER;
}
break;
case IMAGE_TYPE_RAMIMAGE:
EdbgOutputDebugString("OEMLaunch: IMAGE_TYPE_RAMIMAGE/r/n");
if ( !WriteRamImageToBootMedia(g_dwTocEntry) ) {
RETAILMSG(1, (TEXT("OEMLaunch ERROR: Failed to write image to boot media./r/n")));
SPIN_FOREVER;
}
break;
case (IMAGE_TYPE_RAMIMAGE|IMAGE_TYPE_BINFS|IMAGE_TYPE_MXIP):
case (IMAGE_TYPE_RAMIMAGE|IMAGE_TYPE_BINFS):
EdbgOutputDebugString("OEMLaunch: (%s|IMAGE_TYPE_BINFS)/r/n",
(g_ImageType & IMAGE_TYPE_MXIP) ? "IMAGE_TYPE_MXIP" : "IMAGE_TYPE_RAMIMAGE");
if ( !WriteRegionsToBootMedia(dwImageStart, dwImageLength, dwLaunchAddr) ) {
//调用函数将下载镜像写入flash,注意这里写入的nb0,而不是bin文件
EdbgOutputDebugString("WARNING: OEMLaunch: Failed to store BinFS regions to boot media./r/n");
SPIN_FOREVER;
}
break;
default:
EdbgOutputDebugString("OEMLaunch ERROR: unknown image type: 0x%x /r/n",
g_pTOC->id[g_dwTocEntry].dwImageType);
SPIN_FOREVER;
}
}
// Remember kernel launch address or recall stored address if this download didn't provide one (i.e., we didn't download the kernel region).
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();
}//如果TOC的内核运行地址和下载的不一致,更新运行地址
else
{
dwLaunchAddr= g_pTOC->id[g_dwTocEntry].dwJumpAddress;
EdbgOutputDebugString("INFO: using TOC[%d] dwJumpAddress: 0x%x/r/n", g_dwTocEntry, dwLaunchAddr);
}
// Our Launch function takes Physical address, so we need to convert it
// to physical address
dwLaunchAddr = ToPhysicalAddr(dwLaunchAddr);//下面的启动函数只认识物理地址,所以转换地址
EdbgOutputDebugString("/r/n::: Physical Launch Address: 0x%Xh/r/n",dwLaunchAddr);
Launch(dwLaunchAddr);
//启动内核
// never returned
SPIN_FOREVER;
}
从上面的注释你不难发现这个函数其实功能也相当单一,首先检测是不是需要将内核写入磁盘,如果需要先更新TOC,然后写入磁盘,最后开始运行内核。
在这里首先接受两个简单函数,一个地址转换,另一个跳转。这两个函数虽然简单,但是在整个ARM编程中很具有代表性,所以在这里需要提一下:
DWORD ToPhysicalAddr(DWORD add)
{
padd = add - vir_start + phy_start;
return padd;
}//这里代码不是这样的,我是为了清晰将关键的部分提取出来了,其实也简单。ARM中无论汇编还是C其实现的思想就是这样,是没法变的。
下面是Launch()原型:
TEXTAREA
LEAF_ENTRY Launch
ldr r2, = PhysicalStart
ldr r3, = (VIR_RAM_START - PHY_RAM_START)
sub r2, r2, r3//计算PhysicalStart的物理地址
mov r1, #0x0070 ; Disable MMU
mcr p15, 0, r1, c1, c0, 0
nop//关MMU并且进入PhysicalStart
mov pc, r2 ; Jump to PStart
nop
; MMU & caches now disabled.
PhysicalStart
mov r2, #0
mcr p15, 0, r2, c8, c7, 0 ; Flush the TLB
mov pc, r0 ; Jump to program we are launching.
//启动内核,注意这里MMU启动与否都没有关系,因为内核还需要初始化一次内核
mov pc, lr
END

接下来我们讨论最后一个棘手的问题就是WriteRegionsToBootMedia()函数,写入内核到FLASH的BINFS分区。这个函数比较繁琐,所以将它的说明放在了附录中。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: