Skip to content
Snippets Groups Projects
Commit 045b5c4c authored by Laura Abbott's avatar Laura Abbott
Browse files

linux-ramdump-parser-v2: Add basic support for 64-bit

Ram dumps are now being generated for 64-bit ramdumps. Update the
parser to account for this.

Change-Id: I54a354f39701ad96dd7b81010ec6c5989dd471ad
parent a3954277
Branches
No related tags found
No related merge requests found
......@@ -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.
......@@ -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()
......@@ -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,6 +69,9 @@ class RamDump():
end = ramdump.addr_lookup('__stop_unwind_idx')
self.ramdump = ramdump
if (start is None) or (end is None):
if ramdump.arm64:
self.unwind_frame = self.unwind_frame_generic64
else:
self.unwind_frame = self.unwind_frame_generic
return None
# addresses
......@@ -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,20 +936,26 @@ 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))
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))
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
......@@ -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'))
......@@ -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,6 +152,10 @@ if __name__ == '__main__':
try:
import local_settings
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:
......@@ -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.')
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment