Commit 4da51013 authored by Jamie McCrae's avatar Jamie McCrae Committed by Jamie
Browse files

zephyr: Add shared data support



Adds the ability to share mcuboot configuration with the
application using Zephyr's retention subsystem.

Signed-off-by: default avatarJamie McCrae <jamie.mccrae@nordicsemi.no>
parent 9bef51ce
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@
#include "bootutil/image.h"
#include "flash_map_backend/flash_map_backend.h"

#if !defined(MCUBOOT_CUSTOM_DATA_SHARING_FUNCTION)
/**
 * @var shared_memory_init_done
 *
@@ -113,6 +114,7 @@ boot_add_data_to_shared_area(uint8_t major_type,
    return SHARED_MEMORY_OK;
}
#endif /* MCUBOOT_MEASURED_BOOT OR MCUBOOT_DATA_SHARING */
#endif /* !MCUBOOT_CUSTOM_DATA_SHARING_FUNCTION */

#ifdef MCUBOOT_MEASURED_BOOT
/* See in boot_record.h */
+19 −0
Original line number Diff line number Diff line
@@ -93,6 +93,12 @@ if(NOT DEFINED CONFIG_FLASH_PAGE_LAYOUT)
    )
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 @@ zephyr_library_sources(
  ${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)

+79 −5
Original line number Diff line number Diff line
@@ -374,17 +374,91 @@ config BOOT_MAX_IMG_SECTORS
	  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"
+25 −0
Original line number Diff line number Diff line
@@ -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
+124 −0
Original line number Diff line number Diff line
/*
 * 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;
}