Skip to content

Commit a0e8b6f

Browse files
Emil-JuhlEmil-Juhl
authored andcommitted
boot: boot_serial: add custom BNO_IMAGE_TRAILER support
For B&O there is a requirment to have updateable bootloaders in the products. To achieve this, the NRF secure immutable bootloader (b0) is used together with 2x MCUboot. b0 is a very simple bootloader with no peripheral interactions and simply chooses between the two MCUboot slots. For b0 to choose correctly when upgrading/downgrading MCUboot, an image trailer is added as the last bytes of the corresponding flash partition. This image trailer contains the current status of the given image, i.e. if it is ACTIVE, INACTIVE, ... Like MCUboot, b0 is also modified to accomodate this way of reporting (and updating) status for images. This results in a strong, but hard to enforce, dependency of the trailer interface between MCUboot and b0. This commit copies the image trailer definitions, as specified by b0. And implements writing the image trailer, with default status as TESTING, whenever one of the MCUboot partitions are flashed. For regular images, the trailer should not be written. Additionally, to mark an image as ACTIVE, the mcumgr protocol implementation is extended slightly to also cover the `image confirm` command from the client. This command will mark the currently active image as ACTIVE and mark the inactive MCUboot as INACTIVE, thus locking the selection of the current image. A custom Kconfig is added for the purpose. It is dependent on the MCUBOOT_SERIAL_MCUMGR_SIMPLE_IMAGE_INDEX option due to implementation details only.
1 parent 9457715 commit a0e8b6f

File tree

2 files changed

+173
-1
lines changed

2 files changed

+173
-1
lines changed

boot/boot_serial/src/boot_serial.c

Lines changed: 158 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,74 @@ BOOT_LOG_MODULE_DECLARE(mcuboot);
114114
#define IMAGES_ITER(x)
115115
#endif
116116

