diff mbox series

target/riscv: Fix PMP propagation for tlb

Message ID 20221012060016.30856-1-zhiwei_liu@linux.alibaba.com (mailing list archive)
State New, archived
Headers show
Series target/riscv: Fix PMP propagation for tlb | expand

Commit Message

LIU Zhiwei Oct. 12, 2022, 6 a.m. UTC
Only the pmp index that be checked by pmp_hart_has_privs can be used
by pmp_get_tlb_size to avoid an error pmp index.

Before modification, we may use an error pmp index. For example,
we check address 0x4fc, and the size 0x4 in pmp_hart_has_privs. If there
is an pmp rule, valid range is [0x4fc, 0x500), then pmp_hart_has_privs
will return true;

However, this checked pmp index is discarded as pmp_hart_has_privs
return bool value. In pmp_is_range_in_tlb, it will traverse all pmp
rules. The tlb_sa will be 0x0, and tlb_ea will be 0xfff. If there is
a pmp rule [0x10, 0x14), it will be misused as it is legal in
pmp_get_tlb_size.

As we have already known the correct pmp index, just remove the
remove the pmp_is_range_in_tlb and get tlb size directly from
pmp_get_tlb_size.

Signed-off-by: LIU Zhiwei <zhiwei_liu@linux.alibaba.com>
---
 target/riscv/cpu_helper.c | 16 ++++---
 target/riscv/pmp.c        | 90 +++++++++++++--------------------------
 target/riscv/pmp.h        |  6 +--
 3 files changed, 42 insertions(+), 70 deletions(-)

Comments

LIU Zhiwei Oct. 21, 2022, 1:22 a.m. UTC | #1
ping

