zephyr: Add shared data support
Adds the ability to share mcuboot configuration with the
application using Zephyr's retention subsystem.
Signed-off-by: Jamie McCrae <jamie.mccrae@nordicsemi.no>
diff --git a/boot/zephyr/CMakeLists.txt b/boot/zephyr/CMakeLists.txt
index d15977e..638bd91 100644
--- a/boot/zephyr/CMakeLists.txt
+++ b/boot/zephyr/CMakeLists.txt
@@ -93,6 +93,12 @@
)
endif()
+if(DEFINED CONFIG_BOOT_SHARE_BACKEND_RETENTION)
+ zephyr_library_sources(
+ shared_data.c
+ )
+endif()
+
# Generic bootutil sources and includes.
zephyr_library_include_directories(${BOOT_DIR}/bootutil/include)
zephyr_library_sources(
@@ -106,6 +112,19 @@
${BOOT_DIR}/bootutil/src/fault_injection_hardening.c
)
+if(DEFINED CONFIG_MEASURED_BOOT OR DEFINED CONFIG_BOOT_SHARE_DATA)
+ zephyr_library_sources(
+ ${BOOT_DIR}/bootutil/src/boot_record.c
+ )
+
+ # Set a define for this file which will allow inclusion of the Zephyr version
+ # include file
+ set_source_files_properties(
+ ${BOOT_DIR}/bootutil/src/boot_record.c
+ PROPERTIES COMPILE_FLAGS -DZEPHYR_VER_INCLUDE=1
+ )
+endif()
+
# library which might be common source code for MCUBoot and an application
zephyr_link_libraries(MCUBOOT_BOOTUTIL)
diff --git a/boot/zephyr/Kconfig b/boot/zephyr/Kconfig
index e8b6309..9517e91 100644
--- a/boot/zephyr/Kconfig
+++ b/boot/zephyr/Kconfig
@@ -374,17 +374,91 @@
memory usage; larger values allow it to support larger images.
If unsure, leave at the default value.
-config MEASURED_BOOT
- bool "Store the boot state/measurements in shared memory"
+config BOOT_SHARE_BACKEND_AVAILABLE
+ bool
default n
help
+ Hidden open which indicates if there is a sharing backend available.
+
+# Workaround for not being able to have commas in macro arguments
+DT_CHOSEN_BOOTLOADER_INFO := zephyr,bootloader-info
+
+config BOOT_SHARE_BACKEND_AVAILABLE
+ bool
+ default n
+ help
+ Hidden open which indicates if there is a sharing backend available.
+
+choice BOOT_SHARE_BACKEND
+ prompt "Shared data backend"
+ default BOOT_SHARE_BACKEND_DISABLED
+
+config BOOT_SHARE_BACKEND_DISABLED
+ bool "Disabled"
+ help
+ No data sharing support.
+
+config BOOT_SHARE_BACKEND_RETENTION
+ bool "Retention"
+ depends on RETENTION
+ depends on $(dt_chosen_enabled,$(DT_CHOSEN_BOOTLOADER_INFO))
+ select BOOT_SHARE_BACKEND_AVAILABLE
+ help
+ Use retention to share data with application. Requires:
+ - Retained memory area
+ - Retention partition of retained memory area
+ - Chosen node "zephyr,bootloader-info" to be set to the retention
+ partition
+
+config BOOT_SHARE_BACKEND_EXTERNAL
+ bool "External (user-provided code)"
+ select BOOT_SHARE_BACKEND_AVAILABLE
+ help
+ Use a custom user-specified storage.
+
+endchoice
+
+menuconfig BOOT_SHARE_DATA
+ bool "Save application specific data"
+ default n
+ depends on BOOT_SHARE_BACKEND_AVAILABLE
+ help
+ This will allow data to be shared between MCUboot and an application,
+ it does not include any informatiom by default.
+
+ Note: This requires a backend to function, see
+ BOOT_SHARE_BACKEND_RETENTION for details on using the retention
+ subsystem as a backend.
+
+config BOOT_SHARE_DATA_BOOTINFO
+ bool "Save boot information data"
+ default n
+ depends on BOOT_SHARE_DATA
+ help
+ This will place information about the MCUboot configuration and
+ running application into a shared memory area.
+
+menuconfig MEASURED_BOOT
+ bool "Store the boot state/measurements in shared memory area"
+ default n
+ depends on BOOT_SHARE_BACKEND_AVAILABLE
+ help
If enabled, the bootloader will store certain boot measurements such as
the hash of the firmware image in a shared memory area. This data can
be used later by runtime services (e.g. by a device attestation service).
-config BOOT_SHARE_DATA
- bool "Save application specific data in shared memory area"
- default n
+ Note: This requires a backend to function, see
+ BOOT_SHARE_BACKEND_RETENTION for details on using the retention
+ subsystem as a backend.
+
+config MEASURED_BOOT_MAX_CBOR_SIZE
+ int "Maximum CBOR size of boot state/measurements"
+ default 64
+ range 0 256
+ depends on MEASURED_BOOT
+ help
+ The maximum size of the CBOR message which stores boot
+ state/measurements.
choice BOOT_FAULT_INJECTION_HARDENING_PROFILE
prompt "Fault injection hardening profile"
diff --git a/boot/zephyr/include/mcuboot_config/mcuboot_config.h b/boot/zephyr/include/mcuboot_config/mcuboot_config.h
index 483d7a5..04e4c59 100644
--- a/boot/zephyr/include/mcuboot_config/mcuboot_config.h
+++ b/boot/zephyr/include/mcuboot_config/mcuboot_config.h
@@ -155,6 +155,18 @@
#define MCUBOOT_DATA_SHARING
#endif
+#ifdef CONFIG_BOOT_SHARE_BACKEND_RETENTION
+#define MCUBOOT_CUSTOM_DATA_SHARING_FUNCTION
+#endif
+
+#ifdef CONFIG_BOOT_SHARE_DATA_BOOTINFO
+#define MCUBOOT_DATA_SHARING_BOOTINFO
+#endif
+
+#ifdef CONFIG_MEASURED_BOOT_MAX_CBOR_SIZE
+#define MAX_BOOT_RECORD_SZ CONFIG_MEASURED_BOOT_MAX_CBOR_SIZE
+#endif
+
#ifdef CONFIG_BOOT_FIH_PROFILE_OFF
#define MCUBOOT_FIH_PROFILE_OFF
#endif
@@ -193,6 +205,10 @@
#define MCUBOOT_VERIFY_IMG_ADDRESS
#endif
+#ifdef CONFIG_MCUBOOT_SERIAL
+#define MCUBOOT_SERIAL
+#endif
+
/*
* The configuration option enables direct image upload with the
* serial recovery.
@@ -266,6 +282,15 @@
#define MCUBOOT_SERIAL_UNALIGNED_BUFFER_SIZE CONFIG_BOOT_SERIAL_UNALIGNED_BUFFER_SIZE
#endif
+#if defined(MCUBOOT_DATA_SHARING) && defined(ZEPHYR_VER_INCLUDE)
+#include <app_version.h>
+
+#define MCUBOOT_VERSION_AVAILABLE
+#define MCUBOOT_VERSION_MAJOR APP_VERSION_MAJOR
+#define MCUBOOT_VERSION_MINOR APP_VERSION_MINOR
+#define MCUBOOT_VERSION_PATCHLEVEL APP_PATCHLEVEL
+#endif
+
/* Support 32-byte aligned flash sizes */
#if DT_HAS_CHOSEN(zephyr_flash)
#if DT_PROP_OR(DT_CHOSEN(zephyr_flash), write_block_size, 0) > 8
diff --git a/boot/zephyr/shared_data.c b/boot/zephyr/shared_data.c
new file mode 100644
index 0000000..5554a7e
--- /dev/null
+++ b/boot/zephyr/shared_data.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2023, Nordic Semiconductor ASA
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <assert.h>
+#include <zephyr/kernel.h>
+#include <zephyr/devicetree.h>
+#include <zephyr/retention/retention.h>
+#include <zephyr/logging/log.h>
+#include <bootutil/boot_record.h>
+#include <bootutil/boot_status.h>
+#include <../../bootutil/src/bootutil_priv.h>
+
+#define SHARED_MEMORY_MIN_SIZE 8
+
+LOG_MODULE_REGISTER(bootloader_info, CONFIG_RETENTION_LOG_LEVEL);
+
+static bool shared_memory_init_done = false;
+static uint16_t shared_data_size = SHARED_DATA_HEADER_SIZE;
+static ssize_t shared_data_max_size = 0;
+static const struct device *bootloader_info_dev =
+ DEVICE_DT_GET(DT_CHOSEN(zephyr_bootloader_info));
+
+BUILD_ASSERT(SHARED_MEMORY_MIN_SIZE < \
+ DT_REG_SIZE_BY_IDX(DT_CHOSEN(zephyr_bootloader_info), 0), \
+ "zephyr,bootloader-info area is too small for bootloader information struct");
+
+int boot_add_data_to_shared_area(uint8_t major_type,
+ uint16_t minor_type,
+ size_t size,
+ const uint8_t *data)
+{
+ struct shared_data_tlv_header header = {
+ .tlv_magic = SHARED_DATA_TLV_INFO_MAGIC,
+ .tlv_tot_len = shared_data_size,
+ };
+ struct shared_data_tlv_entry tlv_entry = {0};
+ uint16_t boot_data_size;
+ uintptr_t tlv_end, offset;
+ int rc;
+
+ if (data == NULL) {
+ return SHARED_MEMORY_GEN_ERROR;
+ }
+
+ /* Check whether first time to call this function. If does then initialise
+ * shared data area.
+ */
+ if (!shared_memory_init_done) {
+ retention_clear(bootloader_info_dev);
+ shared_data_max_size = retention_size(bootloader_info_dev);
+ shared_memory_init_done = true;
+ }
+
+ /* Check whether TLV entry is already added.
+ * Get the boundaries of TLV section
+ */
+ tlv_end = shared_data_size;
+ offset = SHARED_DATA_HEADER_SIZE;
+
+ /* Iterates over the TLV section looks for the same entry if found then
+ * returns with error: SHARED_MEMORY_OVERWRITE
+ */
+ while (offset < tlv_end) {
+ /* Create local copy to avoid unaligned access */
+ rc = retention_read(bootloader_info_dev, offset, (void *)&tlv_entry,
+ SHARED_DATA_ENTRY_HEADER_SIZE);
+
+ if (rc) {
+ return SHARED_MEMORY_READ_ERROR;
+ }
+
+ if (GET_MAJOR(tlv_entry.tlv_type) == major_type &&
+ GET_MINOR(tlv_entry.tlv_type) == minor_type) {
+ return SHARED_MEMORY_OVERWRITE;
+ }
+
+ offset += SHARED_DATA_ENTRY_SIZE(tlv_entry.tlv_len);
+ }
+
+ /* Add TLV entry */
+ tlv_entry.tlv_type = SET_TLV_TYPE(major_type, minor_type);
+ tlv_entry.tlv_len = size;
+
+ if (!boot_u16_safe_add(&boot_data_size, shared_data_size,
+ SHARED_DATA_ENTRY_SIZE(size))) {
+ return SHARED_MEMORY_GEN_ERROR;
+ }
+
+ /* Verify overflow of shared area */
+ if (boot_data_size > shared_data_max_size) {
+ return SHARED_MEMORY_OVERFLOW;
+ }
+
+ offset = shared_data_size;
+ rc = retention_write(bootloader_info_dev, offset, (void*)&tlv_entry,
+ SHARED_DATA_ENTRY_HEADER_SIZE);
+ if (rc) {
+ LOG_ERR("Shared data TLV header write failed: %d", rc);
+ return SHARED_MEMORY_WRITE_ERROR;
+ }
+
+ offset += SHARED_DATA_ENTRY_HEADER_SIZE;
+ rc = retention_write(bootloader_info_dev, offset, data, size);
+
+ if (rc) {
+ LOG_ERR("Shared data TLV data write failed: %d", rc);
+ return SHARED_MEMORY_WRITE_ERROR;
+ }
+
+ shared_data_size += SHARED_DATA_ENTRY_SIZE(size);
+ header.tlv_tot_len = shared_data_size;
+
+ rc = retention_write(bootloader_info_dev, 0, (void *)&header,
+ sizeof(header));
+
+ if (rc) {
+ return SHARED_MEMORY_WRITE_ERROR;
+ }
+
+ return SHARED_MEMORY_OK;
+}