www.pudn.com > iMx31_WCE600.rar > nand.c


//----------------------------------------------------------------------------- 
// 
//  Use of this source code is subject to the terms of the Microsoft end-user 
//  license agreement (EULA) under which you licensed this SOFTWARE PRODUCT. 
//  If you did not accept the terms of the EULA, you are not authorized to use 
//  this source code. For a copy of the EULA, please see the LICENSE.RTF on 
//  your install media. 
// 
//----------------------------------------------------------------------------- 
// 
//  Copyright (C) 2004-2007, Freescale Semiconductor, Inc. All Rights Reserved. 
//  THIS SOURCE CODE, AND ITS USE AND DISTRIBUTION, IS SUBJECT TO THE TERMS 
//  AND CONDITIONS OF THE APPLICABLE LICENSE AGREEMENT 
// 
//----------------------------------------------------------------------------- 
// 
//  File:  nand.c 
// 
//  Contains BOOT NAND flash support functions. 
// 
//----------------------------------------------------------------------------- 
#include "bsp.h" 
#include "loader.h" 
#pragma warning(push) 
#pragma warning(disable: 4115) 
#include  
#pragma warning(pop) 
 
//----------------------------------------------------------------------------- 
// External Functions 
 
 
//----------------------------------------------------------------------------- 
// External Variables 
extern BOOL g_bNandExist; 
 
 
//----------------------------------------------------------------------------- 
// Defines 
#define     NANDFC_BOOT_SIZE        (2*1024) 
 
 
//----------------------------------------------------------------------------- 
// Types 
 
 
//----------------------------------------------------------------------------- 
// Global Variables 
BYTE sectorBuf[NANDFC_BOOT_SIZE]; 
static FlashInfo g_flashInfo; 
 
 
//----------------------------------------------------------------------------- 
// Local Variables 
 
 
//----------------------------------------------------------------------------- 
// Local Functions 
 
 
//----------------------------------------------------------------------------- 
// 
//  Function:  NANDWriteXldr 
// 
//  This function writes to NAND flash memory the XLDR image stored  
//  in the RAM file cache area. 
// 
//  Parameters: 
//      dwStartAddr  
//          [in] Address in flash memory where the start of the downloaded  
//          XLDR image is to be written. 
// 
//      dwLength  
//          [in] Length of the XLDR image, in bytes, to be written to flash 
//          memory.             
// 
//  Returns: 
//      TRUE indicates success. FALSE indicates failure. 
// 
//----------------------------------------------------------------------------- 
BOOL NANDWriteXldr(DWORD dwStartAddr, DWORD dwLength) 
{ 
    FlashInfo flashInfo; 
    LPBYTE pSectorBuf, pImage; 
    SectorInfo sectorInfo; 
    SECTOR_ADDR sectorAddr, endSectorAddr; 
 
    // Check for NAND device availability 
    // 
    if (!g_bNandExist) 
    { 
        EdbgOutputDebugString("WARNING: NAND device doesn't exist - unable to store image.\r\n"); 
        return(FALSE); 
    } 
 
    EdbgOutputDebugString("INFO: Writing XLDR image to NAND (please wait)...\r\n"); 
 
    if (!FMD_GetInfo(&flashInfo)) 
    { 
        EdbgOutputDebugString("ERROR: Unable to get NAND flash information.\r\n"); 
        return(FALSE); 
    } 
 
    // XLDR is placed in block #0 of the NAND device.   
 
    // Block #0 is always good, so just erase it 
    if (!FMD_EraseBlock(0)) 
    { 
        EdbgOutputDebugString("ERROR: Unable to erase NAND flash block #0\r\n"); 
        return(FALSE); 
    } 
 
    // Get cached image location 
    pImage = OEMMapMemAddr(dwStartAddr, dwStartAddr); 
 
    // If image is larger than 4K, this must be xldr.bin produced by 
    // ROMIMAGE 
    if (dwLength > 0x1000) 
    { 
        // ROMIMAGE adds 4K page at the beginning of the image. 
        // We will flash the first 2K bytes that appear after this 4K page. 
        // 
        pImage += 0x1000; 
        dwLength -= 0x1000; 
    } 
 
    // Make sure XLDR length does not exceed size that can be supported by NANDFC (2KB) 
    if (dwLength > NANDFC_BOOT_SIZE) 
    { 
        EdbgOutputDebugString("ERROR: XLDR exceeds 2KByte\r\n"); 
        return(FALSE); 
    } 
 
    // Fill unused space with 0xFF 
    memset(pImage + dwLength, 0xFF, (NANDFC_BOOT_SIZE) - dwLength); 
 
    EdbgOutputDebugString("INFO: Using XLDR image from flash cache address 0x%x, size = %d\r\n", pImage, dwLength); 
 
    sectorInfo.dwReserved1 = 0xFFFFFFFF; 
    sectorInfo.bOEMReserved = 0x00; 
    sectorInfo.bBadBlock = 0xFF;     
    sectorInfo.wReserved2 = 0xFFFF; 
     
    endSectorAddr = (NANDFC_BOOT_SIZE / flashInfo.wDataBytesPerSector); 
     
    // Write XLDR to NAND flash 
    pSectorBuf = pImage; 
    for (sectorAddr = 0; sectorAddr < endSectorAddr; sectorAddr++) 
    { 
        if (!FMD_WriteSector(sectorAddr, pSectorBuf, §orInfo, 1)) 
        { 
            EdbgOutputDebugString("ERROR: Failed to update XLDR.\r\n"); 
            return(FALSE); 
        } 
 
        pSectorBuf += flashInfo.wDataBytesPerSector; 
    } 
 
    // Read XLDR from NAND flash to verify contents 
    pSectorBuf = pImage + NANDFC_BOOT_SIZE; 
    for (sectorAddr = 0; sectorAddr < endSectorAddr; sectorAddr++) 
    { 
        if (!FMD_ReadSector(sectorAddr, pSectorBuf, §orInfo, 1)) 
        { 
            EdbgOutputDebugString("ERROR: Failed to verify XLDR.\r\n"); 
            return(FALSE); 
        } 
 
        pSectorBuf += flashInfo.wDataBytesPerSector; 
    } 
 
    if (memcmp(pImage, pImage + NANDFC_BOOT_SIZE, NANDFC_BOOT_SIZE) != 0) 
    { 
        EdbgOutputDebugString("ERROR: Failed to verify XLDR.\r\n"); 
    } 
     
 
    EdbgOutputDebugString("INFO: Update of XLDR completed successfully.\r\n"); 
 
     
    return(TRUE); 
 
} 
 
 
//----------------------------------------------------------------------------- 
// 
//  Function:  NANDWriteBoot 
// 
//  This function writes to NAND flash memory the Boot image stored  
//  in the RAM file cache area. 
// 
//  Parameters: 
//      dwStartAddr  
//          [in] Address in flash memory where the start of the downloaded  
//          Boot image is to be written. 
// 
//      dwLength  
//          [in] Length of the Boot image, in bytes, to be written to flash 
//          memory.             
// 
//  Returns: 
//      TRUE indicates success. FALSE indicates failure. 
// 
//----------------------------------------------------------------------------- 
BOOL NANDWriteBoot(DWORD dwStartAddr, DWORD dwLength) 
{ 
    FlashInfo flashInfo; 
    LPBYTE pSectorBuf, pImage; 
    SectorInfo sectorInfo; 
    BLOCK_ID blockID, startBlockID, endBlockID; 
    SECTOR_ADDR sectorAddr, startSectorAddr, endSectorAddr; 
 
    // Check for NAND device availability 
    // 
    if (!g_bNandExist) 
    { 
        EdbgOutputDebugString("WARNING: NAND device doesn't exist - unable to store image.\r\n"); 
        return(FALSE); 
    } 
 
    EdbgOutputDebugString("INFO: Writing Boot image to NAND (please wait)...\r\n"); 
 
    if (!FMD_GetInfo(&flashInfo)) 
    { 
        EdbgOutputDebugString("ERROR: Unable to get NAND flash information.\r\n"); 
        return(FALSE); 
    } 
 
    // Make sure Boot length does not exceed reserved NAND size 
    if (dwLength > IMAGE_BOOT_BOOTIMAGE_NAND_SIZE) 
    { 
        EdbgOutputDebugString("ERROR: Boot size exceeds reserved NAND region (size = 0x%x)\r\n", dwLength); 
        return(FALSE); 
    } 
 
    // Calculate the physical block range for the EBOOT image 
    startBlockID = IMAGE_BOOT_BOOTIMAGE_NAND_OFFSET / flashInfo.dwBytesPerBlock; 
    endBlockID = startBlockID + (IMAGE_BOOT_BOOTIMAGE_NAND_SIZE / flashInfo.dwBytesPerBlock); 
     
    EdbgOutputDebugString("INFO: Erasing NAND flash blocks [0x%x - 0x%x].\r\n", startBlockID, endBlockID); 
     
    // Erase range of NAND blocks reserved for EBOOT 
    for (blockID = startBlockID; blockID < endBlockID; blockID++) 
    { 
        // Skip bad blocks 
        if (FMD_GetBlockStatus(blockID) == BLOCK_STATUS_BAD) 
        { 
            EdbgOutputDebugString("INFO: Found bad NAND flash block [0x%x].\r\n", blockID); 
            continue; 
        } 
 
        // Erase the block... 
        if (!FMD_EraseBlock(blockID)) 
        { 
            EdbgOutputDebugString("ERROR: Unable to erase NAND flash block [0x%x].\r\n", blockID); 
            return(FALSE); 
        } 
    } 
 
    // Get cached image location 
    pImage = OEMMapMemAddr(dwStartAddr, dwStartAddr); 
 
    // Fill unused space with 0xFF 
    memset(pImage + dwLength, 0xFF, (IMAGE_BOOT_BOOTIMAGE_NAND_SIZE) - dwLength); 
 
    EdbgOutputDebugString("INFO: Programming EBOOT/SBOOT image from flash cache address 0x%x, size = %d\r\n", pImage, dwLength); 
 
    sectorInfo.dwReserved1 = 0xFFFFFFFF; 
    sectorInfo.bOEMReserved = 0x00; 
    sectorInfo.bBadBlock = 0xFF;     
    sectorInfo.wReserved2 = 0xFFFF; 
     
    // Write EBOOT to NAND flash 
    pSectorBuf = pImage; 
 
    for (blockID = startBlockID; blockID < endBlockID; blockID++) 
    {         
        // Skip bad blocks 
        if (FMD_GetBlockStatus(blockID) == BLOCK_STATUS_BAD) 
        { 
            EdbgOutputDebugString("INFO: Found bad NAND flash block [0x%x].\r\n", blockID); 
            continue; 
        } 
 
        // Compute sector address based on current physical block 
        startSectorAddr = blockID * flashInfo.wSectorsPerBlock; 
        endSectorAddr = startSectorAddr + flashInfo.wSectorsPerBlock; 
         
        for (sectorAddr = startSectorAddr; sectorAddr < endSectorAddr; sectorAddr++) 
        { 
            if (!FMD_WriteSector(sectorAddr, pSectorBuf, §orInfo, 1)) 
            { 
                EdbgOutputDebugString("ERROR: Failed to update EBOOT/SBOOT.\r\n"); 
                return(FALSE); 
            } 
 
            pSectorBuf += flashInfo.wDataBytesPerSector; 
        } 
    } 
     
    // Read EBOOT from NAND flash to verify contents 
    pSectorBuf = pImage + IMAGE_BOOT_BOOTIMAGE_NAND_SIZE; 
 
    for (blockID = startBlockID; blockID < endBlockID ; blockID++) 
    {         
        // Skip bad blocks 
        if (FMD_GetBlockStatus(blockID) == BLOCK_STATUS_BAD) 
        { 
            EdbgOutputDebugString("INFO: Found bad NAND flash block [0x%x].\r\n", blockID); 
            continue; 
        } 
 
        // Compute sector address based on current physical block 
        startSectorAddr = blockID * flashInfo.wSectorsPerBlock; 
        endSectorAddr = startSectorAddr + flashInfo.wSectorsPerBlock; 
         
        for (sectorAddr = startSectorAddr; sectorAddr < endSectorAddr; sectorAddr++) 
        { 
            if (!FMD_ReadSector(sectorAddr, pSectorBuf, §orInfo, 1)) 
            { 
                EdbgOutputDebugString("ERROR: Failed to update EBOOT/SBOOT.\r\n"); 
                return(FALSE); 
            } 
 
            pSectorBuf += flashInfo.wDataBytesPerSector; 
        } 
    } 
 
    EdbgOutputDebugString("INFO: Verifying image.\r\n"); 
 
    if (memcmp(pImage, pImage + IMAGE_BOOT_BOOTIMAGE_NAND_SIZE, IMAGE_BOOT_BOOTIMAGE_NAND_SIZE) != 0) 
    { 
        EdbgOutputDebugString("ERROR: Failed to verify EBOOT/SBOOT.\r\n"); 
    } 
     
 
    EdbgOutputDebugString("INFO: Update of EBOOT/SBOOT completed successfully.\r\n"); 
 
     
    return(TRUE); 
 
} 
 
