| /****************************************************************************** |
| ** COPYRIGHT 2007 Marvell Inernational Ltd. |
| ** All Rights Reserved |
| ******************************************************************************/ |
| |
| #include "Typedef.h" |
| #include "FM.h" |
| #include "Partition.h" |
| #include "Errors.h" |
| #include "Flash.h" |
| #include "misc.h" |
| #include "loadoffsets.h" |
| |
| #if COPYIMAGESTOFLASH |
| #include "BootLoader.h" |
| #endif |
| |
| //keep a pointer that points the beginning of the UNUSED data space allocated for FM |
| UINT_T *pFM_SPACE = NULL; |
| UINT_T max_tim_size_byte = MAX_TIM_SIZE; |
| |
| //routine to get a pointer to the FM properties |
| FMProperties_T FMProperties; |
| |
| P_FMProperties_T GetFMProperties() |
| { |
| return &FMProperties; |
| } |
| |
| void SCAN_FactoryBadBlock() |
| { |
| #ifdef BBM_LEGACY_EXT |
| return SCAN_FactoryBadBlockLegacyExt(); |
| #else |
| 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(); |
| P_ReloTable_T pBBT = pFMP->pLBBT; |
| GenerateFBBT_F Gen_FBBT = pFlashProp->GenerateFBBT; |
| |
| //return early if not supported |
| if( (Gen_FBBT == NULL) || (pFlashProp->FlashSettings.UseBBM == 0) ) |
| return; |
| |
| if((pBBT == NULL) || ((UINT16)BBT_TYPE_LEGACY != pBBT->Header)) |
| return; |
| |
| //get RP boundaries |
| #if defined(NAND_CODE) || defined(SPINAND_CODE) |
| initial_blk = pFlashProp->NumBlocks - 1; |
| #endif |
| |
| end_blk = initial_blk - (pFlashProp->NumBlocks * LEGACY_BBM_RELOC_PERCENTAGE + 99) / 100; |
| |
| fBBNum = Gen_FBBT(badblocklist); |
| |
| num_relo = pBBT->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) |
| { |
| pBBT->Relo[pBBT->NumReloc].From = badblocklist[i]; |
| pBBT->Relo[pBBT->NumReloc].To = 0xFFFF; |
| pBBT->NumReloc++; |
| } |
| } |
| |
| if(num_relo != pBBT->NumReloc) |
| SetBBTState(BBT_CHANGED, BOOT_FLASH); |
| |
| for(i = 0; i < fBBNum; i++){ |
| if(badblocklist[i] <= end_blk) |
| RelocateBlock(badblocklist[i], &NewBlock, BOOT_FLASH); |
| } |
| #endif |
| } |
| |
| /* |
| * IntializeFM |
| * |
| * This function intializes the Flash Management, so as to allow use of Bad Block Management |
| * and a Partition Table. This function supports legacy BBM, as well as the new Marvell method. |
| * |
| * Inputs: |
| * FM_type - tells us which type of Flash Management we should search for (Legacy, Marvell, both, etc.) |
| * Outputs: none |
| * Returns: none |
| */ |
| void InitializeFM(FM_Method_T FM_type, FlashBootType_T fbt) |
| { |
| UINT_T Retval, j, PageSize; |
| P_FMProperties_T pFMProp = GetFMProperties(); |
| P_ReloTable_T pLBBT = NULL; |
| P_ABBT_Table_T pABBT = NULL; |
| UINT_T BBTMaxEntryNum = 0; |
| UINT_T DumpEntryNum = 0; |
| |
| PlatformInitFM(&max_tim_size_byte); |
| UINT_T BlockOffset = 0; |
| |
| #ifdef ENABLE_BBM |
| PageSize = GetPageSize(fbt); |
| pFM_SPACE = malloc(PageSize << 1); |
| |
| if(pFM_SPACE == NULL) |
| return HeapExhaustedError; |
| #ifdef BBM_LEGACY_EXT |
| InitL1BBTBlocks(); /* Initialize the blocks which are handled by L1 BBT */ |
| #endif |
| //if we only allow legacy method, just search for that |
| if(FM_type == LEGACY_METHOD) |
| { |
| #ifdef BBM_LEGACY_EXT |
| pFMProp->FindBBT = &FindBBT_LegacyExt; |
| #else |
| pFMProp->FindBBT = &FindBBT_Legacy; |
| #endif |
| |
| #if DUAL_TIM |
| if(DualTimEnabled()) { |
| BlockOffset = GetCurTimBlock() * GetBlockSize(fbt); |
| } |
| #endif |
| |
| Retval = pFMProp->FindBBT(BlockOffset, fbt); |
| #if COPYIMAGESTOFLASH |
| if(Retval != NoError) |
| { |
| #ifdef BBM_LEGACY_EXT |
| CreateBBT_LegacyExt(NULL); |
| #else |
| CreateBBT_Legacy(NULL); |
| #endif |
| #if defined(NAND_CODE) || defined(SPINAND_CODE) |
| //Retval = ScanNANDForBadBlocks(fbt); |
| SCAN_FactoryBadBlock(); |
| #endif |
| } |
| #endif |
| } |
| |
| UpdateBBT(); |
| |
| #ifdef BBM_LEGACY_EXT |
| pFMProp->ABBT_MaxNum = ( PageSize - ABBT_HEADER_SIZE) / sizeof(ReloPair_T) ; |
| obm_printf("ABBT max entry number: %d\n\r", pFMProp->ABBT_MaxNum); |
| |
| pLBBT = pFMProp->pLBBT; |
| pABBT = pFMProp->pABBT; |
| |
| BBTMaxEntryNum = ( PageSize - BBT_HEADER_SIZE) / sizeof(ReloPair_T) ; |
| |
| if (pLBBT == NULL || pLBBT->NumReloc == 0) { |
| obm_printf("BBT: Empty\n\r"); |
| }else{ |
| obm_printf("BBT: %d entry\n\r", pLBBT->NumReloc); |
| DumpEntryNum = (BBTMaxEntryNum < pLBBT->NumReloc) ? BBTMaxEntryNum : pLBBT->NumReloc; |
| for(j = 0; j < DumpEntryNum; j++) { |
| if (pLBBT->Relo[j].From != BLOCK_BAD || pLBBT->Relo[j].To != BLOCK_BAD) |
| obm_printf("%d: block %d ---> %d\n\r", j+1, pLBBT->Relo[j].From, pLBBT->Relo[j].To); |
| } |
| } |
| |
| if (pABBT == NULL || pABBT->NumReloc == 0) { |
| obm_printf("ABBT: Empty\n\r"); |
| } else { |
| obm_printf("ABBT: %d entry\n\r", pABBT->NumReloc); |
| DumpEntryNum = (pFMProp->ABBT_MaxNum < pABBT->NumReloc) ? (pFMProp->ABBT_MaxNum) : pABBT->NumReloc; |
| for(j = 0; j < DumpEntryNum; j++) { |
| obm_printf("%d: block %d ---> %d\n\r", j+1, pABBT->Relo[j].From, pABBT->Relo[j].To); |
| } |
| } |
| #endif |
| #endif |
| } |
| |
| //This function will write out any unwritten PT or BBT |
| void FinalizeFM(FlashBootType_T fbt) |
| { |
| UINT_T Retval, size; |
| P_FMProperties_T pFMProp = GetFMProperties(); |
| |
| //second, write out the PT - second conditional is a sanity check (can't overwrite TIM) |
| if( (pFMProp->PT_state == PT_NEW) && (pFMProp->PT_location > 0) ) |
| { |
| size = sizeof(PartitionTable_T) + pFMProp->pMPT->NumPartitions * sizeof(PartitionInfo_T); |
| WriteFlash(pFMProp->PT_location, (UINT)pFMProp->pMPT, size, fbt); |
| } |
| #ifdef ENABLE_BBM |
| //third, make sure the BBT is written out. This is for the case that the partition was already |
| //set to 0, and the outstanding BBT did not get written. NOTE: it must work this way, since |
| //the PT in Block 0 MUST be written before any other BBT in block 0 (MLC restriction on page burning) |
| #ifdef BBM_LEGACY_EXT |
| //In case that bitflips happen during booting, flush the BBT out before recycle and erase the bitflip blocks |
| UpdateBBT(); |
| |
| RecyleBlocks_LegacyExt(); |
| |
| UpdateBBT(); |
| |
| RemapRecycledBlocks(); |
| |
| UpdateBBT(); |
| #endif |
| //lastly, clear out the structure |
| ClearFM(fbt); |
| #endif |
| } |
| |
| /* |
| * ClearFM |
| * |
| * This function clears out the Flash Management data space and |
| * sets all state variables to 'invalid', so as to invalidate the |
| * Flash Management until is has been intialized. |
| */ |
| void ClearFM(FlashBootType_T fbt) |
| { |
| #ifdef ENABLE_BBM |
| UINT_T tempPartNum, i; |
| P_FMProperties_T pFMProp = GetFMProperties(); |
| |
| //Step 1: Set variables to a default state |
| //Clear out the properties structure (deletes stale data) |
| // '>> 2' converts byte total to DWord total |
| tempPartNum = pFMProp->PI_num; // Save this value as it was determined from flash driver |
| for (i = 0; i < (sizeof(FMProperties_T) >> 2); i++) |
| ((UINT_T*)pFMProp)[i] = 0; |
| pFMProp->PI_num = tempPartNum; // Restore value |
| |
| //invalidate the BBT and PT |
| SetBBTState(BBT_INVALID, fbt); |
| pFMProp->PT_state = PT_INVALID; |
| |
| //Clear out our global area space |
| if(pFM_SPACE) { |
| free(pFM_SPACE); |
| pFM_SPACE = NULL; |
| } |
| #endif |
| } |
| |
| /* LocateBlock |
| * |
| * This function using FM structures to determine if the input block |
| * has been relocated. It will check to see if legacy BBM is being used (and |
| * therefore use that BBT) or if Marvell method is being used. |
| * |
| * NOTE: this function assumes user already called SetPartition at a higher level |
| * |
| * Inputs: |
| * BlockNum: The block number to check. This block num is relative to |
| * the partition number |
| * fbt: Flash boot type - BOOT_FLASH or SAVE_STATE_FLASH |
| * Outputs: none |
| * Returns: The block number that the input relocates to (or the same number if the |
| * original block was not relocated) |
| */ |
| UINT_T LocateBlock(UINT_T BlockNum, FlashBootType_T fbt) |
| { |
| #ifdef ENABLE_BBM |
| UINT_T RetBlock; |
| P_FMProperties_T pFMProp = GetFMProperties(); |
| |
| //make sure this device uses BBM |
| if(GetFlashProperties(fbt)->FlashSettings.UseBBM == 0) |
| return BlockNum; |
| |
| //make sure a BBT was found, and scan function was linked |
| if(pFMProp->ScanBBT == NULL) |
| return BlockNum; |
| |
| //scan the BBT |
| RetBlock = pFMProp->ScanBBT(BlockNum); |
| //Return the relocated block |
| return RetBlock; |
| #else |
| return BlockNum; |
| #endif |
| } |
| |
| /* |
| * Relocate Block |
| * |
| * This function will take in a block number, call the correct RelocateBlock routine, |
| * and return with the new block num |
| * |
| * Inputs: |
| * BlockNum: The bad block to be put into the table |
| * fbt: Flash boot type |
| * Outputs: |
| * *NewBlock: sets the new block number |
| * Returns: No Error on a successful relocation |
| * NotSupportedError if the flash device does not use BBM |
| */ |
| UINT_T RelocateBlock(UINT_T BlockNum, UINT_T* NewBlock, FlashBootType_T fbt) |
| { |
| UINT_T Retval = NotSupportedError; |
| |
| #ifdef ENABLE_BBM |
| #if COPYIMAGESTOFLASH |
| //if this flash device does not support BBM, return with an error |
| if(GetFlashProperties(fbt)->FlashSettings.UseBBM == 0) |
| return NotSupportedError; |
| |
| //otherwise, call the relevant flash management Relocate routine |
| if(GetFMProperties()->BBT_type == BBT_TYPE_LEGACY) |
| #ifdef BBM_LEGACY_EXT |
| Retval = RelocateBlock_LegacyExt(BlockNum, NewBlock, 0); |
| #else |
| Retval = RelocateBlock_Legacy(BlockNum, NewBlock); |
| #endif |
| |
| #endif |
| |
| return Retval; |
| #else |
| *NewBlock = BlockNum; |
| return NoError; |
| #endif |
| } |
| |
| #if DUAL_TIM |
| static INT_T TimBlockEraseFlag = 0; |
| #endif |
| |
| void CheckFMEraseState(UINT_T BlockNum, FlashBootType_T fbt) |
| { |
| P_FMProperties_T pFMProp = GetFMProperties(); |
| |
| #ifdef ENABLE_BBM |
| //if we have a PT, and we are erasing block 0, mark that it needs to be reburnt |
| if((pFMProp->PT_state != PT_INVALID) && (BlockNum == 0)) |
| pFMProp->PT_state = PT_NEW; |
| |
| #if DUAL_TIM |
| UINT_T EraseTrigger = 1; |
| pAsrDualTimInfo pDualTimInfo = GetDualTimInfo(); |
| if(DualTimEnabled()) { |
| EraseTrigger = (1 << 0) | (1 << pDualTimInfo->BackupTimBlock); |
| } |
| |
| if((pFMProp->BBT_State != BBT_INVALID) && (0 == BlockNum || pDualTimInfo->BackupTimBlock == BlockNum) ) { |
| TimBlockEraseFlag |= (1 << BlockNum); |
| } |
| |
| if(TimBlockEraseFlag == EraseTrigger) { |
| SetBBTState(BBT_ERASED, fbt); |
| TimBlockEraseFlag = 0; |
| } |
| |
| #else |
| //if we have a BBT, check to see if the block containing the BBT is being erase |
| if((pFMProp->BBT_State != BBT_INVALID) && 0 == BlockNum ) |
| SetBBTState(BBT_ERASED, fbt); |
| #endif |
| |
| #endif |
| } |
| |
| /* |
| * SetBBTState |
| * |
| * This function is used to change tbe BBT state. This allows FM to keep |
| * track of whether or not to use the BBT, and if it needs to be re-written |
| * when the flash is shutdown. |
| */ |
| void SetBBTState(ReloState_T newState, FlashBootType_T fbt) |
| { |
| #ifdef ENABLE_BBM |
| UINT_T BlkSize, PageSize, TIMpagesUsed; |
| 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->BBT_Reverse) { |
| pFMProps->BBT_Slot = (BlkSize / PageSize) - 1; |
| pFMProps->BBT_NextSlot = pFMProps->BBT_Slot - 1; |
| } else { |
| TIMpagesUsed = (max_tim_size_byte + PageSize - 1) / PageSize; |
| pFMProps->BBT_Slot = TIMpagesUsed; |
| pFMProps->BBT_NextSlot = pFMProps->BBT_Slot + 1; |
| } |
| pFMProps->BBT_location = pFMProps->BBT_Slot * PageSize; |
| } |
| |
| pFMProps->BBT_State = newState; |
| #endif |
| } |
| |
| //this function creates a read disturb table in FM_SPACE... space |
| P_ReadDisturbList_T CreateReadDisturbList() |
| { |
| P_FMProperties_T pFMProp = GetFMProperties(); |
| |
| //if the list already exists, just return it |
| if(pFMProp->pReadDisturbList != NULL) |
| return pFMProp->pReadDisturbList; |
| |
| obm_printf("Create Read Disturb list\n\r"); |
| pFMProp->pReadDisturbList = malloc(sizeof(ReadDisturbList_T)); |
| if(pFMProp->pReadDisturbList == NULL) |
| return HeapExhaustedError; |
| //put structure into data (FM) space |
| //pFMProp->pReadDisturbList = (P_ReadDisturbList_T) DDR_NAND_DISTURBLIST_ADDR; // pFM_SPACE; |
| //pFM_SPACE += sizeof(ReadDisturbList_T); |
| |
| //initialize structure |
| pFMProp->pReadDisturbList->RDID = READ_DISTURB_LIST_ID; |
| pFMProp->pReadDisturbList->num_entries = 0; |
| |
| return pFMProp->pReadDisturbList; |
| } |
| |
| unsigned int AddReadDisturbEntry(unsigned int partition, unsigned int block) |
| { |
| unsigned int i; |
| ReadDisturbEntry_T entry; |
| P_ReadDisturbList_T pRDlist = GetFMProperties()->pReadDisturbList; |
| |
| //make sure we have a valid list |
| if(pRDlist == NULL) |
| pRDlist = CreateReadDisturbList(); |
| |
| entry.partition = partition; |
| entry.block = block; |
| |
| //parse list and return if the entry is already listed |
| for(i = 0; i < pRDlist->num_entries; i++) |
| if(entry.value == pRDlist->entries[i].value) |
| return NoError; |
| |
| //at this point, entry wasn't found. add it to list |
| pRDlist->entries[pRDlist->num_entries].value = entry.value; |
| pRDlist->num_entries++; |
| return NoError; |
| } |
| |
| |
| void ImportReadDisturbList(unsigned int import_list) |
| { |
| unsigned int i; |
| P_ReadDisturbList_T import = (P_ReadDisturbList_T) import_list; |
| |
| //no need to check if an internal table exists, AddReadDisturbEntry will check for us |
| |
| //need to parse thru entries in the import list |
| for (i = 0; i < import->num_entries; i++) |
| AddReadDisturbEntry(import->entries[i].partition, import->entries[i].block); |
| } |
| |
| /* SetPartition |
| * This function sets the FM structures to point to a particular partition. |
| * This includes updating the Partition Info structure in memory, and the runtime |
| * BBT (both from FLASH). If there is not partition table, this function just returns. |
| * |
| * Inputs: |
| * PartitionNum - partition number to set to |
| * fbt - flash boot type to use |
| * Outputs: none |
| * Returns: none |
| */ |
| |
| #if MMC_CODE |
| UINT_T SetPartition(UINT_T PartitionNum, FlashBootType_T fbt) |
| { |
| UINT_T Retval = NoError; |
| P_FlashProperties_T pFlashP = GetFlashProperties(fbt); |
| P_FMProperties_T pFMProp = GetFMProperties(); |
| |
| if(pFMProp->PI_num == PartitionNum) |
| return NoError; |
| |
| if(pFlashP->ChangePartition != NULL) |
| { |
| Retval = pFlashP->ChangePartition(PartitionNum, fbt); |
| pFMProp->PI_num = PartitionNum; |
| } |
| return Retval; |
| } |
| #endif |
| |
| /* GetPartitionOffset |
| * |
| * This function returns the logical offset of where the currently set partition begins. |
| * If no partition table exists OR no partition was set this function returns 0. |
| * |
| */ |
| UINT_T GetPartitionOffset(FlashBootType_T fbt) |
| { |
| P_FMProperties_T pFMProp = GetFMProperties(); |
| |
| //if we don't have a Partition table, return an offset of 0 |
| if(pFMProp->PT_state == PT_INVALID) |
| return 0; |
| |
| //if the partition was not set, return an offset of 0 |
| if(pFMProp->pPI == NULL) |
| return 0; |
| |
| return pFMProp->pPI->StartAddr; |
| |
| } |
| |
| /* CreateBBT_Legacy |
| * This function will creates a legacy BBT. If the Partition Info pointer is valid, use the values in |
| * it to create the BBT. If the Partition Info pointer is NULL, this function will create a default BBT |
| * that will get written after the TIM. |
| * |
| * NOTE!!:: To support older flash management, you can change the initial slot for the BBT from right after |
| * the TIM, to the last slot of block zero in this routine. |
| * Inputs: |
| * pPI |
| * Outputs: |
| * None |
| * |
| */ |
| void CreateBBT_Legacy(P_PartitionInfo_T pPI) |
| { |
| #ifdef ENABLE_BBM |
| #if COPYIMAGESTOFLASH |
| |
| int i; |
| UINT_T Retval = NoError; |
| UINT_T PageSize, BlkSize, mask, TIMpagesUsed; |
| |
| P_FMProperties_T pFMProp = GetFMProperties(); |
| P_ReloTable_T pLBBT = pFMProp->pLBBT; |
| |
| //If this flash device does not use BBM, just return |
| if(GetFlashProperties(BOOT_FLASH)->FlashSettings.UseBBM == 0) |
| return; |
| |
| //Initialize FMProperties structure: |
| SetBBTState(BBT_INVALID, BOOT_FLASH); |
| PageSize = GetPageSize(BOOT_FLASH); |
| BlkSize = GetBlockSize(BOOT_FLASH); |
| memset(pFM_SPACE, 0xFF, PageSize); |
| |
| 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 |
| // pFMProp->BBT_Slot = (PageSize >= max_tim_size_byte) ? 1 : max_tim_size_byte / PageSize; |
| #ifdef BBT_ORDER_REVERSE |
| pFMProp->BBT_Slot = (BlkSize / PageSize) - 1; |
| pFMProp->BBT_NextSlot = pFMProp->BBT_Slot - 1; |
| #else |
| TIMpagesUsed = (max_tim_size_byte + PageSize - 1) / PageSize; |
| pFMProp->BBT_Slot = TIMpagesUsed; |
| pFMProp->BBT_NextSlot = pFMProp->BBT_Slot + 1; |
| #endif |
| pFMProp->BBT_location = pFMProp->BBT_Slot * PageSize; |
| |
| // initialize the FMProps for the new BBT |
| SetBBTState(BBT_CHANGED, BOOT_FLASH); |
| |
| //increment our memory pointer (DWORD pointer!!) |
| // pFM_SPACE = (UINT*) ((UINT)pFM_SPACE + PageSize); |
| |
| #endif |
| #endif |
| return; |
| } |
| |
| |
| void UpdateBBT() |
| { |
| #ifdef ENABLE_BBM |
| #ifdef BBM_LEGACY_EXT |
| return UpdateBBT_LegacyExt(); |
| #else |
| #if COPYIMAGESTOFLASH |
| UINT_T addr = 0; |
| UINT_T Retval, BlkSize, PageSize, NumPages, TIMpagesUsed, bbt; |
| P_FMProperties_T pFMProps = GetFMProperties(); |
| P_ReloTable_T pBBT; |
| P_FlashProperties_T pFlashProp = GetFlashProperties(BOOT_FLASH); |
| UINT_T maxBlockNum, i; |
| UINT8_T *ptemp = NULL; |
| |
| //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) |
| return; |
| |
| //if we never found a BBT, just return |
| if(pFMProps->BBT_State == BBT_INVALID) |
| return; |
| |
| // Now set some values that will work |
| PageSize = GetPageSize(BOOT_FLASH); |
| BlkSize = GetBlockSize(BOOT_FLASH); |
| |
| ptemp = malloc(BlkSize); |
| if (ptemp == NULL) |
| return HeapExhaustedError; |
| |
| |
| //are we burning to block 0, where TIM resides?? |
| if(pFMProps->BBT_location < BlkSize) |
| { |
| //Find and read first page after the TIM |
| TIMpagesUsed = (max_tim_size_byte + PageSize - 1) / PageSize; |
| |
| NumPages = BlkSize / PageSize; |
| |
| //Did we run out of BBT Slots? |
| if((pFMProps->BBT_Slot < TIMpagesUsed) || pFMProps->BBT_Slot >= NumPages) |
| { |
| // ASSUMES ONLY TIM and BBT are located in block 0 |
| //save data in lower portion of block 0 |
| Retval = ReadFlash(0, ptemp, max_tim_size_byte, BOOT_FLASH); |
| if(Retval != NoError){ |
| free(ptemp); |
| return; |
| } |
| |
| //erase all of block 0 |
| Retval = EraseFlash(0, BlkSize, BOOT_FLASH); |
| if(Retval != NoError){ |
| free(ptemp); |
| return; |
| } |
| |
| //restore TIM data in lower portion of block 0 |
| Retval = WriteFlash(0, ptemp, max_tim_size_byte, BOOT_FLASH); |
| if(Retval != NoError){ |
| free(ptemp); |
| return; |
| } |
| |
| 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; |
| } |
| } |
| |
| if(pFMProps->BBT_type == BBT_TYPE_LEGACY) |
| bbt = (UINT)pFMProps->pLBBT; |
| else |
| bbt = (UINT)pFMProps->pRBBT; |
| |
| |
| //get the block offset from BBT_location |
| addr = pFMProps->BBT_location & ~(BlkSize - 1); |
| //get the page offset from SLot |
| addr |= pFMProps->BBT_Slot * PageSize; |
| |
| //write out BBT |
| WriteFlash(addr, bbt, PageSize, BOOT_FLASH); |
| |
| //update slot info |
| if (pFMProps->BBT_Reverse) { |
| pFMProps->BBT_Slot--; |
| pFMProps->BBT_NextSlot--; |
| } |
| else |
| { |
| pFMProps->BBT_Slot++; |
| pFMProps->BBT_NextSlot++; |
| } |
| |
| //set the state back to unchanged |
| SetBBTState(BBT_UNCHANGED, BOOT_FLASH); |
| |
| // make sure the related block is not out of range, 0 to max block number |
| #if defined(NAND_CODE) || defined(SPINAND_CODE) |
| maxBlockNum = pFlashProp->NumBlocks - 1; |
| #endif |
| |
| if(pFMProps->BBT_type == BBT_TYPE_LEGACY) |
| { |
| pBBT = pFMProps->pLBBT; |
| for (i = 0; i < pBBT->NumReloc; i++) |
| { |
| if ((pBBT->Relo[i].From > maxBlockNum) || (pBBT->Relo[i].To > maxBlockNum)) |
| { |
| obm_printf("BBT out of range, %s %d !!!\n\r", __FUNCTION__, __LINE__); |
| FatalError(BBTOUTOFRANGE, NULL, NULL); |
| } |
| } |
| } |
| |
| free(ptemp); |
| #endif |
| return; |
| #endif |
| #endif |
| } |
| |
| |
| /* ScanBBT_Legacy - uses Legacy BBT structure |
| * This function will take in a Block Number and return with the relocated |
| * block number. If the block is not relocated, then it will simply return |
| * with the original block number |
| * |
| * Inputs: |
| * BlockNum: The block number to look up in the relocation table |
| * Outputs: |
| * None |
| * Retval value: Relocated block number |
| */ |
| UINT_T ScanBBT_Legacy(UINT_T BlockNum) |
| { |
| P_FMProperties_T pFMProp = GetFMProperties(); |
| P_ReloTable_T pBBT = pFMProp->pLBBT; |
| UINT_T i; |
| |
| //if no BBT was loaded, return original block num |
| if(pFMProp->BBT_State == BBT_INVALID) |
| return BlockNum; |
| |
| //block 0 is guaranteed by manufacture |
| if(BlockNum == 0) |
| return 0; |
| |
| // Has this block been relocated? If so we'll need to use the relocated block |
| for (i = 0; i < pBBT->NumReloc; ) |
| { |
| if (pBBT->Relo[i].From == BlockNum) |
| { |
| BlockNum = pBBT->Relo[i].To; |
| i = 0; // Start over in case remapped block is now bad |
| continue; |
| } |
| i++; |
| } |
| return BlockNum; |
| } |
| |
| /* |
| * RelocateBlock_Legacy |
| */ |
| UINT_T RelocateBlock_Legacy(UINT_T BlockNum, UINT_T* ReloBlock) |
| { |
| UINT_T BlockRelocatedTo; |
| UINT_T num_relo, BlkSize, initial_blk, end_blk, i; |
| P_FMProperties_T pFMP = GetFMProperties(); |
| P_ReloTable_T pBBT = pFMP->pLBBT; |
| P_FlashProperties_T pFlashProp = GetFlashProperties(BOOT_FLASH); |
| |
| //if our BBT is not valid |
| if((pBBT == NULL) || ((UINT16)BBT_TYPE_LEGACY != pBBT->Header)) |
| return BBTReadError; |
| |
| //block 0 is guaranteed by manufacture: DO NOT RELOCATE IT |
| if(BlockNum == 0) |
| { |
| *ReloBlock = 0; |
| return NoError; |
| } |
| |
| BlkSize = GetBlockSize(BOOT_FLASH); |
| num_relo = pBBT->NumReloc; |
| |
| //get RP boundaries |
| /* |
| * |
| * GigaDevice GD5F1GQ4RAYIG: |
| * |
| * Some blocks are reserved for security consideration. All operations to these |
| * blocks are prohibited. When erase or program command is issued to these blocks, |
| * the feature bit E_FAIL or P_FAIL will be set to high to reports the failure |
| * status of the two operations. If a page read operation is issued to these blocks, |
| * no data is read out from the blocks. |
| * |
| * These blocks are listed as follows: |
| * |
| * For 1Gbit: block 1024~1021. |
| * |
| */ |
| #if defined(NAND_CODE) || defined(SPINAND_CODE) |
| initial_blk = pFlashProp->NumBlocks - 1; |
| #endif |
| end_blk = initial_blk - (pFlashProp->NumBlocks * LEGACY_BBM_RELOC_PERCENTAGE + 99) / 100; |
| |
| //start at first RP block |
| BlockRelocatedTo = initial_blk; |
| |
| //if the table is not empty, we need to find the next available RP block |
| if (num_relo != 0) |
| { |
| //run through all the entries to find that smallest 'To' entry |
| for(i = 0; i < pBBT->NumReloc; i++) |
| if(BlockRelocatedTo > pBBT->Relo[i].To) |
| BlockRelocatedTo = pBBT->Relo[i].To; |
| |
| //since we just found the last 'used' RP block, try the next one |
| BlockRelocatedTo--; |
| |
| //now we have to make sure this block isn't marked factory bad [From X, To -1] |
| i = 0; |
| do { |
| if(pBBT->Relo[i].From == BlockRelocatedTo) |
| { //this block is bad. |
| BlockRelocatedTo--; // try next one |
| i = 0; // restart search |
| continue; |
| } |
| i++; |
| } while(i < pBBT->NumReloc); |
| |
| //lastly, make sure we didn't exceed RP |
| if(BlockRelocatedTo <= end_blk) |
| { |
| obm_printf("BBT Exhausted Error, %s %d\n\r", __FUNCTION__, __LINE__); |
| return BBTExhaustedError; |
| } |
| } |
| |
| |
| SetBBTState(BBT_CHANGED, BOOT_FLASH); |
| *ReloBlock = (UINT) BlockRelocatedTo; |
| // Relocate it |
| pBBT->Relo[num_relo].To = BlockRelocatedTo; |
| pBBT->Relo[num_relo].From = BlockNum; |
| pBBT->NumReloc++; |
| |
| return NoError; |
| } |
| |
| |
| /* FindBBT_Legacy |
| * This function searchs for the Legacy Bad Block Table (LBBT) within a block |
| * It will start by checking the first and last page for BBTs. If both are found, it |
| * will load the one with the most relocations. if only one is found, it will setup a |
| * search in that direction. If neither are found, it will return with an error. |
| * |
| * If a BBT is found, the FM structure will be loaded with the relevant BBT data. |
| * |
| * Inputs: |
| * Flash Boot Type |
| * Outputs: |
| * None |
| * |
| * NOTE: This function will update the Flash Management Properties structure |
| */ |
| UINT_T FindBBT_Legacy(UINT_T BlockOffset, FlashBootType_T fbt) |
| { |
| #ifdef ENABLE_BBM |
| UINT_T Retval1, Retval2; |
| UINT_T Slot, StartSlot, EndSlot, Block2; |
| UINT_T PageSize, BlkSize, NumPages, id; |
| UINT_T pBuffer[MAX_PAGE_SIZE_WORDS]; |
| P_ReloTable_T pTempBBT = (P_ReloTable_T)pBuffer; |
| P_FMProperties_T pFMProp = GetFMProperties(); |
| P_ReloTable_T pBBT = pFMProp->pLBBT; |
| INT_T CrcError1, CrcError2; |
| UINT_T BRSlot; |
| |
| #if DUAL_TIM |
| UINT_T Slot2, BlockOffset2; |
| pAsrDualTimInfo pDualTimInfo; |
| #endif |
| |
| //If this flash device does not use BBM, just return |
| if(GetFlashProperties(fbt)->FlashSettings.UseBBM == 0) |
| return NoError; |
| CrcError1 = CrcError2 = 0; |
| // Now set some values that will work |
| PageSize = GetPageSize(fbt); |
| BlkSize = GetBlockSize(fbt); |
| NumPages = BlkSize / PageSize; |
| id = (UINT_T) BBT_TYPE_LEGACY; |
| |
| //we need to get a pointer of where to put the BBT |
| //if we already had a BBT (from another partition), reuse the same pointer |
| if(pFMProp->pLBBT != NULL) |
| pBBT = pFMProp->pLBBT; |
| else |
| {//if we never had a BBT, get the next spot in the global space |
| pBBT = pFMProp->pLBBT = (P_ReloTable_T)pFM_SPACE; |
| } |
| |
| //invalidate state |
| SetBBTState(BBT_INVALID, fbt); |
| //null out, to ignore stale data |
| pBBT->Header = NULL; |
| //set the scan to Legacy BBTs. If we dont' find a BBT, it'll get cleared below |
| pFMProp->ScanBBT = &ScanBBT_Legacy; |
| pFMProp->BBT_location = BlockOffset; |
| |
| Slot = PageSize >= max_tim_size_byte ? 1 : max_tim_size_byte / PageSize; |
| |
| //read the first slot |
| Retval1 = ReadFlash((Slot * PageSize) + BlockOffset, (UINT_T)pBBT, PageSize, fbt); |
| //read the last slot |
| Retval2 = ReadFlash((BlkSize - PageSize) + BlockOffset, (UINT_T)pTempBBT, PageSize, fbt); |
| |
| pFMProp->BBT_Reverse = 1; |
| //first slot contains BBT |
| if((Retval1 == NoError) && (pBBT->Header == (USHORT)BBT_TYPE_LEGACY)) |
| if((Retval2 == NoError) && (pTempBBT->Header == (USHORT)BBT_TYPE_LEGACY)) |
| //start slot = good BBT end slot = good BBT |
| { //figure out which one is "more" valid |
| if(pBBT->NumReloc < pTempBBT->NumReloc) |
| { //grows upward |
| memcpy(pBBT, pTempBBT, PageSize); |
| pFMProp->BBT_location += Slot * PageSize; |
| pFMProp->BBT_Reverse = 0; |
| StartSlot = Slot; |
| EndSlot = NumPages - 1; |
| Slot = NumPages - 1; |
| } |
| else |
| { //grows downward |
| pFMProp->BBT_location += (NumPages - 1) * PageSize; |
| pFMProp->BBT_Reverse = 1; |
| EndSlot = Slot; |
| StartSlot = NumPages - 1; |
| } |
| } |
| else //start slot = good BBT end slot = no BBT |
| { //set search params for "upward" search |
| StartSlot = Slot; |
| EndSlot = NumPages - 2; |
| pFMProp->BBT_location += Slot * PageSize; |
| pFMProp->BBT_Reverse = 0; |
| } |
| else //first slot does NOT contain BBT |
| if((Retval2 == NoError) && (pTempBBT->Header == (USHORT)BBT_TYPE_LEGACY)) |
| //start slot = NO BBT end slot = good BBT |
| { //move the found BBT into the allocated space |
| memcpy(pBBT, pTempBBT, PageSize); |
| //set search params for "downward" search |
| StartSlot = NumPages - 1; |
| EndSlot = Slot + 1; |
| Slot = NumPages - 1; //remember where we found the first BBT |
| pFMProp->BBT_location += Slot * PageSize; |
| pFMProp->BBT_Reverse = 1; |
| } |
| else |
| { //if neither slot have a good BBT, clear out pointers and return error |
| //pFMProp->ScanBBT = NULL; |
| #if DUAL_TIM |
| if (!DualTimEnabled()) { //for dual tim, still have another chance to get BBT |
| pFMProp->pLBBT = NULL; |
| return BBTReadError; |
| } |
| else{ |
| /* TODO: Dual TIM use positive BBT by default */ |
| #ifdef BBT_ORDER_REVERSE |
| pFMProp->BBT_Reverse = 1; |
| EndSlot = Slot; |
| StartSlot = NumPages - 1; |
| #else |
| pFMProp->BBT_Reverse = 0; |
| StartSlot = Slot; |
| EndSlot = NumPages - 1; |
| #endif |
| Retval1 = NotFoundError; |
| goto search_bbt_in_backup_tim; |
| } |
| #else |
| pFMProp->pLBBT = NULL; |
| return BBTReadError; |
| #endif |
| } |
| // do the binary search |
| Retval1 = BinarySearch((StartSlot * PageSize) + BlockOffset, |
| (EndSlot * PageSize) + BlockOffset, |
| (UINT_T)pTempBBT, |
| &Slot, |
| (UINT8_T*)&id, |
| 2, |
| 0, |
| fbt, |
| 0); /* don't check crc first time */ |
| |
| if(Retval1 == NoError) { |
| /* This BBT works for BootROM to boot OBM here */ |
| memcpy(pBBT, pTempBBT, PageSize); |
| BRSlot = Slot; |
| |
| // do the binary search with CRC check |
| Retval1 = BinarySearch((StartSlot * PageSize) + BlockOffset, |
| (EndSlot * PageSize) + BlockOffset, |
| (UINT_T)pTempBBT, |
| &Slot, |
| (UINT8_T*)&id, |
| 2, |
| 0, |
| fbt, |
| &CrcError1); |
| //if binary search found a more recent BBT, copy it to the allocated space |
| if(Retval1 == NoError) |
| memcpy(pBBT, pTempBBT, PageSize); |
| else |
| obm_printf("All BBTs in boot TIM block CRC error\n\r"); |
| } |
| |
| //set slot information |
| if (pFMProp->BBT_Reverse) { |
| pFMProp->BBT_Slot = BRSlot - 1; |
| pFMProp->BBT_NextSlot = BRSlot - 2; |
| } |
| else { |
| pFMProp->BBT_Slot = BRSlot + 1; |
| pFMProp->BBT_NextSlot = BRSlot + 2; |
| } |
| |
| SetBBTState(BBT_UNCHANGED, fbt); |
| #if DUAL_TIM |
| search_bbt_in_backup_tim: |
| pDualTimInfo = GetDualTimInfo(); |
| Block2 = (BlockOffset == 0) ? (pDualTimInfo->BackupTimBlock) : 0; |
| if (DualTimEnabled() && !IsBlockRelocated(Block2)) { |
| BlockOffset2 = Block2 * BlkSize; |
| memset(pTempBBT, 0, sizeof(ReloTable_T)); |
| Retval2 = BinarySearch((StartSlot * PageSize) + BlockOffset2, |
| (EndSlot * PageSize) + BlockOffset2, |
| (UINT_T)pTempBBT, |
| &Slot2, |
| (UINT8_T*)&id, |
| 2, |
| 0, |
| fbt, |
| &CrcError2); |
| if (Retval2 != NoError) |
| pTempBBT->Header = NULL; |
| |
| if (Retval1 == NoError) { |
| if (Retval2 == NoError) |
| { |
| if ((pTempBBT->NumReloc != pBBT->NumReloc) || |
| (Slot != Slot2) || CrcError1 || CrcError2) |
| { |
| SetBBTState(BBT_NEED_SYNC, fbt); |
| } |
| if (pTempBBT->NumReloc > pBBT->NumReloc) |
| memcpy(pBBT, pTempBBT, PageSize); |
| } else { |
| if (!IsBlockRelocated(pDualTimInfo->BackupTimBlock)) |
| SetBBTState(BBT_NEED_SYNC, fbt); |
| } |
| } else { |
| if (Retval2 == NoError) { |
| memcpy(pBBT, pTempBBT, PageSize); |
| if (!IsBlockRelocated(0)) |
| SetBBTState(BBT_NEED_SYNC, fbt); |
| } else { |
| if (pBBT->Header != (UINT16)BBT_TYPE_LEGACY){ |
| return BBTReadError; |
| } |
| else { |
| /* TODO: live with a corrupt BBT */ |
| SetBBTState(BBT_NEED_SYNC, fbt); |
| } |
| } |
| } |
| UpdateBBT(); |
| } |
| #else |
| if (BRSlot != Slot || Retval1 != NoError) { |
| SetBBTState(BBT_CHANGED, fbt); |
| } |
| #endif |
| |
| pFMProp->BBT_type = BBT_TYPE_LEGACY; |
| obm_printf("BBT order: %s\n\r", pFMProp->BBT_Reverse ? "reverse" : "positive"); |
| |
| if (pBBT->Header != (UINT16)BBT_TYPE_LEGACY) |
| return BBTReadError; |
| #endif |
| |
| return NoError; |
| } |
| |
| /* Binary Search Function |
| * This function searchs within a flash block for the "last" page with a matching MagicNumber at |
| * an offset into the page as specified by the MagicNumOffset input. The page is then loaded |
| * into memory at the address specified by the buffer pointer input. The direction |
| * of the search is determined by the values of the StartAddr and EndAddr inputs. |
| * |
| * Example |
| * |
| * Inputs: |
| * StartAddr: Absolute address of first possible entry location |
| * EndAddr: Absolute address of the last possible entry location |
| * MagicNumber: Pointer to a value to be checked when searching the block |
| * MagicNumSize: Size, in bytes, of the magic word. |
| * MagicNumOffset: Byte offset into the page at with to check for the MagicNumber |
| * fbt: Flash Boot Type that we are to search |
| * Outputs: |
| * buffer: Pointer to where the page will be loaded |
| * SlotNum: Page number of the last found page |
| * |
| * Return Value: |
| * NoError if page found |
| * SlotNotFound if no valid page found |
| * |
| * NOTE: buffer MUST be allocated to a full flash Page, or else data corruption will occur |
| */ |
| UINT_T BinarySearch(UINT_T StartAddr, UINT_T EndAddr, UINT_T buffer, UINT_T* SlotNum, |
| UINT8_T *pMagicNumber, UINT_T MagicNumSize, UINT_T MagicNumOffset, FlashBootType_T fbt, |
| INT_T *CrcError) |
| { |
| #ifdef ENABLE_BBM |
| UINT_T Retval; |
| UINT_T PageSize, BlkSize, BlkOffset, CurrentPageOffset; |
| INT_T MaxSlot, MinSlot, CurrentSlot, LastFoundSlot, LastFoundSlot2; //page number inside the block |
| UINT8_T direction; // 0 = up, 1 = down |
| UINT8_T found = 0; // used on each page check: 0 = not found, 1 = found |
| if (CrcError) |
| *CrcError = 0; |
| |
| // Now set some values that will work |
| PageSize = GetPageSize(fbt); |
| BlkSize = GetBlockSize(fbt); |
| BlkOffset = StartAddr & ~(BlkSize - 1); |
| //chop off the block address, since we're only searching WITHIN a block |
| StartAddr &= (BlkSize - 1); |
| EndAddr &= (BlkSize - 1); |
| //set this to an invalid number. At the end, it will tell us if we found a good slot |
| LastFoundSlot = -1; |
| LastFoundSlot2 = -1; |
| |
| if(StartAddr > EndAddr) |
| { |
| MaxSlot = StartAddr / PageSize; |
| MinSlot = EndAddr / PageSize; |
| CurrentSlot = MaxSlot; |
| direction = 1; //search downward |
| } |
| else |
| { |
| MaxSlot = EndAddr / PageSize; |
| MinSlot = StartAddr / PageSize; |
| CurrentSlot = MinSlot; |
| direction = 0; //search upward |
| } |
| |
| //Now we must locate the last used slot. Do this by searching for the magic number. |
| // We start at the boundary (to minimize search time) and do a binary search to locate the last used slot. |
| // Note: found variable will be cleared on each iteration |
| for(;MinSlot <= MaxSlot; found = 0) |
| { |
| //get the flash address to read |
| CurrentPageOffset = ((UINT_T)CurrentSlot * PageSize) + BlkOffset; |
| |
| Retval = ReadFlash(CurrentPageOffset, buffer, PageSize, fbt); |
| |
| // Did we find a valid entry? |
| if((Retval == NoError) && (memcmp(((void *)(buffer + MagicNumOffset)), pMagicNumber, MagicNumSize) == 0)) |
| { |
| if (CrcError) { |
| if (CheckMagicBBT(*(uint32_t *)pMagicNumber, buffer)) { |
| //save the slot number |
| LastFoundSlot2 = CurrentSlot; |
| } else { |
| *CrcError = 1; |
| } |
| } |
| found = 1; |
| //save the slot number |
| LastFoundSlot = CurrentSlot; |
| } |
| |
| //if: searching upward and DO NOT find a slot -OR- searching downward and find a slot |
| // lower the max slot |
| //else: searching upward and find a slot -OR- downward and DO NOT find a slot |
| // raise minimum slot |
| if (found == direction) |
| //Set new Max slot to the next page below |
| MaxSlot = CurrentSlot - 1; |
| else |
| //Set new Min slot to the next page above |
| MinSlot = CurrentSlot + 1; |
| |
| //adjust current slot to midpoint of max and min to get next offset |
| CurrentSlot = (MaxSlot + MinSlot) >> 1; |
| } // End while |
| |
| //Check to make sure we even found a good slot |
| if (CrcError) { |
| LastFoundSlot = LastFoundSlot2; |
| } |
| |
| if(LastFoundSlot == -1) |
| Retval = NotFoundError; |
| else |
| { |
| *SlotNum = (UINT_T)LastFoundSlot; |
| //The last valid slot probably got overwritten during the search |
| //So we need to re-read the last valid entry in the buffer |
| CurrentPageOffset = ((UINT_T)LastFoundSlot * PageSize) + BlkOffset; |
| Retval = ReadFlash(CurrentPageOffset, buffer, PageSize, fbt); |
| } |
| return Retval; |
| #endif |
| } |
| |
| |
| UINT_T GetBadBlockNum(void) |
| { |
| #ifdef BBM_LEGACY_EXT |
| return GetBadBlockNumExt(); |
| #else |
| P_FMProperties_T pFMProps = GetFMProperties(); |
| P_ReloTable_T pBBT; |
| return pBBT->NumReloc; |
| #endif |
| } |