1 /*
2  * Copyright (c) 2019-2024, Arm Limited. All rights reserved.
3  * Copyright (c) 2022-2024 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 
spm_associate_call_params(struct connection_t * p_connection,uint32_t ctrl_param,const psa_invec * inptr,psa_outvec * outptr)19 psa_status_t spm_associate_call_params(struct connection_t *p_connection,
20                                        uint32_t            ctrl_param,
21                                        const psa_invec     *inptr,
22                                        psa_outvec          *outptr)
23 {
24     psa_invec  ivecs_local[PSA_MAX_IOVEC];
25     psa_outvec ovecs_local[PSA_MAX_IOVEC];
26     int        i, j;
27     fih_int    fih_rc      = FIH_FAILURE;
28     uint32_t   ns_access   = 0;
29     size_t     ivec_num    = PARAM_UNPACK_IN_LEN(ctrl_param);
30     size_t     ovec_num    = PARAM_UNPACK_OUT_LEN(ctrl_param);
31     struct partition_t *curr_partition = GET_CURRENT_COMPONENT();
32     int32_t type = PARAM_UNPACK_TYPE(ctrl_param);
33 
34     /* The request type must be zero or positive. */
35     if (type < 0) {
36         return PSA_ERROR_PROGRAMMER_ERROR;
37     }
38 
39     p_connection->msg.type = type;
40 
41     if (!PARAM_HAS_IOVEC(ctrl_param)) {
42         return PSA_SUCCESS;
43     }
44 
45     /* Process IO vectors */
46     /* in_len + out_len SHOULD <= PSA_MAX_IOVEC */
47     if ((ivec_num > SIZE_MAX - ovec_num) ||
48         (ivec_num + ovec_num > PSA_MAX_IOVEC)) {
49         return PSA_ERROR_PROGRAMMER_ERROR;
50     }
51 
52     if (PARAM_IS_NS_VEC(ctrl_param)) {
53         ns_access = TFM_HAL_ACCESS_NS;
54     }
55 
56     /*
57      * Read client invecs from the wrap input vector. It is a PROGRAMMER ERROR
58      * if the memory reference for the wrap input vector is invalid or not
59      * readable.
60      */
61     FIH_CALL(tfm_hal_memory_check, fih_rc,
62              curr_partition->boundary, (uintptr_t)inptr,
63              ivec_num * sizeof(psa_invec), TFM_HAL_ACCESS_READABLE | ns_access);
64     if (fih_not_eq(fih_rc, fih_int_encode(PSA_SUCCESS))) {
65         return PSA_ERROR_PROGRAMMER_ERROR;
66     }
67 
68     spm_memset(ivecs_local, 0, sizeof(ivecs_local));
69     spm_memcpy(ivecs_local, inptr, ivec_num * sizeof(psa_invec));
70 
71     /*
72      * Read client outvecs from the wrap output vector and will update the
73      * actual length later. It is a PROGRAMMER ERROR if the memory reference for
74      * the wrap output vector is invalid or not read-write.
75      */
76     FIH_CALL(tfm_hal_memory_check, fih_rc,
77              curr_partition->boundary, (uintptr_t)outptr,
78              ovec_num * sizeof(psa_outvec), TFM_HAL_ACCESS_READWRITE | ns_access);
79     if (fih_not_eq(fih_rc, fih_int_encode(PSA_SUCCESS))) {
80         return PSA_ERROR_PROGRAMMER_ERROR;
81     }
82 
83     spm_memset(ovecs_local, 0, sizeof(ovecs_local));
84     spm_memcpy(ovecs_local, outptr, ovec_num * sizeof(psa_outvec));
85 
86     /*
87      * Clients must never overlap input parameters because of the risk of a
88      * double-fetch inconsistency.
89      * Overflow is checked in tfm_hal_memory_check functions.
90      */
91     for (i = 0; i + 1 < ivec_num; i++) {
92         for (j = i + 1; j < ivec_num; j++) {
93             if (!((char *) ivecs_local[j].base + ivecs_local[j].len <=
94                   (char *) ivecs_local[i].base ||
95                   (char *) ivecs_local[j].base >=
96                   (char *) ivecs_local[i].base + ivecs_local[i].len)) {
97                 return PSA_ERROR_PROGRAMMER_ERROR;
98             }
99         }
100     }
101 
102     if (PARAM_IS_NS_INVEC(ctrl_param)) {
103         /* Vector descriptor is non-secure then vectors are non-secure. */
104         ns_access = TFM_HAL_ACCESS_NS;
105     }
106 
107     /* Make sure in_size and out_size arrays in the msg structure are cleared first */
108     spm_memset(p_connection->msg.in_size, 0, sizeof(p_connection->msg.in_size));
109     spm_memset(p_connection->msg.out_size, 0, sizeof(p_connection->msg.out_size));
110 
111     /*
112      * For client input vector, it is a PROGRAMMER ERROR if the provided payload
113      * memory reference was invalid or not readable.
114      */
115     for (i = 0; i < ivec_num; i++) {
116         FIH_CALL(tfm_hal_memory_check, fih_rc,
117                  curr_partition->boundary, (uintptr_t)ivecs_local[i].base,
118                  ivecs_local[i].len, TFM_HAL_ACCESS_READABLE | ns_access);
119         if (fih_not_eq(fih_rc, fih_int_encode(PSA_SUCCESS))) {
120             return PSA_ERROR_PROGRAMMER_ERROR;
121         }
122 
123         p_connection->msg.in_size[i]    = ivecs_local[i].len;
124         p_connection->invec_base[i]     = ivecs_local[i].base;
125         p_connection->invec_accessed[i] = 0;
126     }
127 
128     if (ns_access == TFM_HAL_ACCESS_NS &&
129         !PARAM_IS_NS_VEC(ctrl_param)   &&
130         !PARAM_IS_NS_OUTVEC(ctrl_param)) {
131         ns_access = 0;
132     }
133 
134     /*
135      * For client output vector, it is a PROGRAMMER ERROR if the provided
136      * payload memory reference was invalid or not read-write.
137      */
138     for (i = 0; i < ovec_num; i++) {
139         FIH_CALL(tfm_hal_memory_check, fih_rc,
140                  curr_partition->boundary, (uintptr_t)ovecs_local[i].base,
141                  ovecs_local[i].len, TFM_HAL_ACCESS_READWRITE | ns_access);
142         if (fih_not_eq(fih_rc, fih_int_encode(PSA_SUCCESS))) {
143             return PSA_ERROR_PROGRAMMER_ERROR;
144         }
145 
146         p_connection->msg.out_size[i]   = ovecs_local[i].len;
147         p_connection->outvec_base[i]    = ovecs_local[i].base;
148         p_connection->outvec_written[i] = 0;
149     }
150 
151     p_connection->caller_outvec = outptr;
152 
153     return PSA_SUCCESS;
154 }
155 
tfm_spm_client_psa_call(psa_handle_t handle,uint32_t ctrl_param,const psa_invec * inptr,psa_outvec * outptr)156 psa_status_t tfm_spm_client_psa_call(psa_handle_t handle,
157                                      uint32_t ctrl_param,
158                                      const psa_invec *inptr,
159                                      psa_outvec *outptr)
160 {
161     struct connection_t *p_connection;
162     int32_t client_id;
163     bool ns_caller = tfm_spm_is_ns_caller();
164     psa_status_t status;
165 
166     client_id = tfm_spm_get_client_id(ns_caller);
167 
168     status = spm_get_idle_connection(&p_connection, handle, client_id);
169     if (status != PSA_SUCCESS) {
170         return status;
171     }
172 
173     status = spm_associate_call_params(p_connection, ctrl_param, inptr, outptr);
174     if (status != PSA_SUCCESS) {
175         if (IS_STATIC_HANDLE(handle)) {
176             spm_free_connection(p_connection);
177         }
178         return status;
179     }
180 
181     return backend_messaging(p_connection);
182 }
183