examples/mcuboot: add example to update from binary in local storage

This example makes it possible to use a binary from local storage for MCUBoot update.
It copies the binary file to the secondary slot instead of downloading from a remote URL.
Can be used to update from a SD Card, for example.

Signed-off-by: Filipe Cavalcanti <filipe.cavalcanti@espressif.com>
This commit is contained in:
Filipe Cavalcanti
2025-07-14 21:46:08 -03:00
committed by Xiang Xiao
parent a7e4a2a30e
commit d27d6e635f
5 changed files with 429 additions and 0 deletions

View File

@@ -0,0 +1,33 @@
# ##############################################################################
# apps/examples/mcuboot/update_agent_local/CMakeLists.txt
#
# SPDX-License-Identifier: Apache-2.0
#
# Licensed to the Apache Software Foundation (ASF) under one or more contributor
# license agreements. See the NOTICE file distributed with this work for
# additional information regarding copyright ownership. The ASF licenses this
# file to you under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
#
# ##############################################################################
if(CONFIG_EXAMPLES_MCUBOOT_LOCAL_AGENT)
nuttx_add_application(
NAME
${CONFIG_EXAMPLES_MCUBOOT_LOCAL_AGENT_PROGNAME}
SRCS
mcuboot_local_agent_main.c
STACKSIZE
${CONFIG_EXAMPLES_MCUBOOT_LOCAL_AGENT_STACKSIZE}
PRIORITY
${CONFIG_EXAMPLES_MCUBOOT_LOCAL_AGENT_PRIORITY})
endif()

View File

@@ -0,0 +1,41 @@
#
# For a description of the syntax of this configuration file,
# see the file kconfig-language.txt in the NuttX tools repository.
#
config EXAMPLES_MCUBOOT_LOCAL_AGENT
tristate "MCUBoot Local Update Agent"
default n
depends on BOOT_MCUBOOT
select BOARDCTL
select BOARDCTL_RESET
---help---
Enable the MCUBoot Local Update Agent example.
This application reads a firmware binary from local storage
and copies it to the MCUBoot secondary flash slot for update.
if EXAMPLES_MCUBOOT_LOCAL_AGENT
config EXAMPLES_MCUBOOT_LOCAL_AGENT_PROGNAME
string "Program name"
default "mcuboot_local_agent"
---help---
This is the name of the program that will be used when the NSH ELF
program is installed.
config EXAMPLES_MCUBOOT_LOCAL_AGENT_PRIORITY
int "MCUBoot Local Agent task priority"
default 100
config EXAMPLES_MCUBOOT_LOCAL_AGENT_STACKSIZE
int "MCUBoot Local Agent stack size"
default DEFAULT_TASK_STACKSIZE
config EXAMPLES_MCUBOOT_LOCAL_AGENT_DEFAULT_PATH
string "Default firmware file path"
default "/mnt/sdcard/firmware.bin"
---help---
Default path to the firmware binary file on local storage.
This can be overridden by passing a path as a command line argument.
endif # EXAMPLES_MCUBOOT_LOCAL_AGENT

View File

@@ -0,0 +1,25 @@
############################################################################
# apps/examples/mcuboot/update_agent_local/Make.defs
#
# SPDX-License-Identifier: Apache-2.0
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership. The
# ASF licenses this file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance with the
# License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
############################################################################
ifneq ($(CONFIG_EXAMPLES_MCUBOOT_LOCAL_AGENT),)
CONFIGURED_APPS += $(APPDIR)/examples/mcuboot/update_agent_local
endif

View File

@@ -0,0 +1,32 @@
############################################################################
# apps/examples/mcuboot/update_agent_local/Makefile
#
# SPDX-License-Identifier: Apache-2.0
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership. The
# ASF licenses this file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance with the
# License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
############################################################################
include $(APPDIR)/Make.defs
MAINSRC = mcuboot_local_agent_main.c
PROGNAME = $(CONFIG_EXAMPLES_MCUBOOT_LOCAL_AGENT_PROGNAME)
PRIORITY = $(CONFIG_EXAMPLES_MCUBOOT_LOCAL_AGENT_PRIORITY)
STACKSIZE = $(CONFIG_EXAMPLES_MCUBOOT_LOCAL_AGENT_STACKSIZE)
MODULE = $(CONFIG_EXAMPLES_MCUBOOT_LOCAL_AGENT)
include $(APPDIR)/Application.mk

View File

