retention: Add bootloader configuration interface

Adds a bootloader configuration interface which allows for a
bootloader (e.g. MCUboot) to set configuration in a shared data
area which is then read by the application.

Signed-off-by: Jamie McCrae <jamie.mccrae@nordicsemi.no>
This commit is contained in:
Jamie McCrae 2023-03-14 12:37:49 +00:00 committed by Anas Nashif
parent 59c501c6df
commit db4febc584
6 changed files with 270 additions and 5 deletions

View file

@ -0,0 +1,58 @@
/*
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief Public API for boot mode interface
*/
#ifndef ZEPHYR_INCLUDE_RETENTION_BLINFO_
#define ZEPHYR_INCLUDE_RETENTION_BLINFO_
#include <stdint.h>
#include <stddef.h>
#include <zephyr/kernel.h>
#if defined(CONFIG_RETENTION_BOOTLOADER_INFO_TYPE_MCUBOOT)
#include <bootutil/boot_status.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Bootloader info interface
* @defgroup bootloader_info_interface Bootloader info interface
* @ingroup retention_api
* @{
*/
#if IS_ENABLED(CONFIG_RETENTION_BOOTLOADER_INFO_OUTPUT_FUNCTION) || defined(__DOXYGEN__)
/**
* @brief Returns bootinfo information.
*
* @param key The information to return (for MCUboot: minor TLV).
* @param val Where the return information will be placed.
* @param val_len_max The maximum size of the provided buffer.
*
* @retval 0 If successful.
* @retval -EOVERFLOW If the data is too large to fit the supplied buffer.
* @retval -EIO If the requested key was not found.
* @retval -errno Error code.
*/
int blinfo_lookup(uint16_t key, char *val, int val_len_max);
#endif
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif /* ZEPHYR_INCLUDE_RETENTION_BLINFO_ */

View file

@ -624,11 +624,13 @@ flagged.
"BOOT_SERIAL_CDC_ACM", # Used in (sysbuild-based) test "BOOT_SERIAL_CDC_ACM", # Used in (sysbuild-based) test
"BOOT_SERIAL_ENTRANCE_GPIO", # Used in (sysbuild-based) test "BOOT_SERIAL_ENTRANCE_GPIO", # Used in (sysbuild-based) test
"BOOT_SERIAL_IMG_GRP_HASH", # Used in documentation "BOOT_SERIAL_IMG_GRP_HASH", # Used in documentation
"BOOT_SIGNATURE_KEY_FILE", # MCUboot setting used by sysbuild "BOOT_SHARE_DATA", # Used in Kconfig text
"BOOT_SHARE_BACKEND_RETENTION", # Used in Kconfig text
"BOOT_SIGNATURE_KEY_FILE", # MCUboot setting used by sysbuild
"BOOT_SIGNATURE_TYPE_ECDSA_P256", # MCUboot setting used by sysbuild "BOOT_SIGNATURE_TYPE_ECDSA_P256", # MCUboot setting used by sysbuild
"BOOT_SIGNATURE_TYPE_ED25519", # MCUboot setting used by sysbuild "BOOT_SIGNATURE_TYPE_ED25519", # MCUboot setting used by sysbuild
"BOOT_SIGNATURE_TYPE_NONE", # MCUboot setting used by sysbuild "BOOT_SIGNATURE_TYPE_NONE", # MCUboot setting used by sysbuild
"BOOT_SIGNATURE_TYPE_RSA", # MCUboot setting used by sysbuild "BOOT_SIGNATURE_TYPE_RSA", # MCUboot setting used by sysbuild
"BOOT_VALIDATE_SLOT0", # Used in (sysbuild-based) test "BOOT_VALIDATE_SLOT0", # Used in (sysbuild-based) test
"BOOT_WATCHDOG_FEED", # Used in (sysbuild-based) test "BOOT_WATCHDOG_FEED", # Used in (sysbuild-based) test
"BTTESTER_LOG_LEVEL", # Used in tests/bluetooth/tester "BTTESTER_LOG_LEVEL", # Used in tests/bluetooth/tester

View file

@ -3,3 +3,8 @@
zephyr_library() zephyr_library()
zephyr_library_sources(retention.c) zephyr_library_sources(retention.c)
zephyr_library_sources_ifdef(CONFIG_RETENTION_BOOT_MODE bootmode.c) zephyr_library_sources_ifdef(CONFIG_RETENTION_BOOT_MODE bootmode.c)
if(CONFIG_RETENTION_BOOTLOADER_INFO_TYPE_MCUBOOT)
zephyr_library_sources(blinfo_mcuboot.c)
zephyr_link_libraries(MCUBOOT_BOOTUTIL)
endif()

View file

@ -17,7 +17,7 @@ config RETENTION_INIT_PRIORITY
default 86 default 86
help help
Retention device initialization priority (must be higher than init Retention device initialization priority (must be higher than init
priorities for retained memory drivers. priorities for retained memory drivers).
config RETENTION_MUTEXES config RETENTION_MUTEXES
bool bool
@ -55,6 +55,8 @@ config RETENTION_BOOT_MODE
byte must be created and set as the "zephyr,boot-mode" chosen node byte must be created and set as the "zephyr,boot-mode" chosen node
via device tree. via device tree.
source "subsys/retention/Kconfig.blinfo"
endmenu endmenu
module = RETENTION module = RETENTION

View file

@ -0,0 +1,55 @@
# Copyright (c) 2023, Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
menuconfig RETENTION_BOOTLOADER_INFO
bool "Bootloader info"
help
Adds a bootloader information sharing system which allows for
retreiving data from the bootloader when data sharing is enabled.
if RETENTION_BOOTLOADER_INFO
# Workaround for not being able to have commas in macro arguments
DT_CHOSEN_BOOTLOADER_INFO := zephyr,bootloader-info
config RETENTION_BOOTLOADER_INFO_TYPE_MCUBOOT
bool "MCUboot"
depends on !MCUBOOT && BOOTLOADER_MCUBOOT
depends on $(dt_chosen_enabled,$(DT_CHOSEN_BOOTLOADER_INFO))
select MCUBOOT_BOOTUTIL_LIB
help
Adds a bootloader information sharing system for MCUboot and
applications which allows applications to read the configuration of
MCUboot and the running image. This can be used by applications so
that they know how to e.g. handle firmware updates and place them
into the correct slot.
In order to use this, a retention area must be created and set as
the "zephyr,bootloader-info" chosen node via device tree, MCUboot
must be built with the same "zephyr,bootloader-info" DTS node and
have CONFIG_BOOT_SHARE_DATA, CONFIG_BOOT_SHARE_DATA_BOOTINFO and
CONFIG_BOOT_SHARE_BACKEND_RETENTION enabled, or the shared
information will not be accessible.
config RETENTION_BOOTLOADER_INFO_INIT_PRIORITY
int "Bootloader info init priority"
default 87
help
Bootloader info initialization priority (must be higher than init
priorities for for retention subsystem).
config RETENTION_BOOTLOADER_INFO_OUTPUT_FUNCTION
bool "Function"
default y
help
Allows bootloader settings to be fetched by calling a function which
will update a buffer with the requested data.
config RETENTION_BOOTLOADER_INFO_OUTPUT_SETTINGS
bool "Settings"
depends on SETTINGS
help
Allows bootloader settings to be fetched using settings with the
"blinfo" prefix.
endif

View file

@ -0,0 +1,143 @@
/*
* Copyright (c) 2023, Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <limits.h>
#include <zephyr/kernel.h>
#include <zephyr/init.h>
#include <zephyr/devicetree.h>
#include <zephyr/retention/retention.h>
#include <zephyr/logging/log.h>
#include <zephyr/settings/settings.h>
#include <zephyr/retention/blinfo.h>
#include <bootutil/boot_status.h>
LOG_MODULE_REGISTER(blinfo_mcuboot, CONFIG_RETENTION_LOG_LEVEL);
static const struct device *bootloader_info_dev =
DEVICE_DT_GET(DT_CHOSEN(zephyr_bootloader_info));
#if !defined(CONFIG_RETENTION_BOOTLOADER_INFO_OUTPUT_FUNCTION)
static
#endif
int blinfo_lookup(uint16_t key, char *val, int val_len_max)
{
struct shared_data_tlv_header header;
struct shared_data_tlv_entry tlv_entry = {0};
uintptr_t offset = SHARED_DATA_HEADER_SIZE;
int rc;
rc = retention_read(bootloader_info_dev, 0, (void *)&header, sizeof(header));
if (rc != 0) {
return rc;
}
/* Iterate over the whole shared MCUboot data section and look for a TLV with
* the required tag.
*/
while (offset < header.tlv_tot_len) {
rc = retention_read(bootloader_info_dev, offset, (void *)&tlv_entry,
sizeof(tlv_entry));
if (rc != 0) {
return rc;
}
if (GET_MAJOR(tlv_entry.tlv_type) == TLV_MAJOR_BLINFO &&
GET_MINOR(tlv_entry.tlv_type) == key) {
/* Return an error if the provided buffer is too small to fit the
* value in it, bootloader values are small and concise and should
* be able to fit in a single buffer.
*/
if (tlv_entry.tlv_len > val_len_max) {
return -EOVERFLOW;
}
offset += SHARED_DATA_ENTRY_HEADER_SIZE;
rc = retention_read(bootloader_info_dev, offset, val,
tlv_entry.tlv_len);
if (rc != 0) {
return rc;
}
return tlv_entry.tlv_len;
}
offset += SHARED_DATA_ENTRY_SIZE(tlv_entry.tlv_len);
}
/* Return IO error as a valid key name was provided but the TLV was not found in
* the shared data section.
*/
return -EIO;
}
#if defined(CONFIG_RETENTION_BOOTLOADER_INFO_OUTPUT_SETTINGS)
static int blinfo_handle_get(const char *name, char *val, int val_len_max);
static struct settings_handler blinfo_handler = {
.name = "blinfo",
.h_get = blinfo_handle_get,
};
static int blinfo_handle_get(const char *name, char *val, int val_len_max)
{
const char *next;
uint16_t index;
/* Allowed keys are mode, signature_type, recovery, running_slot, bootloader_version
* and max_application_size which cannot contain any additional entries
*/
if (settings_name_steq(name, "mode", &next) && !next) {
index = BLINFO_MODE;
} else if (settings_name_steq(name, "signature_type", &next) && !next) {
index = BLINFO_SIGNATURE_TYPE;
} else if (settings_name_steq(name, "recovery", &next) && !next) {
index = BLINFO_RECOVERY;
} else if (settings_name_steq(name, "running_slot", &next) && !next) {
index = BLINFO_RUNNING_SLOT;
} else if (settings_name_steq(name, "bootloader_version", &next) && !next) {
index = BLINFO_BOOTLOADER_VERSION;
} else if (settings_name_steq(name, "max_application_size", &next) && !next) {
index = BLINFO_MAX_APPLICATION_SIZE;
} else {
return -ENOENT;
}
return blinfo_lookup(index, val, val_len_max);
}
#endif
static int blinfo_init(void)
{
int rc;
rc = retention_is_valid(bootloader_info_dev);
if (rc == 1 || rc == -ENOTSUP) {
struct shared_data_tlv_header header;
rc = retention_read(bootloader_info_dev, 0, (void *)&header, sizeof(header));
if (rc == 0 && header.tlv_magic != SHARED_DATA_TLV_INFO_MAGIC) {
/* Unknown data present */
LOG_ERR("MCUboot data load failed, expected magic value: 0x%x, got: 0x%x",
SHARED_DATA_TLV_INFO_MAGIC, header.tlv_magic);
rc = -EINVAL;
}
}
#if defined(CONFIG_RETENTION_BOOTLOADER_INFO_OUTPUT_SETTINGS)
if (rc == 0) {
settings_register(&blinfo_handler);
}
#endif
return rc;
}
SYS_INIT(blinfo_init, APPLICATION, CONFIG_RETENTION_BOOTLOADER_INFO_INIT_PRIORITY);