diff --git a/tests/subsys/mgmt/mcumgr/smp_version/CMakeLists.txt b/tests/subsys/mgmt/mcumgr/smp_version/CMakeLists.txt new file mode 100644 index 00000000000..f4fcdd2e6b7 --- /dev/null +++ b/tests/subsys/mgmt/mcumgr/smp_version/CMakeLists.txt @@ -0,0 +1,16 @@ +# +# Copyright (c) 2023 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(smp_version) + +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/) diff --git a/tests/subsys/mgmt/mcumgr/smp_version/boards/qemu_riscv32_smp.conf b/tests/subsys/mgmt/mcumgr/smp_version/boards/qemu_riscv32_smp.conf new file mode 100644 index 00000000000..1ede042960b --- /dev/null +++ b/tests/subsys/mgmt/mcumgr/smp_version/boards/qemu_riscv32_smp.conf @@ -0,0 +1 @@ +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096 diff --git a/tests/subsys/mgmt/mcumgr/smp_version/boards/qemu_riscv64.conf b/tests/subsys/mgmt/mcumgr/smp_version/boards/qemu_riscv64.conf new file mode 100644 index 00000000000..1ede042960b --- /dev/null +++ b/tests/subsys/mgmt/mcumgr/smp_version/boards/qemu_riscv64.conf @@ -0,0 +1 @@ +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096 diff --git a/tests/subsys/mgmt/mcumgr/smp_version/boards/qemu_riscv64_smp.conf b/tests/subsys/mgmt/mcumgr/smp_version/boards/qemu_riscv64_smp.conf new file mode 100644 index 00000000000..1ede042960b --- /dev/null +++ b/tests/subsys/mgmt/mcumgr/smp_version/boards/qemu_riscv64_smp.conf @@ -0,0 +1 @@ +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096 diff --git a/tests/subsys/mgmt/mcumgr/smp_version/prj.conf b/tests/subsys/mgmt/mcumgr/smp_version/prj.conf new file mode 100644 index 00000000000..fb9a0c91c5c --- /dev/null +++ b/tests/subsys/mgmt/mcumgr/smp_version/prj.conf @@ -0,0 +1,18 @@ +# +# Copyright (c) 2023 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_MCUMGR=y +CONFIG_MCUMGR_TRANSPORT_DUMMY=y +CONFIG_MCUMGR_TRANSPORT_DUMMY_RX_BUF_SIZE=256 +CONFIG_MCUMGR_GRP_OS=y +CONFIG_MCUMGR_GRP_OS_INFO=y +CONFIG_ZTEST_NEW_API=y +CONFIG_ZTEST_STACK_SIZE=2048 +CONFIG_REBOOT=n diff --git a/tests/subsys/mgmt/mcumgr/smp_version/src/main.c b/tests/subsys/mgmt/mcumgr/smp_version/src/main.c new file mode 100644 index 00000000000..be3516c9ae5 --- /dev/null +++ b/tests/subsys/mgmt/mcumgr/smp_version/src/main.c @@ -0,0 +1,365 @@ +/* + * Copyright (c) 2022 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "smp_test_util.h" + +#include +LOG_MODULE_REGISTER(mcumgr_fs_grp, 4); + +enum { + LEGACY_VERSION = 0, + CURRENT_VERSION, + FUTURE_VERSION, +}; + +#define SMP_RESPONSE_WAIT_TIME 3 +#define QUERY_BUFFER_SIZE 16 +#define ZCBOR_BUFFER_SIZE 256 +#define OUTPUT_BUFFER_SIZE 256 +#define ZCBOR_HISTORY_ARRAY_SIZE 4 + +#ifdef CONFIG_MCUMGR_SMP_SUPPORT_ORIGINAL_PROTOCOL +/* Response to legacy packet */ +static const uint8_t response_old[] = { + 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x01, 0x07, + 0xbf, 0x62, 0x72, 0x63, 0x03, 0xff +}; +#else +/* Response if a legacy packet is sent and server does not support it */ +static const uint8_t response_old[] = { + 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x01, 0x07, + 0xbf, 0x62, 0x72, 0x63, 0x0c, 0xff +}; +#endif + +/* Response to current packet */ +static const uint8_t response_current[] = { + 0x09, 0x00, 0x00, 0x13, 0x00, 0x00, 0x01, 0x07, + 0xbf, 0x63, 0x72, 0x65, 0x74, 0xbf, 0x65, 0x67, + 0x72, 0x6f, 0x75, 0x70, 0x00, 0x62, 0x72, 0x63, + 0x02, 0xff, 0xff +}; + +/* Response if an invalid (too high) version packet is sent and server does not support it */ +static const uint8_t response_new[] = { + 0x09, 0x00, 0x00, 0x06, 0x00, 0x00, 0x01, 0x07, + 0xbf, 0x62, 0x72, 0x63, 0x0d, 0xff +}; + +static struct net_buf *nb; + +const uint8_t query_fake[] = "8"; + +struct group_error { + uint16_t group; + uint16_t rc; + bool found; +}; + +static bool mcumgr_ret_decode(zcbor_state_t *state, struct group_error *result) +{ + bool ok; + size_t decoded; + uint32_t tmp_group; + uint32_t tmp_rc; + + struct zcbor_map_decode_key_val output_decode[] = { + ZCBOR_MAP_DECODE_KEY_DECODER("group", zcbor_uint32_decode, &tmp_group), + ZCBOR_MAP_DECODE_KEY_DECODER("rc", zcbor_uint32_decode, &tmp_rc), + }; + + result->found = false; + + ok = zcbor_map_decode_bulk(state, output_decode, ARRAY_SIZE(output_decode), &decoded) == 0; + + if (ok && + zcbor_map_decode_bulk_key_found(output_decode, ARRAY_SIZE(output_decode), "group") && + zcbor_map_decode_bulk_key_found(output_decode, ARRAY_SIZE(output_decode), "rc")) { + result->group = (uint16_t)tmp_group; + result->rc = (uint16_t)tmp_rc; + result->found = true; + } + + return ok; +} + +ZTEST(smp_version, test_legacy_command) +{ + 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 zcbor_string output = { 0 }; + size_t decoded = 0; + struct smp_hdr *smp_header; + int32_t rc; + struct group_error group_error; + + struct zcbor_map_decode_key_val output_decode[] = { + ZCBOR_MAP_DECODE_KEY_DECODER("output", zcbor_tstr_decode, &output), + ZCBOR_MAP_DECODE_KEY_DECODER("rc", zcbor_int32_decode, &rc), + ZCBOR_MAP_DECODE_KEY_DECODER("ret", mcumgr_ret_decode, &group_error), + }; + + 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_mcumgr_format_packet(zse, query_fake, buffer, buffer_out, &buffer_size, + LEGACY_VERSION); + 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(); + + /* 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 and ensure validity */ + nb = smp_dummy_get_outgoing(); + smp_dummy_disable(); + + /* Check that the received response matches the expected response */ + zassert_equal(sizeof(response_old), nb->len, "Expected received data length mismatch"); + zassert_mem_equal(response_old, nb->data, nb->len, "Expected received data mismatch"); + + /* Process received data by removing header */ + smp_header = net_buf_pull_mem(nb, sizeof(struct smp_hdr)); + zassert_equal(smp_header->nh_version, LEGACY_VERSION, + "Expected response header version mismatch"); + + zcbor_new_decode_state(zsd, 4, nb->data, nb->len, 1); + 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_equal( + zcbor_map_decode_bulk_key_found(output_decode, ARRAY_SIZE(output_decode), + "output"), 0, + "Did not expect to get output in response"); + + zassert_equal( + zcbor_map_decode_bulk_key_found(output_decode, ARRAY_SIZE(output_decode), + "rc"), 1, + "Expected to get rc in response"); + + zassert_equal( + zcbor_map_decode_bulk_key_found(output_decode, ARRAY_SIZE(output_decode), + "ret"), 0, + "Did not expect to get ret in response"); + +#ifdef CONFIG_MCUMGR_SMP_SUPPORT_ORIGINAL_PROTOCOL + zassert_equal(rc, MGMT_ERR_EINVAL, "Expected to get MGMT_ERR_EINVAL error"); +#else + zassert_equal(rc, MGMT_ERR_UNSUPPORTED_TOO_OLD, + "Expected to get MGMT_ERR_UNSUPPORTED_TOO_OLD error"); +#endif +} + +ZTEST(smp_version, test_current_command) +{ + 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 zcbor_string output = { 0 }; + size_t decoded = 0; + struct smp_hdr *smp_header; + int32_t rc; + struct group_error group_error; + + struct zcbor_map_decode_key_val output_decode[] = { + ZCBOR_MAP_DECODE_KEY_DECODER("output", zcbor_tstr_decode, &output), + ZCBOR_MAP_DECODE_KEY_DECODER("rc", zcbor_int32_decode, &rc), + ZCBOR_MAP_DECODE_KEY_DECODER("ret", mcumgr_ret_decode, &group_error), + }; + + 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_mcumgr_format_packet(zse, query_fake, buffer, buffer_out, &buffer_size, + CURRENT_VERSION); + 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(); + + /* 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 and ensure validity */ + nb = smp_dummy_get_outgoing(); + smp_dummy_disable(); + + /* Check that the received response matches the expected response */ + zassert_equal(sizeof(response_current), nb->len, "Expected received data length mismatch"); + zassert_mem_equal(response_current, nb->data, nb->len, "Expected received data mismatch"); + + /* Process received data by removing header */ + smp_header = net_buf_pull_mem(nb, sizeof(struct smp_hdr)); + zassert_equal(smp_header->nh_version, CURRENT_VERSION, + "Expected response header version mismatch"); + + zcbor_new_decode_state(zsd, 4, nb->data, nb->len, 1); + 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_equal( + zcbor_map_decode_bulk_key_found(output_decode, ARRAY_SIZE(output_decode), + "output"), 0, + "Did not expect to get output in response"); + + zassert_equal( + zcbor_map_decode_bulk_key_found(output_decode, ARRAY_SIZE(output_decode), + "rc"), 0, + "Did not expect to get rc in response"); + + zassert_equal( + zcbor_map_decode_bulk_key_found(output_decode, ARRAY_SIZE(output_decode), + "ret"), 1, + "Expected to get ret in response"); + + zassert_true(group_error.found, "Expected both group and rc in ret to be found"); + zassert_equal(group_error.group, MGMT_GROUP_ID_OS, + "Expected to get MGMT_GROUP_ID_OS for ret group"); + zassert_equal(group_error.rc, OS_MGMT_RET_RC_INVALID_FORMAT, + "Expected to get OS_MGMT_RET_RC_INVALID_FORMAT for ret rc"); +} + +ZTEST(smp_version, test_new_command) +{ + 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 zcbor_string output = { 0 }; + size_t decoded = 0; + struct smp_hdr *smp_header; + int32_t rc; + struct group_error group_error; + + struct zcbor_map_decode_key_val output_decode[] = { + ZCBOR_MAP_DECODE_KEY_DECODER("output", zcbor_tstr_decode, &output), + ZCBOR_MAP_DECODE_KEY_DECODER("rc", zcbor_int32_decode, &rc), + ZCBOR_MAP_DECODE_KEY_DECODER("ret", mcumgr_ret_decode, &group_error), + }; + + 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_mcumgr_format_packet(zse, query_fake, buffer, buffer_out, &buffer_size, + FUTURE_VERSION); + 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(); + + /* 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 and ensure validity */ + nb = smp_dummy_get_outgoing(); + smp_dummy_disable(); + + /* Check that the received response matches the expected response */ + zassert_equal(sizeof(response_new), nb->len, "Expected received data length mismatch"); + zassert_mem_equal(response_new, nb->data, nb->len, "Expected received data mismatch"); + + /* Process received data by removing header */ + smp_header = net_buf_pull_mem(nb, sizeof(struct smp_hdr)); + zassert_equal(smp_header->nh_version, CURRENT_VERSION, + "Expected response header version mismatch"); + + zcbor_new_decode_state(zsd, 4, nb->data, nb->len, 1); + 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_equal( + zcbor_map_decode_bulk_key_found(output_decode, ARRAY_SIZE(output_decode), + "output"), 0, + "Did not expect to get output in response"); + + zassert_equal( + zcbor_map_decode_bulk_key_found(output_decode, ARRAY_SIZE(output_decode), + "rc"), 1, + "Expected to get rc in response"); + + zassert_equal( + zcbor_map_decode_bulk_key_found(output_decode, ARRAY_SIZE(output_decode), + "ret"), 0, + "Did not expect to get ret in response"); + + zassert_equal(rc, MGMT_ERR_UNSUPPORTED_TOO_NEW, + "Expected to get MGMT_ERR_UNSUPPORTED_TOO_NEW error"); +} + +static void cleanup_test(void *p) +{ + if (nb != NULL) { + net_buf_unref(nb); + nb = NULL; + } +} + +/* Main test set */ +ZTEST_SUITE(smp_version, NULL, NULL, NULL, cleanup_test, NULL); diff --git a/tests/subsys/mgmt/mcumgr/smp_version/src/smp_test_util.c b/tests/subsys/mgmt/mcumgr/smp_version/src/smp_test_util.c new file mode 100644 index 00000000000..e93ad69ff2a --- /dev/null +++ b/tests/subsys/mgmt/mcumgr/smp_version/src/smp_test_util.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "smp_test_util.h" +#include +#include +#include +#include + +/* SMP header function for generating os_mgmt info command header with sequence number set to 1 */ +void smp_make_hdr(struct smp_hdr *rsp_hdr, size_t len, uint8_t version) +{ + *rsp_hdr = (struct smp_hdr) { + .nh_len = sys_cpu_to_be16(len), + .nh_flags = 0, + .nh_op = 0, + .nh_group = sys_cpu_to_be16(MGMT_GROUP_ID_OS), + .nh_seq = 1, + .nh_id = OS_MGMT_ID_INFO, + .nh_version = version, + }; +} + +/* Function for creating an os_mgmt info command */ +bool create_mcumgr_format_packet(zcbor_state_t *zse, const uint8_t *format, uint8_t *buffer, + uint8_t *output_buffer, uint16_t *buffer_size, uint8_t version) +{ + bool ok; + + ok = zcbor_map_start_encode(zse, 2) && + zcbor_tstr_put_lit(zse, "format") && + zcbor_tstr_put_term(zse, format) && + zcbor_map_end_encode(zse, 2); + + *buffer_size = (zse->payload_mut - buffer); + smp_make_hdr((struct smp_hdr *)output_buffer, *buffer_size, version); + memcpy(&output_buffer[sizeof(struct smp_hdr)], buffer, *buffer_size); + *buffer_size += sizeof(struct smp_hdr); + + return ok; +} diff --git a/tests/subsys/mgmt/mcumgr/smp_version/src/smp_test_util.h b/tests/subsys/mgmt/mcumgr/smp_version/src/smp_test_util.h new file mode 100644 index 00000000000..8ad74d34384 --- /dev/null +++ b/tests/subsys/mgmt/mcumgr/smp_version/src/smp_test_util.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2022 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef H_SMP_TEST_UTIL_ +#define H_SMP_TEST_UTIL_ + +#include +#include +#include +#include + +/* SMP header function for generating os_mgmt info command header with sequence number set to 1 */ +void smp_make_hdr(struct smp_hdr *rsp_hdr, size_t len, uint8_t version); + +/* Function for creating an os_mgmt info command */ +bool create_mcumgr_format_packet(zcbor_state_t *zse, const uint8_t *format, uint8_t *buffer, + uint8_t *output_buffer, uint16_t *buffer_size, uint8_t version); + + +#endif diff --git a/tests/subsys/mgmt/mcumgr/smp_version/testcase.yaml b/tests/subsys/mgmt/mcumgr/smp_version/testcase.yaml new file mode 100644 index 00000000000..a17e53ebff6 --- /dev/null +++ b/tests/subsys/mgmt/mcumgr/smp_version/testcase.yaml @@ -0,0 +1,28 @@ +# +# Copyright (c) 2023 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 +# +tests: + smp.version: + tags: smp_version + # FIXME: Exclude systems whereby the processor type is not known and emits a warning + arch_exclude: + - nios2 + - sparc + - mips + - posix + platform_exclude: + - qemu_kvm_arm64 + smp.version_no_legacy: + tags: smp_version + extra_configs: + - CONFIG_MCUMGR_SMP_SUPPORT_ORIGINAL_PROTOCOL=n + # FIXME: Exclude systems whereby the processor type is not known and emits a warning + arch_exclude: + - nios2 + - sparc + - mips + - posix + platform_exclude: + - qemu_kvm_arm64