1
linux/arch/ia64/kvm/kvm-ia64.c
Julia Lawall cab7a1eeeb KVM: ia64: Fix irq disabling leak in error handling code
There is a call to local_irq_restore in the normal exit case, so it would
seem that there should be one on an error return as well.

The semantic patch that finds this problem is as follows:
(http://www.emn.fr/x-info/coccinelle/)

// <smpl>
@@
expression l;
expression E,E1,E2;
@@

local_irq_save(l);
... when != local_irq_restore(l)
    when != spin_unlock_irqrestore(E,l)
    when any
    when strict
(
if (...) { ... when != local_irq_restore(l)
               when != spin_unlock_irqrestore(E1,l)
+   local_irq_restore(l);
    return ...;
}
|
if (...)
+   {local_irq_restore(l);
    return ...;
+   }
|
spin_unlock_irqrestore(E2,l);
|
local_irq_restore(l);
)
// </smpl>

Signed-off-by: Julia Lawall <julia@diku.dk>
Signed-off-by: Avi Kivity <avi@qumranet.com>
2008-07-27 11:35:32 +03:00

1805 lines
39 KiB
C

/*
* kvm_ia64.c: Basic KVM suppport On Itanium series processors
*
*
* Copyright (C) 2007, Intel Corporation.
* Xiantao Zhang (xiantao.zhang@intel.com)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* 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., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307 USA.
*
*/
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/percpu.h>
#include <linux/gfp.h>
#include <linux/fs.h>
#include <linux/smp.h>
#include <linux/kvm_host.h>
#include <linux/kvm.h>
#include <linux/bitops.h>
#include <linux/hrtimer.h>
#include <linux/uaccess.h>
#include <asm/pgtable.h>
#include <asm/gcc_intrin.h>
#include <asm/pal.h>
#include <asm/cacheflush.h>
#include <asm/div64.h>
#include <asm/tlb.h>
#include "misc.h"
#include "vti.h"
#include "iodev.h"
#include "ioapic.h"
#include "lapic.h"
static unsigned long kvm_vmm_base;
static unsigned long kvm_vsa_base;
static unsigned long kvm_vm_buffer;
static unsigned long kvm_vm_buffer_size;
unsigned long kvm_vmm_gp;
static long vp_env_info;
static struct kvm_vmm_info *kvm_vmm_info;
static DEFINE_PER_CPU(struct kvm_vcpu *, last_vcpu);
struct kvm_stats_debugfs_item debugfs_entries[] = {
{ NULL }
};
struct fdesc{
unsigned long ip;
unsigned long gp;
};
static void kvm_flush_icache(unsigned long start, unsigned long len)
{
int l;
for (l = 0; l < (len + 32); l += 32)
ia64_fc(start + l);
ia64_sync_i();
ia64_srlz_i();
}
static void kvm_flush_tlb_all(void)
{
unsigned long i, j, count0, count1, stride0, stride1, addr;
long flags;
addr = local_cpu_data->ptce_base;
count0 = local_cpu_data->ptce_count[0];
count1 = local_cpu_data->ptce_count[1];
stride0 = local_cpu_data->ptce_stride[0];
stride1 = local_cpu_data->ptce_stride[1];
local_irq_save(flags);
for (i = 0; i < count0; ++i) {
for (j = 0; j < count1; ++j) {
ia64_ptce(addr);
addr += stride1;
}
addr += stride0;
}
local_irq_restore(flags);
ia64_srlz_i(); /* srlz.i implies srlz.d */
}
long ia64_pal_vp_create(u64 *vpd, u64 *host_iva, u64 *opt_handler)
{
struct ia64_pal_retval iprv;
PAL_CALL_STK(iprv, PAL_VP_CREATE, (u64)vpd, (u64)host_iva,
(u64)opt_handler);
return iprv.status;
}
static DEFINE_SPINLOCK(vp_lock);
void kvm_arch_hardware_enable(void *garbage)
{
long status;
long tmp_base;
unsigned long pte;
unsigned long saved_psr;
int slot;
pte = pte_val(mk_pte_phys(__pa(kvm_vmm_base),
PAGE_KERNEL));
local_irq_save(saved_psr);
slot = ia64_itr_entry(0x3, KVM_VMM_BASE, pte, KVM_VMM_SHIFT);
local_irq_restore(saved_psr);
if (slot < 0)
return;
spin_lock(&vp_lock);
status = ia64_pal_vp_init_env(kvm_vsa_base ?
VP_INIT_ENV : VP_INIT_ENV_INITALIZE,
__pa(kvm_vm_buffer), KVM_VM_BUFFER_BASE, &tmp_base);
if (status != 0) {
printk(KERN_WARNING"kvm: Failed to Enable VT Support!!!!\n");
return ;
}
if (!kvm_vsa_base) {
kvm_vsa_base = tmp_base;
printk(KERN_INFO"kvm: kvm_vsa_base:0x%lx\n", kvm_vsa_base);
}
spin_unlock(&vp_lock);
ia64_ptr_entry(0x3, slot);
}
void kvm_arch_hardware_disable(void *garbage)
{
long status;
int slot;
unsigned long pte;
unsigned long saved_psr;
unsigned long host_iva = ia64_getreg(_IA64_REG_CR_IVA);
pte = pte_val(mk_pte_phys(__pa(kvm_vmm_base),
PAGE_KERNEL));
local_irq_save(saved_psr);
slot = ia64_itr_entry(0x3, KVM_VMM_BASE, pte, KVM_VMM_SHIFT);
local_irq_restore(saved_psr);
if (slot < 0)
return;
status = ia64_pal_vp_exit_env(host_iva);
if (status)
printk(KERN_DEBUG"kvm: Failed to disable VT support! :%ld\n",
status);
ia64_ptr_entry(0x3, slot);
}
void kvm_arch_check_processor_compat(void *rtn)
{
*(int *)rtn = 0;
}
int kvm_dev_ioctl_check_extension(long ext)
{
int r;
switch (ext) {
case KVM_CAP_IRQCHIP:
case KVM_CAP_USER_MEMORY:
r = 1;
break;
case KVM_CAP_COALESCED_MMIO:
r = KVM_COALESCED_MMIO_PAGE_OFFSET;
break;
default:
r = 0;
}
return r;
}
static struct kvm_io_device *vcpu_find_mmio_dev(struct kvm_vcpu *vcpu,
gpa_t addr, int len, int is_write)
{
struct kvm_io_device *dev;
dev = kvm_io_bus_find_dev(&vcpu->kvm->mmio_bus, addr, len, is_write);
return dev;
}
static int handle_vm_error(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
{
kvm_run->exit_reason = KVM_EXIT_UNKNOWN;
kvm_run->hw.hardware_exit_reason = 1;
return 0;
}
static int handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
{
struct kvm_mmio_req *p;
struct kvm_io_device *mmio_dev;
p = kvm_get_vcpu_ioreq(vcpu);
if ((p->addr & PAGE_MASK) == IOAPIC_DEFAULT_BASE_ADDRESS)
goto mmio;
vcpu->mmio_needed = 1;
vcpu->mmio_phys_addr = kvm_run->mmio.phys_addr = p->addr;
vcpu->mmio_size = kvm_run->mmio.len = p->size;
vcpu->mmio_is_write = kvm_run->mmio.is_write = !p->dir;
if (vcpu->mmio_is_write)
memcpy(vcpu->mmio_data, &p->data, p->size);
memcpy(kvm_run->mmio.data, &p->data, p->size);
kvm_run->exit_reason = KVM_EXIT_MMIO;
return 0;
mmio:
mmio_dev = vcpu_find_mmio_dev(vcpu, p->addr, p->size, !p->dir);
if (mmio_dev) {
if (!p->dir)
kvm_iodevice_write(mmio_dev, p->addr, p->size,
&p->data);
else
kvm_iodevice_read(mmio_dev, p->addr, p->size,
&p->data);
} else
printk(KERN_ERR"kvm: No iodevice found! addr:%lx\n", p->addr);
p->state = STATE_IORESP_READY;
return 1;
}
static int handle_pal_call(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
{
struct exit_ctl_data *p;
p = kvm_get_exit_data(vcpu);
if (p->exit_reason == EXIT_REASON_PAL_CALL)
return kvm_pal_emul(vcpu, kvm_run);
else {
kvm_run->exit_reason = KVM_EXIT_UNKNOWN;
kvm_run->hw.hardware_exit_reason = 2;
return 0;
}
}
static int handle_sal_call(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
{
struct exit_ctl_data *p;
p = kvm_get_exit_data(vcpu);
if (p->exit_reason == EXIT_REASON_SAL_CALL) {
kvm_sal_emul(vcpu);
return 1;
} else {
kvm_run->exit_reason = KVM_EXIT_UNKNOWN;
kvm_run->hw.hardware_exit_reason = 3;
return 0;
}
}
/*
* offset: address offset to IPI space.
* value: deliver value.
*/
static void vcpu_deliver_ipi(struct kvm_vcpu *vcpu, uint64_t dm,
uint64_t vector)
{
switch (dm) {
case SAPIC_FIXED:
kvm_apic_set_irq(vcpu, vector, 0);
break;
case SAPIC_NMI:
kvm_apic_set_irq(vcpu, 2, 0);
break;
case SAPIC_EXTINT:
kvm_apic_set_irq(vcpu, 0, 0);
break;
case SAPIC_INIT:
case SAPIC_PMI:
default:
printk(KERN_ERR"kvm: Unimplemented Deliver reserved IPI!\n");
break;
}
}
static struct kvm_vcpu *lid_to_vcpu(struct kvm *kvm, unsigned long id,
unsigned long eid)
{
union ia64_lid lid;
int i;
for (i = 0; i < KVM_MAX_VCPUS; i++) {
if (kvm->vcpus[i]) {
lid.val = VCPU_LID(kvm->vcpus[i]);
if (lid.id == id && lid.eid == eid)
return kvm->vcpus[i];
}
}
return NULL;
}
static int handle_ipi(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
{
struct exit_ctl_data *p = kvm_get_exit_data(vcpu);
struct kvm_vcpu *target_vcpu;
struct kvm_pt_regs *regs;
union ia64_ipi_a addr = p->u.ipi_data.addr;
union ia64_ipi_d data = p->u.ipi_data.data;
target_vcpu = lid_to_vcpu(vcpu->kvm, addr.id, addr.eid);
if (!target_vcpu)
return handle_vm_error(vcpu, kvm_run);
if (!target_vcpu->arch.launched) {
regs = vcpu_regs(target_vcpu);
regs->cr_iip = vcpu->kvm->arch.rdv_sal_data.boot_ip;
regs->r1 = vcpu->kvm->arch.rdv_sal_data.boot_gp;
target_vcpu->arch.mp_state = KVM_MP_STATE_RUNNABLE;
if (waitqueue_active(&target_vcpu->wq))
wake_up_interruptible(&target_vcpu->wq);
} else {
vcpu_deliver_ipi(target_vcpu, data.dm, data.vector);
if (target_vcpu != vcpu)
kvm_vcpu_kick(target_vcpu);
}
return 1;
}
struct call_data {
struct kvm_ptc_g ptc_g_data;
struct kvm_vcpu *vcpu;
};
static void vcpu_global_purge(void *info)
{
struct call_data *p = (struct call_data *)info;
struct kvm_vcpu *vcpu = p->vcpu;
if (test_bit(KVM_REQ_TLB_FLUSH, &vcpu->requests))
return;
set_bit(KVM_REQ_PTC_G, &vcpu->requests);
if (vcpu->arch.ptc_g_count < MAX_PTC_G_NUM) {
vcpu->arch.ptc_g_data[vcpu->arch.ptc_g_count++] =
p->ptc_g_data;
} else {
clear_bit(KVM_REQ_PTC_G, &vcpu->requests);
vcpu->arch.ptc_g_count = 0;
set_bit(KVM_REQ_TLB_FLUSH, &vcpu->requests);
}
}
static int handle_global_purge(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
{
struct exit_ctl_data *p = kvm_get_exit_data(vcpu);
struct kvm *kvm = vcpu->kvm;
struct call_data call_data;
int i;
call_data.ptc_g_data = p->u.ptc_g_data;
for (i = 0; i < KVM_MAX_VCPUS; i++) {
if (!kvm->vcpus[i] || kvm->vcpus[i]->arch.mp_state ==
KVM_MP_STATE_UNINITIALIZED ||
vcpu == kvm->vcpus[i])
continue;
if (waitqueue_active(&kvm->vcpus[i]->wq))
wake_up_interruptible(&kvm->vcpus[i]->wq);
if (kvm->vcpus[i]->cpu != -1) {
call_data.vcpu = kvm->vcpus[i];
smp_call_function_single(kvm->vcpus[i]->cpu,
vcpu_global_purge, &call_data, 1);
} else
printk(KERN_WARNING"kvm: Uninit vcpu received ipi!\n");
}
return 1;
}
static int handle_switch_rr6(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
{
return 1;
}
int kvm_emulate_halt(struct kvm_vcpu *vcpu)
{
ktime_t kt;
long itc_diff;
unsigned long vcpu_now_itc;
unsigned long expires;
struct hrtimer *p_ht = &vcpu->arch.hlt_timer;
unsigned long cyc_per_usec = local_cpu_data->cyc_per_usec;
struct vpd *vpd = to_host(vcpu->kvm, vcpu->arch.vpd);
vcpu_now_itc = ia64_getreg(_IA64_REG_AR_ITC) + vcpu->arch.itc_offset;
if (time_after(vcpu_now_itc, vpd->itm)) {
vcpu->arch.timer_check = 1;
return 1;
}
itc_diff = vpd->itm - vcpu_now_itc;
if (itc_diff < 0)
itc_diff = -itc_diff;
expires = div64_u64(itc_diff, cyc_per_usec);
kt = ktime_set(0, 1000 * expires);
vcpu->arch.ht_active = 1;
hrtimer_start(p_ht, kt, HRTIMER_MODE_ABS);
if (irqchip_in_kernel(vcpu->kvm)) {
vcpu->arch.mp_state = KVM_MP_STATE_HALTED;
kvm_vcpu_block(vcpu);
hrtimer_cancel(p_ht);
vcpu->arch.ht_active = 0;
if (vcpu->arch.mp_state != KVM_MP_STATE_RUNNABLE)
return -EINTR;
return 1;
} else {
printk(KERN_ERR"kvm: Unsupported userspace halt!");
return 0;
}
}
static int handle_vm_shutdown(struct kvm_vcpu *vcpu,
struct kvm_run *kvm_run)
{
kvm_run->exit_reason = KVM_EXIT_SHUTDOWN;
return 0;
}
static int handle_external_interrupt(struct kvm_vcpu *vcpu,
struct kvm_run *kvm_run)
{
return 1;
}
static int (*kvm_vti_exit_handlers[])(struct kvm_vcpu *vcpu,
struct kvm_run *kvm_run) = {
[EXIT_REASON_VM_PANIC] = handle_vm_error,
[EXIT_REASON_MMIO_INSTRUCTION] = handle_mmio,
[EXIT_REASON_PAL_CALL] = handle_pal_call,
[EXIT_REASON_SAL_CALL] = handle_sal_call,
[EXIT_REASON_SWITCH_RR6] = handle_switch_rr6,
[EXIT_REASON_VM_DESTROY] = handle_vm_shutdown,
[EXIT_REASON_EXTERNAL_INTERRUPT] = handle_external_interrupt,
[EXIT_REASON_IPI] = handle_ipi,
[EXIT_REASON_PTC_G] = handle_global_purge,
};
static const int kvm_vti_max_exit_handlers =
sizeof(kvm_vti_exit_handlers)/sizeof(*kvm_vti_exit_handlers);
static void kvm_prepare_guest_switch(struct kvm_vcpu *vcpu)
{
}
static uint32_t kvm_get_exit_reason(struct kvm_vcpu *vcpu)
{
struct exit_ctl_data *p_exit_data;
p_exit_data = kvm_get_exit_data(vcpu);
return p_exit_data->exit_reason;
}
/*
* The guest has exited. See if we can fix it or if we need userspace
* assistance.
*/
static int kvm_handle_exit(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu)
{
u32 exit_reason = kvm_get_exit_reason(vcpu);
vcpu->arch.last_exit = exit_reason;
if (exit_reason < kvm_vti_max_exit_handlers
&& kvm_vti_exit_handlers[exit_reason])
return kvm_vti_exit_handlers[exit_reason](vcpu, kvm_run);
else {
kvm_run->exit_reason = KVM_EXIT_UNKNOWN;
kvm_run->hw.hardware_exit_reason = exit_reason;
}
return 0;
}
static inline void vti_set_rr6(unsigned long rr6)
{
ia64_set_rr(RR6, rr6);
ia64_srlz_i();
}
static int kvm_insert_vmm_mapping(struct kvm_vcpu *vcpu)
{
unsigned long pte;
struct kvm *kvm = vcpu->kvm;
int r;
/*Insert a pair of tr to map vmm*/
pte = pte_val(mk_pte_phys(__pa(kvm_vmm_base), PAGE_KERNEL));
r = ia64_itr_entry(0x3, KVM_VMM_BASE, pte, KVM_VMM_SHIFT);
if (r < 0)
goto out;
vcpu->arch.vmm_tr_slot = r;
/*Insert a pairt of tr to map data of vm*/
pte = pte_val(mk_pte_phys(__pa(kvm->arch.vm_base), PAGE_KERNEL));
r = ia64_itr_entry(0x3, KVM_VM_DATA_BASE,
pte, KVM_VM_DATA_SHIFT);
if (r < 0)
goto out;
vcpu->arch.vm_tr_slot = r;
r = 0;
out:
return r;
}
static void kvm_purge_vmm_mapping(struct kvm_vcpu *vcpu)
{
ia64_ptr_entry(0x3, vcpu->arch.vmm_tr_slot);
ia64_ptr_entry(0x3, vcpu->arch.vm_tr_slot);
}
static int kvm_vcpu_pre_transition(struct kvm_vcpu *vcpu)
{
int cpu = smp_processor_id();
if (vcpu->arch.last_run_cpu != cpu ||
per_cpu(last_vcpu, cpu) != vcpu) {
per_cpu(last_vcpu, cpu) = vcpu;
vcpu->arch.last_run_cpu = cpu;
kvm_flush_tlb_all();
}
vcpu->arch.host_rr6 = ia64_get_rr(RR6);
vti_set_rr6(vcpu->arch.vmm_rr);
return kvm_insert_vmm_mapping(vcpu);
}
static void kvm_vcpu_post_transition(struct kvm_vcpu *vcpu)
{
kvm_purge_vmm_mapping(vcpu);
vti_set_rr6(vcpu->arch.host_rr6);
}
static int vti_vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
{
union context *host_ctx, *guest_ctx;
int r;
/*Get host and guest context with guest address space.*/
host_ctx = kvm_get_host_context(vcpu);
guest_ctx = kvm_get_guest_context(vcpu);
r = kvm_vcpu_pre_transition(vcpu);
if (r < 0)
goto out;
kvm_vmm_info->tramp_entry(host_ctx, guest_ctx);
kvm_vcpu_post_transition(vcpu);
r = 0;
out:
return r;
}
static int __vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
{
int r;
again:
preempt_disable();
kvm_prepare_guest_switch(vcpu);
local_irq_disable();
if (signal_pending(current)) {
local_irq_enable();
preempt_enable();
r = -EINTR;
kvm_run->exit_reason = KVM_EXIT_INTR;
goto out;
}
vcpu->guest_mode = 1;
kvm_guest_enter();
r = vti_vcpu_run(vcpu, kvm_run);
if (r < 0) {
local_irq_enable();
preempt_enable();
kvm_run->exit_reason = KVM_EXIT_FAIL_ENTRY;
goto out;
}
vcpu->arch.launched = 1;
vcpu->guest_mode = 0;
local_irq_enable();
/*
* We must have an instruction between local_irq_enable() and
* kvm_guest_exit(), so the timer interrupt isn't delayed by
* the interrupt shadow. The stat.exits increment will do nicely.
* But we need to prevent reordering, hence this barrier():
*/
barrier();
kvm_guest_exit();
preempt_enable();
r = kvm_handle_exit(kvm_run, vcpu);
if (r > 0) {
if (!need_resched())
goto again;
}
out:
if (r > 0) {
kvm_resched(vcpu);
goto again;
}
return r;
}
static void kvm_set_mmio_data(struct kvm_vcpu *vcpu)
{
struct kvm_mmio_req *p = kvm_get_vcpu_ioreq(vcpu);
if (!vcpu->mmio_is_write)
memcpy(&p->data, vcpu->mmio_data, 8);
p->state = STATE_IORESP_READY;
}
int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
{
int r;
sigset_t sigsaved;
vcpu_load(vcpu);
if (unlikely(vcpu->arch.mp_state == KVM_MP_STATE_UNINITIALIZED)) {
kvm_vcpu_block(vcpu);
vcpu_put(vcpu);
return -EAGAIN;
}
if (vcpu->sigset_active)
sigprocmask(SIG_SETMASK, &vcpu->sigset, &sigsaved);
if (vcpu->mmio_needed) {
memcpy(vcpu->mmio_data, kvm_run->mmio.data, 8);
kvm_set_mmio_data(vcpu);
vcpu->mmio_read_completed = 1;
vcpu->mmio_needed = 0;
}
r = __vcpu_run(vcpu, kvm_run);
if (vcpu->sigset_active)
sigprocmask(SIG_SETMASK, &sigsaved, NULL);
vcpu_put(vcpu);
return r;
}
/*
* Allocate 16M memory for every vm to hold its specific data.
* Its memory map is defined in kvm_host.h.
*/
static struct kvm *kvm_alloc_kvm(void)
{
struct kvm *kvm;
uint64_t vm_base;
vm_base = __get_free_pages(GFP_KERNEL, get_order(KVM_VM_DATA_SIZE));
if (!vm_base)
return ERR_PTR(-ENOMEM);
printk(KERN_DEBUG"kvm: VM data's base Address:0x%lx\n", vm_base);
/* Zero all pages before use! */
memset((void *)vm_base, 0, KVM_VM_DATA_SIZE);
kvm = (struct kvm *)(vm_base + KVM_VM_OFS);
kvm->arch.vm_base = vm_base;
return kvm;
}
struct kvm_io_range {
unsigned long start;
unsigned long size;
unsigned long type;
};
static const struct kvm_io_range io_ranges[] = {
{VGA_IO_START, VGA_IO_SIZE, GPFN_FRAME_BUFFER},
{MMIO_START, MMIO_SIZE, GPFN_LOW_MMIO},
{LEGACY_IO_START, LEGACY_IO_SIZE, GPFN_LEGACY_IO},
{IO_SAPIC_START, IO_SAPIC_SIZE, GPFN_IOSAPIC},
{PIB_START, PIB_SIZE, GPFN_PIB},
};
static void kvm_build_io_pmt(struct kvm *kvm)
{
unsigned long i, j;
/* Mark I/O ranges */
for (i = 0; i < (sizeof(io_ranges) / sizeof(struct kvm_io_range));
i++) {
for (j = io_ranges[i].start;
j < io_ranges[i].start + io_ranges[i].size;
j += PAGE_SIZE)
kvm_set_pmt_entry(kvm, j >> PAGE_SHIFT,
io_ranges[i].type, 0);
}
}
/*Use unused rids to virtualize guest rid.*/
#define GUEST_PHYSICAL_RR0 0x1739
#define GUEST_PHYSICAL_RR4 0x2739
#define VMM_INIT_RR 0x1660
static void kvm_init_vm(struct kvm *kvm)
{
long vm_base;
BUG_ON(!kvm);
kvm->arch.metaphysical_rr0 = GUEST_PHYSICAL_RR0;
kvm->arch.metaphysical_rr4 = GUEST_PHYSICAL_RR4;
kvm->arch.vmm_init_rr = VMM_INIT_RR;
vm_base = kvm->arch.vm_base;
if (vm_base) {
kvm->arch.vhpt_base = vm_base + KVM_VHPT_OFS;
kvm->arch.vtlb_base = vm_base + KVM_VTLB_OFS;
kvm->arch.vpd_base = vm_base + KVM_VPD_OFS;
}
/*
*Fill P2M entries for MMIO/IO ranges
*/
kvm_build_io_pmt(kvm);
}
struct kvm *kvm_arch_create_vm(void)
{
struct kvm *kvm = kvm_alloc_kvm();
if (IS_ERR(kvm))
return ERR_PTR(-ENOMEM);
kvm_init_vm(kvm);
return kvm;
}
static int kvm_vm_ioctl_get_irqchip(struct kvm *kvm,
struct kvm_irqchip *chip)
{
int r;
r = 0;
switch (chip->chip_id) {
case KVM_IRQCHIP_IOAPIC:
memcpy(&chip->chip.ioapic, ioapic_irqchip(kvm),
sizeof(struct kvm_ioapic_state));
break;
default:
r = -EINVAL;
break;
}
return r;
}
static int kvm_vm_ioctl_set_irqchip(struct kvm *kvm, struct kvm_irqchip *chip)
{
int r;
r = 0;
switch (chip->chip_id) {
case KVM_IRQCHIP_IOAPIC:
memcpy(ioapic_irqchip(kvm),
&chip->chip.ioapic,
sizeof(struct kvm_ioapic_state));
break;
default:
r = -EINVAL;
break;
}
return r;
}
#define RESTORE_REGS(_x) vcpu->arch._x = regs->_x
int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
{
int i;
struct vpd *vpd = to_host(vcpu->kvm, vcpu->arch.vpd);
int r;
vcpu_load(vcpu);
for (i = 0; i < 16; i++) {
vpd->vgr[i] = regs->vpd.vgr[i];
vpd->vbgr[i] = regs->vpd.vbgr[i];
}
for (i = 0; i < 128; i++)
vpd->vcr[i] = regs->vpd.vcr[i];
vpd->vhpi = regs->vpd.vhpi;
vpd->vnat = regs->vpd.vnat;
vpd->vbnat = regs->vpd.vbnat;
vpd->vpsr = regs->vpd.vpsr;
vpd->vpr = regs->vpd.vpr;
r = -EFAULT;
r = copy_from_user(&vcpu->arch.guest, regs->saved_guest,
sizeof(union context));
if (r)
goto out;
r = copy_from_user(vcpu + 1, regs->saved_stack +
sizeof(struct kvm_vcpu),
IA64_STK_OFFSET - sizeof(struct kvm_vcpu));
if (r)
goto out;
vcpu->arch.exit_data =
((struct kvm_vcpu *)(regs->saved_stack))->arch.exit_data;
RESTORE_REGS(mp_state);
RESTORE_REGS(vmm_rr);
memcpy(vcpu->arch.itrs, regs->itrs, sizeof(struct thash_data) * NITRS);
memcpy(vcpu->arch.dtrs, regs->dtrs, sizeof(struct thash_data) * NDTRS);
RESTORE_REGS(itr_regions);
RESTORE_REGS(dtr_regions);
RESTORE_REGS(tc_regions);
RESTORE_REGS(irq_check);
RESTORE_REGS(itc_check);
RESTORE_REGS(timer_check);
RESTORE_REGS(timer_pending);
RESTORE_REGS(last_itc);
for (i = 0; i < 8; i++) {
vcpu->arch.vrr[i] = regs->vrr[i];
vcpu->arch.ibr[i] = regs->ibr[i];
vcpu->arch.dbr[i] = regs->dbr[i];
}
for (i = 0; i < 4; i++)
vcpu->arch.insvc[i] = regs->insvc[i];
RESTORE_REGS(xtp);
RESTORE_REGS(metaphysical_rr0);
RESTORE_REGS(metaphysical_rr4);
RESTORE_REGS(metaphysical_saved_rr0);
RESTORE_REGS(metaphysical_saved_rr4);
RESTORE_REGS(fp_psr);
RESTORE_REGS(saved_gp);
vcpu->arch.irq_new_pending = 1;
vcpu->arch.itc_offset = regs->saved_itc - ia64_getreg(_IA64_REG_AR_ITC);
set_bit(KVM_REQ_RESUME, &vcpu->requests);
vcpu_put(vcpu);
r = 0;
out:
return r;
}
long kvm_arch_vm_ioctl(struct file *filp,
unsigned int ioctl, unsigned long arg)
{
struct kvm *kvm = filp->private_data;
void __user *argp = (void __user *)arg;
int r = -EINVAL;
switch (ioctl) {
case KVM_SET_MEMORY_REGION: {
struct kvm_memory_region kvm_mem;
struct kvm_userspace_memory_region kvm_userspace_mem;
r = -EFAULT;
if (copy_from_user(&kvm_mem, argp, sizeof kvm_mem))
goto out;
kvm_userspace_mem.slot = kvm_mem.slot;
kvm_userspace_mem.flags = kvm_mem.flags;
kvm_userspace_mem.guest_phys_addr =
kvm_mem.guest_phys_addr;
kvm_userspace_mem.memory_size = kvm_mem.memory_size;
r = kvm_vm_ioctl_set_memory_region(kvm,
&kvm_userspace_mem, 0);
if (r)
goto out;
break;
}
case KVM_CREATE_IRQCHIP:
r = -EFAULT;
r = kvm_ioapic_init(kvm);
if (r)
goto out;
break;
case KVM_IRQ_LINE: {
struct kvm_irq_level irq_event;
r = -EFAULT;
if (copy_from_user(&irq_event, argp, sizeof irq_event))
goto out;
if (irqchip_in_kernel(kvm)) {
mutex_lock(&kvm->lock);
kvm_ioapic_set_irq(kvm->arch.vioapic,
irq_event.irq,
irq_event.level);
mutex_unlock(&kvm->lock);
r = 0;
}
break;
}
case KVM_GET_IRQCHIP: {
/* 0: PIC master, 1: PIC slave, 2: IOAPIC */
struct kvm_irqchip chip;
r = -EFAULT;
if (copy_from_user(&chip, argp, sizeof chip))
goto out;
r = -ENXIO;
if (!irqchip_in_kernel(kvm))
goto out;
r = kvm_vm_ioctl_get_irqchip(kvm, &chip);
if (r)
goto out;
r = -EFAULT;
if (copy_to_user(argp, &chip, sizeof chip))
goto out;
r = 0;
break;
}
case KVM_SET_IRQCHIP: {
/* 0: PIC master, 1: PIC slave, 2: IOAPIC */
struct kvm_irqchip chip;
r = -EFAULT;
if (copy_from_user(&chip, argp, sizeof chip))
goto out;
r = -ENXIO;
if (!irqchip_in_kernel(kvm))
goto out;
r = kvm_vm_ioctl_set_irqchip(kvm, &chip);
if (r)
goto out;
r = 0;
break;
}
default:
;
}
out:
return r;
}
int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu,
struct kvm_sregs *sregs)
{
return -EINVAL;
}
int kvm_arch_vcpu_ioctl_get_sregs(struct kvm_vcpu *vcpu,
struct kvm_sregs *sregs)
{
return -EINVAL;
}
int kvm_arch_vcpu_ioctl_translate(struct kvm_vcpu *vcpu,
struct kvm_translation *tr)
{
return -EINVAL;
}
static int kvm_alloc_vmm_area(void)
{
if (!kvm_vmm_base && (kvm_vm_buffer_size < KVM_VM_BUFFER_SIZE)) {
kvm_vmm_base = __get_free_pages(GFP_KERNEL,
get_order(KVM_VMM_SIZE));
if (!kvm_vmm_base)
return -ENOMEM;
memset((void *)kvm_vmm_base, 0, KVM_VMM_SIZE);
kvm_vm_buffer = kvm_vmm_base + VMM_SIZE;
printk(KERN_DEBUG"kvm:VMM's Base Addr:0x%lx, vm_buffer:0x%lx\n",
kvm_vmm_base, kvm_vm_buffer);
}
return 0;
}
static void kvm_free_vmm_area(void)
{
if (kvm_vmm_base) {
/*Zero this area before free to avoid bits leak!!*/
memset((void *)kvm_vmm_base, 0, KVM_VMM_SIZE);
free_pages(kvm_vmm_base, get_order(KVM_VMM_SIZE));
kvm_vmm_base = 0;
kvm_vm_buffer = 0;
kvm_vsa_base = 0;
}
}
static void vti_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
{
}
static int vti_init_vpd(struct kvm_vcpu *vcpu)
{
int i;
union cpuid3_t cpuid3;
struct vpd *vpd = to_host(vcpu->kvm, vcpu->arch.vpd);
if (IS_ERR(vpd))
return PTR_ERR(vpd);
/* CPUID init */
for (i = 0; i < 5; i++)
vpd->vcpuid[i] = ia64_get_cpuid(i);
/* Limit the CPUID number to 5 */
cpuid3.value = vpd->vcpuid[3];
cpuid3.number = 4; /* 5 - 1 */
vpd->vcpuid[3] = cpuid3.value;
/*Set vac and vdc fields*/
vpd->vac.a_from_int_cr = 1;
vpd->vac.a_to_int_cr = 1;
vpd->vac.a_from_psr = 1;
vpd->vac.a_from_cpuid = 1;
vpd->vac.a_cover = 1;
vpd->vac.a_bsw = 1;
vpd->vac.a_int = 1;
vpd->vdc.d_vmsw = 1;
/*Set virtual buffer*/
vpd->virt_env_vaddr = KVM_VM_BUFFER_BASE;
return 0;
}
static int vti_create_vp(struct kvm_vcpu *vcpu)
{
long ret;
struct vpd *vpd = vcpu->arch.vpd;
unsigned long vmm_ivt;
vmm_ivt = kvm_vmm_info->vmm_ivt;
printk(KERN_DEBUG "kvm: vcpu:%p,ivt: 0x%lx\n", vcpu, vmm_ivt);
ret = ia64_pal_vp_create((u64 *)vpd, (u64 *)vmm_ivt, 0);
if (ret) {
printk(KERN_ERR"kvm: ia64_pal_vp_create failed!\n");
return -EINVAL;
}
return 0;
}
static void init_ptce_info(struct kvm_vcpu *vcpu)
{
ia64_ptce_info_t ptce = {0};
ia64_get_ptce(&ptce);
vcpu->arch.ptce_base = ptce.base;
vcpu->arch.ptce_count[0] = ptce.count[0];
vcpu->arch.ptce_count[1] = ptce.count[1];
vcpu->arch.ptce_stride[0] = ptce.stride[0];
vcpu->arch.ptce_stride[1] = ptce.stride[1];
}
static void kvm_migrate_hlt_timer(struct kvm_vcpu *vcpu)
{
struct hrtimer *p_ht = &vcpu->arch.hlt_timer;
if (hrtimer_cancel(p_ht))
hrtimer_start(p_ht, p_ht->expires, HRTIMER_MODE_ABS);
}
static enum hrtimer_restart hlt_timer_fn(struct hrtimer *data)
{
struct kvm_vcpu *vcpu;
wait_queue_head_t *q;
vcpu = container_of(data, struct kvm_vcpu, arch.hlt_timer);
if (vcpu->arch.mp_state != KVM_MP_STATE_HALTED)
goto out;
q = &vcpu->wq;
if (waitqueue_active(q)) {
vcpu->arch.mp_state = KVM_MP_STATE_RUNNABLE;
wake_up_interruptible(q);
}
out:
vcpu->arch.timer_check = 1;
return HRTIMER_NORESTART;
}
#define PALE_RESET_ENTRY 0x80000000ffffffb0UL
int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
{
struct kvm_vcpu *v;
int r;
int i;
long itc_offset;
struct kvm *kvm = vcpu->kvm;
struct kvm_pt_regs *regs = vcpu_regs(vcpu);
union context *p_ctx = &vcpu->arch.guest;
struct kvm_vcpu *vmm_vcpu = to_guest(vcpu->kvm, vcpu);
/*Init vcpu context for first run.*/
if (IS_ERR(vmm_vcpu))
return PTR_ERR(vmm_vcpu);
if (vcpu->vcpu_id == 0) {
vcpu->arch.mp_state = KVM_MP_STATE_RUNNABLE;
/*Set entry address for first run.*/
regs->cr_iip = PALE_RESET_ENTRY;
/*Initilize itc offset for vcpus*/
itc_offset = 0UL - ia64_getreg(_IA64_REG_AR_ITC);
for (i = 0; i < MAX_VCPU_NUM; i++) {
v = (struct kvm_vcpu *)((char *)vcpu + VCPU_SIZE * i);
v->arch.itc_offset = itc_offset;
v->arch.last_itc = 0;
}
} else
vcpu->arch.mp_state = KVM_MP_STATE_UNINITIALIZED;
r = -ENOMEM;
vcpu->arch.apic = kzalloc(sizeof(struct kvm_lapic), GFP_KERNEL);
if (!vcpu->arch.apic)
goto out;
vcpu->arch.apic->vcpu = vcpu;
p_ctx->gr[1] = 0;
p_ctx->gr[12] = (unsigned long)((char *)vmm_vcpu + IA64_STK_OFFSET);
p_ctx->gr[13] = (unsigned long)vmm_vcpu;
p_ctx->psr = 0x1008522000UL;
p_ctx->ar[40] = FPSR_DEFAULT; /*fpsr*/
p_ctx->caller_unat = 0;
p_ctx->pr = 0x0;
p_ctx->ar[36] = 0x0; /*unat*/
p_ctx->ar[19] = 0x0; /*rnat*/
p_ctx->ar[18] = (unsigned long)vmm_vcpu +
((sizeof(struct kvm_vcpu)+15) & ~15);
p_ctx->ar[64] = 0x0; /*pfs*/
p_ctx->cr[0] = 0x7e04UL;
p_ctx->cr[2] = (unsigned long)kvm_vmm_info->vmm_ivt;
p_ctx->cr[8] = 0x3c;
/*Initilize region register*/
p_ctx->rr[0] = 0x30;
p_ctx->rr[1] = 0x30;
p_ctx->rr[2] = 0x30;
p_ctx->rr[3] = 0x30;
p_ctx->rr[4] = 0x30;
p_ctx->rr[5] = 0x30;
p_ctx->rr[7] = 0x30;
/*Initilize branch register 0*/
p_ctx->br[0] = *(unsigned long *)kvm_vmm_info->vmm_entry;
vcpu->arch.vmm_rr = kvm->arch.vmm_init_rr;
vcpu->arch.metaphysical_rr0 = kvm->arch.metaphysical_rr0;
vcpu->arch.metaphysical_rr4 = kvm->arch.metaphysical_rr4;
hrtimer_init(&vcpu->arch.hlt_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
vcpu->arch.hlt_timer.function = hlt_timer_fn;
vcpu->arch.last_run_cpu = -1;
vcpu->arch.vpd = (struct vpd *)VPD_ADDR(vcpu->vcpu_id);
vcpu->arch.vsa_base = kvm_vsa_base;
vcpu->arch.__gp = kvm_vmm_gp;
vcpu->arch.dirty_log_lock_pa = __pa(&kvm->arch.dirty_log_lock);
vcpu->arch.vhpt.hash = (struct thash_data *)VHPT_ADDR(vcpu->vcpu_id);
vcpu->arch.vtlb.hash = (struct thash_data *)VTLB_ADDR(vcpu->vcpu_id);
init_ptce_info(vcpu);
r = 0;
out:
return r;
}
static int vti_vcpu_setup(struct kvm_vcpu *vcpu, int id)
{
unsigned long psr;
int r;
local_irq_save(psr);
r = kvm_insert_vmm_mapping(vcpu);
if (r)
goto fail;
r = kvm_vcpu_init(vcpu, vcpu->kvm, id);
if (r)
goto fail;
r = vti_init_vpd(vcpu);
if (r) {
printk(KERN_DEBUG"kvm: vpd init error!!\n");
goto uninit;
}
r = vti_create_vp(vcpu);
if (r)
goto uninit;
kvm_purge_vmm_mapping(vcpu);
local_irq_restore(psr);
return 0;
uninit:
kvm_vcpu_uninit(vcpu);
fail:
local_irq_restore(psr);
return r;
}
struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm,
unsigned int id)
{
struct kvm_vcpu *vcpu;
unsigned long vm_base = kvm->arch.vm_base;
int r;
int cpu;
r = -ENOMEM;
if (!vm_base) {
printk(KERN_ERR"kvm: Create vcpu[%d] error!\n", id);
goto fail;
}
vcpu = (struct kvm_vcpu *)(vm_base + KVM_VCPU_OFS + VCPU_SIZE * id);
vcpu->kvm = kvm;
cpu = get_cpu();
vti_vcpu_load(vcpu, cpu);
r = vti_vcpu_setup(vcpu, id);
put_cpu();
if (r) {
printk(KERN_DEBUG"kvm: vcpu_setup error!!\n");
goto fail;
}
return vcpu;
fail:
return ERR_PTR(r);
}
int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu)
{
return 0;
}
int kvm_arch_vcpu_ioctl_get_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu)
{
return -EINVAL;
}
int kvm_arch_vcpu_ioctl_set_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu)
{
return -EINVAL;
}
int kvm_arch_vcpu_ioctl_debug_guest(struct kvm_vcpu *vcpu,
struct kvm_debug_guest *dbg)
{
return -EINVAL;
}
static void free_kvm(struct kvm *kvm)
{
unsigned long vm_base = kvm->arch.vm_base;
if (vm_base) {
memset((void *)vm_base, 0, KVM_VM_DATA_SIZE);
free_pages(vm_base, get_order(KVM_VM_DATA_SIZE));
}
}
static void kvm_release_vm_pages(struct kvm *kvm)
{
struct kvm_memory_slot *memslot;
int i, j;
unsigned long base_gfn;
for (i = 0; i < kvm->nmemslots; i++) {
memslot = &kvm->memslots[i];
base_gfn = memslot->base_gfn;
for (j = 0; j < memslot->npages; j++) {
if (memslot->rmap[j])
put_page((struct page *)memslot->rmap[j]);
}
}
}
void kvm_arch_destroy_vm(struct kvm *kvm)
{
kfree(kvm->arch.vioapic);
kvm_release_vm_pages(kvm);
kvm_free_physmem(kvm);
free_kvm(kvm);
}
void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
{
}
void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
{
if (cpu != vcpu->cpu) {
vcpu->cpu = cpu;
if (vcpu->arch.ht_active)
kvm_migrate_hlt_timer(vcpu);
}
}
#define SAVE_REGS(_x) regs->_x = vcpu->arch._x
int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
{
int i;
int r;
struct vpd *vpd = to_host(vcpu->kvm, vcpu->arch.vpd);
vcpu_load(vcpu);
for (i = 0; i < 16; i++) {
regs->vpd.vgr[i] = vpd->vgr[i];
regs->vpd.vbgr[i] = vpd->vbgr[i];
}
for (i = 0; i < 128; i++)
regs->vpd.vcr[i] = vpd->vcr[i];
regs->vpd.vhpi = vpd->vhpi;
regs->vpd.vnat = vpd->vnat;
regs->vpd.vbnat = vpd->vbnat;
regs->vpd.vpsr = vpd->vpsr;
regs->vpd.vpr = vpd->vpr;
r = -EFAULT;
r = copy_to_user(regs->saved_guest, &vcpu->arch.guest,
sizeof(union context));
if (r)
goto out;
r = copy_to_user(regs->saved_stack, (void *)vcpu, IA64_STK_OFFSET);
if (r)
goto out;
SAVE_REGS(mp_state);
SAVE_REGS(vmm_rr);
memcpy(regs->itrs, vcpu->arch.itrs, sizeof(struct thash_data) * NITRS);
memcpy(regs->dtrs, vcpu->arch.dtrs, sizeof(struct thash_data) * NDTRS);
SAVE_REGS(itr_regions);
SAVE_REGS(dtr_regions);
SAVE_REGS(tc_regions);
SAVE_REGS(irq_check);
SAVE_REGS(itc_check);
SAVE_REGS(timer_check);
SAVE_REGS(timer_pending);
SAVE_REGS(last_itc);
for (i = 0; i < 8; i++) {
regs->vrr[i] = vcpu->arch.vrr[i];
regs->ibr[i] = vcpu->arch.ibr[i];
regs->dbr[i] = vcpu->arch.dbr[i];
}
for (i = 0; i < 4; i++)
regs->insvc[i] = vcpu->arch.insvc[i];
regs->saved_itc = vcpu->arch.itc_offset + ia64_getreg(_IA64_REG_AR_ITC);
SAVE_REGS(xtp);
SAVE_REGS(metaphysical_rr0);
SAVE_REGS(metaphysical_rr4);
SAVE_REGS(metaphysical_saved_rr0);
SAVE_REGS(metaphysical_saved_rr4);
SAVE_REGS(fp_psr);
SAVE_REGS(saved_gp);
vcpu_put(vcpu);
r = 0;
out:
return r;
}
void kvm_arch_vcpu_uninit(struct kvm_vcpu *vcpu)
{
hrtimer_cancel(&vcpu->arch.hlt_timer);
kfree(vcpu->arch.apic);
}
long kvm_arch_vcpu_ioctl(struct file *filp,
unsigned int ioctl, unsigned long arg)
{
return -EINVAL;
}
int kvm_arch_set_memory_region(struct kvm *kvm,
struct kvm_userspace_memory_region *mem,
struct kvm_memory_slot old,
int user_alloc)
{
unsigned long i;
struct page *page;
int npages = mem->memory_size >> PAGE_SHIFT;
struct kvm_memory_slot *memslot = &kvm->memslots[mem->slot];
unsigned long base_gfn = memslot->base_gfn;
for (i = 0; i < npages; i++) {
page = gfn_to_page(kvm, base_gfn + i);
kvm_set_pmt_entry(kvm, base_gfn + i,
page_to_pfn(page) << PAGE_SHIFT,
_PAGE_AR_RWX|_PAGE_MA_WB);
memslot->rmap[i] = (unsigned long)page;
}
return 0;
}
void kvm_arch_flush_shadow(struct kvm *kvm)
{
}
long kvm_arch_dev_ioctl(struct file *filp,
unsigned int ioctl, unsigned long arg)
{
return -EINVAL;
}
void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
{
kvm_vcpu_uninit(vcpu);
}
static int vti_cpu_has_kvm_support(void)
{
long avail = 1, status = 1, control = 1;
long ret;
ret = ia64_pal_proc_get_features(&avail, &status, &control, 0);
if (ret)
goto out;
if (!(avail & PAL_PROC_VM_BIT))
goto out;
printk(KERN_DEBUG"kvm: Hardware Supports VT\n");
ret = ia64_pal_vp_env_info(&kvm_vm_buffer_size, &vp_env_info);
if (ret)
goto out;
printk(KERN_DEBUG"kvm: VM Buffer Size:0x%lx\n", kvm_vm_buffer_size);
if (!(vp_env_info & VP_OPCODE)) {
printk(KERN_WARNING"kvm: No opcode ability on hardware, "
"vm_env_info:0x%lx\n", vp_env_info);
}
return 1;
out:
return 0;
}
static int kvm_relocate_vmm(struct kvm_vmm_info *vmm_info,
struct module *module)
{
unsigned long module_base;
unsigned long vmm_size;
unsigned long vmm_offset, func_offset, fdesc_offset;
struct fdesc *p_fdesc;
BUG_ON(!module);
if (!kvm_vmm_base) {
printk("kvm: kvm area hasn't been initilized yet!!\n");
return -EFAULT;
}
/*Calculate new position of relocated vmm module.*/
module_base = (unsigned long)module->module_core;
vmm_size = module->core_size;
if (unlikely(vmm_size > KVM_VMM_SIZE))
return -EFAULT;
memcpy((void *)kvm_vmm_base, (void *)module_base, vmm_size);
kvm_flush_icache(kvm_vmm_base, vmm_size);
/*Recalculate kvm_vmm_info based on new VMM*/
vmm_offset = vmm_info->vmm_ivt - module_base;
kvm_vmm_info->vmm_ivt = KVM_VMM_BASE + vmm_offset;
printk(KERN_DEBUG"kvm: Relocated VMM's IVT Base Addr:%lx\n",
kvm_vmm_info->vmm_ivt);
fdesc_offset = (unsigned long)vmm_info->vmm_entry - module_base;
kvm_vmm_info->vmm_entry = (kvm_vmm_entry *)(KVM_VMM_BASE +
fdesc_offset);
func_offset = *(unsigned long *)vmm_info->vmm_entry - module_base;
p_fdesc = (struct fdesc *)(kvm_vmm_base + fdesc_offset);
p_fdesc->ip = KVM_VMM_BASE + func_offset;
p_fdesc->gp = KVM_VMM_BASE+(p_fdesc->gp - module_base);
printk(KERN_DEBUG"kvm: Relocated VMM's Init Entry Addr:%lx\n",
KVM_VMM_BASE+func_offset);
fdesc_offset = (unsigned long)vmm_info->tramp_entry - module_base;
kvm_vmm_info->tramp_entry = (kvm_tramp_entry *)(KVM_VMM_BASE +
fdesc_offset);
func_offset = *(unsigned long *)vmm_info->tramp_entry - module_base;
p_fdesc = (struct fdesc *)(kvm_vmm_base + fdesc_offset);
p_fdesc->ip = KVM_VMM_BASE + func_offset;
p_fdesc->gp = KVM_VMM_BASE + (p_fdesc->gp - module_base);
kvm_vmm_gp = p_fdesc->gp;
printk(KERN_DEBUG"kvm: Relocated VMM's Entry IP:%p\n",
kvm_vmm_info->vmm_entry);
printk(KERN_DEBUG"kvm: Relocated VMM's Trampoline Entry IP:0x%lx\n",
KVM_VMM_BASE + func_offset);
return 0;
}
int kvm_arch_init(void *opaque)
{
int r;
struct kvm_vmm_info *vmm_info = (struct kvm_vmm_info *)opaque;
if (!vti_cpu_has_kvm_support()) {
printk(KERN_ERR "kvm: No Hardware Virtualization Support!\n");
r = -EOPNOTSUPP;
goto out;
}
if (kvm_vmm_info) {
printk(KERN_ERR "kvm: Already loaded VMM module!\n");
r = -EEXIST;
goto out;
}
r = -ENOMEM;
kvm_vmm_info = kzalloc(sizeof(struct kvm_vmm_info), GFP_KERNEL);
if (!kvm_vmm_info)
goto out;
if (kvm_alloc_vmm_area())
goto out_free0;
r = kvm_relocate_vmm(vmm_info, vmm_info->module);
if (r)
goto out_free1;
return 0;
out_free1:
kvm_free_vmm_area();
out_free0:
kfree(kvm_vmm_info);
out:
return r;
}
void kvm_arch_exit(void)
{
kvm_free_vmm_area();
kfree(kvm_vmm_info);
kvm_vmm_info = NULL;
}
static int kvm_ia64_sync_dirty_log(struct kvm *kvm,
struct kvm_dirty_log *log)
{
struct kvm_memory_slot *memslot;
int r, i;
long n, base;
unsigned long *dirty_bitmap = (unsigned long *)((void *)kvm - KVM_VM_OFS
+ KVM_MEM_DIRTY_LOG_OFS);
r = -EINVAL;
if (log->slot >= KVM_MEMORY_SLOTS)
goto out;
memslot = &kvm->memslots[log->slot];
r = -ENOENT;
if (!memslot->dirty_bitmap)
goto out;
n = ALIGN(memslot->npages, BITS_PER_LONG) / 8;
base = memslot->base_gfn / BITS_PER_LONG;
for (i = 0; i < n/sizeof(long); ++i) {
memslot->dirty_bitmap[i] = dirty_bitmap[base + i];
dirty_bitmap[base + i] = 0;
}
r = 0;
out:
return r;
}
int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm,
struct kvm_dirty_log *log)
{
int r;
int n;
struct kvm_memory_slot *memslot;
int is_dirty = 0;
spin_lock(&kvm->arch.dirty_log_lock);
r = kvm_ia64_sync_dirty_log(kvm, log);
if (r)
goto out;
r = kvm_get_dirty_log(kvm, log, &is_dirty);
if (r)
goto out;
/* If nothing is dirty, don't bother messing with page tables. */
if (is_dirty) {
kvm_flush_remote_tlbs(kvm);
memslot = &kvm->memslots[log->slot];
n = ALIGN(memslot->npages, BITS_PER_LONG) / 8;
memset(memslot->dirty_bitmap, 0, n);
}
r = 0;
out:
spin_unlock(&kvm->arch.dirty_log_lock);
return r;
}
int kvm_arch_hardware_setup(void)
{
return 0;
}
void kvm_arch_hardware_unsetup(void)
{
}
static void vcpu_kick_intr(void *info)
{
#ifdef DEBUG
struct kvm_vcpu *vcpu = (struct kvm_vcpu *)info;
printk(KERN_DEBUG"vcpu_kick_intr %p \n", vcpu);
#endif
}
void kvm_vcpu_kick(struct kvm_vcpu *vcpu)
{
int ipi_pcpu = vcpu->cpu;
if (waitqueue_active(&vcpu->wq))
wake_up_interruptible(&vcpu->wq);
if (vcpu->guest_mode)
smp_call_function_single(ipi_pcpu, vcpu_kick_intr, vcpu, 0);
}
int kvm_apic_set_irq(struct kvm_vcpu *vcpu, u8 vec, u8 trig)
{
struct vpd *vpd = to_host(vcpu->kvm, vcpu->arch.vpd);
if (!test_and_set_bit(vec, &vpd->irr[0])) {
vcpu->arch.irq_new_pending = 1;
if (vcpu->arch.mp_state == KVM_MP_STATE_RUNNABLE)
kvm_vcpu_kick(vcpu);
else if (vcpu->arch.mp_state == KVM_MP_STATE_HALTED) {
vcpu->arch.mp_state = KVM_MP_STATE_RUNNABLE;
if (waitqueue_active(&vcpu->wq))
wake_up_interruptible(&vcpu->wq);
}
return 1;
}
return 0;
}
int kvm_apic_match_physical_addr(struct kvm_lapic *apic, u16 dest)
{
return apic->vcpu->vcpu_id == dest;
}
int kvm_apic_match_logical_addr(struct kvm_lapic *apic, u8 mda)
{
return 0;
}
struct kvm_vcpu *kvm_get_lowest_prio_vcpu(struct kvm *kvm, u8 vector,
unsigned long bitmap)
{
struct kvm_vcpu *lvcpu = kvm->vcpus[0];
int i;
for (i = 1; i < KVM_MAX_VCPUS; i++) {
if (!kvm->vcpus[i])
continue;
if (lvcpu->arch.xtp > kvm->vcpus[i]->arch.xtp)
lvcpu = kvm->vcpus[i];
}
return lvcpu;
}
static int find_highest_bits(int *dat)
{
u32 bits, bitnum;
int i;
/* loop for all 256 bits */
for (i = 7; i >= 0 ; i--) {
bits = dat[i];
if (bits) {
bitnum = fls(bits);
return i * 32 + bitnum - 1;
}
}
return -1;
}
int kvm_highest_pending_irq(struct kvm_vcpu *vcpu)
{
struct vpd *vpd = to_host(vcpu->kvm, vcpu->arch.vpd);
if (vpd->irr[0] & (1UL << NMI_VECTOR))
return NMI_VECTOR;
if (vpd->irr[0] & (1UL << ExtINT_VECTOR))
return ExtINT_VECTOR;
return find_highest_bits((int *)&vpd->irr[0]);
}
int kvm_cpu_has_interrupt(struct kvm_vcpu *vcpu)
{
if (kvm_highest_pending_irq(vcpu) != -1)
return 1;
return 0;
}
int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu)
{
return 0;
}
gfn_t unalias_gfn(struct kvm *kvm, gfn_t gfn)
{
return gfn;
}
int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu)
{
return vcpu->arch.mp_state == KVM_MP_STATE_RUNNABLE;
}
int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu,
struct kvm_mp_state *mp_state)
{
return -EINVAL;
}
int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu,
struct kvm_mp_state *mp_state)
{
return -EINVAL;
}