diff mbox series

[v2,2/4] Implementation of enhanced PMP(ePMP) support

Message ID SG2PR02MB263462CCDBCBBAD36983C2CD93450@SG2PR02MB2634.apcprd02.prod.outlook.com
State Superseded
Headers show
Series None | expand

Commit Message

Hou Weiying Aug. 11, 2020, 12:23 a.m. UTC
The ePMP can be found in:
https://docs.google.com/document/d/1Mh_aiHYxemL0umN3GTTw8vsbmzHZ_nxZXgjgOUzbvc8/edit#heading=h.9wsr1lnxtwe2

Signed-off-by: Hongzheng-Li <Ethan.Lee.QNL@gmail.com>
Signed-off-by: Hou Weiying <weiying_hou@outlook.com>
Signed-off-by: Myriad-Dreamin <camiyoru@gmail.com>
---
 target/riscv/pmp.c        | 134 ++++++++++++++++++++++++++++++++++----
 target/riscv/pmp.h        |  12 ++++
 target/riscv/trace-events |   4 ++
 3 files changed, 138 insertions(+), 12 deletions(-)

Comments

Alistair Francis Feb. 9, 2021, 6:25 p.m. UTC | #1
On Mon, Aug 10, 2020 at 5:24 PM Hou Weiying <weiying_hou@outlook.com> wrote:
>

> The ePMP can be found in:

> https://docs.google.com/document/d/1Mh_aiHYxemL0umN3GTTw8vsbmzHZ_nxZXgjgOUzbvc8/edit#heading=h.9wsr1lnxtwe2

>

> Signed-off-by: Hongzheng-Li <Ethan.Lee.QNL@gmail.com>

> Signed-off-by: Hou Weiying <weiying_hou@outlook.com>

> Signed-off-by: Myriad-Dreamin <camiyoru@gmail.com>


Thanks for the patch!

I haven't forgotten about this. I have been hoping that the CSR
addresses would be finalised so then this can be merged. Unfortunately
that still hasn't happened!

> ---

>  target/riscv/pmp.c        | 134 ++++++++++++++++++++++++++++++++++----

>  target/riscv/pmp.h        |  12 ++++

>  target/riscv/trace-events |   4 ++

>  3 files changed, 138 insertions(+), 12 deletions(-)

>

> diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c

> index 2a2b9f5363..b1fa703aff 100644

> --- a/target/riscv/pmp.c

> +++ b/target/riscv/pmp.c

