* [RFC PATCH v2 0/5] Add Smsdid and Smmpt supervisor domain protection
@ 2026-07-27 13:55 Rahul Pathak
2026-07-27 13:55 ` [RFC PATCH v2 1/5] riscv: Add Smsdid and Smmpt hart extensions Rahul Pathak
` (4 more replies)
0 siblings, 5 replies; 11+ messages in thread
From: Rahul Pathak @ 2026-07-27 13:55 UTC (permalink / raw)
To: opensbi; +Cc: rahul.pathak, rahul
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.
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
Please note that extra some logs in OpenSBI and Qemu are added for testing
which will be removed later.
Boot Logs:
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_v2
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.
v1 -> v2:
1. Rebase to latest master
2. Change probe order between mode and sdidlen.
3. Add sbi_mpt_query_access() function.
4. Formatting changes.
Rahul Pathak (5):
riscv: Add Smsdid and Smmpt hart extensions
mpt: Add Smsdid and Smmpt supervisor domain core
platform/generic: Initialise MPT at boot
sbi: hsm: Activate MPT for domain on hart start/resume
sbi: init: Print supervisor domains at boot
include/sbi/sbi_hart.h | 4 +
include/sbi/sbi_mpt.h | 334 ++++++++++++
lib/sbi/objects.mk | 2 +
lib/sbi/sbi_hart.c | 2 +
lib/sbi/sbi_hsm.c | 7 +
lib/sbi/sbi_init.c | 4 +
lib/sbi/sbi_mpt.c | 926 +++++++++++++++++++++++++++++++++
lib/sbi/sbi_mpt_mode.c | 997 ++++++++++++++++++++++++++++++++++++
platform/generic/platform.c | 101 ++++
9 files changed, 2377 insertions(+)
create mode 100644 include/sbi/sbi_mpt.h
create mode 100644 lib/sbi/sbi_mpt.c
create mode 100644 lib/sbi/sbi_mpt_mode.c
--
2.51.0
--
opensbi mailing list
opensbi@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/opensbi
^ permalink raw reply [flat|nested] 11+ messages in thread* [RFC PATCH v2 1/5] riscv: Add Smsdid and Smmpt hart extensions 2026-07-27 13:55 [RFC PATCH v2 0/5] Add Smsdid and Smmpt supervisor domain protection Rahul Pathak @ 2026-07-27 13:55 ` Rahul Pathak 2026-07-27 13:56 ` [RFC PATCH v2 2/5] mpt: Add Smsdid and Smmpt supervisor domain core Rahul Pathak ` (3 subsequent siblings) 4 siblings, 0 replies; 11+ messages in thread From: Rahul Pathak @ 2026-07-27 13:55 UTC (permalink / raw) To: opensbi; +Cc: rahul.pathak, rahul, 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.51.0 -- opensbi mailing list opensbi@lists.infradead.org http://lists.infradead.org/mailman/listinfo/opensbi ^ permalink raw reply related [flat|nested] 11+ messages in thread
* [RFC PATCH v2 2/5] mpt: Add Smsdid and Smmpt supervisor domain core 2026-07-27 13:55 [RFC PATCH v2 0/5] Add Smsdid and Smmpt supervisor domain protection Rahul Pathak 2026-07-27 13:55 ` [RFC PATCH v2 1/5] riscv: Add Smsdid and Smmpt hart extensions Rahul Pathak @ 2026-07-27 13:56 ` Rahul Pathak 2026-08-14 18:28 ` Pawandeep Oza 2026-07-27 13:56 ` [RFC PATCH v2 3/5] platform/generic: Initialise MPT at boot Rahul Pathak ` (2 subsequent siblings) 4 siblings, 1 reply; 11+ messages in thread From: Rahul Pathak @ 2026-07-27 13:56 UTC (permalink / raw) To: opensbi; +Cc: rahul.pathak, rahul Introduce the supervisord omain 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> --- include/sbi/sbi_mpt.h | 334 ++++++++++++++ lib/sbi/objects.mk | 2 + lib/sbi/sbi_mpt.c | 926 ++++++++++++++++++++++++++++++++++++++ lib/sbi/sbi_mpt_mode.c | 997 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 2259 insertions(+) create mode 100644 include/sbi/sbi_mpt.h create mode 100644 lib/sbi/sbi_mpt.c create mode 100644 lib/sbi/sbi_mpt_mode.c diff --git a/include/sbi/sbi_mpt.h b/include/sbi/sbi_mpt.h new file mode 100644 index 00000000..7d17bd80 --- /dev/null +++ b/include/sbi/sbi_mpt.h @@ -0,0 +1,334 @@ +/* SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 Qualcomm Inc. + * + * Authors: Rahul Pathak <rahul.pathak@oss.qualcomm.com> + */ + +#ifndef __SBI_MPT_H__ +#define __SBI_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; + 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; +} + +/* + * Domain creation config + * + * sbi_dom and regions are mutually exclusive. + * when a supervisor domain is backed by a SBI domain + * then the regions list and access permissions are derived + * from the SBI domain structures. + */ +struct sbi_mpt_domain_config { + /* SBI domain whose S/U memregions are mapped. */ + struct sbi_domain *sbi_dom; + /* permission for regions inherited from sbi_dom */ + u8 xwr; + /* protect firmware regions for the domain */ + bool fw_protect; + /* + * explicit region list mapped in MPT table. + * NULL when sbi_dom covers everything + */ + struct sbi_mpt_region *regions; + u32 nregions; +}; + +int sbi_mpt_init(void); + +int sbi_mpt_domain_create(const struct sbi_mpt_domain_config *cfg, u32 *out_sdid); + +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); + +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_MPT_H__ */ diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk index c29c888f..3b3c3ea3 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_mpt.o +libsbi-objs-y += sbi_mpt_mode.o diff --git a/lib/sbi/sbi_mpt.c b/lib/sbi/sbi_mpt.c new file mode 100644 index 00000000..306f82db --- /dev/null +++ b/lib/sbi/sbi_mpt.c @@ -0,0 +1,926 @@ +/* SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 Qualcomm Inc. + * + * Authors: Rahul Pathak <rahul.pathak@oss.qualcomm.com> + */ + +#include <sbi/sbi_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/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].valid ? &mpt_ctrl.domains[sdid] : 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; +} + +/* + * sbi_mpt_domain_create(): Create a Supervisor Domain + */ +int sbi_mpt_domain_create(const struct sbi_mpt_domain_config *cfg, + u32 *out_sdid) +{ + int rc; + u32 sdid, i; + unsigned long root_pa; + struct sbi_mpt_mode *sch; + struct sbi_mpt_domain *dom; + const struct sbi_domain_memregion *mr; + struct sbi_mpt_ctrl *ctrl = &mpt_ctrl; + u8 base_xwr = SBI_MPT_PERM_NONE; + + if (!ctrl->ready) + return SBI_ENODEV; + + if (cfg->sbi_dom && cfg->nregions) { + sbi_printf("sbi_mpt: sbi_dom and explicit regions are mutually exclusive\n"); + return SBI_EINVAL; + } + + /* + * sdid_bitmap empty means every available SDID is + * currently in use and no domain can be created. + */ + if (bitmap_empty(ctrl->sdid_bitmap, ctrl->max_domains)) + return SBI_ENOMEM; + + 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\n"); + return SBI_ENOMEM; + } + + /* + * Root MPT memory allocated — take SDID from bitmap for root supervisor + * domain + */ + for (sdid = 0; sdid < ctrl->max_domains; sdid++) + if (bitmap_test(ctrl->sdid_bitmap, sdid)) + break; + bitmap_clear(ctrl->sdid_bitmap, sdid, 1); + + dom = &ctrl->domains[sdid]; + SPIN_LOCK_INIT(dom->lock); + + dom->sdid = sdid; + dom->valid = true; + dom->root_pa = root_pa; + dom->mode = sch; + dom->sbi_dom = cfg->sbi_dom; + dom->nregions = 0; + + if (cfg->sbi_dom) { + /* + * 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(cfg->sbi_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, cfg->xwr); + rc = sch->map_full_range(dom, base_xwr); + if (rc) + goto fail; + + if (dom->nregions < SBI_MPT_MAX_REGIONS_DOMAIN) { + struct sbi_mpt_region *r = &dom->regions[dom->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(cfg->sbi_dom, mr) { + if (mr->order >= sizeof(unsigned long) * 8) + continue; + + rc = mpt_map_range_perm(dom, sch, mr->base, BIT(mr->order), + mpt_xwr_from_memregion(mr, cfg->xwr), false); + if (rc) + goto fail; + } + } + + /* Map explicit regions which are not part of any SBI domain */ + for (i = 0; i < cfg->nregions; i++) { + rc = mpt_map_range_perm(dom, sch, cfg->regions[i].pa, cfg->regions[i].size, + cfg->regions[i].xwr, false); + if (rc) + goto fail; + } + + /* + * Map firmware region with no permission with and mark as locked + */ + if (cfg->fw_protect) { + rc = mpt_map_range_perm(dom, 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); + goto fail; + } + } + + ctrl->ndomain++; + if (out_sdid) + *out_sdid = sdid; + + sbi_mpt_fence_sdid(sdid); + + /* To be removed */ + sbi_printf("sbi_mpt: SDID %u root=0x%lx mode=%s dom=%s\n", + sdid, root_pa, sch->name, cfg->sbi_dom ? cfg->sbi_dom->name : "(none)"); + return 0; + +fail: + bitmap_set(ctrl->sdid_bitmap, (int)sdid, 1); + dom->valid = false; + return rc; +} + +/* + * 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 + */ +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) +{ + u32 s; + + if (!mpt_ctrl.ready) + return SBI_ENODEV; + if (!sbi_dom) + return SBI_EINVAL; + + /* + * Search all supervisor domains for associated sbi_dom and + * activate harts for that supervisor domain + */ + for (s = 0; s < mpt_ctrl.max_domains; s++) { + if (mpt_ctrl.domains[s].valid && mpt_ctrl.domains[s].sbi_dom == sbi_dom) + return sbi_mpt_hart_activate(s); + } + + return 0; +} + +/* + * 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].valid = false; +} + +/* + * 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) +{ + 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; + + 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->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_mode.c b/lib/sbi/sbi_mpt_mode.c new file mode 100644 index 00000000..8baf83a3 --- /dev/null +++ b/lib/sbi/sbi_mpt_mode.c @@ -0,0 +1,997 @@ +/* SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 Qualcomm Inc. + * + * Authors: Rahul Pathak <rahul.pathak@oss.qualcomm.com> + */ + +#include <sbi/sbi_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) + +/* + * 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 >> 5); + 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.51.0 -- opensbi mailing list opensbi@lists.infradead.org http://lists.infradead.org/mailman/listinfo/opensbi ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [RFC PATCH v2 2/5] mpt: Add Smsdid and Smmpt supervisor domain core 2026-07-27 13:56 ` [RFC PATCH v2 2/5] mpt: Add Smsdid and Smmpt supervisor domain core Rahul Pathak @ 2026-08-14 18:28 ` Pawandeep Oza 2026-08-22 12:18 ` Rahul Pathak 0 siblings, 1 reply; 11+ messages in thread From: Pawandeep Oza @ 2026-08-14 18:28 UTC (permalink / raw) To: Rahul Pathak; +Cc: opensbi, rahul On Mon, Jul 27, 2026 at 6:56 AM Rahul Pathak <rahul.pathak@oss.qualcomm.com> wrote: > > Introduce the supervisord omain memory-protection (Smmpt) table Oza: typo: "supervisord omain" > 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> > --- > include/sbi/sbi_mpt.h | 334 ++++++++++++++ > lib/sbi/objects.mk | 2 + > lib/sbi/sbi_mpt.c | 926 ++++++++++++++++++++++++++++++++++++++ > lib/sbi/sbi_mpt_mode.c | 997 +++++++++++++++++++++++++++++++++++++++++ > 4 files changed, 2259 insertions(+) > create mode 100644 include/sbi/sbi_mpt.h > create mode 100644 lib/sbi/sbi_mpt.c > create mode 100644 lib/sbi/sbi_mpt_mode.c > > diff --git a/include/sbi/sbi_mpt.h b/include/sbi/sbi_mpt.h > new file mode 100644 > index 00000000..7d17bd80 > --- /dev/null > +++ b/include/sbi/sbi_mpt.h > @@ -0,0 +1,334 @@ > +/* SPDX-License-Identifier: BSD-2-Clause > + * > + * Copyright (c) 2026 Qualcomm Inc. > + * > + * Authors: Rahul Pathak <rahul.pathak@oss.qualcomm.com> > + */ > + > +#ifndef __SBI_MPT_H__ > +#define __SBI_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; > + 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; > +} > + > +/* > + * Domain creation config > + * > + * sbi_dom and regions are mutually exclusive. > + * when a supervisor domain is backed by a SBI domain > + * then the regions list and access permissions are derived > + * from the SBI domain structures. > + */ > +struct sbi_mpt_domain_config { > + /* SBI domain whose S/U memregions are mapped. */ > + struct sbi_domain *sbi_dom; > + /* permission for regions inherited from sbi_dom */ > + u8 xwr; > + /* protect firmware regions for the domain */ > + bool fw_protect; > + /* > + * explicit region list mapped in MPT table. > + * NULL when sbi_dom covers everything > + */ > + struct sbi_mpt_region *regions; > + u32 nregions; > +}; > + > +int sbi_mpt_init(void); > + > +int sbi_mpt_domain_create(const struct sbi_mpt_domain_config *cfg, u32 *out_sdid); > + > +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); > + > +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_MPT_H__ */ > diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk > index c29c888f..3b3c3ea3 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_mpt.o > +libsbi-objs-y += sbi_mpt_mode.o > diff --git a/lib/sbi/sbi_mpt.c b/lib/sbi/sbi_mpt.c > new file mode 100644 > index 00000000..306f82db > --- /dev/null > +++ b/lib/sbi/sbi_mpt.c > @@ -0,0 +1,926 @@ > +/* SPDX-License-Identifier: BSD-2-Clause > + * > + * Copyright (c) 2026 Qualcomm Inc. > + * > + * Authors: Rahul Pathak <rahul.pathak@oss.qualcomm.com> > + */ > + > +#include <sbi/sbi_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/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].valid ? &mpt_ctrl.domains[sdid] : 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; > +} > + > +/* > + * sbi_mpt_domain_create(): Create a Supervisor Domain Oza: "Create a Supervisor Domain" ? You probably meant "Create an mpt domain" ! > + */ > +int sbi_mpt_domain_create(const struct sbi_mpt_domain_config *cfg, > + u32 *out_sdid) > +{ > + int rc; > + u32 sdid, i; > + unsigned long root_pa; > + struct sbi_mpt_mode *sch; > + struct sbi_mpt_domain *dom; > + const struct sbi_domain_memregion *mr; > + struct sbi_mpt_ctrl *ctrl = &mpt_ctrl; > + u8 base_xwr = SBI_MPT_PERM_NONE; > + > + if (!ctrl->ready) > + return SBI_ENODEV; > + > + if (cfg->sbi_dom && cfg->nregions) { > + sbi_printf("sbi_mpt: sbi_dom and explicit regions are mutually exclusive\n"); > + return SBI_EINVAL; > + } > + > + /* > + * sdid_bitmap empty means every available SDID is > + * currently in use and no domain can be created. > + */ > + if (bitmap_empty(ctrl->sdid_bitmap, ctrl->max_domains)) > + return SBI_ENOMEM; > + > + 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\n"); > + return SBI_ENOMEM; > + } > + > + /* > + * Root MPT memory allocated — take SDID from bitmap for root supervisor > + * domain > + */ > + for (sdid = 0; sdid < ctrl->max_domains; sdid++) > + if (bitmap_test(ctrl->sdid_bitmap, sdid)) > + break; > + bitmap_clear(ctrl->sdid_bitmap, sdid, 1); > + > + dom = &ctrl->domains[sdid]; > + SPIN_LOCK_INIT(dom->lock); > + > + dom->sdid = sdid; > + dom->valid = true; > + dom->root_pa = root_pa; > + dom->mode = sch; > + dom->sbi_dom = cfg->sbi_dom; > + dom->nregions = 0; > + > + if (cfg->sbi_dom) { > + /* > + * 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(cfg->sbi_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, cfg->xwr); > + rc = sch->map_full_range(dom, base_xwr); > + if (rc) > + goto fail; > + > + if (dom->nregions < SBI_MPT_MAX_REGIONS_DOMAIN) { > + struct sbi_mpt_region *r = &dom->regions[dom->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(cfg->sbi_dom, mr) { > + if (mr->order >= sizeof(unsigned long) * 8) > + continue; > + > + rc = mpt_map_range_perm(dom, sch, mr->base, BIT(mr->order), > + mpt_xwr_from_memregion(mr, cfg->xwr), false); > + if (rc) > + goto fail; > + } > + } > + > + /* Map explicit regions which are not part of any SBI domain */ > + for (i = 0; i < cfg->nregions; i++) { > + rc = mpt_map_range_perm(dom, sch, cfg->regions[i].pa, cfg->regions[i].size, > + cfg->regions[i].xwr, false); > + if (rc) > + goto fail; > + } > + > + /* > + * Map firmware region with no permission with and mark as locked > + */ > + if (cfg->fw_protect) { > + rc = mpt_map_range_perm(dom, 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); > + goto fail; > + } > + } > + > + ctrl->ndomain++; > + if (out_sdid) > + *out_sdid = sdid; > + > + sbi_mpt_fence_sdid(sdid); > + > + /* To be removed */ > + sbi_printf("sbi_mpt: SDID %u root=0x%lx mode=%s dom=%s\n", > + sdid, root_pa, sch->name, cfg->sbi_dom ? cfg->sbi_dom->name : "(none)"); > + return 0; > + > +fail: > + bitmap_set(ctrl->sdid_bitmap, (int)sdid, 1); > + dom->valid = false; > + return rc; > +} > + > +/* > + * 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 > + */ > +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) > +{ > + u32 s; > + > + if (!mpt_ctrl.ready) > + return SBI_ENODEV; > + if (!sbi_dom) > + return SBI_EINVAL; > + > + /* > + * Search all supervisor domains for associated sbi_dom and > + * activate harts for that supervisor domain > + */ > + for (s = 0; s < mpt_ctrl.max_domains; s++) { > + if (mpt_ctrl.domains[s].valid && mpt_ctrl.domains[s].sbi_dom == sbi_dom) > + return sbi_mpt_hart_activate(s); > + } > + > + return 0; > +} > + > +/* > + * 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].valid = false; > +} > + > +/* > + * 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) > +{ > + 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; > + > + 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->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_mode.c b/lib/sbi/sbi_mpt_mode.c > new file mode 100644 > index 00000000..8baf83a3 > --- /dev/null > +++ b/lib/sbi/sbi_mpt_mode.c > @@ -0,0 +1,997 @@ > +/* SPDX-License-Identifier: BSD-2-Clause > + * > + * Copyright (c) 2026 Qualcomm Inc. > + * > + * Authors: Rahul Pathak <rahul.pathak@oss.qualcomm.com> > + */ > + > +#include <sbi/sbi_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) > + > +/* > + * 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; Oza: need to validate ?? or trust top_level her ? if (!top_level) return 0; > + > + 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 >> 5); Oza: can we define 5 ? > + 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; Oza: do you need to decrement lvl here since you are splitting ? e.g. lvl-- > + 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.51.0 > > > -- > opensbi mailing list > opensbi@lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi Reviewed-by: Pawandeep Oza <pawandeep.oza@oss.qualcomm.com> -- opensbi mailing list opensbi@lists.infradead.org http://lists.infradead.org/mailman/listinfo/opensbi ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [RFC PATCH v2 2/5] mpt: Add Smsdid and Smmpt supervisor domain core 2026-08-14 18:28 ` Pawandeep Oza @ 2026-08-22 12:18 ` Rahul Pathak 0 siblings, 0 replies; 11+ messages in thread From: Rahul Pathak @ 2026-08-22 12:18 UTC (permalink / raw) To: Pawandeep Oza; +Cc: opensbi, rahul On Fri, Aug 14, 2026 at 11:58 PM Pawandeep Oza <pawandeep.oza@oss.qualcomm.com> wrote: > > On Mon, Jul 27, 2026 at 6:56 AM Rahul Pathak > <rahul.pathak@oss.qualcomm.com> wrote: > > > > Introduce the supervisord omain memory-protection (Smmpt) table > Oza: typo: "supervisord omain" Rahul: Sure, will update > > 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> > > --- > > include/sbi/sbi_mpt.h | 334 ++++++++++++++ > > lib/sbi/objects.mk | 2 + > > lib/sbi/sbi_mpt.c | 926 ++++++++++++++++++++++++++++++++++++++ > > lib/sbi/sbi_mpt_mode.c | 997 +++++++++++++++++++++++++++++++++++++++++ > > 4 files changed, 2259 insertions(+) > > create mode 100644 include/sbi/sbi_mpt.h > > create mode 100644 lib/sbi/sbi_mpt.c > > create mode 100644 lib/sbi/sbi_mpt_mode.c > > > > + return xwr; > > +} > > + > > +/* > > + * sbi_mpt_domain_create(): Create a Supervisor Domain > Oza: "Create a Supervisor Domain" ? You probably meant "Create an mpt domain" ! Rahul: Yes, will change > > > + */ > > +int sbi_mpt_domain_create(const struct sbi_mpt_domain_config *cfg, > > + u32 *out_sdid) > > +{ > > + int rc; > > + u32 sdid, i; > > + unsigned long root_pa; > > + struct sbi_mpt_mode *sch; > > + struct sbi_mpt_domain *dom; > > + const struct sbi_domain_memregion *mr; > > + struct sbi_mpt_ctrl *ctrl = &mpt_ctrl; > > + u8 base_xwr = SBI_MPT_PERM_NONE; > > + > > + if (!ctrl->ready) > > + return SBI_ENODEV; > > + > > + if (cfg->sbi_dom && cfg->nregions) { > > + sbi_printf("sbi_mpt: sbi_dom and explicit regions are mutually exclusive\n"); > > + return SBI_EINVAL; > > + } > > + > > + /* > > + * sdid_bitmap empty means every available SDID is > > + * currently in use and no domain can be created. > > + */ > > + if (bitmap_empty(ctrl->sdid_bitmap, ctrl->max_domains)) > > + return SBI_ENOMEM; > > + > > + sch = ctrl->mode; > > + > > + root_pa = sbi_mpt_pool_alloc(sch->root_table_size(), > > + sch->root_table_align()); > > + if (!root_pa) { 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; > Oza: need to validate ?? or trust top_level her ? > if (!top_level) return 0; Rahul: This if condition will be redundant since below for loop will not enter if top_level == 0 > > + > > + 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 >> 5); > Oza: can we define 5 ? Rahul: Sure, will define a macro for it > > + 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; > Oza: do you need to decrement lvl here since you are splitting ? e.g. lvl-- Rahul: below continue make sure that lvl is decremented in next pass of for loop Do you mean I have to decrement lvl again here though due to below continue, it will be decremented twice? > > > + 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)); > > +} > > + -- opensbi mailing list opensbi@lists.infradead.org http://lists.infradead.org/mailman/listinfo/opensbi ^ permalink raw reply [flat|nested] 11+ messages in thread
* [RFC PATCH v2 3/5] platform/generic: Initialise MPT at boot 2026-07-27 13:55 [RFC PATCH v2 0/5] Add Smsdid and Smmpt supervisor domain protection Rahul Pathak 2026-07-27 13:55 ` [RFC PATCH v2 1/5] riscv: Add Smsdid and Smmpt hart extensions Rahul Pathak 2026-07-27 13:56 ` [RFC PATCH v2 2/5] mpt: Add Smsdid and Smmpt supervisor domain core Rahul Pathak @ 2026-07-27 13:56 ` Rahul Pathak 2026-07-28 17:53 ` Pawandeep Oza 2026-07-27 13:56 ` [RFC PATCH v2 4/5] sbi: hsm: Activate MPT for domain on hart start/resume Rahul Pathak 2026-07-27 13:56 ` [RFC PATCH v2 5/5] sbi: init: Print supervisor domains at boot Rahul Pathak 4 siblings, 1 reply; 11+ messages in thread From: Rahul Pathak @ 2026-07-27 13:56 UTC (permalink / raw) To: opensbi; +Cc: rahul.pathak, rahul Integrates MPT initialisation into the generic platform boot flow. mpt_early_init() probes for SMMPT during cold-boot early init and initialises the structures required to build MPT tables. mpt_final_init() creates the root supervisor domain which is mapping all physical memory with the firmware region explcitly denied to S-mode and adds support for an MPT domain for each additional registered SBI domain. The heap budget is extended to reserve space for MPT tables. With this patch MPT tables are built at boot. Signed-off-by: Rahul Pathak <rahul.pathak@oss.qualcomm.com> --- platform/generic/platform.c | 101 ++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/platform/generic/platform.c b/platform/generic/platform.c index 1df0280d..b9a715b6 100644 --- a/platform/generic/platform.c +++ b/platform/generic/platform.c @@ -17,6 +17,9 @@ #include <sbi/sbi_string.h> #include <sbi/sbi_system.h> #include <sbi/sbi_tlb.h> +#include <sbi/sbi_mpt.h> +#include <sbi/sbi_domain.h> +#include <sbi/sbi_console.h> #include <sbi_utils/cache/fdt_cmo_helper.h> #include <sbi_utils/fdt/fdt_domain.h> #include <sbi_utils/fdt/fdt_driver.h> @@ -33,6 +36,86 @@ /* List of platform override modules generated at compile time */ extern const struct fdt_driver *const platform_override_modules[]; +/* + * mpt_early_init(): Detects the SMMPT mode and initializes the + * structures (not MPT tables) requried to prepare MPT tables and + * domain programming. + */ +int mpt_early_init(bool cold_boot) +{ + int rc; + + if (!cold_boot) + return 0; + + rc = sbi_mpt_init(); + if (rc == SBI_ENODEV) + return 0; + + return rc; +} + +/* + * mpt_final_init(): Performs the final initialzation including + * creation of Root supervisor domain. + */ +int mpt_final_init(bool cold_boot) +{ + int rc; + u32 sdid; + struct sbi_domain *dom, *root_dom; + struct sbi_mpt_domain_config cfg; + + if (!cold_boot) + return 0; + + /* SMMPT may be unavailable */ + if (!sbi_mpt_ctrl_get()->ready) + return 0; + + /* + * Create MPT domain for Root domain. + * + * Root domain maps all physical memory so the + * firmware region must be explicitly denied to S-mode. + * + * regions pointer is NULL and nregions is 0 since there + * are no other regions other than respective SBI domain for mapping. + */ + root_dom = sbi_domain_thishart_ptr(); + cfg.sbi_dom = root_dom; + cfg.xwr = SBI_MPT_PERM_RWX; + cfg.fw_protect = true; + cfg.regions = NULL; + cfg.nregions = 0; + + rc = sbi_mpt_domain_create(&cfg, &sdid); + if (rc) { + sbi_printf("mpt: Root domain create failed rc=%d\n", rc); + return rc; + } + + sbi_memset(&cfg, 0, sizeof(cfg)); + + /* Create MPT domain for all other registered SBI domains. */ + sbi_domain_for_each(dom) { + if (dom == root_dom) + continue; + + cfg.sbi_dom = dom; + cfg.xwr = SBI_MPT_PERM_RWX; + cfg.fw_protect = false; + cfg.regions = NULL; + cfg.nregions = 0; + + rc = sbi_mpt_domain_create(&cfg, &sdid); + if (rc) + sbi_printf("mpt: domain '%s' failed rc=%d\n", dom->name, rc); + } + + return 0; +} + static u32 fw_platform_calculate_heap_size(u32 hart_count) { u32 heap_size; @@ -42,6 +125,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); } @@ -229,6 +321,10 @@ int generic_early_init(bool cold_boot) return rc; fdt_driver_init_all(fdt, fdt_early_drivers); + + rc = mpt_early_init(cold_boot); + if (rc) + return rc; } return fdt_cmo_init(cold_boot); @@ -236,11 +332,16 @@ int generic_early_init(bool cold_boot) int generic_final_init(bool cold_boot) { + int rc; void *fdt = fdt_get_address_rw(); if (!cold_boot) return 0; + rc = mpt_final_init(cold_boot); + if (rc) + return rc; + fdt_cpu_fixup(fdt); fdt_fixups(fdt); fdt_domain_fixup(fdt); -- 2.51.0 -- opensbi mailing list opensbi@lists.infradead.org http://lists.infradead.org/mailman/listinfo/opensbi ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [RFC PATCH v2 3/5] platform/generic: Initialise MPT at boot 2026-07-27 13:56 ` [RFC PATCH v2 3/5] platform/generic: Initialise MPT at boot Rahul Pathak @ 2026-07-28 17:53 ` Pawandeep Oza 2026-07-29 2:37 ` Rahul Pathak 0 siblings, 1 reply; 11+ messages in thread From: Pawandeep Oza @ 2026-07-28 17:53 UTC (permalink / raw) To: Rahul Pathak; +Cc: opensbi, rahul On Mon, Jul 27, 2026 at 6:56 AM Rahul Pathak <rahul.pathak@oss.qualcomm.com> wrote: > > Integrates MPT initialisation into the generic platform boot flow. > > mpt_early_init() probes for SMMPT during cold-boot early init and > initialises the structures required to build MPT tables. > > mpt_final_init() creates the root supervisor domain which is mapping all > physical memory with the firmware region explcitly denied to S-mode and > adds support for an MPT domain for each additional registered SBI domain. > > The heap budget is extended to reserve space for MPT tables. > > With this patch MPT tables are built at boot. > > Signed-off-by: Rahul Pathak <rahul.pathak@oss.qualcomm.com> > --- > platform/generic/platform.c | 101 ++++++++++++++++++++++++++++++++++++ > 1 file changed, 101 insertions(+) > > diff --git a/platform/generic/platform.c b/platform/generic/platform.c > index 1df0280d..b9a715b6 100644 > --- a/platform/generic/platform.c > +++ b/platform/generic/platform.c > @@ -17,6 +17,9 @@ > #include <sbi/sbi_string.h> > #include <sbi/sbi_system.h> > #include <sbi/sbi_tlb.h> > +#include <sbi/sbi_mpt.h> > +#include <sbi/sbi_domain.h> > +#include <sbi/sbi_console.h> > #include <sbi_utils/cache/fdt_cmo_helper.h> > #include <sbi_utils/fdt/fdt_domain.h> > #include <sbi_utils/fdt/fdt_driver.h> > @@ -33,6 +36,86 @@ > /* List of platform override modules generated at compile time */ > extern const struct fdt_driver *const platform_override_modules[]; > > +/* > + * mpt_early_init(): Detects the SMMPT mode and initializes the > + * structures (not MPT tables) requried to prepare MPT tables and > + * domain programming. > + */ > +int mpt_early_init(bool cold_boot) > +{ > + int rc; > + > + if (!cold_boot) > + return 0; > + > + rc = sbi_mpt_init(); > + if (rc == SBI_ENODEV) > + return 0; > + > + return rc; > +} > + > +/* > + * mpt_final_init(): Performs the final initialzation including > + * creation of Root supervisor domain. > + */ > +int mpt_final_init(bool cold_boot) > +{ > + int rc; > + u32 sdid; > + struct sbi_domain *dom, *root_dom; > + struct sbi_mpt_domain_config cfg; > + > + if (!cold_boot) > + return 0; > + > + /* SMMPT may be unavailable */ > + if (!sbi_mpt_ctrl_get()->ready) > + return 0; > + > + /* > + * Create MPT domain for Root domain. > + * > + * Root domain maps all physical memory so the > + * firmware region must be explicitly denied to S-mode. > + * > + * regions pointer is NULL and nregions is 0 since there > + * are no other regions other than respective SBI domain for mapping. > + */ > + root_dom = sbi_domain_thishart_ptr(); > + cfg.sbi_dom = root_dom; > + cfg.xwr = SBI_MPT_PERM_RWX; > + cfg.fw_protect = true; > + cfg.regions = NULL; > + cfg.nregions = 0; > + > + rc = sbi_mpt_domain_create(&cfg, &sdid); > + if (rc) { > + sbi_printf("mpt: Root domain create failed rc=%d\n", rc); > + return rc; > + } > + > + sbi_memset(&cfg, 0, sizeof(cfg)); > + > + /* Create MPT domain for all other registered SBI domains. */ > + sbi_domain_for_each(dom) { > + if (dom == root_dom) > + continue; > + > + cfg.sbi_dom = dom; > + cfg.xwr = SBI_MPT_PERM_RWX; > + cfg.fw_protect = false; > + cfg.regions = NULL; > + cfg.nregions = 0; > + > + rc = sbi_mpt_domain_create(&cfg, &sdid); > + if (rc) > + sbi_printf("mpt: domain '%s' failed rc=%d\n", dom->name, rc); > + } > + > + return 0; > +} > + > static u32 fw_platform_calculate_heap_size(u32 hart_count) > { > u32 heap_size; > @@ -42,6 +125,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); > } > > @@ -229,6 +321,10 @@ int generic_early_init(bool cold_boot) > return rc; > > fdt_driver_init_all(fdt, fdt_early_drivers); > + > + rc = mpt_early_init(cold_boot); > + if (rc) > + return rc; > } > > return fdt_cmo_init(cold_boot); > @@ -236,11 +332,16 @@ int generic_early_init(bool cold_boot) > > int generic_final_init(bool cold_boot) > { > + int rc; > void *fdt = fdt_get_address_rw(); > > if (!cold_boot) > return 0; > > + rc = mpt_final_init(cold_boot); > + if (rc) > + return rc; > + Oza: do you think - if there is a need to move this call after all the fixup calls ? > fdt_cpu_fixup(fdt); > fdt_fixups(fdt); > fdt_domain_fixup(fdt); > -- > 2.51.0 > > > -- > opensbi mailing list > opensbi@lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi Reviewed-by: Pawandeep Oza <pawandeep.oza@oss.qualcomm.com> -- opensbi mailing list opensbi@lists.infradead.org http://lists.infradead.org/mailman/listinfo/opensbi ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [RFC PATCH v2 3/5] platform/generic: Initialise MPT at boot 2026-07-28 17:53 ` Pawandeep Oza @ 2026-07-29 2:37 ` Rahul Pathak 0 siblings, 0 replies; 11+ messages in thread From: Rahul Pathak @ 2026-07-29 2:37 UTC (permalink / raw) To: Pawandeep Oza; +Cc: Rahul Pathak, opensbi On Tue, Jul 28, 2026 at 11:30 PM Pawandeep Oza <pawandeep.oza@oss.qualcomm.com> wrote: > > On Mon, Jul 27, 2026 at 6:56 AM Rahul Pathak > <rahul.pathak@oss.qualcomm.com> wrote: > > > > Integrates MPT initialisation into the generic platform boot flow. > > > > mpt_early_init() probes for SMMPT during cold-boot early init and > > initialises the structures required to build MPT tables. > > > > mpt_final_init() creates the root supervisor domain which is mapping all > > physical memory with the firmware region explcitly denied to S-mode and > > adds support for an MPT domain for each additional registered SBI domain. > > > > The heap budget is extended to reserve space for MPT tables. > > > > With this patch MPT tables are built at boot. > > > > Signed-off-by: Rahul Pathak <rahul.pathak@oss.qualcomm.com> > > --- > > platform/generic/platform.c | 101 ++++++++++++++++++++++++++++++++++++ > > 1 file changed, 101 insertions(+) > > > > > > int generic_final_init(bool cold_boot) > > { > > + int rc; > > void *fdt = fdt_get_address_rw(); > > > > if (!cold_boot) > > return 0; > > > > + rc = mpt_final_init(cold_boot); > > + if (rc) > > + return rc; > > + > Oza: do you think - if there is a need to move this call after all > the fixup calls ? The only dependency is whether sbi domain memregions have finalized, which already happens before sbi_platform_final_init. So call to mpt_final_init before or after the *fixups wont change anything IMO. > > fdt_cpu_fixup(fdt); > > fdt_fixups(fdt); > > fdt_domain_fixup(fdt); > > -- > > 2. -- opensbi mailing list opensbi@lists.infradead.org http://lists.infradead.org/mailman/listinfo/opensbi ^ permalink raw reply [flat|nested] 11+ messages in thread
* [RFC PATCH v2 4/5] sbi: hsm: Activate MPT for domain on hart start/resume 2026-07-27 13:55 [RFC PATCH v2 0/5] Add Smsdid and Smmpt supervisor domain protection Rahul Pathak ` (2 preceding siblings ...) 2026-07-27 13:56 ` [RFC PATCH v2 3/5] platform/generic: Initialise MPT at boot Rahul Pathak @ 2026-07-27 13:56 ` Rahul Pathak 2026-07-28 18:18 ` Pawandeep Oza 2026-07-27 13:56 ` [RFC PATCH v2 5/5] sbi: init: Print supervisor domains at boot Rahul Pathak 4 siblings, 1 reply; 11+ messages in thread From: Rahul Pathak @ 2026-07-27 13:56 UTC (permalink / raw) To: opensbi; +Cc: rahul.pathak, rahul Program the hart mmpt CSR for its supervisor domain on the HSM start and resume paths before entering S-mode. Signed-off-by: Rahul Pathak <rahul.pathak@oss.qualcomm.com> --- lib/sbi/sbi_hsm.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/sbi/sbi_hsm.c b/lib/sbi/sbi_hsm.c index 0a355f9c..eb39d5c2 100644 --- a/lib/sbi/sbi_hsm.c +++ b/lib/sbi/sbi_hsm.c @@ -25,6 +25,7 @@ #include <sbi/sbi_system.h> #include <sbi/sbi_timer.h> #include <sbi/sbi_console.h> +#include <sbi/sbi_mpt.h> #define __sbi_hsm_hart_change_state(hdata, oldstate, newstate) \ ({ \ @@ -154,6 +155,9 @@ void __noreturn sbi_hsm_hart_start_finish(struct sbi_scratch *scratch, next_mode = scratch->next_mode; hsm_start_ticket_release(hdata); + /* Program hart mmpt for its supervisor domain before entering S-mode. */ + sbi_mpt_hart_activate_for_domain(sbi_domain_thishart_ptr()); + sbi_hart_switch_mode(hartid, next_arg1, next_addr, next_mode, false); } @@ -478,6 +482,9 @@ void __noreturn sbi_hsm_hart_resume_finish(struct sbi_scratch *scratch, */ __sbi_hsm_suspend_non_ret_restore(scratch); + /* Reprogram mmpt on the a resumed hart */ + sbi_mpt_hart_activate_for_domain(sbi_domain_thishart_ptr()); + sbi_hart_switch_mode(hartid, scratch->next_arg1, scratch->next_addr, scratch->next_mode, false); -- 2.51.0 -- opensbi mailing list opensbi@lists.infradead.org http://lists.infradead.org/mailman/listinfo/opensbi ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [RFC PATCH v2 4/5] sbi: hsm: Activate MPT for domain on hart start/resume 2026-07-27 13:56 ` [RFC PATCH v2 4/5] sbi: hsm: Activate MPT for domain on hart start/resume Rahul Pathak @ 2026-07-28 18:18 ` Pawandeep Oza 0 siblings, 0 replies; 11+ messages in thread From: Pawandeep Oza @ 2026-07-28 18:18 UTC (permalink / raw) To: Rahul Pathak; +Cc: opensbi, rahul On Mon, Jul 27, 2026 at 6:56 AM Rahul Pathak <rahul.pathak@oss.qualcomm.com> wrote: > > Program the hart mmpt CSR for its supervisor domain on the HSM > start and resume paths before entering S-mode. > > Signed-off-by: Rahul Pathak <rahul.pathak@oss.qualcomm.com> > --- > lib/sbi/sbi_hsm.c | 7 +++++++ > 1 file changed, 7 insertions(+) > > diff --git a/lib/sbi/sbi_hsm.c b/lib/sbi/sbi_hsm.c > index 0a355f9c..eb39d5c2 100644 > --- a/lib/sbi/sbi_hsm.c > +++ b/lib/sbi/sbi_hsm.c > @@ -25,6 +25,7 @@ > #include <sbi/sbi_system.h> > #include <sbi/sbi_timer.h> > #include <sbi/sbi_console.h> > +#include <sbi/sbi_mpt.h> > > #define __sbi_hsm_hart_change_state(hdata, oldstate, newstate) \ > ({ \ > @@ -154,6 +155,9 @@ void __noreturn sbi_hsm_hart_start_finish(struct sbi_scratch *scratch, > next_mode = scratch->next_mode; > hsm_start_ticket_release(hdata); > > + /* Program hart mmpt for its supervisor domain before entering S-mode. */ > + sbi_mpt_hart_activate_for_domain(sbi_domain_thishart_ptr()); > + > sbi_hart_switch_mode(hartid, next_arg1, next_addr, next_mode, false); > } > > @@ -478,6 +482,9 @@ void __noreturn sbi_hsm_hart_resume_finish(struct sbi_scratch *scratch, > */ > __sbi_hsm_suspend_non_ret_restore(scratch); > > + /* Reprogram mmpt on the a resumed hart */ > + sbi_mpt_hart_activate_for_domain(sbi_domain_thishart_ptr()); > + > sbi_hart_switch_mode(hartid, scratch->next_arg1, > scratch->next_addr, > scratch->next_mode, false); > -- > 2.51.0 > > > -- > opensbi mailing list > opensbi@lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi Reviewed-by: Pawandeep Oza <pawandeep.oza@oss.qualcomm.com> -- opensbi mailing list opensbi@lists.infradead.org http://lists.infradead.org/mailman/listinfo/opensbi ^ permalink raw reply [flat|nested] 11+ messages in thread
* [RFC PATCH v2 5/5] sbi: init: Print supervisor domains at boot 2026-07-27 13:55 [RFC PATCH v2 0/5] Add Smsdid and Smmpt supervisor domain protection Rahul Pathak ` (3 preceding siblings ...) 2026-07-27 13:56 ` [RFC PATCH v2 4/5] sbi: hsm: Activate MPT for domain on hart start/resume Rahul Pathak @ 2026-07-27 13:56 ` Rahul Pathak 4 siblings, 0 replies; 11+ messages in thread From: Rahul Pathak @ 2026-07-27 13:56 UTC (permalink / raw) To: opensbi; +Cc: rahul.pathak, rahul Print the configured supervisor domains alongside the existing domain details during boot. Signed-off-by: Rahul Pathak <rahul.pathak@oss.qualcomm.com> --- lib/sbi/sbi_init.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/sbi/sbi_init.c b/lib/sbi/sbi_init.c index 9bb1a37e..86a05ac6 100644 --- a/lib/sbi/sbi_init.c +++ b/lib/sbi/sbi_init.c @@ -35,6 +35,7 @@ #include <sbi/sbi_tlb.h> #include <sbi/sbi_version.h> #include <sbi/sbi_unit_test.h> +#include <sbi/sbi_mpt.h> #define BANNER \ " ____ _____ ____ _____\n" \ @@ -162,6 +163,9 @@ static void sbi_boot_print_domains(struct sbi_scratch *scratch) /* Domain details */ sbi_domain_dump_all(" "); + + /* Print Supervisor Domains */ + sbi_mpt_dump(); } static void sbi_boot_print_hart(struct sbi_scratch *scratch, u32 hartid) -- 2.51.0 -- opensbi mailing list opensbi@lists.infradead.org http://lists.infradead.org/mailman/listinfo/opensbi ^ permalink raw reply related [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-08-22 12:19 UTC | newest] Thread overview: 11+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-07-27 13:55 [RFC PATCH v2 0/5] Add Smsdid and Smmpt supervisor domain protection Rahul Pathak 2026-07-27 13:55 ` [RFC PATCH v2 1/5] riscv: Add Smsdid and Smmpt hart extensions Rahul Pathak 2026-07-27 13:56 ` [RFC PATCH v2 2/5] mpt: Add Smsdid and Smmpt supervisor domain core Rahul Pathak 2026-08-14 18:28 ` Pawandeep Oza 2026-08-22 12:18 ` Rahul Pathak 2026-07-27 13:56 ` [RFC PATCH v2 3/5] platform/generic: Initialise MPT at boot Rahul Pathak 2026-07-28 17:53 ` Pawandeep Oza 2026-07-29 2:37 ` Rahul Pathak 2026-07-27 13:56 ` [RFC PATCH v2 4/5] sbi: hsm: Activate MPT for domain on hart start/resume Rahul Pathak 2026-07-28 18:18 ` Pawandeep Oza 2026-07-27 13:56 ` [RFC PATCH v2 5/5] sbi: init: Print supervisor domains at boot Rahul Pathak
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox