From fcc2c2ab5524ff9f391fe003daf62d08eaaf1b72 Mon Sep 17 00:00:00 2001 From: Jamie McCrae Date: Tue, 16 Jul 2024 14:01:34 +0100 Subject: [PATCH 1/4] mgmt: mcumgr: grp: img_mgmt: Add slot info command Adds a new command that will return information on slots themselves, listing the slot sizes, without any internal information about the slot data. Will also return the maximum size of an image for a set of slots if built using sysbuild. Signed-off-by: Jamie McCrae --- .../mgmt/mcumgr/grp/img_mgmt/img_mgmt.h | 3 +- .../mcumgr/grp/img_mgmt/img_mgmt_callbacks.h | 38 +++ include/zephyr/mgmt/mcumgr/mgmt/callbacks.h | 6 + subsys/mgmt/mcumgr/grp/img_mgmt/Kconfig | 14 + .../mgmt/mcumgr/grp/img_mgmt/src/img_mgmt.c | 256 +++++++++++++++++- 5 files changed, 315 insertions(+), 2 deletions(-) diff --git a/include/zephyr/mgmt/mcumgr/grp/img_mgmt/img_mgmt.h b/include/zephyr/mgmt/mcumgr/grp/img_mgmt/img_mgmt.h index 82e642c73757..49dffc6b7ca3 100644 --- a/include/zephyr/mgmt/mcumgr/grp/img_mgmt/img_mgmt.h +++ b/include/zephyr/mgmt/mcumgr/grp/img_mgmt/img_mgmt.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2018-2021 mcumgr authors - * Copyright (c) 2022-2023 Nordic Semiconductor ASA + * Copyright (c) 2022-2024 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ @@ -56,6 +56,7 @@ extern "C" { #define IMG_MGMT_ID_CORELIST 3 #define IMG_MGMT_ID_CORELOAD 4 #define IMG_MGMT_ID_ERASE 5 +#define IMG_MGMT_ID_SLOT_INFO 6 /** * Command result codes for image management group. diff --git a/include/zephyr/mgmt/mcumgr/grp/img_mgmt/img_mgmt_callbacks.h b/include/zephyr/mgmt/mcumgr/grp/img_mgmt/img_mgmt_callbacks.h index ac6f01cc244a..5c479fd99182 100644 --- a/include/zephyr/mgmt/mcumgr/grp/img_mgmt/img_mgmt_callbacks.h +++ b/include/zephyr/mgmt/mcumgr/grp/img_mgmt/img_mgmt_callbacks.h @@ -55,6 +55,44 @@ struct img_mgmt_state_slot_encode { const int flags; }; +/** + * Structure provided in the #MGMT_EVT_OP_IMG_MGMT_SLOT_INFO_IMAGE notification callback: This + * callback function is called once per image when the slot info command is used, it can be used + * to return additional information/fields in the response. + */ +struct img_mgmt_slot_info_image { + /** The image that is currently being enumerated. */ + const uint8_t image; + + /** + * The zcbor encoder which is currently being used to output information, additional fields + * can be added using this. + */ + zcbor_state_t *zse; +}; + +/** + * Structure provided in the #MGMT_EVT_OP_IMG_MGMT_SLOT_INFO_SLOT notification callback: This + * callback function is called once per slot per image when the slot info command is used, it can + * be used to return additional information/fields in the response. + */ +struct img_mgmt_slot_info_slot { + /** The image that is currently being enumerated. */ + const uint8_t image; + + /** The slot that is currently being enumerated. */ + const uint8_t slot; + + /** Flash area of the slot that is current being enumerated. */ + const struct flash_area *fa; + + /** + * The zcbor encoder which is currently being used to output information, additional fields + * can be added using this. + */ + zcbor_state_t *zse; +}; + /** * @} */ diff --git a/include/zephyr/mgmt/mcumgr/mgmt/callbacks.h b/include/zephyr/mgmt/mcumgr/mgmt/callbacks.h index c32dc6a544de..e3cd408ea731 100644 --- a/include/zephyr/mgmt/mcumgr/mgmt/callbacks.h +++ b/include/zephyr/mgmt/mcumgr/mgmt/callbacks.h @@ -188,6 +188,12 @@ enum img_mgmt_group_events { /** Callback when an image slot's state is encoded for a response. */ MGMT_EVT_OP_IMG_MGMT_IMAGE_SLOT_STATE = MGMT_DEF_EVT_OP_ID(MGMT_EVT_GRP_IMG, 6), + /** Callback when an slot list command outputs fields for an image. */ + MGMT_EVT_OP_IMG_MGMT_SLOT_INFO_IMAGE = MGMT_DEF_EVT_OP_ID(MGMT_EVT_GRP_IMG, 7), + + /** Callback when an slot list command outputs fields for a slot of an image. */ + MGMT_EVT_OP_IMG_MGMT_SLOT_INFO_SLOT = MGMT_DEF_EVT_OP_ID(MGMT_EVT_GRP_IMG, 8), + /** Used to enable all img_mgmt_group events. */ MGMT_EVT_OP_IMG_MGMT_ALL = MGMT_DEF_EVT_OP_ALL(MGMT_EVT_GRP_IMG), }; diff --git a/subsys/mgmt/mcumgr/grp/img_mgmt/Kconfig b/subsys/mgmt/mcumgr/grp/img_mgmt/Kconfig index 56030a1cd5c4..d0d80531c117 100644 --- a/subsys/mgmt/mcumgr/grp/img_mgmt/Kconfig +++ b/subsys/mgmt/mcumgr/grp/img_mgmt/Kconfig @@ -201,6 +201,20 @@ config MCUMGR_GRP_IMG_IMAGE_SLOT_STATE_STATES The value does not affect memory allocation, it is used by zcbor to figure out how to encode map depending on its predicted size. +config MCUMGR_GRP_IMG_SLOT_INFO + bool "Slot info" + default y if MCUMGR_GRP_IMG_UPDATABLE_IMAGE_NUMBER > 1 + help + This will enable the slot information function which will return information about all + images and slots that the application can see. + +config MCUMGR_GRP_IMG_SLOT_INFO_HOOKS + bool "Slot info hooks" + depends on MCUMGR_GRP_IMG_SLOT_INFO && MCUMGR_MGMT_NOTIFICATION_HOOKS + help + This will enable the slot info function hooks which can be used to add additional + information to responses. + module = MCUMGR_GRP_IMG module-str = mcumgr_grp_img source "subsys/logging/Kconfig.template.log_config" diff --git a/subsys/mgmt/mcumgr/grp/img_mgmt/src/img_mgmt.c b/subsys/mgmt/mcumgr/grp/img_mgmt/src/img_mgmt.c index 467eb03bcedb..913aec15c888 100644 --- a/subsys/mgmt/mcumgr/grp/img_mgmt/src/img_mgmt.c +++ b/subsys/mgmt/mcumgr/grp/img_mgmt/src/img_mgmt.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2018-2021 mcumgr authors - * Copyright (c) 2022-2023 Nordic Semiconductor ASA + * Copyright (c) 2022-2024 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -31,6 +32,7 @@ #ifdef CONFIG_MCUMGR_MGMT_NOTIFICATION_HOOKS #include +#include #endif #ifndef CONFIG_FLASH_LOAD_OFFSET @@ -73,6 +75,8 @@ BUILD_ASSERT(sizeof(struct image_header) == IMAGE_HEADER_SIZE, #define ACTIVE_IMAGE_IS 0 #endif +#define SLOTS_PER_IMAGE 2 + LOG_MODULE_REGISTER(mcumgr_img_grp, CONFIG_MCUMGR_GRP_IMG_LOG_LEVEL); struct img_mgmt_state g_img_mgmt_state; @@ -109,6 +113,62 @@ void img_mgmt_release_lock(void) #endif } +#if defined(CONFIG_MCUMGR_GRP_IMG_SLOT_INFO_HOOKS) +static bool img_mgmt_reset_zse(struct smp_streamer *ctxt) +{ + zcbor_state_t *zse = ctxt->writer->zs; + + /* Because there is already data in the buffer, it must be cleared first */ + net_buf_reset(ctxt->writer->nb); + ctxt->writer->nb->len = sizeof(struct smp_hdr); + zcbor_new_encode_state(zse, ARRAY_SIZE(ctxt->writer->zs), + ctxt->writer->nb->data + sizeof(struct smp_hdr), + net_buf_tailroom(ctxt->writer->nb), 0); + + return zcbor_map_start_encode(zse, CONFIG_MCUMGR_SMP_CBOR_MAX_MAIN_MAP_ENTRIES); +} + +#if defined(CONFIG_MCUMGR_GRP_IMG_TOO_LARGE_SYSBUILD) +static bool img_mgmt_slot_max_size(size_t *area_sizes, zcbor_state_t *zse) +{ + bool ok = true; + + if (area_sizes[0] > 0 && area_sizes[1] > 0) { + /* Calculate maximum image size */ + size_t area_size_difference = (size_t)abs((ssize_t)area_sizes[1] - + (ssize_t)area_sizes[0]); + + if (CONFIG_MCUBOOT_UPDATE_FOOTER_SIZE >= area_size_difference) { + ok = zcbor_tstr_put_lit(zse, "max_image_size") && + zcbor_uint32_put(zse, (uint32_t)(area_sizes[0] - + CONFIG_MCUBOOT_UPDATE_FOOTER_SIZE)); } + } + + return ok; +} +#elif defined(CONFIG_MCUMGR_GRP_IMG_TOO_LARGE_BOOTLOADER_INFO) +static bool img_mgmt_slot_max_size(size_t *area_sizes, zcbor_state_t *zse) +{ + bool ok = true; + int rc; + int max_app_size; + + ARG_UNUSED(area_sizes); + + rc = blinfo_lookup(BLINFO_MAX_APPLICATION_SIZE, &max_app_size, sizeof(max_app_size)) + + if (rc < 0) { + LOG_ERR("Failed to lookup max application size: %d", rc); + } else if (rc > 0) { + ok = zcbor_tstr_put_lit(zse, "max_image_size") && + zcbor_uint32_put(zse, (uint32_t)max_app_size); + } + + return ok; +} +#endif +#endif + /** * Finds the TLVs in the specified image slot, if any. */ @@ -388,6 +448,194 @@ img_mgmt_erase(struct smp_streamer *ctxt) return MGMT_ERR_EOK; } +#if defined(CONFIG_MCUMGR_GRP_IMG_SLOT_INFO) +/** + * Command handler: image slot info + */ +static int img_mgmt_slot_info(struct smp_streamer *ctxt) +{ + int rc; + zcbor_state_t *zse = ctxt->writer->zs; + bool ok; + uint8_t i = 0; + size_t area_sizes[SLOTS_PER_IMAGE]; + +#if defined(CONFIG_MCUMGR_GRP_IMG_SLOT_INFO_HOOKS) + int32_t err_rc; + uint16_t err_group; + enum mgmt_cb_return status; +#endif + + img_mgmt_take_lock(); + + ok = zcbor_tstr_put_lit(zse, "images") && + zcbor_list_start_encode(zse, 10); + + while (i < CONFIG_MCUMGR_GRP_IMG_UPDATABLE_IMAGE_NUMBER * SLOTS_PER_IMAGE) { + const struct flash_area *fa; + int area_id = img_mgmt_flash_area_id(i); + + if ((i % SLOTS_PER_IMAGE) == 0) { + memset(area_sizes, 0, sizeof(area_sizes)); + + ok = zcbor_map_start_encode(zse, 4) && + zcbor_tstr_put_lit(zse, "image") && + zcbor_uint32_put(zse, (uint32_t)(i / SLOTS_PER_IMAGE)) && + zcbor_tstr_put_lit(zse, "slots") && + zcbor_list_start_encode(zse, 4); + + if (!ok) { + goto finish; + } + } + + ok = zcbor_map_start_encode(zse, 4) && + zcbor_tstr_put_lit(zse, "slot") && + zcbor_uint32_put(zse, (uint32_t)(i % SLOTS_PER_IMAGE)); + + if (!ok) { + goto finish; + } + + rc = flash_area_open(area_id, &fa); + + if (rc) { + /* Failed opening slot, mark as error */ + ok = zcbor_tstr_put_lit(zse, "rc") && + zcbor_int32_put(zse, rc); + + LOG_ERR("Failed to open slot %d for information fetching: %d", area_id, rc); + } else { +#if defined(CONFIG_MCUMGR_GRP_IMG_SLOT_INFO_HOOKS) + struct img_mgmt_slot_info_slot slot_info_data = { + .image = (i / SLOTS_PER_IMAGE), + .slot = (i % SLOTS_PER_IMAGE), + .fa = fa, + .zse = zse, + }; +#endif + + if (sizeof(fa->fa_size) == sizeof(uint64_t)) { + ok = zcbor_tstr_put_lit(zse, "size") && + zcbor_uint64_put(zse, fa->fa_size); + } else { + ok = zcbor_tstr_put_lit(zse, "size") && + zcbor_uint32_put(zse, fa->fa_size); + } + + area_sizes[(i % SLOTS_PER_IMAGE)] = fa->fa_size; + + if (!ok) { + goto finish; + } + + /* + * Check if we support uploading to this slot and if so, return the + * image ID + */ +#if defined(CONFIG_MCUMGR_GRP_IMG_DIRECT_UPLOAD) + ok = zcbor_tstr_put_lit(zse, "upload_image_id") && + zcbor_uint32_put(zse, (i + 1)); +#else + if (img_mgmt_active_slot((i / SLOTS_PER_IMAGE)) != i) { + ok = zcbor_tstr_put_lit(zse, "upload_image_id") && + zcbor_uint32_put(zse, (i / SLOTS_PER_IMAGE)); + } +#endif + + if (!ok) { + goto finish; + } + +#if defined(CONFIG_MCUMGR_GRP_IMG_SLOT_INFO_HOOKS) + status = mgmt_callback_notify(MGMT_EVT_OP_IMG_MGMT_SLOT_INFO_SLOT, + &slot_info_data, sizeof(slot_info_data), + &err_rc, &err_group); +#endif + + flash_area_close(fa); + +#if defined(CONFIG_MCUMGR_GRP_IMG_SLOT_INFO_HOOKS) + if (status != MGMT_CB_OK) { + if (status == MGMT_CB_ERROR_RC) { + img_mgmt_release_lock(); + return err_rc; + } + + ok = img_mgmt_reset_zse(ctxt) && + smp_add_cmd_err(zse, err_group, (uint16_t)err_rc); + + goto finish; + } +#endif + } + + ok &= zcbor_map_end_encode(zse, 4); + + if (!ok) { + goto finish; + } + + if ((i % SLOTS_PER_IMAGE) == (SLOTS_PER_IMAGE - 1)) { +#if defined(CONFIG_MCUMGR_GRP_IMG_SLOT_INFO_HOOKS) + struct img_mgmt_slot_info_image image_info_data = { + .image = (i / SLOTS_PER_IMAGE), + .zse = zse, + }; +#endif + + ok = zcbor_list_end_encode(zse, 4); + + if (!ok) { + goto finish; + } + +#if defined(CONFIG_MCUMGR_GRP_IMG_TOO_LARGE_SYSBUILD) || \ + defined(MCUMGR_GRP_IMG_TOO_LARGE_BOOTLOADER_INFO) + ok = img_mgmt_slot_max_size(area_sizes, zse); + + if (!ok) { + goto finish; + } +#endif + +#if defined(CONFIG_MCUMGR_GRP_IMG_SLOT_INFO_HOOKS) + status = mgmt_callback_notify(MGMT_EVT_OP_IMG_MGMT_SLOT_INFO_IMAGE, + &image_info_data, sizeof(image_info_data), + &err_rc, &err_group); + + if (status != MGMT_CB_OK) { + if (status == MGMT_CB_ERROR_RC) { + img_mgmt_release_lock(); + return err_rc; + } + + ok = img_mgmt_reset_zse(ctxt) && + smp_add_cmd_err(zse, err_group, (uint16_t)err_rc); + + goto finish; + } +#endif + + ok = zcbor_map_end_encode(zse, 4); + + if (!ok) { + goto finish; + } + } + + ++i; + } + + ok = zcbor_list_end_encode(zse, 10); + +finish: + img_mgmt_release_lock(); + + return ok ? MGMT_ERR_EOK : MGMT_ERR_EMSGSIZE; +} +#endif + static int img_mgmt_upload_good_rsp(struct smp_streamer *ctxt) { @@ -822,6 +1070,12 @@ static const struct mgmt_handler img_mgmt_handlers[] = { .mh_read = NULL, .mh_write = img_mgmt_erase }, +#if defined(CONFIG_MCUMGR_GRP_IMG_SLOT_INFO) + [IMG_MGMT_ID_SLOT_INFO] = { + .mh_read = img_mgmt_slot_info, + .mh_write = NULL + }, +#endif }; static const struct mgmt_handler img_mgmt_handlers[]; From 32a6cc18438b6236ce6857295a4a7f084bf6ec25 Mon Sep 17 00:00:00 2001 From: Jamie McCrae Date: Fri, 26 Jul 2024 08:19:32 +0100 Subject: [PATCH 2/4] doc: services: device_mgmt: smp: Add img mgmt slot info docs Adds documentation describing the new slot info command Signed-off-by: Jamie McCrae --- .../device_mgmt/smp_groups/smp_group_1.rst | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/doc/services/device_mgmt/smp_groups/smp_group_1.rst b/doc/services/device_mgmt/smp_groups/smp_group_1.rst index 744835fad25b..5651e126c352 100644 --- a/doc/services/device_mgmt/smp_groups/smp_group_1.rst +++ b/doc/services/device_mgmt/smp_groups/smp_group_1.rst @@ -26,6 +26,8 @@ Application/software image management group defines following commands: +-------------------+-----------------------------------------------+ | ``5`` | Image erase | +-------------------+-----------------------------------------------+ + | ``6`` | Slot info | + +-------------------+-----------------------------------------------+ Notion of "slots" and "images" in Zephyr **************************************** @@ -516,3 +518,115 @@ where: Response from Zephyr running device may have "rc" value of :c:enumerator:`MGMT_ERR_EBADSTATE`, which means that the secondary image has been marked for next boot already and may not be erased. + +Slot info +********* + +The command is used for fetching information on slots that are available. This command can be +enabled with :kconfig:option:`CONFIG_MCUMGR_GRP_IMG_SLOT_INFO`. + +Slot info request +================= + +Slot info request header fields: + +.. table:: + :align: center + + +--------+--------------+----------------+ + | ``OP`` | ``Group ID`` | ``Command ID`` | + +========+==============+================+ + | ``0`` | ``1`` | ``6`` | + +--------+--------------+----------------+ + +The command sends an empty CBOR map as data. + +Slot info response +================== + +Slot info response header fields: + +.. table:: + :align: center + + +--------+--------------+----------------+ + | ``OP`` | ``Group ID`` | ``Command ID`` | + +========+==============+================+ + | ``1`` | ``1`` | ``6`` | + +--------+--------------+----------------+ + +CBOR data of successful response: + +.. code-block:: none + + { + (str)"images" : [ + { + (str)"image" : (uint) + (str)"slots" : [ + { + (str)"slot" : (uint) + (str)"size" : (uint) + (str,opt)"upload_image_id" : (uint) + } + ... + ] + (str,opt)"max_image_size" : (uint) + } + ... + ] + } + +In case of error the CBOR data takes the form: + +.. tabs:: + + .. group-tab:: SMP version 2 + + .. code-block:: none + + { + (str)"err" : { + (str)"group" : (uint) + (str)"rc" : (uint) + } + } + + .. group-tab:: SMP version 1 (and non-group SMP version 2) + + .. code-block:: none + + { + (str)"rc" : (int) + } + +where: + +.. table:: + :align: center + + +-------------------+--------------------------------------------------------------------------------------------+ + | "image" | the image number being enumerated. | + +-------------------+--------------------------------------------------------------------------------------------+ + | "slot" | the slot inside the image being enumerated (note: this will be 0 or 1, it is the slot of | + | | the image not the physical slot number). | + +-------------------+--------------------------------------------------------------------------------------------+ + | "size" | the size of the slot. | + +-------------------+--------------------------------------------------------------------------------------------+ + | "upload_image_id" | optional field (only present if :kconfig:option:`CONFIG_MCUMGR_GRP_IMG_DIRECT_UPLOAD` is | + | | enabled to allow direct image uploads) which specifies the image ID that can be used by | + | | external tools to upload an image to that slot. | + +-------------------+--------------------------------------------------------------------------------------------+ + | "max_image_size" | optional field (only present if :kconfig:option:`CONFIG_MCUMGR_GRP_IMG_TOO_LARGE_SYSBUILD` | + | | or :kconfig:option:`CONFIG_MCUMGR_GRP_IMG_TOO_LARGE_BOOTLOADER_INFO` are enabled) which | + | | specifies the maximum size of an application that can be uploaded to that image number. | + +-------------------+--------------------------------------------------------------------------------------------+ + | "err" -> "group" | :c:enum:`mcumgr_group_t` group of the group-based error code. Only appears if an error is | + | | returned when using SMP version 2. | + +-------------------+--------------------------------------------------------------------------------------------+ + | "err" -> "rc" | contains the index of the group-based error code. Only appears ifnon-zero (error | + | | condition) when using SMP version 2. | + +-------------------+--------------------------------------------------------------------------------------------+ + | "rc" | :c:enum:`mcumgr_err_t` only appears if non-zero (error condition) when using SMP version 1 | + | | or for SMP errors when using SMP version 2. | + +-------------------+--------------------------------------------------------------------------------------------+ From 48c1e63f5773478be45c022926b33221aae4a722 Mon Sep 17 00:00:00 2001 From: Jamie McCrae Date: Fri, 6 Sep 2024 13:54:13 +0100 Subject: [PATCH 3/4] tests: mgmt: mcumgr: Add img_mgmt_slot_info test Adds a test to exercise this new feature Signed-off-by: Jamie McCrae --- .../mcumgr/img_mgmt_slot_info/CMakeLists.txt | 17 + .../nrf52840dk_nrf52840_dual_slot.overlay | 38 ++ ...nrf5340dk_nrf5340_cpuapp_dual_slot.overlay | 40 ++ .../mgmt/mcumgr/img_mgmt_slot_info/prj.conf | 22 + .../img_mgmt_slot_info/prj_dual_slot.conf | 23 + .../img_mgmt_slot_info/prj_too_large.conf | 23 + .../mgmt/mcumgr/img_mgmt_slot_info/src/main.c | 603 ++++++++++++++++++ .../img_mgmt_slot_info/src/smp_test_util.c | 45 ++ .../img_mgmt_slot_info/src/smp_test_util.h | 19 + .../sysbuild_too_large.conf | 1 + .../mcumgr/img_mgmt_slot_info/testcase.yaml | 22 + 11 files changed, 853 insertions(+) create mode 100644 tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/CMakeLists.txt create mode 100644 tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/boards/nrf52840dk_nrf52840_dual_slot.overlay create mode 100644 tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/boards/nrf5340dk_nrf5340_cpuapp_dual_slot.overlay create mode 100644 tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/prj.conf create mode 100644 tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/prj_dual_slot.conf create mode 100644 tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/prj_too_large.conf create mode 100644 tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/src/main.c create mode 100644 tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/src/smp_test_util.c create mode 100644 tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/src/smp_test_util.h create mode 100644 tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/sysbuild_too_large.conf create mode 100644 tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/testcase.yaml diff --git a/tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/CMakeLists.txt b/tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/CMakeLists.txt new file mode 100644 index 000000000000..d41feff96df2 --- /dev/null +++ b/tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/CMakeLists.txt @@ -0,0 +1,17 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 +# + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(img_mgmt_slot_info) + +FILE(GLOB app_sources + src/*.c +) + +target_sources(app PRIVATE ${app_sources}) +target_include_directories(app PRIVATE ${ZEPHYR_BASE}/subsys/mgmt/mcumgr/transport/include/mgmt/mcumgr/transport/) +zephyr_link_libraries(MCUBOOT_BOOTUTIL) diff --git a/tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/boards/nrf52840dk_nrf52840_dual_slot.overlay b/tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/boards/nrf52840dk_nrf52840_dual_slot.overlay new file mode 100644 index 000000000000..fb68e4df471c --- /dev/null +++ b/tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/boards/nrf52840dk_nrf52840_dual_slot.overlay @@ -0,0 +1,38 @@ +/* + * Copyright 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/delete-node/ &boot_partition; +/delete-node/ &slot0_partition; +/delete-node/ &slot1_partition; + +&flash0 { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + boot_partition: partition@0 { + label = "mcuboot"; + reg = <0x00000000 0x10000>; + }; + slot0_partition: partition@10000 { + label = "image-0"; + reg = <0x00010000 0x40000>; + }; + slot1_partition: partition@50000 { + label = "image-1"; + reg = <0x00050000 0x40000>; + }; + slot2_partition: partition@90000 { + label = "image-2"; + reg = <0x00090000 0x30000>; + }; + slot3_partition: partition@c0000 { + label = "image-3"; + reg = <0x000c0000 0x30000>; + }; + }; +}; diff --git a/tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/boards/nrf5340dk_nrf5340_cpuapp_dual_slot.overlay b/tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/boards/nrf5340dk_nrf5340_cpuapp_dual_slot.overlay new file mode 100644 index 000000000000..7f612e21444b --- /dev/null +++ b/tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/boards/nrf5340dk_nrf5340_cpuapp_dual_slot.overlay @@ -0,0 +1,40 @@ +/* + * Copyright 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/delete-node/ &boot_partition; +/delete-node/ &slot0_partition; +/delete-node/ &slot1_partition; +/delete-node/ &slot0_ns_partition; +/delete-node/ &slot1_ns_partition; + +&flash0 { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + boot_partition: partition@0 { + label = "mcuboot"; + reg = <0x00000000 0x10000>; + }; + slot0_partition: partition@10000 { + label = "image-0"; + reg = <0x00010000 0x40000>; + }; + slot1_partition: partition@50000 { + label = "image-1"; + reg = <0x00050000 0x40000>; + }; + slot2_partition: partition@90000 { + label = "image-2"; + reg = <0x00090000 0x30000>; + }; + slot3_partition: partition@c0000 { + label = "image-3"; + reg = <0x000c0000 0x30000>; + }; + }; +}; diff --git a/tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/prj.conf b/tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/prj.conf new file mode 100644 index 000000000000..6bf3bc7061b6 --- /dev/null +++ b/tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/prj.conf @@ -0,0 +1,22 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 +# +CONFIG_ZTEST=y +CONFIG_NET_BUF=y +CONFIG_BASE64=y +CONFIG_ZCBOR=y +CONFIG_CRC=y +CONFIG_FLASH=y +CONFIG_FLASH_MAP=y +CONFIG_STREAM_FLASH=y +CONFIG_IMG_MANAGER=y +CONFIG_MCUMGR=y +CONFIG_MCUMGR_TRANSPORT_DUMMY=y +CONFIG_MCUMGR_TRANSPORT_DUMMY_RX_BUF_SIZE=1024 +CONFIG_MCUMGR_GRP_IMG=y +CONFIG_MCUMGR_GRP_IMG_SLOT_INFO=y +CONFIG_MCUMGR_GRP_IMG_SLOT_INFO_HOOKS=y +CONFIG_MCUMGR_MGMT_NOTIFICATION_HOOKS=y +CONFIG_ZTEST_STACK_SIZE=3096 diff --git a/tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/prj_dual_slot.conf b/tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/prj_dual_slot.conf new file mode 100644 index 000000000000..819cee3e535f --- /dev/null +++ b/tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/prj_dual_slot.conf @@ -0,0 +1,23 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 +# +CONFIG_ZTEST=y +CONFIG_NET_BUF=y +CONFIG_BASE64=y +CONFIG_ZCBOR=y +CONFIG_CRC=y +CONFIG_FLASH=y +CONFIG_FLASH_MAP=y +CONFIG_STREAM_FLASH=y +CONFIG_IMG_MANAGER=y +CONFIG_MCUMGR=y +CONFIG_MCUMGR_TRANSPORT_DUMMY=y +CONFIG_MCUMGR_TRANSPORT_DUMMY_RX_BUF_SIZE=1024 +CONFIG_MCUMGR_GRP_IMG=y +CONFIG_MCUMGR_GRP_IMG_SLOT_INFO=y +CONFIG_MCUMGR_GRP_IMG_SLOT_INFO_HOOKS=y +CONFIG_MCUMGR_MGMT_NOTIFICATION_HOOKS=y +CONFIG_ZTEST_STACK_SIZE=3096 +CONFIG_UPDATEABLE_IMAGE_NUMBER=2 diff --git a/tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/prj_too_large.conf b/tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/prj_too_large.conf new file mode 100644 index 000000000000..b21ae488cddd --- /dev/null +++ b/tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/prj_too_large.conf @@ -0,0 +1,23 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 +# +CONFIG_ZTEST=y +CONFIG_NET_BUF=y +CONFIG_BASE64=y +CONFIG_ZCBOR=y +CONFIG_CRC=y +CONFIG_FLASH=y +CONFIG_FLASH_MAP=y +CONFIG_STREAM_FLASH=y +CONFIG_IMG_MANAGER=y +CONFIG_MCUMGR=y +CONFIG_MCUMGR_TRANSPORT_DUMMY=y +CONFIG_MCUMGR_TRANSPORT_DUMMY_RX_BUF_SIZE=1024 +CONFIG_MCUMGR_GRP_IMG=y +CONFIG_MCUMGR_GRP_IMG_SLOT_INFO=y +CONFIG_MCUMGR_GRP_IMG_SLOT_INFO_HOOKS=y +CONFIG_MCUMGR_MGMT_NOTIFICATION_HOOKS=y +CONFIG_MCUMGR_GRP_IMG_TOO_LARGE_SYSBUILD=y +CONFIG_ZTEST_STACK_SIZE=3096 diff --git a/tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/src/main.c b/tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/src/main.c new file mode 100644 index 000000000000..c3cdc4d11343 --- /dev/null +++ b/tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/src/main.c @@ -0,0 +1,603 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "smp_test_util.h" + +#define SMP_RESPONSE_WAIT_TIME 3 +#define ZCBOR_BUFFER_SIZE 128 +#define OUTPUT_BUFFER_SIZE 512 +#define ZCBOR_HISTORY_ARRAY_SIZE 10 + +static struct net_buf *nb; +static bool slot_info_image_callback_got; +static bool slot_info_slot_callback_got; +static bool other_callback_got; +static bool block_access; +static bool add_field; + +struct partition_entries_t { + uint8_t partition_id; + uint32_t size; +}; + +struct partition_entries_t partition_entries[] = { + { FIXED_PARTITION_ID(slot0_partition), 0 }, + { FIXED_PARTITION_ID(slot1_partition), 0 }, +#if FIXED_PARTITION_EXISTS(slot2_partition) + { FIXED_PARTITION_ID(slot2_partition), 0 }, +#endif +#if FIXED_PARTITION_EXISTS(slot3_partition) + { FIXED_PARTITION_ID(slot3_partition), 0 }, +#endif +}; + +static void cleanup_test(void *p) +{ + if (nb != NULL) { + net_buf_unref(nb); + nb = NULL; + } + + slot_info_image_callback_got = false; + slot_info_slot_callback_got = false; + other_callback_got = false; + block_access = false; + add_field = false; +} + +struct slot_info_t { + uint32_t slot; + bool slot_received; + uint32_t size; + bool size_received; + uint32_t upload_image_id; + bool upload_image_id_received; + uint32_t test2; + bool test2_received; +}; + +struct image_info_t { + uint32_t image; + bool image_received; + struct slot_info_t slots[2]; + uint32_t max_image_size; + bool max_image_size_received; + uint32_t test1; + bool test1_received; + + uint8_t current_slot; +}; + +struct receive_info_t { + struct image_info_t images[CONFIG_UPDATEABLE_IMAGE_NUMBER]; + + uint8_t current_image; +}; + +static bool parse_slot_entries(zcbor_state_t *state, void *user_data) +{ + size_t decoded = 0; + struct image_info_t *image_data = (struct image_info_t *)user_data; + bool ok; + + if (!zcbor_list_start_decode(state)) { + return false; + } + + while (!zcbor_array_at_end(state)) { + struct slot_info_t *slot_data = &image_data->slots[image_data->current_slot]; + struct zcbor_map_decode_key_val output_decode[] = { + ZCBOR_MAP_DECODE_KEY_DECODER("slot", zcbor_uint32_decode, + &slot_data->slot), + ZCBOR_MAP_DECODE_KEY_DECODER("size", zcbor_uint32_decode, + &slot_data->size), + ZCBOR_MAP_DECODE_KEY_DECODER("upload_image_id", zcbor_uint32_decode, + &slot_data->upload_image_id), + ZCBOR_MAP_DECODE_KEY_DECODER("test2", zcbor_uint32_decode, + &slot_data->test2), + }; + + + ok = zcbor_map_decode_bulk(state, output_decode, ARRAY_SIZE(output_decode), + &decoded) == 0; + zassert_true(ok, "Expected decode to be successful"); + zassert_true((decoded == 2 || decoded == 3 || decoded == 4), + "Expected to receive 2-4 decoded zcbor elements"); + + slot_data->slot_received = zcbor_map_decode_bulk_key_found(output_decode, + ARRAY_SIZE(output_decode), + "slot"); + slot_data->size_received = zcbor_map_decode_bulk_key_found(output_decode, + ARRAY_SIZE(output_decode), + "slot"); + slot_data->upload_image_id_received = zcbor_map_decode_bulk_key_found( + output_decode, + ARRAY_SIZE(output_decode), + "upload_image_id"); + slot_data->test2_received = zcbor_map_decode_bulk_key_found(output_decode, + ARRAY_SIZE(output_decode), + "test2"); + + ++image_data->current_slot; + } + + (void)zcbor_list_end_decode(state); + + return true; +} + +static bool parse_images_entries(zcbor_state_t *state, void *user_data) +{ + struct receive_info_t *receive_data = (struct receive_info_t *)user_data; + bool ok; + + if (!zcbor_list_start_decode(state)) { + return false; + } + + while (!zcbor_array_at_end(state)) { + size_t decoded = 0; + struct image_info_t *image_data = + &receive_data->images[receive_data->current_image]; + + struct zcbor_map_decode_key_val output_decode[] = { + ZCBOR_MAP_DECODE_KEY_DECODER("image", zcbor_uint32_decode, + &image_data->image), + ZCBOR_MAP_DECODE_KEY_DECODER("slots", parse_slot_entries, image_data), + ZCBOR_MAP_DECODE_KEY_DECODER("max_image_size", zcbor_uint32_decode, + &image_data->max_image_size), + ZCBOR_MAP_DECODE_KEY_DECODER("test1", zcbor_uint32_decode, + &image_data->test1), + }; + + ok = zcbor_map_decode_bulk(state, output_decode, ARRAY_SIZE(output_decode), + &decoded) == 0; + zassert_true(ok, "Expected decode to be successful"); + zassert_true((decoded == 2 || decoded == 3 || decoded == 4), + "Expected to receive 2-4 decoded zcbor elements"); + + image_data->image_received = zcbor_map_decode_bulk_key_found(output_decode, + ARRAY_SIZE(output_decode), + "image"); + image_data->max_image_size_received = zcbor_map_decode_bulk_key_found( + output_decode, + ARRAY_SIZE(output_decode), + "max_image_size"); + image_data->test1_received = zcbor_map_decode_bulk_key_found(output_decode, + ARRAY_SIZE(output_decode), + "test1"); + + ++receive_data->current_image; + } + + (void)zcbor_list_end_decode(state); + + return true; +} + +ZTEST(img_mgmt, test_list) +{ + uint8_t buffer[ZCBOR_BUFFER_SIZE]; + uint8_t buffer_out[OUTPUT_BUFFER_SIZE]; + bool ok; + uint16_t buffer_size; + zcbor_state_t zse[ZCBOR_HISTORY_ARRAY_SIZE] = { 0 }; + zcbor_state_t zsd[ZCBOR_HISTORY_ARRAY_SIZE] = { 0 }; + bool received; + struct smp_hdr *header; + size_t decoded = 0; + struct receive_info_t receive_response = { 0 }; + uint8_t i = 0; + + struct zcbor_map_decode_key_val output_decode[] = { + ZCBOR_MAP_DECODE_KEY_DECODER("images", parse_images_entries, &receive_response), + }; + + memset(buffer, 0, sizeof(buffer)); + memset(buffer_out, 0, sizeof(buffer_out)); + buffer_size = 0; + memset(zse, 0, sizeof(zse)); + memset(zsd, 0, sizeof(zsd)); + + zcbor_new_encode_state(zse, 2, buffer, ARRAY_SIZE(buffer), 0); + + ok = create_img_mgmt_slot_info_packet(zse, buffer, buffer_out, &buffer_size); + zassert_true(ok, "Expected packet creation to be successful"); + + /* Enable dummy SMP backend and ready for usage */ + smp_dummy_enable(); + smp_dummy_clear_state(); + + /* Send query command to dummy SMP backend */ + (void)smp_dummy_tx_pkt(buffer_out, buffer_size); + smp_dummy_add_data(); + + /* Wait for a short duration to see if response has been received */ + received = smp_dummy_wait_for_data(SMP_RESPONSE_WAIT_TIME); + zassert_true(received, "Expected to receive data but timed out"); + + /* Retrieve response buffer */ + nb = smp_dummy_get_outgoing(); + smp_dummy_disable(); + + /* Check response is as expected */ + header = net_buf_pull_mem(nb, sizeof(struct smp_hdr)); + + zassert_equal(header->nh_flags, 0, "SMP header flags mismatch"); + zassert_equal(header->nh_op, MGMT_OP_READ_RSP, "SMP header operation mismatch"); + zassert_equal(header->nh_group, sys_cpu_to_be16(MGMT_GROUP_ID_IMAGE), + "SMP header group mismatch"); + zassert_equal(header->nh_seq, 1, "SMP header sequence number mismatch"); + zassert_equal(header->nh_id, IMG_MGMT_ID_SLOT_INFO, "SMP header command ID mismatch"); + zassert_equal(header->nh_version, 1, "SMP header version mismatch"); + + /* Get the response value to compare */ + zcbor_new_decode_state(zsd, 8, nb->data, nb->len, 1, NULL, 0); + ok = zcbor_map_decode_bulk(zsd, output_decode, ARRAY_SIZE(output_decode), &decoded) == 0; + zassert_true(ok, "Expected decode to be successful"); + zassert_equal(decoded, 1, "Expected to receive 1 decoded zcbor element"); + + /* Ensure the right amount of data was read and that the values match */ + zassert_equal(receive_response.current_image, CONFIG_UPDATEABLE_IMAGE_NUMBER, + "Expected data mismatch"); + + while (i < receive_response.current_image) { + struct image_info_t *current_image = + &receive_response.images[i]; + uint8_t l = 0; +#ifdef CONFIG_MCUMGR_GRP_IMG_TOO_LARGE_SYSBUILD + uint32_t expected_max_size; +#endif + + zassert_equal(current_image->current_slot, 2, "Expected data mismatch"); + zassert_true(current_image->image_received, "Expected data mismatch"); + +#ifdef CONFIG_MCUMGR_GRP_IMG_TOO_LARGE_SYSBUILD + expected_max_size = MAX(partition_entries[(i * 2)].size, + partition_entries[((i * 2) + 1)].size); + expected_max_size -= CONFIG_MCUBOOT_UPDATE_FOOTER_SIZE; + + zassert_true(current_image->max_image_size_received, "Expected data mismatch"); + zassert_equal(current_image->max_image_size, expected_max_size, + "Expected data mismatch"); +#else + zassert_false(current_image->max_image_size_received, "Expected data mismatch"); +#endif + + zassert_false(current_image->test1_received, "Expected data mismatch"); + + while (l < current_image->current_slot) { + struct slot_info_t *current_slot = + ¤t_image->slots[l]; + + zassert_true(current_slot->slot_received, "Expected data mismatch"); + zassert_true(current_slot->size_received, "Expected data mismatch"); + zassert_false(current_slot->test2_received, "Expected data mismatch"); + + if (l == 1) { + zassert_true(current_slot->upload_image_id_received, + "Expected data mismatch"); + zassert_equal(current_slot->upload_image_id, i, + "Expected data mismatch"); + } else { + zassert_false(current_slot->upload_image_id_received, + "Expected data mismatch"); + } + + zassert_equal(current_slot->slot, l, "Expected data mismatch"); + zassert_equal(current_slot->size, partition_entries[((i * 2) + l)].size, + "Expected data mismatch"); + + ++l; + } + + ++i; + } + + zassert_false(slot_info_image_callback_got, "Did not expect to get image callback"); + zassert_false(slot_info_slot_callback_got, "Did not expect to get slot callback"); + zassert_false(other_callback_got, "Did not expect to get other callback"); + + /* Clean up test */ + cleanup_test(NULL); +} + +ZTEST(img_mgmt, test_blocked) +{ + uint8_t buffer[ZCBOR_BUFFER_SIZE]; + uint8_t buffer_out[OUTPUT_BUFFER_SIZE]; + bool ok; + uint16_t buffer_size; + zcbor_state_t zse[ZCBOR_HISTORY_ARRAY_SIZE] = { 0 }; + zcbor_state_t zsd[ZCBOR_HISTORY_ARRAY_SIZE] = { 0 }; + bool received; + struct smp_hdr *header; + size_t decoded = 0; + uint32_t rc = 0; + + struct zcbor_map_decode_key_val output_decode[] = { + ZCBOR_MAP_DECODE_KEY_DECODER("rc", zcbor_uint32_decode, &rc), + }; + + memset(buffer, 0, sizeof(buffer)); + memset(buffer_out, 0, sizeof(buffer_out)); + buffer_size = 0; + memset(zse, 0, sizeof(zse)); + memset(zsd, 0, sizeof(zsd)); + + zcbor_new_encode_state(zse, 2, buffer, ARRAY_SIZE(buffer), 0); + + ok = create_img_mgmt_slot_info_packet(zse, buffer, buffer_out, &buffer_size); + zassert_true(ok, "Expected packet creation to be successful"); + + block_access = true; + + /* Enable dummy SMP backend and ready for usage */ + smp_dummy_enable(); + smp_dummy_clear_state(); + + /* Send query command to dummy SMP backend */ + (void)smp_dummy_tx_pkt(buffer_out, buffer_size); + smp_dummy_add_data(); + + /* Wait for a short duration to see if response has been received */ + received = smp_dummy_wait_for_data(SMP_RESPONSE_WAIT_TIME); + zassert_true(received, "Expected to receive data but timed out"); + + /* Retrieve response buffer */ + nb = smp_dummy_get_outgoing(); + smp_dummy_disable(); + + /* Check response is as expected */ + header = net_buf_pull_mem(nb, sizeof(struct smp_hdr)); + + zassert_equal(header->nh_flags, 0, "SMP header flags mismatch"); + zassert_equal(header->nh_op, MGMT_OP_READ_RSP, "SMP header operation mismatch"); + zassert_equal(header->nh_group, sys_cpu_to_be16(MGMT_GROUP_ID_IMAGE), + "SMP header group mismatch"); + zassert_equal(header->nh_seq, 1, "SMP header sequence number mismatch"); + zassert_equal(header->nh_id, IMG_MGMT_ID_SLOT_INFO, "SMP header command ID mismatch"); + zassert_equal(header->nh_version, 1, "SMP header version mismatch"); + + /* Get the response value to compare */ + zcbor_new_decode_state(zsd, 8, nb->data, nb->len, 1, NULL, 0); + ok = zcbor_map_decode_bulk(zsd, output_decode, ARRAY_SIZE(output_decode), &decoded) == 0; + zassert_true(ok, "Expected decode to be successful"); + zassert_equal(decoded, 1, "Expected to receive 1 decoded zcbor element"); + + zassert_true(slot_info_slot_callback_got, "Expected callback to have ran"); + zassert_false(slot_info_image_callback_got, "Did not expect other callback to have ran"); + zassert_false(other_callback_got, "Did not expect invalid callback to have ran"); + zassert_equal(rc, MGMT_ERR_EPERUSER, "Expected error was not returned"); + + /* Clean up test */ + cleanup_test(NULL); +} + +ZTEST(img_mgmt, test_callback) +{ + uint8_t buffer[ZCBOR_BUFFER_SIZE]; + uint8_t buffer_out[OUTPUT_BUFFER_SIZE]; + bool ok; + uint16_t buffer_size; + zcbor_state_t zse[ZCBOR_HISTORY_ARRAY_SIZE] = { 0 }; + zcbor_state_t zsd[ZCBOR_HISTORY_ARRAY_SIZE] = { 0 }; + bool received; + struct smp_hdr *header; + size_t decoded = 0; + struct receive_info_t receive_response = { 0 }; + uint8_t i = 0; + + struct zcbor_map_decode_key_val output_decode[] = { + ZCBOR_MAP_DECODE_KEY_DECODER("images", parse_images_entries, &receive_response), + }; + + memset(buffer, 0, sizeof(buffer)); + memset(buffer_out, 0, sizeof(buffer_out)); + buffer_size = 0; + memset(zse, 0, sizeof(zse)); + memset(zsd, 0, sizeof(zsd)); + + zcbor_new_encode_state(zse, 2, buffer, ARRAY_SIZE(buffer), 0); + + ok = create_img_mgmt_slot_info_packet(zse, buffer, buffer_out, &buffer_size); + zassert_true(ok, "Expected packet creation to be successful"); + + add_field = true; + + /* Enable dummy SMP backend and ready for usage */ + smp_dummy_enable(); + smp_dummy_clear_state(); + + /* Send query command to dummy SMP backend */ + (void)smp_dummy_tx_pkt(buffer_out, buffer_size); + smp_dummy_add_data(); + + /* Wait for a short duration to see if response has been received */ + received = smp_dummy_wait_for_data(SMP_RESPONSE_WAIT_TIME); + zassert_true(received, "Expected to receive data but timed out"); + + /* Retrieve response buffer */ + nb = smp_dummy_get_outgoing(); + smp_dummy_disable(); + + /* Check response is as expected */ + header = net_buf_pull_mem(nb, sizeof(struct smp_hdr)); + + zassert_equal(header->nh_flags, 0, "SMP header flags mismatch"); + zassert_equal(header->nh_op, MGMT_OP_READ_RSP, "SMP header operation mismatch"); + zassert_equal(header->nh_group, sys_cpu_to_be16(MGMT_GROUP_ID_IMAGE), + "SMP header group mismatch"); + zassert_equal(header->nh_seq, 1, "SMP header sequence number mismatch"); + zassert_equal(header->nh_id, IMG_MGMT_ID_SLOT_INFO, "SMP header command ID mismatch"); + zassert_equal(header->nh_version, 1, "SMP header version mismatch"); + + /* Get the response value to compare */ + zcbor_new_decode_state(zsd, 8, nb->data, nb->len, 1, NULL, 0); + ok = zcbor_map_decode_bulk(zsd, output_decode, ARRAY_SIZE(output_decode), &decoded) == 0; + zassert_true(ok, "Expected decode to be successful"); + zassert_equal(decoded, 1, "Expected to receive 1 decoded zcbor element"); + + /* Ensure the right amount of data was read and that the values match */ + zassert_equal(receive_response.current_image, CONFIG_UPDATEABLE_IMAGE_NUMBER, + "Expected data mismatch"); + + while (i < receive_response.current_image) { + struct image_info_t *current_image = + &receive_response.images[i]; + uint8_t l = 0; +#ifdef CONFIG_MCUMGR_GRP_IMG_TOO_LARGE_SYSBUILD + uint32_t expected_max_size; +#endif + + zassert_equal(current_image->current_slot, 2, "Expected data mismatch"); + zassert_true(current_image->image_received, "Expected data mismatch"); + +#ifdef CONFIG_MCUMGR_GRP_IMG_TOO_LARGE_SYSBUILD + expected_max_size = MAX(partition_entries[(i * 2)].size, + partition_entries[((i * 2) + 1)].size); + expected_max_size -= CONFIG_MCUBOOT_UPDATE_FOOTER_SIZE; + + zassert_true(current_image->max_image_size_received, "Expected data mismatch"); + zassert_equal(current_image->max_image_size, expected_max_size, + "Expected data mismatch"); +#else + zassert_false(current_image->max_image_size_received, "Expected data mismatch"); +#endif + + zassert_true(current_image->test1_received, "Expected data mismatch"); + zassert_equal(current_image->test1, (i + 18), "Expected data mismatch"); + + while (l < current_image->current_slot) { + struct slot_info_t *current_slot = + ¤t_image->slots[l]; + + zassert_true(current_slot->test2_received, "Expected data mismatch"); + zassert_true(current_slot->slot_received, "Expected data mismatch"); + zassert_true(current_slot->size_received, "Expected data mismatch"); + + if (l == 1) { + zassert_true(current_slot->upload_image_id_received, + "Expected data mismatch"); + zassert_equal(current_slot->upload_image_id, i, + "Expected data mismatch"); + } else { + zassert_false(current_slot->upload_image_id_received, + "Expected data mismatch"); + } + + zassert_equal(current_slot->slot, l, "Expected data mismatch"); + zassert_equal(current_slot->size, partition_entries[((i * 2) + l)].size, + "Expected data mismatch"); + zassert_equal(current_slot->test2, + ((current_image->image * 2) + current_slot->slot + 3), + "Expected data mismatch"); + + ++l; + } + + ++i; + } + + zassert_true(slot_info_image_callback_got, "Expected to get image callback"); + zassert_true(slot_info_slot_callback_got, "Expected to get slot callback"); + zassert_false(other_callback_got, "Did not expect to get other callback"); + + /* Clean up test */ + cleanup_test(NULL); +} + +static enum mgmt_cb_return mgmt_event_cmd_callback(uint32_t event, enum mgmt_cb_return prev_status, + int32_t *rc, uint16_t *group, bool *abort_more, + void *data, size_t data_size) +{ + if (event == MGMT_EVT_OP_IMG_MGMT_SLOT_INFO_IMAGE) { + if (add_field) { + struct img_mgmt_slot_info_image *img_data = + (struct img_mgmt_slot_info_image *)data; + uint32_t temp = (img_data->image + 18); + bool ok; + + slot_info_image_callback_got = true; + + ok = zcbor_tstr_put_lit(img_data->zse, "test1") && + zcbor_uint32_encode(img_data->zse, &temp); + + if (!ok) { + *rc = MGMT_ERR_EUNKNOWN; + return MGMT_CB_ERROR_RC; + } + } + } else if (event == MGMT_EVT_OP_IMG_MGMT_SLOT_INFO_SLOT) { + if (block_access) { + slot_info_slot_callback_got = true; + *rc = MGMT_ERR_EPERUSER; + return MGMT_CB_ERROR_RC; + } else if (add_field) { + struct img_mgmt_slot_info_slot *slot_data = + (struct img_mgmt_slot_info_slot *)data; + uint32_t temp = ((slot_data->image * 2) + slot_data->slot + 3); + bool ok; + + slot_info_slot_callback_got = true; + + ok = zcbor_tstr_put_lit(slot_data->zse, "test2") && + zcbor_uint32_encode(slot_data->zse, &temp); + + if (!ok) { + *rc = MGMT_ERR_EUNKNOWN; + return MGMT_CB_ERROR_RC; + } + } + } else { + other_callback_got = true; + } + + return MGMT_CB_OK; +} + +static struct mgmt_callback mgmt_event_callback = { + .callback = mgmt_event_cmd_callback, + .event_id = (MGMT_EVT_OP_IMG_MGMT_SLOT_INFO_IMAGE | MGMT_EVT_OP_IMG_MGMT_SLOT_INFO_SLOT), +}; + +static void *setup_test(void) +{ + int rc; + uint8_t i = 0; + + while (i < ARRAY_SIZE(partition_entries)) { + const struct flash_area *fa; + + rc = flash_area_open(partition_entries[i].partition_id, &fa); + zassert_ok(rc, "Expected data mismatch"); + flash_area_close(fa); + + partition_entries[i].size = fa->fa_size; + + ++i; + } + + mgmt_callback_register(&mgmt_event_callback); + + return NULL; +} + +ZTEST_SUITE(img_mgmt, NULL, setup_test, NULL, cleanup_test, NULL); diff --git a/tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/src/smp_test_util.c b/tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/src/smp_test_util.c new file mode 100644 index 000000000000..e755b6571bc2 --- /dev/null +++ b/tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/src/smp_test_util.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2022-2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "smp_test_util.h" +#include +#include +#include +#include + +/* SMP header function for generating MCUmgr command header with sequence number set to 1 */ +static void smp_make_hdr(struct smp_hdr *rsp_hdr, size_t len, uint8_t type, bool write) +{ + *rsp_hdr = (struct smp_hdr) { + .nh_len = sys_cpu_to_be16(len), + .nh_flags = 0, + .nh_op = (write ? MGMT_OP_WRITE : MGMT_OP_READ), + .nh_group = sys_cpu_to_be16(MGMT_GROUP_ID_IMAGE), + .nh_seq = 1, + .nh_id = type, + .nh_version = 1, + }; +} + +bool create_img_mgmt_slot_info_packet(zcbor_state_t *zse, uint8_t *buffer, uint8_t *output_buffer, + uint16_t *buffer_size) +{ + bool ok; + + ok = zcbor_map_start_encode(zse, 2) && + zcbor_map_end_encode(zse, 2); + + if (!ok) { + return false; + } + + *buffer_size = (zse->payload_mut - buffer); + smp_make_hdr((struct smp_hdr *)output_buffer, *buffer_size, IMG_MGMT_ID_SLOT_INFO, false); + memcpy(&output_buffer[sizeof(struct smp_hdr)], buffer, *buffer_size); + *buffer_size += sizeof(struct smp_hdr); + + return true; +} diff --git a/tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/src/smp_test_util.h b/tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/src/smp_test_util.h new file mode 100644 index 000000000000..7c36d7fc8e5f --- /dev/null +++ b/tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/src/smp_test_util.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2023-2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef H_SMP_TEST_UTIL_ +#define H_SMP_TEST_UTIL_ + +#include +#include +#include +#include + +/* Function for creating an img_mgmt slot info command */ +bool create_img_mgmt_slot_info_packet(zcbor_state_t *zse, uint8_t *buffer, uint8_t *output_buffer, + uint16_t *buffer_size); + +#endif diff --git a/tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/sysbuild_too_large.conf b/tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/sysbuild_too_large.conf new file mode 100644 index 000000000000..47f00ff3cff8 --- /dev/null +++ b/tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/sysbuild_too_large.conf @@ -0,0 +1 @@ +SB_CONFIG_BOOTLOADER_MCUBOOT=y diff --git a/tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/testcase.yaml b/tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/testcase.yaml new file mode 100644 index 000000000000..dc795e49fb97 --- /dev/null +++ b/tests/subsys/mgmt/mcumgr/img_mgmt_slot_info/testcase.yaml @@ -0,0 +1,22 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 +# +common: + platform_allow: + - nrf52840dk/nrf52840 + - nrf5340dk/nrf5340/cpuapp + tags: + - mgmt + - mcumgr + - img_mgmt + build_only: false +tests: + img.mgmt.slot.info: {} + img.mgmt.slot.info.too.large: + extra_args: + - FILE_SUFFIX=too_large + img.mgmt.slot.info.dual.slot: + extra_args: + - FILE_SUFFIX=dual_slot From 0e08b9e91ec7b9b9e30117f70fae77971967a052 Mon Sep 17 00:00:00 2001 From: Jamie McCrae Date: Fri, 6 Sep 2024 13:57:15 +0100 Subject: [PATCH 4/4] doc: release: 4.0: Add note on new MCUmgr img mgmt slot info Adds information on the new img mgmt slot info command Signed-off-by: Jamie McCrae --- doc/releases/release-notes-4.0.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/releases/release-notes-4.0.rst b/doc/releases/release-notes-4.0.rst index d1c387b6e499..41f11fcc0b2d 100644 --- a/doc/releases/release-notes-4.0.rst +++ b/doc/releases/release-notes-4.0.rst @@ -345,6 +345,8 @@ Libraries / Subsystems * Added support for custom os mgmt bootloader info responses using notification hooks, this can be enabled witbh :kconfig:option:`CONFIG_MCUMGR_GRP_OS_BOOTLOADER_INFO_HOOK`, the data structure is :c:struct:`os_mgmt_bootloader_info_data`. + * Added support for img mgmt slot info command, which allows for listing information on + images and slots on the device. * Logging