|  | 
|  | 1 | +/* | 
|  | 2 | + * Copyright (c) 2025 Nordic Semiconductor ASA | 
|  | 3 | + * | 
|  | 4 | + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause | 
|  | 5 | + */ | 
|  | 6 | + | 
|  | 7 | +#include <zephyr/ztest.h> | 
|  | 8 | +#include <hal/nrf_timer.h> | 
|  | 9 | + | 
|  | 10 | +/* As peripheral registers are not cleared during RAM cleanup, we can | 
|  | 11 | + * use a register of a peripheral unused by NSIB to save data which can then | 
|  | 12 | + * be read by the application. | 
|  | 13 | + * We use this trick to save the start and end addresses of the RAMFUNC region. | 
|  | 14 | + * This region is not cleared during the RAM cleanup, as it contains the code responsible | 
|  | 15 | + * for performing the RAM cleanup, so it must be skipped when checking if the RAM cleanup | 
|  | 16 | + * has been successfully performed. | 
|  | 17 | + * The TIMER00 is not used by NSIB, and is therefore its CC[0] and CC[1] registers | 
|  | 18 | + * are used for this purpose. | 
|  | 19 | + */ | 
|  | 20 | +#define RAMFUNC_START_SAVE_REGISTER NRF_TIMER00->CC[0] | 
|  | 21 | +#define RAMFUNC_END_SAVE_REGISTER NRF_TIMER00->CC[1] | 
|  | 22 | + | 
|  | 23 | +/* Using 2-byte magic values allows to load the whole value with movw */ | 
|  | 24 | +#define RAM_CLEANUP_SUCCESS_MAGIC 0x5678 | 
|  | 25 | +#define RAM_CLEANUP_FAILURE_MAGIC 0x4321 | 
|  | 26 | + | 
|  | 27 | +static uint32_t __noinit ram_cleanup_result; | 
|  | 28 | +static uint32_t __noinit uncleared_address; | 
|  | 29 | +static uint32_t __noinit uncleared_value; | 
|  | 30 | + | 
|  | 31 | +/** | 
|  | 32 | + * This hook needs to have the attribute __attribute__((naked)), | 
|  | 33 | + * as otherwise the stack would be modified when calling it, leading to | 
|  | 34 | + * test failure, as part of the RAM in which the stack resides wouldn't | 
|  | 35 | + * be zeroed. | 
|  | 36 | + */ | 
|  | 37 | +__attribute__((naked)) void soc_early_reset_hook(void) | 
|  | 38 | +{ | 
|  | 39 | +	__asm__ volatile ( | 
|  | 40 | +		/* Load zero (value used to clear memory) into r0 */ | 
|  | 41 | +		"   mov  r0, #0\n" | 
|  | 42 | +		/*  Explicitly clear the ram_cleanup_result variable */ | 
|  | 43 | +		"   mov  r2, %6\n" | 
|  | 44 | +		"   str  r0, [r2]\n" | 
|  | 45 | +		/* Load the location of the saved ram func start address to r1 */ | 
|  | 46 | +		"   mov  r1, %0\n" | 
|  | 47 | +		/* Load the ram func start address to r2 */ | 
|  | 48 | +		"   ldr  r2, [r1]\n" | 
|  | 49 | +		/* Load the location of the saved ram func end address to r1 */ | 
|  | 50 | +		"   mov  r1, %1\n" | 
|  | 51 | +		/* Load the ram func end address to r3*/ | 
|  | 52 | +		"   ldr  r3, [r1]\n" | 
|  | 53 | +		/* Clear memory from ram func start to ram func end. | 
|  | 54 | +		 * The area is skipped during the RAM cleanup, | 
|  | 55 | +		 * so it is not cleared. | 
|  | 56 | +		 * Simply cleaning it up here is quicker | 
|  | 57 | +		 * than verifying if the current address is within the gap | 
|  | 58 | +		 * in each iteration of the loop. | 
|  | 59 | +		 */ | 
|  | 60 | +		"ram_func_zero_loop:\n" | 
|  | 61 | +		"   cmp  r2, r3\n" | 
|  | 62 | +		"   bge  ram_func_zero_loop_done\n" | 
|  | 63 | +		"   str  r0, [r2]\n" | 
|  | 64 | +		/* Increment the address by 4 (word size) */ | 
|  | 65 | +		"   add  r2, r2, #4\n" | 
|  | 66 | +		"   b    ram_func_zero_loop\n" | 
|  | 67 | +		"ram_func_zero_loop_done:\n" | 
|  | 68 | +		/* Verify that all of the RAM memory has been cleared */ | 
|  | 69 | +		/* Load SRAM base address to r1 */ | 
|  | 70 | +		"   mov  r1, %2\n" | 
|  | 71 | +		/* Load SRAM size to r2 */ | 
|  | 72 | +		"   mov  r2, %3\n" | 
|  | 73 | +		/* Calculate SRAM end address (base + size) */ | 
|  | 74 | +		"   add  r2, r1, r2\n" | 
|  | 75 | +		"verify_ram_loop:\n" | 
|  | 76 | +		"   cmp  r1, r2\n" | 
|  | 77 | +		"   bge  verify_ram_done\n" | 
|  | 78 | +		/* Load value at current address and verify if it equals 0 */ | 
|  | 79 | +		"   ldr  r3, [r1]\n" | 
|  | 80 | +		"   cmp  r3, #0\n" | 
|  | 81 | +		"   bne  ram_cleanup_failed\n" | 
|  | 82 | +		/* Increment the address by 4 (word size) */ | 
|  | 83 | +		"   add  r1, r1, #4\n" | 
|  | 84 | +		"   b    verify_ram_loop\n" | 
|  | 85 | +		"ram_cleanup_failed:\n" | 
|  | 86 | +		/* Load RAM_CLEANUP_FAILURE_MAGIC into r0 */ | 
|  | 87 | +		"   movw r0, %4\n" | 
|  | 88 | +		/* Save the uncleared address in uncleared_address variable */ | 
|  | 89 | +		"   mov  r2, %7\n" | 
|  | 90 | +		"   str  r1, [r2]\n" | 
|  | 91 | +		/* Save the uncleared value in uncleared_value variable */ | 
|  | 92 | +		"   mov  r2, %8\n" | 
|  | 93 | +		"   str  r3, [r2]\n" | 
|  | 94 | +		"   b    verification_done\n" | 
|  | 95 | +		"verify_ram_done:\n" | 
|  | 96 | +		/* Load RAM_CLEANUP_SUCCESS_MAGIC */ | 
|  | 97 | +		"   movw r0, %5\n" | 
|  | 98 | +		"verification_done:\n" | 
|  | 99 | +		/* Store result in ram_cleanup_result variable */ | 
|  | 100 | +		"   mov  r2, %6\n" | 
|  | 101 | +		"   str  r0, [r2]\n" | 
|  | 102 | +		/* __attribute__((naked)) requires manual branching  */ | 
|  | 103 | +		"   bx   lr\n" | 
|  | 104 | +		: | 
|  | 105 | +		: "r" (&RAMFUNC_START_SAVE_REGISTER), | 
|  | 106 | +		  "r" (&RAMFUNC_END_SAVE_REGISTER), | 
|  | 107 | +		  "r" (CONFIG_SRAM_BASE_ADDRESS), | 
|  | 108 | +		  "r" (CONFIG_SRAM_SIZE * 1024), | 
|  | 109 | +		  "i" (RAM_CLEANUP_FAILURE_MAGIC), | 
|  | 110 | +		  "i" (RAM_CLEANUP_SUCCESS_MAGIC), | 
|  | 111 | +		  "r" (&ram_cleanup_result), | 
|  | 112 | +		  "r" (&uncleared_address), | 
|  | 113 | +		  "r" (&uncleared_value) | 
|  | 114 | +		: "r0", "r1", "r2", "r3", "lr", "memory" | 
|  | 115 | +	); | 
|  | 116 | +} | 
|  | 117 | + | 
|  | 118 | +ZTEST(b0_ram_cleanup, test_EARLY_RESET_HOOK) | 
|  | 119 | +{ | 
|  | 120 | +	zassert_true((ram_cleanup_result == RAM_CLEANUP_SUCCESS_MAGIC) || | 
|  | 121 | +		     (ram_cleanup_result == RAM_CLEANUP_FAILURE_MAGIC), | 
|  | 122 | +		     "RAM cleanup result should be either success or failure"); | 
|  | 123 | +	zassert_equal(ram_cleanup_result, RAM_CLEANUP_SUCCESS_MAGIC, | 
|  | 124 | +		      "Uncleared word detected at address %p, value 0x%x", uncleared_address, | 
|  | 125 | +		      uncleared_value); | 
|  | 126 | +} | 
|  | 127 | + | 
|  | 128 | +ZTEST_SUITE(b0_ram_cleanup, NULL, NULL, NULL, NULL, NULL); | 
0 commit comments