diff --git a/include/zephyr/retention/blinfo.h b/include/zephyr/retention/blinfo.h new file mode 100644 index 0000000000..0ecd051771 --- /dev/null +++ b/include/zephyr/retention/blinfo.h @@ -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 +#include +#include + +#if defined(CONFIG_RETENTION_BOOTLOADER_INFO_TYPE_MCUBOOT) +#include +#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_ */ diff --git a/scripts/ci/check_compliance.py b/scripts/ci/check_compliance.py index 140c59d4fb..a54d2f807a 100755 --- a/scripts/ci/check_compliance.py +++ b/scripts/ci/check_compliance.py @@ -624,11 +624,13 @@ flagged. "BOOT_SERIAL_CDC_ACM", # Used in (sysbuild-based) test "BOOT_SERIAL_ENTRANCE_GPIO", # Used in (sysbuild-based) test "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_ED25519", # 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_ED25519", # MCUboot setting used by sysbuild + "BOOT_SIGNATURE_TYPE_NONE", # MCUboot setting used by sysbuild + "BOOT_SIGNATURE_TYPE_RSA", # MCUboot setting used by sysbuild "BOOT_VALIDATE_SLOT0", # Used in (sysbuild-based) test "BOOT_WATCHDOG_FEED", # Used in (sysbuild-based) test "BTTESTER_LOG_LEVEL", # Used in tests/bluetooth/tester diff --git a/subsys/retention/CMakeLists.txt b/subsys/retention/CMakeLists.txt index 6cea5b490d..a70469a417 100644 --- a/subsys/retention/CMakeLists.txt +++ b/subsys/retention/CMakeLists.txt @@ -3,3 +3,8 @@ zephyr_library() zephyr_library_sources(retention.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() diff --git a/subsys/retention/Kconfig b/subsys/retention/Kconfig index 0f3def9abc..e42d7e7ebf 100644 --- a/subsys/retention/Kconfig +++ b/subsys/retention/Kconfig @@ -17,7 +17,7 @@ config RETENTION_INIT_PRIORITY default 86 help Retention device initialization priority (must be higher than init - priorities for retained memory drivers. + priorities for retained memory drivers). config RETENTION_MUTEXES bool @@ -55,6 +55,8 @@ config RETENTION_BOOT_MODE byte must be created and set as the "zephyr,boot-mode" chosen node via device tree. +source "subsys/retention/Kconfig.blinfo" + endmenu module = RETENTION diff --git a/subsys/retention/Kconfig.blinfo b/subsys/retention/Kconfig.blinfo new file mode 100644 index 0000000000..57c5222511 --- /dev/null +++ b/subsys/retention/Kconfig.blinfo @@ -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 diff --git a/subsys/retention/blinfo_mcuboot.c b/subsys/retention/blinfo_mcuboot.c new file mode 100644 index 0000000000..35845dee71 --- /dev/null +++ b/subsys/retention/blinfo_mcuboot.c @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2023, Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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);