Skip to content

Commit f797b2c

Browse files
committed
[nrf noup] zephyr: Keep boot preference after reboot
fixup! [nrf noup] bootloader: Add bootloader requests Add a possibility to keep the boot preference value between device resets. Ref: NCSDK-35479 Signed-off-by: Tomasz Chyrowicz <[email protected]>
1 parent fdcf758 commit f797b2c

File tree

5 files changed

+873
-305
lines changed

5 files changed

+873
-305
lines changed

boot/bootutil/zephyr/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,15 @@ zephyr_library_sources(
1717
../src/bootutil_public.c
1818
)
1919
if(CONFIG_NRF_MCUBOOT_BOOT_REQUEST)
20+
zephyr_library_sources(
21+
src/boot_request.c
22+
)
2023
zephyr_library_sources_ifdef(CONFIG_NRF_MCUBOOT_BOOT_REQUEST_IMPL_RETENTION
2124
src/boot_request_retention.c
2225
)
26+
zephyr_library_sources_ifdef(CONFIG_NRF_MCUBOOT_BOOT_REQUEST_IMPL_FLASH
27+
src/boot_request_flash.c
28+
)
2329
endif()
2430

2531
# Sensitivity to the TEST_BOOT_IMAGE_ACCESS_HOOKS define is implemented for
Lines changed: 378 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,378 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* Copyright (c) 2025 Nordic Semiconductor ASA
5+
*/
6+
#include <bootutil/boot_request.h>
7+
8+
#include "bootutil/bootutil_log.h"
9+
#include "boot_request_mem.h"
10+
11+
/** Special value of image number, indicating a request to the bootloader. */
12+
#define BOOT_REQUEST_IMG_BOOTLOADER 0xFF
13+
14+
/** Additional memory used by the retention subsystem (2B - prefix, 4B - CRC).*/
15+
#define BOOT_REQUEST_ENTRY_METADATA_SIZE (2 + 4)
16+
17+
/** Number of images supported by the bootloader requests. */
18+
#define BOOT_REQUEST_IMG_NUM 2
19+
20+
BOOT_LOG_MODULE_REGISTER(bootloader_request);
21+
22+
enum boot_request_type {
23+
/** Invalid request. */
24+
BOOT_REQUEST_INVALID = 0,
25+
26+
/** Request a change in the bootloader boot mode.
27+
*
28+
* @details Use @p boot_request_mode as argument.
29+
* @p BOOT_REQUEST_IMG_BOOTLOADER as image number.
30+
*
31+
* @note Used to trigger recovery through i.e. retention sybsystem.
32+
*/
33+
BOOT_REQUEST_BOOT_MODE = 1,
34+
35+
/** Select the preferred image to be selected during boot or update.
36+
*
37+
* @details Use @p boot_request_slot_t as argument.
38+
*
39+
* @note Used in the Direct XIP mode.
40+
*/
41+
BOOT_REQUEST_IMG_PREFERENCE = 2,
42+
43+
/** Request a confirmation of an image.
44+
*
45+
* @details Use @p boot_request_slot_t as argument.
46+
*
47+
* @note Used if the code cannot modify the image trailer directly.
48+
*/
49+
BOOT_REQUEST_IMG_CONFIRM = 3,
50+
};
51+
52+
/* Entries inside the boot request shared memory. */
53+
enum boot_request_entry {
54+
BOOT_REQUEST_ENTRY_BOOT_MODE = 0,
55+
BOOT_REQUEST_ENTRY_IMAGE_0_PREFERENCE = 1,
56+
BOOT_REQUEST_ENTRY_IMAGE_0_CONFIRM = 2,
57+
BOOT_REQUEST_ENTRY_IMAGE_1_PREFERENCE = 3,
58+
BOOT_REQUEST_ENTRY_IMAGE_1_CONFIRM = 4,
59+
BOOT_REQUEST_ENTRY_MAX = 5,
60+
};
61+
62+
/* Assert that all requests will fit within the retention area. */
63+
BUILD_ASSERT((BOOT_REQUEST_ENTRY_METADATA_SIZE + BOOT_REQUEST_ENTRY_MAX * sizeof(uint8_t)) <
64+
DT_REG_SIZE_BY_IDX(DT_CHOSEN(nrf_bootloader_request), 0),
65+
"nrf,bootloader-request area is too small for bootloader request struct");
66+
67+
enum boot_request_slot {
68+
/** Unsupported value. */
69+
BOOT_REQUEST_SLOT_INVALID = 0,
70+
/** Primary slot. */
71+
BOOT_REQUEST_SLOT_PRIMARY = 1,
72+
/** Secondary slot. */
73+
BOOT_REQUEST_SLOT_SECONDARY = 2,
74+
};
75+
76+
/** Alias type for the image and number. */
77+
typedef uint8_t boot_request_slot_t;
78+
79+
enum boot_request_mode {
80+
/** Execute a regular boot logic. */
81+
BOOT_REQUEST_MODE_REGULAR = 0,
82+
/** Execute the recovery boot logic. */
83+
BOOT_REQUEST_MODE_RECOVERY = 1,
84+
/** Execute the firmware loader logic. */
85+
BOOT_REQUEST_MODE_FIRMWARE_LOADER = 2,
86+
/** Unsupported value. */
87+
BOOT_REQUEST_MODE_INVALID = 0xFF,
88+
};
89+
90+
/** Alias type for the image number. */
91+
typedef uint8_t boot_request_img_t;
92+
93+
/**
94+
* @brief Find an entry for a given request.
95+
*
96+
* @param[in] type Type of request.
97+
* @param[in] image Image number. Use @p BOOT_REQUEST_IMG_BOOTLOADER for generic requests.
98+
* @param[out] entry Entry to use.
99+
*
100+
* @return 0 on success; nonzero on failure.
101+
*/
102+
static int boot_request_entry_find(enum boot_request_type type, boot_request_img_t image,
103+
size_t *entry)
104+
{
105+
if (entry == NULL) {
106+
return -EINVAL;
107+
}
108+
109+
switch (type) {
110+
case BOOT_REQUEST_BOOT_MODE:
111+
*entry = BOOT_REQUEST_ENTRY_BOOT_MODE;
112+
break;
113+
case BOOT_REQUEST_IMG_PREFERENCE:
114+
switch (image) {
115+
case 0:
116+
*entry = BOOT_REQUEST_ENTRY_IMAGE_0_PREFERENCE;
117+
break;
118+
case 1:
119+
*entry = BOOT_REQUEST_ENTRY_IMAGE_1_PREFERENCE;
120+
break;
121+
default:
122+
return -EINVAL;
123+
}
124+
break;
125+
case BOOT_REQUEST_IMG_CONFIRM:
126+
switch (image) {
127+
case 0:
128+
*entry = BOOT_REQUEST_ENTRY_IMAGE_0_CONFIRM;
129+
break;
130+
case 1:
131+
*entry = BOOT_REQUEST_ENTRY_IMAGE_1_CONFIRM;
132+
break;
133+
default:
134+
return -EINVAL;
135+
}
136+
break;
137+
default:
138+
return -EINVAL;
139+
}
140+
141+
return 0;
142+
}
143+
144+
int boot_request_init(void)
145+
{
146+
return boot_request_mem_init();
147+
}
148+
149+
int boot_request_clear(void)
150+
{
151+
#ifdef CONFIG_NRF_MCUBOOT_BOOT_REQUEST_PREFERENCE_KEEP
152+
size_t nv_indexes[BOOT_REQUEST_IMG_NUM];
153+
uint8_t image;
154+
155+
for (image = 0; image < BOOT_REQUEST_IMG_NUM; image++) {
156+
if (boot_request_entry_find(BOOT_REQUEST_IMG_PREFERENCE, image,
157+
&nv_indexes[image]) != 0) {
158+
return -EINVAL;
159+
}
160+
}
161+
162+
return boot_request_mem_selective_erase(nv_indexes, BOOT_REQUEST_IMG_NUM);
163+
#else
164+
return boot_request_mem_selective_erase(NULL, 0);
165+
#endif
166+
}
167+
168+
int boot_request_confirm_slot(uint8_t image, enum boot_slot slot)
169+
{
170+
uint8_t value = BOOT_REQUEST_SLOT_INVALID;
171+
size_t req_entry;
172+
int ret;
173+
174+
#ifdef CONFIG_NRF_MCUBOOT_BOOT_REQUEST_PREFERENCE_KEEP
175+
if (!boot_request_mem_updateable()) {
176+
BOOT_LOG_ERR("Unable to confirm slot - area not updateable.");
177+
/* Cannot update area if it is corrupted. */
178+
return -EIO;
179+
}
180+
#endif
181+
182+
ret = boot_request_entry_find(BOOT_REQUEST_IMG_CONFIRM, image, &req_entry);
183+
if (ret != 0) {
184+
return ret;
185+
}
186+
187+
switch (slot) {
188+
case BOOT_SLOT_PRIMARY:
189+
value = BOOT_REQUEST_SLOT_PRIMARY;
190+
break;
191+
case BOOT_SLOT_SECONDARY:
192+
value = BOOT_REQUEST_SLOT_SECONDARY;
193+
break;
194+
default:
195+
return -EINVAL;
196+
}
197+
198+
return boot_request_mem_write(req_entry * sizeof(value), &value);
199+
}
200+
201+
bool boot_request_check_confirmed_slot(uint8_t image, enum boot_slot slot)
202+
{
203+
uint8_t value = BOOT_REQUEST_SLOT_INVALID;
204+
size_t req_entry;
205+
int ret;
206+
207+
ret = boot_request_entry_find(BOOT_REQUEST_IMG_CONFIRM, image, &req_entry);
208+
if (ret != 0) {
209+
return false;
210+
}
211+
212+
ret = boot_request_mem_read(req_entry * sizeof(uint8_t), &value);
213+
if (ret != 0) {
214+
return false;
215+
}
216+
217+
switch (value) {
218+
case BOOT_REQUEST_SLOT_PRIMARY:
219+
return (slot == BOOT_SLOT_PRIMARY);
220+
case BOOT_REQUEST_SLOT_SECONDARY:
221+
return (slot == BOOT_SLOT_SECONDARY);
222+
default:
223+
break;
224+
}
225+
226+
return false;
227+
}
228+
229+
int boot_request_set_preferred_slot(uint8_t image, enum boot_slot slot)
230+
{
231+
uint8_t value = BOOT_REQUEST_SLOT_INVALID;
232+
size_t req_entry;
233+
int ret;
234+
235+
#ifdef CONFIG_NRF_MCUBOOT_BOOT_REQUEST_PREFERENCE_KEEP
236+
if (!boot_request_mem_updateable()) {
237+
BOOT_LOG_ERR("Unable to select slot - area not updateable.");
238+
/* Cannot update area if it is corrupted. */
239+
return -EIO;
240+
}
241+
#endif
242+
243+
ret = boot_request_entry_find(BOOT_REQUEST_IMG_PREFERENCE, image, &req_entry);
244+
if (ret != 0) {
245+
return ret;
246+
}
247+
248+
switch (slot) {
249+
case BOOT_SLOT_PRIMARY:
250+
value = BOOT_REQUEST_SLOT_PRIMARY;
251+
break;
252+
case BOOT_SLOT_SECONDARY:
253+
value = BOOT_REQUEST_SLOT_SECONDARY;
254+
break;
255+
default:
256+
return -EINVAL;
257+
}
258+
259+
return boot_request_mem_write(req_entry * sizeof(value), &value);
260+
}
261+
262+
enum boot_slot boot_request_get_preferred_slot(uint8_t image)
263+
{
264+
uint8_t value = BOOT_REQUEST_SLOT_INVALID;
265+
size_t req_entry;
266+
int ret;
267+
268+
ret = boot_request_entry_find(BOOT_REQUEST_IMG_PREFERENCE, image, &req_entry);
269+
if (ret != 0) {
270+
return BOOT_SLOT_NONE;
271+
}
272+
273+
ret = boot_request_mem_read(req_entry * sizeof(uint8_t), &value);
274+
if (ret != 0) {
275+
return BOOT_SLOT_NONE;
276+
}
277+
278+
switch (value) {
279+
case BOOT_REQUEST_SLOT_PRIMARY:
280+
return BOOT_SLOT_PRIMARY;
281+
case BOOT_REQUEST_SLOT_SECONDARY:
282+
return BOOT_SLOT_SECONDARY;
283+
default:
284+
break;
285+
}
286+
287+
return BOOT_SLOT_NONE;
288+
}
289+
290+
int boot_request_enter_recovery(void)
291+
{
292+
uint8_t value = BOOT_REQUEST_MODE_RECOVERY;
293+
size_t req_entry;
294+
int ret;
295+
296+
#ifdef CONFIG_NRF_MCUBOOT_BOOT_REQUEST_PREFERENCE_KEEP
297+
if (!boot_request_mem_updateable()) {
298+
BOOT_LOG_ERR("Unable to enter recovery - area not updateable.");
299+
/* Cannot update area if it is corrupted. */
300+
return -EIO;
301+
}
302+
#endif
303+
304+
ret = boot_request_entry_find(BOOT_REQUEST_BOOT_MODE, BOOT_REQUEST_IMG_BOOTLOADER,
305+
&req_entry);
306+
if (ret != 0) {
307+
return ret;
308+
}
309+
310+
return boot_request_mem_write(req_entry * sizeof(value), &value);
311+
}
312+
313+
#ifdef CONFIG_NRF_BOOT_SERIAL_BOOT_REQ
314+
bool boot_request_detect_recovery(void)
315+
{
316+
uint8_t value = BOOT_REQUEST_MODE_INVALID;
317+
size_t req_entry;
318+
int ret;
319+
320+
ret = boot_request_entry_find(BOOT_REQUEST_BOOT_MODE, BOOT_REQUEST_IMG_BOOTLOADER,
321+
&req_entry);
322+
if (ret != 0) {
323+
return false;
324+
}
325+
326+
ret = boot_request_mem_read(req_entry * sizeof(uint8_t), &value);
327+
if ((ret == 0) && (value == BOOT_REQUEST_MODE_RECOVERY)) {
328+
return true;
329+
}
330+
331+
return false;
332+
}
333+
#endif /* CONFIG_NRF_BOOT_SERIAL_BOOT_REQ */
334+
335+
int boot_request_enter_firmware_loader(void)
336+
{
337+
uint8_t value = BOOT_REQUEST_MODE_FIRMWARE_LOADER;
338+
size_t req_entry;
339+
int ret;
340+
341+
#ifdef CONFIG_NRF_MCUBOOT_BOOT_REQUEST_PREFERENCE_KEEP
342+
if (!boot_request_mem_updateable()) {
343+
BOOT_LOG_ERR("Unable to enter FW loader - area not updateable.");
344+
/* Cannot update area if it is corrupted. */
345+
return -EIO;
346+
}
347+
#endif
348+
349+
ret = boot_request_entry_find(BOOT_REQUEST_BOOT_MODE, BOOT_REQUEST_IMG_BOOTLOADER,
350+
&req_entry);
351+
if (ret != 0) {
352+
return ret;
353+
}
354+
355+
return boot_request_mem_write(req_entry * sizeof(value), &value);
356+
}
357+
358+
#ifdef CONFIG_NRF_BOOT_FIRMWARE_LOADER_BOOT_REQ
359+
bool boot_request_detect_firmware_loader(void)
360+
{
361+
uint8_t value = BOOT_REQUEST_MODE_INVALID;
362+
size_t req_entry;
363+
int ret;
364+
365+
ret = boot_request_entry_find(BOOT_REQUEST_BOOT_MODE, BOOT_REQUEST_IMG_BOOTLOADER,
366+
&req_entry);
367+
if (ret != 0) {
368+
return false;
369+
}
370+
371+
ret = boot_request_mem_read(req_entry * sizeof(uint8_t), &value);
372+
if ((ret == 0) && (value == BOOT_REQUEST_MODE_FIRMWARE_LOADER)) {
373+
return true;
374+
}
375+
376+
return false;
377+
}
378+
#endif /* CONFIG_NRF_BOOT_FIRMWARE_LOADER_BOOT_REQ */

0 commit comments

Comments
 (0)