diff mbox

[v8,29/35] RISC-V: Implement existential predicates for CSRs

Message ID 1524699938-6764-30-git-send-email-mjc@sifive.com (mailing list archive)
State New, archived
Headers show

Commit Message

Michael Clark April 25, 2018, 11:45 p.m. UTC
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(-)

Comments

Alistair Francis May 3, 2018, 9:21 p.m. UTC | #1
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
diff mbox

Patch

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
 };