Merge pull request #9653 from kfnta/tfm_src_integ

TF-M sources integration to Mbed-OS
pull/9774/head
Cruz Monrreal 2019-02-19 15:28:44 -06:00 committed by GitHub
commit 4c04771c5d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
109 changed files with 9608 additions and 277 deletions

View File

@ -22,4 +22,5 @@ TESTS/mbed_hal/trng/pithy
targets
components/802.15.4_RF
components/wifi
components/TARGET_PSA/TARGET_TFM
tools

View File

@ -323,6 +323,8 @@ matrix:
- env:
- NAME=psa-autogen
script:
# Run SPM code generator and check that changes are not needed
- python tools/spm/generate_partition_code.py
# Run SPM code generators and check that changes are not needed
- python tools/psa/generate_mbed_spm_partition_code.py
- git diff --exit-code
- python tools/psa/generate_tfm_partition_code.py
- git diff --exit-code

View File

@ -5,6 +5,7 @@ Folders containing files under different permissive license than Apache 2.0 are
- [cmsis](./cmsis) - MIT, BSD-3-Clause
- [components/802.15.4_RF/mcr20a-rf-driver](./components/802.15.4_RF/mcr20a-rf-driver) - BSD-3-Clause
- [components/TARGET_PSA/TARGET_TFM](./components/TARGET_PSA/TARGET_TFM) - BSD-3-Clause
- [features/cryptocell/FEATURE_CRYPTOCELL310](./features/cryptocell/FEATURE_CRYPTOCELL310) - ARM Object Code and Header Files License
- [features/FEATURE_BOOTLOADER](./features/FEATURE_BOOTLOADER) - PBL
- [features/FEATURE_BLE/targets](./features/FEATURE_BLE/targets) - BSD-style, PBL, MIT-style

View File

@ -19,6 +19,10 @@
#error [NOT_SUPPORTED] SPM tests can run only on SPM-enabled targets
#endif // COMPONENT_PSA_SRV_IPC
#ifndef TARGET_MBED_SPM
#error [NOT_SUPPORTED] SPM tests currently only run on MBED_SPM targets
#endif // TARGET_MBED_SPM
#include "mbed.h"
#include "greentea-client/test_env.h"
#include "unity.h"

View File

@ -19,6 +19,10 @@
#error [NOT_SUPPORTED] SPM tests can run only on SPM-enabled targets
#endif // COMPONENT_PSA_SRV_IPC
#ifndef TARGET_MBED_SPM
#error [NOT_SUPPORTED] SPM tests currently only run on MBED_SPM targets
#endif // TARGET_MBED_SPM
#include "mbed.h"
#include "greentea-client/test_env.h"
#include "unity.h"

View File

@ -19,6 +19,11 @@
#ifndef COMPONENT_PSA_SRV_IPC
#error [NOT_SUPPORTED] SPM tests can run only on SPM-enabled targets
#endif // COMPONENT_PSA_SRV_IPC
#ifndef TARGET_MBED_SPM
#error [NOT_SUPPORTED] SPM tests currently only run on MBED_SPM targets
#endif // TARGET_MBED_SPM
/* -------------------------------------- Includes ----------------------------------- */
#include "greentea-client/test_env.h"

View File

@ -0,0 +1,87 @@
/*
* Copyright (c) 2017-2018, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include <stdint.h>
#include <stdbool.h>
#include "cmsis.h"
#include "rtx_os.h"
#include "cmsis_os2.h"
#include "tfm_api.h"
#include "tfm_ns_lock.h"
/**
* \brief struct ns_lock_state type
*/
struct ns_lock_state
{
bool init;
osMutexId_t id;
};
/**
* \brief ns_lock status
*/
static struct ns_lock_state ns_lock = {.init=false, .id=NULL};
/**
* \brief Mutex properties, NS lock
*/
static osRtxMutex_t ns_lock_cb = { 0 };
static const osMutexAttr_t ns_lock_attrib = {
.name = "ns_lock",
.attr_bits = osMutexPrioInherit,
.cb_mem = &ns_lock_cb,
.cb_size = sizeof(ns_lock_cb)
};
/**
* \brief NS world, NS lock based dispatcher
*/
uint32_t tfm_ns_lock_dispatch(veneer_fn fn,
uint32_t arg0, uint32_t arg1,
uint32_t arg2, uint32_t arg3)
{
uint32_t result;
/* Check the NS lock has been initialized */
if (ns_lock.init == false) {
return TFM_ERROR_GENERIC;
}
/* TFM request protected by NS lock */
osMutexAcquire(ns_lock.id,osWaitForever);
result = fn(arg0, arg1, arg2, arg3);
osMutexRelease(ns_lock.id);
return result;
}
/**
* \brief NS world, Init NS lock
*/
uint32_t tfm_ns_lock_init()
{
if (ns_lock.init == false) {
ns_lock.id = osMutexNew(&ns_lock_attrib);
ns_lock.init = true;
return TFM_SUCCESS;
}
else {
return TFM_ERROR_GENERIC;
}
}
bool tfm_ns_lock_get_init_state()
{
return ns_lock.init;
}

View File

@ -0,0 +1,75 @@
/*
* Copyright (c) 2018, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include "interface/include/psa_client.h"
#include "tfm_ns_lock.h"
#include "tfm_api.h"
/**** API functions ****/
uint32_t psa_framework_version(void)
{
return tfm_ns_lock_dispatch((veneer_fn)tfm_psa_framework_version_veneer,
0,
0,
0,
0);
}
uint32_t psa_version(uint32_t sid)
{
return tfm_ns_lock_dispatch((veneer_fn)tfm_psa_version_veneer,
sid,
0,
0,
0);
}
psa_handle_t psa_connect(uint32_t sid, uint32_t minor_version)
{
return tfm_ns_lock_dispatch((veneer_fn)tfm_psa_connect_veneer,
sid,
minor_version,
0,
0);
}
psa_status_t psa_call(psa_handle_t handle,
const psa_invec *in_vec,
size_t in_len,
psa_outvec *out_vec,
size_t out_len)
{
/* FixMe: sanity check can be added to offload some NS thread checks from
* TFM secure API
*/
/* Due to v8M restrictions, TF-M NS API needs to add another layer of
* serialization in order for NS to pass arguments to S
*/
psa_invec in_vecs, out_vecs;
in_vecs.base = in_vec;
in_vecs.len = in_len;
out_vecs.base = out_vec;
out_vecs.len = out_len;
return tfm_ns_lock_dispatch((veneer_fn)tfm_psa_call_veneer,
(uint32_t)handle,
(uint32_t)&in_vecs,
(uint32_t)&out_vecs,
0);
}
void psa_close(psa_handle_t handle)
{
tfm_ns_lock_dispatch((veneer_fn)tfm_psa_close_veneer,
(uint32_t)handle,
0,
0,
0);
}

View File

@ -0,0 +1,37 @@
/* Copyright (c) 2017-2019 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.
*/
/*********** WARNING: This is an auto-generated file. Do not edit! ***********/
#ifndef __TFM_PARTITION_DEFS_INC__
#define __TFM_PARTITION_DEFS_INC__
#ifdef TFM_PSA_API
#define ITS_ID (TFM_SP_BASE + 0)
#endif
#ifdef TFM_PSA_API
#define PLATFORM_ID (TFM_SP_BASE + 1)
#endif
#ifdef TFM_PSA_API
#define CRYPTO_SRV_ID (TFM_SP_BASE + 2)
#endif
#define TFM_MAX_USER_PARTITIONS (3)
#endif /* __TFM_PARTITION_DEFS_INC__ */

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
/*********** WARNING: This is an auto-generated file. Do not edit! ***********/
#ifndef __TFM_PARTITION_LIST_INC__
#define __TFM_PARTITION_LIST_INC__
#ifdef TFM_PSA_API
/******** ITS ********/
PARTITION_DECLARE(ITS, 0
| SPM_PART_FLAG_IPC
, "APPLICATION-ROT", 10, NORMAL, 2048);
PARTITION_ADD_INIT_FUNC(ITS, its_entry);
#endif /* TFM_PSA_API */
#ifdef TFM_PSA_API
/******** PLATFORM ********/
PARTITION_DECLARE(PLATFORM, 0
| SPM_PART_FLAG_IPC
, "APPLICATION-ROT", 8, NORMAL, 1024);
PARTITION_ADD_INIT_FUNC(PLATFORM, platform_partition_entry);
#endif /* TFM_PSA_API */
#ifdef TFM_PSA_API
/******** CRYPTO_SRV ********/
PARTITION_DECLARE(CRYPTO_SRV, 0
| SPM_PART_FLAG_IPC
, "APPLICATION-ROT", 35, NORMAL, 16384);
PARTITION_ADD_INIT_FUNC(CRYPTO_SRV, crypto_main);
#endif /* TFM_PSA_API */
#endif /* __TFM_PARTITION_LIST_INC__ */

View File

@ -0,0 +1,44 @@
/*
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
/*********** WARNING: This is an auto-generated file. Do not edit! ***********/
#ifndef __TFM_SERVICE_LIST_INC__
#define __TFM_SERVICE_LIST_INC__
#ifdef TFM_PSA_API
/******** ITS ********/
{"PSA_ITS_GET", ITS_ID, PSA_ITS_GET_MSK, 0x00011A00, true, 1, TFM_VERSION_POLICY_RELAXED},
{"PSA_ITS_SET", ITS_ID, PSA_ITS_SET_MSK, 0x00011A01, true, 1, TFM_VERSION_POLICY_RELAXED},
{"PSA_ITS_INFO", ITS_ID, PSA_ITS_INFO_MSK, 0x00011A02, true, 1, TFM_VERSION_POLICY_RELAXED},
{"PSA_ITS_REMOVE", ITS_ID, PSA_ITS_REMOVE_MSK, 0x00011A03, true, 1, TFM_VERSION_POLICY_RELAXED},
{"PSA_ITS_RESET", ITS_ID, PSA_ITS_RESET_MSK, 0x00011A04, false, 1, TFM_VERSION_POLICY_RELAXED},
#endif /* TFM_PSA_API */
#ifdef TFM_PSA_API
/******** PLATFORM ********/
{"PSA_PLATFORM_LC_GET", PLATFORM_ID, PSA_PLATFORM_LC_GET_MSK, 0x00011000, true, 1, TFM_VERSION_POLICY_RELAXED},
{"PSA_PLATFORM_LC_SET", PLATFORM_ID, PSA_PLATFORM_LC_SET_MSK, 0x00011001, true, 1, TFM_VERSION_POLICY_RELAXED},
{"PSA_PLATFORM_SYSTEM_RESET", PLATFORM_ID, PSA_PLATFORM_SYSTEM_RESET_MSK, 0x00011002, true, 1, TFM_VERSION_POLICY_RELAXED},
#endif /* TFM_PSA_API */
#ifdef TFM_PSA_API
/******** CRYPTO_SRV ********/
{"PSA_CRYPTO_INIT_ID", CRYPTO_SRV_ID, PSA_CRYPTO_INIT, 0x00000F00, true, 1, TFM_VERSION_POLICY_STRICT},
{"PSA_MAC_ID", CRYPTO_SRV_ID, PSA_MAC, 0x00000F01, true, 1, TFM_VERSION_POLICY_STRICT},
{"PSA_HASH_ID", CRYPTO_SRV_ID, PSA_HASH, 0x00000F02, true, 1, TFM_VERSION_POLICY_STRICT},
{"PSA_ASYMMETRIC_ID", CRYPTO_SRV_ID, PSA_ASYMMETRIC, 0x00000F03, true, 1, TFM_VERSION_POLICY_STRICT},
{"PSA_SYMMETRIC_ID", CRYPTO_SRV_ID, PSA_SYMMETRIC, 0x00000F04, true, 1, TFM_VERSION_POLICY_STRICT},
{"PSA_AEAD_ID", CRYPTO_SRV_ID, PSA_AEAD, 0x00000F05, true, 1, TFM_VERSION_POLICY_STRICT},
{"PSA_KEY_MNG_ID", CRYPTO_SRV_ID, PSA_KEY_MNG, 0x00000F06, true, 1, TFM_VERSION_POLICY_STRICT},
{"PSA_RNG_ID", CRYPTO_SRV_ID, PSA_RNG, 0x00000F07, true, 1, TFM_VERSION_POLICY_STRICT},
{"PSA_CRYPTO_FREE_ID", CRYPTO_SRV_ID, PSA_CRYPTO_FREE, 0x00000F08, true, 1, TFM_VERSION_POLICY_STRICT},
{"PSA_GENERATOR_ID", CRYPTO_SRV_ID, PSA_GENERATOR, 0x00000F09, true, 1, TFM_VERSION_POLICY_STRICT},
{"PSA_ENTROPY_ID", CRYPTO_SRV_ID, PSA_ENTROPY_INJECT, 0x00000F0A, true, 1, TFM_VERSION_POLICY_STRICT},
#endif /* TFM_PSA_API */
#endif /* __TFM_SERVICE_LIST_INC__ */

View File

@ -0,0 +1,52 @@
/*
* Copyright (c) 2018, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef __TFM_SPM_SIGNAL_DEFS_H__
#define __TFM_SPM_SIGNAL_DEFS_H__
#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 PSA_ITS_RESET_MSK_POS (8UL)
#define PSA_ITS_RESET_MSK (1UL << PSA_ITS_RESET_MSK_POS)
#define PSA_PLATFORM_LC_GET_MSK_POS (4UL)
#define PSA_PLATFORM_LC_GET_MSK (1UL << PSA_PLATFORM_LC_GET_MSK_POS)
#define PSA_PLATFORM_LC_SET_MSK_POS (5UL)
#define PSA_PLATFORM_LC_SET_MSK (1UL << PSA_PLATFORM_LC_SET_MSK_POS)
#define PSA_PLATFORM_SYSTEM_RESET_MSK_POS (6UL)
#define PSA_PLATFORM_SYSTEM_RESET_MSK (1UL << PSA_PLATFORM_SYSTEM_RESET_MSK_POS)
#define PSA_CRYPTO_INIT_POS (4UL)
#define PSA_CRYPTO_INIT (1UL << PSA_CRYPTO_INIT_POS)
#define PSA_MAC_POS (5UL)
#define PSA_MAC (1UL << PSA_MAC_POS)
#define PSA_HASH_POS (6UL)
#define PSA_HASH (1UL << PSA_HASH_POS)
#define PSA_ASYMMETRIC_POS (7UL)
#define PSA_ASYMMETRIC (1UL << PSA_ASYMMETRIC_POS)
#define PSA_SYMMETRIC_POS (8UL)
#define PSA_SYMMETRIC (1UL << PSA_SYMMETRIC_POS)
#define PSA_AEAD_POS (9UL)
#define PSA_AEAD (1UL << PSA_AEAD_POS)
#define PSA_KEY_MNG_POS (10UL)
#define PSA_KEY_MNG (1UL << PSA_KEY_MNG_POS)
#define PSA_RNG_POS (11UL)
#define PSA_RNG (1UL << PSA_RNG_POS)
#define PSA_CRYPTO_FREE_POS (12UL)
#define PSA_CRYPTO_FREE (1UL << PSA_CRYPTO_FREE_POS)
#define PSA_GENERATOR_POS (13UL)
#define PSA_GENERATOR (1UL << PSA_GENERATOR_POS)
#define PSA_ENTROPY_INJECT_POS (14UL)
#define PSA_ENTROPY_INJECT (1UL << PSA_ENTROPY_INJECT_POS)
#endif

View File

@ -0,0 +1,54 @@
/*
* Copyright (c) 2018, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef __BOOT_RECORD_H__
#define __BOOT_RECORD_H__
#include <stdint.h>
#include <stddef.h>
#include <limits.h>
#ifdef __cplusplus
extern "C" {
#endif
/*!
* \enum shared_data_err_t
*
* \brief Return values for adding data entry to shared memory area
*/
enum shared_memory_err_t {
SHARED_MEMORY_OK = 0,
SHARED_MEMORY_OVERFLOW = 1,
SHARED_MEMORY_OVERWRITE = 2,
/* This is used to force the maximum size */
TLV_TYPE_MAX = INT_MAX
};
/*!
* \brief Add a data item to the shared data area between bootloader and
* runtime SW
*
* \param[in] major_type TLV major type, identify consumer
* \param[in] minor_type TLV minor type, identify TLV type
* \param[in] size length of added data
* \param[in] data pointer to data
*
* \return Returns error code as specified in \ref shared_memory_err_t
*/
enum shared_memory_err_t
boot_add_data_to_shared_area(uint8_t major_type,
uint8_t minor_type,
size_t size,
const uint8_t *data);
#ifdef __cplusplus
}
#endif
#endif /* __BOOT_RECORD_H__ */

View File

@ -0,0 +1,86 @@
/*
* Copyright (c) 2018, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef __TFM_BOOT_STATUS_H__
#define __TFM_BOOT_STATUS_H__
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Major numbers to identify the consumer of shared data in runtime SW */
#define TLV_MAJOR_CORE 0x0
#define TLV_MAJOR_IAS 0x1
/* PSA Root of Trust */
#define TLV_MINOR_IAS_PRoT_SHA256 0x00
#define TLV_MINOR_IAS_PRoT_SW_VERSION 0x01
#define TLV_MINOR_IAS_PRoT_EPOCH 0x02
/* Application Root of Trust */
#define TLV_MINOR_IAS_ARoT_SHA256 0x03
#define TLV_MINOR_IAS_ARoT_SW_VERSION 0x04
#define TLV_MINOR_IAS_ARoT_EPOCH 0x05
/* Non-secure processing environment: single non-secure image */
#define TLV_MINOR_IAS_NSPE_SHA256 0x06
#define TLV_MINOR_IAS_NSPE_SW_VERSION 0x07
#define TLV_MINOR_IAS_NSPE_EPOCH 0x08
/* ARoT + PRoT: single secure image */
#define TLV_MINOR_IAS_S_SHA256 0x09
#define TLV_MINOR_IAS_S_SW_VERSION 0x0a
#define TLV_MINOR_IAS_S_EPOCH 0x0b
/* S + NS: combined secure and non-secure image */
#define TLV_MINOR_IAS_S_NS_SHA256 0x0c
#define TLV_MINOR_IAS_S_NS_SW_VERSION 0x0d
#define TLV_MINOR_IAS_S_NS_EPOCH 0x0e
#define SHARED_DATA_TLV_INFO_MAGIC 0x2016
/**
* Shared data TLV header. All fields in little endian.
*
* ---------------------------
* | tlv_magic | tlv_tot_len |
* ---------------------------
*/
struct shared_data_tlv_header {
uint16_t tlv_magic;
uint16_t tlv_tot_len; /* size of whole TLV area (including this header) */
};
#define SHARED_DATA_HEADER_SIZE sizeof(struct shared_data_tlv_header)
/**
* Shared data TLV entry header format. All fields in little endian.
*
* ---------------------------------------------
* | tlv_major_type | tlv_minor_type | tlv_len |
* ---------------------------------------------
* | Raw data |
* ---------------------------------------------
*/
struct shared_data_tlv_entry {
uint8_t tlv_major_type;
uint8_t tlv_minor_type;
uint16_t tlv_len; /* size of single TLV entry (including this header). */
};
#define SHARED_DATA_ENTRY_HEADER_SIZE sizeof(struct shared_data_tlv_entry)
#define SHARED_DATA_ENTRY_SIZE(size) (size + SHARED_DATA_ENTRY_HEADER_SIZE)
#ifdef __cplusplus
}
#endif
#endif /* __TFM_BOOT_STATUS_H__ */

View File

@ -0,0 +1,21 @@
{
"name": "tfm",
"config": {
"handle_pool_size": {
"help": "maximum number of handles that can be opened at the same time",
"macro_name": "TFM_CONN_HANDLE_MAX_NUM",
"value": 10
},
"rot_pool_size": {
"help": "maximum number of RoT services allowed",
"macro_name": "TFM_SPM_MAX_ROT_SERV_NUM",
"value": 20
},
"message_pool_size": {
"help": "maximum number of RoT services allowed",
"macro_name": "TFM_MSG_QUEUE_MAX_MSG_NUM",
"value": 10
}
}
}

View File

@ -0,0 +1,69 @@
/*
* Copyright (c) 2013-2016 ARM Limited. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the License); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* $Date: 2. Jan 2014
* $Revision: V2.00
*
* Project: Common Driver definitions
*/
/* History:
* Version 2.00
* Changed prefix ARM_DRV -> ARM_DRIVER
* Added General return codes definitions
* Version 1.10
* Namespace prefix ARM_ added
* Version 1.00
* Initial release
*/
#ifndef __DRIVER_COMMON_H
#define __DRIVER_COMMON_H
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#define ARM_DRIVER_VERSION_MAJOR_MINOR(major,minor) (((major) << 8) | (minor))
/**
\brief Driver Version
*/
typedef struct _ARM_DRIVER_VERSION {
uint16_t api; ///< API version
uint16_t drv; ///< Driver version
} ARM_DRIVER_VERSION;
/* General return codes */
#define ARM_DRIVER_OK 0 ///< Operation succeeded
#define ARM_DRIVER_ERROR -1 ///< Unspecified error
#define ARM_DRIVER_ERROR_BUSY -2 ///< Driver is busy
#define ARM_DRIVER_ERROR_TIMEOUT -3 ///< Timeout occurred
#define ARM_DRIVER_ERROR_UNSUPPORTED -4 ///< Operation not supported
#define ARM_DRIVER_ERROR_PARAMETER -5 ///< Parameter error
#define ARM_DRIVER_ERROR_SPECIFIC -6 ///< Start of driver specific errors
/**
\brief General power states
*/
typedef enum _ARM_POWER_STATE {
ARM_POWER_OFF, ///< Power off: no operation possible
ARM_POWER_LOW, ///< Low Power mode: retain state, detect and signal wake-up events
ARM_POWER_FULL ///< Power on: full operation at maximum performance
} ARM_POWER_STATE;
#endif /* __DRIVER_COMMON_H */

View File

@ -0,0 +1,153 @@
/*
* Copyright (c) 2016-2018 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __DRIVER_MPC_H
#define __DRIVER_MPC_H
#include "Driver_Common.h"
/* API version */
#define ARM_MPC_API_VERSION ARM_DRIVER_VERSION_MAJOR_MINOR(1,0)
/* Error code returned by the driver functions */
#define ARM_MPC_ERR_NOT_INIT (ARM_DRIVER_ERROR_SPECIFIC - 1) ///< MPC not initialized */
#define ARM_MPC_ERR_NOT_IN_RANGE (ARM_DRIVER_ERROR_SPECIFIC - 2) ///< Address does not belong to a range controlled by the MPC */
#define ARM_MPC_ERR_NOT_ALIGNED (ARM_DRIVER_ERROR_SPECIFIC - 3) ///< Address is not aligned on the block size of this MPC */
#define ARM_MPC_ERR_INVALID_RANGE (ARM_DRIVER_ERROR_SPECIFIC - 4) ///< The given address range to configure is invalid
#define ARM_MPC_ERR_RANGE_SEC_ATTR_NON_COMPATIBLE (ARM_DRIVER_ERROR_SPECIFIC - 4) ///< The given range cannot be accessed with the wanted security attributes */
#define ARM_MPC_ERR_UNSPECIFIED (ARM_DRIVER_ERROR_SPECIFIC - 5) ///< Unspecified error */
/* Security attribute used in various place of the API */
typedef enum _ARM_MPC_SEC_ATTR {
ARM_MPC_ATTR_SECURE, ///< Secure attribute
ARM_MPC_ATTR_NONSECURE, ///< Non-secure attribute
/* Used when getting the configuration of a memory range and some blocks are
* secure whereas some other are non secure */
ARM_MPC_ATTR_MIXED, ///< Mixed attribute
} ARM_MPC_SEC_ATTR;
/* Function documentation */
/**
\fn ARM_DRIVER_VERSION ARM_MPC_GetVersion (void)
\brief Get driver version.
\return \ref ARM_DRIVER_VERSION
\fn int32_t ARM_MPC_Initialize (void)
\brief Initialize MPC Interface.
\return Returns error code.
\fn int32_t ARM_MPC_Uninitialize (void)
\brief De-initialize MPC Interface. The controlled memory region
should not be accessed after a call to this function, as
it is allowed to configure everything to be secure (to
prevent information leak for example).
\return Returns error code.
\fn int32_t ARM_MPC_GetBlockSize (uint32_t* blk_size)
\brief Get the block size of the MPC. All regions must be aligned
on this block size (base address and limit+1 address).
\param[out] blk_size: The block size in bytes.
\return Returns error code.
\fn int32_t ARM_MPC_GetCtrlConfig (uint32_t* ctrl_val)
\brief Get some information on how the MPC IP is configured.
\param[out] ctrl_val: MPC control configuration
\return Returns error code.
\fn int32_t ARM_MPC_SetCtrlConfig (uint32_t ctrl)
\brief Set new control configuration for the MPC IP.
\param[in] ctrl: New control configuration.
\return Returns error code.
\fn int32_t ARM_MPC_ConfigRegion (uintptr_t base,
uintptr_t limit,
ARM_MPC_SEC_ATTR attr)
\brief Configure a memory region (base and limit included).
Both base and limit addresses must belong to the same
memory range, and this range must be managed by this MPC.
Also, some ranges are only allowed to be configured as
secure/non-secure, because of hardware requirements
(security aliases), and only a relevant security attribute
is therefore allowed for such ranges.
\param[in] base: Base address of the region to configure. This
bound is included in the configured region.
This must be aligned on the block size of this MPC.
\param[in] limit: Limit address of the region to configure. This
bound is included in the configured region.
Limit+1 must be aligned on the block size of this MPC.
\param[in] attr: Wanted security attribute of the region.
\return Returns error code.
\fn int32_t ARM_MPC_GetRegionConfig (uintptr_t base,
uintptr_t limit,
ARM_MPC_SEC_ATTR *attr)
\brief Gets a memory region (base and limit included).
\param[in] base: Base address of the region to poll. This
bound is included. It does not need to be aligned
in any way.
\param[in] limit: Limit address of the region to poll. This
bound is included. (limit+1) does not need to be aligned
in any way.
\param[out] attr: Security attribute of the region.
If the region has mixed secure/non-secure,
a special value is returned (\ref ARM_MPC_SEC_ATTR).
In case base and limit+1 addresses are not aligned on
the block size, the enclosing region with base and
limit+1 aligned on block size will be queried.
In case of early termination of the function (error), the
security attribute will be set to ARM_MPC_ATTR_MIXED.
\return Returns error code.
\fn int32_t ARM_MPC_EnableInterrupt (void)
\brief Enable MPC interrupt.
\return Returns error code.
\fn void ARM_MPC_DisableInterrupt (void)
\brief Disable MPC interrupt.
\fn void ARM_MPC_ClearInterrupt (void)
\brief Clear MPC interrupt.
\fn uint32_t ARM_MPC_InterruptState (void)
\brief MPC interrupt state.
\return Returns 1 if the interrupt is active, 0 otherwise.
\fn int32_t ARM_MPC_LockDown (void)
\brief Lock down the MPC configuration.
\return Returns error code.
*/
/**
* \brief Access structure of the MPC Driver.
*/
typedef struct _ARM_DRIVER_MPC {
ARM_DRIVER_VERSION (*GetVersion) (void); ///< Pointer to \ref ARM_MPC_GetVersion : Get driver version.
int32_t (*Initialize) (void); ///< Pointer to \ref ARM_MPC_Initialize : Initialize the MPC Interface.
int32_t (*Uninitialize) (void); ///< Pointer to \ref ARM_MPC_Uninitialize : De-initialize the MPC Interface.
int32_t (*GetBlockSize) (uint32_t* blk_size); ///< Pointer to \ref ARM_MPC_GetBlockSize : Get MPC block size
int32_t (*GetCtrlConfig) (uint32_t* ctrl_val); ///< Pointer to \ref ARM_MPC_GetCtrlConfig : Get the MPC control configuration flags.
int32_t (*SetCtrlConfig) (uint32_t ctrl); ///< Pointer to \ref ARM_MPC_SetCtrlConfig : Set the MPC control configuration flags.
int32_t (*ConfigRegion) (uintptr_t base, uintptr_t limit, ARM_MPC_SEC_ATTR attr); ///< Pointer to \ref ARM_MPC_ConfigRegion : Configure a region using the driver for the specific MPC.
int32_t (*GetRegionConfig) (uintptr_t base, uintptr_t limit, ARM_MPC_SEC_ATTR *attr); ///< Pointer to \ref ARM_MPC_GetRegionConfig : Get the configuration of a specific region on this MPC.
int32_t (*EnableInterrupt) (void); ///< Pointer to \ref ARM_MPC_EnableInterrupt : Enable MPC interrupt.
void (*DisableInterrupt) (void); ///< Pointer to \ref ARM_MPC_DisableInterrupt : Disable MPC interrupt.
void (*ClearInterrupt) (void); ///< Pointer to \ref ARM_MPC_ClearInterrupt : Clear MPC interrupt.
uint32_t (*InterruptState) (void); ///< Pointer to \ref ARM_MPC_InterruptState : MPC interrupt State.
int32_t (*LockDown) (void); ///< Pointer to \ref ARM_MPC_LockDown : Lock down the MPC configuration.
} const ARM_DRIVER_MPC;
#endif /* __DRIVER_MPC_H */

View File

@ -0,0 +1,116 @@
/*
* Copyright (c) 2016 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __CMSIS_PPC_DRV_H__
#define __CMSIS_PPC_DRV_H__
#include "Driver_Common.h"
/* API version */
#define ARM_PPC_API_VERSION ARM_DRIVER_VERSION_MAJOR_MINOR(1,0)
/* Security attribute used to configure the peripheral */
typedef enum _ARM_PPC_SecAttr {
ARM_PPC_SECURE_ONLY, ///< Secure access
ARM_PPC_NONSECURE_ONLY, ///< Non-secure access
} ARM_PPC_SecAttr;
/* Privilege attribute used to configure the peripheral */
typedef enum _ARM_PPC_PrivAttr {
ARM_PPC_PRIV_AND_NONPRIV, ///< Privilege and non-privilege access
ARM_PPC_PRIV_ONLY, ///< Privilege only access
} ARM_PPC_PrivAttr;
/* Function documentation */
/**
\fn ARM_DRIVER_VERSION ARM_PPC_GetVersion (void)
\brief Get driver version.
\return \ref ARM_DRIVER_VERSION
\fn int32_t ARM_PPC_Initialize (void)
\brief Initialize PPC Interface.
\return Returns ARM error code.
\fn int32_t ARM_PPC_Uninitialize (void)
\brief De-initialize MPC Interface.
\return Returns ARM error code.
\fn int32_t ARM_PPC_ConfigPeriph (uint8_t periph,
ARM_PPC_SecAttr sec_attr,
ARM_PPC_PrivAttr priv_attr)
\brief Configures a peripheral controlled by the given PPC.
\param[in] periph: Peripheral position in SPCTRL and NSPCTRL registers.
\param[in] sec_attr: Secure attribute value.
\param[in] priv_attr: Privilege attrivute value.
Secure Privilege Control Block ( SPCTRL )
Non-Secure Privilege Control Block ( NSPCTRL )
\return Returns ARM error code.
\fn int32_t ARM_PPC_IsPeriphSecure (uint8_t periph)
\brief Check if the peripheral is configured to be secure.
\param[in] periph: Peripheral position in SPCTRL and NSPCTRL registers.
Secure Privilege Control Block ( SPCTRL )
Non-Secure Privilege Control Block ( NSPCTRL )
\return Returns 1 if the peripheral is configured as secure,
0 for non-secure.
\fn uint32_t ARM_PPC_IsPeriphPrivOnly (uint8_t periph)
\brief Check if the peripheral is configured to be privilege only.
\param[in] periph: Peripheral position in SPCTRL and NSPCTRL registers.
Secure Privilege Control Block ( SPCTRL )
Non-Secure Privilege Control Block ( NSPCTRL )
\return Returns 1 if the peripheral is configured as privilege access
only, 0 for privilege and unprivilege access mode.
\fn int32_t ARM_PPC_EnableInterrupt (void)
\brief Enable PPC interrupt.
\return Returns ARM error code.
\fn void ARM_PPC_DisableInterrupt (void)
\brief Disable PPC interrupt.
\fn void ARM_PPC_ClearInterrupt (void)
\brief Clear PPC interrupt.
\fn int32_t ARM_PPC_InterruptState (void)
\brief PPC interrupt state.
\return Returns 1 if the interrupt is active, 0 otherwise.
*/
/**
* \brief Access structure of the MPC Driver.
*/
typedef struct _ARM_DRIVER_PPC {
ARM_DRIVER_VERSION (*GetVersion) (void); ///< Pointer to \ref ARM_PPC_GetVersion : Get driver version.
int32_t (*Initialize) (void); ///< Pointer to \ref ARM_PPC_Initialize : Initialize the PPC Interface.
int32_t (*Uninitialize) (void); ///< Pointer to \ref ARM_PPC_Uninitialize : De-initialize the PPC Interface.
int32_t (*ConfigPeriph) (uint8_t periph, ARM_PPC_SecAttr sec_attr, ARM_PPC_PrivAttr priv_attr); ///< Pointer to \ref ARM_PPC_ConfigPeriph : Configure a peripheral controlled by the PPC.
uint32_t (*IsPeriphSecure) (uint8_t periph); ///< Pointer to \ref IsPeriphSecure : Check if the peripheral is configured to be secure.
uint32_t (*IsPeriphPrivOnly) (uint8_t periph); ///< Pointer to \ref IsPeriphPrivOnly : Check if the peripheral is configured to be privilege only.
int32_t (*EnableInterrupt) (void); ///< Pointer to \ref ARM_PPC_EnableInterrupt : Enable PPC interrupt.
void (*DisableInterrupt) (void); ///< Pointer to \ref ARM_PPC_DisableInterrupt : Disable PPC interrupt.
void (*ClearInterrupt) (void); ///< Pointer to \ref ARM_PPC_ClearInterrupt : Clear PPC interrupt.
uint32_t (*InterruptState) (void); ///< Pointer to \ref ARM_PPC_InterruptState : PPC interrupt State.
} const ARM_DRIVER_PPC;
#endif /* __CMSIS_PPC_DRV_H__ */

View File

@ -0,0 +1,56 @@
/*
* Copyright (c) 2018, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef __TFM_PLAT_BOOT_SEED_H__
#define __TFM_PLAT_BOOT_SEED_H__
/**
* \file tfm_plat_boot_seed.h
*
* Boot seed is used by a validating entity to ensure multiple reports were
* generated in the same boot session. Boot seed is a random number, generated
* only once during a boot cycle and its value is constant in the same cycle.
* Size recommendation is 256-bit to meet the statistically improbable property.
* Boot seed can be generated by secure boot loader an included to the measured
* boot state or can be generated by PRoT SW.
*/
/**
* \note The interfaces defined in this file must be implemented for each
* SoC.
*/
#include <stdint.h>
#include "tfm_plat_defs.h"
#ifdef __cplusplus
extern "C" {
#endif
/*!
* \def BOOT_SEED_SIZE
*
* \brief Size of boot seed in bytes.
*/
#define BOOT_SEED_SIZE (32u)
/**
* \brief Gets the boot seed, which is a constant random number during a boot
* cycle.
*
* \param[in] size The required size of boot seed in bytes
* \param[out] buf Pointer to the buffer to store boot seed
*
* \return TFM_PLAT_ERR_SUCCESS if the value is generated correctly. Otherwise,
* it returns TFM_PLAT_ERR_SYSTEM_ERR.
*/
enum tfm_plat_err_t tfm_plat_get_boot_seed(uint32_t size, uint8_t *buf);
#ifdef __cplusplus
}
#endif
#endif /* __TFM_PLAT_BOOT_SEED_H__ */

View File

@ -0,0 +1,38 @@
/*
* Copyright (c) 2017-2019, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef __TFM_PLAT_DEFS_H__
#define __TFM_PLAT_DEFS_H__
/**
* \note The interfaces defined in this file must be implemented for each
* target.
*/
#include <stdint.h>
#include <limits.h>
enum tfm_plat_err_t {
TFM_PLAT_ERR_SUCCESS = 0,
TFM_PLAT_ERR_SYSTEM_ERR,
TFM_PLAT_ERR_MAX_VALUE,
/* Following entry is only to ensure the error code of int size */
TFM_PLAT_ERR_FORCE_INT_SIZE = INT_MAX
};
/*!
* \def TFM_LINK_SET_OBJECT_IN_PARTITION_SECTION(TFM_PARTITION_NAME)
*
* \brief This macro provides a mechanism to place a function code in a specific
* secure partition at linker time in TF-M Level 3.
*
* \param[in] TFM_PARTITION_NAME TF-M partition name assigned in the manifest
* file "name" field.
*/
#define TFM_LINK_SET_OBJECT_IN_PARTITION_SECTION(TFM_PARTITION_NAME) \
__attribute__((section(TFM_PARTITION_NAME"_ATTR_FN")))
#endif /* __TFM_PLAT_DEFS_H__ */

View File

@ -0,0 +1,52 @@
/*
* Copyright (c) 2018, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef __TFM_PLAT_DEVICE_ID_H__
#define __TFM_PLAT_DEVICE_ID_H__
/**
* \file tfm_plat_device_id.h
* Provide the Universal Entity ID (UEID) of the device.
* It identifies the entire device or a submodule or subsystem. Must be
* universally and globally unique and immutable. Variable length with a
* maximum size of 33 bytes: 1 type byte and 256 bits.
*/
/**
* \note The interfaces defined in this file must be implemented for each
* SoC.
*/
#include <stdint.h>
#include "tfm_plat_defs.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* \def DEVICE_ID_MAX_SIZE
*
* \brief Maximum size of device ID in bytes
*/
#define DEVICE_ID_MAX_SIZE (33u)
/**
* \brief Get the UEID of the device.
*
* \param[in] size The size of the buffer in bytes to store the UEID
* \param[out] buf Pointer to the buffer to store the UEID
*
* \return The size of device ID in bytes, if buffer big enough to store the
* ID, otherwise -1.
*/
int32_t tfm_plat_get_device_id(uint32_t size, uint8_t *buf);
#ifdef __cplusplus
}
#endif
#endif /* __TFM_PLAT_DEVICE_ID_H__ */

View File

