diff mbox series

[v7,3/3] target/riscv: Add vstimecmp support

Message ID 20220803082516.2271344-4-atishp@rivosinc.com
State New
Headers show
Series Implement Sstc extension | expand

Commit Message

Atish Kumar Patra Aug. 3, 2022, 8:25 a.m. UTC
vstimecmp CSR allows the guest OS or to program the next guest timer
interrupt directly. Thus, hypervisor no longer need to inject the
timer interrupt to the guest if vstimecmp is used. This was ratified
as a part of the Sstc extension.

Signed-off-by: Atish Patra <atishp@rivosinc.com>
---
 target/riscv/cpu.h         |   4 ++
 target/riscv/cpu_bits.h    |   4 ++
 target/riscv/cpu_helper.c  |  11 ++--
 target/riscv/csr.c         | 100 ++++++++++++++++++++++++++++++++++++-
 target/riscv/machine.c     |   1 +
 target/riscv/time_helper.c |  16 ++++++
 6 files changed, 131 insertions(+), 5 deletions(-)

Comments

Weiwei Li Aug. 3, 2022, 8:49 a.m. UTC | #1
在 2022/8/3 下午4:25, Atish Patra 写道:
> vstimecmp CSR allows the guest OS or to program the next guest timer
> interrupt directly. Thus, hypervisor no longer need to inject the
> timer interrupt to the guest if vstimecmp is used. This was ratified
> as a part of the Sstc extension.
>
> Signed-off-by: Atish Patra <atishp@rivosinc.com>
> ---
>   target/riscv/cpu.h         |   4 ++
>   target/riscv/cpu_bits.h    |   4 ++
>   target/riscv/cpu_helper.c  |  11 ++--
>   target/riscv/csr.c         | 100 ++++++++++++++++++++++++++++++++++++-
>   target/riscv/machine.c     |   1 +
>   target/riscv/time_helper.c |  16 ++++++
>   6 files changed, 131 insertions(+), 5 deletions(-)
>
> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> index 4cda2905661e..1fd382b2717f 100644
> --- a/target/riscv/cpu.h
> +++ b/target/riscv/cpu.h
> @@ -312,6 +312,8 @@ struct CPUArchState {
>       /* Sstc CSRs */
>       uint64_t stimecmp;
>   
> +    uint64_t vstimecmp;
> +
>       /* physical memory protection */
>       pmp_table_t pmp_state;
>       target_ulong mseccfg;
> @@ -366,6 +368,8 @@ struct CPUArchState {
>   
>       /* Fields from here on are preserved across CPU reset. */
>       QEMUTimer *stimer; /* Internal timer for S-mode interrupt */
> +    QEMUTimer *vstimer; /* Internal timer for VS-mode interrupt */
> +    bool vstime_irq;
>   
>       hwaddr kernel_addr;
>       hwaddr fdt_addr;
> diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
> index ac17cf1515c0..095dab19f512 100644
> --- a/target/riscv/cpu_bits.h
> +++ b/target/riscv/cpu_bits.h
> @@ -257,6 +257,10 @@
>   #define CSR_VSIP            0x244
>   #define CSR_VSATP           0x280
>   
> +/* Sstc virtual CSRs */
> +#define CSR_VSTIMECMP       0x24D
> +#define CSR_VSTIMECMPH      0x25D
> +
>   #define CSR_MTINST          0x34a
>   #define CSR_MTVAL2          0x34b
>   
> diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
> index 650574accf0a..1e4faa84e839 100644
> --- a/target/riscv/cpu_helper.c
> +++ b/target/riscv/cpu_helper.c
> @@ -345,8 +345,9 @@ uint64_t riscv_cpu_all_pending(CPURISCVState *env)
>   {
>       uint32_t gein = get_field(env->hstatus, HSTATUS_VGEIN);
>       uint64_t vsgein = (env->hgeip & (1ULL << gein)) ? MIP_VSEIP : 0;
> +    uint64_t vstip = (env->vstime_irq) ? MIP_VSTIP : 0;
>   
> -    return (env->mip | vsgein) & env->mie;
> +    return (env->mip | vsgein | vstip) & env->mie;
>   }
>   
>   int riscv_cpu_mirq_pending(CPURISCVState *env)
> @@ -605,7 +606,7 @@ uint64_t riscv_cpu_update_mip(RISCVCPU *cpu, uint64_t mask, uint64_t value)
>   {
>       CPURISCVState *env = &cpu->env;
>       CPUState *cs = CPU(cpu);
> -    uint64_t gein, vsgein = 0, old = env->mip;
> +    uint64_t gein, vsgein = 0, vstip = 0, old = env->mip;
>       bool locked = false;
>   
>       if (riscv_cpu_virt_enabled(env)) {
> @@ -613,6 +614,10 @@ uint64_t riscv_cpu_update_mip(RISCVCPU *cpu, uint64_t mask, uint64_t value)
>           vsgein = (env->hgeip & (1ULL << gein)) ? MIP_VSEIP : 0;
>       }
>   
> +    /* No need to update mip for VSTIP */
> +    mask = ((mask == MIP_VSTIP) && env->vstime_irq) ? 0 : mask;
> +    vstip = env->vstime_irq ? MIP_VSTIP : 0;
> +
>       if (!qemu_mutex_iothread_locked()) {
>           locked = true;
>           qemu_mutex_lock_iothread();
> @@ -620,7 +625,7 @@ uint64_t riscv_cpu_update_mip(RISCVCPU *cpu, uint64_t mask, uint64_t value)
>   
>       env->mip = (env->mip & ~mask) | (value & mask);
>   
> -    if (env->mip | vsgein) {
> +    if (env->mip | vsgein | vstip) {
>           cpu_interrupt(cs, CPU_INTERRUPT_HARD);
>       } else {
>           cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
> diff --git a/target/riscv/csr.c b/target/riscv/csr.c
> index b71e2509b64f..d4265dd3cca2 100644
> --- a/target/riscv/csr.c
> +++ b/target/riscv/csr.c
> @@ -833,17 +833,98 @@ static RISCVException sstc(CPURISCVState *env, int csrno)
>       return RISCV_EXCP_NONE;
>   }
>   
> +static RISCVException sstc_hmode(CPURISCVState *env, int csrno)
> +{
> +    CPUState *cs = env_cpu(env);
> +    RISCVCPU *cpu = RISCV_CPU(cs);
> +
> +    if (!cpu->cfg.ext_sstc || !env->rdtime_fn) {
> +        return RISCV_EXCP_ILLEGAL_INST;
> +    }
> +
> +    if (env->priv == PRV_M) {
> +        return RISCV_EXCP_NONE;
> +    }
> +
> +    if (!(get_field(env->mcounteren, COUNTEREN_TM) &
> +          get_field(env->menvcfg, MENVCFG_STCE))) {
> +        return RISCV_EXCP_ILLEGAL_INST;
> +    }
> +
> +    if (!(get_field(env->hcounteren, COUNTEREN_TM) &
> +          get_field(env->henvcfg, HENVCFG_STCE))) {
> +        return RISCV_EXCP_VIRT_INSTRUCTION_FAULT;
> +    }
> +
I think hcounteren only works for VS mode here. So we should add check 
for virt  mode is enabled here.
> +    return RISCV_EXCP_NONE;
> +}

It's better to return hmode(env, csrno) instead of RISCV_EXCP_NONE here.

Regards,

Weiwei Li

> +
> +static RISCVException read_vstimecmp(CPURISCVState *env, int csrno,
> +                                    target_ulong *val)
> +{
> +    *val = env->vstimecmp;
> +
> +    return RISCV_EXCP_NONE;
> +}
> +
> +static RISCVException read_vstimecmph(CPURISCVState *env, int csrno,
> +                                    target_ulong *val)
> +{
> +    *val = env->vstimecmp >> 32;
> +
> +    return RISCV_EXCP_NONE;
> +}
> +
> +static RISCVException write_vstimecmp(CPURISCVState *env, int csrno,
> +                                    target_ulong val)
> +{
> +    RISCVCPU *cpu = env_archcpu(env);
> +
> +    if (riscv_cpu_mxl(env) == MXL_RV32) {
> +        env->vstimecmp = deposit64(env->vstimecmp, 0, 32, (uint64_t)val);
> +    } else {
> +        env->vstimecmp = val;
> +    }
> +
> +    riscv_timer_write_timecmp(cpu, env->vstimer, env->vstimecmp,
> +                              env->htimedelta, MIP_VSTIP);
> +
> +    return RISCV_EXCP_NONE;
> +}
> +
> +static RISCVException write_vstimecmph(CPURISCVState *env, int csrno,
> +                                    target_ulong val)
> +{
> +    RISCVCPU *cpu = env_archcpu(env);
> +
> +    env->vstimecmp = deposit64(env->vstimecmp, 32, 32, (uint64_t)val);
> +    riscv_timer_write_timecmp(cpu, env->vstimer, env->vstimecmp,
> +                              env->htimedelta, MIP_VSTIP);
> +
> +    return RISCV_EXCP_NONE;
> +}
> +
>   static RISCVException read_stimecmp(CPURISCVState *env, int csrno,
>                                       target_ulong *val)
>   {
> -    *val = env->stimecmp;
> +    if (riscv_cpu_virt_enabled(env)) {
> +        *val = env->vstimecmp;
> +    } else {
> +        *val = env->stimecmp;
> +    }
> +
>       return RISCV_EXCP_NONE;
>   }
>   
>   static RISCVException read_stimecmph(CPURISCVState *env, int csrno,
>                                       target_ulong *val)
>   {
> -    *val = env->stimecmp >> 32;
> +    if (riscv_cpu_virt_enabled(env)) {
> +        *val = env->vstimecmp >> 32;
> +    } else {
> +        *val = env->stimecmp >> 32;
> +    }
> +
>       return RISCV_EXCP_NONE;
>   }
>   
> @@ -852,6 +933,10 @@ static RISCVException write_stimecmp(CPURISCVState *env, int csrno,
>   {
>       RISCVCPU *cpu = env_archcpu(env);
>   
> +    if (riscv_cpu_virt_enabled(env)) {
> +        return write_vstimecmp(env, csrno, val);
> +    }
> +
>       if (riscv_cpu_mxl(env) == MXL_RV32) {
>           env->stimecmp = deposit64(env->stimecmp, 0, 32, (uint64_t)val);
>       } else {
> @@ -868,6 +953,10 @@ static RISCVException write_stimecmph(CPURISCVState *env, int csrno,
>   {
>       RISCVCPU *cpu = env_archcpu(env);
>   
> +    if (riscv_cpu_virt_enabled(env)) {
> +        return write_vstimecmph(env, csrno, val);
> +    }
> +
>       env->stimecmp = deposit64(env->stimecmp, 32, 32, (uint64_t)val);
>       riscv_timer_write_timecmp(cpu, env->stimer, env->stimecmp, 0, MIP_STIP);
>   
> @@ -1805,6 +1894,7 @@ static RISCVException rmw_mip64(CPURISCVState *env, int csrno,
>       if (csrno != CSR_HVIP) {
>           gin = get_field(env->hstatus, HSTATUS_VGEIN);
>           old_mip |= (env->hgeip & ((target_ulong)1 << gin)) ? MIP_VSEIP : 0;
> +        old_mip |= env->vstime_irq ? MIP_VSTIP : 0;
>       }
>   
>       if (ret_val) {
> @@ -3665,6 +3755,12 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
>                                             .min_priv_ver = PRIV_VERSION_1_12_0 },
>       [CSR_STIMECMPH] = { "stimecmph", sstc, read_stimecmph, write_stimecmph,
>                                             .min_priv_ver = PRIV_VERSION_1_12_0 },
> +    [CSR_VSTIMECMP] = { "vstimecmp", sstc_hmode, read_vstimecmp,
> +                                          write_vstimecmp,
> +                                          .min_priv_ver = PRIV_VERSION_1_12_0 },
> +    [CSR_VSTIMECMPH] = { "vstimecmph", sstc_hmode, read_vstimecmph,
> +                                          write_vstimecmph,
> +                                          .min_priv_ver = PRIV_VERSION_1_12_0 },
>   
>       /* Supervisor Protection and Translation */
>       [CSR_SATP]     = { "satp",     smode, read_satp,     write_satp     },
> diff --git a/target/riscv/machine.c b/target/riscv/machine.c
> index 622fface484e..4ba55705d147 100644
> --- a/target/riscv/machine.c
> +++ b/target/riscv/machine.c
> @@ -92,6 +92,7 @@ static const VMStateDescription vmstate_hyper = {
>           VMSTATE_UINTTL(env.hgeie, RISCVCPU),
>           VMSTATE_UINTTL(env.hgeip, RISCVCPU),
>           VMSTATE_UINT64(env.htimedelta, RISCVCPU),
> +        VMSTATE_UINT64(env.vstimecmp, RISCVCPU),
>   
>           VMSTATE_UINTTL(env.hvictl, RISCVCPU),
>           VMSTATE_UINT8_ARRAY(env.hviprio, RISCVCPU, 64),
> diff --git a/target/riscv/time_helper.c b/target/riscv/time_helper.c
> index f3fb5eac7b7b..8cce667dfd47 100644
> --- a/target/riscv/time_helper.c
> +++ b/target/riscv/time_helper.c
> @@ -22,6 +22,14 @@
>   #include "time_helper.h"
>   #include "hw/intc/riscv_aclint.h"
>   
> +static void riscv_vstimer_cb(void *opaque)
> +{
> +    RISCVCPU *cpu = opaque;
> +    CPURISCVState *env = &cpu->env;
> +    env->vstime_irq = 1;
> +    riscv_cpu_update_mip(cpu, MIP_VSTIP, BOOL_TO_MASK(1));
> +}
> +
>   static void riscv_stimer_cb(void *opaque)
>   {
>       RISCVCPU *cpu = opaque;
> @@ -47,10 +55,16 @@ void riscv_timer_write_timecmp(RISCVCPU *cpu, QEMUTimer *timer,
>            * If we're setting an stimecmp value in the "past",
>            * immediately raise the timer interrupt
>            */
> +        if (timer_irq == MIP_VSTIP) {
> +            env->vstime_irq = 1;
> +        }
>           riscv_cpu_update_mip(cpu, timer_irq, BOOL_TO_MASK(1));
>           return;
>       }
>   
> +    if (timer_irq == MIP_VSTIP) {
> +        env->vstime_irq = 0;
> +    }
>       /* Clear the [V]STIP bit in mip */
>       riscv_cpu_update_mip(cpu, timer_irq, BOOL_TO_MASK(0));
>   
> @@ -95,4 +109,6 @@ void riscv_timer_init(RISCVCPU *cpu)
>       env->stimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &riscv_stimer_cb, cpu);
>       env->stimecmp = 0;
>   
> +    env->vstimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &riscv_vstimer_cb, cpu);
> +    env->vstimecmp = 0;
>   }
Atish Kumar Patra Aug. 3, 2022, 9:05 p.m. UTC | #2
On Wed, Aug 3, 2022 at 1:49 AM Weiwei Li <liweiwei@iscas.ac.cn> wrote:

>
> 在 2022/8/3 下午4:25, Atish Patra 写道:
> > vstimecmp CSR allows the guest OS or to program the next guest timer
> > interrupt directly. Thus, hypervisor no longer need to inject the
> > timer interrupt to the guest if vstimecmp is used. This was ratified
> > as a part of the Sstc extension.
> >
> > Signed-off-by: Atish Patra <atishp@rivosinc.com>
> > ---
> >   target/riscv/cpu.h         |   4 ++
> >   target/riscv/cpu_bits.h    |   4 ++
> >   target/riscv/cpu_helper.c  |  11 ++--
> >   target/riscv/csr.c         | 100 ++++++++++++++++++++++++++++++++++++-
> >   target/riscv/machine.c     |   1 +
> >   target/riscv/time_helper.c |  16 ++++++
> >   6 files changed, 131 insertions(+), 5 deletions(-)
> >
> > diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> > index 4cda2905661e..1fd382b2717f 100644
> > --- a/target/riscv/cpu.h
> > +++ b/target/riscv/cpu.h
> > @@ -312,6 +312,8 @@ struct CPUArchState {
> >       /* Sstc CSRs */
> >       uint64_t stimecmp;
> >
> > +    uint64_t vstimecmp;
> > +
> >       /* physical memory protection */
> >       pmp_table_t pmp_state;
> >       target_ulong mseccfg;
> > @@ -366,6 +368,8 @@ struct CPUArchState {
> >
> >       /* Fields from here on are preserved across CPU reset. */
> >       QEMUTimer *stimer; /* Internal timer for S-mode interrupt */
> > +    QEMUTimer *vstimer; /* Internal timer for VS-mode interrupt */
> > +    bool vstime_irq;
> >
> >       hwaddr kernel_addr;
> >       hwaddr fdt_addr;
> > diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
> > index ac17cf1515c0..095dab19f512 100644
> > --- a/target/riscv/cpu_bits.h
> > +++ b/target/riscv/cpu_bits.h
> > @@ -257,6 +257,10 @@
> >   #define CSR_VSIP            0x244
> >   #define CSR_VSATP           0x280
> >
> > +/* Sstc virtual CSRs */
> > +#define CSR_VSTIMECMP       0x24D
> > +#define CSR_VSTIMECMPH      0x25D
> > +
> >   #define CSR_MTINST          0x34a
> >   #define CSR_MTVAL2          0x34b
> >
> > diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
> > index 650574accf0a..1e4faa84e839 100644
> > --- a/target/riscv/cpu_helper.c
> > +++ b/target/riscv/cpu_helper.c
> > @@ -345,8 +345,9 @@ uint64_t riscv_cpu_all_pending(CPURISCVState *env)
> >   {
> >       uint32_t gein = get_field(env->hstatus, HSTATUS_VGEIN);
> >       uint64_t vsgein = (env->hgeip & (1ULL << gein)) ? MIP_VSEIP : 0;
> > +    uint64_t vstip = (env->vstime_irq) ? MIP_VSTIP : 0;
> >
> > -    return (env->mip | vsgein) & env->mie;
> > +    return (env->mip | vsgein | vstip) & env->mie;
> >   }
> >
> >   int riscv_cpu_mirq_pending(CPURISCVState *env)
> > @@ -605,7 +606,7 @@ uint64_t riscv_cpu_update_mip(RISCVCPU *cpu,
> uint64_t mask, uint64_t value)
> >   {
> >       CPURISCVState *env = &cpu->env;
> >       CPUState *cs = CPU(cpu);
> > -    uint64_t gein, vsgein = 0, old = env->mip;
> > +    uint64_t gein, vsgein = 0, vstip = 0, old = env->mip;
> >       bool locked = false;
> >
> >       if (riscv_cpu_virt_enabled(env)) {
> > @@ -613,6 +614,10 @@ uint64_t riscv_cpu_update_mip(RISCVCPU *cpu,
> uint64_t mask, uint64_t value)
> >           vsgein = (env->hgeip & (1ULL << gein)) ? MIP_VSEIP : 0;
> >       }
> >
> > +    /* No need to update mip for VSTIP */
> > +    mask = ((mask == MIP_VSTIP) && env->vstime_irq) ? 0 : mask;
> > +    vstip = env->vstime_irq ? MIP_VSTIP : 0;
> > +
> >       if (!qemu_mutex_iothread_locked()) {
> >           locked = true;
> >           qemu_mutex_lock_iothread();
> > @@ -620,7 +625,7 @@ uint64_t riscv_cpu_update_mip(RISCVCPU *cpu,
> uint64_t mask, uint64_t value)
> >
> >       env->mip = (env->mip & ~mask) | (value & mask);
> >
> > -    if (env->mip | vsgein) {
> > +    if (env->mip | vsgein | vstip) {
> >           cpu_interrupt(cs, CPU_INTERRUPT_HARD);
> >       } else {
> >           cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
> > diff --git a/target/riscv/csr.c b/target/riscv/csr.c
> > index b71e2509b64f..d4265dd3cca2 100644
> > --- a/target/riscv/csr.c
> > +++ b/target/riscv/csr.c
> > @@ -833,17 +833,98 @@ static RISCVException sstc(CPURISCVState *env, int
> csrno)
> >       return RISCV_EXCP_NONE;
> >   }
> >
> > +static RISCVException sstc_hmode(CPURISCVState *env, int csrno)
> > +{
> > +    CPUState *cs = env_cpu(env);
> > +    RISCVCPU *cpu = RISCV_CPU(cs);
> > +
> > +    if (!cpu->cfg.ext_sstc || !env->rdtime_fn) {
> > +        return RISCV_EXCP_ILLEGAL_INST;
> > +    }
> > +
> > +    if (env->priv == PRV_M) {
> > +        return RISCV_EXCP_NONE;
> > +    }
> > +
> > +    if (!(get_field(env->mcounteren, COUNTEREN_TM) &
> > +          get_field(env->menvcfg, MENVCFG_STCE))) {
> > +        return RISCV_EXCP_ILLEGAL_INST;
> > +    }
> > +
> > +    if (!(get_field(env->hcounteren, COUNTEREN_TM) &
> > +          get_field(env->henvcfg, HENVCFG_STCE))) {
> > +        return RISCV_EXCP_VIRT_INSTRUCTION_FAULT;
> > +    }
> > +
> I think hcounteren only works for VS mode here. So we should add check
> for virt  mode is enabled here.
>

vstimecmp can be accessed from HS mode as well. In fact if STCE bit is
disabled in henvcfg,
hypervisor can emulate sstc behavior as well.


> > +    return RISCV_EXCP_NONE;
> > +}
>
> It's better to return hmode(env, csrno) instead of RISCV_EXCP_NONE here.
>
>
Sure. Will change it.


> Regards,
>
> Weiwei Li
>
> > +
> > +static RISCVException read_vstimecmp(CPURISCVState *env, int csrno,
> > +                                    target_ulong *val)
> > +{
> > +    *val = env->vstimecmp;
> > +
> > +    return RISCV_EXCP_NONE;
> > +}
> > +
> > +static RISCVException read_vstimecmph(CPURISCVState *env, int csrno,
> > +                                    target_ulong *val)
> > +{
> > +    *val = env->vstimecmp >> 32;
> > +
> > +    return RISCV_EXCP_NONE;
> > +}
> > +
> > +static RISCVException write_vstimecmp(CPURISCVState *env, int csrno,
> > +                                    target_ulong val)
> > +{
> > +    RISCVCPU *cpu = env_archcpu(env);
> > +
> > +    if (riscv_cpu_mxl(env) == MXL_RV32) {
> > +        env->vstimecmp = deposit64(env->vstimecmp, 0, 32,
> (uint64_t)val);
> > +    } else {
> > +        env->vstimecmp = val;
> > +    }
> > +
> > +    riscv_timer_write_timecmp(cpu, env->vstimer, env->vstimecmp,
> > +                              env->htimedelta, MIP_VSTIP);
> > +
> > +    return RISCV_EXCP_NONE;
> > +}
> > +
> > +static RISCVException write_vstimecmph(CPURISCVState *env, int csrno,
> > +                                    target_ulong val)
> > +{
> > +    RISCVCPU *cpu = env_archcpu(env);
> > +
> > +    env->vstimecmp = deposit64(env->vstimecmp, 32, 32, (uint64_t)val);
> > +    riscv_timer_write_timecmp(cpu, env->vstimer, env->vstimecmp,
> > +                              env->htimedelta, MIP_VSTIP);
> > +
> > +    return RISCV_EXCP_NONE;
> > +}
> > +
> >   static RISCVException read_stimecmp(CPURISCVState *env, int csrno,
> >                                       target_ulong *val)
> >   {
> > -    *val = env->stimecmp;
> > +    if (riscv_cpu_virt_enabled(env)) {
> > +        *val = env->vstimecmp;
> > +    } else {
> > +        *val = env->stimecmp;
> > +    }
> > +
> >       return RISCV_EXCP_NONE;
> >   }
> >
> >   static RISCVException read_stimecmph(CPURISCVState *env, int csrno,
> >                                       target_ulong *val)
> >   {
> > -    *val = env->stimecmp >> 32;
> > +    if (riscv_cpu_virt_enabled(env)) {
> > +        *val = env->vstimecmp >> 32;
> > +    } else {
> > +        *val = env->stimecmp >> 32;
> > +    }
> > +
> >       return RISCV_EXCP_NONE;
> >   }
> >
> > @@ -852,6 +933,10 @@ static RISCVException write_stimecmp(CPURISCVState
> *env, int csrno,
> >   {
> >       RISCVCPU *cpu = env_archcpu(env);
> >
> > +    if (riscv_cpu_virt_enabled(env)) {
> > +        return write_vstimecmp(env, csrno, val);
> > +    }
> > +
> >       if (riscv_cpu_mxl(env) == MXL_RV32) {
> >           env->stimecmp = deposit64(env->stimecmp, 0, 32, (uint64_t)val);
> >       } else {
> > @@ -868,6 +953,10 @@ static RISCVException write_stimecmph(CPURISCVState
> *env, int csrno,
> >   {
> >       RISCVCPU *cpu = env_archcpu(env);
> >
> > +    if (riscv_cpu_virt_enabled(env)) {
> > +        return write_vstimecmph(env, csrno, val);
> > +    }
> > +
> >       env->stimecmp = deposit64(env->stimecmp, 32, 32, (uint64_t)val);
> >       riscv_timer_write_timecmp(cpu, env->stimer, env->stimecmp, 0,
> MIP_STIP);
> >
> > @@ -1805,6 +1894,7 @@ static RISCVException rmw_mip64(CPURISCVState
> *env, int csrno,
> >       if (csrno != CSR_HVIP) {
> >           gin = get_field(env->hstatus, HSTATUS_VGEIN);
> >           old_mip |= (env->hgeip & ((target_ulong)1 << gin)) ? MIP_VSEIP
> : 0;
> > +        old_mip |= env->vstime_irq ? MIP_VSTIP : 0;
> >       }
> >
> >       if (ret_val) {
> > @@ -3665,6 +3755,12 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
> >                                             .min_priv_ver =
> PRIV_VERSION_1_12_0 },
> >       [CSR_STIMECMPH] = { "stimecmph", sstc, read_stimecmph,
> write_stimecmph,
> >                                             .min_priv_ver =
> PRIV_VERSION_1_12_0 },
> > +    [CSR_VSTIMECMP] = { "vstimecmp", sstc_hmode, read_vstimecmp,
> > +                                          write_vstimecmp,
> > +                                          .min_priv_ver =
> PRIV_VERSION_1_12_0 },
> > +    [CSR_VSTIMECMPH] = { "vstimecmph", sstc_hmode, read_vstimecmph,
> > +                                          write_vstimecmph,
> > +                                          .min_priv_ver =
> PRIV_VERSION_1_12_0 },
> >
> >       /* Supervisor Protection and Translation */
> >       [CSR_SATP]     = { "satp",     smode, read_satp,     write_satp
>  },
> > diff --git a/target/riscv/machine.c b/target/riscv/machine.c
> > index 622fface484e..4ba55705d147 100644
> > --- a/target/riscv/machine.c
> > +++ b/target/riscv/machine.c
> > @@ -92,6 +92,7 @@ static const VMStateDescription vmstate_hyper = {
> >           VMSTATE_UINTTL(env.hgeie, RISCVCPU),
> >           VMSTATE_UINTTL(env.hgeip, RISCVCPU),
> >           VMSTATE_UINT64(env.htimedelta, RISCVCPU),
> > +        VMSTATE_UINT64(env.vstimecmp, RISCVCPU),
> >
> >           VMSTATE_UINTTL(env.hvictl, RISCVCPU),
> >           VMSTATE_UINT8_ARRAY(env.hviprio, RISCVCPU, 64),
> > diff --git a/target/riscv/time_helper.c b/target/riscv/time_helper.c
> > index f3fb5eac7b7b..8cce667dfd47 100644
> > --- a/target/riscv/time_helper.c
> > +++ b/target/riscv/time_helper.c
> > @@ -22,6 +22,14 @@
> >   #include "time_helper.h"
> >   #include "hw/intc/riscv_aclint.h"
> >
> > +static void riscv_vstimer_cb(void *opaque)
> > +{
> > +    RISCVCPU *cpu = opaque;
> > +    CPURISCVState *env = &cpu->env;
> > +    env->vstime_irq = 1;
> > +    riscv_cpu_update_mip(cpu, MIP_VSTIP, BOOL_TO_MASK(1));
> > +}
> > +
> >   static void riscv_stimer_cb(void *opaque)
> >   {
> >       RISCVCPU *cpu = opaque;
> > @@ -47,10 +55,16 @@ void riscv_timer_write_timecmp(RISCVCPU *cpu,
> QEMUTimer *timer,
> >            * If we're setting an stimecmp value in the "past",
> >            * immediately raise the timer interrupt
> >            */
> > +        if (timer_irq == MIP_VSTIP) {
> > +            env->vstime_irq = 1;
> > +        }
> >           riscv_cpu_update_mip(cpu, timer_irq, BOOL_TO_MASK(1));
> >           return;
> >       }
> >
> > +    if (timer_irq == MIP_VSTIP) {
> > +        env->vstime_irq = 0;
> > +    }
> >       /* Clear the [V]STIP bit in mip */
> >       riscv_cpu_update_mip(cpu, timer_irq, BOOL_TO_MASK(0));
> >
> > @@ -95,4 +109,6 @@ void riscv_timer_init(RISCVCPU *cpu)
> >       env->stimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &riscv_stimer_cb,
> cpu);
> >       env->stimecmp = 0;
> >
> > +    env->vstimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &riscv_vstimer_cb,
> cpu);
> > +    env->vstimecmp = 0;
> >   }
>
>
Weiwei Li Aug. 4, 2022, 1:15 a.m. UTC | #3
在 2022/8/4 上午5:05, Atish Kumar Patra 写道:
>
>
> On Wed, Aug 3, 2022 at 1:49 AM Weiwei Li <liweiwei@iscas.ac.cn 
> <mailto:liweiwei@iscas.ac.cn>> wrote:
>
>
>     在 2022/8/3 下午4:25, Atish Patra 写道:
>     > vstimecmp CSR allows the guest OS or to program the next guest timer
>     > interrupt directly. Thus, hypervisor no longer need to inject the
>     > timer interrupt to the guest if vstimecmp is used. This was ratified
>     > as a part of the Sstc extension.
>     >
>     > Signed-off-by: Atish Patra <atishp@rivosinc.com
>     <mailto:atishp@rivosinc.com>>
>     > ---
>     >   target/riscv/cpu.h         |   4 ++
>     >   target/riscv/cpu_bits.h    |   4 ++
>     >   target/riscv/cpu_helper.c  |  11 ++--
>     >   target/riscv/csr.c         | 100
>     ++++++++++++++++++++++++++++++++++++-
>     >   target/riscv/machine.c     |   1 +
>     >   target/riscv/time_helper.c |  16 ++++++
>     >   6 files changed, 131 insertions(+), 5 deletions(-)
>     >
>     > diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
>     > index 4cda2905661e..1fd382b2717f 100644
>     > --- a/target/riscv/cpu.h
>     > +++ b/target/riscv/cpu.h
>     > @@ -312,6 +312,8 @@ struct CPUArchState {
>     >       /* Sstc CSRs */
>     >       uint64_t stimecmp;
>     >
>     > +    uint64_t vstimecmp;
>     > +
>     >       /* physical memory protection */
>     >       pmp_table_t pmp_state;
>     >       target_ulong mseccfg;
>     > @@ -366,6 +368,8 @@ struct CPUArchState {
>     >
>     >       /* Fields from here on are preserved across CPU reset. */
>     >       QEMUTimer *stimer; /* Internal timer for S-mode interrupt */
>     > +    QEMUTimer *vstimer; /* Internal timer for VS-mode interrupt */
>     > +    bool vstime_irq;
>     >
>     >       hwaddr kernel_addr;
>     >       hwaddr fdt_addr;
>     > diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
>     > index ac17cf1515c0..095dab19f512 100644
>     > --- a/target/riscv/cpu_bits.h
>     > +++ b/target/riscv/cpu_bits.h
>     > @@ -257,6 +257,10 @@
>     >   #define CSR_VSIP            0x244
>     >   #define CSR_VSATP           0x280
>     >
>     > +/* Sstc virtual CSRs */
>     > +#define CSR_VSTIMECMP       0x24D
>     > +#define CSR_VSTIMECMPH      0x25D
>     > +
>     >   #define CSR_MTINST          0x34a
>     >   #define CSR_MTVAL2          0x34b
>     >
>     > diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
>     > index 650574accf0a..1e4faa84e839 100644
>     > --- a/target/riscv/cpu_helper.c
>     > +++ b/target/riscv/cpu_helper.c
>     > @@ -345,8 +345,9 @@ uint64_t riscv_cpu_all_pending(CPURISCVState
>     *env)
>     >   {
>     >       uint32_t gein = get_field(env->hstatus, HSTATUS_VGEIN);
>     >       uint64_t vsgein = (env->hgeip & (1ULL << gein)) ?
>     MIP_VSEIP : 0;
>     > +    uint64_t vstip = (env->vstime_irq) ? MIP_VSTIP : 0;
>     >
>     > -    return (env->mip | vsgein) & env->mie;
>     > +    return (env->mip | vsgein | vstip) & env->mie;
>     >   }
>     >
>     >   int riscv_cpu_mirq_pending(CPURISCVState *env)
>     > @@ -605,7 +606,7 @@ uint64_t riscv_cpu_update_mip(RISCVCPU *cpu,
>     uint64_t mask, uint64_t value)
>     >   {
>     >       CPURISCVState *env = &cpu->env;
>     >       CPUState *cs = CPU(cpu);
>     > -    uint64_t gein, vsgein = 0, old = env->mip;
>     > +    uint64_t gein, vsgein = 0, vstip = 0, old = env->mip;
>     >       bool locked = false;
>     >
>     >       if (riscv_cpu_virt_enabled(env)) {
>     > @@ -613,6 +614,10 @@ uint64_t riscv_cpu_update_mip(RISCVCPU
>     *cpu, uint64_t mask, uint64_t value)
>     >           vsgein = (env->hgeip & (1ULL << gein)) ? MIP_VSEIP : 0;
>     >       }
>     >
>     > +    /* No need to update mip for VSTIP */
>     > +    mask = ((mask == MIP_VSTIP) && env->vstime_irq) ? 0 : mask;
>     > +    vstip = env->vstime_irq ? MIP_VSTIP : 0;
>     > +
>     >       if (!qemu_mutex_iothread_locked()) {
>     >           locked = true;
>     >           qemu_mutex_lock_iothread();
>     > @@ -620,7 +625,7 @@ uint64_t riscv_cpu_update_mip(RISCVCPU *cpu,
>     uint64_t mask, uint64_t value)
>     >
>     >       env->mip = (env->mip & ~mask) | (value & mask);
>     >
>     > -    if (env->mip | vsgein) {
>     > +    if (env->mip | vsgein | vstip) {
>     >           cpu_interrupt(cs, CPU_INTERRUPT_HARD);
>     >       } else {
>     >           cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
>     > diff --git a/target/riscv/csr.c b/target/riscv/csr.c
>     > index b71e2509b64f..d4265dd3cca2 100644
>     > --- a/target/riscv/csr.c
>     > +++ b/target/riscv/csr.c
>     > @@ -833,17 +833,98 @@ static RISCVException sstc(CPURISCVState
>     *env, int csrno)
>     >       return RISCV_EXCP_NONE;
>     >   }
>     >
>     > +static RISCVException sstc_hmode(CPURISCVState *env, int csrno)
>     > +{
>     > +    CPUState *cs = env_cpu(env);
>     > +    RISCVCPU *cpu = RISCV_CPU(cs);
>     > +
>     > +    if (!cpu->cfg.ext_sstc || !env->rdtime_fn) {
>     > +        return RISCV_EXCP_ILLEGAL_INST;
>     > +    }
>     > +
>     > +    if (env->priv == PRV_M) {
>     > +        return RISCV_EXCP_NONE;
>     > +    }
>     > +
>     > +    if (!(get_field(env->mcounteren, COUNTEREN_TM) &
>     > +          get_field(env->menvcfg, MENVCFG_STCE))) {
>     > +        return RISCV_EXCP_ILLEGAL_INST;
>     > +    }
>     > +
>     > +    if (!(get_field(env->hcounteren, COUNTEREN_TM) &
>     > +          get_field(env->henvcfg, HENVCFG_STCE))) {
>     > +        return RISCV_EXCP_VIRT_INSTRUCTION_FAULT;
>     > +    }
>     > +
>     I think hcounteren only works for VS mode here. So we should add
>     check
>     for virt  mode is enabled here.
>
>
> vstimecmp can be accessed from HS mode as well. In fact if STCE bit is 
> disabled in henvcfg,
> hypervisor can emulate sstc behavior as well.

Yes. Because of this, we should add check for virt  mode is enabled 
here. If not, HS mode access will also

trigger RISCV_EXCP_VIRT_INSTRUCTION_FAULT if hcounteren.TM is zero.

Regards,

Weiwei Li

>     > +    return RISCV_EXCP_NONE;
>     > +}
>
>     It's better to return hmode(env, csrno) instead of RISCV_EXCP_NONE
>     here.
>
>
> Sure. Will change it.
>
>     Regards,
>
>     Weiwei Li
>
>     > +
>     > +static RISCVException read_vstimecmp(CPURISCVState *env, int csrno,
>     > +                                    target_ulong *val)
>     > +{
>     > +    *val = env->vstimecmp;
>     > +
>     > +    return RISCV_EXCP_NONE;
>     > +}
>     > +
>     > +static RISCVException read_vstimecmph(CPURISCVState *env, int
>     csrno,
>     > +                                    target_ulong *val)
>     > +{
>     > +    *val = env->vstimecmp >> 32;
>     > +
>     > +    return RISCV_EXCP_NONE;
>     > +}
>     > +
>     > +static RISCVException write_vstimecmp(CPURISCVState *env, int
>     csrno,
>     > +                                    target_ulong val)
>     > +{
>     > +    RISCVCPU *cpu = env_archcpu(env);
>     > +
>     > +    if (riscv_cpu_mxl(env) == MXL_RV32) {
>     > +        env->vstimecmp = deposit64(env->vstimecmp, 0, 32,
>     (uint64_t)val);
>     > +    } else {
>     > +        env->vstimecmp = val;
>     > +    }
>     > +
>     > +    riscv_timer_write_timecmp(cpu, env->vstimer, env->vstimecmp,
>     > +                              env->htimedelta, MIP_VSTIP);
>     > +
>     > +    return RISCV_EXCP_NONE;
>     > +}
>     > +
>     > +static RISCVException write_vstimecmph(CPURISCVState *env, int
>     csrno,
>     > +                                    target_ulong val)
>     > +{
>     > +    RISCVCPU *cpu = env_archcpu(env);
>     > +
>     > +    env->vstimecmp = deposit64(env->vstimecmp, 32, 32,
>     (uint64_t)val);
>     > +    riscv_timer_write_timecmp(cpu, env->vstimer, env->vstimecmp,
>     > +                              env->htimedelta, MIP_VSTIP);
>     > +
>     > +    return RISCV_EXCP_NONE;
>     > +}
>     > +
>     >   static RISCVException read_stimecmp(CPURISCVState *env, int csrno,
>     >                                       target_ulong *val)
>     >   {
>     > -    *val = env->stimecmp;
>     > +    if (riscv_cpu_virt_enabled(env)) {
>     > +        *val = env->vstimecmp;
>     > +    } else {
>     > +        *val = env->stimecmp;
>     > +    }
>     > +
>     >       return RISCV_EXCP_NONE;
>     >   }
>     >
>     >   static RISCVException read_stimecmph(CPURISCVState *env, int
>     csrno,
>     >                                       target_ulong *val)
>     >   {
>     > -    *val = env->stimecmp >> 32;
>     > +    if (riscv_cpu_virt_enabled(env)) {
>     > +        *val = env->vstimecmp >> 32;
>     > +    } else {
>     > +        *val = env->stimecmp >> 32;
>     > +    }
>     > +
>     >       return RISCV_EXCP_NONE;
>     >   }
>     >
>     > @@ -852,6 +933,10 @@ static RISCVException
>     write_stimecmp(CPURISCVState *env, int csrno,
>     >   {
>     >       RISCVCPU *cpu = env_archcpu(env);
>     >
>     > +    if (riscv_cpu_virt_enabled(env)) {
>     > +        return write_vstimecmp(env, csrno, val);
>     > +    }
>     > +
>     >       if (riscv_cpu_mxl(env) == MXL_RV32) {
>     >           env->stimecmp = deposit64(env->stimecmp, 0, 32,
>     (uint64_t)val);
>     >       } else {
>     > @@ -868,6 +953,10 @@ static RISCVException
>     write_stimecmph(CPURISCVState *env, int csrno,
>     >   {
>     >       RISCVCPU *cpu = env_archcpu(env);
>     >
>     > +    if (riscv_cpu_virt_enabled(env)) {
>     > +        return write_vstimecmph(env, csrno, val);
>     > +    }
>     > +
>     >       env->stimecmp = deposit64(env->stimecmp, 32, 32,
>     (uint64_t)val);
>     >       riscv_timer_write_timecmp(cpu, env->stimer, env->stimecmp,
>     0, MIP_STIP);
>     >
>     > @@ -1805,6 +1894,7 @@ static RISCVException
>     rmw_mip64(CPURISCVState *env, int csrno,
>     >       if (csrno != CSR_HVIP) {
>     >           gin = get_field(env->hstatus, HSTATUS_VGEIN);
>     >           old_mip |= (env->hgeip & ((target_ulong)1 << gin)) ?
>     MIP_VSEIP : 0;
>     > +        old_mip |= env->vstime_irq ? MIP_VSTIP : 0;
>     >       }
>     >
>     >       if (ret_val) {
>     > @@ -3665,6 +3755,12 @@ riscv_csr_operations
>     csr_ops[CSR_TABLE_SIZE] = {
>     >  .min_priv_ver = PRIV_VERSION_1_12_0 },
>     >       [CSR_STIMECMPH] = { "stimecmph", sstc, read_stimecmph,
>     write_stimecmph,
>     >  .min_priv_ver = PRIV_VERSION_1_12_0 },
>     > +    [CSR_VSTIMECMP] = { "vstimecmp", sstc_hmode, read_vstimecmp,
>     > + write_vstimecmp,
>     > + .min_priv_ver = PRIV_VERSION_1_12_0 },
>     > +    [CSR_VSTIMECMPH] = { "vstimecmph", sstc_hmode, read_vstimecmph,
>     > + write_vstimecmph,
>     > + .min_priv_ver = PRIV_VERSION_1_12_0 },
>     >
>     >       /* Supervisor Protection and Translation */
>     >       [CSR_SATP]     = { "satp",     smode, read_satp,  
>      write_satp     },
>     > diff --git a/target/riscv/machine.c b/target/riscv/machine.c
>     > index 622fface484e..4ba55705d147 100644
>     > --- a/target/riscv/machine.c
>     > +++ b/target/riscv/machine.c
>     > @@ -92,6 +92,7 @@ static const VMStateDescription vmstate_hyper = {
>     >           VMSTATE_UINTTL(env.hgeie, RISCVCPU),
>     >           VMSTATE_UINTTL(env.hgeip, RISCVCPU),
>     >           VMSTATE_UINT64(env.htimedelta, RISCVCPU),
>     > +        VMSTATE_UINT64(env.vstimecmp, RISCVCPU),
>     >
>     >           VMSTATE_UINTTL(env.hvictl, RISCVCPU),
>     >           VMSTATE_UINT8_ARRAY(env.hviprio, RISCVCPU, 64),
>     > diff --git a/target/riscv/time_helper.c b/target/riscv/time_helper.c
>     > index f3fb5eac7b7b..8cce667dfd47 100644
>     > --- a/target/riscv/time_helper.c
>     > +++ b/target/riscv/time_helper.c
>     > @@ -22,6 +22,14 @@
>     >   #include "time_helper.h"
>     >   #include "hw/intc/riscv_aclint.h"
>     >
>     > +static void riscv_vstimer_cb(void *opaque)
>     > +{
>     > +    RISCVCPU *cpu = opaque;
>     > +    CPURISCVState *env = &cpu->env;
>     > +    env->vstime_irq = 1;
>     > +    riscv_cpu_update_mip(cpu, MIP_VSTIP, BOOL_TO_MASK(1));
>     > +}
>     > +
>     >   static void riscv_stimer_cb(void *opaque)
>     >   {
>     >       RISCVCPU *cpu = opaque;
>     > @@ -47,10 +55,16 @@ void riscv_timer_write_timecmp(RISCVCPU
>     *cpu, QEMUTimer *timer,
>     >            * If we're setting an stimecmp value in the "past",
>     >            * immediately raise the timer interrupt
>     >            */
>     > +        if (timer_irq == MIP_VSTIP) {
>     > +            env->vstime_irq = 1;
>     > +        }
>     >           riscv_cpu_update_mip(cpu, timer_irq, BOOL_TO_MASK(1));
>     >           return;
>     >       }
>     >
>     > +    if (timer_irq == MIP_VSTIP) {
>     > +        env->vstime_irq = 0;
>     > +    }
>     >       /* Clear the [V]STIP bit in mip */
>     >       riscv_cpu_update_mip(cpu, timer_irq, BOOL_TO_MASK(0));
>     >
>     > @@ -95,4 +109,6 @@ void riscv_timer_init(RISCVCPU *cpu)
>     >       env->stimer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
>     &riscv_stimer_cb, cpu);
>     >       env->stimecmp = 0;
>     >
>     > +    env->vstimer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
>     &riscv_vstimer_cb, cpu);
>     > +    env->vstimecmp = 0;
>     >   }
>
diff mbox series

Patch

diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 4cda2905661e..1fd382b2717f 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -312,6 +312,8 @@  struct CPUArchState {
     /* Sstc CSRs */
     uint64_t stimecmp;
 
+    uint64_t vstimecmp;
+
     /* physical memory protection */
     pmp_table_t pmp_state;
     target_ulong mseccfg;
@@ -366,6 +368,8 @@  struct CPUArchState {
 
     /* Fields from here on are preserved across CPU reset. */
     QEMUTimer *stimer; /* Internal timer for S-mode interrupt */
+    QEMUTimer *vstimer; /* Internal timer for VS-mode interrupt */
+    bool vstime_irq;
 
     hwaddr kernel_addr;
     hwaddr fdt_addr;
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index ac17cf1515c0..095dab19f512 100644
--- a/target/riscv/cpu_bits.h
+++ b/target/riscv/cpu_bits.h
@@ -257,6 +257,10 @@ 
 #define CSR_VSIP            0x244
 #define CSR_VSATP           0x280
 
+/* Sstc virtual CSRs */
+#define CSR_VSTIMECMP       0x24D
+#define CSR_VSTIMECMPH      0x25D
+
 #define CSR_MTINST          0x34a
 #define CSR_MTVAL2          0x34b
 
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index 650574accf0a..1e4faa84e839 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -345,8 +345,9 @@  uint64_t riscv_cpu_all_pending(CPURISCVState *env)
 {
     uint32_t gein = get_field(env->hstatus, HSTATUS_VGEIN);
     uint64_t vsgein = (env->hgeip & (1ULL << gein)) ? MIP_VSEIP : 0;
+    uint64_t vstip = (env->vstime_irq) ? MIP_VSTIP : 0;
 
-    return (env->mip | vsgein) & env->mie;
+    return (env->mip | vsgein | vstip) & env->mie;
 }
 
 int riscv_cpu_mirq_pending(CPURISCVState *env)
@@ -605,7 +606,7 @@  uint64_t riscv_cpu_update_mip(RISCVCPU *cpu, uint64_t mask, uint64_t value)
 {
     CPURISCVState *env = &cpu->env;
     CPUState *cs = CPU(cpu);
-    uint64_t gein, vsgein = 0, old = env->mip;
+    uint64_t gein, vsgein = 0, vstip = 0, old = env->mip;
     bool locked = false;
 
     if (riscv_cpu_virt_enabled(env)) {
@@ -613,6 +614,10 @@  uint64_t riscv_cpu_update_mip(RISCVCPU *cpu, uint64_t mask, uint64_t value)
         vsgein = (env->hgeip & (1ULL << gein)) ? MIP_VSEIP : 0;
     }
 
+    /* No need to update mip for VSTIP */
+    mask = ((mask == MIP_VSTIP) && env->vstime_irq) ? 0 : mask;
+    vstip = env->vstime_irq ? MIP_VSTIP : 0;
+
     if (!qemu_mutex_iothread_locked()) {
         locked = true;
         qemu_mutex_lock_iothread();
@@ -620,7 +625,7 @@  uint64_t riscv_cpu_update_mip(RISCVCPU *cpu, uint64_t mask, uint64_t value)
 
     env->mip = (env->mip & ~mask) | (value & mask);
 
-    if (env->mip | vsgein) {
+    if (env->mip | vsgein | vstip) {
         cpu_interrupt(cs, CPU_INTERRUPT_HARD);
     } else {
         cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index b71e2509b64f..d4265dd3cca2 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -833,17 +833,98 @@  static RISCVException sstc(CPURISCVState *env, int csrno)
     return RISCV_EXCP_NONE;
 }
 
+static RISCVException sstc_hmode(CPURISCVState *env, int csrno)
+{
+    CPUState *cs = env_cpu(env);
+    RISCVCPU *cpu = RISCV_CPU(cs);
+
+    if (!cpu->cfg.ext_sstc || !env->rdtime_fn) {
+        return RISCV_EXCP_ILLEGAL_INST;
+    }
+
+    if (env->priv == PRV_M) {
+        return RISCV_EXCP_NONE;
+    }
+
+    if (!(get_field(env->mcounteren, COUNTEREN_TM) &
+          get_field(env->menvcfg, MENVCFG_STCE))) {
+        return RISCV_EXCP_ILLEGAL_INST;
+    }
+
+    if (!(get_field(env->hcounteren, COUNTEREN_TM) &
+          get_field(env->henvcfg, HENVCFG_STCE))) {
+        return RISCV_EXCP_VIRT_INSTRUCTION_FAULT;
+    }
+
+    return RISCV_EXCP_NONE;
+}
+
+static RISCVException read_vstimecmp(CPURISCVState *env, int csrno,
+                                    target_ulong *val)
+{
+    *val = env->vstimecmp;
+
+    return RISCV_EXCP_NONE;
+}
+
+static RISCVException read_vstimecmph(CPURISCVState *env, int csrno,
+                                    target_ulong *val)
+{
+    *val = env->vstimecmp >> 32;
+
+    return RISCV_EXCP_NONE;
+}
+
+static RISCVException write_vstimecmp(CPURISCVState *env, int csrno,
+                                    target_ulong val)
+{
+    RISCVCPU *cpu = env_archcpu(env);
+
+    if (riscv_cpu_mxl(env) == MXL_RV32) {
+        env->vstimecmp = deposit64(env->vstimecmp, 0, 32, (uint64_t)val);
+    } else {
+        env->vstimecmp = val;
+    }
+
+    riscv_timer_write_timecmp(cpu, env->vstimer, env->vstimecmp,
+                              env->htimedelta, MIP_VSTIP);
+
+    return RISCV_EXCP_NONE;
+}
+
+static RISCVException write_vstimecmph(CPURISCVState *env, int csrno,
+                                    target_ulong val)
+{
+    RISCVCPU *cpu = env_archcpu(env);
+
+    env->vstimecmp = deposit64(env->vstimecmp, 32, 32, (uint64_t)val);
+    riscv_timer_write_timecmp(cpu, env->vstimer, env->vstimecmp,
+                              env->htimedelta, MIP_VSTIP);
+
+    return RISCV_EXCP_NONE;
+}
+
 static RISCVException read_stimecmp(CPURISCVState *env, int csrno,
                                     target_ulong *val)
 {
-    *val = env->stimecmp;
+    if (riscv_cpu_virt_enabled(env)) {
+        *val = env->vstimecmp;
+    } else {
+        *val = env->stimecmp;
+    }
+
     return RISCV_EXCP_NONE;
 }
 
 static RISCVException read_stimecmph(CPURISCVState *env, int csrno,
                                     target_ulong *val)
 {
-    *val = env->stimecmp >> 32;
+    if (riscv_cpu_virt_enabled(env)) {
+        *val = env->vstimecmp >> 32;
+    } else {
+        *val = env->stimecmp >> 32;
+    }
+
     return RISCV_EXCP_NONE;
 }
 
@@ -852,6 +933,10 @@  static RISCVException write_stimecmp(CPURISCVState *env, int csrno,
 {
     RISCVCPU *cpu = env_archcpu(env);
 
+    if (riscv_cpu_virt_enabled(env)) {
+        return write_vstimecmp(env, csrno, val);
+    }
+
     if (riscv_cpu_mxl(env) == MXL_RV32) {
         env->stimecmp = deposit64(env->stimecmp, 0, 32, (uint64_t)val);
     } else {
@@ -868,6 +953,10 @@  static RISCVException write_stimecmph(CPURISCVState *env, int csrno,
 {
     RISCVCPU *cpu = env_archcpu(env);
 
+    if (riscv_cpu_virt_enabled(env)) {
+        return write_vstimecmph(env, csrno, val);
+    }
+
     env->stimecmp = deposit64(env->stimecmp, 32, 32, (uint64_t)val);
     riscv_timer_write_timecmp(cpu, env->stimer, env->stimecmp, 0, MIP_STIP);
 
@@ -1805,6 +1894,7 @@  static RISCVException rmw_mip64(CPURISCVState *env, int csrno,
     if (csrno != CSR_HVIP) {
         gin = get_field(env->hstatus, HSTATUS_VGEIN);
         old_mip |= (env->hgeip & ((target_ulong)1 << gin)) ? MIP_VSEIP : 0;
+        old_mip |= env->vstime_irq ? MIP_VSTIP : 0;
     }
 
     if (ret_val) {
@@ -3665,6 +3755,12 @@  riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
                                           .min_priv_ver = PRIV_VERSION_1_12_0 },
     [CSR_STIMECMPH] = { "stimecmph", sstc, read_stimecmph, write_stimecmph,
                                           .min_priv_ver = PRIV_VERSION_1_12_0 },
+    [CSR_VSTIMECMP] = { "vstimecmp", sstc_hmode, read_vstimecmp,
+                                          write_vstimecmp,
+                                          .min_priv_ver = PRIV_VERSION_1_12_0 },
+    [CSR_VSTIMECMPH] = { "vstimecmph", sstc_hmode, read_vstimecmph,
+                                          write_vstimecmph,
+                                          .min_priv_ver = PRIV_VERSION_1_12_0 },
 
     /* Supervisor Protection and Translation */
     [CSR_SATP]     = { "satp",     smode, read_satp,     write_satp     },
diff --git a/target/riscv/machine.c b/target/riscv/machine.c
index 622fface484e..4ba55705d147 100644
--- a/target/riscv/machine.c
+++ b/target/riscv/machine.c
@@ -92,6 +92,7 @@  static const VMStateDescription vmstate_hyper = {
         VMSTATE_UINTTL(env.hgeie, RISCVCPU),
         VMSTATE_UINTTL(env.hgeip, RISCVCPU),
         VMSTATE_UINT64(env.htimedelta, RISCVCPU),
+        VMSTATE_UINT64(env.vstimecmp, RISCVCPU),
 
         VMSTATE_UINTTL(env.hvictl, RISCVCPU),
         VMSTATE_UINT8_ARRAY(env.hviprio, RISCVCPU, 64),
diff --git a/target/riscv/time_helper.c b/target/riscv/time_helper.c
index f3fb5eac7b7b..8cce667dfd47 100644
--- a/target/riscv/time_helper.c
+++ b/target/riscv/time_helper.c
@@ -22,6 +22,14 @@ 
 #include "time_helper.h"
 #include "hw/intc/riscv_aclint.h"
 
+static void riscv_vstimer_cb(void *opaque)
+{
+    RISCVCPU *cpu = opaque;
+    CPURISCVState *env = &cpu->env;
+    env->vstime_irq = 1;
+    riscv_cpu_update_mip(cpu, MIP_VSTIP, BOOL_TO_MASK(1));
+}
+
 static void riscv_stimer_cb(void *opaque)
 {
     RISCVCPU *cpu = opaque;
@@ -47,10 +55,16 @@  void riscv_timer_write_timecmp(RISCVCPU *cpu, QEMUTimer *timer,
          * If we're setting an stimecmp value in the "past",
          * immediately raise the timer interrupt
          */
+        if (timer_irq == MIP_VSTIP) {
+            env->vstime_irq = 1;
+        }
         riscv_cpu_update_mip(cpu, timer_irq, BOOL_TO_MASK(1));
         return;
     }
 
+    if (timer_irq == MIP_VSTIP) {
+        env->vstime_irq = 0;
+    }
     /* Clear the [V]STIP bit in mip */
     riscv_cpu_update_mip(cpu, timer_irq, BOOL_TO_MASK(0));
 
@@ -95,4 +109,6 @@  void riscv_timer_init(RISCVCPU *cpu)
     env->stimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &riscv_stimer_cb, cpu);
     env->stimecmp = 0;
 
+    env->vstimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &riscv_vstimer_cb, cpu);
+    env->vstimecmp = 0;
 }