//------------------------------------------------------------------------------ 
// 
//  Function:  NANDWriteIPL 
// 
//  N/A 
// 
//  Parameters: 
//      None. 
// 
//  Returns: 
//      TRUE indicates success. FALSE indicates failure. 
// 
//----------------------------------------------------------------------------- 
BOOL NANDWriteIPL(DWORD dwStartAddr, DWORD dwLength) 
{ 
    FlashInfo flashInfo; 
    LPBYTE pSectorBuf, pImage; 
    SectorInfo sectorInfo; 
    BLOCK_ID blockID, startBlockID, endBlockID; 
    SECTOR_ADDR sectorAddr, startSectorAddr, endSectorAddr; 
 
    // Check for NAND device availability 
    // 
    if (!g_bNandExist) 
    { 
        EdbgOutputDebugString("WARNING: NAND device doesn't exist - unable to store image.\r\n"); 
        return(FALSE); 
    } 
 
    EdbgOutputDebugString("INFO: Writing IPL image to NAND (please wait)...\r\n"); 
 
    if (!FMD_GetInfo(&flashInfo)) 
    { 
        EdbgOutputDebugString("ERROR: Unable to get NAND flash information.\r\n"); 
        return(FALSE); 
    } 
 
    // Make sure IPL length does not exceed reserved NAND size 
    if (dwLength > IMAGE_BOOT_IPLIMAGE_NAND_SIZE) 
    { 
        EdbgOutputDebugString("ERROR: IPL size exceeds reserved NAND region (size = 0x%x)\r\n", dwLength); 
        return(FALSE); 
    } 
 
    // Calculate the physical block range for the IPL image 
    startBlockID = IMAGE_BOOT_IPLIMAGE_NAND_OFFSET / flashInfo.dwBytesPerBlock; 
    endBlockID = startBlockID + (IMAGE_BOOT_IPLIMAGE_NAND_SIZE / flashInfo.dwBytesPerBlock); 
     
    EdbgOutputDebugString("INFO: Erasing NAND flash blocks [0x%x - 0x%x].\r\n", startBlockID, endBlockID); 
     
    // Erase range of NAND blocks reserved for IPL 
    for (blockID = startBlockID; blockID < endBlockID; blockID++) 
    { 
        // Skip bad blocks 
        if (FMD_GetBlockStatus(blockID) == BLOCK_STATUS_BAD) 
        { 
            EdbgOutputDebugString("INFO: Found bad NAND flash block [0x%x].\r\n", blockID); 
            continue; 
        } 
 
        // Erase the block... 
        if (!FMD_EraseBlock(blockID)) 
        { 
            EdbgOutputDebugString("ERROR: Unable to erase NAND flash block [0x%x].\r\n", blockID); 
            return(FALSE); 
        } 
    } 
 
    // Get cached image location 
    pImage = OEMMapMemAddr(dwStartAddr, dwStartAddr); 
 
    // Fill unused space with 0xFF 
    memset(pImage + dwLength, 0xFF, (IMAGE_BOOT_IPLIMAGE_NAND_SIZE) - dwLength); 
 
    EdbgOutputDebugString("INFO: Programming IPL image from flash cache address 0x%x, size = %d\r\n", pImage, dwLength); 
 
    sectorInfo.dwReserved1 = 0xFFFFFFFF; 
    sectorInfo.bOEMReserved = 0x00; 
    sectorInfo.bBadBlock = 0xFF;     
    sectorInfo.wReserved2 = 0xFFFF; 
     
    // Write IPL to NAND flash 
    pSectorBuf = pImage; 
 
    for (blockID = startBlockID; blockID < endBlockID; blockID++) 
    {         
        // Skip bad blocks 
        if (FMD_GetBlockStatus(blockID) == BLOCK_STATUS_BAD) 
        { 
            EdbgOutputDebugString("INFO: Found bad NAND flash block [0x%x].\r\n", blockID); 
            continue; 
        } 
 
        // Compute sector address based on current physical block 
        startSectorAddr = blockID * flashInfo.wSectorsPerBlock; 
        endSectorAddr = startSectorAddr + flashInfo.wSectorsPerBlock; 
         
        for (sectorAddr = startSectorAddr; sectorAddr < endSectorAddr; sectorAddr++) 
        { 
            if (!FMD_WriteSector(sectorAddr, pSectorBuf, §orInfo, 1)) 
            { 
                EdbgOutputDebugString("ERROR: Failed to update IPL.\r\n"); 
                return(FALSE); 
            } 
 
            pSectorBuf += flashInfo.wDataBytesPerSector; 
        } 
    } 
     
    // Read IPL from NAND flash to verify contents 
    pSectorBuf = pImage + IMAGE_BOOT_IPLIMAGE_NAND_SIZE; 
 
    for (blockID = startBlockID; blockID < endBlockID ; blockID++) 
    {         
        // Skip bad blocks 
        if (FMD_GetBlockStatus(blockID) == BLOCK_STATUS_BAD) 
        { 
            EdbgOutputDebugString("INFO: Found bad NAND flash block [0x%x].\r\n", blockID); 
            continue; 
        } 
 
        // Compute sector address based on current physical block 
        startSectorAddr = blockID * flashInfo.wSectorsPerBlock; 
        endSectorAddr = startSectorAddr + flashInfo.wSectorsPerBlock; 
         
        for (sectorAddr = startSectorAddr; sectorAddr < endSectorAddr; sectorAddr++) 
        { 
            if (!FMD_ReadSector(sectorAddr, pSectorBuf, §orInfo, 1)) 
            { 
                EdbgOutputDebugString("ERROR: Failed to update IPL.\r\n"); 
                return(FALSE); 
            } 
 
            pSectorBuf += flashInfo.wDataBytesPerSector; 
        } 
    } 
 
    EdbgOutputDebugString("INFO: Verifying image.\r\n"); 
 
    if (memcmp(pImage, pImage + IMAGE_BOOT_IPLIMAGE_NAND_SIZE, IMAGE_BOOT_IPLIMAGE_NAND_SIZE) != 0) 
    { 
        EdbgOutputDebugString("ERROR: Failed to verify IPL.\r\n"); 
    } 
     
    EdbgOutputDebugString("INFO: Update of IPL completed successfully.\r\n"); 
 
    return(TRUE); 
} 
 