@ -0,0 +1,190 @@
/*
* Copyright (c) 2018, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef __TFM_SPM_HAL_H__
#define __TFM_SPM_HAL_H__
#include <stdint.h>
#include "tfm_secure_api.h"
#include "spm_api.h"
/**
* \brief Holds peripheral specific data fields required to manage the
* peripherals isolation
*
* This structure has to be defined in the platform directory, and may have
* different definition for each platform. The structure should contain fields
* that describe the peripheral for the functions that are prototyped in this
* file and are responsible for configuring the isolation of the peripherals.
*
* Pointers to structures of this type are managed by the SPM, and passed to the
* necessary function on isolation request. The pointers are also defined by the
* platform in the header file tfm_peripherals_def.h. For details on this, see
* the documentation of that file.
*/
struct tfm_spm_partition_platform_data_t;
#if TFM_LVL != 1
/**
* \brief Holds SPM db fields that define the memory regions used by a
* partition.
*/
struct tfm_spm_partition_memory_data_t
{
uint32_t code_start; /*!< Start of the code memory of this partition. */
uint32_t code_limit; /*!< Address of the byte beyond the end of the code
* memory of this partition.
*/
uint32_t ro_start; /*!< Start of the read only memory of this
* partition.
*/
uint32_t ro_limit; /*!< Address of the byte beyond the end of the read
* only memory of this partition.
*/
uint32_t rw_start; /*!< Start of the data region of this partition. */
uint32_t rw_limit; /*!< Address of the byte beyond the end of the data
* region of this partition.
*/
uint32_t zi_start; /*!< Start of the zero initialised data region of
* this partition.
*/
uint32_t zi_limit; /*!< Address of the byte beyond the end of the zero
* initialised region of this partition.
*/
uint32_t stack_bottom; /*!< The bottom of the stack for the partition. */
uint32_t stack_top; /*!< The top of the stack for the partition. */
};
#endif
/**
* \brief This function initialises the HW used for isolation, and sets the
* default configuration for them.
*
* This function is called during TF-M core early startup, before DB init
*/
void tfm_spm_hal_init_isolation_hw(void);
/**
* \brief This function initialises the HW used for isolation, and sets the
* default configuration for them.
* This function is called during TF-M core early startup, after DB init
*/
void tfm_spm_hal_setup_isolation_hw(void);
/**
* \brief Configure peripherals for a partition based on the platfotm data from
* the DB
*
* This function is called during partition initialisation (before calling the
* init function for the partition)
*
* \param[in] platform_data The platform fields of the partition DB record to
* be used for configuration. Can be NULL.
*/
void tfm_spm_hal_configure_default_isolation(
const struct tfm_spm_partition_platform_data_t *platform_data);
/**
* \brief Configures the system debug properties.
* The default configuration of this function should disable secure debug
* when either DAUTH_NONE or DAUTH_NS_ONLY define is set. It is up to the
* platform owner to decide if secure debug can be turned on in their
* system, if DAUTH_FULL define is present.
* The DAUTH_CHIP_DEFAULT define should not be considered a safe default
* option unless explicitly noted by the chip vendor.
* The implementation has to expect that one of those defines is going to
* be set. Otherwise, a compile error needs to be triggered.
*/
void tfm_spm_hal_init_debug(void);
/**
* \brief Enables the fault handlers
*/
void enable_fault_handlers(void);
/**
* \brief Configures the system reset request properties
*/
void system_reset_cfg(void);
/**
* \brief Configures all external interrupts to target the
* NS state, apart for the ones associated to secure
* peripherals (plus MPC and PPC)
*/
void nvic_interrupt_target_state_cfg(void);
/**
* \brief This function enable the interrupts associated
* to the secure peripherals (plus the isolation boundary violation
* interrupts)
*/
void nvic_interrupt_enable(void);
/**
* \brief Get the VTOR value of non-secure image
*
* \return Returns the address where the vector table of the non-secure image
* is located
*/
uint32_t tfm_spm_hal_get_ns_VTOR(void);
/**
* \brief Get the initial address of non-secure image main stack
*
* \return Returns the initial non-secure MSP
*/
uint32_t tfm_spm_hal_get_ns_MSP(void);
/**
* \brief Get the entry point of the non-secure image
*
* \return Returns the address of the non-secure image entry point
*/
uint32_t tfm_spm_hal_get_ns_entry_point(void);
#if TFM_LVL != 1
/**
* \brief Configure the sandbox for a partition.
*
* \param[in] memory_data The memory ranges from the partition DB for this
* partition
* \param[in] platform_data The platform fields of the partition DB record
* for this partition. Can be NULL.
*
* \return Returns the result operation as per \ref spm_err_t
*/
enum spm_err_t tfm_spm_hal_partition_sandbox_config(
const struct tfm_spm_partition_memory_data_t *memory_data,
const struct tfm_spm_partition_platform_data_t *platform_data);
/**
* \brief Deconfigure the sandbox for a partition.
*
* \param[in] memory_data The memory ranges from the partition DB for this
* partition
* \param[in] platform_data The platform fields of the partition DB record
* for this partition. Can be NULL.
*
* \return Returns the result operation as per \ref spm_err_t
*/
enum spm_err_t tfm_spm_hal_partition_sandbox_deconfig(
const struct tfm_spm_partition_memory_data_t *memory_data,
const struct tfm_spm_partition_platform_data_t *platform_data);
/**
* \brief Set the share region mode
*
* \param[in] share The mode to set
*
* \return Returns the result operation as per \ref spm_err_t
*/
enum spm_err_t tfm_spm_hal_set_share_region(
enum tfm_buffer_share_region_e share);
#endif
#endif /* __TFM_SPM_HAL_H__ */

View File

@ -0,0 +1,66 @@
/*
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef __TFM_ARCH_V8M_H__
#define __TFM_ARCH_V8M_H__
#include "cmsis.h"
#define XPSR_T32 0x01000000
#define LR_UNPRIVILEGED 0xfffffffd
/* This header file collects the ARCH related operations. */
struct tfm_state_context_base {
uint32_t r0;
uint32_t r1;
uint32_t r2;
uint32_t r3;
uint32_t r12;
uint32_t ra_lr;
uint32_t ra;
uint32_t xpsr;
};
struct tfm_state_context_ext {
uint32_t r4;
uint32_t r5;
uint32_t r6;
uint32_t r7;
uint32_t r8;
uint32_t r9;
uint32_t r10;
uint32_t r11;
uint32_t sp;
uint32_t sp_limit;
uint32_t dummy;
uint32_t lr;
};
struct tfm_state_context {
struct tfm_state_context_ext ctxb;
};
#define TFM_STATE_1ST_ARG(ctx) \
(((struct tfm_state_context_base *)(ctx)->ctxb.sp)->r0)
#define TFM_STATE_2ND_ARG(ctx) \
(((struct tfm_state_context_base *)(ctx)->ctxb.sp)->r1)
#define TFM_STATE_3RD_ARG(ctx) \
(((struct tfm_state_context_base *)(ctx)->ctxb.sp)->r2)
#define TFM_STATE_4TH_ARG(ctx) \
(((struct tfm_state_context_base *)(ctx)->ctxb.sp)->r3)
#define TFM_STATE_RET_VAL(ctx) \
(((struct tfm_state_context_base *)(ctx)->ctxb.sp)->r0)
__STATIC_INLINE void tfm_trigger_pendsv(void)
{
SCB->ICSR = SCB_ICSR_PENDSVSET_Msk;
}
void tfm_initialize_context(struct tfm_state_context *ctx,
uint32_t r0, uint32_t ra,
uint32_t sp, uint32_t sp_limit);
#endif

View File

@ -0,0 +1,18 @@
/*
* Copyright (c) 2018, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef __TFM_INTERNAL_DEFINES_H__
#define __TFM_INTERNAL_DEFINES_H__
/* IPC internal return status */
#define IPC_SUCCESS 0
#define IPC_ERROR_BAD_PARAMETERS (INT32_MIN)
#define IPC_ERROR_SHORT_BUFFER (INT32_MIN + 1)
#define IPC_ERROR_VERSION (INT32_MIN + 2)
#define IPC_ERROR_MEMORY_CHECK (INT32_MIN + 3)
#define IPC_ERROR_GENERIC (INT32_MIN + 0x1F)
#endif

View File

@ -0,0 +1,98 @@
/*
* Copyright (c) 2018, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef __TFM_LIST_H__
#define __TFM_LIST_H__
/* List structure */
struct tfm_list_node_t {
struct tfm_list_node_t *prev;
struct tfm_list_node_t *next;
};
/**
* \brief Initialize list head.
*
* \param[in] head List head need to be initialized.
*/
__STATIC_INLINE void tfm_list_init(struct tfm_list_node_t *head)
{
head->next = head;
head->prev = head;
}
/**
* \brief Add one node to list tail.
*
* \param[in] head List head initialized by \ref tfm_list_init.
* \param[in] node List node want to be added.
*/
__STATIC_INLINE void
tfm_list_add_tail(struct tfm_list_node_t *head, struct tfm_list_node_t *node)
{
head->prev->next = node;
node->prev = head->prev;
head->prev = node;
node->next = head;
}
/**
* \brief Check if a list is empty.
*
* \param[in] head List head initialized by \ref tfm_list_init.
*
* \returns returns 1 for empty, or 0 for not.
*/
__STATIC_INLINE int32_t tfm_list_is_empty(struct tfm_list_node_t *head)
{
return (head->next == head);
}
/**
* \brief Insert one node to list head.
*
* \param[in] head List head initialized by \ref tfm_list_init.
* \param[in] node List node want to be inserted.
*/
__STATIC_INLINE void
tfm_list_insert_first(struct tfm_list_node_t *head,
struct tfm_list_node_t *node)
{
node->next = head->next;
node->prev = head;
head->next->prev = node;
head->next = node;
}
/**
* \brief Retrieve the fist node from list.
*
* \param[in] head List head initialized by \ref tfm_list_init.
*
* \returns Returns the pointer to first list node.
*/
__STATIC_INLINE
struct tfm_list_node_t *tfm_list_first_node(struct tfm_list_node_t *head)
{
return head->next;
}
/**
* \brief Delete one node from list.
*
* \param[in] node List node want to be deleted.
*/
__STATIC_INLINE void tfm_list_del_node(struct tfm_list_node_t *node)
{
node->prev->next = node->next;
node->next->prev = node->prev;
}
/* Go through each node of a list */
#define TFM_LIST_FOR_EACH(node, head) \
for (node = (head)->next; node != head; node = node->next)
#endif

View File

@ -0,0 +1,68 @@
/*
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef __TFM_MESSAGE_QUEUE_H__
#define __TFM_MESSAGE_QUEUE_H__
#ifndef TFM_MSG_QUEUE_MAX_MSG_NUM
#define TFM_MSG_QUEUE_MAX_MSG_NUM 128
#endif
#define TFM_MSG_MAGIC 0x15154343
/* Message struct to collect parameter from client */
struct tfm_msg_body_t {
int32_t magic;
struct tfm_spm_service_t *service; /* RoT service pointer */
psa_handle_t handle; /* Connected Service handle */
struct tfm_event_ctx ack_mtx; /* Event for ack reponse */
psa_msg_t msg; /* PSA message body */
psa_invec invec[PSA_MAX_IOVEC]; /* Put in/out vectors in msg body */
psa_outvec outvec[PSA_MAX_IOVEC];
psa_outvec *caller_outvec; /*
* Save caller outvec pointer for
* write length update
*/
struct tfm_msg_body_t *next; /* List operators */
};
struct tfm_msg_queue_t {
struct tfm_msg_body_t *head; /* Queue head */
struct tfm_msg_body_t *tail; /* Queue tail */
uint32_t size; /* Number of the queue member */
};
/**
* \brief Enqueue a message into message queue.
*
* \param[in] queue Message queue, it will be initialized
* if has not been initialized.
* \param[in] node Message queue node want to be enqueue.
*
* \retval IPC_SUCCESS Success.
* \retval IPC_ERROR_BAD_PARAMETERS Parameters error.
*/
int32_t tfm_msg_enqueue(struct tfm_msg_queue_t *queue,
struct tfm_msg_body_t *node);
/**
* \brief Dequeue a message from message queue.
*
* \param[in] queue Message queue.
*
* \retval node pointer Success.
* \retval NULL Queue is NULL or size is zero.
*/
struct tfm_msg_body_t *tfm_msg_dequeue(struct tfm_msg_queue_t *queue);
/**
* \brief Check if a message queue is empty.
*
* \param[in] queue Message queue.
*
* \returns Returns 1 for empty, or 0 for not.
*/
int32_t tfm_msg_queue_is_empty(struct tfm_msg_queue_t *queue);
#endif

View File

@ -0,0 +1,85 @@
/*
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef __TFM_POOLS_H__
#define __TFM_POOLS_H__
/*
* Resource pool - few known size resources allocation/free is required,
* so pool is more applicable than heap.
*/
/*
* Pool Instance:
* [ Pool Instance ] + N * [ Pool Chunks ]
*/
struct tfm_pool_chunk_t {
struct tfm_list_node_t list; /* Chunk list */
void *pool; /* Point to the parent pool */
uint8_t data[0]; /* Data indicator */
};
struct tfm_pool_instance_t {
size_t chunksz; /* Chunks size of pool member */
struct tfm_list_node_t chunks_list; /* Chunk list head in pool */
struct tfm_pool_chunk_t chunks[0]; /* Data indicator */
};
/*
* This will declares a static memory pool variable with chunk memory.
* Parameters:
* name - Variable name, will be used when register
* chunksz - chunk size in bytes
* num - Number of chunks
*/
#define TFM_POOL_DECLARE(name, chunksz, num) \
static uint8_t name##_pool_buf[((chunksz) + \
sizeof(struct tfm_pool_chunk_t)) * (num) \
+ sizeof(struct tfm_pool_instance_t)] \
__attribute__((aligned(4))); \
static struct tfm_pool_instance_t *name = \
(struct tfm_pool_instance_t *)name##_pool_buf
/* Get the head size of memory pool */
#define POOL_HEAD_SIZE (sizeof(struct tfm_pool_instance_t) + \
sizeof(struct tfm_pool_chunk_t))
/* Get the whole size of memory pool */
#define POOL_BUFFER_SIZE(name) sizeof(name##_pool_buf)
/**
* \brief Register a memory pool.
*
* \param[in] pool Pointer to memory pool declared by
* \ref TFM_POOL_DECLARE
* \param[in] poolsz Size of the pool buffer.
* \param[in] chunksz Size of chunks.
* \param[in] num Number of chunks.
*
* \retval IPC_SUCCESS Success.
* \retval IPC_ERROR_BAD_PARAMETERS Parameters error.
*/
int32_t tfm_pool_init(struct tfm_pool_instance_t *pool, size_t poolsz,
size_t chunksz, size_t num);
/**
* \brief Allocate a memory from pool.
*
* \param[in] pool pool pointer decleared by \ref TFM_POOL_DECLARE
*
* \retval buffer pointer Success.
* \retval NULL Failed.
*/
void *tfm_pool_alloc(struct tfm_pool_instance_t *pool);
/**
* \brief Free the allocated memory.
*
* \param[in] ptr Buffer pointer want to free.
*/
void tfm_pool_free(void *ptr);
#endif

View File

@ -0,0 +1,296 @@
/*
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef __TFM_SPM_H__
#define __TFM_SPM_H__
#include <stdbool.h>
#include "tfm_list.h"
#ifndef TFM_SPM_MAX_ROT_SERV_NUM
#define TFM_SPM_MAX_ROT_SERV_NUM 28
#endif
#define TFM_VERSION_POLICY_RELAXED 0
#define TFM_VERSION_POLICY_STRICT 1
#ifndef TFM_CONN_HANDLE_MAX_NUM
#define TFM_CONN_HANDLE_MAX_NUM 32
#endif
/* RoT connection handle list */
struct tfm_conn_handle_t {
psa_handle_t handle; /* Handle value */
void *rhandle; /* Reverse handle value */
struct tfm_list_node_t list; /* list node */
};
/* Service database defined by manifest */
struct tfm_spm_service_db_t {
char *name; /* Service name */
uint32_t partition_id; /* Partition ID which service belong to */
psa_signal_t signal; /* Service signal */
uint32_t sid; /* Service identifier */
bool non_secure_client; /* If can be called by non secure client */
uint32_t minor_version; /* Minor version */
uint32_t minor_policy; /* Minor version policy */
};
/* RoT Service data */
struct tfm_spm_service_t {
struct tfm_spm_service_db_t *service_db; /* Service database pointer */
struct tfm_spm_ipc_partition_t *partition; /*
* Point to secure partition
* data
*/
struct tfm_list_node_t handle_list; /* Service handle list */
struct tfm_msg_queue_t msg_queue; /* Message queue */
struct tfm_list_node_t list; /* For list operation */
};
/*
* FixMe: This structure is for IPC partition which is different with library
* mode partition. There needs to be an alignment for IPC partition database
* and library mode database.
*/
/* Secure Partition data */
struct tfm_spm_ipc_partition_t {
int32_t index; /* Partition index */
int32_t id; /* Secure partition ID */
struct tfm_event_ctx signal_event; /* Event signal */
uint32_t signals; /* Service signals had been triggered*/
uint32_t signal_mask; /* Service signal mask passed by psa_wait() */
struct tfm_list_node_t service_list;/* Service list */
};
/*************************** Extended SPM functions **************************/
/**
* \brief Get the running partition ID.
*
* \return Returns the partition ID
*/
uint32_t tfm_spm_partition_get_running_partition_id_ext(void);
/******************** Service handle management functions ********************/
/**
* \brief Create connection handle for client connect
*
* \param[in] service Target service context pointer
*
* \retval PSA_NULL_HANDLE Create failed \ref PSA_NULL_HANDLE
* \retval >0 Service handle created, \ref psa_handle_t
*/
psa_handle_t tfm_spm_create_conn_handle(struct tfm_spm_service_t *service);
/**
* \brief Free connection handle which not used anymore.
*
* \param[in] service Target service context pointer
* \param[in] conn_handle Connection handle created by
* tfm_spm_create_conn_handle(), \ref psa_handle_t
*
* \retval IPC_SUCCESS Success
* \retval IPC_ERROR_BAD_PARAMETERS Bad parameters input
* \retval "Does not return" Panic for not find service by handle
*/
int32_t tfm_spm_free_conn_handle(struct tfm_spm_service_t *service,
psa_handle_t conn_handle);
/**
* \brief Set reverse handle value for connection.
*
* \param[in] service Target service context pointer
* \param[in] conn_handle Connection handle created by
* tfm_spm_create_conn_handle(), \ref psa_handle_t
* \param[in] rhandle rhandle need to save
*
* \retval IPC_SUCCESS Success
* \retval IPC_ERROR_BAD_PARAMETERS Bad parameters input
* \retval "Does not return" Panic for not find handle node
*/
int32_t tfm_spm_set_rhandle(struct tfm_spm_service_t *service,
psa_handle_t conn_handle,
void *rhandle);
/**
* \brief Get reverse handle value from connection hanlde.
*
* \param[in] service Target service context pointer
* \param[in] conn_handle Connection handle created by
* tfm_spm_create_conn_handle(), \ref psa_handle_t
*
* \retval void * Success
* \retval "Does not return" Panic for those:
* service pointer are NULL
* hanlde is \ref PSA_NULL_HANDLE
* handle node does not be found
*/
void *tfm_spm_get_rhandle(struct tfm_spm_service_t *service,
psa_handle_t conn_handle);
/******************** Partition management functions *************************/
/**
* \brief Get current running partition context.
*
* \retval NULL Failed
* \retval "Not NULL" Return the parttion context pointer
* \ref spm_partition_t structures
*/
struct tfm_spm_ipc_partition_t *tfm_spm_get_running_partition(void);
/**
* \brief Get the service context by signal.
*
* \param[in] partition Partition context pointer
* \ref spm_partition_t structures
* \param[in] signal Signal associated with inputs to the Secure
* Partition, \ref psa_signal_t
*
* \retval NULL Failed
* \retval "Not NULL" Target service context pointer,
* \ref spm_service_t structures
*/
struct tfm_spm_service_t *
tfm_spm_get_service_by_signal(struct tfm_spm_ipc_partition_t *partition,
psa_signal_t signal);
/**
* \brief Get the service context by service ID.
*
* \param[in] sid RoT Service identity
*
* \retval NULL Failed
* \retval "Not NULL" Target service context pointer,
* \ref spm_service_t structures
*/
struct tfm_spm_service_t *tfm_spm_get_service_by_sid(uint32_t sid);
/**
* \brief Get the service context by connection handle.
*
* \param[in] conn_handle Connection handle created by
* tfm_spm_create_conn_handle()
*
* \retval NULL Failed
* \retval "Not NULL" Target service context pointer,
* \ref spm_service_t structures
*/
struct tfm_spm_service_t *
tfm_spm_get_service_by_handle(psa_handle_t conn_handle);
/**
* \brief Get the partition context by partition ID.
*
* \param[in] partition_id Partition identity
*
* \retval NULL Failed
* \retval "Not NULL" Target partition context pointer,
* \ref spm_partition_t structures
*/
struct tfm_spm_ipc_partition_t *
tfm_spm_get_partition_by_id(int32_t partition_id);
/************************ Message functions **********************************/
/**
* \brief Get message context by message handle.
*
* \param[in] msg_handle Message handle which is a reference generated
* by the SPM to a specific message.
*
* \return The message body context pointer
* \ref msg_body_t structures
*/
struct tfm_msg_body_t *tfm_spm_get_msg_from_handle(psa_handle_t msg_handle);
/**
* \brief Create a message for PSA client call.
*
* \param[in] service Target service context pointer, which can be
* obtained by partition management functions
* \prarm[in] handle Connect handle return by psa_connect(). Should
* be \ref PSA_NULL_HANDLE in psa_connect().
* \param[in] type Message type, PSA_IPC_CONNECT, PSA_IPC_CALL or
* PSA_IPC_DISCONNECT
* \param[in] ns_caller Whether from NS caller
* \param[in] invec Array of input \ref psa_invec structures
* \param[in] in_len Number of input \ref psa_invec structures
* \param[in] outvec Array of output \ref psa_outvec structures
* \param[in] out_len Number of output \ref psa_outvec structures
* \param[in] caller_outvec Array of caller output \ref psa_outvec structures
*
* \retval NULL Failed
* \retval "Not NULL" New message body pointer \ref msg_body_t structures
*/
struct tfm_msg_body_t *tfm_spm_create_msg(struct tfm_spm_service_t *service,
psa_handle_t handle,
uint32_t type, int32_t ns_caller,
psa_invec *invec, size_t in_len,
psa_outvec *outvec, size_t out_len,
psa_outvec *caller_outvec);
/**
* \brief Free message which unused anymore
*
* \param[in] msg Message pointer which want to free
* \ref msg_body_t structures
*
* \retval void Success
* \retval "Does not return" Failed
*/
void tfm_spm_free_msg(struct tfm_msg_body_t *msg);
/**
* \brief Send message and wake up the SP who is waiting on
* message queue, block the current thread and
* scheduler triggered
*
* \param[in] service Target service context pointer, which can be
* obtained by partition management functions
* \param[in] msg message created by spm_create_msg()
* \ref msg_body_t structures
*
* \retval IPC_SUCCESS Success
* \retval IPC_ERROR_BAD_PARAMETERS Bad parameters input
* \retval IPC_ERROR_GENERIC Failed to enqueue message to service message queue
*/
int32_t tfm_spm_send_event(struct tfm_spm_service_t *service,
struct tfm_msg_body_t *msg);
/**
* \brief Check the client minor version according to
* version policy
*
* \param[in] service Target service context pointer, which can be get
* by partition management functions
* \param[in] minor_version Client support minor version
*
* \retval IPC_SUCCESS Success
* \retval IPC_ERROR_BAD_PARAMETERS Bad parameters input
* \retval IPC_ERROR_VERSION Check failed
*/
int32_t tfm_spm_check_client_version(struct tfm_spm_service_t *service,
uint32_t minor_version);
/**
* \brief Check the memory reference is valid.
*
* \param[in] buffer Pointer of memory reference
* \param[in] len Length of memory reference in bytes
* \param[in] ns_caller From non-secure caller
*
* \retval IPC_SUCCESS Success
* \retval IPC_ERROR_BAD_PARAMETERS Bad parameters input
* \retval IPC_ERROR_MEMORY_CHECK Check failed
*/
int32_t tfm_memory_check(void *buffer, size_t len, int32_t ns_caller);
/* This function should be called before schedule function */
void tfm_spm_init(void);
#endif

View File

@ -0,0 +1,105 @@
/*
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef __TFM_SVCALLS_H__
#define __TFM_SVCALLS_H__
/* Svcall for PSA Client APIs */
/**
* \brief SVC handler for \ref psa_framework_version.
*
* \return version The version of the PSA Framework implementation
* that is providing the runtime services to the
* caller.
*/
uint32_t tfm_svcall_psa_framework_version(void);
/**
* \brief SVC handler for \ref psa_version.
*
* \param[in] args Include all input arguments: sid.
* \param[in] ns_caller If 'non-zero', call from non-secure client.
* Or from secure client.
*
* \retval PSA_VERSION_NONE The RoT Service is not implemented, or the
* caller is not permitted to access the service.
* \retval > 0 The minor version of the implemented RoT
* Service.
*/
uint32_t tfm_svcall_psa_version(uint32_t *args, int32_t ns_caller);
/**
* \brief SVC handler for \ref psa_connect.
*
* \param[in] args Include all input arguments:
* sid, minor_version.
* \param[in] ns_caller If 'non-zero', call from non-secure client.
* Or from secure client.
*
* \retval > 0 A handle for the connection.
* \retval PSA_CONNECTION_REFUSED The SPM or RoT Service has refused the
* connection.
* \retval PSA_CONNECTION_BUSY The SPM or RoT Service cannot make the
* connection at the moment.
* \retval "Does not return" The RoT Service ID and version are not
* supported, or the caller is not permitted to
* access the service.
*/
psa_handle_t tfm_svcall_psa_connect(uint32_t *args, int32_t ns_caller);
/**
* \brief SVC handler for \ref psa_call.
*
* \param[in] args Include all input arguments:
* handle, in_vec, in_len, out_vec, out_len.
* \param[in] ns_caller If 'non-zero', call from non-secure client.
* Or from secure client.
*
* \retval >=0 RoT Service-specific status value.
* \retval <0 RoT Service-specific error code.
* \retval PSA_DROP_CONNECTION The connection has been dropped by the RoT
* Service. This indicates that either this or
* a previous message was invalid.
* \retval "Does not return" The call is invalid, one or more of the
* following are true:
* \arg An invalid handle was passed.
* \arg The connection is already handling a request.
* \arg An invalid memory reference was provided.
* \arg in_len + out_len > PSA_MAX_IOVEC.
* \arg The message is unrecognized by the RoT
* Service or incorrectly formatted.
*/
psa_status_t tfm_svcall_psa_call(uint32_t *args, int32_t ns_caller);
/**
* \brief SVC handler for \ref psa_close.
*
* \param[in] args Include all input arguments: handle.
* \param[in] ns_caller If 'non-zero', call from non-secure client.
* Or from secure client.
*
* \retval void Success.
* \retval "Does not return" The call is invalid, one or more of the
* following are true:
* \arg An invalid handle was provided that is not
* the null handle.
* \arg The connection is handling a request.
*/
void tfm_svcall_psa_close(uint32_t *args, int32_t ns_caller);
/**
* \brief SVC handler for IPC functions
*
* \param[in] svc_num SVC number
* \param[in] ctx Argument context
*
* \returns Return values from those who has,
* or PSA_SUCCESS.
*/
int32_t SVC_Handler_IPC(tfm_svc_number_t svc_num, uint32_t *ctx);
#endif

View File

@ -0,0 +1,223 @@
/*
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef __TFM_THREAD_H__
#define __TFM_THREAD_H__
#include "tfm_arch_v8m.h"
#include "cmsis_compiler.h"
/* Status code */
#define THRD_STAT_CREATING 0
#define THRD_STAT_RUNNING 1
#define THRD_STAT_BLOCK 2
#define THRD_STAT_DETACH 3
#define THRD_STAT_INVALID 4
/* Security attribute - default as security */
#define THRD_ATTR_SECURE_OFFSET 16
#define THRD_ATTR_SECURE (0)
#define THRD_ATTR_NON_SECURE (1 << THRD_ATTR_SECURE_OFFSET)
/* Lower value has higher priority */
#define THRD_PRIOR_MASK 0xFF
#define THRD_PRIOR_HIGHEST 0x0
#define THRD_PRIOR_MEDIUM 0x7F
#define THRD_PRIOR_LOWEST 0xFF
/* Error code */
#define THRD_SUCCESS 0
#define THRD_ERR_INVALID_PARAM 1
/* Thread entry function type */
typedef void *(*tfm_thrd_func_t)(void *);
/* Thread context */
struct tfm_thrd_ctx {
tfm_thrd_func_t pfn; /* entry function */
void *param; /* entry parameter */
uint8_t *sp_base; /* stack bottom */
uint8_t *sp_top; /* stack top */
uint32_t prior; /* priority */
uint32_t status; /* status */
struct tfm_state_context state_ctx; /* State context */
struct tfm_thrd_ctx *next; /* next thread in list */
};
/*
* Initialize a thread context with the necessary info.
*
* Parameters :
* pth - pointer of caller provided thread context
* pfn - thread entry function
* param - thread entry function parameter
* sp_base - stack pointer base (higher address)
* sp_top - stack pointer top (lower address)
*
* Notes :
* Thread contex rely on caller allocated memory; initialize members in
* context. This function does not insert thread into schedulable list.
*/
void tfm_thrd_init(struct tfm_thrd_ctx *pth,
tfm_thrd_func_t pfn, void *param,
uint8_t *sp_base, uint8_t *sp_top);
/* Set thread priority.
*
* Parameters :
* pth - pointer of thread context
* prior - priority value (0~255)
*
* Notes :
* Set thread priority. Priority is set to THRD_PRIOR_MEDIUM in
* tfm_thrd_init().
*/
void __STATIC_INLINE tfm_thrd_priority(struct tfm_thrd_ctx *pth,
uint32_t prior)
{
pth->prior &= ~THRD_PRIOR_MASK;
pth->prior |= prior & THRD_PRIOR_MASK;
}
/*
* Set thread security attribute.
*
* Parameters :
* pth - pointer of thread context
* attr_secure - THRD_ATTR_SECURE or THRD_ATTR_NON_SECURE
*
* Notes
* Reuse prior of thread context to shift down non-secure thread priority.
*/
void __STATIC_INLINE tfm_thrd_secure(struct tfm_thrd_ctx *pth,
uint32_t attr_secure)
{
pth->prior &= ~THRD_ATTR_NON_SECURE;
pth->prior |= attr_secure;
}
/*
* Set thread status.
*
* Parameters :
* pth - pointer of thread context
* new_status - new status of thread
*
* Return :
* None
*
* Notes :
* Thread status is not changed if invalid status value inputed.
*/
void tfm_thrd_set_status(struct tfm_thrd_ctx *pth, uint32_t new_status);
/*
* Get thread status.
*
* Parameters :
* pth - pointer of thread context
*
* Return :
* Status of thread
*/
uint32_t __STATIC_INLINE tfm_thrd_get_status(struct tfm_thrd_ctx *pth)
{
return pth->status;
}
/*
* Set thread state return value.
*
* Parameters :
* pth - pointer of thread context
* retval - return value to be set for thread state
*
* Notes :
* This API is useful for blocked syscall blocking thread. Syscall
* could set its return value to the caller before caller goes.
*/
void __STATIC_INLINE tfm_thrd_set_retval(struct tfm_thrd_ctx *pth,
uint32_t retval)
{
TFM_STATE_RET_VAL(&pth->state_ctx) = retval;
}
/*
* Validate thread context and insert it into schedulable list.
*
* Parameters :
* pth - pointer of thread context
*
* Return :
* THRD_SUCCESS for success. Or an error is returned.
*
* Notes :
* This function validates thread info. It returns error if thread info
* is not correct. Thread is avaliable after successful tfm_thrd_start().
*/
uint32_t tfm_thrd_start(struct tfm_thrd_ctx *pth);
/*
* Get current running thread.
*
* Return :
* Current running thread context pointer.
*/
struct tfm_thrd_ctx *tfm_thrd_curr_thread(void);
/*
* Get next running thread in list.
*
* Return :
* Pointer of next thread to be run.
*/
struct tfm_thrd_ctx *tfm_thrd_next_thread(void);
/*
* Activate a scheduling action after exception.
*
* Notes :
* This function could be called multiple times before scheduling.
*/
void tfm_thrd_activate_schedule(void);
/*
* Save current context into 'prev' thread and switch to 'next'.
*
* Parameters :
* ctxb - latest caller context
* prev - previous thread to be switched out
* next - thread to be run
*
* Notes :
* This function could be called multiple times before scheduling.
*/
void tfm_thrd_context_switch(struct tfm_state_context_ext *ctxb,
struct tfm_thrd_ctx *prev,
struct tfm_thrd_ctx *next);
/*
* Exit current running thread.
*
* Notes :
* Remove current thread out of schedulable list.
*/
void tfm_thrd_do_exit(void);
/*
* PendSV specified function.
*
* Parameters :
* ctxb - State context storage pointer
*
* Notes:
* This is a staging API. Scheduler should be called in SPM finally and
* this function will be obsoleted later.
*/
void tfm_pendsv_do_schedule(struct tfm_state_context_ext *ctxb);
#endif

View File

@ -0,0 +1,29 @@
/*
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef __TFM_UTILS_H__
#define __TFM_UTILS_H__
/* CPU spin here */
void tfm_panic(void);
/* Assert and spin */
#define TFM_ASSERT(cond) \
do { \
if (!(cond)) { \
printf("Assert:%s:%d", __FUNCTION__, __LINE__); \
while (1) \
; \
} \
} while (0)
/* Get container structure start address from member */
#define TFM_GET_CONTAINER_PTR(ptr, type, member) \
(type *)((unsigned long)(ptr) - offsetof(type, member))
int32_t tfm_bitcount(uint32_t n);
#endif

View File

@ -0,0 +1,86 @@
/*
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef __TFM_WAIT_H__
#define __TFM_WAIT_H__
#include "cmsis_compiler.h"
#define EVENT_MAGIC 0x65766e74
#define EVENT_STAT_WAITED 0x0
#define EVENT_STAT_SIGNALED 0x1
struct tfm_event_ctx {
uint32_t magic; /* 'evnt' */
struct tfm_thrd_ctx *owner; /* waiting thread */
uint32_t status; /* status */
uint32_t retval; /* return value */
};
/*
* Initialize an event context.
*
* Parameters :
* pevt - pointer of event context caller provided
* stat - initial status (EVENT_STAT_WAITED or EVENT_STAT_SIGNALED)
*/
void __STATIC_INLINE tfm_event_init(struct tfm_event_ctx *pevt, uint32_t stat)
{
pevt->magic = EVENT_MAGIC;
pevt->status = stat;
pevt->owner = NULL;
pevt->retval = 0;
}
/*
* Wait on an event.
*
* Parameters :
* pevt - pointer of event context
*
* Notes :
* Thread is blocked if event is not signaled.
*/
void tfm_event_wait(struct tfm_event_ctx *pevt);
/*
* Signal an event.
*
* Parameters :
* pevt - pointer of event context
*
* Notes :
* Waiting thread on this event will be running.
*/
void tfm_event_signal(struct tfm_event_ctx *pevt);
/*
* Peek an event status.
*
* Parameters :
* pevt - pointer of event context
*
* Return :
* Status of event.
*
* Notes :
* This function is used for getting event status without blocking thread.
*/
uint32_t tfm_event_peek(struct tfm_event_ctx *pevt);
/*
* Set event owner return value.
*
* Parameters :
* pevt - pointer of event context
* retval - return value of blocked owner thread
*
* Notes :
* Thread return value is set while thread is to be running.
*/
void tfm_event_owner_retval(struct tfm_event_ctx *pevt, uint32_t retval);
#endif

View File

