Skip to content

Commit 8813f5c

Browse files
author
Mads Bligaard Nielsen
authored
b0 bootloader: new optional mcuboot trailer to support upgradable mcuboot (#1)
b0 bootloader: new optional image trailer to support upgradable mcuboot This feature is always enabled but NOP if magic header not detected. The new optional image trailer in mcuboot s0 and s1 partitions are located in the last 16 bytes of each partition. This feature also require a similar extension of mcuboot. b0 validates and selects which mcuboot slot to boot from (supports fallback, if unconfirmed): 1) mcuboot erase flash sector for mcuboot_image_trailer 2) mcuboot writes new image to flash (e.g. "mcumgr image upload") 3) mcuboot writes mcuboot_image_trailer with value mcuboot_image_trailer_init as final step of image upload 4) device is rebooted via "mcumgr reboot" or external reset 5) b0 checks if one or both mcuboot slots have a trailer.magic, if not normal boot proceeds 6) b0 updates mcuboot_image_trailer.status appropriately: From TESTING to BOOTING (if any TESTING) From BOOTING to INACTIVE (if any BOOTING) 7) b0 gives a slot 1st priority if its trailer.status=TESTING otherwise it prioritizes trailer.status=ACTIVE 8) mcuboot changes trailer.status of booted image to ACTIVE if confirmed by "mcumgr image confirm" and simultaneously marks the not-booted image as INACTIVE NOTE: if any image fails to validate, the other bank will still be attempted as well. Hence b0 can "fallback" to an image otherwise marked as INACTIVE.
1 parent e8bfecd commit 8813f5c

File tree

3 files changed

+141
-1
lines changed

3 files changed

+141
-1
lines changed

include/bl_storage.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,23 @@ uint32_t s0_address_read(void);
9595
*/
9696
uint32_t s1_address_read(void);
9797

98+
/**
99+
* @brief Is slot0 or slot1 the highest priority
100+
*/
101+
typedef enum slot_priority {
102+
SLOT_PRIORITY_S0 = 0,
103+
SLOT_PRIORITY_S1 = 1
104+
} slot_priority;
105+
106+
/**
107+
* @brief Function that checks from optional mcuboot s0/s1 image trailer
108+
* if slot#1 should have priority over slot#0.
109+
* Might also update status in trailer(s) from TESTING to BOOTING
110+
*
111+
* @retval SLOT_PRIORITY_S0 or SLOT_PRIORITY_S1
112+
*/
113+
slot_priority slot_priority_from_image_trailers(void);
114+
98115
/**
99116
* @brief Function for reading number of public key data slots.
100117
*

samples/bootloader/src/main.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,15 @@ void main(void)
9292
uint32_t s1_addr = s1_address_read();
9393
const struct fw_info *s0_info = fw_info_find(s0_addr);
9494
const struct fw_info *s1_info = fw_info_find(s1_addr);
95+
slot_priority priority = slot_priority_from_image_trailers();
9596

96-
if (!s1_info || (s0_info->version >= s1_info->version)) {
97+
if (s0_info && s1_info && (s0_info->version != s1_info->version)) {
98+
if (s0_info->version > s1_info->version)
99+
priority = SLOT_PRIORITY_S0;
100+
else if (s1_info->version > s0_info->version)
101+
priority = SLOT_PRIORITY_S1;
102+
}
103+
if (priority == SLOT_PRIORITY_S0) {
97104
validate_and_boot(s0_info, BOOT_SLOT_0);
98105
validate_and_boot(s1_info, BOOT_SLOT_1);
99106
} else {

subsys/bootloader/bl_storage/bl_storage.c

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@
55
*/
66

77
#include "bl_storage.h"
8+
#include <inttypes.h>
89
#include <string.h>
910
#include <errno.h>
1011
#include <nrf.h>
1112
#include <assert.h>
1213
#include <nrfx_nvmc.h>
1314
#include <pm_config.h>
15+
#include <zephyr/sys/printk.h>
1416

1517
/** A single monotonic counter. It consists of a description value, a 'type',
1618
* to know what this counter counts. Further, it has a number of slots
@@ -39,6 +41,120 @@ struct counter_collection {
3941
#define TYPE_COUNTERS 1 /* Type referring to counter collection. */
4042
#define COUNTER_DESC_VERSION 1 /* Counter description value for firmware version. */
4143

