OP-TEE uses asynchronous notifications to request bottom-half processing in normal world without waiting for a client call.
Allocate a TEE-to-REE RPMI signal and pass its ID to OP-TEE when enabling asynchronous notifications. Queue bottom-half processing on a private workqueue.
Initialize notification delivery before publishing the TEE devices. Allow probe to continue if setup fails, keeping synchronous RPC notifications available.
On cleanup, request that OP-TEE stop asynchronous notifications, relinquish the signal and drain the bottom-half workqueue. Transport relinquishment does not synchronize an already-selected callback.
Signed-off-by: Amirreza Zarrabi amirreza.zarrabi@oss.qualcomm.com --- drivers/tee/optee/optee_private.h | 6 +++ drivers/tee/optee/rpmi_abi.c | 92 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+)
diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h index fcb291a43b7b..3bc65ffb6f99 100644 --- a/drivers/tee/optee/optee_private.h +++ b/drivers/tee/optee/optee_private.h @@ -207,11 +207,17 @@ struct optee_ffa { * @rdev: owning RPMI service device * @shm_map: lookup by the host-endian parcel ID and nonce pair * @nonce_ida: nonzero SHM nonces, reusable after successful parcel reclamation + * @signal: allocated TEE-to-REE doorbell, independent of logical keys + * @notif_wq: private bottom-half workqueue, NULL when inactive + * @notif_work: processes bottom halves requested by the RPMI signal */ struct optee_rpmi { struct rpmi_tee_device *rdev; struct optee_shm_handle_map shm_map; struct ida nonce_ida; + u32 signal; + struct workqueue_struct *notif_wq; + struct work_struct notif_work; }; #endif
diff --git a/drivers/tee/optee/rpmi_abi.c b/drivers/tee/optee/rpmi_abi.c index 0ed2de96758e..6434767a2249 100644 --- a/drivers/tee/optee/rpmi_abi.c +++ b/drivers/tee/optee/rpmi_abi.c @@ -580,6 +580,92 @@ static int optee_rpmi_do_call_with_arg(struct tee_context *ctx, return optee_rpmi_yielding_call(ctx, &req, rpc_arg, system_thread); }
+/* Yielding bottom halves run here, not in the transport retrieval worker. */ +static void optee_rpmi_notif_work(struct work_struct *work) +{ + struct optee_rpmi *rpmi = container_of(work, struct optee_rpmi, + notif_work); + struct optee *optee = container_of(rpmi, struct optee, rpmi); + + optee_do_bottom_half(optee->ctx); +} + +static void optee_rpmi_notif_callback(struct rpmi_tee_device *rdev, u32 signal, + void *cb_data) +{ + struct optee *optee = cb_data; + + queue_work(optee->rpmi.notif_wq, &optee->rpmi.notif_work); +} + +/* Relinquish is not a barrier for an already-selected transport callback. */ +static void optee_rpmi_async_notif_uninit(struct optee *optee) +{ + struct optee_rpmi *rpmi = &optee->rpmi; + struct rpmi_tee_device *rdev = rpmi->rdev; + int rc; + + if (!rpmi->notif_wq) + return; + + rc = optee_stop_async_notif(optee->ctx); + if (rc) + dev_warn(&rdev->dev, "stop notifications failed: %d\n", rc); + + rc = rdev->ops->notifier_ops->notify_relinquish(rdev, rpmi->signal); + if (rc && rc != -EOPNOTSUPP) + dev_warn(&rdev->dev, + "relinquish notification failed: %d\n", rc); + + destroy_workqueue(rpmi->notif_wq); + + rpmi->notif_wq = NULL; +} + +/* Enable OP-TEE's bottom-half doorbell using the reserved RPMI signal. */ +static int optee_rpmi_enable_async_notif(struct optee *optee) +{ + struct optee_rpmi_enable_notif_req req = { + .op = cpu_to_le32(OPTEE_RPMI_ENABLE_ASYNC_NOTIF), + .signal_id = cpu_to_le32(optee->rpmi.signal), + }; + struct optee_rpmi_status_resp resp; + + return optee_rpmi_call(optee, &req, sizeof(req), &resp, sizeof(resp)); +} + +static int optee_rpmi_async_notif_init(struct optee *optee) +{ + struct optee_rpmi *rpmi = &optee->rpmi; + struct rpmi_tee_device *rdev = rpmi->rdev; + struct workqueue_struct *wq; + int rc; + + INIT_WORK(&rpmi->notif_work, optee_rpmi_notif_work); + wq = alloc_workqueue("optee_rpmi_notif", WQ_UNBOUND, 1); + if (!wq) + return -ENOMEM; + + rpmi->notif_wq = wq; + + rc = rdev->ops->notifier_ops->notify_request(rdev, + optee_rpmi_notif_callback, + optee, &rpmi->signal); + if (rc) { + destroy_workqueue(rpmi->notif_wq); + /* Checked in optee_rpmi_async_notif_uninit(). */ + rpmi->notif_wq = NULL; + + return rc; + } + + rc = optee_rpmi_enable_async_notif(optee); + if (rc) + optee_rpmi_async_notif_uninit(optee); + + return rc; +} + /* Query and store the trusted OS revision. */ static int optee_rpmi_get_os_version(struct optee *optee) { @@ -720,6 +806,7 @@ static void optee_rpmi_remove(struct rpmi_tee_device *rdev) { struct optee *optee = rpmi_tee_dev_get_drvdata(rdev);
+ optee_rpmi_async_notif_uninit(optee); optee_remove_common(optee); optee_shm_handle_map_uninit(&optee->rpmi.shm_map); ida_destroy(&optee->rpmi.nonce_ida); @@ -807,6 +894,11 @@ static int optee_rpmi_probe(struct rpmi_tee_device *rdev)
rpmi_tee_dev_set_drvdata(rdev, optee);
+ rc = optee_rpmi_async_notif_init(optee); + if (rc) + dev_warn(&rdev->dev, "async notifications unavailable: %d\n", + rc); + if (optee->in_kernel_rpmb_routing) blocking_notifier_chain_register(&optee_rpmb_intf_added, &optee->rpmb_intf);