hacks/dump-imessages/iphone-dataprotection/python_scripts/nand/vfl.py

189 lines
8.8 KiB
Python

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)