Skip to content

boot: zephyr: serial_recovery: Add boot mode enter ability #1629

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 46 additions & 14 deletions boot/zephyr/Kconfig.serial_recovery
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
# Copyright (c) 2017-2020 Linaro Limited
# Copyright (c) 2020 Arm Limited
# Copyright (c) 2017-2022 Nordic Semiconductor ASA
# Copyright (c) 2017-2023 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0


menuconfig MCUBOOT_SERIAL
bool "MCUboot serial recovery"
default n
select REBOOT
select GPIO
select SERIAL
select UART_INTERRUPT_DRIVEN
select BASE64
Expand Down Expand Up @@ -85,14 +83,6 @@ config BOOT_SERIAL_MAX_RECEIVE_SIZE
by the number of receive buffers, BOOT_LINE_BUFS to allow for
optimal data transfer speeds).

config BOOT_SERIAL_DETECT_DELAY
int "Serial detect pin detection delay time [ms]"
default 0
help
Used to prevent the bootloader from loading on button press.
Useful for powering on when using the same button as
the one used to place the device in bootloader mode.

config BOOT_ERASE_PROGRESSIVELY
bool "Erase flash progressively when receiving new firmware"
default y if SOC_FAMILY_NRF
Expand All @@ -117,6 +107,7 @@ menuconfig ENABLE_MGMT_PERUSER
function is required to process these commands.

if ENABLE_MGMT_PERUSER

config BOOT_MGMT_CUSTOM_STORAGE_ERASE
bool "Enable storage erase command"
help
Expand All @@ -142,8 +133,26 @@ config BOOT_SERIAL_ENCRYPT_EC256
encryption mechanism used in this case is ECIES using primitives
described under "ECIES-P256 encryption" in docs/encrypted_images.md.

config BOOT_SERIAL_WAIT_FOR_DFU
bool "Wait for a prescribed duration to see if DFU is invoked by receiving a mcumgr comand"
menu "Entrance methods"

menuconfig BOOT_SERIAL_ENTRANCE_GPIO
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be a 'choice' in kconfig, or is the intention that you can enable multiple of the modes.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multiple modes can be enabled, e.g. GPIO will be enabled by default but would also probably make sense to enable GPIO and "if application does not exist" - could even then change it so GPIO is only enabled by default if the line is present in the dts but these would all be future plans

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@d3zd3z Are you fine with merging this as is?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, if more than one can be enabled.

bool "GPIO"
default y
depends on GPIO
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Previously Kconfig selects GPIO.
GPIO will be selected by default as its driver should be enabled in DT. Right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the board selects it by default or has a driver that enables it by default, yes, which should be all boards as per the zephyr porting guide.

help
Use a GPIO to enter serial recovery mode.

config BOOT_SERIAL_DETECT_DELAY
int "Serial detect pin detection delay time [ms]"
default 0
depends on BOOT_SERIAL_ENTRANCE_GPIO
help
Used to prevent the bootloader from loading on button press.
Useful for powering on when using the same button as
the one used to place the device in bootloader mode.

menuconfig BOOT_SERIAL_WAIT_FOR_DFU
bool "Wait a prescribed duration to see if DFU is invoked by receiving a MCUmgr comand"
depends on BOOT_SERIAL_UART || BOOT_SERIAL_CDC_ACM
help
If y, MCUboot waits for a prescribed duration of time to allow
Expand All @@ -155,6 +164,29 @@ config BOOT_SERIAL_WAIT_FOR_DFU_TIMEOUT
default 500
depends on BOOT_SERIAL_WAIT_FOR_DFU
help
timeout in ms for MCUboot to wait to allow for DFU to be invoked.
Timeout in ms for MCUboot to wait to allow for DFU to be invoked.

config BOOT_SERIAL_BOOT_MODE
bool "Check boot mode via retention subsystem"
depends on RETENTION_BOOT_MODE
help
Allows for entering serial recovery mode by using Zephyr's boot mode
retention system (i.e. an application must set the boot mode to stay
in serial recovery mode and reboot the module).
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is module?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As in what is the retention system? Or another question?

