xref: /qemu/scripts/dump-guest-memory.py (revision 368e3adc)
13e16d14fSLaszlo Ersek# This python script adds a new gdb command, "dump-guest-memory". It
23e16d14fSLaszlo Ersek# should be loaded with "source dump-guest-memory.py" at the (gdb)
33e16d14fSLaszlo Ersek# prompt.
43e16d14fSLaszlo Ersek#
53e16d14fSLaszlo Ersek# Copyright (C) 2013, Red Hat, Inc.
63e16d14fSLaszlo Ersek#
73e16d14fSLaszlo Ersek# Authors:
83e16d14fSLaszlo Ersek#   Laszlo Ersek <lersek@redhat.com>
9*368e3adcSJanosch Frank#   Janosch Frank <frankja@linux.vnet.ibm.com>
103e16d14fSLaszlo Ersek#
113e16d14fSLaszlo Ersek# This work is licensed under the terms of the GNU GPL, version 2 or later. See
123e16d14fSLaszlo Ersek# the COPYING file in the top-level directory.
133e16d14fSLaszlo Ersek#
143e16d14fSLaszlo Ersek# The leading docstring doesn't have idiomatic Python formatting. It is
153e16d14fSLaszlo Ersek# printed by gdb's "help" command (the first line is printed in the
163e16d14fSLaszlo Ersek# "help data" summary), and it should match how other help texts look in
173e16d14fSLaszlo Ersek# gdb.
183e16d14fSLaszlo Ersek
19*368e3adcSJanosch Frankimport ctypes
203e16d14fSLaszlo Ersek
2147890203SJanosch FrankUINTPTR_T = gdb.lookup_type("uintptr_t")
2247890203SJanosch Frank
233e16d14fSLaszlo ErsekTARGET_PAGE_SIZE = 0x1000
243e16d14fSLaszlo ErsekTARGET_PAGE_MASK = 0xFFFFFFFFFFFFF000
253e16d14fSLaszlo Ersek
263e16d14fSLaszlo Ersek# Special value for e_phnum. This indicates that the real number of
273e16d14fSLaszlo Ersek# program headers is too large to fit into e_phnum. Instead the real
283e16d14fSLaszlo Ersek# value is in the field sh_info of section 0.
293e16d14fSLaszlo ErsekPN_XNUM = 0xFFFF
303e16d14fSLaszlo Ersek
31*368e3adcSJanosch FrankEV_CURRENT = 1
32*368e3adcSJanosch Frank
33*368e3adcSJanosch FrankELFCLASS32 = 1
34*368e3adcSJanosch FrankELFCLASS64 = 2
35*368e3adcSJanosch Frank
36*368e3adcSJanosch FrankELFDATA2LSB = 1
37*368e3adcSJanosch FrankELFDATA2MSB = 2
38*368e3adcSJanosch Frank
39*368e3adcSJanosch FrankET_CORE = 4
40*368e3adcSJanosch Frank
41*368e3adcSJanosch FrankPT_LOAD = 1
42*368e3adcSJanosch FrankPT_NOTE = 4
43*368e3adcSJanosch Frank
44*368e3adcSJanosch FrankEM_386 = 3
45*368e3adcSJanosch FrankEM_PPC = 20
46*368e3adcSJanosch FrankEM_PPC64 = 21
47*368e3adcSJanosch FrankEM_S390 = 22
48*368e3adcSJanosch FrankEM_AARCH = 183
49*368e3adcSJanosch FrankEM_X86_64 = 62
50*368e3adcSJanosch Frank
51*368e3adcSJanosch Frankclass ELF(object):
52*368e3adcSJanosch Frank    """Representation of a ELF file."""
53*368e3adcSJanosch Frank
54*368e3adcSJanosch Frank    def __init__(self, arch):
55*368e3adcSJanosch Frank        self.ehdr = None
56*368e3adcSJanosch Frank        self.notes = []
57*368e3adcSJanosch Frank        self.segments = []
58*368e3adcSJanosch Frank        self.notes_size = 0
59*368e3adcSJanosch Frank        self.endianess = None
60*368e3adcSJanosch Frank        self.elfclass = ELFCLASS64
61*368e3adcSJanosch Frank
62*368e3adcSJanosch Frank        if arch == 'aarch64-le':
63*368e3adcSJanosch Frank            self.endianess = ELFDATA2LSB
64*368e3adcSJanosch Frank            self.elfclass = ELFCLASS64
65*368e3adcSJanosch Frank            self.ehdr = get_arch_ehdr(self.endianess, self.elfclass)
66*368e3adcSJanosch Frank            self.ehdr.e_machine = EM_AARCH
67*368e3adcSJanosch Frank
68*368e3adcSJanosch Frank        elif arch == 'aarch64-be':
69*368e3adcSJanosch Frank            self.endianess = ELFDATA2MSB
70*368e3adcSJanosch Frank            self.ehdr = get_arch_ehdr(self.endianess, self.elfclass)
71*368e3adcSJanosch Frank            self.ehdr.e_machine = EM_AARCH
72*368e3adcSJanosch Frank
73*368e3adcSJanosch Frank        elif arch == 'X86_64':
74*368e3adcSJanosch Frank            self.endianess = ELFDATA2LSB
75*368e3adcSJanosch Frank            self.ehdr = get_arch_ehdr(self.endianess, self.elfclass)
76*368e3adcSJanosch Frank            self.ehdr.e_machine = EM_X86_64
77*368e3adcSJanosch Frank
78*368e3adcSJanosch Frank        elif arch == '386':
79*368e3adcSJanosch Frank            self.endianess = ELFDATA2LSB
80*368e3adcSJanosch Frank            self.elfclass = ELFCLASS32
81*368e3adcSJanosch Frank            self.ehdr = get_arch_ehdr(self.endianess, self.elfclass)
82*368e3adcSJanosch Frank            self.ehdr.e_machine = EM_386
83*368e3adcSJanosch Frank
84*368e3adcSJanosch Frank        elif arch == 's390':
85*368e3adcSJanosch Frank            self.endianess = ELFDATA2MSB
86*368e3adcSJanosch Frank            self.ehdr = get_arch_ehdr(self.endianess, self.elfclass)
87*368e3adcSJanosch Frank            self.ehdr.e_machine = EM_S390
88*368e3adcSJanosch Frank
89*368e3adcSJanosch Frank        elif arch == 'ppc64-le':
90*368e3adcSJanosch Frank            self.endianess = ELFDATA2LSB
91*368e3adcSJanosch Frank            self.ehdr = get_arch_ehdr(self.endianess, self.elfclass)
92*368e3adcSJanosch Frank            self.ehdr.e_machine = EM_PPC64
93*368e3adcSJanosch Frank
94*368e3adcSJanosch Frank        elif arch == 'ppc64-be':
95*368e3adcSJanosch Frank            self.endianess = ELFDATA2MSB
96*368e3adcSJanosch Frank            self.ehdr = get_arch_ehdr(self.endianess, self.elfclass)
97*368e3adcSJanosch Frank            self.ehdr.e_machine = EM_PPC64
98*368e3adcSJanosch Frank
99*368e3adcSJanosch Frank        else:
100*368e3adcSJanosch Frank            raise gdb.GdbError("No valid arch type specified.\n"
101*368e3adcSJanosch Frank                               "Currently supported types:\n"
102*368e3adcSJanosch Frank                               "aarch64-be, aarch64-le, X86_64, 386, s390, "
103*368e3adcSJanosch Frank                               "ppc64-be, ppc64-le")
104*368e3adcSJanosch Frank
105*368e3adcSJanosch Frank        self.add_segment(PT_NOTE, 0, 0)
106*368e3adcSJanosch Frank
107*368e3adcSJanosch Frank    def add_note(self, n_name, n_desc, n_type):
108*368e3adcSJanosch Frank        """Adds a note to the ELF."""
109*368e3adcSJanosch Frank
110*368e3adcSJanosch Frank        note = get_arch_note(self.endianess, len(n_name), len(n_desc))
111*368e3adcSJanosch Frank        note.n_namesz = len(n_name) + 1
112*368e3adcSJanosch Frank        note.n_descsz = len(n_desc)
113*368e3adcSJanosch Frank        note.n_name = n_name.encode()
114*368e3adcSJanosch Frank        note.n_type = n_type
115*368e3adcSJanosch Frank
116*368e3adcSJanosch Frank        # Desc needs to be 4 byte aligned (although the 64bit spec
117*368e3adcSJanosch Frank        # specifies 8 byte). When defining n_desc as uint32 it will be
118*368e3adcSJanosch Frank        # automatically aligned but we need the memmove to copy the
119*368e3adcSJanosch Frank        # string into it.
120*368e3adcSJanosch Frank        ctypes.memmove(note.n_desc, n_desc.encode(), len(n_desc))
121*368e3adcSJanosch Frank
122*368e3adcSJanosch Frank        self.notes.append(note)
123*368e3adcSJanosch Frank        self.segments[0].p_filesz += ctypes.sizeof(note)
124*368e3adcSJanosch Frank        self.segments[0].p_memsz += ctypes.sizeof(note)
125*368e3adcSJanosch Frank
126*368e3adcSJanosch Frank    def add_segment(self, p_type, p_paddr, p_size):
127*368e3adcSJanosch Frank        """Adds a segment to the elf."""
128*368e3adcSJanosch Frank
129*368e3adcSJanosch Frank        phdr = get_arch_phdr(self.endianess, self.elfclass)
130*368e3adcSJanosch Frank        phdr.p_type = p_type
131*368e3adcSJanosch Frank        phdr.p_paddr = p_paddr
132*368e3adcSJanosch Frank        phdr.p_filesz = p_size
133*368e3adcSJanosch Frank        phdr.p_memsz = p_size
134*368e3adcSJanosch Frank        self.segments.append(phdr)
135*368e3adcSJanosch Frank        self.ehdr.e_phnum += 1
136*368e3adcSJanosch Frank
137*368e3adcSJanosch Frank    def to_file(self, elf_file):
138*368e3adcSJanosch Frank        """Writes all ELF structures to the the passed file.
139*368e3adcSJanosch Frank
140*368e3adcSJanosch Frank        Structure:
141*368e3adcSJanosch Frank        Ehdr
142*368e3adcSJanosch Frank        Segment 0:PT_NOTE
143*368e3adcSJanosch Frank        Segment 1:PT_LOAD
144*368e3adcSJanosch Frank        Segment N:PT_LOAD
145*368e3adcSJanosch Frank        Note    0..N
146*368e3adcSJanosch Frank        Dump contents
147*368e3adcSJanosch Frank        """
148*368e3adcSJanosch Frank        elf_file.write(self.ehdr)
149*368e3adcSJanosch Frank        off = ctypes.sizeof(self.ehdr) + \
150*368e3adcSJanosch Frank              len(self.segments) * ctypes.sizeof(self.segments[0])
151*368e3adcSJanosch Frank
152*368e3adcSJanosch Frank        for phdr in self.segments:
153*368e3adcSJanosch Frank            phdr.p_offset = off
154*368e3adcSJanosch Frank            elf_file.write(phdr)
155*368e3adcSJanosch Frank            off += phdr.p_filesz
156*368e3adcSJanosch Frank
157*368e3adcSJanosch Frank        for note in self.notes:
158*368e3adcSJanosch Frank            elf_file.write(note)
159*368e3adcSJanosch Frank
160*368e3adcSJanosch Frank
161*368e3adcSJanosch Frankdef get_arch_note(endianess, len_name, len_desc):
162*368e3adcSJanosch Frank    """Returns a Note class with the specified endianess."""
163*368e3adcSJanosch Frank
164*368e3adcSJanosch Frank    if endianess == ELFDATA2LSB:
165*368e3adcSJanosch Frank        superclass = ctypes.LittleEndianStructure
166*368e3adcSJanosch Frank    else:
167*368e3adcSJanosch Frank        superclass = ctypes.BigEndianStructure
168*368e3adcSJanosch Frank
169*368e3adcSJanosch Frank    len_name = len_name + 1
170*368e3adcSJanosch Frank
171*368e3adcSJanosch Frank    class Note(superclass):
172*368e3adcSJanosch Frank        """Represents an ELF note, includes the content."""
173*368e3adcSJanosch Frank
174*368e3adcSJanosch Frank        _fields_ = [("n_namesz", ctypes.c_uint32),
175*368e3adcSJanosch Frank                    ("n_descsz", ctypes.c_uint32),
176*368e3adcSJanosch Frank                    ("n_type", ctypes.c_uint32),
177*368e3adcSJanosch Frank                    ("n_name", ctypes.c_char * len_name),
178*368e3adcSJanosch Frank                    ("n_desc", ctypes.c_uint32 * ((len_desc + 3) // 4))]
179*368e3adcSJanosch Frank    return Note()
180*368e3adcSJanosch Frank
181*368e3adcSJanosch Frank
182*368e3adcSJanosch Frankclass Ident(ctypes.Structure):
183*368e3adcSJanosch Frank    """Represents the ELF ident array in the ehdr structure."""
184*368e3adcSJanosch Frank
185*368e3adcSJanosch Frank    _fields_ = [('ei_mag0', ctypes.c_ubyte),
186*368e3adcSJanosch Frank                ('ei_mag1', ctypes.c_ubyte),
187*368e3adcSJanosch Frank                ('ei_mag2', ctypes.c_ubyte),
188*368e3adcSJanosch Frank                ('ei_mag3', ctypes.c_ubyte),
189*368e3adcSJanosch Frank                ('ei_class', ctypes.c_ubyte),
190*368e3adcSJanosch Frank                ('ei_data', ctypes.c_ubyte),
191*368e3adcSJanosch Frank                ('ei_version', ctypes.c_ubyte),
192*368e3adcSJanosch Frank                ('ei_osabi', ctypes.c_ubyte),
193*368e3adcSJanosch Frank                ('ei_abiversion', ctypes.c_ubyte),
194*368e3adcSJanosch Frank                ('ei_pad', ctypes.c_ubyte * 7)]
195*368e3adcSJanosch Frank
196*368e3adcSJanosch Frank    def __init__(self, endianess, elfclass):
197*368e3adcSJanosch Frank        self.ei_mag0 = 0x7F
198*368e3adcSJanosch Frank        self.ei_mag1 = ord('E')
199*368e3adcSJanosch Frank        self.ei_mag2 = ord('L')
200*368e3adcSJanosch Frank        self.ei_mag3 = ord('F')
201*368e3adcSJanosch Frank        self.ei_class = elfclass
202*368e3adcSJanosch Frank        self.ei_data = endianess
203*368e3adcSJanosch Frank        self.ei_version = EV_CURRENT
204*368e3adcSJanosch Frank
205*368e3adcSJanosch Frank
206*368e3adcSJanosch Frankdef get_arch_ehdr(endianess, elfclass):
207*368e3adcSJanosch Frank    """Returns a EHDR64 class with the specified endianess."""
208*368e3adcSJanosch Frank
209*368e3adcSJanosch Frank    if endianess == ELFDATA2LSB:
210*368e3adcSJanosch Frank        superclass = ctypes.LittleEndianStructure
211*368e3adcSJanosch Frank    else:
212*368e3adcSJanosch Frank        superclass = ctypes.BigEndianStructure
213*368e3adcSJanosch Frank
214*368e3adcSJanosch Frank    class EHDR64(superclass):
215*368e3adcSJanosch Frank        """Represents the 64 bit ELF header struct."""
216*368e3adcSJanosch Frank
217*368e3adcSJanosch Frank        _fields_ = [('e_ident', Ident),
218*368e3adcSJanosch Frank                    ('e_type', ctypes.c_uint16),
219*368e3adcSJanosch Frank                    ('e_machine', ctypes.c_uint16),
220*368e3adcSJanosch Frank                    ('e_version', ctypes.c_uint32),
221*368e3adcSJanosch Frank                    ('e_entry', ctypes.c_uint64),
222*368e3adcSJanosch Frank                    ('e_phoff', ctypes.c_uint64),
223*368e3adcSJanosch Frank                    ('e_shoff', ctypes.c_uint64),
224*368e3adcSJanosch Frank                    ('e_flags', ctypes.c_uint32),
225*368e3adcSJanosch Frank                    ('e_ehsize', ctypes.c_uint16),
226*368e3adcSJanosch Frank                    ('e_phentsize', ctypes.c_uint16),
227*368e3adcSJanosch Frank                    ('e_phnum', ctypes.c_uint16),
228*368e3adcSJanosch Frank                    ('e_shentsize', ctypes.c_uint16),
229*368e3adcSJanosch Frank                    ('e_shnum', ctypes.c_uint16),
230*368e3adcSJanosch Frank                    ('e_shstrndx', ctypes.c_uint16)]
231*368e3adcSJanosch Frank
232*368e3adcSJanosch Frank        def __init__(self):
233*368e3adcSJanosch Frank            super(superclass, self).__init__()
234*368e3adcSJanosch Frank            self.e_ident = Ident(endianess, elfclass)
235*368e3adcSJanosch Frank            self.e_type = ET_CORE
236*368e3adcSJanosch Frank            self.e_version = EV_CURRENT
237*368e3adcSJanosch Frank            self.e_ehsize = ctypes.sizeof(self)
238*368e3adcSJanosch Frank            self.e_phoff = ctypes.sizeof(self)
239*368e3adcSJanosch Frank            self.e_phentsize = ctypes.sizeof(get_arch_phdr(endianess, elfclass))
240*368e3adcSJanosch Frank            self.e_phnum = 0
241*368e3adcSJanosch Frank
242*368e3adcSJanosch Frank
243*368e3adcSJanosch Frank    class EHDR32(superclass):
244*368e3adcSJanosch Frank        """Represents the 32 bit ELF header struct."""
245*368e3adcSJanosch Frank
246*368e3adcSJanosch Frank        _fields_ = [('e_ident', Ident),
247*368e3adcSJanosch Frank                    ('e_type', ctypes.c_uint16),
248*368e3adcSJanosch Frank                    ('e_machine', ctypes.c_uint16),
249*368e3adcSJanosch Frank                    ('e_version', ctypes.c_uint32),
250*368e3adcSJanosch Frank                    ('e_entry', ctypes.c_uint32),
251*368e3adcSJanosch Frank                    ('e_phoff', ctypes.c_uint32),
252*368e3adcSJanosch Frank                    ('e_shoff', ctypes.c_uint32),
253*368e3adcSJanosch Frank                    ('e_flags', ctypes.c_uint32),
254*368e3adcSJanosch Frank                    ('e_ehsize', ctypes.c_uint16),
255*368e3adcSJanosch Frank                    ('e_phentsize', ctypes.c_uint16),
256*368e3adcSJanosch Frank                    ('e_phnum', ctypes.c_uint16),
257*368e3adcSJanosch Frank                    ('e_shentsize', ctypes.c_uint16),
258*368e3adcSJanosch Frank                    ('e_shnum', ctypes.c_uint16),
259*368e3adcSJanosch Frank                    ('e_shstrndx', ctypes.c_uint16)]
260*368e3adcSJanosch Frank
261*368e3adcSJanosch Frank        def __init__(self):
262*368e3adcSJanosch Frank            super(superclass, self).__init__()
263*368e3adcSJanosch Frank            self.e_ident = Ident(endianess, elfclass)
264*368e3adcSJanosch Frank            self.e_type = ET_CORE
265*368e3adcSJanosch Frank            self.e_version = EV_CURRENT
266*368e3adcSJanosch Frank            self.e_ehsize = ctypes.sizeof(self)
267*368e3adcSJanosch Frank            self.e_phoff = ctypes.sizeof(self)
268*368e3adcSJanosch Frank            self.e_phentsize = ctypes.sizeof(get_arch_phdr(endianess, elfclass))
269*368e3adcSJanosch Frank            self.e_phnum = 0
270*368e3adcSJanosch Frank
271*368e3adcSJanosch Frank    # End get_arch_ehdr
272*368e3adcSJanosch Frank    if elfclass == ELFCLASS64:
273*368e3adcSJanosch Frank        return EHDR64()
274*368e3adcSJanosch Frank    else:
275*368e3adcSJanosch Frank        return EHDR32()
276*368e3adcSJanosch Frank
277*368e3adcSJanosch Frank
278*368e3adcSJanosch Frankdef get_arch_phdr(endianess, elfclass):
279*368e3adcSJanosch Frank    """Returns a 32 or 64 bit PHDR class with the specified endianess."""
280*368e3adcSJanosch Frank
281*368e3adcSJanosch Frank    if endianess == ELFDATA2LSB:
282*368e3adcSJanosch Frank        superclass = ctypes.LittleEndianStructure
283*368e3adcSJanosch Frank    else:
284*368e3adcSJanosch Frank        superclass = ctypes.BigEndianStructure
285*368e3adcSJanosch Frank
286*368e3adcSJanosch Frank    class PHDR64(superclass):
287*368e3adcSJanosch Frank        """Represents the 64 bit ELF program header struct."""
288*368e3adcSJanosch Frank
289*368e3adcSJanosch Frank        _fields_ = [('p_type', ctypes.c_uint32),
290*368e3adcSJanosch Frank                    ('p_flags', ctypes.c_uint32),
291*368e3adcSJanosch Frank                    ('p_offset', ctypes.c_uint64),
292*368e3adcSJanosch Frank                    ('p_vaddr', ctypes.c_uint64),
293*368e3adcSJanosch Frank                    ('p_paddr', ctypes.c_uint64),
294*368e3adcSJanosch Frank                    ('p_filesz', ctypes.c_uint64),
295*368e3adcSJanosch Frank                    ('p_memsz', ctypes.c_uint64),
296*368e3adcSJanosch Frank                    ('p_align', ctypes.c_uint64)]
297*368e3adcSJanosch Frank
298*368e3adcSJanosch Frank    class PHDR32(superclass):
299*368e3adcSJanosch Frank        """Represents the 32 bit ELF program header struct."""
300*368e3adcSJanosch Frank
301*368e3adcSJanosch Frank        _fields_ = [('p_type', ctypes.c_uint32),
302*368e3adcSJanosch Frank                    ('p_offset', ctypes.c_uint32),
303*368e3adcSJanosch Frank                    ('p_vaddr', ctypes.c_uint32),
304*368e3adcSJanosch Frank                    ('p_paddr', ctypes.c_uint32),
305*368e3adcSJanosch Frank                    ('p_filesz', ctypes.c_uint32),
306*368e3adcSJanosch Frank                    ('p_memsz', ctypes.c_uint32),
307*368e3adcSJanosch Frank                    ('p_flags', ctypes.c_uint32),
308*368e3adcSJanosch Frank                    ('p_align', ctypes.c_uint32)]
309*368e3adcSJanosch Frank
310*368e3adcSJanosch Frank    # End get_arch_phdr
311*368e3adcSJanosch Frank    if elfclass == ELFCLASS64:
312*368e3adcSJanosch Frank        return PHDR64()
313*368e3adcSJanosch Frank    else:
314*368e3adcSJanosch Frank        return PHDR32()
315*368e3adcSJanosch Frank
3163e16d14fSLaszlo Ersek
31747890203SJanosch Frankdef int128_get64(val):
3186782c0e7SJanosch Frank    """Returns low 64bit part of Int128 struct."""
3196782c0e7SJanosch Frank
3206782c0e7SJanosch Frank    assert val["hi"] == 0
32147890203SJanosch Frank    return val["lo"]
32247890203SJanosch Frank
3236782c0e7SJanosch Frank
32447890203SJanosch Frankdef qlist_foreach(head, field_str):
3256782c0e7SJanosch Frank    """Generator for qlists."""
3266782c0e7SJanosch Frank
32747890203SJanosch Frank    var_p = head["lh_first"]
3286782c0e7SJanosch Frank    while var_p != 0:
32947890203SJanosch Frank        var = var_p.dereference()
33047890203SJanosch Frank        var_p = var[field_str]["le_next"]
3316782c0e7SJanosch Frank        yield var
3326782c0e7SJanosch Frank
33347890203SJanosch Frank
33447890203SJanosch Frankdef qemu_get_ram_block(ram_addr):
3356782c0e7SJanosch Frank    """Returns the RAMBlock struct to which the given address belongs."""
3366782c0e7SJanosch Frank
33747890203SJanosch Frank    ram_blocks = gdb.parse_and_eval("ram_list.blocks")
3386782c0e7SJanosch Frank
33947890203SJanosch Frank    for block in qlist_foreach(ram_blocks, "next"):
3406782c0e7SJanosch Frank        if (ram_addr - block["offset"]) < block["used_length"]:
34147890203SJanosch Frank            return block
3426782c0e7SJanosch Frank
34347890203SJanosch Frank    raise gdb.GdbError("Bad ram offset %x" % ram_addr)
34447890203SJanosch Frank
3456782c0e7SJanosch Frank
34647890203SJanosch Frankdef qemu_get_ram_ptr(ram_addr):
3476782c0e7SJanosch Frank    """Returns qemu vaddr for given guest physical address."""
3486782c0e7SJanosch Frank
34947890203SJanosch Frank    block = qemu_get_ram_block(ram_addr)
35047890203SJanosch Frank    return block["host"] + (ram_addr - block["offset"])
35147890203SJanosch Frank
3526782c0e7SJanosch Frank
3536782c0e7SJanosch Frankdef memory_region_get_ram_ptr(memory_region):
3546782c0e7SJanosch Frank    if memory_region["alias"] != 0:
3556782c0e7SJanosch Frank        return (memory_region_get_ram_ptr(memory_region["alias"].dereference())
3566782c0e7SJanosch Frank                + memory_region["alias_offset"])
3576782c0e7SJanosch Frank
3586782c0e7SJanosch Frank    return qemu_get_ram_ptr(memory_region["ram_addr"] & TARGET_PAGE_MASK)
3596782c0e7SJanosch Frank
36047890203SJanosch Frank
36147890203SJanosch Frankdef get_guest_phys_blocks():
3626782c0e7SJanosch Frank    """Returns a list of ram blocks.
3636782c0e7SJanosch Frank
3646782c0e7SJanosch Frank    Each block entry contains:
3656782c0e7SJanosch Frank    'target_start': guest block phys start address
3666782c0e7SJanosch Frank    'target_end':   guest block phys end address
3676782c0e7SJanosch Frank    'host_addr':    qemu vaddr of the block's start
3686782c0e7SJanosch Frank    """
3696782c0e7SJanosch Frank
37047890203SJanosch Frank    guest_phys_blocks = []
3716782c0e7SJanosch Frank
3727cb1089dSJanosch Frank    print("guest RAM blocks:")
37347890203SJanosch Frank    print("target_start     target_end       host_addr        message "
37447890203SJanosch Frank          "count")
37547890203SJanosch Frank    print("---------------- ---------------- ---------------- ------- "
37647890203SJanosch Frank          "-----")
37747890203SJanosch Frank
37847890203SJanosch Frank    current_map_p = gdb.parse_and_eval("address_space_memory.current_map")
37947890203SJanosch Frank    current_map = current_map_p.dereference()
3807cb1089dSJanosch Frank
3817cb1089dSJanosch Frank    # Conversion to int is needed for python 3
3827cb1089dSJanosch Frank    # compatibility. Otherwise range doesn't cast the value itself and
3837cb1089dSJanosch Frank    # breaks.
3847cb1089dSJanosch Frank    for cur in range(int(current_map["nr"])):
38547890203SJanosch Frank        flat_range = (current_map["ranges"] + cur).dereference()
3866782c0e7SJanosch Frank        memory_region = flat_range["mr"].dereference()
38747890203SJanosch Frank
38847890203SJanosch Frank        # we only care about RAM
3896782c0e7SJanosch Frank        if not memory_region["ram"]:
39047890203SJanosch Frank            continue
39147890203SJanosch Frank
39247890203SJanosch Frank        section_size = int128_get64(flat_range["addr"]["size"])
39347890203SJanosch Frank        target_start = int128_get64(flat_range["addr"]["start"])
39447890203SJanosch Frank        target_end = target_start + section_size
3956782c0e7SJanosch Frank        host_addr = (memory_region_get_ram_ptr(memory_region)
3966782c0e7SJanosch Frank                     + flat_range["offset_in_region"])
39747890203SJanosch Frank        predecessor = None
39847890203SJanosch Frank
39947890203SJanosch Frank        # find continuity in guest physical address space
4006782c0e7SJanosch Frank        if len(guest_phys_blocks) > 0:
40147890203SJanosch Frank            predecessor = guest_phys_blocks[-1]
40247890203SJanosch Frank            predecessor_size = (predecessor["target_end"] -
40347890203SJanosch Frank                                predecessor["target_start"])
40447890203SJanosch Frank
40547890203SJanosch Frank            # the memory API guarantees monotonically increasing
40647890203SJanosch Frank            # traversal
4076782c0e7SJanosch Frank            assert predecessor["target_end"] <= target_start
40847890203SJanosch Frank
40947890203SJanosch Frank            # we want continuity in both guest-physical and
41047890203SJanosch Frank            # host-virtual memory
41147890203SJanosch Frank            if (predecessor["target_end"] < target_start or
41247890203SJanosch Frank                predecessor["host_addr"] + predecessor_size != host_addr):
41347890203SJanosch Frank                predecessor = None
41447890203SJanosch Frank
4156782c0e7SJanosch Frank        if predecessor is None:
41647890203SJanosch Frank            # isolated mapping, add it to the list
41747890203SJanosch Frank            guest_phys_blocks.append({"target_start": target_start,
41847890203SJanosch Frank                                      "target_end":   target_end,
41947890203SJanosch Frank                                      "host_addr":    host_addr})
42047890203SJanosch Frank            message = "added"
42147890203SJanosch Frank        else:
42247890203SJanosch Frank            # expand predecessor until @target_end; predecessor's
42347890203SJanosch Frank            # start doesn't change
42447890203SJanosch Frank            predecessor["target_end"] = target_end
42547890203SJanosch Frank            message = "joined"
42647890203SJanosch Frank
42747890203SJanosch Frank        print("%016x %016x %016x %-7s %5u" %
42847890203SJanosch Frank              (target_start, target_end, host_addr.cast(UINTPTR_T),
42947890203SJanosch Frank               message, len(guest_phys_blocks)))
43047890203SJanosch Frank
43147890203SJanosch Frank    return guest_phys_blocks
43247890203SJanosch Frank
43347890203SJanosch Frank
434ca81ce72SJanosch Frankclass DumpGuestMemory(gdb.Command):
435ca81ce72SJanosch Frank    """Extract guest vmcore from qemu process coredump.
436ca81ce72SJanosch Frank
437*368e3adcSJanosch FrankThe two required arguments are FILE and ARCH:
438*368e3adcSJanosch FrankFILE identifies the target file to write the guest vmcore to.
439*368e3adcSJanosch FrankARCH specifies the architecture for which the core will be generated.
440ca81ce72SJanosch Frank
441ca81ce72SJanosch FrankThis GDB command reimplements the dump-guest-memory QMP command in
442ca81ce72SJanosch Frankpython, using the representation of guest memory as captured in the qemu
443ca81ce72SJanosch Frankcoredump. The qemu process that has been dumped must have had the
444*368e3adcSJanosch Frankcommand line option "-machine dump-guest-core=on" which is the default.
445ca81ce72SJanosch Frank
446ca81ce72SJanosch FrankFor simplicity, the "paging", "begin" and "end" parameters of the QMP
447ca81ce72SJanosch Frankcommand are not supported -- no attempt is made to get the guest's
448ca81ce72SJanosch Frankinternal paging structures (ie. paging=false is hard-wired), and guest
449ca81ce72SJanosch Frankmemory is always fully dumped.
450ca81ce72SJanosch Frank
451*368e3adcSJanosch FrankCurrently aarch64-be, aarch64-le, X86_64, 386, s390, ppc64-be,
452*368e3adcSJanosch Frankppc64-le guests are supported.
453ca81ce72SJanosch Frank
454ca81ce72SJanosch FrankThe CORE/NT_PRSTATUS and QEMU notes (that is, the VCPUs' statuses) are
455ca81ce72SJanosch Franknot written to the vmcore. Preparing these would require context that is
456ca81ce72SJanosch Frankonly present in the KVM host kernel module when the guest is alive. A
457ca81ce72SJanosch Frankfake ELF note is written instead, only to keep the ELF parser of "crash"
458ca81ce72SJanosch Frankhappy.
459ca81ce72SJanosch Frank
460ca81ce72SJanosch FrankDependent on how busted the qemu process was at the time of the
461ca81ce72SJanosch Frankcoredump, this command might produce unpredictable results. If qemu
462ca81ce72SJanosch Frankdeliberately called abort(), or it was dumped in response to a signal at
463ca81ce72SJanosch Franka halfway fortunate point, then its coredump should be in reasonable
464ca81ce72SJanosch Frankshape and this command should mostly work."""
465ca81ce72SJanosch Frank
4663e16d14fSLaszlo Ersek    def __init__(self):
4673e16d14fSLaszlo Ersek        super(DumpGuestMemory, self).__init__("dump-guest-memory",
4683e16d14fSLaszlo Ersek                                              gdb.COMMAND_DATA,
4693e16d14fSLaszlo Ersek                                              gdb.COMPLETE_FILENAME)
470*368e3adcSJanosch Frank        self.elf = None
47147890203SJanosch Frank        self.guest_phys_blocks = None
4723e16d14fSLaszlo Ersek
473*368e3adcSJanosch Frank    def dump_init(self, vmcore):
474*368e3adcSJanosch Frank        """Prepares and writes ELF structures to core file."""
4753e16d14fSLaszlo Ersek
476*368e3adcSJanosch Frank        # Needed to make crash happy, data for more useful notes is
477*368e3adcSJanosch Frank        # not available in a qemu core.
478*368e3adcSJanosch Frank        self.elf.add_note("NONE", "EMPTY", 0)
4793e16d14fSLaszlo Ersek
480*368e3adcSJanosch Frank        # We should never reach PN_XNUM for paging=false dumps,
481*368e3adcSJanosch Frank        # there's just a handful of discontiguous ranges after
482*368e3adcSJanosch Frank        # merging.
483*368e3adcSJanosch Frank        # The constant is needed to account for the PT_NOTE segment.
484*368e3adcSJanosch Frank        phdr_num = len(self.guest_phys_blocks) + 1
485*368e3adcSJanosch Frank        assert phdr_num < PN_XNUM
4863e16d14fSLaszlo Ersek
4873e16d14fSLaszlo Ersek        for block in self.guest_phys_blocks:
488*368e3adcSJanosch Frank            block_size = block["target_end"] - block["target_start"]
489*368e3adcSJanosch Frank            self.elf.add_segment(PT_LOAD, block["target_start"], block_size)
490*368e3adcSJanosch Frank
491*368e3adcSJanosch Frank        self.elf.to_file(vmcore)
4923e16d14fSLaszlo Ersek
4933e16d14fSLaszlo Ersek    def dump_iterate(self, vmcore):
494*368e3adcSJanosch Frank        """Writes guest core to file."""
495*368e3adcSJanosch Frank
4963e16d14fSLaszlo Ersek        qemu_core = gdb.inferiors()[0]
4973e16d14fSLaszlo Ersek        for block in self.guest_phys_blocks:
4983e16d14fSLaszlo Ersek            cur = block["host_addr"]
4993e16d14fSLaszlo Ersek            left = block["target_end"] - block["target_start"]
5003e16d14fSLaszlo Ersek            print("dumping range at %016x for length %016x" %
50147890203SJanosch Frank                  (cur.cast(UINTPTR_T), left))
502*368e3adcSJanosch Frank
5036782c0e7SJanosch Frank            while left > 0:
504ca81ce72SJanosch Frank                chunk_size = min(TARGET_PAGE_SIZE, left)
5053e16d14fSLaszlo Ersek                chunk = qemu_core.read_memory(cur, chunk_size)
5063e16d14fSLaszlo Ersek                vmcore.write(chunk)
5073e16d14fSLaszlo Ersek                cur += chunk_size
5083e16d14fSLaszlo Ersek                left -= chunk_size
5093e16d14fSLaszlo Ersek
5103e16d14fSLaszlo Ersek    def invoke(self, args, from_tty):
511*368e3adcSJanosch Frank        """Handles command invocation from gdb."""
512*368e3adcSJanosch Frank
5133e16d14fSLaszlo Ersek        # Unwittingly pressing the Enter key after the command should
5143e16d14fSLaszlo Ersek        # not dump the same multi-gig coredump to the same file.
5153e16d14fSLaszlo Ersek        self.dont_repeat()
5163e16d14fSLaszlo Ersek
5173e16d14fSLaszlo Ersek        argv = gdb.string_to_argv(args)
518*368e3adcSJanosch Frank        if len(argv) != 2:
519*368e3adcSJanosch Frank            raise gdb.GdbError("usage: dump-guest-memory FILE ARCH")
5203e16d14fSLaszlo Ersek
521*368e3adcSJanosch Frank        self.elf = ELF(argv[1])
522*368e3adcSJanosch Frank        self.guest_phys_blocks = get_guest_phys_blocks()
523*368e3adcSJanosch Frank
524*368e3adcSJanosch Frank        with open(argv[0], "wb") as vmcore:
525*368e3adcSJanosch Frank            self.dump_init(vmcore)
526*368e3adcSJanosch Frank            self.dump_iterate(vmcore)
5273e16d14fSLaszlo Ersek
5283e16d14fSLaszlo ErsekDumpGuestMemory()
529