@ -0,0 +1,55 @@
/*
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include <inttypes.h>
#include <stdio.h>
#include "tfm_svc.h"
#include "psa_client.h"
__attribute__((naked))
uint32_t psa_framework_version(void)
{
__ASM("SVC %0 \n"
"BX LR \n"
: : "I" (TFM_SVC_PSA_FRAMEWORK_VERSION));
}
__attribute__((naked))
uint32_t psa_version(uint32_t sid)
{
__ASM("SVC %0 \n"
"BX LR \n"
: : "I" (TFM_SVC_PSA_VERSION));
}
__attribute__((naked))
psa_handle_t psa_connect(uint32_t sid, uint32_t minor_version)
{
__ASM("SVC %0 \n"
"BX LR \n"
: : "I" (TFM_SVC_PSA_CONNECT));
}
__attribute__((naked))
psa_status_t psa_call(psa_handle_t handle,
const psa_invec *in_vec,
size_t in_len,
psa_outvec *out_vec,
size_t out_len)
{
__ASM("SVC %0 \n"
"BX LR \n"
: : "I" (TFM_SVC_PSA_CALL));
}
__attribute__((naked))
void psa_close(psa_handle_t handle)
{
__ASM("SVC %0 \n"
"BX LR \n"
: : "I" (TFM_SVC_PSA_CLOSE));
}

View File

@ -0,0 +1,96 @@
/*
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include <inttypes.h>
#include <stdio.h>
#include "tfm_svc.h"
#include "psa_client.h"
#include "psa_service.h"
__attribute__((naked))
psa_signal_t psa_wait(psa_signal_t signal_mask, uint32_t timeout)
{
__ASM("SVC %0 \n"
"BX LR \n"
: : "I" (TFM_SVC_PSA_WAIT));
}
__attribute__((naked))
psa_status_t psa_get(psa_signal_t signal, psa_msg_t *msg)
{
__ASM("SVC %0 \n"
"BX LR \n"
: : "I" (TFM_SVC_PSA_GET));
}
__attribute__((naked))
void psa_set_rhandle(psa_handle_t msg_handle, void *rhandle)
{
__ASM("SVC %0 \n"
"BX LR \n"
: : "I" (TFM_SVC_PSA_SET_RHANDLE));
}
__attribute__((naked))
size_t psa_read(psa_handle_t msg_handle, uint32_t invec_idx,
void *buffer, size_t num_bytes)
{
__ASM("SVC %0 \n"
"BX LR \n"
: : "I" (TFM_SVC_PSA_READ));
}
__attribute__((naked))
size_t psa_skip(psa_handle_t msg_handle, uint32_t invec_idx, size_t num_bytes)
{
__ASM("SVC %0 \n"
"BX LR \n"
: : "I" (TFM_SVC_PSA_SKIP));
}
__attribute__((naked))
void psa_write(psa_handle_t msg_handle, uint32_t outvec_idx,
const void *buffer, size_t num_bytes)
{
__ASM("SVC %0 \n"
"BX LR \n"
: : "I" (TFM_SVC_PSA_WRITE));
}
__attribute__((naked))
void psa_reply(psa_handle_t msg_handle, psa_status_t retval)
{
__ASM("SVC %0 \n"
"BX LR \n"
: : "I" (TFM_SVC_PSA_REPLY));
}
__attribute__((naked))
void psa_notify(int32_t partition_id)
{
__ASM("SVC %0 \n"
"BX LR \n"
: : "I" (TFM_SVC_PSA_NOTIFY));
}
__attribute__((naked))
void psa_clear(void)
{
__ASM("SVC %0 \n"
"BX LR \n"
: : "I" (TFM_SVC_PSA_CLEAR));
}
__attribute__((naked))
void psa_eoi(psa_signal_t irq_signal)
{
__ASM("SVC %0 \n"
"BX LR \n"
: : "I" (TFM_SVC_PSA_EOI));
}

View File

@ -0,0 +1,124 @@
/*
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include <inttypes.h>
#include <stdio.h>
#include "tfm_arch_v8m.h"
#include "cmsis.h"
#include "psa_client.h"
#include "psa_service.h"
#include "secure_utilities.h"
#include "tfm_utils.h"
#include "tfm_thread.h"
/* This file contains the ARCH code for ARM V8M */
/*
* Thread exit zone.
* This function is set as the return address of thread entry and only
* privileged thread could return here. Un-privileged thread triggers
* fault if it tries to jump here and it gets exit by fault handler.
*
* The reason of putting this function here is for fault handler checking.
* Function address could be checked in fault handler to know it is a REAL
* thread exit or just an exception.
*/
static void exit_zone(void)
{
tfm_thrd_do_exit();
}
void tfm_initialize_context(struct tfm_state_context *ctx,
uint32_t r0, uint32_t ra,
uint32_t sp, uint32_t sp_limit)
{
/*
* For security consideration, set unused registers into ZERO;
* and only necessary registers are set here.
*/
struct tfm_state_context_base *p_ctxa =
(struct tfm_state_context_base *)sp;
/*
* Shift back SP to leave space for holding base context
* since thread is kicked off through exception return.
*/
p_ctxa--;
/* Basic context is considerate at thread start.*/
tfm_memset(p_ctxa, 0, sizeof(*p_ctxa));
p_ctxa->r0 = r0;
p_ctxa->ra = ra;
p_ctxa->ra_lr = (uint32_t)exit_zone;
p_ctxa->xpsr = XPSR_T32;
tfm_memset(ctx, 0, sizeof(*ctx));
ctx->ctxb.sp = (uint32_t)p_ctxa;
ctx->ctxb.sp_limit = sp_limit;
ctx->ctxb.lr = LR_UNPRIVILEGED;
}
/*
* Stack status at PendSV entry:
*
* [ R0 - R3 ]<- PSP
* [ R12 ]
* [ LR_of_RA ]
* MSP->[ ........ ] [ RA ]
* [ ........ ] [ XPSR ]
* [ ........ ]
* [ ........ ]
*
* Stack status before calling pendsv_do_schedule():
*
* MSP->[ R4 - R11 ]
* [ PSP ]--->[ R0 - R3 ]
* [ PSP Limit] [ R12 ]
* [ R2(dummy)] [ LR_of_RA ]
* [ LR ] [ RA ]
* [ ........ ] [ XPSR ]
* [ ........ ] [ ........ ]
* [ ........ ]
*
* pendsv_do_schedule() updates stacked context into current thread and
* replace stacked context with context of next thread.
*
* Scheduler does not support handler mode thread so take PSP/PSP_LIMIT as
* thread SP/SP_LIMIT. R2 holds dummy data due to stack operation is 8 bytes
* aligned.
*/
__attribute__((naked)) void PendSV_Handler(void)
{
__ASM(
"mrs r0, psp \n"
"mrs r1, psplim \n"
"push {r0, r1, r2, lr} \n"
"push {r4-r11} \n"
"mov r0, sp \n"
"bl tfm_pendsv_do_schedule \n"
"pop {r4-r11} \n"
"pop {r0, r1, r2, lr} \n"
"msr psp, r0 \n"
"msr psplim, r1 \n"
"bx lr \n"
);
}
/* Reserved for future usage */
__attribute__((naked)) void MemManage_Handler(void)
{
__ASM("b .");
}
__attribute__((naked)) void BusFault_Handler(void)
{
__ASM("b .");
}
__attribute__((naked)) void UsageFault_Handler(void)
{
__ASM("b .");
}

View File

@ -0,0 +1,56 @@
/*
* Copyright (c) 2018, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include <inttypes.h>
#include <stdio.h>
#include "tfm_thread.h"
#include "tfm_wait.h"
#include "psa_client.h"
#include "psa_service.h"
#include "tfm_internal_defines.h"
#include "tfm_message_queue.h"
/* Message queue process */
int32_t tfm_msg_enqueue(struct tfm_msg_queue_t *queue,
struct tfm_msg_body_t *node)
{
if (!queue || !node) {
return IPC_ERROR_BAD_PARAMETERS;
}
if (queue->size == 0) {
queue->head = node;
queue->tail = node;
} else {
queue->tail->next = node;
queue->tail = node;
}
queue->size++;
return IPC_SUCCESS;
}
struct tfm_msg_body_t *tfm_msg_dequeue(struct tfm_msg_queue_t *queue)
{
struct tfm_msg_body_t *pop_node;
if (!queue) {
return NULL;
}
if (queue->size == 0) {
return NULL;
}
pop_node = queue->head;
queue->head = queue->head->next;
queue->size--;
return pop_node;
}
int32_t tfm_msg_queue_is_empty(struct tfm_msg_queue_t *queue)
{
return queue->size == 0 ? 1 : 0;
}

View File

@ -0,0 +1,87 @@
/*
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include "tfm_thread.h"
#include "tfm_wait.h"
#include "psa_client.h"
#include "psa_service.h"
#include "tfm_internal_defines.h"
#include "cmsis_compiler.h"
#include "tfm_utils.h"
#include "tfm_list.h"
#include "tfm_pools.h"
#include "secure_utilities.h"
int32_t tfm_pool_init(struct tfm_pool_instance_t *pool, size_t poolsz,
size_t chunksz, size_t num)
{
struct tfm_pool_chunk_t *pchunk;
size_t i;
if (!pool || num == 0) {
return IPC_ERROR_BAD_PARAMETERS;
}
/* Ensure buffer is large enough */
if (poolsz != ((chunksz + sizeof(struct tfm_pool_chunk_t)) * num +
sizeof(struct tfm_pool_instance_t))) {
return IPC_ERROR_BAD_PARAMETERS;
}
/* Buffer should be BSS cleared but clear it again */
tfm_memset(pool, 0, poolsz);
/* Chain pool chunks */
tfm_list_init(&pool->chunks_list);
pchunk = (struct tfm_pool_chunk_t *)pool->chunks;
for (i = 0; i < num; i++) {
pchunk->pool = pool;
tfm_list_add_tail(&pool->chunks_list, &pchunk->list);
pchunk = (struct tfm_pool_chunk_t *)&pchunk->data[chunksz];
}
/* Prepare instance and insert to pool list */
pool->chunksz = chunksz;
return IPC_SUCCESS;
}
void *tfm_pool_alloc(struct tfm_pool_instance_t *pool)
{
struct tfm_list_node_t *node;
struct tfm_pool_chunk_t *pchunk;
if (!pool) {
return NULL;
}
if (tfm_list_is_empty(&pool->chunks_list)) {
return NULL;
}
node = tfm_list_first_node(&pool->chunks_list);
pchunk = TFM_GET_CONTAINER_PTR(node, struct tfm_pool_chunk_t, list);
/* Remove node from list node, it will be added when pool free */
tfm_list_del_node(node);
return &pchunk->data;
}
void tfm_pool_free(void *ptr)
{
struct tfm_pool_chunk_t *pchunk;
struct tfm_pool_instance_t *pool;
pchunk = TFM_GET_CONTAINER_PTR(ptr, struct tfm_pool_chunk_t, data);
pool = (struct tfm_pool_instance_t *)pchunk->pool;
tfm_list_add_tail(&pool->chunks_list, &pchunk->list);
}

View File

@ -0,0 +1,628 @@
/*
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include <inttypes.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include "psa_client.h"
#include "psa_service.h"
#include "tfm_utils.h"
#include "spm_api.h"
#include "spm_db.h"
#include "spm_db_setup.h"
#include "tfm_internal_defines.h"
#include "tfm_wait.h"
#include "tfm_message_queue.h"
#include "tfm_list.h"
#include "tfm_pools.h"
#include "tfm_spm.h"
#include "tfm_spm_signal_defs.h"
#include "tfm_thread.h"
#include "region_defs.h"
#include "tfm_nspm.h"
/*
* IPC partitions.
* FixMe: Need to get align with spm_partition_db_t.
*/
static struct tfm_spm_ipc_partition_t
g_spm_ipc_partition[SPM_MAX_PARTITIONS] = {};
/* Extern SPM variable */
extern struct spm_partition_db_t g_spm_partition_db;
/* Pools */
TFM_POOL_DECLARE(conn_handle_pool, sizeof(struct tfm_conn_handle_t),
TFM_CONN_HANDLE_MAX_NUM);
TFM_POOL_DECLARE(spm_service_pool, sizeof(struct tfm_spm_service_t),
TFM_SPM_MAX_ROT_SERV_NUM);
TFM_POOL_DECLARE(msg_db_pool, sizeof(struct tfm_msg_body_t),
TFM_MSG_QUEUE_MAX_MSG_NUM);
static struct tfm_spm_service_db_t g_spm_service_db[] = {
#include "tfm_service_list.inc"
};
/********************** SPM functions for handler mode ***********************/
/* Service handle management functions */
psa_handle_t tfm_spm_create_conn_handle(struct tfm_spm_service_t *service)
{
struct tfm_conn_handle_t *node;
TFM_ASSERT(service);
/* Get buffer for handle list structure from handle pool */
node = (struct tfm_conn_handle_t *)tfm_pool_alloc(conn_handle_pool);
if (!node) {
return PSA_NULL_HANDLE;
}
/* Global unique handle, use handle buffer address directly */
node->handle = (psa_handle_t)node;
/* Add handle node to list for next psa functions */
tfm_list_add_tail(&service->handle_list, &node->list);
return node->handle;
}
static struct tfm_conn_handle_t *
tfm_spm_find_conn_handle_node(struct tfm_spm_service_t *service,
psa_handle_t conn_handle)
{
struct tfm_conn_handle_t *handle_node;
struct tfm_list_node_t *node, *head;
TFM_ASSERT(service);
head = &service->handle_list;
TFM_LIST_FOR_EACH(node, head) {
handle_node = TFM_GET_CONTAINER_PTR(node, struct tfm_conn_handle_t,
list);
if (handle_node->handle == conn_handle) {
return handle_node;
}
}
return NULL;
}
int32_t tfm_spm_free_conn_handle(struct tfm_spm_service_t *service,
psa_handle_t conn_handle)
{
struct tfm_conn_handle_t *node;
TFM_ASSERT(service);
/* There are many handles for each RoT Service */
node = tfm_spm_find_conn_handle_node(service, conn_handle);
if (!node) {
tfm_panic();
}
/* Remove node from handle list */
tfm_list_del_node(&node->list);
node->rhandle = NULL;
/* Back handle buffer to pool */
tfm_pool_free(node);
return IPC_SUCCESS;
}
int32_t tfm_spm_set_rhandle(struct tfm_spm_service_t *service,
psa_handle_t conn_handle,
void *rhandle)
{
struct tfm_conn_handle_t *node;
TFM_ASSERT(service);
/* Set reverse handle value only be allowed for a connected handle */
TFM_ASSERT(conn_handle != PSA_NULL_HANDLE);
/* There are many handles for each RoT Service */
node = tfm_spm_find_conn_handle_node(service, conn_handle);
if (!node) {
tfm_panic();
}
node->rhandle = rhandle;
return IPC_SUCCESS;
}
void *tfm_spm_get_rhandle(struct tfm_spm_service_t *service,
psa_handle_t conn_handle)
{
struct tfm_conn_handle_t *node;
TFM_ASSERT(service);
/* Get reverse handle value only be allowed for a connected handle */
TFM_ASSERT(conn_handle != PSA_NULL_HANDLE);
/* There are many handles for each RoT Service */
node = tfm_spm_find_conn_handle_node(service, conn_handle);
if (!node) {
tfm_panic();
}
return node->rhandle;
}
/* Partition management functions */
struct tfm_spm_service_t *
tfm_spm_get_service_by_signal(struct tfm_spm_ipc_partition_t *partition,
psa_signal_t signal)
{
struct tfm_list_node_t *node, *head;
struct tfm_spm_service_t *service;
TFM_ASSERT(partition);
if (tfm_list_is_empty(&partition->service_list)) {
tfm_panic();
}
head = &partition->service_list;
TFM_LIST_FOR_EACH(node, head) {
service = TFM_GET_CONTAINER_PTR(node, struct tfm_spm_service_t, list);
if (service->service_db->signal == signal) {
return service;
}
}
return NULL;
}
struct tfm_spm_service_t *tfm_spm_get_service_by_sid(uint32_t sid)
{
uint32_t i;
struct tfm_list_node_t *node, *head;
struct tfm_spm_service_t *service;
for (i = 0; i < SPM_MAX_PARTITIONS; i++) {
/* Skip partition without IPC flag */
if ((tfm_spm_partition_get_flags(g_spm_ipc_partition[i].index) &
SPM_PART_FLAG_IPC) == 0) {
continue;
}
if (tfm_list_is_empty(&g_spm_ipc_partition[i].service_list)) {
continue;
}
head = &g_spm_ipc_partition[i].service_list;
TFM_LIST_FOR_EACH(node, head) {
service = TFM_GET_CONTAINER_PTR(node, struct tfm_spm_service_t,
list);
if (service->service_db->sid == sid) {
return service;
}
}
}
return NULL;
}
struct tfm_spm_service_t *
tfm_spm_get_service_by_handle(psa_handle_t conn_handle)
{
uint32_t i;
struct tfm_conn_handle_t *handle;
struct tfm_list_node_t *service_node, *service_head;
struct tfm_list_node_t *handle_node, *handle_head;
struct tfm_spm_service_t *service;
for (i = 0; i < SPM_MAX_PARTITIONS; i++) {
/* Skip partition without IPC flag */
if ((tfm_spm_partition_get_flags(g_spm_ipc_partition[i].index) &
SPM_PART_FLAG_IPC) == 0) {
continue;
}
if (tfm_list_is_empty(&g_spm_ipc_partition[i].service_list)) {
continue;
}
service_head = &g_spm_ipc_partition[i].service_list;
TFM_LIST_FOR_EACH(service_node, service_head) {
service = TFM_GET_CONTAINER_PTR(service_node,
struct tfm_spm_service_t, list);
handle_head = &service->handle_list;
TFM_LIST_FOR_EACH(handle_node, handle_head) {
handle = TFM_GET_CONTAINER_PTR(handle_node,
struct tfm_conn_handle_t, list);
if (handle->handle == conn_handle) {
return service;
}
}
}
}
return NULL;
}
struct tfm_spm_ipc_partition_t *
tfm_spm_get_partition_by_id(int32_t partition_id)
{
uint32_t i;
for (i = 0; i < SPM_MAX_PARTITIONS; i++) {
if (g_spm_ipc_partition[i].id == partition_id) {
return &g_spm_ipc_partition[i];
}
}
return NULL;
}
struct tfm_spm_ipc_partition_t *tfm_spm_get_running_partition(void)
{
uint32_t spid;
spid = tfm_spm_partition_get_running_partition_id_ext();
return tfm_spm_get_partition_by_id(spid);
}
int32_t tfm_spm_check_client_version(struct tfm_spm_service_t *service,
uint32_t minor_version)
{
TFM_ASSERT(service);
switch (service->service_db->minor_policy) {
case TFM_VERSION_POLICY_RELAXED:
if (minor_version > service->service_db->minor_version) {
return IPC_ERROR_VERSION;
}
break;
case TFM_VERSION_POLICY_STRICT:
if (minor_version != service->service_db->minor_version) {
return IPC_ERROR_VERSION;
}
break;
default:
return IPC_ERROR_VERSION;
}
return IPC_SUCCESS;
}
/* Message functions */
struct tfm_msg_body_t *tfm_spm_get_msg_from_handle(psa_handle_t msg_handle)
{
/*
* There may be one error handle passed by the caller in two conditions:
* 1. Not a valid message handle.
* 2. Handle between different Partitions. Partition A passes one handle
* belong to other Partitions and tries to access other's data.
* So, need do necessary checking to prevent those conditions.
*/
struct tfm_msg_body_t *msg;
uint32_t partition_id;
msg = (struct tfm_msg_body_t *)msg_handle;
if (!msg) {
return NULL;
}
/*
* FixMe: For condition 1: using a magic number to define it's a message.
* It needs to be an enhancement to check the handle belong to service.
*/
if (msg->magic != TFM_MSG_MAGIC) {
return NULL;
}
/* For condition 2: check if the partition ID is same */
partition_id = tfm_spm_partition_get_running_partition_id_ext();
if (partition_id != msg->service->partition->id) {
return NULL;
}
return msg;
}
struct tfm_msg_body_t *tfm_spm_create_msg(struct tfm_spm_service_t *service,
psa_handle_t handle,
uint32_t type, int32_t ns_caller,
psa_invec *invec, size_t in_len,
psa_outvec *outvec, size_t out_len,
psa_outvec *caller_outvec)
{
struct tfm_msg_body_t *msg = NULL;
uint32_t i;
TFM_ASSERT(service);
TFM_ASSERT(!(invec == NULL && in_len != 0));
TFM_ASSERT(!(outvec == NULL && out_len != 0));
TFM_ASSERT(in_len <= PSA_MAX_IOVEC);
TFM_ASSERT(out_len <= PSA_MAX_IOVEC);
TFM_ASSERT(in_len + out_len <= PSA_MAX_IOVEC);
/* Get message buffer from message pool */
msg = (struct tfm_msg_body_t *)tfm_pool_alloc(msg_db_pool);
if (!msg) {
return NULL;
}
/* Clear message buffer before using it */
tfm_memset(msg, 0, sizeof(struct tfm_msg_body_t));
tfm_event_init(&msg->ack_mtx, EVENT_STAT_WAITED);
msg->magic = TFM_MSG_MAGIC;
msg->service = service;
msg->handle = handle;
msg->caller_outvec = caller_outvec;
/* Get current partition id */
if (ns_caller) {
msg->msg.client_id = tfm_nspm_get_current_client_id();
} else {
msg->msg.client_id = tfm_spm_partition_get_running_partition_id_ext();
}
/* Copy contents */
msg->msg.type = type;
for (i = 0; i < in_len; i++) {
msg->msg.in_size[i] = invec[i].len;
msg->invec[i].base = invec[i].base;
}
for (i = 0; i < out_len; i++) {
msg->msg.out_size[i] = outvec[i].len;
msg->outvec[i].base = outvec[i].base;
/* Out len is used to record the writed number, set 0 here again */
msg->outvec[i].len = 0;
}
/* Use message address as handle */
msg->msg.handle = (psa_handle_t)msg;
/* For connected handle, set rhandle to every message */
if (handle != PSA_NULL_HANDLE) {
msg->msg.rhandle = tfm_spm_get_rhandle(service, handle);
}
return msg;
}
void tfm_spm_free_msg(struct tfm_msg_body_t *msg)
{
tfm_pool_free(msg);
}
int32_t tfm_spm_send_event(struct tfm_spm_service_t *service,
struct tfm_msg_body_t *msg)
{
TFM_ASSERT(service);
TFM_ASSERT(msg);
/* Enqueue message to service message queue */
if (tfm_msg_enqueue(&service->msg_queue, msg) != IPC_SUCCESS) {
return IPC_ERROR_GENERIC;
}
/* Messages put. Update signals */
service->partition->signals |= service->service_db->signal;
/* Save return value for blocked threads */
tfm_event_owner_retval(&service->partition->signal_event,
service->partition->signals &
service->partition->signal_mask);
/* Wake waiting thread up */
tfm_event_signal(&service->partition->signal_event);
tfm_event_wait(&msg->ack_mtx);
return IPC_SUCCESS;
}
/* SPM extend functions */
uint32_t tfm_spm_partition_get_running_partition_id_ext(void)
{
struct tfm_thrd_ctx *pth = tfm_thrd_curr_thread();
struct spm_partition_desc_t *partition;
partition = TFM_GET_CONTAINER_PTR(pth, struct spm_partition_desc_t,
sp_thrd);
return partition->static_data.partition_id;
}
static struct tfm_thrd_ctx *
tfm_spm_partition_get_thread_info_ext(uint32_t partition_idx)
{
return &g_spm_partition_db.partitions[partition_idx].sp_thrd;
}
static uint32_t tfm_spm_partition_get_stack_size_ext(uint32_t partition_idx)
{
return g_spm_partition_db.partitions[partition_idx].stack_size;
}
static uint32_t tfm_spm_partition_get_stack_limit_ext(uint32_t partition_idx)
{
return g_spm_partition_db.partitions[partition_idx].stack_limit;
}
static uint32_t tfm_spm_partition_get_stack_base_ext(uint32_t partition_idx)
{
return tfm_spm_partition_get_stack_limit_ext(partition_idx) + tfm_spm_partition_get_stack_size_ext(partition_idx);
}
static tfm_thrd_func_t
tfm_spm_partition_get_init_func_ext(uint32_t partition_idx)
{
return (tfm_thrd_func_t)(g_spm_partition_db.partitions[partition_idx].
static_data.partition_init);
}
static uint32_t tfm_spm_partition_get_priority_ext(uint32_t partition_idx)
{
return g_spm_partition_db.partitions[partition_idx].static_data.
partition_priority;
}
/* Macros to pick linker symbols and allow references to sections in all level*/
#define REGION_DECLARE_EXT(a, b, c) extern uint32_t REGION_NAME(a, b, c)
REGION_DECLARE_EXT(Image$$, ARM_LIB_HEAP, $$ZI$$Base);
REGION_DECLARE_EXT(Image$$, ARM_LIB_HEAP, $$ZI$$Limit);
REGION_DECLARE_EXT(Image$$, ER_TFM_DATA, $$Base);
REGION_DECLARE_EXT(Image$$, ER_TFM_DATA, $$Limit);
REGION_DECLARE_EXT(Image$$, TFM_SECURE_STACK, $$ZI$$Base);
REGION_DECLARE_EXT(Image$$, TFM_SECURE_STACK, $$ZI$$Limit);
REGION_DECLARE_EXT(Image$$, TFM_UNPRIV_SCRATCH, $$ZI$$Base);
REGION_DECLARE_EXT(Image$$, TFM_UNPRIV_SCRATCH, $$ZI$$Limit);
/*
* \brief Check the memory whether in the given range.
*
* \param[in] buffer Pointer of memory reference
* \param[in] len Length of memory reference in bytes
* \param[in] base The base address
* \param[in] limit The limit address, the first byte of next
* area memory
*
* \retval IPC_SUCCESS Success
* \retval IPC_ERROR_MEMORY_CHECK Check failed
*/
static int32_t memory_check_range(const void *buffer, size_t len,
uintptr_t base, uintptr_t limit)
{
if (((uintptr_t)buffer >= base) &&
((uintptr_t)((uint8_t *)buffer + len - 1) < limit)) {
return IPC_SUCCESS;
}
return IPC_ERROR_MEMORY_CHECK;
}
/* FixMe: This is only valid for TFM LVL 1 now */
int32_t tfm_memory_check(void *buffer, size_t len, int32_t ns_caller)
{
uintptr_t base, limit;
/* If len is zero, this indicates an empty buffer and base is ignored */
if (len == 0) {
return IPC_SUCCESS;
}
if (!buffer) {
return IPC_ERROR_BAD_PARAMETERS;
}
if ((uintptr_t)buffer > (UINTPTR_MAX - len)) {
return IPC_ERROR_MEMORY_CHECK;
}
if (ns_caller) {
base = (uintptr_t)NS_DATA_START;
limit = (uintptr_t)(NS_DATA_START + NS_DATA_SIZE);
if (memory_check_range(buffer, len, base, limit) == IPC_SUCCESS) {
return IPC_SUCCESS;
}
base = (uintptr_t)NS_CODE_START;
limit = (uintptr_t)(NS_CODE_START + NS_CODE_SIZE);
if (memory_check_range(buffer, len, base, limit) == IPC_SUCCESS) {
return IPC_SUCCESS;
}
} else {
base = (uintptr_t)&REGION_NAME(Image$$, ARM_LIB_HEAP, $$ZI$$Base);
limit = (uintptr_t)&REGION_NAME(Image$$, ARM_LIB_HEAP, $$ZI$$Limit);
if (memory_check_range(buffer, len, base, limit) == IPC_SUCCESS) {
return IPC_SUCCESS;
}
base = (uintptr_t)&REGION_NAME(Image$$, ER_TFM_DATA, $$Base);
limit = (uintptr_t)&REGION_NAME(Image$$, ER_TFM_DATA, $$Limit);
if (memory_check_range(buffer, len, base, limit) == IPC_SUCCESS) {
return IPC_SUCCESS;
}
base = (uintptr_t)&REGION_NAME(Image$$, TFM_SECURE_STACK, $$ZI$$Base);
limit = (uintptr_t)&REGION_NAME(Image$$, TFM_SECURE_STACK, $$ZI$$Limit);
if (memory_check_range(buffer, len, base, limit) == IPC_SUCCESS) {
return IPC_SUCCESS;
}
base = (uintptr_t)&REGION_NAME(Image$$, TFM_UNPRIV_SCRATCH, $$ZI$$Base);
limit = (uintptr_t)&REGION_NAME(Image$$, TFM_UNPRIV_SCRATCH,
$$ZI$$Limit);
if (memory_check_range(buffer, len, base, limit) == IPC_SUCCESS) {
return IPC_SUCCESS;
}
}
return IPC_ERROR_MEMORY_CHECK;
}
/********************** SPM functions for thread mode ************************/
void tfm_spm_init(void)
{
uint32_t i, num;
struct tfm_spm_ipc_partition_t *partition;
struct tfm_spm_service_t *service;
struct tfm_thrd_ctx *pth;
tfm_pool_init(conn_handle_pool,
POOL_BUFFER_SIZE(conn_handle_pool),
sizeof(struct tfm_conn_handle_t),
TFM_CONN_HANDLE_MAX_NUM);
tfm_pool_init(spm_service_pool, POOL_BUFFER_SIZE(spm_service_pool),
sizeof(struct tfm_spm_service_t),
TFM_SPM_MAX_ROT_SERV_NUM);
tfm_pool_init(msg_db_pool, POOL_BUFFER_SIZE(msg_db_pool),
sizeof(struct tfm_msg_body_t),
TFM_MSG_QUEUE_MAX_MSG_NUM);
/* Init partition first for it will be used when init service */
for (i = 0; i < SPM_MAX_PARTITIONS; i++) {
if ((tfm_spm_partition_get_flags(i) & SPM_PART_FLAG_IPC) == 0) {
continue;
}
g_spm_ipc_partition[i].index = i;
g_spm_ipc_partition[i].id = tfm_spm_partition_get_partition_id(i);
tfm_event_init(&g_spm_ipc_partition[i].signal_event, EVENT_STAT_WAITED);
tfm_list_init(&g_spm_ipc_partition[i].service_list);
pth = tfm_spm_partition_get_thread_info_ext(i);
if (!pth) {
tfm_panic();
}
tfm_thrd_init(pth,
tfm_spm_partition_get_init_func_ext(i),
NULL,
(uint8_t *)tfm_spm_partition_get_stack_base_ext(i),
(uint8_t *)tfm_spm_partition_get_stack_limit_ext(i));
pth->prior = tfm_spm_partition_get_priority_ext(i);
/* Kick off */
if (tfm_thrd_start(pth) != THRD_SUCCESS) {
tfm_panic();
}
}
/* Init Service */
num = sizeof(g_spm_service_db) / sizeof(struct tfm_spm_service_db_t);
for (i = 0; i < num; i++) {
partition =
tfm_spm_get_partition_by_id(g_spm_service_db[i].partition_id);
if (!partition) {
tfm_panic();
}
service = (struct tfm_spm_service_t *)tfm_pool_alloc(spm_service_pool);
if (!service) {
tfm_panic();
}
service->service_db = &g_spm_service_db[i];
service->partition = partition;
tfm_list_init(&service->handle_list);
tfm_list_add_tail(&partition->service_list, &service->list);
}
/* All thread inited.... trigger scheduler */
tfm_thrd_activate_schedule();
}

View File

