2012-11-22 10:34:02 +08:00
|
|
|
/*
|
|
|
|
* This file is subject to the terms and conditions of the GNU General Public
|
|
|
|
* License. See the file "COPYING" in the main directory of this archive
|
|
|
|
* for more details.
|
|
|
|
*
|
|
|
|
* KVM/MIPS: MIPS specific KVM APIs
|
|
|
|
*
|
|
|
|
* Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved.
|
|
|
|
* Authors: Sanjay Lal <sanjayl@kymasys.com>
|
2014-06-27 03:11:34 +08:00
|
|
|
*/
|
2012-11-22 10:34:02 +08:00
|
|
|
|
2016-06-16 02:29:56 +08:00
|
|
|
#include <linux/bitops.h>
|
2012-11-22 10:34:02 +08:00
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/err.h>
|
2014-11-18 22:09:12 +08:00
|
|
|
#include <linux/kdebug.h>
|
2012-11-22 10:34:02 +08:00
|
|
|
#include <linux/module.h>
|
2016-10-19 07:24:27 +08:00
|
|
|
#include <linux/uaccess.h>
|
2012-11-22 10:34:02 +08:00
|
|
|
#include <linux/vmalloc.h>
|
2017-02-03 02:15:33 +08:00
|
|
|
#include <linux/sched/signal.h>
|
2012-11-22 10:34:02 +08:00
|
|
|
#include <linux/fs.h>
|
2018-10-31 06:09:49 +08:00
|
|
|
#include <linux/memblock.h>
|
2020-06-09 12:32:42 +08:00
|
|
|
#include <linux/pgtable.h>
|
2017-02-03 02:15:33 +08:00
|
|
|
|
KVM: MIPS: Don't leak FPU/DSP to guest
The FPU and DSP are enabled via the CP0 Status CU1 and MX bits by
kvm_mips_set_c0_status() on a guest exit, presumably in case there is
active state that needs saving if pre-emption occurs. However neither of
these bits are cleared again when returning to the guest.
This effectively gives the guest access to the FPU/DSP hardware after
the first guest exit even though it is not aware of its presence,
allowing FP instructions in guest user code to intermittently actually
execute instead of trapping into the guest OS for emulation. It will
then read & manipulate the hardware FP registers which technically
belong to the user process (e.g. QEMU), or are stale from another user
process. It can also crash the guest OS by causing an FP exception, for
which a guest exception handler won't have been registered.
First lets save and disable the FPU (and MSA) state with lose_fpu(1)
before entering the guest. This simplifies the problem, especially for
when guest FPU/MSA support is added in the future, and prevents FR=1 FPU
state being live when the FR bit gets cleared for the guest, which
according to the architecture causes the contents of the FPU and vector
registers to become UNPREDICTABLE.
We can then safely remove the enabling of the FPU in
kvm_mips_set_c0_status(), since there should never be any active FPU or
MSA state to save at pre-emption, which should plug the FPU leak.
DSP state is always live rather than being lazily restored, so for that
it is simpler to just clear the MX bit again when re-entering the guest.
Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: Sanjay Lal <sanjayl@kymasys.com>
Cc: Gleb Natapov <gleb@kernel.org>
Cc: kvm@vger.kernel.org
Cc: linux-mips@linux-mips.org
Cc: <stable@vger.kernel.org> # v3.10+: 044f0f03eca0: MIPS: KVM: Deliver guest interrupts
Cc: <stable@vger.kernel.org> # v3.10+
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2015-02-05 01:06:37 +08:00
|
|
|
#include <asm/fpu.h>
|
2012-11-22 10:34:02 +08:00
|
|
|
#include <asm/page.h>
|
|
|
|
#include <asm/cacheflush.h>
|
|
|
|
#include <asm/mmu_context.h>
|
2015-05-01 20:50:18 +08:00
|
|
|
#include <asm/pgalloc.h>
|
2012-11-22 10:34:02 +08:00
|
|
|
|
|
|
|
#include <linux/kvm_host.h>
|
|
|
|
|
2014-06-27 03:11:38 +08:00
|
|
|
#include "interrupt.h"
|
|
|
|
#include "commpage.h"
|
2012-11-22 10:34:02 +08:00
|
|
|
|
|
|
|
#define CREATE_TRACE_POINTS
|
|
|
|
#include "trace.h"
|
|
|
|
|
|
|
|
#ifndef VECTORSPACING
|
|
|
|
#define VECTORSPACING 0x100 /* for EI/VI mode */
|
|
|
|
#endif
|
|
|
|
|
|
|
|
struct kvm_stats_debugfs_item debugfs_entries[] = {
|
2020-04-14 23:56:25 +08:00
|
|
|
VCPU_STAT("wait", wait_exits),
|
|
|
|
VCPU_STAT("cache", cache_exits),
|
|
|
|
VCPU_STAT("signal", signal_exits),
|
|
|
|
VCPU_STAT("interrupt", int_exits),
|
|
|
|
VCPU_STAT("cop_unusable", cop_unusable_exits),
|
|
|
|
VCPU_STAT("tlbmod", tlbmod_exits),
|
|
|
|
VCPU_STAT("tlbmiss_ld", tlbmiss_ld_exits),
|
|
|
|
VCPU_STAT("tlbmiss_st", tlbmiss_st_exits),
|
|
|
|
VCPU_STAT("addrerr_st", addrerr_st_exits),
|
|
|
|
VCPU_STAT("addrerr_ld", addrerr_ld_exits),
|
|
|
|
VCPU_STAT("syscall", syscall_exits),
|
|
|
|
VCPU_STAT("resvd_inst", resvd_inst_exits),
|
|
|
|
VCPU_STAT("break_inst", break_inst_exits),
|
|
|
|
VCPU_STAT("trap_inst", trap_inst_exits),
|
|
|
|
VCPU_STAT("msa_fpe", msa_fpe_exits),
|
|
|
|
VCPU_STAT("fpe", fpe_exits),
|
|
|
|
VCPU_STAT("msa_disabled", msa_disabled_exits),
|
|
|
|
VCPU_STAT("flush_dcache", flush_dcache_exits),
|
2017-03-14 18:15:18 +08:00
|
|
|
#ifdef CONFIG_KVM_MIPS_VZ
|
2020-04-14 23:56:25 +08:00
|
|
|
VCPU_STAT("vz_gpsi", vz_gpsi_exits),
|
|
|
|
VCPU_STAT("vz_gsfc", vz_gsfc_exits),
|
|
|
|
VCPU_STAT("vz_hc", vz_hc_exits),
|
|
|
|
VCPU_STAT("vz_grr", vz_grr_exits),
|
|
|
|
VCPU_STAT("vz_gva", vz_gva_exits),
|
|
|
|
VCPU_STAT("vz_ghfc", vz_ghfc_exits),
|
|
|
|
VCPU_STAT("vz_gpa", vz_gpa_exits),
|
|
|
|
VCPU_STAT("vz_resvd", vz_resvd_exits),
|
2020-06-15 14:56:55 +08:00
|
|
|
#ifdef CONFIG_CPU_LOONGSON64
|
2020-05-23 15:56:38 +08:00
|
|
|
VCPU_STAT("vz_cpucfg", vz_cpucfg_exits),
|
2020-06-15 14:56:55 +08:00
|
|
|
#endif
|
2017-03-14 18:15:18 +08:00
|
|
|
#endif
|
2020-04-14 23:56:25 +08:00
|
|
|
VCPU_STAT("halt_successful_poll", halt_successful_poll),
|
|
|
|
VCPU_STAT("halt_attempted_poll", halt_attempted_poll),
|
|
|
|
VCPU_STAT("halt_poll_invalid", halt_poll_invalid),
|
|
|
|
VCPU_STAT("halt_wakeup", halt_wakeup),
|
2020-05-09 02:22:40 +08:00
|
|
|
VCPU_STAT("halt_poll_success_ns", halt_poll_success_ns),
|
|
|
|
VCPU_STAT("halt_poll_fail_ns", halt_poll_fail_ns),
|
2012-11-22 10:34:02 +08:00
|
|
|
{NULL}
|
|
|
|
};
|
|
|
|
|
2017-03-14 18:15:40 +08:00
|
|
|
bool kvm_trace_guest_mode_change;
|
|
|
|
|
|
|
|
int kvm_guest_mode_change_trace_reg(void)
|
|
|
|
{
|
2020-04-29 22:09:35 +08:00
|
|
|
kvm_trace_guest_mode_change = true;
|
2017-03-14 18:15:40 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void kvm_guest_mode_change_trace_unreg(void)
|
|
|
|
{
|
2020-04-29 22:09:35 +08:00
|
|
|
kvm_trace_guest_mode_change = false;
|
2017-03-14 18:15:40 +08:00
|
|
|
}
|
|
|
|
|
2014-06-27 03:11:34 +08:00
|
|
|
/*
|
|
|
|
* XXXKYMA: We are simulatoring a processor that has the WII bit set in
|
|
|
|
* Config7, so we are "runnable" if interrupts are pending
|
2012-11-22 10:34:02 +08:00
|
|
|
*/
|
|
|
|
int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu)
|
|
|
|
{
|
|
|
|
return !!(vcpu->arch.pending_exceptions);
|
|
|
|
}
|
|
|
|
|
2017-08-08 12:05:32 +08:00
|
|
|
bool kvm_arch_vcpu_in_kernel(struct kvm_vcpu *vcpu)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-11-22 10:34:02 +08:00
|
|
|
int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu)
|
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2014-08-28 21:13:03 +08:00
|
|
|
int kvm_arch_hardware_enable(void)
|
2012-11-22 10:34:02 +08:00
|
|
|
{
|
2017-03-14 18:15:23 +08:00
|
|
|
return kvm_mips_callbacks->hardware_enable();
|
|
|
|
}
|
|
|
|
|
|
|
|
void kvm_arch_hardware_disable(void)
|
|
|
|
{
|
|
|
|
kvm_mips_callbacks->hardware_disable();
|
2012-11-22 10:34:02 +08:00
|
|
|
}
|
|
|
|
|
2020-03-22 04:25:55 +08:00
|
|
|
int kvm_arch_hardware_setup(void *opaque)
|
2012-11-22 10:34:02 +08:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-03-22 04:25:55 +08:00
|
|
|
int kvm_arch_check_processor_compat(void *opaque)
|
2012-11-22 10:34:02 +08:00
|
|
|
{
|
2019-04-20 13:18:17 +08:00
|
|
|
return 0;
|
2012-11-22 10:34:02 +08:00
|
|
|
}
|
|
|
|
|
2020-05-23 15:56:37 +08:00
|
|
|
extern void kvm_init_loongson_ipi(struct kvm *kvm);
|
|
|
|
|
2012-11-22 10:34:02 +08:00
|
|
|
int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
|
|
|
|
{
|
2017-03-14 18:15:19 +08:00
|
|
|
switch (type) {
|
2020-09-10 18:33:51 +08:00
|
|
|
case KVM_VM_MIPS_AUTO:
|
|
|
|
break;
|
2017-03-14 18:15:31 +08:00
|
|
|
#ifdef CONFIG_KVM_MIPS_VZ
|
|
|
|
case KVM_VM_MIPS_VZ:
|
|
|
|
#else
|
2017-03-14 18:15:19 +08:00
|
|
|
case KVM_VM_MIPS_TE:
|
2017-03-14 18:15:31 +08:00
|
|
|
#endif
|
2017-03-14 18:15:19 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* Unsupported KVM type */
|
|
|
|
return -EINVAL;
|
|
|
|
};
|
|
|
|
|
2015-05-01 20:50:18 +08:00
|
|
|
/* Allocate page table to map GPA -> RPA */
|
|
|
|
kvm->arch.gpa_mm.pgd = kvm_pgd_alloc();
|
|
|
|
if (!kvm->arch.gpa_mm.pgd)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2020-05-23 15:56:37 +08:00
|
|
|
#ifdef CONFIG_CPU_LOONGSON64
|
|
|
|
kvm_init_loongson_ipi(kvm);
|
|
|
|
#endif
|
|
|
|
|
2012-11-22 10:34:02 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void kvm_mips_free_vcpus(struct kvm *kvm)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
struct kvm_vcpu *vcpu;
|
|
|
|
|
|
|
|
kvm_for_each_vcpu(i, vcpu, kvm) {
|
2019-12-19 05:55:14 +08:00
|
|
|
kvm_vcpu_destroy(vcpu);
|
2012-11-22 10:34:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
mutex_lock(&kvm->lock);
|
|
|
|
|
|
|
|
for (i = 0; i < atomic_read(&kvm->online_vcpus); i++)
|
|
|
|
kvm->vcpus[i] = NULL;
|
|
|
|
|
|
|
|
atomic_set(&kvm->online_vcpus, 0);
|
|
|
|
|
|
|
|
mutex_unlock(&kvm->lock);
|
|
|
|
}
|
|
|
|
|
2015-05-01 20:50:18 +08:00
|
|
|
static void kvm_mips_free_gpa_pt(struct kvm *kvm)
|
|
|
|
{
|
|
|
|
/* It should always be safe to remove after flushing the whole range */
|
|
|
|
WARN_ON(!kvm_mips_flush_gpa_pt(kvm, 0, ~0));
|
|
|
|
pgd_free(NULL, kvm->arch.gpa_mm.pgd);
|
|
|
|
}
|
|
|
|
|
2012-11-22 10:34:02 +08:00
|
|
|
void kvm_arch_destroy_vm(struct kvm *kvm)
|
|
|
|
{
|
|
|
|
kvm_mips_free_vcpus(kvm);
|
2015-05-01 20:50:18 +08:00
|
|
|
kvm_mips_free_gpa_pt(kvm);
|
2012-11-22 10:34:02 +08:00
|
|
|
}
|
|
|
|
|
2014-06-27 03:11:34 +08:00
|
|
|
long kvm_arch_dev_ioctl(struct file *filp, unsigned int ioctl,
|
|
|
|
unsigned long arg)
|
2012-11-22 10:34:02 +08:00
|
|
|
{
|
2013-05-24 00:49:10 +08:00
|
|
|
return -ENOIOCTLCMD;
|
2012-11-22 10:34:02 +08:00
|
|
|
}
|
|
|
|
|
2016-10-25 07:01:37 +08:00
|
|
|
void kvm_arch_flush_shadow_all(struct kvm *kvm)
|
|
|
|
{
|
|
|
|
/* Flush whole GPA */
|
|
|
|
kvm_mips_flush_gpa_pt(kvm, 0, ~0);
|
|
|
|
|
|
|
|
/* Let implementation do the rest */
|
|
|
|
kvm_mips_callbacks->flush_shadow_all(kvm);
|
|
|
|
}
|
|
|
|
|
|
|
|
void kvm_arch_flush_shadow_memslot(struct kvm *kvm,
|
|
|
|
struct kvm_memory_slot *slot)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* The slot has been made invalid (ready for moving or deletion), so we
|
|
|
|
* need to ensure that it can no longer be accessed by any guest VCPUs.
|
|
|
|
*/
|
|
|
|
|
|
|
|
spin_lock(&kvm->mmu_lock);
|
|
|
|
/* Flush slot from GPA */
|
|
|
|
kvm_mips_flush_gpa_pt(kvm, slot->base_gfn,
|
|
|
|
slot->base_gfn + slot->npages - 1);
|
|
|
|
/* Let implementation do the rest */
|
|
|
|
kvm_mips_callbacks->flush_shadow_memslot(kvm, slot);
|
|
|
|
spin_unlock(&kvm->mmu_lock);
|
|
|
|
}
|
|
|
|
|
2012-11-22 10:34:02 +08:00
|
|
|
int kvm_arch_prepare_memory_region(struct kvm *kvm,
|
2014-06-27 03:11:34 +08:00
|
|
|
struct kvm_memory_slot *memslot,
|
2015-05-18 19:59:39 +08:00
|
|
|
const struct kvm_userspace_memory_region *mem,
|
2014-06-27 03:11:34 +08:00
|
|
|
enum kvm_mr_change change)
|
2012-11-22 10:34:02 +08:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void kvm_arch_commit_memory_region(struct kvm *kvm,
|
2015-05-18 19:59:39 +08:00
|
|
|
const struct kvm_userspace_memory_region *mem,
|
2020-02-19 05:07:24 +08:00
|
|
|
struct kvm_memory_slot *old,
|
2015-05-18 19:20:23 +08:00
|
|
|
const struct kvm_memory_slot *new,
|
2014-06-27 03:11:34 +08:00
|
|
|
enum kvm_mr_change change)
|
2012-11-22 10:34:02 +08:00
|
|
|
{
|
2016-12-06 22:56:20 +08:00
|
|
|
int needs_flush;
|
|
|
|
|
2012-11-22 10:34:02 +08:00
|
|
|
kvm_debug("%s: kvm: %p slot: %d, GPA: %llx, size: %llx, QVA: %llx\n",
|
|
|
|
__func__, kvm, mem->slot, mem->guest_phys_addr,
|
|
|
|
mem->memory_size, mem->userspace_addr);
|
2016-12-06 22:56:20 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If dirty page logging is enabled, write protect all pages in the slot
|
|
|
|
* ready for dirty logging.
|
|
|
|
*
|
|
|
|
* There is no need to do this in any of the following cases:
|
|
|
|
* CREATE: No dirty mappings will already exist.
|
|
|
|
* MOVE/DELETE: The old mappings will already have been cleaned up by
|
|
|
|
* kvm_arch_flush_shadow_memslot()
|
|
|
|
*/
|
|
|
|
if (change == KVM_MR_FLAGS_ONLY &&
|
|
|
|
(!(old->flags & KVM_MEM_LOG_DIRTY_PAGES) &&
|
|
|
|
new->flags & KVM_MEM_LOG_DIRTY_PAGES)) {
|
|
|
|
spin_lock(&kvm->mmu_lock);
|
|
|
|
/* Write protect GPA page table entries */
|
|
|
|
needs_flush = kvm_mips_mkclean_gpa_pt(kvm, new->base_gfn,
|
|
|
|
new->base_gfn + new->npages - 1);
|
|
|
|
/* Let implementation do the rest */
|
|
|
|
if (needs_flush)
|
|
|
|
kvm_mips_callbacks->flush_shadow_memslot(kvm, new);
|
|
|
|
spin_unlock(&kvm->mmu_lock);
|
|
|
|
}
|
2012-11-22 10:34:02 +08:00
|
|
|
}
|
|
|
|
|
2016-06-24 00:34:40 +08:00
|
|
|
static inline void dump_handler(const char *symbol, void *start, void *end)
|
|
|
|
{
|
|
|
|
u32 *p;
|
|
|
|
|
|
|
|
pr_debug("LEAF(%s)\n", symbol);
|
|
|
|
|
|
|
|
pr_debug("\t.set push\n");
|
|
|
|
pr_debug("\t.set noreorder\n");
|
|
|
|
|
|
|
|
for (p = start; p < (u32 *)end; ++p)
|
|
|
|
pr_debug("\t.word\t0x%08x\t\t# %p\n", *p, p);
|
|
|
|
|
|
|
|
pr_debug("\t.set\tpop\n");
|
|
|
|
|
|
|
|
pr_debug("\tEND(%s)\n", symbol);
|
|
|
|
}
|
|
|
|
|
2020-02-04 02:42:00 +08:00
|
|
|
/* low level hrtimer wake routine */
|
|
|
|
static enum hrtimer_restart kvm_mips_comparecount_wakeup(struct hrtimer *timer)
|
2020-02-04 02:41:59 +08:00
|
|
|
{
|
2020-02-04 02:42:00 +08:00
|
|
|
struct kvm_vcpu *vcpu;
|
|
|
|
|
|
|
|
vcpu = container_of(timer, struct kvm_vcpu, arch.comparecount_timer);
|
2020-02-04 02:41:59 +08:00
|
|
|
|
|
|
|
kvm_mips_callbacks->queue_timer_int(vcpu);
|
|
|
|
|
|
|
|
vcpu->arch.wait = 0;
|
2020-04-24 13:48:37 +08:00
|
|
|
rcuwait_wake_up(&vcpu->wait);
|
2020-02-04 02:41:59 +08:00
|
|
|
|
|
|
|
return kvm_mips_count_timeout(vcpu);
|
|
|
|
}
|
|
|
|
|
2019-12-19 05:55:09 +08:00
|
|
|
int kvm_arch_vcpu_precreate(struct kvm *kvm, unsigned int id)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-12-19 05:55:15 +08:00
|
|
|
int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
|
2012-11-22 10:34:02 +08:00
|
|
|
{
|
2016-06-24 00:34:39 +08:00
|
|
|
int err, size;
|
2016-09-11 06:56:46 +08:00
|
|
|
void *gebase, *p, *handler, *refill_start, *refill_end;
|
2012-11-22 10:34:02 +08:00
|
|
|
int i;
|
|
|
|
|
2019-12-19 05:55:15 +08:00
|
|
|
kvm_debug("kvm @ %p: create cpu %d at %p\n",
|
|
|
|
vcpu->kvm, vcpu->vcpu_id, vcpu);
|
2012-11-22 10:34:02 +08:00
|
|
|
|
2019-12-19 05:55:24 +08:00
|
|
|
err = kvm_mips_callbacks->vcpu_init(vcpu);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
hrtimer_init(&vcpu->arch.comparecount_timer, CLOCK_MONOTONIC,
|
|
|
|
HRTIMER_MODE_REL);
|
|
|
|
vcpu->arch.comparecount_timer.function = kvm_mips_comparecount_wakeup;
|
|
|
|
|
2014-06-27 03:11:34 +08:00
|
|
|
/*
|
|
|
|
* Allocate space for host mode exception handlers that handle
|
2012-11-22 10:34:02 +08:00
|
|
|
* guest mode exits
|
|
|
|
*/
|
2014-06-27 03:11:34 +08:00
|
|
|
if (cpu_has_veic || cpu_has_vint)
|
2012-11-22 10:34:02 +08:00
|
|
|
size = 0x200 + VECTORSPACING * 64;
|
2014-06-27 03:11:34 +08:00
|
|
|
else
|
2014-05-29 17:16:23 +08:00
|
|
|
size = 0x4000;
|
2012-11-22 10:34:02 +08:00
|
|
|
|
|
|
|
gebase = kzalloc(ALIGN(size, PAGE_SIZE), GFP_KERNEL);
|
|
|
|
|
|
|
|
if (!gebase) {
|
|
|
|
err = -ENOMEM;
|
2019-12-19 05:55:24 +08:00
|
|
|
goto out_uninit_vcpu;
|
2012-11-22 10:34:02 +08:00
|
|
|
}
|
2014-05-29 17:16:43 +08:00
|
|
|
kvm_debug("Allocated %d bytes for KVM Exception Handlers @ %p\n",
|
|
|
|
ALIGN(size, PAGE_SIZE), gebase);
|
2012-11-22 10:34:02 +08:00
|
|
|
|
2016-07-08 18:53:26 +08:00
|
|
|
/*
|
|
|
|
* Check new ebase actually fits in CP0_EBase. The lack of a write gate
|
|
|
|
* limits us to the low 512MB of physical address space. If the memory
|
|
|
|
* we allocate is out of range, just give up now.
|
|
|
|
*/
|
|
|
|
if (!cpu_has_ebase_wg && virt_to_phys(gebase) >= 0x20000000) {
|
|
|
|
kvm_err("CP0_EBase.WG required for guest exception base %pK\n",
|
|
|
|
gebase);
|
|
|
|
err = -ENOMEM;
|
|
|
|
goto out_free_gebase;
|
|
|
|
}
|
|
|
|
|
2012-11-22 10:34:02 +08:00
|
|
|
/* Save new ebase */
|
|
|
|
vcpu->arch.guest_ebase = gebase;
|
|
|
|
|
2016-06-24 00:34:39 +08:00
|
|
|
/* Build guest exception vectors dynamically in unmapped memory */
|
2016-06-24 00:34:46 +08:00
|
|
|
handler = gebase + 0x2000;
|
2012-11-22 10:34:02 +08:00
|
|
|
|
2017-03-14 18:15:26 +08:00
|
|
|
/* TLB refill (or XTLB refill on 64-bit VZ where KX=1) */
|
2016-09-11 06:56:46 +08:00
|
|
|
refill_start = gebase;
|
2017-03-14 18:15:26 +08:00
|
|
|
if (IS_ENABLED(CONFIG_KVM_MIPS_VZ) && IS_ENABLED(CONFIG_64BIT))
|
|
|
|
refill_start += 0x080;
|
2016-09-11 06:56:46 +08:00
|
|
|
refill_end = kvm_mips_build_tlb_refill_exception(refill_start, handler);
|
2012-11-22 10:34:02 +08:00
|
|
|
|
|
|
|
/* General Exception Entry point */
|
2016-06-24 00:34:46 +08:00
|
|
|
kvm_mips_build_exception(gebase + 0x180, handler);
|
2012-11-22 10:34:02 +08:00
|
|
|
|
|
|
|
/* For vectored interrupts poke the exception code @ all offsets 0-7 */
|
|
|
|
for (i = 0; i < 8; i++) {
|
|
|
|
kvm_debug("L1 Vectored handler @ %p\n",
|
|
|
|
gebase + 0x200 + (i * VECTORSPACING));
|
2016-06-24 00:34:46 +08:00
|
|
|
kvm_mips_build_exception(gebase + 0x200 + i * VECTORSPACING,
|
|
|
|
handler);
|
2012-11-22 10:34:02 +08:00
|
|
|
}
|
|
|
|
|
2016-06-24 00:34:39 +08:00
|
|
|
/* General exit handler */
|
2016-06-24 00:34:46 +08:00
|
|
|
p = handler;
|
2016-06-24 00:34:39 +08:00
|
|
|
p = kvm_mips_build_exit(p);
|
|
|
|
|
|
|
|
/* Guest entry routine */
|
|
|
|
vcpu->arch.vcpu_run = p;
|
|
|
|
p = kvm_mips_build_vcpu_run(p);
|
2016-06-09 17:50:43 +08:00
|
|
|
|
2016-06-24 00:34:40 +08:00
|
|
|
/* Dump the generated code */
|
|
|
|
pr_debug("#include <asm/asm.h>\n");
|
|
|
|
pr_debug("#include <asm/regdef.h>\n");
|
|
|
|
pr_debug("\n");
|
|
|
|
dump_handler("kvm_vcpu_run", vcpu->arch.vcpu_run, p);
|
2016-09-11 06:56:46 +08:00
|
|
|
dump_handler("kvm_tlb_refill", refill_start, refill_end);
|
2016-06-24 00:34:40 +08:00
|
|
|
dump_handler("kvm_gen_exc", gebase + 0x180, gebase + 0x200);
|
|
|
|
dump_handler("kvm_exit", gebase + 0x2000, vcpu->arch.vcpu_run);
|
|
|
|
|
2012-11-22 10:34:02 +08:00
|
|
|
/* Invalidate the icache for these ranges */
|
2017-01-04 01:43:01 +08:00
|
|
|
flush_icache_range((unsigned long)gebase,
|
|
|
|
(unsigned long)gebase + ALIGN(size, PAGE_SIZE));
|
2012-11-22 10:34:02 +08:00
|
|
|
|
2014-06-27 03:11:34 +08:00
|
|
|
/*
|
|
|
|
* Allocate comm page for guest kernel, a TLB will be reserved for
|
|
|
|
* mapping GVA @ 0xFFFF8000 to this page
|
|
|
|
*/
|
2012-11-22 10:34:02 +08:00
|
|
|
vcpu->arch.kseg0_commpage = kzalloc(PAGE_SIZE << 1, GFP_KERNEL);
|
|
|
|
|
|
|
|
if (!vcpu->arch.kseg0_commpage) {
|
|
|
|
err = -ENOMEM;
|
|
|
|
goto out_free_gebase;
|
|
|
|
}
|
|
|
|
|
2014-05-29 17:16:43 +08:00
|
|
|
kvm_debug("Allocated COMM page @ %p\n", vcpu->arch.kseg0_commpage);
|
2012-11-22 10:34:02 +08:00
|
|
|
kvm_mips_commpage_init(vcpu);
|
|
|
|
|
|
|
|
/* Init */
|
|
|
|
vcpu->arch.last_sched_cpu = -1;
|
2017-03-14 18:15:31 +08:00
|
|
|
vcpu->arch.last_exec_cpu = -1;
|
2012-11-22 10:34:02 +08:00
|
|
|
|
2019-12-19 05:55:19 +08:00
|
|
|
/* Initial guest state */
|
|
|
|
err = kvm_mips_callbacks->vcpu_setup(vcpu);
|
|
|
|
if (err)
|
|
|
|
goto out_free_commpage;
|
|
|
|
|
2019-12-19 05:55:15 +08:00
|
|
|
return 0;
|
2012-11-22 10:34:02 +08:00
|
|
|
|
2019-12-19 05:55:19 +08:00
|
|
|
out_free_commpage:
|
|
|
|
kfree(vcpu->arch.kseg0_commpage);
|
2012-11-22 10:34:02 +08:00
|
|
|
out_free_gebase:
|
|
|
|
kfree(gebase);
|
2019-12-19 05:55:24 +08:00
|
|
|
out_uninit_vcpu:
|
|
|
|
kvm_mips_callbacks->vcpu_uninit(vcpu);
|
2019-12-19 05:55:15 +08:00
|
|
|
return err;
|
2012-11-22 10:34:02 +08:00
|
|
|
}
|
|
|
|
|
2019-12-19 05:55:02 +08:00
|
|
|
void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
|
2012-11-22 10:34:02 +08:00
|
|
|
{
|
|
|
|
hrtimer_cancel(&vcpu->arch.comparecount_timer);
|
|
|
|
|
|
|
|
kvm_mips_dump_stats(vcpu);
|
|
|
|
|
2016-12-16 23:57:00 +08:00
|
|
|
kvm_mmu_free_memory_caches(vcpu);
|
2014-05-29 17:16:44 +08:00
|
|
|
kfree(vcpu->arch.guest_ebase);
|
|
|
|
kfree(vcpu->arch.kseg0_commpage);
|
2019-12-19 05:55:24 +08:00
|
|
|
|
|
|
|
kvm_mips_callbacks->vcpu_uninit(vcpu);
|
2012-11-22 10:34:02 +08:00
|
|
|
}
|
|
|
|
|
2014-06-27 03:11:34 +08:00
|
|
|
int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu,
|
|
|
|
struct kvm_guest_debug *dbg)
|
2012-11-22 10:34:02 +08:00
|
|
|
{
|
2013-05-24 00:49:10 +08:00
|
|
|
return -ENOIOCTLCMD;
|
2012-11-22 10:34:02 +08:00
|
|
|
}
|
|
|
|
|
2020-04-16 13:10:57 +08:00
|
|
|
int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
|
2012-11-22 10:34:02 +08:00
|
|
|
{
|
2017-02-08 18:50:15 +08:00
|
|
|
int r = -EINTR;
|
2012-11-22 10:34:02 +08:00
|
|
|
|
2017-12-05 04:35:25 +08:00
|
|
|
vcpu_load(vcpu);
|
|
|
|
|
2017-11-25 05:39:01 +08:00
|
|
|
kvm_sigset_activate(vcpu);
|
2012-11-22 10:34:02 +08:00
|
|
|
|
|
|
|
if (vcpu->mmio_needed) {
|
|
|
|
if (!vcpu->mmio_is_write)
|
2020-06-23 21:14:17 +08:00
|
|
|
kvm_mips_complete_mmio_load(vcpu);
|
2012-11-22 10:34:02 +08:00
|
|
|
vcpu->mmio_needed = 0;
|
|
|
|
}
|
|
|
|
|
2020-06-23 21:14:17 +08:00
|
|
|
if (vcpu->run->immediate_exit)
|
2017-02-08 18:50:15 +08:00
|
|
|
goto out;
|
|
|
|
|
KVM: MIPS: Don't leak FPU/DSP to guest
The FPU and DSP are enabled via the CP0 Status CU1 and MX bits by
kvm_mips_set_c0_status() on a guest exit, presumably in case there is
active state that needs saving if pre-emption occurs. However neither of
these bits are cleared again when returning to the guest.
This effectively gives the guest access to the FPU/DSP hardware after
the first guest exit even though it is not aware of its presence,
allowing FP instructions in guest user code to intermittently actually
execute instead of trapping into the guest OS for emulation. It will
then read & manipulate the hardware FP registers which technically
belong to the user process (e.g. QEMU), or are stale from another user
process. It can also crash the guest OS by causing an FP exception, for
which a guest exception handler won't have been registered.
First lets save and disable the FPU (and MSA) state with lose_fpu(1)
before entering the guest. This simplifies the problem, especially for
when guest FPU/MSA support is added in the future, and prevents FR=1 FPU
state being live when the FR bit gets cleared for the guest, which
according to the architecture causes the contents of the FPU and vector
registers to become UNPREDICTABLE.
We can then safely remove the enabling of the FPU in
kvm_mips_set_c0_status(), since there should never be any active FPU or
MSA state to save at pre-emption, which should plug the FPU leak.
DSP state is always live rather than being lazily restored, so for that
it is simpler to just clear the MX bit again when re-entering the guest.
Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: Sanjay Lal <sanjayl@kymasys.com>
Cc: Gleb Natapov <gleb@kernel.org>
Cc: kvm@vger.kernel.org
Cc: linux-mips@linux-mips.org
Cc: <stable@vger.kernel.org> # v3.10+: 044f0f03eca0: MIPS: KVM: Deliver guest interrupts
Cc: <stable@vger.kernel.org> # v3.10+
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2015-02-05 01:06:37 +08:00
|
|
|
lose_fpu(1);
|
|
|
|
|
2014-05-29 17:16:32 +08:00
|
|
|
local_irq_disable();
|
2016-06-15 21:18:26 +08:00
|
|
|
guest_enter_irqoff();
|
2016-06-14 16:40:14 +08:00
|
|
|
trace_kvm_enter(vcpu);
|
2016-09-16 07:06:43 +08:00
|
|
|
|
2016-11-29 06:45:04 +08:00
|
|
|
/*
|
|
|
|
* Make sure the read of VCPU requests in vcpu_run() callback is not
|
|
|
|
* reordered ahead of the write to vcpu->mode, or we could miss a TLB
|
|
|
|
* flush request while the requester sees the VCPU as outside of guest
|
|
|
|
* mode and not needing an IPI.
|
|
|
|
*/
|
|
|
|
smp_store_mb(vcpu->mode, IN_GUEST_MODE);
|
|
|
|
|
2020-06-23 21:14:17 +08:00
|
|
|
r = kvm_mips_callbacks->vcpu_run(vcpu);
|
2016-09-16 07:06:43 +08:00
|
|
|
|
2016-06-14 16:40:14 +08:00
|
|
|
trace_kvm_out(vcpu);
|
2016-06-15 21:18:26 +08:00
|
|
|
guest_exit_irqoff();
|
2012-11-22 10:34:02 +08:00
|
|
|
local_irq_enable();
|
|
|
|
|
2017-02-08 18:50:15 +08:00
|
|
|
out:
|
2017-11-25 05:39:01 +08:00
|
|
|
kvm_sigset_deactivate(vcpu);
|
2012-11-22 10:34:02 +08:00
|
|
|
|
2017-12-05 04:35:25 +08:00
|
|
|
vcpu_put(vcpu);
|
2012-11-22 10:34:02 +08:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2014-06-27 03:11:34 +08:00
|
|
|
int kvm_vcpu_ioctl_interrupt(struct kvm_vcpu *vcpu,
|
|
|
|
struct kvm_mips_interrupt *irq)
|
2012-11-22 10:34:02 +08:00
|
|
|
{
|
|
|
|
int intr = (int)irq->irq;
|
|
|
|
struct kvm_vcpu *dvcpu = NULL;
|
|
|
|
|
KVM: MIPS: Add more types of virtual interrupts
In current implementation, MIPS KVM uses IP2, IP3, IP4 and IP7 for
external interrupt, two kinds of IPIs and timer interrupt respectively,
but Loongson-3 based machines prefer to use IP2, IP3, IP6 and IP7 for
two kinds of external interrupts, IPI and timer interrupt. So we define
two priority-irq mapping tables: kvm_loongson3_priority_to_irq[] for
Loongson-3, and kvm_default_priority_to_irq[] for others. The virtual
interrupt infrastructure is updated to deliver all types of interrupts
from IP2, IP3, IP4, IP6 and IP7.
Reviewed-by: Aleksandar Markovic <aleksandar.qemu.devel@gmail.com>
Signed-off-by: Huacai Chen <chenhc@lemote.com>
Co-developed-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
Message-Id: <1590220602-3547-10-git-send-email-chenhc@lemote.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2020-05-23 15:56:36 +08:00
|
|
|
if (intr == kvm_priority_to_irq[MIPS_EXC_INT_IPI_1] ||
|
|
|
|
intr == kvm_priority_to_irq[MIPS_EXC_INT_IPI_2] ||
|
|
|
|
intr == (-kvm_priority_to_irq[MIPS_EXC_INT_IPI_1]) ||
|
|
|
|
intr == (-kvm_priority_to_irq[MIPS_EXC_INT_IPI_2]))
|
2012-11-22 10:34:02 +08:00
|
|
|
kvm_debug("%s: CPU: %d, INTR: %d\n", __func__, irq->cpu,
|
|
|
|
(int)intr);
|
|
|
|
|
|
|
|
if (irq->cpu == -1)
|
|
|
|
dvcpu = vcpu;
|
|
|
|
else
|
|
|
|
dvcpu = vcpu->kvm->vcpus[irq->cpu];
|
|
|
|
|
KVM: MIPS: Add more types of virtual interrupts
In current implementation, MIPS KVM uses IP2, IP3, IP4 and IP7 for
external interrupt, two kinds of IPIs and timer interrupt respectively,
but Loongson-3 based machines prefer to use IP2, IP3, IP6 and IP7 for
two kinds of external interrupts, IPI and timer interrupt. So we define
two priority-irq mapping tables: kvm_loongson3_priority_to_irq[] for
Loongson-3, and kvm_default_priority_to_irq[] for others. The virtual
interrupt infrastructure is updated to deliver all types of interrupts
from IP2, IP3, IP4, IP6 and IP7.
Reviewed-by: Aleksandar Markovic <aleksandar.qemu.devel@gmail.com>
Signed-off-by: Huacai Chen <chenhc@lemote.com>
Co-developed-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
Message-Id: <1590220602-3547-10-git-send-email-chenhc@lemote.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2020-05-23 15:56:36 +08:00
|
|
|
if (intr == 2 || intr == 3 || intr == 4 || intr == 6) {
|
2012-11-22 10:34:02 +08:00
|
|
|
kvm_mips_callbacks->queue_io_int(dvcpu, irq);
|
|
|
|
|
KVM: MIPS: Add more types of virtual interrupts
In current implementation, MIPS KVM uses IP2, IP3, IP4 and IP7 for
external interrupt, two kinds of IPIs and timer interrupt respectively,
but Loongson-3 based machines prefer to use IP2, IP3, IP6 and IP7 for
two kinds of external interrupts, IPI and timer interrupt. So we define
two priority-irq mapping tables: kvm_loongson3_priority_to_irq[] for
Loongson-3, and kvm_default_priority_to_irq[] for others. The virtual
interrupt infrastructure is updated to deliver all types of interrupts
from IP2, IP3, IP4, IP6 and IP7.
Reviewed-by: Aleksandar Markovic <aleksandar.qemu.devel@gmail.com>
Signed-off-by: Huacai Chen <chenhc@lemote.com>
Co-developed-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
Message-Id: <1590220602-3547-10-git-send-email-chenhc@lemote.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2020-05-23 15:56:36 +08:00
|
|
|
} else if (intr == -2 || intr == -3 || intr == -4 || intr == -6) {
|
2012-11-22 10:34:02 +08:00
|
|
|
kvm_mips_callbacks->dequeue_io_int(dvcpu, irq);
|
|
|
|
} else {
|
|
|
|
kvm_err("%s: invalid interrupt ioctl (%d:%d)\n", __func__,
|
|
|
|
irq->cpu, irq->irq);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
dvcpu->arch.wait = 0;
|
|
|
|
|
2020-04-24 13:48:37 +08:00
|
|
|
rcuwait_wake_up(&dvcpu->wait);
|
2012-11-22 10:34:02 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-06-27 03:11:34 +08:00
|
|
|
int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu,
|
|
|
|
struct kvm_mp_state *mp_state)
|
2012-11-22 10:34:02 +08:00
|
|
|
{
|
2013-05-24 00:49:10 +08:00
|
|
|
return -ENOIOCTLCMD;
|
2012-11-22 10:34:02 +08:00
|
|
|
}
|
|
|
|
|
2014-06-27 03:11:34 +08:00
|
|
|
int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu,
|
|
|
|
struct kvm_mp_state *mp_state)
|
2012-11-22 10:34:02 +08:00
|
|
|
{
|
2013-05-24 00:49:10 +08:00
|
|
|
return -ENOIOCTLCMD;
|
2012-11-22 10:34:02 +08:00
|
|
|
}
|
|
|
|
|
2013-05-24 00:49:09 +08:00
|
|
|
static u64 kvm_mips_get_one_regs[] = {
|
|
|
|
KVM_REG_MIPS_R0,
|
|
|
|
KVM_REG_MIPS_R1,
|
|
|
|
KVM_REG_MIPS_R2,
|
|
|
|
KVM_REG_MIPS_R3,
|
|
|
|
KVM_REG_MIPS_R4,
|
|
|
|
KVM_REG_MIPS_R5,
|
|
|
|
KVM_REG_MIPS_R6,
|
|
|
|
KVM_REG_MIPS_R7,
|
|
|
|
KVM_REG_MIPS_R8,
|
|
|
|
KVM_REG_MIPS_R9,
|
|
|
|
KVM_REG_MIPS_R10,
|
|
|
|
KVM_REG_MIPS_R11,
|
|
|
|
KVM_REG_MIPS_R12,
|
|
|
|
KVM_REG_MIPS_R13,
|
|
|
|
KVM_REG_MIPS_R14,
|
|
|
|
KVM_REG_MIPS_R15,
|
|
|
|
KVM_REG_MIPS_R16,
|
|
|
|
KVM_REG_MIPS_R17,
|
|
|
|
KVM_REG_MIPS_R18,
|
|
|
|
KVM_REG_MIPS_R19,
|
|
|
|
KVM_REG_MIPS_R20,
|
|
|
|
KVM_REG_MIPS_R21,
|
|
|
|
KVM_REG_MIPS_R22,
|
|
|
|
KVM_REG_MIPS_R23,
|
|
|
|
KVM_REG_MIPS_R24,
|
|
|
|
KVM_REG_MIPS_R25,
|
|
|
|
KVM_REG_MIPS_R26,
|
|
|
|
KVM_REG_MIPS_R27,
|
|
|
|
KVM_REG_MIPS_R28,
|
|
|
|
KVM_REG_MIPS_R29,
|
|
|
|
KVM_REG_MIPS_R30,
|
|
|
|
KVM_REG_MIPS_R31,
|
|
|
|
|
2016-07-05 02:35:11 +08:00
|
|
|
#ifndef CONFIG_CPU_MIPSR6
|
2013-05-24 00:49:09 +08:00
|
|
|
KVM_REG_MIPS_HI,
|
|
|
|
KVM_REG_MIPS_LO,
|
2016-07-05 02:35:11 +08:00
|
|
|
#endif
|
2013-05-24 00:49:09 +08:00
|
|
|
KVM_REG_MIPS_PC,
|
|
|
|
};
|
|
|
|
|
2016-06-16 02:29:51 +08:00
|
|
|
static u64 kvm_mips_get_one_regs_fpu[] = {
|
|
|
|
KVM_REG_MIPS_FCR_IR,
|
|
|
|
KVM_REG_MIPS_FCR_CSR,
|
|
|
|
};
|
|
|
|
|
|
|
|
static u64 kvm_mips_get_one_regs_msa[] = {
|
|
|
|
KVM_REG_MIPS_MSA_IR,
|
|
|
|
KVM_REG_MIPS_MSA_CSR,
|
|
|
|
};
|
|
|
|
|
2016-06-16 02:29:49 +08:00
|
|
|
static unsigned long kvm_mips_num_regs(struct kvm_vcpu *vcpu)
|
|
|
|
{
|
|
|
|
unsigned long ret;
|
|
|
|
|
|
|
|
ret = ARRAY_SIZE(kvm_mips_get_one_regs);
|
2016-06-16 02:29:51 +08:00
|
|
|
if (kvm_mips_guest_can_have_fpu(&vcpu->arch)) {
|
|
|
|
ret += ARRAY_SIZE(kvm_mips_get_one_regs_fpu) + 48;
|
|
|
|
/* odd doubles */
|
|
|
|
if (boot_cpu_data.fpu_id & MIPS_FPIR_F64)
|
|
|
|
ret += 16;
|
|
|
|
}
|
|
|
|
if (kvm_mips_guest_can_have_msa(&vcpu->arch))
|
|
|
|
ret += ARRAY_SIZE(kvm_mips_get_one_regs_msa) + 32;
|
2016-06-16 02:29:49 +08:00
|
|
|
ret += kvm_mips_callbacks->num_regs(vcpu);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int kvm_mips_copy_reg_indices(struct kvm_vcpu *vcpu, u64 __user *indices)
|
|
|
|
{
|
2016-06-16 02:29:51 +08:00
|
|
|
u64 index;
|
|
|
|
unsigned int i;
|
|
|
|
|
2016-06-16 02:29:49 +08:00
|
|
|
if (copy_to_user(indices, kvm_mips_get_one_regs,
|
|
|
|
sizeof(kvm_mips_get_one_regs)))
|
|
|
|
return -EFAULT;
|
|
|
|
indices += ARRAY_SIZE(kvm_mips_get_one_regs);
|
|
|
|
|
2016-06-16 02:29:51 +08:00
|
|
|
if (kvm_mips_guest_can_have_fpu(&vcpu->arch)) {
|
|
|
|
if (copy_to_user(indices, kvm_mips_get_one_regs_fpu,
|
|
|
|
sizeof(kvm_mips_get_one_regs_fpu)))
|
|
|
|
return -EFAULT;
|
|
|
|
indices += ARRAY_SIZE(kvm_mips_get_one_regs_fpu);
|
|
|
|
|
|
|
|
for (i = 0; i < 32; ++i) {
|
|
|
|
index = KVM_REG_MIPS_FPR_32(i);
|
|
|
|
if (copy_to_user(indices, &index, sizeof(index)))
|
|
|
|
return -EFAULT;
|
|
|
|
++indices;
|
|
|
|
|
|
|
|
/* skip odd doubles if no F64 */
|
|
|
|
if (i & 1 && !(boot_cpu_data.fpu_id & MIPS_FPIR_F64))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
index = KVM_REG_MIPS_FPR_64(i);
|
|
|
|
if (copy_to_user(indices, &index, sizeof(index)))
|
|
|
|
return -EFAULT;
|
|
|
|
++indices;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (kvm_mips_guest_can_have_msa(&vcpu->arch)) {
|
|
|
|
if (copy_to_user(indices, kvm_mips_get_one_regs_msa,
|
|
|
|
sizeof(kvm_mips_get_one_regs_msa)))
|
|
|
|
return -EFAULT;
|
|
|
|
indices += ARRAY_SIZE(kvm_mips_get_one_regs_msa);
|
|
|
|
|
|
|
|
for (i = 0; i < 32; ++i) {
|
|
|
|
index = KVM_REG_MIPS_VEC_128(i);
|
|
|
|
if (copy_to_user(indices, &index, sizeof(index)))
|
|
|
|
return -EFAULT;
|
|
|
|
++indices;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-16 02:29:49 +08:00
|
|
|
return kvm_mips_callbacks->copy_reg_indices(vcpu, indices);
|
|
|
|
}
|
|
|
|
|
2013-05-24 00:49:09 +08:00
|
|
|
static int kvm_mips_get_reg(struct kvm_vcpu *vcpu,
|
|
|
|
const struct kvm_one_reg *reg)
|
|
|
|
{
|
|
|
|
struct mips_coproc *cop0 = vcpu->arch.cop0;
|
2014-12-02 23:48:24 +08:00
|
|
|
struct mips_fpu_struct *fpu = &vcpu->arch.fpu;
|
2014-05-29 17:16:29 +08:00
|
|
|
int ret;
|
2013-05-24 00:49:09 +08:00
|
|
|
s64 v;
|
2014-12-02 23:48:24 +08:00
|
|
|
s64 vs[2];
|
2014-12-02 23:48:24 +08:00
|
|
|
unsigned int idx;
|
2013-05-24 00:49:09 +08:00
|
|
|
|
|
|
|
switch (reg->id) {
|
2014-12-02 23:48:24 +08:00
|
|
|
/* General purpose registers */
|
2013-05-24 00:49:09 +08:00
|
|
|
case KVM_REG_MIPS_R0 ... KVM_REG_MIPS_R31:
|
|
|
|
v = (long)vcpu->arch.gprs[reg->id - KVM_REG_MIPS_R0];
|
|
|
|
break;
|
2016-07-05 02:35:11 +08:00
|
|
|
#ifndef CONFIG_CPU_MIPSR6
|
2013-05-24 00:49:09 +08:00
|
|
|
case KVM_REG_MIPS_HI:
|
|
|
|
v = (long)vcpu->arch.hi;
|
|
|
|
break;
|
|
|
|
case KVM_REG_MIPS_LO:
|
|
|
|
v = (long)vcpu->arch.lo;
|
|
|
|
break;
|
2016-07-05 02:35:11 +08:00
|
|
|
#endif
|
2013-05-24 00:49:09 +08:00
|
|
|
case KVM_REG_MIPS_PC:
|
|
|
|
v = (long)vcpu->arch.pc;
|
|
|
|
break;
|
|
|
|
|
2014-12-02 23:48:24 +08:00
|
|
|
/* Floating point registers */
|
|
|
|
case KVM_REG_MIPS_FPR_32(0) ... KVM_REG_MIPS_FPR_32(31):
|
|
|
|
if (!kvm_mips_guest_has_fpu(&vcpu->arch))
|
|
|
|
return -EINVAL;
|
|
|
|
idx = reg->id - KVM_REG_MIPS_FPR_32(0);
|
|
|
|
/* Odd singles in top of even double when FR=0 */
|
|
|
|
if (kvm_read_c0_guest_status(cop0) & ST0_FR)
|
|
|
|
v = get_fpr32(&fpu->fpr[idx], 0);
|
|
|
|
else
|
|
|
|
v = get_fpr32(&fpu->fpr[idx & ~1], idx & 1);
|
|
|
|
break;
|
|
|
|
case KVM_REG_MIPS_FPR_64(0) ... KVM_REG_MIPS_FPR_64(31):
|
|
|
|
if (!kvm_mips_guest_has_fpu(&vcpu->arch))
|
|
|
|
return -EINVAL;
|
|
|
|
idx = reg->id - KVM_REG_MIPS_FPR_64(0);
|
|
|
|
/* Can't access odd doubles in FR=0 mode */
|
|
|
|
if (idx & 1 && !(kvm_read_c0_guest_status(cop0) & ST0_FR))
|
|
|
|
return -EINVAL;
|
|
|
|
v = get_fpr64(&fpu->fpr[idx], 0);
|
|
|
|
break;
|
|
|
|
case KVM_REG_MIPS_FCR_IR:
|
|
|
|
if (!kvm_mips_guest_has_fpu(&vcpu->arch))
|
|
|
|
return -EINVAL;
|
|
|
|
v = boot_cpu_data.fpu_id;
|
|
|
|
break;
|
|
|
|
case KVM_REG_MIPS_FCR_CSR:
|
|
|
|
if (!kvm_mips_guest_has_fpu(&vcpu->arch))
|
|
|
|
return -EINVAL;
|
|
|
|
v = fpu->fcr31;
|
|
|
|
break;
|
|
|
|
|
2014-12-02 23:48:24 +08:00
|
|
|
/* MIPS SIMD Architecture (MSA) registers */
|
|
|
|
case KVM_REG_MIPS_VEC_128(0) ... KVM_REG_MIPS_VEC_128(31):
|
|
|
|
if (!kvm_mips_guest_has_msa(&vcpu->arch))
|
|
|
|
return -EINVAL;
|
|
|
|
/* Can't access MSA registers in FR=0 mode */
|
|
|
|
if (!(kvm_read_c0_guest_status(cop0) & ST0_FR))
|
|
|
|
return -EINVAL;
|
|
|
|
idx = reg->id - KVM_REG_MIPS_VEC_128(0);
|
|
|
|
#ifdef CONFIG_CPU_LITTLE_ENDIAN
|
|
|
|
/* least significant byte first */
|
|
|
|
vs[0] = get_fpr64(&fpu->fpr[idx], 0);
|
|
|
|
vs[1] = get_fpr64(&fpu->fpr[idx], 1);
|
|
|
|
#else
|
|
|
|
/* most significant byte first */
|
|
|
|
vs[0] = get_fpr64(&fpu->fpr[idx], 1);
|
|
|
|
vs[1] = get_fpr64(&fpu->fpr[idx], 0);
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
case KVM_REG_MIPS_MSA_IR:
|
|
|
|
if (!kvm_mips_guest_has_msa(&vcpu->arch))
|
|
|
|
return -EINVAL;
|
|
|
|
v = boot_cpu_data.msa_id;
|
|
|
|
break;
|
|
|
|
case KVM_REG_MIPS_MSA_CSR:
|
|
|
|
if (!kvm_mips_guest_has_msa(&vcpu->arch))
|
|
|
|
return -EINVAL;
|
|
|
|
v = fpu->msacsr;
|
|
|
|
break;
|
|
|
|
|
2014-05-29 17:16:29 +08:00
|
|
|
/* registers to be handled specially */
|
2016-06-16 02:29:48 +08:00
|
|
|
default:
|
2014-05-29 17:16:29 +08:00
|
|
|
ret = kvm_mips_callbacks->get_one_reg(vcpu, reg, &v);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
break;
|
2013-05-24 00:49:09 +08:00
|
|
|
}
|
2013-06-11 03:33:48 +08:00
|
|
|
if ((reg->id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U64) {
|
|
|
|
u64 __user *uaddr64 = (u64 __user *)(long)reg->addr;
|
2014-06-27 03:11:34 +08:00
|
|
|
|
2013-06-11 03:33:48 +08:00
|
|
|
return put_user(v, uaddr64);
|
|
|
|
} else if ((reg->id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U32) {
|
|
|
|
u32 __user *uaddr32 = (u32 __user *)(long)reg->addr;
|
|
|
|
u32 v32 = (u32)v;
|
2014-06-27 03:11:34 +08:00
|
|
|
|
2013-06-11 03:33:48 +08:00
|
|
|
return put_user(v32, uaddr32);
|
2014-12-02 23:48:24 +08:00
|
|
|
} else if ((reg->id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U128) {
|
|
|
|
void __user *uaddr = (void __user *)(long)reg->addr;
|
|
|
|
|
2016-02-28 23:35:59 +08:00
|
|
|
return copy_to_user(uaddr, vs, 16) ? -EFAULT : 0;
|
2013-06-11 03:33:48 +08:00
|
|
|
} else {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2013-05-24 00:49:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int kvm_mips_set_reg(struct kvm_vcpu *vcpu,
|
|
|
|
const struct kvm_one_reg *reg)
|
|
|
|
{
|
|
|
|
struct mips_coproc *cop0 = vcpu->arch.cop0;
|
2014-12-02 23:48:24 +08:00
|
|
|
struct mips_fpu_struct *fpu = &vcpu->arch.fpu;
|
|
|
|
s64 v;
|
2014-12-02 23:48:24 +08:00
|
|
|
s64 vs[2];
|
2014-12-02 23:48:24 +08:00
|
|
|
unsigned int idx;
|
2013-05-24 00:49:09 +08:00
|
|
|
|
2013-06-11 03:33:48 +08:00
|
|
|
if ((reg->id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U64) {
|
|
|
|
u64 __user *uaddr64 = (u64 __user *)(long)reg->addr;
|
|
|
|
|
|
|
|
if (get_user(v, uaddr64) != 0)
|
|
|
|
return -EFAULT;
|
|
|
|
} else if ((reg->id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U32) {
|
|
|
|
u32 __user *uaddr32 = (u32 __user *)(long)reg->addr;
|
|
|
|
s32 v32;
|
|
|
|
|
|
|
|
if (get_user(v32, uaddr32) != 0)
|
|
|
|
return -EFAULT;
|
|
|
|
v = (s64)v32;
|
2014-12-02 23:48:24 +08:00
|
|
|
} else if ((reg->id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U128) {
|
|
|
|
void __user *uaddr = (void __user *)(long)reg->addr;
|
|
|
|
|
2016-02-28 23:35:59 +08:00
|
|
|
return copy_from_user(vs, uaddr, 16) ? -EFAULT : 0;
|
2013-06-11 03:33:48 +08:00
|
|
|
} else {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2013-05-24 00:49:09 +08:00
|
|
|
|
|
|
|
switch (reg->id) {
|
2014-12-02 23:48:24 +08:00
|
|
|
/* General purpose registers */
|
2013-05-24 00:49:09 +08:00
|
|
|
case KVM_REG_MIPS_R0:
|
|
|
|
/* Silently ignore requests to set $0 */
|
|
|
|
break;
|
|
|
|
case KVM_REG_MIPS_R1 ... KVM_REG_MIPS_R31:
|
|
|
|
vcpu->arch.gprs[reg->id - KVM_REG_MIPS_R0] = v;
|
|
|
|
break;
|
2016-07-05 02:35:11 +08:00
|
|
|
#ifndef CONFIG_CPU_MIPSR6
|
2013-05-24 00:49:09 +08:00
|
|
|
case KVM_REG_MIPS_HI:
|
|
|
|
vcpu->arch.hi = v;
|
|
|
|
break;
|
|
|
|
case KVM_REG_MIPS_LO:
|
|
|
|
vcpu->arch.lo = v;
|
|
|
|
break;
|
2016-07-05 02:35:11 +08:00
|
|
|
#endif
|
2013-05-24 00:49:09 +08:00
|
|
|
case KVM_REG_MIPS_PC:
|
|
|
|
vcpu->arch.pc = v;
|
|
|
|
break;
|
|
|
|
|
2014-12-02 23:48:24 +08:00
|
|
|
/* Floating point registers */
|
|
|
|
case KVM_REG_MIPS_FPR_32(0) ... KVM_REG_MIPS_FPR_32(31):
|
|
|
|
if (!kvm_mips_guest_has_fpu(&vcpu->arch))
|
|
|
|
return -EINVAL;
|
|
|
|
idx = reg->id - KVM_REG_MIPS_FPR_32(0);
|
|
|
|
/* Odd singles in top of even double when FR=0 */
|
|
|
|
if (kvm_read_c0_guest_status(cop0) & ST0_FR)
|
|
|
|
set_fpr32(&fpu->fpr[idx], 0, v);
|
|
|
|
else
|
|
|
|
set_fpr32(&fpu->fpr[idx & ~1], idx & 1, v);
|
|
|
|
break;
|
|
|
|
case KVM_REG_MIPS_FPR_64(0) ... KVM_REG_MIPS_FPR_64(31):
|
|
|
|
if (!kvm_mips_guest_has_fpu(&vcpu->arch))
|
|
|
|
return -EINVAL;
|
|
|
|
idx = reg->id - KVM_REG_MIPS_FPR_64(0);
|
|
|
|
/* Can't access odd doubles in FR=0 mode */
|
|
|
|
if (idx & 1 && !(kvm_read_c0_guest_status(cop0) & ST0_FR))
|
|
|
|
return -EINVAL;
|
|
|
|
set_fpr64(&fpu->fpr[idx], 0, v);
|
|
|
|
break;
|
|
|
|
case KVM_REG_MIPS_FCR_IR:
|
|
|
|
if (!kvm_mips_guest_has_fpu(&vcpu->arch))
|
|
|
|
return -EINVAL;
|
|
|
|
/* Read-only */
|
|
|
|
break;
|
|
|
|
case KVM_REG_MIPS_FCR_CSR:
|
|
|
|
if (!kvm_mips_guest_has_fpu(&vcpu->arch))
|
|
|
|
return -EINVAL;
|
|
|
|
fpu->fcr31 = v;
|
|
|
|
break;
|
|
|
|
|
2014-12-02 23:48:24 +08:00
|
|
|
/* MIPS SIMD Architecture (MSA) registers */
|
|
|
|
case KVM_REG_MIPS_VEC_128(0) ... KVM_REG_MIPS_VEC_128(31):
|
|
|
|
if (!kvm_mips_guest_has_msa(&vcpu->arch))
|
|
|
|
return -EINVAL;
|
|
|
|
idx = reg->id - KVM_REG_MIPS_VEC_128(0);
|
|
|
|
#ifdef CONFIG_CPU_LITTLE_ENDIAN
|
|
|
|
/* least significant byte first */
|
|
|
|
set_fpr64(&fpu->fpr[idx], 0, vs[0]);
|
|
|
|
set_fpr64(&fpu->fpr[idx], 1, vs[1]);
|
|
|
|
#else
|
|
|
|
/* most significant byte first */
|
|
|
|
set_fpr64(&fpu->fpr[idx], 1, vs[0]);
|
|
|
|
set_fpr64(&fpu->fpr[idx], 0, vs[1]);
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
case KVM_REG_MIPS_MSA_IR:
|
|
|
|
if (!kvm_mips_guest_has_msa(&vcpu->arch))
|
|
|
|
return -EINVAL;
|
|
|
|
/* Read-only */
|
|
|
|
break;
|
|
|
|
case KVM_REG_MIPS_MSA_CSR:
|
|
|
|
if (!kvm_mips_guest_has_msa(&vcpu->arch))
|
|
|
|
return -EINVAL;
|
|
|
|
fpu->msacsr = v;
|
|
|
|
break;
|
|
|
|
|
2014-05-29 17:16:29 +08:00
|
|
|
/* registers to be handled specially */
|
2013-05-24 00:49:09 +08:00
|
|
|
default:
|
2016-06-16 02:29:48 +08:00
|
|
|
return kvm_mips_callbacks->set_one_reg(vcpu, reg, v);
|
2013-05-24 00:49:09 +08:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-12-09 07:07:56 +08:00
|
|
|
static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu,
|
|
|
|
struct kvm_enable_cap *cap)
|
|
|
|
{
|
|
|
|
int r = 0;
|
|
|
|
|
|
|
|
if (!kvm_vm_ioctl_check_extension(vcpu->kvm, cap->cap))
|
|
|
|
return -EINVAL;
|
|
|
|
if (cap->flags)
|
|
|
|
return -EINVAL;
|
|
|
|
if (cap->args[0])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
switch (cap->cap) {
|
|
|
|
case KVM_CAP_MIPS_FPU:
|
|
|
|
vcpu->arch.fpu_enabled = true;
|
|
|
|
break;
|
2014-12-09 07:07:56 +08:00
|
|
|
case KVM_CAP_MIPS_MSA:
|
|
|
|
vcpu->arch.msa_enabled = true;
|
|
|
|
break;
|
2014-12-09 07:07:56 +08:00
|
|
|
default:
|
|
|
|
r = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2017-12-13 00:41:34 +08:00
|
|
|
long kvm_arch_vcpu_async_ioctl(struct file *filp, unsigned int ioctl,
|
|
|
|
unsigned long arg)
|
2012-11-22 10:34:02 +08:00
|
|
|
{
|
|
|
|
struct kvm_vcpu *vcpu = filp->private_data;
|
|
|
|
void __user *argp = (void __user *)arg;
|
|
|
|
|
2017-12-05 04:35:36 +08:00
|
|
|
if (ioctl == KVM_INTERRUPT) {
|
|
|
|
struct kvm_mips_interrupt irq;
|
|
|
|
|
|
|
|
if (copy_from_user(&irq, argp, sizeof(irq)))
|
|
|
|
return -EFAULT;
|
|
|
|
kvm_debug("[%d] %s: irq: %d\n", vcpu->vcpu_id, __func__,
|
|
|
|
irq.irq);
|
|
|
|
|
|
|
|
return kvm_vcpu_ioctl_interrupt(vcpu, &irq);
|
|
|
|
}
|
|
|
|
|
2017-12-13 00:41:34 +08:00
|
|
|
return -ENOIOCTLCMD;
|
|
|
|
}
|
|
|
|
|
|
|
|
long kvm_arch_vcpu_ioctl(struct file *filp, unsigned int ioctl,
|
|
|
|
unsigned long arg)
|
|
|
|
{
|
|
|
|
struct kvm_vcpu *vcpu = filp->private_data;
|
|
|
|
void __user *argp = (void __user *)arg;
|
|
|
|
long r;
|
|
|
|
|
2017-12-05 04:35:36 +08:00
|
|
|
vcpu_load(vcpu);
|
|
|
|
|
2012-11-22 10:34:02 +08:00
|
|
|
switch (ioctl) {
|
2013-05-24 00:49:09 +08:00
|
|
|
case KVM_SET_ONE_REG:
|
|
|
|
case KVM_GET_ONE_REG: {
|
|
|
|
struct kvm_one_reg reg;
|
2014-06-27 03:11:34 +08:00
|
|
|
|
2017-12-05 04:35:36 +08:00
|
|
|
r = -EFAULT;
|
2013-05-24 00:49:09 +08:00
|
|
|
if (copy_from_user(®, argp, sizeof(reg)))
|
2017-12-05 04:35:36 +08:00
|
|
|
break;
|
2013-05-24 00:49:09 +08:00
|
|
|
if (ioctl == KVM_SET_ONE_REG)
|
2017-12-05 04:35:36 +08:00
|
|
|
r = kvm_mips_set_reg(vcpu, ®);
|
2013-05-24 00:49:09 +08:00
|
|
|
else
|
2017-12-05 04:35:36 +08:00
|
|
|
r = kvm_mips_get_reg(vcpu, ®);
|
|
|
|
break;
|
2013-05-24 00:49:09 +08:00
|
|
|
}
|
|
|
|
case KVM_GET_REG_LIST: {
|
|
|
|
struct kvm_reg_list __user *user_list = argp;
|
|
|
|
struct kvm_reg_list reg_list;
|
|
|
|
unsigned n;
|
|
|
|
|
2017-12-05 04:35:36 +08:00
|
|
|
r = -EFAULT;
|
2013-05-24 00:49:09 +08:00
|
|
|
if (copy_from_user(®_list, user_list, sizeof(reg_list)))
|
2017-12-05 04:35:36 +08:00
|
|
|
break;
|
2013-05-24 00:49:09 +08:00
|
|
|
n = reg_list.n;
|
2016-06-16 02:29:49 +08:00
|
|
|
reg_list.n = kvm_mips_num_regs(vcpu);
|
2013-05-24 00:49:09 +08:00
|
|
|
if (copy_to_user(user_list, ®_list, sizeof(reg_list)))
|
2017-12-05 04:35:36 +08:00
|
|
|
break;
|
|
|
|
r = -E2BIG;
|
2013-05-24 00:49:09 +08:00
|
|
|
if (n < reg_list.n)
|
2012-11-22 10:34:02 +08:00
|
|
|
break;
|
2017-12-05 04:35:36 +08:00
|
|
|
r = kvm_mips_copy_reg_indices(vcpu, user_list->reg);
|
|
|
|
break;
|
|
|
|
}
|
2014-12-09 07:07:56 +08:00
|
|
|
case KVM_ENABLE_CAP: {
|
|
|
|
struct kvm_enable_cap cap;
|
|
|
|
|
2017-12-05 04:35:36 +08:00
|
|
|
r = -EFAULT;
|
2014-12-09 07:07:56 +08:00
|
|
|
if (copy_from_user(&cap, argp, sizeof(cap)))
|
2017-12-05 04:35:36 +08:00
|
|
|
break;
|
2014-12-09 07:07:56 +08:00
|
|
|
r = kvm_vcpu_ioctl_enable_cap(vcpu, &cap);
|
|
|
|
break;
|
|
|
|
}
|
2012-11-22 10:34:02 +08:00
|
|
|
default:
|
2013-05-24 00:49:09 +08:00
|
|
|
r = -ENOIOCTLCMD;
|
2012-11-22 10:34:02 +08:00
|
|
|
}
|
2017-12-05 04:35:36 +08:00
|
|
|
|
|
|
|
vcpu_put(vcpu);
|
2012-11-22 10:34:02 +08:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2020-02-19 05:07:29 +08:00
|
|
|
void kvm_arch_sync_dirty_log(struct kvm *kvm, struct kvm_memory_slot *memslot)
|
2012-11-22 10:34:02 +08:00
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-02-19 05:07:29 +08:00
|
|
|
void kvm_arch_flush_remote_tlbs_memslot(struct kvm *kvm,
|
|
|
|
struct kvm_memory_slot *memslot)
|
kvm: introduce manual dirty log reprotect
There are two problems with KVM_GET_DIRTY_LOG. First, and less important,
it can take kvm->mmu_lock for an extended period of time. Second, its user
can actually see many false positives in some cases. The latter is due
to a benign race like this:
1. KVM_GET_DIRTY_LOG returns a set of dirty pages and write protects
them.
2. The guest modifies the pages, causing them to be marked ditry.
3. Userspace actually copies the pages.
4. KVM_GET_DIRTY_LOG returns those pages as dirty again, even though
they were not written to since (3).
This is especially a problem for large guests, where the time between
(1) and (3) can be substantial. This patch introduces a new
capability which, when enabled, makes KVM_GET_DIRTY_LOG not
write-protect the pages it returns. Instead, userspace has to
explicitly clear the dirty log bits just before using the content
of the page. The new KVM_CLEAR_DIRTY_LOG ioctl can also operate on a
64-page granularity rather than requiring to sync a full memslot;
this way, the mmu_lock is taken for small amounts of time, and
only a small amount of time will pass between write protection
of pages and the sending of their content.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2018-10-23 08:36:47 +08:00
|
|
|
{
|
2020-02-19 05:07:29 +08:00
|
|
|
/* Let implementation handle TLB/GVA invalidation */
|
|
|
|
kvm_mips_callbacks->flush_shadow_memslot(kvm, memslot);
|
kvm: introduce manual dirty log reprotect
There are two problems with KVM_GET_DIRTY_LOG. First, and less important,
it can take kvm->mmu_lock for an extended period of time. Second, its user
can actually see many false positives in some cases. The latter is due
to a benign race like this:
1. KVM_GET_DIRTY_LOG returns a set of dirty pages and write protects
them.
2. The guest modifies the pages, causing them to be marked ditry.
3. Userspace actually copies the pages.
4. KVM_GET_DIRTY_LOG returns those pages as dirty again, even though
they were not written to since (3).
This is especially a problem for large guests, where the time between
(1) and (3) can be substantial. This patch introduces a new
capability which, when enabled, makes KVM_GET_DIRTY_LOG not
write-protect the pages it returns. Instead, userspace has to
explicitly clear the dirty log bits just before using the content
of the page. The new KVM_CLEAR_DIRTY_LOG ioctl can also operate on a
64-page granularity rather than requiring to sync a full memslot;
this way, the mmu_lock is taken for small amounts of time, and
only a small amount of time will pass between write protection
of pages and the sending of their content.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2018-10-23 08:36:47 +08:00
|
|
|
}
|
|
|
|
|
2012-11-22 10:34:02 +08:00
|
|
|
long kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
|
|
|
|
{
|
|
|
|
long r;
|
|
|
|
|
|
|
|
switch (ioctl) {
|
|
|
|
default:
|
2013-05-24 00:49:10 +08:00
|
|
|
r = -ENOIOCTLCMD;
|
2012-11-22 10:34:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
int kvm_arch_init(void *opaque)
|
|
|
|
{
|
|
|
|
if (kvm_mips_callbacks) {
|
|
|
|
kvm_err("kvm: module already exists\n");
|
|
|
|
return -EEXIST;
|
|
|
|
}
|
|
|
|
|
2014-06-27 03:11:36 +08:00
|
|
|
return kvm_mips_emulation_init(&kvm_mips_callbacks);
|
2012-11-22 10:34:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void kvm_arch_exit(void)
|
|
|
|
{
|
|
|
|
kvm_mips_callbacks = NULL;
|
|
|
|
}
|
|
|
|
|
2014-06-27 03:11:34 +08:00
|
|
|
int kvm_arch_vcpu_ioctl_get_sregs(struct kvm_vcpu *vcpu,
|
|
|
|
struct kvm_sregs *sregs)
|
2012-11-22 10:34:02 +08:00
|
|
|
{
|
2013-05-24 00:49:10 +08:00
|
|
|
return -ENOIOCTLCMD;
|
2012-11-22 10:34:02 +08:00
|
|
|
}
|
|
|
|
|
2014-06-27 03:11:34 +08:00
|
|
|
int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu,
|
|
|
|
struct kvm_sregs *sregs)
|
2012-11-22 10:34:02 +08:00
|
|
|
{
|
2013-05-24 00:49:10 +08:00
|
|
|
return -ENOIOCTLCMD;
|
2012-11-22 10:34:02 +08:00
|
|
|
}
|
|
|
|
|
2014-12-04 22:47:07 +08:00
|
|
|
void kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu)
|
2012-11-22 10:34:02 +08:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
int kvm_arch_vcpu_ioctl_get_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu)
|
|
|
|
{
|
2013-05-24 00:49:10 +08:00
|
|
|
return -ENOIOCTLCMD;
|
2012-11-22 10:34:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int kvm_arch_vcpu_ioctl_set_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu)
|
|
|
|
{
|
2013-05-24 00:49:10 +08:00
|
|
|
return -ENOIOCTLCMD;
|
2012-11-22 10:34:02 +08:00
|
|
|
}
|
|
|
|
|
2018-04-19 03:19:58 +08:00
|
|
|
vm_fault_t kvm_arch_vcpu_fault(struct kvm_vcpu *vcpu, struct vm_fault *vmf)
|
2012-11-22 10:34:02 +08:00
|
|
|
{
|
|
|
|
return VM_FAULT_SIGBUS;
|
|
|
|
}
|
|
|
|
|
2014-07-15 00:27:35 +08:00
|
|
|
int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
|
2012-11-22 10:34:02 +08:00
|
|
|
{
|
|
|
|
int r;
|
|
|
|
|
|
|
|
switch (ext) {
|
2013-05-24 00:49:09 +08:00
|
|
|
case KVM_CAP_ONE_REG:
|
2014-12-09 07:07:56 +08:00
|
|
|
case KVM_CAP_ENABLE_CAP:
|
2015-05-09 00:11:49 +08:00
|
|
|
case KVM_CAP_READONLY_MEM:
|
KVM: MIPS/MMU: Implement KVM_CAP_SYNC_MMU
Implement the SYNC_MMU capability for KVM MIPS, allowing changes in the
underlying user host virtual address (HVA) mappings to be promptly
reflected in the corresponding guest physical address (GPA) mappings.
This allows for several features to work with guest RAM which require
mappings to be altered or protected, such as copy-on-write, KSM (Kernel
Samepage Merging), idle page tracking, memory swapping, and guest memory
ballooning.
There are two main aspects of this change, described below.
The KVM MMU notifier architecture callbacks are implemented so we can be
notified of changes in the HVA mappings. These arrange for the guest
physical address (GPA) page tables to be modified and possibly for
derived mappings (GVA page tables and TLBs) to be flushed.
- kvm_unmap_hva[_range]() - These deal with HVA mappings being removed,
for example before a copy-on-write takes place, which requires the
corresponding GPA page table mappings to be removed too.
- kvm_set_spte_hva() - These update a GPA page table entry to match the
new HVA entry, but must be careful to respect KVM specific
configuration such as not dirtying a clean guest page which is dirty
to the host, and write protecting writable pages in read only
memslots (which will soon be supported).
- kvm[_test]_age_hva() - These update GPA page table entries to be old
(invalid) so that access can be tracked, making them young again.
The GPA page fault handling (kvm_mips_map_page) is updated to use
gfn_to_pfn_prot() (which may provide read-only pages), to handle
asynchronous page table invalidation from MMU notifier callbacks, and to
handle more cases in the fast path.
- mmu_notifier_seq is used to detect asynchronous page table
invalidations while we're holding a pfn from gfn_to_pfn_prot()
outside of kvm->mmu_lock, retrying if invalidations have taken place,
e.g. a COW or a KSM page merge.
- The fast path (_kvm_mips_map_page_fast) now handles marking old pages
as young / accessed, and disallowing dirtying of clean pages that
aren't actually writable (e.g. shared pages that should COW, and
read-only memory regions when they are enabled in a future patch).
- Due to the use of MMU notifications we no longer need to keep the
page references after we've updated the GPA page tables.
Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: "Radim Krčmář" <rkrcmar@redhat.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: linux-mips@linux-mips.org
Cc: kvm@vger.kernel.org
2016-12-14 00:32:39 +08:00
|
|
|
case KVM_CAP_SYNC_MMU:
|
2017-02-08 18:50:15 +08:00
|
|
|
case KVM_CAP_IMMEDIATE_EXIT:
|
2013-05-24 00:49:09 +08:00
|
|
|
r = 1;
|
|
|
|
break;
|
2016-12-14 06:39:39 +08:00
|
|
|
case KVM_CAP_NR_VCPUS:
|
|
|
|
r = num_online_cpus();
|
|
|
|
break;
|
|
|
|
case KVM_CAP_MAX_VCPUS:
|
|
|
|
r = KVM_MAX_VCPUS;
|
|
|
|
break;
|
2019-05-24 00:43:08 +08:00
|
|
|
case KVM_CAP_MAX_VCPU_ID:
|
|
|
|
r = KVM_MAX_VCPU_ID;
|
|
|
|
break;
|
2014-12-09 07:07:56 +08:00
|
|
|
case KVM_CAP_MIPS_FPU:
|
2016-04-22 17:38:48 +08:00
|
|
|
/* We don't handle systems with inconsistent cpu_has_fpu */
|
|
|
|
r = !!raw_cpu_has_fpu;
|
2014-12-09 07:07:56 +08:00
|
|
|
break;
|
2014-12-09 07:07:56 +08:00
|
|
|
case KVM_CAP_MIPS_MSA:
|
|
|
|
/*
|
|
|
|
* We don't support MSA vector partitioning yet:
|
|
|
|
* 1) It would require explicit support which can't be tested
|
|
|
|
* yet due to lack of support in current hardware.
|
|
|
|
* 2) It extends the state that would need to be saved/restored
|
|
|
|
* by e.g. QEMU for migration.
|
|
|
|
*
|
|
|
|
* When vector partitioning hardware becomes available, support
|
|
|
|
* could be added by requiring a flag when enabling
|
|
|
|
* KVM_CAP_MIPS_MSA capability to indicate that userland knows
|
|
|
|
* to save/restore the appropriate extra state.
|
|
|
|
*/
|
|
|
|
r = cpu_has_msa && !(boot_cpu_data.msa_id & MSA_IR_WRPF);
|
|
|
|
break;
|
2012-11-22 10:34:02 +08:00
|
|
|
default:
|
2017-03-14 18:15:22 +08:00
|
|
|
r = kvm_mips_callbacks->check_extension(kvm, ext);
|
2012-11-22 10:34:02 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu)
|
|
|
|
{
|
2017-03-14 18:15:39 +08:00
|
|
|
return kvm_mips_pending_timer(vcpu) ||
|
|
|
|
kvm_read_c0_guest_cause(vcpu->arch.cop0) & C_TI;
|
2012-11-22 10:34:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int kvm_arch_vcpu_dump_regs(struct kvm_vcpu *vcpu)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct mips_coproc *cop0;
|
|
|
|
|
|
|
|
if (!vcpu)
|
|
|
|
return -1;
|
|
|
|
|
2014-06-27 03:11:35 +08:00
|
|
|
kvm_debug("VCPU Register Dump:\n");
|
|
|
|
kvm_debug("\tpc = 0x%08lx\n", vcpu->arch.pc);
|
|
|
|
kvm_debug("\texceptions: %08lx\n", vcpu->arch.pending_exceptions);
|
2012-11-22 10:34:02 +08:00
|
|
|
|
|
|
|
for (i = 0; i < 32; i += 4) {
|
2014-06-27 03:11:35 +08:00
|
|
|
kvm_debug("\tgpr%02d: %08lx %08lx %08lx %08lx\n", i,
|
2012-11-22 10:34:02 +08:00
|
|
|
vcpu->arch.gprs[i],
|
|
|
|
vcpu->arch.gprs[i + 1],
|
|
|
|
vcpu->arch.gprs[i + 2], vcpu->arch.gprs[i + 3]);
|
|
|
|
}
|
2014-06-27 03:11:35 +08:00
|
|
|
kvm_debug("\thi: 0x%08lx\n", vcpu->arch.hi);
|
|
|
|
kvm_debug("\tlo: 0x%08lx\n", vcpu->arch.lo);
|
2012-11-22 10:34:02 +08:00
|
|
|
|
|
|
|
cop0 = vcpu->arch.cop0;
|
KVM: MIPS: Abstract guest CP0 register access for VZ
Abstract the MIPS KVM guest CP0 register access macros into inline
functions which are generated by macros. This allows them to be
generated differently for VZ, where they will usually need to access the
hardware guest CP0 context rather than the saved values in RAM.
Accessors for each individual register are generated using these macros:
- __BUILD_KVM_*_SW() for registers which are not present in the VZ
hardware guest context, so kvm_{read,write}_c0_guest_##name() will
access the saved value in RAM regardless of whether VZ is enabled.
- __BUILD_KVM_*_HW() for registers which are present in the VZ hardware
guest context, so kvm_{read,write}_c0_guest_##name() will access the
hardware register when VZ is enabled.
These build the underlying accessors using further macros:
- __BUILD_KVM_*_SAVED() builds e.g. kvm_{read,write}_sw_gc0_##name()
functions for accessing the saved versions of the registers in RAM.
This is used for implementing the common
kvm_{read,write}_c0_guest_##name() accessors with T&E where registers
are always stored in RAM, but are also available with VZ HW registers
to allow them to be accessed while saved.
- __BUILD_KVM_*_VZ() builds e.g. kvm_{read,write}_vz_gc0_##name()
functions for accessing the VZ hardware guest context registers
directly. This is used for implementing the common
kvm_{read,write}_c0_guest_##name() accessors with VZ.
- __BUILD_KVM_*_WRAP() builds wrappers with different names, which
allows the common kvm_{read,write}_c0_guest_##name() functions to be
implemented using the VZ accessors while still having the SAVED
accessors available too.
- __BUILD_KVM_SAVE_VZ() builds functions for saving and restoring VZ
hardware guest context register state to RAM, improving conciseness
of VZ context saving and restoring.
Similar macros exist for generating modifiers (set, clear, change),
either with a normal unlocked read/modify/write, or using atomic LL/SC
sequences.
These changes change the types of 32-bit registers to u32 instead of
unsigned long, which requires some changes to printk() functions in MIPS
KVM.
Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: "Radim Krčmář" <rkrcmar@redhat.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: linux-mips@linux-mips.org
Cc: kvm@vger.kernel.org
2017-03-14 18:15:25 +08:00
|
|
|
kvm_debug("\tStatus: 0x%08x, Cause: 0x%08x\n",
|
2014-06-27 03:11:35 +08:00
|
|
|
kvm_read_c0_guest_status(cop0),
|
|
|
|
kvm_read_c0_guest_cause(cop0));
|
2012-11-22 10:34:02 +08:00
|
|
|
|
2014-06-27 03:11:35 +08:00
|
|
|
kvm_debug("\tEPC: 0x%08lx\n", kvm_read_c0_guest_epc(cop0));
|
2012-11-22 10:34:02 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2017-12-05 04:35:27 +08:00
|
|
|
vcpu_load(vcpu);
|
|
|
|
|
2013-05-24 00:49:08 +08:00
|
|
|
for (i = 1; i < ARRAY_SIZE(vcpu->arch.gprs); i++)
|
2013-05-24 00:49:07 +08:00
|
|
|
vcpu->arch.gprs[i] = regs->gpr[i];
|
2013-05-24 00:49:08 +08:00
|
|
|
vcpu->arch.gprs[0] = 0; /* zero is special, and cannot be set. */
|
2012-11-22 10:34:02 +08:00
|
|
|
vcpu->arch.hi = regs->hi;
|
|
|
|
vcpu->arch.lo = regs->lo;
|
|
|
|
vcpu->arch.pc = regs->pc;
|
|
|
|
|
2017-12-05 04:35:27 +08:00
|
|
|
vcpu_put(vcpu);
|
2013-05-24 00:49:09 +08:00
|
|
|
return 0;
|
2012-11-22 10:34:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2017-12-05 04:35:26 +08:00
|
|
|
vcpu_load(vcpu);
|
|
|
|
|
2013-05-24 00:49:08 +08:00
|
|
|
for (i = 0; i < ARRAY_SIZE(vcpu->arch.gprs); i++)
|
2013-05-24 00:49:07 +08:00
|
|
|
regs->gpr[i] = vcpu->arch.gprs[i];
|
2012-11-22 10:34:02 +08:00
|
|
|
|
|
|
|
regs->hi = vcpu->arch.hi;
|
|
|
|
regs->lo = vcpu->arch.lo;
|
|
|
|
regs->pc = vcpu->arch.pc;
|
|
|
|
|
2017-12-05 04:35:26 +08:00
|
|
|
vcpu_put(vcpu);
|
2013-05-24 00:49:09 +08:00
|
|
|
return 0;
|
2012-11-22 10:34:02 +08:00
|
|
|
}
|
|
|
|
|
2014-06-27 03:11:34 +08:00
|
|
|
int kvm_arch_vcpu_ioctl_translate(struct kvm_vcpu *vcpu,
|
|
|
|
struct kvm_translation *tr)
|
2012-11-22 10:34:02 +08:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-06-27 03:11:34 +08:00
|
|
|
static void kvm_mips_set_c0_status(void)
|
2012-11-22 10:34:02 +08:00
|
|
|
{
|
2016-06-09 21:19:08 +08:00
|
|
|
u32 status = read_c0_status();
|
2012-11-22 10:34:02 +08:00
|
|
|
|
|
|
|
if (cpu_has_dsp)
|
|
|
|
status |= (ST0_MX);
|
|
|
|
|
|
|
|
write_c0_status(status);
|
|
|
|
ehb();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return value is in the form (errcode<<2 | RESUME_FLAG_HOST | RESUME_FLAG_NV)
|
|
|
|
*/
|
2020-06-23 21:14:18 +08:00
|
|
|
int kvm_mips_handle_exit(struct kvm_vcpu *vcpu)
|
2012-11-22 10:34:02 +08:00
|
|
|
{
|
2020-06-23 21:14:18 +08:00
|
|
|
struct kvm_run *run = vcpu->run;
|
2016-06-09 21:19:08 +08:00
|
|
|
u32 cause = vcpu->arch.host_cp0_cause;
|
|
|
|
u32 exccode = (cause >> CAUSEB_EXCCODE) & 0x1f;
|
|
|
|
u32 __user *opc = (u32 __user *) vcpu->arch.pc;
|
2012-11-22 10:34:02 +08:00
|
|
|
unsigned long badvaddr = vcpu->arch.host_cp0_badvaddr;
|
|
|
|
enum emulation_result er = EMULATE_DONE;
|
2016-11-29 01:23:14 +08:00
|
|
|
u32 inst;
|
2012-11-22 10:34:02 +08:00
|
|
|
int ret = RESUME_GUEST;
|
|
|
|
|
2016-11-29 06:45:04 +08:00
|
|
|
vcpu->mode = OUTSIDE_GUEST_MODE;
|
|
|
|
|
2015-02-04 18:52:03 +08:00
|
|
|
/* re-enable HTW before enabling interrupts */
|
2017-03-14 18:15:30 +08:00
|
|
|
if (!IS_ENABLED(CONFIG_KVM_MIPS_VZ))
|
|
|
|
htw_start();
|
2015-02-04 18:52:03 +08:00
|
|
|
|
2012-11-22 10:34:02 +08:00
|
|
|
/* Set a default exit reason */
|
|
|
|
run->exit_reason = KVM_EXIT_UNKNOWN;
|
|
|
|
run->ready_for_interrupt_injection = 1;
|
|
|
|
|
2014-06-27 03:11:34 +08:00
|
|
|
/*
|
|
|
|
* Set the appropriate status bits based on host CPU features,
|
|
|
|
* before we hit the scheduler
|
|
|
|
*/
|
2012-11-22 10:34:02 +08:00
|
|
|
kvm_mips_set_c0_status();
|
|
|
|
|
|
|
|
local_irq_enable();
|
|
|
|
|
|
|
|
kvm_debug("kvm_mips_handle_exit: cause: %#x, PC: %p, kvm_run: %p, kvm_vcpu: %p\n",
|
|
|
|
cause, opc, run, vcpu);
|
2016-06-14 16:40:12 +08:00
|
|
|
trace_kvm_exit(vcpu, exccode);
|
2012-11-22 10:34:02 +08:00
|
|
|
|
2017-03-14 18:15:30 +08:00
|
|
|
if (!IS_ENABLED(CONFIG_KVM_MIPS_VZ)) {
|
|
|
|
/*
|
|
|
|
* Do a privilege check, if in UM most of these exit conditions
|
|
|
|
* end up causing an exception to be delivered to the Guest
|
|
|
|
* Kernel
|
|
|
|
*/
|
2020-06-23 21:14:17 +08:00
|
|
|
er = kvm_mips_check_privilege(cause, opc, vcpu);
|
2017-03-14 18:15:30 +08:00
|
|
|
if (er == EMULATE_PRIV_FAIL) {
|
|
|
|
goto skip_emul;
|
|
|
|
} else if (er == EMULATE_FAIL) {
|
|
|
|
run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
|
|
|
|
ret = RESUME_HOST;
|
|
|
|
goto skip_emul;
|
|
|
|
}
|
2012-11-22 10:34:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (exccode) {
|
2015-12-17 07:49:33 +08:00
|
|
|
case EXCCODE_INT:
|
|
|
|
kvm_debug("[%d]EXCCODE_INT @ %p\n", vcpu->vcpu_id, opc);
|
2012-11-22 10:34:02 +08:00
|
|
|
|
|
|
|
++vcpu->stat.int_exits;
|
|
|
|
|
2014-06-27 03:11:34 +08:00
|
|
|
if (need_resched())
|
2012-11-22 10:34:02 +08:00
|
|
|
cond_resched();
|
|
|
|
|
|
|
|
ret = RESUME_GUEST;
|
|
|
|
break;
|
|
|
|
|
2015-12-17 07:49:33 +08:00
|
|
|
case EXCCODE_CPU:
|
|
|
|
kvm_debug("EXCCODE_CPU: @ PC: %p\n", opc);
|
2012-11-22 10:34:02 +08:00
|
|
|
|
|
|
|
++vcpu->stat.cop_unusable_exits;
|
|
|
|
ret = kvm_mips_callbacks->handle_cop_unusable(vcpu);
|
|
|
|
/* XXXKYMA: Might need to return to user space */
|
2014-06-27 03:11:34 +08:00
|
|
|
if (run->exit_reason == KVM_EXIT_IRQ_WINDOW_OPEN)
|
2012-11-22 10:34:02 +08:00
|
|
|
ret = RESUME_HOST;
|
|
|
|
break;
|
|
|
|
|
2015-12-17 07:49:33 +08:00
|
|
|
case EXCCODE_MOD:
|
2012-11-22 10:34:02 +08:00
|
|
|
++vcpu->stat.tlbmod_exits;
|
|
|
|
ret = kvm_mips_callbacks->handle_tlb_mod(vcpu);
|
|
|
|
break;
|
|
|
|
|
2015-12-17 07:49:33 +08:00
|
|
|
case EXCCODE_TLBS:
|
KVM: MIPS: Abstract guest CP0 register access for VZ
Abstract the MIPS KVM guest CP0 register access macros into inline
functions which are generated by macros. This allows them to be
generated differently for VZ, where they will usually need to access the
hardware guest CP0 context rather than the saved values in RAM.
Accessors for each individual register are generated using these macros:
- __BUILD_KVM_*_SW() for registers which are not present in the VZ
hardware guest context, so kvm_{read,write}_c0_guest_##name() will
access the saved value in RAM regardless of whether VZ is enabled.
- __BUILD_KVM_*_HW() for registers which are present in the VZ hardware
guest context, so kvm_{read,write}_c0_guest_##name() will access the
hardware register when VZ is enabled.
These build the underlying accessors using further macros:
- __BUILD_KVM_*_SAVED() builds e.g. kvm_{read,write}_sw_gc0_##name()
functions for accessing the saved versions of the registers in RAM.
This is used for implementing the common
kvm_{read,write}_c0_guest_##name() accessors with T&E where registers
are always stored in RAM, but are also available with VZ HW registers
to allow them to be accessed while saved.
- __BUILD_KVM_*_VZ() builds e.g. kvm_{read,write}_vz_gc0_##name()
functions for accessing the VZ hardware guest context registers
directly. This is used for implementing the common
kvm_{read,write}_c0_guest_##name() accessors with VZ.
- __BUILD_KVM_*_WRAP() builds wrappers with different names, which
allows the common kvm_{read,write}_c0_guest_##name() functions to be
implemented using the VZ accessors while still having the SAVED
accessors available too.
- __BUILD_KVM_SAVE_VZ() builds functions for saving and restoring VZ
hardware guest context register state to RAM, improving conciseness
of VZ context saving and restoring.
Similar macros exist for generating modifiers (set, clear, change),
either with a normal unlocked read/modify/write, or using atomic LL/SC
sequences.
These changes change the types of 32-bit registers to u32 instead of
unsigned long, which requires some changes to printk() functions in MIPS
KVM.
Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: "Radim Krčmář" <rkrcmar@redhat.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: linux-mips@linux-mips.org
Cc: kvm@vger.kernel.org
2017-03-14 18:15:25 +08:00
|
|
|
kvm_debug("TLB ST fault: cause %#x, status %#x, PC: %p, BadVaddr: %#lx\n",
|
2014-06-27 03:11:34 +08:00
|
|
|
cause, kvm_read_c0_guest_status(vcpu->arch.cop0), opc,
|
|
|
|
badvaddr);
|
2012-11-22 10:34:02 +08:00
|
|
|
|
|
|
|
++vcpu->stat.tlbmiss_st_exits;
|
|
|
|
ret = kvm_mips_callbacks->handle_tlb_st_miss(vcpu);
|
|
|
|
break;
|
|
|
|
|
2015-12-17 07:49:33 +08:00
|
|
|
case EXCCODE_TLBL:
|
2012-11-22 10:34:02 +08:00
|
|
|
kvm_debug("TLB LD fault: cause %#x, PC: %p, BadVaddr: %#lx\n",
|
|
|
|
cause, opc, badvaddr);
|
|
|
|
|
|
|
|
++vcpu->stat.tlbmiss_ld_exits;
|
|
|
|
ret = kvm_mips_callbacks->handle_tlb_ld_miss(vcpu);
|
|
|
|
break;
|
|
|
|
|
2015-12-17 07:49:33 +08:00
|
|
|
case EXCCODE_ADES:
|
2012-11-22 10:34:02 +08:00
|
|
|
++vcpu->stat.addrerr_st_exits;
|
|
|
|
ret = kvm_mips_callbacks->handle_addr_err_st(vcpu);
|
|
|
|
break;
|
|
|
|
|
2015-12-17 07:49:33 +08:00
|
|
|
case EXCCODE_ADEL:
|
2012-11-22 10:34:02 +08:00
|
|
|
++vcpu->stat.addrerr_ld_exits;
|
|
|
|
ret = kvm_mips_callbacks->handle_addr_err_ld(vcpu);
|
|
|
|
break;
|
|
|
|
|
2015-12-17 07:49:33 +08:00
|
|
|
case EXCCODE_SYS:
|
2012-11-22 10:34:02 +08:00
|
|
|
++vcpu->stat.syscall_exits;
|
|
|
|
ret = kvm_mips_callbacks->handle_syscall(vcpu);
|
|
|
|
break;
|
|
|
|
|
2015-12-17 07:49:33 +08:00
|
|
|
case EXCCODE_RI:
|
2012-11-22 10:34:02 +08:00
|
|
|
++vcpu->stat.resvd_inst_exits;
|
|
|
|
ret = kvm_mips_callbacks->handle_res_inst(vcpu);
|
|
|
|
break;
|
|
|
|
|
2015-12-17 07:49:33 +08:00
|
|
|
case EXCCODE_BP:
|
2012-11-22 10:34:02 +08:00
|
|
|
++vcpu->stat.break_inst_exits;
|
|
|
|
ret = kvm_mips_callbacks->handle_break(vcpu);
|
|
|
|
break;
|
|
|
|
|
2015-12-17 07:49:33 +08:00
|
|
|
case EXCCODE_TR:
|
2015-02-07 00:03:57 +08:00
|
|
|
++vcpu->stat.trap_inst_exits;
|
|
|
|
ret = kvm_mips_callbacks->handle_trap(vcpu);
|
|
|
|
break;
|
|
|
|
|
2015-12-17 07:49:33 +08:00
|
|
|
case EXCCODE_MSAFPE:
|
2015-02-06 18:56:27 +08:00
|
|
|
++vcpu->stat.msa_fpe_exits;
|
|
|
|
ret = kvm_mips_callbacks->handle_msa_fpe(vcpu);
|
|
|
|
break;
|
|
|
|
|
2015-12-17 07:49:33 +08:00
|
|
|
case EXCCODE_FPE:
|
2015-02-06 18:56:27 +08:00
|
|
|
++vcpu->stat.fpe_exits;
|
|
|
|
ret = kvm_mips_callbacks->handle_fpe(vcpu);
|
|
|
|
break;
|
|
|
|
|
2015-12-17 07:49:33 +08:00
|
|
|
case EXCCODE_MSADIS:
|
2015-02-06 18:56:27 +08:00
|
|
|
++vcpu->stat.msa_disabled_exits;
|
2015-02-06 19:11:56 +08:00
|
|
|
ret = kvm_mips_callbacks->handle_msa_disabled(vcpu);
|
|
|
|
break;
|
|
|
|
|
2017-03-14 18:15:24 +08:00
|
|
|
case EXCCODE_GE:
|
|
|
|
/* defer exit accounting to handler */
|
|
|
|
ret = kvm_mips_callbacks->handle_guest_exit(vcpu);
|
|
|
|
break;
|
|
|
|
|
2012-11-22 10:34:02 +08:00
|
|
|
default:
|
2016-11-29 01:23:14 +08:00
|
|
|
if (cause & CAUSEF_BD)
|
|
|
|
opc += 1;
|
|
|
|
inst = 0;
|
2015-04-23 23:54:35 +08:00
|
|
|
kvm_get_badinstr(opc, vcpu, &inst);
|
KVM: MIPS: Abstract guest CP0 register access for VZ
Abstract the MIPS KVM guest CP0 register access macros into inline
functions which are generated by macros. This allows them to be
generated differently for VZ, where they will usually need to access the
hardware guest CP0 context rather than the saved values in RAM.
Accessors for each individual register are generated using these macros:
- __BUILD_KVM_*_SW() for registers which are not present in the VZ
hardware guest context, so kvm_{read,write}_c0_guest_##name() will
access the saved value in RAM regardless of whether VZ is enabled.
- __BUILD_KVM_*_HW() for registers which are present in the VZ hardware
guest context, so kvm_{read,write}_c0_guest_##name() will access the
hardware register when VZ is enabled.
These build the underlying accessors using further macros:
- __BUILD_KVM_*_SAVED() builds e.g. kvm_{read,write}_sw_gc0_##name()
functions for accessing the saved versions of the registers in RAM.
This is used for implementing the common
kvm_{read,write}_c0_guest_##name() accessors with T&E where registers
are always stored in RAM, but are also available with VZ HW registers
to allow them to be accessed while saved.
- __BUILD_KVM_*_VZ() builds e.g. kvm_{read,write}_vz_gc0_##name()
functions for accessing the VZ hardware guest context registers
directly. This is used for implementing the common
kvm_{read,write}_c0_guest_##name() accessors with VZ.
- __BUILD_KVM_*_WRAP() builds wrappers with different names, which
allows the common kvm_{read,write}_c0_guest_##name() functions to be
implemented using the VZ accessors while still having the SAVED
accessors available too.
- __BUILD_KVM_SAVE_VZ() builds functions for saving and restoring VZ
hardware guest context register state to RAM, improving conciseness
of VZ context saving and restoring.
Similar macros exist for generating modifiers (set, clear, change),
either with a normal unlocked read/modify/write, or using atomic LL/SC
sequences.
These changes change the types of 32-bit registers to u32 instead of
unsigned long, which requires some changes to printk() functions in MIPS
KVM.
Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: "Radim Krčmář" <rkrcmar@redhat.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: linux-mips@linux-mips.org
Cc: kvm@vger.kernel.org
2017-03-14 18:15:25 +08:00
|
|
|
kvm_err("Exception Code: %d, not yet handled, @ PC: %p, inst: 0x%08x BadVaddr: %#lx Status: %#x\n",
|
2016-11-29 01:23:14 +08:00
|
|
|
exccode, opc, inst, badvaddr,
|
2014-06-27 03:11:34 +08:00
|
|
|
kvm_read_c0_guest_status(vcpu->arch.cop0));
|
2012-11-22 10:34:02 +08:00
|
|
|
kvm_arch_vcpu_dump_regs(vcpu);
|
|
|
|
run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
|
|
|
|
ret = RESUME_HOST;
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
skip_emul:
|
|
|
|
local_irq_disable();
|
|
|
|
|
2017-03-14 18:15:39 +08:00
|
|
|
if (ret == RESUME_GUEST)
|
|
|
|
kvm_vz_acquire_htimer(vcpu);
|
|
|
|
|
2012-11-22 10:34:02 +08:00
|
|
|
if (er == EMULATE_DONE && !(ret & RESUME_HOST))
|
|
|
|
kvm_mips_deliver_interrupts(vcpu, cause);
|
|
|
|
|
|
|
|
if (!(ret & RESUME_HOST)) {
|
2014-06-27 03:11:34 +08:00
|
|
|
/* Only check for signals if not already exiting to userspace */
|
2012-11-22 10:34:02 +08:00
|
|
|
if (signal_pending(current)) {
|
|
|
|
run->exit_reason = KVM_EXIT_INTR;
|
|
|
|
ret = (-EINTR << 2) | RESUME_HOST;
|
|
|
|
++vcpu->stat.signal_exits;
|
2016-06-14 16:40:12 +08:00
|
|
|
trace_kvm_exit(vcpu, KVM_TRACE_EXIT_SIGNAL);
|
2012-11-22 10:34:02 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-18 22:09:12 +08:00
|
|
|
if (ret == RESUME_GUEST) {
|
2016-06-14 16:40:14 +08:00
|
|
|
trace_kvm_reenter(vcpu);
|
|
|
|
|
2016-11-29 06:45:04 +08:00
|
|
|
/*
|
|
|
|
* Make sure the read of VCPU requests in vcpu_reenter()
|
|
|
|
* callback is not reordered ahead of the write to vcpu->mode,
|
|
|
|
* or we could miss a TLB flush request while the requester sees
|
|
|
|
* the VCPU as outside of guest mode and not needing an IPI.
|
|
|
|
*/
|
|
|
|
smp_store_mb(vcpu->mode, IN_GUEST_MODE);
|
|
|
|
|
2020-06-23 21:14:17 +08:00
|
|
|
kvm_mips_callbacks->vcpu_reenter(vcpu);
|
2016-09-16 07:06:43 +08:00
|
|
|
|
2014-11-18 22:09:12 +08:00
|
|
|
/*
|
2015-03-05 19:43:36 +08:00
|
|
|
* If FPU / MSA are enabled (i.e. the guest's FPU / MSA context
|
|
|
|
* is live), restore FCR31 / MSACSR.
|
2014-11-18 22:09:12 +08:00
|
|
|
*
|
|
|
|
* This should be before returning to the guest exception
|
2015-03-05 19:43:36 +08:00
|
|
|
* vector, as it may well cause an [MSA] FP exception if there
|
|
|
|
* are pending exception bits unmasked. (see
|
2014-11-18 22:09:12 +08:00
|
|
|
* kvm_mips_csr_die_notifier() for how that is handled).
|
|
|
|
*/
|
|
|
|
if (kvm_mips_guest_has_fpu(&vcpu->arch) &&
|
|
|
|
read_c0_status() & ST0_CU1)
|
|
|
|
__kvm_restore_fcsr(&vcpu->arch);
|
2015-03-05 19:43:36 +08:00
|
|
|
|
|
|
|
if (kvm_mips_guest_has_msa(&vcpu->arch) &&
|
|
|
|
read_c0_config5() & MIPS_CONF5_MSAEN)
|
|
|
|
__kvm_restore_msacsr(&vcpu->arch);
|
2014-11-18 22:09:12 +08:00
|
|
|
}
|
|
|
|
|
2015-02-04 18:52:03 +08:00
|
|
|
/* Disable HTW before returning to guest or host */
|
2017-03-14 18:15:30 +08:00
|
|
|
if (!IS_ENABLED(CONFIG_KVM_MIPS_VZ))
|
|
|
|
htw_stop();
|
2015-02-04 18:52:03 +08:00
|
|
|
|
2012-11-22 10:34:02 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-11-18 22:09:12 +08:00
|
|
|
/* Enable FPU for guest and restore context */
|
|
|
|
void kvm_own_fpu(struct kvm_vcpu *vcpu)
|
|
|
|
{
|
|
|
|
struct mips_coproc *cop0 = vcpu->arch.cop0;
|
|
|
|
unsigned int sr, cfg5;
|
|
|
|
|
|
|
|
preempt_disable();
|
|
|
|
|
2015-03-05 19:43:36 +08:00
|
|
|
sr = kvm_read_c0_guest_status(cop0);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If MSA state is already live, it is undefined how it interacts with
|
|
|
|
* FR=0 FPU state, and we don't want to hit reserved instruction
|
|
|
|
* exceptions trying to save the MSA state later when CU=1 && FR=1, so
|
|
|
|
* play it safe and save it first.
|
|
|
|
*
|
|
|
|
* In theory we shouldn't ever hit this case since kvm_lose_fpu() should
|
|
|
|
* get called when guest CU1 is set, however we can't trust the guest
|
|
|
|
* not to clobber the status register directly via the commpage.
|
|
|
|
*/
|
|
|
|
if (cpu_has_msa && sr & ST0_CU1 && !(sr & ST0_FR) &&
|
2016-06-14 16:40:10 +08:00
|
|
|
vcpu->arch.aux_inuse & KVM_MIPS_AUX_MSA)
|
2015-03-05 19:43:36 +08:00
|
|
|
kvm_lose_fpu(vcpu);
|
|
|
|
|
2014-11-18 22:09:12 +08:00
|
|
|
/*
|
|
|
|
* Enable FPU for guest
|
|
|
|
* We set FR and FRE according to guest context
|
|
|
|
*/
|
|
|
|
change_c0_status(ST0_CU1 | ST0_FR, sr);
|
|
|
|
if (cpu_has_fre) {
|
|
|
|
cfg5 = kvm_read_c0_guest_config5(cop0);
|
|
|
|
change_c0_config5(MIPS_CONF5_FRE, cfg5);
|
|
|
|
}
|
|
|
|
enable_fpu_hazard();
|
|
|
|
|
|
|
|
/* If guest FPU state not active, restore it now */
|
2016-06-14 16:40:10 +08:00
|
|
|
if (!(vcpu->arch.aux_inuse & KVM_MIPS_AUX_FPU)) {
|
2014-11-18 22:09:12 +08:00
|
|
|
__kvm_restore_fpu(&vcpu->arch);
|
2016-06-14 16:40:10 +08:00
|
|
|
vcpu->arch.aux_inuse |= KVM_MIPS_AUX_FPU;
|
2016-06-14 16:40:11 +08:00
|
|
|
trace_kvm_aux(vcpu, KVM_TRACE_AUX_RESTORE, KVM_TRACE_AUX_FPU);
|
|
|
|
} else {
|
|
|
|
trace_kvm_aux(vcpu, KVM_TRACE_AUX_ENABLE, KVM_TRACE_AUX_FPU);
|
2014-11-18 22:09:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
preempt_enable();
|
|
|
|
}
|
|
|
|
|
2015-03-05 19:43:36 +08:00
|
|
|
#ifdef CONFIG_CPU_HAS_MSA
|
|
|
|
/* Enable MSA for guest and restore context */
|
|
|
|
void kvm_own_msa(struct kvm_vcpu *vcpu)
|
|
|
|
{
|
|
|
|
struct mips_coproc *cop0 = vcpu->arch.cop0;
|
|
|
|
unsigned int sr, cfg5;
|
|
|
|
|
|
|
|
preempt_disable();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Enable FPU if enabled in guest, since we're restoring FPU context
|
|
|
|
* anyway. We set FR and FRE according to guest context.
|
|
|
|
*/
|
|
|
|
if (kvm_mips_guest_has_fpu(&vcpu->arch)) {
|
|
|
|
sr = kvm_read_c0_guest_status(cop0);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If FR=0 FPU state is already live, it is undefined how it
|
|
|
|
* interacts with MSA state, so play it safe and save it first.
|
|
|
|
*/
|
|
|
|
if (!(sr & ST0_FR) &&
|
2016-06-14 16:40:10 +08:00
|
|
|
(vcpu->arch.aux_inuse & (KVM_MIPS_AUX_FPU |
|
|
|
|
KVM_MIPS_AUX_MSA)) == KVM_MIPS_AUX_FPU)
|
2015-03-05 19:43:36 +08:00
|
|
|
kvm_lose_fpu(vcpu);
|
|
|
|
|
|
|
|
change_c0_status(ST0_CU1 | ST0_FR, sr);
|
|
|
|
if (sr & ST0_CU1 && cpu_has_fre) {
|
|
|
|
cfg5 = kvm_read_c0_guest_config5(cop0);
|
|
|
|
change_c0_config5(MIPS_CONF5_FRE, cfg5);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Enable MSA for guest */
|
|
|
|
set_c0_config5(MIPS_CONF5_MSAEN);
|
|
|
|
enable_fpu_hazard();
|
|
|
|
|
2016-06-14 16:40:10 +08:00
|
|
|
switch (vcpu->arch.aux_inuse & (KVM_MIPS_AUX_FPU | KVM_MIPS_AUX_MSA)) {
|
|
|
|
case KVM_MIPS_AUX_FPU:
|
2015-03-05 19:43:36 +08:00
|
|
|
/*
|
|
|
|
* Guest FPU state already loaded, only restore upper MSA state
|
|
|
|
*/
|
|
|
|
__kvm_restore_msa_upper(&vcpu->arch);
|
2016-06-14 16:40:10 +08:00
|
|
|
vcpu->arch.aux_inuse |= KVM_MIPS_AUX_MSA;
|
2016-06-14 16:40:11 +08:00
|
|
|
trace_kvm_aux(vcpu, KVM_TRACE_AUX_RESTORE, KVM_TRACE_AUX_MSA);
|
2015-03-05 19:43:36 +08:00
|
|
|
break;
|
|
|
|
case 0:
|
|
|
|
/* Neither FPU or MSA already active, restore full MSA state */
|
|
|
|
__kvm_restore_msa(&vcpu->arch);
|
2016-06-14 16:40:10 +08:00
|
|
|
vcpu->arch.aux_inuse |= KVM_MIPS_AUX_MSA;
|
2015-03-05 19:43:36 +08:00
|
|
|
if (kvm_mips_guest_has_fpu(&vcpu->arch))
|
2016-06-14 16:40:10 +08:00
|
|
|
vcpu->arch.aux_inuse |= KVM_MIPS_AUX_FPU;
|
2016-06-14 16:40:11 +08:00
|
|
|
trace_kvm_aux(vcpu, KVM_TRACE_AUX_RESTORE,
|
|
|
|
KVM_TRACE_AUX_FPU_MSA);
|
2015-03-05 19:43:36 +08:00
|
|
|
break;
|
|
|
|
default:
|
2016-06-14 16:40:11 +08:00
|
|
|
trace_kvm_aux(vcpu, KVM_TRACE_AUX_ENABLE, KVM_TRACE_AUX_MSA);
|
2015-03-05 19:43:36 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
preempt_enable();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Drop FPU & MSA without saving it */
|
2014-11-18 22:09:12 +08:00
|
|
|
void kvm_drop_fpu(struct kvm_vcpu *vcpu)
|
|
|
|
{
|
|
|
|
preempt_disable();
|
2016-06-14 16:40:10 +08:00
|
|
|
if (cpu_has_msa && vcpu->arch.aux_inuse & KVM_MIPS_AUX_MSA) {
|
2015-03-05 19:43:36 +08:00
|
|
|
disable_msa();
|
2016-06-14 16:40:11 +08:00
|
|
|
trace_kvm_aux(vcpu, KVM_TRACE_AUX_DISCARD, KVM_TRACE_AUX_MSA);
|
2016-06-14 16:40:10 +08:00
|
|
|
vcpu->arch.aux_inuse &= ~KVM_MIPS_AUX_MSA;
|
2015-03-05 19:43:36 +08:00
|
|
|
}
|
2016-06-14 16:40:10 +08:00
|
|
|
if (vcpu->arch.aux_inuse & KVM_MIPS_AUX_FPU) {
|
2014-11-18 22:09:12 +08:00
|
|
|
clear_c0_status(ST0_CU1 | ST0_FR);
|
2016-06-14 16:40:11 +08:00
|
|
|
trace_kvm_aux(vcpu, KVM_TRACE_AUX_DISCARD, KVM_TRACE_AUX_FPU);
|
2016-06-14 16:40:10 +08:00
|
|
|
vcpu->arch.aux_inuse &= ~KVM_MIPS_AUX_FPU;
|
2014-11-18 22:09:12 +08:00
|
|
|
}
|
|
|
|
preempt_enable();
|
|
|
|
}
|
|
|
|
|
2015-03-05 19:43:36 +08:00
|
|
|
/* Save and disable FPU & MSA */
|
2014-11-18 22:09:12 +08:00
|
|
|
void kvm_lose_fpu(struct kvm_vcpu *vcpu)
|
|
|
|
{
|
|
|
|
/*
|
2017-03-14 18:15:17 +08:00
|
|
|
* With T&E, FPU & MSA get disabled in root context (hardware) when it
|
|
|
|
* is disabled in guest context (software), but the register state in
|
|
|
|
* the hardware may still be in use.
|
|
|
|
* This is why we explicitly re-enable the hardware before saving.
|
2014-11-18 22:09:12 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
preempt_disable();
|
2016-06-14 16:40:10 +08:00
|
|
|
if (cpu_has_msa && vcpu->arch.aux_inuse & KVM_MIPS_AUX_MSA) {
|
2017-03-14 18:15:17 +08:00
|
|
|
if (!IS_ENABLED(CONFIG_KVM_MIPS_VZ)) {
|
|
|
|
set_c0_config5(MIPS_CONF5_MSAEN);
|
|
|
|
enable_fpu_hazard();
|
|
|
|
}
|
2015-03-05 19:43:36 +08:00
|
|
|
|
|
|
|
__kvm_save_msa(&vcpu->arch);
|
2016-06-14 16:40:11 +08:00
|
|
|
trace_kvm_aux(vcpu, KVM_TRACE_AUX_SAVE, KVM_TRACE_AUX_FPU_MSA);
|
2015-03-05 19:43:36 +08:00
|
|
|
|
|
|
|
/* Disable MSA & FPU */
|
|
|
|
disable_msa();
|
2016-06-14 16:40:10 +08:00
|
|
|
if (vcpu->arch.aux_inuse & KVM_MIPS_AUX_FPU) {
|
2015-03-05 19:43:36 +08:00
|
|
|
clear_c0_status(ST0_CU1 | ST0_FR);
|
2016-04-22 17:38:49 +08:00
|
|
|
disable_fpu_hazard();
|
|
|
|
}
|
2016-06-14 16:40:10 +08:00
|
|
|
vcpu->arch.aux_inuse &= ~(KVM_MIPS_AUX_FPU | KVM_MIPS_AUX_MSA);
|
|
|
|
} else if (vcpu->arch.aux_inuse & KVM_MIPS_AUX_FPU) {
|
2017-03-14 18:15:17 +08:00
|
|
|
if (!IS_ENABLED(CONFIG_KVM_MIPS_VZ)) {
|
|
|
|
set_c0_status(ST0_CU1);
|
|
|
|
enable_fpu_hazard();
|
|
|
|
}
|
2014-11-18 22:09:12 +08:00
|
|
|
|
|
|
|
__kvm_save_fpu(&vcpu->arch);
|
2016-06-14 16:40:10 +08:00
|
|
|
vcpu->arch.aux_inuse &= ~KVM_MIPS_AUX_FPU;
|
2016-06-14 16:40:11 +08:00
|
|
|
trace_kvm_aux(vcpu, KVM_TRACE_AUX_SAVE, KVM_TRACE_AUX_FPU);
|
2014-11-18 22:09:12 +08:00
|
|
|
|
|
|
|
/* Disable FPU */
|
|
|
|
clear_c0_status(ST0_CU1 | ST0_FR);
|
2016-04-22 17:38:49 +08:00
|
|
|
disable_fpu_hazard();
|
2014-11-18 22:09:12 +08:00
|
|
|
}
|
|
|
|
preempt_enable();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2015-03-05 19:43:36 +08:00
|
|
|
* Step over a specific ctc1 to FCSR and a specific ctcmsa to MSACSR which are
|
|
|
|
* used to restore guest FCSR/MSACSR state and may trigger a "harmless" FP/MSAFP
|
|
|
|
* exception if cause bits are set in the value being written.
|
2014-11-18 22:09:12 +08:00
|
|
|
*/
|
|
|
|
static int kvm_mips_csr_die_notify(struct notifier_block *self,
|
|
|
|
unsigned long cmd, void *ptr)
|
|
|
|
{
|
|
|
|
struct die_args *args = (struct die_args *)ptr;
|
|
|
|
struct pt_regs *regs = args->regs;
|
|
|
|
unsigned long pc;
|
|
|
|
|
2015-03-05 19:43:36 +08:00
|
|
|
/* Only interested in FPE and MSAFPE */
|
|
|
|
if (cmd != DIE_FP && cmd != DIE_MSAFP)
|
2014-11-18 22:09:12 +08:00
|
|
|
return NOTIFY_DONE;
|
|
|
|
|
|
|
|
/* Return immediately if guest context isn't active */
|
|
|
|
if (!(current->flags & PF_VCPU))
|
|
|
|
return NOTIFY_DONE;
|
|
|
|
|
|
|
|
/* Should never get here from user mode */
|
|
|
|
BUG_ON(user_mode(regs));
|
|
|
|
|
|
|
|
pc = instruction_pointer(regs);
|
|
|
|
switch (cmd) {
|
|
|
|
case DIE_FP:
|
|
|
|
/* match 2nd instruction in __kvm_restore_fcsr */
|
|
|
|
if (pc != (unsigned long)&__kvm_restore_fcsr + 4)
|
|
|
|
return NOTIFY_DONE;
|
|
|
|
break;
|
2015-03-05 19:43:36 +08:00
|
|
|
case DIE_MSAFP:
|
|
|
|
/* match 2nd/3rd instruction in __kvm_restore_msacsr */
|
|
|
|
if (!cpu_has_msa ||
|
|
|
|
pc < (unsigned long)&__kvm_restore_msacsr + 4 ||
|
|
|
|
pc > (unsigned long)&__kvm_restore_msacsr + 8)
|
|
|
|
return NOTIFY_DONE;
|
|
|
|
break;
|
2014-11-18 22:09:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Move PC forward a little and continue executing */
|
|
|
|
instruction_pointer(regs) += 4;
|
|
|
|
|
|
|
|
return NOTIFY_STOP;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct notifier_block kvm_mips_csr_die_notifier = {
|
|
|
|
.notifier_call = kvm_mips_csr_die_notify,
|
|
|
|
};
|
|
|
|
|
KVM: MIPS: Add more types of virtual interrupts
In current implementation, MIPS KVM uses IP2, IP3, IP4 and IP7 for
external interrupt, two kinds of IPIs and timer interrupt respectively,
but Loongson-3 based machines prefer to use IP2, IP3, IP6 and IP7 for
two kinds of external interrupts, IPI and timer interrupt. So we define
two priority-irq mapping tables: kvm_loongson3_priority_to_irq[] for
Loongson-3, and kvm_default_priority_to_irq[] for others. The virtual
interrupt infrastructure is updated to deliver all types of interrupts
from IP2, IP3, IP4, IP6 and IP7.
Reviewed-by: Aleksandar Markovic <aleksandar.qemu.devel@gmail.com>
Signed-off-by: Huacai Chen <chenhc@lemote.com>
Co-developed-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
Message-Id: <1590220602-3547-10-git-send-email-chenhc@lemote.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2020-05-23 15:56:36 +08:00
|
|
|
static u32 kvm_default_priority_to_irq[MIPS_EXC_MAX] = {
|
|
|
|
[MIPS_EXC_INT_TIMER] = C_IRQ5,
|
|
|
|
[MIPS_EXC_INT_IO_1] = C_IRQ0,
|
|
|
|
[MIPS_EXC_INT_IPI_1] = C_IRQ1,
|
|
|
|
[MIPS_EXC_INT_IPI_2] = C_IRQ2,
|
|
|
|
};
|
|
|
|
|
|
|
|
static u32 kvm_loongson3_priority_to_irq[MIPS_EXC_MAX] = {
|
|
|
|
[MIPS_EXC_INT_TIMER] = C_IRQ5,
|
|
|
|
[MIPS_EXC_INT_IO_1] = C_IRQ0,
|
|
|
|
[MIPS_EXC_INT_IO_2] = C_IRQ1,
|
|
|
|
[MIPS_EXC_INT_IPI_1] = C_IRQ4,
|
|
|
|
};
|
|
|
|
|
|
|
|
u32 *kvm_priority_to_irq = kvm_default_priority_to_irq;
|
|
|
|
|
|
|
|
u32 kvm_irq_to_priority(u32 irq)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = MIPS_EXC_INT_TIMER; i < MIPS_EXC_MAX; i++) {
|
|
|
|
if (kvm_priority_to_irq[i] == (1 << (irq + 8)))
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
return MIPS_EXC_MAX;
|
|
|
|
}
|
|
|
|
|
2015-12-17 07:49:32 +08:00
|
|
|
static int __init kvm_mips_init(void)
|
2012-11-22 10:34:02 +08:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
MIPS: MemoryMapID (MMID) Support
Introduce support for using MemoryMapIDs (MMIDs) as an alternative to
Address Space IDs (ASIDs). The major difference between the two is that
MMIDs are global - ie. an MMID uniquely identifies an address space
across all coherent CPUs. In contrast ASIDs are non-global per-CPU IDs,
wherein each address space is allocated a separate ASID for each CPU
upon which it is used. This global namespace allows a new GINVT
instruction be used to globally invalidate TLB entries associated with a
particular MMID across all coherent CPUs in the system, removing the
need for IPIs to invalidate entries with separate ASIDs on each CPU.
The allocation scheme used here is largely borrowed from arm64 (see
arch/arm64/mm/context.c). In essence we maintain a bitmap to track
available MMIDs, and MMIDs in active use at the time of a rollover to a
new MMID version are preserved in the new version. The allocation scheme
requires efficient 64 bit atomics in order to perform reasonably, so
this support depends upon CONFIG_GENERIC_ATOMIC64=n (ie. currently it
will only be included in MIPS64 kernels).
The first, and currently only, available CPU with support for MMIDs is
the MIPS I6500. This CPU supports 16 bit MMIDs, and so for now we cap
our MMIDs to 16 bits wide in order to prevent the bitmap growing to
absurd sizes if any future CPU does implement 32 bit MMIDs as the
architecture manuals suggest is recommended.
When MMIDs are in use we also make use of GINVT instruction which is
available due to the global nature of MMIDs. By executing a sequence of
GINVT & SYNC 0x14 instructions we can avoid the overhead of an IPI to
each remote CPU in many cases. One complication is that GINVT will
invalidate wired entries (in all cases apart from type 0, which targets
the entire TLB). In order to avoid GINVT invalidating any wired TLB
entries we set up, we make sure to create those entries using a reserved
MMID (0) that we never associate with any address space.
Also of note is that KVM will require further work in order to support
MMIDs & GINVT, since KVM is involved in allocating IDs for guests & in
configuring the MMU. That work is not part of this patch, so for now
when MMIDs are in use KVM is disabled.
Signed-off-by: Paul Burton <paul.burton@mips.com>
Cc: linux-mips@vger.kernel.org
2019-02-02 09:43:28 +08:00
|
|
|
if (cpu_has_mmid) {
|
|
|
|
pr_warn("KVM does not yet support MMIDs. KVM Disabled\n");
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
2016-06-24 00:34:45 +08:00
|
|
|
ret = kvm_mips_entry_setup();
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2012-11-22 10:34:02 +08:00
|
|
|
ret = kvm_init(NULL, sizeof(struct kvm_vcpu), 0, THIS_MODULE);
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
KVM: MIPS: Add more types of virtual interrupts
In current implementation, MIPS KVM uses IP2, IP3, IP4 and IP7 for
external interrupt, two kinds of IPIs and timer interrupt respectively,
but Loongson-3 based machines prefer to use IP2, IP3, IP6 and IP7 for
two kinds of external interrupts, IPI and timer interrupt. So we define
two priority-irq mapping tables: kvm_loongson3_priority_to_irq[] for
Loongson-3, and kvm_default_priority_to_irq[] for others. The virtual
interrupt infrastructure is updated to deliver all types of interrupts
from IP2, IP3, IP4, IP6 and IP7.
Reviewed-by: Aleksandar Markovic <aleksandar.qemu.devel@gmail.com>
Signed-off-by: Huacai Chen <chenhc@lemote.com>
Co-developed-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
Message-Id: <1590220602-3547-10-git-send-email-chenhc@lemote.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2020-05-23 15:56:36 +08:00
|
|
|
if (boot_cpu_type() == CPU_LOONGSON64)
|
|
|
|
kvm_priority_to_irq = kvm_loongson3_priority_to_irq;
|
|
|
|
|
2014-11-18 22:09:12 +08:00
|
|
|
register_die_notifier(&kvm_mips_csr_die_notifier);
|
|
|
|
|
2012-11-22 10:34:02 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-12-17 07:49:32 +08:00
|
|
|
static void __exit kvm_mips_exit(void)
|
2012-11-22 10:34:02 +08:00
|
|
|
{
|
|
|
|
kvm_exit();
|
|
|
|
|
2014-11-18 22:09:12 +08:00
|
|
|
unregister_die_notifier(&kvm_mips_csr_die_notifier);
|
2012-11-22 10:34:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
module_init(kvm_mips_init);
|
|
|
|
module_exit(kvm_mips_exit);
|
|
|
|
|
|
|
|
EXPORT_TRACEPOINT_SYMBOL(kvm_exit);
|