From d943487441c33d9bb2fa91f0ded7d190eced993d Mon Sep 17 00:00:00 2001
From: Patrick Daly <pdaly@codeaurora.org>
Date: Thu, 11 Jan 2018 19:25:35 -0800
Subject: [PATCH] lrdp: debug_image: Support guest VM cpu register dump tables

The hypervisor may populate an imem cookie with the location of
a standard dump table, instead of using the table allocated by linux.
Read the table location from this cookie instead of relying on the
linux variable 'memdump'.
On error, fall back to the old approach of reading from 'memdump'.

Change-Id: Id99bb643fb7f35de79ff7308a999cc2143d7aff0
---
 linux-ramdump-parser-v2/debug_image_v2.py | 77 +++++++++++++++++------
 linux-ramdump-parser-v2/ramdump.py        | 18 +++---
 2 files changed, 67 insertions(+), 28 deletions(-)

diff --git a/linux-ramdump-parser-v2/debug_image_v2.py b/linux-ramdump-parser-v2/debug_image_v2.py
index ca588ac..7494ef8 100644
--- a/linux-ramdump-parser-v2/debug_image_v2.py
+++ b/linux-ramdump-parser-v2/debug_image_v2.py
@@ -35,6 +35,7 @@ from fcmdump import FCM_Dump
 
 MEMDUMPV2_MAGIC = 0x42445953
 MAX_NUM_ENTRIES = 0x150
+IMEM_OFFSET_MEM_DUMP_TABLE = 0x3f010
 
 class client(object):
     MSM_DUMP_DATA_CPU_CTX = 0x00
@@ -598,6 +599,40 @@ class DebugImage_v2():
         results.append((client_name, client_id,client_table[client_name], entry_pa_addr,end_addr))
         return results
 
+    class MsmDumpTable(object):
+        def __init__(self):
+            self.name = "Anon"
+            self.phys_addr = 0x0
+            self.version = 0x0
+            self.num_entries = 0x0
+
+    """ Create an instance of MsmDumpTable, or None on error """
+    def validateMsmDumpTable(self, ram_dump, name, table_phys):
+        if table_phys is None:
+            print_out_str('debug_image.py: Table {}: Unable to read dump table base address'.format(name))
+            return None
+
+        version = ram_dump.read_structure_field(
+                    table_phys, 'struct msm_dump_table', 'version',
+                    virtual = False)
+        if version is None:
+            print_out_str('Table {}: Version is bogus! Can\'t parse debug image'.format(name))
+            return None
+
+        num_entries = ram_dump.read_structure_field(
+                        table_phys, 'struct msm_dump_table', 'num_entries',
+                        virtual = False)
+        if num_entries is None or num_entries > 100:
+            print_out_str('Table {}: num_entries is bogus! Can\'t parse debug image'.format(name))
+            return None
+
+        table = self.MsmDumpTable()
+        table.name = name
+        table.phys_addr = table_phys
+        table.version = version
+        table.num_entries = num_entries
+        return table
+
     def parse_dump_v2(self, ram_dump):
         self.dump_type_lookup_table = ram_dump.gdbmi.get_enum_lookup_table(
             'msm_dump_type', 2)
@@ -674,32 +709,34 @@ class DebugImage_v2():
             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')
+            """
+            Some multi-guest hypervisor systems override the imem location
+            with a table for a crashed guest. So the value from IMEM may
+            not match the value saved in the linux variable 'memdump'.
+            """
+            table_phys = ram_dump.read_word(
+                ram_dump.board.imem_start + IMEM_OFFSET_MEM_DUMP_TABLE,
+                virtual = False)
+            root_table = self.validateMsmDumpTable(ram_dump, "IMEM", table_phys)
+
+            if root_table is None:
+                table_phys = ram_dump.read_structure_field(
+                    'memdump', 'struct msm_memory_dump', 'table_phys')
+                root_table = self.validateMsmDumpTable(ram_dump, "RAM", table_phys)
+
+            if root_table is None:
                 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))
+                root_table.version >> 20, root_table.version & 0xFFFFF, root_table.num_entries))
             print_out_str('--------')
 
-            for i in range(0, mem_table_num_entry):
-                this_entry = mem_dump_table + dump_table_entry_offset + \
+            for i in range(0, root_table.num_entries):
+                this_entry = root_table.phys_addr + 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)
+                entry_id = ram_dump.read_u32(this_entry + dump_entry_id_offset, virtual = False)
+                entry_type = ram_dump.read_u32(this_entry + dump_entry_type_offset, virtual = False)
+                entry_addr = ram_dump.read_word(this_entry + dump_entry_addr_offset, virtual = False)
 
                 if entry_id < 0 or entry_id > len(self.dump_table_id_lookup_table):
                     print_out_str(
diff --git a/linux-ramdump-parser-v2/ramdump.py b/linux-ramdump-parser-v2/ramdump.py
index 9fbabf7..1072435 100644
--- a/linux-ramdump-parser-v2/ramdump.py
+++ b/linux-ramdump-parser-v2/ramdump.py
@@ -51,7 +51,9 @@ extra_mem_file_names = ['EBI1CS1.BIN', 'DDRCS1.BIN', 'ebi1_cs1.bin',
                         'DDRCS1_5.BIN']
 
 DDR_FILE_NAMES = ['DDRCS0.BIN', 'DDRCS1.BIN', 'DDRCS0_0.BIN',
-                  'DDRCS1_0.BIN', 'DDRCS0_1.BIN', 'DDRCS1_1.BIN']
+                  'DDRCS1_0.BIN', 'DDRCS0_1.BIN', 'DDRCS1_1.BIN',
+                  'DDR_0.BIN', 'DDR_1.BIN', 'DDR_2.BIN', 'DDR_3.BIN',
+                  'RESET_INFO.BIN']
 OTHER_DUMP_FILE_NAMES = ['PIMEM.BIN', 'OCIMEM.BIN','md_shared_imem.BIN',
                          'md_smem_info.BIN']
 RAM_FILE_NAMES = set(DDR_FILE_NAMES +
@@ -1213,6 +1215,7 @@ class RamDump():
             self.kaslr_addr = board.kaslr_addr
         else:
             self.kaslr_addr = None
+        self.board = board
         return True
 
     def resolve_virt(self, virt_or_name):
@@ -1491,19 +1494,18 @@ class RamDump():
         fn = self.read_u32 if self.sizeof('void *') == 4 else self.read_u64
         return fn(addr_or_name, virtual, cpu)
 
-    def read_structure_field(self, addr_or_name, struct_name, field):
+    def read_structure_field(self, addr_or_name, struct_name, field, virtual=True):
         """reads a 4 or 8 byte field from a structure"""
         size = self.sizeof("(({0} *)0)->{1}".format(struct_name, field))
-        virt = self.resolve_virt(addr_or_name)
-        if virt is None or size is None:
+        addr = self.resolve_virt(addr_or_name)
+        if addr is None or size is None:
             return None
 
+        addr += self.field_offset(struct_name, field)
         if size == 4:
-            return self.read_u32(virt + self.field_offset(struct_name,
-                                                                  field))
+            return self.read_u32(addr, virtual)
         if size == 8:
-            return self.read_u64(virt + self.field_offset(struct_name,
-                                                                  field))
+            return self.read_u64(addr, virtual)
         return None
 
     def read_structure_cstring(self, addr_or_name, struct_name, field,
-- 
GitLab