OP-TEE: asynchronous notifications with FF-A

Add support for asynchronous notifications in the OP-TEE FF-A driver. This
 is the FF-A counterpart to the asynchronous notifications already
 available in the OP-TEE SMC ABI.
 -----BEGIN PGP SIGNATURE-----
 
 iQJOBAABCgA4FiEEFV+gSSXZJY9ZyuB5LinzTIcAHJcFAmV25p0aHGplbnMud2lr
 bGFuZGVyQGxpbmFyby5vcmcACgkQLinzTIcAHJefFRAA0BUWmcfYMQdvcy+4FIJw
 9X5RwnEscVXN3UxFCmL06aZXcrsz+O9qIP2ZGksAadI7EhN+V/RO+gBF1yXO/jWA
 GLv5LTDB1fYnKDYlEFkAqya8hVlsBS6DiNEwWm50lTyQlrYIMofa+sRuGsIFz2U3
 QZnDK4ZWc5DTrBMp7Y3untdmirp8qLmRcO0QI6q3tpFgqZjj6DB8no4AAMlRdvB+
 MLhC0Fz9jZffpSDxoE67fHMKDCS2mnZImleYLqkfDCWkZh1GCDgCxrd7PRw+p5eF
 Hy+XtKI2Le83OPXTKjY/rzUhq2r7HcYiGlwqwJe68+Owz1HZReTT3WTG7ocuMyHq
 YkSxbfzD7ug7k0YrUN/wUmtDMp87t4Q8wmx7/goH9ZSZ66SKJJvNVIN8uPWMc/eD
 85MWFik5/EIcZYWgPg291Cay82mAqFU2RTJmA559pLylSx37/I6mwAXIKoQ/uI+P
 q9+3XM2pnSBmI2qQt4s+49rCLcwYeDvc7aEo2NUxauC5tU5yhJXK7WR/qiPqZBIW
 00+BZJdF1PUpjEloajfiRyFaS1X6M0zOzC5KABddmJYj+DVQRs/9wEZ+ROxpqu1G
 nekOPODzN1vJVEKGuWOHQaieGYdHXariP4jckabYMFj6Blt28XPcebqFJjlnhFu/
 lDRrWMfXfhLp+N+Iyf9ayZQ=
 =xdbP
 -----END PGP SIGNATURE-----
gpgsig -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEiK/NIGsWEZVxh/FrYKtH/8kJUicFAmWFaEcACgkQYKtH/8kJ
 Uifwsg//dx7fg3xdy7K0Ec92cWlvg2JTIOgTW5+qDrrAO8if+zZeLhAY/xiTtqKH
 gTYbQ1GEgqVK7GEGwxXQEZt2wt22Vt7evRC2G/68I1PI6Kzhfr/DyU/9ITX0IfiJ
 coCSTcD3q9sOgvY5NPiqTNXyY6MCYnpHXCYciPC96NccWyN5JaL14amRN3CVWntN
 y9imCzVEIEeVkt3rPuckrmYZldZLtsUC30XHXjRPdkK2dC+6+vmpk93tI/fAGh01
 DEpezN3X6ot+sZnRhY646gR2D6hkxL250A4Fd2REXdLojZJOyB3vye8wCBOSqpjN
 PR6x0in2FkkIqzTGVoeOjzkIfvLPHAg5hImTjaXTl1380+hp+LWetGJsQT+WF4I5
 f3xadidwEf/L8im1EHNw6DTHZb/bUWDbAob7Xk7qARi/r1tHWmFLbYrJ27kUjTVN
 zMMirrA5B7Ucot9nmebGZfx1G6oVoeJdyr3+AuOG4uI1+xaJgJi/w8Q1siGZZGju
 yTp6oO8VAf47rNhGNLIXOIHMG6cSUwFvuJmOmJs/HLvboCFeOR3UEPx/btz4Z6kd
 WaAHWJAvS0iVHd5GjzkWJo28wukupS4QMxQVbLMBmZQ+8gV7u5UZNnCWiXjROpU+
 EkTNr0qHUCKyzAdZCszT6KUpYDP9vqOP+EKFOSJKgchnL9wPhM0=
 =UC68
 -----END PGP SIGNATURE-----

Merge tag 'ffa-notif-for-v6.8' of https://git.linaro.org/people/jens.wiklander/linux-tee into soc/drivers

OP-TEE: asynchronous notifications with FF-A

Add support for asynchronous notifications in the OP-TEE FF-A driver. This
is the FF-A counterpart to the asynchronous notifications already
available in the OP-TEE SMC ABI.

* tag 'ffa-notif-for-v6.8' of https://git.linaro.org/people/jens.wiklander/linux-tee:
  optee: ffa_abi: add asynchronous notifications
  optee: provide optee_do_bottom_half() as a common function