//----------------------------------------------------------------------------- 
// 
//  Function:  NANDWriteNK 
// 
//  This function writes to NAND flash memory the OS image stored  
//  in the RAM file cache area. 
// 
//  Parameters: 
//      dwStartAddr  
//          [in] Address in flash memory where the start of the downloaded  
//          OS image is to be written. 
// 
//      dwLength  
//          [in] Length of the OS image, in bytes, to be written to flash 
//          memory.             
// 
//  Returns: 
//      TRUE indicates success. FALSE indicates failure. 
// 
//----------------------------------------------------------------------------- 
BOOL NANDWriteNK(DWORD dwStartAddr, DWORD dwLength) 
{ 
    FlashInfo flashInfo; 
    LPBYTE pSectorBuf, pImage; 
    SectorInfo sectorInfo; 
    BLOCK_ID blockID, startBlockID, endBlockID; 
    SECTOR_ADDR sectorAddr, startSectorAddr, endSectorAddr; 
    UINT32 percentComplete, lastPercentComplete; 
 
 
    // Check for NAND device availability 
    // 
    if (!g_bNandExist) 
    { 
        EdbgOutputDebugString("WARNING: NAND device doesn't exist - unable to store image.\r\n"); 
        return(FALSE); 
    } 
 
    EdbgOutputDebugString("INFO: Writing NK image to NAND (please wait)...\r\n"); 
 
    if (!FMD_GetInfo(&flashInfo)) 
    { 
        EdbgOutputDebugString("ERROR: Unable to get NAND flash information.\r\n"); 
        return(FALSE); 
    } 
 
    // Make sure NK length does not exceed reserved NAND size 
    if (dwLength > IMAGE_BOOT_NKIMAGE_NAND_SIZE) 
    { 
        EdbgOutputDebugString("ERROR: NK size exceeds reserved NAND region (size = 0x%x)\r\n", dwLength); 
        return(FALSE); 
    } 
 
    // Calculate the physical block range for the NK image 
    startBlockID = IMAGE_BOOT_NKIMAGE_NAND_OFFSET / flashInfo.dwBytesPerBlock; 
    endBlockID = startBlockID + (IMAGE_BOOT_NKIMAGE_NAND_SIZE / flashInfo.dwBytesPerBlock); 
     
    EdbgOutputDebugString("INFO: Erasing NAND flash blocks [0x%x - 0x%x].\r\n", startBlockID, endBlockID); 
 
    lastPercentComplete = 0; 
     
    // Erase range of NAND blocks reserved for NK 
    for (blockID = startBlockID; blockID < endBlockID; blockID++) 
    { 
        // Skip bad blocks 
        if (FMD_GetBlockStatus(blockID) == BLOCK_STATUS_BAD) 
        { 
            EdbgOutputDebugString("INFO: Found bad NAND flash block [0x%x].\r\n", blockID); 
            continue; 
        } 
 
        // Erase the block... 
        if (!FMD_EraseBlock(blockID)) 
        { 
            EdbgOutputDebugString("ERROR: Unable to erase NAND flash block [0x%x].\r\n", blockID); 
            return(FALSE); 
        } 
    } 
 
    // Get cached image location 
    pImage = OEMMapMemAddr(dwStartAddr, dwStartAddr); 
 
    // Fill unused space with 0xFF 
    memset(pImage + dwLength, 0xFF, (IMAGE_BOOT_NKIMAGE_NAND_SIZE) - dwLength); 
 
    EdbgOutputDebugString("INFO: Programming NK image from flash cache address 0x%x, size = %d\r\n", pImage, dwLength); 
 
    sectorInfo.dwReserved1 = 0xFFFFFFFF; 
    sectorInfo.bOEMReserved = 0x00; 
    sectorInfo.bBadBlock = 0xFF;     
    sectorInfo.wReserved2 = 0xFFFF; 
     
    // Write NK to NAND flash 
    pSectorBuf = pImage; 
    lastPercentComplete = 0; 
 
    for (blockID = startBlockID; blockID < endBlockID; blockID++) 
    {         
        // Skip bad blocks 
        if (FMD_GetBlockStatus(blockID) == BLOCK_STATUS_BAD) 
        { 
            EdbgOutputDebugString("INFO: Found bad NAND flash block [0x%x].\r\n", blockID); 
            continue; 
        } 
 
        // Compute sector address based on current physical block 
        startSectorAddr = blockID * flashInfo.wSectorsPerBlock; 
        endSectorAddr = startSectorAddr + flashInfo.wSectorsPerBlock; 
         
        for (sectorAddr = startSectorAddr; sectorAddr < endSectorAddr; sectorAddr++) 
        { 
            if (!FMD_WriteSector(sectorAddr, pSectorBuf, §orInfo, 1)) 
            { 
                EdbgOutputDebugString("ERROR: Failed to update NK.\r\n"); 
                return(FALSE); 
            } 
 
            pSectorBuf += flashInfo.wDataBytesPerSector; 
        } 
 
        percentComplete =  100 * (blockID - startBlockID + 1) / (endBlockID - startBlockID); 
 
        // If percentage complete has changed, show the progress 
        if (lastPercentComplete != percentComplete) 
        { 
            lastPercentComplete = percentComplete; 
            OEMWriteDebugByte('\r'); 
            EdbgOutputDebugString("INFO: Program is %d%% complete.", percentComplete); 
        } 
 
    } 
     
    EdbgOutputDebugString("\r\nINFO: Reading image in NAND for verification.\r\n"); 
 
    // Read NK from NAND flash to verify contents 
    pSectorBuf = pImage + IMAGE_BOOT_NKIMAGE_NAND_SIZE; 
    lastPercentComplete = 0; 
 
    for (blockID = startBlockID; blockID < endBlockID ; blockID++) 
    {         
        // Skip bad blocks 
        if (FMD_GetBlockStatus(blockID) == BLOCK_STATUS_BAD) 
        { 
            EdbgOutputDebugString("\r\nINFO: Found bad NAND flash block [0x%x].\r\n", blockID); 
            continue; 
        } 
 
        // Compute sector address based on current physical block 
        startSectorAddr = blockID * flashInfo.wSectorsPerBlock; 
        endSectorAddr = startSectorAddr + flashInfo.wSectorsPerBlock; 
         
        for (sectorAddr = startSectorAddr; sectorAddr < endSectorAddr; sectorAddr++) 
        { 
            if (!FMD_ReadSector(sectorAddr, pSectorBuf, §orInfo, 1)) 
            { 
                EdbgOutputDebugString("\r\nERROR: Failed to update NK.\r\n"); 
                return(FALSE); 
            } 
 
            pSectorBuf += flashInfo.wDataBytesPerSector; 
        } 
 
        percentComplete =  100 * (blockID - startBlockID + 1) / (endBlockID - startBlockID); 
 
        // If percentage complete has changed, show the progress 
        if (lastPercentComplete != percentComplete) 
        { 
            lastPercentComplete = percentComplete; 
            OEMWriteDebugByte('\r'); 
            EdbgOutputDebugString("INFO: Read is %d%% complete.", percentComplete); 
        } 
    } 
 
    EdbgOutputDebugString("\nINFO: Verifying image.\r\n"); 
 
    if (memcmp(pImage, pImage + IMAGE_BOOT_NKIMAGE_NAND_SIZE, IMAGE_BOOT_NKIMAGE_NAND_SIZE) != 0) 
    { 
        EdbgOutputDebugString("ERROR: Failed to verify NK.\r\n"); 
    } 
     
 
    EdbgOutputDebugString("INFO: Update of NK completed successfully.\r\n"); 
 
     
    return(TRUE); 
 
} 
 
