diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2022-01-10 08:13:52 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2022-01-10 08:13:52 -0800 |
commit | e85195d5bf8979f6db3f12cf8f1294887bf6b037 (patch) | |
tree | 7133f9676401c4f384df295b696ca29447f1d524 /drivers/tee/optee | |
parent | 0dca3c5e017ab81ebe21eb9096f657c45a6b17a4 (diff) | |
parent | 13ee75c7b57c546f7973984d9a87cfa7d73cbf5c (diff) | |
download | linux-e85195d5bf8979f6db3f12cf8f1294887bf6b037.tar.bz2 |
Merge tag 'drivers-5.17' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc
Pull ARM SoC driver updates from Arnd Bergmann:
"There are cleanups and minor bugfixes across several SoC specific
drivers, for Qualcomm, Samsung, NXP i.MX, AT91, Tegra, Keystone,
Renesas, ZynqMP
Noteworthy new features are:
- The op-tee firmware driver gains support for asynchronous
notifications from secure-world firmware.
- Qualcomm platforms gain support for new SoC types in various
drivers: power domain, cache controller, RPM sleep, soc-info
- Samsung SoC drivers gain support for new SoCs in ChipID and PMU, as
well as a new USIv2 driver that handles various types of serial
communiction (uart, i2c, spi)
- Renesas adds support for R-Car S4-8 (R8A779F0) in multiple drivers,
as well as memory controller support for RZ/G2L (R9A07G044).
- Apple M1 gains support for the PMGR power management driver"
* tag 'drivers-5.17' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc: (94 commits)
soc: qcom: rpmh-rsc: Fix typo in a comment
soc: qcom: socinfo: Add SM6350 and SM7225
dt-bindings: arm: msm: Don't mark LLCC interrupt as required
dt-bindings: firmware: scm: Add SM6350 compatible
dt-bindings: arm: msm: Add LLCC for SM6350
soc: qcom: rpmhpd: Sort power-domain definitions and lists
soc: qcom: rpmhpd: Remove mx/cx relationship on sc7280
soc: qcom: rpmhpd: Rename rpmhpd struct names
soc: qcom: rpmhpd: sm8450: Add the missing .peer for sm8450_cx_ao
soc: qcom: socinfo: add SM8450 ID
soc: qcom: rpmhpd: Add SM8450 power domains
dt-bindings: power: rpmpd: Add SM8450 to rpmpd binding
soc: qcom: smem: Update max processor count
dt-bindings: arm: qcom: Document SM8450 SoC and boards
dt-bindings: firmware: scm: Add SM8450 compatible
dt-bindings: arm: cpus: Add kryo780 compatible
soc: qcom: rpmpd: Add support for sm6125
dt-bindings: qcom-rpmpd: Add sm6125 power domains
soc: qcom: aoss: constify static struct thermal_cooling_device_ops
PM: AVS: qcom-cpr: Use div64_ul instead of do_div
...
Diffstat (limited to 'drivers/tee/optee')
-rw-r--r-- | drivers/tee/optee/Makefile | 1 | ||||
-rw-r--r-- | drivers/tee/optee/core.c | 2 | ||||
-rw-r--r-- | drivers/tee/optee/ffa_abi.c | 6 | ||||
-rw-r--r-- | drivers/tee/optee/notif.c | 125 | ||||
-rw-r--r-- | drivers/tee/optee/optee_msg.h | 9 | ||||
-rw-r--r-- | drivers/tee/optee/optee_private.h | 28 | ||||
-rw-r--r-- | drivers/tee/optee/optee_rpc_cmd.h | 31 | ||||
-rw-r--r-- | drivers/tee/optee/optee_smc.h | 75 | ||||
-rw-r--r-- | drivers/tee/optee/rpc.c | 71 | ||||
-rw-r--r-- | drivers/tee/optee/smc_abi.c | 237 |
10 files changed, 464 insertions, 121 deletions
diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile index 66b8a17f14c4..a6eff388d300 100644 --- a/drivers/tee/optee/Makefile +++ b/drivers/tee/optee/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_OPTEE) += optee.o optee-objs += core.o optee-objs += call.o +optee-objs += notif.o optee-objs += rpc.o optee-objs += supp.o optee-objs += device.o diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index 2a66a5203d2f..1ca320885fad 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -157,6 +157,7 @@ void optee_remove_common(struct optee *optee) /* Unregister OP-TEE specific client devices on TEE bus */ optee_unregister_devices(); + optee_notif_uninit(optee); /* * The two devices have to be unregistered before we can free the * other resources. @@ -165,7 +166,6 @@ void optee_remove_common(struct optee *optee) tee_device_unregister(optee->teedev); tee_shm_pool_free(optee->pool); - optee_wait_queue_exit(&optee->wait_queue); optee_supp_uninit(&optee->supp); mutex_destroy(&optee->call_queue.mutex); } diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c index d8c8683863aa..20a1b1a3d965 100644 --- a/drivers/tee/optee/ffa_abi.c +++ b/drivers/tee/optee/ffa_abi.c @@ -855,9 +855,13 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev) mutex_init(&optee->ffa.mutex); mutex_init(&optee->call_queue.mutex); INIT_LIST_HEAD(&optee->call_queue.waiters); - optee_wait_queue_init(&optee->wait_queue); optee_supp_init(&optee->supp); ffa_dev_set_drvdata(ffa_dev, optee); + rc = optee_notif_init(optee, OPTEE_DEFAULT_MAX_NOTIF_VALUE); + if (rc) { + optee_ffa_remove(ffa_dev); + return rc; + } rc = optee_enumerate_devices(PTA_CMD_GET_DEVICES); if (rc) { diff --git a/drivers/tee/optee/notif.c b/drivers/tee/optee/notif.c new file mode 100644 index 000000000000..a28fa03dcd0e --- /dev/null +++ b/drivers/tee/optee/notif.c @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2015-2021, Linaro Limited + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/arm-smccc.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/tee_drv.h> +#include "optee_private.h" + +struct notif_entry { + struct list_head link; + struct completion c; + u_int key; +}; + +static bool have_key(struct optee *optee, u_int key) +{ + struct notif_entry *entry; + + list_for_each_entry(entry, &optee->notif.db, link) + if (entry->key == key) + return true; + + return false; +} + +int optee_notif_wait(struct optee *optee, u_int key) +{ + unsigned long flags; + struct notif_entry *entry; + int rc = 0; + + if (key > optee->notif.max_key) + return -EINVAL; + + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + return -ENOMEM; + init_completion(&entry->c); + entry->key = key; + + spin_lock_irqsave(&optee->notif.lock, flags); + + /* + * If the bit is already set it means that the key has already + * been posted and we must not wait. + */ + if (test_bit(key, optee->notif.bitmap)) { + clear_bit(key, optee->notif.bitmap); + goto out; + } + + /* + * Check if someone is already waiting for this key. If there is + * it's a programming error. + */ + if (have_key(optee, key)) { + rc = -EBUSY; + goto out; + } + + list_add_tail(&entry->link, &optee->notif.db); + + /* + * Unlock temporarily and wait for completion. + */ + spin_unlock_irqrestore(&optee->notif.lock, flags); + wait_for_completion(&entry->c); + spin_lock_irqsave(&optee->notif.lock, flags); + + list_del(&entry->link); +out: + spin_unlock_irqrestore(&optee->notif.lock, flags); + + kfree(entry); + + return rc; +} + +int optee_notif_send(struct optee *optee, u_int key) +{ + unsigned long flags; + struct notif_entry *entry; + + if (key > optee->notif.max_key) + return -EINVAL; + + spin_lock_irqsave(&optee->notif.lock, flags); + + list_for_each_entry(entry, &optee->notif.db, link) + if (entry->key == key) { + complete(&entry->c); + goto out; + } + + /* Only set the bit in case there where nobody waiting */ + set_bit(key, optee->notif.bitmap); +out: + spin_unlock_irqrestore(&optee->notif.lock, flags); + + return 0; +} + +int optee_notif_init(struct optee *optee, u_int max_key) +{ + spin_lock_init(&optee->notif.lock); + INIT_LIST_HEAD(&optee->notif.db); + optee->notif.bitmap = bitmap_zalloc(max_key, GFP_KERNEL); + if (!optee->notif.bitmap) + return -ENOMEM; + + optee->notif.max_key = max_key; + + return 0; +} + +void optee_notif_uninit(struct optee *optee) +{ + kfree(optee->notif.bitmap); +} diff --git a/drivers/tee/optee/optee_msg.h b/drivers/tee/optee/optee_msg.h index 2422e185d400..70e9cc2ee96b 100644 --- a/drivers/tee/optee/optee_msg.h +++ b/drivers/tee/optee/optee_msg.h @@ -318,6 +318,13 @@ struct optee_msg_arg { * [in] param[0].u.rmem.shm_ref holds shared memory reference * [in] param[0].u.rmem.offs 0 * [in] param[0].u.rmem.size 0 + * + * OPTEE_MSG_CMD_DO_BOTTOM_HALF does the scheduled bottom half processing + * of a driver. + * + * OPTEE_MSG_CMD_STOP_ASYNC_NOTIF informs secure world that from now is + * normal world unable to process asynchronous notifications. Typically + * used when the driver is shut down. */ #define OPTEE_MSG_CMD_OPEN_SESSION 0 #define OPTEE_MSG_CMD_INVOKE_COMMAND 1 @@ -325,6 +332,8 @@ struct optee_msg_arg { #define OPTEE_MSG_CMD_CANCEL 3 #define OPTEE_MSG_CMD_REGISTER_SHM 4 #define OPTEE_MSG_CMD_UNREGISTER_SHM 5 +#define OPTEE_MSG_CMD_DO_BOTTOM_HALF 6 +#define OPTEE_MSG_CMD_STOP_ASYNC_NOTIF 7 #define OPTEE_MSG_FUNCID_CALL_WITH_ARG 0x0004 #endif /* _OPTEE_MSG_H */ diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h index 6660e05298db..46f74ab07c7e 100644 --- a/drivers/tee/optee/optee_private.h +++ b/drivers/tee/optee/optee_private.h @@ -28,6 +28,13 @@ #define TEEC_ORIGIN_COMMS 0x00000002 +/* + * This value should be larger than the number threads in secure world to + * meet the need from secure world. The number of threads in secure world + * are usually not even close to 255 so we should be safe for now. + */ +#define OPTEE_DEFAULT_MAX_NOTIF_VALUE 255 + typedef void (optee_invoke_fn)(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, @@ -44,10 +51,13 @@ struct optee_call_queue { struct list_head waiters; }; -struct optee_wait_queue { - /* Serializes access to this struct */ - struct mutex mu; +struct optee_notif { + u_int max_key; + struct tee_context *ctx; + /* Serializes access to the elements below in this struct */ + spinlock_t lock; struct list_head db; + u_long *bitmap; }; /** @@ -79,6 +89,7 @@ struct optee_smc { optee_invoke_fn *invoke_fn; void *memremaped_shm; u32 sec_caps; + unsigned int notif_irq; }; /** @@ -129,8 +140,7 @@ struct optee_ops { * @smc: specific to SMC ABI * @ffa: specific to FF-A ABI * @call_queue: queue of threads waiting to call @invoke_fn - * @wait_queue: queue of threads from secure world waiting for a - * secure world sync object + * @notif: notification synchronization struct * @supp: supplicant synchronization struct for RPC to supplicant * @pool: shared memory pool * @rpc_arg_count: If > 0 number of RPC parameters to make room for @@ -147,7 +157,7 @@ struct optee { struct optee_ffa ffa; }; struct optee_call_queue call_queue; - struct optee_wait_queue wait_queue; + struct optee_notif notif; struct optee_supp supp; struct tee_shm_pool *pool; unsigned int rpc_arg_count; @@ -185,8 +195,10 @@ struct optee_call_ctx { size_t num_entries; }; -void optee_wait_queue_init(struct optee_wait_queue *wq); -void optee_wait_queue_exit(struct optee_wait_queue *wq); +int optee_notif_init(struct optee *optee, u_int max_key); +void optee_notif_uninit(struct optee *optee); +int optee_notif_wait(struct optee *optee, u_int key); +int optee_notif_send(struct optee *optee, u_int key); u32 optee_supp_thrd_req(struct tee_context *ctx, u32 func, size_t num_params, struct tee_param *param); diff --git a/drivers/tee/optee/optee_rpc_cmd.h b/drivers/tee/optee/optee_rpc_cmd.h index b8275140cef8..f3f06e0994a7 100644 --- a/drivers/tee/optee/optee_rpc_cmd.h +++ b/drivers/tee/optee/optee_rpc_cmd.h @@ -28,24 +28,27 @@ #define OPTEE_RPC_CMD_GET_TIME 3 /* - * Wait queue primitive, helper for secure world to implement a wait queue. + * Notification from/to secure world. * - * If secure world needs to wait for a secure world mutex it issues a sleep - * request instead of spinning in secure world. Conversely is a wakeup - * request issued when a secure world mutex with a thread waiting thread is - * unlocked. + * If secure world needs to wait for something, for instance a mutex, it + * does a notification wait request instead of spinning in secure world. + * Conversely can a synchronous notification can be sent when a secure + * world mutex with a thread waiting thread is unlocked. * - * Waiting on a key - * [in] value[0].a OPTEE_RPC_WAIT_QUEUE_SLEEP - * [in] value[0].b Wait key + * This interface can also be used to wait for a asynchronous notification + * which instead is sent via a non-secure interrupt. * - * Waking up a key - * [in] value[0].a OPTEE_RPC_WAIT_QUEUE_WAKEUP - * [in] value[0].b Wakeup key + * Waiting on notification + * [in] value[0].a OPTEE_RPC_NOTIFICATION_WAIT + * [in] value[0].b notification value + * + * Sending a synchronous notification + * [in] value[0].a OPTEE_RPC_NOTIFICATION_SEND + * [in] value[0].b notification value */ -#define OPTEE_RPC_CMD_WAIT_QUEUE 4 -#define OPTEE_RPC_WAIT_QUEUE_SLEEP 0 -#define OPTEE_RPC_WAIT_QUEUE_WAKEUP 1 +#define OPTEE_RPC_CMD_NOTIFICATION 4 +#define OPTEE_RPC_NOTIFICATION_WAIT 0 +#define OPTEE_RPC_NOTIFICATION_SEND 1 /* * Suspend execution diff --git a/drivers/tee/optee/optee_smc.h b/drivers/tee/optee/optee_smc.h index 80eb763a8a80..d44a6ae994f8 100644 --- a/drivers/tee/optee/optee_smc.h +++ b/drivers/tee/optee/optee_smc.h @@ -107,6 +107,12 @@ struct optee_smc_call_get_os_revision_result { /* * Call with struct optee_msg_arg as argument * + * When calling this function normal world has a few responsibilities: + * 1. It must be able to handle eventual RPCs + * 2. Non-secure interrupts should not be masked + * 3. If asynchronous notifications has been negotiated successfully, then + * asynchronous notifications should be unmasked during this call. + * * Call register usage: * a0 SMC Function ID, OPTEE_SMC*CALL_WITH_ARG * a1 Upper 32 bits of a 64-bit physical pointer to a struct optee_msg_arg @@ -195,7 +201,8 @@ struct optee_smc_get_shm_config_result { * Normal return register usage: * a0 OPTEE_SMC_RETURN_OK * a1 bitfield of secure world capabilities OPTEE_SMC_SEC_CAP_* - * a2-7 Preserved + * a2 The maximum secure world notification number + * a3-7 Preserved * * Error return register usage: * a0 OPTEE_SMC_RETURN_ENOTAVAIL, can't use the capabilities from normal world @@ -218,6 +225,8 @@ struct optee_smc_get_shm_config_result { #define OPTEE_SMC_SEC_CAP_VIRTUALIZATION BIT(3) /* Secure world supports Shared Memory with a NULL reference */ #define OPTEE_SMC_SEC_CAP_MEMREF_NULL BIT(4) +/* Secure world supports asynchronous notification of normal world */ +#define OPTEE_SMC_SEC_CAP_ASYNC_NOTIF BIT(5) #define OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES 9 #define OPTEE_SMC_EXCHANGE_CAPABILITIES \ @@ -226,8 +235,8 @@ struct optee_smc_get_shm_config_result { struct optee_smc_exchange_capabilities_result { unsigned long status; unsigned long capabilities; + unsigned long max_notif_value; unsigned long reserved0; - unsigned long reserved1; }; /* @@ -320,6 +329,68 @@ struct optee_smc_disable_shm_cache_result { OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_GET_THREAD_COUNT) /* + * Inform OP-TEE that normal world is able to receive asynchronous + * notifications. + * + * Call requests usage: + * a0 SMC Function ID, OPTEE_SMC_ENABLE_ASYNC_NOTIF + * a1-6 Not used + * a7 Hypervisor Client ID register + * + * Normal return register usage: + * a0 OPTEE_SMC_RETURN_OK + * a1-7 Preserved + * + * Not supported return register usage: + * a0 OPTEE_SMC_RETURN_ENOTAVAIL + * a1-7 Preserved + */ +#define OPTEE_SMC_FUNCID_ENABLE_ASYNC_NOTIF 16 +#define OPTEE_SMC_ENABLE_ASYNC_NOTIF \ + OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_ENABLE_ASYNC_NOTIF) + +/* + * Retrieve a value of notifications pending since the last call of this + * function. + * + * OP-TEE keeps a record of all posted values. When an interrupt is + * received which indicates that there are posted values this function + * should be called until all pended values have been retrieved. When a + * value is retrieved, it's cleared from the record in secure world. + * + * Call requests usage: + * a0 SMC Function ID, OPTEE_SMC_GET_ASYNC_NOTIF_VALUE + * a1-6 Not used + * a7 Hypervisor Client ID register + * + * Normal return register usage: + * a0 OPTEE_SMC_RETURN_OK + * a1 value + * a2 Bit[0]: OPTEE_SMC_ASYNC_NOTIF_VALUE_VALID if the value in a1 is + * valid, else 0 if no values where pending + * a2 Bit[1]: OPTEE_SMC_ASYNC_NOTIF_VALUE_PENDING if another value is + * pending, else 0. + * Bit[31:2]: MBZ + * a3-7 Preserved + * + * Not supported return register usage: + * a0 OPTEE_SMC_RETURN_ENOTAVAIL + * a1-7 Preserved + */ +#define OPTEE_SMC_ASYNC_NOTIF_VALUE_VALID BIT(0) +#define OPTEE_SMC_ASYNC_NOTIF_VALUE_PENDING BIT(1) + +/* + * Notification that OP-TEE expects a yielding call to do some bottom half + * work in a driver. + */ +#define OPTEE_SMC_ASYNC_NOTIF_VALUE_DO_BOTTOM_HALF 0 + +#define OPTEE_SMC_FUNCID_GET_ASYNC_NOTIF_VALUE 17 +#define OPTEE_SMC_GET_ASYNC_NOTIF_VALUE \ + OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_GET_ASYNC_NOTIF_VALUE) + +/* * Resume from RPC (for example after processing a foreign interrupt) * * Call register usage: diff --git a/drivers/tee/optee/rpc.c b/drivers/tee/optee/rpc.c index cd642e340eaf..e69bc6380683 100644 --- a/drivers/tee/optee/rpc.c +++ b/drivers/tee/optee/rpc.c @@ -12,23 +12,6 @@ #include "optee_private.h" #include "optee_rpc_cmd.h" -struct wq_entry { - struct list_head link; - struct completion c; - u32 key; -}; - -void optee_wait_queue_init(struct optee_wait_queue *priv) -{ - mutex_init(&priv->mu); - INIT_LIST_HEAD(&priv->db); -} - -void optee_wait_queue_exit(struct optee_wait_queue *priv) -{ - mutex_destroy(&priv->mu); -} - static void handle_rpc_func_cmd_get_time(struct optee_msg_arg *arg) { struct timespec64 ts; @@ -144,48 +127,6 @@ static void handle_rpc_func_cmd_i2c_transfer(struct tee_context *ctx, } #endif -static struct wq_entry *wq_entry_get(struct optee_wait_queue *wq, u32 key) -{ - struct wq_entry *w; - - mutex_lock(&wq->mu); - - list_for_each_entry(w, &wq->db, link) - if (w->key == key) - goto out; - - w = kmalloc(sizeof(*w), GFP_KERNEL); - if (w) { - init_completion(&w->c); - w->key = key; - list_add_tail(&w->link, &wq->db); - } -out: - mutex_unlock(&wq->mu); - return w; -} - -static void wq_sleep(struct optee_wait_queue *wq, u32 key) -{ - struct wq_entry *w = wq_entry_get(wq, key); - - if (w) { - wait_for_completion(&w->c); - mutex_lock(&wq->mu); - list_del(&w->link); - mutex_unlock(&wq->mu); - kfree(w); - } -} - -static void wq_wakeup(struct optee_wait_queue *wq, u32 key) -{ - struct wq_entry *w = wq_entry_get(wq, key); - - if (w) - complete(&w->c); -} - static void handle_rpc_func_cmd_wq(struct optee *optee, struct optee_msg_arg *arg) { @@ -197,11 +138,13 @@ static void handle_rpc_func_cmd_wq(struct optee *optee, goto bad; switch (arg->params[0].u.value.a) { - case OPTEE_RPC_WAIT_QUEUE_SLEEP: - wq_sleep(&optee->wait_queue, arg->params[0].u.value.b); + case OPTEE_RPC_NOTIFICATION_WAIT: + if (optee_notif_wait(optee, arg->params[0].u.value.b)) + goto bad; break; - case OPTEE_RPC_WAIT_QUEUE_WAKEUP: - wq_wakeup(&optee->wait_queue, arg->params[0].u.value.b); + case OPTEE_RPC_NOTIFICATION_SEND: + if (optee_notif_send(optee, arg->params[0].u.value.b)) + goto bad; break; default: goto bad; @@ -319,7 +262,7 @@ void optee_rpc_cmd(struct tee_context *ctx, struct optee *optee, case OPTEE_RPC_CMD_GET_TIME: handle_rpc_func_cmd_get_time(arg); break; - case OPTEE_RPC_CMD_WAIT_QUEUE: + case OPTEE_RPC_CMD_NOTIFICATION: handle_rpc_func_cmd_wq(optee, arg); break; case OPTEE_RPC_CMD_SUSPEND: diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c index cf2e3293567d..449d6a72d289 100644 --- a/drivers/tee/optee/smc_abi.c +++ b/drivers/tee/optee/smc_abi.c @@ -8,13 +8,16 @@ #include <linux/arm-smccc.h> #include <linux/errno.h> +#include <linux/interrupt.h> #include <linux/io.h> -#include <linux/sched.h> +#include <linux/irqdomain.h> #include <linux/mm.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_irq.h> #include <linux/of_platform.h> #include <linux/platform_device.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/string.h> #include <linux/tee_drv.h> @@ -35,7 +38,8 @@ * 2. Low level support functions to register shared memory in secure world * 3. Dynamic shared memory pool based on alloc_pages() * 4. Do a normal scheduled call into secure world - * 5. Driver initialization. + * 5. Asynchronous notification + * 6. Driver initialization. */ #define OPTEE_SHM_NUM_PRIV_PAGES CONFIG_OPTEE_SHM_NUM_PRIV_PAGES @@ -877,10 +881,137 @@ static int optee_smc_do_call_with_arg(struct tee_context *ctx, return rc; } +static int simple_call_with_arg(struct tee_context *ctx, u32 cmd) +{ + struct optee_msg_arg *msg_arg; + struct tee_shm *shm; + + shm = optee_get_msg_arg(ctx, 0, &msg_arg); + if (IS_ERR(shm)) + return PTR_ERR(shm); + + msg_arg->cmd = cmd; + optee_smc_do_call_with_arg(ctx, shm); + + tee_shm_free(shm); + return 0; +} + +static int optee_smc_do_bottom_half(struct tee_context *ctx) +{ + return simple_call_with_arg(ctx, OPTEE_MSG_CMD_DO_BOTTOM_HALF); +} + +static int optee_smc_stop_async_notif(struct tee_context *ctx) +{ + return simple_call_with_arg(ctx, OPTEE_MSG_CMD_STOP_ASYNC_NOTIF); +} + /* - * 5. Driver initialization + * 5. Asynchronous notification + */ + +static u32 get_async_notif_value(optee_invoke_fn *invoke_fn, bool *value_valid, + bool *value_pending) +{ + struct arm_smccc_res res; + + invoke_fn(OPTEE_SMC_GET_ASYNC_NOTIF_VALUE, 0, 0, 0, 0, 0, 0, 0, &res); + + if (res.a0) + return 0; + *value_valid = (res.a2 & OPTEE_SMC_ASYNC_NOTIF_VALUE_VALID); + *value_pending = (res.a2 & OPTEE_SMC_ASYNC_NOTIF_VALUE_PENDING); + return res.a1; +} + +static irqreturn_t notif_irq_handler(int irq, void *dev_id) +{ + struct optee *optee = dev_id; + bool do_bottom_half = false; + bool value_valid; + bool value_pending; + u32 value; + + do { + value = get_async_notif_value(optee->smc.invoke_fn, + &value_valid, &value_pending); + if (!value_valid) + break; + + if (value == OPTEE_SMC_ASYNC_NOTIF_VALUE_DO_BOTTOM_HALF) + do_bottom_half = true; + else + optee_notif_send(optee, value); + } while (value_pending); + + if (do_bottom_half) + return IRQ_WAKE_THREAD; + return IRQ_HANDLED; +} + +static irqreturn_t notif_irq_thread_fn(int irq, void *dev_id) +{ + struct optee *optee = dev_id; + + optee_smc_do_bottom_half(optee->notif.ctx); + + return IRQ_HANDLED; +} + +static int optee_smc_notif_init_irq(struct optee *optee, u_int irq) +{ + struct tee_context *ctx; + int rc; + + ctx = teedev_open(optee->teedev); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); + + optee->notif.ctx = ctx; + rc = request_threaded_irq(irq, notif_irq_handler, + notif_irq_thread_fn, + 0, "optee_notification", optee); + if (rc) + goto err_close_ctx; + + optee->smc.notif_irq = irq; + + return 0; + +err_close_ctx: + teedev_close_context(optee->notif.ctx); + optee->notif.ctx = NULL; + + return rc; +} + +static void optee_smc_notif_uninit_irq(struct optee *optee) +{ + if (optee->notif.ctx) { + optee_smc_stop_async_notif(optee->notif.ctx); + if (optee->smc.notif_irq) { + free_irq(optee->smc.notif_irq, optee); + irq_dispose_mapping(optee->smc.notif_irq); + } + + /* + * The thread normally working with optee->notif.ctx was + * stopped with free_irq() above. + * + * Note we're not using teedev_close_context() or + * tee_client_close_context() since we have already called + * tee_device_put() while initializing to avoid a circular + * reference counting. + */ + teedev_close_context(optee->notif.ctx); + } +} + +/* + * 6. Driver initialization * - * During driver inititialization is secure world probed to find out which + * During driver initialization is secure world probed to find out which * features it supports so the driver can be initialized with a matching * configuration. This involves for instance support for dynamic shared * memory instead of a static memory carvout. @@ -952,6 +1083,17 @@ static const struct optee_ops optee_ops = { .from_msg_param = optee_from_msg_param, }; +static int enable_async_notif(optee_invoke_fn *invoke_fn) +{ + struct arm_smccc_res res; + + invoke_fn(OPTEE_SMC_ENABLE_ASYNC_NOTIF, 0, 0, 0, 0, 0, 0, 0, &res); + + if (res.a0) + return -EINVAL; + return 0; +} + static bool optee_msg_api_uid_is_optee_api(optee_invoke_fn *invoke_fn) { struct arm_smccc_res res; @@ -1001,7 +1143,7 @@ static bool optee_msg_api_revision_is_compatible(optee_invoke_fn *invoke_fn) } static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn, - u32 *sec_caps) + u32 *sec_caps, u32 *max_notif_value) { union { struct arm_smccc_res smccc; @@ -1024,6 +1166,11 @@ static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn, return false; *sec_caps = res.result.capabilities; + if (*sec_caps & OPTEE_SMC_SEC_CAP_ASYNC_NOTIF) + *max_notif_value = res.result.max_notif_value; + else + *max_notif_value = OPTEE_DEFAULT_MAX_NOTIF_VALUE; + return true; } @@ -1188,6 +1335,8 @@ static int optee_smc_remove(struct platform_device *pdev) */ optee_disable_shm_cache(optee); + optee_smc_notif_uninit_irq(optee); + optee_remove_common(optee); if (optee->smc.memremaped_shm) @@ -1217,6 +1366,7 @@ static int optee_probe(struct platform_device *pdev) struct optee *optee = NULL; void *memremaped_shm = NULL; struct tee_device *teedev; + u32 max_notif_value; u32 sec_caps; int rc; @@ -1236,7 +1386,8 @@ static int optee_probe(struct platform_device *pdev) return -EINVAL; } - if (!optee_msg_exchange_capabilities(invoke_fn, &sec_caps)) { + if (!optee_msg_exchange_capabilities(invoke_fn, &sec_caps, + &max_notif_value)) { pr_warn("capabilities mismatch\n"); return -EINVAL; } @@ -1259,7 +1410,7 @@ static int optee_probe(struct platform_device *pdev) optee = kzalloc(sizeof(*optee), GFP_KERNEL); if (!optee) { rc = -ENOMEM; - goto err; + goto err_free_pool; } optee->ops = &optee_ops; @@ -1269,32 +1420,55 @@ static int optee_probe(struct platform_device *pdev) teedev = tee_device_alloc(&optee_clnt_desc, NULL, pool, optee); if (IS_ERR(teedev)) { rc = PTR_ERR(teedev); - goto err; + goto err_free_optee; } optee->teedev = teedev; teedev = tee_device_alloc(&optee_supp_desc, NULL, pool, optee); if (IS_ERR(teedev)) { rc = PTR_ERR(teedev); - goto err; + goto err_unreg_teedev; } optee->supp_teedev = teedev; rc = tee_device_register(optee->teedev); if (rc) - goto err; + goto err_unreg_supp_teedev; rc = tee_device_register(optee->supp_teedev); if (rc) - goto err; + goto err_unreg_supp_teedev; mutex_init(&optee->call_queue.mutex); INIT_LIST_HEAD(&optee->call_queue.waiters); - optee_wait_queue_init(&optee->wait_queue); optee_supp_init(&optee->supp); optee->smc.memremaped_shm = memremaped_shm; optee->pool = pool; + platform_set_drvdata(pdev, optee); + rc = optee_notif_init(optee, max_notif_value); + if (rc) + goto err_supp_uninit; + + if (sec_caps & OPTEE_SMC_SEC_CAP_ASYNC_NOTIF) { + unsigned int irq; + + rc = platform_get_irq(pdev, 0); + if (rc < 0) { + pr_err("platform_get_irq: ret %d\n", rc); + goto err_notif_uninit; + } + irq = rc; + + rc = optee_smc_notif_init_irq(optee, irq); + if (rc) { + irq_dispose_mapping(irq); + goto err_notif_uninit; + } + enable_async_notif(optee->smc.invoke_fn); + pr_info("Asynchronous notifications enabled\n"); + } + /* * Ensure that there are no pre-existing shm objects before enabling * the shm cache so that there's no chance of receiving an invalid @@ -1309,29 +1483,30 @@ static int optee_probe(struct platform_device *pdev) if (optee->smc.sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) pr_info("dynamic shared memory is enabled\n"); - platform_set_drvdata(pdev, optee); - rc = optee_enumerate_devices(PTA_CMD_GET_DEVICES); - if (rc) { - optee_smc_remove(pdev); - return rc; - } + if (rc) + goto err_disable_shm_cache; pr_info("initialized driver\n"); return 0; -err: - if (optee) { - /* - * tee_device_unregister() is safe to call even if the - * devices hasn't been registered with - * tee_device_register() yet. - */ - tee_device_unregister(optee->supp_teedev); - tee_device_unregister(optee->teedev); - kfree(optee); - } - if (pool) - tee_shm_pool_free(pool); + +err_disable_shm_cache: + optee_disable_shm_cache(optee); + optee_smc_notif_uninit_irq(optee); + optee_unregister_devices(); +err_notif_uninit: + optee_notif_uninit(optee); +err_supp_uninit: + optee_supp_uninit(&optee->supp); + mutex_destroy(&optee->call_queue.mutex); +err_unreg_supp_teedev: + tee_device_unregister(optee->supp_teedev); +err_unreg_teedev: + tee_device_unregister(optee->teedev); +err_free_optee: + kfree(optee); +err_free_pool: + tee_shm_pool_free(pool); if (memremaped_shm) memunmap(memremaped_shm); return rc; |