[PATCH v2 1/5] lib: sbi: allow platform to override PMP (un)configuration

Bo Gan
2025-11-17T05:50:40Z

Platform sometimes wants to override the entire PMP (un)config phase,
not just performing additional work for each individual PMP entry. This
allows platforms to insert SoC/core specific PMP entries in a way that
works together with the existing ones set by lib/ code, not conflicting.
platform can also choose to merge or skip memory regions in a reasonable
way in case there's a shortage of PMP entries.

In addition, logic in `sbi_hart_oldpmp_configure` is abstracted out as
`sbi_hart_oldpmp_set`, and made public, so platform with traditional PMP
can leverage it in its own `pmp_configure`

Signed-off-by: Bo Gan <ganboing@gmail.com>
---
include/sbi/sbi_hart.h | 9 ++++
include/sbi/sbi_platform.h | 53 ++++++++++++++++++++
lib/sbi/sbi_domain_context.c | 11 +---
lib/sbi/sbi_hart.c | 97 ++++++++++++++++++++++++------------
4 files changed, 129 insertions(+), 41 deletions(-)

diff --git a/include/sbi/sbi_hart.h b/include/sbi/sbi_hart.h
index e66dd52f..8ece6342 100644
--- a/include/sbi/sbi_hart.h
+++ b/include/sbi/sbi_hart.h
@@ -133,6 +133,8 @@ struct sbi_hart_features {
};

struct sbi_scratch;
+struct sbi_domain;
+struct sbi_domain_memregion;

int sbi_hart_reinit(struct sbi_scratch *scratch);
int sbi_hart_init(struct sbi_scratch *scratch, bool cold_boot);
@@ -147,7 +149,14 @@ unsigned int sbi_hart_pmp_log2gran(struct sbi_scratch *scratch);
unsigned int sbi_hart_pmp_addrbits(struct sbi_scratch *scratch);
unsigned int sbi_hart_mhpm_bits(struct sbi_scratch *scratch);
bool sbi_hart_smepmp_is_fw_region(unsigned int pmp_idx);
+void sbi_hart_pmp_unconfigure(struct sbi_scratch *scratch);
int sbi_hart_pmp_configure(struct sbi_scratch *scratch);
+void sbi_hart_oldpmp_set(struct sbi_scratch *scratch,
+ struct sbi_domain *dom,
+ struct sbi_domain_memregion *reg,
+ unsigned int pmp_idx,
+ unsigned int pmp_log2gran,
+ unsigned long pmp_addr_max);
int sbi_hart_map_saddr(unsigned long base, unsigned long size);
int sbi_hart_unmap_saddr(void);
int sbi_hart_priv_version(struct sbi_scratch *scratch);
diff --git a/include/sbi/sbi_platform.h b/include/sbi/sbi_platform.h
index d75c12de..a53e1797 100644
--- a/include/sbi/sbi_platform.h
+++ b/include/sbi/sbi_platform.h
@@ -146,6 +146,18 @@ struct sbi_platform_operations {
unsigned long log2len);
/** platform specific pmp disable on current HART */
void (*pmp_disable)(unsigned int n);
+
+ /** platform pmp configure override on current HART */
+ int (*pmp_configure)(unsigned int pmp_count,
+ unsigned int pmp_log2gran,
+ unsigned long pmp_addr_max);
+ /**
+ * You need both pmp_configure/unconfigure to properly
+ * provide platform override
+ */
+
+ /** platform pmp unconfigure override on current HART */
+ void (*pmp_unconfigure)(void);
};

/** Platform default per-HART stack size for exception/interrupt handling */
@@ -666,6 +678,47 @@ static inline void sbi_platform_pmp_disable(const struct sbi_platform *plat,
sbi_platform_ops(plat)->pmp_disable(n);
}

