221 lines
8.6 KiB
Python
221 lines
8.6 KiB
Python
from carver import NANDCarver
|
|
from construct.core import Struct
|
|
from construct.macros import ULInt32, ULInt16, Array, ULInt8, Padding
|
|
from pprint import pprint
|
|
from structs import SpareData
|
|
from util import hexdump
|
|
from vfl import VFL
|
|
import plistlib
|
|
|
|
"""
|
|
openiboot/plat-s5l8900/ftl.c
|
|
openiboot/plat-s5l8900/includes/s5l8900/ftl.h
|
|
"""
|
|
FTLCxtLog = Struct("FTLCxtLog",
|
|
ULInt32("usn"),
|
|
ULInt16("wVbn"),
|
|
ULInt16("wLbn"),
|
|
ULInt32("wPageOffsets"),
|
|
ULInt16("pagesUsed"),
|
|
ULInt16("pagesCurrent"),
|
|
ULInt32("isSequential")
|
|
)
|
|
|
|
FTLCxtElement2 = Struct("FTLCxtElement2",
|
|
ULInt16("field_0"),
|
|
ULInt16("field_2")
|
|
)
|
|
|
|
FTLCxt = Struct("FTLCxt",
|
|
ULInt32("usnDec"),
|
|
ULInt32("nextblockusn"),
|
|
ULInt16("wNumOfFreeVb"),
|
|
ULInt16("nextFreeIdx"),
|
|
ULInt16("swapCounter"),
|
|
Array(20, ULInt16("awFreeVb")),
|
|
ULInt16("field_36"),
|
|
Array(18, ULInt32("pages_for_pawMapTable")),
|
|
Array(36, ULInt32("pages_for_pawEraseCounterTable")),
|
|
Array(34, ULInt32("pages_for_wPageOffsets")),
|
|
ULInt32("pawMapTable"),
|
|
ULInt32("pawEraseCounterTable"),
|
|
ULInt32("wPageOffsets"),
|
|
Array(18, FTLCxtLog),
|
|
ULInt32("eraseCounterPagesDirty"),
|
|
ULInt16("unk3"),
|
|
Array(3, ULInt16("FTLCtrlBlock")),
|
|
ULInt32("FTLCtrlPage"),
|
|
ULInt32("clean"),
|
|
Array(36, ULInt32("pages_for_pawReadCounterTable")),
|
|
ULInt32("pawReadCounterTable"),
|
|
Array(5, FTLCxtElement2),
|
|
ULInt32("field_3C8"),
|
|
ULInt32("totalReadCount"),
|
|
ULInt32("page_for_FTLCountsTable"),
|
|
ULInt32("hasFTLCountsTable"),
|
|
Padding(0x420), #, ULInt8("field_3D8")),
|
|
ULInt32("versionLower"),
|
|
ULInt32("versionUpper")
|
|
)
|
|
|
|
FTL_CTX_TYPE = 0x43
|
|
FTL_BLOCK_MAP = 0x44
|
|
FTL_ERASE_COUNTER = 0x46
|
|
FTL_MOUNTED = 0x47
|
|
FTL_CTX_TYPE_MAX = 0x4F
|
|
USER_TYPE = 0x40
|
|
USER_LAST_TYPE = 0x41 #last user page in superblock?
|
|
|
|
class FTL(object):
|
|
def __init__(self, nand, vfl):
|
|
self.nand = nand
|
|
self.vfl = vfl
|
|
self.pawMapTable = {} #maps logical blocks to virtual blocks
|
|
self.pLogs = {}
|
|
if not self.FTL_open():
|
|
self.FTL_restore()
|
|
|
|
def FTL_open(self):
|
|
minUsnDec = 0xffffffff
|
|
ftlCtrlBlock = 0xffff
|
|
for vb in self.vfl.VFL_get_FTLCtrlBlock():
|
|
s, d = self.vfl.read_single_page(vb * self.vfl.pages_per_sublk)
|
|
if not s:
|
|
continue
|
|
if s.type >= FTL_CTX_TYPE and s.type <= FTL_CTX_TYPE_MAX:
|
|
if s.usn < minUsnDec:
|
|
ftlCtrlBlock = vb
|
|
minUsnDec = s.usn
|
|
|
|
print ftlCtrlBlock
|
|
self.ftlCtrlBlock = ftlCtrlBlock
|
|
for p in xrange(self.vfl.pages_per_sublk-1,1, -1):
|
|
s, d = self.vfl.read_single_page(ftlCtrlBlock * self.vfl.pages_per_sublk + p)
|
|
if not s:
|
|
continue
|
|
#print s
|
|
#print p
|
|
if s.type == FTL_CTX_TYPE:
|
|
print s.usn
|
|
ctx = FTLCxt.parse(d)
|
|
if ctx.versionLower == 0x46560001:
|
|
print ctx
|
|
assert ctx.FTLCtrlPage == (ftlCtrlBlock * self.vfl.pages_per_sublk + p)
|
|
break
|
|
else:
|
|
print "Unclean shutdown, last type 0x%x" % s.type
|
|
return False
|
|
self.ctx = ctx
|
|
print "FTL_open OK !"
|
|
return True
|
|
|
|
def determine_block_type(self, block):
|
|
maxUSN = 0
|
|
isSequential = True
|
|
for page in xrange(self.vfl.pages_per_sublk-1,1, -1):
|
|
s, _ = self.vfl.read_single_page(block * self.vfl.pages_per_sublk + page)
|
|
if not s:
|
|
continue
|
|
if s.usn > maxUSN:
|
|
maxUSN = s.usn
|
|
if s.lpn % self.vfl.pages_per_sublk != page:
|
|
isSequential = False
|
|
return isSequential, maxUSN
|
|
return isSequential, maxUSN
|
|
|
|
def FTL_restore(self):
|
|
self.pLogs = self.vfl.nand.loadCachedData("pLogs")
|
|
self.pawMapTable = self.vfl.nand.loadCachedData("pawMapTable")
|
|
if self.pLogs and self.pawMapTable:
|
|
print "Found cached FTL restore information"
|
|
return
|
|
self.pawMapTable = {}
|
|
self.pLogs = {}
|
|
ctx = None
|
|
for p in xrange(self.vfl.pages_per_sublk-1,1, -1):
|
|
s, d = self.vfl.read_single_page(self.ftlCtrlBlock * self.vfl.pages_per_sublk + p)
|
|
if not s:
|
|
continue
|
|
if s.type == FTL_CTX_TYPE:
|
|
print s.usn
|
|
ctx = FTLCxt.parse(d)
|
|
if ctx.versionLower == 0x46560001:
|
|
print ctx
|
|
assert ctx.FTLCtrlPage == (self.ftlCtrlBlock * self.vfl.pages_per_sublk + p)
|
|
print "Found most recent ctx"
|
|
break
|
|
if not ctx:
|
|
print "FTL_restore fail did not find ctx"
|
|
raise
|
|
blockMap = {}
|
|
self.nonSequential = {}
|
|
print "FTL_restore in progress ..."
|
|
for sblock in xrange(self.vfl.userSuBlksTotal + 23):
|
|
for page in xrange(self.vfl.pages_per_sublk):
|
|
s, d = self.vfl.read_single_page(sblock * self.vfl.pages_per_sublk + page)
|
|
if not s:
|
|
continue
|
|
if s.type >= FTL_CTX_TYPE and s.type <= FTL_CTX_TYPE_MAX:
|
|
break
|
|
if s.type != USER_TYPE and s.type != USER_LAST_TYPE:
|
|
print "Weird page type %x at %x %x" % (s.type, sblock, page)
|
|
continue
|
|
if s.lpn % self.vfl.pages_per_sublk != page:
|
|
print "Block %d non sequential" % sblock
|
|
self.nonSequential[sblock] = 1
|
|
blockMap[sblock] = (s.lpn / self.vfl.pages_per_sublk, s.usn)
|
|
break
|
|
|
|
z = dict([(i, [(a, blockMap[a][1]) for a in blockMap.keys() if blockMap[a][0] ==i]) for i in xrange(self.vfl.userSuBlksTotal)])
|
|
for k,v in z.items():
|
|
if len(v) == 2:
|
|
print k, v
|
|
vbA, usnA = v[0]
|
|
vbB, usnB = v[1]
|
|
if usnA > usnB: #smallest USN is map block, highest log block
|
|
self.pawMapTable[k] = vbB
|
|
self.restoreLogBlock(k, vbA)
|
|
else:
|
|
self.pawMapTable[k] = vbA
|
|
self.restoreLogBlock(k, vbB)
|
|
elif len(v) > 2:
|
|
raise Exception("fufu", k, v)
|
|
else:
|
|
self.pawMapTable[k] = v[0][0]
|
|
self.vfl.nand.cacheData("pLogs", self.pLogs)
|
|
self.vfl.nand.cacheData("pawMapTable", self.pawMapTable)
|
|
|
|
def restoreLogBlock(self, lbn, vbn):
|
|
log = {"wVbn": vbn, "wPageOffsets": {}}
|
|
for page in xrange(self.vfl.pages_per_sublk):
|
|
s, d = self.vfl.read_single_page(vbn * self.vfl.pages_per_sublk + page)
|
|
if not s:
|
|
break
|
|
log["wPageOffsets"][s.lpn % self.vfl.pages_per_sublk] = page
|
|
self.pLogs[lbn] = log
|
|
|
|
def mapPage(self, lbn, offset):
|
|
if self.pLogs.has_key(lbn):
|
|
if self.pLogs[lbn]["wPageOffsets"].has_key(offset):
|
|
offset = self.pLogs[lbn]["wPageOffsets"][offset]
|
|
#print "mapPage got log %d %d" % (lbn, offset)
|
|
return self.pLogs[lbn]["wVbn"] * self.vfl.pages_per_sublk + offset
|
|
if not self.pawMapTable.has_key(lbn):
|
|
return 0xFFFFFFFF
|
|
return self.pawMapTable[lbn] * self.vfl.pages_per_sublk + offset
|
|
|
|
def readLPN(self, lpn, key=None):
|
|
lbn = lpn / self.vfl.pages_per_sublk
|
|
offset = lpn % self.vfl.pages_per_sublk
|
|
vpn = self.mapPage(lbn, offset)
|
|
if vpn == 0xFFFFFFFF:
|
|
print "lbn not found %d" % lbn
|
|
return "\xFF" * self.nand.pageSize
|
|
s,d = self.vfl.read_single_page(vpn, key, lpn)
|
|
if not s:
|
|
return None
|
|
if s.lpn != lpn:
|
|
raise Exception("FTL translation FAIL spare lpn=%d vs expected %d" % (s.lpn, lpn))
|
|
return d
|
|
|