Skip to content

Commit 88380ce

Browse files
adsz-nordicrlubos
authored andcommitted
suit: Rework SDFW and SDFW Recovery sinks
New implementation of SDFW and SDFW Recovery sinks. Supports fixed installation order (SDFW, then SDFW Recovery). Treats SDFW Recovery installation as optional. Envelope is saved when SDFW slot is successfully installed. Ref: NCSDK-29561 Signed-off-by: Adam Szczygieł <[email protected]>
1 parent ab17c60 commit 88380ce

File tree

7 files changed

+239
-323
lines changed

7 files changed

+239
-323
lines changed

subsys/suit/plat_err/include/suit_plat_err.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ typedef int suit_plat_err_t;
9797
#define SUIT_PLAT_ERR_UNSUPPORTED -2010 /**< Attempt to perform an unsupported operation */
9898
#define SUIT_PLAT_ERR_IPC -2011 /**< IPC error */
9999
#define SUIT_PLAT_ERR_NO_RESOURCES -2012 /**< Not enough resources */
100+
#define SUIT_PLAT_ERR_SDRFW_FAILURE -2013 /**< Failure during SDFW Recovery update */
100101

101102
/**
102103
* If the error code is a common platform error code return it.

subsys/suit/platform/sdfw/src/suit_plat_copy.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -251,9 +251,11 @@ int suit_plat_copy(suit_component_t dst_handle, suit_component_t src_handle,
251251
}
252252

253253
if (ret == SUIT_SUCCESS) {
254-
ret = suit_generic_address_streamer_stream(payload_ptr, payload_size, &dst_sink);
255-
if (ret != SUIT_PLAT_SUCCESS) {
256-
LOG_ERR("memptr_streamer failed - error %i", ret);
254+
plat_ret =
255+
suit_generic_address_streamer_stream(payload_ptr, payload_size, &dst_sink);
256+
if (plat_ret != SUIT_PLAT_SUCCESS) {
257+
LOG_ERR("memptr_streamer failed - error %i", plat_ret);
258+
ret = suit_plat_err_to_processor_err_convert(plat_ret);
257259
}
258260
}
259261
#endif /* CONFIG_SUIT_STREAM_SOURCE_MEMPTR */

subsys/suit/platform/src/suit_plat_error_convert.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ int suit_plat_err_to_processor_err_convert(suit_plat_err_t plat_err)
2424
case SUIT_PLAT_ERR_CBOR_DECODING:
2525
proc_err = SUIT_ERR_DECODING;
2626
break;
27+
case SUIT_PLAT_ERR_SDRFW_FAILURE:
28+
proc_err = SUIT_FAIL_CONDITION;
29+
break;
2730
/* To be extended */
2831
default:
2932
/* Return SUIT_ERR_CRASH */

subsys/suit/stream/stream_sinks/src/suit_sdfw_recovery_sink.c

Lines changed: 90 additions & 191 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,10 @@
1414
#include <sdfw/sdfw_update.h>
1515
#include <suit_plat_mem_util.h>
1616

17-
#include <suit_digest_sink.h>
18-
1917
#define SUIT_MAX_SDFW_RECOVERY_COMPONENTS 1
2018

21-
#define SDFW_RECOVERY_SINK_ERR_AGAIN \
22-
1 /* Reboot is needed before proceeding. Call the API again. \
23-
*/
24-
2519
LOG_MODULE_REGISTER(suit_sdfw_recovery_sink, CONFIG_SUIT_LOG_LEVEL);
2620

27-
typedef int sdf_sink_err_t;
28-
2921
struct sdfw_recovery_sink_context {
3022
bool in_use;
3123
bool write_called;
@@ -44,55 +36,18 @@ static struct sdfw_recovery_sink_context *get_new_context(void)
4436
return NULL;
4537
}
4638