Copy link

@wbober wbober Apr 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was referring to module in ... reboot the module. How about device or SoC?

I find module a bit ambiguous.


config BOOT_SERIAL_NO_APPLICATION
bool "Stay in bootloader if no application"
help
Allows for entering serial recovery mode if there is no bootable
application that the bootloader can jump to.

config BOOT_SERIAL_PIN_RESET
bool "Check for device reset by pin"
select HWINFO
help
Checks if the module reset was caused by the reset pin and will
remain in bootloader serial recovery mode if it was.

endmenu

endif # MCUBOOT_SERIAL
93 changes: 76 additions & 17 deletions boot/zephyr/main.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* Copyright (c) 2012-2014 Wind River Systems, Inc.
* Copyright (c) 2020 Arm Limited
* Copyright (c) 2021 Nordic Semiconductor ASA
* Copyright (c) 2021-2023 Nordic Semiconductor ASA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -52,6 +52,10 @@ const struct boot_uart_funcs boot_funcs = {
};
#endif

#ifdef CONFIG_BOOT_SERIAL_BOOT_MODE
#include <zephyr/retention/bootmode.h>
#endif

#if defined(CONFIG_BOOT_USB_DFU_WAIT) || defined(CONFIG_BOOT_USB_DFU_GPIO)
#include <zephyr/usb/class/usb_dfu.h>
#endif
Expand All @@ -60,6 +64,10 @@ const struct boot_uart_funcs boot_funcs = {
#include <arm_cleanup.h>
#endif

#ifdef CONFIG_BOOT_SERIAL_PIN_RESET
#include <zephyr/drivers/hwinfo.h>
#endif

/* CONFIG_LOG_MINIMAL is the legacy Kconfig property,
* replaced by CONFIG_LOG_MODE_MINIMAL.
*/
Expand Down Expand Up @@ -120,6 +128,17 @@ static inline bool boot_skip_serial_recovery()

BOOT_LOG_MODULE_REGISTER(mcuboot);

/* Validate serial recovery configuration */
#ifdef CONFIG_MCUBOOT_SERIAL
#if !defined(CONFIG_BOOT_SERIAL_ENTRANCE_GPIO) && \
!defined(CONFIG_BOOT_SERIAL_WAIT_FOR_DFU) && \
!defined(CONFIG_BOOT_SERIAL_BOOT_MODE) && \
!defined(CONFIG_BOOT_SERIAL_NO_APPLICATION) && \
!defined(CONFIG_BOOT_SERIAL_PIN_RESET)
#error "Serial recovery selected without an entrance mode set"
#endif
#endif

#ifdef CONFIG_MCUBOOT_INDICATION_LED

/*
Expand Down Expand Up @@ -385,25 +404,20 @@ void zephyr_boot_log_stop(void)
* !defined(CONFIG_LOG_PROCESS_THREAD) && !defined(ZEPHYR_LOG_MODE_MINIMAL)
*/

#if defined(CONFIG_MCUBOOT_SERIAL) || defined(CONFIG_BOOT_USB_DFU_GPIO)
#if defined(CONFIG_BOOT_SERIAL_ENTRANCE_GPIO) || defined(CONFIG_BOOT_USB_DFU_GPIO)

#ifdef CONFIG_MCUBOOT_SERIAL
#define BUTTON_0_DETECT_DELAY CONFIG_BOOT_SERIAL_DETECT_DELAY
#else
#define BUTTON_0_DETECT_DELAY CONFIG_BOOT_USB_DFU_DETECT_DELAY
#endif


#define BUTTON_0_NODE DT_ALIAS(mcuboot_button0)

#if DT_NODE_EXISTS(BUTTON_0_NODE) && DT_NODE_HAS_PROP(BUTTON_0_NODE, gpios)

static const struct gpio_dt_spec button0 = GPIO_DT_SPEC_GET(BUTTON_0_NODE, gpios);

#elif defined(CONFIG_MCUBOOT_SERIAL) || defined(CONFIG_BOOT_USB_DFU_GPIO)

#else
#error "Serial recovery/USB DFU button must be declared in device tree as 'mcuboot_button0'"

#endif

static bool detect_pin(void)
Expand Down Expand Up @@ -462,12 +476,39 @@ static bool detect_pin(void)
}
#endif

