Merge pull request #8744 from kfnta/psa_spm_base

PSA Secure partition manager and services
pull/8882/head
Martin Kojtal 2018-11-27 15:26:38 +01:00 committed by GitHub
commit e69aa15aea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
102 changed files with 11412 additions and 23 deletions

3
.gitignore vendored
View File

@ -94,3 +94,6 @@ log
# Icetea related file
test_suite.json
# default delivery dir
DELIVERY/

View File

@ -0,0 +1,49 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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_HAL_SPM_FAULT_FUNCTIONS__
#define __MBED_HAL_SPM_FAULT_FUNCTIONS__
#include "cmsis_compiler.h"
#ifdef __cplusplus
extern "C" {
#endif
// Retruns the value of the LR register
// Used to determine which stack the exception happend in
__STATIC_FORCEINLINE uint32_t __get_LR(void);
// This function is required as we need a symbol/address
// to jump to from fault handler.
void do_nothing(void);
// Test exception handler
static void hard_fault_handler_test();
// 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);
#ifdef __cplusplus
}
#endif
#endif // __MBED_HAL_SPM_FAULT_FUNCTIONS__

156
TESTS/mbed_hal/spm/main.cpp Normal file
View File

@ -0,0 +1,156 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
#if !defined(COMPONENT_PSA_SRV_IPC)
#error [NOT_SUPPORTED] Test supported only on PSA targets
#endif
#if (defined( __CC_ARM ) || defined(__ARMCC_VERSION) || defined( __ICCARM__ ))
#error [NOT_SUPPORTED] this test is supported on GCC only
#endif
#include "utest/utest.h"
#include "unity/unity.h"
#include "greentea-client/test_env.h"
#include "cmsis.h"
#include "spm_api.h"
#include <stdlib.h>
#include "fault_functions.h"
using namespace utest::v1;
#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;
__STATIC_FORCEINLINE uint32_t __get_LR(void)
{
uint32_t result;
__ASM volatile("MOV %0, lr" : "=r"(result));
return (result);
}
void do_nothing(void)
{
__NOP();
}
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;
}
__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);
}

View File

@ -0,0 +1,82 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
/***********************************************************************************************************************
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT.
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* Template Version 1.0
* Generated by tools/spm/generate_partition_code.py Version 1.0
**********************************************************************************************************************/
#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;
}

View File

@ -0,0 +1,37 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
#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;
}

View File

@ -0,0 +1,101 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
/***********************************************************************************************************************
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT.
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* Template Version 1.0
* Generated by tools/spm/generate_partition_code.py Version 1.0
**********************************************************************************************************************/
#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");
}
}

View File

@ -0,0 +1,50 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
/***********************************************************************************************************************
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT.
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* Template Version 1.0
* Generated by tools/spm/generate_partition_code.py Version 1.0
**********************************************************************************************************************/
#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)
#endif // PSA_TEST_ITS_RESET_PARTITION_H

View File

@ -0,0 +1,59 @@
/* Copyright (c) 2018 ARM Limited
*
* 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.
*/
#include <string.h>
#include <stdlib.h>
#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

View File

@ -0,0 +1,65 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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 <string.h>
#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

View File

@ -0,0 +1,31 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
/***********************************************************************************************************************
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT.
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* Template Version 1.0
* Generated by tools/spm/generate_partition_code.py Version 1.0
**********************************************************************************************************************/
#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

View File

@ -34,8 +34,6 @@ extern "C"
/**
* \brief Remove the provided key and its associated data from the storage
*
* \param[in] uid The uid value
*
* \return A status indicating the success/failure of the operation
*
* \retval PSA_ITS_SUCCESS The operation completed successfully

View File

@ -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"
]
}

View File

@ -38,6 +38,9 @@ static void pits_test()
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);

View File

@ -0,0 +1,483 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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 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;
}

View File

@ -0,0 +1,31 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
/***********************************************************************************************************************
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* 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

View File

@ -0,0 +1,125 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
/***********************************************************************************************************************
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT.
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* Template Version 1.0
* Generated by tools/spm/generate_partition_code.py Version 1.0
**********************************************************************************************************************/
#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");
}
}

