diff mbox series

[v3,1/7] target/riscv: Update pmp_get_tlb_size()

Message ID 20230419032725.29721-2-liweiwei@iscas.ac.cn (mailing list archive)
State New, archived
Headers show
Series target/riscv: Fix PMP related problem | expand

Commit Message

Weiwei Li April 19, 2023, 3:27 a.m. UTC
PMP entries before the matched PMP entry(including the matched PMP entry)
may overlap partial of the tlb page, which may make different regions in
that page have different permission rights, such as for
PMP0(0x80000008~0x8000000F, R) and PMP1(0x80001000~0x80001FFF, RWX))
write access to 0x80000000 will match PMP1. However we cannot cache the tlb
for it since this will make the write access to 0x80000008 bypass the check
of PMP0. So we should check all of them and set the tlb size to 1 in this
case.

Signed-off-by: Weiwei Li <liweiwei@iscas.ac.cn>
Signed-off-by: Junqiang Wang <wangjunqiang@iscas.ac.cn>
---
 target/riscv/cpu_helper.c |  7 ++-----
 target/riscv/pmp.c        | 39 ++++++++++++++++++++++++++-------------
 target/riscv/pmp.h        |  3 +--
 3 files changed, 29 insertions(+), 20 deletions(-)

Comments

LIU Zhiwei April 20, 2023, 11:58 a.m. UTC | #1
On 2023/4/19 11:27, Weiwei Li wrote:
> PMP entries before the matched PMP entry(including the matched PMP entry)
> may overlap partial of the tlb page, which may make different regions in
> that page have different permission rights, such as for
> PMP0(0x80000008~0x8000000F, R) and PMP1(0x80001000~0x80001FFF, RWX))
> write access to 0x80000000 will match PMP1. However we cannot cache the tlb
> for it since this will make the write access to 0x80000008 bypass the check
> of PMP0. So we should check all of them and set the tlb size to 1 in this
> case.
>
> Signed-off-by: Weiwei Li <liweiwei@iscas.ac.cn>
> Signed-off-by: Junqiang Wang <wangjunqiang@iscas.ac.cn>
> ---
>   target/riscv/cpu_helper.c |  7 ++-----
>   target/riscv/pmp.c        | 39 ++++++++++++++++++++++++++-------------
>   target/riscv/pmp.h        |  3 +--
>   3 files changed, 29 insertions(+), 20 deletions(-)
>
> diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
> index 433ea529b0..075fc0538a 100644
> --- a/target/riscv/cpu_helper.c
> +++ b/target/riscv/cpu_helper.c
> @@ -703,11 +703,8 @@ static int get_physical_address_pmp(CPURISCVState *env, int *prot,
>       }
>   
>       *prot = pmp_priv_to_page_prot(pmp_priv);
> -    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);
> +    if (tlb_size != NULL) {
> +        *tlb_size = pmp_get_tlb_size(env, addr);
>       }
>   
>       return TRANSLATE_SUCCESS;
> diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c
> index 1f5aca42e8..22f3b3f217 100644
> --- a/target/riscv/pmp.c
> +++ b/target/riscv/pmp.c
> @@ -601,28 +601,41 @@ target_ulong mseccfg_csr_read(CPURISCVState *env)
>   }
>   
>   /*
> - * Calculate the TLB size if the start address or the end address of
> + * Calculate the TLB size if any start address or the end address of
>    * PMP entry is presented in the TLB page.
>    */

  If we don't pass the start address or the end address parameter, we 
can comment this function:

Calculate the TLB size according to the PMP rule with the highest priority.

