Blame qemu.c

Packit Service 501009
/*
Packit Service 501009
 * Derive kernel base from a QEMU saved VM file
Packit Service 501009
 *
Packit Service 501009
 * Copyright (C) 2009, 2010 Red Hat, Inc.
Packit Service 501009
 * Written by Paolo Bonzini.
Packit Service 501009
 *
Packit Service 501009
 * Portions Copyright (C) 2009 David Anderson
Packit Service 501009
 *
Packit Service 501009
 * This program is free software; you can redistribute it and/or modify
Packit Service 501009
 * it under the terms of the GNU General Public License as published by
Packit Service 501009
 * the Free Software Foundation; either version 2 of the License, or
Packit Service 501009
 * (at your option) any later version.
Packit Service 501009
 *
Packit Service 501009
 * This program is distributed in the hope that it will be useful,
Packit Service 501009
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 501009
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service 501009
 * GNU General Public License for more details.
Packit Service 501009
 */
Packit Service 501009
Packit Service 501009
#include <stdlib.h>
Packit Service 501009
#include <errno.h>
Packit Service 501009
#include <stdio.h>
Packit Service 501009
#include <assert.h>
Packit Service 501009
Packit Service 501009
#include "qemu-load.h"
Packit Service 501009
Packit Service 501009
#include "kvmdump.h"
Packit Service 501009
Packit Service 501009
/*
Packit Service 501009
 * Some bits we need to access in the control registers and page tables.
Packit Service 501009
 */
Packit Service 501009
Packit Service 501009
#define MSR_EFER_LMA	(1 << 10)
Packit Service 501009
#define PG_PRESENT_MASK	(1 << 0)
Packit Service 501009
#define PG_PSE_MASK	(1 << 7)
Packit Service 501009
#define CR0_PG_MASK	(1 << 31)
Packit Service 501009
#define CR4_PAE_MASK	(1 << 31)
Packit Service 501009
#define CR4_PSE_MASK	(1 << 31)
Packit Service 501009
Packit Service 501009
static uint32_t
Packit Service 501009
ldl (struct qemu_device_x86 *dx86, struct qemu_device_ram *dram, uint64_t addr)
Packit Service 501009
{
Packit Service 501009
	char buf[4096];
Packit Service 501009
	if (dx86->a20_masked)
Packit Service 501009
		addr &= ~(1LL<<20);
Packit Service 501009
	if (!ram_read_phys_page (dram, buf, addr & ~0xfff))
Packit Service 501009
		return 0;
Packit Service 501009
Packit Service 501009
	assert ((addr & 0xfff) <= 0xffc);
Packit Service 501009
	return *(uint32_t *)(buf + (addr & 0xfff));
Packit Service 501009
}
Packit Service 501009
Packit Service 501009
static uint64_t
Packit Service 501009
ldq (struct qemu_device_x86 *dx86, struct qemu_device_ram *dram, uint64_t addr)
Packit Service 501009
{
Packit Service 501009
	char buf[4096];
Packit Service 501009
	if (dx86->a20_masked)
Packit Service 501009
		addr &= ~(1LL<<20);
Packit Service 501009
	if (!ram_read_phys_page (dram, buf, addr & ~0xfff))
Packit Service 501009
		return 0;
Packit Service 501009
Packit Service 501009
	assert ((addr & 0xfff) <= 0xff8);
Packit Service 501009
	return *(uint64_t *)(buf + (addr & 0xfff));
Packit Service 501009
}
Packit Service 501009
Packit Service 501009
/*
Packit Service 501009
 * Messy x86 TLB fault logic, walking the page tables to find the physical
Packit Service 501009
 * address corresponding to ADDR.  Taken from QEMU.
Packit Service 501009
 */
Packit Service 501009
Packit Service 501009
static uint64_t
Packit Service 501009
get_phys_page(struct qemu_device_x86 *dx86, struct qemu_device_ram *dram,
Packit Service 501009
	      uint64_t addr)
