Пример #1
0
BOOL WriteBlock(DWORD dwBlock, LPBYTE pbBlock, PSectorInfo pSectorInfoTable)
{
int i;
    for (int iSector = 0; iSector < g_FlashInfo.wSectorsPerBlock; iSector++) {
        #ifdef IROMBOOT
		if((dwBlock==0) && (iSector<(STEPLDR_LENGTH/SECTOR_SIZE))){
				OALMSG(TRUE, (TEXT("Stepldr for iROM NANDFlash Boot...\r\n")));
	        if (!FMD_WriteSector_Stepldr(dwBlock * g_FlashInfo.wSectorsPerBlock + iSector, pbBlock, pSectorInfoTable, 1))
	            return FALSE;
		}
		else

        #endif  // !IROMBOOT

        if (!FMD_WriteSector(dwBlock * g_FlashInfo.wSectorsPerBlock + iSector, pbBlock, pSectorInfoTable, 1))
            return FALSE;
		/*		for(i=0;i<2048;i++)
				RETAILMSG(1, (L"%x ",*(pbBlock+i)));
			RETAILMSG(1, (L"\r\n"));
*/
        if (pbBlock)
            pbBlock += g_FlashInfo.wDataBytesPerSector;
        if (pSectorInfoTable)        
            pSectorInfoTable++;        
    }
    return TRUE;
}
Пример #2
0
BOOL WriteSector(SECTOR_ADDR sector, VOID *pBuffer, SectorInfo *pInfo)
{
    BOOL rc;
    UINT32 retry = 4;

    do {
        rc = FMD_WriteSector(sector, pBuffer, pInfo, 1);
    } while (!rc && --retry > 0);
    return rc;
}
Пример #3
0
BOOL WriteBlock(DWORD dwBlock, LPBYTE pbBlock, PSectorInfo pSectorInfoTable)
{
    for (int iSector = 0; iSector < g_FlashInfo.wSectorsPerBlock; iSector++) {

#ifdef _IROMBOOT_
        if((dwBlock==0) && (iSector<(IMAGE_STEPLOADER_SIZE/SECTOR_SIZE))){
            if (!FMD_WriteSector_Stepldr(dwBlock * g_FlashInfo.wSectorsPerBlock + iSector, pbBlock, pSectorInfoTable, 1))
                return FALSE;
        } else
#endif
        if (!FMD_WriteSector(dwBlock * g_FlashInfo.wSectorsPerBlock + iSector, pbBlock, pSectorInfoTable, 1))
            return FALSE;

//#endif  // !_IROMBOOT_

        if (pbBlock)
            pbBlock += g_FlashInfo.wDataBytesPerSector;
        if (pSectorInfoTable)
            pSectorInfoTable++;
    }
    return TRUE;
}
Пример #4
0
void ReserveLoaderArea()
{
   DWORD block, sector;
   DWORD startsector=0;
   SectorInfo si;
   
   RETAILMSG(0,(L"+FlashReserveLoaderArea \r\n"));

#ifdef 	IROMBOOT
	startsector=(STEPLDR_LENGTH/SECTOR_SIZE);
	OALMSG(TRUE, (TEXT("SECTOR_SIZE : 0x%x ...\r\n"), SECTOR_SIZE));
#endif 
   // to keep bootpart off of our reserved blocks we must mark it as bad, reserved & read-only
   si.bOEMReserved = ~(OEM_BLOCK_RESERVED | OEM_BLOCK_READONLY);
   si.bBadBlock    = BADBLOCKMARK;
   si.dwReserved1  = 0xffffffff;
   si.wReserved2   = 0xffff;
   
   OALMSG(TRUE, (TEXT("Reserving Blocks [0x%x - 0x%x] ...\r\n"), 0, IMAGE_START_BLOCK-1));
   for(block = 0; block < IMAGE_START_BLOCK; block++) 
   {
      if( FlashIsRealBadBlock(block) )
         RETAILMSG(1,(TEXT("  ..0x%x block is bad -- NOT ERASED\r\n"),block));
      else
      {
         RETAILMSG(1,(TEXT("  ..0x%x block is reserved\r\n"),block));
	  //if(block!=0)
	  //	startsector=0;
    //     for( sector = startsector; sector < g_FlashInfo.wSectorsPerBlock; sector++){
  //          FMD_WriteSector(block * g_FlashInfo.wSectorsPerBlock + sector, NULL, &si, 1);
  FMD_WriteSector(BLOCK_TO_SECTOR(block), NULL, &si, 1);
		 //}
   	  }
   }
   OALMSG(TRUE, (TEXT("...reserve complete.\r\n")));
   
   RETAILMSG(0,(L"-FlashReserveLoaderArea \r\n"));
}
Пример #5
0
BOOL WriteRawImageToBootMedia(DWORD dwImageStart, DWORD dwImageLength, DWORD dwLaunchAddr)
{
	DWORD dwBlock = 0;
	DWORD dwNumBlocks = 0;
	LPBYTE pbBuffer;
       SectorInfo si;
	//unsigned int sectorchk;
	unsigned int sectorcnt = 0;
	unsigned int areachange = 0;

    OALMSG(OAL_FUNC, (TEXT("+WriteRawImageToBootMedia\r\n")));

    if ( !g_bBootMediaExist ) 
    {
	    OALMSG(OAL_ERROR, (TEXT("ERROR: WriteRawImageToBootMedia: device doesn't exist.\r\n")));
        return(FALSE);
    }
	if(dwLaunchAddr==0x10)
		{
		dwBlock = LOGO_BLOCK;
		//dwImageLength = UPLDR_RAM_IMAGE_SIZE;
		g_ImageType=IMAGE_TYPE_RAWBIN;
		}else{

	if (g_ImageType == IMAGE_TYPE_LOADER)
	{
		dwBlock = EBOOT_BLOCK;
	    if ( !VALID_TOC(g_pTOC) ) 
	    {
		    OALMSG(OAL_WARN, (TEXT("WARN: WriteRawImageToBootMedia: 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);
        	}
    	}
	}
	else if (g_ImageType == IMAGE_TYPE_STEPLDR)
	{
		dwBlock = STEPLDR_BLOCK;
		dwImageStart += dwLaunchAddr;
		//dwImageLength = 0x2050; //step loader can support 8k bytes.
		//dwImageLength = STEPLDR_RAM_IMAGE_SIZE; //step loader can support 4k bytes only. NO SuperIPL case is 16K....... 3 Block
	}
	else if (g_ImageType == IMAGE_TYPE_DIONB0)
	{
		dwBlock = DIONB0_BLOCK;
		dwImageStart += dwLaunchAddr;
		dwImageLength -= (dwImageLength / (g_FlashInfo.wDataBytesPerSector + 8))*8;
	}
	else if (g_ImageType == IMAGE_TYPE_UPLDR)
	{
		dwBlock = UPLDR_BLOCK;
		dwImageStart += dwLaunchAddr;
		dwImageLength = UPLDR_RAM_IMAGE_SIZE; //step loader can support 4k bytes only. NO SuperIPL case is 16K....... 3 Block
	}
	else if (g_ImageType == IMAGE_TYPE_FLASHBIN)
	{
		dwBlock = FLASHBIN_BLOCK;
//		dwImageStart += dwLaunchAddr;
//		dwImageLength = dwImageLength; //step loader can support 4k bytes only. NO SuperIPL case is 16K....... 3 Block
	}
			}
		if(dwLaunchAddr==0x10)
			pbBuffer=(LPBYTE)dwImageStart;
		else
	pbBuffer = OEMMapMemAddr(dwImageStart, dwImageStart);
	 
    // Compute number of blocks.
    dwNumBlocks = (dwImageLength / (g_FlashInfo.wDataBytesPerSector*g_FlashInfo.wSectorsPerBlock)) + (dwImageLength%(g_FlashInfo.wDataBytesPerSector*g_FlashInfo.wSectorsPerBlock) ? 1: 0);
    OALMSG(TRUE, (TEXT("dwImageLength = 0x%x \r\n"), dwImageLength));
    OALMSG(TRUE, (TEXT("dwNumBlocks = 0x%x \r\n"), dwNumBlocks));

    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.

	    OALMSG(TRUE, (TEXT("dwBlock(0x%x) X "), dwBlock));
	    OALMSG(TRUE, (TEXT("g_FlashInfo.wSectorsPerBlock(0x%x)"), g_FlashInfo.wSectorsPerBlock));
	    OALMSG(TRUE, (TEXT(" = 0x%x \r\n"), dwBlock*g_FlashInfo.wSectorsPerBlock));

        if (!FMD_EraseBlock(dwBlock)) 
        {
            OALMSG(TRUE, (TEXT("WriteData: failed to erase block (0x%x).\r\n"), dwBlock));
			++dwBlock;
			++dwNumBlocks;		  // Compensate for fact that we didn't write any blocks.
			continue;
        }

//		FMD_GetBlockStatus(dwBlock);
        if ((FMD_GetBlockStatus(dwBlock) & (BLOCK_STATUS_BAD | BLOCK_STATUS_RESERVED))&&dwBlock!=0) {
		    OALMSG(TRUE, (TEXT("FMD_GetBlockStatus Error \r\n")));
            ++dwBlock;
            ++dwNumBlocks;        // Compensate for fact that we didn't write any blocks.
            continue;
        }

		//FMD_ReadSector(dwBlock*g_FlashInfo.wSectorsPerBlock, NULL, &si, 1);
		if (!FMD_ReadSector(dwBlock*g_FlashInfo.wSectorsPerBlock, NULL, &si, 1)) 
		{
		    OALMSG(TRUE, (TEXT("FMD_ReadSector : Failed to get block(0x%x)'s sector info.\r\n"), dwBlock));
            ++dwBlock;
            ++dwNumBlocks;        // Compensate for fact that we didn't write any blocks.
            continue;
		}

		// Stepldr & Eboot image in nand flash
		// block mark as BLOCK_STATUS_RESERVED & BLOCK_STATUS_READONLY & BLOCK_STATUS_BAD
        if ((si.bBadBlock == 0x0) && (si.bOEMReserved !=0xfc )&&dwBlock!=0)
        {
            OALMSG(TRUE, (TEXT("si.bBadBlock failed, si.bOEMReserved %x,si.bBadBlock %x.\r\n"),si.bOEMReserved, si.bBadBlock));
            ++dwBlock;
            ++dwNumBlocks;        // Compensate for fact that we didn't write any blocks.
            continue;
        }
        if (!ReadBlock(dwBlock, NULL, g_pSectorInfoBuf)) 
        {
		    OALMSG(TRUE, (TEXT("WriteData: failed to read block (0x%x).\r\n"), dwBlock));
            return(FALSE);
        }


		if (g_ImageType == IMAGE_TYPE_DIONB0)
		{
			for ( int iSector = 0; iSector < g_FlashInfo.wSectorsPerBlock; iSector ++ )
			{
				if (!WritePage(dwBlock*g_FlashInfo.wSectorsPerBlock + iSector, pbBuffer, (PSectorInfo)(pbBuffer+g_FlashInfo.wDataBytesPerSector) )) 
				{
					OALMSG(TRUE, (TEXT("WriteData: failed to write block (0x%x).\r\n"), dwBlock));
					return(FALSE);
				}
				pbBuffer += (g_FlashInfo.wDataBytesPerSector + 8);
			}

			++dwBlock;
		}
		else if (g_ImageType == IMAGE_TYPE_FLASHBIN)
		{
			for ( int iSector = 0; iSector < g_FlashInfo.wSectorsPerBlock; iSector ++ )
			{
				//RETAILMSG(1, (TEXT("WriteData: (0x%x)(0x%x,0x%x,0x%x).\r\n"), dwBlock*g_FlashInfo.wSectorsPerBlock + iSector, g_pSectorInfoBuf->dwReserved1, g_pSectorInfoBuf->bOEMReserved, g_pSectorInfoBuf->wReserved2));

				if (!WritePage(dwBlock*g_FlashInfo.wSectorsPerBlock + iSector, pbBuffer, (PSectorInfo)(pbBuffer+g_FlashInfo.wDataBytesPerSector) )) 
				{
					OALMSG(TRUE, (TEXT("WriteData: failed to write block (0x%x).\r\n"), dwBlock));
                    #ifdef IROMBOOT
					FMD_EraseBlock(dwBlock);		
							 
					// this block is actually considered to bad block.
					// because it may be erased initial bad block information. 
					si.bOEMReserved = 0xff;
					si.bBadBlock    = BADBLOCKMARK;
					si.dwReserved1  = 0xffffffff;
					si.wReserved2   = 0xffff;

					FMD_WriteSector(BLOCK_TO_SECTOR(dwBlock), NULL, &si, 1);
					pbBuffer -= (g_FlashInfo.wDataBytesPerSector + 8)*iSector;
					iSector = 0;
					break;					
                    
                    #else
					return(FALSE);

                    #endif  // !IROMBOOT
				}
				pbBuffer += (g_FlashInfo.wDataBytesPerSector + 8);
			}

			++dwBlock;
		}
		else
		{
			OALMSG(TRUE, (TEXT(" Write Reserved Area \n")));

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

			++dwBlock;
			OALMSG(TRUE, (TEXT("pbBuffer + %x .\r\n"), SECTOR_SIZE/*SECTORS_PER_PAGE*/*PAGES_PER_BLOCK));
            #ifdef IROMBOOT
			pbBuffer += (SECTOR_SIZE/*SECTORS_PER_PAGE*/*PAGES_PER_BLOCK);

            #else
			pbBuffer += 0x4000;

            #endif  // !IROMBOOT
		}
    }
	
	if (g_ImageType == IMAGE_TYPE_LOADER)
	{
    	g_pTOC->id[0].dwLoadAddress = dwImageStart;
    	g_pTOC->id[0].dwJumpAddress = 0;
    	g_pTOC->id[0].dwTtlSectors  = FILE_TO_SECTOR_SIZE(dwImageLength);
    	g_pTOC->id[0].sgList[0].dwSector = BLOCK_TO_SECTOR(EBOOT_BLOCK);
    	g_pTOC->id[0].sgList[0].dwLength = g_pTOC->id[0].dwTtlSectors;
    }

    OALMSG(OAL_FUNC, (TEXT("_WriteRawImageToBootMedia\r\n")));
	return TRUE;
}
Пример #6
0
BOOL WritePage(DWORD dwPage, LPBYTE pbPage, PSectorInfo pSectorInfo)
{
	if (!FMD_WriteSector(dwPage, pbPage, pSectorInfo, 1))
		return FALSE;
    return TRUE;
}
Пример #7
0
//
// Store TOC to Nand
// BUGBUG: only uses 1 sector for now.
//
BOOL TOC_Write(void)
{
    SectorInfo si, si2;

    EdbgOutputDebugString("+TOC_Write\r\n");

    if ( !g_bBootMediaExist ) {
        EdbgOutputDebugString("TOC_Write WARN: no boot media\r\n");
        return FALSE;
    }

    // is it a valid TOC?
    if ( !VALID_TOC(g_pTOC) ) {
        EdbgOutputDebugString("TOC_Write ERROR: INVALID_TOC Signature: 0x%x\r\n", g_pTOC->dwSignature);
       // return FALSE;
    }

    // is it a valid image descriptor?
    if ( !VALID_IMAGE_DESCRIPTOR(&g_pTOC->id[g_dwTocEntry]) ) {
        EdbgOutputDebugString("TOC_Write ERROR: INVALID_IMAGE[%u] Signature: 0x%x\r\n",
            g_dwTocEntry, g_pTOC->id[g_dwTocEntry].dwSignature);
        //return FALSE;
    }

    // in order to write a sector we must erase the entire block first
	// !! BUGBUG: must cache the TOC first so we don't trash other image descriptors !!
    if ( !FMD_EraseBlock(TOC_BLOCK) ) {
        RETAILMSG(1, (TEXT("TOC_Write ERROR: EraseBlock[%d] \r\n"), TOC_BLOCK));
        return FALSE;
    }

    // setup our metadata so filesys won't stomp us
    si.dwReserved1 = 0;
    si.bOEMReserved = OEM_BLOCK_RESERVED | OEM_BLOCK_READONLY;
    si.bBadBlock = BADBLOCKMARK;
    si.wReserved2 = 0;

    // write the sector & metadata
    if ( !FMD_WriteSector(TOC_SECTOR, (PUCHAR)&g_TOC, &si, 1) ) {
        EdbgOutputDebugString("TOC_Write ERROR: Unable to save TOC\r\n");
        return FALSE;
    }

    // read it back & verify both data & metadata
    if ( !FMD_ReadSector(TOC_SECTOR, (PUCHAR)&toc, &si2, 1) ) {
        EdbgOutputDebugString("TOC_Write ERROR: Unable to read/verify TOC\r\n");
        return FALSE;
    }

    if ( 0 != memcmp(&g_TOC, &toc, SECTOR_SIZE) ) {
        EdbgOutputDebugString("TOC_Write ERROR: TOC verify failed\r\n");
        return FALSE;
    }

    if ( 0 != memcmp(&si, &si2, sizeof(si)) ) {
        EdbgOutputDebugString("TOC_Write ERROR: SectorInfo verify failed: %x %x %x %x\r\n",
            si.dwReserved1, si.bOEMReserved, si.bBadBlock, si.wReserved2);
        return FALSE;
    }

    EdbgOutputDebugString("-TOC_Write\r\n");
    return TRUE;
}
Пример #8
0
BOOL nand_ecc_test( DWORD Context,  UCHAR *pInBuffer, UCHAR *p_outbuf)
{
    NANDTEST_CONTEXT *pContext = (NANDTEST_CONTEXT *)Context;
    BOOL rc = FALSE;
    DWORD block;
    FlashInfo flashInfo;
    UCHAR outBuf[SECTOR_SIZE];
    SectorInfo secInfo;
    UCHAR *p_gooddata, *p_baddata;
	
    /* NAND ECC test
         sector: pContext.test_sector
         data: p_gooddata, p_baddata
    */
    p_gooddata = pInBuffer;
    p_baddata = pInBuffer + SECTOR_SIZE;
	
    DEBUGMSG(ZONE_FUNCTION, (L"+nand_ecc_test: p_gooddata=%x, p_baddata=%x\r\n", p_gooddata, p_baddata));

    /* Step 0: Erase block */
    if (!FMD_GetInfo(&flashInfo))
    {
        DEBUGMSG(ZONE_ERROR,  (L"ERROR: FMD_GetInfo call failed!\r\n"));
        goto cleanUp;
    }
	
    block  = pContext->test_sector / flashInfo.wSectorsPerBlock;
	
    rc = FMD_EraseBlock(block);
    if(!rc)
    {
        DEBUGMSG(ZONE_ERROR,  (L"ERROR: nand_ecc_test ERASE block(%d) failed!\r\n", block));
        goto cleanUp;
    }
    /* Step 1: flash the NAND with Good data */
    rc = FMD_WriteSector(pContext->test_sector, p_gooddata, NULL, 1); 
    if( !rc )
    {
        DEBUGMSG(ZONE_ERROR,  (L"ERROR: nand_ecc_test WRITE good data in sector(%d) failed!\r\n", pContext->test_sector));
        goto cleanUp;
    }
	
    /* Step 2: read back to see if there are errors */
    rc = FMD_ReadSector(pContext->test_sector, outBuf, NULL, 1); 

    if( !rc )
    {
        DEBUGMSG(ZONE_ERROR,  (L"ERROR: nand_ecc_test READ good data in sector(%d) failed!\r\n", pContext->test_sector));
        goto cleanUp;
    }
    if(memcmp(outBuf, p_gooddata, SECTOR_SIZE))
    {
        DEBUGMSG(ZONE_ERROR,  (L"ERROR: nand_ecc_test READ data does not match good data in sector(%d) failed!\r\n", pContext->test_sector));
        goto cleanUp;
    }

    /* Step 3: flash the NAND with Bad data WITHOUT UPDATING ecc*/
    memset (&secInfo, 0, sizeof(secInfo));
    secInfo.bOEMReserved = SKIP_ECC_WRITE_MAGIC_NUMBER;
    rc = FMD_WriteSector(pContext->test_sector, p_baddata, &secInfo, 1); 
    if( !rc )
    {
        DEBUGMSG(ZONE_ERROR,  (L"ERROR: nand_ecc_test WRITE bad data in sector(%d) failed!\r\n", pContext->test_sector));
        goto cleanUp;
    }
	
    /* Step 4: read back to see if there are errors */
    rc = FMD_ReadSector(pContext->test_sector, outBuf, NULL, 1); 
    if( !rc )
    {
        DEBUGMSG(ZONE_ERROR,  (L"ERROR: nand_ecc_test READ good data in sector(%d) failed!\r\n", pContext->test_sector));
        goto cleanUp; 
    }
	
    if(memcmp(outBuf, p_gooddata, SECTOR_SIZE))
    {
        RETAILMSG(TRUE,  (L"ERROR: nand_ecc_test READ data after correction does not match good data in sector(%d) failed!\r\n",
			pContext->test_sector));
        goto cleanUp;
    }
    rc = TRUE;
	
cleanUp:
    memcpy(p_outbuf, outBuf, SECTOR_SIZE );
	
    return rc;
}
BOOL
BLWriteBootCfg(
    BOOT_CFG *pBootCfg
    )
{
    BOOL rc = FALSE;
#ifndef BSP_NO_NAND_IN_SDBOOT
    HANDLE hFMD;
    PCI_REG_INFO regInfo;
    FlashInfo flashInfo;
    SectorInfo sectorInfo;
    SECTOR_ADDR sector;
    BLOCK_ID block;
    UINT32 count, offset, length;
    UINT8 buffer[2048];
    UINT8 *pEBOOT;
    

    // EBOOT configuration is placed in last sector of image
    regInfo.MemBase.Reg[0] = g_ulFlashBase;
    hFMD = FMD_Init(NULL, &regInfo, NULL);
    if (hFMD == NULL)
        {
        OALMSG(OAL_ERROR, (L"ERROR: FMD_Init call failed!\r\n"));
        goto cleanUp;
        }

    // Get flash info
    if (!FMD_GetInfo(&flashInfo))
        {
        OALMSG(OAL_ERROR, (L"ERROR: FMD_GetInfo call failed!\r\n"));
        goto cleanUp;
        }

    // We can support only flash with sector size which fit to our buffer
    if (flashInfo.wDataBytesPerSector > sizeof(buffer))
        {
        OALMSG(OAL_ERROR, (L"ERROR: "
            L"Flash sector size %d bytes bigger that supported %d bytes\r\n",
            flashInfo.wDataBytesPerSector, sizeof(buffer)    
            ));
        goto cleanUp;
        }

    // Configuration is located in last sector of last EBOOT block
    offset = IMAGE_XLDR_BOOTSEC_NAND_SIZE + IMAGE_EBOOT_BOOTSEC_NAND_SIZE;
    
    // Skip X-Loader & EBOOT code & bad blocks
    // Note that we also check the last eboot block in order to ensure it is good
    block  = 0;
    count = 0;
    while ((count < offset) && (block < flashInfo.dwNumBlocks))
        {
        if ((FMD_GetBlockStatus(block) & BLOCK_STATUS_BAD) == 0)
            {
            count += flashInfo.dwBytesPerBlock;
            }
        block++;
        }

    // We've incremented past the last eboot block in order to check it too
    // Back up now, the previous block is the last one containing eboot and is good
    block--;

    // Need to copy off the block contents to RAM (minus the config sector)
    pEBOOT = (UINT8*)IMAGE_WINCE_CODE_CA;
    length = flashInfo.dwBytesPerBlock - flashInfo.wDataBytesPerSector;

    memset((VOID*)pEBOOT, 0xFF, flashInfo.dwBytesPerBlock);
    sector = block * flashInfo.wSectorsPerBlock;
    offset = 0;

    while (offset < length) 
    {
        // When block read fail, there isn't what we can do more
        if (!FMD_ReadSector(sector, pEBOOT + offset, &sectorInfo, 1)) {
                OALMSG(OAL_ERROR, (L"\r\nERROR: EBOOT!BLWriteBootCfg: "
                    L"Failed read sector %d from flash\r\n", sector
                ));
            goto cleanUp;
            }

        // Move to next sector
        sector++;
        offset += flashInfo.wDataBytesPerSector;
    }


    //  Copy the config info into last sector of saved block in RAM
    memcpy(pEBOOT + offset, pBootCfg, sizeof(BOOT_CFG)); 


    // Erase block
    if (!FMD_EraseBlock(block))
        {
        OALMSG(OAL_ERROR, (L"ERROR: EBOOT!BLWriteBootCfg: "
            L"Flash block %d erase failed\r\n", block
            ));
        goto cleanUp;
        }


    // Write contents of the save block + config sector back to flash
    pEBOOT = (UINT8*)IMAGE_WINCE_CODE_CA;
    length = flashInfo.dwBytesPerBlock;

    sector = block * flashInfo.wSectorsPerBlock;
    offset = 0;
    while (offset < length)
    {
        // Prepare sector info
        memset(&sectorInfo, 0xFF, sizeof(sectorInfo));
        sectorInfo.bOEMReserved &= ~(OEM_BLOCK_READONLY|OEM_BLOCK_RESERVED);
        sectorInfo.dwReserved1 = 0;
        sectorInfo.wReserved2 = 0;

        // Write sector        
        if (!FMD_WriteSector(sector, pEBOOT + offset, &sectorInfo, 1))
            {
            OALMSG(OAL_ERROR, (L"ERROR: EBOOT!BLWriteBootCfg: "
                L"Flash sector %d write failed\r\n", sector
                ));
            goto cleanUp;
            }

        // Move to next sector
        sector++;
        offset += flashInfo.wDataBytesPerSector;
    }

    // Done    
    rc = TRUE;

cleanUp:
    if (hFMD != NULL) FMD_Deinit(hFMD);
#else
    UNREFERENCED_PARAMETER(pBootCfg);
#endif
    return rc;
}