1414#include <sdfw/sdfw_update.h>
1515#include <suit_plat_mem_util.h>
1616
17+ #include <suit_digest_sink.h>
18+
1719#define SUIT_MAX_SDFW_RECOVERY_COMPONENTS 1
1820
21+ #define SDFW_RECOVERY_SINK_ERR_AGAIN \
22+ 1 /* Reboot is needed before proceeding. Call the API again. \
23+ */
24+
1925LOG_MODULE_REGISTER (suit_sdfw_recovery_sink , CONFIG_SUIT_LOG_LEVEL );
2026
27+ typedef int sdf_sink_err_t ;
28+
2129struct sdfw_recovery_sink_context {
2230 bool in_use ;
2331 bool write_called ;
@@ -36,19 +44,56 @@ static struct sdfw_recovery_sink_context *get_new_context(void)
3644 return NULL ;
3745}
3846
39- static suit_plat_err_t schedule_update (const uint8_t * buf , size_t size )
47+ static digest_sink_err_t verify_digest (uint8_t * buf , size_t buf_size , psa_algorithm_t algorithm ,
48+ uint8_t * expected_digest )
4049{
41- int err = 0 ;
50+ struct stream_sink digest_sink ;
51+ suit_plat_err_t err = suit_digest_sink_get (& digest_sink , algorithm , expected_digest );
4252
43- if (!suit_plat_mem_clear_sicr_update_registers ()) {
44- LOG_ERR ("Failed to clear update registers" );
45- /* By design SDFW Recovery update error should not result in failing whole
46- * installation.
47- * Because of that, set specific error code instead of SUIT_PLAT_ERR_CRASH.
48- */
49- return SUIT_PLAT_ERR_SDRFW_FAILURE ;
53+ if (err != SUIT_PLAT_SUCCESS ) {
54+ LOG_ERR ("Failed to get digest sink: %d" , err );
55+ return err ;
56+ }
57+
58+ err = digest_sink .write (digest_sink .ctx , buf , buf_size );
59+ if (err != SUIT_PLAT_SUCCESS ) {
60+ LOG_ERR ("Failed to write to stream: %d" , err );
61+ (void )digest_sink .release (digest_sink .ctx );
62+ return err ;
5063 }
5164
65+ digest_sink_err_t ret = suit_digest_sink_digest_match (digest_sink .ctx );
66+
67+ err = digest_sink .release (digest_sink .ctx );
68+ if (err != SUIT_PLAT_SUCCESS ) {
69+ LOG_WRN ("Failed to release stream: %d" , err );
70+ }
71+
72+ return ret ;
73+ }
74+
75+ static suit_plat_err_t clear_urot_update_status (void )
76+ {
77+ mram_erase ((uintptr_t )& NRF_SICR -> UROT .UPDATE ,
78+ sizeof (NRF_SICR -> UROT .UPDATE ) / CONFIG_SDFW_MRAM_WORD_SIZE );
79+
80+ /* Clearing the registers is crucial for correct handling by SecROM. */
81+ /* Incorrect mram_erase behavior was observed on FPGA. */
82+ /* Since mram_erase returns void, there is a need for extra check and returning error code
83+ * to handle such case.
84+ */
85+ if (NRF_SICR -> UROT .UPDATE .STATUS == SICR_UROT_UPDATE_STATUS_CODE_None &&
86+ NRF_SICR -> UROT .UPDATE .OPERATION == SICR_UROT_UPDATE_OPERATION_OPCODE_Nop ) {
87+ return SUIT_PLAT_SUCCESS ;
88+ } else {
89+ return SUIT_PLAT_ERR_IO ;
90+ }
91+ }
92+
93+ static suit_plat_err_t schedule_sdfw_recovery_update (const uint8_t * buf , size_t size )
94+ {
95+ int err = 0 ;
96+
5297 const struct sdfw_update_blob update_blob = {
5398 .manifest_addr =
5499 (uintptr_t )(buf + CONFIG_SUIT_SDFW_RECOVERY_UPDATE_SIGNED_MANIFEST_OFFSET ),
@@ -77,115 +122,126 @@ static suit_plat_err_t schedule_update(const uint8_t *buf, size_t size)
77122
78123 if (err ) {
79124 LOG_ERR ("Failed to schedule SDFW Recovery update: %d" , err );
80- err = SUIT_PLAT_ERR_CRASH ;
81- } else {
82- err = SUIT_PLAT_SUCCESS ;
125+ return SUIT_PLAT_ERR_CRASH ;
83126 }
84127
85- return err ;
128+ LOG_INF ("SDFW Recovery update scheduled" );
129+
130+ return SUIT_PLAT_SUCCESS ;
86131}
87132
88- static void reboot_to_continue ( void )
133+ static sdf_sink_err_t check_update_candidate ( const uint8_t * buf , size_t size )
89134{
90- if (IS_ENABLED (CONFIG_SUIT_UPDATE_REBOOT_ENABLED )) {
91- LOG_INF ("Reboot the system to continue update" );
135+ uint8_t * candidate_binary_start =
136+ (uint8_t * )(buf + CONFIG_SUIT_SDFW_RECOVERY_UPDATE_FIRMWARE_OFFSET );
137+ uint8_t * candidate_digest_in_manifest =
138+ (uint8_t * )(buf + CONFIG_SUIT_SDFW_RECOVERY_UPDATE_DIGEST_OFFSET );
139+ uint8_t * current_sdfw_recovery_digest =
140+ (uint8_t * )(NRF_SICR -> UROT .RECOVERY .SM .TBS .FW .DIGEST );
92141
93- LOG_PANIC ();
142+ /* First check if calculated digest of candidate matches the digest from Signed Manifest */
143+ digest_sink_err_t err =
144+ verify_digest (candidate_binary_start ,
145+ size - (size_t )CONFIG_SUIT_SDFW_RECOVERY_UPDATE_FIRMWARE_OFFSET ,
146+ PSA_ALG_SHA_512 , candidate_digest_in_manifest );
147+
148+ if (err != SUIT_PLAT_SUCCESS ) {
149+ if (err == DIGEST_SINK_ERR_DIGEST_MISMATCH ) {
150+ LOG_ERR ("Candidate inconsistent" );
151+ } else {
152+ LOG_ERR ("Failed to calculate digest: %d" , err );
153+ }
94154
95- sys_reboot (SYS_REBOOT_COLD );
96- } else {
97- LOG_DBG ("Reboot disabled - perform manually" );
155+ return SUIT_PLAT_ERR_CRASH ;
98156 }
99- }
100157
101- static suit_plat_err_t schedule_update_and_reboot (const uint8_t * buf , size_t size )
102- {
103- suit_plat_err_t err = schedule_update (buf , size );
158+ LOG_DBG ("Candidate consistent" );
104159
160+ /* Then compare candidate's digest with current SDFW Recovery digest */
161+ err = verify_digest (candidate_binary_start ,
162+ size - (size_t )CONFIG_SUIT_SDFW_RECOVERY_UPDATE_FIRMWARE_OFFSET ,
163+ PSA_ALG_SHA_512 , current_sdfw_recovery_digest );
105164 if (err == SUIT_PLAT_SUCCESS ) {
106- reboot_to_continue ();
107- if (IS_ENABLED (CONFIG_SUIT_UPDATE_REBOOT_ENABLED )) {
108- /* If this code is reached, it means that reboot did not work. */
109- /* In such case report an error. */
110- LOG_ERR ("Expected reboot did not happen" );
111- err = SUIT_PLAT_ERR_UNREACHABLE_PATH ;
165+ LOG_INF ("Same candidate - skip update" );
166+ return SUIT_PLAT_SUCCESS ;
167+ } else if (err == DIGEST_SINK_ERR_DIGEST_MISMATCH ) {
168+ LOG_INF ("Different candidate" );
169+ err = schedule_sdfw_recovery_update (buf , size );
170+ if (err == SUIT_PLAT_SUCCESS ) {
171+ LOG_DBG ("Update scheduled" );
172+ err = SDFW_RECOVERY_SINK_ERR_AGAIN ;
112173 }
174+ return err ;
113175 }
114176
115- return err ;
177+ LOG_ERR ("Failed to calculate digest: %d" , err );
178+ return SUIT_PLAT_ERR_CRASH ;
116179}
117180
118- static suit_plat_err_t update_already_ongoing ( const uint8_t * buf , size_t size )
181+ static void reboot_to_continue ( void )
119182{
120- suit_plat_err_t err = SUIT_PLAT_SUCCESS ;
183+ if (IS_ENABLED (CONFIG_SUIT_UPDATE_REBOOT_ENABLED )) {
184+ LOG_INF ("Reboot the system to continue SDFW Recovery update" );
121185
122- enum sdfw_update_status update_status = sdfw_update_initial_status_get ();
186+ LOG_PANIC ();
123187
124- /* Candidate is different than current FW but SDFW Recovery update is already ongoing. */
125- switch (update_status ) {
126- case SDFW_UPDATE_STATUS_NONE : {
127- /* No pending operation even though operation indicates SDFW Recovery update.
128- * Yet candidate differs from current FW, so schedule the update.
129- */
130- err = schedule_update_and_reboot (buf , size );
131- break ;
132- }
133- default : {
134- /* SecROM indicates error during update */
135- LOG_ERR ("Update failure: %08x" , update_status );
136- /* By design SDFW Recovery update error should not result in failing whole
137- * installation.
138- * Because of that, set specific error code instead of SUIT_PLAT_ERR_CRASH.
139- */
140- err = SUIT_PLAT_ERR_SDRFW_FAILURE ;
141- break ;
142- }
188+ sys_reboot (SYS_REBOOT_COLD );
189+ } else {
190+ LOG_DBG ("Reboot disabled - perform manually" );
143191 }
144-
145- return err ;
146192}
147193
148- static suit_plat_err_t update_needed_action (const uint8_t * buf , size_t size )
194+ static suit_plat_err_t check_urot_none (const uint8_t * buf , size_t size )
149195{
150- suit_plat_err_t err = SUIT_PLAT_SUCCESS ;
196+ /* Detect update candidate. */
197+ /* It is enough to check Public Key Size field which occupies first 4B of Signed Manifest.
198+ */
199+ if (* ((uint32_t * )buf ) == EMPTY_STORAGE_VALUE ) {
200+ LOG_INF ("Update candidate not found" );
201+ return SUIT_PLAT_ERR_NOT_FOUND ;
202+ }
151203
152- enum sdfw_update_operation initial_operation = sdfw_update_initial_operation_get ( );
204+ LOG_INF ( "Update candidate found" );
153205
154- switch (initial_operation ) {
155- case SDFW_UPDATE_OPERATION_NOP :
156- case SDFW_UPDATE_OPERATION_UROT_ACTIVATE : {
157- /* No previously running update or after the other slot update. */
158- /* Schedule an update of this slot. */
159- err = schedule_update_and_reboot (buf , size );
160- break ;
161- }
162- case SDFW_UPDATE_OPERATION_RECOVERY_ACTIVATE : {
163- /* SDFW Recovery update already ongoing */
164- err = update_already_ongoing (buf , size );
165- break ;
166- }
167- default : {
168- LOG_ERR ("Unhandled operation: %08x" , initial_operation );
169- err = SUIT_PLAT_ERR_CRASH ;
170- break ;
171- }
206+ suit_plat_err_t err = check_update_candidate (buf , size );
207+
208+ if (err == SDFW_RECOVERY_SINK_ERR_AGAIN ) {
209+ /* Update scheduled, continue after reboot */
210+ reboot_to_continue ();
211+ if (IS_ENABLED (CONFIG_SUIT_UPDATE_REBOOT_ENABLED )) {
212+ /* If this code is reached, it means that reboot did not work. */
213+ /* In such case report an error and convert the error code. */
214+ LOG_ERR ("Expected reboot did not happen" );
215+ err = SUIT_PLAT_ERR_UNREACHABLE_PATH ;
216+ }
172217 }
173218
174219 return err ;
175220}
176221
177- static bool is_update_needed (const uint8_t * buf , size_t size )
222+ static suit_plat_err_t check_recovery_activated (const uint8_t * buf , size_t size )
178223{
179- const uint8_t * candidate_digest_in_manifest =
180- (uint8_t * )(buf + CONFIG_SUIT_SDFW_RECOVERY_UPDATE_DIGEST_OFFSET );
181- const uint8_t * current_sdfw_recovery_digest =
182- (uint8_t * )(NRF_SICR -> UROT .RECOVERY .SM .TBS .FW .DIGEST );
224+ uint8_t * candidate_binary_start =
225+ (uint8_t * )(buf + CONFIG_SUIT_SDFW_RECOVERY_UPDATE_FIRMWARE_OFFSET );
226+ uint8_t * current_sdfw_digest = (uint8_t * )(NRF_SICR -> UROT .RECOVERY .SM .TBS .FW .DIGEST );
227+
228+ /* Compare candidate's digest with current SDFW Recovery digest */
229+ digest_sink_err_t err =
230+ verify_digest (candidate_binary_start ,
231+ size - (size_t )CONFIG_SUIT_SDFW_RECOVERY_UPDATE_FIRMWARE_OFFSET ,
232+ PSA_ALG_SHA_512 , current_sdfw_digest );
233+ if (err != SUIT_PLAT_SUCCESS ) {
234+ if (err == DIGEST_SINK_ERR_DIGEST_MISMATCH ) {
235+ LOG_ERR ("Digest mismatch - update failure" );
236+ return SUIT_PLAT_ERR_AUTHENTICATION ;
237+ }
183238
184- bool digests_match = memcmp (candidate_digest_in_manifest , current_sdfw_recovery_digest ,
185- PSA_HASH_LENGTH (PSA_ALG_SHA_512 )) == 0 ;
239+ LOG_ERR ("Failed to calculate digest: %d" , err );
240+ return SUIT_PLAT_ERR_CRASH ;
241+ }
186242
187- /* Update is needed when candidate's digest doesn't match current FW digest */
188- return ! digests_match ;
243+ LOG_DBG ( "Digest match - update success" );
244+ return SUIT_PLAT_SUCCESS ;
189245}
190246
191247/* NOTE: Size means size of the SDFW binary to be updated,
@@ -211,12 +267,57 @@ static suit_plat_err_t write(void *ctx, const uint8_t *buf, size_t size)
211267 context -> write_called = true;
212268
213269 suit_plat_err_t err = SUIT_PLAT_SUCCESS ;
270+ bool clear_registers = true;
214271
215- if (is_update_needed (buf , size )) {
216- LOG_INF ("Update needed" );
217- err = update_needed_action (buf , size );
218- } else {
219- LOG_INF ("Update not needed" );
272+ switch (NRF_SICR -> UROT .UPDATE .STATUS ) {
273+ case SICR_UROT_UPDATE_STATUS_CODE_None : {
274+ err = check_urot_none (buf , size );
275+ /* Potential start of update process - SecROM needs the registers to be set */
276+ clear_registers = false;
277+ break ;
278+ }
279+
280+ case SICR_UROT_UPDATE_STATUS_CODE_RecoveryActivated : {
281+ err = check_recovery_activated (buf , size );
282+ clear_registers = true;
283+ break ;
284+ }
285+
286+ /* TODO: Add handling of status RecoveryUnconfirmed and RecoveryConfirmed.
287+ * For now the defines for these states are missing in mdk header files.
288+ * NCSDK-26939
289+ */
290+
291+ case SICR_UROT_UPDATE_STATUS_CODE_UROTActivated :
292+ case SICR_UROT_UPDATE_STATUS_CODE_VerifyOK :
293+ case SICR_UROT_UPDATE_STATUS_CODE_AROTRecovery : {
294+ LOG_ERR ("Unsupported Recovery update status: 0x%08X" , NRF_SICR -> UROT .UPDATE .STATUS );
295+ err = SUIT_PLAT_ERR_INCORRECT_STATE ;
296+ clear_registers = true;
297+ break ;
298+ }
299+
300+ default : {
301+ LOG_ERR ("SDFW Recovery update failure: 0x%08X" , NRF_SICR -> UROT .UPDATE .STATUS );
302+ err = NRF_SICR -> UROT .UPDATE .STATUS ;
303+ clear_registers = true;
304+ break ;
305+ }
306+ }
307+
308+ if (clear_registers ) {
309+ suit_plat_err_t clear_err = clear_urot_update_status ();
310+
311+ if (clear_err ) {
312+ LOG_ERR ("Failed to clear UROT update status" );
313+ /* If the only error was during register clearing - report it. */
314+ /* Otherwise report the original cause of failure. */
315+ if (err == SUIT_PLAT_SUCCESS ) {
316+ err = clear_err ;
317+ }
318+ } else {
319+ LOG_DBG ("UROT update status cleared" );
320+ }
220321 }
221322
222323 return err ;
0 commit comments