]> git.proxmox.com Git - mirror_qemu.git/blobdiff - scripts/dump-guest-memory.py
Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20190613-1' into...
[mirror_qemu.git] / scripts / dump-guest-memory.py
index c0a2e99f465db607fa18b0b94348249f579ad237..2c587cbefc5702544b78ffea196d9dcdd984085d 100644 (file)
@@ -12,10 +12,17 @@ Authors:
 This work is licensed under the terms of the GNU GPL, version 2 or later. See
 the COPYING file in the top-level directory.
 """
+from __future__ import print_function
 
 import ctypes
+import struct
 
-UINTPTR_T = gdb.lookup_type("uintptr_t")
+try:
+    UINTPTR_T = gdb.lookup_type("uintptr_t")
+except Exception as inst:
+    raise gdb.GdbError("Symbols must be loaded prior to sourcing dump-guest-memory.\n"
+                       "Symbols may be loaded by 'attach'ing a QEMU process id or by "
+                       "'load'ing a QEMU binary.")
 
 TARGET_PAGE_SIZE = 0x1000
 TARGET_PAGE_MASK = 0xFFFFFFFFFFFFF000
@@ -45,6 +52,17 @@ EM_S390 = 22
 EM_AARCH = 183
 EM_X86_64 = 62
 
+VMCOREINFO_FORMAT_ELF = 1
+
+def le16_to_cpu(val):
+    return struct.unpack("<H", struct.pack("=H", val))[0]
+
+def le32_to_cpu(val):
+    return struct.unpack("<I", struct.pack("=I", val))[0]
+
+def le64_to_cpu(val):
+    return struct.unpack("<Q", struct.pack("=Q", val))[0]
+
 class ELF(object):
     """Representation of a ELF file."""
 
@@ -53,44 +71,44 @@ class ELF(object):
         self.notes = []
         self.segments = []
         self.notes_size = 0
-        self.endianess = None
+        self.endianness = None
         self.elfclass = ELFCLASS64
 
         if arch == 'aarch64-le':
-            self.endianess = ELFDATA2LSB
+            self.endianness = ELFDATA2LSB
             self.elfclass = ELFCLASS64
-            self.ehdr = get_arch_ehdr(self.endianess, self.elfclass)
+            self.ehdr = get_arch_ehdr(self.endianness, self.elfclass)
             self.ehdr.e_machine = EM_AARCH
 
         elif arch == 'aarch64-be':
-            self.endianess = ELFDATA2MSB
-            self.ehdr = get_arch_ehdr(self.endianess, self.elfclass)
+            self.endianness = ELFDATA2MSB
+            self.ehdr = get_arch_ehdr(self.endianness, self.elfclass)
             self.ehdr.e_machine = EM_AARCH
 
         elif arch == 'X86_64':
-            self.endianess = ELFDATA2LSB
-            self.ehdr = get_arch_ehdr(self.endianess, self.elfclass)
+            self.endianness = ELFDATA2LSB
+            self.ehdr = get_arch_ehdr(self.endianness, self.elfclass)
             self.ehdr.e_machine = EM_X86_64
 
         elif arch == '386':
-            self.endianess = ELFDATA2LSB
+            self.endianness = ELFDATA2LSB
             self.elfclass = ELFCLASS32
-            self.ehdr = get_arch_ehdr(self.endianess, self.elfclass)
+            self.ehdr = get_arch_ehdr(self.endianness, self.elfclass)
             self.ehdr.e_machine = EM_386
 
         elif arch == 's390':
-            self.endianess = ELFDATA2MSB
-            self.ehdr = get_arch_ehdr(self.endianess, self.elfclass)
+            self.endianness = ELFDATA2MSB
+            self.ehdr = get_arch_ehdr(self.endianness, self.elfclass)
             self.ehdr.e_machine = EM_S390
 
         elif arch == 'ppc64-le':
-            self.endianess = ELFDATA2LSB
-            self.ehdr = get_arch_ehdr(self.endianess, self.elfclass)
+            self.endianness = ELFDATA2LSB
+            self.ehdr = get_arch_ehdr(self.endianness, self.elfclass)
             self.ehdr.e_machine = EM_PPC64
 
         elif arch == 'ppc64-be':
-            self.endianess = ELFDATA2MSB
-            self.ehdr = get_arch_ehdr(self.endianess, self.elfclass)
+            self.endianness = ELFDATA2MSB
+            self.ehdr = get_arch_ehdr(self.endianness, self.elfclass)
             self.ehdr.e_machine = EM_PPC64
 
         else:
@@ -104,7 +122,7 @@ class ELF(object):
     def add_note(self, n_name, n_desc, n_type):
         """Adds a note to the ELF."""
 
-        note = get_arch_note(self.endianess, len(n_name), len(n_desc))
+        note = get_arch_note(self.endianness, len(n_name), len(n_desc))
         note.n_namesz = len(n_name) + 1
         note.n_descsz = len(n_desc)
         note.n_name = n_name.encode()
@@ -120,12 +138,32 @@ class ELF(object):
         self.segments[0].p_filesz += ctypes.sizeof(note)
         self.segments[0].p_memsz += ctypes.sizeof(note)
 
+
+    def add_vmcoreinfo_note(self, vmcoreinfo):
+        """Adds a vmcoreinfo note to the ELF dump."""
+        # compute the header size, and copy that many bytes from the note
+        header = get_arch_note(self.endianness, 0, 0)
+        ctypes.memmove(ctypes.pointer(header),
+                       vmcoreinfo, ctypes.sizeof(header))
+        if header.n_descsz > 1 << 20:
+            print('warning: invalid vmcoreinfo size')
+            return
+        # now get the full note
+        note = get_arch_note(self.endianness,
+                             header.n_namesz - 1, header.n_descsz)
+        ctypes.memmove(ctypes.pointer(note), vmcoreinfo, ctypes.sizeof(note))
+
+        self.notes.append(note)
+        self.segments[0].p_filesz += ctypes.sizeof(note)
+        self.segments[0].p_memsz += ctypes.sizeof(note)
+
     def add_segment(self, p_type, p_paddr, p_size):
         """Adds a segment to the elf."""
 
-        phdr = get_arch_phdr(self.endianess, self.elfclass)
+        phdr = get_arch_phdr(self.endianness, self.elfclass)
         phdr.p_type = p_type
         phdr.p_paddr = p_paddr
+        phdr.p_vaddr = p_paddr
         phdr.p_filesz = p_size
         phdr.p_memsz = p_size
         self.segments.append(phdr)
@@ -155,10 +193,10 @@ class ELF(object):
             elf_file.write(note)
 
 
-def get_arch_note(endianess, len_name, len_desc):
-    """Returns a Note class with the specified endianess."""
+def get_arch_note(endianness, len_name, len_desc):
+    """Returns a Note class with the specified endianness."""
 
-    if endianess == ELFDATA2LSB:
+    if endianness == ELFDATA2LSB:
         superclass = ctypes.LittleEndianStructure
     else:
         superclass = ctypes.BigEndianStructure
@@ -190,20 +228,20 @@ class Ident(ctypes.Structure):
                 ('ei_abiversion', ctypes.c_ubyte),
                 ('ei_pad', ctypes.c_ubyte * 7)]
 
-    def __init__(self, endianess, elfclass):
+    def __init__(self, endianness, elfclass):
         self.ei_mag0 = 0x7F
         self.ei_mag1 = ord('E')
         self.ei_mag2 = ord('L')
         self.ei_mag3 = ord('F')
         self.ei_class = elfclass
-        self.ei_data = endianess
+        self.ei_data = endianness
         self.ei_version = EV_CURRENT
 
 
-def get_arch_ehdr(endianess, elfclass):
-    """Returns a EHDR64 class with the specified endianess."""
+def get_arch_ehdr(endianness, elfclass):
+    """Returns a EHDR64 class with the specified endianness."""
 
-    if endianess == ELFDATA2LSB:
+    if endianness == ELFDATA2LSB:
         superclass = ctypes.LittleEndianStructure
     else:
         superclass = ctypes.BigEndianStructure
@@ -228,12 +266,12 @@ def get_arch_ehdr(endianess, elfclass):
 
         def __init__(self):
             super(superclass, self).__init__()
-            self.e_ident = Ident(endianess, elfclass)
+            self.e_ident = Ident(endianness, elfclass)
             self.e_type = ET_CORE
             self.e_version = EV_CURRENT
             self.e_ehsize = ctypes.sizeof(self)
             self.e_phoff = ctypes.sizeof(self)
-            self.e_phentsize = ctypes.sizeof(get_arch_phdr(endianess, elfclass))
+            self.e_phentsize = ctypes.sizeof(get_arch_phdr(endianness, elfclass))
             self.e_phnum = 0
 
 
@@ -257,12 +295,12 @@ def get_arch_ehdr(endianess, elfclass):
 
         def __init__(self):
             super(superclass, self).__init__()
-            self.e_ident = Ident(endianess, elfclass)
+            self.e_ident = Ident(endianness, elfclass)
             self.e_type = ET_CORE
             self.e_version = EV_CURRENT
             self.e_ehsize = ctypes.sizeof(self)
             self.e_phoff = ctypes.sizeof(self)
-            self.e_phentsize = ctypes.sizeof(get_arch_phdr(endianess, elfclass))
+            self.e_phentsize = ctypes.sizeof(get_arch_phdr(endianness, elfclass))
             self.e_phnum = 0
 
     # End get_arch_ehdr
@@ -272,10 +310,10 @@ def get_arch_ehdr(endianess, elfclass):
         return EHDR32()
 
 
-def get_arch_phdr(endianess, elfclass):
-    """Returns a 32 or 64 bit PHDR class with the specified endianess."""
+def get_arch_phdr(endianness, elfclass):
+    """Returns a 32 or 64 bit PHDR class with the specified endianness."""
 
-    if endianess == ELFDATA2LSB:
+    if endianness == ELFDATA2LSB:
         superclass = ctypes.LittleEndianStructure
     else:
         superclass = ctypes.BigEndianStructure
@@ -314,8 +352,18 @@ def get_arch_phdr(endianess, elfclass):
 def int128_get64(val):
     """Returns low 64bit part of Int128 struct."""
 
-    assert val["hi"] == 0
-    return val["lo"]
+    try:
+        assert val["hi"] == 0
+        return val["lo"]
+    except gdb.error:
+        u64t = gdb.lookup_type('uint64_t').array(2)
+        u64 = val.cast(u64t)
+        if sys.byteorder == 'little':
+            assert u64[1] == 0
+            return u64[0]
+        else:
+            assert u64[0] == 0
+            return u64[1]
 
 
 def qlist_foreach(head, field_str):
@@ -328,23 +376,10 @@ def qlist_foreach(head, field_str):
         yield var
 
 
-def qemu_get_ram_block(ram_addr):
-    """Returns the RAMBlock struct to which the given address belongs."""
-
-    ram_blocks = gdb.parse_and_eval("ram_list.blocks")
-
-    for block in qlist_foreach(ram_blocks, "next"):
-        if (ram_addr - block["offset"]) < block["used_length"]:
-            return block
-
-    raise gdb.GdbError("Bad ram offset %x" % ram_addr)
-
-
-def qemu_get_ram_ptr(ram_addr):
+def qemu_map_ram_ptr(block, offset):
     """Returns qemu vaddr for given guest physical address."""
 
-    block = qemu_get_ram_block(ram_addr)
-    return block["host"] + (ram_addr - block["offset"])
+    return block["host"] + offset
 
 
 def memory_region_get_ram_ptr(memory_region):
@@ -352,7 +387,7 @@ def memory_region_get_ram_ptr(memory_region):
         return (memory_region_get_ram_ptr(memory_region["alias"].dereference())
                 + memory_region["alias_offset"])
 
-    return qemu_get_ram_ptr(memory_region["ram_block"]["offset"])
+    return qemu_map_ram_ptr(memory_region["ram_block"], 0)
 
 
 def get_guest_phys_blocks():
@@ -383,7 +418,9 @@ def get_guest_phys_blocks():
         memory_region = flat_range["mr"].dereference()
 
         # we only care about RAM
-        if not memory_region["ram"]:
+        if (not memory_region["ram"] or
+            memory_region["ram_device"] or
+            memory_region["nonvolatile"]):
             continue
 
         section_size = int128_get64(flat_range["addr"]["size"])
@@ -508,6 +545,38 @@ shape and this command should mostly work."""
                 cur += chunk_size
                 left -= chunk_size
 
