Ejemplo n.º 1
0
static error_t vfl_vsvfl_get_info(vfl_device_t *_vfl, vfl_info_t _item, void *_result, size_t _sz)
{
    vfl_vsvfl_device_t *vfl = CONTAINER_OF(vfl_vsvfl_device_t, vfl, _vfl);
    nand_device_t *nand = vfl->device;

    if(_sz > 4 || _sz == 3) {
        return EINVAL;
    }

    switch(_item) {
    case diPagesPerBlockTotalBanks:
        auto_store(_result, _sz, vfl->geometry.pages_per_sublk);
        return SUCCESS;

    case diSomeThingFromVFLCXT:
        auto_store(_result, _sz, vfl->contexts[0].usable_blocks_per_bank);
        return SUCCESS;

    case diFTLType:
        auto_store(_result, _sz, vfl->contexts[0].ftl_type);
        return SUCCESS;

    case diBytesPerPageFTL:
        nand_device_get_info(nand, diBytesPerPage, _result, _sz);
        return SUCCESS;

    case diMetaBytes0xC:
        auto_store(_result, _sz, 0xC);
        return SUCCESS;

    case diUnkn20_1:
        auto_store(_result, _sz, 1);
        return SUCCESS;

    case diTotalBanks:
        nand_device_get_info(nand, diTotalBanks_VFL, _result, _sz);
        return SUCCESS;

    default:
        return ENOENT;
    }
}
Ejemplo n.º 2
0
error_t nand_device_read_special_page(nand_device_t *_dev, uint32_t _ce, char _page[16], uint8_t *_buffer, size_t _amt)
{
	uint32_t bytesPerPage, blocksPerCE, pagesPerBlock;

	error_t ret = nand_device_get_info(_dev, diBytesPerPage, &bytesPerPage, sizeof(bytesPerPage));
	if(FAILED(ret))
		return EINVAL;

	ret = nand_device_get_info(_dev, diBlocksPerCE, &blocksPerCE, sizeof(blocksPerCE));
	if(FAILED(ret))
		return EINVAL;

	ret = nand_device_get_info(_dev, diPagesPerBlock, &pagesPerBlock, sizeof(pagesPerBlock));
	if(FAILED(ret))
		return EINVAL;

	uint8_t* buffer = memalign(DMA_ALIGN, bytesPerPage);
	int lowestBlock = blocksPerCE - (blocksPerCE / 10);
	int block;
	for(block = blocksPerCE - 1; block >= lowestBlock; block--)
	{
		int page;
		int badBlockCount = 0;
		for(page = 0; page < pagesPerBlock; page++)
		{
			if(badBlockCount > 2)
			{
				DebugPrintf("vfl: read_special_page - too many bad pages, skipping block %d\r\n", block);
				break;
			}

			int ret = nand_device_read_single_page(_dev, _ce, block, page, buffer, NULL);
			if(ret != 0)
			{
				if(ret == 1)
				{
					DebugPrintf("vfl: read_special_page - found 'badBlock' on ce %d, page %d\r\n", _ce, (block * pagesPerBlock) + page);
					badBlockCount++;
				}

				DebugPrintf("vfl: read_special_page - skipping ce %d, page %d\r\n", _ce, (block * pagesPerBlock) + page);
				continue;
			}

			if(memcmp(buffer, _page, sizeof(_page)) == 0)
			{
				if(_buffer)
				{
					size_t amt = ((size_t*)buffer)[13]; 
					if(amt > _amt)
						amt = _amt;

					memcpy(_buffer, buffer + 0x38, amt);
				}

				free(buffer);
				return SUCCESS;
			}
			else
				DebugPrintf("vfl: did not find signature on ce %d, page %d\r\n", _ce, (block * pagesPerBlock) + page);
		}
	}

	free(buffer);
	return ENOENT;
}
Ejemplo n.º 3
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;
}