+/**
+ * Check if platform wants to override PMP (un)configuration
+ *
+ * @param plat pointer to struct sbi_platform
+ */
+static inline bool sbi_platform_pmp_override(const struct sbi_platform *plat)
+{
+ return plat &&
+ sbi_platform_ops(plat)->pmp_configure &&
+ sbi_platform_ops(plat)->pmp_unconfigure;
+}
+
+/**
+ * Platform PMP configuration override
+ *
+ * @param plat pointer to struct sbi_platform
+ * @param pmp_count number of PMP entries
+ * @param pmp_log2gran PMP granularity
+ * @param pmp_addr_max largest value pmpaddr(x) can hold
+ */
+static inline int sbi_platform_pmp_configure(const struct sbi_platform *plat,
+ unsigned int pmp_count,
+ unsigned int pmp_log2gran,
+ unsigned long pmp_addr_max)
+{
+ return sbi_platform_ops(plat)->pmp_configure(pmp_count,
+ pmp_log2gran,
+ pmp_addr_max);
+}
+
+/**
+ * Platform PMP unconfiguration override
+ *
+ * @param plat pointer to struct sbi_platform
+ */
+static inline void sbi_platform_pmp_unconfigure(
+ const struct sbi_platform *plat)
+{
+ return sbi_platform_ops(plat)->pmp_unconfigure();
+}
+
#endif

