[PATCH v2] target/riscv: Fix LCOFI interrupt masking in sie/sip/mvip

Mayuresh Chitale posted 1 patch 2 weeks, 5 days ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/20260907065112.2287127-1-mayuresh.chitale@oss.qualcomm.com
Maintainers: Palmer Dabbelt <palmer@dabbelt.com>, Alistair Francis <alistair.francis@wdc.com>, Weiwei Li <liwei1518@gmail.com>, Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com>, Liu Zhiwei <zhiwei_liu@linux.alibaba.com>, Chao Liu <chao.liu@processmission.com>
target/riscv/tcg/csr.c | 59 +++++++++++++++++++++++++++++++++++-------
1 file changed, 50 insertions(+), 9 deletions(-)
[PATCH v2] target/riscv: Fix LCOFI interrupt masking in sie/sip/mvip
Posted by Mayuresh Chitale 2 weeks, 5 days ago
Commit 27f9566dcd98 dropped bit 13 (MIP_LCOFIP) from the LOCAL_INTERRUPTS
macro and hence also from individual masks such as all_ints,
sip_writable_mask etc.

As a result, with Sscofpmf enabled and LCOFIP delegated to S-mode, the
overflow interrupt was raised in mip but was not visible in sip and it
never got delivered to the PMU driver. For the same reason, sie.LCOFIE
also became read-only 0.

Add back bit 13 in the affected masks, but gate it on Sscofpmf being
implemented. In addition, for the virtual interrupt path, mvip/mvien
LCOFI also requires Smcdeleg and Smaia to be implemented. Also make
LCOFI delegable only if Sscofpmf is implemented.

Fixes: 27f9566dcd98 ("target/riscv: Update the local interrupt mask")
Suggested-by: Gong Shuai <gsh517025@gmail.com>
Suggested-by: TianCheng TANG <lyndra@linux.alibaba.com>
Signed-off-by: Mayuresh Chitale <mayuresh.chitale@oss.qualcomm.com>
---
v2:
- Fix the sip.LCOFIP read-back mask in rmw_sip64() so that it matches
  the write path.
- Gate LCOFI on the Sscofpmf, Smcdeleg and Smaia extensions, depending
  on the register being accessed and on whether it is the virtual
  interrupt path.

 target/riscv/tcg/csr.c | 59 +++++++++++++++++++++++++++++++++++-------
 1 file changed, 50 insertions(+), 9 deletions(-)

