boot_serial: Add optional img mgmt slot info feature
Adds a minimal version of the slot info feature to serial recovery,
and enables it by default.
Signed-off-by: Jamie McCrae <jamie.mccrae@nordicsemi.no>
diff --git a/boot/boot_serial/src/boot_serial.c b/boot/boot_serial/src/boot_serial.c
index fb73e6b..2e9df80 100644
--- a/boot/boot_serial/src/boot_serial.c
+++ b/boot/boot_serial/src/boot_serial.c
@@ -95,12 +95,28 @@
#else
#define BOOT_SERIAL_HASH_SIZE_MAX 0
#endif
+#ifdef MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO
+#define BOOT_SERIAL_SLOT_INFO_SIZE_MAX 164
+#else
+#define BOOT_SERIAL_SLOT_INFO_SIZE_MAX 0
+#endif
-#define BOOT_SERIAL_OUT_MAX ((128 + BOOT_SERIAL_IMAGE_STATE_SIZE_MAX + \
- BOOT_SERIAL_HASH_SIZE_MAX) * BOOT_IMAGE_NUMBER)
+#if (128 + BOOT_SERIAL_IMAGE_STATE_SIZE_MAX + BOOT_SERIAL_HASH_SIZE_MAX) > \
+ BOOT_SERIAL_SLOT_INFO_SIZE_MAX
+#define BOOT_SERIAL_MAX_MESSAGE_SIZE (128 + BOOT_SERIAL_IMAGE_STATE_SIZE_MAX + \
+ BOOT_SERIAL_HASH_SIZE_MAX)
+#else
+#define BOOT_SERIAL_MAX_MESSAGE_SIZE BOOT_SERIAL_SLOT_INFO_SIZE_MAX
+#endif
+
+#define BOOT_SERIAL_OUT_MAX (BOOT_SERIAL_MAX_MESSAGE_SIZE * BOOT_IMAGE_NUMBER)
#define BOOT_SERIAL_FRAME_MTU 124 /* 127 - pkt start (2 bytes) and stop (1 byte) */
+/* Number of estimated CBOR elements for responses */
+#define CBOR_ENTRIES_SLOT_INFO_IMAGE_MAP 4
+#define CBOR_ENTRIES_SLOT_INFO_SLOTS_MAP 3
+
#ifdef __ZEPHYR__
/* base64 lib encodes data to null-terminated string */
#define BASE64_ENCODE_SIZE(in_size) ((((((in_size) - 1) / 3) * 4) + 4) + 1)
@@ -259,11 +275,7 @@
int swap_status = boot_swap_type_multi(image_index);
#endif
-#ifdef MCUBOOT_SINGLE_APPLICATION_SLOT
- for (slot = 0; slot < 1; slot++) {
-#else
- for (slot = 0; slot < 2; slot++) {
-#endif
+ for (slot = 0; slot < MCUBOOT_IMAGE_NUMBER; slot++) {
FIH_DECLARE(fih_rc, FIH_FAILURE);
uint8_t tmpbuf[64];
@@ -576,6 +588,139 @@
}
}
+#ifdef MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO
+static void
+bs_slot_info(uint8_t op, char *buf, int len)
+{
+ uint32_t slot, area_id;
+ const struct flash_area *fap;
+ uint8_t image_index = 0;
+ int rc;
+ bool ok = true;
+ const struct image_max_size *image_max_sizes;
+
+ if (op != NMGR_OP_READ) {
+ bs_rc_rsp(MGMT_ERR_ENOTSUP);
+ }
+
+ image_max_sizes = boot_get_max_app_size();
+
+ zcbor_map_start_encode(cbor_state, 1);
+ zcbor_tstr_put_lit_cast(cbor_state, "images");
+ zcbor_list_start_encode(cbor_state, MCUBOOT_IMAGE_NUMBER);
+
+ IMAGES_ITER(image_index) {
+ for (slot = 0; slot < MCUBOOT_IMAGE_NUMBER; slot++) {
+ if (slot == 0) {
+ ok = zcbor_map_start_encode(cbor_state, CBOR_ENTRIES_SLOT_INFO_IMAGE_MAP) &&
+ zcbor_tstr_put_lit(cbor_state, "image") &&
+ zcbor_uint32_put(cbor_state, (uint32_t)image_index) &&
+ zcbor_tstr_put_lit(cbor_state, "slots") &&
+ zcbor_list_start_encode(cbor_state, MCUBOOT_IMAGE_NUMBER);
+
+ if (!ok) {
+ goto finish;
+ }
+ }
+
+ ok = zcbor_map_start_encode(cbor_state, CBOR_ENTRIES_SLOT_INFO_SLOTS_MAP) &&
+ zcbor_tstr_put_lit(cbor_state, "slot") &&
+ zcbor_uint32_put(cbor_state, slot);
+
+ if (!ok) {
+ goto finish;
+ }
+
+ area_id = flash_area_id_from_multi_image_slot(image_index, slot);
+ rc = flash_area_open(area_id, &fap);
+
+ if (rc) {
+ ok = zcbor_tstr_put_lit(cbor_state, "rc") &&
+ zcbor_int32_put(cbor_state, rc);
+ } else {
+ if (sizeof(fap->fa_size) == sizeof(uint64_t)) {
+ ok = zcbor_tstr_put_lit(cbor_state, "size") &&
+ zcbor_uint64_put(cbor_state, fap->fa_size);
+ } else {
+ ok = zcbor_tstr_put_lit(cbor_state, "size") &&
+ zcbor_uint32_put(cbor_state, fap->fa_size);
+ }
+
+ if (!ok) {
+ flash_area_close(fap);
+ goto finish;
+ }
+
+ /*
+ * Check if we support uploading to this slot and if so, return the
+ * image ID
+ */
+#if defined(MCUBOOT_SINGLE_APPLICATION_SLOT)
+ ok = zcbor_tstr_put_lit(cbor_state, "upload_image_id") &&
+ zcbor_uint32_put(cbor_state, (image_index + 1));
+#elif defined(MCUBOOT_SERIAL_DIRECT_IMAGE_UPLOAD)
+ ok = zcbor_tstr_put_lit(cbor_state, "upload_image_id") &&
+ zcbor_uint32_put(cbor_state, (image_index * 2 + slot + 1));
+#else
+ if (slot == 1) {
+ ok = zcbor_tstr_put_lit(cbor_state, "upload_image_id") &&
+ zcbor_uint32_put(cbor_state, (image_index * 2 + 1));
+ }
+#endif
+
+ flash_area_close(fap);
+
+ if (!ok) {
+ goto finish;
+ }
+
+ ok = zcbor_map_end_encode(cbor_state, CBOR_ENTRIES_SLOT_INFO_SLOTS_MAP);
+
+ if (!ok) {
+ goto finish;
+ }
+
+ if (slot == (MCUBOOT_IMAGE_NUMBER - 1)) {
+ ok = zcbor_list_end_encode(cbor_state, MCUBOOT_IMAGE_NUMBER);
+
+ if (!ok) {
+ goto finish;
+ }
+
+ if (image_max_sizes[image_index].calculated == true) {
+ ok = zcbor_tstr_put_lit(cbor_state, "max_image_size") &&
+ zcbor_uint32_put(cbor_state,
+ image_max_sizes[image_index].max_size);
+
+ if (!ok) {
+ goto finish;
+ }
+ }
+
+ ok = zcbor_map_end_encode(cbor_state, CBOR_ENTRIES_SLOT_INFO_IMAGE_MAP);
+
+ }
+ }
+
+ if (!ok) {
+ goto finish;
+ }
+ }
+ }
+
+ ok = zcbor_list_end_encode(cbor_state, MCUBOOT_IMAGE_NUMBER) &&
+ zcbor_map_end_encode(cbor_state, 1);
+
+finish:
+ if (!ok) {
+ reset_cbor_state();
+ bs_rc_rsp(MGMT_ERR_ENOMEM);
+ }
+
+ boot_serial_output();
+}
+#endif
+
#ifdef MCUBOOT_ERASE_PROGRESSIVELY
/** Erases range of flash, aligned to sector size
*
@@ -1019,6 +1164,11 @@
case IMGMGR_NMGR_ID_UPLOAD:
bs_upload(buf, len);
break;
+#ifdef MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO
+ case IMGMGR_NMGR_ID_SLOT_INFO:
+ bs_slot_info(hdr->nh_op, buf, len);
+ break;
+#endif
default:
bs_rc_rsp(MGMT_ERR_ENOTSUP);
break;
diff --git a/boot/boot_serial/src/boot_serial_priv.h b/boot/boot_serial/src/boot_serial_priv.h
index 7056aef..1801da1 100644
--- a/boot/boot_serial/src/boot_serial_priv.h
+++ b/boot/boot_serial/src/boot_serial_priv.h
@@ -81,6 +81,7 @@
*/
#define IMGMGR_NMGR_ID_STATE 0
#define IMGMGR_NMGR_ID_UPLOAD 1
+#define IMGMGR_NMGR_ID_SLOT_INFO 6
void boot_serial_input(char *buf, int len);
extern const struct boot_uart_funcs *boot_uf;
diff --git a/boot/bootutil/src/loader.c b/boot/bootutil/src/loader.c
index 4b0299f..8663fbf 100644
--- a/boot/bootutil/src/loader.c
+++ b/boot/bootutil/src/loader.c
@@ -63,10 +63,23 @@
static struct boot_loader_state boot_data;
-#if defined(MCUBOOT_DATA_SHARING)
+#if defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) || defined(MCUBOOT_DATA_SHARING)
static struct image_max_size image_max_sizes[BOOT_IMAGE_NUMBER] = {0};
#endif
+#if !defined(__BOOTSIM__)
+/* Used for holding static buffers in multiple functions to work around issues
+ * in older versions of gcc (e.g. 4.8.4)
+ */
+struct sector_buffer_t {
+ boot_sector_t *primary;
+ boot_sector_t *secondary;
+#if MCUBOOT_SWAP_USING_SCRATCH
+ boot_sector_t *scratch;
+#endif
+};
+#endif
+
#if (BOOT_IMAGE_NUMBER > 1)
#define IMAGES_ITER(x) for ((x) = 0; (x) < BOOT_IMAGE_NUMBER; ++(x))
#else
@@ -1840,7 +1853,7 @@
int rc;
FIH_DECLARE(fih_rc, FIH_FAILURE);
-#if defined(MCUBOOT_DATA_SHARING)
+#if defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) || defined(MCUBOOT_DATA_SHARING)
int max_size;
#endif
@@ -1870,7 +1883,7 @@
return;
}
-#if defined(MCUBOOT_DATA_SHARING)
+#if defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) || defined(MCUBOOT_DATA_SHARING)
/* Fetch information on maximum sizes for later usage, if needed */
max_size = app_max_size(state);
@@ -2100,11 +2113,36 @@
#endif
}
+#if !defined(__BOOTSIM__)
+static void boot_get_sector_buffers(struct sector_buffer_t *buffers)
+{
+ /* The array of slot sectors are defined here (as opposed to file scope) so
+ * that they don't get allocated for non-boot-loader apps. This is
+ * necessary because the gcc option "-fdata-sections" doesn't seem to have
+ * any effect in older gcc versions (e.g., 4.8.4).
+ */
+ static boot_sector_t primary_slot_sectors[BOOT_IMAGE_NUMBER][BOOT_MAX_IMG_SECTORS];
+ static boot_sector_t secondary_slot_sectors[BOOT_IMAGE_NUMBER][BOOT_MAX_IMG_SECTORS];
+#if MCUBOOT_SWAP_USING_SCRATCH
+ static boot_sector_t scratch_sectors[BOOT_MAX_IMG_SECTORS];
+#endif
+
+ buffers->primary = (boot_sector_t *)&primary_slot_sectors;
+ buffers->secondary = (boot_sector_t *)&secondary_slot_sectors;
+#if MCUBOOT_SWAP_USING_SCRATCH
+ buffers->scratch = (boot_sector_t *)&scratch_sectors;
+#endif
+}
+#endif
+
fih_ret
context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp)
{
size_t slot;
struct boot_status bs;
+#if !defined(__BOOTSIM__)
+ struct sector_buffer_t sector_buffers;
+#endif
int rc = -1;
FIH_DECLARE(fih_rc, FIH_FAILURE);
int fa_id;
@@ -2112,6 +2150,7 @@
bool has_upgrade;
volatile int fih_cnt;
+#if defined(__BOOTSIM__)
/* The array of slot sectors are defined here (as opposed to file scope) so
* that they don't get allocated for non-boot-loader apps. This is
* necessary because the gcc option "-fdata-sections" doesn't seem to have
@@ -2122,6 +2161,7 @@
#if MCUBOOT_SWAP_USING_SCRATCH
TARGET_STATIC boot_sector_t scratch_sectors[BOOT_MAX_IMG_SECTORS];
#endif
+#endif
has_upgrade = false;
@@ -2129,6 +2169,10 @@
(void)has_upgrade;
#endif
+#if !defined(__BOOTSIM__)
+ boot_get_sector_buffers(§or_buffers);
+#endif
+
/* Iterate over all the images. By the end of the loop the swap type has
* to be determined for each image and all aborted swaps have to be
* completed.
@@ -2149,6 +2193,15 @@
image_index = BOOT_CURR_IMG(state);
+#if !defined(__BOOTSIM__)
+ BOOT_IMG(state, BOOT_PRIMARY_SLOT).sectors =
+ §or_buffers.primary[image_index];
+ BOOT_IMG(state, BOOT_SECONDARY_SLOT).sectors =
+ §or_buffers.secondary[image_index];
+#if MCUBOOT_SWAP_USING_SCRATCH
+ state->scratch.sectors = sector_buffers.scratch;
+#endif
+#else
BOOT_IMG(state, BOOT_PRIMARY_SLOT).sectors =
primary_slot_sectors[image_index];
BOOT_IMG(state, BOOT_SECONDARY_SLOT).sectors =
@@ -2156,6 +2209,7 @@
#if MCUBOOT_SWAP_USING_SCRATCH
state->scratch.sectors = scratch_sectors;
#endif
+#endif
/* Open primary and secondary image areas for the duration
* of this call.
@@ -3337,12 +3391,100 @@
}
}
-#if defined(MCUBOOT_DATA_SHARING)
+#if defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO)
/**
-* Fetches the maximum allowed size of the image
-*/
+ * Reads image data to find out the maximum application sizes. Only needs to
+ * be called in serial recovery mode, as the state informatio is unpopulated
+ * at that time
+ */
+static void boot_fetch_slot_state_sizes(void)
+{
+ struct sector_buffer_t sector_buffers;
+ size_t slot;
+ int rc = -1;
+ int fa_id;
+ int image_index;
+
+ boot_get_sector_buffers(§or_buffers);
+
+ IMAGES_ITER(BOOT_CURR_IMG(&boot_data)) {
+ int max_size = 0;
+
+ image_index = BOOT_CURR_IMG(&boot_data);
+
+ BOOT_IMG(&boot_data, BOOT_PRIMARY_SLOT).sectors =
+ §or_buffers.primary[image_index];
+ BOOT_IMG(&boot_data, BOOT_SECONDARY_SLOT).sectors =
+ §or_buffers.secondary[image_index];
+#if MCUBOOT_SWAP_USING_SCRATCH
+ boot_data.scratch.sectors = sector_buffers.scratch;;
+#endif
+
+ /* Open primary and secondary image areas for the duration
+ * of this call.
+ */
+ for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) {
+ fa_id = flash_area_id_from_multi_image_slot(image_index, slot);
+ rc = flash_area_open(fa_id, &BOOT_IMG_AREA(&boot_data, slot));
+ assert(rc == 0);
+
+ if (rc != 0) {
+ goto finish;
+ }
+ }
+
+#if MCUBOOT_SWAP_USING_SCRATCH
+ rc = flash_area_open(FLASH_AREA_IMAGE_SCRATCH,
+ &BOOT_SCRATCH_AREA(&boot_data));
+ assert(rc == 0);
+
+ if (rc != 0) {
+ goto finish;
+ }
+#endif
+
+ /* Determine the sector layout of the image slots and scratch area. */
+ rc = boot_read_sectors(&boot_data);
+
+ if (rc == 0) {
+ max_size = app_max_size(&boot_data);
+
+ if (max_size > 0) {
+ image_max_sizes[image_index].calculated = true;
+ image_max_sizes[image_index].max_size = max_size;
+ }
+ }
+ }
+
+finish:
+ close_all_flash_areas(&boot_data);
+ memset(&boot_data, 0x00, sizeof(boot_data));
+}
+#endif
+
+#if defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) || defined(MCUBOOT_DATA_SHARING)
+/**
+ * Fetches the maximum allowed size of the image
+ */
const struct image_max_size *boot_get_max_app_size(void)
{
+#if defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO)
+ uint8_t i = 0;
+
+ while (i < BOOT_IMAGE_NUMBER) {
+ if (image_max_sizes[i].calculated == true) {
+ break;
+ }
+
+ ++i;
+ }
+
+ if (i == BOOT_IMAGE_NUMBER) {
+ /* Information not available, need to fetch it */
+ boot_fetch_slot_state_sizes();
+ }
+#endif
+
return image_max_sizes;
}
#endif
diff --git a/boot/zephyr/Kconfig.serial_recovery b/boot/zephyr/Kconfig.serial_recovery
index fe82697..7ed9a7d 100644
--- a/boot/zephyr/Kconfig.serial_recovery
+++ b/boot/zephyr/Kconfig.serial_recovery
@@ -202,4 +202,11 @@
If y, image states will be included with image lists and the set
state command can be used to mark an image as test/confirmed.
+config BOOT_SERIAL_IMG_GRP_SLOT_INFO
+ bool "Slot info"
+ default y if UPDATEABLE_IMAGE_NUMBER > 1
+ help
+ If y, will include the slot info command which lists what available
+ slots there are in the system.
+
endif # MCUBOOT_SERIAL
diff --git a/boot/zephyr/include/mcuboot_config/mcuboot_config.h b/boot/zephyr/include/mcuboot_config/mcuboot_config.h
index ab2e8ba..fe1ec2e 100644
--- a/boot/zephyr/include/mcuboot_config/mcuboot_config.h
+++ b/boot/zephyr/include/mcuboot_config/mcuboot_config.h
@@ -233,6 +233,10 @@
#define MCUBOOT_SERIAL_IMG_GRP_IMAGE_STATE
#endif
+#ifdef CONFIG_BOOT_SERIAL_IMG_GRP_SLOT_INFO
+#define MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO
+#endif
+
#ifdef CONFIG_MCUBOOT_SERIAL
#define MCUBOOT_SERIAL_RECOVERY
#endif