162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
362306a36Sopenharmony_ci * License.  See the file "COPYING" in the main directory of this archive
462306a36Sopenharmony_ci * for more details.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * KVM/MIPS: MIPS specific KVM APIs
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Copyright (C) 2012  MIPS Technologies, Inc.  All rights reserved.
962306a36Sopenharmony_ci * Authors: Sanjay Lal <sanjayl@kymasys.com>
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/bitops.h>
1362306a36Sopenharmony_ci#include <linux/errno.h>
1462306a36Sopenharmony_ci#include <linux/err.h>
1562306a36Sopenharmony_ci#include <linux/kdebug.h>
1662306a36Sopenharmony_ci#include <linux/module.h>
1762306a36Sopenharmony_ci#include <linux/uaccess.h>
1862306a36Sopenharmony_ci#include <linux/vmalloc.h>
1962306a36Sopenharmony_ci#include <linux/sched/signal.h>
2062306a36Sopenharmony_ci#include <linux/fs.h>
2162306a36Sopenharmony_ci#include <linux/memblock.h>
2262306a36Sopenharmony_ci#include <linux/pgtable.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include <asm/fpu.h>
2562306a36Sopenharmony_ci#include <asm/page.h>
2662306a36Sopenharmony_ci#include <asm/cacheflush.h>
2762306a36Sopenharmony_ci#include <asm/mmu_context.h>
2862306a36Sopenharmony_ci#include <asm/pgalloc.h>
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#include <linux/kvm_host.h>
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#include "interrupt.h"
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#define CREATE_TRACE_POINTS
3562306a36Sopenharmony_ci#include "trace.h"
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#ifndef VECTORSPACING
3862306a36Sopenharmony_ci#define VECTORSPACING 0x100	/* for EI/VI mode */
3962306a36Sopenharmony_ci#endif
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ciconst struct _kvm_stats_desc kvm_vm_stats_desc[] = {
4262306a36Sopenharmony_ci	KVM_GENERIC_VM_STATS()
4362306a36Sopenharmony_ci};
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ciconst struct kvm_stats_header kvm_vm_stats_header = {
4662306a36Sopenharmony_ci	.name_size = KVM_STATS_NAME_SIZE,
4762306a36Sopenharmony_ci	.num_desc = ARRAY_SIZE(kvm_vm_stats_desc),
4862306a36Sopenharmony_ci	.id_offset = sizeof(struct kvm_stats_header),
4962306a36Sopenharmony_ci	.desc_offset = sizeof(struct kvm_stats_header) + KVM_STATS_NAME_SIZE,
5062306a36Sopenharmony_ci	.data_offset = sizeof(struct kvm_stats_header) + KVM_STATS_NAME_SIZE +
5162306a36Sopenharmony_ci		       sizeof(kvm_vm_stats_desc),
5262306a36Sopenharmony_ci};
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ciconst struct _kvm_stats_desc kvm_vcpu_stats_desc[] = {
5562306a36Sopenharmony_ci	KVM_GENERIC_VCPU_STATS(),
5662306a36Sopenharmony_ci	STATS_DESC_COUNTER(VCPU, wait_exits),
5762306a36Sopenharmony_ci	STATS_DESC_COUNTER(VCPU, cache_exits),
5862306a36Sopenharmony_ci	STATS_DESC_COUNTER(VCPU, signal_exits),
5962306a36Sopenharmony_ci	STATS_DESC_COUNTER(VCPU, int_exits),
6062306a36Sopenharmony_ci	STATS_DESC_COUNTER(VCPU, cop_unusable_exits),
6162306a36Sopenharmony_ci	STATS_DESC_COUNTER(VCPU, tlbmod_exits),
6262306a36Sopenharmony_ci	STATS_DESC_COUNTER(VCPU, tlbmiss_ld_exits),
6362306a36Sopenharmony_ci	STATS_DESC_COUNTER(VCPU, tlbmiss_st_exits),
6462306a36Sopenharmony_ci	STATS_DESC_COUNTER(VCPU, addrerr_st_exits),
6562306a36Sopenharmony_ci	STATS_DESC_COUNTER(VCPU, addrerr_ld_exits),
6662306a36Sopenharmony_ci	STATS_DESC_COUNTER(VCPU, syscall_exits),
6762306a36Sopenharmony_ci	STATS_DESC_COUNTER(VCPU, resvd_inst_exits),
6862306a36Sopenharmony_ci	STATS_DESC_COUNTER(VCPU, break_inst_exits),
6962306a36Sopenharmony_ci	STATS_DESC_COUNTER(VCPU, trap_inst_exits),
7062306a36Sopenharmony_ci	STATS_DESC_COUNTER(VCPU, msa_fpe_exits),
7162306a36Sopenharmony_ci	STATS_DESC_COUNTER(VCPU, fpe_exits),
7262306a36Sopenharmony_ci	STATS_DESC_COUNTER(VCPU, msa_disabled_exits),
7362306a36Sopenharmony_ci	STATS_DESC_COUNTER(VCPU, flush_dcache_exits),
7462306a36Sopenharmony_ci	STATS_DESC_COUNTER(VCPU, vz_gpsi_exits),
7562306a36Sopenharmony_ci	STATS_DESC_COUNTER(VCPU, vz_gsfc_exits),
7662306a36Sopenharmony_ci	STATS_DESC_COUNTER(VCPU, vz_hc_exits),
7762306a36Sopenharmony_ci	STATS_DESC_COUNTER(VCPU, vz_grr_exits),
7862306a36Sopenharmony_ci	STATS_DESC_COUNTER(VCPU, vz_gva_exits),
7962306a36Sopenharmony_ci	STATS_DESC_COUNTER(VCPU, vz_ghfc_exits),
8062306a36Sopenharmony_ci	STATS_DESC_COUNTER(VCPU, vz_gpa_exits),
8162306a36Sopenharmony_ci	STATS_DESC_COUNTER(VCPU, vz_resvd_exits),
8262306a36Sopenharmony_ci#ifdef CONFIG_CPU_LOONGSON64
8362306a36Sopenharmony_ci	STATS_DESC_COUNTER(VCPU, vz_cpucfg_exits),
8462306a36Sopenharmony_ci#endif
8562306a36Sopenharmony_ci};
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ciconst struct kvm_stats_header kvm_vcpu_stats_header = {
8862306a36Sopenharmony_ci	.name_size = KVM_STATS_NAME_SIZE,
8962306a36Sopenharmony_ci	.num_desc = ARRAY_SIZE(kvm_vcpu_stats_desc),
9062306a36Sopenharmony_ci	.id_offset = sizeof(struct kvm_stats_header),
9162306a36Sopenharmony_ci	.desc_offset = sizeof(struct kvm_stats_header) + KVM_STATS_NAME_SIZE,
9262306a36Sopenharmony_ci	.data_offset = sizeof(struct kvm_stats_header) + KVM_STATS_NAME_SIZE +
9362306a36Sopenharmony_ci		       sizeof(kvm_vcpu_stats_desc),
9462306a36Sopenharmony_ci};
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cibool kvm_trace_guest_mode_change;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ciint kvm_guest_mode_change_trace_reg(void)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	kvm_trace_guest_mode_change = true;
10162306a36Sopenharmony_ci	return 0;
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_civoid kvm_guest_mode_change_trace_unreg(void)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	kvm_trace_guest_mode_change = false;
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci/*
11062306a36Sopenharmony_ci * XXXKYMA: We are simulatoring a processor that has the WII bit set in
11162306a36Sopenharmony_ci * Config7, so we are "runnable" if interrupts are pending
11262306a36Sopenharmony_ci */
11362306a36Sopenharmony_ciint kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	return !!(vcpu->arch.pending_exceptions);
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cibool kvm_arch_vcpu_in_kernel(struct kvm_vcpu *vcpu)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	return false;
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ciint kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	return 1;
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ciint kvm_arch_hardware_enable(void)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	return kvm_mips_callbacks->hardware_enable();
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_civoid kvm_arch_hardware_disable(void)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	kvm_mips_callbacks->hardware_disable();
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ciextern void kvm_init_loongson_ipi(struct kvm *kvm);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ciint kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	switch (type) {
14362306a36Sopenharmony_ci	case KVM_VM_MIPS_AUTO:
14462306a36Sopenharmony_ci		break;
14562306a36Sopenharmony_ci	case KVM_VM_MIPS_VZ:
14662306a36Sopenharmony_ci		break;
14762306a36Sopenharmony_ci	default:
14862306a36Sopenharmony_ci		/* Unsupported KVM type */
14962306a36Sopenharmony_ci		return -EINVAL;
15062306a36Sopenharmony_ci	}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	/* Allocate page table to map GPA -> RPA */
15362306a36Sopenharmony_ci	kvm->arch.gpa_mm.pgd = kvm_pgd_alloc();
15462306a36Sopenharmony_ci	if (!kvm->arch.gpa_mm.pgd)
15562306a36Sopenharmony_ci		return -ENOMEM;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci#ifdef CONFIG_CPU_LOONGSON64
15862306a36Sopenharmony_ci	kvm_init_loongson_ipi(kvm);
15962306a36Sopenharmony_ci#endif
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	return 0;
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic void kvm_mips_free_gpa_pt(struct kvm *kvm)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	/* It should always be safe to remove after flushing the whole range */
16762306a36Sopenharmony_ci	WARN_ON(!kvm_mips_flush_gpa_pt(kvm, 0, ~0));
16862306a36Sopenharmony_ci	pgd_free(NULL, kvm->arch.gpa_mm.pgd);
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_civoid kvm_arch_destroy_vm(struct kvm *kvm)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	kvm_destroy_vcpus(kvm);
17462306a36Sopenharmony_ci	kvm_mips_free_gpa_pt(kvm);
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cilong kvm_arch_dev_ioctl(struct file *filp, unsigned int ioctl,
17862306a36Sopenharmony_ci			unsigned long arg)
17962306a36Sopenharmony_ci{
18062306a36Sopenharmony_ci	return -ENOIOCTLCMD;
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_civoid kvm_arch_flush_shadow_all(struct kvm *kvm)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	/* Flush whole GPA */
18662306a36Sopenharmony_ci	kvm_mips_flush_gpa_pt(kvm, 0, ~0);
18762306a36Sopenharmony_ci	kvm_flush_remote_tlbs(kvm);
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_civoid kvm_arch_flush_shadow_memslot(struct kvm *kvm,
19162306a36Sopenharmony_ci				   struct kvm_memory_slot *slot)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	/*
19462306a36Sopenharmony_ci	 * The slot has been made invalid (ready for moving or deletion), so we
19562306a36Sopenharmony_ci	 * need to ensure that it can no longer be accessed by any guest VCPUs.
19662306a36Sopenharmony_ci	 */
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	spin_lock(&kvm->mmu_lock);
19962306a36Sopenharmony_ci	/* Flush slot from GPA */
20062306a36Sopenharmony_ci	kvm_mips_flush_gpa_pt(kvm, slot->base_gfn,
20162306a36Sopenharmony_ci			      slot->base_gfn + slot->npages - 1);
20262306a36Sopenharmony_ci	kvm_flush_remote_tlbs_memslot(kvm, slot);
20362306a36Sopenharmony_ci	spin_unlock(&kvm->mmu_lock);
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ciint kvm_arch_prepare_memory_region(struct kvm *kvm,
20762306a36Sopenharmony_ci				   const struct kvm_memory_slot *old,
20862306a36Sopenharmony_ci				   struct kvm_memory_slot *new,
20962306a36Sopenharmony_ci				   enum kvm_mr_change change)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	return 0;
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_civoid kvm_arch_commit_memory_region(struct kvm *kvm,
21562306a36Sopenharmony_ci				   struct kvm_memory_slot *old,
21662306a36Sopenharmony_ci				   const struct kvm_memory_slot *new,
21762306a36Sopenharmony_ci				   enum kvm_mr_change change)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	int needs_flush;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	/*
22262306a36Sopenharmony_ci	 * If dirty page logging is enabled, write protect all pages in the slot
22362306a36Sopenharmony_ci	 * ready for dirty logging.
22462306a36Sopenharmony_ci	 *
22562306a36Sopenharmony_ci	 * There is no need to do this in any of the following cases:
22662306a36Sopenharmony_ci	 * CREATE:	No dirty mappings will already exist.
22762306a36Sopenharmony_ci	 * MOVE/DELETE:	The old mappings will already have been cleaned up by
22862306a36Sopenharmony_ci	 *		kvm_arch_flush_shadow_memslot()
22962306a36Sopenharmony_ci	 */
23062306a36Sopenharmony_ci	if (change == KVM_MR_FLAGS_ONLY &&
23162306a36Sopenharmony_ci	    (!(old->flags & KVM_MEM_LOG_DIRTY_PAGES) &&
23262306a36Sopenharmony_ci	     new->flags & KVM_MEM_LOG_DIRTY_PAGES)) {
23362306a36Sopenharmony_ci		spin_lock(&kvm->mmu_lock);
23462306a36Sopenharmony_ci		/* Write protect GPA page table entries */
23562306a36Sopenharmony_ci		needs_flush = kvm_mips_mkclean_gpa_pt(kvm, new->base_gfn,
23662306a36Sopenharmony_ci					new->base_gfn + new->npages - 1);
23762306a36Sopenharmony_ci		if (needs_flush)
23862306a36Sopenharmony_ci			kvm_flush_remote_tlbs_memslot(kvm, new);
23962306a36Sopenharmony_ci		spin_unlock(&kvm->mmu_lock);
24062306a36Sopenharmony_ci	}
24162306a36Sopenharmony_ci}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_cistatic inline void dump_handler(const char *symbol, void *start, void *end)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	u32 *p;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	pr_debug("LEAF(%s)\n", symbol);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	pr_debug("\t.set push\n");
25062306a36Sopenharmony_ci	pr_debug("\t.set noreorder\n");
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	for (p = start; p < (u32 *)end; ++p)
25362306a36Sopenharmony_ci		pr_debug("\t.word\t0x%08x\t\t# %p\n", *p, p);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	pr_debug("\t.set\tpop\n");
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	pr_debug("\tEND(%s)\n", symbol);
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci/* low level hrtimer wake routine */
26162306a36Sopenharmony_cistatic enum hrtimer_restart kvm_mips_comparecount_wakeup(struct hrtimer *timer)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	struct kvm_vcpu *vcpu;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	vcpu = container_of(timer, struct kvm_vcpu, arch.comparecount_timer);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	kvm_mips_callbacks->queue_timer_int(vcpu);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	vcpu->arch.wait = 0;
27062306a36Sopenharmony_ci	rcuwait_wake_up(&vcpu->wait);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	return kvm_mips_count_timeout(vcpu);
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ciint kvm_arch_vcpu_precreate(struct kvm *kvm, unsigned int id)
27662306a36Sopenharmony_ci{
27762306a36Sopenharmony_ci	return 0;
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ciint kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	int err, size;
28362306a36Sopenharmony_ci	void *gebase, *p, *handler, *refill_start, *refill_end;
28462306a36Sopenharmony_ci	int i;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	kvm_debug("kvm @ %p: create cpu %d at %p\n",
28762306a36Sopenharmony_ci		  vcpu->kvm, vcpu->vcpu_id, vcpu);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	err = kvm_mips_callbacks->vcpu_init(vcpu);
29062306a36Sopenharmony_ci	if (err)
29162306a36Sopenharmony_ci		return err;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	hrtimer_init(&vcpu->arch.comparecount_timer, CLOCK_MONOTONIC,
29462306a36Sopenharmony_ci		     HRTIMER_MODE_REL);
29562306a36Sopenharmony_ci	vcpu->arch.comparecount_timer.function = kvm_mips_comparecount_wakeup;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	/*
29862306a36Sopenharmony_ci	 * Allocate space for host mode exception handlers that handle
29962306a36Sopenharmony_ci	 * guest mode exits
30062306a36Sopenharmony_ci	 */
30162306a36Sopenharmony_ci	if (cpu_has_veic || cpu_has_vint)
30262306a36Sopenharmony_ci		size = 0x200 + VECTORSPACING * 64;
30362306a36Sopenharmony_ci	else
30462306a36Sopenharmony_ci		size = 0x4000;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	gebase = kzalloc(ALIGN(size, PAGE_SIZE), GFP_KERNEL);
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	if (!gebase) {
30962306a36Sopenharmony_ci		err = -ENOMEM;
31062306a36Sopenharmony_ci		goto out_uninit_vcpu;
31162306a36Sopenharmony_ci	}
31262306a36Sopenharmony_ci	kvm_debug("Allocated %d bytes for KVM Exception Handlers @ %p\n",
31362306a36Sopenharmony_ci		  ALIGN(size, PAGE_SIZE), gebase);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	/*
31662306a36Sopenharmony_ci	 * Check new ebase actually fits in CP0_EBase. The lack of a write gate
31762306a36Sopenharmony_ci	 * limits us to the low 512MB of physical address space. If the memory
31862306a36Sopenharmony_ci	 * we allocate is out of range, just give up now.
31962306a36Sopenharmony_ci	 */
32062306a36Sopenharmony_ci	if (!cpu_has_ebase_wg && virt_to_phys(gebase) >= 0x20000000) {
32162306a36Sopenharmony_ci		kvm_err("CP0_EBase.WG required for guest exception base %pK\n",
32262306a36Sopenharmony_ci			gebase);
32362306a36Sopenharmony_ci		err = -ENOMEM;
32462306a36Sopenharmony_ci		goto out_free_gebase;
32562306a36Sopenharmony_ci	}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	/* Save new ebase */
32862306a36Sopenharmony_ci	vcpu->arch.guest_ebase = gebase;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	/* Build guest exception vectors dynamically in unmapped memory */
33162306a36Sopenharmony_ci	handler = gebase + 0x2000;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	/* TLB refill (or XTLB refill on 64-bit VZ where KX=1) */
33462306a36Sopenharmony_ci	refill_start = gebase;
33562306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_64BIT))
33662306a36Sopenharmony_ci		refill_start += 0x080;
33762306a36Sopenharmony_ci	refill_end = kvm_mips_build_tlb_refill_exception(refill_start, handler);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	/* General Exception Entry point */
34062306a36Sopenharmony_ci	kvm_mips_build_exception(gebase + 0x180, handler);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	/* For vectored interrupts poke the exception code @ all offsets 0-7 */
34362306a36Sopenharmony_ci	for (i = 0; i < 8; i++) {
34462306a36Sopenharmony_ci		kvm_debug("L1 Vectored handler @ %p\n",
34562306a36Sopenharmony_ci			  gebase + 0x200 + (i * VECTORSPACING));
34662306a36Sopenharmony_ci		kvm_mips_build_exception(gebase + 0x200 + i * VECTORSPACING,
34762306a36Sopenharmony_ci					 handler);
34862306a36Sopenharmony_ci	}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	/* General exit handler */
35162306a36Sopenharmony_ci	p = handler;
35262306a36Sopenharmony_ci	p = kvm_mips_build_exit(p);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	/* Guest entry routine */
35562306a36Sopenharmony_ci	vcpu->arch.vcpu_run = p;
35662306a36Sopenharmony_ci	p = kvm_mips_build_vcpu_run(p);
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	/* Dump the generated code */
35962306a36Sopenharmony_ci	pr_debug("#include <asm/asm.h>\n");
36062306a36Sopenharmony_ci	pr_debug("#include <asm/regdef.h>\n");
36162306a36Sopenharmony_ci	pr_debug("\n");
36262306a36Sopenharmony_ci	dump_handler("kvm_vcpu_run", vcpu->arch.vcpu_run, p);
36362306a36Sopenharmony_ci	dump_handler("kvm_tlb_refill", refill_start, refill_end);
36462306a36Sopenharmony_ci	dump_handler("kvm_gen_exc", gebase + 0x180, gebase + 0x200);
36562306a36Sopenharmony_ci	dump_handler("kvm_exit", gebase + 0x2000, vcpu->arch.vcpu_run);
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	/* Invalidate the icache for these ranges */
36862306a36Sopenharmony_ci	flush_icache_range((unsigned long)gebase,
36962306a36Sopenharmony_ci			   (unsigned long)gebase + ALIGN(size, PAGE_SIZE));
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	/* Init */
37262306a36Sopenharmony_ci	vcpu->arch.last_sched_cpu = -1;
37362306a36Sopenharmony_ci	vcpu->arch.last_exec_cpu = -1;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	/* Initial guest state */
37662306a36Sopenharmony_ci	err = kvm_mips_callbacks->vcpu_setup(vcpu);
37762306a36Sopenharmony_ci	if (err)
37862306a36Sopenharmony_ci		goto out_free_gebase;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	return 0;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ciout_free_gebase:
38362306a36Sopenharmony_ci	kfree(gebase);
38462306a36Sopenharmony_ciout_uninit_vcpu:
38562306a36Sopenharmony_ci	kvm_mips_callbacks->vcpu_uninit(vcpu);
38662306a36Sopenharmony_ci	return err;
38762306a36Sopenharmony_ci}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_civoid kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
39062306a36Sopenharmony_ci{
39162306a36Sopenharmony_ci	hrtimer_cancel(&vcpu->arch.comparecount_timer);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	kvm_mips_dump_stats(vcpu);
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	kvm_mmu_free_memory_caches(vcpu);
39662306a36Sopenharmony_ci	kfree(vcpu->arch.guest_ebase);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	kvm_mips_callbacks->vcpu_uninit(vcpu);
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ciint kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu,
40262306a36Sopenharmony_ci					struct kvm_guest_debug *dbg)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	return -ENOIOCTLCMD;
40562306a36Sopenharmony_ci}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci/*
40862306a36Sopenharmony_ci * Actually run the vCPU, entering an RCU extended quiescent state (EQS) while
40962306a36Sopenharmony_ci * the vCPU is running.
41062306a36Sopenharmony_ci *
41162306a36Sopenharmony_ci * This must be noinstr as instrumentation may make use of RCU, and this is not
41262306a36Sopenharmony_ci * safe during the EQS.
41362306a36Sopenharmony_ci */
41462306a36Sopenharmony_cistatic int noinstr kvm_mips_vcpu_enter_exit(struct kvm_vcpu *vcpu)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	int ret;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	guest_state_enter_irqoff();
41962306a36Sopenharmony_ci	ret = kvm_mips_callbacks->vcpu_run(vcpu);
42062306a36Sopenharmony_ci	guest_state_exit_irqoff();
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	return ret;
42362306a36Sopenharmony_ci}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ciint kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
42662306a36Sopenharmony_ci{
42762306a36Sopenharmony_ci	int r = -EINTR;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	vcpu_load(vcpu);
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	kvm_sigset_activate(vcpu);
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	if (vcpu->mmio_needed) {
43462306a36Sopenharmony_ci		if (!vcpu->mmio_is_write)
43562306a36Sopenharmony_ci			kvm_mips_complete_mmio_load(vcpu);
43662306a36Sopenharmony_ci		vcpu->mmio_needed = 0;
43762306a36Sopenharmony_ci	}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	if (vcpu->run->immediate_exit)
44062306a36Sopenharmony_ci		goto out;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	lose_fpu(1);
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	local_irq_disable();
44562306a36Sopenharmony_ci	guest_timing_enter_irqoff();
44662306a36Sopenharmony_ci	trace_kvm_enter(vcpu);
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	/*
44962306a36Sopenharmony_ci	 * Make sure the read of VCPU requests in vcpu_run() callback is not
45062306a36Sopenharmony_ci	 * reordered ahead of the write to vcpu->mode, or we could miss a TLB
45162306a36Sopenharmony_ci	 * flush request while the requester sees the VCPU as outside of guest
45262306a36Sopenharmony_ci	 * mode and not needing an IPI.
45362306a36Sopenharmony_ci	 */
45462306a36Sopenharmony_ci	smp_store_mb(vcpu->mode, IN_GUEST_MODE);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	r = kvm_mips_vcpu_enter_exit(vcpu);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	/*
45962306a36Sopenharmony_ci	 * We must ensure that any pending interrupts are taken before
46062306a36Sopenharmony_ci	 * we exit guest timing so that timer ticks are accounted as
46162306a36Sopenharmony_ci	 * guest time. Transiently unmask interrupts so that any
46262306a36Sopenharmony_ci	 * pending interrupts are taken.
46362306a36Sopenharmony_ci	 *
46462306a36Sopenharmony_ci	 * TODO: is there a barrier which ensures that pending interrupts are
46562306a36Sopenharmony_ci	 * recognised? Currently this just hopes that the CPU takes any pending
46662306a36Sopenharmony_ci	 * interrupts between the enable and disable.
46762306a36Sopenharmony_ci	 */
46862306a36Sopenharmony_ci	local_irq_enable();
46962306a36Sopenharmony_ci	local_irq_disable();
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	trace_kvm_out(vcpu);
47262306a36Sopenharmony_ci	guest_timing_exit_irqoff();
47362306a36Sopenharmony_ci	local_irq_enable();
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ciout:
47662306a36Sopenharmony_ci	kvm_sigset_deactivate(vcpu);
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	vcpu_put(vcpu);
47962306a36Sopenharmony_ci	return r;
48062306a36Sopenharmony_ci}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ciint kvm_vcpu_ioctl_interrupt(struct kvm_vcpu *vcpu,
48362306a36Sopenharmony_ci			     struct kvm_mips_interrupt *irq)
48462306a36Sopenharmony_ci{
48562306a36Sopenharmony_ci	int intr = (int)irq->irq;
48662306a36Sopenharmony_ci	struct kvm_vcpu *dvcpu = NULL;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	if (intr == kvm_priority_to_irq[MIPS_EXC_INT_IPI_1] ||
48962306a36Sopenharmony_ci	    intr == kvm_priority_to_irq[MIPS_EXC_INT_IPI_2] ||
49062306a36Sopenharmony_ci	    intr == (-kvm_priority_to_irq[MIPS_EXC_INT_IPI_1]) ||
49162306a36Sopenharmony_ci	    intr == (-kvm_priority_to_irq[MIPS_EXC_INT_IPI_2]))
49262306a36Sopenharmony_ci		kvm_debug("%s: CPU: %d, INTR: %d\n", __func__, irq->cpu,
49362306a36Sopenharmony_ci			  (int)intr);
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	if (irq->cpu == -1)
49662306a36Sopenharmony_ci		dvcpu = vcpu;
49762306a36Sopenharmony_ci	else
49862306a36Sopenharmony_ci		dvcpu = kvm_get_vcpu(vcpu->kvm, irq->cpu);
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	if (intr == 2 || intr == 3 || intr == 4 || intr == 6) {
50162306a36Sopenharmony_ci		kvm_mips_callbacks->queue_io_int(dvcpu, irq);
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	} else if (intr == -2 || intr == -3 || intr == -4 || intr == -6) {
50462306a36Sopenharmony_ci		kvm_mips_callbacks->dequeue_io_int(dvcpu, irq);
50562306a36Sopenharmony_ci	} else {
50662306a36Sopenharmony_ci		kvm_err("%s: invalid interrupt ioctl (%d:%d)\n", __func__,
50762306a36Sopenharmony_ci			irq->cpu, irq->irq);
50862306a36Sopenharmony_ci		return -EINVAL;
50962306a36Sopenharmony_ci	}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	dvcpu->arch.wait = 0;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	rcuwait_wake_up(&dvcpu->wait);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	return 0;
51662306a36Sopenharmony_ci}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ciint kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu,
51962306a36Sopenharmony_ci				    struct kvm_mp_state *mp_state)
52062306a36Sopenharmony_ci{
52162306a36Sopenharmony_ci	return -ENOIOCTLCMD;
52262306a36Sopenharmony_ci}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ciint kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu,
52562306a36Sopenharmony_ci				    struct kvm_mp_state *mp_state)
52662306a36Sopenharmony_ci{
52762306a36Sopenharmony_ci	return -ENOIOCTLCMD;
52862306a36Sopenharmony_ci}
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_cistatic u64 kvm_mips_get_one_regs[] = {
53162306a36Sopenharmony_ci	KVM_REG_MIPS_R0,
53262306a36Sopenharmony_ci	KVM_REG_MIPS_R1,
53362306a36Sopenharmony_ci	KVM_REG_MIPS_R2,
53462306a36Sopenharmony_ci	KVM_REG_MIPS_R3,
53562306a36Sopenharmony_ci	KVM_REG_MIPS_R4,
53662306a36Sopenharmony_ci	KVM_REG_MIPS_R5,
53762306a36Sopenharmony_ci	KVM_REG_MIPS_R6,
53862306a36Sopenharmony_ci	KVM_REG_MIPS_R7,
53962306a36Sopenharmony_ci	KVM_REG_MIPS_R8,
54062306a36Sopenharmony_ci	KVM_REG_MIPS_R9,
54162306a36Sopenharmony_ci	KVM_REG_MIPS_R10,
54262306a36Sopenharmony_ci	KVM_REG_MIPS_R11,
54362306a36Sopenharmony_ci	KVM_REG_MIPS_R12,
54462306a36Sopenharmony_ci	KVM_REG_MIPS_R13,
54562306a36Sopenharmony_ci	KVM_REG_MIPS_R14,
54662306a36Sopenharmony_ci	KVM_REG_MIPS_R15,
54762306a36Sopenharmony_ci	KVM_REG_MIPS_R16,
54862306a36Sopenharmony_ci	KVM_REG_MIPS_R17,
54962306a36Sopenharmony_ci	KVM_REG_MIPS_R18,
55062306a36Sopenharmony_ci	KVM_REG_MIPS_R19,
55162306a36Sopenharmony_ci	KVM_REG_MIPS_R20,
55262306a36Sopenharmony_ci	KVM_REG_MIPS_R21,
55362306a36Sopenharmony_ci	KVM_REG_MIPS_R22,
55462306a36Sopenharmony_ci	KVM_REG_MIPS_R23,
55562306a36Sopenharmony_ci	KVM_REG_MIPS_R24,
55662306a36Sopenharmony_ci	KVM_REG_MIPS_R25,
55762306a36Sopenharmony_ci	KVM_REG_MIPS_R26,
55862306a36Sopenharmony_ci	KVM_REG_MIPS_R27,
55962306a36Sopenharmony_ci	KVM_REG_MIPS_R28,
56062306a36Sopenharmony_ci	KVM_REG_MIPS_R29,
56162306a36Sopenharmony_ci	KVM_REG_MIPS_R30,
56262306a36Sopenharmony_ci	KVM_REG_MIPS_R31,
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci#ifndef CONFIG_CPU_MIPSR6
56562306a36Sopenharmony_ci	KVM_REG_MIPS_HI,
56662306a36Sopenharmony_ci	KVM_REG_MIPS_LO,
56762306a36Sopenharmony_ci#endif
56862306a36Sopenharmony_ci	KVM_REG_MIPS_PC,
56962306a36Sopenharmony_ci};
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_cistatic u64 kvm_mips_get_one_regs_fpu[] = {
57262306a36Sopenharmony_ci	KVM_REG_MIPS_FCR_IR,
57362306a36Sopenharmony_ci	KVM_REG_MIPS_FCR_CSR,
57462306a36Sopenharmony_ci};
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_cistatic u64 kvm_mips_get_one_regs_msa[] = {
57762306a36Sopenharmony_ci	KVM_REG_MIPS_MSA_IR,
57862306a36Sopenharmony_ci	KVM_REG_MIPS_MSA_CSR,
57962306a36Sopenharmony_ci};
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_cistatic unsigned long kvm_mips_num_regs(struct kvm_vcpu *vcpu)
58262306a36Sopenharmony_ci{
58362306a36Sopenharmony_ci	unsigned long ret;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	ret = ARRAY_SIZE(kvm_mips_get_one_regs);
58662306a36Sopenharmony_ci	if (kvm_mips_guest_can_have_fpu(&vcpu->arch)) {
58762306a36Sopenharmony_ci		ret += ARRAY_SIZE(kvm_mips_get_one_regs_fpu) + 48;
58862306a36Sopenharmony_ci		/* odd doubles */
58962306a36Sopenharmony_ci		if (boot_cpu_data.fpu_id & MIPS_FPIR_F64)
59062306a36Sopenharmony_ci			ret += 16;
59162306a36Sopenharmony_ci	}
59262306a36Sopenharmony_ci	if (kvm_mips_guest_can_have_msa(&vcpu->arch))
59362306a36Sopenharmony_ci		ret += ARRAY_SIZE(kvm_mips_get_one_regs_msa) + 32;
59462306a36Sopenharmony_ci	ret += kvm_mips_callbacks->num_regs(vcpu);
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	return ret;
59762306a36Sopenharmony_ci}
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_cistatic int kvm_mips_copy_reg_indices(struct kvm_vcpu *vcpu, u64 __user *indices)
60062306a36Sopenharmony_ci{
60162306a36Sopenharmony_ci	u64 index;
60262306a36Sopenharmony_ci	unsigned int i;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	if (copy_to_user(indices, kvm_mips_get_one_regs,
60562306a36Sopenharmony_ci			 sizeof(kvm_mips_get_one_regs)))
60662306a36Sopenharmony_ci		return -EFAULT;
60762306a36Sopenharmony_ci	indices += ARRAY_SIZE(kvm_mips_get_one_regs);
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	if (kvm_mips_guest_can_have_fpu(&vcpu->arch)) {
61062306a36Sopenharmony_ci		if (copy_to_user(indices, kvm_mips_get_one_regs_fpu,
61162306a36Sopenharmony_ci				 sizeof(kvm_mips_get_one_regs_fpu)))
61262306a36Sopenharmony_ci			return -EFAULT;
61362306a36Sopenharmony_ci		indices += ARRAY_SIZE(kvm_mips_get_one_regs_fpu);
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci		for (i = 0; i < 32; ++i) {
61662306a36Sopenharmony_ci			index = KVM_REG_MIPS_FPR_32(i);
61762306a36Sopenharmony_ci			if (copy_to_user(indices, &index, sizeof(index)))
61862306a36Sopenharmony_ci				return -EFAULT;
61962306a36Sopenharmony_ci			++indices;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci			/* skip odd doubles if no F64 */
62262306a36Sopenharmony_ci			if (i & 1 && !(boot_cpu_data.fpu_id & MIPS_FPIR_F64))
62362306a36Sopenharmony_ci				continue;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci			index = KVM_REG_MIPS_FPR_64(i);
62662306a36Sopenharmony_ci			if (copy_to_user(indices, &index, sizeof(index)))
62762306a36Sopenharmony_ci				return -EFAULT;
62862306a36Sopenharmony_ci			++indices;
62962306a36Sopenharmony_ci		}
63062306a36Sopenharmony_ci	}
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	if (kvm_mips_guest_can_have_msa(&vcpu->arch)) {
63362306a36Sopenharmony_ci		if (copy_to_user(indices, kvm_mips_get_one_regs_msa,
63462306a36Sopenharmony_ci				 sizeof(kvm_mips_get_one_regs_msa)))
63562306a36Sopenharmony_ci			return -EFAULT;
63662306a36Sopenharmony_ci		indices += ARRAY_SIZE(kvm_mips_get_one_regs_msa);
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci		for (i = 0; i < 32; ++i) {
63962306a36Sopenharmony_ci			index = KVM_REG_MIPS_VEC_128(i);
64062306a36Sopenharmony_ci			if (copy_to_user(indices, &index, sizeof(index)))
64162306a36Sopenharmony_ci				return -EFAULT;
64262306a36Sopenharmony_ci			++indices;
64362306a36Sopenharmony_ci		}
64462306a36Sopenharmony_ci	}
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	return kvm_mips_callbacks->copy_reg_indices(vcpu, indices);
64762306a36Sopenharmony_ci}
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_cistatic int kvm_mips_get_reg(struct kvm_vcpu *vcpu,
65062306a36Sopenharmony_ci			    const struct kvm_one_reg *reg)
65162306a36Sopenharmony_ci{
65262306a36Sopenharmony_ci	struct mips_coproc *cop0 = &vcpu->arch.cop0;
65362306a36Sopenharmony_ci	struct mips_fpu_struct *fpu = &vcpu->arch.fpu;
65462306a36Sopenharmony_ci	int ret;
65562306a36Sopenharmony_ci	s64 v;
65662306a36Sopenharmony_ci	s64 vs[2];
65762306a36Sopenharmony_ci	unsigned int idx;
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	switch (reg->id) {
66062306a36Sopenharmony_ci	/* General purpose registers */
66162306a36Sopenharmony_ci	case KVM_REG_MIPS_R0 ... KVM_REG_MIPS_R31:
66262306a36Sopenharmony_ci		v = (long)vcpu->arch.gprs[reg->id - KVM_REG_MIPS_R0];
66362306a36Sopenharmony_ci		break;
66462306a36Sopenharmony_ci#ifndef CONFIG_CPU_MIPSR6
66562306a36Sopenharmony_ci	case KVM_REG_MIPS_HI:
66662306a36Sopenharmony_ci		v = (long)vcpu->arch.hi;
66762306a36Sopenharmony_ci		break;
66862306a36Sopenharmony_ci	case KVM_REG_MIPS_LO:
66962306a36Sopenharmony_ci		v = (long)vcpu->arch.lo;
67062306a36Sopenharmony_ci		break;
67162306a36Sopenharmony_ci#endif
67262306a36Sopenharmony_ci	case KVM_REG_MIPS_PC:
67362306a36Sopenharmony_ci		v = (long)vcpu->arch.pc;
67462306a36Sopenharmony_ci		break;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	/* Floating point registers */
67762306a36Sopenharmony_ci	case KVM_REG_MIPS_FPR_32(0) ... KVM_REG_MIPS_FPR_32(31):
67862306a36Sopenharmony_ci		if (!kvm_mips_guest_has_fpu(&vcpu->arch))
67962306a36Sopenharmony_ci			return -EINVAL;
68062306a36Sopenharmony_ci		idx = reg->id - KVM_REG_MIPS_FPR_32(0);
68162306a36Sopenharmony_ci		/* Odd singles in top of even double when FR=0 */
68262306a36Sopenharmony_ci		if (kvm_read_c0_guest_status(cop0) & ST0_FR)
68362306a36Sopenharmony_ci			v = get_fpr32(&fpu->fpr[idx], 0);
68462306a36Sopenharmony_ci		else
68562306a36Sopenharmony_ci			v = get_fpr32(&fpu->fpr[idx & ~1], idx & 1);
68662306a36Sopenharmony_ci		break;
68762306a36Sopenharmony_ci	case KVM_REG_MIPS_FPR_64(0) ... KVM_REG_MIPS_FPR_64(31):
68862306a36Sopenharmony_ci		if (!kvm_mips_guest_has_fpu(&vcpu->arch))
68962306a36Sopenharmony_ci			return -EINVAL;
69062306a36Sopenharmony_ci		idx = reg->id - KVM_REG_MIPS_FPR_64(0);
69162306a36Sopenharmony_ci		/* Can't access odd doubles in FR=0 mode */
69262306a36Sopenharmony_ci		if (idx & 1 && !(kvm_read_c0_guest_status(cop0) & ST0_FR))
69362306a36Sopenharmony_ci			return -EINVAL;
69462306a36Sopenharmony_ci		v = get_fpr64(&fpu->fpr[idx], 0);
69562306a36Sopenharmony_ci		break;
69662306a36Sopenharmony_ci	case KVM_REG_MIPS_FCR_IR:
69762306a36Sopenharmony_ci		if (!kvm_mips_guest_has_fpu(&vcpu->arch))
69862306a36Sopenharmony_ci			return -EINVAL;
69962306a36Sopenharmony_ci		v = boot_cpu_data.fpu_id;
70062306a36Sopenharmony_ci		break;
70162306a36Sopenharmony_ci	case KVM_REG_MIPS_FCR_CSR:
70262306a36Sopenharmony_ci		if (!kvm_mips_guest_has_fpu(&vcpu->arch))
70362306a36Sopenharmony_ci			return -EINVAL;
70462306a36Sopenharmony_ci		v = fpu->fcr31;
70562306a36Sopenharmony_ci		break;
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	/* MIPS SIMD Architecture (MSA) registers */
70862306a36Sopenharmony_ci	case KVM_REG_MIPS_VEC_128(0) ... KVM_REG_MIPS_VEC_128(31):
70962306a36Sopenharmony_ci		if (!kvm_mips_guest_has_msa(&vcpu->arch))
71062306a36Sopenharmony_ci			return -EINVAL;
71162306a36Sopenharmony_ci		/* Can't access MSA registers in FR=0 mode */
71262306a36Sopenharmony_ci		if (!(kvm_read_c0_guest_status(cop0) & ST0_FR))
71362306a36Sopenharmony_ci			return -EINVAL;
71462306a36Sopenharmony_ci		idx = reg->id - KVM_REG_MIPS_VEC_128(0);
71562306a36Sopenharmony_ci#ifdef CONFIG_CPU_LITTLE_ENDIAN
71662306a36Sopenharmony_ci		/* least significant byte first */
71762306a36Sopenharmony_ci		vs[0] = get_fpr64(&fpu->fpr[idx], 0);
71862306a36Sopenharmony_ci		vs[1] = get_fpr64(&fpu->fpr[idx], 1);
71962306a36Sopenharmony_ci#else
72062306a36Sopenharmony_ci		/* most significant byte first */
72162306a36Sopenharmony_ci		vs[0] = get_fpr64(&fpu->fpr[idx], 1);
72262306a36Sopenharmony_ci		vs[1] = get_fpr64(&fpu->fpr[idx], 0);
72362306a36Sopenharmony_ci#endif
72462306a36Sopenharmony_ci		break;
72562306a36Sopenharmony_ci	case KVM_REG_MIPS_MSA_IR:
72662306a36Sopenharmony_ci		if (!kvm_mips_guest_has_msa(&vcpu->arch))
72762306a36Sopenharmony_ci			return -EINVAL;
72862306a36Sopenharmony_ci		v = boot_cpu_data.msa_id;
72962306a36Sopenharmony_ci		break;
73062306a36Sopenharmony_ci	case KVM_REG_MIPS_MSA_CSR:
73162306a36Sopenharmony_ci		if (!kvm_mips_guest_has_msa(&vcpu->arch))
73262306a36Sopenharmony_ci			return -EINVAL;
73362306a36Sopenharmony_ci		v = fpu->msacsr;
73462306a36Sopenharmony_ci		break;
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	/* registers to be handled specially */
73762306a36Sopenharmony_ci	default:
73862306a36Sopenharmony_ci		ret = kvm_mips_callbacks->get_one_reg(vcpu, reg, &v);
73962306a36Sopenharmony_ci		if (ret)
74062306a36Sopenharmony_ci			return ret;
74162306a36Sopenharmony_ci		break;
74262306a36Sopenharmony_ci	}
74362306a36Sopenharmony_ci	if ((reg->id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U64) {
74462306a36Sopenharmony_ci		u64 __user *uaddr64 = (u64 __user *)(long)reg->addr;
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci		return put_user(v, uaddr64);
74762306a36Sopenharmony_ci	} else if ((reg->id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U32) {
74862306a36Sopenharmony_ci		u32 __user *uaddr32 = (u32 __user *)(long)reg->addr;
74962306a36Sopenharmony_ci		u32 v32 = (u32)v;
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci		return put_user(v32, uaddr32);
75262306a36Sopenharmony_ci	} else if ((reg->id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U128) {
75362306a36Sopenharmony_ci		void __user *uaddr = (void __user *)(long)reg->addr;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci		return copy_to_user(uaddr, vs, 16) ? -EFAULT : 0;
75662306a36Sopenharmony_ci	} else {
75762306a36Sopenharmony_ci		return -EINVAL;
75862306a36Sopenharmony_ci	}
75962306a36Sopenharmony_ci}
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_cistatic int kvm_mips_set_reg(struct kvm_vcpu *vcpu,
76262306a36Sopenharmony_ci			    const struct kvm_one_reg *reg)
76362306a36Sopenharmony_ci{
76462306a36Sopenharmony_ci	struct mips_coproc *cop0 = &vcpu->arch.cop0;
76562306a36Sopenharmony_ci	struct mips_fpu_struct *fpu = &vcpu->arch.fpu;
76662306a36Sopenharmony_ci	s64 v;
76762306a36Sopenharmony_ci	s64 vs[2];
76862306a36Sopenharmony_ci	unsigned int idx;
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	if ((reg->id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U64) {
77162306a36Sopenharmony_ci		u64 __user *uaddr64 = (u64 __user *)(long)reg->addr;
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci		if (get_user(v, uaddr64) != 0)
77462306a36Sopenharmony_ci			return -EFAULT;
77562306a36Sopenharmony_ci	} else if ((reg->id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U32) {
77662306a36Sopenharmony_ci		u32 __user *uaddr32 = (u32 __user *)(long)reg->addr;
77762306a36Sopenharmony_ci		s32 v32;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci		if (get_user(v32, uaddr32) != 0)
78062306a36Sopenharmony_ci			return -EFAULT;
78162306a36Sopenharmony_ci		v = (s64)v32;
78262306a36Sopenharmony_ci	} else if ((reg->id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U128) {
78362306a36Sopenharmony_ci		void __user *uaddr = (void __user *)(long)reg->addr;
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci		return copy_from_user(vs, uaddr, 16) ? -EFAULT : 0;
78662306a36Sopenharmony_ci	} else {
78762306a36Sopenharmony_ci		return -EINVAL;
78862306a36Sopenharmony_ci	}
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	switch (reg->id) {
79162306a36Sopenharmony_ci	/* General purpose registers */
79262306a36Sopenharmony_ci	case KVM_REG_MIPS_R0:
79362306a36Sopenharmony_ci		/* Silently ignore requests to set $0 */
79462306a36Sopenharmony_ci		break;
79562306a36Sopenharmony_ci	case KVM_REG_MIPS_R1 ... KVM_REG_MIPS_R31:
79662306a36Sopenharmony_ci		vcpu->arch.gprs[reg->id - KVM_REG_MIPS_R0] = v;
79762306a36Sopenharmony_ci		break;
79862306a36Sopenharmony_ci#ifndef CONFIG_CPU_MIPSR6
79962306a36Sopenharmony_ci	case KVM_REG_MIPS_HI:
80062306a36Sopenharmony_ci		vcpu->arch.hi = v;
80162306a36Sopenharmony_ci		break;
80262306a36Sopenharmony_ci	case KVM_REG_MIPS_LO:
80362306a36Sopenharmony_ci		vcpu->arch.lo = v;
80462306a36Sopenharmony_ci		break;
80562306a36Sopenharmony_ci#endif
80662306a36Sopenharmony_ci	case KVM_REG_MIPS_PC:
80762306a36Sopenharmony_ci		vcpu->arch.pc = v;
80862306a36Sopenharmony_ci		break;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	/* Floating point registers */
81162306a36Sopenharmony_ci	case KVM_REG_MIPS_FPR_32(0) ... KVM_REG_MIPS_FPR_32(31):
81262306a36Sopenharmony_ci		if (!kvm_mips_guest_has_fpu(&vcpu->arch))
81362306a36Sopenharmony_ci			return -EINVAL;
81462306a36Sopenharmony_ci		idx = reg->id - KVM_REG_MIPS_FPR_32(0);
81562306a36Sopenharmony_ci		/* Odd singles in top of even double when FR=0 */
81662306a36Sopenharmony_ci		if (kvm_read_c0_guest_status(cop0) & ST0_FR)
81762306a36Sopenharmony_ci			set_fpr32(&fpu->fpr[idx], 0, v);
81862306a36Sopenharmony_ci		else
81962306a36Sopenharmony_ci			set_fpr32(&fpu->fpr[idx & ~1], idx & 1, v);
82062306a36Sopenharmony_ci		break;
82162306a36Sopenharmony_ci	case KVM_REG_MIPS_FPR_64(0) ... KVM_REG_MIPS_FPR_64(31):
82262306a36Sopenharmony_ci		if (!kvm_mips_guest_has_fpu(&vcpu->arch))
82362306a36Sopenharmony_ci			return -EINVAL;
82462306a36Sopenharmony_ci		idx = reg->id - KVM_REG_MIPS_FPR_64(0);
82562306a36Sopenharmony_ci		/* Can't access odd doubles in FR=0 mode */
82662306a36Sopenharmony_ci		if (idx & 1 && !(kvm_read_c0_guest_status(cop0) & ST0_FR))
82762306a36Sopenharmony_ci			return -EINVAL;
82862306a36Sopenharmony_ci		set_fpr64(&fpu->fpr[idx], 0, v);
82962306a36Sopenharmony_ci		break;
83062306a36Sopenharmony_ci	case KVM_REG_MIPS_FCR_IR:
83162306a36Sopenharmony_ci		if (!kvm_mips_guest_has_fpu(&vcpu->arch))
83262306a36Sopenharmony_ci			return -EINVAL;
83362306a36Sopenharmony_ci		/* Read-only */
83462306a36Sopenharmony_ci		break;
83562306a36Sopenharmony_ci	case KVM_REG_MIPS_FCR_CSR:
83662306a36Sopenharmony_ci		if (!kvm_mips_guest_has_fpu(&vcpu->arch))
83762306a36Sopenharmony_ci			return -EINVAL;
83862306a36Sopenharmony_ci		fpu->fcr31 = v;
83962306a36Sopenharmony_ci		break;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	/* MIPS SIMD Architecture (MSA) registers */
84262306a36Sopenharmony_ci	case KVM_REG_MIPS_VEC_128(0) ... KVM_REG_MIPS_VEC_128(31):
84362306a36Sopenharmony_ci		if (!kvm_mips_guest_has_msa(&vcpu->arch))
84462306a36Sopenharmony_ci			return -EINVAL;
84562306a36Sopenharmony_ci		idx = reg->id - KVM_REG_MIPS_VEC_128(0);
84662306a36Sopenharmony_ci#ifdef CONFIG_CPU_LITTLE_ENDIAN
84762306a36Sopenharmony_ci		/* least significant byte first */
84862306a36Sopenharmony_ci		set_fpr64(&fpu->fpr[idx], 0, vs[0]);
84962306a36Sopenharmony_ci		set_fpr64(&fpu->fpr[idx], 1, vs[1]);
85062306a36Sopenharmony_ci#else
85162306a36Sopenharmony_ci		/* most significant byte first */
85262306a36Sopenharmony_ci		set_fpr64(&fpu->fpr[idx], 1, vs[0]);
85362306a36Sopenharmony_ci		set_fpr64(&fpu->fpr[idx], 0, vs[1]);
85462306a36Sopenharmony_ci#endif
85562306a36Sopenharmony_ci		break;
85662306a36Sopenharmony_ci	case KVM_REG_MIPS_MSA_IR:
85762306a36Sopenharmony_ci		if (!kvm_mips_guest_has_msa(&vcpu->arch))
85862306a36Sopenharmony_ci			return -EINVAL;
85962306a36Sopenharmony_ci		/* Read-only */
86062306a36Sopenharmony_ci		break;
86162306a36Sopenharmony_ci	case KVM_REG_MIPS_MSA_CSR:
86262306a36Sopenharmony_ci		if (!kvm_mips_guest_has_msa(&vcpu->arch))
86362306a36Sopenharmony_ci			return -EINVAL;
86462306a36Sopenharmony_ci		fpu->msacsr = v;
86562306a36Sopenharmony_ci		break;
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	/* registers to be handled specially */
86862306a36Sopenharmony_ci	default:
86962306a36Sopenharmony_ci		return kvm_mips_callbacks->set_one_reg(vcpu, reg, v);
87062306a36Sopenharmony_ci	}
87162306a36Sopenharmony_ci	return 0;
87262306a36Sopenharmony_ci}
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_cistatic int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu,
87562306a36Sopenharmony_ci				     struct kvm_enable_cap *cap)
87662306a36Sopenharmony_ci{
87762306a36Sopenharmony_ci	int r = 0;
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	if (!kvm_vm_ioctl_check_extension(vcpu->kvm, cap->cap))
88062306a36Sopenharmony_ci		return -EINVAL;
88162306a36Sopenharmony_ci	if (cap->flags)
88262306a36Sopenharmony_ci		return -EINVAL;
88362306a36Sopenharmony_ci	if (cap->args[0])
88462306a36Sopenharmony_ci		return -EINVAL;
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	switch (cap->cap) {
88762306a36Sopenharmony_ci	case KVM_CAP_MIPS_FPU:
88862306a36Sopenharmony_ci		vcpu->arch.fpu_enabled = true;
88962306a36Sopenharmony_ci		break;
89062306a36Sopenharmony_ci	case KVM_CAP_MIPS_MSA:
89162306a36Sopenharmony_ci		vcpu->arch.msa_enabled = true;
89262306a36Sopenharmony_ci		break;
89362306a36Sopenharmony_ci	default:
89462306a36Sopenharmony_ci		r = -EINVAL;
89562306a36Sopenharmony_ci		break;
89662306a36Sopenharmony_ci	}
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	return r;
89962306a36Sopenharmony_ci}
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_cilong kvm_arch_vcpu_async_ioctl(struct file *filp, unsigned int ioctl,
90262306a36Sopenharmony_ci			       unsigned long arg)
90362306a36Sopenharmony_ci{
90462306a36Sopenharmony_ci	struct kvm_vcpu *vcpu = filp->private_data;
90562306a36Sopenharmony_ci	void __user *argp = (void __user *)arg;
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	if (ioctl == KVM_INTERRUPT) {
90862306a36Sopenharmony_ci		struct kvm_mips_interrupt irq;
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci		if (copy_from_user(&irq, argp, sizeof(irq)))
91162306a36Sopenharmony_ci			return -EFAULT;
91262306a36Sopenharmony_ci		kvm_debug("[%d] %s: irq: %d\n", vcpu->vcpu_id, __func__,
91362306a36Sopenharmony_ci			  irq.irq);
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci		return kvm_vcpu_ioctl_interrupt(vcpu, &irq);
91662306a36Sopenharmony_ci	}
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	return -ENOIOCTLCMD;
91962306a36Sopenharmony_ci}
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_cilong kvm_arch_vcpu_ioctl(struct file *filp, unsigned int ioctl,
92262306a36Sopenharmony_ci			 unsigned long arg)
92362306a36Sopenharmony_ci{
92462306a36Sopenharmony_ci	struct kvm_vcpu *vcpu = filp->private_data;
92562306a36Sopenharmony_ci	void __user *argp = (void __user *)arg;
92662306a36Sopenharmony_ci	long r;
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	vcpu_load(vcpu);
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	switch (ioctl) {
93162306a36Sopenharmony_ci	case KVM_SET_ONE_REG:
93262306a36Sopenharmony_ci	case KVM_GET_ONE_REG: {
93362306a36Sopenharmony_ci		struct kvm_one_reg reg;
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci		r = -EFAULT;
93662306a36Sopenharmony_ci		if (copy_from_user(&reg, argp, sizeof(reg)))
93762306a36Sopenharmony_ci			break;
93862306a36Sopenharmony_ci		if (ioctl == KVM_SET_ONE_REG)
93962306a36Sopenharmony_ci			r = kvm_mips_set_reg(vcpu, &reg);
94062306a36Sopenharmony_ci		else
94162306a36Sopenharmony_ci			r = kvm_mips_get_reg(vcpu, &reg);
94262306a36Sopenharmony_ci		break;
94362306a36Sopenharmony_ci	}
94462306a36Sopenharmony_ci	case KVM_GET_REG_LIST: {
94562306a36Sopenharmony_ci		struct kvm_reg_list __user *user_list = argp;
94662306a36Sopenharmony_ci		struct kvm_reg_list reg_list;
94762306a36Sopenharmony_ci		unsigned n;
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci		r = -EFAULT;
95062306a36Sopenharmony_ci		if (copy_from_user(&reg_list, user_list, sizeof(reg_list)))
95162306a36Sopenharmony_ci			break;
95262306a36Sopenharmony_ci		n = reg_list.n;
95362306a36Sopenharmony_ci		reg_list.n = kvm_mips_num_regs(vcpu);
95462306a36Sopenharmony_ci		if (copy_to_user(user_list, &reg_list, sizeof(reg_list)))
95562306a36Sopenharmony_ci			break;
95662306a36Sopenharmony_ci		r = -E2BIG;
95762306a36Sopenharmony_ci		if (n < reg_list.n)
95862306a36Sopenharmony_ci			break;
95962306a36Sopenharmony_ci		r = kvm_mips_copy_reg_indices(vcpu, user_list->reg);
96062306a36Sopenharmony_ci		break;
96162306a36Sopenharmony_ci	}
96262306a36Sopenharmony_ci	case KVM_ENABLE_CAP: {
96362306a36Sopenharmony_ci		struct kvm_enable_cap cap;
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci		r = -EFAULT;
96662306a36Sopenharmony_ci		if (copy_from_user(&cap, argp, sizeof(cap)))
96762306a36Sopenharmony_ci			break;
96862306a36Sopenharmony_ci		r = kvm_vcpu_ioctl_enable_cap(vcpu, &cap);
96962306a36Sopenharmony_ci		break;
97062306a36Sopenharmony_ci	}
97162306a36Sopenharmony_ci	default:
97262306a36Sopenharmony_ci		r = -ENOIOCTLCMD;
97362306a36Sopenharmony_ci	}
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	vcpu_put(vcpu);
97662306a36Sopenharmony_ci	return r;
97762306a36Sopenharmony_ci}
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_civoid kvm_arch_sync_dirty_log(struct kvm *kvm, struct kvm_memory_slot *memslot)
98062306a36Sopenharmony_ci{
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci}
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ciint kvm_arch_flush_remote_tlbs(struct kvm *kvm)
98562306a36Sopenharmony_ci{
98662306a36Sopenharmony_ci	kvm_mips_callbacks->prepare_flush_shadow(kvm);
98762306a36Sopenharmony_ci	return 1;
98862306a36Sopenharmony_ci}
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ciint kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
99162306a36Sopenharmony_ci{
99262306a36Sopenharmony_ci	int r;
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	switch (ioctl) {
99562306a36Sopenharmony_ci	default:
99662306a36Sopenharmony_ci		r = -ENOIOCTLCMD;
99762306a36Sopenharmony_ci	}
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	return r;
100062306a36Sopenharmony_ci}
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ciint kvm_arch_vcpu_ioctl_get_sregs(struct kvm_vcpu *vcpu,
100362306a36Sopenharmony_ci				  struct kvm_sregs *sregs)
100462306a36Sopenharmony_ci{
100562306a36Sopenharmony_ci	return -ENOIOCTLCMD;
100662306a36Sopenharmony_ci}
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ciint kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu,
100962306a36Sopenharmony_ci				  struct kvm_sregs *sregs)
101062306a36Sopenharmony_ci{
101162306a36Sopenharmony_ci	return -ENOIOCTLCMD;
101262306a36Sopenharmony_ci}
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_civoid kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu)
101562306a36Sopenharmony_ci{
101662306a36Sopenharmony_ci}
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ciint kvm_arch_vcpu_ioctl_get_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu)
101962306a36Sopenharmony_ci{
102062306a36Sopenharmony_ci	return -ENOIOCTLCMD;
102162306a36Sopenharmony_ci}
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ciint kvm_arch_vcpu_ioctl_set_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu)
102462306a36Sopenharmony_ci{
102562306a36Sopenharmony_ci	return -ENOIOCTLCMD;
102662306a36Sopenharmony_ci}
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_civm_fault_t kvm_arch_vcpu_fault(struct kvm_vcpu *vcpu, struct vm_fault *vmf)
102962306a36Sopenharmony_ci{
103062306a36Sopenharmony_ci	return VM_FAULT_SIGBUS;
103162306a36Sopenharmony_ci}
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ciint kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
103462306a36Sopenharmony_ci{
103562306a36Sopenharmony_ci	int r;
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	switch (ext) {
103862306a36Sopenharmony_ci	case KVM_CAP_ONE_REG:
103962306a36Sopenharmony_ci	case KVM_CAP_ENABLE_CAP:
104062306a36Sopenharmony_ci	case KVM_CAP_READONLY_MEM:
104162306a36Sopenharmony_ci	case KVM_CAP_SYNC_MMU:
104262306a36Sopenharmony_ci	case KVM_CAP_IMMEDIATE_EXIT:
104362306a36Sopenharmony_ci		r = 1;
104462306a36Sopenharmony_ci		break;
104562306a36Sopenharmony_ci	case KVM_CAP_NR_VCPUS:
104662306a36Sopenharmony_ci		r = min_t(unsigned int, num_online_cpus(), KVM_MAX_VCPUS);
104762306a36Sopenharmony_ci		break;
104862306a36Sopenharmony_ci	case KVM_CAP_MAX_VCPUS:
104962306a36Sopenharmony_ci		r = KVM_MAX_VCPUS;
105062306a36Sopenharmony_ci		break;
105162306a36Sopenharmony_ci	case KVM_CAP_MAX_VCPU_ID:
105262306a36Sopenharmony_ci		r = KVM_MAX_VCPU_IDS;
105362306a36Sopenharmony_ci		break;
105462306a36Sopenharmony_ci	case KVM_CAP_MIPS_FPU:
105562306a36Sopenharmony_ci		/* We don't handle systems with inconsistent cpu_has_fpu */
105662306a36Sopenharmony_ci		r = !!raw_cpu_has_fpu;
105762306a36Sopenharmony_ci		break;
105862306a36Sopenharmony_ci	case KVM_CAP_MIPS_MSA:
105962306a36Sopenharmony_ci		/*
106062306a36Sopenharmony_ci		 * We don't support MSA vector partitioning yet:
106162306a36Sopenharmony_ci		 * 1) It would require explicit support which can't be tested
106262306a36Sopenharmony_ci		 *    yet due to lack of support in current hardware.
106362306a36Sopenharmony_ci		 * 2) It extends the state that would need to be saved/restored
106462306a36Sopenharmony_ci		 *    by e.g. QEMU for migration.
106562306a36Sopenharmony_ci		 *
106662306a36Sopenharmony_ci		 * When vector partitioning hardware becomes available, support
106762306a36Sopenharmony_ci		 * could be added by requiring a flag when enabling
106862306a36Sopenharmony_ci		 * KVM_CAP_MIPS_MSA capability to indicate that userland knows
106962306a36Sopenharmony_ci		 * to save/restore the appropriate extra state.
107062306a36Sopenharmony_ci		 */
107162306a36Sopenharmony_ci		r = cpu_has_msa && !(boot_cpu_data.msa_id & MSA_IR_WRPF);
107262306a36Sopenharmony_ci		break;
107362306a36Sopenharmony_ci	default:
107462306a36Sopenharmony_ci		r = kvm_mips_callbacks->check_extension(kvm, ext);
107562306a36Sopenharmony_ci		break;
107662306a36Sopenharmony_ci	}
107762306a36Sopenharmony_ci	return r;
107862306a36Sopenharmony_ci}
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ciint kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu)
108162306a36Sopenharmony_ci{
108262306a36Sopenharmony_ci	return kvm_mips_pending_timer(vcpu) ||
108362306a36Sopenharmony_ci		kvm_read_c0_guest_cause(&vcpu->arch.cop0) & C_TI;
108462306a36Sopenharmony_ci}
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ciint kvm_arch_vcpu_dump_regs(struct kvm_vcpu *vcpu)
108762306a36Sopenharmony_ci{
108862306a36Sopenharmony_ci	int i;
108962306a36Sopenharmony_ci	struct mips_coproc *cop0;
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	if (!vcpu)
109262306a36Sopenharmony_ci		return -1;
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	kvm_debug("VCPU Register Dump:\n");
109562306a36Sopenharmony_ci	kvm_debug("\tpc = 0x%08lx\n", vcpu->arch.pc);
109662306a36Sopenharmony_ci	kvm_debug("\texceptions: %08lx\n", vcpu->arch.pending_exceptions);
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	for (i = 0; i < 32; i += 4) {
109962306a36Sopenharmony_ci		kvm_debug("\tgpr%02d: %08lx %08lx %08lx %08lx\n", i,
110062306a36Sopenharmony_ci		       vcpu->arch.gprs[i],
110162306a36Sopenharmony_ci		       vcpu->arch.gprs[i + 1],
110262306a36Sopenharmony_ci		       vcpu->arch.gprs[i + 2], vcpu->arch.gprs[i + 3]);
110362306a36Sopenharmony_ci	}
110462306a36Sopenharmony_ci	kvm_debug("\thi: 0x%08lx\n", vcpu->arch.hi);
110562306a36Sopenharmony_ci	kvm_debug("\tlo: 0x%08lx\n", vcpu->arch.lo);
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	cop0 = &vcpu->arch.cop0;
110862306a36Sopenharmony_ci	kvm_debug("\tStatus: 0x%08x, Cause: 0x%08x\n",
110962306a36Sopenharmony_ci		  kvm_read_c0_guest_status(cop0),
111062306a36Sopenharmony_ci		  kvm_read_c0_guest_cause(cop0));
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	kvm_debug("\tEPC: 0x%08lx\n", kvm_read_c0_guest_epc(cop0));
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	return 0;
111562306a36Sopenharmony_ci}
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ciint kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
111862306a36Sopenharmony_ci{
111962306a36Sopenharmony_ci	int i;
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	vcpu_load(vcpu);
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	for (i = 1; i < ARRAY_SIZE(vcpu->arch.gprs); i++)
112462306a36Sopenharmony_ci		vcpu->arch.gprs[i] = regs->gpr[i];
112562306a36Sopenharmony_ci	vcpu->arch.gprs[0] = 0; /* zero is special, and cannot be set. */
112662306a36Sopenharmony_ci	vcpu->arch.hi = regs->hi;
112762306a36Sopenharmony_ci	vcpu->arch.lo = regs->lo;
112862306a36Sopenharmony_ci	vcpu->arch.pc = regs->pc;
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	vcpu_put(vcpu);
113162306a36Sopenharmony_ci	return 0;
113262306a36Sopenharmony_ci}
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ciint kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
113562306a36Sopenharmony_ci{
113662306a36Sopenharmony_ci	int i;
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	vcpu_load(vcpu);
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(vcpu->arch.gprs); i++)
114162306a36Sopenharmony_ci		regs->gpr[i] = vcpu->arch.gprs[i];
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	regs->hi = vcpu->arch.hi;
114462306a36Sopenharmony_ci	regs->lo = vcpu->arch.lo;
114562306a36Sopenharmony_ci	regs->pc = vcpu->arch.pc;
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	vcpu_put(vcpu);
114862306a36Sopenharmony_ci	return 0;
114962306a36Sopenharmony_ci}
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ciint kvm_arch_vcpu_ioctl_translate(struct kvm_vcpu *vcpu,
115262306a36Sopenharmony_ci				  struct kvm_translation *tr)
115362306a36Sopenharmony_ci{
115462306a36Sopenharmony_ci	return 0;
115562306a36Sopenharmony_ci}
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_cistatic void kvm_mips_set_c0_status(void)
115862306a36Sopenharmony_ci{
115962306a36Sopenharmony_ci	u32 status = read_c0_status();
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	if (cpu_has_dsp)
116262306a36Sopenharmony_ci		status |= (ST0_MX);
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	write_c0_status(status);
116562306a36Sopenharmony_ci	ehb();
116662306a36Sopenharmony_ci}
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci/*
116962306a36Sopenharmony_ci * Return value is in the form (errcode<<2 | RESUME_FLAG_HOST | RESUME_FLAG_NV)
117062306a36Sopenharmony_ci */
117162306a36Sopenharmony_cistatic int __kvm_mips_handle_exit(struct kvm_vcpu *vcpu)
117262306a36Sopenharmony_ci{
117362306a36Sopenharmony_ci	struct kvm_run *run = vcpu->run;
117462306a36Sopenharmony_ci	u32 cause = vcpu->arch.host_cp0_cause;
117562306a36Sopenharmony_ci	u32 exccode = (cause >> CAUSEB_EXCCODE) & 0x1f;
117662306a36Sopenharmony_ci	u32 __user *opc = (u32 __user *) vcpu->arch.pc;
117762306a36Sopenharmony_ci	unsigned long badvaddr = vcpu->arch.host_cp0_badvaddr;
117862306a36Sopenharmony_ci	enum emulation_result er = EMULATE_DONE;
117962306a36Sopenharmony_ci	u32 inst;
118062306a36Sopenharmony_ci	int ret = RESUME_GUEST;
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	vcpu->mode = OUTSIDE_GUEST_MODE;
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	/* Set a default exit reason */
118562306a36Sopenharmony_ci	run->exit_reason = KVM_EXIT_UNKNOWN;
118662306a36Sopenharmony_ci	run->ready_for_interrupt_injection = 1;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	/*
118962306a36Sopenharmony_ci	 * Set the appropriate status bits based on host CPU features,
119062306a36Sopenharmony_ci	 * before we hit the scheduler
119162306a36Sopenharmony_ci	 */
119262306a36Sopenharmony_ci	kvm_mips_set_c0_status();
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci	local_irq_enable();
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci	kvm_debug("kvm_mips_handle_exit: cause: %#x, PC: %p, kvm_run: %p, kvm_vcpu: %p\n",
119762306a36Sopenharmony_ci			cause, opc, run, vcpu);
119862306a36Sopenharmony_ci	trace_kvm_exit(vcpu, exccode);
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci	switch (exccode) {
120162306a36Sopenharmony_ci	case EXCCODE_INT:
120262306a36Sopenharmony_ci		kvm_debug("[%d]EXCCODE_INT @ %p\n", vcpu->vcpu_id, opc);
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci		++vcpu->stat.int_exits;
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci		if (need_resched())
120762306a36Sopenharmony_ci			cond_resched();
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci		ret = RESUME_GUEST;
121062306a36Sopenharmony_ci		break;
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	case EXCCODE_CPU:
121362306a36Sopenharmony_ci		kvm_debug("EXCCODE_CPU: @ PC: %p\n", opc);
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci		++vcpu->stat.cop_unusable_exits;
121662306a36Sopenharmony_ci		ret = kvm_mips_callbacks->handle_cop_unusable(vcpu);
121762306a36Sopenharmony_ci		/* XXXKYMA: Might need to return to user space */
121862306a36Sopenharmony_ci		if (run->exit_reason == KVM_EXIT_IRQ_WINDOW_OPEN)
121962306a36Sopenharmony_ci			ret = RESUME_HOST;
122062306a36Sopenharmony_ci		break;
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	case EXCCODE_MOD:
122362306a36Sopenharmony_ci		++vcpu->stat.tlbmod_exits;
122462306a36Sopenharmony_ci		ret = kvm_mips_callbacks->handle_tlb_mod(vcpu);
122562306a36Sopenharmony_ci		break;
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	case EXCCODE_TLBS:
122862306a36Sopenharmony_ci		kvm_debug("TLB ST fault:  cause %#x, status %#x, PC: %p, BadVaddr: %#lx\n",
122962306a36Sopenharmony_ci			  cause, kvm_read_c0_guest_status(&vcpu->arch.cop0), opc,
123062306a36Sopenharmony_ci			  badvaddr);
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci		++vcpu->stat.tlbmiss_st_exits;
123362306a36Sopenharmony_ci		ret = kvm_mips_callbacks->handle_tlb_st_miss(vcpu);
123462306a36Sopenharmony_ci		break;
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci	case EXCCODE_TLBL:
123762306a36Sopenharmony_ci		kvm_debug("TLB LD fault: cause %#x, PC: %p, BadVaddr: %#lx\n",
123862306a36Sopenharmony_ci			  cause, opc, badvaddr);
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci		++vcpu->stat.tlbmiss_ld_exits;
124162306a36Sopenharmony_ci		ret = kvm_mips_callbacks->handle_tlb_ld_miss(vcpu);
124262306a36Sopenharmony_ci		break;
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci	case EXCCODE_ADES:
124562306a36Sopenharmony_ci		++vcpu->stat.addrerr_st_exits;
124662306a36Sopenharmony_ci		ret = kvm_mips_callbacks->handle_addr_err_st(vcpu);
124762306a36Sopenharmony_ci		break;
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci	case EXCCODE_ADEL:
125062306a36Sopenharmony_ci		++vcpu->stat.addrerr_ld_exits;
125162306a36Sopenharmony_ci		ret = kvm_mips_callbacks->handle_addr_err_ld(vcpu);
125262306a36Sopenharmony_ci		break;
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	case EXCCODE_SYS:
125562306a36Sopenharmony_ci		++vcpu->stat.syscall_exits;
125662306a36Sopenharmony_ci		ret = kvm_mips_callbacks->handle_syscall(vcpu);
125762306a36Sopenharmony_ci		break;
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	case EXCCODE_RI:
126062306a36Sopenharmony_ci		++vcpu->stat.resvd_inst_exits;
126162306a36Sopenharmony_ci		ret = kvm_mips_callbacks->handle_res_inst(vcpu);
126262306a36Sopenharmony_ci		break;
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci	case EXCCODE_BP:
126562306a36Sopenharmony_ci		++vcpu->stat.break_inst_exits;
126662306a36Sopenharmony_ci		ret = kvm_mips_callbacks->handle_break(vcpu);
126762306a36Sopenharmony_ci		break;
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci	case EXCCODE_TR:
127062306a36Sopenharmony_ci		++vcpu->stat.trap_inst_exits;
127162306a36Sopenharmony_ci		ret = kvm_mips_callbacks->handle_trap(vcpu);
127262306a36Sopenharmony_ci		break;
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	case EXCCODE_MSAFPE:
127562306a36Sopenharmony_ci		++vcpu->stat.msa_fpe_exits;
127662306a36Sopenharmony_ci		ret = kvm_mips_callbacks->handle_msa_fpe(vcpu);
127762306a36Sopenharmony_ci		break;
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci	case EXCCODE_FPE:
128062306a36Sopenharmony_ci		++vcpu->stat.fpe_exits;
128162306a36Sopenharmony_ci		ret = kvm_mips_callbacks->handle_fpe(vcpu);
128262306a36Sopenharmony_ci		break;
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	case EXCCODE_MSADIS:
128562306a36Sopenharmony_ci		++vcpu->stat.msa_disabled_exits;
128662306a36Sopenharmony_ci		ret = kvm_mips_callbacks->handle_msa_disabled(vcpu);
128762306a36Sopenharmony_ci		break;
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	case EXCCODE_GE:
129062306a36Sopenharmony_ci		/* defer exit accounting to handler */
129162306a36Sopenharmony_ci		ret = kvm_mips_callbacks->handle_guest_exit(vcpu);
129262306a36Sopenharmony_ci		break;
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci	default:
129562306a36Sopenharmony_ci		if (cause & CAUSEF_BD)
129662306a36Sopenharmony_ci			opc += 1;
129762306a36Sopenharmony_ci		inst = 0;
129862306a36Sopenharmony_ci		kvm_get_badinstr(opc, vcpu, &inst);
129962306a36Sopenharmony_ci		kvm_err("Exception Code: %d, not yet handled, @ PC: %p, inst: 0x%08x  BadVaddr: %#lx Status: %#x\n",
130062306a36Sopenharmony_ci			exccode, opc, inst, badvaddr,
130162306a36Sopenharmony_ci			kvm_read_c0_guest_status(&vcpu->arch.cop0));
130262306a36Sopenharmony_ci		kvm_arch_vcpu_dump_regs(vcpu);
130362306a36Sopenharmony_ci		run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
130462306a36Sopenharmony_ci		ret = RESUME_HOST;
130562306a36Sopenharmony_ci		break;
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	}
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci	local_irq_disable();
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci	if (ret == RESUME_GUEST)
131262306a36Sopenharmony_ci		kvm_vz_acquire_htimer(vcpu);
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	if (er == EMULATE_DONE && !(ret & RESUME_HOST))
131562306a36Sopenharmony_ci		kvm_mips_deliver_interrupts(vcpu, cause);
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	if (!(ret & RESUME_HOST)) {
131862306a36Sopenharmony_ci		/* Only check for signals if not already exiting to userspace */
131962306a36Sopenharmony_ci		if (signal_pending(current)) {
132062306a36Sopenharmony_ci			run->exit_reason = KVM_EXIT_INTR;
132162306a36Sopenharmony_ci			ret = (-EINTR << 2) | RESUME_HOST;
132262306a36Sopenharmony_ci			++vcpu->stat.signal_exits;
132362306a36Sopenharmony_ci			trace_kvm_exit(vcpu, KVM_TRACE_EXIT_SIGNAL);
132462306a36Sopenharmony_ci		}
132562306a36Sopenharmony_ci	}
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci	if (ret == RESUME_GUEST) {
132862306a36Sopenharmony_ci		trace_kvm_reenter(vcpu);
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci		/*
133162306a36Sopenharmony_ci		 * Make sure the read of VCPU requests in vcpu_reenter()
133262306a36Sopenharmony_ci		 * callback is not reordered ahead of the write to vcpu->mode,
133362306a36Sopenharmony_ci		 * or we could miss a TLB flush request while the requester sees
133462306a36Sopenharmony_ci		 * the VCPU as outside of guest mode and not needing an IPI.
133562306a36Sopenharmony_ci		 */
133662306a36Sopenharmony_ci		smp_store_mb(vcpu->mode, IN_GUEST_MODE);
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci		kvm_mips_callbacks->vcpu_reenter(vcpu);
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci		/*
134162306a36Sopenharmony_ci		 * If FPU / MSA are enabled (i.e. the guest's FPU / MSA context
134262306a36Sopenharmony_ci		 * is live), restore FCR31 / MSACSR.
134362306a36Sopenharmony_ci		 *
134462306a36Sopenharmony_ci		 * This should be before returning to the guest exception
134562306a36Sopenharmony_ci		 * vector, as it may well cause an [MSA] FP exception if there
134662306a36Sopenharmony_ci		 * are pending exception bits unmasked. (see
134762306a36Sopenharmony_ci		 * kvm_mips_csr_die_notifier() for how that is handled).
134862306a36Sopenharmony_ci		 */
134962306a36Sopenharmony_ci		if (kvm_mips_guest_has_fpu(&vcpu->arch) &&
135062306a36Sopenharmony_ci		    read_c0_status() & ST0_CU1)
135162306a36Sopenharmony_ci			__kvm_restore_fcsr(&vcpu->arch);
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci		if (kvm_mips_guest_has_msa(&vcpu->arch) &&
135462306a36Sopenharmony_ci		    read_c0_config5() & MIPS_CONF5_MSAEN)
135562306a36Sopenharmony_ci			__kvm_restore_msacsr(&vcpu->arch);
135662306a36Sopenharmony_ci	}
135762306a36Sopenharmony_ci	return ret;
135862306a36Sopenharmony_ci}
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ciint noinstr kvm_mips_handle_exit(struct kvm_vcpu *vcpu)
136162306a36Sopenharmony_ci{
136262306a36Sopenharmony_ci	int ret;
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci	guest_state_exit_irqoff();
136562306a36Sopenharmony_ci	ret = __kvm_mips_handle_exit(vcpu);
136662306a36Sopenharmony_ci	guest_state_enter_irqoff();
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci	return ret;
136962306a36Sopenharmony_ci}
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci/* Enable FPU for guest and restore context */
137262306a36Sopenharmony_civoid kvm_own_fpu(struct kvm_vcpu *vcpu)
137362306a36Sopenharmony_ci{
137462306a36Sopenharmony_ci	struct mips_coproc *cop0 = &vcpu->arch.cop0;
137562306a36Sopenharmony_ci	unsigned int sr, cfg5;
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ci	preempt_disable();
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci	sr = kvm_read_c0_guest_status(cop0);
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci	/*
138262306a36Sopenharmony_ci	 * If MSA state is already live, it is undefined how it interacts with
138362306a36Sopenharmony_ci	 * FR=0 FPU state, and we don't want to hit reserved instruction
138462306a36Sopenharmony_ci	 * exceptions trying to save the MSA state later when CU=1 && FR=1, so
138562306a36Sopenharmony_ci	 * play it safe and save it first.
138662306a36Sopenharmony_ci	 */
138762306a36Sopenharmony_ci	if (cpu_has_msa && sr & ST0_CU1 && !(sr & ST0_FR) &&
138862306a36Sopenharmony_ci	    vcpu->arch.aux_inuse & KVM_MIPS_AUX_MSA)
138962306a36Sopenharmony_ci		kvm_lose_fpu(vcpu);
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_ci	/*
139262306a36Sopenharmony_ci	 * Enable FPU for guest
139362306a36Sopenharmony_ci	 * We set FR and FRE according to guest context
139462306a36Sopenharmony_ci	 */
139562306a36Sopenharmony_ci	change_c0_status(ST0_CU1 | ST0_FR, sr);
139662306a36Sopenharmony_ci	if (cpu_has_fre) {
139762306a36Sopenharmony_ci		cfg5 = kvm_read_c0_guest_config5(cop0);
139862306a36Sopenharmony_ci		change_c0_config5(MIPS_CONF5_FRE, cfg5);
139962306a36Sopenharmony_ci	}
140062306a36Sopenharmony_ci	enable_fpu_hazard();
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci	/* If guest FPU state not active, restore it now */
140362306a36Sopenharmony_ci	if (!(vcpu->arch.aux_inuse & KVM_MIPS_AUX_FPU)) {
140462306a36Sopenharmony_ci		__kvm_restore_fpu(&vcpu->arch);
140562306a36Sopenharmony_ci		vcpu->arch.aux_inuse |= KVM_MIPS_AUX_FPU;
140662306a36Sopenharmony_ci		trace_kvm_aux(vcpu, KVM_TRACE_AUX_RESTORE, KVM_TRACE_AUX_FPU);
140762306a36Sopenharmony_ci	} else {
140862306a36Sopenharmony_ci		trace_kvm_aux(vcpu, KVM_TRACE_AUX_ENABLE, KVM_TRACE_AUX_FPU);
140962306a36Sopenharmony_ci	}
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci	preempt_enable();
141262306a36Sopenharmony_ci}
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci#ifdef CONFIG_CPU_HAS_MSA
141562306a36Sopenharmony_ci/* Enable MSA for guest and restore context */
141662306a36Sopenharmony_civoid kvm_own_msa(struct kvm_vcpu *vcpu)
141762306a36Sopenharmony_ci{
141862306a36Sopenharmony_ci	struct mips_coproc *cop0 = &vcpu->arch.cop0;
141962306a36Sopenharmony_ci	unsigned int sr, cfg5;
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci	preempt_disable();
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci	/*
142462306a36Sopenharmony_ci	 * Enable FPU if enabled in guest, since we're restoring FPU context
142562306a36Sopenharmony_ci	 * anyway. We set FR and FRE according to guest context.
142662306a36Sopenharmony_ci	 */
142762306a36Sopenharmony_ci	if (kvm_mips_guest_has_fpu(&vcpu->arch)) {
142862306a36Sopenharmony_ci		sr = kvm_read_c0_guest_status(cop0);
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci		/*
143162306a36Sopenharmony_ci		 * If FR=0 FPU state is already live, it is undefined how it
143262306a36Sopenharmony_ci		 * interacts with MSA state, so play it safe and save it first.
143362306a36Sopenharmony_ci		 */
143462306a36Sopenharmony_ci		if (!(sr & ST0_FR) &&
143562306a36Sopenharmony_ci		    (vcpu->arch.aux_inuse & (KVM_MIPS_AUX_FPU |
143662306a36Sopenharmony_ci				KVM_MIPS_AUX_MSA)) == KVM_MIPS_AUX_FPU)
143762306a36Sopenharmony_ci			kvm_lose_fpu(vcpu);
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_ci		change_c0_status(ST0_CU1 | ST0_FR, sr);
144062306a36Sopenharmony_ci		if (sr & ST0_CU1 && cpu_has_fre) {
144162306a36Sopenharmony_ci			cfg5 = kvm_read_c0_guest_config5(cop0);
144262306a36Sopenharmony_ci			change_c0_config5(MIPS_CONF5_FRE, cfg5);
144362306a36Sopenharmony_ci		}
144462306a36Sopenharmony_ci	}
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_ci	/* Enable MSA for guest */
144762306a36Sopenharmony_ci	set_c0_config5(MIPS_CONF5_MSAEN);
144862306a36Sopenharmony_ci	enable_fpu_hazard();
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci	switch (vcpu->arch.aux_inuse & (KVM_MIPS_AUX_FPU | KVM_MIPS_AUX_MSA)) {
145162306a36Sopenharmony_ci	case KVM_MIPS_AUX_FPU:
145262306a36Sopenharmony_ci		/*
145362306a36Sopenharmony_ci		 * Guest FPU state already loaded, only restore upper MSA state
145462306a36Sopenharmony_ci		 */
145562306a36Sopenharmony_ci		__kvm_restore_msa_upper(&vcpu->arch);
145662306a36Sopenharmony_ci		vcpu->arch.aux_inuse |= KVM_MIPS_AUX_MSA;
145762306a36Sopenharmony_ci		trace_kvm_aux(vcpu, KVM_TRACE_AUX_RESTORE, KVM_TRACE_AUX_MSA);
145862306a36Sopenharmony_ci		break;
145962306a36Sopenharmony_ci	case 0:
146062306a36Sopenharmony_ci		/* Neither FPU or MSA already active, restore full MSA state */
146162306a36Sopenharmony_ci		__kvm_restore_msa(&vcpu->arch);
146262306a36Sopenharmony_ci		vcpu->arch.aux_inuse |= KVM_MIPS_AUX_MSA;
146362306a36Sopenharmony_ci		if (kvm_mips_guest_has_fpu(&vcpu->arch))
146462306a36Sopenharmony_ci			vcpu->arch.aux_inuse |= KVM_MIPS_AUX_FPU;
146562306a36Sopenharmony_ci		trace_kvm_aux(vcpu, KVM_TRACE_AUX_RESTORE,
146662306a36Sopenharmony_ci			      KVM_TRACE_AUX_FPU_MSA);
146762306a36Sopenharmony_ci		break;
146862306a36Sopenharmony_ci	default:
146962306a36Sopenharmony_ci		trace_kvm_aux(vcpu, KVM_TRACE_AUX_ENABLE, KVM_TRACE_AUX_MSA);
147062306a36Sopenharmony_ci		break;
147162306a36Sopenharmony_ci	}
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_ci	preempt_enable();
147462306a36Sopenharmony_ci}
147562306a36Sopenharmony_ci#endif
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci/* Drop FPU & MSA without saving it */
147862306a36Sopenharmony_civoid kvm_drop_fpu(struct kvm_vcpu *vcpu)
147962306a36Sopenharmony_ci{
148062306a36Sopenharmony_ci	preempt_disable();
148162306a36Sopenharmony_ci	if (cpu_has_msa && vcpu->arch.aux_inuse & KVM_MIPS_AUX_MSA) {
148262306a36Sopenharmony_ci		disable_msa();
148362306a36Sopenharmony_ci		trace_kvm_aux(vcpu, KVM_TRACE_AUX_DISCARD, KVM_TRACE_AUX_MSA);
148462306a36Sopenharmony_ci		vcpu->arch.aux_inuse &= ~KVM_MIPS_AUX_MSA;
148562306a36Sopenharmony_ci	}
148662306a36Sopenharmony_ci	if (vcpu->arch.aux_inuse & KVM_MIPS_AUX_FPU) {
148762306a36Sopenharmony_ci		clear_c0_status(ST0_CU1 | ST0_FR);
148862306a36Sopenharmony_ci		trace_kvm_aux(vcpu, KVM_TRACE_AUX_DISCARD, KVM_TRACE_AUX_FPU);
148962306a36Sopenharmony_ci		vcpu->arch.aux_inuse &= ~KVM_MIPS_AUX_FPU;
149062306a36Sopenharmony_ci	}
149162306a36Sopenharmony_ci	preempt_enable();
149262306a36Sopenharmony_ci}
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_ci/* Save and disable FPU & MSA */
149562306a36Sopenharmony_civoid kvm_lose_fpu(struct kvm_vcpu *vcpu)
149662306a36Sopenharmony_ci{
149762306a36Sopenharmony_ci	/*
149862306a36Sopenharmony_ci	 * With T&E, FPU & MSA get disabled in root context (hardware) when it
149962306a36Sopenharmony_ci	 * is disabled in guest context (software), but the register state in
150062306a36Sopenharmony_ci	 * the hardware may still be in use.
150162306a36Sopenharmony_ci	 * This is why we explicitly re-enable the hardware before saving.
150262306a36Sopenharmony_ci	 */
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_ci	preempt_disable();
150562306a36Sopenharmony_ci	if (cpu_has_msa && vcpu->arch.aux_inuse & KVM_MIPS_AUX_MSA) {
150662306a36Sopenharmony_ci		__kvm_save_msa(&vcpu->arch);
150762306a36Sopenharmony_ci		trace_kvm_aux(vcpu, KVM_TRACE_AUX_SAVE, KVM_TRACE_AUX_FPU_MSA);
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci		/* Disable MSA & FPU */
151062306a36Sopenharmony_ci		disable_msa();
151162306a36Sopenharmony_ci		if (vcpu->arch.aux_inuse & KVM_MIPS_AUX_FPU) {
151262306a36Sopenharmony_ci			clear_c0_status(ST0_CU1 | ST0_FR);
151362306a36Sopenharmony_ci			disable_fpu_hazard();
151462306a36Sopenharmony_ci		}
151562306a36Sopenharmony_ci		vcpu->arch.aux_inuse &= ~(KVM_MIPS_AUX_FPU | KVM_MIPS_AUX_MSA);
151662306a36Sopenharmony_ci	} else if (vcpu->arch.aux_inuse & KVM_MIPS_AUX_FPU) {
151762306a36Sopenharmony_ci		__kvm_save_fpu(&vcpu->arch);
151862306a36Sopenharmony_ci		vcpu->arch.aux_inuse &= ~KVM_MIPS_AUX_FPU;
151962306a36Sopenharmony_ci		trace_kvm_aux(vcpu, KVM_TRACE_AUX_SAVE, KVM_TRACE_AUX_FPU);
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_ci		/* Disable FPU */
152262306a36Sopenharmony_ci		clear_c0_status(ST0_CU1 | ST0_FR);
152362306a36Sopenharmony_ci		disable_fpu_hazard();
152462306a36Sopenharmony_ci	}
152562306a36Sopenharmony_ci	preempt_enable();
152662306a36Sopenharmony_ci}
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_ci/*
152962306a36Sopenharmony_ci * Step over a specific ctc1 to FCSR and a specific ctcmsa to MSACSR which are
153062306a36Sopenharmony_ci * used to restore guest FCSR/MSACSR state and may trigger a "harmless" FP/MSAFP
153162306a36Sopenharmony_ci * exception if cause bits are set in the value being written.
153262306a36Sopenharmony_ci */
153362306a36Sopenharmony_cistatic int kvm_mips_csr_die_notify(struct notifier_block *self,
153462306a36Sopenharmony_ci				   unsigned long cmd, void *ptr)
153562306a36Sopenharmony_ci{
153662306a36Sopenharmony_ci	struct die_args *args = (struct die_args *)ptr;
153762306a36Sopenharmony_ci	struct pt_regs *regs = args->regs;
153862306a36Sopenharmony_ci	unsigned long pc;
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci	/* Only interested in FPE and MSAFPE */
154162306a36Sopenharmony_ci	if (cmd != DIE_FP && cmd != DIE_MSAFP)
154262306a36Sopenharmony_ci		return NOTIFY_DONE;
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_ci	/* Return immediately if guest context isn't active */
154562306a36Sopenharmony_ci	if (!(current->flags & PF_VCPU))
154662306a36Sopenharmony_ci		return NOTIFY_DONE;
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_ci	/* Should never get here from user mode */
154962306a36Sopenharmony_ci	BUG_ON(user_mode(regs));
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_ci	pc = instruction_pointer(regs);
155262306a36Sopenharmony_ci	switch (cmd) {
155362306a36Sopenharmony_ci	case DIE_FP:
155462306a36Sopenharmony_ci		/* match 2nd instruction in __kvm_restore_fcsr */
155562306a36Sopenharmony_ci		if (pc != (unsigned long)&__kvm_restore_fcsr + 4)
155662306a36Sopenharmony_ci			return NOTIFY_DONE;
155762306a36Sopenharmony_ci		break;
155862306a36Sopenharmony_ci	case DIE_MSAFP:
155962306a36Sopenharmony_ci		/* match 2nd/3rd instruction in __kvm_restore_msacsr */
156062306a36Sopenharmony_ci		if (!cpu_has_msa ||
156162306a36Sopenharmony_ci		    pc < (unsigned long)&__kvm_restore_msacsr + 4 ||
156262306a36Sopenharmony_ci		    pc > (unsigned long)&__kvm_restore_msacsr + 8)
156362306a36Sopenharmony_ci			return NOTIFY_DONE;
156462306a36Sopenharmony_ci		break;
156562306a36Sopenharmony_ci	}
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_ci	/* Move PC forward a little and continue executing */
156862306a36Sopenharmony_ci	instruction_pointer(regs) += 4;
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_ci	return NOTIFY_STOP;
157162306a36Sopenharmony_ci}
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_cistatic struct notifier_block kvm_mips_csr_die_notifier = {
157462306a36Sopenharmony_ci	.notifier_call = kvm_mips_csr_die_notify,
157562306a36Sopenharmony_ci};
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_cistatic u32 kvm_default_priority_to_irq[MIPS_EXC_MAX] = {
157862306a36Sopenharmony_ci	[MIPS_EXC_INT_TIMER] = C_IRQ5,
157962306a36Sopenharmony_ci	[MIPS_EXC_INT_IO_1]  = C_IRQ0,
158062306a36Sopenharmony_ci	[MIPS_EXC_INT_IPI_1] = C_IRQ1,
158162306a36Sopenharmony_ci	[MIPS_EXC_INT_IPI_2] = C_IRQ2,
158262306a36Sopenharmony_ci};
158362306a36Sopenharmony_ci
158462306a36Sopenharmony_cistatic u32 kvm_loongson3_priority_to_irq[MIPS_EXC_MAX] = {
158562306a36Sopenharmony_ci	[MIPS_EXC_INT_TIMER] = C_IRQ5,
158662306a36Sopenharmony_ci	[MIPS_EXC_INT_IO_1]  = C_IRQ0,
158762306a36Sopenharmony_ci	[MIPS_EXC_INT_IO_2]  = C_IRQ1,
158862306a36Sopenharmony_ci	[MIPS_EXC_INT_IPI_1] = C_IRQ4,
158962306a36Sopenharmony_ci};
159062306a36Sopenharmony_ci
159162306a36Sopenharmony_ciu32 *kvm_priority_to_irq = kvm_default_priority_to_irq;
159262306a36Sopenharmony_ci
159362306a36Sopenharmony_ciu32 kvm_irq_to_priority(u32 irq)
159462306a36Sopenharmony_ci{
159562306a36Sopenharmony_ci	int i;
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_ci	for (i = MIPS_EXC_INT_TIMER; i < MIPS_EXC_MAX; i++) {
159862306a36Sopenharmony_ci		if (kvm_priority_to_irq[i] == (1 << (irq + 8)))
159962306a36Sopenharmony_ci			return i;
160062306a36Sopenharmony_ci	}
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_ci	return MIPS_EXC_MAX;
160362306a36Sopenharmony_ci}
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_cistatic int __init kvm_mips_init(void)
160662306a36Sopenharmony_ci{
160762306a36Sopenharmony_ci	int ret;
160862306a36Sopenharmony_ci
160962306a36Sopenharmony_ci	if (cpu_has_mmid) {
161062306a36Sopenharmony_ci		pr_warn("KVM does not yet support MMIDs. KVM Disabled\n");
161162306a36Sopenharmony_ci		return -EOPNOTSUPP;
161262306a36Sopenharmony_ci	}
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_ci	ret = kvm_mips_entry_setup();
161562306a36Sopenharmony_ci	if (ret)
161662306a36Sopenharmony_ci		return ret;
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_ci	ret = kvm_mips_emulation_init();
161962306a36Sopenharmony_ci	if (ret)
162062306a36Sopenharmony_ci		return ret;
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_ci	if (boot_cpu_type() == CPU_LOONGSON64)
162462306a36Sopenharmony_ci		kvm_priority_to_irq = kvm_loongson3_priority_to_irq;
162562306a36Sopenharmony_ci
162662306a36Sopenharmony_ci	register_die_notifier(&kvm_mips_csr_die_notifier);
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_ci	ret = kvm_init(sizeof(struct kvm_vcpu), 0, THIS_MODULE);
162962306a36Sopenharmony_ci	if (ret) {
163062306a36Sopenharmony_ci		unregister_die_notifier(&kvm_mips_csr_die_notifier);
163162306a36Sopenharmony_ci		return ret;
163262306a36Sopenharmony_ci	}
163362306a36Sopenharmony_ci	return 0;
163462306a36Sopenharmony_ci}
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_cistatic void __exit kvm_mips_exit(void)
163762306a36Sopenharmony_ci{
163862306a36Sopenharmony_ci	kvm_exit();
163962306a36Sopenharmony_ci
164062306a36Sopenharmony_ci	unregister_die_notifier(&kvm_mips_csr_die_notifier);
164162306a36Sopenharmony_ci}
164262306a36Sopenharmony_ci
164362306a36Sopenharmony_cimodule_init(kvm_mips_init);
164462306a36Sopenharmony_cimodule_exit(kvm_mips_exit);
164562306a36Sopenharmony_ci
164662306a36Sopenharmony_ciEXPORT_TRACEPOINT_SYMBOL(kvm_exit);
1647