eboot加载NK.nb0的详细流程

CE5.0 - eboot烧写NK.nb0的详细流程

nk.nb0首先通过umon下载到DDR中,然后执行烧写操作,烧写到flash上.
PLATFORM\SMDK2440A\Src\Bootloader\Eboot\main.c
==>BootloaderMain
==>OEMPlatformInit  =>  MainMenu()从串口打印menu选择菜单
==>DownloadImage
        dwImageStart   =  *pdwImageStart    = 0x80001000; //0x80001000 & 0x8C200000;                    
        dwImageLength =  *pdwImageLength = 0x1500000;  // 21M 它给固定死了,而且仅仅21M,所以应该根据自己的nk.nb0的大小进行修改[luther.gliethttp]
        *pdwLaunchAddr   = 0x8002C794;// lanch地址也是固定的
            显示menu,根据选择烧写相应的文件,比如输入3表示烧写
        // Nk.nb0
        case '3':        
            EdbgOutputDebugString ("Nk.nb0 chosed...\r\n");    
            dwImageStart   =  *pdwImageStart = 0x80001000;         
             dwImageLength =  *pdwImageLength = 0x1500000;  
            *pdwLaunchAddr   = 0x8002c794;
            g_ImageType = IMAGE_TYPE_RAMIMAGE;//选择将要烧写的文件为Nk.nb0
        goto len;//接着接收用户输入的image文件大小[luther.gliethttp]

         mid:
                if (!g_DownloadManifest.dwNumRegions)//这是第一次调用g_DownloadManifest结构体,所以一定等于0
                {
                    g_DownloadManifest.dwNumRegions             = 1;//region总数为1
                    g_DownloadManifest.Region[0].dwRegionStart  = dwImageStart;//起始地址为nk.nb0加载地址,也是umon下载地址[luther.gliethttp]
                    g_DownloadManifest.Region[0].dwRegionLength = dwImageLength;//nk.nb0文件长度

                    // Provide the download manifest to the OEM.
                    //
                    if (g_pOEMMultiBINNotify)
                    {
//在OEMDebugInit中g_pOEMMultiBINNotify = OEMMultiBINNotify;进行了赋值.
                        g_pOEMMultiBINNotify((PDownloadManifest)&g_DownloadManifest);//仅定义1 region
                    }
                }
