// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2021, Linaro Limited
+ * Copyright (c) 2021, 2023 Linaro Limited
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
static bool optee_ffa_exchange_caps(struct ffa_device *ffa_dev,
const struct ffa_ops *ops,
u32 *sec_caps,
- unsigned int *rpc_param_count)
+ unsigned int *rpc_param_count,
+ unsigned int *max_notif_value)
{
struct ffa_send_direct_data data = { OPTEE_FFA_EXCHANGE_CAPABILITIES };
int rc;
*rpc_param_count = (u8)data.data1;
*sec_caps = data.data2;
+ if (data.data3)
+ *max_notif_value = data.data3;
+ else
+ *max_notif_value = OPTEE_DEFAULT_MAX_NOTIF_VALUE;
return true;
}
+static void notif_callback(int notify_id, void *cb_data)
+{
+ struct optee *optee = cb_data;
+
+ if (notify_id == optee->ffa.bottom_half_value)
+ optee_do_bottom_half(optee->ctx);
+ else
+ optee_notif_send(optee, notify_id);
+}
+
+static int enable_async_notif(struct optee *optee)
+{
+ struct ffa_device *ffa_dev = optee->ffa.ffa_dev;
+ struct ffa_send_direct_data data = {
+ .data0 = OPTEE_FFA_ENABLE_ASYNC_NOTIF,
+ .data1 = optee->ffa.bottom_half_value,
+ };
+ int rc;
+
+ rc = ffa_dev->ops->msg_ops->sync_send_receive(ffa_dev, &data);
+ if (rc)
+ return rc;
+ return data.data0;
+}
+
static void optee_ffa_get_version(struct tee_device *teedev,
struct tee_ioctl_version_data *vers)
{
static void optee_ffa_remove(struct ffa_device *ffa_dev)
{
struct optee *optee = ffa_dev_get_drvdata(ffa_dev);
+ u32 bottom_half_id = optee->ffa.bottom_half_value;
+ if (bottom_half_id != U32_MAX)
+ ffa_dev->ops->notifier_ops->notify_relinquish(ffa_dev,
+ bottom_half_id);
optee_remove_common(optee);
mutex_destroy(&optee->ffa.mutex);
kfree(optee);
}
+static int optee_ffa_async_notif_init(struct ffa_device *ffa_dev,
+ struct optee *optee)
+{
+ bool is_per_vcpu = false;
+ u32 notif_id = 0;
+ int rc;
+
+ while (true) {
+ rc = ffa_dev->ops->notifier_ops->notify_request(ffa_dev,
+ is_per_vcpu,
+ notif_callback,
+ optee,
+ notif_id);
+ if (!rc)
+ break;
+ /*
+ * -EACCES means that the notification ID was
+ * already bound, try the next one as long as we
+ * haven't reached the max. Any other error is a
+ * permanent error, so skip asynchronous
+ * notifications in that case.
+ */
+ if (rc != -EACCES)
+ return rc;
+ notif_id++;
+ if (notif_id >= OPTEE_FFA_MAX_ASYNC_NOTIF_VALUE)
+ return rc;
+ }
+ optee->ffa.bottom_half_value = notif_id;
+
+ rc = enable_async_notif(optee);
+ if (rc < 0) {
+ ffa_dev->ops->notifier_ops->notify_relinquish(ffa_dev,
+ notif_id);
+ optee->ffa.bottom_half_value = U32_MAX;
+ }
+
+ return rc;
+}
+
static int optee_ffa_probe(struct ffa_device *ffa_dev)
{
+ const struct ffa_notifier_ops *notif_ops;
const struct ffa_ops *ffa_ops;
+ unsigned int max_notif_value;
unsigned int rpc_param_count;
struct tee_shm_pool *pool;
struct tee_device *teedev;
int rc;
ffa_ops = ffa_dev->ops;
+ notif_ops = ffa_ops->notifier_ops;
if (!optee_ffa_api_is_compatbile(ffa_dev, ffa_ops))
return -EINVAL;
if (!optee_ffa_exchange_caps(ffa_dev, ffa_ops, &sec_caps,
- &rpc_param_count))
+ &rpc_param_count, &max_notif_value))
return -EINVAL;
if (sec_caps & OPTEE_FFA_SEC_CAP_ARG_OFFSET)
arg_cache_flags |= OPTEE_SHM_ARG_SHARED;
optee->ops = &optee_ffa_ops;
optee->ffa.ffa_dev = ffa_dev;
+ optee->ffa.bottom_half_value = U32_MAX;
optee->rpc_param_count = rpc_param_count;
teedev = tee_device_alloc(&optee_ffa_clnt_desc, NULL, optee->pool,
rc = optee_notif_init(optee, OPTEE_DEFAULT_MAX_NOTIF_VALUE);
if (rc)
goto err_close_ctx;
+ if (sec_caps & OPTEE_FFA_SEC_CAP_ASYNC_NOTIF) {
+ rc = optee_ffa_async_notif_init(ffa_dev, optee);
+ if (rc < 0)
+ pr_err("Failed to initialize async notifications: %d",
+ rc);
+ }
rc = optee_enumerate_devices(PTA_CMD_GET_DEVICES);
if (rc)
err_unregister_devices:
optee_unregister_devices();
+ if (optee->ffa.bottom_half_value != U32_MAX)
+ notif_ops->notify_relinquish(ffa_dev,
+ optee->ffa.bottom_half_value);
optee_notif_uninit(optee);
err_close_ctx:
teedev_close_context(ctx);
/* SPDX-License-Identifier: BSD-2-Clause */
/*
- * Copyright (c) 2019-2021, Linaro Limited
+ * Copyright (c) 2019-2021, 2023 Linaro Limited
*/
/*
*
* Call register usage:
* w3: Service ID, OPTEE_FFA_EXCHANGE_CAPABILITIES
- * w4-w7: Note used (MBZ)
+ * w4-w7: Not used (MBZ)
*
* Return register usage:
* w3: Error code, 0 on success
* OPTEE_FFA_YIELDING_CALL_WITH_ARG.
* Bit[31:8]: Reserved (MBZ)
* w5: Bitfield of secure world capabilities OPTEE_FFA_SEC_CAP_* below,
- * unused bits MBZ.
- * w6-w7: Not used (MBZ)
+ * w6: The maximum secure world notification number
+ * w7: Not used (MBZ)
*/
/*
* Secure world supports giving an offset into the argument shared memory
* object, see also OPTEE_FFA_YIELDING_CALL_WITH_ARG
*/
#define OPTEE_FFA_SEC_CAP_ARG_OFFSET BIT(0)
+/* OP-TEE supports asynchronous notification via FF-A */
+#define OPTEE_FFA_SEC_CAP_ASYNC_NOTIF BIT(1)
#define OPTEE_FFA_EXCHANGE_CAPABILITIES OPTEE_FFA_BLOCKING_CALL(2)
*/
#define OPTEE_FFA_UNREGISTER_SHM OPTEE_FFA_BLOCKING_CALL(3)
+/*
+ * Inform OP-TEE that the normal world is able to receive asynchronous
+ * notifications.
+ *
+ * Call register usage:
+ * w3: Service ID, OPTEE_FFA_ENABLE_ASYNC_NOTIF
+ * w4: Notification value to request bottom half processing, should be
+ * less than OPTEE_FFA_MAX_ASYNC_NOTIF_VALUE.
+ * w5-w7: Not used (MBZ)
+ *
+ * Return register usage:
+ * w3: Error code, 0 on success
+ * w4-w7: Note used (MBZ)
+ */
+#define OPTEE_FFA_ENABLE_ASYNC_NOTIF OPTEE_FFA_BLOCKING_CALL(5)
+
+#define OPTEE_FFA_MAX_ASYNC_NOTIF_VALUE 64
+
/*
* Call with struct optee_msg_arg as argument in the supplied shared memory
* with a zero internal offset and normal cached memory attributes.