[Qemu-devel] [PATCH v8 29/35] RISC-V: Implement existential predicates for CSRs

Michael Clark posted 35 patches 7 years, 9 months ago
[Qemu-devel] [PATCH v8 29/35] RISC-V: Implement existential predicates for CSRs
Posted by Michael Clark 7 years, 9 months ago
CSR predicate functions are added to the CSR table.
mstatus.FS and counter enable checks are moved
to predicate functions and two new predicates are
added to check misa.S for s* CSRs and a new PMP
CPU feature for pmp* CSRs.

Processors that don't implement S-mode will trap
on access to s* CSRs and processors that don't
implement PMP will trap on accesses to pmp* CSRs.

PMP checks are disabled in riscv_cpu_handle_mmu_fault
when the PMP CPU feature is not present.

Cc: Sagar Karandikar <sagark@eecs.berkeley.edu>
Cc: Bastian Koppelmann <kbastian@mail.uni-paderborn.de>
Cc: Palmer Dabbelt <palmer@sifive.com>
Cc: Alistair Francis <Alistair.Francis@wdc.com>
Signed-off-by: Michael Clark <mjc@sifive.com>
---
 target/riscv/cpu.c        |   6 ++
 target/riscv/cpu.h        |   5 +-
 target/riscv/cpu_helper.c |   3 +-
 target/riscv/csr.c        | 172 ++++++++++++++++++++++++++--------------------
 4 files changed, 107 insertions(+), 79 deletions(-)

diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index 4e5a56d..26741d0 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -124,6 +124,7 @@ static void rv32gcsu_priv1_09_1_cpu_init(Object *obj)
     set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_09_1);
     set_resetvec(env, DEFAULT_RSTVEC);
     set_feature(env, RISCV_FEATURE_MMU);
+    set_feature(env, RISCV_FEATURE_PMP);
 }
 
 static void rv32gcsu_priv1_10_0_cpu_init(Object *obj)
@@ -133,6 +134,7 @@ static void rv32gcsu_priv1_10_0_cpu_init(Object *obj)
     set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
     set_resetvec(env, DEFAULT_RSTVEC);
     set_feature(env, RISCV_FEATURE_MMU);
+    set_feature(env, RISCV_FEATURE_PMP);
 }
 
 static void rv32imacu_nommu_cpu_init(Object *obj)
@@ -141,6 +143,7 @@ static void rv32imacu_nommu_cpu_init(Object *obj)
     set_misa(env, RV32 | RVI | RVM | RVA | RVC | RVU);
     set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
     set_resetvec(env, DEFAULT_RSTVEC);
+    set_feature(env, RISCV_FEATURE_PMP);
 }
 
 #elif defined(TARGET_RISCV64)
@@ -152,6 +155,7 @@ static void rv64gcsu_priv1_09_1_cpu_init(Object *obj)
     set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_09_1);
     set_resetvec(env, DEFAULT_RSTVEC);
     set_feature(env, RISCV_FEATURE_MMU);
+    set_feature(env, RISCV_FEATURE_PMP);
 }
 
 static void rv64gcsu_priv1_10_0_cpu_init(Object *obj)
@@ -161,6 +165,7 @@ static void rv64gcsu_priv1_10_0_cpu_init(Object *obj)
     set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
     set_resetvec(env, DEFAULT_RSTVEC);
     set_feature(env, RISCV_FEATURE_MMU);
+    set_feature(env, RISCV_FEATURE_PMP);
 }
 
 static void rv64imacu_nommu_cpu_init(Object *obj)
@@ -169,6 +174,7 @@ static void rv64imacu_nommu_cpu_init(Object *obj)
     set_misa(env, RV64 | RVI | RVM | RVA | RVC | RVU);
     set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
     set_resetvec(env, DEFAULT_RSTVEC);
+    set_feature(env, RISCV_FEATURE_PMP);
 }
 
 #endif
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index dc79f7c..3fed92d 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -83,9 +83,10 @@
 /* S extension denotes that Supervisor mode exists, however it is possible
    to have a core that support S mode but does not have an MMU and there
    is currently no bit in misa to indicate whether an MMU exists or not
-   so a cpu features bitfield is required */
+   so a cpu features bitfield is required, likewise for optional PMP support */
 enum {
-    RISCV_FEATURE_MMU
+    RISCV_FEATURE_MMU,
+    RISCV_FEATURE_PMP
 };
 
 #define USER_VERSION_2_02_0 0x00020200
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index 2937da0..5d33f7b 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -403,7 +403,8 @@ int riscv_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size,
     qemu_log_mask(CPU_LOG_MMU,
             "%s address=%" VADDR_PRIx " ret %d physical " TARGET_FMT_plx
              " prot %d\n", __func__, address, ret, pa, prot);
