194 lines
9.3 KiB
Python
194 lines
9.3 KiB
Python
from construct import *
|
|
from structs import next_power_of_two, PAGETYPE_VFL, CEIL_DIVIDE
|
|
from vfl import vfl_check_checksum, _vfl_vsvfl_spare_data
|
|
|
|
"""
|
|
https://github.com/iDroid-Project/openiBoot/blob/master/vfl-vsvfl/vsvfl.c
|
|
https://github.com/iDroid-Project/openiBoot/blob/master/vfl-vsvfl/includes/vfl/vsvfl.h
|
|
"""
|
|
|
|
_vfl_vsvfl_context = Struct("_vfl_vsvfl_context",
|
|
ULInt32("usn_inc"),
|
|
ULInt32("usn_dec"),
|
|
ULInt32("ftl_type"),
|
|
ULInt16("usn_block"),
|
|
ULInt16("usn_page"),
|
|
ULInt16("active_context_block"),
|
|
ULInt16("write_failure_count"),
|
|
ULInt16("bad_block_count"),
|
|
Array(4, ULInt8("replaced_block_count")),
|
|
ULInt16("num_reserved_blocks"),
|
|
ULInt16("field_1C"),
|
|
ULInt16("total_reserved_blocks"),
|
|
Array(6, ULInt8("field_20")),
|
|
Array(820, ULInt16("reserved_block_pool_map")),
|
|
Array(4, ULInt16("vfl_context_block")),
|
|
ULInt16("usable_blocks_per_bank"),
|
|
ULInt16("reserved_block_pool_start"),
|
|
Array(3, ULInt16("control_block")),
|
|
ULInt16("scrub_list_length"),
|
|
Array(20, ULInt16("scrub_list")),
|
|
Array(4, ULInt32("field_6CA")),
|
|
ULInt32("vendor_type"),
|
|
Array(204, ULInt8("field_6DE")),
|
|
ULInt16("remapping_schedule_start"),
|
|
Array(0x48, ULInt8("unk3")),
|
|
ULInt32("version"),
|
|
ULInt32("checksum1"),
|
|
ULInt32("checksum2")
|
|
)
|
|
|
|
|
|
class VSVFL(object):
|
|
def __init__(self, nand):
|
|
self.nand = nand
|
|
self.banks_per_ce_vfl = 1
|
|
if self.nand.vendorType in [0x100010, 0x100014, 0x120014, 0x150011]:
|
|
self.banks_per_ce_vfl = 2
|
|
self.banks_total = nand.nCEs * self.banks_per_ce_vfl
|
|
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_vfl * 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_vfl
|
|
self.vendorType = nand.vendorType
|
|
if self.vendorType == 0x10001:
|
|
self.virtual_to_physical = self.virtual_to_physical_10001
|
|
elif self.vendorType == 0x150011:
|
|
self.virtual_to_physical = self.virtual_to_physical_100014
|
|
elif self.vendorType in [0x100010, 0x100014, 0x120014]:
|
|
self.virtual_to_physical = self.virtual_to_physical_150011
|
|
else:
|
|
raise Exception("VSVFL: unsupported vendor 0x%x" % self.vendorType)
|
|
self.bank_address_space = nand.bank_address_space
|
|
self.vflContexts = []
|
|
self.bbt = []
|
|
self.current_version = 0
|
|
reserved_blocks = 0
|
|
if self.nand.bfn:
|
|
reserved_blocks = 16
|
|
fs_start_block = reserved_blocks+16 #XXX
|
|
for ce in xrange(self.num_ce):
|
|
vflctx = None
|
|
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_vsvfl_context.parse(d)
|
|
if not vfl_check_checksum(vflctx, _vfl_vsvfl_context):
|
|
vflctx = None
|
|
continue
|
|
break
|
|
if not vflctx:
|
|
raise Exception("Unable to find VSVFL context for CE %d" % ce)
|
|
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 or s.type1 != PAGETYPE_VFL:
|
|
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 or s.type1 != PAGETYPE_VFL:
|
|
break
|
|
last = d
|
|
vflctx = _vfl_vsvfl_context.parse(last)
|
|
if not vfl_check_checksum(vflctx, _vfl_vsvfl_context):
|
|
print "VSVFL checksum FAIL"
|
|
self.vflContexts.append(vflctx)
|
|
if vflctx.version == 2 and vflctx.usn_inc >= self.current_version:
|
|
self.current_version = vflctx.usn_inc
|
|
self.context = vflctx
|
|
if not self.context:
|
|
raise Exception("VSVFL open FAIL")
|
|
|
|
num_reserved = self.vflContexts[0].reserved_block_pool_start
|
|
num_non_reserved = self.blocks_per_bank_vfl - num_reserved
|
|
for ce in xrange(self.num_ce):
|
|
bbt = [0xFF] * (CEIL_DIVIDE(self.blocks_per_ce, 8))
|
|
ctx = self.vflContexts[ce]
|
|
for bank in xrange(0, self.banks_per_ce_vfl):
|
|
for i in xrange(0, num_non_reserved):
|
|
mapEntry = ctx.reserved_block_pool_map[bank*num_non_reserved + i]
|
|
if mapEntry == 0xFFF0:
|
|
continue
|
|
if mapEntry < self.blocks_per_ce:
|
|
pBlock = mapEntry
|
|
elif mapEntry > 0xFFF0:
|
|
pBlock = self.virtual_block_to_physical_block(ce + bank * self.num_ce, num_reserved + i)
|
|
else:
|
|
print "VSVFL: bad map table"
|
|
bbt[pBlock / 8] &= ~(1 << (pBlock % 8))
|
|
self.bbt.append(bbt)
|
|
print "VSVFL 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 virtual_to_physical_10001(self, vBank, vPage):
|
|
return vBank, vPage
|
|
|
|
def virtual_to_physical_100014(self, vBank, vPage):
|
|
pBank = vBank / self.num_ce;
|
|
pPage = ((self.pages_per_block - 1) & vPage) | (2 * (~(self.pages_per_block - 1) & vPage))
|
|
if (pBank & 1):
|
|
pPage |= self.pages_per_block
|
|
return vBank % self.num_ce, pPage
|
|
|
|
def virtual_to_physical_150011(self, vBank, vPage):
|
|
pBlock = 2 * (vPage / self.pages_per_block)
|
|
if(vBank % (2 * self.num_ce) >= self.num_ce):
|
|
pBlock += 1
|
|
return vBank % self.num_ce, self.pages_per_block * pBlock | (vPage % 128)
|
|
|
|
def virtual_block_to_physical_block(self, vBank, vBlock):
|
|
ce, pPage = self.virtual_to_physical(vBank, self.pages_per_block * vBlock)
|
|
return pPage / self.pages_per_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)
|
|
return (bbt[block / 8] & (1 << (block % 8))) != 0
|
|
|
|
def remap_block(self, ce, pBlock):
|
|
if self.vfl_is_good_block(self.bbt[ce], pBlock):
|
|
return pBlock
|
|
ctx = self.vflContexts[ce]
|
|
for pwDesPbn in xrange(0, self.blocks_per_ce - ctx.reserved_block_pool_start * self.banks_per_ce_vfl):
|
|
if ctx.reserved_block_pool_map[pwDesPbn] == pBlock:
|
|
vBank = ce + self.num_ce * (pwDesPbn / (self.blocks_per_bank_vfl - ctx.reserved_block_pool_start))
|
|
vBlock = ctx.reserved_block_pool_start + (pwDesPbn % (self.blocks_per_bank_vfl - ctx.reserved_block_pool_start))
|
|
z = self.virtual_block_to_physical_block(vBank, vBlock)
|
|
#print "remapped block %d => %d" % (pBlock, z)
|
|
return z
|
|
print "Bad block %d not remapped" % pBlock
|
|
return pBlock
|
|
|
|
def virtual_page_number_to_physical(self, vpn):
|
|
vBank = vpn % self.banks_total
|
|
ce = vBank % self.nand.nCEs
|
|
|
|
pBlock = self.virtual_block_to_physical_block(vBank, vpn / self.pages_per_sublk)
|
|
pBlock = self.remap_block(ce, pBlock)
|
|
bank_offset = self.bank_address_space * (pBlock / self.blocks_per_bank)
|
|
page = self.pages_per_block_2 * (bank_offset + (pBlock % self.blocks_per_bank)) \
|
|
+ ((vpn % self.pages_per_sublk) / self.banks_total)
|
|
return ce, page
|
|
|
|
def read_single_page(self, vpn, key=None, lpn=None):
|
|
ce, pPage = self.virtual_page_number_to_physical(vpn)
|
|
#print "VFL read_single_page %d => %d, %d" % (vpn,ce,pPage)
|
|
return self.nand.readPage(ce, pPage, key, lpn)
|