On 2022/10/12 14:00, LIU Zhiwei wrote:
> Only the pmp index that be checked by pmp_hart_has_privs can be used
> by pmp_get_tlb_size to avoid an error pmp index.
>
> Before modification, we may use an error pmp index. For example,
> we check address 0x4fc, and the size 0x4 in pmp_hart_has_privs. If there
> is an pmp rule, valid range is [0x4fc, 0x500), then pmp_hart_has_privs
> will return true;
>
> However, this checked pmp index is discarded as pmp_hart_has_privs
> return bool value. In pmp_is_range_in_tlb, it will traverse all pmp
> rules. The tlb_sa will be 0x0, and tlb_ea will be 0xfff. If there is
> a pmp rule [0x10, 0x14), it will be misused as it is legal in
> pmp_get_tlb_size.
>
> As we have already known the correct pmp index, just remove the
> remove the pmp_is_range_in_tlb and get tlb size directly from
> pmp_get_tlb_size.
>
> Signed-off-by: LIU Zhiwei <zhiwei_liu@linux.alibaba.com>
> ---
>   target/riscv/cpu_helper.c | 16 ++++---
>   target/riscv/pmp.c        | 90 +++++++++++++--------------------------
>   target/riscv/pmp.h        |  6 +--
>   3 files changed, 42 insertions(+), 70 deletions(-)
>
> diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
> index 278d163803..5d66246c2c 100644
> --- a/target/riscv/cpu_helper.c
> +++ b/target/riscv/cpu_helper.c
> @@ -706,24 +706,26 @@ static int get_physical_address_pmp(CPURISCVState *env, int *prot,
>                                       int mode)
>   {
>       pmp_priv_t pmp_priv;
> -    target_ulong tlb_size_pmp = 0;
> +    int pmp_index = -1;
>   
>       if (!riscv_feature(env, RISCV_FEATURE_PMP)) {
>           *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
>           return TRANSLATE_SUCCESS;
>       }
>   
> -    if (!pmp_hart_has_privs(env, addr, size, 1 << access_type, &pmp_priv,
> -                            mode)) {
> +    pmp_index = pmp_hart_has_privs(env, addr, size, 1 << access_type,
> +                                   &pmp_priv, mode);
> +    if (pmp_index < 0) {
>           *prot = 0;
>           return TRANSLATE_PMP_FAIL;
>       }
>   
>       *prot = pmp_priv_to_page_prot(pmp_priv);
> -    if (tlb_size != NULL) {
> -        if (pmp_is_range_in_tlb(env, addr & ~(*tlb_size - 1), &tlb_size_pmp)) {
> -            *tlb_size = tlb_size_pmp;
> -        }
> +    if ((tlb_size != NULL) && pmp_index != MAX_RISCV_PMPS) {
> +        target_ulong tlb_sa = addr & ~(TARGET_PAGE_SIZE - 1);
> +        target_ulong tlb_ea = tlb_sa + TARGET_PAGE_SIZE - 1;
> +
> +        *tlb_size = pmp_get_tlb_size(env, pmp_index, tlb_sa, tlb_ea);
>       }
>   
>       return TRANSLATE_SUCCESS;
> diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c
> index 2b43e399b8..d1126a6066 100644
> --- a/target/riscv/pmp.c
> +++ b/target/riscv/pmp.c
> @@ -292,8 +292,11 @@ static bool pmp_hart_has_privs_default(CPURISCVState *env, target_ulong addr,
>   
>   /*
>    * Check if the address has required RWX privs to complete desired operation
> + * Return PMP rule index if a pmp rule match
> + * Return MAX_RISCV_PMPS if default match
> + * Return negtive value if no match
>    */
> -bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
> +int pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
>       target_ulong size, pmp_priv_t privs, pmp_priv_t *allowed_privs,
>       target_ulong mode)
>   {
> @@ -305,8 +308,10 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
>   
>       /* Short cut if no rules */
>       if (0 == pmp_get_num_rules(env)) {
> -        return pmp_hart_has_privs_default(env, addr, size, privs,
> -                                          allowed_privs, mode);
> +        if (pmp_hart_has_privs_default(env, addr, size, privs,
> +                                       allowed_privs, mode)) {
> +            ret = MAX_RISCV_PMPS;
> +        }
>       }
>   
>       if (size == 0) {
> @@ -333,7 +338,7 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
>           if ((s + e) == 1) {
>               qemu_log_mask(LOG_GUEST_ERROR,
>                             "pmp violation - access is partially inside\n");
> -            ret = 0;
> +            ret = -1;
>               break;
>           }
>   
> @@ -436,18 +441,22 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
>                   }
>               }
>   
> -            ret = ((privs & *allowed_privs) == privs);
> +            if ((privs & *allowed_privs) == privs) {
> +                ret = i;
> +            }
>               break;
>           }
>       }
>   
>       /* No rule matched */
>       if (ret == -1) {
> -        return pmp_hart_has_privs_default(env, addr, size, privs,
> -                                          allowed_privs, mode);
> +        if (pmp_hart_has_privs_default(env, addr, size, privs,
> +                                       allowed_privs, mode)) {
> +            ret = MAX_RISCV_PMPS;
> +        }
>       }
>   
> -    return ret == 1 ? true : false;
> +    return ret;
>   }
>   
>   /*
> @@ -586,64 +595,25 @@ target_ulong mseccfg_csr_read(CPURISCVState *env)
>    * Calculate the TLB size if the start address or the end address of
>    * PMP entry is presented in the TLB page.
>    */
> -static target_ulong pmp_get_tlb_size(CPURISCVState *env, int pmp_index,
> -                                     target_ulong tlb_sa, target_ulong tlb_ea)
> +target_ulong pmp_get_tlb_size(CPURISCVState *env, int pmp_index,
> +                              target_ulong tlb_sa, target_ulong tlb_ea)
>   {
>       target_ulong pmp_sa = env->pmp_state.addr[pmp_index].sa;
>       target_ulong pmp_ea = env->pmp_state.addr[pmp_index].ea;
>   
> -    if (pmp_sa >= tlb_sa && pmp_ea <= tlb_ea) {
> -        return pmp_ea - pmp_sa + 1;
> -    }
> -
> -    if (pmp_sa >= tlb_sa && pmp_sa <= tlb_ea && pmp_ea >= tlb_ea) {
> -        return tlb_ea - pmp_sa + 1;
> -    }
> -
> -    if (pmp_ea <= tlb_ea && pmp_ea >= tlb_sa && pmp_sa <= tlb_sa) {
> -        return pmp_ea - tlb_sa + 1;
> -    }
> -
> -    return 0;
> -}
> -
> -/*
> - * Check is there a PMP entry which range covers this page. If so,
> - * try to find the minimum granularity for the TLB size.
> - */
> -bool pmp_is_range_in_tlb(CPURISCVState *env, hwaddr tlb_sa,
> -                         target_ulong *tlb_size)
> -{
> -    int i;
> -    target_ulong val;
> -    target_ulong tlb_ea = (tlb_sa + TARGET_PAGE_SIZE - 1);
> -
> -    for (i = 0; i < MAX_RISCV_PMPS; i++) {
> -        val = pmp_get_tlb_size(env, i, tlb_sa, tlb_ea);
> -        if (val) {
> -            if (*tlb_size == 0 || *tlb_size > val) {
> -                *tlb_size = val;
> -            }
> -        }
> -    }
> -
> -    if (*tlb_size != 0) {
> +    if (pmp_sa <= tlb_sa && pmp_ea >= tlb_ea) {
> +        return TARGET_PAGE_SIZE;
> +    } else {
>           /*
> -         * At this point we have a tlb_size that is the smallest possible size
> -         * That fits within a TARGET_PAGE_SIZE and the PMP region.
> -         *
> -         * If the size is less then TARGET_PAGE_SIZE we drop the size to 1.
> -         * This means the result isn't cached in the TLB and is only used for
> -         * a single translation.
> -         */
> -        if (*tlb_size < TARGET_PAGE_SIZE) {
> -            *tlb_size = 1;
> -        }
> -
> -        return true;
> +        * At this point we have a tlb_size that is the smallest possible size
> +        * That fits within a TARGET_PAGE_SIZE and the PMP region.
> +        *
> +        * If the size is less then TARGET_PAGE_SIZE we drop the size to 1.
> +        * This means the result isn't cached in the TLB and is only used for
> +        * a single translation.
> +        */
> +        return 1;
>       }
> -
> -    return false;
>   }
>   
>   /*
> diff --git a/target/riscv/pmp.h b/target/riscv/pmp.h
> index a8dd797476..da32c61c85 100644
> --- a/target/riscv/pmp.h
> +++ b/target/riscv/pmp.h
> @@ -72,11 +72,11 @@ target_ulong mseccfg_csr_read(CPURISCVState *env);
>   void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index,
>       target_ulong val);
>   target_ulong pmpaddr_csr_read(CPURISCVState *env, uint32_t addr_index);
> -bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
> +int pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
>       target_ulong size, pmp_priv_t privs, pmp_priv_t *allowed_privs,
>       target_ulong mode);
> -bool pmp_is_range_in_tlb(CPURISCVState *env, hwaddr tlb_sa,
> -                         target_ulong *tlb_size);
> +target_ulong pmp_get_tlb_size(CPURISCVState *env, int pmp_index,
> +                              target_ulong tlb_sa, target_ulong tlb_ea);
>   void pmp_update_rule_addr(CPURISCVState *env, uint32_t pmp_index);
>   void pmp_update_rule_nums(CPURISCVState *env);
>   uint32_t pmp_get_num_rules(CPURISCVState *env);
Alistair Francis Oct. 24, 2022, 2:32 a.m. UTC | #2
On Wed, Oct 12, 2022 at 4:01 PM LIU Zhiwei <zhiwei_liu@linux.alibaba.com> wrote:
>
> Only the pmp index that be checked by pmp_hart_has_privs can be used
> by pmp_get_tlb_size to avoid an error pmp index.
>
> Before modification, we may use an error pmp index. For example,
> we check address 0x4fc, and the size 0x4 in pmp_hart_has_privs. If there
> is an pmp rule, valid range is [0x4fc, 0x500), then pmp_hart_has_privs
> will return true;
>
> However, this checked pmp index is discarded as pmp_hart_has_privs
> return bool value. In pmp_is_range_in_tlb, it will traverse all pmp
> rules. The tlb_sa will be 0x0, and tlb_ea will be 0xfff. If there is
> a pmp rule [0x10, 0x14), it will be misused as it is legal in
> pmp_get_tlb_size.
>
> As we have already known the correct pmp index, just remove the
> remove the pmp_is_range_in_tlb and get tlb size directly from
> pmp_get_tlb_size.
>
> Signed-off-by: LIU Zhiwei <zhiwei_liu@linux.alibaba.com>

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

Alistair

> ---
>  target/riscv/cpu_helper.c | 16 ++++---
>  target/riscv/pmp.c        | 90 +++++++++++++--------------------------
>  target/riscv/pmp.h        |  6 +--
>  3 files changed, 42 insertions(+), 70 deletions(-)
>
> diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
> index 278d163803..5d66246c2c 100644
> --- a/target/riscv/cpu_helper.c
> +++ b/target/riscv/cpu_helper.c
> @@ -706,24 +706,26 @@ static int get_physical_address_pmp(CPURISCVState *env, int *prot,
>                                      int mode)
>  {
>      pmp_priv_t pmp_priv;
> -    target_ulong tlb_size_pmp = 0;
> +    int pmp_index = -1;
>
>      if (!riscv_feature(env, RISCV_FEATURE_PMP)) {
>          *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
>          return TRANSLATE_SUCCESS;
>      }
>
> -    if (!pmp_hart_has_privs(env, addr, size, 1 << access_type, &pmp_priv,
> -                            mode)) {
> +    pmp_index = pmp_hart_has_privs(env, addr, size, 1 << access_type,
> +                                   &pmp_priv, mode);
> +    if (pmp_index < 0) {
>          *prot = 0;
>          return TRANSLATE_PMP_FAIL;
>      }
>
>      *prot = pmp_priv_to_page_prot(pmp_priv);
> -    if (tlb_size != NULL) {
> -        if (pmp_is_range_in_tlb(env, addr & ~(*tlb_size - 1), &tlb_size_pmp)) {
> -            *tlb_size = tlb_size_pmp;
> -        }
> +    if ((tlb_size != NULL) && pmp_index != MAX_RISCV_PMPS) {
> +        target_ulong tlb_sa = addr & ~(TARGET_PAGE_SIZE - 1);
> +        target_ulong tlb_ea = tlb_sa + TARGET_PAGE_SIZE - 1;
> +
> +        *tlb_size = pmp_get_tlb_size(env, pmp_index, tlb_sa, tlb_ea);
>      }
>
>      return TRANSLATE_SUCCESS;
> diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c
> index 2b43e399b8..d1126a6066 100644
> --- a/target/riscv/pmp.c
> +++ b/target/riscv/pmp.c
> @@ -292,8 +292,11 @@ static bool pmp_hart_has_privs_default(CPURISCVState *env, target_ulong addr,
>
>  /*
>   * Check if the address has required RWX privs to complete desired operation
> + * Return PMP rule index if a pmp rule match
> + * Return MAX_RISCV_PMPS if default match
> + * Return negtive value if no match
>   */
> -bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
> +int pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
>      target_ulong size, pmp_priv_t privs, pmp_priv_t *allowed_privs,
>      target_ulong mode)
>  {
> @@ -305,8 +308,10 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
>
>      /* Short cut if no rules */
>      if (0 == pmp_get_num_rules(env)) {
> -        return pmp_hart_has_privs_default(env, addr, size, privs,
> -                                          allowed_privs, mode);
> +        if (pmp_hart_has_privs_default(env, addr, size, privs,
> +                                       allowed_privs, mode)) {
> +            ret = MAX_RISCV_PMPS;
> +        }
>      }
>
>      if (size == 0) {
> @@ -333,7 +338,7 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
>          if ((s + e) == 1) {
>              qemu_log_mask(LOG_GUEST_ERROR,
>                            "pmp violation - access is partially inside\n");
> -            ret = 0;
> +            ret = -1;
>              break;
>          }
>
> @@ -436,18 +441,22 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
>                  }
>              }
>
> -            ret = ((privs & *allowed_privs) == privs);
> +            if ((privs & *allowed_privs) == privs) {
> +                ret = i;
> +            }
>              break;
>          }
>      }
>
>      /* No rule matched */
>      if (ret == -1) {
> -        return pmp_hart_has_privs_default(env, addr, size, privs,
> -                                          allowed_privs, mode);
> +        if (pmp_hart_has_privs_default(env, addr, size, privs,
> +                                       allowed_privs, mode)) {
> +            ret = MAX_RISCV_PMPS;
> +        }
>      }
>
> -    return ret == 1 ? true : false;
> +    return ret;
>  }
>
>  /*
> @@ -586,64 +595,25 @@ target_ulong mseccfg_csr_read(CPURISCVState *env)
>   * Calculate the TLB size if the start address or the end address of
>   * PMP entry is presented in the TLB page.
>   */
> -static target_ulong pmp_get_tlb_size(CPURISCVState *env, int pmp_index,
> -                                     target_ulong tlb_sa, target_ulong tlb_ea)
> +target_ulong pmp_get_tlb_size(CPURISCVState *env, int pmp_index,
> +                              target_ulong tlb_sa, target_ulong tlb_ea)
>  {
>      target_ulong pmp_sa = env->pmp_state.addr[pmp_index].sa;
>      target_ulong pmp_ea = env->pmp_state.addr[pmp_index].ea;
>
> -    if (pmp_sa >= tlb_sa && pmp_ea <= tlb_ea) {
> -        return pmp_ea - pmp_sa + 1;
> -    }
> -
> -    if (pmp_sa >= tlb_sa && pmp_sa <= tlb_ea && pmp_ea >= tlb_ea) {
> -        return tlb_ea - pmp_sa + 1;
> -    }
> -
> -    if (pmp_ea <= tlb_ea && pmp_ea >= tlb_sa && pmp_sa <= tlb_sa) {
> -        return pmp_ea - tlb_sa + 1;
> -    }
> -
> -    return 0;
> -}
> -
> -/*
> - * Check is there a PMP entry which range covers this page. If so,
> - * try to find the minimum granularity for the TLB size.
> - */
> -bool pmp_is_range_in_tlb(CPURISCVState *env, hwaddr tlb_sa,
> -                         target_ulong *tlb_size)
> -{
> -    int i;
> -    target_ulong val;
> -    target_ulong tlb_ea = (tlb_sa + TARGET_PAGE_SIZE - 1);
> -
> -    for (i = 0; i < MAX_RISCV_PMPS; i++) {
> -        val = pmp_get_tlb_size(env, i, tlb_sa, tlb_ea);
> -        if (val) {
> -            if (*tlb_size == 0 || *tlb_size > val) {
> -                *tlb_size = val;
> -            }
> -        }
> -    }
> -
> -    if (*tlb_size != 0) {
> +    if (pmp_sa <= tlb_sa && pmp_ea >= tlb_ea) {
> +        return TARGET_PAGE_SIZE;
> +    } else {
>          /*
> -         * At this point we have a tlb_size that is the smallest possible size
> -         * That fits within a TARGET_PAGE_SIZE and the PMP region.
> -         *
> -         * If the size is less then TARGET_PAGE_SIZE we drop the size to 1.
> -         * This means the result isn't cached in the TLB and is only used for
> -         * a single translation.
> -         */
> -        if (*tlb_size < TARGET_PAGE_SIZE) {
> -            *tlb_size = 1;
> -        }
> -
> -        return true;
> +        * At this point we have a tlb_size that is the smallest possible size
> +        * That fits within a TARGET_PAGE_SIZE and the PMP region.
> +        *
> +        * If the size is less then TARGET_PAGE_SIZE we drop the size to 1.
> +        * This means the result isn't cached in the TLB and is only used for
> +        * a single translation.
> +        */
> +        return 1;
>      }
> -
> -    return false;
>  }
>
>  /*
> diff --git a/target/riscv/pmp.h b/target/riscv/pmp.h
> index a8dd797476..da32c61c85 100644
> --- a/target/riscv/pmp.h
> +++ b/target/riscv/pmp.h
> @@ -72,11 +72,11 @@ target_ulong mseccfg_csr_read(CPURISCVState *env);
>  void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index,
>      target_ulong val);
>  target_ulong pmpaddr_csr_read(CPURISCVState *env, uint32_t addr_index);
> -bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
> +int pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
>      target_ulong size, pmp_priv_t privs, pmp_priv_t *allowed_privs,
>      target_ulong mode);
> -bool pmp_is_range_in_tlb(CPURISCVState *env, hwaddr tlb_sa,
> -                         target_ulong *tlb_size);
> +target_ulong pmp_get_tlb_size(CPURISCVState *env, int pmp_index,
> +                              target_ulong tlb_sa, target_ulong tlb_ea);
>  void pmp_update_rule_addr(CPURISCVState *env, uint32_t pmp_index);
>  void pmp_update_rule_nums(CPURISCVState *env);
>  uint32_t pmp_get_num_rules(CPURISCVState *env);
> --
> 2.25.1
>
>
Alistair Francis Oct. 24, 2022, 3:16 a.m. UTC | #3
On Wed, Oct 12, 2022 at 4:01 PM LIU Zhiwei <zhiwei_liu@linux.alibaba.com> wrote:
>
> Only the pmp index that be checked by pmp_hart_has_privs can be used
> by pmp_get_tlb_size to avoid an error pmp index.
>
> Before modification, we may use an error pmp index. For example,
> we check address 0x4fc, and the size 0x4 in pmp_hart_has_privs. If there
> is an pmp rule, valid range is [0x4fc, 0x500), then pmp_hart_has_privs
> will return true;
>
> However, this checked pmp index is discarded as pmp_hart_has_privs
> return bool value. In pmp_is_range_in_tlb, it will traverse all pmp
> rules. The tlb_sa will be 0x0, and tlb_ea will be 0xfff. If there is
> a pmp rule [0x10, 0x14), it will be misused as it is legal in
> pmp_get_tlb_size.
>
> As we have already known the correct pmp index, just remove the
> remove the pmp_is_range_in_tlb and get tlb size directly from
> pmp_get_tlb_size.
>
> Signed-off-by: LIU Zhiwei <zhiwei_liu@linux.alibaba.com>

Thanks!

Applied to riscv-to-apply.next

Alistair

> ---
>  target/riscv/cpu_helper.c | 16 ++++---
>  target/riscv/pmp.c        | 90 +++++++++++++--------------------------
>  target/riscv/pmp.h        |  6 +--
>  3 files changed, 42 insertions(+), 70 deletions(-)
>
> diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
> index 278d163803..5d66246c2c 100644
> --- a/target/riscv/cpu_helper.c
> +++ b/target/riscv/cpu_helper.c
> @@ -706,24 +706,26 @@ static int get_physical_address_pmp(CPURISCVState *env, int *prot,
>                                      int mode)
>  {
>      pmp_priv_t pmp_priv;
> -    target_ulong tlb_size_pmp = 0;
> +    int pmp_index = -1;
>
>      if (!riscv_feature(env, RISCV_FEATURE_PMP)) {
>          *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
>          return TRANSLATE_SUCCESS;
>      }
>
> -    if (!pmp_hart_has_privs(env, addr, size, 1 << access_type, &pmp_priv,
> -                            mode)) {
> +    pmp_index = pmp_hart_has_privs(env, addr, size, 1 << access_type,
> +                                   &pmp_priv, mode);
> +    if (pmp_index < 0) {
>          *prot = 0;
>          return TRANSLATE_PMP_FAIL;
>      }
>
>      *prot = pmp_priv_to_page_prot(pmp_priv);
> -    if (tlb_size != NULL) {
> -        if (pmp_is_range_in_tlb(env, addr & ~(*tlb_size - 1), &tlb_size_pmp)) {
> -            *tlb_size = tlb_size_pmp;
> -        }
> +    if ((tlb_size != NULL) && pmp_index != MAX_RISCV_PMPS) {
> +        target_ulong tlb_sa = addr & ~(TARGET_PAGE_SIZE - 1);
> +        target_ulong tlb_ea = tlb_sa + TARGET_PAGE_SIZE - 1;
> +
> +        *tlb_size = pmp_get_tlb_size(env, pmp_index, tlb_sa, tlb_ea);
>      }
>
>      return TRANSLATE_SUCCESS;
> diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c
> index 2b43e399b8..d1126a6066 100644
> --- a/target/riscv/pmp.c
> +++ b/target/riscv/pmp.c
> @@ -292,8 +292,11 @@ static bool pmp_hart_has_privs_default(CPURISCVState *env, target_ulong addr,
>
>  /*
>   * Check if the address has required RWX privs to complete desired operation
> + * Return PMP rule index if a pmp rule match
> + * Return MAX_RISCV_PMPS if default match
> + * Return negtive value if no match
>   */
> -bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
> +int pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
>      target_ulong size, pmp_priv_t privs, pmp_priv_t *allowed_privs,
>      target_ulong mode)
>  {
> @@ -305,8 +308,10 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
>
>      /* Short cut if no rules */
>      if (0 == pmp_get_num_rules(env)) {
> -        return pmp_hart_has_privs_default(env, addr, size, privs,
> -                                          allowed_privs, mode);
> +        if (pmp_hart_has_privs_default(env, addr, size, privs,
> +                                       allowed_privs, mode)) {
> +            ret = MAX_RISCV_PMPS;
> +        }
>      }
>
>      if (size == 0) {
> @@ -333,7 +338,7 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
>          if ((s + e) == 1) {
>              qemu_log_mask(LOG_GUEST_ERROR,
>                            "pmp violation - access is partially inside\n");
> -            ret = 0;
> +            ret = -1;
>              break;
>          }
>
> @@ -436,18 +441,22 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
>                  }
>              }
>
> -            ret = ((privs & *allowed_privs) == privs);
> +            if ((privs & *allowed_privs) == privs) {
> +                ret = i;
> +            }
>              break;
>          }
>      }
>
>      /* No rule matched */
>      if (ret == -1) {
> -        return pmp_hart_has_privs_default(env, addr, size, privs,
> -                                          allowed_privs, mode);
> +        if (pmp_hart_has_privs_default(env, addr, size, privs,
> +                                       allowed_privs, mode)) {
> +            ret = MAX_RISCV_PMPS;
> +        }
>      }
>
> -    return ret == 1 ? true : false;
> +    return ret;
>  }
>
>  /*
> @@ -586,64 +595,25 @@ target_ulong mseccfg_csr_read(CPURISCVState *env)
>   * Calculate the TLB size if the start address or the end address of
>   * PMP entry is presented in the TLB page.
>   */
> -static target_ulong pmp_get_tlb_size(CPURISCVState *env, int pmp_index,
> -                                     target_ulong tlb_sa, target_ulong tlb_ea)
> +target_ulong pmp_get_tlb_size(CPURISCVState *env, int pmp_index,
> +                              target_ulong tlb_sa, target_ulong tlb_ea)
>  {
>      target_ulong pmp_sa = env->pmp_state.addr[pmp_index].sa;
>      target_ulong pmp_ea = env->pmp_state.addr[pmp_index].ea;
>
> -    if (pmp_sa >= tlb_sa && pmp_ea <= tlb_ea) {
> -        return pmp_ea - pmp_sa + 1;
> -    }
> -
> -    if (pmp_sa >= tlb_sa && pmp_sa <= tlb_ea && pmp_ea >= tlb_ea) {
> -        return tlb_ea - pmp_sa + 1;
> -    }
> -
> -    if (pmp_ea <= tlb_ea && pmp_ea >= tlb_sa && pmp_sa <= tlb_sa) {
> -        return pmp_ea - tlb_sa + 1;
> -    }
> -
> -    return 0;
> -}
> -
> -/*
> - * Check is there a PMP entry which range covers this page. If so,
> - * try to find the minimum granularity for the TLB size.
> - */
> -bool pmp_is_range_in_tlb(CPURISCVState *env, hwaddr tlb_sa,
> -                         target_ulong *tlb_size)
> -{
> -    int i;
> -    target_ulong val;
> -    target_ulong tlb_ea = (tlb_sa + TARGET_PAGE_SIZE - 1);
> -
> -    for (i = 0; i < MAX_RISCV_PMPS; i++) {
> -        val = pmp_get_tlb_size(env, i, tlb_sa, tlb_ea);
> -        if (val) {
> -            if (*tlb_size == 0 || *tlb_size > val) {
> -                *tlb_size = val;
> -            }
> -        }
> -    }
> -
> -    if (*tlb_size != 0) {
> +    if (pmp_sa <= tlb_sa && pmp_ea >= tlb_ea) {
> +        return TARGET_PAGE_SIZE;
> +    } else {
>          /*
> -         * At this point we have a tlb_size that is the smallest possible size
> -         * That fits within a TARGET_PAGE_SIZE and the PMP region.
> -         *
> -         * If the size is less then TARGET_PAGE_SIZE we drop the size to 1.
> -         * This means the result isn't cached in the TLB and is only used for
> -         * a single translation.
> -         */
> -        if (*tlb_size < TARGET_PAGE_SIZE) {
> -            *tlb_size = 1;
> -        }
> -
> -        return true;
> +        * At this point we have a tlb_size that is the smallest possible size
> +        * That fits within a TARGET_PAGE_SIZE and the PMP region.
> +        *
> +        * If the size is less then TARGET_PAGE_SIZE we drop the size to 1.
> +        * This means the result isn't cached in the TLB and is only used for
> +        * a single translation.
> +        */
> +        return 1;
>      }
> -
> -    return false;
>  }
>
>  /*
> diff --git a/target/riscv/pmp.h b/target/riscv/pmp.h
> index a8dd797476..da32c61c85 100644
> --- a/target/riscv/pmp.h
> +++ b/target/riscv/pmp.h
> @@ -72,11 +72,11 @@ target_ulong mseccfg_csr_read(CPURISCVState *env);
>  void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index,
>      target_ulong val);
>  target_ulong pmpaddr_csr_read(CPURISCVState *env, uint32_t addr_index);
> -bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
> +int pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
>      target_ulong size, pmp_priv_t privs, pmp_priv_t *allowed_privs,
>      target_ulong mode);
> -bool pmp_is_range_in_tlb(CPURISCVState *env, hwaddr tlb_sa,
> -                         target_ulong *tlb_size);
> +target_ulong pmp_get_tlb_size(CPURISCVState *env, int pmp_index,
> +                              target_ulong tlb_sa, target_ulong tlb_ea);
>  void pmp_update_rule_addr(CPURISCVState *env, uint32_t pmp_index);
>  void pmp_update_rule_nums(CPURISCVState *env);
>  uint32_t pmp_get_num_rules(CPURISCVState *env);
> --
> 2.25.1
>
>
diff mbox series

Patch

diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index 278d163803..5d66246c2c 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -706,24 +706,26 @@  static int get_physical_address_pmp(CPURISCVState *env, int *prot,
                                     int mode)
 {
     pmp_priv_t pmp_priv;
-    target_ulong tlb_size_pmp = 0;
+    int pmp_index = -1;
 
     if (!riscv_feature(env, RISCV_FEATURE_PMP)) {
         *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
         return TRANSLATE_SUCCESS;
     }
 
-    if (!pmp_hart_has_privs(env, addr, size, 1 << access_type, &pmp_priv,
-                            mode)) {
+    pmp_index = pmp_hart_has_privs(env, addr, size, 1 << access_type,
+                                   &pmp_priv, mode);
+    if (pmp_index < 0) {
         *prot = 0;
         return TRANSLATE_PMP_FAIL;
     }
 
     *prot = pmp_priv_to_page_prot(pmp_priv);
-    if (tlb_size != NULL) {
-        if (pmp_is_range_in_tlb(env, addr & ~(*tlb_size - 1), &tlb_size_pmp)) {
-            *tlb_size = tlb_size_pmp;
-        }
+    if ((tlb_size != NULL) && pmp_index != MAX_RISCV_PMPS) {
+        target_ulong tlb_sa = addr & ~(TARGET_PAGE_SIZE - 1);
+        target_ulong tlb_ea = tlb_sa + TARGET_PAGE_SIZE - 1;
+
+        *tlb_size = pmp_get_tlb_size(env, pmp_index, tlb_sa, tlb_ea);
     }
 
     return TRANSLATE_SUCCESS;
diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c
index 2b43e399b8..d1126a6066 100644
--- a/target/riscv/pmp.c
+++ b/target/riscv/pmp.c
@@ -292,8 +292,11 @@  static bool pmp_hart_has_privs_default(CPURISCVState *env, target_ulong addr,
 
 /*
  * Check if the address has required RWX privs to complete desired operation
+ * Return PMP rule index if a pmp rule match
+ * Return MAX_RISCV_PMPS if default match
+ * Return negtive value if no match
  */
-bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
+int pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
     target_ulong size, pmp_priv_t privs, pmp_priv_t *allowed_privs,
     target_ulong mode)
 {
@@ -305,8 +308,10 @@  bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
 
     /* Short cut if no rules */
     if (0 == pmp_get_num_rules(env)) {
-        return pmp_hart_has_privs_default(env, addr, size, privs,
-                                          allowed_privs, mode);
+        if (pmp_hart_has_privs_default(env, addr, size, privs,
+                                       allowed_privs, mode)) {
+            ret = MAX_RISCV_PMPS;
+        }
     }
 
     if (size == 0) {
@@ -333,7 +338,7 @@  bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
         if ((s + e) == 1) {
             qemu_log_mask(LOG_GUEST_ERROR,
                           "pmp violation - access is partially inside\n");
-            ret = 0;
+            ret = -1;
             break;
         }
 
@@ -436,18 +441,22 @@  bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
                 }
             }
 
