pmp_hart_has_privs decides the permissions of a byte range by testing
only the two endpoint bytes against each PMP entry. An active entry
lying strictly between the endpoints matches neither byte and is
skipped, so a lower-priority entry silently grants an access that the
higher-priority entry must deny.

Replace the endpoint sampling with interval tests, mirroring the
predicate pmp_get_tlb_size already uses.

Signed-off-by: Max Chou <[email protected]>
---
 target/riscv/tcg/pmp.c | 31 ++++++++++---------------------
 1 file changed, 10 insertions(+), 21 deletions(-)

diff --git a/target/riscv/tcg/pmp.c b/target/riscv/tcg/pmp.c
index 41b55519a8e..94224920d8d 100644
--- a/target/riscv/tcg/pmp.c
+++ b/target/riscv/tcg/pmp.c
@@ -299,20 +299,6 @@ void pmp_update_rule_nums(CPURISCVState *env)
     }
 }
 
-static int pmp_is_in_range(CPURISCVState *env, int pmp_index, hwaddr addr)
-{
-    int result = 0;
-
-    if ((addr >= env->pmp_state.addr[pmp_index].sa) &&
-        (addr <= env->pmp_state.addr[pmp_index].ea)) {
-        result = 1;
-    } else {
-        result = 0;
-    }
-
-    return result;
-}
-
 /*
  * Check if the address has required RWX privs when no PMP entry is matched.
  */
@@ -387,8 +373,8 @@ bool pmp_hart_has_privs(CPURISCVState *env, hwaddr addr,
 {
     int i = 0;
     int pmp_size = 0;
-    hwaddr s = 0;
-    hwaddr e = 0;
+    hwaddr last = 0;
+    bool size_known = size != 0;
     uint8_t pmp_regions = riscv_cpu_cfg(env)->pmp_regions;
 
     /* Short cut if no rules */
@@ -414,12 +400,15 @@ bool pmp_hart_has_privs(CPURISCVState *env, hwaddr addr,
      * 1.10 draft priv spec states there is an implicit order
      * from low to high
      */
+    last = addr + pmp_size - 1;
+
     for (i = 0; i < pmp_regions; i++) {
-        s = pmp_is_in_range(env, i, addr);
-        e = pmp_is_in_range(env, i, addr + pmp_size - 1);
+        hwaddr sa = env->pmp_state.addr[i].sa;
+        hwaddr ea = env->pmp_state.addr[i].ea;
+        bool contains = (sa <= addr) && (last <= ea);
+        bool overlaps = (addr <= ea) && (sa <= last);
 
-        /* partially inside */
-        if ((s + e) == 1) {
+        if (size_known && overlaps && !contains) {
             qemu_log_mask(LOG_GUEST_ERROR,
                           "pmp violation - access is partially inside\n");
             *allowed_privs = 0;
@@ -430,7 +419,7 @@ bool pmp_hart_has_privs(CPURISCVState *env, hwaddr addr,
         const uint8_t a_field =
             pmp_get_a_field(env->pmp_state.pmp[i].cfg_reg);
 
-        if (((s + e) == 2) && (PMP_AMATCH_OFF != a_field)) {
+        if (contains && (PMP_AMATCH_OFF != a_field)) {
             /*
              * If the PMP entry is not off and the address is in range,
              * do the priv check
-- 
2.43.0


Reply via email to