diff --git a/.gitignore b/.gitignore index a00b0a4..e89472c 100644 --- a/.gitignore +++ b/.gitignore @@ -94,3 +94,6 @@ # Icetea related file test_suite.json + +# default delivery dir +DELIVERY/ diff --git a/TESTS/mbed_hal/spm/main.cpp b/TESTS/mbed_hal/spm/main.cpp new file mode 100644 index 0000000..644ec7d --- /dev/null +++ b/TESTS/mbed_hal/spm/main.cpp @@ -0,0 +1,163 @@ +/* mbed Microcontroller Library + * Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#include "cmsis.h" +#include "spm_api.h" +#include + +using namespace utest::v1; + +#if !defined(COMPONENT_PSA_SRV_IPC) +#error [NOT_SUPPORTED] Test supported only on PSA targets +#endif + +#if !defined ( __GNUC__ ) +#error [NOT_SUPPORTED] this test is supported on GCC only +#endif + +extern "C" { +#define HARDFAULT_IRQn ((IRQn_Type)-13) +#define EXC_RETURN_RETURN_STACK_MSK ((uint32_t)(0x00000004)) +#define PC_INDEX_IN_STACK_FRAME 6 + +volatile uint32_t fault_occurred; +uint32_t real_hard_fault_handler; + +__attribute__ ((always_inline)) __STATIC_INLINE uint32_t __get_LR(void) +{ + register uint32_t result; + + __ASM volatile ("MOV %0, LR\n" : "=r" (result)); + return result; +} + +// This function is required as we need a symbol/address +// to jump to from fault handler. +void do_nothing(void) +{ + __NOP(); +} + +// Test exception handler +static void hard_fault_handler_test() +{ + fault_occurred++; + // LR is set EXC_RETURN + // lowest bits identify PSP vs MSP stack used for stacking + uint32_t lr = __get_LR(); + uint32_t sp; + + if (lr & EXC_RETURN_RETURN_STACK_MSK) { + sp = __get_PSP(); + } else { + sp = __get_MSP(); + } + + // Overwrite return address. + // Fake return to a our special function since current + // instruction under test will always fail due to memory protection + ((uint32_t *)sp)[PC_INDEX_IN_STACK_FRAME] = (uint32_t)do_nothing; +} + +// Using naked function as it will not be executed from beginning to the end. +// The execution flow expected to be interrupted by exception and we will +// return to other function. +// compiler will not produce prolog and epilog code for naked function +// and thus will preserve stack in un-corrupted state +__attribute__((naked)) void call_mem(uint32_t addr) +{ + // Only first instruction will be executed in positive flow, + // since exception will be generated for invalid memory access. + // Other instructions are for calling do_nothing function according to AAPCS. + __ASM( + "LDR r1, [r0]\n" + "BX lr\n" + ); +} +} + +static void test_memory(uint32_t addr, uint32_t expected_fatal_count) +{ + call_mem(addr); + // Although call_mem is a "naked" function, it is called using AAPCS. + // Thus we can assume LR will point to next instruction, and caller save registers are backed up + TEST_ASSERT_EQUAL(expected_fatal_count, fault_occurred); +} + +static void secure_ram_fault_test(void) +{ + test_memory(PSA_SECURE_RAM_START, 1); +} + +static void secure_flash_fault_test(void) +{ + test_memory(PSA_SECURE_ROM_START, 1); +} + +static void non_secure_ram_fault_test(void) +{ + test_memory(PSA_NON_SECURE_RAM_START, 0); +} + +static void non_secure_flash_fault_test(void) +{ + test_memory(PSA_NON_SECURE_ROM_START, 0); +} + +utest::v1::status_t fault_override_setup(const Case *const source, const size_t index_of_case) +{ + // Save old hard fault handler and replace it with a new one + // NOTE: only works when VTOR is set to RAM + real_hard_fault_handler = NVIC_GetVector(HARDFAULT_IRQn); + NVIC_SetVector(HARDFAULT_IRQn, (uint32_t)&hard_fault_handler_test); + fault_occurred = 0; + + return greentea_case_setup_handler(source, index_of_case); +} + +utest::v1::status_t fault_override_teardown(const Case *const source, const size_t passed, const size_t failed, + const failure_t reason) +{ + // Restore real hard fault handler + NVIC_SetVector(HARDFAULT_IRQn, real_hard_fault_handler); + + return greentea_case_teardown_handler(source, passed, failed, reason); +} + +Case cases[] = { + Case("SPM - Access secure RAM", fault_override_setup, secure_ram_fault_test, fault_override_teardown), + Case("SPM - Access secure Flash", fault_override_setup, secure_flash_fault_test, fault_override_teardown), + Case("SPM - Access non-secure RAM", fault_override_setup, non_secure_ram_fault_test, fault_override_teardown), + Case("SPM - Access non-secure Flash", fault_override_setup, non_secure_flash_fault_test, fault_override_teardown), +}; + +utest::v1::status_t greentea_test_setup(const size_t number_of_cases) +{ +#ifndef NO_GREENTEA + GREENTEA_SETUP(20, "default_auto"); +#endif + return greentea_test_setup_handler(number_of_cases); +} + +Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler); + +int main() +{ + Harness::run(specification); +} diff --git a/TESTS/psa/prot_internal_storage/COMPONENT_SPE/psa_setup.c b/TESTS/psa/prot_internal_storage/COMPONENT_SPE/psa_setup.c new file mode 100644 index 0000000..6f1c953 --- /dev/null +++ b/TESTS/psa/prot_internal_storage/COMPONENT_SPE/psa_setup.c @@ -0,0 +1,78 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#include "spm_panic.h" +#include "spm_internal.h" +#include "handles_manager.h" +#include "cmsis.h" +#include "psa_test_its_reset_partition.h" +#include "psa_its_partition.h" + + +spm_partition_t g_partitions[2] = { + { + .partition_id = TEST_ITS_RESET_ID, + .thread_id = 0, + .flags_rot_srv = TEST_ITS_RESET_WAIT_ANY_SID_MSK, + .flags_interrupts = 0, + .rot_services = NULL, + .rot_services_count = TEST_ITS_RESET_ROT_SRV_COUNT, + .extern_sids = NULL, + .extern_sids_count = TEST_ITS_RESET_EXT_ROT_SRV_COUNT, + .irq_mapper = NULL, + }, + { + .partition_id = ITS_ID, + .thread_id = 0, + .flags_rot_srv = ITS_WAIT_ANY_SID_MSK, + .flags_interrupts = 0, + .rot_services = NULL, + .rot_services_count = ITS_ROT_SRV_COUNT, + .extern_sids = NULL, + .extern_sids_count = ITS_EXT_ROT_SRV_COUNT, + .irq_mapper = NULL, + }, +}; + +/* Check all the defined memory regions for overlapping. */ + +/* A list of all the memory regions. */ +const mem_region_t *mem_regions = NULL; + +const uint32_t mem_region_count = 0; + +// forward declaration of partition initializers +void test_its_reset_init(spm_partition_t *partition); +void its_init(spm_partition_t *partition); + +uint32_t init_partitions(spm_partition_t **partitions) +{ + if (NULL == partitions) { + SPM_PANIC("partitions is NULL!\n"); + } + + test_its_reset_init(&(g_partitions[0])); + its_init(&(g_partitions[1])); + + *partitions = g_partitions; + return 2; +} + diff --git a/TESTS/psa/prot_internal_storage/its_reset/COMPONENT_PSA_SRV_IPC/test_pits.c b/TESTS/psa/prot_internal_storage/its_reset/COMPONENT_PSA_SRV_IPC/test_pits.c new file mode 100644 index 0000000..309c493 --- /dev/null +++ b/TESTS/psa/prot_internal_storage/its_reset/COMPONENT_PSA_SRV_IPC/test_pits.c @@ -0,0 +1,20 @@ +#include "spm_client.h" +#include "psa_prot_internal_storage.h" +#include "test_pits.h" +#include "psa_test_its_reset_ifs.h" + +psa_its_status_t test_psa_its_reset(void) +{ + psa_handle_t conn = psa_connect(TEST_PSA_ITS_RESET, 1); + if (conn <= PSA_NULL_HANDLE) { + return PSA_ITS_ERROR_STORAGE_FAILURE; + } + + psa_error_t status = psa_call(conn, NULL, 0, NULL, 0); + if (status == PSA_DROP_CONNECTION) { + status = PSA_ITS_ERROR_STORAGE_FAILURE; + } + + psa_close(conn); + return status; +} diff --git a/TESTS/psa/prot_internal_storage/its_reset/COMPONENT_SPE/psa_test_its_reset_partition.c b/TESTS/psa/prot_internal_storage/its_reset/COMPONENT_SPE/psa_test_its_reset_partition.c new file mode 100644 index 0000000..042de8b --- /dev/null +++ b/TESTS/psa/prot_internal_storage/its_reset/COMPONENT_SPE/psa_test_its_reset_partition.c @@ -0,0 +1,97 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#include "cmsis.h" +#include "mbed_toolchain.h" /* For using MBED_ALIGN macro */ +#include "rtx_os.h" +#include "spm_panic.h" +#include "spm_internal.h" +#include "psa_test_its_reset_partition.h" +#include "psa_test_its_reset_ifs.h" + + +/* Threads stacks */ +MBED_ALIGN(8) uint8_t test_its_reset_thread_stack[1024] = {0}; + +/* Threads control blocks */ +osRtxThread_t test_its_reset_thread_cb = {0}; + +/* Thread attributes - for thread initialization */ +osThreadAttr_t test_its_reset_thread_attr = { + .name = "test_its_reset", + .attr_bits = 0, + .cb_mem = &test_its_reset_thread_cb, + .cb_size = sizeof(test_its_reset_thread_cb), + .stack_mem = test_its_reset_thread_stack, + .stack_size = 1024, + .priority = osPriorityNormal, + .tz_module = 0, + .reserved = 0 + }; + +spm_rot_service_t test_its_reset_rot_services[TEST_ITS_RESET_ROT_SRV_COUNT] = { + { + .sid = TEST_PSA_ITS_RESET, + .mask = TEST_PSA_ITS_RESET_MSK, + .partition = NULL, + .min_version = 1, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, +}; + + +static osRtxMutex_t test_its_reset_mutex = {0}; +static const osMutexAttr_t test_its_reset_mutex_attr = { + .name = "test_its_reset_mutex", + .attr_bits = osMutexRecursive | osMutexPrioInherit | osMutexRobust, + .cb_mem = &test_its_reset_mutex, + .cb_size = sizeof(test_its_reset_mutex), +}; + + +extern void test_pits_entry(void *ptr); + +void test_its_reset_init(spm_partition_t *partition) +{ + if (NULL == partition) { + SPM_PANIC("partition is NULL!\n"); + } + + partition->mutex = osMutexNew(&test_its_reset_mutex_attr); + if (NULL == partition->mutex) { + SPM_PANIC("Failed to create mutex for secure partition test_its_reset!\n"); + } + + for (uint32_t i = 0; i < TEST_ITS_RESET_ROT_SRV_COUNT; ++i) { + test_its_reset_rot_services[i].partition = partition; + } + partition->rot_services = test_its_reset_rot_services; + + partition->thread_id = osThreadNew(test_pits_entry, NULL, &test_its_reset_thread_attr); + if (NULL == partition->thread_id) { + SPM_PANIC("Failed to create start main thread of partition test_its_reset!\n"); + } +} diff --git a/TESTS/psa/prot_internal_storage/its_reset/COMPONENT_SPE/psa_test_its_reset_partition.h b/TESTS/psa/prot_internal_storage/its_reset/COMPONENT_SPE/psa_test_its_reset_partition.h new file mode 100644 index 0000000..4c16e89 --- /dev/null +++ b/TESTS/psa/prot_internal_storage/its_reset/COMPONENT_SPE/psa_test_its_reset_partition.h @@ -0,0 +1,52 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#ifndef PSA_TEST_ITS_RESET_PARTITION_H +#define PSA_TEST_ITS_RESET_PARTITION_H + +#define TEST_ITS_RESET_ID 11 + +#define TEST_ITS_RESET_ROT_SRV_COUNT (1UL) +#define TEST_ITS_RESET_EXT_ROT_SRV_COUNT (0UL) + +/* TEST_ITS_RESET event flags */ +#define TEST_ITS_RESET_RESERVED1_POS (1UL) +#define TEST_ITS_RESET_RESERVED1_MSK (1UL << TEST_ITS_RESET_RESERVED1_POS) + +#define TEST_ITS_RESET_RESERVED2_POS (2UL) +#define TEST_ITS_RESET_RESERVED2_MSK (1UL << TEST_ITS_RESET_RESERVED2_POS) + + + +#define TEST_PSA_ITS_RESET_MSK_POS (4UL) +#define TEST_PSA_ITS_RESET_MSK (1UL << TEST_PSA_ITS_RESET_MSK_POS) + +#define TEST_ITS_RESET_WAIT_ANY_SID_MSK (\ + TEST_PSA_ITS_RESET_MSK) + +/* +#define TEST_ITS_RESET_WAIT_ANY_MSK (\ + TEST_ITS_RESET_WAIT_ANY_SID_MSK) | \ + PSA_DOORBELL) +*/ + + +#endif // PSA_TEST_ITS_RESET_PARTITION_H diff --git a/TESTS/psa/prot_internal_storage/its_reset/COMPONENT_SPE/test_pits_impl.cpp b/TESTS/psa/prot_internal_storage/its_reset/COMPONENT_SPE/test_pits_impl.cpp new file mode 100644 index 0000000..21d805d --- /dev/null +++ b/TESTS/psa/prot_internal_storage/its_reset/COMPONENT_SPE/test_pits_impl.cpp @@ -0,0 +1,57 @@ +/* Copyright (c) 2018 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include "psa_prot_internal_storage.h" +#include "test_pits_impl.h" +#include "kv_config.h" +#include "KVMap.h" +#include "KVStore.h" +#include "mbed_error.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +using namespace mbed; + +#define STR_EXPAND(tok) #tok + +psa_its_status_t test_psa_its_reset_impl(void) +{ + psa_its_status_t status = PSA_ITS_SUCCESS; + + int kv_status = kv_init_storage_config(); + if (kv_status != MBED_SUCCESS) { + return PSA_ITS_ERROR_STORAGE_FAILURE; + } + + KVMap &kv_map = KVMap::get_instance(); + KVStore *kvstore = kv_map.get_main_kv_instance(STR_EXPAND(MBED_CONF_STORAGE_DEFAULT_KV)); + if (!kvstore) { + return PSA_ITS_ERROR_STORAGE_FAILURE; + } + + if (kvstore->reset() != MBED_SUCCESS) { + status = PSA_ITS_ERROR_STORAGE_FAILURE; + } + + return status; +} + +#ifdef __cplusplus +} +#endif diff --git a/TESTS/psa/prot_internal_storage/its_reset/COMPONENT_SPE/test_pits_reset_partition.c b/TESTS/psa/prot_internal_storage/its_reset/COMPONENT_SPE/test_pits_reset_partition.c new file mode 100644 index 0000000..c25f6a0 --- /dev/null +++ b/TESTS/psa/prot_internal_storage/its_reset/COMPONENT_SPE/test_pits_reset_partition.c @@ -0,0 +1,51 @@ +// -------------------------------------- Includes ----------------------------------- + +#include +#include "cmsis_os2.h" +#include "spm_server.h" +#include "spm_panic.h" +#include "psa_test_its_reset_partition.h" +#include "psa_prot_internal_storage.h" +#include "test_pits_impl.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +void test_pits_entry(void *ptr) +{ + uint32_t signals = 0; + psa_msg_t msg = {0}; + psa_error_t status = PSA_SUCCESS; + + while (1) { + signals = psa_wait_any(PSA_BLOCK); + if ((signals & TEST_PSA_ITS_RESET_MSK) != 0) { + psa_get(TEST_PSA_ITS_RESET_MSK, &msg); + switch (msg.type) { + case PSA_IPC_CONNECT: //fallthrough + case PSA_IPC_DISCONNECT: + { + break; + } + case PSA_IPC_CALL: + { + status = test_psa_its_reset_impl(); + break; + } + default: + { + SPM_PANIC("Unexpected message type %d!", (int)(msg.type)); + break; + } + } + + psa_reply(msg.handle, status); + } + } +} + +#ifdef __cplusplus +} +#endif diff --git a/TESTS/psa/prot_internal_storage/its_reset/psa_test_its_reset_ifs.h b/TESTS/psa/prot_internal_storage/its_reset/psa_test_its_reset_ifs.h new file mode 100644 index 0000000..468516b --- /dev/null +++ b/TESTS/psa/prot_internal_storage/its_reset/psa_test_its_reset_ifs.h @@ -0,0 +1,27 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#ifndef PSA_TEST_ITS_RESET_PARTITION_ROT_SERVICES_H +#define PSA_TEST_ITS_RESET_PARTITION_ROT_SERVICES_H + +#define TEST_PSA_ITS_RESET 0x00011A04 + +#endif // PSA_TEST_ITS_RESET_PARTITION_ROT_SERVICES_H diff --git a/TESTS/psa/prot_internal_storage/its_reset/test_pits_psa.json b/TESTS/psa/prot_internal_storage/its_reset/test_pits_psa.json new file mode 100644 index 0000000..c2abfdb --- /dev/null +++ b/TESTS/psa/prot_internal_storage/its_reset/test_pits_psa.json @@ -0,0 +1,21 @@ +{ + "name": "TEST_ITS_RESET", + "type": "APPLICATION-ROT", + "priority": "NORMAL", + "id": "0x0000000B", + "entry_point": "test_pits_entry", + "stack_size": "0x400", + "heap_size": "0x400", + "services": [{ + "name": "TEST_PSA_ITS_RESET", + "identifier": "0x00011A04", + "signal": "TEST_PSA_ITS_RESET_MSK", + "non_secure_clients": true, + "minor_version": 1, + "minor_policy": "RELAXED" + } + ], + "source_files": [ + "COMPONENT_SPE/test_pits_reset_partition.c" + ] + } diff --git a/TESTS/psa/prot_internal_storage/main.cpp b/TESTS/psa/prot_internal_storage/main.cpp index ec274a4..5eb940e 100644 --- a/TESTS/psa/prot_internal_storage/main.cpp +++ b/TESTS/psa/prot_internal_storage/main.cpp @@ -38,6 +38,9 @@ struct psa_its_info_t info = {0, PSA_ITS_WRITE_ONCE_FLAG}; memset(read_buff, 0, TEST_BUFF_SIZE); + status = test_psa_its_reset(); + TEST_ASSERT_EQUAL(PSA_ITS_SUCCESS, status); + status = psa_its_get_info(5, &info); TEST_ASSERT_EQUAL(PSA_ITS_ERROR_KEY_NOT_FOUND, status); diff --git a/TESTS/psa/spm_client/COMPONENT_NSPE/client_ipc_tests.cpp b/TESTS/psa/spm_client/COMPONENT_NSPE/client_ipc_tests.cpp new file mode 100644 index 0000000..63099f1 --- /dev/null +++ b/TESTS/psa/spm_client/COMPONENT_NSPE/client_ipc_tests.cpp @@ -0,0 +1,473 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef COMPONENT_PSA_SRV_IPC +#error [NOT_SUPPORTED] SPM tests can run only on SPM-enabled targets +#endif // COMPONENT_PSA_SRV_IPC + +#include "mbed.h" +#include "greentea-client/test_env.h" +#include "unity.h" +#include "utest.h" +#include "spm_client.h" +#include "psa_client_tests_part1_ifs.h" + +using namespace utest::v1; + +#define MINOR_VER 0 +#define DROP_CONN_MINOR_VER 5 +#define CLIENT_RSP_BUF_SIZE 128 +#define OFFSET_POS 1 +#define INVALID_SID 0x00001A020 + + +typedef struct th_struct { + psa_handle_t handle; + psa_invec_t *iovec_temp; + uint8_t *expected; + uint8_t expected_size; +} th_struct_t; + +/* ------------------------------------- Functions ----------------------------------- */ + +static psa_handle_t client_ipc_tests_connect(uint32_t sid, uint32_t minor_version) +{ + psa_handle_t handle = psa_connect(sid, minor_version); + + TEST_ASSERT_TRUE(handle > 0); + + return handle; +} + +static void client_ipc_tests_call( + psa_handle_t handle, + psa_invec_t *iovec_temp, + size_t tx_len, + size_t rx_len, + uint8_t *expected, + uint8_t expected_size + ) +{ + error_t status = PSA_SUCCESS; + uint8_t *response_buf = (uint8_t*)malloc(CLIENT_RSP_BUF_SIZE * sizeof(uint8_t)); + memset(response_buf, 0, CLIENT_RSP_BUF_SIZE); + psa_outvec_t resp = {NULL, rx_len}; + + if (rx_len > 0) { + resp.base = response_buf; + } + + status = psa_call( handle, + (tx_len ? iovec_temp : NULL), + tx_len, + (rx_len ? &resp : NULL), + (rx_len ? 1 : 0) + ); + + if (expected) { + TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, response_buf, expected_size); + } + free(response_buf); + TEST_ASSERT_EQUAL_INT(PSA_SUCCESS, status); +} + +static void client_ipc_tests_close(psa_handle_t handle) +{ + psa_close(handle); + + // Wait for psa_close to finish on server side + osDelay(50); +} + +//Testing iovec 0 sent as NULL +void iovec_0_NULL() +{ + psa_handle_t handle = client_ipc_tests_connect(PART1_ROT_SRV1, MINOR_VER); + + uint8_t expect_size = 5; + uint8_t off = 2; + + uint8_t meta_iovec[2] = {expect_size, off}; + uint8_t buff1[] = {1, 2, 3, 4, 5}; + uint8_t expected_buff[] = {1, 2, 3, 4, 5}; + + psa_invec_t iovec_temp[PSA_MAX_IOVEC - 1] = {{NULL, 0}, + {meta_iovec, sizeof(meta_iovec)}, + {buff1, sizeof(buff1)}}; + + client_ipc_tests_call(handle, iovec_temp, PSA_MAX_IOVEC - 1, CLIENT_RSP_BUF_SIZE, expected_buff, sizeof(expected_buff)); + + client_ipc_tests_close(handle); +} + +//Testing iovec 1 sent as NULL +void iovec_1_NULL() +{ + psa_handle_t handle = client_ipc_tests_connect(PART1_ROT_SRV1, MINOR_VER); + + uint8_t expect_size = 2; + uint8_t off = 3; + + uint8_t meta_iovec[2] = {expect_size, off}; + uint8_t buff1[] = {1, 2, 3, 4, 5}; + uint8_t expected_buff[] = {2, 3}; + + psa_invec_t iovec_temp[PSA_MAX_IOVEC - 1] = {{meta_iovec, sizeof(meta_iovec)}, + {NULL, 0}, + {buff1, sizeof(buff1)}}; + + client_ipc_tests_call(handle, iovec_temp, PSA_MAX_IOVEC - 1, CLIENT_RSP_BUF_SIZE, expected_buff, sizeof(expected_buff)); + + client_ipc_tests_close(handle); +} + +//Testing iovec 2 sent as NULL +void iovec_2_NULL() +{ + psa_handle_t handle = client_ipc_tests_connect(PART1_ROT_SRV1, MINOR_VER); + + uint8_t expect_size = 2; + uint8_t off = 3; + + uint8_t meta_iovec[2] = {expect_size, off}; + uint8_t buff1[] = {1, 2, 3, 4, 5}; + uint8_t expected_buff[] = {2, 3}; + + psa_invec_t iovec_temp[PSA_MAX_IOVEC - 1] = {{meta_iovec, sizeof(meta_iovec)}, + {buff1, sizeof(buff1)}, + {NULL, 0}}; + + client_ipc_tests_call(handle, iovec_temp, PSA_MAX_IOVEC - 1, CLIENT_RSP_BUF_SIZE, expected_buff, sizeof(expected_buff)); + + client_ipc_tests_close(handle); +} + +// Testing in_vec[i] sent with size 0 and base not NULL +void in_vec_base_not_NULL_size_0() +{ + uint8_t dummy_buff[] = {1, 2, 3, 4, 5}; + psa_invec_t iovec_temp[1] = { {dummy_buff, 0} }; + + psa_handle_t handle = client_ipc_tests_connect(PART1_ROT_SRV1, MINOR_VER); + + client_ipc_tests_call(handle, iovec_temp, 1, 0, NULL, 0); + + client_ipc_tests_close(handle); +} + +// Testing in_len is 0 but in_vec is not NULL +void in_len_0_in_vec_not_NULL() +{ + uint8_t dummy_buff[] = {1, 2, 3, 4, 5}; + psa_invec_t iovec_temp[1] = { {dummy_buff, sizeof(dummy_buff)} }; + + psa_handle_t handle = client_ipc_tests_connect(PART1_ROT_SRV1, MINOR_VER); + + client_ipc_tests_call(handle, iovec_temp, 0, 0, NULL, 0); + + client_ipc_tests_close(handle); +} + +// Testing out_len is 0 but out_vec is not NULL +void out_len_0_outvec_not_NULL() +{ + error_t status = PSA_SUCCESS; + + uint8_t dummy_res[10] = {0}; + psa_outvec_t outvec_temp[1] = {{dummy_res, sizeof(dummy_res)}}; + + uint8_t dummy_buff[] = {1, 2, 3, 4, 5}; + + psa_handle_t handle = client_ipc_tests_connect(PART1_ROT_SRV1, MINOR_VER); + + psa_invec_t in_vec_temp[2] = { {dummy_buff, sizeof(dummy_buff)}, + {dummy_buff, sizeof(dummy_buff)} + }; + + status = psa_call(handle, in_vec_temp, 2, outvec_temp, 0); + + TEST_ASSERT_EQUAL_INT32(PSA_SUCCESS, status); + + client_ipc_tests_close(handle); +} + +//Testing rx_buff sent as NULL and rx_len as 0 +void rx_buff_null() +{ + psa_handle_t handle = client_ipc_tests_connect(PART1_ROT_SRV1, MINOR_VER); + + uint8_t expect_size = 0, off = 2; + + uint8_t meta_iovec[2] = {expect_size, off}; + uint8_t buff1[] = {1, 2, 3, 4, 5}, buff2[] = {6}; + + psa_invec_t iovec_temp[PSA_MAX_IOVEC - 1] = {{meta_iovec, sizeof(meta_iovec)}, + {buff1, sizeof(buff1)}, + {buff2, sizeof(buff2)}}; + + client_ipc_tests_call(handle, iovec_temp, PSA_MAX_IOVEC - 1, 0, NULL, 0); + + client_ipc_tests_close(handle); +} + +//Testing tx_buff sent as NULL and tx_len as 0 +void tx_buff_null() +{ + psa_handle_t handle = client_ipc_tests_connect(PART1_ROT_SRV1, MINOR_VER); + + client_ipc_tests_call(handle, NULL, 0, CLIENT_RSP_BUF_SIZE, NULL, 0); + + client_ipc_tests_close(handle); +} + +//Testing rx_buff and tx_null sent as NULL and rx_len and tx_len as 0 +void rx_tx_null() +{ + psa_handle_t handle = client_ipc_tests_connect(PART1_ROT_SRV1, MINOR_VER); + + client_ipc_tests_call(handle, NULL, 0, 0, NULL, 0); + + client_ipc_tests_close(handle); +} + +//Testing multiple subsequent calls to the same SID +void multiple_call() +{ + psa_handle_t handle = client_ipc_tests_connect(PART1_ROT_SRV1, MINOR_VER); + + uint8_t expect_size = 2, off = 2; + + uint8_t meta_iovec[2] = {expect_size, off}; + uint8_t buff1[] = {1, 2, 3}; + uint8_t buff2[] = {4, 5, 6}; + uint8_t expected_buff[] = {1, 2}; + + psa_invec_t iovec_temp[PSA_MAX_IOVEC - 1] = {{meta_iovec, sizeof(meta_iovec)}, + {buff1, sizeof(buff1)}, + {buff2, sizeof(buff2)}}; + + client_ipc_tests_call(handle, iovec_temp, PSA_MAX_IOVEC - 1, CLIENT_RSP_BUF_SIZE, expected_buff, sizeof(expected_buff)); + + meta_iovec[1] = 3; //off + iovec_temp[0].base = meta_iovec; + expected_buff[0] = 2; + expected_buff[1] = 3; + + client_ipc_tests_call(handle, iovec_temp, PSA_MAX_IOVEC - 1, CLIENT_RSP_BUF_SIZE, expected_buff, sizeof(expected_buff)); + + meta_iovec[1] = 4; //off + iovec_temp[0].base = meta_iovec; + expected_buff[0] = 3; + expected_buff[1] = 4; + + client_ipc_tests_call(handle, iovec_temp, PSA_MAX_IOVEC - 1, CLIENT_RSP_BUF_SIZE, expected_buff, sizeof(expected_buff)); + + client_ipc_tests_close(handle); +} + +static void set_struct(th_struct_t *thr_attr, psa_handle_t handle, psa_invec_t *iovec_temp, uint8_t *expect, uint8_t expected_size) +{ + thr_attr->handle = handle; + thr_attr->iovec_temp = iovec_temp; + thr_attr->expected = expect; + thr_attr->expected_size = expected_size; +} + +static void call_diff_handle(th_struct_t *thr_attr) +{ + psa_handle_t handle = client_ipc_tests_connect(PART1_ROT_SRV1, MINOR_VER); + + client_ipc_tests_call(handle, + thr_attr->iovec_temp, + PSA_MAX_IOVEC - 1, + CLIENT_RSP_BUF_SIZE, + thr_attr->expected, + thr_attr->expected_size); + + osDelay(10); + + client_ipc_tests_close(handle); +} + +//Testing multiple parallel calls to the same SID with different handles +void multi_thread_diff_handles() +{ + Thread T1, T2, T3; + th_struct_t thr_attr[] = {{0}, {0}, {0}}; + + uint8_t meta_iovec_1[] = { 2, //expect_size + 2 //off + }; + uint8_t buff1[] = {1, 2, 3}; + uint8_t buff2[] = {4, 5, 6}; + uint8_t expected_buff_1[] = {1, 2}; + + psa_invec_t iovec_temp_1[PSA_MAX_IOVEC - 1] = {{meta_iovec_1, sizeof(meta_iovec_1)}, + {buff1, sizeof(buff1)}, + {buff2, sizeof(buff2)}}; + + set_struct(&thr_attr[0], 0, iovec_temp_1, expected_buff_1, sizeof(expected_buff_1)); + osStatus err = T1.start(callback(call_diff_handle, (th_struct_t *)&thr_attr[0])); + if (err) { + TEST_FAIL_MESSAGE("creating thread failed!"); + } + + uint8_t meta_iovec_2[] = { 2, //expect_size + 3 //off + }; + uint8_t expected_buff_2[] = {2, 3}; + + psa_invec_t iovec_temp_2[PSA_MAX_IOVEC - 1] = {{meta_iovec_2, sizeof(meta_iovec_2)}, + {buff1, sizeof(buff1)}, + {buff2, sizeof(buff2)}}; + set_struct(&thr_attr[1], 0, iovec_temp_2, expected_buff_2, sizeof(expected_buff_2)); + err = T2.start(callback(call_diff_handle, (th_struct_t *)&thr_attr[1])); + if (err) { + TEST_FAIL_MESSAGE("creating thread failed!"); + } + + uint8_t meta_iovec_3[] = { 2, //expect_size + 4 //off + }; + uint8_t expected_buff_3[] = {3, 4}; + + psa_invec_t iovec_temp_3[PSA_MAX_IOVEC - 1] = {{meta_iovec_3, sizeof(meta_iovec_3)}, + {buff1, sizeof(buff1)}, + {buff2, sizeof(buff2)}}; + + set_struct(&thr_attr[2], 0, iovec_temp_3, expected_buff_3, sizeof(expected_buff_3)); + err = T3.start(callback(call_diff_handle, (th_struct_t *)&thr_attr[2])); + if (err) { + TEST_FAIL_MESSAGE("creating thread failed!"); + } + + err = T1.join(); + if (err) { + TEST_FAIL_MESSAGE("joining thread failed!"); + } + err = T2.join(); + if (err) { + TEST_FAIL_MESSAGE("joining thread failed!"); + } + err = T3.join(); + if (err) { + TEST_FAIL_MESSAGE("joining thread failed!"); + } + +} + + +//Testing exceeding num of max channels allowed by psa_connect +void exceed_num_of_max_channels() +{ + int i = 0; + psa_handle_t handle[MBED_CONF_SPM_IPC_MAX_NUM_OF_CHANNELS + 1] = {0}; + + for (i = 0; i < MBED_CONF_SPM_IPC_MAX_NUM_OF_CHANNELS + 1; i++) { + if (i != MBED_CONF_SPM_IPC_MAX_NUM_OF_CHANNELS) { + handle[i] = client_ipc_tests_connect(PART1_ROT_SRV1, MINOR_VER); + } + else { + handle[i] = psa_connect(PART1_ROT_SRV1, MINOR_VER); + TEST_ASSERT_EQUAL_INT32(PSA_CONNECTION_REFUSED, handle[i]); + } + } + + for (i = 0; i < MBED_CONF_SPM_IPC_MAX_NUM_OF_CHANNELS; i++) { + client_ipc_tests_close(handle[i]); + } +} + +void client_close_null_handle() +{ + client_ipc_tests_close(PSA_NULL_HANDLE); +} + +void drop_connection() +{ + psa_handle_t handle = client_ipc_tests_connect(DROP_CONN, DROP_CONN_MINOR_VER); + psa_error_t status = psa_call(handle, NULL, 0, NULL, 0); + TEST_ASSERT_EQUAL_INT(PSA_DROP_CONNECTION, status); + + status = PSA_SUCCESS; + status = psa_call(handle, NULL, 0, NULL, 0); + TEST_ASSERT_EQUAL_INT(PSA_DROP_CONNECTION, status); + + client_ipc_tests_close(handle); +} + +void verify_psa_framework_version() +{ + uint32_t ff_version = psa_framework_version(); + TEST_ASSERT_EQUAL_INT(PSA_FRAMEWORK_VERSION, ff_version); +} + +void psa_version_existing() +{ + uint32_t rot_version = psa_version(DROP_CONN); + TEST_ASSERT_EQUAL_INT(DROP_CONN_MINOR_VER, rot_version); +} + +void psa_version_non_existing() +{ + uint32_t rot_version = psa_version(INVALID_SID); + TEST_ASSERT_EQUAL_INT(PSA_VERSION_NONE, rot_version); +} + +void psa_version_secure_access_only() +{ + uint32_t rot_version = psa_version(SECURE_CLIENTS_ONLY); + TEST_ASSERT_EQUAL_INT(PSA_VERSION_NONE, rot_version); +} + + + // Test cases +Case cases[] = { + Case("Testing client iovec_0_NULL", iovec_0_NULL), + Case("Testing client iovec_1_NULL", iovec_1_NULL), + Case("Testing client iovec_2_NULL", iovec_2_NULL), + Case("Testing client in_vec 0 base not NULL size 0", in_vec_base_not_NULL_size_0), + Case("Testing client in_len 0 in_vec not NULL", in_len_0_in_vec_not_NULL), + Case("Testing client out_len is 0 but out_vec is not NULL", out_len_0_outvec_not_NULL), + Case("Testing client rx_buff_null", rx_buff_null), + Case("Testing client tx_buff_null", tx_buff_null), + Case("Testing client rx_tx_null", rx_tx_null), + Case("Testing client multiple_call from a single thread", multiple_call), + Case("Testing client multiple calls on different channels to the same SID", multi_thread_diff_handles), + Case("Testing client exceed num of max channels allowed", exceed_num_of_max_channels), + Case("Testing client close on NULL handle", client_close_null_handle), + Case("Testing DROP_CONNECTION State", drop_connection), + Case("Testing client psa_framework_version() API", verify_psa_framework_version), + Case("Testing client psa_version() API on existing SID", psa_version_existing), + Case("Testing client psa_version() API on non-existing SID", psa_version_non_existing), + Case("Testing client psa_version() API to a service that is not NSPE callable", psa_version_secure_access_only), +}; + +utest::v1::status_t test_setup(const size_t number_of_cases) +{ + // Setup Greentea using a reasonable timeout in seconds +#ifndef NO_GREENTEA + GREENTEA_SETUP(60, "default_auto"); +#endif + return verbose_test_setup_handler(number_of_cases); +} + +Specification specification(test_setup, cases); + +int main() +{ + Harness::run(specification); + return 0; +} diff --git a/TESTS/psa/spm_client/COMPONENT_SPE/psa_client_tests_part1_ifs.h b/TESTS/psa/spm_client/COMPONENT_SPE/psa_client_tests_part1_ifs.h new file mode 100644 index 0000000..aadba6f --- /dev/null +++ b/TESTS/psa/spm_client/COMPONENT_SPE/psa_client_tests_part1_ifs.h @@ -0,0 +1,29 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#ifndef PSA_CLIENT_TESTS_PART1_PARTITION_ROT_SERVICES_H +#define PSA_CLIENT_TESTS_PART1_PARTITION_ROT_SERVICES_H + +#define PART1_ROT_SRV1 0x00001A05 +#define DROP_CONN 0x00001A06 +#define SECURE_CLIENTS_ONLY 0x00001A07 + +#endif // PSA_CLIENT_TESTS_PART1_PARTITION_ROT_SERVICES_H diff --git a/TESTS/psa/spm_client/COMPONENT_SPE/psa_client_tests_part1_partition.c b/TESTS/psa/spm_client/COMPONENT_SPE/psa_client_tests_part1_partition.c new file mode 100644 index 0000000..2182c94 --- /dev/null +++ b/TESTS/psa/spm_client/COMPONENT_SPE/psa_client_tests_part1_partition.c @@ -0,0 +1,121 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#include "cmsis.h" +#include "mbed_toolchain.h" /* For using MBED_ALIGN macro */ +#include "rtx_os.h" +#include "spm_panic.h" +#include "spm_internal.h" +#include "psa_client_tests_part1_partition.h" +#include "psa_client_tests_part1_ifs.h" + + +/* Threads stacks */ +MBED_ALIGN(8) uint8_t client_tests_part1_thread_stack[1024] = {0}; + +/* Threads control blocks */ +osRtxThread_t client_tests_part1_thread_cb = {0}; + +/* Thread attributes - for thread initialization */ +osThreadAttr_t client_tests_part1_thread_attr = { + .name = "client_tests_part1", + .attr_bits = 0, + .cb_mem = &client_tests_part1_thread_cb, + .cb_size = sizeof(client_tests_part1_thread_cb), + .stack_mem = client_tests_part1_thread_stack, + .stack_size = 1024, + .priority = osPriorityNormal, + .tz_module = 0, + .reserved = 0 + }; + +spm_rot_service_t client_tests_part1_rot_services[CLIENT_TESTS_PART1_ROT_SRV_COUNT] = { + { + .sid = PART1_ROT_SRV1, + .mask = PART1_ROT_SRV1_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, + { + .sid = DROP_CONN, + .mask = DROP_CONN_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, + { + .sid = SECURE_CLIENTS_ONLY, + .mask = SECURE_CLIENTS_ONLY_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = false, + .queue = { + .head = NULL, + .tail = NULL + } + }, +}; + + +static osRtxMutex_t client_tests_part1_mutex = {0}; +static const osMutexAttr_t client_tests_part1_mutex_attr = { + .name = "client_tests_part1_mutex", + .attr_bits = osMutexRecursive | osMutexPrioInherit | osMutexRobust, + .cb_mem = &client_tests_part1_mutex, + .cb_size = sizeof(client_tests_part1_mutex), +}; + + +extern void server_main(void *ptr); + +void client_tests_part1_init(spm_partition_t *partition) +{ + if (NULL == partition) { + SPM_PANIC("partition is NULL!\n"); + } + + partition->mutex = osMutexNew(&client_tests_part1_mutex_attr); + if (NULL == partition->mutex) { + SPM_PANIC("Failed to create mutex for secure partition client_tests_part1!\n"); + } + + for (uint32_t i = 0; i < CLIENT_TESTS_PART1_ROT_SRV_COUNT; ++i) { + client_tests_part1_rot_services[i].partition = partition; + } + partition->rot_services = client_tests_part1_rot_services; + + partition->thread_id = osThreadNew(server_main, NULL, &client_tests_part1_thread_attr); + if (NULL == partition->thread_id) { + SPM_PANIC("Failed to create start main thread of partition client_tests_part1!\n"); + } +} diff --git a/TESTS/psa/spm_client/COMPONENT_SPE/psa_client_tests_part1_partition.h b/TESTS/psa/spm_client/COMPONENT_SPE/psa_client_tests_part1_partition.h new file mode 100644 index 0000000..20903eb --- /dev/null +++ b/TESTS/psa/spm_client/COMPONENT_SPE/psa_client_tests_part1_partition.h @@ -0,0 +1,58 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#ifndef PSA_CLIENT_TESTS_PART1_PARTITION_H +#define PSA_CLIENT_TESTS_PART1_PARTITION_H + +#define CLIENT_TESTS_PART1_ID 1 + +#define CLIENT_TESTS_PART1_ROT_SRV_COUNT (3UL) +#define CLIENT_TESTS_PART1_EXT_ROT_SRV_COUNT (0UL) + +/* CLIENT_TESTS_PART1 event flags */ +#define CLIENT_TESTS_PART1_RESERVED1_POS (1UL) +#define CLIENT_TESTS_PART1_RESERVED1_MSK (1UL << CLIENT_TESTS_PART1_RESERVED1_POS) + +#define CLIENT_TESTS_PART1_RESERVED2_POS (2UL) +#define CLIENT_TESTS_PART1_RESERVED2_MSK (1UL << CLIENT_TESTS_PART1_RESERVED2_POS) + + + +#define PART1_ROT_SRV1_MSK_POS (4UL) +#define PART1_ROT_SRV1_MSK (1UL << PART1_ROT_SRV1_MSK_POS) +#define DROP_CONN_MSK_POS (5UL) +#define DROP_CONN_MSK (1UL << DROP_CONN_MSK_POS) +#define SECURE_CLIENTS_ONLY_MSK_POS (6UL) +#define SECURE_CLIENTS_ONLY_MSK (1UL << SECURE_CLIENTS_ONLY_MSK_POS) + +#define CLIENT_TESTS_PART1_WAIT_ANY_SID_MSK (\ + PART1_ROT_SRV1_MSK | \ + DROP_CONN_MSK | \ + SECURE_CLIENTS_ONLY_MSK) + +/* +#define CLIENT_TESTS_PART1_WAIT_ANY_MSK (\ + CLIENT_TESTS_PART1_WAIT_ANY_SID_MSK) | \ + PSA_DOORBELL) +*/ + + +#endif // PSA_CLIENT_TESTS_PART1_PARTITION_H diff --git a/TESTS/psa/spm_client/COMPONENT_SPE/psa_setup.c b/TESTS/psa/spm_client/COMPONENT_SPE/psa_setup.c new file mode 100644 index 0000000..8f0ba3e --- /dev/null +++ b/TESTS/psa/spm_client/COMPONENT_SPE/psa_setup.c @@ -0,0 +1,78 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#include "spm_panic.h" +#include "spm_internal.h" +#include "handles_manager.h" +#include "cmsis.h" +#include "psa_client_tests_part1_partition.h" +#include "psa_its_partition.h" + + +spm_partition_t g_partitions[2] = { + { + .partition_id = CLIENT_TESTS_PART1_ID, + .thread_id = 0, + .flags_rot_srv = CLIENT_TESTS_PART1_WAIT_ANY_SID_MSK, + .flags_interrupts = 0, + .rot_services = NULL, + .rot_services_count = CLIENT_TESTS_PART1_ROT_SRV_COUNT, + .extern_sids = NULL, + .extern_sids_count = CLIENT_TESTS_PART1_EXT_ROT_SRV_COUNT, + .irq_mapper = NULL, + }, + { + .partition_id = ITS_ID, + .thread_id = 0, + .flags_rot_srv = ITS_WAIT_ANY_SID_MSK, + .flags_interrupts = 0, + .rot_services = NULL, + .rot_services_count = ITS_ROT_SRV_COUNT, + .extern_sids = NULL, + .extern_sids_count = ITS_EXT_ROT_SRV_COUNT, + .irq_mapper = NULL, + }, +}; + +/* Check all the defined memory regions for overlapping. */ + +/* A list of all the memory regions. */ +const mem_region_t *mem_regions = NULL; + +const uint32_t mem_region_count = 0; + +// forward declaration of partition initializers +void client_tests_part1_init(spm_partition_t *partition); +void its_init(spm_partition_t *partition); + +uint32_t init_partitions(spm_partition_t **partitions) +{ + if (NULL == partitions) { + SPM_PANIC("partitions is NULL!\n"); + } + + client_tests_part1_init(&(g_partitions[0])); + its_init(&(g_partitions[1])); + + *partitions = g_partitions; + return 2; +} + diff --git a/TESTS/psa/spm_client/COMPONENT_SPE/server.c b/TESTS/psa/spm_client/COMPONENT_SPE/server.c new file mode 100644 index 0000000..5eddff9 --- /dev/null +++ b/TESTS/psa/spm_client/COMPONENT_SPE/server.c @@ -0,0 +1,83 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include "spm_server.h" +#include "spm_panic.h" +#include "psa_client_tests_part1_partition.h" + +#define MSG_BUF_SIZE 128 +uint8_t data[MSG_BUF_SIZE] = {0}; + +void server_main(void *ptr) +{ + uint32_t signals = 0; + psa_msg_t msg = {0}; + while (1) { + signals = psa_wait_any(PSA_BLOCK); + if (signals & PART1_ROT_SRV1_MSK) { + psa_get(PART1_ROT_SRV1_MSK, &msg); + switch (msg.type) { + case PSA_IPC_CONNECT: + case PSA_IPC_DISCONNECT: + break; + case PSA_IPC_CALL: { + memset(data, 0, sizeof(data)); + if (msg.in_size[0] + msg.in_size[1] + msg.in_size[2] > 1) { + size_t offset = psa_read(msg.handle, 0, (void *)data, msg.in_size[0]); + offset += psa_read(msg.handle, 1, (void *)(data + offset), msg.in_size[1]); + psa_read(msg.handle, 2, (void *)(data + offset), msg.in_size[2]); + } + if (msg.out_size[0] > 0) { + uint8_t resp_size = data[0]; + uint8_t resp_offset = data[1]; + psa_write(msg.handle, 0, (const void *)(data + resp_offset), resp_size); + } + break; + } + default: { + SPM_PANIC("Invalid msg type"); + } + } + psa_reply(msg.handle, PSA_SUCCESS); + } else if (signals & DROP_CONN_MSK) { + psa_get(DROP_CONN_MSK, &msg); + switch (msg.type) { + case PSA_IPC_CONNECT: + case PSA_IPC_DISCONNECT: + psa_reply(msg.handle, PSA_SUCCESS); + break; + case PSA_IPC_CALL: + psa_reply(msg.handle, PSA_DROP_CONNECTION); + break; + default: + SPM_PANIC("Invalid msg type"); + } + } else if (signals & SECURE_CLIENTS_ONLY_MSK){ + psa_get(SECURE_CLIENTS_ONLY_MSK, &msg); + switch (msg.type) { + case PSA_IPC_CONNECT: + case PSA_IPC_DISCONNECT: + case PSA_IPC_CALL: + psa_reply(msg.handle, PSA_SUCCESS); + break; + default: + SPM_PANIC("Invalid msg type"); + } + } else { + SPM_PANIC("Received invalid signal %d", signals); + } + } +} + diff --git a/TESTS/psa/spm_client/part1_psa.json b/TESTS/psa/spm_client/part1_psa.json new file mode 100644 index 0000000..71760aa --- /dev/null +++ b/TESTS/psa/spm_client/part1_psa.json @@ -0,0 +1,37 @@ +{ + "name": "CLIENT_TESTS_PART1", + "type": "APPLICATION-ROT", + "priority": "NORMAL", + "id": "0x00000001", + "entry_point": "server_main", + "stack_size": "0x400", + "heap_size": "0x400", + "services": [{ + "name": "PART1_ROT_SRV1", + "identifier": "0x00001A05", + "signal": "PART1_ROT_SRV1_MSK", + "non_secure_clients": true, + "minor_version": 5, + "minor_policy": "RELAXED" + }, + { + "name": "DROP_CONN", + "identifier": "0x00001A06", + "signal": "DROP_CONN_MSK", + "non_secure_clients": true, + "minor_version": 5, + "minor_policy": "RELAXED" + }, + { + "name": "SECURE_CLIENTS_ONLY", + "identifier": "0x00001A07", + "signal": "SECURE_CLIENTS_ONLY_MSK", + "non_secure_clients": false, + "minor_version": 5, + "minor_policy": "RELAXED" + } + ], + "source_files": [ + "COMPONENT_SPE/server.c" + ] +} diff --git a/TESTS/psa/spm_client/psa_client_tests_part1_ifs.h b/TESTS/psa/spm_client/psa_client_tests_part1_ifs.h new file mode 100644 index 0000000..aadba6f --- /dev/null +++ b/TESTS/psa/spm_client/psa_client_tests_part1_ifs.h @@ -0,0 +1,29 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#ifndef PSA_CLIENT_TESTS_PART1_PARTITION_ROT_SERVICES_H +#define PSA_CLIENT_TESTS_PART1_PARTITION_ROT_SERVICES_H + +#define PART1_ROT_SRV1 0x00001A05 +#define DROP_CONN 0x00001A06 +#define SECURE_CLIENTS_ONLY 0x00001A07 + +#endif // PSA_CLIENT_TESTS_PART1_PARTITION_ROT_SERVICES_H diff --git a/TESTS/psa/spm_server/COMPONENT_NSPE/main.cpp b/TESTS/psa/spm_server/COMPONENT_NSPE/main.cpp new file mode 100644 index 0000000..ba7394e1 --- /dev/null +++ b/TESTS/psa/spm_server/COMPONENT_NSPE/main.cpp @@ -0,0 +1,284 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef COMPONENT_PSA_SRV_IPC +#error [NOT_SUPPORTED] SPM tests can run only on SPM-enabled targets +#endif // COMPONENT_PSA_SRV_IPC + +#include "mbed.h" +#include "greentea-client/test_env.h" +#include "unity.h" +#include "utest.h" +#include "spm_client.h" +#include "psa_server_test_part1_ifs.h" +#include "server_tests.h" +using namespace utest::v1; + +#define TEST_ROT_SRV_MINOR 12 +#define OUT_BUFFER_SIZE 60 + +psa_handle_t control_handle = 0; +char test_str[] = "abcdefghijklmnopqrstuvwxyz"; +char cross_part_buf[] = "Hello and welcome SPM"; + + +PSA_TEST_CLIENT(wait_timeout) +{ + osDelay(50); + psa_handle_t test_handle = psa_connect(TEST, TEST_ROT_SRV_MINOR); + TEST_ASSERT(test_handle > 0); + + psa_close(test_handle); +} + + +PSA_TEST_CLIENT(identity_during_connect) +{ + psa_handle_t test_handle = psa_connect(TEST, TEST_ROT_SRV_MINOR); + TEST_ASSERT(test_handle > 0); + + psa_close(test_handle); +} + + +PSA_TEST_CLIENT(identity_during_call) +{ + psa_error_t status = PSA_SUCCESS; + psa_handle_t test_handle = psa_connect(TEST, TEST_ROT_SRV_MINOR); + TEST_ASSERT(test_handle > 0); + + status = psa_call(test_handle, NULL, 0, NULL, 0); + TEST_ASSERT_EQUAL(PSA_SUCCESS, status); + + psa_close(test_handle); +} + +PSA_TEST_CLIENT(msg_size_assertion) +{ + psa_error_t status = PSA_SUCCESS; + psa_invec_t data[PSA_MAX_IOVEC] = { + {test_str, 4}, + {test_str + 5, 6}, + {test_str + 13, 1}, + {NULL, 0} + }; + psa_handle_t test_handle = psa_connect(TEST, TEST_ROT_SRV_MINOR); + TEST_ASSERT(test_handle > 0); + + status = psa_call(test_handle, data, PSA_MAX_IOVEC, NULL, 0); + TEST_ASSERT_EQUAL(PSA_SUCCESS, status); + + psa_close(test_handle); +} + +PSA_TEST_CLIENT(reject_connection) +{ + psa_handle_t test_handle = psa_connect(TEST, TEST_ROT_SRV_MINOR); + TEST_ASSERT_EQUAL(PSA_CONNECTION_REFUSED, test_handle); +} + +PSA_TEST_CLIENT(read_at_outofboud_offset) +{ + psa_error_t status = PSA_SUCCESS; + psa_invec_t data = { test_str, sizeof(test_str) }; + psa_handle_t test_handle = psa_connect(TEST, TEST_ROT_SRV_MINOR); + TEST_ASSERT(test_handle > 0); + + status = psa_call(test_handle, &data, 1, NULL, 0); + TEST_ASSERT_EQUAL(PSA_SUCCESS, status); + + psa_close(test_handle); +} + +PSA_TEST_CLIENT(msg_read_truncation) +{ + psa_error_t status = PSA_SUCCESS; + psa_invec_t data[3] = { + {test_str, 4}, + {test_str + 5, 6}, + {test_str + 13, 1} + }; + psa_handle_t test_handle = psa_connect(TEST, TEST_ROT_SRV_MINOR); + TEST_ASSERT(test_handle > 0); + + status = psa_call(test_handle, data, 3, NULL, 0); + TEST_ASSERT_EQUAL(PSA_SUCCESS, status); + + psa_close(test_handle); +} + +PSA_TEST_CLIENT(skip_zero) +{ + psa_error_t status = PSA_SUCCESS; + psa_invec_t data = { test_str, sizeof(test_str) }; + psa_handle_t test_handle = psa_connect(TEST, TEST_ROT_SRV_MINOR); + TEST_ASSERT(test_handle > 0); + + status = psa_call(test_handle, &data, 1, NULL, 0); + TEST_ASSERT_EQUAL(PSA_SUCCESS, status); + + psa_close(test_handle); +} + +PSA_TEST_CLIENT(skip_some) +{ + psa_error_t status = PSA_SUCCESS; + psa_invec_t data = { test_str, sizeof(test_str) }; + psa_handle_t test_handle = psa_connect(TEST, TEST_ROT_SRV_MINOR); + TEST_ASSERT(test_handle > 0); + + status = psa_call(test_handle, &data, 1, NULL, 0); + TEST_ASSERT_EQUAL(PSA_SUCCESS, status); + + psa_close(test_handle); +} + +PSA_TEST_CLIENT(skip_more_than_left) +{ + psa_error_t status = PSA_SUCCESS; + psa_invec_t data = { test_str, 8 }; + psa_handle_t test_handle = psa_connect(TEST, TEST_ROT_SRV_MINOR); + TEST_ASSERT(test_handle > 0); + + status = psa_call(test_handle, &data, 1, NULL, 0); + TEST_ASSERT_EQUAL(PSA_SUCCESS, status); + + psa_close(test_handle); +} + +PSA_TEST_CLIENT(rhandle_factorial) +{ + uint32_t secure_value = 0; + uint32_t value = 1; + psa_error_t status = PSA_SUCCESS; + psa_outvec_t resp = { &secure_value, sizeof(secure_value) }; + psa_handle_t test_handle = psa_connect(TEST, TEST_ROT_SRV_MINOR); + TEST_ASSERT(test_handle > 0); + + for (uint32_t i = 1; i <= 5; i++) { + value *= i; + status = psa_call(test_handle, NULL, 0, &resp, 1); + TEST_ASSERT_EQUAL(PSA_SUCCESS, status); + TEST_ASSERT_EQUAL(value, secure_value); + } + + psa_close(test_handle); +} + +PSA_TEST_CLIENT(cross_partition_call) +{ + psa_handle_t test_handle = psa_connect(TEST, TEST_ROT_SRV_MINOR); + size_t in_len = strlen(cross_part_buf); + TEST_ASSERT_MESSAGE(test_handle > 0, "psa_connect() failed"); + + psa_invec_t iovec = { cross_part_buf, in_len }; + uint8_t *response_buf = (uint8_t*)malloc(sizeof(uint8_t) * OUT_BUFFER_SIZE); + memset(response_buf, 0, OUT_BUFFER_SIZE); + psa_outvec_t resp = { response_buf, OUT_BUFFER_SIZE }; + + psa_error_t status = psa_call(test_handle, &iovec, 1, &resp, 1); + TEST_ASSERT_EQUAL(PSA_SUCCESS, status); + TEST_ASSERT_EQUAL_STRING_LEN("MPS emoclew dna olleHMPS emoclew dna olleH", response_buf, in_len*2); + free(response_buf); + + psa_close(test_handle); +} + +// Test a common DOORBELL scenario +PSA_TEST_CLIENT(doorbell_test) +{ + psa_handle_t test_handle = psa_connect(TEST, TEST_ROT_SRV_MINOR); + TEST_ASSERT_MESSAGE(test_handle > 0, "psa_connect() failed"); + + psa_error_t status = psa_call(test_handle, NULL, 0, NULL, 0); + TEST_ASSERT_EQUAL(PSA_SUCCESS, status); + + psa_close(test_handle); +} + + +utest::v1::status_t spm_setup(const size_t number_of_cases) { + control_handle = psa_connect(CONTROL, 0); + if (control_handle < 0) { + error("Could not open a connection with CONTROL ROT_SRV"); + } + +#ifndef NO_GREENTEA + GREENTEA_SETUP(60, "default_auto"); +#endif + return greentea_test_setup_handler(number_of_cases); +} + +void spm_teardown(const size_t passed, const size_t failed, const failure_t failure) +{ + psa_close(control_handle); + greentea_test_teardown_handler(passed, failed, failure); +} + +utest::v1::status_t spm_case_setup(const Case *const source, const size_t index_of_case) +{ + psa_error_t status = PSA_SUCCESS; + test_action_t action = START_TEST; + psa_invec_t data = {&action, sizeof(action)}; + + status = psa_call(control_handle, &data, 1, NULL, 0); + TEST_ASSERT_EQUAL(PSA_SUCCESS, status); + osDelay(50); + return greentea_case_setup_handler(source, index_of_case); +} + +utest::v1::status_t spm_case_teardown(const Case *const source, const size_t passed, const size_t failed, const failure_t reason) +{ + psa_error_t status = PSA_SUCCESS; + psa_error_t test_status = PSA_SUCCESS; + test_action_t action = GET_TEST_RESULT; + psa_invec_t data = {&action, sizeof(action)}; + psa_outvec_t resp = {&test_status, sizeof(test_status)}; + + // Wait for psa_close to finish on server side + osDelay(50); + + status = psa_call(control_handle, &data, 1, &resp, 1); + TEST_ASSERT_EQUAL(PSA_SUCCESS, status); + TEST_ASSERT_EQUAL(PSA_SUCCESS, test_status); + return greentea_case_teardown_handler(source, passed, failed, reason); +} + +#define SPM_UTEST_CASE(desc, test) Case(desc, spm_case_setup, PSA_TEST_CLIENT_NAME(test), spm_case_teardown) + +Case cases[] = { + SPM_UTEST_CASE("Wait invalid time", wait_timeout), + SPM_UTEST_CASE("Get identity during connect", identity_during_connect), + SPM_UTEST_CASE("Get identity during call", identity_during_call), + SPM_UTEST_CASE("Assert msg size", msg_size_assertion), + SPM_UTEST_CASE("Reject on connect", reject_connection), + SPM_UTEST_CASE("Read at an out of bound offset", read_at_outofboud_offset), + SPM_UTEST_CASE("Read msg with size bigger than message", msg_read_truncation), + SPM_UTEST_CASE("Make sure skip with 0 byte number skips nothing", skip_zero), + SPM_UTEST_CASE("Skip a few bytes while reading a message", skip_some), + SPM_UTEST_CASE("Try to skip more bytes than left while reading", skip_more_than_left), + SPM_UTEST_CASE("Test rhandle implementation by calculating the factorial function", rhandle_factorial), + SPM_UTEST_CASE("Test a call flow between 2 secure partitions", cross_partition_call), + SPM_UTEST_CASE("Test a common DOORBELL scenario", doorbell_test), +}; + +//Declare your test specification with a custom setup handler +Specification specification(spm_setup, cases, spm_teardown); + +int main(int, char**) +{ + Harness::run(specification); + return 0; +} diff --git a/TESTS/psa/spm_server/COMPONENT_SPE/partition.c b/TESTS/psa/spm_server/COMPONENT_SPE/partition.c new file mode 100644 index 0000000..1fd7a91 --- /dev/null +++ b/TESTS/psa/spm_server/COMPONENT_SPE/partition.c @@ -0,0 +1,88 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "string.h" +#include "spm_server.h" +#include "spm_panic.h" +#include "psa_server_test_part1_partition.h" +#include "server_tests.h" + +extern psa_test_server_side_func test_list[]; +static size_t num_of_tests = 0; +static psa_msg_t msg = {0}; +static void init_num_of_tests() +{ + size_t i = 0; + while(test_list[i] != NULL) { + i++; + } + + num_of_tests = i; +} + +void part1_main(void *ptr) +{ + uint32_t signals = 0; + psa_error_t test_status = PSA_SUCCESS; // status of the api calls during the test + psa_error_t test_result = PSA_SUCCESS; // result of the critical section of the test + test_action_t action; + uint32_t test_idx = 0; + + init_num_of_tests(); + while (1) { + signals = psa_wait_any(PSA_BLOCK); + if (0 == (signals & CONTROL_MSK)) { + SPM_PANIC("returned from psa_wait_any without CONTROL_ROT_SRV bit on signals=(0x%08x)\n", signals); + } + + psa_get(CONTROL_MSK, &msg); + switch (msg.type) { + case PSA_IPC_CALL: + if (msg.in_size[0] == 0) { + SPM_PANIC("got a zero message size to CONTROL ROT_SRV\n"); + } + + if (psa_read(msg.handle, 0, &action, sizeof(action)) != sizeof(action)) { + SPM_PANIC("could not read the entire test payload structure\n"); + } + + switch (action) { + case START_TEST: + if ((test_idx >= num_of_tests) || (test_list[test_idx] == NULL)) { + SPM_PANIC("Invalid test ID was sent!\n"); + } + + psa_reply(msg.handle, PSA_SUCCESS); + test_status = test_list[test_idx](&test_result); + break; + case GET_TEST_RESULT: + test_idx++; + psa_write(msg.handle, 0, &test_result, sizeof(test_result)); + psa_reply(msg.handle, test_status); + break; + default: + SPM_PANIC("Got illegal Value in test action"); + } + + break; + case PSA_IPC_CONNECT: + case PSA_IPC_DISCONNECT: + psa_reply(msg.handle, PSA_SUCCESS); + break; + default: + SPM_PANIC("Unexpected message type %d!", (int)(msg.type)); + } + } +} diff --git a/TESTS/psa/spm_server/COMPONENT_SPE/partition2.c b/TESTS/psa/spm_server/COMPONENT_SPE/partition2.c new file mode 100644 index 0000000..8c23c35 --- /dev/null +++ b/TESTS/psa/spm_server/COMPONENT_SPE/partition2.c @@ -0,0 +1,100 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "cmsis_os2.h" +#include "spm_server.h" +#include "spm_panic.h" +#include "psa_server_test_part2_partition.h" + +static psa_msg_t msg = {0}; + +void part2_main(void *ptr) +{ + uint32_t signals = 0; + size_t len = 0; + char *str = NULL; + + while (1) { + signals = psa_wait_any(PSA_BLOCK); + if (0 == (signals & (ROT_SRV_REVERSE_MSK | ROT_SRV_DB_TST_MSK))) { + SPM_PANIC("returned from psa_wait_any without ROT_SRV_REVERSE_MSK or ROT_SRV_DB_TST_MSK bit on\n"); + } + if(signals & ROT_SRV_REVERSE_MSK) { + psa_get(ROT_SRV_REVERSE_MSK, &msg); + switch (msg.type) { + case PSA_IPC_CALL: + { + if ((msg.in_size[0] + msg.in_size[1] + msg.in_size[2]) == 0) { + SPM_PANIC("got a zero message size to REVERSE ROT_SRV\n"); + } + + len = msg.in_size[0]; + str = (char*)malloc(sizeof(char) * len); + if (NULL == str) { + SPM_PANIC("memory allocation failure\n"); + } + psa_read(msg.handle, 0, str, len); + for(size_t i = 0; i < len / 2; i ++) { + char a = str[i]; + str[i] = str[len - i - 1]; + str[len - i - 1] = a; + } + + psa_write(msg.handle, 0, str, len); + free(str); + str = NULL; + break; + } + case PSA_IPC_CONNECT: + case PSA_IPC_DISCONNECT: + break; + default: + SPM_PANIC("Unexpected message type %d!", (int)(msg.type)); + break; + } + + psa_reply(msg.handle, PSA_SUCCESS); + } + else { // -- Doorbell test + + psa_get(ROT_SRV_DB_TST_MSK, &msg); + switch (msg.type) { + case PSA_IPC_CALL: + { + int32_t caller_part_id = psa_identity(msg.handle); + // Doorbell contract is valid only between secure partitions + if (PSA_NSPE_IDENTIFIER == caller_part_id) { + SPM_PANIC("Caller partition is non secure\n"); + } + // In doorbell scenario the server first calls psa_reply() + psa_reply(msg.handle, PSA_SUCCESS); + // Then the servers waits to some driver making long calculations - imitate using osDelay() + osDelay(20); + // After work is done, ring the doorbell + psa_notify(caller_part_id); + break; + } + case PSA_IPC_CONNECT: + case PSA_IPC_DISCONNECT: + psa_reply(msg.handle, PSA_SUCCESS); + break; + default: + SPM_PANIC("Unexpected message type %d!", (int)(msg.type)); + break; + } + } + } +} diff --git a/TESTS/psa/spm_server/COMPONENT_SPE/psa_server_test_part1_ifs.h b/TESTS/psa/spm_server/COMPONENT_SPE/psa_server_test_part1_ifs.h new file mode 100644 index 0000000..e24a0d4 --- /dev/null +++ b/TESTS/psa/spm_server/COMPONENT_SPE/psa_server_test_part1_ifs.h @@ -0,0 +1,28 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#ifndef PSA_SERVER_TEST_PART1_PARTITION_ROT_SERVICES_H +#define PSA_SERVER_TEST_PART1_PARTITION_ROT_SERVICES_H + +#define CONTROL 0x00001A01 +#define TEST 0x00001A02 + +#endif // PSA_SERVER_TEST_PART1_PARTITION_ROT_SERVICES_H diff --git a/TESTS/psa/spm_server/COMPONENT_SPE/psa_server_test_part1_partition.c b/TESTS/psa/spm_server/COMPONENT_SPE/psa_server_test_part1_partition.c new file mode 100644 index 0000000..82c05a1 --- /dev/null +++ b/TESTS/psa/spm_server/COMPONENT_SPE/psa_server_test_part1_partition.c @@ -0,0 +1,116 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#include "cmsis.h" +#include "mbed_toolchain.h" /* For using MBED_ALIGN macro */ +#include "rtx_os.h" +#include "spm_panic.h" +#include "spm_internal.h" +#include "psa_server_test_part1_partition.h" +#include "psa_server_test_part1_ifs.h" +#include "psa_server_test_part2_ifs.h" + + +/* Threads stacks */ +MBED_ALIGN(8) uint8_t server_test_part1_thread_stack[1024] = {0}; + +/* Threads control blocks */ +osRtxThread_t server_test_part1_thread_cb = {0}; + +/* Thread attributes - for thread initialization */ +osThreadAttr_t server_test_part1_thread_attr = { + .name = "server_test_part1", + .attr_bits = 0, + .cb_mem = &server_test_part1_thread_cb, + .cb_size = sizeof(server_test_part1_thread_cb), + .stack_mem = server_test_part1_thread_stack, + .stack_size = 1024, + .priority = osPriorityNormal, + .tz_module = 0, + .reserved = 0 + }; + +spm_rot_service_t server_test_part1_rot_services[SERVER_TEST_PART1_ROT_SRV_COUNT] = { + { + .sid = CONTROL, + .mask = CONTROL_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, + { + .sid = TEST, + .mask = TEST_MSK, + .partition = NULL, + .min_version = 12, + .min_version_policy = PSA_MINOR_VERSION_POLICY_STRICT, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, +}; + +/* External SIDs used by SERVER_TEST_PART1 */ +const uint32_t server_test_part1_external_sids[2] = +{ + ROT_SRV_REVERSE, + ROT_SRV_DB_TST, +}; + +static osRtxMutex_t server_test_part1_mutex = {0}; +static const osMutexAttr_t server_test_part1_mutex_attr = { + .name = "server_test_part1_mutex", + .attr_bits = osMutexRecursive | osMutexPrioInherit | osMutexRobust, + .cb_mem = &server_test_part1_mutex, + .cb_size = sizeof(server_test_part1_mutex), +}; + + +extern void part1_main(void *ptr); + +void server_test_part1_init(spm_partition_t *partition) +{ + if (NULL == partition) { + SPM_PANIC("partition is NULL!\n"); + } + + partition->mutex = osMutexNew(&server_test_part1_mutex_attr); + if (NULL == partition->mutex) { + SPM_PANIC("Failed to create mutex for secure partition server_test_part1!\n"); + } + + for (uint32_t i = 0; i < SERVER_TEST_PART1_ROT_SRV_COUNT; ++i) { + server_test_part1_rot_services[i].partition = partition; + } + partition->rot_services = server_test_part1_rot_services; + + partition->thread_id = osThreadNew(part1_main, NULL, &server_test_part1_thread_attr); + if (NULL == partition->thread_id) { + SPM_PANIC("Failed to create start main thread of partition server_test_part1!\n"); + } +} diff --git a/TESTS/psa/spm_server/COMPONENT_SPE/psa_server_test_part1_partition.h b/TESTS/psa/spm_server/COMPONENT_SPE/psa_server_test_part1_partition.h new file mode 100644 index 0000000..5b45296 --- /dev/null +++ b/TESTS/psa/spm_server/COMPONENT_SPE/psa_server_test_part1_partition.h @@ -0,0 +1,55 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#ifndef PSA_SERVER_TEST_PART1_PARTITION_H +#define PSA_SERVER_TEST_PART1_PARTITION_H + +#define SERVER_TEST_PART1_ID 2 + +#define SERVER_TEST_PART1_ROT_SRV_COUNT (2UL) +#define SERVER_TEST_PART1_EXT_ROT_SRV_COUNT (2UL) + +/* SERVER_TEST_PART1 event flags */ +#define SERVER_TEST_PART1_RESERVED1_POS (1UL) +#define SERVER_TEST_PART1_RESERVED1_MSK (1UL << SERVER_TEST_PART1_RESERVED1_POS) + +#define SERVER_TEST_PART1_RESERVED2_POS (2UL) +#define SERVER_TEST_PART1_RESERVED2_MSK (1UL << SERVER_TEST_PART1_RESERVED2_POS) + + + +#define CONTROL_MSK_POS (4UL) +#define CONTROL_MSK (1UL << CONTROL_MSK_POS) +#define TEST_MSK_POS (5UL) +#define TEST_MSK (1UL << TEST_MSK_POS) + +#define SERVER_TEST_PART1_WAIT_ANY_SID_MSK (\ + CONTROL_MSK | \ + TEST_MSK) + +/* +#define SERVER_TEST_PART1_WAIT_ANY_MSK (\ + SERVER_TEST_PART1_WAIT_ANY_SID_MSK) | \ + PSA_DOORBELL) +*/ + + +#endif // PSA_SERVER_TEST_PART1_PARTITION_H diff --git a/TESTS/psa/spm_server/COMPONENT_SPE/psa_server_test_part2_ifs.h b/TESTS/psa/spm_server/COMPONENT_SPE/psa_server_test_part2_ifs.h new file mode 100644 index 0000000..e06c8f7 --- /dev/null +++ b/TESTS/psa/spm_server/COMPONENT_SPE/psa_server_test_part2_ifs.h @@ -0,0 +1,28 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#ifndef PSA_SERVER_TEST_PART2_PARTITION_ROT_SERVICES_H +#define PSA_SERVER_TEST_PART2_PARTITION_ROT_SERVICES_H + +#define ROT_SRV_REVERSE 0x00001A03 +#define ROT_SRV_DB_TST 0x00001A04 + +#endif // PSA_SERVER_TEST_PART2_PARTITION_ROT_SERVICES_H diff --git a/TESTS/psa/spm_server/COMPONENT_SPE/psa_server_test_part2_partition.c b/TESTS/psa/spm_server/COMPONENT_SPE/psa_server_test_part2_partition.c new file mode 100644 index 0000000..fbe2814 --- /dev/null +++ b/TESTS/psa/spm_server/COMPONENT_SPE/psa_server_test_part2_partition.c @@ -0,0 +1,109 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#include "cmsis.h" +#include "mbed_toolchain.h" /* For using MBED_ALIGN macro */ +#include "rtx_os.h" +#include "spm_panic.h" +#include "spm_internal.h" +#include "psa_server_test_part2_partition.h" +#include "psa_server_test_part2_ifs.h" + + +/* Threads stacks */ +MBED_ALIGN(8) uint8_t server_test_part2_thread_stack[1024] = {0}; + +/* Threads control blocks */ +osRtxThread_t server_test_part2_thread_cb = {0}; + +/* Thread attributes - for thread initialization */ +osThreadAttr_t server_test_part2_thread_attr = { + .name = "server_test_part2", + .attr_bits = 0, + .cb_mem = &server_test_part2_thread_cb, + .cb_size = sizeof(server_test_part2_thread_cb), + .stack_mem = server_test_part2_thread_stack, + .stack_size = 1024, + .priority = osPriorityNormal, + .tz_module = 0, + .reserved = 0 + }; + +spm_rot_service_t server_test_part2_rot_services[SERVER_TEST_PART2_ROT_SRV_COUNT] = { + { + .sid = ROT_SRV_REVERSE, + .mask = ROT_SRV_REVERSE_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_STRICT, + .allow_nspe = false, + .queue = { + .head = NULL, + .tail = NULL + } + }, + { + .sid = ROT_SRV_DB_TST, + .mask = ROT_SRV_DB_TST_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_STRICT, + .allow_nspe = false, + .queue = { + .head = NULL, + .tail = NULL + } + }, +}; + + +static osRtxMutex_t server_test_part2_mutex = {0}; +static const osMutexAttr_t server_test_part2_mutex_attr = { + .name = "server_test_part2_mutex", + .attr_bits = osMutexRecursive | osMutexPrioInherit | osMutexRobust, + .cb_mem = &server_test_part2_mutex, + .cb_size = sizeof(server_test_part2_mutex), +}; + + +extern void part2_main(void *ptr); + +void server_test_part2_init(spm_partition_t *partition) +{ + if (NULL == partition) { + SPM_PANIC("partition is NULL!\n"); + } + + partition->mutex = osMutexNew(&server_test_part2_mutex_attr); + if (NULL == partition->mutex) { + SPM_PANIC("Failed to create mutex for secure partition server_test_part2!\n"); + } + + for (uint32_t i = 0; i < SERVER_TEST_PART2_ROT_SRV_COUNT; ++i) { + server_test_part2_rot_services[i].partition = partition; + } + partition->rot_services = server_test_part2_rot_services; + + partition->thread_id = osThreadNew(part2_main, NULL, &server_test_part2_thread_attr); + if (NULL == partition->thread_id) { + SPM_PANIC("Failed to create start main thread of partition server_test_part2!\n"); + } +} diff --git a/TESTS/psa/spm_server/COMPONENT_SPE/psa_server_test_part2_partition.h b/TESTS/psa/spm_server/COMPONENT_SPE/psa_server_test_part2_partition.h new file mode 100644 index 0000000..371588c --- /dev/null +++ b/TESTS/psa/spm_server/COMPONENT_SPE/psa_server_test_part2_partition.h @@ -0,0 +1,55 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#ifndef PSA_SERVER_TEST_PART2_PARTITION_H +#define PSA_SERVER_TEST_PART2_PARTITION_H + +#define SERVER_TEST_PART2_ID 3 + +#define SERVER_TEST_PART2_ROT_SRV_COUNT (2UL) +#define SERVER_TEST_PART2_EXT_ROT_SRV_COUNT (0UL) + +/* SERVER_TEST_PART2 event flags */ +#define SERVER_TEST_PART2_RESERVED1_POS (1UL) +#define SERVER_TEST_PART2_RESERVED1_MSK (1UL << SERVER_TEST_PART2_RESERVED1_POS) + +#define SERVER_TEST_PART2_RESERVED2_POS (2UL) +#define SERVER_TEST_PART2_RESERVED2_MSK (1UL << SERVER_TEST_PART2_RESERVED2_POS) + + + +#define ROT_SRV_REVERSE_MSK_POS (4UL) +#define ROT_SRV_REVERSE_MSK (1UL << ROT_SRV_REVERSE_MSK_POS) +#define ROT_SRV_DB_TST_MSK_POS (5UL) +#define ROT_SRV_DB_TST_MSK (1UL << ROT_SRV_DB_TST_MSK_POS) + +#define SERVER_TEST_PART2_WAIT_ANY_SID_MSK (\ + ROT_SRV_REVERSE_MSK | \ + ROT_SRV_DB_TST_MSK) + +/* +#define SERVER_TEST_PART2_WAIT_ANY_MSK (\ + SERVER_TEST_PART2_WAIT_ANY_SID_MSK) | \ + PSA_DOORBELL) +*/ + + +#endif // PSA_SERVER_TEST_PART2_PARTITION_H diff --git a/TESTS/psa/spm_server/COMPONENT_SPE/psa_setup.c b/TESTS/psa/spm_server/COMPONENT_SPE/psa_setup.c new file mode 100644 index 0000000..3c8e946 --- /dev/null +++ b/TESTS/psa/spm_server/COMPONENT_SPE/psa_setup.c @@ -0,0 +1,93 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#include "spm_panic.h" +#include "spm_internal.h" +#include "handles_manager.h" +#include "cmsis.h" +#include "psa_server_test_part1_partition.h" +#include "psa_server_test_part2_partition.h" +#include "psa_its_partition.h" + +extern const uint32_t server_test_part1_external_sids[2]; + +spm_partition_t g_partitions[3] = { + { + .partition_id = SERVER_TEST_PART1_ID, + .thread_id = 0, + .flags_rot_srv = SERVER_TEST_PART1_WAIT_ANY_SID_MSK, + .flags_interrupts = 0, + .rot_services = NULL, + .rot_services_count = SERVER_TEST_PART1_ROT_SRV_COUNT, + .extern_sids = server_test_part1_external_sids, + .extern_sids_count = SERVER_TEST_PART1_EXT_ROT_SRV_COUNT, + .irq_mapper = NULL, + }, + { + .partition_id = SERVER_TEST_PART2_ID, + .thread_id = 0, + .flags_rot_srv = SERVER_TEST_PART2_WAIT_ANY_SID_MSK, + .flags_interrupts = 0, + .rot_services = NULL, + .rot_services_count = SERVER_TEST_PART2_ROT_SRV_COUNT, + .extern_sids = NULL, + .extern_sids_count = SERVER_TEST_PART2_EXT_ROT_SRV_COUNT, + .irq_mapper = NULL, + }, + { + .partition_id = ITS_ID, + .thread_id = 0, + .flags_rot_srv = ITS_WAIT_ANY_SID_MSK, + .flags_interrupts = 0, + .rot_services = NULL, + .rot_services_count = ITS_ROT_SRV_COUNT, + .extern_sids = NULL, + .extern_sids_count = ITS_EXT_ROT_SRV_COUNT, + .irq_mapper = NULL, + }, +}; + +/* Check all the defined memory regions for overlapping. */ + +/* A list of all the memory regions. */ +const mem_region_t *mem_regions = NULL; + +const uint32_t mem_region_count = 0; + +// forward declaration of partition initializers +void server_test_part1_init(spm_partition_t *partition); +void server_test_part2_init(spm_partition_t *partition); +void its_init(spm_partition_t *partition); + +uint32_t init_partitions(spm_partition_t **partitions) +{ + if (NULL == partitions) { + SPM_PANIC("partitions is NULL!\n"); + } + + server_test_part1_init(&(g_partitions[0])); + server_test_part2_init(&(g_partitions[1])); + its_init(&(g_partitions[2])); + + *partitions = g_partitions; + return 3; +} + diff --git a/TESTS/psa/spm_server/COMPONENT_SPE/server_tests.h b/TESTS/psa/spm_server/COMPONENT_SPE/server_tests.h new file mode 100644 index 0000000..2d0cb37 --- /dev/null +++ b/TESTS/psa/spm_server/COMPONENT_SPE/server_tests.h @@ -0,0 +1,56 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __UVISOR_MBED_SPM_SERVER_TESTS_H__ +#define __UVISOR_MBED_SPM_SERVER_TESTS_H__ + +typedef enum { + START_TEST = 1, + GET_TEST_RESULT = 2 +} test_action_t; + +typedef struct factorial_data{ + uint32_t count; + uint32_t val; +} factorial_data_t; + +typedef psa_error_t (*psa_test_server_side_func)(psa_error_t*); +#define PSA_TEST_ERROR (-1L) +#define PSA_TEST_CLIENT_NAME(name) psa_test_client_side_ ## name +#define PSA_TEST_SERVER_NAME(name) psa_test_server_side_ ## name + +#define PSA_TEST_CLIENT(name) void PSA_TEST_CLIENT_NAME(name) (void) +#define PSA_TEST_SERVER(name) psa_error_t PSA_TEST_SERVER_NAME(name) (psa_error_t* status_ptr) + +#define PSA_TEST(name) \ + PSA_TEST_CLIENT(name); \ + PSA_TEST_SERVER(name); \ + + +PSA_TEST(wait_timeout) +PSA_TEST(identity_during_connect) +PSA_TEST(identity_during_call) +PSA_TEST(get_msg_twice) +PSA_TEST(msg_size_assertion) +PSA_TEST(reject_connection) +PSA_TEST(read_at_outofboud_offset) +PSA_TEST(msg_read_truncation) +PSA_TEST(skip_zero) +PSA_TEST(skip_some) +PSA_TEST(skip_more_than_left) +PSA_TEST(rhandle_factorial) +PSA_TEST(cross_partition_call) +PSA_TEST(doorbell_test) +#endif /* __UVISOR_MBED_SPM_SERVER_TESTS_H__ */ diff --git a/TESTS/psa/spm_server/COMPONENT_SPE/tests.c b/TESTS/psa/spm_server/COMPONENT_SPE/tests.c new file mode 100644 index 0000000..7679d62 --- /dev/null +++ b/TESTS/psa/spm_server/COMPONENT_SPE/tests.c @@ -0,0 +1,679 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "string.h" +#include "spm_client.h" +#include "spm_server.h" +#include "spm_panic.h" +#include "psa_server_test_part1_partition.h" +#include "psa_server_test_part2_ifs.h" +#include "server_tests.h" + +/** + * Process a generic connect message to TEST ROT_SRV. + * @return PSA_SUCCESS or negative error code if failed. + */ +static psa_error_t process_connect_request(void) +{ + psa_error_t res = PSA_SUCCESS; + psa_msg_t msg = {0}; + uint32_t signals = psa_wait_any(PSA_BLOCK); + if ((signals & TEST_MSK) == 0) { + res = PSA_TEST_ERROR; + } + + psa_get(TEST_MSK, &msg); + if (msg.type != PSA_IPC_CONNECT) { + res = ((res != PSA_SUCCESS) ? res : PSA_TEST_ERROR); + } + + psa_reply(msg.handle, res); + + return res; +} + +/** + * Process a generic disconnect message to TEST ROT_SRV. + * @return PSA_SUCCESS or negative error code if failed. + */ +static psa_error_t process_disconnect_request(void) +{ + psa_error_t res = PSA_SUCCESS; + psa_msg_t msg = {0}; + uint32_t signals = psa_wait_any(PSA_BLOCK); + if ((signals & TEST_MSK) == 0) { + res = PSA_TEST_ERROR; + } + + psa_get(TEST_MSK, &msg); + if (msg.type != PSA_IPC_DISCONNECT) { + res = ((res != PSA_SUCCESS) ? res : PSA_TEST_ERROR); + } + + psa_reply(msg.handle, PSA_SUCCESS); + + return res; +} + +PSA_TEST_SERVER(wait_timeout) +{ + psa_error_t test_status = PSA_SUCCESS; + uint32_t signals = psa_wait_any(7); + *status_ptr = ((signals & TEST_MSK) == 0) ? PSA_SUCCESS : PSA_TEST_ERROR;; + + test_status = process_connect_request(); + if (test_status != PSA_SUCCESS) { + return test_status; + } + test_status = process_disconnect_request(); + if (test_status != PSA_SUCCESS) { + return test_status; + } + + return ((signals & TEST_MSK) == 0) ? PSA_SUCCESS : PSA_TEST_ERROR; +} + +PSA_TEST_SERVER(identity_during_connect) +{ + psa_error_t test_status = PSA_SUCCESS; + psa_error_t disconnect_status = PSA_SUCCESS; + psa_msg_t msg = {0}; + int32_t identity = 0; + + uint32_t signals = psa_wait_any(PSA_BLOCK); + if ((signals & TEST_MSK) == 0) { + test_status = PSA_TEST_ERROR; + } + + psa_get(TEST_MSK, &msg); + if (msg.type != PSA_IPC_CONNECT) { + test_status = ((test_status != PSA_SUCCESS) ? test_status : PSA_TEST_ERROR); + } + + identity = psa_identity(msg.handle); + *status_ptr = (identity == -1) ? PSA_SUCCESS : PSA_TEST_ERROR; + + psa_reply(msg.handle, PSA_SUCCESS); + + disconnect_status = process_disconnect_request(); + test_status = (test_status != PSA_SUCCESS) ? test_status : disconnect_status; + + return test_status; +} + +PSA_TEST_SERVER(identity_during_call) +{ + psa_error_t test_status = PSA_SUCCESS; + psa_error_t disconnect_status = PSA_SUCCESS; + psa_msg_t msg = {0}; + int32_t identity = 0; + uint32_t signals = 0; + + test_status = process_connect_request(); + if (test_status != PSA_SUCCESS) { + return test_status; + } + + signals = psa_wait_any(PSA_BLOCK); + if ((signals & TEST_MSK) == 0) { + test_status = PSA_TEST_ERROR; + } + + psa_get(TEST_MSK, &msg); + if (msg.type != PSA_IPC_CALL) { + test_status = ((test_status != PSA_SUCCESS) ? test_status : PSA_TEST_ERROR); + } + + identity = psa_identity(msg.handle); + *status_ptr = (identity == -1) ? PSA_SUCCESS : PSA_TEST_ERROR; + + psa_reply(msg.handle, PSA_SUCCESS); + + disconnect_status = process_disconnect_request(); + test_status = (test_status != PSA_SUCCESS) ? test_status : disconnect_status; + + return test_status; +} + +PSA_TEST_SERVER(msg_size_assertion) +{ + psa_error_t test_status = PSA_SUCCESS; + psa_error_t disconnect_status = PSA_SUCCESS; + psa_msg_t msg = {0}; + uint32_t signals = 0; + size_t read_size = 0; + + char *buff = malloc(sizeof(char) * 11); + if (NULL == buff) { + SPM_PANIC("memory allocation failure\n"); + } + memset(buff, 0, 11); + + test_status = process_connect_request(); + if (test_status != PSA_SUCCESS) { + free(buff); + return test_status; + } + + signals = psa_wait_any(PSA_BLOCK); + if ((signals & TEST_MSK) == 0) { + test_status = PSA_TEST_ERROR; + } + + psa_get(TEST_MSK, &msg); + if (msg.type != PSA_IPC_CALL) { + test_status = ((test_status != PSA_SUCCESS) ? test_status : PSA_TEST_ERROR); + } + for (size_t i = 0; i < PSA_MAX_IOVEC; i++) { + read_size += psa_read(msg.handle, i, buff + read_size, msg.in_size[i]); + } + + if (((msg.in_size[0] + msg.in_size[1] + msg.in_size[2] + msg.in_size[3]) != 11) || + (read_size != 11) || + (strncmp(buff, "abcdfghijkn", 11) != 0)) { + *status_ptr = PSA_TEST_ERROR; + } else { + *status_ptr = PSA_SUCCESS; + } + + psa_reply(msg.handle, test_status); + free(buff); + disconnect_status = process_disconnect_request(); + test_status = (test_status != PSA_SUCCESS) ? test_status : disconnect_status; + return test_status; +} + +PSA_TEST_SERVER(reject_connection) +{ + psa_error_t res = PSA_SUCCESS; + psa_msg_t msg = {0}; + uint32_t signals = psa_wait_any(PSA_BLOCK); + if ((signals & TEST_MSK) == 0) { + res = PSA_TEST_ERROR; + } + + psa_get(TEST_MSK, &msg); + if (msg.type != PSA_IPC_CONNECT) { + res = ((res != PSA_SUCCESS) ? res : PSA_TEST_ERROR); + } + + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + *status_ptr = res; + return res; +} + +PSA_TEST_SERVER(read_at_outofboud_offset) +{ + uint32_t signals = 0; + psa_msg_t msg = {0}; + psa_error_t test_status = PSA_SUCCESS; + psa_error_t disconnect_status = PSA_SUCCESS; + uint32_t buff = 52; + + test_status = process_connect_request(); + if (test_status != PSA_SUCCESS) { + return test_status; + } + + signals = psa_wait_any(PSA_BLOCK); + if ((signals & TEST_MSK) == 0) { + test_status = PSA_TEST_ERROR; + } + + psa_get(TEST_MSK, &msg); + if (msg.type != PSA_IPC_CALL) { + test_status = ((test_status != PSA_SUCCESS) ? test_status : PSA_TEST_ERROR); + } + + size_t read_size = psa_read(msg.handle, 1, &buff, sizeof(buff)); + if ((0 != read_size) || (52 != buff)){ + *status_ptr = PSA_TEST_ERROR; + } else { + *status_ptr = PSA_SUCCESS; + } + + psa_reply(msg.handle, test_status); + disconnect_status = process_disconnect_request(); + test_status = (test_status != PSA_SUCCESS) ? test_status : disconnect_status; + return test_status; +} + + +PSA_TEST_SERVER(msg_read_truncation) +{ + psa_error_t test_status = PSA_SUCCESS; + psa_error_t disconnect_status = PSA_SUCCESS; + psa_msg_t msg = {0}; + uint32_t signals = 0; + size_t read_size = 0; + char *buff = malloc(sizeof(char) * 11); + if (NULL == buff) { + SPM_PANIC("memory allocation failure\n"); + } + memset(buff, 0, 11); + + test_status = process_connect_request(); + if (test_status != PSA_SUCCESS) { + free(buff); + return test_status; + } + + signals = psa_wait_any(PSA_BLOCK); + if ((signals & TEST_MSK) == 0) { + test_status = PSA_TEST_ERROR; + } + + psa_get(TEST_MSK, &msg); + if (msg.type != PSA_IPC_CALL) { + test_status = ((test_status != PSA_SUCCESS) ? test_status : PSA_TEST_ERROR); + } + + read_size = psa_read(msg.handle, 1, buff, 11); + if ((msg.in_size[1] != read_size) || + ((msg.in_size[0] + msg.in_size[1] + msg.in_size[2]) != 11) || + (buff[6] != 0) || + (strncmp(buff, "fghijk", 6) != 0)) { + *status_ptr = PSA_TEST_ERROR; + } else { + *status_ptr = PSA_SUCCESS; + } + + psa_reply(msg.handle, test_status); + disconnect_status = process_disconnect_request(); + free(buff); + test_status = (test_status != PSA_SUCCESS) ? test_status : disconnect_status; + return test_status; +} + +PSA_TEST_SERVER(skip_zero) +{ + psa_error_t test_status = PSA_SUCCESS; + psa_error_t disconnect_status = PSA_SUCCESS; + psa_msg_t msg = {0}; + uint32_t signals = 0; + size_t read_size = 0; + size_t skip_size = 0; + char *buff = malloc(sizeof(char) * 11); + if (NULL == buff) { + SPM_PANIC("memory allocation failure\n"); + } + + test_status = process_connect_request(); + if (test_status != PSA_SUCCESS) { + free(buff); + return test_status; + } + + signals = psa_wait_any(PSA_BLOCK); + if ((signals & TEST_MSK) == 0) { + test_status = PSA_TEST_ERROR; + } + + psa_get(TEST_MSK, &msg); + if (msg.type != PSA_IPC_CALL) { + test_status = ((test_status != PSA_SUCCESS) ? test_status : PSA_TEST_ERROR); + } + + skip_size = psa_skip(msg.handle, 0, 0); + read_size = psa_read(msg.handle, 0, buff, 11); + if ((skip_size != 0) || + (read_size != 11) || + (strncmp(buff, "abcdefghijk", 11)) != 0) { + *status_ptr = PSA_TEST_ERROR; + } else { + *status_ptr = PSA_SUCCESS; + } + + psa_reply(msg.handle, test_status); + disconnect_status = process_disconnect_request(); + free(buff); + test_status = (test_status != PSA_SUCCESS) ? test_status : disconnect_status; + return test_status; +} + +PSA_TEST_SERVER(skip_some) +{ + psa_error_t test_status = PSA_SUCCESS; + psa_error_t disconnect_status = PSA_SUCCESS; + psa_msg_t msg = {0}; + uint32_t signals = 0; + size_t read_size1 = 0; + size_t read_size2 = 0; + size_t skip_size = 0; + char *buff = malloc(sizeof(char) * 11); + if (NULL == buff) { + SPM_PANIC("memory allocation failure\n"); + } + + test_status = process_connect_request(); + if (test_status != PSA_SUCCESS) { + free(buff); + return test_status; + } + + signals = psa_wait_any(PSA_BLOCK); + if ((signals & TEST_MSK) == 0) { + test_status = PSA_TEST_ERROR; + } + + psa_get(TEST_MSK, &msg); + if (msg.type != PSA_IPC_CALL) { + test_status = ((test_status != PSA_SUCCESS) ? test_status : PSA_TEST_ERROR); + } + + read_size1 = psa_read(msg.handle, 0, buff, 3); + skip_size = psa_skip(msg.handle, 0, 5); + read_size2 = psa_read(msg.handle, 0, buff + 3, 8); + if ((read_size1 != 3) || + (skip_size != 5) || + (read_size2 != 8) || + (strncmp(buff, "abcijklmnop", 11) != 0)) { + *status_ptr = PSA_TEST_ERROR; + } else { + *status_ptr = PSA_SUCCESS; + } + + psa_reply(msg.handle, test_status); + disconnect_status = process_disconnect_request(); + free(buff); + test_status = (test_status != PSA_SUCCESS) ? test_status : disconnect_status; + return test_status; +} + +PSA_TEST_SERVER(skip_more_than_left) +{ + psa_error_t test_status = PSA_SUCCESS; + psa_error_t disconnect_status = PSA_SUCCESS; + psa_msg_t msg = {0}; + uint32_t signals = 0; + size_t read_size1 = 0; + size_t read_size2 = 0; + size_t skip_size = 0; + char *buff = malloc(sizeof(char) * 8); + if (NULL == buff) { + SPM_PANIC("memory allocation failure\n"); + } + + test_status = process_connect_request(); + if (test_status != PSA_SUCCESS) { + free(buff); + return test_status; + } + + signals = psa_wait_any(PSA_BLOCK); + if ((signals & TEST_MSK) == 0) { + test_status = PSA_TEST_ERROR; + } + + psa_get(TEST_MSK, &msg); + if (msg.type != PSA_IPC_CALL) { + test_status = ((test_status != PSA_SUCCESS) ? test_status : PSA_TEST_ERROR); + } + + read_size1 = psa_read(msg.handle, 0, buff, 5); + skip_size = psa_skip(msg.handle, 0, 4); + read_size2 = psa_read(msg.handle, 0, buff + 5, 2); + if ((read_size1 != 5) || + (skip_size != 3) || + (read_size2 != 0) || + (strncmp(buff, "abcde", 5) != 0)) { + *status_ptr = PSA_TEST_ERROR; + } else { + *status_ptr = PSA_SUCCESS; + } + + psa_reply(msg.handle, test_status); + disconnect_status = process_disconnect_request(); + free(buff); + test_status = (test_status != PSA_SUCCESS) ? test_status : disconnect_status; + return test_status; +} + +PSA_TEST_SERVER(rhandle_factorial) +{ + uint32_t signals = 0; + psa_msg_t msg = {0}; + factorial_data_t *num = NULL; + factorial_data_t *asserted_ptr = NULL; + uint32_t connect_count = 0; + uint32_t call_count = 0; + uint32_t disconnect_count = 0; + + while (1) { + signals = psa_wait_any(PSA_BLOCK); + if (0 == (signals & TEST_MSK)) { + SPM_PANIC("returned from psa_wait_any without ROT_SRV_FACTORIAL bit on\n"); + } + + psa_get(TEST_MSK, &msg); + switch (msg.type) { + case PSA_IPC_CONNECT: + if (NULL != msg.rhandle){ + SPM_PANIC("got rhandle on connect message\n"); + } + + num = (factorial_data_t *)malloc(sizeof(factorial_data_t)); + if (NULL == num) { + SPM_PANIC("memory allocation failure\n"); + } + num->count = 0; + num->val = 1; + psa_set_rhandle(msg.handle, num); + asserted_ptr = num; + connect_count++; + break; + case PSA_IPC_CALL: + if (msg.in_size[0] + msg.in_size[1] + msg.in_size[2] > 0) { + SPM_PANIC("ROT_SRV_FACTORIAL ROT_SRV should not get any params\n"); + } + + if (NULL == msg.rhandle){ + SPM_PANIC("got NULL rhandle on call message\n"); + } + + if (asserted_ptr != msg.rhandle){ + SPM_PANIC("rhandle value changed between calls\n"); + } + + num = (factorial_data_t *)msg.rhandle; + num->count++; + num->val *= num->count; + psa_write(msg.handle, 0, &(num->val), sizeof(num->val)); + call_count++; + break; + case PSA_IPC_DISCONNECT: + if (NULL == msg.rhandle){ + SPM_PANIC("got NULL rhandle on disconnect message\n"); + } + + if (asserted_ptr != msg.rhandle){ + SPM_PANIC("rhandle value changed between calls\n"); + } + + // Setting rhandle during disconnection should have no effect + uint8_t my_rhandle = 10; + psa_set_rhandle(msg.handle, &my_rhandle); + + free(msg.rhandle); + disconnect_count++; + break; + default: + SPM_PANIC("Unexpected message type %d!", (int)(msg.type)); + } + + num = NULL; + psa_reply(msg.handle, PSA_SUCCESS); + if (disconnect_count > 0) { + break; + } + } + + if ((connect_count != 1) || + (call_count != 5) || + (disconnect_count != 1)) { + *status_ptr = PSA_TEST_ERROR; + } else { + *status_ptr = PSA_SUCCESS; + } + + return *status_ptr; +} + +PSA_TEST_SERVER(cross_partition_call) +{ + uint32_t signals = 0; + psa_msg_t msg = {0}; + psa_error_t test_status = PSA_SUCCESS; + psa_error_t disconnect_status = PSA_SUCCESS; + psa_error_t partition_call_status = PSA_SUCCESS; + uint32_t data_read = 0; + uint32_t str_len = 0; + char *buff = malloc(sizeof(char) * 60); + + if (NULL == buff) { + SPM_PANIC("memory allocation failure\n"); + } + + memset(buff, 0, 60); + + test_status = process_connect_request(); + if (test_status != PSA_SUCCESS) { + free(buff); + return test_status; + } + + signals = psa_wait_any(PSA_BLOCK); + if ((signals & TEST_MSK) == 0) { + test_status = PSA_TEST_ERROR; + } + + psa_get(TEST_MSK, &msg); + if ((msg.in_size[0] + msg.in_size[1] + msg.in_size[2]) == 0) { + test_status = ((test_status != PSA_SUCCESS) ? test_status : PSA_TEST_ERROR); + } + + str_len = msg.in_size[0]; + data_read = psa_read(msg.handle, 0, buff, str_len); + if (data_read != 21) { + test_status = ((test_status != PSA_SUCCESS) ? test_status : PSA_TEST_ERROR); + } + + memcpy(buff + str_len, buff, str_len); + data_read *= 2; + + psa_invec_t data = { buff, data_read }; + + psa_outvec_t resp = { buff, data_read }; + psa_handle_t conn_handle = psa_connect(ROT_SRV_REVERSE, 5); + if (conn_handle <= 0) { + partition_call_status = PSA_TEST_ERROR; + } + + if (partition_call_status == PSA_SUCCESS) { + partition_call_status = psa_call(conn_handle, &data, 1, &resp, 1); + } + + *status_ptr = partition_call_status; + if (partition_call_status == PSA_SUCCESS) { + psa_close(conn_handle); + } + + if (PSA_SUCCESS == partition_call_status) { + psa_write(msg.handle, 0, buff, data_read); + } + + psa_reply(msg.handle, partition_call_status); + free(buff); + disconnect_status = process_disconnect_request(); + test_status = (test_status != PSA_SUCCESS) ? test_status : disconnect_status; + return test_status; +} + +// Test a common DOORBELL scenario +PSA_TEST_SERVER(doorbell_test) +{ + uint32_t signals = 0; + psa_msg_t msg = {0}; + psa_error_t test_status = PSA_SUCCESS; + psa_error_t disconnect_status = PSA_SUCCESS; + psa_error_t partition_call_status = PSA_SUCCESS; + + + test_status = process_connect_request(); + if (test_status != PSA_SUCCESS) { + return test_status; + } + + signals = psa_wait_any(PSA_BLOCK); + if ((signals & TEST_MSK) == 0) { + test_status = PSA_TEST_ERROR; + } + + psa_get(TEST_MSK, &msg); + if (((msg.in_size[0] + msg.in_size[1] + msg.in_size[2]) != 0) || (msg.out_size[0] != 0)) { + test_status = ((test_status != PSA_SUCCESS) ? test_status : PSA_TEST_ERROR); + } + + // -- Connection with partition2 - START + psa_handle_t conn_handle = psa_connect(ROT_SRV_DB_TST, 5); + if (conn_handle <= 0) { + partition_call_status = PSA_TEST_ERROR; + } + + if (partition_call_status == PSA_SUCCESS) { + partition_call_status = psa_call(conn_handle, NULL, 0, NULL, 0); + } + + if (partition_call_status == PSA_SUCCESS) { + // Wait for doorball notification - Only after that call psa_reply() for the client called you + signals = psa_wait_interrupt(PSA_DOORBELL, PSA_BLOCK); + if ((signals & PSA_DOORBELL) == 0) { + partition_call_status = PSA_TEST_ERROR; + } + } + + if (partition_call_status == PSA_SUCCESS) { + psa_clear(); + psa_close(conn_handle); + } + // -- Connection with partition2 - END + + *status_ptr = partition_call_status; + + psa_reply(msg.handle, partition_call_status); + disconnect_status = process_disconnect_request(); + + test_status = (test_status != PSA_SUCCESS) ? test_status : disconnect_status; + + return test_status; +} + + +psa_test_server_side_func test_list[] = { + PSA_TEST_SERVER_NAME(wait_timeout), + PSA_TEST_SERVER_NAME(identity_during_connect), + PSA_TEST_SERVER_NAME(identity_during_call), + PSA_TEST_SERVER_NAME(msg_size_assertion), + PSA_TEST_SERVER_NAME(reject_connection), + PSA_TEST_SERVER_NAME(read_at_outofboud_offset), + PSA_TEST_SERVER_NAME(msg_read_truncation), + PSA_TEST_SERVER_NAME(skip_zero), + PSA_TEST_SERVER_NAME(skip_some), + PSA_TEST_SERVER_NAME(skip_more_than_left), + PSA_TEST_SERVER_NAME(rhandle_factorial), + PSA_TEST_SERVER_NAME(cross_partition_call), + PSA_TEST_SERVER_NAME(doorbell_test), + NULL +}; diff --git a/TESTS/psa/spm_server/part1_psa.json b/TESTS/psa/spm_server/part1_psa.json new file mode 100644 index 0000000..7112a1e --- /dev/null +++ b/TESTS/psa/spm_server/part1_psa.json @@ -0,0 +1,33 @@ +{ + "name": "SERVER_TEST_PART1", + "type": "APPLICATION-ROT", + "priority": "NORMAL", + "id": "0x00000002", + "entry_point": "part1_main", + "stack_size": "0x400", + "heap_size": "0x400", + "services": [{ + "name": "CONTROL", + "identifier": "0x00001A01", + "signal": "CONTROL_MSK", + "non_secure_clients": true, + "minor_version": 5, + "minor_policy": "RELAXED" + }, + { + "name": "TEST", + "identifier": "0x00001A02", + "signal": "TEST_MSK", + "non_secure_clients": true, + "minor_version": 12, + "minor_policy": "STRICT" + } + ], + "extern_sids": [ + "ROT_SRV_REVERSE", + "ROT_SRV_DB_TST" + ], + "source_files": [ + "COMPONENT_SPE/partition.c" + ] +} diff --git a/TESTS/psa/spm_server/part2_psa.json b/TESTS/psa/spm_server/part2_psa.json new file mode 100644 index 0000000..6ac95f5 --- /dev/null +++ b/TESTS/psa/spm_server/part2_psa.json @@ -0,0 +1,29 @@ +{ + "name": "SERVER_TEST_PART2", + "type": "APPLICATION-ROT", + "priority": "NORMAL", + "id": "0x00000003", + "entry_point": "part2_main", + "stack_size": "0x400", + "heap_size": "0x400", + "services": [{ + "name": "ROT_SRV_REVERSE", + "identifier": "0x00001A03", + "signal": "ROT_SRV_REVERSE_MSK", + "non_secure_clients": false, + "minor_version": 5, + "minor_policy": "STRICT" + }, + { + "name": "ROT_SRV_DB_TST", + "identifier": "0x00001A04", + "signal": "ROT_SRV_DB_TST_MSK", + "non_secure_clients": false, + "minor_version": 5, + "minor_policy": "STRICT" + } + ], + "source_files": [ + "COMPONENT_SPE/partition2.c" + ] +} diff --git a/TESTS/psa/spm_server/psa_server_test_part1_ifs.h b/TESTS/psa/spm_server/psa_server_test_part1_ifs.h new file mode 100644 index 0000000..e24a0d4 --- /dev/null +++ b/TESTS/psa/spm_server/psa_server_test_part1_ifs.h @@ -0,0 +1,28 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#ifndef PSA_SERVER_TEST_PART1_PARTITION_ROT_SERVICES_H +#define PSA_SERVER_TEST_PART1_PARTITION_ROT_SERVICES_H + +#define CONTROL 0x00001A01 +#define TEST 0x00001A02 + +#endif // PSA_SERVER_TEST_PART1_PARTITION_ROT_SERVICES_H diff --git a/TESTS/psa/spm_server/psa_server_test_part2_ifs.h b/TESTS/psa/spm_server/psa_server_test_part2_ifs.h new file mode 100644 index 0000000..e06c8f7 --- /dev/null +++ b/TESTS/psa/spm_server/psa_server_test_part2_ifs.h @@ -0,0 +1,28 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#ifndef PSA_SERVER_TEST_PART2_PARTITION_ROT_SERVICES_H +#define PSA_SERVER_TEST_PART2_PARTITION_ROT_SERVICES_H + +#define ROT_SRV_REVERSE 0x00001A03 +#define ROT_SRV_DB_TST 0x00001A04 + +#endif // PSA_SERVER_TEST_PART2_PARTITION_ROT_SERVICES_H diff --git a/TESTS/psa/spm_server/server_tests.h b/TESTS/psa/spm_server/server_tests.h new file mode 100644 index 0000000..2d0cb37 --- /dev/null +++ b/TESTS/psa/spm_server/server_tests.h @@ -0,0 +1,56 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __UVISOR_MBED_SPM_SERVER_TESTS_H__ +#define __UVISOR_MBED_SPM_SERVER_TESTS_H__ + +typedef enum { + START_TEST = 1, + GET_TEST_RESULT = 2 +} test_action_t; + +typedef struct factorial_data{ + uint32_t count; + uint32_t val; +} factorial_data_t; + +typedef psa_error_t (*psa_test_server_side_func)(psa_error_t*); +#define PSA_TEST_ERROR (-1L) +#define PSA_TEST_CLIENT_NAME(name) psa_test_client_side_ ## name +#define PSA_TEST_SERVER_NAME(name) psa_test_server_side_ ## name + +#define PSA_TEST_CLIENT(name) void PSA_TEST_CLIENT_NAME(name) (void) +#define PSA_TEST_SERVER(name) psa_error_t PSA_TEST_SERVER_NAME(name) (psa_error_t* status_ptr) + +#define PSA_TEST(name) \ + PSA_TEST_CLIENT(name); \ + PSA_TEST_SERVER(name); \ + + +PSA_TEST(wait_timeout) +PSA_TEST(identity_during_connect) +PSA_TEST(identity_during_call) +PSA_TEST(get_msg_twice) +PSA_TEST(msg_size_assertion) +PSA_TEST(reject_connection) +PSA_TEST(read_at_outofboud_offset) +PSA_TEST(msg_read_truncation) +PSA_TEST(skip_zero) +PSA_TEST(skip_some) +PSA_TEST(skip_more_than_left) +PSA_TEST(rhandle_factorial) +PSA_TEST(cross_partition_call) +PSA_TEST(doorbell_test) +#endif /* __UVISOR_MBED_SPM_SERVER_TESTS_H__ */ diff --git a/TESTS/psa/spm_smoke/COMPONENT_NSPE/main.cpp b/TESTS/psa/spm_smoke/COMPONENT_NSPE/main.cpp new file mode 100644 index 0000000..a7d78fa --- /dev/null +++ b/TESTS/psa/spm_smoke/COMPONENT_NSPE/main.cpp @@ -0,0 +1,90 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef COMPONENT_PSA_SRV_IPC +#error [NOT_SUPPORTED] SPM tests can run only on SPM-enabled targets +#endif // COMPONENT_PSA_SRV_IPC +/* -------------------------------------- Includes ----------------------------------- */ + +#include "greentea-client/test_env.h" +#include "unity.h" +#include "utest.h" +#include "spm_client.h" +#include "psa_smoke_test_part1_ifs.h" + +using namespace utest::v1; + +/* ------------------------------------ Definitions ---------------------------------- */ + +#define CLIENT_MINOR_VERSION 0 +#define CLIENT_RSP_BUF_SIZE 20 + +#define CLIENT_TX_MSG "Hello and welcome SPM" +#define CLIENT_EXPECTED_RESPONSE "Response1" + +/* ------------------------------------ Client Code ---------------------------------- */ + +char msg_buf[] = CLIENT_TX_MSG; + +void example_main(void) +{ + psa_handle_t conn_handle = psa_connect(ROT_SRV1, CLIENT_MINOR_VERSION); + TEST_ASSERT_MESSAGE(conn_handle > 0, "psa_connect() failed"); + + + psa_invec_t iovec[PSA_MAX_IOVEC - 1] = { + { msg_buf, 6 }, + { msg_buf + 6, 12 }, + { msg_buf + 18, 4 } + }; + + uint8_t *response_buf = (uint8_t*)malloc(sizeof(uint8_t) * CLIENT_RSP_BUF_SIZE); + memset(response_buf, 0, CLIENT_RSP_BUF_SIZE); + psa_outvec_t outvec = {response_buf, CLIENT_RSP_BUF_SIZE}; + + psa_error_t status = psa_call(conn_handle, iovec, PSA_MAX_IOVEC - 1, &outvec, 1); + TEST_ASSERT_MESSAGE(PSA_SUCCESS == status, "psa_call() failed"); + TEST_ASSERT_EQUAL_STRING(CLIENT_EXPECTED_RESPONSE, response_buf); + + free(response_buf); + psa_close(conn_handle); + + // Wait for psa_close to finish on server side + osDelay(50); +} + +// --------------------------------- Test Framework ---------------------------------- */ + +utest::v1::status_t greentea_setup(const size_t number_of_cases) { +#ifndef NO_GREENTEA + GREENTEA_SETUP(20, "default_auto"); +#endif + // Call the default reporting function + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("example", example_main) +}; + +// Declare your test specification with a custom setup handler +Specification specification(greentea_setup, cases); + +int main(int, char **) +{ // Run the test specification + Harness::run(specification); + return 0; +} diff --git a/TESTS/psa/spm_smoke/COMPONENT_SPE/partition1.c b/TESTS/psa/spm_smoke/COMPONENT_SPE/partition1.c new file mode 100644 index 0000000..022c456 --- /dev/null +++ b/TESTS/psa/spm_smoke/COMPONENT_SPE/partition1.c @@ -0,0 +1,107 @@ +// -------------------------------------- Includes ----------------------------------- + +#include + +#include "cmsis_os2.h" +#include "spm_server.h" +#include "spm_panic.h" +#include "psa_smoke_test_part1_partition.h" + +// ------------------------------------ Definitions ---------------------------------- + +#define SERVER_READ_MSG_BUF_SIZE 30 +#define SERVER_RSP_BUF_SIZE 20 +#define ACTUAL_MSG_SIZE 22 + +// ---------------------------------- Global Variables ------------------------------- + +const char SERVER_EXPECTED_READ_MSG[] = "Hello and welcome SPM"; +const char WRITE_MSG_BUF[] = "Response1"; + +// ------------------------------ Partition's Main Thread ---------------------------- + +void part1_main(void *ptr) +{ + uint32_t signals = 0; + int32_t client_id = 0; + psa_msg_t msg = {0}; + + while (1) { + + signals = psa_wait_any(PSA_BLOCK); + if ((signals & ROT_SRV1_MSK) != ROT_SRV1_MSK) { + SPM_PANIC("Received unknown signal (0x%08x)\n", signals); + } + + osDelay(500); + + psa_get(ROT_SRV1_MSK, &msg); + if (msg.handle != PSA_NULL_HANDLE) { + client_id = psa_identity(msg.handle); + if (client_id != PSA_NSPE_IDENTIFIER) { + SPM_PANIC("Received message from unexpected source (0x%08x)\n", client_id); + } + } + + switch (msg.type) { + case PSA_IPC_CALL: + { + if ( + ((msg.in_size[0] + msg.in_size[1] + msg.in_size[2] + msg.in_size[3]) != ACTUAL_MSG_SIZE) || + (msg.out_size[0] != SERVER_RSP_BUF_SIZE) + ) { + SPM_PANIC("Received message does not comply with message convention"); + } + + + char *read_msg_buf = malloc(sizeof(char) * SERVER_READ_MSG_BUF_SIZE); + if (NULL == read_msg_buf) { + SPM_PANIC("Failed to allocate Memory"); + } + memset(read_msg_buf, 0, SERVER_READ_MSG_BUF_SIZE); + char *read_ptr = read_msg_buf; + + for (size_t i = 0; i < PSA_MAX_IOVEC - 1; i++) { + uint32_t bytes_read = psa_read(msg.handle, i, read_ptr, msg.in_size[i]); + if (bytes_read != msg.in_size[i]) { + SPM_PANIC("Expected to read %d, got %d", msg.in_size[i], bytes_read); + } + + read_ptr += bytes_read; + } + + int cmp_res = strcmp(SERVER_EXPECTED_READ_MSG, read_msg_buf); + if(cmp_res != 0) { + SPM_PANIC("psa_read() - Bad reading!!"); + } + + psa_write(msg.handle, 0, WRITE_MSG_BUF, strlen(WRITE_MSG_BUF) + 1); + free(read_msg_buf); + read_msg_buf = NULL; + read_ptr = NULL; + break; + } + case PSA_IPC_DISCONNECT: + // Fallthrough + case PSA_IPC_CONNECT: + { + if ( + (msg.out_size[0] != 0) || (msg.out_size[1] != 0) || + (msg.out_size[2] != 0) || (msg.out_size[3] != 0) || + (msg.in_size[0] != 0) || (msg.in_size[1] != 0) || + (msg.in_size[2] != 0) || (msg.in_size[3] != 0) + ) { + SPM_PANIC("Should not receive iovecs in PSA_IPC_CONNECT or PSA_IPC_DISCONNECT"); + } + + break; + } + default: + SPM_PANIC("Unexpected message type %d!", (int)(msg.type)); + break; + } + + psa_reply(msg.handle, PSA_SUCCESS); + } +} + diff --git a/TESTS/psa/spm_smoke/COMPONENT_SPE/psa_setup.c b/TESTS/psa/spm_smoke/COMPONENT_SPE/psa_setup.c new file mode 100644 index 0000000..f52e0b0 --- /dev/null +++ b/TESTS/psa/spm_smoke/COMPONENT_SPE/psa_setup.c @@ -0,0 +1,78 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#include "spm_panic.h" +#include "spm_internal.h" +#include "handles_manager.h" +#include "cmsis.h" +#include "psa_smoke_test_part1_partition.h" +#include "psa_its_partition.h" + + +spm_partition_t g_partitions[2] = { + { + .partition_id = SMOKE_TEST_PART1_ID, + .thread_id = 0, + .flags_rot_srv = SMOKE_TEST_PART1_WAIT_ANY_SID_MSK, + .flags_interrupts = 0, + .rot_services = NULL, + .rot_services_count = SMOKE_TEST_PART1_ROT_SRV_COUNT, + .extern_sids = NULL, + .extern_sids_count = SMOKE_TEST_PART1_EXT_ROT_SRV_COUNT, + .irq_mapper = NULL, + }, + { + .partition_id = ITS_ID, + .thread_id = 0, + .flags_rot_srv = ITS_WAIT_ANY_SID_MSK, + .flags_interrupts = 0, + .rot_services = NULL, + .rot_services_count = ITS_ROT_SRV_COUNT, + .extern_sids = NULL, + .extern_sids_count = ITS_EXT_ROT_SRV_COUNT, + .irq_mapper = NULL, + }, +}; + +/* Check all the defined memory regions for overlapping. */ + +/* A list of all the memory regions. */ +const mem_region_t *mem_regions = NULL; + +const uint32_t mem_region_count = 0; + +// forward declaration of partition initializers +void smoke_test_part1_init(spm_partition_t *partition); +void its_init(spm_partition_t *partition); + +uint32_t init_partitions(spm_partition_t **partitions) +{ + if (NULL == partitions) { + SPM_PANIC("partitions is NULL!\n"); + } + + smoke_test_part1_init(&(g_partitions[0])); + its_init(&(g_partitions[1])); + + *partitions = g_partitions; + return 2; +} + diff --git a/TESTS/psa/spm_smoke/COMPONENT_SPE/psa_smoke_test_part1_ifs.h b/TESTS/psa/spm_smoke/COMPONENT_SPE/psa_smoke_test_part1_ifs.h new file mode 100644 index 0000000..d0a55a8 --- /dev/null +++ b/TESTS/psa/spm_smoke/COMPONENT_SPE/psa_smoke_test_part1_ifs.h @@ -0,0 +1,27 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#ifndef PSA_SMOKE_TEST_PART1_PARTITION_ROT_SERVICES_H +#define PSA_SMOKE_TEST_PART1_PARTITION_ROT_SERVICES_H + +#define ROT_SRV1 0x00001A00 + +#endif // PSA_SMOKE_TEST_PART1_PARTITION_ROT_SERVICES_H diff --git a/TESTS/psa/spm_smoke/COMPONENT_SPE/psa_smoke_test_part1_partition.c b/TESTS/psa/spm_smoke/COMPONENT_SPE/psa_smoke_test_part1_partition.c new file mode 100644 index 0000000..8a21b30 --- /dev/null +++ b/TESTS/psa/spm_smoke/COMPONENT_SPE/psa_smoke_test_part1_partition.c @@ -0,0 +1,97 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#include "cmsis.h" +#include "mbed_toolchain.h" /* For using MBED_ALIGN macro */ +#include "rtx_os.h" +#include "spm_panic.h" +#include "spm_internal.h" +#include "psa_smoke_test_part1_partition.h" +#include "psa_smoke_test_part1_ifs.h" + + +/* Threads stacks */ +MBED_ALIGN(8) uint8_t smoke_test_part1_thread_stack[512] = {0}; + +/* Threads control blocks */ +osRtxThread_t smoke_test_part1_thread_cb = {0}; + +/* Thread attributes - for thread initialization */ +osThreadAttr_t smoke_test_part1_thread_attr = { + .name = "smoke_test_part1", + .attr_bits = 0, + .cb_mem = &smoke_test_part1_thread_cb, + .cb_size = sizeof(smoke_test_part1_thread_cb), + .stack_mem = smoke_test_part1_thread_stack, + .stack_size = 512, + .priority = osPriorityNormal, + .tz_module = 0, + .reserved = 0 + }; + +spm_rot_service_t smoke_test_part1_rot_services[SMOKE_TEST_PART1_ROT_SRV_COUNT] = { + { + .sid = ROT_SRV1, + .mask = ROT_SRV1_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, +}; + + +static osRtxMutex_t smoke_test_part1_mutex = {0}; +static const osMutexAttr_t smoke_test_part1_mutex_attr = { + .name = "smoke_test_part1_mutex", + .attr_bits = osMutexRecursive | osMutexPrioInherit | osMutexRobust, + .cb_mem = &smoke_test_part1_mutex, + .cb_size = sizeof(smoke_test_part1_mutex), +}; + + +extern void part1_main(void *ptr); + +void smoke_test_part1_init(spm_partition_t *partition) +{ + if (NULL == partition) { + SPM_PANIC("partition is NULL!\n"); + } + + partition->mutex = osMutexNew(&smoke_test_part1_mutex_attr); + if (NULL == partition->mutex) { + SPM_PANIC("Failed to create mutex for secure partition smoke_test_part1!\n"); + } + + for (uint32_t i = 0; i < SMOKE_TEST_PART1_ROT_SRV_COUNT; ++i) { + smoke_test_part1_rot_services[i].partition = partition; + } + partition->rot_services = smoke_test_part1_rot_services; + + partition->thread_id = osThreadNew(part1_main, NULL, &smoke_test_part1_thread_attr); + if (NULL == partition->thread_id) { + SPM_PANIC("Failed to create start main thread of partition smoke_test_part1!\n"); + } +} diff --git a/TESTS/psa/spm_smoke/COMPONENT_SPE/psa_smoke_test_part1_partition.h b/TESTS/psa/spm_smoke/COMPONENT_SPE/psa_smoke_test_part1_partition.h new file mode 100644 index 0000000..9f594bf --- /dev/null +++ b/TESTS/psa/spm_smoke/COMPONENT_SPE/psa_smoke_test_part1_partition.h @@ -0,0 +1,52 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#ifndef PSA_SMOKE_TEST_PART1_PARTITION_H +#define PSA_SMOKE_TEST_PART1_PARTITION_H + +#define SMOKE_TEST_PART1_ID 4 + +#define SMOKE_TEST_PART1_ROT_SRV_COUNT (1UL) +#define SMOKE_TEST_PART1_EXT_ROT_SRV_COUNT (0UL) + +/* SMOKE_TEST_PART1 event flags */ +#define SMOKE_TEST_PART1_RESERVED1_POS (1UL) +#define SMOKE_TEST_PART1_RESERVED1_MSK (1UL << SMOKE_TEST_PART1_RESERVED1_POS) + +#define SMOKE_TEST_PART1_RESERVED2_POS (2UL) +#define SMOKE_TEST_PART1_RESERVED2_MSK (1UL << SMOKE_TEST_PART1_RESERVED2_POS) + + + +#define ROT_SRV1_MSK_POS (4UL) +#define ROT_SRV1_MSK (1UL << ROT_SRV1_MSK_POS) + +#define SMOKE_TEST_PART1_WAIT_ANY_SID_MSK (\ + ROT_SRV1_MSK) + +/* +#define SMOKE_TEST_PART1_WAIT_ANY_MSK (\ + SMOKE_TEST_PART1_WAIT_ANY_SID_MSK) | \ + PSA_DOORBELL) +*/ + + +#endif // PSA_SMOKE_TEST_PART1_PARTITION_H diff --git a/TESTS/psa/spm_smoke/part1_psa.json b/TESTS/psa/spm_smoke/part1_psa.json new file mode 100644 index 0000000..c4374d1 --- /dev/null +++ b/TESTS/psa/spm_smoke/part1_psa.json @@ -0,0 +1,21 @@ +{ + "name": "SMOKE_TEST_PART1", + "type": "APPLICATION-ROT", + "priority": "NORMAL", + "id": "0x00000004", + "entry_point": "part1_main", + "stack_size": "0x200", + "heap_size": "0x400", + "services": [{ + "name": "ROT_SRV1", + "identifier": "0x00001A00", + "signal": "ROT_SRV1_MSK", + "non_secure_clients": true, + "minor_version": 5, + "minor_policy": "RELAXED" + } + ], + "source_files": [ + "COMPONENT_SPE/partition1.c" + ] +} diff --git a/TESTS/psa/spm_smoke/psa_smoke_test_part1_ifs.h b/TESTS/psa/spm_smoke/psa_smoke_test_part1_ifs.h new file mode 100644 index 0000000..d0a55a8 --- /dev/null +++ b/TESTS/psa/spm_smoke/psa_smoke_test_part1_ifs.h @@ -0,0 +1,27 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#ifndef PSA_SMOKE_TEST_PART1_PARTITION_ROT_SERVICES_H +#define PSA_SMOKE_TEST_PART1_PARTITION_ROT_SERVICES_H + +#define ROT_SRV1 0x00001A00 + +#endif // PSA_SMOKE_TEST_PART1_PARTITION_ROT_SERVICES_H diff --git a/components/TARGET_PSA/services/psa_prot_internal_storage/COMPONENT_PSA_SRV_IPC/psa_prot_internal_storage.c b/components/TARGET_PSA/services/psa_prot_internal_storage/COMPONENT_PSA_SRV_IPC/psa_prot_internal_storage.c new file mode 100644 index 0000000..aa81d19 --- /dev/null +++ b/components/TARGET_PSA/services/psa_prot_internal_storage/COMPONENT_PSA_SRV_IPC/psa_prot_internal_storage.c @@ -0,0 +1,114 @@ +/* Copyright (c) 2018 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "spm_client.h" +#include "psa_prot_internal_storage.h" +#include "psa_its_ifs.h" + +psa_its_status_t psa_its_set(uint32_t uid, uint32_t data_length, const void *p_data, psa_its_create_flags_t create_flags) +{ + if (!p_data && data_length) { + return PSA_ITS_ERROR_BAD_POINTER; + } + + psa_invec_t msg[3] = { + { &uid, sizeof(uid) }, + { p_data, data_length }, + { &create_flags, sizeof(create_flags) } + }; + + psa_handle_t conn = psa_connect(PSA_ITS_SET, 1); + if (conn <= PSA_NULL_HANDLE) { + return PSA_ITS_ERROR_STORAGE_FAILURE; + } + + psa_error_t status = psa_call(conn, msg, 3, NULL, 0); + if (status == PSA_DROP_CONNECTION) { + status = PSA_ITS_ERROR_STORAGE_FAILURE; + } + + psa_close(conn); + return status; +} + +psa_its_status_t psa_its_get(uint32_t uid, uint32_t data_offset, uint32_t data_length, void *p_data) +{ + if (!p_data && data_length) { + return PSA_ITS_ERROR_BAD_POINTER; + } + + psa_invec_t msg[2] = { + { &uid, sizeof(uid) }, + { &data_offset, sizeof(data_offset) } + }; + psa_outvec_t resp = { p_data, data_length }; + + psa_handle_t conn = psa_connect(PSA_ITS_GET, 1); + if (conn <= PSA_NULL_HANDLE) { + return PSA_ITS_ERROR_STORAGE_FAILURE; + } + + psa_error_t status = psa_call(conn, msg, 2, &resp, 1); + + if (status == PSA_DROP_CONNECTION) { + status = PSA_ITS_ERROR_STORAGE_FAILURE; + } + + psa_close(conn); + return status; +} + +psa_its_status_t psa_its_get_info(uint32_t uid, struct psa_its_info_t *p_info) +{ + if (!p_info) { + return PSA_ITS_ERROR_BAD_POINTER; + } + + struct psa_its_info_t info = { 0 }; + psa_invec_t msg = { &uid, sizeof(uid) }; + psa_outvec_t resp = { &info, sizeof(info) }; + psa_handle_t conn = psa_connect(PSA_ITS_INFO, 1); + if (conn <= PSA_NULL_HANDLE) { + return PSA_ITS_ERROR_STORAGE_FAILURE; + } + + psa_error_t status = psa_call(conn, &msg, 1, &resp, 1); + + *p_info = info; + + if (status == PSA_DROP_CONNECTION) { + status = PSA_ITS_ERROR_STORAGE_FAILURE; + } + + psa_close(conn); + return status; +} + +psa_its_status_t psa_its_remove(uint32_t uid) +{ + psa_invec_t msg = { &uid, sizeof(uid) }; + psa_handle_t conn = psa_connect(PSA_ITS_REMOVE, 1); + if (conn <= PSA_NULL_HANDLE) { + return PSA_ITS_ERROR_STORAGE_FAILURE; + } + + psa_error_t status = psa_call(conn, &msg, 1, NULL, 0); + if (status == PSA_DROP_CONNECTION) { + status = PSA_ITS_ERROR_STORAGE_FAILURE; + } + + psa_close(conn); + return status; +} diff --git a/components/TARGET_PSA/services/psa_prot_internal_storage/COMPONENT_SPE/its_partition.c b/components/TARGET_PSA/services/psa_prot_internal_storage/COMPONENT_SPE/its_partition.c new file mode 100644 index 0000000..499ab26 --- /dev/null +++ b/components/TARGET_PSA/services/psa_prot_internal_storage/COMPONENT_SPE/its_partition.c @@ -0,0 +1,197 @@ +/* Copyright (c) 2018 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "cmsis_os2.h" +#include "spm_server.h" +#include "spm_panic.h" +#include "psa_its_partition.h" +#include "psa_prot_internal_storage.h" +#include "pits_impl.h" +#include "kv_config.h" +#include "mbed_error.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef psa_error_t (*SignalHandler)(psa_msg_t *); + +static psa_error_t storage_set(psa_msg_t *msg) +{ + uint32_t key = 0; + void *data = NULL; + uint32_t alloc_size = msg->in_size[1]; + psa_its_create_flags_t flags = 0; + + if ((msg->in_size[0] != sizeof(key)) || (msg->in_size[2] != sizeof(flags))) { + return PSA_DROP_CONNECTION; + } + + if (psa_read(msg->handle, 0, &key, sizeof(key)) != sizeof(key)) { + return PSA_DROP_CONNECTION; + } + + if (psa_read(msg->handle, 2, &flags, sizeof(flags)) != sizeof(flags)) { + return PSA_DROP_CONNECTION; + } + + data = malloc(alloc_size); + if (data == NULL) { + return PSA_ITS_ERROR_STORAGE_FAILURE; + } + + if (psa_read(msg->handle, 1, data, msg->in_size[1]) != msg->in_size[1]) { + free(data); + return PSA_ITS_ERROR_STORAGE_FAILURE; + } + + psa_its_status_t status = psa_its_set_impl(psa_identity(msg->handle), key, alloc_size, data, flags); + + memset(data, 0, alloc_size); + free(data); + return status; +} + +static psa_error_t storage_get(psa_msg_t *msg) +{ + uint32_t key = 0; + uint32_t offset = 0; + + if ((msg->in_size[0] != sizeof(key)) || (msg->in_size[1] != sizeof(offset))) { + return PSA_DROP_CONNECTION; + } + + if (psa_read(msg->handle, 0, &key, sizeof(key)) != sizeof(key)) { + return PSA_DROP_CONNECTION; + } + + if (psa_read(msg->handle, 1, &offset, sizeof(offset)) != sizeof(offset)) { + return PSA_DROP_CONNECTION; + } + + uint8_t *data = (uint8_t *)malloc(msg->out_size[0]); + if (data == NULL) { + return PSA_ITS_ERROR_STORAGE_FAILURE; + } + + psa_its_status_t status = psa_its_get_impl(psa_identity(msg->handle), key, offset, msg->out_size[0], data); + if (status == PSA_ITS_SUCCESS) { + psa_write(msg->handle, 0, data, msg->out_size[0]); + } + + memset(data, 0, msg->out_size[0]); + free(data); + return status; +} + +static psa_error_t storage_info(psa_msg_t *msg) +{ + struct psa_its_info_t info = { 0 }; + uint32_t key = 0; + + if ((msg->in_size[0] != sizeof(key)) || (msg->out_size[0] != sizeof(info))) { + return PSA_DROP_CONNECTION; + } + + if (psa_read(msg->handle, 0, &key, sizeof(key)) != sizeof(key)) { + return PSA_DROP_CONNECTION; + } + + psa_its_status_t status = psa_its_get_info_impl(psa_identity(msg->handle), key, &info); + if (status == PSA_ITS_SUCCESS) { + psa_write(msg->handle, 0, &info, msg->out_size[0]); + } + + return status; +} + +static psa_error_t storage_remove(psa_msg_t *msg) +{ + uint32_t key = 0; + + if (msg->in_size[0] != sizeof(key)) { + return PSA_DROP_CONNECTION; + } + + if (psa_read(msg->handle, 0, &key, sizeof(key)) != sizeof(key)) { + return PSA_DROP_CONNECTION; + } + + return psa_its_remove_impl(psa_identity(msg->handle), key); +} + +static void message_handler(psa_msg_t *msg, SignalHandler handler) +{ + psa_error_t status = PSA_SUCCESS; + switch (msg->type) { + case PSA_IPC_CONNECT: //fallthrough + case PSA_IPC_DISCONNECT: + { + break; + } + case PSA_IPC_CALL: + { + status = handler(msg); + break; + } + default: + { + SPM_PANIC("Unexpected message type %d!", (int)(msg->type)); + break; + } + } + psa_reply(msg->handle, status); +} + +void pits_entry(void *ptr) +{ + uint32_t signals = 0; + psa_msg_t msg = {0}; + + while (1) { + signals = psa_wait_any(PSA_BLOCK); + + // KVStore initiation: + // - Must be done after the psa_wait_any() call since only now we know OS initialization is done + // - Repeating calls has no effect + int kv_status = kv_init_storage_config(); + if(kv_status != MBED_SUCCESS) { + SPM_PANIC("KVStore initiation failed with status %d!", kv_status); + } + + if ((signals & PSA_ITS_SET_MSK) != 0) { + psa_get(PSA_ITS_SET_MSK, &msg); + message_handler(&msg, storage_set); + } + if ((signals & PSA_ITS_GET_MSK) != 0) { + psa_get(PSA_ITS_GET_MSK, &msg); + message_handler(&msg, storage_get); + } + if ((signals & PSA_ITS_INFO_MSK) != 0) { + psa_get(PSA_ITS_INFO_MSK, &msg); + message_handler(&msg, storage_info); + } + if ((signals & PSA_ITS_REMOVE_MSK) != 0) { + psa_get(PSA_ITS_REMOVE_MSK, &msg); + message_handler(&msg, storage_remove); + } + } +} + +#ifdef __cplusplus +} +#endif diff --git a/components/TARGET_PSA/services/psa_prot_internal_storage/COMPONENT_SPE/psa_its_partition.c b/components/TARGET_PSA/services/psa_prot_internal_storage/COMPONENT_SPE/psa_its_partition.c new file mode 100644 index 0000000..cce44eb --- /dev/null +++ b/components/TARGET_PSA/services/psa_prot_internal_storage/COMPONENT_SPE/psa_its_partition.c @@ -0,0 +1,133 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#include "cmsis.h" +#include "mbed_toolchain.h" /* For using MBED_ALIGN macro */ +#include "rtx_os.h" +#include "spm_panic.h" +#include "spm_internal.h" +#include "psa_its_partition.h" +#include "psa_its_ifs.h" + + +/* Threads stacks */ +MBED_ALIGN(8) uint8_t its_thread_stack[1024] = {0}; + +/* Threads control blocks */ +osRtxThread_t its_thread_cb = {0}; + +/* Thread attributes - for thread initialization */ +osThreadAttr_t its_thread_attr = { + .name = "its", + .attr_bits = 0, + .cb_mem = &its_thread_cb, + .cb_size = sizeof(its_thread_cb), + .stack_mem = its_thread_stack, + .stack_size = 1024, + .priority = osPriorityNormal, + .tz_module = 0, + .reserved = 0 + }; + +spm_rot_service_t its_rot_services[ITS_ROT_SRV_COUNT] = { + { + .sid = PSA_ITS_GET, + .mask = PSA_ITS_GET_MSK, + .partition = NULL, + .min_version = 1, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, + { + .sid = PSA_ITS_SET, + .mask = PSA_ITS_SET_MSK, + .partition = NULL, + .min_version = 1, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, + { + .sid = PSA_ITS_INFO, + .mask = PSA_ITS_INFO_MSK, + .partition = NULL, + .min_version = 1, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, + { + .sid = PSA_ITS_REMOVE, + .mask = PSA_ITS_REMOVE_MSK, + .partition = NULL, + .min_version = 1, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, +}; + + +static osRtxMutex_t its_mutex = {0}; +static const osMutexAttr_t its_mutex_attr = { + .name = "its_mutex", + .attr_bits = osMutexRecursive | osMutexPrioInherit | osMutexRobust, + .cb_mem = &its_mutex, + .cb_size = sizeof(its_mutex), +}; + + +extern void pits_entry(void *ptr); + +void its_init(spm_partition_t *partition) +{ + if (NULL == partition) { + SPM_PANIC("partition is NULL!\n"); + } + + partition->mutex = osMutexNew(&its_mutex_attr); + if (NULL == partition->mutex) { + SPM_PANIC("Failed to create mutex for secure partition its!\n"); + } + + for (uint32_t i = 0; i < ITS_ROT_SRV_COUNT; ++i) { + its_rot_services[i].partition = partition; + } + partition->rot_services = its_rot_services; + + partition->thread_id = osThreadNew(pits_entry, NULL, &its_thread_attr); + if (NULL == partition->thread_id) { + SPM_PANIC("Failed to create start main thread of partition its!\n"); + } +} diff --git a/components/TARGET_PSA/services/psa_prot_internal_storage/COMPONENT_SPE/psa_its_partition.h b/components/TARGET_PSA/services/psa_prot_internal_storage/COMPONENT_SPE/psa_its_partition.h new file mode 100644 index 0000000..8f6a93b --- /dev/null +++ b/components/TARGET_PSA/services/psa_prot_internal_storage/COMPONENT_SPE/psa_its_partition.h @@ -0,0 +1,61 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#ifndef PSA_ITS_PARTITION_H +#define PSA_ITS_PARTITION_H + +#define ITS_ID 10 + +#define ITS_ROT_SRV_COUNT (4UL) +#define ITS_EXT_ROT_SRV_COUNT (0UL) + +/* ITS event flags */ +#define ITS_RESERVED1_POS (1UL) +#define ITS_RESERVED1_MSK (1UL << ITS_RESERVED1_POS) + +#define ITS_RESERVED2_POS (2UL) +#define ITS_RESERVED2_MSK (1UL << ITS_RESERVED2_POS) + + + +#define PSA_ITS_GET_MSK_POS (4UL) +#define PSA_ITS_GET_MSK (1UL << PSA_ITS_GET_MSK_POS) +#define PSA_ITS_SET_MSK_POS (5UL) +#define PSA_ITS_SET_MSK (1UL << PSA_ITS_SET_MSK_POS) +#define PSA_ITS_INFO_MSK_POS (6UL) +#define PSA_ITS_INFO_MSK (1UL << PSA_ITS_INFO_MSK_POS) +#define PSA_ITS_REMOVE_MSK_POS (7UL) +#define PSA_ITS_REMOVE_MSK (1UL << PSA_ITS_REMOVE_MSK_POS) + +#define ITS_WAIT_ANY_SID_MSK (\ + PSA_ITS_GET_MSK | \ + PSA_ITS_SET_MSK | \ + PSA_ITS_INFO_MSK | \ + PSA_ITS_REMOVE_MSK) + +/* +#define ITS_WAIT_ANY_MSK (\ + ITS_WAIT_ANY_SID_MSK) | \ + PSA_DOORBELL) +*/ + + +#endif // PSA_ITS_PARTITION_H diff --git a/components/TARGET_PSA/services/psa_prot_internal_storage/pits_psa.json b/components/TARGET_PSA/services/psa_prot_internal_storage/pits_psa.json new file mode 100644 index 0000000..61fee6e --- /dev/null +++ b/components/TARGET_PSA/services/psa_prot_internal_storage/pits_psa.json @@ -0,0 +1,45 @@ +{ + "name": "ITS", + "type": "APPLICATION-ROT", + "priority": "NORMAL", + "id": "0x0000000A", + "entry_point": "pits_entry", + "stack_size": "0x400", + "heap_size": "0x400", + "services": [{ + "name": "PSA_ITS_GET", + "identifier": "0x00011A00", + "signal": "PSA_ITS_GET_MSK", + "non_secure_clients": true, + "minor_version": 1, + "minor_policy": "RELAXED" + }, + { + "name": "PSA_ITS_SET", + "identifier": "0x00011A01", + "signal": "PSA_ITS_SET_MSK", + "non_secure_clients": true, + "minor_version": 1, + "minor_policy": "RELAXED" + }, + { + "name": "PSA_ITS_INFO", + "identifier": "0x00011A02", + "signal": "PSA_ITS_INFO_MSK", + "non_secure_clients": true, + "minor_version": 1, + "minor_policy": "RELAXED" + }, + { + "name": "PSA_ITS_REMOVE", + "identifier": "0x00011A03", + "signal": "PSA_ITS_REMOVE_MSK", + "non_secure_clients": true, + "minor_version": 1, + "minor_policy": "RELAXED" + } + ], + "source_files": [ + "COMPONENT_SPE/its_partition.c" + ] + } diff --git a/components/TARGET_PSA/services/psa_prot_internal_storage/psa_its_ifs.h b/components/TARGET_PSA/services/psa_prot_internal_storage/psa_its_ifs.h new file mode 100644 index 0000000..7678469 --- /dev/null +++ b/components/TARGET_PSA/services/psa_prot_internal_storage/psa_its_ifs.h @@ -0,0 +1,30 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#ifndef PSA_ITS_PARTITION_ROT_SERVICES_H +#define PSA_ITS_PARTITION_ROT_SERVICES_H + +#define PSA_ITS_GET 0x00011A00 +#define PSA_ITS_SET 0x00011A01 +#define PSA_ITS_INFO 0x00011A02 +#define PSA_ITS_REMOVE 0x00011A03 + +#endif // PSA_ITS_PARTITION_ROT_SERVICES_H diff --git a/components/TARGET_PSA/spm/COMPONENT_SPE/handles_manager.c b/components/TARGET_PSA/spm/COMPONENT_SPE/handles_manager.c new file mode 100644 index 0000000..cd58770 --- /dev/null +++ b/components/TARGET_PSA/spm/COMPONENT_SPE/handles_manager.c @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2017, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* -------------------------------------- Includes ----------------------------------- */ + +#include "psa_defs.h" +#include "cmsis_os2.h" +#include "mbed_critical.h" +#include "spm_internal.h" +#include "spm_panic.h" +#include "handles_manager.h" + +#include +#include +#include + + + +/* ------------------------------------ Definitions ---------------------------------- */ + +#define PSA_HANDLE_MGR_HANDLE_INDEX_POS 16 +#define PSA_HANDLE_MGR_HANDLE_INDEX_MSK 0xFFFF + + + +/* -------------------------------- Handle Manager Module ---------------------------- */ + +/* The Handle Manager Module manages handles. + * + * It basically generates and exposes a unique handle identifier [handle] per + * handle memory [handle_mem] it receives from the user. + * Then users can use the exposed handle identifier to relate to the "registered" + * handle memory. + * + * Users can: + * - Ask for a unique handle identifier for a given handle memory [handle_create] + * - Ask for a pointer to the handle memory corresponding to a + * handle identifier [handle_get_mem] + * - Remove a handle from the handle manager module [handle_destroy] + * + * Note: + * Handles generation is done exclusively. + * Once we got a handle, removing a handle or getting its memory can be + * done non-exclusive. + * The assumption is that only one context is dealing with a handle after it was + * generated. + */ + +/* ------------------------------------- Functions ----------------------------------- */ + +/********************************************************************************************************************************** + * Function : psa_hndl_mgr_handle_create + * + * Description: This function generates a unique handle identifier, and "couples" it with the received handle memory. + * If there is no vacant space for the new handle, the function fails. + * + * Note: This function is expected to pass since it is always coupled with memory pool allocation of the same size. + * In case memory pool allocation fails, this function should not be called. + * This function will panic on non vacant space use case. + * + * Parameters : handle_mgr - [IN] A pointer to the handle manager object + * handle_mem - [IN] A pointer to a pre-allocated handle memory to get a handle identifier for + * friend_pid - [IN] The partition id which is allowed to get_mem() and destroy() in addition to the handle owner. + * Use PSA_HANDLE_MGR_INVALID_FRIEND_OWNER to denote there is no friend partition. + * + * Return : The created handle identifier + *********************************************************************************************************************************/ +psa_handle_t psa_hndl_mgr_handle_create(psa_handle_manager_t *handle_mgr, void *handle_mem, int32_t friend_pid) +{ + // Make sanity checks on arguments + SPM_ASSERT(handle_mgr != NULL); + SPM_ASSERT(handle_mem != NULL); + + // Get active partition id - Needed for requester identification + spm_partition_t *curr_part_ptr = get_active_partition(); + int32_t current_pid = ((curr_part_ptr != NULL) ? curr_part_ptr->partition_id : PSA_NSPE_IDENTIFIER); + uint32_t expected = UINT16_MAX; + + // Avoid passing UINT16_MAX. Start again from 0 if reached. + // The reason for this is that we use the 16 upper bits to store the handle's index in the handles pool (for performance reasons) + core_util_atomic_cas_u32( (uint32_t *)( &(handle_mgr->handle_generator) ), + &expected, + PSA_HANDLE_MGR_INVALID_HANDLE + ); + + // Generate a new handle identifier + uint32_t tmp_handle = core_util_atomic_incr_u32(&(handle_mgr->handle_generator), 1); + uint32_t new_handle = PSA_HANDLE_MGR_INVALID_HANDLE; + uint32_t pool_ix = 0; + + // Look for a vacant space in handles pool for the generated handle + for(pool_ix = 0; pool_ix < handle_mgr->pool_size; pool_ix++) { + + expected = PSA_HANDLE_MGR_INVALID_HANDLE; + + // Write the handles pool index in the upper 16 bits of the handle + new_handle = ((pool_ix << PSA_HANDLE_MGR_HANDLE_INDEX_POS) | tmp_handle); + + // Store the generated handle in the handles pool + if(core_util_atomic_cas_u32( (uint32_t *)( &(handle_mgr->handles_pool[pool_ix].handle) ), + &expected, + new_handle + )) { + + // Handle is successfully stored in handles pool + + // Store the handle memory in the handles pool, "coupled" with the stored handle + handle_mgr->handles_pool[pool_ix].handle_mem = handle_mem; + handle_mgr->handles_pool[pool_ix].handle_owner = current_pid; + handle_mgr->handles_pool[pool_ix].handle_friend = friend_pid; + + break; + } + + // Occupied index in handles pool - continue looping + } + + // Handle creation should only occur after a successful memory allocation + // and is not expected to fail. + SPM_ASSERT(pool_ix != handle_mgr->pool_size); + + return new_handle; +} + + +/********************************************************************************************************************************** + * Function : psa_hndl_mgr_handle_destroy + * + * Description: This function removes a handle from the handle manager. + * + * Parameters : handle_mgr - [IN] A pointer to the handle manager object + * handle - [IN] The handle to be removed + * + * Return : Void + *********************************************************************************************************************************/ +void psa_hndl_mgr_handle_destroy(psa_handle_manager_t *handle_mgr, psa_handle_t handle) +{ + // Make sanity checks on arguments + SPM_ASSERT(handle_mgr != NULL); + SPM_ASSERT(handle != PSA_NULL_HANDLE); + + + // Get the handle's index in the handles pool + uint32_t pool_ix = ((handle >> PSA_HANDLE_MGR_HANDLE_INDEX_POS) & PSA_HANDLE_MGR_HANDLE_INDEX_MSK); + if(pool_ix >= handle_mgr->pool_size) { + SPM_PANIC("[ERROR] Handle's index [%d] is bigger than handles pool size [%d]! \n", (int)pool_ix, (int)(handle_mgr->pool_size)); + } + + if(handle_mgr->handles_pool[pool_ix].handle != handle) { + SPM_PANIC("[ERROR] Handle %d is not found in expected index! \n", (int)handle); + } + + // Get active partition id - Needed for requester identification + spm_partition_t *curr_part_ptr = get_active_partition(); + int32_t current_pid = ((curr_part_ptr != NULL) ? curr_part_ptr->partition_id : PSA_NSPE_IDENTIFIER); + + if( (handle_mgr->handles_pool[pool_ix].handle_owner != current_pid) && + (handle_mgr->handles_pool[pool_ix].handle_friend != current_pid) + ) { + SPM_PANIC("[ERROR] Request for destroy by non-owner or friend!\n"); + } + + handle_mgr->handles_pool[pool_ix].handle = PSA_NULL_HANDLE; + handle_mgr->handles_pool[pool_ix].handle_owner = PSA_HANDLE_MGR_INVALID_FRIEND_OWNER; + handle_mgr->handles_pool[pool_ix].handle_friend = PSA_HANDLE_MGR_INVALID_FRIEND_OWNER; +} + + +/********************************************************************************************************************************** + * Function : psa_hndl_mgr_handle_get_mem + * + * Description: This function looks for the handle memory corresponding to . + * If it is not found in the expected index in the handles pool, the function fails. + * + * Parameters : handle_mgr - [IN] A pointer to the handle manager object. + * handle - [IN] The handle for which we request the corresponding memory handle. + * + * Return : A pointer to the memory corresponding to the handle. + *********************************************************************************************************************************/ +void *psa_hndl_mgr_handle_get_mem(psa_handle_manager_t *handle_mgr, psa_handle_t handle) +{ + SPM_ASSERT(handle_mgr != NULL); + + if(handle == PSA_NULL_HANDLE) { + SPM_PANIC("[ERROR] Trying to get memory for an invalid handle! \n"); + } + + // Get the handle's index in the handles pool + uint32_t pool_ix = ((handle >> PSA_HANDLE_MGR_HANDLE_INDEX_POS) & PSA_HANDLE_MGR_HANDLE_INDEX_MSK); + if(pool_ix >= handle_mgr->pool_size) { + SPM_PANIC("[ERROR] Handle's index [%d] is bigger than handles pool size [%d]! \n", (int)pool_ix, (int)(handle_mgr->pool_size)); + } + + if(handle_mgr->handles_pool[pool_ix].handle != handle) { + SPM_PANIC("[ERROR] Handle %d is not found in expected index! \n", (int)handle); + } + + // Get active partition id - Needed for requester identification + spm_partition_t *curr_part_ptr = get_active_partition(); + int32_t current_pid = ((curr_part_ptr != NULL) ? curr_part_ptr->partition_id : PSA_NSPE_IDENTIFIER); + + if( (current_pid != handle_mgr->handles_pool[pool_ix].handle_owner) && + (current_pid != handle_mgr->handles_pool[pool_ix].handle_friend) + ) { + SPM_PANIC("[ERROR] Request for handle memory is not allowed for this partition! \n"); + } + + // If a valid handle is "coupled" with a NULL handle memory then + // it is an internal module error or memory was overwritten --> Assert + SPM_ASSERT(handle_mgr->handles_pool[pool_ix].handle_mem != NULL); + + return handle_mgr->handles_pool[pool_ix].handle_mem; +} diff --git a/components/TARGET_PSA/spm/COMPONENT_SPE/handles_manager.h b/components/TARGET_PSA/spm/COMPONENT_SPE/handles_manager.h new file mode 100644 index 0000000..d964f80 --- /dev/null +++ b/components/TARGET_PSA/spm/COMPONENT_SPE/handles_manager.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2017, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MBED_HANDLE_MANAGER_H__ +#define __MBED_HANDLE_MANAGER_H__ + +/* -------------------------------------- Includes ----------------------------------- */ + +#include "psa_defs.h" + +#include + + + +/* --------------------------------- extern "C" wrapper ------------------------------ */ + +#ifdef __cplusplus +extern "C" { +#endif + + + +/* ------------------------------------ Definitions ---------------------------------- */ + +#define PSA_HANDLE_MGR_INVALID_HANDLE ((uint32_t)PSA_NULL_HANDLE) + +#define PSA_HANDLE_MGR_INVALID_FRIEND_OWNER 0 // Denoting invalid friend or invalid owner + +#define PSA_HANDLE_MGR_MAX_HANDLES_NUM 0x8000 // Handles manager pool indexes must be in range 0 - 0x7FFF. + // The reason for this limitation is that the index is stored in the upper 16 bits of a handle, + // and the most significant bit must be zero to keep handles non negative. + + + +/* -------------------------------------- Structs ------------------------------------ */ + +typedef struct psa_handle_item_t { + + psa_handle_t handle; /* The user exposed handle [unique identifier] */ + int32_t handle_owner; /* The partition id of the handle creator - allowed to get_mem() / destroy() */ + int32_t handle_friend; /* The partition id of a "friend" partition - allowed to get_mem() */ + void *handle_mem; /* Points to memory allocated by the user */ + +} psa_handle_item_t; + + +typedef struct psa_handle_manager_t { + + uint32_t handle_generator; /* A counter supplying handle numbers */ + uint32_t pool_size; /* The maximum number of handles that pool can contain */ + psa_handle_item_t *handles_pool; /* Holding couples of handles and their memory "blocks" */ + +} psa_handle_manager_t; + + +/* +handles_pool + | + | + | + --> *--------------------------------------------------------------------------* + | handle | handle | handle | | | ... | + *--------------------------------------------------------------------------* + | handle_owner | handle_owner | handle_owner | | | ... | + *--------------------------------------------------------------------------* + | handle_friend | handle_friend | handle_friend | | | ... | + *--------------------------------------------------------------------------* + | handle_mem | handle_mem | handle_mem | | | ... | + *--------------------------------------------------------------------------* +*/ + + + +/* ------------------------------------- Functions ----------------------------------- */ + +psa_handle_t psa_hndl_mgr_handle_create(psa_handle_manager_t *handle_mgr, void *handle_mem, int32_t friend_pid); +void psa_hndl_mgr_handle_destroy(psa_handle_manager_t *handle_mgr, psa_handle_t handle); +void *psa_hndl_mgr_handle_get_mem(psa_handle_manager_t *handle_mgr, psa_handle_t handle); + + +#ifdef __cplusplus +} +#endif + +#endif /* __MBED_HANDLE_MANAGER_H__ */ diff --git a/components/TARGET_PSA/spm/COMPONENT_SPE/psa_setup.c b/components/TARGET_PSA/spm/COMPONENT_SPE/psa_setup.c new file mode 100644 index 0000000..8516ce1 --- /dev/null +++ b/components/TARGET_PSA/spm/COMPONENT_SPE/psa_setup.c @@ -0,0 +1,68 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#include "spm_panic.h" +#include "spm_internal.h" +#include "handles_manager.h" +#include "cmsis.h" +#include "psa_its_partition.h" + + +__attribute__((weak)) +spm_partition_t g_partitions[1] = { + { + .partition_id = ITS_ID, + .thread_id = 0, + .flags_rot_srv = ITS_WAIT_ANY_SID_MSK, + .flags_interrupts = 0, + .rot_services = NULL, + .rot_services_count = ITS_ROT_SRV_COUNT, + .extern_sids = NULL, + .extern_sids_count = ITS_EXT_ROT_SRV_COUNT, + .irq_mapper = NULL, + }, +}; + +/* Check all the defined memory regions for overlapping. */ + +/* A list of all the memory regions. */ +__attribute__((weak)) +const mem_region_t *mem_regions = NULL; + +__attribute__((weak)) +const uint32_t mem_region_count = 0; + +// forward declaration of partition initializers +void its_init(spm_partition_t *partition); + +__attribute__((weak)) +uint32_t init_partitions(spm_partition_t **partitions) +{ + if (NULL == partitions) { + SPM_PANIC("partitions is NULL!\n"); + } + + its_init(&(g_partitions[0])); + + *partitions = g_partitions; + return 1; +} + diff --git a/components/TARGET_PSA/spm/COMPONENT_SPE/spm_client.c b/components/TARGET_PSA/spm/COMPONENT_SPE/spm_client.c new file mode 100644 index 0000000..7b516e1 --- /dev/null +++ b/components/TARGET_PSA/spm/COMPONENT_SPE/spm_client.c @@ -0,0 +1,351 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "mbed_toolchain.h" +#include "rtx_os.h" +#include "spm_client.h" +#include "spm_messages.h" +#include "spm_internal.h" +#include "spm_panic.h" +#include "handles_manager.h" + +extern spm_db_t g_spm; + +static inline spm_rot_service_t *rot_service_in_partition_get_by_sid(spm_partition_t *partition, uint32_t sid) +{ + for (uint32_t i = 0; i < partition->rot_services_count; ++i) { + spm_rot_service_t *rot_service = &(partition->rot_services[i]); + if (rot_service->sid == sid) { + return rot_service; + } + } + + return NULL; +} + +static inline spm_rot_service_t *rot_service_get(uint32_t sid) +{ + for (uint32_t i = 0; i < g_spm.partition_count; ++i) { + spm_rot_service_t *rot_service = rot_service_in_partition_get_by_sid(&(g_spm.partitions[i]), sid); + if (NULL != rot_service) { + return rot_service; + } + } + + return NULL; +} + +static inline void spm_validate_connection_allowed(spm_rot_service_t *target, spm_partition_t *source) +{ + if ((NULL == source) && (false == target->allow_nspe)) { + SPM_PANIC("SID 0x%x is not allowed to be called from NSPE\n", target->sid); + } + + if (NULL != source) { + if (NULL == source->extern_sids) { + SPM_PANIC("Partition %d did not declare extern functions\n", source->partition_id); + } + + for (uint32_t i = 0; i < source->extern_sids_count; i++) { + if (source->extern_sids[i] == target->sid) { + return; + } + } + + SPM_PANIC("SID 0x%x is not in partition %d extern functions list\n", target->sid, source->partition_id); + } +} + +static inline psa_handle_t create_channel_handle(void *handle_mem, int32_t friend_pid) +{ + return psa_hndl_mgr_handle_create(&(g_spm.channels_handle_mgr), handle_mem, friend_pid); +} + +static inline spm_ipc_channel_t *get_channel_from_handle(psa_handle_t handle) +{ + return (spm_ipc_channel_t *)psa_hndl_mgr_handle_get_mem(&(g_spm.channels_handle_mgr), handle); +} + +static void spm_rot_service_queue_enqueue(spm_rot_service_t *rot_service, spm_ipc_channel_t *item) +{ + osStatus_t os_status = osMutexAcquire(rot_service->partition->mutex, osWaitForever); + SPM_ASSERT(osOK == os_status); + PSA_UNUSED(os_status); + + if (rot_service->queue.tail == NULL) { + rot_service->queue.head = item; + } else { + rot_service->queue.tail->next = item; + } + + rot_service->queue.tail = item; + + uint32_t flags = osThreadFlagsSet(rot_service->partition->thread_id, rot_service->mask); + // osThreadFlagsSet() sets the msb on failure. + // flags is not allowed to be 0 since only dequeue operation can clear the flags, + // and both operations (enqueue and dequeue) are mutex protected. + SPM_ASSERT((flags & SPM_CMSIS_RTOS_ERROR_BIT_MSK) == 0); + SPM_ASSERT(flags & rot_service->mask); + PSA_UNUSED(flags); + + os_status = osMutexRelease(rot_service->partition->mutex); + SPM_ASSERT(osOK == os_status); +} + +void psa_connect_async(uint32_t sid, spm_pending_connect_msg_t *msg) +{ + SPM_ASSERT(msg != NULL); + + spm_rot_service_t *dst_rot_service = rot_service_get(sid); + if (NULL == dst_rot_service) { + SPM_PANIC("SID 0x%x is invalid!\n", sid); + } + + if (((dst_rot_service->min_version_policy == PSA_MINOR_VERSION_POLICY_RELAXED) && (msg->min_version > dst_rot_service->min_version)) || + ((dst_rot_service->min_version_policy == PSA_MINOR_VERSION_POLICY_STRICT) && (msg->min_version != dst_rot_service->min_version))) { + SPM_PANIC("minor version %d does not comply with sid %d minor version %d and minor policy %d", + msg->min_version, dst_rot_service->sid, dst_rot_service->min_version, dst_rot_service->min_version_policy); + } + + spm_partition_t *origin_partition = get_active_partition(); + spm_validate_connection_allowed(dst_rot_service, origin_partition); + + if (!is_buffer_accessible(msg, sizeof(*msg), origin_partition)) { + SPM_PANIC("Pending connect message is inaccessible\n"); + } + + // Allocating from SPM-Core internal memory + spm_ipc_channel_t *channel = (spm_ipc_channel_t *)osMemoryPoolAlloc(g_spm.channel_mem_pool, PSA_POLL); + if (NULL == channel) { + msg->rc = PSA_CONNECTION_REFUSED; + if (origin_partition != NULL) { + osStatus_t os_status = osSemaphoreRelease(msg->completion_sem_id); + SPM_ASSERT(osOK == os_status); + PSA_UNUSED(os_status); + } else { + nspe_done(msg->completion_sem_id); + } + + return; + } + + // Create the handle in the user message so we could destroy it in case of failure. + msg->rc = (psa_error_t)create_channel_handle(channel, dst_rot_service->partition->partition_id); + + // NOTE: all struct fields must be initialized as the allocated memory is not zeroed. + channel->state = SPM_CHANNEL_STATE_CONNECTING; + channel->src_partition = origin_partition; + channel->dst_rot_service = dst_rot_service; + channel->msg_ptr = msg; + channel->msg_type = PSA_IPC_CONNECT; + channel->rhandle = NULL; + channel->next = NULL; + channel->is_dropped = FALSE; + + spm_rot_service_queue_enqueue(dst_rot_service, channel); +} + +psa_handle_t psa_connect(uint32_t sid, uint32_t minor_version) +{ + osRtxSemaphore_t msg_sem_storage = {0}; + const osSemaphoreAttr_t msg_sem_attr = { + .name = NULL, + .attr_bits = 0, + .cb_mem = &msg_sem_storage, + .cb_size = sizeof(msg_sem_storage), + }; + + spm_pending_connect_msg_t msg = { + .min_version = minor_version, + .rc = PSA_NULL_HANDLE, + .completion_sem_id = osSemaphoreNew( + SPM_COMPLETION_SEM_MAX_COUNT, + SPM_COMPLETION_SEM_INITIAL_COUNT, + &msg_sem_attr) + }; + + if (NULL == msg.completion_sem_id) { + SPM_PANIC("could not create a semaphore for connect message"); + } + + psa_connect_async(sid, &msg); + + osStatus_t os_status = osSemaphoreAcquire(msg.completion_sem_id, osWaitForever); + SPM_ASSERT(osOK == os_status); + + os_status = osSemaphoreDelete(msg.completion_sem_id); + SPM_ASSERT(osOK == os_status); + + PSA_UNUSED(os_status); + + return (psa_handle_t)msg.rc; +} + +void psa_call_async(psa_handle_t handle, spm_pending_call_msg_t *msg) +{ + SPM_ASSERT(msg != NULL); + spm_ipc_channel_t *channel = get_channel_from_handle(handle); + SPM_ASSERT(channel != NULL); + + if (!is_buffer_accessible(msg, sizeof(*msg), channel->src_partition)) { + SPM_PANIC("Pending call message is inaccessible\n"); + } + + if (channel->is_dropped == TRUE) { + msg->rc = PSA_DROP_CONNECTION; + + if (channel->src_partition == NULL) { + nspe_done(msg->completion_sem_id); + } else { + osStatus_t os_status = osSemaphoreRelease(msg->completion_sem_id); + SPM_ASSERT(osOK == os_status); + PSA_UNUSED(os_status); + } + + return; + } + + channel_state_switch(&channel->state, + SPM_CHANNEL_STATE_IDLE, SPM_CHANNEL_STATE_PENDING); + + channel->msg_ptr = msg; + channel->msg_type = PSA_IPC_CALL; + + validate_iovec(msg->in_vec, msg->in_vec_size, msg->out_vec, msg->out_vec_size); + spm_rot_service_queue_enqueue(channel->dst_rot_service, channel); +} + +psa_error_t psa_call( + psa_handle_t handle, + const psa_invec_t *in_vec, + size_t in_len, + const psa_outvec_t *out_vec, + size_t out_len + ) +{ + osRtxSemaphore_t msg_sem_storage = {0}; + const osSemaphoreAttr_t msg_sem_attr = { + .name = NULL, + .attr_bits = 0, + .cb_mem = &msg_sem_storage, + .cb_size = sizeof(msg_sem_storage), + }; + + spm_pending_call_msg_t msg = { + .in_vec = in_vec, + .in_vec_size = in_len, + .out_vec = out_vec, + .out_vec_size = out_len, + .rc = PSA_SUCCESS, + .completion_sem_id = osSemaphoreNew( + SPM_COMPLETION_SEM_MAX_COUNT, + SPM_COMPLETION_SEM_INITIAL_COUNT, + &msg_sem_attr) + }; + + if (NULL == msg.completion_sem_id) { + SPM_PANIC("could not create a semaphore for connect message"); + } + + psa_call_async(handle, &msg); + + osStatus_t os_status = osSemaphoreAcquire(msg.completion_sem_id, osWaitForever); + SPM_ASSERT(osOK == os_status); + + os_status = osSemaphoreDelete(msg.completion_sem_id); + SPM_ASSERT(osOK == os_status); + + PSA_UNUSED(os_status); + + return (psa_error_t)msg.rc; +} + +void psa_close_async(psa_handle_t handle, spm_pending_close_msg_t *msg) +{ + SPM_ASSERT(msg != NULL); + + spm_ipc_channel_t *channel = get_channel_from_handle(handle); + SPM_ASSERT(channel != NULL); + + if (!is_buffer_accessible(msg, sizeof(*msg), channel->src_partition)) { + SPM_PANIC("Pending close message is inaccessible\n"); + } + + channel_state_assert(&(channel->state), SPM_CHANNEL_STATE_IDLE); + + channel->msg_ptr = msg; + channel->msg_type = PSA_IPC_DISCONNECT; + + spm_rot_service_queue_enqueue(channel->dst_rot_service, channel); +} + +void psa_close(psa_handle_t handle) +{ + if (handle == PSA_NULL_HANDLE) { + // Invalid handles will fail inside handle manager [called from psa_close_async()] + return; + } + + osRtxSemaphore_t msg_sem_storage = {0}; + const osSemaphoreAttr_t msg_sem_attr = { + .name = NULL, + .attr_bits = 0, + .cb_mem = &msg_sem_storage, + .cb_size = sizeof(msg_sem_storage), + }; + + spm_pending_close_msg_t msg = { + .handle = handle, + .completion_sem_id = osSemaphoreNew( SPM_COMPLETION_SEM_MAX_COUNT, + SPM_COMPLETION_SEM_INITIAL_COUNT, + &msg_sem_attr + ), + + }; + + if (NULL == msg.completion_sem_id) { + SPM_PANIC("Could not create a semaphore for close message"); + } + + psa_close_async(handle, &msg); + + osStatus_t os_status = osSemaphoreAcquire(msg.completion_sem_id, osWaitForever); + SPM_ASSERT(osOK == os_status); + + os_status = osSemaphoreDelete(msg.completion_sem_id); + SPM_ASSERT(osOK == os_status); + + PSA_UNUSED(os_status); +} + +uint32_t psa_framework_version(void) +{ + return (uint32_t)PSA_FRAMEWORK_VERSION; +} + + +uint32_t psa_version(uint32_t sid) +{ + uint32_t version = PSA_VERSION_NONE; + spm_rot_service_t *service = rot_service_get(sid); + if (service != NULL) { + if ((get_active_partition() != NULL) || (service->allow_nspe)) { + version = service->min_version; + } + } + + return version; +} diff --git a/components/TARGET_PSA/spm/COMPONENT_SPE/spm_common.c b/components/TARGET_PSA/spm/COMPONENT_SPE/spm_common.c new file mode 100644 index 0000000..44be4d1 --- /dev/null +++ b/components/TARGET_PSA/spm/COMPONENT_SPE/spm_common.c @@ -0,0 +1,173 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "cmsis_os2.h" +#include "psa_defs.h" +#include "spm_internal.h" +#include "spm_panic.h" +#include "spm_api.h" + +bool core_util_atomic_cas_u8(volatile uint8_t *ptr, uint8_t *expectedCurrentValue, uint8_t desiredValue); + +inline void validate_iovec( + const void *in_vec, + const uint32_t in_len, + const void *out_vec, + const uint32_t out_len + ) +{ + if ( + !( + ((in_vec != NULL) || (in_len == 0)) && + ((out_vec != NULL) || (out_len == 0)) && + (in_len + out_len <= PSA_MAX_IOVEC) + ) + ) { + SPM_PANIC("Failed iovec Validation invec=(0X%p) inlen=(%d) outvec=(0X%p) outlen=(%d)\n", in_vec, in_len, out_vec, out_len); + } +} + +inline void channel_state_switch(uint8_t *current_state, uint8_t expected_state, uint8_t new_state) +{ + uint8_t backup_expected = expected_state; + if (!core_util_atomic_cas_u8(current_state, &expected_state, new_state)) { + SPM_PANIC("channel in incorrect processing state: %d while %d is expected!\n", + expected_state, backup_expected); + } +} + +inline void channel_state_assert(uint8_t *current_state, uint8_t expected_state) +{ + if (*current_state != expected_state) { + SPM_PANIC("channel in incorrect processing state: %d while %d is expected!\n", + *current_state, expected_state); + } +} + +extern const mem_region_t *mem_regions; +extern const uint32_t mem_region_count; + +const mem_region_t *get_mem_regions(int32_t partition_id, uint32_t *region_count) +{ + uint32_t i; + + SPM_ASSERT(NULL != region_count); + *region_count = 0; + + if (partition_id == MEM_PARTITIONS_ALL) { + *region_count = mem_region_count; + return mem_regions; + } + + // The entries in the array of memory regions are grouped by partition id. + // This is ensured by the way this array is automatically generated from the manifest files. + for (i = 0; i < mem_region_count && mem_regions[i].partition_id != partition_id; i++); + + if (i == mem_region_count) { + return NULL; + } + + const mem_region_t *regions = &mem_regions[i]; + for (; i < mem_region_count && mem_regions[i].partition_id == partition_id; i++, (*region_count)++); + + return regions; +} + +extern spm_db_t g_spm; + +spm_partition_t *get_active_partition(void) +{ + osThreadId_t active_thread_id = osThreadGetId(); + SPM_ASSERT(NULL != active_thread_id); + + for (uint32_t i = 0; i < g_spm.partition_count; ++i) { + if (g_spm.partitions[i].thread_id == active_thread_id) { + return &(g_spm.partitions[i]); + } + } + + return NULL; // Valid in case of NSPE +} + +bool is_buffer_accessible(const void *ptr, size_t size, spm_partition_t *accessing_partition) +{ + if (NULL == ptr) { + return false; + } + + if (size == 0) { + return true; + } + + // Check wrap around of ptr + size + if (((uintptr_t)ptr + size - 1) < (uintptr_t)ptr) { + return false; + } + + // Note: Sanity checks on platform addresses and sizes is done in psa_spm_init() + + uint32_t secure_ram_base = PSA_SECURE_RAM_START; + size_t secure_ram_len = PSA_SECURE_RAM_SIZE; + uint32_t secure_rom_base = PSA_SECURE_ROM_START; + size_t secure_rom_len = PSA_SECURE_ROM_SIZE; + + uint32_t non_secure_ram_base = PSA_NON_SECURE_RAM_START; + size_t non_secure_ram_len = PSA_NON_SECURE_RAM_SIZE; + uint32_t non_secure_rom_base = PSA_NON_SECURE_ROM_START; + size_t non_secure_rom_len = PSA_NON_SECURE_ROM_SIZE; + + + // Check NSPE case + if (accessing_partition == NULL) { + + // Make sure the NSPE is accessing the non-secure ram range OR the non-secure flash range + + // RAM + if ( ((uintptr_t)ptr >= (uintptr_t)non_secure_ram_base) && ((uintptr_t)ptr < ((uintptr_t)non_secure_ram_base + non_secure_ram_len)) && + (((uintptr_t)ptr + size) <= ((uintptr_t)non_secure_ram_base + non_secure_ram_len)) + ) { + return true; + } + + // FLASH + if ( ((uintptr_t)ptr >= (uintptr_t)non_secure_rom_base) && ((uintptr_t)ptr < ((uintptr_t)non_secure_rom_base + non_secure_rom_len)) && + (((uintptr_t)ptr + size) <= ((uintptr_t)non_secure_rom_base + non_secure_rom_len)) + ) { + return true; + } + } + else // Check SPE case + { + // As we do not expect secure partitions to use SPE addresses for iovecs, we make sure here that the SPE is accessing + // the secure ram range OR the secure flash range + + // RAM + if ( ((uintptr_t)ptr >= (uintptr_t)secure_ram_base) && ((uintptr_t)ptr < ((uintptr_t)secure_ram_base + secure_ram_len)) && + (((uintptr_t)ptr + size) <= ((uintptr_t)secure_ram_base + secure_ram_len)) + ) { + return true; + } + + // FLASH + if ( ((uintptr_t)ptr >= (uintptr_t)secure_rom_base) && ((uintptr_t)ptr < ((uintptr_t)secure_rom_base + secure_rom_len)) && + (((uintptr_t)ptr + size) <= ((uintptr_t)secure_rom_base + secure_rom_len)) + ) { + return true; + } + + } + + return false; +} diff --git a/components/TARGET_PSA/spm/COMPONENT_SPE/spm_init.c b/components/TARGET_PSA/spm/COMPONENT_SPE/spm_init.c new file mode 100644 index 0000000..a50745c --- /dev/null +++ b/components/TARGET_PSA/spm/COMPONENT_SPE/spm_init.c @@ -0,0 +1,165 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mbed_assert.h" +#include "rtx_os.h" +#include "spm_internal.h" +#include "handles_manager.h" +#include "spm_api.h" + +MBED_STATIC_ASSERT( + MBED_CONF_SPM_IPC_MAX_NUM_OF_CHANNELS <= PSA_HANDLE_MGR_MAX_HANDLES_NUM, + "Number of channels exceeds maximum number of handles allowed in handles manager!" +); + +MBED_STATIC_ASSERT( + MBED_CONF_SPM_IPC_MAX_NUM_OF_MESSAGES <= PSA_HANDLE_MGR_MAX_HANDLES_NUM, + "Number of active messages exceeds maximum number of handles allowed in handles manager!" +); + + +// Sanity check - memory ranges are well formed + +MBED_STATIC_ASSERT( + (PSA_SECURE_RAM_START + PSA_SECURE_RAM_SIZE - 1) > PSA_SECURE_RAM_START, + "Illegal secure ram region!" +); + +MBED_STATIC_ASSERT( + (PSA_SECURE_ROM_START + PSA_SECURE_ROM_SIZE - 1) > PSA_SECURE_ROM_START, + "Illegal secure flash region!" +); + +MBED_STATIC_ASSERT( + (PSA_NON_SECURE_RAM_START + PSA_NON_SECURE_RAM_SIZE - 1) > PSA_NON_SECURE_RAM_START, + "Illegal non-secure ram region!" +); + +MBED_STATIC_ASSERT( + (PSA_NON_SECURE_ROM_START + PSA_NON_SECURE_ROM_SIZE - 1) > PSA_NON_SECURE_ROM_START, + "Illegal non-secure flash region!" +); + +MBED_STATIC_ASSERT( + (PSA_SHARED_RAM_START + PSA_SHARED_RAM_SIZE - 1) > PSA_SHARED_RAM_START, + "Illegal shared ram region!" +); + + +// Sanity check - memory ranges are not overlapping + +#ifndef MIN +#define MIN( a, b ) ( ( ( a ) < ( b ) ) ? ( a ) : ( b ) ) +#endif + +#ifndef MAX +#define MAX( a, b ) ( ( ( a ) > ( b ) ) ? ( a ) : ( b ) ) +#endif + +#define OVERLAP_CHECK( name1, start1, size1, name2, start2, size2) \ + MBED_STATIC_ASSERT( \ + MAX(start1, start2) >= MIN((start1 + size1), (start2 + size2)), \ + name1 " and " name2 " are overlapping!!!" \ + ) + +OVERLAP_CHECK( + "PSA_SECURE_RAM", PSA_SECURE_RAM_START, PSA_SECURE_RAM_SIZE, + "PSA_NON_SECURE_RAM", PSA_NON_SECURE_RAM_START, PSA_NON_SECURE_RAM_SIZE +); + +OVERLAP_CHECK( + "PSA_SECURE_RAM", PSA_SECURE_RAM_START, PSA_SECURE_RAM_SIZE, + "PSA_SHARED_RAM", PSA_SHARED_RAM_START, PSA_SHARED_RAM_SIZE +); + +OVERLAP_CHECK( + "PSA_SHARED_RAM", PSA_SHARED_RAM_START, PSA_SHARED_RAM_SIZE, + "PSA_NON_SECURE_RAM", PSA_NON_SECURE_RAM_START, PSA_NON_SECURE_RAM_SIZE +); + +OVERLAP_CHECK( + "PSA_SECURE_FLASH", PSA_SECURE_ROM_START, PSA_SECURE_ROM_SIZE, + "PSA_NON_SECURE_FLASH", PSA_NON_SECURE_ROM_START, PSA_NON_SECURE_ROM_SIZE +); + + +psa_handle_item_t g_channels_handle_storage[MBED_CONF_SPM_IPC_MAX_NUM_OF_CHANNELS] = {0}; +spm_ipc_channel_t g_channel_data[MBED_CONF_SPM_IPC_MAX_NUM_OF_CHANNELS] = {0}; +osRtxMemoryPool_t g_channel_mem_pool_storage = {0}; +osMemoryPoolAttr_t g_channel_mem_pool_attr = { + .name = "SPM_channel_pool", + .attr_bits = 0, + .cb_mem = &g_channel_mem_pool_storage, + .cb_size = sizeof(g_channel_mem_pool_storage), + .mp_mem = g_channel_data, + .mp_size = sizeof(g_channel_data) +}; + +psa_handle_item_t g_messages_handle_storage[MBED_CONF_SPM_IPC_MAX_NUM_OF_MESSAGES] = {0}; +spm_active_msg_t g_active_messages_data[MBED_CONF_SPM_IPC_MAX_NUM_OF_MESSAGES] = {0}; +osRtxMemoryPool_t g_active_messages_mem_pool_storage = {0}; +osMemoryPoolAttr_t g_active_messages_mem_pool_attr = { + .name = "SPM_active_messages_pool", + .attr_bits = 0, + .cb_mem = &g_active_messages_mem_pool_storage, + .cb_size = sizeof(g_active_messages_mem_pool_storage), + .mp_mem = g_active_messages_data, + .mp_size = sizeof(g_active_messages_data) +}; + +spm_db_t g_spm = { + .partitions = NULL, + .partition_count = 0, + .channels_handle_mgr = { + .handle_generator = PSA_HANDLE_MGR_INVALID_HANDLE, + .pool_size = MBED_CONF_SPM_IPC_MAX_NUM_OF_CHANNELS, + .handles_pool = g_channels_handle_storage + }, + .messages_handle_mgr = { + .handle_generator = PSA_HANDLE_MGR_INVALID_HANDLE, + .pool_size = MBED_CONF_SPM_IPC_MAX_NUM_OF_MESSAGES, + .handles_pool = g_messages_handle_storage + }, + .channel_mem_pool = NULL, + .active_messages_mem_pool = NULL +}; + +// forward declaration +uint32_t init_partitions(spm_partition_t **partitions); + +void psa_spm_init(void) +{ + spm_hal_memory_protection_init(); + + g_spm.channel_mem_pool = osMemoryPoolNew( + MBED_CONF_SPM_IPC_MAX_NUM_OF_CHANNELS, + sizeof(spm_ipc_channel_t), + &g_channel_mem_pool_attr + ); + if (NULL == g_spm.channel_mem_pool) { + error("%s - Failed to create channel memory pool!\n", __func__); + } + + g_spm.active_messages_mem_pool = osMemoryPoolNew( + MBED_CONF_SPM_IPC_MAX_NUM_OF_MESSAGES, + sizeof(spm_active_msg_t), + &g_active_messages_mem_pool_attr + ); + if (NULL == g_spm.active_messages_mem_pool) { + error("%s - Failed to create active messages memory pool!\n", __func__); + } + + g_spm.partition_count = init_partitions(&(g_spm.partitions)); +} diff --git a/components/TARGET_PSA/spm/COMPONENT_SPE/spm_internal.h b/components/TARGET_PSA/spm/COMPONENT_SPE/spm_internal.h new file mode 100644 index 0000000..43cb17c --- /dev/null +++ b/components/TARGET_PSA/spm/COMPONENT_SPE/spm_internal.h @@ -0,0 +1,235 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SPM_INTERNAL_H +#define SPM_INTERNAL_H + +#include +#include "cmsis_os2.h" +#include "cmsis.h" +#include "psa_defs.h" +#include "spm_messages.h" +#include "spm_panic.h" +#include "handles_manager.h" +#include "cmsis_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define SPM_COMPLETION_SEM_MAX_COUNT (1UL) /* Maximum number of available tokens for a completion semaphore. */ +#define SPM_COMPLETION_SEM_INITIAL_COUNT (0UL) /* Initial number of available tokens for a completion semaphore. */ + +#define PSA_MMIO_PERM_READ_ONLY (0x000000001) +#define PSA_MMIO_PERM_READ_WRITE (0x000000003) + +#define PSA_RESERVED_ERROR_MIN (INT32_MIN + 1) +#define PSA_RESERVED_ERROR_MAX (INT32_MIN + 127) + +#define SPM_CHANNEL_STATE_INVALID (0x01) +#define SPM_CHANNEL_STATE_CONNECTING (0x02) +#define SPM_CHANNEL_STATE_IDLE (0x03) +#define SPM_CHANNEL_STATE_PENDING (0x04) +#define SPM_CHANNEL_STATE_ACTIVE (0x05) + +#define MEM_PARTITIONS_ALL (0) /* A constant to use to retrieve the memory regions for all the partitions at once. */ + +#define SPM_CMSIS_RTOS_ERROR_BIT_MSK (0x80000000) + +#ifndef TRUE +#define TRUE (1) +#endif + +#ifndef FALSE +#define FALSE (0) +#endif + +struct spm_partition; +struct spm_ipc_channel; + +/* + * Structure to describe MMIO region along with owning partition. + */ +typedef struct mem_region { + const uint32_t base; + const uint32_t size; + const uint32_t permission; + const int32_t partition_id; +} mem_region_t; + +typedef union spm_iovec { + psa_invec_t in; + psa_outvec_t out; +} spm_iovec_t; + +/* + * IRQ signal mapper definition. + * The function will not return on invalid signal. + */ +typedef IRQn_Type (*spm_signal_to_irq_mapper_t)(uint32_t); + +/* + * Structure to aggregate channels queue in a Root of Trust Service. + */ +typedef struct spm_channel_linked_list { + struct spm_ipc_channel *head; /* List's first object*/ + struct spm_ipc_channel *tail; /* List's last object*/ +} spm_channel_linked_list_t; + +/* + * Structure containing resources and attributes of a Root of Trust Service. + */ +typedef struct spm_rot_service { + const uint32_t sid; /* The Root of Trust Service ID.*/ + const uint32_t mask; /* The signal for this Root of Trust Service*/ + struct spm_partition *partition; /* Pointer to the Partition which the Root of Trust Service belongs to.*/ + const uint32_t min_version; /* Minor version of the Root of Trust Service interface.*/ + const uint32_t min_version_policy; /* Minor version policy of the Root of Trust Service.*/ + const bool allow_nspe; /* Whether to allow non-secure clients to connect to the Root of Trust Service.*/ + spm_channel_linked_list_t queue; /* Queue of channels holding ROT_SRV operations waiting to be served. */ +} spm_rot_service_t; + +/* + * Structure containing Partition->RoT-Service channel information. + */ +typedef struct spm_ipc_channel { + struct spm_partition *src_partition; /* Pointer to the Partition which connects to the Root of Trust Service.*/ + spm_rot_service_t *dst_rot_service; /* Pointer to the connected Root of Trust Service.*/ + void *rhandle; /* Reverse handle to be used for this channel.*/ + void *msg_ptr; /* message data sent from user */ + struct spm_ipc_channel *next; /* Next channel in the chain */ + uint8_t msg_type; /* The message type.*/ + uint8_t state; /* The current processing state of the channel.*/ + uint8_t is_dropped; +} spm_ipc_channel_t; + +/* + * Structure containing the currently active message for a Root of Trust Service. + */ +typedef struct spm_active_msg { + spm_ipc_channel_t *channel; /* Pointer to the channel delivering this message.*/ + spm_iovec_t iovecs[PSA_MAX_IOVEC]; /* IOvecs sent for message and response.*/ + uint8_t out_index; /* First index of outvecs in iovecs*/ +} spm_active_msg_t; + +/* + * Structure containing resources and attributes of a Secure Partition. + */ +typedef struct spm_partition { + const int32_t partition_id; /* The Partition ID.*/ + osThreadId_t thread_id; /* Thread ID of the Partition thread.*/ + const uint32_t flags_rot_srv; /* Mask of all the ROT_SRV signals the partition supports.*/ + const uint32_t flags_interrupts; /* Mask of all the IRQs & doorbell which the partition supports.*/ + spm_rot_service_t *rot_services; /* Array of the Partition's Root of Trust Services.*/ + const uint32_t rot_services_count; /* Number of the Partition's Root of Trust Services.*/ + const uint32_t *extern_sids; /* Array of Root of Trust Service IDs which the partition can connect to.*/ + const uint32_t extern_sids_count; /* Number of Root of Trust Services which the partition can connect to.*/ + osMutexId_t mutex; /* Mutex for all rot_service's queues operations. */ + spm_signal_to_irq_mapper_t irq_mapper; /* a function which maps signal to irq number*/ +} spm_partition_t; + +/* + * Structure containing the SPM internal data. + */ +typedef struct spm_db { + spm_partition_t *partitions; /* Array of all the Secure Partitions in the system.*/ + uint32_t partition_count; /* Number of Secure Partitions in the system.*/ + psa_handle_manager_t channels_handle_mgr; + psa_handle_manager_t messages_handle_mgr; + osMemoryPoolId_t channel_mem_pool; /* Channel memory pool identifier.*/ + osMemoryPoolId_t active_messages_mem_pool; /* Channel memory pool identifier.*/ +} spm_db_t; + +/* + * Returns a pointer to the currently active secure partition or NULL in case called from NSPE. + */ +spm_partition_t *get_active_partition(void); + +/* + * Return an array of memory regions used by a given partition. + * + * @param[in] partition_id - a partition ID to find memory regions for, if MEM_PARTITIONS_ALL then + * memory regions for all the partitions are returned + * @param[out] region_count - will be set to the number of memory regions returned + */ +const mem_region_t *get_mem_regions(int32_t partition_id, uint32_t *region_count); + +// Platform dependent APIs + +/** + * Validates a memory block is accessable from a specific partition + * + * @param[in] ptr pointer to the beggining of the memory block. + * @param[in] size size of the memory block in bytes. + * @param[in] accessing_partition which partition is trying to access the memory. + * @return true if the entire memory block is accessable from given partition. + */ +bool is_buffer_accessible(const void *ptr, size_t size, spm_partition_t * accessing_partition); + +/** + * Alerts NSPE that a proccess (connect or call) has ended. + * + * @param[in] completion_sem_id semaphore id in NSPE. + */ +void nspe_done(osSemaphoreId_t completion_sem_id); + +/* + * Validates parameters sent from caller and queues a connect message on the correct ROT_SRV. + * + * @param[in] sid - desired RoT service ID + * @param[in] msg - pointer to connect message struct + */ +void psa_connect_async(uint32_t sid, spm_pending_connect_msg_t *msg); + +/* + * Validates parameters sent from caller and queues a call message on the correct ROT_SRV. + * + * @param[in] handle - channel handle for the connection + * @param[in] msg - pointer to call message struct + */ +void psa_call_async(psa_handle_t handle, spm_pending_call_msg_t *msg); + +/* + * Validates parameters sent from caller and queues a disconnect message on the correct ROT_SRV. + * + * @param[in] handle - handle of channel to close + * @param[in] msg - pointer to close message struct + */ +void psa_close_async(psa_handle_t handle, spm_pending_close_msg_t *msg); + + +/* + * Validates IOvecs. + * + * @param[in] in_vec - psa_invec_t array + * @param[in] in_len - number of elements in in_vec + * @param[in] out_vec - psa_outvec_t array + * @param[in] out_len - number of elements in out_vec +*/ +void validate_iovec( + const void *in_vec, + const uint32_t in_len, + const void *out_vec, + const uint32_t out_len + ); + +void channel_state_switch(uint8_t *current_state, uint8_t expected_state, uint8_t new_state); +void channel_state_assert(uint8_t *current_state, uint8_t expected_state); + +#ifdef __cplusplus +} +#endif + +#endif // SPM_INTERNAL_H diff --git a/components/TARGET_PSA/spm/COMPONENT_SPE/spm_main.c b/components/TARGET_PSA/spm/COMPONENT_SPE/spm_main.c new file mode 100644 index 0000000..d949914 --- /dev/null +++ b/components/TARGET_PSA/spm/COMPONENT_SPE/spm_main.c @@ -0,0 +1,17 @@ +#if defined(COMPONENT_SPM_MAILBOX) + +#include "ipc_queue.h" +extern ipc_consumer_queue_t *cons_queue; +// this is the dispatcher thread for dual-chip systems +int main(void) +{ + while (true) { + ipc_queue_drain(cons_queue); + } +} + +#else + +#error "Non dual-chip platforms are not yet supported" + +#endif // defined(COMPONENT_SPM_MAILBOX) diff --git a/components/TARGET_PSA/spm/COMPONENT_SPE/spm_panic.h b/components/TARGET_PSA/spm/COMPONENT_SPE/spm_panic.h new file mode 100644 index 0000000..16cce72 --- /dev/null +++ b/components/TARGET_PSA/spm/COMPONENT_SPE/spm_panic.h @@ -0,0 +1,64 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MBED_SPM_PANIC_H__ +#define __MBED_SPM_PANIC_H__ + +/** @addtogroup SPM + * @{ + */ + +#include "mbed_toolchain.h" +#include "mbed_assert.h" +#include "mbed_error.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifdef SPM_SPE_BUILD + +#error "Trying to build for SPE with SPM_SPE_BUILD flag - Not supported" + +#else + +/** + * Generate a system panic + * + * @param[in] format The format string to output on panic + * @param[in] ... (Additional arguments) Depending on the format string + */ +#define SPM_PANIC(format, ...) error("%s %u: " format, __func__, __LINE__, ##__VA_ARGS__) + + +/** + * Assert on condition (debug build only) + * + * @param[in] expr Condition to be asserted + */ +#define SPM_ASSERT(expr) MBED_ASSERT(expr) + +#endif + + +#ifdef __cplusplus +} +#endif + +/** @}*/ // end of SPM group + +#endif // __MBED_SPM_PANIC_H__ diff --git a/components/TARGET_PSA/spm/COMPONENT_SPE/spm_server.c b/components/TARGET_PSA/spm/COMPONENT_SPE/spm_server.c new file mode 100644 index 0000000..a465d70 --- /dev/null +++ b/components/TARGET_PSA/spm/COMPONENT_SPE/spm_server.c @@ -0,0 +1,586 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "mbed_toolchain.h" +#include "spm_server.h" +#include "spm_messages.h" +#include "spm_internal.h" +#include "spm_panic.h" +#include "handles_manager.h" +#include "cmsis.h" + +extern spm_db_t g_spm; + +static inline spm_partition_t *get_partition_by_pid(int32_t partition_id) +{ + for (uint32_t i = 0; i < g_spm.partition_count; ++i) { + if (g_spm.partitions[i].partition_id == partition_id) { + return &(g_spm.partitions[i]); + } + } + + return NULL; +} + +static inline psa_handle_t create_msg_handle(void *handle_mem, int32_t friend_pid) +{ + return psa_hndl_mgr_handle_create(&(g_spm.messages_handle_mgr), handle_mem, friend_pid); +} + +static inline spm_active_msg_t *get_msg_from_handle(psa_handle_t handle) +{ + return (spm_active_msg_t *)psa_hndl_mgr_handle_get_mem(&(g_spm.messages_handle_mgr), handle); +} + +static inline void destroy_msg_handle(psa_handle_t handle) +{ + psa_hndl_mgr_handle_destroy(&(g_spm.messages_handle_mgr), handle); +} + +static inline void destroy_channel_handle(psa_handle_t handle) +{ + psa_hndl_mgr_handle_destroy(&(g_spm.channels_handle_mgr), handle); +} + + +static inline spm_rot_service_t *get_rot_service(spm_partition_t *prt, psa_signal_t signal) +{ + for (size_t i = 0; i < prt->rot_services_count; i++) { + if (prt->rot_services[i].mask == signal) { + return &prt->rot_services[i]; + } + } + + return NULL; +} + +/* + * This function validates the parameters sent from the user and copies it to an active message + * + * Function assumptions: + * * channel is allocated from SPM Core memory + * * channel->msg_ptr potentially allocated from nonsecure memory + * * user_msg allocated from secure partition memory - not trusted by SPM Core +*/ +static void copy_message_to_spm(spm_ipc_channel_t *channel, psa_msg_t *user_msg) +{ + // Memory allocated from MemoryPool isn't zeroed - thus a temporary variable will make sure we will start from a clear state. + spm_active_msg_t temp_active_message = { + .channel = channel, + .iovecs = {{.in = {0}}}, + .out_index = 0, + }; + + if (channel->msg_type == PSA_IPC_CALL) { + if (!is_buffer_accessible(channel->msg_ptr, sizeof(spm_pending_call_msg_t), channel->src_partition)) { + SPM_PANIC("message data is inaccessible\n"); + } + + spm_pending_call_msg_t *call_msg_data = (spm_pending_call_msg_t *)channel->msg_ptr; + + // Copy pointers and sizes to secure memory to prevent TOCTOU + const psa_invec_t *temp_invec = call_msg_data->in_vec; + const uint32_t temp_invec_size = call_msg_data->in_vec_size; + const psa_outvec_t *temp_outvec = call_msg_data->out_vec; + const uint32_t temp_outvec_size = call_msg_data->out_vec_size; + + validate_iovec(temp_invec, temp_invec_size, temp_outvec, temp_outvec_size); + temp_active_message.out_index = (uint8_t)temp_invec_size; + + if (temp_invec_size > 0) { + if (!is_buffer_accessible(temp_invec, temp_invec_size * sizeof(*temp_invec), channel->src_partition)) { + SPM_PANIC("in_vec is inaccessible\n"); + } + + for (uint32_t i = 0; i < temp_invec_size; ++i) { + if (temp_invec[i].len == 0) { + continue; + } + + // Copy struct + temp_active_message.iovecs[i].in = temp_invec[i]; + user_msg->in_size[i] = temp_invec[i].len; + + // Copy then check to prevent TOCTOU + if (!is_buffer_accessible( + temp_active_message.iovecs[i].in.base, + temp_active_message.iovecs[i].in.len, + channel->src_partition)) { + SPM_PANIC("in_vec[%d] is inaccessible\n", i); + } + } + } + + if (temp_outvec_size > 0) { + if (!is_buffer_accessible(temp_outvec, temp_outvec_size * sizeof(*temp_outvec), channel->src_partition)) { + SPM_PANIC("out_vec is inaccessible\n"); + } + + for (uint32_t i = 0; i < temp_outvec_size; ++i) { + if (temp_outvec[i].len == 0) { + continue; + } + + // Copy struct + temp_active_message.iovecs[temp_invec_size + i].out = temp_outvec[i]; + user_msg->out_size[i] = temp_outvec[i].len; + + // Copy then check to prevent TOCTOU + if (!is_buffer_accessible( + temp_active_message.iovecs[temp_invec_size + i].out.base, + temp_active_message.iovecs[temp_invec_size + i].out.len, + channel->src_partition)) { + SPM_PANIC("out_vec[%d] is inaccessible\n", i); + } + } + } + } + + // Allocating from SPM-Core internal memory + spm_active_msg_t *active_msg = (spm_active_msg_t *)osMemoryPoolAlloc(g_spm.active_messages_mem_pool, osWaitForever); + if (NULL == active_msg) { + SPM_PANIC("Could not allocate active message"); + } + + // Copy struct + *active_msg = temp_active_message; + + psa_handle_t handle = create_msg_handle(active_msg, PSA_HANDLE_MGR_INVALID_FRIEND_OWNER); + + user_msg->type = channel->msg_type; + user_msg->rhandle = channel->rhandle; + user_msg->handle = handle; +} + +static spm_ipc_channel_t *spm_rot_service_queue_dequeue(spm_rot_service_t *rot_service) +{ + osStatus_t os_status = osMutexAcquire(rot_service->partition->mutex, osWaitForever); + SPM_ASSERT(osOK == os_status); + PSA_UNUSED(os_status); + + spm_ipc_channel_t *ret = rot_service->queue.head; + + if (ret == NULL) { + SPM_PANIC("Dequeue from empty queue"); + } + + rot_service->queue.head = ret->next; + ret->next = NULL; + + if (rot_service->queue.head == NULL) { + rot_service->queue.tail = NULL; + + uint32_t flags = osThreadFlagsClear(rot_service->mask); + + // osThreadFlagsClear() sets the msb on failure + SPM_ASSERT((flags & SPM_CMSIS_RTOS_ERROR_BIT_MSK) == 0); + SPM_ASSERT(flags & rot_service->mask); + PSA_UNUSED(flags); + } + + os_status = osMutexRelease(rot_service->partition->mutex); + SPM_ASSERT(osOK == os_status); + + return ret; +} + + +static uint32_t psa_wait(bool wait_any, uint32_t bitmask, uint32_t timeout) +{ + spm_partition_t *curr_partition = get_active_partition(); + SPM_ASSERT(NULL != curr_partition); // active thread in SPM must be in partition DB + + uint32_t flags_interrupts = curr_partition->flags_interrupts | PSA_DOORBELL; + uint32_t flags_all = curr_partition->flags_rot_srv | flags_interrupts; + + // In case we're waiting for any signal the bitmask must contain all the flags, otherwise + // we should be waiting for a subset of interrupt signals. + if (wait_any) { + bitmask = flags_all; + } else { + // Make sure the interrupt mask contains only a subset of interrupt signal mask. + if (bitmask != (flags_interrupts & bitmask)) { + SPM_PANIC("interrupt mask 0x%x must have only bits from 0x%x!\n", + bitmask, flags_interrupts); + } + } + + uint32_t asserted_signals = osThreadFlagsWait( + bitmask, + osFlagsWaitAny | osFlagsNoClear, + (PSA_BLOCK == timeout) ? osWaitForever : timeout + ); + + // Asserted_signals must be a subset of the supported ROT_SRV and interrupt signals. + SPM_ASSERT((asserted_signals == (asserted_signals & flags_all)) || + ((PSA_BLOCK != timeout) && (osFlagsErrorTimeout == asserted_signals))); + + return (osFlagsErrorTimeout == asserted_signals) ? 0 : asserted_signals; +} + +uint32_t psa_wait_any(uint32_t timeout) +{ + return psa_wait(true, 0, timeout); +} + +uint32_t psa_wait_interrupt(uint32_t interrupt_mask, uint32_t timeout) +{ + return psa_wait(false, interrupt_mask, timeout); +} + +void psa_get(psa_signal_t signum, psa_msg_t *msg) +{ + spm_partition_t *curr_partition = get_active_partition(); + SPM_ASSERT(NULL != curr_partition); // active thread in SPM must be in partition DB + + if (!is_buffer_accessible(msg, sizeof(*msg), curr_partition)) { + SPM_PANIC("msg is inaccessible\n"); + } + + memset(msg, 0, sizeof(*msg)); + + // signum must be ONLY ONE of the bits of curr_partition->flags_rot_srv + bool is_one_bit = ((signum != 0) && !(signum & (signum - 1))); + if (!is_one_bit || !(signum & curr_partition->flags_rot_srv)) { + SPM_PANIC( + "signum 0x%x must have only 1 bit ON and must be a subset of 0x%x!\n", + signum, + curr_partition->flags_rot_srv + ); + } + + uint32_t active_flags = osThreadFlagsGet(); + if (0 == (signum & active_flags)) { + SPM_PANIC("flag is not active!\n"); + } + + spm_rot_service_t *curr_rot_service = get_rot_service(curr_partition, signum); + if (curr_rot_service == NULL) { + SPM_PANIC("Received signal (0x%08x) that does not match any root of trust service", signum); + } + spm_ipc_channel_t *curr_channel = spm_rot_service_queue_dequeue(curr_rot_service); + + switch (curr_channel->msg_type) { + case PSA_IPC_CONNECT: + channel_state_assert( + &curr_channel->state, + SPM_CHANNEL_STATE_CONNECTING + ); + break; + + case PSA_IPC_CALL: + channel_state_switch( + &curr_channel->state, + SPM_CHANNEL_STATE_PENDING, + SPM_CHANNEL_STATE_ACTIVE + ); + break; + + case PSA_IPC_DISCONNECT: + { + channel_state_assert( + &curr_channel->state, + SPM_CHANNEL_STATE_IDLE + ); + break; + } + default: + SPM_PANIC("psa_get - unexpected message type=0x%08X", curr_channel->msg_type); + break; + } + + copy_message_to_spm(curr_channel, msg); +} + +static size_t read_or_skip(psa_handle_t msg_handle, uint32_t invec_idx, void *buf, size_t num_bytes) +{ + spm_active_msg_t *active_msg = get_msg_from_handle(msg_handle); + + channel_state_assert(&active_msg->channel->state, SPM_CHANNEL_STATE_ACTIVE); + + if (invec_idx >= PSA_MAX_IOVEC) { + SPM_PANIC("Invalid invec_idx\n"); + } + + if (invec_idx >= active_msg->out_index) { + return 0; + } + + psa_invec_t *active_iovec = &active_msg->iovecs[invec_idx].in; + + if (num_bytes > active_iovec->len) { + num_bytes = active_iovec->len; + } + + if (num_bytes > 0) { + if (buf) { + memcpy(buf, active_iovec->base, num_bytes); + } + active_iovec->base = (void *)((uint8_t*)active_iovec->base + num_bytes); + active_iovec->len -= num_bytes; + } + + return num_bytes; +} + +size_t psa_read(psa_handle_t msg_handle, uint32_t invec_idx, void *buf, size_t num_bytes) +{ + spm_partition_t *curr_partition = get_active_partition(); + SPM_ASSERT(NULL != curr_partition); // active thread in SPM must be in partition DB + + if (!is_buffer_accessible(buf, num_bytes, curr_partition)) { + SPM_PANIC("buffer is inaccessible\n"); + } + + return read_or_skip(msg_handle, invec_idx, buf, num_bytes); +} + +size_t psa_skip(psa_handle_t msg_handle, uint32_t invec_idx, size_t num_bytes) +{ + return read_or_skip(msg_handle, invec_idx, NULL, num_bytes); +} + +void psa_write(psa_handle_t msg_handle, uint32_t outvec_idx, const void *buffer, size_t num_bytes) +{ + if (0 == num_bytes) { + return; + } + + spm_partition_t *curr_partition = get_active_partition(); + SPM_ASSERT(NULL != curr_partition); // active thread in S + + if (!is_buffer_accessible(buffer, num_bytes, curr_partition)) { + SPM_PANIC("buffer is inaccessible\n"); + } + + spm_active_msg_t *active_msg = get_msg_from_handle(msg_handle); + + channel_state_assert(&active_msg->channel->state, SPM_CHANNEL_STATE_ACTIVE); + + outvec_idx += active_msg->out_index; + if (outvec_idx >= PSA_MAX_IOVEC) { + SPM_PANIC("Invalid outvec_idx\n"); + } + + psa_outvec_t *active_iovec = &active_msg->iovecs[outvec_idx].out; + if (num_bytes > active_iovec->len) { + SPM_PANIC("Invalid write operation (Requested %d, Avialable %d)\n", num_bytes, active_iovec->len); + } + + memcpy((uint8_t *)(active_iovec->base), buffer, num_bytes); + active_iovec->base = (void *)((uint8_t *)active_iovec->base + num_bytes); + active_iovec->len -= num_bytes; + + return; +} + +void psa_reply(psa_handle_t msg_handle, psa_error_t status) +{ + spm_active_msg_t *active_msg = get_msg_from_handle(msg_handle); + spm_ipc_channel_t *active_channel = active_msg->channel; + SPM_ASSERT(active_channel != NULL); + SPM_ASSERT(active_channel->msg_ptr != NULL); + + // !!!!!NOTE!!!!! handles must be destroyed before osMemoryPoolFree(). + // Message creation blocked on resource exhaustion and handle will be created + // only after a successful memory allocation and is not expected to fail. + { + destroy_msg_handle(msg_handle); + + memset(active_msg, 0, sizeof(*active_msg)); + osStatus_t os_status = osMemoryPoolFree(g_spm.active_messages_mem_pool, active_msg); + SPM_ASSERT(osOK == os_status); + PSA_UNUSED(os_status); + } + + osSemaphoreId_t completion_sem_id = NULL; + bool nspe_call = (active_channel->src_partition == NULL); + switch (active_channel->msg_type) { + case PSA_IPC_CONNECT: + { + if ((status != PSA_CONNECTION_ACCEPTED) && (status != PSA_CONNECTION_REFUSED)) { + SPM_PANIC("status (0X%08x) is not allowed for PSA_IPC_CONNECT", status); + } + + spm_pending_connect_msg_t *connect_msg_data = (spm_pending_connect_msg_t *)(active_channel->msg_ptr); + completion_sem_id = connect_msg_data->completion_sem_id; + if (status == PSA_CONNECTION_REFUSED) { + channel_state_assert(&active_channel->state, SPM_CHANNEL_STATE_CONNECTING); + // !!!!!NOTE!!!!! handles must be destroyed before osMemoryPoolFree(). + // Channel creation fails on resource exhaustion and handle will be created + // only after a successful memory allocation and is not expected to fail. + { + // In psa_connect the handle was written to user's memory before + // the connection was established. + // Channel state machine and ACL will prevent attacker from + // doing bad stuff before we overwrite it with a negative return code. + destroy_channel_handle((psa_handle_t)connect_msg_data->rc); + + memset(active_channel, 0, sizeof(*active_channel)); + osStatus_t os_status = osMemoryPoolFree(g_spm.channel_mem_pool, active_channel); + SPM_ASSERT(osOK == os_status); + PSA_UNUSED(os_status); + } + // Replace the handle we created in the user's memory with the error code + connect_msg_data->rc = status; + active_channel = NULL; + } else { + channel_state_switch(&active_channel->state, + SPM_CHANNEL_STATE_CONNECTING, SPM_CHANNEL_STATE_IDLE); + } + break; + } + case PSA_IPC_CALL: + { + if ((status >= PSA_RESERVED_ERROR_MIN) && (status <= PSA_RESERVED_ERROR_MAX)) { + SPM_PANIC("status (0X%08x) is not allowed for PSA_IPC_CALL", status); + } + + channel_state_switch(&active_channel->state, + SPM_CHANNEL_STATE_ACTIVE, SPM_CHANNEL_STATE_IDLE); + + if (status == PSA_DROP_CONNECTION) { + active_channel->is_dropped = TRUE; + } + + spm_pending_call_msg_t *call_msg_data = (spm_pending_call_msg_t *)(active_channel->msg_ptr); + call_msg_data->rc = status; + completion_sem_id = call_msg_data->completion_sem_id; + break; + } + case PSA_IPC_DISCONNECT: + { + spm_pending_close_msg_t *close_msg_data = (spm_pending_close_msg_t *)(active_channel->msg_ptr); + completion_sem_id = close_msg_data->completion_sem_id; + + channel_state_switch( &(active_channel->state), + SPM_CHANNEL_STATE_IDLE, + SPM_CHANNEL_STATE_INVALID + ); + + // !!!!!NOTE!!!!! handles must be destroyed before osMemoryPoolFree(). + // Channel creation fails on resource exhaustion and handle will be created + // only after a successful memory allocation and is not expected to fail. + { + destroy_channel_handle(close_msg_data->handle); + + memset(active_channel, 0, sizeof(*active_channel)); + osStatus_t os_status = osMemoryPoolFree(g_spm.channel_mem_pool, active_channel); + active_channel = NULL; + SPM_ASSERT(osOK == os_status); + PSA_UNUSED(os_status); + } + break; + + // Note: The status code is ignored for PSA_IPC_DISCONNECT message type + } + default: + SPM_PANIC("psa_reply() - Unexpected message type=0x%08X", active_channel->msg_type); + break; + } + + if (active_channel != NULL) { + active_channel->msg_ptr = NULL; + active_channel->msg_type = 0; // uninitialized + } + + if (nspe_call) { + nspe_done(completion_sem_id); + } else { + osStatus_t os_status = osSemaphoreRelease(completion_sem_id); + SPM_ASSERT(osOK == os_status); + PSA_UNUSED(os_status); + } + + return; +} + +void psa_notify(int32_t partition_id) +{ + spm_partition_t *target_partition = get_partition_by_pid(partition_id); + if (NULL == target_partition) { + SPM_PANIC("Could not find partition (partition_id = %d)\n", + partition_id + ); + } + + uint32_t flags = osThreadFlagsSet(target_partition->thread_id, PSA_DOORBELL); + // osThreadFlagsSet() sets the msb on failure + // flags is allowed to be 0 since by the time osThreadFlagsSet() returns + // the flag could have been cleared by another thread + SPM_ASSERT((flags & SPM_CMSIS_RTOS_ERROR_BIT_MSK) == 0); + PSA_UNUSED(flags); +} + +void psa_clear(void) +{ + uint32_t flags = osThreadFlagsClear(PSA_DOORBELL); + + // osThreadFlagsClear() sets the msb on failure + SPM_ASSERT((flags & SPM_CMSIS_RTOS_ERROR_BIT_MSK) == 0); + + // psa_clear() must not be called when doorbell signal is not currently asserted + if ((flags & PSA_DOORBELL) != PSA_DOORBELL) { + SPM_PANIC("psa_call() called without signaled doorbell\n"); + } +} + +int32_t psa_identity(psa_handle_t msg_handle) +{ + spm_active_msg_t *active_msg = get_msg_from_handle(msg_handle); + SPM_ASSERT(active_msg->channel != NULL); + if (active_msg->channel->src_partition == NULL) { + return PSA_NSPE_IDENTIFIER; + } + + return active_msg->channel->src_partition->partition_id; +} + +void psa_set_rhandle(psa_handle_t msg_handle, void *rhandle) +{ + spm_active_msg_t *active_msg = get_msg_from_handle(msg_handle); + active_msg->channel->rhandle = rhandle; +} + +void psa_eoi(uint32_t irq_signal) +{ + spm_partition_t *curr_partition = get_active_partition(); + SPM_ASSERT(NULL != curr_partition); + if (curr_partition->irq_mapper == NULL) { + SPM_PANIC("Try to clear an interrupt flag without declaring IRQ"); + } + + if (0 == (curr_partition->flags_interrupts & irq_signal)) { + SPM_PANIC("Signal %d not in irq range\n", irq_signal); + } + + bool is_one_bit = ((irq_signal != 0) && !(irq_signal & (irq_signal - 1))); + if (!is_one_bit) { + SPM_PANIC("signal 0x%x must have only 1 bit ON!\n",irq_signal); + } + + IRQn_Type irq_line = curr_partition->irq_mapper(irq_signal); + uint32_t flags = osThreadFlagsClear(irq_signal); + // osThreadFlagsClear() sets the msb on failure + SPM_ASSERT((flags & SPM_CMSIS_RTOS_ERROR_BIT_MSK) == 0); + + // psa_eoi() must not be called with an unasserted flag. + if ((flags & irq_signal) != irq_signal) { + SPM_PANIC("psa_eoi() called without signaled IRQ\n"); + } + + NVIC_EnableIRQ(irq_line); +} diff --git a/components/TARGET_PSA/spm/COMPONENT_SPE/spm_server.h b/components/TARGET_PSA/spm/COMPONENT_SPE/spm_server.h new file mode 100644 index 0000000..5d7d468 --- /dev/null +++ b/components/TARGET_PSA/spm/COMPONENT_SPE/spm_server.h @@ -0,0 +1,170 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MBED_SPM_SERVER_H__ +#define __MBED_SPM_SERVER_H__ + +/** @addtogroup SPM + * The SPM (Secure Partition Manager) is responsible for isolating software in partitions,@n + * managing the execution of software within partitions, and providing IPC between partitions. + * @{ + */ + +/* -------------------------------------- Includes ----------------------------------- */ + +#include "psa_defs.h" + +/* --------------------------------- extern "C" wrapper ------------------------------ */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** @addtogroup RoT-Service-API + * The C interface for a Root of Trust Service in a Partition. + * @{ + */ + +/** + * Return the signals that have been asserted.@n + * + * @param[in] timeout timeout value:@n + * @a PSA_BLOCK block the caller until any signal is asserted.@n + * @a PSA_POLL Returns immediately. + * @return 32-bit value of asserted signals. + */ +uint32_t psa_wait_any(uint32_t timeout); + +/** + * Return interrupt/doorbell signals that have been asserted based on the bitmask provided.@n + * The mask contains a set of signals the caller is interested in handling and must be a subset + * of combined interrupt and doorbell mask for the calling partition. + * + * @param[in] interrupt_mask mask of signals. + * @param[in] timeout timeout value:@n + * @a PSA_BLOCK block the caller until one of the requested signals is asserted.@n + * @a PSA_POLL Returns immediately. + * @return 32-bit value of asserted signals. + */ +uint32_t psa_wait_interrupt(uint32_t interrupt_mask, uint32_t timeout); + +/** + * Return the Partition ID of the caller. + * + * @note Bit[31] is set if the caller is from the NSPE. + * + * @param[in] msg_handle Handle for the caller's message. + * @return Caller's partition ID + */ +int32_t psa_identity(psa_handle_t msg_handle); + +/** + * Get the message which corresponds to a given signal. + * + * @param[in] signum an asserted signal returned from psa_wait(). + * @param[out] msg pointer to a psa_msg structure. + */ +void psa_get(psa_signal_t signum, psa_msg_t *msg); + +/** + * Associate the caller-provided private data with a specified handle. + * + * @param[in] msg_handle Handle for the caller's message. + * @param[in] rhandle Reverse handle allocated by the Root of Trust Service. + */ +void psa_set_rhandle(psa_handle_t msg_handle, void *rhandle); + +/** + * Copy up to @a len bytes from position @a offset within the client message + * payload into the Secure Partition buffer @a buffer.@n + * + * @note Callers should know how much data is available to read based on the@n + * @a size attribute of the psa_msg structure returned from psa_get().@n + * The copy is truncated if the requested range extends beyond the end of the payload.@n + * In such a case, the remaining space in @a buffer is not modified. + * + * @param[in] msg_handle Handle for the client's message. + * @param[in] invec_idx ::psa_invec index to be read. + * @param[out] buf Buffer to copy the requested data to. + * @param[in] num_bytes Number of bytes to read from the client's message payload. + * @return Number of bytes copied or 0 if offset is greater than the size attribute of psa_msg. + */ +size_t psa_read(psa_handle_t msg_handle, uint32_t invec_idx, void *buf, size_t num_bytes); + +/** + * Advance the current read offset by skipping @a num_bytes bytes for input vector + * indexed by @а invec_idx.@n + * If @a num_bytes is greater than the remaining number of bytes in the vector then + * all the remaining bytes are skipped. + * + * @param[in] msg_handle Handle for the client's message. + * @param[in] invec_idx ::psa_invec index to be skipped. + * @param[in] num_bytes Number of bytes to skip. + * @return Number of bytes skipped or 0 if offset is greater than the size attribute of psa_msg. + */ +size_t psa_skip(psa_handle_t msg_handle, uint32_t invec_idx, size_t num_bytes); + +/** + * Write a response payload of @a bytes bytes starting at position @a offset in the client's response buffer. + * + * @note If the caller writes data beyond the client's response buffer size + * (@a response_size attribute of the psa_msg structure returned from psa_get()) a fatal error occurs. + * + * @param[in] msg_handle Handle for the client's message. + * @param[in] outvec_idx ::psa_outvec index to be written to. + * @param[in] buffer Buffer with the data to write. + * @param[in] num_bytes Number of bytes to write. + */ +void psa_write(psa_handle_t msg_handle, uint32_t outvec_idx, const void *buffer, size_t num_bytes); + +/** + * Complete handling of specific message and unblocks the client. + * + * A return code must be specified, which will be sent to the client.@n + * Negative return code represent errors, Positive integers are application-specific. + * + * @param[in] msg_handle Handle for the client's message. + * @param[in] status Message result value to be reported to the client. + */ +void psa_reply(psa_handle_t msg_handle, psa_error_t status); + +/** + * Send a doorbell signal to a specific partition that is listening for that signal type. + * + * @param[in] partition_id partition ID of the target partition. + */ +void psa_notify(int32_t partition_id); + +/** + * Clear the doorbell signal. + */ +void psa_clear(void); + +/** + * Inform the SPM that an interrupt has been handled (end of interrupt). + * + * @param[in] irq_signal The interrupt signal that has been processed. + */ +void psa_eoi(uint32_t irq_signal); + +/** @}*/ // end of RoT-Service-API group + +#ifdef __cplusplus +} +#endif + +/** @}*/ // end of SPM group + +#endif /* __MBED_SPM_SERVER_H__ */ diff --git a/components/TARGET_PSA/spm/COMPONENT_SPM_MAILBOX/COMPONENT_NSPE/spm_client_proxy.c b/components/TARGET_PSA/spm/COMPONENT_SPM_MAILBOX/COMPONENT_NSPE/spm_client_proxy.c new file mode 100644 index 0000000..1332d0b --- /dev/null +++ b/components/TARGET_PSA/spm/COMPONENT_SPM_MAILBOX/COMPONENT_NSPE/spm_client_proxy.c @@ -0,0 +1,229 @@ +/* Copyright (c) 2018 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +// Includes +// -------- + +#include +#include "ipc_defs.h" +#include "ipc_queue.h" +#include "spm_client.h" +#include "spm_messages.h" +#include "mbed_critical.h" +#include "mbed_assert.h" +#include "cmsis_os2.h" +#include "rtx_lib.h" + + +// Globals +// ------- + +extern ipc_producer_queue_t *prod_queue; + +// API Functions Implementation +// ---------------------------- +uint32_t psa_framework_version(void) +{ + return (uint32_t)PSA_FRAMEWORK_VERSION; +} + +uint32_t psa_version(uint32_t sid) +{ + osRtxSemaphore_t res_sem_storage = {0, 0, 0, 0, 0, 0, 0, 0}; + + const osSemaphoreAttr_t res_sem_attr = { + .name = "VER_RES_SEM", + .attr_bits = 0, + .cb_mem = &res_sem_storage, + .cb_size = sizeof(res_sem_storage), + }; + + const spm_pending_version_msg_t msg = { + .rc = PSA_VERSION_NONE, + .completion_sem_id = osSemaphoreNew( IPC_RES_SEM_MAX_COUNT, + IPC_RES_SEM_INITIAL_COUNT, + &res_sem_attr + ) + }; + + MBED_ASSERT(msg.completion_sem_id != NULL); + + ipc_queue_item_t queue_item = { + .a = PSA_IPC_VERSION, + .b = (uint32_t)(&msg), + .c = sid + }; + + ipc_queue_enqueue(prod_queue, queue_item); + + osStatus_t os_status = osSemaphoreAcquire(msg.completion_sem_id, osWaitForever); + MBED_ASSERT(osOK == os_status); + + os_status = osSemaphoreDelete(msg.completion_sem_id); + MBED_ASSERT(osOK == os_status); + + PSA_UNUSED(os_status); + + return msg.rc; +} + +psa_handle_t psa_connect(uint32_t sid, uint32_t minor_version) +{ + osRtxSemaphore_t res_sem_storage = {0, 0, 0, 0, 0, 0, 0, 0}; + + const osSemaphoreAttr_t res_sem_attr = { + .name = "CONN_RES_SEM", + .attr_bits = 0, + .cb_mem = &res_sem_storage, + .cb_size = sizeof(res_sem_storage), + }; + + const spm_pending_connect_msg_t msg = { + .min_version = minor_version, + .rc = PSA_SUCCESS, + .completion_sem_id = osSemaphoreNew( IPC_RES_SEM_MAX_COUNT, + IPC_RES_SEM_INITIAL_COUNT, + &res_sem_attr + ) + }; + + MBED_ASSERT(msg.completion_sem_id != NULL); + + ipc_queue_item_t queue_item = { + .a = PSA_IPC_CONNECT, + .b = (uint32_t)(&msg), + .c = sid + }; + + ipc_queue_enqueue(prod_queue, queue_item); + + osStatus_t os_status = osSemaphoreAcquire(msg.completion_sem_id, osWaitForever); + MBED_ASSERT(osOK == os_status); + + os_status = osSemaphoreDelete(msg.completion_sem_id); + MBED_ASSERT(osOK == os_status); + + PSA_UNUSED(os_status); + + return (psa_handle_t)(msg.rc); +} + +psa_error_t psa_call( psa_handle_t handle, + const psa_invec_t *in_vec, + size_t in_len, + const psa_outvec_t *out_vec, + size_t out_len + ) +{ + // - Immediate errors are checked here. + // - Other errors are checked on the SPM core code + + // TODO: Panic instead + MBED_ASSERT(handle > 0); + // TODO: Panic instead + MBED_ASSERT((in_vec != NULL) || (in_len == 0)); + // TODO: Panic instead + MBED_ASSERT((out_vec != NULL) || (out_len == 0)); + // TODO: Panic instead + MBED_ASSERT(in_len + out_len <= PSA_MAX_IOVEC); + + osRtxSemaphore_t res_sem_storage = {0, 0, 0, 0, 0, 0, 0, 0}; + + const osSemaphoreAttr_t res_sem_attr = { + .name = "CALL_RES_SEM", + .attr_bits = 0, + .cb_mem = &res_sem_storage, + .cb_size = sizeof(res_sem_storage), + }; + + const spm_pending_call_msg_t msg = { + .in_vec = in_vec, + .in_vec_size = in_len, + .out_vec = out_vec, + .out_vec_size = out_len, + .rc = PSA_SUCCESS, + .completion_sem_id = osSemaphoreNew( IPC_RES_SEM_MAX_COUNT, + IPC_RES_SEM_INITIAL_COUNT, + &res_sem_attr + ) + }; + + MBED_ASSERT(msg.completion_sem_id != NULL); + + ipc_queue_item_t queue_item = { + .a = PSA_IPC_CALL, + .b = (uint32_t)(&msg), + .c = (uint32_t)handle + }; + + ipc_queue_enqueue(prod_queue, queue_item); + + osStatus_t os_status = osSemaphoreAcquire(msg.completion_sem_id, osWaitForever); + MBED_ASSERT(osOK == os_status); + + os_status = osSemaphoreDelete(msg.completion_sem_id); + MBED_ASSERT(osOK == os_status); + + PSA_UNUSED(os_status); + + return msg.rc; +} + + +void psa_close(psa_handle_t handle) +{ + if (handle == PSA_NULL_HANDLE) { + return; + } + + // - Immediate errors are checked here. + // - Other errors are checked on the SPM core code + // TODO: Panic instead + MBED_ASSERT(handle >= 0); + + osRtxSemaphore_t res_sem_storage = {0, 0, 0, 0, 0, 0, 0, 0}; + const osSemaphoreAttr_t res_sem_attr = { + .name = "CLOSE_RES_SEM", + .attr_bits = 0, + .cb_mem = &res_sem_storage, + .cb_size = sizeof(res_sem_storage), + }; + + spm_pending_close_msg_t msg = { + .handle = handle, + .completion_sem_id = osSemaphoreNew( IPC_RES_SEM_MAX_COUNT, + IPC_RES_SEM_INITIAL_COUNT, + &res_sem_attr + ) + }; + + MBED_ASSERT(msg.completion_sem_id != NULL); + ipc_queue_item_t queue_item = { + .a = PSA_IPC_DISCONNECT, + .b = (uint32_t)(&msg), + .c = (uint32_t)handle + }; + + ipc_queue_enqueue(prod_queue, queue_item); + + osStatus_t os_status = osSemaphoreAcquire(msg.completion_sem_id, osWaitForever); + MBED_ASSERT(osOK == os_status); + + os_status = osSemaphoreDelete(msg.completion_sem_id); + MBED_ASSERT(osOK == os_status); + + PSA_UNUSED(os_status); +} diff --git a/components/TARGET_PSA/spm/COMPONENT_SPM_MAILBOX/COMPONENT_NSPE/spm_mailbox_nspe.c b/components/TARGET_PSA/spm/COMPONENT_SPM_MAILBOX/COMPONENT_NSPE/spm_mailbox_nspe.c new file mode 100644 index 0000000..fffea52 --- /dev/null +++ b/components/TARGET_PSA/spm/COMPONENT_SPM_MAILBOX/COMPONENT_NSPE/spm_mailbox_nspe.c @@ -0,0 +1,106 @@ +#include "cmsis_os2.h" +#include "psa_defs.h" +#include "mbed_assert.h" +#include "ipc_queue.h" +#include "ipc_defs.h" +#include "spm_api.h" + +static os_mutex_t queue_mutex_storage; +static os_semaphore_t full_sema; +static os_semaphore_t read_sema; + +static const osMutexAttr_t queue_mutex_attr = { + .name = "Q_MUT", + .attr_bits = osMutexRecursive | osMutexPrioInherit | osMutexRobust, + .cb_mem = &queue_mutex_storage, + .cb_size = sizeof(queue_mutex_storage) +}; + +// Full queue semaphore attributes for the consumer queue +static const osSemaphoreAttr_t full_sem_attr = { + .name = "Q_W_SEM", + .attr_bits = 0, + .cb_mem = &full_sema, + .cb_size = sizeof(full_sema) +}; + +// Read semaphore attributes for the consumer queue +static const osSemaphoreAttr_t read_sem_attr = { + .name = "Q_R_SEM", + .attr_bits = 0, + .cb_mem = &read_sema, + .cb_size = sizeof(read_sema) +}; + +static ipc_producer_queue_t _prod_queue; +ipc_producer_queue_t *prod_queue = &_prod_queue; +static ipc_consumer_queue_t _cons_queue; +ipc_consumer_queue_t *cons_queue = &_cons_queue; + + +void on_new_item(void) +{ + spm_hal_mailbox_notify(); +} + +void on_vacancy(void) +{ + spm_hal_mailbox_notify(); +} + +void on_popped_item(ipc_queue_item_t item) +{ + osStatus_t os_status = osSemaphoreRelease((osSemaphoreId_t)(item.b)); + MBED_ASSERT(osOK == os_status); + PSA_UNUSED(os_status); +} + + +void spm_ipc_mailbox_init(void) +{ + // Initialization by data from shared memory + // ----------------------------------------- + + // This table is holding addresses of the platform's shared memory. + addr_table_t *shared_addr_table_ptr = (addr_table_t *)PSA_SHARED_RAM_START; + MBED_ASSERT(shared_addr_table_ptr->magic = ADDR_TABLE_MAGIC); + + ipc_base_queue_t *tx_queue_mem_ptr = (ipc_base_queue_t *)(shared_addr_table_ptr->tx_queue_ptr); + MBED_ASSERT(tx_queue_mem_ptr->magic == IPC_QUEUE_BASE_MAGIC); + + ipc_base_queue_t *rx_queue_mem_ptr = (ipc_base_queue_t *)(shared_addr_table_ptr->rx_queue_ptr); + MBED_ASSERT(rx_queue_mem_ptr->magic == IPC_QUEUE_BASE_MAGIC); + + osMutexId_t queue_mutex = osMutexNew(&queue_mutex_attr); + MBED_ASSERT(queue_mutex != NULL); // TODO: Panic instead + + osSemaphoreId_t full_queue_sem = osSemaphoreNew(IPC_QUEUE_SEM_MAX_COUNT, IPC_QUEUE_SEM_INITIAL_COUNT, &full_sem_attr); + MBED_ASSERT(full_queue_sem != NULL); // TODO: Panic instead + + osSemaphoreId_t queue_read_sem = osSemaphoreNew(IPC_QUEUE_SEM_MAX_COUNT, IPC_QUEUE_SEM_INITIAL_COUNT, &read_sem_attr); + MBED_ASSERT(queue_read_sem != NULL); // TODO: Panic instead + + ipc_producer_queue_init(prod_queue, tx_queue_mem_ptr, queue_mutex, full_queue_sem); + ipc_consumer_queue_init(cons_queue, rx_queue_mem_ptr, queue_read_sem); +} + +void spm_mailbox_irq_callback(void) +{ + osStatus_t os_status = osSemaphoreRelease(prod_queue->full_queue_sem); + MBED_ASSERT((osOK == os_status) || (osErrorResource == os_status)); + + os_status = osSemaphoreRelease(cons_queue->read_sem); + MBED_ASSERT((osOK == os_status) || (osErrorResource == os_status)); + + PSA_UNUSED(os_status); +} + +/******************************************************************************* +* Function Name: psa_spm_mailbox_dispatcher +*******************************************************************************/ +void psa_spm_mailbox_dispatcher(void *arg) +{ + while (true) { + ipc_queue_drain(cons_queue); + } +} diff --git a/components/TARGET_PSA/spm/COMPONENT_SPM_MAILBOX/COMPONENT_SPE/spm_mailbox_spe.c b/components/TARGET_PSA/spm/COMPONENT_SPM_MAILBOX/COMPONENT_SPE/spm_mailbox_spe.c new file mode 100644 index 0000000..9cb2ef7 --- /dev/null +++ b/components/TARGET_PSA/spm/COMPONENT_SPM_MAILBOX/COMPONENT_SPE/spm_mailbox_spe.c @@ -0,0 +1,167 @@ +#include "psa_defs.h" +#include "spm_client.h" +#include "spm_messages.h" +#include "spm_internal.h" +#include "mbed_assert.h" +#include "ipc_queue.h" +#include "ipc_defs.h" +#include "spm_api.h" + +static os_mutex_t queue_mutex_storage; +static os_semaphore_t full_sema; +static os_semaphore_t read_sema; + +static const osMutexAttr_t queue_mutex_attr = { + .name = "Q_MUT", + .attr_bits = osMutexRecursive | osMutexPrioInherit | osMutexRobust, + .cb_mem = &queue_mutex_storage, + .cb_size = sizeof(queue_mutex_storage) +}; + +// Full queue semaphore attributes for the consumer queue +static const osSemaphoreAttr_t full_sem_attr = { + .name = "Q_W_SEM", + .attr_bits = 0, + .cb_mem = &full_sema, + .cb_size = sizeof(full_sema) +}; + +// Read semaphore attributes for the consumer queue +static const osSemaphoreAttr_t read_sem_attr = { + .name = "Q_R_SEM", + .attr_bits = 0, + .cb_mem = &read_sema, + .cb_size = sizeof(read_sema) +}; + + +static ipc_producer_queue_t _prod_queue; +ipc_producer_queue_t *prod_queue = &_prod_queue; +static ipc_consumer_queue_t _cons_queue; +ipc_consumer_queue_t *cons_queue = &_cons_queue; + +MBED_STATIC_ASSERT( + (sizeof(addr_table_t) + sizeof(ipc_base_queue_t) + sizeof(ipc_base_queue_t)) <= PSA_SHARED_RAM_SIZE, + "shared memory size is too small!" +); + +void spm_mailbox_irq_callback(void) +{ + osStatus_t os_status = osSemaphoreRelease(prod_queue->full_queue_sem); + MBED_ASSERT((osOK == os_status) || (osErrorResource == os_status)); + + os_status = osSemaphoreRelease(cons_queue->read_sem); + MBED_ASSERT((osOK == os_status) || (osErrorResource == os_status)); + + PSA_UNUSED(os_status); +} + +void on_new_item(void) +{ + spm_hal_mailbox_notify(); +} + +void on_vacancy(void) +{ + spm_hal_mailbox_notify(); +} + +void on_popped_item(ipc_queue_item_t item) +{ + // item.a hold the message type (connect / call /close) + switch (item.a) + { + case PSA_IPC_CONNECT: + { + psa_connect_async(item.c, (spm_pending_connect_msg_t *)(item.b)); + break; + } + + case PSA_IPC_CALL: + { + psa_call_async((psa_handle_t)(item.c), (spm_pending_call_msg_t *)(item.b)); + break; + } + + case PSA_IPC_DISCONNECT: + { + psa_close_async((psa_handle_t)(item.c), (spm_pending_close_msg_t *)(item.b)); + break; + } + + case PSA_IPC_VERSION: + { + spm_pending_version_msg_t *msg = (spm_pending_version_msg_t *)(item.b); + if (!is_buffer_accessible(msg, sizeof(*msg), NULL)) { + SPM_PANIC("message data is inaccessible\n"); + } + + msg->rc = psa_version(item.c); + nspe_done(msg->completion_sem_id); + break; + } + + default: + { + MBED_ASSERT(false); + } + } +} + +void spm_ipc_mailbox_init(void) +{ + uint32_t *shared_memory_start = (uint32_t *)PSA_SHARED_RAM_START; + + // This struct is set with initial values for the address table (addresses of CM0+ / CM4 shared memory) + const addr_table_t shared_addr_table = { + .magic = ADDR_TABLE_MAGIC, + .tx_queue_ptr = (uintptr_t)( shared_memory_start + + (sizeof(addr_table_t) / sizeof(uint32_t)) + ), + .rx_queue_ptr = (uintptr_t)( shared_memory_start + + ((sizeof(addr_table_t) + sizeof(ipc_base_queue_t)) / sizeof(uint32_t)) + ) + }; + + // This struct is set with initial values to be used for IPC queues initialization. + // Same values are used to initial both tx and rx queues. + const ipc_base_queue_t queue_mem = { + .magic = IPC_QUEUE_BASE_MAGIC, + .read_idx = 0, + .write_idx = 0, + .data = {{0, 0}} + }; + + ipc_base_queue_t *tx_queue_mem_ptr = (ipc_base_queue_t *)(shared_addr_table.tx_queue_ptr); + ipc_base_queue_t *rx_queue_mem_ptr = (ipc_base_queue_t *)(shared_addr_table.rx_queue_ptr); + + // Copy initial queue values for tx & tx queues to the right location in the CM0+ / CM4 shared memory + memcpy(tx_queue_mem_ptr, &queue_mem, sizeof(ipc_base_queue_t)); + memcpy(rx_queue_mem_ptr, &queue_mem, sizeof(ipc_base_queue_t)); + + // Copy the content of shared_addr_table to the start address of CM0+ / CM4 shared memory. + memcpy(shared_memory_start, &shared_addr_table, sizeof(shared_addr_table)); + + // Init producer queue and consumer queue + + osMutexId_t queue_mutex = osMutexNew(&queue_mutex_attr); + MBED_ASSERT(queue_mutex != NULL); // TODO: Panic instead + + osSemaphoreId_t full_queue_sem = osSemaphoreNew(IPC_QUEUE_SEM_MAX_COUNT, IPC_QUEUE_SEM_INITIAL_COUNT, &full_sem_attr); + MBED_ASSERT(full_queue_sem != NULL); // TODO: Panic instead + + osSemaphoreId_t queue_read_sem = osSemaphoreNew(IPC_QUEUE_SEM_MAX_COUNT, IPC_QUEUE_SEM_INITIAL_COUNT, &read_sem_attr); + MBED_ASSERT(queue_read_sem != NULL); // TODO: Panic instead + + ipc_producer_queue_init(prod_queue, rx_queue_mem_ptr, queue_mutex, full_queue_sem); + ipc_consumer_queue_init(cons_queue, tx_queue_mem_ptr, queue_read_sem); +} + +void nspe_done(osSemaphoreId_t completion_sem_id) +{ + ipc_queue_item_t item_to_enqueue = { + .b = (uint32_t)(completion_sem_id) + }; + + ipc_queue_enqueue(prod_queue, item_to_enqueue); +} diff --git a/components/TARGET_PSA/spm/COMPONENT_SPM_MAILBOX/ipc_defs.h b/components/TARGET_PSA/spm/COMPONENT_SPM_MAILBOX/ipc_defs.h new file mode 100644 index 0000000..e4708c7 --- /dev/null +++ b/components/TARGET_PSA/spm/COMPONENT_SPM_MAILBOX/ipc_defs.h @@ -0,0 +1,48 @@ +/* Copyright (c) 2018 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef __PSA_MBED_IPC_DEFS_H__ +#define __PSA_MBED_IPC_DEFS_H__ + + +// Includes +// -------- + +#include + +#include "mbed_assert.h" + + +// Definitions +// ----------- + +#define IPC_RES_SEM_MAX_COUNT (1UL) // Maximum number of available tokens for an IPC result semaphore +#define IPC_RES_SEM_INITIAL_COUNT (0UL) // Initial number of available tokens for an IPC result semaphore + +#define ADDR_TABLE_MAGIC 0xDEADBEEF + + +// NOTE: STRUCT SIZE MUST BE 4 BYTES ALIGNED !! +typedef struct addr_table_t { + uint32_t magic; + uintptr_t tx_queue_ptr; + uintptr_t rx_queue_ptr; + +} addr_table_t; +MBED_STATIC_ASSERT((sizeof(addr_table_t) % sizeof(uint32_t) == 0), "addr_table_t: Struct size must be 4 bytes aligned!"); + + +#endif // __PSA_MBED_IPC_DEFS_H__ diff --git a/components/TARGET_PSA/spm/COMPONENT_SPM_MAILBOX/ipc_queue.c b/components/TARGET_PSA/spm/COMPONENT_SPM_MAILBOX/ipc_queue.c new file mode 100644 index 0000000..7989853 --- /dev/null +++ b/components/TARGET_PSA/spm/COMPONENT_SPM_MAILBOX/ipc_queue.c @@ -0,0 +1,119 @@ +/* Copyright (c) 2018 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +// Includes +// -------- + +#include "mbed_assert.h" +#include "ipc_queue.h" +#include "psa_defs.h" + + +// API Implmentation +// ----------------- + +void ipc_producer_queue_init( ipc_producer_queue_t *queue, + ipc_base_queue_t *base_queue_mem, + osMutexId_t mutex, + osSemaphoreId_t full_queue_sem + ) +{ + MBED_ASSERT(queue != NULL); + MBED_ASSERT(base_queue_mem != NULL); + MBED_ASSERT(base_queue_mem->magic == IPC_QUEUE_BASE_MAGIC); + MBED_ASSERT(mutex != NULL); + MBED_ASSERT(full_queue_sem != NULL); + + queue->magic = IPC_QUEUE_PRODUCER_MAGIC; + queue->read_idx = &(base_queue_mem->read_idx); + queue->write_idx = &(base_queue_mem->write_idx); + queue->data = base_queue_mem->data; + queue->mutex = mutex; + queue->full_queue_sem = full_queue_sem; +} + +void ipc_consumer_queue_init( ipc_consumer_queue_t *queue, + ipc_base_queue_t *base_queue_mem, + osSemaphoreId_t read_sem + ) +{ + MBED_ASSERT(queue != NULL); + MBED_ASSERT(base_queue_mem != NULL); + MBED_ASSERT(base_queue_mem->magic == IPC_QUEUE_BASE_MAGIC); + MBED_ASSERT(read_sem != NULL); + + queue->magic = IPC_QUEUE_CONSUMER_MAGIC; + queue->read_idx = &(base_queue_mem->read_idx); + queue->write_idx = &(base_queue_mem->write_idx); + queue->data = base_queue_mem->data; + queue->read_sem = read_sem; +} + +void ipc_queue_enqueue( ipc_producer_queue_t *queue, + ipc_queue_item_t queue_item + ) +{ + MBED_ASSERT(queue != NULL); + MBED_ASSERT(queue->magic == IPC_QUEUE_PRODUCER_MAGIC); + + osStatus_t os_status = osMutexAcquire(queue->mutex, osWaitForever); + MBED_ASSERT(osOK == os_status); + + // While queue is full, wait on full_queue_sem + while (((*(queue->write_idx) + 1) % IPC_QUEUE_SLOTS) == *(queue->read_idx)) { + os_status = osSemaphoreAcquire(queue->full_queue_sem, IPC_QUEUE_WAIT_ON_FULL_MS); + MBED_ASSERT((osOK == os_status) || (osErrorTimeout == os_status)); + } + + // Write data to queue (shallow copy) + (queue->data)[*(queue->write_idx)] = queue_item; + *(queue->write_idx) = ((*(queue->write_idx) + 1) % IPC_QUEUE_SLOTS); + + // If the queue was empty before the push, call the supplied CB function + if (((*(queue->read_idx) + 1) % IPC_QUEUE_SLOTS) == *(queue->write_idx)) { + on_new_item(); + } + + os_status = osMutexRelease(queue->mutex); + MBED_ASSERT(osOK == os_status); + PSA_UNUSED(os_status); +} + +void ipc_queue_drain( ipc_consumer_queue_t *queue) +{ + MBED_ASSERT(queue != NULL); + MBED_ASSERT(queue->magic == IPC_QUEUE_CONSUMER_MAGIC); + + // queue->read_sem is released when the queue becomes non empty + osStatus_t os_status = osSemaphoreAcquire(queue->read_sem, IPC_QUEUE_WAIT_ON_EMPTY_MS); + MBED_ASSERT((osOK == os_status) || (osErrorTimeout == os_status)); + PSA_UNUSED(os_status); + + // While queue is not empty, keep on popping queue items + while (*(queue->read_idx) != *(queue->write_idx)) { + + // Pop an item from the queue (shallow copy) + ipc_queue_item_t popped_item = (queue->data)[*(queue->read_idx)]; + *(queue->read_idx) = ((*(queue->read_idx) + 1) % IPC_QUEUE_SLOTS); + + // If queue was full before this pop, call the corresponding CB function + if (((*(queue->write_idx) + 2) % IPC_QUEUE_SLOTS) == *(queue->read_idx)) { + on_vacancy(); + } + + on_popped_item(popped_item); + } +} diff --git a/components/TARGET_PSA/spm/COMPONENT_SPM_MAILBOX/ipc_queue.h b/components/TARGET_PSA/spm/COMPONENT_SPM_MAILBOX/ipc_queue.h new file mode 100644 index 0000000..b82f288 --- /dev/null +++ b/components/TARGET_PSA/spm/COMPONENT_SPM_MAILBOX/ipc_queue.h @@ -0,0 +1,124 @@ +/* Copyright (c) 2018 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef __PSA_MBED_IPC_QUEUE_H__ +#define __PSA_MBED_IPC_QUEUE_H__ + + +// Includes +// -------- + +#include "mbed_assert.h" +#include "cmsis_os2.h" +#include "cmsis_compiler.h" +#include "rtx_lib.h" + + +// IPC Queue Definitions +// --------------------- + + +#ifndef IPC_QUEUE_SLOTS +#define IPC_QUEUE_SLOTS 4 +#endif + +// Maximum number of queue items equals (IPC_QUEUE_SLOTS - 1). +// Therefore we enforce a minimum of 2 IPC_QUEUE_SLOTS +#if IPC_QUEUE_SLOTS <= 1 +#undef IPC_QUEUE_SLOTS +#define IPC_QUEUE_SLOTS 2 +#endif + +#define IPC_QUEUE_BASE_MAGIC 0xCAFEBABE +#define IPC_QUEUE_PRODUCER_MAGIC 0xDAC0FFEE +#define IPC_QUEUE_CONSUMER_MAGIC 0xFACEB00C + +#define IPC_QUEUE_WAIT_ON_FULL_MS 500 +#define IPC_QUEUE_WAIT_ON_EMPTY_MS 500 + +#define IPC_QUEUE_SEM_MAX_COUNT (1UL) // Maximum number of available tokens for an IPC Queue semaphore +#define IPC_QUEUE_SEM_INITIAL_COUNT (0UL) // Initial number of available tokens for an IPC Queue semaphore + +// NOTE: STRUCT SIZE MUST BE 4 BYTES ALIGNED !! +typedef __PACKED_STRUCT ipc_queue_item_t { + uint32_t a; + uint32_t b; + uint32_t c; + +} __ALIGNED(4) ipc_queue_item_t; +MBED_STATIC_ASSERT((sizeof(ipc_queue_item_t) % sizeof(uint32_t) == 0), "ipc_queue_item_t: Struct size must be 4 bytes aligned!"); + + +// NOTE: STRUCT SIZE MUST BE 4 BYTES ALIGNED !! +typedef __PACKED_STRUCT ipc_base_queue_t { + uint32_t magic; + volatile uint32_t read_idx; + volatile uint32_t write_idx; + ipc_queue_item_t data[IPC_QUEUE_SLOTS]; + +} __ALIGNED(4) ipc_base_queue_t; +MBED_STATIC_ASSERT((sizeof(ipc_base_queue_t) % sizeof(uint32_t) == 0), "ipc_base_queue_t: Struct size must be 4 bytes aligned!"); + +typedef struct ipc_producer_queue_t { + uint32_t magic; + volatile __PACKED uint32_t *read_idx; + volatile __PACKED uint32_t *write_idx; + __PACKED ipc_queue_item_t *data; + osMutexId_t mutex; + osSemaphoreId_t full_queue_sem; + +} ipc_producer_queue_t; + +typedef struct ipc_consumer_queue_t { + uint32_t magic; + volatile __PACKED uint32_t *read_idx; + volatile __PACKED uint32_t *write_idx; + __PACKED ipc_queue_item_t *data; + osSemaphoreId_t read_sem; + +} ipc_consumer_queue_t; + + +// Event handling functions +// ------------------------ + +void on_new_item(void); +void on_vacancy(void); +void on_popped_item(ipc_queue_item_t item); + + +// IPC Queue API +// ------------- + +void ipc_producer_queue_init( ipc_producer_queue_t *queue, + ipc_base_queue_t *base_queue_mem, + osMutexId_t mutex, + osSemaphoreId_t full_queue_sem + ); + +void ipc_consumer_queue_init( ipc_consumer_queue_t *queue, + ipc_base_queue_t *base_queue_mem, + osSemaphoreId_t read_sem + ); + +void ipc_queue_enqueue( ipc_producer_queue_t *queue, + ipc_queue_item_t item_ptr + ); + +void ipc_queue_drain( ipc_consumer_queue_t *queue); + + +#endif // __PSA_MBED_IPC_QUEUE_H__ diff --git a/components/TARGET_PSA/spm/TARGET_IGNORE/neg_client_tests/NEG_CLIENT_PART1/psa_neg_client_part1_ifs.h b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_client_tests/NEG_CLIENT_PART1/psa_neg_client_part1_ifs.h new file mode 100644 index 0000000..303c9ac --- /dev/null +++ b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_client_tests/NEG_CLIENT_PART1/psa_neg_client_part1_ifs.h @@ -0,0 +1,28 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#ifndef PSA_NEG_CLIENT_PART1_PARTITION_ROT_SERVICES_H +#define PSA_NEG_CLIENT_PART1_PARTITION_ROT_SERVICES_H + +#define NEG_CLIENT_PART1_ROT_SRV1 0x00001A06 +#define NEG_CLIENT_PART1_ROT_SRV2 0x00001A07 + +#endif // PSA_NEG_CLIENT_PART1_PARTITION_ROT_SERVICES_H diff --git a/components/TARGET_PSA/spm/TARGET_IGNORE/neg_client_tests/NEG_CLIENT_PART1/psa_neg_client_part1_partition.c b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_client_tests/NEG_CLIENT_PART1/psa_neg_client_part1_partition.c new file mode 100644 index 0000000..5797378 --- /dev/null +++ b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_client_tests/NEG_CLIENT_PART1/psa_neg_client_part1_partition.c @@ -0,0 +1,109 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#include "cmsis.h" +#include "mbed_toolchain.h" /* For using MBED_ALIGN macro */ +#include "rtx_os.h" +#include "spm_panic.h" +#include "spm_internal.h" +#include "psa_neg_client_part1_partition.h" +#include "psa_neg_client_part1_ifs.h" + + +/* Threads stacks */ +MBED_ALIGN(8) uint8_t neg_client_part1_thread_stack[512] = {0}; + +/* Threads control blocks */ +osRtxThread_t neg_client_part1_thread_cb = {0}; + +/* Thread attributes - for thread initialization */ +osThreadAttr_t neg_client_part1_thread_attr = { + .name = "neg_client_part1", + .attr_bits = 0, + .cb_mem = &neg_client_part1_thread_cb, + .cb_size = sizeof(neg_client_part1_thread_cb), + .stack_mem = neg_client_part1_thread_stack, + .stack_size = 512, + .priority = osPriorityNormal, + .tz_module = 0, + .reserved = 0 + }; + +spm_rot_service_t neg_client_part1_rot_services[NEG_CLIENT_PART1_ROT_SRV_COUNT] = { + { + .sid = NEG_CLIENT_PART1_ROT_SRV1, + .mask = NEG_CLIENT_PART1_ROT_SRV1_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, + { + .sid = NEG_CLIENT_PART1_ROT_SRV2, + .mask = NEG_CLIENT_PART1_ROT_SRV2_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_STRICT, + .allow_nspe = false, + .queue = { + .head = NULL, + .tail = NULL + } + }, +}; + + +static osRtxMutex_t neg_client_part1_mutex = {0}; +static const osMutexAttr_t neg_client_part1_mutex_attr = { + .name = "neg_client_part1_mutex", + .attr_bits = osMutexRecursive | osMutexPrioInherit | osMutexRobust, + .cb_mem = &neg_client_part1_mutex, + .cb_size = sizeof(neg_client_part1_mutex), +}; + + +extern void server_main1(void *ptr); + +void neg_client_part1_init(spm_partition_t *partition) +{ + if (NULL == partition) { + SPM_PANIC("partition is NULL!\n"); + } + + partition->mutex = osMutexNew(&neg_client_part1_mutex_attr); + if (NULL == partition->mutex) { + SPM_PANIC("Failed to create mutex for secure partition neg_client_part1!\n"); + } + + for (uint32_t i = 0; i < NEG_CLIENT_PART1_ROT_SRV_COUNT; ++i) { + neg_client_part1_rot_services[i].partition = partition; + } + partition->rot_services = neg_client_part1_rot_services; + + partition->thread_id = osThreadNew(server_main1, NULL, &neg_client_part1_thread_attr); + if (NULL == partition->thread_id) { + SPM_PANIC("Failed to create start main thread of partition neg_client_part1!\n"); + } +} diff --git a/components/TARGET_PSA/spm/TARGET_IGNORE/neg_client_tests/NEG_CLIENT_PART1/psa_neg_client_part1_partition.h b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_client_tests/NEG_CLIENT_PART1/psa_neg_client_part1_partition.h new file mode 100644 index 0000000..9a02e5c --- /dev/null +++ b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_client_tests/NEG_CLIENT_PART1/psa_neg_client_part1_partition.h @@ -0,0 +1,55 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#ifndef PSA_NEG_CLIENT_PART1_PARTITION_H +#define PSA_NEG_CLIENT_PART1_PARTITION_H + +#define NEG_CLIENT_PART1_ID 5 + +#define NEG_CLIENT_PART1_ROT_SRV_COUNT (2UL) +#define NEG_CLIENT_PART1_EXT_ROT_SRV_COUNT (0UL) + +/* NEG_CLIENT_PART1 event flags */ +#define NEG_CLIENT_PART1_RESERVED1_POS (1UL) +#define NEG_CLIENT_PART1_RESERVED1_MSK (1UL << NEG_CLIENT_PART1_RESERVED1_POS) + +#define NEG_CLIENT_PART1_RESERVED2_POS (2UL) +#define NEG_CLIENT_PART1_RESERVED2_MSK (1UL << NEG_CLIENT_PART1_RESERVED2_POS) + + + +#define NEG_CLIENT_PART1_ROT_SRV1_MSK_POS (4UL) +#define NEG_CLIENT_PART1_ROT_SRV1_MSK (1UL << NEG_CLIENT_PART1_ROT_SRV1_MSK_POS) +#define NEG_CLIENT_PART1_ROT_SRV2_MSK_POS (5UL) +#define NEG_CLIENT_PART1_ROT_SRV2_MSK (1UL << NEG_CLIENT_PART1_ROT_SRV2_MSK_POS) + +#define NEG_CLIENT_PART1_WAIT_ANY_SID_MSK (\ + NEG_CLIENT_PART1_ROT_SRV1_MSK | \ + NEG_CLIENT_PART1_ROT_SRV2_MSK) + +/* +#define NEG_CLIENT_PART1_WAIT_ANY_MSK (\ + NEG_CLIENT_PART1_WAIT_ANY_SID_MSK) | \ + PSA_DOORBELL) +*/ + + +#endif // PSA_NEG_CLIENT_PART1_PARTITION_H diff --git a/components/TARGET_PSA/spm/TARGET_IGNORE/neg_client_tests/neg_ipc_tests.cpp b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_client_tests/neg_ipc_tests.cpp new file mode 100644 index 0000000..372336e --- /dev/null +++ b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_client_tests/neg_ipc_tests.cpp @@ -0,0 +1,357 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if !ENABLE_SPM + #error [NOT_SUPPORTED] SPM is not supported on this platform +#endif + +#include "cmsis_os2.h" +#include "mbed.h" +#include "greentea-client/test_env.h" +#include "unity.h" +#include "utest.h" +#include "rtos.h" +#include "spm_client.h" +#include "psa_neg_client_part1_ifs.h" +#include "neg_tests.h" + + +#define MINOR_VER 5 +#define CLIENT_RSP_BUF_SIZE 128 +#define OFFSET_POS 1 +#define INVALID_SID (NEG_CLIENT_PART1_ROT_SRV1 + 30) +#define INVALID_MINOR (MINOR_VER + 10) +#define INVALID_TX_LEN (PSA_MAX_IOVEC + 1) + + +using namespace utest::v1; + +Semaphore test_sem(0); +bool error_thrown = false; +uint8_t response_buf[CLIENT_RSP_BUF_SIZE]; +extern "C" void spm_reboot(void); + +void error(const char* format, ...) +{ + error_thrown = true; + osStatus status = test_sem.release(); + MBED_ASSERT(status == osOK); + PSA_UNUSED(status); + while(1); +} + +/* ------------------------------------- Functions ----------------------------------- */ + +static psa_handle_t negative_client_ipc_tests_connect(uint32_t sid, uint32_t minor_version) +{ + psa_handle_t handle = psa_connect(sid, minor_version); + TEST_ASSERT_TRUE(handle > 0); + return handle; +} + +static void negative_client_ipc_tests_call( psa_handle_t handle, + psa_invec_t *iovec_temp, + size_t tx_len, + size_t rx_len + ) +{ + error_t status = PSA_SUCCESS; + memset(response_buf, 0, sizeof(response_buf)); + psa_outvec_t resp = { response_buf, rx_len }; + + status = psa_call(handle, iovec_temp, tx_len, &resp, 1); + + TEST_ASSERT_EQUAL_INT(PSA_SUCCESS, status); +} + +static void negative_client_ipc_tests_close(psa_handle_t handle) +{ + psa_close(handle); + + // Wait for psa_close to finish on server side + osDelay(50); +} + +//Testing client call with an invalid SID +void client_connect_invalid_sid() +{ + psa_connect( INVALID_SID, + MINOR_VER + ); + + TEST_FAIL_MESSAGE("client_connect_invalid_sid negative test failed"); +} + +//Testing client connect version policy is RELAXED and minor version is bigger than the minimum version +void client_connect_invalid_pol_ver_RELAXED() +{ + psa_connect( NEG_CLIENT_PART1_ROT_SRV1, //NEG_CLIENT_PART1_ROT_SRV1 should have RELAXED policy + INVALID_MINOR + ); + + TEST_FAIL_MESSAGE("client_connect_invalid_pol_ver_RELAXED negative test failed"); +} + +//Testing client connect version policy is STRICT and requeted version is higher than the minimum version +void client_connect_invalid_high_pol_ver_STRICT() +{ + psa_connect( NEG_CLIENT_PART1_ROT_SRV2, //NEG_CLIENT_PART1_ROT_SRV2 should have STRICT policy + INVALID_MINOR + ); + + TEST_FAIL_MESSAGE("client_connect_invalid_high_pol_ver_STRICT negative test failed"); +} + +//Testing client connect version policy is STRICT and requeted version equales the minimum version +void client_connect_invalid_equal_pol_ver_STRICT() +{ + psa_connect( NEG_CLIENT_PART1_ROT_SRV2, //NEG_CLIENT_PART1_ROT_SRV2 should have STRICT policy + MINOR_VER + ); + + TEST_FAIL_MESSAGE("client_connect_invalid_equal_pol_ver_STRICT negative test failed"); +} + +//Testing client call num of iovecs (tx_len + rx_len) is bigger than max value allowed +void client_call_invalid_iovecs_len() +{ + psa_handle_t handle = 0; + + handle = negative_client_ipc_tests_connect(NEG_CLIENT_PART1_ROT_SRV1, MINOR_VER); + + uint8_t data[2] = {1, 0}; + + psa_invec_t invec_temp[PSA_MAX_IOVEC] = { + {data, sizeof(data)}, + {data, sizeof(data)}, + {data, sizeof(data)}, + {data, sizeof(data)} + }; + + psa_outvec_t outvec_temp[1] = { + {data, sizeof(data)} + }; + + // PSA_MAX_IOVEC invecs + 1 outvec + psa_call(handle, invec_temp, PSA_MAX_IOVEC, outvec_temp, 1); + + TEST_FAIL_MESSAGE("client_call_invalid_iovecs_len negative test failed"); +} + +//Testing client call return buffer (rx_buff) is NULL and return buffer len is not 0 +void client_call_rx_buff_null_rx_len_not_zero() +{ + psa_handle_t handle = 0; + uint8_t data[2] = {1, 0}; + psa_invec_t iovec_temp[3] = { + {data, sizeof(data)}, + {data, sizeof(data)}, + {data, sizeof(data)} + }; + + handle = negative_client_ipc_tests_connect(NEG_CLIENT_PART1_ROT_SRV1, MINOR_VER); + + psa_call(handle, iovec_temp, 3, NULL, 1); + + TEST_FAIL_MESSAGE("client_call_rx_buff_null_rx_len_not_zero negative test failed"); +} + +//Testing client call iovecs pointer is NULL and num of iovecs is not 0 +void client_call_iovecs_null_tx_len_not_zero() +{ + psa_handle_t handle = 0; + + handle = negative_client_ipc_tests_connect(NEG_CLIENT_PART1_ROT_SRV1, MINOR_VER); + memset(response_buf, 0, CLIENT_RSP_BUF_SIZE); + psa_outvec_t resp = { response_buf, CLIENT_RSP_BUF_SIZE }; + + psa_call(handle, NULL, 2, &resp, 1); + + TEST_FAIL_MESSAGE("client_call_iovecs_null_tx_len_not_zero negative test failed"); +} + +//Testing client call iovec base pointer is NULL and iovec size is not 0 +void client_call_iovec_base_null_len_not_zero() +{ + negative_client_ipc_tests_connect(NEG_CLIENT_PART1_ROT_SRV1, MINOR_VER); + + uint8_t data[2] = {1, 0}; + + psa_invec_t iovec_temp[PSA_MAX_IOVEC - 1] = { + {NULL, sizeof(data)}, + {data, sizeof(data)}, + {data, sizeof(data)} + }; + + negative_client_ipc_tests_call(PSA_NULL_HANDLE, iovec_temp, PSA_MAX_IOVEC - 1, 0); + + TEST_FAIL_MESSAGE("client_call_iovec_base_null_len_not_zero negative test failed"); +} + +//Testing client call handle does not exist on the platform +void client_call_invalid_handle() +{ + psa_handle_t handle = 0, invalid_handle = 0; + + handle = negative_client_ipc_tests_connect(NEG_CLIENT_PART1_ROT_SRV1, MINOR_VER); + invalid_handle = handle + 10; + + uint8_t data[2] = {1, 0}; + + psa_invec_t iovec_temp[PSA_MAX_IOVEC - 1] = { + {data, sizeof(data)}, + {data, sizeof(data)}, + {data, sizeof(data)} + }; + + negative_client_ipc_tests_call(invalid_handle, iovec_temp, PSA_MAX_IOVEC - 1, 0); + + TEST_FAIL_MESSAGE("client_call_invalid_handle negative test failed"); +} + +//Testing client call handle is null (PSA_NULL_HANDLE) +void client_call_handle_is_null() +{ + negative_client_ipc_tests_connect(NEG_CLIENT_PART1_ROT_SRV1, MINOR_VER); + + uint8_t data[2] = {1, 0}; + + psa_invec_t iovec_temp[PSA_MAX_IOVEC - 1] = { + {data, sizeof(data)}, + {data, sizeof(data)}, + {data, sizeof(data)} + }; + + negative_client_ipc_tests_call(PSA_NULL_HANDLE, iovec_temp, PSA_MAX_IOVEC - 1, 0); + + TEST_FAIL_MESSAGE("client_call_handle_is_null negative test failed"); +} + +//Testing client close handle does not exist on the platform +void client_close_invalid_handle() +{ + psa_handle_t handle = 0, invalid_handle = 0; + + handle = negative_client_ipc_tests_connect(NEG_CLIENT_PART1_ROT_SRV1, MINOR_VER); + invalid_handle = handle + 10; + + uint8_t data[2] = {1, 0}; + + psa_invec_t iovec_temp[PSA_MAX_IOVEC - 1] = { + {data, sizeof(data)}, + {data, sizeof(data)}, + {data, sizeof(data)} + }; + + negative_client_ipc_tests_call(handle, iovec_temp, PSA_MAX_IOVEC - 1, 0); + + negative_client_ipc_tests_close(invalid_handle); + + TEST_FAIL_MESSAGE("client_close_invalid_handle negative test failed"); +} + +void client_call_buffer_wrap_around() +{ + psa_handle_t handle = 0; + psa_invec_t iovec_temp = { (void *)0x80000000, UINT32_MAX }; + + handle = negative_client_ipc_tests_connect(NEG_CLIENT_PART1_ROT_SRV1, MINOR_VER); + psa_call(handle, &iovec_temp, 1, NULL, 0); + + TEST_FAIL_MESSAGE("client_call_buffer_wrap_around negative test failed"); +} + +void client_connect_not_allowed_from_nspe() +{ + psa_connect(NEG_CLIENT_PART1_ROT_SRV2, 5); + + TEST_FAIL_MESSAGE("client_connect_not_allowed_from_nspe negative test failed"); +} + +void client_call_excess_outvec() +{ + psa_handle_t handle = 0; + uint8_t data[2] = {1, 0}; + psa_outvec_t iovec_temp[PSA_MAX_IOVEC + 1] = { + {data, sizeof(data)}, + {data, sizeof(data)}, + {data, sizeof(data)}, + {data, sizeof(data)}, + {data, sizeof(data)} + }; + + handle = negative_client_ipc_tests_connect(NEG_CLIENT_PART1_ROT_SRV1, MINOR_VER); + psa_call(handle, NULL, 0, iovec_temp, PSA_MAX_IOVEC + 1); + + TEST_FAIL_MESSAGE("client_call_excess_outvec negative test failed"); +} + +PSA_NEG_TEST(client_connect_invalid_sid) +PSA_NEG_TEST(client_connect_invalid_pol_ver_RELAXED) +PSA_NEG_TEST(client_connect_invalid_high_pol_ver_STRICT) +PSA_NEG_TEST(client_connect_invalid_equal_pol_ver_STRICT) +PSA_NEG_TEST(client_call_invalid_iovecs_len) +PSA_NEG_TEST(client_call_rx_buff_null_rx_len_not_zero) +PSA_NEG_TEST(client_call_iovecs_null_tx_len_not_zero) +PSA_NEG_TEST(client_call_iovec_base_null_len_not_zero) +PSA_NEG_TEST(client_call_invalid_handle) +PSA_NEG_TEST(client_call_handle_is_null) +PSA_NEG_TEST(client_close_invalid_handle) +PSA_NEG_TEST(client_call_buffer_wrap_around) +PSA_NEG_TEST(client_connect_not_allowed_from_nspe) +PSA_NEG_TEST(client_call_excess_outvec) + +utest::v1::status_t spm_case_teardown(const Case *const source, const size_t passed, const size_t failed, const failure_t reason) +{ + spm_reboot(); + error_thrown = false; + return greentea_case_teardown_handler(source, passed, failed, reason); +} + +#define SPM_UTEST_CASE(desc, test) Case(desc, PSA_NEG_TEST_NAME(test), spm_case_teardown) + +// Test cases +Case cases[] = { + SPM_UTEST_CASE("Testing client connect invalid sid", client_connect_invalid_sid), + SPM_UTEST_CASE("Testing client connect version policy RELAXED invalid minor", client_connect_invalid_pol_ver_RELAXED), + SPM_UTEST_CASE("Testing client connect version policy STRICT high minor", client_connect_invalid_high_pol_ver_STRICT), + SPM_UTEST_CASE("Testing client connect version policy STRICT equal minor", client_connect_invalid_equal_pol_ver_STRICT), + SPM_UTEST_CASE("Testing client call invalid num of iovecs (tx_len + rx_len)", client_call_invalid_iovecs_len), + SPM_UTEST_CASE("Testing client call rx_buff is NULL rx_len is not 0", client_call_rx_buff_null_rx_len_not_zero), + SPM_UTEST_CASE("Testing client call iovecs is NULL tx_len is not 0", client_call_iovecs_null_tx_len_not_zero), + SPM_UTEST_CASE("Testing client call iovec base NULL while iovec len not 0", client_call_iovec_base_null_len_not_zero), + SPM_UTEST_CASE("Testing client call handle does not exist", client_call_invalid_handle), + SPM_UTEST_CASE("Testing client call handle is PSA_NULL_HANDLE", client_call_handle_is_null), + SPM_UTEST_CASE("Testing client close handle does not exist", client_close_invalid_handle), + SPM_UTEST_CASE("Testing client call with buffer wrap-around", client_call_buffer_wrap_around), + SPM_UTEST_CASE("Testing client connect to non-NSPE ROT_SRV", client_connect_not_allowed_from_nspe), + SPM_UTEST_CASE("Testing client call with too much outvec's", client_call_excess_outvec) +}; + +utest::v1::status_t spm_setup(const size_t number_of_cases) +{ +#ifndef NO_GREENTEA + GREENTEA_SETUP(60, "default_auto"); +#endif + return greentea_test_setup_handler(number_of_cases); +} + +Specification specification(spm_setup, cases); + +int main() +{ + Harness::run(specification); + return 0; +} diff --git a/components/TARGET_PSA/spm/TARGET_IGNORE/neg_client_tests/neg_tests.h b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_client_tests/neg_tests.h new file mode 100644 index 0000000..d2b7aff --- /dev/null +++ b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_client_tests/neg_tests.h @@ -0,0 +1,32 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define PSA_NEG_TEST_NAME(test_name) psa_neg_test_ ## test_name + +#define PSA_NEG_TEST(test_name) \ +void PSA_NEG_TEST_NAME(test_name)() \ +{ \ + osStatus status = osOK; \ + Thread T1; \ + \ + status = T1.start(test_name); \ + TEST_ASSERT_TRUE(status == osOK); \ + test_sem.wait(osWaitForever); \ + TEST_ASSERT_TRUE(error_thrown); \ + \ + status = T1.terminate(); \ + TEST_ASSERT_TRUE(status == osOK); \ +} \ + diff --git a/components/TARGET_PSA/spm/TARGET_IGNORE/neg_client_tests/part1_psa.json b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_client_tests/part1_psa.json new file mode 100644 index 0000000..7ab3099 --- /dev/null +++ b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_client_tests/part1_psa.json @@ -0,0 +1,29 @@ +{ + "name": "NEG_CLIENT_PART1", + "type": "APPLICATION-ROT", + "priority": "NORMAL", + "id": "0x00000005", + "entry_point": "server_main1", + "stack_size": "0x200", + "heap_size": "0x400", + "services": [{ + "name": "NEG_CLIENT_PART1_ROT_SRV1", + "identifier": "0x00001A06", + "signal": "NEG_CLIENT_PART1_ROT_SRV1_MSK", + "non_secure_clients": true, + "minor_version": 5, + "minor_policy": "RELAXED" + }, + { + "name": "NEG_CLIENT_PART1_ROT_SRV2", + "identifier": "0x00001A07", + "signal": "NEG_CLIENT_PART1_ROT_SRV2_MSK", + "non_secure_clients": false, + "minor_version": 5, + "minor_policy": "STRICT" + } + ], + "source_files": [ + "server1.c" + ] +} diff --git a/components/TARGET_PSA/spm/TARGET_IGNORE/neg_client_tests/psa_setup.c b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_client_tests/psa_setup.c new file mode 100644 index 0000000..cfe824a --- /dev/null +++ b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_client_tests/psa_setup.c @@ -0,0 +1,64 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#include "spm_panic.h" +#include "spm_internal.h" +#include "handles_manager.h" +#include "cmsis.h" +#include "psa_neg_client_part1_partition.h" + + +spm_partition_t g_partitions[1] = { + { + .partition_id = NEG_CLIENT_PART1_ID, + .thread_id = 0, + .flags_rot_srv = NEG_CLIENT_PART1_WAIT_ANY_SID_MSK, + .flags_interrupts = 0, + .rot_services = NULL, + .rot_services_count = NEG_CLIENT_PART1_ROT_SRV_COUNT, + .extern_sids = NULL, + .extern_sids_count = NEG_CLIENT_PART1_EXT_ROT_SRV_COUNT, + .irq_mapper = NULL, + }, +}; + +/* Check all the defined memory regions for overlapping. */ + +/* A list of all the memory regions. */ +const mem_region_t *mem_regions = NULL; + +const uint32_t mem_region_count = 0; + +// forward declaration of partition initializers +void neg_client_part1_init(spm_partition_t *partition); + +uint32_t init_partitions(spm_partition_t **partitions) +{ + if (NULL == partitions) { + SPM_PANIC("partitions is NULL!\n"); + } + + neg_client_part1_init(&(g_partitions[0])); + + *partitions = g_partitions; + return 1; +} + diff --git a/components/TARGET_PSA/spm/TARGET_IGNORE/neg_client_tests/server1.c b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_client_tests/server1.c new file mode 100644 index 0000000..2c94f60 --- /dev/null +++ b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_client_tests/server1.c @@ -0,0 +1,58 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "spm_server.h" +#include "spm_panic.h" +#include "psa_neg_client_part1_partition.h" + +#define msg_buff_SIZE 128 +char msg_buff[msg_buff_SIZE]; + +void server_main1(void *ptr) +{ + uint32_t signals = 0; + memset(msg_buff, 0, sizeof(msg_buff)); + while (true) { + signals = psa_wait_any(PSA_BLOCK); + if (signals & NEG_CLIENT_PART1_ROT_SRV1_MSK) { + psa_msg_t msg = {0}; + psa_get(NEG_CLIENT_PART1_ROT_SRV1_MSK, &msg); + switch (msg.type) { + case PSA_IPC_CONNECT: + case PSA_IPC_DISCONNECT: + break; + case PSA_IPC_CALL: { + memset(msg_buff, 0, msg_buff_SIZE); + uint32_t bytes_read = 0; + for (size_t i = 0; i < PSA_MAX_IOVEC; i++) { + bytes_read = psa_read(msg.handle, i, msg_buff + bytes_read, msg.in_size[i]); + } + + if (msg.out_size[0] > 0) { + psa_write(msg.handle, 0, msg_buff, bytes_read); + } + break; + } + default: + SPM_PANIC("Unknown message type"); + break; + } + + psa_reply(msg.handle, PSA_SUCCESS); + } + } +} diff --git a/components/TARGET_PSA/spm/TARGET_IGNORE/neg_client_tests/spm_reboot.c b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_client_tests/spm_reboot.c new file mode 100644 index 0000000..35865b9 --- /dev/null +++ b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_client_tests/spm_reboot.c @@ -0,0 +1,67 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "spm_internal.h" + +extern spm_db_t g_spm; +extern psa_handle_item_t g_channels_handle_storage[]; +extern psa_handle_item_t g_messages_handle_storage[]; +extern spm_ipc_channel_t g_channel_data[]; +extern spm_active_msg_t g_active_messages_data[]; + +void psa_spm_init(void); + +void spm_reboot(void) +{ + osStatus_t status; + + for (uint32_t i = 0; i < g_spm.partition_count; ++i) { + status = osThreadTerminate(g_spm.partitions[i].thread_id); + MBED_ASSERT(status == osOK); + status = osMutexDelete(g_spm.partitions[i].mutex); + MBED_ASSERT(status == osOK); + + for (uint32_t j = 0; j < g_spm.partitions[i].rot_services_count; ++j) { + g_spm.partitions[i].rot_services[j].partition = NULL; + g_spm.partitions[i].rot_services[j].queue.head = NULL; + g_spm.partitions[i].rot_services[j].queue.tail = NULL; + } + + g_spm.partitions[i].rot_services = NULL; + g_spm.partitions[i].mutex = NULL; + g_spm.partitions[i].thread_id = NULL; + } + + status = osMemoryPoolDelete(g_spm.channel_mem_pool); + MBED_ASSERT(status == osOK); + status = osMemoryPoolDelete(g_spm.active_messages_mem_pool); + MBED_ASSERT(status == osOK); + memset(g_channels_handle_storage, 0, (sizeof(psa_handle_item_t) * MBED_CONF_SPM_IPC_MAX_NUM_OF_CHANNELS)); + memset(g_messages_handle_storage, 0, (sizeof(psa_handle_item_t) * MBED_CONF_SPM_IPC_MAX_NUM_OF_MESSAGES)); + memset(g_channel_data, 0, (sizeof(spm_ipc_channel_t) * MBED_CONF_SPM_IPC_MAX_NUM_OF_CHANNELS)); + memset(g_active_messages_data, 0, (sizeof(spm_active_msg_t) * MBED_CONF_SPM_IPC_MAX_NUM_OF_MESSAGES)); + memset(&g_spm, 0, sizeof(g_spm)); + + g_spm.channels_handle_mgr.handle_generator = PSA_HANDLE_MGR_INVALID_HANDLE; + g_spm.channels_handle_mgr.pool_size = MBED_CONF_SPM_IPC_MAX_NUM_OF_CHANNELS; + g_spm.channels_handle_mgr.handles_pool = g_channels_handle_storage; + g_spm.messages_handle_mgr.handle_generator = PSA_HANDLE_MGR_INVALID_HANDLE; + g_spm.messages_handle_mgr.pool_size = MBED_CONF_SPM_IPC_MAX_NUM_OF_MESSAGES; + g_spm.messages_handle_mgr.handles_pool = g_messages_handle_storage; + + psa_spm_init(); + PSA_UNUSED(status); +} diff --git a/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/NEG_DUAL_PART1/psa_neg_dual_part1_ifs.h b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/NEG_DUAL_PART1/psa_neg_dual_part1_ifs.h new file mode 100644 index 0000000..f32df2b --- /dev/null +++ b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/NEG_DUAL_PART1/psa_neg_dual_part1_ifs.h @@ -0,0 +1,27 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#ifndef PSA_NEG_DUAL_PART1_PARTITION_ROT_SERVICES_H +#define PSA_NEG_DUAL_PART1_PARTITION_ROT_SERVICES_H + +#define PART1_CALL_NON_EXISTS_EXTERN_SID 0x00001A08 + +#endif // PSA_NEG_DUAL_PART1_PARTITION_ROT_SERVICES_H diff --git a/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/NEG_DUAL_PART1/psa_neg_dual_part1_partition.c b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/NEG_DUAL_PART1/psa_neg_dual_part1_partition.c new file mode 100644 index 0000000..01eea0a --- /dev/null +++ b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/NEG_DUAL_PART1/psa_neg_dual_part1_partition.c @@ -0,0 +1,97 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#include "cmsis.h" +#include "mbed_toolchain.h" /* For using MBED_ALIGN macro */ +#include "rtx_os.h" +#include "spm_panic.h" +#include "spm_internal.h" +#include "psa_neg_dual_part1_partition.h" +#include "psa_neg_dual_part1_ifs.h" + + +/* Threads stacks */ +MBED_ALIGN(8) uint8_t neg_dual_part1_thread_stack[512] = {0}; + +/* Threads control blocks */ +osRtxThread_t neg_dual_part1_thread_cb = {0}; + +/* Thread attributes - for thread initialization */ +osThreadAttr_t neg_dual_part1_thread_attr = { + .name = "neg_dual_part1", + .attr_bits = 0, + .cb_mem = &neg_dual_part1_thread_cb, + .cb_size = sizeof(neg_dual_part1_thread_cb), + .stack_mem = neg_dual_part1_thread_stack, + .stack_size = 512, + .priority = osPriorityNormal, + .tz_module = 0, + .reserved = 0 + }; + +spm_rot_service_t neg_dual_part1_rot_services[NEG_DUAL_PART1_ROT_SRV_COUNT] = { + { + .sid = PART1_CALL_NON_EXISTS_EXTERN_SID, + .mask = PART1_CALL_NON_EXISTS_EXTERN_SID_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, +}; + + +static osRtxMutex_t neg_dual_part1_mutex = {0}; +static const osMutexAttr_t neg_dual_part1_mutex_attr = { + .name = "neg_dual_part1_mutex", + .attr_bits = osMutexRecursive | osMutexPrioInherit | osMutexRobust, + .cb_mem = &neg_dual_part1_mutex, + .cb_size = sizeof(neg_dual_part1_mutex), +}; + + +extern void server_main1(void *ptr); + +void neg_dual_part1_init(spm_partition_t *partition) +{ + if (NULL == partition) { + SPM_PANIC("partition is NULL!\n"); + } + + partition->mutex = osMutexNew(&neg_dual_part1_mutex_attr); + if (NULL == partition->mutex) { + SPM_PANIC("Failed to create mutex for secure partition neg_dual_part1!\n"); + } + + for (uint32_t i = 0; i < NEG_DUAL_PART1_ROT_SRV_COUNT; ++i) { + neg_dual_part1_rot_services[i].partition = partition; + } + partition->rot_services = neg_dual_part1_rot_services; + + partition->thread_id = osThreadNew(server_main1, NULL, &neg_dual_part1_thread_attr); + if (NULL == partition->thread_id) { + SPM_PANIC("Failed to create start main thread of partition neg_dual_part1!\n"); + } +} diff --git a/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/NEG_DUAL_PART1/psa_neg_dual_part1_partition.h b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/NEG_DUAL_PART1/psa_neg_dual_part1_partition.h new file mode 100644 index 0000000..40b1167 --- /dev/null +++ b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/NEG_DUAL_PART1/psa_neg_dual_part1_partition.h @@ -0,0 +1,52 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#ifndef PSA_NEG_DUAL_PART1_PARTITION_H +#define PSA_NEG_DUAL_PART1_PARTITION_H + +#define NEG_DUAL_PART1_ID 6 + +#define NEG_DUAL_PART1_ROT_SRV_COUNT (1UL) +#define NEG_DUAL_PART1_EXT_ROT_SRV_COUNT (0UL) + +/* NEG_DUAL_PART1 event flags */ +#define NEG_DUAL_PART1_RESERVED1_POS (1UL) +#define NEG_DUAL_PART1_RESERVED1_MSK (1UL << NEG_DUAL_PART1_RESERVED1_POS) + +#define NEG_DUAL_PART1_RESERVED2_POS (2UL) +#define NEG_DUAL_PART1_RESERVED2_MSK (1UL << NEG_DUAL_PART1_RESERVED2_POS) + + + +#define PART1_CALL_NON_EXISTS_EXTERN_SID_MSK_POS (4UL) +#define PART1_CALL_NON_EXISTS_EXTERN_SID_MSK (1UL << PART1_CALL_NON_EXISTS_EXTERN_SID_MSK_POS) + +#define NEG_DUAL_PART1_WAIT_ANY_SID_MSK (\ + PART1_CALL_NON_EXISTS_EXTERN_SID_MSK) + +/* +#define NEG_DUAL_PART1_WAIT_ANY_MSK (\ + NEG_DUAL_PART1_WAIT_ANY_SID_MSK) | \ + PSA_DOORBELL) +*/ + + +#endif // PSA_NEG_DUAL_PART1_PARTITION_H diff --git a/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/NEG_DUAL_PART2/psa_neg_dual_part2_ifs.h b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/NEG_DUAL_PART2/psa_neg_dual_part2_ifs.h new file mode 100644 index 0000000..ffe0b9b --- /dev/null +++ b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/NEG_DUAL_PART2/psa_neg_dual_part2_ifs.h @@ -0,0 +1,28 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#ifndef PSA_NEG_DUAL_PART2_PARTITION_ROT_SERVICES_H +#define PSA_NEG_DUAL_PART2_PARTITION_ROT_SERVICES_H + +#define DUMMY 0x00001A09 +#define PART2_CALL_INSIDE_PARTITION 0x00001A0A + +#endif // PSA_NEG_DUAL_PART2_PARTITION_ROT_SERVICES_H diff --git a/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/NEG_DUAL_PART2/psa_neg_dual_part2_partition.c b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/NEG_DUAL_PART2/psa_neg_dual_part2_partition.c new file mode 100644 index 0000000..4f27cbe --- /dev/null +++ b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/NEG_DUAL_PART2/psa_neg_dual_part2_partition.c @@ -0,0 +1,115 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#include "cmsis.h" +#include "mbed_toolchain.h" /* For using MBED_ALIGN macro */ +#include "rtx_os.h" +#include "spm_panic.h" +#include "spm_internal.h" +#include "psa_neg_dual_part2_partition.h" +#include "psa_neg_dual_part2_ifs.h" +#include "psa_neg_dual_part1_ifs.h" + + +/* Threads stacks */ +MBED_ALIGN(8) uint8_t neg_dual_part2_thread_stack[512] = {0}; + +/* Threads control blocks */ +osRtxThread_t neg_dual_part2_thread_cb = {0}; + +/* Thread attributes - for thread initialization */ +osThreadAttr_t neg_dual_part2_thread_attr = { + .name = "neg_dual_part2", + .attr_bits = 0, + .cb_mem = &neg_dual_part2_thread_cb, + .cb_size = sizeof(neg_dual_part2_thread_cb), + .stack_mem = neg_dual_part2_thread_stack, + .stack_size = 512, + .priority = osPriorityNormal, + .tz_module = 0, + .reserved = 0 + }; + +spm_rot_service_t neg_dual_part2_rot_services[NEG_DUAL_PART2_ROT_SRV_COUNT] = { + { + .sid = DUMMY, + .mask = DUMMY_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = false, + .queue = { + .head = NULL, + .tail = NULL + } + }, + { + .sid = PART2_CALL_INSIDE_PARTITION, + .mask = PART2_CALL_INSIDE_PARTITION_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, +}; + +/* External SIDs used by NEG_DUAL_PART2 */ +const uint32_t neg_dual_part2_external_sids[1] = +{ + PART1_CALL_NON_EXISTS_EXTERN_SID, +}; + +static osRtxMutex_t neg_dual_part2_mutex = {0}; +static const osMutexAttr_t neg_dual_part2_mutex_attr = { + .name = "neg_dual_part2_mutex", + .attr_bits = osMutexRecursive | osMutexPrioInherit | osMutexRobust, + .cb_mem = &neg_dual_part2_mutex, + .cb_size = sizeof(neg_dual_part2_mutex), +}; + + +extern void server_main2(void *ptr); + +void neg_dual_part2_init(spm_partition_t *partition) +{ + if (NULL == partition) { + SPM_PANIC("partition is NULL!\n"); + } + + partition->mutex = osMutexNew(&neg_dual_part2_mutex_attr); + if (NULL == partition->mutex) { + SPM_PANIC("Failed to create mutex for secure partition neg_dual_part2!\n"); + } + + for (uint32_t i = 0; i < NEG_DUAL_PART2_ROT_SRV_COUNT; ++i) { + neg_dual_part2_rot_services[i].partition = partition; + } + partition->rot_services = neg_dual_part2_rot_services; + + partition->thread_id = osThreadNew(server_main2, NULL, &neg_dual_part2_thread_attr); + if (NULL == partition->thread_id) { + SPM_PANIC("Failed to create start main thread of partition neg_dual_part2!\n"); + } +} diff --git a/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/NEG_DUAL_PART2/psa_neg_dual_part2_partition.h b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/NEG_DUAL_PART2/psa_neg_dual_part2_partition.h new file mode 100644 index 0000000..6a2d9fc --- /dev/null +++ b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/NEG_DUAL_PART2/psa_neg_dual_part2_partition.h @@ -0,0 +1,55 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#ifndef PSA_NEG_DUAL_PART2_PARTITION_H +#define PSA_NEG_DUAL_PART2_PARTITION_H + +#define NEG_DUAL_PART2_ID 7 + +#define NEG_DUAL_PART2_ROT_SRV_COUNT (2UL) +#define NEG_DUAL_PART2_EXT_ROT_SRV_COUNT (1UL) + +/* NEG_DUAL_PART2 event flags */ +#define NEG_DUAL_PART2_RESERVED1_POS (1UL) +#define NEG_DUAL_PART2_RESERVED1_MSK (1UL << NEG_DUAL_PART2_RESERVED1_POS) + +#define NEG_DUAL_PART2_RESERVED2_POS (2UL) +#define NEG_DUAL_PART2_RESERVED2_MSK (1UL << NEG_DUAL_PART2_RESERVED2_POS) + + + +#define DUMMY_MSK_POS (4UL) +#define DUMMY_MSK (1UL << DUMMY_MSK_POS) +#define PART2_CALL_INSIDE_PARTITION_MSK_POS (5UL) +#define PART2_CALL_INSIDE_PARTITION_MSK (1UL << PART2_CALL_INSIDE_PARTITION_MSK_POS) + +#define NEG_DUAL_PART2_WAIT_ANY_SID_MSK (\ + DUMMY_MSK | \ + PART2_CALL_INSIDE_PARTITION_MSK) + +/* +#define NEG_DUAL_PART2_WAIT_ANY_MSK (\ + NEG_DUAL_PART2_WAIT_ANY_SID_MSK) | \ + PSA_DOORBELL) +*/ + + +#endif // PSA_NEG_DUAL_PART2_PARTITION_H diff --git a/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/neg_dual_tests.cpp b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/neg_dual_tests.cpp new file mode 100644 index 0000000..d537bae --- /dev/null +++ b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/neg_dual_tests.cpp @@ -0,0 +1,101 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if !ENABLE_SPM + #error [NOT_SUPPORTED] SPM is not supported on this platform +#endif + +#include "cmsis_os2.h" +#include "mbed.h" +#include "greentea-client/test_env.h" +#include "unity.h" +#include "utest.h" +#include "rtos.h" +#include "spm_client.h" +#include "psa_neg_dual_part1_ifs.h" +#include "psa_neg_dual_part2_ifs.h" +#include "neg_tests.h" + +#define MINOR_VER 5 + +using namespace utest::v1; + +Semaphore test_sem(0); +bool error_thrown = false; +extern "C" void spm_reboot(void); + +void error(const char* format, ...) +{ + error_thrown = true; + osStatus status = test_sem.release(); + MBED_ASSERT(status == osOK); + PSA_UNUSED(status); + while(1); +} + +/* ------------------------------------- Functions ----------------------------------- */ + +void server_call_sid_without_extern_sid() +{ + psa_handle_t handle = psa_connect(PART1_CALL_NON_EXISTS_EXTERN_SID, MINOR_VER); + TEST_ASSERT_TRUE(handle > 0); + + psa_call(handle, NULL, 0, NULL, 0); + TEST_FAIL_MESSAGE("psa_call from SP to SID not listed as extern_sids didn't fail"); +} + +void server_call_sid_in_same_partition() +{ + psa_handle_t handle = psa_connect(PART2_CALL_INSIDE_PARTITION, MINOR_VER); + TEST_ASSERT_TRUE(handle > 0); + + psa_call(handle, NULL, 0, NULL, 0); + TEST_FAIL_MESSAGE("psa_call from SP to SID within same SP didn't fail"); +} + + +PSA_NEG_TEST(server_call_sid_without_extern_sid) +PSA_NEG_TEST(server_call_sid_in_same_partition) + +utest::v1::status_t spm_case_teardown(const Case *const source, const size_t passed, const size_t failed, const failure_t reason) +{ + spm_reboot(); + error_thrown = false; + return greentea_case_teardown_handler(source, passed, failed, reason); +} + +#define SPM_UTEST_CASE(desc, test) Case(desc, PSA_NEG_TEST_NAME(test), spm_case_teardown) + +// Test cases +Case cases[] = { + SPM_UTEST_CASE("Testing server calling sid without extern sid", server_call_sid_without_extern_sid), + SPM_UTEST_CASE("Testing server calling sid within the same partition", server_call_sid_in_same_partition) +}; + +utest::v1::status_t spm_setup(const size_t number_of_cases) +{ +#ifndef NO_GREENTEA + GREENTEA_SETUP(60, "default_auto"); +#endif + return greentea_test_setup_handler(number_of_cases); +} + +Specification specification(spm_setup, cases); + +int main() +{ + Harness::run(specification); + return 0; +} diff --git a/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/neg_tests.h b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/neg_tests.h new file mode 100644 index 0000000..d2b7aff --- /dev/null +++ b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/neg_tests.h @@ -0,0 +1,32 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define PSA_NEG_TEST_NAME(test_name) psa_neg_test_ ## test_name + +#define PSA_NEG_TEST(test_name) \ +void PSA_NEG_TEST_NAME(test_name)() \ +{ \ + osStatus status = osOK; \ + Thread T1; \ + \ + status = T1.start(test_name); \ + TEST_ASSERT_TRUE(status == osOK); \ + test_sem.wait(osWaitForever); \ + TEST_ASSERT_TRUE(error_thrown); \ + \ + status = T1.terminate(); \ + TEST_ASSERT_TRUE(status == osOK); \ +} \ + diff --git a/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/part1_psa.json b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/part1_psa.json new file mode 100644 index 0000000..9460a22 --- /dev/null +++ b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/part1_psa.json @@ -0,0 +1,21 @@ +{ + "name": "NEG_DUAL_PART1", + "type": "APPLICATION-ROT", + "priority": "NORMAL", + "id": "0x00000006", + "entry_point": "server_main1", + "stack_size": "0x200", + "heap_size": "0x400", + "services": [{ + "name": "PART1_CALL_NON_EXISTS_EXTERN_SID", + "identifier": "0x00001A08", + "signal": "PART1_CALL_NON_EXISTS_EXTERN_SID_MSK", + "non_secure_clients": true, + "minor_version": 5, + "minor_policy": "RELAXED" + } + ], + "source_files": [ + "server1.c" + ] +} diff --git a/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/part2_psa.json b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/part2_psa.json new file mode 100644 index 0000000..660822e --- /dev/null +++ b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/part2_psa.json @@ -0,0 +1,32 @@ +{ + "name": "NEG_DUAL_PART2", + "type": "APPLICATION-ROT", + "priority": "NORMAL", + "id": "0x00000007", + "entry_point": "server_main2", + "stack_size": "0x200", + "heap_size": "0x400", + "services": [{ + "name": "DUMMY", + "identifier": "0x00001A09", + "signal": "DUMMY_MSK", + "non_secure_clients": false, + "minor_version": 5, + "minor_policy": "RELAXED" + }, + { + "name": "PART2_CALL_INSIDE_PARTITION", + "identifier": "0x00001A0A", + "signal": "PART2_CALL_INSIDE_PARTITION_MSK", + "non_secure_clients": true, + "minor_version": 5, + "minor_policy": "RELAXED" + } + ], + "extern_sids": [ + "PART1_CALL_NON_EXISTS_EXTERN_SID" + ], + "source_files": [ + "server2.c" + ] +} diff --git a/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/psa_setup.c b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/psa_setup.c new file mode 100644 index 0000000..2c1bdaf --- /dev/null +++ b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/psa_setup.c @@ -0,0 +1,79 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#include "spm_panic.h" +#include "spm_internal.h" +#include "handles_manager.h" +#include "cmsis.h" +#include "psa_neg_dual_part1_partition.h" +#include "psa_neg_dual_part2_partition.h" + +extern const uint32_t neg_dual_part2_external_sids[1]; + +spm_partition_t g_partitions[2] = { + { + .partition_id = NEG_DUAL_PART1_ID, + .thread_id = 0, + .flags_rot_srv = NEG_DUAL_PART1_WAIT_ANY_SID_MSK, + .flags_interrupts = 0, + .rot_services = NULL, + .rot_services_count = NEG_DUAL_PART1_ROT_SRV_COUNT, + .extern_sids = NULL, + .extern_sids_count = NEG_DUAL_PART1_EXT_ROT_SRV_COUNT, + .irq_mapper = NULL, + }, + { + .partition_id = NEG_DUAL_PART2_ID, + .thread_id = 0, + .flags_rot_srv = NEG_DUAL_PART2_WAIT_ANY_SID_MSK, + .flags_interrupts = 0, + .rot_services = NULL, + .rot_services_count = NEG_DUAL_PART2_ROT_SRV_COUNT, + .extern_sids = neg_dual_part2_external_sids, + .extern_sids_count = NEG_DUAL_PART2_EXT_ROT_SRV_COUNT, + .irq_mapper = NULL, + }, +}; + +/* Check all the defined memory regions for overlapping. */ + +/* A list of all the memory regions. */ +const mem_region_t *mem_regions = NULL; + +const uint32_t mem_region_count = 0; + +// forward declaration of partition initializers +void neg_dual_part1_init(spm_partition_t *partition); +void neg_dual_part2_init(spm_partition_t *partition); + +uint32_t init_partitions(spm_partition_t **partitions) +{ + if (NULL == partitions) { + SPM_PANIC("partitions is NULL!\n"); + } + + neg_dual_part1_init(&(g_partitions[0])); + neg_dual_part2_init(&(g_partitions[1])); + + *partitions = g_partitions; + return 2; +} + diff --git a/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/server1.c b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/server1.c new file mode 100644 index 0000000..181cfb4 --- /dev/null +++ b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/server1.c @@ -0,0 +1,55 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "unity.h" +#include "spm_panic.h" +#include "spm_server.h" +#include "spm_client.h" +#include "spm_panic.h" +#include "psa_neg_dual_part1_partition.h" +#include "psa_neg_dual_part2_ifs.h" + +#define MINOR_VER 5 + +void server_main1(void *ptr) +{ + psa_msg_t msg = {0}; + uint32_t signals = 0; + + while (true) { + signals = psa_wait_any(PSA_BLOCK); + if (signals & PART1_CALL_NON_EXISTS_EXTERN_SID_MSK) { + psa_get(PART1_CALL_NON_EXISTS_EXTERN_SID_MSK, &msg); + switch (msg.type) { + case PSA_IPC_CONNECT: { + psa_reply(msg.handle, PSA_SUCCESS); + break; + } + case PSA_IPC_CALL: { + psa_connect(PART2_CALL_INSIDE_PARTITION, MINOR_VER); + TEST_FAIL_MESSAGE("server_call_sid_without_extern_sid negative test failed"); + break; + } + default: { + TEST_FAIL_MESSAGE("server_call_sid_without_extern_sid msg type failure"); + } + } + } else { + SPM_PANIC("Unknown signal (0x%08x)", signals); + } + } +} diff --git a/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/server2.c b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/server2.c new file mode 100644 index 0000000..982955c --- /dev/null +++ b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/server2.c @@ -0,0 +1,67 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "cmsis_os2.h" +#include "unity.h" +#include "spm_panic.h" +#include "spm_server.h" +#include "spm_client.h" +#include "spm_panic.h" +#include "psa_neg_dual_part2_partition.h" +#include "psa_neg_dual_part2_ifs.h" + +#define MINOR_VER 5 + +void server_main2(void *ptr) +{ + uint32_t signals = 0; + psa_msg_t msg = {0}; + + while (1) { + signals = psa_wait_any(PSA_BLOCK); + if (signals & DUMMY_MSK) { + psa_get(DUMMY_MSK, &msg); + switch (msg.type) { + case PSA_IPC_CALL: + case PSA_IPC_CONNECT: + case PSA_IPC_DISCONNECT: + psa_reply(msg.handle, PSA_SUCCESS); + break; + default: + SPM_PANIC("Invalid msg type"); + } + } + else if (signals & PART2_CALL_INSIDE_PARTITION_MSK) { + psa_get(PART2_CALL_INSIDE_PARTITION_MSK, &msg); + switch (msg.type) { + case PSA_IPC_CONNECT: { + psa_reply(msg.handle, PSA_SUCCESS); + break; + } + case PSA_IPC_CALL: { + psa_connect(DUMMY, MINOR_VER); + TEST_FAIL_MESSAGE("server_call_sid_in_same_partition negative test failed"); + break; + } + default: { + TEST_FAIL_MESSAGE("server_call_sid_in_same_partition msg type failure"); + } + } + } else { + SPM_PANIC("Unknown signal (0x%08x)", signals); + } + } +} diff --git a/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/spm_reboot.c b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/spm_reboot.c new file mode 100644 index 0000000..35865b9 --- /dev/null +++ b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_dual_partition/spm_reboot.c @@ -0,0 +1,67 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "spm_internal.h" + +extern spm_db_t g_spm; +extern psa_handle_item_t g_channels_handle_storage[]; +extern psa_handle_item_t g_messages_handle_storage[]; +extern spm_ipc_channel_t g_channel_data[]; +extern spm_active_msg_t g_active_messages_data[]; + +void psa_spm_init(void); + +void spm_reboot(void) +{ + osStatus_t status; + + for (uint32_t i = 0; i < g_spm.partition_count; ++i) { + status = osThreadTerminate(g_spm.partitions[i].thread_id); + MBED_ASSERT(status == osOK); + status = osMutexDelete(g_spm.partitions[i].mutex); + MBED_ASSERT(status == osOK); + + for (uint32_t j = 0; j < g_spm.partitions[i].rot_services_count; ++j) { + g_spm.partitions[i].rot_services[j].partition = NULL; + g_spm.partitions[i].rot_services[j].queue.head = NULL; + g_spm.partitions[i].rot_services[j].queue.tail = NULL; + } + + g_spm.partitions[i].rot_services = NULL; + g_spm.partitions[i].mutex = NULL; + g_spm.partitions[i].thread_id = NULL; + } + + status = osMemoryPoolDelete(g_spm.channel_mem_pool); + MBED_ASSERT(status == osOK); + status = osMemoryPoolDelete(g_spm.active_messages_mem_pool); + MBED_ASSERT(status == osOK); + memset(g_channels_handle_storage, 0, (sizeof(psa_handle_item_t) * MBED_CONF_SPM_IPC_MAX_NUM_OF_CHANNELS)); + memset(g_messages_handle_storage, 0, (sizeof(psa_handle_item_t) * MBED_CONF_SPM_IPC_MAX_NUM_OF_MESSAGES)); + memset(g_channel_data, 0, (sizeof(spm_ipc_channel_t) * MBED_CONF_SPM_IPC_MAX_NUM_OF_CHANNELS)); + memset(g_active_messages_data, 0, (sizeof(spm_active_msg_t) * MBED_CONF_SPM_IPC_MAX_NUM_OF_MESSAGES)); + memset(&g_spm, 0, sizeof(g_spm)); + + g_spm.channels_handle_mgr.handle_generator = PSA_HANDLE_MGR_INVALID_HANDLE; + g_spm.channels_handle_mgr.pool_size = MBED_CONF_SPM_IPC_MAX_NUM_OF_CHANNELS; + g_spm.channels_handle_mgr.handles_pool = g_channels_handle_storage; + g_spm.messages_handle_mgr.handle_generator = PSA_HANDLE_MGR_INVALID_HANDLE; + g_spm.messages_handle_mgr.pool_size = MBED_CONF_SPM_IPC_MAX_NUM_OF_MESSAGES; + g_spm.messages_handle_mgr.handles_pool = g_messages_handle_storage; + + psa_spm_init(); + PSA_UNUSED(status); +} diff --git a/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/NEG_SERVER_PART1/psa_neg_server_part1_ifs.h b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/NEG_SERVER_PART1/psa_neg_server_part1_ifs.h new file mode 100644 index 0000000..d549014 --- /dev/null +++ b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/NEG_SERVER_PART1/psa_neg_server_part1_ifs.h @@ -0,0 +1,30 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#ifndef PSA_NEG_SERVER_PART1_PARTITION_ROT_SERVICES_H +#define PSA_NEG_SERVER_PART1_PARTITION_ROT_SERVICES_H + +#define PART1_REPLY_INVALID_RETVAL_CONNECT 0x00001A25 +#define PART1_REPLY_INVALID_RETVAL_CALL 0x00001A26 +#define PART1_CLEAR_NO_DOORBELL 0x00001A27 +#define PART1_WRITE_OUTVEC_IX_SIZE_0 0x00001A28 + +#endif // PSA_NEG_SERVER_PART1_PARTITION_ROT_SERVICES_H diff --git a/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/NEG_SERVER_PART1/psa_neg_server_part1_partition.c b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/NEG_SERVER_PART1/psa_neg_server_part1_partition.c new file mode 100644 index 0000000..6a2d48e --- /dev/null +++ b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/NEG_SERVER_PART1/psa_neg_server_part1_partition.c @@ -0,0 +1,133 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#include "cmsis.h" +#include "mbed_toolchain.h" /* For using MBED_ALIGN macro */ +#include "rtx_os.h" +#include "spm_panic.h" +#include "spm_internal.h" +#include "psa_neg_server_part1_partition.h" +#include "psa_neg_server_part1_ifs.h" + + +/* Threads stacks */ +MBED_ALIGN(8) uint8_t neg_server_part1_thread_stack[512] = {0}; + +/* Threads control blocks */ +osRtxThread_t neg_server_part1_thread_cb = {0}; + +/* Thread attributes - for thread initialization */ +osThreadAttr_t neg_server_part1_thread_attr = { + .name = "neg_server_part1", + .attr_bits = 0, + .cb_mem = &neg_server_part1_thread_cb, + .cb_size = sizeof(neg_server_part1_thread_cb), + .stack_mem = neg_server_part1_thread_stack, + .stack_size = 512, + .priority = osPriorityNormal, + .tz_module = 0, + .reserved = 0 + }; + +spm_rot_service_t neg_server_part1_rot_services[NEG_SERVER_PART1_ROT_SRV_COUNT] = { + { + .sid = PART1_REPLY_INVALID_RETVAL_CONNECT, + .mask = PART1_REPLY_INVALID_RETVAL_CONNECT_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, + { + .sid = PART1_REPLY_INVALID_RETVAL_CALL, + .mask = PART1_REPLY_INVALID_RETVAL_CALL_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, + { + .sid = PART1_CLEAR_NO_DOORBELL, + .mask = PART1_CLEAR_NO_DOORBELL_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, + { + .sid = PART1_WRITE_OUTVEC_IX_SIZE_0, + .mask = PART1_WRITE_OUTVEC_IX_SIZE_0_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, +}; + + +static osRtxMutex_t neg_server_part1_mutex = {0}; +static const osMutexAttr_t neg_server_part1_mutex_attr = { + .name = "neg_server_part1_mutex", + .attr_bits = osMutexRecursive | osMutexPrioInherit | osMutexRobust, + .cb_mem = &neg_server_part1_mutex, + .cb_size = sizeof(neg_server_part1_mutex), +}; + + +extern void server_main1(void *ptr); + +void neg_server_part1_init(spm_partition_t *partition) +{ + if (NULL == partition) { + SPM_PANIC("partition is NULL!\n"); + } + + partition->mutex = osMutexNew(&neg_server_part1_mutex_attr); + if (NULL == partition->mutex) { + SPM_PANIC("Failed to create mutex for secure partition neg_server_part1!\n"); + } + + for (uint32_t i = 0; i < NEG_SERVER_PART1_ROT_SRV_COUNT; ++i) { + neg_server_part1_rot_services[i].partition = partition; + } + partition->rot_services = neg_server_part1_rot_services; + + partition->thread_id = osThreadNew(server_main1, NULL, &neg_server_part1_thread_attr); + if (NULL == partition->thread_id) { + SPM_PANIC("Failed to create start main thread of partition neg_server_part1!\n"); + } +} diff --git a/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/NEG_SERVER_PART1/psa_neg_server_part1_partition.h b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/NEG_SERVER_PART1/psa_neg_server_part1_partition.h new file mode 100644 index 0000000..d417cdc --- /dev/null +++ b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/NEG_SERVER_PART1/psa_neg_server_part1_partition.h @@ -0,0 +1,61 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#ifndef PSA_NEG_SERVER_PART1_PARTITION_H +#define PSA_NEG_SERVER_PART1_PARTITION_H + +#define NEG_SERVER_PART1_ID 7 + +#define NEG_SERVER_PART1_ROT_SRV_COUNT (4UL) +#define NEG_SERVER_PART1_EXT_ROT_SRV_COUNT (0UL) + +/* NEG_SERVER_PART1 event flags */ +#define NEG_SERVER_PART1_RESERVED1_POS (1UL) +#define NEG_SERVER_PART1_RESERVED1_MSK (1UL << NEG_SERVER_PART1_RESERVED1_POS) + +#define NEG_SERVER_PART1_RESERVED2_POS (2UL) +#define NEG_SERVER_PART1_RESERVED2_MSK (1UL << NEG_SERVER_PART1_RESERVED2_POS) + + + +#define PART1_REPLY_INVALID_RETVAL_CONNECT_MSK_POS (4UL) +#define PART1_REPLY_INVALID_RETVAL_CONNECT_MSK (1UL << PART1_REPLY_INVALID_RETVAL_CONNECT_MSK_POS) +#define PART1_REPLY_INVALID_RETVAL_CALL_MSK_POS (5UL) +#define PART1_REPLY_INVALID_RETVAL_CALL_MSK (1UL << PART1_REPLY_INVALID_RETVAL_CALL_MSK_POS) +#define PART1_CLEAR_NO_DOORBELL_MSK_POS (6UL) +#define PART1_CLEAR_NO_DOORBELL_MSK (1UL << PART1_CLEAR_NO_DOORBELL_MSK_POS) +#define PART1_WRITE_OUTVEC_IX_SIZE_0_MSK_POS (7UL) +#define PART1_WRITE_OUTVEC_IX_SIZE_0_MSK (1UL << PART1_WRITE_OUTVEC_IX_SIZE_0_MSK_POS) + +#define NEG_SERVER_PART1_WAIT_ANY_SID_MSK (\ + PART1_REPLY_INVALID_RETVAL_CONNECT_MSK | \ + PART1_REPLY_INVALID_RETVAL_CALL_MSK | \ + PART1_CLEAR_NO_DOORBELL_MSK | \ + PART1_WRITE_OUTVEC_IX_SIZE_0_MSK) + +/* +#define NEG_SERVER_PART1_WAIT_ANY_MSK (\ + NEG_SERVER_PART1_WAIT_ANY_SID_MSK) | \ + PSA_DOORBELL) +*/ + + +#endif // PSA_NEG_SERVER_PART1_PARTITION_H diff --git a/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/NEG_SERVER_PART2/psa_neg_server_part2_ifs.h b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/NEG_SERVER_PART2/psa_neg_server_part2_ifs.h new file mode 100644 index 0000000..0c2de85 --- /dev/null +++ b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/NEG_SERVER_PART2/psa_neg_server_part2_ifs.h @@ -0,0 +1,52 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#ifndef PSA_NEG_SERVER_PART2_PARTITION_ROT_SERVICES_H +#define PSA_NEG_SERVER_PART2_PARTITION_ROT_SERVICES_H + +#define PART2_INT_MASK 0x00001A0B +#define PART2_GET_MSG_NULL 0x00001A0C +#define PART2_GET_SIGNUM_MULTIPLE_BIT 0x00001A0D +#define PART2_GET_SIGNUM_NOT_SUBSET 0x00001A0E +#define PART2_GET_SIGNUM_NOT_ACTIVE 0x00001A0F +#define PART2_GET_SIGNUM_TWICE 0x00001A10 +#define PART2_READ_INVALID_HANDLE 0x00001A11 +#define PART2_READ_NULL_HANDLE 0x00001A12 +#define PART2_READ_TX_SIZE_ZERO 0x00001A13 +#define PART2_WRITE_BUFFER_NULL 0x00001A14 +#define PART2_WRITE_RX_BUFF_NULL 0x00001A15 +#define PART2_WRITE_INVALID_HANDLE 0x00001A16 +#define PART2_WRITE_NULL_HANDLE 0x00001A17 +#define PART2_REPLY_INVALID_HANDLE 0x00001A18 +#define PART2_REPLY_NULL_HANDLE 0x00001A19 +#define PART2_NOTIFY_PART_ID_INVALID 0x00001A1A +#define PART2_IDENTITY_INVALID_HANDLE 0x00001A1B +#define PART2_IDENTITY_NULL_HANDLE 0x00001A1C +#define PART2_SET_RHANDLE_INVALID_HANDLE 0x00001A1D +#define PART2_SET_RHANDLE_NULL_HANDLE 0x00001A1E +#define PART2_READ_WRAPAROUND 0x00001A1F +#define PART2_READ_EXCESS_INVEC 0x00001A20 +#define PART2_WRITE_WRAPAROUND 0x00001A21 +#define PART2_WRITE_EXCESS_OUTVEC 0x00001A22 +#define PART2_WRITE_SIZE_OVERFLOW 0x00001A23 +#define PART2_READ_NULL_BUFFER 0x00001A24 + +#endif // PSA_NEG_SERVER_PART2_PARTITION_ROT_SERVICES_H diff --git a/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/NEG_SERVER_PART2/psa_neg_server_part2_partition.c b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/NEG_SERVER_PART2/psa_neg_server_part2_partition.c new file mode 100644 index 0000000..6d95378 --- /dev/null +++ b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/NEG_SERVER_PART2/psa_neg_server_part2_partition.c @@ -0,0 +1,397 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#include "cmsis.h" +#include "mbed_toolchain.h" /* For using MBED_ALIGN macro */ +#include "rtx_os.h" +#include "spm_panic.h" +#include "spm_internal.h" +#include "psa_neg_server_part2_partition.h" +#include "psa_neg_server_part2_ifs.h" + + +/* Threads stacks */ +MBED_ALIGN(8) uint8_t neg_server_part2_thread_stack[640] = {0}; + +/* Threads control blocks */ +osRtxThread_t neg_server_part2_thread_cb = {0}; + +/* Thread attributes - for thread initialization */ +osThreadAttr_t neg_server_part2_thread_attr = { + .name = "neg_server_part2", + .attr_bits = 0, + .cb_mem = &neg_server_part2_thread_cb, + .cb_size = sizeof(neg_server_part2_thread_cb), + .stack_mem = neg_server_part2_thread_stack, + .stack_size = 640, + .priority = osPriorityNormal, + .tz_module = 0, + .reserved = 0 + }; + +spm_rot_service_t neg_server_part2_rot_services[NEG_SERVER_PART2_ROT_SRV_COUNT] = { + { + .sid = PART2_INT_MASK, + .mask = PART2_INT_MASK_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, + { + .sid = PART2_GET_MSG_NULL, + .mask = PART2_GET_MSG_NULL_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, + { + .sid = PART2_GET_SIGNUM_MULTIPLE_BIT, + .mask = PART2_GET_SIGNUM_MULTIPLE_BIT_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, + { + .sid = PART2_GET_SIGNUM_NOT_SUBSET, + .mask = PART2_GET_SIGNUM_NOT_SUBSET_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, + { + .sid = PART2_GET_SIGNUM_NOT_ACTIVE, + .mask = PART2_GET_SIGNUM_NOT_ACTIVE_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, + { + .sid = PART2_GET_SIGNUM_TWICE, + .mask = PART2_GET_SIGNUM_TWICE_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, + { + .sid = PART2_READ_INVALID_HANDLE, + .mask = PART2_READ_INVALID_HANDLE_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, + { + .sid = PART2_READ_NULL_HANDLE, + .mask = PART2_READ_NULL_HANDLE_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, + { + .sid = PART2_READ_TX_SIZE_ZERO, + .mask = PART2_READ_TX_SIZE_ZERO_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, + { + .sid = PART2_WRITE_BUFFER_NULL, + .mask = PART2_WRITE_BUFFER_NULL_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, + { + .sid = PART2_WRITE_RX_BUFF_NULL, + .mask = PART2_WRITE_RX_BUFF_NULL_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, + { + .sid = PART2_WRITE_INVALID_HANDLE, + .mask = PART2_WRITE_INVALID_HANDLE_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, + { + .sid = PART2_WRITE_NULL_HANDLE, + .mask = PART2_WRITE_NULL_HANDLE_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, + { + .sid = PART2_REPLY_INVALID_HANDLE, + .mask = PART2_REPLY_INVALID_HANDLE_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, + { + .sid = PART2_REPLY_NULL_HANDLE, + .mask = PART2_REPLY_NULL_HANDLE_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, + { + .sid = PART2_NOTIFY_PART_ID_INVALID, + .mask = PART2_NOTIFY_PART_ID_INVALID_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, + { + .sid = PART2_IDENTITY_INVALID_HANDLE, + .mask = PART2_IDENTITY_INVALID_HANDLE_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, + { + .sid = PART2_IDENTITY_NULL_HANDLE, + .mask = PART2_IDENTITY_NULL_HANDLE_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, + { + .sid = PART2_SET_RHANDLE_INVALID_HANDLE, + .mask = PART2_SET_RHANDLE_INVALID_HANDLE_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, + { + .sid = PART2_SET_RHANDLE_NULL_HANDLE, + .mask = PART2_SET_RHANDLE_NULL_HANDLE_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, + { + .sid = PART2_READ_WRAPAROUND, + .mask = PART2_READ_WRAPAROUND_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, + { + .sid = PART2_READ_EXCESS_INVEC, + .mask = PART2_READ_EXCESS_INVEC_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, + { + .sid = PART2_WRITE_WRAPAROUND, + .mask = PART2_WRITE_WRAPAROUND_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, + { + .sid = PART2_WRITE_EXCESS_OUTVEC, + .mask = PART2_WRITE_EXCESS_OUTVEC_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, + { + .sid = PART2_WRITE_SIZE_OVERFLOW, + .mask = PART2_WRITE_SIZE_OVERFLOW_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, + { + .sid = PART2_READ_NULL_BUFFER, + .mask = PART2_READ_NULL_BUFFER_MSK, + .partition = NULL, + .min_version = 5, + .min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED, + .allow_nspe = true, + .queue = { + .head = NULL, + .tail = NULL + } + }, +}; + + +static osRtxMutex_t neg_server_part2_mutex = {0}; +static const osMutexAttr_t neg_server_part2_mutex_attr = { + .name = "neg_server_part2_mutex", + .attr_bits = osMutexRecursive | osMutexPrioInherit | osMutexRobust, + .cb_mem = &neg_server_part2_mutex, + .cb_size = sizeof(neg_server_part2_mutex), +}; + + +extern void server_main2(void *ptr); + +void neg_server_part2_init(spm_partition_t *partition) +{ + if (NULL == partition) { + SPM_PANIC("partition is NULL!\n"); + } + + partition->mutex = osMutexNew(&neg_server_part2_mutex_attr); + if (NULL == partition->mutex) { + SPM_PANIC("Failed to create mutex for secure partition neg_server_part2!\n"); + } + + for (uint32_t i = 0; i < NEG_SERVER_PART2_ROT_SRV_COUNT; ++i) { + neg_server_part2_rot_services[i].partition = partition; + } + partition->rot_services = neg_server_part2_rot_services; + + partition->thread_id = osThreadNew(server_main2, NULL, &neg_server_part2_thread_attr); + if (NULL == partition->thread_id) { + SPM_PANIC("Failed to create start main thread of partition neg_server_part2!\n"); + } +} diff --git a/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/NEG_SERVER_PART2/psa_neg_server_part2_partition.h b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/NEG_SERVER_PART2/psa_neg_server_part2_partition.h new file mode 100644 index 0000000..a06c3d0 --- /dev/null +++ b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/NEG_SERVER_PART2/psa_neg_server_part2_partition.h @@ -0,0 +1,127 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#ifndef PSA_NEG_SERVER_PART2_PARTITION_H +#define PSA_NEG_SERVER_PART2_PARTITION_H + +#define NEG_SERVER_PART2_ID 8 + +#define NEG_SERVER_PART2_ROT_SRV_COUNT (26UL) +#define NEG_SERVER_PART2_EXT_ROT_SRV_COUNT (0UL) + +/* NEG_SERVER_PART2 event flags */ +#define NEG_SERVER_PART2_RESERVED1_POS (1UL) +#define NEG_SERVER_PART2_RESERVED1_MSK (1UL << NEG_SERVER_PART2_RESERVED1_POS) + +#define NEG_SERVER_PART2_RESERVED2_POS (2UL) +#define NEG_SERVER_PART2_RESERVED2_MSK (1UL << NEG_SERVER_PART2_RESERVED2_POS) + + + +#define PART2_INT_MASK_MSK_POS (4UL) +#define PART2_INT_MASK_MSK (1UL << PART2_INT_MASK_MSK_POS) +#define PART2_GET_MSG_NULL_MSK_POS (5UL) +#define PART2_GET_MSG_NULL_MSK (1UL << PART2_GET_MSG_NULL_MSK_POS) +#define PART2_GET_SIGNUM_MULTIPLE_BIT_MSK_POS (6UL) +#define PART2_GET_SIGNUM_MULTIPLE_BIT_MSK (1UL << PART2_GET_SIGNUM_MULTIPLE_BIT_MSK_POS) +#define PART2_GET_SIGNUM_NOT_SUBSET_MSK_POS (7UL) +#define PART2_GET_SIGNUM_NOT_SUBSET_MSK (1UL << PART2_GET_SIGNUM_NOT_SUBSET_MSK_POS) +#define PART2_GET_SIGNUM_NOT_ACTIVE_MSK_POS (8UL) +#define PART2_GET_SIGNUM_NOT_ACTIVE_MSK (1UL << PART2_GET_SIGNUM_NOT_ACTIVE_MSK_POS) +#define PART2_GET_SIGNUM_TWICE_MSK_POS (9UL) +#define PART2_GET_SIGNUM_TWICE_MSK (1UL << PART2_GET_SIGNUM_TWICE_MSK_POS) +#define PART2_READ_INVALID_HANDLE_MSK_POS (10UL) +#define PART2_READ_INVALID_HANDLE_MSK (1UL << PART2_READ_INVALID_HANDLE_MSK_POS) +#define PART2_READ_NULL_HANDLE_MSK_POS (11UL) +#define PART2_READ_NULL_HANDLE_MSK (1UL << PART2_READ_NULL_HANDLE_MSK_POS) +#define PART2_READ_TX_SIZE_ZERO_MSK_POS (12UL) +#define PART2_READ_TX_SIZE_ZERO_MSK (1UL << PART2_READ_TX_SIZE_ZERO_MSK_POS) +#define PART2_WRITE_BUFFER_NULL_MSK_POS (13UL) +#define PART2_WRITE_BUFFER_NULL_MSK (1UL << PART2_WRITE_BUFFER_NULL_MSK_POS) +#define PART2_WRITE_RX_BUFF_NULL_MSK_POS (14UL) +#define PART2_WRITE_RX_BUFF_NULL_MSK (1UL << PART2_WRITE_RX_BUFF_NULL_MSK_POS) +#define PART2_WRITE_INVALID_HANDLE_MSK_POS (15UL) +#define PART2_WRITE_INVALID_HANDLE_MSK (1UL << PART2_WRITE_INVALID_HANDLE_MSK_POS) +#define PART2_WRITE_NULL_HANDLE_MSK_POS (16UL) +#define PART2_WRITE_NULL_HANDLE_MSK (1UL << PART2_WRITE_NULL_HANDLE_MSK_POS) +#define PART2_REPLY_INVALID_HANDLE_MSK_POS (17UL) +#define PART2_REPLY_INVALID_HANDLE_MSK (1UL << PART2_REPLY_INVALID_HANDLE_MSK_POS) +#define PART2_REPLY_NULL_HANDLE_MSK_POS (18UL) +#define PART2_REPLY_NULL_HANDLE_MSK (1UL << PART2_REPLY_NULL_HANDLE_MSK_POS) +#define PART2_NOTIFY_PART_ID_INVALID_MSK_POS (19UL) +#define PART2_NOTIFY_PART_ID_INVALID_MSK (1UL << PART2_NOTIFY_PART_ID_INVALID_MSK_POS) +#define PART2_IDENTITY_INVALID_HANDLE_MSK_POS (20UL) +#define PART2_IDENTITY_INVALID_HANDLE_MSK (1UL << PART2_IDENTITY_INVALID_HANDLE_MSK_POS) +#define PART2_IDENTITY_NULL_HANDLE_MSK_POS (21UL) +#define PART2_IDENTITY_NULL_HANDLE_MSK (1UL << PART2_IDENTITY_NULL_HANDLE_MSK_POS) +#define PART2_SET_RHANDLE_INVALID_HANDLE_MSK_POS (22UL) +#define PART2_SET_RHANDLE_INVALID_HANDLE_MSK (1UL << PART2_SET_RHANDLE_INVALID_HANDLE_MSK_POS) +#define PART2_SET_RHANDLE_NULL_HANDLE_MSK_POS (23UL) +#define PART2_SET_RHANDLE_NULL_HANDLE_MSK (1UL << PART2_SET_RHANDLE_NULL_HANDLE_MSK_POS) +#define PART2_READ_WRAPAROUND_MSK_POS (24UL) +#define PART2_READ_WRAPAROUND_MSK (1UL << PART2_READ_WRAPAROUND_MSK_POS) +#define PART2_READ_EXCESS_INVEC_MSK_POS (25UL) +#define PART2_READ_EXCESS_INVEC_MSK (1UL << PART2_READ_EXCESS_INVEC_MSK_POS) +#define PART2_WRITE_WRAPAROUND_MSK_POS (26UL) +#define PART2_WRITE_WRAPAROUND_MSK (1UL << PART2_WRITE_WRAPAROUND_MSK_POS) +#define PART2_WRITE_EXCESS_OUTVEC_MSK_POS (27UL) +#define PART2_WRITE_EXCESS_OUTVEC_MSK (1UL << PART2_WRITE_EXCESS_OUTVEC_MSK_POS) +#define PART2_WRITE_SIZE_OVERFLOW_MSK_POS (28UL) +#define PART2_WRITE_SIZE_OVERFLOW_MSK (1UL << PART2_WRITE_SIZE_OVERFLOW_MSK_POS) +#define PART2_READ_NULL_BUFFER_MSK_POS (29UL) +#define PART2_READ_NULL_BUFFER_MSK (1UL << PART2_READ_NULL_BUFFER_MSK_POS) + +#define NEG_SERVER_PART2_WAIT_ANY_SID_MSK (\ + PART2_INT_MASK_MSK | \ + PART2_GET_MSG_NULL_MSK | \ + PART2_GET_SIGNUM_MULTIPLE_BIT_MSK | \ + PART2_GET_SIGNUM_NOT_SUBSET_MSK | \ + PART2_GET_SIGNUM_NOT_ACTIVE_MSK | \ + PART2_GET_SIGNUM_TWICE_MSK | \ + PART2_READ_INVALID_HANDLE_MSK | \ + PART2_READ_NULL_HANDLE_MSK | \ + PART2_READ_TX_SIZE_ZERO_MSK | \ + PART2_WRITE_BUFFER_NULL_MSK | \ + PART2_WRITE_RX_BUFF_NULL_MSK | \ + PART2_WRITE_INVALID_HANDLE_MSK | \ + PART2_WRITE_NULL_HANDLE_MSK | \ + PART2_REPLY_INVALID_HANDLE_MSK | \ + PART2_REPLY_NULL_HANDLE_MSK | \ + PART2_NOTIFY_PART_ID_INVALID_MSK | \ + PART2_IDENTITY_INVALID_HANDLE_MSK | \ + PART2_IDENTITY_NULL_HANDLE_MSK | \ + PART2_SET_RHANDLE_INVALID_HANDLE_MSK | \ + PART2_SET_RHANDLE_NULL_HANDLE_MSK | \ + PART2_READ_WRAPAROUND_MSK | \ + PART2_READ_EXCESS_INVEC_MSK | \ + PART2_WRITE_WRAPAROUND_MSK | \ + PART2_WRITE_EXCESS_OUTVEC_MSK | \ + PART2_WRITE_SIZE_OVERFLOW_MSK | \ + PART2_READ_NULL_BUFFER_MSK) + +/* +#define NEG_SERVER_PART2_WAIT_ANY_MSK (\ + NEG_SERVER_PART2_WAIT_ANY_SID_MSK) | \ + PSA_DOORBELL) +*/ + + +#endif // PSA_NEG_SERVER_PART2_PARTITION_H diff --git a/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/neg_ipc_tests.cpp b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/neg_ipc_tests.cpp new file mode 100644 index 0000000..8fc7551 --- /dev/null +++ b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/neg_ipc_tests.cpp @@ -0,0 +1,460 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if !ENABLE_SPM + #error [NOT_SUPPORTED] SPM is not supported on this platform +#endif + +#include "cmsis_os2.h" +#include "mbed.h" +#include "greentea-client/test_env.h" +#include "unity.h" +#include "utest.h" +#include "rtos.h" +#include "spm_client.h" +#include "psa_neg_server_part1_ifs.h" +#include "psa_neg_server_part2_ifs.h" +#include "neg_tests.h" + + +#define MINOR_VER 5 +#define CLIENT_RSP_BUF_SIZE 128 +#define OFFSET_POS 1 +#define INVALID_SID (PART1_ROT_SRV1 + 30) +#define INVALID_MINOR (MINOR_VER + 10) + +using namespace utest::v1; + +Semaphore test_sem(0); +bool error_thrown = false; +uint8_t response_buf[CLIENT_RSP_BUF_SIZE]; +extern "C" void spm_reboot(void); + +void error(const char* format, ...) +{ + error_thrown = true; + osStatus status = test_sem.release(); + MBED_ASSERT(status == osOK); + PSA_UNUSED(status); + while(1); +} + +/* ------------------------------------- Functions ----------------------------------- */ + +static psa_handle_t negative_server_ipc_tests_connect(uint32_t sid, uint32_t minor_version) +{ + psa_handle_t handle = psa_connect(sid, minor_version); + TEST_ASSERT_TRUE(handle > 0); + return handle; +} + +static void negative_server_ipc_tests_call( psa_handle_t handle, + psa_invec_t *invec, + size_t tx_len, + psa_outvec_t *outvec, + size_t rx_len + ) +{ + error_t status = PSA_SUCCESS; + + status = psa_call(handle, invec, tx_len, outvec, rx_len); + + TEST_ASSERT_EQUAL_INT(PSA_SUCCESS, status); +} + +//Testing server interrupt mask contains only a subset of interrupt signal mask +void server_interrupt_mask_invalid() +{ + psa_connect( PART2_INT_MASK, MINOR_VER); + + TEST_FAIL_MESSAGE("server_interrupt_mask_invalid negative test failed at client side"); +} + +//Testing server get with msg NULL +void server_get_msg_null() +{ + psa_connect( PART2_GET_MSG_NULL, MINOR_VER); + + TEST_FAIL_MESSAGE("server_get_msg_null negative test failed at client side"); +} + +//Testing server get signum have more than one bit ON +void server_get_multiple_bit_signum() +{ + psa_connect( PART2_GET_SIGNUM_MULTIPLE_BIT, MINOR_VER); + + TEST_FAIL_MESSAGE("server_get_multiple_bit_signum negative test failed at client side"); +} + +//Testing server get signum is not a subset of current partition flags +void server_get_signum_not_subset() +{ + psa_connect( PART2_GET_SIGNUM_NOT_SUBSET, MINOR_VER); + + TEST_FAIL_MESSAGE("server_get_signum_not_subset negative test failed at client side"); +} + +//Testing server get signum flag is not active +void server_get_signum_not_active() +{ + psa_connect( PART2_GET_SIGNUM_NOT_ACTIVE, MINOR_VER); + + TEST_FAIL_MESSAGE("server_get_signum_not_active negative test failed at client side"); +} + +//Testing server get signum flag twice +void server_get_signum_twice() +{ + psa_connect( PART2_GET_SIGNUM_TWICE, MINOR_VER); + + TEST_FAIL_MESSAGE("server_get_signum_twice negative test failed at client side"); +} + +//Testing server read handle does not exist on the platform +void server_read_invalid_handle() +{ + uint32_t data = 52; + psa_invec_t data_vec = {&data, sizeof(data)}; + psa_handle_t handle = negative_server_ipc_tests_connect(PART2_READ_INVALID_HANDLE, MINOR_VER); + + negative_server_ipc_tests_call(handle, &data_vec, 1, NULL, 0); + + TEST_FAIL_MESSAGE("server_read_invalid_handle negative test failed at client side"); +} + +//Testing server read handle is null (PSA_NULL_HANDLE) +void server_read_null_handle() +{ + uint32_t data = 52; + psa_invec_t data_vec = {&data, sizeof(data)}; + psa_handle_t handle = negative_server_ipc_tests_connect(PART2_READ_NULL_HANDLE, MINOR_VER); + + negative_server_ipc_tests_call(handle, &data_vec, 1, NULL, 0); + + TEST_FAIL_MESSAGE("server_read_null_handle negative test failed at client side"); +} + +//Testing server read buffer is null +void server_read_null_buffer() +{ + uint32_t data = 52; + psa_invec_t data_vec = {&data, sizeof(data)}; + psa_handle_t handle = negative_server_ipc_tests_connect(PART2_READ_NULL_BUFFER, MINOR_VER); + + negative_server_ipc_tests_call(handle, &data_vec, 1, NULL, 0); + + TEST_FAIL_MESSAGE("server_read_null_buffer negative test failed at client side"); +} + +//Testing server write buffer is null +void server_write_null_buffer() +{ + psa_handle_t handle = negative_server_ipc_tests_connect(PART2_WRITE_BUFFER_NULL, MINOR_VER); + + uint8_t data[2] = {1, 0}; + psa_invec_t iovec_temp[PSA_MAX_IOVEC - 1] = { + {data, sizeof(data)}, + {data, sizeof(data)}, + {data, sizeof(data)} + }; + psa_outvec_t resp = { NULL, CLIENT_RSP_BUF_SIZE }; + + negative_server_ipc_tests_call(handle, iovec_temp, PSA_MAX_IOVEC - 1, &resp, 1); + + TEST_FAIL_MESSAGE("server_write_null_buffer negative test failed at client side"); +} + +//Testing server write rx_buff is null +void server_write_rx_buff_null() +{ + psa_handle_t handle = negative_server_ipc_tests_connect(PART2_WRITE_RX_BUFF_NULL, MINOR_VER); + + uint8_t data[2] = {1, 0}; + psa_invec_t iovec_temp[PSA_MAX_IOVEC] = { + {data, sizeof(data)}, + {data, sizeof(data)}, + {data, sizeof(data)}, + {data, sizeof(data)} + }; + + negative_server_ipc_tests_call(handle, iovec_temp, PSA_MAX_IOVEC, NULL, 0); + + TEST_FAIL_MESSAGE("server_write_rx_buff_null negative test failed at client side"); +} + +//Testing server write handle does not exist on the platform +void server_write_invalid_handle() +{ + psa_handle_t handle = negative_server_ipc_tests_connect(PART2_WRITE_INVALID_HANDLE, MINOR_VER); + + uint8_t data[2] = {1, 0}; + psa_invec_t iovec_temp[PSA_MAX_IOVEC - 1] = { + {data, sizeof(data)}, + {data, sizeof(data)}, + {data, sizeof(data)} + }; + psa_outvec_t resp = { response_buf, CLIENT_RSP_BUF_SIZE }; + + negative_server_ipc_tests_call(handle, iovec_temp, PSA_MAX_IOVEC - 1, &resp, 1); + + TEST_FAIL_MESSAGE("server_write_invalid_handle negative test failed at client side"); +} + +//Testing server write handle is null (PSA_NULL_HANDLE) +void server_write_null_handle() +{ + psa_handle_t handle = negative_server_ipc_tests_connect(PART2_WRITE_NULL_HANDLE, MINOR_VER); + + uint8_t data[2] = {1, 0}; + psa_invec_t iovec_temp[PSA_MAX_IOVEC - 1] = { + {data, sizeof(data)}, + {data, sizeof(data)}, + {data, sizeof(data)} + }; + psa_outvec_t resp = { response_buf, CLIENT_RSP_BUF_SIZE }; + + negative_server_ipc_tests_call(handle, iovec_temp, PSA_MAX_IOVEC - 1, &resp, 1); + + TEST_FAIL_MESSAGE("server_write_null_handle negative test failed at client side"); +} + +//Testing server reply with a null handle +void server_reply_null_handle() +{ + psa_connect(PART2_REPLY_NULL_HANDLE, MINOR_VER); + + TEST_FAIL_MESSAGE("server_reply_null_handle negative test failed at client side"); +} + +//Testing server reply handle does not exist on the platform +void server_reply_invalid_handle() +{ + psa_connect(PART2_REPLY_INVALID_HANDLE, MINOR_VER); + + TEST_FAIL_MESSAGE("server_reply_invalid_handle negative test failed at client side"); +} + +//Testing server reply invalid retval for connect +void server_reply_invalid_retval_connect() +{ + psa_connect(PART1_REPLY_INVALID_RETVAL_CONNECT, MINOR_VER); + + TEST_FAIL_MESSAGE("server_reply_invalid_retval_connect negative test failed at client side"); +} + +//Testing server reply invalid retval for call +void server_reply_invalid_retval_call() +{ + psa_handle_t handle = negative_server_ipc_tests_connect(PART1_REPLY_INVALID_RETVAL_CALL, MINOR_VER); + + psa_call(handle, NULL, 0, NULL, 0); + + TEST_FAIL_MESSAGE("server_reply_invalid_retval_call negative test failed at client side"); +} + +//Testing server notify partition id doesnt exist +void server_notify_part_id_invalid() +{ + psa_connect(PART2_NOTIFY_PART_ID_INVALID, MINOR_VER); + + TEST_FAIL_MESSAGE("server_notify_part_id_invalid negative test failed at client side"); +} + +//Testing server psa_identity handle does not exist on the platform +void server_psa_identity_invalid_handle() +{ + psa_connect(PART2_IDENTITY_INVALID_HANDLE, MINOR_VER); + + TEST_FAIL_MESSAGE("server_psa_identity_invalid_handle negative test failed at client side"); +} + +//Testing server psa_identity handle is null (PSA_NULL_HANDLE) +void server_psa_identity_null_handle() +{ + psa_connect(PART2_IDENTITY_NULL_HANDLE, MINOR_VER); + + TEST_FAIL_MESSAGE("server_psa_identity_null_handle negative test failed at client side"); +} + +//Testing server psa_set_rhandle handle does not exist on the platform +void server_set_rhandle_invalid_handle() +{ + psa_connect(PART2_SET_RHANDLE_INVALID_HANDLE, MINOR_VER); + + TEST_FAIL_MESSAGE("server_set_rhandle_invalid_handle negative test failed at client side"); +} + +//Testing server psa_set_rhandle handle is null (PSA_NULL_HANDLE) +void server_set_rhandle_null_handle() +{ + psa_connect(PART2_SET_RHANDLE_NULL_HANDLE, MINOR_VER); + + TEST_FAIL_MESSAGE("server_set_rhandle_null_handle negative test failed at client side"); +} + +void server_read_on_wraparound_msg_ptr() +{ + psa_handle_t handle = negative_server_ipc_tests_connect(PART2_READ_WRAPAROUND, MINOR_VER); + psa_call(handle, NULL, 0, NULL, 0); + + TEST_FAIL_MESSAGE("server_read_on_wraparound_msg_ptr negative test failed at client side"); +} + +void server_read_from_excess_invec() +{ + psa_handle_t handle = negative_server_ipc_tests_connect(PART2_READ_EXCESS_INVEC, MINOR_VER); + psa_call(handle, NULL, 0, NULL, 0); + + TEST_FAIL_MESSAGE("server_read_from_excess_invec negative test failed at client side"); +} + +void server_write_on_wraparound_msg_ptr() +{ + psa_handle_t handle = negative_server_ipc_tests_connect(PART2_WRITE_WRAPAROUND, MINOR_VER); + psa_call(handle, NULL, 0, NULL, 0); + + TEST_FAIL_MESSAGE("server_write_on_wraparound_msg_ptr negative test failed at client side"); +} + +void server_write_from_excess_outvec() +{ + psa_handle_t handle = negative_server_ipc_tests_connect(PART2_WRITE_EXCESS_OUTVEC, MINOR_VER); + psa_call(handle, NULL, 0, NULL, 0); + + TEST_FAIL_MESSAGE("server_write_from_excess_outvec negative test failed at client side"); +} + +void server_write_from_outvec_index_size_0() +{ + psa_handle_t handle = negative_server_ipc_tests_connect(PART1_WRITE_OUTVEC_IX_SIZE_0, MINOR_VER); + + psa_outvec_t resp[2] = { {response_buf, CLIENT_RSP_BUF_SIZE}, + {response_buf, 0} + }; + + psa_call(handle, NULL, 0, resp, 2); + + TEST_FAIL_MESSAGE("server_write_from_outvec_index_size_0 negative test failed at client side"); +} + +void server_write_with_size_overflow() +{ + psa_handle_t handle = negative_server_ipc_tests_connect(PART2_WRITE_SIZE_OVERFLOW, MINOR_VER); + psa_outvec_t resp = { response_buf, CLIENT_RSP_BUF_SIZE }; + psa_call(handle, NULL, 0, &resp, 1); + TEST_FAIL_MESSAGE("server_write_with_size_overflow negative test failed at client side"); +} + +// Testing server psa_clear() when doorbell signal is not currently asserted +void server_clear_no_doorbell() +{ + psa_connect(PART1_CLEAR_NO_DOORBELL, MINOR_VER); + + TEST_FAIL_MESSAGE("server_clear_no_doorbell negative test failed at client side"); +} + +PSA_NEG_TEST(server_interrupt_mask_invalid) +PSA_NEG_TEST(server_get_msg_null) +PSA_NEG_TEST(server_get_multiple_bit_signum) +PSA_NEG_TEST(server_get_signum_not_subset) +PSA_NEG_TEST(server_get_signum_not_active) +PSA_NEG_TEST(server_get_signum_twice) +PSA_NEG_TEST(server_read_invalid_handle) +PSA_NEG_TEST(server_read_null_handle) +PSA_NEG_TEST(server_read_null_buffer) +PSA_NEG_TEST(server_write_null_buffer) +PSA_NEG_TEST(server_write_rx_buff_null) +PSA_NEG_TEST(server_write_invalid_handle) +PSA_NEG_TEST(server_write_null_handle) +PSA_NEG_TEST(server_reply_null_handle) +PSA_NEG_TEST(server_reply_invalid_handle) +PSA_NEG_TEST(server_reply_invalid_retval_connect) +PSA_NEG_TEST(server_reply_invalid_retval_call) +PSA_NEG_TEST(server_notify_part_id_invalid) +PSA_NEG_TEST(server_psa_identity_invalid_handle) +PSA_NEG_TEST(server_psa_identity_null_handle) +PSA_NEG_TEST(server_set_rhandle_invalid_handle) +PSA_NEG_TEST(server_set_rhandle_null_handle) +PSA_NEG_TEST(server_read_on_wraparound_msg_ptr) +PSA_NEG_TEST(server_read_from_excess_invec) +PSA_NEG_TEST(server_write_on_wraparound_msg_ptr) +PSA_NEG_TEST(server_write_with_size_overflow) +PSA_NEG_TEST(server_write_from_excess_outvec) +PSA_NEG_TEST(server_write_from_outvec_index_size_0) +PSA_NEG_TEST(server_clear_no_doorbell) + +utest::v1::status_t spm_case_setup(const Case *const source, const size_t index_of_case) +{ + memset(response_buf, 0, sizeof(response_buf)); + return greentea_case_setup_handler(source, index_of_case); +} + +utest::v1::status_t spm_case_teardown(const Case *const source, const size_t passed, const size_t failed, const failure_t reason) +{ + spm_reboot(); + error_thrown = false; + return greentea_case_teardown_handler(source, passed, failed, reason); +} + +#define SPM_UTEST_CASE(desc, test) Case(desc, spm_case_setup, PSA_NEG_TEST_NAME(test), spm_case_teardown) + +// Test cases +Case cases[] = { + SPM_UTEST_CASE("Testing server interrupt mask invalid", server_interrupt_mask_invalid), + SPM_UTEST_CASE("Testing server get with msg NULL", server_get_msg_null), + SPM_UTEST_CASE("Testing server get signum have more than one bit ON", server_get_multiple_bit_signum), + SPM_UTEST_CASE("Testing server get signum flag is not a subset of current partition flags", server_get_signum_not_subset), + SPM_UTEST_CASE("Testing server get signum flag is not active", server_get_signum_not_active), + SPM_UTEST_CASE("Testing server get signum twice", server_get_signum_twice), + SPM_UTEST_CASE("Testing server read handle does not exist on the platform", server_read_invalid_handle), + SPM_UTEST_CASE("Testing server read handle is PSA_NULL_HANDLE", server_read_null_handle), + SPM_UTEST_CASE("Testing server read buffer is NULL", server_read_null_buffer), + SPM_UTEST_CASE("Testing server write buffer is NULL", server_write_null_buffer), + SPM_UTEST_CASE("Testing server write rx_buff is null", server_write_rx_buff_null), + SPM_UTEST_CASE("Testing server write handle does not exist on the platform", server_write_invalid_handle), + SPM_UTEST_CASE("Testing server write handle is PSA_NULL_HANDLE", server_write_null_handle), + SPM_UTEST_CASE("Testing server reply with PSA_NULL_HANDLE", server_reply_null_handle), + SPM_UTEST_CASE("Testing server reply handle does not exist on the platform", server_reply_invalid_handle), + SPM_UTEST_CASE("Testing server reply invalid retval for connect", server_reply_invalid_retval_connect), + SPM_UTEST_CASE("Testing server reply invalid retval for call", server_reply_invalid_retval_call), + SPM_UTEST_CASE("Testing server notify partition id does not exist", server_notify_part_id_invalid), + SPM_UTEST_CASE("Testing server identify handle does not exist on the platform", server_psa_identity_invalid_handle), + SPM_UTEST_CASE("Testing server identify handle is PSA_NULL_HANDLE", server_psa_identity_null_handle), + SPM_UTEST_CASE("Testing server set_rhandle handle does not exist on the platform", server_set_rhandle_invalid_handle), + SPM_UTEST_CASE("Testing server set_rhandle handle is PSA_NULL_HANDLE", server_set_rhandle_null_handle), + SPM_UTEST_CASE("Testing server read on wrap around pointer", server_read_on_wraparound_msg_ptr), + SPM_UTEST_CASE("Testing server read on excess invec index", server_read_from_excess_invec), + SPM_UTEST_CASE("Testing server write on wrap around pointer", server_write_on_wraparound_msg_ptr), + SPM_UTEST_CASE("Testing server write on excess out_vec index", server_write_from_excess_outvec), + SPM_UTEST_CASE("Testing server write on out_vec index with size 0", server_write_from_outvec_index_size_0), + SPM_UTEST_CASE("Testing server write with size overflow", server_write_with_size_overflow), + SPM_UTEST_CASE("Testing server clear when doorbell signal is not asserted", server_clear_no_doorbell) +}; + +utest::v1::status_t spm_setup(const size_t number_of_cases) +{ +#ifndef NO_GREENTEA + GREENTEA_SETUP(60, "default_auto"); +#endif + return greentea_test_setup_handler(number_of_cases); +} + +Specification specification(spm_setup, cases); + +int main() +{ + Harness::run(specification); + return 0; +} diff --git a/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/neg_tests.h b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/neg_tests.h new file mode 100644 index 0000000..d2b7aff --- /dev/null +++ b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/neg_tests.h @@ -0,0 +1,32 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define PSA_NEG_TEST_NAME(test_name) psa_neg_test_ ## test_name + +#define PSA_NEG_TEST(test_name) \ +void PSA_NEG_TEST_NAME(test_name)() \ +{ \ + osStatus status = osOK; \ + Thread T1; \ + \ + status = T1.start(test_name); \ + TEST_ASSERT_TRUE(status == osOK); \ + test_sem.wait(osWaitForever); \ + TEST_ASSERT_TRUE(error_thrown); \ + \ + status = T1.terminate(); \ + TEST_ASSERT_TRUE(status == osOK); \ +} \ + diff --git a/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/part1_psa.json b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/part1_psa.json new file mode 100644 index 0000000..96a796b --- /dev/null +++ b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/part1_psa.json @@ -0,0 +1,45 @@ +{ + "name": "NEG_SERVER_PART1", + "type": "APPLICATION-ROT", + "priority": "NORMAL", + "id": "0x00000007", + "entry_point": "server_main1", + "stack_size": "0x200", + "heap_size": "0x400", + "services": [{ + "name": "PART1_REPLY_INVALID_RETVAL_CONNECT", + "identifier": "0x00001A25", + "signal": "PART1_REPLY_INVALID_RETVAL_CONNECT_MSK", + "non_secure_clients": true, + "minor_version": 5, + "minor_policy": "RELAXED" + }, + { + "name": "PART1_REPLY_INVALID_RETVAL_CALL", + "identifier": "0x00001A26", + "signal": "PART1_REPLY_INVALID_RETVAL_CALL_MSK", + "non_secure_clients": true, + "minor_version": 5, + "minor_policy": "RELAXED" + }, + { + "name": "PART1_CLEAR_NO_DOORBELL", + "identifier": "0x00001A27", + "signal": "PART1_CLEAR_NO_DOORBELL_MSK", + "non_secure_clients": true, + "minor_version": 5, + "minor_policy": "RELAXED" + }, + { + "name": "PART1_WRITE_OUTVEC_IX_SIZE_0", + "identifier": "0x00001A28", + "signal": "PART1_WRITE_OUTVEC_IX_SIZE_0_MSK", + "non_secure_clients": true, + "minor_version": 5, + "minor_policy": "RELAXED" + } + ], + "source_files": [ + "server1.c" + ] +} diff --git a/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/part2_psa.json b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/part2_psa.json new file mode 100644 index 0000000..536e97c --- /dev/null +++ b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/part2_psa.json @@ -0,0 +1,221 @@ +{ + "name": "NEG_SERVER_PART2", + "type": "APPLICATION-ROT", + "priority": "NORMAL", + "id": "0x00000008", + "entry_point": "server_main2", + "stack_size": "0x280", + "heap_size": "0x400", + "services": [{ + "name": "PART2_INT_MASK", + "identifier": "0x00001A0B", + "signal": "PART2_INT_MASK_MSK", + "non_secure_clients": true, + "minor_version": 5, + "minor_policy": "RELAXED" + }, + { + "name": "PART2_GET_MSG_NULL", + "identifier": "0x00001A0C", + "signal": "PART2_GET_MSG_NULL_MSK", + "non_secure_clients": true, + "minor_version": 5, + "minor_policy": "RELAXED" + }, + { + "name": "PART2_GET_SIGNUM_MULTIPLE_BIT", + "identifier": "0x00001A0D", + "signal": "PART2_GET_SIGNUM_MULTIPLE_BIT_MSK", + "non_secure_clients": true, + "minor_version": 5, + "minor_policy": "RELAXED" + }, + { + "name": "PART2_GET_SIGNUM_NOT_SUBSET", + "identifier": "0x00001A0E", + "signal": "PART2_GET_SIGNUM_NOT_SUBSET_MSK", + "non_secure_clients": true, + "minor_version": 5, + "minor_policy": "RELAXED" + }, + { + "name": "PART2_GET_SIGNUM_NOT_ACTIVE", + "identifier": "0x00001A0F", + "signal": "PART2_GET_SIGNUM_NOT_ACTIVE_MSK", + "non_secure_clients": true, + "minor_version": 5, + "minor_policy": "RELAXED" + }, + { + "name": "PART2_GET_SIGNUM_TWICE", + "identifier": "0x00001A10", + "signal": "PART2_GET_SIGNUM_TWICE_MSK", + "non_secure_clients": true, + "minor_version": 5, + "minor_policy": "RELAXED" + }, + { + "name": "PART2_READ_INVALID_HANDLE", + "identifier": "0x00001A11", + "signal": "PART2_READ_INVALID_HANDLE_MSK", + "non_secure_clients": true, + "minor_version": 5, + "minor_policy": "RELAXED" + }, + { + "name": "PART2_READ_NULL_HANDLE", + "identifier": "0x00001A12", + "signal": "PART2_READ_NULL_HANDLE_MSK", + "non_secure_clients": true, + "minor_version": 5, + "minor_policy": "RELAXED" + }, + { + "name": "PART2_READ_TX_SIZE_ZERO", + "identifier": "0x00001A13", + "signal": "PART2_READ_TX_SIZE_ZERO_MSK", + "non_secure_clients": true, + "minor_version": 5, + "minor_policy": "RELAXED" + }, + { + "name": "PART2_WRITE_BUFFER_NULL", + "identifier": "0x00001A14", + "signal": "PART2_WRITE_BUFFER_NULL_MSK", + "non_secure_clients": true, + "minor_version": 5, + "minor_policy": "RELAXED" + }, + { + "name": "PART2_WRITE_RX_BUFF_NULL", + "identifier": "0x00001A15", + "signal": "PART2_WRITE_RX_BUFF_NULL_MSK", + "non_secure_clients": true, + "minor_version": 5, + "minor_policy": "RELAXED" + }, + { + "name": "PART2_WRITE_INVALID_HANDLE", + "identifier": "0x00001A16", + "signal": "PART2_WRITE_INVALID_HANDLE_MSK", + "non_secure_clients": true, + "minor_version": 5, + "minor_policy": "RELAXED" + }, + { + "name": "PART2_WRITE_NULL_HANDLE", + "identifier": "0x00001A17", + "signal": "PART2_WRITE_NULL_HANDLE_MSK", + "non_secure_clients": true, + "minor_version": 5, + "minor_policy": "RELAXED" + }, + { + "name": "PART2_REPLY_INVALID_HANDLE", + "identifier": "0x00001A18", + "signal": "PART2_REPLY_INVALID_HANDLE_MSK", + "non_secure_clients": true, + "minor_version": 5, + "minor_policy": "RELAXED" + }, + { + "name": "PART2_REPLY_NULL_HANDLE", + "identifier": "0x00001A19", + "signal": "PART2_REPLY_NULL_HANDLE_MSK", + "non_secure_clients": true, + "minor_version": 5, + "minor_policy": "RELAXED" + }, + { + "name": "PART2_NOTIFY_PART_ID_INVALID", + "identifier": "0x00001A1A", + "signal": "PART2_NOTIFY_PART_ID_INVALID_MSK", + "non_secure_clients": true, + "minor_version": 5, + "minor_policy": "RELAXED" + }, + { + "name": "PART2_IDENTITY_INVALID_HANDLE", + "identifier": "0x00001A1B", + "signal": "PART2_IDENTITY_INVALID_HANDLE_MSK", + "non_secure_clients": true, + "minor_version": 5, + "minor_policy": "RELAXED" + }, + { + "name": "PART2_IDENTITY_NULL_HANDLE", + "identifier": "0x00001A1C", + "signal": "PART2_IDENTITY_NULL_HANDLE_MSK", + "non_secure_clients": true, + "minor_version": 5, + "minor_policy": "RELAXED" + }, + { + "name": "PART2_SET_RHANDLE_INVALID_HANDLE", + "identifier": "0x00001A1D", + "signal": "PART2_SET_RHANDLE_INVALID_HANDLE_MSK", + "non_secure_clients": true, + "minor_version": 5, + "minor_policy": "RELAXED" + }, + { + "name": "PART2_SET_RHANDLE_NULL_HANDLE", + "identifier": "0x00001A1E", + "signal": "PART2_SET_RHANDLE_NULL_HANDLE_MSK", + "non_secure_clients": true, + "minor_version": 5, + "minor_policy": "RELAXED" + }, + { + "name": "PART2_READ_WRAPAROUND", + "identifier": "0x00001A1F", + "signal": "PART2_READ_WRAPAROUND_MSK", + "non_secure_clients": true, + "minor_version": 5, + "minor_policy": "RELAXED" + }, + { + "name": "PART2_READ_EXCESS_INVEC", + "identifier": "0x00001A20", + "signal": "PART2_READ_EXCESS_INVEC_MSK", + "non_secure_clients": true, + "minor_version": 5, + "minor_policy": "RELAXED" + }, + { + "name": "PART2_WRITE_WRAPAROUND", + "identifier": "0x00001A21", + "signal": "PART2_WRITE_WRAPAROUND_MSK", + "non_secure_clients": true, + "minor_version": 5, + "minor_policy": "RELAXED" + }, + { + "name": "PART2_WRITE_EXCESS_OUTVEC", + "identifier": "0x00001A22", + "signal": "PART2_WRITE_EXCESS_OUTVEC_MSK", + "non_secure_clients": true, + "minor_version": 5, + "minor_policy": "RELAXED" + }, + { + "name": "PART2_WRITE_SIZE_OVERFLOW", + "identifier": "0x00001A23", + "signal": "PART2_WRITE_SIZE_OVERFLOW_MSK", + "non_secure_clients": true, + "minor_version": 5, + "minor_policy": "RELAXED" + }, + { + "name": "PART2_READ_NULL_BUFFER", + "identifier": "0x00001A24", + "signal": "PART2_READ_NULL_BUFFER_MSK", + "non_secure_clients": true, + "minor_version": 5, + "minor_policy": "RELAXED" + } + ], + "source_files": [ + "server2.c" + ] +} diff --git a/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/psa_setup.c b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/psa_setup.c new file mode 100644 index 0000000..b97149d --- /dev/null +++ b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/psa_setup.c @@ -0,0 +1,78 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#include "spm_panic.h" +#include "spm_internal.h" +#include "handles_manager.h" +#include "cmsis.h" +#include "psa_neg_server_part1_partition.h" +#include "psa_neg_server_part2_partition.h" + + +spm_partition_t g_partitions[2] = { + { + .partition_id = NEG_SERVER_PART1_ID, + .thread_id = 0, + .flags_rot_srv = NEG_SERVER_PART1_WAIT_ANY_SID_MSK, + .flags_interrupts = 0, + .rot_services = NULL, + .rot_services_count = NEG_SERVER_PART1_ROT_SRV_COUNT, + .extern_sids = NULL, + .extern_sids_count = NEG_SERVER_PART1_EXT_ROT_SRV_COUNT, + .irq_mapper = NULL, + }, + { + .partition_id = NEG_SERVER_PART2_ID, + .thread_id = 0, + .flags_rot_srv = NEG_SERVER_PART2_WAIT_ANY_SID_MSK, + .flags_interrupts = 0, + .rot_services = NULL, + .rot_services_count = NEG_SERVER_PART2_ROT_SRV_COUNT, + .extern_sids = NULL, + .extern_sids_count = NEG_SERVER_PART2_EXT_ROT_SRV_COUNT, + .irq_mapper = NULL, + }, +}; + +/* Check all the defined memory regions for overlapping. */ + +/* A list of all the memory regions. */ +const mem_region_t *mem_regions = NULL; + +const uint32_t mem_region_count = 0; + +// forward declaration of partition initializers +void neg_server_part1_init(spm_partition_t *partition); +void neg_server_part2_init(spm_partition_t *partition); + +uint32_t init_partitions(spm_partition_t **partitions) +{ + if (NULL == partitions) { + SPM_PANIC("partitions is NULL!\n"); + } + + neg_server_part1_init(&(g_partitions[0])); + neg_server_part2_init(&(g_partitions[1])); + + *partitions = g_partitions; + return 2; +} + diff --git a/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/server1.c b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/server1.c new file mode 100644 index 0000000..d984150 --- /dev/null +++ b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/server1.c @@ -0,0 +1,93 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "unity.h" +#include "spm_panic.h" +#include "spm_server.h" +#include "psa_neg_server_part1_partition.h" + + +void server_main1(void *ptr) +{ + psa_msg_t msg = {0}; + uint32_t signals = 0; + + while (true) { + signals = psa_wait_any(PSA_BLOCK); + if (signals & PART1_CLEAR_NO_DOORBELL_MSK) { + psa_get(PART1_CLEAR_NO_DOORBELL_MSK, &msg); + switch (msg.type) { + case PSA_IPC_CONNECT: { + psa_clear(); + TEST_FAIL_MESSAGE("server_clear_no_doorbell negative test failed"); + } + default: { + TEST_FAIL_MESSAGE("server_clear_no_doorbell msg type failure"); + } + } + } + else if (signals & PART1_REPLY_INVALID_RETVAL_CONNECT_MSK) { + psa_get(PART1_REPLY_INVALID_RETVAL_CONNECT_MSK, &msg); + switch (msg.type) { + case PSA_IPC_CONNECT: { + psa_reply(msg.handle, PSA_CONNECTION_ACCEPTED + 1); + TEST_FAIL_MESSAGE("server_reply_invalid_retval_connect negative test failed"); + } + default: { + TEST_FAIL_MESSAGE("server_reply_invalid_retval_connect msg type failure"); + } + } + } + else if (signals & PART1_REPLY_INVALID_RETVAL_CALL_MSK) { + psa_get(PART1_REPLY_INVALID_RETVAL_CALL_MSK, &msg); + switch (msg.type) { + case PSA_IPC_CONNECT: { + psa_reply(msg.handle, PSA_CONNECTION_ACCEPTED); + break; + } + case PSA_IPC_CALL: { + psa_reply(msg.handle, PSA_DROP_CONNECTION + 1); + TEST_FAIL_MESSAGE("server_reply_invalid_retval_call negative test failed"); + } + default: { + TEST_FAIL_MESSAGE("server_reply_invalid_retval_call msg type failure"); + } + } + } + else if (signals & PART1_WRITE_OUTVEC_IX_SIZE_0_MSK) { + psa_get(PART1_WRITE_OUTVEC_IX_SIZE_0_MSK, &msg); + switch (msg.type) { + case PSA_IPC_CONNECT: { + psa_reply(msg.handle, PSA_CONNECTION_ACCEPTED); + break; + } + case PSA_IPC_CALL: { + uint32_t val = 0; + psa_write(msg.handle, 1, &val, sizeof(val)); + TEST_FAIL_MESSAGE("server_write_from_outvec_index_size_0 negative test failed"); + } + default: { + TEST_FAIL_MESSAGE("server_write_from_outvec_index_size_0 msg type failure"); + } + } + } + else { + SPM_ASSERT(false); + } + } +} + diff --git a/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/server2.c b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/server2.c new file mode 100644 index 0000000..04e6022 --- /dev/null +++ b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/server2.c @@ -0,0 +1,409 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "unity.h" +#include "spm_panic.h" +#include "spm_server.h" +#include "psa_neg_server_part2_partition.h" + +#define MSG_BUF_SIZE 128 +#define CLIENT_RSP_BUF_SIZE 128 +#define INVALID_INT_MASK 0xFFFFFFFF +#define MULTIPLE_SIGNUM 0x00000003 +#define NUM_OF_FLAGS 24 +#define INVALID_PARTION_ID 10 + + +char msg_buf[MSG_BUF_SIZE]; +char res_buff[MSG_BUF_SIZE]; + +void server_main2(void *ptr) +{ + psa_msg_t msg = {0}; + uint32_t signals = 0; + memset(msg_buf, 0, sizeof(msg_buf)); + memset(res_buff, 0, sizeof(res_buff)); + + while (true) { + signals = psa_wait_any(PSA_BLOCK); + if (signals & PART2_INT_MASK_MSK) { + psa_wait_interrupt(INVALID_INT_MASK, PSA_BLOCK); + TEST_FAIL_MESSAGE("server_interrupt_mask_invalid negative test failed"); + } + else if (signals & PART2_GET_MSG_NULL_MSK) { + psa_get(PART2_GET_MSG_NULL_MSK, NULL); + TEST_FAIL_MESSAGE("server_get_msg_null negative test failed"); + } + else if (signals & PART2_GET_SIGNUM_MULTIPLE_BIT_MSK) { + psa_get(MULTIPLE_SIGNUM, &msg); + TEST_FAIL_MESSAGE("server_get_multiple_bit_signum negative test failed"); + } + else if (signals & PART2_GET_SIGNUM_NOT_SUBSET_MSK) { + psa_get(PART2_GET_SIGNUM_NOT_SUBSET_MSK << NUM_OF_FLAGS, &msg); + TEST_FAIL_MESSAGE("server_get_signum_not_subset negative test failed"); + } + else if (signals & PART2_GET_SIGNUM_NOT_ACTIVE_MSK) { + psa_get(PART2_GET_MSG_NULL_MSK, &msg); //send wrong flag + TEST_FAIL_MESSAGE("server_get_signum_not_active negative test failed"); + } + else if (signals & PART2_GET_SIGNUM_TWICE_MSK) { + psa_get(PART2_GET_SIGNUM_TWICE_MSK, &msg); + psa_get(PART2_GET_SIGNUM_TWICE_MSK, &msg); + TEST_FAIL_MESSAGE("server_get_signum_twice negative test failed"); + } + else if (signals & PART2_READ_INVALID_HANDLE_MSK) { + psa_get(PART2_READ_INVALID_HANDLE_MSK, &msg); + switch (msg.type) { + case PSA_IPC_CONNECT: { + break; + } + case PSA_IPC_CALL: { + psa_handle_t invalid_handle = msg.handle + 10; + psa_read(invalid_handle, 0, msg_buf, msg.in_size[0]); + TEST_FAIL_MESSAGE("server_read_invalid_handle negative test failed"); + break; + } + default: { + TEST_FAIL_MESSAGE("server_read_invalid_handle msg type failure"); + } + } + psa_reply(msg.handle, PSA_SUCCESS); + } + else if (signals & PART2_READ_NULL_HANDLE_MSK) { + psa_get(PART2_READ_NULL_HANDLE_MSK, &msg); + switch (msg.type) { + case PSA_IPC_CONNECT: { + break; + } + case PSA_IPC_CALL: { + psa_read(PSA_NULL_HANDLE, 0, msg_buf, msg.in_size[0]); + TEST_FAIL_MESSAGE("server_read_null_handle negative test failed"); + break; + } + default: { + TEST_FAIL_MESSAGE("server_read_null_handle msg type failure"); + } + } + psa_reply(msg.handle, PSA_SUCCESS); + } + else if (signals & PART2_READ_NULL_BUFFER_MSK) { + psa_get(PART2_READ_NULL_BUFFER_MSK, &msg); + switch (msg.type) { + case PSA_IPC_CONNECT: { + break; + } + case PSA_IPC_CALL: { + psa_read(msg.handle, 0, NULL, msg.in_size[0]); + TEST_FAIL_MESSAGE("server_read_null_buffer negative test failed"); + break; + } + default: { + TEST_FAIL_MESSAGE("server_read_null_buffer msg type failure"); + } + } + psa_reply(msg.handle, PSA_SUCCESS); + } + else if (signals & PART2_WRITE_BUFFER_NULL_MSK) { + psa_get(PART2_WRITE_BUFFER_NULL_MSK, &msg); + switch (msg.type) { + case PSA_IPC_CONNECT: { + break; + } + case PSA_IPC_CALL: { + size_t read_bytes = 0; + for (size_t i = 0; i< PSA_MAX_IOVEC - 1; i++) { + read_bytes += psa_read(msg.handle, i, msg_buf + read_bytes, msg.in_size[i]); + } + + psa_write(msg.handle, 0, NULL, read_bytes); + TEST_FAIL_MESSAGE("server_write_null_buffer negative test failed"); + break; + } + default: { + TEST_FAIL_MESSAGE("server_write_null_buffer msg type failure"); + } + } + psa_reply(msg.handle, PSA_SUCCESS); + } + else if (signals & PART2_WRITE_RX_BUFF_NULL_MSK) { + psa_get(PART2_WRITE_RX_BUFF_NULL_MSK, &msg); + switch (msg.type) { + case PSA_IPC_CONNECT: { + break; + } + case PSA_IPC_CALL: { + size_t read_bytes = 0; + for (size_t i = 0; i< PSA_MAX_IOVEC; i++) { + read_bytes += psa_read(msg.handle, i, msg_buf + read_bytes, msg.in_size[i]); + } + + psa_write(msg.handle, 0, res_buff, read_bytes); + TEST_FAIL_MESSAGE("server_write_rx_buff_null negative test failed"); + break; + } + default: { + TEST_FAIL_MESSAGE("server_write_rx_buff_null msg type failure"); + } + } + psa_reply(msg.handle, PSA_SUCCESS); + } + else if (signals & PART2_WRITE_INVALID_HANDLE_MSK) { + psa_get(PART2_WRITE_INVALID_HANDLE_MSK, &msg); + switch (msg.type) { + case PSA_IPC_CONNECT: { + break; + } + case PSA_IPC_CALL: { + size_t read_bytes = 0; + for (size_t i = 0; i< PSA_MAX_IOVEC - 1; i++) { + read_bytes += psa_read(msg.handle, i, msg_buf + read_bytes, msg.in_size[i]); + } + + psa_handle_t invalid_handle = msg.handle + 10; + psa_write(invalid_handle, 0, res_buff, read_bytes); + TEST_FAIL_MESSAGE("server_write_invalid_handle negative test failed"); + break; + } + default: { + TEST_FAIL_MESSAGE("server_write_invalid_handle msg type failure"); + } + } + psa_reply(msg.handle, PSA_SUCCESS); + } + else if (signals & PART2_WRITE_NULL_HANDLE_MSK) { + psa_get(PART2_WRITE_NULL_HANDLE_MSK, &msg); + switch (msg.type) { + case PSA_IPC_CONNECT: { + break; + } + case PSA_IPC_CALL: { + size_t read_bytes = 0; + for (size_t i = 0; i< PSA_MAX_IOVEC - 1; i++) { + read_bytes += psa_read(msg.handle, i, msg_buf + read_bytes, msg.in_size[i]); + } + + psa_write(PSA_NULL_HANDLE, 0, res_buff, read_bytes); + TEST_FAIL_MESSAGE("server_write_null_handle negative test failed"); + break; + } + default: { + TEST_FAIL_MESSAGE("server_write_null_handle msg type failure"); + } + } + psa_reply(msg.handle, PSA_SUCCESS); + } + else if (signals & PART2_REPLY_INVALID_HANDLE_MSK) { + psa_get(PART2_REPLY_INVALID_HANDLE_MSK, &msg); + switch (msg.type) { + case PSA_IPC_CONNECT: { + break; + } + default: { + TEST_FAIL_MESSAGE("server_reply_invalid_handle msg type failure"); + } + } + psa_handle_t invalid_handle = msg.handle + 10; + psa_reply(invalid_handle, PSA_SUCCESS); + TEST_FAIL_MESSAGE("server_reply_invalid_handle negative test failed"); + } + else if (signals & PART2_REPLY_NULL_HANDLE_MSK) { + psa_get(PART2_REPLY_NULL_HANDLE_MSK, &msg); + switch (msg.type) { + case PSA_IPC_CONNECT: { + psa_reply(PSA_NULL_HANDLE, PSA_CONNECTION_ACCEPTED); + TEST_FAIL_MESSAGE("server_reply_null_handle negative test failed"); + break; + } + default: { + TEST_FAIL_MESSAGE("server_reply_null_handle msg type failure"); + } + } + } + else if (signals & PART2_NOTIFY_PART_ID_INVALID_MSK) { + psa_get(PART2_NOTIFY_PART_ID_INVALID_MSK, &msg); + switch (msg.type) { + case PSA_IPC_CONNECT: { + psa_notify(INVALID_PARTION_ID); + TEST_FAIL_MESSAGE("server_notify_part_id_invalid negative test failed"); + break; + } + default: { + TEST_FAIL_MESSAGE("server_notify_part_id_invalid msg type failure"); + } + } + psa_reply(msg.handle, PSA_SUCCESS); + } + else if (signals & PART2_IDENTITY_INVALID_HANDLE_MSK) { + psa_get(PART2_IDENTITY_INVALID_HANDLE_MSK, &msg); + switch (msg.type) { + case PSA_IPC_CONNECT: { + int32_t ret = 0; + psa_handle_t invalid_handle = msg.handle + 10; + ret = psa_identity(invalid_handle); + PSA_UNUSED(ret); + TEST_FAIL_MESSAGE("server_psa_identity_invalid_handle negative test failed"); + break; + } + default: { + TEST_FAIL_MESSAGE("server_psa_identity_invalid_handle msg type failure"); + } + } + psa_reply(msg.handle, PSA_SUCCESS); + } + else if (signals & PART2_IDENTITY_NULL_HANDLE_MSK) { + psa_get(PART2_IDENTITY_NULL_HANDLE_MSK, &msg); + switch (msg.type) { + case PSA_IPC_CONNECT: { + int32_t ret = 0; + ret = psa_identity(PSA_NULL_HANDLE); + PSA_UNUSED(ret); + TEST_FAIL_MESSAGE("server_psa_identity_null_handle negative test failed"); + break; + } + default: { + TEST_FAIL_MESSAGE("server_psa_identity_null_handle msg type failure"); + } + } + psa_reply(msg.handle, PSA_SUCCESS); + } + else if (signals & PART2_SET_RHANDLE_INVALID_HANDLE_MSK) { + psa_get(PART2_SET_RHANDLE_INVALID_HANDLE_MSK, &msg); + switch (msg.type) { + case PSA_IPC_CONNECT: { + psa_handle_t invalid_handle = msg.handle + 10; + psa_set_rhandle(invalid_handle, NULL); + TEST_FAIL_MESSAGE("server_set_rhandle_invalid_handle negative test failed"); + break; + } + default: { + TEST_FAIL_MESSAGE("server_set_rhandle_invalid_handle msg type failure"); + } + } + psa_reply(msg.handle, PSA_SUCCESS); + } + else if (signals & PART2_SET_RHANDLE_NULL_HANDLE_MSK) { + psa_get(PART2_SET_RHANDLE_NULL_HANDLE_MSK, &msg); + switch (msg.type) { + case PSA_IPC_CONNECT: { + psa_set_rhandle(PSA_NULL_HANDLE, NULL); + TEST_FAIL_MESSAGE("server_set_rhandle_null_handle negative test failed"); + break; + } + default: { + TEST_FAIL_MESSAGE("server_set_rhandle_null_handle msg type failure"); + } + } + psa_reply(msg.handle, PSA_SUCCESS); + } + else if (signals & PART2_READ_WRAPAROUND_MSK) { + psa_get(PART2_READ_WRAPAROUND_MSK, &msg); + switch (msg.type) { + case PSA_IPC_CONNECT: { + break; + } + case PSA_IPC_CALL: { + psa_read(msg.handle, 0, (void*)0x80000000, UINT32_MAX); + TEST_FAIL_MESSAGE("server_read_on_wraparound_msg_ptr negative test failed"); + break; + } + + default: { + TEST_FAIL_MESSAGE("server_read_on_wraparound_msg_ptr msg type failure"); + } + } + psa_reply(msg.handle, PSA_SUCCESS); + } + else if (signals & PART2_READ_EXCESS_INVEC_MSK) { + psa_get(PART2_READ_EXCESS_INVEC_MSK, &msg); + switch (msg.type) { + case PSA_IPC_CONNECT: { + break; + } + case PSA_IPC_CALL: { + uint32_t val = 0; + psa_read(msg.handle, PSA_MAX_IOVEC, &val, sizeof(val)); + TEST_FAIL_MESSAGE("server_read_on_wraparound_msg_ptr negative test failed"); + break; + } + + default: { + TEST_FAIL_MESSAGE("server_read_on_wraparound_msg_ptr msg type failure"); + } + } + psa_reply(msg.handle, PSA_SUCCESS); + } + else if (signals & PART2_WRITE_WRAPAROUND_MSK) { + psa_get(PART2_WRITE_WRAPAROUND_MSK, &msg); + switch (msg.type) { + case PSA_IPC_CONNECT: { + break; + } + case PSA_IPC_CALL: { + psa_write(msg.handle, 0, (void*)0x80000000, UINT32_MAX); + TEST_FAIL_MESSAGE("server_write_on_wraparound_msg_ptr negative test failed"); + break; + } + + default: { + TEST_FAIL_MESSAGE("server_write_on_wraparound_msg_ptr msg type failure"); + } + } + psa_reply(msg.handle, PSA_SUCCESS); + } + else if (signals & PART2_WRITE_SIZE_OVERFLOW_MSK) { + psa_get(PART2_WRITE_SIZE_OVERFLOW_MSK, &msg); + switch (msg.type) { + case PSA_IPC_CONNECT: { + break; + } + case PSA_IPC_CALL: { + uint8_t write_buf[CLIENT_RSP_BUF_SIZE + 1] = {0}; + psa_write(msg.handle, 0, write_buf, sizeof(write_buf)); + TEST_FAIL_MESSAGE("server_write_with_size_overflow negative test failed"); + break; + } + + default: { + TEST_FAIL_MESSAGE("server_write_with_size_overflow msg type failure"); + } + } + psa_reply(msg.handle, PSA_SUCCESS); + } + else if (signals & PART2_WRITE_EXCESS_OUTVEC_MSK) { + psa_get(PART2_WRITE_EXCESS_OUTVEC_MSK, &msg); + switch (msg.type) { + case PSA_IPC_CONNECT: { + break; + } + case PSA_IPC_CALL: { + uint32_t val = 0; + psa_write(msg.handle, PSA_MAX_IOVEC, &val, sizeof(val)); + TEST_FAIL_MESSAGE("server_write_from_excess_outvec negative test failed"); + break; + } + + default: { + TEST_FAIL_MESSAGE("server_write_from_excess_outvec msg type failure"); + } + } + psa_reply(msg.handle, PSA_SUCCESS); + } else { + SPM_PANIC("Unknown signal (0x%08x)", signals); + } + } +} + diff --git a/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/spm_reboot.c b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/spm_reboot.c new file mode 100644 index 0000000..35865b9 --- /dev/null +++ b/components/TARGET_PSA/spm/TARGET_IGNORE/neg_server_tests/spm_reboot.c @@ -0,0 +1,67 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "spm_internal.h" + +extern spm_db_t g_spm; +extern psa_handle_item_t g_channels_handle_storage[]; +extern psa_handle_item_t g_messages_handle_storage[]; +extern spm_ipc_channel_t g_channel_data[]; +extern spm_active_msg_t g_active_messages_data[]; + +void psa_spm_init(void); + +void spm_reboot(void) +{ + osStatus_t status; + + for (uint32_t i = 0; i < g_spm.partition_count; ++i) { + status = osThreadTerminate(g_spm.partitions[i].thread_id); + MBED_ASSERT(status == osOK); + status = osMutexDelete(g_spm.partitions[i].mutex); + MBED_ASSERT(status == osOK); + + for (uint32_t j = 0; j < g_spm.partitions[i].rot_services_count; ++j) { + g_spm.partitions[i].rot_services[j].partition = NULL; + g_spm.partitions[i].rot_services[j].queue.head = NULL; + g_spm.partitions[i].rot_services[j].queue.tail = NULL; + } + + g_spm.partitions[i].rot_services = NULL; + g_spm.partitions[i].mutex = NULL; + g_spm.partitions[i].thread_id = NULL; + } + + status = osMemoryPoolDelete(g_spm.channel_mem_pool); + MBED_ASSERT(status == osOK); + status = osMemoryPoolDelete(g_spm.active_messages_mem_pool); + MBED_ASSERT(status == osOK); + memset(g_channels_handle_storage, 0, (sizeof(psa_handle_item_t) * MBED_CONF_SPM_IPC_MAX_NUM_OF_CHANNELS)); + memset(g_messages_handle_storage, 0, (sizeof(psa_handle_item_t) * MBED_CONF_SPM_IPC_MAX_NUM_OF_MESSAGES)); + memset(g_channel_data, 0, (sizeof(spm_ipc_channel_t) * MBED_CONF_SPM_IPC_MAX_NUM_OF_CHANNELS)); + memset(g_active_messages_data, 0, (sizeof(spm_active_msg_t) * MBED_CONF_SPM_IPC_MAX_NUM_OF_MESSAGES)); + memset(&g_spm, 0, sizeof(g_spm)); + + g_spm.channels_handle_mgr.handle_generator = PSA_HANDLE_MGR_INVALID_HANDLE; + g_spm.channels_handle_mgr.pool_size = MBED_CONF_SPM_IPC_MAX_NUM_OF_CHANNELS; + g_spm.channels_handle_mgr.handles_pool = g_channels_handle_storage; + g_spm.messages_handle_mgr.handle_generator = PSA_HANDLE_MGR_INVALID_HANDLE; + g_spm.messages_handle_mgr.pool_size = MBED_CONF_SPM_IPC_MAX_NUM_OF_MESSAGES; + g_spm.messages_handle_mgr.handles_pool = g_messages_handle_storage; + + psa_spm_init(); + PSA_UNUSED(status); +} diff --git a/components/TARGET_PSA/spm/doc/INTRO.md b/components/TARGET_PSA/spm/doc/INTRO.md new file mode 100644 index 0000000..7796b33 --- /dev/null +++ b/components/TARGET_PSA/spm/doc/INTRO.md @@ -0,0 +1,117 @@ +## The Secure Partition Manager + +The **Secure Partition Manager (SPM)** is a Platform Security Architecture (PSA) compliant software hypervisor that creates and manages independent Secure Partitions on Arm Cortex®-M microcontrollers. It increases resilience against malware and protects secrets from leaking between different modules in the same application. The SPM complements other important security features, such as safe firmware updates and secure crypto libraries. + +The SPM provides hardware-enforced partitions for individual code blocks by limiting access to memories and peripherals using the existing hardware security features of the Cortex®-M microcontrollers. It isolates software in partitions, managing the execution of software within those partitions and providing Inter Process Communication (IPC) between the partitions. Correct use of SPM prevents malware from becoming resident on the device and enables protection of device secrets, such as cryptographic keys. + +### Isolating partitions in the Secure Processing Environment + +The SPM and the secure partitions are located in the Secure Processing Environment (SPE), isolating them from the Non-Secure Processing Environment (NSPE), which contains the application firmware, OS kernel and libraries, and other nonsecure hardware resources. + +A secure partition is a container for one or more root of trust services, and a platform may have multiple secure partitions. Secure partitions provide the execution environment for security functionality. + +Platform hardware, such as the Security Attribution Unit (SAU) and Memory Protection Unit (MPU) in the new ARMv8-M platforms, enforces the separation of partitions. Other platforms may use different mechanisms to provide equivalent isolation for the partitions. + +#### PSA levels of isolation + +If you are prototyping software or using platforms without SAU or MPU, you can choose to have no isolation between the SPE and NSPE (sometimes referred to as Level 0), though the PSA does not specify this. However, for production software, consider implementing one of the following levels of isolation: + +* **Level 1 - SPE isolation** In this level, the SPE is fully isolated from the nonsecure application firmware and hardware. +* **Level 2 - Root of Trust isolation** In this level, the SPE is fully isolated from the nonsecure application firmware and hardware and the trusted partitions (secure partitions that implement Root of Trust services) are isolated from other secure partitions. +* **Level 3 - Maximum firmware isolation** In this level, the SPE is fully isolated from the nonsecure application firmware and hardware, and all secure and trusted partitions are individually sandboxed from one another and from the SPM. + +### Using secure partitions + +Secure partitions are located within the SPE, and must contain at least one set of related security operations (known as a root of trust service) or at least one Interrupt Request (IRQ). You can have multiple root of trust services in a single secure partition. + +For a secure partition, you need: + +* The **secure partition code**, which must: + * Be single threaded. + * Be structured as a loop that waits for inputs. + * Never exit its loop (considered as a programming error). + * Be written in C or 'extern "C"' to avoid C++ name mangling. + * Follow PSA IPC rules. Secure partitions communicate with one another using the IPC API defined in [IPC API](https://github.com/ARMmbed/PSA-IPC-doc/blob/master/IPC_revision.md). All IPC messages must eventually be completed [`call psa_end()`]. Note that the SPM does not schedule IPC messages fairly. +* A **manifest file** in JSON format, that describes the Secure Partition characteristics. The specifications in the manifest file are validated during the build process and at run time. + +### Manifest file example + +The secure partition manifest file describes the properties of the secure partitions. In this file: + +* **entry_point** is the function name of the partition's thread. +* **source_files** is the list of source files containing the partition's code. +* **heap_size** sets the heap size for platforms that have an isolation level of 2 and higher. +* **services** is the list of the partition's root of trust services with their properties. +* **extern_sids** defines a dependency to other Root of Trust Service (referenced by SID). If the manifest does not specify the access between a partition (acting as client) and a root of trust service (acting as server), then the client is not able to send any messages to the root of trust service. + +For example: + +```json +{ + "name": "BOX_MAIN", + "type": "APPLICATION-ROT", + "priority": "NORMAL", + "id": "0x7BADD00D", + "entry_point": "main", + "stack_size": 10, + "heap_size": "0x0400", + "mmio_regions": [ + { + "name": "CMU", + "permission": "READ-WRITE" + }, + { + "name": "MSC", + "permission": "READ-WRITE" + }, + { + "name": "GPIO", + "permission": "READ-WRITE" + }, + { + "name": "TIMER0", + "permission": "READ-WRITE" + }, + { + "name": "UART0", + "permission": "READ-WRITE" + }, + { + "base": "0x10000000", + "size": "0x1000", + "permission": "READ-ONLY" + }, + { + "base": "0x42000000", + "size": "0x02000000", + "permission": "READ-ONLY" + } + ], + "services": [ + { + "sid": "PSA_TRUSTED_UPDATE", + "signal": "PSA_TRUSTED_UPDATE", + "non_secure_clients": true, + "minor_version": 1, + "minor_policy": "STRICT" + } + ], + "extern_sids": [ + "PSA_CRYPTO_RSA", + "PSA_CRYPTO_AES" + ], + "source_files": [ + "../source/main.cpp" + ], + "irqs": [ + { + "signal": "MY_IRQ", + "line_num": 4 + } + ] +} +``` + +#### Code example + +[Mbed SPM example on GitHub](https://github.com/ARMmbed/mbed-os-example-spm) diff --git a/components/TARGET_PSA/spm/doc/README.md b/components/TARGET_PSA/spm/doc/README.md new file mode 100644 index 0000000..fb7cf74 --- /dev/null +++ b/components/TARGET_PSA/spm/doc/README.md @@ -0,0 +1,23 @@ +# Mbed Secure Partition Manager (SPM) + +The Platform Security Architecture (PSA) firmware framework specifications contain a logic component called the Secure Partition Manager (SPM). PSA defines a Secure Processing Environment (SPE) for: + +* Sensitive data, such as keys, credentials and firmware. +* The code that manages it. +* Its trusted hardware resources. + +The PSA SPM interfaces decouple components, allowing reuse of components in other device platform and helps to reduce an integration effort. + +Mbed SPM is an implementation of PSA SPM, which: + +* Secures low cost IoT devices, where a full Trusted Execution Environment (TEE) would not be appropriate. +* Protects sensitive assets (keys, credentials and firmware) by separating these from the application firmware and hardware. +* Is architecture agnostic and can be implemented on different Arm Cortex®-M architectures, offering variable level of protection, based on platform resources. + +![diagram](png/PSA-standardized-Interfaces-diagram.png) + +## Further reading + +* The [introduction to PSA SPM](INTRO.md) provides more details about PSA SPM. +* Visit the official Arm Platform Security Architecture web page https://pages.arm.com/psa-resources +* Trusted firmware presentation during the Linaro Connect event by James King on IOT http://connect.linaro.org/resource/hkg18/hkg18-212/ diff --git a/components/TARGET_PSA/spm/doc/png/PSA-standardized-Interfaces-diagram.png b/components/TARGET_PSA/spm/doc/png/PSA-standardized-Interfaces-diagram.png new file mode 100644 index 0000000..f8c4cc8 --- /dev/null +++ b/components/TARGET_PSA/spm/doc/png/PSA-standardized-Interfaces-diagram.png Binary files differ diff --git a/components/TARGET_PSA/spm/mbed_lib.json b/components/TARGET_PSA/spm/mbed_lib.json new file mode 100644 index 0000000..68c7d67 --- /dev/null +++ b/components/TARGET_PSA/spm/mbed_lib.json @@ -0,0 +1,13 @@ +{ + "name": "spm", + "config": { + "ipc_max_num_of_channels": { + "help": "maximum number of PSA channels that can be opened at the same time", + "value": 10 + }, + "ipc_max_num_of_messages": { + "help": "maximum number of messages SPM Core can handle at a time", + "value": 10 + } + } +} diff --git a/components/TARGET_PSA/spm/psa_defs.h b/components/TARGET_PSA/spm/psa_defs.h new file mode 100644 index 0000000..08a4d8a --- /dev/null +++ b/components/TARGET_PSA/spm/psa_defs.h @@ -0,0 +1,117 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MBED_PSA_DEFS_H__ +#define __MBED_PSA_DEFS_H__ + +/** @addtogroup SPM + * @{ + */ + +/* -------------------------------------- Includes ----------------------------------- */ + +#include +#include + +/* --------------------------------- extern "C" wrapper ------------------------------ */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------------------------ Definitions ---------------------------------- */ + +#if !defined(UINT32_MAX) +#define UINT32_MAX ((uint32_t)-1) +#endif + +#if !defined(INT32_MIN) +#define INT32_MIN (-0x7fffffff - 1) +#endif + +#define PSA_FRAMEWORK_VERSION (0x0009) /**< Version of the PSA Framework API. */ +#define PSA_VERSION_NONE (0L) /**< Identifier for an unimplemented RoT Service. */ + +#define PSA_NSPE_IDENTIFIER (-1L) /**< "Partition" identifier of the NSPE.*/ + +#define PSA_NULL_HANDLE ((psa_handle_t)0) /**< Denotes an invalid handle.*/ + +#define PSA_MAX_IOVEC (4UL) /**< Maximum number of psa_invec_t and psa_outvec_t structures allowed for psa_call().*/ + +#define PSA_POLL (0x00000000UL) /**< Returns immediately even if none of the requested signals is asserted.*/ +#define PSA_BLOCK (0x80000000UL) /**< Block the caller until one of the requested signals is asserted.*/ + +#define PSA_MINOR_VERSION_POLICY_RELAXED (0UL) /**< Don't perform minor version check during psa_connect().*/ +#define PSA_MINOR_VERSION_POLICY_STRICT (1UL) /**< Force minor version check during psa_connect().*/ + +#define PSA_DOORBELL (0x00000008UL) /**< Mask for PSA_DOORBELL signal.*/ + +#define PSA_SUCCESS (0L) /**< A general result code for calls to psa_call() indicating success.*/ +#define PSA_CONNECTION_ACCEPTED (0L) /**< The result code for calls to psa_connect() indicating the acceptance of a new connection request.*/ +#define PSA_IPC_CONNECT (1) /**< The IPC message type that indicates a new connection.*/ +#define PSA_IPC_CALL (2) /**< The IPC message type that indicates a client request.*/ +#define PSA_IPC_DISCONNECT (3) /**< The IPC message type that indicates the end of a connection.*/ +#define PSA_IPC_VERSION (4) /**< The IPC message type that indicates a client query for a specific sid.*/ + +/* Error codes */ +#define PSA_DROP_CONNECTION (INT32_MIN) /**< The result code in a call to psa_reply() to indicate a non-recoverable error in the client.*/ +#define PSA_CONNECTION_REFUSED (INT32_MIN + 1) /**< The return value from psa_connect() if the RoT Service or SPM was unable to establish a connection.*/ + +#define PSA_UNUSED(var) ((void)(var)) + +/* -------------------------------------- Typedefs ----------------------------------- */ + +typedef uint32_t psa_signal_t; +typedef int32_t psa_error_t; +typedef int32_t psa_handle_t; +typedef psa_error_t error_t; + +/* -------------------------------------- Structs ------------------------------------ */ + +/** + * Structure containing the PSA IPC message sent from a client partition to a Root of Trust Service. + */ +typedef struct psa_msg { + uint32_t type; /**< The message type.*/ + psa_handle_t handle; /**< Handle for the internal message structure.*/ + void *rhandle; /**< Reverse handle.*/ + size_t in_size[PSA_MAX_IOVEC]; /**< Array of sizes in bytes of the message payloads.*/ + size_t out_size[PSA_MAX_IOVEC]; /**< Array of sizes in bytes of the response buffers.*/ +} psa_msg_t; + +/** + * Structure which describes a scatter-gather input buffer. + */ +typedef struct psa_invec { + const void *base; /**< Starting address of the buffer.*/ + size_t len; /**< Length in bytes of the buffer.*/ +} psa_invec_t; + +/** + * Structure which describes a scatter-gather output buffer. + */ +typedef struct psa_outvec { + void *base; /**< Starting address of the buffer.*/ + size_t len; /**< Length in bytes of the buffer.*/ +} psa_outvec_t; + +#ifdef __cplusplus +} +#endif + + +/** @}*/ // end of SPM group + +#endif /* __MBED_PSA_DEFS_H__ */ diff --git a/components/TARGET_PSA/spm/spm_client.h b/components/TARGET_PSA/spm/spm_client.h new file mode 100644 index 0000000..29575fa --- /dev/null +++ b/components/TARGET_PSA/spm/spm_client.h @@ -0,0 +1,110 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MBED_SPM_CLIENT_H__ +#define __MBED_SPM_CLIENT_H__ + +/** @addtogroup SPM + * The SPM (Secure Partition Manager) is responsible for isolating software in partitions,@n + * managing the execution of software within partitions, and providing IPC between partitions. + * @{ + */ + +/* -------------------------------------- Includes ----------------------------------- */ + +#include "psa_defs.h" + +/* --------------------------------- extern "C" wrapper ------------------------------ */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** @addtogroup Client-API + * The C interface for connecting to a Root of Trust Service and calling it.@n + * All functions are blocking. + * @{ + */ + +/** + * Retrieve the version of the PSA Framework API that is implemented. + * + * @note The PSA Framework API version is made up of the major and minor versions as follows: + * @code + * ((major_version << 8) | minor_version) + * @endcode + * @return The PSA Framework API version + */ +uint32_t psa_framework_version(void); + +/** + * Retrieve the minor version of a Root of Trust Service by its SID. + * + * @param[in] sid The Root of Trust Service ID + * @return Minor version of Root of Trust Service or PSA_VERSION_NONE if Root of Trust Service not present on the system. + */ +uint32_t psa_version(uint32_t sid); + +/** + * Connect to a Root of Trust Service by its SID. + * + * @note A minor version number must be provided to allow the Root of Trust Service to handle minor variations of the protocol. + * + * @param[in] sid The Root of Trust Service ID. + * @param[in] minor_version The minor version to be used for this connection. + * @return A handle for the connection if greater than 0, else:@n + * @a PSA_CONNECTION_REFUSED if the Root of Trust Service cannot handle any more connections.@n + */ +psa_handle_t psa_connect(uint32_t sid, uint32_t minor_version); + +/** + * Call a connected Root of Trust Service.@n + * The caller must provide an array of ::psa_invec_t structures as the input payload. + * + * @param[in] handle Handle for the connection. + * @param[in] in_vec Array of ::psa_invec_t structures. + * @param[in] in_len Number of ::psa_invec_t structures in in_vec. (At most ::PSA_MAX_IOVEC - out_len) + * @param[out] out_vec Array of ::psa_outvec_t structures for optional Root of Trust Service response. + * @param[in] out_len Number of ::psa_outvec_t structures in out_vec. (At most ::PSA_MAX_IOVEC - in_len) + * @return 0 for success or@n + * @a positive numbers for application-specific return code. + * @a negative number for application-specific error code. + * @a PSA_DROP_CONNECTION if the connection has been dropped by the RoT Service. + */ +psa_error_t psa_call( + psa_handle_t handle, + const psa_invec_t *in_vec, + size_t in_len, + const psa_outvec_t *out_vec, + size_t out_len + ); + +/** + * Close a connection to a Root of Trust Service. + * Sends the ::PSA_IPC_DISCONNECT message to the Root of Trust Service so it can clean up resources. + * + * @param[in] handle Handle for the connection. + */ +void psa_close(psa_handle_t handle); + +/** @}*/ // end of Client-API group + +#ifdef __cplusplus +} +#endif + +/** @}*/ // end of SPM group + +#endif /* __MBED_SPM_CLIENT_H__ */ diff --git a/components/TARGET_PSA/spm/spm_init.h b/components/TARGET_PSA/spm/spm_init.h new file mode 100644 index 0000000..c596aa2 --- /dev/null +++ b/components/TARGET_PSA/spm/spm_init.h @@ -0,0 +1,37 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MBED_SPM_INIT_H__ +#define __MBED_SPM_INIT_H__ + +#if defined(COMPONENT_SPE) + +void psa_spm_init(void); + +#endif // defined(COMPONENT_SPE) + +#if defined(COMPONENT_SPM_MAILBOX) + +void spm_ipc_mailbox_init(void); + +#if defined(COMPONENT_NSPE) + +void psa_spm_mailbox_dispatcher(void); + +#endif // defined(COMPONENT_NSPE) + +#endif // defined(COMPONENT_SPM_MAILBOX) + +#endif // __MBED_SPM_INIT_H__ diff --git a/components/TARGET_PSA/spm/spm_messages.h b/components/TARGET_PSA/spm/spm_messages.h new file mode 100644 index 0000000..6d693e0 --- /dev/null +++ b/components/TARGET_PSA/spm/spm_messages.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2017, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SPM_MESSAGES_H +#define SPM_MESSAGES_H + +#include "cmsis_compiler.h" +#include "cmsis_os2.h" +#include "psa_defs.h" + +// All spm_pending_*_msg structs below are packed since in a dual processor +// solution they are used in both processors + +/* + * Structure containing data sent from NSPE for ROT_SRV call. + */ +typedef __PACKED_STRUCT spm_pending_call_msg { + const psa_invec_t *in_vec; /* Invecs sent.*/ + uint32_t in_vec_size; /* Number of Invecs sent.*/ + const psa_outvec_t *out_vec; /* Outvecs for response.*/ + uint32_t out_vec_size; /* Number of Outvecs for response.*/ + psa_error_t rc; /* Return code to be filled by the Root of Trust Service.*/ + osSemaphoreId_t completion_sem_id; /* Semaphore to be released at the end of execution */ +} __ALIGNED(4) spm_pending_call_msg_t; + +/* + * Structure containing data sent from NSPE for connection. + */ +typedef __PACKED_STRUCT spm_pending_connect_msg { + uint32_t min_version; /* Minor version of the Root of Trust Service interface.*/ + psa_error_t rc; /* Return code to be filled by the Root of Trust Service.*/ + osSemaphoreId_t completion_sem_id; /* Semaphore to be released at the end of execution */ +} __ALIGNED(4) spm_pending_connect_msg_t; + + +/* + * Structure containing data sent from NSPE for RoT-Service version query. + */ +typedef __PACKED_STRUCT spm_pending_version_msg { + uint32_t rc; /* Return code to be filled by the Root of Trust Service.*/ + osSemaphoreId_t completion_sem_id; /* Semaphore to be released at the end of execution */ +} __ALIGNED(4) spm_pending_version_msg_t; + +/* + * Structure containing data sent from NSPE for closing a connection. + */ +typedef __PACKED_STRUCT spm_pending_close_msg { + psa_handle_t handle; /* Handle of channel to be closed */ + osSemaphoreId_t completion_sem_id; /* Semaphore to be released at the end of execution */ +} __ALIGNED(4) spm_pending_close_msg_t; + +#endif // SPM_MESSAGES_H diff --git a/hal/spm_api.h b/hal/spm_api.h new file mode 100644 index 0000000..da38627 --- /dev/null +++ b/hal/spm_api.h @@ -0,0 +1,101 @@ +/* Copyright (c) 2018 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __SPM_API_H__ +#define __SPM_API_H__ + + +/** @addtogroup SPM + * The SPM (Secure Partition Manager) is responsible for isolating software in + * partitions, managing the execution of software within partitions, and + * providing IPC between partitions. + * @{ + */ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/** @addtogroup HAL-SPE + * The HAL functions for SPE. + * @{ + */ + + +/* ------------------------------------ HAL-SPE API ------------------------- */ + + +#if defined(COMPONENT_SPE) +/** + * Start running the NSPE. + * + * SPE (Secure Processing Environment) expected to boot first. Once all + * the initializations are done, NSPE (Non-Secure Processing Environment) + * should be booted. + * + * @note The function must be implemented by target specific code. + */ +void spm_hal_start_nspe(void); + + +/** + * Configure memory protection mechanism. + * + * Apply memory protection schemes to ensure secure memory can only be accessed + * from secure-state + * + * @note The function must be implemented by target specific code. + * + */ +void spm_hal_memory_protection_init(void); + +#endif // defined(COMPONENT_SPE) + +/* ---------------------------------- HAL-Mailbox API ----------------------- */ + +#if defined(COMPONENT_SPM_MAILBOX) +/** + * @brief Wakeup mailbox dispatcher thread + * + * This function is implemented by ARM and expected to be called by target + * specific Inter-Processor-Communication logic on mailbox interrupt handler. + * + */ +void spm_mailbox_irq_callback(void); + +/** + * @brief Notify the peer processor about a general event occurrence. + * + * Wakeup the peer processor waiting on the mailbox driver event. + * + * @note The functions below should be implemented by target specific code. + */ +void spm_hal_mailbox_notify(void); + +#endif // defined(COMPONENT_SPM_MAILBOX) + +/** @}*/ // end of HAL-SPE group + +#ifdef __cplusplus +} +#endif + +/** @}*/ // end of SPM group + +#endif // __SPM_API_H__ diff --git a/rtos/TARGET_CORTEX/mbed_rtos_rtx.c b/rtos/TARGET_CORTEX/mbed_rtos_rtx.c index 934eb8a..6e7a11c 100644 --- a/rtos/TARGET_CORTEX/mbed_rtos_rtx.c +++ b/rtos/TARGET_CORTEX/mbed_rtos_rtx.c @@ -23,6 +23,27 @@ #include "mbed_critical.h" #include "mbed_boot.h" +#if defined(TARGET_PSA) +#include "spm_init.h" +#include "spm_api.h" +#endif + + +#if defined(COMPONENT_NSPE) && defined(COMPONENT_SPM_MAILBOX) + +MBED_ALIGN(8) char psa_spm_dispatcher_th_stack[0x100]; +mbed_rtos_storage_thread_t psa_spm_dispatcher_th_tcb; +const osThreadAttr_t psa_spm_dispatcher_th_attr = { + .name = "SPM_DISP", + .priority = osPriorityNormal, + .stack_mem = psa_spm_dispatcher_th_stack, + .stack_size = sizeof(psa_spm_dispatcher_th_stack), + .cb_mem = &psa_spm_dispatcher_th_tcb, + .cb_size = sizeof(psa_spm_dispatcher_th_tcb) +}; + +#endif // defined(COMPONENT_NSPE) && defined(COMPONENT_SPM_MAILBOX) + osThreadAttr_t _main_thread_attr; #ifndef MBED_CONF_APP_MAIN_STACK_SIZE @@ -59,6 +80,23 @@ _main_thread_attr.tz_module = 1U; #endif +#if defined(COMPONENT_SPM_MAILBOX) + spm_ipc_mailbox_init(); +#endif // defined(COMPONENT_SPM_MAILBOX) + +#if defined(COMPONENT_SPE) + // At this point, the mailbox is already initialized + spm_hal_start_nspe(); + psa_spm_init(); +#endif // defined(COMPONENT_SPE) + +#if defined(COMPONENT_NSPE) && defined(COMPONENT_SPM_MAILBOX) + osThreadId_t spm_result = osThreadNew((osThreadFunc_t)psa_spm_mailbox_dispatcher, NULL, &psa_spm_dispatcher_th_attr); + if ((void *)spm_result == NULL) { + MBED_ERROR1(MBED_MAKE_ERROR(MBED_MODULE_PLATFORM, MBED_ERROR_CODE_INITIALIZATION_FAILED), "Dispatcher thread not created", &psa_spm_dispatcher_th_attr); + } +#endif // defined(COMPONENT_NSPE) && defined(COMPONENT_SPM_MAILBOX) + singleton_mutex_id = osMutexNew(&singleton_mutex_attr); osThreadId_t result = osThreadNew((osThreadFunc_t)mbed_start, NULL, &_main_thread_attr); if ((void *)result == NULL) { diff --git a/targets/targets.json b/targets/targets.json index 156f5ae..7ce44d5 100644 --- a/targets/targets.json +++ b/targets/targets.json @@ -32,6 +32,79 @@ } } }, + "PSA_Target": { + "public": false, + "config": { + "secure-rom-start": { + "help": "Starting address of Secure ROM", + "value": null, + "macro_name": "PSA_SECURE_ROM_START", + "conflicts": ["target.mbed_rom_start"] + }, + "secure-rom-size": { + "help": "Size in bytes of Secure ROM", + "value": null, + "macro_name": "PSA_SECURE_ROM_SIZE", + "conflicts": ["target.mbed_rom_size"] + }, + "non-secure-rom-start": { + "help": "Starting address of Non-secure ROM", + "value": null, + "macro_name": "PSA_NON_SECURE_ROM_START", + "conflicts": ["target.mbed_rom_start"] + }, + "non-secure-rom-size": { + "help": "Size in bytes of Non-secure ROM", + "value": null, + "macro_name": "PSA_NON_SECURE_ROM_SIZE", + "conflicts": ["target.mbed_rom_size"] + }, + "secure-ram-start": { + "help": "Starting address of Secure RAM", + "value": null, + "macro_name": "PSA_SECURE_RAM_START", + "conflicts": ["target.mbed_ram_start"] + }, + "secure-ram-size": { + "help": "Size in bytes of Secure RAM", + "value": null, + "macro_name": "PSA_SECURE_RAM_SIZE", + "conflicts": ["target.mbed_ram_size"] + }, + "non-secure-ram-start": { + "help": "Starting address of Non-secure RAM", + "value": null, + "macro_name": "PSA_NON_SECURE_RAM_START", + "conflicts": ["target.mbed_ram_start"] + }, + "non-secure-ram-size": { + "help": "Size in bytes of Non-secure RAM", + "value": null, + "macro_name": "PSA_NON_SECURE_RAM_SIZE", + "conflicts": ["target.mbed_ram_size"] + }, + "shared-ram-start": { + "help": "Starting address of Shared RAM between Secure and Non-secure worlds", + "value": null, + "macro_name": "PSA_SHARED_RAM_START" + }, + "shared-ram-size": { + "help": "Size in bytes of Shared RAM between Secure and Non-secure worlds", + "value": null, + "macro_name": "PSA_SHARED_RAM_SIZE" + } + } + }, + "NSPE_Target": { + "inherits": ["PSA_Target"], + "components": ["PSA_SRV_IPC", "NSPE"], + "public": false + }, + "SPE_Target": { + "inherits": ["PSA_Target"], + "components": ["PSA_SRV_IMPL", "PSA_SRV_IPC", "SPE"], + "public": false + }, "CM4_UARM": { "inherits": ["Target"], "core": "Cortex-M4", diff --git a/tools/build.py b/tools/build.py index 52d8810..a11cd2f 100644 --- a/tools/build.py +++ b/tools/build.py @@ -31,7 +31,7 @@ from tools.toolchains import TOOLCHAINS, TOOLCHAIN_CLASSES, TOOLCHAIN_PATHS from tools.toolchains import mbedToolchain -from tools.targets import TARGET_NAMES, TARGET_MAP +from tools.targets import TARGET_NAMES, TARGET_MAP, Target from tools.options import get_default_options_parser from tools.options import extract_profile from tools.options import extract_mcus @@ -187,7 +187,20 @@ notifier = TerminalNotifier(options.verbose, options.silent) mcu = TARGET_MAP[target] profile = extract_profile(parser, options, toolchain) - if options.source_dir: + + if Target.get_target(mcu).is_PSA_secure_target: + lib_build_res = build_library( + ROOT, options.build_dir, mcu, toolchain, + jobs=options.jobs, + clean=options.clean, + archive=(not options.no_archive), + macros=options.macros, + name=options.artifact_name, + build_profile=profile, + ignore=options.ignore, + notify=notifier, + ) + elif options.source_dir: lib_build_res = build_library( options.source_dir, options.build_dir, mcu, toolchain, jobs=options.jobs, diff --git a/tools/build_api.py b/tools/build_api.py index 28c6007..3702447 100644 --- a/tools/build_api.py +++ b/tools/build_api.py @@ -35,7 +35,7 @@ from .arm_pack_manager import Cache from .utils import (mkdir, run_cmd, run_cmd_ext, NotSupportedException, ToolException, InvalidReleaseTargetException, - intelhex_offset, integer, generate_update_filename) + intelhex_offset, integer, generate_update_filename, copy_when_different) from .paths import (MBED_CMSIS_PATH, MBED_TARGETS_PATH, MBED_LIBRARIES, MBED_HEADER, MBED_DRIVERS, MBED_PLATFORM, MBED_HAL, MBED_CONFIG_FILE, MBED_LIBRARIES_DRIVERS, @@ -457,7 +457,8 @@ notify=None, name=None, macros=None, inc_dirs=None, jobs=1, report=None, properties=None, project_id=None, project_description=None, config=None, - app_config=None, build_profile=None, stats_depth=None, ignore=None): + app_config=None, build_profile=None, stats_depth=None, + ignore=None, spe_build=False): """ Build a project. A project may be a test or a user program. Positional arguments: @@ -527,7 +528,8 @@ try: resources = Resources(notify).scan_with_toolchain( src_paths, toolchain, inc_dirs=inc_dirs) - + if spe_build: + resources.filter_spe() # Change linker script if specified if linker_script is not None: resources.add_file_ref(FileType.LD_SCRIPT, linker_script, linker_script) @@ -560,6 +562,21 @@ res, _ = toolchain.link_program(resources, build_path, name) res = (res, None) + into_dir, extra_artifacts = toolchain.config.deliver_into() + if into_dir: + copy_when_different(res[0], into_dir) + if not extra_artifacts: + if ( + CORE_ARCH[toolchain.target.core] == 8 and + not toolchain.target.core.endswith("NS") + ): + cmse_lib = join(dirname(res[0]), "cmse_lib.o") + copy_when_different(cmse_lib, into_dir) + else: + for tc, art in extra_artifacts: + if toolchain_name == tc: + copy_when_different(join(build_path, art), into_dir) + memap_instance = getattr(toolchain, 'memap_instance', None) memap_table = '' if memap_instance: diff --git a/tools/config/__init__.py b/tools/config/__init__.py index ccc0240..f7ad8eb 100644 --- a/tools/config/__init__.py +++ b/tools/config/__init__.py @@ -37,12 +37,19 @@ from ..arm_pack_manager import Cache from ..targets import (CUMULATIVE_ATTRIBUTES, TARGET_MAP, generate_py_target, get_resolution_order, Target) +from ..settings import DELIVERY_DIR try: unicode except NameError: unicode = str -PATH_OVERRIDES = set(["target.bootloader_img"]) +PATH_OVERRIDES = set([ + "target.bootloader_img" +]) +DELIVERY_OVERRIDES = set([ + "target.deliver_to_target", + "target.deliver_artifacts", +]) ROM_OVERRIDES = set([ # managed BL "target.bootloader_img", "target.restrict_size", @@ -60,7 +67,7 @@ "target.mbed_ram_start", "target.mbed_ram_size", ]) -BOOTLOADER_OVERRIDES = ROM_OVERRIDES | RAM_OVERRIDES +BOOTLOADER_OVERRIDES = ROM_OVERRIDES | RAM_OVERRIDES | DELIVERY_OVERRIDES ALLOWED_FEATURES = [ @@ -122,6 +129,7 @@ self.accepted_values = data.get("accepted_values") self.help_text = data.get("help", None) self.required = data.get("required", False) + self.conflicts = data.get("conflicts", []) self.macro_name = data.get("macro_name", "MBED_CONF_%s" % self.sanitize(self.name.upper())) self.config_errors = [] @@ -249,6 +257,8 @@ return desc + " No value set" desc += " Macro name: %s\n" % self.macro_name desc += " Value: %s (set by %s)" % (self.value, self.set_by) + if self.conflicts: + desc += " Conflicts with %s" % ", ".join(self.conflicts) return desc class ConfigMacro(object): @@ -506,10 +516,17 @@ self.app_config_data.get("custom_targets", {}), tgt) self.target = deepcopy(self.target) self.target_labels = self.target.labels + po_without_target = set(o.split(".")[1] for o in PATH_OVERRIDES) for override in BOOTLOADER_OVERRIDES: _, attr = override.split(".") if not hasattr(self.target, attr): setattr(self.target, attr, None) + elif attr in po_without_target: + new_path = join( + dirname(self.target._from_file), + getattr(self.target, attr) + ) + setattr( self.target, attr, new_path) self.cumulative_overrides = {key: ConfigCumulativeOverride(key) for key in CUMULATIVE_ATTRIBUTES} @@ -586,6 +603,17 @@ return True return False + def deliver_into(self): + if self.target.deliver_to_target: + label_dir = "TARGET_{}".format(self.target.deliver_to_target) + target_delivery_dir = join(DELIVERY_DIR, label_dir) + if not exists(target_delivery_dir): + os.makedirs(target_delivery_dir) + + return target_delivery_dir, self.target.deliver_artifacts + else: + return None, None + @property def sectors(self): """Return a list of tuples of sector start,size""" @@ -1176,6 +1204,28 @@ continue else: raise error + for param in params.values(): + for conflict in param.conflicts: + if conflict in BOOTLOADER_OVERRIDES: + _, attr = conflict.split(".") + conf = ConfigParameter( + conflict, {"value": getattr(self.target, attr)}, + "target", "target" + ) + else: + conf = params.get(conflict) + if ( + param.value and conf and conf.value + and param.value != conf.value + ): + raise ConfigException( + ("Configuration parameter {} with value {} conflicts " + "with {} with value {}").format( + param.name, param.value, conf.name, conf.value + ) + ) + + return True diff --git a/tools/make.py b/tools/make.py index c3bcacd..606ed25 100644 --- a/tools/make.py +++ b/tools/make.py @@ -50,6 +50,8 @@ from utils import argparse_many from utils import argparse_dir_not_parent from tools.toolchains import TOOLCHAIN_CLASSES, TOOLCHAIN_PATHS +from tools.settings import ROOT +from tools.targets import Target def default_args_dict(options): @@ -305,6 +307,9 @@ args_error(parser, "argument -t/--tool is required") toolchain = options.tool[0] + if Target.get_target(mcu).is_PSA_secure_target: + options.source_dir = ROOT + if (options.program is None) and (not options.source_dir): args_error(parser, "one of -p, -n, or --source is required") diff --git a/tools/resources/__init__.py b/tools/resources/__init__.py index 34399dc..d1b9489 100644 --- a/tools/resources/__init__.py +++ b/tools/resources/__init__.py @@ -527,3 +527,7 @@ config.load_resources(self) return self + def filter_spe(self): + spe_filter = lambda x: 'COMPONENT_SPE' in x + for type in [FileType.ASM_SRC, FileType.C_SRC, FileType.CPP_SRC]: + self._file_refs[type] = set([f for f in self._file_refs[type] if spe_filter(f.name) or spe_filter(f.path)]) diff --git a/tools/settings.py b/tools/settings.py index 647a445..c8ca8e5 100644 --- a/tools/settings.py +++ b/tools/settings.py @@ -27,6 +27,8 @@ ############################################################################## BUILD_DIR = abspath(join(ROOT, "BUILD")) +DELIVERY_DIR = abspath(join(ROOT, "DELIVERY")) + # ARM Compiler 5 ARM_PATH = "" diff --git a/tools/spm/__init__.py b/tools/spm/__init__.py new file mode 100644 index 0000000..2d69156 --- /dev/null +++ b/tools/spm/__init__.py @@ -0,0 +1,7 @@ +from .generate_partition_code import \ + generate_partitions_sources, generate_psa_setup + +__all__ = [ + 'generate_partitions_sources', + 'generate_psa_setup', +] diff --git a/tools/spm/generate_partition_code.py b/tools/spm/generate_partition_code.py new file mode 100644 index 0000000..f01c580 --- /dev/null +++ b/tools/spm/generate_partition_code.py @@ -0,0 +1,755 @@ +#!/usr/bin/python +import fnmatch +import itertools +import json +import os +from os.path import join as path_join + +from jinja2 import Environment, FileSystemLoader +from jsonschema import validate + +SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) +TEMPLATES_DIR = path_join(SCRIPT_DIR, 'templates') +MANIFEST_TEMPLATES = filter( + lambda filename: '_NAME_' in filename, + [os.path.join(dp, f) for dp, dn, fn in os.walk(TEMPLATES_DIR) for f in fn if f.endswith('.tpl')] +) +COMMON_TEMPLATES = filter( + lambda filename: '_NAME_' not in filename, + [os.path.join(dp, f) for dp, dn, fn in os.walk(TEMPLATES_DIR) for f in fn if f.endswith('.tpl')] +) +MANIFEST_FILE_PATTERN = '*_psa.json' +MBED_OS_ROOT = os.path.abspath(path_join(SCRIPT_DIR, os.pardir, os.pardir)) +SPM_CORE_ROOT = path_join(MBED_OS_ROOT, 'components', 'TARGET_PSA', 'spm') +SPM_TESTS_ROOT = path_join(MBED_OS_ROOT, 'TESTS', 'psa') + + +def assert_int(num): + """ + Tries to parse an integer num from a given string + + :param num: Number in int/string type + :return: Numeric value + """ + if isinstance(num, int): + return num + num_str = str(num) + radix = 16 if num_str.lower().startswith('0x') else 10 + res = int(num_str, radix) + # Python converts str to int as a signed integer + if res > 0x7FFFFFFF: + res -= 0x100000000 + return res + + +class RotService(object): + MINOR_POLICIES = ['STRICT', 'RELAXED'] + + def __init__( + self, + name, + identifier, + signal, + non_secure_clients, + minor_version=1, + minor_policy='STRICT' + ): + """ + Root of Trust Service C'tor (Aligned with json schema) + + :param name: Root of Trust Service identifier (available to user) + :param identifier: Root of Trust Service numeric enumeration. + :param signal: Root of Trust Service identifier inside the partition + :param non_secure_clients: True to allow connections from non-secure + partitions + :param minor_version: Root of Trust Service version + :param minor_policy: Enforcement level of minor version + """ + self.name = name + self.id = identifier + self.signal = signal + + assert assert_int(identifier) + + assert isinstance(non_secure_clients, bool), \ + 'non_secure_clients parameter must be of boolean type' + self.nspe_callable = non_secure_clients + + self.minor_version = assert_int(minor_version) + assert self.minor_version > 0, 'minor_version parameter is invalid' + + assert minor_policy in self.MINOR_POLICIES, \ + 'minor_policy parameter is invalid' + self.minor_policy = minor_policy + + @property + def numeric_id(self): + return assert_int(self.id) + + def __eq__(self, other): + return ( + (self.name == other.name) and + (self.id == other.id) and + (self.signal == other.signal) and + (self.nspe_callable == other.nspe_callable) and + (self.minor_version == other.minor_version) and + (self.minor_policy == other.minor_policy) + ) + + +class MmioRegion(object): + MMIO_PERMISSIONS = { + 'READ-ONLY': 'PSA_MMIO_PERM_READ_ONLY', + 'READ-WRITE': 'PSA_MMIO_PERM_READ_WRITE' + } + + def __init__(self, **kwargs): + """ + MMIO Region C'tor (Aligned with json schema) + + Supports both named and numeric regions + In case of named region the acceptable params are name and permission + In case of numeric region the acceptable params are name, size and + permission + + :param name: C definition name of the region (size will be + auto-generated) + :param base: C hex string defining a memory address (must be 32bit) + :param size: size of a region (Applicable only for numbered regions) + :param permission: Access permissions to the described region (R/RW) + + """ + assert 'permission' in kwargs + self.permission = self.MMIO_PERMISSIONS[kwargs['permission']] + if 'name' in kwargs: + self.base = kwargs['name'] + self.size = '(sizeof(*({})))'.format(kwargs['name']) + if 'base' in kwargs: + self.base = kwargs['base'] + self.size = assert_int(kwargs['size']) + + assert 'partition_id' in kwargs + self.partition_id = assert_int(kwargs['partition_id']) + + assert hasattr(self, 'base') + assert hasattr(self, 'size') + assert hasattr(self, 'permission') + assert hasattr(self, 'partition_id') + + def __eq__(self, other): + return ( + (self.base == other.base) and + (self.size == other.size) and + (self.permission == other.permission) + ) + + +class Irq(object): + def __init__(self, line_num, signal): + """ + IRQ line C'tor (Aligned with json schema) + + :param line_num: number of interrupt used by the partition + :param signal: IRQ line identifier inside the partition + """ + self.line_num = assert_int(line_num) + assert isinstance(signal, basestring) + self.signal = signal + + def __eq__(self, other): + return (self.line_num == other.line_num) and \ + (self.signal == other.signal) + + +class Manifest(object): + PRIORITY = { + 'LOW': 'osPriorityLow', + 'NORMAL': 'osPriorityNormal', + 'HIGH': 'osPriorityHigh' + } + PARTITION_TYPES = ['APPLICATION-ROT', 'PSA-ROT'] + # The following signal bits cannot be used: + # bit[0-2] | Reserved + # bit[3] | PSA Doorbell + # bit[31] | RTX error bit + RESERVED_SIGNALS = 5 + + def __init__( + self, + manifest_file, + name, + partition_id, + partition_type, + priority, + entry_point, + heap_size, + stack_size, + source_files, + mmio_regions=None, + rot_services=None, + extern_sids=None, + irqs=None + ): + """ + Manifest C'tor (Aligned with json schema) + + :param manifest_file: Path to json manifest + :param name: Partition unique name + :param partition_id: Partition identifier + :param partition_type: Whether the partition is unprivileged or part + of the trusted computing base + :param priority: Priority of the partition's thread + :param entry_point: C symbol name of the partition's main function + :param heap_size: Size of heap required for the partition + :param stack_size: Size of stack required for the partition + :param source_files: List of files assembling the partition + (relative paths) + :param mmio_regions: List of MMIO regions used by the partition + :param rot_services: List of Root of Trust Services declared by the + partition + :param extern_sids: List of Root of Trust Services the partition can call + :param irqs: List of interrupts the partition can handle + """ + assert manifest_file is not None + assert name is not None + assert partition_id is not None + assert partition_type is not None + assert entry_point is not None + assert priority is not None + assert heap_size is not None + assert stack_size is not None + assert source_files is not None + + mmio_regions = [] if mmio_regions is None else mmio_regions + rot_services = [] if rot_services is None else rot_services + extern_sids = [] if extern_sids is None else extern_sids + irqs = [] if irqs is None else irqs + + assert os.path.isfile(manifest_file) + assert isinstance(partition_id, (int, long)) + assert isinstance(heap_size, int) + assert isinstance(stack_size, int) + assert isinstance(entry_point, basestring) + assert partition_type in self.PARTITION_TYPES + assert partition_id > 0 + + self.file = manifest_file + self.name = name + self.id = partition_id + self.type = partition_type + self.priority = self.PRIORITY[priority] + self.heap_size = heap_size + self.stack_size = stack_size + self.entry_point = entry_point + if isinstance(source_files, list): + self.source_files = source_files + else: + self.source_files = [source_files] + + self.mmio_regions = mmio_regions + self.rot_services = rot_services + self.extern_sids = extern_sids + self.irqs = irqs + + for src_file in self.source_files: + assert os.path.isfile(src_file), \ + "The source file {} mentioned in {} doesn't exist.".format( + src_file, self.file + ) + + for rot_srv in self.rot_services: + assert isinstance(rot_srv, RotService) + + for extern_sid in self.extern_sids: + assert isinstance(extern_sid, basestring) + + assert len(self.extern_sids) == len(set(self.extern_sids)), \ + 'Detected duplicates external SIDs in {}'.format(self.file) + + for irq in self.irqs: + assert isinstance(irq, Irq) + + total_signals = len(self.rot_services) + len(self.irqs) + assert total_signals <= 32 - self.RESERVED_SIGNALS, \ + 'Manifest {} - {} exceeds limit of RoT services and IRQs allowed ' \ + '({}).'.format( + self.name, self.file, 32 - self.RESERVED_SIGNALS + ) + + def __eq__(self, other): + return ( + (self.file == other.file) and + (self.name == other.name) and + (self.id == other.id) and + (self.type == other.type) and + (self.priority == other.priority) and + (self.heap_size == other.heap_size) and + (self.stack_size == other.stack_size) and + (self.entry_point == other.entry_point) and + (self.source_files == other.source_files) and + (self.mmio_regions == other.mmio_regions) and + (self.rot_services == other.rot_services) and + (self.extern_sids == other.extern_sids) and + (self.irqs == other.irqs) + ) + + @classmethod + def from_json(cls, manifest_file, skip_src=False): + """ + Load a partition manifest file + + :param manifest_file: Manifest file path + :param skip_src: Ignore the `source_files` entry + :return: Manifest object + """ + + partition_schema_path = path_join( + SCRIPT_DIR, + 'partition_description_schema.json' + ) + with open(partition_schema_path) as schema_fh: + partition_schema = json.load(schema_fh) + + # Load partition manifest file. + with open(manifest_file) as fh: + manifest = json.load(fh) + + validate(manifest, partition_schema) + manifest_dir = os.path.dirname(manifest_file) + + source_files = [] + if not skip_src: + for src_file in manifest['source_files']: + source_files.append( + os.path.normpath(path_join(manifest_dir, src_file))) + + mmio_regions = [] + for mmio_region in manifest.get('mmio_regions', []): + mmio_regions.append(MmioRegion(partition_id=manifest['id'], **mmio_region)) + + rot_services = [] + for rot_srv in manifest.get('services', []): + rot_services.append(RotService(**rot_srv)) + + irqs = [] + for irq in manifest.get('irqs', []): + irqs.append(Irq(**irq)) + + return Manifest( + manifest_file=manifest_file, + name=manifest['name'], + partition_id=assert_int(manifest['id']), + partition_type=manifest['type'], + priority=manifest['priority'], + heap_size=assert_int(manifest['heap_size']), + stack_size=assert_int(manifest['stack_size']), + entry_point=manifest['entry_point'], + source_files=source_files, + mmio_regions=mmio_regions, + rot_services=rot_services, + extern_sids=manifest.get('extern_sids', []), + irqs=irqs + ) + + @property + def sids(self): + return [rot_srv.name for rot_srv in self.rot_services] + + @property + def autogen_folder(self): + return os.path.abspath(os.path.dirname(self.file)) + + def find_dependencies(self, manifests): + """ + Find other manifests which holds Root of Trust Services that + are declared as extern in this manifest + + :param manifests: list of manifests to filter + :return: list of manifest's names that holds current + extern Root of Trust Services + """ + + manifests = filter(lambda man: man != self, manifests) + extern_sids_set = set(self.extern_sids) + return [manifest.name for manifest in manifests + if extern_sids_set.intersection(set(manifest.sids))] + + def templates_to_files(self, templates, templates_base, output_dir): + """ + Translates a list of partition templates to file names + + :param templates: List of partition templates + :param output_dir: Output directory (Default is autogen folder property) + :return: Dictionary of template to output file translation + """ + + generated_files = {} + for t in templates: + fname = os.path.relpath(t, templates_base) + _tpl = fname.replace('NAME', self.name.lower()) + full_path = path_join( + output_dir, + os.path.splitext(_tpl)[0] + ) + generated_files[t] = full_path + + return generated_files + + +def check_circular_call_dependencies(manifests): + """ + Check if there is a circular dependency between the partitions described by the manifests. + A circular dependency might happen if there is a scenario in which a partition calls a Root of Trust Service in + another partition which than calls another Root of Trust Service which resides in the originating partition. + For example: Partition A has a Root of Trust Service A1 and extern sid B1, + partition B has a Root of Trust Service B1 and extern sid A1. + + :param manifests: List of the partition manifests. + :return: True if a circular dependency exists, false otherwise. + """ + + # Construct a call graph. + call_graph = {} + for manifest in manifests: + call_graph[manifest.name] = { + 'calls': manifest.find_dependencies(manifests), + 'called_by': set() + } + for manifest_name in call_graph: + for called in call_graph[manifest_name]['calls']: + call_graph[called]['called_by'].add(manifest_name) + + # Run topological sort on the call graph. + while len(call_graph) > 0: + # Find all the nodes that aren't called by anyone and + # therefore can be removed. + nodes_to_remove = filter(lambda x: len(call_graph[x]['called_by']) == 0, + call_graph.keys()) + + # If no node can be removed we have a circle. + if not nodes_to_remove: + return True + + # Remove the nodes. + for node in nodes_to_remove: + for called in call_graph[node]['calls']: + call_graph[called]['called_by'].remove(node) + call_graph.pop(node) + + return False + + +def validate_partition_manifests(manifests): + """ + Check the correctness of the manifests list + (no conflicts, no missing elements, etc.) + + :param manifests: List of the partition manifests + """ + for manifest in manifests: + assert isinstance(manifest, Manifest) + + partitions_names = {} + partitions_ids = {} + rot_service_ids = {} + rot_service_names = {} + rot_service_signals = {} + irq_signals = {} + irq_numbers = {} + all_extern_sids = set() + spe_contained_manifests = [] + + for manifest in manifests: + # Make sure the partition names are unique. + if manifest.name in partitions_names: + raise ValueError( + 'Partition name {} is not unique, ' + 'found in both {} and {}.'.format( + manifest.name, + partitions_names[manifest.name], + manifest.file + ) + ) + partitions_names[manifest.name] = manifest.file + + # Make sure the partition ID's are unique. + if manifest.id in partitions_ids: + raise ValueError( + 'Partition id {} is not unique, ' + 'found in both {} and {}.'.format( + manifest.id, + partitions_ids[manifest.id], + manifest.file + ) + ) + partitions_ids[manifest.id] = manifest.file + + is_nspe_callabale = False + + # Make sure all the Root of Trust Service IDs and signals are unique. + for rot_service in manifest.rot_services: + if rot_service.name in rot_service_names: + raise ValueError( + 'Root of Trust Service name {} is found ' + 'in both {} and {}.'.format( + rot_service.name, + rot_service_names[rot_service.name], + manifest.file + ) + ) + rot_service_names[rot_service.name] = manifest.file + + if rot_service.signal in rot_service_signals: + raise ValueError( + 'Root of Trust Service signal {} is found ' + 'in both {} and {}.'.format( + rot_service.signal, + rot_service_signals[rot_service.signal], + manifest.file + ) + ) + rot_service_signals[rot_service.signal] = manifest.file + + if rot_service.numeric_id in rot_service_ids: + raise ValueError( + 'Root of Trust Service identifier {} is found ' + 'in both {} and {}.'.format( + rot_service.numeric_id, + rot_service_ids[rot_service.numeric_id], + manifest.file + ) + ) + rot_service_ids[rot_service.numeric_id] = manifest.file + is_nspe_callabale |= rot_service.nspe_callable + + if not is_nspe_callabale: + spe_contained_manifests.append(manifest) + + # Make sure all the IRQ signals and line-numbers are unique. + for irq in manifest.irqs: + if irq.signal in irq_signals: + raise ValueError( + 'IRQ signal {} is found in both {} and {}.'.format( + irq.signal, + irq_signals[irq.signal], + manifest.file + ) + ) + irq_signals[irq.signal] = manifest.file + + if irq.line_num in irq_numbers: + raise ValueError( + 'IRQ line number {} is found in both {} and {}.'.format( + irq.line_num, + irq_numbers[irq.line_num], + manifest.file + ) + ) + irq_numbers[irq.line_num] = manifest.file + + all_extern_sids.update(manifest.extern_sids) + + # Check that all the external SIDs can be found. + declared_sids = set(rot_service_names.keys()) + for manifest in manifests: + extern_sids = set(manifest.extern_sids) + if not extern_sids.issubset(declared_sids): + missing_sids = extern_sids.difference(declared_sids) + raise ValueError( + "External SID(s) {} required by {} can't be found in " + "any partition manifest.".format( + ', '.join(missing_sids), manifest.file) + ) + + if check_circular_call_dependencies(manifests): + raise ValueError( + "Detected a circular call dependency between the partitions.") + + for manifest in spe_contained_manifests: + rot_services = set([service.name for service in manifest.rot_services]) + if not rot_services.intersection(all_extern_sids) and len( + manifest.irqs) == 0: + raise ValueError( + 'Partition {} (defined by {}) is not accessible from NSPE ' + 'and not referenced by any other partition.'.format( + manifest.name, + manifest.file + ) + ) + + +def generate_source_files( + templates, + render_args, + output_folder, + extra_filters=None +): + """ + Generate SPM common C code from manifests using given templates + + :param templates: Dictionary of template and their auto-generated products + :param render_args: Dictionary of arguments that should be passed to render + :param output_folder: Output directory for file generation + :param extra_filters: Dictionary of extra filters to use in the rendering + process + :return: Path to generated folder containing common generated files + """ + + rendered_files = [] + templates_dirs = list( + set([os.path.dirname(path) for path in templates]) + ) + template_files = {os.path.basename(t): t for t in templates} + + # Load templates for the code generation. + env = Environment( + loader=FileSystemLoader(templates_dirs), + lstrip_blocks=True, + trim_blocks=True + ) + if extra_filters: + env.filters.update(extra_filters) + + for tf in template_files: + template = env.get_template(tf) + rendered_files.append( + (templates[template_files[tf]], template.render(**render_args))) + rendered_file_dir = os.path.dirname(templates[template_files[tf]]) + if not os.path.exists(rendered_file_dir): + os.makedirs(rendered_file_dir) + + if not os.path.exists(output_folder): + os.makedirs(output_folder) + + for fname, data in rendered_files: + with open(fname, 'wt') as fh: + fh.write(data) + + return output_folder + + +def generate_partitions_sources(manifest_files, extra_filters=None): + """ + Process all the given manifest files and generate C code from them + + :param manifest_files: List of manifest files + :param extra_filters: Dictionary of extra filters to use in the rendering + process + :return: List of paths to the generated files + """ + + # Construct a list of all the manifests and sids. + manifests = [] + for manifest_file in manifest_files: + manifest = Manifest.from_json(manifest_file) + manifests.append(manifest) + + generated_folders = set() + for manifest in manifests: + manifest_output_folder = manifest.autogen_folder + + render_args = { + 'partition': manifest, + 'dependent_partitions': manifest.find_dependencies(manifests) + } + manifest_output_folder = generate_source_files( + manifest.templates_to_files(MANIFEST_TEMPLATES, + TEMPLATES_DIR, + manifest_output_folder), + render_args, + manifest_output_folder, + extra_filters=extra_filters + ) + generated_folders.add(manifest_output_folder) + + return list(generated_folders) + + +def generate_psa_setup(manifest_files, output_dir, weak_setup, extra_filters=None): + """ +Process all the given manifest files and generate C setup code from them + :param manifest_files: List of manifest files + :param output_dir: Output directory for the generated files + :param weak_setup: Is the functions/data in the setup file weak (can be overridden by another setup file) + :param extra_filters: Dictionary of extra filters to use in the rendering + process + :return: path to the setup generated files + """ + autogen_folder = output_dir + templates_dict = { + t: path_join(autogen_folder, os.path.relpath(os.path.splitext(t)[0], TEMPLATES_DIR)) + for t in COMMON_TEMPLATES + } + + complete_source_list = templates_dict.values() + + # Construct lists of all the manifests and mmio_regions. + region_list = [] + manifests = [] + for manifest_file in manifest_files: + manifest_obj = Manifest.from_json(manifest_file) + manifests.append(manifest_obj) + for region in manifest_obj.mmio_regions: + region_list.append(region) + complete_source_list.extend( + manifest_obj.templates_to_files( + MANIFEST_TEMPLATES, + TEMPLATES_DIR, + manifest_obj.autogen_folder).values() + ) + + # Validate the correctness of the manifest collection. + validate_partition_manifests(manifests) + + render_args = { + 'partitions': manifests, + 'regions': region_list, + 'region_pair_list': list(itertools.combinations(region_list, 2)), + 'weak': weak_setup + } + + return generate_source_files( + templates_dict, + render_args, + autogen_folder, + extra_filters=extra_filters + ) + + +def manifests_discovery(root_dir): + manifest_files = set() + + for root, dirs, files in os.walk(root_dir): + to_add = [path_join(root, f) for f in fnmatch.filter(files, MANIFEST_FILE_PATTERN) if 'TARGET_IGNORE' not in root] + manifest_files.update(to_add) + + return list(manifest_files) + + +def generate_psa_code(): + # Find all manifest files in the mbed-os tree + manifest_files = manifests_discovery(MBED_OS_ROOT) + + # Generate partition code for each manifest file + generate_partitions_sources(manifest_files) + + test_manifest_files = sorted([path for path in manifest_files if 'TESTS' in path]) + system_manifest_files = list(set(manifest_files) - set(test_manifest_files)) + + # Generate default system psa setup file (only system partitions) + generate_psa_setup(system_manifest_files, SPM_CORE_ROOT, weak_setup=True) + + tests_dir_content = [path_join(SPM_TESTS_ROOT, f) for f in os.listdir(SPM_TESTS_ROOT)] + spm_tests = [path for path in tests_dir_content if os.path.isdir(path)] + + # Build a dictionary for test partition in the form of { test_root: manifest_list } + # For each test generate specific psa setup file (system + test partitions) + tests_dict = {test_root: [] for test_root in spm_tests} + for test_root in spm_tests: + tests_dict[test_root] = filter(lambda manifest_path: test_root in manifest_path, test_manifest_files) + tests_dict[test_root] += system_manifest_files + generate_psa_setup(tests_dict[test_root], test_root, weak_setup=False) + + +if __name__ == '__main__': + generate_psa_code() diff --git a/tools/spm/partition_description_schema.json b/tools/spm/partition_description_schema.json new file mode 100644 index 0000000..1089c8d --- /dev/null +++ b/tools/spm/partition_description_schema.json @@ -0,0 +1,196 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "schema for a partition description.", + "type": "object", + "required": ["name", "type", "priority", "id", "entry_point", "stack_size", "heap_size", "source_files"], + "anyOf": [ + {"required" : ["services"]}, + {"required" : ["irqs"]} + ], + "properties": { + "name": { + "description": "Alphanumeric C macro for referring to a partition. (all capital)", + "$ref": "#/definitions/c_macro" + }, + "type": { + "description": "Whether the partition is unprivileged or part of the trusted computing base.", + "enum": ["APPLICATION-ROT", "PSA-ROT"] + }, + "priority": { + "description": "Partition task priority.", + "enum": ["LOW", "NORMAL", "HIGH"] + }, + "id": { + "description": "Partition numeric unique positive identifier. (must be a positive 8 bytes hex string)", + "type": "string", + "pattern": "^0x[0-7][0-9a-fA-F]{7}$" + }, + "entry_point": { + "description": "C symbol name of the partition's entry point. (unmangled, use extern C if needed)", + "$ref": "#/definitions/c_symbol" + }, + "stack_size": { + "description": "Partition's task stack size in bytes.", + "$ref": "#/definitions/positive_integer_or_hex_string" + }, + "heap_size": { + "description": "Partition's task heap size in bytes.", + "$ref": "#/definitions/positive_integer_or_hex_string" + }, + "mmio_regions": { + "description": "List of Memory-Mapped IO region objects which the partition has access to.", + "type": "array", + "items": { + "anyOf": [{ + "$ref": "#/definitions/named_region" + }, + { + "$ref": "#/definitions/numbered_region" + } + ] + }, + "uniqueItems": true + }, + "services": { + "description": "List of RoT Service objects which the partition implements.", + "type": "array", + "items": { + "$ref": "#/definitions/service" + }, + "uniqueItems": true + }, + "extern_sids": { + "description": "List of SID which the partition code depends on and allowed to access.", + "type": "array", + "items": { + "$ref": "#/definitions/c_macro" + }, + "uniqueItems": true + }, + "source_files": { + "description": "List of source files relative to PSA Manifest file. A Secure Partition is built from explicit file list.", + "type": "array", + "items": { + "type": "string", + "pattern": "^[a-zA-Z0-9-_./]+$" + }, + "minItems": 1, + "uniqueItems": true + }, + "irqs": { + "description": "List of IRQ objects which the partition implements.", + "type": "array", + "items": { + "$ref": "#/definitions/irq" + }, + "uniqueItems": true + } + }, + "definitions": { + "c_macro": { + "type": "string", + "pattern": "^[A-Z_][A-Z0-9_]*$" + }, + "c_symbol": { + "type": "string", + "pattern": "^[a-zA-Z_][a-zA-Z0-9_]*$" + }, + "hex_string": { + "type": "string", + "pattern": "^0x(0*[1-9a-fA-F][0-9a-fA-F]*)$", + "minLength": 3, + "maxLength": 10 + }, + "positive_integer": { + "type": "integer", + "exclusiveMinimum": true, + "minimum": 0 + }, + "positive_integer_or_hex_string": { + "oneOf": [{ + "$ref": "#/definitions/positive_integer" + }, + { + "$ref": "#/definitions/hex_string" + } + ] + }, + "named_region": { + "description": "MMIO region which is described by it's C macro name and access permissions.", + "required": ["name", "permission"], + "properties": { + "name": { + "description": "Alphanumeric C macro for referring to the region.", + "$ref": "#/definitions/c_macro" + }, + "permission": { + "description": "Access permissions for the region.", + "enum": ["READ-ONLY", "READ-WRITE"] + } + } + }, + "numbered_region": { + "description": "MMIO region which is described by it's base address, size and access permissions.", + "required": ["base", "size", "permission"], + "properties": { + "base": { + "description": "The base address of the region.", + "$ref": "#/definitions/hex_string" + }, + "size": { + "description": "Size in bytes of the region.", + "$ref": "#/definitions/positive_integer_or_hex_string" + }, + "permission": { + "description": "Access permissions for the region.", + "enum": ["READ-ONLY", "READ-WRITE"] + } + } + }, + "service": { + "required": ["name", "identifier", "non_secure_clients", "signal"], + "properties": { + "name": { + "description": "Alphanumeric C macro for referring to a RoT Service from source code (all capital)", + "$ref": "#/definitions/c_macro" + }, + "identifier": { + "description": "The integer value of the NAME field", + "$ref": "#/definitions/positive_integer_or_hex_string" + }, + "non_secure_clients": { + "description": "Denote whether the RoT Service is exposed to non-secure clients.", + "type": "boolean" + }, + "signal": { + "description": "Alphanumeric C macro for referring to the RoT Service's signal value. (all capital)", + "$ref": "#/definitions/c_macro" + }, + "minor_version": { + "description": "Optional: Minor version number of the RoT Service's interface.", + "$ref": "#/definitions/positive_integer", + "default": 1 + }, + "minor_policy": { + "description": "Optional: Minor version policy to apply on connections to the RoT Service.", + "enum": ["STRICT", "RELAXED"], + "default": "STRICT" + } + } + }, + "irq": { + "required": ["line_num", "signal"], + "properties": { + "line_num": { + "description": "Interrupt line number for registering to ISR table entry and enable/disable the specific IRQ once received.", + "type": "integer", + "minimum": 0 + }, + "signal": { + "description": "Alphanumeric C macro for referring to the IRQ's signal value. (all capital)", + "$ref": "#/definitions/c_macro" + } + } + } + } +} diff --git a/tools/spm/templates/COMPONENT_SPE/psa_NAME_partition.c.tpl b/tools/spm/templates/COMPONENT_SPE/psa_NAME_partition.c.tpl new file mode 100644 index 0000000..0a19c1a --- /dev/null +++ b/tools/spm/templates/COMPONENT_SPE/psa_NAME_partition.c.tpl @@ -0,0 +1,143 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#include "cmsis.h" +#include "mbed_toolchain.h" /* For using MBED_ALIGN macro */ +#include "rtx_os.h" +#include "spm_panic.h" +#include "spm_internal.h" +#include "psa_{{partition.name|lower}}_partition.h" +#include "psa_{{partition.name|lower}}_ifs.h" +{% for partition in dependent_partitions %} +#include "psa_{{partition|lower}}_ifs.h" +{% endfor %} + + +/* Threads stacks */ +MBED_ALIGN(8) uint8_t {{partition.name|lower}}_thread_stack[{{partition.stack_size}}] = {0}; + +/* Threads control blocks */ +osRtxThread_t {{partition.name|lower}}_thread_cb = {0}; + +/* Thread attributes - for thread initialization */ +osThreadAttr_t {{partition.name|lower}}_thread_attr = { + .name = "{{partition.name|lower}}", + .attr_bits = 0, + .cb_mem = &{{partition.name|lower}}_thread_cb, + .cb_size = sizeof({{partition.name|lower}}_thread_cb), + .stack_mem = {{partition.name|lower}}_thread_stack, + .stack_size = {{partition.stack_size}}, + .priority = {{partition.priority}}, + .tz_module = 0, + .reserved = 0 + }; + +{% if partition.rot_services|count > 0 %} +spm_rot_service_t {{partition.name|lower}}_rot_services[{{partition.name|upper}}_ROT_SRV_COUNT] = { +{% for rot_srv in partition.rot_services %} + { + .sid = {{rot_srv.name|upper}}, + .mask = {{rot_srv.signal|upper}}, + .partition = NULL, + .min_version = {{rot_srv.minor_version}}, + .min_version_policy = PSA_MINOR_VERSION_POLICY_{{rot_srv.minor_policy|upper}}, +{% if rot_srv.nspe_callable %} + .allow_nspe = true, +{% else %} + .allow_nspe = false, +{% endif %} + .queue = { + .head = NULL, + .tail = NULL + } + }, +{% endfor %} +}; +{% endif %} + +{% if partition.extern_sids|count > 0 %} +/* External SIDs used by {{partition.name}} */ +const uint32_t {{partition.name|lower}}_external_sids[{{partition.extern_sids|count}}] = +{ +{% for sid in partition.extern_sids %} + {{sid|upper}}, +{% endfor %} +}; +{% endif %} +{% for rot_srv in partition.rot_services %} +{% endfor %} + +static osRtxMutex_t {{partition.name|lower}}_mutex = {0}; +static const osMutexAttr_t {{partition.name|lower}}_mutex_attr = { + .name = "{{partition.name|lower}}_mutex", + .attr_bits = osMutexRecursive | osMutexPrioInherit | osMutexRobust, + .cb_mem = &{{partition.name|lower}}_mutex, + .cb_size = sizeof({{partition.name|lower}}_mutex), +}; + +{% if partition.irqs|count > 0 %} +// Mapper function from irq signal to interupts number +IRQn_Type spm_{{partition.name|lower}}_signal_to_irq_mapper(uint32_t signal) +{ + SPM_ASSERT({{partition.name|upper}}_WAIT_ANY_IRQ_MSK & signal); + switch(signal){ + {% for irq in partition.irqs %} + case {{ irq.signal }}: + return (IRQn_Type){{irq.line_num}}; + break; + {% endfor %} + default: + break; + } + + SPM_PANIC("Unknown signal number %d", signal); + return 0; +} +{% endif %} + +extern void {{partition.entry_point}}(void *ptr); + +void {{partition.name|lower}}_init(spm_partition_t *partition) +{ + if (NULL == partition) { + SPM_PANIC("partition is NULL!\n"); + } + + partition->mutex = osMutexNew(&{{partition.name|lower}}_mutex_attr); + if (NULL == partition->mutex) { + SPM_PANIC("Failed to create mutex for secure partition {{partition.name|lower}}!\n"); + } + + {% if partition.rot_services|count > 0 %} + for (uint32_t i = 0; i < {{partition.name|upper}}_ROT_SRV_COUNT; ++i) { + {{partition.name|lower}}_rot_services[i].partition = partition; + } + partition->rot_services = {{partition.name|lower}}_rot_services; + {% else %} + partition->rot_services = NULL; + {% endif %} + + partition->thread_id = osThreadNew({{partition.entry_point}}, NULL, &{{partition.name|lower}}_thread_attr); + if (NULL == partition->thread_id) { + SPM_PANIC("Failed to create start main thread of partition {{partition.name|lower}}!\n"); + } +} +{# End of file #} diff --git a/tools/spm/templates/COMPONENT_SPE/psa_NAME_partition.h.tpl b/tools/spm/templates/COMPONENT_SPE/psa_NAME_partition.h.tpl new file mode 100644 index 0000000..9ece31a --- /dev/null +++ b/tools/spm/templates/COMPONENT_SPE/psa_NAME_partition.h.tpl @@ -0,0 +1,79 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#ifndef PSA_{{partition.name|upper}}_PARTITION_H +#define PSA_{{partition.name|upper}}_PARTITION_H + +#define {{partition.name|upper}}_ID {{partition.id}} + +{% if partition.rot_services|count > 0 %} +#define {{partition.name|upper}}_ROT_SRV_COUNT ({{partition.rot_services|count}}UL) +{% endif %} +#define {{partition.name|upper}}_EXT_ROT_SRV_COUNT ({{partition.extern_sids|count}}UL) + +/* {{partition.name}} event flags */ +#define {{partition.name|upper}}_RESERVED1_POS (1UL) +#define {{partition.name|upper}}_RESERVED1_MSK (1UL << {{partition.name|upper}}_RESERVED1_POS) + +#define {{partition.name|upper}}_RESERVED2_POS (2UL) +#define {{partition.name|upper}}_RESERVED2_MSK (1UL << {{partition.name|upper}}_RESERVED2_POS) + +{% for irq in partition.irqs %} +#define {{irq.signal|upper}}_POS ({{loop.index + 3 }}UL) +#define {{irq.signal|upper}} (1UL << {{irq.signal|upper}}_POS) +{% endfor %} + +{% if partition.irqs|count > 0 %} +#define {{partition.name|upper}}_WAIT_ANY_IRQ_MSK (\ +{% for irq in partition.irqs %} + {{irq.signal|upper}}{{")" if loop.last else " | \\"}} +{% endfor %} +{% endif %} + +{% for rot_srv in partition.rot_services %} +#define {{rot_srv.signal|upper}}_POS ({{loop.index + 3 + partition.irqs|count}}UL) +#define {{rot_srv.signal|upper}} (1UL << {{rot_srv.signal|upper}}_POS) +{% endfor %} + +{% if partition.rot_services|count > 0 %} +#define {{partition.name|upper}}_WAIT_ANY_SID_MSK (\ +{% for rot_srv in partition.rot_services %} + {{rot_srv.signal|upper}}{{")" if loop.last else " | \\"}} +{% endfor %} +{% endif %} + +/* +#define {{partition.name|upper}}_WAIT_ANY_MSK (\ +{% if partition.irqs|count > 0 %} + {{partition.name|upper}}_WAIT_ANY_IRQ_MSK | \ +{% endif %} +{% if partition.rot_services|count > 0 %} + {{partition.name|upper}}_WAIT_ANY_SID_MSK) | \ +{% endif %} + PSA_DOORBELL) +*/ + +{% if partition.irqs|count > 0 %} +uint32_t spm_{{partition.name|lower}}_signal_to_irq_mapper(uint32_t signal); +{% endif %} + +#endif // PSA_{{partition.name|upper}}_PARTITION_H +{# End of file #} diff --git a/tools/spm/templates/COMPONENT_SPE/psa_setup.c.tpl b/tools/spm/templates/COMPONENT_SPE/psa_setup.c.tpl new file mode 100644 index 0000000..a22be5d --- /dev/null +++ b/tools/spm/templates/COMPONENT_SPE/psa_setup.c.tpl @@ -0,0 +1,149 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#include "spm_panic.h" +#include "spm_internal.h" +#include "handles_manager.h" +#include "cmsis.h" +{% for partition in partitions %} +#include "psa_{{partition.name|lower}}_partition.h" +{% endfor %} {# partition in partitions #} + +{% for partition in partitions %} +{% if partition.extern_sids|count > 0 %} +extern const uint32_t {{partition.name|lower}}_external_sids[{{partition.extern_sids|count}}]; +{% endif %} +{% endfor %} {# partition in partitions #} + +{% if partitions|count > 0 %} +{% if weak %} +__attribute__((weak)) +{% endif %} +spm_partition_t g_partitions[{{partitions|count}}] = { +{% for partition in partitions %} + { + .partition_id = {{partition.name|upper}}_ID, + .thread_id = 0, + {% if partition.rot_services|count > 0 %} + .flags_rot_srv = {{partition.name|upper}}_WAIT_ANY_SID_MSK, + {% else %} + .flags_rot_srv = 0, + {% endif %} + {% if partition.irqs|count > 0 %} + .flags_interrupts = {{partition.name|upper}}_WAIT_ANY_IRQ_MSK, + {% else %} + .flags_interrupts = 0, + {% endif %} + .rot_services = NULL, + {% if partition.rot_services|count > 0 %} + .rot_services_count = {{partition.name|upper}}_ROT_SRV_COUNT, + {% else %} + .rot_services_count = 0, + {% endif %} + {% if partition.extern_sids|count > 0 %} + .extern_sids = {{partition.name|lower}}_external_sids, + {% else %} + .extern_sids = NULL, + {% endif %} + .extern_sids_count = {{partition.name|upper}}_EXT_ROT_SRV_COUNT, + {% if partition.irqs|count > 0 %} + .irq_mapper = spm_{{partition.name|lower}}_signal_to_irq_mapper, + {% else %} + .irq_mapper = NULL, + {% endif %} + }, +{% endfor %} +}; +{% else %} +{% if weak %} +__attribute__((weak)) +{% endif %} +spm_partition_t *g_partitions = NULL; +{% endif %} + +/* Check all the defined memory regions for overlapping. */ +{% for region_pair in region_pair_list %} +MBED_STATIC_ASSERT( + ((uintptr_t)({{region_pair[0].base}}) + {{region_pair[0].size}} - 1 < (uintptr_t)({{region_pair[1].base}})) || + ((uintptr_t)({{region_pair[1].base}}) + {{region_pair[1].size}} - 1 < (uintptr_t)({{region_pair[0].base}})), + "The region with base {{region_pair[0].base}} and size {{region_pair[0].size}} overlaps with the region with base {{region_pair[1].base}} and size {{region_pair[1].size}}!"); +{% endfor %} + +/* A list of all the memory regions. */ +{% if regions|count > 0 %} +{% if weak %} +__attribute__((weak)) +{% endif %} +const mem_region_t mem_regions[] = { +{% for region in regions %} + { (uint32_t)({{region.base}}), {{region.size}}, {{region.permission}}, {{region.partition_id}} }, +{% endfor %} +}; +{% else %} +{% if weak %} +__attribute__((weak)) +{% endif %} +const mem_region_t *mem_regions = NULL; +{% endif %} + +{% if weak %} +__attribute__((weak)) +{% endif %} +const uint32_t mem_region_count = {{regions|count}}; + +// forward declaration of partition initializers +{% for partition in partitions %} +void {{partition.name|lower}}_init(spm_partition_t *partition); +{% endfor %} {# partition in partitions #} + +{% if weak %} +__attribute__((weak)) +{% endif %} +uint32_t init_partitions(spm_partition_t **partitions) +{ + if (NULL == partitions) { + SPM_PANIC("partitions is NULL!\n"); + } + +{% for partition in partitions %} + {{partition.name|lower}}_init(&(g_partitions[{{loop.index0}}])); +{% endfor %} {# partition in partitions #} + + *partitions = g_partitions; + return {{partitions|count}}; +} + +{% for partition in partitions %} + {% set partition_loop = loop %} + {% for irq in partition.irqs %} +// ISR handler for interrupt {irq.line_num} +void spm_irq_{{irq.signal}}_{{partition.name|lower}}(void) +{ + NVIC_DisableIRQ((IRQn_Type){{irq.line_num}}); + osThreadFlagsSet( + g_partitions[{{ partition_loop.index0 }}].thread_id, + {{irq.signal|upper}} + ); +} + +{% endfor %} +{% endfor %} +{# End of file #} diff --git a/tools/spm/templates/psa_NAME_ifs.h.tpl b/tools/spm/templates/psa_NAME_ifs.h.tpl new file mode 100644 index 0000000..149dc8f --- /dev/null +++ b/tools/spm/templates/psa_NAME_ifs.h.tpl @@ -0,0 +1,30 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********************************************************************************************************************** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT. + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + **********************************************************************************************************************/ + +#ifndef PSA_{{partition.name|upper}}_PARTITION_ROT_SERVICES_H +#define PSA_{{partition.name|upper}}_PARTITION_ROT_SERVICES_H + +{% for rot_srv in partition.rot_services %} +#define {{rot_srv.name|upper}} {{rot_srv.id}} +{% endfor %} + +#endif // PSA_{{partition.name|upper}}_PARTITION_ROT_SERVICES_H +{# End of file #} diff --git a/tools/targets/__init__.py b/tools/targets/__init__.py index 1441fae..42b6494 100644 --- a/tools/targets/__init__.py +++ b/tools/targets/__init__.py @@ -166,8 +166,12 @@ @cached def get_json_target_data(): """Load the description of JSON target data""" - targets = json_file_to_dict(Target.__targets_json_location or - Target.__targets_json_location_default) + from_file = (Target.__targets_json_location or + Target.__targets_json_location_default) + + targets = json_file_to_dict(from_file) + for tgt in targets.values(): + tgt["_from_file"] = from_file for extra_target in Target.__extra_target_json_files: for k, v in json_file_to_dict(extra_target).items(): @@ -176,6 +180,7 @@ 'target.' % k) else: targets[k] = v + targets[k]["_from_file"] = extra_target return targets @@ -331,6 +336,14 @@ labels = (names + CORE_LABELS[self.core] + self.extra_labels) return labels + @property + def is_PSA_secure_target(self): + return 'SPE_Target' in self.labels + + @property + def is_PSA_non_secure_target(self): + return 'NSPE_Target' in self.labels + def init_hooks(self, hook, toolchain): """Initialize the post-build hooks for a toolchain. For now, this function only allows "post binary" hooks (hooks that are executed diff --git a/tools/test.py b/tools/test.py index 57af4cf..84bfd6b 100644 --- a/tools/test.py +++ b/tools/test.py @@ -43,7 +43,8 @@ from tools.utils import argparse_dir_not_parent from tools.toolchains import mbedToolchain, TOOLCHAIN_PATHS, TOOLCHAIN_CLASSES from tools.settings import CLI_COLOR_MAP - +from tools.settings import ROOT +from tools.targets import Target if __name__ == '__main__': try: # Parse Options @@ -146,6 +147,7 @@ if options.mcu is None: args_error(parser, "argument -m/--mcu is required") mcu = extract_mcus(parser, options)[0] + mcu_secured = Target.get_target(mcu).is_PSA_secure_target # Toolchain if options.tool is None: @@ -209,7 +211,10 @@ if not options.build_dir: args_error(parser, "argument --build is required") - base_source_paths = options.source_dir + if mcu_secured: + base_source_paths = ROOT + else: + base_source_paths = options.source_dir # Default base source path is the current directory if not base_source_paths: @@ -268,7 +273,8 @@ app_config=config, build_profile=profile, stats_depth=options.stats_depth, - ignore=options.ignore) + ignore=options.ignore, + spe_build=mcu_secured) # If a path to a test spec is provided, write it to a file if options.test_spec: diff --git a/tools/test/spm/__init__.py b/tools/test/spm/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tools/test/spm/__init__.py diff --git a/tools/test/spm/test_data.py b/tools/test/spm/test_data.py new file mode 100644 index 0000000..0374ab6 --- /dev/null +++ b/tools/test/spm/test_data.py @@ -0,0 +1,716 @@ +manifests = [ + { + 'name': 'TEST_PARTITION', + 'id': "0x7FFFFFFF", + "type": "APPLICATION-ROT", + 'priority': 'NORMAL', + 'entry_point': 'test_main', + 'stack_size': 512, # 512 == 0x200 + 'heap_size': 2048, + 'mmio_regions': [ + { + 'name': 'PERIPH1', + 'permission': 'READ-ONLY' + }, + { + 'name': 'PERIPH2', + 'permission': 'READ-ONLY' + }, + { + 'base': '0xCCCCCCCC', + 'size': 4096, 'permission': 'READ-ONLY' + }, + { + 'base': '0xDDDDDDDD', + 'size': 33554432, 'permission': 'READ-WRITE' + } + ], + 'services': [ + { + 'name': 'SID1', + 'identifier': '0x00000001', + 'signal': 'SID1', + 'minor_version': 1, + 'minor_policy': 'RELAXED', + 'non_secure_clients': True + }, + { + 'name': 'SID2', + 'identifier': '0x00000002', + 'signal': 'SID2', + 'minor_version': 2, + 'minor_policy': 'STRICT', + 'non_secure_clients': False + }, + ], + 'source_files': ['src1.cpp', 'src2.cpp'], + 'irqs': [ + {"line_num": 20, "signal": "ISR20"}, + {"line_num": 21, "signal": "ISR21"} + ], + 'extern_sids': ['SID3', 'SID4'] + }, + { + 'name': 'TEST_PARTITION2', + 'id': "0x7FFFFFFE", + "type": "APPLICATION-ROT", + 'priority': 'NORMAL', + 'entry_point': 'test2_main', + 'stack_size': 512, # 512 == 0x200 + 'heap_size': 2048, + 'mmio_regions': [ + { + 'name': 'PERIPH1', + 'permission': 'READ-ONLY' + }, + { + 'name': 'PERIPH3', + 'permission': 'READ-ONLY' + }, + { + 'base': '0xAAAAAAAA', + 'size': 4096, 'permission': 'READ-ONLY' + }, + { + 'base': '0xBBBBBBBB', + 'size': 33554432, 'permission': 'READ-WRITE' + } + ], + 'services': [ + { + 'name': 'SID3', + 'identifier': '0x00000003', + 'signal': 'SID3', + 'minor_version': 5, + 'minor_policy': 'RELAXED', + 'non_secure_clients': True + }, + { + 'name': 'SID4', + 'identifier': '0x00000004', + 'signal': 'SID4', + 'minor_version': 12, + 'minor_policy': 'STRICT', + 'non_secure_clients': False + }, + ], + 'source_files': ['src3.cpp', 'src4.cpp'], + 'irqs': [ + {"line_num": 22, "signal": "ISR22"}, + {"line_num": 23, "signal": "ISR23"} + ] + } +] + +manifests_for_circular_call_dependency_checks = [ + { + 'name': 'PARTITION1', + 'id': '0x7FFFFFFF', + 'type': 'APPLICATION-ROT', + 'priority': 'NORMAL', + 'entry_point': 'test_main', + 'stack_size': 512, + 'heap_size': 2048, + 'source_files': ['src1.cpp'], + 'services': [ + { + 'name': 'SID1', + 'identifier': '0x00000001', + 'signal': 'SID1', + 'non_secure_clients': False + }, + { + 'name': 'SID2', + 'identifier': '0x00000002', + 'signal': 'SID2', + 'non_secure_clients': False + } + ], + 'extern_sids': ['SID3', 'SID4'] + }, + { + 'name': 'PARTITION2', + 'id': '0x7FFFFFFE', + 'type': 'APPLICATION-ROT', + 'priority': 'NORMAL', + 'entry_point': 'test_main', + 'stack_size': 512, + 'heap_size': 2048, + 'source_files': ['src2.cpp'], + 'services': [ + { + 'name': 'SID3', + 'identifier': '0x00000003', + 'signal': 'SID3', + 'non_secure_clients': False + }, + { + 'name': 'SID4', + 'identifier': '0x00000004', + 'signal': 'SID4', + 'non_secure_clients': False + } + ], + 'extern_sids': ['SID1', 'SID2'] + }, + { + 'name': 'PARTITION3', + 'id': '0x7FFFFFFD', + 'type': 'APPLICATION-ROT', + 'priority': 'NORMAL', + 'entry_point': 'test_main', + 'stack_size': 512, + 'heap_size': 2048, + 'source_files': ['src3.cpp'], + 'services': [ + { + 'name': 'SID5', + 'identifier': '0x00000005', + 'signal': 'SID5', + 'non_secure_clients': False + } + ], + 'extern_sids': ['SID7'] + }, + { + 'name': 'PARTITION4', + 'id': '0x7FFFFFFC', + 'type': 'APPLICATION-ROT', + 'priority': 'NORMAL', + 'entry_point': 'test_main', + 'stack_size': 512, + 'heap_size': 2048, + 'source_files': ['src4.cpp'], + 'services': [ + { + 'name': 'SID6', + 'identifier': '0x00000006', + 'signal': 'SID6', + 'non_secure_clients': False + }, + { + 'name': 'SID7', + 'identifier': '0x00000007', + 'signal': 'SID7', + 'non_secure_clients': False + }, + ], + 'extern_sids': ['SID9'] + }, + { + 'name': 'PARTITION5', + 'id': '0x7FFFFFFB', + 'type': 'APPLICATION-ROT', + 'priority': 'NORMAL', + 'entry_point': 'test_main', + 'stack_size': 512, + 'heap_size': 2048, + 'source_files': ['src5.cpp'], + 'services': [ + { + 'name': 'SID8', + 'identifier': '0x00000008', + 'signal': 'SID8', + 'non_secure_clients': False + }, + { + 'name': 'SID9', + 'identifier': '0x00000009', + 'signal': 'SID9', + 'non_secure_clients': False + } + ], + 'extern_sids': ['SID5'] + }, + { + 'name': 'PARTITION6', + 'id': '0x7FFFFFFA', + 'type': 'APPLICATION-ROT', + 'priority': 'NORMAL', + 'entry_point': 'test_main', + 'stack_size': 512, + 'heap_size': 2048, + 'source_files': ['src6.cpp'], + 'services': [ + { + 'name': 'SID10', + 'identifier': '0x0000000A', + 'signal': 'SID10', + 'non_secure_clients': False + }, + { + 'name': 'SID11', + 'identifier': '0x0000000B', + 'signal': 'SID11', + 'non_secure_clients': False + } + ], + 'extern_sids': ['SID7', 'SID5'] + }, + { + 'name': 'PARTITION7', + 'id': '0x7FFFFFF9', + 'type': 'APPLICATION-ROT', + 'priority': 'NORMAL', + 'entry_point': 'test_main', + 'stack_size': 512, + 'heap_size': 2048, + 'source_files': ['src6.cpp'], + 'services': [ + { + 'name': 'SID12', + 'identifier': '0x0000000C', + 'signal': 'SID12', + 'non_secure_clients': False + } + ] + } +] + +invalid_minor_version_policy_rot_srv = [ + { + 'name': 'SID1', + 'identifier': '0x00000001', + 'signal': 'SID1', + 'minor_version': 1, + 'minor_policy': 'invalid_policy', + 'non_secure_clients': True + } +] + +invalid_nspe_callable_rot_srv = [ + { + 'name': 'SID1', + 'identifier': '0x00000001', + 'signal': 'SID1', + 'minor_version': 1, + 'minor_policy': 'STRICT', + 'non_secure_clients': 'invalid_value' + } +] + +missing_nspe_callable_rot_srv = [ + { + 'name': 'SID1', + 'identifier': '0x00000001', + 'signal': 'SID1', + 'minor_version': 1, + 'minor_policy': 'STRICT' + } +] + +duplicate_signal_rot_services = [ + { + 'name': 'SID3', + 'identifier': '0x00000001', + 'signal': 'SID1', + 'minor_version': 5, + 'minor_policy': 'RELAXED', + 'non_secure_clients': True + }, + { + 'name': 'SID4', + 'identifier': '0x00000002', + 'signal': 'SID2', + 'minor_version': 12, + 'minor_policy': 'STRICT', + 'non_secure_clients': True + }, +] + +duplicate_identifier_rot_services = [ + { + 'name': 'SID3', + 'identifier': '0x00000003', + 'signal': 'SID3', + 'minor_version': 5, + 'minor_policy': 'RELAXED', + 'non_secure_clients': True + }, + { + 'name': 'SID4', + 'identifier': '0x00000002', + 'signal': 'SID4', + 'minor_version': 12, + 'minor_policy': 'STRICT', + 'non_secure_clients': True + }, +] + +spe_contained_rot_services = [ + { + 'name': 'SID5', + 'identifier': '0x00000005', + 'signal': 'SID5', + 'minor_version': 5, + 'minor_policy': 'RELAXED', + 'non_secure_clients': False + }, + { + 'name': 'SID6', + 'identifier': '0x00000006', + 'signal': 'SID6', + 'minor_version': 12, + 'minor_policy': 'STRICT', + 'non_secure_clients': False + } +] + +missing_minor_version_rot_srv = [ + { + 'name': 'SID1', + 'identifier': '0x00000001', + 'signal': 'SID1', + 'minor_policy': 'RELAXED', + 'non_secure_clients': True + } +] + +missing_minor_version_policy_rot_srv = [ + { + 'name': 'SID2', + 'identifier': '0x00000002', + 'signal': 'SID2', + 'minor_version': 1, + 'non_secure_clients': True + } +] + +missing_minor_completley_rot_srv = [ + {'name': 'SID2', 'identifier': '0x00000002', 'signal': 'SID2', + 'non_secure_clients': True} +] + +duplicate_signal_irqs = [ + {"line_num": 22, "signal": "ISR20"} +] + +duplicate_line_num_irqs = [ + {"line_num": 21, "signal": "ISR22"} +] + +invalid_mmioregion_base = { + 'base': 'str', + 'size': 4096, + 'permission': 'READ-ONLY' +} + +invalid_mmioregion_size = { + 'base': '0xEEEEEEEE', + 'size': 'str', + 'permission': 'READ-ONLY' +} + +test_mock_files = { + 'manifest1': 1, + 'manifest2': 2, + 'template_common1': 3, + 'template_common2': 4, + 'template_NAME_3': 5, + 'template_NAME_4': 6, + 'gen1': 7, + 'gen2': 8, + 'gen3': 9, + 'gen4': 10, + 'gen5': 11, + 'gen6': 12 +} + +test_common_template = '''{ + "num_of_partitions": {{partitions|count}}, + "partition_names": [ +{% for partition in partitions %} + "{{partition.name}}"{{"" if loop.last else ","}} +{% endfor %} + ], + "num_of_region_pairs": {{region_pair_list|count}} +} +''' + +test_common_expected = '''{ + "num_of_partitions": 2, + "partition_names": [ + "TEST_PARTITION", + "TEST_PARTITION2" + ], + "num_of_region_pairs": 28 +} +''' + +test_partition_template = '''{ + "name": "{{partition.name}}", + "id": "0x{{"%0x"|format(partition.id|int)|upper}}", + "type": "{{partition.type}}", + "priority": "{{partition.priority|find_priority_key}}", + "entry_point": "{{partition.entry_point}}", + "stack_size": {{partition.stack_size}}, + "heap_size": {{partition.heap_size}}, + "mmio_regions": [ +{% for mmio in partition.mmio_regions %} + { + {% if mmio.size|int %} + "base": "{{mmio.base}}", + "size": {{mmio.size}}, + {% else %} + "name": "{{mmio.base}}", + {% endif %} + "permission": "{{mmio.permission|find_permission_key}}" + {{"}" if loop.last else "},"}} +{% endfor %} + ], + "services": [ +{% for rot_srv in partition.rot_services %} + { + "name": "{{rot_srv.name}}", + "identifier": "{{rot_srv.id}}", + "signal": "{{rot_srv.signal}}", + "minor_version": {{rot_srv.minor_version}}, + "minor_policy": "{{rot_srv.minor_policy}}", + "non_secure_clients": {{rot_srv.nspe_callable|lower}} + {{"}" if loop.last else "},"}} +{% endfor %} + ], +{% if partition.extern_sids %} + "extern_sids": [ +{% for ext_sid in partition.extern_sids %} + "{{ext_sid}}"{{"" if loop.last else ","}} +{% endfor %} + ], +{% endif %} + "source_files": [ +{% for src in partition.source_files %} + "{{src|basename}}"{{"" if loop.last else ","}} +{% endfor %} + ], + "irqs": [ +{% for irq in partition.irqs %} + { + "line_num": {{irq.line_num}}, + "signal": "{{irq.signal}}" + {{"}" if loop.last else "},"}} +{% endfor %} + ] +} +''' + +exceeding_services = [ + { + "name": "XSID1", + "signal": "XSID1", + "non_secure_clients": True, + "minor_version": 5, + "minor_policy": "RELAXED", + "identifier": "0x00000009" + }, + { + "name": "XSID2", + "signal": "XSID2", + "non_secure_clients": True, + "minor_version": 5, + "minor_policy": "RELAXED", + "identifier": "0x0000000a" + }, { + "name": "XSID3", + "signal": "XSID3", + "non_secure_clients": True, + "minor_version": 5, + "minor_policy": "RELAXED", + "identifier": "0x0000000b" + }, { + "name": "XSID4", + "signal": "XSID4", + "non_secure_clients": True, + "minor_version": 5, + "minor_policy": "RELAXED", + "identifier": "0x0000000c" + }, { + "name": "XSID5", + "signal": "XSID5", + "non_secure_clients": True, + "minor_version": 5, + "minor_policy": "RELAXED", + "identifier": "0x0000000d" + }, { + "name": "XSID6", + "signal": "XSID6", + "non_secure_clients": True, + "minor_version": 5, + "minor_policy": "RELAXED", + "identifier": "0x0000000e" + }, { + "name": "XSID7", + "signal": "XSID7", + "non_secure_clients": True, + "minor_version": 5, + "minor_policy": "RELAXED", + "identifier": "0x0000000f" + }, { + "name": "XSID8", + "signal": "XSID8", + "non_secure_clients": True, + "minor_version": 5, + "minor_policy": "RELAXED", + "identifier": "0x00000010" + }, { + "name": "XSID9", + "signal": "XSID9", + "non_secure_clients": True, + "minor_version": 5, + "minor_policy": "RELAXED", + "identifier": "0x00000011" + }, { + "name": "XSID10", + "signal": "XSID10", + "non_secure_clients": True, + "minor_version": 5, + "minor_policy": "RELAXED", + "identifier": "0x00000012" + }, { + "name": "XSID11", + "signal": "XSID11", + "non_secure_clients": True, + "minor_version": 5, + "minor_policy": "RELAXED", + "identifier": "0x00000013" + }, { + "name": "XSID12", + "signal": "XSID12", + "non_secure_clients": True, + "minor_version": 5, + "minor_policy": "RELAXED", + "identifier": "0x00000014" + }, { + "name": "XSID13", + "signal": "XSID13", + "non_secure_clients": True, + "minor_version": 5, + "minor_policy": "RELAXED", + "identifier": "0x00000015" + }, { + "name": "XSID14", + "signal": "XSID14", + "non_secure_clients": True, + "minor_version": 5, + "minor_policy": "RELAXED", + "identifier": "0x00000016" + }, { + "name": "XSID15", + "signal": "XSID15", + "non_secure_clients": True, + "minor_version": 5, + "minor_policy": "RELAXED", + "identifier": "0x00000017" + }, { + "name": "XSID16", + "signal": "XSID16", + "non_secure_clients": True, + "minor_version": 5, + "minor_policy": "RELAXED", + "identifier": "0x00000018" + }, { + "name": "XSID17", + "signal": "XSID17", + "non_secure_clients": True, + "minor_version": 5, + "minor_policy": "RELAXED", + "identifier": "0x00000019" + }, { + "name": "XSID18", + "signal": "XSID18", + "non_secure_clients": True, + "minor_version": 5, + "minor_policy": "RELAXED", + "identifier": "0x0000001a" + }, { + "name": "XSID19", + "signal": "XSID19", + "non_secure_clients": True, + "minor_version": 5, + "minor_policy": "RELAXED", + "identifier": "0x0000001b" + }, { + "name": "XSID20", + "signal": "XSID20", + "non_secure_clients": True, + "minor_version": 5, + "minor_policy": "RELAXED", + "identifier": "0x0000001c" + }, { + "name": "XSID21", + "signal": "XSID21", + "non_secure_clients": True, + "minor_version": 5, + "minor_policy": "RELAXED", + "identifier": "0x0000001d" + }, { + "name": "XSID22", + "signal": "XSID22", + "non_secure_clients": True, + "minor_version": 5, + "minor_policy": "RELAXED", + "identifier": "0x0000001e" + }, { + "name": "XSID23", + "signal": "XSID23", + "non_secure_clients": True, + "minor_version": 5, + "minor_policy": "RELAXED", + "identifier": "0x0000001f" + }, { + "name": "XSID24", + "signal": "XSID24", + "non_secure_clients": True, + "minor_version": 5, + "minor_policy": "RELAXED", + "identifier": "0x00000020" + }, { + "name": "XSID25", + "signal": "XSID25", + "non_secure_clients": True, + "minor_version": 5, + "minor_policy": "RELAXED", + "identifier": "0x00000021" + }, { + "name": "XSID26", + "signal": "XSID26", + "non_secure_clients": True, + "minor_version": 5, + "minor_policy": "RELAXED", + "identifier": "0x00000022" + }, { + "name": "XSID27", + "signal": "XSID27", + "non_secure_clients": True, + "minor_version": 5, + "minor_policy": "RELAXED", + "identifier": "0x00000023" + }, { + "name": "XSID28", + "signal": "XSID28", + "non_secure_clients": True, + "minor_version": 5, + "minor_policy": "RELAXED", + "identifier": "0x00000024" + }, { + "name": "XSID29", + "signal": "XSID29", + "non_secure_clients": True, + "minor_version": 5, + "minor_policy": "RELAXED", + "identifier": "0x00000025" + }, { + "name": "XSID30", + "signal": "XSID30", + "non_secure_clients": True, + "minor_version": 5, + "minor_policy": "RELAXED", + "identifier": "0x00000026" + }, { + "name": "XSID31", + "signal": "XSID31", + "non_secure_clients": True, + "minor_version": 5, + "minor_policy": "RELAXED", + "identifier": "0x00000027" + } +] diff --git a/tools/test/spm/test_generate_partition_code.py b/tools/test/spm/test_generate_partition_code.py new file mode 100644 index 0000000..0d73ad0 --- /dev/null +++ b/tools/test/spm/test_generate_partition_code.py @@ -0,0 +1,748 @@ +import filecmp +import re +import shutil +import tempfile + +import jsonschema.exceptions as jexcep +import pytest +from jinja2.defaults import DEFAULT_FILTERS + +from test_data import * +from tools.spm.generate_partition_code import * + +# Imported again as a module for monkey-patching +import tools.spm.generate_partition_code as generate_partition_code + +SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) + + +def extract_test_name(line): + return re.search(r'.*\[(.*)\]', line).group(1) + + +def dump_manifest_to_json(manifest, test_name, test_dir, create_files=True): + """ + Create a JSON manifest file from a dictionary. + + :param manifest: The manifest dictionary. + :param test_name: Name of the test. + :param test_dir: Directory to contain the JSON file. + :param create_files: Whether to create the source files listed in the + manifest 'source_files' entry. + :return: Path of the JSON file. + """ + test_file_name = test_dir.join('{}.json'.format(test_name)) + with open(test_file_name.strpath, 'wt') as fh: + json.dump(manifest, fh, indent=2) + + # Create all the partition source files + if create_files: + [test_dir.join(name).write(name) for name in + manifest.get('source_files', [])] + + return test_file_name.strpath + + +def find_priority_key(value): + """ + Finds the key in 'Manifest.PRIORITY' of a given value. + + :param value: The value. + :return: The key of the given value. + """ + return next( + (key for key, val in Manifest.PRIORITY.items() if val == value), + None + ) + + +def find_permission_key(value): + """ + Finds the key in 'MmioRegion.MMIO_PERMISIONS' of a given value. + + :param value: The value. + :return: The key of the given value. + """ + return next( + (key for key, val in MmioRegion.MMIO_PERMISSIONS.items() if + val == value), + None + ) + + +@pytest.fixture(scope="session") +def temp_test_data(tmpdir_factory): + """ + Fixture (https://docs.pytest.org/en/latest/fixture.html) function to be + used by the tests. + This fixture function Creates a valid JSON manifest file in a temporary + directory. The scope of this fixture is the entire test session. + + :param tmpdir_factory: Fixture used to create temporary directories. + see: https://docs.pytest.org/en/latest/tmpdir.html#the-tmpdir-factory-fixture + :return: A dictionary containing these keys: + 'dir': The temporary directory object created by this fixture. + 'json': The created valid manifest JSON file. + 'manifest': The manifest object read from the JSON file. + """ + test_dir = tmpdir_factory.mktemp('test_data') + fname = dump_manifest_to_json(manifests[0], 'valid_partition', test_dir) + valid_manifest = Manifest.from_json(fname) + return {'dir': test_dir, 'json': fname, 'manifest': valid_manifest} + + +""" +'modified_json_params' contain the parameters to be used in the +'modified_json' fixture. +Each key in the dictionary represents a different parameter to be used by +'modified_json', so for each test which uses +the 'modified_json' fixture, the test will run len(modified_json_params) times, + each time with different parameters. +Each parameter is a dictionary which contains these keys: + 'partition': A modified partition dictionary. + 'assert': The expected assertion which must occur when running with this + parameter. +""" +modified_json_params = { + 'missing_partition_name': { + 'partition': {k: manifests[0][k] for k in manifests[0] if k != 'name'}, + 'assert': jexcep.ValidationError + }, + 'missing_partition_id': { + 'partition': {k: manifests[0][k] for k in manifests[0] if k != 'id'}, + 'assert': jexcep.ValidationError + }, + 'missing_partition_priority': { + 'partition': {k: manifests[0][k] for k in manifests[0] if + k != 'priority'}, + 'assert': jexcep.ValidationError + }, + 'missing_entry_point': { + 'partition': {k: manifests[0][k] for k in manifests[0] if + k != 'entry_point'}, + 'assert': jexcep.ValidationError + }, + 'missing_stack_size': { + 'partition': {k: manifests[0][k] for k in manifests[0] if + k != 'stack_size'}, + 'assert': jexcep.ValidationError + }, + 'missing_heap_size': { + 'partition': {k: manifests[0][k] for k in manifests[0] if + k != 'heap_size'}, + 'assert': jexcep.ValidationError + }, + 'missing_source_files': { + 'partition': {k: manifests[0][k] for k in manifests[0] if + k != 'source_files'}, + 'assert': jexcep.ValidationError + }, + 'missing_irqs_and_sids': { + 'partition': {k: manifests[0][k] for k in manifests[0] if + k not in ['services', 'irqs']}, + 'assert': jexcep.ValidationError + }, + 'empty_source_files': { + 'partition': dict(manifests[0], source_files=[]), + 'assert': jexcep.ValidationError + }, + 'invalid_minor_policy': { + 'partition': dict(manifests[0], + services=invalid_minor_version_policy_rot_srv), + 'assert': jexcep.ValidationError + }, + 'invalid_nspe_callable': { + 'partition': dict(manifests[0], + services=invalid_nspe_callable_rot_srv), + 'assert': jexcep.ValidationError + }, + 'missing_nspe_callable': { + 'partition': dict(manifests[0], + services=missing_nspe_callable_rot_srv), + 'assert': jexcep.ValidationError + }, + 'invalid_stack_size': { + 'partition': dict(manifests[0], stack_size='str'), + 'assert': jexcep.ValidationError + }, + 'invalid_heap_size': { + 'partition': dict(manifests[0], heap_size='str'), + 'assert': jexcep.ValidationError + }, + 'invalid_priority': { + 'partition': dict(manifests[0], priority='invalid_priority'), + 'assert': jexcep.ValidationError + }, + 'invalid_mmioregion_base': { + 'partition': dict(manifests[0], + mmio_regions=[invalid_mmioregion_base]), + 'assert': jexcep.ValidationError + }, + 'invalid_mmioregion_size': { + 'partition': dict(manifests[0], + mmio_regions=[invalid_mmioregion_size]), + 'assert': jexcep.ValidationError + }, + 'invalid_irq_num': { + 'partition': dict(manifests[0], + irqs=[{"line_num": "str", "signal": "ISR22"}]), + 'assert': jexcep.ValidationError + }, + 'not_exist_src_filename': { + 'partition': dict(manifests[0], source_files=['missing.cpp']), + 'assert': AssertionError + }, + 'invalid_partition_id_decimal': { + 'partition': dict(manifests[0], id=-1), + 'assert': jexcep.ValidationError + }, + 'invalid_partition_id_hex': { + 'partition': dict(manifests[0], id='0xFFFFFFFF'), + 'assert': jexcep.ValidationError + }, + 'duplicates_extern_sids': { + 'partition': dict(manifests[0], extern_sids=['SID66', 'SID66']), + 'assert': jexcep.ValidationError + }, + 'exceeding_services': { + 'partition': dict(manifests[1], services=exceeding_services), + 'assert': AssertionError + } +} + + +@pytest.fixture(params=modified_json_params.values(), + ids=modified_json_params.keys()) +def modified_json(request, temp_test_data): + """ + Fixture (https://docs.pytest.org/en/latest/fixture.html) function to be + used by the tests. + This fixture function Creates a JSON manifest file from a given partition + dictionary and save it + to a temporary directory. + This fixture uses the 'temp_test_data' fixture. + This fixture is a parametrized fixture + (https://docs.pytest.org/en/latest/fixture.html#parametrizing-fixtures). + The scope of this fixture is a specific test. + + :param request: Request object which contain the current parameter from + 'modified_json_params'. + :param temp_test_data: The 'temp_test_data' fixture. + :return: A list containing these values: + - The created manifest JSON file for the current parameter. + - The expected assertion for the current parameter. + """ + testname = extract_test_name(request.node.name) + test_file = dump_manifest_to_json(request.param['partition'], testname, + temp_test_data['dir'], False) + return test_file, request.param['assert'] + + +def test_invalid_json(modified_json): + """ + Test which gets an invalid JSON manifest file (from the + 'modified_json' fixture) and tries to create a + Manifest object from it. + The test expects an assertion to happen. + + :param modified_json: The 'modified_json' fixture. + :return: + """ + with pytest.raises(modified_json[1]): + Manifest.from_json(modified_json[0]) + + +def test_valid_json(temp_test_data): + """ + Test which gets a valid JSON manifest file (from the 'temp_test_data' + fixture) and tries to create a Manifest object from it. + The test expects the Manifest to be same as the Manifest created by the + 'temp_test_data' fixture. + + :param temp_test_data: The 'temp_test_data' fixture. + :return: + """ + manifest = Manifest.from_json(temp_test_data['json']) + assert manifest == temp_test_data['manifest'] + + +# Test parametrization decorator +# See https://docs.pytest.org/en/latest/parametrize.html#pytest-mark-parametrize-parametrizing-test-functions +# Contain the parameters to be used in the 'test_validate_partition_manifest' +# test. It defines a list of (manifest, assertion) tuples which each entry +# will be the input of the 'test_validate_partition_manifest' test, the test +# will run len(LIST_OF_TUPPLES) times, each time with different (manifest, +# assertion) tuple. +# The tuple fields are: +# 'manifest': A modified partition dictionary. +# 'assertion': A tuple containing the expected assertion and assertion +# string which must occur when running with this parameter. +@pytest.mark.parametrize( + 'manifests, assertion', + [ + pytest.param( + [manifests[0], dict(manifests[1], name=manifests[0]['name'])], + (ValueError, r'Partition name .* is not unique, .*'), + id='duplicate_partition_name' + ), + pytest.param( + [manifests[0], dict(manifests[1], id=manifests[0]['id'])], + (ValueError, r'Partition id .* is not unique, .*'), + id='duplicate_partition_id' + ), + pytest.param( + [manifests[0], dict(manifests[1], services=manifests[0]['services'])], + (ValueError, r'Root of Trust Service name .* is found in both .*'), + id='duplicate_rot_srv_name' + ), + pytest.param( + [manifests[0], dict(manifests[1], services=duplicate_signal_rot_services)], + (ValueError, r'Root of Trust Service signal .* is found in both .*'), + id='duplicate_rot_srv_signal' + ), + pytest.param( + [manifests[0], dict(manifests[1], services=duplicate_identifier_rot_services)], + (ValueError, r'Root of Trust Service identifier .* is found in both .*'), + id='duplicate_rot_srv_identifier' + ), + pytest.param( + [manifests[0], dict(manifests[1], irqs=duplicate_signal_irqs)], + (ValueError, r'IRQ signal .* is found in both .*'), + id='duplicate_irq_signal' + ), + pytest.param( + [manifests[0], dict(manifests[1], irqs=duplicate_line_num_irqs)], + (ValueError, r'IRQ line number .* is found in both .*'), + id='duplicate_irq_line_num' + ), + pytest.param( + [manifests[0], dict(manifests[1], extern_sids=['SID66', 'SID999'])], + (ValueError, r'External SID\(s\) .* can\'t be found in any partition manifest.'), + id='orphan_extern_ids' + ), + pytest.param( + [manifests[0], dict(manifests[1], extern_sids=[manifests[0]['services'][0]['name']])], + (ValueError, r'Detected a circular call dependency between the partitions.'), + id='circular_call_dependency' + ), + pytest.param( + [{k: manifests[0][k] for k in manifests[0] if k != 'extern_sids'}, + dict({k: manifests[1][k] for k in manifests[1] if k != 'services' + and k != 'irqs'}, services=spe_contained_rot_services)], + (ValueError, r'Partition .* is not accessible from NSPE ' + 'and not referenced by any other partition.'), + id='dead_partition' + ) + ] +) +def test_validate_partition_manifest(request, temp_test_data, manifests, assertion): + """ + Test which creates an invalid manifest object (after passing JSON schema + validation) and call + validate_partition_manifests() with it and with a valid manifest object. + The test expects an assertion to happen. + + :param request: Request object. + :param temp_test_data: The 'temp_test_data' fixture. + :param manifest: The manifest value from the (manifest, assertion) tuple + for the current parameter. + :param assertion: The assertion value from the (manifest, assertion) tuple + for the current parameter. + :return: + """ + test_name = extract_test_name(request.node.name) + jsons = [dump_manifest_to_json(m, '%s_%d' % (test_name, i), temp_test_data['dir']) for i, m in enumerate(manifests)] + created_manifests = [Manifest.from_json(json) for json in jsons] + + with pytest.raises(assertion[0], match=assertion[1]): + validate_partition_manifests(created_manifests) + + +""" +'verify_json_params' contain the parameters to be used in the 'verify_json' +fixture. Each key in the dictionary represents a different parameter to be used +by 'verify_json', so for each test which uses the 'verify_json' fixture, the +test will run len(verify_json_params) times, each time with different +parameters. +Each parameter is a dictionary which contains these keys: + 'partition': A modified partition dictionary. + 'field': The modified field name. + 'expected': The expected field object. +""" +verify_json_params = { + 'missing_minor_version_rot_services': { + 'partition': dict(manifests[0], + services=missing_minor_version_rot_srv), + 'field': 'rot_services', + 'expected': [ + RotService( + name='SID1', identifier='0x00000001',signal='SID1', + minor_policy='RELAXED', non_secure_clients=True, minor_version=1 + ) + ] + }, + 'missing_minor_version_policy_rot_services': { + 'partition': dict(manifests[0], + services=missing_minor_version_policy_rot_srv), + 'field': 'rot_services', + 'expected': [ + RotService( + name='SID2', identifier='0x00000002', signal='SID2', + minor_policy='STRICT', non_secure_clients=True, minor_version=1 + ) + ] + }, + 'missing_minor_completley_rot_services': { + 'partition': dict(manifests[0], + services=missing_minor_completley_rot_srv), + 'field': 'rot_services', + 'expected': [ + RotService( + name='SID2', identifier='0x00000002', signal='SID2', + minor_policy='STRICT', non_secure_clients=True, minor_version=1 + ) + ] + } +} + + +@pytest.fixture(params=verify_json_params.values(), + ids=verify_json_params.keys()) +def verify_json(request, tmpdir_factory): + """ + Fixture (https://docs.pytest.org/en/latest/fixture.html) function to be + used by the tests. + This fixture function Creates 2 JSON manifest files (The 1st from + 'verify_json_params', the 2nd from manifests[1]) and saves them to a + temporary directory. This fixture is a parametrized fixture + (https://docs.pytest.org/en/latest/fixture.html#parametrizing-fixtures). + The scope of this fixture is a specific test. + + :param request: Request object which contain the current parameter from + 'verify_json_params'. + :param tmpdir_factory: The 'tmpdir_factory' fixture. + :return: A dictionary containing these keys: + 'files_list': A list of the created manifest JSON files. + 'field': The changed field in the 1st manifest. + 'expected': The expected 'field' object. + """ + test_dir = tmpdir_factory.mktemp('test_data') + test_name = extract_test_name(request.node.name) + files_list = [ + dump_manifest_to_json(request.param['partition'], '%s1' % test_name, + test_dir), + dump_manifest_to_json(dict(manifests[1], extern_sids=[]), + '%s2' % test_name, test_dir) + ] + return {'files_list': files_list, 'field': request.param['field'], + 'expected': request.param['expected']} + + +def test_verify_json(verify_json): + """ + Test which gets 2 JSON manifest files (from the 'verify_json' fixture), + create Manifest objects from them, call validate_partition_manifests() on + the manifest objects and check that the 1st Manifest object is as expected. + + :param verify_json: The 'verify_json' fixture. + :return: + """ + manifest1 = Manifest.from_json(verify_json['files_list'][0]) + manifest2 = Manifest.from_json(verify_json['files_list'][1]) + + validate_partition_manifests([manifest1, manifest2]) + assert getattr(manifest1, verify_json['field']) == verify_json['expected'] + + +@pytest.fixture(scope="function") +def test_template_setup(tmpdir_factory): + """ + Fixture (https://docs.pytest.org/en/latest/fixture.html) function to be + used by the tests. This fixture function Creates JSON manifest files, + Manifest objects from 'manifest' and template files in a temporary + directory. The scope of this fixture is the entire test session. + + :param tmpdir_factory: Fixture used to create temporary directories. + see: https://docs.pytest.org/en/latest/tmpdir.html#the-tmpdir-factory-fixture + :return: A dictionary containing these keys: + 'dir': The temporary directory object created by this fixture. + 'template_files': List of the created template files. + 'manifest_files': List of the created manifest JSON files. + 'manifests': List of the created Manifest objects. + 'filters': Dictionary with additional filters for + generate_source_files() + """ + + def find_priority_key(value): + """ + Finds the key in 'Manifest.PRIORITY' of a given value. + + :param value: The value. + :return: The key of the given value. + """ + return next( + (key for key, val in Manifest.PRIORITY.items() if val == value), + None) + + def find_permission_key(value): + """ + Finds the key in 'MmioRegion.MMIO_PERMISIONS' of a given value. + + :param value: The value. + :return: The key of the given value. + """ + return next((key for key, val in MmioRegion.MMIO_PERMISSIONS.items() if + val == value), None) + + test_dir = tmpdir_factory.mktemp('test_data') + manifest_files = [ + dump_manifest_to_json(manifest, manifest['name'], test_dir) for + manifest in manifests] + manifest_objects = [Manifest.from_json(_file) for _file in manifest_files] + filters = { + 'basename': os.path.basename, + 'find_priority_key': find_priority_key, + 'find_permission_key': find_permission_key + } + template_files = [test_dir.join('_NAME_.json.tpl'), + test_dir.join('common.json.tpl')] + for template, _file in [(test_partition_template, template_files[0]), + (test_common_template, template_files[1])]: + _file.write(template) + template_files = [_file.strpath for _file in template_files] + + expected_common_files = [test_dir.join('common.json')] + for output, _file in [(test_common_expected, expected_common_files[0])]: + _file.write(output) + expected_common_files = [_file.strpath for _file in expected_common_files] + + return { + 'dir': test_dir.strpath, + 'template_files': template_files, + 'manifest_files': manifest_files, + 'common_files': expected_common_files, + 'manifests': manifest_objects, + 'filters': filters + } + + +def test_generate_source_files(test_template_setup): + """ + Test which calls generate_source_files() with the data from + 'test_template_setup' fixture and checks normal output. + + :param test_template_setup: The 'test_template_setup' fixture. + :return: + """ + + before_file_list = set(os.listdir(test_template_setup['dir'])) + partition_templates = filter(lambda filename: '_NAME_' in filename, test_template_setup['template_files']) + common_templates = filter(lambda filename: '_NAME_' not in filename, test_template_setup['template_files']) + common_templates = { + t: path_join(test_template_setup['dir'], os.path.basename(os.path.splitext(t)[0])) for t in common_templates + } + region_list = [] + + for manifest in test_template_setup['manifests']: + generate_source_files( + templates=manifest.templates_to_files(partition_templates, test_template_setup['dir'], test_template_setup['dir']), + render_args={ + 'partition': manifest, + 'dependent_partitions': manifest.find_dependencies(test_template_setup['manifests']) + }, + output_folder=test_template_setup['dir'], + extra_filters=test_template_setup['filters'] + ) + for region in manifest.mmio_regions: + region_list.append(region) + + generate_source_files( + common_templates, + render_args={ + 'partitions': test_template_setup['manifests'], + 'region_pair_list': list(itertools.combinations(region_list, 2)) + }, + output_folder=test_template_setup['dir'], + extra_filters=test_template_setup['filters'] + ) + + after_file_list = set(os.listdir(test_template_setup['dir'])) + generated_files = list(after_file_list.difference(before_file_list)) + + for gen_file in [os.path.join(test_template_setup['dir'], f) for f in generated_files]: + """ + For each generated json file in 'autogen_dir': + 1. Load the json file to a dictionary named 'generated'. + 2. If it was generated from a partition template ('generated' has a 'name' key): + a) Read the original manifest json from the test temp dir. + b) Load the manifest json file to a dictionary named 'expected'. + Else (generated from a common template): + a) Calculate 'region_list'. + b) Build the 'expected' dictionary with values from the original manifest objects. + 3. Compare 'generated' with 'expected'. + """ + with open(gen_file) as fh: + generated = json.load(fh) + + if 'name' in generated: + input_file = os.path.join(test_template_setup['dir'], + generated['name'] + '.json') + assert os.path.isfile(input_file) + assert input_file in test_template_setup['manifest_files'] + with open(input_file) as fh: + expected = json.load(fh) + else: + region_list = [region for manifest in + test_template_setup['manifests'] for region in + manifest.mmio_regions] + expected = { + 'num_of_partitions': len(test_template_setup['manifests']), + 'partition_names': [manifest.name for manifest in + test_template_setup['manifests']], + 'num_of_region_pairs': len( + list(itertools.combinations(region_list, 2))) + } + assert generated == expected + + +def test_generate_partitions_sources(monkeypatch, test_template_setup): + """ + Test which calls generate_partitions_sources() with the data from + 'test_template_setup' fixture. + Because generate_partitions_sources() is a compound of the other functions in + the module which are tested individually, this test just do the following: + 1. Calls generate_partitions_sources() and checks that the autogen directory + was created. + 2. Saves the modified times of the generated files. + 3. Calls generate_partitions_sources() again, checks that the autogen directory + still exist and that modified times of the generated files didn't + change. + + :param monkeypatch: The 'monkeypatch' fixture + (https://docs.pytest.org/en/latest/monkeypatch.html). + :param test_template_setup: The 'test_template_setup' fixture. + :return: + """ + monkeypatch.setitem(DEFAULT_FILTERS, 'basename', os.path.basename) + monkeypatch.setitem(DEFAULT_FILTERS, 'find_priority_key', + find_priority_key) + monkeypatch.setitem(DEFAULT_FILTERS, 'find_permission_key', + find_permission_key) + + autogen_dirs = generate_partitions_sources(test_template_setup['manifest_files']) + autogen_dirs_backup = tempfile.mkdtemp() + for directory in autogen_dirs: + assert os.path.isdir(directory) + shutil.copytree(directory, os.path.join(autogen_dirs_backup, os.path.split(directory)[1])) + + autogen_dirs = generate_partitions_sources(test_template_setup['manifest_files']) + for directory in autogen_dirs: + assert os.path.isdir(directory) + dcmp = filecmp.dircmp(directory, os.path.join(autogen_dirs_backup, os.path.split(directory)[1])) + assert not dcmp.diff_files + + spm_output_dir = generate_psa_setup( + test_template_setup['manifest_files'], + os.path.join(test_template_setup['dir'], 'SETUP'), + weak_setup=False, + extra_filters=test_template_setup['filters'] + ) + + assert os.path.isdir(spm_output_dir) + shutil.copytree(spm_output_dir, os.path.join(autogen_dirs_backup, os.path.split(spm_output_dir)[1])) + + for gen_file in test_template_setup['common_files']: + generated_file = os.path.join(spm_output_dir, gen_file) + expected_file = os.path.join(test_template_setup['dir'], gen_file) + assert os.path.isfile(generated_file) + assert os.path.isfile(expected_file) + + with open(generated_file) as gfh: + with open(expected_file) as efh: + assert json.load(gfh) == json.load(efh) + + spm_output_dir = generate_psa_setup( + test_template_setup['manifest_files'], + os.path.join(test_template_setup['dir'], 'SETUP'), + weak_setup=False, + extra_filters=test_template_setup['filters'] + ) + + assert os.path.isdir(spm_output_dir) + dcmp = filecmp.dircmp(spm_output_dir, os.path.join(autogen_dirs_backup, os.path.split(spm_output_dir)[1])) + assert not dcmp.diff_files + + +circular_call_dependency_params = { + 'no manifests': { + 'manifests': [], + 'result': False + }, + 'one manifest': { + 'manifests': ['PARTITION1'], + 'result': False + }, + '2 manifests with dependency': { + 'manifests': ['PARTITION1', 'PARTITION2'], + 'result': True + }, + '2 manifests without dependency': { + 'manifests': ['PARTITION1', 'PARTITION3'], + 'result': False + }, + '5 manifests with dependency': { + 'manifests': ['PARTITION1', 'PARTITION3', 'PARTITION4', 'PARTITION5', 'PARTITION6'], + 'result': True + }, + '5 manifests without dependency': { + 'manifests': ['PARTITION1', 'PARTITION3', 'PARTITION4', 'PARTITION6', 'PARTITION7'], + 'result': False + } +} + + +@pytest.fixture(params=circular_call_dependency_params.values(), + ids=circular_call_dependency_params.keys()) +def circular_dependencies(request, tmpdir_factory): + """ + Fixture (https://docs.pytest.org/en/latest/fixture.html) function to be + used by the tests. + This fixture function Creates a JSON manifest file from a given partition + dictionary and save it + to a temporary directory. + This fixture uses the 'temp_test_data' fixture. + This fixture is a parametrized fixture + (https://docs.pytest.org/en/latest/fixture.html#parametrizing-fixtures). + The scope of this fixture is a specific test. + + :param request: Request object which contain the current parameter from + 'circular_call_dependency_params'. + :param temp_test_data: The 'temp_test_data' fixture. + :return: A Dictionary containing these values: + - files - list of manifest filesgenerated + - The expected result from check_circular_call_dependencies + """ + test_dir = tmpdir_factory.mktemp('test_data') + + test_manifests = filter(lambda x: x['name'] in request.param['manifests'], + manifests_for_circular_call_dependency_checks) + manifest_files = [ + dump_manifest_to_json(manifest, manifest['name'], test_dir) for + manifest in test_manifests] + + return {'files': manifest_files, 'result': request.param['result']} + + +def test_check_circular_call_dependencies(circular_dependencies): + """ + Test detection of circular call dependencies between the partitions. + The test performs the circular call dependency check in a few + predefined partition topologies and compares the result with the expected value. + + :param circular_dependencies: the 'circular_dependencies' fixture + :return: + """ + + objects = [Manifest.from_json(_file) for _file in circular_dependencies['files']] + + assert check_circular_call_dependencies(objects) == circular_dependencies['result'] diff --git a/tools/test_api.py b/tools/test_api.py index c5de731..7d285c6 100644 --- a/tools/test_api.py +++ b/tools/test_api.py @@ -2229,7 +2229,7 @@ clean=False, notify=None, jobs=1, macros=None, silent=False, report=None, properties=None, continue_on_build_fail=False, app_config=None, - build_profile=None, stats_depth=None, ignore=None): + build_profile=None, stats_depth=None, ignore=None, spe_build=False): """Given the data structure from 'find_tests' and the typical build parameters, build all the tests @@ -2287,7 +2287,8 @@ 'build_profile': build_profile, 'toolchain_paths': TOOLCHAIN_PATHS, 'stats_depth': stats_depth, - 'notify': MockNotifier() + 'notify': MockNotifier(), + 'spe_build': spe_build } results.append(p.apply_async(build_test_worker, args, kwargs)) diff --git a/tools/toolchains/__init__.py b/tools/toolchains/__init__.py index 0d4ac82..7a49949 100644 --- a/tools/toolchains/__init__.py +++ b/tools/toolchains/__init__.py @@ -787,6 +787,27 @@ self.ld.append(define_string) self.flags["ld"].append(define_string) + flags2params = {} + if self.target.is_PSA_non_secure_target: + flags2params = { + "MBED_ROM_START": "target.non-secure-rom-start", + "MBED_ROM_SIZE": "target.non-secure-rom-size", + "MBED_RAM_START": "target.non-secure-ram-start", + "MBED_RAM_SIZE": "target.non-secure-ram-size" + } + if self.target.is_PSA_secure_target: + flags2params = { + "MBED_ROM_START": "target.secure-rom-start", + "MBED_ROM_SIZE": "target.secure-rom-size", + "MBED_RAM_START": "target.secure-ram-start", + "MBED_RAM_SIZE": "target.secure-ram-size" + } + + for flag, param in flags2params.items(): + define_string = self.make_ld_define(flag, params[param].value) + self.ld.append(define_string) + self.flags["ld"].append(define_string) + # Set the configuration data def set_config_data(self, config_data): self.config_data = config_data diff --git a/tools/utils.py b/tools/utils.py index 368b968..fb8869c 100644 --- a/tools/utils.py +++ b/tools/utils.py @@ -215,6 +215,23 @@ copyfile(src, dst) +def copy_when_different(src, dst): + """ Only copy the file when it's different from its destination. + + Positional arguments: + src - the source of the copy operation + dst - the destination of the copy operation + """ + if isdir(dst): + _, base = split(src) + dst = join(dst, base) + if exists(dst): + with open(src, 'rb') as srcfd, open(dst, 'rb') as dstfd: + if srcfd.read() == dstfd.read(): + return + copyfile(src, dst) + + def delete_dir_files(directory): """ A function that does rm -rf