Commit | Line | Data |
---|---|---|
9c92ab61 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
4fb0a5eb | 2 | /* |
4602c584 | 3 | * Copyright (c) 2015-2021, Linaro Limited |
4fb0a5eb JW |
4 | */ |
5 | ||
6 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | |
7 | ||
8 | #include <linux/delay.h> | |
c05210ab | 9 | #include <linux/i2c.h> |
4fb0a5eb JW |
10 | #include <linux/slab.h> |
11 | #include <linux/tee_drv.h> | |
12 | #include "optee_private.h" | |
617d8e8b | 13 | #include "optee_rpc_cmd.h" |
4fb0a5eb | 14 | |
4fb0a5eb JW |
15 | static void handle_rpc_func_cmd_get_time(struct optee_msg_arg *arg) |
16 | { | |
17 | struct timespec64 ts; | |
18 | ||
19 | if (arg->num_params != 1) | |
20 | goto bad; | |
21 | if ((arg->params[0].attr & OPTEE_MSG_ATTR_TYPE_MASK) != | |
22 | OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT) | |
23 | goto bad; | |
24 | ||
cf89fe88 | 25 | ktime_get_real_ts64(&ts); |
4fb0a5eb JW |
26 | arg->params[0].u.value.a = ts.tv_sec; |
27 | arg->params[0].u.value.b = ts.tv_nsec; | |
28 | ||
29 | arg->ret = TEEC_SUCCESS; | |
30 | return; | |
31 | bad: | |
32 | arg->ret = TEEC_ERROR_BAD_PARAMETERS; | |
33 | } | |
34 | ||
539f8fc2 | 35 | #if IS_REACHABLE(CONFIG_I2C) |
c05210ab JRO |
36 | static void handle_rpc_func_cmd_i2c_transfer(struct tee_context *ctx, |
37 | struct optee_msg_arg *arg) | |
38 | { | |
4602c584 | 39 | struct optee *optee = tee_get_drvdata(ctx->teedev); |
c05210ab | 40 | struct tee_param *params; |
67bc8097 AB |
41 | struct i2c_adapter *adapter; |
42 | struct i2c_msg msg = { }; | |
c05210ab JRO |
43 | size_t i; |
44 | int ret = -EOPNOTSUPP; | |
45 | u8 attr[] = { | |
46 | TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT, | |
47 | TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT, | |
48 | TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT, | |
49 | TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT, | |
50 | }; | |
51 | ||
52 | if (arg->num_params != ARRAY_SIZE(attr)) { | |
53 | arg->ret = TEEC_ERROR_BAD_PARAMETERS; | |
54 | return; | |
55 | } | |
56 | ||
57 | params = kmalloc_array(arg->num_params, sizeof(struct tee_param), | |
58 | GFP_KERNEL); | |
59 | if (!params) { | |
60 | arg->ret = TEEC_ERROR_OUT_OF_MEMORY; | |
61 | return; | |
62 | } | |
63 | ||
4602c584 JW |
64 | if (optee->ops->from_msg_param(optee, params, arg->num_params, |
65 | arg->params)) | |
c05210ab JRO |
66 | goto bad; |
67 | ||
68 | for (i = 0; i < arg->num_params; i++) { | |
69 | if (params[i].attr != attr[i]) | |
70 | goto bad; | |
71 | } | |
72 | ||
67bc8097 AB |
73 | adapter = i2c_get_adapter(params[0].u.value.b); |
74 | if (!adapter) | |
c05210ab JRO |
75 | goto bad; |
76 | ||
617d8e8b | 77 | if (params[1].u.value.a & OPTEE_RPC_I2C_FLAGS_TEN_BIT) { |
67bc8097 | 78 | if (!i2c_check_functionality(adapter, |
c05210ab | 79 | I2C_FUNC_10BIT_ADDR)) { |
67bc8097 | 80 | i2c_put_adapter(adapter); |
c05210ab JRO |
81 | goto bad; |
82 | } | |
83 | ||
67bc8097 | 84 | msg.flags = I2C_M_TEN; |
c05210ab JRO |
85 | } |
86 | ||
67bc8097 AB |
87 | msg.addr = params[0].u.value.c; |
88 | msg.buf = params[2].u.memref.shm->kaddr; | |
89 | msg.len = params[2].u.memref.size; | |
c05210ab JRO |
90 | |
91 | switch (params[0].u.value.a) { | |
617d8e8b | 92 | case OPTEE_RPC_I2C_TRANSFER_RD: |
67bc8097 | 93 | msg.flags |= I2C_M_RD; |
c05210ab | 94 | break; |
617d8e8b | 95 | case OPTEE_RPC_I2C_TRANSFER_WR: |
c05210ab JRO |
96 | break; |
97 | default: | |
67bc8097 | 98 | i2c_put_adapter(adapter); |
c05210ab JRO |
99 | goto bad; |
100 | } | |
101 | ||
67bc8097 AB |
102 | ret = i2c_transfer(adapter, &msg, 1); |
103 | ||
c05210ab JRO |
104 | if (ret < 0) { |
105 | arg->ret = TEEC_ERROR_COMMUNICATION; | |
106 | } else { | |
67bc8097 | 107 | params[3].u.value.a = msg.len; |
4602c584 JW |
108 | if (optee->ops->to_msg_param(optee, arg->params, |
109 | arg->num_params, params)) | |
c05210ab JRO |
110 | arg->ret = TEEC_ERROR_BAD_PARAMETERS; |
111 | else | |
112 | arg->ret = TEEC_SUCCESS; | |
113 | } | |
114 | ||
67bc8097 | 115 | i2c_put_adapter(adapter); |
c05210ab JRO |
116 | kfree(params); |
117 | return; | |
118 | bad: | |
119 | kfree(params); | |
120 | arg->ret = TEEC_ERROR_BAD_PARAMETERS; | |
121 | } | |
122 | #else | |
123 | static void handle_rpc_func_cmd_i2c_transfer(struct tee_context *ctx, | |
124 | struct optee_msg_arg *arg) | |
125 | { | |
126 | arg->ret = TEEC_ERROR_NOT_SUPPORTED; | |
127 | } | |
128 | #endif | |
129 | ||
4fb0a5eb JW |
130 | static void handle_rpc_func_cmd_wq(struct optee *optee, |
131 | struct optee_msg_arg *arg) | |
132 | { | |
133 | if (arg->num_params != 1) | |
134 | goto bad; | |
135 | ||
136 | if ((arg->params[0].attr & OPTEE_MSG_ATTR_TYPE_MASK) != | |
137 | OPTEE_MSG_ATTR_TYPE_VALUE_INPUT) | |
138 | goto bad; | |
139 | ||
140 | switch (arg->params[0].u.value.a) { | |
787c80cc JW |
141 | case OPTEE_RPC_NOTIFICATION_WAIT: |
142 | if (optee_notif_wait(optee, arg->params[0].u.value.b)) | |
143 | goto bad; | |
4fb0a5eb | 144 | break; |
787c80cc JW |
145 | case OPTEE_RPC_NOTIFICATION_SEND: |
146 | if (optee_notif_send(optee, arg->params[0].u.value.b)) | |
147 | goto bad; | |
4fb0a5eb JW |
148 | break; |
149 | default: | |
150 | goto bad; | |
151 | } | |
152 | ||
153 | arg->ret = TEEC_SUCCESS; | |
154 | return; | |
155 | bad: | |
156 | arg->ret = TEEC_ERROR_BAD_PARAMETERS; | |
157 | } | |
158 | ||
159 | static void handle_rpc_func_cmd_wait(struct optee_msg_arg *arg) | |
160 | { | |
161 | u32 msec_to_wait; | |
162 | ||
163 | if (arg->num_params != 1) | |
164 | goto bad; | |
165 | ||
166 | if ((arg->params[0].attr & OPTEE_MSG_ATTR_TYPE_MASK) != | |
167 | OPTEE_MSG_ATTR_TYPE_VALUE_INPUT) | |
168 | goto bad; | |
169 | ||
170 | msec_to_wait = arg->params[0].u.value.a; | |
171 | ||
a9980e94 | 172 | /* Go to interruptible sleep */ |
173 | msleep_interruptible(msec_to_wait); | |
4fb0a5eb JW |
174 | |
175 | arg->ret = TEEC_SUCCESS; | |
176 | return; | |
177 | bad: | |
178 | arg->ret = TEEC_ERROR_BAD_PARAMETERS; | |
179 | } | |
180 | ||
4602c584 | 181 | static void handle_rpc_supp_cmd(struct tee_context *ctx, struct optee *optee, |
4fb0a5eb JW |
182 | struct optee_msg_arg *arg) |
183 | { | |
184 | struct tee_param *params; | |
185 | ||
186 | arg->ret_origin = TEEC_ORIGIN_COMMS; | |
187 | ||
188 | params = kmalloc_array(arg->num_params, sizeof(struct tee_param), | |
189 | GFP_KERNEL); | |
190 | if (!params) { | |
191 | arg->ret = TEEC_ERROR_OUT_OF_MEMORY; | |
192 | return; | |
193 | } | |
194 | ||
4602c584 JW |
195 | if (optee->ops->from_msg_param(optee, params, arg->num_params, |
196 | arg->params)) { | |
4fb0a5eb JW |
197 | arg->ret = TEEC_ERROR_BAD_PARAMETERS; |
198 | goto out; | |
199 | } | |
200 | ||
201 | arg->ret = optee_supp_thrd_req(ctx, arg->cmd, arg->num_params, params); | |
202 | ||
4602c584 JW |
203 | if (optee->ops->to_msg_param(optee, arg->params, arg->num_params, |
204 | params)) | |
4fb0a5eb JW |
205 | arg->ret = TEEC_ERROR_BAD_PARAMETERS; |
206 | out: | |
207 | kfree(params); | |
208 | } | |
209 | ||
c51a564a | 210 | struct tee_shm *optee_rpc_cmd_alloc_suppl(struct tee_context *ctx, size_t sz) |
4fb0a5eb JW |
211 | { |
212 | u32 ret; | |
213 | struct tee_param param; | |
214 | struct optee *optee = tee_get_drvdata(ctx->teedev); | |
215 | struct tee_shm *shm; | |
216 | ||
217 | param.attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT; | |
617d8e8b | 218 | param.u.value.a = OPTEE_RPC_SHM_TYPE_APPL; |
4fb0a5eb JW |
219 | param.u.value.b = sz; |
220 | param.u.value.c = 0; | |
221 | ||
617d8e8b | 222 | ret = optee_supp_thrd_req(ctx, OPTEE_RPC_CMD_SHM_ALLOC, 1, ¶m); |
4fb0a5eb JW |
223 | if (ret) |
224 | return ERR_PTR(-ENOMEM); | |
225 | ||
1647a5ac | 226 | mutex_lock(&optee->supp.mutex); |
4fb0a5eb JW |
227 | /* Increases count as secure world doesn't have a reference */ |
228 | shm = tee_shm_get_from_id(optee->supp.ctx, param.u.value.c); | |
1647a5ac | 229 | mutex_unlock(&optee->supp.mutex); |
4fb0a5eb JW |
230 | return shm; |
231 | } | |
232 | ||
c51a564a | 233 | void optee_rpc_cmd_free_suppl(struct tee_context *ctx, struct tee_shm *shm) |
4fb0a5eb JW |
234 | { |
235 | struct tee_param param; | |
236 | ||
237 | param.attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT; | |
617d8e8b | 238 | param.u.value.a = OPTEE_RPC_SHM_TYPE_APPL; |
4fb0a5eb JW |
239 | param.u.value.b = tee_shm_get_id(shm); |
240 | param.u.value.c = 0; | |
241 | ||
242 | /* | |
243 | * Match the tee_shm_get_from_id() in cmd_alloc_suppl() as secure | |
244 | * world has released its reference. | |
245 | * | |
246 | * It's better to do this before sending the request to supplicant | |
247 | * as we'd like to let the process doing the initial allocation to | |
248 | * do release the last reference too in order to avoid stacking | |
249 | * many pending fput() on the client process. This could otherwise | |
250 | * happen if secure world does many allocate and free in a single | |
251 | * invoke. | |
252 | */ | |
253 | tee_shm_put(shm); | |
254 | ||
617d8e8b | 255 | optee_supp_thrd_req(ctx, OPTEE_RPC_CMD_SHM_FREE, 1, ¶m); |
4fb0a5eb JW |
256 | } |
257 | ||
c51a564a JW |
258 | void optee_rpc_cmd(struct tee_context *ctx, struct optee *optee, |
259 | struct optee_msg_arg *arg) | |
4fb0a5eb | 260 | { |
4fb0a5eb | 261 | switch (arg->cmd) { |
617d8e8b | 262 | case OPTEE_RPC_CMD_GET_TIME: |
4fb0a5eb JW |
263 | handle_rpc_func_cmd_get_time(arg); |
264 | break; | |
787c80cc | 265 | case OPTEE_RPC_CMD_NOTIFICATION: |
4fb0a5eb JW |
266 | handle_rpc_func_cmd_wq(optee, arg); |
267 | break; | |
617d8e8b | 268 | case OPTEE_RPC_CMD_SUSPEND: |
4fb0a5eb JW |
269 | handle_rpc_func_cmd_wait(arg); |
270 | break; | |
617d8e8b | 271 | case OPTEE_RPC_CMD_I2C_TRANSFER: |
c05210ab JRO |
272 | handle_rpc_func_cmd_i2c_transfer(ctx, arg); |
273 | break; | |
4fb0a5eb | 274 | default: |
4602c584 | 275 | handle_rpc_supp_cmd(ctx, optee, arg); |
4fb0a5eb JW |
276 | } |
277 | } | |
278 | ||
4fb0a5eb | 279 |