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(®, argp, sizeof(reg))) 93762306a36Sopenharmony_ci break; 93862306a36Sopenharmony_ci if (ioctl == KVM_SET_ONE_REG) 93962306a36Sopenharmony_ci r = kvm_mips_set_reg(vcpu, ®); 94062306a36Sopenharmony_ci else 94162306a36Sopenharmony_ci r = kvm_mips_get_reg(vcpu, ®); 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(®_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, ®_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