xref: /linux/drivers/firmware/qcom/qcom_pas_tee.c (revision 59e6295fac26b8e85c1ea859cdd89fa1e47519d7)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
4  */
5 
6 #include <linux/delay.h>
7 #include <linux/of.h>
8 #include <linux/firmware/qcom/qcom_pas.h>
9 #include <linux/kernel.h>
10 #include <linux/module.h>
11 #include <linux/slab.h>
12 #include <linux/tee_drv.h>
13 #include <linux/uuid.h>
14 
15 #include "qcom_pas.h"
16 
17 /*
18  * Peripheral Authentication Service (PAS) supported.
19  *
20  * [in]  params[0].value.a:	Unique 32bit remote processor identifier
21  */
22 #define TA_QCOM_PAS_IS_SUPPORTED		1
23 
24 /*
25  * PAS capabilities.
26  *
27  * [in]  params[0].value.a:	Unique 32bit remote processor identifier
28  * [out] params[1].value.a:	PAS capability flags
29  */
30 #define TA_QCOM_PAS_CAPABILITIES		2
31 
32 /*
33  * PAS image initialization.
34  *
35  * [in]  params[0].value.a:	Unique 32bit remote processor identifier
36  * [in]  params[1].memref:	Loadable firmware metadata
37  */
38 #define TA_QCOM_PAS_INIT_IMAGE			3
39 
40 /*
41  * PAS memory setup.
42  *
43  * [in]  params[0].value.a:	Unique 32bit remote processor identifier
44  * [in]  params[0].value.b:	Relocatable firmware size
45  * [in]  params[1].value.a:	32bit LSB relocatable firmware memory address
46  * [in]  params[1].value.b:	32bit MSB relocatable firmware memory address
47  */
48 #define TA_QCOM_PAS_MEM_SETUP			4
49 
50 /*
51  * PAS get resource table.
52  *
53  * [in]     params[0].value.a:	Unique 32bit remote processor identifier
54  * [inout]  params[1].memref:	Resource table config
55  */
56 #define TA_QCOM_PAS_GET_RESOURCE_TABLE		5
57 
58 /*
59  * PAS image authentication and co-processor reset.
60  *
61  * [in]  params[0].value.a:	Unique 32bit remote processor identifier
62  * [in]  params[0].value.b:	Firmware size
63  * [in]  params[1].value.a:	32bit LSB firmware memory address
64  * [in]  params[1].value.b:	32bit MSB firmware memory address
65  * [in]  params[2].memref:	Optional fw memory space shared/lent
66  */
67 #define TA_QCOM_PAS_AUTH_AND_RESET		6
68 
69 /*
70  * PAS co-processor set suspend/resume state.
71  *
72  * [in]  params[0].value.a:	Unique 32bit remote processor identifier
73  * [in]  params[0].value.b:	Co-processor state identifier
74  */
75 #define TA_QCOM_PAS_SET_REMOTE_STATE		7
76 
77 /*
78  * PAS co-processor shutdown.
79  *
80  * [in]  params[0].value.a:	Unique 32bit remote processor identifier
81  */
82 #define TA_QCOM_PAS_SHUTDOWN			8
83 
84 #define TEE_NUM_PARAMS				4
85 
86 /**
87  * struct qcom_pas_tee_private - PAS service private data
88  * @dev:		PAS service device.
89  * @ctx:		TEE context handler.
90  * @session_id:		PAS TA session identifier.
91  */
92 struct qcom_pas_tee_private {
93 	struct device *dev;
94 	struct tee_context *ctx;
95 	u32 session_id;
96 };
97 
98 static bool qcom_pas_tee_supported(struct device *dev, u32 pas_id)
99 {
100 	struct qcom_pas_tee_private *data = dev_get_drvdata(dev);
101 	struct tee_ioctl_invoke_arg inv_arg = {
102 		.func = TA_QCOM_PAS_IS_SUPPORTED,
103 		.session = data->session_id,
104 		.num_params = TEE_NUM_PARAMS
105 	};
106 	struct tee_param param[4] = {
107 		[0] = {
108 			.attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
109 			.u.value.a = pas_id
110 		}
111 	};
112 	int ret;
113 
114 	ret = tee_client_invoke_func(data->ctx, &inv_arg, param);
115 	if (ret < 0 || inv_arg.ret != 0) {
116 		dev_err(dev, "PAS not supported, pas_id: %d, ret: %d, err: 0x%x\n",
117 			pas_id, ret, inv_arg.ret);
118 		return false;
119 	}
120 
121 	return true;
122 }
123 
124 static int qcom_pas_tee_init_image(struct device *dev, u32 pas_id,
125 				   const void *metadata, size_t size,
126 				   struct qcom_pas_context *ctx)
127 {
128 	struct qcom_pas_tee_private *data = dev_get_drvdata(dev);
129 	struct tee_ioctl_invoke_arg inv_arg = {
130 		.func = TA_QCOM_PAS_INIT_IMAGE,
131 		.session = data->session_id,
132 		.num_params = TEE_NUM_PARAMS
133 	};
134 	struct tee_param param[4] = {
135 		[0] = {
136 			.attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
137 			.u.value.a = pas_id
138 		},
139 		[1] = {
140 			.attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT,
141 		}
142 	};
143 	struct tee_shm *mdata_shm;
144 	u8 *mdata_buf = NULL;
145 	int ret;
146 
147 	mdata_shm = tee_shm_alloc_kernel_buf(data->ctx, size);
148 	if (IS_ERR(mdata_shm)) {
149 		dev_err(dev, "mdata_shm allocation failed\n");
150 		return PTR_ERR(mdata_shm);
151 	}
152 
153 	mdata_buf = tee_shm_get_va(mdata_shm, 0);
154 	if (IS_ERR(mdata_buf)) {
155 		dev_err(dev, "mdata_buf get VA failed\n");
156 		tee_shm_free(mdata_shm);
157 		return PTR_ERR(mdata_buf);
158 	}
159 	memcpy(mdata_buf, metadata, size);
160 
161 	param[1].u.memref.shm = mdata_shm;
162 	param[1].u.memref.size = size;
163 
164 	ret = tee_client_invoke_func(data->ctx, &inv_arg, param);
165 	if (ret < 0 || inv_arg.ret != 0) {
166 		dev_err(dev, "PAS init image failed, pas_id: %d, ret: %d, err: 0x%x\n",
167 			pas_id, ret, inv_arg.ret);
168 		tee_shm_free(mdata_shm);
169 		return ret ?: -EINVAL;
170 	}
171 
172 	if (ctx)
173 		ctx->ptr = (void *)mdata_shm;
174 	else
175 		tee_shm_free(mdata_shm);
176 
177 	return ret;
178 }
179 
180 static int qcom_pas_tee_mem_setup(struct device *dev, u32 pas_id,
181 				  phys_addr_t addr, phys_addr_t size)
182 {
183 	struct qcom_pas_tee_private *data = dev_get_drvdata(dev);
184 	struct tee_ioctl_invoke_arg inv_arg = {
185 		.func = TA_QCOM_PAS_MEM_SETUP,
186 		.session = data->session_id,
187 		.num_params = TEE_NUM_PARAMS
188 	};
189 	struct tee_param param[4] = {
190 		[0] = {
191 			.attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
192 			.u.value.a = pas_id,
193 			.u.value.b = size,
194 		},
195 		[1] = {
196 			.attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
197 			.u.value.a = lower_32_bits(addr),
198 			.u.value.b = upper_32_bits(addr),
199 		}
200 	};
201 	int ret;
202 
203 	ret = tee_client_invoke_func(data->ctx, &inv_arg, param);
204 	if (ret < 0 || inv_arg.ret != 0) {
205 		dev_err(dev, "PAS mem setup failed, pas_id: %d, ret: %d, err: 0x%x\n",
206 			pas_id, ret, inv_arg.ret);
207 		return ret ?: -EINVAL;
208 	}
209 
210 	return ret;
211 }
212 
213 DEFINE_FREE(shm_free, struct tee_shm *, tee_shm_free(_T))
214 
215 static void *qcom_pas_tee_get_rsc_table(struct device *dev,
216 					struct qcom_pas_context *ctx,
217 					void *input_rt, size_t input_rt_size,
218 					size_t *output_rt_size)
219 {
220 	struct qcom_pas_tee_private *data = dev_get_drvdata(dev);
221 	struct tee_ioctl_invoke_arg inv_arg = {
222 		.func = TA_QCOM_PAS_GET_RESOURCE_TABLE,
223 		.session = data->session_id,
224 		.num_params = TEE_NUM_PARAMS
225 	};
226 	struct tee_param param[4] = {
227 		[0] = {
228 			.attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
229 			.u.value.a = ctx->pas_id,
230 		},
231 		[1] = {
232 			.attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT,
233 			.u.memref.size = input_rt_size,
234 		}
235 	};
236 	void *rt_buf = NULL;
237 	int ret;
238 
239 	ret = tee_client_invoke_func(data->ctx, &inv_arg, param);
240 	if (ret < 0 || inv_arg.ret != 0) {
241 		dev_err(dev, "PAS get RT failed, pas_id: %d, ret: %d, err: 0x%x\n",
242 			ctx->pas_id, ret, inv_arg.ret);
243 		return ret ? ERR_PTR(ret) : ERR_PTR(-EINVAL);
244 	}
245 
246 	if (param[1].u.memref.size >= input_rt_size) {
247 		struct tee_shm *rt_shm __free(shm_free) =
248 			tee_shm_alloc_kernel_buf(data->ctx,
249 						 param[1].u.memref.size);
250 		void *rt_shm_va;
251 
252 		if (IS_ERR_OR_NULL(rt_shm)) {
253 			dev_err(dev, "rt_shm allocation failed\n");
254 			rt_shm = NULL;
255 			return ERR_PTR(-ENOMEM);
256 		}
257 
258 		rt_shm_va = tee_shm_get_va(rt_shm, 0);
259 		if (IS_ERR(rt_shm_va)) {
260 			dev_err(dev, "rt_shm get VA failed\n");
261 			return ERR_CAST(rt_shm_va);
262 		}
263 		memcpy(rt_shm_va, input_rt, input_rt_size);
264 
265 		param[1].u.memref.shm = rt_shm;
266 		ret = tee_client_invoke_func(data->ctx, &inv_arg, param);
267 		if (ret < 0 || inv_arg.ret != 0) {
268 			dev_err(dev, "PAS get RT failed, pas_id: %d, ret: %d, err: 0x%x\n",
269 				ctx->pas_id, ret, inv_arg.ret);
270 			return ret ? ERR_PTR(ret) : ERR_PTR(-EINVAL);
271 		}
272 
273 		if (param[1].u.memref.size) {
274 			*output_rt_size = param[1].u.memref.size;
275 			rt_buf = kmemdup(rt_shm_va, *output_rt_size, GFP_KERNEL);
276 			if (!rt_buf)
277 				return ERR_PTR(-ENOMEM);
278 		}
279 	} else {
280 		*output_rt_size = 0;
281 	}
282 
283 	return rt_buf;
284 }
285 
286 static int __qcom_pas_tee_auth_and_reset(struct device *dev, u32 pas_id,
287 					 phys_addr_t mem_phys, size_t mem_size)
288 {
289 	struct qcom_pas_tee_private *data = dev_get_drvdata(dev);
290 	struct tee_ioctl_invoke_arg inv_arg = {
291 		.func = TA_QCOM_PAS_AUTH_AND_RESET,
292 		.session = data->session_id,
293 		.num_params = TEE_NUM_PARAMS
294 	};
295 	struct tee_param param[4] = {
296 		[0] = {
297 			.attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
298 			.u.value.a = pas_id,
299 			.u.value.b = mem_size,
300 		},
301 		[1] = {
302 			.attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
303 			.u.value.a = lower_32_bits(mem_phys),
304 			.u.value.b = upper_32_bits(mem_phys),
305 		},
306 		/* Reserved for fw memory space to be shared or lent */
307 		[2] = {
308 			.attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT,
309 		}
310 	};
311 	int ret;
312 
313 	ret = tee_client_invoke_func(data->ctx, &inv_arg, param);
314 	if (ret < 0 || inv_arg.ret != 0) {
315 		dev_err(dev, "PAS auth reset failed, pas_id: %d, ret: %d, err: 0x%x\n",
316 			pas_id, ret, inv_arg.ret);
317 		return ret ?: -EINVAL;
318 	}
319 
320 	return ret;
321 }
322 
323 static int qcom_pas_tee_auth_and_reset(struct device *dev, u32 pas_id)
324 {
325 	return __qcom_pas_tee_auth_and_reset(dev, pas_id, 0, 0);
326 }
327 
328 static int qcom_pas_tee_prepare_and_auth_reset(struct device *dev,
329 					       struct qcom_pas_context *ctx)
330 {
331 	return __qcom_pas_tee_auth_and_reset(dev, ctx->pas_id, ctx->mem_phys,
332 					     ctx->mem_size);
333 }
334 
335 static int qcom_pas_tee_set_remote_state(struct device *dev, u32 state,
336 					 u32 pas_id)
337 {
338 	struct qcom_pas_tee_private *data = dev_get_drvdata(dev);
339 	struct tee_ioctl_invoke_arg inv_arg = {
340 		.func = TA_QCOM_PAS_SET_REMOTE_STATE,
341 		.session = data->session_id,
342 		.num_params = TEE_NUM_PARAMS
343 	};
344 	struct tee_param param[4] = {
345 		[0] = {
346 			.attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
347 			.u.value.a = pas_id,
348 			.u.value.b = state,
349 		}
350 	};
351 	int ret;
352 
353 	ret = tee_client_invoke_func(data->ctx, &inv_arg, param);
354 	if (ret < 0 || inv_arg.ret != 0) {
355 		dev_err(dev, "PAS set remote state failed, pas_id: %d, ret: %d, err: 0x%x\n",
356 			pas_id, ret, inv_arg.ret);
357 		return ret ?: -EINVAL;
358 	}
359 
360 	return ret;
361 }
362 
363 static int qcom_pas_tee_shutdown(struct device *dev, u32 pas_id)
364 {
365 	struct qcom_pas_tee_private *data = dev_get_drvdata(dev);
366 	struct tee_ioctl_invoke_arg inv_arg = {
367 		.func = TA_QCOM_PAS_SHUTDOWN,
368 		.session = data->session_id,
369 		.num_params = TEE_NUM_PARAMS
370 	};
371 	struct tee_param param[4] = {
372 		[0] = {
373 			.attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
374 			.u.value.a = pas_id
375 		}
376 	};
377 	int ret;
378 
379 	ret = tee_client_invoke_func(data->ctx, &inv_arg, param);
380 	if (ret < 0 || inv_arg.ret != 0) {
381 		dev_err(dev, "PAS shutdown failed, pas_id: %d, ret: %d, err: 0x%x\n",
382 			pas_id, ret, inv_arg.ret);
383 		return ret ?: -EINVAL;
384 	}
385 
386 	return ret;
387 }
388 
389 static void qcom_pas_tee_metadata_release(struct device *dev,
390 					  struct qcom_pas_context *ctx)
391 {
392 	struct tee_shm *mdata_shm = ctx->ptr;
393 
394 	tee_shm_free(mdata_shm);
395 	ctx->ptr = NULL;
396 }
397 
398 static struct qcom_pas_ops qcom_pas_ops_tee = {
399 	.drv_name		= "qcom-pas-tee",
400 	.supported		= qcom_pas_tee_supported,
401 	.init_image		= qcom_pas_tee_init_image,
402 	.mem_setup		= qcom_pas_tee_mem_setup,
403 	.get_rsc_table		= qcom_pas_tee_get_rsc_table,
404 	.auth_and_reset		= qcom_pas_tee_auth_and_reset,
405 	.prepare_and_auth_reset	= qcom_pas_tee_prepare_and_auth_reset,
406 	.set_remote_state	= qcom_pas_tee_set_remote_state,
407 	.shutdown		= qcom_pas_tee_shutdown,
408 	.metadata_release	= qcom_pas_tee_metadata_release,
409 };
410 
411 static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data)
412 {
413 	return ver->impl_id == TEE_IMPL_ID_OPTEE;
414 }
415 
416 static int qcom_pas_tee_probe(struct tee_client_device *pas_dev)
417 {
418 	struct device *dev = &pas_dev->dev;
419 	struct qcom_pas_tee_private *data;
420 	struct tee_ioctl_open_session_arg sess_arg = {
421 		.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL
422 	};
423 	int ret;
424 
425 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
426 	if (!data)
427 		return -ENOMEM;
428 
429 	data->ctx = tee_client_open_context(NULL, optee_ctx_match, NULL, NULL);
430 	if (IS_ERR(data->ctx))
431 		return -ENODEV;
432 
433 	export_uuid(sess_arg.uuid, &pas_dev->id.uuid);
434 	ret = tee_client_open_session(data->ctx, &sess_arg, NULL);
435 	if (ret < 0 || sess_arg.ret != 0) {
436 		dev_err(dev, "tee_client_open_session failed, ret: %d, err: 0x%x\n",
437 			ret, sess_arg.ret);
438 		tee_client_close_context(data->ctx);
439 		return ret ?: -EINVAL;
440 	}
441 
442 	data->session_id = sess_arg.session;
443 	dev_set_drvdata(dev, data);
444 	qcom_pas_ops_tee.dev = dev;
445 	qcom_pas_ops_register(&qcom_pas_ops_tee);
446 
447 	return ret;
448 }
449 
450 static void qcom_pas_tee_remove(struct tee_client_device *pas_dev)
451 {
452 	struct device *dev = &pas_dev->dev;
453 	struct qcom_pas_tee_private *data = dev_get_drvdata(dev);
454 
455 	qcom_pas_ops_unregister();
456 	tee_client_close_session(data->ctx, data->session_id);
457 	tee_client_close_context(data->ctx);
458 }
459 
460 static const struct tee_client_device_id qcom_pas_tee_id_table[] = {
461 	{UUID_INIT(0xcff7d191, 0x7ca0, 0x4784,
462 		   0xaf, 0x13, 0x48, 0x22, 0x3b, 0x9a, 0x4f, 0xbe)},
463 	{}
464 };
465 MODULE_DEVICE_TABLE(tee, qcom_pas_tee_id_table);
466 
467 static struct tee_client_driver optee_pas_tee_driver = {
468 	.probe		= qcom_pas_tee_probe,
469 	.remove		= qcom_pas_tee_remove,
470 	.id_table	= qcom_pas_tee_id_table,
471 	.driver		= {
472 		.name		= "qcom-pas-tee",
473 	},
474 };
475 
476 module_tee_client_driver(optee_pas_tee_driver);
477 
478 MODULE_LICENSE("GPL");
479 MODULE_DESCRIPTION("Qualcomm PAS TEE driver");
480