1414/** Additional memory used by the retention subsystem (2B - prefix, 4B - CRC).*/
1515#define BOOT_REQUEST_ENTRY_METADATA_SIZE (2 + 4)
1616
17+ /** Size of the retention area used for copying boot request entries. */
18+ #define BOOT_REQUEST_RETENTION_SIZE (BOOT_REQUEST_ENTRY_MAX * sizeof(uint8_t))
19+
20+ /** Value indicating that the data in the retention area is valid. */
21+ #define DATA_VALID_VALUE 1
22+
23+ /** Number of images supported by the bootloader requests. */
24+ #define BOOT_REQUEST_IMG_NUM 2
25+
1726BOOT_LOG_MODULE_REGISTER (bootloader_request );
1827
1928static const struct device * bootloader_request_dev =
2029 DEVICE_DT_GET (DT_CHOSEN (nrf_bootloader_request ));
30+ #ifdef NRF_MCUBOOT_BOOT_REQUEST_PREFERENCE_KEEP
31+ static const struct device * bootloader_request_backup_dev =
32+ DEVICE_DT_GET (DT_CHOSEN (nrf_bootloader_request_backup ));
33+ #endif
2134
2235enum boot_request_type {
2336 /** Invalid request. */
@@ -141,18 +154,190 @@ static int boot_request_entry_find(enum boot_request_type type, boot_request_img
141154 return 0 ;
142155}
143156
157+ #ifdef NRF_MCUBOOT_BOOT_REQUEST_PREFERENCE_KEEP
158+ static int boot_request_copy (const struct device * dst , const struct device * src )
159+ {
160+ uint8_t buffer [BOOT_REQUEST_RETENTION_SIZE ];
161+ int rc ;
162+
163+ rc = retention_read (src , 0 , buffer , sizeof (buffer ));
164+ if (rc != 0 ) {
165+ return rc ;
166+ }
167+
168+ rc = retention_write (dst , 0 , buffer , sizeof (buffer ));
169+ if (rc != 0 ) {
170+ return rc ;
171+ }
172+
173+ return 0 ;
174+ }
175+
176+ static bool boot_request_equal (const struct device * dev1 , const struct device * dev2 )
177+ {
178+ uint8_t buffer1 [BOOT_REQUEST_RETENTION_SIZE ];
179+ uint8_t buffer2 [BOOT_REQUEST_RETENTION_SIZE ];
180+ int rc ;
181+
182+ rc = retention_read (dev1 , 0 , buffer1 , sizeof (buffer1 ));
183+ if (rc != 0 ) {
184+ return false;
185+ }
186+
187+ rc = retention_read (dev2 , 0 , buffer2 , sizeof (buffer2 ));
188+ if (rc != 0 ) {
189+ return false;
190+ }
191+
192+ return (memcmp (buffer1 , buffer2 , sizeof (buffer1 )) == 0 );
193+ }
194+
195+ static bool boot_request_updateable (void )
196+ {
197+ if (retention_is_valid (bootloader_request_dev ) != DATA_VALID_VALUE ) {
198+ if (retention_is_valid (bootloader_request_backup_dev ) == DATA_VALID_VALUE ) {
199+ /* Recover main area from a valid backup. */
200+ if (boot_request_copy (bootloader_request_dev ,
201+ bootloader_request_backup_dev ) != 0 ) {
202+ return false;
203+ }
204+ } else if (retention_clear (bootloader_request_dev ) != 0 ) {
205+ /* Both areas are not valid - clear the main area. */
206+ return false;
207+ }
208+ }
209+
210+ if (retention_is_valid (bootloader_request_dev ) != DATA_VALID_VALUE ) {
211+ /* Cannot update area if it is still corrupted. */
212+ return false;
213+ }
214+
215+ return true;
216+ }
217+ #endif /* NRF_MCUBOOT_BOOT_REQUEST_PREFERENCE_KEEP */
218+
219+ static int boot_request_read (enum boot_request_type type , boot_request_img_t image , uint8_t * value )
220+ {
221+ size_t req_entry ;
222+ int ret ;
223+
224+ ret = boot_request_entry_find (type , image , & req_entry );
225+ if (ret != 0 ) {
226+ return - EINVAL ;
227+ }
228+
229+ #ifdef NRF_MCUBOOT_BOOT_REQUEST_PREFERENCE_KEEP
230+ if (retention_is_valid (bootloader_request_dev ) == DATA_VALID_VALUE ) {
231+ /* Read from the main area. */
232+ ret = retention_read (bootloader_request_dev , req_entry * sizeof (uint8_t ),
233+ (void * )value , sizeof (uint8_t ));
234+ } else if (retention_is_valid (bootloader_request_backup_dev ) == DATA_VALID_VALUE ) {
235+ /* Read from backup area. */
236+ ret = retention_read (bootloader_request_backup_dev , req_entry * sizeof (uint8_t ),
237+ (void * )value , sizeof (uint8_t ));
238+ } else {
239+ /* Both areas are invalid. */
240+ return - EIO ;
241+ }
242+ #else
243+ ret = retention_read (bootloader_request_dev , req_entry * sizeof (uint8_t ), (void * )value ,
244+ sizeof (uint8_t ));
245+ #endif
246+ return ret ;
247+ }
248+
144249int boot_request_init (void )
145250{
146251 if (!device_is_ready (bootloader_request_dev )) {
147252 return - EIO ;
148253 }
149254
255+ #ifdef NRF_MCUBOOT_BOOT_REQUEST_PREFERENCE_KEEP
256+ if (!device_is_ready (bootloader_request_backup_dev )) {
257+ return - EIO ;
258+ }
259+
260+ /* Both areas must have the same size. */
261+ if ((retenstion_size (bootloader_request_dev ) != BOOT_REQUEST_RETENTION_SIZE ) ||
262+ (retention_size (bootloader_request_backup_dev ) != BOOT_REQUEST_RETENTION_SIZE )) {
263+ return - EIO ;
264+ }
265+
266+ if (retention_is_valid (bootloader_request_dev ) == DATA_VALID_VALUE ) {
267+ if (retention_is_valid (bootloader_request_backup_dev ) == DATA_VALID_VALUE ) {
268+ if (!boot_request_equal (bootloader_request_dev ,
269+ bootloader_request_backup_dev )) {
270+ /* Primary is valid, backup is outdated. */
271+ boot_request_copy (bootloader_request_backup_dev ,
272+ bootloader_request_dev );
273+ } else {
274+ /* Both are valid and equal, nothing to do. */
275+ }
276+ } else {
277+ /* Backup is invalid, copy primary to backup. */
278+ boot_request_copy (bootloader_request_backup_dev , bootloader_request_dev );
279+ }
280+ } else {
281+ if (retention_is_valid (bootloader_request_backup_dev ) == DATA_VALID_VALUE ) {
282+ /* Primary is invalid, restore from backup. */
283+ boot_request_copy (bootloader_request_dev , bootloader_request_backup_dev );
284+ } else {
285+ /* Both are invalid, clear both. */
286+ retention_clear (bootloader_request_dev );
287+ retention_clear (bootloader_request_backup_dev );
288+ }
289+ }
290+ #endif
291+
150292 return 0 ;
151293}
152294
153295int boot_request_clear (void )
154296{
297+ #ifdef NRF_MCUBOOT_BOOT_REQUEST_PREFERENCE_KEEP
298+ uint8_t value = BOOT_REQUEST_SLOT_INVALID ;
299+ size_t req_entry ;
300+ int ret ;
301+ uint8_t image ;
302+
303+ if (retention_is_valid (bootloader_request_backup_dev ) == DATA_VALID_VALUE ) {
304+ /* Erase the main area. */
305+ ret = retention_clear (bootloader_request_dev );
306+ if (ret != 0 ) {
307+ return ret ;
308+ }
309+
310+ /* Set the boot preference, based on the backup data. */
311+ for (image = 0 ; image < BOOT_REQUEST_IMG_NUM ; image ++ ) {
312+ ret = boot_request_entry_find (BOOT_REQUEST_IMG_PREFERENCE , image ,
313+ & req_entry );
314+ if (ret != 0 ) {
315+ return ret ;
316+ }
317+
318+ ret = retention_read (bootloader_request_backup_dev ,
319+ req_entry * sizeof (value ), (void * )& value ,
320+ sizeof (value ));
321+ if (ret != 0 ) {
322+ return ret ;
323+ }
324+
325+ ret = retention_write (bootloader_request_dev , req_entry * sizeof (value ),
326+ (void * )& value , sizeof (value ));
327+ if (ret != 0 ) {
328+ return ret ;
329+ }
330+ }
331+
332+ /* Sync backup with the main area. */
333+ return boot_request_copy (bootloader_request_backup_dev , bootloader_request_dev );
334+ }
335+
336+ /* Backup is invalid, do not clear the memory to keep at least one valid copy. */
337+ return 0 ;
338+ #else
155339 return retention_clear (bootloader_request_dev );
340+ #endif
156341}
157342
158343int boot_request_confirm_slot (uint8_t image , enum boot_slot slot )
@@ -161,6 +346,13 @@ int boot_request_confirm_slot(uint8_t image, enum boot_slot slot)
161346 size_t req_entry ;
162347 int ret ;
163348
349+ #ifdef NRF_MCUBOOT_BOOT_REQUEST_PREFERENCE_KEEP
350+ if (!boot_request_updateable ()) {
351+ /* Cannot update area if it is corrupted. */
352+ return - EIO ;
353+ }
354+ #endif
355+
164356 ret = boot_request_entry_find (BOOT_REQUEST_IMG_CONFIRM , image , & req_entry );
165357 if (ret != 0 ) {
166358 return ret ;
@@ -184,16 +376,9 @@ int boot_request_confirm_slot(uint8_t image, enum boot_slot slot)
184376bool boot_request_check_confirmed_slot (uint8_t image , enum boot_slot slot )
185377{
186378 uint8_t value = BOOT_REQUEST_SLOT_INVALID ;
187- size_t req_entry ;
188379 int ret ;
189380
190- ret = boot_request_entry_find (BOOT_REQUEST_IMG_CONFIRM , image , & req_entry );
191- if (ret != 0 ) {
192- return false;
193- }
194-
195- ret = retention_read (bootloader_request_dev , req_entry * sizeof (value ), (void * )& value ,
196- sizeof (value ));
381+ ret = boot_request_read (BOOT_REQUEST_IMG_CONFIRM , image , & value );
197382 if (ret != 0 ) {
198383 return false;
199384 }
@@ -216,6 +401,13 @@ int boot_request_set_preferred_slot(uint8_t image, enum boot_slot slot)
216401 size_t req_entry ;
217402 int ret ;
218403
404+ #ifdef NRF_MCUBOOT_BOOT_REQUEST_PREFERENCE_KEEP
405+ if (!boot_request_updateable ()) {
406+ /* Cannot update area if it is corrupted. */
407+ return - EIO ;
408+ }
409+ #endif
410+
219411 ret = boot_request_entry_find (BOOT_REQUEST_IMG_PREFERENCE , image , & req_entry );
220412 if (ret != 0 ) {
221413 return ret ;
@@ -239,16 +431,9 @@ int boot_request_set_preferred_slot(uint8_t image, enum boot_slot slot)
239431enum boot_slot boot_request_get_preferred_slot (uint8_t image )
240432{
241433 uint8_t value = BOOT_REQUEST_SLOT_INVALID ;
242- size_t req_entry ;
243434 int ret ;
244435
245- ret = boot_request_entry_find (BOOT_REQUEST_IMG_PREFERENCE , image , & req_entry );
246- if (ret != 0 ) {
247- return BOOT_SLOT_NONE ;
248- }
249-
250- ret = retention_read (bootloader_request_dev , req_entry * sizeof (value ), (void * )& value ,
251- sizeof (value ));
436+ ret = boot_request_read (BOOT_REQUEST_IMG_PREFERENCE , image , & value );
252437 if (ret != 0 ) {
253438 return BOOT_SLOT_NONE ;
254439 }
@@ -271,6 +456,13 @@ int boot_request_enter_recovery(void)
271456 size_t req_entry ;
272457 int ret ;
273458
459+ #ifdef NRF_MCUBOOT_BOOT_REQUEST_PREFERENCE_KEEP
460+ if (!boot_request_updateable ()) {
461+ /* Cannot update area if it is corrupted. */
462+ return - EIO ;
463+ }
464+ #endif
465+
274466 ret = boot_request_entry_find (BOOT_REQUEST_BOOT_MODE , BOOT_REQUEST_IMG_BOOTLOADER ,
275467 & req_entry );
276468 if (ret != 0 ) {
@@ -285,17 +477,9 @@ int boot_request_enter_recovery(void)
285477bool boot_request_detect_recovery (void )
286478{
287479 uint8_t value = BOOT_REQUEST_MODE_INVALID ;
288- size_t req_entry ;
289480 int ret ;
290481
291- ret = boot_request_entry_find (BOOT_REQUEST_BOOT_MODE , BOOT_REQUEST_IMG_BOOTLOADER ,
292- & req_entry );
293- if (ret != 0 ) {
294- return false;
295- }
296-
297- ret = retention_read (bootloader_request_dev , req_entry * sizeof (value ), (void * )& value ,
298- sizeof (value ));
482+ ret = boot_request_read (BOOT_REQUEST_BOOT_MODE , BOOT_REQUEST_IMG_BOOTLOADER , & value );
299483 if ((ret == 0 ) && (value == BOOT_REQUEST_MODE_RECOVERY )) {
300484 return true;
301485 }
@@ -310,6 +494,13 @@ int boot_request_enter_firmware_loader(void)
310494 size_t req_entry ;
311495 int ret ;
312496
497+ #ifdef NRF_MCUBOOT_BOOT_REQUEST_PREFERENCE_KEEP
498+ if (!boot_request_updateable ()) {
499+ /* Cannot update area if it is corrupted. */
500+ return - EIO ;
501+ }
502+ #endif
503+
313504 ret = boot_request_entry_find (BOOT_REQUEST_BOOT_MODE , BOOT_REQUEST_IMG_BOOTLOADER ,
314505 & req_entry );
315506 if (ret != 0 ) {
@@ -324,17 +515,9 @@ int boot_request_enter_firmware_loader(void)
324515bool boot_request_detect_firmware_loader (void )
325516{
326517 uint8_t value = BOOT_REQUEST_MODE_INVALID ;
327- size_t req_entry ;
328518 int ret ;
329519
330- ret = boot_request_entry_find (BOOT_REQUEST_BOOT_MODE , BOOT_REQUEST_IMG_BOOTLOADER ,
331- & req_entry );
332- if (ret != 0 ) {
333- return false;
334- }
335-
336- ret = retention_read (bootloader_request_dev , req_entry * sizeof (value ), (void * )& value ,
337- sizeof (value ));
520+ ret = boot_request_read (BOOT_REQUEST_BOOT_MODE , BOOT_REQUEST_IMG_BOOTLOADER , & value );
338521 if ((ret == 0 ) && (value == BOOT_REQUEST_MODE_FIRMWARE_LOADER )) {
339522 return true;
340523 }
0 commit comments