import argparse | |
import os | |
from subprocess import Popen, PIPE | |
version = "1.2" | |
#Fixed Pattern | |
EDBGINFO = "EDBGINFO\x05\x00\x00\x00" | |
SKIPPATTERN = "\x53\x4B\x49\x50" | |
MRDUMPBI = "MRDUMPBI" | |
#Start/End Guard Eable | |
haveStartGuard = False | |
haveEndGuard = False | |
#Default AP vmlinux path (for auto find function) | |
apSymbolPathWIN = "\\\\mtksfs00\\srv_colgin_ci\\image\\SanityLoad\\" | |
apSymbolPathLINUX = "/proj/srv_colgin_ci/image/SanityLoad/" | |
#Default value (would be overwitten) | |
remappingBank4 = [[0x40000000, 0x8e000000], [0x46000000, 0x84000000]] | |
remappingBank0 = [0x00000000, 0xae000000] | |
DRAMOffset = 0x40000000 | |
fileOffset = 0x00004000 | |
def ParsingArgument() : | |
parser = argparse.ArgumentParser() | |
parser.add_argument("-i", type=str, dest="core_dump", required=True, help="Input full ram dump path") | |
parser.add_argument("-o", type=str, dest="output", required=True, help="Output memory dump file path") | |
parser.add_argument("-p", "--use_mrdump_tool",type=str, dest="use_mrdump_tool", default="No", help="Set this flag \"True\" to use merdump tool to extract SYS_COREDUMP info for supporting lagacy version") | |
parser.add_argument("-e", "--elf", type=str, default='', dest="vmlinux", help="Input vmlinux (ELF) path") | |
parser.add_argument("-t", "--mrdump-tool", type=str, default='mrdump-gdb', dest="dump_tool_path",help="MRDUMP Tool path (ex. dump-tools-1.00/mrdump-gdb)\ | |
https://wiki.mediatek.inc/pages/viewpage.action?pageId=58229130") | |
return (parser.parse_args().core_dump, parser.parse_args().output, parser.parse_args().use_mrdump_tool, parser.parse_args().vmlinux, parser.parse_args().dump_tool_path) | |
def ConvertMD2APAddress(MDAddress) : | |
if MDAddress[-1] >> 4 == 0 or MDAddress[-1] >> 4 == 2 or MDAddress[-1] >> 4 == 3 or MDAddress[-1] >> 4 == 6 or MDAddress[-1] >> 4 == 9 : | |
bank = 0 | |
elif MDAddress[-1] >> 4 == 1 or MDAddress[-1] >> 4 == 7 : | |
bank = 1 | |
elif MDAddress[-1] >> 4 == 4 : | |
bank = 4 | |
else : | |
bank = -1 | |
intMDAddress = int.from_bytes(MDAddress[0:3], "little") | |
if bank == 0 : | |
APAddress = intMDAddress - remappingBank0[0] + remappingBank0[1] | |
elif bank == 1 : | |
APAddress = intMDAddress - remappingBank0[0] + remappingBank0[1] + 0x02000000 | |
elif bank == 4 : | |
if MDAddress[-1] & 15 < 6 : | |
APAddress = intMDAddress - remappingBank4[0][0] + remappingBank4[0][1] | |
else : | |
APAddress = intMDAddress - remappingBank4[1][0] + remappingBank4[1][1] | |
APAddress +=0x40000000 | |
else : | |
print("Can't find remapping address") | |
APAddress = intMDAddress | |
APAddress += (MDAddress[3] & 15) << 24 | |
return APAddress.to_bytes(4, "little") | |
def ParsingMIDRStartAddress(inp) : | |
inp.seek(remappingBank4[1][1] - DRAMOffset + fileOffset) | |
stage = 0 | |
ch = inp.read(1) | |
while ch : | |
if ord(ch) == ord(EDBGINFO[stage]) : | |
stage += 1 | |
else : | |
stage = 0 | |
if stage == len(EDBGINFO) : | |
return True | |
ch = inp.read(1) | |
return False | |
def ParsingTLV(inp, outp) : | |
global haveStartGuard, haveEndGuard | |
typeID = inp.read(1) | |
#By MDMP_TLV_END = 0 | |
while ord(typeID) != 0: | |
lens = inp.read(2) | |
value = inp.read(int.from_bytes(lens, byteorder="little")) | |
if int.from_bytes(value, "little") & 1 == True : | |
haveStartGuard = True | |
if int.from_bytes(value, "little") & 2 == True : | |
haveEndGuard = True | |
outp.write(typeID+lens+value) | |
typeID = inp.read(1) | |
lens = inp.read(2) | |
outp.write(typeID+lens) | |
def StartGuardCheck(inp, outp) : | |
startGuard = inp.read(1) | |
if startGuard != b'\xce' : | |
return False | |
outp.write(startGuard) | |
return True | |
# ToDo: when DHL add end guard, it need to add end guard check | |
def EndGuardCheck(inp, outp) : | |
endGuard = inp.read(1) | |
outp.write(endGuard) | |
return True | |
def ProcessMemOnEMI(inp, outp, mdAddress, lens) : | |
print("Processing mem on EMI : ") | |
apAddress = ConvertMD2APAddress(mdAddress) | |
print('MD address', '0x%08x'%int.from_bytes(mdAddress, "little")) | |
print("AP address", '0x%08x'%int.from_bytes(apAddress, "little")) | |
DRAMAddress = int.from_bytes(apAddress, "little") - DRAMOffset + fileOffset | |
print("File offset", '0x%08x'%DRAMAddress) | |
storeFilePointer = inp.tell() | |
inp.seek(DRAMAddress, 0) | |
memData = inp.read(int.from_bytes(lens, "little")) | |
outp.write(memData) | |
inp.seek(storeFilePointer, 0) | |
def ParsingDumpHeader(inp, outp) : | |
if haveStartGuard == True and StartGuardCheck(inp, outp) == False : | |
print("Error! Start Guard Missing") | |
exit() | |
memType = inp.read(1) | |
print("Mem type : ", hex(int.from_bytes(memType, "little"))) | |
if memType == b'\xff' : | |
endDump = inp.read(4) | |
# end dump check | |
if endDump != b"\x44\x45\x41\x44" : | |
print("End Dump Pattern Error!") | |
exit() | |
outp.write(memType+endDump) | |
return memType | |
# 128 = 0b1000 0000 | |
if ord(memType) & 128 : | |
headerType = 1 | |
else : | |
headerType = 0 | |
outp.write(memType) | |
address = inp.read(4) | |
outp.write(address) | |
addressMapping = -1 | |
if headerType == 1 : | |
addressMapping = inp.read(4) | |
outp.write(addressMapping) | |
length = inp.read(2) | |
outp.write(length) | |
if length == b'\xff\xff' : | |
length = inp.read(4) | |
outp.write(length) | |
print("Length: ", int.from_bytes(length, "little")) | |
skip = inp.read(4) | |
if skip == bytes(SKIPPATTERN, "ascii") : | |
ProcessMemOnEMI(inp, outp, address, length) | |
else : | |
inp.seek(-4, 1) | |
print('MD address', '0x%08x'%int.from_bytes(address, "little")) | |
rawData = inp.read(int.from_bytes(length, "little")) | |
outp.write(rawData) | |
if haveEndGuard == True and EndGuardCheck(inp, outp) == False: | |
print("Error! End Guard Missing") | |
return memType | |
def RebuildFullMemoryDumpFile(inputFileName, outputFileName) : | |
inp = open(inputFileName, "rb") | |
outp = open(outputFileName, "wb") | |
if ParsingMIDRStartAddress(inp) == False : | |
print("Not Found MIDR") | |
exit() | |
for byte in EDBGINFO : | |
outp.write(ord(byte).to_bytes(1, "little")) | |
ParsingTLV(inp, outp) | |
memType = ParsingDumpHeader(inp, outp) | |
while memType != b'\xff' : | |
print("\n======\n") | |
memType = ParsingDumpHeader(inp, outp) | |
inp.close() | |
outp.close() | |
def GetDRAMFileOffset(inputFileName) : | |
global fileOffset | |
proc = Popen("readelf -l -W %s"%inputFileName, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE) | |
commandResult = proc.communicate() | |
if commandResult[1] != b'' : | |
print(str(proc.communicate()[1], 'utf-8')) | |
print("%s is invalid!! Please use the correct SYS_COREDUMP file!!" %inputFileName) | |
exit() | |
result = commandResult[0] | |
result = str(result, 'utf-8') | |
SRAMPos = result.find("LOAD") | |
DRAMPos = result.find("LOAD", SRAMPos + 1) | |
hexStart = result.find("0x", DRAMPos) | |
hexEnd = result.find(" ", hexStart) | |
fileOffset = int(result[hexStart : hexEnd], 16) | |
print("File offset : 0x%08x" %fileOffset) | |
def MRDUMPToolParser(inputFileName, elf, dumpToolPath) : | |
proc = Popen("%s %s %s "%(dumpToolPath, elf, inputFileName), shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE) | |
result = proc.communicate("print /x modem_sys[0].mem_layout.md_bank0.base_ap_view_phy\n\ | |
print /x modem_sys[0].mem_layout.md_bank0.base_md_view_phy\n\ | |
print /x modem_sys[0].mem_layout.md_bank4_noncacheable.base_ap_view_phy\n\ | |
print /x modem_sys[0].mem_layout.md_bank4_noncacheable.base_md_view_phy\n\ | |
print /x modem_sys[0].mem_layout.md_bank4_cacheable.base_ap_view_phy\n\ | |
print /x modem_sys[0].mem_layout.md_bank4_cacheable.base_md_view_phy\n".encode())[0] | |
result = str(result, 'utf-8') | |
remappingBank0[1] = int(result[result.find("$1")+4 : result.find('\n', result.find("$1"))], 16) | |
remappingBank0[0] = int(result[result.find("$2")+4 : result.find('\n', result.find("$2"))], 16) | |
remappingBank4[0][1] = int(result[result.find("$3")+4 : result.find('\n', result.find("$3"))], 16) | |
remappingBank4[0][0] = int(result[result.find("$4")+4 : result.find('\n', result.find("$4"))], 16) | |
remappingBank4[1][1] = int(result[result.find("$5")+4 : result.find('\n', result.find("$5"))], 16) | |
remappingBank4[1][0] = int(result[result.find("$6")+4 : result.find('\n', result.find("$6"))], 16) | |
print("Bank0/1 \t\t MD address: 0x%08x <-> AP address 0x%08x"%(remappingBank0[0], remappingBank0[1])) | |
print("Bank4(noncacheable) \t MD address: 0x%08x <-> AP address 0x%08x"%(remappingBank4[0][0], remappingBank4[0][1])) | |
print("Bank4(cacheable) \t MD address: 0x%08x <-> AP address 0x%08x"%(remappingBank4[1][0], remappingBank4[1][1])) | |
GetDRAMFileOffset(inputFileName) | |
return | |
def GetAPInfo(coreDumpFile) : | |
checkInfoList = ["BRANCH_INFO=" , "VERNO=", "SUBTARGET=", "PLATFORM="] | |
apLoadInfo = [] | |
inp = open(coreDumpFile, "rb") | |
for info in checkInfoList : | |
stage = 0 | |
tempInfo = "" | |
wantInfo = "" | |
ch = inp.read(1) | |
while ch : | |
if ord(ch) == ord(info[stage]) : | |
stage += 1 | |
tempInfo = tempInfo + ch.decode() | |
else : | |
stage = 0 | |
tempInfo = "" | |
if stage == len(info) : | |
ch = inp.read(1) | |
if (ord(ch) >= ord('A') and ord(ch) <= ord('Z')) or (ord(ch) >= ord('a') and ord(ch) <= ord('z')) : | |
while ord(ch) != ord('\n') : | |
wantInfo = wantInfo + ch.decode() | |
ch = inp.read(1) | |
apLoadInfo.append(wantInfo) | |
break | |
else : | |
stage = 0 | |
tempInfo = "" | |
ch = inp.read(1) | |
if len(checkInfoList) != len(apLoadInfo): | |
return [] | |
return apLoadInfo | |
def AutoFindAPELF(coreDumpFile) : | |
print ("======\nAuto find AP ELF mode...") | |
apInfo = GetAPInfo(coreDumpFile) | |
if len(apInfo) == 0 : | |
print("\nCan't find AP version info in full RAM dump!\nPlease enter the vmliux path manually!") | |
exit() | |
print("\nBRANCH_INFO:\t%s\nVERNO:\t\t%s\nSUBTARGET:\t%s\nPLATFORM:\t%s\n" %(apInfo[0], apInfo[1], apInfo[2], apInfo[3])) | |
vmlinuxPath = apSymbolPathLINUX + apInfo[0] + "/Symbols/" + apInfo[1] + "/" + apInfo[3] + "/" + apInfo[2] + "/vmlinux" | |
if os.path.isfile(vmlinuxPath) == False : | |
print("Can't find the vmlinux in %s !\nPlease update your AP version or enter the vmliux path manually!" %vmlinuxPath) | |
exit() | |
print("vmlinux path : " + vmlinuxPath + "\n") | |
return vmlinuxPath | |
# Manual input mode (for backup) | |
def ManualInput() : | |
bank0StartAddress = input("Please enter the AP address mapping to Bank0/1 (0x00000000) :\n") | |
bank4MDCacheableStartAddress = input("Please enter the Bank4 cacheable MD address :\n") | |
bank4CacheableStartAddress = input("Please enter the AP address mapping to Bank4 (cacheable) :\n") | |
bank4UncacheableStartAddress = input("Please enter the AP address mapping to Bank4 (uncacheable) :\n") | |
remappingBank0[1] = int(bank0StartAddress, 16) | |
remappingBank4[1][0] = int(bank4MDCacheableStartAddress, 16) | |
remappingBank4[1][1] = int(bank4CacheableStartAddress, 16) | |
remappingBank4[0][1] = int(bank4UncacheableStartAddress, 16) | |
def ParseMRDUMPBIHeader(coreDumpFile) : | |
inp = open(coreDumpFile, "rb") | |
stage = 0 | |
currentPos = 1 | |
foundMRDUMPBI = False | |
GetDRAMFileOffset(coreDumpFile) | |
ch = inp.read(1) | |
# MRDUMPBI should be stored before DRAM dump | |
while ch and currentPos <= fileOffset: | |
# Parsing MRDUMPBI to find MD/AP remapping info | |
if ord(ch) == ord(MRDUMPBI[stage]) : | |
stage += 1 | |
else : | |
stage = 0 | |
currentPos = currentPos + 1 | |
if stage == len(MRDUMPBI) : | |
# Get AP/MD address remapping info | |
remappingBank0[1] = int.from_bytes(inp.read(8), "little") | |
remappingBank0[0] = int.from_bytes(inp.read(8), "little") | |
remappingBank4[1][1] = int.from_bytes(inp.read(8), "little") | |
remappingBank4[1][0] = int.from_bytes(inp.read(8), "little") | |
remappingBank4[0][1] = int.from_bytes(inp.read(8), "little") | |
remappingBank4[0][0] = int.from_bytes(inp.read(8), "little") | |
print("Bank0/1 \t\t MD address: 0x%08x <-> AP address: 0x%08x"%(remappingBank0[0], remappingBank0[1])) | |
print("Bank4(noncacheable) \t MD address: 0x%08x <-> AP address: 0x%08x"%(remappingBank4[0][0], remappingBank4[0][1])) | |
print("Bank4(cacheable) \t MD address: 0x%08x <-> AP address: 0x%08x"%(remappingBank4[1][0], remappingBank4[1][1])) | |
foundMRDUMPBI = True | |
break | |
ch = inp.read(1) | |
if foundMRDUMPBI == False : | |
print("\nCan't find MRDUMPBI! The AP version doesn't provide AP/MD address remapping info!!\n\nEnter maunal input mode: \n") | |
ManualInput() | |
if __name__ == "__main__": | |
print("MIDR dump post-processing script V %s"%version) | |
inputFileName, outputFileName, useMRDumptool, elf, dumpToolPath = ParsingArgument() | |
if useMRDumptool == "True" : | |
# Legacy version : need mrdump tool and vmlinux file to get AP/MD address remapping | |
if os.name != "posix" : | |
print("\nmrdump tool only can run on Linux!") | |
exit() | |
# Test mrdump tool | |
proc = Popen("%s "%dumpToolPath, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE) | |
result = proc.communicate() | |
if str(result[1], 'utf-8') != "" : | |
print(str(result[1], 'utf-8')) | |
print("Please enter the correct mrdump tool path!") | |
exit() | |
if elf == '' : | |
elf = AutoFindAPELF(inputFileName) | |
print("======\nGet MD/AP address remapping information by mrdump tool...") | |
MRDUMPToolParser(inputFileName, elf, dumpToolPath) | |
print("\nDone!\n") | |
else : | |
# From V1.2 : AP write AP/MD address remapping info to SYS_COREDUMP header, we can parse header to get those info | |
print("======\nGet MD/AP address remapping information from dump header...") | |
ParseMRDUMPBIHeader(inputFileName) | |
print("\nDone!\n") | |
print("======\n\nGenerating FULL Memory Dump File...\n") | |
RebuildFullMemoryDumpFile(inputFileName, outputFileName) | |
print("Done!") |