from array import array from construct.core import Struct, Union from construct.macros import * from structs import next_power_of_two, CEIL_DIVIDE, PAGETYPE_VFL import struct """ https://github.com/iDroid-Project/openiBoot/blob/master/plat-s5l8900/includes/s5l8900/ftl.h https://github.com/iDroid-Project/openiBoot/blob/master/plat-s5l8900/ftl.c https://github.com/iDroid-Project/openiBoot/blob/master/plat-s5l8900/nand.c static const NANDDeviceType SupportedDevices[] = { """ SupportedDevices = {0x2555D5EC: [8192, 128, 4, 64, 4, 2, 4, 2, 7744, 4, 6], 0xB614D5EC: [4096, 128, 8, 128, 4, 2, 4, 2, 3872, 4, 6], 0xB655D7EC: [8192, 128, 8, 128, 4, 2, 4, 2, 7744, 4, 6], 0xA514D3AD: [4096, 128, 4, 64, 4, 2, 4, 2, 3872, 4, 6], 0xA555D5AD: [8192, 128, 4, 64, 4, 2, 4, 2, 7744, 4, 6], 0xB614D5AD: [4096, 128, 8, 128, 4, 2, 4, 2, 3872, 4, 6], 0xB655D7AD: [8192, 128, 8, 128, 4, 2, 4, 2, 7744, 4, 6], 0xA585D598: [8320, 128, 4, 64, 6, 2, 4, 2, 7744, 4, 6], 0xBA94D598: [4096, 128, 8, 216, 6, 2, 4, 2, 3872, 8, 8], 0xBA95D798: [8192, 128, 8, 216, 6, 2, 4, 2, 7744, 8, 8], 0x3ED5D789: [8192, 128, 8, 216, 4, 2, 4, 2, 7744, 8, 8], 0x3E94D589: [4096, 128, 8, 216, 4, 2, 4, 2, 3872, 8, 8], 0x3ED5D72C: [8192, 128, 8, 216, 4, 2, 4, 2, 7744, 8, 8], 0x3E94D52C: [4096, 128, 8, 216, 4, 2, 4, 2, 3872, 8, 8] } _vfl_vfl_context = Struct("_vfl_vfl_context", ULInt32("usn_inc"), Array(3, ULInt16("control_block")), ULInt16("unk1"), ULInt32("usn_dec"), ULInt16("active_context_block"), ULInt16("next_context_page"), ULInt16("unk2"), ULInt16("field_16"), ULInt16("field_18"), ULInt16("num_reserved_blocks"), ULInt16("reserved_block_pool_start"), ULInt16("total_reserved_blocks"), Array(820, ULInt16("reserved_block_pool_map")), Array(282, ULInt8("bad_block_table")), Array(4, ULInt16("vfl_context_block")), ULInt16("remapping_schedule_start"), Array(0x48, ULInt8("unk3")), ULInt32("version"), ULInt32("checksum1"), ULInt32("checksum2") ) _vfl_vsvfl_spare_data = Struct("_vfl_vsvfl_spare_data", Union("foo", Struct("user",ULInt32("logicalPageNumber"),ULInt32("usn")), Struct("meta",ULInt32("usnDec"),ULInt16("idx"), ULInt8("field_6"), ULInt8("field_7")) ), ULInt8("type2"), ULInt8("type1"), ULInt8("eccMark"), ULInt8("field_B"), ) def vfl_checksum(data): x = 0 y = 0 for z in array("I", data): x = (x + z) & 0xffffffff y = (y ^ z) & 0xffffffff return (x + 0xAABBCCDD) & 0xffffffff, (y ^ 0xAABBCCDD) & 0xffffffff def vfl_check_checksum(ctx, ctxtype): c1, c2 = vfl_checksum(ctxtype.build(ctx)[:-8]) return c1 == ctx.checksum1 and c2 == ctx.checksum2 class VFL(object): def __init__(self, nand): self.nand = nand #XXX check self.banks_total = nand.nCEs * nand.banks_per_ce_physical self.num_ce = nand.nCEs self.banks_per_ce = nand.banks_per_ce_physical self.blocks_per_ce = nand.blocksPerCE self.pages_per_block = nand.pagesPerBlock self.pages_per_block_2 = next_power_of_two(self.pages_per_block) self.pages_per_sublk = self.pages_per_block * self.banks_per_ce * self.num_ce self.blocks_per_bank = self.blocks_per_ce / self.banks_per_ce self.blocks_per_bank_vfl = self.blocks_per_ce / self.banks_per_ce self.vendorType = nand.vendorType self.fs_start_block = 5 #field_4 = 5; if not SupportedDevices.has_key(nand.deviceReadId): raise Exception("VFL: unsupported device 0x%x" % nand.deviceReadId) userSuBlksTotal = self.userSuBlksTotal = SupportedDevices[nand.deviceReadId][8]#7744 userPagesTotal = userSuBlksTotal * self.pages_per_sublk suBlksTotal = self.blocks_per_ce FTLData_field_2 = suBlksTotal - userSuBlksTotal - 28 print suBlksTotal, userSuBlksTotal, FTLData_field_2 FTLData_field_4 = FTLData_field_2 + 5 self.FTLData_field_4 = FTLData_field_4 #FTLData_sysSuBlks = FTLData_field_2 + 4 #FTLData_field_6 = 3 #FTLData_field_8 = 23 self.vflContexts = [] self.bbt = [] self.current_version = 0 self.context = None reserved_blocks = 0 fs_start_block = reserved_blocks+10 #XXX for ce in xrange(self.num_ce): for b in xrange(reserved_blocks, fs_start_block): s, d = nand.readMetaPage(ce, b, 0, _vfl_vsvfl_spare_data) if not d: continue vflctx = _vfl_vfl_context.parse(d) if not vfl_check_checksum(vflctx, _vfl_vfl_context): vflctx = None continue break MostRecentVFLCxtBlock = -1 minUsn = 0xFFFFFFFF for b in vflctx.vfl_context_block: s, d = nand.readMetaPage(ce, b, 0, _vfl_vsvfl_spare_data) if not d: continue if s.foo.meta.usnDec > 0 and s.foo.meta.usnDec <= minUsn: minUsn = s.foo.meta.usnDec; MostRecentVFLCxtBlock = b if MostRecentVFLCxtBlock == -1: print "MostRecentVFLCxtBlock == -1" return last = None for pageNum in xrange(0, self.pages_per_block, 1): s,d = nand.readMetaPage(ce, MostRecentVFLCxtBlock, pageNum, _vfl_vsvfl_spare_data) if not d: break vflctx = _vfl_vfl_context.parse(d) if vfl_check_checksum(vflctx, _vfl_vfl_context): last = vflctx if not last: raise Exception("VFL open FAIL 1") self.vflContexts.append(last) if last.version == 1 and last.usn_inc >= self.current_version: self.current_version = last.usn_inc self.context = last if not self.context: raise Exception("VFL open FAIL") print "VFL context open OK" def VFL_get_FTLCtrlBlock(self): for ctx in self.vflContexts: if ctx.usn_inc == self.current_version: return ctx.control_block def vfl_is_good_block(self, bbt, block): if block > self.blocks_per_ce: raise Exception("vfl_is_good_block block %d out of bounds" % block) index = block/8 return ((bbt[index / 8] >> (7 - (index % 8))) & 0x1) == 0x1 def virtual_block_to_physical_block(self, ce, pBlock): if self.vfl_is_good_block(self.vflContexts[ce].bad_block_table, pBlock): return pBlock ctx = self.vflContexts[ce] for pwDesPbn in xrange(0, ctx.num_reserved_blocks): if ctx.reserved_block_pool_map[pwDesPbn] == pBlock: if pwDesPbn > self.blocks_per_ce: raise Exception("Destination physical block for remapping is greater than number of blocks per bank!") return ctx.reserved_block_pool_start + pwDesPbn print "Bad block %d not remapped" % pBlock return pBlock def virtual_page_number_to_virtual_address(self, vpn): vbank = vpn % self.num_ce vblock = vpn / self.pages_per_sublk vpage = (vpn / self.num_ce) % self.pages_per_block return vbank, vblock, vpage def read_single_page(self, vpn, key=None, lpn=None): vpn += self.pages_per_sublk * self.FTLData_field_4 vbank, vblock, vpage = self.virtual_page_number_to_virtual_address(vpn) pblock = self.virtual_block_to_physical_block(vbank, vblock) #print "VFL read_single_page %d => %d, %d" % (vpn,ce,pPage) return self.nand.readPage(vbank, pblock*self.nand.pagesPerBlock + vpage, key, lpn)