#endif
diff --git a/lib/sbi/sbi_domain_context.c b/lib/sbi/sbi_domain_context.c
index 74ad25e8..ea7f741b 100644
--- a/lib/sbi/sbi_domain_context.c
+++ b/lib/sbi/sbi_domain_context.c
@@ -102,7 +102,6 @@ static int switch_to_next_domain_context(struct hart_context *ctx,
struct sbi_trap_context *trap_ctx;
struct sbi_domain *current_dom, *target_dom;
struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
- unsigned int pmp_count = sbi_hart_pmp_count(scratch);

if (!ctx || !dom_ctx || ctx == dom_ctx)
return SBI_EINVAL;
@@ -120,15 +119,7 @@ static int switch_to_next_domain_context(struct hart_context *ctx,
sbi_hartmask_set_hartindex(hartindex, &target_dom->assigned_harts);
spin_unlock(&target_dom->assigned_harts_lock);

- /* Reconfigure PMP settings for the new domain */
- for (int i = 0; i < pmp_count; i++) {
- /* Don't revoke firmware access permissions */
- if (sbi_hart_smepmp_is_fw_region(i))
- continue;
-
- sbi_platform_pmp_disable(sbi_platform_thishart_ptr(), i);
- pmp_disable(i);
- }
+ sbi_hart_pmp_unconfigure(scratch);
sbi_hart_pmp_configure(scratch);

/* Save current CSR context and restore target domain's CSR context */
diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c
index a91703b4..b68b18d4 100644
--- a/lib/sbi/sbi_hart.c
+++ b/lib/sbi/sbi_hart.c
@@ -433,6 +433,44 @@ static int sbi_hart_smepmp_configure(struct sbi_scratch *scratch,
return 0;
}

+void sbi_hart_oldpmp_set(struct sbi_scratch *scratch,
+ struct sbi_domain *dom,
+ struct sbi_domain_memregion *reg,
+ unsigned int pmp_idx,
+ unsigned int pmp_log2gran,
+ unsigned long pmp_addr_max)
+{
+ unsigned int pmp_flags = 0;
+ unsigned long pmp_addr;
+
+ /*
+ * If permissions are to be enforced for all modes on
+ * this region, the lock bit should be set.
+ */
+ if (reg->flags & SBI_DOMAIN_MEMREGION_ENF_PERMISSIONS)
+ pmp_flags |= PMP_L;
+
+ if (reg->flags & SBI_DOMAIN_MEMREGION_SU_READABLE)
+ pmp_flags |= PMP_R;
+ if (reg->flags & SBI_DOMAIN_MEMREGION_SU_WRITABLE)
+ pmp_flags |= PMP_W;
+ if (reg->flags & SBI_DOMAIN_MEMREGION_SU_EXECUTABLE)
+ pmp_flags |= PMP_X;
+
+ pmp_addr = reg->base >> PMP_SHIFT;
+ if (pmp_log2gran <= reg->order && pmp_addr < pmp_addr_max) {
+ sbi_platform_pmp_set(sbi_platform_ptr(scratch),
+ pmp_idx, reg->flags, pmp_flags,
+ reg->base, reg->order);
+ pmp_set(pmp_idx, pmp_flags, reg->base, reg->order);
+ } else {
+ sbi_printf("Can not configure pmp for domain %s because"
+ " memory region address 0x%lx or size 0x%lx "
+ "is not in range.\n", dom->name, reg->base,
+ reg->order);
+ }
+}
+
static int sbi_hart_oldpmp_configure(struct sbi_scratch *scratch,
unsigned int pmp_count,
unsigned int pmp_log2gran,
@@ -441,41 +479,13 @@ static int sbi_hart_oldpmp_configure(struct sbi_scratch *scratch,
struct sbi_domain_memregion *reg;
struct sbi_domain *dom = sbi_domain_thishart_ptr();
unsigned int pmp_idx = 0;
- unsigned int pmp_flags;
- unsigned long pmp_addr;

sbi_domain_for_each_memregion(dom, reg) {
if (!is_valid_pmp_idx(pmp_count, pmp_idx))
return SBI_EFAIL;

- pmp_flags = 0;
-
- /*
- * If permissions are to be enforced for all modes on
- * this region, the lock bit should be set.
- */
- if (reg->flags & SBI_DOMAIN_MEMREGION_ENF_PERMISSIONS)
- pmp_flags |= PMP_L;
-
- if (reg->flags & SBI_DOMAIN_MEMREGION_SU_READABLE)
- pmp_flags |= PMP_R;
- if (reg->flags & SBI_DOMAIN_MEMREGION_SU_WRITABLE)
- pmp_flags |= PMP_W;
- if (reg->flags & SBI_DOMAIN_MEMREGION_SU_EXECUTABLE)
- pmp_flags |= PMP_X;
-
- pmp_addr = reg->base >> PMP_SHIFT;
- if (pmp_log2gran <= reg->order && pmp_addr < pmp_addr_max) {
- sbi_platform_pmp_set(sbi_platform_ptr(scratch),
- pmp_idx, reg->flags, pmp_flags,
- reg->base, reg->order);
- pmp_set(pmp_idx++, pmp_flags, reg->base, reg->order);
- } else {
- sbi_printf("Can not configure pmp for domain %s because"
- " memory region address 0x%lx or size 0x%lx "
- "is not in range.\n", dom->name, reg->base,
- reg->order);
- }
+ sbi_hart_oldpmp_set(scratch, dom, reg, pmp_idx++,
+ pmp_log2gran, pmp_addr_max);
}

return 0;
@@ -528,12 +538,34 @@ int sbi_hart_unmap_saddr(void)
return pmp_disable(SBI_SMEPMP_RESV_ENTRY);
}

+void sbi_hart_pmp_unconfigure(struct sbi_scratch *scratch)
+{
+ unsigned int pmp_count = sbi_hart_pmp_count(scratch);
+ const struct sbi_platform *plat = sbi_platform_ptr(scratch);
+
+ if (sbi_platform_pmp_override(plat)) {
+ sbi_platform_pmp_unconfigure(plat);
+ return;
+ }
+
+ /* Reconfigure PMP settings for the new domain */
+ for (unsigned int i = 0; i < pmp_count; i++) {
+ /* Don't revoke firmware access permissions */
+ if (sbi_hart_smepmp_is_fw_region(i))
+ continue;
+
+ sbi_platform_pmp_disable(sbi_platform_thishart_ptr(), i);
+ pmp_disable(i);
+ }
+}
+
int sbi_hart_pmp_configure(struct sbi_scratch *scratch)
{
int rc;
unsigned int pmp_bits, pmp_log2gran;
unsigned int pmp_count = sbi_hart_pmp_count(scratch);
unsigned long pmp_addr_max;
+ const struct sbi_platform *plat = sbi_platform_ptr(scratch);

if (!pmp_count)
return 0;
@@ -542,7 +574,10 @@ int sbi_hart_pmp_configure(struct sbi_scratch *scratch)
pmp_bits = sbi_hart_pmp_addrbits(scratch) - 1;
pmp_addr_max = (1UL << pmp_bits) | ((1UL << pmp_bits) - 1);

- if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SMEPMP))
+ if (sbi_platform_pmp_override(plat))
+ rc = sbi_platform_pmp_configure(plat, pmp_count,
+ pmp_log2gran, pmp_addr_max);
+ else if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SMEPMP))
rc = sbi_hart_smepmp_configure(scratch, pmp_count,
pmp_log2gran, pmp_addr_max);
else