18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
38c2ecf20Sopenharmony_ci * License.  See the file "COPYING" in the main directory of this archive
48c2ecf20Sopenharmony_ci * for more details.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * KVM/MIPS: MIPS specific KVM APIs
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Copyright (C) 2012  MIPS Technologies, Inc.  All rights reserved.
98c2ecf20Sopenharmony_ci * Authors: Sanjay Lal <sanjayl@kymasys.com>
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/bitops.h>
138c2ecf20Sopenharmony_ci#include <linux/errno.h>
148c2ecf20Sopenharmony_ci#include <linux/err.h>
158c2ecf20Sopenharmony_ci#include <linux/kdebug.h>
168c2ecf20Sopenharmony_ci#include <linux/module.h>
178c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
188c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
198c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
208c2ecf20Sopenharmony_ci#include <linux/fs.h>
218c2ecf20Sopenharmony_ci#include <linux/memblock.h>
228c2ecf20Sopenharmony_ci#include <linux/pgtable.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include <asm/fpu.h>
258c2ecf20Sopenharmony_ci#include <asm/page.h>
268c2ecf20Sopenharmony_ci#include <asm/cacheflush.h>
278c2ecf20Sopenharmony_ci#include <asm/mmu_context.h>
288c2ecf20Sopenharmony_ci#include <asm/pgalloc.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#include <linux/kvm_host.h>
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#include "interrupt.h"
338c2ecf20Sopenharmony_ci#include "commpage.h"
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#define CREATE_TRACE_POINTS
368c2ecf20Sopenharmony_ci#include "trace.h"
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#ifndef VECTORSPACING
398c2ecf20Sopenharmony_ci#define VECTORSPACING 0x100	/* for EI/VI mode */
408c2ecf20Sopenharmony_ci#endif
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistruct kvm_stats_debugfs_item debugfs_entries[] = {
438c2ecf20Sopenharmony_ci	VCPU_STAT("wait", wait_exits),
448c2ecf20Sopenharmony_ci	VCPU_STAT("cache", cache_exits),
458c2ecf20Sopenharmony_ci	VCPU_STAT("signal", signal_exits),
468c2ecf20Sopenharmony_ci	VCPU_STAT("interrupt", int_exits),
478c2ecf20Sopenharmony_ci	VCPU_STAT("cop_unusable", cop_unusable_exits),
488c2ecf20Sopenharmony_ci	VCPU_STAT("tlbmod", tlbmod_exits),
498c2ecf20Sopenharmony_ci	VCPU_STAT("tlbmiss_ld", tlbmiss_ld_exits),
508c2ecf20Sopenharmony_ci	VCPU_STAT("tlbmiss_st", tlbmiss_st_exits),
518c2ecf20Sopenharmony_ci	VCPU_STAT("addrerr_st", addrerr_st_exits),
528c2ecf20Sopenharmony_ci	VCPU_STAT("addrerr_ld", addrerr_ld_exits),
538c2ecf20Sopenharmony_ci	VCPU_STAT("syscall", syscall_exits),
548c2ecf20Sopenharmony_ci	VCPU_STAT("resvd_inst", resvd_inst_exits),
558c2ecf20Sopenharmony_ci	VCPU_STAT("break_inst", break_inst_exits),
568c2ecf20Sopenharmony_ci	VCPU_STAT("trap_inst", trap_inst_exits),
578c2ecf20Sopenharmony_ci	VCPU_STAT("msa_fpe", msa_fpe_exits),
588c2ecf20Sopenharmony_ci	VCPU_STAT("fpe", fpe_exits),
598c2ecf20Sopenharmony_ci	VCPU_STAT("msa_disabled", msa_disabled_exits),
608c2ecf20Sopenharmony_ci	VCPU_STAT("flush_dcache", flush_dcache_exits),
618c2ecf20Sopenharmony_ci#ifdef CONFIG_KVM_MIPS_VZ
628c2ecf20Sopenharmony_ci	VCPU_STAT("vz_gpsi", vz_gpsi_exits),
638c2ecf20Sopenharmony_ci	VCPU_STAT("vz_gsfc", vz_gsfc_exits),
648c2ecf20Sopenharmony_ci	VCPU_STAT("vz_hc", vz_hc_exits),
658c2ecf20Sopenharmony_ci	VCPU_STAT("vz_grr", vz_grr_exits),
668c2ecf20Sopenharmony_ci	VCPU_STAT("vz_gva", vz_gva_exits),
678c2ecf20Sopenharmony_ci	VCPU_STAT("vz_ghfc", vz_ghfc_exits),
688c2ecf20Sopenharmony_ci	VCPU_STAT("vz_gpa", vz_gpa_exits),
698c2ecf20Sopenharmony_ci	VCPU_STAT("vz_resvd", vz_resvd_exits),
708c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_LOONGSON64
718c2ecf20Sopenharmony_ci	VCPU_STAT("vz_cpucfg", vz_cpucfg_exits),
728c2ecf20Sopenharmony_ci#endif
738c2ecf20Sopenharmony_ci#endif
748c2ecf20Sopenharmony_ci	VCPU_STAT("halt_successful_poll", halt_successful_poll),
758c2ecf20Sopenharmony_ci	VCPU_STAT("halt_attempted_poll", halt_attempted_poll),
768c2ecf20Sopenharmony_ci	VCPU_STAT("halt_poll_invalid", halt_poll_invalid),
778c2ecf20Sopenharmony_ci	VCPU_STAT("halt_wakeup", halt_wakeup),
788c2ecf20Sopenharmony_ci	VCPU_STAT("halt_poll_success_ns", halt_poll_success_ns),
798c2ecf20Sopenharmony_ci	VCPU_STAT("halt_poll_fail_ns", halt_poll_fail_ns),
808c2ecf20Sopenharmony_ci	{NULL}
818c2ecf20Sopenharmony_ci};
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cibool kvm_trace_guest_mode_change;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ciint kvm_guest_mode_change_trace_reg(void)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	kvm_trace_guest_mode_change = true;
888c2ecf20Sopenharmony_ci	return 0;
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_civoid kvm_guest_mode_change_trace_unreg(void)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	kvm_trace_guest_mode_change = false;
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci/*
978c2ecf20Sopenharmony_ci * XXXKYMA: We are simulatoring a processor that has the WII bit set in
988c2ecf20Sopenharmony_ci * Config7, so we are "runnable" if interrupts are pending
998c2ecf20Sopenharmony_ci */
1008c2ecf20Sopenharmony_ciint kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	return !!(vcpu->arch.pending_exceptions);
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cibool kvm_arch_vcpu_in_kernel(struct kvm_vcpu *vcpu)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	return false;
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ciint kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	return 1;
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ciint kvm_arch_hardware_enable(void)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	return kvm_mips_callbacks->hardware_enable();
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_civoid kvm_arch_hardware_disable(void)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	kvm_mips_callbacks->hardware_disable();
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ciint kvm_arch_hardware_setup(void *opaque)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	return 0;
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ciint kvm_arch_check_processor_compat(void *opaque)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	return 0;
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ciextern void kvm_init_loongson_ipi(struct kvm *kvm);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ciint kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	switch (type) {
1408c2ecf20Sopenharmony_ci	case KVM_VM_MIPS_AUTO:
1418c2ecf20Sopenharmony_ci		break;
1428c2ecf20Sopenharmony_ci#ifdef CONFIG_KVM_MIPS_VZ
1438c2ecf20Sopenharmony_ci	case KVM_VM_MIPS_VZ:
1448c2ecf20Sopenharmony_ci#else
1458c2ecf20Sopenharmony_ci	case KVM_VM_MIPS_TE:
1468c2ecf20Sopenharmony_ci#endif
1478c2ecf20Sopenharmony_ci		break;
1488c2ecf20Sopenharmony_ci	default:
1498c2ecf20Sopenharmony_ci		/* Unsupported KVM type */
1508c2ecf20Sopenharmony_ci		return -EINVAL;
1518c2ecf20Sopenharmony_ci	};
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	/* Allocate page table to map GPA -> RPA */
1548c2ecf20Sopenharmony_ci	kvm->arch.gpa_mm.pgd = kvm_pgd_alloc();
1558c2ecf20Sopenharmony_ci	if (!kvm->arch.gpa_mm.pgd)
1568c2ecf20Sopenharmony_ci		return -ENOMEM;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_LOONGSON64
1598c2ecf20Sopenharmony_ci	kvm_init_loongson_ipi(kvm);
1608c2ecf20Sopenharmony_ci#endif
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	return 0;
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_civoid kvm_mips_free_vcpus(struct kvm *kvm)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	unsigned int i;
1688c2ecf20Sopenharmony_ci	struct kvm_vcpu *vcpu;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	kvm_for_each_vcpu(i, vcpu, kvm) {
1718c2ecf20Sopenharmony_ci		kvm_vcpu_destroy(vcpu);
1728c2ecf20Sopenharmony_ci	}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	mutex_lock(&kvm->lock);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	for (i = 0; i < atomic_read(&kvm->online_vcpus); i++)
1778c2ecf20Sopenharmony_ci		kvm->vcpus[i] = NULL;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	atomic_set(&kvm->online_vcpus, 0);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	mutex_unlock(&kvm->lock);
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_cistatic void kvm_mips_free_gpa_pt(struct kvm *kvm)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	/* It should always be safe to remove after flushing the whole range */
1878c2ecf20Sopenharmony_ci	WARN_ON(!kvm_mips_flush_gpa_pt(kvm, 0, ~0));
1888c2ecf20Sopenharmony_ci	pgd_free(NULL, kvm->arch.gpa_mm.pgd);
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_civoid kvm_arch_destroy_vm(struct kvm *kvm)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	kvm_mips_free_vcpus(kvm);
1948c2ecf20Sopenharmony_ci	kvm_mips_free_gpa_pt(kvm);
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cilong kvm_arch_dev_ioctl(struct file *filp, unsigned int ioctl,
1988c2ecf20Sopenharmony_ci			unsigned long arg)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	return -ENOIOCTLCMD;
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_civoid kvm_arch_flush_shadow_all(struct kvm *kvm)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	/* Flush whole GPA */
2068c2ecf20Sopenharmony_ci	kvm_mips_flush_gpa_pt(kvm, 0, ~0);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	/* Let implementation do the rest */
2098c2ecf20Sopenharmony_ci	kvm_mips_callbacks->flush_shadow_all(kvm);
2108c2ecf20Sopenharmony_ci}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_civoid kvm_arch_flush_shadow_memslot(struct kvm *kvm,
2138c2ecf20Sopenharmony_ci				   struct kvm_memory_slot *slot)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	/*
2168c2ecf20Sopenharmony_ci	 * The slot has been made invalid (ready for moving or deletion), so we
2178c2ecf20Sopenharmony_ci	 * need to ensure that it can no longer be accessed by any guest VCPUs.
2188c2ecf20Sopenharmony_ci	 */
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	spin_lock(&kvm->mmu_lock);
2218c2ecf20Sopenharmony_ci	/* Flush slot from GPA */
2228c2ecf20Sopenharmony_ci	kvm_mips_flush_gpa_pt(kvm, slot->base_gfn,
2238c2ecf20Sopenharmony_ci			      slot->base_gfn + slot->npages - 1);
2248c2ecf20Sopenharmony_ci	/* Let implementation do the rest */
2258c2ecf20Sopenharmony_ci	kvm_mips_callbacks->flush_shadow_memslot(kvm, slot);
2268c2ecf20Sopenharmony_ci	spin_unlock(&kvm->mmu_lock);
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ciint kvm_arch_prepare_memory_region(struct kvm *kvm,
2308c2ecf20Sopenharmony_ci				   struct kvm_memory_slot *memslot,
2318c2ecf20Sopenharmony_ci				   const struct kvm_userspace_memory_region *mem,
2328c2ecf20Sopenharmony_ci				   enum kvm_mr_change change)
2338c2ecf20Sopenharmony_ci{
2348c2ecf20Sopenharmony_ci	return 0;
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_civoid kvm_arch_commit_memory_region(struct kvm *kvm,
2388c2ecf20Sopenharmony_ci				   const struct kvm_userspace_memory_region *mem,
2398c2ecf20Sopenharmony_ci				   struct kvm_memory_slot *old,
2408c2ecf20Sopenharmony_ci				   const struct kvm_memory_slot *new,
2418c2ecf20Sopenharmony_ci				   enum kvm_mr_change change)
2428c2ecf20Sopenharmony_ci{
2438c2ecf20Sopenharmony_ci	int needs_flush;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	kvm_debug("%s: kvm: %p slot: %d, GPA: %llx, size: %llx, QVA: %llx\n",
2468c2ecf20Sopenharmony_ci		  __func__, kvm, mem->slot, mem->guest_phys_addr,
2478c2ecf20Sopenharmony_ci		  mem->memory_size, mem->userspace_addr);
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	/*
2508c2ecf20Sopenharmony_ci	 * If dirty page logging is enabled, write protect all pages in the slot
2518c2ecf20Sopenharmony_ci	 * ready for dirty logging.
2528c2ecf20Sopenharmony_ci	 *
2538c2ecf20Sopenharmony_ci	 * There is no need to do this in any of the following cases:
2548c2ecf20Sopenharmony_ci	 * CREATE:	No dirty mappings will already exist.
2558c2ecf20Sopenharmony_ci	 * MOVE/DELETE:	The old mappings will already have been cleaned up by
2568c2ecf20Sopenharmony_ci	 *		kvm_arch_flush_shadow_memslot()
2578c2ecf20Sopenharmony_ci	 */
2588c2ecf20Sopenharmony_ci	if (change == KVM_MR_FLAGS_ONLY &&
2598c2ecf20Sopenharmony_ci	    (!(old->flags & KVM_MEM_LOG_DIRTY_PAGES) &&
2608c2ecf20Sopenharmony_ci	     new->flags & KVM_MEM_LOG_DIRTY_PAGES)) {
2618c2ecf20Sopenharmony_ci		spin_lock(&kvm->mmu_lock);
2628c2ecf20Sopenharmony_ci		/* Write protect GPA page table entries */
2638c2ecf20Sopenharmony_ci		needs_flush = kvm_mips_mkclean_gpa_pt(kvm, new->base_gfn,
2648c2ecf20Sopenharmony_ci					new->base_gfn + new->npages - 1);
2658c2ecf20Sopenharmony_ci		/* Let implementation do the rest */
2668c2ecf20Sopenharmony_ci		if (needs_flush)
2678c2ecf20Sopenharmony_ci			kvm_mips_callbacks->flush_shadow_memslot(kvm, new);
2688c2ecf20Sopenharmony_ci		spin_unlock(&kvm->mmu_lock);
2698c2ecf20Sopenharmony_ci	}
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_cistatic inline void dump_handler(const char *symbol, void *start, void *end)
2738c2ecf20Sopenharmony_ci{
2748c2ecf20Sopenharmony_ci	u32 *p;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	pr_debug("LEAF(%s)\n", symbol);
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	pr_debug("\t.set push\n");
2798c2ecf20Sopenharmony_ci	pr_debug("\t.set noreorder\n");
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	for (p = start; p < (u32 *)end; ++p)
2828c2ecf20Sopenharmony_ci		pr_debug("\t.word\t0x%08x\t\t# %p\n", *p, p);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	pr_debug("\t.set\tpop\n");
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	pr_debug("\tEND(%s)\n", symbol);
2878c2ecf20Sopenharmony_ci}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci/* low level hrtimer wake routine */
2908c2ecf20Sopenharmony_cistatic enum hrtimer_restart kvm_mips_comparecount_wakeup(struct hrtimer *timer)
2918c2ecf20Sopenharmony_ci{
2928c2ecf20Sopenharmony_ci	struct kvm_vcpu *vcpu;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	vcpu = container_of(timer, struct kvm_vcpu, arch.comparecount_timer);
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	kvm_mips_callbacks->queue_timer_int(vcpu);
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	vcpu->arch.wait = 0;
2998c2ecf20Sopenharmony_ci	rcuwait_wake_up(&vcpu->wait);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	return kvm_mips_count_timeout(vcpu);
3028c2ecf20Sopenharmony_ci}
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ciint kvm_arch_vcpu_precreate(struct kvm *kvm, unsigned int id)
3058c2ecf20Sopenharmony_ci{
3068c2ecf20Sopenharmony_ci	return 0;
3078c2ecf20Sopenharmony_ci}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ciint kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
3108c2ecf20Sopenharmony_ci{
3118c2ecf20Sopenharmony_ci	int err, size;
3128c2ecf20Sopenharmony_ci	void *gebase, *p, *handler, *refill_start, *refill_end;
3138c2ecf20Sopenharmony_ci	int i;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	kvm_debug("kvm @ %p: create cpu %d at %p\n",
3168c2ecf20Sopenharmony_ci		  vcpu->kvm, vcpu->vcpu_id, vcpu);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	err = kvm_mips_callbacks->vcpu_init(vcpu);
3198c2ecf20Sopenharmony_ci	if (err)
3208c2ecf20Sopenharmony_ci		return err;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	hrtimer_init(&vcpu->arch.comparecount_timer, CLOCK_MONOTONIC,
3238c2ecf20Sopenharmony_ci		     HRTIMER_MODE_REL);
3248c2ecf20Sopenharmony_ci	vcpu->arch.comparecount_timer.function = kvm_mips_comparecount_wakeup;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	/*
3278c2ecf20Sopenharmony_ci	 * Allocate space for host mode exception handlers that handle
3288c2ecf20Sopenharmony_ci	 * guest mode exits
3298c2ecf20Sopenharmony_ci	 */
3308c2ecf20Sopenharmony_ci	if (cpu_has_veic || cpu_has_vint)
3318c2ecf20Sopenharmony_ci		size = 0x200 + VECTORSPACING * 64;
3328c2ecf20Sopenharmony_ci	else
3338c2ecf20Sopenharmony_ci		size = 0x4000;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	gebase = kzalloc(ALIGN(size, PAGE_SIZE), GFP_KERNEL);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	if (!gebase) {
3388c2ecf20Sopenharmony_ci		err = -ENOMEM;
3398c2ecf20Sopenharmony_ci		goto out_uninit_vcpu;
3408c2ecf20Sopenharmony_ci	}
3418c2ecf20Sopenharmony_ci	kvm_debug("Allocated %d bytes for KVM Exception Handlers @ %p\n",
3428c2ecf20Sopenharmony_ci		  ALIGN(size, PAGE_SIZE), gebase);
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	/*
3458c2ecf20Sopenharmony_ci	 * Check new ebase actually fits in CP0_EBase. The lack of a write gate
3468c2ecf20Sopenharmony_ci	 * limits us to the low 512MB of physical address space. If the memory
3478c2ecf20Sopenharmony_ci	 * we allocate is out of range, just give up now.
3488c2ecf20Sopenharmony_ci	 */
3498c2ecf20Sopenharmony_ci	if (!cpu_has_ebase_wg && virt_to_phys(gebase) >= 0x20000000) {
3508c2ecf20Sopenharmony_ci		kvm_err("CP0_EBase.WG required for guest exception base %pK\n",
3518c2ecf20Sopenharmony_ci			gebase);
3528c2ecf20Sopenharmony_ci		err = -ENOMEM;
3538c2ecf20Sopenharmony_ci		goto out_free_gebase;
3548c2ecf20Sopenharmony_ci	}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	/* Save new ebase */
3578c2ecf20Sopenharmony_ci	vcpu->arch.guest_ebase = gebase;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	/* Build guest exception vectors dynamically in unmapped memory */
3608c2ecf20Sopenharmony_ci	handler = gebase + 0x2000;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	/* TLB refill (or XTLB refill on 64-bit VZ where KX=1) */
3638c2ecf20Sopenharmony_ci	refill_start = gebase;
3648c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_KVM_MIPS_VZ) && IS_ENABLED(CONFIG_64BIT))
3658c2ecf20Sopenharmony_ci		refill_start += 0x080;
3668c2ecf20Sopenharmony_ci	refill_end = kvm_mips_build_tlb_refill_exception(refill_start, handler);
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	/* General Exception Entry point */
3698c2ecf20Sopenharmony_ci	kvm_mips_build_exception(gebase + 0x180, handler);
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	/* For vectored interrupts poke the exception code @ all offsets 0-7 */
3728c2ecf20Sopenharmony_ci	for (i = 0; i < 8; i++) {
3738c2ecf20Sopenharmony_ci		kvm_debug("L1 Vectored handler @ %p\n",
3748c2ecf20Sopenharmony_ci			  gebase + 0x200 + (i * VECTORSPACING));
3758c2ecf20Sopenharmony_ci		kvm_mips_build_exception(gebase + 0x200 + i * VECTORSPACING,
3768c2ecf20Sopenharmony_ci					 handler);
3778c2ecf20Sopenharmony_ci	}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	/* General exit handler */
3808c2ecf20Sopenharmony_ci	p = handler;
3818c2ecf20Sopenharmony_ci	p = kvm_mips_build_exit(p);
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	/* Guest entry routine */
3848c2ecf20Sopenharmony_ci	vcpu->arch.vcpu_run = p;
3858c2ecf20Sopenharmony_ci	p = kvm_mips_build_vcpu_run(p);
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	/* Dump the generated code */
3888c2ecf20Sopenharmony_ci	pr_debug("#include <asm/asm.h>\n");
3898c2ecf20Sopenharmony_ci	pr_debug("#include <asm/regdef.h>\n");
3908c2ecf20Sopenharmony_ci	pr_debug("\n");
3918c2ecf20Sopenharmony_ci	dump_handler("kvm_vcpu_run", vcpu->arch.vcpu_run, p);
3928c2ecf20Sopenharmony_ci	dump_handler("kvm_tlb_refill", refill_start, refill_end);
3938c2ecf20Sopenharmony_ci	dump_handler("kvm_gen_exc", gebase + 0x180, gebase + 0x200);
3948c2ecf20Sopenharmony_ci	dump_handler("kvm_exit", gebase + 0x2000, vcpu->arch.vcpu_run);
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	/* Invalidate the icache for these ranges */
3978c2ecf20Sopenharmony_ci	flush_icache_range((unsigned long)gebase,
3988c2ecf20Sopenharmony_ci			   (unsigned long)gebase + ALIGN(size, PAGE_SIZE));
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	/*
4018c2ecf20Sopenharmony_ci	 * Allocate comm page for guest kernel, a TLB will be reserved for
4028c2ecf20Sopenharmony_ci	 * mapping GVA @ 0xFFFF8000 to this page
4038c2ecf20Sopenharmony_ci	 */
4048c2ecf20Sopenharmony_ci	vcpu->arch.kseg0_commpage = kzalloc(PAGE_SIZE << 1, GFP_KERNEL);
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	if (!vcpu->arch.kseg0_commpage) {
4078c2ecf20Sopenharmony_ci		err = -ENOMEM;
4088c2ecf20Sopenharmony_ci		goto out_free_gebase;
4098c2ecf20Sopenharmony_ci	}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	kvm_debug("Allocated COMM page @ %p\n", vcpu->arch.kseg0_commpage);
4128c2ecf20Sopenharmony_ci	kvm_mips_commpage_init(vcpu);
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	/* Init */
4158c2ecf20Sopenharmony_ci	vcpu->arch.last_sched_cpu = -1;
4168c2ecf20Sopenharmony_ci	vcpu->arch.last_exec_cpu = -1;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	/* Initial guest state */
4198c2ecf20Sopenharmony_ci	err = kvm_mips_callbacks->vcpu_setup(vcpu);
4208c2ecf20Sopenharmony_ci	if (err)
4218c2ecf20Sopenharmony_ci		goto out_free_commpage;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	return 0;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ciout_free_commpage:
4268c2ecf20Sopenharmony_ci	kfree(vcpu->arch.kseg0_commpage);
4278c2ecf20Sopenharmony_ciout_free_gebase:
4288c2ecf20Sopenharmony_ci	kfree(gebase);
4298c2ecf20Sopenharmony_ciout_uninit_vcpu:
4308c2ecf20Sopenharmony_ci	kvm_mips_callbacks->vcpu_uninit(vcpu);
4318c2ecf20Sopenharmony_ci	return err;
4328c2ecf20Sopenharmony_ci}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_civoid kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
4358c2ecf20Sopenharmony_ci{
4368c2ecf20Sopenharmony_ci	hrtimer_cancel(&vcpu->arch.comparecount_timer);
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	kvm_mips_dump_stats(vcpu);
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	kvm_mmu_free_memory_caches(vcpu);
4418c2ecf20Sopenharmony_ci	kfree(vcpu->arch.guest_ebase);
4428c2ecf20Sopenharmony_ci	kfree(vcpu->arch.kseg0_commpage);
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	kvm_mips_callbacks->vcpu_uninit(vcpu);
4458c2ecf20Sopenharmony_ci}
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ciint kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu,
4488c2ecf20Sopenharmony_ci					struct kvm_guest_debug *dbg)
4498c2ecf20Sopenharmony_ci{
4508c2ecf20Sopenharmony_ci	return -ENOIOCTLCMD;
4518c2ecf20Sopenharmony_ci}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ciint kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
4548c2ecf20Sopenharmony_ci{
4558c2ecf20Sopenharmony_ci	int r = -EINTR;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	vcpu_load(vcpu);
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	kvm_sigset_activate(vcpu);
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	if (vcpu->mmio_needed) {
4628c2ecf20Sopenharmony_ci		if (!vcpu->mmio_is_write)
4638c2ecf20Sopenharmony_ci			kvm_mips_complete_mmio_load(vcpu);
4648c2ecf20Sopenharmony_ci		vcpu->mmio_needed = 0;
4658c2ecf20Sopenharmony_ci	}
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	if (vcpu->run->immediate_exit)
4688c2ecf20Sopenharmony_ci		goto out;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	lose_fpu(1);
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	local_irq_disable();
4738c2ecf20Sopenharmony_ci	guest_enter_irqoff();
4748c2ecf20Sopenharmony_ci	trace_kvm_enter(vcpu);
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	/*
4778c2ecf20Sopenharmony_ci	 * Make sure the read of VCPU requests in vcpu_run() callback is not
4788c2ecf20Sopenharmony_ci	 * reordered ahead of the write to vcpu->mode, or we could miss a TLB
4798c2ecf20Sopenharmony_ci	 * flush request while the requester sees the VCPU as outside of guest
4808c2ecf20Sopenharmony_ci	 * mode and not needing an IPI.
4818c2ecf20Sopenharmony_ci	 */
4828c2ecf20Sopenharmony_ci	smp_store_mb(vcpu->mode, IN_GUEST_MODE);
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	r = kvm_mips_callbacks->vcpu_run(vcpu);
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	trace_kvm_out(vcpu);
4878c2ecf20Sopenharmony_ci	guest_exit_irqoff();
4888c2ecf20Sopenharmony_ci	local_irq_enable();
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ciout:
4918c2ecf20Sopenharmony_ci	kvm_sigset_deactivate(vcpu);
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	vcpu_put(vcpu);
4948c2ecf20Sopenharmony_ci	return r;
4958c2ecf20Sopenharmony_ci}
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ciint kvm_vcpu_ioctl_interrupt(struct kvm_vcpu *vcpu,
4988c2ecf20Sopenharmony_ci			     struct kvm_mips_interrupt *irq)
4998c2ecf20Sopenharmony_ci{
5008c2ecf20Sopenharmony_ci	int intr = (int)irq->irq;
5018c2ecf20Sopenharmony_ci	struct kvm_vcpu *dvcpu = NULL;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	if (intr == kvm_priority_to_irq[MIPS_EXC_INT_IPI_1] ||
5048c2ecf20Sopenharmony_ci	    intr == kvm_priority_to_irq[MIPS_EXC_INT_IPI_2] ||
5058c2ecf20Sopenharmony_ci	    intr == (-kvm_priority_to_irq[MIPS_EXC_INT_IPI_1]) ||
5068c2ecf20Sopenharmony_ci	    intr == (-kvm_priority_to_irq[MIPS_EXC_INT_IPI_2]))
5078c2ecf20Sopenharmony_ci		kvm_debug("%s: CPU: %d, INTR: %d\n", __func__, irq->cpu,
5088c2ecf20Sopenharmony_ci			  (int)intr);
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	if (irq->cpu == -1)
5118c2ecf20Sopenharmony_ci		dvcpu = vcpu;
5128c2ecf20Sopenharmony_ci	else
5138c2ecf20Sopenharmony_ci		dvcpu = vcpu->kvm->vcpus[irq->cpu];
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	if (intr == 2 || intr == 3 || intr == 4 || intr == 6) {
5168c2ecf20Sopenharmony_ci		kvm_mips_callbacks->queue_io_int(dvcpu, irq);
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	} else if (intr == -2 || intr == -3 || intr == -4 || intr == -6) {
5198c2ecf20Sopenharmony_ci		kvm_mips_callbacks->dequeue_io_int(dvcpu, irq);
5208c2ecf20Sopenharmony_ci	} else {
5218c2ecf20Sopenharmony_ci		kvm_err("%s: invalid interrupt ioctl (%d:%d)\n", __func__,
5228c2ecf20Sopenharmony_ci			irq->cpu, irq->irq);
5238c2ecf20Sopenharmony_ci		return -EINVAL;
5248c2ecf20Sopenharmony_ci	}
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	dvcpu->arch.wait = 0;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	rcuwait_wake_up(&dvcpu->wait);
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	return 0;
5318c2ecf20Sopenharmony_ci}
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ciint kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu,
5348c2ecf20Sopenharmony_ci				    struct kvm_mp_state *mp_state)
5358c2ecf20Sopenharmony_ci{
5368c2ecf20Sopenharmony_ci	return -ENOIOCTLCMD;
5378c2ecf20Sopenharmony_ci}
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ciint kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu,
5408c2ecf20Sopenharmony_ci				    struct kvm_mp_state *mp_state)
5418c2ecf20Sopenharmony_ci{
5428c2ecf20Sopenharmony_ci	return -ENOIOCTLCMD;
5438c2ecf20Sopenharmony_ci}
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_cistatic u64 kvm_mips_get_one_regs[] = {
5468c2ecf20Sopenharmony_ci	KVM_REG_MIPS_R0,
5478c2ecf20Sopenharmony_ci	KVM_REG_MIPS_R1,
5488c2ecf20Sopenharmony_ci	KVM_REG_MIPS_R2,
5498c2ecf20Sopenharmony_ci	KVM_REG_MIPS_R3,
5508c2ecf20Sopenharmony_ci	KVM_REG_MIPS_R4,
5518c2ecf20Sopenharmony_ci	KVM_REG_MIPS_R5,
5528c2ecf20Sopenharmony_ci	KVM_REG_MIPS_R6,
5538c2ecf20Sopenharmony_ci	KVM_REG_MIPS_R7,
5548c2ecf20Sopenharmony_ci	KVM_REG_MIPS_R8,
5558c2ecf20Sopenharmony_ci	KVM_REG_MIPS_R9,
5568c2ecf20Sopenharmony_ci	KVM_REG_MIPS_R10,
5578c2ecf20Sopenharmony_ci	KVM_REG_MIPS_R11,
5588c2ecf20Sopenharmony_ci	KVM_REG_MIPS_R12,
5598c2ecf20Sopenharmony_ci	KVM_REG_MIPS_R13,
5608c2ecf20Sopenharmony_ci	KVM_REG_MIPS_R14,
5618c2ecf20Sopenharmony_ci	KVM_REG_MIPS_R15,
5628c2ecf20Sopenharmony_ci	KVM_REG_MIPS_R16,
5638c2ecf20Sopenharmony_ci	KVM_REG_MIPS_R17,
5648c2ecf20Sopenharmony_ci	KVM_REG_MIPS_R18,
5658c2ecf20Sopenharmony_ci	KVM_REG_MIPS_R19,
5668c2ecf20Sopenharmony_ci	KVM_REG_MIPS_R20,
5678c2ecf20Sopenharmony_ci	KVM_REG_MIPS_R21,
5688c2ecf20Sopenharmony_ci	KVM_REG_MIPS_R22,
5698c2ecf20Sopenharmony_ci	KVM_REG_MIPS_R23,
5708c2ecf20Sopenharmony_ci	KVM_REG_MIPS_R24,
5718c2ecf20Sopenharmony_ci	KVM_REG_MIPS_R25,
5728c2ecf20Sopenharmony_ci	KVM_REG_MIPS_R26,
5738c2ecf20Sopenharmony_ci	KVM_REG_MIPS_R27,
5748c2ecf20Sopenharmony_ci	KVM_REG_MIPS_R28,
5758c2ecf20Sopenharmony_ci	KVM_REG_MIPS_R29,
5768c2ecf20Sopenharmony_ci	KVM_REG_MIPS_R30,
5778c2ecf20Sopenharmony_ci	KVM_REG_MIPS_R31,
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci#ifndef CONFIG_CPU_MIPSR6
5808c2ecf20Sopenharmony_ci	KVM_REG_MIPS_HI,
5818c2ecf20Sopenharmony_ci	KVM_REG_MIPS_LO,
5828c2ecf20Sopenharmony_ci#endif
5838c2ecf20Sopenharmony_ci	KVM_REG_MIPS_PC,
5848c2ecf20Sopenharmony_ci};
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_cistatic u64 kvm_mips_get_one_regs_fpu[] = {
5878c2ecf20Sopenharmony_ci	KVM_REG_MIPS_FCR_IR,
5888c2ecf20Sopenharmony_ci	KVM_REG_MIPS_FCR_CSR,
5898c2ecf20Sopenharmony_ci};
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_cistatic u64 kvm_mips_get_one_regs_msa[] = {
5928c2ecf20Sopenharmony_ci	KVM_REG_MIPS_MSA_IR,
5938c2ecf20Sopenharmony_ci	KVM_REG_MIPS_MSA_CSR,
5948c2ecf20Sopenharmony_ci};
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_cistatic unsigned long kvm_mips_num_regs(struct kvm_vcpu *vcpu)
5978c2ecf20Sopenharmony_ci{
5988c2ecf20Sopenharmony_ci	unsigned long ret;
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	ret = ARRAY_SIZE(kvm_mips_get_one_regs);
6018c2ecf20Sopenharmony_ci	if (kvm_mips_guest_can_have_fpu(&vcpu->arch)) {
6028c2ecf20Sopenharmony_ci		ret += ARRAY_SIZE(kvm_mips_get_one_regs_fpu) + 48;
6038c2ecf20Sopenharmony_ci		/* odd doubles */
6048c2ecf20Sopenharmony_ci		if (boot_cpu_data.fpu_id & MIPS_FPIR_F64)
6058c2ecf20Sopenharmony_ci			ret += 16;
6068c2ecf20Sopenharmony_ci	}
6078c2ecf20Sopenharmony_ci	if (kvm_mips_guest_can_have_msa(&vcpu->arch))
6088c2ecf20Sopenharmony_ci		ret += ARRAY_SIZE(kvm_mips_get_one_regs_msa) + 32;
6098c2ecf20Sopenharmony_ci	ret += kvm_mips_callbacks->num_regs(vcpu);
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	return ret;
6128c2ecf20Sopenharmony_ci}
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_cistatic int kvm_mips_copy_reg_indices(struct kvm_vcpu *vcpu, u64 __user *indices)
6158c2ecf20Sopenharmony_ci{
6168c2ecf20Sopenharmony_ci	u64 index;
6178c2ecf20Sopenharmony_ci	unsigned int i;
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	if (copy_to_user(indices, kvm_mips_get_one_regs,
6208c2ecf20Sopenharmony_ci			 sizeof(kvm_mips_get_one_regs)))
6218c2ecf20Sopenharmony_ci		return -EFAULT;
6228c2ecf20Sopenharmony_ci	indices += ARRAY_SIZE(kvm_mips_get_one_regs);
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	if (kvm_mips_guest_can_have_fpu(&vcpu->arch)) {
6258c2ecf20Sopenharmony_ci		if (copy_to_user(indices, kvm_mips_get_one_regs_fpu,
6268c2ecf20Sopenharmony_ci				 sizeof(kvm_mips_get_one_regs_fpu)))
6278c2ecf20Sopenharmony_ci			return -EFAULT;
6288c2ecf20Sopenharmony_ci		indices += ARRAY_SIZE(kvm_mips_get_one_regs_fpu);
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci		for (i = 0; i < 32; ++i) {
6318c2ecf20Sopenharmony_ci			index = KVM_REG_MIPS_FPR_32(i);
6328c2ecf20Sopenharmony_ci			if (copy_to_user(indices, &index, sizeof(index)))
6338c2ecf20Sopenharmony_ci				return -EFAULT;
6348c2ecf20Sopenharmony_ci			++indices;
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci			/* skip odd doubles if no F64 */
6378c2ecf20Sopenharmony_ci			if (i & 1 && !(boot_cpu_data.fpu_id & MIPS_FPIR_F64))
6388c2ecf20Sopenharmony_ci				continue;
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci			index = KVM_REG_MIPS_FPR_64(i);
6418c2ecf20Sopenharmony_ci			if (copy_to_user(indices, &index, sizeof(index)))
6428c2ecf20Sopenharmony_ci				return -EFAULT;
6438c2ecf20Sopenharmony_ci			++indices;
6448c2ecf20Sopenharmony_ci		}
6458c2ecf20Sopenharmony_ci	}
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	if (kvm_mips_guest_can_have_msa(&vcpu->arch)) {
6488c2ecf20Sopenharmony_ci		if (copy_to_user(indices, kvm_mips_get_one_regs_msa,
6498c2ecf20Sopenharmony_ci				 sizeof(kvm_mips_get_one_regs_msa)))
6508c2ecf20Sopenharmony_ci			return -EFAULT;
6518c2ecf20Sopenharmony_ci		indices += ARRAY_SIZE(kvm_mips_get_one_regs_msa);
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci		for (i = 0; i < 32; ++i) {
6548c2ecf20Sopenharmony_ci			index = KVM_REG_MIPS_VEC_128(i);
6558c2ecf20Sopenharmony_ci			if (copy_to_user(indices, &index, sizeof(index)))
6568c2ecf20Sopenharmony_ci				return -EFAULT;
6578c2ecf20Sopenharmony_ci			++indices;
6588c2ecf20Sopenharmony_ci		}
6598c2ecf20Sopenharmony_ci	}
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	return kvm_mips_callbacks->copy_reg_indices(vcpu, indices);
6628c2ecf20Sopenharmony_ci}
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_cistatic int kvm_mips_get_reg(struct kvm_vcpu *vcpu,
6658c2ecf20Sopenharmony_ci			    const struct kvm_one_reg *reg)
6668c2ecf20Sopenharmony_ci{
6678c2ecf20Sopenharmony_ci	struct mips_coproc *cop0 = vcpu->arch.cop0;
6688c2ecf20Sopenharmony_ci	struct mips_fpu_struct *fpu = &vcpu->arch.fpu;
6698c2ecf20Sopenharmony_ci	int ret;
6708c2ecf20Sopenharmony_ci	s64 v;
6718c2ecf20Sopenharmony_ci	s64 vs[2];
6728c2ecf20Sopenharmony_ci	unsigned int idx;
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	switch (reg->id) {
6758c2ecf20Sopenharmony_ci	/* General purpose registers */
6768c2ecf20Sopenharmony_ci	case KVM_REG_MIPS_R0 ... KVM_REG_MIPS_R31:
6778c2ecf20Sopenharmony_ci		v = (long)vcpu->arch.gprs[reg->id - KVM_REG_MIPS_R0];
6788c2ecf20Sopenharmony_ci		break;
6798c2ecf20Sopenharmony_ci#ifndef CONFIG_CPU_MIPSR6
6808c2ecf20Sopenharmony_ci	case KVM_REG_MIPS_HI:
6818c2ecf20Sopenharmony_ci		v = (long)vcpu->arch.hi;
6828c2ecf20Sopenharmony_ci		break;
6838c2ecf20Sopenharmony_ci	case KVM_REG_MIPS_LO:
6848c2ecf20Sopenharmony_ci		v = (long)vcpu->arch.lo;
6858c2ecf20Sopenharmony_ci		break;
6868c2ecf20Sopenharmony_ci#endif
6878c2ecf20Sopenharmony_ci	case KVM_REG_MIPS_PC:
6888c2ecf20Sopenharmony_ci		v = (long)vcpu->arch.pc;
6898c2ecf20Sopenharmony_ci		break;
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	/* Floating point registers */
6928c2ecf20Sopenharmony_ci	case KVM_REG_MIPS_FPR_32(0) ... KVM_REG_MIPS_FPR_32(31):
6938c2ecf20Sopenharmony_ci		if (!kvm_mips_guest_has_fpu(&vcpu->arch))
6948c2ecf20Sopenharmony_ci			return -EINVAL;
6958c2ecf20Sopenharmony_ci		idx = reg->id - KVM_REG_MIPS_FPR_32(0);
6968c2ecf20Sopenharmony_ci		/* Odd singles in top of even double when FR=0 */
6978c2ecf20Sopenharmony_ci		if (kvm_read_c0_guest_status(cop0) & ST0_FR)
6988c2ecf20Sopenharmony_ci			v = get_fpr32(&fpu->fpr[idx], 0);
6998c2ecf20Sopenharmony_ci		else
7008c2ecf20Sopenharmony_ci			v = get_fpr32(&fpu->fpr[idx & ~1], idx & 1);
7018c2ecf20Sopenharmony_ci		break;
7028c2ecf20Sopenharmony_ci	case KVM_REG_MIPS_FPR_64(0) ... KVM_REG_MIPS_FPR_64(31):
7038c2ecf20Sopenharmony_ci		if (!kvm_mips_guest_has_fpu(&vcpu->arch))
7048c2ecf20Sopenharmony_ci			return -EINVAL;
7058c2ecf20Sopenharmony_ci		idx = reg->id - KVM_REG_MIPS_FPR_64(0);
7068c2ecf20Sopenharmony_ci		/* Can't access odd doubles in FR=0 mode */
7078c2ecf20Sopenharmony_ci		if (idx & 1 && !(kvm_read_c0_guest_status(cop0) & ST0_FR))
7088c2ecf20Sopenharmony_ci			return -EINVAL;
7098c2ecf20Sopenharmony_ci		v = get_fpr64(&fpu->fpr[idx], 0);
7108c2ecf20Sopenharmony_ci		break;
7118c2ecf20Sopenharmony_ci	case KVM_REG_MIPS_FCR_IR:
7128c2ecf20Sopenharmony_ci		if (!kvm_mips_guest_has_fpu(&vcpu->arch))
7138c2ecf20Sopenharmony_ci			return -EINVAL;
7148c2ecf20Sopenharmony_ci		v = boot_cpu_data.fpu_id;
7158c2ecf20Sopenharmony_ci		break;
7168c2ecf20Sopenharmony_ci	case KVM_REG_MIPS_FCR_CSR:
7178c2ecf20Sopenharmony_ci		if (!kvm_mips_guest_has_fpu(&vcpu->arch))
7188c2ecf20Sopenharmony_ci			return -EINVAL;
7198c2ecf20Sopenharmony_ci		v = fpu->fcr31;
7208c2ecf20Sopenharmony_ci		break;
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	/* MIPS SIMD Architecture (MSA) registers */
7238c2ecf20Sopenharmony_ci	case KVM_REG_MIPS_VEC_128(0) ... KVM_REG_MIPS_VEC_128(31):
7248c2ecf20Sopenharmony_ci		if (!kvm_mips_guest_has_msa(&vcpu->arch))
7258c2ecf20Sopenharmony_ci			return -EINVAL;
7268c2ecf20Sopenharmony_ci		/* Can't access MSA registers in FR=0 mode */
7278c2ecf20Sopenharmony_ci		if (!(kvm_read_c0_guest_status(cop0) & ST0_FR))
7288c2ecf20Sopenharmony_ci			return -EINVAL;
7298c2ecf20Sopenharmony_ci		idx = reg->id - KVM_REG_MIPS_VEC_128(0);
7308c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_LITTLE_ENDIAN
7318c2ecf20Sopenharmony_ci		/* least significant byte first */
7328c2ecf20Sopenharmony_ci		vs[0] = get_fpr64(&fpu->fpr[idx], 0);
7338c2ecf20Sopenharmony_ci		vs[1] = get_fpr64(&fpu->fpr[idx], 1);
7348c2ecf20Sopenharmony_ci#else
7358c2ecf20Sopenharmony_ci		/* most significant byte first */
7368c2ecf20Sopenharmony_ci		vs[0] = get_fpr64(&fpu->fpr[idx], 1);
7378c2ecf20Sopenharmony_ci		vs[1] = get_fpr64(&fpu->fpr[idx], 0);
7388c2ecf20Sopenharmony_ci#endif
7398c2ecf20Sopenharmony_ci		break;
7408c2ecf20Sopenharmony_ci	case KVM_REG_MIPS_MSA_IR:
7418c2ecf20Sopenharmony_ci		if (!kvm_mips_guest_has_msa(&vcpu->arch))
7428c2ecf20Sopenharmony_ci			return -EINVAL;
7438c2ecf20Sopenharmony_ci		v = boot_cpu_data.msa_id;
7448c2ecf20Sopenharmony_ci		break;
7458c2ecf20Sopenharmony_ci	case KVM_REG_MIPS_MSA_CSR:
7468c2ecf20Sopenharmony_ci		if (!kvm_mips_guest_has_msa(&vcpu->arch))
7478c2ecf20Sopenharmony_ci			return -EINVAL;
7488c2ecf20Sopenharmony_ci		v = fpu->msacsr;
7498c2ecf20Sopenharmony_ci		break;
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	/* registers to be handled specially */
7528c2ecf20Sopenharmony_ci	default:
7538c2ecf20Sopenharmony_ci		ret = kvm_mips_callbacks->get_one_reg(vcpu, reg, &v);
7548c2ecf20Sopenharmony_ci		if (ret)
7558c2ecf20Sopenharmony_ci			return ret;
7568c2ecf20Sopenharmony_ci		break;
7578c2ecf20Sopenharmony_ci	}
7588c2ecf20Sopenharmony_ci	if ((reg->id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U64) {
7598c2ecf20Sopenharmony_ci		u64 __user *uaddr64 = (u64 __user *)(long)reg->addr;
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci		return put_user(v, uaddr64);
7628c2ecf20Sopenharmony_ci	} else if ((reg->id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U32) {
7638c2ecf20Sopenharmony_ci		u32 __user *uaddr32 = (u32 __user *)(long)reg->addr;
7648c2ecf20Sopenharmony_ci		u32 v32 = (u32)v;
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci		return put_user(v32, uaddr32);
7678c2ecf20Sopenharmony_ci	} else if ((reg->id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U128) {
7688c2ecf20Sopenharmony_ci		void __user *uaddr = (void __user *)(long)reg->addr;
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci		return copy_to_user(uaddr, vs, 16) ? -EFAULT : 0;
7718c2ecf20Sopenharmony_ci	} else {
7728c2ecf20Sopenharmony_ci		return -EINVAL;
7738c2ecf20Sopenharmony_ci	}
7748c2ecf20Sopenharmony_ci}
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_cistatic int kvm_mips_set_reg(struct kvm_vcpu *vcpu,
7778c2ecf20Sopenharmony_ci			    const struct kvm_one_reg *reg)
7788c2ecf20Sopenharmony_ci{
7798c2ecf20Sopenharmony_ci	struct mips_coproc *cop0 = vcpu->arch.cop0;
7808c2ecf20Sopenharmony_ci	struct mips_fpu_struct *fpu = &vcpu->arch.fpu;
7818c2ecf20Sopenharmony_ci	s64 v;
7828c2ecf20Sopenharmony_ci	s64 vs[2];
7838c2ecf20Sopenharmony_ci	unsigned int idx;
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	if ((reg->id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U64) {
7868c2ecf20Sopenharmony_ci		u64 __user *uaddr64 = (u64 __user *)(long)reg->addr;
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci		if (get_user(v, uaddr64) != 0)
7898c2ecf20Sopenharmony_ci			return -EFAULT;
7908c2ecf20Sopenharmony_ci	} else if ((reg->id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U32) {
7918c2ecf20Sopenharmony_ci		u32 __user *uaddr32 = (u32 __user *)(long)reg->addr;
7928c2ecf20Sopenharmony_ci		s32 v32;
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci		if (get_user(v32, uaddr32) != 0)
7958c2ecf20Sopenharmony_ci			return -EFAULT;
7968c2ecf20Sopenharmony_ci		v = (s64)v32;
7978c2ecf20Sopenharmony_ci	} else if ((reg->id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U128) {
7988c2ecf20Sopenharmony_ci		void __user *uaddr = (void __user *)(long)reg->addr;
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci		return copy_from_user(vs, uaddr, 16) ? -EFAULT : 0;
8018c2ecf20Sopenharmony_ci	} else {
8028c2ecf20Sopenharmony_ci		return -EINVAL;
8038c2ecf20Sopenharmony_ci	}
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	switch (reg->id) {
8068c2ecf20Sopenharmony_ci	/* General purpose registers */
8078c2ecf20Sopenharmony_ci	case KVM_REG_MIPS_R0:
8088c2ecf20Sopenharmony_ci		/* Silently ignore requests to set $0 */
8098c2ecf20Sopenharmony_ci		break;
8108c2ecf20Sopenharmony_ci	case KVM_REG_MIPS_R1 ... KVM_REG_MIPS_R31:
8118c2ecf20Sopenharmony_ci		vcpu->arch.gprs[reg->id - KVM_REG_MIPS_R0] = v;
8128c2ecf20Sopenharmony_ci		break;
8138c2ecf20Sopenharmony_ci#ifndef CONFIG_CPU_MIPSR6
8148c2ecf20Sopenharmony_ci	case KVM_REG_MIPS_HI:
8158c2ecf20Sopenharmony_ci		vcpu->arch.hi = v;
8168c2ecf20Sopenharmony_ci		break;
8178c2ecf20Sopenharmony_ci	case KVM_REG_MIPS_LO:
8188c2ecf20Sopenharmony_ci		vcpu->arch.lo = v;
8198c2ecf20Sopenharmony_ci		break;
8208c2ecf20Sopenharmony_ci#endif
8218c2ecf20Sopenharmony_ci	case KVM_REG_MIPS_PC:
8228c2ecf20Sopenharmony_ci		vcpu->arch.pc = v;
8238c2ecf20Sopenharmony_ci		break;
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci	/* Floating point registers */
8268c2ecf20Sopenharmony_ci	case KVM_REG_MIPS_FPR_32(0) ... KVM_REG_MIPS_FPR_32(31):
8278c2ecf20Sopenharmony_ci		if (!kvm_mips_guest_has_fpu(&vcpu->arch))
8288c2ecf20Sopenharmony_ci			return -EINVAL;
8298c2ecf20Sopenharmony_ci		idx = reg->id - KVM_REG_MIPS_FPR_32(0);
8308c2ecf20Sopenharmony_ci		/* Odd singles in top of even double when FR=0 */
8318c2ecf20Sopenharmony_ci		if (kvm_read_c0_guest_status(cop0) & ST0_FR)
8328c2ecf20Sopenharmony_ci			set_fpr32(&fpu->fpr[idx], 0, v);
8338c2ecf20Sopenharmony_ci		else
8348c2ecf20Sopenharmony_ci			set_fpr32(&fpu->fpr[idx & ~1], idx & 1, v);
8358c2ecf20Sopenharmony_ci		break;
8368c2ecf20Sopenharmony_ci	case KVM_REG_MIPS_FPR_64(0) ... KVM_REG_MIPS_FPR_64(31):
8378c2ecf20Sopenharmony_ci		if (!kvm_mips_guest_has_fpu(&vcpu->arch))
8388c2ecf20Sopenharmony_ci			return -EINVAL;
8398c2ecf20Sopenharmony_ci		idx = reg->id - KVM_REG_MIPS_FPR_64(0);
8408c2ecf20Sopenharmony_ci		/* Can't access odd doubles in FR=0 mode */
8418c2ecf20Sopenharmony_ci		if (idx & 1 && !(kvm_read_c0_guest_status(cop0) & ST0_FR))
8428c2ecf20Sopenharmony_ci			return -EINVAL;
8438c2ecf20Sopenharmony_ci		set_fpr64(&fpu->fpr[idx], 0, v);
8448c2ecf20Sopenharmony_ci		break;
8458c2ecf20Sopenharmony_ci	case KVM_REG_MIPS_FCR_IR:
8468c2ecf20Sopenharmony_ci		if (!kvm_mips_guest_has_fpu(&vcpu->arch))
8478c2ecf20Sopenharmony_ci			return -EINVAL;
8488c2ecf20Sopenharmony_ci		/* Read-only */
8498c2ecf20Sopenharmony_ci		break;
8508c2ecf20Sopenharmony_ci	case KVM_REG_MIPS_FCR_CSR:
8518c2ecf20Sopenharmony_ci		if (!kvm_mips_guest_has_fpu(&vcpu->arch))
8528c2ecf20Sopenharmony_ci			return -EINVAL;
8538c2ecf20Sopenharmony_ci		fpu->fcr31 = v;
8548c2ecf20Sopenharmony_ci		break;
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	/* MIPS SIMD Architecture (MSA) registers */
8578c2ecf20Sopenharmony_ci	case KVM_REG_MIPS_VEC_128(0) ... KVM_REG_MIPS_VEC_128(31):
8588c2ecf20Sopenharmony_ci		if (!kvm_mips_guest_has_msa(&vcpu->arch))
8598c2ecf20Sopenharmony_ci			return -EINVAL;
8608c2ecf20Sopenharmony_ci		idx = reg->id - KVM_REG_MIPS_VEC_128(0);
8618c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_LITTLE_ENDIAN
8628c2ecf20Sopenharmony_ci		/* least significant byte first */
8638c2ecf20Sopenharmony_ci		set_fpr64(&fpu->fpr[idx], 0, vs[0]);
8648c2ecf20Sopenharmony_ci		set_fpr64(&fpu->fpr[idx], 1, vs[1]);
8658c2ecf20Sopenharmony_ci#else
8668c2ecf20Sopenharmony_ci		/* most significant byte first */
8678c2ecf20Sopenharmony_ci		set_fpr64(&fpu->fpr[idx], 1, vs[0]);
8688c2ecf20Sopenharmony_ci		set_fpr64(&fpu->fpr[idx], 0, vs[1]);
8698c2ecf20Sopenharmony_ci#endif
8708c2ecf20Sopenharmony_ci		break;
8718c2ecf20Sopenharmony_ci	case KVM_REG_MIPS_MSA_IR:
8728c2ecf20Sopenharmony_ci		if (!kvm_mips_guest_has_msa(&vcpu->arch))
8738c2ecf20Sopenharmony_ci			return -EINVAL;
8748c2ecf20Sopenharmony_ci		/* Read-only */
8758c2ecf20Sopenharmony_ci		break;
8768c2ecf20Sopenharmony_ci	case KVM_REG_MIPS_MSA_CSR:
8778c2ecf20Sopenharmony_ci		if (!kvm_mips_guest_has_msa(&vcpu->arch))
8788c2ecf20Sopenharmony_ci			return -EINVAL;
8798c2ecf20Sopenharmony_ci		fpu->msacsr = v;
8808c2ecf20Sopenharmony_ci		break;
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	/* registers to be handled specially */
8838c2ecf20Sopenharmony_ci	default:
8848c2ecf20Sopenharmony_ci		return kvm_mips_callbacks->set_one_reg(vcpu, reg, v);
8858c2ecf20Sopenharmony_ci	}
8868c2ecf20Sopenharmony_ci	return 0;
8878c2ecf20Sopenharmony_ci}
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_cistatic int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu,
8908c2ecf20Sopenharmony_ci				     struct kvm_enable_cap *cap)
8918c2ecf20Sopenharmony_ci{
8928c2ecf20Sopenharmony_ci	int r = 0;
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci	if (!kvm_vm_ioctl_check_extension(vcpu->kvm, cap->cap))
8958c2ecf20Sopenharmony_ci		return -EINVAL;
8968c2ecf20Sopenharmony_ci	if (cap->flags)
8978c2ecf20Sopenharmony_ci		return -EINVAL;
8988c2ecf20Sopenharmony_ci	if (cap->args[0])
8998c2ecf20Sopenharmony_ci		return -EINVAL;
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	switch (cap->cap) {
9028c2ecf20Sopenharmony_ci	case KVM_CAP_MIPS_FPU:
9038c2ecf20Sopenharmony_ci		vcpu->arch.fpu_enabled = true;
9048c2ecf20Sopenharmony_ci		break;
9058c2ecf20Sopenharmony_ci	case KVM_CAP_MIPS_MSA:
9068c2ecf20Sopenharmony_ci		vcpu->arch.msa_enabled = true;
9078c2ecf20Sopenharmony_ci		break;
9088c2ecf20Sopenharmony_ci	default:
9098c2ecf20Sopenharmony_ci		r = -EINVAL;
9108c2ecf20Sopenharmony_ci		break;
9118c2ecf20Sopenharmony_ci	}
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ci	return r;
9148c2ecf20Sopenharmony_ci}
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_cilong kvm_arch_vcpu_async_ioctl(struct file *filp, unsigned int ioctl,
9178c2ecf20Sopenharmony_ci			       unsigned long arg)
9188c2ecf20Sopenharmony_ci{
9198c2ecf20Sopenharmony_ci	struct kvm_vcpu *vcpu = filp->private_data;
9208c2ecf20Sopenharmony_ci	void __user *argp = (void __user *)arg;
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	if (ioctl == KVM_INTERRUPT) {
9238c2ecf20Sopenharmony_ci		struct kvm_mips_interrupt irq;
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci		if (copy_from_user(&irq, argp, sizeof(irq)))
9268c2ecf20Sopenharmony_ci			return -EFAULT;
9278c2ecf20Sopenharmony_ci		kvm_debug("[%d] %s: irq: %d\n", vcpu->vcpu_id, __func__,
9288c2ecf20Sopenharmony_ci			  irq.irq);
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_ci		return kvm_vcpu_ioctl_interrupt(vcpu, &irq);
9318c2ecf20Sopenharmony_ci	}
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci	return -ENOIOCTLCMD;
9348c2ecf20Sopenharmony_ci}
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_cilong kvm_arch_vcpu_ioctl(struct file *filp, unsigned int ioctl,
9378c2ecf20Sopenharmony_ci			 unsigned long arg)
9388c2ecf20Sopenharmony_ci{
9398c2ecf20Sopenharmony_ci	struct kvm_vcpu *vcpu = filp->private_data;
9408c2ecf20Sopenharmony_ci	void __user *argp = (void __user *)arg;
9418c2ecf20Sopenharmony_ci	long r;
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci	vcpu_load(vcpu);
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci	switch (ioctl) {
9468c2ecf20Sopenharmony_ci	case KVM_SET_ONE_REG:
9478c2ecf20Sopenharmony_ci	case KVM_GET_ONE_REG: {
9488c2ecf20Sopenharmony_ci		struct kvm_one_reg reg;
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci		r = -EFAULT;
9518c2ecf20Sopenharmony_ci		if (copy_from_user(&reg, argp, sizeof(reg)))
9528c2ecf20Sopenharmony_ci			break;
9538c2ecf20Sopenharmony_ci		if (ioctl == KVM_SET_ONE_REG)
9548c2ecf20Sopenharmony_ci			r = kvm_mips_set_reg(vcpu, &reg);
9558c2ecf20Sopenharmony_ci		else
9568c2ecf20Sopenharmony_ci			r = kvm_mips_get_reg(vcpu, &reg);
9578c2ecf20Sopenharmony_ci		break;
9588c2ecf20Sopenharmony_ci	}
9598c2ecf20Sopenharmony_ci	case KVM_GET_REG_LIST: {
9608c2ecf20Sopenharmony_ci		struct kvm_reg_list __user *user_list = argp;
9618c2ecf20Sopenharmony_ci		struct kvm_reg_list reg_list;
9628c2ecf20Sopenharmony_ci		unsigned n;
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci		r = -EFAULT;
9658c2ecf20Sopenharmony_ci		if (copy_from_user(&reg_list, user_list, sizeof(reg_list)))
9668c2ecf20Sopenharmony_ci			break;
9678c2ecf20Sopenharmony_ci		n = reg_list.n;
9688c2ecf20Sopenharmony_ci		reg_list.n = kvm_mips_num_regs(vcpu);
9698c2ecf20Sopenharmony_ci		if (copy_to_user(user_list, &reg_list, sizeof(reg_list)))
9708c2ecf20Sopenharmony_ci			break;
9718c2ecf20Sopenharmony_ci		r = -E2BIG;
9728c2ecf20Sopenharmony_ci		if (n < reg_list.n)
9738c2ecf20Sopenharmony_ci			break;
9748c2ecf20Sopenharmony_ci		r = kvm_mips_copy_reg_indices(vcpu, user_list->reg);
9758c2ecf20Sopenharmony_ci		break;
9768c2ecf20Sopenharmony_ci	}
9778c2ecf20Sopenharmony_ci	case KVM_ENABLE_CAP: {
9788c2ecf20Sopenharmony_ci		struct kvm_enable_cap cap;
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci		r = -EFAULT;
9818c2ecf20Sopenharmony_ci		if (copy_from_user(&cap, argp, sizeof(cap)))
9828c2ecf20Sopenharmony_ci			break;
9838c2ecf20Sopenharmony_ci		r = kvm_vcpu_ioctl_enable_cap(vcpu, &cap);
9848c2ecf20Sopenharmony_ci		break;
9858c2ecf20Sopenharmony_ci	}
9868c2ecf20Sopenharmony_ci	default:
9878c2ecf20Sopenharmony_ci		r = -ENOIOCTLCMD;
9888c2ecf20Sopenharmony_ci	}
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci	vcpu_put(vcpu);
9918c2ecf20Sopenharmony_ci	return r;
9928c2ecf20Sopenharmony_ci}
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_civoid kvm_arch_sync_dirty_log(struct kvm *kvm, struct kvm_memory_slot *memslot)
9958c2ecf20Sopenharmony_ci{
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci}
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_civoid kvm_arch_flush_remote_tlbs_memslot(struct kvm *kvm,
10008c2ecf20Sopenharmony_ci					struct kvm_memory_slot *memslot)
10018c2ecf20Sopenharmony_ci{
10028c2ecf20Sopenharmony_ci	/* Let implementation handle TLB/GVA invalidation */
10038c2ecf20Sopenharmony_ci	kvm_mips_callbacks->flush_shadow_memslot(kvm, memslot);
10048c2ecf20Sopenharmony_ci}
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_cilong kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
10078c2ecf20Sopenharmony_ci{
10088c2ecf20Sopenharmony_ci	long r;
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci	switch (ioctl) {
10118c2ecf20Sopenharmony_ci	default:
10128c2ecf20Sopenharmony_ci		r = -ENOIOCTLCMD;
10138c2ecf20Sopenharmony_ci	}
10148c2ecf20Sopenharmony_ci
10158c2ecf20Sopenharmony_ci	return r;
10168c2ecf20Sopenharmony_ci}
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ciint kvm_arch_init(void *opaque)
10198c2ecf20Sopenharmony_ci{
10208c2ecf20Sopenharmony_ci	if (kvm_mips_callbacks) {
10218c2ecf20Sopenharmony_ci		kvm_err("kvm: module already exists\n");
10228c2ecf20Sopenharmony_ci		return -EEXIST;
10238c2ecf20Sopenharmony_ci	}
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ci	return kvm_mips_emulation_init(&kvm_mips_callbacks);
10268c2ecf20Sopenharmony_ci}
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_civoid kvm_arch_exit(void)
10298c2ecf20Sopenharmony_ci{
10308c2ecf20Sopenharmony_ci	kvm_mips_callbacks = NULL;
10318c2ecf20Sopenharmony_ci}
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ciint kvm_arch_vcpu_ioctl_get_sregs(struct kvm_vcpu *vcpu,
10348c2ecf20Sopenharmony_ci				  struct kvm_sregs *sregs)
10358c2ecf20Sopenharmony_ci{
10368c2ecf20Sopenharmony_ci	return -ENOIOCTLCMD;
10378c2ecf20Sopenharmony_ci}
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ciint kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu,
10408c2ecf20Sopenharmony_ci				  struct kvm_sregs *sregs)
10418c2ecf20Sopenharmony_ci{
10428c2ecf20Sopenharmony_ci	return -ENOIOCTLCMD;
10438c2ecf20Sopenharmony_ci}
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_civoid kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu)
10468c2ecf20Sopenharmony_ci{
10478c2ecf20Sopenharmony_ci}
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ciint kvm_arch_vcpu_ioctl_get_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu)
10508c2ecf20Sopenharmony_ci{
10518c2ecf20Sopenharmony_ci	return -ENOIOCTLCMD;
10528c2ecf20Sopenharmony_ci}
10538c2ecf20Sopenharmony_ci
10548c2ecf20Sopenharmony_ciint kvm_arch_vcpu_ioctl_set_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu)
10558c2ecf20Sopenharmony_ci{
10568c2ecf20Sopenharmony_ci	return -ENOIOCTLCMD;
10578c2ecf20Sopenharmony_ci}
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_civm_fault_t kvm_arch_vcpu_fault(struct kvm_vcpu *vcpu, struct vm_fault *vmf)
10608c2ecf20Sopenharmony_ci{
10618c2ecf20Sopenharmony_ci	return VM_FAULT_SIGBUS;
10628c2ecf20Sopenharmony_ci}
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ciint kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
10658c2ecf20Sopenharmony_ci{
10668c2ecf20Sopenharmony_ci	int r;
10678c2ecf20Sopenharmony_ci
10688c2ecf20Sopenharmony_ci	switch (ext) {
10698c2ecf20Sopenharmony_ci	case KVM_CAP_ONE_REG:
10708c2ecf20Sopenharmony_ci	case KVM_CAP_ENABLE_CAP:
10718c2ecf20Sopenharmony_ci	case KVM_CAP_READONLY_MEM:
10728c2ecf20Sopenharmony_ci	case KVM_CAP_SYNC_MMU:
10738c2ecf20Sopenharmony_ci	case KVM_CAP_IMMEDIATE_EXIT:
10748c2ecf20Sopenharmony_ci		r = 1;
10758c2ecf20Sopenharmony_ci		break;
10768c2ecf20Sopenharmony_ci	case KVM_CAP_NR_VCPUS:
10778c2ecf20Sopenharmony_ci		r = num_online_cpus();
10788c2ecf20Sopenharmony_ci		break;
10798c2ecf20Sopenharmony_ci	case KVM_CAP_MAX_VCPUS:
10808c2ecf20Sopenharmony_ci		r = KVM_MAX_VCPUS;
10818c2ecf20Sopenharmony_ci		break;
10828c2ecf20Sopenharmony_ci	case KVM_CAP_MAX_VCPU_ID:
10838c2ecf20Sopenharmony_ci		r = KVM_MAX_VCPU_ID;
10848c2ecf20Sopenharmony_ci		break;
10858c2ecf20Sopenharmony_ci	case KVM_CAP_MIPS_FPU:
10868c2ecf20Sopenharmony_ci		/* We don't handle systems with inconsistent cpu_has_fpu */
10878c2ecf20Sopenharmony_ci		r = !!raw_cpu_has_fpu;
10888c2ecf20Sopenharmony_ci		break;
10898c2ecf20Sopenharmony_ci	case KVM_CAP_MIPS_MSA:
10908c2ecf20Sopenharmony_ci		/*
10918c2ecf20Sopenharmony_ci		 * We don't support MSA vector partitioning yet:
10928c2ecf20Sopenharmony_ci		 * 1) It would require explicit support which can't be tested
10938c2ecf20Sopenharmony_ci		 *    yet due to lack of support in current hardware.
10948c2ecf20Sopenharmony_ci		 * 2) It extends the state that would need to be saved/restored
10958c2ecf20Sopenharmony_ci		 *    by e.g. QEMU for migration.
10968c2ecf20Sopenharmony_ci		 *
10978c2ecf20Sopenharmony_ci		 * When vector partitioning hardware becomes available, support
10988c2ecf20Sopenharmony_ci		 * could be added by requiring a flag when enabling
10998c2ecf20Sopenharmony_ci		 * KVM_CAP_MIPS_MSA capability to indicate that userland knows
11008c2ecf20Sopenharmony_ci		 * to save/restore the appropriate extra state.
11018c2ecf20Sopenharmony_ci		 */
11028c2ecf20Sopenharmony_ci		r = cpu_has_msa && !(boot_cpu_data.msa_id & MSA_IR_WRPF);
11038c2ecf20Sopenharmony_ci		break;
11048c2ecf20Sopenharmony_ci	default:
11058c2ecf20Sopenharmony_ci		r = kvm_mips_callbacks->check_extension(kvm, ext);
11068c2ecf20Sopenharmony_ci		break;
11078c2ecf20Sopenharmony_ci	}
11088c2ecf20Sopenharmony_ci	return r;
11098c2ecf20Sopenharmony_ci}
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ciint kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu)
11128c2ecf20Sopenharmony_ci{
11138c2ecf20Sopenharmony_ci	return kvm_mips_pending_timer(vcpu) ||
11148c2ecf20Sopenharmony_ci		kvm_read_c0_guest_cause(vcpu->arch.cop0) & C_TI;
11158c2ecf20Sopenharmony_ci}
11168c2ecf20Sopenharmony_ci
11178c2ecf20Sopenharmony_ciint kvm_arch_vcpu_dump_regs(struct kvm_vcpu *vcpu)
11188c2ecf20Sopenharmony_ci{
11198c2ecf20Sopenharmony_ci	int i;
11208c2ecf20Sopenharmony_ci	struct mips_coproc *cop0;
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci	if (!vcpu)
11238c2ecf20Sopenharmony_ci		return -1;
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci	kvm_debug("VCPU Register Dump:\n");
11268c2ecf20Sopenharmony_ci	kvm_debug("\tpc = 0x%08lx\n", vcpu->arch.pc);
11278c2ecf20Sopenharmony_ci	kvm_debug("\texceptions: %08lx\n", vcpu->arch.pending_exceptions);
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_ci	for (i = 0; i < 32; i += 4) {
11308c2ecf20Sopenharmony_ci		kvm_debug("\tgpr%02d: %08lx %08lx %08lx %08lx\n", i,
11318c2ecf20Sopenharmony_ci		       vcpu->arch.gprs[i],
11328c2ecf20Sopenharmony_ci		       vcpu->arch.gprs[i + 1],
11338c2ecf20Sopenharmony_ci		       vcpu->arch.gprs[i + 2], vcpu->arch.gprs[i + 3]);
11348c2ecf20Sopenharmony_ci	}
11358c2ecf20Sopenharmony_ci	kvm_debug("\thi: 0x%08lx\n", vcpu->arch.hi);
11368c2ecf20Sopenharmony_ci	kvm_debug("\tlo: 0x%08lx\n", vcpu->arch.lo);
11378c2ecf20Sopenharmony_ci
11388c2ecf20Sopenharmony_ci	cop0 = vcpu->arch.cop0;
11398c2ecf20Sopenharmony_ci	kvm_debug("\tStatus: 0x%08x, Cause: 0x%08x\n",
11408c2ecf20Sopenharmony_ci		  kvm_read_c0_guest_status(cop0),
11418c2ecf20Sopenharmony_ci		  kvm_read_c0_guest_cause(cop0));
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ci	kvm_debug("\tEPC: 0x%08lx\n", kvm_read_c0_guest_epc(cop0));
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_ci	return 0;
11468c2ecf20Sopenharmony_ci}
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_ciint kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
11498c2ecf20Sopenharmony_ci{
11508c2ecf20Sopenharmony_ci	int i;
11518c2ecf20Sopenharmony_ci
11528c2ecf20Sopenharmony_ci	vcpu_load(vcpu);
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_ci	for (i = 1; i < ARRAY_SIZE(vcpu->arch.gprs); i++)
11558c2ecf20Sopenharmony_ci		vcpu->arch.gprs[i] = regs->gpr[i];
11568c2ecf20Sopenharmony_ci	vcpu->arch.gprs[0] = 0; /* zero is special, and cannot be set. */
11578c2ecf20Sopenharmony_ci	vcpu->arch.hi = regs->hi;
11588c2ecf20Sopenharmony_ci	vcpu->arch.lo = regs->lo;
11598c2ecf20Sopenharmony_ci	vcpu->arch.pc = regs->pc;
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci	vcpu_put(vcpu);
11628c2ecf20Sopenharmony_ci	return 0;
11638c2ecf20Sopenharmony_ci}
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_ciint kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
11668c2ecf20Sopenharmony_ci{
11678c2ecf20Sopenharmony_ci	int i;
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_ci	vcpu_load(vcpu);
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(vcpu->arch.gprs); i++)
11728c2ecf20Sopenharmony_ci		regs->gpr[i] = vcpu->arch.gprs[i];
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci	regs->hi = vcpu->arch.hi;
11758c2ecf20Sopenharmony_ci	regs->lo = vcpu->arch.lo;
11768c2ecf20Sopenharmony_ci	regs->pc = vcpu->arch.pc;
11778c2ecf20Sopenharmony_ci
11788c2ecf20Sopenharmony_ci	vcpu_put(vcpu);
11798c2ecf20Sopenharmony_ci	return 0;
11808c2ecf20Sopenharmony_ci}
11818c2ecf20Sopenharmony_ci
11828c2ecf20Sopenharmony_ciint kvm_arch_vcpu_ioctl_translate(struct kvm_vcpu *vcpu,
11838c2ecf20Sopenharmony_ci				  struct kvm_translation *tr)
11848c2ecf20Sopenharmony_ci{
11858c2ecf20Sopenharmony_ci	return 0;
11868c2ecf20Sopenharmony_ci}
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_cistatic void kvm_mips_set_c0_status(void)
11898c2ecf20Sopenharmony_ci{
11908c2ecf20Sopenharmony_ci	u32 status = read_c0_status();
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_ci	if (cpu_has_dsp)
11938c2ecf20Sopenharmony_ci		status |= (ST0_MX);
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_ci	write_c0_status(status);
11968c2ecf20Sopenharmony_ci	ehb();
11978c2ecf20Sopenharmony_ci}
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_ci/*
12008c2ecf20Sopenharmony_ci * Return value is in the form (errcode<<2 | RESUME_FLAG_HOST | RESUME_FLAG_NV)
12018c2ecf20Sopenharmony_ci */
12028c2ecf20Sopenharmony_ciint kvm_mips_handle_exit(struct kvm_vcpu *vcpu)
12038c2ecf20Sopenharmony_ci{
12048c2ecf20Sopenharmony_ci	struct kvm_run *run = vcpu->run;
12058c2ecf20Sopenharmony_ci	u32 cause = vcpu->arch.host_cp0_cause;
12068c2ecf20Sopenharmony_ci	u32 exccode = (cause >> CAUSEB_EXCCODE) & 0x1f;
12078c2ecf20Sopenharmony_ci	u32 __user *opc = (u32 __user *) vcpu->arch.pc;
12088c2ecf20Sopenharmony_ci	unsigned long badvaddr = vcpu->arch.host_cp0_badvaddr;
12098c2ecf20Sopenharmony_ci	enum emulation_result er = EMULATE_DONE;
12108c2ecf20Sopenharmony_ci	u32 inst;
12118c2ecf20Sopenharmony_ci	int ret = RESUME_GUEST;
12128c2ecf20Sopenharmony_ci
12138c2ecf20Sopenharmony_ci	vcpu->mode = OUTSIDE_GUEST_MODE;
12148c2ecf20Sopenharmony_ci
12158c2ecf20Sopenharmony_ci	/* re-enable HTW before enabling interrupts */
12168c2ecf20Sopenharmony_ci	if (!IS_ENABLED(CONFIG_KVM_MIPS_VZ))
12178c2ecf20Sopenharmony_ci		htw_start();
12188c2ecf20Sopenharmony_ci
12198c2ecf20Sopenharmony_ci	/* Set a default exit reason */
12208c2ecf20Sopenharmony_ci	run->exit_reason = KVM_EXIT_UNKNOWN;
12218c2ecf20Sopenharmony_ci	run->ready_for_interrupt_injection = 1;
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci	/*
12248c2ecf20Sopenharmony_ci	 * Set the appropriate status bits based on host CPU features,
12258c2ecf20Sopenharmony_ci	 * before we hit the scheduler
12268c2ecf20Sopenharmony_ci	 */
12278c2ecf20Sopenharmony_ci	kvm_mips_set_c0_status();
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_ci	local_irq_enable();
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_ci	kvm_debug("kvm_mips_handle_exit: cause: %#x, PC: %p, kvm_run: %p, kvm_vcpu: %p\n",
12328c2ecf20Sopenharmony_ci			cause, opc, run, vcpu);
12338c2ecf20Sopenharmony_ci	trace_kvm_exit(vcpu, exccode);
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_ci	if (!IS_ENABLED(CONFIG_KVM_MIPS_VZ)) {
12368c2ecf20Sopenharmony_ci		/*
12378c2ecf20Sopenharmony_ci		 * Do a privilege check, if in UM most of these exit conditions
12388c2ecf20Sopenharmony_ci		 * end up causing an exception to be delivered to the Guest
12398c2ecf20Sopenharmony_ci		 * Kernel
12408c2ecf20Sopenharmony_ci		 */
12418c2ecf20Sopenharmony_ci		er = kvm_mips_check_privilege(cause, opc, vcpu);
12428c2ecf20Sopenharmony_ci		if (er == EMULATE_PRIV_FAIL) {
12438c2ecf20Sopenharmony_ci			goto skip_emul;
12448c2ecf20Sopenharmony_ci		} else if (er == EMULATE_FAIL) {
12458c2ecf20Sopenharmony_ci			run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
12468c2ecf20Sopenharmony_ci			ret = RESUME_HOST;
12478c2ecf20Sopenharmony_ci			goto skip_emul;
12488c2ecf20Sopenharmony_ci		}
12498c2ecf20Sopenharmony_ci	}
12508c2ecf20Sopenharmony_ci
12518c2ecf20Sopenharmony_ci	switch (exccode) {
12528c2ecf20Sopenharmony_ci	case EXCCODE_INT:
12538c2ecf20Sopenharmony_ci		kvm_debug("[%d]EXCCODE_INT @ %p\n", vcpu->vcpu_id, opc);
12548c2ecf20Sopenharmony_ci
12558c2ecf20Sopenharmony_ci		++vcpu->stat.int_exits;
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_ci		if (need_resched())
12588c2ecf20Sopenharmony_ci			cond_resched();
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_ci		ret = RESUME_GUEST;
12618c2ecf20Sopenharmony_ci		break;
12628c2ecf20Sopenharmony_ci
12638c2ecf20Sopenharmony_ci	case EXCCODE_CPU:
12648c2ecf20Sopenharmony_ci		kvm_debug("EXCCODE_CPU: @ PC: %p\n", opc);
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_ci		++vcpu->stat.cop_unusable_exits;
12678c2ecf20Sopenharmony_ci		ret = kvm_mips_callbacks->handle_cop_unusable(vcpu);
12688c2ecf20Sopenharmony_ci		/* XXXKYMA: Might need to return to user space */
12698c2ecf20Sopenharmony_ci		if (run->exit_reason == KVM_EXIT_IRQ_WINDOW_OPEN)
12708c2ecf20Sopenharmony_ci			ret = RESUME_HOST;
12718c2ecf20Sopenharmony_ci		break;
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_ci	case EXCCODE_MOD:
12748c2ecf20Sopenharmony_ci		++vcpu->stat.tlbmod_exits;
12758c2ecf20Sopenharmony_ci		ret = kvm_mips_callbacks->handle_tlb_mod(vcpu);
12768c2ecf20Sopenharmony_ci		break;
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_ci	case EXCCODE_TLBS:
12798c2ecf20Sopenharmony_ci		kvm_debug("TLB ST fault:  cause %#x, status %#x, PC: %p, BadVaddr: %#lx\n",
12808c2ecf20Sopenharmony_ci			  cause, kvm_read_c0_guest_status(vcpu->arch.cop0), opc,
12818c2ecf20Sopenharmony_ci			  badvaddr);
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ci		++vcpu->stat.tlbmiss_st_exits;
12848c2ecf20Sopenharmony_ci		ret = kvm_mips_callbacks->handle_tlb_st_miss(vcpu);
12858c2ecf20Sopenharmony_ci		break;
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_ci	case EXCCODE_TLBL:
12888c2ecf20Sopenharmony_ci		kvm_debug("TLB LD fault: cause %#x, PC: %p, BadVaddr: %#lx\n",
12898c2ecf20Sopenharmony_ci			  cause, opc, badvaddr);
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci		++vcpu->stat.tlbmiss_ld_exits;
12928c2ecf20Sopenharmony_ci		ret = kvm_mips_callbacks->handle_tlb_ld_miss(vcpu);
12938c2ecf20Sopenharmony_ci		break;
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_ci	case EXCCODE_ADES:
12968c2ecf20Sopenharmony_ci		++vcpu->stat.addrerr_st_exits;
12978c2ecf20Sopenharmony_ci		ret = kvm_mips_callbacks->handle_addr_err_st(vcpu);
12988c2ecf20Sopenharmony_ci		break;
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_ci	case EXCCODE_ADEL:
13018c2ecf20Sopenharmony_ci		++vcpu->stat.addrerr_ld_exits;
13028c2ecf20Sopenharmony_ci		ret = kvm_mips_callbacks->handle_addr_err_ld(vcpu);
13038c2ecf20Sopenharmony_ci		break;
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_ci	case EXCCODE_SYS:
13068c2ecf20Sopenharmony_ci		++vcpu->stat.syscall_exits;
13078c2ecf20Sopenharmony_ci		ret = kvm_mips_callbacks->handle_syscall(vcpu);
13088c2ecf20Sopenharmony_ci		break;
13098c2ecf20Sopenharmony_ci
13108c2ecf20Sopenharmony_ci	case EXCCODE_RI:
13118c2ecf20Sopenharmony_ci		++vcpu->stat.resvd_inst_exits;
13128c2ecf20Sopenharmony_ci		ret = kvm_mips_callbacks->handle_res_inst(vcpu);
13138c2ecf20Sopenharmony_ci		break;
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_ci	case EXCCODE_BP:
13168c2ecf20Sopenharmony_ci		++vcpu->stat.break_inst_exits;
13178c2ecf20Sopenharmony_ci		ret = kvm_mips_callbacks->handle_break(vcpu);
13188c2ecf20Sopenharmony_ci		break;
13198c2ecf20Sopenharmony_ci
13208c2ecf20Sopenharmony_ci	case EXCCODE_TR:
13218c2ecf20Sopenharmony_ci		++vcpu->stat.trap_inst_exits;
13228c2ecf20Sopenharmony_ci		ret = kvm_mips_callbacks->handle_trap(vcpu);
13238c2ecf20Sopenharmony_ci		break;
13248c2ecf20Sopenharmony_ci
13258c2ecf20Sopenharmony_ci	case EXCCODE_MSAFPE:
13268c2ecf20Sopenharmony_ci		++vcpu->stat.msa_fpe_exits;
13278c2ecf20Sopenharmony_ci		ret = kvm_mips_callbacks->handle_msa_fpe(vcpu);
13288c2ecf20Sopenharmony_ci		break;
13298c2ecf20Sopenharmony_ci
13308c2ecf20Sopenharmony_ci	case EXCCODE_FPE:
13318c2ecf20Sopenharmony_ci		++vcpu->stat.fpe_exits;
13328c2ecf20Sopenharmony_ci		ret = kvm_mips_callbacks->handle_fpe(vcpu);
13338c2ecf20Sopenharmony_ci		break;
13348c2ecf20Sopenharmony_ci
13358c2ecf20Sopenharmony_ci	case EXCCODE_MSADIS:
13368c2ecf20Sopenharmony_ci		++vcpu->stat.msa_disabled_exits;
13378c2ecf20Sopenharmony_ci		ret = kvm_mips_callbacks->handle_msa_disabled(vcpu);
13388c2ecf20Sopenharmony_ci		break;
13398c2ecf20Sopenharmony_ci
13408c2ecf20Sopenharmony_ci	case EXCCODE_GE:
13418c2ecf20Sopenharmony_ci		/* defer exit accounting to handler */
13428c2ecf20Sopenharmony_ci		ret = kvm_mips_callbacks->handle_guest_exit(vcpu);
13438c2ecf20Sopenharmony_ci		break;
13448c2ecf20Sopenharmony_ci
13458c2ecf20Sopenharmony_ci	default:
13468c2ecf20Sopenharmony_ci		if (cause & CAUSEF_BD)
13478c2ecf20Sopenharmony_ci			opc += 1;
13488c2ecf20Sopenharmony_ci		inst = 0;
13498c2ecf20Sopenharmony_ci		kvm_get_badinstr(opc, vcpu, &inst);
13508c2ecf20Sopenharmony_ci		kvm_err("Exception Code: %d, not yet handled, @ PC: %p, inst: 0x%08x  BadVaddr: %#lx Status: %#x\n",
13518c2ecf20Sopenharmony_ci			exccode, opc, inst, badvaddr,
13528c2ecf20Sopenharmony_ci			kvm_read_c0_guest_status(vcpu->arch.cop0));
13538c2ecf20Sopenharmony_ci		kvm_arch_vcpu_dump_regs(vcpu);
13548c2ecf20Sopenharmony_ci		run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
13558c2ecf20Sopenharmony_ci		ret = RESUME_HOST;
13568c2ecf20Sopenharmony_ci		break;
13578c2ecf20Sopenharmony_ci
13588c2ecf20Sopenharmony_ci	}
13598c2ecf20Sopenharmony_ci
13608c2ecf20Sopenharmony_ciskip_emul:
13618c2ecf20Sopenharmony_ci	local_irq_disable();
13628c2ecf20Sopenharmony_ci
13638c2ecf20Sopenharmony_ci	if (ret == RESUME_GUEST)
13648c2ecf20Sopenharmony_ci		kvm_vz_acquire_htimer(vcpu);
13658c2ecf20Sopenharmony_ci
13668c2ecf20Sopenharmony_ci	if (er == EMULATE_DONE && !(ret & RESUME_HOST))
13678c2ecf20Sopenharmony_ci		kvm_mips_deliver_interrupts(vcpu, cause);
13688c2ecf20Sopenharmony_ci
13698c2ecf20Sopenharmony_ci	if (!(ret & RESUME_HOST)) {
13708c2ecf20Sopenharmony_ci		/* Only check for signals if not already exiting to userspace */
13718c2ecf20Sopenharmony_ci		if (signal_pending(current)) {
13728c2ecf20Sopenharmony_ci			run->exit_reason = KVM_EXIT_INTR;
13738c2ecf20Sopenharmony_ci			ret = (-EINTR << 2) | RESUME_HOST;
13748c2ecf20Sopenharmony_ci			++vcpu->stat.signal_exits;
13758c2ecf20Sopenharmony_ci			trace_kvm_exit(vcpu, KVM_TRACE_EXIT_SIGNAL);
13768c2ecf20Sopenharmony_ci		}
13778c2ecf20Sopenharmony_ci	}
13788c2ecf20Sopenharmony_ci
13798c2ecf20Sopenharmony_ci	if (ret == RESUME_GUEST) {
13808c2ecf20Sopenharmony_ci		trace_kvm_reenter(vcpu);
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci		/*
13838c2ecf20Sopenharmony_ci		 * Make sure the read of VCPU requests in vcpu_reenter()
13848c2ecf20Sopenharmony_ci		 * callback is not reordered ahead of the write to vcpu->mode,
13858c2ecf20Sopenharmony_ci		 * or we could miss a TLB flush request while the requester sees
13868c2ecf20Sopenharmony_ci		 * the VCPU as outside of guest mode and not needing an IPI.
13878c2ecf20Sopenharmony_ci		 */
13888c2ecf20Sopenharmony_ci		smp_store_mb(vcpu->mode, IN_GUEST_MODE);
13898c2ecf20Sopenharmony_ci
13908c2ecf20Sopenharmony_ci		kvm_mips_callbacks->vcpu_reenter(vcpu);
13918c2ecf20Sopenharmony_ci
13928c2ecf20Sopenharmony_ci		/*
13938c2ecf20Sopenharmony_ci		 * If FPU / MSA are enabled (i.e. the guest's FPU / MSA context
13948c2ecf20Sopenharmony_ci		 * is live), restore FCR31 / MSACSR.
13958c2ecf20Sopenharmony_ci		 *
13968c2ecf20Sopenharmony_ci		 * This should be before returning to the guest exception
13978c2ecf20Sopenharmony_ci		 * vector, as it may well cause an [MSA] FP exception if there
13988c2ecf20Sopenharmony_ci		 * are pending exception bits unmasked. (see
13998c2ecf20Sopenharmony_ci		 * kvm_mips_csr_die_notifier() for how that is handled).
14008c2ecf20Sopenharmony_ci		 */
14018c2ecf20Sopenharmony_ci		if (kvm_mips_guest_has_fpu(&vcpu->arch) &&
14028c2ecf20Sopenharmony_ci		    read_c0_status() & ST0_CU1)
14038c2ecf20Sopenharmony_ci			__kvm_restore_fcsr(&vcpu->arch);
14048c2ecf20Sopenharmony_ci
14058c2ecf20Sopenharmony_ci		if (kvm_mips_guest_has_msa(&vcpu->arch) &&
14068c2ecf20Sopenharmony_ci		    read_c0_config5() & MIPS_CONF5_MSAEN)
14078c2ecf20Sopenharmony_ci			__kvm_restore_msacsr(&vcpu->arch);
14088c2ecf20Sopenharmony_ci	}
14098c2ecf20Sopenharmony_ci
14108c2ecf20Sopenharmony_ci	/* Disable HTW before returning to guest or host */
14118c2ecf20Sopenharmony_ci	if (!IS_ENABLED(CONFIG_KVM_MIPS_VZ))
14128c2ecf20Sopenharmony_ci		htw_stop();
14138c2ecf20Sopenharmony_ci
14148c2ecf20Sopenharmony_ci	return ret;
14158c2ecf20Sopenharmony_ci}
14168c2ecf20Sopenharmony_ci
14178c2ecf20Sopenharmony_ci/* Enable FPU for guest and restore context */
14188c2ecf20Sopenharmony_civoid kvm_own_fpu(struct kvm_vcpu *vcpu)
14198c2ecf20Sopenharmony_ci{
14208c2ecf20Sopenharmony_ci	struct mips_coproc *cop0 = vcpu->arch.cop0;
14218c2ecf20Sopenharmony_ci	unsigned int sr, cfg5;
14228c2ecf20Sopenharmony_ci
14238c2ecf20Sopenharmony_ci	preempt_disable();
14248c2ecf20Sopenharmony_ci
14258c2ecf20Sopenharmony_ci	sr = kvm_read_c0_guest_status(cop0);
14268c2ecf20Sopenharmony_ci
14278c2ecf20Sopenharmony_ci	/*
14288c2ecf20Sopenharmony_ci	 * If MSA state is already live, it is undefined how it interacts with
14298c2ecf20Sopenharmony_ci	 * FR=0 FPU state, and we don't want to hit reserved instruction
14308c2ecf20Sopenharmony_ci	 * exceptions trying to save the MSA state later when CU=1 && FR=1, so
14318c2ecf20Sopenharmony_ci	 * play it safe and save it first.
14328c2ecf20Sopenharmony_ci	 *
14338c2ecf20Sopenharmony_ci	 * In theory we shouldn't ever hit this case since kvm_lose_fpu() should
14348c2ecf20Sopenharmony_ci	 * get called when guest CU1 is set, however we can't trust the guest
14358c2ecf20Sopenharmony_ci	 * not to clobber the status register directly via the commpage.
14368c2ecf20Sopenharmony_ci	 */
14378c2ecf20Sopenharmony_ci	if (cpu_has_msa && sr & ST0_CU1 && !(sr & ST0_FR) &&
14388c2ecf20Sopenharmony_ci	    vcpu->arch.aux_inuse & KVM_MIPS_AUX_MSA)
14398c2ecf20Sopenharmony_ci		kvm_lose_fpu(vcpu);
14408c2ecf20Sopenharmony_ci
14418c2ecf20Sopenharmony_ci	/*
14428c2ecf20Sopenharmony_ci	 * Enable FPU for guest
14438c2ecf20Sopenharmony_ci	 * We set FR and FRE according to guest context
14448c2ecf20Sopenharmony_ci	 */
14458c2ecf20Sopenharmony_ci	change_c0_status(ST0_CU1 | ST0_FR, sr);
14468c2ecf20Sopenharmony_ci	if (cpu_has_fre) {
14478c2ecf20Sopenharmony_ci		cfg5 = kvm_read_c0_guest_config5(cop0);
14488c2ecf20Sopenharmony_ci		change_c0_config5(MIPS_CONF5_FRE, cfg5);
14498c2ecf20Sopenharmony_ci	}
14508c2ecf20Sopenharmony_ci	enable_fpu_hazard();
14518c2ecf20Sopenharmony_ci
14528c2ecf20Sopenharmony_ci	/* If guest FPU state not active, restore it now */
14538c2ecf20Sopenharmony_ci	if (!(vcpu->arch.aux_inuse & KVM_MIPS_AUX_FPU)) {
14548c2ecf20Sopenharmony_ci		__kvm_restore_fpu(&vcpu->arch);
14558c2ecf20Sopenharmony_ci		vcpu->arch.aux_inuse |= KVM_MIPS_AUX_FPU;
14568c2ecf20Sopenharmony_ci		trace_kvm_aux(vcpu, KVM_TRACE_AUX_RESTORE, KVM_TRACE_AUX_FPU);
14578c2ecf20Sopenharmony_ci	} else {
14588c2ecf20Sopenharmony_ci		trace_kvm_aux(vcpu, KVM_TRACE_AUX_ENABLE, KVM_TRACE_AUX_FPU);
14598c2ecf20Sopenharmony_ci	}
14608c2ecf20Sopenharmony_ci
14618c2ecf20Sopenharmony_ci	preempt_enable();
14628c2ecf20Sopenharmony_ci}
14638c2ecf20Sopenharmony_ci
14648c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_HAS_MSA
14658c2ecf20Sopenharmony_ci/* Enable MSA for guest and restore context */
14668c2ecf20Sopenharmony_civoid kvm_own_msa(struct kvm_vcpu *vcpu)
14678c2ecf20Sopenharmony_ci{
14688c2ecf20Sopenharmony_ci	struct mips_coproc *cop0 = vcpu->arch.cop0;
14698c2ecf20Sopenharmony_ci	unsigned int sr, cfg5;
14708c2ecf20Sopenharmony_ci
14718c2ecf20Sopenharmony_ci	preempt_disable();
14728c2ecf20Sopenharmony_ci
14738c2ecf20Sopenharmony_ci	/*
14748c2ecf20Sopenharmony_ci	 * Enable FPU if enabled in guest, since we're restoring FPU context
14758c2ecf20Sopenharmony_ci	 * anyway. We set FR and FRE according to guest context.
14768c2ecf20Sopenharmony_ci	 */
14778c2ecf20Sopenharmony_ci	if (kvm_mips_guest_has_fpu(&vcpu->arch)) {
14788c2ecf20Sopenharmony_ci		sr = kvm_read_c0_guest_status(cop0);
14798c2ecf20Sopenharmony_ci
14808c2ecf20Sopenharmony_ci		/*
14818c2ecf20Sopenharmony_ci		 * If FR=0 FPU state is already live, it is undefined how it
14828c2ecf20Sopenharmony_ci		 * interacts with MSA state, so play it safe and save it first.
14838c2ecf20Sopenharmony_ci		 */
14848c2ecf20Sopenharmony_ci		if (!(sr & ST0_FR) &&
14858c2ecf20Sopenharmony_ci		    (vcpu->arch.aux_inuse & (KVM_MIPS_AUX_FPU |
14868c2ecf20Sopenharmony_ci				KVM_MIPS_AUX_MSA)) == KVM_MIPS_AUX_FPU)
14878c2ecf20Sopenharmony_ci			kvm_lose_fpu(vcpu);
14888c2ecf20Sopenharmony_ci
14898c2ecf20Sopenharmony_ci		change_c0_status(ST0_CU1 | ST0_FR, sr);
14908c2ecf20Sopenharmony_ci		if (sr & ST0_CU1 && cpu_has_fre) {
14918c2ecf20Sopenharmony_ci			cfg5 = kvm_read_c0_guest_config5(cop0);
14928c2ecf20Sopenharmony_ci			change_c0_config5(MIPS_CONF5_FRE, cfg5);
14938c2ecf20Sopenharmony_ci		}
14948c2ecf20Sopenharmony_ci	}
14958c2ecf20Sopenharmony_ci
14968c2ecf20Sopenharmony_ci	/* Enable MSA for guest */
14978c2ecf20Sopenharmony_ci	set_c0_config5(MIPS_CONF5_MSAEN);
14988c2ecf20Sopenharmony_ci	enable_fpu_hazard();
14998c2ecf20Sopenharmony_ci
15008c2ecf20Sopenharmony_ci	switch (vcpu->arch.aux_inuse & (KVM_MIPS_AUX_FPU | KVM_MIPS_AUX_MSA)) {
15018c2ecf20Sopenharmony_ci	case KVM_MIPS_AUX_FPU:
15028c2ecf20Sopenharmony_ci		/*
15038c2ecf20Sopenharmony_ci		 * Guest FPU state already loaded, only restore upper MSA state
15048c2ecf20Sopenharmony_ci		 */
15058c2ecf20Sopenharmony_ci		__kvm_restore_msa_upper(&vcpu->arch);
15068c2ecf20Sopenharmony_ci		vcpu->arch.aux_inuse |= KVM_MIPS_AUX_MSA;
15078c2ecf20Sopenharmony_ci		trace_kvm_aux(vcpu, KVM_TRACE_AUX_RESTORE, KVM_TRACE_AUX_MSA);
15088c2ecf20Sopenharmony_ci		break;
15098c2ecf20Sopenharmony_ci	case 0:
15108c2ecf20Sopenharmony_ci		/* Neither FPU or MSA already active, restore full MSA state */
15118c2ecf20Sopenharmony_ci		__kvm_restore_msa(&vcpu->arch);
15128c2ecf20Sopenharmony_ci		vcpu->arch.aux_inuse |= KVM_MIPS_AUX_MSA;
15138c2ecf20Sopenharmony_ci		if (kvm_mips_guest_has_fpu(&vcpu->arch))
15148c2ecf20Sopenharmony_ci			vcpu->arch.aux_inuse |= KVM_MIPS_AUX_FPU;
15158c2ecf20Sopenharmony_ci		trace_kvm_aux(vcpu, KVM_TRACE_AUX_RESTORE,
15168c2ecf20Sopenharmony_ci			      KVM_TRACE_AUX_FPU_MSA);
15178c2ecf20Sopenharmony_ci		break;
15188c2ecf20Sopenharmony_ci	default:
15198c2ecf20Sopenharmony_ci		trace_kvm_aux(vcpu, KVM_TRACE_AUX_ENABLE, KVM_TRACE_AUX_MSA);
15208c2ecf20Sopenharmony_ci		break;
15218c2ecf20Sopenharmony_ci	}
15228c2ecf20Sopenharmony_ci
15238c2ecf20Sopenharmony_ci	preempt_enable();
15248c2ecf20Sopenharmony_ci}
15258c2ecf20Sopenharmony_ci#endif
15268c2ecf20Sopenharmony_ci
15278c2ecf20Sopenharmony_ci/* Drop FPU & MSA without saving it */
15288c2ecf20Sopenharmony_civoid kvm_drop_fpu(struct kvm_vcpu *vcpu)
15298c2ecf20Sopenharmony_ci{
15308c2ecf20Sopenharmony_ci	preempt_disable();
15318c2ecf20Sopenharmony_ci	if (cpu_has_msa && vcpu->arch.aux_inuse & KVM_MIPS_AUX_MSA) {
15328c2ecf20Sopenharmony_ci		disable_msa();
15338c2ecf20Sopenharmony_ci		trace_kvm_aux(vcpu, KVM_TRACE_AUX_DISCARD, KVM_TRACE_AUX_MSA);
15348c2ecf20Sopenharmony_ci		vcpu->arch.aux_inuse &= ~KVM_MIPS_AUX_MSA;
15358c2ecf20Sopenharmony_ci	}
15368c2ecf20Sopenharmony_ci	if (vcpu->arch.aux_inuse & KVM_MIPS_AUX_FPU) {
15378c2ecf20Sopenharmony_ci		clear_c0_status(ST0_CU1 | ST0_FR);
15388c2ecf20Sopenharmony_ci		trace_kvm_aux(vcpu, KVM_TRACE_AUX_DISCARD, KVM_TRACE_AUX_FPU);
15398c2ecf20Sopenharmony_ci		vcpu->arch.aux_inuse &= ~KVM_MIPS_AUX_FPU;
15408c2ecf20Sopenharmony_ci	}
15418c2ecf20Sopenharmony_ci	preempt_enable();
15428c2ecf20Sopenharmony_ci}
15438c2ecf20Sopenharmony_ci
15448c2ecf20Sopenharmony_ci/* Save and disable FPU & MSA */
15458c2ecf20Sopenharmony_civoid kvm_lose_fpu(struct kvm_vcpu *vcpu)
15468c2ecf20Sopenharmony_ci{
15478c2ecf20Sopenharmony_ci	/*
15488c2ecf20Sopenharmony_ci	 * With T&E, FPU & MSA get disabled in root context (hardware) when it
15498c2ecf20Sopenharmony_ci	 * is disabled in guest context (software), but the register state in
15508c2ecf20Sopenharmony_ci	 * the hardware may still be in use.
15518c2ecf20Sopenharmony_ci	 * This is why we explicitly re-enable the hardware before saving.
15528c2ecf20Sopenharmony_ci	 */
15538c2ecf20Sopenharmony_ci
15548c2ecf20Sopenharmony_ci	preempt_disable();
15558c2ecf20Sopenharmony_ci	if (cpu_has_msa && vcpu->arch.aux_inuse & KVM_MIPS_AUX_MSA) {
15568c2ecf20Sopenharmony_ci		if (!IS_ENABLED(CONFIG_KVM_MIPS_VZ)) {
15578c2ecf20Sopenharmony_ci			set_c0_config5(MIPS_CONF5_MSAEN);
15588c2ecf20Sopenharmony_ci			enable_fpu_hazard();
15598c2ecf20Sopenharmony_ci		}
15608c2ecf20Sopenharmony_ci
15618c2ecf20Sopenharmony_ci		__kvm_save_msa(&vcpu->arch);
15628c2ecf20Sopenharmony_ci		trace_kvm_aux(vcpu, KVM_TRACE_AUX_SAVE, KVM_TRACE_AUX_FPU_MSA);
15638c2ecf20Sopenharmony_ci
15648c2ecf20Sopenharmony_ci		/* Disable MSA & FPU */
15658c2ecf20Sopenharmony_ci		disable_msa();
15668c2ecf20Sopenharmony_ci		if (vcpu->arch.aux_inuse & KVM_MIPS_AUX_FPU) {
15678c2ecf20Sopenharmony_ci			clear_c0_status(ST0_CU1 | ST0_FR);
15688c2ecf20Sopenharmony_ci			disable_fpu_hazard();
15698c2ecf20Sopenharmony_ci		}
15708c2ecf20Sopenharmony_ci		vcpu->arch.aux_inuse &= ~(KVM_MIPS_AUX_FPU | KVM_MIPS_AUX_MSA);
15718c2ecf20Sopenharmony_ci	} else if (vcpu->arch.aux_inuse & KVM_MIPS_AUX_FPU) {
15728c2ecf20Sopenharmony_ci		if (!IS_ENABLED(CONFIG_KVM_MIPS_VZ)) {
15738c2ecf20Sopenharmony_ci			set_c0_status(ST0_CU1);
15748c2ecf20Sopenharmony_ci			enable_fpu_hazard();
15758c2ecf20Sopenharmony_ci		}
15768c2ecf20Sopenharmony_ci
15778c2ecf20Sopenharmony_ci		__kvm_save_fpu(&vcpu->arch);
15788c2ecf20Sopenharmony_ci		vcpu->arch.aux_inuse &= ~KVM_MIPS_AUX_FPU;
15798c2ecf20Sopenharmony_ci		trace_kvm_aux(vcpu, KVM_TRACE_AUX_SAVE, KVM_TRACE_AUX_FPU);
15808c2ecf20Sopenharmony_ci
15818c2ecf20Sopenharmony_ci		/* Disable FPU */
15828c2ecf20Sopenharmony_ci		clear_c0_status(ST0_CU1 | ST0_FR);
15838c2ecf20Sopenharmony_ci		disable_fpu_hazard();
15848c2ecf20Sopenharmony_ci	}
15858c2ecf20Sopenharmony_ci	preempt_enable();
15868c2ecf20Sopenharmony_ci}
15878c2ecf20Sopenharmony_ci
15888c2ecf20Sopenharmony_ci/*
15898c2ecf20Sopenharmony_ci * Step over a specific ctc1 to FCSR and a specific ctcmsa to MSACSR which are
15908c2ecf20Sopenharmony_ci * used to restore guest FCSR/MSACSR state and may trigger a "harmless" FP/MSAFP
15918c2ecf20Sopenharmony_ci * exception if cause bits are set in the value being written.
15928c2ecf20Sopenharmony_ci */
15938c2ecf20Sopenharmony_cistatic int kvm_mips_csr_die_notify(struct notifier_block *self,
15948c2ecf20Sopenharmony_ci				   unsigned long cmd, void *ptr)
15958c2ecf20Sopenharmony_ci{
15968c2ecf20Sopenharmony_ci	struct die_args *args = (struct die_args *)ptr;
15978c2ecf20Sopenharmony_ci	struct pt_regs *regs = args->regs;
15988c2ecf20Sopenharmony_ci	unsigned long pc;
15998c2ecf20Sopenharmony_ci
16008c2ecf20Sopenharmony_ci	/* Only interested in FPE and MSAFPE */
16018c2ecf20Sopenharmony_ci	if (cmd != DIE_FP && cmd != DIE_MSAFP)
16028c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
16038c2ecf20Sopenharmony_ci
16048c2ecf20Sopenharmony_ci	/* Return immediately if guest context isn't active */
16058c2ecf20Sopenharmony_ci	if (!(current->flags & PF_VCPU))
16068c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
16078c2ecf20Sopenharmony_ci
16088c2ecf20Sopenharmony_ci	/* Should never get here from user mode */
16098c2ecf20Sopenharmony_ci	BUG_ON(user_mode(regs));
16108c2ecf20Sopenharmony_ci
16118c2ecf20Sopenharmony_ci	pc = instruction_pointer(regs);
16128c2ecf20Sopenharmony_ci	switch (cmd) {
16138c2ecf20Sopenharmony_ci	case DIE_FP:
16148c2ecf20Sopenharmony_ci		/* match 2nd instruction in __kvm_restore_fcsr */
16158c2ecf20Sopenharmony_ci		if (pc != (unsigned long)&__kvm_restore_fcsr + 4)
16168c2ecf20Sopenharmony_ci			return NOTIFY_DONE;
16178c2ecf20Sopenharmony_ci		break;
16188c2ecf20Sopenharmony_ci	case DIE_MSAFP:
16198c2ecf20Sopenharmony_ci		/* match 2nd/3rd instruction in __kvm_restore_msacsr */
16208c2ecf20Sopenharmony_ci		if (!cpu_has_msa ||
16218c2ecf20Sopenharmony_ci		    pc < (unsigned long)&__kvm_restore_msacsr + 4 ||
16228c2ecf20Sopenharmony_ci		    pc > (unsigned long)&__kvm_restore_msacsr + 8)
16238c2ecf20Sopenharmony_ci			return NOTIFY_DONE;
16248c2ecf20Sopenharmony_ci		break;
16258c2ecf20Sopenharmony_ci	}
16268c2ecf20Sopenharmony_ci
16278c2ecf20Sopenharmony_ci	/* Move PC forward a little and continue executing */
16288c2ecf20Sopenharmony_ci	instruction_pointer(regs) += 4;
16298c2ecf20Sopenharmony_ci
16308c2ecf20Sopenharmony_ci	return NOTIFY_STOP;
16318c2ecf20Sopenharmony_ci}
16328c2ecf20Sopenharmony_ci
16338c2ecf20Sopenharmony_cistatic struct notifier_block kvm_mips_csr_die_notifier = {
16348c2ecf20Sopenharmony_ci	.notifier_call = kvm_mips_csr_die_notify,
16358c2ecf20Sopenharmony_ci};
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_cistatic u32 kvm_default_priority_to_irq[MIPS_EXC_MAX] = {
16388c2ecf20Sopenharmony_ci	[MIPS_EXC_INT_TIMER] = C_IRQ5,
16398c2ecf20Sopenharmony_ci	[MIPS_EXC_INT_IO_1]  = C_IRQ0,
16408c2ecf20Sopenharmony_ci	[MIPS_EXC_INT_IPI_1] = C_IRQ1,
16418c2ecf20Sopenharmony_ci	[MIPS_EXC_INT_IPI_2] = C_IRQ2,
16428c2ecf20Sopenharmony_ci};
16438c2ecf20Sopenharmony_ci
16448c2ecf20Sopenharmony_cistatic u32 kvm_loongson3_priority_to_irq[MIPS_EXC_MAX] = {
16458c2ecf20Sopenharmony_ci	[MIPS_EXC_INT_TIMER] = C_IRQ5,
16468c2ecf20Sopenharmony_ci	[MIPS_EXC_INT_IO_1]  = C_IRQ0,
16478c2ecf20Sopenharmony_ci	[MIPS_EXC_INT_IO_2]  = C_IRQ1,
16488c2ecf20Sopenharmony_ci	[MIPS_EXC_INT_IPI_1] = C_IRQ4,
16498c2ecf20Sopenharmony_ci};
16508c2ecf20Sopenharmony_ci
16518c2ecf20Sopenharmony_ciu32 *kvm_priority_to_irq = kvm_default_priority_to_irq;
16528c2ecf20Sopenharmony_ci
16538c2ecf20Sopenharmony_ciu32 kvm_irq_to_priority(u32 irq)
16548c2ecf20Sopenharmony_ci{
16558c2ecf20Sopenharmony_ci	int i;
16568c2ecf20Sopenharmony_ci
16578c2ecf20Sopenharmony_ci	for (i = MIPS_EXC_INT_TIMER; i < MIPS_EXC_MAX; i++) {
16588c2ecf20Sopenharmony_ci		if (kvm_priority_to_irq[i] == (1 << (irq + 8)))
16598c2ecf20Sopenharmony_ci			return i;
16608c2ecf20Sopenharmony_ci	}
16618c2ecf20Sopenharmony_ci
16628c2ecf20Sopenharmony_ci	return MIPS_EXC_MAX;
16638c2ecf20Sopenharmony_ci}
16648c2ecf20Sopenharmony_ci
16658c2ecf20Sopenharmony_cistatic int __init kvm_mips_init(void)
16668c2ecf20Sopenharmony_ci{
16678c2ecf20Sopenharmony_ci	int ret;
16688c2ecf20Sopenharmony_ci
16698c2ecf20Sopenharmony_ci	if (cpu_has_mmid) {
16708c2ecf20Sopenharmony_ci		pr_warn("KVM does not yet support MMIDs. KVM Disabled\n");
16718c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
16728c2ecf20Sopenharmony_ci	}
16738c2ecf20Sopenharmony_ci
16748c2ecf20Sopenharmony_ci	ret = kvm_mips_entry_setup();
16758c2ecf20Sopenharmony_ci	if (ret)
16768c2ecf20Sopenharmony_ci		return ret;
16778c2ecf20Sopenharmony_ci
16788c2ecf20Sopenharmony_ci	ret = kvm_init(NULL, sizeof(struct kvm_vcpu), 0, THIS_MODULE);
16798c2ecf20Sopenharmony_ci
16808c2ecf20Sopenharmony_ci	if (ret)
16818c2ecf20Sopenharmony_ci		return ret;
16828c2ecf20Sopenharmony_ci
16838c2ecf20Sopenharmony_ci	if (boot_cpu_type() == CPU_LOONGSON64)
16848c2ecf20Sopenharmony_ci		kvm_priority_to_irq = kvm_loongson3_priority_to_irq;
16858c2ecf20Sopenharmony_ci
16868c2ecf20Sopenharmony_ci	register_die_notifier(&kvm_mips_csr_die_notifier);
16878c2ecf20Sopenharmony_ci
16888c2ecf20Sopenharmony_ci	return 0;
16898c2ecf20Sopenharmony_ci}
16908c2ecf20Sopenharmony_ci
16918c2ecf20Sopenharmony_cistatic void __exit kvm_mips_exit(void)
16928c2ecf20Sopenharmony_ci{
16938c2ecf20Sopenharmony_ci	kvm_exit();
16948c2ecf20Sopenharmony_ci
16958c2ecf20Sopenharmony_ci	unregister_die_notifier(&kvm_mips_csr_die_notifier);
16968c2ecf20Sopenharmony_ci}
16978c2ecf20Sopenharmony_ci
16988c2ecf20Sopenharmony_cimodule_init(kvm_mips_init);
16998c2ecf20Sopenharmony_cimodule_exit(kvm_mips_exit);
17008c2ecf20Sopenharmony_ci
17018c2ecf20Sopenharmony_ciEXPORT_TRACEPOINT_SYMBOL(kvm_exit);
1702