Understanding the S0 vs S1 MCUBoot images and slots

Hello,

We are currently helping one of our client develop a product using the nRF5340.

DK versions:

  • nRF5340-DK
  • PCA10095
  • 2.0.1

Host OS:

  • Ubuntu 22.04.2
  • Linux 6.5.0-41-generic

nRF SDK and toolchains version:

  • nRF SDK 2.6.1
  • toolchains 2.6.1

We need to implement a custom upgrade process, where the application, or the bootloader if possible, would check if an upgrade file exists on the SD-Card, and flash if existing and valid. Both bootloader and application must be upgradable.

We currently have implemented a dual bootloader setup, with an application which reads the update file from the UART (no SD-card at the moment, so purely evaluation purpose) and uses DFUTarget to flash the memory chunk by chunk. This application can upgrade itself without any issue using the build/zephyr/app_update.bin file as the upgrade file sent via UART. I attach you the application and the prj.conf. 

Now I'm trying to understand how MCUBoot is upgraded. From what I read, it uses both S0 and S1 slots, and only boots the latest valid MCUBoot flashed in one of those. 

I can successfully upgrade MCUBoot by alternating with the s0 and s1 update file that I send to my test application implementing DFUTarget. I need clarification about the usage of those two images and how they are flashed:

  • Why is it needed to switch between build/zephyr/signed_by_mcuboot_and_b0_s0_image_update.bin and build/zephyr/signed_by_mcuboot_and_b0_s1_image_update.bin?
  • How will it then be possible to manage multiple devices when deployed on field?
  • Should we always send both and do a check of which on is currently in use to select the correct one to flash? 
  • Why nothing happens when sending s0 image when the device currently uses s0 as the bootloader slot?

Thanks a lot in advance for you help, and please correct me if I'm wrong on any topic I covered asking my questions!

Best regards

David TRUAN

#include <stdio.h>
#include <string.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/sys/crc.h>
#include <zephyr/kernel.h>
#include <zephyr/sys/reboot.h>

#include <dfu/dfu_target.h>
#include <dfu/dfu_target_mcuboot.h>

#include <errno.h>

#include <zephyr/device.h>
#include <zephyr/drivers/flash.h>
#include <zephyr/storage/flash_map.h>
#include <logging.h>

#define PRG_BUFSIZ 32000
#define VERSION 1
#define CHUNK_SZ	1024

#define MCUBOOT_BUF_SZ	512

static const struct device *uart_dev;

/* The buffer to receive  */
static char mcuboot_buf[MCUBOOT_BUF_SZ] __aligned(4);

/**
 * @brief Helper to read the update size using UART
 *
 * @param uart_dev the UART device to use to read
 * @return int32_t >= 0 on success, -1 otherwise
 */
int32_t read_update_size_uart(const struct device *uart_dev) {

	int err;
	uint32_t prg_size = 0;
	uint32_t bytes_read = 0;
	uint8_t byte;


	do {

		err = uart_poll_in(uart_dev, &byte);

		if (err == 0) {

			prg_size |= (int)byte << (bytes_read * 8);
			bytes_read++;

		} else if (err == -1) {

			continue;
		} else {
			log_error("Error detected %d", err);
			return -1;
		}

	} while (bytes_read != sizeof(uint32_t));


	return prg_size;
}

/**
 * @brief Callback handler for DFUTarget events
 *
 * @param evt the event which called the callback
 */
static void dfu_target_callback_handler(enum dfu_target_evt_id evt) {

	switch (evt) {

	case DFU_TARGET_EVT_TIMEOUT:

		printf("DFU_TARGET_EVT_TIMEOUT");
		break;

	case DFU_TARGET_EVT_ERASE_DONE:

		printf("DFU_TARGET_EVT_ERASE_DONE");
		break;

	case DFU_TARGET_EVT_ERASE_PENDING:

		printf("DFU_TARGET_EVT_ERASE_PENDING");
		break;
	}
}

int main(void) {

	static char prg_data_buf[CHUNK_SZ];
	char *prg_data;
	int update_size_full;
	int err;
	int total_bytes_read;

	uart_dev = DEVICE_DT_GET(DT_NODELABEL(uart0));
	prg_data = prg_data_buf;
	total_bytes_read = 0;
	update_size_full = 0;

	k_msleep(1000);

    log_set_level(LOG_LEVEL_DEBUG);


	log_info("current version: %d board: %s", VERSION, CONFIG_BOARD);
	log_info("Waiting for playload...");

	/* Retrieve the update size */
	update_size_full = read_update_size_uart(uart_dev);
	if (update_size_full == -1) {

		log_error("Error while getting update size!");

		return 1;
	}

	log_info("Update size: %d", update_size_full);

	/* Set the buffer used by DFUTarget in MCUBoot mode */
	if ((err = dfu_target_mcuboot_set_buf(mcuboot_buf, MCUBOOT_BUF_SZ)) != 0) {

		log_error("dfu_target_mcuboot_set_buf error: %d", err);

		return -1;
	}

	/* Initialize the DFUTarget library with our image info */
	if ((err = dfu_target_init(DFU_TARGET_IMAGE_TYPE_ANY_APPLICATION, 0, update_size_full, dfu_target_callback_handler)) != 0) {

		log_error("dfu_target_init error: %d", err);

		return -1;
	}


	while (1) {
		err = uart_poll_in(uart_dev, prg_data);

		if (err == 0) {

			prg_data++;
			total_bytes_read++;

			if (total_bytes_read == update_size_full) {

				log_info("Received the full file!");

				dfu_target_write(prg_data_buf, total_bytes_read % CHUNK_SZ);

				break;
			}


			if (total_bytes_read % CHUNK_SZ == 0) {
				dfu_target_write(prg_data_buf, CHUNK_SZ);
				prg_data = prg_data_buf;
			}

		} else if (err == -ENOSYS) {

			log_error("Error RX");

			return 1;
		} else if (err == -EBUSY) {

			log_error("Error UART busy!");

			return 1;
		}
	}

	/* Ask DFU target to clean its data */
	if ((err = dfu_target_done(true)) != 0) {

		log_error("dfu_target_done failed: %d", err);

		return 1;
	} else {

		log_info("DFU target download done!");
	}

	/* Indicate to DFUTarget that the update is ready
	and reboot to boot into the updated app if successful */
	if ((err = dfu_target_schedule_update(-1)) != 0) {

		log_error("dfu_target_schedule_update failed: %d", err);

		return 1;
	} else {

		log_info("DFU target successfully scheduled the update!");

		sys_reboot(SYS_REBOOT_WARM);
	}

	return 0;
}
5488.prj.conf

Parents Reply Children
No Data
Related