+    def phys_memory_read(self, addr, size):
+        qemu_core = gdb.inferiors()[0]
+        for block in self.guest_phys_blocks:
+            if block["target_start"] <= addr \
+               and addr + size <= block["target_end"]:
+                haddr = block["host_addr"] + (addr - block["target_start"])
+                return qemu_core.read_memory(haddr, size)
+        return None
+
+    def add_vmcoreinfo(self):
+        if gdb.lookup_symbol("vmcoreinfo_realize")[0] is None:
+            return
+        vmci = 'vmcoreinfo_realize::vmcoreinfo_state'
+        if not gdb.parse_and_eval("%s" % vmci) \
+           or not gdb.parse_and_eval("(%s)->has_vmcoreinfo" % vmci):
+            return
+
+        fmt = gdb.parse_and_eval("(%s)->vmcoreinfo.guest_format" % vmci)
+        addr = gdb.parse_and_eval("(%s)->vmcoreinfo.paddr" % vmci)
+        size = gdb.parse_and_eval("(%s)->vmcoreinfo.size" % vmci)
+
+        fmt = le16_to_cpu(fmt)
+        addr = le64_to_cpu(addr)
+        size = le32_to_cpu(size)
+
+        if fmt != VMCOREINFO_FORMAT_ELF:
+            return
+
+        vmcoreinfo = self.phys_memory_read(addr, size)
+        if vmcoreinfo:
+            self.elf.add_vmcoreinfo_note(bytes(vmcoreinfo))
+
     def invoke(self, args, from_tty):
         """Handles command invocation from gdb."""
 
@@ -521,6 +590,7 @@ shape and this command should mostly work."""
 
         self.elf = ELF(argv[1])
         self.guest_phys_blocks = get_guest_phys_blocks()
+        self.add_vmcoreinfo()
 
         with open(argv[0], "wb") as vmcore:
             self.dump_init(vmcore)