View File

@ -0,0 +1,56 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
/***********************************************************************************************************************
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT.
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* Template Version 1.0
* Generated by tools/spm/generate_partition_code.py Version 1.0
**********************************************************************************************************************/
#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)
#endif // PSA_CLIENT_TESTS_PART1_PARTITION_H

View File

@ -0,0 +1,82 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
/***********************************************************************************************************************
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT.
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* Template Version 1.0
* Generated by tools/spm/generate_partition_code.py Version 1.0
**********************************************************************************************************************/
#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;
}

View File

@ -0,0 +1,85 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
#include <string.h>
#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);
}
}
}

View File

@ -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"
]
}

View File

@ -0,0 +1,33 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
/***********************************************************************************************************************
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT.
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* Template Version 1.0
* Generated by tools/spm/generate_partition_code.py Version 1.0
**********************************************************************************************************************/
#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

View File

@ -0,0 +1,287 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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 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;
}

View File

@ -0,0 +1,90 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
#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));
}
}
}

View File

@ -0,0 +1,99 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
#include <string.h>
#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;
}
}
}
}

View File

@ -0,0 +1,30 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
/***********************************************************************************************************************
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* 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

View File

@ -0,0 +1,119 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
/***********************************************************************************************************************
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT.
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* Template Version 1.0
* Generated by tools/spm/generate_partition_code.py Version 1.0
**********************************************************************************************************************/
#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");
}
}

View File

@ -0,0 +1,53 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
/***********************************************************************************************************************
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT.
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* Template Version 1.0
* Generated by tools/spm/generate_partition_code.py Version 1.0
**********************************************************************************************************************/
#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)
#endif // PSA_SERVER_TEST_PART1_PARTITION_H

View File

@ -0,0 +1,30 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
/***********************************************************************************************************************
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* 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

View File

@ -0,0 +1,113 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
/***********************************************************************************************************************
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT.
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* Template Version 1.0
* Generated by tools/spm/generate_partition_code.py Version 1.0
**********************************************************************************************************************/
#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");
}
}

View File

@ -0,0 +1,53 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
/***********************************************************************************************************************
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT.
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* Template Version 1.0
* Generated by tools/spm/generate_partition_code.py Version 1.0
**********************************************************************************************************************/
#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)
#endif // PSA_SERVER_TEST_PART2_PARTITION_H

View File

@ -0,0 +1,97 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
/***********************************************************************************************************************
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT.
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* Template Version 1.0
* Generated by tools/spm/generate_partition_code.py Version 1.0
**********************************************************************************************************************/
#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;
}

View File

@ -0,0 +1,58 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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 __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__ */

View File

@ -0,0 +1,681 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
#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
};

View File

@ -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"
]
}

View File

@ -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"
]
}

View File

@ -0,0 +1,32 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
/***********************************************************************************************************************
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT.
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* Template Version 1.0
* Generated by tools/spm/generate_partition_code.py Version 1.0
**********************************************************************************************************************/
#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

View File

@ -0,0 +1,32 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
/***********************************************************************************************************************
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT.
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* Template Version 1.0
* Generated by tools/spm/generate_partition_code.py Version 1.0
**********************************************************************************************************************/
#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

View File

@ -0,0 +1,58 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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 __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__ */

View File

@ -0,0 +1,94 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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 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;
}

View File

@ -0,0 +1,122 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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 <string.h>
#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);
}
}

View File

@ -0,0 +1,82 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
/***********************************************************************************************************************
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT.
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* Template Version 1.0
* Generated by tools/spm/generate_partition_code.py Version 1.0
**********************************************************************************************************************/
#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;
}

View File

