Register an RPMI service driver matching the OP-TEE service UUID and integrate it with OP-TEE module initialization and removal.
Check the service API version, query the trusted OS revision, and obtain the RPC parameter and logical notification counts. Initialize shared-memory tracking, the call queue, supplicant state and internal context before publishing the client and supplicant TEE devices.
Connect the RPMI backend to the common OP-TEE operations and enumerate trusted application devices. Enable in-kernel RPMB routing when the RPMB subsystem is reachable.
Add removal and probe failure cleanup for the backend resources.
Signed-off-by: Amirreza Zarrabi amirreza.zarrabi@oss.qualcomm.com --- drivers/tee/optee/core.c | 10 +- drivers/tee/optee/optee_private.h | 18 ++- drivers/tee/optee/rpmi_abi.c | 299 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 322 insertions(+), 5 deletions(-)
diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index a52c1f498b99..d9bb4d3a34b3 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -220,6 +220,7 @@ void optee_remove_common(struct optee *optee)
static int smc_abi_rc; static int ffa_abi_rc; +static int rpmi_abi_rc; static bool intf_is_regged;
static int __init optee_core_init(void) @@ -245,14 +246,15 @@ static int __init optee_core_init(void)
smc_abi_rc = optee_smc_abi_register(); ffa_abi_rc = optee_ffa_abi_register(); + rpmi_abi_rc = optee_rpmi_abi_register();
- /* If both failed there's no point with this module */ - if (smc_abi_rc && ffa_abi_rc) { + /* Keep the module if any transport registered successfully. */ + if (smc_abi_rc && ffa_abi_rc && rpmi_abi_rc) { if (IS_REACHABLE(CONFIG_RPMB)) { rpmb_interface_unregister(&rpmb_class_intf); intf_is_regged = false; } - return smc_abi_rc; + return -EOPNOTSUPP; }
return 0; @@ -270,6 +272,8 @@ static void __exit optee_core_exit(void) optee_smc_abi_unregister(); if (!ffa_abi_rc) optee_ffa_abi_unregister(); + if (!rpmi_abi_rc) + optee_rpmi_abi_unregister(); } module_exit(optee_core_exit);
diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h index efc1e37fe1bf..fcb291a43b7b 100644 --- a/drivers/tee/optee/optee_private.h +++ b/drivers/tee/optee/optee_private.h @@ -224,8 +224,8 @@ struct optee; * @os_build_id: OP-TEE OS build identifier (0 if unspecified) * * Values come from OPTEE_SMC_CALL_GET_OS_REVISION (SMC ABI) or - * OPTEE_FFA_GET_OS_VERSION (FF-A ABI); this is the trusted OS revision, not an - * FF-A ABI version. + * OPTEE_FFA_GET_OS_VERSION (FF-A ABI) or OPTEE_RPMI_GET_OS_VERSION (RPMI ABI). + * This is the trusted OS revision, not a transport ABI version. */ struct optee_revision { u32 os_major; @@ -503,4 +503,18 @@ static inline void optee_ffa_abi_unregister(void) } #endif
+#if IS_REACHABLE(CONFIG_RISCV_RPMI_TEE_TRANSPORT) +int optee_rpmi_abi_register(void); +void optee_rpmi_abi_unregister(void); +#else +static inline int optee_rpmi_abi_register(void) +{ + return -EOPNOTSUPP; +} + +static inline void optee_rpmi_abi_unregister(void) +{ +} +#endif + #endif /*OPTEE_PRIVATE_H*/ diff --git a/drivers/tee/optee/rpmi_abi.c b/drivers/tee/optee/rpmi_abi.c index 9a6439e15a33..0ed2de96758e 100644 --- a/drivers/tee/optee/rpmi_abi.c +++ b/drivers/tee/optee/rpmi_abi.c @@ -579,3 +579,302 @@ static int optee_rpmi_do_call_with_arg(struct tee_context *ctx,
return optee_rpmi_yielding_call(ctx, &req, rpc_arg, system_thread); } + +/* Query and store the trusted OS revision. */ +static int optee_rpmi_get_os_version(struct optee *optee) +{ + struct optee_rpmi_probe_req req = { + .op = cpu_to_le32(OPTEE_RPMI_GET_OS_VERSION), + }; + struct optee_rpmi_os_resp os; + int rc; + + rc = optee_rpmi_call(optee, &req, sizeof(req), &os, sizeof(os)); + if (rc) + return rc; + + optee->revision.os_major = le32_to_cpu(os.major); + optee->revision.os_minor = le32_to_cpu(os.minor); + optee->revision.os_build_id = le64_to_cpu(os.build_id); + + if (optee->revision.os_build_id) + pr_info("revision %u.%u (%016llx)\n", + optee->revision.os_major, optee->revision.os_minor, + optee->revision.os_build_id); + else + pr_info("revision %u.%u\n", optee->revision.os_major, + optee->revision.os_minor); + + return 0; +} + +/* Query secure-world limits; version 1 defines no optional capability bits. */ +static int optee_rpmi_exchange_caps(struct optee *optee, u32 *rpc_param_count, + u32 *max_notif_key) +{ + struct optee_rpmi_probe_req req = { + .op = cpu_to_le32(OPTEE_RPMI_EXCHANGE_CAPABILITIES), + }; + struct optee_rpmi_caps_resp caps; + u32 rpc_count, max_key; + int rc; + + rc = optee_rpmi_call(optee, &req, sizeof(req), &caps, sizeof(caps)); + if (rc) + return rc; + + rpc_count = le32_to_cpu(caps.rpc_param_count); + if (!rpc_count) + return -EPROTO; + + *rpc_param_count = rpc_count; + + max_key = le32_to_cpu(caps.max_notification_key); + if (max_key) + *max_notif_key = max_key; + else + *max_notif_key = OPTEE_DEFAULT_MAX_NOTIF_VALUE; + + return 0; +} + +static int optee_rpmi_api_is_compatible(struct optee *optee) +{ + struct optee_rpmi_probe_req req = { + .op = cpu_to_le32(OPTEE_RPMI_GET_API_VERSION), + }; + struct optee_rpmi_api_resp api; + int rc; + + rc = optee_rpmi_call(optee, &req, sizeof(req), &api, sizeof(api)); + if (rc) + return rc; + + if (le32_to_cpu(api.major) != OPTEE_RPMI_VERSION_MAJOR) + return -EPROTONOSUPPORT; + + /* Version 1.0 has no minimum minor revision beyond zero. */ + return 0; +} + +static void optee_rpmi_get_version(struct tee_device *teedev, + struct tee_ioctl_version_data *vers) +{ + *vers = (struct tee_ioctl_version_data) { + .impl_id = TEE_IMPL_ID_OPTEE, + .gen_caps = TEE_GEN_CAP_GP | TEE_GEN_CAP_REG_MEM | + TEE_GEN_CAP_MEMREF_NULL, + }; +} + +static int optee_rpmi_open(struct tee_context *ctx) +{ + return optee_open(ctx, true); +} + +static const struct tee_driver_ops optee_rpmi_clnt_ops = { + .get_version = optee_rpmi_get_version, + .get_tee_revision = optee_get_revision, + .open = optee_rpmi_open, + .release = optee_release, + .open_session = optee_open_session, + .close_session = optee_close_session, + .invoke_func = optee_invoke_func, + .cancel_req = optee_cancel_req, + .shm_register = optee_rpmi_shm_register, + .shm_unregister = optee_rpmi_shm_unregister, +}; + +static const struct tee_driver_ops optee_rpmi_supp_ops = { + .get_version = optee_rpmi_get_version, + .get_tee_revision = optee_get_revision, + .open = optee_rpmi_open, + .release = optee_release_supp, + .supp_recv = optee_supp_recv, + .supp_send = optee_supp_send, + .shm_register = optee_rpmi_shm_register, + .shm_unregister = optee_rpmi_shm_unregister_supp, +}; + +static const struct tee_desc optee_rpmi_clnt_desc = { + .name = DRIVER_NAME "-rpmi-clnt", + .ops = &optee_rpmi_clnt_ops, + .owner = THIS_MODULE, +}; + +static const struct tee_desc optee_rpmi_supp_desc = { + .name = DRIVER_NAME "-rpmi-supp", + .ops = &optee_rpmi_supp_ops, + .owner = THIS_MODULE, + .flags = TEE_DESC_PRIVILEGED, +}; + +static const struct optee_ops optee_rpmi_ops = { + .do_call_with_arg = optee_rpmi_do_call_with_arg, + .to_msg_param = optee_rpmi_to_msg_param, + .from_msg_param = optee_rpmi_from_msg_param, +}; + +/* Keep callback state and memory tables alive until all TEE users release. */ +static void optee_rpmi_remove(struct rpmi_tee_device *rdev) +{ + struct optee *optee = rpmi_tee_dev_get_drvdata(rdev); + + optee_remove_common(optee); + optee_shm_handle_map_uninit(&optee->rpmi.shm_map); + ida_destroy(&optee->rpmi.nonce_ida); + kfree(optee); +} + +static int optee_rpmi_probe(struct rpmi_tee_device *rdev) +{ + struct tee_device *teedev; + struct tee_shm_pool *pool; + struct optee *optee; + struct tee_context *ctx; + u32 rpc_param_count, max_notif_key; + int rc; + + optee = kzalloc_obj(*optee); + if (!optee) + return -ENOMEM; + + optee->rpmi.rdev = rdev; + ida_init(&optee->rpmi.nonce_ida); + optee->ops = &optee_rpmi_ops; + + rc = optee_rpmi_api_is_compatible(optee); + if (rc) + goto err_optee; + + rc = optee_rpmi_get_os_version(optee); + if (rc) + goto err_optee; + + rc = optee_rpmi_exchange_caps(optee, &rpc_param_count, &max_notif_key); + if (rc) + goto err_optee; + + optee->rpc_param_count = rpc_param_count; + optee->in_kernel_rpmb_routing = IS_REACHABLE(CONFIG_RPMB); + + pool = optee_rpmi_shm_pool_alloc(); + if (IS_ERR(pool)) { + rc = PTR_ERR(pool); + goto err_optee; + } + optee->pool = pool; + + rc = optee_shm_handle_map_init(&optee->rpmi.shm_map); + if (rc) + goto err_pool; + + optee_cq_init(&optee->call_queue, 0); + optee_supp_init(&optee->supp); + optee_shm_arg_cache_init(optee, OPTEE_SHM_ARG_SHARED); + mutex_init(&optee->rpmb_dev_mutex); + INIT_WORK(&optee->rpmb_scan_bus_work, optee_bus_scan_rpmb); + optee->rpmb_intf.notifier_call = optee_rpmb_intf_rdev; + rc = optee_notif_init(optee, max_notif_key); + if (rc) + goto err_common; + + teedev = tee_device_alloc(&optee_rpmi_clnt_desc, &rdev->dev, + optee->pool, optee); + if (IS_ERR(teedev)) { + rc = PTR_ERR(teedev); + goto err_notif; + } + optee->teedev = teedev; + + teedev = tee_device_alloc(&optee_rpmi_supp_desc, &rdev->dev, + optee->pool, optee); + if (IS_ERR(teedev)) { + rc = PTR_ERR(teedev); + goto err_devices; + } + optee->supp_teedev = teedev; + + optee_set_dev_group(optee); + + /* Internal RPC allocation must be ready before userspace can enter. */ + ctx = teedev_open(optee->teedev); + if (IS_ERR(ctx)) { + rc = PTR_ERR(ctx); + goto err_devices; + } + optee->ctx = ctx; + + rpmi_tee_dev_set_drvdata(rdev, optee); + + if (optee->in_kernel_rpmb_routing) + blocking_notifier_chain_register(&optee_rpmb_intf_added, + &optee->rpmb_intf); + + rc = tee_device_register(optee->teedev); + if (rc) + goto err_initialized; + + rc = tee_device_register(optee->supp_teedev); + if (rc) + goto err_initialized; + + rc = optee_enumerate_devices(PTA_CMD_GET_DEVICES); + if (rc) + goto err_initialized; + + dev_info(&rdev->dev, "OP-TEE RPMI %u.%u initialized\n", + optee->revision.os_major, optee->revision.os_minor); + + return 0; + +err_initialized: + /* The remove path owns and frees the published backend state. */ + optee_rpmi_remove(rdev); + + return rc; + +err_devices: + tee_device_unregister(optee->supp_teedev); + tee_device_unregister(optee->teedev); + optee_shm_arg_cache_uninit(optee); +err_notif: + optee_notif_uninit(optee); +err_common: + optee_supp_uninit(&optee->supp); + mutex_destroy(&optee->call_queue.mutex); + rpmb_dev_put(optee->rpmb_dev); + mutex_destroy(&optee->rpmb_dev_mutex); + optee_shm_handle_map_uninit(&optee->rpmi.shm_map); +err_pool: + tee_shm_pool_free(optee->pool); +err_optee: + ida_destroy(&optee->rpmi.nonce_ida); + kfree(optee); + + return rc; +} + +static const struct rpmi_tee_device_id optee_rpmi_device_ids[] = { + { OPTEE_RPMI_SERVICE_UUID }, + {} +}; + +static struct rpmi_tee_driver optee_rpmi_driver = { + .name = DRIVER_NAME "-rpmi", + .probe = optee_rpmi_probe, + .remove = optee_rpmi_remove, + .id_table = optee_rpmi_device_ids, +}; + +int optee_rpmi_abi_register(void) +{ + return rpmi_tee_register(&optee_rpmi_driver); +} + +void optee_rpmi_abi_unregister(void) +{ + rpmi_tee_unregister(&optee_rpmi_driver); +} + +MODULE_ALIAS("rpmi_tee:486178e0-e7f8-11e3-bc5e-0002a5d5c51b");