initial code for dumping imessages in a reasonable format
This commit is contained in:
@@ -0,0 +1,357 @@
|
||||
from array import array
|
||||
from construct.core import Struct, Union
|
||||
from construct.macros import *
|
||||
from progressbar import ProgressBar
|
||||
from structs import *
|
||||
import struct
|
||||
|
||||
|
||||
#https://github.com/iDroid-Project/openiBoot/blob/master/openiboot/ftl-yaftl/yaftl.c
|
||||
YAFTL_CXT = Struct("YAFTL_CXT",
|
||||
String("version", 4),
|
||||
ULInt32("unknCalculatedValue0"),
|
||||
ULInt32("totalPages"),
|
||||
ULInt32("latestUserBlock"),
|
||||
ULInt32("cxt_unkn0_usn"),
|
||||
ULInt32("latestIndexBlock"),
|
||||
ULInt32("maxIndexUsn"),
|
||||
ULInt32("blockStatsField4"),
|
||||
ULInt32("blockStatsField10"),
|
||||
ULInt32("numAllocatedBlocks"),
|
||||
ULInt32("numIAllocatedBlocks"),
|
||||
ULInt32("unk184_0xA"),
|
||||
Array(10, ULInt32("cxt_unkn1")),
|
||||
ULInt32("field_58"),
|
||||
ULInt16("tocArrayLength"),
|
||||
ULInt16("tocPagesPerBlock"),
|
||||
ULInt16("tocEntriesPerPage"),
|
||||
ULInt16("unkn_0x2A"),
|
||||
ULInt16("userPagesPerBlock"),
|
||||
ULInt16("unk64"),
|
||||
Array(11, ULInt32("cxt_unkn2")),
|
||||
ULInt8("unk188_0x63"),
|
||||
)
|
||||
|
||||
TOCStruct = Struct("TOCStruct",
|
||||
ULInt32("indexPage"),
|
||||
ULInt16("cacheNum"),
|
||||
ULInt16("TOCUnkMember2"),
|
||||
)
|
||||
|
||||
BlockStats = Struct("BlockStats",
|
||||
ULInt32("numAllocated"),
|
||||
ULInt32("field_4"),
|
||||
ULInt32("numValidDPages"),
|
||||
ULInt32("numIAllocated"),
|
||||
ULInt32("field_10"),
|
||||
ULInt32("numValidIPages"),
|
||||
ULInt32("numFree"),
|
||||
ULInt32("field_1C"),
|
||||
)
|
||||
|
||||
|
||||
class YAFTL(object):
|
||||
def __init__(self, vfl, usn=0):
|
||||
self.vfl = vfl
|
||||
self.lpnToVpn = None
|
||||
bytesPerPage = vfl.nand.pageSize
|
||||
numBlocks = vfl.context.usable_blocks_per_bank
|
||||
self.blankPage = bytesPerPage * "\x00"
|
||||
self.numBlocks = numBlocks
|
||||
self.tocPagesPerBlock = vfl.pages_per_sublk * 4 / bytesPerPage
|
||||
if vfl.pages_per_sublk * 4 % bytesPerPage:
|
||||
self.tocPagesPerBlock += 1
|
||||
self.tocEntriesPerPage = bytesPerPage / 4
|
||||
self.tocArrayLength = CEIL_DIVIDE(vfl.pages_per_sublk * numBlocks * 4, bytesPerPage)
|
||||
self.nPagesTocPageIndices = CEIL_DIVIDE(self.tocArrayLength * 4, bytesPerPage)
|
||||
self.nPagesBlockStatuses = CEIL_DIVIDE(numBlocks * 1, bytesPerPage)
|
||||
self.nPagesBlockReadCounts = CEIL_DIVIDE(numBlocks * 2, bytesPerPage)
|
||||
self.nPagesBlockEraseCounts = CEIL_DIVIDE(numBlocks * 4, bytesPerPage)
|
||||
self.nPagesBlockValidPagesDNumbers = self.nPagesBlockReadCounts
|
||||
self.nPagesBlockValidPagesINumbers = self.nPagesBlockReadCounts
|
||||
self.ctrlBlockPageOffset = self.nPagesTocPageIndices \
|
||||
+ self.nPagesBlockStatuses \
|
||||
+ self.nPagesBlockReadCounts \
|
||||
+ self.nPagesBlockEraseCounts \
|
||||
+ self.nPagesBlockValidPagesDNumbers \
|
||||
+ self.nPagesBlockValidPagesINumbers \
|
||||
+ 2 * self.tocPagesPerBlock \
|
||||
+ 2
|
||||
self.totalPages = (self.numBlocks - 8) * (self.vfl.pages_per_sublk - self.tocPagesPerBlock)# - unknCalculatedValue0
|
||||
self.userPagesPerBlock = self.vfl.pages_per_sublk - self.tocPagesPerBlock
|
||||
maxUsn = 0
|
||||
ftlCtrlBlock = -1
|
||||
for b in self.vfl.VFL_get_FTLCtrlBlock():
|
||||
s,d = self.YAFTL_readPage(b * self.vfl.pages_per_sublk)
|
||||
if not d:
|
||||
continue
|
||||
if usn and s.usn > usn:
|
||||
break
|
||||
if s.usn > maxUsn:
|
||||
maxUsn = s.usn
|
||||
ftlCtrlBlock = b
|
||||
if ftlCtrlBlock == -1 or not maxUsn:
|
||||
print "ftlCtrlBlock not found, restore needed"
|
||||
self.YAFTL_restore()
|
||||
return
|
||||
i = 0
|
||||
maxUsn = 0
|
||||
while i < self.vfl.pages_per_sublk - self.ctrlBlockPageOffset:
|
||||
s,d = self.YAFTL_readPage(ftlCtrlBlock*self.vfl.pages_per_sublk + i + self.ctrlBlockPageOffset)
|
||||
if not d:
|
||||
if self.YAFTL_readCxtInfo(ftlCtrlBlock*self.vfl.pages_per_sublk + i):
|
||||
return
|
||||
print "YaFTL_readCxtInfo FAIL, restore needed maxUsn=%d" % maxUsn
|
||||
self.YAFTL_restore()
|
||||
return
|
||||
if s and s.usn > maxUsn:
|
||||
maxUsn = s.usn
|
||||
i += self.ctrlBlockPageOffset + 1
|
||||
print "YaFTL open fail"
|
||||
self.YAFTL_restore()
|
||||
|
||||
def readBTOCPages(self, block, maxVal):
|
||||
data = ""
|
||||
for i in xrange(self.tocPagesPerBlock):
|
||||
s,d = self.YAFTL_readPage((block+1) * self.vfl.pages_per_sublk - self.tocPagesPerBlock + i)
|
||||
if not s:
|
||||
return None
|
||||
data += d
|
||||
btoc = array("I",data)
|
||||
for i in xrange(len(btoc)):
|
||||
if btoc[i] > maxVal:
|
||||
btoc[i] = 0xFFFFFFFF
|
||||
return btoc
|
||||
|
||||
def YAFTL_restore(self):
|
||||
self.lpnToVpn = self.vfl.nand.loadCachedData("yaftlrestore")
|
||||
if self.lpnToVpn:
|
||||
print "Found cached FTL restore information"
|
||||
return
|
||||
userBlocks = {}
|
||||
indexBlocks = {}
|
||||
print "FTL restore in progress"
|
||||
pbar = ProgressBar(self.numBlocks)
|
||||
pbar.start()
|
||||
for b in xrange(0, self.numBlocks):
|
||||
pbar.update(b)
|
||||
#read fist page in block, if empty then block is empty
|
||||
s,d = self.YAFTL_readPage(b * self.vfl.pages_per_sublk + 0)
|
||||
if not s:
|
||||
continue
|
||||
if s.type == PAGETYPE_INDEX:
|
||||
indexBlocks[s.usn] = b
|
||||
elif s.type == PAGETYPE_LBN:
|
||||
if userBlocks.has_key(s.usn):
|
||||
print "Two blocks with same USN, something is weird"
|
||||
userBlocks[s.usn] = b
|
||||
elif s.type == PAGETYPE_FTL_CLEAN:
|
||||
pass
|
||||
pbar.finish()
|
||||
lpnToVpn = {}
|
||||
for usn in sorted(userBlocks.keys(), reverse=True):
|
||||
b = userBlocks[usn]
|
||||
btoc = self.readBTOCPages(b, self.totalPages)
|
||||
if btoc:
|
||||
for i in xrange(self.userPagesPerBlock-1,-1, -1):
|
||||
if not lpnToVpn.has_key(btoc[i]):
|
||||
lpnToVpn[btoc[i]] = b * self.vfl.pages_per_sublk + i
|
||||
else:
|
||||
print "BTOC not found for block %d (usn %d), scanning all pages" % (b, usn)
|
||||
i = 0
|
||||
for p in xrange(self.vfl.pages_per_sublk - self.tocPagesPerBlock -1, -1, -1):
|
||||
s,d = self.YAFTL_readPage(b * self.vfl.pages_per_sublk + p)
|
||||
if s:
|
||||
i+= 1
|
||||
if s and not lpnToVpn.has_key(s.lpn):
|
||||
lpnToVpn[s.lpn] = b * self.vfl.pages_per_sublk + p
|
||||
print "%d used pages in block" % i
|
||||
self.vfl.nand.cacheData("yaftlrestore", lpnToVpn)
|
||||
self.lpnToVpn = lpnToVpn
|
||||
return lpnToVpn
|
||||
|
||||
def YAFTL_readCxtInfo(self, page):
|
||||
s,d = self.YAFTL_readPage(page)
|
||||
if not s or s.type != PAGETYPE_FTL_CLEAN:
|
||||
return False
|
||||
ctx = YAFTL_CXT.parse(d)
|
||||
ctx.spareUsn = s.usn
|
||||
if ctx.version != "CX01":
|
||||
print "Wrong FTL version %s" % ctx.version
|
||||
return False
|
||||
self.usn = s.usn
|
||||
pageToRead = page + 1;
|
||||
userTOCBuffer = self.YAFTL_read_n_Page(pageToRead, self.tocPagesPerBlock)
|
||||
if not userTOCBuffer:
|
||||
raise(Exception("userTOCBuffer"))
|
||||
pageToRead += self.tocPagesPerBlock
|
||||
indexTOCBuffer = self.YAFTL_read_n_Page(pageToRead, self.tocPagesPerBlock)
|
||||
pageToRead += self.tocPagesPerBlock + 1
|
||||
tocArrayIndexPages = self.YAFTL_read_n_Page(pageToRead, self.nPagesTocPageIndices)
|
||||
self.tocArrayIndexPages = array("I", tocArrayIndexPages)
|
||||
assert self.tocArrayIndexPages.itemsize == 4
|
||||
self.indexCache = {}
|
||||
pageToRead += self.nPagesTocPageIndices
|
||||
|
||||
if False: #we don't care, we just want to read
|
||||
blockStatuses = self.YAFTL_read_n_Page(pageToRead, self.nPagesBlockStatuses)
|
||||
pageToRead += self.nPagesBlockStatuses
|
||||
blockReadCounts = self.YAFTL_read_n_Page(pageToRead, self.nPagesBlockReadCounts)
|
||||
pageToRead += self.nPagesBlockReadCounts
|
||||
blockEraseCounts = self.YAFTL_read_n_Page(pageToRead, self.nPagesBlockEraseCounts)
|
||||
pageToRead += self.nPagesBlockEraseCounts
|
||||
validPagesINo = self.YAFTL_read_n_Page(pageToRead, self.nPagesBlockValidPagesINumbers)
|
||||
pageToRead += self.nPagesBlockValidPagesINumbers
|
||||
validPagesDNo = self.YAFTL_read_n_Page(pageToRead, self.nPagesBlockValidPagesDNumbers)
|
||||
|
||||
print "YaFTL context OK, version=%s maxIndexUsn=%d context usn=%d" % (ctx.version, ctx.maxIndexUsn, self.usn)
|
||||
return True
|
||||
|
||||
def YAFTL_read_n_Page(self, page, n, failIfBlank=False):
|
||||
r = ""
|
||||
for i in xrange(0, n):
|
||||
s,d = self.YAFTL_readPage(page +i)
|
||||
if not d:
|
||||
if failIfBlank:
|
||||
return
|
||||
return r
|
||||
r += d
|
||||
return r
|
||||
|
||||
def YAFTL_readPage(self, page, key=META_KEY, lpn=None):
|
||||
return self.vfl.read_single_page(page, key, lpn)
|
||||
|
||||
def build_lpn_to_vpn(self):
|
||||
lpnToVpn = {}
|
||||
for p in xrange(self.totalPages):
|
||||
x = self.translateLPNtoVPN(p)
|
||||
if x != 0xFFFFFFFF:
|
||||
lpnToVpn[p] = x
|
||||
self.vfl.nand.cacheData("currentftl", lpnToVpn)
|
||||
return lpnToVpn
|
||||
|
||||
def translateLPNtoVPN(self, lpn):
|
||||
if self.lpnToVpn:
|
||||
return self.lpnToVpn.get(lpn, 0xFFFFFFFF)
|
||||
tocPageNum = (lpn) / self.tocEntriesPerPage
|
||||
indexPage = self.tocArrayIndexPages[tocPageNum]
|
||||
if indexPage == 0xffffffff:
|
||||
return 0xffffffff
|
||||
#print "indexPage %x" % indexPage
|
||||
if self.indexCache.has_key(indexPage):
|
||||
tocPageBuffer = self.indexCache[indexPage]
|
||||
else:
|
||||
s,tocPageBuffer = self.YAFTL_readPage(indexPage)
|
||||
if not tocPageBuffer:
|
||||
print "tocPageBuffer fail"
|
||||
return 0xffffffff
|
||||
assert s.type == PAGETYPE_INDEX
|
||||
tocPageBuffer = array("I", tocPageBuffer)
|
||||
self.indexCache[indexPage] = tocPageBuffer
|
||||
|
||||
tocEntry = tocPageBuffer[lpn % self.tocEntriesPerPage]
|
||||
return tocEntry
|
||||
|
||||
def readLPN(self, lpn, key=None):#, nPages):
|
||||
vpn = self.translateLPNtoVPN(lpn)
|
||||
if vpn == 0xffffffff:
|
||||
return self.blankPage
|
||||
#print "tocEntry %d" % tocEntry
|
||||
#print "FTL %d => %d" % (lpn, vpn)
|
||||
s,d = self.YAFTL_readPage(vpn, key, lpn)
|
||||
if d == None:
|
||||
return self.blankPage
|
||||
if s.lpn != lpn:
|
||||
raise Exception("YAFTL translation FAIL spare lpn=%d vs expected %d" % (s.lpn, lpn))
|
||||
return d
|
||||
|
||||
def YAFTL_lookup1(self):
|
||||
hax = self.vfl.nand.loadCachedData("YAFTL_lookup1")
|
||||
if hax:
|
||||
print "Found cached FTL lookup table"
|
||||
return hax
|
||||
userBlocks = {}
|
||||
indexBlocks = {}
|
||||
print "Building FTL lookup table v1"
|
||||
pbar = ProgressBar(self.numBlocks)
|
||||
pbar.start()
|
||||
for b in xrange(0, self.numBlocks):
|
||||
pbar.update(b)
|
||||
#read fist page in block, if empty then block is empty
|
||||
s,d = self.YAFTL_readPage(b * self.vfl.pages_per_sublk + 0)
|
||||
if not s:
|
||||
continue
|
||||
if s.type == PAGETYPE_INDEX:
|
||||
indexBlocks[s.usn] = b
|
||||
elif s.type == PAGETYPE_LBN:
|
||||
if userBlocks.has_key(s.usn):
|
||||
print "Two blocks with same USN, something is weird"
|
||||
userBlocks[s.usn] = b
|
||||
elif s.type == PAGETYPE_FTL_CLEAN:
|
||||
pass#print b, "ftl block"
|
||||
pbar.finish()
|
||||
lpnToVpn = {}
|
||||
for usn in sorted(userBlocks.keys(), reverse=False):
|
||||
b = userBlocks[usn]
|
||||
btoc = self.readBTOCPages(b, self.totalPages)
|
||||
#print usn, b
|
||||
if btoc:
|
||||
for i in xrange(self.userPagesPerBlock-1,-1, -1):
|
||||
lpnToVpn.setdefault(btoc[i], []).append(b * self.vfl.pages_per_sublk + i)
|
||||
else:
|
||||
#print "btoc not found for block %d (usn %d), scanning all pages" % (b, usn)
|
||||
i = 0
|
||||
usn = -1
|
||||
for p in xrange(self.vfl.pages_per_sublk - self.tocPagesPerBlock -1, -1, -1):
|
||||
s,d = self.YAFTL_readPage(b * self.vfl.pages_per_sublk + p)
|
||||
if not s:
|
||||
break
|
||||
i+= 1
|
||||
if usn == -1:
|
||||
usn = s.usn
|
||||
if usn != s.usn:
|
||||
#print "Two usns in same block %d %d" % (usn, s.usn)
|
||||
usn = s.usn
|
||||
lpnToVpn.setdefault(s.lpn, []).append(b * self.vfl.pages_per_sublk + p)
|
||||
#print "%d used pages in block" % i
|
||||
#self.vfl.nand.cacheData("YAFTL_lookup1", (lpnToVpn, userBlocks))
|
||||
return lpnToVpn, userBlocks
|
||||
|
||||
def YAFTL_hax2(self):
|
||||
hax = self.vfl.nand.loadCachedData("YAFTL_hax2")
|
||||
if hax:
|
||||
print "Found cached FTL HAX2 information"
|
||||
return hax
|
||||
|
||||
print "FTL hax2 in progress"
|
||||
pbar = ProgressBar(self.numBlocks)
|
||||
pbar.start()
|
||||
lpnToVpn = {}
|
||||
for b in xrange(0, self.numBlocks):
|
||||
pbar.update(b)
|
||||
#read fist page in block, if empty then block is empty (right?)
|
||||
s,d = self.YAFTL_readPage(b * self.vfl.pages_per_sublk + 0)
|
||||
if not s:
|
||||
continue
|
||||
if s.type == PAGETYPE_LBN:
|
||||
i = 0
|
||||
usn = -1
|
||||
for p in xrange(0, self.vfl.pages_per_sublk - self.tocPagesPerBlock):
|
||||
s,d = self.YAFTL_readPage(b * self.vfl.pages_per_sublk + p)
|
||||
if not s:
|
||||
break
|
||||
lpnToVpn.setdefault(s.lpn, {}).setdefault(s.usn, []).append(b * self.vfl.pages_per_sublk + p)
|
||||
i+= 1
|
||||
|
||||
pbar.finish()
|
||||
self.vfl.nand.cacheData("YAFTL_hax2", lpnToVpn)
|
||||
return lpnToVpn
|
||||
|
||||
def block_lpn_to_vpn(self, block):
|
||||
res = {}
|
||||
for p in xrange(0, self.vfl.pages_per_sublk - self.tocPagesPerBlock):
|
||||
s,d = self.YAFTL_readPage(block * self.vfl.pages_per_sublk + p)
|
||||
if not s:
|
||||
break
|
||||
res[s.lpn] = block * self.vfl.pages_per_sublk + p
|
||||
return res
|
||||
Reference in New Issue
Block a user