@ -0,0 +1,29 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
/***********************************************************************************************************************
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* 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

View File

@ -0,0 +1,101 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
/***********************************************************************************************************************
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT.
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* Template Version 1.0
* Generated by tools/spm/generate_partition_code.py Version 1.0
**********************************************************************************************************************/
#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");
}
}

View File

@ -0,0 +1,50 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
/***********************************************************************************************************************
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT.
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* Template Version 1.0
* Generated by tools/spm/generate_partition_code.py Version 1.0
**********************************************************************************************************************/
#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)
#endif // PSA_SMOKE_TEST_PART1_PARTITION_H

View File

@ -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"
]
}

View File

@ -0,0 +1,31 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
/***********************************************************************************************************************
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT.
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* Template Version 1.0
* Generated by tools/spm/generate_partition_code.py Version 1.0
**********************************************************************************************************************/
#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

View File

@ -0,0 +1,116 @@
/* Copyright (c) 2018 ARM Limited
*
* 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.
*/
#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;
}

View File

@ -0,0 +1,196 @@
/* Copyright (c) 2018 ARM Limited
*
* 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.
*/
#include <string.h>
#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

View File

@ -0,0 +1,137 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
/***********************************************************************************************************************
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT.
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* Template Version 1.0
* Generated by tools/spm/generate_partition_code.py Version 1.0
**********************************************************************************************************************/
#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");
}
}

View File

@ -0,0 +1,59 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
/***********************************************************************************************************************
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT.
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* Template Version 1.0
* Generated by tools/spm/generate_partition_code.py Version 1.0
**********************************************************************************************************************/
#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)
#endif // PSA_ITS_PARTITION_H

View File

@ -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"
]
}

View File

@ -0,0 +1,34 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
/***********************************************************************************************************************
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT.
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* Template Version 1.0
* Generated by tools/spm/generate_partition_code.py Version 1.0
**********************************************************************************************************************/
#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

View File

@ -0,0 +1,165 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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 <string.h>
#include <stdbool.h>
#include <stdlib.h>
/* ------------------------------------ Definitions ---------------------------------- */
#define PSA_HANDLE_MGR_HANDLE_INDEX_POS 16
#define PSA_HANDLE_MGR_HANDLE_INDEX_MSK 0xFFFF
/* ------------------------------------- Functions ----------------------------------- */
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;
}
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;
}
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;
}

View File

@ -0,0 +1,160 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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 <stdint.h>
/* -------------------------------- 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.
*/
/* --------------------------------- 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
// 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.
#define PSA_HANDLE_MGR_MAX_HANDLES_NUM 0x8000
/* -------------------------------------- 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 ----------------------------------- */
/*
* @brief create unique handle identifier
*
* 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.
*
* @param[in] handle_mgr A pointer to the handle manager object
* @param[in] handle_mem A pointer to a pre-allocated handle memory to get a handle identifier for
* @param[in] friend_pid 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);
/*
* @brief remove a handle from the handle manager.
*
* @param handle_mgr A pointer to the handle manager object
* @param handle The handle to be removed
*/
void psa_hndl_mgr_handle_destroy(psa_handle_manager_t *handle_mgr, psa_handle_t handle);
/*
* @brief dereference handle
*
* This function retrieves the pointer associated with the input <handle>.
*
* @note This function will panic in case caller not allowed to dereference the memory
* or handler does not correspond to a valid existing handle
*
* @param handle_mgr A pointer to the handle manager object.
* @param handle The handle for which we request the corresponding memory handle.
* @return void* 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);
#ifdef __cplusplus
}
#endif
#endif /* __MBED_HANDLE_MANAGER_H__ */

View File

@ -0,0 +1,72 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
/***********************************************************************************************************************
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT.
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* Template Version 1.0
* Generated by tools/spm/generate_partition_code.py Version 1.0
**********************************************************************************************************************/
#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;
}

View File

@ -0,0 +1,353 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
#include <string.h>
#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;
}

View File

@ -0,0 +1,172 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
#include "cmsis_os2.h"
#include "psa_defs.h"
#include "spm_internal.h"
#include "spm_panic.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;
}

