[PATCH 3/3] RISC-V: KVM: Add extensible CSR emulation framework
Zhao Liu
zhao1.liu at linux.intel.com
Sun Jun 12 09:12:20 PDT 2022
kvm-riscv at lists.infradead.org, linux-riscv at lists.infradead.org,
linux-kernel at vger.kernel.org, Anup Patel <apatel at ventanamicro.com>,
Zhao Liu <zhao1.liu at linux.intel.com>, Zhenyu Wang
<zhenyuw at linux.intel.com>
Bcc:
Subject: Re: [PATCH 3/3] RISC-V: KVM: Add extensible CSR emulation framework
Reply-To:
In-Reply-To: <20220610050555.288251-4-apatel at ventanamicro.com>
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 at ventanamicro.com>
> 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 at ventanamicro.com>
> ---
> 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;
}
> 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.
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 at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-riscv
More information about the kvm-riscv
mailing list