The Secure Request Framework (SRF) exists to provide a reusable infastructure for implementing multiple secure operations via a single non-secure-callable entry point.
This framework is designed to be generic with no dependency on or knowledge of any particular hardware operation. It can be used directly by user code.
Through the SRF, the secure world exposes one or more operations, which are roughly equivalent to a function. These operations can be combined into a module - a unit of integration between different entities which wish to expose their operations. For example, the Peripheral Driver Library (PDL) would be one module, while each driver would be a submodule.
The non-secure world invokes an operation by submitting a secure request via the mtb_srf_request_submit API. This request is then passed to the secure world, where its memory addresses are validated, as are the permissions required to perform the operation. A default validation strategy is performed through mtb_srf_memory_validate, and further operation specific validation should be done inside the operation itself. The default mtb_srf_memory_validate API checks that all memory passed in is accessible to the NS side, ensured the minimum number of inVec and outVec are present, and that a legal number of ioVec (input-output vectors) have been provided (discussed further below). This API is defined weakly to enable overriding with a custom implementation.
The mtb_srf_request_submit API accepts inVec and outVec arrays. The max total number of vectors cannot exceed MTB_SRF_MAX_IOVEC. That is, should the max ioVec be 4, legal combinations could be one inVec and three outVec, two of each, or three inVec and one outVec. Unused vectors should be set to NULL.
- Note
- It is possible to completely disable the SRF by adding a define for CY_SRF_DISABLE in the cases where the SRF library is included as a default dependency but not used.
Prerequisites
This framework requires the user manage a mtb_srf_config.h file that contains macros for the various modules that use the SRF. The file comes with the default macros for user and PDL module IDs. Should a library be added to the project that uses SRF, that library should generate a relevant error message requesting that its module ID is added to the mtb_srf_config.h file. The exact values of the module IDs do not matter, as long as they are unique and unsigned integers. The number of SRF modules should be defined as MTB_SRF_MODULE_COUNT. The SRF uses this macro internally to determine how many module IDs exist in a given project.
Code Snippets
Operations are grouped into arrays called submodules, and submodules are grouped into modules. This snippet demonstrates declaring operations, packaging them into submodules and modules, and registering them. Registering a module makes it available for the non-secure application to invoke through the SRF.
cy_stc_smif_context_t SMIFContext;
cy_stc_smif_mem_config_t memConfig;
#define MTB_SRF_MODULE_USER_DEMO (0U)
typedef enum
{
APP_SRF_USER_SUBMODULE_SMIF,
APP_SRF_USER_SUBMODULE_SYSCLK,
APP_SRF_USER_SUBMODULE_MAX
} CY_PDL_SRF_SUBMODULE;
#define APP_SRF_SMIF_OP_WRITE (0U)
#define APP_SRF_SMIF_OP_READ (1U)
uint8_t inputs_ptr_ns_cnt,
uint8_t outputs_ptr_cnt_ns)
{
cy_rslt_t result = (cy_rslt_t)Cy_SMIF_MemWrite(inputs_ns->
request.
base,
(const uint8_t*)inputs_ptr_ns[0].base,
inputs_ptr_ns[0].len, &SMIFContext);
CY_UNUSED_PARAMETER(outputs_ns);
CY_UNUSED_PARAMETER(outputs_ptr_ns);
CY_UNUSED_PARAMETER(inputs_ptr_ns_cnt);
CY_UNUSED_PARAMETER(outputs_ptr_cnt_ns);
return result;
}
{
{
.base = (void*)SMIF0_BASE,
.sub_block = 0,
.write_allowed = true,
},
};
{
{
.module_id = MTB_SRF_MODULE_USER_DEMO,
.submodule_id = APP_SRF_USER_SUBMODULE_SMIF,
.op_id = APP_SRF_SMIF_OP_WRITE,
.write_required = true,
.impl = app_srf_submod_smif_write_fn,
.input_values_len = 1U * sizeof(uint32_t),
.output_values_len = 0UL,
.input_len ={ 0UL, 0UL, 0UL },
.needs_copy ={ false, false, false },
.output_len ={ 0UL, 0UL, 0UL },
.allowed_rsc = &app_srf_smif_permission[0],
.num_allowed = sizeof(app_srf_smif_permission) / sizeof(app_srf_smif_permission[0]),
},
};
{
app_srf_smif_operations,
app_srf_sysclk_operations,
};
size_t app_srf_num_operations[APP_SRF_USER_SUBMODULE_MAX] =
{
sizeof(app_srf_smif_operations) / sizeof(app_srf_smif_operations[0]),
sizeof(app_srf_sysclk_operations) / sizeof(app_srf_sysclk_operations[0]),
};
{
.module_id = MTB_SRF_MODULE_USER_DEMO,
.op_by_submod = app_srf_operations,
.num_op_by_submod = app_srf_num_operations,
.num_submod = sizeof(app_srf_num_operations) / sizeof(app_srf_num_operations[0]),
};
cy_rslt_t snippet_mtb_srf_register_module_api(void)
{
cy_rslt_t result = CY_RSLT_SUCCESS;
if (result != CY_RSLT_SUCCESS)
{
}
(void)result;
return result;
}
#endif
The non-secure application is responsible for populating ioVecs with arguments to identity the target operation, identify the resource the operation should be invoked on (if applicable), and package arguments for the SRF to pass to the operation. It must also unpack the results afterwards, if any are expected.
#define MTB_SRF_MODULE_PDL_DEMO (0U)
typedef enum
{
CY_PDL_SRF_SUBMODULE_SYSCLK,
CY_PDL_SRF_SUBMODULE_SYSPM,
CY_PDL_SRF_SUBMODULE_MAX = CY_PDL_SRF_SUBMODULE_SYSPM
} CY_PDL_SRF_SUBMODULE;
#define CY_SYSCLK_SRF_OP_HF_GET_FREQUENCY (0U)
#define CY_SYSCLK_SRF_OP_HF_GET_ENABLED (1U)
#define CY_SYSPM_SRF_OP_ENTER_SLEEP (0U)
#define CY_SYSPM_SRF_OP_ENTER_DEEPSLEEP (1U)
cy_rslt_t snippet_mtb_srf_submit_request_api(void)
{
cy_rslt_t result = CY_RSLT_SUCCESS;
if (result != CY_RSLT_SUCCESS)
{
return result;
}
output->
len = 1UL *
sizeof(uint32_t);
uint8_t inVec_cnt = 1;
uint8_t outVec_cnt = 1;
return result;
}
The default mtb_srf_request_submit implementations can be disabled via defining MTB_SRF_CUSTOM_REQUEST_SUBMIT. This allows adding custom logic. This can be used to add additional validation logic, argument modifications, or even selecting between multiple context structures in an advanced use case.
#if defined(COMPONENT_SECURE_DEVICE)
{
cy_rslt_t result = CY_RSLT_SUCCESS;
outVec_cnt_ns);
return result;
}
#endif
#if defined(COMPONENT_NON_SECURE_DEVICE) && (CY_SYSTEM_CPU_M55)
{
#if defined(MTB_SRF_SUBMIT_USE_IPC) && defined(COMPONENT_MW_MTB_IPC)
outVec_ns, outVec_cnt_ns);
#else
CY_UNUSED_PARAMETER(inVec_ns);
CY_UNUSED_PARAMETER(inVec_cnt_ns);
CY_UNUSED_PARAMETER(outVec_ns);
CY_UNUSED_PARAMETER(outVec_cnt_ns);
CY_ASSERT(false);
#endif
}
#endif
|
|
#define | MTB_SRF_ERR_BAD_PARAM CY_RSLT_CREATE(CY_RSLT_TYPE_ERROR, CY_RSLT_MODULE_MIDDLEWARE_SRF, 0) |
| | An invalid parameter value is passed in.
|
|
#define | MTB_SRF_ERR_UNKNOWN_OPERATION CY_RSLT_CREATE(CY_RSLT_TYPE_ERROR, CY_RSLT_MODULE_MIDDLEWARE_SRF, 1) |
| | Unknown operation requested.
|
|
#define | MTB_SRF_ERR_UNKNOWN_MODULE CY_RSLT_CREATE(CY_RSLT_TYPE_ERROR, CY_RSLT_MODULE_MIDDLEWARE_SRF, 2) |
| | Unknown module requested.
|
|
#define | MTB_SRF_ERR_SECURITY_POLICY_VIOLATION CY_RSLT_CREATE(CY_RSLT_TYPE_ERROR, CY_RSLT_MODULE_MIDDLEWARE_SRF, 3) |
| | Request violates resource security permissions.
|
|
#define | MTB_SRF_ERR_MODULE_ID_INVALID CY_RSLT_CREATE(CY_RSLT_TYPE_ERROR, CY_RSLT_MODULE_MIDDLEWARE_SRF, 4) |
| | Module index provided at registration is invalid.
|
|
#define | MTB_SRF_ERR_ALLOCATE_FREE CY_RSLT_CREATE(CY_RSLT_TYPE_ERROR, CY_RSLT_MODULE_MIDDLEWARE_SRF, 5) |
| | Failed to allocate/free secure request.
|
|
#define | MTB_SRF_ERR_RINGBUF_EMPTY CY_RSLT_CREATE(CY_RSLT_TYPE_ERROR, CY_RSLT_MODULE_MIDDLEWARE_SRF, 6) |
| | Ringbuffer is empty.
|
|
#define | MTB_SRF_ERR_RINGBUF_FULL CY_RSLT_CREATE(CY_RSLT_TYPE_ERROR, CY_RSLT_MODULE_MIDDLEWARE_SRF, 7) |
| | Ringbuffer is full.
|
|
#define | MTB_SRF_ERR_ARGBUFF_FULL CY_RSLT_CREATE(CY_RSLT_TYPE_ERROR, CY_RSLT_MODULE_MIDDLEWARE_SRF, 8) |
| | No space left in secure argument buffer.
|
|
#define | _MTB_SRF_DATA_ALIGN |
| | This define can be used for aligning address of the data structures with enabled Data Cache.
|
|
#define | MTB_SRF_NEVER_TIMEOUT (0xFFFFFFFFU) |
| | Indicates that an operation should wait indefinitely.
|
|
| cy_rslt_t | mtb_srf_init (mtb_srf_context_s_t *context_s) |
| | Initialize the SRF.
|
| cy_rslt_t | mtb_srf_module_register (mtb_srf_context_s_t *context_s, mtb_srf_module_s_t *module_s) |
| | Register a module for use.
|
| cy_rslt_t | mtb_srf_memory_validate (volatile mtb_srf_invec_ns_t *inVec_ns, uint8_t inVec_cnt_ns, volatile mtb_srf_outvec_ns_t *outVec_ns, uint8_t outVec_cnt_ns, mtb_srf_invec_ns_t *inVec, mtb_srf_outvec_ns_t *outVec) |
| | Validate invec/outvec memory address on non-secure's permission.
|
| cy_rslt_t | mtb_srf_request_execute (mtb_srf_context_s_t *context_s, mtb_srf_invec_ns_t *inVec_ns, uint8_t inVec_cnt_ns, mtb_srf_outvec_ns_t *outVec_ns, uint8_t outVec_cnt_ns) |
| | Execute a request.
|
| bool | mtb_srf_is_memory_ns_accessible (uint32_t addr, size_t size) |
| | Check if an address is non secure accessible.
|
| cy_rslt_t | mtb_srf_copy_input_value (void *input, size_t size, const mtb_srf_input_ns_t *inputs_ns) |
| | Copy the operation defined structure value to input_values and validate the structure size If size, which is defined in secure-side, is not eqaul to size of the input_values array, it returns MTB_SRF_ERR_BAD_PARAM.
|
| cy_rslt_t | mtb_srf_copy_output_value (mtb_srf_output_ns_t *outputs_ns, void *output, size_t size) |
| | Copy the operation defined structure value to input_values and validate the structure size If size, which is defined in secure-side, is not eqaul to size of the output_values array, it returns MTB_SRF_ERR_BAD_PARAM.
|
| cy_rslt_t | mtb_srf_request_submit (mtb_srf_invec_ns_t *inVec_ns, uint8_t inVec_cnt_ns, mtb_srf_outvec_ns_t *outVec_ns, uint8_t outVec_cnt_ns) |
| | Submit a request from the non-secure side to the secure.
|
◆ mtb_srf_permission_s_t
| struct mtb_srf_permission_s_t |
| Data Fields |
|
void * |
base |
Register base address for the hardware resource. If not applicable, set to NULL. |
|
uint16_t |
sub_block |
Sub-block index, if applicable. E.g. TCPWM counter index. If not applicable, set to 0. |
|
bool |
write_allowed |
If true, operations that modify the hardware state are allowed. |
◆ mtb_srf_op_s_t
| Data Fields |
|
uint8_t |
module_id |
Module ID for this operation. Must be unique within this operation. |
|
uint8_t |
submodule_id |
Submodule ID for this operation. |
|
uint8_t |
op_id |
ID for this operation. |
|
bool |
write_required |
If this operation requires modifying the hardware state. |
|
mtb_srf_op_fn_t |
impl |
Function pointer to the operation. May perform custom validation, if any, before executing request. |
|
size_t |
input_values_len |
Required size of input_values. |
|
size_t |
output_values_len |
Required size of output_values. |
|
size_t |
input_len[MTB_SRF_MAX_IOVEC - 1] |
Minimum length of inVec pointer arguments needed (From First element) |
|
bool |
needs_copy[MTB_SRF_MAX_IOVEC - 1] |
Whether an input should be copied into secure memory before processing. This should be set to true if validation decisions are made based on data in this argument. inVec[0] is always copied. |
|
size_t |
output_len[MTB_SRF_MAX_IOVEC - 1] |
Minimum length of outVec pointer arguments needed (From First element) |
|
mtb_srf_permission_s_t * |
allowed_rsc |
List of hardware resources on which non-secure is permitted to perform this operation. Permission validation will be bypassed if this is null. |
|
size_t |
num_allowed |
Length of the allowed_rsc array. |
◆ mtb_srf_module_s_t
| struct mtb_srf_module_s_t |
| Data Fields |
|
uint8_t |
module_id |
ID for this module. Must be globally unique. |
|
mtb_srf_op_s_t ** |
op_by_submod |
Array of operations in this module/submodule. |
|
size_t * |
num_op_by_submod |
Length of each entry of the op_by_submod array. |
|
size_t |
num_submod |
Length of the num_op_by_submod array. |
◆ mtb_srf_context_s_t
| struct mtb_srf_context_s_t |
◆ mtb_srf_protection_range_s_t
| struct mtb_srf_protection_range_s_t |
| Data Fields |
|
void * |
start |
Start of the memory range. |
|
size_t |
length |
Length of the memory range. |
|
bool |
is_secure |
If the range is Secure or Non-Secure accessible. |
◆ mtb_srf_memory_protection_s_t
| struct mtb_srf_memory_protection_s_t |
◆ mtb_srf_op_fn_t
Function pointer to the operation.
May perform custom validation, if any, before executing request
- Parameters
-
| [in] | inputs_ns | Struct describing the operation and containing non-pointer input arguments |
| [in,out] | outputs_ns | Struct containing the non-pointer output arguments |
| [in,out] | inputs_ptr_ns | Array of InVec arguments. InVec[0] is passed as input_ns, so this array starts at inVec[1]. |
| [in] | inputs_ptr_cnt_ns | Number of entries in inputs_ptr_ns |
| [in,out] | outputs_ptr_ns | Array of outVec arguments. OutVec[0] is passed as output_ns, so this array starts at outVec[1]. |
| [in] | outputs_ptr_cnt_ns | Number of entries in outputs_ptr_ns |
- Returns
- Result of validation check
◆ mtb_srf_init()
Initialize the SRF.
- Parameters
-
| [in,out] | context_s | The context object for this asset |
- Returns
- the status of initialization
◆ mtb_srf_module_register()
Register a module for use.
- Parameters
-
| [in,out] | context_s | The context object for this asset |
| [in] | module_s | The module to be registered |
- Returns
- the status of registration
◆ mtb_srf_memory_validate()
Validate invec/outvec memory address on non-secure's permission.
This is a weak function with a default implementation so that TFM (where used) can override these checks via its own infrastructure
- Parameters
-
| [in] | inVec_ns | Vector for all operation and SRF input |
| [in] | inVec_cnt_ns | Number of inVecs passed in to validate |
| [in] | outVec_ns | Vector for all operation and SRF output |
| [in] | outVec_cnt_ns | Number of outVecs passed in to validate |
| [out] | inVec | Vector to store local copied inVec_ns |
| [out] | outVec | Vector to store local copied inVec_ns |
- Returns
- the status of validation
◆ mtb_srf_request_execute()
Execute a request.
- Parameters
-
| [in] | context_s | The context object for this asset |
| [in,out] | inVec_ns | Vector for all operation and SRF input |
| [in] | inVec_cnt_ns | Number of input vectors in inVec_ns array |
| [in,out] | outVec_ns | Vector for all operation and SRF output |
| [in] | outVec_cnt_ns | Number of output vectors in outVec_ns array |
- Returns
- the status of execution
◆ mtb_srf_is_memory_ns_accessible()
| bool mtb_srf_is_memory_ns_accessible |
( |
uint32_t | addr, |
|
|
size_t | size ) |
Check if an address is non secure accessible.
- Parameters
-
| [in] | addr | The start address to check address for |
| [in] | size | The size of the area to check |
- Returns
- true if it is ns acccsible, false otherwise
◆ mtb_srf_copy_input_value()
| cy_rslt_t mtb_srf_copy_input_value |
( |
void * | input, |
|
|
size_t | size, |
|
|
const mtb_srf_input_ns_t * | inputs_ns ) |
Copy the operation defined structure value to input_values and validate the structure size If size, which is defined in secure-side, is not eqaul to size of the input_values array, it returns MTB_SRF_ERR_BAD_PARAM.
- Parameters
-
| [out] | input | Point to operation defined structure |
| [in] | size | The size of input to be copied and checked |
| [in] | inputs_ns | Struct describing the operation and containing non-pointer input arguments |
- Returns
- the status of execution
◆ mtb_srf_copy_output_value()
| cy_rslt_t mtb_srf_copy_output_value |
( |
mtb_srf_output_ns_t * | outputs_ns, |
|
|
void * | output, |
|
|
size_t | size ) |
Copy the operation defined structure value to input_values and validate the structure size If size, which is defined in secure-side, is not eqaul to size of the output_values array, it returns MTB_SRF_ERR_BAD_PARAM.
- Parameters
-
| [out] | outputs_ns | Struct containing the non-pointer output arguments |
| [in] | output | Point to operation defined structure |
| [in] | size | The size of output to be copied and checked |
- Returns
- the status of execution
◆ mtb_srf_request_submit()
Submit a request from the non-secure side to the secure.
This is a function with a default implementation within the BSP. The user may define MTB_SRF_CUSTOM_REQUEST_SUBMIT and override this with their own implementation should they need further action taken. However custom implementations must ultimately call mtb_srf_request_execute.
When the IPC library is present, this function may be called from other cores without TrustZone. The request will be passed to the non-secure side of the TrustZone enabled core, which will then submit it to the secure side.
On Cores which support DCache, it is the user's responsibility that all data pointed to by the inVec[1-3] and outVec[1-3] base pointers is DCache aligned. The input and output structures (inVec[0] and outVec[0], respectively) as well as the ioVec structures themselves are already DCache aligned when allocated from a pool.
- Parameters
-
| [in,out] | inVec_ns | Vector for all operation and SRF input |
| [in] | inVec_cnt_ns | Number of input vectors in inVec_ns array |
| [in,out] | outVec_ns | Vector for all operation and SRF output |
| [in] | outVec_cnt_ns | Number of output vectors in outVec_ns array |
- Returns
- the status of submission