| #include "Typedef.h" |
| #include "FM.h" |
| #include "Partition.h" |
| #include "Errors.h" |
| #include "Flash.h" |
| #include "misc.h" |
| #include "loadoffsets.h" |
| #include "tim.h" |
| #include "malloc.h" |
| |
| #if COPYIMAGESTOFLASH |
| #include "BootLoader.h" |
| #endif |
| |
| extern UINT_T *pFM_SPACE; |
| |
| #ifdef BBM_LEGACY_EXT |
| static int EntryIsBlockInfo(void *Entry) |
| { |
| P_BlockState_T pBlkInfo = (P_BlockState_T)Entry; |
| |
| if((pBlkInfo->State >= BLOCK_RECYCLED) && |
| (pBlkInfo->State != BLK_FLIP_COUNT)) |
| return 1; |
| else |
| return 0; |
| } |
| |
| static int EntryIsReloPair(void *Entry) |
| { |
| P_ReloPair_T pRelo = (P_ReloPair_T)Entry; |
| P_FlashProperties_T pFlashProp = GetFlashProperties(BOOT_FLASH); |
| if(pRelo->From < pFlashProp->NumBlocks) |
| return 1; |
| else |
| return 0; |
| } |
| |
| void SetABBTState(ReloState_T newState, FlashBootType_T fbt) |
| { |
| UINT_T BlkSize, PageSize; |
| P_FMProperties_T pFMProps = GetFMProperties(); |
| |
| //make sure this device uses BBM |
| if(GetFlashProperties(fbt)->FlashSettings.UseBBM == 0) |
| return; |
| // Now set some values that will work |
| PageSize = GetPageSize(fbt); |
| BlkSize = GetBlockSize(fbt); |
| |
| //check if the BBT got erase. If so, set the slot numbers back to their starting slot |
| if ( newState == BBT_ERASED ) |
| { |
| if (pFMProps->ABBT_Reverse) |
| pFMProps->ABBT_CurSlot = (BlkSize / PageSize) - 1; |
| else |
| pFMProps->ABBT_CurSlot = 0; |
| } |
| |
| pFMProps->ABBT_State &= ~ABBT_STATE_MASK; |
| pFMProps->ABBT_State |= newState; |
| } |
| |
| UINT_T GetABBTState(FlashBootType_T fbt) |
| { |
| P_FMProperties_T pFMProps = GetFMProperties(); |
| |
| //make sure this device uses BBM |
| if(GetFlashProperties(fbt)->FlashSettings.UseBBM == 0) |
| return 0; |
| |
| return (pFMProps->ABBT_State & ABBT_STATE_MASK); |
| } |
| |
| void SetBBTStates(ReloState_T bbtState, ReloState_T abbtState, FlashBootType_T fbt) |
| { |
| SetBBTState(bbtState, fbt); |
| SetABBTState(abbtState, fbt); |
| } |
| |
| static void UpdateBBTCrc(P_ReloTable_T pBBT) |
| { |
| UINT_T CRC, ABBTVersion, BBTLen; |
| BBT_INFO BBTInfo; |
| BBTLen = BBT_HEADER_SIZE + pBBT->NumReloc * sizeof(ReloPair_T); |
| |
| ABBTVersion = BU_REG_READ((UINT_T)pBBT + BBTLen); |
| if (ABBTVersion != ABBT_VERSION_2001){ |
| //obm_printf("Upgrade BBT for CRC!!!\n\r"); |
| BU_REG_WRITE((UINT_T)pBBT + BBTLen, ABBT_VERSION_2001); |
| } |
| |
| BBTInfo.Value = 0; |
| BBTInfo.Bits.Owner = BBT_OBM; |
| BU_REG_WRITE((UINT_T)pBBT + BBTLen + 4, BBTInfo.Value); |
| BBTLen = BBTLen + 4 /* version */ + 2 /* half info */; |
| CRC = malbrain_crc32(0, pBBT, BBTLen); |
| BBTInfo.Bits.CRC = from32to16(CRC); |
| BU_REG_WRITE16((UINT_T)pBBT + BBTLen, BBTInfo.Bits.CRC); |
| } |
| |
| INT_T IsBBTValid(P_ReloTable_T pBBT) |
| { |
| UINT_T CRC1, CRC2, ABBTVersion, BBTLen; |
| BBTLen = BBT_HEADER_SIZE + pBBT->NumReloc * sizeof(ReloPair_T); |
| |
| ABBTVersion = BU_REG_READ((UINT_T)pBBT + BBTLen); |
| |
| switch(ABBTVersion) |
| { |
| case 0xFFFFFFFF: |
| return (pBBT->Header == BBT_TYPE_LEGACY); |
| default: |
| obm_printf("Warning: unexpected version: 0x%x\n\r", ABBTVersion); |
| case ABBT_VERSION_2001: |
| CRC1 = BU_REG_READ16((UINT_T)pBBT + BBTLen + 6); |
| CRC2 = malbrain_crc32(0, pBBT, BBTLen + 6); |
| CRC2 = from32to16(CRC2); |
| return (CRC1 == CRC2); |
| } |
| |
| return 0; |
| } |
| |
| static void UpdateABBTCrc(P_ABBT_Table_T pABBT) |
| { |
| UINT_T CRC, CrcLen; |
| if (pABBT->Version != ABBT_VERSION_2001) { |
| obm_printf("Upgrade ABBT for CRC!!!\n\r"); |
| pABBT->Version = ABBT_VERSION_2001; |
| } |
| |
| CrcLen = ABBT_HEADER_SIZE + pABBT->NumReloc * sizeof(ReloPair_T); |
| pABBT->Info.Value = 0; |
| pABBT->Info.Bits.Owner = BBT_OBM; |
| CRC = malbrain_crc32(0, pABBT, CrcLen); |
| pABBT->Info.Bits.CRC = from32to16(CRC); |
| } |
| |
| static INT_T IsABBTValid(P_ABBT_Table_T pABBT) |
| { |
| UINT_T CrcLen, CRC1, CRC2; |
| switch(pABBT->Version) |
| { |
| case ABBT_VERSION: |
| case ABBT_VERSION_1002: |
| return (pABBT->Identifier == BBT_TYPE_ASR); |
| default: |
| obm_printf("Warning: unexpected ABBT version: 0x%x\n\r", pABBT->Version); |
| case ABBT_VERSION_2001: |
| CrcLen = ABBT_HEADER_SIZE + pABBT->NumReloc * sizeof(ReloPair_T); |
| CRC1 = pABBT->Info.Bits.CRC; |
| pABBT->Info.Bits.CRC = 0; |
| CRC2 = malbrain_crc32(0, pABBT, CrcLen); |
| CRC2 = from32to16(CRC2); |
| return (CRC1 == CRC2); |
| } |
| |
| return 0; |
| } |
| |
| INT CheckMagicBBT(UINT_T Magic, VOID *BbtBuffer) |
| { |
| P_ReloTable_T pBBT = NULL; |
| P_ABBT_Table_T pABBT = NULL; |
| |
| switch(Magic) { |
| case BBT_TYPE_LEGACY: |
| pBBT = (P_ReloTable_T)BbtBuffer; |
| return IsBBTValid(pBBT); |
| case BBT_TYPE_ASR: |
| pABBT = (P_ABBT_Table_T)BbtBuffer; |
| return IsABBTValid(pABBT); |
| default: |
| obm_printf("Invalid BBT magic: 0x%x\n\r", Magic); |
| return 1; //??? |
| } |
| |
| return 0; |
| } |
| |
| /* This is the blocks are maintained by L1 BBT */ |
| static INT_T L1BbtBlocks[L1_BBT_BLOCK_NUM]; |
| static UINT_T L1BbtBlockNum = 0; |
| |
| VOID InitL1BBTBlocks(VOID) |
| { |
| P_FlashProperties_T pFlashProp = GetFlashProperties(BOOT_FLASH); |
| |
| INT_T i = 0; |
| #if DUAL_TIM |
| if (DualTimEnabled()) { |
| pAsrDualTimInfo pDualTimInfo = GetDualTimInfo(); |
| L1BbtBlocks[i++] = pDualTimInfo->MainObmBlock; //Main OBM block |
| L1BbtBlocks[i++] = pDualTimInfo->BackupTimBlock; //Backup TIM block |
| L1BbtBlocks[i++] = pDualTimInfo->BackupObmBlock; //Backup OBM Block |
| L1BbtBlocks[i++] = pFlashProp->NumBlocks - 2; //main ABBT |
| L1BbtBlocks[i++] = pFlashProp->NumBlocks - 1; //backup ABBT |
| L1BbtBlocks[i++] = 0; //Main TIM |
| L1BbtBlockNum = i; |
| } else |
| #endif |
| { |
| L1BbtBlocks[i++] = 1; //OBM Block |
| L1BbtBlocks[i++] = pFlashProp->NumBlocks - 2; //main ABBT |
| L1BbtBlocks[i++] = pFlashProp->NumBlocks - 1; //backup ABBT |
| L1BbtBlockNum = i; |
| } |
| } |
| |
| INT_T IsLogicBlockInL1BBT(UINT_T BlockNum) { |
| INT_T i; |
| |
| for (i = 0 ; i < L1BbtBlockNum; i++) |
| { |
| if(L1BbtBlocks[i] == BlockNum) |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| INT_T IsTimBlock(UINT_T BlockNum) { |
| #if DUAL_TIM |
| if (DualTimEnabled()) { |
| pAsrDualTimInfo pDualTimInfo = GetDualTimInfo(); |
| if(BlockNum == 0 || BlockNum == pDualTimInfo->BackupTimBlock) |
| return 1; |
| } else |
| #endif |
| { |
| return (BlockNum == 0); |
| } |
| |
| return 0; |
| } |
| |
| INT_T IsBlockRelocated(UINT_T BlockNum) { |
| UINT_T BlockTo = BlockNum; |
| |
| BlockTo = ScanBBT_LegacyExt(BlockNum); |
| |
| if (BlockTo != BlockNum) |
| return 1; |
| |
| return 0; |
| } |
| |
| void CreateBBT_LegacyExt(P_PartitionInfo_T pPI) |
| { |
| #if COPYIMAGESTOFLASH |
| int i; |
| UINT_T Retval = NoError; |
| UINT_T PageSize, BlkSize, mask, TIMpagesUsed; |
| |
| P_FMProperties_T pFMProp = GetFMProperties(); |
| P_FlashProperties_T pFlashProp = GetFlashProperties(BOOT_FLASH); |
| |
| //If this flash device does not use BBM, just return |
| if(pFlashProp->FlashSettings.UseBBM == 0) |
| return; |
| |
| //Initialize FMProperties structure: |
| SetBBTStates(BBT_INVALID, BBT_INVALID, BOOT_FLASH); |
| |
| PageSize = pFlashProp->PageSize; |
| BlkSize = pFlashProp->BlockSize; |
| |
| //One page for BBT, the other for ABBT |
| //pFM_SPACE need to be valued before(in ClearFM) |
| memset(pFM_SPACE, 0xFF, PageSize*2); |
| |
| pFMProp->pLBBT = (P_ReloTable_T) pFM_SPACE; |
| pFMProp->pLBBT->Header = (USHORT) BBT_TYPE_LEGACY; |
| pFMProp->pLBBT->NumReloc = 0; |
| pFMProp->BBT_type = BBT_TYPE_LEGACY; |
| //NOTE:: SET THE STARTING SLOT HERE!!! |
| // For now use the last page in block zero to match XDB |
| #ifdef BBT_ORDER_REVERSE |
| pFMProp->BBT_Slot = (BlkSize / PageSize) - 1; |
| pFMProp->BBT_NextSlot = pFMProp->BBT_Slot - 1; |
| pFMProp->BBT_Reverse = 1; |
| #else |
| TIMpagesUsed = (max_tim_size_byte + PageSize - 1) / PageSize; |
| pFMProp->BBT_Slot = TIMpagesUsed; |
| pFMProp->BBT_NextSlot = pFMProp->BBT_Slot + 1; |
| pFMProp->BBT_Reverse = 0; |
| #endif |
| pFMProp->BBT_location = pFMProp->BBT_Slot * PageSize; |
| SetBBTState(BBT_CHANGED, BOOT_FLASH); |
| |
| pFMProp->pABBT = (P_ABBT_Table_T) (((UINT_T)pFM_SPACE)+PageSize); |
| pFMProp->pABBT->Identifier = BBT_TYPE_ASR; |
| pFMProp->pABBT->Type = BBT_TYPE_MBBT_RUN; |
| pFMProp->pABBT->NumReloc = 0; |
| pFMProp->pABBT->RefCounter = 0; |
| pFMProp->pABBT->NumBlockRecyled = 0; |
| pFMProp->pABBT->NumBlockToRecyle = 0; |
| #if DUAL_TIM |
| if(DualTimEnabled()) { |
| pAsrDualTimInfo pDualTimInfo = GetDualTimInfo(); |
| pFMProp->pABBT->BackupBBTLoc = pDualTimInfo->BackupTimBlock * BlkSize; |
| } |
| else |
| #endif |
| { |
| pFMProp->pABBT->BackupBBTLoc = 0; |
| } |
| pFMProp->pABBT->Version = ABBT_VERSION_2001; |
| pFMProp->ABBT_block0 = pFlashProp->NumBlocks - 1; |
| pFMProp->ABBT_block1 = pFlashProp->NumBlocks - 2; |
| |
| //Start with the last page |
| #ifdef BBT_ORDER_REVERSE |
| pFMProp->ABBT_CurSlot = (BlkSize / PageSize) - 1; |
| pFMProp->ABBT_Reverse = 1; |
| #else |
| pFMProp->ABBT_CurSlot = 0; |
| pFMProp->ABBT_Reverse = 0; |
| #endif |
| pFMProp->ScanBBT = &ScanBBT_LegacyExt; |
| |
| SetABBTState(BBT_CHANGED, BOOT_FLASH); |
| #endif |
| return; |
| } |
| |
| /* This function is only for TIM block, which are never relocated |
| * Logic block number is always equal to physical block number |
| */ |
| static UINT_T EraseBlockAndRestoreTIM(P_FlashProperties_T pFlashProp, UINT_T BlockNum, INT_T RestoreBBT) |
| { |
| UINT_T Retval = NoError, BlkSize; |
| EraseFlash_F erase = pFlashProp->EraseFlash; |
| ReadFlash_F read = pFlashProp->ReadFromFlash; |
| WriteFlash_F write = pFlashProp->WriteToFlash; |
| P_FMProperties_T pFMProps = NULL; |
| UINT8_T *ptemp = 0; |
| UINT_T ReadSize, WriteOffset, BBTWriteLen, PageSize; |
| UINT_T BlockOffset; |
| |
| ptemp = malloc(pFlashProp->BlockSize); |
| |
| if(ptemp == NULL) |
| return HeapExhaustedError; |
| |
| BlkSize = GetBlockSize(BOOT_FLASH); |
| |
| BlockOffset = BlkSize * BlockNum; |
| |
| if(RestoreBBT) |
| ReadSize = BlkSize; |
| else |
| ReadSize = max_tim_size_byte; |
| |
| Retval = read(BlockOffset, ptemp, ReadSize, BOOT_FLASH); |
| if(Retval != NoError && Retval != ReadDisturbError) |
| Retval = read(BlockOffset, ptemp, ReadSize, BOOT_FLASH); |
| |
| if(Retval != NoError && Retval != ReadDisturbError){ |
| free(ptemp); |
| return Retval; |
| } |
| |
| //erase all the block |
| Retval = erase(BlockOffset, BlkSize, BOOT_FLASH); |
| |
| if(Retval != NoError) |
| Retval = erase(BlockOffset, BlkSize, BOOT_FLASH); |
| |
| if(Retval != NoError){ |
| free(ptemp); |
| return Retval; |
| } |
| |
| //restore TIM data in lower portion of the block |
| Retval = write(BlockOffset, ptemp, max_tim_size_byte, BOOT_FLASH); |
| if(Retval != NoError){ //write fail, erase and try another time |
| Retval = erase(BlockOffset, BlkSize, BOOT_FLASH); |
| if(Retval != NoError){ |
| free(ptemp); |
| return Retval; |
| } |
| Retval = write(BlockOffset, ptemp, max_tim_size_byte, BOOT_FLASH); |
| } |
| |
| if (RestoreBBT) { |
| PageSize = GetPageSize(BOOT_FLASH); |
| pFMProps = GetFMProperties(); |
| if (pFMProps->BBT_Reverse) { |
| WriteOffset = (pFMProps->BBT_Slot + 1) * PageSize; |
| BBTWriteLen = BlkSize - WriteOffset; |
| Retval = write(BlockOffset+WriteOffset, ptemp+WriteOffset, BBTWriteLen, BOOT_FLASH); |
| } else { |
| WriteOffset = max_tim_size_byte; |
| BBTWriteLen = pFMProps->BBT_Slot * PageSize - max_tim_size_byte; |
| Retval = write(BlockOffset+WriteOffset, ptemp+WriteOffset, BBTWriteLen, BOOT_FLASH); |
| } |
| } |
| |
| free(ptemp); |
| return Retval; |
| } |
| |
| static UINT_T WriteABBT2Flash(UINT_T Block2Write, INT_T NeedErase, INT_T *ABBT_changed) |
| { |
| UINT_T Retval = NoError; |
| P_FMProperties_T pFMProps = GetFMProperties(); |
| P_FlashProperties_T pFlashProp = GetFlashProperties(BOOT_FLASH); |
| EraseFlash_F erase = pFlashProp->EraseFlash; |
| WriteFlash_F write = pFlashProp->WriteToFlash; |
| ReadFlash_F read = pFlashProp->ReadFromFlash; |
| UINT_T BlockIdx, BlockAddr, PageAddr; |
| UINT_T BlkSize, PageSize, NumPages, StartPage, EndPage; |
| INT_T i, Retry = TRUE; |
| P_ABBT_Table_T pABBT = pFMProps->pABBT; |
| UINT8_T *ptemp = 0; |
| |
| BlkSize = pFlashProp->BlockSize; |
| PageSize = pFlashProp->PageSize; |
| NumPages = BlkSize/PageSize; |
| |
| BlockIdx = ScanBBT_LegacyExt(Block2Write); |
| ptemp = malloc(BlkSize); |
| if(ptemp == NULL) |
| return HeapExhaustedError; |
| |
| while(NeedErase) |
| { |
| BlockAddr = BlockIdx * BlkSize; |
| Retval = erase(BlockAddr, BlkSize, BOOT_FLASH); |
| if(Retval != NoError) //retry again |
| Retval = erase(BlockAddr, BlkSize, BOOT_FLASH); |
| if(Retval != NoError){ |
| Retval = RelocateBlock_LegacyExt(BlockIdx, &BlockIdx, 0); |
| UpdateABBTCrc(pABBT); |
| |
| if(Retval != NoError){ |
| free(ptemp); |
| return Retval; |
| } |
| else |
| *ABBT_changed = TRUE; //relocation causes BBTs changed |
| |
| continue; |
| } |
| break; //Erase succeeds |
| } |
| |
| StartPage = pFMProps->ABBT_CurSlot; |
| EndPage = pFMProps->ABBT_CurSlot + 1; //only need to write one page |
| BlockAddr =BlockIdx * BlkSize; |
| |
| for(i = StartPage; i < EndPage; i++) |
| { |
| PageAddr = i * PageSize; |
| Retval = read(BlockAddr+PageAddr, ptemp, PageSize, BOOT_FLASH); |
| if(Retval == NoError) { |
| if(CheckPattern(ptemp, 0xFF, PageSize) == 1) //All 0xFFs |
| Retval = write(BlockAddr+PageAddr, (UINT_T)pABBT, PageSize, BOOT_FLASH); |
| else |
| Retval = FlashReadError; //Page to read is not All 0xFF |
| |
| /* ABBT read back and check */ |
| if (Retval == NoError) { |
| Retval = read(BlockAddr+PageAddr, ptemp, PageSize, BOOT_FLASH); |
| if(Retval == NoError){ |
| if (memcmp(ptemp, pABBT, PageSize)) { |
| Retval = FlashReadError; //ABBT Read back mismatch |
| } |
| } |
| } |
| } |
| |
| if(Retval != NoError) { |
| if(Retry == TRUE){ |
| Retry = FALSE; |
| Retval = erase(BlockAddr, BlkSize, BOOT_FLASH); |
| if(Retval == NoError){ |
| //succeed to erase, need to write the previous page with valid(latest) ABBT |
| if (pFMProps->ABBT_Reverse) { |
| i = StartPage - 1; //i++ still works, write ABBT from StartPage to last page of ABBT_block |
| EndPage = NumPages; |
| } else { |
| i = - 1; //i++ still works, write ABBT from page 0 to StartPage |
| EndPage = StartPage + 1; |
| } |
| continue; |
| } |
| //If erase fails, go through to relocate |
| } |
| Retry = TRUE; |
| Retval = RelocateBlock_LegacyExt(BlockIdx, &BlockIdx, 0); |
| UpdateABBTCrc(pABBT); |
| if(Retval != NoError){ |
| free(ptemp); |
| //Relocation fails, return with Error |
| return Retval; |
| } |
| else{ |
| if (pFMProps->ABBT_Reverse) { |
| i = StartPage - 1; //i++ still works |
| //need to write the previous page with valid(latest) ABBT |
| EndPage = NumPages; |
| } else { |
| i = -1; |
| EndPage = StartPage + 1; |
| } |
| BlockAddr = BlockIdx * BlkSize; |
| *ABBT_changed = TRUE; //relocation causes BBTs changed |
| } |
| //retry writing, with the relocated block |
| } |
| } |
| |
| free(ptemp); |
| return Retval; |
| } |
| |
| static VOID BBT_RemoveDummyEntries(P_ReloTable_T pBBT) |
| { |
| INT_T i; |
| |
| for (i = 0; i < pBBT->NumReloc; i++) |
| { |
| if ((pBBT->Relo[i].From == BLOCK_BAD) && |
| (pBBT->Relo[i].To == BLOCK_BAD)) |
| { |
| pBBT->Relo[i].From = pBBT->Relo[pBBT->NumReloc - 1].From; |
| pBBT->Relo[i].To = pBBT->Relo[pBBT->NumReloc - 1].To; |
| pBBT->NumReloc --; |
| i--; |
| } |
| } |
| } |
| |
| static UINT_T WriteBBT2Flash(UINT_T Block2Write, INT_T BBTNeedRestore) |
| { |
| UINT_T Retval = NoError, BlkSize, PageSize; |
| P_FMProperties_T pFMProps = GetFMProperties(); |
| P_FlashProperties_T pFlashProp = GetFlashProperties(BOOT_FLASH); |
| WriteFlash_F write = pFlashProp->WriteToFlash; |
| ReadFlash_F read = pFlashProp->ReadFromFlash; |
| EraseFlash_F erase = pFlashProp->EraseFlash; |
| UINT_T Addr = 0; |
| P_ReloTable_T pBBT; |
| UINT_T pBuffer; |
| |
| PageSize = pFlashProp->PageSize; |
| BlkSize = pFlashProp->BlockSize; |
| //NumPages = BlkSize / PageSize; |
| pBBT = pFMProps->pLBBT; |
| |
| if (IsBlockRelocated(Block2Write)) |
| { |
| //obm_printf("Try to write a relocated TIM block\n\r"); |
| return NoError; |
| } |
| |
| if (BBTNeedRestore) |
| { |
| BBT_RemoveDummyEntries(pBBT); |
| Retval = EraseBlockAndRestoreTIM(pFlashProp, Block2Write, 0); |
| if(Retval != NoError){ |
| obm_printf("Erase and restore backup TIM error\n\r"); |
| return BBTWriteError; |
| } |
| } |
| |
| pBuffer = malloc(PageSize); |
| if (pBuffer == NULL) { |
| obm_printf("Warning: no buffer for BBT RBC\n\r"); |
| return NoError; |
| } |
| |
| #if DUAL_TIM |
| if (DualTimEnabled()) { |
| Addr = pFMProps->BBT_Slot * PageSize; //offset in TIM block |
| Addr += (Block2Write * BlkSize); |
| |
| /* write BBT to TIM block */ |
| if (IsBlockRelocated(Block2Write)) { |
| ; //Do nothing if TIM block is already relocated |
| } else { |
| Retval = read(Addr, pBuffer, PageSize, BOOT_FLASH); |
| if (Retval != NoError) { |
| free(pBuffer); |
| return BBTReadError; |
| } |
| if (CheckPattern(pBuffer, 0xFF, PageSize) == 0) { |
| free(pBuffer); |
| //obm_printf("CheckPattern fails\n\r"); |
| return BBTReadError; |
| } |
| Retval = WriteFlash(Addr, (UINT_T)pBBT, PageSize, BOOT_FLASH); |
| if(Retval != NoError){ |
| obm_printf("BBT Write TIM block%d Error\n\r", Block2Write); |
| } |
| if(IsBlockRelocated(Block2Write)) { |
| EraseFlash((Block2Write * BlkSize) , BlkSize, BOOT_FLASH); |
| erase((Block2Write * BlkSize) , BlkSize, BOOT_FLASH); |
| } |
| } |
| } else |
| #endif |
| { |
| //get the block offset from BBT_location |
| Addr = pFMProps->BBT_location & ~(BlkSize - 1); |
| //get the page offset from SLot |
| Addr |= pFMProps->BBT_Slot * PageSize; |
| Retval = write(Addr, (UINT_T)pBBT, PageSize, BOOT_FLASH); |
| if(Retval != NoError){ |
| //as block0 is always good, should never reach here |
| obm_printf("BBT Write Error, %s %d!!!\n\r", __FUNCTION__, __LINE__); |
| free(pBuffer); |
| //FatalError(BBTWriteError, NULL, NULL); |
| return BBTWriteError; |
| } |
| } |
| |
| Retval = read(Addr, (UINT_T)pBuffer, PageSize, BOOT_FLASH); |
| if (Retval != NoError) { |
| Retval = BBTReadError; |
| } |
| |
| /* BBT read back check */ |
| if (Retval == NoError && memcmp(pBBT, pBuffer, PageSize)){ |
| Retval = BBTReadError; |
| } |
| |
| free(pBuffer); |
| return Retval; |
| } |
| |
| void UpdateBBT_LegacyExt() |
| { |
| #if COPYIMAGESTOFLASH |
| UINT_T Retval = NoError, BlkSize, PageSize, NumPages, TIMpagesUsed; |
| P_FMProperties_T pFMProps = GetFMProperties(); |
| P_FlashProperties_T pFlashProp = GetFlashProperties(BOOT_FLASH); |
| P_ReloTable_T pBBT; |
| UINT_T maxBlockNum, Retval1 = NoError; |
| UINT_T PrimaryBlock, MirrorBlock; |
| INT_T i, NeedErase = FALSE, ABBT_changed = FALSE; |
| INT_T BBTNeedRestore = FALSE; |
| INT_T BBTRetry = 0; |
| #if DUAL_TIM |
| pAsrDualTimInfo pDualTimInfo; |
| #endif |
| |
| //If this flash device does not use BBM, just return |
| if(pFlashProp->FlashSettings.UseBBM == 0) |
| return; |
| |
| //update BBT (only if there are changes) |
| if((pFMProps->BBT_State == BBT_UNCHANGED) && |
| (GetABBTState(BOOT_FLASH) == BBT_UNCHANGED)) |
| return; |
| |
| //if we never found a BBT, just return |
| if(pFMProps->BBT_State == BBT_INVALID) |
| return; |
| |
| // Now set some values that will work |
| PageSize = pFlashProp->PageSize; |
| BlkSize = pFlashProp->BlockSize; |
| NumPages = BlkSize / PageSize; |
| |
| #if DUAL_TIM |
| if (DualTimEnabled()) { |
| pDualTimInfo = GetDualTimInfo(); |
| pFMProps->pABBT->BackupBBTLoc = pDualTimInfo->BackupTimBlock * BlkSize; |
| } |
| #endif |
| |
| update_bbts_again: |
| //During updating ABBT, BBT could be changed, therefore, update ABBT before BBT |
| if(GetABBTState(BOOT_FLASH) == BBT_CHANGED || |
| GetABBTState(BOOT_FLASH) == BBT_ERASED) |
| { |
| do{ |
| ABBT_changed = FALSE; |
| if(pFMProps->pABBT->RefCounter % 2){ |
| PrimaryBlock = pFMProps->ABBT_block0; |
| MirrorBlock = pFMProps->ABBT_block1; |
| }else{ |
| PrimaryBlock = pFMProps->ABBT_block1; |
| MirrorBlock = pFMProps->ABBT_block0; |
| } |
| |
| if(pFMProps->ABBT_CurSlot == INVALID_PAGE) { |
| NeedErase = TRUE; |
| if (pFMProps->ABBT_Reverse) |
| pFMProps->ABBT_CurSlot = NumPages - 1; |
| else |
| pFMProps->ABBT_CurSlot = 0; |
| } |
| |
| pFMProps->pABBT->RefCounter++; |
| |
| UpdateABBTCrc(pFMProps->pABBT); |
| |
| //Write ABBT to the two blocks |
| Retval1 = WriteABBT2Flash(PrimaryBlock, NeedErase, &ABBT_changed); |
| if(Retval1 != NoError) |
| { |
| obm_printf("Warning: fail to update ABBT in one block\n\r"); |
| } |
| |
| Retval = WriteABBT2Flash(MirrorBlock, NeedErase, &ABBT_changed); |
| if(Retval1 != NoError && Retval != NoError) |
| { |
| //Neither block of ABBT is correctly updated. |
| obm_printf("Error: fail to update ABBT in both blocks\n\r"); |
| } |
| |
| if ((pFMProps->ABBT_CurSlot == (NumPages - 1) && !pFMProps->ABBT_Reverse) || |
| (pFMProps->ABBT_CurSlot == 0 && pFMProps->ABBT_Reverse) || |
| pFMProps->ABBT_CurSlot >= NumPages /*unexpected*/) { |
| pFMProps->ABBT_CurSlot = INVALID_PAGE; |
| } else { |
| if (pFMProps->ABBT_Reverse) |
| pFMProps->ABBT_CurSlot--; |
| else |
| pFMProps->ABBT_CurSlot++; |
| } |
| |
| }while(ABBT_changed); |
| |
| SetABBTState(BBT_UNCHANGED, BOOT_FLASH); |
| } |
| |
| //Legacy way to update BBT |
| if(pFMProps->BBT_State == BBT_CHANGED || pFMProps->BBT_State == BBT_ERASED || |
| pFMProps->BBT_State == BBT_NEED_SYNC) |
| { |
| //Find and read first page after the TIM |
| TIMpagesUsed = (max_tim_size_byte + PageSize - 1) / PageSize; |
| UpdateBBTCrc(pFMProps->pLBBT); |
| //Did we run out of BBT Slots? |
| if( pFMProps->BBT_Slot < TIMpagesUsed || pFMProps->BBT_Slot >= NumPages || |
| pFMProps->BBT_State == BBT_NEED_SYNC ) |
| { |
| BBTNeedRestore = TRUE; |
| //reset slot counters |
| if (pFMProps->BBT_Reverse) { |
| pFMProps->BBT_Slot = NumPages - 1; |
| pFMProps->BBT_NextSlot = NumPages - 2; |
| } else { |
| pFMProps->BBT_Slot = TIMpagesUsed; |
| pFMProps->BBT_NextSlot = pFMProps->BBT_Slot + 1; |
| } |
| pFMProps->BBT_location = pFMProps->BBT_Slot*PageSize; |
| |
| } |
| |
| Retval = WriteBBT2Flash(0, BBTNeedRestore); |
| #if DUAL_TIM |
| if (DualTimEnabled()) { |
| Retval1 = WriteBBT2Flash(pDualTimInfo->BackupTimBlock, BBTNeedRestore); |
| if (Retval == BBTReadError || Retval1 == BBTReadError) { |
| if (BBTRetry < 2) { /* read-back-check fails, retry twice */ |
| BBTRetry ++; |
| SetBBTState(BBT_NEED_SYNC, BOOT_FLASH); |
| goto update_bbts_again; |
| } |
| } else { |
| if (Retval != NoError && Retval1 != NoError) |
| FatalError(BBTWriteError, "DualWriteErr", 0); |
| } |
| if(IsBlockRelocated(pDualTimInfo->BackupTimBlock) && IsBlockRelocated(0)) |
| FatalError(BBTWriteError, "DUAL_TIM_RELOCATED", 0); |
| } |
| #endif |
| |
| //update slot info |
| if (pFMProps->BBT_Reverse) { |
| pFMProps->BBT_Slot--; |
| pFMProps->BBT_NextSlot--; |
| } else { |
| pFMProps->BBT_Slot++; |
| pFMProps->BBT_NextSlot++; |
| } |
| |
| /* If relocations happens to TIM blocks when update BBT */ |
| if (GetABBTState(BOOT_FLASH) == BBT_CHANGED) |
| goto update_bbts_again; |
| |
| //set the state back to unchanged |
| SetBBTState(BBT_UNCHANGED, BOOT_FLASH); |
| } |
| |
| #endif |
| return; |
| } |
| |
| UINT_T ScanBBT_LegacyExt(UINT_T BlockNum) |
| { |
| P_FMProperties_T pFMProp = GetFMProperties(); |
| P_ReloTable_T pBBT = pFMProp->pLBBT; |
| P_ABBT_Table_T pABBT = pFMProp->pABBT; |
| UINT_T i; |
| |
| //if no BBT was loaded, return original block num. No BBT, No ABBT either. |
| if(pFMProp->BBT_State == BBT_INVALID ) |
| return BlockNum; |
| |
| /* No relocation needed for Block0 if DualTIM is not enabled */ |
| if(BlockNum == 0 && !DualTimEnabled()) |
| return 0; |
| |
| //Scan First level BBT if the block is used for ABBT |
| if(IsLogicBlockInL1BBT(BlockNum)) |
| { |
| for (i = 0; i < pBBT->NumReloc; i++) |
| { |
| if (pBBT->Relo[i].From == BlockNum) |
| return pBBT->Relo[i].To; |
| } |
| |
| //No matched relocate pair found |
| if(i == pBBT->NumReloc) |
| return BlockNum; |
| } |
| |
| if(GetABBTState(BOOT_FLASH) == BBT_INVALID) |
| return BlockNum; |
| |
| for (i = 0; i < pABBT->NumReloc; i++) |
| { |
| if (pABBT->Relo[i].From == BlockNum) |
| { |
| return pABBT->Relo[i].To; |
| //We make sure that the remapped block is always good, |
| //which means there is no A--Relocated-To-->B--Relocated-To-->C like map |
| //Don't need to start over any more. |
| //i = 0; // Start over in case remapped block is now bad |
| //continue; |
| } |
| } |
| |
| return BlockNum; |
| } |
| |
| static INT_T BlockIsBad(UINT_T BlockNum) |
| { |
| P_FMProperties_T pFMProp = GetFMProperties(); |
| P_ABBT_Table_T pABBT = pFMProp->pABBT; |
| INT_T i; |
| for(i = 0; i < pABBT->NumReloc; i++) |
| { |
| if(pABBT->BlkInfo[i].Index == BlockNum && pABBT->BlkInfo[i].State == BLOCK_BAD) |
| return TRUE; |
| } |
| |
| return FALSE; |
| |
| } |
| /* |
| * RelocateBlock_Legacy |
| */ |
| extern UINT_T All_FF_flag; |
| UINT_T __RelocateBlock_LegacyExt(UINT_T BlockNum, UINT_T* ReloBlock, UINT_T isBitFlip, UINT_T NeedErase) |
| { |
| UINT_T Retval = NoError; |
| UINT_T BlockRelocatedTo, BlockToRead; |
| UINT_T NumRelo, BlkSize, initial_blk, end_blk, i; |
| INT_T idx = -1, RelocatedBlockEntry = -1; |
| INT_T RecyledBlock = -1; |
| UINT8_T *pBuffer = 0; |
| UINT8_T *pReadBuffer = 0; |
| UINT_T BlockAddr1, BlockAddr2, Addr, PageSize, All_FF_flag_restore, BitflipEntryNum, PoolBlockNum; |
| P_FMProperties_T pFMProp = GetFMProperties(); |
| P_FlashProperties_T pFlashProp = GetFlashProperties(BOOT_FLASH); |
| P_ReloTable_T pBBT = pFMProp->pLBBT; |
| P_ABBT_Table_T pABBT = pFMProp->pABBT; |
| EraseFlash_F erase = pFlashProp->EraseFlash; |
| ReadFlash_F read = pFlashProp->ReadFromFlash; |
| WriteFlash_F write = pFlashProp->WriteToFlash; |
| |
| /* |
| * Block0 is guaranteed by manufacturer. |
| * And there is no need to relocate other TIM blocks. |
| */ |
| if(BlockNum == 0 && !DualTimEnabled()) |
| { |
| *ReloBlock = 0; |
| return TIMBlocksEraseWriteFail; |
| } |
| |
| if (DualTimEnabled() && IsTimBlock(BlockNum) && !isBitFlip) { |
| obm_printf("TIM blocks relocation happens\n\r"); |
| } |
| |
| //if our BBT is not valid |
| if((pBBT == NULL) || ((UINT16)BBT_TYPE_LEGACY != pBBT->Header)) |
| return BBTReadError; |
| |
| BlkSize = pFlashProp->BlockSize; |
| BlockToRead = BlockNum; |
| |
| //get RP boundaries |
| initial_blk = pFlashProp->NumBlocks - 1 - ABBT_BLOCK_NUM; |
| |
| //TODO end_blk+1 should be the last valid reserved block? |
| end_blk = initial_blk - (pFlashProp->NumBlocks * LEGACY_BBM_RELOC_PERCENTAGE + 99) / 100; |
| |
| SearchBlockForRelocation: |
| |
| NumRelo = pABBT->NumReloc; |
| //start at first RP block |
| BlockRelocatedTo = initial_blk; |
| |
| if ( isBitFlip ) { |
| PoolBlockNum = initial_blk - end_blk; |
| BitflipEntryNum = 0; |
| for ( i = 0 ; i < pABBT->NumReloc; i++ ) { |
| if ( (pABBT->Relo[i].To <= end_blk) && (pABBT->BlkInfo[i].State != BLOCK_BAD) ) |
| BitflipEntryNum++; |
| } |
| |
| if ( pFMProp->ABBT_MaxNum && |
| (BitflipEntryNum + PoolBlockNum * 2) >= pFMProp->ABBT_MaxNum) { |
| obm_printf("Too much bitflip blocks: %d %d %d\n\r", |
| BitflipEntryNum, PoolBlockNum, pFMProp->ABBT_MaxNum); |
| return BBTToMuchBitflips; |
| } |
| } |
| |
| if ( NumRelo >= (pFMProp->ABBT_MaxNum - 2) ) { |
| obm_printf("Warning: ABBT Entry exhausted\n\r"); |
| return NoError; |
| } |
| |
| //if the table is not empty, we need to find the next available RP block |
| if (NumRelo != 0) |
| { |
| //run through all the entries to find that smallest 'To' entry |
| for (i = 0; i < pABBT->NumReloc; i++) { |
| if((pABBT->Relo[i].To > end_blk && pABBT->Relo[i].To <= initial_blk) //in RP |
| && (BlockRelocatedTo > pABBT->Relo[i].To)) |
| //ignore the factory bad block here |
| if(pABBT->BlkInfo[i].State != BLOCK_BAD) |
| BlockRelocatedTo = pABBT->Relo[i].To; |
| } |
| |
| for(i = 0; i < pABBT->NumReloc; i++){ |
| //There may be 0xFFFx->BlockNum Map here, pABBT->Relo[i].From should be valid block |
| if(BlockNum == pABBT->Relo[i].To && EntryIsReloPair(&pABBT->Relo[i])){ |
| //The block to be relocated is a block relocated to other block before |
| //To avoid the map A--relocated to-->B--relocated to-->C |
| //Just A--relocacted to-->C and BLOCK_BAD--relocated to-->B |
| BlockToRead = BlockNum; |
| BlockNum = pABBT->Relo[i].From; |
| if(isBitFlip){ |
| //If the relocation is for bitflip, we record the entry |
| //and handle it after the copy succeeds(mark it as BLOCK_TO_RECYCLE) |
| RelocatedBlockEntry = i; |
| } |
| else |
| pABBT->BlkInfo[i].State = BLOCK_BAD; |
| break; |
| } |
| } |
| |
| //since we just found the last 'used' RP block, try the next one |
| //and make sure it is not a bad block. |
| do{ |
| BlockRelocatedTo--; |
| }while(BlockIsBad(BlockRelocatedTo)); |
| |
| //lastly, make sure we didn't exceed RP |
| if(BlockRelocatedTo <= end_blk) |
| { |
| /* If the reserved pool is exhausted, check if there is |
| * recycled block introduced by bitflips scrubbing. |
| */ |
| #ifdef BITFLIPS_SCRUBBING |
| //There is no recylced blocks, relocation fails |
| if(pABBT->NumBlockRecyled == 0) |
| { |
| obm_printf("BBT Exhausted Error, %s %d\n\r", __FUNCTION__, __LINE__); |
| return BBTExhaustedError; |
| } |
| for(i = 0; i < pABBT->NumReloc; i++) { |
| //Find the first recycled block |
| if(pABBT->BlkInfo[i].State == BLOCK_RECYCLED) |
| { |
| RecyledBlock = i; //reuse the relo entry |
| BlockRelocatedTo = pABBT->BlkInfo[i].Index; |
| break; |
| } |
| } |
| #else |
| obm_printf("BBT Exhausted Error, %s %d\n\r", __FUNCTION__, __LINE__); |
| return BBTExhaustedError; |
| #endif |
| } |
| } |
| |
| if(NeedErase){ |
| //If OBM, need to disable spare area for following erase |
| #if NAND_CODE //To word around the issue that BootROM disable sprare area |
| if(BlockNum == OBM_BLOCK){ |
| DisableSpareAreaForOBM(BlockRelocatedTo*BlkSize, 0); |
| } |
| #endif |
| |
| Retval = erase(BlockRelocatedTo*BlkSize, BlkSize, BOOT_FLASH); |
| |
| if(Retval != NoError) //try again |
| Retval = erase(BlockRelocatedTo*BlkSize, BlkSize, BOOT_FLASH); |
| |
| if(Retval != NoError) { |
| //If erase fails, mark BlockRelocatedTo as bad block and re-search |
| if(RecyledBlock == -1){ |
| pABBT->NumReloc++; |
| //mark it as bad |
| pABBT->BlkInfo[NumRelo].Index = BlockRelocatedTo; |
| pABBT->BlkInfo[NumRelo].State = BLOCK_BAD; |
| } |
| else{ |
| pABBT->NumBlockRecyled --; |
| pABBT->BlkInfo[RecyledBlock].State = BLOCK_BAD; |
| } |
| SetABBTState(BBT_CHANGED, BOOT_FLASH); |
| goto SearchBlockForRelocation; |
| } |
| } |
| |
| //Back up the block before the relocation is done |
| //Here we need to use the low level erase/read/write without scan BBT. |
| if(isBitFlip) |
| { |
| BlockAddr1 = BlockToRead*BlkSize; |
| BlockAddr2 = BlockRelocatedTo*BlkSize; |
| PageSize = pFlashProp->PageSize; |
| |
| All_FF_flag_restore = All_FF_flag; |
| All_FF_flag = 1; //Don't write all 0xFFFFFFFF pages |
| pBuffer = malloc(PageSize << 1); |
| if(pBuffer == 0) |
| return HeapExhaustedError; |
| pReadBuffer = pBuffer + PageSize; |
| for(Addr = 0; Addr < BlkSize; Addr += PageSize) |
| { |
| Retval = read(BlockAddr1+Addr, (UINT_T)pBuffer, PageSize, BOOT_FLASH); |
| if(Retval != NoError && Retval != ReadDisturbError) |
| Retval = read(BlockAddr1+Addr, (UINT_T)pBuffer, PageSize, BOOT_FLASH); |
| if(Retval != NoError && Retval != ReadDisturbError){ |
| //Tiny possibility reach here |
| free(pBuffer); |
| return Retval; |
| } |
| Retval = write(BlockAddr2+Addr, (UINT_T)pBuffer, PageSize, BOOT_FLASH); |
| if (Retval == NoError) { |
| Retval = read(BlockAddr2+Addr, (UINT_T)pReadBuffer, PageSize, BOOT_FLASH); |
| if (Retval != NoError || memcmp(pBuffer, pReadBuffer, PageSize)) |
| { |
| Retval = ProgramError; |
| BlockNum = BLOCK_TO_RECYCLE; /* read back fail, mark as to recyled */ |
| free(pBuffer); |
| goto RelocateDone; |
| } |
| } else { //write fails, mark BlockRelocatedTo as bad block and return |
| Retval = ProgramError; |
| BlockNum = BLOCK_BAD; |
| free(pBuffer); |
| goto RelocateDone; |
| } |
| } |
| free(pBuffer); |
| All_FF_flag = All_FF_flag_restore; |
| |
| //Block to relocate is relocated to another block |
| if(RelocatedBlockEntry != -1) |
| { |
| pABBT->BlkInfo[RelocatedBlockEntry].State = BLOCK_TO_RECYCLE; |
| } |
| } |
| |
| if(IsLogicBlockInL1BBT(BlockNum)) { |
| //If it's not the first time to relocate this block |
| //overwrite the old relocation pair with a useless entry just to increase the NumReloc |
| for(i = 0; i < pBBT->NumReloc; i++){ |
| if(pBBT->Relo[i].From == BlockNum) |
| { |
| //To be compatible with Legacy BBT |
| pBBT->Relo[i].From = BLOCK_BAD; |
| break; |
| } |
| } |
| pBBT->Relo[pBBT->NumReloc].To = BlockRelocatedTo; |
| pBBT->Relo[pBBT->NumReloc].From = BlockNum; |
| pBBT->NumReloc++; |
| SetBBTState(BBT_CHANGED, BOOT_FLASH); |
| } |
| |
| *ReloBlock = (UINT) BlockRelocatedTo; |
| |
| RelocateDone: |
| SetABBTState(BBT_CHANGED, BOOT_FLASH); |
| |
| #ifdef BITFLIPS_SCRUBBING |
| if(RecyledBlock == -1){ |
| pABBT->NumReloc++; |
| // Relocate it |
| pABBT->Relo[NumRelo].To = BlockRelocatedTo; |
| pABBT->Relo[NumRelo].From = BlockNum; |
| } |
| else{ |
| pABBT->NumBlockRecyled --; |
| pABBT->Relo[RecyledBlock].To = BlockRelocatedTo; |
| // if the copy for bitflips fails, mark a bad block(BlockNum = BLOCK_BAD) |
| pABBT->Relo[RecyledBlock].From = BlockNum; |
| //remove the redundant entry(A-->A) |
| if(pABBT->Relo[RecyledBlock].From == pABBT->Relo[RecyledBlock].To) |
| { |
| for(i = RecyledBlock; i < pABBT->NumReloc - 1; i++) |
| { |
| pABBT->ReloValue[i] = pABBT->ReloValue[i+1]; |
| } |
| pABBT->NumReloc--; |
| } |
| } |
| #else |
| pABBT->NumReloc++; |
| pABBT->Relo[NumRelo].To = BlockRelocatedTo; |
| pABBT->Relo[NumRelo].From = BlockNum; |
| #endif |
| obm_printf("Relocation: %d to %d, bitflip: %s\n\r", BlockNum, BlockRelocatedTo, isBitFlip?"yes":"no"); |
| |
| return Retval; |
| } |
| |
| UINT_T RelocateBlock_LegacyExt(UINT_T BlockNum, UINT_T* ReloBlock, UINT_T isBitFlip) |
| { |
| return __RelocateBlock_LegacyExt(BlockNum, ReloBlock, isBitFlip, 1); |
| } |
| |
| UINT_T FindBBT_LegacyExt_Order(UINT_T AbbtBlock, FlashBootType_T fbt) |
| { |
| UINT_T PageSize, BlkSize, NumPages; |
| UINT_T *pBuffer1 = 0; //MAX_PAGE_SIZE_WORDS |
| UINT_T *pBuffer2 = 0; //MAX_PAGE_SIZE_WORDS |
| P_ABBT_Table_T pTempBBT = NULL, pTempBBT1, pTempBBT2; |
| P_FMProperties_T pFMProp = GetFMProperties(); |
| P_FlashProperties_T pFlashProp = GetFlashProperties(fbt); |
| UINT_T CurrentPageOffset; |
| UINT_T Retval1, Retval2; |
| INT_T CurrentSlot; |
| INT_T Low_Valid, High_Valid; |
| INT_T Order_Reverse = 1; |
| |
| PageSize = pFlashProp->PageSize; |
| BlkSize = pFlashProp->BlockSize; |
| NumPages = BlkSize / PageSize; |
| |
| pBuffer1 = malloc(PageSize); |
| pBuffer2 = malloc(PageSize); |
| if(pBuffer1 == NULL || pBuffer2 == NULL) |
| return HeapExhaustedError; |
| |
| pTempBBT1 = (P_ABBT_Table_T)pBuffer1; |
| pTempBBT2 = (P_ABBT_Table_T)pBuffer2; |
| |
| //read the first slot |
| CurrentSlot = 0; |
| do { |
| CurrentPageOffset = ((UINT_T)CurrentSlot * PageSize) + AbbtBlock*BlkSize; |
| Retval1 = ReadFlash(CurrentPageOffset, (UINT_T)pTempBBT1, PageSize, fbt); |
| if (Retval1 == NoError) |
| break; |
| } while (++CurrentSlot < NumPages); |
| |
| //read the last slot |
| Retval2 = ReadFlash((BlkSize - PageSize) + AbbtBlock*BlkSize, |
| (UINT_T)pTempBBT2, PageSize, fbt); |
| CurrentSlot = NumPages - 1; |
| do { |
| CurrentPageOffset = ((UINT_T)CurrentSlot * PageSize) + AbbtBlock*BlkSize; |
| Retval2 = ReadFlash(CurrentPageOffset, (UINT_T)pTempBBT2, PageSize, fbt); |
| if (Retval2 == NoError) |
| break; |
| } while (--CurrentSlot >= 0); |
| |
| if((Retval1 == NoError) && (pTempBBT1->Identifier == BBT_TYPE_ASR)) |
| Low_Valid = 1; |
| else |
| Low_Valid = 0; |
| |
| if((Retval2 == NoError) && (pTempBBT2->Identifier == BBT_TYPE_ASR)) |
| High_Valid = 1; |
| else |
| High_Valid = 0; |
| |
| if (Low_Valid && !High_Valid) { |
| Order_Reverse = 0; |
| } else if (!Low_Valid && High_Valid) { |
| Order_Reverse = 1; |
| } else if (Low_Valid && High_Valid) { |
| if (pTempBBT1->RefCounter < pTempBBT2->RefCounter) |
| Order_Reverse = 0; |
| else |
| Order_Reverse = 1; |
| } else { |
| obm_printf("ERR: No valid ABBT in block %d\n\r", AbbtBlock); |
| free(pBuffer1); |
| free(pBuffer2); |
| return BBTReadError; |
| } |
| |
| obm_printf("ABBT order: %s\n\r", Order_Reverse ? "reverse" : "positive"); |
| pFMProp->ABBT_Reverse = Order_Reverse; |
| free(pBuffer1); |
| free(pBuffer2); |
| return NoError; |
| } |
| |
| UINT_T FindBBT_LegacyExt(UINT_T BlockOffset, FlashBootType_T fbt) |
| { |
| UINT_T Retval, Retval1, Retval2; |
| UINT_T i, Slot1 = 0, Slot2 = 0, StartSlot, EndSlot, NewABBTState; |
| UINT_T AbbtBlock0, AbbtBlock1, ReloBlock, Block2Update = INVALID_BLOCK; |
| UINT_T PageSize, BlkSize, NumPages, id, CurSlot = INVALID_PAGE, Retry = TRUE; |
| UINT_T *pBuffer1 = 0; //MAX_PAGE_SIZE_WORDS |
| UINT_T *pBuffer2 = 0; //MAX_PAGE_SIZE_WORDS |
| P_ABBT_Table_T pTempBBT = NULL, pTempBBT1, pTempBBT2; |
| P_FMProperties_T pFMProp = GetFMProperties(); |
| P_FlashProperties_T pFlashProp = GetFlashProperties(fbt); |
| EraseFlash_F erase = pFlashProp->EraseFlash; |
| WriteFlash_F write = pFlashProp->WriteToFlash; |
| P_ReloTable_T pBBT; |
| P_ABBT_Table_T pABBT; |
| INT_T NeedEraseNext = 0; |
| INT_T CrcError1 = 0, CrcError2 = 0; |
| INT_T BBTNotFound = 0; |
| |
| //If this flash device does not use BBM, just return |
| if(pFlashProp->FlashSettings.UseBBM == 0) |
| return NoError; |
| |
| SetABBTState(BBT_INVALID, fbt); |
| |
| Retval = FindBBT_Legacy(BlockOffset, fbt); |
| //ScanBBT needs to be overwritten no matter if BBT is found |
| pFMProp->ScanBBT = &ScanBBT_LegacyExt; |
| |
| if(Retval != NoError){ |
| BBTNotFound = 1; |
| SetBBTState(BBT_INVALID, fbt); |
| } else { |
| //The first level BBT is valid now, go find the second level |
| //During this, the first level BBT could be updated, need to handle carefully |
| pBBT= pFMProp->pLBBT; |
| } |
| |
| // Now set some values that will work |
| PageSize = pFlashProp->PageSize; |
| BlkSize = pFlashProp->BlockSize; |
| NumPages = BlkSize / PageSize; |
| id = (UINT_T) BBT_TYPE_ASR; |
| |
| pBuffer1 = malloc(PageSize); |
| pBuffer2 = malloc(PageSize); |
| if(pBuffer1 == NULL || pBuffer2 == NULL) |
| return HeapExhaustedError; |
| |
| pTempBBT1 = (P_ABBT_Table_T)pBuffer1; |
| pTempBBT2 = (P_ABBT_Table_T)pBuffer2; |
| |
| if(pFMProp->pABBT != NULL){ |
| pABBT = pFMProp->pABBT; |
| AbbtBlock0 = pFMProp->ABBT_block0; |
| AbbtBlock1 = pFMProp->ABBT_block1; |
| } |
| else{ |
| pABBT = pFMProp->pABBT = (P_ABBT_Table_T)((UINT_T)pFM_SPACE + PageSize); |
| AbbtBlock0 = pFMProp->ABBT_block0 = pFlashProp->NumBlocks - 1; |
| AbbtBlock1 = pFMProp->ABBT_block1 = pFlashProp->NumBlocks - 2; |
| } |
| |
| #ifdef BBT_ORDER_REVERSE |
| pFMProp->ABBT_Reverse = 1; |
| #else |
| pFMProp->ABBT_Reverse = 0; |
| #endif |
| Retval1 = FindBBT_LegacyExt_Order(AbbtBlock0, fbt); |
| if (Retval1 != NoError) |
| Retval1 = FindBBT_LegacyExt_Order(AbbtBlock1, fbt); |
| |
| if (Retval1 != NoError) |
| goto creat_new_abbt; |
| |
| //Search second level BBT |
| if (pFMProp->ABBT_Reverse) { |
| StartSlot = NumPages - 1; |
| EndSlot = 0; |
| } else { |
| StartSlot = 0; |
| EndSlot = NumPages - 1; |
| } |
| |
| search_abbt: |
| Block2Update = INVALID_BLOCK; |
| //Search first block |
| Retval1 = BinarySearch((StartSlot * PageSize) + AbbtBlock0*BlkSize, |
| (EndSlot * PageSize) + AbbtBlock0*BlkSize, |
| (UINT_T)pTempBBT1, |
| &Slot1, |
| (UINT8_T*)&id, |
| 4, |
| 0, |
| fbt, |
| &CrcError1); |
| //search the backup block |
| Retval2 = BinarySearch((StartSlot * PageSize) + AbbtBlock1*BlkSize, |
| (EndSlot * PageSize) + AbbtBlock1*BlkSize, |
| (UINT_T)pTempBBT2, |
| &Slot2, |
| (UINT8_T*)&id, |
| 4, |
| 0, |
| fbt, |
| &CrcError2); |
| |
| NewABBTState = BBT_UNCHANGED; |
| if(Retval1 != NoError && Retval2 != NoError) { |
| creat_new_abbt: |
| if (BBTNotFound) |
| return NotFoundError; |
| |
| //As we found the father BBT, should reach here. |
| //Any way, restructure ABBT according to BBT |
| memset(pABBT, 0xFF, PageSize); |
| pABBT->Identifier = BBT_TYPE_ASR; |
| pABBT->Version = ABBT_VERSION_2001; |
| pABBT->Type = BBT_TYPE_MBBT_RUN; |
| pABBT->NumReloc = 0; |
| pABBT->RefCounter = 0; //RefCounter >= NumRelo |
| pABBT->NumBlockRecyled = 0; |
| pABBT->NumBlockToRecyle = 0; |
| pABBT->BackupBBTLoc = 0; |
| #if DUAL_TIM |
| if(DualTimEnabled()) { |
| pAsrDualTimInfo pDualTimInfo = GetDualTimInfo(); |
| pABBT->BackupBBTLoc = pDualTimInfo->BackupTimBlock * BlkSize; |
| } |
| #endif |
| for (i = 0; i < pBBT->NumReloc; i++){ |
| if(pBBT->Relo[i].From != BLOCK_BAD && pBBT->Relo[i].To != BLOCK_BAD) { |
| pABBT->Relo[pABBT->NumReloc].From = pBBT->Relo[i].From; |
| pABBT->Relo[pABBT->NumReloc].To = pBBT->Relo[i].To; |
| pABBT->NumReloc++; |
| } |
| } |
| UpdateABBTCrc(pABBT); |
| if (NeedEraseNext) |
| pFMProp->ABBT_CurSlot = INVALID_PAGE; |
| else { |
| pFMProp->ABBT_CurSlot = (pFMProp->ABBT_Reverse)? (NumPages - 1) : 0; |
| } |
| SetABBTState(BBT_CHANGED, fbt); |
| free(pBuffer1); free(pBuffer2); |
| return NoError; |
| }else if(Retval1 == NoError && Retval2 != NoError){ |
| //ABBT BLOCK0 has valid BBT, but ABBT Block1 doesn't |
| pTempBBT = pTempBBT1; |
| if (pFMProp->ABBT_Reverse) { |
| StartSlot = Slot1; |
| EndSlot = NumPages; |
| } else { |
| StartSlot = 0;//Slot1; |
| EndSlot = Slot1 + 1;//NumPages; |
| } |
| Block2Update = AbbtBlock1; |
| CurSlot = Slot1; |
| }else if(Retval1 != NoError && Retval2 == NoError){ |
| //ABBT BLOCK1 has valid BBT, but ABBT Block0 doesn't |
| pTempBBT = pTempBBT2; |
| if (pFMProp->ABBT_Reverse) { |
| StartSlot = Slot2; |
| EndSlot = NumPages; |
| } else { |
| StartSlot = 0;//Slot2; |
| EndSlot = Slot2 + 1;// NumPages; |
| } |
| Block2Update = AbbtBlock0; |
| CurSlot = Slot2; |
| }else{//Both ABBT BLOCK0 and ABBT BLOCK1 have valid BBT |
| //ABBT BLOCK0 has more recent BBT |
| if(Slot2 > Slot1){ |
| if (pFMProp->ABBT_Reverse) { |
| pTempBBT = pTempBBT1; |
| StartSlot = Slot1; |
| EndSlot = Slot2; |
| Block2Update = AbbtBlock1; |
| CurSlot = Slot1; |
| } else { |
| pTempBBT = pTempBBT2; |
| StartSlot = Slot1 + 1; |
| EndSlot = Slot2 + 1; |
| Block2Update = AbbtBlock0; |
| CurSlot = Slot2; |
| } |
| }else if(Slot2 < Slot1){ |
| if (pFMProp->ABBT_Reverse) { |
| pTempBBT = pTempBBT2; |
| StartSlot = Slot2; |
| EndSlot = Slot1; |
| Block2Update = AbbtBlock0; |
| CurSlot = Slot2; |
| } else { |
| pTempBBT = pTempBBT1; |
| StartSlot = Slot2 + 1; |
| EndSlot = Slot1 + 1; |
| Block2Update = AbbtBlock1; |
| CurSlot = Slot1; |
| } |
| }else{ |
| CurSlot = Slot1; |
| //Even both blocks have valid ABBT in same page, they may be different |
| if(pTempBBT1->RefCounter > pTempBBT2->RefCounter){ |
| pTempBBT = pTempBBT1; |
| NewABBTState = BBT_CHANGED; |
| }else if(pTempBBT1->RefCounter < pTempBBT2->RefCounter){ |
| pTempBBT = pTempBBT2; |
| NewABBTState = BBT_CHANGED; |
| }else{ |
| if(pTempBBT1->NumReloc > pTempBBT2->NumReloc){ |
| pTempBBT = pTempBBT1; |
| NewABBTState = BBT_CHANGED; |
| }else if(pTempBBT1->NumReloc < pTempBBT2->NumReloc){ |
| pTempBBT = pTempBBT2; |
| NewABBTState = BBT_CHANGED; |
| }else{ |
| pTempBBT = pTempBBT1; |
| } |
| } |
| } |
| } |
| |
| if(pTempBBT != NULL) |
| memcpy(pABBT, pTempBBT, PageSize); |
| |
| if (CurSlot != INVALID_PAGE) { |
| if (pFMProp->ABBT_Reverse) { |
| if (CurSlot == 0) |
| pFMProp->ABBT_CurSlot = INVALID_PAGE; |
| else |
| pFMProp->ABBT_CurSlot = CurSlot -1; |
| } |
| else { |
| if (CurSlot >= (NumPages - 1)) |
| pFMProp->ABBT_CurSlot = INVALID_PAGE; |
| else |
| pFMProp->ABBT_CurSlot = CurSlot + 1; |
| } |
| } else { |
| pFMProp->ABBT_CurSlot = INVALID_PAGE; |
| } |
| |
| //Set valid ABBT state, CHANGED or UNCHANGED, denpends on the search result |
| SetABBTState(NewABBTState, fbt); |
| |
| if ((CrcError2 == 0) && (CrcError1 == 0)) { |
| #ifdef BITFLIPS_SCRUBBING |
| //If Bitflip happens during read ABBT_Block0 |
| if(pFMProp->ABBT_State & ABBT_BITFLIP0){ |
| Retval = ScrubBlock_LegacyExt(ScanBBT_LegacyExt(AbbtBlock0), &ReloBlock); |
| if(Retval != NoError){ |
| obm_printf("abbt block0 bitflip, no relocation\n\r"); |
| Retval = NoError; |
| } |
| else |
| pFMProp->ABBT_State &= ~ABBT_BITFLIP0; |
| UpdateABBTCrc(pABBT); |
| } |
| |
| //If Bitflip happens during read ABBT_Block1 |
| if(pFMProp->ABBT_State & ABBT_BITFLIP1){ |
| Retval = ScrubBlock_LegacyExt(ScanBBT_LegacyExt(AbbtBlock1), &ReloBlock); |
| if(Retval != NoError){ |
| obm_printf("abbt block1 bitflip, no relocation\n\r"); |
| Retval = NoError; |
| } |
| else |
| pFMProp->ABBT_State &= ~ABBT_BITFLIP1; |
| UpdateABBTCrc(pABBT); |
| } |
| #endif |
| |
| //Fill the holes of Block2Update |
| if((Block2Update != INVALID_BLOCK) && (StartSlot < EndSlot)) |
| { |
| Block2Update = ScanBBT_LegacyExt(Block2Update); |
| //Most time, we should not reach here |
| for(i = StartSlot; i < EndSlot; i++) |
| { |
| Retval = write(Block2Update*BlkSize+i*PageSize, (UINT_T)pTempBBT, PageSize, fbt); |
| if(Retval != NoError){ |
| if(Retry == TRUE){ |
| Retry = FALSE; |
| Retval = erase(Block2Update*BlkSize, BlkSize, fbt); |
| if(Retval == NoError) |
| { |
| if (pFMProp->ABBT_Reverse) { |
| // copy from start to block end |
| EndSlot = NumPages; |
| i = StartSlot - 1; //restart the copy, i++ |
| } else { |
| // copy from block 0 to latest slot |
| i = - 1; //restart the copy, i++ |
| } |
| continue; |
| } |
| } |
| Retry = TRUE; |
| Retval = RelocateBlock_LegacyExt(Block2Update, &Block2Update, 0); |
| UpdateABBTCrc(pABBT); |
| if(Retval != NoError){ |
| /* If Relocate fails, break the loop。 |
| * Block2Update doesn't have the latest invalid BBT |
| * but we can live with this. |
| */ |
| break; |
| } |
| else{ |
| if (pFMProp->ABBT_Reverse) { |
| // copy from start to block end |
| EndSlot = NumPages; |
| i = StartSlot - 1; //restart the copy, i++ |
| } else { |
| // copy from block 0 to latest slot |
| i = - 1; //restart the copy, i++ |
| } |
| } |
| } |
| } |
| } |
| } /* if ((CrcError2 == 0) && (CrcError1 == 0)) */ |
| |
| if (CrcError1 || CrcError2) { |
| pFMProp->ABBT_CurSlot = INVALID_PAGE; //erase due to CRC error |
| SetABBTState(BBT_CHANGED, BOOT_FLASH); |
| } |
| |
| free(pBuffer1); free(pBuffer2); |
| if (BBTNotFound) { |
| /* Restore BBT based on ABBT, corner case */ |
| CreateBBT_Legacy(NULL); |
| pBBT = pFMProp->pLBBT; |
| for (i = 0; i < pABBT->NumReloc; i++) { |
| if (IsLogicBlockInL1BBT(pABBT->Relo[i].From) ) { |
| pBBT->Relo[pBBT->NumReloc].From = pABBT->Relo[i].From; |
| pBBT->Relo[pBBT->NumReloc].To = pABBT->Relo[i].To; |
| pBBT->NumReloc ++; |
| } |
| } |
| } |
| return NoError; |
| } |
| |
| UINT_T ScrubBlock_LegacyExt(UINT_T BlockNum, UINT_T* ReloBlock) |
| { |
| UINT_T Retval = NoError; |
| |
| #ifdef BITFLIPS_SCRUBBING |
| UINT_T RelocateBlock; |
| INT_T i, ABBT_updated = FALSE; |
| P_FMProperties_T pFMProp = GetFMProperties(); |
| P_ReloTable_T pBBT = pFMProp->pLBBT; |
| P_ABBT_Table_T pABBT = pFMProp->pABBT; |
| P_FlashProperties_T pFlashProp = GetFlashProperties(BOOT_FLASH); |
| |
| if(pBBT == NULL || pFMProp->BBT_State == BBT_INVALID) |
| return NoError; |
| |
| /* If bitflip happens in Tim Blocks, do nothing. Block0 should be always good. |
| * If dual TIM is enabled, should ignore bitflips in block1(back up TIM) too. |
| */ |
| if(IsTimBlock(BlockNum)){ |
| obm_printf("Warning: bitflip in Tim Block%d\n\r", BlockNum); |
| #if DUAL_TIM |
| if (DualTimEnabled()) { |
| EraseBlockAndRestoreTIM(pFlashProp, BlockNum, 1); |
| } |
| #endif |
| return NoError; |
| } |
| |
| //When bitflip happens when reading ABBT from Flash(BBT is OK, but ABBT is not), |
| //then we don't handle it immediately, record the state and handle it when ABBT is OK, |
| //This should only happens during finding bbt from flash |
| if( (pABBT == NULL) || (GetABBTState(BOOT_FLASH) == BBT_INVALID) ) |
| { |
| *ReloBlock = BlockNum; |
| //Backward Search the block index |
| for(i = 0; i < pBBT->NumReloc; i++) |
| { |
| if((pBBT->Relo[i].To == BlockNum) && EntryIsReloPair(&pBBT->Relo[i])) |
| { |
| BlockNum = pBBT->Relo[i].From; |
| break; |
| } |
| } |
| if(pFMProp->ABBT_block0 == BlockNum){ |
| pFMProp->ABBT_State |= ABBT_BITFLIP0; |
| } |
| else if(pFMProp->ABBT_block1 == BlockNum){ |
| pFMProp->ABBT_State |= ABBT_BITFLIP1; |
| }else{ |
| //Most times, ABBT should be OK when bitflip happens, |
| //except when reading ABBT_block0 or ABBT_block1. |
| //Just return error here. |
| return BBTReadError; |
| } |
| return NoError; |
| } |
| |
| do{ |
| //Need back up before relocate |
| Retval = RelocateBlock_LegacyExt(BlockNum, ReloBlock, 1); |
| |
| }while(Retval == ProgramError); |
| |
| if (Retval == BBTToMuchBitflips) { |
| return NoError; |
| } |
| |
| if(Retval != NoError) |
| return Retval; |
| |
| RelocateBlock = *ReloBlock; |
| |
| //Check the recycled block info if already updated in RelocateBlock API |
| for(i = 0; i < pABBT->NumReloc; i++) |
| { |
| if((pABBT->BlkInfo[i].State == BLOCK_TO_RECYCLE) && |
| (pABBT->BlkInfo[i].Index == BlockNum)) |
| { |
| ABBT_updated = TRUE; |
| break; |
| } |
| } |
| |
| if(ABBT_updated == FALSE){ |
| pABBT->BlkInfo[pABBT->NumReloc].State = BLOCK_TO_RECYCLE; |
| pABBT->BlkInfo[pABBT->NumReloc].Index = BlockNum; |
| pABBT->NumReloc++; |
| } |
| |
| pABBT->NumBlockToRecyle++; |
| |
| /* record the bitflip times */ |
| for(i = 0; i < pABBT->NumReloc; i++) |
| { |
| if(pABBT->BlkInfo[i].State == BLK_FLIP_COUNT) |
| { |
| pABBT->BlkInfo[i].Index++; |
| break; |
| } |
| } |
| if (i == pABBT->NumReloc) { |
| pABBT->BlkInfo[pABBT->NumReloc].State = BLK_FLIP_COUNT; |
| pABBT->BlkInfo[pABBT->NumReloc].Index = 1; |
| pABBT->NumReloc ++; |
| } |
| |
| SetABBTState(BBT_CHANGED, BOOT_FLASH); |
| #endif |
| |
| return Retval; |
| } |
| |
| INT_T RawCopyFlashBlock(UINT_T BlockFrom, UINT_T BlockTo, INT_T NeedErase) |
| { |
| UINT_T Retval = NoError; |
| UINT_T BlkSize, PageSize, PageOff, SrcAddr, DstAddr; |
| P_FlashProperties_T pFlashProp = GetFlashProperties(BOOT_FLASH); |
| EraseFlash_F erase = pFlashProp->EraseFlash; |
| ReadFlash_F read = pFlashProp->ReadFromFlash; |
| WriteFlash_F write = pFlashProp->WriteToFlash; |
| UINT8_T *ptemp = 0; |
| |
| BlkSize = GetBlockSize(BOOT_FLASH); |
| PageSize = GetPageSize(BOOT_FLASH); |
| |
| DstAddr = BlockTo * BlkSize; |
| SrcAddr = BlockFrom * BlkSize; |
| |
| if (NeedErase) { |
| Retval = erase(DstAddr, BlkSize, BOOT_FLASH); |
| if(Retval != NoError) //retry again |
| Retval = erase(DstAddr, BlkSize, BOOT_FLASH); |
| if(Retval) |
| return EraseError; |
| } |
| |
| ptemp = malloc(pFlashProp->BlockSize); |
| if(ptemp == NULL) |
| return HeapExhaustedError; |
| |
| Retval = read(SrcAddr, ptemp, BlkSize, BOOT_FLASH); |
| if (Retval != NoError && Retval != ReadDisturbError){ |
| free(ptemp); |
| return ReadError; |
| } |
| |
| for (PageOff = 0; PageOff < BlkSize; PageOff += PageSize) { |
| if(CheckPattern(ptemp+PageOff, 0xFF, PageSize) == 1){ |
| continue; //skip the 0xFF pages |
| } |
| else |
| { |
| Retval = write(DstAddr+PageOff, ptemp+PageOff, PageSize, BOOT_FLASH); |
| if(Retval != NoError) { |
| free(ptemp); |
| return WriteError; |
| } |
| } |
| } |
| |
| free(ptemp); |
| return NoError; |
| } |
| |
| UINT_T RemapRecycledBlocks(void) |
| { |
| INT_T i, j, k; |
| UINT_T Retval = NoError; |
| P_FMProperties_T pFMProp = GetFMProperties(); |
| P_FlashProperties_T pFlashProp = GetFlashProperties(BOOT_FLASH); |
| P_ABBT_Table_T pABBT = pFMProp->pABBT; |
| P_ReloTable_T pBBT = pFMProp->pLBBT; |
| EraseFlash_F erase = pFlashProp->EraseFlash; |
| UINT_T BlkSize = GetBlockSize(BOOT_FLASH); |
| INT_T BlockToErase = -1; |
| |
| if (pFlashProp->FlashSettings.UseBBM == 0 || pABBT->NumReloc == 0) |
| return NoError; |
| |
| for ( i = 0; i < pABBT->NumReloc; i ++) |
| { |
| if(pABBT->BlkInfo[i].State == BLOCK_RECYCLED) { |
| for ( j = 0; j < pABBT->NumReloc; j++) |
| { |
| if (pABBT->Relo[j].From == pABBT->BlkInfo[i].Index) { |
| Retval = RawCopyFlashBlock(pABBT->Relo[j].To, pABBT->BlkInfo[i].Index, 1); |
| if(Retval == ReadError) { |
| Retval = NoError; |
| continue; /* Do nothing if read error, should never happens*/ |
| } |
| else if (Retval == EraseError || Retval == WriteError) { |
| pABBT->BlkInfo[i].State = BLOCK_BAD; |
| pABBT->RefCounter ++; |
| pABBT->NumBlockRecyled --; |
| pFMProp->ABBT_State = BBT_CHANGED; |
| UpdateBBT_LegacyExt(); |
| Retval = NoError; |
| } else { /* Retval == NoError */ |
| if(IsLogicBlockInL1BBT(pABBT->Relo[j].From)) { |
| for (k = 0; k <pBBT->NumReloc; k++) { |
| if(pBBT->Relo[k].From == pABBT->Relo[j].From) { |
| pBBT->Relo[k].From = BLOCK_BAD; |
| pBBT->Relo[k].To = BLOCK_BAD; |
| pFMProp->BBT_State = BBT_CHANGED; |
| break; |
| } |
| } |
| } |
| pABBT->BlkInfo[j].State = BLOCK_RECYCLED; |
| BlockToErase = pABBT->BlkInfo[j].Index; |
| pABBT->Relo[i].From = pABBT->Relo[pABBT->NumReloc-1].From; |
| pABBT->Relo[i].To = pABBT->Relo[pABBT->NumReloc-1].To; |
| pABBT->Relo[pABBT->NumReloc-1].From = 0xFFFF; |
| pABBT->Relo[pABBT->NumReloc-1].To = 0xFFFF; //delete an entry |
| pABBT->NumReloc --; |
| pABBT->RefCounter ++; |
| pFMProp->ABBT_State = BBT_CHANGED; |
| UpdateBBT_LegacyExt(); |
| i--; |
| if (BlockToErase > 0) { |
| Retval = erase(BlockToErase*BlkSize, BlkSize, BOOT_FLASH); |
| if(Retval != NoError) |
| Retval = erase(BlockToErase*BlkSize, BlkSize, BOOT_FLASH); |
| } |
| } |
| break; |
| } |
| } |
| } |
| } |
| |
| return Retval; |
| } |
| |
| UINT_T RecyleBlocks_LegacyExt(void) |
| { |
| UINT_T Retval = NoError; |
| #ifdef BITFLIPS_SCRUBBING |
| INT_T i; |
| P_FMProperties_T pFMProp = GetFMProperties(); |
| P_FlashProperties_T pFlashProp = GetFlashProperties(BOOT_FLASH); |
| P_ABBT_Table_T pABBT = pFMProp->pABBT; |
| |
| if(pFlashProp->FlashSettings.UseBBM == 0 || |
| pABBT->NumBlockToRecyle == 0) |
| return NoError; |
| for(i = 0; i < pABBT->NumReloc; i++) |
| { |
| if(pABBT->BlkInfo[i].State == BLOCK_TO_RECYCLE) |
| { |
| Retval = TortureBlock(pABBT->BlkInfo[i].Index); |
| if(Retval == NoError){ |
| pABBT->NumBlockRecyled ++; |
| pABBT->BlkInfo[i].State = BLOCK_RECYCLED; |
| } |
| else |
| pABBT->BlkInfo[i].State = BLOCK_RECYCLE_FAILED; |
| if(pABBT->NumBlockToRecyle) |
| pABBT->NumBlockToRecyle--; |
| } |
| } |
| SetABBTState(BBT_CHANGED, BOOT_FLASH); |
| if(pABBT->NumBlockToRecyle) |
| Retval = FlashBlockRecycleError; |
| else |
| Retval = NoError; |
| #endif |
| return Retval; |
| } |
| |
| static UINT8_T Patterns[] = {0xa5, 0x5a, 0x0}; |
| |
| INT_T CheckPattern(const VOID *buf, UINT8_T patt, INT_T size) |
| { |
| INT_T i; |
| |
| for (i = 0; i < size; i++) |
| if (((const UINT8_T *)buf)[i] != patt) |
| return 0; |
| return 1; |
| } |
| |
| INT_T TortureBlock(UINT_T BlockNum) |
| { |
| INT_T Retval = NoError, i, PattCount; |
| UINT_T BlockSize, BlockAddr; |
| UINT8_T *pBuffer = 0; |
| P_FlashProperties_T pFlashProp = GetFlashProperties(BOOT_FLASH); |
| BlockSize = pFlashProp->BlockSize; |
| EraseFlash_F erase = pFlashProp->EraseFlash; |
| ReadFlash_F read = pFlashProp->ReadFromFlash; |
| WriteFlash_F write = pFlashProp->WriteToFlash; |
| UINT_T TotalBlockNum = pFlashProp->NumBlocks; |
| |
| if(BlockNum >= pFlashProp->NumBlocks) { |
| obm_printf("Torture invalid block %d\n\r", BlockNum); |
| return InvalidAddressRangeError; |
| } |
| |
| PattCount = sizeof(Patterns)/sizeof(Patterns[0]); |
| BlockAddr = BlockNum*BlockSize; |
| |
| pBuffer = malloc(BlockSize); |
| if(pBuffer == 0) |
| return HeapExhaustedError; |
| |
| /*Here we need to use the low level erase/read/write interface without scan |
| *BBT, as the block itself is relocated to another one. |
| */ |
| for (i = 0; i < PattCount; i++){ |
| Retval = erase(BlockAddr, BlockSize, BOOT_FLASH); |
| if(Retval != NoError){ |
| free(pBuffer); |
| return Retval; |
| } |
| Retval = read(BlockAddr, (UINT_T)pBuffer, BlockSize, BOOT_FLASH); |
| if(Retval != NoError){ |
| free(pBuffer); |
| return Retval; |
| } |
| Retval = CheckPattern(pBuffer, 0xFF, BlockSize); |
| if(Retval == 0) {//Erased, but a non-0xFF byte found |
| free(pBuffer); |
| return EraseError; |
| } |
| /* Write a pattern and check it */ |
| memset(pBuffer, Patterns[i], BlockSize); |
| Retval = write(BlockAddr, (UINT_T)pBuffer, BlockSize, BOOT_FLASH); |
| if(Retval != NoError){ |
| free(pBuffer); |
| return Retval; |
| } |
| memset(pBuffer, ~Patterns[i], BlockSize); |
| Retval = read(BlockAddr, (UINT_T)pBuffer, BlockSize, BOOT_FLASH); |
| if(Retval != NoError){ |
| free(pBuffer); |
| return Retval; |
| } |
| Retval = CheckPattern(pBuffer, Patterns[i], BlockSize); |
| if(Retval == 0){ |
| free(pBuffer); |
| return FlashReadError; |
| } |
| Retval = erase(BlockAddr, BlockSize, BOOT_FLASH); |
| if(Retval != NoError){ |
| free(pBuffer); |
| return Retval; |
| } |
| } |
| if(i == PattCount) |
| Retval = NoError; |
| |
| free(pBuffer); |
| return Retval; |
| } |
| |
| void SCAN_FactoryBadBlockLegacyExt(void) |
| { |
| UINT16 i, badblocklist[100]; |
| UINT fBBNum, NewBlock, initial_blk, end_blk, num_relo; |
| P_FlashProperties_T pFlashProp = GetFlashProperties(BOOT_FLASH); |
| P_FMProperties_T pFMP = GetFMProperties(); |
| GenerateFBBT_F Gen_FBBT = pFlashProp->GenerateFBBT; |
| P_ABBT_Table_T pABBT = pFMP->pABBT; |
| P_ReloTable_T pBBT = pFMP->pLBBT; |
| |
| //return early if not supported |
| if( (Gen_FBBT == NULL) || (pFlashProp->FlashSettings.UseBBM == 0) ) |
| return; |
| |
| if((pBBT == NULL) || (pABBT == NULL) || ((UINT16)BBT_TYPE_LEGACY != pBBT->Header)) |
| return; |
| |
| //get RP boundaries |
| #if defined(NAND_CODE) || defined(SPINAND_CODE) |
| initial_blk = pFlashProp->NumBlocks - 1 - ABBT_BLOCK_NUM; |
| #endif |
| |
| end_blk = initial_blk - (pFlashProp->NumBlocks * LEGACY_BBM_RELOC_PERCENTAGE + 99) / 100; |
| |
| fBBNum = Gen_FBBT(badblocklist); |
| |
| num_relo = pABBT->NumReloc; |
| |
| for(i = 0; i < fBBNum; i++){ |
| //if the block is in reserved pool, just mark it as bad block |
| if(badblocklist[i] > end_blk && badblocklist[i] <= initial_blk) |
| { |
| pABBT->BlkInfo[pABBT->NumReloc].Index = badblocklist[i]; |
| pABBT->BlkInfo[pABBT->NumReloc].State = BLOCK_BAD; |
| pABBT->NumReloc++; |
| } |
| } |
| |
| if(num_relo != pBBT->NumReloc) |
| SetABBTState(BBT_CHANGED, BOOT_FLASH); |
| |
| for(i = 0; i < fBBNum; i++){ |
| if(badblocklist[i] <= end_blk || badblocklist[i] > initial_blk) |
| RelocateBlock_LegacyExt(badblocklist[i], &NewBlock, 0); |
| } |
| } |
| |
| static VOID DetectScrubBitflipBlock(UINT32 Block) |
| { |
| pIMAGE_INFO_3_4_0 pImageInfo; |
| UINT_T BlkSize, Retval, BlockNum, BlockToRecyle, i; |
| P_FlashProperties_T pFlashProp = GetFlashProperties(BOOT_FLASH); |
| EraseFlash_F erase = pFlashProp->EraseFlash; |
| ReadFlash_F read = pFlashProp->ReadFromFlash; |
| WriteFlash_F write = pFlashProp->WriteToFlash; |
| P_FMProperties_T pFMP = GetFMProperties(); |
| P_ABBT_Table_T pABBT = pFMP->pABBT; |
| UCHAR *pBuffer = 0; |
| |
| BlkSize = pFlashProp->BlockSize; |
| |
| #if NAND_CODE //To word around the issue that BootROM disable sprare area |
| if(Block == OBM_BLOCK) |
| DisableSpareAreaForOBM(Block*BlkSize, BlkSize); |
| #endif |
| BlockNum = ScanBBT_LegacyExt(Block); |
| pBuffer = malloc(BlkSize); |
| if(pBuffer == NULL) |
| return HeapExhaustedError; |
| |
| Retval = read(BlockNum*BlkSize, pBuffer, BlkSize, BOOT_FLASH); |
| if ( Retval == ReadDisturbError ) { |
| ScrubBlock_LegacyExt(BlockNum, &BlockNum); |
| SetBBTStates(BBT_CHANGED, BBT_CHANGED, BOOT_FLASH); |
| UpdateBBT(); |
| } |
| free(pBuffer); |
| |
| return; |
| } |
| |
| /* This is to detect the bitflip of the blocks which are only read by BootROM */ |
| VOID DetectScrubBitflipBlocks(VOID) |
| { |
| #if MMC_CODE |
| return; |
| #endif |
| |
| if(!DualTimEnabled()) |
| { |
| /* As BootROM can't transfer the read disturb error of OBM block to OBM, |
| * OBM read itself instead, if bitflip happens on OBM block, it will be handled, |
| * so that BootROM will not meet read disturb error when reading OBM. |
| * If Dual TIM is enabled, both OBM blocks are read in InitializeDualTimLate() |
| */ |
| DetectScrubBitflipBlock(OBM_BLOCK); /* OBM block */ |
| } |
| |
| return; |
| } |
| |
| UINT_T GetBadBlockNumExt(void) |
| { |
| UINT_T BlockNum; |
| INT_T i; |
| P_FMProperties_T pFMProp = GetFMProperties(); |
| P_ABBT_Table_T pABBT = pFMProp->pABBT; |
| |
| BlockNum = pABBT->NumReloc; |
| |
| for (i = 0; i < pABBT->NumReloc; ++i) |
| { |
| //The block which is recycled is good block now |
| if(pABBT->BlkInfo[i].State == BLOCK_RECYCLED) |
| BlockNum--; |
| } |
| |
| return BlockNum; |
| } |
| |
| void Dump_FM_Info(void) |
| { |
| INT_T i; |
| P_FMProperties_T pFMProp = GetFMProperties(); |
| P_ReloTable_T pBBT = pFMProp->pLBBT; |
| P_ABBT_Table_T pABBT = pFMProp->pABBT; |
| obm_printf("FM:\n\r"); |
| obm_printf("BBT_State: 0x%x\n\r", pFMProp->BBT_State); |
| obm_printf("ABBT_State: 0x%x\n\r", pFMProp->ABBT_State); |
| obm_printf("BBT_Slot: 0x%x\n\r", pFMProp->BBT_Slot); |
| obm_printf("BBT_NextSlot: 0x%x\n\r", pFMProp->BBT_NextSlot); |
| obm_printf("BBT_location: 0x%x\n\r", pFMProp->BBT_location); |
| obm_printf("BBT_Reverse: %d\n\r", pFMProp->BBT_Reverse); |
| obm_printf("ABBT_Reverse: %d\n\r", pFMProp->ABBT_Reverse); |
| obm_printf("ABBT_CurSlot: 0x%x\n\r", pFMProp->ABBT_CurSlot); |
| |
| obm_printf("BBT:\n\r"); |
| obm_printf("Header: 0x%x\n\r", pBBT->Header); |
| obm_printf("NumReloc: %d\n\r", pBBT->NumReloc); |
| for (i = 0; i < pBBT->NumReloc; i++) |
| { |
| obm_printf("%d: block %d ---> %d\n\r", i, pBBT->Relo[i].From, pBBT->Relo[i].To); |
| } |
| |
| obm_printf("ABBT:\n\r"); |
| obm_printf("Identifier: 0x%x\n\r", pABBT->Identifier); |
| obm_printf("Version: 0x%x\n\r", pABBT->Version); |
| obm_printf("Type: 0x%x\n\r", pABBT->Type); |
| obm_printf("RefCounter: %d\n\r", pABBT->RefCounter); |
| obm_printf("NumReloc: %d\n\r", pABBT->NumReloc); |
| obm_printf("NumBlockRecyled: %d\n\r", pABBT->NumBlockRecyled); |
| obm_printf("NumBlockToRecyle: %d\n\r", pABBT->NumBlockToRecyle); |
| |
| for (i = 0; i < pABBT->NumReloc; i++) |
| { |
| obm_printf("%d: block %d ---> %d\n\r", i, pABBT->Relo[i].From, pABBT->Relo[i].To); |
| } |
| } |
| |
| #endif |