@ -0,0 +1,976 @@
/*
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include "psa_client.h"
#include "psa_service.h"
#include "tfm_svc.h"
#include "tfm_svcalls.h"
#include "tfm_thread.h"
#include "tfm_wait.h"
#include "tfm_utils.h"
#include "tfm_internal_defines.h"
#include "tfm_message_queue.h"
#include "tfm_spm.h"
#include "secure_utilities.h"
#include "tfm_api.h"
#include "tfm_secure_api.h"
#define PSA_TIMEOUT_MASK PSA_BLOCK
/************************* SVC handler for PSA Client APIs *******************/
uint32_t tfm_svcall_psa_framework_version(void)
{
return PSA_FRAMEWORK_VERSION;
}
uint32_t tfm_svcall_psa_version(uint32_t *args, int32_t ns_caller)
{
uint32_t sid;
struct tfm_spm_service_t *service;
TFM_ASSERT(args != NULL);
sid = (uint32_t)args[0];
/*
* It should return PSA_VERSION_NONE if the RoT Service is not
* implemented.
*/
service = tfm_spm_get_service_by_sid(sid);
if (!service) {
return PSA_VERSION_NONE;
}
/*
* It should return PSA_VERSION_NONE if the caller is not authorized
* to access the RoT Service.
*/
if (ns_caller && !service->service_db->non_secure_client) {
return PSA_VERSION_NONE;
}
return service->service_db->minor_version;
}
psa_handle_t tfm_svcall_psa_connect(uint32_t *args, int32_t ns_caller)
{
uint32_t sid;
uint32_t minor_version;
struct tfm_spm_service_t *service;
struct tfm_msg_body_t *msg;
TFM_ASSERT(args != NULL);
sid = (uint32_t)args[0];
minor_version = (uint32_t)args[1];
/* It is a fatal error if the RoT Service does not exist on the platform */
service = tfm_spm_get_service_by_sid(sid);
if (!service) {
tfm_panic();
}
/*
* It is a fatal error if the caller is not authorized to access the RoT
* Service.
*/
if (ns_caller && !service->service_db->non_secure_client) {
tfm_panic();
}
/*
* It is a fatal error if the version of the RoT Service requested is not
* supported on the platform.
*/
if (tfm_spm_check_client_version(service, minor_version) != IPC_SUCCESS) {
tfm_panic();
}
/* No input or output needed for connect message */
msg = tfm_spm_create_msg(service, PSA_NULL_HANDLE, PSA_IPC_CONNECT,
ns_caller, NULL, 0, NULL, 0, NULL);
if (!msg) {
return PSA_NULL_HANDLE;
}
/*
* Send message and wake up the SP who is waiting on message queue,
* and scheduler triggered
*/
tfm_spm_send_event(service, msg);
return PSA_NULL_HANDLE;
}
psa_status_t tfm_svcall_psa_call(uint32_t *args, int32_t ns_caller)
{
psa_handle_t handle;
psa_invec *inptr, invecs[PSA_MAX_IOVEC];
psa_outvec *outptr, outvecs[PSA_MAX_IOVEC];
size_t in_num, out_num;
struct tfm_spm_service_t *service;
struct tfm_msg_body_t *msg;
int i;
TFM_ASSERT(args != NULL);
handle = (psa_handle_t)args[0];
if (!ns_caller) {
inptr = (psa_invec *)args[1];
in_num = (size_t)args[2];
outptr = (psa_outvec *)args[3];
/*
* FixMe: 5th parameter is pushed at stack top before SVC; plus
* exception stacked contents, 5th parameter is now at 8th position
* in SVC handler. However, if thread mode applies FloatPoint, then
* FloatPoint context is pushed into stack and then 5th parameter
* will not be args[8].
* Will refine it later.
*/
out_num = (size_t)args[8];
} else {
/*
* FixMe: From non-secure caller, vec and len are composed into a new
* struct parameter. Need to extract them.
*/
if (tfm_memory_check((void *)args[1], sizeof(uint32_t),
ns_caller) != IPC_SUCCESS) {
tfm_panic();
}
if (tfm_memory_check((void *)args[2], sizeof(uint32_t),
ns_caller) != IPC_SUCCESS) {
tfm_panic();
}
inptr = (psa_invec *)((psa_invec *)args[1])->base;
in_num = ((psa_invec *)args[1])->len;
outptr = (psa_outvec *)((psa_invec *)args[2])->base;
out_num = ((psa_invec *)args[2])->len;
}
/* It is a fatal error if in_len + out_len > PSA_MAX_IOVEC. */
if (in_num + out_num > PSA_MAX_IOVEC) {
tfm_panic();
}
/* It is a fatal error if an invalid handle was passed. */
service = tfm_spm_get_service_by_handle(handle);
if (!service) {
/* FixMe: Need to implement one mechanism to resolve this failure. */
tfm_panic();
}
/* It is a fatal error if an invalid memory reference was provide. */
if (tfm_memory_check((void *)inptr, in_num * sizeof(psa_invec),
ns_caller) != IPC_SUCCESS) {
tfm_panic();
}
if (tfm_memory_check((void *)outptr, out_num * sizeof(psa_outvec),
ns_caller) != IPC_SUCCESS) {
tfm_panic();
}
tfm_memset(invecs, 0, sizeof(invecs));
tfm_memset(outvecs, 0, sizeof(outvecs));
/* Copy the address out to avoid TOCTOU attacks. */
tfm_memcpy(invecs, inptr, in_num * sizeof(psa_invec));
tfm_memcpy(outvecs, outptr, out_num * sizeof(psa_outvec));
/*
* It is a fatal error if an invalid payload memory reference
* was provided.
*/
for (i = 0; i < in_num; i++) {
if (tfm_memory_check((void *)invecs[i].base, invecs[i].len,
ns_caller) != IPC_SUCCESS) {
tfm_panic();
}
}
for (i = 0; i < out_num; i++) {
if (tfm_memory_check(outvecs[i].base, outvecs[i].len,
ns_caller) != IPC_SUCCESS) {
tfm_panic();
}
}
/*
* FixMe: Need to check if the message is unrecognized by the RoT
* Service or incorrectly formatted.
*/
msg = tfm_spm_create_msg(service, handle, PSA_IPC_CALL, ns_caller, invecs,
in_num, outvecs, out_num, outptr);
if (!msg) {
/* FixMe: Need to implement one mechanism to resolve this failure. */
tfm_panic();
}
/*
* Send message and wake up the SP who is waiting on message queue,
* and scheduler triggered
*/
if (tfm_spm_send_event(service, msg) != IPC_SUCCESS) {
/* FixMe: Need to refine failure process here. */
tfm_panic();
}
return PSA_SUCCESS;
}
void tfm_svcall_psa_close(uint32_t *args, int32_t ns_caller)
{
psa_handle_t handle;
struct tfm_spm_service_t *service;
struct tfm_msg_body_t *msg;
TFM_ASSERT(args != NULL);
handle = args[0];
/* It will have no effect if called with the NULL handle */
if (handle == PSA_NULL_HANDLE) {
return;
}
/*
* It is a fatal error if an invalid handle was provided that is not the
* null handle..
*/
service = tfm_spm_get_service_by_handle(handle);
if (!service) {
/* FixMe: Need to implement one mechanism to resolve this failure. */
tfm_panic();
}
/* No input or output needed for close message */
msg = tfm_spm_create_msg(service, handle, PSA_IPC_DISCONNECT, ns_caller,
NULL, 0, NULL, 0, NULL);
if (!msg) {
/* FixMe: Need to implement one mechanism to resolve this failure. */
return;
}
/*
* Send message and wake up the SP who is waiting on message queue,
* and scheduler triggered
*/
tfm_spm_send_event(service, msg);
/* Service handle is not used anymore */
tfm_spm_free_conn_handle(service, handle);
}
/*********************** SVC handler for PSA Service APIs ********************/
/**
* \brief SVC handler for \ref psa_wait.
*
* \param[in] args Include all input arguments:
* signal_mask, timeout.
*
* \retval >0 At least one signal is asserted.
* \retval 0 No signals are asserted. This is only seen when
* a polling timeout is used.
*/
static psa_signal_t tfm_svcall_psa_wait(uint32_t *args)
{
psa_signal_t signal_mask;
uint32_t timeout;
struct tfm_spm_ipc_partition_t *partition = NULL;
TFM_ASSERT(args != NULL);
signal_mask = (psa_signal_t)args[0];
timeout = args[1];
/*
* Timeout[30:0] are reserved for future use.
* SPM must ignore the value of RES.
*/
timeout &= PSA_TIMEOUT_MASK;
partition = tfm_spm_get_running_partition();
if (!partition) {
tfm_panic();
}
/*
* Expected signals are included in signal wait mask, ignored signals
* should not be set and affect caller thread status. Save this mask for
* further checking while signals are ready to be set.
*/
partition->signal_mask = signal_mask;
/*
* tfm_event_wait() blocks the caller thread if no signals are available.
* In this case, the return value of this function is temporary set into
* runtime context. After new signal(s) are available, the return value
* is updated with the available signal(s) and blocked thread gets to run.
*/
if ((timeout == PSA_BLOCK) && ((partition->signals & signal_mask) == 0)) {
tfm_event_wait(&partition->signal_event);
}
return partition->signals & signal_mask;
}
/**
* \brief SVC handler for \ref psa_get.
*
* \param[in] args Include all input arguments: signal, msg.
*
* \retval PSA_SUCCESS Success, *msg will contain the delivered
* message.
* \retval PSA_ERR_NOMSG Message could not be delivered.
* \retval "Does not return" The call is invalid because one or more of the
* following are true:
* \arg signal has more than a single bit set.
* \arg signal does not correspond to a RoT Service.
* \arg The RoT Service signal is not currently
* asserted.
* \arg The msg pointer provided is not a valid memory
* reference.
*/
static psa_status_t tfm_svcall_psa_get(uint32_t *args)
{
psa_signal_t signal;
psa_msg_t *msg = NULL;
struct tfm_spm_service_t *service = NULL;
struct tfm_msg_body_t *tmp_msg = NULL;
struct tfm_spm_ipc_partition_t *partition = NULL;
TFM_ASSERT(args != NULL);
signal = (psa_signal_t)args[0];
msg = (psa_msg_t *)args[1];
/*
* Only one message could be retrieved every time for psa_get(). It is a
* fatal error if the input signal has more than a signal bit set.
*/
if (tfm_bitcount(signal) != 1) {
tfm_panic();
}
/*
* It is a fatal error if the input msg pointer is not a valid memory
* reference.
*/
if (tfm_memory_check((void *)msg, sizeof(psa_msg_t),
false) != IPC_SUCCESS) {
tfm_panic();
}
partition = tfm_spm_get_running_partition();
if (!partition) {
tfm_panic();
}
/*
* It is a fatal error if the caller call psa_get() when no message has
* been set. The caller must call this function after a RoT Service signal
* is returned by psa_wait().
*/
if (partition->signals == 0) {
tfm_panic();
}
/*
* It is a fatal error if the RoT Service signal is not currently asserted.
*/
if ((partition->signals & signal) == 0) {
tfm_panic();
}
/*
* Get Rot service by signal from partition. It is a fatal error if geting
* failed which mean the input signal is not correspond to a RoT service.
*/
service = tfm_spm_get_service_by_signal(partition, signal);
if (!service) {
tfm_panic();
}
tmp_msg = tfm_msg_dequeue(&service->msg_queue);
if (!tmp_msg) {
return PSA_ERR_NOMSG;
}
tfm_memcpy(msg, &tmp_msg->msg, sizeof(psa_msg_t));
/*
* There may be mutiple messages for this RoT Service signal, do not clear
* its mask until no remaining message.
*/
if (tfm_msg_queue_is_empty(&service->msg_queue)) {
partition->signals &= ~signal;
}
return PSA_SUCCESS;
}
/**
* \brief SVC handler for \ref psa_set_rhandle.
*
* \param[in] args Include all input arguments:
* msg_handle, rhandle.
*
* \retval void Success, rhandle will be provided with all
* subsequent messages delivered on this
* connection.
* \retval "Does not return" msg_handle is invalid.
*/
static void tfm_svcall_psa_set_rhandle(uint32_t *args)
{
psa_handle_t msg_handle;
void *rhandle = NULL;
struct tfm_msg_body_t *msg = NULL;
TFM_ASSERT(args != NULL);
msg_handle = (psa_handle_t)args[0];
rhandle = (void *)args[1];
/* It is a fatal error if message handle is invalid */
msg = tfm_spm_get_msg_from_handle(msg_handle);
if (!msg) {
tfm_panic();
}
/*
* Connection handle is not created while SP is processing PSA_IPC_CONNECT
* message. Store reverse handle temporarily and re-set it after the
* connection created.
*/
if (msg->handle != PSA_NULL_HANDLE) {
tfm_spm_set_rhandle(msg->service, msg->handle, rhandle);
} else {
msg->msg.rhandle = rhandle;
}
}
/**
* \brief SVC handler for \ref psa_read.
*
* \param[in] args Include all input arguments:
* msg_handle, invec_idx, buffer, num_bytes.
*
* \retval >0 Number of bytes copied.
* \retval 0 There was no remaining data in this input
* vector.
* \retval "Does not return" The call is invalid, one or more of the
* following are true:
* \arg msg_handle is invalid.
* \arg msg_handle does not refer to a
* \ref PSA_IPC_CALL message.
* \arg invec_idx is equal to or greater than
* \ref PSA_MAX_IOVEC.
* \arg the memory reference for buffer is invalid or
* not writable.
*/
static size_t tfm_svcall_psa_read(uint32_t *args)
{
psa_handle_t msg_handle;
uint32_t invec_idx;
void *buffer = NULL;
size_t num_bytes;
size_t bytes;
struct tfm_msg_body_t *msg = NULL;
TFM_ASSERT(args != NULL);
msg_handle = (psa_handle_t)args[0];
invec_idx = args[1];
buffer = (void *)args[2];
num_bytes = (size_t)args[3];
/* It is a fatal error if message handle is invalid */
msg = tfm_spm_get_msg_from_handle(msg_handle);
if (!msg) {
tfm_panic();
}
/*
* It is a fatal error if message handle does not refer to a PSA_IPC_CALL
* message
*/
if (msg->msg.type != PSA_IPC_CALL) {
tfm_panic();
}
/*
* It is a fatal error if invec_idx is equal to or greater than
* PSA_MAX_IOVEC
*/
if (invec_idx >= PSA_MAX_IOVEC) {
tfm_panic();
}
/* There was no remaining data in this input vector */
if (msg->msg.in_size[invec_idx] == 0) {
return 0;
}
/*
* It is a fatal error if the memory reference for buffer is invalid or
* not writable
*/
/* FixMe: write permission check to be added */
if (tfm_memory_check(buffer, num_bytes, false) != IPC_SUCCESS) {
tfm_panic();
}
bytes = num_bytes > msg->msg.in_size[invec_idx] ?
msg->msg.in_size[invec_idx] : num_bytes;
tfm_memcpy(buffer, msg->invec[invec_idx].base, bytes);
/* There maybe some remaining data */
msg->invec[invec_idx].base += bytes;
msg->msg.in_size[invec_idx] -= bytes;
return bytes;
}
/**
* \brief SVC handler for \ref psa_skip.
*
* \param[in] args Include all input arguments:
* msg_handle, invec_idx, num_bytes.
*
* \retval >0 Number of bytes skipped.
* \retval 0 There was no remaining data in this input
* vector.
* \retval "Does not return" The call is invalid, one or more of the
* following are true:
* \arg msg_handle is invalid.
* \arg msg_handle does not refer to a
* \ref PSA_IPC_CALL message.
* \arg invec_idx is equal to or greater than
* \ref PSA_MAX_IOVEC.
*/
static size_t tfm_svcall_psa_skip(uint32_t *args)
{
psa_handle_t msg_handle;
uint32_t invec_idx;
size_t num_bytes;
struct tfm_msg_body_t *msg = NULL;
TFM_ASSERT(args != NULL);
msg_handle = (psa_handle_t)args[0];
invec_idx = args[1];
num_bytes = (size_t)args[2];
/* It is a fatal error if message handle is invalid */
msg = tfm_spm_get_msg_from_handle(msg_handle);
if (!msg) {
tfm_panic();
}
/*
* It is a fatal error if message handle does not refer to a PSA_IPC_CALL
* message
*/
if (msg->msg.type != PSA_IPC_CALL) {
tfm_panic();
}
/*
* It is a fatal error if invec_idx is equal to or greater than
* PSA_MAX_IOVEC
*/
if (invec_idx >= PSA_MAX_IOVEC) {
tfm_panic();
}
/* There was no remaining data in this input vector */
if (msg->msg.in_size[invec_idx] == 0) {
return 0;
}
/*
* If num_bytes is greater than the remaining size of the input vector then
* the remaining size of the input vector is used.
*/
if (num_bytes > msg->msg.in_size[invec_idx]) {
num_bytes = msg->msg.in_size[invec_idx];
}
/* There maybe some remaining data */
msg->invec[invec_idx].base += num_bytes;
msg->msg.in_size[invec_idx] -= num_bytes;
return num_bytes;
}
/**
* \brief SVC handler for \ref psa_write.
*
* \param[in] args Include all input arguments:
* msg_handle, outvec_idx, buffer, num_bytes.
*
* \retval void Success
* \retval "Does not return" The call is invalid, one or more of the
* following are true:
* \arg msg_handle is invalid.
* \arg msg_handle does not refer to a
* \ref PSA_IPC_CALL message.
* \arg outvec_idx is equal to or greater than
* \ref PSA_MAX_IOVEC.
* \arg The memory reference for buffer is invalid.
* \arg The call attempts to write data past the end
* of the client output vector.
*/
static void tfm_svcall_psa_write(uint32_t *args)
{
psa_handle_t msg_handle;
uint32_t outvec_idx;
void *buffer = NULL;
size_t num_bytes;
struct tfm_msg_body_t *msg = NULL;
TFM_ASSERT(args != NULL);
msg_handle = (psa_handle_t)args[0];
outvec_idx = args[1];
buffer = (void *)args[2];
num_bytes = (size_t)args[3];
/* It is a fatal error if message handle is invalid */
msg = tfm_spm_get_msg_from_handle(msg_handle);
if (!msg) {
tfm_panic();
}
/*
* It is a fatal error if message handle does not refer to a PSA_IPC_CALL
* message
*/
if (msg->msg.type != PSA_IPC_CALL) {
tfm_panic();
}
/*
* It is a fatal error if outvec_idx is equal to or greater than
* PSA_MAX_IOVEC
*/
if (outvec_idx >= PSA_MAX_IOVEC) {
tfm_panic();
}
/*
* It is a fatal error if the call attempts to write data past the end of
* the client output vector
*/
if (num_bytes > msg->msg.out_size[outvec_idx] -
msg->outvec[outvec_idx].len) {
tfm_panic();
}
/* It is a fatal error if the memory reference for buffer is valid */
if (tfm_memory_check(buffer, num_bytes, false) != IPC_SUCCESS) {
tfm_panic();
}
tfm_memcpy(msg->outvec[outvec_idx].base + msg->outvec[outvec_idx].len,
buffer, num_bytes);
/* Update the write number */
msg->outvec[outvec_idx].len += num_bytes;
}
static void update_caller_outvec_len(struct tfm_msg_body_t *msg)
{
int32_t i = 0;
/*
* FixeMe: abstract these part into dedicated functions to avoid
* accessing thread context in psa layer
*/
TFM_ASSERT(msg->ack_mtx.owner->status == THRD_STAT_BLOCK);
while (msg->msg.out_size[i] != 0) {
TFM_ASSERT(msg->caller_outvec[i].base == msg->outvec[i].base);
msg->caller_outvec[i].len = msg->outvec[i].len;
i++;
}
}
/**
* \brief SVC handler for \ref psa_reply.
*
* \param[in] args Include all input arguments:
* msg_handle, status.
*
* \retval void Success.
* \retval "Does not return" The call is invalid, one or more of the
* following are true:
* \arg msg_handle is invalid.
* \arg An invalid status code is specified for the
* type of message.
*/
static void tfm_svcall_psa_reply(uint32_t *args)
{
psa_handle_t msg_handle;
psa_status_t status;
struct tfm_spm_service_t *service = NULL;
struct tfm_msg_body_t *msg = NULL;
psa_handle_t connect_handle;
int32_t ret = PSA_SUCCESS;
TFM_ASSERT(args != NULL);
msg_handle = (psa_handle_t)args[0];
status = (psa_status_t)args[1];
/* It is a fatal error if message handle is invalid */
msg = tfm_spm_get_msg_from_handle(msg_handle);
if (!msg) {
tfm_panic();
}
/*
* RoT Service information is needed in this function, stored it in message
* body structure. Only two parameters are passed in this function: handle
* and status, so it is useful and simply to do like this.
*/
service = msg->service;
if (!service) {
tfm_panic();
}
/*
* Three type of message are passed in this function: CONNECT, CALL,
* DISCONNECT. It needs to process differently for each type.
*/
switch (msg->msg.type) {
case PSA_IPC_CONNECT:
/*
* Reply to PSA_IPC_CONNECT message. Connect handle is created if the
* input status is PSA_SUCCESS. Others return values are based on the
* input status.
*/
if (status == PSA_SUCCESS) {
connect_handle = tfm_spm_create_conn_handle(service);
if (connect_handle == PSA_NULL_HANDLE) {
tfm_panic();
}
ret = connect_handle;
/* Set reverse handle after connection created if needed. */
if (msg->msg.rhandle) {
tfm_spm_set_rhandle(service, connect_handle, msg->msg.rhandle);
}
} else if (status == PSA_CONNECTION_REFUSED) {
ret = PSA_CONNECTION_REFUSED;
} else if (status == PSA_CONNECTION_BUSY) {
ret = PSA_CONNECTION_BUSY;
} else {
tfm_panic();
}
break;
case PSA_IPC_CALL:
/* Reply to PSA_IPC_CALL message. Return values are based on status */
if (status == PSA_SUCCESS) {
ret = PSA_SUCCESS;
} else if (status == PSA_DROP_CONNECTION) {
ret = PSA_DROP_CONNECTION;
} else if ((status >= (INT32_MIN + 1)) &&
(status <= (INT32_MIN + 127))) {
tfm_panic();
} else if ((status >= (INT32_MIN + 128)) && (status <= -1)) {
ret = status;
} else if ((status >= 1) && (status <= INT32_MAX)) {
ret = status;
} else {
tfm_panic();
}
/*
* The total number of bytes written to a single parameter must be
* reported to the client by updating the len member of the psa_outvec
* structure for the parameter before returning from psa_call().
*/
update_caller_outvec_len(msg);
break;
case PSA_IPC_DISCONNECT:
/*
* If the message type is PSA_IPC_DISCONNECT, then the status code is
* ignored
*/
break;
default:
tfm_panic();
}
/* Save return value for blocked threads */
tfm_event_owner_retval(&msg->ack_mtx, ret);
/* Wake waiting thread up */
tfm_event_signal(&msg->ack_mtx);
/* Message should not be unsed anymore */
tfm_spm_free_msg(msg);
}
/**
* \brief SVC handler for \ref psa_notify.
*
* \param[in] args Include all input arguments: partition_id.
*
* \retval void Success.
* \retval "Does not return" partition_id does not correspond to a Secure
* Partition.
*/
static void tfm_svcall_psa_notify(uint32_t *args)
{
int32_t partition_id;
struct tfm_spm_ipc_partition_t *partition = NULL;
TFM_ASSERT(args != NULL);
partition_id = (int32_t)args[0];
/*
* The value of partition_id must be greater than zero as the target of
* notification must be a Secure Partition, providing a Non-secure
* Partition ID is a fatal error.
*/
if (!TFM_CLIENT_ID_IS_S(partition_id)) {
tfm_panic();
}
/*
* It is a fatal error if partition_id does not correspond to a Secure
* Partition.
*/
partition = tfm_spm_get_partition_by_id(partition_id);
if (!partition) {
tfm_panic();
}
partition->signals |= PSA_DOORBELL;
/*
* The target partition may be blocked with waiting for signals after
* called psa_wait(). Set the return value with the available signals
* before wake it up with tfm_event_signal().
*/
tfm_event_owner_retval(&partition->signal_event,
partition->signals & partition->signal_mask);
/* Wake waiting thread up */
tfm_event_signal(&partition->signal_event);
}
/**
* \brief SVC handler for \ref psa_clear.
*
* \retval void Success.
* \retval "Does not return" The Secure Partition's doorbell signal is not
* currently asserted.
*/
static void tfm_svcall_psa_clear(uint32_t *args)
{
struct tfm_spm_ipc_partition_t *partition = NULL;
partition = tfm_spm_get_running_partition();
if (!partition) {
tfm_panic();
}
/*
* It is a fatal error if the Secure Partition's doorbell signal is not
* currently asserted.
*/
if ((partition->signals & PSA_DOORBELL) == 0) {
tfm_panic();
}
partition->signals &= ~PSA_DOORBELL;
}
/**
* \brief SVC handler for \ref psa_eoi.
*
* \param[in] args Include all input arguments: irq_signal.
*
* \retval void Success.
* \retval "Does not return" The call is invalid, one or more of the
* following are true:
* \arg irq_signal is not an interrupt signal.
* \arg irq_signal indicates more than one signal.
* \arg irq_signal is not currently asserted.
*/
static void tfm_svcall_psa_eoi(uint32_t *args)
{
psa_signal_t irq_signal;
struct tfm_spm_ipc_partition_t *partition = NULL;
TFM_ASSERT(args != NULL);
irq_signal = (psa_signal_t)args[0];
partition = tfm_spm_get_running_partition();
if (!partition) {
tfm_panic();
}
/*
* FixMe: It is a fatal error if passed signal is not an interrupt signal.
*/
/* It is a fatal error if passed signal indicates more than one signals. */
if (tfm_bitcount(partition->signals) != 1) {
tfm_panic();
}
/* It is a fatal error if passed signal is not currently asserted */
if ((partition->signals & irq_signal) == 0) {
tfm_panic();
}
partition->signals &= ~irq_signal;
/* FixMe: re-enable interrupt */
}
int32_t SVC_Handler_IPC(tfm_svc_number_t svc_num, uint32_t *ctx)
{
switch (svc_num) {
case TFM_SVC_SCHEDULE:
tfm_thrd_activate_schedule();
break;
case TFM_SVC_PSA_FRAMEWORK_VERSION:
return tfm_svcall_psa_framework_version();
case TFM_SVC_PSA_VERSION:
return tfm_svcall_psa_version(ctx, 0);
case TFM_SVC_PSA_CONNECT:
return tfm_svcall_psa_connect(ctx, 0);
case TFM_SVC_PSA_CALL:
return tfm_svcall_psa_call(ctx, 0);
case TFM_SVC_PSA_CLOSE:
tfm_svcall_psa_close(ctx, 0);
break;
case TFM_SVC_PSA_WAIT:
return tfm_svcall_psa_wait(ctx);
case TFM_SVC_PSA_GET:
return tfm_svcall_psa_get(ctx);
case TFM_SVC_PSA_SET_RHANDLE:
tfm_svcall_psa_set_rhandle(ctx);
break;
case TFM_SVC_PSA_READ:
return tfm_svcall_psa_read(ctx);
case TFM_SVC_PSA_SKIP:
return tfm_svcall_psa_skip(ctx);
case TFM_SVC_PSA_WRITE:
tfm_svcall_psa_write(ctx);
break;
case TFM_SVC_PSA_REPLY:
tfm_svcall_psa_reply(ctx);
break;
case TFM_SVC_PSA_NOTIFY:
tfm_svcall_psa_notify(ctx);
break;
case TFM_SVC_PSA_CLEAR:
tfm_svcall_psa_clear(ctx);
break;
case TFM_SVC_PSA_EOI:
tfm_svcall_psa_eoi(ctx);
break;
default:
break;
}
return PSA_SUCCESS;
}

View File

@ -0,0 +1,195 @@
/*
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include <inttypes.h>
#include <stdio.h>
#include "tfm_arch_v8m.h"
#include "tfm_thread.h"
#include "tfm_utils.h"
#include "secure_utilities.h"
/* Force ZERO in case ZI(bss) clear is missing */
static struct tfm_thrd_ctx *p_thrd_head = NULL;
static struct tfm_thrd_ctx *p_runn_head = NULL;
static struct tfm_thrd_ctx *p_curr_thrd = NULL;
/* Define Macro to fetch global to support future expansion (PERCPU e.g.) */
#define LIST_HEAD p_thrd_head
#define RUNN_HEAD p_runn_head
#define CURR_THRD p_curr_thrd
/* To get next running thread for scheduler */
struct tfm_thrd_ctx *tfm_thrd_next_thread(void)
{
struct tfm_thrd_ctx *pth = RUNN_HEAD;
/*
* First RUNNING thread has highest priority since threads are sorted with
* priority.
*/
while (pth && pth->status != THRD_STAT_RUNNING) {
pth = pth->next;
}
return pth;
}
/* To get current thread for caller */
struct tfm_thrd_ctx *tfm_thrd_curr_thread()
{
return CURR_THRD;
}
/* Insert a new thread into list by descending priority (Highest at head) */
static void insert_by_prior(struct tfm_thrd_ctx **head,
struct tfm_thrd_ctx *node)
{
if (*head == NULL || (node->prior <= (*head)->prior)) {
node->next = *head;
*head = node;
} else {
struct tfm_thrd_ctx *iter = *head;
while (iter->next && (node->prior > iter->next->prior)) {
iter = iter->next;
}
node->next = iter->next;
iter->next = node;
}
}
/*
* Set first running thread as head to reduce enumerate
* depth while searching for a first running thread.
*/
static void update_running_head(struct tfm_thrd_ctx **runn,
struct tfm_thrd_ctx *node)
{
if ((node->status == THRD_STAT_RUNNING) &&
(*runn == NULL || (node->prior <= (*runn)->prior))) {
*runn = node;
} else {
*runn = tfm_thrd_next_thread();
}
}
/* Set context members only. No validation here */
void tfm_thrd_init(struct tfm_thrd_ctx *pth,
tfm_thrd_func_t pfn, void *param,
uint8_t *sp_base, uint8_t *sp_top)
{
pth->prior = THRD_PRIOR_MEDIUM;
pth->status = THRD_STAT_CREATING;
pth->pfn = pfn;
pth->param = param;
pth->sp_base = sp_base;
pth->sp_top = sp_top;
}
uint32_t tfm_thrd_start(struct tfm_thrd_ctx *pth)
{
/* Validate parameters before really start */
if ((pth->status != THRD_STAT_CREATING) ||
(pth->pfn == NULL) ||
(pth->sp_base == NULL) ||
(pth->sp_top == NULL)) {
return THRD_ERR_INVALID_PARAM;
}
/* Thread management runs in handler mode; set context for thread mode. */
tfm_initialize_context(&pth->state_ctx,
(uint32_t)pth->param, (uint32_t)pth->pfn,
(uint32_t)pth->sp_base, (uint32_t)pth->sp_top);
/* Insert a new thread with priority */
insert_by_prior(&LIST_HEAD, pth);
/* Mark it as RUNNING after insertion */
tfm_thrd_set_status(pth, THRD_STAT_RUNNING);
return THRD_SUCCESS;
}
void tfm_thrd_set_status(struct tfm_thrd_ctx *pth, uint32_t new_status)
{
TFM_ASSERT(pth != NULL && new_status < THRD_STAT_INVALID);
pth->status = new_status;
update_running_head(&RUNN_HEAD, pth);
}
/*
* TEMP WORKAROUND: The caller function who called thread module init needs to
* be returned. The caller is not a thread. Create a dummy IDLE thread to
* collect caller context; and schedule back to the caller with this context
* after all other real threads blocked.
*
* This WORKAROUND needs to be removed after IPC NSPM takes place.
*/
#define DUMMY_IDLE_TAG 0xDEEDDEED
static uint8_t idle_stack[32] __attribute__((aligned(8)));
static struct tfm_thrd_ctx idle_thread;
static struct tfm_thrd_ctx *init_idle_thread(struct tfm_thrd_ctx *pth)
{
/*
* IDLE thread is a thread with the lowest priority.
* It gets scheduled after all other higher priority threads get blocked.
* The entry of IDLE thread is a dummy and has no mean.
*/
tfm_thrd_init(pth, (tfm_thrd_func_t)DUMMY_IDLE_TAG, NULL,
(uint8_t *)&idle_stack[32], (uint8_t *)idle_stack);
tfm_thrd_priority(pth, THRD_PRIOR_LOWEST);
tfm_thrd_start(pth);
return pth;
}
/* Scheduling won't happen immediately but after the exception returns */
void tfm_thrd_activate_schedule(void)
{
/*
* The current thread can be NULL only when initializing. Create the IDLE
* thread and set it as the current thread to collect caller context.
*/
if (CURR_THRD == NULL) {
CURR_THRD = init_idle_thread(&idle_thread);
}
tfm_trigger_pendsv();
}
/* Remove current thread out of the schedulable list */
void tfm_thrd_do_exit(void)
{
CURR_THRD->status = THRD_STAT_DETACH;
tfm_trigger_pendsv();
}
void tfm_thrd_context_switch(struct tfm_state_context_ext *ctxb,
struct tfm_thrd_ctx *prev,
struct tfm_thrd_ctx *next)
{
/* Update latest context into the current thread context */
tfm_memcpy(&prev->state_ctx.ctxb, ctxb, sizeof(*ctxb));
/* Update background context with next thread's context */
tfm_memcpy(ctxb, &next->state_ctx.ctxb, sizeof(next->state_ctx.ctxb));
/* Set current thread indicator with next thread */
CURR_THRD = next;
}
/*
* This function is a reference implementation for PendSV handler in
* isolation level 1. More jobs (sandboxing e.g.) need to be done while
* scheduling in other isolation levels.
*/
void tfm_pendsv_do_schedule(struct tfm_state_context_ext *ctxb)
{
struct tfm_thrd_ctx *pth = tfm_thrd_next_thread();
/* Swith context if another thread ready to run */
if (pth && pth != CURR_THRD) {
tfm_thrd_context_switch(ctxb, CURR_THRD, pth);
}
}

View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include <inttypes.h>
#include <stdio.h>
#include "tfm_utils.h"
void tfm_panic(void)
{
while (1)
;
}
int32_t tfm_bitcount(uint32_t n)
{
int32_t count = 0;
uint8_t tmp;
while (n) {
tmp = n & 0xFF;
while (tmp) {
count += tmp & 0x1;
tmp >>= 1;
}
n >>= 8;
}
return count;
}

View File

@ -0,0 +1,62 @@
/*
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include <inttypes.h>
#include <stdio.h>
#include "tfm_arch_v8m.h"
#include "tfm_thread.h"
#include "tfm_utils.h"
#include "tfm_wait.h"
void tfm_event_wait(struct tfm_event_ctx *pevt)
{
struct tfm_thrd_ctx *curr_thrd = tfm_thrd_curr_thread();
TFM_ASSERT(pevt && pevt->magic == EVENT_MAGIC);
if (pevt->status == EVENT_STAT_WAITED) {
pevt->owner = curr_thrd;
pevt->retval = TFM_STATE_1ST_ARG(&pevt->owner->state_ctx);
tfm_thrd_set_status(pevt->owner, THRD_STAT_BLOCK);
tfm_thrd_activate_schedule();
}
pevt->status = EVENT_STAT_WAITED;
}
/* Peek the status to see if caller would block. */
uint32_t tfm_event_peek(struct tfm_event_ctx *pevt)
{
TFM_ASSERT(pevt && pevt->magic == EVENT_MAGIC);
return pevt->status;
}
void tfm_event_signal(struct tfm_event_ctx *pevt)
{
TFM_ASSERT(pevt && pevt->magic == EVENT_MAGIC);
pevt->status = EVENT_STAT_SIGNALED;
/*
* Wake the blocked owner up and keep the status as EVENT_STAT_WAITED
* if there is an owner. Or the second event wait caller will return
* without block since status is EVENT_STAT_SIGNALED.
*/
if (pevt->owner && pevt->owner->status == THRD_STAT_BLOCK) {
tfm_thrd_set_status(pevt->owner, THRD_STAT_RUNNING);
tfm_thrd_set_retval(pevt->owner, pevt->retval);
pevt->status = EVENT_STAT_WAITED;
tfm_thrd_activate_schedule();
}
}
void tfm_event_owner_retval(struct tfm_event_ctx *pmtx, uint32_t retval)
{
TFM_ASSERT(pmtx && pmtx->magic == EVENT_MAGIC);
pmtx->retval = retval;
}

View File

@ -0,0 +1,132 @@
/*
* Copyright (c) 2017-2018, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef __SECURE_UTILITIES_H__
#define __SECURE_UTILITIES_H__
#include "cmsis_compiler.h"
#include "tfm_svc.h"
#include "string.h"
#define EXC_RETURN_INDICATOR (0xF << 28)
#define EXC_RETURN_SECURITY_STACK_STATUS_MASK (0x3 << 5)
#define EXC_RETURN_SECURE_STACK (1 << 6)
#define EXC_RETURN_FPU_FRAME_BASIC (1 << 4)
#define EXC_RETURN_MODE_THREAD (1 << 3)
#define EXC_RETURN_STACK_PROCESS (1 << 2)
#define EXC_RETURN_EXC_SECURE (1)
#define EXC_NUM_THREAD_MODE (0)
#define EXC_NUM_SVCALL (11)
#define EXC_NUM_PENDSV (14)
#define EXC_NUM_SYSTICK (15)
#define printf(...)
/* Disable NS exceptions by setting NS PRIMASK to 1 */
#define TFM_NS_EXC_DISABLE() __TZ_set_PRIMASK_NS(1)
/* Enable NS exceptions by setting NS PRIMASK to 0 */
#define TFM_NS_EXC_ENABLE() __TZ_set_PRIMASK_NS(0)
struct tfm_exc_stack_t {
uint32_t R0;
uint32_t R1;
uint32_t R2;
uint32_t R3;
uint32_t R12;
uint32_t LR;
uint32_t RetAddr;
uint32_t XPSR;
};
#ifdef TFM_CORE_DEBUG
#define LOG_MSG_HDLR(MSG) printf("[Sec Handler] %s\r\n", MSG)
#else
/* FixMe: redirect to secure log area */
#define LOG_MSG_HDLR(MSG) printf("[Sec Handler] %s\r\n", MSG)
#endif
#define LOG_MSG_THR(MSG) \
__ASM("MOV r0, %0\n" \
"SVC %1\n" \
: : "r" (MSG), "I" (TFM_SVC_PRINT))
#define LOG_MSG(MSG) \
do { \
if (__get_active_exc_num()) { \
LOG_MSG_HDLR(MSG); \
} else { \
LOG_MSG_THR(MSG); \
} \
} while (0)
#ifdef TFM_CORE_DEBUG
#define ERROR_MSG(MSG) printf("[Sec Error] %s\r\n", MSG)
#else
/* FixMe: redirect to secure log area */
#define ERROR_MSG(MSG) printf("[Sec Error] %s\r\n", MSG)
#endif
/**
* \brief Get Link Register
* \details Returns the value of the Link Register (LR)
* \return LR value
*/
__attribute__ ((always_inline)) __STATIC_INLINE uint32_t __get_LR(void)
{
register uint32_t result;
__ASM volatile ("MOV %0, LR\n" : "=r" (result));
return result;
}
__attribute__ ((always_inline))
__STATIC_INLINE uint32_t __get_active_exc_num(void)
{
IPSR_Type IPSR;
/* if non-zero, exception is active. NOT banked S/NS */
IPSR.w = __get_IPSR();
return IPSR.b.ISR;
}
__attribute__ ((always_inline))
__STATIC_INLINE void __set_CONTROL_SPSEL(int32_t SPSEL)
{
CONTROL_Type ctrl;
ctrl.w = __get_CONTROL();
ctrl.b.SPSEL = SPSEL;
__set_CONTROL(ctrl.w);
__asm("ISB");
}
/* FIXME: The following functions are wrappers around standard C library
* functions: memcpy, memcmp, memset
* In long term standard C library might be removed from TF-M project or
* replaced with a secure implementation due to security concerns.
*/
__attribute__ ((always_inline)) __STATIC_INLINE
void tfm_memcpy(void *dest, const void *src, uint32_t size)
{
memcpy(dest, src, size);
}
__attribute__ ((always_inline)) __STATIC_INLINE
int32_t tfm_memcmp(const void * ptr1, const void * ptr2, size_t num)
{
return (memcmp(ptr1, ptr2, num));
}
__attribute__ ((always_inline)) __STATIC_INLINE
void * tfm_memset(void * ptr, int value, size_t num)
{
return (memset(ptr, value, num));
}
#endif /* __SECURE_UTILITIES_H__ */

View File

@ -0,0 +1,124 @@
/*
* Copyright (c) 2018, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include <stdint.h>
#include "bl2/include/tfm_boot_status.h"
#include "secure_utilities.h"
#include "tfm_internal.h"
#include "tfm_api.h"
#include "flash_layout.h"
#include "secure_fw/spm/spm_api.h"
/*!
* \def BOOT_DATA_VALID
*
* \brief Indicates that shared data between bootloader and runtime firmware was
* passed the sanity check with success.
*/
#define BOOT_DATA_VALID (1u)
/*!
* \def BOOT_DATA_INVALID
*
* \brief Indicates that shared data between bootloader and runtime firmware was
* failed on sanity check.
*/
#define BOOT_DATA_INVALID (0u)
/*!
* \var is_boot_data_valid
*
* \brief Indicates the status of shared data between bootloader and runtime
* firmware
*/
static uint32_t is_boot_data_valid = BOOT_DATA_INVALID;
void tfm_core_validate_boot_data(void)
{
struct shared_data_tlv_header *tlv_header;
tlv_header = (struct shared_data_tlv_header *)BOOT_TFM_SHARED_DATA_BASE;
/* FixMe: Enhance sanity check of shared memory area, it might be invalid:
* - temporal exposure of RAM to non-secure actors
* - mismatched addresses
* - version mismatch between bootloader and runtime binary
* - etc.
*/
if (tlv_header->tlv_magic == SHARED_DATA_TLV_INFO_MAGIC) {
is_boot_data_valid = BOOT_DATA_VALID;
}
}
void tfm_core_get_boot_data_handler(uint32_t args[])
{
uint8_t tlv_major = (uint8_t)args[0];
uint8_t *ptr = (uint8_t *)args[1];
uint16_t buf_size = (uint16_t)args[2];
uint8_t *buf_start = ptr;
uint32_t running_partition_idx =
tfm_spm_partition_get_running_partition_idx();
struct shared_data_tlv_header *tlv_header;
struct shared_data_tlv_entry *tlv_entry;
uintptr_t tlv_end, offset;
uint32_t res;
/* Make sure that the output pointer points to a memory area that is owned
* by the partition
*/
res = tfm_core_check_buffer_access(running_partition_idx,
(void*)buf_start,
buf_size,
2);
if (!res) {
/* Not in accessible range, return error */
args[0] = TFM_ERROR_INVALID_PARAMETER;
return;
}
/* FixMe: Check whether caller has access right to given tlv_major_type */
if (is_boot_data_valid != BOOT_DATA_VALID) {
args[0] = TFM_ERROR_INVALID_PARAMETER;
return;
}
/* Get the boundaries of TLV section */
tlv_header = (struct shared_data_tlv_header *)BOOT_TFM_SHARED_DATA_BASE;
tlv_end = BOOT_TFM_SHARED_DATA_BASE + tlv_header->tlv_tot_len;
offset = BOOT_TFM_SHARED_DATA_BASE + SHARED_DATA_HEADER_SIZE;
/* Add header to output buffer as well */
if (buf_size < SHARED_DATA_HEADER_SIZE) {
args[0] = TFM_ERROR_INVALID_PARAMETER;
return;
} else {
tfm_memcpy(ptr, tlv_header, SHARED_DATA_HEADER_SIZE);
ptr += SHARED_DATA_HEADER_SIZE;
}
/* Iterates over the TLV section and copy TLVs with requested major
* type to the provided buffer.
*/
for(; offset < tlv_end; offset += tlv_entry->tlv_len) {
tlv_entry = (struct shared_data_tlv_entry *)offset;
if (tlv_entry->tlv_major_type == tlv_major) {
/* Check buffer overflow */
if ((ptr - buf_start + tlv_entry->tlv_len) > buf_size) {
args[0] = TFM_ERROR_INVALID_PARAMETER;
return;
}
tfm_memcpy(ptr, (const void *)tlv_entry, tlv_entry->tlv_len);
ptr += tlv_entry->tlv_len;
tlv_header->tlv_tot_len += tlv_entry->tlv_len;
}
}
args[0] = TFM_SUCCESS;
return;
}

View File