44+
#ifdef PM_S0_OFFSET /* partition manager available */
45+
/* upgradable mcuboot bootloader option, to validate and select which mcuboot slot to boot:
46+
* optional trailer at the end of of flash partitions for mcuboot slot0 and slot1
47+
* 1) mcuboot erase flash sector for mcuboot_image_trailer
48+
* 2) mcuboot writes new image to flash (e.g. "mcumgr image upload")
49+
* 3) mcuboot writes mcuboot_image_trailer with value mcuboot_image_trailer_init as final step of image upload
50+
* 4) device is rebooted via "mcumgr reboot" or external reset
51+
* 5) b0 checks if one or both mcuboot slots have a trailer.magic, if not normal boot proceeds
52+
* 6) b0 updates mcuboot_image_trailer.status appropriately:
53+
* From TESTING to BOOTING (if any TESTING)
54+
* From BOOTING to INACTIVE (if any BOOTING)
55+
* 7) b0 gives a slot 1st priority if its trailer.status=TESTING
56+
otherwise it prioritizes trailer.status=ACTIVE
57+
* 8) mcuboot changes trailer.status of booted image to ACTIVE if confirmed by "mcumgr image confirm"
58+
* and simultaneously marks the not-booted image as INACTIVE
59+
* NOTE: if any image fails to validate, the other bank will still be attempted as well.
60+
* Hence b0 can "fallback" to an image otherwise marked as INACTIVE.
61+
*/
62+
63+
/* Note1: interface cloned by mcuboot/boot/boot_serial/src/boot_serial.c */
64+
/* Note2: these constants rely on bits in flash can be changed from 1 to 0 without erasing */
65+
#define MCUBOOT_IMAGE_TRAILER_STATUS_UNKNOWN 0xFFFFFFFF
66+
#define MCUBOOT_IMAGE_TRAILER_STATUS_TESTING 0xFFFFFFFE
67+
#define MCUBOOT_IMAGE_TRAILER_STATUS_BOOTING 0xFFFFFFFC
68+
#define MCUBOOT_IMAGE_TRAILER_STATUS_ACTIVE 0xFFFFFFF8
69+
#define MCUBOOT_IMAGE_TRAILER_STATUS_INACTIVE 0x00000000
70+
#define MCUBOOT_IMAGE_TRAILER_MAGIC_SZ 3
71+
72+
typedef struct {
73+
uint32_t status; /* mcuboot writes TESTING or ACTIVE, b0 writes BOOTING if TESTING found */
74+
uint32_t magic[MCUBOOT_IMAGE_TRAILER_MAGIC_SZ]; /* mcuboot writes*/
75+
} mcuboot_image_trailer;
76+
77+
const mcuboot_image_trailer mcuboot_image_trailer_init = {
78+
.status = MCUBOOT_IMAGE_TRAILER_STATUS_TESTING,
79+
.magic = {
80+
0x1234beef,
81+
0xbeef1234,
82+
0x1234beef
83+
}
84+
};
85+
86+
static uint32_t check_and_fix_image_trailer(const char* prefix, const mcuboot_image_trailer* trailer)
87+
{
88+
int result = MCUBOOT_IMAGE_TRAILER_STATUS_UNKNOWN;
89+
if (memcmp(&trailer->magic, &mcuboot_image_trailer_init.magic, sizeof(trailer->magic)) == 0) {
90+
result = trailer->status;
91+
switch (trailer->status) {
92+
case MCUBOOT_IMAGE_TRAILER_STATUS_TESTING:
93+
/* change status to BOOTING */
94+
printk("%s: TESTING => BOOTING\n\r", prefix);
95+
__DSB(); /* Because of nRF9160 Erratum 7 */
96+
nrfx_nvmc_word_write((uint32_t)&trailer->status, MCUBOOT_IMAGE_TRAILER_STATUS_BOOTING);
97+
break;
98+
case MCUBOOT_IMAGE_TRAILER_STATUS_BOOTING:
99+
printk("%s: BOOTING => INACTIVE (boot once)\n\r", prefix);
100+
__DSB(); /* Because of nRF9160 Erratum 7 */
101+
nrfx_nvmc_word_write((uint32_t)&trailer->status, MCUBOOT_IMAGE_TRAILER_STATUS_INACTIVE);
102+
break;
103+
case MCUBOOT_IMAGE_TRAILER_STATUS_ACTIVE:
104+
printk("%s: ACTIVE\n\r", prefix);
105+
break;
106+
case MCUBOOT_IMAGE_TRAILER_STATUS_INACTIVE:
107+
printk("%s: INACTIVE\n\r", prefix);
108+
break;
109+
case MCUBOOT_IMAGE_TRAILER_STATUS_UNKNOWN:
110+
printk("%s: UNKNOWN\n\r", prefix);
111+
break;
112+
default:
113+
printk("%s: UNKNOWN, status=0x%"PRIu32"\n\r", prefix, trailer->status);
114+
}
115+
} else {
116+
printk("%s: UNKNOWN (no image trailer)\n\r", prefix);
117+
}
118+
return result;
119+
}
120+
121+
slot_priority slot_priority_from_image_trailers(void)
122+
{
123+
/* preserve behavior of b0 if no magic headers are present, by "prioritizing" S0 as default */
124+
int result = SLOT_PRIORITY_S0;
125+
uint32_t s0_addr = s0_address_read();
126+
uint32_t s1_addr = s1_address_read();
127+
/* sanity check that the addresses from "provisioning" and the PM config match */
128+
if (s0_addr == PM_S0_OFFSET && s1_addr == PM_S1_OFFSET) {
129+
const mcuboot_image_trailer* s0 = (mcuboot_image_trailer*)(s0_addr + PM_S0_SIZE - sizeof(mcuboot_image_trailer));
130+
const mcuboot_image_trailer* s1 = (mcuboot_image_trailer*)(s1_addr + PM_S1_SIZE - sizeof(mcuboot_image_trailer));
131+
uint32_t s0_status = check_and_fix_image_trailer("slot0", s0);
132+
uint32_t s1_status = check_and_fix_image_trailer("slot1", s1);
133+
switch (s1_status) {
134+
case MCUBOOT_IMAGE_TRAILER_STATUS_TESTING:
135+
case MCUBOOT_IMAGE_TRAILER_STATUS_BOOTING:
136+
case MCUBOOT_IMAGE_TRAILER_STATUS_ACTIVE:
137+
switch (s0_status) {
138+
case MCUBOOT_IMAGE_TRAILER_STATUS_TESTING:
139+
case MCUBOOT_IMAGE_TRAILER_STATUS_BOOTING:
140+
break; /* SLOT_PRIORITY_S0 */
141+
default:
142+
result = SLOT_PRIORITY_S1;
143+
}
144+
}
145+
}
146+
return result;
147+
}
148+
149+
#else
150+
151+
slot_priority slot_priority_from_image_trailers(void)
152+
{
153+
return SLOT_PRIORITY_S0;
154+
}
155+
#endif
156+
157+
42158
uint32_t s0_address_read(void)
43159
{
44160
uint32_t addr = BL_STORAGE->s0_address;

0 commit comments

Comments
 (0)