View File

@ -0,0 +1,167 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
#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));
}

View File

@ -0,0 +1,251 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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_INTERNAL_H
#define SPM_INTERNAL_H
#include <stdbool.h>
#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
);
/*
* Assert and modify PSA IPC channel state machine state
*
* @param[in,out] current_state - current state
* @param[in] expected_state - expected state
* @param[in] new_state - new state
*/
void channel_state_switch(uint8_t *current_state, uint8_t expected_state, uint8_t new_state);
/*
* Assert PSA IPC channel state machine state
*
* @param[in] current_state - current state
* @param[in] expected_state - expected state
*/
void channel_state_assert(uint8_t *current_state, uint8_t expected_state);
#ifdef __cplusplus
}
#endif
#endif // SPM_INTERNAL_H

View File

@ -0,0 +1,34 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
#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)

View File

@ -0,0 +1,66 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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_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__

View File

@ -0,0 +1,584 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
#include <string.h>
#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);
}

View File

@ -0,0 +1,172 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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_SPM_SERVER_H__
#define __MBED_SPM_SERVER_H__
/** @addtogroup SPM
* The Secure Partition Manager (SPM) 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 and 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 that 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 is 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__ */

View File

@ -0,0 +1,229 @@
/* Copyright (c) 2018 ARM Limited
*
* 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 <stdio.h>
#include "ipc_defs.h"
#include "ipc_queue.h"
#include "spm_client.h"
#include "spm_messages.h"
#include "mbed_assert.h"
#include "cmsis_os2.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);
}

View File

@ -0,0 +1,123 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
#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);
}
}

View File

@ -0,0 +1,178 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
#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);
}

View File

@ -0,0 +1,50 @@
/* Copyright (c) 2018 ARM Limited
*
* 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 __PSA_MBED_IPC_DEFS_H__
#define __PSA_MBED_IPC_DEFS_H__
// Includes
// --------
#include <stdint.h>
#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 0x3d339a77
// 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__

View File

@ -0,0 +1,121 @@
/* Copyright (c) 2018 ARM Limited
*
* 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 "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);
}
}

View File

@ -0,0 +1,126 @@
/* Copyright (c) 2018 ARM Limited
*
* 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 __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 0x63284A0C
#define IPC_QUEUE_PRODUCER_MAGIC 0XA248D9FF
#define IPC_QUEUE_CONSUMER_MAGIC 0XA68B6542
#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__

View File

@ -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&reg;-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&reg;-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)

View File

@ -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&reg;-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/

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -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
}
}
}

View File

@ -0,0 +1,119 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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_PSA_DEFS_H__
#define __MBED_PSA_DEFS_H__
/** @addtogroup SPM
* @{
*/
/* -------------------------------------- Includes ----------------------------------- */
#include <stdint.h>
#include <stdlib.h>
/* --------------------------------- 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 Root of Trust (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 nonrecoverable 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 an RoT 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 that 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__ */

View File

@ -0,0 +1,112 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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_SPM_CLIENT_H__
#define __MBED_SPM_CLIENT_H__
/** @addtogroup SPM
* The Secure Partition Manager (SPM) 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 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 is 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 numbers 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__ */

View File

@ -0,0 +1,51 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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_SPM_INIT_H__
#define __MBED_SPM_INIT_H__
#if defined(COMPONENT_SPE)
/*
* Initialize SPM
*/
void psa_spm_init(void);
#endif // defined(COMPONENT_SPE)
#if defined(COMPONENT_SPM_MAILBOX)
/*
* Initialize SPM mailbox driver
*/
void spm_ipc_mailbox_init(void);
#if defined(COMPONENT_NSPE)
/*
* PSA mailbox dispatcher
*
* Dispatcher thread never returns and expected to be called
* from startup code as a detached thread entry point
*/
void psa_spm_mailbox_dispatcher(void);
#endif // defined(COMPONENT_NSPE)
#endif // defined(COMPONENT_SPM_MAILBOX)
#endif // __MBED_SPM_INIT_H__