117+
#ifdef CONFIG_BNO_IMAGE_STATE_TRAILER
118+
/* Assert dependency on CONFIG_MCUBOOT_SERIAL_MCUMGR_SIMPLE_IMAGE_INDEX */
119+
#ifndef CONFIG_MCUBOOT_SERIAL_MCUMGR_SIMPLE_IMAGE_INDEX
120+
#error "CONFIG_BNO_IMAGE_STATE_TRAILER requires CONFIG_MCUBOOT_SERIAL_MCUMGR_SIMPLE_IMAGE_INDEX to be set"
121+
#endif
122+
/* upgradable mcuboot bootloader option for usage with the B&O custom version of
123+
* NRF secure immutable bootloader (b0).
124+
* Adds an image state trailer at the end of of flash partitions slot0 and slot1
125+
*
126+
* The upgrade flow is then supposed to follow:
127+
*
128+
* 1) mcuboot erase flash sector for mcuboot_image_trailer
129+
*
130+
* 2) mcuboot writes new image to flash (e.g. "mcumgr image upload")
131+
*
132+
* 3) mcuboot writes mcuboot_image_trailer with value mcuboot_image_trailer_init
133+
* as final step of image upload
134+
*
135+
* 4) device is rebooted via "mcumgr reboot" or external reset
136+
*
137+
* 5) b0 checks if one or both mcuboot slots have a trailer.magic. If not,
138+
* normal boot proceeds
139+
*
140+
* 6) b0 updates mcuboot_image_trailer.status appropriately:
141+
* From TESTING to BOOTING (if any TESTING)
142+
* From BOOTING to INACTIVE (if any BOOTING)
143+
*
144+
* 7) b0 gives a slot 1st priority if its trailer.status=TESTING otherwise it
145+
* prioritizes trailer.status=ACTIVE
146+
*
147+
* 8) mcuboot changes trailer.status from BOOTING to ACTIVE if confirmed by
148+
* "mcumgr image confirm" mcuboot also marks passive slot INACTIVE, to avoid
149+
* booting in that (except fallback)
150+
* NOTE: if any image fails to validate, the other bank will still be
151+
* attempted as well. Hence b0 can "fallback" to an image otherwise
152+
* marked as INACTIVE.
153+
* */
154+
155+
/* Note1: interface cloned by `sdk-nrf` in
156+
* subsys/bootloader/bl_storage/bl_storage.c
157+
*
158+
* Note2: these constants rely on bits in flash can be changed from 1 to 0
159+
* without erasing
160+
* */
161+
#define MCUBOOT_IMAGE_TRAILER_STATUS_UNKNOWN 0xFFFFFFFF
162+
#define MCUBOOT_IMAGE_TRAILER_STATUS_TESTING 0xFFFFFFFE
163+
#define MCUBOOT_IMAGE_TRAILER_STATUS_BOOTING 0xFFFFFFFC
164+
#define MCUBOOT_IMAGE_TRAILER_STATUS_ACTIVE 0xFFFFFFF8
165+
#define MCUBOOT_IMAGE_TRAILER_STATUS_INACTIVE 0x00000000
166+
#define MCUBOOT_IMAGE_TRAILER_MAGIC_SZ 3
167+
168+
typedef struct {
169+
uint32_t status; /* mcuboot writes TESTING or ACTIVE, b0 writes BOOTING if
170+
* TESTING found
171+
* */
172+
uint32_t magic[MCUBOOT_IMAGE_TRAILER_MAGIC_SZ]; /* mcuboot writes this*/
173+
} mcuboot_image_trailer;
174+
175+
const mcuboot_image_trailer mcuboot_image_trailer_init = {
176+
.status = MCUBOOT_IMAGE_TRAILER_STATUS_TESTING,
177+
.magic = {
178+
0x1234beef,
179+
0xbeef1234,
180+
0x1234beef
181+
}
182+
};
183+
#endif
184+
117185
static char in_buf[MCUBOOT_SERIAL_MAX_RECEIVE_SIZE + 1];
118186
static char dec_buf[MCUBOOT_SERIAL_MAX_RECEIVE_SIZE + 1];
119187
const struct boot_uart_funcs *boot_uf;
@@ -662,6 +730,33 @@ bs_upload(char *buf, int len)
662730
goto out;
663731
}
664732
#endif
733+
#ifdef CONFIG_BNO_IMAGE_STATE_TRAILER
734+
/* Final step; write the B&O magic header with image booting status
735+
* for b0 immutable bootloader to read.
736+
* We're reusing the existing "status_sector" as implemented by
737+
* mcuboot, as this is used for the same purpose with a different
738+
* setup (hence not conflicting for us).
739+
*
740+
* We can write without erase here because the bs_upload routine has
741+
* taken care of erasing either the entire partition at once or in
742+
* steps (see the code just above).
743+
* */
744+
/* only add the trailer if this is an mcuboot partition */
745+
if (img_num == 3 || img_num == 4) {
746+
off_t tr_sect_off = flash_sector_get_off(&status_sector);
747+
uint32_t tr_sect_sz = flash_sector_get_size(&status_sector);
748+
749+
uint32_t tr_sz = sizeof(mcuboot_image_trailer);
750+
off_t tr_off = tr_sect_off + tr_sect_sz - tr_sz;
751+
if (flash_area_write(fap, tr_off,
752+
(void *)&mcuboot_image_trailer_init,
753+
tr_sz)) {
754+
rc = MGMT_ERR_EUNKNOWN;
755+
goto out;
756+
}
757+
}
758+
#endif
759+
665760
rc = BOOT_HOOK_CALL(boot_serial_uploaded_hook, 0, img_num, fap,
666761
img_size);
667762
if (rc) {
@@ -776,6 +871,62 @@ bs_reset(char *buf, int len)
776871
#endif
777872
}
778873