==>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));//ok,将BINinfo信息转储到全局变量g_BINRegionInfo中,以便其它单元引用到我们的nk.nb0这个image足够信息[luther.gliethttp]

    OALMSG(TRUE, (TEXT("-----------------------------------------------------\r\n")));
    OALMSG(OAL_FUNC, (TEXT("_OEMMultiBINNotify.\r\n")));
}
==>if (OEMMapMemAddr (dwImageStart, dwImageStart + ROM_SIGNATURE_OFFSET) == ROM_SIGNATURE) 即nk.nb0的第0x40偏移处应该为0x43454345
        // Check for pTOC signature ("CECE") here, after image in place
        if (*(LPDWORD) OEMMapMemAddr (dwImageStart, dwImageStart + ROM_SIGNATURE_OFFSET) == ROM_SIGNATURE)
        {
//#define ROM_SIGNATURE_OFFSET   0x40         // Offset from the image's physfirst address to the ROM signature.
//#define ROM_SIGNATURE          0x43454345
//#define ROM_TOC_POINTER_OFFSET 0x44         // Offset from the image's physfirst address to the TOC pointer.
//#define ROM_TOC_OFFSET_OFFSET  0x48         // Offset from the image's physfirst address to the TOC offset (from physfirst).
//使用winhex在nk.nb0获得如下数据
//00000040 : 45 43 45 43 C8 ED 90 81 C8 DD 90 01 00 00 00 00
//所以dwImageStart + ROM_SIGNATURE_OFFSET + sizeof(ULONG)大小等于0x40+4=0x44所以对应的内容为0x8190EDC8
//在上面的DownloadImage中可以看到dwImageStart = 0x80001000;
//0x8190EDC8为TOC指针虚拟地址值,0x190DDC8为其对应的物理地址偏移
//所以0x8190EDC8 - 0x80001000 = 0x190DDC8
//其实这个差值就存储在了0x48偏移地址处[luther.gliethttp]
//dwpToc = *(LPDWORD)0x8190EDC8;取出该虚拟地址处的数据,即偏移0x190DDC8处的4字节数据
//使用winhex获得数据为
//E3 01 DA 01
//即:0x1DA01E3
//所以最后dwpToc = 0x1DA01E3 + g_dwROMOffset;//这里g_dwROMOffset因为没有地方对其赋值,所以其值为默认值0
//typedef struct ROMHDR {
//    ULONG   dllfirst;               // first DLL address
//    ULONG   dlllast;                // last DLL address
//    ULONG   physfirst;              // first physical address
//    ULONG   physlast;               // highest physical address
//    ULONG   nummods;                // number of TOCentry's
//    ULONG   ulRAMStart;             // start of RAM
//    ULONG   ulRAMFree;              // start of RAM free space
//    ULONG   ulRAMEnd;               // end of RAM
//    ULONG   ulCopyEntries;          // number of copy section entries
//    ULONG   ulCopyOffset;           // offset to copy section
//    ULONG   ulProfileLen;           // length of PROFentries RAM
//    ULONG   ulProfileOffset;        // offset to PROFentries
//    ULONG   numfiles;               // number of FILES
//    ULONG   ulKernelFlags;          // optional kernel flags from ROMFLAGS .bib config option
//    ULONG   ulFSRamPercent;         // Percentage of RAM used for filesystem
//                                        // from FSRAMPERCENT .bib config option
//                                        // byte 0 = #4K chunks/Mbyte of RAM for filesystem 0-2Mbytes 0-255
//                                        // byte 1 = #4K chunks/Mbyte of RAM for filesystem 2-4Mbytes 0-255
//                                        // byte 2 = #4K chunks/Mbyte of RAM for filesystem 4-6Mbytes 0-255
//                                        // byte 3 = #4K chunks/Mbyte of RAM for filesystem > 6Mbytes 0-255
//
//    ULONG   ulDrivglobStart;        // device driver global starting address
//    ULONG   ulDrivglobLen;          // device driver global length
//    USHORT  usCPUType;              // CPU (machine) Type
//    USHORT  usMiscFlags;            // Miscellaneous flags
//    PVOID   pExtensions;            // pointer to ROM Header extensions
//    ULONG   ulTrackingStart;        // tracking memory starting address
//    ULONG   ulTrackingLen;          // tracking memory ending address
//} ROMHDR;
//#define TOCentry_dwFileAttributes 0
//#define TOCentry_ftTime         4
//#define TOCentry_lpszFileSize   12
//#define TOCentry_lpszFileName   16
//#define TOCentry_ulE32Offset    20
//#define TOCentry_ulO32Offset    24
//#define TOCentry_ulLoadOffset   28
//#define SIZEOF_TOCentry         32
//typedef struct TOCentry {           // MODULE BIB section structure
//    DWORD dwFileAttributes;
//    FILETIME ftTime;
//    DWORD nFileSize;
//    LPSTR   lpszFileName;
//    ULONG   ulE32Offset;            // Offset to E32 structure
//    ULONG   ulO32Offset;            // Offset to O32 structure
//    ULONG   ulLoadOffset;           // MODULE load buffer offset
//} TOCentry, *LPTOCentry;
//0190DDC0 : 57 EF 50 00 58 00 00 00 E3 01 DA 01 00 00 00 02
//0190DDD0 : 00 10 00 80 94 0D 91 81 AD 00 00 00 00 00 20 8C
//0190DDE0 : 00 90 22 8C 00 00 00 8E 01 00 00 00 C0 3D C2 80
//0190DDF0 : 00 00 00 00 00 00 00 00 5A 00 00 00 02 00 00 00
//0190DE00 : 80 80 80 80 00 00 00 00 00 00 00 00 C2 01 02 00
//0190DE10 : 10 32 00 80 00 00 00 00 00 00 00 00 07 00 00 00 //07 00 00 00 开始为TOC,一共占32字节空间
//0190DE20 : D4 A3 9A 28 AB 1E C7 01 00 B0 06 00 F8 1F C5 80 //该4字节F8 1F C5 80为TOCentry_lpszFileName虚拟地址,其偏移值为0x80C51FF8 - 0x80001000 = 0xC50FF8
//0190DE30 : 84 CF 58 80 9C CF 1F 80 00 10 00 80 07 10 00 00 //从该07 10 00 00 00开始为下一个TOC,一共占32字节空间
//0190DE40 : 3A F3 8F 3C AB 1E C7 01 00 84 08 00 F4 5F 4D 80
//0190DE50 : 6C 5F C9 80 A0 0F C2 80 00 90 09 80 07 00 00 00
//从0x0190DDC8开始
//dllfirst  = 0x01DA01E3
//dlllast   = 0x20000000
//physfirst = 0x80001000
//physlast  = 0x81910D94
//nummods   = 0x000000AD
//ulRAMStart= 0x8C200000
//ulRAMFree = 0x8C229000
//ulRAMEnd  = 0x8E000000
//ulCopyEntries = 0x00000001
//ulCopyOffset  = 0x80C23DC0
//ulProfileLen  = 0x00000000
//ulProfileOffset   = 0x00000000
//numfiles  = 0x0000005A
//ulKernelFlags = 0x00000002
//ulFSRamPercent= 0x80808080
//ulDrivglobStart   = 0x00000000
//ulDrivglobLen = 0x00000000
//usCPUType = 0x01C2
//usMiscFlags   = 0x0002
//pExtensions   = 0x80003210
//ulTrackingStart   = 0x00000000
//ulTrackingLen = 0x00000000
//紧跟ROMHDR其后的为nummods个TOCentry结构体
            dwpToc = *(LPDWORD) OEMMapMemAddr (dwImageStart, dwImageStart + ROM_SIGNATURE_OFFSET + sizeof(ULONG));//OEMMapMemAddr直接返回dwImageStart + ROM_SIGNATURE_OFFSET + sizeof(ULONG))数值,即0x8190EDC8这个虚拟地址处的内容,0x8190EDC8虚拟地址对应的物理偏移值为0x190DDC8,该值位于0x48偏移处[luther.gliethttp]
            // need to map the content again since the pointer is going to be in a fixup address
            dwpToc = (DWORD) OEMMapMemAddr (dwImageStart, dwpToc + g_dwROMOffset);

            EdbgOutputDebugString ("ROMHDR at Address %Xh\r\n", dwImageStart + ROM_SIGNATURE_OFFSET + sizeof (DWORD)); // right after signature
        }
