riscv: pmp: properly initialize per-thread m-mode/u-mode entry array

Retrieve the pmpaddr value matching the last global PMP slot and add it
to the per-thread m-mode and u-mode entry array. Even if that value is
not written out again on thread context switch, that value can still be
used by set_pmp_entry() to attempt a single-slot TOR mapping with it.

Nicely abstract this with the new z_riscv_pmp_thread_init() where the
PMP_M_MODE(thread) and PMP_U_MODE(thread) argument generators can be
used.

Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
diff --git a/arch/riscv/core/pmp.c b/arch/riscv/core/pmp.c
index 8dd9325..e150cf9 100644
--- a/arch/riscv/core/pmp.c
+++ b/arch/riscv/core/pmp.c
@@ -296,6 +296,7 @@
  * we could have non-locked entries here too.
  */
 static ulong_t global_pmp_cfg[1];
+static ulong_t global_pmp_last_addr;
 
 /* End of global PMP entry range */
 static unsigned int global_pmp_end_index;
@@ -333,10 +334,12 @@
 	if (global_pmp_end_index != 0) {
 		__ASSERT(global_pmp_end_index == index, "");
 		__ASSERT(global_pmp_cfg[0] == pmp_cfg[0], "");
+		__ASSERT(global_pmp_last_addr == pmp_addr[index - 1]);
 	}
 #endif
 
 	global_pmp_cfg[0] = pmp_cfg[0];
+	global_pmp_last_addr = pmp_addr[index - 1];
 	global_pmp_end_index = index;
 
 	if (PMP_DEBUG_DUMP) {
@@ -344,6 +347,29 @@
 	}
 }
 
+/**
+ * @Brief Initialize the per-thread PMP register copy with global values.
+ */
+static inline unsigned int z_riscv_pmp_thread_init(ulong_t *pmp_addr,
+						   ulong_t *pmp_cfg,
+						   unsigned int index_limit)
+{
+	ARG_UNUSED(index_limit);
+
+	/*
+	 * Retrieve pmpcfg0 partial content from global entries.
+	 */
+	pmp_cfg[0] = global_pmp_cfg[0];
+
+	/*
+	 * Retrieve the pmpaddr value matching the last global PMP slot.
+	 * This is so that set_pmp_entry() can safely attempt TOR with it.
+	 */
+	pmp_addr[global_pmp_end_index - 1] = global_pmp_last_addr;
+
+	return global_pmp_end_index;
+}
+
 #ifdef CONFIG_PMP_STACK_GUARD
 
 /**
@@ -353,12 +379,9 @@
  */
 void z_riscv_pmp_stackguard_prepare(struct k_thread *thread)
 {
-	unsigned int index = global_pmp_end_index;
+	unsigned int index = z_riscv_pmp_thread_init(PMP_M_MODE(thread));
 	uintptr_t stack_bottom;
 
-	/* Retrieve pmpcfg0 partial content from global entries */
-	thread->arch.m_mode_pmpcfg_regs[0] = global_pmp_cfg[0];
-
 	/* make the bottom addresses of our stack inaccessible */
 	stack_bottom = thread->stack_info.start - K_KERNEL_STACK_RESERVED;
 #ifdef CONFIG_USERSPACE
@@ -445,10 +468,7 @@
  */
 void z_riscv_pmp_usermode_prepare(struct k_thread *thread)
 {
-	unsigned int index = global_pmp_end_index;
-
-	/* Retrieve pmpcfg0 partial content from global entries */
-	thread->arch.u_mode_pmpcfg_regs[0] = global_pmp_cfg[0];
+	unsigned int index = z_riscv_pmp_thread_init(PMP_U_MODE(thread));
 
 	/* Map the usermode stack */
 	set_pmp_entry(&index, PMP_R | PMP_W,