#ifdef CONFIG_MCUBOOT_SERIAL
static void boot_serial_enter()
{
int rc;

#ifdef CONFIG_MCUBOOT_INDICATION_LED
gpio_pin_set_dt(&led0, 1);
#endif

mcuboot_status_change(MCUBOOT_STATUS_SERIAL_DFU_ENTERED);

BOOT_LOG_INF("Enter the serial recovery mode");
rc = boot_console_init();
__ASSERT(rc == 0, "Error initializing boot console.\n");
boot_serial_start(&boot_funcs);
__ASSERT(0, "Bootloader serial process was terminated unexpectedly.\n");
}
#endif

void main(void)
{
struct boot_rsp rsp;
int rc;
FIH_DECLARE(fih_rc, FIH_FAILURE);

#ifdef CONFIG_BOOT_SERIAL_BOOT_MODE
int32_t boot_mode;
#endif

#ifdef CONFIG_BOOT_SERIAL_PIN_RESET
uint32_t reset_cause;
#endif

MCUBOOT_WATCHDOG_FEED();

#if !defined(MCUBOOT_DIRECT_XIP)
Expand All @@ -489,20 +530,19 @@ void main(void)

mcuboot_status_change(MCUBOOT_STATUS_STARTUP);

#ifdef CONFIG_MCUBOOT_SERIAL
#ifdef CONFIG_BOOT_SERIAL_ENTRANCE_GPIO
if (detect_pin() &&
!boot_skip_serial_recovery()) {
#ifdef CONFIG_MCUBOOT_INDICATION_LED
gpio_pin_set_dt(&led0, 1);
boot_serial_enter();
}
#endif

mcuboot_status_change(MCUBOOT_STATUS_SERIAL_DFU_ENTERED);
#ifdef CONFIG_BOOT_SERIAL_PIN_RESET
rc = hwinfo_get_reset_cause(&reset_cause);

BOOT_LOG_INF("Enter the serial recovery mode");
rc = boot_console_init();
__ASSERT(rc == 0, "Error initializing boot console.\n");
boot_serial_start(&boot_funcs);
__ASSERT(0, "Bootloader serial process was terminated unexpectedly.\n");
if (rc == 0 && reset_cause == RESET_PIN) {
(void)hwinfo_clear_reset_cause();
boot_serial_enter();
}
#endif

Expand Down Expand Up @@ -552,6 +592,18 @@ void main(void)

FIH_CALL(boot_go, fih_rc, &rsp);

#ifdef CONFIG_BOOT_SERIAL_BOOT_MODE
boot_mode = bootmode_check(BOOT_MODE_TYPE_BOOTLOADER);

if (boot_mode == 1) {
/* Boot mode to stay in bootloader, clear status and enter serial
* recovery mode
*/
bootmode_clear();
boot_serial_enter();
}
#endif

#ifdef CONFIG_BOOT_SERIAL_WAIT_FOR_DFU
timeout_in_ms -= (k_uptime_get_32() - start);
if( timeout_in_ms <= 0 ) {
Expand All @@ -566,6 +618,13 @@ void main(void)

mcuboot_status_change(MCUBOOT_STATUS_NO_BOOTABLE_IMAGE_FOUND);

#ifdef CONFIG_BOOT_SERIAL_NO_APPLICATION
/* No bootable image and configuration set to remain in serial
* recovery mode
*/
boot_serial_enter();
#endif

FIH_PANIC;
}

Expand Down