View File

@ -0,0 +1,66 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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

103
hal/spm_api.h Normal file
View File

@ -0,0 +1,103 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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_API_H__
#define __SPM_API_H__
/** @addtogroup SPM
* The Secure Partition Manager (SPM) is responsible for isolating software in
* partitions, managing the execution of software within partitions and
* providing IPC between partitions.
* @{
*/
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/** @addtogroup HAL-SPE
* The HAL functions for SPE.
* @{
*/
/* ------------------------------------ HAL-SPE API ------------------------- */
#if defined(COMPONENT_SPE)
/**
* Start running the NSPE.
*
* Secure Processing Environment (SPE) expected to boot first. Once all
* the initializations are done, Nonsecure Processing Environment (NSPE)
* 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
*
* Arm implements this function, which is 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.
*
* Wake up the peer processor waiting on the mailbox driver event.
*
* @note Implement the functions below with 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__

View File

@ -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 @@ MBED_NORETURN void mbed_rtos_start()
_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) {

View File

@ -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",
@ -3215,7 +3288,7 @@
"detect_code": ["9015"],
"release_versions": ["2", "5"],
"device_name" : "STM32F439VI",
"components": ["SD"],
"components_add": ["SD"],
"bootloader_supported": true,
"overrides": {
"network-default-interface-type": "CELLULAR"
@ -4677,7 +4750,7 @@
"inherits": ["ARM_IOTSS_Target"],
"core": "Cortex-M3",
"supported_toolchains": ["ARM", "GCC_ARM", "IAR"],
"components": ["SMSC9220"],
"components_add": ["SMSC9220"],
"extra_labels": ["ARM_SSG", "CM3DS_MPS2"],
"OUTPUT_EXT": "elf",
"macros": ["CMSDK_CM3DS"],
@ -7187,7 +7260,7 @@
"USTICKER"
],
"release_versions": ["5"],
"components": ["LAN91C111"],
"components_add": ["LAN91C111"],
"overrides": {
"network-default-interface-type": "ETHERNET"
}

View File

@ -31,7 +31,7 @@ sys.path.insert(0, ROOT)
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 @@ if __name__ == '__main__':
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,

View File

@ -35,7 +35,7 @@ from jinja2.environment import Environment
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 @@ def build_project(src_paths, build_path, target, toolchain_name,
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 @@ def build_project(src_paths, build_path, target, toolchain_name,
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 @@ def build_project(src_paths, build_path, target, toolchain_name,
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:

View File

@ -37,12 +37,19 @@ from ..utils import (json_file_to_dict, intelhex_offset, integer,
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 @@ RAM_OVERRIDES = set([
"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 @@ class ConfigParameter(object):
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 @@ class ConfigParameter(object):
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 @@ class Config(object):
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 @@ class Config(object):
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 @@ class Config(object):
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

View File

@ -50,6 +50,8 @@ from utils import argparse_filestring_type
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 @@ if __name__ == '__main__':
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")

View File

@ -527,3 +527,7 @@ class Resources(object):
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)])

View File

@ -27,6 +27,10 @@ ROOT = abspath(join(dirname(__file__), ".."))
##############################################################################
BUILD_DIR = abspath(join(ROOT, "BUILD"))
# Default directory for dual-core and v8 targets
# delivering secure binaries across builds
DELIVERY_DIR = abspath(join(ROOT, "DELIVERY"))
# ARM Compiler 5
ARM_PATH = ""

23
tools/spm/__init__.py Normal file
View File

@ -0,0 +1,23 @@
# Copyright (c) 2017-2018 ARM Limited
#
# 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.
from .generate_partition_code import \
generate_partitions_sources, generate_psa_setup
__all__ = [
'generate_partitions_sources',
'generate_psa_setup',
]

View File

@ -0,0 +1,774 @@
#!/usr/bin/python
# Copyright (c) 2017-2018 ARM Limited
#
# 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.
import fnmatch
import itertools
import json
import os
from os.path import join as path_join
from jinja2 import Environment, FileSystemLoader, StrictUndefined
from jsonschema import validate
__version__ = '1.0'
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,
undefined=StrictUndefined
)
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),
'script_ver': __version__
}
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,
'script_ver': __version__
}
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()

View File

@ -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"
}
}
}
}
}

View File

@ -0,0 +1,146 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
/***********************************************************************************************************************
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT.
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* Template Version 1.0
* Generated by tools/spm/generate_partition_code.py Version {{script_ver}}
**********************************************************************************************************************/
#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 #}