//------------------------------------------------------------------------------ 
// 
//  Function:  IsSectorEmpty 
// 
//  N/A 
// 
//  Parameters: 
//      None. 
// 
//  Returns: 
//      TRUE indicates success. FALSE indicates failure. 
// 
//----------------------------------------------------------------------------- 
BOOL IsSectorEmpty(UCHAR *pData, ULONG sectorSize, SectorInfo *pSectorInfo) 
{ 
    BOOL rc = FALSE; 
    ULONG idx; 
 
    if (pSectorInfo->dwReserved1 != 0xFFFFFFFF) goto cleanUp; 
    if (pSectorInfo->wReserved2 != 0xFFFF) goto cleanUp; 
    if (pSectorInfo->bOEMReserved != 0xFF) goto cleanUp; 
 
    for (idx = 0; idx < sectorSize; idx++) 
    { 
        if (pData[idx] != 0xFF)  
            goto cleanUp; 
    }       
 
    rc = TRUE; 
 
cleanUp: 
    return rc; 
} 
 
//------------------------------------------------------------------------------ 
// 
//  Function:  NANDStartWriteBinDIO 
// 
//  N/A 
// 
//  Parameters: 
//      None. 
// 
//  Returns: 
//      TRUE indicates success. FALSE indicates failure. 
// 
//----------------------------------------------------------------------------- 
BOOL NANDStartWriteBinDIO(DWORD dwStartAddr, DWORD dwLength) 
{ 
    BLOCK_ID blockID, startBlockID, endBlockID; 
    UINT32 percentComplete, lastPercentComplete = 0; 
     
    // Remove-W4: Warning C4100 workaround 
    UNREFERENCED_PARAMETER(dwStartAddr); 
 
    // Check for NAND device availability 
    // 
    if (!g_bNandExist) 
    { 
        EdbgOutputDebugString("WARNING: NAND device doesn't exist - unable to store image.\r\n"); 
        return(FALSE); 
    } 
 
    EdbgOutputDebugString("INFO: Prepare for writing DIO image to NAND.\r\n"); 
 
    if (!FMD_GetInfo(&g_flashInfo)) 
    { 
        EdbgOutputDebugString("ERROR: Unable to get NAND flash information.\r\n"); 
        return(FALSE); 
    } 
 
    // Make sure DIO image length does not exceed reserved NAND size 
    if (dwLength > IMAGE_BOOT_DIOIMAGE_NAND_SIZE) 
    { 
        EdbgOutputDebugString("ERROR: DIO image size exceeds reserved NAND region (size = 0x%x)\r\n", dwLength); 
        return(FALSE); 
    } 
 
    // Force all blocks in XLDR(128KB), EBOOT(256KB) and IPL(256KB) to be reserved 
    startBlockID = 0; 
    endBlockID = (IMAGE_BOOT_DIOIMAGE_NAND_OFFSET / g_flashInfo.dwBytesPerBlock) - 1; 
    for (blockID = startBlockID; blockID < endBlockID; blockID++) 
    { 
        // Skip bad blocks 
        if (FMD_GetBlockStatus(blockID) == BLOCK_STATUS_BAD) 
        { 
            EdbgOutputDebugString("INFO: Found bad NAND flash block [0x%x].\r\n", blockID); 
            continue; 
        } 
 
        // Set reserved status 
        if ((FMD_GetBlockStatus(blockID) & BLOCK_STATUS_RESERVED) == 0) 
        { 
            EdbgOutputDebugString("INFO: Block [0x%x] not marked as bad or reserved. Force status to reserved.\r\n", blockID); 
 
            if (!FMD_SetBlockStatus(blockID, BLOCK_STATUS_RESERVED)) 
            { 
                EdbgOutputDebugString("ERROR: Block [0x%x] force reserved status failed.\r\n", blockID); 
                return(FALSE); 
            } 
        }         
    } 
 
    // Calculate the physical block range for the DIO image 
    startBlockID = IMAGE_BOOT_DIOIMAGE_NAND_OFFSET / g_flashInfo.dwBytesPerBlock; 
    endBlockID = startBlockID + (IMAGE_BOOT_DIOIMAGE_NAND_SIZE / g_flashInfo.dwBytesPerBlock); 
 
    EdbgOutputDebugString("INFO: Erasing NAND flash blocks [0x%x - 0x%x].\r\n", startBlockID, endBlockID); 
     
    // Erase range of NAND blocks reserved for DIO image 
    for (blockID = startBlockID; blockID < endBlockID; blockID++) 
    { 
        // Skip bad blocks 
        if (FMD_GetBlockStatus(blockID) == BLOCK_STATUS_BAD) 
        { 
            EdbgOutputDebugString("INFO: Found bad NAND flash block [0x%x].\r\n", blockID); 
            continue; 
        } 
 
        // Erase the block... 
        if (!FMD_EraseBlock(blockID)) 
        { 
            EdbgOutputDebugString("ERROR: Unable to erase NAND flash block [0x%x].\r\n", blockID); 
            return(FALSE); 
        } 
 
        percentComplete = 100 * (blockID - startBlockID + 1) / (endBlockID - startBlockID); 
        // If percentage complete has changed, show the progress 
        if (lastPercentComplete != percentComplete) 
        { 
            lastPercentComplete = percentComplete; 
            OEMWriteDebugByte('\r'); 
            EdbgOutputDebugString("INFO: Erase is %d%% complete.", percentComplete); 
        } 
 
    } 
 
    EdbgOutputDebugString("\r\nINFO: Ready to write DIO image ... \r\n"); 
     
    return(TRUE); 
} 
 
