diff -u dumpread.O/Arch.py dumpread/Arch.py --- dumpread.O/Arch.py 2006-06-09 20:14:45.000000000 +0900 +++ dumpread/Arch.py 2006-06-30 16:30:08.000000000 +0900 @@ -10,6 +10,9 @@ self.max_vcpu = kw['max_vcpu'] self.elf_format = kw['elf_format'] self.elf_machine = kw['elf_machine'] + self.hypervisor_virt_start = kw['hypervisor_virt_start'] + self.hypervisor_virt_end = kw['hypervisor_virt_end'] + self.hypervisor_page_offset = kw['hypervisor_page_offset'] def maddr_to_mfn(self, maddr): '''return mfn from maddr''' @@ -31,7 +34,9 @@ '''return size round by pagesize''' return ((size + (self.page_size -1)) / self.page_size) * self.page_size - + def is_xen_virt(self, pfn): + '''return pfn is in xen virtual address space''' + return self.hypervisor_virt_start <= pfn <= self.hypervisor_virt_end archparam = {'x86_32': {'page_shift': 12, @@ -39,6 +44,19 @@ 'max_vcpu': 32, 'elf_format': 'ELF32', 'elf_machine': libelf.EM_386, + 'hypervisor_virt_start': 0xFC000L, + 'hypervisor_virt_end': 0xFFFFFL, + 'hypervisor_page_offset': 0xFF000000L, + }, + 'x86_32_pae': + {'page_shift': 12, + 'pt_format':'L', + 'max_vcpu': 32, + 'elf_format': 'ELF32', + 'elf_machine': libelf.EM_386, + 'hypervisor_virt_start': 0xF5800L, + 'hypervisor_virt_end': 0xFFFFFL, + 'hypervisor_page_offset': 0xFF000000L, }, 'x86_64': {'page_shift': 12, @@ -46,13 +64,19 @@ 'max_vcpu': 32, 'elf_format': 'ELF64', 'elf_machine': libelf.EM_386, + 'hypervisor_virt_start': 0xFFFF800000000L, + 'hypervisor_virt_end': 0xFFFF880000000L, + 'hypervisor_page_offset': 0xFFFF830000000000L, }, 'ia64': {'page_shift': 14, 'pt_format':'Q', - 'max_vcpu': 4, + 'max_vcpu': 64, 'elf_format': 'ELF64', 'elf_machine': libelf.EM_IA_64, + 'hypervisor_virt_start': 0x3c00000000000L, + 'hypervisor_virt_end': 0x3d00000000000L, + 'hypervisor_page_offset': 0xe000000000000000L, }} arch = {'x86_32': Arch('x86_32', **archparam['x86_32']), diff -u dumpread.O/README dumpread/README --- dumpread.O/README 2006-06-09 20:14:45.000000000 +0900 +++ dumpread/README 2006-06-30 16:33:49.000000000 +0900 @@ -21,7 +21,8 @@ output dump file for output -dDUMPNAME, --xendump=DUMPNAME xen dump file for input - --target=TARGET extract target("dom0" or "xen") + --target=TARGET extract target(domain id in decimal number or "x" for + xen) -xXENSYMS, --xensyms=XENSYMS xen binary with symbols -vVMLINUX, --vmlinux=VMLINUX @@ -30,3 +31,4 @@ -tFILETYPE, --type=FILETYPE output file type("xen" or "elf") -f, --force overwrite output file + -l, --list-domain print domain list diff -u dumpread.O/XenCore.py dumpread/XenCore.py --- dumpread.O/XenCore.py 2006-06-09 20:14:45.000000000 +0900 +++ dumpread/XenCore.py 2006-06-30 16:30:08.000000000 +0900 @@ -90,12 +90,11 @@ for i, reg in enumerate(regs): self.ctxts[i].set_register(reg) - def set_ctrlreg(self, num, val): + def set_ctrlreg(self, vcpu, num, val): '''set all ctrlreg[num] as val''' # XXXX it works on uni processor only # walkaround to generate Xen core header.. - for ctxt in self.ctxts: - ctxt.set_ctrlreg(num, val) + self.ctxts[vcpu].set_ctrlreg(num, val) def set_v2m(self, v2m): # not used in Xen core format. diff -u dumpread.O/dom0cut_x86_32.py dumpread/dom0cut_x86_32.py --- dumpread.O/dom0cut_x86_32.py 2006-06-09 20:14:45.000000000 +0900 +++ dumpread/dom0cut_x86_32.py 2006-06-30 16:30:08.000000000 +0900 @@ -13,12 +13,13 @@ from optparse import OptionParser import XenCore import ElfCore - +import sets +from itertools import izip typemap = { - ('xen', 'dom0'): XenCore.XenCoreWriter, - ('xen', 'xen'): None, - ('elf', 'dom0'): ElfCore.ElfCoreWriterV2P, + ('xen', 'domain'): XenCore.XenCoreWriter, + ('xen', 'xen'): XenCore.XenCoreWriter, + ('elf', 'domain'): ElfCore.ElfCoreWriterV2P, ('elf', 'xen'): ElfCore.ElfCoreWriterV2M, } @@ -43,30 +44,66 @@ def get_xen_idle_pt(self): '''idle domain page table''' - ptaddr = self.st['idle_pg_table'] - 0xff000000L # PAGE_OFFSET + ptaddr = self.st['idle_pg_table'] - self.dump.arch.hypervisor_page_offset # PAGE_OFFSET return PageTable.PageTable(self.dump, self.dump.arch.maddr_to_mfn(ptaddr)) - def get_dom0p(self): - """domain.arch_domain.mm_perdomain_pt 's offset """ + def get_domain_list(self): + '''return list of all domain pointers''' + + # dom0 is start of list. domain list is sorted by domain id. + domainp = self.dump.read_struct(self.pt.v2m(self.st['dom0']), 'L')[0] + domain_list = [] + domain_next_in_list_offset = self.tree.calc_struct_member_offset('domain', 'next_in_list') + while domainp != 0x0: + domain_list.append(domainp) + domainp = self.dump.read_struct(self.pt.v2m(domainp + domain_next_in_list_offset), 'L')[0] + + return domain_list + + def get_domainp(self, domid): + """return domain pointer specified by domain id. + If there isn't domain with given domain id, raise KeyError. + """ - return self.dump.read_struct(self.pt.v2m(self.st['dom0']), 'L')[0] + domlst = self.get_domain_list() + + for domp in domlst: + if self.get_domain_id(domp) == domid: + return domp + raise KeyError + + def get_domain_id(self, domainp): + # domain ID + domain_domain_id_offset = self.tree.calc_struct_member_offset('domain', 'domain_id') + domain_id = self.dump.read_struct(self.pt.v2m(domainp + domain_domain_id_offset), 'L')[0] + + return domain_id + def get_domain_pt(self, domainp): '''get kernel pagetable for domain. domainp is virtual address for struct domain''' vcpu_offset = self.tree.calc_struct_member_offset('domain', 'vcpu') # vcpu[0] - return self.get_vcpu_pt(self.dump.read_struct(self.pt.v2m(domainp + vcpu_offset), 'L')[0], - 'guest_table') + vcpu = self.dump.read_struct(self.pt.v2m(domainp + vcpu_offset), 'L')[0] + return self.get_vcpu_pt(vcpu, 'guest_table') def get_xen_pt(self): '''get idle domain pagetable. ''' - return self.get_vcpu_pt(self.dump.read_struct(self.pt.v2m(self.st['idle_vcpu']), 'L')[0], - 'monitor_table') + + doms = self.get_domainp(0) + vcpus = [] + for domain in doms: + vcpus += self.get_domain_vcpus(domain) + pagetables = [] + for vcpu in vcpus: + pagetables.append(self.get_vcpu_pt(vcpu, 'monitor_table')) + return pagetables def get_vcpu_pt(self, vcpu, table): '''get VCPU pagetable. vcpu is vcpu_info pointer, table is "guest_table" or "monitor_table"''' + table_offset = self.tree.calc_struct_member_offset('vcpu', 'arch') table_offset += self.tree.calc_struct_member_offset('arch_vcpu', table) @@ -79,27 +116,35 @@ def get_domain_context(self, domainp): '''get domain context informations''' - - # domain->vcpu[0] - vcpu_offset = self.tree.calc_struct_member_offset('domain', 'vcpu') # vcpu[0] - domain_vcpus = [ - self.dump.read_struct(self.pt.v2m(domainp + - vcpu_offset + - struct.calcsize(self.dump.arch.pt_format) * i), - 'L')[0] - for i in range(self.dump.arch.max_vcpu)] + vcpus = self.get_domain_vcpus(domainp) # vcpu->arch.vcpu_guest_context ctxt_offset = self.tree.calc_struct_member_offset('vcpu', 'arch') - ctxt_domain_offset = self.tree.calc_struct_member_offset('vcpu', 'domain') ctxt_offset += self.tree.calc_struct_member_offset('arch_vcpu', 'guest_context') ctxt_size = self.tree.get_struct_size('vcpu_guest_context') ctxts = [XenCore.XenContext(self.tree, context=self.pt.load_data(vcpu + ctxt_offset, ctxt_size)) - for vcpu in domain_vcpus if vcpu != 0] - + for vcpu in vcpus] return ctxts + def get_domain_vcpus(self, domainp): + '''return list of all vcpu pointers of a domain''' + + vcpu_offset = self.tree.calc_struct_member_offset('domain', 'vcpu') + ptrsize = struct.calcsize(self.dump.arch.pt_format) + domain_vcpus = [ + self.dump.read_struct(self.pt.v2m(domainp + vcpu_offset + ptrsize * i), 'L')[0] + for i in range(self.dump.arch.max_vcpu)] + domain_vcpus = [vcpu for vcpu in domain_vcpus if vcpu != 0] + return domain_vcpus + + + def get_domain_summary_string(self, domainp): + '''print domain summary for passwd domain''' + domain_id = self.get_domain_id(domainp) + nr_vcpu = len(self.get_domain_vcpus(domainp)) + + return "domain id=%d, %d vcpu(s)" % (domain_id, nr_vcpu) class DomainImage: ''' @@ -142,35 +187,44 @@ return pfn2mfn +def list_domain(dump, xensyms): + xenimg = XenImage(dump, xensyms) + domlist = xenimg.get_domain_list() + for dom in domlist: + print xenimg.get_domain_summary_string(dom) -def dom0extract(outdump, dump, xensyms, vmlinux): +def domextract(outdump, dump, xensyms, vmlinux, domid): '''analyse dump with xen-syms, vmlinux, arch to make dom0 dump file outdump. ''' xenimg = XenImage(dump, xensyms) # get dom0 info from xen - dom0p = xenimg.get_dom0p() - dom0_pt = xenimg.get_domain_pt(dom0p) - dom0context = xenimg.get_domain_context(dom0p) + domp = xenimg.get_domainp(domid) + dom_pt = xenimg.get_domain_pt(domp) + dom_context = xenimg.get_domain_context(domp) - # make dom0 image - dom0img = DomainImage(dump, vmlinux, dom0_pt) + # make domain image + dom_img = DomainImage(dump, vmlinux, dom_pt) # set header info - outdump.set_context(dom0context) + outdump.set_context(dom_context) if isinstance(outdump, XenCore.XenCoreWriter): - dom0regs = dump.get_registers() - outdump.set_registers(dom0regs) - outdump.set_ctrlreg(3, dom0img.pt.mfn * outdump.arch.page_size) + #dom_regs = dump.get_registers() + #outdump.set_registers(dom_regs) + + for count, vcpup in enumerate(xenimg.get_domain_vcpus(domp)): + outdump.set_ctrlreg(count, 3, + xenimg.get_vcpu_pt(vcpup, 'guest_table').mfn + * outdump.arch.page_size) if isinstance(outdump, ElfCore.ElfCoreWriter): outdump.set_note(dump.get_note()) - p2m = dom0img.get_p2m() + p2m = dom_img.get_p2m() outdump.set_p2m(p2m) - v2m = dom0_pt.get_virt_to_machine() + v2m = dom_pt.get_virt_to_machine() outdump.set_v2m(v2m) # write header @@ -187,23 +241,35 @@ xenimg = XenImage(dump, xensyms) # get xen info from xen - dom0p = xenimg.get_dom0p() - xen_pt = xenimg.get_xen_pt() + dom0p = xenimg.get_domainp(0) + xen_pts = xenimg.get_xen_pt() dom0context = xenimg.get_domain_context(dom0p) # set header info outdump.set_context(dom0context) if isinstance(outdump, XenCore.XenCoreWriter): + # copy all pages contained in pagetables dom0regs = dump.get_registers() outdump.set_registers(dom0regs) - outdump.set_ctrlreg(3, xen_pt.mfn * outdump.arch.page_size) + outdump.set_ctrlreg(3, xen_pts[0].mfn * outdump.arch.page_size) + mfns = sets.Set() + for pt in xen_pts: + v2m = pt.get_virt_to_machine() + pages = [v2m[v] for v in v2m if dump.arch.is_xen_virt(v)] + mfns |= sets.Set(pages) + + mfns &= sets.Set(dump.get_pagelist()) + p2m = dict(izip(xrange(len(mfns)), mfns)) + outdump.set_p2m(p2m) + outdump.set_v2m({}) if isinstance(outdump, ElfCore.ElfCoreWriter): + # copy pages contained in dom0->vcpu[0] pagetable only outdump.set_note(dump.get_note()) + outdump.set_p2m({}) + v2m = xen_pts[0].get_virt_to_machine() # dom0->vcpu[0] + outdump.set_v2m(v2m) - outdump.set_p2m({}) - v2m = xen_pt.get_virt_to_machine() - outdump.set_v2m(v2m) # write header outdump.write_header() @@ -218,15 +284,15 @@ oparser = OptionParser() oparser.add_option('-o', '--outdump', dest='outdumpname', default='dom0mem', type='string', help='output dump file for output') oparser.add_option('-d', '--xendump', dest='dumpname', default='dump', type='string', help='xen dump file for input') - oparser.add_option('--target', dest='target', default='dom0', type='string', help='extract target("dom0" or "xen")') + oparser.add_option('--target', dest='target', default='0', type='string', help='extract target(domain id in decimal number or "x" for xen)') oparser.add_option('-x', '--xensyms', dest='xensyms', default='xen-syms', type='string', help='xen binary with symbols') oparser.add_option('-v', '--vmlinux', dest='vmlinux', default='vmlinux', type='string', help='linux binary with symbols') oparser.add_option('-a', '--arch', dest='arch', type='string', default='x86_32', help="architecture (now 'x86_32' only)") oparser.add_option("-t", "--type", dest="filetype", default="xen", type="string", help='output file type("xen" or "elf")') oparser.add_option("-f", "--force", dest="force", default=False, action='store_true', help='overwrite output file') + oparser.add_option("-l", "--list-domain", dest="list_domain", default=False, action='store_true', help='print domain list') (options, args) = oparser.parse_args() - if not os.path.exists(options.dumpname): print options.dumpname, 'doesn\'t exists!' @@ -240,26 +306,41 @@ if options.arch != 'x86_32': print 'not supported architecture: ', options.arch return - if options.filetype != 'elf' and options.target == 'xen': - print 'xen extract support ELF core format only.' - - # filetype, file, arch + # input whole-machine dump dump = ElfCore.ElfCoreReader(options.dumpname, options.arch) + if options.list_domain: + list_domain(dump, options.xensyms) + return + if os.path.exists(options.outdumpname) and not (options.force): print options.outdumpname, 'already exists!' return else: file(options.outdumpname, 'w') # create blank file - outputFormat = typemap[(options.filetype, options.target)] + + + if options.target == 'x': + target = 'xen' + else: + target = 'domain' + + outputFormat = typemap[(options.filetype, target)] outdump = outputFormat(options.outdumpname, options.arch) - if options.target == 'dom0': - dom0extract(outdump, dump, options.xensyms, options.vmlinux) - elif options.target == 'xen': + if options.target == 'x': xenextract(outdump, dump, options.xensyms, options.vmlinux) - + else: + domid = int(options.target) + domextract(outdump, dump, options.xensyms, options.vmlinux, domid) + if __name__ == '__main__': + try: + import psyco + psyco.full() + except: + pass + main()