static error_t vfl_vsvfl_write_single_page(vfl_device_t *_vfl, uint32_t dwVpn, uint8_t* buffer, uint8_t* spare, int _scrub) { vfl_vsvfl_device_t *vfl = CONTAINER_OF(vfl_vsvfl_device_t, vfl, _vfl); uint32_t pCE = 0, pPage = 0; int ret; ret = virtual_page_number_to_physical(vfl, dwVpn, &pCE, &pPage); if(FAILED(ret)) { bufferPrintf("vfl_vsvfl_write_single_page: virtual_page_number_to_physical returned an error (dwVpn %d)!\r\n", dwVpn); return ret; } ret = nand_device_write_single_page(vfl->device, pCE, 0, pPage, buffer, spare); if(FAILED(ret)) { if(!vfl_check_checksum(vfl, pCE)) system_panic("vfl_vsfl_write_single_page: failed checksum\r\n"); vfl->contexts[pCE].write_failure_count++; vfl_gen_checksum(vfl, pCE); // TODO: add block map support // vsvfl_mark_page_as_bad(pCE, pPage, ret); if(_scrub) add_block_to_scrub_list(vfl, pCE, pPage / vfl->geometry.pages_per_block); // Something like that, I think return ret; } return SUCCESS; }
static error_t vsvfl_write_vfl_cxt_to_flash(vfl_vsvfl_device_t *_vfl, uint32_t _ce) { if(_ce >= _vfl->geometry.num_ce) return EINVAL; if(!vfl_check_checksum(_vfl, _ce)) system_panic("vsvfl_write_vfl_cxt_to_flash: failed checksum\r\n"); uint8_t* pageBuffer = memalign(0x40, _vfl->geometry.bytes_per_page); uint8_t* spareBuffer = memalign(0x40, _vfl->geometry.bytes_per_spare); if(pageBuffer == NULL || spareBuffer == NULL) { bufferPrintf("vfl: cannot allocate page and spare buffer\r\n"); return ENOMEM; } memset(pageBuffer, 0x0, _vfl->geometry.bytes_per_page); vfl_vsvfl_context_t *curVFLCxt = &_vfl->contexts[_ce]; curVFLCxt->usn_inc = _vfl->current_version++; uint32_t curPage = curVFLCxt->usn_page; curVFLCxt->usn_page += 8; curVFLCxt->usn_dec -= 1; vfl_gen_checksum(_vfl, _ce); memcpy(pageBuffer, curVFLCxt, 0x800); int i; for (i = 0; i < 8; i++) { memset(spareBuffer, 0xFF, _vfl->geometry.bytes_per_spare); ((uint32_t*)spareBuffer)[0] = curVFLCxt->usn_dec; spareBuffer[8] = 0; spareBuffer[9] = 0x80; uint32_t bankStart = (curVFLCxt->vfl_context_block[curVFLCxt->usn_block] / _vfl->geometry.blocks_per_bank) * _vfl->geometry.bank_address_space; uint32_t blockOffset = curVFLCxt->vfl_context_block[curVFLCxt->usn_block] % _vfl->geometry.blocks_per_bank; int status = nand_device_write_single_page(_vfl->device, _ce, 0, (bankStart + blockOffset) * _vfl->geometry.pages_per_block_2 + curPage + i, pageBuffer, spareBuffer); if(FAILED(status)) { bufferPrintf("vfl_write_vfl_cxt_to_flash: Failed write\r\n"); free(pageBuffer); free(spareBuffer); // vsvfl_mark_page_as_bad(_ce, (bankStart + blockOffset) * _vfl->geometry.pages_per_block_2 + curPage + i, status); return EIO; } } int fails = 0; for (i = 0; i < 8; i++) { uint32_t bankStart = (curVFLCxt->vfl_context_block[curVFLCxt->usn_block] / _vfl->geometry.blocks_per_bank) * _vfl->geometry.bank_address_space; uint32_t blockOffset = curVFLCxt->vfl_context_block[curVFLCxt->usn_block] % _vfl->geometry.blocks_per_bank; if(FAILED(nand_device_read_single_page(_vfl->device, _ce, 0, (bankStart + blockOffset) * _vfl->geometry.pages_per_block_2 + curPage + i, pageBuffer, spareBuffer, 0))) { //vsvfl_store_block_map_single_page(_ce, (bankStart + blockOffset) * _vfl->geometry.pages_per_block_2 + curPage + i); fails++; continue; } if(memcmp(pageBuffer, curVFLCxt, 0x6E0) || ((uint32_t*)spareBuffer)[0] != curVFLCxt->usn_dec || spareBuffer[8] || spareBuffer[9] != 0x80) fails++; } free(pageBuffer); free(spareBuffer); if(fails > 3) return EIO; else return SUCCESS; }
int VFL_Write(u32 virtualPageNumber, u8* buffer, u8* spare) { u16 virtualBank; u16 virtualBlock; u16 virtualPage; u16 physicalBlock; u32 dwVpn; int page; int ret; dwVpn = virtualPageNumber + (NANDGeometry->pagesPerSuBlk * FTLData->field_4); if(dwVpn >= NANDGeometry->pagesTotal) { LOG("ftl: dwVpn overflow: %d\n", dwVpn); return -EINVAL; } if(dwVpn < NANDGeometry->pagesPerSuBlk) { LOG("ftl: dwVpn underflow: %d\n", dwVpn); } virtual_page_number_to_virtual_address(dwVpn, &virtualBank, &virtualBlock, &virtualPage); physicalBlock = virtual_block_to_physical_block(virtualBank, virtualBlock); page = physicalBlock * NANDGeometry->pagesPerBlock + virtualPage; #ifdef IPHONE_DEBUG LOG("ftl: vfl_write: vpn: %u, bank %d, page %u\n", virtualPageNumber, virtualBank, page); #endif ret = nand_read(virtualBank, page, PageBuffer, SpareBuffer, true, true); if(ret != ERROR_EMPTYBLOCK) { LOG("ftl: WTF trying to write to a non-blank page! vpn = %u bank = %d page = %u\r\n", virtualPageNumber, virtualBank, page); return -1; } ret = nand_write(virtualBank, page, buffer, spare, true); if(ret == 0) return 0; ++pstVFLCxt[virtualBank].field_16; vfl_gen_checksum(virtualBank); vfl_schedule_block_for_remap(virtualBank, virtualBlock); return -1; }
static int add_block_to_scrub_list(vfl_vsvfl_device_t *_vfl, uint32_t _ce, uint32_t _block) { if(is_block_in_scrub_list(_vfl, _ce, _block)) return 0; if(_vfl->contexts[_ce].scrub_list_length > 0x13) { bufferPrintf("vfl: too many scrubs!\r\n"); return 0; } if(!vfl_check_checksum(_vfl, _ce)) system_panic("vfl_add_block_to_scrub_list: failed checksum\r\n"); _vfl->contexts[_ce].scrub_list[_vfl->contexts[_ce].scrub_list_length++] = _block; vfl_gen_checksum(_vfl, _ce); return vsvfl_store_vfl_cxt(_vfl, _ce); }
static int vfl_store_cxt(int bank) { int i; int good; SpareData* spareData = (SpareData*) SpareBuffer; --pstVFLCxt[bank].usnDec; pstVFLCxt[bank].usnInc = ++curVFLusnInc; pstVFLCxt[bank].nextcxtpage += 8; vfl_gen_checksum(bank); memset(spareData, 0xFF, NANDGeometry->bytesPerSpare); spareData->meta.usnDec = pstVFLCxt[bank].usnDec; spareData->type2 = 0; spareData->type1 = 0x80; for(i = 0; i < 8; ++i) { u32 index = pstVFLCxt[bank].activecxtblock; u32 block = pstVFLCxt[bank].VFLCxtBlock[index]; u32 page = block * NANDGeometry->pagesPerBlock; page += pstVFLCxt[bank].nextcxtpage - 8 + i; nand_write(bank, page, (u8*) &pstVFLCxt[bank], (u8*) spareData, true); } good = 0; for(i = 0; i < 8; ++i) { u32 index = pstVFLCxt[bank].activecxtblock; u32 block = pstVFLCxt[bank].VFLCxtBlock[index]; u32 page = block * NANDGeometry->pagesPerBlock; page += pstVFLCxt[bank].nextcxtpage - 8 + i; if(nand_read(bank, page, PageBuffer, (u8*) spareData, true, true) != 0) continue; if(memcmp(PageBuffer, &pstVFLCxt[bank], sizeof(VFLCxt)) != 0) continue; if(spareData->type2 == 0 && spareData->type1 == 0x80) ++good; } return (good > 3) ? 0 : -1; }
static error_t vsvfl_store_vfl_cxt(vfl_vsvfl_device_t *_vfl, uint32_t _ce) { if(_ce >= _vfl->geometry.num_ce) system_panic("vfl: Can't store VFLCxt on non-existent CE\r\n"); vfl_vsvfl_context_t *curVFLCxt = &_vfl->contexts[_ce]; if(curVFLCxt->usn_page + 8 > _vfl->geometry.pages_per_block || FAILED(vsvfl_write_vfl_cxt_to_flash(_vfl, _ce))) { int startBlock = curVFLCxt->usn_block; int nextBlock = (curVFLCxt->usn_block + 1) % 4; while(startBlock != nextBlock) { if(curVFLCxt->vfl_context_block[nextBlock] != 0xFFFF) { int fail = 0; int i; for (i = 0; i < 4; i++) { uint32_t bankStart = (curVFLCxt->vfl_context_block[nextBlock] / _vfl->geometry.blocks_per_bank) * _vfl->geometry.bank_address_space; uint32_t blockOffset = curVFLCxt->vfl_context_block[nextBlock] % _vfl->geometry.blocks_per_bank; int status = nand_device_erase_single_block(_vfl->device, _ce, bankStart + blockOffset); if(SUCCEEDED(status)) break; //vsvfl_mark_bad_vfl_block(_vfl, _ce, curVFLCxt->vfl_context_block[nextBlock], status); if(i == 3) fail = 1; } if(!fail) { if(!vfl_check_checksum(_vfl, _ce)) system_panic("vsvfl_store_vfl_cxt: failed checksum\r\n"); curVFLCxt->usn_block = nextBlock; curVFLCxt->usn_page = 0; vfl_gen_checksum(_vfl, _ce); int result = vsvfl_write_vfl_cxt_to_flash(_vfl, _ce); if(SUCCEEDED(result)) return result; } } nextBlock = (nextBlock + 1) % 4; } return EIO; } return SUCCESS; }
static error_t vfl_vsvfl_write_context(vfl_device_t *_vfl, uint16_t *_control_block) { vfl_vsvfl_device_t *vfl = CONTAINER_OF(vfl_vsvfl_device_t, vfl, _vfl); uint32_t ce = vfl->current_version % vfl->geometry.num_ce; uint32_t i; // check and update cxt of each CE for(i = 0; i < vfl->geometry.num_ce; i++) { if(vfl_check_checksum(vfl, i) == FALSE) system_panic("vsvfl: VFLCxt has bad checksum.\r\n"); memmove(vfl->contexts[i].control_block, _control_block, 6); vfl_gen_checksum(vfl, i); } // write cxt on the ce with the oldest cxt if(FAILED(vsvfl_store_vfl_cxt(vfl, ce))) { bufferPrintf("vsvfl: context write fail!\r\n"); return EIO; } return 0; }
static bool vfl_schedule_block_for_remap(int bank, u16 block) { if(vfl_check_remap_scheduled(bank, block)) return true; LOG("ftl: attempting to schedule bank %d, block %d for remap!\n", bank, block); // don't do anything for right now to avoid consequences for false positives return false; if(pstVFLCxt[bank].remappingScheduledStart == (pstVFLCxt[bank].numReservedBlocks + 10)) { // oh crap, we only have 10 free spares left. back out now. return false; } // stick this into the list --pstVFLCxt[bank].remappingScheduledStart; pstVFLCxt[bank].reservedBlockPoolMap[pstVFLCxt[bank].remappingScheduledStart] = block; vfl_gen_checksum(bank); return vfl_commit_cxt(bank); }
int VFL_Open(void) { void* FTLCtrlBlock; u16 buffer[3]; int bank = 0; for(bank = 0; bank < NANDGeometry->banksTotal; bank++) { VFLCxt* curVFLCxt; int i; int minUsn; int VFLCxtIdx; int last; int page; if(!findDeviceInfoBBT(bank, pstBBTArea)) { LOG("ftl: findDeviceInfoBBT failed\n"); return -1; } if(bank >= NANDGeometry->banksTotal) { return -1; } curVFLCxt = &pstVFLCxt[bank]; // Any VFLCxt page will contain an up-to-date list of all blocks used to store VFLCxt pages. Find any such // page in the system area. for(i = 1; i < FTLData->sysSuBlks; i++) { // so pstBBTArea is a bit array of some sort if(!(pstBBTArea[i / 8] & (1 << (i & 0x7)))) continue; if(nand_read_vfl_cxt_page(bank, i, 0, PageBuffer, SpareBuffer) == true) { memcpy(curVFLCxt->VFLCxtBlock, ((VFLCxt*)PageBuffer)->VFLCxtBlock, sizeof(curVFLCxt->VFLCxtBlock)); break; } } if(i == FTLData->sysSuBlks) { LOG("ftl: cannot find readable VFLCxtBlock\n"); return -1; } // Since VFLCxtBlock is a ringbuffer, if blockA.page0.spare.usnDec < blockB.page0.usnDec, then for any page a // in blockA and any page b in blockB, a.spare.usNDec < b.spare.usnDec. Therefore, to begin finding the // page/VFLCxt with the lowest usnDec, we should just look at the first page of each block in the ring. minUsn = 0xFFFFFFFF; VFLCxtIdx = 4; for(i = 0; i < 4; i++) { SpareData* spareData; u16 block = curVFLCxt->VFLCxtBlock[i]; if(block == 0xFFFF) continue; if(nand_read_vfl_cxt_page(bank, block, 0, PageBuffer, SpareBuffer) != true) continue; spareData = (SpareData*) SpareBuffer; if(spareData->meta.usnDec > 0 && spareData->meta.usnDec <= minUsn) { minUsn = spareData->meta.usnDec; VFLCxtIdx = i; } } if(VFLCxtIdx == 4) { LOG("ftl: cannot find readable VFLCxtBlock index in spares\n"); return -1; } // VFLCxts are stored in the block such that they are duplicated 8 times. Therefore, we only need to // read every 8th page, and nand_read_vfl_cxt_page will try the 7 subsequent pages if the first was // no good. The last non-blank page will have the lowest spare.usnDec and highest usnInc for VFLCxt // in all the land (and is the newest). last = 0; for(page = 8; page < NANDGeometry->pagesPerBlock; page += 8) { if(nand_read_vfl_cxt_page(bank, curVFLCxt->VFLCxtBlock[VFLCxtIdx], page, PageBuffer, SpareBuffer) == false) { break; } last = page; } if(nand_read_vfl_cxt_page(bank, curVFLCxt->VFLCxtBlock[VFLCxtIdx], last, PageBuffer, SpareBuffer) == false) { LOG("ftl: cannot find readable VFLCxt\n"); return -1; } // Aha, so the upshot is that this finds the VFLCxt and copies it into pstVFLCxt memcpy(&pstVFLCxt[bank], PageBuffer, sizeof(VFLCxt)); // This is the newest VFLCxt across all banks if(curVFLCxt->usnInc >= curVFLusnInc) { curVFLusnInc = curVFLCxt->usnInc; } // Verify the checksum if(vfl_check_checksum(bank) == false) { LOG("ftl: VFLCxt has bad checksum\n"); return -1; } } // retrieve the FTL control blocks from the latest VFL across all banks. FTLCtrlBlock = VFL_GetFTLCtrlBlock(); // Need a buffer because eventually we'll copy over the source memcpy(buffer, FTLCtrlBlock, sizeof(buffer)); // Then we update the VFLCxts on every bank with that information. for(bank = 0; bank < NANDGeometry->banksTotal; bank++) { memcpy(pstVFLCxt[bank].FTLCtrlBlock, buffer, sizeof(buffer)); vfl_gen_checksum(bank); } return 0; }
static int VFL_Open() { int bank = 0; for(bank = 0; bank < Data->banksTotal; bank++) { if(!findDeviceInfoBBT(bank, pstBBTArea)) { bufferPrintf("ftl: findDeviceInfoBBT failed\r\n"); return -1; } if(bank >= Data->banksTotal) { return -1; } VFLCxt* curVFLCxt = &pstVFLCxt[bank]; uint8_t* pageBuffer = malloc(Data->bytesPerPage); uint8_t* spareBuffer = malloc(Data->bytesPerSpare); if(pageBuffer == NULL || spareBuffer == NULL) { bufferPrintf("ftl: cannot allocate page and spare buffer\r\n"); return -1; } int i = 1; for(i = 1; i < Data2->field_0; i++) { // so pstBBTArea is a bit array of some sort if(!(pstBBTArea[i / 8] & (1 << (i & 0x7)))) continue; if(vfl_read_page(bank, i, 0, pageBuffer, spareBuffer) == TRUE) { memcpy(curVFLCxt->VFLCxtBlock, ((VFLCxt*)pageBuffer)->VFLCxtBlock, sizeof(curVFLCxt->VFLCxtBlock)); break; } } if(i == Data2->field_0) { bufferPrintf("ftl: cannot find readable VFLCxtBlock\r\n"); free(pageBuffer); free(spareBuffer); return -1; } int minLpn = 0xFFFFFFFF; int VFLCxtIdx = 4; for(i = 0; i < 4; i++) { uint16_t block = curVFLCxt->VFLCxtBlock[i]; if(block == 0xFFFF) continue; if(vfl_read_page(bank, block, 0, pageBuffer, spareBuffer) != TRUE) continue; SpareData* spareData = (SpareData*) spareBuffer; if(spareData->logicalPageNumber > 0 && spareData->logicalPageNumber <= minLpn) { minLpn = spareData->logicalPageNumber; VFLCxtIdx = i; } } if(VFLCxtIdx == 4) { bufferPrintf("ftl: cannot find readable VFLCxtBlock index in spares\r\n"); free(pageBuffer); free(spareBuffer); return -1; } int page = 8; int last = 0; for(page = 8; page < Data->pagesPerBlock; page += 8) { if(vfl_read_page(bank, curVFLCxt->VFLCxtBlock[VFLCxtIdx], page, pageBuffer, spareBuffer) == FALSE) { break; } last = page; } if(vfl_read_page(bank, curVFLCxt->VFLCxtBlock[VFLCxtIdx], last, pageBuffer, spareBuffer) == FALSE) { bufferPrintf("ftl: cannot find readable VFLCxt\n"); free(pageBuffer); free(spareBuffer); return -1; } // Aha, so the upshot is that this finds the VFLCxt and copies it into pstVFLCxt memcpy(&pstVFLCxt[bank], pageBuffer, sizeof(VFLCxt)); if(curVFLCxt->field_0 >= VFLData4) { VFLData4 = curVFLCxt->field_0; } free(pageBuffer); free(spareBuffer); if(vfl_check_checksum(bank) == FALSE) { bufferPrintf("ftl: VFLCxt has bad checksum\n"); return -1; } } void* maxThing = VFL_get_maxThing(); uint16_t buffer[3]; memcpy(buffer, maxThing, 6); for(bank = 0; bank < Data->banksTotal; bank++) { memcpy(pstVFLCxt[bank].field_4, buffer, sizeof(buffer)); vfl_gen_checksum(bank); } return 0; }
static error_t vfl_vsvfl_open(vfl_device_t *_vfl, nand_device_t *_nand) { vfl_vsvfl_device_t *vfl = CONTAINER_OF(vfl_vsvfl_device_t, vfl, _vfl); if(vfl->device || !_nand) return EINVAL; vfl->device = _nand; error_t ret = vfl_vsvfl_setup_geometry(vfl); if(FAILED(ret)) return ret; bufferPrintf("vsvfl: Opening %p.\r\n", _nand); vfl->contexts = malloc(vfl->geometry.num_ce * sizeof(vfl_vsvfl_context_t)); memset(vfl->contexts, 0, vfl->geometry.num_ce * sizeof(vfl_vsvfl_context_t)); vfl->pageBuffer = (uint32_t*) malloc(vfl->geometry.pages_per_block * sizeof(uint32_t)); vfl->chipBuffer = (uint16_t*) malloc(vfl->geometry.pages_per_block * sizeof(uint16_t)); vfl->blockBuffer = (uint16_t*) malloc(vfl->geometry.banks_total * sizeof(uint16_t)); uint32_t ce = 0; for(ce = 0; ce < vfl->geometry.num_ce; ce++) { vfl->bbt[ce] = (uint8_t*) malloc(CEIL_DIVIDE(vfl->geometry.blocks_per_ce, 8)); bufferPrintf("vsvfl: Checking CE %d.\r\n", ce); if(FAILED(nand_device_read_special_page(_nand, ce, "DEVICEINFOBBT\0\0\0", vfl->bbt[ce], CEIL_DIVIDE(vfl->geometry.blocks_per_ce, 8)))) { bufferPrintf("vsvfl: Failed to find DEVICEINFOBBT!\r\n"); return EIO; } if(ce >= vfl->geometry.num_ce) return EIO; vfl_vsvfl_context_t *curVFLCxt = &vfl->contexts[ce]; uint8_t* pageBuffer = malloc(vfl->geometry.bytes_per_page); uint8_t* spareBuffer = malloc(vfl->geometry.bytes_per_spare); if(pageBuffer == NULL || spareBuffer == NULL) { bufferPrintf("ftl: cannot allocate page and spare buffer\r\n"); return ENOMEM; } // Any VFLCxt page will contain an up-to-date list of all blocks used to store VFLCxt pages. Find any such // page in the system area. int i; for(i = vfl->geometry.reserved_blocks; i < vfl->geometry.fs_start_block; i++) { // so pstBBTArea is a bit array of some sort if(!(vfl->bbt[ce][i / 8] & (1 << (i & 0x7)))) continue; if(SUCCEEDED(nand_device_read_single_page(vfl->device, ce, i, 0, pageBuffer, spareBuffer, 0))) { memcpy(curVFLCxt->vfl_context_block, ((vfl_vsvfl_context_t*)pageBuffer)->vfl_context_block, sizeof(curVFLCxt->vfl_context_block)); break; } } if(i == vfl->geometry.fs_start_block) { bufferPrintf("vsvfl: cannot find readable VFLCxtBlock\r\n"); free(pageBuffer); free(spareBuffer); return EIO; } // Since VFLCxtBlock is a ringbuffer, if blockA.page0.spare.usnDec < blockB.page0.usnDec, then for any page a // in blockA and any page b in blockB, a.spare.usNDec < b.spare.usnDec. Therefore, to begin finding the // page/VFLCxt with the lowest usnDec, we should just look at the first page of each block in the ring. int minUsn = 0xFFFFFFFF; int VFLCxtIdx = 4; for(i = 0; i < 4; i++) { uint16_t block = curVFLCxt->vfl_context_block[i]; if(block == 0xFFFF) continue; if(FAILED(nand_device_read_single_page(vfl->device, ce, block, 0, pageBuffer, spareBuffer, 0))) continue; vfl_vsvfl_spare_data_t *spareData = (vfl_vsvfl_spare_data_t*)spareBuffer; if(spareData->meta.usnDec > 0 && spareData->meta.usnDec <= minUsn) { minUsn = spareData->meta.usnDec; VFLCxtIdx = i; } } if(VFLCxtIdx == 4) { bufferPrintf("vsvfl: cannot find readable VFLCxtBlock index in spares\r\n"); free(pageBuffer); free(spareBuffer); return EIO; } // VFLCxts are stored in the block such that they are duplicated 8 times. Therefore, we only need to // read every 8th page, and nand_readvfl_cxt_page will try the 7 subsequent pages if the first was // no good. The last non-blank page will have the lowest spare.usnDec and highest usnInc for VFLCxt // in all the land (and is the newest). int page = 8; int last = 0; for(page = 8; page < vfl->geometry.pages_per_block; page += 8) { if(nand_device_read_single_page(vfl->device, ce, curVFLCxt->vfl_context_block[VFLCxtIdx], page, pageBuffer, spareBuffer, 0) != 0) { break; } last = page; } if(nand_device_read_single_page(vfl->device, ce, curVFLCxt->vfl_context_block[VFLCxtIdx], last, pageBuffer, spareBuffer, 0) != 0) { bufferPrintf("vsvfl: cannot find readable VFLCxt\n"); free(pageBuffer); free(spareBuffer); return -1; } // Aha, so the upshot is that this finds the VFLCxt and copies it into vfl->contexts memcpy(&vfl->contexts[ce], pageBuffer, sizeof(vfl_vsvfl_context_t)); // This is the newest VFLCxt across all CEs if(curVFLCxt->usn_inc >= vfl->current_version) { vfl->current_version = curVFLCxt->usn_inc; } free(pageBuffer); free(spareBuffer); // Verify the checksum if(vfl_check_checksum(vfl, ce) == FALSE) { bufferPrintf("vsvfl: VFLCxt has bad checksum.\r\n"); return EIO; } } // retrieve some global parameters from the latest VFL across all CEs. vfl_vsvfl_context_t *latestCxt = get_most_updated_context(vfl); // Then we update the VFLCxts on every ce with that information. for(ce = 0; ce < vfl->geometry.num_ce; ce++) { // Don't copy over own data. if(&vfl->contexts[ce] != latestCxt) { // Copy the data, and generate the new checksum. memcpy(vfl->contexts[ce].control_block, latestCxt->control_block, sizeof(latestCxt->control_block)); vfl->contexts[ce].usable_blocks_per_bank = latestCxt->usable_blocks_per_bank; vfl->contexts[ce].reserved_block_pool_start = latestCxt->reserved_block_pool_start; vfl->contexts[ce].ftl_type = latestCxt->ftl_type; memcpy(vfl->contexts[ce].field_6CA, latestCxt->field_6CA, sizeof(latestCxt->field_6CA)); vfl_gen_checksum(vfl, ce); } } // Vendor-specific virtual-from/to-physical functions. // Note: support for some vendors is still missing. nand_device_t *nand = vfl->device; uint32_t vendorType = vfl->contexts[0].vendor_type; if(!vendorType) if(FAILED(nand_device_get_info(nand, diVendorType, &vendorType, sizeof(vendorType)))) return EIO; switch(vendorType) { case 0x10001: vfl->geometry.banks_per_ce = 1; vfl->virtual_to_physical = virtual_to_physical_10001; break; case 0x100010: case 0x100014: case 0x120014: vfl->geometry.banks_per_ce = 2; vfl->virtual_to_physical = virtual_to_physical_100014; break; case 0x150011: vfl->geometry.banks_per_ce = 2; vfl->virtual_to_physical = virtual_to_physical_150011; break; default: bufferPrintf("vsvfl: unsupported vendor 0x%06x\r\n", vendorType); return EIO; } if(FAILED(nand_device_set_info(nand, diVendorType, &vendorType, sizeof(vendorType)))) return EIO; vfl->geometry.pages_per_sublk = vfl->geometry.pages_per_block * vfl->geometry.banks_per_ce * vfl->geometry.num_ce; vfl->geometry.banks_total = vfl->geometry.num_ce * vfl->geometry.banks_per_ce; vfl->geometry.blocks_per_bank_vfl = vfl->geometry.blocks_per_ce / vfl->geometry.banks_per_ce; uint32_t banksPerCE = vfl->geometry.banks_per_ce; if(FAILED(nand_device_set_info(nand, diBanksPerCE_VFL, &banksPerCE, sizeof(banksPerCE)))) return EIO; bufferPrintf("vsvfl: detected chip vendor 0x%06x\r\n", vendorType); // Now, discard the old scfg bad-block table, and set it using the VFL context's reserved block pool map. uint32_t bank, i; uint32_t num_reserved = vfl->contexts[0].reserved_block_pool_start; uint32_t num_non_reserved = vfl->geometry.blocks_per_bank_vfl - num_reserved; for(ce = 0; ce < vfl->geometry.num_ce; ce++) { memset(vfl->bbt[ce], 0xFF, CEIL_DIVIDE(vfl->geometry.blocks_per_ce, 8)); for(bank = 0; bank < banksPerCE; bank++) { for(i = 0; i < num_non_reserved; i++) { uint16_t mapEntry = vfl->contexts[ce].reserved_block_pool_map[bank * num_non_reserved + i]; uint32_t pBlock; if(mapEntry == 0xFFF0) continue; if(mapEntry < vfl->geometry.blocks_per_ce) { pBlock = mapEntry; } else if(mapEntry > 0xFFF0) { virtual_block_to_physical_block(vfl, ce + bank * vfl->geometry.num_ce, num_reserved + i, &pBlock); } else { system_panic("vsvfl: bad map table: CE %d, entry %d, value 0x%08x\r\n", ce, bank * num_non_reserved + i, mapEntry); } vfl->bbt[ce][pBlock / 8] &= ~(1 << (pBlock % 8)); } } } bufferPrintf("vsvfl: VFL successfully opened!\r\n"); return SUCCESS; }