18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/errno.h>
78c2ecf20Sopenharmony_ci#include <linux/err.h>
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci#include <linux/preempt.h>
108c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
118c2ecf20Sopenharmony_ci#include <asm/cacheflush.h>
128c2ecf20Sopenharmony_ci#include <asm/cacheops.h>
138c2ecf20Sopenharmony_ci#include <asm/cmpxchg.h>
148c2ecf20Sopenharmony_ci#include <asm/fpu.h>
158c2ecf20Sopenharmony_ci#include <asm/inst.h>
168c2ecf20Sopenharmony_ci#include <asm/mmu_context.h>
178c2ecf20Sopenharmony_ci#include <asm/numa.h>
188c2ecf20Sopenharmony_ci#include <asm/cacheflush.h>
198c2ecf20Sopenharmony_ci#include <asm/time.h>
208c2ecf20Sopenharmony_ci#include <asm/tlb.h>
218c2ecf20Sopenharmony_ci#include <asm/watch.h>
228c2ecf20Sopenharmony_ci#include "kvmcpu.h"
238c2ecf20Sopenharmony_ci#include <linux/kvm_host.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include "trace.h"
268c2ecf20Sopenharmony_ci#include "kvm_compat.h"
278c2ecf20Sopenharmony_ci#include "kvmcsr.h"
288c2ecf20Sopenharmony_ci#include "intc/ls3a_ext_irq.h"
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci/*
318c2ecf20Sopenharmony_ci * Loongarch KVM callback handling for not implemented guest exiting
328c2ecf20Sopenharmony_ci */
338c2ecf20Sopenharmony_cistatic int _kvm_fault_ni(struct kvm_vcpu *vcpu)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	unsigned long estat, badv;
368c2ecf20Sopenharmony_ci	unsigned int exccode, inst;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	/*
398c2ecf20Sopenharmony_ci	 *  Fetch the instruction.
408c2ecf20Sopenharmony_ci	 */
418c2ecf20Sopenharmony_ci	badv = vcpu->arch.badv;
428c2ecf20Sopenharmony_ci	estat = vcpu->arch.host_estat;
438c2ecf20Sopenharmony_ci	exccode = (estat & KVM_ESTAT_EXC) >> KVM_ESTAT_EXC_SHIFT;
448c2ecf20Sopenharmony_ci	inst = vcpu->arch.badi;
458c2ecf20Sopenharmony_ci	kvm_err("Exccode: %d PC=%#lx inst=0x%08x BadVaddr=%#lx estat=%#llx\n",
468c2ecf20Sopenharmony_ci		exccode, vcpu->arch.pc, inst, badv, kvm_read_gcsr_estat());
478c2ecf20Sopenharmony_ci	kvm_arch_vcpu_dump_regs(vcpu);
488c2ecf20Sopenharmony_ci	vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
498c2ecf20Sopenharmony_ci	return RESUME_HOST;
508c2ecf20Sopenharmony_ci}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic int _kvm_handle_csr(struct kvm_vcpu *vcpu, larch_inst inst)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	enum emulation_result er = EMULATE_DONE;
558c2ecf20Sopenharmony_ci	unsigned int rd, rj, csrid;
568c2ecf20Sopenharmony_ci	unsigned long csr_mask;
578c2ecf20Sopenharmony_ci	unsigned long val = 0;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	/*
608c2ecf20Sopenharmony_ci	 * CSR value mask imm
618c2ecf20Sopenharmony_ci	 * rj = 0 means csrrd
628c2ecf20Sopenharmony_ci	 * rj = 1 means csrwr
638c2ecf20Sopenharmony_ci	 * rj != 0,1 means csrxchg
648c2ecf20Sopenharmony_ci	 */
658c2ecf20Sopenharmony_ci	rd = inst.reg2csr_format.rd;
668c2ecf20Sopenharmony_ci	rj = inst.reg2csr_format.rj;
678c2ecf20Sopenharmony_ci	csrid = inst.reg2csr_format.csr;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	/* Process CSR ops */
708c2ecf20Sopenharmony_ci	if (rj == 0) {
718c2ecf20Sopenharmony_ci		/* process csrrd */
728c2ecf20Sopenharmony_ci		val = _kvm_emu_read_csr(vcpu, csrid);
738c2ecf20Sopenharmony_ci		if (er != EMULATE_FAIL)
748c2ecf20Sopenharmony_ci			vcpu->arch.gprs[rd] = val;
758c2ecf20Sopenharmony_ci	} else if (rj == 1) {
768c2ecf20Sopenharmony_ci		/* process csrwr */
778c2ecf20Sopenharmony_ci		val = vcpu->arch.gprs[rd];
788c2ecf20Sopenharmony_ci		_kvm_emu_write_csr(vcpu, csrid, val);
798c2ecf20Sopenharmony_ci	} else {
808c2ecf20Sopenharmony_ci		/* process csrxchg */
818c2ecf20Sopenharmony_ci		val = vcpu->arch.gprs[rd];
828c2ecf20Sopenharmony_ci		csr_mask = vcpu->arch.gprs[rj];
838c2ecf20Sopenharmony_ci		_kvm_emu_xchg_csr(vcpu, csrid, csr_mask, val);
848c2ecf20Sopenharmony_ci	}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	return er;
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic int _kvm_emu_cache(struct kvm_vcpu *vcpu, larch_inst inst)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	return EMULATE_DONE;
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic int _kvm_trap_handle_gspr(struct kvm_vcpu *vcpu)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	enum emulation_result er = EMULATE_DONE;
978c2ecf20Sopenharmony_ci	struct kvm_run *run = vcpu->run;
988c2ecf20Sopenharmony_ci	larch_inst inst;
998c2ecf20Sopenharmony_ci	unsigned long curr_pc;
1008c2ecf20Sopenharmony_ci	int rd, rj;
1018c2ecf20Sopenharmony_ci	unsigned int index;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	/*
1048c2ecf20Sopenharmony_ci	 *  Fetch the instruction.
1058c2ecf20Sopenharmony_ci	 */
1068c2ecf20Sopenharmony_ci	inst.word = vcpu->arch.badi;
1078c2ecf20Sopenharmony_ci	curr_pc = vcpu->arch.pc;
1088c2ecf20Sopenharmony_ci	update_pc(&vcpu->arch);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	er = EMULATE_FAIL;
1118c2ecf20Sopenharmony_ci	switch (((inst.word >> 24) & 0xff)) {
1128c2ecf20Sopenharmony_ci	case 0x0:
1138c2ecf20Sopenharmony_ci		/* cpucfg GSPR */
1148c2ecf20Sopenharmony_ci		if (inst.reg2_format.opcode == 0x1B) {
1158c2ecf20Sopenharmony_ci			rd = inst.reg2_format.rd;
1168c2ecf20Sopenharmony_ci			rj = inst.reg2_format.rj;
1178c2ecf20Sopenharmony_ci			++vcpu->stat.cpucfg_exits;
1188c2ecf20Sopenharmony_ci			index = vcpu->arch.gprs[rj];
1198c2ecf20Sopenharmony_ci			vcpu->arch.gprs[rd] = vcpu->kvm->arch.cpucfgs.cpucfg[index];
1208c2ecf20Sopenharmony_ci			if ((index == 2) || (vcpu->arch.gprs[rd] == 0))
1218c2ecf20Sopenharmony_ci				/*
1228c2ecf20Sopenharmony_ci				 * Fallback to get host cpucfg info, this is just for
1238c2ecf20Sopenharmony_ci				 * compatible with older qemu.
1248c2ecf20Sopenharmony_ci				 */
1258c2ecf20Sopenharmony_ci				vcpu->arch.gprs[rd] = read_cpucfg(index);
1268c2ecf20Sopenharmony_ci			if (index == 2)
1278c2ecf20Sopenharmony_ci				/* do not support nested virtualization */
1288c2ecf20Sopenharmony_ci				vcpu->arch.gprs[rd] &= ~CPUCFG2_LVZP;
1298c2ecf20Sopenharmony_ci			er = EMULATE_DONE;
1308c2ecf20Sopenharmony_ci		}
1318c2ecf20Sopenharmony_ci		break;
1328c2ecf20Sopenharmony_ci	case 0x4:
1338c2ecf20Sopenharmony_ci		/* csr GSPR */
1348c2ecf20Sopenharmony_ci		er = _kvm_handle_csr(vcpu, inst);
1358c2ecf20Sopenharmony_ci		break;
1368c2ecf20Sopenharmony_ci	case 0x6:
1378c2ecf20Sopenharmony_ci		/* iocsr,cacop,idle GSPR */
1388c2ecf20Sopenharmony_ci		switch (((inst.word >> 22) & 0x3ff)) {
1398c2ecf20Sopenharmony_ci		case 0x18:
1408c2ecf20Sopenharmony_ci			/* cache GSPR */
1418c2ecf20Sopenharmony_ci			er = _kvm_emu_cache(vcpu, inst);
1428c2ecf20Sopenharmony_ci			trace_kvm_exit(vcpu, KVM_TRACE_EXIT_CACHE);
1438c2ecf20Sopenharmony_ci			break;
1448c2ecf20Sopenharmony_ci		case 0x19:
1458c2ecf20Sopenharmony_ci			/* iocsr/idle GSPR */
1468c2ecf20Sopenharmony_ci			switch (((inst.word >> 15) & 0x1ffff)) {
1478c2ecf20Sopenharmony_ci			case 0xc90:
1488c2ecf20Sopenharmony_ci				/* iocsr GSPR */
1498c2ecf20Sopenharmony_ci				er = _kvm_emu_iocsr(inst, run, vcpu);
1508c2ecf20Sopenharmony_ci				break;
1518c2ecf20Sopenharmony_ci			case idle_op:
1528c2ecf20Sopenharmony_ci				/* idle GSPR */
1538c2ecf20Sopenharmony_ci				er = _kvm_emu_idle(vcpu);
1548c2ecf20Sopenharmony_ci				break;
1558c2ecf20Sopenharmony_ci			default:
1568c2ecf20Sopenharmony_ci				er = EMULATE_FAIL;
1578c2ecf20Sopenharmony_ci				break;
1588c2ecf20Sopenharmony_ci			}
1598c2ecf20Sopenharmony_ci			break;
1608c2ecf20Sopenharmony_ci		default:
1618c2ecf20Sopenharmony_ci			er = EMULATE_FAIL;
1628c2ecf20Sopenharmony_ci			break;
1638c2ecf20Sopenharmony_ci		}
1648c2ecf20Sopenharmony_ci		break;
1658c2ecf20Sopenharmony_ci	default:
1668c2ecf20Sopenharmony_ci		er = EMULATE_FAIL;
1678c2ecf20Sopenharmony_ci		break;
1688c2ecf20Sopenharmony_ci	}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	/* Rollback PC only if emulation was unsuccessful */
1718c2ecf20Sopenharmony_ci	if (er == EMULATE_FAIL) {
1728c2ecf20Sopenharmony_ci		kvm_err("[%#lx]%s: unsupported gspr instruction 0x%08x\n",
1738c2ecf20Sopenharmony_ci			curr_pc, __func__, inst.word);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci		kvm_arch_vcpu_dump_regs(vcpu);
1768c2ecf20Sopenharmony_ci		vcpu->arch.pc = curr_pc;
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci	return er;
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistatic int _kvm_check_hypcall(struct kvm_vcpu *vcpu)
1828c2ecf20Sopenharmony_ci{
1838c2ecf20Sopenharmony_ci	enum emulation_result ret;
1848c2ecf20Sopenharmony_ci	larch_inst inst;
1858c2ecf20Sopenharmony_ci	unsigned long curr_pc;
1868c2ecf20Sopenharmony_ci	unsigned int code;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	/*
1898c2ecf20Sopenharmony_ci	 * Update PC and hold onto current PC in case there is
1908c2ecf20Sopenharmony_ci	 * an error and we want to rollback the PC
1918c2ecf20Sopenharmony_ci	 */
1928c2ecf20Sopenharmony_ci	inst.word = vcpu->arch.badi;
1938c2ecf20Sopenharmony_ci	code = inst.reg0i15_format.simmediate;
1948c2ecf20Sopenharmony_ci	curr_pc = vcpu->arch.pc;
1958c2ecf20Sopenharmony_ci	update_pc(&vcpu->arch);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	ret = EMULATE_DONE;
1988c2ecf20Sopenharmony_ci	switch (code) {
1998c2ecf20Sopenharmony_ci	case KVM_HC_CODE_SERIVCE:
2008c2ecf20Sopenharmony_ci		ret = EMULATE_PV_HYPERCALL;
2018c2ecf20Sopenharmony_ci		break;
2028c2ecf20Sopenharmony_ci	case KVM_HC_CODE_SWDBG:
2038c2ecf20Sopenharmony_ci		/*
2048c2ecf20Sopenharmony_ci		 * Only SWDBG(SoftWare DeBug) could stop vm
2058c2ecf20Sopenharmony_ci		 * code other than 0 is ignored.
2068c2ecf20Sopenharmony_ci		 */
2078c2ecf20Sopenharmony_ci		ret = EMULATE_DEBUG;
2088c2ecf20Sopenharmony_ci		break;
2098c2ecf20Sopenharmony_ci	default:
2108c2ecf20Sopenharmony_ci		kvm_info("[%#lx] HYPCALL %#03x unsupported\n", vcpu->arch.pc, code);
2118c2ecf20Sopenharmony_ci		break;
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	if (ret == EMULATE_DEBUG)
2158c2ecf20Sopenharmony_ci		vcpu->arch.pc = curr_pc;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	return ret;
2188c2ecf20Sopenharmony_ci}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci/* Execute cpucfg instruction will tirggerGSPR,
2218c2ecf20Sopenharmony_ci * Also the access to unimplemented csrs 0x15
2228c2ecf20Sopenharmony_ci * 0x16, 0x50~0x53, 0x80, 0x81, 0x90~0x95, 0x98
2238c2ecf20Sopenharmony_ci * 0xc0~0xff, 0x100~0x109, 0x500~0x502,
2248c2ecf20Sopenharmony_ci * cacop_op, idle_op iocsr ops the same */
2258c2ecf20Sopenharmony_cistatic int _kvm_handle_gspr(struct kvm_vcpu *vcpu)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	enum emulation_result er = EMULATE_DONE;
2288c2ecf20Sopenharmony_ci	int ret = RESUME_GUEST;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	vcpu->arch.is_hypcall = 0;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	er = _kvm_trap_handle_gspr(vcpu);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	if (er == EMULATE_DONE) {
2358c2ecf20Sopenharmony_ci		ret = RESUME_GUEST;
2368c2ecf20Sopenharmony_ci	} else if (er == EMULATE_DO_MMIO) {
2378c2ecf20Sopenharmony_ci		vcpu->run->exit_reason = KVM_EXIT_MMIO;
2388c2ecf20Sopenharmony_ci		ret = RESUME_HOST;
2398c2ecf20Sopenharmony_ci	} else if (er == EMULATE_DO_IOCSR) {
2408c2ecf20Sopenharmony_ci		vcpu->run->exit_reason = KVM_EXIT_LOONGARCH_IOCSR;
2418c2ecf20Sopenharmony_ci		ret = RESUME_HOST;
2428c2ecf20Sopenharmony_ci	} else {
2438c2ecf20Sopenharmony_ci		kvm_err("%s internal error\n", __func__);
2448c2ecf20Sopenharmony_ci		vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
2458c2ecf20Sopenharmony_ci		ret = RESUME_HOST;
2468c2ecf20Sopenharmony_ci	}
2478c2ecf20Sopenharmony_ci	return ret;
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_cistatic int _kvm_handle_hypcall(struct kvm_vcpu *vcpu)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	enum emulation_result er = EMULATE_DONE;
2538c2ecf20Sopenharmony_ci	int ret = RESUME_GUEST;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	vcpu->arch.is_hypcall = 0;
2568c2ecf20Sopenharmony_ci	er = _kvm_check_hypcall(vcpu);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	if (er == EMULATE_PV_HYPERCALL)
2598c2ecf20Sopenharmony_ci		ret = _kvm_handle_pv_hcall(vcpu);
2608c2ecf20Sopenharmony_ci	else if (er == EMULATE_DEBUG) {
2618c2ecf20Sopenharmony_ci		vcpu->run->exit_reason = KVM_EXIT_DEBUG;
2628c2ecf20Sopenharmony_ci		ret = RESUME_HOST;
2638c2ecf20Sopenharmony_ci	} else
2648c2ecf20Sopenharmony_ci		ret = RESUME_GUEST;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	return ret;
2678c2ecf20Sopenharmony_ci}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_cistatic int _kvm_handle_gcm(struct kvm_vcpu *vcpu)
2708c2ecf20Sopenharmony_ci{
2718c2ecf20Sopenharmony_ci	int ret, subcode;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	vcpu->arch.is_hypcall = 0;
2748c2ecf20Sopenharmony_ci	ret = RESUME_GUEST;
2758c2ecf20Sopenharmony_ci	subcode = (vcpu->arch.host_estat & KVM_ESTAT_ESUBCODE) >> KVM_ESTAT_ESUBCODE_SHIFT;
2768c2ecf20Sopenharmony_ci	if ((subcode != EXCSUBCODE_GCSC) && (subcode != EXCSUBCODE_GCHC)) {
2778c2ecf20Sopenharmony_ci		kvm_err("%s internal error\n", __func__);
2788c2ecf20Sopenharmony_ci		vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
2798c2ecf20Sopenharmony_ci		ret = RESUME_HOST;
2808c2ecf20Sopenharmony_ci	}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	return ret;
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci/**
2868c2ecf20Sopenharmony_ci * _kvm_handle_fpu_disabled() - Guest used fpu however it is disabled at host
2878c2ecf20Sopenharmony_ci * @vcpu:	Virtual CPU context.
2888c2ecf20Sopenharmony_ci *
2898c2ecf20Sopenharmony_ci * Handle when the guest attempts to use fpu which hasn't been allowed
2908c2ecf20Sopenharmony_ci * by the root context.
2918c2ecf20Sopenharmony_ci */
2928c2ecf20Sopenharmony_cistatic int _kvm_handle_fpu_disabled(struct kvm_vcpu *vcpu)
2938c2ecf20Sopenharmony_ci{
2948c2ecf20Sopenharmony_ci	struct kvm_run *run = vcpu->run;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	/*
2978c2ecf20Sopenharmony_ci	 * If guest FPU not present, the FPU operation should have been
2988c2ecf20Sopenharmony_ci	 * treated as a reserved instruction!
2998c2ecf20Sopenharmony_ci	 * If FPU already in use, we shouldn't get this at all.
3008c2ecf20Sopenharmony_ci	 */
3018c2ecf20Sopenharmony_ci	if (WARN_ON(!_kvm_guest_has_fpu(&vcpu->arch) ||
3028c2ecf20Sopenharmony_ci				vcpu->arch.aux_inuse & KVM_LARCH_FPU)) {
3038c2ecf20Sopenharmony_ci		kvm_err("%s internal error\n", __func__);
3048c2ecf20Sopenharmony_ci		run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
3058c2ecf20Sopenharmony_ci		return RESUME_HOST;
3068c2ecf20Sopenharmony_ci	}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	kvm_own_fpu(vcpu);
3098c2ecf20Sopenharmony_ci	return RESUME_GUEST;
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci/**
3138c2ecf20Sopenharmony_ci * _kvm_handle_lsx_disabled() - Guest used LSX while disabled in root.
3148c2ecf20Sopenharmony_ci * @vcpu:	Virtual CPU context.
3158c2ecf20Sopenharmony_ci *
3168c2ecf20Sopenharmony_ci * Handle when the guest attempts to use LSX when it is disabled in the root
3178c2ecf20Sopenharmony_ci * context.
3188c2ecf20Sopenharmony_ci */
3198c2ecf20Sopenharmony_cistatic int _kvm_handle_lsx_disabled(struct kvm_vcpu *vcpu)
3208c2ecf20Sopenharmony_ci{
3218c2ecf20Sopenharmony_ci	struct kvm_run *run = vcpu->run;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	/*
3248c2ecf20Sopenharmony_ci	 * If LSX not present or not exposed to guest, the LSX operation
3258c2ecf20Sopenharmony_ci	 * should have been treated as a reserved instruction!
3268c2ecf20Sopenharmony_ci	 * If LSX already in use, we shouldn't get this at all.
3278c2ecf20Sopenharmony_ci	 */
3288c2ecf20Sopenharmony_ci	if (!_kvm_guest_has_lsx(&vcpu->arch) ||
3298c2ecf20Sopenharmony_ci	    !(kvm_read_gcsr_euen() & KVM_EUEN_LSXEN) ||
3308c2ecf20Sopenharmony_ci	    vcpu->arch.aux_inuse & KVM_LARCH_LSX) {
3318c2ecf20Sopenharmony_ci		kvm_err("%s internal error, lsx %d guest euen %llx aux %x",
3328c2ecf20Sopenharmony_ci			__func__, _kvm_guest_has_lsx(&vcpu->arch),
3338c2ecf20Sopenharmony_ci			kvm_read_gcsr_euen(), vcpu->arch.aux_inuse);
3348c2ecf20Sopenharmony_ci		run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
3358c2ecf20Sopenharmony_ci		return RESUME_HOST;
3368c2ecf20Sopenharmony_ci	}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	kvm_own_lsx(vcpu);
3398c2ecf20Sopenharmony_ci	return RESUME_GUEST;
3408c2ecf20Sopenharmony_ci}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_cibool _kvm_guest_has_lasx(struct kvm_vcpu *vcpu)
3438c2ecf20Sopenharmony_ci{
3448c2ecf20Sopenharmony_ci      return cpu_has_lasx && vcpu->arch.lsx_enabled && vcpu->kvm->arch.cpucfg_lasx;
3458c2ecf20Sopenharmony_ci}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci/**
3488c2ecf20Sopenharmony_ci * _kvm_handle_lasx_disabled() - Guest used LASX while disabled in root.
3498c2ecf20Sopenharmony_ci * @vcpu:	Virtual CPU context.
3508c2ecf20Sopenharmony_ci *
3518c2ecf20Sopenharmony_ci * Handle when the guest attempts to use LASX when it is disabled in the root
3528c2ecf20Sopenharmony_ci * context.
3538c2ecf20Sopenharmony_ci */
3548c2ecf20Sopenharmony_cistatic int _kvm_handle_lasx_disabled(struct kvm_vcpu *vcpu)
3558c2ecf20Sopenharmony_ci{
3568c2ecf20Sopenharmony_ci	struct kvm_run *run = vcpu->run;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	/*
3598c2ecf20Sopenharmony_ci	 * If LASX not present or not exposed to guest, the LASX operation
3608c2ecf20Sopenharmony_ci	 * should have been treated as a reserved instruction!
3618c2ecf20Sopenharmony_ci	 * If LASX already in use, we shouldn't get this at all.
3628c2ecf20Sopenharmony_ci	 */
3638c2ecf20Sopenharmony_ci	if (!_kvm_guest_has_lasx(vcpu) ||
3648c2ecf20Sopenharmony_ci	    !(kvm_read_gcsr_euen() & KVM_EUEN_LSXEN) ||
3658c2ecf20Sopenharmony_ci	    !(kvm_read_gcsr_euen() & KVM_EUEN_LASXEN) ||
3668c2ecf20Sopenharmony_ci	    vcpu->arch.aux_inuse & KVM_LARCH_LASX) {
3678c2ecf20Sopenharmony_ci		kvm_err("%s internal error, lasx %d guest euen %llx aux %x",
3688c2ecf20Sopenharmony_ci			__func__, _kvm_guest_has_lasx(vcpu),
3698c2ecf20Sopenharmony_ci			kvm_read_gcsr_euen(), vcpu->arch.aux_inuse);
3708c2ecf20Sopenharmony_ci		run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
3718c2ecf20Sopenharmony_ci		return RESUME_HOST;
3728c2ecf20Sopenharmony_ci	}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	kvm_own_lasx(vcpu);
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	return RESUME_GUEST;
3778c2ecf20Sopenharmony_ci}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci/**
3808c2ecf20Sopenharmony_ci * _kvm_handle_fpu_disabled() - Guest used lbt however it is disabled at host
3818c2ecf20Sopenharmony_ci * @vcpu:       Virtual CPU context.
3828c2ecf20Sopenharmony_ci *
3838c2ecf20Sopenharmony_ci * Handle when the guest attempts to use lbt which hasn't been allowed
3848c2ecf20Sopenharmony_ci * by the root context.
3858c2ecf20Sopenharmony_ci */
3868c2ecf20Sopenharmony_cistatic int _kvm_handle_lbt_disabled(struct kvm_vcpu *vcpu)
3878c2ecf20Sopenharmony_ci{
3888c2ecf20Sopenharmony_ci	struct kvm_run *run = vcpu->run;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	/*
3918c2ecf20Sopenharmony_ci	 * If guest LBT not present, the LBT operation should have been
3928c2ecf20Sopenharmony_ci	 * treated as a reserved instruction!
3938c2ecf20Sopenharmony_ci	 * If LBT already in use, we shouldn't get this at all.
3948c2ecf20Sopenharmony_ci	 */
3958c2ecf20Sopenharmony_ci	if (vcpu->arch.aux_inuse & KVM_LARCH_LBT) {
3968c2ecf20Sopenharmony_ci		kvm_err("%s internal error\n", __func__);
3978c2ecf20Sopenharmony_ci		run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
3988c2ecf20Sopenharmony_ci		return RESUME_HOST;
3998c2ecf20Sopenharmony_ci	}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	kvm_own_lbt(vcpu);
4028c2ecf20Sopenharmony_ci	return RESUME_GUEST;
4038c2ecf20Sopenharmony_ci}
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_cistatic int _kvm_handle_read_fault(struct kvm_vcpu *vcpu)
4068c2ecf20Sopenharmony_ci{
4078c2ecf20Sopenharmony_ci	struct kvm_run *run = vcpu->run;
4088c2ecf20Sopenharmony_ci	ulong badv = vcpu->arch.badv;
4098c2ecf20Sopenharmony_ci	larch_inst inst;
4108c2ecf20Sopenharmony_ci	enum emulation_result er = EMULATE_DONE;
4118c2ecf20Sopenharmony_ci	int ret = RESUME_GUEST;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	if (kvm_handle_mm_fault(vcpu, badv, false)) {
4148c2ecf20Sopenharmony_ci		/* A code fetch fault doesn't count as an MMIO */
4158c2ecf20Sopenharmony_ci		if (kvm_is_ifetch_fault(&vcpu->arch)) {
4168c2ecf20Sopenharmony_ci			kvm_err("%s ifetch error addr:%lx\n", __func__, badv);
4178c2ecf20Sopenharmony_ci			run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
4188c2ecf20Sopenharmony_ci			return RESUME_HOST;
4198c2ecf20Sopenharmony_ci		}
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci		/* Treat as MMIO */
4228c2ecf20Sopenharmony_ci		inst.word =  vcpu->arch.badi;
4238c2ecf20Sopenharmony_ci		er = _kvm_emu_mmio_read(vcpu, inst);
4248c2ecf20Sopenharmony_ci		if (er == EMULATE_FAIL) {
4258c2ecf20Sopenharmony_ci			kvm_err("Guest Emulate Load failed: PC: %#lx, BadVaddr: %#lx\n",
4268c2ecf20Sopenharmony_ci				vcpu->arch.pc, badv);
4278c2ecf20Sopenharmony_ci			run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
4288c2ecf20Sopenharmony_ci		}
4298c2ecf20Sopenharmony_ci	}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	if (er == EMULATE_DONE) {
4328c2ecf20Sopenharmony_ci		ret = RESUME_GUEST;
4338c2ecf20Sopenharmony_ci	} else if (er == EMULATE_DO_MMIO) {
4348c2ecf20Sopenharmony_ci		run->exit_reason = KVM_EXIT_MMIO;
4358c2ecf20Sopenharmony_ci		ret = RESUME_HOST;
4368c2ecf20Sopenharmony_ci	} else {
4378c2ecf20Sopenharmony_ci		run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
4388c2ecf20Sopenharmony_ci		ret = RESUME_HOST;
4398c2ecf20Sopenharmony_ci	}
4408c2ecf20Sopenharmony_ci	return ret;
4418c2ecf20Sopenharmony_ci}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_cistatic int _kvm_handle_write_fault(struct kvm_vcpu *vcpu)
4448c2ecf20Sopenharmony_ci{
4458c2ecf20Sopenharmony_ci	struct kvm_run *run = vcpu->run;
4468c2ecf20Sopenharmony_ci	ulong badv = vcpu->arch.badv;
4478c2ecf20Sopenharmony_ci	larch_inst inst;
4488c2ecf20Sopenharmony_ci	enum emulation_result er = EMULATE_DONE;
4498c2ecf20Sopenharmony_ci	int ret = RESUME_GUEST;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	if (kvm_handle_mm_fault(vcpu, badv, true)) {
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci		/* Treat as MMIO */
4548c2ecf20Sopenharmony_ci		inst.word =  vcpu->arch.badi;
4558c2ecf20Sopenharmony_ci		er = _kvm_emu_mmio_write(vcpu, inst);
4568c2ecf20Sopenharmony_ci		if (er == EMULATE_FAIL) {
4578c2ecf20Sopenharmony_ci			kvm_err("Guest Emulate Store failed: PC:  %#lx, BadVaddr: %#lx\n",
4588c2ecf20Sopenharmony_ci				vcpu->arch.pc, badv);
4598c2ecf20Sopenharmony_ci			run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
4608c2ecf20Sopenharmony_ci		}
4618c2ecf20Sopenharmony_ci	}
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	if (er == EMULATE_DONE) {
4648c2ecf20Sopenharmony_ci		ret = RESUME_GUEST;
4658c2ecf20Sopenharmony_ci	} else if (er == EMULATE_DO_MMIO) {
4668c2ecf20Sopenharmony_ci		run->exit_reason = KVM_EXIT_MMIO;
4678c2ecf20Sopenharmony_ci		ret = RESUME_HOST;
4688c2ecf20Sopenharmony_ci	} else {
4698c2ecf20Sopenharmony_ci		run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
4708c2ecf20Sopenharmony_ci		ret = RESUME_HOST;
4718c2ecf20Sopenharmony_ci	}
4728c2ecf20Sopenharmony_ci	return ret;
4738c2ecf20Sopenharmony_ci}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_cistatic int _kvm_handle_debug(struct kvm_vcpu *vcpu)
4768c2ecf20Sopenharmony_ci{
4778c2ecf20Sopenharmony_ci	uint32_t fwps, mwps;
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	fwps = kvm_csr_readq(KVM_CSR_FWPS);
4808c2ecf20Sopenharmony_ci	mwps = kvm_csr_readq(KVM_CSR_MWPS);
4818c2ecf20Sopenharmony_ci	if (fwps & 0xff)
4828c2ecf20Sopenharmony_ci		kvm_csr_writeq(fwps, KVM_CSR_FWPS);
4838c2ecf20Sopenharmony_ci	if (mwps & 0xff)
4848c2ecf20Sopenharmony_ci		kvm_csr_writeq(mwps, KVM_CSR_MWPS);
4858c2ecf20Sopenharmony_ci	vcpu->run->debug.arch.exception = EXCCODE_WATCH;
4868c2ecf20Sopenharmony_ci	vcpu->run->debug.arch.fwps = fwps;
4878c2ecf20Sopenharmony_ci	vcpu->run->debug.arch.mwps = mwps;
4888c2ecf20Sopenharmony_ci	vcpu->run->exit_reason = KVM_EXIT_DEBUG;
4898c2ecf20Sopenharmony_ci	return RESUME_HOST;
4908c2ecf20Sopenharmony_ci}
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_cistatic exit_handle_fn _kvm_fault_tables[EXCCODE_INT_START] = {
4938c2ecf20Sopenharmony_ci	[EXCCODE_TLBL]		= _kvm_handle_read_fault,
4948c2ecf20Sopenharmony_ci	[EXCCODE_TLBS]		= _kvm_handle_write_fault,
4958c2ecf20Sopenharmony_ci	[EXCCODE_TLBI]		= _kvm_handle_read_fault,
4968c2ecf20Sopenharmony_ci	[EXCCODE_TLBM]		= _kvm_handle_write_fault,
4978c2ecf20Sopenharmony_ci	[EXCCODE_TLBNR]		= _kvm_handle_read_fault,
4988c2ecf20Sopenharmony_ci	[EXCCODE_TLBNX]		= _kvm_handle_read_fault,
4998c2ecf20Sopenharmony_ci	[EXCCODE_FPDIS]		= _kvm_handle_fpu_disabled,
5008c2ecf20Sopenharmony_ci	[EXCCODE_LSXDIS]	= _kvm_handle_lsx_disabled,
5018c2ecf20Sopenharmony_ci	[EXCCODE_LASXDIS]	= _kvm_handle_lasx_disabled,
5028c2ecf20Sopenharmony_ci	[EXCCODE_WATCH]		= _kvm_handle_debug,
5038c2ecf20Sopenharmony_ci	[EXCCODE_GSPR]		= _kvm_handle_gspr,
5048c2ecf20Sopenharmony_ci	[EXCCODE_HVC]		= _kvm_handle_hypcall,
5058c2ecf20Sopenharmony_ci	[EXCCODE_GCM]		= _kvm_handle_gcm,
5068c2ecf20Sopenharmony_ci	[EXCCODE_BTDIS]		= _kvm_handle_lbt_disabled,
5078c2ecf20Sopenharmony_ci};
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ciint _kvm_handle_fault(struct kvm_vcpu *vcpu, int fault)
5108c2ecf20Sopenharmony_ci{
5118c2ecf20Sopenharmony_ci	return _kvm_fault_tables[fault](vcpu);
5128c2ecf20Sopenharmony_ci}
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_civoid _kvm_init_fault(void)
5158c2ecf20Sopenharmony_ci{
5168c2ecf20Sopenharmony_ci	int i;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	for (i = 0; i < EXCCODE_INT_START; i++)
5198c2ecf20Sopenharmony_ci		if (!_kvm_fault_tables[i])
5208c2ecf20Sopenharmony_ci			_kvm_fault_tables[i] = _kvm_fault_ni;
5218c2ecf20Sopenharmony_ci}
522