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 | 87 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+)
diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h index 07c27e322a71..e5c6381bb872 100644 --- a/drivers/tee/optee/optee_private.h +++ b/drivers/tee/optee/optee_private.h @@ -190,6 +190,9 @@ struct rpmi_tee_device; * @shm_rht: lookup by the host-endian parcel ID and nonce pair * @sec_caps: negotiated optional OPTEE_RPMI_CAP_* features * @notification_count: negotiated nonzero number of logical notification keys + * @signal: allocated TEE-to-REE doorbell, independent of logical keys + * @notif_wq: private bottom-half workqueue, NULL when notifications are inactive + * @notif_work: processes bottom halves requested by the RPMI signal * * Callers keep their tee_shm alive while using its registration. Lookup * returns a raw pointer; the mutex does not protect its lifetime after @@ -203,6 +206,9 @@ struct optee_rpmi { struct rhashtable shm_rht; u32 sec_caps; u32 notification_count; + 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 db541f3de425..ffe44d81e857 100644 --- a/drivers/tee/optee/rpmi_abi.c +++ b/drivers/tee/optee/rpmi_abi.c @@ -685,6 +685,88 @@ 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 ret; + + if (!rpmi->notif_wq) + return; + + ret = optee_stop_async_notif(optee->ctx); + if (ret) + dev_warn(&rdev->dev, "stop notifications failed: %d\n", ret); + + ret = rdev->ops->notifier_ops->notify_relinquish(rdev, rpmi->signal); + if (ret && ret != -EOPNOTSUPP) + dev_warn(&rdev->dev, + "relinquish notification failed: %d\n", ret); + + 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; + int ret; + + INIT_WORK(&rpmi->notif_work, optee_rpmi_notif_work); + rpmi->notif_wq = alloc_workqueue("optee_rpmi_notif", WQ_UNBOUND, 1); + if (!rpmi->notif_wq) + return -ENOMEM; + + ret = rdev->ops->notifier_ops->notify_request(rdev, + optee_rpmi_notif_callback, + optee, &rpmi->signal); + if (ret) { + destroy_workqueue(rpmi->notif_wq); + /* Checked in optee_rpmi_async_notif_uninit(). */ + rpmi->notif_wq = NULL; + + return ret; + } + + ret = optee_rpmi_enable_async_notif(optee); + if (ret) + optee_rpmi_async_notif_uninit(optee); + + return ret; +} + /* Query and store the trusted OS revision. */ static int optee_rpmi_get_os_version(struct optee *optee) { @@ -823,6 +905,7 @@ static void optee_rpmi_remove(struct rpmi_tee_device *rdev) { struct optee *optee = dev_get_drvdata(&rdev->dev);
+ optee_rpmi_async_notif_uninit(optee); optee_remove_common(optee); optee_rpmi_shm_rht_uninit(optee); kfree(optee); @@ -901,6 +984,10 @@ static int optee_rpmi_probe(struct rpmi_tee_device *rdev)
optee->ctx = ctx; dev_set_drvdata(&rdev->dev, optee); + ret = optee_rpmi_async_notif_init(optee); + if (ret) + dev_warn(&rdev->dev, "async notifications unavailable: %d\n", ret); + if (optee->in_kernel_rpmb_routing) blocking_notifier_chain_register(&optee_rpmb_intf_added, &optee->rpmb_intf);