-    if (!pmp_hart_has_privs(env, pa, TARGET_PAGE_SIZE, 1 << rw)) {
+    if (riscv_feature(env, RISCV_FEATURE_PMP) &&
+        !pmp_hart_has_privs(env, pa, TARGET_PAGE_SIZE, 1 << rw)) {
         ret = TRANSLATE_FAIL;
     }
     if (ret == TRANSLATE_SUCCESS) {
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index c704545..ecf74a0 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -26,6 +26,7 @@
 
 /* Control and Status Register function table forward declaration */
 
+typedef int (*riscv_csr_predicate_fn)(CPURISCVState *env, int csrno);
 typedef int (*riscv_csr_read_fn)(CPURISCVState *env, int csrno,
     target_ulong *ret_value);
 typedef int (*riscv_csr_write_fn)(CPURISCVState *env, int csrno,
@@ -34,6 +35,7 @@ typedef int (*riscv_csr_op_fn)(CPURISCVState *env, int csrno,
     target_ulong *ret_value, target_ulong new_value, target_ulong write_mask);
 
 typedef struct {
+    riscv_csr_predicate_fn predicate;
     riscv_csr_read_fn read;
     riscv_csr_write_fn write;
     riscv_csr_op_fn op;
@@ -42,6 +44,47 @@ typedef struct {
 static const riscv_csr_operations csr_ops[];
 
 
+/* Predicates */
+
+static int fs(CPURISCVState *env, int csrno)
+{
+#if !defined(CONFIG_USER_ONLY)
+    if (!(env->mstatus & MSTATUS_FS)) {
+        return -1;
+    }
+#endif
+    return 0;
+}
+
+static int ctr(CPURISCVState *env, int csrno)
+{
+#if !defined(CONFIG_USER_ONLY)
+    target_ulong ctr_en = env->priv == PRV_U ? env->scounteren :
+                          env->priv == PRV_S ? env->mcounteren : -1U;
+    if (!(ctr_en & (1 << (csrno & 31)))) {
+        return -1;
+    }
+#endif
+    return 0;
+}
+
+#if !defined(CONFIG_USER_ONLY)
+static int any(CPURISCVState *env, int csrno)
+{
+    return 0;
+}
+
+static int smode(CPURISCVState *env, int csrno)
+{
+    return -!riscv_has_ext(env, RVS);
+}
+
+static int pmp(CPURISCVState *env, int csrno)
+{
+    return -!riscv_feature(env, RISCV_FEATURE_PMP);
+}
+#endif
+
 /* User Floating-Point CSRs */
 
 static int read_fflags(CPURISCVState *env, int csrno, target_ulong *val)
@@ -117,33 +160,8 @@ static int write_fcsr(CPURISCVState *env, int csrno, target_ulong val)
 
 /* User Timers and Counters */
 
-static int counter_enabled(CPURISCVState *env, int csrno)
-{
-#ifndef CONFIG_USER_ONLY
-    target_ulong ctr_en = env->priv == PRV_U ? env->scounteren :
-                          env->priv == PRV_S ? env->mcounteren : -1U;
-#else
-    target_ulong ctr_en = -1;
-#endif
-    return (ctr_en >> (csrno & 31)) & 1;
-}
-
-#if !defined(CONFIG_USER_ONLY)
-static int read_zero_counter(CPURISCVState *env, int csrno, target_ulong *val)
-{
-    if (!counter_enabled(env, csrno)) {
-        return -1;
-    }
-    *val = 0;
-    return 0;
-}
-#endif
-
 static int read_instret(CPURISCVState *env, int csrno, target_ulong *val)
 {
-    if (!counter_enabled(env, csrno)) {
-        return -1;
-    }
 #if !defined(CONFIG_USER_ONLY)
     if (use_icount) {
         *val = cpu_get_icount();
@@ -159,9 +177,6 @@ static int read_instret(CPURISCVState *env, int csrno, target_ulong *val)
 #if defined(TARGET_RISCV32)
 static int read_instreth(CPURISCVState *env, int csrno, target_ulong *val)
 {
-    if (!counter_enabled(env, csrno)) {
-        return -1;
-    }
 #if !defined(CONFIG_USER_ONLY)
     if (use_icount) {
         *val = cpu_get_icount() >> 32;
@@ -726,6 +741,11 @@ int riscv_csrrw(CPURISCVState *env, int csrno, target_ulong *ret_value,
     }
 #endif
 
+    /* check predicate */
+    if (!csr_ops[csrno].predicate || csr_ops[csrno].predicate(env, csrno) < 0) {
+        return -1;
+    }
+
     /* execute combined read/write operation if it exists */
     if (csr_ops[csrno].op) {
         return csr_ops[csrno].op(env, csrno, ret_value, new_value, write_mask);
@@ -765,89 +785,89 @@ int riscv_csrrw(CPURISCVState *env, int csrno, target_ulong *ret_value,
 
 static const riscv_csr_operations csr_ops[0xfff] = {
     /* User Floating-Point CSRs */
-    [CSR_FFLAGS] =              { read_fflags,      write_fflags      },
-    [CSR_FRM] =                 { read_frm,         write_frm         },
-    [CSR_FCSR] =                { read_fcsr,        write_fcsr        },
+    [CSR_FFLAGS] =              { fs,   read_fflags,      write_fflags      },
+    [CSR_FRM] =                 { fs,   read_frm,         write_frm         },
+    [CSR_FCSR] =                { fs,   read_fcsr,        write_fcsr        },
 
     /* User Timers and Counters */
-    [CSR_CYCLE] =               { read_instret                        },
-    [CSR_INSTRET] =             { read_instret                        },
+    [CSR_CYCLE] =               { ctr,  read_instret                        },
+    [CSR_INSTRET] =             { ctr,  read_instret                        },
 #if defined(TARGET_RISCV32)
-    [CSR_CYCLEH] =              { read_instreth                       },
-    [CSR_INSTRETH] =            { read_instreth                       },
+    [CSR_CYCLEH] =              { ctr,  read_instreth                       },
+    [CSR_INSTRETH] =            { ctr,  read_instreth                       },
 #endif
 
     /* User-level time CSRs are only available in linux-user
      * In privileged mode, the monitor emulates these CSRs */
 #if defined(CONFIG_USER_ONLY)
-    [CSR_TIME] =                { read_time                           },
+    [CSR_TIME] =                { ctr,  read_time                           },
 #if defined(TARGET_RISCV32)
-    [CSR_TIMEH] =               { read_timeh                          },
+    [CSR_TIMEH] =               { ctr,  read_timeh                          },
 #endif
 #endif
 
 #if !defined(CONFIG_USER_ONLY)
     /* Machine Timers and Counters */
-    [CSR_MCYCLE] =              { read_instret                        },
-    [CSR_MINSTRET] =            { read_instret                        },
+    [CSR_MCYCLE] =              { any,  read_instret                        },
+    [CSR_MINSTRET] =            { any,  read_instret                        },
 #if defined(TARGET_RISCV32)
-    [CSR_MCYCLEH] =             { read_instreth                       },
-    [CSR_MINSTRETH] =           { read_instreth                       },
+    [CSR_MCYCLEH] =             { any,  read_instreth                       },
+    [CSR_MINSTRETH] =           { any,  read_instreth                       },
 #endif
 
     /* Machine Information Registers */
-    [CSR_MVENDORID] =           { read_zero                           },
-    [CSR_MARCHID] =             { read_zero                           },
-    [CSR_MIMPID] =              { read_zero                           },
-    [CSR_MHARTID] =             { read_mhartid                        },
+    [CSR_MVENDORID] =           { any,  read_zero                           },
+    [CSR_MARCHID] =             { any,  read_zero                           },
+    [CSR_MIMPID] =              { any,  read_zero                           },
+    [CSR_MHARTID] =             { any,  read_mhartid                        },
 
     /* Machine Trap Setup */
-    [CSR_MSTATUS] =             { read_mstatus,     write_mstatus     },
-    [CSR_MISA] =                { read_misa                           },
-    [CSR_MIDELEG] =             { read_mideleg,     write_mideleg     },
-    [CSR_MEDELEG] =             { read_medeleg,     write_medeleg     },
-    [CSR_MIE] =                 { read_mie,         write_mie         },
-    [CSR_MTVEC] =               { read_mtvec,       write_mtvec       },
-    [CSR_MCOUNTEREN] =          { read_mcounteren,  write_mcounteren  },
+    [CSR_MSTATUS] =             { any,  read_mstatus,     write_mstatus     },
+    [CSR_MISA] =                { any,  read_misa                           },
+    [CSR_MIDELEG] =             { any,  read_mideleg,     write_mideleg     },
+    [CSR_MEDELEG] =             { any,  read_medeleg,     write_medeleg     },
+    [CSR_MIE] =                 { any,  read_mie,         write_mie         },
+    [CSR_MTVEC] =               { any,  read_mtvec,       write_mtvec       },
+    [CSR_MCOUNTEREN] =          { any,  read_mcounteren,  write_mcounteren  },
 
     /* Legacy Counter Setup (priv v1.9.1) */
-    [CSR_MUCOUNTEREN] =         { read_mucounteren, write_mucounteren },
-    [CSR_MSCOUNTEREN] =         { read_mscounteren, write_mscounteren },
+    [CSR_MUCOUNTEREN] =         { any,  read_mucounteren, write_mucounteren },
+    [CSR_MSCOUNTEREN] =         { any,  read_mscounteren, write_mscounteren },
 
     /* Machine Trap Handling */
-    [CSR_MSCRATCH] =            { read_mscratch,    write_mscratch    },
-    [CSR_MEPC] =                { read_mepc,        write_mepc        },
-    [CSR_MCAUSE] =              { read_mcause,      write_mcause      },
-    [CSR_MBADADDR] =            { read_mbadaddr,    write_mbadaddr    },
-    [CSR_MIP] =                 { NULL,     NULL,     rmw_mip         },
+    [CSR_MSCRATCH] =            { any,  read_mscratch,    write_mscratch    },
+    [CSR_MEPC] =                { any,  read_mepc,        write_mepc        },
+    [CSR_MCAUSE] =              { any,  read_mcause,      write_mcause      },
+    [CSR_MBADADDR] =            { any,  read_mbadaddr,    write_mbadaddr    },
+    [CSR_MIP] =                 { any,  NULL,     NULL,     rmw_mip         },
 
     /* Supervisor Trap Setup */
-    [CSR_SSTATUS] =             { read_sstatus,     write_sstatus     },
-    [CSR_SIE] =                 { read_sie,         write_sie         },
-    [CSR_STVEC] =               { read_stvec,       write_stvec       },
-    [CSR_SCOUNTEREN] =          { read_scounteren,  write_scounteren  },
+    [CSR_SSTATUS] =             { smode, read_sstatus,     write_sstatus     },
+    [CSR_SIE] =                 { smode, read_sie,         write_sie         },
+    [CSR_STVEC] =               { smode, read_stvec,       write_stvec       },
+    [CSR_SCOUNTEREN] =          { smode, read_scounteren,  write_scounteren  },
 
     /* Supervisor Trap Handling */
-    [CSR_SSCRATCH] =            { read_sscratch,    write_sscratch    },
-    [CSR_SEPC] =                { read_sepc,        write_sepc        },
-    [CSR_SCAUSE] =              { read_scause,      write_scause      },
-    [CSR_SBADADDR] =            { read_sbadaddr,    write_sbadaddr    },
-    [CSR_SIP] =                 { NULL,     NULL,     rmw_sip         },
+    [CSR_SSCRATCH] =            { smode, read_sscratch,    write_sscratch    },
+    [CSR_SEPC] =                { smode, read_sepc,        write_sepc        },
+    [CSR_SCAUSE] =              { smode, read_scause,      write_scause      },
+    [CSR_SBADADDR] =            { smode, read_sbadaddr,    write_sbadaddr    },
+    [CSR_SIP] =                 { smode, NULL,     NULL,     rmw_sip         },
 
     /* Supervisor Protection and Translation */
-    [CSR_SATP] =                { read_satp,        write_satp        },
+    [CSR_SATP] =                { smode, read_satp,        write_satp        },
 
     /* Physical Memory Protection */
-    [CSR_PMPCFG0  ... CSR_PMPADDR9] =  { read_pmpcfg,  write_pmpcfg   },
-    [CSR_PMPADDR0 ... CSR_PMPADDR15] = { read_pmpaddr, write_pmpaddr  },
+    [CSR_PMPCFG0  ... CSR_PMPADDR9] =  { pmp,   read_pmpcfg,  write_pmpcfg   },
+    [CSR_PMPADDR0 ... CSR_PMPADDR15] = { pmp,   read_pmpaddr, write_pmpaddr  },
 
     /* Performance Counters */
-    [CSR_HPMCOUNTER3   ... CSR_HPMCOUNTER31] =    { read_zero_counter },
-    [CSR_MHPMCOUNTER3  ... CSR_MHPMCOUNTER31] =   { read_zero         },
-    [CSR_MHPMEVENT3    ... CSR_MHPMEVENT31] =     { read_zero         },
+    [CSR_HPMCOUNTER3   ... CSR_HPMCOUNTER31] =    { ctr,  read_zero          },
+    [CSR_MHPMCOUNTER3  ... CSR_MHPMCOUNTER31] =   { any,  read_zero          },
+    [CSR_MHPMEVENT3    ... CSR_MHPMEVENT31] =     { any,  read_zero          },
 #if defined(TARGET_RISCV32)
-    [CSR_HPMCOUNTER3H  ... CSR_HPMCOUNTER31H] =   { read_zero_counter },
-    [CSR_MHPMCOUNTER3H ... CSR_MHPMCOUNTER31H] =  { read_zero         },
+    [CSR_HPMCOUNTER3H  ... CSR_HPMCOUNTER31H] =   { ctr,  read_zero          },
+    [CSR_MHPMCOUNTER3H ... CSR_MHPMCOUNTER31H] =  { any,  read_zero          },
 #endif
 #endif
 };
-- 
2.7.0


Re: [Qemu-devel] [PATCH v8 29/35] RISC-V: Implement existential predicates for CSRs
Posted by Alistair Francis 7 years, 9 months ago
On Wed, Apr 25, 2018 at 5:05 PM Michael Clark <mjc@sifive.com> wrote:

> CSR predicate functions are added to the CSR table.
> mstatus.FS and counter enable checks are moved
> to predicate functions and two new predicates are
> added to check misa.S for s* CSRs and a new PMP
> CPU feature for pmp* CSRs.

> Processors that don't implement S-mode will trap
> on access to s* CSRs and processors that don't
> implement PMP will trap on accesses to pmp* CSRs.

> PMP checks are disabled in riscv_cpu_handle_mmu_fault
> when the PMP CPU feature is not present.

> Cc: Sagar Karandikar <sagark@eecs.berkeley.edu>
> Cc: Bastian Koppelmann <kbastian@mail.uni-paderborn.de>
> Cc: Palmer Dabbelt <palmer@sifive.com>
> Cc: Alistair Francis <Alistair.Francis@wdc.com>
> Signed-off-by: Michael Clark <mjc@sifive.com>
> ---
>   target/riscv/cpu.c        |   6 ++
>   target/riscv/cpu.h        |   5 +-
>   target/riscv/cpu_helper.c |   3 +-
>   target/riscv/csr.c        | 172
++++++++++++++++++++++++++--------------------
>   4 files changed, 107 insertions(+), 79 deletions(-)

> diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
> index 4e5a56d..26741d0 100644
> --- a/target/riscv/cpu.c
> +++ b/target/riscv/cpu.c
> @@ -124,6 +124,7 @@ static void rv32gcsu_priv1_09_1_cpu_init(Object *obj)
>       set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_09_1);
>       set_resetvec(env, DEFAULT_RSTVEC);
>       set_feature(env, RISCV_FEATURE_MMU);
> +    set_feature(env, RISCV_FEATURE_PMP);
>   }

>   static void rv32gcsu_priv1_10_0_cpu_init(Object *obj)
> @@ -133,6 +134,7 @@ static void rv32gcsu_priv1_10_0_cpu_init(Object *obj)
>       set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
>       set_resetvec(env, DEFAULT_RSTVEC);
>       set_feature(env, RISCV_FEATURE_MMU);
> +    set_feature(env, RISCV_FEATURE_PMP);
>   }

>   static void rv32imacu_nommu_cpu_init(Object *obj)
> @@ -141,6 +143,7 @@ static void rv32imacu_nommu_cpu_init(Object *obj)
>       set_misa(env, RV32 | RVI | RVM | RVA | RVC | RVU);
>       set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
>       set_resetvec(env, DEFAULT_RSTVEC);
> +    set_feature(env, RISCV_FEATURE_PMP);
>   }

>   #elif defined(TARGET_RISCV64)
> @@ -152,6 +155,7 @@ static void rv64gcsu_priv1_09_1_cpu_init(Object *obj)
>       set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_09_1);
>       set_resetvec(env, DEFAULT_RSTVEC);
>       set_feature(env, RISCV_FEATURE_MMU);
> +    set_feature(env, RISCV_FEATURE_PMP);
>   }

>   static void rv64gcsu_priv1_10_0_cpu_init(Object *obj)
> @@ -161,6 +165,7 @@ static void rv64gcsu_priv1_10_0_cpu_init(Object *obj)
>       set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
>       set_resetvec(env, DEFAULT_RSTVEC);
>       set_feature(env, RISCV_FEATURE_MMU);
> +    set_feature(env, RISCV_FEATURE_PMP);
>   }

>   static void rv64imacu_nommu_cpu_init(Object *obj)
> @@ -169,6 +174,7 @@ static void rv64imacu_nommu_cpu_init(Object *obj)
>       set_misa(env, RV64 | RVI | RVM | RVA | RVC | RVU);
>       set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
>       set_resetvec(env, DEFAULT_RSTVEC);
> +    set_feature(env, RISCV_FEATURE_PMP);
>   }

>   #endif
> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> index dc79f7c..3fed92d 100644
> --- a/target/riscv/cpu.h
> +++ b/target/riscv/cpu.h
> @@ -83,9 +83,10 @@
>   /* S extension denotes that Supervisor mode exists, however it is
possible
>      to have a core that support S mode but does not have an MMU and there
>      is currently no bit in misa to indicate whether an MMU exists or not
> -   so a cpu features bitfield is required */
> +   so a cpu features bitfield is required, likewise for optional PMP
support */
>   enum {
> -    RISCV_FEATURE_MMU
> +    RISCV_FEATURE_MMU,
> +    RISCV_FEATURE_PMP
>   };

>   #define USER_VERSION_2_02_0 0x00020200
> diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
> index 2937da0..5d33f7b 100644
> --- a/target/riscv/cpu_helper.c
> +++ b/target/riscv/cpu_helper.c
> @@ -403,7 +403,8 @@ int riscv_cpu_handle_mmu_fault(CPUState *cs, vaddr
address, int size,
>       qemu_log_mask(CPU_LOG_MMU,
>               "%s address=%" VADDR_PRIx " ret %d physical " TARGET_FMT_plx
>                " prot %d\n", __func__, address, ret, pa, prot);
> -    if (!pmp_hart_has_privs(env, pa, TARGET_PAGE_SIZE, 1 << rw)) {
> +    if (riscv_feature(env, RISCV_FEATURE_PMP) &&
> +        !pmp_hart_has_privs(env, pa, TARGET_PAGE_SIZE, 1 << rw)) {
>           ret = TRANSLATE_FAIL;
>       }
>       if (ret == TRANSLATE_SUCCESS) {
> diff --git a/target/riscv/csr.c b/target/riscv/csr.c
> index c704545..ecf74a0 100644
> --- a/target/riscv/csr.c
> +++ b/target/riscv/csr.c
> @@ -26,6 +26,7 @@

>   /* Control and Status Register function table forward declaration */

> +typedef int (*riscv_csr_predicate_fn)(CPURISCVState *env, int csrno);
>   typedef int (*riscv_csr_read_fn)(CPURISCVState *env, int csrno,
>       target_ulong *ret_value);
>   typedef int (*riscv_csr_write_fn)(CPURISCVState *env, int csrno,
> @@ -34,6 +35,7 @@ typedef int (*riscv_csr_op_fn)(CPURISCVState *env, int
csrno,
>       target_ulong *ret_value, target_ulong new_value, target_ulong
write_mask);

>   typedef struct {
> +    riscv_csr_predicate_fn predicate;
>       riscv_csr_read_fn read;
>       riscv_csr_write_fn write;
>       riscv_csr_op_fn op;
> @@ -42,6 +44,47 @@ typedef struct {
>   static const riscv_csr_operations csr_ops[];


> +/* Predicates */
> +
> +static int fs(CPURISCVState *env, int csrno)
> +{
> +#if !defined(CONFIG_USER_ONLY)

I find it's easier to read if defined instead of if not defined. In general
it would be nicer if these could be if defined SOFTMMU.

Acked-by: Alistair Francis <alistair.francis@wdc.com>

Alistair


> +    if (!(env->mstatus & MSTATUS_FS)) {
> +        return -1;
> +    }
> +#endif
> +    return 0;
> +}
> +
> +static int ctr(CPURISCVState *env, int csrno)
> +{
> +#if !defined(CONFIG_USER_ONLY)
> +    target_ulong ctr_en = env->priv == PRV_U ? env->scounteren :
> +                          env->priv == PRV_S ? env->mcounteren : -1U;
> +    if (!(ctr_en & (1 << (csrno & 31)))) {
> +        return -1;
> +    }
> +#endif
> +    return 0;
> +}
> +
> +#if !defined(CONFIG_USER_ONLY)
> +static int any(CPURISCVState *env, int csrno)
> +{
> +    return 0;
> +}
> +
> +static int smode(CPURISCVState *env, int csrno)
> +{
> +    return -!riscv_has_ext(env, RVS);
> +}
> +
> +static int pmp(CPURISCVState *env, int csrno)
> +{
> +    return -!riscv_feature(env, RISCV_FEATURE_PMP);
> +}
> +#endif
> +
>   /* User Floating-Point CSRs */

>   static int read_fflags(CPURISCVState *env, int csrno, target_ulong *val)
> @@ -117,33 +160,8 @@ static int write_fcsr(CPURISCVState *env, int csrno,
target_ulong val)

>   /* User Timers and Counters */

> -static int counter_enabled(CPURISCVState *env, int csrno)
> -{
> -#ifndef CONFIG_USER_ONLY
> -    target_ulong ctr_en = env->priv == PRV_U ? env->scounteren :
> -                          env->priv == PRV_S ? env->mcounteren : -1U;
> -#else
> -    target_ulong ctr_en = -1;
> -#endif
> -    return (ctr_en >> (csrno & 31)) & 1;
> -}
> -
> -#if !defined(CONFIG_USER_ONLY)
> -static int read_zero_counter(CPURISCVState *env, int csrno, target_ulong
*val)
> -{
> -    if (!counter_enabled(env, csrno)) {
> -        return -1;
> -    }
> -    *val = 0;
> -    return 0;
> -}
> -#endif
> -
>   static int read_instret(CPURISCVState *env, int csrno, target_ulong *val)
>   {
> -    if (!counter_enabled(env, csrno)) {
> -        return -1;
> -    }
>   #if !defined(CONFIG_USER_ONLY)
>       if (use_icount) {
>           *val = cpu_get_icount();
> @@ -159,9 +177,6 @@ static int read_instret(CPURISCVState *env, int
csrno, target_ulong *val)
>   #if defined(TARGET_RISCV32)
>   static int read_instreth(CPURISCVState *env, int csrno, target_ulong
*val)
>   {
> -    if (!counter_enabled(env, csrno)) {
> -        return -1;
> -    }
>   #if !defined(CONFIG_USER_ONLY)
>       if (use_icount) {
>           *val = cpu_get_icount() >> 32;
> @@ -726,6 +741,11 @@ int riscv_csrrw(CPURISCVState *env, int csrno,
target_ulong *ret_value,
>       }
>   #endif

> +    /* check predicate */
> +    if (!csr_ops[csrno].predicate || csr_ops[csrno].predicate(env,
csrno) < 0) {
> +        return -1;
> +    }
> +
>       /* execute combined read/write operation if it exists */
>       if (csr_ops[csrno].op) {
>           return csr_ops[csrno].op(env, csrno, ret_value, new_value,
write_mask);
> @@ -765,89 +785,89 @@ int riscv_csrrw(CPURISCVState *env, int csrno,
target_ulong *ret_value,

>   static const riscv_csr_operations csr_ops[0xfff] = {
>       /* User Floating-Point CSRs */
> -    [CSR_FFLAGS] =              { read_fflags,      write_fflags      },
> -    [CSR_FRM] =                 { read_frm,         write_frm         },
> -    [CSR_FCSR] =                { read_fcsr,        write_fcsr        },
> +    [CSR_FFLAGS] =              { fs,   read_fflags,      write_fflags
    },
> +    [CSR_FRM] =                 { fs,   read_frm,         write_frm
     },
> +    [CSR_FCSR] =                { fs,   read_fcsr,        write_fcsr
    },

>       /* User Timers and Counters */
> -    [CSR_CYCLE] =               { read_instret                        },
> -    [CSR_INSTRET] =             { read_instret                        },
> +    [CSR_CYCLE] =               { ctr,  read_instret
    },
> +    [CSR_INSTRET] =             { ctr,  read_instret
    },
>   #if defined(TARGET_RISCV32)
> -    [CSR_CYCLEH] =              { read_instreth                       },
> -    [CSR_INSTRETH] =            { read_instreth                       },
> +    [CSR_CYCLEH] =              { ctr,  read_instreth
     },
> +    [CSR_INSTRETH] =            { ctr,  read_instreth
     },
>   #endif

>       /* User-level time CSRs are only available in linux-user
>        * In privileged mode, the monitor emulates these CSRs */
>   #if defined(CONFIG_USER_ONLY)
> -    [CSR_TIME] =                { read_time                           },
> +    [CSR_TIME] =                { ctr,  read_time
     },
>   #if defined(TARGET_RISCV32)
> -    [CSR_TIMEH] =               { read_timeh                          },
> +    [CSR_TIMEH] =               { ctr,  read_timeh
    },
>   #endif
>   #endif

>   #if !defined(CONFIG_USER_ONLY)
>       /* Machine Timers and Counters */
> -    [CSR_MCYCLE] =              { read_instret                        },
> -    [CSR_MINSTRET] =            { read_instret                        },
> +    [CSR_MCYCLE] =              { any,  read_instret
    },
> +    [CSR_MINSTRET] =            { any,  read_instret
    },
>   #if defined(TARGET_RISCV32)
> -    [CSR_MCYCLEH] =             { read_instreth                       },
> -    [CSR_MINSTRETH] =           { read_instreth                       },
> +    [CSR_MCYCLEH] =             { any,  read_instreth
     },
> +    [CSR_MINSTRETH] =           { any,  read_instreth
     },
>   #endif

>       /* Machine Information Registers */
> -    [CSR_MVENDORID] =           { read_zero                           },
> -    [CSR_MARCHID] =             { read_zero                           },
> -    [CSR_MIMPID] =              { read_zero                           },
> -    [CSR_MHARTID] =             { read_mhartid                        },
> +    [CSR_MVENDORID] =           { any,  read_zero
     },
> +    [CSR_MARCHID] =             { any,  read_zero
     },
> +    [CSR_MIMPID] =              { any,  read_zero
     },
> +    [CSR_MHARTID] =             { any,  read_mhartid
    },

>       /* Machine Trap Setup */
> -    [CSR_MSTATUS] =             { read_mstatus,     write_mstatus     },
> -    [CSR_MISA] =                { read_misa                           },
> -    [CSR_MIDELEG] =             { read_mideleg,     write_mideleg     },
> -    [CSR_MEDELEG] =             { read_medeleg,     write_medeleg     },
> -    [CSR_MIE] =                 { read_mie,         write_mie         },
> -    [CSR_MTVEC] =               { read_mtvec,       write_mtvec       },
> -    [CSR_MCOUNTEREN] =          { read_mcounteren,  write_mcounteren  },
> +    [CSR_MSTATUS] =             { any,  read_mstatus,     write_mstatus
     },
> +    [CSR_MISA] =                { any,  read_misa
     },
> +    [CSR_MIDELEG] =             { any,  read_mideleg,     write_mideleg
     },
> +    [CSR_MEDELEG] =             { any,  read_medeleg,     write_medeleg
     },
> +    [CSR_MIE] =                 { any,  read_mie,         write_mie
     },
> +    [CSR_MTVEC] =               { any,  read_mtvec,       write_mtvec
     },
> +    [CSR_MCOUNTEREN] =          { any,  read_mcounteren,
  write_mcounteren  },

>       /* Legacy Counter Setup (priv v1.9.1) */
> -    [CSR_MUCOUNTEREN] =         { read_mucounteren, write_mucounteren },
> -    [CSR_MSCOUNTEREN] =         { read_mscounteren, write_mscounteren },
> +    [CSR_MUCOUNTEREN] =         { any,  read_mucounteren,
write_mucounteren },
> +    [CSR_MSCOUNTEREN] =         { any,  read_mscounteren,
write_mscounteren },

>       /* Machine Trap Handling */
> -    [CSR_MSCRATCH] =            { read_mscratch,    write_mscratch    },
> -    [CSR_MEPC] =                { read_mepc,        write_mepc        },
> -    [CSR_MCAUSE] =              { read_mcause,      write_mcause      },
> -    [CSR_MBADADDR] =            { read_mbadaddr,    write_mbadaddr    },
> -    [CSR_MIP] =                 { NULL,     NULL,     rmw_mip         },
> +    [CSR_MSCRATCH] =            { any,  read_mscratch,    write_mscratch
    },
> +    [CSR_MEPC] =                { any,  read_mepc,        write_mepc
    },
> +    [CSR_MCAUSE] =              { any,  read_mcause,      write_mcause
    },
> +    [CSR_MBADADDR] =            { any,  read_mbadaddr,    write_mbadaddr
    },
> +    [CSR_MIP] =                 { any,  NULL,     NULL,     rmw_mip
     },

>       /* Supervisor Trap Setup */
> -    [CSR_SSTATUS] =             { read_sstatus,     write_sstatus     },
> -    [CSR_SIE] =                 { read_sie,         write_sie         },
> -    [CSR_STVEC] =               { read_stvec,       write_stvec       },
> -    [CSR_SCOUNTEREN] =          { read_scounteren,  write_scounteren  },
> +    [CSR_SSTATUS] =             { smode, read_sstatus,     write_sstatus
     },
> +    [CSR_SIE] =                 { smode, read_sie,         write_sie
     },
> +    [CSR_STVEC] =               { smode, read_stvec,       write_stvec
     },
> +    [CSR_SCOUNTEREN] =          { smode, read_scounteren,
  write_scounteren  },

>       /* Supervisor Trap Handling */
> -    [CSR_SSCRATCH] =            { read_sscratch,    write_sscratch    },
> -    [CSR_SEPC] =                { read_sepc,        write_sepc        },
> -    [CSR_SCAUSE] =              { read_scause,      write_scause      },
> -    [CSR_SBADADDR] =            { read_sbadaddr,    write_sbadaddr    },
> -    [CSR_SIP] =                 { NULL,     NULL,     rmw_sip         },
> +    [CSR_SSCRATCH] =            { smode, read_sscratch,
  write_sscratch    },
> +    [CSR_SEPC] =                { smode, read_sepc,        write_sepc
      },
> +    [CSR_SCAUSE] =              { smode, read_scause,      write_scause
      },
> +    [CSR_SBADADDR] =            { smode, read_sbadaddr,
  write_sbadaddr    },
> +    [CSR_SIP] =                 { smode, NULL,     NULL,     rmw_sip
     },

>       /* Supervisor Protection and Translation */
> -    [CSR_SATP] =                { read_satp,        write_satp        },
> +    [CSR_SATP] =                { smode, read_satp,        write_satp
      },

>       /* Physical Memory Protection */
> -    [CSR_PMPCFG0  ... CSR_PMPADDR9] =  { read_pmpcfg,  write_pmpcfg   },
> -    [CSR_PMPADDR0 ... CSR_PMPADDR15] = { read_pmpaddr, write_pmpaddr  },
> +    [CSR_PMPCFG0  ... CSR_PMPADDR9] =  { pmp,   read_pmpcfg,
  write_pmpcfg   },
> +    [CSR_PMPADDR0 ... CSR_PMPADDR15] = { pmp,   read_pmpaddr,
write_pmpaddr  },

>       /* Performance Counters */
> -    [CSR_HPMCOUNTER3   ... CSR_HPMCOUNTER31] =    { read_zero_counter },
> -    [CSR_MHPMCOUNTER3  ... CSR_MHPMCOUNTER31] =   { read_zero         },
> -    [CSR_MHPMEVENT3    ... CSR_MHPMEVENT31] =     { read_zero         },
> +    [CSR_HPMCOUNTER3   ... CSR_HPMCOUNTER31] =    { ctr,  read_zero
      },
> +    [CSR_MHPMCOUNTER3  ... CSR_MHPMCOUNTER31] =   { any,  read_zero
      },
> +    [CSR_MHPMEVENT3    ... CSR_MHPMEVENT31] =     { any,  read_zero
      },
>   #if defined(TARGET_RISCV32)
> -    [CSR_HPMCOUNTER3H  ... CSR_HPMCOUNTER31H] =   { read_zero_counter },
> -    [CSR_MHPMCOUNTER3H ... CSR_MHPMCOUNTER31H] =  { read_zero         },
> +    [CSR_HPMCOUNTER3H  ... CSR_HPMCOUNTER31H] =   { ctr,  read_zero
      },
> +    [CSR_MHPMCOUNTER3H ... CSR_MHPMCOUNTER31H] =  { any,  read_zero
      },
>   #endif
>   #endif
>   };
> --
> 2.7.0