@ -0,0 +1,196 @@
/*
* Copyright (c) 2017-2018, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include <stdio.h>
#include "region_defs.h"
#include "tfm_core.h"
#include "tfm_internal.h"
#include "tfm_api.h"
#include "platform/include/tfm_spm_hal.h"
#include "secure_utilities.h"
#include "secure_fw/spm/spm_api.h"
#include "secure_fw/include/tfm_spm_services_api.h"
#ifdef TFM_PSA_API
#include "psa_client.h"
#include "psa_service.h"
#include "tfm_thread.h"
#include "tfm_wait.h"
#include "tfm_message_queue.h"
#include "tfm_spm.h"
#endif
/*
* Avoids the semihosting issue
* FixMe: describe 'semihosting issue'
*/
#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050)
__asm(" .global __ARM_use_no_argv\n");
#endif
#if defined ( __GNUC__ )
/* The macro cmse_nsfptr_create defined in the gcc library uses the non-standard
* gcc C lanuage extension 'typeof'. TF-M is built with '-std=c99' so typeof
* cannot be used in the code. As a workaround cmse_nsfptr_create is redefined
* here to use only standard language elements. */
#undef cmse_nsfptr_create
#define cmse_nsfptr_create(p) ((intptr_t) (p) & ~1)
#endif
#ifndef TFM_LVL
#error TFM_LVL is not defined!
#endif
#if (TFM_LVL != 1) && (TFM_LVL != 3)
#error Only TFM_LVL 1 and 3 are supported!
#endif
/* Macros to pick linker symbols and allow to form the partition data base */
#define REGION(a, b, c) a##b##c
#define REGION_NAME(a, b, c) REGION(a, b, c)
#define REGION_DECLARE(a, b, c) extern uint32_t REGION_NAME(a, b, c)
REGION_DECLARE(Image$$, TFM_UNPRIV_SCRATCH, $$ZI$$Base);
REGION_DECLARE(Image$$, TFM_UNPRIV_SCRATCH, $$ZI$$Limit);
void configure_ns_code(void)
{
/* SCB_NS.VTOR points to the Non-secure vector table base address */
SCB_NS->VTOR = tfm_spm_hal_get_ns_VTOR();
/* Setups Main stack pointer of the non-secure code */
uint32_t ns_msp = tfm_spm_hal_get_ns_MSP();
__TZ_set_MSP_NS(ns_msp);
/* Get the address of non-secure code entry point to jump there */
uint32_t entry_ptr = tfm_spm_hal_get_ns_entry_point();
/* Clears LSB of the function address to indicate the function-call
* will perform the switch from secure to non-secure
*/
ns_entry = (nsfptr_t) cmse_nsfptr_create(entry_ptr);
}
int32_t tfm_core_init(void)
{
/* Enables fault handlers */
enable_fault_handlers();
/* Configures the system reset request properties */
system_reset_cfg();
/* Configures debug authentication */
tfm_spm_hal_init_debug();
__enable_irq();
LOG_MSG("Secure image initializing!");
#ifdef TFM_CORE_DEBUG
printf("TFM level is: %d\r\n", TFM_LVL);
#endif
tfm_core_validate_boot_data();
tfm_spm_hal_init_isolation_hw();
configure_ns_code();
/* Configures all interrupts to retarget NS state, except for
* secure peripherals
*/
nvic_interrupt_target_state_cfg();
/* Enable secure peripherals interrupts */
nvic_interrupt_enable();
tfm_scratch_area =
(uint8_t *)&REGION_NAME(Image$$, TFM_UNPRIV_SCRATCH, $$ZI$$Base);
tfm_scratch_area_size =
(uint32_t)&REGION_NAME(Image$$, TFM_UNPRIV_SCRATCH, $$ZI$$Limit) -
(uint32_t)&REGION_NAME(Image$$, TFM_UNPRIV_SCRATCH, $$ZI$$Base);
return 0;
}
static int32_t tfm_core_set_secure_exception_priorities(void)
{
uint32_t VECTKEY;
SCB_Type *scb = SCB;
uint32_t AIRCR;
/* Set PRIS flag is AIRCR */
AIRCR = scb->AIRCR;
VECTKEY = (~AIRCR & SCB_AIRCR_VECTKEYSTAT_Msk);
scb->AIRCR = SCB_AIRCR_PRIS_Msk |
VECTKEY |
(AIRCR & ~SCB_AIRCR_VECTKEY_Msk);
/* FixMe: Explicitly set secure fault and Secure SVC priority to highest */
return TFM_SUCCESS;
}
void tfm_core_spm_request_handler(const struct tfm_exc_stack_t *svc_ctx)
{
uint32_t *res_ptr = (uint32_t *)&svc_ctx->R0;
/* FixMe: check if caller partition is permitted to make an SPM request */
switch (svc_ctx->R0) {
case TFM_SPM_REQUEST_RESET_VOTE:
/* FixMe: this is a placeholder for checks to be performed before
* allowing execution of reset
*/
*res_ptr = TFM_SUCCESS;
break;
default:
*res_ptr = TFM_ERROR_INVALID_PARAMETER;
}
}
int main(void)
{
tfm_core_init();
tfm_spm_db_init();
tfm_spm_hal_setup_isolation_hw();
tfm_spm_partition_set_state(TFM_SP_CORE_ID, SPM_PARTITION_STATE_RUNNING);
extern uint32_t Image$$ARM_LIB_STACK$$ZI$$Base[];
uint32_t psp_stack_bottom = (uint32_t)Image$$ARM_LIB_STACK$$ZI$$Base;
__set_PSPLIM(psp_stack_bottom);
if (tfm_spm_partition_init() != SPM_ERR_OK) {
/* Certain systems might refuse to boot altogether if partitions fail
* to initialize. This is a placeholder for such an error handler
*/
}
#ifdef TFM_PSA_API
tfm_spm_init();
#endif
#ifdef TFM_CORE_DEBUG
/* Jumps to non-secure code */
LOG_MSG("Jumping to non-secure code...");
#endif
/* We close the TFM_SP_CORE_ID partition, because its only purpose is
* to be able to pass the state checks for the tests started from secure.
*/
tfm_spm_partition_set_state(TFM_SP_CORE_ID, SPM_PARTITION_STATE_CLOSED);
tfm_spm_partition_set_state(TFM_SP_NON_SECURE_ID,
SPM_PARTITION_STATE_RUNNING);
/* Prioritise secure exceptions to avoid NS being able to pre-empt secure
* SVC or SecureFault
*/
tfm_core_set_secure_exception_priorities();
jump_to_ns_code();
}

View File

@ -0,0 +1,18 @@
/*
* Copyright (c) 2017, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef __TFM_CORE_H__
#define __TFM_CORE_H__
#include "arm_cmse.h"
#include "tfm_svc.h"
#include "secure_utilities.h"
extern int32_t tfm_scratch_area_size;
extern uint8_t *tfm_scratch_area;
#endif /* __TFM_CORE_H__ */

View File

@ -0,0 +1,224 @@
/*
* Copyright (c) 2017-2018, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include <stdio.h>
#include <string.h>
#include "cmsis.h"
#include "secure_utilities.h"
#include "arm_acle.h"
#include "tfm_svc.h"
#include "tfm_secure_api.h"
#include "region_defs.h"
#include "tfm_api.h"
#include "tfm_internal.h"
#ifdef TFM_PSA_API
#include <stdbool.h>
#include "tfm_svcalls.h"
#endif
/* This SVC handler is called when a secure partition requests access to a
* buffer area
*/
extern int32_t tfm_core_set_buffer_area_handler(const uint32_t args[]);
#ifdef TFM_PSA_API
extern void tfm_psa_ipc_request_handler(const uint32_t svc_args[]);
#endif
struct tfm_fault_context_s {
uint32_t R0;
uint32_t R1;
uint32_t R2;
uint32_t R3;
uint32_t R12;
uint32_t LR;
uint32_t ReturnAddress;
uint32_t RETPSR;
} tfm_fault_context;
#if defined(__ARM_ARCH_8M_MAIN__)
/**
* \brief Overwrites default Secure fault handler.
*/
void SecureFault_Handler(void)
{
/* figure out context from which we landed in fault handler */
uint32_t lr = __get_LR();
uint32_t sp;
if (lr & EXC_RETURN_SECURE_STACK) {
if (lr & EXC_RETURN_STACK_PROCESS) {
sp = __get_PSP();
} else {
sp = __get_MSP();
}
} else {
if (lr & EXC_RETURN_STACK_PROCESS) {
sp = __TZ_get_PSP_NS();
} else {
sp = __TZ_get_MSP_NS();
}
}
/* Only save the context if sp is valid */
if ((sp >= S_DATA_START &&
sp <= S_DATA_LIMIT - sizeof(tfm_fault_context) + 1) ||
(sp >= NS_DATA_START &&
sp <= NS_DATA_LIMIT - sizeof(tfm_fault_context) + 1)) {
tfm_memcpy(&tfm_fault_context,
(const void *)sp,
sizeof(tfm_fault_context));
}
LOG_MSG("Oops... Secure fault!!! You're not going anywhere!");
while (1) {
;
}
}
#elif defined(__ARM_ARCH_8M_BASE__)
/**
* \brief Overwrites default Hard fault handler.
*
* In case of a baseline implementation fault conditions that would generate a
* SecureFault in a mainline implementation instead generate a Secure HardFault.
*/
void HardFault_Handler(void)
{
/* In a baseline implementation there is no way, to find out whether this is
* a hard fault triggered directly, or another fault that has been
* escalated.
*/
while (1) {
;
}
}
#else
#error "Unsupported ARM Architecture."
#endif
#if defined(__ARM_ARCH_8M_MAIN__)
__attribute__((naked)) void SVC_Handler(void)
{
__ASM(
"TST lr, #4\n" /* Check store SP in thread mode to r0 */
"IT EQ\n"
"BXEQ lr\n"
"MRS r0, PSP\n"
"MOV r1, lr\n"
"BL SVCHandler_main\n"
"BX r0\n"
);
}
#elif defined(__ARM_ARCH_8M_BASE__)
__attribute__((naked)) void SVC_Handler(void)
{
__ASM(
".syntax unified\n"
"MOVS r0, #4\n" /* Check store SP in thread mode to r0 */
"MOV r1, lr\n"
"TST r0, r1\n"
"BEQ handler\n"
"MRS r0, PSP\n" /* Coming from thread mode */
"B sp_stored\n"
"handler:\n"
"BX lr\n" /* Coming from handler mode */
"sp_stored:\n"
"MOV r1, lr\n"
"BL SVCHandler_main\n"
"BX r0\n"
);
}
#else
#error "Unsupported ARM Architecture."
#endif
uint32_t SVCHandler_main(uint32_t *svc_args, uint32_t lr)
{
uint8_t svc_number;
/*
* Stack contains:
* r0, r1, r2, r3, r12, r14 (lr), the return address and xPSR
* First argument (r0) is svc_args[0]
*/
if (lr & EXC_RETURN_SECURE_STACK) {
/* SV called directly from secure context. Check instruction for
* svc_number
*/
svc_number = ((uint8_t *)svc_args[6])[-2];
} else {
/* Secure SV executing with NS return.
* NS cannot directly trigger S SVC so this should not happen
* FixMe: check for security implications
*/
return lr;
}
switch (svc_number) {
case TFM_SVC_SFN_REQUEST:
lr = tfm_core_partition_request_svc_handler(svc_args, lr);
break;
case TFM_SVC_SFN_RETURN:
lr = tfm_core_partition_return_handler(lr);
break;
case TFM_SVC_VALIDATE_SECURE_CALLER:
tfm_core_validate_secure_caller_handler(svc_args);
break;
case TFM_SVC_GET_CALLER_CLIENT_ID:
tfm_core_get_caller_client_id_handler(svc_args);
break;
case TFM_SVC_SPM_REQUEST:
tfm_core_spm_request_handler((struct tfm_exc_stack_t *)svc_args);
break;
case TFM_SVC_MEMORY_CHECK:
tfm_core_memory_permission_check_handler(svc_args);
break;
case TFM_SVC_SET_SHARE_AREA:
tfm_core_set_buffer_area_handler(svc_args);
break;
#ifdef TFM_PSA_API
case TFM_SVC_IPC_REQUEST:
tfm_psa_ipc_request_handler(svc_args);
break;
#endif
case TFM_SVC_PRINT:
printf("\e[1;34m[Sec Thread] %s\e[0m\r\n", (char *)svc_args[0]);
break;
case TFM_SVC_GET_BOOT_DATA:
tfm_core_get_boot_data_handler(svc_args);
break;
#ifdef TFM_PSA_API
case TFM_SVC_PSA_FRAMEWORK_VERSION:
case TFM_SVC_PSA_VERSION:
case TFM_SVC_PSA_CONNECT:
case TFM_SVC_PSA_CALL:
case TFM_SVC_PSA_CLOSE:
case TFM_SVC_PSA_WAIT:
case TFM_SVC_PSA_GET:
case TFM_SVC_PSA_SET_RHANDLE:
case TFM_SVC_PSA_READ:
case TFM_SVC_PSA_SKIP:
case TFM_SVC_PSA_WRITE:
case TFM_SVC_PSA_REPLY:
case TFM_SVC_PSA_NOTIFY:
case TFM_SVC_PSA_CLEAR:
case TFM_SVC_PSA_EOI:
svc_args[0] = SVC_Handler_IPC(svc_number, svc_args);
break;
#endif
default:
LOG_MSG("Unknown SVC number requested!");
break;
}
return lr;
}
void tfm_access_violation_handler(void)
{
while (1) {
;
}
}

View File

@ -0,0 +1,100 @@
/*
* Copyright (c) 2018, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include "secure_utilities.h"
#ifndef __TFM_INTERNAL_H__
#define __TFM_INTERNAL_H__
/*
* This function pointer is meant to only hold non secure function pointers.
* It will be turned into a non-secure one (LSB cleared) before being called
* whatever happens anyway (unless cast to another function pointer type).
* Registers will be cleared before branching so that no information leaks
* from secure to non-secure world.
*/
typedef void __attribute__((cmse_nonsecure_call)) (*nsfptr_t) (void);
extern nsfptr_t ns_entry;
/**
* \brief Signal that secure partition initialisation is finished
*/
void tfm_secure_api_init_done(void);
/**
* \brief Jumps to non-secure code.
*/
void jump_to_ns_code(void);
/**
* \brief Called if veneer is running in thread mode
*/
uint32_t tfm_core_partition_request_svc_handler(
uint32_t *svc_args, uint32_t lr);
/**
* \brief Called when secure service returns
*/
uint32_t tfm_core_partition_return_handler(uint32_t lr);
/**
* \brief Called by secure service to check if client is secure
*/
void tfm_core_validate_secure_caller_handler(const uint32_t svc_args[]);
/**
* \brief Stores caller's client id in state context
*/
void tfm_core_get_caller_client_id_handler(const uint32_t svc_args[]);
/**
* \brief Checks if a secure service's access to a memory location is permitted
*/
void tfm_core_memory_permission_check_handler(const uint32_t svc_args[]);
/**
* \brief Handle an SPM request by a secure service
*/
void tfm_core_spm_request_handler(const struct tfm_exc_stack_t *svc_ctx);
/**
* \brief Check whether a buffer is ok for writing to by the privileged API
* function.
*
* This function checks whether the caller partition owns the buffer, can write
* to it, and the buffer has proper alignment.
*
* \param[in] partition_idx Partition index
* \param[in] start_addr The start address of the buffer
* \param[in] len The length of the buffer
* \param[in] alignment The expected alignment (in bits)
*
* \return 1 if the check passes, 0 otherwise.
*
* \note For a 0 long buffer the check fails.
*/
int32_t tfm_core_check_buffer_access(uint32_t partition_idx,
void *start_addr,
size_t len,
uint32_t alignment);
/**
* \brief Retrieve secure partition related data from shared memory area, which
* stores shared data between bootloader and runtime firmware.
*
* \param[in] args Pointer to stack frame, which carries input parameters.
*/
void tfm_core_get_boot_data_handler(uint32_t args[]);
/**
* \brief Validate the content of shared memory area, which stores the shared
* data between bootloader and runtime firmware.
*/
void tfm_core_validate_boot_data(void);
#endif /* __TFM_INTERNAL_H__ */

View File

@ -0,0 +1,302 @@
/*
* Copyright (c) 2018, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include <stdio.h>
#include "secure_utilities.h"
#include "tfm_api.h"
#ifndef TFM_MAX_NS_THREAD_COUNT
#define TFM_MAX_NS_THREAD_COUNT 8
#endif
#define INVALID_CLIENT_ID 0
#define DEFAULT_NS_CLIENT_ID ((int32_t)-1)
#define INVALID_NS_CLIENT_IDX (-1)
#define DEFAULT_NS_CLIENT_IDX 0
typedef uint32_t TZ_ModuleId_t;
typedef uint32_t TZ_MemoryId_t;
static struct ns_client_list_t {
int32_t ns_client_id;
int32_t next_free_index;
} NsClientIdList[TFM_MAX_NS_THREAD_COUNT];
static int32_t free_index = 0U;
static int32_t active_ns_client_idx = INVALID_NS_CLIENT_IDX;
static int get_next_ns_client_id()
{
#ifdef TFM_NS_CLIENT_IDENTIFICATION
static int32_t next_ns_client_id = DEFAULT_NS_CLIENT_ID;
if (next_ns_client_id > 0)
{
next_ns_client_id = DEFAULT_NS_CLIENT_ID;
}
return next_ns_client_id--;
#else
return DEFAULT_NS_CLIENT_ID;
#endif
}
void tfm_nspm_configure_clients(void)
{
int32_t i;
/* Default to one NS client */
free_index = 1;
NsClientIdList[0].ns_client_id = get_next_ns_client_id();
for (i = 1; i < TFM_MAX_NS_THREAD_COUNT; ++i) {
NsClientIdList[i].ns_client_id = INVALID_CLIENT_ID;
}
active_ns_client_idx = DEFAULT_NS_CLIENT_IDX;
}
int32_t tfm_nspm_get_current_client_id()
{
if (active_ns_client_idx == INVALID_NS_CLIENT_IDX)
{
return 0;
} else {
return NsClientIdList[active_ns_client_idx].ns_client_id;
}
}
/* TF-M implementation of the CMSIS TZ RTOS thread context management API */
/// Initialize secure context memory system
/// \return execution status (1: success, 0: error)
/* This veneer is TF-M internal, not a secure service */
__attribute__((cmse_nonsecure_entry))
uint32_t TZ_InitContextSystem_S(void)
{
int32_t i;
if (__get_active_exc_num() == EXC_NUM_THREAD_MODE) {
/* This veneer should only be called by NS RTOS in handler mode */
return 0U;
}
/* NS RTOS supports TZ context management, override defaults */
#ifdef PRINT_NSPM_DEBUG
LOG_MSG("NS RTOS initialized TZ RTOS context management");
#endif /* PRINT_NSPM_DEBUG */
for (i = 1; i < TFM_MAX_NS_THREAD_COUNT; ++i) {
NsClientIdList[i].ns_client_id = INVALID_CLIENT_ID;
NsClientIdList[i].next_free_index = i + 1;
}
/* Terminate list */
NsClientIdList[i - 1].next_free_index = INVALID_NS_CLIENT_IDX;
/* Success */
return 1U;
}
/// Allocate context memory for calling secure software modules in TrustZone
/// \param[in] module identifies software modules called from non-secure mode
/// \return value != 0 id TrustZone memory slot identifier
/// \return value 0 no memory available or internal error
/* This veneer is TF-M internal, not a secure service */
__attribute__((cmse_nonsecure_entry))
TZ_MemoryId_t TZ_AllocModuleContext_S (TZ_ModuleId_t module)
{
TZ_MemoryId_t tz_id;
(void) module; /* Currently unused */
if (__get_active_exc_num() == EXC_NUM_THREAD_MODE) {
/* This veneer should only be called by NS RTOS in handler mode */
return 0U;
}
if (free_index < 0) {
/* No more free slots */
return 0U;
}
/* TZ_MemoryId_t must be a positive integer */
tz_id = free_index + 1;
NsClientIdList[free_index].ns_client_id = get_next_ns_client_id();
#ifdef PRINT_NSPM_DEBUG
printf("TZ_AllocModuleContext_S called, returning id %d\r\n",
NsClientIdList[free_index].ns_client_id);
#endif /* PRINT_NSPM_DEBUG */
free_index = NsClientIdList[free_index].next_free_index;
return tz_id;
}
/// Free context memory that was previously allocated with \ref TZ_AllocModuleContext_S
/// \param[in] id TrustZone memory slot identifier
/// \return execution status (1: success, 0: error)
/* This veneer is TF-M internal, not a secure service */
__attribute__((cmse_nonsecure_entry))
uint32_t TZ_FreeModuleContext_S (TZ_MemoryId_t id)
{
uint32_t index;
if (__get_active_exc_num() == EXC_NUM_THREAD_MODE) {
/* This veneer should only be called by NS RTOS in handler mode */
return 0U;
}
if ((id == 0U) || (id > TFM_MAX_NS_THREAD_COUNT)) {
/* Invalid TZ_MemoryId_t */
return 0U;
}
index = id - 1;
if (NsClientIdList[index].ns_client_id == INVALID_CLIENT_ID) {
/* Non-existent client */
return 0U;
}
#ifdef PRINT_NSPM_DEBUG
printf("TZ_FreeModuleContext_S called for id %d\r\n",
NsClientIdList[index].ns_client_id);
#endif /* PRINT_NSPM_DEBUG */
if (active_ns_client_idx == index) {
#ifdef PRINT_NSPM_DEBUG
printf("Freeing active NS client, NS inactive\r\n");
#endif /* PRINT_NSPM_DEBUG */
active_ns_client_idx = DEFAULT_NS_CLIENT_IDX;
}
NsClientIdList[index].ns_client_id = INVALID_CLIENT_ID;
NsClientIdList[index].next_free_index = free_index;
free_index = index;
return 1U; // Success
}
/// Load secure context (called on RTOS thread context switch)
/// \param[in] id TrustZone memory slot identifier
/// \return execution status (1: success, 0: error)
/* This veneer is TF-M internal, not a secure service */
__attribute__((cmse_nonsecure_entry))
uint32_t TZ_LoadContext_S (TZ_MemoryId_t id)
{
uint32_t index;
if (__get_active_exc_num() == EXC_NUM_THREAD_MODE) {
/* This veneer should only be called by NS RTOS in handler mode */
return 0U;
}
#ifdef PRINT_NSPM_DEBUG
LOG_MSG("TZ_LoadContext_S called");
#endif /* PRINT_NSPM_DEBUG */
if ((id == 0U) || (id > TFM_MAX_NS_THREAD_COUNT)) {
/* Invalid TZ_MemoryId_t */
return 0U;
}
index = id - 1;
if (NsClientIdList[index].ns_client_id == INVALID_CLIENT_ID) {
/* Non-existent client */
return 0U;
}
active_ns_client_idx = index;
#ifdef PRINT_NSPM_DEBUG
printf("TZ_LoadContext_S called for id %d\r\n",
NsClientIdList[index].ns_client_id);
#endif /* PRINT_NSPM_DEBUG */
return 1U; // Success
}
/// Store secure context (called on RTOS thread context switch)
/// \param[in] id TrustZone memory slot identifier
/// \return execution status (1: success, 0: error)
/* This veneer is TF-M internal, not a secure service */
__attribute__((cmse_nonsecure_entry))
uint32_t TZ_StoreContext_S (TZ_MemoryId_t id)
{
uint32_t index;
if (__get_active_exc_num() == EXC_NUM_THREAD_MODE) {
/* This veneer should only be called by NS RTOS in handler mode */
return 0U;
}
#ifdef PRINT_NSPM_DEBUG
LOG_MSG("TZ_StoreContext_S called");
#endif /* PRINT_NSPM_DEBUG */
/* id corresponds to context being swapped out on NS side */
if ((id == 0U) || (id > TFM_MAX_NS_THREAD_COUNT)) {
/* Invalid TZ_MemoryId_t */
return 0U;
}
index = id - 1;
if (NsClientIdList[index].ns_client_id == INVALID_CLIENT_ID) {
/* Non-existent client */
return 0U;
}
if (active_ns_client_idx != index) {
#ifdef PRINT_NSPM_DEBUG
printf("TZ_StoreContext_S called for id %d, active id: %d\r\n",
NsClientIdList[index].ns_client_id,
NsClientIdList[active_ns_client_idx].ns_client_id);
#endif /* PRINT_NSPM_DEBUG */
return 0U;
}
#ifdef PRINT_NSPM_DEBUG
printf("TZ_StoreContext_S called for id %d\r\n",
NsClientIdList[index].ns_client_id);
#endif /* PRINT_NSPM_DEBUG */
active_ns_client_idx = DEFAULT_NS_CLIENT_IDX;
return 1U; // Success
}
#ifdef TFM_NS_CLIENT_IDENTIFICATION
__attribute__((cmse_nonsecure_entry))
enum tfm_status_e tfm_register_client_id (int32_t ns_client_id)
{
int current_client_id;
if (__get_active_exc_num() == EXC_NUM_THREAD_MODE) {
/* This veneer should only be called by NS RTOS in handler mode */
return TFM_ERROR_NS_THREAD_MODE_CALL;
}
if (ns_client_id >= 0) {
/* The client ID is invalid */
return TFM_ERROR_INVALID_PARAMETER;
}
if (active_ns_client_idx < 0) {
/* No client is active */
return TFM_ERROR_GENERIC;
}
current_client_id = NsClientIdList[active_ns_client_idx].ns_client_id;
if (current_client_id >= 0 ) {
/* The client ID is invalid */
return TFM_ERROR_INVALID_PARAMETER;
}
NsClientIdList[active_ns_client_idx].ns_client_id = ns_client_id;
#ifdef PRINT_NSPM_DEBUG
printf("tfm_register_client_id called with id %d\r\n", ns_client_id);
#endif /* PRINT_NSPM_DEBUG */
return TFM_SUCCESS;
}
#endif

View File

@ -0,0 +1,26 @@
/*
* Copyright (c) 2018, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef __TFM_NSPM_H__
#define __TFM_NSPM_H__
#include <stdint.h>
/**
* \brief initialise the NS context database
*/
void tfm_nspm_configure_clients(void);
/**
* \brief Get the client ID of the current NS client
*
* \return The client id of the current NS client. 0 (invalid client id) is
* returned in case of error.
*/
int32_t tfm_nspm_get_current_client_id(void);
#endif /* __TFM_NSPM_H__ */

View File

@ -0,0 +1,24 @@
/*
* Copyright (c) 2018, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef __TFM_PLATFORM_CORE_API_H__
#define __TFM_PLATFORM_CORE_API_H__
/**
* \brief Should be called in case of access violation.
*
* There might be platform specific means, by which it is possible on a
* subsystem to detect access violation. For example a platform can have a
* Peripheral Protection Controller, to detect unauthorised accesses to
* peripheral registers. Setting up the protection, and handling the violation
* is implemented in platform specific code. However TF-M should be able to
* decide how to proceed if a violation happens. So to notify TF-M, platform
* code have to call this function, if a violation happens.
*/
void tfm_access_violation_handler(void);
#endif /* __TFM_PLATFORM_CORE_API_H__ */

View File

@ -0,0 +1,134 @@
/*
* Copyright (c) 2018, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include <stdbool.h>
#include <stdio.h>
#include "psa_client.h"
#include "psa_service.h"
#include "secure_utilities.h"
#include "tfm_secure_api.h"
#include "tfm_api.h"
#include "tfm_svcalls.h"
/* FixMe: check if this is really needed */
extern int32_t tfm_secure_lock;
__attribute__ ((always_inline)) __STATIC_INLINE
int32_t tfm_psa_veneer_sanity_check(struct tfm_sfn_req_s *desc_ptr)
{
if (desc_ptr->ns_caller) {
if (tfm_secure_lock != 0) {
/* Secure domain is already locked!
* FixMe: Decide if this is a fault or permitted in case of PSA
* API usage
*/
return TFM_ERROR_SECURE_DOMAIN_LOCKED;
}
} else {
/* Secure partition should not call a different secure partition
* using TFM PSA veneers
*/
return TFM_ERROR_INVALID_EXC_MODE;
}
return TFM_SUCCESS;
}
/* Veneer implementation */
#define TFM_CORE_NS_IPC_REQUEST_VENEER(fn, a, b, c, d) \
return tfm_core_ns_ipc_request(fn, (int32_t)a, (int32_t)b, \
(int32_t)c, (int32_t)d)
__attribute__ ((always_inline)) __STATIC_INLINE
int32_t tfm_core_ns_ipc_request(void *fn, int32_t arg1, int32_t arg2,
int32_t arg3, int32_t arg4)
{
int32_t args[4] = {arg1, arg2, arg3, arg4};
volatile struct tfm_sfn_req_s desc;
struct tfm_sfn_req_s *desc_ptr = &desc;
int32_t res;
desc.sfn = fn;
desc.args = args;
desc.ns_caller = cmse_nonsecure_caller();
if (__get_active_exc_num() != EXC_NUM_THREAD_MODE)
{
/* FIXME: Proper error handling to be implemented */
return TFM_ERROR_INVALID_EXC_MODE;
} else {
__ASM("MOV r0, %1\n"
"SVC %2\n"
"MOV %0, r0\n"
: "=r" (res)
: "r" (desc_ptr), "I" (TFM_SVC_IPC_REQUEST)
: "r0");
return res;
}
}
/* FixMe: these functions need to have different attributes compared to those
* legacy veneers which may be called by secure partitions.
* They won't call legacy SFN but instead will be handlers for TF-M
*/
__tfm_secure_gateway_attributes__
uint32_t tfm_psa_framework_version_veneer(void)
{
TFM_CORE_NS_IPC_REQUEST_VENEER(tfm_svcall_psa_framework_version, 0, 0,
0, 0);
}
__tfm_secure_gateway_attributes__
uint32_t tfm_psa_version_veneer(uint32_t sid)
{
TFM_CORE_NS_IPC_REQUEST_VENEER(tfm_svcall_psa_version, sid, 0, 0, 0);
}
__tfm_secure_gateway_attributes__
psa_handle_t tfm_psa_connect_veneer(uint32_t sid, uint32_t minor_version)
{
TFM_CORE_NS_IPC_REQUEST_VENEER(tfm_svcall_psa_connect, sid,
minor_version, 0, 0);
}
__tfm_secure_gateway_attributes__
psa_status_t tfm_psa_call_veneer(psa_handle_t handle,
const psa_invec *in_vecs,
psa_outvec *out_vecs)
{
TFM_CORE_NS_IPC_REQUEST_VENEER(tfm_svcall_psa_call, handle, in_vecs,
out_vecs, 0);
}
__tfm_secure_gateway_attributes__
psa_status_t tfm_psa_close_veneer(psa_handle_t handle)
{
TFM_CORE_NS_IPC_REQUEST_VENEER(tfm_svcall_psa_close, handle, 0, 0, 0);
}
void tfm_psa_ipc_request_handler(uint32_t svc_ctx[])
{
uint32_t *r0_ptr = svc_ctx;
/* The only argument to the SVC call is stored in the stacked r0 */
struct tfm_sfn_req_s *desc_ptr = (struct tfm_sfn_req_s *) *r0_ptr;
if(tfm_psa_veneer_sanity_check(desc_ptr) != TFM_SUCCESS) {
/* FixMe: consider error handling - this may be critical error */
*r0_ptr = TFM_ERROR_INVALID_PARAMETER;
return;
}
/* Store SVC return value in stacked r0 */
*r0_ptr = desc_ptr->sfn((int32_t)desc_ptr->args,
desc_ptr->ns_caller,
0,
0);
return;
}

View File

