162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2015-2021, Linaro Limited
462306a36Sopenharmony_ci * Copyright (c) 2016, EPAM Systems
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/arm-smccc.h>
1062306a36Sopenharmony_ci#include <linux/cpuhotplug.h>
1162306a36Sopenharmony_ci#include <linux/errno.h>
1262306a36Sopenharmony_ci#include <linux/firmware.h>
1362306a36Sopenharmony_ci#include <linux/interrupt.h>
1462306a36Sopenharmony_ci#include <linux/io.h>
1562306a36Sopenharmony_ci#include <linux/irqdomain.h>
1662306a36Sopenharmony_ci#include <linux/kernel.h>
1762306a36Sopenharmony_ci#include <linux/mm.h>
1862306a36Sopenharmony_ci#include <linux/module.h>
1962306a36Sopenharmony_ci#include <linux/of.h>
2062306a36Sopenharmony_ci#include <linux/of_irq.h>
2162306a36Sopenharmony_ci#include <linux/of_platform.h>
2262306a36Sopenharmony_ci#include <linux/platform_device.h>
2362306a36Sopenharmony_ci#include <linux/sched.h>
2462306a36Sopenharmony_ci#include <linux/slab.h>
2562306a36Sopenharmony_ci#include <linux/string.h>
2662306a36Sopenharmony_ci#include <linux/tee_drv.h>
2762306a36Sopenharmony_ci#include <linux/types.h>
2862306a36Sopenharmony_ci#include <linux/workqueue.h>
2962306a36Sopenharmony_ci#include "optee_private.h"
3062306a36Sopenharmony_ci#include "optee_smc.h"
3162306a36Sopenharmony_ci#include "optee_rpc_cmd.h"
3262306a36Sopenharmony_ci#include <linux/kmemleak.h>
3362306a36Sopenharmony_ci#define CREATE_TRACE_POINTS
3462306a36Sopenharmony_ci#include "optee_trace.h"
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci/*
3762306a36Sopenharmony_ci * This file implement the SMC ABI used when communicating with secure world
3862306a36Sopenharmony_ci * OP-TEE OS via raw SMCs.
3962306a36Sopenharmony_ci * This file is divided into the following sections:
4062306a36Sopenharmony_ci * 1. Convert between struct tee_param and struct optee_msg_param
4162306a36Sopenharmony_ci * 2. Low level support functions to register shared memory in secure world
4262306a36Sopenharmony_ci * 3. Dynamic shared memory pool based on alloc_pages()
4362306a36Sopenharmony_ci * 4. Do a normal scheduled call into secure world
4462306a36Sopenharmony_ci * 5. Asynchronous notification
4562306a36Sopenharmony_ci * 6. Driver initialization.
4662306a36Sopenharmony_ci */
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/*
4962306a36Sopenharmony_ci * A typical OP-TEE private shm allocation is 224 bytes (argument struct
5062306a36Sopenharmony_ci * with 6 parameters, needed for open session). So with an alignment of 512
5162306a36Sopenharmony_ci * we'll waste a bit more than 50%. However, it's only expected that we'll
5262306a36Sopenharmony_ci * have a handful of these structs allocated at a time. Most memory will
5362306a36Sopenharmony_ci * be allocated aligned to the page size, So all in all this should scale
5462306a36Sopenharmony_ci * up and down quite well.
5562306a36Sopenharmony_ci */
5662306a36Sopenharmony_ci#define OPTEE_MIN_STATIC_POOL_ALIGN    9 /* 512 bytes aligned */
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/* SMC ABI considers at most a single TEE firmware */
5962306a36Sopenharmony_cistatic unsigned int pcpu_irq_num;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic int optee_cpuhp_enable_pcpu_irq(unsigned int cpu)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	enable_percpu_irq(pcpu_irq_num, IRQ_TYPE_NONE);
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	return 0;
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic int optee_cpuhp_disable_pcpu_irq(unsigned int cpu)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	disable_percpu_irq(pcpu_irq_num);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	return 0;
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci/*
7662306a36Sopenharmony_ci * 1. Convert between struct tee_param and struct optee_msg_param
7762306a36Sopenharmony_ci *
7862306a36Sopenharmony_ci * optee_from_msg_param() and optee_to_msg_param() are the main
7962306a36Sopenharmony_ci * functions.
8062306a36Sopenharmony_ci */
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic int from_msg_param_tmp_mem(struct tee_param *p, u32 attr,
8362306a36Sopenharmony_ci				  const struct optee_msg_param *mp)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	struct tee_shm *shm;
8662306a36Sopenharmony_ci	phys_addr_t pa;
8762306a36Sopenharmony_ci	int rc;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT +
9062306a36Sopenharmony_ci		  attr - OPTEE_MSG_ATTR_TYPE_TMEM_INPUT;
9162306a36Sopenharmony_ci	p->u.memref.size = mp->u.tmem.size;
9262306a36Sopenharmony_ci	shm = (struct tee_shm *)(unsigned long)mp->u.tmem.shm_ref;
9362306a36Sopenharmony_ci	if (!shm) {
9462306a36Sopenharmony_ci		p->u.memref.shm_offs = 0;
9562306a36Sopenharmony_ci		p->u.memref.shm = NULL;
9662306a36Sopenharmony_ci		return 0;
9762306a36Sopenharmony_ci	}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	rc = tee_shm_get_pa(shm, 0, &pa);
10062306a36Sopenharmony_ci	if (rc)
10162306a36Sopenharmony_ci		return rc;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	p->u.memref.shm_offs = mp->u.tmem.buf_ptr - pa;
10462306a36Sopenharmony_ci	p->u.memref.shm = shm;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	return 0;
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic void from_msg_param_reg_mem(struct tee_param *p, u32 attr,
11062306a36Sopenharmony_ci				   const struct optee_msg_param *mp)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	struct tee_shm *shm;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT +
11562306a36Sopenharmony_ci		  attr - OPTEE_MSG_ATTR_TYPE_RMEM_INPUT;
11662306a36Sopenharmony_ci	p->u.memref.size = mp->u.rmem.size;
11762306a36Sopenharmony_ci	shm = (struct tee_shm *)(unsigned long)mp->u.rmem.shm_ref;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	if (shm) {
12062306a36Sopenharmony_ci		p->u.memref.shm_offs = mp->u.rmem.offs;
12162306a36Sopenharmony_ci		p->u.memref.shm = shm;
12262306a36Sopenharmony_ci	} else {
12362306a36Sopenharmony_ci		p->u.memref.shm_offs = 0;
12462306a36Sopenharmony_ci		p->u.memref.shm = NULL;
12562306a36Sopenharmony_ci	}
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci/**
12962306a36Sopenharmony_ci * optee_from_msg_param() - convert from OPTEE_MSG parameters to
13062306a36Sopenharmony_ci *			    struct tee_param
13162306a36Sopenharmony_ci * @optee:	main service struct
13262306a36Sopenharmony_ci * @params:	subsystem internal parameter representation
13362306a36Sopenharmony_ci * @num_params:	number of elements in the parameter arrays
13462306a36Sopenharmony_ci * @msg_params:	OPTEE_MSG parameters
13562306a36Sopenharmony_ci * Returns 0 on success or <0 on failure
13662306a36Sopenharmony_ci */
13762306a36Sopenharmony_cistatic int optee_from_msg_param(struct optee *optee, struct tee_param *params,
13862306a36Sopenharmony_ci				size_t num_params,
13962306a36Sopenharmony_ci				const struct optee_msg_param *msg_params)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	int rc;
14262306a36Sopenharmony_ci	size_t n;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	for (n = 0; n < num_params; n++) {
14562306a36Sopenharmony_ci		struct tee_param *p = params + n;
14662306a36Sopenharmony_ci		const struct optee_msg_param *mp = msg_params + n;
14762306a36Sopenharmony_ci		u32 attr = mp->attr & OPTEE_MSG_ATTR_TYPE_MASK;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci		switch (attr) {
15062306a36Sopenharmony_ci		case OPTEE_MSG_ATTR_TYPE_NONE:
15162306a36Sopenharmony_ci			p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
15262306a36Sopenharmony_ci			memset(&p->u, 0, sizeof(p->u));
15362306a36Sopenharmony_ci			break;
15462306a36Sopenharmony_ci		case OPTEE_MSG_ATTR_TYPE_VALUE_INPUT:
15562306a36Sopenharmony_ci		case OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT:
15662306a36Sopenharmony_ci		case OPTEE_MSG_ATTR_TYPE_VALUE_INOUT:
15762306a36Sopenharmony_ci			optee_from_msg_param_value(p, attr, mp);
15862306a36Sopenharmony_ci			break;
15962306a36Sopenharmony_ci		case OPTEE_MSG_ATTR_TYPE_TMEM_INPUT:
16062306a36Sopenharmony_ci		case OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT:
16162306a36Sopenharmony_ci		case OPTEE_MSG_ATTR_TYPE_TMEM_INOUT:
16262306a36Sopenharmony_ci			rc = from_msg_param_tmp_mem(p, attr, mp);
16362306a36Sopenharmony_ci			if (rc)
16462306a36Sopenharmony_ci				return rc;
16562306a36Sopenharmony_ci			break;
16662306a36Sopenharmony_ci		case OPTEE_MSG_ATTR_TYPE_RMEM_INPUT:
16762306a36Sopenharmony_ci		case OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT:
16862306a36Sopenharmony_ci		case OPTEE_MSG_ATTR_TYPE_RMEM_INOUT:
16962306a36Sopenharmony_ci			from_msg_param_reg_mem(p, attr, mp);
17062306a36Sopenharmony_ci			break;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci		default:
17362306a36Sopenharmony_ci			return -EINVAL;
17462306a36Sopenharmony_ci		}
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci	return 0;
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cistatic int to_msg_param_tmp_mem(struct optee_msg_param *mp,
18062306a36Sopenharmony_ci				const struct tee_param *p)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	int rc;
18362306a36Sopenharmony_ci	phys_addr_t pa;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	mp->attr = OPTEE_MSG_ATTR_TYPE_TMEM_INPUT + p->attr -
18662306a36Sopenharmony_ci		   TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	mp->u.tmem.shm_ref = (unsigned long)p->u.memref.shm;
18962306a36Sopenharmony_ci	mp->u.tmem.size = p->u.memref.size;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	if (!p->u.memref.shm) {
19262306a36Sopenharmony_ci		mp->u.tmem.buf_ptr = 0;
19362306a36Sopenharmony_ci		return 0;
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	rc = tee_shm_get_pa(p->u.memref.shm, p->u.memref.shm_offs, &pa);
19762306a36Sopenharmony_ci	if (rc)
19862306a36Sopenharmony_ci		return rc;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	mp->u.tmem.buf_ptr = pa;
20162306a36Sopenharmony_ci	mp->attr |= OPTEE_MSG_ATTR_CACHE_PREDEFINED <<
20262306a36Sopenharmony_ci		    OPTEE_MSG_ATTR_CACHE_SHIFT;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	return 0;
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cistatic int to_msg_param_reg_mem(struct optee_msg_param *mp,
20862306a36Sopenharmony_ci				const struct tee_param *p)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	mp->attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT + p->attr -
21162306a36Sopenharmony_ci		   TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	mp->u.rmem.shm_ref = (unsigned long)p->u.memref.shm;
21462306a36Sopenharmony_ci	mp->u.rmem.size = p->u.memref.size;
21562306a36Sopenharmony_ci	mp->u.rmem.offs = p->u.memref.shm_offs;
21662306a36Sopenharmony_ci	return 0;
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci/**
22062306a36Sopenharmony_ci * optee_to_msg_param() - convert from struct tee_params to OPTEE_MSG parameters
22162306a36Sopenharmony_ci * @optee:	main service struct
22262306a36Sopenharmony_ci * @msg_params:	OPTEE_MSG parameters
22362306a36Sopenharmony_ci * @num_params:	number of elements in the parameter arrays
22462306a36Sopenharmony_ci * @params:	subsystem itnernal parameter representation
22562306a36Sopenharmony_ci * Returns 0 on success or <0 on failure
22662306a36Sopenharmony_ci */
22762306a36Sopenharmony_cistatic int optee_to_msg_param(struct optee *optee,
22862306a36Sopenharmony_ci			      struct optee_msg_param *msg_params,
22962306a36Sopenharmony_ci			      size_t num_params, const struct tee_param *params)
23062306a36Sopenharmony_ci{
23162306a36Sopenharmony_ci	int rc;
23262306a36Sopenharmony_ci	size_t n;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	for (n = 0; n < num_params; n++) {
23562306a36Sopenharmony_ci		const struct tee_param *p = params + n;
23662306a36Sopenharmony_ci		struct optee_msg_param *mp = msg_params + n;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci		switch (p->attr) {
23962306a36Sopenharmony_ci		case TEE_IOCTL_PARAM_ATTR_TYPE_NONE:
24062306a36Sopenharmony_ci			mp->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
24162306a36Sopenharmony_ci			memset(&mp->u, 0, sizeof(mp->u));
24262306a36Sopenharmony_ci			break;
24362306a36Sopenharmony_ci		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT:
24462306a36Sopenharmony_ci		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT:
24562306a36Sopenharmony_ci		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT:
24662306a36Sopenharmony_ci			optee_to_msg_param_value(mp, p);
24762306a36Sopenharmony_ci			break;
24862306a36Sopenharmony_ci		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT:
24962306a36Sopenharmony_ci		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT:
25062306a36Sopenharmony_ci		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT:
25162306a36Sopenharmony_ci			if (tee_shm_is_dynamic(p->u.memref.shm))
25262306a36Sopenharmony_ci				rc = to_msg_param_reg_mem(mp, p);
25362306a36Sopenharmony_ci			else
25462306a36Sopenharmony_ci				rc = to_msg_param_tmp_mem(mp, p);
25562306a36Sopenharmony_ci			if (rc)
25662306a36Sopenharmony_ci				return rc;
25762306a36Sopenharmony_ci			break;
25862306a36Sopenharmony_ci		default:
25962306a36Sopenharmony_ci			return -EINVAL;
26062306a36Sopenharmony_ci		}
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci	return 0;
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci/*
26662306a36Sopenharmony_ci * 2. Low level support functions to register shared memory in secure world
26762306a36Sopenharmony_ci *
26862306a36Sopenharmony_ci * Functions to enable/disable shared memory caching in secure world, that
26962306a36Sopenharmony_ci * is, lazy freeing of previously allocated shared memory. Freeing is
27062306a36Sopenharmony_ci * performed when a request has been compled.
27162306a36Sopenharmony_ci *
27262306a36Sopenharmony_ci * Functions to register and unregister shared memory both for normal
27362306a36Sopenharmony_ci * clients and for tee-supplicant.
27462306a36Sopenharmony_ci */
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci/**
27762306a36Sopenharmony_ci * optee_enable_shm_cache() - Enables caching of some shared memory allocation
27862306a36Sopenharmony_ci *			      in OP-TEE
27962306a36Sopenharmony_ci * @optee:	main service struct
28062306a36Sopenharmony_ci */
28162306a36Sopenharmony_cistatic void optee_enable_shm_cache(struct optee *optee)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	struct optee_call_waiter w;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	/* We need to retry until secure world isn't busy. */
28662306a36Sopenharmony_ci	optee_cq_wait_init(&optee->call_queue, &w);
28762306a36Sopenharmony_ci	while (true) {
28862306a36Sopenharmony_ci		struct arm_smccc_res res;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci		optee->smc.invoke_fn(OPTEE_SMC_ENABLE_SHM_CACHE,
29162306a36Sopenharmony_ci				     0, 0, 0, 0, 0, 0, 0, &res);
29262306a36Sopenharmony_ci		if (res.a0 == OPTEE_SMC_RETURN_OK)
29362306a36Sopenharmony_ci			break;
29462306a36Sopenharmony_ci		optee_cq_wait_for_completion(&optee->call_queue, &w);
29562306a36Sopenharmony_ci	}
29662306a36Sopenharmony_ci	optee_cq_wait_final(&optee->call_queue, &w);
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci/**
30062306a36Sopenharmony_ci * __optee_disable_shm_cache() - Disables caching of some shared memory
30162306a36Sopenharmony_ci *				 allocation in OP-TEE
30262306a36Sopenharmony_ci * @optee:	main service struct
30362306a36Sopenharmony_ci * @is_mapped:	true if the cached shared memory addresses were mapped by this
30462306a36Sopenharmony_ci *		kernel, are safe to dereference, and should be freed
30562306a36Sopenharmony_ci */
30662306a36Sopenharmony_cistatic void __optee_disable_shm_cache(struct optee *optee, bool is_mapped)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	struct optee_call_waiter w;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	/* We need to retry until secure world isn't busy. */
31162306a36Sopenharmony_ci	optee_cq_wait_init(&optee->call_queue, &w);
31262306a36Sopenharmony_ci	while (true) {
31362306a36Sopenharmony_ci		union {
31462306a36Sopenharmony_ci			struct arm_smccc_res smccc;
31562306a36Sopenharmony_ci			struct optee_smc_disable_shm_cache_result result;
31662306a36Sopenharmony_ci		} res;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci		optee->smc.invoke_fn(OPTEE_SMC_DISABLE_SHM_CACHE,
31962306a36Sopenharmony_ci				     0, 0, 0, 0, 0, 0, 0, &res.smccc);
32062306a36Sopenharmony_ci		if (res.result.status == OPTEE_SMC_RETURN_ENOTAVAIL)
32162306a36Sopenharmony_ci			break; /* All shm's freed */
32262306a36Sopenharmony_ci		if (res.result.status == OPTEE_SMC_RETURN_OK) {
32362306a36Sopenharmony_ci			struct tee_shm *shm;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci			/*
32662306a36Sopenharmony_ci			 * Shared memory references that were not mapped by
32762306a36Sopenharmony_ci			 * this kernel must be ignored to prevent a crash.
32862306a36Sopenharmony_ci			 */
32962306a36Sopenharmony_ci			if (!is_mapped)
33062306a36Sopenharmony_ci				continue;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci			shm = reg_pair_to_ptr(res.result.shm_upper32,
33362306a36Sopenharmony_ci					      res.result.shm_lower32);
33462306a36Sopenharmony_ci			tee_shm_free(shm);
33562306a36Sopenharmony_ci		} else {
33662306a36Sopenharmony_ci			optee_cq_wait_for_completion(&optee->call_queue, &w);
33762306a36Sopenharmony_ci		}
33862306a36Sopenharmony_ci	}
33962306a36Sopenharmony_ci	optee_cq_wait_final(&optee->call_queue, &w);
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci/**
34362306a36Sopenharmony_ci * optee_disable_shm_cache() - Disables caching of mapped shared memory
34462306a36Sopenharmony_ci *			       allocations in OP-TEE
34562306a36Sopenharmony_ci * @optee:	main service struct
34662306a36Sopenharmony_ci */
34762306a36Sopenharmony_cistatic void optee_disable_shm_cache(struct optee *optee)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci	return __optee_disable_shm_cache(optee, true);
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci/**
35362306a36Sopenharmony_ci * optee_disable_unmapped_shm_cache() - Disables caching of shared memory
35462306a36Sopenharmony_ci *					allocations in OP-TEE which are not
35562306a36Sopenharmony_ci *					currently mapped
35662306a36Sopenharmony_ci * @optee:	main service struct
35762306a36Sopenharmony_ci */
35862306a36Sopenharmony_cistatic void optee_disable_unmapped_shm_cache(struct optee *optee)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	return __optee_disable_shm_cache(optee, false);
36162306a36Sopenharmony_ci}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci#define PAGELIST_ENTRIES_PER_PAGE				\
36462306a36Sopenharmony_ci	((OPTEE_MSG_NONCONTIG_PAGE_SIZE / sizeof(u64)) - 1)
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci/*
36762306a36Sopenharmony_ci * The final entry in each pagelist page is a pointer to the next
36862306a36Sopenharmony_ci * pagelist page.
36962306a36Sopenharmony_ci */
37062306a36Sopenharmony_cistatic size_t get_pages_list_size(size_t num_entries)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	int pages = DIV_ROUND_UP(num_entries, PAGELIST_ENTRIES_PER_PAGE);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	return pages * OPTEE_MSG_NONCONTIG_PAGE_SIZE;
37562306a36Sopenharmony_ci}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_cistatic u64 *optee_allocate_pages_list(size_t num_entries)
37862306a36Sopenharmony_ci{
37962306a36Sopenharmony_ci	return alloc_pages_exact(get_pages_list_size(num_entries), GFP_KERNEL);
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_cistatic void optee_free_pages_list(void *list, size_t num_entries)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	free_pages_exact(list, get_pages_list_size(num_entries));
38562306a36Sopenharmony_ci}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci/**
38862306a36Sopenharmony_ci * optee_fill_pages_list() - write list of user pages to given shared
38962306a36Sopenharmony_ci * buffer.
39062306a36Sopenharmony_ci *
39162306a36Sopenharmony_ci * @dst: page-aligned buffer where list of pages will be stored
39262306a36Sopenharmony_ci * @pages: array of pages that represents shared buffer
39362306a36Sopenharmony_ci * @num_pages: number of entries in @pages
39462306a36Sopenharmony_ci * @page_offset: offset of user buffer from page start
39562306a36Sopenharmony_ci *
39662306a36Sopenharmony_ci * @dst should be big enough to hold list of user page addresses and
39762306a36Sopenharmony_ci *	links to the next pages of buffer
39862306a36Sopenharmony_ci */
39962306a36Sopenharmony_cistatic void optee_fill_pages_list(u64 *dst, struct page **pages, int num_pages,
40062306a36Sopenharmony_ci				  size_t page_offset)
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	int n = 0;
40362306a36Sopenharmony_ci	phys_addr_t optee_page;
40462306a36Sopenharmony_ci	/*
40562306a36Sopenharmony_ci	 * Refer to OPTEE_MSG_ATTR_NONCONTIG description in optee_msg.h
40662306a36Sopenharmony_ci	 * for details.
40762306a36Sopenharmony_ci	 */
40862306a36Sopenharmony_ci	struct {
40962306a36Sopenharmony_ci		u64 pages_list[PAGELIST_ENTRIES_PER_PAGE];
41062306a36Sopenharmony_ci		u64 next_page_data;
41162306a36Sopenharmony_ci	} *pages_data;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	/*
41462306a36Sopenharmony_ci	 * Currently OP-TEE uses 4k page size and it does not looks
41562306a36Sopenharmony_ci	 * like this will change in the future.  On other hand, there are
41662306a36Sopenharmony_ci	 * no know ARM architectures with page size < 4k.
41762306a36Sopenharmony_ci	 * Thus the next built assert looks redundant. But the following
41862306a36Sopenharmony_ci	 * code heavily relies on this assumption, so it is better be
41962306a36Sopenharmony_ci	 * safe than sorry.
42062306a36Sopenharmony_ci	 */
42162306a36Sopenharmony_ci	BUILD_BUG_ON(PAGE_SIZE < OPTEE_MSG_NONCONTIG_PAGE_SIZE);
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	pages_data = (void *)dst;
42462306a36Sopenharmony_ci	/*
42562306a36Sopenharmony_ci	 * If linux page is bigger than 4k, and user buffer offset is
42662306a36Sopenharmony_ci	 * larger than 4k/8k/12k/etc this will skip first 4k pages,
42762306a36Sopenharmony_ci	 * because they bear no value data for OP-TEE.
42862306a36Sopenharmony_ci	 */
42962306a36Sopenharmony_ci	optee_page = page_to_phys(*pages) +
43062306a36Sopenharmony_ci		round_down(page_offset, OPTEE_MSG_NONCONTIG_PAGE_SIZE);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	while (true) {
43362306a36Sopenharmony_ci		pages_data->pages_list[n++] = optee_page;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci		if (n == PAGELIST_ENTRIES_PER_PAGE) {
43662306a36Sopenharmony_ci			pages_data->next_page_data =
43762306a36Sopenharmony_ci				virt_to_phys(pages_data + 1);
43862306a36Sopenharmony_ci			pages_data++;
43962306a36Sopenharmony_ci			n = 0;
44062306a36Sopenharmony_ci		}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci		optee_page += OPTEE_MSG_NONCONTIG_PAGE_SIZE;
44362306a36Sopenharmony_ci		if (!(optee_page & ~PAGE_MASK)) {
44462306a36Sopenharmony_ci			if (!--num_pages)
44562306a36Sopenharmony_ci				break;
44662306a36Sopenharmony_ci			pages++;
44762306a36Sopenharmony_ci			optee_page = page_to_phys(*pages);
44862306a36Sopenharmony_ci		}
44962306a36Sopenharmony_ci	}
45062306a36Sopenharmony_ci}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_cistatic int optee_shm_register(struct tee_context *ctx, struct tee_shm *shm,
45362306a36Sopenharmony_ci			      struct page **pages, size_t num_pages,
45462306a36Sopenharmony_ci			      unsigned long start)
45562306a36Sopenharmony_ci{
45662306a36Sopenharmony_ci	struct optee *optee = tee_get_drvdata(ctx->teedev);
45762306a36Sopenharmony_ci	struct optee_msg_arg *msg_arg;
45862306a36Sopenharmony_ci	struct tee_shm *shm_arg;
45962306a36Sopenharmony_ci	u64 *pages_list;
46062306a36Sopenharmony_ci	size_t sz;
46162306a36Sopenharmony_ci	int rc;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	if (!num_pages)
46462306a36Sopenharmony_ci		return -EINVAL;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	rc = optee_check_mem_type(start, num_pages);
46762306a36Sopenharmony_ci	if (rc)
46862306a36Sopenharmony_ci		return rc;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	pages_list = optee_allocate_pages_list(num_pages);
47162306a36Sopenharmony_ci	if (!pages_list)
47262306a36Sopenharmony_ci		return -ENOMEM;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	/*
47562306a36Sopenharmony_ci	 * We're about to register shared memory we can't register shared
47662306a36Sopenharmony_ci	 * memory for this request or there's a catch-22.
47762306a36Sopenharmony_ci	 *
47862306a36Sopenharmony_ci	 * So in this we'll have to do the good old temporary private
47962306a36Sopenharmony_ci	 * allocation instead of using optee_get_msg_arg().
48062306a36Sopenharmony_ci	 */
48162306a36Sopenharmony_ci	sz = optee_msg_arg_size(optee->rpc_param_count);
48262306a36Sopenharmony_ci	shm_arg = tee_shm_alloc_priv_buf(ctx, sz);
48362306a36Sopenharmony_ci	if (IS_ERR(shm_arg)) {
48462306a36Sopenharmony_ci		rc = PTR_ERR(shm_arg);
48562306a36Sopenharmony_ci		goto out;
48662306a36Sopenharmony_ci	}
48762306a36Sopenharmony_ci	msg_arg = tee_shm_get_va(shm_arg, 0);
48862306a36Sopenharmony_ci	if (IS_ERR(msg_arg)) {
48962306a36Sopenharmony_ci		rc = PTR_ERR(msg_arg);
49062306a36Sopenharmony_ci		goto out;
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	optee_fill_pages_list(pages_list, pages, num_pages,
49462306a36Sopenharmony_ci			      tee_shm_get_page_offset(shm));
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	memset(msg_arg, 0, OPTEE_MSG_GET_ARG_SIZE(1));
49762306a36Sopenharmony_ci	msg_arg->num_params = 1;
49862306a36Sopenharmony_ci	msg_arg->cmd = OPTEE_MSG_CMD_REGISTER_SHM;
49962306a36Sopenharmony_ci	msg_arg->params->attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT |
50062306a36Sopenharmony_ci				OPTEE_MSG_ATTR_NONCONTIG;
50162306a36Sopenharmony_ci	msg_arg->params->u.tmem.shm_ref = (unsigned long)shm;
50262306a36Sopenharmony_ci	msg_arg->params->u.tmem.size = tee_shm_get_size(shm);
50362306a36Sopenharmony_ci	/*
50462306a36Sopenharmony_ci	 * In the least bits of msg_arg->params->u.tmem.buf_ptr we
50562306a36Sopenharmony_ci	 * store buffer offset from 4k page, as described in OP-TEE ABI.
50662306a36Sopenharmony_ci	 */
50762306a36Sopenharmony_ci	msg_arg->params->u.tmem.buf_ptr = virt_to_phys(pages_list) |
50862306a36Sopenharmony_ci	  (tee_shm_get_page_offset(shm) & (OPTEE_MSG_NONCONTIG_PAGE_SIZE - 1));
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	if (optee->ops->do_call_with_arg(ctx, shm_arg, 0) ||
51162306a36Sopenharmony_ci	    msg_arg->ret != TEEC_SUCCESS)
51262306a36Sopenharmony_ci		rc = -EINVAL;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	tee_shm_free(shm_arg);
51562306a36Sopenharmony_ciout:
51662306a36Sopenharmony_ci	optee_free_pages_list(pages_list, num_pages);
51762306a36Sopenharmony_ci	return rc;
51862306a36Sopenharmony_ci}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_cistatic int optee_shm_unregister(struct tee_context *ctx, struct tee_shm *shm)
52162306a36Sopenharmony_ci{
52262306a36Sopenharmony_ci	struct optee *optee = tee_get_drvdata(ctx->teedev);
52362306a36Sopenharmony_ci	struct optee_msg_arg *msg_arg;
52462306a36Sopenharmony_ci	struct tee_shm *shm_arg;
52562306a36Sopenharmony_ci	int rc = 0;
52662306a36Sopenharmony_ci	size_t sz;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	/*
52962306a36Sopenharmony_ci	 * We're about to unregister shared memory and we may not be able
53062306a36Sopenharmony_ci	 * register shared memory for this request in case we're called
53162306a36Sopenharmony_ci	 * from optee_shm_arg_cache_uninit().
53262306a36Sopenharmony_ci	 *
53362306a36Sopenharmony_ci	 * So in order to keep things simple in this function just as in
53462306a36Sopenharmony_ci	 * optee_shm_register() we'll use temporary private allocation
53562306a36Sopenharmony_ci	 * instead of using optee_get_msg_arg().
53662306a36Sopenharmony_ci	 */
53762306a36Sopenharmony_ci	sz = optee_msg_arg_size(optee->rpc_param_count);
53862306a36Sopenharmony_ci	shm_arg = tee_shm_alloc_priv_buf(ctx, sz);
53962306a36Sopenharmony_ci	if (IS_ERR(shm_arg))
54062306a36Sopenharmony_ci		return PTR_ERR(shm_arg);
54162306a36Sopenharmony_ci	msg_arg = tee_shm_get_va(shm_arg, 0);
54262306a36Sopenharmony_ci	if (IS_ERR(msg_arg)) {
54362306a36Sopenharmony_ci		rc = PTR_ERR(msg_arg);
54462306a36Sopenharmony_ci		goto out;
54562306a36Sopenharmony_ci	}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	memset(msg_arg, 0, sz);
54862306a36Sopenharmony_ci	msg_arg->num_params = 1;
54962306a36Sopenharmony_ci	msg_arg->cmd = OPTEE_MSG_CMD_UNREGISTER_SHM;
55062306a36Sopenharmony_ci	msg_arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT;
55162306a36Sopenharmony_ci	msg_arg->params[0].u.rmem.shm_ref = (unsigned long)shm;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	if (optee->ops->do_call_with_arg(ctx, shm_arg, 0) ||
55462306a36Sopenharmony_ci	    msg_arg->ret != TEEC_SUCCESS)
55562306a36Sopenharmony_ci		rc = -EINVAL;
55662306a36Sopenharmony_ciout:
55762306a36Sopenharmony_ci	tee_shm_free(shm_arg);
55862306a36Sopenharmony_ci	return rc;
55962306a36Sopenharmony_ci}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_cistatic int optee_shm_register_supp(struct tee_context *ctx, struct tee_shm *shm,
56262306a36Sopenharmony_ci				   struct page **pages, size_t num_pages,
56362306a36Sopenharmony_ci				   unsigned long start)
56462306a36Sopenharmony_ci{
56562306a36Sopenharmony_ci	/*
56662306a36Sopenharmony_ci	 * We don't want to register supplicant memory in OP-TEE.
56762306a36Sopenharmony_ci	 * Instead information about it will be passed in RPC code.
56862306a36Sopenharmony_ci	 */
56962306a36Sopenharmony_ci	return optee_check_mem_type(start, num_pages);
57062306a36Sopenharmony_ci}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_cistatic int optee_shm_unregister_supp(struct tee_context *ctx,
57362306a36Sopenharmony_ci				     struct tee_shm *shm)
57462306a36Sopenharmony_ci{
57562306a36Sopenharmony_ci	return 0;
57662306a36Sopenharmony_ci}
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci/*
57962306a36Sopenharmony_ci * 3. Dynamic shared memory pool based on alloc_pages()
58062306a36Sopenharmony_ci *
58162306a36Sopenharmony_ci * Implements an OP-TEE specific shared memory pool which is used
58262306a36Sopenharmony_ci * when dynamic shared memory is supported by secure world.
58362306a36Sopenharmony_ci *
58462306a36Sopenharmony_ci * The main function is optee_shm_pool_alloc_pages().
58562306a36Sopenharmony_ci */
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_cistatic int pool_op_alloc(struct tee_shm_pool *pool,
58862306a36Sopenharmony_ci			 struct tee_shm *shm, size_t size, size_t align)
58962306a36Sopenharmony_ci{
59062306a36Sopenharmony_ci	/*
59162306a36Sopenharmony_ci	 * Shared memory private to the OP-TEE driver doesn't need
59262306a36Sopenharmony_ci	 * to be registered with OP-TEE.
59362306a36Sopenharmony_ci	 */
59462306a36Sopenharmony_ci	if (shm->flags & TEE_SHM_PRIV)
59562306a36Sopenharmony_ci		return optee_pool_op_alloc_helper(pool, shm, size, align, NULL);
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	return optee_pool_op_alloc_helper(pool, shm, size, align,
59862306a36Sopenharmony_ci					  optee_shm_register);
59962306a36Sopenharmony_ci}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_cistatic void pool_op_free(struct tee_shm_pool *pool,
60262306a36Sopenharmony_ci			 struct tee_shm *shm)
60362306a36Sopenharmony_ci{
60462306a36Sopenharmony_ci	if (!(shm->flags & TEE_SHM_PRIV))
60562306a36Sopenharmony_ci		optee_pool_op_free_helper(pool, shm, optee_shm_unregister);
60662306a36Sopenharmony_ci	else
60762306a36Sopenharmony_ci		optee_pool_op_free_helper(pool, shm, NULL);
60862306a36Sopenharmony_ci}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_cistatic void pool_op_destroy_pool(struct tee_shm_pool *pool)
61162306a36Sopenharmony_ci{
61262306a36Sopenharmony_ci	kfree(pool);
61362306a36Sopenharmony_ci}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_cistatic const struct tee_shm_pool_ops pool_ops = {
61662306a36Sopenharmony_ci	.alloc = pool_op_alloc,
61762306a36Sopenharmony_ci	.free = pool_op_free,
61862306a36Sopenharmony_ci	.destroy_pool = pool_op_destroy_pool,
61962306a36Sopenharmony_ci};
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci/**
62262306a36Sopenharmony_ci * optee_shm_pool_alloc_pages() - create page-based allocator pool
62362306a36Sopenharmony_ci *
62462306a36Sopenharmony_ci * This pool is used when OP-TEE supports dymanic SHM. In this case
62562306a36Sopenharmony_ci * command buffers and such are allocated from kernel's own memory.
62662306a36Sopenharmony_ci */
62762306a36Sopenharmony_cistatic struct tee_shm_pool *optee_shm_pool_alloc_pages(void)
62862306a36Sopenharmony_ci{
62962306a36Sopenharmony_ci	struct tee_shm_pool *pool = kzalloc(sizeof(*pool), GFP_KERNEL);
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	if (!pool)
63262306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	pool->ops = &pool_ops;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	return pool;
63762306a36Sopenharmony_ci}
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci/*
64062306a36Sopenharmony_ci * 4. Do a normal scheduled call into secure world
64162306a36Sopenharmony_ci *
64262306a36Sopenharmony_ci * The function optee_smc_do_call_with_arg() performs a normal scheduled
64362306a36Sopenharmony_ci * call into secure world. During this call may normal world request help
64462306a36Sopenharmony_ci * from normal world using RPCs, Remote Procedure Calls. This includes
64562306a36Sopenharmony_ci * delivery of non-secure interrupts to for instance allow rescheduling of
64662306a36Sopenharmony_ci * the current task.
64762306a36Sopenharmony_ci */
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_cistatic void handle_rpc_func_cmd_shm_free(struct tee_context *ctx,
65062306a36Sopenharmony_ci					 struct optee_msg_arg *arg)
65162306a36Sopenharmony_ci{
65262306a36Sopenharmony_ci	struct tee_shm *shm;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	arg->ret_origin = TEEC_ORIGIN_COMMS;
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	if (arg->num_params != 1 ||
65762306a36Sopenharmony_ci	    arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT) {
65862306a36Sopenharmony_ci		arg->ret = TEEC_ERROR_BAD_PARAMETERS;
65962306a36Sopenharmony_ci		return;
66062306a36Sopenharmony_ci	}
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	shm = (struct tee_shm *)(unsigned long)arg->params[0].u.value.b;
66362306a36Sopenharmony_ci	switch (arg->params[0].u.value.a) {
66462306a36Sopenharmony_ci	case OPTEE_RPC_SHM_TYPE_APPL:
66562306a36Sopenharmony_ci		optee_rpc_cmd_free_suppl(ctx, shm);
66662306a36Sopenharmony_ci		break;
66762306a36Sopenharmony_ci	case OPTEE_RPC_SHM_TYPE_KERNEL:
66862306a36Sopenharmony_ci		tee_shm_free(shm);
66962306a36Sopenharmony_ci		break;
67062306a36Sopenharmony_ci	default:
67162306a36Sopenharmony_ci		arg->ret = TEEC_ERROR_BAD_PARAMETERS;
67262306a36Sopenharmony_ci	}
67362306a36Sopenharmony_ci	arg->ret = TEEC_SUCCESS;
67462306a36Sopenharmony_ci}
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_cistatic void handle_rpc_func_cmd_shm_alloc(struct tee_context *ctx,
67762306a36Sopenharmony_ci					  struct optee *optee,
67862306a36Sopenharmony_ci					  struct optee_msg_arg *arg,
67962306a36Sopenharmony_ci					  struct optee_call_ctx *call_ctx)
68062306a36Sopenharmony_ci{
68162306a36Sopenharmony_ci	phys_addr_t pa;
68262306a36Sopenharmony_ci	struct tee_shm *shm;
68362306a36Sopenharmony_ci	size_t sz;
68462306a36Sopenharmony_ci	size_t n;
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	arg->ret_origin = TEEC_ORIGIN_COMMS;
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	if (!arg->num_params ||
68962306a36Sopenharmony_ci	    arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT) {
69062306a36Sopenharmony_ci		arg->ret = TEEC_ERROR_BAD_PARAMETERS;
69162306a36Sopenharmony_ci		return;
69262306a36Sopenharmony_ci	}
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	for (n = 1; n < arg->num_params; n++) {
69562306a36Sopenharmony_ci		if (arg->params[n].attr != OPTEE_MSG_ATTR_TYPE_NONE) {
69662306a36Sopenharmony_ci			arg->ret = TEEC_ERROR_BAD_PARAMETERS;
69762306a36Sopenharmony_ci			return;
69862306a36Sopenharmony_ci		}
69962306a36Sopenharmony_ci	}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	sz = arg->params[0].u.value.b;
70262306a36Sopenharmony_ci	switch (arg->params[0].u.value.a) {
70362306a36Sopenharmony_ci	case OPTEE_RPC_SHM_TYPE_APPL:
70462306a36Sopenharmony_ci		shm = optee_rpc_cmd_alloc_suppl(ctx, sz);
70562306a36Sopenharmony_ci		break;
70662306a36Sopenharmony_ci	case OPTEE_RPC_SHM_TYPE_KERNEL:
70762306a36Sopenharmony_ci		shm = tee_shm_alloc_priv_buf(optee->ctx, sz);
70862306a36Sopenharmony_ci		break;
70962306a36Sopenharmony_ci	default:
71062306a36Sopenharmony_ci		arg->ret = TEEC_ERROR_BAD_PARAMETERS;
71162306a36Sopenharmony_ci		return;
71262306a36Sopenharmony_ci	}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	if (IS_ERR(shm)) {
71562306a36Sopenharmony_ci		arg->ret = TEEC_ERROR_OUT_OF_MEMORY;
71662306a36Sopenharmony_ci		return;
71762306a36Sopenharmony_ci	}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	if (tee_shm_get_pa(shm, 0, &pa)) {
72062306a36Sopenharmony_ci		arg->ret = TEEC_ERROR_BAD_PARAMETERS;
72162306a36Sopenharmony_ci		goto bad;
72262306a36Sopenharmony_ci	}
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	sz = tee_shm_get_size(shm);
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	if (tee_shm_is_dynamic(shm)) {
72762306a36Sopenharmony_ci		struct page **pages;
72862306a36Sopenharmony_ci		u64 *pages_list;
72962306a36Sopenharmony_ci		size_t page_num;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci		pages = tee_shm_get_pages(shm, &page_num);
73262306a36Sopenharmony_ci		if (!pages || !page_num) {
73362306a36Sopenharmony_ci			arg->ret = TEEC_ERROR_OUT_OF_MEMORY;
73462306a36Sopenharmony_ci			goto bad;
73562306a36Sopenharmony_ci		}
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci		pages_list = optee_allocate_pages_list(page_num);
73862306a36Sopenharmony_ci		if (!pages_list) {
73962306a36Sopenharmony_ci			arg->ret = TEEC_ERROR_OUT_OF_MEMORY;
74062306a36Sopenharmony_ci			goto bad;
74162306a36Sopenharmony_ci		}
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci		call_ctx->pages_list = pages_list;
74462306a36Sopenharmony_ci		call_ctx->num_entries = page_num;
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci		arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT |
74762306a36Sopenharmony_ci				      OPTEE_MSG_ATTR_NONCONTIG;
74862306a36Sopenharmony_ci		/*
74962306a36Sopenharmony_ci		 * In the least bits of u.tmem.buf_ptr we store buffer offset
75062306a36Sopenharmony_ci		 * from 4k page, as described in OP-TEE ABI.
75162306a36Sopenharmony_ci		 */
75262306a36Sopenharmony_ci		arg->params[0].u.tmem.buf_ptr = virt_to_phys(pages_list) |
75362306a36Sopenharmony_ci			(tee_shm_get_page_offset(shm) &
75462306a36Sopenharmony_ci			 (OPTEE_MSG_NONCONTIG_PAGE_SIZE - 1));
75562306a36Sopenharmony_ci		arg->params[0].u.tmem.size = tee_shm_get_size(shm);
75662306a36Sopenharmony_ci		arg->params[0].u.tmem.shm_ref = (unsigned long)shm;
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci		optee_fill_pages_list(pages_list, pages, page_num,
75962306a36Sopenharmony_ci				      tee_shm_get_page_offset(shm));
76062306a36Sopenharmony_ci	} else {
76162306a36Sopenharmony_ci		arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT;
76262306a36Sopenharmony_ci		arg->params[0].u.tmem.buf_ptr = pa;
76362306a36Sopenharmony_ci		arg->params[0].u.tmem.size = sz;
76462306a36Sopenharmony_ci		arg->params[0].u.tmem.shm_ref = (unsigned long)shm;
76562306a36Sopenharmony_ci	}
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	arg->ret = TEEC_SUCCESS;
76862306a36Sopenharmony_ci	return;
76962306a36Sopenharmony_cibad:
77062306a36Sopenharmony_ci	tee_shm_free(shm);
77162306a36Sopenharmony_ci}
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_cistatic void free_pages_list(struct optee_call_ctx *call_ctx)
77462306a36Sopenharmony_ci{
77562306a36Sopenharmony_ci	if (call_ctx->pages_list) {
77662306a36Sopenharmony_ci		optee_free_pages_list(call_ctx->pages_list,
77762306a36Sopenharmony_ci				      call_ctx->num_entries);
77862306a36Sopenharmony_ci		call_ctx->pages_list = NULL;
77962306a36Sopenharmony_ci		call_ctx->num_entries = 0;
78062306a36Sopenharmony_ci	}
78162306a36Sopenharmony_ci}
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_cistatic void optee_rpc_finalize_call(struct optee_call_ctx *call_ctx)
78462306a36Sopenharmony_ci{
78562306a36Sopenharmony_ci	free_pages_list(call_ctx);
78662306a36Sopenharmony_ci}
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_cistatic void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee,
78962306a36Sopenharmony_ci				struct optee_msg_arg *arg,
79062306a36Sopenharmony_ci				struct optee_call_ctx *call_ctx)
79162306a36Sopenharmony_ci{
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	switch (arg->cmd) {
79462306a36Sopenharmony_ci	case OPTEE_RPC_CMD_SHM_ALLOC:
79562306a36Sopenharmony_ci		free_pages_list(call_ctx);
79662306a36Sopenharmony_ci		handle_rpc_func_cmd_shm_alloc(ctx, optee, arg, call_ctx);
79762306a36Sopenharmony_ci		break;
79862306a36Sopenharmony_ci	case OPTEE_RPC_CMD_SHM_FREE:
79962306a36Sopenharmony_ci		handle_rpc_func_cmd_shm_free(ctx, arg);
80062306a36Sopenharmony_ci		break;
80162306a36Sopenharmony_ci	default:
80262306a36Sopenharmony_ci		optee_rpc_cmd(ctx, optee, arg);
80362306a36Sopenharmony_ci	}
80462306a36Sopenharmony_ci}
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci/**
80762306a36Sopenharmony_ci * optee_handle_rpc() - handle RPC from secure world
80862306a36Sopenharmony_ci * @ctx:	context doing the RPC
80962306a36Sopenharmony_ci * @param:	value of registers for the RPC
81062306a36Sopenharmony_ci * @call_ctx:	call context. Preserved during one OP-TEE invocation
81162306a36Sopenharmony_ci *
81262306a36Sopenharmony_ci * Result of RPC is written back into @param.
81362306a36Sopenharmony_ci */
81462306a36Sopenharmony_cistatic void optee_handle_rpc(struct tee_context *ctx,
81562306a36Sopenharmony_ci			     struct optee_msg_arg *rpc_arg,
81662306a36Sopenharmony_ci			     struct optee_rpc_param *param,
81762306a36Sopenharmony_ci			     struct optee_call_ctx *call_ctx)
81862306a36Sopenharmony_ci{
81962306a36Sopenharmony_ci	struct tee_device *teedev = ctx->teedev;
82062306a36Sopenharmony_ci	struct optee *optee = tee_get_drvdata(teedev);
82162306a36Sopenharmony_ci	struct optee_msg_arg *arg;
82262306a36Sopenharmony_ci	struct tee_shm *shm;
82362306a36Sopenharmony_ci	phys_addr_t pa;
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	switch (OPTEE_SMC_RETURN_GET_RPC_FUNC(param->a0)) {
82662306a36Sopenharmony_ci	case OPTEE_SMC_RPC_FUNC_ALLOC:
82762306a36Sopenharmony_ci		shm = tee_shm_alloc_priv_buf(optee->ctx, param->a1);
82862306a36Sopenharmony_ci		if (!IS_ERR(shm) && !tee_shm_get_pa(shm, 0, &pa)) {
82962306a36Sopenharmony_ci			reg_pair_from_64(&param->a1, &param->a2, pa);
83062306a36Sopenharmony_ci			reg_pair_from_64(&param->a4, &param->a5,
83162306a36Sopenharmony_ci					 (unsigned long)shm);
83262306a36Sopenharmony_ci		} else {
83362306a36Sopenharmony_ci			param->a1 = 0;
83462306a36Sopenharmony_ci			param->a2 = 0;
83562306a36Sopenharmony_ci			param->a4 = 0;
83662306a36Sopenharmony_ci			param->a5 = 0;
83762306a36Sopenharmony_ci		}
83862306a36Sopenharmony_ci		kmemleak_not_leak(shm);
83962306a36Sopenharmony_ci		break;
84062306a36Sopenharmony_ci	case OPTEE_SMC_RPC_FUNC_FREE:
84162306a36Sopenharmony_ci		shm = reg_pair_to_ptr(param->a1, param->a2);
84262306a36Sopenharmony_ci		tee_shm_free(shm);
84362306a36Sopenharmony_ci		break;
84462306a36Sopenharmony_ci	case OPTEE_SMC_RPC_FUNC_FOREIGN_INTR:
84562306a36Sopenharmony_ci		/*
84662306a36Sopenharmony_ci		 * A foreign interrupt was raised while secure world was
84762306a36Sopenharmony_ci		 * executing, since they are handled in Linux a dummy RPC is
84862306a36Sopenharmony_ci		 * performed to let Linux take the interrupt through the normal
84962306a36Sopenharmony_ci		 * vector.
85062306a36Sopenharmony_ci		 */
85162306a36Sopenharmony_ci		break;
85262306a36Sopenharmony_ci	case OPTEE_SMC_RPC_FUNC_CMD:
85362306a36Sopenharmony_ci		if (rpc_arg) {
85462306a36Sopenharmony_ci			arg = rpc_arg;
85562306a36Sopenharmony_ci		} else {
85662306a36Sopenharmony_ci			shm = reg_pair_to_ptr(param->a1, param->a2);
85762306a36Sopenharmony_ci			arg = tee_shm_get_va(shm, 0);
85862306a36Sopenharmony_ci			if (IS_ERR(arg)) {
85962306a36Sopenharmony_ci				pr_err("%s: tee_shm_get_va %p failed\n",
86062306a36Sopenharmony_ci				       __func__, shm);
86162306a36Sopenharmony_ci				break;
86262306a36Sopenharmony_ci			}
86362306a36Sopenharmony_ci		}
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci		handle_rpc_func_cmd(ctx, optee, arg, call_ctx);
86662306a36Sopenharmony_ci		break;
86762306a36Sopenharmony_ci	default:
86862306a36Sopenharmony_ci		pr_warn("Unknown RPC func 0x%x\n",
86962306a36Sopenharmony_ci			(u32)OPTEE_SMC_RETURN_GET_RPC_FUNC(param->a0));
87062306a36Sopenharmony_ci		break;
87162306a36Sopenharmony_ci	}
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	param->a0 = OPTEE_SMC_CALL_RETURN_FROM_RPC;
87462306a36Sopenharmony_ci}
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci/**
87762306a36Sopenharmony_ci * optee_smc_do_call_with_arg() - Do an SMC to OP-TEE in secure world
87862306a36Sopenharmony_ci * @ctx:	calling context
87962306a36Sopenharmony_ci * @shm:	shared memory holding the message to pass to secure world
88062306a36Sopenharmony_ci * @offs:	offset of the message in @shm
88162306a36Sopenharmony_ci *
88262306a36Sopenharmony_ci * Does and SMC to OP-TEE in secure world and handles eventual resulting
88362306a36Sopenharmony_ci * Remote Procedure Calls (RPC) from OP-TEE.
88462306a36Sopenharmony_ci *
88562306a36Sopenharmony_ci * Returns return code from secure world, 0 is OK
88662306a36Sopenharmony_ci */
88762306a36Sopenharmony_cistatic int optee_smc_do_call_with_arg(struct tee_context *ctx,
88862306a36Sopenharmony_ci				      struct tee_shm *shm, u_int offs)
88962306a36Sopenharmony_ci{
89062306a36Sopenharmony_ci	struct optee *optee = tee_get_drvdata(ctx->teedev);
89162306a36Sopenharmony_ci	struct optee_call_waiter w;
89262306a36Sopenharmony_ci	struct optee_rpc_param param = { };
89362306a36Sopenharmony_ci	struct optee_call_ctx call_ctx = { };
89462306a36Sopenharmony_ci	struct optee_msg_arg *rpc_arg = NULL;
89562306a36Sopenharmony_ci	int rc;
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	if (optee->rpc_param_count) {
89862306a36Sopenharmony_ci		struct optee_msg_arg *arg;
89962306a36Sopenharmony_ci		unsigned int rpc_arg_offs;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci		arg = tee_shm_get_va(shm, offs);
90262306a36Sopenharmony_ci		if (IS_ERR(arg))
90362306a36Sopenharmony_ci			return PTR_ERR(arg);
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci		rpc_arg_offs = OPTEE_MSG_GET_ARG_SIZE(arg->num_params);
90662306a36Sopenharmony_ci		rpc_arg = tee_shm_get_va(shm, offs + rpc_arg_offs);
90762306a36Sopenharmony_ci		if (IS_ERR(rpc_arg))
90862306a36Sopenharmony_ci			return PTR_ERR(rpc_arg);
90962306a36Sopenharmony_ci	}
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	if  (rpc_arg && tee_shm_is_dynamic(shm)) {
91262306a36Sopenharmony_ci		param.a0 = OPTEE_SMC_CALL_WITH_REGD_ARG;
91362306a36Sopenharmony_ci		reg_pair_from_64(&param.a1, &param.a2, (u_long)shm);
91462306a36Sopenharmony_ci		param.a3 = offs;
91562306a36Sopenharmony_ci	} else {
91662306a36Sopenharmony_ci		phys_addr_t parg;
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci		rc = tee_shm_get_pa(shm, offs, &parg);
91962306a36Sopenharmony_ci		if (rc)
92062306a36Sopenharmony_ci			return rc;
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci		if (rpc_arg)
92362306a36Sopenharmony_ci			param.a0 = OPTEE_SMC_CALL_WITH_RPC_ARG;
92462306a36Sopenharmony_ci		else
92562306a36Sopenharmony_ci			param.a0 = OPTEE_SMC_CALL_WITH_ARG;
92662306a36Sopenharmony_ci		reg_pair_from_64(&param.a1, &param.a2, parg);
92762306a36Sopenharmony_ci	}
92862306a36Sopenharmony_ci	/* Initialize waiter */
92962306a36Sopenharmony_ci	optee_cq_wait_init(&optee->call_queue, &w);
93062306a36Sopenharmony_ci	while (true) {
93162306a36Sopenharmony_ci		struct arm_smccc_res res;
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci		trace_optee_invoke_fn_begin(&param);
93462306a36Sopenharmony_ci		optee->smc.invoke_fn(param.a0, param.a1, param.a2, param.a3,
93562306a36Sopenharmony_ci				     param.a4, param.a5, param.a6, param.a7,
93662306a36Sopenharmony_ci				     &res);
93762306a36Sopenharmony_ci		trace_optee_invoke_fn_end(&param, &res);
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci		if (res.a0 == OPTEE_SMC_RETURN_ETHREAD_LIMIT) {
94062306a36Sopenharmony_ci			/*
94162306a36Sopenharmony_ci			 * Out of threads in secure world, wait for a thread
94262306a36Sopenharmony_ci			 * become available.
94362306a36Sopenharmony_ci			 */
94462306a36Sopenharmony_ci			optee_cq_wait_for_completion(&optee->call_queue, &w);
94562306a36Sopenharmony_ci		} else if (OPTEE_SMC_RETURN_IS_RPC(res.a0)) {
94662306a36Sopenharmony_ci			cond_resched();
94762306a36Sopenharmony_ci			param.a0 = res.a0;
94862306a36Sopenharmony_ci			param.a1 = res.a1;
94962306a36Sopenharmony_ci			param.a2 = res.a2;
95062306a36Sopenharmony_ci			param.a3 = res.a3;
95162306a36Sopenharmony_ci			optee_handle_rpc(ctx, rpc_arg, &param, &call_ctx);
95262306a36Sopenharmony_ci		} else {
95362306a36Sopenharmony_ci			rc = res.a0;
95462306a36Sopenharmony_ci			break;
95562306a36Sopenharmony_ci		}
95662306a36Sopenharmony_ci	}
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	optee_rpc_finalize_call(&call_ctx);
95962306a36Sopenharmony_ci	/*
96062306a36Sopenharmony_ci	 * We're done with our thread in secure world, if there's any
96162306a36Sopenharmony_ci	 * thread waiters wake up one.
96262306a36Sopenharmony_ci	 */
96362306a36Sopenharmony_ci	optee_cq_wait_final(&optee->call_queue, &w);
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	return rc;
96662306a36Sopenharmony_ci}
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_cistatic int simple_call_with_arg(struct tee_context *ctx, u32 cmd)
96962306a36Sopenharmony_ci{
97062306a36Sopenharmony_ci	struct optee_shm_arg_entry *entry;
97162306a36Sopenharmony_ci	struct optee_msg_arg *msg_arg;
97262306a36Sopenharmony_ci	struct tee_shm *shm;
97362306a36Sopenharmony_ci	u_int offs;
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	msg_arg = optee_get_msg_arg(ctx, 0, &entry, &shm, &offs);
97662306a36Sopenharmony_ci	if (IS_ERR(msg_arg))
97762306a36Sopenharmony_ci		return PTR_ERR(msg_arg);
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	msg_arg->cmd = cmd;
98062306a36Sopenharmony_ci	optee_smc_do_call_with_arg(ctx, shm, offs);
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	optee_free_msg_arg(ctx, entry, offs);
98362306a36Sopenharmony_ci	return 0;
98462306a36Sopenharmony_ci}
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_cistatic int optee_smc_do_bottom_half(struct tee_context *ctx)
98762306a36Sopenharmony_ci{
98862306a36Sopenharmony_ci	return simple_call_with_arg(ctx, OPTEE_MSG_CMD_DO_BOTTOM_HALF);
98962306a36Sopenharmony_ci}
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_cistatic int optee_smc_stop_async_notif(struct tee_context *ctx)
99262306a36Sopenharmony_ci{
99362306a36Sopenharmony_ci	return simple_call_with_arg(ctx, OPTEE_MSG_CMD_STOP_ASYNC_NOTIF);
99462306a36Sopenharmony_ci}
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci/*
99762306a36Sopenharmony_ci * 5. Asynchronous notification
99862306a36Sopenharmony_ci */
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_cistatic u32 get_async_notif_value(optee_invoke_fn *invoke_fn, bool *value_valid,
100162306a36Sopenharmony_ci				 bool *value_pending)
100262306a36Sopenharmony_ci{
100362306a36Sopenharmony_ci	struct arm_smccc_res res;
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	invoke_fn(OPTEE_SMC_GET_ASYNC_NOTIF_VALUE, 0, 0, 0, 0, 0, 0, 0, &res);
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	if (res.a0) {
100862306a36Sopenharmony_ci		*value_valid = false;
100962306a36Sopenharmony_ci		return 0;
101062306a36Sopenharmony_ci	}
101162306a36Sopenharmony_ci	*value_valid = (res.a2 & OPTEE_SMC_ASYNC_NOTIF_VALUE_VALID);
101262306a36Sopenharmony_ci	*value_pending = (res.a2 & OPTEE_SMC_ASYNC_NOTIF_VALUE_PENDING);
101362306a36Sopenharmony_ci	return res.a1;
101462306a36Sopenharmony_ci}
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_cistatic irqreturn_t irq_handler(struct optee *optee)
101762306a36Sopenharmony_ci{
101862306a36Sopenharmony_ci	bool do_bottom_half = false;
101962306a36Sopenharmony_ci	bool value_valid;
102062306a36Sopenharmony_ci	bool value_pending;
102162306a36Sopenharmony_ci	u32 value;
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	do {
102462306a36Sopenharmony_ci		value = get_async_notif_value(optee->smc.invoke_fn,
102562306a36Sopenharmony_ci					      &value_valid, &value_pending);
102662306a36Sopenharmony_ci		if (!value_valid)
102762306a36Sopenharmony_ci			break;
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci		if (value == OPTEE_SMC_ASYNC_NOTIF_VALUE_DO_BOTTOM_HALF)
103062306a36Sopenharmony_ci			do_bottom_half = true;
103162306a36Sopenharmony_ci		else
103262306a36Sopenharmony_ci			optee_notif_send(optee, value);
103362306a36Sopenharmony_ci	} while (value_pending);
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	if (do_bottom_half)
103662306a36Sopenharmony_ci		return IRQ_WAKE_THREAD;
103762306a36Sopenharmony_ci	return IRQ_HANDLED;
103862306a36Sopenharmony_ci}
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_cistatic irqreturn_t notif_irq_handler(int irq, void *dev_id)
104162306a36Sopenharmony_ci{
104262306a36Sopenharmony_ci	struct optee *optee = dev_id;
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	return irq_handler(optee);
104562306a36Sopenharmony_ci}
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_cistatic irqreturn_t notif_irq_thread_fn(int irq, void *dev_id)
104862306a36Sopenharmony_ci{
104962306a36Sopenharmony_ci	struct optee *optee = dev_id;
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	optee_smc_do_bottom_half(optee->ctx);
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	return IRQ_HANDLED;
105462306a36Sopenharmony_ci}
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_cistatic int init_irq(struct optee *optee, u_int irq)
105762306a36Sopenharmony_ci{
105862306a36Sopenharmony_ci	int rc;
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	rc = request_threaded_irq(irq, notif_irq_handler,
106162306a36Sopenharmony_ci				  notif_irq_thread_fn,
106262306a36Sopenharmony_ci				  0, "optee_notification", optee);
106362306a36Sopenharmony_ci	if (rc)
106462306a36Sopenharmony_ci		return rc;
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	optee->smc.notif_irq = irq;
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	return 0;
106962306a36Sopenharmony_ci}
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_cistatic irqreturn_t notif_pcpu_irq_handler(int irq, void *dev_id)
107262306a36Sopenharmony_ci{
107362306a36Sopenharmony_ci	struct optee_pcpu *pcpu = dev_id;
107462306a36Sopenharmony_ci	struct optee *optee = pcpu->optee;
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	if (irq_handler(optee) == IRQ_WAKE_THREAD)
107762306a36Sopenharmony_ci		queue_work(optee->smc.notif_pcpu_wq,
107862306a36Sopenharmony_ci			   &optee->smc.notif_pcpu_work);
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	return IRQ_HANDLED;
108162306a36Sopenharmony_ci}
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_cistatic void notif_pcpu_irq_work_fn(struct work_struct *work)
108462306a36Sopenharmony_ci{
108562306a36Sopenharmony_ci	struct optee_smc *optee_smc = container_of(work, struct optee_smc,
108662306a36Sopenharmony_ci						   notif_pcpu_work);
108762306a36Sopenharmony_ci	struct optee *optee = container_of(optee_smc, struct optee, smc);
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	optee_smc_do_bottom_half(optee->ctx);
109062306a36Sopenharmony_ci}
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_cistatic int init_pcpu_irq(struct optee *optee, u_int irq)
109362306a36Sopenharmony_ci{
109462306a36Sopenharmony_ci	struct optee_pcpu __percpu *optee_pcpu;
109562306a36Sopenharmony_ci	int cpu, rc;
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	optee_pcpu = alloc_percpu(struct optee_pcpu);
109862306a36Sopenharmony_ci	if (!optee_pcpu)
109962306a36Sopenharmony_ci		return -ENOMEM;
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	for_each_present_cpu(cpu)
110262306a36Sopenharmony_ci		per_cpu_ptr(optee_pcpu, cpu)->optee = optee;
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	rc = request_percpu_irq(irq, notif_pcpu_irq_handler,
110562306a36Sopenharmony_ci				"optee_pcpu_notification", optee_pcpu);
110662306a36Sopenharmony_ci	if (rc)
110762306a36Sopenharmony_ci		goto err_free_pcpu;
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	INIT_WORK(&optee->smc.notif_pcpu_work, notif_pcpu_irq_work_fn);
111062306a36Sopenharmony_ci	optee->smc.notif_pcpu_wq = create_workqueue("optee_pcpu_notification");
111162306a36Sopenharmony_ci	if (!optee->smc.notif_pcpu_wq) {
111262306a36Sopenharmony_ci		rc = -EINVAL;
111362306a36Sopenharmony_ci		goto err_free_pcpu_irq;
111462306a36Sopenharmony_ci	}
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	optee->smc.optee_pcpu = optee_pcpu;
111762306a36Sopenharmony_ci	optee->smc.notif_irq = irq;
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci	pcpu_irq_num = irq;
112062306a36Sopenharmony_ci	rc = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "optee/pcpu-notif:starting",
112162306a36Sopenharmony_ci			       optee_cpuhp_enable_pcpu_irq,
112262306a36Sopenharmony_ci			       optee_cpuhp_disable_pcpu_irq);
112362306a36Sopenharmony_ci	if (!rc)
112462306a36Sopenharmony_ci		rc = -EINVAL;
112562306a36Sopenharmony_ci	if (rc < 0)
112662306a36Sopenharmony_ci		goto err_free_pcpu_irq;
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	optee->smc.notif_cpuhp_state = rc;
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	return 0;
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_cierr_free_pcpu_irq:
113362306a36Sopenharmony_ci	free_percpu_irq(irq, optee_pcpu);
113462306a36Sopenharmony_cierr_free_pcpu:
113562306a36Sopenharmony_ci	free_percpu(optee_pcpu);
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	return rc;
113862306a36Sopenharmony_ci}
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_cistatic int optee_smc_notif_init_irq(struct optee *optee, u_int irq)
114162306a36Sopenharmony_ci{
114262306a36Sopenharmony_ci	if (irq_is_percpu_devid(irq))
114362306a36Sopenharmony_ci		return init_pcpu_irq(optee, irq);
114462306a36Sopenharmony_ci	else
114562306a36Sopenharmony_ci		return init_irq(optee, irq);
114662306a36Sopenharmony_ci}
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_cistatic void uninit_pcpu_irq(struct optee *optee)
114962306a36Sopenharmony_ci{
115062306a36Sopenharmony_ci	cpuhp_remove_state(optee->smc.notif_cpuhp_state);
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	destroy_workqueue(optee->smc.notif_pcpu_wq);
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci	free_percpu_irq(optee->smc.notif_irq, optee->smc.optee_pcpu);
115562306a36Sopenharmony_ci	free_percpu(optee->smc.optee_pcpu);
115662306a36Sopenharmony_ci}
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_cistatic void optee_smc_notif_uninit_irq(struct optee *optee)
115962306a36Sopenharmony_ci{
116062306a36Sopenharmony_ci	if (optee->smc.sec_caps & OPTEE_SMC_SEC_CAP_ASYNC_NOTIF) {
116162306a36Sopenharmony_ci		optee_smc_stop_async_notif(optee->ctx);
116262306a36Sopenharmony_ci		if (optee->smc.notif_irq) {
116362306a36Sopenharmony_ci			if (irq_is_percpu_devid(optee->smc.notif_irq))
116462306a36Sopenharmony_ci				uninit_pcpu_irq(optee);
116562306a36Sopenharmony_ci			else
116662306a36Sopenharmony_ci				free_irq(optee->smc.notif_irq, optee);
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci			irq_dispose_mapping(optee->smc.notif_irq);
116962306a36Sopenharmony_ci		}
117062306a36Sopenharmony_ci	}
117162306a36Sopenharmony_ci}
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci/*
117462306a36Sopenharmony_ci * 6. Driver initialization
117562306a36Sopenharmony_ci *
117662306a36Sopenharmony_ci * During driver initialization is secure world probed to find out which
117762306a36Sopenharmony_ci * features it supports so the driver can be initialized with a matching
117862306a36Sopenharmony_ci * configuration. This involves for instance support for dynamic shared
117962306a36Sopenharmony_ci * memory instead of a static memory carvout.
118062306a36Sopenharmony_ci */
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_cistatic void optee_get_version(struct tee_device *teedev,
118362306a36Sopenharmony_ci			      struct tee_ioctl_version_data *vers)
118462306a36Sopenharmony_ci{
118562306a36Sopenharmony_ci	struct tee_ioctl_version_data v = {
118662306a36Sopenharmony_ci		.impl_id = TEE_IMPL_ID_OPTEE,
118762306a36Sopenharmony_ci		.impl_caps = TEE_OPTEE_CAP_TZ,
118862306a36Sopenharmony_ci		.gen_caps = TEE_GEN_CAP_GP,
118962306a36Sopenharmony_ci	};
119062306a36Sopenharmony_ci	struct optee *optee = tee_get_drvdata(teedev);
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci	if (optee->smc.sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM)
119362306a36Sopenharmony_ci		v.gen_caps |= TEE_GEN_CAP_REG_MEM;
119462306a36Sopenharmony_ci	if (optee->smc.sec_caps & OPTEE_SMC_SEC_CAP_MEMREF_NULL)
119562306a36Sopenharmony_ci		v.gen_caps |= TEE_GEN_CAP_MEMREF_NULL;
119662306a36Sopenharmony_ci	*vers = v;
119762306a36Sopenharmony_ci}
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_cistatic int optee_smc_open(struct tee_context *ctx)
120062306a36Sopenharmony_ci{
120162306a36Sopenharmony_ci	struct optee *optee = tee_get_drvdata(ctx->teedev);
120262306a36Sopenharmony_ci	u32 sec_caps = optee->smc.sec_caps;
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci	return optee_open(ctx, sec_caps & OPTEE_SMC_SEC_CAP_MEMREF_NULL);
120562306a36Sopenharmony_ci}
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_cistatic const struct tee_driver_ops optee_clnt_ops = {
120862306a36Sopenharmony_ci	.get_version = optee_get_version,
120962306a36Sopenharmony_ci	.open = optee_smc_open,
121062306a36Sopenharmony_ci	.release = optee_release,
121162306a36Sopenharmony_ci	.open_session = optee_open_session,
121262306a36Sopenharmony_ci	.close_session = optee_close_session,
121362306a36Sopenharmony_ci	.invoke_func = optee_invoke_func,
121462306a36Sopenharmony_ci	.cancel_req = optee_cancel_req,
121562306a36Sopenharmony_ci	.shm_register = optee_shm_register,
121662306a36Sopenharmony_ci	.shm_unregister = optee_shm_unregister,
121762306a36Sopenharmony_ci};
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_cistatic const struct tee_desc optee_clnt_desc = {
122062306a36Sopenharmony_ci	.name = DRIVER_NAME "-clnt",
122162306a36Sopenharmony_ci	.ops = &optee_clnt_ops,
122262306a36Sopenharmony_ci	.owner = THIS_MODULE,
122362306a36Sopenharmony_ci};
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_cistatic const struct tee_driver_ops optee_supp_ops = {
122662306a36Sopenharmony_ci	.get_version = optee_get_version,
122762306a36Sopenharmony_ci	.open = optee_smc_open,
122862306a36Sopenharmony_ci	.release = optee_release_supp,
122962306a36Sopenharmony_ci	.supp_recv = optee_supp_recv,
123062306a36Sopenharmony_ci	.supp_send = optee_supp_send,
123162306a36Sopenharmony_ci	.shm_register = optee_shm_register_supp,
123262306a36Sopenharmony_ci	.shm_unregister = optee_shm_unregister_supp,
123362306a36Sopenharmony_ci};
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_cistatic const struct tee_desc optee_supp_desc = {
123662306a36Sopenharmony_ci	.name = DRIVER_NAME "-supp",
123762306a36Sopenharmony_ci	.ops = &optee_supp_ops,
123862306a36Sopenharmony_ci	.owner = THIS_MODULE,
123962306a36Sopenharmony_ci	.flags = TEE_DESC_PRIVILEGED,
124062306a36Sopenharmony_ci};
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_cistatic const struct optee_ops optee_ops = {
124362306a36Sopenharmony_ci	.do_call_with_arg = optee_smc_do_call_with_arg,
124462306a36Sopenharmony_ci	.to_msg_param = optee_to_msg_param,
124562306a36Sopenharmony_ci	.from_msg_param = optee_from_msg_param,
124662306a36Sopenharmony_ci};
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_cistatic int enable_async_notif(optee_invoke_fn *invoke_fn)
124962306a36Sopenharmony_ci{
125062306a36Sopenharmony_ci	struct arm_smccc_res res;
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci	invoke_fn(OPTEE_SMC_ENABLE_ASYNC_NOTIF, 0, 0, 0, 0, 0, 0, 0, &res);
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	if (res.a0)
125562306a36Sopenharmony_ci		return -EINVAL;
125662306a36Sopenharmony_ci	return 0;
125762306a36Sopenharmony_ci}
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_cistatic bool optee_msg_api_uid_is_optee_api(optee_invoke_fn *invoke_fn)
126062306a36Sopenharmony_ci{
126162306a36Sopenharmony_ci	struct arm_smccc_res res;
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	invoke_fn(OPTEE_SMC_CALLS_UID, 0, 0, 0, 0, 0, 0, 0, &res);
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci	if (res.a0 == OPTEE_MSG_UID_0 && res.a1 == OPTEE_MSG_UID_1 &&
126662306a36Sopenharmony_ci	    res.a2 == OPTEE_MSG_UID_2 && res.a3 == OPTEE_MSG_UID_3)
126762306a36Sopenharmony_ci		return true;
126862306a36Sopenharmony_ci	return false;
126962306a36Sopenharmony_ci}
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci#ifdef CONFIG_OPTEE_INSECURE_LOAD_IMAGE
127262306a36Sopenharmony_cistatic bool optee_msg_api_uid_is_optee_image_load(optee_invoke_fn *invoke_fn)
127362306a36Sopenharmony_ci{
127462306a36Sopenharmony_ci	struct arm_smccc_res res;
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci	invoke_fn(OPTEE_SMC_CALLS_UID, 0, 0, 0, 0, 0, 0, 0, &res);
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	if (res.a0 == OPTEE_MSG_IMAGE_LOAD_UID_0 &&
127962306a36Sopenharmony_ci	    res.a1 == OPTEE_MSG_IMAGE_LOAD_UID_1 &&
128062306a36Sopenharmony_ci	    res.a2 == OPTEE_MSG_IMAGE_LOAD_UID_2 &&
128162306a36Sopenharmony_ci	    res.a3 == OPTEE_MSG_IMAGE_LOAD_UID_3)
128262306a36Sopenharmony_ci		return true;
128362306a36Sopenharmony_ci	return false;
128462306a36Sopenharmony_ci}
128562306a36Sopenharmony_ci#endif
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_cistatic void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
128862306a36Sopenharmony_ci{
128962306a36Sopenharmony_ci	union {
129062306a36Sopenharmony_ci		struct arm_smccc_res smccc;
129162306a36Sopenharmony_ci		struct optee_smc_call_get_os_revision_result result;
129262306a36Sopenharmony_ci	} res = {
129362306a36Sopenharmony_ci		.result = {
129462306a36Sopenharmony_ci			.build_id = 0
129562306a36Sopenharmony_ci		}
129662306a36Sopenharmony_ci	};
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0,
129962306a36Sopenharmony_ci		  &res.smccc);
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci	if (res.result.build_id)
130262306a36Sopenharmony_ci		pr_info("revision %lu.%lu (%08lx)", res.result.major,
130362306a36Sopenharmony_ci			res.result.minor, res.result.build_id);
130462306a36Sopenharmony_ci	else
130562306a36Sopenharmony_ci		pr_info("revision %lu.%lu", res.result.major, res.result.minor);
130662306a36Sopenharmony_ci}
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_cistatic bool optee_msg_api_revision_is_compatible(optee_invoke_fn *invoke_fn)
130962306a36Sopenharmony_ci{
131062306a36Sopenharmony_ci	union {
131162306a36Sopenharmony_ci		struct arm_smccc_res smccc;
131262306a36Sopenharmony_ci		struct optee_smc_calls_revision_result result;
131362306a36Sopenharmony_ci	} res;
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	invoke_fn(OPTEE_SMC_CALLS_REVISION, 0, 0, 0, 0, 0, 0, 0, &res.smccc);
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	if (res.result.major == OPTEE_MSG_REVISION_MAJOR &&
131862306a36Sopenharmony_ci	    (int)res.result.minor >= OPTEE_MSG_REVISION_MINOR)
131962306a36Sopenharmony_ci		return true;
132062306a36Sopenharmony_ci	return false;
132162306a36Sopenharmony_ci}
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_cistatic bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn,
132462306a36Sopenharmony_ci					    u32 *sec_caps, u32 *max_notif_value,
132562306a36Sopenharmony_ci					    unsigned int *rpc_param_count)
132662306a36Sopenharmony_ci{
132762306a36Sopenharmony_ci	union {
132862306a36Sopenharmony_ci		struct arm_smccc_res smccc;
132962306a36Sopenharmony_ci		struct optee_smc_exchange_capabilities_result result;
133062306a36Sopenharmony_ci	} res;
133162306a36Sopenharmony_ci	u32 a1 = 0;
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	/*
133462306a36Sopenharmony_ci	 * TODO This isn't enough to tell if it's UP system (from kernel
133562306a36Sopenharmony_ci	 * point of view) or not, is_smp() returns the information
133662306a36Sopenharmony_ci	 * needed, but can't be called directly from here.
133762306a36Sopenharmony_ci	 */
133862306a36Sopenharmony_ci	if (!IS_ENABLED(CONFIG_SMP) || nr_cpu_ids == 1)
133962306a36Sopenharmony_ci		a1 |= OPTEE_SMC_NSEC_CAP_UNIPROCESSOR;
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci	invoke_fn(OPTEE_SMC_EXCHANGE_CAPABILITIES, a1, 0, 0, 0, 0, 0, 0,
134262306a36Sopenharmony_ci		  &res.smccc);
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci	if (res.result.status != OPTEE_SMC_RETURN_OK)
134562306a36Sopenharmony_ci		return false;
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_ci	*sec_caps = res.result.capabilities;
134862306a36Sopenharmony_ci	if (*sec_caps & OPTEE_SMC_SEC_CAP_ASYNC_NOTIF)
134962306a36Sopenharmony_ci		*max_notif_value = res.result.max_notif_value;
135062306a36Sopenharmony_ci	else
135162306a36Sopenharmony_ci		*max_notif_value = OPTEE_DEFAULT_MAX_NOTIF_VALUE;
135262306a36Sopenharmony_ci	if (*sec_caps & OPTEE_SMC_SEC_CAP_RPC_ARG)
135362306a36Sopenharmony_ci		*rpc_param_count = (u8)res.result.data;
135462306a36Sopenharmony_ci	else
135562306a36Sopenharmony_ci		*rpc_param_count = 0;
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci	return true;
135862306a36Sopenharmony_ci}
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_cistatic struct tee_shm_pool *
136162306a36Sopenharmony_cioptee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm)
136262306a36Sopenharmony_ci{
136362306a36Sopenharmony_ci	union {
136462306a36Sopenharmony_ci		struct arm_smccc_res smccc;
136562306a36Sopenharmony_ci		struct optee_smc_get_shm_config_result result;
136662306a36Sopenharmony_ci	} res;
136762306a36Sopenharmony_ci	unsigned long vaddr;
136862306a36Sopenharmony_ci	phys_addr_t paddr;
136962306a36Sopenharmony_ci	size_t size;
137062306a36Sopenharmony_ci	phys_addr_t begin;
137162306a36Sopenharmony_ci	phys_addr_t end;
137262306a36Sopenharmony_ci	void *va;
137362306a36Sopenharmony_ci	void *rc;
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci	invoke_fn(OPTEE_SMC_GET_SHM_CONFIG, 0, 0, 0, 0, 0, 0, 0, &res.smccc);
137662306a36Sopenharmony_ci	if (res.result.status != OPTEE_SMC_RETURN_OK) {
137762306a36Sopenharmony_ci		pr_err("static shm service not available\n");
137862306a36Sopenharmony_ci		return ERR_PTR(-ENOENT);
137962306a36Sopenharmony_ci	}
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci	if (res.result.settings != OPTEE_SMC_SHM_CACHED) {
138262306a36Sopenharmony_ci		pr_err("only normal cached shared memory supported\n");
138362306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
138462306a36Sopenharmony_ci	}
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci	begin = roundup(res.result.start, PAGE_SIZE);
138762306a36Sopenharmony_ci	end = rounddown(res.result.start + res.result.size, PAGE_SIZE);
138862306a36Sopenharmony_ci	paddr = begin;
138962306a36Sopenharmony_ci	size = end - begin;
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_ci	va = memremap(paddr, size, MEMREMAP_WB);
139262306a36Sopenharmony_ci	if (!va) {
139362306a36Sopenharmony_ci		pr_err("shared memory ioremap failed\n");
139462306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
139562306a36Sopenharmony_ci	}
139662306a36Sopenharmony_ci	vaddr = (unsigned long)va;
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci	rc = tee_shm_pool_alloc_res_mem(vaddr, paddr, size,
139962306a36Sopenharmony_ci					OPTEE_MIN_STATIC_POOL_ALIGN);
140062306a36Sopenharmony_ci	if (IS_ERR(rc))
140162306a36Sopenharmony_ci		memunmap(va);
140262306a36Sopenharmony_ci	else
140362306a36Sopenharmony_ci		*memremaped_shm = va;
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_ci	return rc;
140662306a36Sopenharmony_ci}
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci/* Simple wrapper functions to be able to use a function pointer */
140962306a36Sopenharmony_cistatic void optee_smccc_smc(unsigned long a0, unsigned long a1,
141062306a36Sopenharmony_ci			    unsigned long a2, unsigned long a3,
141162306a36Sopenharmony_ci			    unsigned long a4, unsigned long a5,
141262306a36Sopenharmony_ci			    unsigned long a6, unsigned long a7,
141362306a36Sopenharmony_ci			    struct arm_smccc_res *res)
141462306a36Sopenharmony_ci{
141562306a36Sopenharmony_ci	arm_smccc_smc(a0, a1, a2, a3, a4, a5, a6, a7, res);
141662306a36Sopenharmony_ci}
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_cistatic void optee_smccc_hvc(unsigned long a0, unsigned long a1,
141962306a36Sopenharmony_ci			    unsigned long a2, unsigned long a3,
142062306a36Sopenharmony_ci			    unsigned long a4, unsigned long a5,
142162306a36Sopenharmony_ci			    unsigned long a6, unsigned long a7,
142262306a36Sopenharmony_ci			    struct arm_smccc_res *res)
142362306a36Sopenharmony_ci{
142462306a36Sopenharmony_ci	arm_smccc_hvc(a0, a1, a2, a3, a4, a5, a6, a7, res);
142562306a36Sopenharmony_ci}
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_cistatic optee_invoke_fn *get_invoke_func(struct device *dev)
142862306a36Sopenharmony_ci{
142962306a36Sopenharmony_ci	const char *method;
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci	pr_info("probing for conduit method.\n");
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ci	if (device_property_read_string(dev, "method", &method)) {
143462306a36Sopenharmony_ci		pr_warn("missing \"method\" property\n");
143562306a36Sopenharmony_ci		return ERR_PTR(-ENXIO);
143662306a36Sopenharmony_ci	}
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	if (!strcmp("hvc", method))
143962306a36Sopenharmony_ci		return optee_smccc_hvc;
144062306a36Sopenharmony_ci	else if (!strcmp("smc", method))
144162306a36Sopenharmony_ci		return optee_smccc_smc;
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci	pr_warn("invalid \"method\" property: %s\n", method);
144462306a36Sopenharmony_ci	return ERR_PTR(-EINVAL);
144562306a36Sopenharmony_ci}
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci/* optee_remove - Device Removal Routine
144862306a36Sopenharmony_ci * @pdev: platform device information struct
144962306a36Sopenharmony_ci *
145062306a36Sopenharmony_ci * optee_remove is called by platform subsystem to alert the driver
145162306a36Sopenharmony_ci * that it should release the device
145262306a36Sopenharmony_ci */
145362306a36Sopenharmony_cistatic int optee_smc_remove(struct platform_device *pdev)
145462306a36Sopenharmony_ci{
145562306a36Sopenharmony_ci	struct optee *optee = platform_get_drvdata(pdev);
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	/*
145862306a36Sopenharmony_ci	 * Ask OP-TEE to free all cached shared memory objects to decrease
145962306a36Sopenharmony_ci	 * reference counters and also avoid wild pointers in secure world
146062306a36Sopenharmony_ci	 * into the old shared memory range.
146162306a36Sopenharmony_ci	 */
146262306a36Sopenharmony_ci	if (!optee->rpc_param_count)
146362306a36Sopenharmony_ci		optee_disable_shm_cache(optee);
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci	optee_smc_notif_uninit_irq(optee);
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci	optee_remove_common(optee);
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_ci	if (optee->smc.memremaped_shm)
147062306a36Sopenharmony_ci		memunmap(optee->smc.memremaped_shm);
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci	kfree(optee);
147362306a36Sopenharmony_ci
147462306a36Sopenharmony_ci	return 0;
147562306a36Sopenharmony_ci}
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci/* optee_shutdown - Device Removal Routine
147862306a36Sopenharmony_ci * @pdev: platform device information struct
147962306a36Sopenharmony_ci *
148062306a36Sopenharmony_ci * platform_shutdown is called by the platform subsystem to alert
148162306a36Sopenharmony_ci * the driver that a shutdown, reboot, or kexec is happening and
148262306a36Sopenharmony_ci * device must be disabled.
148362306a36Sopenharmony_ci */
148462306a36Sopenharmony_cistatic void optee_shutdown(struct platform_device *pdev)
148562306a36Sopenharmony_ci{
148662306a36Sopenharmony_ci	struct optee *optee = platform_get_drvdata(pdev);
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci	if (!optee->rpc_param_count)
148962306a36Sopenharmony_ci		optee_disable_shm_cache(optee);
149062306a36Sopenharmony_ci}
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci#ifdef CONFIG_OPTEE_INSECURE_LOAD_IMAGE
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_ci#define OPTEE_FW_IMAGE "optee/tee.bin"
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_cistatic optee_invoke_fn *cpuhp_invoke_fn;
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_cistatic int optee_cpuhp_probe(unsigned int cpu)
149962306a36Sopenharmony_ci{
150062306a36Sopenharmony_ci	/*
150162306a36Sopenharmony_ci	 * Invoking a call on a CPU will cause OP-TEE to perform the required
150262306a36Sopenharmony_ci	 * setup for that CPU. Just invoke the call to get the UID since that
150362306a36Sopenharmony_ci	 * has no side effects.
150462306a36Sopenharmony_ci	 */
150562306a36Sopenharmony_ci	if (optee_msg_api_uid_is_optee_api(cpuhp_invoke_fn))
150662306a36Sopenharmony_ci		return 0;
150762306a36Sopenharmony_ci	else
150862306a36Sopenharmony_ci		return -EINVAL;
150962306a36Sopenharmony_ci}
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_cistatic int optee_load_fw(struct platform_device *pdev,
151262306a36Sopenharmony_ci			 optee_invoke_fn *invoke_fn)
151362306a36Sopenharmony_ci{
151462306a36Sopenharmony_ci	const struct firmware *fw = NULL;
151562306a36Sopenharmony_ci	struct arm_smccc_res res;
151662306a36Sopenharmony_ci	phys_addr_t data_pa;
151762306a36Sopenharmony_ci	u8 *data_buf = NULL;
151862306a36Sopenharmony_ci	u64 data_size;
151962306a36Sopenharmony_ci	u32 data_pa_high, data_pa_low;
152062306a36Sopenharmony_ci	u32 data_size_high, data_size_low;
152162306a36Sopenharmony_ci	int rc;
152262306a36Sopenharmony_ci	int hp_state;
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_ci	if (!optee_msg_api_uid_is_optee_image_load(invoke_fn))
152562306a36Sopenharmony_ci		return 0;
152662306a36Sopenharmony_ci
152762306a36Sopenharmony_ci	rc = request_firmware(&fw, OPTEE_FW_IMAGE, &pdev->dev);
152862306a36Sopenharmony_ci	if (rc) {
152962306a36Sopenharmony_ci		/*
153062306a36Sopenharmony_ci		 * The firmware in the rootfs will not be accessible until we
153162306a36Sopenharmony_ci		 * are in the SYSTEM_RUNNING state, so return EPROBE_DEFER until
153262306a36Sopenharmony_ci		 * that point.
153362306a36Sopenharmony_ci		 */
153462306a36Sopenharmony_ci		if (system_state < SYSTEM_RUNNING)
153562306a36Sopenharmony_ci			return -EPROBE_DEFER;
153662306a36Sopenharmony_ci		goto fw_err;
153762306a36Sopenharmony_ci	}
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci	data_size = fw->size;
154062306a36Sopenharmony_ci	/*
154162306a36Sopenharmony_ci	 * This uses the GFP_DMA flag to ensure we are allocated memory in the
154262306a36Sopenharmony_ci	 * 32-bit space since TF-A cannot map memory beyond the 32-bit boundary.
154362306a36Sopenharmony_ci	 */
154462306a36Sopenharmony_ci	data_buf = kmemdup(fw->data, fw->size, GFP_KERNEL | GFP_DMA);
154562306a36Sopenharmony_ci	if (!data_buf) {
154662306a36Sopenharmony_ci		rc = -ENOMEM;
154762306a36Sopenharmony_ci		goto fw_err;
154862306a36Sopenharmony_ci	}
154962306a36Sopenharmony_ci	data_pa = virt_to_phys(data_buf);
155062306a36Sopenharmony_ci	reg_pair_from_64(&data_pa_high, &data_pa_low, data_pa);
155162306a36Sopenharmony_ci	reg_pair_from_64(&data_size_high, &data_size_low, data_size);
155262306a36Sopenharmony_ci	goto fw_load;
155362306a36Sopenharmony_ci
155462306a36Sopenharmony_cifw_err:
155562306a36Sopenharmony_ci	pr_warn("image loading failed\n");
155662306a36Sopenharmony_ci	data_pa_high = 0;
155762306a36Sopenharmony_ci	data_pa_low = 0;
155862306a36Sopenharmony_ci	data_size_high = 0;
155962306a36Sopenharmony_ci	data_size_low = 0;
156062306a36Sopenharmony_ci
156162306a36Sopenharmony_cifw_load:
156262306a36Sopenharmony_ci	/*
156362306a36Sopenharmony_ci	 * Always invoke the SMC, even if loading the image fails, to indicate
156462306a36Sopenharmony_ci	 * to EL3 that we have passed the point where it should allow invoking
156562306a36Sopenharmony_ci	 * this SMC.
156662306a36Sopenharmony_ci	 */
156762306a36Sopenharmony_ci	pr_warn("OP-TEE image loaded from kernel, this can be insecure");
156862306a36Sopenharmony_ci	invoke_fn(OPTEE_SMC_CALL_LOAD_IMAGE, data_size_high, data_size_low,
156962306a36Sopenharmony_ci		  data_pa_high, data_pa_low, 0, 0, 0, &res);
157062306a36Sopenharmony_ci	if (!rc)
157162306a36Sopenharmony_ci		rc = res.a0;
157262306a36Sopenharmony_ci	if (fw)
157362306a36Sopenharmony_ci		release_firmware(fw);
157462306a36Sopenharmony_ci	kfree(data_buf);
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ci	if (!rc) {
157762306a36Sopenharmony_ci		/*
157862306a36Sopenharmony_ci		 * We need to initialize OP-TEE on all other running cores as
157962306a36Sopenharmony_ci		 * well. Any cores that aren't running yet will get initialized
158062306a36Sopenharmony_ci		 * when they are brought up by the power management functions in
158162306a36Sopenharmony_ci		 * TF-A which are registered by the OP-TEE SPD. Due to that we
158262306a36Sopenharmony_ci		 * can un-register the callback right after registering it.
158362306a36Sopenharmony_ci		 */
158462306a36Sopenharmony_ci		cpuhp_invoke_fn = invoke_fn;
158562306a36Sopenharmony_ci		hp_state = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "optee:probe",
158662306a36Sopenharmony_ci					     optee_cpuhp_probe, NULL);
158762306a36Sopenharmony_ci		if (hp_state < 0) {
158862306a36Sopenharmony_ci			pr_warn("Failed with CPU hotplug setup for OP-TEE");
158962306a36Sopenharmony_ci			return -EINVAL;
159062306a36Sopenharmony_ci		}
159162306a36Sopenharmony_ci		cpuhp_remove_state(hp_state);
159262306a36Sopenharmony_ci		cpuhp_invoke_fn = NULL;
159362306a36Sopenharmony_ci	}
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_ci	return rc;
159662306a36Sopenharmony_ci}
159762306a36Sopenharmony_ci#else
159862306a36Sopenharmony_cistatic inline int optee_load_fw(struct platform_device *pdev,
159962306a36Sopenharmony_ci				optee_invoke_fn *invoke_fn)
160062306a36Sopenharmony_ci{
160162306a36Sopenharmony_ci	return 0;
160262306a36Sopenharmony_ci}
160362306a36Sopenharmony_ci#endif
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_cistatic int optee_probe(struct platform_device *pdev)
160662306a36Sopenharmony_ci{
160762306a36Sopenharmony_ci	optee_invoke_fn *invoke_fn;
160862306a36Sopenharmony_ci	struct tee_shm_pool *pool = ERR_PTR(-EINVAL);
160962306a36Sopenharmony_ci	struct optee *optee = NULL;
161062306a36Sopenharmony_ci	void *memremaped_shm = NULL;
161162306a36Sopenharmony_ci	unsigned int rpc_param_count;
161262306a36Sopenharmony_ci	struct tee_device *teedev;
161362306a36Sopenharmony_ci	struct tee_context *ctx;
161462306a36Sopenharmony_ci	u32 max_notif_value;
161562306a36Sopenharmony_ci	u32 arg_cache_flags;
161662306a36Sopenharmony_ci	u32 sec_caps;
161762306a36Sopenharmony_ci	int rc;
161862306a36Sopenharmony_ci
161962306a36Sopenharmony_ci	invoke_fn = get_invoke_func(&pdev->dev);
162062306a36Sopenharmony_ci	if (IS_ERR(invoke_fn))
162162306a36Sopenharmony_ci		return PTR_ERR(invoke_fn);
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_ci	rc = optee_load_fw(pdev, invoke_fn);
162462306a36Sopenharmony_ci	if (rc)
162562306a36Sopenharmony_ci		return rc;
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_ci	if (!optee_msg_api_uid_is_optee_api(invoke_fn)) {
162862306a36Sopenharmony_ci		pr_warn("api uid mismatch\n");
162962306a36Sopenharmony_ci		return -EINVAL;
163062306a36Sopenharmony_ci	}
163162306a36Sopenharmony_ci
163262306a36Sopenharmony_ci	optee_msg_get_os_revision(invoke_fn);
163362306a36Sopenharmony_ci
163462306a36Sopenharmony_ci	if (!optee_msg_api_revision_is_compatible(invoke_fn)) {
163562306a36Sopenharmony_ci		pr_warn("api revision mismatch\n");
163662306a36Sopenharmony_ci		return -EINVAL;
163762306a36Sopenharmony_ci	}
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci	if (!optee_msg_exchange_capabilities(invoke_fn, &sec_caps,
164062306a36Sopenharmony_ci					     &max_notif_value,
164162306a36Sopenharmony_ci					     &rpc_param_count)) {
164262306a36Sopenharmony_ci		pr_warn("capabilities mismatch\n");
164362306a36Sopenharmony_ci		return -EINVAL;
164462306a36Sopenharmony_ci	}
164562306a36Sopenharmony_ci
164662306a36Sopenharmony_ci	/*
164762306a36Sopenharmony_ci	 * Try to use dynamic shared memory if possible
164862306a36Sopenharmony_ci	 */
164962306a36Sopenharmony_ci	if (sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) {
165062306a36Sopenharmony_ci		/*
165162306a36Sopenharmony_ci		 * If we have OPTEE_SMC_SEC_CAP_RPC_ARG we can ask
165262306a36Sopenharmony_ci		 * optee_get_msg_arg() to pre-register (by having
165362306a36Sopenharmony_ci		 * OPTEE_SHM_ARG_ALLOC_PRIV cleared) the page used to pass
165462306a36Sopenharmony_ci		 * an argument struct.
165562306a36Sopenharmony_ci		 *
165662306a36Sopenharmony_ci		 * With the page is pre-registered we can use a non-zero
165762306a36Sopenharmony_ci		 * offset for argument struct, this is indicated with
165862306a36Sopenharmony_ci		 * OPTEE_SHM_ARG_SHARED.
165962306a36Sopenharmony_ci		 *
166062306a36Sopenharmony_ci		 * This means that optee_smc_do_call_with_arg() will use
166162306a36Sopenharmony_ci		 * OPTEE_SMC_CALL_WITH_REGD_ARG for pre-registered pages.
166262306a36Sopenharmony_ci		 */
166362306a36Sopenharmony_ci		if (sec_caps & OPTEE_SMC_SEC_CAP_RPC_ARG)
166462306a36Sopenharmony_ci			arg_cache_flags = OPTEE_SHM_ARG_SHARED;
166562306a36Sopenharmony_ci		else
166662306a36Sopenharmony_ci			arg_cache_flags = OPTEE_SHM_ARG_ALLOC_PRIV;
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_ci		pool = optee_shm_pool_alloc_pages();
166962306a36Sopenharmony_ci	}
167062306a36Sopenharmony_ci
167162306a36Sopenharmony_ci	/*
167262306a36Sopenharmony_ci	 * If dynamic shared memory is not available or failed - try static one
167362306a36Sopenharmony_ci	 */
167462306a36Sopenharmony_ci	if (IS_ERR(pool) && (sec_caps & OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM)) {
167562306a36Sopenharmony_ci		/*
167662306a36Sopenharmony_ci		 * The static memory pool can use non-zero page offsets so
167762306a36Sopenharmony_ci		 * let optee_get_msg_arg() know that with OPTEE_SHM_ARG_SHARED.
167862306a36Sopenharmony_ci		 *
167962306a36Sopenharmony_ci		 * optee_get_msg_arg() should not pre-register the
168062306a36Sopenharmony_ci		 * allocated page used to pass an argument struct, this is
168162306a36Sopenharmony_ci		 * indicated with OPTEE_SHM_ARG_ALLOC_PRIV.
168262306a36Sopenharmony_ci		 *
168362306a36Sopenharmony_ci		 * This means that optee_smc_do_call_with_arg() will use
168462306a36Sopenharmony_ci		 * OPTEE_SMC_CALL_WITH_ARG if rpc_param_count is 0, else
168562306a36Sopenharmony_ci		 * OPTEE_SMC_CALL_WITH_RPC_ARG.
168662306a36Sopenharmony_ci		 */
168762306a36Sopenharmony_ci		arg_cache_flags = OPTEE_SHM_ARG_SHARED |
168862306a36Sopenharmony_ci				  OPTEE_SHM_ARG_ALLOC_PRIV;
168962306a36Sopenharmony_ci		pool = optee_config_shm_memremap(invoke_fn, &memremaped_shm);
169062306a36Sopenharmony_ci	}
169162306a36Sopenharmony_ci
169262306a36Sopenharmony_ci	if (IS_ERR(pool))
169362306a36Sopenharmony_ci		return PTR_ERR(pool);
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_ci	optee = kzalloc(sizeof(*optee), GFP_KERNEL);
169662306a36Sopenharmony_ci	if (!optee) {
169762306a36Sopenharmony_ci		rc = -ENOMEM;
169862306a36Sopenharmony_ci		goto err_free_pool;
169962306a36Sopenharmony_ci	}
170062306a36Sopenharmony_ci
170162306a36Sopenharmony_ci	optee->ops = &optee_ops;
170262306a36Sopenharmony_ci	optee->smc.invoke_fn = invoke_fn;
170362306a36Sopenharmony_ci	optee->smc.sec_caps = sec_caps;
170462306a36Sopenharmony_ci	optee->rpc_param_count = rpc_param_count;
170562306a36Sopenharmony_ci
170662306a36Sopenharmony_ci	teedev = tee_device_alloc(&optee_clnt_desc, NULL, pool, optee);
170762306a36Sopenharmony_ci	if (IS_ERR(teedev)) {
170862306a36Sopenharmony_ci		rc = PTR_ERR(teedev);
170962306a36Sopenharmony_ci		goto err_free_optee;
171062306a36Sopenharmony_ci	}
171162306a36Sopenharmony_ci	optee->teedev = teedev;
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_ci	teedev = tee_device_alloc(&optee_supp_desc, NULL, pool, optee);
171462306a36Sopenharmony_ci	if (IS_ERR(teedev)) {
171562306a36Sopenharmony_ci		rc = PTR_ERR(teedev);
171662306a36Sopenharmony_ci		goto err_unreg_teedev;
171762306a36Sopenharmony_ci	}
171862306a36Sopenharmony_ci	optee->supp_teedev = teedev;
171962306a36Sopenharmony_ci
172062306a36Sopenharmony_ci	rc = tee_device_register(optee->teedev);
172162306a36Sopenharmony_ci	if (rc)
172262306a36Sopenharmony_ci		goto err_unreg_supp_teedev;
172362306a36Sopenharmony_ci
172462306a36Sopenharmony_ci	rc = tee_device_register(optee->supp_teedev);
172562306a36Sopenharmony_ci	if (rc)
172662306a36Sopenharmony_ci		goto err_unreg_supp_teedev;
172762306a36Sopenharmony_ci
172862306a36Sopenharmony_ci	mutex_init(&optee->call_queue.mutex);
172962306a36Sopenharmony_ci	INIT_LIST_HEAD(&optee->call_queue.waiters);
173062306a36Sopenharmony_ci	optee_supp_init(&optee->supp);
173162306a36Sopenharmony_ci	optee->smc.memremaped_shm = memremaped_shm;
173262306a36Sopenharmony_ci	optee->pool = pool;
173362306a36Sopenharmony_ci	optee_shm_arg_cache_init(optee, arg_cache_flags);
173462306a36Sopenharmony_ci
173562306a36Sopenharmony_ci	platform_set_drvdata(pdev, optee);
173662306a36Sopenharmony_ci	ctx = teedev_open(optee->teedev);
173762306a36Sopenharmony_ci	if (IS_ERR(ctx)) {
173862306a36Sopenharmony_ci		rc = PTR_ERR(ctx);
173962306a36Sopenharmony_ci		goto err_supp_uninit;
174062306a36Sopenharmony_ci	}
174162306a36Sopenharmony_ci	optee->ctx = ctx;
174262306a36Sopenharmony_ci	rc = optee_notif_init(optee, max_notif_value);
174362306a36Sopenharmony_ci	if (rc)
174462306a36Sopenharmony_ci		goto err_close_ctx;
174562306a36Sopenharmony_ci
174662306a36Sopenharmony_ci	if (sec_caps & OPTEE_SMC_SEC_CAP_ASYNC_NOTIF) {
174762306a36Sopenharmony_ci		unsigned int irq;
174862306a36Sopenharmony_ci
174962306a36Sopenharmony_ci		rc = platform_get_irq(pdev, 0);
175062306a36Sopenharmony_ci		if (rc < 0) {
175162306a36Sopenharmony_ci			pr_err("platform_get_irq: ret %d\n", rc);
175262306a36Sopenharmony_ci			goto err_notif_uninit;
175362306a36Sopenharmony_ci		}
175462306a36Sopenharmony_ci		irq = rc;
175562306a36Sopenharmony_ci
175662306a36Sopenharmony_ci		rc = optee_smc_notif_init_irq(optee, irq);
175762306a36Sopenharmony_ci		if (rc) {
175862306a36Sopenharmony_ci			irq_dispose_mapping(irq);
175962306a36Sopenharmony_ci			goto err_notif_uninit;
176062306a36Sopenharmony_ci		}
176162306a36Sopenharmony_ci		enable_async_notif(optee->smc.invoke_fn);
176262306a36Sopenharmony_ci		pr_info("Asynchronous notifications enabled\n");
176362306a36Sopenharmony_ci	}
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_ci	/*
176662306a36Sopenharmony_ci	 * Ensure that there are no pre-existing shm objects before enabling
176762306a36Sopenharmony_ci	 * the shm cache so that there's no chance of receiving an invalid
176862306a36Sopenharmony_ci	 * address during shutdown. This could occur, for example, if we're
176962306a36Sopenharmony_ci	 * kexec booting from an older kernel that did not properly cleanup the
177062306a36Sopenharmony_ci	 * shm cache.
177162306a36Sopenharmony_ci	 */
177262306a36Sopenharmony_ci	optee_disable_unmapped_shm_cache(optee);
177362306a36Sopenharmony_ci
177462306a36Sopenharmony_ci	/*
177562306a36Sopenharmony_ci	 * Only enable the shm cache in case we're not able to pass the RPC
177662306a36Sopenharmony_ci	 * arg struct right after the normal arg struct.
177762306a36Sopenharmony_ci	 */
177862306a36Sopenharmony_ci	if (!optee->rpc_param_count)
177962306a36Sopenharmony_ci		optee_enable_shm_cache(optee);
178062306a36Sopenharmony_ci
178162306a36Sopenharmony_ci	if (optee->smc.sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM)
178262306a36Sopenharmony_ci		pr_info("dynamic shared memory is enabled\n");
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_ci	rc = optee_enumerate_devices(PTA_CMD_GET_DEVICES);
178562306a36Sopenharmony_ci	if (rc)
178662306a36Sopenharmony_ci		goto err_disable_shm_cache;
178762306a36Sopenharmony_ci
178862306a36Sopenharmony_ci	pr_info("initialized driver\n");
178962306a36Sopenharmony_ci	return 0;
179062306a36Sopenharmony_ci
179162306a36Sopenharmony_cierr_disable_shm_cache:
179262306a36Sopenharmony_ci	if (!optee->rpc_param_count)
179362306a36Sopenharmony_ci		optee_disable_shm_cache(optee);
179462306a36Sopenharmony_ci	optee_smc_notif_uninit_irq(optee);
179562306a36Sopenharmony_ci	optee_unregister_devices();
179662306a36Sopenharmony_cierr_notif_uninit:
179762306a36Sopenharmony_ci	optee_notif_uninit(optee);
179862306a36Sopenharmony_cierr_close_ctx:
179962306a36Sopenharmony_ci	teedev_close_context(ctx);
180062306a36Sopenharmony_cierr_supp_uninit:
180162306a36Sopenharmony_ci	optee_shm_arg_cache_uninit(optee);
180262306a36Sopenharmony_ci	optee_supp_uninit(&optee->supp);
180362306a36Sopenharmony_ci	mutex_destroy(&optee->call_queue.mutex);
180462306a36Sopenharmony_cierr_unreg_supp_teedev:
180562306a36Sopenharmony_ci	tee_device_unregister(optee->supp_teedev);
180662306a36Sopenharmony_cierr_unreg_teedev:
180762306a36Sopenharmony_ci	tee_device_unregister(optee->teedev);
180862306a36Sopenharmony_cierr_free_optee:
180962306a36Sopenharmony_ci	kfree(optee);
181062306a36Sopenharmony_cierr_free_pool:
181162306a36Sopenharmony_ci	tee_shm_pool_free(pool);
181262306a36Sopenharmony_ci	if (memremaped_shm)
181362306a36Sopenharmony_ci		memunmap(memremaped_shm);
181462306a36Sopenharmony_ci	return rc;
181562306a36Sopenharmony_ci}
181662306a36Sopenharmony_ci
181762306a36Sopenharmony_cistatic const struct of_device_id optee_dt_match[] = {
181862306a36Sopenharmony_ci	{ .compatible = "linaro,optee-tz" },
181962306a36Sopenharmony_ci	{},
182062306a36Sopenharmony_ci};
182162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, optee_dt_match);
182262306a36Sopenharmony_ci
182362306a36Sopenharmony_cistatic struct platform_driver optee_driver = {
182462306a36Sopenharmony_ci	.probe  = optee_probe,
182562306a36Sopenharmony_ci	.remove = optee_smc_remove,
182662306a36Sopenharmony_ci	.shutdown = optee_shutdown,
182762306a36Sopenharmony_ci	.driver = {
182862306a36Sopenharmony_ci		.name = "optee",
182962306a36Sopenharmony_ci		.of_match_table = optee_dt_match,
183062306a36Sopenharmony_ci	},
183162306a36Sopenharmony_ci};
183262306a36Sopenharmony_ci
183362306a36Sopenharmony_ciint optee_smc_abi_register(void)
183462306a36Sopenharmony_ci{
183562306a36Sopenharmony_ci	return platform_driver_register(&optee_driver);
183662306a36Sopenharmony_ci}
183762306a36Sopenharmony_ci
183862306a36Sopenharmony_civoid optee_smc_abi_unregister(void)
183962306a36Sopenharmony_ci{
184062306a36Sopenharmony_ci	platform_driver_unregister(&optee_driver);
184162306a36Sopenharmony_ci}
1842