Hi Alistair,

On 4/10/26 9:36 AM, Alistair Francis wrote:
On Thu, Apr 9, 2026 at 12:07 AM LIU Zhiwei <[email protected]> wrote:
This patch introduces the core implementation for the Memory Protection Table
(MPT) walk, which is the central mechanism of the SMMPT extension.

A new file, `riscv_smmpt.c`, is added to encapsulate the MPT logic. It
implements the `smmpt_lookup()` function, which performs a multi-level
page table-like walk starting from the physical address specified in the
`mptppn` CSR field. This walk determines the access permissions (read,
write, execute) for a given physical address.

The implementation supports various SMMPT modes (SMMPT34, SMMPT43, etc.) and
correctly handles leaf and non-leaf entries, including reserved bit
checks. Helper functions for parsing MPT entries and converting access
permissions are also included in the new `riscv_smmpt.h` header.

Co-authored-by: Huang Tao <[email protected]>
Co-authored-by: TANG Tiancheng <[email protected]>
Signed-off-by: LIU Zhiwei <[email protected]>
Reviewed-by: Daniel Henrique Barboza <[email protected]>
---
  target/riscv/cpu_helper.c  |   5 +-
  target/riscv/meson.build   |   1 +
  target/riscv/pmp.h         |   3 +
  target/riscv/riscv_smmpt.c | 274 +++++++++++++++++++++++++++++++++++++
  target/riscv/riscv_smmpt.h |  15 ++
  5 files changed, 295 insertions(+), 3 deletions(-)
  create mode 100644 target/riscv/riscv_smmpt.c

diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index dd6c861a90..e3361fadae 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -1089,9 +1089,8 @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong 
newpriv, bool virt_en)
   * @access_type: The type of MMU access
   * @mode: Indicates current privilege level.
   */
-static int get_physical_address_pmp(CPURISCVState *env, int *prot, hwaddr addr,
-                                    int size, MMUAccessType access_type,
-                                    int mode)
+int get_physical_address_pmp(CPURISCVState *env, int *prot, hwaddr addr,
+                             int size, MMUAccessType access_type, int mode)
  {
      pmp_priv_t pmp_priv;
      bool pmp_has_privs;
diff --git a/target/riscv/meson.build b/target/riscv/meson.build
index 3842c7c1a8..bd1c2e8fad 100644
--- a/target/riscv/meson.build
+++ b/target/riscv/meson.build
@@ -34,6 +34,7 @@ riscv_system_ss = ss.source_set()
  riscv_system_ss.add(files(
    'arch_dump.c',
    'pmp.c',
+  'riscv_smmpt.c',
    'debug.c',
    'monitor.c',
    'machine.c',
diff --git a/target/riscv/pmp.h b/target/riscv/pmp.h
index 467fb6b4b1..1ed574227c 100644
--- a/target/riscv/pmp.h
+++ b/target/riscv/pmp.h
@@ -86,6 +86,9 @@ void pmp_update_rule_nums(CPURISCVState *env);
  uint32_t pmp_get_num_rules(CPURISCVState *env);
  int pmp_priv_to_page_prot(pmp_priv_t pmp_priv);
  void pmp_unlock_entries(CPURISCVState *env);
+int get_physical_address_pmp(CPURISCVState *env, int *prot, hwaddr addr,
+                             int size, MMUAccessType access_type,
+                             int mode);

  #define MSECCFG_MML_ISSET(env) get_field(env->mseccfg, MSECCFG_MML)
  #define MSECCFG_MMWP_ISSET(env) get_field(env->mseccfg, MSECCFG_MMWP)
diff --git a/target/riscv/riscv_smmpt.c b/target/riscv/riscv_smmpt.c
new file mode 100644
index 0000000000..b7b47c5ae1
--- /dev/null
+++ b/target/riscv/riscv_smmpt.c
@@ -0,0 +1,274 @@
+/*
+ * QEMU RISC-V Smmpt (Memory Protection Table)
+ *
+ * Copyright (c) 2024 Alibaba Group. All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "riscv_smmpt.h"
+#include "pmp.h"
+#include "system/memory.h"
+
+typedef uint64_t load_entry_fn(AddressSpace *, hwaddr,
+                               MemTxAttrs, MemTxResult *);
+
+static uint64_t load_entry_32(AddressSpace *as, hwaddr addr,
+                              MemTxAttrs attrs, MemTxResult *result)
+{
+    return address_space_ldl(as, addr, attrs, result);
+}
+
+static uint64_t load_entry_64(AddressSpace *as, hwaddr addr,
+                              MemTxAttrs attrs, MemTxResult *result)
+{
+    return address_space_ldq(as, addr, attrs, result);
+}
+
+typedef union {
+    uint64_t raw;
+    struct {
+        uint32_t v:1;
+        uint32_t l:1;
+        uint32_t rsv1:5;
+        uint32_t perms:24;
+        uint32_t n:1;
+    } leaf32;
+    struct {
+        uint32_t v:1;
+        uint32_t l:1;
+        uint32_t rsv1:8;
+        uint32_t ppn:22;
+    } nonleaf32;
+    struct {
+        uint64_t v:1;
+        uint64_t l:1;
+        uint64_t rsv1:8;
+        uint64_t perms:48;
+        uint64_t rsv2:5;
+        uint64_t n:1;
+    } leaf64;
+    struct {
+        uint64_t v:1;
+        uint64_t l:1;
+        uint64_t rsv1:8;
+        uint64_t ppn:52;
+        uint64_t rsv2:1;
+        uint64_t n:1;
+    } nonleaf64;
+} mpte_union_t;
There is no guarantee in C that these bit fields are packed and laid
out like you are expecting
Good catch! Thanks for pointing it out. I have used the explicit extraction method for bit fields in mpte in v6 patch set.

Thanks,
Zhiwei

+
+static inline bool mpte_is_leaf(uint64_t mpte)
+{
+   return mpte & 0x2;
+}
+
+static inline bool mpte_is_valid(uint64_t mpte)
+{
+    return mpte & 0x1;
+}
+
+static uint64_t mpte_get_rsv(CPURISCVState *env, uint64_t mpte)
+{
+    RISCVMXL mxl = riscv_cpu_mxl(env);
+    bool leaf = mpte_is_leaf(mpte);
+    mpte_union_t *u = (mpte_union_t *)&mpte;
So this cast

+
+    if (mxl == MXL_RV32) {
+        return leaf ? u->leaf32.rsv1 : u->nonleaf32.rsv1;
and these accesses are not valid.

On some platforms this will work, but on others it won't

See the notes here
https://en.cppreference.com/w/cpp/language/bit_field.html for more
details

Alistair

Reply via email to