874+
static void
875+
bs_confirm_active_mcuboot(void)
876+
{
877+
#ifdef CONFIG_BNO_IMAGE_STATE_TRAILER
878+
const struct flash_area *fap;
879+
880+
/* iterate both mcuboot images (id 3 + 4) */
881+
for (uint32_t img = 3; img <= 4; ++img) {
882+
if (flash_area_open(flash_area_id_from_upload_id(img), &fap)) {
883+
goto conf_err;
884+
}
885+
886+
mcuboot_image_trailer image_trailer = mcuboot_image_trailer_init;
887+
if (flash_area_is_active_bootloader(fap)) {
888+
/* Confirm active bank*/
889+
image_trailer.status = MCUBOOT_IMAGE_TRAILER_STATUS_ACTIVE;
890+
} else {
891+
/* Mark other bank inactive */
892+
image_trailer.status = MCUBOOT_IMAGE_TRAILER_STATUS_INACTIVE;
893+
}
894+
895+
/* obtain offsets for the flash page that holds the trailer */
896+
struct flash_sector tr_sect;
897+
if (flash_area_sector_from_off(boot_status_off(fap), &tr_sect)) {
898+
goto conf_err;
899+
}
900+
off_t tr_sect_off = flash_sector_get_off(&tr_sect);
901+
uint32_t tr_sect_sz = flash_sector_get_size(&tr_sect);
902+
903+
/* Erase the flash page first to be able to write to it */
904+
if (flash_area_erase(fap, tr_sect_off, tr_sect_sz)) {
905+
goto conf_err;
906+
}
907+
908+
/* Calculate trailer offset + size */
909+
uint32_t tr_sz = sizeof(mcuboot_image_trailer);
910+
off_t tr_off = tr_sect_off + tr_sect_sz - tr_sz;
911+
/* Write the new image trailer to the flash */
912+
if (flash_area_write(fap, tr_off, (void *)&image_trailer, tr_sz)) {
913+
goto conf_err;
914+
}
915+
916+
/* Close the flash area for good measure */
917+
flash_area_close(fap);
918+
}
919+
bs_rc_rsp(MGMT_ERR_OK);
920+
return;
921+
922+
conf_err:
923+
flash_area_close(fap);
924+
bs_rc_rsp(MGMT_ERR_EUNKNOWN);
925+
#else
926+
bs_rc_rsp(MGMT_ERR_ENOTSUP);
927+
#endif
928+
}
929+
779930
/*
780931
* Parse incoming line of input from console.
781932
* Expect newtmgr protocol with serial transport.
@@ -805,7 +956,13 @@ boot_serial_input(char *buf, int len)
805956
if (hdr->nh_group == MGMT_GROUP_ID_IMAGE) {
806957
switch (hdr->nh_id) {
807958
case IMGMGR_NMGR_ID_STATE:
808-
bs_list(buf, len);
959+
if (hdr->nh_op == NMGR_OP_READ) {
960+
/* State read is the "image list" command */
961+
bs_list(buf, len);
962+
} else {
963+
/* State write is the "image confirm" command */
964+
bs_confirm_active_mcuboot();
965+
}
809966
break;
810967
case IMGMGR_NMGR_ID_UPLOAD:
811968
bs_upload(buf, len);

boot/zephyr/Kconfig

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,21 @@ config PM_PARTITION_SIZE_MCUBOOT_SECONDARY
8282
help
8383
The secondary mcuboot partition is used for net-core image
8484

85+
config BNO_IMAGE_STATE_TRAILER
86+
bool "Add support for the B&O image state trailer"
87+
default n
88+
depends on MCUBOOT_SERIAL_MCUMGR_SIMPLE_IMAGE_INDEX
89+
help
90+
Adds support for the custom B&O image state
91+
trailer, which will then be placed as the
92+
the last part of the last flash sector in the
93+
flash area of the corresponding image.
94+
This state trailer is supported in the custom
95+
B&O NRF immutable bootloader.
96+
Current implementation is dependent on the
97+
MCUBOOT_SERIAL_MCUMGR_SIMPLE_IMAGE_INDEX
98+
option.
99+
85100
config MCUBOOT_SERIAL_MCUMGR_SIMPLE_IMAGE_INDEX
86101
bool "mcumgr:image_upload assumes n=1+image*2+slot"
87102
default n

0 commit comments

Comments
 (0)