//------------------------------------------------------------------------------ 
// 
//  Function:  NANDContinueWriteBinDIO 
// 
//  N/A 
// 
//  Parameters: 
//      None. 
// 
//  Returns: 
//      TRUE indicates success. FALSE indicates failure. 
// 
//----------------------------------------------------------------------------- 
BOOL NANDContinueWriteBinDIO(DWORD dwAddress, BYTE *pbData, DWORD dwSize) 
{ 
    BOOL bOK; 
    BLOCK_ID blockID; 
    SECTOR_ADDR sectorAddr; 
    SectorInfo *pSectorInfo; 
    UINT32 blockSize, sectorSize; 
    UINT32 dwCount; 
    static UINT32 nTimes = 0, dwBadBlock = 0; 
 
    // Check for NAND device availability 
    // 
    if (!g_bNandExist) 
    { 
        EdbgOutputDebugString("WARNING: NAND device doesn't exist - unable to store image.\r\n"); 
        return(FALSE); 
    } 
 
    // First we need to calculate position where to write 
    sectorSize = g_flashInfo.wDataBytesPerSector + sizeof(SectorInfo); 
    blockSize = g_flashInfo.wSectorsPerBlock * sectorSize; 
    blockID = dwAddress / blockSize; 
    sectorAddr = (dwAddress - blockID * blockSize) / sectorSize; 
 
    // Shift block by DIO image region base 
    blockID += (IMAGE_BOOT_DIOIMAGE_NAND_OFFSET / g_flashInfo.dwBytesPerBlock); 
 
    // Skip the number of bad blocks 
    blockID += dwBadBlock; 
 
    // Write record 
    dwCount = 0; 
    while (dwCount < dwSize && blockID < g_flashInfo.dwNumBlocks)  
    { 
        // Skip bad blocks 
        if (FMD_GetBlockStatus(blockID) == BLOCK_STATUS_BAD) 
        { 
            EdbgOutputDebugString("INFO: Found bad NAND flash block [0x%x].\r\n", blockID); 
            blockID++; 
            dwBadBlock++; 
            continue; 
        } 
   
        // Write sectors 
        bOK = TRUE; 
        while (sectorAddr < g_flashInfo.wSectorsPerBlock && dwCount < dwSize)  
        { 
            // First we have to check for empty sectors 
            pSectorInfo = (SectorInfo *)(pbData + dwCount + g_flashInfo.wDataBytesPerSector); 
 
            // Don't write empty sector 
            if (IsSectorEmpty(pbData + dwCount, g_flashInfo.wDataBytesPerSector, pSectorInfo)) 
            { 
                // EdbgOutputDebugString("INFO: Skipping empty sector [0x%x].\r\n", sectorAddr); 
                 
                // Move to next sector 
                dwCount += sectorSize; 
                sectorAddr++; 
                continue; 
            } 
 
            // Clear reserved flag if set 
            pSectorInfo->bOEMReserved |= OEM_BLOCK_RESERVED; 
 
            // Never ever write sector with bad block 
            if (pSectorInfo->bBadBlock != 0xFF) 
            { 
                EdbgOutputDebugString("ERROR: Incorrect or corrupted DIO BIN file - bad block flag set.\r\n"); 
                return(FALSE); 
            } 
 
            // Write sector 
            if ((bOK = FMD_WriteSector(blockID * g_flashInfo.wSectorsPerBlock + sectorAddr,  
                pbData + dwCount, pSectorInfo, 1)) == 0) 
            { 
                EdbgOutputDebugString("ERROR: Writing sector [0x%x] failed.\r\n", sectorAddr); 
                break; 
            } 
 
            // Move to next sector 
            dwCount += sectorSize; 
            sectorAddr++; 
        } 
 
        // When sector write failed, mark block as bad and move back 
        if (!bOK)  
        { 
            EdbgOutputDebugString("WARN: Block [0x%x] / sector [0x%x] write failed, mark block as bad.\r\n", blockID, sectorAddr); 
 
            // First move back 
            dwCount -= sectorAddr * sectorSize; 
            // Mark block as bad 
            FMD_SetBlockStatus(blockID, BLOCK_STATUS_BAD); 
            blockID++; 
            continue; 
        } 
 
        // We are done with block 
        sectorAddr = 0; 
        blockID++; 
    } 
 
    // Progress 
    OEMWriteDebugByte('\r'); 
    switch (nTimes++ % 4) 
    { 
        case 0: 
            OEMWriteDebugByte('\\'); 
            break; 
                 
        case 1: 
            OEMWriteDebugByte('|'); 
            break; 
                 
        case 2: 
            OEMWriteDebugByte('/'); 
            break; 
 
        case 3: 
            OEMWriteDebugByte('-'); 
    } 
 
    // Before we leave erase buffer 
    memset(pbData, 0xFF, dwSize); 
 
    // If we wrote all, we are succesfull 
    return (dwCount >= dwSize); 
} 
 