Packit Service 501009
{
Packit Service 501009
	uint64_t pde_addr, pte_addr;
Packit Service 501009
	uint64_t pte, paddr;
Packit Service 501009
	uint32_t page_offset;
Packit Service 501009
	int page_size;
Packit Service 501009
Packit Service 501009
	if ((dx86->cr4 & CR4_PAE_MASK) || (dx86->efer & MSR_EFER_LMA)) {
Packit Service 501009
		uint64_t pdpe_addr;
Packit Service 501009
		uint64_t pde, pdpe;
Packit Service 501009
Packit Service 501009
		if (dx86->cr4 & CR4_PAE_MASK)
Packit Service 501009
			dprintf ("PAE active\n");
Packit Service 501009
		if (dx86->efer & MSR_EFER_LMA) {
Packit Service 501009
			uint64_t pml4e_addr, pml4e;
Packit Service 501009
			int32_t sext;
Packit Service 501009
Packit Service 501009
			dprintf ("long mode active\n");
Packit Service 501009
Packit Service 501009
			/* test virtual address sign extension */
Packit Service 501009
			sext = (int64_t) addr >> 47;
Packit Service 501009
			if (sext != 0 && sext != -1)
Packit Service 501009
				return -1;
Packit Service 501009
Packit Service 501009
			pml4e_addr = ((dx86->cr3 & ~0xfff)
Packit Service 501009
				      + (((addr >> 39) & 0x1ff) << 3));
Packit Service 501009
			pml4e = ldq (dx86, dram, pml4e_addr);
Packit Service 501009
			if (!(pml4e & PG_PRESENT_MASK))
Packit Service 501009
				return -1;
Packit Service 501009
			dprintf ("PML4 page present\n");
Packit Service 501009
Packit Service 501009
			pdpe_addr = ((pml4e & ~0xfff)
Packit Service 501009
				     + (((addr >> 30) & 0x1ff) << 3));
Packit Service 501009
			pdpe = ldq (dx86, dram, pdpe_addr);
Packit Service 501009
			if (!(pdpe & PG_PRESENT_MASK))
Packit Service 501009
				return -1;
Packit Service 501009
			dprintf ("PDPE page present\n");
Packit Service 501009
		} else {
Packit Service 501009
			dprintf ("long mode inactive\n");
Packit Service 501009
Packit Service 501009
			pdpe_addr = ((dx86->cr3 & ~0x1f)
Packit Service 501009
				     + ((addr >> 27) & 0x18));
Packit Service 501009
			pdpe = ldq (dx86, dram, pdpe_addr);
Packit Service 501009
			if (!(pdpe & PG_PRESENT_MASK))
Packit Service 501009
				return -1;
Packit Service 501009
			dprintf ("PDPE page present\n");
Packit Service 501009
		}
Packit Service 501009
Packit Service 501009
		pde_addr = (pdpe & ~0xfff) + (((addr >> 21) & 0x1ff) << 3);
Packit Service 501009
		pde = ldq (dx86, dram, pde_addr);
Packit Service 501009
		if (!(pde & PG_PRESENT_MASK))
Packit Service 501009
			return -1;
Packit Service 501009
		dprintf ("PDE page present\n");
Packit Service 501009
Packit Service 501009
		if (pde & PG_PSE_MASK) {
Packit Service 501009
			/* 2 MB page */
Packit Service 501009
			dprintf ("2MB page\n");
Packit Service 501009
Packit Service 501009
			page_size = 2048 * 1024;
Packit Service 501009
			pte = pde & ~((page_size - 1) & ~0xfff);
Packit Service 501009
		} else {
Packit Service 501009
			/* 4 KB page */
Packit Service 501009
			dprintf ("4 KB PAE page\n");
Packit Service 501009
Packit Service 501009
			pte_addr = ((pde & ~0xfff)
Packit Service 501009
				    + (((addr >> 12) & 0x1ff) << 3));
Packit Service 501009
			page_size = 4096;
Packit Service 501009
			pte = ldq (dx86, dram, pte_addr);
Packit Service 501009
			if (!(pte & PG_PRESENT_MASK))
Packit Service 501009
				return -1;
Packit Service 501009
			dprintf ("PTE page present\n");
Packit Service 501009
		}
Packit Service 501009
Packit Service 501009
	} else {
Packit Service 501009
		/* Not PAE.  */
Packit Service 501009
Packit Service 501009
		uint32_t pde;
Packit Service 501009
		if (!(dx86->cr0 & CR0_PG_MASK)) {
Packit Service 501009
			dprintf ("Paging inactive\n");
Packit Service 501009
Packit Service 501009
			pte = addr;
Packit Service 501009
			page_size = 4096;
Packit Service 501009
		} else {
Packit Service 501009
			/* page directory entry */
Packit Service 501009
			pde_addr = ((dx86->cr3 & ~0xfff)
Packit Service 501009
				    + ((addr >> 20) & 0xffc));
Packit Service 501009
			pde = ldl (dx86, dram, pde_addr);
Packit Service 501009
			if (!(pde & PG_PRESENT_MASK))
Packit Service 501009
				return -1;
Packit Service 501009
			dprintf ("PDE page present\n");
Packit Service 501009
			if ((pde & PG_PSE_MASK) && (dx86->cr4 & CR4_PSE_MASK)) {
Packit Service 501009
				page_size = 4096 * 1024;
Packit Service 501009
				pte = pde & ~((page_size - 1) & ~0xfff);
Packit Service 501009
			} else {
Packit Service 501009
				page_size = 4096;
Packit Service 501009
				pte_addr = ((pde & ~0xfff)
Packit Service 501009
					    + ((addr >> 10) & 0xffc));
Packit Service 501009
				pte = ldl (dx86, dram, pte_addr);
Packit Service 501009
				if (!(pte & PG_PRESENT_MASK))
Packit Service 501009
					return -1;
Packit Service 501009
				dprintf ("PTE page present\n");
Packit Service 501009
			}
Packit Service 501009
		}
Packit Service 501009
	}
Packit Service 501009
Packit Service 501009
	page_offset = (addr & 0xfff) & (page_size - 1);
Packit Service 501009
	paddr = (pte & ~0xfff) + page_offset;
Packit Service 501009
	return paddr;
Packit Service 501009
}
Packit Service 501009
Packit Service 501009
/*
Packit Service 501009
 * I'm using the IDT base as a quick way to find the bottom of the
Packit Service 501009
 * kernel memory.
Packit Service 501009
 */