@ -0,0 +1,799 @@
/*
* Copyright (c) 2017-2019, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include "cmsis.h"
#include "tfm_secure_api.h"
#include "tfm_nspm.h"
#include "secure_utilities.h"
#include "secure_fw/spm/spm_api.h"
#include "region_defs.h"
#include "tfm_api.h"
#define EXC_RETURN_SECURE_FUNCTION 0xFFFFFFFD
#ifndef TFM_LVL
#error TFM_LVL is not defined!
#endif
#if TFM_LVL == 1
/* Macros to pick linker symbols and allow references to sections */
#define REGION(a, b, c) a##b##c
#define REGION_NAME(a, b, c) REGION(a, b, c)
#define REGION_DECLARE(a, b, c) extern uint32_t REGION_NAME(a, b, c)
REGION_DECLARE(Image$$, TFM_SECURE_STACK, $$ZI$$Base);
REGION_DECLARE(Image$$, TFM_SECURE_STACK, $$ZI$$Limit);
#endif
/* This is the "Big Lock" on the secure side, to guarantee single entry
* to SPE
*/
int32_t tfm_secure_lock;
static int32_t tfm_secure_api_initializing = 1;
static int32_t *prepare_partition_ctx(
struct tfm_exc_stack_t *svc_ctx,
struct tfm_sfn_req_s *desc_ptr,
int32_t *dst)
{
/* XPSR = as was when called, but make sure it's thread mode */
*(--dst) = svc_ctx->XPSR & 0xFFFFFE00;
/* ReturnAddress = resume veneer in new context */
*(--dst) = svc_ctx->RetAddr;
/* LR = sfn address */
*(--dst) = (int32_t)desc_ptr->sfn;
/* R12 = don't care */
*(--dst) = 0;
/* R0-R3 = sfn arguments */
int32_t i = 4;
while (i > 0) {
i--;
*(--dst) = (uint32_t)desc_ptr->args[i];
}
return dst;
}
static void restore_caller_ctx(
struct tfm_exc_stack_t *svc_ctx,
struct tfm_exc_stack_t *target_ctx)
{
/* ReturnAddress = resume veneer after second SVC */
target_ctx->RetAddr = svc_ctx->RetAddr;
/* R0 = function return value */
target_ctx->R0 = svc_ctx->R0;
return;
}
static int32_t tfm_start_partition(struct tfm_sfn_req_s *desc_ptr,
uint32_t excReturn)
{
uint32_t caller_partition_idx = desc_ptr->caller_part_idx;
const struct spm_partition_runtime_data_t *curr_part_data;
uint32_t caller_flags;
register uint32_t partition_idx;
uint32_t psp = __get_PSP();
uint32_t partition_psp, partition_psplim;
uint32_t partition_state;
uint32_t partition_flags;
struct tfm_exc_stack_t *svc_ctx = (struct tfm_exc_stack_t *)psp;
uint32_t caller_partition_id;
int32_t client_id;
caller_flags = tfm_spm_partition_get_flags(caller_partition_idx);
/* Check partition state consistency */
if (((caller_flags & SPM_PART_FLAG_APP_ROT) != 0)
!= (!desc_ptr->ns_caller)) {
/* Partition state inconsistency detected */
return TFM_SECURE_LOCK_FAILED;
}
if((caller_flags & SPM_PART_FLAG_APP_ROT) == 0) {
/* Disable NS exception handling while secure service is running.
* FixMe:
* This restriction is applied to limit the number of possible attack
* vectors.
* To be removed when pre-emption and context management issues have
* been analysed and resolved.
*/
TFM_NS_EXC_DISABLE();
}
partition_idx = get_partition_idx(desc_ptr->sp_id);
curr_part_data = tfm_spm_partition_get_runtime_data(partition_idx);
partition_state = curr_part_data->partition_state;
partition_flags = tfm_spm_partition_get_flags(partition_idx);
caller_partition_id = tfm_spm_partition_get_partition_id(
caller_partition_idx);
if (tfm_secure_api_initializing) {
#if TFM_LVL != 1
/* Make thread mode unprivileged while untrusted partition init is
* executed
*/
if ((partition_flags & SPM_PART_FLAG_PSA_ROT) == 0) {
CONTROL_Type ctrl;
ctrl.w = __get_CONTROL();
ctrl.b.nPRIV = 1;
__set_CONTROL(ctrl.w);
__DSB();
__ISB();
}
#endif
} else if (partition_state == SPM_PARTITION_STATE_RUNNING ||
partition_state == SPM_PARTITION_STATE_SUSPENDED ||
partition_state == SPM_PARTITION_STATE_BLOCKED) {
/* Recursion is not permitted! */
return TFM_ERROR_PARTITION_NON_REENTRANT;
} else if (partition_state != SPM_PARTITION_STATE_IDLE) {
/* The partition to be called is not in a proper state */
return TFM_SECURE_LOCK_FAILED;
}
#if TFM_LVL == 1
/* Prepare switch to shared secure partition stack */
partition_psp =
(uint32_t)&REGION_NAME(Image$$, TFM_SECURE_STACK, $$ZI$$Limit);
partition_psplim =
(uint32_t)&REGION_NAME(Image$$, TFM_SECURE_STACK, $$ZI$$Base);
#else
partition_psp = curr_part_data->stack_ptr;
partition_psplim = tfm_spm_partition_get_stack_bottom(partition_idx);
#endif
/* Store the context for the partition call */
tfm_spm_partition_set_caller_partition_idx(partition_idx,
caller_partition_idx);
tfm_spm_partition_store_context(caller_partition_idx, psp, excReturn);
if ((caller_flags & SPM_PART_FLAG_APP_ROT)) {
tfm_spm_partition_set_caller_client_id(partition_idx,
caller_partition_id);
} else {
client_id = tfm_nspm_get_current_client_id();
if (client_id >= 0)
{
return TFM_SECURE_LOCK_FAILED;
}
tfm_spm_partition_set_caller_client_id(partition_idx, client_id);
}
#if (TFM_LVL != 1) && (TFM_LVL != 2)
/* Dynamic partitioning is only done is TFM level 3 */
tfm_spm_partition_sandbox_deconfig(caller_partition_idx);
/* Configure partition execution environment */
if (tfm_spm_partition_sandbox_config(partition_idx) != SPM_ERR_OK) {
ERROR_MSG("Failed to configure sandbox for partition!");
tfm_secure_api_error_handler();
}
#endif
/* Default share to scratch area in case of partition to partition calls
* this way partitions always get default access to input buffers
*/
/* FixMe: return value/error handling TBD */
tfm_spm_partition_set_share(partition_idx, desc_ptr->ns_caller ?
TFM_BUFFER_SHARE_NS_CODE : TFM_BUFFER_SHARE_SCRATCH);
#if TFM_LVL == 1
/* In level one, only switch context and return from exception if in
* handler mode
*/
if ((desc_ptr->ns_caller) || (tfm_secure_api_initializing)) {
/* Prepare the partition context, update stack ptr */
psp = (uint32_t)prepare_partition_ctx(
svc_ctx, desc_ptr, (int32_t *)partition_psp);
__set_PSP(psp);
__set_PSPLIM(partition_psplim);
}
#else
/* Prepare the partition context, update stack ptr */
psp = (uint32_t)prepare_partition_ctx(svc_ctx, desc_ptr,
(int32_t *)partition_psp);
__set_PSP(psp);
__set_PSPLIM(partition_psplim);
#endif
tfm_spm_partition_set_state(caller_partition_idx,
SPM_PARTITION_STATE_BLOCKED);
tfm_spm_partition_set_state(partition_idx, SPM_PARTITION_STATE_RUNNING);
tfm_secure_lock++;
return TFM_SUCCESS;
}
static int32_t tfm_return_from_partition(uint32_t *excReturn)
{
uint32_t current_partition_idx =
tfm_spm_partition_get_running_partition_idx();
const struct spm_partition_runtime_data_t *curr_part_data, *ret_part_data;
uint32_t current_partition_flags;
uint32_t return_partition_idx;
uint32_t return_partition_flags;
uint32_t psp = __get_PSP();
struct tfm_exc_stack_t *svc_ctx = (struct tfm_exc_stack_t *)psp;
if (current_partition_idx == SPM_INVALID_PARTITION_IDX) {
return TFM_SECURE_UNLOCK_FAILED;
}
curr_part_data = tfm_spm_partition_get_runtime_data(current_partition_idx);
return_partition_idx = curr_part_data->caller_partition_idx;
if (return_partition_idx == SPM_INVALID_PARTITION_IDX) {
return TFM_SECURE_UNLOCK_FAILED;
}
ret_part_data = tfm_spm_partition_get_runtime_data(return_partition_idx);
return_partition_flags = tfm_spm_partition_get_flags(return_partition_idx);
current_partition_flags = tfm_spm_partition_get_flags(
current_partition_idx);
tfm_secure_lock--;
if((return_partition_flags & SPM_PART_FLAG_APP_ROT) == 0) {
/* Re-enable NS exceptions when secure service returns to NS client.
* FixMe:
* To be removed when pre-emption and context management issues have
* been analysed and resolved.
*/
TFM_NS_EXC_ENABLE();
}
#if (TFM_LVL != 1) && (TFM_LVL != 2)
/* Deconfigure completed partition environment */
tfm_spm_partition_sandbox_deconfig(current_partition_idx);
if (tfm_secure_api_initializing) {
/* Restore privilege for thread mode during TF-M init. This is only
* have to be done if the partition is not trusted.
*/
if ((current_partition_flags & SPM_PART_FLAG_PSA_ROT) == 0) {
CONTROL_Type ctrl;
ctrl.w = __get_CONTROL();
ctrl.b.nPRIV = 0;
__set_CONTROL(ctrl.w);
__DSB();
__ISB();
}
} else {
/* Configure the caller partition environment in case this was a
* partition to partition call and returning to untrusted partition
*/
if (tfm_spm_partition_sandbox_config(return_partition_idx)
!= SPM_ERR_OK) {
ERROR_MSG("Failed to configure sandbox for partition!");
tfm_secure_api_error_handler();
}
if (return_partition_flags & SPM_PART_FLAG_APP_ROT) {
/* Restore share status */
tfm_spm_partition_set_share(
return_partition_idx,
tfm_spm_partition_get_runtime_data(
return_partition_idx)->share);
}
}
#endif
#if TFM_LVL == 1
if (!(return_partition_flags & SPM_PART_FLAG_APP_ROT) ||
(tfm_secure_api_initializing)) {
/* In TFM level 1 context restore is only done when
* returning to NS or after initialization
*/
/* Restore caller context */
restore_caller_ctx(svc_ctx,
(struct tfm_exc_stack_t *)ret_part_data->stack_ptr);
*excReturn = ret_part_data->lr;
__set_PSP(ret_part_data->stack_ptr);
extern uint32_t Image$$ARM_LIB_STACK$$ZI$$Base[];
uint32_t psp_stack_bottom = (uint32_t)Image$$ARM_LIB_STACK$$ZI$$Base;
__set_PSPLIM(psp_stack_bottom);
}
#else
/* Restore caller context */
restore_caller_ctx(svc_ctx,
(struct tfm_exc_stack_t *)ret_part_data->stack_ptr);
*excReturn = ret_part_data->lr;
__set_PSP(ret_part_data->stack_ptr);
__set_PSPLIM(tfm_spm_partition_get_stack_bottom(return_partition_idx));
/* Clear the context entry before returning */
tfm_spm_partition_set_stack(
current_partition_idx, psp - sizeof(struct tfm_exc_stack_t));
#endif
tfm_spm_partition_cleanup_context(current_partition_idx);
tfm_spm_partition_set_state(current_partition_idx,
SPM_PARTITION_STATE_IDLE);
tfm_spm_partition_set_state(return_partition_idx,
SPM_PARTITION_STATE_RUNNING);
return TFM_SUCCESS;
}
void tfm_secure_api_error_handler(void)
{
ERROR_MSG("Security violation when calling secure API");
while (1) {
;
}
}
static int32_t tfm_check_sfn_req_integrity(struct tfm_sfn_req_s *desc_ptr)
{
if ((desc_ptr == NULL) ||
(desc_ptr->sp_id == 0) ||
(desc_ptr->sfn == NULL)) {
/* invalid parameter */
return TFM_ERROR_INVALID_PARAMETER;
}
return TFM_SUCCESS;
}
static int32_t tfm_core_check_sfn_req_rules(
struct tfm_sfn_req_s *desc_ptr)
{
/* Check partition idx validity */
if (desc_ptr->caller_part_idx == SPM_INVALID_PARTITION_IDX) {
return TFM_ERROR_NO_ACTIVE_PARTITION;
}
if ((desc_ptr->ns_caller) && (tfm_secure_lock != 0)) {
/* Secure domain is already locked!
* This should only happen if caller is secure partition!
* FixMe: This scenario is a potential security breach
* Take appropriate action!
*/
return TFM_ERROR_SECURE_DOMAIN_LOCKED;
}
if (tfm_secure_api_initializing) {
int32_t id =
tfm_spm_partition_get_partition_id(desc_ptr->caller_part_idx);
if ((id != TFM_SP_CORE_ID) || (tfm_secure_lock != 0)) {
/* Invalid request during system initialization */
ERROR_MSG("Invalid service request during initialization!");
return TFM_ERROR_NOT_INITIALIZED;
}
}
return TFM_SUCCESS;
}
void tfm_secure_api_init_done(void)
{
tfm_secure_api_initializing = 0;
#if TFM_LVL != 1
if (tfm_spm_partition_sandbox_config(TFM_SP_NON_SECURE_ID) != SPM_ERR_OK) {
ERROR_MSG("Failed to configure sandbox for partition!");
tfm_secure_api_error_handler();
}
#endif
}
int32_t tfm_core_sfn_request_handler(
struct tfm_sfn_req_s *desc_ptr, uint32_t excReturn)
{
int32_t res;
res = tfm_check_sfn_req_integrity(desc_ptr);
if (res != TFM_SUCCESS) {
ERROR_MSG("Invalid service request!");
return TFM_ERROR_STATUS(res);
}
__disable_irq();
desc_ptr->caller_part_idx = tfm_spm_partition_get_running_partition_idx();
res = tfm_core_check_sfn_req_rules(desc_ptr);
if (res != TFM_SUCCESS) {
/* FixMe: error compartmentalization TBD */
tfm_spm_partition_set_state(
desc_ptr->caller_part_idx, SPM_PARTITION_STATE_CLOSED);
__enable_irq();
ERROR_MSG("Unauthorized service request!");
return TFM_ERROR_STATUS(res);
}
res = tfm_start_partition(desc_ptr, excReturn);
if (res != TFM_SUCCESS) {
/* FixMe: consider possible fault scenarios */
__enable_irq();
ERROR_MSG("Failed to process service request!");
return TFM_ERROR_STATUS(res);
}
__enable_irq();
return res;
}
#if TFM_LVL == 1
int32_t tfm_core_sfn_request_thread_mode(struct tfm_sfn_req_s *desc_ptr)
{
int32_t res;
int32_t *args;
int32_t retVal;
/* No excReturn value is needed as no exception handling is used */
res = tfm_core_sfn_request_handler(desc_ptr, 0);
if (res != TFM_SUCCESS) {
tfm_secure_api_error_handler();
}
/* Secure partition to secure partition call in TFM level 1 */
args = desc_ptr->args;
retVal = desc_ptr->sfn(args[0], args[1], args[2], args[3]);
/* return handler should restore original exc_return value... */
res = tfm_return_from_partition(NULL);
if (res == TFM_SUCCESS) {
/* If unlock successful, pass SS return value to caller */
res = retVal;
} else {
/* Unlock errors indicate ctx database corruption or unknown
* anomalies. Halt execution
*/
ERROR_MSG("Secure API error during unlock!");
tfm_secure_api_error_handler();
}
return res;
}
#endif
void tfm_core_validate_secure_caller_handler(uint32_t *svc_args)
{
int32_t res = TFM_ERROR_GENERIC;
uint32_t running_partition_idx =
tfm_spm_partition_get_running_partition_idx();
const struct spm_partition_runtime_data_t *curr_part_data =
tfm_spm_partition_get_runtime_data(running_partition_idx);
uint32_t running_partition_flags =
tfm_spm_partition_get_flags(running_partition_idx);
uint32_t caller_partition_flags =
tfm_spm_partition_get_flags(curr_part_data->caller_partition_idx);
if (!(running_partition_flags & SPM_PART_FLAG_APP_ROT)) {
/* This handler shouldn't be called from outside partition context.
* Partitions are only allowed to run while S domain is locked.
*/
svc_args[0] = TFM_ERROR_INVALID_PARAMETER;
return;
}
/* Store return value in r0 */
if (caller_partition_flags & SPM_PART_FLAG_APP_ROT) {
res = TFM_SUCCESS;
}
svc_args[0] = res;
}
int32_t tfm_core_check_buffer_access(uint32_t partition_idx,
void *start_addr,
size_t len,
uint32_t alignment)
{
uintptr_t start_addr_value = (uintptr_t)start_addr;
uintptr_t end_addr_value = (uintptr_t)start_addr + len;
uintptr_t alignment_mask;
alignment_mask = (((uintptr_t)1) << alignment) - 1;
/* Check that the pointer is aligned properly */
if (start_addr_value & alignment_mask) {
/* not aligned, return error */
return 0;
}
/* Protect against overflow (and zero len) */
if (end_addr_value <= start_addr_value)
{
return 0;
}
#if TFM_LVL == 1
/* For privileged partition execution, all secure data memory and stack
* is accessible
*/
if (start_addr_value >= S_DATA_START &&
end_addr_value <= (S_DATA_START + S_DATA_SIZE)) {
return 1;
}
#else
/* For non-privileged execution the partition's data and stack is
* accessible
*/
if (start_addr_value >=
tfm_spm_partition_get_stack_bottom(partition_idx) &&
end_addr_value <=
tfm_spm_partition_get_stack_top(partition_idx)) {
return 1;
}
if (start_addr_value >=
tfm_spm_partition_get_rw_start(partition_idx) &&
end_addr_value <=
tfm_spm_partition_get_rw_limit(partition_idx)) {
return 1;
}
if (start_addr_value >=
tfm_spm_partition_get_zi_start(partition_idx) &&
end_addr_value <=
tfm_spm_partition_get_zi_limit(partition_idx)) {
return 1;
}
#endif
return 0;
}
void tfm_core_get_caller_client_id_handler(uint32_t *svc_args)
{
uintptr_t result_ptr_value = svc_args[0];
uint32_t running_partition_idx =
tfm_spm_partition_get_running_partition_idx();
const uint32_t running_partition_flags =
tfm_spm_partition_get_flags(running_partition_idx);
const struct spm_partition_runtime_data_t *curr_part_data =
tfm_spm_partition_get_runtime_data(running_partition_idx);
int res = 0;
if (!(running_partition_flags & SPM_PART_FLAG_APP_ROT)) {
/* This handler shouldn't be called from outside partition context.
* Partitions are only allowed to run while S domain is locked.
*/
svc_args[0] = TFM_ERROR_INVALID_PARAMETER;
return;
}
/* Make sure that the output pointer points to a memory area that is owned
* by the partition
*/
res = tfm_core_check_buffer_access(running_partition_idx,
(void *)result_ptr_value,
sizeof(curr_part_data->caller_client_id),
2);
if (!res) {
/* Not in accessible range, return error */
svc_args[0] = TFM_ERROR_INVALID_PARAMETER;
return;
}
*((int32_t *)result_ptr_value) = curr_part_data->caller_client_id;
/* Store return value in r0 */
svc_args[0] = TFM_SUCCESS;
}
void tfm_core_memory_permission_check_handler(uint32_t *svc_args)
{
uint32_t ptr = svc_args[0];
uint32_t size = svc_args[1];
int32_t access = svc_args[2];
uint32_t max_buf_size, ptr_start, range_limit, range_check = false;
int32_t res;
uint32_t running_partition_idx =
tfm_spm_partition_get_running_partition_idx();
const struct spm_partition_runtime_data_t *curr_part_data =
tfm_spm_partition_get_runtime_data(running_partition_idx);
uint32_t running_partition_flags =
tfm_spm_partition_get_flags(running_partition_idx);
int32_t flags = 0;
void *rangeptr;
if (!(running_partition_flags & SPM_PART_FLAG_APP_ROT) || (size == 0)) {
/* This handler should only be called from a secure partition. */
svc_args[0] = TFM_ERROR_INVALID_PARAMETER;
return;
}
if (curr_part_data->share != TFM_BUFFER_SHARE_PRIV) {
flags |= CMSE_MPU_UNPRIV;
}
if (access == TFM_MEMORY_ACCESS_RW) {
flags |= CMSE_MPU_READWRITE;
} else {
flags |= CMSE_MPU_READ;
}
/* Check if partition access to address would fail */
rangeptr = cmse_check_address_range((void *)ptr, size, flags);
/* Get regions associated with address */
cmse_address_info_t addr_info = cmse_TT((void *)ptr);
if (rangeptr == NULL) {
svc_args[0] = TFM_ERROR_INVALID_PARAMETER;
return;
}
if (addr_info.flags.secure) {
#if TFM_LVL == 1
/* For privileged partition execution, all secure data memory is
* accessible
*/
max_buf_size = S_DATA_SIZE;
ptr_start = S_DATA_START;
range_limit = S_DATA_LIMIT;
#else
/* Only scratch is permitted in secure memory */
max_buf_size = (uint32_t)tfm_scratch_area_size;
ptr_start = (uint32_t)tfm_scratch_area;
range_limit = (uint32_t)tfm_scratch_area + tfm_scratch_area_size - 1;
#endif
range_check = true;
} else {
if (!addr_info.flags.sau_region_valid) {
/* If address is NS, TF-M expects SAU to be configured
*/
svc_args[0] = TFM_ERROR_INVALID_PARAMETER;
return;
}
switch (addr_info.flags.sau_region) {
case TFM_NS_REGION_CODE:
if (access == TFM_MEMORY_ACCESS_RW) {
res = TFM_ERROR_INVALID_PARAMETER;
} else {
/* Currently TF-M does not support checks for NS Memory
* accesses by partitions
*/
res = TFM_SUCCESS;
}
break;
case TFM_NS_REGION_DATA:
/* Currently TF-M does not support checks for NS Memory
* accesses by partitions
*/
res = TFM_SUCCESS;
break;
default:
/* Only NS data and code regions can be accessed as buffers */
res = TFM_ERROR_INVALID_PARAMETER;
break;
}
}
if (range_check == true) {
if ((size <= max_buf_size) && (ptr >= ptr_start)
&& (ptr <= range_limit + 1 - size)) {
res = TFM_SUCCESS;
} else {
res = TFM_ERROR_INVALID_PARAMETER;
}
}
/* Store return value in r0 */
svc_args[0] = res;
}
/* This SVC handler is called if veneer is running in thread mode */
uint32_t tfm_core_partition_request_svc_handler(
struct tfm_exc_stack_t *svc_ctx, uint32_t excReturn)
{
if (!(excReturn & EXC_RETURN_STACK_PROCESS)) {
/* Service request SVC called with MSP active.
* Either invalid configuration for Thread mode or SVC called
* from Handler mode, which is not supported.
* FixMe: error severity TBD
*/
ERROR_MSG("Service request SVC called with MSP active!");
tfm_secure_api_error_handler();
}
struct tfm_sfn_req_s *desc_ptr = (struct tfm_sfn_req_s *)svc_ctx->R0;
if (tfm_core_sfn_request_handler(desc_ptr, excReturn) != TFM_SUCCESS) {
tfm_secure_api_error_handler();
}
return EXC_RETURN_SECURE_FUNCTION;
}
/* This SVC handler is called when sfn returns */
uint32_t tfm_core_partition_return_handler(uint32_t lr)
{
int32_t res;
if (!(lr & EXC_RETURN_STACK_PROCESS)) {
/* Partition return SVC called with MSP active.
* This should not happen!
*/
ERROR_MSG("Partition return SVC called with MSP active!");
tfm_secure_api_error_handler();
}
/* Store return value from secure partition */
int32_t retVal = *(int32_t *)__get_PSP();
if ((retVal > TFM_SUCCESS) &&
(retVal < TFM_PARTITION_SPECIFIC_ERROR_MIN)) {
/* Secure function returned a reserved value */
#ifdef TFM_CORE_DEBUG
LOG_MSG("Invalid return value from secure partition!");
#endif
/* FixMe: error can be traced to specific secure partition
* and Core is not compromised. Error handling flow can be
* refined
*/
tfm_secure_api_error_handler();
}
res = tfm_return_from_partition(&lr);
if (res != TFM_SUCCESS) {
/* Unlock errors indicate ctx database corruption or unknown anomalies
* Halt execution
*/
ERROR_MSG("Secure API error during unlock!");
tfm_secure_api_error_handler();
}
return lr;
}
void tfm_core_set_buffer_area_handler(uint32_t *args)
{
/* r0 is stored in args[0] in exception stack frame
* Store input parameter before writing return value to that address
*/
enum tfm_buffer_share_region_e share;
uint32_t running_partition_idx =
tfm_spm_partition_get_running_partition_idx();
const struct spm_partition_runtime_data_t *curr_part_data =
tfm_spm_partition_get_runtime_data(running_partition_idx);
uint32_t caller_partition_idx = curr_part_data->caller_partition_idx;
uint32_t running_partition_flags =
tfm_spm_partition_get_flags(running_partition_idx);
uint32_t caller_partition_flags =
tfm_spm_partition_get_flags(caller_partition_idx);
/* tfm_core_set_buffer_area() returns int32_t */
int32_t *res_ptr = (int32_t *)&args[0];
if (!(running_partition_flags & SPM_PART_FLAG_APP_ROT)) {
/* This handler should only be called from a secure partition. */
*res_ptr = TFM_ERROR_INVALID_PARAMETER;
return;
}
switch (args[0]) {
case TFM_BUFFER_SHARE_DEFAULT:
share = (!(caller_partition_flags & SPM_PART_FLAG_APP_ROT)) ?
(TFM_BUFFER_SHARE_NS_CODE) : (TFM_BUFFER_SHARE_SCRATCH);
break;
case TFM_BUFFER_SHARE_SCRATCH:
case TFM_BUFFER_SHARE_NS_CODE:
share = args[0];
break;
default:
*res_ptr = TFM_ERROR_INVALID_PARAMETER;
return;
}
if (tfm_spm_partition_set_share(running_partition_idx, share) ==
SPM_ERR_OK) {
*res_ptr = TFM_SUCCESS;
} else {
*res_ptr = TFM_ERROR_INVALID_PARAMETER;
}
return;
}

View File

@ -0,0 +1,118 @@
/*
* Copyright (c) 2017-2018, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef __TFM_SECURE_API_H__
#define __TFM_SECURE_API_H__
#include "arm_cmse.h"
#include "tfm_svc.h"
#include "secure_utilities.h"
#include "tfm_core.h"
#include "tfm_api.h"
/*!
* \def __tfm_secure_gateway_attributes__
*
* \brief Attributes for secure gateway functions
*/
#define __tfm_secure_gateway_attributes__ \
__attribute__((cmse_nonsecure_entry, noinline, section("SFN")))
/* Hide specific errors if not debugging */
#ifdef TFM_CORE_DEBUG
#define TFM_ERROR_STATUS(status) (status)
#else
#define TFM_ERROR_STATUS(status) (TFM_PARTITION_BUSY)
#endif
#ifndef TFM_LVL
#error TFM_LVL is not defined!
#endif
extern void tfm_secure_api_error_handler(void);
typedef int32_t(*sfn_t)(int32_t, int32_t, int32_t, int32_t);
struct tfm_sfn_req_s {
uint32_t sp_id;
sfn_t sfn;
int32_t *args;
uint32_t caller_part_idx;
int32_t ns_caller : 1;
};
enum tfm_buffer_share_region_e {
TFM_BUFFER_SHARE_DISABLE,
TFM_BUFFER_SHARE_NS_CODE,
TFM_BUFFER_SHARE_SCRATCH,
TFM_BUFFER_SHARE_PRIV, /* only for TCB in level 2, all in level 1 */
TFM_BUFFER_SHARE_DEFAULT,
};
enum tfm_ns_region_e {
TFM_NS_REGION_CODE = 0,
TFM_NS_REGION_DATA,
TFM_NS_REGION_VENEER,
TFM_NS_REGION_PERIPH_1,
TFM_NS_REGION_PERIPH_2,
TFM_NS_SECONDARY_IMAGE_REGION,
};
enum tfm_memory_access_e {
TFM_MEMORY_ACCESS_RO = 1,
TFM_MEMORY_ACCESS_RW = 2,
};
extern int32_t tfm_core_set_buffer_area(enum tfm_buffer_share_region_e share);
extern int32_t tfm_core_validate_secure_caller(void);
extern int32_t tfm_core_get_caller_client_id(int32_t *caller_client_id);
extern int32_t tfm_core_memory_permission_check(
void *ptr, uint32_t size, int32_t access);
extern int32_t tfm_core_get_boot_data(uint8_t major_type, void *ptr,
uint32_t len);
int32_t tfm_core_sfn_request(struct tfm_sfn_req_s *desc_ptr);
int32_t tfm_core_sfn_request_thread_mode(struct tfm_sfn_req_s *desc_ptr);
#define TFM_CORE_SFN_REQUEST(id, fn, a, b, c, d) \
return tfm_core_partition_request(id, fn, (int32_t)a, (int32_t)b, \
(int32_t)c, (int32_t)d)
__attribute__ ((always_inline)) __STATIC_INLINE
int32_t tfm_core_partition_request(uint32_t id, void *fn,
int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4)
{
int32_t args[4] = {arg1, arg2, arg3, arg4};
struct tfm_sfn_req_s desc, *desc_ptr = &desc;
desc.sp_id = id;
desc.sfn = fn;
desc.args = args;
desc.ns_caller = cmse_nonsecure_caller();
if (__get_active_exc_num() != EXC_NUM_THREAD_MODE) {
/* FixMe: Error severity TBD */
return TFM_ERROR_GENERIC;
} else {
#if TFM_LVL == 1
if (desc.ns_caller) {
return tfm_core_sfn_request(desc_ptr);
} else {
return tfm_core_sfn_request_thread_mode(desc_ptr);
}
#else
return tfm_core_sfn_request(desc_ptr);
#endif
}
}
#endif /* __TFM_SECURE_API_H__ */

View File

@ -0,0 +1,164 @@
/*
* Copyright (c) 2017-2018, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include <arm_cmse.h>
#include "tfm_svc.h"
#include "tfm_secure_api.h"
#include "tfm_internal.h"
#include "secure_fw/include/tfm_spm_services_api.h"
uint8_t *tfm_scratch_area;
int32_t tfm_scratch_area_size;
nsfptr_t ns_entry;
void jump_to_ns_code(void)
{
#if TFM_LVL != 1
/* Initialization is done, set thread mode to unprivileged. */
CONTROL_Type ctrl;
ctrl.w = __get_CONTROL();
ctrl.b.nPRIV = 1;
__set_CONTROL(ctrl.w);
#endif
/* All changes made to memory will be effective after this point */
__DSB();
__ISB();
/* Calls the non-secure Reset_Handler to jump to the non-secure binary */
ns_entry();
}
#if defined(__ARM_ARCH_8M_MAIN__)
__attribute__((naked)) int32_t tfm_core_sfn_request(
struct tfm_sfn_req_s *desc_ptr)
{
__ASM(
"PUSH {r4-r12, lr}\n"
"SVC %[SVC_REQ]\n"
"MOV r4, #0\n"
"MOV r5, #0\n"
"MOV r6, #0\n"
"MOV r7, #0\n"
"MOV r8, #0\n"
"MOV r9, #0\n"
"MOV r10, #0\n"
"MOV r11, #0\n"
"BLX lr\n"
"SVC %[SVC_RET]\n"
"POP {r4-r12, pc}\n"
: : [SVC_REQ] "I" (TFM_SVC_SFN_REQUEST)
, [SVC_RET] "I" (TFM_SVC_SFN_RETURN)
: "r0");
}
#elif defined(__ARM_ARCH_8M_BASE__)
__attribute__((naked)) int32_t tfm_core_sfn_request(
struct tfm_sfn_req_s *desc_ptr)
{
__ASM(
".syntax unified\n"
"PUSH {lr}\n"
"PUSH {r4-r7}\n"
"MOV r4, r8\n"
"MOV r5, r9\n"
"MOV r6, r10\n"
"MOV r7, r11\n"
"PUSH {r4-r7}\n"
"MOV r4, r12\n"
"PUSH {r4}\n"
"SVC %[SVC_REQ]\n"
"MOVS r4, #0\n"
"MOV r5, r4\n"
"MOV r6, r4\n"
"MOV r7, r4\n"
"MOV r8, r4\n"
"MOV r9, r4\n"
"MOV r10, r4\n"
"MOV r11, r4\n"
"BLX lr\n"
"SVC %[SVC_RET]\n"
"POP {r4}\n"
"MOV r12, r4\n"
"POP {r4-r7}\n"
"MOV r8, r4\n"
"MOV r9, r5\n"
"MOV r10, r6\n"
"MOV r11, r7\n"
"POP {r4-r7}\n"
"POP {pc}\n"
: : [SVC_REQ] "I" (TFM_SVC_SFN_REQUEST)
, [SVC_RET] "I" (TFM_SVC_SFN_RETURN)
: "r0");
}
#else
#error "Unsupported ARM Architecture."
#endif
__attribute__((naked))
int32_t tfm_core_memory_permission_check(
void *ptr, uint32_t len, int32_t access)
{
__ASM(
"SVC %0\n"
"BX lr\n"
: : "I" (TFM_SVC_MEMORY_CHECK));
}
__attribute__((naked))
int32_t tfm_core_get_caller_client_id(int32_t *caller_client_id)
{
__ASM(
"SVC %0\n"
"BX LR\n"
: : "I" (TFM_SVC_GET_CALLER_CLIENT_ID));
}
__attribute__((naked))
int32_t tfm_spm_request_reset_vote(void)
{
__ASM(
"MOVS R0, %0\n"
"B tfm_spm_request\n"
: : "I" (TFM_SPM_REQUEST_RESET_VOTE));
}
__attribute__((naked))
int32_t tfm_spm_request(void)
{
__ASM(
"SVC %0\n"
"BX lr\n"
: : "I" (TFM_SVC_SPM_REQUEST));
}
__attribute__((naked))
int32_t tfm_core_validate_secure_caller(void)
{
__ASM(
"SVC %0\n"
"BX lr\n"
: : "I" (TFM_SVC_VALIDATE_SECURE_CALLER));
}
__attribute__((naked))
int32_t tfm_core_set_buffer_area(enum tfm_buffer_share_region_e share)
{
__ASM(
"SVC %0\n"
"BX lr\n"
: : "I" (TFM_SVC_SET_SHARE_AREA));
}
__attribute__((naked))
int32_t tfm_core_get_boot_data(uint8_t major_type, void *ptr, uint32_t len)
{
__ASM(
"SVC %0\n"
"BX lr\n"
: : "I" (TFM_SVC_GET_BOOT_DATA));
}

View File

@ -0,0 +1,48 @@
/*
* Copyright (c) 2017-2018, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef __TFM_SVC_H__
#define __TFM_SVC_H__
#include "cmsis.h"
typedef enum {
TFM_SVC_SFN_REQUEST = 0,
TFM_SVC_SFN_RETURN,
TFM_SVC_VALIDATE_SECURE_CALLER,
TFM_SVC_GET_CALLER_CLIENT_ID,
TFM_SVC_MEMORY_CHECK,
TFM_SVC_SET_SHARE_AREA,
TFM_SVC_SPM_REQUEST,
TFM_SVC_PRINT,
TFM_SVC_GET_BOOT_DATA,
#ifdef TFM_PSA_API
TFM_SVC_IPC_REQUEST,
TFM_SVC_SCHEDULE,
/* PSA Client SVC */
TFM_SVC_PSA_FRAMEWORK_VERSION,
TFM_SVC_PSA_VERSION,
TFM_SVC_PSA_CONNECT,
TFM_SVC_PSA_CALL,
TFM_SVC_PSA_CLOSE,
/* PSA Service SVC */
TFM_SVC_PSA_WAIT,
TFM_SVC_PSA_GET,
TFM_SVC_PSA_SET_RHANDLE,
TFM_SVC_PSA_READ,
TFM_SVC_PSA_SKIP,
TFM_SVC_PSA_WRITE,
TFM_SVC_PSA_REPLY,
TFM_SVC_PSA_NOTIFY,
TFM_SVC_PSA_CLEAR,
TFM_SVC_PSA_EOI,
#endif
} tfm_svc_number_t;
#define SVC(code) __ASM("svc %0" : : "I" (code))
#endif /* __TFM_SVC_H__ */

View File

@ -0,0 +1,22 @@
/*
* Copyright (c) 2018, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef __TFM_SPM_SERVICES_API_H__
#define __TFM_SPM_SERVICES_API_H__
enum tfm_spm_request_type_t {
TFM_SPM_REQUEST_RESET_VOTE,
};
/**
* \brief Request a vote from SPM on a system reset
*
* \return Returns 0 if request is accepted, any other value means reject
*/
int32_t tfm_spm_request_reset_vote(void);
#endif /* __TFM_SPM_SERVICES_API_H__ */

View File

@ -0,0 +1,328 @@
/*
* Copyright (c) 2017-2019, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
/* This file contains the APIs exported by the SPM to tfm core */
#include <stdio.h>
#include <string.h>
#include "spm_api.h"
#include "platform/include/tfm_spm_hal.h"
#include "secure_utilities.h"
#include "spm_db_setup.h"
#include "tfm_internal.h"
#include "tfm_api.h"
#include "tfm_nspm.h"
#include "secure_fw/core/tfm_core.h"
#include "platform_retarget.h"
#include "tfm_peripherals_def.h"
#include "spm_partition_defs.h"
struct spm_partition_db_t g_spm_partition_db = {0,};
typedef enum {
TFM_INIT_FAILURE,
} sp_error_type_t;
/*
* This function is called when a secure partition causes an error.
* In case of an error in the error handling, a non-zero value have to be
* returned.
*/
static void tfm_spm_partition_err_handler(
struct spm_partition_desc_t *partition,
sp_error_type_t err_type,
int32_t err_code)
{
#ifdef TFM_CORE_DEBUG
if (err_type == TFM_INIT_FAILURE) {
printf("Partition init failed for partition id 0x%08X\r\n",
partition->static_data.partition_id);
} else {
printf("Unknown partition error %d for partition id 0x%08X\r\n",
err_type, partition->static_data.partition_id);
}
#endif
tfm_spm_partition_set_state(partition->static_data.partition_id,
SPM_PARTITION_STATE_CLOSED);
}
uint32_t get_partition_idx(uint32_t partition_id)
{
int i;
if (partition_id == INVALID_PARTITION_ID) {
return SPM_INVALID_PARTITION_IDX;
}
for (i = 0; i < g_spm_partition_db.partition_count; ++i) {
if (g_spm_partition_db.partitions[i].static_data.partition_id ==
partition_id) {
return i;
}
}
return SPM_INVALID_PARTITION_IDX;
}
enum spm_err_t tfm_spm_db_init(void)
{
struct spm_partition_desc_t *part_ptr;
tfm_memset (&g_spm_partition_db, 0, sizeof(g_spm_partition_db));
/* This function initialises partition db */
g_spm_partition_db.running_partition_idx = SPM_INVALID_PARTITION_IDX;
g_spm_partition_db.partition_count = 0;
/* There are a few partitions that are used by TF-M internally.
* These are explicitly added to the partition db here.
*/
/* For the non secure Execution environment */
#if TFM_LVL != 1
extern uint32_t Image$$ARM_LIB_STACK$$ZI$$Base[];
extern uint32_t Image$$ARM_LIB_STACK$$ZI$$Limit[];
uint32_t psp_stack_bottom = (uint32_t)Image$$ARM_LIB_STACK$$ZI$$Base;
uint32_t psp_stack_top = (uint32_t)Image$$ARM_LIB_STACK$$ZI$$Limit;
#endif
if (g_spm_partition_db.partition_count >= SPM_MAX_PARTITIONS) {
return SPM_ERR_INVALID_CONFIG;
}
part_ptr = &(g_spm_partition_db.partitions[
g_spm_partition_db.partition_count]);
part_ptr->static_data.partition_id = TFM_SP_NON_SECURE_ID;
part_ptr->static_data.partition_flags = 0;
#if TFM_LVL != 1
part_ptr->memory_data.stack_bottom = psp_stack_bottom;
part_ptr->memory_data.stack_top = psp_stack_top;
/* Since RW, ZI and stack are configured as one MPU region, configure
* RW start address to psp_stack_bottom to get RW access to stack
*/
part_ptr->memory_data.rw_start = psp_stack_bottom;
#endif
part_ptr->runtime_data.partition_state = SPM_PARTITION_STATE_UNINIT;
tfm_nspm_configure_clients();
++g_spm_partition_db.partition_count;
/* For the TF-M core environment itself */
if (g_spm_partition_db.partition_count >= SPM_MAX_PARTITIONS) {
return SPM_ERR_INVALID_CONFIG;
}
part_ptr = &(g_spm_partition_db.partitions[
g_spm_partition_db.partition_count]);
part_ptr->static_data.partition_id = TFM_SP_CORE_ID;
part_ptr->static_data.partition_flags =
SPM_PART_FLAG_APP_ROT | SPM_PART_FLAG_PSA_ROT;
part_ptr->runtime_data.partition_state = SPM_PARTITION_STATE_UNINIT;
++g_spm_partition_db.partition_count;
/* Add user-defined secure partitions */
#include "tfm_partition_list.inc"
g_spm_partition_db.is_init = 1;
return SPM_ERR_OK;
}
enum spm_err_t tfm_spm_partition_init(void)
{
struct spm_partition_desc_t *part;
struct tfm_sfn_req_s desc;
int32_t args[4] = {0};
int32_t fail_cnt = 0;
uint32_t idx;
/* Call the init function for each partition */
for (idx = 0; idx < g_spm_partition_db.partition_count; ++idx) {
part = &g_spm_partition_db.partitions[idx];
#ifdef TFM_PSA_API
if (part->static_data.partition_flags & SPM_PART_FLAG_IPC) {
continue;
}
#endif
tfm_spm_hal_configure_default_isolation(part->platform_data);
if (part->static_data.partition_init == NULL) {
tfm_spm_partition_set_state(idx, SPM_PARTITION_STATE_IDLE);
tfm_spm_partition_set_caller_partition_idx(idx,
SPM_INVALID_PARTITION_IDX);
} else {
int32_t res;
desc.args = args;
desc.ns_caller = 0;
desc.sfn = (sfn_t)part->static_data.partition_init;
desc.sp_id = part->static_data.partition_id;
res = tfm_core_sfn_request(&desc);
if (res == TFM_SUCCESS) {
tfm_spm_partition_set_state(idx, SPM_PARTITION_STATE_IDLE);
} else {
tfm_spm_partition_err_handler(part, TFM_INIT_FAILURE, res);
fail_cnt++;
}
}
}
tfm_secure_api_init_done();
if (fail_cnt == 0) {
return SPM_ERR_OK;
} else {
return SPM_ERR_PARTITION_NOT_AVAILABLE;
}
}
#if TFM_LVL != 1
enum spm_err_t tfm_spm_partition_sandbox_config(uint32_t partition_idx)
{
struct spm_partition_desc_t *part;
if (!g_spm_partition_db.is_init) {
return SPM_ERR_PARTITION_DB_NOT_INIT;
}
part = &g_spm_partition_db.partitions[partition_idx];
return tfm_spm_hal_partition_sandbox_config(&(part->memory_data),
part->platform_data);
}
enum spm_err_t tfm_spm_partition_sandbox_deconfig(uint32_t partition_idx)
{
/* This function takes a partition id and disables the
* SPM partition for that partition
*/
struct spm_partition_desc_t *part;
part = &g_spm_partition_db.partitions[partition_idx];
return tfm_spm_hal_partition_sandbox_deconfig(&(part->memory_data),
part->platform_data);
}
uint32_t tfm_spm_partition_get_stack_bottom(uint32_t partition_idx)
{
return g_spm_partition_db.partitions[partition_idx].
memory_data.stack_bottom;
}
uint32_t tfm_spm_partition_get_stack_top(uint32_t partition_idx)
{
return g_spm_partition_db.partitions[partition_idx].memory_data.stack_top;
}
uint32_t tfm_spm_partition_get_zi_start(uint32_t partition_idx)
{
return g_spm_partition_db.partitions[partition_idx].
memory_data.zi_start;
}
uint32_t tfm_spm_partition_get_zi_limit(uint32_t partition_idx)
{
return g_spm_partition_db.partitions[partition_idx].
memory_data.zi_limit;
}
uint32_t tfm_spm_partition_get_rw_start(uint32_t partition_idx)
{
return g_spm_partition_db.partitions[partition_idx].
memory_data.rw_start;
}
uint32_t tfm_spm_partition_get_rw_limit(uint32_t partition_idx)
{
return g_spm_partition_db.partitions[partition_idx].
memory_data.rw_limit;
}
void tfm_spm_partition_set_stack(uint32_t partition_idx, uint32_t stack_ptr)
{
g_spm_partition_db.partitions[partition_idx].
runtime_data.stack_ptr = stack_ptr;
}
#endif
void tfm_spm_partition_store_context(uint32_t partition_idx,
uint32_t stack_ptr, uint32_t lr)
{
g_spm_partition_db.partitions[partition_idx].
runtime_data.stack_ptr = stack_ptr;
g_spm_partition_db.partitions[partition_idx].
runtime_data.lr = lr;
}
uint32_t tfm_spm_partition_get_partition_id(uint32_t partition_idx)
{
return g_spm_partition_db.partitions[partition_idx].static_data.
partition_id;
}
uint32_t tfm_spm_partition_get_flags(uint32_t partition_idx)
{
return g_spm_partition_db.partitions[partition_idx].static_data.
partition_flags;
}
const struct spm_partition_runtime_data_t *
tfm_spm_partition_get_runtime_data(uint32_t partition_idx)
{
return &(g_spm_partition_db.partitions[partition_idx].runtime_data);
}
void tfm_spm_partition_set_state(uint32_t partition_idx, uint32_t state)
{
g_spm_partition_db.partitions[partition_idx].runtime_data.partition_state =
state;
if (state == SPM_PARTITION_STATE_RUNNING) {
g_spm_partition_db.running_partition_idx = partition_idx;
}
}
void tfm_spm_partition_set_caller_partition_idx(uint32_t partition_idx,
uint32_t caller_partition_idx)
{
g_spm_partition_db.partitions[partition_idx].runtime_data.
caller_partition_idx = caller_partition_idx;
}
void tfm_spm_partition_set_caller_client_id(uint32_t partition_idx,
int32_t caller_client_id)
{
g_spm_partition_db.partitions[partition_idx].runtime_data.
caller_client_id = caller_client_id;
}
enum spm_err_t tfm_spm_partition_set_share(uint32_t partition_idx,
uint32_t share)
{
enum spm_err_t ret = SPM_ERR_OK;
#if TFM_LVL != 1
/* Only need to set configuration on levels higher than 1 */
ret = tfm_spm_hal_set_share_region(share);
#endif
if (ret == SPM_ERR_OK) {
g_spm_partition_db.partitions[partition_idx].runtime_data.share = share;
}
return ret;
}
uint32_t tfm_spm_partition_get_running_partition_idx(void)
{
return g_spm_partition_db.running_partition_idx;
}
void tfm_spm_partition_cleanup_context(uint32_t partition_idx)
{
struct spm_partition_desc_t *partition =
&(g_spm_partition_db.partitions[partition_idx]);
partition->runtime_data.caller_partition_idx = SPM_INVALID_PARTITION_IDX;
partition->runtime_data.share = 0;
}

View File

