예제 #1
0
파일: vsvfl.c 프로젝트: Cephrus/openiBoot
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;
}
예제 #2
0
파일: vsvfl.c 프로젝트: Cephrus/openiBoot
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;
}
예제 #3
0
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;
}
예제 #4
0
파일: vsvfl.c 프로젝트: Cephrus/openiBoot
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);
}
예제 #5
0
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;
}
예제 #6
0
파일: vsvfl.c 프로젝트: Cephrus/openiBoot
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;
}
예제 #7
0
파일: vsvfl.c 프로젝트: Cephrus/openiBoot
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;
}
예제 #8
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);
}
예제 #9
0
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;
}
예제 #10
0
파일: ftl.c 프로젝트: cantona/iphonelinux
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;
}
예제 #11
0
파일: vsvfl.c 프로젝트: snapite/openiBoot
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;
}