Packit Service 501009
static uint64_t
Packit Service 501009
get_idt_base(struct qemu_device_list *dl)
Packit Service 501009
{
Packit Service 501009
	struct qemu_device_x86 *dx86 = (struct qemu_device_x86 *)
Packit Service 501009
		device_find_instance (dl, "cpu", 0);
Packit Service 501009
Packit Service 501009
	return dx86->idt.base;
Packit Service 501009
}
Packit Service 501009
Packit Service 501009
static uint64_t
Packit Service 501009
get_kernel_base(struct qemu_device_list *dl)
Packit Service 501009
{
Packit Service 501009
	int i;
Packit Service 501009
	uint64_t kernel_base = -1;
Packit Service 501009
	uint64_t base_vaddr, last, mask;
Packit Service 501009
	struct qemu_device_x86 *dx86 = (struct qemu_device_x86 *)
Packit Service 501009
		device_find_instance (dl, "cpu", 0);
Packit Service 501009
	struct qemu_device_ram *dram = (struct qemu_device_ram *)
Packit Service 501009
		device_find_instance (dl, "ram", 0);
Packit Service 501009
Packit Service 501009
	for (i = 30, last = -1; (kernel_base == -1) && (i >= 20); i--)
Packit Service 501009
        {
Packit Service 501009
                mask = ~((1LL << i) - 1);
Packit Service 501009
                base_vaddr = dx86->idt.base & mask;
Packit Service 501009
		if (base_vaddr == last)
Packit Service 501009
			continue;
Packit Service 501009
		if (base_vaddr < kvm->kvbase) {
Packit Service 501009
			fprintf(stderr, 
Packit Service 501009
			    "WARNING: IDT base contains: %llx\n         "
Packit Service 501009
			    "cannot determine physical base address: defaulting to 0\n\n", 
Packit Service 501009
				(unsigned long long)base_vaddr);
Packit Service 501009
			return 0;
Packit Service 501009
		}
Packit Service 501009
		dprintf("get_kernel_base: %llx\n", (unsigned long long)base_vaddr);
Packit Service 501009
                kernel_base = get_phys_page(dx86, dram, base_vaddr);
Packit Service 501009
		last = base_vaddr;
Packit Service 501009
        }
Packit Service 501009
Packit Service 501009
        if (kernel_base != -1) {
Packit Service 501009
		dprintf("kvbase: %llx vaddr used: %llx physical: %llx\n",
Packit Service 501009
			(unsigned long long)kvm->kvbase,
Packit Service 501009
			(unsigned long long)base_vaddr,
Packit Service 501009
			(unsigned long long)kernel_base);
Packit Service 501009
		/*
Packit Service 501009
		 *  Subtract the offset between the virtual address used
Packit Service 501009
		 *  and the kernel's base virtual address.
Packit Service 501009
		 */
Packit Service 501009
                kernel_base -= (base_vaddr - kvm->kvbase);
Packit Service 501009
        } else {
Packit Service 501009
		dprintf("WARNING: cannot determine physical base address:"
Packit Service 501009
			" defaulting to 0\n\n");
Packit Service 501009
		kernel_base = 0;
Packit Service 501009
		kvm->flags |= NO_PHYS_BASE;
Packit Service 501009
	}
Packit Service 501009
Packit Service 501009
	return kernel_base;
Packit Service 501009
}
Packit Service 501009
Packit Service 501009
Packit Service 501009
#ifdef MAIN_FROM_TEST_C
Packit Service 501009
int main (int argc, char **argv)
Packit Service 501009
{
Packit Service 501009
	struct qemu_device_list *dl;
Packit Service 501009
	FILE *fp;
Packit Service 501009
Packit Service 501009
	if (argc != 2) {
Packit Service 501009
		fprintf (stderr, "Usage: test SAVE-FILE\n");
Packit Service 501009
		exit (1);
Packit Service 501009
	}
Packit Service 501009
Packit Service 501009
	fp = fopen(argv[1], "r");
Packit Service 501009
	if (!fp) {
Packit Service 501009
		fprintf (stderr, "Error: %s\n", strerror (errno));
Packit Service 501009
		exit (1);
Packit Service 501009
	}
Packit Service 501009
Packit Service 501009
#ifdef HOST_32BIT
Packit Service 501009
	dl = qemu_load (devices_x86_32, QEMU_FEATURE_CPU|QEMU_FEATURE_RAM, fp);
Packit Service 501009
#else
Packit Service 501009
	dl = qemu_load (devices_x86_64, QEMU_FEATURE_CPU|QEMU_FEATURE_RAM, fp);
Packit Service 501009
#endif
Packit Service 501009
	printf ("IDT at %llx\n", get_idt_base (dl));
Packit Service 501009
	printf ("Physical kernel base at %llx\n", get_kernel_base (dl));
Packit Service 501009
	device_list_free (dl);
Packit Service 501009
	fclose (fp);
Packit Service 501009
	exit (0);
Packit Service 501009
}
Packit Service 501009
#endif
Packit Service 501009
Packit Service 501009
Packit Service 501009
/*
Packit Service 501009
 *  crash utility adaptation
Packit Service 501009
 */