@ -0,0 +1,287 @@
/*
* Copyright (c) 2017-2019, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef __SPM_API_H__
#define __SPM_API_H__
/* This file contains the apis exported by the SPM to tfm core */
#include "spm_partition_defs.h"
#include "secure_fw/core/tfm_secure_api.h"
#define SPM_INVALID_PARTITION_IDX (~0U)
enum spm_err_t {
SPM_ERR_OK = 0,
SPM_ERR_PARTITION_DB_NOT_INIT,
SPM_ERR_PARTITION_ALREADY_ACTIVE,
SPM_ERR_PARTITION_NOT_AVAILABLE,
SPM_ERR_INVALID_CONFIG,
};
enum spm_part_state_t {
SPM_PARTITION_STATE_UNINIT = 0,
SPM_PARTITION_STATE_IDLE,
SPM_PARTITION_STATE_RUNNING,
SPM_PARTITION_STATE_SUSPENDED,
SPM_PARTITION_STATE_BLOCKED,
SPM_PARTITION_STATE_CLOSED
};
enum spm_part_flag_mask_t {
SPM_PART_FLAG_APP_ROT = 0x01,
SPM_PART_FLAG_PSA_ROT = 0x02,
SPM_PART_FLAG_IPC = 0x04
};
/**
* \brief Runtime context information of a partition
*/
struct spm_partition_runtime_data_t {
uint32_t partition_state;
uint32_t caller_partition_idx;
int32_t caller_client_id;
uint32_t share;
uint32_t stack_ptr;
uint32_t lr;
};
/**
* \brief Returns the index of the partition with the given partition ID.
*
* \param[in] partition_id Partition id
*
* \return the partition idx if partition_id is valid,
* \ref SPM_INVALID_PARTITION_IDX othervise
*/
uint32_t get_partition_idx(uint32_t partition_id);
/**
* \brief Configure isolated sandbox for a partition
*
* \param[in] partition_idx Partition index
*
* \return Error code \ref spm_err_t
*
* \note This function doesn't check if partition_idx is valid.
*/
enum spm_err_t tfm_spm_partition_sandbox_config(uint32_t partition_idx);
/**
* \brief Deconfigure sandbox for a partition
*
* \param[in] partition_idx Partition index
*
* \return Error code \ref spm_err_t
*
* \note This function doesn't check if partition_idx is valid.
*/
enum spm_err_t tfm_spm_partition_sandbox_deconfig(uint32_t partition_idx);
/**
* \brief Get bottom of stack region for a partition
*
* \param[in] partition_idx Partition index
*
* \return Stack region bottom value
*
* \note This function doesn't check if partition_idx is valid.
*/
uint32_t tfm_spm_partition_get_stack_bottom(uint32_t partition_idx);
/**
* \brief Get top of stack region for a partition
*
* \param[in] partition_idx Partition index
*
* \return Stack region top value
*
* \note This function doesn't check if partition_idx is valid.
*/
uint32_t tfm_spm_partition_get_stack_top(uint32_t partition_idx);
/**
* \brief Get the id of the partition for its index from the db
*
* \param[in] partition_idx Partition index
*
* \return Partition ID for that partition
*
* \note This function doesn't check if partition_idx is valid.
*/
uint32_t tfm_spm_partition_get_partition_id(uint32_t partition_idx);
/**
* \brief Get the flags associated with a partition
*
* \param[in] partition_idx Partition index
*
* \return Flags associated with the partition
*
* \note This function doesn't check if partition_idx is valid.
*/
uint32_t tfm_spm_partition_get_flags(uint32_t partition_idx);
/**
* \brief Get the start of the zero-initialised region for a partition
*
* \param[in] partition_idx Partition idx
*
* \return Start of the zero-initialised region
*
* \note This function doesn't check if partition_idx is valid.
*/
uint32_t tfm_spm_partition_get_zi_start(uint32_t partition_idx);
/**
* \brief Get the limit of the zero-initialised region for a partition
*
* \param[in] partition_idx Partition idx
*
* \return Limit of the zero-initialised region
*
* \note This function doesn't check if partition_idx is valid.
* \note The address returned is not part of the region.
*/
uint32_t tfm_spm_partition_get_zi_limit(uint32_t partition_idx);
/**
* \brief Get the start of the read-write region for a partition
*
* \param[in] partition_idx Partition idx
*
* \return Start of the read-write region
*
* \note This function doesn't check if partition_idx is valid.
*/
uint32_t tfm_spm_partition_get_rw_start(uint32_t partition_idx);
/**
* \brief Get the limit of the read-write region for a partition
*
* \param[in] partition_idx Partition idx
*
* \return Limit of the read-write region
*
* \note This function doesn't check if partition_idx is valid.
* \note The address returned is not part of the region.
*/
uint32_t tfm_spm_partition_get_rw_limit(uint32_t partition_idx);
/**
* \brief Get the current runtime data of a partition
*
* \param[in] partition_idx Partition index
*
* \return The runtime data of the specified partition
*
* \note This function doesn't check if partition_idx is valid.
*/
const struct spm_partition_runtime_data_t *
tfm_spm_partition_get_runtime_data(uint32_t partition_idx);
/**
* \brief Returns the index of the partition that has running state
*
* \return The index of the partition with the running state, if there is any
* set. 0 otherwise.
*/
uint32_t tfm_spm_partition_get_running_partition_idx(void);
/**
* \brief Save stack pointer for partition in database
*
* \param[in] partition_idx Partition index
* \param[in] stack_ptr Stack pointer to be stored
*
* \note This function doesn't check if partition_idx is valid.
*/
void tfm_spm_partition_set_stack(uint32_t partition_id, uint32_t stack_ptr);
/**
* \brief Save stack pointer and link register for partition in database
*
* \param[in] partition_idx Partition index
* \param[in] stack_ptr Stack pointer to be stored
* \param[in] lr Link register to be stored
*
* \note This function doesn't check if partition_idx is valid.
*/
void tfm_spm_partition_store_context(uint32_t partition_idx,
uint32_t stack_ptr, uint32_t lr);
/**
* \brief Set the current state of a partition
*
* \param[in] partition_idx Partition index
* \param[in] state The state to be set
*
* \note This function doesn't check if partition_idx is valid.
* \note The \ref state has to have the value set of \ref spm_part_state_t.
*/
void tfm_spm_partition_set_state(uint32_t partition_idx, uint32_t state);
/**
* \brief Set the caller partition index for a given partition
*
* \param[in] partition_idx Partition index
* \param[in] caller_partition_idx The index of the caller partition
*
* \note This function doesn't check if any of the partition_idxs are valid.
*/
void tfm_spm_partition_set_caller_partition_idx(uint32_t partition_idx,
uint32_t caller_partition_idx);
/**
* \brief Set the caller client ID for a given partition
*
* \param[in] partition_idx Partition index
* \param[in] caller_client_id The ID of the calling client
*
* \note This function doesn't check if any of the partition_idxs are valid.
*/
void tfm_spm_partition_set_caller_client_id(uint32_t partition_idx,
int32_t caller_client_id);
/**
* \brief Set the buffer share region of the partition
*
* \param[in] partition_idx Partition index
* \param[in] share The buffer share region to be set
*
* \return Error code \ref spm_err_t
*
* \note This function doesn't check if partition_idx is valid.
* \note share has to have the value set of \ref tfm_buffer_share_region_e
*/
enum spm_err_t tfm_spm_partition_set_share(uint32_t partition_idx,
uint32_t share);
/**
* \brief Initialize partition database
*
* \return Error code \ref spm_err_t
*/
enum spm_err_t tfm_spm_db_init(void);
/**
* \brief Execute partition init function
*
* \return Error code \ref spm_err_t
*/
enum spm_err_t tfm_spm_partition_init(void);
/**
* \brief Clears the context info from the database for a partition.
*
* \param[in] partition_idx Partition index
*
* \note This function doesn't check if partition_idx is valid.
*/
void tfm_spm_partition_cleanup_context(uint32_t partition_idx);
#endif /*__SPM_API_H__ */

View File

@ -0,0 +1,87 @@
/*
* Copyright (c) 2017-2019, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef __SPM_DB_H__
#define __SPM_DB_H__
#ifdef TFM_PSA_API
#include "tfm_thread.h"
#endif
struct spm_partition_desc_t;
struct spm_partition_db_t;
uint32_t get_partition_idx(uint32_t partition_id);
typedef int32_t(*sp_init_function)(void);
#define TFM_PARTITION_TYPE_APP "APPLICATION-ROT"
#define TFM_PARTITION_TYPE_PSA "PSA-ROT"
#define TFM_STACK_SIZE 1024
#ifdef TFM_PSA_API
enum tfm_partition_priority {
TFM_PRIORITY_LOW = THRD_PRIOR_LOWEST,
TFM_PRIORITY_NORMAL = THRD_PRIOR_MEDIUM,
TFM_PRIORITY_HIGH = THRD_PRIOR_HIGHEST,
};
#else
enum tfm_partition_priority {
TFM_PRIORITY_LOW = 0xFF,
TFM_PRIORITY_NORMAL = 0x7F,
TFM_PRIORITY_HIGH = 0,
};
#endif
#define TFM_PRIORITY(LEVEL) TFM_PRIORITY_##LEVEL
/**
* Holds the fields of the partition DB used by the SPM code. The values of
* these fields are calculated at compile time, and set during initialisation
* phase.
*/
struct spm_partition_static_data_t {
uint32_t partition_id;
uint32_t partition_flags;
uint32_t partition_priority;
sp_init_function partition_init;
};
/**
* Holds the fields that define a partition for SPM. The fields are further
* divided to structures, to keep the related fields close to each other.
*/
struct spm_partition_desc_t {
struct spm_partition_static_data_t static_data;
struct spm_partition_runtime_data_t runtime_data;
struct tfm_spm_partition_platform_data_t *platform_data;
#if TFM_LVL != 1
struct tfm_spm_partition_memory_data_t memory_data;
#endif
#ifdef TFM_PSA_API
struct tfm_thrd_ctx sp_thrd;
/*
* stack_limit points to starting address of the partitions' stack plus the partitions' stack size.
*/
uint32_t stack_limit;
uint32_t stack_size;
#endif
};
/* Macros to pick linker symbols and allow to form the partition data base */
#define REGION(a, b, c) a##b##c
#define REGION_NAME(a, b, c) REGION(a, b, c)
#if TFM_LVL == 1
#define REGION_DECLARE(a, b, c)
#else
#define REGION_DECLARE(a, b, c) extern uint32_t REGION_NAME(a, b, c)
#define PART_REGION_ADDR(partition, region) \
(uint32_t)&REGION_NAME(Image$$, partition, region)
#endif
#endif /* __SPM_DB_H__ */

View File

@ -0,0 +1,130 @@
/*
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef __SPM_DB_SETUP_H__
#define __SPM_DB_SETUP_H__
#include <stdint.h>
#include "spm_db.h"
/**
* \brief Get the index of a partition.
*
* Gets the index of a partition in the partition db based on the partition ID
* provided as a parameter.
*
* \param[in] partition_id The ID of the partition
*
* \return \ref INVALID_PARTITION_IDX if the provided ID is invalid. The index
* of the partition otherwise.
*/
uint32_t get_partition_idx(uint32_t partition_id);
struct spm_partition_db_t {
uint32_t is_init;
uint32_t partition_count;
uint32_t running_partition_idx;
struct spm_partition_desc_t partitions[SPM_MAX_PARTITIONS];
};
#define PARTITION_INIT_STATIC_DATA(data, partition, flags, id, priority) \
do { \
data.partition_id = partition##_ID; \
data.partition_flags = flags; \
data.partition_priority = TFM_PRIORITY(priority); \
} while (0)
#if TFM_LVL == 1
#define PARTITION_INIT_MEMORY_DATA(data, partition)
#else
#define PARTITION_INIT_MEMORY_DATA(data, partition) \
do { \
data.code_start = PART_REGION_ADDR(partition, $$Base); \
data.code_limit = PART_REGION_ADDR(partition, $$Limit); \
data.ro_start = PART_REGION_ADDR(partition, $$RO$$Base); \
data.ro_limit = PART_REGION_ADDR(partition, $$RO$$Limit); \
data.rw_start = PART_REGION_ADDR(partition, _DATA$$RW$$Base); \
data.rw_limit = PART_REGION_ADDR(partition, _DATA$$RW$$Limit); \
data.zi_start = PART_REGION_ADDR(partition, _DATA$$ZI$$Base); \
data.zi_limit = PART_REGION_ADDR(partition, _DATA$$ZI$$Limit); \
data.stack_bottom = PART_REGION_ADDR(partition, _STACK$$ZI$$Base); \
data.stack_top = PART_REGION_ADDR(partition, _STACK$$ZI$$Limit); \
} while (0)
#endif
#if TFM_LVL == 1
#define PARTITION_INIT_RUNTIME_DATA(data, partition) \
do { \
data.partition_state = SPM_PARTITION_STATE_UNINIT; \
} while (0)
#else
#define PARTITION_INIT_RUNTIME_DATA(data, partition) \
do { \
data.partition_state = SPM_PARTITION_STATE_UNINIT; \
data.stack_ptr = \
PART_REGION_ADDR(partition, _STACK$$ZI$$Limit); \
} while (0)
#endif
#define PARTITION_DECLARE(partition, flag, type, id, priority, part_stack_size) \
do { \
REGION_DECLARE(Image$$, partition, $$Base); \
REGION_DECLARE(Image$$, partition, $$Limit); \
REGION_DECLARE(Image$$, partition, $$RO$$Base); \
REGION_DECLARE(Image$$, partition, $$RO$$Limit); \
REGION_DECLARE(Image$$, partition, _DATA$$RW$$Base); \
REGION_DECLARE(Image$$, partition, _DATA$$RW$$Limit); \
REGION_DECLARE(Image$$, partition, _DATA$$ZI$$Base); \
REGION_DECLARE(Image$$, partition, _DATA$$ZI$$Limit); \
REGION_DECLARE(Image$$, partition, _STACK$$ZI$$Base); \
REGION_DECLARE(Image$$, partition, _STACK$$ZI$$Limit); \
int32_t flags = flag; \
if (tfm_memcmp(type, TFM_PARTITION_TYPE_APP, \
strlen(TFM_PARTITION_TYPE_APP)) == 0) { \
flags |= SPM_PART_FLAG_APP_ROT; \
} else if (tfm_memcmp(type, TFM_PARTITION_TYPE_PSA, \
strlen(TFM_PARTITION_TYPE_PSA)) == 0) { \
flags |= SPM_PART_FLAG_PSA_ROT | SPM_PART_FLAG_APP_ROT; \
} else { \
return SPM_ERR_INVALID_CONFIG; \
} \
struct spm_partition_desc_t *part_ptr; \
if (g_spm_partition_db.partition_count >= SPM_MAX_PARTITIONS) { \
return SPM_ERR_INVALID_CONFIG; \
} \
__attribute__((section(".data.partitions_stacks"))) \
static uint8_t partition##_stack[part_stack_size] __attribute__((aligned(8))); \
part_ptr = &(g_spm_partition_db.partitions[ \
g_spm_partition_db.partition_count]); \
part_ptr->stack_limit = (uint32_t)partition##_stack; \
part_ptr->stack_size = part_stack_size; \
PARTITION_INIT_STATIC_DATA(part_ptr->static_data, partition, flags, \
id, priority); \
PARTITION_INIT_RUNTIME_DATA(part_ptr->runtime_data, partition); \
PARTITION_INIT_MEMORY_DATA(part_ptr->memory_data, partition); \
++g_spm_partition_db.partition_count; \
} while (0)
#define PARTITION_ADD_INIT_FUNC(partition, init_func) \
do { \
extern int32_t init_func(void); \
uint32_t partition_idx = get_partition_idx(partition##_ID); \
struct spm_partition_desc_t *part_ptr = \
&(g_spm_partition_db.partitions[partition_idx]); \
part_ptr->static_data.partition_init = init_func; \
} while (0)
#define PARTITION_ADD_PERIPHERAL(partition, peripheral) \
do { \
uint32_t partition_idx = get_partition_idx(partition##_ID); \
struct spm_partition_desc_t *part_ptr = \
&(g_spm_partition_db.partitions[partition_idx]); \
part_ptr->platform_data = peripheral; \
} while (0)
#endif /* __SPM_DB_SETUP_H__ */

View File

@ -0,0 +1,40 @@
/*
* Copyright (c) 2017-2018, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef __SPM_PARTITION_DEFS_H__
#define __SPM_PARTITION_DEFS_H__
/* FixMe: allocations to be settled.
* 8 bits reserved by TFM for secure partition Id in this prototype
*/
#define TFM_SP_BASE 256
/* A reserved partition ID that is used for uninitialised data */
#define INVALID_PARTITION_ID (~0U)
/* ***** partition ID-s internal to the TFM ***** */
#define TFM_INTERNAL_PARTITIONS (2)
/* From the SPM point of view the non secure processing environment is handled
* as a special secure partition. This simplifies the context switch
* operations.
*/
#define TFM_SP_NON_SECURE_ID (0)
/* A dummy partition for TFM_SP_CORE is created to handle secure partition
* calls done directly from the core, before NS execution started.
*/
#define TFM_SP_CORE_ID (1)
#include "tfm_partition_defs.inc"
/* This limit is only used to define the size of the database reserved for
* partitions. There's no requirement that it match the number of partitions
* that get registered in a specific build
*/
#define SPM_MAX_PARTITIONS (TFM_MAX_USER_PARTITIONS + TFM_INTERNAL_PARTITIONS)
#endif /* __SPM_PARTITION_DEFS_H__ */

View File

@ -0,0 +1,2 @@
Unless specifically indicated otherwise in a file, TF-M files in this directory are licensed under the BSD-3-Clause license,
as can be found in: LICENSE-bsd-3-clause.txt

View File

@ -0,0 +1,26 @@
Copyright 2019 Arm Limited and affiliates.
SPDX-License-Identifier: BSD-3-Clause
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,144 @@
/*
* Copyright (c) 2018, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef __PSA_CLIENT_H__
#define __PSA_CLIENT_H__
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stddef.h>
/*********************** PSA Client Macros and Types *************************/
#define PSA_FRAMEWORK_VERSION (0x0100)
#define PSA_VERSION_NONE (0)
/* PSA response types */
#define PSA_SUCCESS (0)
#define PSA_CONNECTION_REFUSED (INT32_MIN + 1)
#define PSA_CONNECTION_BUSY (INT32_MIN + 2)
#define PSA_DROP_CONNECTION (INT32_MIN)
/* PSA message handles */
#define PSA_NULL_HANDLE ((psa_handle_t)0)
typedef int32_t psa_status_t;
typedef int32_t psa_handle_t;
/**
* A read-only input memory region provided to an RoT Service.
*/
typedef struct psa_invec {
const void *base; /*!< the start address of the memory buffer */
size_t len; /*!< the size in bytes */
} psa_invec;
/**
* A writable output memory region provided to an RoT Service.
*/
typedef struct psa_outvec {
void *base; /*!< the start address of the memory buffer */
size_t len; /*!< the size in bytes */
} psa_outvec;
/*************************** PSA Client API **********************************/
/**
* \brief Retrieve the version of the PSA Framework API that is implemented.
*
* \return version The version of the PSA Framework implementation
* that is providing the runtime services to the
* caller. The major and minor version are encoded
* as follows:
* \arg version[15:8] -- major version number.
* \arg version[7:0] -- minor version number.
*/
uint32_t psa_framework_version(void);
/**
* \brief Retrieve the minor version of an RoT Service or indicate that it is
* not present on this system.
*
* \param[in] sid ID of the RoT Service to query.
*
* \retval PSA_VERSION_NONE The RoT Service is not implemented, or the
* caller is not permitted to access the service.
* \retval > 0 The minor version of the implemented RoT
* Service.
*/
uint32_t psa_version(uint32_t sid);
/**
* \brief Connect to an RoT Service by its SID.
*
* \param[in] sid ID of the RoT Service to connect to.
* \param[in] minor_version Requested version of the RoT Service.
*
* \retval > 0 A handle for the connection.
* \retval PSA_CONNECTION_REFUSED The SPM or RoT Service has refused the
* connection.
* \retval PSA_CONNECTION_BUSY The SPM or RoT Service cannot make the
* connection at the moment.
* \retval "Does not return" The RoT Service ID and version are not
* supported, or the caller is not permitted to
* access the service.
*/
psa_handle_t psa_connect(uint32_t sid, uint32_t minor_version);
/**
* \brief Call an RoT Service on an established connection.
*
* \param[in] handle A handle to an established connection.
* \param[in] in_vec Array of input \ref psa_invec structures.
* \param[in] in_len Number of input \ref psa_invec structures.
* \param[in/out] out_vec Array of output \ref psa_outvec structures.
* \param[in] out_len Number of output \ref psa_outvec structures.
*
* \retval >=0 RoT Service-specific status value.
* \retval <0 RoT Service-specific error code.
* \retval PSA_DROP_CONNECTION The connection has been dropped by the RoT
* Service. This indicates that either this or
* a previous message was invalid.
* \retval "Does not return" The call is invalid, one or more of the
* following are true:
* \arg An invalid handle was passed.
* \arg The connection is already handling a request.
* \arg An invalid memory reference was provided.
* \arg in_len + out_len > PSA_MAX_IOVEC.
* \arg The message is unrecognized by the RoT
* Service or incorrectly formatted.
*/
psa_status_t psa_call(psa_handle_t handle,
const psa_invec *in_vec,
size_t in_len,
psa_outvec *out_vec,
size_t out_len);
/**
* \brief Close a connection to an RoT Service.
*
* \param[in] handle A handle to an established connection, or the
* null handle.
*
* \retval void Success.
* \retval "Does not return" The call is invalid, one or more of the
* following are true:
* \arg An invalid handle was provided that is not
* the null handle.
* \arg The connection is handling a request.
*/
void psa_close(psa_handle_t handle);
#ifdef __cplusplus
}
#endif
#endif /* __PSA_CLIENT_H__ */

View File

@ -0,0 +1,252 @@
/*
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef __PSA_SERVICE_H__
#define __PSA_SERVICE_H__
#ifdef __cplusplus
extern "C" {
#endif
#include <inttypes.h>
/********************** PSA Secure Partition Macros and Types ****************/
/* PSA wait timeouts */
#define PSA_POLL (0x00000000u)
#define PSA_BLOCK (0x80000000u)
/* A mask value that includes all Secure Partition signals */
#define PSA_WAIT_ANY (~0u)
/* Doorbell signal */
#define PSA_DOORBELL (0x00000008u)
/* PSA message types */
#define PSA_IPC_CONNECT (1)
#define PSA_IPC_CALL (2)
#define PSA_IPC_DISCONNECT (3)
/* Maximum number of input and output vectors */
#define PSA_MAX_IOVEC (4)
/* Return code from psa_get() */
#define PSA_ERR_NOMSG (INT32_MIN + 3)
/* Store a set of one or more Secure Partition signals */
typedef uint32_t psa_signal_t;
/**
* Describe a message received by an RoT Service after calling \ref psa_get().
*/
typedef struct psa_msg_t {
uint32_t type; /* One of the following values:
* \ref PSA_IPC_CONNECT
* \ref PSA_IPC_CALL
* \ref PSA_IPC_DISCONNECT
*/
psa_handle_t handle; /* A reference generated by the SPM to the
* message returned by psa_get().
*/
int32_t client_id; /* Partition ID of the sender of the message */
void *rhandle; /* Be useful for binding a connection to some
* application-specific data or function
* pointer within the RoT Service
* implementation.
*/
size_t in_size[PSA_MAX_IOVEC]; /* Provide the size of each client input
* vector in bytes.
*/
size_t out_size[PSA_MAX_IOVEC];/* Provide the size of each client output
* vector in bytes.
*/
} psa_msg_t;
/************************* PSA Secure Partition API **************************/
/**
* \brief Return the Secure Partition interrupt signals that have been asserted
* from a subset of signals provided by the caller.
*
* \param[in] signal_mask A set of signals to query. Signals that are not
* in this set will be ignored.
* \param[in] timeout Specify either blocking \ref PSA_BLOCK or
* polling \ref PSA_POLL operation.
*
* \retval >0 At least one signal is asserted.
* \retval 0 No signals are asserted. This is only seen when
* a polling timeout is used.
*/
psa_signal_t psa_wait(psa_signal_t signal_mask, uint32_t timeout);
/**
* \brief Retrieve the message which corresponds to a given RoT Service signal
* and remove the message from the RoT Service queue.
*
* \param[in] signal The signal value for an asserted RoT Service.
* \param[out] msg Pointer to \ref psa_msg_t object for receiving
* the message.
*
* \retval PSA_SUCCESS Success, *msg will contain the delivered
* message.
* \retval PSA_ERR_NOMSG Message could not be delivered.
* \retval "Does not return" The call is invalid because one or more of the
* following are true:
* \arg signal has more than a single bit set.
* \arg signal does not correspond to an RoT Service.
* \arg The RoT Service signal is not currently
* asserted.
* \arg The msg pointer provided is not a valid memory
* reference.
*/
psa_status_t psa_get(psa_signal_t signal, psa_msg_t *msg);
/**
* \brief Associate some RoT Service private data with a client connection.
*
* \param[in] msg_handle Handle for the client's message.
* \param[in] rhandle Reverse handle allocated by the RoT Service.
*
* \retval void Success, rhandle will be provided with all
* subsequent messages delivered on this
* connection.
* \retval "Does not return" msg_handle is invalid.
*/
void psa_set_rhandle(psa_handle_t msg_handle, void *rhandle);
/**
* \brief Read a message parameter or part of a message parameter from a client
* input vector.
*
* \param[in] msg_handle Handle for the client's message.
* \param[in] invec_idx Index of the input vector to read from. Must be
* less than \ref PSA_MAX_IOVEC.
* \param[out] buffer Buffer in the Secure Partition to copy the
* requested data to.
* \param[in] num_bytes Maximum number of bytes to be read from the
* client input vector.
*
* \retval >0 Number of bytes copied.
* \retval 0 There was no remaining data in this input
* vector.
* \retval "Does not return" The call is invalid, one or more of the
* following are true:
* \arg msg_handle is invalid.
* \arg msg_handle does not refer to a
* \ref PSA_IPC_CALL message.
* \arg invec_idx is equal to or greater than
* \ref PSA_MAX_IOVEC.
* \arg the memory reference for buffer is invalid or
* not writable.
*/
size_t psa_read(psa_handle_t msg_handle, uint32_t invec_idx,
void *buffer, size_t num_bytes);
/**
* \brief Skip over part of a client input vector.
*
* \param[in] msg_handle Handle for the client's message.
* \param[in] invec_idx Index of input vector to skip from. Must be
* less than \ref PSA_MAX_IOVEC.
* \param[in] num_bytes Maximum number of bytes to skip in the client
* input vector.
*
* \retval >0 Number of bytes skipped.
* \retval 0 There was no remaining data in this input
* vector.
* \retval "Does not return" The call is invalid, one or more of the
* following are true:
* \arg msg_handle is invalid.
* \arg msg_handle does not refer to a
* \ref PSA_IPC_CALL message.
* \arg invec_idx is equal to or greater than
* \ref PSA_MAX_IOVEC.
*/
size_t psa_skip(psa_handle_t msg_handle, uint32_t invec_idx, size_t num_bytes);
/**
* \brief Write a message response to a client output vector.
*
* \param[in] msg_handle Handle for the client's message.
* \param[out] outvec_idx Index of output vector in message to write to.
* Must be less than \ref PSA_MAX_IOVEC.
* \param[in] buffer Buffer with the data to write.
* \param[in] num_bytes Number of bytes to write to the client output
* vector.
*
* \retval void Success
* \retval "Does not return" The call is invalid, one or more of the
* following are true:
* \arg msg_handle is invalid.
* \arg msg_handle does not refer to a
* \ref PSA_IPC_CALL message.
* \arg outvec_idx is equal to or greater than
* \ref PSA_MAX_IOVEC.
* \arg The memory reference for buffer is invalid.
* \arg The call attempts to write data past the end
* of the client output vector.
*/
void psa_write(psa_handle_t msg_handle, uint32_t outvec_idx,
const void *buffer, size_t num_bytes);
/**
* \brief Complete handling of a specific message and unblock the client.
*
* \param[in] msg_handle Handle for the client's message.
* \param[in] status Message result value to be reported to the
* client.
*
* \retval void Success.
* \retval "Does not return" The call is invalid, one or more of the
* following are true:
* \arg msg_handle is invalid.
* \arg An invalid status code is specified for the
* type of message.
*/
void psa_reply(psa_handle_t msg_handle, psa_status_t status);
/**
* \brief Send a PSA_DOORBELL signal to a specific Secure Partition.
*
* \param[in] partition_id Secure Partition ID of the target partition.
*
* \retval void Success.
* \retval "Does not return" partition_id does not correspond to a Secure
* Partition.
*/
void psa_notify(int32_t partition_id);
/**
* \brief Clear the PSA_DOORBELL signal.
*
* \param[in] void
*
* \retval void Success.
* \retval "Does not return" The Secure Partition's doorbell signal is not
* currently asserted.
*/
void psa_clear(void);
/**
* \brief Inform the SPM that an interrupt has been handled (end of interrupt).
*
* \param[in] irq_signal The interrupt signal that has been processed.
*
* \retval void Success.
* \retval "Does not return" The call is invalid, one or more of the
* following are true:
* \arg irq_signal is not an interrupt signal.
* \arg irq_signal indicates more than one signal.
* \arg irq_signal is not currently asserted.
*/
void psa_eoi(psa_signal_t irq_signal);
#ifdef __cplusplus
}
#endif
#endif /* __PSA_SERVICE_H__ */

View File

@ -0,0 +1,131 @@
/*
* Copyright (c) 2017-2019, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef __TFM_API_H__
#define __TFM_API_H__
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include "interface/include/psa_client.h"
#define TFM_INVALID_CLIENT_ID 0
/**
* \brief Checks if the provided client ID is a secure client ID.
*
* \param[in] client_id Client ID to check
*
* \return Returns 1 if the client Id is secure. Otherwise, returns 0.
*/
#define TFM_CLIENT_ID_IS_S(client_id) ((client_id)>0)
/**
* \brief Checks if the provided client ID is a non-secure client ID.
*
* \param[in] client_id Client ID to check
*
* \return Returns 1 if the client Id is non-secure. Otherwise, returns 0.
*/
#define TFM_CLIENT_ID_IS_NS(client_id) ((client_id)<0)
/* FixMe: sort out DEBUG compile option and limit return value options
* on external interfaces */
/* Note:
* TFM will only return values recognized and parsed by TFM core.
* Service return codes are not automatically passed on to REE.
* Any non-zero return value is interpreted as an error that may trigger
* TEE error handling flow.
*/
enum tfm_status_e
{
TFM_SUCCESS = 0,
TFM_PARTITION_BUSY,
TFM_ERROR_SECURE_DOMAIN_LOCKED,
TFM_ERROR_INVALID_PARAMETER,
TFM_ERROR_PARTITION_NON_REENTRANT,
TFM_ERROR_NS_THREAD_MODE_CALL,
TFM_ERROR_NOT_INITIALIZED,
TFM_ERROR_NO_ACTIVE_PARTITION,
TFM_ERROR_INVALID_EXC_MODE,
TFM_SECURE_LOCK_FAILED,
TFM_SECURE_UNLOCK_FAILED,
TFM_ERROR_GENERIC = 0x1F,
TFM_PARTITION_SPECIFIC_ERROR_MIN,
};
//==================== Secure function declarations ==========================//
/**
* \brief Assign client ID to the current TZ context
*
* \param[in] ns_client_id The client ID to be assigned to the current
* context
* \return TFM_SUCCESS if the client ID assigned successfully, an error code
* according to \ref tfm_status_e in case of error.
*
* \note This function have to be called from handler mode.
*/
enum tfm_status_e tfm_register_client_id (int32_t ns_client_id);
/**
* \brief Retrieve the version of the PSA Framework API that is implemented
*
* \return The version of the PSA Framework
*/
uint32_t tfm_psa_framework_version_veneer(void);
/**
* \brief Return version of secure function provided by secure binary
*
* \param[in] sid ID of secure service
*
* \return Version number of secure function
*/
uint32_t tfm_psa_version_veneer(uint32_t sid);
/**
* \brief Connect to secure function
*
* \param[in] sid ID of secure service
* \param[in] minor_version Minor version of SF requested by client
*
* \return Returns handle to connection
*/
psa_handle_t tfm_psa_connect_veneer(uint32_t sid, uint32_t minor_version);
/**
* \brief Call a secure function referenced by a connection handle
*
* \param[in] handle Handle to connection
* \param[in] in_vecs invec containing pointer/count of input vectors
* \param[in] out_vecs outvec containing pointer/count of output vectors
*
* \return Returns \ref psa_status_t status code
*/
psa_status_t tfm_psa_call_veneer(psa_handle_t handle,
const psa_invec *in_vecs,
psa_outvec *out_vecs);
/**
* \brief Close connection to secure function referenced by a connection handle
*
* \param[in] handle Handle to connection
*
* \return Returns \ref psa_status_t status code
*/
psa_status_t tfm_psa_close_veneer(psa_handle_t handle);
//================ End Secure function declarations ==========================//
#ifdef __cplusplus
}
#endif
#endif /* __TFM_API_H__ */

View File

@ -0,0 +1,41 @@
/*
* Copyright (c) 2017-2018, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef __TFM_NS_LOCK_H__
#define __TFM_NS_LOCK_H__
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
typedef int32_t (*veneer_fn) (uint32_t arg0, uint32_t arg1,
uint32_t arg2, uint32_t arg3);
/**
* \brief NS world, NS lock based dispatcher
*
* \details To be called from the wrapper API interface
*/
uint32_t tfm_ns_lock_dispatch(veneer_fn fn,
uint32_t arg0, uint32_t arg1,
uint32_t arg2, uint32_t arg3);
/**
* \brief NS world, Init NS lock
*
* \details Needs to be called during non-secure app init
* to initialize the TFM NS lock object
*/
uint32_t tfm_ns_lock_init();
#ifdef __cplusplus
}
#endif
#endif /* __TFM_NS_LOCK_H__ */

View File

@ -0,0 +1,68 @@
/*
* Copyright (c) 2017-2018, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include <stdio.h>
#include <cmsis_compiler.h>
#ifndef __TFM_NS_SVC_H__
#define __TFM_NS_SVC_H__
#ifdef __cplusplus
extern "C" {
#endif
/**
* \brief Include all the SVC handler headers
*/
#include "tfm_nspm_svc_handler.h"
/**
* \brief Macro to encode an svc instruction
*
*/
#define SVC(code) __ASM("svc %0" : : "I" (code))
/**
* \def LIST_SVC_NSPM
*
* \brief This is an X macro which lists
* the SVC interface exposed by TF-M
* for the NS OS.
*
*/
#define LIST_SVC_NSPM \
X(SVC_TFM_NSPM_REGISTER_CLIENT_ID, tfm_nspm_svc_register_client_id) \
/**
* \brief Numbers associated to each SVC available
*
* \details Start from 1 as 0 is reserved by RTX
*/
enum tfm_svc_num {
SVC_INVALID = 0,
#define X(SVC_ENUM, SVC_HANDLER) SVC_ENUM,
/* SVC API for Services */
#ifdef TFM_NS_CLIENT_IDENTIFICATION
LIST_SVC_NSPM
#endif
#undef X
/* add all the new entries above this line */
SVC_TFM_MAX,
};
/* number of user SVC functions */
#define USER_SVC_COUNT (SVC_TFM_MAX - 1)
#ifdef __cplusplus
}
#endif
#endif /* __TFM_NS_SVC_H__ */

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2018, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef __TFM_NSPM_SVC_HANDLER_H__
#define __TFM_NSPM_SVC_HANDLER_H__
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* \brief Reports the client ID of this task to TF-M (SVC function)
*
* \param [in] client_id Client ID to register.
*
* \return Returns 1 if the client ID was successfully reported 0 otherwise
*/
uint32_t tfm_nspm_svc_register_client_id(uint32_t client_id);
#ifdef __cplusplus
}
#endif
#endif /* __TFM_NSPM_SVC_HANDLER_H__ */

View File

