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