View File

@ -0,0 +1,72 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
/***********************************************************************************************************************
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT.
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* Template Version 1.0
* Generated by tools/spm/generate_partition_code.py Version {{script_ver}}
**********************************************************************************************************************/
#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 %}
{% 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 #}

View File

@ -0,0 +1,153 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
/***********************************************************************************************************************
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT.
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* Template Version 1.0
* Generated by tools/spm/generate_partition_code.py Version {{script_ver}}
**********************************************************************************************************************/
#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 #}

View File

@ -0,0 +1,34 @@
/* Copyright (c) 2017-2018 ARM Limited
*
* 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.
*/
/***********************************************************************************************************************
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT.
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* Template Version 1.0
* Generated by tools/spm/generate_partition_code.py Version {{script_ver}}
**********************************************************************************************************************/
#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 #}

View File

@ -166,8 +166,12 @@ class Target(namedtuple("Target", "name json_data resolution_order resolution_or
@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 @@ class Target(namedtuple("Target", "name json_data resolution_order resolution_or
'target.' % k)
else:
targets[k] = v
targets[k]["_from_file"] = extra_target
return targets
@ -331,6 +336,14 @@ class Target(namedtuple("Target", "name json_data resolution_order resolution_or
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

View File

@ -43,7 +43,8 @@ from tools.utils import argparse_filestring_type, argparse_lowercase_type, argpa
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 __name__ == '__main__':
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 __name__ == '__main__':
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 @@ if __name__ == '__main__':
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:

View File

@ -135,8 +135,12 @@ class BuildApiTests(unittest.TestCase):
app_config = "app_config"
mock_exists.return_value = False
mock_prepare_toolchain().link_program.return_value = 1, 2
mock_prepare_toolchain().config = namedtuple(
"Config", "has_regions name lib_config_data")(None, None, {})
mock_prepare_toolchain().config = MagicMock(
has_regions=None,
name=None,
lib_config_data=None,
)
mock_prepare_toolchain().config.deliver_into.return_value = (None, None)
build_project(self.src_paths, self.build_path, self.target,
self.toolchain_name, app_config=app_config, notify=notify)
@ -165,8 +169,12 @@ class BuildApiTests(unittest.TestCase):
mock_exists.return_value = False
# Needed for the unpacking of the returned value
mock_prepare_toolchain().link_program.return_value = 1, 2
mock_prepare_toolchain().config = namedtuple(
"Config", "has_regions name lib_config_data")(None, None, {})
mock_prepare_toolchain().config = MagicMock(
has_regions=None,
name=None,
lib_config_data=None,
)
mock_prepare_toolchain().config.deliver_into.return_value = (None, None)
build_project(self.src_paths, self.build_path, self.target,
self.toolchain_name, notify=notify)

View File

732
tools/test/spm/test_data.py Normal file
View File

@ -0,0 +1,732 @@
# Copyright (c) 2017-2018 ARM Limited
#
# 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.
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"
}
]

View File

@ -0,0 +1,764 @@
# Copyright (c) 2017-2018 ARM Limited
#
# 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.
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']

View File

@ -2229,7 +2229,7 @@ def build_tests(tests, base_source_paths, build_path, target, toolchain_name,
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 @@ def build_tests(tests, base_source_paths, build_path, target, toolchain_name,
'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))

Some files were not shown because too many files have changed in this diff Show More