@ -0,0 +1,119 @@
# TF-M integration to Mbed-OS
This document is an initial draft for TF-M for Mbed-OS porting guide .
## Audience
This guide is intended for developers wishing to port Mbed-OS with TF-M used as a secure kernel for ARMv8-M targets.
Prior knowledge with both TF-M & Mbed-OS concepts is assumed.
## Build system concepts:
Mbed-OS build system is based on [Mbed-CLI](https://github.com/ARMmbed/mbed-cli).
Mbed-CLI build system performs lookup for source and header files within project directory and adds them all to a build. All folders will be scanned for sources except for:
- folders starting with `TARGET_*`
- folders starting with `COMPONENT_*`
- folders starting with `FEATURE_*`
- folders starting with `TESTS_*` (not true for `mbed test` builds)
- files and folders listed in `.mbedignore`
The ignored folders listed above can be explicitly added to a compilation by adding following keys to a target description in `targets.json`:
- adding `extra_labels_add`, `inherits` and `sub_target` for adding `TARGET_*`
- adding `components_add` for adding `COMPONENT_*`
- `features_add` for adding `FEATURE_*`
TF-M is built as bare-metal in a secure target, in order to build a secure target with TF-M as its' kernel need to add `--app-config <MBED-OS-ROOT>/tools/psa/tfm/mbed_app.json` to the build command of the secure target.
## Build hooks
Mbed-OS testing tools are designed to work with a single image (`.bin` or `.hex`).
When building mbed-os for ARMv8-M targets two images are created. One for normal world(NW) and one for TrustZone(TZ).
Mbed-OS build system provides `post_binary_hook` that allows executing arbitrary Python script for merging NW and TZ images. Typically `post_binary_hook` is added to NW target and assumes TZ target images as a prerequisite.
## Porting ARMv8-M targets
Typically firmware for ARMv8-M targets consist of 2 or more images: normal world and TrustZone image. More images can be present in case boot loaders are used.
Two images must be built and linked separately. TrustZone image must be built first.
There may be code and/or header files sharing between the two targets.
Nested folder layout typically provides more easy code reuse between two targets:
Example:
```txt
└── tragets
└── TARGET_<VENDOR>
└── TARGET_<BOARD>
├── common_files <-- files shared between NW and TZ images
├── TARGET_<BOARD>_NS
│   └── normal_world_files <-- files only to be included for NW build
└── TARGET_<BOARD>_S
└── trustzone_files <-- files only to be included for TZ build
```
The target should be represented in a following way in `target.json` (`MUSCA_A1` is taken for example and should be substituted):
```json
...
"ARM_MUSCA_A1": {
"public": false,
"inherits": ["Target"],
"supported_toolchains": ["ARMC6", "GCC_ARM"],
"default_toolchain": "ARMC6",
"extra_labels": ["ARM_SSG", "MUSCA_A1"],
},
"ARM_MUSCA_A1_NS": {
"inherits": ["NSPE_Target", "ARM_MUSCA_A1"],
"core": "Cortex-M33-NS",
"device_has_add": ["INTERRUPTIN", "LPTICKER", "SERIAL", "SLEEP", "USTICKER"],
"macros": [
"MBED_TZ_DEFAULT_ACCESS=1",
"MBED_FAULT_HANDLER_DISABLED",
"TFM_PSA_API",
"MBEDTLS_PSA_CRYPTO_C"
],
"extra_labels_add": ["MUSCA_A1_NS", "PSA", "TFM"],
"post_binary_hook": {"function": "ArmMuscaA1Code.binary_hook"}
},
"ARM_MUSCA_A1_S": {
"inherits": ["SPE_Target", "ARM_MUSCA_A1"],
"core": "Cortex-M33",
"device_has_add": ["FLASH"],
"macros": [
"MBED_FAULT_HANDLER_DISABLED",
"MBED_MPU_CUSTOM",
"BYPASS_NVSTORE_CHECK",
"TFM_LVL=1",
"TFM_PSA_API",
"MBEDTLS_PSA_CRYPTO_SPM",
"MBEDTLS_PSA_CRYPTO_C",
"MBEDTLS_ENTROPY_NV_SEED",
"MBEDTLS_PLATFORM_NV_SEED_READ_MACRO=mbed_default_seed_read",
"MBEDTLS_PLATFORM_NV_SEED_WRITE_MACRO=mbed_default_seed_write"
],
"components_add": ["FLASHIAP"],
"extra_labels_add": ["MUSCA_A1_S", "PSA", "TFM"]
},
```
Example details:
- Secure target:
- `"device_has_add": ["FLASH"]` and `"components_add": ["FLASHIAP"]` for enabling storage stack. Required by PSA Internal storage service.
- `"extra_labels_add": ["PSA", "TFM"]` are required to add PSA services and TF-M SPM implementation sources to a compilation
- all the macros from the example above are required
- must inherit from `SPE_Target`
- Nonsecure target:
- must inherit from `NSPE_Target`
- `"extra_labels_add": ["PSA", "TFM"]` are required to add PSA services and TF-M SPM implementation sources to a compilation
- all the macros from the example above are required
- `post_binary_hook` is used to combine secure and non-secure images
### HAL
For porting Mbed-OS & TF-M both Mbed-OS and TF-M HAL layers should be created.
#### Mbed-OS HAL:
Follow instructions for [Mbed-OS HAL porting](https://os.mbed.com/docs/mbed-os/v5.11/porting/porting-hal-modules.html)
#### TF-M:
Mbed-OS contains customized TF-M version. TF-M services reference implementation was replaced by Mbed-OS version. Thus TF-M has different HAL layer comparing to vanilla [TF-M reference implementation](https://git.trustedfirmware.org/trusted-firmware-m.git/about/).
The porting layer consists of:
- All functions listed in: `components/TARGET_PSA/TARGET_TFM/COMPONENT_SPE/platform/include/tfm_spm_hal.h`
- Flash API `mbed-os/hal/flash_api.h` implementation is required for TZ image. It is used by PSA Internal trusted storage implementation.

View File

@ -62,6 +62,11 @@ uint32_t psa_security_lifecycle_state(void);
psa_status_t mbed_psa_reboot_and_request_new_security_state(uint32_t new_state);
/** \brief Resets the system
*
*/
void psa_system_reset();
#ifdef __cplusplus
}
#endif

View File

@ -24,9 +24,7 @@
#include <stdlib.h>
#include <string.h>
#include "psa_crypto_srv_ifs.h"
#include "psa/client.h"
#include "crypto.h"
#include "crypto_platform_spe.h"

View File

@ -1,10 +1,15 @@
// ---------------------------------- Includes ---------------------------------
#include "psa/service.h"
#include "psa/client.h"
#include <stdint.h>
#include <string.h>
#include "psa/client.h"
#include "psa/service.h"
#if defined(TARGET_TFM)
#define SPM_PANIC(format, ...) \
{ \
while(1){}; \
}
#endif
#define PSA_CRYPTO_SECURE 1
#include "crypto_spe.h"
#include "crypto_platform_spe.h"
@ -446,7 +451,11 @@ static void psa_hash_operation(void)
case PSA_HASH_CLONE_BEGIN: {
size_t index = 0;
#if defined(TARGET_MBED_SPM)
status = reserve_hash_clone(psa_identity(msg.handle), msg.rhandle, &index);
#else
status = reserve_hash_clone(msg.client_id, msg.rhandle, &index);
#endif
if (status == PSA_SUCCESS) {
psa_write(msg.handle, 0, &index, sizeof(index));
}
@ -462,7 +471,11 @@ static void psa_hash_operation(void)
SPM_PANIC("SPM read length mismatch");
}
#if defined(TARGET_MBED_SPM)
status = get_hash_clone(index, psa_identity(msg.handle), &hash_clone);
#else
status = get_hash_clone(index, msg.client_id, &hash_clone);
#endif
if (status == PSA_SUCCESS) {
status = psa_hash_clone(hash_clone->source_operation, msg.rhandle);
release_hash_clone(hash_clone);
@ -1488,7 +1501,12 @@ void psa_crypto_generator_operations(void)
void crypto_main(void *ptr)
{
while (1) {
uint32_t signals = psa_wait_any(PSA_BLOCK);
uint32_t signals = 0;
#if defined(TARGET_MBED_SPM)
signals = psa_wait_any(PSA_BLOCK);
#else
signals = psa_wait(CRYPTO_SRV_WAIT_ANY_SID_MSK, PSA_BLOCK);
#endif
if (signals & PSA_CRYPTO_INIT) {
psa_crypto_init_operation();
}

View File

@ -28,3 +28,8 @@ psa_status_t mbed_psa_reboot_and_request_new_security_state(uint32_t new_state)
{
return psa_platfrom_lifecycle_change_request_impl(new_state);
}
void psa_system_reset(void)
{
psa_system_reset_impl();
}

View File

@ -18,6 +18,8 @@
#include "psa/lifecycle.h"
#include "psa/internal_trusted_storage.h"
#include "platform_srv_impl.h"
#include "mbed_toolchain.h"
#include "cmsis.h"
#ifndef MBED_CONF_LIFECYCLE_STATE
#define MBED_CONF_LIFECYCLE_STATE PSA_LIFECYCLE_ASSEMBLY_AND_TEST
@ -38,3 +40,9 @@ psa_status_t psa_platfrom_lifecycle_change_request_impl(uint32_t state)
}
return PSA_LIFECYCLE_ERROR;
}
MBED_WEAK void psa_system_reset_impl(void)
{
/* Reset the system */
NVIC_SystemReset();
}

View File

@ -22,5 +22,6 @@
psa_status_t psa_platfrom_lifecycle_get_impl(uint32_t *lc_state);
psa_status_t psa_platfrom_lifecycle_change_request_impl(uint32_t lc_state);
void psa_system_reset_impl(void);
#endif // __PLATFROM_SRV_IMPL_H__

View File

@ -18,6 +18,7 @@
#include "psa_platform_ifs.h"
#include "psa/lifecycle.h"
#include "psa/client.h"
#include "mbed_toolchain.h"
uint32_t psa_security_lifecycle_state(void)
{
@ -56,3 +57,12 @@ psa_status_t mbed_psa_reboot_and_request_new_security_state(uint32_t new_state)
return status;
}
MBED_NORETURN void psa_system_reset(void)
{
psa_handle_t conn = psa_connect(PSA_PLATFORM_LC_SET, 1);
if (conn <= PSA_NULL_HANDLE) {
return;
}
psa_call(conn, NULL, 0, NULL, 0);
}

View File

@ -77,6 +77,18 @@ spm_rot_service_t platform_rot_services[PLATFORM_ROT_SRV_COUNT] = {
.tail = NULL
}
},
{
.sid = PSA_PLATFORM_SYSTEM_RESET,
.mask = PSA_PLATFORM_SYSTEM_RESET_MSK,
.partition = NULL,
.min_version = 1,
.min_version_policy = PSA_MINOR_VERSION_POLICY_RELAXED,
.allow_nspe = true,
.queue = {
.head = NULL,
.tail = NULL
}
},
};
/* External SIDs used by PLATFORM */

View File

@ -20,6 +20,13 @@
#include "psa/internal_trusted_storage.h"
#include "psa/service.h"
#if defined(TARGET_TFM)
#define SPM_PANIC(format, ...) \
{ \
while(1){}; \
}
#endif
typedef psa_status_t (*SignalHandler)(psa_msg_t *);
static psa_status_t lifecycle_get(psa_msg_t *msg)
@ -52,6 +59,12 @@ static psa_status_t lifecycle_change_request(psa_msg_t *msg)
}
static psa_status_t system_reset_request(psa_msg_t *msg)
{
(void)msg;
psa_system_reset_impl();
}
static void message_handler(psa_msg_t *msg, SignalHandler handler)
{
psa_status_t status = PSA_SUCCESS;
@ -77,7 +90,12 @@ void platform_partition_entry(void *ptr)
uint32_t signals = 0;
psa_msg_t msg = {0};
while (1) {
#if defined(TARGET_MBED_SPM)
signals = psa_wait_any(PSA_BLOCK);
#else
signals = psa_wait(PLATFORM_WAIT_ANY_SID_MSK, PSA_BLOCK);
#endif
if ((signals & PSA_PLATFORM_LC_GET_MSK) != 0) {
psa_get(PSA_PLATFORM_LC_GET_MSK, &msg);
message_handler(&msg, lifecycle_get);
@ -86,5 +104,9 @@ void platform_partition_entry(void *ptr)
psa_get(PSA_PLATFORM_LC_SET_MSK, &msg);
message_handler(&msg, lifecycle_change_request);
}
if ((signals & PSA_PLATFORM_SYSTEM_RESET_MSK) != 0) {
psa_get(PSA_PLATFORM_SYSTEM_RESET_MSK, &msg);
message_handler(&msg, system_reset_request);
}
}
}

View File

@ -28,7 +28,7 @@
#define PLATFORM_ID 8
#define PLATFORM_ROT_SRV_COUNT (2UL)
#define PLATFORM_ROT_SRV_COUNT (3UL)
#define PLATFORM_EXT_ROT_SRV_COUNT (1UL)
/* PLATFORM event flags */
@ -44,10 +44,13 @@
#define PSA_PLATFORM_LC_GET_MSK (1UL << PSA_PLATFORM_LC_GET_MSK_POS)
#define PSA_PLATFORM_LC_SET_MSK_POS (5UL)
#define PSA_PLATFORM_LC_SET_MSK (1UL << PSA_PLATFORM_LC_SET_MSK_POS)
#define PSA_PLATFORM_SYSTEM_RESET_MSK_POS (6UL)
#define PSA_PLATFORM_SYSTEM_RESET_MSK (1UL << PSA_PLATFORM_SYSTEM_RESET_MSK_POS)
#define PLATFORM_WAIT_ANY_SID_MSK (\
PSA_PLATFORM_LC_GET_MSK | \
PSA_PLATFORM_LC_SET_MSK)
PSA_PLATFORM_LC_SET_MSK | \
PSA_PLATFORM_SYSTEM_RESET_MSK)
#endif // PSA_PLATFORM_PARTITION_H

View File

@ -21,6 +21,14 @@
"non_secure_clients": true,
"minor_version": 1,
"minor_policy": "RELAXED"
},
{
"name": "PSA_PLATFORM_SYSTEM_RESET",
"identifier": "0x00011002",
"signal": "PSA_PLATFORM_SYSTEM_RESET_MSK",
"non_secure_clients": true,
"minor_version": 1,
"minor_policy": "RELAXED"
}
],
"extern_sids": [

View File

@ -28,5 +28,6 @@
#define PSA_PLATFORM_LC_GET 0x00011000
#define PSA_PLATFORM_LC_SET 0x00011001
#define PSA_PLATFORM_SYSTEM_RESET 0x00011002
#endif // PSA_PLATFORM_PARTITION_ROT_SERVICES_H

View File

@ -0,0 +1,158 @@
/* 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 <cstring>
#include "KVStore.h"
#include "TDBStore.h"
#include "psa/internal_trusted_storage.h"
#include "pits_impl.h"
#include "mbed_error.h"
#include "mbed_toolchain.h"
#include "FlashIAP.h"
#include "FlashIAPBlockDevice.h"
using namespace mbed;
static KVStore *internal_store = NULL;
static bool is_tfm_kv_initialized = false;
static inline uint32_t align_up(uint64_t val, uint64_t size)
{
return (((val - 1) / size) + 1) * size;
}
static inline uint32_t align_down(uint64_t val, uint64_t size)
{
return (((val) / size)) * size;
}
static BlockDevice *_get_blockdevice(bd_addr_t start_address, bd_size_t size)
{
int ret = MBED_SUCCESS;
bd_addr_t flash_end_address;
bd_addr_t flash_start_address;
bd_addr_t aligned_start_address;
bd_addr_t aligned_end_address;
bd_addr_t end_address;
FlashIAP flash;
ret = flash.init();
if (ret != 0) {
return NULL;
}
//Get flash parameters before starting
flash_start_address = flash.get_flash_start();
flash_end_address = flash_start_address + flash.get_flash_size();;
aligned_start_address = align_down(start_address, flash.get_sector_size(start_address));
if (start_address != aligned_start_address) {
flash.deinit();
return NULL;
}
end_address = start_address + size;
if (end_address > flash_end_address) {
flash.deinit();
return NULL;
}
aligned_end_address = align_up(end_address, flash.get_sector_size(end_address - 1));
if (end_address != aligned_end_address) {
flash.deinit();
return NULL;
}
static FlashIAPBlockDevice bd(start_address, size);
flash.deinit();
return &bd;
}
static int _calculate_blocksize_match_tdbstore(BlockDevice *bd)
{
bd_size_t size = bd->size();
bd_size_t erase_size = bd->get_erase_size();
bd_size_t number_of_sector = size / erase_size;
if (number_of_sector < 2) {
return -1;
}
return 0;
}
static int tfm_kv_init(void)
{
int ret = MBED_SUCCESS;
bd_size_t internal_size = MBED_CONF_STORAGE_TDB_INTERNAL_INTERNAL_SIZE;
bd_addr_t internal_start_address = MBED_CONF_STORAGE_TDB_INTERNAL_INTERNAL_BASE_ADDRESS;
//Get internal memory FLASHIAP block device.
BlockDevice *internal_bd = _get_blockdevice(internal_start_address, internal_size);
if (internal_bd == NULL) {
return -1; // TODO: Error code
}
ret = internal_bd->init();
if (ret != 0) {
return ret;
}
//Check that internal flash has 2 or more sectors
if (_calculate_blocksize_match_tdbstore(internal_bd) != 0) {
return -1; // TODO: Error code
}
//Deinitialize internal block device and TDB will reinitialize and take control on it.
ret = internal_bd->deinit();
if (ret != 0) {
return ret;
}
//Create a TDBStore in the internal FLASHIAP block device.
static TDBStore tdb_internal(internal_bd);
internal_store = &tdb_internal;
ret = internal_store->init();
return ret;
}
/*
* \brief Get default KVStore instance for internal flesh storage
*
* \return valid pointer to KVStore
*/
KVStore *get_its_kvstore_instance(void)
{
return internal_store;
}
int kv_init_storage_config()
{
int ret = MBED_SUCCESS;
if (!is_tfm_kv_initialized) {
ret = tfm_kv_init();
}
is_tfm_kv_initialized = (ret == MBED_SUCCESS) ? true : false;
return ret;
}

View File

@ -16,21 +16,38 @@
*/
#include <cstring>
#include "KVMap.h"
#include "KVStore.h"
#include "TDBStore.h"
#include "psa/internal_trusted_storage.h"
#include "pits_impl.h"
#include "pits_version_impl.h"
#include "mbed_error.h"
#include "mbed_assert.h"
#include "mbed_toolchain.h"
#if defined(TARGET_TFM)
using namespace mbed;
#ifdef __cplusplus
extern "C"
KVStore *get_its_kvstore_instance(void);
#else
#include "KVMap.h"
using namespace mbed;
/*
* \brief Get default KVStore instance for internal flesh storage
*
* \return valid pointer to KVStore
*/
KVStore *get_its_kvstore_instance(void)
{
#endif
KVMap &kv_map = KVMap::get_instance();
return kv_map.get_internal_kv_instance(STR_EXPAND(MBED_CONF_STORAGE_DEFAULT_KV));
}
#endif // defined(TARGET_TFM)
// Maximum length of filename we use for kvstore API.
// pid: 6; delimiter: 1; uid: 11; str terminator: 1
@ -50,10 +67,16 @@ const uint8_t base64_coding_table[] = {
static KVStore *kvstore = NULL;
MBED_WEAK psa_its_status_t its_version_migrate(void *storage, const its_version_t *version)
{
(void)storage;
(void)version;
return PSA_ITS_SUCCESS;
}
static void its_init(void)
{
KVMap &kv_map = KVMap::get_instance();
kvstore = kv_map.get_internal_kv_instance(STR_EXPAND(MBED_CONF_STORAGE_DEFAULT_KV));
kvstore = get_its_kvstore_instance();
if (!kvstore) {
// Can only happen due to system misconfiguration.
// Thus considered as unrecoverable error for runtime.
@ -105,19 +128,6 @@ static void its_init(void)
}
}
// used from test only
void its_deinit(void)
{
kvstore = NULL;
}
MBED_WEAK psa_its_status_t its_version_migrate(void *storage, const its_version_t *version)
{
(void)storage;
(void)version;
return PSA_ITS_SUCCESS;
}
/*
* \brief Convert KVStore stauts codes to PSA internal storage status codes
*
@ -316,7 +326,3 @@ psa_its_status_t psa_its_reset_impl()
int status = kvstore->reset();
return convert_status(status);
}
#ifdef __cplusplus
}
#endif

View File

@ -25,6 +25,9 @@ extern "C"
{
#endif
#if defined(TARGET_TFM) && defined(COMPONENT_SPE)
extern int kv_init_storage_config();
#endif
#define PITS_DATA_PTR_AT_OFFSET(ptr, offset) ((void *)(((uintptr_t)ptr) + ((uintptr_t)offset)))
#define STR_EXPAND(tok) #tok
@ -34,6 +37,8 @@ psa_its_status_t psa_its_get_info_impl(int32_t pid, psa_its_uid_t uid, struct ps
psa_its_status_t psa_its_remove_impl(int32_t pid, psa_its_uid_t uid);
psa_its_status_t psa_its_reset_impl();
psa_its_status_t psa_its_reset_impl(void);
#ifdef __cplusplus
}
#endif

View File

@ -33,7 +33,7 @@
/* Threads stacks */
MBED_ALIGN(8) uint8_t its_thread_stack[1024] = {0};
MBED_ALIGN(8) uint8_t its_thread_stack[2048] = {0};
/* Threads control blocks */
osRtxThread_t its_thread_cb = {0};
@ -45,7 +45,7 @@ osThreadAttr_t its_thread_attr = {
.cb_mem = &its_thread_cb,
.cb_size = sizeof(its_thread_cb),
.stack_mem = its_thread_stack,
.stack_size = 1024,
.stack_size = 2048,
.priority = osPriorityNormal,
.tz_module = 0,
.reserved = 0
@ -124,7 +124,7 @@ static const osMutexAttr_t its_mutex_attr = {
};
extern void pits_entry(void *ptr);
extern void its_entry(void *ptr);
void its_init(spm_partition_t *partition)
{
@ -142,7 +142,7 @@ void its_init(spm_partition_t *partition)
}
partition->rot_services = its_rot_services;
partition->thread_id = osThreadNew(pits_entry, NULL, &its_thread_attr);
partition->thread_id = osThreadNew(its_entry, NULL, &its_thread_attr);
if (NULL == partition->thread_id) {
SPM_PANIC("Failed to create start main thread of partition its!\n");
}

View File

@ -21,14 +21,25 @@
#include "psa_its_partition.h"
#include "psa/internal_trusted_storage.h"
#include "pits_impl.h"
#include "kv_config.h"
#include "mbed_error.h"
#if defined(TARGET_MBED_SPM)
#include "kv_config.h"
#endif
#ifdef __cplusplus
extern "C"
{
#endif
#if defined(TARGET_TFM)
#define SPM_PANIC(format, ...) \
{ \
while(1){}; \
}
#endif
typedef psa_status_t (*SignalHandler)(psa_msg_t *);
static psa_status_t storage_set(psa_msg_t *msg)
@ -59,9 +70,11 @@ static psa_status_t storage_set(psa_msg_t *msg)
free(data);
return PSA_ITS_ERROR_STORAGE_FAILURE;
}
#if defined(TARGET_MBED_SPM)
psa_its_status_t status = psa_its_set_impl(psa_identity(msg->handle), key, alloc_size, data, flags);
#else
psa_its_status_t status = psa_its_set_impl(msg->client_id, key, alloc_size, data, flags);
#endif
memset(data, 0, alloc_size);
free(data);
return status;
@ -89,7 +102,12 @@ static psa_status_t storage_get(psa_msg_t *msg)
return PSA_ITS_ERROR_STORAGE_FAILURE;
}
#if defined(TARGET_MBED_SPM)
psa_its_status_t status = psa_its_get_impl(psa_identity(msg->handle), key, offset, msg->out_size[0], data);
#else
psa_its_status_t status = psa_its_get_impl(msg->client_id, key, offset, msg->out_size[0], data);
#endif
if (status == PSA_ITS_SUCCESS) {
psa_write(msg->handle, 0, data, msg->out_size[0]);
}
@ -112,7 +130,12 @@ static psa_status_t storage_info(psa_msg_t *msg)
return PSA_DROP_CONNECTION;
}
#if defined(TARGET_MBED_SPM)
psa_its_status_t status = psa_its_get_info_impl(psa_identity(msg->handle), key, &info);
#else
psa_its_status_t status = psa_its_get_info_impl(msg->client_id, key, &info);
#endif
if (status == PSA_ITS_SUCCESS) {
psa_write(msg->handle, 0, &info, msg->out_size[0]);
}
@ -132,15 +155,20 @@ static psa_status_t storage_remove(psa_msg_t *msg)
return PSA_DROP_CONNECTION;
}
#if defined(TARGET_MBED_SPM)
return psa_its_remove_impl(psa_identity(msg->handle), key);
#else
return psa_its_remove_impl(msg->client_id, key);
#endif
}
static psa_status_t storage_reset(psa_msg_t *msg)
{
(void)msg;
return psa_its_reset_impl();
}
static void message_handler(psa_msg_t *msg, SignalHandler handler)
{
psa_status_t status = PSA_SUCCESS;
@ -161,13 +189,17 @@ static void message_handler(psa_msg_t *msg, SignalHandler handler)
psa_reply(msg->handle, status);
}
void pits_entry(void *ptr)
void its_entry(void *ptr)
{
uint32_t signals = 0;
psa_msg_t msg = {0};
while (1) {
#if defined(TARGET_MBED_SPM)
signals = psa_wait_any(PSA_BLOCK);
#else
signals = psa_wait(ITS_WAIT_ANY_SID_MSK, PSA_BLOCK);
#endif
// KVStore initiation:
// - Must be done after the psa_wait_any() call since only now we know OS initialization is done
@ -197,6 +229,7 @@ void pits_entry(void *ptr)
psa_get(PSA_ITS_RESET_MSK, &msg);
message_handler(&msg, storage_reset);
}
}
}

View File

@ -3,8 +3,8 @@
"type": "APPLICATION-ROT",
"priority": "NORMAL",
"id": "0x0000000A",
"entry_point": "pits_entry",
"stack_size": "0x400",
"entry_point": "its_entry",
"stack_size": "0x800",
"heap_size": "0x400",
"services": [{
"name": "PSA_ITS_GET",

View File

@ -14,6 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <time.h>
#include "platform/platform.h"
#include "platform/FilePath.h"
@ -904,6 +905,9 @@ extern "C" long PREFIX(_flen)(FILEHANDLE fh)
return size;
}
// Do not compile this code for TFM secure target
#if !defined(COMPONENT_SPE) || !defined(TARGET_TFM)
extern "C" char Image$$RW_IRAM1$$ZI$$Limit[];
extern "C" MBED_WEAK __value_in_regs struct __initial_stackheap _mbed_user_setup_stackheap(uint32_t R0, uint32_t R1, uint32_t R2, uint32_t R3)
@ -924,6 +928,8 @@ extern "C" __value_in_regs struct __initial_stackheap __user_setup_stackheap(uin
return _mbed_user_setup_stackheap(R0, R1, R2, R3);
}
#endif // !defined(COMPONENT_SPE) || !defined(TARGET_TFM)
#endif
@ -1205,7 +1211,7 @@ extern "C" WEAK void __cxa_pure_virtual(void)
// SP. This make it compatible with RTX RTOS thread stacks.
#if defined(TOOLCHAIN_GCC_ARM)
#if defined(TARGET_CORTEX_A)
#if defined(TARGET_CORTEX_A) || (defined(TARGET_TFM) && defined(COMPONENT_SPE))
extern "C" uint32_t __HeapLimit;
#endif
@ -1234,7 +1240,7 @@ extern "C" WEAK caddr_t _sbrk(int incr)
unsigned char *prev_heap = heap;
unsigned char *new_heap = heap + incr;
#if defined(TARGET_CORTEX_A)
#if defined(TARGET_CORTEX_A) || (defined(TARGET_TFM) && defined(COMPONENT_SPE))
if (new_heap >= (unsigned char *)&__HeapLimit) { /* __HeapLimit is end of heap section */
#else
if (new_heap >= (unsigned char *)__get_MSP()) {

View File

@ -28,6 +28,9 @@
#include "spm_init.h"
#include "spm_api.h"
#endif
#if defined(TARGET_TFM) && defined(COMPONENT_NSPE)
#include "TARGET_TFM/interface/include/tfm_ns_lock.h"
#endif
#if defined(COMPONENT_NSPE) && defined(COMPONENT_SPM_MAILBOX)
@ -98,9 +101,12 @@ MBED_NORETURN void mbed_rtos_start()
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)
#endif // defined(TARGET_MBED_SPM)
#if defined(TARGET_TFM) && defined(COMPONENT_NSPE)
tfm_ns_lock_init();
#endif // defined(TARGET_TFM) && defined(COMPONENT_NSPE)
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

@ -0,0 +1,101 @@
{
"files" : [
{
"src_file" : "interface/src/tfm_ns_lock_rtx.c",
"dest_file" : "components/TARGET_PSA/TARGET_TFM/COMPONENT_NSPE/interface/src/tfm_ns_lock_rtx.c"
},
{
"src_file" : "interface/src/tfm_psa_ns_api.c",
"dest_file" : "components/TARGET_PSA/TARGET_TFM/COMPONENT_NSPE/interface/src/tfm_psa_ns_api.c"
},
{
"src_file" : "interface/include/psa_client.h",
"dest_file" : "components/TARGET_PSA/TARGET_TFM/interface/include/psa_client.h"
},
{
"src_file" : "interface/include/psa_service.h",
"dest_file" : "components/TARGET_PSA/TARGET_TFM/interface/include/psa_service.h"
},
{
"src_file" : "interface/include/tfm_api.h",
"dest_file" : "components/TARGET_PSA/TARGET_TFM/interface/include/tfm_api.h"
},
{
"src_file" : "interface/include/tfm_ns_lock.h",
"dest_file" : "components/TARGET_PSA/TARGET_TFM/interface/include/tfm_ns_lock.h"
},
{
"src_file" : "interface/include/tfm_ns_svc.h",
"dest_file" : "components/TARGET_PSA/TARGET_TFM/interface/include/tfm_ns_svc.h"
},
{
"src_file" : "interface/include/tfm_nspm_svc_handler.h",
"dest_file" : "components/TARGET_PSA/TARGET_TFM/interface/include/tfm_nspm_svc_handler.h"
},
{
"src_file" : "platform/include/tfm_plat_boot_seed.h",
"dest_file" : "components/TARGET_PSA/TARGET_TFM/COMPONENT_SPE/platform/include/tfm_plat_boot_seed.h"
},
{
"src_file" : "platform/include/tfm_plat_defs.h",
"dest_file" : "components/TARGET_PSA/TARGET_TFM/COMPONENT_SPE/platform/include/tfm_plat_defs.h"
},
{
"src_file" : "platform/include/tfm_plat_device_id.h",
"dest_file" : "components/TARGET_PSA/TARGET_TFM/COMPONENT_SPE/platform/include/tfm_plat_device_id.h"
},
{
"src_file" : "platform/include/tfm_spm_hal.h",
"dest_file" : "components/TARGET_PSA/TARGET_TFM/COMPONENT_SPE/platform/include/tfm_spm_hal.h"
},
{
"src_file" : "platform/ext/driver/Driver_Common.h",
"dest_file" : "components/TARGET_PSA/TARGET_TFM/COMPONENT_SPE/platform/ext/driver/Driver_Common.h"
},
{
"src_file" : "platform/ext/driver/Driver_MPC.h",
"dest_file" : "components/TARGET_PSA/TARGET_TFM/COMPONENT_SPE/platform/ext/driver/Driver_MPC.h"
},
{
"src_file" : "platform/ext/driver/Driver_PPC.h",
"dest_file" : "components/TARGET_PSA/TARGET_TFM/COMPONENT_SPE/platform/ext/driver/Driver_PPC.h"
}
],
"folders" : [
{
"src_folder" : "secure_fw/core",
"dest_folder" : "components/TARGET_PSA/TARGET_TFM/COMPONENT_SPE/secure_fw/core"
},
{
"src_folder" : "secure_fw/core/ipc",
"dest_folder" : "components/TARGET_PSA/TARGET_TFM/COMPONENT_SPE/secure_fw/core/ipc"
},
{
"src_folder" : "secure_fw/core/ipc/include",
"dest_folder" : "components/TARGET_PSA/TARGET_TFM/COMPONENT_SPE/secure_fw/core/ipc/include"
},
{
"src_folder" : "secure_fw/include",
"dest_folder" : "components/TARGET_PSA/TARGET_TFM/COMPONENT_SPE/secure_fw/include"
},
{
"src_folder" : "secure_fw/spm",
"dest_folder" : "components/TARGET_PSA/TARGET_TFM/COMPONENT_SPE/secure_fw/spm"
},
{
"src_folder" : "bl2/include",
"dest_folder" : "components/TARGET_PSA/TARGET_TFM/COMPONENT_SPE/bl2/include"
}
],
"commit_sha" : [
"11e5abc451acc7e7596e01b0f5605b4ad3e1965e",
"9541a37d7c878d057a40734ab4174cb46d81a922",
"e87efab83af6273a12b471ab574ddbf4359ff0de",
"bc275ff42a5c6275efffce81c91cce37e3749a3f",
"fb6b17dcdd59faa023e7940a6bb2f052956044c0",
"38bd4a279d22ff083d37c7f6a034d4a589e3527e",
"8c33f1e25ada6e3cbc15bd982825473ba9a57540",
"1134fd4dbb4245d19c010861a9f0bec7210c9701"
]
}

0
tools/psa/__init__.py Normal file
View File

View File

@ -0,0 +1,227 @@
#!/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 itertools
import os
import sys
from os.path import join as path_join
from jinja2 import Environment, FileSystemLoader, StrictUndefined
# Be sure that the tools directory is in the search path
ROOT = os.path.abspath(path_join(os.path.dirname(__file__), os.pardir, os.pardir))
sys.path.insert(0, ROOT)
from tools.psa.mbed_spm_tfm_common import Manifest, validate_partition_manifests, manifests_discovery
__version__ = '1.0'
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
TEMPLATES_DIR = path_join(SCRIPT_DIR, 'mbed_spm', 'templates')
MANIFEST_TEMPLATES = [filename for filename in
[os.path.join(dp, f) for dp, dn, fn in
os.walk(TEMPLATES_DIR) for f in fn if f.endswith('.tpl')]
if '_NAME_' in filename]
COMMON_TEMPLATES = [filename for filename in
[os.path.join(dp, f) for dp, dn, fn in
os.walk(TEMPLATES_DIR) for f in fn if f.endswith('.tpl')]
if '_NAME_' not in filename]
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', 'TARGET_MBED_SPM')
SPM_TESTS_ROOT = path_join(MBED_OS_ROOT, 'TESTS', 'psa')
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 = 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(
list(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 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 = sorted(
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] = [manifest_path for manifest_path in
test_manifest_files if
test_root in manifest_path]
if not tests_dict[test_root]:
continue
tests_dict[test_root] += system_manifest_files
generate_psa_setup(sorted(tests_dict[test_root]), test_root,
weak_setup=False)
if __name__ == '__main__':
generate_psa_code()

View File

@ -0,0 +1,102 @@
#!/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 json
import os
import sys
from os.path import join as path_join
from jinja2 import Environment, FileSystemLoader, StrictUndefined
# Be sure that the tools directory is in the search path
ROOT = os.path.abspath(path_join(os.path.dirname(__file__), os.pardir, os.pardir))
sys.path.insert(0, ROOT)
from tools.psa.mbed_spm_tfm_common import Manifest, validate_partition_manifests
__version__ = '1.0'
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
MBED_OS_ROOT = os.path.abspath(path_join(SCRIPT_DIR, os.pardir, os.pardir))
TEMPLATES_LIST_FILE = path_join(SCRIPT_DIR, 'tfm', 'tfm_generated_file_list.json')
SERVICES_DIR = os.path.join(MBED_OS_ROOT, "components", "TARGET_PSA", "services")
SERVICES_MANIFESTS = [
path_join(SERVICES_DIR, 'psa_prot_internal_storage', 'pits_psa.json'),
path_join(SERVICES_DIR, 'platform', 'platform_psa.json'),
path_join(SERVICES_DIR, 'crypto', 'crypto_partition_psa.json')
]
def generate_partition_source_files(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: path to the setup generated files
"""
# 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, psa_type='TFM')
manifests.append(manifest_obj)
for region in manifest_obj.mmio_regions:
region_list.append(region)
# Validate the correctness of the manifest collection.
validate_partition_manifests(manifests)
render_args = {
'partitions': manifests,
}
# Load templates for the code generation.
with open(TEMPLATES_LIST_FILE, 'r') as fh:
templates_data = json.load(fh)
env = Environment(
loader=FileSystemLoader(MBED_OS_ROOT),
lstrip_blocks=True,
trim_blocks=True,
undefined=StrictUndefined
)
if extra_filters:
env.filters.update(extra_filters)
# Generate code for each template
for tpl in templates_data:
template = env.get_template(tpl['template'])
data = template.render(**render_args)
output_path = os.path.join(MBED_OS_ROOT, tpl['output'])
output_folder = os.path.dirname(output_path)
if not os.path.exists(output_folder):
os.makedirs(output_folder)
with open(output_path, 'wt') as fh:
fh.write(data)
def generate_tfm_code():
generate_partition_source_files(SERVICES_MANIFESTS)
if __name__ == '__main__':
generate_tfm_code()

View File

@ -15,31 +15,15 @@
# 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
import json
from jsonschema import validate
import fnmatch
from six import integer_types, string_types
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 = [filename for filename in
[os.path.join(dp, f) for dp, dn, fn in
os.walk(TEMPLATES_DIR) for f in fn if f.endswith('.tpl')]
if '_NAME_' in filename]
COMMON_TEMPLATES = [filename for filename in
[os.path.join(dp, f) for dp, dn, fn in
os.walk(TEMPLATES_DIR) for f in fn if f.endswith('.tpl')]
if '_NAME_' not in filename]
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', 'TARGET_MBED_SPM')
SPM_TESTS_ROOT = path_join(MBED_OS_ROOT, 'TESTS', 'psa')
def assert_int(num):
@ -195,6 +179,7 @@ class Manifest(object):
def __init__(
self,
manifest_file,
psa_type,
name,
partition_id,
partition_type,
@ -212,6 +197,7 @@ class Manifest(object):
Manifest C'tor (Aligned with json schema)
:param manifest_file: Path to json manifest
:param psa_type: PSA implementation type (TFM/MBED_SPM)
:param name: Partition unique name
:param partition_id: Partition identifier
:param partition_type: Whether the partition is unprivileged or part
@ -250,12 +236,17 @@ class Manifest(object):
assert isinstance(entry_point, string_types)
assert partition_type in self.PARTITION_TYPES
assert partition_id > 0
assert psa_type in ['TFM', 'MBED_SPM']
self.file = manifest_file
self.name = name
self.psa_type = psa_type
self.id = partition_id
self.type = partition_type
self.priority = self.PRIORITY[priority]
if psa_type == 'TFM':
self.priority = priority
else:
self.priority = self.PRIORITY[priority]
self.heap_size = heap_size
self.stack_size = stack_size
self.entry_point = entry_point
@ -312,12 +303,13 @@ class Manifest(object):
)
@classmethod
def from_json(cls, manifest_file, skip_src=False):
def from_json(cls, manifest_file, skip_src=False, psa_type='MBED_SPM'):
"""
Load a partition manifest file
:param manifest_file: Manifest file path
:param skip_src: Ignore the `source_files` entry
:param psa_type: PSA implementation type (TFM/MBED_SPM)
:return: Manifest object
"""
@ -356,6 +348,7 @@ class Manifest(object):
return Manifest(
manifest_file=manifest_file,
psa_type=psa_type,
name=manifest['name'],
partition_id=assert_int(manifest['id']),
partition_type=manifest['type'],
@ -398,6 +391,7 @@ class Manifest(object):
Translates a list of partition templates to file names
:param templates: List of partition templates
:param templates_base: Base directory of the templates
:param output_dir: Output directory (Default is autogen folder property)
:return: Dictionary of template to output file translation
"""
@ -600,150 +594,6 @@ def validate_partition_manifests(manifests):
)
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 = 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(
list(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()
@ -754,42 +604,3 @@ def manifests_discovery(root_dir):
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 = sorted(
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] = [manifest_path for manifest_path in
test_manifest_files if
test_root in manifest_path]
if not tests_dict[test_root]:
continue
tests_dict[test_root] += system_manifest_files
generate_psa_setup(sorted(tests_dict[test_root]), test_root,
weak_setup=False)
if __name__ == '__main__':
generate_psa_code()

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