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