diff --git a/target/riscv/tcg/csr.c b/target/riscv/tcg/csr.c
index 002f7e69c1..966a0897bb 100644
--- a/target/riscv/tcg/csr.c
+++ b/target/riscv/tcg/csr.c
@@ -1812,7 +1812,7 @@ static RISCVException write_stimecmph(CPURISCVState *env, int csrno,
 #define LOCAL_INTERRUPTS   (~0xFFFFULL)
 
 static const uint64_t delegable_ints =
-    S_MODE_INTERRUPTS | VS_MODE_INTERRUPTS | MIP_LCOFIP;
+    S_MODE_INTERRUPTS | VS_MODE_INTERRUPTS;
 static const uint64_t vs_delegable_ints =
     (VS_MODE_INTERRUPTS | LOCAL_INTERRUPTS) & ~MIP_LCOFIP;
 static const uint64_t all_ints = M_MODE_INTERRUPTS | S_MODE_INTERRUPTS |
@@ -1874,6 +1874,25 @@ static const uint64_t hvien_writable_mask = LOCAL_INTERRUPTS;
 
 static const uint64_t vsip_writable_mask = MIP_VSSIP | LOCAL_INTERRUPTS;
 
+static bool lcofi_present(CPURISCVState *env, bool virt_int)
+{
+    const RISCVCPUConfig *cfg = riscv_cpu_cfg(env);
+
+    if (!cfg->ext_sscofpmf) {
+        return false;
+    }
+
+    /*
+     * For the virtual interrupts path, we also need to check if smcdeleg and
+     * smaia are both implemented.
+     */
+    if (virt_int && !(cfg->ext_smcdeleg && cfg->ext_smaia)) {
+        return false;
+    }
+
+    return true;
+}
+
 /* Machine Information Registers */
 static RISCVException read_zero(CPURISCVState *env, int csrno,
                                 target_ulong *val)
@@ -2248,6 +2267,10 @@ static RISCVException rmw_mideleg64(CPURISCVState *env, int csrno,
 {
     uint64_t mask = wr_mask & delegable_ints;
 
+    if (lcofi_present(env, false)) {
+        mask |= wr_mask & MIP_LCOFIP;
+    }
+
     if (ret_val) {
         *ret_val = env->mideleg;
     }
@@ -2299,6 +2322,10 @@ static RISCVException rmw_mie64(CPURISCVState *env, int csrno,
 {
     uint64_t mask = wr_mask & all_ints;
 
+    if (lcofi_present(env, false)) {
+        mask |= wr_mask & MIP_LCOFIP;
+    }
+
     if (ret_val) {
         *ret_val = env->mie;
     }
@@ -2349,6 +2376,10 @@ static RISCVException rmw_mvien64(CPURISCVState *env, int csrno,
 {
     uint64_t mask = wr_mask & mvien_writable_mask;
 
+    if (lcofi_present(env, true)) {
+        mask |= wr_mask & MIP_LCOFIP;
+    }
+
     if (ret_val) {
         *ret_val = env->mvien;
     }
@@ -3784,6 +3815,10 @@ static RISCVException rmw_mip64(CPURISCVState *env, int csrno,
     uint64_t old_mip, mask = wr_mask & delegable_ints;
     uint32_t gin;
 
+    if (lcofi_present(env, false)) {
+        mask |= wr_mask & MIP_LCOFIP;
+    }
+
     /*
      * When mvien[9]=1, mip.SEIP is read-only and reflects only
      * the external interrupt signal from the interrupt controller.
@@ -3897,9 +3932,11 @@ static RISCVException rmw_mvip64(CPURISCVState *env, int csrno,
      *  alias_mask denotes the bits that come from mip nalias_mask denotes bits
      *  that come from hvip.
      */
-    uint64_t alias_mask = ((S_MODE_INTERRUPTS | LOCAL_INTERRUPTS) &
+    bool virt_int = (csrno == CSR_MVIP) || (csrno == CSR_MVIPH);
+    uint64_t lcofi_mask = lcofi_present(env, virt_int) ? MIP_LCOFIP : 0;
+    uint64_t alias_mask = ((S_MODE_INTERRUPTS | lcofi_mask | LOCAL_INTERRUPTS) &
         (env->mideleg | ~env->mvien)) | MIP_STIP;
-    uint64_t nalias_mask = (S_MODE_INTERRUPTS | LOCAL_INTERRUPTS) &
+    uint64_t nalias_mask = (S_MODE_INTERRUPTS | lcofi_mask | LOCAL_INTERRUPTS) &
         (~env->mideleg & env->mvien);
     uint64_t wr_mask_mvip;
     uint64_t wr_mask_mip;
@@ -3928,8 +3965,8 @@ static RISCVException rmw_mvip64(CPURISCVState *env, int csrno,
         alias_mask &= ~MIP_STIP;
     }
 
-    wr_mask_mip = wr_mask & alias_mask & mvip_writable_mask;
-    wr_mask_mvip = wr_mask & nalias_mask & mvip_writable_mask;
+    wr_mask_mip = wr_mask & alias_mask & (mvip_writable_mask | lcofi_mask);
+    wr_mask_mvip = wr_mask & nalias_mask & (mvip_writable_mask | lcofi_mask);
 
     /*
      * For bits set in alias_mask, mvip needs to be alias of mip, so forward
@@ -4132,9 +4169,11 @@ static RISCVException rmw_sie64(CPURISCVState *env, int csrno,
                                 uint64_t *ret_val,
                                 uint64_t new_val, uint64_t wr_mask)
 {
-    uint64_t nalias_mask = (S_MODE_INTERRUPTS | LOCAL_INTERRUPTS) &
+    uint64_t lcofi_mask = lcofi_present(env, false) ? MIP_LCOFIP : 0;
+    uint64_t nalias_mask = (S_MODE_INTERRUPTS | lcofi_mask | LOCAL_INTERRUPTS) &
         (~env->mideleg & env->mvien);
-    uint64_t alias_mask = (S_MODE_INTERRUPTS | LOCAL_INTERRUPTS) & env->mideleg;
+    uint64_t alias_mask = (S_MODE_INTERRUPTS | lcofi_mask | LOCAL_INTERRUPTS) &
+        env->mideleg;
     uint64_t sie_mask = wr_mask & nalias_mask;
     RISCVException ret;
 
@@ -4379,7 +4418,9 @@ static RISCVException rmw_sip64(CPURISCVState *env, int csrno,
                                 uint64_t new_val, uint64_t wr_mask)
 {
     RISCVException ret;
-    uint64_t mask = (env->mideleg | env->mvien) & sip_writable_mask;
+    uint64_t lcofi_mask = lcofi_present(env, false) ? MIP_LCOFIP : 0;
+    uint64_t mask = (env->mideleg | env->mvien) &
+                    (sip_writable_mask | lcofi_mask);
 
     if (env->virt_enabled) {
         if (env->hvictl & HVICTL_VTI) {
@@ -4392,7 +4433,7 @@ static RISCVException rmw_sip64(CPURISCVState *env, int csrno,
 
     if (ret_val) {
         *ret_val &= (env->mideleg | env->mvien) &
-            (S_MODE_INTERRUPTS | LOCAL_INTERRUPTS);
+            (S_MODE_INTERRUPTS | lcofi_mask | LOCAL_INTERRUPTS);
     }
 
     return ret;

base-commit: a925240509d1b4b656cc480f1cc79ba4d7c8bc08
-- 
2.43.0
Re: [PATCH v2] target/riscv: Fix LCOFI interrupt masking in sie/sip/mvip
Posted by Daniel Henrique Barboza 2 weeks, 3 days ago

On 9/7/2026 3:51 AM, Mayuresh Chitale wrote:
> Commit 27f9566dcd98 dropped bit 13 (MIP_LCOFIP) from the LOCAL_INTERRUPTS
> macro and hence also from individual masks such as all_ints,
> sip_writable_mask etc.
> 
> As a result, with Sscofpmf enabled and LCOFIP delegated to S-mode, the
> overflow interrupt was raised in mip but was not visible in sip and it
> never got delivered to the PMU driver. For the same reason, sie.LCOFIE
> also became read-only 0.
> 
> Add back bit 13 in the affected masks, but gate it on Sscofpmf being
> implemented. In addition, for the virtual interrupt path, mvip/mvien
> LCOFI also requires Smcdeleg and Smaia to be implemented. Also make
> LCOFI delegable only if Sscofpmf is implemented.
> 
> Fixes: 27f9566dcd98 ("target/riscv: Update the local interrupt mask")
> Suggested-by: Gong Shuai <gsh517025@gmail.com>
> Suggested-by: TianCheng TANG <lyndra@linux.alibaba.com>
> Signed-off-by: Mayuresh Chitale <mayuresh.chitale@oss.qualcomm.com>
> ---

Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com>

> v2:
> - Fix the sip.LCOFIP read-back mask in rmw_sip64() so that it matches
>    the write path.
> - Gate LCOFI on the Sscofpmf, Smcdeleg and Smaia extensions, depending
>    on the register being accessed and on whether it is the virtual
>    interrupt path.
> 
>   target/riscv/tcg/csr.c | 59 +++++++++++++++++++++++++++++++++++-------
>   1 file changed, 50 insertions(+), 9 deletions(-)
> 
> diff --git a/target/riscv/tcg/csr.c b/target/riscv/tcg/csr.c
> index 002f7e69c1..966a0897bb 100644
> --- a/target/riscv/tcg/csr.c
> +++ b/target/riscv/tcg/csr.c
> @@ -1812,7 +1812,7 @@ static RISCVException write_stimecmph(CPURISCVState *env, int csrno,
>   #define LOCAL_INTERRUPTS   (~0xFFFFULL)
>   
>   static const uint64_t delegable_ints =
> -    S_MODE_INTERRUPTS | VS_MODE_INTERRUPTS | MIP_LCOFIP;
> +    S_MODE_INTERRUPTS | VS_MODE_INTERRUPTS;
>   static const uint64_t vs_delegable_ints =
>       (VS_MODE_INTERRUPTS | LOCAL_INTERRUPTS) & ~MIP_LCOFIP;
>   static const uint64_t all_ints = M_MODE_INTERRUPTS | S_MODE_INTERRUPTS |
> @@ -1874,6 +1874,25 @@ static const uint64_t hvien_writable_mask = LOCAL_INTERRUPTS;
>   
>   static const uint64_t vsip_writable_mask = MIP_VSSIP | LOCAL_INTERRUPTS;
>   
> +static bool lcofi_present(CPURISCVState *env, bool virt_int)
> +{
> +    const RISCVCPUConfig *cfg = riscv_cpu_cfg(env);
> +
> +    if (!cfg->ext_sscofpmf) {
> +        return false;
> +    }
> +
> +    /*
> +     * For the virtual interrupts path, we also need to check if smcdeleg and
> +     * smaia are both implemented.
> +     */
> +    if (virt_int && !(cfg->ext_smcdeleg && cfg->ext_smaia)) {
> +        return false;
> +    }
> +
> +    return true;
> +}
> +
>   /* Machine Information Registers */
>   static RISCVException read_zero(CPURISCVState *env, int csrno,
>                                   target_ulong *val)
> @@ -2248,6 +2267,10 @@ static RISCVException rmw_mideleg64(CPURISCVState *env, int csrno,
>   {
>       uint64_t mask = wr_mask & delegable_ints;
>   
> +    if (lcofi_present(env, false)) {
> +        mask |= wr_mask & MIP_LCOFIP;
> +    }
> +
>       if (ret_val) {
>           *ret_val = env->mideleg;
>       }
> @@ -2299,6 +2322,10 @@ static RISCVException rmw_mie64(CPURISCVState *env, int csrno,
>   {
>       uint64_t mask = wr_mask & all_ints;
>   
> +    if (lcofi_present(env, false)) {
> +        mask |= wr_mask & MIP_LCOFIP;
> +    }
> +
>       if (ret_val) {
>           *ret_val = env->mie;
>       }
> @@ -2349,6 +2376,10 @@ static RISCVException rmw_mvien64(CPURISCVState *env, int csrno,
>   {
>       uint64_t mask = wr_mask & mvien_writable_mask;
>   
> +    if (lcofi_present(env, true)) {
> +        mask |= wr_mask & MIP_LCOFIP;
> +    }
> +
>       if (ret_val) {
>           *ret_val = env->mvien;
>       }
> @@ -3784,6 +3815,10 @@ static RISCVException rmw_mip64(CPURISCVState *env, int csrno,
>       uint64_t old_mip, mask = wr_mask & delegable_ints;
>       uint32_t gin;
>   
> +    if (lcofi_present(env, false)) {
> +        mask |= wr_mask & MIP_LCOFIP;
> +    }
> +
>       /*
>        * When mvien[9]=1, mip.SEIP is read-only and reflects only
>        * the external interrupt signal from the interrupt controller.
> @@ -3897,9 +3932,11 @@ static RISCVException rmw_mvip64(CPURISCVState *env, int csrno,
>        *  alias_mask denotes the bits that come from mip nalias_mask denotes bits
>        *  that come from hvip.
>        */
> -    uint64_t alias_mask = ((S_MODE_INTERRUPTS | LOCAL_INTERRUPTS) &
> +    bool virt_int = (csrno == CSR_MVIP) || (csrno == CSR_MVIPH);
> +    uint64_t lcofi_mask = lcofi_present(env, virt_int) ? MIP_LCOFIP : 0;
> +    uint64_t alias_mask = ((S_MODE_INTERRUPTS | lcofi_mask | LOCAL_INTERRUPTS) &
>           (env->mideleg | ~env->mvien)) | MIP_STIP;
> -    uint64_t nalias_mask = (S_MODE_INTERRUPTS | LOCAL_INTERRUPTS) &
> +    uint64_t nalias_mask = (S_MODE_INTERRUPTS | lcofi_mask | LOCAL_INTERRUPTS) &
>           (~env->mideleg & env->mvien);
>       uint64_t wr_mask_mvip;
>       uint64_t wr_mask_mip;
> @@ -3928,8 +3965,8 @@ static RISCVException rmw_mvip64(CPURISCVState *env, int csrno,
>           alias_mask &= ~MIP_STIP;
>       }
>   
> -    wr_mask_mip = wr_mask & alias_mask & mvip_writable_mask;
> -    wr_mask_mvip = wr_mask & nalias_mask & mvip_writable_mask;
> +    wr_mask_mip = wr_mask & alias_mask & (mvip_writable_mask | lcofi_mask);
> +    wr_mask_mvip = wr_mask & nalias_mask & (mvip_writable_mask | lcofi_mask);
>   
>       /*
>        * For bits set in alias_mask, mvip needs to be alias of mip, so forward
> @@ -4132,9 +4169,11 @@ static RISCVException rmw_sie64(CPURISCVState *env, int csrno,
>                                   uint64_t *ret_val,
>                                   uint64_t new_val, uint64_t wr_mask)
>   {
> -    uint64_t nalias_mask = (S_MODE_INTERRUPTS | LOCAL_INTERRUPTS) &
> +    uint64_t lcofi_mask = lcofi_present(env, false) ? MIP_LCOFIP : 0;
> +    uint64_t nalias_mask = (S_MODE_INTERRUPTS | lcofi_mask | LOCAL_INTERRUPTS) &
>           (~env->mideleg & env->mvien);
> -    uint64_t alias_mask = (S_MODE_INTERRUPTS | LOCAL_INTERRUPTS) & env->mideleg;
> +    uint64_t alias_mask = (S_MODE_INTERRUPTS | lcofi_mask | LOCAL_INTERRUPTS) &
> +        env->mideleg;
>       uint64_t sie_mask = wr_mask & nalias_mask;
>       RISCVException ret;
>   
> @@ -4379,7 +4418,9 @@ static RISCVException rmw_sip64(CPURISCVState *env, int csrno,
>                                   uint64_t new_val, uint64_t wr_mask)
>   {
>       RISCVException ret;
> -    uint64_t mask = (env->mideleg | env->mvien) & sip_writable_mask;
> +    uint64_t lcofi_mask = lcofi_present(env, false) ? MIP_LCOFIP : 0;
> +    uint64_t mask = (env->mideleg | env->mvien) &
> +                    (sip_writable_mask | lcofi_mask);
>   
>       if (env->virt_enabled) {
>           if (env->hvictl & HVICTL_VTI) {
> @@ -4392,7 +4433,7 @@ static RISCVException rmw_sip64(CPURISCVState *env, int csrno,
>   
>       if (ret_val) {
>           *ret_val &= (env->mideleg | env->mvien) &
> -            (S_MODE_INTERRUPTS | LOCAL_INTERRUPTS);
> +            (S_MODE_INTERRUPTS | lcofi_mask | LOCAL_INTERRUPTS);
>       }
>   
>       return ret;
> 
> base-commit: a925240509d1b4b656cc480f1cc79ba4d7c8bc08
Re: [PATCH v2] target/riscv: Fix LCOFI interrupt masking in sie/sip/mvip
Posted by Mayuresh Chitale 1 week, 2 days ago
On Wed, Sep 9, 2026 at 7:12 PM Daniel Henrique Barboza
<daniel.barboza@oss.qualcomm.com> wrote:
>
>
>
> On 9/7/2026 3:51 AM, Mayuresh Chitale wrote:
> > Commit 27f9566dcd98 dropped bit 13 (MIP_LCOFIP) from the LOCAL_INTERRUPTS
> > macro and hence also from individual masks such as all_ints,
> > sip_writable_mask etc.
> >
> > As a result, with Sscofpmf enabled and LCOFIP delegated to S-mode, the
> > overflow interrupt was raised in mip but was not visible in sip and it
> > never got delivered to the PMU driver. For the same reason, sie.LCOFIE
> > also became read-only 0.
> >
> > Add back bit 13 in the affected masks, but gate it on Sscofpmf being
> > implemented. In addition, for the virtual interrupt path, mvip/mvien
> > LCOFI also requires Smcdeleg and Smaia to be implemented. Also make
> > LCOFI delegable only if Sscofpmf is implemented.
> >
> > Fixes: 27f9566dcd98 ("target/riscv: Update the local interrupt mask")
> > Suggested-by: Gong Shuai <gsh517025@gmail.com>
> > Suggested-by: TianCheng TANG <lyndra@linux.alibaba.com>
> > Signed-off-by: Mayuresh Chitale <mayuresh.chitale@oss.qualcomm.com>
> > ---
>
> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com>
Thanks Daniel. I am working on v3 that will also fix the broken kernel
sbi pmu selftest. I will add your RB in v3.
>
> > v2:
> > - Fix the sip.LCOFIP read-back mask in rmw_sip64() so that it matches
> >    the write path.
> > - Gate LCOFI on the Sscofpmf, Smcdeleg and Smaia extensions, depending
> >    on the register being accessed and on whether it is the virtual
> >    interrupt path.
> >
> >   target/riscv/tcg/csr.c | 59 +++++++++++++++++++++++++++++++++++-------
> >   1 file changed, 50 insertions(+), 9 deletions(-)
> >
> > diff --git a/target/riscv/tcg/csr.c b/target/riscv/tcg/csr.c
> > index 002f7e69c1..966a0897bb 100644
> > --- a/target/riscv/tcg/csr.c
> > +++ b/target/riscv/tcg/csr.c
> > @@ -1812,7 +1812,7 @@ static RISCVException write_stimecmph(CPURISCVState *env, int csrno,
> >   #define LOCAL_INTERRUPTS   (~0xFFFFULL)
> >
> >   static const uint64_t delegable_ints =
> > -    S_MODE_INTERRUPTS | VS_MODE_INTERRUPTS | MIP_LCOFIP;
> > +    S_MODE_INTERRUPTS | VS_MODE_INTERRUPTS;
> >   static const uint64_t vs_delegable_ints =
> >       (VS_MODE_INTERRUPTS | LOCAL_INTERRUPTS) & ~MIP_LCOFIP;
> >   static const uint64_t all_ints = M_MODE_INTERRUPTS | S_MODE_INTERRUPTS |
> > @@ -1874,6 +1874,25 @@ static const uint64_t hvien_writable_mask = LOCAL_INTERRUPTS;
> >
> >   static const uint64_t vsip_writable_mask = MIP_VSSIP | LOCAL_INTERRUPTS;
> >
> > +static bool lcofi_present(CPURISCVState *env, bool virt_int)
> > +{
> > +    const RISCVCPUConfig *cfg = riscv_cpu_cfg(env);
> > +
> > +    if (!cfg->ext_sscofpmf) {
> > +        return false;
> > +    }
> > +
> > +    /*
> > +     * For the virtual interrupts path, we also need to check if smcdeleg and
> > +     * smaia are both implemented.
> > +     */
> > +    if (virt_int && !(cfg->ext_smcdeleg && cfg->ext_smaia)) {
> > +        return false;
> > +    }
> > +
> > +    return true;
> > +}
> > +
> >   /* Machine Information Registers */
> >   static RISCVException read_zero(CPURISCVState *env, int csrno,
> >                                   target_ulong *val)
> > @@ -2248,6 +2267,10 @@ static RISCVException rmw_mideleg64(CPURISCVState *env, int csrno,
> >   {
> >       uint64_t mask = wr_mask & delegable_ints;
> >
> > +    if (lcofi_present(env, false)) {
> > +        mask |= wr_mask & MIP_LCOFIP;
> > +    }
> > +
> >       if (ret_val) {
> >           *ret_val = env->mideleg;
> >       }
> > @@ -2299,6 +2322,10 @@ static RISCVException rmw_mie64(CPURISCVState *env, int csrno,
> >   {
> >       uint64_t mask = wr_mask & all_ints;
> >
> > +    if (lcofi_present(env, false)) {
> > +        mask |= wr_mask & MIP_LCOFIP;
> > +    }
> > +
> >       if (ret_val) {
> >           *ret_val = env->mie;
> >       }
> > @@ -2349,6 +2376,10 @@ static RISCVException rmw_mvien64(CPURISCVState *env, int csrno,
> >   {
> >       uint64_t mask = wr_mask & mvien_writable_mask;
> >
> > +    if (lcofi_present(env, true)) {
> > +        mask |= wr_mask & MIP_LCOFIP;
> > +    }
> > +
> >       if (ret_val) {
> >           *ret_val = env->mvien;
> >       }
> > @@ -3784,6 +3815,10 @@ static RISCVException rmw_mip64(CPURISCVState *env, int csrno,
> >       uint64_t old_mip, mask = wr_mask & delegable_ints;
> >       uint32_t gin;
> >
> > +    if (lcofi_present(env, false)) {
> > +        mask |= wr_mask & MIP_LCOFIP;
> > +    }
> > +
> >       /*
> >        * When mvien[9]=1, mip.SEIP is read-only and reflects only
> >        * the external interrupt signal from the interrupt controller.
> > @@ -3897,9 +3932,11 @@ static RISCVException rmw_mvip64(CPURISCVState *env, int csrno,
> >        *  alias_mask denotes the bits that come from mip nalias_mask denotes bits
> >        *  that come from hvip.
> >        */
> > -    uint64_t alias_mask = ((S_MODE_INTERRUPTS | LOCAL_INTERRUPTS) &
> > +    bool virt_int = (csrno == CSR_MVIP) || (csrno == CSR_MVIPH);
> > +    uint64_t lcofi_mask = lcofi_present(env, virt_int) ? MIP_LCOFIP : 0;
> > +    uint64_t alias_mask = ((S_MODE_INTERRUPTS | lcofi_mask | LOCAL_INTERRUPTS) &
> >           (env->mideleg | ~env->mvien)) | MIP_STIP;
> > -    uint64_t nalias_mask = (S_MODE_INTERRUPTS | LOCAL_INTERRUPTS) &
> > +    uint64_t nalias_mask = (S_MODE_INTERRUPTS | lcofi_mask | LOCAL_INTERRUPTS) &
> >           (~env->mideleg & env->mvien);
> >       uint64_t wr_mask_mvip;
> >       uint64_t wr_mask_mip;
> > @@ -3928,8 +3965,8 @@ static RISCVException rmw_mvip64(CPURISCVState *env, int csrno,
> >           alias_mask &= ~MIP_STIP;
> >       }
> >
> > -    wr_mask_mip = wr_mask & alias_mask & mvip_writable_mask;
> > -    wr_mask_mvip = wr_mask & nalias_mask & mvip_writable_mask;
> > +    wr_mask_mip = wr_mask & alias_mask & (mvip_writable_mask | lcofi_mask);
> > +    wr_mask_mvip = wr_mask & nalias_mask & (mvip_writable_mask | lcofi_mask);
> >
> >       /*
> >        * For bits set in alias_mask, mvip needs to be alias of mip, so forward
> > @@ -4132,9 +4169,11 @@ static RISCVException rmw_sie64(CPURISCVState *env, int csrno,
> >                                   uint64_t *ret_val,
> >                                   uint64_t new_val, uint64_t wr_mask)
> >   {
> > -    uint64_t nalias_mask = (S_MODE_INTERRUPTS | LOCAL_INTERRUPTS) &
> > +    uint64_t lcofi_mask = lcofi_present(env, false) ? MIP_LCOFIP : 0;
> > +    uint64_t nalias_mask = (S_MODE_INTERRUPTS | lcofi_mask | LOCAL_INTERRUPTS) &
> >           (~env->mideleg & env->mvien);
> > -    uint64_t alias_mask = (S_MODE_INTERRUPTS | LOCAL_INTERRUPTS) & env->mideleg;
> > +    uint64_t alias_mask = (S_MODE_INTERRUPTS | lcofi_mask | LOCAL_INTERRUPTS) &
> > +        env->mideleg;
> >       uint64_t sie_mask = wr_mask & nalias_mask;
> >       RISCVException ret;
> >
> > @@ -4379,7 +4418,9 @@ static RISCVException rmw_sip64(CPURISCVState *env, int csrno,
> >                                   uint64_t new_val, uint64_t wr_mask)
> >   {
> >       RISCVException ret;
> > -    uint64_t mask = (env->mideleg | env->mvien) & sip_writable_mask;
> > +    uint64_t lcofi_mask = lcofi_present(env, false) ? MIP_LCOFIP : 0;
> > +    uint64_t mask = (env->mideleg | env->mvien) &
> > +                    (sip_writable_mask | lcofi_mask);
> >
> >       if (env->virt_enabled) {
> >           if (env->hvictl & HVICTL_VTI) {
> > @@ -4392,7 +4433,7 @@ static RISCVException rmw_sip64(CPURISCVState *env, int csrno,
> >
> >       if (ret_val) {
> >           *ret_val &= (env->mideleg | env->mvien) &
> > -            (S_MODE_INTERRUPTS | LOCAL_INTERRUPTS);
> > +            (S_MODE_INTERRUPTS | lcofi_mask | LOCAL_INTERRUPTS);
> >       }
> >
> >       return ret;
> >
> > base-commit: a925240509d1b4b656cc480f1cc79ba4d7c8bc08
>