On Sun, Jun 12, 2022 at 9:38 PM Zhao Liu <zhao1.liu@xxxxxxxxxxxxxxx> wrote: > > kvm-riscv@xxxxxxxxxxxxxxxxxxx, linux-riscv@xxxxxxxxxxxxxxxxxxx, > linux-kernel@xxxxxxxxxxxxxxx, Anup Patel <apatel@xxxxxxxxxxxxxxxx>, > Zhao Liu <zhao1.liu@xxxxxxxxxxxxxxx>, Zhenyu Wang > <zhenyuw@xxxxxxxxxxxxxxx> > Bcc: > Subject: Re: [PATCH 3/3] RISC-V: KVM: Add extensible CSR emulation framework > Reply-To: > In-Reply-To: <20220610050555.288251-4-apatel@xxxxxxxxxxxxxxxx> > > On Fri, Jun 10, 2022 at 10:35:55AM +0530, Anup Patel wrote: > > Date: Fri, 10 Jun 2022 10:35:55 +0530 > > From: Anup Patel <apatel@xxxxxxxxxxxxxxxx> > > Subject: [PATCH 3/3] RISC-V: KVM: Add extensible CSR emulation framework > > X-Mailer: git-send-email 2.34.1 > > > > We add an extensible CSR emulation framework which is based upon the > > existing system instruction emulation. This will be useful to upcoming > > AIA, PMU, Nested and other virtualization features. > > > > The CSR emulation framework also has provision to emulate CSR in user > > space but this will be used only in very specific cases such as AIA > > IMSIC CSR emulation in user space or vendor specific CSR emulation > > in user space. > > > > By default, all CSRs not handled by KVM RISC-V will be redirected back > > to Guest VCPU as illegal instruction trap. > > > > Signed-off-by: Anup Patel <apatel@xxxxxxxxxxxxxxxx> > > --- > > arch/riscv/include/asm/kvm_host.h | 5 + > > arch/riscv/include/asm/kvm_vcpu_insn.h | 6 + > > arch/riscv/kvm/vcpu.c | 11 ++ > > arch/riscv/kvm/vcpu_insn.c | 169 +++++++++++++++++++++++++ > > include/uapi/linux/kvm.h | 8 ++ > > 5 files changed, 199 insertions(+) > > > > diff --git a/arch/riscv/include/asm/kvm_host.h b/arch/riscv/include/asm/kvm_host.h > > index 03103b86dd86..a54744d7e1ba 100644 > > --- a/arch/riscv/include/asm/kvm_host.h > > +++ b/arch/riscv/include/asm/kvm_host.h > > @@ -64,6 +64,8 @@ struct kvm_vcpu_stat { > > u64 wfi_exit_stat; > > u64 mmio_exit_user; > > u64 mmio_exit_kernel; > > + u64 csr_exit_user; > > + u64 csr_exit_kernel; > > u64 exits; > > }; > > > > @@ -209,6 +211,9 @@ struct kvm_vcpu_arch { > > /* MMIO instruction details */ > > struct kvm_mmio_decode mmio_decode; > > > > + /* CSR instruction details */ > > + struct kvm_csr_decode csr_decode; > > + > > /* SBI context */ > > struct kvm_sbi_context sbi_context; > > > > diff --git a/arch/riscv/include/asm/kvm_vcpu_insn.h b/arch/riscv/include/asm/kvm_vcpu_insn.h > > index 3351eb61a251..350011c83581 100644 > > --- a/arch/riscv/include/asm/kvm_vcpu_insn.h > > +++ b/arch/riscv/include/asm/kvm_vcpu_insn.h > > @@ -18,6 +18,11 @@ struct kvm_mmio_decode { > > int return_handled; > > }; > > > > +struct kvm_csr_decode { > > + unsigned long insn; > > + int return_handled; > > +}; > > + > > /* Return values used by function emulating a particular instruction */ > > enum kvm_insn_return { > > KVM_INSN_EXIT_TO_USER_SPACE = 0, > > @@ -28,6 +33,7 @@ enum kvm_insn_return { > > }; > > > > void kvm_riscv_vcpu_wfi(struct kvm_vcpu *vcpu); > > +int kvm_riscv_vcpu_csr_return(struct kvm_vcpu *vcpu, struct kvm_run *run); > > int kvm_riscv_vcpu_virtual_insn(struct kvm_vcpu *vcpu, struct kvm_run *run, > > struct kvm_cpu_trap *trap); > > > > diff --git a/arch/riscv/kvm/vcpu.c b/arch/riscv/kvm/vcpu.c > > index 7f4ad5e4373a..cf9616da68f6 100644 > > --- a/arch/riscv/kvm/vcpu.c > > +++ b/arch/riscv/kvm/vcpu.c > > @@ -26,6 +26,8 @@ const struct _kvm_stats_desc kvm_vcpu_stats_desc[] = { > > STATS_DESC_COUNTER(VCPU, wfi_exit_stat), > > STATS_DESC_COUNTER(VCPU, mmio_exit_user), > > STATS_DESC_COUNTER(VCPU, mmio_exit_kernel), > > + STATS_DESC_COUNTER(VCPU, csr_exit_user), > > + STATS_DESC_COUNTER(VCPU, csr_exit_kernel), > > STATS_DESC_COUNTER(VCPU, exits) > > }; > > > > @@ -869,6 +871,15 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) > > } > > } > > > > + /* Process CSR value returned from user-space */ > > + if (run->exit_reason == KVM_EXIT_RISCV_CSR) { > > + ret = kvm_riscv_vcpu_csr_return(vcpu, vcpu->run); > > + if (ret) { > > + kvm_vcpu_srcu_read_unlock(vcpu); > > + return ret; > > + } > > + } > > + > > > Hi Anup, what about a `switch` to handle exit_reason? > switch(run->exit_reason) { > case KVM_EXIT_MMIO: > ret = kvm_riscv_vcpu_mmio_return(vcpu, vcpu->run); > break; > case KVM_EXIT_RISCV_SBI: > ret = kvm_riscv_vcpu_sbi_return(vcpu, vcpu->run); > break; > case KVM_EXIT_RISCV_CSR: > ret = kvm_riscv_vcpu_csr_return(vcpu, vcpu->run); > break; > case default: > break; > } > if (ret) { > kvm_vcpu_srcu_read_unlock(vcpu); > return ret; > } I agree with your suggestion. I will use switch-case in v2. > > > if (run->immediate_exit) { > > kvm_vcpu_srcu_read_unlock(vcpu); > > return -EINTR; > > diff --git a/arch/riscv/kvm/vcpu_insn.c b/arch/riscv/kvm/vcpu_insn.c > > index 75ca62a7fba5..c9542ba98431 100644 > > --- a/arch/riscv/kvm/vcpu_insn.c > > +++ b/arch/riscv/kvm/vcpu_insn.c > > @@ -14,6 +14,19 @@ > > #define INSN_MASK_WFI 0xffffffff > > #define INSN_MATCH_WFI 0x10500073 > > > > +#define INSN_MATCH_CSRRW 0x1073 > > +#define INSN_MASK_CSRRW 0x707f > > +#define INSN_MATCH_CSRRS 0x2073 > > +#define INSN_MASK_CSRRS 0x707f > > +#define INSN_MATCH_CSRRC 0x3073 > > +#define INSN_MASK_CSRRC 0x707f > > +#define INSN_MATCH_CSRRWI 0x5073 > > +#define INSN_MASK_CSRRWI 0x707f > > +#define INSN_MATCH_CSRRSI 0x6073 > > +#define INSN_MASK_CSRRSI 0x707f > > +#define INSN_MATCH_CSRRCI 0x7073 > > +#define INSN_MASK_CSRRCI 0x707f > > + > > #define INSN_MATCH_LB 0x3 > > #define INSN_MASK_LB 0x707f > > #define INSN_MATCH_LH 0x1003 > > @@ -71,6 +84,7 @@ > > #define SH_RS1 15 > > #define SH_RS2 20 > > #define SH_RS2C 2 > > +#define MASK_RX 0x1f > > > > #define RV_X(x, s, n) (((x) >> (s)) & ((1 << (n)) - 1)) > > #define RVC_LW_IMM(x) ((RV_X(x, 6, 1) << 2) | \ > > @@ -189,7 +203,162 @@ static int wfi_insn(struct kvm_vcpu *vcpu, struct kvm_run *run, ulong insn) > > return KVM_INSN_CONTINUE_NEXT_SEPC; > > } > > > > +struct csr_func { > > + unsigned int base; > > + unsigned int count; > > + /* > > + * Possible return values are as same as "func" callback in > > + * "struct insn_func". > > + */ > > + int (*func)(struct kvm_vcpu *vcpu, unsigned int csr_num, > > + unsigned long *val, unsigned long new_val, > > + unsigned long wr_mask); > > +}; > > + > > +static const struct csr_func csr_funcs[] = { }; > > + > > +/** > > + * kvm_riscv_vcpu_csr_return -- Handle CSR read/write after user space > > + * emulation or in-kernel emulation > > + * > > + * @vcpu: The VCPU pointer > > + * @run: The VCPU run struct containing the CSR data > > + * > > + * Returns > 0 upon failure and 0 upon success > > + */ > > +int kvm_riscv_vcpu_csr_return(struct kvm_vcpu *vcpu, struct kvm_run *run) > > +{ > > + ulong insn; > > + > > + if (vcpu->arch.csr_decode.return_handled) > > + return 0; > > + vcpu->arch.csr_decode.return_handled = 1; > > + > > + /* Update destination register for CSR reads */ > > + insn = vcpu->arch.csr_decode.insn; > > + if ((insn >> SH_RD) & MASK_RX) > > + SET_RD(insn, &vcpu->arch.guest_context, > > + run->riscv_csr.ret_value); > > + > > + /* Move to next instruction */ > > + vcpu->arch.guest_context.sepc += INSN_LEN(insn); > > + > > + return 0; > > +} > > + > > +static int csr_insn(struct kvm_vcpu *vcpu, struct kvm_run *run, ulong insn) > > +{ > > + int i, rc = KVM_INSN_ILLEGAL_TRAP; > > + unsigned int csr_num = insn >> SH_RS2; > > + unsigned int rs1_num = (insn >> SH_RS1) & MASK_RX; > > + ulong rs1_val = GET_RS1(insn, &vcpu->arch.guest_context); > > + const struct csr_func *tcfn, *cfn = NULL; > > + ulong val = 0, wr_mask = 0, new_val = 0; > > + > > + /* Decode the CSR instruction */ > > + switch (GET_RM(insn)) { > > + case 1: > > It's better to define these rounding mode. > What about this name: #define INSN_RM_RTZ 1. Actually, there is no "Rm" field in CSR instruction encoding. Instead, the BIT[14:12] of CSR instruction is "funct3" field. I will fix this in v2. Also, instead of adding new defines for "funct3" field of CSR instruction, we can simply use INSN_MATCH_xyz defines to avoid hard-coding. Regards, Anup > > Thanks, > Zhao > > > + wr_mask = -1UL; > > + new_val = rs1_val; > > + break; > > + case 2: > > + wr_mask = rs1_val; > > + new_val = -1UL; > > + break; > > + case 3: > > + wr_mask = rs1_val; > > + new_val = 0; > > + break; > > + case 5: > > + wr_mask = -1UL; > > + new_val = rs1_num; > > + break; > > + case 6: > > + wr_mask = rs1_num; > > + new_val = -1UL; > > + break; > > + case 7: > > + wr_mask = rs1_num; > > + new_val = 0; > > + break; > > + default: > > + return rc; > > + }; > > + > > + /* Save instruction decode info */ > > + vcpu->arch.csr_decode.insn = insn; > > + vcpu->arch.csr_decode.return_handled = 0; > > + > > + /* Update CSR details in kvm_run struct */ > > + run->riscv_csr.csr_num = csr_num; > > + run->riscv_csr.new_value = new_val; > > + run->riscv_csr.write_mask = wr_mask; > > + run->riscv_csr.ret_value = 0; > > + > > + /* Find in-kernel CSR function */ > > + for (i = 0; i < ARRAY_SIZE(csr_funcs); i++) { > > + tcfn = &csr_funcs[i]; > > + if ((tcfn->base <= csr_num) && > > + (csr_num < (tcfn->base + tcfn->count))) { > > + cfn = tcfn; > > + break; > > + } > > + } > > + > > + /* First try in-kernel CSR emulation */ > > + if (cfn && cfn->func) { > > + rc = cfn->func(vcpu, csr_num, &val, new_val, wr_mask); > > + if (rc > KVM_INSN_EXIT_TO_USER_SPACE) { > > + if (rc == KVM_INSN_CONTINUE_NEXT_SEPC) { > > + run->riscv_csr.ret_value = val; > > + vcpu->stat.csr_exit_kernel++; > > + kvm_riscv_vcpu_csr_return(vcpu, run); > > + rc = KVM_INSN_CONTINUE_SAME_SEPC; > > + } > > + return rc; > > + } > > + } > > + > > + /* Exit to user-space for CSR emulation */ > > + if (rc <= KVM_INSN_EXIT_TO_USER_SPACE) { > > + vcpu->stat.csr_exit_user++; > > + run->exit_reason = KVM_EXIT_RISCV_CSR; > > + } > > + > > + return rc; > > +} > > + > > static const struct insn_func system_opcode_funcs[] = { > > + { > > + .mask = INSN_MASK_CSRRW, > > + .match = INSN_MATCH_CSRRW, > > + .func = csr_insn, > > + }, > > + { > > + .mask = INSN_MASK_CSRRS, > > + .match = INSN_MATCH_CSRRS, > > + .func = csr_insn, > > + }, > > + { > > + .mask = INSN_MASK_CSRRC, > > + .match = INSN_MATCH_CSRRC, > > + .func = csr_insn, > > + }, > > + { > > + .mask = INSN_MASK_CSRRWI, > > + .match = INSN_MATCH_CSRRWI, > > + .func = csr_insn, > > + }, > > + { > > + .mask = INSN_MASK_CSRRSI, > > + .match = INSN_MATCH_CSRRSI, > > + .func = csr_insn, > > + }, > > + { > > + .mask = INSN_MASK_CSRRCI, > > + .match = INSN_MATCH_CSRRCI, > > + .func = csr_insn, > > + }, > > { > > .mask = INSN_MASK_WFI, > > .match = INSN_MATCH_WFI, > > diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h > > index 5088bd9f1922..c48fd3d1c45b 100644 > > --- a/include/uapi/linux/kvm.h > > +++ b/include/uapi/linux/kvm.h > > @@ -270,6 +270,7 @@ struct kvm_xen_exit { > > #define KVM_EXIT_X86_BUS_LOCK 33 > > #define KVM_EXIT_XEN 34 > > #define KVM_EXIT_RISCV_SBI 35 > > +#define KVM_EXIT_RISCV_CSR 36 > > > > /* For KVM_EXIT_INTERNAL_ERROR */ > > /* Emulate instruction failed. */ > > @@ -496,6 +497,13 @@ struct kvm_run { > > unsigned long args[6]; > > unsigned long ret[2]; > > } riscv_sbi; > > + /* KVM_EXIT_RISCV_CSR */ > > + struct { > > + unsigned long csr_num; > > + unsigned long new_value; > > + unsigned long write_mask; > > + unsigned long ret_value; > > + } riscv_csr; > > /* Fix the size of the union. */ > > char padding[256]; > > }; > > -- > > 2.34.1 > > > > > > _______________________________________________ > > linux-riscv mailing list > > linux-riscv@xxxxxxxxxxxxxxxxxxx > > http://lists.infradead.org/mailman/listinfo/linux-riscv