static LIST_HEAD(vm_list);
struct kvm_arch_ops *kvm_arch_ops;
-struct kvm_stat kvm_stat;
-EXPORT_SYMBOL_GPL(kvm_stat);
+
+#define STAT_OFFSET(x) offsetof(struct kvm_vcpu, stat.x)
static struct kvm_stats_debugfs_item {
const char *name;
- u32 *data;
+ int offset;
struct dentry *dentry;
} debugfs_entries[] = {
- { "pf_fixed", &kvm_stat.pf_fixed },
- { "pf_guest", &kvm_stat.pf_guest },
- { "tlb_flush", &kvm_stat.tlb_flush },
- { "invlpg", &kvm_stat.invlpg },
- { "exits", &kvm_stat.exits },
- { "io_exits", &kvm_stat.io_exits },
- { "mmio_exits", &kvm_stat.mmio_exits },
- { "signal_exits", &kvm_stat.signal_exits },
- { "irq_window", &kvm_stat.irq_window_exits },
- { "halt_exits", &kvm_stat.halt_exits },
- { "request_irq", &kvm_stat.request_irq_exits },
- { "irq_exits", &kvm_stat.irq_exits },
- { NULL, NULL }
+ { "pf_fixed", STAT_OFFSET(pf_fixed) },
+ { "pf_guest", STAT_OFFSET(pf_guest) },
+ { "tlb_flush", STAT_OFFSET(tlb_flush) },
+ { "invlpg", STAT_OFFSET(invlpg) },
+ { "exits", STAT_OFFSET(exits) },
+ { "io_exits", STAT_OFFSET(io_exits) },
+ { "mmio_exits", STAT_OFFSET(mmio_exits) },
+ { "signal_exits", STAT_OFFSET(signal_exits) },
+ { "irq_window", STAT_OFFSET(irq_window_exits) },
+ { "halt_exits", STAT_OFFSET(halt_exits) },
+ { "request_irq", STAT_OFFSET(request_irq_exits) },
+ { "irq_exits", STAT_OFFSET(irq_exits) },
+ { NULL }
};
static struct dentry *debugfs_dir;
u64 pdpte;
u64 *pdpt;
int ret;
- struct kvm_memory_slot *memslot;
+ struct page *page;
spin_lock(&vcpu->kvm->lock);
- memslot = gfn_to_memslot(vcpu->kvm, pdpt_gfn);
- /* FIXME: !memslot - emulate? 0xff? */
- pdpt = kmap_atomic(gfn_to_page(memslot, pdpt_gfn), KM_USER0);
+ page = gfn_to_page(vcpu->kvm, pdpt_gfn);
+ /* FIXME: !page - emulate? 0xff? */
+ pdpt = kmap_atomic(page, KM_USER0);
ret = 1;
for (i = 0; i < 4; ++i) {
void lmsw(struct kvm_vcpu *vcpu, unsigned long msw)
{
- kvm_arch_ops->decache_cr0_cr4_guest_bits(vcpu);
set_cr0(vcpu, (vcpu->cr0 & ~0x0ful) | (msw & 0x0f));
}
EXPORT_SYMBOL_GPL(lmsw);
return r;
}
-struct kvm_memory_slot *gfn_to_memslot(struct kvm *kvm, gfn_t gfn)
+/*
+ * Set a new alias region. Aliases map a portion of physical memory into
+ * another portion. This is useful for memory windows, for example the PC
+ * VGA region.
+ */
+static int kvm_vm_ioctl_set_memory_alias(struct kvm *kvm,
+ struct kvm_memory_alias *alias)
+{
+ int r, n;
+ struct kvm_mem_alias *p;
+
+ r = -EINVAL;
+ /* General sanity checks */
+ if (alias->memory_size & (PAGE_SIZE - 1))
+ goto out;
+ if (alias->guest_phys_addr & (PAGE_SIZE - 1))
+ goto out;
+ if (alias->slot >= KVM_ALIAS_SLOTS)
+ goto out;
+ if (alias->guest_phys_addr + alias->memory_size
+ < alias->guest_phys_addr)
+ goto out;
+ if (alias->target_phys_addr + alias->memory_size
+ < alias->target_phys_addr)
+ goto out;
+
+ spin_lock(&kvm->lock);
+
+ p = &kvm->aliases[alias->slot];
+ p->base_gfn = alias->guest_phys_addr >> PAGE_SHIFT;
+ p->npages = alias->memory_size >> PAGE_SHIFT;
+ p->target_gfn = alias->target_phys_addr >> PAGE_SHIFT;
+
+ for (n = KVM_ALIAS_SLOTS; n > 0; --n)
+ if (kvm->aliases[n - 1].npages)
+ break;
+ kvm->naliases = n;
+
+ spin_unlock(&kvm->lock);
+
+ vcpu_load(&kvm->vcpus[0]);
+ spin_lock(&kvm->lock);
+ kvm_mmu_zap_all(&kvm->vcpus[0]);
+ spin_unlock(&kvm->lock);
+ vcpu_put(&kvm->vcpus[0]);
+
+ return 0;
+
+out:
+ return r;
+}
+
+static gfn_t unalias_gfn(struct kvm *kvm, gfn_t gfn)
+{
+ int i;
+ struct kvm_mem_alias *alias;
+
+ for (i = 0; i < kvm->naliases; ++i) {
+ alias = &kvm->aliases[i];
+ if (gfn >= alias->base_gfn
+ && gfn < alias->base_gfn + alias->npages)
+ return alias->target_gfn + gfn - alias->base_gfn;
+ }
+ return gfn;
+}
+
+static struct kvm_memory_slot *__gfn_to_memslot(struct kvm *kvm, gfn_t gfn)
{
int i;
}
return NULL;
}
-EXPORT_SYMBOL_GPL(gfn_to_memslot);
+
+struct kvm_memory_slot *gfn_to_memslot(struct kvm *kvm, gfn_t gfn)
+{
+ gfn = unalias_gfn(kvm, gfn);
+ return __gfn_to_memslot(kvm, gfn);
+}
+
+struct page *gfn_to_page(struct kvm *kvm, gfn_t gfn)
+{
+ struct kvm_memory_slot *slot;
+
+ gfn = unalias_gfn(kvm, gfn);
+ slot = __gfn_to_memslot(kvm, gfn);
+ if (!slot)
+ return NULL;
+ return slot->phys_mem[gfn - slot->base_gfn];
+}
+EXPORT_SYMBOL_GPL(gfn_to_page);
void mark_page_dirty(struct kvm *kvm, gfn_t gfn)
{
}
static int emulator_read_std(unsigned long addr,
- unsigned long *val,
+ void *val,
unsigned int bytes,
struct x86_emulate_ctxt *ctxt)
{
unsigned offset = addr & (PAGE_SIZE-1);
unsigned tocopy = min(bytes, (unsigned)PAGE_SIZE - offset);
unsigned long pfn;
- struct kvm_memory_slot *memslot;
- void *page;
+ struct page *page;
+ void *page_virt;
if (gpa == UNMAPPED_GVA)
return X86EMUL_PROPAGATE_FAULT;
pfn = gpa >> PAGE_SHIFT;
- memslot = gfn_to_memslot(vcpu->kvm, pfn);
- if (!memslot)
+ page = gfn_to_page(vcpu->kvm, pfn);
+ if (!page)
return X86EMUL_UNHANDLEABLE;
- page = kmap_atomic(gfn_to_page(memslot, pfn), KM_USER0);
+ page_virt = kmap_atomic(page, KM_USER0);
- memcpy(data, page + offset, tocopy);
+ memcpy(data, page_virt + offset, tocopy);
- kunmap_atomic(page, KM_USER0);
+ kunmap_atomic(page_virt, KM_USER0);
bytes -= tocopy;
data += tocopy;
}
static int emulator_write_std(unsigned long addr,
- unsigned long val,
+ const void *val,
unsigned int bytes,
struct x86_emulate_ctxt *ctxt)
{
}
static int emulator_read_emulated(unsigned long addr,
- unsigned long *val,
+ void *val,
unsigned int bytes,
struct x86_emulate_ctxt *ctxt)
{
}
static int emulator_write_phys(struct kvm_vcpu *vcpu, gpa_t gpa,
- unsigned long val, int bytes)
+ const void *val, int bytes)
{
- struct kvm_memory_slot *m;
struct page *page;
void *virt;
if (((gpa + bytes - 1) >> PAGE_SHIFT) != (gpa >> PAGE_SHIFT))
return 0;
- m = gfn_to_memslot(vcpu->kvm, gpa >> PAGE_SHIFT);
- if (!m)
+ page = gfn_to_page(vcpu->kvm, gpa >> PAGE_SHIFT);
+ if (!page)
return 0;
- page = gfn_to_page(m, gpa >> PAGE_SHIFT);
kvm_mmu_pre_write(vcpu, gpa, bytes);
mark_page_dirty(vcpu->kvm, gpa >> PAGE_SHIFT);
virt = kmap_atomic(page, KM_USER0);
- memcpy(virt + offset_in_page(gpa), &val, bytes);
+ memcpy(virt + offset_in_page(gpa), val, bytes);
kunmap_atomic(virt, KM_USER0);
kvm_mmu_post_write(vcpu, gpa, bytes);
return 1;
}
static int emulator_write_emulated(unsigned long addr,
- unsigned long val,
+ const void *val,
unsigned int bytes,
struct x86_emulate_ctxt *ctxt)
{
struct kvm_vcpu *vcpu = ctxt->vcpu;
gpa_t gpa = vcpu->mmu.gva_to_gpa(vcpu, addr);
- if (gpa == UNMAPPED_GVA)
+ if (gpa == UNMAPPED_GVA) {
+ kvm_arch_ops->inject_page_fault(vcpu, addr, 2);
return X86EMUL_PROPAGATE_FAULT;
+ }
if (emulator_write_phys(vcpu, gpa, val, bytes))
return X86EMUL_CONTINUE;
vcpu->mmio_phys_addr = gpa;
vcpu->mmio_size = bytes;
vcpu->mmio_is_write = 1;
- memcpy(vcpu->mmio_data, &val, bytes);
+ memcpy(vcpu->mmio_data, val, bytes);
return X86EMUL_CONTINUE;
}
static int emulator_cmpxchg_emulated(unsigned long addr,
- unsigned long old,
- unsigned long new,
+ const void *old,
+ const void *new,
unsigned int bytes,
struct x86_emulate_ctxt *ctxt)
{
return emulator_write_emulated(addr, new, bytes, ctxt);
}
-#ifdef CONFIG_X86_32
-
-static int emulator_cmpxchg8b_emulated(unsigned long addr,
- unsigned long old_lo,
- unsigned long old_hi,
- unsigned long new_lo,
- unsigned long new_hi,
- struct x86_emulate_ctxt *ctxt)
-{
- static int reported;
- int r;
-
- if (!reported) {
- reported = 1;
- printk(KERN_WARNING "kvm: emulating exchange8b as write\n");
- }
- r = emulator_write_emulated(addr, new_lo, 4, ctxt);
- if (r != X86EMUL_CONTINUE)
- return r;
- return emulator_write_emulated(addr+4, new_hi, 4, ctxt);
-}
-
-#endif
-
static unsigned long get_segment_base(struct kvm_vcpu *vcpu, int seg)
{
return kvm_arch_ops->get_segment_base(vcpu, seg);
{
unsigned long cr0;
- kvm_arch_ops->decache_cr0_cr4_guest_bits(vcpu);
cr0 = vcpu->cr0 & ~CR0_TS_MASK;
kvm_arch_ops->set_cr0(vcpu, cr0);
return X86EMUL_CONTINUE;
.read_emulated = emulator_read_emulated,
.write_emulated = emulator_write_emulated,
.cmpxchg_emulated = emulator_cmpxchg_emulated,
-#ifdef CONFIG_X86_32
- .cmpxchg8b_emulated = emulator_cmpxchg8b_emulated,
-#endif
};
int emulate_instruction(struct kvm_vcpu *vcpu,
int r;
int cs_db, cs_l;
+ vcpu->mmio_fault_cr2 = cr2;
kvm_arch_ops->cache_regs(vcpu);
kvm_arch_ops->get_cs_db_l_bits(vcpu, &cs_db, &cs_l);
kvm_arch_ops->decache_regs(vcpu);
kvm_arch_ops->set_rflags(vcpu, emulate_ctxt.eflags);
- if (vcpu->mmio_is_write)
+ if (vcpu->mmio_is_write) {
+ vcpu->mmio_needed = 0;
return EMULATE_DO_MMIO;
+ }
return EMULATE_DONE;
}
unsigned long realmode_get_cr(struct kvm_vcpu *vcpu, int cr)
{
- kvm_arch_ops->decache_cr0_cr4_guest_bits(vcpu);
+ kvm_arch_ops->decache_cr4_guest_bits(vcpu);
switch (cr) {
case 0:
return vcpu->cr0;
printk(KERN_WARNING "%s: MSR_IA32_MC0_STATUS 0x%llx, nop\n",
__FUNCTION__, data);
break;
+ case MSR_IA32_MCG_STATUS:
+ printk(KERN_WARNING "%s: MSR_IA32_MCG_STATUS 0x%llx, nop\n",
+ __FUNCTION__, data);
+ break;
case MSR_IA32_UCODE_REV:
case MSR_IA32_UCODE_WRITE:
case 0x200 ... 0x2ff: /* MTRRs */
void kvm_resched(struct kvm_vcpu *vcpu)
{
+ if (!need_resched())
+ return;
vcpu_put(vcpu);
cond_resched();
vcpu_load(vcpu);
vcpu->regs[VCPU_REGS_RSI] += delta;
}
- vcpu->run->io_completed = 0;
-
kvm_arch_ops->decache_regs(vcpu);
io->count -= io->cur_count;
/* re-sync apic's tpr */
vcpu->cr8 = kvm_run->cr8;
- if (kvm_run->io_completed) {
- if (vcpu->pio.cur_count) {
- r = complete_pio(vcpu);
- if (r)
- goto out;
- } else {
- memcpy(vcpu->mmio_data, kvm_run->mmio.data, 8);
- vcpu->mmio_read_completed = 1;
- }
+ if (vcpu->pio.cur_count) {
+ r = complete_pio(vcpu);
+ if (r)
+ goto out;
}
- vcpu->mmio_needed = 0;
+ if (vcpu->mmio_needed) {
+ memcpy(vcpu->mmio_data, kvm_run->mmio.data, 8);
+ vcpu->mmio_read_completed = 1;
+ vcpu->mmio_needed = 0;
+ r = emulate_instruction(vcpu, kvm_run,
+ vcpu->mmio_fault_cr2, 0);
+ if (r == EMULATE_DO_MMIO) {
+ /*
+ * Read-modify-write. Back to userspace.
+ */
+ kvm_run->exit_reason = KVM_EXIT_MMIO;
+ r = 0;
+ goto out;
+ }
+ }
if (kvm_run->exit_reason == KVM_EXIT_HYPERCALL) {
kvm_arch_ops->cache_regs(vcpu);
sregs->gdt.limit = dt.limit;
sregs->gdt.base = dt.base;
- kvm_arch_ops->decache_cr0_cr4_guest_bits(vcpu);
+ kvm_arch_ops->decache_cr4_guest_bits(vcpu);
sregs->cr0 = vcpu->cr0;
sregs->cr2 = vcpu->cr2;
sregs->cr3 = vcpu->cr3;
#endif
vcpu->apic_base = sregs->apic_base;
- kvm_arch_ops->decache_cr0_cr4_guest_bits(vcpu);
+ kvm_arch_ops->decache_cr4_guest_bits(vcpu);
mmu_reset_needed |= vcpu->cr0 != sregs->cr0;
kvm_arch_ops->set_cr0(vcpu, sregs->cr0);
vcpu->host_fx_image = (char*)ALIGN((hva_t)vcpu->fx_buf,
FX_IMAGE_ALIGN);
vcpu->guest_fx_image = vcpu->host_fx_image + FX_IMAGE_SIZE;
+ vcpu->cr0 = 0x10;
r = kvm_arch_ops->vcpu_create(vcpu);
if (r < 0)
return 0;
}
+/*
+ * fxsave fpu state. Taken from x86_64/processor.h. To be killed when
+ * we have asm/x86/processor.h
+ */
+struct fxsave {
+ u16 cwd;
+ u16 swd;
+ u16 twd;
+ u16 fop;
+ u64 rip;
+ u64 rdp;
+ u32 mxcsr;
+ u32 mxcsr_mask;
+ u32 st_space[32]; /* 8*16 bytes for each FP-reg = 128 bytes */
+#ifdef CONFIG_X86_64
+ u32 xmm_space[64]; /* 16*16 bytes for each XMM-reg = 256 bytes */
+#else
+ u32 xmm_space[32]; /* 8*16 bytes for each XMM-reg = 128 bytes */
+#endif
+};
+
+static int kvm_vcpu_ioctl_get_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu)
+{
+ struct fxsave *fxsave = (struct fxsave *)vcpu->guest_fx_image;
+
+ vcpu_load(vcpu);
+
+ memcpy(fpu->fpr, fxsave->st_space, 128);
+ fpu->fcw = fxsave->cwd;
+ fpu->fsw = fxsave->swd;
+ fpu->ftwx = fxsave->twd;
+ fpu->last_opcode = fxsave->fop;
+ fpu->last_ip = fxsave->rip;
+ fpu->last_dp = fxsave->rdp;
+ memcpy(fpu->xmm, fxsave->xmm_space, sizeof fxsave->xmm_space);
+
+ vcpu_put(vcpu);
+
+ return 0;
+}
+
+static int kvm_vcpu_ioctl_set_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu)
+{
+ struct fxsave *fxsave = (struct fxsave *)vcpu->guest_fx_image;
+
+ vcpu_load(vcpu);
+
+ memcpy(fxsave->st_space, fpu->fpr, 128);
+ fxsave->cwd = fpu->fcw;
+ fxsave->swd = fpu->fsw;
+ fxsave->twd = fpu->ftwx;
+ fxsave->fop = fpu->last_opcode;
+ fxsave->rip = fpu->last_ip;
+ fxsave->rdp = fpu->last_dp;
+ memcpy(fxsave->xmm_space, fpu->xmm, sizeof fxsave->xmm_space);
+
+ vcpu_put(vcpu);
+
+ return 0;
+}
+
static long kvm_vcpu_ioctl(struct file *filp,
unsigned int ioctl, unsigned long arg)
{
r = kvm_vcpu_ioctl_set_sigmask(vcpu, &sigset);
break;
}
+ case KVM_GET_FPU: {
+ struct kvm_fpu fpu;
+
+ memset(&fpu, 0, sizeof fpu);
+ r = kvm_vcpu_ioctl_get_fpu(vcpu, &fpu);
+ if (r)
+ goto out;
+ r = -EFAULT;
+ if (copy_to_user(argp, &fpu, sizeof fpu))
+ goto out;
+ r = 0;
+ break;
+ }
+ case KVM_SET_FPU: {
+ struct kvm_fpu fpu;
+
+ r = -EFAULT;
+ if (copy_from_user(&fpu, argp, sizeof fpu))
+ goto out;
+ r = kvm_vcpu_ioctl_set_fpu(vcpu, &fpu);
+ if (r)
+ goto out;
+ r = 0;
+ break;
+ }
default:
;
}
goto out;
break;
}
+ case KVM_SET_MEMORY_ALIAS: {
+ struct kvm_memory_alias alias;
+
+ r = -EFAULT;
+ if (copy_from_user(&alias, argp, sizeof alias))
+ goto out;
+ r = kvm_vm_ioctl_set_memory_alias(kvm, &alias);
+ if (r)
+ goto out;
+ break;
+ }
default:
;
}
{
struct kvm *kvm = vma->vm_file->private_data;
unsigned long pgoff;
- struct kvm_memory_slot *slot;
struct page *page;
*type = VM_FAULT_MINOR;
pgoff = ((address - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
- slot = gfn_to_memslot(kvm, pgoff);
- if (!slot)
- return NOPAGE_SIGBUS;
- page = gfn_to_page(slot, pgoff);
+ page = gfn_to_page(kvm, pgoff);
if (!page)
return NOPAGE_SIGBUS;
get_page(page);
.priority = 20, /* must be > scheduler priority */
};
+static u64 stat_get(void *_offset)
+{
+ unsigned offset = (long)_offset;
+ u64 total = 0;
+ struct kvm *kvm;
+ struct kvm_vcpu *vcpu;
+ int i;
+
+ spin_lock(&kvm_lock);
+ list_for_each_entry(kvm, &vm_list, vm_list)
+ for (i = 0; i < KVM_MAX_VCPUS; ++i) {
+ vcpu = &kvm->vcpus[i];
+ total += *(u32 *)((void *)vcpu + offset);
+ }
+ spin_unlock(&kvm_lock);
+ return total;
+}
+
+static void stat_set(void *offset, u64 val)
+{
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(stat_fops, stat_get, stat_set, "%llu\n");
+
static __init void kvm_init_debug(void)
{
struct kvm_stats_debugfs_item *p;
debugfs_dir = debugfs_create_dir("kvm", NULL);
for (p = debugfs_entries; p->name; ++p)
- p->dentry = debugfs_create_u32(p->name, 0444, debugfs_dir,
- p->data);
+ p->dentry = debugfs_create_file(p->name, 0444, debugfs_dir,
+ (void *)(long)p->offset,
+ &stat_fops);
}
static void kvm_exit_debug(void)
static struct page *bad_page;
int r;
+ r = kvm_mmu_module_init();
+ if (r)
+ goto out4;
+
r = register_filesystem(&kvm_fs_type);
if (r)
goto out3;
out2:
unregister_filesystem(&kvm_fs_type);
out3:
+ kvm_mmu_module_exit();
+out4:
return r;
}
__free_page(pfn_to_page(bad_page_address >> PAGE_SHIFT));
mntput(kvmfs_mnt);
unregister_filesystem(&kvm_fs_type);
+ kvm_mmu_module_exit();
}
module_init(kvm_init)