-            ret = ((privs & *allowed_privs) == privs);
+            if ((privs & *allowed_privs) == privs) {
+                ret = i;
+            }
             break;
         }
     }
 
     /* No rule matched */
     if (ret == -1) {
-        return pmp_hart_has_privs_default(env, addr, size, privs,
-                                          allowed_privs, mode);
+        if (pmp_hart_has_privs_default(env, addr, size, privs,
+                                       allowed_privs, mode)) {
+            ret = MAX_RISCV_PMPS;
+        }
     }
 
-    return ret == 1 ? true : false;
+    return ret;
 }
 
 /*
@@ -586,64 +595,25 @@  target_ulong mseccfg_csr_read(CPURISCVState *env)
  * Calculate the TLB size if the start address or the end address of
  * PMP entry is presented in the TLB page.
  */
-static target_ulong pmp_get_tlb_size(CPURISCVState *env, int pmp_index,
-                                     target_ulong tlb_sa, target_ulong tlb_ea)
+target_ulong pmp_get_tlb_size(CPURISCVState *env, int pmp_index,
+                              target_ulong tlb_sa, target_ulong tlb_ea)
 {
     target_ulong pmp_sa = env->pmp_state.addr[pmp_index].sa;
     target_ulong pmp_ea = env->pmp_state.addr[pmp_index].ea;
 
-    if (pmp_sa >= tlb_sa && pmp_ea <= tlb_ea) {
-        return pmp_ea - pmp_sa + 1;
-    }
-
-    if (pmp_sa >= tlb_sa && pmp_sa <= tlb_ea && pmp_ea >= tlb_ea) {
-        return tlb_ea - pmp_sa + 1;
-    }
-
-    if (pmp_ea <= tlb_ea && pmp_ea >= tlb_sa && pmp_sa <= tlb_sa) {
-        return pmp_ea - tlb_sa + 1;
-    }
-
-    return 0;
-}
-
-/*
- * Check is there a PMP entry which range covers this page. If so,
- * try to find the minimum granularity for the TLB size.
- */
-bool pmp_is_range_in_tlb(CPURISCVState *env, hwaddr tlb_sa,
-                         target_ulong *tlb_size)
-{
-    int i;
-    target_ulong val;
-    target_ulong tlb_ea = (tlb_sa + TARGET_PAGE_SIZE - 1);
-
-    for (i = 0; i < MAX_RISCV_PMPS; i++) {
-        val = pmp_get_tlb_size(env, i, tlb_sa, tlb_ea);
-        if (val) {
-            if (*tlb_size == 0 || *tlb_size > val) {
-                *tlb_size = val;
-            }
-        }
-    }
-
-    if (*tlb_size != 0) {
+    if (pmp_sa <= tlb_sa && pmp_ea >= tlb_ea) {
+        return TARGET_PAGE_SIZE;
+    } else {
         /*
-         * At this point we have a tlb_size that is the smallest possible size
-         * That fits within a TARGET_PAGE_SIZE and the PMP region.
-         *
-         * If the size is less then TARGET_PAGE_SIZE we drop the size to 1.
-         * This means the result isn't cached in the TLB and is only used for
-         * a single translation.
-         */
-        if (*tlb_size < TARGET_PAGE_SIZE) {
-            *tlb_size = 1;
-        }
-
-        return true;
+        * At this point we have a tlb_size that is the smallest possible size
+        * That fits within a TARGET_PAGE_SIZE and the PMP region.
+        *
+        * If the size is less then TARGET_PAGE_SIZE we drop the size to 1.
+        * This means the result isn't cached in the TLB and is only used for
+        * a single translation.
+        */
+        return 1;
     }
-
-    return false;
 }
 
 /*
diff --git a/target/riscv/pmp.h b/target/riscv/pmp.h
index a8dd797476..da32c61c85 100644
--- a/target/riscv/pmp.h
+++ b/target/riscv/pmp.h
@@ -72,11 +72,11 @@  target_ulong mseccfg_csr_read(CPURISCVState *env);
 void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index,
     target_ulong val);
 target_ulong pmpaddr_csr_read(CPURISCVState *env, uint32_t addr_index);
-bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
+int pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
     target_ulong size, pmp_priv_t privs, pmp_priv_t *allowed_privs,
     target_ulong mode);
-bool pmp_is_range_in_tlb(CPURISCVState *env, hwaddr tlb_sa,
-                         target_ulong *tlb_size);
+target_ulong pmp_get_tlb_size(CPURISCVState *env, int pmp_index,
+                              target_ulong tlb_sa, target_ulong tlb_ea);
 void pmp_update_rule_addr(CPURISCVState *env, uint32_t pmp_index);
 void pmp_update_rule_nums(CPURISCVState *env);
 uint32_t pmp_get_num_rules(CPURISCVState *env);