diff --git a/linux-ramdump-parser-v2/README b/linux-ramdump-parser-v2/README index 6819a253f1bde68afaaedf74889e55430e345b82..41c13a715fa9b0ba296485b1ce2db1e14092dd5e 100644 --- a/linux-ramdump-parser-v2/README +++ b/linux-ramdump-parser-v2/README @@ -67,6 +67,8 @@ is Currently supported features: gdb_path - absolute path to the gdb tool for the ramdumps nm_path - absolute path to the gdb tool for the ramdumps +gdb64_path - absolute path to the 64-bit gdb tool for the ramdumps +nm64_path - absolute path to the 64-bit nm tool for the ramdumps Note that local_settings.py is just a python file so the file may take advantage of python features. diff --git a/linux-ramdump-parser-v2/mmu.py b/linux-ramdump-parser-v2/mmu.py index bc4da987b5c5a5f3ddc6f8d4aae5c54e7c0fce6b..e7e95c1394e10fc3fe21c5b8c8d4dc0b2c5bedf2 100644 --- a/linux-ramdump-parser-v2/mmu.py +++ b/linux-ramdump-parser-v2/mmu.py @@ -332,3 +332,140 @@ class Armv7LPAEMMU(MMU): f.write( 'Dumping page tables is not currently supported for Armv7LPAEMMU\n') f.flush() + +class Armv8MMU(MMU): + + """An MMU for ARMv8 VMSA""" + # Descriptor types + DESCRIPTOR_INVALID = 0x0 + DESCRIPTOR_BLOCK = 0x1 + DESCRIPTOR_TABLE = 0x3 + TL_DESCRIPTOR_RESERVED = 0x1 + TL_DESCRIPTOR_PAGE = 0x3 + + def do_fl_sl_level_lookup(self, table_base_address, table_index, + input_addr_split, block_split): + descriptor, addr = self.do_level_lookup( + table_base_address, table_index, + input_addr_split) + if descriptor.dtype == Armv8MMU.DESCRIPTOR_BLOCK: + descriptor.add_field('output_address', (47, block_split)) + elif descriptor.dtype == Armv8MMU.DESCRIPTOR_TABLE: + # we have bits 39:12 of the next-level table in + # next_level_base_addr_upper + descriptor.add_field('next_level_base_addr_upper', (47, 12)) + else: + raise Exception( + 'Invalid stage 1 first- or second-level translation\ndescriptor: (%s)\naddr: (%s)' + % (str(descriptor), str(addr)) + ) + return descriptor + + def do_fl_level_lookup(self, table_base_address, table_index, + input_addr_split): + return self.do_fl_sl_level_lookup(table_base_address, table_index, + input_addr_split, 30) + + def do_sl_level_lookup(self, table_base_address, table_index): + return self.do_fl_sl_level_lookup(table_base_address, table_index, + 12, 21) + + def do_tl_level_lookup(self, table_base_address, table_index): + descriptor, addr = self.do_level_lookup( + table_base_address, table_index, 12) + if descriptor.dtype == Armv8MMU.TL_DESCRIPTOR_PAGE: + descriptor.add_field('output_address', (47, 12)) + else: + raise Exception( + 'Invalid stage 1 third-level translation\ndescriptor: (%s)\naddr: (%s)' + % (str(descriptor), str(addr)) + ) + return descriptor + + def do_level_lookup(self, table_base_address, table_index, + input_addr_split): + """Does a base + index descriptor lookup. + + Returns a tuple with the Register object representing the found + descriptor and a Register object representing the the computed + descriptor address. + + """ + n = input_addr_split + # these Registers are overkill but nice documentation:). + table_base = Register(table_base_address, base=(47, n)) + descriptor_addr = Register(table_base_address, base=(47, n), + offset=(n - 1, 3)) + descriptor_addr.offset = table_index + descriptor_val = self.read_phys_dword(descriptor_addr.value) + descriptor = Register(descriptor_val, + dtype=(1, 0)) + return descriptor, descriptor_addr + + def block_or_page_desc_2_phys(self, desc, virt_r, n): + phys = Register(output_address=(47, n), + page_offset=(n - 1, 0)) + phys.output_address = desc.output_address + virt_r.add_field('rest', (n - 1, 0)) + phys.page_offset |= virt_r.rest + return phys.value + + def fl_block_desc_2_phys(self, desc, virt_r): + """Block descriptor to physical address.""" + return self.block_or_page_desc_2_phys(desc, virt_r, 30) + + def sl_block_desc_2_phys(self, desc, virt_r): + """Block descriptor to physical address.""" + return self.block_or_page_desc_2_phys(desc, virt_r, 21) + + def tl_page_desc_2_phys(self, desc, virt_r): + """Page descriptor to physical address.""" + return self.block_or_page_desc_2_phys(desc, virt_r, 12) + + def read_phys_dword(self, physaddr): + return self.ramdump.read_dword(physaddr, virtual=False) + + def load_page_tables(self): + pass + + def page_table_walk(self, virt): + + self.ttbr = self.ramdump.swapper_pg_dir_addr + self.ramdump.phys_offset + + virt_r = Register(virt, + zl_index=(47,39), + fl_index=(38,30), + sl_index=(29,21), + tl_index=(20,12), + page_index=(11,0)) + + fl_desc = self.do_fl_sl_level_lookup(self.ttbr, virt_r.fl_index, 12, 30) + + if fl_desc.dtype == Armv8MMU.DESCRIPTOR_BLOCK: + return self.fl_block_desc_2_phys(fl_desc, virt_r) + + base = Register(base=(47, 12)) + base.base = fl_desc.next_level_base_addr_upper + try: + sl_desc = self.do_sl_level_lookup( + base.value, virt_r.sl_index) + except: + return None + + if sl_desc.dtype == Armv8MMU.DESCRIPTOR_BLOCK: + r = self.sl_block_desc_2_phys(sl_desc, virt_r) + return r + + base.base = sl_desc.next_level_base_addr_upper + try: + tl_desc = self.do_tl_level_lookup(base.value, virt_r.tl_index) + except: + return None + + r = self.tl_page_desc_2_phys(tl_desc, virt_r) + return r + + def dump_page_tables(self, f): + f.write( + 'Dumping page tables is not currently supported for Armv8MMU\n') + f.flush() diff --git a/linux-ramdump-parser-v2/ramdump.py b/linux-ramdump-parser-v2/ramdump.py index a7399d67cf2b3c0c0dbf18ed6262a327c062687f..6badffc6521d17447a92a010767adb13581371ca 100644 --- a/linux-ramdump-parser-v2/ramdump.py +++ b/linux-ramdump-parser-v2/ramdump.py @@ -15,19 +15,19 @@ import os import struct import gzip import functools +import string from boards import get_supported_boards, get_supported_ids from tempfile import NamedTemporaryFile import gdbmi from print_out import print_out_str -from mmu import Armv7MMU, Armv7LPAEMMU +from mmu import Armv7MMU, Armv7LPAEMMU, Armv8MMU from parser_util import cleanupString FP = 11 SP = 13 LR = 14 PC = 15 -THREAD_SIZE = 8192 launch_config_str = 'OS=\nID=T32_1000002\nTMP=C:\\TEMP\nSYS=C:\\T32\nHELP=C:\\T32\\pdf\n\nPBI=SIM\nSCREEN=\nFONT=SMALL\nHEADER=Trace32-ScorpionSimulator\nPRINTER=WINDOWS' @@ -69,7 +69,10 @@ class RamDump(): end = ramdump.addr_lookup('__stop_unwind_idx') self.ramdump = ramdump if (start is None) or (end is None): - self.unwind_frame = self.unwind_frame_generic + if ramdump.arm64: + self.unwind_frame = self.unwind_frame_generic64 + else: + self.unwind_frame = self.unwind_frame_generic return None # addresses self.unwind_frame = self.unwind_frame_tables @@ -104,12 +107,20 @@ class RamDump(): stop = mid return stop + def unwind_frame_generic64(self, frame, trace=False): + fp = frame.fp + + frame.sp = fp + 0x10 + frame.fp = self.ramdump.read_word(fp) + frame.pc = self.ramdump.read_word(fp + 8) + return 0 + def unwind_frame_generic(self, frame): high = 0 fp = frame.fp low = frame.sp - mask = (THREAD_SIZE) - 1 + mask = (self.ramdump.thread_size) - 1 high = (low + mask) & (~mask) # ALIGN(low, THREAD_SIZE) @@ -328,8 +339,8 @@ class RamDump(): def unwind_frame_tables(self, frame, trace=False): low = frame.sp - high = ((low + (THREAD_SIZE - 1)) & ~(THREAD_SIZE - 1)) + \ - THREAD_SIZE + high = ((low + (self.ramdump.thread_size - 1)) & \ + ~(self.ramdump.thread_size - 1)) + self.ramdump.thread_size idx = self.search_idx(frame.pc) if (idx is None): @@ -399,6 +410,9 @@ class RamDump(): where = frame.pc offset = 0 + if frame.pc is None: + break + r = self.ramdump.unwind_lookup(frame.pc) if r is None: symname = 'UNKNOWN' @@ -416,7 +430,7 @@ class RamDump(): if urc < 0: break - def __init__(self, vmlinux_path, nm_path, gdb_path, ebi, file_path, phys_offset, outdir, hw_id=None, hw_version=None): + def __init__(self, vmlinux_path, nm_path, gdb_path, ebi, file_path, phys_offset, outdir, hw_id=None, hw_version=None, arm64=False): self.ebi_files = [] self.phys_offset = None self.tz_start = 0 @@ -432,7 +446,9 @@ class RamDump(): self.imem_fname = None self.gdbmi = gdbmi.GdbMI(self.gdb_path, self.vmlinux) self.gdbmi.open() + self.arm64 = arm64 self.page_offset = 0xc0000000 + self.thread_size = 8192 if ebi is not None: # TODO sanity check to make sure the memory regions don't overlap for file_path, start, end in ebi: @@ -456,6 +472,9 @@ class RamDump(): self.lookup_table = [] self.page_offset = 0xc0000000 self.config = [] + if self.arm64: + self.page_offset = 0xffffffc000000000 + self.thread_size = 16384 self.setup_symbol_tables() # The address of swapper_pg_dir can be used to determine @@ -466,7 +485,10 @@ class RamDump(): self.swapper_pg_dir_addr = self.addr_lookup('swapper_pg_dir') - self.page_offset self.kernel_text_offset = self.addr_lookup('stext') - self.page_offset pg_dir_size = self.kernel_text_offset - self.swapper_pg_dir_addr - if pg_dir_size == 0x4000: + if self.arm64: + print_out_str('Using 64bit MMU') + self.mmu = Armv8MMU(self) + elif pg_dir_size == 0x4000: print_out_str('Using non-LPAE MMU') self.mmu = Armv7MMU(self) elif pg_dir_size == 0x5000: @@ -914,21 +936,27 @@ class RamDump(): else: return s[0] - # returns the 4 bytes read from the specified virtual address - # return None on error + # returns a word size (pointer) read from ramdump def read_word(self, address, virtual=True, trace=False, cpu=None): if trace: print_out_str('reading {0:x}'.format(address)) - s = self.read_string(address, '<I', virtual, trace, cpu) + if self.arm64: + s = self.read_string(address, '<Q', virtual, trace, cpu) + else: + s = self.read_string(address, '<I', virtual, trace, cpu) if s is None: return None else: return s[0] + # returns a value corresponding to half the word size def read_halfword(self, address, virtual=True, trace=False, cpu=None): if trace: print_out_str('reading {0:x}'.format(address)) - s = self.read_string(address, '<H', virtual, trace, cpu) + if self.arm64: + s = self.read_string(address, '<I', virtual, trace, cpu) + else: + s = self.read_string(address, '<H', virtual, trace, cpu) if s is None: return None else: @@ -1025,17 +1053,32 @@ class RamDump(): def iter_cpus(self): return xrange(self.get_num_cpus()) - def thread_saved_field_common(self, task, reg_offset): + def thread_saved_field_common_32(self, task, reg_offset): thread_info = self.read_word(task + self.field_offset('struct task_struct', 'stack')) cpu_context_offset = self.field_offset('struct thread_info', 'cpu_context') val = self.read_word(thread_info + cpu_context_offset + reg_offset) return val + def thread_saved_field_common_64(self, task, reg_offset): + thread_offset = self.field_offset('struct task_struct', 'thread') + cpu_context_offset = self.field_offset('struct thread_struct', 'cpu_context') + val = self.read_word(task + thread_offset + cpu_context_offset + reg_offset) + return val + def thread_saved_pc(self, task): - return self.thread_saved_field_common(task, self.field_offset('struct cpu_context_save', 'pc')) + if self.arm64: + return self.thread_saved_field_common_64(task, self.field_offset('struct cpu_context', 'pc')) + else: + return self.thread_saved_field_common_32(task, self.field_offset('struct cpu_context_save', 'pc')) def thread_saved_sp(self, task): - return self.thread_saved_field_common(task, self.field_offset('struct cpu_context_save', 'sp')) + if self.arm64: + return self.thread_saved_field_common_64(task, self.field_offset('struct cpu_context', 'sp')) + else: + return self.thread_saved_field_common_32(task, self.field_offset('struct cpu_context_save', 'sp')) def thread_saved_fp(self, task): - return self.thread_saved_field_common(task, self.field_offset('struct cpu_context_save', 'fp')) + if self.arm64: + return self.thread_saved_field_common_64(task, self.field_offset('struct cpu_context', 'fp')) + else: + return self.thread_saved_field_common_32(task, self.field_offset('struct cpu_context_save', 'fp')) diff --git a/linux-ramdump-parser-v2/ramparse.py b/linux-ramdump-parser-v2/ramparse.py index f382552c7b990fd6b810ca93ec10b41f0e6d18d8..105bef1d1ef3e397cb1bd263114d3302fc259d13 100755 --- a/linux-ramdump-parser-v2/ramparse.py +++ b/linux-ramdump-parser-v2/ramparse.py @@ -72,6 +72,8 @@ if __name__ == '__main__': help='Force the hardware detection to a specific hardware version') parser.add_option('', '--parse-qdss', action='store_true', dest='qdss', help='Parse QDSS (deprecated)') + parser.add_option('', '--64-bit', action='store_true', dest='arm64', + help='Parse dumps as 64-bit dumps') for p in parser_util.get_parsers(): parser.add_option(p.shortopt or '', @@ -150,8 +152,12 @@ if __name__ == '__main__': try: import local_settings - gdb_path = gdb_path or local_settings.gdb_path - nm_path = nm_path or local_settings.nm_path + if options.arm64: + gdb_path = gdb_path or local_settings.gdb64_path + nm_path = nm_path or local_settings.nm64_path + else: + gdb_path = gdb_path or local_settings.gdb_path + nm_path = nm_path or local_settings.nm_path except ImportError: cross_compile = os.environ.get('CROSS_COMPILE') if cross_compile is not None: @@ -188,7 +194,8 @@ if __name__ == '__main__': dump = RamDump(options.vmlinux, nm_path, gdb_path, options.ram_addr, options.autodump, options.phys_offset, options.outdir, - options.force_hardware, options.force_hardware_version) + options.force_hardware, options.force_hardware_version, + arm64=options.arm64) if not dump.print_command_line(): print_out_str('!!! Error printing saved command line.')