> @@ -34,6 +34,26 @@ static void pmp_write_cfg(CPURISCVState *env, uint32_t addr_index,

>  static uint8_t pmp_read_cfg(CPURISCVState *env, uint32_t addr_index);

>  static void pmp_update_rule(CPURISCVState *env, uint32_t pmp_index);

>

> +static char mode_to_char(int mode)

> +{

> +    char ret = 0;

> +    switch (mode) {

> +    case PRV_U:

> +        ret = 'u';

> +        break;

> +    case PRV_S:

> +        ret = 's';

> +        break;

> +    case PRV_H:

> +        ret = 'h';

> +        break;

> +    case PRV_M:

> +        ret = 'm';

> +        break;

> +    }

> +    return ret;

> +}

> +

>  /*

>   * Accessor method to extract address matching type 'a field' from cfg reg

>   */

> @@ -99,7 +119,28 @@ static inline uint8_t pmp_read_cfg(CPURISCVState *env, uint32_t pmp_index)

>  static void pmp_write_cfg(CPURISCVState *env, uint32_t pmp_index, uint8_t val)

>  {

>      if (pmp_index < MAX_RISCV_PMPS) {

> -        if (!pmp_is_locked(env, pmp_index)) {

> +        /*

> +         * mseccfg.RLB is set

> +         */


These can just be single line comments instead of three.

> +        if (MSECCFG_RLB_ISSET(env) ||

> +            /*

> +             * mseccfg.MML is set

> +             */

> +            (MSECCFG_MML_ISSET(env) &&

> +            /*

> +             * m model and not adding X bit

> +             */

> +            (((val & PMP_LOCK) != 0 && (val & PMP_EXEC) != PMP_EXEC) ||

> +             /*

> +              * shared region and not adding X bit

> +              */

> +            ((val & PMP_LOCK) != PMP_LOCK &&

> +            (val & 0x7) != (PMP_WRITE | PMP_EXEC)))) ||

> +            /*

> +             * mseccfg.MML is not set

> +             */

> +            (!MSECCFG_MML_ISSET(env) && !pmp_is_locked(env, pmp_index))

> +        ){

>              env->pmp_state.pmp[pmp_index].cfg_reg = val;

>              pmp_update_rule(env, pmp_index);

>          } else {

> @@ -230,6 +271,18 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,

>

>      /* Short cut if no rules */

>      if (0 == pmp_get_num_rules(env)) {

> +        if (MSECCFG_MMWP_ISSET(env)) {

> +            qemu_log_mask(LOG_GUEST_ERROR,

> +                          "pmp violation - %c mode access denied\n",

> +                          mode_to_char(mode));

> +            return false;

> +        }

> +        if (MSECCFG_MML_ISSET(env) && (mode != PRV_M || (privs & PMP_EXEC))) {

> +            qemu_log_mask(LOG_GUEST_ERROR,

> +                          "pmp violation - %c mode access denied\n",

> +                          mode_to_char(mode));

> +            return false;

> +        }

>          return true;

>      }

>

> @@ -265,16 +318,65 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,

>          const uint8_t a_field =

>              pmp_get_a_field(env->pmp_state.pmp[i].cfg_reg);

>

> -        /*

> -         * If the PMP entry is not off and the address is in range, do the priv

> -         * check

> -         */

>          if (((s + e) == 2) && (PMP_AMATCH_OFF != a_field)) {

> -            allowed_privs = PMP_READ | PMP_WRITE | PMP_EXEC;

> -            if ((mode != PRV_M) || pmp_is_locked(env, i)) {

> -                allowed_privs &= env->pmp_state.pmp[i].cfg_reg;

> +            /*

> +             * If the PMP entry is not off and the address is in range,

> +             * do the priv check

> +             */

> +            if (!MSECCFG_MML_ISSET(env)) {

> +                /*

> +                 * If mseccfg.MML Bit is not set, do pmp priv check

> +                 */

> +                allowed_privs = PMP_READ | PMP_WRITE | PMP_EXEC;

> +                if ((mode != PRV_M) || pmp_is_locked(env, i)) {

> +                    allowed_privs &= env->pmp_state.pmp[i].cfg_reg;

> +                }

> +            } else {

> +                /*

> +                 * If mseccfg.MML Bit set, do the enhanced pmp priv check

> +                 */

> +                if (env->pmp_state.pmp[i].cfg_reg & PMP_LOCK) {

> +                    /*

> +                     * Shared Region

> +                     */

> +                    if ((env->pmp_state.pmp[i].cfg_reg &

> +                    (PMP_READ | PMP_WRITE)) == PMP_WRITE) {

> +                        allowed_privs = PMP_EXEC | ((mode == PRV_M &&

> +                        (env->pmp_state.pmp[i].cfg_reg & PMP_EXEC)) ?

> +                        PMP_READ : 0);

> +                    } else {

> +                        allowed_privs = env->pmp_state.pmp[i].cfg_reg &

> +                        (PMP_READ | PMP_WRITE | PMP_EXEC);

> +

> +                        if (mode != PRV_M && allowed_privs) {

> +                            qemu_log_mask(LOG_GUEST_ERROR,

> +                                "pmp violation - %c mode access denied\n",

> +                                mode_to_char(mode));

> +                            ret = 0;

> +                            break;

> +                        }

> +                    }

> +                } else {

> +                    /*

> +                     * Shared Region

> +                     */

> +                    if ((env->pmp_state.pmp[i].cfg_reg &

> +                        (PMP_READ | PMP_WRITE)) == PMP_WRITE) {

> +                        allowed_privs = PMP_READ | ((mode == PRV_M ||

> +                        (env->pmp_state.pmp[i].cfg_reg & PMP_EXEC)) ?

> +                        PMP_WRITE : 0);

> +                    } else {

> +                        allowed_privs = env->pmp_state.pmp[i].cfg_reg &

> +                        (PMP_READ | PMP_WRITE | PMP_EXEC);

> +                        if (mode == PRV_M && allowed_privs) {

> +                            qemu_log_mask(LOG_GUEST_ERROR,

> +                                    "pmp violation - m mode access denied\n");

> +                            ret = 0;

> +                            break;

> +                        }

> +                    }

> +                }

>              }

> -

>              if ((privs & allowed_privs) == privs) {

>                  ret = 1;

>                  break;

> @@ -288,15 +390,23 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,

>      /* No rule matched */

>      if (ret == -1) {

>          if (mode == PRV_M) {

> -            ret = 1; /* Privileged spec v1.10 states if no PMP entry matches an

> -                      * M-Mode access, the access succeeds */

> +            ret = !MSECCFG_MMWP_ISSET(env); /* PMP Enhancements */

> +            if (MSECCFG_MML_ISSET(env) && (privs & PMP_EXEC)) {

> +                ret = 0;

> +            }

>          } else {

>              ret = 0; /* Other modes are not allowed to succeed if they don't

>                        * match a rule, but there are rules.  We've checked for

>                        * no rule earlier in this function. */

>          }

>      }

> -

> +    if (ret) {

> +        trace_pmp_hart_has_privs_pass_match(

> +            env->mhartid, addr, size, privs, mode);

> +    } else {

> +        trace_pmp_hart_has_privs_violation(

> +            env->mhartid, addr, size, privs, mode);

> +    }

>      return ret == 1 ? true : false;

>  }

>

> diff --git a/target/riscv/pmp.h b/target/riscv/pmp.h

> index 8e19793132..7db2069204 100644

> --- a/target/riscv/pmp.h

> +++ b/target/riscv/pmp.h

> @@ -36,6 +36,12 @@ typedef enum {

>      PMP_AMATCH_NAPOT /* Naturally aligned power-of-two region */

>  } pmp_am_t;

>

> +typedef enum {

> +    MSECCFG_MML  = 1 << 0,

> +    MSECCFG_MMWP = 1 << 1,

> +    MSECCFG_RLB  = 1 << 2

> +} mseccfg_field_t;

> +

>  typedef struct {

>      target_ulong addr_reg;

>      uint8_t  cfg_reg;

> @@ -58,7 +64,13 @@ target_ulong pmpcfg_csr_read(CPURISCVState *env, uint32_t reg_index);

>  void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index,

>      target_ulong val);

>  target_ulong pmpaddr_csr_read(CPURISCVState *env, uint32_t addr_index);

> +void mseccfg_csr_write(CPURISCVState *env, target_ulong val);

> +target_ulong mseccfg_csr_read(CPURISCVState *env);

>  bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,

>      target_ulong size, pmp_priv_t priv, target_ulong mode);

>

> +#define MSECCFG_MML_ISSET(env) get_field(env->mseccfg, MSECCFG_MML)

> +#define MSECCFG_MMWP_ISSET(env) get_field(env->mseccfg, MSECCFG_MMWP)

> +#define MSECCFG_RLB_ISSET(env) get_field(env->mseccfg, MSECCFG_RLB)


This fails to compile as the `target_ulong mseccfg` variable has not
been added in this patch.

Alistair

> +

>  #endif

> diff --git a/target/riscv/trace-events b/target/riscv/trace-events

> index 4b6c652ae9..4f877f90f7 100644

> --- a/target/riscv/trace-events

> +++ b/target/riscv/trace-events

> @@ -6,3 +6,7 @@ pmpcfg_csr_read(uint64_t mhartid, uint32_t reg_index, uint64_t val) "hart %" PRI

>  pmpcfg_csr_write(uint64_t mhartid, uint32_t reg_index, uint64_t val) "hart %" PRIu64 ": write reg%" PRIu32", val: 0x%" PRIx64

>  pmpaddr_csr_read(uint64_t mhartid, uint32_t addr_index, uint64_t val) "hart %" PRIu64 ": read addr%" PRIu32", val: 0x%" PRIx64

>  pmpaddr_csr_write(uint64_t mhartid, uint32_t addr_index, uint64_t val) "hart %" PRIu64 ": write addr%" PRIu32", val: 0x%" PRIx64

> +mseccfg_csr_read(uint64_t mhartid, uint64_t val) "hart %" PRIu64 ": read mseccfg, val: 0x%" PRIx64

> +mseccfg_csr_write(uint64_t mhartid, uint64_t val) "hart %" PRIu64 ": write mseccfg, val: 0x%" PRIx64

> +pmp_hart_has_privs_pass_match(uint64_t mhartid, uint64_t addr, uint64_t size, uint64_t privs, uint64_t mode) "hart %"PRId64 "pass PMP 0 match addr:%"PRIu64" size:%"PRIu64 "privs: %"PRIu64 "mode: %"PRIu64

> +pmp_hart_has_privs_violation(uint64_t mhartid, uint64_t addr, uint64_t size, uint64_t privs, uint64_t mode) "hart %"PRId64 "pass PMP 0 match addr:%"PRIu64" size:%"PRIu64 "privs: %"PRIu64 "mode: %"PRIu64

> --

> 2.20.1

>

>
Alistair Francis Feb. 10, 2021, 8:18 p.m. UTC | #2
On Mon, Aug 10, 2020 at 5:24 PM Hou Weiying <weiying_hou@outlook.com> wrote:
>

> The ePMP can be found in:

> https://docs.google.com/document/d/1Mh_aiHYxemL0umN3GTTw8vsbmzHZ_nxZXgjgOUzbvc8/edit#heading=h.9wsr1lnxtwe2

>

> Signed-off-by: Hongzheng-Li <Ethan.Lee.QNL@gmail.com>

> Signed-off-by: Hou Weiying <weiying_hou@outlook.com>

> Signed-off-by: Myriad-Dreamin <camiyoru@gmail.com>

> ---

>  target/riscv/pmp.c        | 134 ++++++++++++++++++++++++++++++++++----

>  target/riscv/pmp.h        |  12 ++++

>  target/riscv/trace-events |   4 ++

>  3 files changed, 138 insertions(+), 12 deletions(-)

>

> diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c

> index 2a2b9f5363..b1fa703aff 100644

> --- a/target/riscv/pmp.c

> +++ b/target/riscv/pmp.c

> @@ -34,6 +34,26 @@ static void pmp_write_cfg(CPURISCVState *env, uint32_t addr_index,

>  static uint8_t pmp_read_cfg(CPURISCVState *env, uint32_t addr_index);

>  static void pmp_update_rule(CPURISCVState *env, uint32_t pmp_index);

>

> +static char mode_to_char(int mode)

> +{

> +    char ret = 0;

> +    switch (mode) {

> +    case PRV_U:

> +        ret = 'u';

> +        break;

> +    case PRV_S:

> +        ret = 's';

> +        break;

> +    case PRV_H:

> +        ret = 'h';

> +        break;

> +    case PRV_M:

> +        ret = 'm';

> +        break;

> +    }

> +    return ret;

> +}

> +

>  /*

>   * Accessor method to extract address matching type 'a field' from cfg reg

>   */

> @@ -99,7 +119,28 @@ static inline uint8_t pmp_read_cfg(CPURISCVState *env, uint32_t pmp_index)

>  static void pmp_write_cfg(CPURISCVState *env, uint32_t pmp_index, uint8_t val)

>  {

>      if (pmp_index < MAX_RISCV_PMPS) {

> -        if (!pmp_is_locked(env, pmp_index)) {

> +        /*

> +         * mseccfg.RLB is set

> +         */

> +        if (MSECCFG_RLB_ISSET(env) ||

> +            /*

> +             * mseccfg.MML is set

> +             */

> +            (MSECCFG_MML_ISSET(env) &&

> +            /*

> +             * m model and not adding X bit

> +             */

> +            (((val & PMP_LOCK) != 0 && (val & PMP_EXEC) != PMP_EXEC) ||

> +             /*

> +              * shared region and not adding X bit

> +              */

> +            ((val & PMP_LOCK) != PMP_LOCK &&

> +            (val & 0x7) != (PMP_WRITE | PMP_EXEC)))) ||

> +            /*

> +             * mseccfg.MML is not set

> +             */

> +            (!MSECCFG_MML_ISSET(env) && !pmp_is_locked(env, pmp_index))

> +        ){

>              env->pmp_state.pmp[pmp_index].cfg_reg = val;

>              pmp_update_rule(env, pmp_index);

>          } else {

> @@ -230,6 +271,18 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,

>

>      /* Short cut if no rules */

>      if (0 == pmp_get_num_rules(env)) {

> +        if (MSECCFG_MMWP_ISSET(env)) {

> +            qemu_log_mask(LOG_GUEST_ERROR,

> +                          "pmp violation - %c mode access denied\n",

> +                          mode_to_char(mode));

> +            return false;

> +        }

> +        if (MSECCFG_MML_ISSET(env) && (mode != PRV_M || (privs & PMP_EXEC))) {

> +            qemu_log_mask(LOG_GUEST_ERROR,

> +                          "pmp violation - %c mode access denied\n",

> +                          mode_to_char(mode));

> +            return false;

> +        }

>          return true;

>      }

>

> @@ -265,16 +318,65 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,

>          const uint8_t a_field =

>              pmp_get_a_field(env->pmp_state.pmp[i].cfg_reg);

>

> -        /*

> -         * If the PMP entry is not off and the address is in range, do the priv

> -         * check

> -         */

>          if (((s + e) == 2) && (PMP_AMATCH_OFF != a_field)) {

> -            allowed_privs = PMP_READ | PMP_WRITE | PMP_EXEC;

> -            if ((mode != PRV_M) || pmp_is_locked(env, i)) {

> -                allowed_privs &= env->pmp_state.pmp[i].cfg_reg;

> +            /*

> +             * If the PMP entry is not off and the address is in range,

> +             * do the priv check

> +             */

> +            if (!MSECCFG_MML_ISSET(env)) {

> +                /*

> +                 * If mseccfg.MML Bit is not set, do pmp priv check

> +                 */

> +                allowed_privs = PMP_READ | PMP_WRITE | PMP_EXEC;

> +                if ((mode != PRV_M) || pmp_is_locked(env, i)) {

> +                    allowed_privs &= env->pmp_state.pmp[i].cfg_reg;

> +                }

> +            } else {

> +                /*

> +                 * If mseccfg.MML Bit set, do the enhanced pmp priv check

> +                 */

> +                if (env->pmp_state.pmp[i].cfg_reg & PMP_LOCK) {

> +                    /*

> +                     * Shared Region

> +                     */

> +                    if ((env->pmp_state.pmp[i].cfg_reg &

> +                    (PMP_READ | PMP_WRITE)) == PMP_WRITE) {

> +                        allowed_privs = PMP_EXEC | ((mode == PRV_M &&

> +                        (env->pmp_state.pmp[i].cfg_reg & PMP_EXEC)) ?

> +                        PMP_READ : 0);

> +                    } else {

> +                        allowed_privs = env->pmp_state.pmp[i].cfg_reg &

> +                        (PMP_READ | PMP_WRITE | PMP_EXEC);

> +

> +                        if (mode != PRV_M && allowed_privs) {

> +                            qemu_log_mask(LOG_GUEST_ERROR,

> +                                "pmp violation - %c mode access denied\n",

> +                                mode_to_char(mode));

> +                            ret = 0;

> +                            break;

> +                        }

> +                    }

> +                } else {

> +                    /*

> +                     * Shared Region

> +                     */

> +                    if ((env->pmp_state.pmp[i].cfg_reg &

> +                        (PMP_READ | PMP_WRITE)) == PMP_WRITE) {

> +                        allowed_privs = PMP_READ | ((mode == PRV_M ||

> +                        (env->pmp_state.pmp[i].cfg_reg & PMP_EXEC)) ?

> +                        PMP_WRITE : 0);

> +                    } else {

> +                        allowed_privs = env->pmp_state.pmp[i].cfg_reg &

> +                        (PMP_READ | PMP_WRITE | PMP_EXEC);

> +                        if (mode == PRV_M && allowed_privs) {

> +                            qemu_log_mask(LOG_GUEST_ERROR,

> +                                    "pmp violation - m mode access denied\n");

> +                            ret = 0;

> +                            break;


This is really hard to follow, I had a lot of trouble wrapping my head
around this and I'm still not sure it's correct.

I feel that a switch satement here would be easier. The spec has a
nice table of all the possible options, for example something like
this:

                if (mode == PRV_M) {
                    switch (epmp_operation) {
                    case 0:
                    case 1:
                    case 4:
                    case 5:
                    case 6:
                    case 7:
                    case 8:
                        allowed_privs = 0;
                        break;
                    case 2:
                    case 3:
                    case 14:
                        allowed_privs = PMP_READ | PMP_WRITE;
                        break;
                    case 9:
                    case 10:
                        allowed_privs = PMP_EXEC;
                        break;
                    case 11:
                    case 13:
                        allowed_privs = PMP_READ | PMP_EXEC;
                        break;
                    case 12:
                    case 15:
                        allowed_privs = PMP_READ;
                        break;
                    }
                } else {
                    switch (epmp_operation) {
                    case 0:
                    case 8:
                    case 9:
                    case 12:
                    case 13:
                    case 14:
                        allowed_privs = 0;
                        break;
                    case 1:
                    case 10:
                    case 11:
                        allowed_privs = PMP_EXEC;
                        break;
                    case 2:
                    case 4:
                    case 15:
                        allowed_privs = PMP_READ;
                        break;
                    case 3:
                    case 6:
                        allowed_privs = PMP_READ | PMP_WRITE;
                        break;
                    case 5:
                        allowed_privs = PMP_READ | PMP_EXEC;
                        break;
                    case 7:
                        allowed_privs = PMP_READ | PMP_WRITE | PMP_EXEC;
                        break;
                    }
                }

It's a little more verbose, but it should still be fairly quick to compute.

If you want to keep the logic that's also fine, I just think it needs
some simplifiation. Maybe assign read, write, exec variables and just
compare them?

bool read = env->pmp_state.pmp[i].cfg_reg & PMP_READ;
bool write = env->pmp_state.pmp[i].cfg_reg & PMP_WRITE;
bool exec = env->pmp_state.pmp[i].cfg_reg & PMP_EXEC;

Then

if ((env->pmp_state.pmp[i].cfg_reg & (PMP_READ | PMP_WRITE)) == PMP_WRITE) {

becomes

if (write && !read) {

Alistair

> +                        }

> +                    }

> +                }

>              }

> -

>              if ((privs & allowed_privs) == privs) {

>                  ret = 1;

>                  break;

> @@ -288,15 +390,23 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,

>      /* No rule matched */

>      if (ret == -1) {

>          if (mode == PRV_M) {

> -            ret = 1; /* Privileged spec v1.10 states if no PMP entry matches an

> -                      * M-Mode access, the access succeeds */

> +            ret = !MSECCFG_MMWP_ISSET(env); /* PMP Enhancements */

> +            if (MSECCFG_MML_ISSET(env) && (privs & PMP_EXEC)) {

> +                ret = 0;

> +            }

>          } else {

>              ret = 0; /* Other modes are not allowed to succeed if they don't

>                        * match a rule, but there are rules.  We've checked for

>                        * no rule earlier in this function. */

>          }

>      }

> -

> +    if (ret) {

> +        trace_pmp_hart_has_privs_pass_match(

> +            env->mhartid, addr, size, privs, mode);

> +    } else {

> +        trace_pmp_hart_has_privs_violation(

> +            env->mhartid, addr, size, privs, mode);

> +    }

>      return ret == 1 ? true : false;

>  }

>

> diff --git a/target/riscv/pmp.h b/target/riscv/pmp.h

> index 8e19793132..7db2069204 100644

> --- a/target/riscv/pmp.h

> +++ b/target/riscv/pmp.h

> @@ -36,6 +36,12 @@ typedef enum {

>      PMP_AMATCH_NAPOT /* Naturally aligned power-of-two region */

>  } pmp_am_t;

>

> +typedef enum {

> +    MSECCFG_MML  = 1 << 0,

> +    MSECCFG_MMWP = 1 << 1,

> +    MSECCFG_RLB  = 1 << 2

> +} mseccfg_field_t;

> +

>  typedef struct {

>      target_ulong addr_reg;

>      uint8_t  cfg_reg;

> @@ -58,7 +64,13 @@ target_ulong pmpcfg_csr_read(CPURISCVState *env, uint32_t reg_index);

>  void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index,

>      target_ulong val);

>  target_ulong pmpaddr_csr_read(CPURISCVState *env, uint32_t addr_index);

> +void mseccfg_csr_write(CPURISCVState *env, target_ulong val);

> +target_ulong mseccfg_csr_read(CPURISCVState *env);

>  bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,

>      target_ulong size, pmp_priv_t priv, target_ulong mode);

>

> +#define MSECCFG_MML_ISSET(env) get_field(env->mseccfg, MSECCFG_MML)

> +#define MSECCFG_MMWP_ISSET(env) get_field(env->mseccfg, MSECCFG_MMWP)

> +#define MSECCFG_RLB_ISSET(env) get_field(env->mseccfg, MSECCFG_RLB)

> +

>  #endif

> diff --git a/target/riscv/trace-events b/target/riscv/trace-events

> index 4b6c652ae9..4f877f90f7 100644

> --- a/target/riscv/trace-events

> +++ b/target/riscv/trace-events

> @@ -6,3 +6,7 @@ pmpcfg_csr_read(uint64_t mhartid, uint32_t reg_index, uint64_t val) "hart %" PRI

>  pmpcfg_csr_write(uint64_t mhartid, uint32_t reg_index, uint64_t val) "hart %" PRIu64 ": write reg%" PRIu32", val: 0x%" PRIx64

>  pmpaddr_csr_read(uint64_t mhartid, uint32_t addr_index, uint64_t val) "hart %" PRIu64 ": read addr%" PRIu32", val: 0x%" PRIx64

>  pmpaddr_csr_write(uint64_t mhartid, uint32_t addr_index, uint64_t val) "hart %" PRIu64 ": write addr%" PRIu32", val: 0x%" PRIx64

> +mseccfg_csr_read(uint64_t mhartid, uint64_t val) "hart %" PRIu64 ": read mseccfg, val: 0x%" PRIx64

> +mseccfg_csr_write(uint64_t mhartid, uint64_t val) "hart %" PRIu64 ": write mseccfg, val: 0x%" PRIx64

> +pmp_hart_has_privs_pass_match(uint64_t mhartid, uint64_t addr, uint64_t size, uint64_t privs, uint64_t mode) "hart %"PRId64 "pass PMP 0 match addr:%"PRIu64" size:%"PRIu64 "privs: %"PRIu64 "mode: %"PRIu64

> +pmp_hart_has_privs_violation(uint64_t mhartid, uint64_t addr, uint64_t size, uint64_t privs, uint64_t mode) "hart %"PRId64 "pass PMP 0 match addr:%"PRIu64" size:%"PRIu64 "privs: %"PRIu64 "mode: %"PRIu64

> --

> 2.20.1

>

>
diff mbox series

Patch

diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c
index 2a2b9f5363..b1fa703aff 100644
--- a/target/riscv/pmp.c
+++ b/target/riscv/pmp.c
@@ -34,6 +34,26 @@  static void pmp_write_cfg(CPURISCVState *env, uint32_t addr_index,
 static uint8_t pmp_read_cfg(CPURISCVState *env, uint32_t addr_index);
 static void pmp_update_rule(CPURISCVState *env, uint32_t pmp_index);
 
+static char mode_to_char(int mode)
+{
+    char ret = 0;
+    switch (mode) {
+    case PRV_U:
+        ret = 'u';
+        break;
+    case PRV_S:
+        ret = 's';
+        break;
+    case PRV_H:
+        ret = 'h';
+        break;
+    case PRV_M:
+        ret = 'm';
+        break;
+    }
+    return ret;
+}
+
 /*
  * Accessor method to extract address matching type 'a field' from cfg reg
  */
@@ -99,7 +119,28 @@  static inline uint8_t pmp_read_cfg(CPURISCVState *env, uint32_t pmp_index)
 static void pmp_write_cfg(CPURISCVState *env, uint32_t pmp_index, uint8_t val)
 {
     if (pmp_index < MAX_RISCV_PMPS) {
-        if (!pmp_is_locked(env, pmp_index)) {
+        /*
+         * mseccfg.RLB is set
+         */
+        if (MSECCFG_RLB_ISSET(env) ||
+            /*
+             * mseccfg.MML is set
+             */
+            (MSECCFG_MML_ISSET(env) &&
+            /*
+             * m model and not adding X bit
+             */
+            (((val & PMP_LOCK) != 0 && (val & PMP_EXEC) != PMP_EXEC) ||
+             /*
+              * shared region and not adding X bit
+              */
+            ((val & PMP_LOCK) != PMP_LOCK &&
+            (val & 0x7) != (PMP_WRITE | PMP_EXEC)))) ||
+            /*
+             * mseccfg.MML is not set
+             */
+            (!MSECCFG_MML_ISSET(env) && !pmp_is_locked(env, pmp_index))
+        ){
             env->pmp_state.pmp[pmp_index].cfg_reg = val;
             pmp_update_rule(env, pmp_index);
         } else {
@@ -230,6 +271,18 @@  bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
 
     /* Short cut if no rules */
     if (0 == pmp_get_num_rules(env)) {
+        if (MSECCFG_MMWP_ISSET(env)) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "pmp violation - %c mode access denied\n",
+                          mode_to_char(mode));
+            return false;
+        }
+        if (MSECCFG_MML_ISSET(env) && (mode != PRV_M || (privs & PMP_EXEC))) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "pmp violation - %c mode access denied\n",
+                          mode_to_char(mode));
+            return false;
+        }
         return true;
     }
 
@@ -265,16 +318,65 @@  bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
         const uint8_t a_field =
             pmp_get_a_field(env->pmp_state.pmp[i].cfg_reg);
 
-        /*
-         * If the PMP entry is not off and the address is in range, do the priv
-         * check
-         */
         if (((s + e) == 2) && (PMP_AMATCH_OFF != a_field)) {
-            allowed_privs = PMP_READ | PMP_WRITE | PMP_EXEC;
-            if ((mode != PRV_M) || pmp_is_locked(env, i)) {
-                allowed_privs &= env->pmp_state.pmp[i].cfg_reg;
+            /*
+             * If the PMP entry is not off and the address is in range,
+             * do the priv check
+             */
+            if (!MSECCFG_MML_ISSET(env)) {
+                /*
+                 * If mseccfg.MML Bit is not set, do pmp priv check
+                 */
+                allowed_privs = PMP_READ | PMP_WRITE | PMP_EXEC;
+                if ((mode != PRV_M) || pmp_is_locked(env, i)) {
+                    allowed_privs &= env->pmp_state.pmp[i].cfg_reg;
+                }
+            } else {
+                /*
+                 * If mseccfg.MML Bit set, do the enhanced pmp priv check
+                 */
+                if (env->pmp_state.pmp[i].cfg_reg & PMP_LOCK) {
+                    /*
+                     * Shared Region
+                     */
+                    if ((env->pmp_state.pmp[i].cfg_reg &
+                    (PMP_READ | PMP_WRITE)) == PMP_WRITE) {
+                        allowed_privs = PMP_EXEC | ((mode == PRV_M &&
+                        (env->pmp_state.pmp[i].cfg_reg & PMP_EXEC)) ?
+                        PMP_READ : 0);
+                    } else {
+                        allowed_privs = env->pmp_state.pmp[i].cfg_reg &
+                        (PMP_READ | PMP_WRITE | PMP_EXEC);
+
+                        if (mode != PRV_M && allowed_privs) {
+                            qemu_log_mask(LOG_GUEST_ERROR,
+                                "pmp violation - %c mode access denied\n",
+                                mode_to_char(mode));
+                            ret = 0;
+                            break;
+                        }
+                    }
+                } else {
+                    /*
+                     * Shared Region
+                     */
+                    if ((env->pmp_state.pmp[i].cfg_reg &
+                        (PMP_READ | PMP_WRITE)) == PMP_WRITE) {
+                        allowed_privs = PMP_READ | ((mode == PRV_M ||
+                        (env->pmp_state.pmp[i].cfg_reg & PMP_EXEC)) ?
+                        PMP_WRITE : 0);
+                    } else {
+                        allowed_privs = env->pmp_state.pmp[i].cfg_reg &
+                        (PMP_READ | PMP_WRITE | PMP_EXEC);
+                        if (mode == PRV_M && allowed_privs) {
+                            qemu_log_mask(LOG_GUEST_ERROR,
+                                    "pmp violation - m mode access denied\n");
+                            ret = 0;
+                            break;
+                        }
+                    }
+                }
             }
-
             if ((privs & allowed_privs) == privs) {
                 ret = 1;
                 break;
@@ -288,15 +390,23 @@  bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
     /* No rule matched */
     if (ret == -1) {
         if (mode == PRV_M) {
-            ret = 1; /* Privileged spec v1.10 states if no PMP entry matches an
-                      * M-Mode access, the access succeeds */
+            ret = !MSECCFG_MMWP_ISSET(env); /* PMP Enhancements */
+            if (MSECCFG_MML_ISSET(env) && (privs & PMP_EXEC)) {
+                ret = 0;
+            }
         } else {
             ret = 0; /* Other modes are not allowed to succeed if they don't
                       * match a rule, but there are rules.  We've checked for
                       * no rule earlier in this function. */
         }
     }
-
+    if (ret) {
+        trace_pmp_hart_has_privs_pass_match(
+            env->mhartid, addr, size, privs, mode);
+    } else {
+        trace_pmp_hart_has_privs_violation(
+            env->mhartid, addr, size, privs, mode);
+    }
     return ret == 1 ? true : false;
 }
 
diff --git a/target/riscv/pmp.h b/target/riscv/pmp.h
index 8e19793132..7db2069204 100644
--- a/target/riscv/pmp.h
+++ b/target/riscv/pmp.h
@@ -36,6 +36,12 @@  typedef enum {
     PMP_AMATCH_NAPOT /* Naturally aligned power-of-two region */
 } pmp_am_t;
 
+typedef enum {
+    MSECCFG_MML  = 1 << 0,
+    MSECCFG_MMWP = 1 << 1,
+    MSECCFG_RLB  = 1 << 2
+} mseccfg_field_t;
+
 typedef struct {
     target_ulong addr_reg;
     uint8_t  cfg_reg;
@@ -58,7 +64,13 @@  target_ulong pmpcfg_csr_read(CPURISCVState *env, uint32_t reg_index);
 void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index,
     target_ulong val);
 target_ulong pmpaddr_csr_read(CPURISCVState *env, uint32_t addr_index);
+void mseccfg_csr_write(CPURISCVState *env, target_ulong val);
+target_ulong mseccfg_csr_read(CPURISCVState *env);
 bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
     target_ulong size, pmp_priv_t priv, target_ulong mode);
 
+#define MSECCFG_MML_ISSET(env) get_field(env->mseccfg, MSECCFG_MML)
+#define MSECCFG_MMWP_ISSET(env) get_field(env->mseccfg, MSECCFG_MMWP)
+#define MSECCFG_RLB_ISSET(env) get_field(env->mseccfg, MSECCFG_RLB)
+
 #endif
diff --git a/target/riscv/trace-events b/target/riscv/trace-events
index 4b6c652ae9..4f877f90f7 100644
--- a/target/riscv/trace-events
+++ b/target/riscv/trace-events
@@ -6,3 +6,7 @@  pmpcfg_csr_read(uint64_t mhartid, uint32_t reg_index, uint64_t val) "hart %" PRI
 pmpcfg_csr_write(uint64_t mhartid, uint32_t reg_index, uint64_t val) "hart %" PRIu64 ": write reg%" PRIu32", val: 0x%" PRIx64
 pmpaddr_csr_read(uint64_t mhartid, uint32_t addr_index, uint64_t val) "hart %" PRIu64 ": read addr%" PRIu32", val: 0x%" PRIx64
 pmpaddr_csr_write(uint64_t mhartid, uint32_t addr_index, uint64_t val) "hart %" PRIu64 ": write addr%" PRIu32", val: 0x%" PRIx64
+mseccfg_csr_read(uint64_t mhartid, uint64_t val) "hart %" PRIu64 ": read mseccfg, val: 0x%" PRIx64
+mseccfg_csr_write(uint64_t mhartid, uint64_t val) "hart %" PRIu64 ": write mseccfg, val: 0x%" PRIx64
+pmp_hart_has_privs_pass_match(uint64_t mhartid, uint64_t addr, uint64_t size, uint64_t privs, uint64_t mode) "hart %"PRId64 "pass PMP 0 match addr:%"PRIu64" size:%"PRIu64 "privs: %"PRIu64 "mode: %"PRIu64
+pmp_hart_has_privs_violation(uint64_t mhartid, uint64_t addr, uint64_t size, uint64_t privs, uint64_t mode) "hart %"PRId64 "pass PMP 0 match addr:%"PRIu64" size:%"PRIu64 "privs: %"PRIu64 "mode: %"PRIu64