162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2019 Western Digital Corporation or its affiliates.
462306a36Sopenharmony_ci * Copyright (c) 2022 Ventana Micro Systems Inc.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/bitops.h>
862306a36Sopenharmony_ci#include <linux/kvm_host.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#define INSN_OPCODE_MASK	0x007c
1162306a36Sopenharmony_ci#define INSN_OPCODE_SHIFT	2
1262306a36Sopenharmony_ci#define INSN_OPCODE_SYSTEM	28
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#define INSN_MASK_WFI		0xffffffff
1562306a36Sopenharmony_ci#define INSN_MATCH_WFI		0x10500073
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define INSN_MATCH_CSRRW	0x1073
1862306a36Sopenharmony_ci#define INSN_MASK_CSRRW		0x707f
1962306a36Sopenharmony_ci#define INSN_MATCH_CSRRS	0x2073
2062306a36Sopenharmony_ci#define INSN_MASK_CSRRS		0x707f
2162306a36Sopenharmony_ci#define INSN_MATCH_CSRRC	0x3073
2262306a36Sopenharmony_ci#define INSN_MASK_CSRRC		0x707f
2362306a36Sopenharmony_ci#define INSN_MATCH_CSRRWI	0x5073
2462306a36Sopenharmony_ci#define INSN_MASK_CSRRWI	0x707f
2562306a36Sopenharmony_ci#define INSN_MATCH_CSRRSI	0x6073
2662306a36Sopenharmony_ci#define INSN_MASK_CSRRSI	0x707f
2762306a36Sopenharmony_ci#define INSN_MATCH_CSRRCI	0x7073
2862306a36Sopenharmony_ci#define INSN_MASK_CSRRCI	0x707f
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define INSN_MATCH_LB		0x3
3162306a36Sopenharmony_ci#define INSN_MASK_LB		0x707f
3262306a36Sopenharmony_ci#define INSN_MATCH_LH		0x1003
3362306a36Sopenharmony_ci#define INSN_MASK_LH		0x707f
3462306a36Sopenharmony_ci#define INSN_MATCH_LW		0x2003
3562306a36Sopenharmony_ci#define INSN_MASK_LW		0x707f
3662306a36Sopenharmony_ci#define INSN_MATCH_LD		0x3003
3762306a36Sopenharmony_ci#define INSN_MASK_LD		0x707f
3862306a36Sopenharmony_ci#define INSN_MATCH_LBU		0x4003
3962306a36Sopenharmony_ci#define INSN_MASK_LBU		0x707f
4062306a36Sopenharmony_ci#define INSN_MATCH_LHU		0x5003
4162306a36Sopenharmony_ci#define INSN_MASK_LHU		0x707f
4262306a36Sopenharmony_ci#define INSN_MATCH_LWU		0x6003
4362306a36Sopenharmony_ci#define INSN_MASK_LWU		0x707f
4462306a36Sopenharmony_ci#define INSN_MATCH_SB		0x23
4562306a36Sopenharmony_ci#define INSN_MASK_SB		0x707f
4662306a36Sopenharmony_ci#define INSN_MATCH_SH		0x1023
4762306a36Sopenharmony_ci#define INSN_MASK_SH		0x707f
4862306a36Sopenharmony_ci#define INSN_MATCH_SW		0x2023
4962306a36Sopenharmony_ci#define INSN_MASK_SW		0x707f
5062306a36Sopenharmony_ci#define INSN_MATCH_SD		0x3023
5162306a36Sopenharmony_ci#define INSN_MASK_SD		0x707f
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci#define INSN_MATCH_C_LD		0x6000
5462306a36Sopenharmony_ci#define INSN_MASK_C_LD		0xe003
5562306a36Sopenharmony_ci#define INSN_MATCH_C_SD		0xe000
5662306a36Sopenharmony_ci#define INSN_MASK_C_SD		0xe003
5762306a36Sopenharmony_ci#define INSN_MATCH_C_LW		0x4000
5862306a36Sopenharmony_ci#define INSN_MASK_C_LW		0xe003
5962306a36Sopenharmony_ci#define INSN_MATCH_C_SW		0xc000
6062306a36Sopenharmony_ci#define INSN_MASK_C_SW		0xe003
6162306a36Sopenharmony_ci#define INSN_MATCH_C_LDSP	0x6002
6262306a36Sopenharmony_ci#define INSN_MASK_C_LDSP	0xe003
6362306a36Sopenharmony_ci#define INSN_MATCH_C_SDSP	0xe002
6462306a36Sopenharmony_ci#define INSN_MASK_C_SDSP	0xe003
6562306a36Sopenharmony_ci#define INSN_MATCH_C_LWSP	0x4002
6662306a36Sopenharmony_ci#define INSN_MASK_C_LWSP	0xe003
6762306a36Sopenharmony_ci#define INSN_MATCH_C_SWSP	0xc002
6862306a36Sopenharmony_ci#define INSN_MASK_C_SWSP	0xe003
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci#define INSN_16BIT_MASK		0x3
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci#define INSN_IS_16BIT(insn)	(((insn) & INSN_16BIT_MASK) != INSN_16BIT_MASK)
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci#define INSN_LEN(insn)		(INSN_IS_16BIT(insn) ? 2 : 4)
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci#ifdef CONFIG_64BIT
7762306a36Sopenharmony_ci#define LOG_REGBYTES		3
7862306a36Sopenharmony_ci#else
7962306a36Sopenharmony_ci#define LOG_REGBYTES		2
8062306a36Sopenharmony_ci#endif
8162306a36Sopenharmony_ci#define REGBYTES		(1 << LOG_REGBYTES)
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci#define SH_RD			7
8462306a36Sopenharmony_ci#define SH_RS1			15
8562306a36Sopenharmony_ci#define SH_RS2			20
8662306a36Sopenharmony_ci#define SH_RS2C			2
8762306a36Sopenharmony_ci#define MASK_RX			0x1f
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci#define RV_X(x, s, n)		(((x) >> (s)) & ((1 << (n)) - 1))
9062306a36Sopenharmony_ci#define RVC_LW_IMM(x)		((RV_X(x, 6, 1) << 2) | \
9162306a36Sopenharmony_ci				 (RV_X(x, 10, 3) << 3) | \
9262306a36Sopenharmony_ci				 (RV_X(x, 5, 1) << 6))
9362306a36Sopenharmony_ci#define RVC_LD_IMM(x)		((RV_X(x, 10, 3) << 3) | \
9462306a36Sopenharmony_ci				 (RV_X(x, 5, 2) << 6))
9562306a36Sopenharmony_ci#define RVC_LWSP_IMM(x)		((RV_X(x, 4, 3) << 2) | \
9662306a36Sopenharmony_ci				 (RV_X(x, 12, 1) << 5) | \
9762306a36Sopenharmony_ci				 (RV_X(x, 2, 2) << 6))
9862306a36Sopenharmony_ci#define RVC_LDSP_IMM(x)		((RV_X(x, 5, 2) << 3) | \
9962306a36Sopenharmony_ci				 (RV_X(x, 12, 1) << 5) | \
10062306a36Sopenharmony_ci				 (RV_X(x, 2, 3) << 6))
10162306a36Sopenharmony_ci#define RVC_SWSP_IMM(x)		((RV_X(x, 9, 4) << 2) | \
10262306a36Sopenharmony_ci				 (RV_X(x, 7, 2) << 6))
10362306a36Sopenharmony_ci#define RVC_SDSP_IMM(x)		((RV_X(x, 10, 3) << 3) | \
10462306a36Sopenharmony_ci				 (RV_X(x, 7, 3) << 6))
10562306a36Sopenharmony_ci#define RVC_RS1S(insn)		(8 + RV_X(insn, SH_RD, 3))
10662306a36Sopenharmony_ci#define RVC_RS2S(insn)		(8 + RV_X(insn, SH_RS2C, 3))
10762306a36Sopenharmony_ci#define RVC_RS2(insn)		RV_X(insn, SH_RS2C, 5)
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci#define SHIFT_RIGHT(x, y)		\
11062306a36Sopenharmony_ci	((y) < 0 ? ((x) << -(y)) : ((x) >> (y)))
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci#define REG_MASK			\
11362306a36Sopenharmony_ci	((1 << (5 + LOG_REGBYTES)) - (1 << LOG_REGBYTES))
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci#define REG_OFFSET(insn, pos)		\
11662306a36Sopenharmony_ci	(SHIFT_RIGHT((insn), (pos) - LOG_REGBYTES) & REG_MASK)
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci#define REG_PTR(insn, pos, regs)	\
11962306a36Sopenharmony_ci	((ulong *)((ulong)(regs) + REG_OFFSET(insn, pos)))
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci#define GET_FUNCT3(insn)	(((insn) >> 12) & 7)
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci#define GET_RS1(insn, regs)	(*REG_PTR(insn, SH_RS1, regs))
12462306a36Sopenharmony_ci#define GET_RS2(insn, regs)	(*REG_PTR(insn, SH_RS2, regs))
12562306a36Sopenharmony_ci#define GET_RS1S(insn, regs)	(*REG_PTR(RVC_RS1S(insn), 0, regs))
12662306a36Sopenharmony_ci#define GET_RS2S(insn, regs)	(*REG_PTR(RVC_RS2S(insn), 0, regs))
12762306a36Sopenharmony_ci#define GET_RS2C(insn, regs)	(*REG_PTR(insn, SH_RS2C, regs))
12862306a36Sopenharmony_ci#define GET_SP(regs)		(*REG_PTR(2, 0, regs))
12962306a36Sopenharmony_ci#define SET_RD(insn, regs, val)	(*REG_PTR(insn, SH_RD, regs) = (val))
13062306a36Sopenharmony_ci#define IMM_I(insn)		((s32)(insn) >> 20)
13162306a36Sopenharmony_ci#define IMM_S(insn)		(((s32)(insn) >> 25 << 5) | \
13262306a36Sopenharmony_ci				 (s32)(((insn) >> 7) & 0x1f))
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cistruct insn_func {
13562306a36Sopenharmony_ci	unsigned long mask;
13662306a36Sopenharmony_ci	unsigned long match;
13762306a36Sopenharmony_ci	/*
13862306a36Sopenharmony_ci	 * Possible return values are as follows:
13962306a36Sopenharmony_ci	 * 1) Returns < 0 for error case
14062306a36Sopenharmony_ci	 * 2) Returns 0 for exit to user-space
14162306a36Sopenharmony_ci	 * 3) Returns 1 to continue with next sepc
14262306a36Sopenharmony_ci	 * 4) Returns 2 to continue with same sepc
14362306a36Sopenharmony_ci	 * 5) Returns 3 to inject illegal instruction trap and continue
14462306a36Sopenharmony_ci	 * 6) Returns 4 to inject virtual instruction trap and continue
14562306a36Sopenharmony_ci	 *
14662306a36Sopenharmony_ci	 * Use enum kvm_insn_return for return values
14762306a36Sopenharmony_ci	 */
14862306a36Sopenharmony_ci	int (*func)(struct kvm_vcpu *vcpu, struct kvm_run *run, ulong insn);
14962306a36Sopenharmony_ci};
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic int truly_illegal_insn(struct kvm_vcpu *vcpu, struct kvm_run *run,
15262306a36Sopenharmony_ci			      ulong insn)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	struct kvm_cpu_trap utrap = { 0 };
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	/* Redirect trap to Guest VCPU */
15762306a36Sopenharmony_ci	utrap.sepc = vcpu->arch.guest_context.sepc;
15862306a36Sopenharmony_ci	utrap.scause = EXC_INST_ILLEGAL;
15962306a36Sopenharmony_ci	utrap.stval = insn;
16062306a36Sopenharmony_ci	utrap.htval = 0;
16162306a36Sopenharmony_ci	utrap.htinst = 0;
16262306a36Sopenharmony_ci	kvm_riscv_vcpu_trap_redirect(vcpu, &utrap);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	return 1;
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cistatic int truly_virtual_insn(struct kvm_vcpu *vcpu, struct kvm_run *run,
16862306a36Sopenharmony_ci			      ulong insn)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	struct kvm_cpu_trap utrap = { 0 };
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	/* Redirect trap to Guest VCPU */
17362306a36Sopenharmony_ci	utrap.sepc = vcpu->arch.guest_context.sepc;
17462306a36Sopenharmony_ci	utrap.scause = EXC_VIRTUAL_INST_FAULT;
17562306a36Sopenharmony_ci	utrap.stval = insn;
17662306a36Sopenharmony_ci	utrap.htval = 0;
17762306a36Sopenharmony_ci	utrap.htinst = 0;
17862306a36Sopenharmony_ci	kvm_riscv_vcpu_trap_redirect(vcpu, &utrap);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	return 1;
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci/**
18462306a36Sopenharmony_ci * kvm_riscv_vcpu_wfi -- Emulate wait for interrupt (WFI) behaviour
18562306a36Sopenharmony_ci *
18662306a36Sopenharmony_ci * @vcpu: The VCPU pointer
18762306a36Sopenharmony_ci */
18862306a36Sopenharmony_civoid kvm_riscv_vcpu_wfi(struct kvm_vcpu *vcpu)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	if (!kvm_arch_vcpu_runnable(vcpu)) {
19162306a36Sopenharmony_ci		kvm_vcpu_srcu_read_unlock(vcpu);
19262306a36Sopenharmony_ci		kvm_vcpu_halt(vcpu);
19362306a36Sopenharmony_ci		kvm_vcpu_srcu_read_lock(vcpu);
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic int wfi_insn(struct kvm_vcpu *vcpu, struct kvm_run *run, ulong insn)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	vcpu->stat.wfi_exit_stat++;
20062306a36Sopenharmony_ci	kvm_riscv_vcpu_wfi(vcpu);
20162306a36Sopenharmony_ci	return KVM_INSN_CONTINUE_NEXT_SEPC;
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistruct csr_func {
20562306a36Sopenharmony_ci	unsigned int base;
20662306a36Sopenharmony_ci	unsigned int count;
20762306a36Sopenharmony_ci	/*
20862306a36Sopenharmony_ci	 * Possible return values are as same as "func" callback in
20962306a36Sopenharmony_ci	 * "struct insn_func".
21062306a36Sopenharmony_ci	 */
21162306a36Sopenharmony_ci	int (*func)(struct kvm_vcpu *vcpu, unsigned int csr_num,
21262306a36Sopenharmony_ci		    unsigned long *val, unsigned long new_val,
21362306a36Sopenharmony_ci		    unsigned long wr_mask);
21462306a36Sopenharmony_ci};
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_cistatic const struct csr_func csr_funcs[] = {
21762306a36Sopenharmony_ci	KVM_RISCV_VCPU_AIA_CSR_FUNCS
21862306a36Sopenharmony_ci	KVM_RISCV_VCPU_HPMCOUNTER_CSR_FUNCS
21962306a36Sopenharmony_ci};
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci/**
22262306a36Sopenharmony_ci * kvm_riscv_vcpu_csr_return -- Handle CSR read/write after user space
22362306a36Sopenharmony_ci *				emulation or in-kernel emulation
22462306a36Sopenharmony_ci *
22562306a36Sopenharmony_ci * @vcpu: The VCPU pointer
22662306a36Sopenharmony_ci * @run:  The VCPU run struct containing the CSR data
22762306a36Sopenharmony_ci *
22862306a36Sopenharmony_ci * Returns > 0 upon failure and 0 upon success
22962306a36Sopenharmony_ci */
23062306a36Sopenharmony_ciint kvm_riscv_vcpu_csr_return(struct kvm_vcpu *vcpu, struct kvm_run *run)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	ulong insn;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	if (vcpu->arch.csr_decode.return_handled)
23562306a36Sopenharmony_ci		return 0;
23662306a36Sopenharmony_ci	vcpu->arch.csr_decode.return_handled = 1;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	/* Update destination register for CSR reads */
23962306a36Sopenharmony_ci	insn = vcpu->arch.csr_decode.insn;
24062306a36Sopenharmony_ci	if ((insn >> SH_RD) & MASK_RX)
24162306a36Sopenharmony_ci		SET_RD(insn, &vcpu->arch.guest_context,
24262306a36Sopenharmony_ci		       run->riscv_csr.ret_value);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	/* Move to next instruction */
24562306a36Sopenharmony_ci	vcpu->arch.guest_context.sepc += INSN_LEN(insn);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	return 0;
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_cistatic int csr_insn(struct kvm_vcpu *vcpu, struct kvm_run *run, ulong insn)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	int i, rc = KVM_INSN_ILLEGAL_TRAP;
25362306a36Sopenharmony_ci	unsigned int csr_num = insn >> SH_RS2;
25462306a36Sopenharmony_ci	unsigned int rs1_num = (insn >> SH_RS1) & MASK_RX;
25562306a36Sopenharmony_ci	ulong rs1_val = GET_RS1(insn, &vcpu->arch.guest_context);
25662306a36Sopenharmony_ci	const struct csr_func *tcfn, *cfn = NULL;
25762306a36Sopenharmony_ci	ulong val = 0, wr_mask = 0, new_val = 0;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	/* Decode the CSR instruction */
26062306a36Sopenharmony_ci	switch (GET_FUNCT3(insn)) {
26162306a36Sopenharmony_ci	case GET_FUNCT3(INSN_MATCH_CSRRW):
26262306a36Sopenharmony_ci		wr_mask = -1UL;
26362306a36Sopenharmony_ci		new_val = rs1_val;
26462306a36Sopenharmony_ci		break;
26562306a36Sopenharmony_ci	case GET_FUNCT3(INSN_MATCH_CSRRS):
26662306a36Sopenharmony_ci		wr_mask = rs1_val;
26762306a36Sopenharmony_ci		new_val = -1UL;
26862306a36Sopenharmony_ci		break;
26962306a36Sopenharmony_ci	case GET_FUNCT3(INSN_MATCH_CSRRC):
27062306a36Sopenharmony_ci		wr_mask = rs1_val;
27162306a36Sopenharmony_ci		new_val = 0;
27262306a36Sopenharmony_ci		break;
27362306a36Sopenharmony_ci	case GET_FUNCT3(INSN_MATCH_CSRRWI):
27462306a36Sopenharmony_ci		wr_mask = -1UL;
27562306a36Sopenharmony_ci		new_val = rs1_num;
27662306a36Sopenharmony_ci		break;
27762306a36Sopenharmony_ci	case GET_FUNCT3(INSN_MATCH_CSRRSI):
27862306a36Sopenharmony_ci		wr_mask = rs1_num;
27962306a36Sopenharmony_ci		new_val = -1UL;
28062306a36Sopenharmony_ci		break;
28162306a36Sopenharmony_ci	case GET_FUNCT3(INSN_MATCH_CSRRCI):
28262306a36Sopenharmony_ci		wr_mask = rs1_num;
28362306a36Sopenharmony_ci		new_val = 0;
28462306a36Sopenharmony_ci		break;
28562306a36Sopenharmony_ci	default:
28662306a36Sopenharmony_ci		return rc;
28762306a36Sopenharmony_ci	}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	/* Save instruction decode info */
29062306a36Sopenharmony_ci	vcpu->arch.csr_decode.insn = insn;
29162306a36Sopenharmony_ci	vcpu->arch.csr_decode.return_handled = 0;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	/* Update CSR details in kvm_run struct */
29462306a36Sopenharmony_ci	run->riscv_csr.csr_num = csr_num;
29562306a36Sopenharmony_ci	run->riscv_csr.new_value = new_val;
29662306a36Sopenharmony_ci	run->riscv_csr.write_mask = wr_mask;
29762306a36Sopenharmony_ci	run->riscv_csr.ret_value = 0;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	/* Find in-kernel CSR function */
30062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(csr_funcs); i++) {
30162306a36Sopenharmony_ci		tcfn = &csr_funcs[i];
30262306a36Sopenharmony_ci		if ((tcfn->base <= csr_num) &&
30362306a36Sopenharmony_ci		    (csr_num < (tcfn->base + tcfn->count))) {
30462306a36Sopenharmony_ci			cfn = tcfn;
30562306a36Sopenharmony_ci			break;
30662306a36Sopenharmony_ci		}
30762306a36Sopenharmony_ci	}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	/* First try in-kernel CSR emulation */
31062306a36Sopenharmony_ci	if (cfn && cfn->func) {
31162306a36Sopenharmony_ci		rc = cfn->func(vcpu, csr_num, &val, new_val, wr_mask);
31262306a36Sopenharmony_ci		if (rc > KVM_INSN_EXIT_TO_USER_SPACE) {
31362306a36Sopenharmony_ci			if (rc == KVM_INSN_CONTINUE_NEXT_SEPC) {
31462306a36Sopenharmony_ci				run->riscv_csr.ret_value = val;
31562306a36Sopenharmony_ci				vcpu->stat.csr_exit_kernel++;
31662306a36Sopenharmony_ci				kvm_riscv_vcpu_csr_return(vcpu, run);
31762306a36Sopenharmony_ci				rc = KVM_INSN_CONTINUE_SAME_SEPC;
31862306a36Sopenharmony_ci			}
31962306a36Sopenharmony_ci			return rc;
32062306a36Sopenharmony_ci		}
32162306a36Sopenharmony_ci	}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	/* Exit to user-space for CSR emulation */
32462306a36Sopenharmony_ci	if (rc <= KVM_INSN_EXIT_TO_USER_SPACE) {
32562306a36Sopenharmony_ci		vcpu->stat.csr_exit_user++;
32662306a36Sopenharmony_ci		run->exit_reason = KVM_EXIT_RISCV_CSR;
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	return rc;
33062306a36Sopenharmony_ci}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_cistatic const struct insn_func system_opcode_funcs[] = {
33362306a36Sopenharmony_ci	{
33462306a36Sopenharmony_ci		.mask  = INSN_MASK_CSRRW,
33562306a36Sopenharmony_ci		.match = INSN_MATCH_CSRRW,
33662306a36Sopenharmony_ci		.func  = csr_insn,
33762306a36Sopenharmony_ci	},
33862306a36Sopenharmony_ci	{
33962306a36Sopenharmony_ci		.mask  = INSN_MASK_CSRRS,
34062306a36Sopenharmony_ci		.match = INSN_MATCH_CSRRS,
34162306a36Sopenharmony_ci		.func  = csr_insn,
34262306a36Sopenharmony_ci	},
34362306a36Sopenharmony_ci	{
34462306a36Sopenharmony_ci		.mask  = INSN_MASK_CSRRC,
34562306a36Sopenharmony_ci		.match = INSN_MATCH_CSRRC,
34662306a36Sopenharmony_ci		.func  = csr_insn,
34762306a36Sopenharmony_ci	},
34862306a36Sopenharmony_ci	{
34962306a36Sopenharmony_ci		.mask  = INSN_MASK_CSRRWI,
35062306a36Sopenharmony_ci		.match = INSN_MATCH_CSRRWI,
35162306a36Sopenharmony_ci		.func  = csr_insn,
35262306a36Sopenharmony_ci	},
35362306a36Sopenharmony_ci	{
35462306a36Sopenharmony_ci		.mask  = INSN_MASK_CSRRSI,
35562306a36Sopenharmony_ci		.match = INSN_MATCH_CSRRSI,
35662306a36Sopenharmony_ci		.func  = csr_insn,
35762306a36Sopenharmony_ci	},
35862306a36Sopenharmony_ci	{
35962306a36Sopenharmony_ci		.mask  = INSN_MASK_CSRRCI,
36062306a36Sopenharmony_ci		.match = INSN_MATCH_CSRRCI,
36162306a36Sopenharmony_ci		.func  = csr_insn,
36262306a36Sopenharmony_ci	},
36362306a36Sopenharmony_ci	{
36462306a36Sopenharmony_ci		.mask  = INSN_MASK_WFI,
36562306a36Sopenharmony_ci		.match = INSN_MATCH_WFI,
36662306a36Sopenharmony_ci		.func  = wfi_insn,
36762306a36Sopenharmony_ci	},
36862306a36Sopenharmony_ci};
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cistatic int system_opcode_insn(struct kvm_vcpu *vcpu, struct kvm_run *run,
37162306a36Sopenharmony_ci			      ulong insn)
37262306a36Sopenharmony_ci{
37362306a36Sopenharmony_ci	int i, rc = KVM_INSN_ILLEGAL_TRAP;
37462306a36Sopenharmony_ci	const struct insn_func *ifn;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(system_opcode_funcs); i++) {
37762306a36Sopenharmony_ci		ifn = &system_opcode_funcs[i];
37862306a36Sopenharmony_ci		if ((insn & ifn->mask) == ifn->match) {
37962306a36Sopenharmony_ci			rc = ifn->func(vcpu, run, insn);
38062306a36Sopenharmony_ci			break;
38162306a36Sopenharmony_ci		}
38262306a36Sopenharmony_ci	}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	switch (rc) {
38562306a36Sopenharmony_ci	case KVM_INSN_ILLEGAL_TRAP:
38662306a36Sopenharmony_ci		return truly_illegal_insn(vcpu, run, insn);
38762306a36Sopenharmony_ci	case KVM_INSN_VIRTUAL_TRAP:
38862306a36Sopenharmony_ci		return truly_virtual_insn(vcpu, run, insn);
38962306a36Sopenharmony_ci	case KVM_INSN_CONTINUE_NEXT_SEPC:
39062306a36Sopenharmony_ci		vcpu->arch.guest_context.sepc += INSN_LEN(insn);
39162306a36Sopenharmony_ci		break;
39262306a36Sopenharmony_ci	default:
39362306a36Sopenharmony_ci		break;
39462306a36Sopenharmony_ci	}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	return (rc <= 0) ? rc : 1;
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci/**
40062306a36Sopenharmony_ci * kvm_riscv_vcpu_virtual_insn -- Handle virtual instruction trap
40162306a36Sopenharmony_ci *
40262306a36Sopenharmony_ci * @vcpu: The VCPU pointer
40362306a36Sopenharmony_ci * @run:  The VCPU run struct containing the mmio data
40462306a36Sopenharmony_ci * @trap: Trap details
40562306a36Sopenharmony_ci *
40662306a36Sopenharmony_ci * Returns > 0 to continue run-loop
40762306a36Sopenharmony_ci * Returns   0 to exit run-loop and handle in user-space.
40862306a36Sopenharmony_ci * Returns < 0 to report failure and exit run-loop
40962306a36Sopenharmony_ci */
41062306a36Sopenharmony_ciint kvm_riscv_vcpu_virtual_insn(struct kvm_vcpu *vcpu, struct kvm_run *run,
41162306a36Sopenharmony_ci				struct kvm_cpu_trap *trap)
41262306a36Sopenharmony_ci{
41362306a36Sopenharmony_ci	unsigned long insn = trap->stval;
41462306a36Sopenharmony_ci	struct kvm_cpu_trap utrap = { 0 };
41562306a36Sopenharmony_ci	struct kvm_cpu_context *ct;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	if (unlikely(INSN_IS_16BIT(insn))) {
41862306a36Sopenharmony_ci		if (insn == 0) {
41962306a36Sopenharmony_ci			ct = &vcpu->arch.guest_context;
42062306a36Sopenharmony_ci			insn = kvm_riscv_vcpu_unpriv_read(vcpu, true,
42162306a36Sopenharmony_ci							  ct->sepc,
42262306a36Sopenharmony_ci							  &utrap);
42362306a36Sopenharmony_ci			if (utrap.scause) {
42462306a36Sopenharmony_ci				utrap.sepc = ct->sepc;
42562306a36Sopenharmony_ci				kvm_riscv_vcpu_trap_redirect(vcpu, &utrap);
42662306a36Sopenharmony_ci				return 1;
42762306a36Sopenharmony_ci			}
42862306a36Sopenharmony_ci		}
42962306a36Sopenharmony_ci		if (INSN_IS_16BIT(insn))
43062306a36Sopenharmony_ci			return truly_illegal_insn(vcpu, run, insn);
43162306a36Sopenharmony_ci	}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	switch ((insn & INSN_OPCODE_MASK) >> INSN_OPCODE_SHIFT) {
43462306a36Sopenharmony_ci	case INSN_OPCODE_SYSTEM:
43562306a36Sopenharmony_ci		return system_opcode_insn(vcpu, run, insn);
43662306a36Sopenharmony_ci	default:
43762306a36Sopenharmony_ci		return truly_illegal_insn(vcpu, run, insn);
43862306a36Sopenharmony_ci	}
43962306a36Sopenharmony_ci}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci/**
44262306a36Sopenharmony_ci * kvm_riscv_vcpu_mmio_load -- Emulate MMIO load instruction
44362306a36Sopenharmony_ci *
44462306a36Sopenharmony_ci * @vcpu: The VCPU pointer
44562306a36Sopenharmony_ci * @run:  The VCPU run struct containing the mmio data
44662306a36Sopenharmony_ci * @fault_addr: Guest physical address to load
44762306a36Sopenharmony_ci * @htinst: Transformed encoding of the load instruction
44862306a36Sopenharmony_ci *
44962306a36Sopenharmony_ci * Returns > 0 to continue run-loop
45062306a36Sopenharmony_ci * Returns   0 to exit run-loop and handle in user-space.
45162306a36Sopenharmony_ci * Returns < 0 to report failure and exit run-loop
45262306a36Sopenharmony_ci */
45362306a36Sopenharmony_ciint kvm_riscv_vcpu_mmio_load(struct kvm_vcpu *vcpu, struct kvm_run *run,
45462306a36Sopenharmony_ci			     unsigned long fault_addr,
45562306a36Sopenharmony_ci			     unsigned long htinst)
45662306a36Sopenharmony_ci{
45762306a36Sopenharmony_ci	u8 data_buf[8];
45862306a36Sopenharmony_ci	unsigned long insn;
45962306a36Sopenharmony_ci	int shift = 0, len = 0, insn_len = 0;
46062306a36Sopenharmony_ci	struct kvm_cpu_trap utrap = { 0 };
46162306a36Sopenharmony_ci	struct kvm_cpu_context *ct = &vcpu->arch.guest_context;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	/* Determine trapped instruction */
46462306a36Sopenharmony_ci	if (htinst & 0x1) {
46562306a36Sopenharmony_ci		/*
46662306a36Sopenharmony_ci		 * Bit[0] == 1 implies trapped instruction value is
46762306a36Sopenharmony_ci		 * transformed instruction or custom instruction.
46862306a36Sopenharmony_ci		 */
46962306a36Sopenharmony_ci		insn = htinst | INSN_16BIT_MASK;
47062306a36Sopenharmony_ci		insn_len = (htinst & BIT(1)) ? INSN_LEN(insn) : 2;
47162306a36Sopenharmony_ci	} else {
47262306a36Sopenharmony_ci		/*
47362306a36Sopenharmony_ci		 * Bit[0] == 0 implies trapped instruction value is
47462306a36Sopenharmony_ci		 * zero or special value.
47562306a36Sopenharmony_ci		 */
47662306a36Sopenharmony_ci		insn = kvm_riscv_vcpu_unpriv_read(vcpu, true, ct->sepc,
47762306a36Sopenharmony_ci						  &utrap);
47862306a36Sopenharmony_ci		if (utrap.scause) {
47962306a36Sopenharmony_ci			/* Redirect trap if we failed to read instruction */
48062306a36Sopenharmony_ci			utrap.sepc = ct->sepc;
48162306a36Sopenharmony_ci			kvm_riscv_vcpu_trap_redirect(vcpu, &utrap);
48262306a36Sopenharmony_ci			return 1;
48362306a36Sopenharmony_ci		}
48462306a36Sopenharmony_ci		insn_len = INSN_LEN(insn);
48562306a36Sopenharmony_ci	}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	/* Decode length of MMIO and shift */
48862306a36Sopenharmony_ci	if ((insn & INSN_MASK_LW) == INSN_MATCH_LW) {
48962306a36Sopenharmony_ci		len = 4;
49062306a36Sopenharmony_ci		shift = 8 * (sizeof(ulong) - len);
49162306a36Sopenharmony_ci	} else if ((insn & INSN_MASK_LB) == INSN_MATCH_LB) {
49262306a36Sopenharmony_ci		len = 1;
49362306a36Sopenharmony_ci		shift = 8 * (sizeof(ulong) - len);
49462306a36Sopenharmony_ci	} else if ((insn & INSN_MASK_LBU) == INSN_MATCH_LBU) {
49562306a36Sopenharmony_ci		len = 1;
49662306a36Sopenharmony_ci		shift = 8 * (sizeof(ulong) - len);
49762306a36Sopenharmony_ci#ifdef CONFIG_64BIT
49862306a36Sopenharmony_ci	} else if ((insn & INSN_MASK_LD) == INSN_MATCH_LD) {
49962306a36Sopenharmony_ci		len = 8;
50062306a36Sopenharmony_ci		shift = 8 * (sizeof(ulong) - len);
50162306a36Sopenharmony_ci	} else if ((insn & INSN_MASK_LWU) == INSN_MATCH_LWU) {
50262306a36Sopenharmony_ci		len = 4;
50362306a36Sopenharmony_ci#endif
50462306a36Sopenharmony_ci	} else if ((insn & INSN_MASK_LH) == INSN_MATCH_LH) {
50562306a36Sopenharmony_ci		len = 2;
50662306a36Sopenharmony_ci		shift = 8 * (sizeof(ulong) - len);
50762306a36Sopenharmony_ci	} else if ((insn & INSN_MASK_LHU) == INSN_MATCH_LHU) {
50862306a36Sopenharmony_ci		len = 2;
50962306a36Sopenharmony_ci#ifdef CONFIG_64BIT
51062306a36Sopenharmony_ci	} else if ((insn & INSN_MASK_C_LD) == INSN_MATCH_C_LD) {
51162306a36Sopenharmony_ci		len = 8;
51262306a36Sopenharmony_ci		shift = 8 * (sizeof(ulong) - len);
51362306a36Sopenharmony_ci		insn = RVC_RS2S(insn) << SH_RD;
51462306a36Sopenharmony_ci	} else if ((insn & INSN_MASK_C_LDSP) == INSN_MATCH_C_LDSP &&
51562306a36Sopenharmony_ci		   ((insn >> SH_RD) & 0x1f)) {
51662306a36Sopenharmony_ci		len = 8;
51762306a36Sopenharmony_ci		shift = 8 * (sizeof(ulong) - len);
51862306a36Sopenharmony_ci#endif
51962306a36Sopenharmony_ci	} else if ((insn & INSN_MASK_C_LW) == INSN_MATCH_C_LW) {
52062306a36Sopenharmony_ci		len = 4;
52162306a36Sopenharmony_ci		shift = 8 * (sizeof(ulong) - len);
52262306a36Sopenharmony_ci		insn = RVC_RS2S(insn) << SH_RD;
52362306a36Sopenharmony_ci	} else if ((insn & INSN_MASK_C_LWSP) == INSN_MATCH_C_LWSP &&
52462306a36Sopenharmony_ci		   ((insn >> SH_RD) & 0x1f)) {
52562306a36Sopenharmony_ci		len = 4;
52662306a36Sopenharmony_ci		shift = 8 * (sizeof(ulong) - len);
52762306a36Sopenharmony_ci	} else {
52862306a36Sopenharmony_ci		return -EOPNOTSUPP;
52962306a36Sopenharmony_ci	}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	/* Fault address should be aligned to length of MMIO */
53262306a36Sopenharmony_ci	if (fault_addr & (len - 1))
53362306a36Sopenharmony_ci		return -EIO;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	/* Save instruction decode info */
53662306a36Sopenharmony_ci	vcpu->arch.mmio_decode.insn = insn;
53762306a36Sopenharmony_ci	vcpu->arch.mmio_decode.insn_len = insn_len;
53862306a36Sopenharmony_ci	vcpu->arch.mmio_decode.shift = shift;
53962306a36Sopenharmony_ci	vcpu->arch.mmio_decode.len = len;
54062306a36Sopenharmony_ci	vcpu->arch.mmio_decode.return_handled = 0;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	/* Update MMIO details in kvm_run struct */
54362306a36Sopenharmony_ci	run->mmio.is_write = false;
54462306a36Sopenharmony_ci	run->mmio.phys_addr = fault_addr;
54562306a36Sopenharmony_ci	run->mmio.len = len;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	/* Try to handle MMIO access in the kernel */
54862306a36Sopenharmony_ci	if (!kvm_io_bus_read(vcpu, KVM_MMIO_BUS, fault_addr, len, data_buf)) {
54962306a36Sopenharmony_ci		/* Successfully handled MMIO access in the kernel so resume */
55062306a36Sopenharmony_ci		memcpy(run->mmio.data, data_buf, len);
55162306a36Sopenharmony_ci		vcpu->stat.mmio_exit_kernel++;
55262306a36Sopenharmony_ci		kvm_riscv_vcpu_mmio_return(vcpu, run);
55362306a36Sopenharmony_ci		return 1;
55462306a36Sopenharmony_ci	}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	/* Exit to userspace for MMIO emulation */
55762306a36Sopenharmony_ci	vcpu->stat.mmio_exit_user++;
55862306a36Sopenharmony_ci	run->exit_reason = KVM_EXIT_MMIO;
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	return 0;
56162306a36Sopenharmony_ci}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci/**
56462306a36Sopenharmony_ci * kvm_riscv_vcpu_mmio_store -- Emulate MMIO store instruction
56562306a36Sopenharmony_ci *
56662306a36Sopenharmony_ci * @vcpu: The VCPU pointer
56762306a36Sopenharmony_ci * @run:  The VCPU run struct containing the mmio data
56862306a36Sopenharmony_ci * @fault_addr: Guest physical address to store
56962306a36Sopenharmony_ci * @htinst: Transformed encoding of the store instruction
57062306a36Sopenharmony_ci *
57162306a36Sopenharmony_ci * Returns > 0 to continue run-loop
57262306a36Sopenharmony_ci * Returns   0 to exit run-loop and handle in user-space.
57362306a36Sopenharmony_ci * Returns < 0 to report failure and exit run-loop
57462306a36Sopenharmony_ci */
57562306a36Sopenharmony_ciint kvm_riscv_vcpu_mmio_store(struct kvm_vcpu *vcpu, struct kvm_run *run,
57662306a36Sopenharmony_ci			      unsigned long fault_addr,
57762306a36Sopenharmony_ci			      unsigned long htinst)
57862306a36Sopenharmony_ci{
57962306a36Sopenharmony_ci	u8 data8;
58062306a36Sopenharmony_ci	u16 data16;
58162306a36Sopenharmony_ci	u32 data32;
58262306a36Sopenharmony_ci	u64 data64;
58362306a36Sopenharmony_ci	ulong data;
58462306a36Sopenharmony_ci	unsigned long insn;
58562306a36Sopenharmony_ci	int len = 0, insn_len = 0;
58662306a36Sopenharmony_ci	struct kvm_cpu_trap utrap = { 0 };
58762306a36Sopenharmony_ci	struct kvm_cpu_context *ct = &vcpu->arch.guest_context;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	/* Determine trapped instruction */
59062306a36Sopenharmony_ci	if (htinst & 0x1) {
59162306a36Sopenharmony_ci		/*
59262306a36Sopenharmony_ci		 * Bit[0] == 1 implies trapped instruction value is
59362306a36Sopenharmony_ci		 * transformed instruction or custom instruction.
59462306a36Sopenharmony_ci		 */
59562306a36Sopenharmony_ci		insn = htinst | INSN_16BIT_MASK;
59662306a36Sopenharmony_ci		insn_len = (htinst & BIT(1)) ? INSN_LEN(insn) : 2;
59762306a36Sopenharmony_ci	} else {
59862306a36Sopenharmony_ci		/*
59962306a36Sopenharmony_ci		 * Bit[0] == 0 implies trapped instruction value is
60062306a36Sopenharmony_ci		 * zero or special value.
60162306a36Sopenharmony_ci		 */
60262306a36Sopenharmony_ci		insn = kvm_riscv_vcpu_unpriv_read(vcpu, true, ct->sepc,
60362306a36Sopenharmony_ci						  &utrap);
60462306a36Sopenharmony_ci		if (utrap.scause) {
60562306a36Sopenharmony_ci			/* Redirect trap if we failed to read instruction */
60662306a36Sopenharmony_ci			utrap.sepc = ct->sepc;
60762306a36Sopenharmony_ci			kvm_riscv_vcpu_trap_redirect(vcpu, &utrap);
60862306a36Sopenharmony_ci			return 1;
60962306a36Sopenharmony_ci		}
61062306a36Sopenharmony_ci		insn_len = INSN_LEN(insn);
61162306a36Sopenharmony_ci	}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	data = GET_RS2(insn, &vcpu->arch.guest_context);
61462306a36Sopenharmony_ci	data8 = data16 = data32 = data64 = data;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	if ((insn & INSN_MASK_SW) == INSN_MATCH_SW) {
61762306a36Sopenharmony_ci		len = 4;
61862306a36Sopenharmony_ci	} else if ((insn & INSN_MASK_SB) == INSN_MATCH_SB) {
61962306a36Sopenharmony_ci		len = 1;
62062306a36Sopenharmony_ci#ifdef CONFIG_64BIT
62162306a36Sopenharmony_ci	} else if ((insn & INSN_MASK_SD) == INSN_MATCH_SD) {
62262306a36Sopenharmony_ci		len = 8;
62362306a36Sopenharmony_ci#endif
62462306a36Sopenharmony_ci	} else if ((insn & INSN_MASK_SH) == INSN_MATCH_SH) {
62562306a36Sopenharmony_ci		len = 2;
62662306a36Sopenharmony_ci#ifdef CONFIG_64BIT
62762306a36Sopenharmony_ci	} else if ((insn & INSN_MASK_C_SD) == INSN_MATCH_C_SD) {
62862306a36Sopenharmony_ci		len = 8;
62962306a36Sopenharmony_ci		data64 = GET_RS2S(insn, &vcpu->arch.guest_context);
63062306a36Sopenharmony_ci	} else if ((insn & INSN_MASK_C_SDSP) == INSN_MATCH_C_SDSP &&
63162306a36Sopenharmony_ci		   ((insn >> SH_RD) & 0x1f)) {
63262306a36Sopenharmony_ci		len = 8;
63362306a36Sopenharmony_ci		data64 = GET_RS2C(insn, &vcpu->arch.guest_context);
63462306a36Sopenharmony_ci#endif
63562306a36Sopenharmony_ci	} else if ((insn & INSN_MASK_C_SW) == INSN_MATCH_C_SW) {
63662306a36Sopenharmony_ci		len = 4;
63762306a36Sopenharmony_ci		data32 = GET_RS2S(insn, &vcpu->arch.guest_context);
63862306a36Sopenharmony_ci	} else if ((insn & INSN_MASK_C_SWSP) == INSN_MATCH_C_SWSP &&
63962306a36Sopenharmony_ci		   ((insn >> SH_RD) & 0x1f)) {
64062306a36Sopenharmony_ci		len = 4;
64162306a36Sopenharmony_ci		data32 = GET_RS2C(insn, &vcpu->arch.guest_context);
64262306a36Sopenharmony_ci	} else {
64362306a36Sopenharmony_ci		return -EOPNOTSUPP;
64462306a36Sopenharmony_ci	}
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	/* Fault address should be aligned to length of MMIO */
64762306a36Sopenharmony_ci	if (fault_addr & (len - 1))
64862306a36Sopenharmony_ci		return -EIO;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	/* Save instruction decode info */
65162306a36Sopenharmony_ci	vcpu->arch.mmio_decode.insn = insn;
65262306a36Sopenharmony_ci	vcpu->arch.mmio_decode.insn_len = insn_len;
65362306a36Sopenharmony_ci	vcpu->arch.mmio_decode.shift = 0;
65462306a36Sopenharmony_ci	vcpu->arch.mmio_decode.len = len;
65562306a36Sopenharmony_ci	vcpu->arch.mmio_decode.return_handled = 0;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	/* Copy data to kvm_run instance */
65862306a36Sopenharmony_ci	switch (len) {
65962306a36Sopenharmony_ci	case 1:
66062306a36Sopenharmony_ci		*((u8 *)run->mmio.data) = data8;
66162306a36Sopenharmony_ci		break;
66262306a36Sopenharmony_ci	case 2:
66362306a36Sopenharmony_ci		*((u16 *)run->mmio.data) = data16;
66462306a36Sopenharmony_ci		break;
66562306a36Sopenharmony_ci	case 4:
66662306a36Sopenharmony_ci		*((u32 *)run->mmio.data) = data32;
66762306a36Sopenharmony_ci		break;
66862306a36Sopenharmony_ci	case 8:
66962306a36Sopenharmony_ci		*((u64 *)run->mmio.data) = data64;
67062306a36Sopenharmony_ci		break;
67162306a36Sopenharmony_ci	default:
67262306a36Sopenharmony_ci		return -EOPNOTSUPP;
67362306a36Sopenharmony_ci	}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	/* Update MMIO details in kvm_run struct */
67662306a36Sopenharmony_ci	run->mmio.is_write = true;
67762306a36Sopenharmony_ci	run->mmio.phys_addr = fault_addr;
67862306a36Sopenharmony_ci	run->mmio.len = len;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	/* Try to handle MMIO access in the kernel */
68162306a36Sopenharmony_ci	if (!kvm_io_bus_write(vcpu, KVM_MMIO_BUS,
68262306a36Sopenharmony_ci			      fault_addr, len, run->mmio.data)) {
68362306a36Sopenharmony_ci		/* Successfully handled MMIO access in the kernel so resume */
68462306a36Sopenharmony_ci		vcpu->stat.mmio_exit_kernel++;
68562306a36Sopenharmony_ci		kvm_riscv_vcpu_mmio_return(vcpu, run);
68662306a36Sopenharmony_ci		return 1;
68762306a36Sopenharmony_ci	}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	/* Exit to userspace for MMIO emulation */
69062306a36Sopenharmony_ci	vcpu->stat.mmio_exit_user++;
69162306a36Sopenharmony_ci	run->exit_reason = KVM_EXIT_MMIO;
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	return 0;
69462306a36Sopenharmony_ci}
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci/**
69762306a36Sopenharmony_ci * kvm_riscv_vcpu_mmio_return -- Handle MMIO loads after user space emulation
69862306a36Sopenharmony_ci *			     or in-kernel IO emulation
69962306a36Sopenharmony_ci *
70062306a36Sopenharmony_ci * @vcpu: The VCPU pointer
70162306a36Sopenharmony_ci * @run:  The VCPU run struct containing the mmio data
70262306a36Sopenharmony_ci */
70362306a36Sopenharmony_ciint kvm_riscv_vcpu_mmio_return(struct kvm_vcpu *vcpu, struct kvm_run *run)
70462306a36Sopenharmony_ci{
70562306a36Sopenharmony_ci	u8 data8;
70662306a36Sopenharmony_ci	u16 data16;
70762306a36Sopenharmony_ci	u32 data32;
70862306a36Sopenharmony_ci	u64 data64;
70962306a36Sopenharmony_ci	ulong insn;
71062306a36Sopenharmony_ci	int len, shift;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	if (vcpu->arch.mmio_decode.return_handled)
71362306a36Sopenharmony_ci		return 0;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	vcpu->arch.mmio_decode.return_handled = 1;
71662306a36Sopenharmony_ci	insn = vcpu->arch.mmio_decode.insn;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	if (run->mmio.is_write)
71962306a36Sopenharmony_ci		goto done;
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	len = vcpu->arch.mmio_decode.len;
72262306a36Sopenharmony_ci	shift = vcpu->arch.mmio_decode.shift;
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	switch (len) {
72562306a36Sopenharmony_ci	case 1:
72662306a36Sopenharmony_ci		data8 = *((u8 *)run->mmio.data);
72762306a36Sopenharmony_ci		SET_RD(insn, &vcpu->arch.guest_context,
72862306a36Sopenharmony_ci			(ulong)data8 << shift >> shift);
72962306a36Sopenharmony_ci		break;
73062306a36Sopenharmony_ci	case 2:
73162306a36Sopenharmony_ci		data16 = *((u16 *)run->mmio.data);
73262306a36Sopenharmony_ci		SET_RD(insn, &vcpu->arch.guest_context,
73362306a36Sopenharmony_ci			(ulong)data16 << shift >> shift);
73462306a36Sopenharmony_ci		break;
73562306a36Sopenharmony_ci	case 4:
73662306a36Sopenharmony_ci		data32 = *((u32 *)run->mmio.data);
73762306a36Sopenharmony_ci		SET_RD(insn, &vcpu->arch.guest_context,
73862306a36Sopenharmony_ci			(ulong)data32 << shift >> shift);
73962306a36Sopenharmony_ci		break;
74062306a36Sopenharmony_ci	case 8:
74162306a36Sopenharmony_ci		data64 = *((u64 *)run->mmio.data);
74262306a36Sopenharmony_ci		SET_RD(insn, &vcpu->arch.guest_context,
74362306a36Sopenharmony_ci			(ulong)data64 << shift >> shift);
74462306a36Sopenharmony_ci		break;
74562306a36Sopenharmony_ci	default:
74662306a36Sopenharmony_ci		return -EOPNOTSUPP;
74762306a36Sopenharmony_ci	}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_cidone:
75062306a36Sopenharmony_ci	/* Move to next instruction */
75162306a36Sopenharmony_ci	vcpu->arch.guest_context.sepc += vcpu->arch.mmio_decode.insn_len;
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	return 0;
75462306a36Sopenharmony_ci}
755