[PATCH 8/8] lib: sbi_domain_context: preserve firmware PMP entries during domain context switch

Yu-Chien Peter Lin
2025-08-14T13:16:29Z

When SmePMP is enabled, clearing firmware PMP entries during a domain
context switch can temporarily revoke access to OpenSBI’s own code and
data, leading to faults.

Keep firmware PMP entries enabled across switches so firmware regions
remain accessible and executable.

Signed-off-by: Yu-Chien Peter Lin <peter.lin@sifive.com>
---
lib/sbi/sbi_domain_context.c | 43 ++++++++++++++++++++++++++++++++++++
1 file changed, 43 insertions(+)

diff --git a/lib/sbi/sbi_domain_context.c b/lib/sbi/sbi_domain_context.c
index 1812c7c9..4ee50ac3 100644
--- a/lib/sbi/sbi_domain_context.c
+++ b/lib/sbi/sbi_domain_context.c
@@ -85,6 +85,45 @@ static void hart_context_set(struct sbi_domain *dom, u32 hartindex,
hart_context_get(sbi_domain_thishart_ptr(), \
current_hartindex())

+/**
+ * Determine whether a PMP entry protects firmware memory regions
+ *
+ * @param n PMP entry index
+ * @param dom current domain
+ */
+static bool pmp_is_fw_region(unsigned int n, struct sbi_domain *dom)
+{
+ struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
+ unsigned int pmp_count = sbi_hart_pmp_count(scratch);
+ struct sbi_domain_memregion *reg;
+ unsigned long addr, log2len;
+ unsigned long prot, fw_flag;
+ int rc;
+
+ if (!dom || (n >= pmp_count))
+ return false;
+
+ /* Decode the pmpcfg and pmpaddr */
+ rc = pmp_get(n, &prot, &addr, &log2len);
+ if (rc)
+ return false;
+
+ sbi_domain_for_each_memregion(dom, reg) {
+ if ((reg->flags & SBI_DOMAIN_MEMREGION_FW) == 0)
+ continue;
+
+ /* Found a firmware region, assume it is always NAPOT mode */
+ fw_flag = sbi_domain_get_smepmp_flags(reg) | PMP_A_NAPOT;
+ if ((reg->base == addr) &&
+ (reg->order == log2len) &&
+ (fw_flag == prot)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
/**
* Switches the HART context from the current domain to the target domain.
* This includes changing domain assignments and reconfiguring PMP, as well
@@ -121,6 +160,10 @@ static void switch_to_next_domain_context(struct hart_context *ctx,
(i == SBI_SMEPMP_RESV_ENTRY)) {
continue;
}
+
+ if (pmp_is_fw_region(i, current_dom))
+ continue;
+
sbi_platform_pmp_disable(sbi_platform_thishart_ptr(), i);
pmp_disable(i);
}