> -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, target_ulong addr)
>   {
> -    target_ulong pmp_sa = env->pmp_state.addr[pmp_index].sa;
> -    target_ulong pmp_ea = env->pmp_state.addr[pmp_index].ea;
> +    target_ulong pmp_sa;
> +    target_ulong pmp_ea;
> +    target_ulong tlb_sa = addr & ~(TARGET_PAGE_SIZE - 1);
> +    target_ulong tlb_ea = tlb_sa + TARGET_PAGE_SIZE - 1;
> +    int i;
> +
> +    for (i = 0; i < MAX_RISCV_PMPS; i++) {
> +        if (pmp_get_a_field(env->pmp_state.pmp[i].cfg_reg) == PMP_AMATCH_OFF) {
> +            continue;
> +        }
> +
> +        pmp_sa = env->pmp_state.addr[i].sa;
> +        pmp_ea = env->pmp_state.addr[i].ea;
>   
> -    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.
> +         * If any start address or the end address of PMP entry is presented
> +         * in the TLB page and cannot override the whole TLB page 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;
> +        if (pmp_sa <= tlb_sa && pmp_ea >= tlb_ea) {
> +            return TARGET_PAGE_SIZE;
> +        } else if ((pmp_sa >= tlb_sa && pmp_sa <= tlb_ea) ||
> +                   (pmp_ea >= tlb_sa && pmp_ea <= tlb_ea)) {
> +            return 1;
> +        }
>       }
> +
> +    return TARGET_PAGE_SIZE;

This implicitly require a success return from the 
get_physical_address_pmp call. If we want this function to be a 
independent one, we should return 0 to indicate no tlb size can return.

Zhiwei

>   }
>   
>   /*
> diff --git a/target/riscv/pmp.h b/target/riscv/pmp.h
> index b296ea1fc6..0a7e24750b 100644
> --- a/target/riscv/pmp.h
> +++ b/target/riscv/pmp.h
> @@ -76,8 +76,7 @@ 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);
> -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, target_ulong addr);
>   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);
Weiwei Li April 20, 2023, 12:27 p.m. UTC | #2
On 2023/4/20 19:58, LIU Zhiwei wrote:
>
> On 2023/4/19 11:27, Weiwei Li wrote:
>> PMP entries before the matched PMP entry(including the matched PMP 
>> entry)
>> may overlap partial of the tlb page, which may make different regions in
>> that page have different permission rights, such as for
>> PMP0(0x80000008~0x8000000F, R) and PMP1(0x80001000~0x80001FFF, RWX))
>> write access to 0x80000000 will match PMP1. However we cannot cache 
>> the tlb
>> for it since this will make the write access to 0x80000008 bypass the 
>> check
>> of PMP0. So we should check all of them and set the tlb size to 1 in 
>> this
>> case.
>>
>> Signed-off-by: Weiwei Li <liweiwei@iscas.ac.cn>
>> Signed-off-by: Junqiang Wang <wangjunqiang@iscas.ac.cn>
>> ---
>>   target/riscv/cpu_helper.c |  7 ++-----
>>   target/riscv/pmp.c        | 39 ++++++++++++++++++++++++++-------------
>>   target/riscv/pmp.h        |  3 +--
>>   3 files changed, 29 insertions(+), 20 deletions(-)
>>
>> diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
>> index 433ea529b0..075fc0538a 100644
>> --- a/target/riscv/cpu_helper.c
>> +++ b/target/riscv/cpu_helper.c
>> @@ -703,11 +703,8 @@ static int 
>> get_physical_address_pmp(CPURISCVState *env, int *prot,
>>       }
>>         *prot = pmp_priv_to_page_prot(pmp_priv);
>> -    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);
>> +    if (tlb_size != NULL) {
>> +        *tlb_size = pmp_get_tlb_size(env, addr);
>>       }
>>         return TRANSLATE_SUCCESS;
>> diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c
>> index 1f5aca42e8..22f3b3f217 100644
>> --- a/target/riscv/pmp.c
>> +++ b/target/riscv/pmp.c
>> @@ -601,28 +601,41 @@ target_ulong mseccfg_csr_read(CPURISCVState *env)
>>   }
>>     /*
>> - * Calculate the TLB size if the start address or the end address of
>> + * Calculate the TLB size if any start address or the end address of
>>    * PMP entry is presented in the TLB page.
>>    */
>
>  If we don't pass the start address or the end address parameter, we 
> can comment this function:
>
> Calculate the TLB size according to the PMP rule with the highest 
> priority.

This seems  also not accurate. It's the first(highest priority) PMP 
entry that overlap with the tlb page really matters.

Maybe we can modify it to:

"Calculate the TLB size. Return 1 if the first PMP entry that overlaps 
with the TLB page doesn't cover the whole page . Return TARGET_PAGE_SIZE 
otherwise."