case BL_JUMP:
==>OEMLaunch
==>switch (g_ImageType)
    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 Smart Media.\r\n")));
                    goto CleanUp;
                }

                if (dwLaunchAddr && (g_pTOC->id[g_dwTocEntry].dwJumpAddress != dwLaunchAddr))
                {
                    //*pdwLaunchAddr   = 0x8002C794;// 我们的lanch地址也是固定的
                    g_pTOC->id[g_dwTocEntry].dwJumpAddress = dwLaunchAddr;//修改跳转地址到位于block块1区的TOC数据
                    if ( !TOC_Write() ) {//回写TOC到block块1
                        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;
    //然后就执行Lanch()登陆.
    // Jump to downloaded image (use the physical address since we'll be turning the MMU off)...
    //
    dwPhysLaunchAddr = (DWORD)OALVAtoPA((void *)dwLaunchAddr);//根据位于PLATFORM\SMDK2440A\Src\Inc\oemaddrtab_cfg.inc下的g_oalAddressTable定义的转换表,将虚拟地址转为对应的物理地址
    OALMSG(TRUE, (TEXT("INFO: OEMLaunch: Jumping to Physical Address 0x%Xh (Virtual Address 0x%Xh)...\r\n\r\n\r\n"), dwPhysLaunchAddr, dwLaunchAddr));//打印该log信息

    // Jump...
    //
    Launch(dwPhysLaunchAddr);//执行PLATFORM\SMDK2440A\Src\Bootloader\Eboot\util.s|32| LEAF_ENTRY Launch中定义的Lanuch函数,代码见下面[lutehr.gliethttp]


/*
    @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, 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: device doesn'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 extension descriptor.
    // This region, if found, details the number, start, and size of each BIN region.
    // 这里我们只有nk.nb0一个region需要烧写
    for (nCount = 0, dwNumExts = 0 ; (nCount < g_BINRegionInfo.dwNumRegions); nCount++)
    {
        // Does this region contain nk.exe and an extension pointer?
        //我们这里返回的数值就是0x80003210,对其分析见后面[luther.gliethttp]
        //对应的nk.nb0偏移值为0x80003210 - 0x80001000 = 0x2210
        pExt = (EXTENSION *)GetKernelExtPointer(g_BINRegionInfo.Region[nCount].dwRegionStart,
                                                g_BINRegionInfo.Region[nCount].dwRegionLength );
        if ( pExt != NULL)
        {
//#define PID_LENGTH 10
//typedef struct ROMPID {
//  union{
//    DWORD dwPID[PID_LENGTH];        // PID 可见该union一共40字节数据
//    struct{
//      char  name[(PID_LENGTH - 4) * sizeof(DWORD)];
//      DWORD type;
//      PVOID pdata;
//      DWORD length;
//      DWORD reserved;
//    };
//  };
//  PVOID pNextExt;                 // pointer to next extension if any
//} ROMPID, EXTENSION;
//所以一共占用了44字节数据
//0x2210 ~ 0x2210 + 44空间全部为0,所以不会找到"chain information"
            // 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).
    {
        //我们的下载程序将执行到这里[luther.gliethttp]
        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.
    //
    //为nk.nb0建立主分区,管理(IMAGE_START_BLOCK+1)*PAGES_PER_BLOCK开始的扇区,管理大小为SECTOR_TO_BLOCK_SIZE(FILE_TO_SECTOR_SIZE(dwBINFSPartLength))*PAGES_PER_BLOCK
    //将该分区所有信息登记到了MBR中,hPart为申请到的主分区表指针[luther.gliethttp]
    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)?
    //
    for (nCount = 0, dwStoreOffset = 0; nCount < g_BINRegionInfo.dwNumRegions ; nCount++)
    {
        DWORD dwRegionStart  = (DWORD)OEMMapMemAddr(0, g_BINRegionInfo.Region[nCount].dwRegionStart);//我们这里就是nk.nb0下载地址0x32001000对应的虚拟地址为0x80001000

        DWORD dwRegionLength = g_BINRegionInfo.Region[nCount].dwRegionLength;

        // Media byte offset where image region is stored.
        dwStoreOffset += nCount ? dwMaxRegionLength[nCount-1] : 0;//如果是MultiBin,那么将一个挨一个的紧凑存储,其紧凑度由dwStoreOffset偏移指针控制,这个偏移指针数值就是这里所谓的
        //逻辑地址[luther.gliethttp]

        // Set the file pointer (byte indexing) to the correct offset for this particular region.
        //
        if ( !BP_SetDataPointer(hPart, dwStoreOffset) )//从该分区的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) )//将数据顺序写到dwStoreOffset(以字节为单位)开始的地址后,长度dwRegionLength,代码见后面.[luther.gliethttp]
        {
            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 ((g_pTOC->id[g_dwTocEntry].dwLoadAddress == g_BINRegionInfo.Region[nCount].dwRegionStart) &&
             g_pTOC->id[g_dwTocEntry].dwTtlSectors == FILE_TO_SECTOR_SIZE(dwRegionLength) )
        {
            //我们的符合该条件,所以执行了下面语句[luther.gliethttp]
            g_pTOC->id[g_dwTocEntry].dwStoreOffset = dwStoreOffset;//对期望Toc进行写操作,那么保存它的存储逻辑地址(以字节为单位)[luther.gliethttp]
            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);
            //在BP_WriteData()中对g_dwLastWrittenLoc进行了更新,
            //g_dwLastWrittenLoc = dwBlock * g_dwDataBytesPerBlock + dwOffsetBlock;//记录现在写的是第几个字节(物理地址)[luther.gliethttp]
            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
    //
    //为系统创建扩展分区,
    //1.eboot.nb0主分区
    //2.nk.nb0主分区
    //3.扩展分区[luther.gliethttp]
    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);//好了nk.nb0对应的MBR也创建了,nk.nb0也写进去了,对应的位于1块的TOC数据也更新了,扩展分区也创建了,工作完成了,返回ok.[luther.gliethttp]
}

/*
    @func   PVOID | GetKernelExtPointer | Locates the kernel region's extension area pointer.
    @rdesc  Pointer to the kernel's extension area.
    @comm    
    @xref   
*/
PVOID GetKernelExtPointer(DWORD dwRegionStart, DWORD dwRegionLength)
{
    DWORD dwCacheAddress = 0;
    ROMHDR *pROMHeader;
    DWORD  dwNumModules = 0;
    TOCentry *pTOC;

    if (dwRegionStart == 0 || dwRegionLength == 0)
        return(NULL);

    if (*(LPDWORD) OEMMapMemAddr (dwRegionStart, dwRegionStart + ROM_SIGNATURE_OFFSET) != ROM_SIGNATURE)//首先检查该region的ROM标志值是否正确[luther.gliethttp]
        return NULL;


    // A pointer to the ROMHDR structure lives just past the ROM_SIGNATURE (which is a longword value).  Note that
    // this pointer is remapped since it might be a flash address (image destined for flash), but is actually cached
    // in RAM.
    //
    dwCacheAddress = *(LPDWORD) OEMMapMemAddr (dwRegionStart, dwRegionStart + ROM_SIGNATURE_OFFSET + sizeof(ULONG));//我们这里就是0x44偏移处的值
    pROMHeader     = (ROMHDR *) OEMMapMemAddr (dwRegionStart, dwCacheAddress);
    
//从0x0190DDC8开始,pROMHeader = 0x0190DDC8偏移处对应的虚拟地址0x8190EDC8,通过使用winhex分析后数据如下:
//dllfirst  = 0x01DA01E3
//dlllast   = 0x20000000
//physfirst = 0x80001000
//physlast  = 0x81910D94
//nummods   = 0x000000AD
//ulRAMStart= 0x8C200000
//ulRAMFree = 0x8C229000
//ulRAMEnd  = 0x8E000000
//ulCopyEntries = 0x00000001
//ulCopyOffset  = 0x80C23DC0
//ulProfileLen  = 0x00000000
//ulProfileOffset   = 0x00000000
//numfiles  = 0x0000005A
//ulKernelFlags = 0x00000002
//ulFSRamPercent= 0x80808080
//ulDrivglobStart   = 0x00000000
//ulDrivglobLen = 0x00000000
//usCPUType = 0x01C2
//usMiscFlags   = 0x0002
//pExtensions   = 0x80003210
//ulTrackingStart   = 0x00000000
//ulTrackingLen = 0x00000000
//紧跟ROMHDR其后的为nummods个TOCentry结构体
//
//00C50FF0 : 74 65 00 00 DC 5F 03 00 6E 6B 2E 65 78 65 00 00  这里6E 6B 2E 65 78 65就是nk.exe
//所以可见在nk.nb0中含有nk.exe字符串的偏移位置为C50FF8
//对应的虚拟的地址为0x80001000 + C50FF8 = 0x80C51FF8其在小段存储模式内存中的十六进制数据为F8 1F C5 80
//使用BC3查找该十六进制串
//就在0190DE2B偏移处.
    // Make sure sure are some modules in the table of contents.
    //
    if ((dwNumModules = pROMHeader->nummods) == 0)
        return NULL;

    // Locate the table of contents and search for the kernel executable and the TOC immediately follows the ROMHDR.
    //
    pTOC = (TOCentry *)(pROMHeader + 1);
    

    while(dwNumModules--) {        
        char* pFileName = OEMMapMemAddr(dwRegionStart, (DWORD)pTOC->lpszFileName);
//改名字在我编译出的nk.nb0的0190DE2B偏移处,刚好为第1个TOC
        if (!strcmp((const char *)pFileName, "nk.exe")) {//找到名字为"nk.exe"的TOC,我们可以在这里打印出所有的TOC名字来进一步了解CE内核结构[luther.gliethttp]
            return ((PVOID)(pROMHeader->pExtensions));//ok,这个该image是合法的nk.nb0,返回pROMHeader->pExtensions数据,这里就是0x80003210
        }
    
        ++pTOC;    
    }
    return NULL;//否则NULL
}

BOOL BP_SetDataPointer (HANDLE hPartition, DWORD dwAddress)
{
    if (hPartition == INVALID_HANDLE_VALUE)
        return FALSE;

    RETAILMSG(1,(TEXT("BP_SetDataPointer at 0x%x\r\n"), dwAddress));
    
    PPARTSTATE pPartState = (PPARTSTATE) hPartition;

    if (dwAddress >= pPartState->pPartEntry->Part_TotalSectors * g_FlashInfo.wDataBytesPerSector)
        return FALSE;
/*
typedef struct _PARTSTATE {
        PPARTENTRY  pPartEntry;
        DWORD         dwDataPointer;        // Pointer to where next read and write will occur
} PARTSTATE, *PPARTSTATE;
*/
    pPartState->dwDataPointer = dwAddress;//对该分区执行读写的逻辑扇区地址,也就是偏移地址[luther.gliethttp]
    return TRUE;
   
}

//将pbBuffer中dwLength个字节数据写到hPartition分区,写入该分区的逻辑扇区地址在BP_SetDataPointer()中已经进行了设置[luther.gliethttp]
BOOL BP_WriteData(HANDLE hPartition, LPBYTE pbBuffer, DWORD dwLength)
{
    if (hPartition == INVALID_HANDLE_VALUE)
        return FALSE;
    
    DWORD dwNumBlocks;
    PPARTSTATE pPartState = (PPARTSTATE) hPartition;
    DWORD dwNextPtrValue = pPartState->dwDataPointer + dwLength;

    RETAILMSG (1, (TEXT("WriteData: Start = 0x%x, Length = 0x%x.\r\n"), pPartState->dwDataPointer, dwLength));

    if (!pbBuffer || !g_pbBlock || dwLength == 0) {
        RETAILMSG(1,(TEXT("BP_WriteData Fails.  pbBuffer = 0x%x, g_pbBlock = 0x%x, dwLength = 0x%x\r\n"), pbBuffer, g_pbBlock, dwLength));
        return(FALSE);
    }

    // Check to make sure buffer size is within limits of partition
    // 检查写入该分区的数据是否超过该扇区所管理的扇区总数[luther.gliethttp]
    if (((dwNextPtrValue - 1) / g_FlashInfo.wDataBytesPerSector) >= pPartState->pPartEntry->Part_TotalSectors) {
        RETAILMSG (1, (TEXT("WriteData: trying to write past end of partition.\r\n")));
        return FALSE;
    }

    // Get the starting physical block
    // 获取dwDataPointer写入/读取指针所在的块号,经过Log2Phys转换之后dwBlock就是实际的物理块号了[luther.gliethttp]
    DWORD dwBlock = Log2Phys (pPartState->dwDataPointer / g_FlashInfo.wDataBytesPerSector + pPartState->pPartEntry->Part_StartSector) / g_FlashInfo.wSectorsPerBlock;
    //计算以该主分区起始地址为基址的块号[luther.gliethttp]
    DWORD dwOffsetBlock = (pPartState->dwDataPointer + pPartState->pPartEntry->Part_StartSector * g_FlashInfo.wDataBytesPerSector) % g_dwDataBytesPerBlock;//计算待写的指针为该块中第几个字节

    // Update the global indicating last written physical address.  Global variable is used by the caller.
    g_dwLastWrittenLoc = dwBlock * g_dwDataBytesPerBlock + dwOffsetBlock;//记录现在写的是第几个字节(物理地址)[luther.gliethttp]

    // If current pointer is not on a block boundary, copy bytes up to the first block boundary
    if (dwOffsetBlock)
    {
        //待写入sector非block开始边界,那么调整为整block,所以先写入非整block的头部数据,之后数据就是整block开始了,这样对大数据读写可以达到加速效果[luther.gliethttp]
        if (!ReadBlock(dwBlock, g_pbBlock, g_pSectorInfoBuf)) {
            RETAILMSG (1, (TEXT("WriteData: failed to read block (0x%x).\r\n"), dwBlock));
            return(FALSE);
        }
        
        DWORD dwNumBytesWrite = g_dwDataBytesPerBlock - dwOffsetBlock;//需要向该block写入的多少个字节数据,从dwOffsetBlock开始写[luther.gliethttp]
        if (dwNumBytesWrite > dwLength)//写入数据大小不会超过该block.
            dwNumBytesWrite = dwLength;

        memcpy(g_pbBlock + dwOffsetBlock, pbBuffer, dwNumBytesWrite);//1.拷贝数据  

        if (!FMD_EraseBlock(dwBlock)) {//2.擦
            RETAILMSG (1, (TEXT("WriteData: failed to erase block (0x%x).\r\n"), dwBlock));
            return FALSE;
        }

        if (!WriteBlock(dwBlock, g_pbBlock, g_pSectorInfoBuf)) {//3.写
            RETAILMSG (1, (TEXT("WriteData: failed to write block (0x%x).\r\n"), dwBlock));
            return(FALSE);
        }
        
        dwLength -= dwNumBytesWrite;//长度调整
        pbBuffer += dwNumBytesWrite;//将数据调整到整块边界[luther.gliethttp]
        dwBlock++;
    }
    //好了,经过上面调整之后,数据指针已经调整为下一个block的边界值了[ltuher.gliethttp]
    // Compute number of blocks.
    dwNumBlocks = (dwLength / g_dwDataBytesPerBlock);
    
    while (dwNumBlocks--)
    {
        // If the block is marked bad, skip to next block.  Note that the assumption in our error checking
        // is that any truely bad block will be marked either by the factory during production or will be marked
        // during the erase and write verification phases.  If anything other than a bad block fails ECC correction
        // in this routine, it's fatal.
        if (IS_BLOCK_UNUSABLE(dwBlock))//该物理块是否损坏
        {
            ++dwBlock;//继续下一块
            //表示我们跳过该物理块,所以应该++dwNumBlocks;恢复
            ++dwNumBlocks;        // Compensate for fact that we didn't write any blocks.
            continue;
        }

        if (!ReadBlock(dwBlock, NULL, g_pSectorInfoBuf)) {
            RETAILMSG (1, (TEXT("WriteData: failed to read block (0x%x).\r\n"), dwBlock));
            return(FALSE);
        }

        if (!FMD_EraseBlock(dwBlock)) {
            RETAILMSG (1, (TEXT("WriteData: failed to erase block (0x%x).\r\n"), dwBlock));
            return FALSE;
        }

        if (!WriteBlock(dwBlock, pbBuffer, g_pSectorInfoBuf)) {
            RETAILMSG (1, (TEXT("WriteData: failed to write block (0x%x).\r\n"), dwBlock));
            return(FALSE);
        }

        ++dwBlock;
        pbBuffer += g_dwDataBytesPerBlock;//ok,开始写吧,开始循环吧[luther.gliethttp]
    }

    DWORD dwNumExtraBytes = (dwLength % g_dwDataBytesPerBlock);//看看收尾是否还需要向下一个block开头部分写些数据
    if (dwNumExtraBytes)
    {
        //还有数据需要写
        // Skip bad blocks
        while (IS_BLOCK_UNUSABLE(dwBlock))
        {
            dwBlock++;//找到紧邻的下一个好块[luther.gliethttp]
            if (dwBlock >= g_FlashInfo.dwNumBlocks)
            {
                // This should never happen since partition has already been created
                RETAILMSG (1, (TEXT("WriteData: corrupt partition.  Reformat flash.\r\n")));                
                return FALSE;
            }
            
        }
        
        if (!ReadBlock(dwBlock, g_pbBlock, g_pSectorInfoBuf)) {
            RETAILMSG (1, (TEXT("WriteData: failed to read block (0x%x).\r\n"), dwBlock));
            return(FALSE);
        }
        
        memcpy(g_pbBlock, pbBuffer, dwNumExtraBytes);   //向该block开头追加未写完的跨块的数据[luther.gliethttp]

        if (!FMD_EraseBlock(dwBlock)) {
            RETAILMSG (1, (TEXT("WriteData: failed to erase block (0x%x).\r\n"), dwBlock));
            return FALSE;
        }

        if (!WriteBlock(dwBlock, g_pbBlock, g_pSectorInfoBuf)) {
            RETAILMSG (1, (TEXT("WriteData: failed to write block (0x%x).\r\n"), dwBlock));
            return(FALSE);
        }
        
    }

    pPartState->dwDataPointer = dwNextPtrValue;//该分区写一次数据发生写入操作时的物理地址,以字节为单位进行计算[luther.gliethttp]
    return(TRUE);
}

static DWORD Log2Phys (DWORD dwLogSector) //该dwLogSector数值已经是加过其在主分区的主分区地址了
{
    // Determine logical block number
    DWORD dwLogBlock = dwLogSector / g_FlashInfo.wSectorsPerBlock;

    // Start searching at the MBR block
    if (g_dwMBRSectorNum == INVALID_ADDR) {
        RETAILMSG(1, (TEXT("Log2Phys: MBR sector number is invalid.\r\n")));        
        return INVALID_ADDR;
    }
    DWORD dwPhysBlock = g_dwMBRSectorNum / g_FlashInfo.wSectorsPerBlock;//g_dwMBRSectorNum为第一个18块开始之后的第一个好块,对该原因的分析见上面[luther.gliethttp]
    //这就是和主分区地址相加之后dwLogSector的物理基地址了[luther.gliethttp]

    if (dwLogBlock >= g_FlashInfo.dwNumBlocks)
        return INVALID_ADDR;

    // The physical block will be the number of logical blocks plus the number of bad blocks
    // starting from the MBR block.
    while (dwLogBlock--) {//找到dwLogBlock块对应的物理块,坏块将只是简单的对物理块地址进行加1操作,简单的略过,之后计算出最终的物理地址[luther.gliethttp]
        dwPhysBlock++;
        while (IS_BLOCK_UNUSABLE (dwPhysBlock) && dwPhysBlock < g_FlashInfo.dwNumBlocks) {
            dwPhysBlock++;
        }
        if (dwPhysBlock >= g_FlashInfo.dwNumBlocks)
            return INVALID_ADDR;
    }

    //打log数据
    RETAILMSG(1, (TEXT("Log2Phys: Logical 0x%x -> Physical 0x%x\r\n"), dwLogSector, dwPhysBlock * g_FlashInfo.wSectorsPerBlock + (dwLogSector % g_FlashInfo.wSectorsPerBlock)));
    //MBR
    return dwPhysBlock * g_FlashInfo.wSectorsPerBlock + (dwLogSector % g_FlashInfo.wSectorsPerBlock);//返回dwLogSector所在的物理sector地址
    //wince对nand的坏块不做任何维护性处理,只是简单的跳过,这和linux下存在BBT(Bad Block Table)坏块表不一样[luther.gliethttp]
}

//根据位于PLATFORM\SMDK2440A\Src\Inc\oemaddrtab_cfg.inc下的g_oalAddressTable定义的转换表,将虚拟地址转为对应的物理地址
UINT32 OALVAtoPA(VOID *pVA)
{
    OAL_ADDRESS_TABLE *pTable = g_oalAddressTable;
    UINT32 va = (UINT32)pVA;
    UINT32 pa = 0;

    OALMSG(OAL_MEMORY&&OAL_FUNC, (L"+OALVAtoPA(0x%08x)\r\n", pVA));

    // Virtual address must be in CACHED or UNCACHED regions.
    if (va < 0x80000000 || va >= 0xC0000000) {
        OALMSG(OAL_ERROR, (
            L"ERROR:OALVAtoPA: invalid virtual address 0x%08x\r\n", pVA
        ));
        goto cleanUp;
    }

    // Address must be cached, as entries in OEMAddressTable are cached address.
    va = va&~OAL_MEMORY_CACHE_BIT;

    // Search the table for address range
    while (pTable->size != 0) {
        if (va >= pTable->CA && va <= pTable->CA + (pTable->size << 20) - 1) {
            break;
        }
        pTable++;
    }

    // If address table entry is valid compute the PA
    if (pTable->size != 0) pa = pTable->PA + va - pTable->CA;

cleanUp:
    // Indicate physical address
    OALMSG(OAL_MEMORY&&OAL_FUNC, (L"-OALVAtoPA(pa = 0x%x)\r\n", pa));
    return pa;
}

位于PLATFORM\SMDK2440A\Src\Bootloader\Eboot\util.s汇编中
    INCLUDE kxarm.h

PHY_RAM_START    EQU    0x30000000
VIR_RAM_START    EQU    0x8c000000

    TEXTAREA

    LEAF_ENTRY Launch

    ldr    r2, = PhysicalStart //获得PhysicalStart虚拟地址值,在8c038000~范围,可在boot.bib中看到eboot的编译地址[luther.gliethttp]
    ldr     r3, = (VIR_RAM_START - PHY_RAM_START)//计算虚拟地址和物理地址的差值

    sub     r2, r2, r3 //计算虚拟地址PhysicalStart对应的物理地址值[luther.gliethttp]

    mov     r1, #0x0070             ; Disable MMU
    mcr     p15, 0, r1, c1, c0, 0   //禁用MMU
    nop
    mov     pc, r2                  ; Jump to PStart//MMU禁止,所以跳转到PhysicalStart对应的物理地址继续执行[luther.gliethttp]
    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. //跳转到dwLaunchAddr登陆地址,之后的进一步内核解压加载等工作就完全由ce内核自身封闭完成了[luther.gliethttp]
posted @ 2009-11-14 16:15  田大头  阅读(2000)  评论(0编辑  收藏  举报