@@ -0,0 +1,298 @@
/****************************************************************************
* apps/examples/mcuboot/update_agent_local/mcuboot_local_agent_main.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/boardctl.h>
#include <bootutil/bootutil_public.h>
#include "flash_map_backend/flash_map_backend.h"
#include "sysflash/sysflash.h"
/****************************************************************************
* Preprocessor Definitions
****************************************************************************/
#define BUFFER_SIZE 4096
#define DEFAULT_FW_PATH "/mnt/sdcard/firmware.bin"
/****************************************************************************
* Private Types
****************************************************************************/
struct update_context_s
{
FAR const struct flash_area *fa;
uint32_t fa_offset;
ssize_t image_size;
int fd;
};
/****************************************************************************
* Private Data
****************************************************************************/
static uint8_t g_buffer[BUFFER_SIZE];
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: get_file_size
*
* Description:
* Retrieves the size of an open file descriptor.
*
* Input Parameters:
* fd - File descriptor of the open file.
*
* Returned Value:
* On success, returns the file size in bytes.
* On failure, returns -1.
*
****************************************************************************/
static ssize_t get_file_size(int fd)
{
struct stat st;
if (fstat(fd, &st) < 0)
{
return -1;
}
return st.st_size;
}
/****************************************************************************
* Name: copy_firmware_from_local
*
* Description:
* Copies a firmware binary from local storage to the secondary flash slot.
*
* Input Parameters:
* filepath - Path to the firmware binary file.
*
* Returned Value:
* On success, returns OK.
* On failure, returns an error code.
*
****************************************************************************/
static int copy_firmware_from_local(FAR const char *filepath)
{
int ret = OK;
struct update_context_s ctx;
ssize_t bytes_read;
uint32_t total_copied = 0;
uint32_t progress;
/* Initialize context */
memset(&ctx, 0, sizeof(ctx));
ctx.fa = NULL;
ctx.fa_offset = 0;
ctx.image_size = -1;
ctx.fd = -1;
/* Open firmware file from local storage */
ctx.fd = open(filepath, O_RDONLY);
if (ctx.fd < 0)
{
fprintf(stderr, "Failed to open firmware file: %s\n", filepath);
ret = -errno;
goto exit;
}
/* Get file size */
ctx.image_size = get_file_size(ctx.fd);
if (ctx.image_size < 0)
{
fprintf(stderr, "Failed to get file size\n");
ret = -errno;
goto exit_close_file;
}
printf("Firmware file size: %zd bytes\n", ctx.image_size);
/* Open secondary flash area for MCUBoot */
ret = flash_area_open(FLASH_AREA_IMAGE_SECONDARY(0), &ctx.fa);
if (ret != OK)
{
fprintf(stderr, "Failed to open secondary flash area: %d\n", ret);
goto exit_close_file;
}
/* Check if file fits in secondary slot */
if (ctx.image_size > ctx.fa->fa_size)
{
fprintf(stderr, "Firmware file too large for secondary slot\n");
fprintf(stderr, "File size: %zd, Slot size: %lu\n",
ctx.image_size, ctx.fa->fa_size);
ret = -EFBIG;
goto exit_close_flash;
}
/* Erase secondary slot */
printf("Erasing secondary flash slot...\n");
ret = flash_area_erase(ctx.fa, 0, ctx.fa->fa_size);
if (ret != OK)
{
fprintf(stderr, "Failed to erase secondary flash area: %d\n", ret);
goto exit_close_flash;
}
/* Copy firmware from local storage to flash */
printf("Copying firmware to secondary slot...\n");
while (total_copied < ctx.image_size)
{
/* Read from local file */
bytes_read = read(ctx.fd, g_buffer, BUFFER_SIZE);
if (bytes_read < 0)
{
fprintf(stderr, "Failed to read from firmware file: %d\n", errno);
ret = -errno;
goto exit_close_flash;
}
if (bytes_read == 0)
{
break; /* EOF reached */
}
/* Adjust bytes to write if near end of file */
if (total_copied + bytes_read > ctx.image_size)
{
bytes_read = ctx.image_size - total_copied;
}
/* Write to flash */
ret = flash_area_write(ctx.fa, ctx.fa_offset, g_buffer, bytes_read);
if (ret != OK)
{
fprintf(stderr, "Failed to write to flash: %d\n", ret);
goto exit_close_flash;
}
ctx.fa_offset += bytes_read;
total_copied += bytes_read;
/* Show progress */
progress = (total_copied * 100) / ctx.image_size;
printf("Progress: %lu/%zd bytes [%lu%%]\n",
total_copied, ctx.image_size, progress);
}
printf("Firmware copy completed successfully!\n");
exit_close_flash:
flash_area_close(ctx.fa);
exit_close_file:
if (ctx.fd >= 0)
{
close(ctx.fd);
}
exit:
return ret;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* mcuboot_local_agent_main
****************************************************************************/
int main(int argc, FAR char *argv[])
{
int ret;
FAR const char *filepath;
printf("MCUBoot Local Update Agent\n");
if (argc > 1)
{
filepath = argv[1];
}
else
{
filepath = DEFAULT_FW_PATH;
}
printf("Firmware file: %s\n", filepath);
/* Copy firmware from local storage to secondary flash slot */
ret = copy_firmware_from_local(filepath);
if (ret != OK)
{
fprintf(stderr, "Firmware update failed: %d\n", ret);
return ERROR;
}
printf("Firmware successfully copied to secondary slot!\n");
/* Mark image as pending for next boot */
boot_set_pending_multi(0, 0);
printf("Update scheduled for next boot. Restarting...\n");
fflush(stdout);
fflush(stderr);
usleep(1000000); /* Wait 1 second */
/* Restart system to apply update */
boardctl(BOARDIOC_RESET, 0);
return OK;
}