--- crash-4.0-8.11/main.c 2009-08-11 15:11:33.000000000 -0400 +++ crash-4.0-8.12/main.c 2009-07-16 09:33:09.000000000 -0400 @@ -405,6 +405,17 @@ pc->readmem = read_kdump; pc->writemem = write_kdump; + } else if (is_kvmdump(argv[optind])) { + if (pc->flags & MEMORY_SOURCES) { + error(INFO, + "too many dumpfile arguments\n"); + program_usage(SHORT_FORM); + } + pc->flags |= KVMDUMP; + pc->dumpfile = argv[optind]; + pc->readmem = read_kvmdump; + pc->writemem = write_kvmdump; + } else if (is_xendump(argv[optind])) { if (pc->flags & MEMORY_SOURCES) { error(INFO, @@ -964,8 +975,8 @@ sprintf(&buf[strlen(buf)], "%sIN_FOREACH", others++ ? "|" : ""); if (pc->flags & MFD_RDWR) sprintf(&buf[strlen(buf)], "%sMFD_RDWR", others++ ? "|" : ""); - if (pc->flags & DFD_RDWR) - sprintf(&buf[strlen(buf)], "%sDFD_RDWR", others++ ? "|" : ""); + if (pc->flags & KVMDUMP) + sprintf(&buf[strlen(buf)], "%sKVMDUMP", others++ ? "|" : ""); if (pc->flags & SILENT) sprintf(&buf[strlen(buf)], "%sSILENT", others++ ? "|" : ""); if (pc->flags & HASH) --- crash-4.0-8.11/tools.c 2009-08-11 15:11:34.000000000 -0400 +++ crash-4.0-8.12/tools.c 2009-08-07 09:08:37.000000000 -0400 @@ -2151,6 +2151,8 @@ pc->flags |= NETDUMP; else if (is_kdump(args[optind], KDUMP_LOCAL)) pc->flags |= KDUMP; + else if (is_kvmdump(args[optind])) + pc->flags |= KVMDUMP; else if (is_xendump(args[optind])) pc->flags |= XENDUMP; else if (is_diskdump(args[optind])) @@ -4344,6 +4346,25 @@ return total; } +int +highest_bit_long(ulong val) +{ + int i, cnt; + int total; + int highest; + + highest = -1; + cnt = sizeof(long) * 8; + + for (i = total = 0; i < cnt; i++) { + if (val & 1) + highest = i; + val >>= 1; + } + + return highest; +} + /* * Debug routine to stop whatever's going on in its tracks. */ @@ -4650,10 +4671,18 @@ void please_wait(char *s) { - if ((pc->flags & SILENT) || !(pc->flags & TTY) || - !DUMPFILE() || (pc->flags & RUNTIME)) + int fd; + + if ((pc->flags & SILENT) || !DUMPFILE() || (pc->flags & RUNTIME)) return; + if (!(pc->flags & TTY) && KVMDUMP_DUMPFILE()) { + if (!isatty(fileno(stdin)) || + ((fd = open("/dev/tty", O_RDONLY)) < 0)) + return; + close(fd); + } + pc->flags |= PLEASE_WAIT; fprintf(fp, "\rplease wait... (%s)", s); @@ -4663,8 +4692,7 @@ void please_wait_done(void) { - if ((pc->flags & SILENT) || !(pc->flags & TTY) || - !DUMPFILE() || (pc->flags & RUNTIME)) + if (!(pc->flags & PLEASE_WAIT)) return; pc->flags &= ~PLEASE_WAIT; --- crash-4.0-8.11/memory.c 2009-08-11 15:11:33.000000000 -0400 +++ crash-4.0-8.12/memory.c 2009-07-08 11:22:00.000000000 -0400 @@ -12421,6 +12421,7 @@ case DEVMEM: case MEMMOD: case CRASHBUILTIN: + case KVMDUMP: psz = (uint)getpagesize(); break; @@ -12630,6 +12631,8 @@ retval = kdump_memory_used(); else if (pc->flags & XENDUMP) retval = xendump_memory_used(); + else if (pc->flags & KVMDUMP) + retval = kvmdump_memory_used(); else if (pc->flags & DISKDUMP) retval = diskdump_memory_used(); else if (pc->flags & LKCD) @@ -12649,6 +12652,8 @@ retval = kdump_free_memory(); else if (pc->flags & XENDUMP) retval = xendump_free_memory(); + else if (pc->flags & KVMDUMP) + retval = kvmdump_free_memory(); else if (pc->flags & DISKDUMP) retval = diskdump_free_memory(); else if (pc->flags & LKCD) @@ -12668,6 +12673,8 @@ retval = kdump_memory_dump(fp); else if (pc->flags & XENDUMP) retval = xendump_memory_dump(fp); + else if (pc->flags & KVMDUMP) + retval = kvmdump_memory_dump(fp); else if (pc->flags & DISKDUMP) retval = diskdump_memory_dump(fp); else if (pc->flags & LKCD) --- crash-4.0-8.11/filesys.c 2009-08-11 15:11:34.000000000 -0400 +++ crash-4.0-8.12/filesys.c 2009-07-08 10:34:02.000000000 -0400 @@ -205,6 +205,10 @@ if (!xendump_init(pc->dumpfile, fp)) error(FATAL, "%s: initialization failed\n", pc->dumpfile); + } else if (pc->flags & KVMDUMP) { + if (!kvmdump_init(pc->dumpfile, fp)) + error(FATAL, "%s: initialization failed\n", + pc->dumpfile); } else if (pc->flags & DISKDUMP) { if (!diskdump_init(pc->dumpfile, fp)) error(FATAL, "%s: initialization failed\n", --- crash-4.0-8.11/task.c 2009-08-11 15:11:34.000000000 -0400 +++ crash-4.0-8.12/task.c 2009-07-08 11:11:12.000000000 -0400 @@ -488,10 +488,25 @@ thread_info_buf = GETBUF(SIZE(irq_ctx)); - i = get_array_length("hardirq_ctx", NULL, 0); - get_symbol_data("hardirq_ctx", - sizeof(long)*(i <= NR_CPUS ? i : NR_CPUS), - &tt->hardirq_ctx[0]); + if (symbol_exists("hardirq_ctx")) { + i = get_array_length("hardirq_ctx", NULL, 0); + get_symbol_data("hardirq_ctx", + sizeof(long)*(i <= NR_CPUS ? i : NR_CPUS), + &tt->hardirq_ctx[0]); + } else if (symbol_exists("per_cpu__hardirq_ctx")) { + if ((kt->flags & SMP) && (kt->flags & PER_CPU_OFF)) { + for (i = 0; i < NR_CPUS; i++) { + if (!kt->__per_cpu_offset[i]) + continue; + tt->hardirq_ctx[i] = + symbol_value("per_cpu__hardirq_ctx") + + kt->__per_cpu_offset[i]; + } + } else + tt->hardirq_ctx[0] = + symbol_value("per_cpu__hardirq_ctx"); + } else + error(WARNING, "cannot determine hardirq_ctx addresses\n"); for (i = 0; i < NR_CPUS; i++) { if (!(tt->hardirq_ctx[i])) @@ -509,10 +524,25 @@ ULONG(thread_info_buf+OFFSET(thread_info_task)); } - i = get_array_length("softirq_ctx", NULL, 0); - get_symbol_data("softirq_ctx", - sizeof(long)*(i <= NR_CPUS ? i : NR_CPUS), - &tt->softirq_ctx[0]); + if (symbol_exists("softirq_ctx")) { + i = get_array_length("softirq_ctx", NULL, 0); + get_symbol_data("softirq_ctx", + sizeof(long)*(i <= NR_CPUS ? i : NR_CPUS), + &tt->softirq_ctx[0]); + } else if (symbol_exists("per_cpu__softirq_ctx")) { + if ((kt->flags & SMP) && (kt->flags & PER_CPU_OFF)) { + for (i = 0; i < NR_CPUS; i++) { + if (!kt->__per_cpu_offset[i]) + continue; + tt->softirq_ctx[i] = + symbol_value("per_cpu__softirq_ctx") + + kt->__per_cpu_offset[i]; + } + } else + tt->softirq_ctx[0] = + symbol_value("per_cpu__softirq_ctx"); + } else + error(WARNING, "cannot determine softirq_ctx addresses\n"); for (i = 0; i < NR_CPUS; i++) { if (!(tt->softirq_ctx[i])) @@ -5499,6 +5529,10 @@ task = get_diskdump_panic_task(); if (task) return task; + } else if (KVMDUMP_DUMPFILE()) { + task = get_kvmdump_panic_task(); + if (task) + return task; } else if (XENDUMP_DUMPFILE()) { task = get_xendump_panic_task(); if (task) @@ -5557,7 +5591,7 @@ if (!found && !(kt->flags & SMP) && (LKCD_DUMPFILE() || NETDUMP_DUMPFILE() || - KDUMP_DUMPFILE() || DISKDUMP_DUMPFILE())) + KDUMP_DUMPFILE() || DISKDUMP_DUMPFILE() || KVMDUMP_DUMPFILE())) tt->panic_threads[0] = get_dumpfile_panic_task(); } --- crash-4.0-8.11/kernel.c 2009-08-11 15:11:34.000000000 -0400 +++ crash-4.0-8.12/kernel.c 2009-08-07 10:00:58.000000000 -0400 @@ -2178,6 +2178,8 @@ get_kdump_regs(bt, &eip, &esp); else if (DISKDUMP_DUMPFILE()) get_diskdump_regs(bt, &eip, &esp); + else if (KVMDUMP_DUMPFILE()) + get_kvmdump_regs(bt, &eip, &esp); else if (LKCD_DUMPFILE()) get_lkcd_regs(bt, &eip, &esp); else if (XENDUMP_DUMPFILE()) @@ -6090,6 +6092,43 @@ } /* + * If it exists, return the highest cpu number in the cpu_online_map. + */ +int +get_highest_cpu_online() +{ + int i, len; + char *buf; + ulong *maskptr, addr; + int high, highest; + + if (!(addr = cpu_map_addr("online"))) + return -1; + + len = cpu_map_size("online"); + buf = GETBUF(len); + highest = -1; + + if (readmem(addr, KVADDR, buf, len, + "cpu_online_map", RETURN_ON_ERROR)) { + + maskptr = (ulong *)buf; + for (i = 0; i < (len/sizeof(ulong)); i++, maskptr++) { + if ((high = highest_bit_long(*maskptr)) < 0) + continue; + highest = high + (i * (sizeof(ulong)*8)); + } + + if (CRASHDEBUG(1)) + error(INFO, "get_highest_cpu_online: %d\n", highest); + } + + FREEBUF(buf); + + return highest; +} + +/* * If it exists, return the number of cpus in the cpu_present_map. */ int --- crash-4.0-8.11/x86_64.c 2009-08-11 15:11:33.000000000 -0400 +++ crash-4.0-8.12/x86_64.c 2009-08-07 14:38:14.000000000 -0400 @@ -805,7 +805,7 @@ kt->flags |= SMP; if ((i = get_cpus_online()) && (i < cpus)) - kt->cpus = i; + kt->cpus = get_highest_cpu_online() + 1; else kt->cpus = cpus; @@ -4143,7 +4143,7 @@ } if ((i = get_cpus_online()) && (i < cpus)) - cpus = i; + cpus = get_highest_cpu_online() + 1; return cpus; } @@ -4995,6 +4995,16 @@ return; } + if (KVMDUMP_DUMPFILE()) { + if (kvmdump_phys_base(&phys_base)) { + machdep->machspec->phys_base = phys_base; + if (CRASHDEBUG(1)) + fprintf(fp, "kvmdump: phys_base: %lx\n", + phys_base); + } + return; + } + if ((vd = get_kdump_vmcore_data())) { for (i = 0; i < vd->num_pt_load_segments; i++) { phdr = vd->load64 + i; --- crash-4.0-8.11/symbols.c 2009-08-11 15:11:33.000000000 -0400 +++ crash-4.0-8.12/symbols.c 2009-07-16 12:04:55.000000000 -0400 @@ -2585,8 +2585,16 @@ switch (swap16(elf32->e_machine, swap)) { case EM_386: - if (machine_type_mismatch(file, "X86", NULL, 0)) + if (machine_type_mismatch(file, "X86", NULL, 0)) { + if (machine_type("X86_64")) { + /* + * Since is_bfd_format() returns TRUE + * in this case, just bail out here. + */ + return FALSE; + } goto bailout; + } break; case EM_S390: --- crash-4.0-8.11/lkcd_x86_trace.c 2009-08-11 15:11:34.000000000 -0400 +++ crash-4.0-8.12/lkcd_x86_trace.c 2009-07-16 14:03:27.000000000 -0400 @@ -1687,6 +1687,7 @@ if (func_name && XEN_HYPER_MODE()) { if (STREQ(func_name, "continue_nmi") || STREQ(func_name, "vmx_asm_vmexit_handler") || + STREQ(func_name, "common_interrupt") || STREQ(func_name, "handle_nmi_mce") || STREQ(func_name, "deferred_nmi")) { /* Interrupt frame */ --- crash-4.0-8.11/kvmdump.c 2009-08-11 16:24:00.000000000 -0400 +++ crash-4.0-8.12/kvmdump.c 2009-07-16 12:08:38.000000000 -0400 @@ -0,0 +1,279 @@ +/* + * kvmdump.c + * + * Copyright (C) 2009 David Anderson + * Copyright (C) 2009 Red Hat, Inc. All rights reserved. + * + * This software may be freely redistributed under the terms of the + * GNU General Public License. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "defs.h" +#include "kvmdump.h" + +static struct kvmdump_data kvmdump_data = { 0 }; +struct kvmdump_data *kvm = &kvmdump_data; +static int cache_page(physaddr_t); + +#define RAM_OFFSET_COMPRESSED (~(off_t)255) +#define QEMU_COMPRESSED ((WRITE_ERROR)-1) +#define CACHE_UNUSED (1ULL) + +int +is_kvmdump(char *filename) +{ + return (is_qemu_vm_file(filename)); +} + +int +kvmdump_init(char *filename, FILE *fptr) +{ + int i, page_size; + char *buf; + + if (!machine_type("X86") && !machine_type("X86_64")) { + error(FATAL, "invalid host architecture for KVM: %s\n", + MACHINE_TYPE); + return FALSE; + } + + kvm->ofp = fptr; + kvm->debug = &pc->debug; + page_size = memory_page_size(); + + if ((kvm->mem = tmpfile()) == NULL) + error(FATAL, + "cannot create tmpfile for KVM file offsets: %s\n", + strerror(errno)); + + if ((buf = calloc(1, KVMDUMP_CACHED_PAGES * page_size)) == NULL) + error(FATAL, "%s: cannot malloc KVM page_cache_buf\n"); + + for (i = 0; i < KVMDUMP_CACHED_PAGES; i++) { + kvm->page_cache[i].paddr = CACHE_UNUSED; + kvm->page_cache[i].bufptr = buf + (i * page_size); + } + + if (qemu_init(filename)) { + kvm->flags |= KVMDUMP_LOCAL; + return TRUE; + } else + return FALSE; +} + +int +read_kvmdump(int fd, void *bufptr, int cnt, ulong addr, physaddr_t paddr) +{ + switch (cache_page(PHYSPAGEBASE(paddr))) + { + case READ_ERROR: + return READ_ERROR; + + case SEEK_ERROR: + return SEEK_ERROR; + + case QEMU_COMPRESSED: + memset(bufptr, kvm->un.compressed, cnt); + break; + + default: + memcpy(bufptr, kvm->un.curbufptr + PAGEOFFSET(paddr), cnt); + break; + } + + return cnt; +} + + +int +write_kvmdump(int fd, void *bufptr, int cnt, ulong addr, physaddr_t paddr) +{ + return SEEK_ERROR; +} + +/* + * kvmdump_free_memory(), and kvmdump_memory_used() + * are debug only, and typically unnecessary to implement. + */ +int +kvmdump_free_memory(void) +{ + return 0; +} + +int +kvmdump_memory_used(void) +{ + return 0; +} + +/* + * This function is dump-type independent, used here to + * to dump the kvmdump_data structure contents. + */ +int +kvmdump_memory_dump(FILE *ofp) +{ + int i, others; + + fprintf(ofp, " flags: %lx (", kvm->flags); + others = 0; + if (kvm->flags & KVMDUMP_LOCAL) + fprintf(ofp, "%sKVMDUMP_LOCAL", others++ ? "|" : ""); + fprintf(ofp, ")\n"); + fprintf(ofp, " ofp: %lx\n", (ulong)kvm->ofp); + fprintf(ofp, " mem: %lx\n", (ulong)kvm->mem); + fprintf(ofp, " vmp: %lx\n", (ulong)kvm->vmp); + fprintf(ofp, " debug: %lx\n", (ulong)kvm->debug); + fprintf(ofp, " phys_base: %lx\n", kvm->phys_base); + fprintf(ofp, "last_ram_offset: %llx\n", (ulonglong)kvm->last_ram_offset); + + fprintf(ofp, " curbufptr: %lx\n", (ulong)kvm->un.curbufptr); + fprintf(ofp, " evict_index: %d\n", kvm->evict_index); + fprintf(ofp, " accesses: %ld\n", kvm->accesses); + fprintf(ofp, " hit_count: %ld ", kvm->hit_count); + if (kvm->accesses) + fprintf(ofp, "(%ld%%)\n", + kvm->hit_count * 100 / kvm->accesses); + else + fprintf(ofp, "\n"); + fprintf(ofp, " compresses: %ld ", kvm->compresses); + if (kvm->accesses) + fprintf(ofp, "(%ld%%)\n", + kvm->compresses * 100 / kvm->accesses); + else + fprintf(ofp, "\n"); + + for (i = 0; i < KVMDUMP_CACHED_PAGES; i++) { + if (kvm->page_cache[i].paddr == CACHE_UNUSED) + fprintf(ofp, " %spage_cache[%d]: CACHE_UNUSED\n", + i < 10 ? " " : "", i); + else + fprintf(ofp, + " %spage_cache[%d]: bufptr: %lx addr: %llx\n", + i < 10 ? " " : "", i, + (ulong)kvm->page_cache[i].bufptr, + (ulonglong)kvm->page_cache[i].paddr); + } + + fprintf(ofp, "\n"); + fflush(ofp); + + dump_qemu_header(ofp); + + return TRUE; +} + +void +get_kvmdump_regs(struct bt_info *bt, ulong *pc, ulong *sp) +{ + machdep->get_stack_frame(bt, pc, sp); +} + +ulong +get_kvmdump_panic_task(void) +{ + return NO_TASK; +} + +int +kvmdump_phys_base(unsigned long *phys_base) +{ + if (KVMDUMP_VALID()) { + *phys_base = kvm->phys_base; + return TRUE; + } + + return FALSE; +} + +static int +cache_page(physaddr_t paddr) +{ + int idx, err; + struct kvm_page_cache_hdr *pgc; + size_t page_size; + off_t offset; + + kvm->accesses++; + + for (idx = 0; idx < KVMDUMP_CACHED_PAGES; idx++) { + pgc = &kvm->page_cache[idx]; + + if (pgc->paddr == CACHE_UNUSED) + continue; + + if (pgc->paddr == paddr) { + kvm->hit_count++; + kvm->un.curbufptr = pgc->bufptr; + return idx; + } + } + + if ((err = load_memfile_offset(paddr, &offset)) < 0) + return err; + + if ((offset & RAM_OFFSET_COMPRESSED) == RAM_OFFSET_COMPRESSED) { + kvm->un.compressed = (unsigned char)(offset & 255); + kvm->compresses++; + return QEMU_COMPRESSED; + } + + idx = kvm->evict_index; + pgc = &kvm->page_cache[idx]; + page_size = memory_page_size(); + + if (fseek(kvm->vmp, offset, SEEK_SET) < 0) { + pgc->paddr = CACHE_UNUSED; + return SEEK_ERROR; + } + if (fread(pgc->bufptr, page_size, 1, kvm->vmp) != 1) { + pgc->paddr = CACHE_UNUSED; + return READ_ERROR; + } + + kvm->evict_index = (idx+1) % KVMDUMP_CACHED_PAGES; + + pgc->paddr = paddr; + kvm->un.curbufptr = pgc->bufptr; + + return idx; +} + +int +store_memfile_offset(uint64_t physaddr, off_t *entry_ptr) +{ + if (fseek(kvm->mem, MEMFILE_OFFSET(physaddr), SEEK_SET) < 0) { + error(INFO, "%s: fseek: %s\n", pc->dumpfile, strerror(errno)); + return SEEK_ERROR; + } + + if (fwrite((entry_ptr), sizeof(off_t), 1, kvm->mem) != 1) { + error(INFO, "%s: fwrite: %s\n", pc->dumpfile, strerror(errno)); + return WRITE_ERROR; + } + + return 0; +} + +int +load_memfile_offset(uint64_t physaddr, off_t *entry_ptr) +{ + if (fseek(kvm->mem, MEMFILE_OFFSET(physaddr), SEEK_SET) < 0) { + error(INFO, "%s: fseek: %s\n", pc->dumpfile, strerror(errno)); + return SEEK_ERROR; + } + + if (fread((entry_ptr), sizeof(off_t), 1, kvm->mem) != 1) { + error(INFO, "%s: fwrite: %s\n", pc->dumpfile, strerror(errno)); + return READ_ERROR; + } + + return 0; +} + + --- crash-4.0-8.11/qemu.c 2009-08-11 16:24:00.000000000 -0400 +++ crash-4.0-8.12/qemu.c 2009-07-16 11:54:47.000000000 -0400 @@ -0,0 +1,284 @@ +/* + * Derive kernel base from a QEMU saved VM file + * + * Copyright (C) 2009 Red Hat, Inc. + * Written by Paolo Bonzini. + * + * Portions Copyright (C) 2009 David Anderson + */ + +#include +#include +#include +#include + +#include "qemu-load.h" + +#include "kvmdump.h" + +/* + * Some bits we need to access in the control registers and page tables. + */ + +#define MSR_EFER_LMA (1 << 10) +#define PG_PRESENT_MASK (1 << 0) +#define PG_PSE_MASK (1 << 7) +#define CR0_PG_MASK (1 << 31) +#define CR4_PAE_MASK (1 << 31) +#define CR4_PSE_MASK (1 << 31) + +static uint32_t +ldl (struct qemu_device_x86 *dx86, struct qemu_device_ram *dram, uint64_t addr) +{ + char buf[4096]; + if (dx86->a20_masked) + addr &= ~(1LL<<20); + if (!ram_read_phys_page (dram, buf, addr & ~0xfff)) + return 0; + + assert ((addr & 0xfff) <= 0xffc); + return *(uint32_t *)(buf + (addr & 0xfff)); +} + +static uint64_t +ldq (struct qemu_device_x86 *dx86, struct qemu_device_ram *dram, uint64_t addr) +{ + char buf[4096]; + if (dx86->a20_masked) + addr &= ~(1LL<<20); + if (!ram_read_phys_page (dram, buf, addr & ~0xfff)) + return 0; + + assert ((addr & 0xfff) <= 0xff8); + return *(uint64_t *)(buf + (addr & 0xfff)); +} + +/* + * Messy x86 TLB fault logic, walking the page tables to find the physical + * address corresponding to ADDR. Taken from QEMU. + */ + +static uint64_t +get_phys_page(struct qemu_device_x86 *dx86, struct qemu_device_ram *dram, + uint64_t addr) +{ + uint64_t pde_addr, pte_addr; + uint64_t pte, paddr; + uint32_t page_offset; + int page_size; + + if ((dx86->cr4 & CR4_PAE_MASK) || (dx86->efer & MSR_EFER_LMA)) { + uint64_t pdpe_addr; + uint64_t pde, pdpe; + + if (dx86->cr4 & CR4_PAE_MASK) + dprintf ("PAE active\n"); + if (dx86->efer & MSR_EFER_LMA) { + uint64_t pml4e_addr, pml4e; + int32_t sext; + + dprintf ("long mode active\n"); + + /* test virtual address sign extension */ + sext = (int64_t) addr >> 47; + if (sext != 0 && sext != -1) + return -1; + + pml4e_addr = ((dx86->cr3 & ~0xfff) + + (((addr >> 39) & 0x1ff) << 3)); + pml4e = ldq (dx86, dram, pml4e_addr); + if (!(pml4e & PG_PRESENT_MASK)) + return -1; + dprintf ("PML4 page present\n"); + + pdpe_addr = ((pml4e & ~0xfff) + + (((addr >> 30) & 0x1ff) << 3)); + pdpe = ldq (dx86, dram, pdpe_addr); + if (!(pdpe & PG_PRESENT_MASK)) + return -1; + dprintf ("PDPE page present\n"); + } else { + dprintf ("long mode inactive\n"); + + pdpe_addr = ((dx86->cr3 & ~0x1f) + + ((addr >> 27) & 0x18)); + pdpe = ldq (dx86, dram, pdpe_addr); + if (!(pdpe & PG_PRESENT_MASK)) + return -1; + dprintf ("PDPE page present\n"); + } + + pde_addr = (pdpe & ~0xfff) + (((addr >> 21) & 0x1ff) << 3); + pde = ldq (dx86, dram, pde_addr); + if (!(pde & PG_PRESENT_MASK)) + return -1; + dprintf ("PDE page present\n"); + + if (pde & PG_PSE_MASK) { + /* 2 MB page */ + dprintf ("2MB page\n"); + + page_size = 2048 * 1024; + pte = pde & ~((page_size - 1) & ~0xfff); + } else { + /* 4 KB page */ + dprintf ("4 KB PAE page\n"); + + pte_addr = ((pde & ~0xfff) + + (((addr >> 12) & 0x1ff) << 3)); + page_size = 4096; + pte = ldq (dx86, dram, pte_addr); + if (!(pte & PG_PRESENT_MASK)) + return -1; + dprintf ("PTE page present\n"); + } + + } else { + /* Not PAE. */ + + uint32_t pde; + if (!(dx86->cr0 & CR0_PG_MASK)) { + dprintf ("Paging inactive\n"); + + pte = addr; + page_size = 4096; + } else { + /* page directory entry */ + pde_addr = ((dx86->cr3 & ~0xfff) + + ((addr >> 20) & 0xffc)); + pde = ldl (dx86, dram, pde_addr); + if (!(pde & PG_PRESENT_MASK)) + return -1; + dprintf ("PDE page present\n"); + if ((pde & PG_PSE_MASK) && (dx86->cr4 & CR4_PSE_MASK)) { + page_size = 4096 * 1024; + pte = pde & ~((page_size - 1) & ~0xfff); + } else { + page_size = 4096; + pte_addr = ((pde & ~0xfff) + + ((addr >> 10) & 0xffc)); + pte = ldl (dx86, dram, pte_addr); + if (!(pte & PG_PRESENT_MASK)) + return -1; + dprintf ("PTE page present\n"); + } + } + } + + page_offset = (addr & 0xfff) & (page_size - 1); + paddr = (pte & ~0xfff) + page_offset; + return paddr; +} + +/* + * I'm using the IDT base as a quick way to find the bottom of the + * kernel memory. + */ +static uint64_t +get_idt_base(struct qemu_device_list *dl) +{ + struct qemu_device_x86 *dx86 = (struct qemu_device_x86 *) + device_find_instance (dl, "cpu", 0); + + return dx86->idt.base; +} + +static uint64_t +get_kernel_base(struct qemu_device_list *dl) +{ + struct qemu_device_x86 *dx86 = (struct qemu_device_x86 *) + device_find_instance (dl, "cpu", 0); + struct qemu_device_ram *dram = (struct qemu_device_ram *) + device_find_instance (dl, "ram", 0); + + uint64_t base_vaddr = dx86->idt.base & ~((1LL << 30) - 1); + dprintf ("Virtual base address guessed at %llx\n", (unsigned long long)base_vaddr); + return get_phys_page(dx86, dram, base_vaddr); +} + + +#ifdef MAIN_FROM_TEST_C +int main (int argc, char **argv) +{ + struct qemu_device_list *dl; + FILE *fp; + + if (argc != 2) { + fprintf (stderr, "Usage: test SAVE-FILE\n"); + exit (1); + } + + fp = fopen(argv[1], "r"); + if (!fp) { + fprintf (stderr, "Error: %s\n", strerror (errno)); + exit (1); + } + +#ifdef HOST_32BIT + dl = qemu_load (devices_x86_32, QEMU_FEATURE_CPU|QEMU_FEATURE_RAM, fp); +#else + dl = qemu_load (devices_x86_64, QEMU_FEATURE_CPU|QEMU_FEATURE_RAM, fp); +#endif + printf ("IDT at %llx\n", get_idt_base (dl)); + printf ("Physical kernel base at %llx\n", get_kernel_base (dl)); + device_list_free (dl); + fclose (fp); + exit (0); +} +#endif + + +/* + * crash utility adaptation + */ + +#include "defs.h" + +int +qemu_init(char *filename) +{ + struct qemu_device_list *dl; + struct qemu_device_ram *dram; + uint64_t idt; + + if (CRASHDEBUG(1)) + dump_qemu_header(kvm->ofp); + + rewind(kvm->vmp); + + please_wait("scanning KVM dumpfile"); + + if (machine_type("X86")) + dl = qemu_load(devices_x86_32, + QEMU_FEATURE_CPU|QEMU_FEATURE_RAM, kvm->vmp); + else if (machine_type("X86_64")) + dl = qemu_load(devices_x86_64, + QEMU_FEATURE_CPU|QEMU_FEATURE_RAM, kvm->vmp); + + please_wait_done(); + + if (dl) { + /* + * TBD: the IDT base is a kernel virtual address -- verify here + * that it makes sense for the host machine type. + */ + idt = get_idt_base(dl); + kvm->phys_base = get_kernel_base(dl); + dram = (struct qemu_device_ram *) + device_find_instance (dl, "ram", 0); + kvm->last_ram_offset = dram->last_ram_offset; + + if (CRASHDEBUG(1)) { + fprintf(kvm->ofp, "IDT: %llx\n", (ulonglong)idt); + fprintf(kvm->ofp, "physical kernel base: %lx\n", + kvm->phys_base); + fprintf(kvm->ofp, "last RAM offset: %llx\n", + (ulonglong)kvm->last_ram_offset); + } + + device_list_free (dl); + } else + fclose(kvm->vmp); + + return dl ? TRUE : FALSE; +} --- crash-4.0-8.11/qemu-load.c 2009-08-11 16:24:00.000000000 -0400 +++ crash-4.0-8.12/qemu-load.c 2009-07-16 15:28:40.000000000 -0400 @@ -0,0 +1,782 @@ +/* + * Qemu save VM loader + * + * Copyright (C) 2009 Red Hat, Inc. + * Written by Paolo Bonzini. + * + * Portions Copyright (C) 2009 David Anderson + */ + +#define _GNU_SOURCE +#include "qemu-load.h" +#include +#include +#include +#include + +#include "kvmdump.h" + +struct qemu_device * +device_alloc (struct qemu_device_list *dl, size_t sz, + struct qemu_device_vtbl *vtbl, uint32_t section_id, uint32_t instance_id) +{ + struct qemu_device *d = calloc (1, sz); + d->vtbl = vtbl; + d->list = dl; + d->section_id = section_id; + d->instance_id = instance_id; + + if (!dl->head) + dl->head = dl->tail = d; + else { + dl->tail->next = d; + d->prev = dl->tail; + } + return d; +} + +struct qemu_device * +device_find (struct qemu_device_list *dl, uint32_t section_id) +{ + struct qemu_device *d; + d = dl->head; + while (d && d->section_id != section_id) + d = d->next; + + return d; +} + +struct qemu_device * +device_find_instance (struct qemu_device_list *dl, const char *name, + uint32_t instance_id) +{ + struct qemu_device *d; + d = dl->head; + while (d && (strcmp (d->vtbl->name, name) || d->instance_id != instance_id)) + d = d->next; + + return d; +} + +void +device_free (struct qemu_device *d) +{ + struct qemu_device_list *dl = d->list; + if (d->prev) + d->prev->next = d->next; + else + dl->head = d->next; + if (d->next) + d->next->prev = d->prev; + else + dl->tail = d->prev; + + d->prev = d->next = NULL; + if (d->vtbl->free) + d->vtbl->free (d, dl); +} + +void +device_list_free (struct qemu_device_list *l) +{ + if (!l) + return; + + while (l->head) + device_free (l->head); +} + + +/* File access. */ + +static inline uint16_t +get_be16 (FILE *fp) +{ + uint8_t a = getc (fp); + uint8_t b = getc (fp); + return (a << 8) | b; +} + +static inline uint16_t +get_le16 (FILE *fp) +{ + uint8_t b = getc (fp); + uint8_t a = getc (fp); + return (a << 8) | b; +} + +static inline uint32_t +get_be32 (FILE *fp) +{ + uint16_t a = get_be16 (fp); + uint16_t b = get_be16 (fp); + return (a << 16) | b; +} + +static inline uint32_t +get_le32 (FILE *fp) +{ + uint16_t b = get_le16 (fp); + uint16_t a = get_le16 (fp); + return (a << 16) | b; +} + +static inline uint64_t +get_be64 (FILE *fp) +{ + uint32_t a = get_be32 (fp); + uint32_t b = get_be32 (fp); + return ((uint64_t)a << 32) | b; +} + +static inline uint64_t +get_le64 (FILE *fp) +{ + uint32_t b = get_le32 (fp); + uint32_t a = get_le32 (fp); + return ((uint64_t)a << 32) | b; +} + +static inline void +get_qemu128 (FILE *fp, union qemu_uint128_t *result) +{ + result->i[1] = get_le32 (fp); + result->i[0] = get_le32 (fp); + result->i[3] = get_le32 (fp); + result->i[2] = get_le32 (fp); +} + + + + +/* RAM loader. */ + +#define RAM_SAVE_FLAG_FULL 0x01 +#define RAM_SAVE_FLAG_COMPRESS 0x02 +#define RAM_SAVE_FLAG_MEM_SIZE 0x04 +#define RAM_SAVE_FLAG_PAGE 0x08 +#define RAM_SAVE_FLAG_EOS 0x10 +#define RAM_SAVE_ADDR_MASK (~4095LL) + +#define RAM_OFFSET_COMPRESSED (~(off_t)255) + +static void +ram_alloc (struct qemu_device_ram *dram, uint64_t size) +{ +// size_t old_npages = dram->offsets ? 0 : dram->last_ram_offset / 4096; +// size_t new_npages = size / 4096; +// assert (size <= SIZE_MAX); +// if (dram->offsets) +// dram->offsets = realloc (dram->offsets, +// new_npages * sizeof (off_t)); +// else +// dram->offsets = malloc (new_npages * sizeof (off_t)); +// +// assert (dram->offsets); +// while (old_npages < new_npages) +// dram->offsets[old_npages++] = RAM_OFFSET_COMPRESSED | 0; + + dram->last_ram_offset = size; +} + +static uint32_t +ram_load (struct qemu_device *d, FILE *fp, enum qemu_save_section sec) +{ + struct qemu_device_ram *dram = (struct qemu_device_ram *)d; + uint64_t header; + + do { + uint64_t addr; + off_t entry; + + header = get_be64 (fp); + assert (!(header & RAM_SAVE_FLAG_FULL)); + + addr = header & RAM_SAVE_ADDR_MASK; + if (header & RAM_SAVE_FLAG_MEM_SIZE) + ram_alloc (dram, addr); + + else if (header & RAM_SAVE_FLAG_COMPRESS) { +// dram->offsets[addr / 4096] = + entry = RAM_OFFSET_COMPRESSED | getc(fp); + store_memfile_offset(addr, &entry); + } + else if (header & RAM_SAVE_FLAG_PAGE) { +// dram->offsets[addr / 4096] = ftell (fp); + entry = ftell(fp); + store_memfile_offset(addr, &entry); + fseek (fp, 4096, SEEK_CUR); + } + + } while (!(header & RAM_SAVE_FLAG_EOS) && !feof (fp) && !ferror (fp)); + + dram->fp = fp; + return QEMU_FEATURE_RAM; +} + +static void +ram_free (struct qemu_device *d, struct qemu_device_list *dl) +{ + struct qemu_device_ram *dram = (struct qemu_device_ram *)d; + free (dram->offsets); +} + +int +ram_read_phys_page (struct qemu_device_ram *dram, void *buf, uint64_t addr) +{ + off_t ofs; + if (addr >= dram->last_ram_offset) + return false; + assert ((addr & 0xfff) == 0); +// ofs = dram->offsets[addr / 4096]; + if (load_memfile_offset(addr, &ofs) < 0) + return 0; + if ((ofs & RAM_OFFSET_COMPRESSED) == RAM_OFFSET_COMPRESSED) + memset (buf, ofs & 255, 4096); + else + pread (fileno (dram->fp), buf, 4096, ofs); + return true; +} + +static struct qemu_device * +ram_init_load (struct qemu_device_list *dl, + uint32_t section_id, uint32_t instance_id, + uint32_t version_id, bool live, FILE *fp) +{ + static struct qemu_device_vtbl ram = { + "ram", + ram_load, + ram_free + }; + + assert (version_id == 3); + return device_alloc (dl, sizeof (struct qemu_device_ram), + &ram, section_id, instance_id); +} + +/* cpu_common loader. */ + +struct qemu_device_cpu_common { + struct qemu_device base; + uint32_t halted; + uint32_t irq; +}; + +static uint32_t +cpu_common_load (struct qemu_device *d, FILE *fp, enum qemu_save_section sec) +{ + struct qemu_device_cpu_common *cpu = (struct qemu_device_cpu_common *)d; + cpu->halted = get_be32 (fp); + cpu->irq = get_be32 (fp); + return 0; +} + +static struct qemu_device * +cpu_common_init_load (struct qemu_device_list *dl, + uint32_t section_id, uint32_t instance_id, + uint32_t version_id, bool live, FILE *fp) +{ + static struct qemu_device_vtbl cpu_common = { + "cpu_common", + cpu_common_load, + NULL + }; + + assert (!live); + return device_alloc (dl, sizeof (struct qemu_device_cpu_common), + &cpu_common, section_id, instance_id); +} + + + +/* CPU loader. */ + +static inline int +get_be_long (FILE *fp, int size) +{ + uint32_t a = size == 32 ? 0 : get_be32 (fp); + uint32_t b = get_be32 (fp); + return ((uint64_t)a << 32) | b; +} + +static inline void +get_be_fp80 (FILE *fp, union qemu_fpu_reg *result) +{ + result->mmx = get_be64 (fp); + result->bytes[9] = getc (fp); + result->bytes[8] = getc (fp); +} + +static void +cpu_load_seg (FILE *fp, struct qemu_x86_seg *seg, int size) +{ + seg->selector = get_be32 (fp); + seg->base = get_be_long (fp, size); + seg->limit = get_be32 (fp); + seg->flags = get_be32 (fp); +} + +static uint32_t +cpu_load (struct qemu_device *d, FILE *fp, int size) +{ + struct qemu_device_x86 *dx86 = (struct qemu_device_x86 *)d; + uint32_t qemu_hflags = 0, qemu_hflags2 = 0; + int nregs = size == 32 ? 8 : 16; + uint32_t version_id = dx86->version_id; + int i; + + struct qemu_device_cpu_common *dcpu; + dcpu = (struct qemu_device_cpu_common *) + device_find_instance (d->list, "cpu_common", d->instance_id); + if (dcpu) { + dx86->halted = dcpu->halted; + dx86->irq = dcpu->irq; + device_free ((struct qemu_device *) dcpu); + } + + for (i = 0; i < nregs; i++) + dx86->regs[i] = get_be_long (fp, size); + + dx86->eip = get_be_long (fp, size); + dx86->eflags = get_be_long (fp, size); + qemu_hflags = get_be32 (fp); + dx86->fpucw = get_be16 (fp); + dx86->fpusw = get_be16 (fp); + dx86->fpu_free = get_be16 (fp); + + if (get_be16 (fp)) + for (i = 0; i < 8; i++) + dx86->st[i].mmx = get_be64 (fp); + else + for (i = 0; i < 8; i++) + get_be_fp80 (fp, &dx86->st[i]); + + cpu_load_seg (fp, &dx86->es, size); + cpu_load_seg (fp, &dx86->cs, size); + cpu_load_seg (fp, &dx86->ss, size); + cpu_load_seg (fp, &dx86->ds, size); + cpu_load_seg (fp, &dx86->fs, size); + cpu_load_seg (fp, &dx86->gs, size); + cpu_load_seg (fp, &dx86->ldt, size); + cpu_load_seg (fp, &dx86->tr, size); + cpu_load_seg (fp, &dx86->gdt, size); + cpu_load_seg (fp, &dx86->idt, size); + + dx86->sysenter.cs = get_be32 (fp); + dx86->sysenter.esp = get_be_long (fp, version_id <= 6 ? 32 : size); + dx86->sysenter.eip = get_be_long (fp, version_id <= 6 ? 32 : size); + + dx86->cr0 = get_be_long (fp, size); + dx86->cr2 = get_be_long (fp, size); + dx86->cr3 = get_be_long (fp, size); + dx86->cr4 = get_be_long (fp, size); + for (i = 0; i < 8; i++) + dx86->dr[i] = get_be_long (fp, size); + + dx86->a20_masked = get_be32 (fp) != 0xffffffff; + dx86->mxcsr = get_be32 (fp); + + for (i = 0; i < nregs; i++) + get_qemu128 (fp, &dx86->xmm[i]); + + if (size == 64) { + dx86->efer = get_be64 (fp); + dx86->star = get_be64 (fp); + dx86->lstar = get_be64 (fp); + dx86->cstar = get_be64 (fp); + dx86->fmask = get_be64 (fp); + dx86->kernel_gs_base = get_be64 (fp); + } + + dx86->smbase = get_be32 (fp); + + dx86->soft_mmu = qemu_hflags & (1 << 2); + dx86->smm = qemu_hflags & (1 << 19); + + if (version_id == 4) + return QEMU_FEATURE_CPU; + + dx86->pat = get_be64 (fp); + qemu_hflags2 = get_be32 (fp); + dx86->global_if = qemu_hflags2 & (1 << 0); + dx86->in_nmi = qemu_hflags2 & (1 << 2); + + if (version_id < 6) + dx86->halted = get_be32 (fp); + + dx86->svm.hsave = get_be64 (fp); + dx86->svm.vmcb = get_be64 (fp); + dx86->svm.tsc_offset = get_be64 (fp); + dx86->svm.in_vmm = qemu_hflags & (1 << 21); + dx86->svm.guest_if_mask = qemu_hflags2 & (1 << 1); + dx86->svm.guest_intr_masking = qemu_hflags2 & (1 << 3); + dx86->svm.intercept_mask = get_be64 (fp); + dx86->svm.cr_read_mask = get_be16 (fp); + dx86->svm.cr_write_mask = get_be16 (fp); + dx86->svm.dr_read_mask = get_be16 (fp); + dx86->svm.dr_write_mask = get_be16 (fp); + dx86->svm.exception_intercept_mask = get_be32 (fp); + dx86->cr8 = getc (fp); + + if (version_id >= 8) { + for (i = 0; i < 12; i++) + dx86->fixed_mtrr[i] = get_be64 (fp); + dx86->deftype_mtrr = get_be64 (fp); + for (i = 0; i < 8; i++) { + dx86->variable_mtrr[i].base = get_be64 (fp); + dx86->variable_mtrr[i].mask = get_be64 (fp); + } + } + + /* This was present only when KVM was enabled up to v8. + * Furthermore, it changed format in v9. */ + if (version_id >= 9) { + int32_t pending_irq = (int32_t) get_be32 (fp); + if (pending_irq >= 0) + dx86->kvm.int_bitmap[pending_irq / 64] |= + (uint64_t)1 << (pending_irq & 63); + + dx86->kvm.mp_state = get_be32 (fp); + dx86->kvm.tsc = get_be64 (fp); + } + + else if (d->list->features & QEMU_FEATURE_KVM) { + for (i = 0; i < 4; i++) + dx86->kvm.int_bitmap[i] = get_be64 (fp); + dx86->kvm.tsc = get_be64 (fp); + if (version_id >= 5) + dx86->kvm.mp_state = get_be32 (fp); + } + return QEMU_FEATURE_CPU; +} + +static uint32_t +cpu_load_32 (struct qemu_device *d, FILE *fp, enum qemu_save_section sec) +{ + return cpu_load (d, fp, 32); +} + +static struct qemu_device * +cpu_init_load_32 (struct qemu_device_list *dl, + uint32_t section_id, uint32_t instance_id, + uint32_t version_id, bool live, FILE *fp) +{ + struct qemu_device_x86 *dx86; + static struct qemu_device_vtbl cpu = { + "cpu", + cpu_load_32, + NULL + }; + + assert (!live); + assert (version_id >= 4 && version_id <= 9); + dx86 = (struct qemu_device_x86 *) + device_alloc (dl, sizeof (struct qemu_device_x86), + &cpu, section_id, instance_id); + dx86->version_id = version_id; + return (struct qemu_device *) dx86; +} + +static uint32_t +cpu_load_64 (struct qemu_device *d, FILE *fp, enum qemu_save_section sec) +{ + return cpu_load (d, fp, 64); +} + +static struct qemu_device * +cpu_init_load_64 (struct qemu_device_list *dl, + uint32_t section_id, uint32_t instance_id, + uint32_t version_id, bool live, FILE *fp) +{ + struct qemu_device_x86 *dx86; + static struct qemu_device_vtbl cpu = { + "cpu", + cpu_load_64, + NULL + }; + + assert (!live); + assert (version_id >= 4 && version_id <= 9); + dx86 = (struct qemu_device_x86 *) + device_alloc (dl, sizeof (struct qemu_device_x86), + &cpu, section_id, instance_id); + dx86->version_id = version_id; + return (struct qemu_device *) dx86; +} + +/* timer loader. */ + +static uint32_t +timer_load (struct qemu_device *d, FILE *fp, enum qemu_save_section sec) +{ + fseek (fp, 24, SEEK_CUR); + return QEMU_FEATURE_TIMER; +} + +static struct qemu_device * +timer_init_load (struct qemu_device_list *dl, + uint32_t section_id, uint32_t instance_id, + uint32_t version_id, bool live, FILE *fp) +{ + static struct qemu_device_vtbl timer = { + "timer", + timer_load, + NULL + }; + + assert (!live); + return device_alloc (dl, sizeof (struct qemu_device), + &timer, section_id, instance_id); +} + + +/* kvm-tpr-opt loader. */ + +static uint32_t +kvm_tpr_opt_load (struct qemu_device *d, FILE *fp, enum qemu_save_section sec) +{ + fseek (fp, 144, SEEK_CUR); + return QEMU_FEATURE_KVM; +} + +static struct qemu_device * +kvm_tpr_opt_init_load (struct qemu_device_list *dl, + uint32_t section_id, uint32_t instance_id, + uint32_t version_id, bool live, FILE *fp) +{ + static struct qemu_device_vtbl kvm_tpr_opt = { + "kvm-tpr-opt", + kvm_tpr_opt_load, + NULL + }; + + assert (!live); + return device_alloc (dl, sizeof (struct qemu_device), + &kvm_tpr_opt, section_id, instance_id); +} + + +/* Putting it together. */ + +const struct qemu_device_loader devices_x86_64[] = { + { "cpu_common", cpu_common_init_load }, + { "kvm-tpr-opt", kvm_tpr_opt_init_load }, + { "cpu", cpu_init_load_64 }, + { "ram", ram_init_load }, + { "timer", timer_init_load }, + { NULL } +}; + +const struct qemu_device_loader devices_x86_32[] = { + { "cpu_common", cpu_common_init_load }, + { "kvm-tpr-opt", kvm_tpr_opt_init_load }, + { "cpu", cpu_init_load_32 }, + { "ram", ram_init_load }, + { "timer", timer_init_load }, + { NULL } +}; + + +#define QEMU_VM_FILE_MAGIC 0x5145564D +#define LIBVIRT_QEMU_VM_FILE_MAGIC 0x4c696276 + +struct libvirt_header { + char magic[16]; + uint32_t version; + uint32_t xml_length; + uint32_t was_running; + uint32_t padding[16]; +}; + +static struct qemu_device * +device_get (const struct qemu_device_loader *devices, + struct qemu_device_list *dl, enum qemu_save_section sec, FILE *fp) +{ + char name[257]; + uint32_t section_id, instance_id, version_id; +// bool live; + int sz; + + section_id = get_be32 (fp); + if (sec != QEMU_VM_SECTION_START && + sec != QEMU_VM_SECTION_FULL) + return device_find (dl, section_id); + + sz = getc (fp); + if (sz == EOF) + return NULL; + fread (name, sz, 1, fp); + name[sz] = 0; + + instance_id = get_be32 (fp); + version_id = get_be32 (fp); + + while (devices->name && strcmp (devices->name, name)) + devices++; + if (!devices->name) + return NULL; + + return devices->init_load (dl, section_id, instance_id, version_id, + sec == QEMU_VM_SECTION_START, fp); +} + +struct qemu_device_list * +qemu_load (const struct qemu_device_loader *devices, uint32_t required_features, + FILE *fp) +{ + struct qemu_device_list *result = NULL; + + switch (get_be32 (fp)) { + case QEMU_VM_FILE_MAGIC: + break; + + case LIBVIRT_QEMU_VM_FILE_MAGIC: { + struct libvirt_header header; + memcpy (header.magic, "Libv", 4); + fread (&header.magic[4], sizeof (header) - 4, 1, fp); + if (memcmp ("LibvirtQemudSave", header.magic, 16)) + goto fail; + + fseek (fp, header.xml_length, SEEK_CUR); + if (get_be32 (fp) != QEMU_VM_FILE_MAGIC) + goto fail; + break; + } + + default: + goto fail; + } + + if (get_be32 (fp) != 3) + return NULL; + + result = calloc (1, sizeof (struct qemu_device_list)); + for (;;) { + struct qemu_device *d; + uint32_t features; + enum qemu_save_section sec = getc (fp); + + if (feof (fp) || ferror (fp)) + break; + if (sec == QEMU_VM_EOF) + break; + + d = device_get (devices, result, sec, fp); + if (!d) + break; + + dprintf("qemu_load: \"%s\"\n", d->vtbl->name); + + features = d->vtbl->load (d, fp, sec); + if (feof (fp) || ferror (fp)) + break; + + if (sec == QEMU_VM_SECTION_END || sec == QEMU_VM_SECTION_FULL) + result->features |= features; + } + + if (ferror (fp) || + (result->features & required_features) != required_features) + goto fail; + + return result; + +fail: + device_list_free (result); + free (result); + return NULL; +} + +/* + * crash utility adaptation. + */ + +#include "defs.h" + +int +is_qemu_vm_file(char *filename) +{ + struct libvirt_header header; + int retval; + char *xml; + + if ((kvm->vmp = fopen(filename, "r")) == NULL) { + error(INFO, "%s: %s\n", filename, strerror(errno)); + return FALSE; + } + + xml = NULL; + + switch (get_be32(kvm->vmp)) + { + case QEMU_VM_FILE_MAGIC: + retval = TRUE; + break; + + case LIBVIRT_QEMU_VM_FILE_MAGIC: + rewind(kvm->vmp); + fread(&header.magic[0], sizeof(header), 1, kvm->vmp); + if (STRNEQ(header.magic, "LibvirtQemudSave")) { + if ((xml = (char *)malloc(header.xml_length))) { + fread(xml, header.xml_length, 1, kvm->vmp); + /* + * Parse here if necessary or desirable. + */ + } else + fseek(kvm->vmp, header.xml_length, SEEK_CUR); + + if (get_be32(kvm->vmp) == QEMU_VM_FILE_MAGIC) + retval = TRUE; + } + break; + + default: + retval = FALSE; + } + + if (xml) + free(xml); + if (retval == FALSE) + fclose(kvm->vmp); + + return retval; +} + +void +dump_qemu_header(FILE *out) +{ + int i; + struct libvirt_header header; + char magic[4]; + uint8_t c; + + rewind(kvm->vmp); + if (get_be32(kvm->vmp) == QEMU_VM_FILE_MAGIC) { + fprintf(out, "%s: QEMU_VM_FILE_MAGIC\n", pc->dumpfile); + return; + } + + rewind(kvm->vmp); + fread(&header, sizeof(header), 1, kvm->vmp); + + fprintf(out, "%s: libvirt_header:\n\n", pc->dumpfile); + fprintf(out, " magic: "); + for (i = 0; i < 16; i++) + fprintf(out, "%c", header.magic[i]); + fprintf(out, "\n"); + fprintf(out, " version: %d\n", header.version); + fprintf(out, " xml_length: %d\n", header.xml_length); + fprintf(out, " was_running: %d\n", header.was_running); + fprintf(out, " padding: (not shown)\n\n"); + for (i = 0; i < header.xml_length; i++) { + c = getc(kvm->vmp); + if (c) + fprintf(out, "%c", c); + } + fprintf(out, "\n"); + fread(&magic, sizeof(char), 4, kvm->vmp); + for (i = 0; i < 4; i++) + fprintf(out, "%c", magic[i]); + fprintf(out, "\n"); +} + --- crash-4.0-8.11/defs.h 2009-08-11 15:11:34.000000000 -0400 +++ crash-4.0-8.12/defs.h 2009-08-10 15:47:48.000000000 -0400 @@ -64,7 +64,7 @@ #define NR_CPUS (256) #endif #ifdef X86_64 -#define NR_CPUS (512) +#define NR_CPUS (4096) #endif #ifdef ALPHA #define NR_CPUS (64) @@ -133,7 +133,7 @@ #define MCLXCD (0x10ULL) #define CMDLINE_IFILE (0x20ULL) #define MFD_RDWR (0x40ULL) -#define DFD_RDWR (0x80ULL) +#define KVMDUMP (0x80ULL) #define SILENT (0x100ULL) #define REMOTE_DAEMON (0x200ULL) #define HASH (0x400ULL) @@ -191,8 +191,8 @@ #define ACTIVE() (pc->flags & LIVE_SYSTEM) #define DUMPFILE() (!(pc->flags & LIVE_SYSTEM)) -#define MEMORY_SOURCES (NETDUMP|KDUMP|MCLXCD|LKCD|DEVMEM|S390D|MEMMOD|DISKDUMP|XENDUMP|CRASHBUILTIN) -#define DUMPFILE_TYPES (DISKDUMP|NETDUMP|KDUMP|MCLXCD|LKCD|S390D|XENDUMP) +#define MEMORY_SOURCES (NETDUMP|KDUMP|MCLXCD|LKCD|DEVMEM|S390D|MEMMOD|DISKDUMP|XENDUMP|CRASHBUILTIN|KVMDUMP) +#define DUMPFILE_TYPES (DISKDUMP|NETDUMP|KDUMP|MCLXCD|LKCD|S390D|XENDUMP|KVMDUMP) #define REMOTE() (pc->flags & REMOTE_DAEMON) #define REMOTE_ACTIVE() (pc->flags & REM_LIVE_SYSTEM) #define REMOTE_DUMPFILE() \ @@ -207,6 +207,7 @@ #define SYSRQ_TASK(X) ((pc->flags & SYSRQ) && is_task_active(X)) #define XEN_CORE_DUMPFILE() (pc->flags & XEN_CORE) #define LKCD_KERNTYPES() (pc->flags & KERNTYPES) +#define KVMDUMP_DUMPFILE() (pc->flags & KVMDUMP) #define NETDUMP_LOCAL (0x1) /* netdump_data flags */ #define NETDUMP_REMOTE (0x2) @@ -217,6 +218,8 @@ #define KDUMP_ELF32 (0x20) #define KDUMP_ELF64 (0x40) #define KDUMP_LOCAL (0x80) +#define KVMDUMP_LOCAL (0x1) +#define KVMDUMP_VALID() (kvm->flags & (KVMDUMP_LOCAL)) #define DUMPFILE_FORMAT(flags) ((flags) & \ (NETDUMP_ELF32|NETDUMP_ELF64|KDUMP_ELF32|KDUMP_ELF64)) @@ -3278,6 +3281,7 @@ int extract_hex(char *, ulong *, char, ulong); int count_bits_int(int); int count_bits_long(ulong); +int highest_bit_long(ulong); void buf_init(void); void sym_buf_init(void); void free_all_bufs(void); @@ -3671,6 +3675,7 @@ int get_cpus_online(void); int get_cpus_present(void); int get_cpus_possible(void); +int get_highest_cpu_online(void); int in_cpu_map(int, int); void paravirt_init(void); void print_stack_text_syms(struct bt_info *, ulong, ulong); @@ -4209,6 +4214,31 @@ struct xendump_data *get_xendump_data(void); /* + * kvmdump.c + */ +int is_kvmdump(char *); +int kvmdump_init(char *, FILE *); +int read_kvmdump(int, void *, int, ulong, physaddr_t); +int write_kvmdump(int, void *, int, ulong, physaddr_t); +int kvmdump_free_memory(void); +int kvmdump_memory_used(void); +int kvmdump_memory_dump(FILE *); +void get_kvmdump_regs(struct bt_info *, ulong *, ulong *); +ulong get_kvmdump_panic_task(void); +int kvmdump_phys_base(unsigned long *); + +/* + * qemu.c + */ +int qemu_init(char *); + +/* + * qemu-load.c + */ +int is_qemu_vm_file(char *); +void dump_qemu_header(FILE *); + +/* * net.c */ void net_init(void); --- crash-4.0-8.11/kvmdump.h 2009-08-11 16:24:00.000000000 -0400 +++ crash-4.0-8.12/kvmdump.h 2009-07-16 11:44:05.000000000 -0400 @@ -0,0 +1,48 @@ +/* + * kvmdump.h + * + * Copyright (C) 2009 David Anderson + * Copyright (C) 2009 Red Hat, Inc. All rights reserved. + * + * This software may be freely redistributed under the terms of the + * GNU General Public License. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define KVMDUMP_CACHED_PAGES 32 + +struct kvmdump_data { + ulong flags; + FILE *ofp; + FILE *mem; + FILE *vmp; + ulong *debug; + ulong phys_base; + uint64_t last_ram_offset; + /* page cache */ + struct kvm_page_cache_hdr { + uint64_t paddr; + char *bufptr; + } page_cache[KVMDUMP_CACHED_PAGES]; + union { + char *curbufptr; + unsigned char compressed; + } un; + int evict_index; + ulong accesses; + ulong hit_count; + ulong compresses; +}; + +extern struct kvmdump_data *kvm; + +#undef dprintf +#define dprintf(x...) do { if (*(kvm->debug)) fprintf(kvm->ofp, x); } while (0) + +#define MEMFILE_OFFSET(addr) ((off_t)((((uint64_t)addr/(uint64_t)4096)) * sizeof(off_t))) + +int store_memfile_offset(uint64_t, off_t *); +int load_memfile_offset(uint64_t, off_t *); --- crash-4.0-8.11/qemu-load.h 2009-08-11 16:24:00.000000000 -0400 +++ crash-4.0-8.12/qemu-load.h 2009-07-09 15:38:35.000000000 -0400 @@ -0,0 +1,195 @@ +/* + * Qemu save VM file description + * + * Copyright (C) 2009 Red Hat, Inc. + * Written by Paolo Bonzini. + */ + +#ifndef QEMU_LOAD_H +#define QEMU_LOAD_H 1 + +#include +#include +#include +#include + +enum qemu_save_section { + QEMU_VM_EOF, + QEMU_VM_SECTION_START, + QEMU_VM_SECTION_PART, + QEMU_VM_SECTION_END, + QEMU_VM_SECTION_FULL +}; + +enum qemu_features { + QEMU_FEATURE_RAM = 1, + QEMU_FEATURE_CPU = 2, + QEMU_FEATURE_TIMER = 4, + QEMU_FEATURE_KVM = 8 +}; + +struct qemu_device_list { + struct qemu_device *head, *tail; + uint32_t features; +}; + +struct qemu_device_loader { + const char *name; + struct qemu_device *(*init_load) (struct qemu_device_list *, uint32_t, + uint32_t, uint32_t, bool, FILE *); +}; + +struct qemu_device_vtbl { + const char *name; + uint32_t (*load) (struct qemu_device *, FILE *, + enum qemu_save_section); + void (*free) (struct qemu_device *, + struct qemu_device_list *); +}; + +struct qemu_device { + struct qemu_device_vtbl *vtbl; + struct qemu_device_list *list; + struct qemu_device *next; + struct qemu_device *prev; + uint32_t section_id; + uint32_t instance_id; +}; + +struct qemu_device_ram { + struct qemu_device dev_base; + uint64_t last_ram_offset; + FILE *fp; + off_t *offsets; +}; + +union qemu_uint128_t { + uint32_t i[4]; + unsigned i128 __attribute__ ((vector_size (16))); +}; + +struct qemu_x86_seg { + uint64_t base; + uint32_t selector; + uint32_t limit; + uint32_t flags; +}; + +struct qemu_x86_sysenter { + uint32_t cs; + uint64_t esp; + uint64_t eip; +}; + +union qemu_fpu_reg { + long double ld; + char bytes[10]; + uint64_t mmx; +}; + + +struct qemu_x86_vmtrr { + uint64_t base; + uint64_t mask; +}; + +struct qemu_x86_svm { + uint64_t hsave; + uint64_t vmcb; + uint64_t tsc_offset; + uint8_t in_vmm : 1; + uint8_t guest_if_mask : 1; + uint8_t guest_intr_masking : 1; + uint16_t cr_read_mask; + uint16_t cr_write_mask; + uint16_t dr_read_mask; + uint16_t dr_write_mask; + uint32_t exception_intercept_mask; + uint64_t intercept_mask; +}; + +struct qemu_x86_kvm { + uint64_t int_bitmap[4]; + uint64_t tsc; + uint32_t mp_state; +}; + +struct qemu_device_x86 { + struct qemu_device dev_base; + + uint32_t halted; + uint32_t irq; + uint32_t version_id; + + uint64_t regs[16]; + uint64_t eip; + uint64_t eflags; + uint16_t fpucw; + uint16_t fpusw; + uint16_t fpu_free; + union qemu_fpu_reg st[8]; + struct qemu_x86_seg cs; + struct qemu_x86_seg ds; + struct qemu_x86_seg es; + struct qemu_x86_seg ss; + struct qemu_x86_seg fs; + struct qemu_x86_seg gs; + struct qemu_x86_seg ldt; + struct qemu_x86_seg tr; + struct qemu_x86_seg gdt; + struct qemu_x86_seg idt; + struct qemu_x86_sysenter sysenter; + uint64_t cr0; + uint64_t cr2; + uint64_t cr3; + uint64_t cr4; + uint64_t dr[8]; + uint8_t cr8; + uint8_t soft_mmu : 1; + uint8_t smm : 1; + uint8_t a20_masked : 1; + uint8_t global_if : 1; + uint8_t in_nmi : 1; + uint32_t mxcsr; + union qemu_uint128_t xmm[16]; + uint64_t efer; + uint64_t star; + uint64_t lstar; + uint64_t cstar; + uint64_t fmask; + uint64_t kernel_gs_base; + uint64_t pat; + uint32_t smbase; + struct qemu_x86_svm svm; + uint64_t fixed_mtrr[12]; + uint64_t deftype_mtrr; + struct qemu_x86_vmtrr variable_mtrr[8]; + struct qemu_x86_kvm kvm; +}; + +struct qemu_timer { + uint64_t cpu_ticks_offset; + uint64_t ticks_per_sec; + uint64_t cpu_clock_offset; +}; + +struct qemu_device *device_alloc (struct qemu_device_list *, size_t, + struct qemu_device_vtbl *, uint32_t, uint32_t); +void device_free (struct qemu_device *); +void device_list_free (struct qemu_device_list *); +struct qemu_device *device_find (struct qemu_device_list *, uint32_t); +struct qemu_device *device_find_instance (struct qemu_device_list *, + const char *, uint32_t); + +struct qemu_device_list *qemu_load (const struct qemu_device_loader *, + uint32_t, FILE *); + +int ram_read_phys_page (struct qemu_device_ram *, void *, uint64_t); + +/* For a 32-bit KVM host. */ +extern const struct qemu_device_loader devices_x86_32[]; + +/* For a 64-bit KVM host. */ +extern const struct qemu_device_loader devices_x86_64[]; + +#endif --- crash-4.0-8.11/Makefile 2009-08-11 16:24:01.000000000 -0400 +++ crash-4.0-8.12/Makefile 2009-08-11 16:24:00.000000000 -0400 @@ -3,8 +3,8 @@ # Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. # www.missioncriticallinux.com, info@missioncriticallinux.com # -# Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 David Anderson -# Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Red Hat, Inc. All rights reserved. +# Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 David Anderson +# Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Red Hat, Inc. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -64,7 +64,7 @@ GENERIC_HFILES=defs.h xen_hyper_defs.h MCORE_HFILES=va_server.h vas_crash.h -REDHAT_HFILES=netdump.h diskdump.h xendump.h +REDHAT_HFILES=netdump.h diskdump.h xendump.h kvmdump.h qemu-load.h LKCD_DUMP_HFILES=lkcd_vmdump_v1.h lkcd_vmdump_v2_v3.h lkcd_dump_v5.h \ lkcd_dump_v7.h lkcd_dump_v8.h LKCD_OBSOLETE_HFILES=lkcd_fix_mem.h @@ -81,7 +81,7 @@ netdump.c diskdump.c xendump.c unwind.c unwind_decoder.c \ unwind_x86_32_64.c \ xen_hyper.c xen_hyper_command.c xen_hyper_global_data.c \ - xen_hyper_dump_tables.c + xen_hyper_dump_tables.c kvmdump.c qemu.c qemu-load.c SOURCE_FILES=${CFILES} ${GENERIC_HFILES} ${MCORE_HFILES} \ ${REDHAT_CFILES} ${REDHAT_HFILES} ${UNWIND_HFILES} \ @@ -97,7 +97,7 @@ lkcd_x86_trace.o unwind_v1.o unwind_v2.o unwind_v3.o \ unwind_x86_32_64.o \ xen_hyper.o xen_hyper_command.o xen_hyper_global_data.o \ - xen_hyper_dump_tables.o + xen_hyper_dump_tables.o kvmdump.o qemu.o qemu-load.o # These are the current set of crash extensions sources. They are not built # by default unless the third command line of the "all:" stanza is uncommented. @@ -224,7 +224,7 @@ # TARGET_CFLAGS will be configured automatically by configure TARGET_CFLAGS= -CFLAGS=-g -D${TARGET} ${TARGET_CFLAGS} +CRASH_CFLAGS=${CFLAGS} -g -D${TARGET} ${TARGET_CFLAGS} TAR_FILES=${SOURCE_FILES} Makefile COPYING README .rh_rpm_package crash.8 \ ${EXTENSION_SOURCE_FILES} @@ -287,7 +287,7 @@ @(cd extensions; make --no-print-directory -i clean) make_build_data: force - cc -c ${CFLAGS} build_data.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} build_data.c ${WARNING_OPTIONS} ${WARNING_ERROR} install: /usr/bin/install ${PROGRAM} ${INSTALLDIR} @@ -309,150 +309,159 @@ @make --no-print-directory gdb_merge main.o: ${GENERIC_HFILES} main.c - cc -c ${CFLAGS} main.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} main.c ${WARNING_OPTIONS} ${WARNING_ERROR} cmdline.o: ${GENERIC_HFILES} cmdline.c - cc -c ${CFLAGS} ${GDB_FLAGS} cmdline.c -I${READLINE_DIRECTORY} ${WARNING_OPTIONS} + cc -c ${CRASH_CFLAGS} ${GDB_FLAGS} cmdline.c -I${READLINE_DIRECTORY} ${WARNING_OPTIONS} ${WARNING_ERROR} tools.o: ${GENERIC_HFILES} tools.c - cc -c ${CFLAGS} tools.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} tools.c ${WARNING_OPTIONS} ${WARNING_ERROR} global_data.o: ${GENERIC_HFILES} global_data.c - cc -c ${CFLAGS} global_data.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} global_data.c ${WARNING_OPTIONS} ${WARNING_ERROR} symbols.o: ${GENERIC_HFILES} symbols.c - cc -c ${CFLAGS} ${GDB_FLAGS} symbols.c -I${BFD_DIRECTORY} -I${GDB_INCLUDE_DIRECTORY} ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} ${GDB_FLAGS} symbols.c -I${BFD_DIRECTORY} -I${GDB_INCLUDE_DIRECTORY} ${WARNING_OPTIONS} ${WARNING_ERROR} filesys.o: ${GENERIC_HFILES} filesys.c - cc -c ${CFLAGS} filesys.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} filesys.c ${WARNING_OPTIONS} ${WARNING_ERROR} help.o: ${GENERIC_HFILES} help.c - cc -c ${CFLAGS} ${GDB_FLAGS} help.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} ${GDB_FLAGS} help.c ${WARNING_OPTIONS} ${WARNING_ERROR} memory.o: ${GENERIC_HFILES} memory.c - cc -c ${CFLAGS} memory.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} memory.c ${WARNING_OPTIONS} ${WARNING_ERROR} test.o: ${GENERIC_HFILES} test.c - cc -c ${CFLAGS} test.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} test.c ${WARNING_OPTIONS} ${WARNING_ERROR} task.o: ${GENERIC_HFILES} task.c - cc -c ${CFLAGS} ${GDB_FLAGS} task.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} ${GDB_FLAGS} task.c ${WARNING_OPTIONS} ${WARNING_ERROR} kernel.o: ${GENERIC_HFILES} kernel.c - cc -c ${CFLAGS} ${GDB_FLAGS} kernel.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} ${GDB_FLAGS} kernel.c ${WARNING_OPTIONS} ${WARNING_ERROR} gdb_interface.o: ${GENERIC_HFILES} gdb_interface.c - cc -c ${CFLAGS} ${GDB_FLAGS} gdb_interface.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} ${GDB_FLAGS} gdb_interface.c ${WARNING_OPTIONS} ${WARNING_ERROR} va_server.o: ${MCORE_HFILES} va_server.c - cc -c ${CFLAGS} va_server.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} va_server.c ${WARNING_OPTIONS} ${WARNING_ERROR} va_server_v1.o: ${MCORE_HFILES} va_server_v1.c - cc -c ${CFLAGS} va_server_v1.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} va_server_v1.c ${WARNING_OPTIONS} ${WARNING_ERROR} lkcd_common.o: ${GENERIC_HFILES} ${LKCD_DUMP_HFILES} lkcd_common.c - cc -c ${CFLAGS} lkcd_common.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} lkcd_common.c ${WARNING_OPTIONS} ${WARNING_ERROR} lkcd_v1.o: ${GENERIC_HFILES} ${LKCD_DUMP_HFILES} lkcd_v1.c - cc -c ${CFLAGS} -DMCLX lkcd_v1.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} -DMCLX lkcd_v1.c ${WARNING_OPTIONS} ${WARNING_ERROR} lkcd_v2_v3.o: ${GENERIC_HFILES} ${LKCD_DUMP_HFILES} lkcd_v2_v3.c - cc -c ${CFLAGS} -DMCLX lkcd_v2_v3.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} -DMCLX lkcd_v2_v3.c ${WARNING_OPTIONS} ${WARNING_ERROR} lkcd_v5.o: ${GENERIC_HFILES} ${LKCD_DUMP_HFILES} lkcd_v5.c - cc -c ${CFLAGS} -DMCLX lkcd_v5.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} -DMCLX lkcd_v5.c ${WARNING_OPTIONS} ${WARNING_ERROR} lkcd_v7.o: ${GENERIC_HFILES} ${LKCD_DUMP_HFILES} lkcd_v7.c - cc -c ${CFLAGS} -DMCLX lkcd_v7.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} -DMCLX lkcd_v7.c ${WARNING_OPTIONS} ${WARNING_ERROR} lkcd_v8.o: ${GENERIC_HFILES} ${LKCD_DUMP_HFILES} lkcd_v8.c - cc -c ${CFLAGS} -DMCLX lkcd_v8.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} -DMCLX lkcd_v8.c ${WARNING_OPTIONS} ${WARNING_ERROR} net.o: ${GENERIC_HFILES} net.c - cc -c ${CFLAGS} net.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} net.c ${WARNING_OPTIONS} ${WARNING_ERROR} dev.o: ${GENERIC_HFILES} dev.c - cc -c ${CFLAGS} dev.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} dev.c ${WARNING_OPTIONS} ${WARNING_ERROR} # remote.c functionality has been deprecated remote.o: ${GENERIC_HFILES} remote.c - @cc -c ${CFLAGS} remote.c ${WARNING_OPTIONS} ${WARNING_ERROR} + @cc -c ${CRASH_CFLAGS} remote.c ${WARNING_OPTIONS} ${WARNING_ERROR} remote_daemon.o: ${GENERIC_HFILES} remote.c - cc -c ${CFLAGS} -DDAEMON remote.c -o remote_daemon.o ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} -DDAEMON remote.c -o remote_daemon.o ${WARNING_OPTIONS} ${WARNING_ERROR} x86.o: ${GENERIC_HFILES} ${REDHAT_HFILES} x86.c - cc -c ${CFLAGS} -DMCLX x86.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} -DMCLX x86.c ${WARNING_OPTIONS} ${WARNING_ERROR} alpha.o: ${GENERIC_HFILES} alpha.c - cc -c ${CFLAGS} ${GDB_FLAGS} alpha.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} ${GDB_FLAGS} alpha.c ${WARNING_OPTIONS} ${WARNING_ERROR} ppc.o: ${GENERIC_HFILES} ppc.c - cc -c ${CFLAGS} ppc.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} ppc.c ${WARNING_OPTIONS} ${WARNING_ERROR} ia64.o: ${GENERIC_HFILES} ${REDHAT_HFILES} ia64.c - cc -c ${CFLAGS} ia64.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} ia64.c ${WARNING_OPTIONS} ${WARNING_ERROR} ppc64.o: ${GENERIC_HFILES} ppc64.c - cc -c ${CFLAGS} ppc64.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} ppc64.c ${WARNING_OPTIONS} ${WARNING_ERROR} x86_64.o: ${GENERIC_HFILES} ${REDHAT_HFILES} x86_64.c - cc -c ${CFLAGS} x86_64.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} x86_64.c ${WARNING_OPTIONS} ${WARNING_ERROR} s390.o: ${GENERIC_HFILES} ${IBM_HFILES} s390.c - cc -c ${CFLAGS} s390.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} s390.c ${WARNING_OPTIONS} ${WARNING_ERROR} s390x.o: ${GENERIC_HFILES} ${IBM_HFILES} s390x.c - cc -c ${CFLAGS} s390x.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} s390x.c ${WARNING_OPTIONS} ${WARNING_ERROR} s390dbf.o: ${GENERIC_HFILES} ${IBM_HFILES} s390dbf.c - cc -c ${CFLAGS} s390dbf.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} s390dbf.c ${WARNING_OPTIONS} ${WARNING_ERROR} s390_dump.o: ${GENERIC_HFILES} ${IBM_HFILES} s390_dump.c - cc -c ${CFLAGS} s390_dump.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} s390_dump.c ${WARNING_OPTIONS} ${WARNING_ERROR} netdump.o: ${GENERIC_HFILES} ${REDHAT_HFILES} netdump.c - cc -c ${CFLAGS} netdump.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} netdump.c ${WARNING_OPTIONS} ${WARNING_ERROR} netdump_daemon.o: ${GENERIC_HFILES} ${REDHAT_HFILES} netdump.c - cc -c ${CFLAGS} -DDAEMON netdump.c -o netdump_daemon.o ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} -DDAEMON netdump.c -o netdump_daemon.o ${WARNING_OPTIONS} ${WARNING_ERROR} diskdump.o: ${GENERIC_HFILES} ${REDHAT_HFILES} diskdump.c - cc -c ${CFLAGS} diskdump.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} diskdump.c ${WARNING_OPTIONS} ${WARNING_ERROR} xendump.o: ${GENERIC_HFILES} ${REDHAT_HFILES} xendump.c - cc -c ${CFLAGS} xendump.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} xendump.c ${WARNING_OPTIONS} ${WARNING_ERROR} + +kvmdump.o: ${GENERIC_HFILES} ${REDHAT_HFILES} kvmdump.c + cc -c ${CRASH_CFLAGS} kvmdump.c ${WARNING_OPTIONS} ${WARNING_ERROR} + +qemu.o: ${GENERIC_HFILES} ${REDHAT_HFILES} qemu.c + cc -c ${CRASH_CFLAGS} qemu.c ${WARNING_OPTIONS} ${WARNING_ERROR} + +qemu-load.o: ${GENERIC_HFILES} ${REDHAT_HFILES} qemu-load.c + cc -c ${CRASH_CFLAGS} qemu-load.c ${WARNING_OPTIONS} ${WARNING_ERROR} extensions.o: ${GENERIC_HFILES} extensions.c - cc -c ${CFLAGS} extensions.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} extensions.c ${WARNING_OPTIONS} ${WARNING_ERROR} lkcd_x86_trace.o: ${GENERIC_HFILES} ${LKCD_TRACE_HFILES} lkcd_x86_trace.c - cc -c ${CFLAGS} -DREDHAT lkcd_x86_trace.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} -DREDHAT lkcd_x86_trace.c ${WARNING_OPTIONS} ${WARNING_ERROR} unwind_x86_32_64.o: ${GENERIC_HFILES} ${UNWIND_HFILES} unwind_x86_32_64.c - cc -c ${CFLAGS} unwind_x86_32_64.c -o unwind_x86_32_64.o ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} unwind_x86_32_64.c -o unwind_x86_32_64.o ${WARNING_OPTIONS} ${WARNING_ERROR} unwind_v1.o: ${GENERIC_HFILES} ${UNWIND_HFILES} unwind.c unwind_decoder.c - cc -c ${CFLAGS} -DREDHAT -DUNWIND_V1 unwind.c -o unwind_v1.o ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} -DREDHAT -DUNWIND_V1 unwind.c -o unwind_v1.o ${WARNING_OPTIONS} ${WARNING_ERROR} unwind_v2.o: ${GENERIC_HFILES} ${UNWIND_HFILES} unwind.c unwind_decoder.c - cc -c ${CFLAGS} -DREDHAT -DUNWIND_V2 unwind.c -o unwind_v2.o ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} -DREDHAT -DUNWIND_V2 unwind.c -o unwind_v2.o ${WARNING_OPTIONS} ${WARNING_ERROR} unwind_v3.o: ${GENERIC_HFILES} ${UNWIND_HFILES} unwind.c unwind_decoder.c - cc -c ${CFLAGS} -DREDHAT -DUNWIND_V3 unwind.c -o unwind_v3.o ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} -DREDHAT -DUNWIND_V3 unwind.c -o unwind_v3.o ${WARNING_OPTIONS} ${WARNING_ERROR} lkcd_fix_mem.o: ${GENERIC_HFILES} ${LKCD_HFILES} lkcd_fix_mem.c - cc -c ${CFLAGS} -DMCLX lkcd_fix_mem.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} -DMCLX lkcd_fix_mem.c ${WARNING_OPTIONS} ${WARNING_ERROR} xen_hyper.o: ${GENERIC_HFILES} xen_hyper.c - cc -c ${CFLAGS} xen_hyper.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} xen_hyper.c ${WARNING_OPTIONS} ${WARNING_ERROR} xen_hyper_command.o: ${GENERIC_HFILES} xen_hyper_command.c - cc -c ${CFLAGS} xen_hyper_command.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} xen_hyper_command.c ${WARNING_OPTIONS} ${WARNING_ERROR} xen_hyper_global_data.o: ${GENERIC_HFILES} xen_hyper_global_data.c - cc -c ${CFLAGS} xen_hyper_global_data.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} xen_hyper_global_data.c ${WARNING_OPTIONS} ${WARNING_ERROR} xen_hyper_dump_tables.o: ${GENERIC_HFILES} xen_hyper_dump_tables.c - cc -c ${CFLAGS} xen_hyper_dump_tables.c ${WARNING_OPTIONS} ${WARNING_ERROR} + cc -c ${CRASH_CFLAGS} xen_hyper_dump_tables.c ${WARNING_OPTIONS} ${WARNING_ERROR} ${PROGRAM}: force @make --no-print-directory all --- crash-4.0-8.11/gdb-6.1/gdb/dwarf2read.c 2009-08-11 15:11:34.000000000 -0400 +++ crash-4.0-8.12/gdb-6.1/gdb/dwarf2read.c 2009-08-11 09:59:48.000000000 -0400 @@ -922,6 +922,10 @@ static int attr_form_is_block (struct attribute *); +static int attr_form_is_section_offset (struct attribute *); + +static int attr_form_is_constant (struct attribute *); + static void dwarf2_symbol_mark_computed (struct attribute *attr, struct symbol *sym, struct dwarf2_cu *cu); @@ -2618,8 +2622,19 @@ attr = dwarf2_attr (die, DW_AT_data_member_location, cu); if (attr) { - FIELD_BITPOS (*fp) = - decode_locdesc (DW_BLOCK (attr), cu) * bits_per_byte; + int byte_offset; + + if (attr_form_is_section_offset (attr)) + { + dwarf2_complex_location_expr_complaint (); + byte_offset = 0; + } + else if (attr_form_is_constant (attr)) + byte_offset = dwarf2_get_attr_constant_value (attr, 0); + else + byte_offset = decode_locdesc (DW_BLOCK (attr), cu); + + FIELD_BITPOS (*fp) = byte_offset * bits_per_byte; } else FIELD_BITPOS (*fp) = 0; @@ -2939,7 +2954,7 @@ { fnp->voffset = decode_locdesc (DW_BLOCK (attr), cu) + 2; } - else if (attr->form == DW_FORM_data4 || attr->form == DW_FORM_data8) + else if (attr_form_is_section_offset (attr)) { dwarf2_complex_location_expr_complaint (); } @@ -3482,7 +3497,7 @@ { base = decode_locdesc (DW_BLOCK (attr), cu); } - else if (attr->form == DW_FORM_data4 || attr->form == DW_FORM_data8) + else if (attr_form_is_section_offset (attr)) { dwarf2_complex_location_expr_complaint (); } @@ -4392,7 +4407,7 @@ { part_die->locdesc = DW_BLOCK (&attr); } - else if (attr.form == DW_FORM_data4 || attr.form == DW_FORM_data8) + else if (attr_form_is_section_offset (&attr)) { dwarf2_complex_location_expr_complaint (); } @@ -8030,11 +8045,56 @@ || attr->form == DW_FORM_block); } +/* Return non-zero if ATTR's value is a section offset --- classes + lineptr, loclistptr, macptr or rangelistptr --- or zero, otherwise. + You may use DW_UNSND (attr) to retrieve such offsets. + + Section 7.5.4, "Attribute Encodings", explains that no attribute + may have a value that belongs to more than one of these classes; it + would be ambiguous if we did, because we use the same forms for all + of them. */ +static int +attr_form_is_section_offset (struct attribute *attr) +{ + return (attr->form == DW_FORM_data4 + || attr->form == DW_FORM_data8); +} + + +/* Return non-zero if ATTR's value falls in the 'constant' class, or + zero otherwise. When this function returns true, you can apply + dwarf2_get_attr_constant_value to it. + + However, note that for some attributes you must check + attr_form_is_section_offset before using this test. DW_FORM_data4 + and DW_FORM_data8 are members of both the constant class, and of + the classes that contain offsets into other debug sections + (lineptr, loclistptr, macptr or rangelistptr). The DWARF spec says + that, if an attribute's can be either a constant or one of the + section offset classes, DW_FORM_data4 and DW_FORM_data8 should be + taken as section offsets, not constants. */ +static int +attr_form_is_constant (struct attribute *attr) +{ + switch (attr->form) + { + case DW_FORM_sdata: + case DW_FORM_udata: + case DW_FORM_data1: + case DW_FORM_data2: + case DW_FORM_data4: + case DW_FORM_data8: + return 1; + default: + return 0; + } +} + static void dwarf2_symbol_mark_computed (struct attribute *attr, struct symbol *sym, struct dwarf2_cu *cu) { - if (attr->form == DW_FORM_data4 || attr->form == DW_FORM_data8) + if (attr_form_is_section_offset (attr)) { struct dwarf2_loclist_baton *baton; --- crash-4.0-8.11/gdb-6.1.patch 2009-08-11 15:11:33.000000000 -0400 +++ crash-4.0-8.12/gdb-6.1.patch 2009-08-11 09:56:41.000000000 -0400 @@ -317,3 +317,64 @@ if ((abfd->flags & (EXEC_P | DYNAMIC)) != 0) sym->symbol.value -= sym->symbol.section->vma; +--- gdb-6.1/readline/readline.h.orig ++++ gdb-6.1/readline/readline.h +@@ -361,7 +361,7 @@ extern int rl_crlf PARAMS((void)); + #if (defined (__STDC__) || defined (__cplusplus)) && defined (USE_VARARGS) && defined (PREFER_STDARG) + extern int rl_message (const char *, ...) __attribute__((__format__ (printf, 1, 2))); + #else +-extern int rl_message (); ++extern int rl_message (void); + #endif + + extern int rl_show_char PARAMS((int)); +--- gdb-6.1/readline/rltypedefs.h.orig ++++ gdb-6.1/readline/rltypedefs.h +@@ -32,10 +32,10 @@ extern "C" { + #if !defined (_FUNCTION_DEF) + # define _FUNCTION_DEF + +-typedef int Function (); +-typedef void VFunction (); +-typedef char *CPFunction (); +-typedef char **CPPFunction (); ++typedef int Function (void); ++typedef void VFunction (void); ++typedef char *CPFunction (void); ++typedef char **CPPFunction (void); + + #endif /* _FUNCTION_DEF */ + +diff -urp gdb-6.1.orig/gdb/ChangeLog gdb-6.1/gdb/ChangeLog +--- gdb-6.1.orig/gdb/ChangeLog 2009-08-08 17:04:24.836969960 +0200 ++++ gdb-6.1/gdb/ChangeLog 2009-08-08 17:00:21.682970174 +0200 +@@ -1,3 +1,14 @@ ++2007-12-13 Jim Blandy ++ ++ * dwarf2read.c (attr_form_is_constant): New function. ++ (dwarf2_add_field): Use it and attr_form_is_section_offset to ++ recognize DW_AT_data_member_location attributes. Use ++ dwarf2_get_attr_constant_value when the attribute is a constant. ++ ++ * dwarf2read.c (attr_form_is_section_offset): New function. ++ (dwarf_add_member_fn, read_common_block, read_partial_die) ++ (dwarf2_symbol_mark_computed): Use it, instead of writing it out. ++ + 2004-04-03 GDB Administrator + + GDB 6.1 released. +diff -urp gdb-6.1.orig/gdb/ChangeLog gdb-6.1/gdb/ChangeLog +--- gdb-6.1.orig/gdb/ChangeLog 2009-08-08 20:49:34.000000000 +0200 ++++ gdb-6.1/gdb/ChangeLog 2009-08-09 13:04:25.842288308 +0200 +@@ -1,3 +1,11 @@ ++2007-12-17 Jim Blandy ++ ++ * dwarf2read.c (dwarf2_add_field): Correctly scale all byte ++ offsets obtained from DW_AT_data_member_location before recording ++ them in FIELD_BITPOS (*fp). ++ ++ * dwarf2read.c (attr_form_is_section_offset): Doc fixes. ++ + 2007-12-13 Jim Blandy + + * dwarf2read.c (attr_form_is_constant): New function.