@@ -178,49 +178,118 @@ K_SEM_DEFINE(boot_log_sem, 1, 1);
178178#define CLEANUP_RAM_GAP_SIZE ((int) (__ramfunc_end - __ramfunc_region_start))
179179
180180#if defined(CONFIG_NCS_MCUBOOT_DISABLE_SELF_RWX )
181- /* Disabling R_X has to be done while running from RAM for obvious reasons.
182- * Moreover as a last step before jumping to application it must work even after
183- * RAM has been cleared, therefore these operations are performed while executing from RAM.
184- * RAM cleanup ommits portion of the memory where code lives.
181+ /* MCUboot disables read/write/execute on its own NVM region(s) before jumping to
182+ * the application. This has to run from RAM: once EXECUTE (and READ) are cleared
183+ * on the MCUboot slot, any further fetch or read from it faults. It is therefore
184+ * a __ramfunc and is invoked from jump_in() before RAM cleanup, while the MCUboot
185+ * stack is still valid.
186+ *
187+ * nRF54L uses RRAMC, nRF71 uses MRAMC. Both controllers expose an identical
188+ * REGION[n].CONFIG bit layout (READ=0, WRITE=1, EXECUTE=2, LOCK=13, SIZE=16..20)
189+ * and a separate REGION[n].ADDRESS, so only the peripheral base, the register
190+ * field macros and the maximum single-region size differ between them.
191+ */
192+ #if defined(CONFIG_SOC_SERIES_NRF71 )
193+ #include <hal/nrf_mramc.h>
194+ #define NVM_REGION NRF_MRAMC->REGION
195+ #define NVM_CONFIG_READ_Msk MRAMC_REGION_CONFIG_READ_Msk
196+ #define NVM_CONFIG_WRITE_Msk MRAMC_REGION_CONFIG_WRITE_Msk
197+ #define NVM_CONFIG_EXECUTE_Msk MRAMC_REGION_CONFIG_EXECUTE_Msk
198+ #define NVM_CONFIG_LOCK_Msk MRAMC_REGION_CONFIG_LOCK_Msk
199+ #define NVM_CONFIG_SIZE_Msk MRAMC_REGION_CONFIG_SIZE_Msk
200+ #define NVM_CONFIG_SIZE_Pos MRAMC_REGION_CONFIG_SIZE_Pos
201+ /* A single MRAMC region covers at most 31 KiB (5-bit SIZE), so region no.4 and
202+ * no.5 are chained to protect the whole MCUboot slot.
185203 */
204+ #define NVM_REGION_MAX_SIZE_KB 31
205+ #define NVM_SELF_REGION_COUNT 2
206+ #else
186207#include <hal/nrf_rramc.h>
187-
188- #define RRAMC_REGION_RWX_LSB 0
189- #define RRAMC_REGION_RWX_WIDTH 3
190-
191- #define RRAMC_REGION_NUMBER 4
192- #define NRF_RRAM_REGION_SIZE_UNIT 0x400
193- #define NRF_RRAM_REGION_ADDRESS_RESOLUTION 0x400
194-
208+ #define NVM_REGION NRF_RRAMC->REGION
209+ #define NVM_CONFIG_READ_Msk RRAMC_REGION_CONFIG_READ_Msk
210+ #define NVM_CONFIG_WRITE_Msk RRAMC_REGION_CONFIG_WRITE_Msk
211+ #define NVM_CONFIG_EXECUTE_Msk RRAMC_REGION_CONFIG_EXECUTE_Msk
212+ #define NVM_CONFIG_LOCK_Msk RRAMC_REGION_CONFIG_LOCK_Msk
213+ #define NVM_CONFIG_SIZE_Msk RRAMC_REGION_CONFIG_SIZE_Msk
214+ #define NVM_CONFIG_SIZE_Pos RRAMC_REGION_CONFIG_SIZE_Pos
195215#if defined(CONFIG_SOC_NRF54L15_CPUAPP ) || defined(CONFIG_SOC_NRF54L05_CPUAPP ) || \
196216 defined(CONFIG_SOC_NRF54L10_CPUAPP )
197- #define MAX_PROTECTED_REGION_SIZE (31 * 1024)
217+ #define NVM_REGION_MAX_SIZE_KB 31
198218#elif defined(CONFIG_SOC_NRF54LV10A_CPUAPP ) || defined(CONFIG_SOC_NRF54LC10A_CPUAPP ) || \
199- defined(CONFIG_SOC_NRF54LM20A_CPUAPP ) || defined(CONFIG_SOC_NRF54LM20B_CPUAPP )
200- #define MAX_PROTECTED_REGION_SIZE (127 * 1024)
219+ defined(CONFIG_SOC_NRF54LM20A_CPUAPP ) || defined(CONFIG_SOC_NRF54LM20B_CPUAPP )
220+ #define NVM_REGION_MAX_SIZE_KB 127
201221#elif defined(CONFIG_SOC_NRF54LS05B_CPUAPP )
202- #define MAX_PROTECTED_REGION_SIZE (1023 * 1024)
222+ #define NVM_REGION_MAX_SIZE_KB 1023
223+ #endif
224+ /* nRF54L fits the whole MCUboot slot in a single region. */
225+ #define NVM_SELF_REGION_COUNT 1
203226#endif
204227
205- #define RRAMC_REGION_CONFIG NRF_RRAMC->REGION[RRAMC_REGION_NUMBER].CONFIG
206- #define RRAMC_REGION_CONFIG_H (((uint32_t)(&(RRAMC_REGION_CONFIG))) >> 16)
207- #define RRAMC_REGION_CONFIG_L (((uint32_t)(&(RRAMC_REGION_CONFIG))) & 0x0000fffful)
208-
209- #define RRAMC_REGION_ADDRESS NRF_RRAMC->REGION[RRAMC_REGION_NUMBER].ADDRESS
210- #define RRAMC_REGION_ADDRESS_H (((uint32_t)(&(RRAMC_REGION_ADDRESS))) >> 16)
211- #define RRAMC_REGION_ADDRESS_L (((uint32_t)(&(RRAMC_REGION_ADDRESS))) & 0x0000fffful)
228+ /* MCUboot self-lock uses region no.4 first (and no.5 when chained). */
229+ #define NVM_SELF_REGION_FIRST 4
230+ #define NVM_REGION_SIZE_UNIT 0x400
231+ #define NVM_REGION_ADDRESS_RESOLUTION 0x400
212232
213- BUILD_ASSERT ((PROTECTED_REGION_START % NRF_RRAM_REGION_ADDRESS_RESOLUTION ) == 0 ,
233+ BUILD_ASSERT ((PROTECTED_REGION_START % NVM_REGION_ADDRESS_RESOLUTION ) == 0 ,
214234 "Start of protected region is not aligned - not possible to protect" );
215235
216- BUILD_ASSERT ((PROTECTED_REGION_SIZE % NRF_RRAM_REGION_SIZE_UNIT ) == 0 ,
236+ BUILD_ASSERT ((PROTECTED_REGION_SIZE % NVM_REGION_SIZE_UNIT ) == 0 ,
217237 "Size of protected region is not aligned - not possible to protect" );
218238
219- BUILD_ASSERT (PROTECTED_REGION_SIZE <= MAX_PROTECTED_REGION_SIZE ,
220- "Size of protected region is too big for protection" );
239+ BUILD_ASSERT (PROTECTED_REGION_SIZE <=
240+ (NVM_SELF_REGION_COUNT * NVM_REGION_MAX_SIZE_KB * 1024 ),
241+ "Size of protected region is too big for protection" );
221242
222- #define PROTECTED_REGION_START_H ((PROTECTED_REGION_START) >> 16)
223- #define PROTECTED_REGION_START_L ((PROTECTED_REGION_START) & 0x0000fffful)
243+ /* Lock one NVM region read/write/execute-disabled. Only compile-time-constant
244+ * register addresses and masks are used, so no read of the (about to be locked)
245+ * MCUboot flash is emitted.
246+ */
247+ static void __ramfunc nvm_region_lock (uint32_t region , uint32_t start , uint32_t size_kb )
248+ {
249+ volatile uint32_t * config = & NVM_REGION [region ].CONFIG ;
250+ volatile uint32_t * address = & NVM_REGION [region ].ADDRESS ;
251+ uint32_t cfg = * config ;
252+
253+ /* If a previous stage (NSIB) already provisioned the region the SIZE
254+ * field is non-zero; otherwise set the address and size ourselves. The
255+ * address register is left untouched when already configured, as the
256+ * region may already be locked.
257+ */
258+ if ((cfg & NVM_CONFIG_SIZE_Msk ) == 0 ) {
259+ cfg &= ~NVM_CONFIG_SIZE_Msk ;
260+ cfg |= (size_kb << NVM_CONFIG_SIZE_Pos ) & NVM_CONFIG_SIZE_Msk ;
261+ * address = start ;
262+ __DSB ();
263+ }
264+
265+ /* Clear read/write/execute and lock against re-enabling. A locked region
266+ * can still be made stricter, which is what happens here.
267+ */
268+ cfg &= ~(NVM_CONFIG_READ_Msk | NVM_CONFIG_WRITE_Msk | NVM_CONFIG_EXECUTE_Msk );
269+ cfg |= NVM_CONFIG_LOCK_Msk ;
270+ * config = cfg ;
271+ __DSB ();
272+ }
273+
274+ /* Lock the whole MCUboot slot, chaining consecutive regions when a single one
275+ * cannot cover it (nRF71). nRF54L fits in one region and loops once.
276+ */
277+ static void __ramfunc disable_self_rwx (void )
278+ {
279+ uint32_t remaining_kb = PROTECTED_REGION_SIZE / NVM_REGION_SIZE_UNIT ;
280+ uint32_t start = PROTECTED_REGION_START ;
281+ uint32_t region = NVM_SELF_REGION_FIRST ;
282+
283+ while (remaining_kb ) {
284+ uint32_t chunk_kb = (remaining_kb < NVM_REGION_MAX_SIZE_KB ) ?
285+ remaining_kb : NVM_REGION_MAX_SIZE_KB ;
286+
287+ nvm_region_lock (region , start , chunk_kb );
288+ start += chunk_kb * NVM_REGION_SIZE_UNIT ;
289+ remaining_kb -= chunk_kb ;
290+ region ++ ;
291+ }
292+ }
224293
225294#endif /* CONFIG_NCS_MCUBOOT_DISABLE_SELF_RWX */
226295
@@ -276,6 +345,15 @@ static void __ramfunc jump_in(struct arm_vector_table *vt)
276345 }
277346#endif
278347
348+ #if defined(CONFIG_NCS_MCUBOOT_DISABLE_SELF_RWX )
349+ /* Lock MCUboot's own NVM here, from RAM and while still on the MCUboot
350+ * stack (i.e. before any RAM cleanup). Everything executed afterwards
351+ * lives in RAM (__ramfunc), so the now non-readable/executable MCUboot
352+ * slot is never fetched or read again.
353+ */
354+ disable_self_rwx ();
355+ #endif
356+
279357#ifdef CONFIG_CPU_CORTEX_M
280358 __set_MSP (vt -> msp );
281359#endif
@@ -335,39 +413,6 @@ static void __ramfunc jump_in(struct arm_vector_table *vt)
335413#endif /* CONFIG_MCUBOOT_INFINITE_LOOP_AFTER_RAM_CLEANUP */
336414#endif /* CONFIG_MCUBOOT_CLEANUP_RAM */
337415
338- #ifdef CONFIG_NCS_MCUBOOT_DISABLE_SELF_RWX
339- ".thumb_func \n "
340- "region_disable_rwx :\n "
341- " movw r1 , %6 \n "
342- " movt r1 , %7 \n "
343- " ldr r2 , [r1 ]\n "
344- /* Size of the region should be set at this point
345- * by NSIB's DISABLE_NEXT_W.
346- * If not, the region has not been configured yet.
347- * Set the size and address to the partition size and address.
348- */
349- " ldr r5 , = %12 \n "
350- " ands r4 , r2 , r5 \n "
351- " cbnz r4 , clear_rwx \n "
352- /* Set the size of the protected region */
353- " movt r2 , %8 \n "
354- /* Set the address of the protected region */
355- " movw r5 , %13 \n "
356- " movt r5 , %14 \n "
357- " movw r6 , %15 \n "
358- " movt r6 , %16 \n "
359- " str r6 , [r5 ]\n "
360- " dsb \n "
361- " clear_rwx :\n "
362- " bfc r2 , %9 , %10 \n "
363- /* Disallow further modifications */
364- " orr r2 , %11 \n "
365- " str r2 , [r1 ]\n "
366- " dsb \n "
367- /* Next assembly line is important for current function */
368-
369- #endif /* CONFIG_NCS_MCUBOOT_DISABLE_SELF_RWX */
370-
371416 /* Jump to reset vector of an app */
372417 " bx r0 \n "
373418 :
@@ -377,19 +422,6 @@ static void __ramfunc jump_in(struct arm_vector_table *vt)
377422 "r" (CLEANUP_RAM_GAP_START ),
378423 "r" (CLEANUP_RAM_GAP_SIZE ),
379424 "i" (0 )
380- #ifdef CONFIG_NCS_MCUBOOT_DISABLE_SELF_RWX
381- , " i " (RRAMC_REGION_CONFIG_L),
382- " i " (RRAMC_REGION_CONFIG_H),
383- " i " ((PROTECTED_REGION_SIZE) / (NRF_RRAM_REGION_SIZE_UNIT)),
384- " i " (RRAMC_REGION_RWX_LSB),
385- " i " (RRAMC_REGION_RWX_WIDTH),
386- " i " (RRAMC_REGION_CONFIG_LOCK_Msk),
387- " i " (RRAMC_REGION_CONFIG_SIZE_Msk),
388- " i " (RRAMC_REGION_ADDRESS_L),
389- " i " (RRAMC_REGION_ADDRESS_H),
390- " i " (PROTECTED_REGION_START_L),
391- " i " (PROTECTED_REGION_START_H)
392- #endif /* CONFIG_NCS_MCUBOOT_DISABLE_SELF_RWX */
393425 : "r0" , "r1" , "r2" , "r3" , "r4" , "r5" , "r6" , "memory"
394426 );
395427}
0 commit comments