//------------------------------------------------------------------------------ 
// 
//  Function:  NANDFinishWriteBinDIO 
// 
//  N/A 
// 
//  Parameters: 
//      None. 
// 
//  Returns: 
//      TRUE indicates success. FALSE indicates failure. 
// 
//----------------------------------------------------------------------------- 
BOOL NANDFinishWriteBinDIO() 
{ 
    if (!g_bNandExist) 
    { 
        EdbgOutputDebugString("\r\nINFO: No NAND present!\r\n"); 
        return(FALSE); 
    } 
     
    // Nothing to do... 
    EdbgOutputDebugString("\r\nINFO: Update of DIO image completed successfully.\r\n"); 
 
    return TRUE; 
} 
 
//------------------------------------------------------------------------------ 
// 
//  Function:  NANDLoadIPL 
// 
//  N/A 
// 
//  Parameters: 
//      None. 
// 
//  Returns: 
//      TRUE indicates success. FALSE indicates failure. 
// 
//----------------------------------------------------------------------------- 
BOOL NANDLoadIPL(VOID) 
{ 
    FlashInfo flashInfo; 
    LPBYTE pSectorBuf; 
    SectorInfo sectorInfo; 
    BLOCK_ID blockID, startBlockID, endBlockID; 
    SECTOR_ADDR sectorAddr, startSectorAddr, endSectorAddr; 
 
    // Check for NAND device availability 
    // 
    if (!g_bNandExist) 
    { 
        EdbgOutputDebugString("WARNING: NAND device doesn't exist - unable to read image.\r\n"); 
        return(FALSE); 
    } 
 
    EdbgOutputDebugString("INFO: Reading IPL image from NAND (please wait)...\r\n"); 
 
    if (!FMD_GetInfo(&flashInfo)) 
    { 
        EdbgOutputDebugString("ERROR: Unable to get NAND flash information.\r\n"); 
        return(FALSE); 
    } 
 
    // Calculate the physical block range for the NK image 
    startBlockID = IMAGE_BOOT_IPLIMAGE_NAND_OFFSET / flashInfo.dwBytesPerBlock; 
    endBlockID = startBlockID + (IMAGE_BOOT_IPLIMAGE_NAND_SIZE / flashInfo.dwBytesPerBlock); 
     
    // Set image load address 
    pSectorBuf = (LPBYTE) OALPAtoUA(IMAGE_BOOT_IPLIMAGE_RAM_START); 
 
    EdbgOutputDebugString("INFO: Copying IPL image to RAM address 0x%x\r\n", pSectorBuf); 
     
    // Copy IPL from NAND flash to RAM 
    for (blockID = startBlockID; blockID < endBlockID ; blockID++) 
    {         
        // Skip bad blocks 
        if (FMD_GetBlockStatus(blockID) == BLOCK_STATUS_BAD) 
        { 
            EdbgOutputDebugString("INFO: Found bad NAND flash block [0x%x].\r\n", blockID); 
            continue; 
        } 
 
        // Compute sector address based on current physical block 
        startSectorAddr = blockID * flashInfo.wSectorsPerBlock; 
        endSectorAddr = startSectorAddr + flashInfo.wSectorsPerBlock; 
         
        for (sectorAddr = startSectorAddr; sectorAddr < endSectorAddr; sectorAddr++) 
        { 
            if (!FMD_ReadSector(sectorAddr, pSectorBuf, §orInfo, 1)) 
            { 
                EdbgOutputDebugString("ERROR: Failed to update IPL.\r\n"); 
                return(FALSE); 
            } 
 
            pSectorBuf += flashInfo.wDataBytesPerSector; 
        } 
    } 
 
    EdbgOutputDebugString("INFO: Copy of IPL completed successfully.\r\n"); 
 
    return(TRUE); 
} 
 
 
//----------------------------------------------------------------------------- 
// 
//  Function:  NANDLoadNK 
// 
//  This function loads an OS image from NAND flash memory into RAM for 
//  execution. 
// 
//  Parameters: 
//      None.      
// 
//  Returns: 
//      TRUE indicates success. FALSE indicates failure. 
// 
//----------------------------------------------------------------------------- 
BOOL NANDLoadNK(VOID) 
{ 
    FlashInfo flashInfo; 
    LPBYTE pSectorBuf; 
    SectorInfo sectorInfo; 
    BLOCK_ID blockID, startBlockID, endBlockID; 
    SECTOR_ADDR sectorAddr, startSectorAddr, endSectorAddr; 
    UINT32 percentComplete, lastPercentComplete; 
 
    // Check for NAND device availability 
    // 
    if (!g_bNandExist) 
    { 
        EdbgOutputDebugString("WARNING: NAND device doesn't exist - unable to read image.\r\n"); 
        return(FALSE); 
    } 
 
    EdbgOutputDebugString("INFO: Reading NK image to NAND (please wait)...\r\n"); 
 
    if (!FMD_GetInfo(&flashInfo)) 
    { 
        EdbgOutputDebugString("ERROR: Unable to get NAND flash information.\r\n"); 
        return(FALSE); 
    } 
 
    // Calculate the physical block range for the NK image 
    startBlockID = IMAGE_BOOT_NKIMAGE_NAND_OFFSET / flashInfo.dwBytesPerBlock; 
    endBlockID = startBlockID + (IMAGE_BOOT_NKIMAGE_NAND_SIZE / flashInfo.dwBytesPerBlock); 
     
    // Set image load address 
    pSectorBuf = (LPBYTE) OALPAtoUA(IMAGE_BOOT_NKIMAGE_RAM_PA_START); 
 
    EdbgOutputDebugString("INFO: Copying NK image to RAM address 0x%x\r\n", pSectorBuf); 
 
    lastPercentComplete = 0; 
     
    // Copy NK from NAND flash to RAM 
    for (blockID = startBlockID; blockID < endBlockID ; blockID++) 
    {         
        // Skip bad blocks 
        if (FMD_GetBlockStatus(blockID) == BLOCK_STATUS_BAD) 
        { 
            EdbgOutputDebugString("INFO: Found bad NAND flash block [0x%x].\r\n", blockID); 
            continue; 
        } 
 
        // Compute sector address based on current physical block 
        startSectorAddr = blockID * flashInfo.wSectorsPerBlock; 
        endSectorAddr = startSectorAddr + flashInfo.wSectorsPerBlock; 
         
        for (sectorAddr = startSectorAddr; sectorAddr < endSectorAddr; sectorAddr++) 
        { 
            if (!FMD_ReadSector(sectorAddr, pSectorBuf, §orInfo, 1)) 
            { 
                EdbgOutputDebugString("ERROR: Failed to update NK.\r\n"); 
                return(FALSE); 
            } 
 
            pSectorBuf += flashInfo.wDataBytesPerSector; 
        } 
 
        percentComplete =  100 * (blockID - startBlockID + 1) / (endBlockID - startBlockID); 
 
        // If percentage complete has changed, show the progress 
        if (lastPercentComplete != percentComplete) 
        { 
            lastPercentComplete = percentComplete; 
            OEMWriteDebugByte('\r'); 
            EdbgOutputDebugString("INFO: Load is %d%% complete.", percentComplete); 
        } 
 
    } 
 
    EdbgOutputDebugString("\r\nINFO: Copy of NK completed successfully.\r\n"); 
 
     
    return(TRUE); 
 
} 
 
 
//----------------------------------------------------------------------------- 
// 
//  Function:  NANDFormatNK 
// 
//  This function formats (erases) the NAND flash region reserved for OS  
//  images. 
// 
//  Parameters: 
//      None.      
// 
//  Returns: 
//      TRUE indicates success. FALSE indicates failure. 
// 
//----------------------------------------------------------------------------- 
BOOL NANDFormatNK(void) 
{ 
    FlashInfo flashInfo; 
    BLOCK_ID blockID, startBlockID, endBlockID; 
    UINT32 percentComplete, lastPercentComplete; 
 
    // Get NAND flash data bytes per sector. 
    // 
    if (!FMD_GetInfo(&flashInfo)) 
    { 
        EdbgOutputDebugString("ERROR: Unable to get NAND flash information.\r\n"); 
        return(FALSE); 
    } 
 
    // Calculate the physical block range for the NK image 
    startBlockID = IMAGE_BOOT_NKIMAGE_NAND_OFFSET / flashInfo.dwBytesPerBlock; 
    endBlockID = startBlockID + (IMAGE_BOOT_NKIMAGE_NAND_SIZE / flashInfo.dwBytesPerBlock); 
 
    EdbgOutputDebugString("INFO: Starting format of NAND NK region.\r\n"); 
 
    lastPercentComplete = 0; 
 
    for (blockID = startBlockID; blockID < endBlockID ; blockID++) 
    { 
        // Is the block bad? 
        // 
        if (FMD_GetBlockStatus(blockID) == BLOCK_STATUS_BAD) 
        { 
            EdbgOutputDebugString("\r\nINFO: Found bad NAND flash block [0x%x].\r\n", blockID); 
            continue; 
        } 
 
        // Erase the block... 
        // 
        if (!FMD_EraseBlock(blockID)) 
        { 
            EdbgOutputDebugString("\r\nERROR: Unable to erase NAND flash block 0x%x.\r\n", blockID); 
            return(FALSE); 
        } 
        else 
        { 
            percentComplete = 100 * (blockID - startBlockID + 1) / (endBlockID - startBlockID); 
 
            // If percentage complete has changed, show the progress 
            if (lastPercentComplete != percentComplete) 
            { 
                lastPercentComplete = percentComplete; 
                OEMWriteDebugByte('\r'); 
                EdbgOutputDebugString("INFO: Format is %d%% complete.", percentComplete); 
            } 
        } 
    } 
 
    EdbgOutputDebugString("\r\nINFO: Format of NAND NK region completed successfully.\r\n"); 
 
     
    return(TRUE); 
} 
 
 
//----------------------------------------------------------------------------- 
// 
//  Function:  NANDFormatAll 
// 
//  This function formats (erases) the entire NAND flash memory. 
// 
//  Parameters: 
//      None.      
// 
//  Returns: 
//      TRUE indicates success. FALSE indicates failure. 
// 
//----------------------------------------------------------------------------- 
BOOL NANDFormatAll(void) 
{ 
    FlashInfo flashInfo; 
    BLOCK_ID blockID, startBlockID, endBlockID; 
    UINT32 percentComplete, lastPercentComplete; 
 
    // Get NAND flash data bytes per sector. 
    // 
    if (!FMD_GetInfo(&flashInfo)) 
    { 
        EdbgOutputDebugString("ERROR: Unable to get NAND flash information.\r\n"); 
        return(FALSE); 
    } 
 
    // Calculate the physical block range for the enrire NAND device 
    startBlockID = 0; 
    endBlockID = flashInfo.dwNumBlocks; 
 
    EdbgOutputDebugString("INFO: Starting format of all NAND regions.\r\n"); 
 
    lastPercentComplete = 0; 
 
    for (blockID = startBlockID; blockID < endBlockID ; blockID++) 
    { 
        // Is the block bad? 
        // 
        if (FMD_GetBlockStatus(blockID) == BLOCK_STATUS_BAD) 
        { 
            EdbgOutputDebugString("INFO: Found bad NAND flash block [0x%x].\r\n", blockID); 
            continue; 
        } 
 
        // Erase the block... 
        // 
        if (!FMD_EraseBlock(blockID)) 
        { 
            EdbgOutputDebugString("ERROR: Unable to erase NAND flash block 0x%x.\r\n", blockID); 
            return(FALSE); 
        } 
        else 
        { 
            percentComplete = 100 * (blockID - startBlockID + 1) / (endBlockID - startBlockID); 
 
            // If percentage complete has changed, show the progress 
            if (lastPercentComplete != percentComplete) 
            { 
                lastPercentComplete = percentComplete; 
                OEMWriteDebugByte('\r'); 
                EdbgOutputDebugString("INFO: Format is %d%% complete.", percentComplete); 
            } 
        } 
    } 
     
    EdbgOutputDebugString("\r\nINFO: Format of all NAND regions completed successfully.\r\n"); 
 
    return(TRUE); 
} 
 
 
//------------------------------------------------------------------------------ 
// 
//  Function:  NANDLoadBootCFG 
// 
//  Retrieves bootloader configuration information (menu settings, etc.) from  
//  the NAND flash. 
// 
//  Parameters: 
//      eBootCFG  
//          [out] Points to bootloader configuration that will be filled with 
//          loaded data.  
// 
//      cbBootCfgSize 
//          [in] Size in bytes of the bootloader configuration. 
// 
//  Returns: 
//      TRUE indicates success. FALSE indicates failure. 
// 
//----------------------------------------------------------------------------- 
BOOL NANDLoadBootCFG(BYTE *pBootCfg, DWORD cbBootCfgSize) 
{ 
    BOOL rc = FALSE; 
    FlashInfo flashInfo; 
    BLOCK_ID blockID, startBlockID, endBlockID; 
    SECTOR_ADDR sectorAddr; 
 
    if (!FMD_GetInfo(&flashInfo)) 
    { 
        EdbgOutputDebugString("ERROR: Unable to get NAND flash information.\r\n"); 
        return(FALSE); 
    } 
 
    // Calculate the physical block range for the boot configuration 
    startBlockID = (flashInfo.dwNumBlocks * flashInfo.dwBytesPerBlock -  
                       IMAGE_BOOT_BOOTCFG_NAND_SIZE) / flashInfo.dwBytesPerBlock; 
    endBlockID = startBlockID + (IMAGE_BOOT_BOOTCFG_NAND_SIZE / flashInfo.dwBytesPerBlock); 
     
    EdbgOutputDebugString("INFO: Loading boot configuration from NAND\r\n"); 
     
    // Find a good block and load the boot configuration 
    for (blockID = startBlockID; (blockID < endBlockID) && (rc == FALSE) ; blockID++) 
    {         
        // Skip bad blocks 
        if (FMD_GetBlockStatus(blockID) == BLOCK_STATUS_BAD) 
        { 
            EdbgOutputDebugString("INFO: Found bad NAND flash block [0x%x].\r\n", blockID); 
            continue; 
        } 
 
        // Compute sector address based on current physical block 
        sectorAddr = blockID * flashInfo.wSectorsPerBlock; 
 
        rc = FMD_ReadSector(sectorAddr, sectorBuf, NULL, 1); 
    } 
 
    if (rc) 
    { 
        if (cbBootCfgSize > NANDFC_BOOT_SIZE) 
			cbBootCfgSize = NANDFC_BOOT_SIZE; 
        memcpy(pBootCfg, sectorBuf, cbBootCfgSize); 
    } 
    else 
    { 
        EdbgOutputDebugString("ERROR: Failed to load boot configuration from NAND\r\n"); 
    } 
 
    return rc; 
} 
 
 
//------------------------------------------------------------------------------ 
// 
//  Function:  NANDStoreBootCFG 
// 
//  Stores bootloader configuration information (menu settings, etc.) to  
//  the NAND flash. 
// 
//  Parameters: 
//      eBootCFG  
//          [out] Points to bootloader configuration that will be stored. 
// 
//      cbBootCfgSize 
//          [in] Size in bytes of the bootloader configuration. 
// 
//  Returns: 
//      TRUE indicates success. FALSE indicates failure. 
// 
//----------------------------------------------------------------------------- 
BOOL NANDStoreBootCFG(BYTE *pBootCfg, DWORD cbBootCfgSize) 
{ 
    FlashInfo flashInfo; 
    SectorInfo sectorInfo; 
    BLOCK_ID blockID, startBlockID, endBlockID; 
    SECTOR_ADDR sectorAddr, startSectorAddr, endSectorAddr; 
 
    if (!FMD_GetInfo(&flashInfo)) 
    { 
        EdbgOutputDebugString("ERROR: Unable to get NAND flash information.\r\n"); 
        return(FALSE); 
    } 
 
	if (cbBootCfgSize > NANDFC_BOOT_SIZE) 
		cbBootCfgSize = NANDFC_BOOT_SIZE; 
    memcpy(sectorBuf, pBootCfg, cbBootCfgSize); 
    memset(sectorBuf + cbBootCfgSize, 0xFF, NANDFC_BOOT_SIZE - cbBootCfgSize); 
 
    sectorInfo.dwReserved1 = 0xFFFFFFFF; 
    sectorInfo.bOEMReserved = 0x00; 
    sectorInfo.bBadBlock = 0xFF;     
    sectorInfo.wReserved2 = 0xFFFF; 
 
    // Calculate the physical block range for the boot configuration 
    startBlockID = (flashInfo.dwNumBlocks * flashInfo.dwBytesPerBlock -  
                       IMAGE_BOOT_BOOTCFG_NAND_SIZE) / flashInfo.dwBytesPerBlock; 
    endBlockID = startBlockID + (IMAGE_BOOT_BOOTCFG_NAND_SIZE / flashInfo.dwBytesPerBlock); 
     
    EdbgOutputDebugString("INFO: Storing boot configuration to NAND\r\n"); 
     
    // Erase range of NAND blocks reserved for boot configuration 
    for (blockID = startBlockID; blockID < endBlockID; blockID++) 
    { 
        // Skip bad blocks 
        if (FMD_GetBlockStatus(blockID) == BLOCK_STATUS_BAD) 
        { 
            EdbgOutputDebugString("INFO: Found bad NAND flash block [0x%x].\r\n", blockID); 
            continue; 
        } 
 
        // Erase the block... 
        if (!FMD_EraseBlock(blockID)) 
        { 
            EdbgOutputDebugString("ERROR: Unable to erase NAND flash block [0x%x].\r\n", blockID); 
            return FALSE; 
        } 
    } 
 
    // Find a good block and store the boot configuration 
    for (blockID = startBlockID; blockID < endBlockID ; blockID++) 
    {         
        // Skip bad blocks 
        if (FMD_GetBlockStatus(blockID) == BLOCK_STATUS_BAD) 
        { 
            EdbgOutputDebugString("INFO: Found bad NAND flash block [0x%x].\r\n", blockID); 
            continue; 
        } 
 
        // Compute sector address based on current physical block 
        startSectorAddr = blockID * flashInfo.wSectorsPerBlock; 
        endSectorAddr = startSectorAddr + flashInfo.wSectorsPerBlock; 
 
        // Write out the boot configuration to all sectors as filler so the 
        // NAND FMD does not try to map it later 
        for (sectorAddr = startSectorAddr; sectorAddr < endSectorAddr; sectorAddr++) 
        { 
            if (!FMD_WriteSector(sectorAddr, sectorBuf, §orInfo, 1)) 
            { 
                EdbgOutputDebugString("ERROR: Failed to update EBOOT.\r\n"); 
                return FALSE; 
            } 
        } 
    } 
 
    EdbgOutputDebugString("INFO: Successfully stored boot configuration to NAND\r\n"); 
 
    return TRUE; 
}