* [RFC PATCH v4 0/4] Add Smsdid and Smmpt supervisor domain protection
@ 2026-08-24 2:09 Rahul Pathak
2026-08-24 2:09 ` [RFC PATCH v4 1/4] lib: sbi_domain: Add finalize callback for per-domain state Rahul Pathak
` (3 more replies)
0 siblings, 4 replies; 8+ messages in thread
From: Rahul Pathak @ 2026-08-24 2:09 UTC (permalink / raw)
To: opensbi; +Cc: rahul.pathak, rahul, Rahul Pathak
This series adds OpenSBI support for the RISC-V SMMPT specification
sub-extensions - Smsdid (supervisor domain ID) and
Smmpt (supervisor domain memory protection).
Together these let M-mode firmware confine each supervisor domain to a
physical address space that is enforced in hardware, via a per domain
memory protection table (MPT) selected by the mmpt CSR.
Every supervisor domain gets its own MPT built from the memory regions
of its SBI domain. Per-region XWR permissions are derived from the
region's S/U access flags from SBI domain, so a region that does not have S/U
access is mapped as an deny instead of skipped.
The Root domain maps all physical memory, with the firmware region explicitly
denied to S-mode. Regions may be shared between domains, each side keeping
its own permissions.
NOTE: This series is based on another unmerged series which is present here:
https://lore.kernel.org/all/20260820121309.2551296-1-rahul.pathak@oss.qualcomm.com/
Testing:
Booted Linux when Smmpt was active. The boot dump shows the expected per region
permissions. Also checked with a small self-test in Qemu which confirms the
firmware region denied and the kernel region permitted from S-mode.
The Qemu RISC-V Virt machine was used to test this. The new cli args
required to be passed to Qemu are -
./qemu-system-riscv64 -nographic -M virt -cpu rv64,x-smsdid=true,x-smmpt=true
Boot Logs:
NOTE: The below logs are due the sbi_mpt_dump() function which is
present but not called. Its kept for debugging purposes.
sbi_mpt: mode=Smmpt52 SDIDLEN=6 max_domains=64
root_size=0x1000 root_align=0x1000
fw 0x0000000080000000+0x19e000 pool=global heap
SDID 0 root=0x0000000080098000 regions=9 dom=root
S-Domain0 Region01 : 0x0000000080000000+0x80000 : xwr=0 (---)
S-Domain0 Region02 : 0x0000000080000000+0x200000 : xwr=0 (---)
S-Domain0 Region03 : 0x0000000000100000+0x1000 : xwr=3 (RW-)
S-Domain0 Region04 : 0x0000000010000000+0x1000 : xwr=3 (RW-)
S-Domain0 Region05 : 0x0000000002000000+0x10000 : xwr=0 (---)
S-Domain0 Region06 : 0x000000000c400000+0x200000 : xwr=3 (RW-)
S-Domain0 Region07 : 0x000000000c000000+0x400000 : xwr=3 (RW-)
S-Domain0 Region08 : 0x0000000080000000+0x19e000 : xwr=0 (---) [LOCKED]
S-Domain0 Default : Rest of the address space : xwr=7 (RWX) [baseline, overlay by regions above]
Boot HART ID : 0
Boot HART Domain : root
Boot HART Priv Version : v1.12
Boot HART Base ISA : rv64imafdch
Boot HART ISA Extensions : sstc,zicntr,zihpm,zicboz,zicbom,sdtrig,svadu,f,d,smsdid,smmpt
Boot HART PMP Count : 16
Boot HART PMP Granularity : 2 bits
Boot HART PMP Address Bits : 54
Boot HART MHPM Info : 16 (0x0007fff8)
Boot HART Debug Triggers : 2 triggers
Boot HART MIDELEG : 0x0000000000001666
Boot HART MEDELEG : 0x0000000000f4b509
The corresponding Qemu Smmpt support from LIU Zhiwei
https://patchew.org/QEMU/20260723200325.24969-1-zhiwei._5Fliu@linux.alibaba.com/
This is part of collaboration work for RISC-V SMMTT development. More
details are here:
https://lists.gnu.org/archive/html/qemu-riscv/2026-07/msg01018.html
These OpenSBI changes are also present in github -
https://github.com/pathakraul/opensbi/tree/rpathak_smmpt_v4
Further Development:
This series add base infrastructure for Smsdid and Smmpt. Development
and testing with more then one Supervisor Domain. Extensive testing with
all the modes and other platform configurations.
v3 -> v4:
1. Drop the map_range/unmap_range rename patch from this series.
2. Move sbi_mpt_init() function out of the platform.c and make it
part of the sbi hart protection mechanism.
3. Drop mpt_early_init() and mpt_final_init() functions. The SMMPT
core registers the sbi domain state callbacks for initialization,
activation and cleanup.
4. Add state_finalize callback in for per domain state finalization.
5. Address the review comments like typos and add relevant macros
instead of using numbers for clarity.
6. Remove the sbi_mpt_dump() function call.
v2 -> v3:
1. Adapt to sbi hart protection abstraction.
2. Add missing sbi_mpt_hart_deactivate() decl in sbi_mpt.h.
3. Rename the *map_range and *unmap_range set of functions in sbi_hart_protection.
4. Update the copyright string.
v1 -> v2:
1. Rebase to latest master
2. Change probe order between mode and sdid.
3. Add sbi_mpt_query_access() function.
4. Formatting changes.
Rahul Pathak (4):
lib: sbi_domain: Add finalize callback for per-domain state
riscv: Add Smsdid and Smmpt hart extensions
mpt: Add Smsdid and Smmpt supervisor domain core
lib: sbi: Initialize SMMPT during coldboot
include/sbi/sbi_domain_state.h | 22 +
include/sbi/sbi_hart.h | 4 +
include/sbi/sbi_hart_mpt.h | 312 ++++++++++
lib/sbi/objects.mk | 2 +
lib/sbi/sbi_domain.c | 16 +
lib/sbi/sbi_domain_state.c | 25 +
lib/sbi/sbi_hart.c | 10 +-
lib/sbi/sbi_hart_mpt.c | 993 +++++++++++++++++++++++++++++++
lib/sbi/sbi_mpt.c | 1000 ++++++++++++++++++++++++++++++++
platform/generic/platform.c | 9 +
10 files changed, 2392 insertions(+), 1 deletion(-)
create mode 100644 include/sbi/sbi_hart_mpt.h
create mode 100644 lib/sbi/sbi_hart_mpt.c
create mode 100644 lib/sbi/sbi_mpt.c
--
2.53.0
--
opensbi mailing list
opensbi@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/opensbi
^ permalink raw reply [flat|nested] 8+ messages in thread
* [RFC PATCH v4 1/4] lib: sbi_domain: Add finalize callback for per-domain state
2026-08-24 2:09 [RFC PATCH v4 0/4] Add Smsdid and Smmpt supervisor domain protection Rahul Pathak
@ 2026-08-24 2:09 ` Rahul Pathak
2026-08-25 16:19 ` Pawandeep Oza
2026-08-24 2:09 ` [RFC PATCH v4 2/4] riscv: Add Smsdid and Smmpt hart extensions Rahul Pathak
` (2 subsequent siblings)
3 siblings, 1 reply; 8+ messages in thread
From: Rahul Pathak @ 2026-08-24 2:09 UTC (permalink / raw)
To: opensbi; +Cc: rahul.pathak, rahul, Rahul Pathak
Per-domain state is registered via sbi_domain_state in state_setup()
but during that time the domain memory regions are not final.
Add optional state_finalize() callback which is called
from sbi_domain_finalize for each domain after all domains are
registered and their memory regions are final.
Signed-off-by: Rahul Pathak <rahul.pathak@oss.qualcomm.com>
---
include/sbi/sbi_domain_state.h | 22 ++++++++++++++++++++++
lib/sbi/sbi_domain.c | 16 ++++++++++++++++
lib/sbi/sbi_domain_state.c | 25 +++++++++++++++++++++++++
3 files changed, 63 insertions(+)
diff --git a/include/sbi/sbi_domain_state.h b/include/sbi/sbi_domain_state.h
index 72030380..6528a95b 100644
--- a/include/sbi/sbi_domain_state.h
+++ b/include/sbi/sbi_domain_state.h
@@ -40,6 +40,18 @@ struct sbi_domain_state {
/** Optional callback to setup domain state */
int (*state_setup)(struct sbi_domain *dom,
struct sbi_domain_state *state, void *state_ptr);
+ /**
+ * Optional callback to finalize domain state
+ *
+ * Called for each domain from sbi_domain_finalize() after all
+ * domains are registered and memory regions are final.
+ *
+ * State from the domain memory regions must be setup here instead
+ * of state_setup()
+ */
+ int (*state_finalize)(struct sbi_domain *dom,
+ struct sbi_domain_state *state, void *state_ptr);
+
/** Optional callback to cleanup domain state */
void (*state_cleanup)(struct sbi_domain *dom,
struct sbi_domain_state *state, void *state_ptr);
@@ -64,6 +76,16 @@ void *sbi_domain_state_ptr(struct sbi_domain *dom, struct sbi_domain_state *stat
*/
int sbi_domain_setup_state(struct sbi_domain *dom);
+/**
+ * Finalize all domain state for a domain
+ * @param dom pointer to domain
+ *
+ * @return 0 on success and negative error code on failure
+ *
+ * Note: This function is used internally within domain framework.
+ */
+int sbi_domain_finalize_state(struct sbi_domain *dom);
+
/**
* Cleanup all domain state for a domain
* @param dom pointer to domain
diff --git a/lib/sbi/sbi_domain.c b/lib/sbi/sbi_domain.c
index 79d61c54..aa85d736 100644
--- a/lib/sbi/sbi_domain.c
+++ b/lib/sbi/sbi_domain.c
@@ -845,6 +845,7 @@ int sbi_domain_startup(struct sbi_scratch *scratch, u32 cold_hartid)
int sbi_domain_finalize(struct sbi_scratch *scratch)
{
int rc;
+ struct sbi_domain *dom;
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
/* Sanity checks */
@@ -865,6 +866,21 @@ int sbi_domain_finalize(struct sbi_scratch *scratch)
*/
domain_finalized = true;
+ /*
+ * Finalize per-domain state of each domain. Now all domains
+ * are finalized already and their memory regions are final.
+ * State which is derived from the domain memory regions is
+ * set up below.
+ */
+ sbi_domain_for_each(dom) {
+ rc = sbi_domain_finalize_state(dom);
+ if (rc) {
+ sbi_printf("%s: domain state finalize failed for %s"
+ " (error %d)\n", __func__, dom->name, rc);
+ return rc;
+ }
+ }
+
return 0;
}
diff --git a/lib/sbi/sbi_domain_state.c b/lib/sbi/sbi_domain_state.c
index 2d1f30e3..f8ccab69 100644
--- a/lib/sbi/sbi_domain_state.c
+++ b/lib/sbi/sbi_domain_state.c
@@ -84,6 +84,31 @@ int sbi_domain_setup_state(struct sbi_domain *dom)
return 0;
}
+int sbi_domain_finalize_state(struct sbi_domain *dom)
+{
+ struct sbi_domain_state *state;
+ void *state_ptr;
+ int rc;
+
+ if (!dom)
+ return SBI_EINVAL;
+
+ sbi_list_for_each_entry(state, &state_list, head) {
+ if (!state->state_finalize)
+ continue;
+
+ state_ptr = sbi_domain_state_ptr(dom, state);
+ if (!state_ptr)
+ continue;
+
+ rc = state->state_finalize(dom, state, state_ptr);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
void sbi_domain_cleanup_state(struct sbi_domain *dom)
{
struct sbi_domain_state *state;
--
2.53.0
--
opensbi mailing list
opensbi@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/opensbi
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [RFC PATCH v4 2/4] riscv: Add Smsdid and Smmpt hart extensions
2026-08-24 2:09 [RFC PATCH v4 0/4] Add Smsdid and Smmpt supervisor domain protection Rahul Pathak
2026-08-24 2:09 ` [RFC PATCH v4 1/4] lib: sbi_domain: Add finalize callback for per-domain state Rahul Pathak
@ 2026-08-24 2:09 ` Rahul Pathak
2026-08-24 2:09 ` [RFC PATCH v4 3/4] mpt: Add Smsdid and Smmpt supervisor domain core Rahul Pathak
2026-08-24 2:09 ` [RFC PATCH v4 4/4] lib: sbi: Initialize SMMPT during coldboot Rahul Pathak
3 siblings, 0 replies; 8+ messages in thread
From: Rahul Pathak @ 2026-08-24 2:09 UTC (permalink / raw)
To: opensbi; +Cc: rahul.pathak, rahul, Rahul Pathak, Pawandeep Oza, Ranbir Singh
Add hart extension identifiers for the RISC-V Smsdid (supervisor
domain ID) and Smmpt (supervisor domain memory protection) extensions.
Signed-off-by: Rahul Pathak <rahul.pathak@oss.qualcomm.com>
Reviewed-by: Pawandeep Oza <pawandeep.oza@oss.qualcomm.com>
Reviewed-by: Ranbir Singh <ranbir.singh@oss.qualcomm.com>
---
include/sbi/sbi_hart.h | 4 ++++
lib/sbi/sbi_hart.c | 2 ++
2 files changed, 6 insertions(+)
diff --git a/include/sbi/sbi_hart.h b/include/sbi/sbi_hart.h
index 543393bb..cbad94d4 100644
--- a/include/sbi/sbi_hart.h
+++ b/include/sbi/sbi_hart.h
@@ -95,6 +95,10 @@ enum sbi_hart_extensions {
SBI_HART_EXT_F,
/** Hart has D extension */
SBI_HART_EXT_D,
+ /** Hart has SMMTT - SMSDID extension */
+ SBI_HART_EXT_SMSDID,
+ /** Hart has SMMTT - SMMPT extension */
+ SBI_HART_EXT_SMMPT,
/** Maximum index of Hart extension */
SBI_HART_EXT_MAX,
diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c
index bee88557..4261fea8 100644
--- a/lib/sbi/sbi_hart.c
+++ b/lib/sbi/sbi_hart.c
@@ -360,6 +360,8 @@ const struct sbi_hart_ext_data sbi_hart_ext[] = {
__SBI_HART_EXT_DATA(v, SBI_HART_EXT_V),
__SBI_HART_EXT_DATA(f, SBI_HART_EXT_F),
__SBI_HART_EXT_DATA(d, SBI_HART_EXT_D),
+ __SBI_HART_EXT_DATA(smsdid, SBI_HART_EXT_SMSDID),
+ __SBI_HART_EXT_DATA(smmpt, SBI_HART_EXT_SMMPT),
};
_Static_assert(SBI_HART_EXT_MAX == array_size(sbi_hart_ext),
--
2.53.0
--
opensbi mailing list
opensbi@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/opensbi
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [RFC PATCH v4 3/4] mpt: Add Smsdid and Smmpt supervisor domain core
2026-08-24 2:09 [RFC PATCH v4 0/4] Add Smsdid and Smmpt supervisor domain protection Rahul Pathak
2026-08-24 2:09 ` [RFC PATCH v4 1/4] lib: sbi_domain: Add finalize callback for per-domain state Rahul Pathak
2026-08-24 2:09 ` [RFC PATCH v4 2/4] riscv: Add Smsdid and Smmpt hart extensions Rahul Pathak
@ 2026-08-24 2:09 ` Rahul Pathak
2026-08-24 2:09 ` [RFC PATCH v4 4/4] lib: sbi: Initialize SMMPT during coldboot Rahul Pathak
3 siblings, 0 replies; 8+ messages in thread
From: Rahul Pathak @ 2026-08-24 2:09 UTC (permalink / raw)
To: opensbi; +Cc: rahul.pathak, rahul, Rahul Pathak, Pawandeep Oza
Introduce the supervisor domain memory-protection (Smmpt) table
management core. This adds the mode agnostic core layer and the
per mode table walkers/builder.
Add defines for the mmpt CSR encoding, MPTE bit layout, permission flags,
structures, fence helpers and the public interfaces.
It implements the RV64 supported modes (Smmpt43/52/64) and
the RV32 mode (Smmpt34), its MPT table support.
Also implements mpt core which integrates Smmpt with SBI domains and
other core layers.
Signed-off-by: Rahul Pathak <rahul.pathak@oss.qualcomm.com>
Reviewed-by: Pawandeep Oza <pawandeep.oza@oss.qualcomm.com>
---
include/sbi/sbi_hart_mpt.h | 312 +++++++++++
lib/sbi/objects.mk | 2 +
lib/sbi/sbi_hart_mpt.c | 993 +++++++++++++++++++++++++++++++++++
lib/sbi/sbi_mpt.c | 1000 ++++++++++++++++++++++++++++++++++++
4 files changed, 2307 insertions(+)
create mode 100644 include/sbi/sbi_hart_mpt.h
create mode 100644 lib/sbi/sbi_hart_mpt.c
create mode 100644 lib/sbi/sbi_mpt.c
diff --git a/include/sbi/sbi_hart_mpt.h b/include/sbi/sbi_hart_mpt.h
new file mode 100644
index 00000000..c60b42bf
--- /dev/null
+++ b/include/sbi/sbi_hart_mpt.h
@@ -0,0 +1,312 @@
+/* SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ *
+ * Authors: Rahul Pathak <rahul.pathak@oss.qualcomm.com>
+ */
+
+#ifndef __SBI_HART_MPT_H__
+#define __SBI_HART_MPT_H__
+
+#include <sbi/sbi_types.h>
+#include <sbi/riscv_locks.h>
+#include <sbi/sbi_scratch.h>
+#include <sbi/sbi_bitmap.h>
+#include <sbi/riscv_asm.h>
+
+struct sbi_domain;
+struct sbi_mpt_domain;
+struct sbi_mpt_ctrl;
+struct sbi_domain_memregion;
+
+/* CSR addresses */
+#define SBI_CSR_MMPT 0x382
+#define SBI_CSR_MSDCFG 0x74E
+
+/* mmpt register field definitions */
+#if __riscv_xlen == 64
+# define SBI_MMPT_MODE_SHIFT 60UL
+# define SBI_MMPT_MODE_MASK (UL(0xF) << SBI_MMPT_MODE_SHIFT)
+# define SBI_MMPT_SDID_SHIFT 52UL
+# define SBI_MMPT_SDID_MASK (UL(0x3F) << SBI_MMPT_SDID_SHIFT)
+# define SBI_MMPT_PPN_MASK UL(0x00000FFFFFFFFFFF)
+#else
+# define SBI_MMPT_MODE_SHIFT 30UL
+# define SBI_MMPT_MODE_MASK (UL(0x3) << SBI_MMPT_MODE_SHIFT)
+# define SBI_MMPT_SDID_SHIFT 22UL
+# define SBI_MMPT_SDID_MASK (UL(0x3F) << SBI_MMPT_SDID_SHIFT)
+# define SBI_MMPT_PPN_MASK UL(0x3FFFFF)
+#endif
+
+#define SBI_MMPT_MODE_BARE 0UL
+#if __riscv_xlen == 64
+# define SBI_MMPT_MODE_SMMPT43 1UL
+# define SBI_MMPT_MODE_SMMPT52 2UL
+# define SBI_MMPT_MODE_SMMPT64 3UL
+#else
+# define SBI_MMPT_MODE_SMMPT34 1UL
+#endif
+
+/*
+ * sbi_mmpt_encode() — assemble the full mmpt CSR value.
+ */
+static inline unsigned long sbi_mmpt_encode(u32 mode, u32 sdid,
+ unsigned long ppn)
+{
+ return (((unsigned long)mode << SBI_MMPT_MODE_SHIFT) & SBI_MMPT_MODE_MASK) |
+ (((unsigned long)sdid << SBI_MMPT_SDID_SHIFT) & SBI_MMPT_SDID_MASK) |
+ (ppn & SBI_MMPT_PPN_MASK);
+}
+
+/*
+ * MPTE bit definitions
+ */
+#define SBI_MPTE_V (1UL << 0)
+#define SBI_MPTE_L (1UL << 1)
+#define SBI_MPTE_N (1UL << 2)
+#define SBI_MPTE_PPN_SHIFT 10U
+#define SBI_MPT_PAGE_SHIFT 12U
+#define SBI_MPT_PAGE_SIZE 4096UL
+#define SBI_MPT_PAGE_MASK (SBI_MPT_PAGE_SIZE - 1UL)
+#define SBI_MPTE_XWR_BASE 8U
+#define SBI_MPTE_XWR_WIDTH 3U
+#define SBI_MPTE_XWR_MASK 7UL
+
+static inline u32 sbi_mpte_xwr_shift(u32 n)
+{
+ return (SBI_MPTE_XWR_BASE + n * SBI_MPTE_XWR_WIDTH);
+}
+
+static inline unsigned long sbi_mpte_leaf_set_xwr(unsigned long mpte,
+ u32 n, u8 xwr)
+{
+ u32 sh = sbi_mpte_xwr_shift(n);
+
+ mpte &= ~(SBI_MPTE_XWR_MASK << sh);
+ mpte |= (xwr & SBI_MPTE_XWR_MASK) << sh;
+ return mpte;
+}
+
+static inline unsigned long sbi_mpte_nonleaf(unsigned long table_pa)
+{
+ return (((table_pa >> SBI_MPT_PAGE_SHIFT) << SBI_MPTE_PPN_SHIFT)
+ | SBI_MPTE_V);
+}
+
+static inline unsigned long sbi_mpte_nonleaf_table_pa(unsigned long mpte)
+{
+ return ((mpte >> SBI_MPTE_PPN_SHIFT) << SBI_MPT_PAGE_SHIFT);
+}
+
+/*
+ * XWR permission constants (XWR=000 means no access)
+ */
+#define SBI_MPT_PERM_NONE 0U
+#define SBI_MPT_PERM_R 1U
+#define SBI_MPT_PERM_W 2U
+#define SBI_MPT_PERM_X 4U
+#define SBI_MPT_PERM_RW (SBI_MPT_PERM_R | SBI_MPT_PERM_W)
+#define SBI_MPT_PERM_RX (SBI_MPT_PERM_R | SBI_MPT_PERM_X)
+#define SBI_MPT_PERM_RWX (SBI_MPT_PERM_R | SBI_MPT_PERM_W | SBI_MPT_PERM_X)
+
+/*
+ * SDID constants
+ */
+#define SBI_MPT_SDIDMAX 6U
+#define SBI_MPT_MAX_DOMAINS (1UL << SBI_MPT_SDIDMAX)
+#define SBI_MPT_SDID_INVALID SBI_MPT_MAX_DOMAINS
+
+/*
+ * SBI_MPT_MAX_REGIONS_DOMAIN: Number of regions a domain may have
+ * Used for tracking of regions
+ */
+#define SBI_MPT_MAX_REGIONS_DOMAIN 32U
+
+/*
+ * SMSDID mfence and minval raw encodings
+ *
+ * R-type, opcode=SYSTEM(0x73), funct3=0, rd=x0, rs1=PADDR, rs2=SDID
+ * mfence.pa funct7=0x19 -> 0x32000073 where rs1=rs2=x0
+ * minval.pa funct7=0x1b -> 0x36000073 where rs1=rs2=x0
+ *
+ * .insn r opcode, func3, func7, rd, rs1, rs2
+ */
+#define SBI_MPT_MFENCE_PA(rs1, rs2) ".insn r 0x73, 0, 0x19, x0, " rs1 ", " rs2
+
+#define SBI_MPT_MINVAL_PA(rs1, rs2) ".insn r 0x73, 0, 0x1b, x0, " rs1 ", " rs2
+
+/*
+ * Fence and invalidation helpers
+ */
+static inline void sbi_mpt_fence_all(void)
+{
+ __asm__ volatile(SBI_MPT_MFENCE_PA("x0", "x0") ::: "memory");
+}
+
+static inline void sbi_mpt_fence_sdid(u32 sdid)
+{
+ unsigned long s = (unsigned long)sdid & (SBI_MPT_MAX_DOMAINS - 1UL);
+
+ __asm__ volatile(SBI_MPT_MFENCE_PA("x0", "%0") :: "r"(s) : "memory");
+}
+
+/*
+ * rs1=paddr and rs2=x0
+ */
+static inline void sbi_mpt_mfence_addr(unsigned long paddr)
+{
+ __asm__ volatile(SBI_MPT_MFENCE_PA("%0", "x0") :: "r"(paddr) : "memory");
+}
+
+/*
+ * rs1=paddr and rs2=sdid
+ */
+static inline void sbi_mpt_mfence_pa(unsigned long paddr, u32 sdid)
+{
+ unsigned long s = (unsigned long)sdid & (SBI_MPT_MAX_DOMAINS - 1UL);
+
+ __asm__ volatile(SBI_MPT_MFENCE_PA("%0", "%1")
+ :: "r"(paddr), "r"(s) : "memory");
+}
+
+/*
+ * rs1=0 and rs2=0
+ */
+static inline void sbi_mpt_minval_all(void)
+{
+ /* Prior stores globally visible before invalidation */
+ __asm__ volatile("sfence.w.inval" ::: "memory");
+ __asm__ volatile(SBI_MPT_MINVAL_PA("x0", "x0") ::: "memory");
+ /* Invalidation completes before subsequent implicit accesses */
+ __asm__ volatile("sfence.inval.ir" ::: "memory");
+}
+
+/*
+ * rs1=paddr and rs2=sdid
+ */
+static inline void sbi_mpt_minval_sdid(unsigned long pa, u32 sdid)
+{
+ unsigned long s = (unsigned long)sdid & (SBI_MPT_MAX_DOMAINS - 1UL);
+
+ __asm__ volatile("sfence.w.inval" ::: "memory");
+ __asm__ volatile(SBI_MPT_MINVAL_PA("%0", "%1")
+ :: "r"(pa), "r"(s) : "memory");
+ __asm__ volatile("sfence.inval.ir" ::: "memory");
+}
+
+/*
+ * Core data structures
+ */
+struct sbi_mpt_region {
+ unsigned long pa;
+ unsigned long size;
+ u8 xwr;
+ /* region is locked and deny modification via add/remove_region */
+ bool locked;
+ /* mapped into more than one domain */
+ bool shared;
+};
+
+/*
+ * struct sbi_mpt_mode — SMMPT mode description.
+ */
+struct sbi_mpt_mode {
+ const char *name;
+ u32 mode_val;
+
+ /* Assemble the MMPT CSR value */
+ unsigned long (*encode_mmpt)(unsigned long ppn, u32 sdid);
+
+ /* map a range in MPT table */
+ int (*map_range)(struct sbi_mpt_domain *dom,
+ unsigned long pa, unsigned long size, u8 xwr);
+
+ /* number bytes for root table allocation */
+ unsigned long (*root_table_size)(void);
+
+ /* required root table physical address alignment */
+ unsigned long (*root_table_align)(void);
+
+ /* check if [pa,pa+size] fits in smmpt mode supported address space */
+ bool (*pa_in_range)(unsigned long pa, unsigned long size);
+
+ /* map full range of address space which is supported by the mode */
+ int (*map_full_range)(struct sbi_mpt_domain *dom, u8 xwr);
+
+ /* read the xwr for a PA */
+ u8 (*get_xwr)(struct sbi_mpt_domain *dom, unsigned long pa);
+};
+
+struct sbi_mpt_domain {
+ u32 sdid;
+ bool valid;
+ unsigned long root_pa;
+ struct sbi_mpt_mode *mode;
+ struct sbi_domain *sbi_dom;
+ unsigned int nregions;
+ struct sbi_mpt_region regions[SBI_MPT_MAX_REGIONS_DOMAIN];
+ spinlock_t lock;
+};
+
+/*
+ * struct sbi_mpt_ctrl — RDSM MPT state
+ */
+struct sbi_mpt_ctrl {
+ bool ready;
+ struct sbi_mpt_mode *mode;
+ /* detected sdidlen from mmpt probe */
+ u32 sdid_len;
+ /* 2^sdid_len — runtime limit for all domain operations */
+ u32 max_domains;
+ unsigned long fw_pa;
+ unsigned long fw_size;
+ /* Per domain state */
+ struct sbi_mpt_domain *domains[SBI_MPT_MAX_DOMAINS];
+ u32 ndomain;
+ /* sdid offset in scratch */
+ unsigned long sdid_offset;
+ /* bitmap to track assigned/free SDID (bit N set = SDID N free) */
+ DECLARE_BITMAP(sdid_bitmap, SBI_MPT_MAX_DOMAINS);
+};
+
+struct sbi_mpt_ctrl *sbi_mpt_ctrl_get(void);
+
+static inline u32 sbi_mpt_thishart_sdid(void)
+{
+ struct sbi_mpt_ctrl *ctrl = sbi_mpt_ctrl_get();
+ u32 *p;
+
+ if (!ctrl->ready || !ctrl->sdid_offset)
+ return (u32)SBI_MPT_SDID_INVALID;
+ p = sbi_scratch_offset_ptr(sbi_scratch_thishart_ptr(),
+ ctrl->sdid_offset);
+ return *p;
+}
+
+int sbi_mpt_init(void);
+
+int sbi_mpt_domain_add_region(u32 sdid, unsigned long pa, unsigned long size, u8 xwr);
+
+int sbi_mpt_domain_remove_region(u32 sdid, unsigned long pa, unsigned long size);
+
+int sbi_mpt_share_region(u32 src_sdid, u32 dst_sdid, unsigned long pa, unsigned long size, u8 dst_xwr);
+
+int sbi_mpt_unshare_region(u32 sdid, unsigned long pa, unsigned long size);
+
+int sbi_mpt_hart_activate(u32 sdid);
+
+void sbi_mpt_hart_deactivate(void);
+
+int sbi_mpt_hart_activate_for_domain(struct sbi_domain *sbi_dom);
+
+struct sbi_mpt_domain *sbi_mpt_domain_get(u32 sdid);
+
+unsigned long sbi_mpt_pool_alloc(unsigned long size, unsigned long align);
+
+void sbi_mpt_sdid_free(u32 sdid);
+
+int sbi_mpt_query_access(u32 sdid, unsigned long pa, u8 *out_xwr);
+
+void sbi_mpt_dump(void);
+
+#endif /* __SBI_HART_MPT_H__ */
diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk
index ae27d0c5..ea69a6f2 100644
--- a/lib/sbi/objects.mk
+++ b/lib/sbi/objects.mk
@@ -108,3 +108,5 @@ libsbi-objs-y += sbi_expected_trap.o
libsbi-objs-y += sbi_cppc.o
libsbi-objs-$(CC_SUPPORT_VECTOR) += sbi_vector.o
libsbi-objs-y += sbi_fp.o
+libsbi-objs-y += sbi_hart_mpt.o
+libsbi-objs-y += sbi_mpt.o
diff --git a/lib/sbi/sbi_hart_mpt.c b/lib/sbi/sbi_hart_mpt.c
new file mode 100644
index 00000000..7a027be0
--- /dev/null
+++ b/lib/sbi/sbi_hart_mpt.c
@@ -0,0 +1,993 @@
+/* SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ *
+ * Authors: Rahul Pathak <rahul.pathak@oss.qualcomm.com>
+ */
+
+#include <sbi/sbi_hart_mpt.h>
+#include <sbi/sbi_domain.h>
+#include <sbi/sbi_error.h>
+#include <sbi/sbi_hart.h>
+#include <sbi/sbi_heap.h>
+#include <sbi/sbi_scratch.h>
+#include <sbi/sbi_string.h>
+#include <sbi/sbi_console.h>
+#include <sbi/sbi_hart_protection.h>
+#include <sbi/riscv_locks.h>
+#include <sbi/riscv_asm.h>
+
+static struct sbi_mpt_ctrl mpt_ctrl;
+
+struct sbi_mpt_ctrl *sbi_mpt_ctrl_get(void)
+{
+ return &mpt_ctrl;
+}
+
+struct sbi_mpt_domain *sbi_mpt_domain_get(u32 sdid)
+{
+ struct sbi_mpt_domain *sd;
+
+ if (sdid >= mpt_ctrl.max_domains)
+ return NULL;
+
+ sd = mpt_ctrl.domains[sdid];
+ if (!sd || !sd->valid)
+ return NULL;
+
+ return sd;
+}
+
+/* Memory allocator for MPT tables from heap */
+unsigned long sbi_mpt_pool_alloc(unsigned long size, unsigned long align)
+{
+ unsigned long ptr = (unsigned long)sbi_aligned_alloc(align, size);
+
+ if (!ptr) {
+ sbi_printf("sbi_mpt: alloc failed (size=0x%lx align=0x%lx)\n",
+ size, align);
+ return 0;
+ }
+
+ /* MPT tables must be zeroed to mark them invalid (mpte.V = 0) */
+ sbi_memset((void *)ptr, 0, size);
+
+ return ptr;
+}
+
+/* Check if two regions overlap */
+static bool regions_overlap(unsigned long pa1, unsigned long size1,
+ unsigned long pa2, unsigned long size2)
+{
+ return (pa1 < pa2 + size2) && (pa2 < pa1 + size1);
+}
+
+/* Check if a region is completely contained inside another region */
+static bool region_contained(unsigned long child_pa, unsigned long child_size,
+ unsigned long parent_pa, unsigned long parent_size)
+{
+ return (child_pa >= parent_pa) &&
+ (child_pa + child_size <= parent_pa + parent_size);
+}
+
+/*
+ * Check if a region [pa, pa+size] overlaps with any mapped
+ * region which is locked
+ */
+static bool region_overlaps_locked(const struct sbi_mpt_domain *dom,
+ unsigned long pa, unsigned long size)
+{
+ unsigned int i;
+
+ for (i = 0; i < dom->nregions; i++)
+ if (dom->regions[i].locked &&
+ regions_overlap(pa, size, dom->regions[i].pa, dom->regions[i].size))
+ return true;
+ return false;
+}
+
+/* Probe SDIDLEN from the mmpt CSR. */
+static u32 probe_sdidlen(u32 mode_val)
+{
+ unsigned long saved, sdid_val;
+ u32 sdidlen = 0;
+
+ saved = csr_read(SBI_CSR_MMPT);
+
+ /* Program a legal non-Bare MODE with all-ones in the SDID field */
+ csr_write(SBI_CSR_MMPT,
+ (((unsigned long)mode_val << SBI_MMPT_MODE_SHIFT) & SBI_MMPT_MODE_MASK) |
+ SBI_MMPT_SDID_MASK);
+
+ sdid_val = (csr_read(SBI_CSR_MMPT) & SBI_MMPT_SDID_MASK) >> SBI_MMPT_SDID_SHIFT;
+
+ while (sdid_val) {
+ sdidlen += 1;
+ sdid_val >>= 1;
+ }
+
+ if (sdidlen > SBI_MPT_SDIDMAX)
+ sdidlen = SBI_MPT_SDIDMAX;
+
+ csr_write(SBI_CSR_MMPT, saved);
+
+ return sdidlen;
+}
+
+#if __riscv_xlen == 64
+extern struct sbi_mpt_mode smmpt43_mode;
+extern struct sbi_mpt_mode smmpt52_mode;
+extern struct sbi_mpt_mode smmpt64_mode;
+#else
+extern struct sbi_mpt_mode smmpt34_mode;
+#endif
+
+static bool probe_mode(u32 mode_val)
+{
+ u32 m;
+ csr_write(SBI_CSR_MMPT,
+ ((unsigned long)mode_val << SBI_MMPT_MODE_SHIFT) & SBI_MMPT_MODE_MASK);
+
+ m = (csr_read(SBI_CSR_MMPT) & SBI_MMPT_MODE_MASK) >> SBI_MMPT_MODE_SHIFT;
+
+ return (m == mode_val);
+}
+
+
+/*
+ * Probe SMMPT mode and initialize the related mode ops
+ *
+ * Deliberately prefer coverage over latency, because latency can be
+ * improved by the MPT cache which hardware may implement but the
+ * coverage is not. So pick the widest mode which is supported by the
+ * hardware.
+ */
+static struct sbi_mpt_mode *init_mpt_mode(u32 *out_mode_val)
+{
+ unsigned long saved = csr_read(SBI_CSR_MMPT);
+ struct sbi_mpt_mode *s = NULL;
+ u32 mode_val = 0;
+
+#if __riscv_xlen == 64
+ if (probe_mode(SBI_MMPT_MODE_SMMPT64)) {
+ s = &smmpt64_mode;
+ mode_val = SBI_MMPT_MODE_SMMPT64;
+ } else if (probe_mode(SBI_MMPT_MODE_SMMPT52)) {
+ s = &smmpt52_mode;
+ mode_val = SBI_MMPT_MODE_SMMPT52;
+ } else if (probe_mode(SBI_MMPT_MODE_SMMPT43)) {
+ s = &smmpt43_mode;
+ mode_val = SBI_MMPT_MODE_SMMPT43;
+ }
+#else
+ if (probe_mode(SBI_MMPT_MODE_SMMPT34)) {
+ s = &smmpt34_mode;
+ mode_val = SBI_MMPT_MODE_SMMPT34;
+ }
+#endif
+
+ csr_write(SBI_CSR_MMPT, saved);
+
+ if (out_mode_val)
+ *out_mode_val = mode_val;
+
+ return s;
+}
+
+/*
+ * mpt_map_range_perm(): Maps [pa, pa+size] region into supervisor domain MPT table
+ * with its access permissions(xwr).
+ *
+ * locked=true: means entry is immutable and it cannot be modified. Used for
+ * firmware deny-all and other such regions for permanenet access policy.
+ *
+ * locked=false: region entry is modifiable at runtime via add/remove_region.
+ * Used for S/U-mode accessible domain regions.
+ */
+
+static int mpt_map_range_perm(struct sbi_mpt_domain *dom, struct sbi_mpt_mode *sch,
+ unsigned long pa, unsigned long size, u8 xwr, bool locked)
+{
+ int rc;
+ bool first_in_range;
+
+ if (!size) {
+ sbi_printf("sbi_mpt: invalid size for range 0x%lx\n", pa);
+ return SBI_EINVAL;
+ }
+
+ if ((pa & SBI_MPT_PAGE_MASK) || (size & SBI_MPT_PAGE_MASK)) {
+ sbi_printf("sbi_mpt: unaligned range 0x%lx+0x%lx\n", pa, size);
+ return SBI_EINVAL;
+ }
+
+ if (!sch->pa_in_range(pa, size)) {
+ /*
+ * A region that pa_in_range() returns false falls into one of two
+ * different cases - PA which is out of range from the mappable
+ * range of that mode and second is PA is in range but the end
+ * (pa + size) is crossing the mappable range.
+ *
+ * Distinguish the two by probing pa_in_range() for just
+ * the first page in that big range and if even the first page
+ * is out of range it is the first case and if the base is in
+ * range but the whole region is not, it is case later.
+ */
+ first_in_range = sch->pa_in_range(pa, SBI_MPT_PAGE_SIZE);
+
+ if (locked) {
+ sbi_printf("sbi_mpt: locked region 0x%lx+0x%lx out of mode range\n",
+ pa, size);
+ return SBI_EINVAL;
+ }
+
+ /* Region with PA which partially crossover the mappable space */
+ if (first_in_range) {
+ sbi_printf("sbi_mpt: fatal region 0x%lx+0x%lx crossover"
+ " mode '%s' ceiling; high part may alias"
+ " with low MPTE entries — not mapping this region\n",
+ pa, size, sch->name);
+ return SBI_EINVAL;
+ }
+
+ /* Region which is entirely above the ceiling — unreachable */
+ sbi_printf("sbi_mpt: skip out-of-range 0x%lx+0x%lx"
+ " (entirely above '%s' ceiling; no MPT entry,\n",
+ pa, size, sch->name);
+ return 0;
+ }
+
+ rc = sch->map_range(dom, pa, size, xwr);
+ if (rc)
+ return rc;
+
+ if (dom->nregions >= SBI_MPT_MAX_REGIONS_DOMAIN)
+ return SBI_ENOMEM;
+
+ dom->regions[dom->nregions].pa = pa;
+ dom->regions[dom->nregions].size = size;
+ dom->regions[dom->nregions].xwr = xwr;
+ dom->regions[dom->nregions].locked = locked;
+ dom->regions[dom->nregions].shared = false;
+ dom->nregions++;
+
+ return 0;
+}
+
+/*
+ * mpt_xwr_from_memregion(): derive an MPTE XWR encoding from a SBI domain
+ * memregion's S/U access flags, clamped by cap.
+ *
+ * cap is an upper bound the caller may impose. A region from SBI domain
+ * may have more permissive access permissions which an supervisor domain
+ * may want to restict and cap them.
+ */
+static u8 mpt_xwr_from_memregion(const struct sbi_domain_memregion *mr,
+ u8 cap)
+{
+ u8 xwr = SBI_MPT_PERM_NONE;
+
+ if (mr->flags & SBI_DOMAIN_MEMREGION_SU_READABLE)
+ xwr |= SBI_MPT_PERM_R;
+ if (mr->flags & SBI_DOMAIN_MEMREGION_SU_WRITABLE)
+ xwr |= SBI_MPT_PERM_W;
+ if (mr->flags & SBI_DOMAIN_MEMREGION_SU_EXECUTABLE)
+ xwr |= SBI_MPT_PERM_X;
+
+ xwr &= cap;
+
+ /* XWR encodings 0b010 (W) and 0b110 (WX) are reserved */
+ if ((xwr & SBI_MPT_PERM_W) && !(xwr & SBI_MPT_PERM_R)) {
+ sbi_printf("sbi_mpt: region 0x%lx: W without R is a reserved"
+ " XWR encoding — denying region\n", mr->base);
+ return SBI_MPT_PERM_NONE;
+ }
+
+ return xwr;
+}
+
+static int smmpt_hart_configure(struct sbi_scratch *scratch,
+ struct sbi_domain *dom)
+{
+ return sbi_mpt_hart_activate_for_domain(dom);
+}
+
+static void smmpt_hart_unconfigure(struct sbi_scratch *scratch,
+ struct sbi_domain *dom)
+{
+ sbi_mpt_hart_deactivate();
+}
+
+static struct sbi_hart_protection smmpt_protection = {
+ .name = "smmpt",
+ .type = SBI_HART_PROTECTION_TYPE_ID,
+ .rating = 100,
+ .configure = smmpt_hart_configure,
+ .unconfigure = smmpt_hart_unconfigure,
+};
+
+/*
+ * Setup the MPT state for each domain
+ *
+ * The state of a supervisor domain is stored in the sbi_domain_state intance
+ * of the SBI domain it belongs to so that it is created and destroyed with
+ * that domain.
+ */
+static int mpt_state_setup(struct sbi_domain *dom, struct sbi_domain_state *state,
+ void *state_ptr)
+{
+ u32 sdid;
+ struct sbi_mpt_domain *sd = state_ptr;
+ struct sbi_mpt_ctrl *ctrl = &mpt_ctrl;
+
+ if (!ctrl->ready)
+ return 0;
+
+ for (sdid = 0; sdid < ctrl->max_domains; sdid++)
+ if (bitmap_test(ctrl->sdid_bitmap, sdid))
+ break;
+
+ if (sdid >= ctrl->max_domains) {
+ sbi_printf("sbi_mpt: no free SDID for domain %s\n", dom->name);
+ return SBI_ENOSPC;
+ }
+
+ bitmap_clear(ctrl->sdid_bitmap, sdid, 1);
+
+ SPIN_LOCK_INIT(sd->lock);
+ sd->sdid = sdid;
+ sd->valid = false;
+ sd->root_pa = 0;
+ sd->mode = ctrl->mode;
+ sd->sbi_dom = dom;
+ sd->nregions = 0;
+
+ ctrl->domains[sdid] = sd;
+
+ return 0;
+}
+
+/*
+ * mpt_state_finalize(): Build the MPT table of a supervisor domain.
+ *
+ * This function called once all domains are registered and their memory regions
+ * are final. General strategy for mapping the memory regions is to create a
+ * baseline mapping of the whole mappable address space supported by that mode
+ * then the respective regions are overlay on top of it with the permissions flags
+ * from the sbi domain. The firmware region is denied to S-mode in the root domain.
+ */
+static int mpt_state_finalize(struct sbi_domain *dom,
+ struct sbi_domain_state *state, void *state_ptr)
+{
+ int rc;
+ u8 base_xwr;
+ unsigned long root_pa;
+ struct sbi_mpt_region *r;
+ struct sbi_mpt_mode *sch;
+ const struct sbi_domain_memregion *mr;
+ struct sbi_mpt_domain *sd = state_ptr;
+ struct sbi_mpt_ctrl *ctrl = &mpt_ctrl;
+
+ if (!ctrl->ready)
+ return 0;
+
+ sch = ctrl->mode;
+
+ root_pa = sbi_mpt_pool_alloc(sch->root_table_size(),
+ sch->root_table_align());
+ if (!root_pa) {
+ sbi_printf("sbi_mpt: OOM root table for domain %s\n", dom->name);
+ return SBI_ENOMEM;
+ }
+ sd->root_pa = root_pa;
+
+ /*
+ * Similar to SBI domain, create a baseline which maps whole
+ * mappable address space by a mode and mark it with XWR
+ * permissions. Later selectively restrict the permissions in
+ * another pass.
+ */
+ sbi_domain_for_each_memregion(dom, mr) {
+ if (mr->order < sizeof(unsigned long) * 8)
+ continue;
+
+ if (!(mr->flags & SBI_DOMAIN_MEMREGION_SU_ACCESS_MASK))
+ continue;
+
+ base_xwr = mpt_xwr_from_memregion(mr, SBI_MPT_PERM_RWX);
+ rc = sch->map_full_range(sd, base_xwr);
+ if (rc)
+ return rc;
+
+ if (sd->nregions < SBI_MPT_MAX_REGIONS_DOMAIN) {
+ r = &sd->regions[sd->nregions++];
+ r->pa = 0;
+ r->size = ~0UL & ~SBI_MPT_PAGE_MASK;
+ r->xwr = base_xwr;
+ r->locked = false;
+ r->shared = false;
+ }
+
+ break;
+ }
+
+ /*
+ * Another pass to map every other region which has its own
+ * permissions derived from the memregion's S/U flags.
+ */
+ sbi_domain_for_each_memregion(dom, mr) {
+ if (mr->order >= sizeof(unsigned long) * 8)
+ continue;
+
+ rc = mpt_map_range_perm(sd, sch, mr->base, BIT(mr->order),
+ mpt_xwr_from_memregion(mr, SBI_MPT_PERM_RWX),
+ false);
+ if (rc)
+ return rc;
+ }
+
+ /*
+ * Explicitly map the firmware region to S-mode with no permissions
+ * and locked.
+ */
+ if (dom->index == 0) {
+ rc = mpt_map_range_perm(sd, sch, ctrl->fw_pa, ctrl->fw_size,
+ SBI_MPT_PERM_NONE, true);
+ if (rc) {
+ sbi_printf("sbi_mpt: cannot protect firmware rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ sd->valid = true;
+ ctrl->ndomain++;
+
+ sbi_mpt_fence_sdid(sd->sdid);
+
+ return 0;
+}
+
+/*
+ * mpt_state_cleanup(): Release the MPT state of a supervisor domain.
+ */
+static void mpt_state_cleanup(struct sbi_domain *dom,
+ struct sbi_domain_state *state, void *state_ptr)
+{
+ struct sbi_mpt_domain *sd = state_ptr;
+ struct sbi_mpt_ctrl *ctrl = &mpt_ctrl;
+
+ if (!ctrl->ready || !sd->valid)
+ return;
+
+ /*
+ * TODO: free the MPT table tree (root, inner and leaf tables)
+ * allocated by mpt_state_finalize(). A recursive free is not
+ * implemented yet so the tables and it will be important when
+ * runtime regions changes will be there.
+ */
+
+ sd->valid = false;
+ ctrl->ndomain--;
+
+ sbi_mpt_fence_sdid(sd->sdid);
+ sbi_mpt_sdid_free(sd->sdid);
+}
+
+static struct sbi_domain_state mpt_domain_state = {
+ .state_size = sizeof(struct sbi_mpt_domain),
+ .state_setup = mpt_state_setup,
+ .state_finalize = mpt_state_finalize,
+ .state_cleanup = mpt_state_cleanup,
+};
+
+/*
+ * sbi_mpt_domain_add_region(): Map a region in a MPT table
+ */
+int sbi_mpt_domain_add_region(u32 sdid, unsigned long pa,
+ unsigned long size, u8 xwr)
+{
+ struct sbi_mpt_domain *dom;
+ struct sbi_mpt_region *r;
+ int rc;
+
+ if (!mpt_ctrl.ready)
+ return SBI_ENODEV;
+ if (!size || (pa & SBI_MPT_PAGE_MASK) || (size & SBI_MPT_PAGE_MASK))
+ return SBI_EINVAL;
+
+ dom = sbi_mpt_domain_get(sdid);
+ if (!dom)
+ return SBI_EINVAL;
+
+ if (region_overlaps_locked(dom, pa, size)) {
+ sbi_printf("sbi_mpt: 0x%lx+0x%lx overlaps locked"
+ " (SDID %u)\n", pa, size, sdid);
+ return SBI_EINVAL;
+ }
+
+ if (!dom->mode->pa_in_range(pa, size)) {
+ /*
+ * A region that pa_in_range() rejects falls into one of two
+ * different cases - PA which is out of range from the mappable
+ * range of that mode and second is PA is in range but the end
+ * (PA + SIZE) is crossing the mappable range.
+ *
+ * Distinguish the two by probing pa_in_range() for just
+ * the base page and if even the base is out of range it is
+ * the first case and if the base is in range but the whole
+ * region is not, it is case later.
+ */
+ if (dom->mode->pa_in_range(pa, SBI_MPT_PAGE_SIZE))
+ sbi_printf("sbi_mpt: 0x%lx+0x%lx crossover mode '%s'"
+ " ceiling (SDID %u) — high part would alias;"
+ " rejected\n",
+ pa, size, dom->mode->name, sdid);
+ else
+ sbi_printf("sbi_mpt: 0x%lx+0x%lx out of range"
+ " (SDID %u)\n", pa, size, sdid);
+ return SBI_EINVAL;
+ }
+
+ spin_lock(&dom->lock);
+ rc = dom->mode->map_range(dom, pa, size, xwr);
+ spin_unlock(&dom->lock);
+
+ if (!rc) {
+ if (dom->nregions < SBI_MPT_MAX_REGIONS_DOMAIN) {
+ r = &dom->regions[dom->nregions++];
+ r->pa = pa;
+ r->size = size;
+ r->xwr = xwr;
+ r->locked = false;
+ r->shared = false;
+ }
+
+ sbi_mpt_fence_sdid(sdid);
+ }
+
+ return rc;
+}
+
+/*
+ * sbi_mpt_domain_remove_region(): Unmap a region from the MPT table
+ */
+int sbi_mpt_domain_remove_region(u32 sdid, unsigned long pa,
+ unsigned long size)
+{
+ int rc;
+ unsigned int i;
+ struct sbi_mpt_domain *dom;
+ struct sbi_mpt_region *r;
+
+ if (!mpt_ctrl.ready)
+ return SBI_ENODEV;
+
+ if (!size || (pa & SBI_MPT_PAGE_MASK) || (size & SBI_MPT_PAGE_MASK))
+ return SBI_EINVAL;
+
+ dom = sbi_mpt_domain_get(sdid);
+ if (!dom)
+ return SBI_EINVAL;
+
+ for (i = 0; i < dom->nregions; i++) {
+ r = &dom->regions[i];
+
+ if (r->locked && region_contained(pa, size, r->pa, r->size))
+ return SBI_EDENIED;
+ /*
+ * Shared regions must be removed via sbi_mpt_unshare_region()
+ * because that function prevents one domain revoking a region
+ * that another domain still has mapped.
+ */
+ if (r->shared && r->pa == pa && r->size == size)
+ return SBI_EDENIED;
+ }
+
+ spin_lock(&dom->lock);
+ rc = dom->mode->map_range(dom, pa, size, SBI_MPT_PERM_NONE);
+ spin_unlock(&dom->lock);
+
+ if (!rc)
+ sbi_mpt_fence_sdid(sdid);
+
+ return rc;
+}
+
+/*
+ * Shared memory regions
+ *
+ * sbi_mpt_share_region() — map a region into a second domain.
+ *
+ * Both domains MPT tables are updated independently — each has its
+ * own root/inner/leaf table tree. The same PA range can have different
+ * XWR permissions in different domains.
+ *
+ */
+
+int sbi_mpt_share_region(u32 src_sdid, u32 dst_sdid,
+ unsigned long pa, unsigned long size,
+ u8 dst_xwr)
+{
+ unsigned int i;
+ int rc;
+ struct sbi_mpt_domain *src_dom, *dst_dom;
+
+ if (!mpt_ctrl.ready)
+ return SBI_ENODEV;
+
+ if (!size || (pa & SBI_MPT_PAGE_MASK) || (size & SBI_MPT_PAGE_MASK))
+ return SBI_EINVAL;
+
+ if (src_sdid == dst_sdid)
+ return SBI_EINVAL;
+
+ src_dom = sbi_mpt_domain_get(src_sdid);
+ dst_dom = sbi_mpt_domain_get(dst_sdid);
+ if (!src_dom || !dst_dom)
+ return SBI_EINVAL;
+
+ if (region_overlaps_locked(dst_dom, pa, size)) {
+ sbi_printf("sbi_mpt: share 0x%lx+0x%lx overlaps locked region in SDID %u\n",
+ pa, size, dst_sdid);
+ return SBI_EINVAL;
+ }
+
+ /* Reject reserved xwr permissions: 0b010 and 0b110 (W without R) */
+ if ((dst_xwr & SBI_MPT_PERM_W) && !(dst_xwr & SBI_MPT_PERM_R)) {
+ sbi_printf("sbi_mpt: share 0x%lx+0x%lx: W without R is a reserved XWR encoding\n",
+ pa, size);
+ return SBI_EINVAL;
+ }
+
+ if (dst_dom->nregions >= SBI_MPT_MAX_REGIONS_DOMAIN) {
+ sbi_printf("sbi_mpt: share: SDID %u region table full\n", dst_sdid);
+ return SBI_ENOMEM;
+ }
+
+ spin_lock(&dst_dom->lock);
+ rc = dst_dom->mode->map_range(dst_dom, pa, size, dst_xwr);
+ spin_unlock(&dst_dom->lock);
+ if (rc)
+ return rc;
+
+ dst_dom->regions[dst_dom->nregions].pa = pa;
+ dst_dom->regions[dst_dom->nregions].size = size;
+ dst_dom->regions[dst_dom->nregions].xwr = dst_xwr;
+ dst_dom->regions[dst_dom->nregions].locked = false;
+ dst_dom->regions[dst_dom->nregions].shared = true;
+ dst_dom->nregions++;
+
+ for (i = 0; i < src_dom->nregions; i++) {
+ if (src_dom->regions[i].pa == pa &&
+ src_dom->regions[i].size == size) {
+ src_dom->regions[i].shared = true;
+ break;
+ }
+ }
+
+ sbi_mpt_fence_sdid(src_sdid);
+ sbi_mpt_fence_sdid(dst_sdid);
+
+ sbi_printf("sbi_mpt: shared 0x%lx+0x%lx SDID %u → SDID %u xwr=%u\n",
+ pa, size, src_sdid, dst_sdid, dst_xwr);
+
+ return 0;
+}
+
+/*
+ * sbi_mpt_unshare_region() — Unmap a shared region from a domain.
+ *
+ * The other domain remains unaffected just the shared flag gets removed
+ * for that region from both the domains.
+ */
+int sbi_mpt_unshare_region(u32 sdid, unsigned long pa, unsigned long size)
+{
+ u32 s;
+ int rc;
+ unsigned int i, j;
+ struct sbi_mpt_domain *dom, *peer;
+
+ if (!mpt_ctrl.ready)
+ return SBI_ENODEV;
+
+ if (!size || (pa & SBI_MPT_PAGE_MASK) || (size & SBI_MPT_PAGE_MASK))
+ return SBI_EINVAL;
+
+ dom = sbi_mpt_domain_get(sdid);
+ if (!dom)
+ return SBI_EINVAL;
+
+ for (i = 0; i < dom->nregions; i++) {
+ if (dom->regions[i].pa == pa &&
+ dom->regions[i].size == size &&
+ dom->regions[i].shared)
+ break;
+ }
+ if (i == dom->nregions) {
+ sbi_printf("sbi_mpt: unshare 0x%lx+0x%lx not found in SDID %u\n",
+ pa, size, sdid);
+ return SBI_EINVAL;
+ }
+
+ spin_lock(&dom->lock);
+ rc = dom->mode->map_range(dom, pa, size, SBI_MPT_PERM_NONE);
+ spin_unlock(&dom->lock);
+
+ if (rc)
+ return rc;
+
+ dom->nregions -= 1;
+ dom->regions[i] = dom->regions[dom->nregions];
+
+ /* Clear the shared flag on any other domain that has this PA region shared.*/
+ for (s = 0; s < mpt_ctrl.max_domains; s++) {
+ peer = sbi_mpt_domain_get(s);
+ if (!peer || peer == dom)
+ continue;
+
+ for (j = 0; j < peer->nregions; j++)
+ if (peer->regions[j].pa == pa && peer->regions[j].size == size)
+ peer->regions[j].shared = false;
+ }
+
+ sbi_mpt_fence_sdid(sdid);
+
+ return 0;
+}
+
+/*
+ * Disable SMMPT on hart
+ */
+void sbi_mpt_hart_deactivate(void)
+{
+ /*
+ * mmpt is inactive in M-mode per spec, so no CSR write
+ * is needed.
+ */
+}
+
+/*
+ * Enable SMMPT on hart on a supervisor domain(sdid)
+ */
+int sbi_mpt_hart_activate(u32 sdid)
+{
+ u32 *p;
+ unsigned long ppn;
+ struct sbi_mpt_domain *dom;
+
+ if (!mpt_ctrl.ready)
+ return SBI_ENODEV;
+
+ dom = sbi_mpt_domain_get(sdid);
+ if (!dom)
+ return SBI_EINVAL;
+
+ ppn = dom->root_pa >> SBI_MPT_PAGE_SHIFT;
+ csr_write(SBI_CSR_MMPT, dom->mode->encode_mmpt(ppn, sdid));
+ sbi_mpt_fence_sdid(sdid);
+
+ if (mpt_ctrl.sdid_offset) {
+ p = sbi_scratch_offset_ptr(sbi_scratch_thishart_ptr(), mpt_ctrl.sdid_offset);
+ *p = sdid;
+ }
+
+ return 0;
+}
+
+/*
+ * Activate SMMPT for all harts which are associated with an linked
+ * SBI domain in an supervisor domain.
+ */
+int sbi_mpt_hart_activate_for_domain(struct sbi_domain *sbi_dom)
+{
+ struct sbi_mpt_domain *sd;
+
+ if (!mpt_ctrl.ready)
+ return SBI_ENODEV;
+ if (!sbi_dom)
+ return SBI_EINVAL;
+
+ sd = sbi_domain_state_ptr(sbi_dom, &mpt_domain_state);
+ if (!sd || !sd->valid)
+ return 0;
+
+ return sbi_mpt_hart_activate(sd->sdid);
+}
+
+/*
+ * sbi_mpt_sdid_free(): Returns SDID to the free pool after domain destroy.
+ */
+void sbi_mpt_sdid_free(u32 sdid)
+{
+ if (!mpt_ctrl.ready || sdid >= mpt_ctrl.max_domains)
+ return;
+
+ bitmap_set(mpt_ctrl.sdid_bitmap, (int)sdid, 1);
+
+ mpt_ctrl.domains[sdid] = NULL;
+}
+
+/*
+ * sbi_mpt_query_access(): Returns the XWR for a PA in supervisor domain(sdid).
+ */
+int sbi_mpt_query_access(u32 sdid, unsigned long pa, u8 *out_xwr)
+{
+ struct sbi_mpt_domain *dom;
+
+ if (!mpt_ctrl.ready)
+ return SBI_ENODEV;
+
+ if (!out_xwr)
+ return SBI_EINVAL;
+
+ dom = sbi_mpt_domain_get(sdid);
+ if (!dom)
+ return SBI_EINVAL;
+
+ if (!dom->mode->pa_in_range(pa, SBI_MPT_PAGE_SIZE)) {
+ *out_xwr = SBI_MPT_PERM_NONE;
+ return 0;
+ }
+
+ spin_lock(&dom->lock);
+ *out_xwr = dom->mode->get_xwr(dom, pa);
+ spin_unlock(&dom->lock);
+
+ return 0;
+}
+
+/*
+ * sbi_mpt_init(): Initialize MPT structures
+ */
+int sbi_mpt_init(void)
+{
+ int rc;
+ u32 *p;
+ u32 mode_val;
+ unsigned long fw_end;
+ struct sbi_scratch *s;
+ struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
+ unsigned long fw_pa = scratch->fw_start;
+ unsigned long fw_size = scratch->fw_size;
+
+ if (mpt_ctrl.ready)
+ return 0;
+
+ if (!sbi_hart_has_extension(scratch, SBI_HART_EXT_SMSDID)) {
+ sbi_printf("sbi_mpt: Smsdid extension absent\n");
+ return SBI_ENODEV;
+ }
+
+ if (!sbi_hart_has_extension(scratch, SBI_HART_EXT_SMMPT)) {
+ sbi_printf("sbi_mpt: Smmpt extension absent\n");
+ return SBI_ENODEV;
+ }
+
+ mpt_ctrl.mode = init_mpt_mode(&mode_val);
+ if (!mpt_ctrl.mode) {
+ sbi_printf("sbi_mpt: WARL probe found no mode\n");
+ return SBI_ENODEV;
+ }
+
+ /*
+ * SDIDLEN must be probed with a legal non-Bare MODE programmed,
+ * probe sdidlen must be done only after the probing the mode.
+ */
+ mpt_ctrl.sdid_len = probe_sdidlen(mode_val);
+ if (!mpt_ctrl.sdid_len) {
+ sbi_printf("sbi_mpt: SDIDLEN=0 — SDID field not implemented\n");
+ return SBI_ENODEV;
+ }
+
+ mpt_ctrl.max_domains = 1UL << mpt_ctrl.sdid_len;
+
+ /*
+ * Initialise SDID bitmap
+ * set bits 0..(max_domains-1) as 1 marking those SDIDs free.
+ */
+ bitmap_fill(mpt_ctrl.sdid_bitmap, mpt_ctrl.max_domains);
+
+ /* Round fw_pa and fw_size to page boundaries before storing. */
+ fw_end = (fw_pa + fw_size + SBI_MPT_PAGE_SIZE - 1UL) & ~SBI_MPT_PAGE_MASK;
+ mpt_ctrl.fw_pa = fw_pa & ~SBI_MPT_PAGE_MASK;
+ mpt_ctrl.fw_size = fw_end - mpt_ctrl.fw_pa;
+
+
+ /* Per-hart SDID scratch slot — initialise all harts to INVALID */
+ mpt_ctrl.sdid_offset = sbi_scratch_alloc_offset(sizeof(u32));
+ if (!mpt_ctrl.sdid_offset) {
+ sbi_printf("sbi_mpt: scratch alloc for SDID failed\n");
+ }
+ else {
+ sbi_for_each_hartindex(i) {
+ s = sbi_hartindex_to_scratch(i);
+ if (s) {
+ p = sbi_scratch_offset_ptr(s, mpt_ctrl.sdid_offset);
+ *p = SBI_MPT_SDID_INVALID;
+ }
+ }
+ }
+
+ mpt_ctrl.ready = true;
+
+ rc = sbi_hart_protection_register(&smmpt_protection);
+ if (rc) {
+ sbi_printf("sbi_mpt: hart protection register failed rc=%d\n", rc);
+ mpt_ctrl.ready = false;
+ return rc;
+ }
+
+ rc = sbi_domain_register_state(&mpt_domain_state);
+ if (rc) {
+ sbi_printf("sbi_mpt: domain data register failed rc=%d\n", rc);
+ mpt_ctrl.ready = false;
+ return rc;
+ }
+
+ return 0;
+}
+
+/*
+ * Debug dump
+ */
+void sbi_mpt_dump(void)
+{
+ u32 s;
+ unsigned int r;
+ const struct sbi_mpt_domain *d;
+ const unsigned long catchall_size = ~0UL & ~SBI_MPT_PAGE_MASK;
+
+ if (!mpt_ctrl.ready) {
+ sbi_printf("sbi_mpt: not initialised\n");
+ return;
+ }
+
+ sbi_printf("sbi_mpt: mode=%-10s SDIDLEN=%u max_domains=%u\n", mpt_ctrl.mode->name,
+ mpt_ctrl.sdid_len, mpt_ctrl.max_domains);
+ sbi_printf("root_size=0x%lx root_align=0x%lx\n",
+ mpt_ctrl.mode->root_table_size(), mpt_ctrl.mode->root_table_align());
+ sbi_printf("fw 0x%016lx+0x%lx pool=global heap\n",
+ mpt_ctrl.fw_pa, mpt_ctrl.fw_size);
+ for (s = 0; s < mpt_ctrl.max_domains; s++) {
+ bool has_baseline = false;
+ u8 baseline_xwr = SBI_MPT_PERM_NONE;
+
+ d = mpt_ctrl.domains[s];
+ if (!d || !d->valid)
+ continue;
+ sbi_printf("SDID %2u root=0x%016lx regions=%u dom=%s\n",
+ d->sdid, d->root_pa, d->nregions, d->sbi_dom ? d->sbi_dom->name : "(none)");
+ for (r = 0; r < d->nregions; r++) {
+
+ if (d->regions[r].pa == 0 &&
+ d->regions[r].size == catchall_size) {
+ has_baseline = true;
+ baseline_xwr = d->regions[r].xwr;
+ continue;
+ }
+
+ sbi_printf("S-Domain%u Region%02u : 0x%016lx+0x%-16lx: xwr=%u (%c%c%c)%s%s\n",
+ d->sdid, r, d->regions[r].pa,
+ d->regions[r].size,
+ d->regions[r].xwr,
+ (d->regions[r].xwr & SBI_MPT_PERM_R) ? 'R' : '-',
+ (d->regions[r].xwr & SBI_MPT_PERM_W) ? 'W' : '-',
+ (d->regions[r].xwr & SBI_MPT_PERM_X) ? 'X' : '-',
+ d->regions[r].locked ? " [LOCKED]" : "",
+ d->regions[r].shared ? " [SHARED]" : "");
+ }
+
+ if (has_baseline) {
+ sbi_printf("S-Domain%u Default : %-37s: xwr=%u (%c%c%c) [baseline, overlay by regions above]\n",
+ d->sdid, "Rest of the address space",
+ baseline_xwr,
+ (baseline_xwr & SBI_MPT_PERM_R) ? 'R' : '-',
+ (baseline_xwr & SBI_MPT_PERM_W) ? 'W' : '-',
+ (baseline_xwr & SBI_MPT_PERM_X) ? 'X' : '-');
+ }
+ else {
+ sbi_printf("S-Domain%u : xwr=0 (---) [no baseline — unmapped PAs denied]\n",
+ d->sdid);
+ }
+ }
+}
diff --git a/lib/sbi/sbi_mpt.c b/lib/sbi/sbi_mpt.c
new file mode 100644
index 00000000..cb1ce937
--- /dev/null
+++ b/lib/sbi/sbi_mpt.c
@@ -0,0 +1,1000 @@
+/* SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ *
+ * Authors: Rahul Pathak <rahul.pathak@oss.qualcomm.com>
+ */
+
+#include <sbi/sbi_hart_mpt.h>
+#include <sbi/sbi_error.h>
+#include <sbi/sbi_string.h>
+#include <sbi/sbi_console.h>
+
+#if __riscv_xlen == 64
+
+/*
+ * Smmpt43 — 43-bit supervisor physical address (SPA)
+ * +---------+---------+---------+---------+--------+
+ * | pn[2] | pn[1] | pn[0] | pi | offset |
+ * | [42:34] | [33:25] | [24:16] | [15:12] | [11:0] |
+ * | 9b | 9b | 9b | 4b | 12b |
+ * +---------+---------+---------+---------+--------+
+ *
+ * Smmpt52 — 52-bit supervisor physical address (SPA)
+ * +---------+---------+---------+---------+---------+--------+
+ * | pn[3] | pn[2] | pn[1] | pn[0] | pi | offset |
+ * | [51:43] | [42:34] | [33:25] | [24:16] | [15:12] | [11:0] |
+ * | 9b | 9b | 9b | 9b | 4b | 12b |
+ * +---------+---------+---------+---------+---------+--------+
+ *
+ * Smmpt64 — 64-bit supervisor physical address (SPA)
+ * +---------+---------+---------+---------+---------+---------+--------+
+ * | pn[4] | pn[3] | pn[2] | pn[1] | pn[0] | pi | offset |
+ * | [63:52] | [51:43] | [42:34] | [33:25] | [24:16] | [15:12] | [11:0] |
+ * | 12b | 9b | 9b | 9b | 9b | 4b | 12b |
+ * +---------+---------+---------+---------+---------+---------+--------+
+ *
+ */
+
+/* Innter entries are MPTEs not in root MPT page */
+#define RV64_INNER_ENTRIES 512U
+#define RV64_MPTE_SIZE 8UL
+#define RV64_TABLE_SIZE (RV64_INNER_ENTRIES * RV64_MPTE_SIZE)
+
+/* Smmpt64 root: 4096 entries × 8B = 32KB, 32KB aligned */
+#define RV64_ROOT_SMMPT64_ENTRIES 4096U
+#define RV64_ROOT_SMMPT64_SIZE (RV64_ROOT_SMMPT64_ENTRIES * RV64_MPTE_SIZE)
+
+#define RV64_NUMPGINRANGE 4U
+#define RV64_PAGES_PER_MPTE (1U << RV64_NUMPGINRANGE)
+#define RV64_PAGES_PER_MPTE_MASK (RV64_PAGES_PER_MPTE - 1U)
+#define RV64_INNER_PN_BITS 9U
+
+/* Bytes covered by one complete leaf table = pages_per_leaf × PAGE_SIZE */
+#define RV64_LEAF_RANGE (RV64_PAGES_PER_MPTE * SBI_MPT_PAGE_SIZE)
+
+/* Shift required to get tuple from sub MPT index (non-root inner table)*/
+#define RV64_SPLIT_TUPLE_SHIFT (RV64_INNER_PN_BITS - RV64_NUMPGINRANGE)
+
+/*
+ * NAPOT G=4 for Smmpt43, Smmpt52 and Smmpt64
+ *
+ * G=4 means 2^(G+1)=32 contiguous leaf mpte which form a NAPOT group.
+ * Each RV64 leaf MPTE covers 64KiB so one NAPOT group covers 2MiB.
+ *
+ * All other G values apart from 4 are reserved.
+ */
+#define RV64_NAPOT_G 4U
+#define RV64_NAPOT_G_SHIFT 12U
+#define RV64_NAPOT_MPTE_COUNT (1U << (RV64_NAPOT_G + 1))
+#define RV64_NAPOT_SIZE ((unsigned long)RV64_NAPOT_MPTE_COUNT * RV64_LEAF_RANGE)
+#define RV64_NAPOT_PN0_ALIGN RV64_NAPOT_MPTE_COUNT
+
+/*
+ * Swith to disable NAPOT compaction of MPTE entries while writing.
+ */
+#define RV64_NAPOT_DISABLE 0
+
+/*
+ * Supervisor Physucal address fields extraction
+ */
+
+/*
+ * rv64_table_idx() — extract pn[level] from a SPA.
+ */
+static inline u32 rv64_table_idx(unsigned long pa, u32 level,
+ const struct sbi_mpt_mode *sch)
+{
+ u32 shift = 16 + level * 9;
+ u32 mask = (sch->mode_val == SBI_MMPT_MODE_SMMPT64 && level == 4) ? 0xFFF : 0x1FF;
+
+ return ((pa >> shift) & mask);
+}
+
+/*
+ * rv64_tuple_idx_shift() — XWR tuple index shift at level i.
+ */
+static inline u32 rv64_tuple_idx_shift(u32 level)
+{
+ return (SBI_MPT_PAGE_SHIFT + level * RV64_INNER_PN_BITS);
+}
+
+static inline u32 rv64_tuple_idx(unsigned long pa, u32 level)
+{
+ return ((pa >> rv64_tuple_idx_shift(level)) & RV64_PAGES_PER_MPTE_MASK);
+}
+
+/*
+ * rv64_mpte_range() — bytes covered by one MPTE at level L.
+ */
+static inline unsigned long rv64_mpte_range(u32 level)
+{
+ return RV64_LEAF_RANGE << (level * RV64_INNER_PN_BITS);
+}
+
+/*
+ * RV64 MPTE Read/Write functions
+ */
+
+static inline u64 rv64_read_mpte(unsigned long pa)
+{
+ return *(volatile u64 *)pa;
+}
+
+static inline void rv64_write_mpte(unsigned long pa, u64 v)
+{
+ *(volatile u64 *)pa = v;
+}
+
+static inline unsigned long rv64_mpte_pa(unsigned long table_pa, u32 idx)
+{
+ return (table_pa + idx * RV64_MPTE_SIZE);
+}
+
+static inline unsigned long rv64_next_table_pa(u64 mpte)
+{
+ return (unsigned long)((u64)(mpte >> SBI_MPTE_PPN_SHIFT) << SBI_MPT_PAGE_SHIFT);
+}
+
+/*
+ * Generate Napot Leaf MPTE
+ *
+ * Constructs one MPTE value used for all
+ * RV64_NAPOT_MPTE_COUNT entries in a NAPOT group.
+ *
+ * All MPTEs in the group are identical
+ */
+static inline u64 rv64_napot_leaf_mpte(u8 xwr)
+{
+ u64 mpte = SBI_MPTE_V | SBI_MPTE_L | SBI_MPTE_N;
+
+ mpte = sbi_mpte_leaf_set_xwr(mpte, 0, xwr);
+ mpte |= ((u64)RV64_NAPOT_G << RV64_NAPOT_G_SHIFT);
+
+ return mpte;
+}
+
+/*
+ * rv64_napot_leaf_xwr() — recover the uniform XWR from a NAPOT leaf.
+ */
+static inline u8 rv64_napot_leaf_xwr(u64 mpte)
+{
+ return ((mpte >> SBI_MPTE_XWR_BASE) & SBI_MPTE_XWR_MASK);
+}
+
+/*
+ * rv64_napot_demote() — Convert a Napot MPTE group into RV64_NAPOT_MPTE_COUNT
+ * normal(Non-Napot) MPTE carrying the SAME permission.
+ *
+ * leaf_mpte_pa is any leaf mpte pa from the Napot group
+ */
+static void rv64_napot_demote(struct sbi_mpt_domain *dom,
+ unsigned long leaf_mpte_pa, unsigned long pa)
+{
+ u8 xwr;
+ u32 pn0, pg, i;
+ u64 leaf;
+ unsigned long grp_base;
+
+ xwr = rv64_napot_leaf_xwr(rv64_read_mpte(leaf_mpte_pa));
+ pn0 = rv64_table_idx(pa, 0, dom->mode);
+ grp_base = leaf_mpte_pa - (pn0 & (RV64_NAPOT_MPTE_COUNT - 1)) * RV64_MPTE_SIZE;
+ leaf = SBI_MPTE_V | SBI_MPTE_L;
+
+ for (pg = 0; pg < RV64_PAGES_PER_MPTE; pg++)
+ leaf = sbi_mpte_leaf_set_xwr(leaf, pg, xwr);
+
+ for (i = 0; i < RV64_NAPOT_MPTE_COUNT; i++)
+ rv64_write_mpte(grp_base + i * RV64_MPTE_SIZE, leaf);
+}
+
+/*
+ * MPT table best-level selection
+ *
+ * Returns the highest level at which the range [pa, pa+size] can be
+ * covered by a single leaf MPTE. Level 0 is always valid because thats
+ * the last resort.
+ */
+static u32 rv64_best_level(unsigned long pa, unsigned long size,
+ u32 top_level)
+{
+ u32 lvl;
+ unsigned long range;
+
+ for (lvl = top_level; lvl >= 1; lvl--) {
+ range = rv64_mpte_range(lvl);
+
+ if (size >= range && (pa & (range - 1)) == 0)
+ return lvl;
+ }
+ return 0;
+}
+
+/*
+ * Generic N-level walk with lazy table allocation
+ */
+static unsigned long rv64_split_leaf(unsigned long parent_ep)
+{
+ u64 parent = rv64_read_mpte(parent_ep);
+ unsigned long sub;
+ u32 j, pg, sh;
+ u8 xwr;
+ u64 child;
+
+ sub = sbi_mpt_pool_alloc(RV64_TABLE_SIZE, SBI_MPT_PAGE_SIZE);
+ if (!sub)
+ return 0;
+
+ for (j = 0; j < RV64_INNER_ENTRIES; j++) {
+ sh = sbi_mpte_xwr_shift(j >> RV64_SPLIT_TUPLE_SHIFT);
+ xwr = (((unsigned long)parent >> sh) & SBI_MPTE_XWR_MASK);
+ child = SBI_MPTE_V | SBI_MPTE_L; /* N=0 uniform leaf */
+
+ for (pg = 0; pg < RV64_PAGES_PER_MPTE; pg++)
+ child = sbi_mpte_leaf_set_xwr(child, pg, xwr);
+
+ rv64_write_mpte(sub + j * RV64_MPTE_SIZE, child);
+ }
+
+ /* parent leaf -> non-leaf pointer to MPT sub table. */
+ rv64_write_mpte(parent_ep, sbi_mpte_nonleaf(sub));
+
+ return sub;
+}
+
+/*
+ * Walk a MPT table and return MPTE PA and its suitable level
+ */
+static unsigned long rv64_walk_alloc(struct sbi_mpt_domain *dom,
+ unsigned long pa,
+ unsigned long size,
+ u32 *out_level,
+ u32 top_level)
+{
+ u32 lvl, idx;
+ u64 mpte;
+ unsigned long ep, sub, new_pa;
+ const struct sbi_mpt_mode *sch = dom->mode;
+ unsigned long table_pa = dom->root_pa;
+ u32 best_lvl = rv64_best_level(pa, size, top_level);
+
+
+ for (lvl = top_level; lvl >= 1; lvl--) {
+ idx = rv64_table_idx(pa, lvl, sch);
+ ep = rv64_mpte_pa(table_pa, idx);
+ mpte = rv64_read_mpte(ep);
+
+ if (mpte & SBI_MPTE_L) {
+ if (lvl > best_lvl) {
+ sub = rv64_split_leaf(ep);
+ if (!sub)
+ return 0;
+
+ table_pa = sub;
+ continue;
+ }
+
+ *out_level = lvl;
+ return ep;
+ }
+
+ if (!(mpte & SBI_MPTE_V)) {
+ if (lvl <= best_lvl) {
+ *out_level = lvl;
+ return ep;
+ }
+ /* Allocate inner table (always 4KiB, all levels, all modes) */
+ new_pa = sbi_mpt_pool_alloc(RV64_TABLE_SIZE,
+ SBI_MPT_PAGE_SIZE);
+ if (!new_pa)
+ return 0;
+
+ rv64_write_mpte(ep, (u64)sbi_mpte_nonleaf(new_pa));
+ table_pa = new_pa;
+ }
+ else {
+ table_pa = rv64_next_table_pa(mpte);
+ }
+ }
+
+ *out_level = 0;
+
+ return rv64_mpte_pa(table_pa, rv64_table_idx(pa, 0, sch));
+}
+
+/*
+ * Generic map_range — shared by Smmpt43, Smmpt52, Smmpt64
+ *
+ * Maps a range [pa, pa+size] in the MPT table
+ */
+static int __rv64_map_range(struct sbi_mpt_domain *dom,
+ unsigned long pa, unsigned long size,
+ u8 xwr, u32 top_level)
+{
+ unsigned long cur = pa;
+ unsigned long end = pa + size;
+ unsigned long leaf_mpte_pa, mpte_range, mpte_base, mpte_end, batch_end;
+ u32 level, pn0, i, pg_first, pg_last, pg;
+ u64 napot, mpte;
+
+ while (cur < end) {
+ /*
+ * Check if the region qualifies for NAPOT range and the
+ * region is contained
+ */
+ if ((cur & (RV64_NAPOT_SIZE - 1)) == 0 &&
+ cur + RV64_NAPOT_SIZE > cur &&
+ cur + RV64_NAPOT_SIZE <= end) {
+ leaf_mpte_pa = rv64_walk_alloc(dom, cur, end - cur, &level, top_level);
+ if (!leaf_mpte_pa)
+ return SBI_ENOMEM;
+
+ pn0 = rv64_table_idx(cur, 0, dom->mode);
+
+ if (!RV64_NAPOT_DISABLE && level == 0 && (pn0 & (RV64_NAPOT_PN0_ALIGN - 1)) == 0) {
+ napot = rv64_napot_leaf_mpte(xwr);
+
+ for (i = 0; i < RV64_NAPOT_MPTE_COUNT; i++)
+ rv64_write_mpte(leaf_mpte_pa + i * RV64_MPTE_SIZE, napot);
+
+ cur += RV64_NAPOT_SIZE;
+ continue;
+ }
+ }
+ else {
+ leaf_mpte_pa = rv64_walk_alloc(dom, cur, end - cur, &level, top_level);
+ if (!leaf_mpte_pa)
+ return SBI_ENOMEM;
+ }
+
+ /* Normal path */
+ mpte_range = rv64_mpte_range(level);
+ pg_first = rv64_tuple_idx(cur, level);
+ mpte_base = cur & ~(mpte_range - 1);
+
+ mpte_end = mpte_base + mpte_range;
+ if (mpte_end < mpte_base)
+ mpte_end = ~0UL;
+
+ batch_end = (end < mpte_end) ? end : mpte_end;
+ pg_last = rv64_tuple_idx(batch_end - SBI_MPT_PAGE_SIZE, level);
+
+ /*
+ * If this level-0 MPTE is a Napot member, expand the
+ * whole naturally-aligned group to plain leaves and write same
+ * permissions xwr
+ */
+ mpte = rv64_read_mpte(leaf_mpte_pa);
+ if (level == 0 && (mpte & (u64)SBI_MPTE_N)) {
+ rv64_napot_demote(dom, leaf_mpte_pa, cur);
+ sbi_mpt_fence_sdid(dom->sdid);
+ mpte = rv64_read_mpte(leaf_mpte_pa);
+ }
+
+ if (!(mpte & (u64)(SBI_MPTE_V | SBI_MPTE_L)))
+ mpte = (u64)(SBI_MPTE_V | SBI_MPTE_L);
+
+ for (pg = pg_first; pg <= pg_last; pg++)
+ mpte = sbi_mpte_leaf_set_xwr(mpte, pg, xwr);
+
+ rv64_write_mpte(leaf_mpte_pa, mpte);
+
+ cur = batch_end;
+ }
+
+ return 0;
+}
+
+/*
+ * Per-mode mmpt encoding
+ */
+
+static unsigned long smmpt43_encode_mmpt(unsigned long ppn, u32 sdid)
+{
+ return sbi_mmpt_encode(SBI_MMPT_MODE_SMMPT43, sdid, ppn);
+}
+
+static unsigned long smmpt52_encode_mmpt(unsigned long ppn, u32 sdid)
+{
+ return sbi_mmpt_encode(SBI_MMPT_MODE_SMMPT52, sdid, ppn);
+}
+
+static unsigned long smmpt64_encode_mmpt(unsigned long ppn, u32 sdid)
+{
+ return sbi_mmpt_encode(SBI_MMPT_MODE_SMMPT64, sdid, ppn);
+}
+
+static unsigned long smmpt43_root_table_size(void)
+{
+ return RV64_TABLE_SIZE;
+}
+
+static unsigned long smmpt43_root_table_align(void)
+{
+ return SBI_MPT_PAGE_SIZE;
+}
+
+static bool smmpt43_pa_in_range(unsigned long pa, unsigned long size)
+{
+ unsigned long pa_max = 1UL << 43;
+
+ return (pa < pa_max && size <= pa_max - pa);
+}
+
+static unsigned long smmpt52_root_table_size(void)
+{
+ return RV64_TABLE_SIZE;
+}
+
+static unsigned long smmpt52_root_table_align(void)
+{
+ return SBI_MPT_PAGE_SIZE;
+}
+
+static bool smmpt52_pa_in_range(unsigned long pa, unsigned long size)
+{
+ unsigned long pa_max = 1UL << 52;
+
+ return (pa < pa_max && size <= pa_max - pa);
+}
+
+static unsigned long smmpt64_root_table_size(void)
+{
+ return RV64_ROOT_SMMPT64_SIZE;
+}
+
+static unsigned long smmpt64_root_table_align(void)
+{
+ return RV64_ROOT_SMMPT64_SIZE;
+}
+
+static bool smmpt64_pa_in_range(unsigned long pa,
+ unsigned long size)
+{
+ /*
+ * Smmpt64 covers the full 64-bit PA space.
+ */
+ return true;
+}
+
+/*
+ * Per-mode map_range wrappers
+ *
+ * Smmpt43:
+ * Root level 2, 9-bit, 512 entries, 4KiB root MPT size
+ *
+ * Smmpt52:
+ * Root level 3, 9-bit, 512 entries, 4KiB root MPT size
+ *
+ * Smmpt64:
+ * Root level 4, 12-bit, 4096 entries, 32KiB root MPT size
+ */
+
+static int smmpt43_map_range(struct sbi_mpt_domain *dom,
+ unsigned long pa, unsigned long size, u8 xwr)
+{
+ return __rv64_map_range(dom, pa, size, xwr, 2);
+}
+
+static int smmpt52_map_range(struct sbi_mpt_domain *dom,
+ unsigned long pa, unsigned long size, u8 xwr)
+{
+ return __rv64_map_range(dom, pa, size, xwr, 3);
+}
+
+static int smmpt64_map_range(struct sbi_mpt_domain *dom,
+ unsigned long pa, unsigned long size, u8 xwr)
+{
+ return __rv64_map_range(dom, pa, size, xwr, 4);
+}
+
+/*
+ * __rv64_map_full_range() — Write every MPTE at Root MPT
+ *
+ * Writes xwr into every root table MPTE directly as a leaf
+ * superpage covering the mode entire addressable PA range.
+ * No intermidiate MPT tables allocated just all leaf MPTEs at
+ * root level.
+ */
+
+static int __rv64_map_full_range(struct sbi_mpt_domain *dom, u8 xwr,
+ unsigned long root_entries)
+{
+ u32 pg;
+ unsigned long i;
+ u64 mpte = SBI_MPTE_V | SBI_MPTE_L;
+
+ for (pg = 0; pg < RV64_PAGES_PER_MPTE; pg++)
+ mpte = sbi_mpte_leaf_set_xwr(mpte, pg, xwr);
+
+ for (i = 0; i < root_entries; i++)
+ rv64_write_mpte(rv64_mpte_pa(dom->root_pa, i), mpte);
+
+ return 0;
+}
+
+static int smmpt43_map_full_range(struct sbi_mpt_domain *dom, u8 xwr)
+{
+ return __rv64_map_full_range(dom, xwr, RV64_INNER_ENTRIES);
+}
+
+static int smmpt52_map_full_range(struct sbi_mpt_domain *dom, u8 xwr)
+{
+ return __rv64_map_full_range(dom, xwr, RV64_INNER_ENTRIES);
+}
+
+static int smmpt64_map_full_range(struct sbi_mpt_domain *dom, u8 xwr)
+{
+ return __rv64_map_full_range(dom, xwr, RV64_ROOT_SMMPT64_ENTRIES);
+}
+
+/*
+ * Get the access permissions(xwr) of a PA
+ */
+static u8 __rv64_get_xwr(struct sbi_mpt_domain *dom, unsigned long pa,
+ u32 top_level)
+{
+ const struct sbi_mpt_mode *sch = dom->mode;
+ unsigned long table_pa = dom->root_pa;
+ u32 level, idx, pi;
+ u64 mpte;
+
+ for (level = top_level; ; level--) {
+ idx = rv64_table_idx(pa, level, sch);
+ mpte = rv64_read_mpte(rv64_mpte_pa(table_pa, idx));
+
+ if (!(mpte & SBI_MPTE_V))
+ return SBI_MPT_PERM_NONE;
+
+ if (!(mpte & SBI_MPTE_L) && (mpte & SBI_MPTE_N))
+ return SBI_MPT_PERM_NONE;
+
+ if (mpte & SBI_MPTE_L) {
+ if (mpte & SBI_MPTE_N)
+ return rv64_napot_leaf_xwr(mpte);
+
+ pi = rv64_tuple_idx(pa, level);
+
+ return (mpte >> sbi_mpte_xwr_shift(pi)) & SBI_MPTE_XWR_MASK;
+ }
+
+ if (level == 0)
+ return SBI_MPT_PERM_NONE;
+
+ table_pa = rv64_next_table_pa(mpte);
+ }
+
+ return SBI_MPT_PERM_NONE;
+}
+
+static u8 smmpt43_get_xwr(struct sbi_mpt_domain *dom, unsigned long pa)
+{
+ return __rv64_get_xwr(dom, pa, 2);
+}
+
+static u8 smmpt52_get_xwr(struct sbi_mpt_domain *dom, unsigned long pa)
+{
+ return __rv64_get_xwr(dom, pa, 3);
+}
+
+static u8 smmpt64_get_xwr(struct sbi_mpt_domain *dom, unsigned long pa)
+{
+ return __rv64_get_xwr(dom, pa, 4);
+}
+
+struct sbi_mpt_mode smmpt43_mode = {
+ .name = "Smmpt43",
+ .mode_val = SBI_MMPT_MODE_SMMPT43,
+ .encode_mmpt = smmpt43_encode_mmpt,
+ .map_range = smmpt43_map_range,
+ .root_table_size = smmpt43_root_table_size,
+ .root_table_align = smmpt43_root_table_align,
+ .pa_in_range = smmpt43_pa_in_range,
+ .map_full_range = smmpt43_map_full_range,
+ .get_xwr = smmpt43_get_xwr,
+};
+
+struct sbi_mpt_mode smmpt52_mode = {
+ .name = "Smmpt52",
+ .mode_val = SBI_MMPT_MODE_SMMPT52,
+ .encode_mmpt = smmpt52_encode_mmpt,
+ .map_range = smmpt52_map_range,
+ .root_table_size = smmpt52_root_table_size,
+ .root_table_align = smmpt52_root_table_align,
+ .pa_in_range = smmpt52_pa_in_range,
+ .map_full_range = smmpt52_map_full_range,
+ .get_xwr = smmpt52_get_xwr,
+};
+
+struct sbi_mpt_mode smmpt64_mode = {
+ .name = "Smmpt64",
+ .mode_val = SBI_MMPT_MODE_SMMPT64,
+ .encode_mmpt = smmpt64_encode_mmpt,
+ .map_range = smmpt64_map_range,
+ .root_table_size = smmpt64_root_table_size,
+ .root_table_align = smmpt64_root_table_align,
+ .pa_in_range = smmpt64_pa_in_range,
+ .map_full_range = smmpt64_map_full_range,
+ .get_xwr = smmpt64_get_xwr,
+};
+
+#else /* __riscv_xlen == 64 */
+
+/*
+ * Smmpt34 (RV32) — 34-bit supervisor physical address (SPA)
+ *
+ * +---------+---------+---------+--------+
+ * | pn[1] | pn[0] | pi | offset |
+ * | [33:25] | [24:15] | [14:12] | [11:0] |
+ * | 9b | 10b | 3b | 12b |
+ * +---------+---------+---------+--------+
+ *
+ */
+
+#define RV32_MPTE_SIZE 4UL
+#define RV32_ROOT_ENTRIES 512U
+#define RV32_LEAF_ENTRIES 1024U
+#define RV32_ROOT_SIZE (RV32_ROOT_ENTRIES * RV32_MPTE_SIZE)
+#define RV32_LEAF_SIZE (RV32_LEAF_ENTRIES * RV32_MPTE_SIZE)
+
+/* pn field widths */
+#define RV32_PN1_BITS 9U
+#define RV32_PN0_BITS 10U
+
+#define RV32_NUMPGINRANGE 3U
+#define RV32_PAGES_PER_MPTE (1U << RV32_NUMPGINRANGE)
+#define RV32_PAGES_PER_MPTE_MASK (RV32_PAGES_PER_MPTE - 1)
+
+/* Range covered by one MPTE at each level */
+#define RV32_LEAF_RANGE (RV32_PAGES_PER_MPTE * SBI_MPT_PAGE_SIZE)
+#define RV32_ROOT_RANGE (RV32_LEAF_RANGE << RV32_PN0_BITS)
+
+/* NAPOT constants (G=6) */
+#define RV32_NAPOT_G 6U
+#define RV32_NAPOT_G_SHIFT 12U
+#define RV32_NAPOT_COUNT (1U << (RV32_NAPOT_G + 1))
+#define RV32_NAPOT_SIZE (RV32_NAPOT_COUNT * RV32_LEAF_RANGE)
+#define RV32_NAPOT_PN0_ALIGN RV32_NAPOT_COUNT
+
+/*
+ * Swith to disable NAPOT compaction of MPTE entries while writing.
+ */
+#define RV32_NAPOT_DISABLE 0
+
+/*
+ * rv32_pn1_table_idx() — extract pn[1] from a SPA.
+ */
+static inline u32 rv32_pn1_table_idx(unsigned long pa)
+{
+ return ((pa >> 25) & 0x1FFU);
+}
+
+/*
+ * rv32_pn0_table_idx() — extract pn[0] from a SPA.
+ */
+static inline u32 rv32_pn0_table_idx(unsigned long pa)
+{
+ return ((pa >> 15) & 0x3FFU);
+}
+
+static inline u32 rv32_tuple_idx_shift(u32 level)
+{
+ return (level == 0) ? SBI_MPT_PAGE_SHIFT : SBI_MPT_PAGE_SHIFT + RV32_PN0_BITS;
+}
+
+static inline u32 rv32_tuple_idx(unsigned long pa, u32 level)
+{
+ return ((pa >> rv32_tuple_idx_shift(level)) & RV32_PAGES_PER_MPTE_MASK);
+}
+
+/*
+ * Bytes covered by one MPTE at the given level
+ */
+static inline unsigned long rv32_mpte_range(u32 level)
+{
+ return (level == 0) ? RV32_LEAF_RANGE : RV32_ROOT_RANGE;
+}
+
+/*
+ * RV32 MPTE Read/Write functions
+ */
+static inline u32 rv32_read_mpte(unsigned long pa)
+{
+ return *(volatile u32 *)pa;
+}
+
+static inline void rv32_write_mpte(unsigned long pa, u32 v)
+{
+ *(volatile u32 *)pa = v;
+}
+
+static inline unsigned long rv32_mpte_pa(unsigned long table_pa, u32 idx)
+{
+ return (table_pa + idx * RV32_MPTE_SIZE);
+}
+
+static inline unsigned long rv32_next_table_pa(u32 mpte)
+{
+ return ((mpte >> SBI_MPTE_PPN_SHIFT) << SBI_MPT_PAGE_SHIFT);
+}
+
+/*
+ * Generate Napot Leaf MPTE
+ *
+ * Constructs one MPTE value used for all RV32_NAPOT_COUNT entries
+ * in a NAPOT group.
+ *
+ * All MPTEs in the group are identical
+ */
+static inline u32 rv32_napot_leaf(u8 xwr)
+{
+ u32 mpte = SBI_MPTE_V | SBI_MPTE_L | SBI_MPTE_N;
+
+ mpte = sbi_mpte_leaf_set_xwr(mpte, 0, xwr);
+ mpte |= ((u32)RV32_NAPOT_G << RV32_NAPOT_G_SHIFT);
+
+ return mpte;
+}
+
+/*
+ * Get the xwr from a Napot leaf.
+ */
+static inline u8 rv32_napot_xwr(u32 mpte)
+{
+ return ((mpte >> SBI_MPTE_XWR_BASE) & SBI_MPTE_XWR_MASK);
+}
+
+/*
+ * rv32_napot_demote() — Convert a Napot MPTE group into RV64_NAPOT_MPTE_COUNT
+ * normal(Non-Napot) MPTE carrying the SAME permission.
+ *
+ * leaf_mpte_pa is any of the group MPTE PA
+ */
+static void rv32_napot_demote(struct sbi_mpt_domain *dom,
+ unsigned long leaf_mpte_pa, unsigned long pa)
+{
+ u32 pg, leaf, i;
+ unsigned long grp_base;
+ u8 xwr = rv32_napot_xwr(rv32_read_mpte(leaf_mpte_pa));
+ u32 pn0 = rv32_pn0_table_idx(pa);
+
+ grp_base = leaf_mpte_pa - (pn0 & (RV32_NAPOT_COUNT - 1)) * RV32_MPTE_SIZE;
+
+ leaf = (SBI_MPTE_V | SBI_MPTE_L); /* N=0 */
+
+ for (pg = 0; pg < RV32_PAGES_PER_MPTE; pg++)
+ leaf = sbi_mpte_leaf_set_xwr(leaf, pg, xwr);
+
+ for (i = 0; i < RV32_NAPOT_COUNT; i++)
+ rv32_write_mpte(grp_base + i * RV32_MPTE_SIZE, leaf);
+}
+
+static inline u32 rv32_best_level(unsigned long pa, unsigned long size)
+{
+ if (size >= RV32_ROOT_RANGE && (pa & (RV32_ROOT_RANGE - 1)) == 0)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * MPT Walk for RV32 Smmpt43
+ *
+ * Returns the PA of the MPTE covering pa
+ */
+static unsigned long rv32_walk_alloc(struct sbi_mpt_domain *dom,
+ unsigned long pa,
+ unsigned long size,
+ u32 *out_level)
+{
+ unsigned long root_ep = rv32_mpte_pa(dom->root_pa, rv32_pn1_table_idx(pa));
+ u32 root_mpte = rv32_read_mpte(root_ep);
+ u32 best = rv32_best_level(pa, size);
+ unsigned long leaf_pa;
+
+ if (root_mpte & (u32)SBI_MPTE_L) {
+ /* Existing root-level leaf */
+ *out_level = 1;
+ return root_ep;
+ }
+
+ if (!(root_mpte & (u32)SBI_MPTE_V) && best == 1) {
+ /* new entry, range fits at root level so skip leaf allocation */
+ *out_level = 1;
+ return root_ep;
+ }
+
+ if (!(root_mpte & (u32)SBI_MPTE_V)) {
+ /* Allocate leaf table from global heap */
+ leaf_pa = sbi_mpt_pool_alloc(RV32_LEAF_SIZE,
+ SBI_MPT_PAGE_SIZE);
+ if (!leaf_pa)
+ return 0;
+
+ rv32_write_mpte(root_ep, sbi_mpte_nonleaf(leaf_pa));
+ *out_level = 0;
+
+ return rv32_mpte_pa(leaf_pa, rv32_pn0_table_idx(pa));
+ }
+
+ /* Valid non-leaf: follow PPN to existing leaf table */
+ *out_level = 0;
+
+ return rv32_mpte_pa(rv32_next_table_pa(rv32_read_mpte(root_ep)), rv32_pn0_table_idx(pa));
+}
+
+/*
+ * smmpt34_map_range(): Maps a range [pa, pa+size] in the MPT table
+ */
+
+static int smmpt34_map_range(struct sbi_mpt_domain *dom,
+ unsigned long pa, unsigned long size, u8 xwr)
+{
+ unsigned long cur = pa;
+ unsigned long end = pa + size;
+ unsigned long leaf_mpte_pa;
+ unsigned long mpte_range, mpte_base, mpte_end, batch_end;
+ u32 level, pg_first, pg_last, pg, i;
+ u32 mpte, napot;
+
+ while (cur < end) {
+ /*
+ * Check if the region qualifies for NAPOT range and the
+ * region is contained
+ */
+ if ((cur & (RV32_NAPOT_SIZE - 1)) == 0 &&
+ cur + RV32_NAPOT_SIZE > cur &&
+ cur + RV32_NAPOT_SIZE <= end) {
+ leaf_mpte_pa = rv32_walk_alloc(dom, cur, end - cur, &level);
+ if (!leaf_mpte_pa)
+ return SBI_ENOMEM;
+
+ if (!RV32_NAPOT_DISABLE && level == 0 && (rv32_pn0_table_idx(cur) & (RV32_NAPOT_PN0_ALIGN - 1)) == 0) {
+ napot = rv32_napot_leaf(xwr);
+
+ for (i = 0; i < RV32_NAPOT_COUNT; i++)
+ rv32_write_mpte(leaf_mpte_pa + i * RV32_MPTE_SIZE, napot);
+
+ cur += RV32_NAPOT_SIZE;
+
+ continue;
+ }
+ } else {
+ leaf_mpte_pa = rv32_walk_alloc(dom, cur, end - cur, &level);
+ if (!leaf_mpte_pa)
+ return SBI_ENOMEM;
+ }
+
+ /* Normal path */
+ mpte_range = rv32_mpte_range(level);
+ pg_first = rv32_tuple_idx(cur, level);
+ mpte_base = cur & ~(mpte_range - 1UL);
+ mpte_end = mpte_base + mpte_range;
+ batch_end = (end < mpte_end) ? end : mpte_end;
+ pg_last = rv32_tuple_idx(batch_end - SBI_MPT_PAGE_SIZE, level);
+
+ /*
+ * If this level-0 MPTE is a Napot member, expand the
+ * whole naturally-aligned group to plain leaves and write same
+ * permissions xwr
+ */
+ mpte = rv32_read_mpte(leaf_mpte_pa);
+ if (level == 0 && (mpte & (u32)SBI_MPTE_N)) {
+ rv32_napot_demote(dom, leaf_mpte_pa, cur);
+ sbi_mpt_fence_sdid(dom->sdid);
+ mpte = rv32_read_mpte(leaf_mpte_pa);
+ }
+ if (!(mpte & (u32)(SBI_MPTE_V | SBI_MPTE_L)))
+ mpte = SBI_MPTE_V | SBI_MPTE_L;
+
+ for (pg = pg_first; pg <= pg_last; pg++)
+ mpte = sbi_mpte_leaf_set_xwr(mpte, pg, xwr);
+
+ rv32_write_mpte(leaf_mpte_pa, mpte);
+ cur = batch_end;
+ }
+
+ return 0;
+}
+
+/*
+ * mmpt encoding
+ */
+static unsigned long smmpt34_encode_mmpt(unsigned long ppn, u32 sdid)
+{
+ return sbi_mmpt_encode(SBI_MMPT_MODE_SMMPT34, sdid, ppn);
+}
+
+static unsigned long smmpt34_root_table_size(void)
+{
+ return RV32_ROOT_SIZE;
+}
+
+static unsigned long smmpt34_root_table_align(void)
+{
+ return SBI_MPT_PAGE_SIZE;
+}
+
+static bool smmpt34_pa_in_range(unsigned long pa, unsigned long size)
+{
+ /*
+ * Return true since 1 << 34 wil be UB
+ * in RV32 case and anyways variables are limited by RV32
+ * architectural width
+ */
+
+ return true;
+}
+
+/*
+ * smmpt34_map_full_range() — Write every MPTE at Root MPT
+ *
+ * Writes xwr into every root table MPTE directly as a leaf
+ * superpage covering the mode entire addressable PA range.
+ * No intermidiate MPT tables allocated just all leaf MPTEs at
+ * root level.
+ */
+
+static int smmpt34_map_full_range(struct sbi_mpt_domain *dom, u8 xwr)
+{
+ u32 mpte = SBI_MPTE_V | SBI_MPTE_L;
+ u32 pg, i;
+
+ for (pg = 0; pg < RV32_PAGES_PER_MPTE; pg++)
+ mpte = sbi_mpte_leaf_set_xwr(mpte, pg, xwr);
+
+ for (i = 0; i < RV32_ROOT_ENTRIES; i++)
+ rv32_write_mpte(rv32_mpte_pa(dom->root_pa, i), mpte);
+
+ return 0;
+}
+
+/* Get the access permissions(xwr) of a PA */
+static u8 smmpt34_get_xwr(struct sbi_mpt_domain *dom, unsigned long pa)
+{
+ u32 mpte, pi;
+ unsigned long table_pa = dom->root_pa;
+
+ mpte = rv32_read_mpte(rv32_mpte_pa(table_pa, rv32_pn1_table_idx(pa)));
+
+ if (!(mpte & SBI_MPTE_V))
+ return SBI_MPT_PERM_NONE;
+
+ if (!(mpte & SBI_MPTE_L) && (mpte & SBI_MPTE_N))
+ return SBI_MPT_PERM_NONE;
+
+ if (mpte & SBI_MPTE_L) {
+ if (mpte & SBI_MPTE_N)
+ return rv32_napot_xwr(mpte);
+
+ pi = rv32_tuple_idx(pa, 1);
+ return (mpte >> sbi_mpte_xwr_shift(pi)) & SBI_MPTE_XWR_MASK;
+ }
+
+ table_pa = rv32_next_table_pa(mpte);
+ mpte = rv32_read_mpte(rv32_mpte_pa(table_pa, rv32_pn0_table_idx(pa)));
+
+ if (!(mpte & SBI_MPTE_V) || !(mpte & SBI_MPTE_L))
+ return SBI_MPT_PERM_NONE;
+
+ if (mpte & SBI_MPTE_N)
+ return rv32_napot_xwr(mpte);
+
+ pi = rv32_tuple_idx(pa, 0);
+
+ return ((mpte >> sbi_mpte_xwr_shift(pi)) & SBI_MPTE_XWR_MASK);
+}
+
+struct sbi_mpt_mode smmpt34_mode = {
+ .name = "Smmpt34",
+ .mode_val = SBI_MMPT_MODE_SMMPT34,
+ .encode_mmpt = smmpt34_encode_mmpt,
+ .map_range = smmpt34_map_range,
+ .root_table_size = smmpt34_root_table_size,
+ .root_table_align = smmpt34_root_table_align,
+ .pa_in_range = smmpt34_pa_in_range,
+ .map_full_range = smmpt34_map_full_range,
+ .get_xwr = smmpt34_get_xwr,
+};
+
+#endif /* __riscv_xlen == 32 */
--
2.53.0
--
opensbi mailing list
opensbi@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/opensbi
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [RFC PATCH v4 4/4] lib: sbi: Initialize SMMPT during coldboot
2026-08-24 2:09 [RFC PATCH v4 0/4] Add Smsdid and Smmpt supervisor domain protection Rahul Pathak
` (2 preceding siblings ...)
2026-08-24 2:09 ` [RFC PATCH v4 3/4] mpt: Add Smsdid and Smmpt supervisor domain core Rahul Pathak
@ 2026-08-24 2:09 ` Rahul Pathak
2026-08-24 5:30 ` Ranbir Singh
3 siblings, 1 reply; 8+ messages in thread
From: Rahul Pathak @ 2026-08-24 2:09 UTC (permalink / raw)
To: opensbi; +Cc: rahul.pathak, rahul, Rahul Pathak
Call sbi_mpt_init() function from coldboot path of
sbi_hart_init() to initialize the SMMPT core. It will
create and the MPT tables for each SBI domain and maps
its memregions with appropriate permissions in MPT tables.
Also each MPT table requires to memory to install tables
so reserve more space in heap for MPT tables
Signed-off-by: Rahul Pathak <rahul.pathak@oss.qualcomm.com>
---
lib/sbi/sbi_hart.c | 8 +++++++-
platform/generic/platform.c | 9 +++++++++
2 files changed, 16 insertions(+), 1 deletion(-)
diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c
index 4261fea8..cc18b2c5 100644
--- a/lib/sbi/sbi_hart.c
+++ b/lib/sbi/sbi_hart.c
@@ -1,4 +1,4 @@
-/*
+/*sbi_hart.
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2019 Western Digital Corporation or its affiliates.
@@ -21,6 +21,7 @@
#include <sbi/sbi_pmu.h>
#include <sbi/sbi_string.h>
#include <sbi/sbi_trap.h>
+#include <sbi/sbi_hart_mpt.h>
extern void __sbi_expected_trap(void);
extern void __sbi_expected_trap_hext(void);
@@ -731,6 +732,11 @@ int sbi_hart_init(struct sbi_scratch *scratch, bool cold_boot)
rc = sbi_hart_pmp_init(scratch);
if (rc)
return rc;
+
+ /* Smmpt is optional. Continue if the Smmpt is not present. */
+ rc = sbi_mpt_init();
+ if (rc && rc != SBI_ENODEV)
+ return rc;
}
return sbi_hart_reinit(scratch);
diff --git a/platform/generic/platform.c b/platform/generic/platform.c
index 1df0280d..ccf6f756 100644
--- a/platform/generic/platform.c
+++ b/platform/generic/platform.c
@@ -42,6 +42,15 @@ static u32 fw_platform_calculate_heap_size(u32 hart_count)
/* For TLB fifo */
heap_size += SBI_TLB_INFO_SIZE * (hart_count) * (hart_count);
+ /*
+ * MPT table budget
+ * 1 MiB memory for MPT allocated currently.
+ *
+ * TODO: Need better way to get the memory budget based on active
+ * SMMPT mode.
+ */
+ heap_size += 1024 * 1024;
+
return BIT_ALIGN(heap_size, HEAP_BASE_ALIGN);
}
--
2.53.0
--
opensbi mailing list
opensbi@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/opensbi
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [RFC PATCH v4 4/4] lib: sbi: Initialize SMMPT during coldboot
2026-08-24 2:09 ` [RFC PATCH v4 4/4] lib: sbi: Initialize SMMPT during coldboot Rahul Pathak
@ 2026-08-24 5:30 ` Ranbir Singh
2026-08-24 5:43 ` Rahul Pathak
0 siblings, 1 reply; 8+ messages in thread
From: Ranbir Singh @ 2026-08-24 5:30 UTC (permalink / raw)
To: Rahul Pathak; +Cc: opensbi, rahul.pathak, rahul
On Mon, Aug 24, 2026 at 7:41 AM Rahul Pathak
<rahul.pathak@oss.qualcomm.com> wrote:
>
> Call sbi_mpt_init() function from coldboot path of
> sbi_hart_init() to initialize the SMMPT core. It will
> create and the MPT tables for each SBI domain and maps
> its memregions with appropriate permissions in MPT tables.
>
> Also each MPT table requires to memory to install tables
> so reserve more space in heap for MPT tables
>
> Signed-off-by: Rahul Pathak <rahul.pathak@oss.qualcomm.com>
> ---
> lib/sbi/sbi_hart.c | 8 +++++++-
> platform/generic/platform.c | 9 +++++++++
> 2 files changed, 16 insertions(+), 1 deletion(-)
>
> diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c
> index 4261fea8..cc18b2c5 100644
> --- a/lib/sbi/sbi_hart.c
> +++ b/lib/sbi/sbi_hart.c
> @@ -1,4 +1,4 @@
> -/*
> +/*sbi_hart.
RS: Looks like this is mistakenly added.
> * SPDX-License-Identifier: BSD-2-Clause
> *
> * Copyright (c) 2019 Western Digital Corporation or its affiliates.
> @@ -21,6 +21,7 @@
> #include <sbi/sbi_pmu.h>
> #include <sbi/sbi_string.h>
> #include <sbi/sbi_trap.h>
> +#include <sbi/sbi_hart_mpt.h>
>
> extern void __sbi_expected_trap(void);
> extern void __sbi_expected_trap_hext(void);
> @@ -731,6 +732,11 @@ int sbi_hart_init(struct sbi_scratch *scratch, bool cold_boot)
> rc = sbi_hart_pmp_init(scratch);
> if (rc)
> return rc;
> +
> + /* Smmpt is optional. Continue if the Smmpt is not present. */
> + rc = sbi_mpt_init();
> + if (rc && rc != SBI_ENODEV)
> + return rc;
> }
>
> return sbi_hart_reinit(scratch);
> diff --git a/platform/generic/platform.c b/platform/generic/platform.c
> index 1df0280d..ccf6f756 100644
> --- a/platform/generic/platform.c
> +++ b/platform/generic/platform.c
> @@ -42,6 +42,15 @@ static u32 fw_platform_calculate_heap_size(u32 hart_count)
> /* For TLB fifo */
> heap_size += SBI_TLB_INFO_SIZE * (hart_count) * (hart_count);
>
> + /*
> + * MPT table budget
> + * 1 MiB memory for MPT allocated currently.
> + *
> + * TODO: Need better way to get the memory budget based on active
> + * SMMPT mode.
> + */
> + heap_size += 1024 * 1024;
RS: See if it is better to define and use macros here like
#define MB(x) (x * 1024 * 1024)
heap_size += MB(1)
> +
> return BIT_ALIGN(heap_size, HEAP_BASE_ALIGN);
> }
>
> --
> 2.53.0
>
>
> --
> opensbi mailing list
> opensbi@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/opensbi
--
opensbi mailing list
opensbi@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/opensbi
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [RFC PATCH v4 4/4] lib: sbi: Initialize SMMPT during coldboot
2026-08-24 5:30 ` Ranbir Singh
@ 2026-08-24 5:43 ` Rahul Pathak
0 siblings, 0 replies; 8+ messages in thread
From: Rahul Pathak @ 2026-08-24 5:43 UTC (permalink / raw)
To: Ranbir Singh; +Cc: opensbi, rahul.pathak, rahul
On Mon, Aug 24, 2026 at 11:00 AM Ranbir Singh
<ranbir.singh@oss.qualcomm.com> wrote:
>
> On Mon, Aug 24, 2026 at 7:41 AM Rahul Pathak
> <rahul.pathak@oss.qualcomm.com> wrote:
> >
> > Call sbi_mpt_init() function from coldboot path of
> > sbi_hart_init() to initialize the SMMPT core. It will
> > create and the MPT tables for each SBI domain and maps
> > its memregions with appropriate permissions in MPT tables.
> >
> > Also each MPT table requires to memory to install tables
> > so reserve more space in heap for MPT tables
> >
> > Signed-off-by: Rahul Pathak <rahul.pathak@oss.qualcomm.com>
> > ---
> > lib/sbi/sbi_hart.c | 8 +++++++-
> > platform/generic/platform.c | 9 +++++++++
> > 2 files changed, 16 insertions(+), 1 deletion(-)
> >
> > diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c
> > index 4261fea8..cc18b2c5 100644
> > --- a/lib/sbi/sbi_hart.c
> > +++ b/lib/sbi/sbi_hart.c
> > @@ -1,4 +1,4 @@
> > -/*
> > +/*sbi_hart.
>
> RS: Looks like this is mistakenly added.
Yes, typo, will correct it
>
> > * SPDX-License-Identifier: BSD-2-Clause
> > *
> > * Copyright (c) 2019 Western Digital Corporation or its affiliates.
> > @@ -21,6 +21,7 @@
> > #include <sbi/sbi_pmu.h>
> > #include <sbi/sbi_string.h>
> > #include <sbi/sbi_trap.h>
> > +#include <sbi/sbi_hart_mpt.h>
> >
> > extern void __sbi_expected_trap(void);
> > extern void __sbi_expected_trap_hext(void);
> > @@ -731,6 +732,11 @@ int sbi_hart_init(struct sbi_scratch *scratch, bool cold_boot)
> > rc = sbi_hart_pmp_init(scratch);
> > if (rc)
> > return rc;
> > +
> > + /* Smmpt is optional. Continue if the Smmpt is not present. */
> > + rc = sbi_mpt_init();
> > + if (rc && rc != SBI_ENODEV)
> > + return rc;
> > }
> >
> > return sbi_hart_reinit(scratch);
> > diff --git a/platform/generic/platform.c b/platform/generic/platform.c
> > index 1df0280d..ccf6f756 100644
> > --- a/platform/generic/platform.c
> > +++ b/platform/generic/platform.c
> > @@ -42,6 +42,15 @@ static u32 fw_platform_calculate_heap_size(u32 hart_count)
> > /* For TLB fifo */
> > heap_size += SBI_TLB_INFO_SIZE * (hart_count) * (hart_count);
> >
> > + /*
> > + * MPT table budget
> > + * 1 MiB memory for MPT allocated currently.
> > + *
> > + * TODO: Need better way to get the memory budget based on active
> > + * SMMPT mode.
> > + */
> > + heap_size += 1024 * 1024;
>
> RS: See if it is better to define and use macros here like
I agree, will update
> #define MB(x) (x * 1024 * 1024)
>
> heap_size += MB(1)
>
> > +
> > return BIT_ALIGN(heap_size, HEAP_BASE_ALIGN);
> > }
> >
> > --
> > 2.53.0
> >
> >
> > --
> > opensbi mailing list
> > opensbi@lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/opensbi
--
opensbi mailing list
opensbi@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/opensbi
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [RFC PATCH v4 1/4] lib: sbi_domain: Add finalize callback for per-domain state
2026-08-24 2:09 ` [RFC PATCH v4 1/4] lib: sbi_domain: Add finalize callback for per-domain state Rahul Pathak
@ 2026-08-25 16:19 ` Pawandeep Oza
0 siblings, 0 replies; 8+ messages in thread
From: Pawandeep Oza @ 2026-08-25 16:19 UTC (permalink / raw)
To: Rahul Pathak; +Cc: opensbi, rahul.pathak, rahul
On Sun, Aug 23, 2026 at 7:10 PM Rahul Pathak
<rahul.pathak@oss.qualcomm.com> wrote:
>
> Per-domain state is registered via sbi_domain_state in state_setup()
> but during that time the domain memory regions are not final.
> Add optional state_finalize() callback which is called
> from sbi_domain_finalize for each domain after all domains are
> registered and their memory regions are final.
>
> Signed-off-by: Rahul Pathak <rahul.pathak@oss.qualcomm.com>
> ---
> include/sbi/sbi_domain_state.h | 22 ++++++++++++++++++++++
> lib/sbi/sbi_domain.c | 16 ++++++++++++++++
> lib/sbi/sbi_domain_state.c | 25 +++++++++++++++++++++++++
> 3 files changed, 63 insertions(+)
>
> diff --git a/include/sbi/sbi_domain_state.h b/include/sbi/sbi_domain_state.h
> index 72030380..6528a95b 100644
> --- a/include/sbi/sbi_domain_state.h
> +++ b/include/sbi/sbi_domain_state.h
> @@ -40,6 +40,18 @@ struct sbi_domain_state {
> /** Optional callback to setup domain state */
> int (*state_setup)(struct sbi_domain *dom,
> struct sbi_domain_state *state, void *state_ptr);
> + /**
> + * Optional callback to finalize domain state
> + *
> + * Called for each domain from sbi_domain_finalize() after all
> + * domains are registered and memory regions are final.
> + *
> + * State from the domain memory regions must be setup here instead
> + * of state_setup()
> + */
> + int (*state_finalize)(struct sbi_domain *dom,
> + struct sbi_domain_state *state, void *state_ptr);
> +
> /** Optional callback to cleanup domain state */
> void (*state_cleanup)(struct sbi_domain *dom,
> struct sbi_domain_state *state, void *state_ptr);
> @@ -64,6 +76,16 @@ void *sbi_domain_state_ptr(struct sbi_domain *dom, struct sbi_domain_state *stat
> */
> int sbi_domain_setup_state(struct sbi_domain *dom);
>
> +/**
> + * Finalize all domain state for a domain
> + * @param dom pointer to domain
> + *
> + * @return 0 on success and negative error code on failure
> + *
> + * Note: This function is used internally within domain framework.
> + */
> +int sbi_domain_finalize_state(struct sbi_domain *dom);
> +
> /**
> * Cleanup all domain state for a domain
> * @param dom pointer to domain
> diff --git a/lib/sbi/sbi_domain.c b/lib/sbi/sbi_domain.c
> index 79d61c54..aa85d736 100644
> --- a/lib/sbi/sbi_domain.c
> +++ b/lib/sbi/sbi_domain.c
> @@ -845,6 +845,7 @@ int sbi_domain_startup(struct sbi_scratch *scratch, u32 cold_hartid)
> int sbi_domain_finalize(struct sbi_scratch *scratch)
> {
> int rc;
> + struct sbi_domain *dom;
> const struct sbi_platform *plat = sbi_platform_ptr(scratch);
>
> /* Sanity checks */
> @@ -865,6 +866,21 @@ int sbi_domain_finalize(struct sbi_scratch *scratch)
> */
> domain_finalized = true;
>
> + /*
> + * Finalize per-domain state of each domain. Now all domains
> + * are finalized already and their memory regions are final.
> + * State which is derived from the domain memory regions is
> + * set up below.
> + */
> + sbi_domain_for_each(dom) {
> + rc = sbi_domain_finalize_state(dom);
> + if (rc) {
> + sbi_printf("%s: domain state finalize failed for %s"
> + " (error %d)\n", __func__, dom->name, rc);
> + return rc;
> + }
> + }
> +
> return 0;
> }
Oza:
I am not sure if this infrastructure would be useful to you for state
finalization. but have a look if you think you could reuse this
notifier infrastructure which is inflight athe the moment.
refer to this patch. this notifier is called before the
[PATCH v2 1/3] lib: sbi: domain: add domain registration notifier infrastructure
but if you look sbi_domain_finalize calls sbi_platform_domains_init
which in turn calls sbi_platform_ops(plat)->domains_init();
and generic_domains_init will eventually call into
fdt_domains_populate and during domina_register this call back
notifier will be called.
have a look to see if it makes sense to use it ? I could be missing
some subtle thing though here.
>
> diff --git a/lib/sbi/sbi_domain_state.c b/lib/sbi/sbi_domain_state.c
> index 2d1f30e3..f8ccab69 100644
> --- a/lib/sbi/sbi_domain_state.c
> +++ b/lib/sbi/sbi_domain_state.c
> @@ -84,6 +84,31 @@ int sbi_domain_setup_state(struct sbi_domain *dom)
> return 0;
> }
>
> +int sbi_domain_finalize_state(struct sbi_domain *dom)
> +{
> + struct sbi_domain_state *state;
> + void *state_ptr;
> + int rc;
> +
> + if (!dom)
> + return SBI_EINVAL;
> +
> + sbi_list_for_each_entry(state, &state_list, head) {
> + if (!state->state_finalize)
> + continue;
> +
> + state_ptr = sbi_domain_state_ptr(dom, state);
> + if (!state_ptr)
> + continue;
> +
> + rc = state->state_finalize(dom, state, state_ptr);
> + if (rc)
> + return rc;
> + }
> +
> + return 0;
> +}
> +
> void sbi_domain_cleanup_state(struct sbi_domain *dom)
> {
> struct sbi_domain_state *state;
> --
> 2.53.0
>
>
> --
> opensbi mailing list
> opensbi@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/opensbi
--
opensbi mailing list
opensbi@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/opensbi
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-08-25 16:19 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-24 2:09 [RFC PATCH v4 0/4] Add Smsdid and Smmpt supervisor domain protection Rahul Pathak
2026-08-24 2:09 ` [RFC PATCH v4 1/4] lib: sbi_domain: Add finalize callback for per-domain state Rahul Pathak
2026-08-25 16:19 ` Pawandeep Oza
2026-08-24 2:09 ` [RFC PATCH v4 2/4] riscv: Add Smsdid and Smmpt hart extensions Rahul Pathak
2026-08-24 2:09 ` [RFC PATCH v4 3/4] mpt: Add Smsdid and Smmpt supervisor domain core Rahul Pathak
2026-08-24 2:09 ` [RFC PATCH v4 4/4] lib: sbi: Initialize SMMPT during coldboot Rahul Pathak
2026-08-24 5:30 ` Ranbir Singh
2026-08-24 5:43 ` Rahul Pathak
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.