>
>> -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, target_ulong addr)
>>   {
>> -    target_ulong pmp_sa = env->pmp_state.addr[pmp_index].sa;
>> -    target_ulong pmp_ea = env->pmp_state.addr[pmp_index].ea;
>> +    target_ulong pmp_sa;
>> +    target_ulong pmp_ea;
>> +    target_ulong tlb_sa = addr & ~(TARGET_PAGE_SIZE - 1);
>> +    target_ulong tlb_ea = tlb_sa + TARGET_PAGE_SIZE - 1;
>> +    int i;
>> +
>> +    for (i = 0; i < MAX_RISCV_PMPS; i++) {
>> +        if (pmp_get_a_field(env->pmp_state.pmp[i].cfg_reg) == 
>> PMP_AMATCH_OFF) {
>> +            continue;
>> +        }
>> +
>> +        pmp_sa = env->pmp_state.addr[i].sa;
>> +        pmp_ea = env->pmp_state.addr[i].ea;
>>   -    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.
>> +         * If any start address or the end address of PMP entry is 
>> presented
>> +         * in the TLB page and cannot override the whole TLB page 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;
>> +        if (pmp_sa <= tlb_sa && pmp_ea >= tlb_ea) {
>> +            return TARGET_PAGE_SIZE;
>> +        } else if ((pmp_sa >= tlb_sa && pmp_sa <= tlb_ea) ||
>> +                   (pmp_ea >= tlb_sa && pmp_ea <= tlb_ea)) {
>> +            return 1;
>> +        }
>>       }
>> +
>> +    return TARGET_PAGE_SIZE;
>
> This implicitly require a success return from the 
> get_physical_address_pmp call. If we want this function to be a 
> independent one, we should return 0 to indicate no tlb size can return.

Yeah. I think the default should be TARGET_PAGE_SIZE if no PMP entry 
overlap with the tlb page, since in this case, tlb_size should be 
TARGET_PAGE_SIZE if the translation(inlcuding PMP check) successes while 
tlb_size will be ignored if the translation fails.

Regards,

Weiwei Li

>
> Zhiwei
>
>>   }
>>     /*
>> diff --git a/target/riscv/pmp.h b/target/riscv/pmp.h
>> index b296ea1fc6..0a7e24750b 100644
>> --- a/target/riscv/pmp.h
>> +++ b/target/riscv/pmp.h
>> @@ -76,8 +76,7 @@ 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);
>> -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, target_ulong addr);
>>   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);
diff mbox series

Patch

diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index 433ea529b0..075fc0538a 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -703,11 +703,8 @@  static int get_physical_address_pmp(CPURISCVState *env, int *prot,
     }
 
     *prot = pmp_priv_to_page_prot(pmp_priv);
-    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);
+    if (tlb_size != NULL) {
+        *tlb_size = pmp_get_tlb_size(env, addr);
     }
 
     return TRANSLATE_SUCCESS;
diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c
index 1f5aca42e8..22f3b3f217 100644
--- a/target/riscv/pmp.c
+++ b/target/riscv/pmp.c
@@ -601,28 +601,41 @@  target_ulong mseccfg_csr_read(CPURISCVState *env)
 }
 
 /*
- * Calculate the TLB size if the start address or the end address of
+ * Calculate the TLB size if any start address or the end address of
  * PMP entry is presented in the TLB page.
  */
-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, target_ulong addr)
 {
-    target_ulong pmp_sa = env->pmp_state.addr[pmp_index].sa;
-    target_ulong pmp_ea = env->pmp_state.addr[pmp_index].ea;
+    target_ulong pmp_sa;
+    target_ulong pmp_ea;
+    target_ulong tlb_sa = addr & ~(TARGET_PAGE_SIZE - 1);
+    target_ulong tlb_ea = tlb_sa + TARGET_PAGE_SIZE - 1;
+    int i;
+
+    for (i = 0; i < MAX_RISCV_PMPS; i++) {
+        if (pmp_get_a_field(env->pmp_state.pmp[i].cfg_reg) == PMP_AMATCH_OFF) {
+            continue;
+        }
+
+        pmp_sa = env->pmp_state.addr[i].sa;
+        pmp_ea = env->pmp_state.addr[i].ea;
 
-    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.
+         * If any start address or the end address of PMP entry is presented
+         * in the TLB page and cannot override the whole TLB page 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;
+        if (pmp_sa <= tlb_sa && pmp_ea >= tlb_ea) {
+            return TARGET_PAGE_SIZE;
+        } else if ((pmp_sa >= tlb_sa && pmp_sa <= tlb_ea) ||
+                   (pmp_ea >= tlb_sa && pmp_ea <= tlb_ea)) {
+            return 1;
+        }
     }
+
+    return TARGET_PAGE_SIZE;
 }
 
 /*
diff --git a/target/riscv/pmp.h b/target/riscv/pmp.h
index b296ea1fc6..0a7e24750b 100644
--- a/target/riscv/pmp.h
+++ b/target/riscv/pmp.h
@@ -76,8 +76,7 @@  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);
-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, target_ulong addr);
 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);