47-
static digest_sink_err_t verify_digest(uint8_t *buf, size_t buf_size, psa_algorithm_t algorithm,
48-
uint8_t *expected_digest)
39+
static suit_plat_err_t schedule_update(const uint8_t *buf, size_t size)
4940
{
50-
struct stream_sink digest_sink;
51-
suit_plat_err_t err = suit_digest_sink_get(&digest_sink, algorithm, expected_digest);
52-
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;
63-
}
64-
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);
41+
int err = 0;
7942

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;
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;
9050
}
91-
}
92-
93-
static suit_plat_err_t schedule_sdfw_recovery_update(const uint8_t *buf, size_t size)
94-
{
95-
int err = 0;
9651

9752
const struct sdfw_update_blob update_blob = {
9853
.manifest_addr =
@@ -122,66 +77,18 @@ static suit_plat_err_t schedule_sdfw_recovery_update(const uint8_t *buf, size_t
12277

12378
if (err) {
12479
LOG_ERR("Failed to schedule SDFW Recovery update: %d", err);
125-
return SUIT_PLAT_ERR_CRASH;
126-
}
127-
128-
LOG_INF("SDFW Recovery update scheduled");
129-
130-
return SUIT_PLAT_SUCCESS;
131-
}
132-
133-
static sdf_sink_err_t check_update_candidate(const uint8_t *buf, size_t size)
134-
{
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);
141-
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-
}
154-
155-
return SUIT_PLAT_ERR_CRASH;
156-
}
157-
158-
LOG_DBG("Candidate consistent");
159-
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);
164-
if (err == SUIT_PLAT_SUCCESS) {
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;
173-
}
174-
return err;
80+
err = SUIT_PLAT_ERR_CRASH;
81+
} else {
82+
err = SUIT_PLAT_SUCCESS;
17583
}
17684

177-
LOG_ERR("Failed to calculate digest: %d", err);
178-
return SUIT_PLAT_ERR_CRASH;
85+
return err;
17986
}
18087

18188
static void reboot_to_continue(void)
18289
{
18390
if (IS_ENABLED(CONFIG_SUIT_UPDATE_REBOOT_ENABLED)) {
184-
LOG_INF("Reboot the system to continue SDFW Recovery update");
91+
LOG_INF("Reboot the system to continue update");
18592

18693
LOG_PANIC();
18794

@@ -191,26 +98,15 @@ static void reboot_to_continue(void)
19198
}
19299
}
193100

194-
static suit_plat_err_t check_urot_none(const uint8_t *buf, size_t size)
101+
static suit_plat_err_t schedule_update_and_reboot(const uint8_t *buf, size_t size)
195102
{
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-
}
203-
204-
LOG_INF("Update candidate found");
103+
suit_plat_err_t err = schedule_update(buf, size);
205104

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 */
105+
if (err == SUIT_PLAT_SUCCESS) {
210106
reboot_to_continue();
211107
if (IS_ENABLED(CONFIG_SUIT_UPDATE_REBOOT_ENABLED)) {
212108
/* If this code is reached, it means that reboot did not work. */
213-
/* In such case report an error and convert the error code. */
109+
/* In such case report an error. */
214110
LOG_ERR("Expected reboot did not happen");
215111
err = SUIT_PLAT_ERR_UNREACHABLE_PATH;
216112
}
@@ -219,29 +115,77 @@ static suit_plat_err_t check_urot_none(const uint8_t *buf, size_t size)
219115
return err;
220116
}
221117

222-
static suit_plat_err_t check_recovery_activated(const uint8_t *buf, size_t size)
118+
static suit_plat_err_t update_already_ongoing(const uint8_t *buf, size_t size)
223119
{
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-
}
120+
suit_plat_err_t err = SUIT_PLAT_SUCCESS;
121+
122+
enum sdfw_update_status update_status = sdfw_update_initial_status_get();
238123

239-
LOG_ERR("Failed to calculate digest: %d", err);
240-
return SUIT_PLAT_ERR_CRASH;
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+
}
241143
}
242144

243-
LOG_DBG("Digest match - update success");
244-
return SUIT_PLAT_SUCCESS;
145+
return err;
146+
}
147+
148+
static suit_plat_err_t update_needed_action(const uint8_t *buf, size_t size)
149+
{
150+
suit_plat_err_t err = SUIT_PLAT_SUCCESS;
151+
152+
enum sdfw_update_operation initial_operation = sdfw_update_initial_operation_get();
153+
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+
}
172+
}
173+
174+
return err;
175+
}
176+
177+
static bool is_update_needed(const uint8_t *buf, size_t size)
178+
{
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);
183+
184+
bool digests_match = memcmp(candidate_digest_in_manifest, current_sdfw_recovery_digest,
185+
PSA_HASH_LENGTH(PSA_ALG_SHA_512)) == 0;
186+
187+
/* Update is needed when candidate's digest doesn't match current FW digest */
188+
return !digests_match;
245189
}
246190

247191
/* NOTE: Size means size of the SDFW binary to be updated,
@@ -267,57 +211,12 @@ static suit_plat_err_t write(void *ctx, const uint8_t *buf, size_t size)
267211
context->write_called = true;
268212

269213
suit_plat_err_t err = SUIT_PLAT_SUCCESS;
270-
bool clear_registers = true;
271-
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-
}
285214

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-
}
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");
321220
}
322221

323222
return err;

0 commit comments

Comments
 (0)