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.')