From 17442cece9a9882354cb930e7fbbee19512359e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=98yvind=20R=C3=B8nningstad?= Date: Wed, 24 Mar 2021 12:37:07 +0100 Subject: [PATCH] tests: arm: Add arm_thread_swap_tz test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For testing secure->non-secure thread swapping. This also tests that the FP context is correctly preserved when calling a secure function. Signed-off-by: Øyvind Rønningstad --- .../arm/arm_thread_swap_tz/CMakeLists.txt | 13 ++ tests/arch/arm/arm_thread_swap_tz/Kconfig | 12 ++ .../boards/nucleo_l552ze_q_ns.overlay | 56 +++++++ .../boards/stm32l562e_dk_ns.overlay | 55 +++++++ tests/arch/arm/arm_thread_swap_tz/prj.conf | 13 ++ tests/arch/arm/arm_thread_swap_tz/src/main.c | 148 ++++++++++++++++++ .../arch/arm/arm_thread_swap_tz/testcase.yaml | 9 ++ 7 files changed, 306 insertions(+) create mode 100644 tests/arch/arm/arm_thread_swap_tz/CMakeLists.txt create mode 100644 tests/arch/arm/arm_thread_swap_tz/Kconfig create mode 100644 tests/arch/arm/arm_thread_swap_tz/boards/nucleo_l552ze_q_ns.overlay create mode 100644 tests/arch/arm/arm_thread_swap_tz/boards/stm32l562e_dk_ns.overlay create mode 100644 tests/arch/arm/arm_thread_swap_tz/prj.conf create mode 100644 tests/arch/arm/arm_thread_swap_tz/src/main.c create mode 100644 tests/arch/arm/arm_thread_swap_tz/testcase.yaml diff --git a/tests/arch/arm/arm_thread_swap_tz/CMakeLists.txt b/tests/arch/arm/arm_thread_swap_tz/CMakeLists.txt new file mode 100644 index 00000000000..7857cbd7b11 --- /dev/null +++ b/tests/arch/arm/arm_thread_swap_tz/CMakeLists.txt @@ -0,0 +1,13 @@ +# +# Copyright (c) 2021 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 +# + +cmake_minimum_required(VERSION 3.13.1) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(NONE) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) diff --git a/tests/arch/arm/arm_thread_swap_tz/Kconfig b/tests/arch/arm/arm_thread_swap_tz/Kconfig new file mode 100644 index 00000000000..6e237c6d596 --- /dev/null +++ b/tests/arch/arm/arm_thread_swap_tz/Kconfig @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright (c) 2021 Nordic Semicondutor ASA + +config ARM_STORE_EXC_RETURN + bool + default y if ARM_NONSECURE_FIRMWARE + help + Redefine this with looser requirements, so the EXC_RETURN value can + be checked in the test. This essentially tests that the official + requirements are sane. + +source "Kconfig.zephyr" diff --git a/tests/arch/arm/arm_thread_swap_tz/boards/nucleo_l552ze_q_ns.overlay b/tests/arch/arm/arm_thread_swap_tz/boards/nucleo_l552ze_q_ns.overlay new file mode 100644 index 00000000000..31eb8025aae --- /dev/null +++ b/tests/arch/arm/arm_thread_swap_tz/boards/nucleo_l552ze_q_ns.overlay @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2020 Linaro Limited. + * + * SPDX-License-Identifier: Apache-2.0 + */ + + + /* This partition table should be used along with TFM configuration: + * - TFM_PSA_API=ON (IPC) + * - ISOLATION_LEVEL 2 + * - TEST_S=ON (REGRESSION) + * - TEST_NS=ON (REGRESSION) + * + * In this configuration, TFM binary includes tests. As a consequence, + * its size is bloated and it is not possible to set secondary partitions + * for secured or non secured images. + */ + +/ { + chosen { + zephyr,code-partition = &slot1_partition; + }; +}; + +&flash0 { + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + boot_partition: partition@0 { + label = "mcuboot"; + reg = <0x00000000 0x00013000>; + read-only; + }; + /* Secure image primary slot */ + slot0_partition: partition@00013000 { + label = "image-0"; + reg = <0x00013000 0x00038000>; + }; + /* Non-secure image primary slot */ + slot1_partition: partition@0004B000 { + label = "image-1"; + reg = <0x0004B000 0x0002A000>; + }; + /* + * The flash starting at 0x7F000 and ending at + * 0x80000 is reserved for the application. + */ + storage_partition: partition@7F000 { + label = "storage"; + reg = <0x0007F000 0x00001000>; + }; + }; +}; diff --git a/tests/arch/arm/arm_thread_swap_tz/boards/stm32l562e_dk_ns.overlay b/tests/arch/arm/arm_thread_swap_tz/boards/stm32l562e_dk_ns.overlay new file mode 100644 index 00000000000..49444e19db9 --- /dev/null +++ b/tests/arch/arm/arm_thread_swap_tz/boards/stm32l562e_dk_ns.overlay @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2021 Yestin Sun + * + * SPDX-License-Identifier: Apache-2.0 + */ + + /* This partition table should be used along with TFM configuration: + * - TFM_PSA_API=ON (IPC) + * - ISOLATION_LEVEL 2 + * - TEST_S=ON (REGRESSION) + * - TEST_NS=OFF (By default) + * + * In this configuration, TFM binary includes tests. As a consequence, + * its size is bloated and it is not possible to set secondary partitions + * for secured or non secured images. + */ + +/ { + chosen { + zephyr,code-partition = &slot1_partition; + }; +}; + +&flash0 { + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + boot_partition: partition@0 { + label = "mcuboot"; + reg = <0x00000000 0x00019000>; + read-only; + }; + /* Secure image primary slot */ + slot0_partition: partition@00019000 { + label = "image-0"; + reg = <0x00019000 0x00038000>; + }; + /* Non-secure image primary slot */ + slot1_partition: partition@00051000 { + label = "image-1"; + reg = <0x00051000 0x0002A000>; + }; + /* + * The flash starting at 0x7F000 and ending at + * 0x80000 is reserved for the application. + */ + storage_partition: partition@7F000 { + label = "storage"; + reg = <0x0007F000 0x00001000>; + }; + }; +}; diff --git a/tests/arch/arm/arm_thread_swap_tz/prj.conf b/tests/arch/arm/arm_thread_swap_tz/prj.conf new file mode 100644 index 00000000000..809da5cf0a5 --- /dev/null +++ b/tests/arch/arm/arm_thread_swap_tz/prj.conf @@ -0,0 +1,13 @@ +# +# Copyright (c) 2021 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 +# + +CONFIG_ZTEST=y +CONFIG_ZTEST_THREAD_PRIORITY=0 +CONFIG_BUILD_WITH_TFM=y +CONFIG_TFM_IPC=y +CONFIG_TFM_PARTITION_AUDIT_LOG=n +CONFIG_FPU=y +CONFIG_FPU_SHARING=y diff --git a/tests/arch/arm/arm_thread_swap_tz/src/main.c b/tests/arch/arm/arm_thread_swap_tz/src/main.c new file mode 100644 index 00000000000..2104dfccc22 --- /dev/null +++ b/tests/arch/arm/arm_thread_swap_tz/src/main.c @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2021 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#ifndef EXC_RETURN_S +/* bit [6] stack used to push registers: 0=Non-secure 1=Secure */ +#define EXC_RETURN_S (0x00000040UL) +#endif + +#define HASH_LEN 32 + +static struct k_work_delayable interrupting_work; +static volatile bool work_done; +static char dummy_string[0x1000]; +static char dummy_digest_correct[HASH_LEN]; +static const uint32_t delay_ms = 4; +static volatile const struct k_thread *main_thread; + +static void do_hash(char *hash) +{ + size_t len; + + /* Calculate correct hash. */ + psa_status_t status = psa_hash_compute(PSA_ALG_SHA_256, dummy_string, + sizeof(dummy_string), hash, HASH_LEN, &len); + + zassert_equal(PSA_SUCCESS, status, NULL); + zassert_equal(HASH_LEN, len, NULL); +} + +static void work_func(struct k_work *work) +{ +#ifdef CONFIG_ARM_NONSECURE_PREEMPTIBLE_SECURE_CALLS + /* Check that the main thread was executing in secure mode. */ + zassert_true(main_thread->arch.mode_exc_return & EXC_RETURN_S, + "EXC_RETURN not secure: 0x%x\n", main_thread->arch.mode_exc_return); + +#else + /* Check that the main thread was executing in nonsecure mode. */ + zassert_false(main_thread->arch.mode_exc_return & EXC_RETURN_S, + "EXC_RETURN not nonsecure: 0x%x\n", main_thread->arch.mode_exc_return); +#endif + + work_done = true; + + /* If FPU is available, clobber FPU context in this thread to check that + * the correct context is restored in the other thread. + */ +#ifdef CONFIG_CPU_HAS_FPU + uint32_t clobber_val[16] = { + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + }; + + __asm__ volatile( + "vldmia %0, {s0-s15}\n" + "vldmia %0, {s16-s31}\n" + :: "r" (clobber_val) : + ); +#endif /* CONFIG_CPU_HAS_FPU */ + + /* Call a secure service here as well, to test the added complexity of + * calling secure services from two threads. + */ + psa_status_t status = psa_hash_compare(PSA_ALG_SHA_256, dummy_string, + sizeof(dummy_string), dummy_digest_correct, HASH_LEN); + + zassert_equal(PSA_SUCCESS, status, NULL); +} + +void test_thread_swap(void) +{ + int err; + char dummy_digest[HASH_LEN]; + k_timeout_t delay = K_MSEC(delay_ms); + k_tid_t curr; + psa_status_t status; + + curr = k_current_get(); + main_thread = (struct k_thread *)curr; + + status = psa_crypto_init(); + zassert_equal(PSA_SUCCESS, status, NULL); + + /* Calculate correct hash. */ + do_hash(dummy_digest_correct); + + /* Set up interrupting_work to fire while call_tfm() is executing. + * This tests that it is safe to switch threads while a secure service + * is running. + */ + k_work_init_delayable(&interrupting_work, work_func); + + err = k_work_reschedule(&interrupting_work, delay); + zassert_equal(1, err, "k_work_reschedule failed: %d\n", err); + + /* If FPU is available, check that FPU context is preserved when calling + * a secure function. + */ +#ifdef CONFIG_CPU_HAS_FPU + uint32_t test_val[16] = { + 0x1a2b3c4d, 0x1a2b3c4d, 0x1a2b3c4d, 0x1a2b3c4d, + 0x1a2b3c4d, 0x1a2b3c4d, 0x1a2b3c4d, 0x1a2b3c4d, + 0x1a2b3c4d, 0x1a2b3c4d, 0x1a2b3c4d, 0x1a2b3c4d, + 0x1a2b3c4d, 0x1a2b3c4d, 0x1a2b3c4d, 0x1a2b3c4d, + }; + uint32_t test_val_res0[16]; + uint32_t test_val_res1[16]; + + __asm__ volatile( + "vldmia %0, {s0-s15}\n" + "vldmia %0, {s16-s31}\n" + :: "r" (test_val) : + ); +#endif /* CONFIG_CPU_HAS_FPU */ + + work_done = false; + do_hash(dummy_digest); + zassert_true(work_done, "Interrupting work never happened\n"); + +#ifdef CONFIG_CPU_HAS_FPU + __asm__ volatile( + "vstmia %0, {s0-s15}\n" + "vstmia %1, {s16-s31}\n" + :: "r" (test_val_res0), "r" (test_val_res1) : + ); + + zassert_mem_equal(dummy_digest, dummy_digest_correct, HASH_LEN, NULL); + zassert_mem_equal(test_val, test_val_res0, sizeof(test_val), NULL); + zassert_mem_equal(test_val, test_val_res1, sizeof(test_val), NULL); +#endif /* CONFIG_CPU_HAS_FPU */ +} + +void test_main(void) +{ + ztest_test_suite(test_thread_swap, + ztest_unit_test(test_thread_swap) + ); + ztest_run_test_suite(test_thread_swap); +} diff --git a/tests/arch/arm/arm_thread_swap_tz/testcase.yaml b/tests/arch/arm/arm_thread_swap_tz/testcase.yaml new file mode 100644 index 00000000000..bcfce873d08 --- /dev/null +++ b/tests/arch/arm/arm_thread_swap_tz/testcase.yaml @@ -0,0 +1,9 @@ +common: + filter: (CONFIG_TFM_BOARD != "") and CONFIG_ARM_NONSECURE_FIRMWARE + tags: arm + arch_allow: arm +tests: + arch.arm.swap.tz: {} + arch.arm.swap.tz_off: + extra_configs: + - CONFIG_ARM_NONSECURE_PREEMPTIBLE_SECURE_CALLS=n