Packit Service 501009
Packit Service 501009
#include "defs.h"
Packit Service 501009
Packit Service 501009
int 
Packit Service 501009
qemu_init(char *filename)
Packit Service 501009
{
Packit Service 501009
	struct qemu_device_list *dl;
Packit Service 501009
	struct qemu_device_ram *dram;
Packit Service 501009
	uint64_t idt = 0;
Packit Service 501009
Packit Service 501009
	if (CRASHDEBUG(1))
Packit Service 501009
		dump_qemu_header(kvm->ofp);
Packit Service 501009
Packit Service 501009
	rewind(kvm->vmp);
Packit Service 501009
Packit Service 501009
	if (kvm->flags & (MAPFILE|MAPFILE_APPENDED))
Packit Service 501009
		return TRUE;
Packit Service 501009
Packit Service 501009
	please_wait("scanning KVM dumpfile");
Packit Service 501009
Packit Service 501009
	if (kvm->flags & KVMHOST_32)
Packit Service 501009
		dl = qemu_load(devices_x86_32, 
Packit Service 501009
			QEMU_FEATURE_CPU|QEMU_FEATURE_RAM, kvm->vmp);
Packit Service 501009
	else
Packit Service 501009
		dl = qemu_load(devices_x86_64, 
Packit Service 501009
			QEMU_FEATURE_CPU|QEMU_FEATURE_RAM, kvm->vmp);
Packit Service 501009
Packit Service 501009
	please_wait_done();
Packit Service 501009
Packit Service 501009
	if (dl) {
Packit Service 501009
		if (machine_type("X86_64")) {
Packit Service 501009
			idt = get_idt_base(dl);
Packit Service 501009
			kvm->mapinfo.phys_base = get_kernel_base(dl);
Packit Service 501009
		}
Packit Service 501009
Packit Service 501009
		dram = (struct qemu_device_ram *) 
Packit Service 501009
			device_find_instance (dl, "ram", 0);
Packit Service 501009
Packit Service 501009
		if (CRASHDEBUG(1)) {
Packit Service 501009
			if (machine_type("X86_64")) {
Packit Service 501009
				fprintf(kvm->ofp, "IDT: %llx\n", 
Packit Service 501009
					(ulonglong)idt);
Packit Service 501009
				fprintf(kvm->ofp, "physical kernel base: %llx\n", 
Packit Service 501009
					(ulonglong)kvm->mapinfo.phys_base); 
Packit Service 501009
			}
Packit Service 501009
			fprintf(kvm->ofp, "last RAM offset: %llx\n", 
Packit Service 501009
				(ulonglong)dram->last_ram_offset); 
Packit Service 501009
		}
Packit Service 501009
Packit Service 501009
		device_list_free (dl);
Packit Service 501009
	} else
Packit Service 501009
		fclose(kvm->vmp);
Packit Service 501009
Packit Service 501009
	return dl ? TRUE : FALSE;
Packit Service 501009
}