From 687e1bab02ad2b4c1e022d77d09969cc9743fee5 Mon Sep 17 00:00:00 2001 From: Soumen Ghosh <soumeng@codeaurora.org> Date: Tue, 9 May 2017 16:41:47 +0530 Subject: [PATCH] lrdp-v2:minidump parsing support add in ramparser A minidump supports fewer features than a full ramdump, but does not require the user to save all 4GB+ of memory. Instead a small subset of DDR is saved by crashscope as an elf file. Currently dmesg logs, rtb logs, and the cpu context parser are supported. Example usage: python ramparse.py --vmlinux <vmlinux> --minidump --ram-elf <elf_file> --ram-file <OCIMEM.bin> --everything Change-Id: Ib689b4811d66472ac20aa7c63893fdaed915512e --- linux-ramdump-parser-v2/README | 5 + linux-ramdump-parser-v2/boards.py | 1 + linux-ramdump-parser-v2/debug_image_v2.py | 354 ++++++++++++++-------- linux-ramdump-parser-v2/minidump_util.py | 56 ++++ linux-ramdump-parser-v2/ramdump.py | 264 ++++++++++------ linux-ramdump-parser-v2/ramparse.py | 48 ++- 6 files changed, 505 insertions(+), 223 deletions(-) mode change 100755 => 100644 linux-ramdump-parser-v2/boards.py mode change 100755 => 100644 linux-ramdump-parser-v2/debug_image_v2.py create mode 100644 linux-ramdump-parser-v2/minidump_util.py diff --git a/linux-ramdump-parser-v2/README b/linux-ramdump-parser-v2/README index efeea31..ac50385 100644 --- a/linux-ramdump-parser-v2/README +++ b/linux-ramdump-parser-v2/README @@ -72,6 +72,11 @@ specify the paths to these tools. This can be done in three ways 1) Using --gdb-path and --nm-path to specify the absolute path 2) Using CROSS_COMPILE to specify the prefix 3) Using local_settings.py as described below +4) Install this library from https://github.com/eliben/pyelftools + - Download the code from avobe github link. Download the zip file. + - After download the zip file, you will find a folder pyelftools-master. + - Inside this folder you will find another folder named "elftools" + - copy that entire folder and paste it in below directory <installed Python path>\Lib\site-packages Just having gdb/nm on the path is not supported as there are too many variations on names to invoke. diff --git a/linux-ramdump-parser-v2/boards.py b/linux-ramdump-parser-v2/boards.py old mode 100755 new mode 100644 index 3909b10..fc96d4d --- a/linux-ramdump-parser-v2/boards.py +++ b/linux-ramdump-parser-v2/boards.py @@ -451,6 +451,7 @@ class Board660(Board): self.cpu = 'CORTEXA53' self.ram_start = 0x80000000 self.smem_addr = 0x6000000 + self.smem_addr_buildinfo = 0x6006ec0 self.phys_offset = 0x80000000 self.imem_start = 0x14680000 self.kaslr_addr = 0x146bf6d0 diff --git a/linux-ramdump-parser-v2/debug_image_v2.py b/linux-ramdump-parser-v2/debug_image_v2.py old mode 100755 new mode 100644 index 302ee62..5e6332a --- a/linux-ramdump-parser-v2/debug_image_v2.py +++ b/linux-ramdump-parser-v2/debug_image_v2.py @@ -86,6 +86,25 @@ qdss_tag_to_field_name = { 'MSM_DUMP_DATA_DBGUI_REG': 'dbgui_start', } +# Client functions will be executed in top-to-bottom order +minidump_dump_table_type = [ + ('MSM_DUMP_DATA_SCANDUMP', 'KSCANDUMP'), + ('MSM_DUMP_DATA_CPU_CTX', 'KCPU_CTX'), + ('MSM_DUMP_DATA_L1_INST_TLB', 'KCPUSS'), + ('MSM_DUMP_DATA_L1_DATA_TLB','KCPUSS'), + ('MSM_DUMP_DATA_L1_INST_CACHE', 'KCPUSS'), + ('MSM_DUMP_DATA_L1_DATA_CACHE', 'KCPUSS'), + ('MSM_DUMP_DATA_L2_CACHE', 'KCPUSS'), + ('MSM_DUMP_DATA_L3_CACHE', 'KCPUSS'), + ('MSM_DUMP_DATA_VSENSE', 'KVSENSE'), + ('MSM_DUMP_DATA_PMIC', 'KPMIC'), + ('MSM_DUMP_DATA_DCC_REG', 'KDCC_REG'), + ('MSM_DUMP_DATA_DCC_SRAM', 'KDCC_SRAM'), + ('MSM_DUMP_DATA_TMC_ETF', 'KTMC_ETF'), + ('MSM_DUMP_DATA_TMC_REG', 'KTMC_REG') + +] + class DebugImage_v2(): def __init__(self, ramdump): @@ -491,6 +510,21 @@ class DebugImage_v2(): results.sort(key=lambda(x): client_names.index(x[0])) return results + def minidump_data_clients(self, ram_dump, client_id,entry_pa_addr, + end_addr): + results = list() + client_table = dict(client_types) + # get first column of client_types + + client_name = self.dump_data_id_lookup_table[client_id] + + if client_name not in client_table: + print_out_str( + '!!! {0} Does not have an associated function. Skipping!'.format(client_name)) + return None + + results.append((client_name, client_id,client_table[client_name], entry_pa_addr,end_addr)) + return results def parse_dump_v2(self, ram_dump): self.dump_type_lookup_table = ram_dump.gdbmi.get_enum_lookup_table( @@ -532,141 +566,207 @@ class DebugImage_v2(): client.MSM_DUMP_DATA_LOG_BUF_FIRST_IDX] = 'MSM_DUMP_DATA_LOG_BUF_FIRST_IDX' self.dump_data_id_lookup_table[ client.MSM_DUMP_DATA_L2_TLB] = 'MSM_DUMP_DATA_L2_TLB' - dump_table_ptr_offset = ram_dump.field_offset( - 'struct msm_memory_dump', 'table') - dump_table_version_offset = ram_dump.field_offset( - 'struct msm_dump_table', 'version') - dump_table_num_entry_offset = ram_dump.field_offset( - 'struct msm_dump_table', 'num_entries') - dump_table_entry_offset = ram_dump.field_offset( - 'struct msm_dump_table', 'entries') - dump_entry_id_offset = ram_dump.field_offset( - 'struct msm_dump_entry', 'id') - dump_entry_name_offset = ram_dump.field_offset( - 'struct msm_dump_entry', 'name') - dump_entry_type_offset = ram_dump.field_offset( - 'struct msm_dump_entry', 'type') - dump_entry_addr_offset = ram_dump.field_offset( - 'struct msm_dump_entry', 'addr') - dump_data_version_offset = ram_dump.field_offset( - 'struct msm_dump_data', 'version') - dump_data_magic_offset = ram_dump.field_offset( - 'struct msm_dump_data', 'magic') - dump_data_name_offset = ram_dump.field_offset( - 'struct msm_dump_data', 'name') - dump_data_addr_offset = ram_dump.field_offset( - 'struct msm_dump_data', 'addr') - dump_data_len_offset = ram_dump.field_offset( - 'struct msm_dump_data', 'len') - dump_data_reserved_offset = ram_dump.field_offset( - 'struct msm_dump_data', 'reserved') - dump_entry_size = ram_dump.sizeof('struct msm_dump_entry') - dump_data_size = ram_dump.sizeof('struct msm_dump_data') - - mem_dump_data = ram_dump.address_of('memdump') - - mem_dump_table = ram_dump.read_word( - mem_dump_data + dump_table_ptr_offset) - - mem_table_version = ram_dump.read_u32( - mem_dump_table + dump_table_version_offset) - if mem_table_version is None: - print_out_str('Version is bogus! Can\'t parse debug image') - return - mem_table_num_entry = ram_dump.read_u32( - mem_dump_table + dump_table_num_entry_offset) - if mem_table_num_entry is None or mem_table_num_entry > 100: - print_out_str('num_entries is bogus! Can\'t parse debug image') - return - - print_out_str('\nDebug image version: {0}.{1} Number of table entries {2}'.format( - mem_table_version >> 20, mem_table_version & 0xFFFFF, mem_table_num_entry)) - print_out_str('--------') - - for i in range(0, mem_table_num_entry): - this_entry = mem_dump_table + dump_table_entry_offset + \ - i * dump_entry_size - entry_id = ram_dump.read_u32(this_entry + dump_entry_id_offset) - entry_type = ram_dump.read_u32(this_entry + dump_entry_type_offset) - entry_addr = ram_dump.read_word(this_entry + dump_entry_addr_offset) - - if entry_id < 0 or entry_id > len(self.dump_table_id_lookup_table): - print_out_str( - '!!! Invalid dump table entry id found {0:x}'.format(entry_id)) - continue - - if entry_type > len(self.dump_type_lookup_table): - print_out_str( - '!!! Invalid dump table entry type found {0:x}'.format(entry_type)) - continue - table_version = ram_dump.read_u32( - entry_addr + dump_table_version_offset, False) - if table_version is None: - print_out_str('Dump table entry version is bogus! Can\'t parse debug image') + if not ram_dump.minidump: + dump_table_ptr_offset = ram_dump.field_offset( + 'struct msm_memory_dump', 'table') + dump_table_version_offset = ram_dump.field_offset( + 'struct msm_dump_table', 'version') + dump_table_num_entry_offset = ram_dump.field_offset( + 'struct msm_dump_table', 'num_entries') + dump_table_entry_offset = ram_dump.field_offset( + 'struct msm_dump_table', 'entries') + dump_entry_id_offset = ram_dump.field_offset( + 'struct msm_dump_entry', 'id') + dump_entry_name_offset = ram_dump.field_offset( + 'struct msm_dump_entry', 'name') + dump_entry_type_offset = ram_dump.field_offset( + 'struct msm_dump_entry', 'type') + dump_entry_addr_offset = ram_dump.field_offset( + 'struct msm_dump_entry', 'addr') + dump_data_version_offset = ram_dump.field_offset( + 'struct msm_dump_data', 'version') + dump_data_magic_offset = ram_dump.field_offset( + 'struct msm_dump_data', 'magic') + dump_data_name_offset = ram_dump.field_offset( + 'struct msm_dump_data', 'name') + dump_data_addr_offset = ram_dump.field_offset( + 'struct msm_dump_data', 'addr') + dump_data_len_offset = ram_dump.field_offset( + 'struct msm_dump_data', 'len') + dump_data_reserved_offset = ram_dump.field_offset( + 'struct msm_dump_data', 'reserved') + dump_entry_size = ram_dump.sizeof('struct msm_dump_entry') + dump_data_size = ram_dump.sizeof('struct msm_dump_data') + + mem_dump_data = ram_dump.address_of('memdump') + + mem_dump_table = ram_dump.read_word( + mem_dump_data + dump_table_ptr_offset) + + mem_table_version = ram_dump.read_u32( + mem_dump_table + dump_table_version_offset) + if mem_table_version is None: + print_out_str('Version is bogus! Can\'t parse debug image') return - table_num_entries = ram_dump.read_u32( - entry_addr + dump_table_num_entry_offset, False) - if table_num_entries is None or table_num_entries > 100: - print_out_str('Dump table entry num_entries is bogus! Can\'t parse debug image') + mem_table_num_entry = ram_dump.read_u32( + mem_dump_table + dump_table_num_entry_offset) + if mem_table_num_entry is None or mem_table_num_entry > 100: + print_out_str('num_entries is bogus! Can\'t parse debug image') return - print_out_str( - 'Debug image version: {0}.{1} Entry id: {2} Entry type: {3} Number of entries: {4}'.format( - table_version >> 20, table_version & 0xFFFFF, self.dump_table_id_lookup_table[entry_id], - self.dump_type_lookup_table[entry_type], table_num_entries)) - - lst = self.sorted_dump_data_clients( - ram_dump, entry_addr + dump_table_entry_offset, - table_num_entries) - for (client_name, func, client_entry) in lst: - print_out_str('--------') - client_id = ram_dump.read_u32( - client_entry + dump_entry_id_offset, False) - client_type = ram_dump.read_u32( - client_entry + dump_entry_type_offset, False) - client_addr = ram_dump.read_word( - client_entry + dump_entry_addr_offset, False) - - if client_type > len(self.dump_type_lookup_table): + print_out_str('\nDebug image version: {0}.{1} Number of table entries {2}'.format( + mem_table_version >> 20, mem_table_version & 0xFFFFF, mem_table_num_entry)) + print_out_str('--------') + + for i in range(0, mem_table_num_entry): + this_entry = mem_dump_table + dump_table_entry_offset + \ + i * dump_entry_size + entry_id = ram_dump.read_u32(this_entry + dump_entry_id_offset) + entry_type = ram_dump.read_u32(this_entry + dump_entry_type_offset) + entry_addr = ram_dump.read_word(this_entry + dump_entry_addr_offset) + + if entry_id < 0 or entry_id > len(self.dump_table_id_lookup_table): print_out_str( - '!!! Invalid dump client type found {0:x}'.format(client_type)) + '!!! Invalid dump table entry id found {0:x}'.format(entry_id)) continue - dump_data_magic = ram_dump.read_u32( - client_addr + dump_data_magic_offset, False) - dump_data_version = ram_dump.read_u32( - client_addr + dump_data_version_offset, False) - dump_data_name = ram_dump.read_cstring( - client_addr + dump_data_name_offset, - ram_dump.sizeof('((struct msm_dump_data *)0x0)->name'), - False) - dump_data_addr = ram_dump.read_dword( - client_addr + dump_data_addr_offset, False) - dump_data_len = ram_dump.read_dword( - client_addr + dump_data_len_offset, False) - print_out_str('Parsing debug information for {0}. Version: {1} Magic: {2:x} Source: {3}'.format( - client_name, dump_data_version, dump_data_magic, - dump_data_name)) - - if dump_data_magic is None: - print_out_str("!!! Address {0:x} is bogus! Can't parse!".format( - client_addr + dump_data_magic_offset)) + if entry_type > len(self.dump_type_lookup_table): + print_out_str( + '!!! Invalid dump table entry type found {0:x}'.format(entry_type)) continue - if dump_data_magic != MEMDUMPV2_MAGIC: - print_out_str("!!! Magic {0:x} doesn't match! No context will be parsed".format(dump_data_magic)) + table_version = ram_dump.read_u32( + entry_addr + dump_table_version_offset, False) + if table_version is None: + print_out_str('Dump table entry version is bogus! Can\'t parse debug image') + return + table_num_entries = ram_dump.read_u32( + entry_addr + dump_table_num_entry_offset, False) + if table_num_entries is None or table_num_entries > 100: + print_out_str('Dump table entry num_entries is bogus! Can\'t parse debug image') + return + + print_out_str( + 'Debug image version: {0}.{1} Entry id: {2} Entry type: {3} Number of entries: {4}'.format( + table_version >> 20, table_version & 0xFFFFF, self.dump_table_id_lookup_table[entry_id], + self.dump_type_lookup_table[entry_type], table_num_entries)) + + lst = self.sorted_dump_data_clients( + ram_dump, entry_addr + dump_table_entry_offset, + table_num_entries) + for (client_name, func, client_entry) in lst: + print_out_str('--------') + client_id = ram_dump.read_u32( + client_entry + dump_entry_id_offset, False) + client_type = ram_dump.read_u32( + client_entry + dump_entry_type_offset, False) + client_addr = ram_dump.read_word( + client_entry + dump_entry_addr_offset, False) + + if client_type > len(self.dump_type_lookup_table): + print_out_str( + '!!! Invalid dump client type found {0:x}'.format(client_type)) + continue + + dump_data_magic = ram_dump.read_u32( + client_addr + dump_data_magic_offset, False) + dump_data_version = ram_dump.read_u32( + client_addr + dump_data_version_offset, False) + dump_data_name = ram_dump.read_cstring( + client_addr + dump_data_name_offset, + ram_dump.sizeof('((struct msm_dump_data *)0x0)->name'), + False) + dump_data_addr = ram_dump.read_dword( + client_addr + dump_data_addr_offset, False) + dump_data_len = ram_dump.read_dword( + client_addr + dump_data_len_offset, False) + print_out_str('Parsing debug information for {0}. Version: {1} Magic: {2:x} Source: {3}'.format( + client_name, dump_data_version, dump_data_magic, + dump_data_name)) + + if dump_data_magic is None: + print_out_str("!!! Address {0:x} is bogus! Can't parse!".format( + client_addr + dump_data_magic_offset)) + continue + + if dump_data_magic != MEMDUMPV2_MAGIC: + print_out_str("!!! Magic {0:x} doesn't match! No context will be parsed".format(dump_data_magic)) + continue + + getattr(DebugImage_v2, func)( + self, dump_data_version, dump_data_addr, + dump_data_addr + dump_data_len, client_id, ram_dump) + else: + dump_smem_table_ptr_offset = ram_dump.field_offset( + 'struct md_table', 'md_smem_table') + dump_table_version_offset = ram_dump.field_offset( + 'struct md_smem_table', 'version') + dump_table_num_entry_offset = ram_dump.field_offset( + 'struct md_table', 'num_regions') + dump_table_entry_offset = ram_dump.field_offset( + 'struct md_table', 'entry') + dump_entry_name_offset = ram_dump.field_offset( + 'struct md_region', 'name') + dump_entry_id_offset = ram_dump.field_offset( + 'struct md_region', 'id') + dump_entry_va_offset = ram_dump.field_offset( + 'struct md_region', 'virt_addr') + dump_entry_pa_offset = ram_dump.field_offset( + 'struct md_region', 'phys_addr') + dump_entry_size_offset = ram_dump.field_offset( + 'struct md_region', 'size') + + dump_entry_size = ram_dump.sizeof('struct md_region') + + mem_dump_data = ram_dump.address_of('minidump_table') + + mem_dump_table = ram_dump.read_word( + mem_dump_data + dump_table_entry_offset) + + mem_dump_smem_table = ram_dump.read_word( + mem_dump_data + dump_smem_table_ptr_offset) + + mem_table_version = ram_dump.read_u32( + mem_dump_smem_table + dump_table_version_offset) + mem_table_num_entry = ram_dump.read_u32( + mem_dump_data + dump_table_num_entry_offset) + + print_out_str('--------') + + for i in range(0, mem_table_num_entry): + this_entry = mem_dump_data + dump_table_entry_offset + \ + i * dump_entry_size + entry_id = ram_dump.read_u32(this_entry + dump_entry_id_offset) + entry_va_addr = ram_dump.read_u64(this_entry + dump_entry_va_offset) + entry_pa_addr = ram_dump.read_u64(this_entry + dump_entry_pa_offset) + entry_size = ram_dump.read_u64(this_entry + dump_entry_size_offset) + + if entry_id < 0 or entry_id > len(self.dump_table_id_lookup_table): + print_out_str( + '!!! Invalid dump table entry id found {0:x}'.format(entry_id)) continue + end_addr = entry_pa_addr + entry_size + minidump_dump_table_value = dict(minidump_dump_table_type) + if entry_pa_addr in ram_dump.ebi_pa_name_map: + section_name = ram_dump.ebi_pa_name_map[entry_pa_addr] + section_name = re.sub("\d+", "", section_name) + if section_name in minidump_dump_table_value.values(): + lst = self.minidump_data_clients( + ram_dump, entry_id,entry_pa_addr,end_addr) + if lst: + client_name, client_id,func,\ + client_entry,client_end = lst[0] + print_out_str('--------') + getattr(DebugImage_v2, func)( + self, 20, client_entry, + client_end, client_id, ram_dump) + if ram_dump.dcc: + self.parse_dcc(ram_dump) + self.qdss.dump_standard(ram_dump) + if not ram_dump.skip_qdss_bin: + self.qdss.save_etf_bin(ram_dump) + self.qdss.save_etr_bin(ram_dump) + if ram_dump.qtf: + self.parse_qtf(ram_dump) - getattr(DebugImage_v2, func)( - self, dump_data_version, dump_data_addr, - dump_data_addr + dump_data_len, client_id, ram_dump) - - self.qdss.dump_standard(ram_dump) - if not ram_dump.skip_qdss_bin: - self.qdss.save_etf_bin(ram_dump) - self.qdss.save_etr_bin(ram_dump) - if ram_dump.qtf: - self.parse_qtf(ram_dump) - if ram_dump.dcc: - self.parse_dcc(ram_dump) diff --git a/linux-ramdump-parser-v2/minidump_util.py b/linux-ramdump-parser-v2/minidump_util.py new file mode 100644 index 0000000..92ef72b --- /dev/null +++ b/linux-ramdump-parser-v2/minidump_util.py @@ -0,0 +1,56 @@ +# Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 and +# only version 2 as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +import sys +import re +import os +from print_out import print_out_str + + +def minidump_virt_to_phys(ebi_files,addr): + pa_addr = None + for a in ebi_files: + idx, pa, end_addr, va,size = a + if addr >= va and addr <= va + size: + offset = addr - va + pa_addr = pa + offset + return pa_addr + return pa_addr + +def read_physical_minidump(ebi_files,ebi_files_ramfile,elffile,addr,length): + ebi = [-1, -1, -1, -1, -1] + for a in ebi_files: + idx, start, end, va, size = a + if addr >= start and addr <= end: + ebi = a + break + if ebi[0] != -1: + idx = ebi[0] + textSec = elffile.get_segment(idx) + off = addr - ebi[1] + elf_content = bytearray(a[4]) + val = textSec.data() + elf_content[0:a[4]] = val + data = elf_content[off:] + return data[:length] + else: + ebi = (-1, -1, -1) + for a in ebi_files_ramfile: + fd, start, end, path = a + if addr >= start and addr <= end: + ebi = a + break + if ebi[0] is -1: + return None + offset = addr - ebi[1] + ebi[0].seek(offset) + a = ebi[0].read(length) + return a diff --git a/linux-ramdump-parser-v2/ramdump.py b/linux-ramdump-parser-v2/ramdump.py index 8d12608..d6814a8 100644 --- a/linux-ramdump-parser-v2/ramdump.py +++ b/linux-ramdump-parser-v2/ramdump.py @@ -27,6 +27,8 @@ import gdbmi from print_out import print_out_str from mmu import Armv7MMU, Armv7LPAEMMU, Armv8MMU import parser_util +import minidump_util +from importlib import import_module FP = 11 SP = 13 @@ -49,7 +51,8 @@ extra_mem_file_names = ['EBI1CS1.BIN', 'DDRCS1.BIN', 'ebi1_cs1.bin', DDR_FILE_NAMES = ['DDRCS0.BIN', 'DDRCS1.BIN', 'DDRCS0_0.BIN', 'DDRCS1_0.BIN', 'DDRCS0_1.BIN', 'DDRCS1_1.BIN'] -OTHER_DUMP_FILE_NAMES = ['PIMEM.BIN', 'OCIMEM.BIN'] +OTHER_DUMP_FILE_NAMES = ['PIMEM.BIN', 'OCIMEM.BIN','md_shared_imem.BIN', + 'md_smem_info.BIN'] RAM_FILE_NAMES = set(DDR_FILE_NAMES + OTHER_DUMP_FILE_NAMES + first_mem_file_names + @@ -499,6 +502,8 @@ class RamDump(): def __init__(self, options, nm_path, gdb_path, objdump_path): self.ebi_files = [] + self.ebi_files_minidump = [] + self.ebi_pa_name_map = {} self.phys_offset = None self.kaslr_offset = options.kaslr_offset self.tz_start = 0 @@ -530,6 +535,20 @@ class RamDump(): self.ipc_log_help = options.ipc_help self.use_stdout = options.stdout self.kernel_version = (0, 0, 0) + self.minidump = options.minidump + self.elffile = None + self.ram_elf_file = None + + if self.minidump: + try: + mod = import_module('elftools.elf.elffile') + ELFFile = mod.ELFFile + StringTableSection = mod.StringTableSection + mod = import_module('elftools.common.py3compat') + bytes2str = mod.bytes2str + except ImportError: + print "Oops, missing required library for minidump. Check README" + sys.exit(1) if options.ram_addr is not None: # TODO sanity check to make sure the memory regions don't overlap @@ -540,11 +559,31 @@ class RamDump(): 'Could not open {0}. Will not be part of dump'.format(file_path)) continue self.ebi_files.append((fd, start, end, file_path)) - else: + elif not options.minidump: if not self.auto_parse(options.autodump): return None - if self.ebi_start == 0: - self.ebi_start = self.ebi_files[0][1] + if options.minidump: + file_path = options.ram_elf_addr + self.ram_elf_file = file_path + fd = open(file_path, 'rb') + self.elffile = ELFFile(fd) + for idx, s in enumerate(self.elffile.iter_segments()): + pa = int(s['p_paddr']) + va = int(s['p_vaddr']) + size = int(s['p_filesz']) + end_addr = pa + size + for section in self.elffile.iter_sections(): + if (not section.is_null() and + s.section_in_segment(section)): + self.ebi_pa_name_map[pa] = section.name + self.ebi_files_minidump.append((idx, pa, end_addr, va,size)) + + if options.minidump: + if self.ebi_start == 0: + self.ebi_start = self.ebi_files_minidump[0][1] + else: + if self.ebi_start == 0: + self.ebi_start = self.ebi_files[0][1] if self.phys_offset is None: self.get_hw_id() @@ -600,10 +639,12 @@ class RamDump(): self.swapper_pg_dir_addr = self.address_of('swapper_pg_dir') if self.swapper_pg_dir_addr is None: print_out_str('!!! Could not get the swapper page directory!') - print_out_str( - '!!! Your vmlinux is probably wrong for these dumps') - print_out_str('!!! Exiting now') - sys.exit(1) + if not self.minidump: + print_out_str( + '!!! Your vmlinux is probably wrong for these dumps') + + print_out_str('!!! Exiting now') + sys.exit(1) stext = self.address_of('stext') if self.kimage_voffset is None: @@ -664,11 +705,12 @@ class RamDump(): '!!! Your vmlinux is probably wrong for these dumps') print_out_str('!!! Exiting now') sys.exit(1) - if not self.get_config(): - print_out_str('!!! Could not get saved configuration') - print_out_str( - '!!! This is really bad and probably indicates RAM corruption') - print_out_str('!!! Some features may be disabled!') + if not self.minidump: + if not self.get_config(): + print_out_str('!!! Could not get saved configuration') + print_out_str( + '!!! This is really bad and probably indicates RAM corruption') + print_out_str('!!! Some features may be disabled!') self.unwind = self.Unwinder(self) @@ -755,14 +797,17 @@ class RamDump(): return s in self.config def kernel_virt_to_phys(self, addr): - va_bits = 39 - if self.kimage_voffset is None: - return addr - self.page_offset + self.phys_offset + if self.minidump: + return minidump_util.minidump_virt_to_phys(self.ebi_files_minidump,addr) else: - if addr & (1 << (va_bits - 1)): + va_bits = 39 + if self.kimage_voffset is None: return addr - self.page_offset + self.phys_offset else: - return addr - (self.kimage_voffset) + if addr & (1 << (va_bits - 1)): + return addr - self.page_offset + self.phys_offset + else: + return addr - (self.kimage_voffset) def get_version(self): banner_addr = self.address_of('linux_banner') @@ -803,10 +848,34 @@ class RamDump(): else: print_out_str('!!! Could not lookup saved command line address') return False + + def print_socinfo_minidump(self): + content_socinfo = None + boards = get_supported_boards() + for board in boards: + if self.hw_id == board.board_num: + content_socinfo = board.ram_start + board.smem_addr_buildinfo + break + sernum_offset = self.field_offset('struct socinfo_v10', 'serial_number') + if sernum_offset is None: + sernum_offset = self.field_offset('struct socinfo_v0_10', 'serial_number') + if sernum_offset is None: + print_out_str("No serial number information available") + return False + if content_socinfo: + addr_of_sernum = content_socinfo + sernum_offset + serial_number = self.read_u32(addr_of_sernum, False) + if serial_number is not None: + print_out_str('Serial number %s' % hex(serial_number)) + return True + return False + + return False def print_socinfo(self): content_socinfo = hex(self.read_pointer('socinfo')) content_socinfo = content_socinfo.strip('L') + sernum_offset = self.field_offset('struct socinfo_v10', 'serial_number') if sernum_offset is None: sernum_offset = self.field_offset('struct socinfo_v0_10', 'serial_number') @@ -819,6 +888,7 @@ class RamDump(): if serial_number is not None: print_out_str('Serial number %s' % hex(serial_number)) return True + return False def auto_parse(self, file_path): @@ -889,41 +959,46 @@ class RamDump(): ebi_path = os.path.abspath(ram[3]) startup_script.write('data.load.binary {0} 0x{1:x}\n'.format( ebi_path, ram[1]).encode('ascii', 'ignore')) - if self.arm64: - startup_script.write('Register.Set NS 1\n'.encode('ascii', 'ignore')) - startup_script.write('Data.Set SPR:0x30201 %Quad 0x{0:x}\n'.format( - self.kernel_virt_to_phys(self.swapper_pg_dir_addr)) - .encode('ascii', 'ignore')) - - if is_cortex_a53: - startup_script.write('Data.Set SPR:0x30202 %Quad 0x00000012B5193519\n'.encode('ascii', 'ignore')) - startup_script.write('Data.Set SPR:0x30A20 %Quad 0x000000FF440C0400\n'.encode('ascii', 'ignore')) - startup_script.write('Data.Set SPR:0x30A30 %Quad 0x0000000000000000\n'.encode('ascii', 'ignore')) - startup_script.write('Data.Set SPR:0x30100 %Quad 0x0000000034D5D91D\n'.encode('ascii', 'ignore')) + if self.minidump: + dload_ram_elf = 'data.load.elf {} /LOGLOAD /nosymbol\n'.format(self.ram_elf_file) + startup_script.write(dload_ram_elf.encode('ascii', 'ignore')) + + if not self.minidump: + if self.arm64: + startup_script.write('Register.Set NS 1\n'.encode('ascii', 'ignore')) + startup_script.write('Data.Set SPR:0x30201 %Quad 0x{0:x}\n'.format( + self.kernel_virt_to_phys(self.swapper_pg_dir_addr)) + .encode('ascii', 'ignore')) + + if is_cortex_a53: + startup_script.write('Data.Set SPR:0x30202 %Quad 0x00000012B5193519\n'.encode('ascii', 'ignore')) + startup_script.write('Data.Set SPR:0x30A20 %Quad 0x000000FF440C0400\n'.encode('ascii', 'ignore')) + startup_script.write('Data.Set SPR:0x30A30 %Quad 0x0000000000000000\n'.encode('ascii', 'ignore')) + startup_script.write('Data.Set SPR:0x30100 %Quad 0x0000000034D5D91D\n'.encode('ascii', 'ignore')) + else: + startup_script.write('Data.Set SPR:0x30202 %Quad 0x00000032B5193519\n'.encode('ascii', 'ignore')) + startup_script.write('Data.Set SPR:0x30A20 %Quad 0x000000FF440C0400\n'.encode('ascii', 'ignore')) + startup_script.write('Data.Set SPR:0x30A30 %Quad 0x0000000000000000\n'.encode('ascii', 'ignore')) + startup_script.write('Data.Set SPR:0x30100 %Quad 0x0000000004C5D93D\n'.encode('ascii', 'ignore')) + + startup_script.write('Register.Set CPSR 0x3C5\n'.encode('ascii', 'ignore')) + startup_script.write('MMU.Delete\n'.encode('ascii', 'ignore')) + startup_script.write('MMU.SCAN PT 0xFFFFFF8000000000--0xFFFFFFFFFFFFFFFF\n'.encode('ascii', 'ignore')) + startup_script.write('mmu.on\n'.encode('ascii', 'ignore')) + startup_script.write('mmu.pt.list 0xffffff8000000000\n'.encode('ascii', 'ignore')) else: - startup_script.write('Data.Set SPR:0x30202 %Quad 0x00000032B5193519\n'.encode('ascii', 'ignore')) - startup_script.write('Data.Set SPR:0x30A20 %Quad 0x000000FF440C0400\n'.encode('ascii', 'ignore')) - startup_script.write('Data.Set SPR:0x30A30 %Quad 0x0000000000000000\n'.encode('ascii', 'ignore')) - startup_script.write('Data.Set SPR:0x30100 %Quad 0x0000000004C5D93D\n'.encode('ascii', 'ignore')) - - startup_script.write('Register.Set CPSR 0x3C5\n'.encode('ascii', 'ignore')) - startup_script.write('MMU.Delete\n'.encode('ascii', 'ignore')) - startup_script.write('MMU.SCAN PT 0xFFFFFF8000000000--0xFFFFFFFFFFFFFFFF\n'.encode('ascii', 'ignore')) - startup_script.write('mmu.on\n'.encode('ascii', 'ignore')) - startup_script.write('mmu.pt.list 0xffffff8000000000\n'.encode('ascii', 'ignore')) - else: - startup_script.write( - 'PER.S.F C15:0x2 %L 0x{0:x}\n'.format(self.mmu.ttbr).encode('ascii', 'ignore')) - if isinstance(self.mmu, Armv7LPAEMMU): - # TTBR1. This gets setup once and never change again even if TTBR0 - # changes - startup_script.write('PER.S.F C15:0x102 %L 0x{0:x}\n'.format( - self.mmu.ttbr + 0x4000).encode('ascii', 'ignore')) - # TTBCR with EAE and T1SZ set approprately startup_script.write( - 'PER.S.F C15:0x202 %L 0x80030000\n'.encode('ascii', 'ignore')) - startup_script.write('mmu.on\n'.encode('ascii', 'ignore')) - startup_script.write('mmu.scan\n'.encode('ascii', 'ignore')) + 'PER.S.F C15:0x2 %L 0x{0:x}\n'.format(self.mmu.ttbr).encode('ascii', 'ignore')) + if isinstance(self.mmu, Armv7LPAEMMU): + # TTBR1. This gets setup once and never change again even if TTBR0 + # changes + startup_script.write('PER.S.F C15:0x102 %L 0x{0:x}\n'.format( + self.mmu.ttbr + 0x4000).encode('ascii', 'ignore')) + # TTBCR with EAE and T1SZ set approprately + startup_script.write( + 'PER.S.F C15:0x202 %L 0x80030000\n'.encode('ascii', 'ignore')) + startup_script.write('mmu.on\n'.encode('ascii', 'ignore')) + startup_script.write('mmu.scan\n'.encode('ascii', 'ignore')) where = os.path.abspath(self.vmlinux) if self.kaslr_offset is not None: @@ -953,8 +1028,8 @@ class RamDump(): 'task.config /opt/t32/demo/arm/kernel/linux/linux.t32\n'.encode('ascii', 'ignore')) startup_script.write( 'menu.reprogram /opt/t32/demo/arm/kernel/linux/linux.men\n'.encode('ascii', 'ignore')) - - startup_script.write('task.dtask\n'.encode('ascii', 'ignore')) + if not self.minidump: + startup_script.write('task.dtask\n'.encode('ascii', 'ignore')) startup_script.write( 'v.v %ASCII %STRING linux_banner\n'.encode('ascii', 'ignore')) if os.path.exists(out_path + '/regs_panic.cmm'): @@ -1024,30 +1099,38 @@ class RamDump(): boards = get_supported_boards() if (self.hw_id is None): - heap_toc_offset = self.field_offset('struct smem_shared', 'heap_toc') - if heap_toc_offset is None: - print_out_str( - '!!!! Could not get a necessary offset for auto detection!') - print_out_str( - '!!!! Please check the gdb path which is used for offsets!') - print_out_str('!!!! Also check that the vmlinux is not stripped') - print_out_str('!!!! Exiting...') - sys.exit(1) + if not self.minidump: + heap_toc_offset = self.field_offset('struct smem_shared', 'heap_toc') + if heap_toc_offset is None: + print_out_str( + '!!!! Could not get a necessary offset for auto detection!') + print_out_str( + '!!!! Please check the gdb path which is used for offsets!') + print_out_str('!!!! Also check that the vmlinux is not stripped') + print_out_str('!!!! Exiting...') + sys.exit(1) - smem_heap_entry_size = self.sizeof('struct smem_heap_entry') - offset_offset = self.field_offset('struct smem_heap_entry', 'offset') + smem_heap_entry_size = self.sizeof('struct smem_heap_entry') + offset_offset = self.field_offset('struct smem_heap_entry', 'offset') for board in boards: - socinfo_start_addr = board.smem_addr + heap_toc_offset + smem_heap_entry_size * SMEM_HW_SW_BUILD_ID + offset_offset + if not self.minidump: + socinfo_start_addr = board.smem_addr + heap_toc_offset + smem_heap_entry_size * SMEM_HW_SW_BUILD_ID + offset_offset + else: + if hasattr(board, 'smem_addr_buildinfo'): + socinfo_start_addr = board.smem_addr_buildinfo + else: + continue if add_offset: socinfo_start_addr += board.ram_start - soc_start = self.read_int(socinfo_start_addr, False) - if soc_start is None: - continue - - socinfo_start = board.smem_addr + soc_start - if add_offset: - socinfo_start += board.ram_start - + if not self.minidump: + soc_start = self.read_int(socinfo_start_addr, False) + if soc_start is None: + continue + socinfo_start = board.smem_addr + soc_start + if add_offset: + socinfo_start += board.ram_start + else: + socinfo_start = socinfo_start_addr socinfo_id = self.read_int(socinfo_start + 4, False) if socinfo_id != board.socid: continue @@ -1117,7 +1200,10 @@ class RamDump(): def virt_to_phys(self, virt_or_name): """Does a virtual-to-physical address lookup of the virtual address or variable name.""" - return self.mmu.virt_to_phys(self.resolve_virt(virt_or_name)) + if self.minidump: + return minidump_util.minidump_virt_to_phys(self.ebi_files_minidump,self.resolve_virt(virt_or_name)) + else: + return self.mmu.virt_to_phys(self.resolve_virt(virt_or_name)) def setup_symbol_tables(self): stream = os.popen(self.nm_path + ' -n ' + self.vmlinux) @@ -1249,18 +1335,24 @@ class RamDump(): return (self.lookup_table[mid][1], self.lookup_table[mid + 1][0] - self.lookup_table[mid][0]) def read_physical(self, addr, length): - ebi = (-1, -1, -1) - for a in self.ebi_files: - fd, start, end, path = a - if addr >= start and addr <= end: - ebi = a - break - if ebi[0] is -1: - return None - offset = addr - ebi[1] - ebi[0].seek(offset) - a = ebi[0].read(length) - return a + if self.minidump: + addr_data = minidump_util.read_physical_minidump( + self.ebi_files_minidump, self.ebi_files,self.elffile, + addr, length) + return addr_data + else: + ebi = (-1, -1, -1) + for a in self.ebi_files: + fd, start, end, path = a + if addr >= start and addr <= end: + ebi = a + break + if ebi[0] is -1: + return None + offset = addr - ebi[1] + ebi[0].seek(offset) + a = ebi[0].read(length) + return a def read_dword(self, addr_or_name, virtual=True, cpu=None): s = self.read_string(addr_or_name, '<Q', virtual, cpu) diff --git a/linux-ramdump-parser-v2/ramparse.py b/linux-ramdump-parser-v2/ramparse.py index 7245e9b..89a6257 100644 --- a/linux-ramdump-parser-v2/ramparse.py +++ b/linux-ramdump-parser-v2/ramparse.py @@ -152,6 +152,10 @@ if __name__ == '__main__': parser.add_option('', '--eval', help='Evaluate some python code directly, or from stdin if "-" is passed. The "dump" variable will be available, as it is with the --shell option.') # noqa parser.add_option('', '--wlan', dest='wlan', help='wlan.ko path') + parser.add_option('', '--minidump', action='store_true', dest='minidump', + help='Parse minidump') + parser.add_option('', '--ram-elf', dest='ram_elf_addr', + help='pass ap_minidump.elf generated by crashscope') for p in parser_util.get_parsers(): parser.add_option(p.shortopt or '', @@ -161,6 +165,11 @@ if __name__ == '__main__': action='store_true') (options, args) = parser.parse_args() + if options.minidump: + default_list = [] + default_list.append("Dmesg") + default_list.append("RTB") + default_list.append("DebugImage") if options.outdir: if not os.path.exists(options.outdir): @@ -224,7 +233,7 @@ if __name__ == '__main__': print_out_str('using vmlinux file {0}'.format(options.vmlinux)) - if options.ram_addr is None and options.autodump is None: + if options.ram_addr is None and options.autodump is None and not options.minidump: print_out_str('Need one of --auto-dump or at least one --ram-file') sys.exit(1) @@ -343,14 +352,20 @@ if __name__ == '__main__': shell.interact() sys.exit(0) - if not dump.print_command_line(): - print_out_str('!!! Error printing saved command line.') - print_out_str('!!! The vmlinux is probably wrong for the ramdumps') - print_out_str('!!! Exiting now...') - sys.exit(1) + if not options.minidump: + if not dump.print_command_line(): + print_out_str('!!! Error printing saved command line.') + print_out_str('!!! The vmlinux is probably wrong for the ramdumps') + print_out_str('!!! Exiting now...') + sys.exit(1) - if not dump.print_socinfo(): - print_out_str('!!! No serial number information available.') + if options.minidump: + if not dump.print_socinfo_minidump(): + print_out_str('!!! No serial number information ' + 'available for this minidump.') + else: + if not dump.print_socinfo(): + print_out_str('!!! No serial number information available.') if options.qdss: print_out_str('!!! --parse-qdss is now deprecated') @@ -365,12 +380,24 @@ if __name__ == '__main__': # we called parser.add_option with dest=p.cls.__name__ above, # so if the user passed that option then `options' will have a # p.cls.__name__ attribute. - parsers_to_run = [p for p in parser_util.get_parsers() + if options.minidump: + parsers_to_run = [p for p in parser_util.get_parsers() + if getattr(options, p.cls.__name__) + or (options.everything and p.cls.__name__ in default_list)] + else: + parsers_to_run = [p for p in parser_util.get_parsers() if getattr(options, p.cls.__name__) or (options.everything and not p.optional)] for i,p in enumerate(parsers_to_run): if i == 0: sys.stderr.write("\n") + if options.minidump: + if p.cls.__name__ not in default_list: + sys.stderr.write(" [%d/%d] %s ... not supported in minidump \n" % + (i + 1, len(parsers_to_run), p.longopt)) + continue + + sys.stderr.write(" [%d/%d] %s ... " % (i + 1, len(parsers_to_run), p.longopt)) before = time.time() @@ -387,5 +414,6 @@ if __name__ == '__main__': sys.stderr.write("\n") - if options.t32launcher or options.everything: + if options.t32launcher or options.everything or options.minidump: dump.create_t32_launcher() + -- GitLab