1 /*
2 * Copyright (c) 2019-2023, Arm Limited. All rights reserved.
3 * Copyright (c) 2022-2023 Cypress Semiconductor Corporation (an Infineon
4 * company) or an affiliate of Cypress Semiconductor Corporation. All rights
5 * reserved.
6 *
7 * SPDX-License-Identifier: BSD-3-Clause
8 *
9 */
10
11 #include "config_impl.h"
12 #include "critical_section.h"
13 #include "ffm/backend.h"
14 #include "ffm/psa_api.h"
15 #include "tfm_hal_isolation.h"
16 #include "tfm_psa_call_pack.h"
17 #include "utilities.h"
18
19 extern struct service_t *stateless_services_ref_tbl[];
20
tfm_spm_client_psa_call(psa_handle_t handle,uint32_t ctrl_param,const psa_invec * inptr,psa_outvec * outptr)21 psa_status_t tfm_spm_client_psa_call(psa_handle_t handle,
22 uint32_t ctrl_param,
23 const psa_invec *inptr,
24 psa_outvec *outptr)
25 {
26 psa_invec invecs[PSA_MAX_IOVEC];
27 psa_outvec outvecs[PSA_MAX_IOVEC];
28 struct connection_t *p_connection;
29 struct service_t *service;
30 int i, j;
31 int32_t client_id;
32 uint32_t sid, version, index;
33 struct critical_section_t cs_assert = CRITICAL_SECTION_STATIC_INIT;
34 bool ns_caller = tfm_spm_is_ns_caller();
35 struct partition_t *curr_partition = GET_CURRENT_COMPONENT();
36 int32_t type = (int32_t)(int16_t)((ctrl_param & TYPE_MASK) >> TYPE_OFFSET);
37 size_t in_num = (size_t)((ctrl_param & IN_LEN_MASK) >> IN_LEN_OFFSET);
38 size_t out_num = (size_t)((ctrl_param & OUT_LEN_MASK) >> OUT_LEN_OFFSET);
39 fih_int fih_rc = FIH_FAILURE;
40
41 /* The request type must be zero or positive. */
42 if (type < 0) {
43 return PSA_ERROR_PROGRAMMER_ERROR;
44 }
45
46 /* It is a PROGRAMMER ERROR if in_len + out_len > PSA_MAX_IOVEC. */
47 if ((in_num > SIZE_MAX - out_num) ||
48 (in_num + out_num > PSA_MAX_IOVEC)) {
49 return PSA_ERROR_PROGRAMMER_ERROR;
50 }
51
52 /* It is a PROGRAMMER ERROR if the handle is a null handle. */
53 if (handle == PSA_NULL_HANDLE) {
54 return PSA_ERROR_PROGRAMMER_ERROR;
55 }
56
57 client_id = tfm_spm_get_client_id(ns_caller);
58
59 /* Allocate space from handle pool for static handle. */
60 if (IS_STATIC_HANDLE(handle)) {
61 index = GET_INDEX_FROM_STATIC_HANDLE(handle);
62
63 service = stateless_services_ref_tbl[index];
64 if (!service) {
65 return PSA_ERROR_PROGRAMMER_ERROR;
66 }
67
68 sid = service->p_ldinf->sid;
69
70 /*
71 * It is a PROGRAMMER ERROR if the caller is not authorized to access
72 * the RoT Service.
73 */
74 if (tfm_spm_check_authorization(sid, service, ns_caller)
75 != PSA_SUCCESS) {
76 return PSA_ERROR_CONNECTION_REFUSED;
77 }
78
79 version = GET_VERSION_FROM_STATIC_HANDLE(handle);
80
81 if (tfm_spm_check_client_version(service, version) != PSA_SUCCESS) {
82 return PSA_ERROR_PROGRAMMER_ERROR;
83 }
84
85 CRITICAL_SECTION_ENTER(cs_assert);
86 p_connection = spm_allocate_connection();
87 CRITICAL_SECTION_LEAVE(cs_assert);
88
89 if (!p_connection) {
90 return PSA_ERROR_CONNECTION_BUSY;
91 }
92
93 #if CONFIG_TFM_CONNECTION_BASED_SERVICE_API == 1
94 p_connection->rhandle = NULL;
95 #endif
96 handle = connection_to_handle(p_connection);
97 } else {
98 #if CONFIG_TFM_CONNECTION_BASED_SERVICE_API == 1
99 /* It is a PROGRAMMER ERROR if an invalid handle was passed. */
100 p_connection = spm_get_client_connection(handle, client_id);
101 if (!p_connection) {
102 return PSA_ERROR_PROGRAMMER_ERROR;
103 }
104
105 /*
106 * It is a PROGRAMMER ERROR if the connection is currently
107 * handling a request.
108 */
109 if (p_connection->status != TFM_HANDLE_STATUS_IDLE) {
110 return PSA_ERROR_PROGRAMMER_ERROR;
111 }
112
113 service = p_connection->service;
114
115 if (!service) {
116 /* FixMe: Need to implement a mechanism to resolve this failure. */
117 return PSA_ERROR_PROGRAMMER_ERROR;
118 }
119 #else
120 return PSA_ERROR_PROGRAMMER_ERROR;
121 #endif
122 }
123
124 /*
125 * Read client invecs from the wrap input vector. It is a PROGRAMMER ERROR
126 * if the memory reference for the wrap input vector is invalid or not
127 * readable.
128 */
129 FIH_CALL(tfm_hal_memory_check, fih_rc,
130 curr_partition->boundary, (uintptr_t)inptr,
131 in_num * sizeof(psa_invec), TFM_HAL_ACCESS_READABLE);
132 if (fih_not_eq(fih_rc, fih_int_encode(PSA_SUCCESS))) {
133 return PSA_ERROR_PROGRAMMER_ERROR;
134 }
135
136 /*
137 * Read client outvecs from the wrap output vector and will update the
138 * actual length later. It is a PROGRAMMER ERROR if the memory reference for
139 * the wrap output vector is invalid or not read-write.
140 */
141 FIH_CALL(tfm_hal_memory_check, fih_rc,
142 curr_partition->boundary, (uintptr_t)outptr,
143 out_num * sizeof(psa_outvec), TFM_HAL_ACCESS_READWRITE);
144 if (fih_not_eq(fih_rc, fih_int_encode(PSA_SUCCESS))) {
145 return PSA_ERROR_PROGRAMMER_ERROR;
146 }
147
148 spm_memset(invecs, 0, sizeof(invecs));
149 spm_memset(outvecs, 0, sizeof(outvecs));
150
151 /* Copy the address out to avoid TOCTOU attacks. */
152 spm_memcpy(invecs, inptr, in_num * sizeof(psa_invec));
153 spm_memcpy(outvecs, outptr, out_num * sizeof(psa_outvec));
154
155 /*
156 * For client input vector, it is a PROGRAMMER ERROR if the provided payload
157 * memory reference was invalid or not readable.
158 */
159 for (i = 0; i < in_num; i++) {
160 FIH_CALL(tfm_hal_memory_check, fih_rc,
161 curr_partition->boundary, (uintptr_t)invecs[i].base,
162 invecs[i].len, TFM_HAL_ACCESS_READABLE);
163 if (fih_not_eq(fih_rc, fih_int_encode(PSA_SUCCESS))) {
164 return PSA_ERROR_PROGRAMMER_ERROR;
165 }
166 }
167
168 /*
169 * Clients must never overlap input parameters because of the risk of a
170 * double-fetch inconsistency.
171 * Overflow is checked in tfm_hal_memory_check functions.
172 */
173 for (i = 0; i + 1 < in_num; i++) {
174 for (j = i+1; j < in_num; j++) {
175 if (!((char *) invecs[j].base + invecs[j].len <=
176 (char *) invecs[i].base ||
177 (char *) invecs[j].base >=
178 (char *) invecs[i].base + invecs[i].len)) {
179 return PSA_ERROR_PROGRAMMER_ERROR;
180 }
181 }
182 }
183
184 /*
185 * For client output vector, it is a PROGRAMMER ERROR if the provided
186 * payload memory reference was invalid or not read-write.
187 */
188 for (i = 0; i < out_num; i++) {
189 FIH_CALL(tfm_hal_memory_check, fih_rc,
190 curr_partition->boundary, (uintptr_t)outvecs[i].base,
191 outvecs[i].len, TFM_HAL_ACCESS_READWRITE);
192 if (fih_not_eq(fih_rc, fih_int_encode(PSA_SUCCESS))) {
193 return PSA_ERROR_PROGRAMMER_ERROR;
194 }
195 }
196
197 spm_fill_message(p_connection, service, handle, type, client_id);
198 for (i = 0; i < in_num; i++) {
199 p_connection->msg.in_size[i] = invecs[i].len;
200 p_connection->invec[i].base = invecs[i].base;
201 }
202
203 for (i = 0; i < out_num; i++) {
204 p_connection->msg.out_size[i] = outvecs[i].len;
205 p_connection->outvec[i].base = outvecs[i].base;
206 /* Out len is used to record the written number, set 0 here again */
207 p_connection->outvec[i].len = 0;
208 }
209 p_connection->caller_outvec = outptr;
210
211 return backend_messaging(service, p_connection);
212 }
213