Link: https://lore.kernel.org/r/20231211105249.GA587253@rayden
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
Arnd Bergmann 2023-12-22 10:43:18 +00:00
commit 95c1e57a38
5 changed files with 155 additions and 42 deletions

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2015-2021, Linaro Limited
* Copyright (c) 2015-2021, 2023 Linaro Limited
*/
#include <linux/device.h>
#include <linux/err.h>
@ -640,3 +640,32 @@ int optee_check_mem_type(unsigned long start, size_t num_pages)
return rc;
}
static int simple_call_with_arg(struct tee_context *ctx, u32 cmd)
{
struct optee *optee = tee_get_drvdata(ctx->teedev);
struct optee_shm_arg_entry *entry;
struct optee_msg_arg *msg_arg;
struct tee_shm *shm;
u_int offs;
msg_arg = optee_get_msg_arg(ctx, 0, &entry, &shm, &offs);
if (IS_ERR(msg_arg))
return PTR_ERR(msg_arg);
msg_arg->cmd = cmd;
optee->ops->do_call_with_arg(ctx, shm, offs, false);
optee_free_msg_arg(ctx, entry, offs);
return 0;
}
int optee_do_bottom_half(struct tee_context *ctx)
{
return simple_call_with_arg(ctx, OPTEE_MSG_CMD_DO_BOTTOM_HALF);
}
int optee_stop_async_notif(struct tee_context *ctx)
{
return simple_call_with_arg(ctx, OPTEE_MSG_CMD_STOP_ASYNC_NOTIF);
}

View File

@ -1,6 +1,6 @@
// 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
@ -695,7 +695,8 @@ static bool optee_ffa_api_is_compatbile(struct ffa_device *ffa_dev,
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;
@ -712,10 +713,39 @@ static bool optee_ffa_exchange_caps(struct ffa_device *ffa_dev,
*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)
{
@ -778,7 +808,11 @@ static const struct optee_ops optee_ffa_ops = {
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);
@ -787,9 +821,51 @@ static void optee_ffa_remove(struct ffa_device *ffa_dev)
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;
@ -800,12 +876,13 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
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;
@ -823,6 +900,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
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,
@ -866,6 +944,12 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
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)
@ -876,6 +960,9 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
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);

View File

@ -1,6 +1,6 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2019-2021, Linaro Limited
* Copyright (c) 2019-2021, 2023 Linaro Limited
*/
/*
@ -73,7 +73,7 @@
*
* 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
@ -82,14 +82,16 @@
* 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)
@ -108,6 +110,24 @@
*/
#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.

View File

@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2015-2021, Linaro Limited
* Copyright (c) 2015-2021, 2023 Linaro Limited
*/
#ifndef OPTEE_PRIVATE_H
@ -147,12 +147,14 @@ struct optee_smc {
* struct optee_ffa_data - FFA communication struct
* @ffa_dev FFA device, contains the destination id, the id of
* OP-TEE in secure world
* @ffa_ops FFA operations
* @bottom_half_value Notification ID used for bottom half signalling or
* U32_MAX if unused
* @mutex Serializes access to @global_ids
* @global_ids FF-A shared memory global handle translation
*/
struct optee_ffa {
struct ffa_device *ffa_dev;
u32 bottom_half_value;
/* Serializes access to @global_ids */
struct mutex mutex;
struct rhashtable global_ids;
@ -346,6 +348,9 @@ void optee_rpc_cmd_free_suppl(struct tee_context *ctx, struct tee_shm *shm);
void optee_rpc_cmd(struct tee_context *ctx, struct optee *optee,
struct optee_msg_arg *arg);
int optee_do_bottom_half(struct tee_context *ctx);
int optee_stop_async_notif(struct tee_context *ctx);
/*
* Small helpers
*/

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2015-2021, Linaro Limited
* Copyright (c) 2015-2021, 2023 Linaro Limited
* Copyright (c) 2016, EPAM Systems
*/
@ -967,34 +967,6 @@ 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_shm_arg_entry *entry;
struct optee_msg_arg *msg_arg;
struct tee_shm *shm;
u_int offs;
msg_arg = optee_get_msg_arg(ctx, 0, &entry, &shm, &offs);
if (IS_ERR(msg_arg))
return PTR_ERR(msg_arg);
msg_arg->cmd = cmd;
optee_smc_do_call_with_arg(ctx, shm, offs, false);
optee_free_msg_arg(ctx, entry, offs);
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. Asynchronous notification
*/
@ -1050,7 +1022,7 @@ static irqreturn_t notif_irq_thread_fn(int irq, void *dev_id)
{
struct optee *optee = dev_id;
optee_smc_do_bottom_half(optee->ctx);
optee_do_bottom_half(optee->ctx);
return IRQ_HANDLED;
}
@ -1088,7 +1060,7 @@ static void notif_pcpu_irq_work_fn(struct work_struct *work)
notif_pcpu_work);
struct optee *optee = container_of(optee_smc, struct optee, smc);
optee_smc_do_bottom_half(optee->ctx);
optee_do_bottom_half(optee->ctx);
}
static int init_pcpu_irq(struct optee *optee, u_int irq)
@ -1160,7 +1132,7 @@ static void uninit_pcpu_irq(struct optee *optee)
static void optee_smc_notif_uninit_irq(struct optee *optee)
{
if (optee->smc.sec_caps & OPTEE_SMC_SEC_CAP_ASYNC_NOTIF) {
optee_smc_stop_async_notif(optee->ctx);
optee_stop_async_notif(optee->ctx);
if (optee->smc.notif_irq) {
if (irq_is_percpu_devid(optee->smc.notif_irq))
uninit_pcpu_irq(optee);