On Qualcomm SoC based platforms, UEFI stores EFI variables within the Replay Protected Memory Block (RPMB) which is only accessible by the Qualcomm Trusted Execution Environment (QTEE).
For Qualcomm platforms without emulated RPMB support, specifically platforms where RPMB is not located within SPI-NOR storage and instead located on UFS/EMMC storage, non-volatile EFI variables can only be set via a callback request from the UEFI Secure Application to the RPMB service running in user-space (within the QTEE supplicant [1]).
Unlike the QCOM-TEE driver, the QSEECOM driver (used by the current QSEECOM based uefisecapp) does not support callback requests. And on certain Qualcomm platforms such as the RB3Gen2, attempts to access the QSEECOM interface fail due to lack of support within Qualcomm TEE. On these platforms, a TEE based uefisecapp client driver is required to: 1. Access cached & volatile EFI variables stored in uefisecapp's memory. 2. Ensure persistence of non-volatile EFI variables via writes through the RPMB service hosted in the QTEE supplicant.
This series introduces such a uefisecapp TEE client driver for the aforementioned Qualcomm platforms which installs efi-var operations _if_ the QCOMTEE driver registers support for an object-IPC based uefisecapp service on the TEE bus during its probe. Only new QTEE firmware versions available at [2] provide this support.
Thus, QCOMTEE now maintains a static list of always-available object-IPC based secure services exposed by QTEE. These services are implemented either within the QTEE kernel or within a pre-loaded Trusted Application (TA) usually loaded by the bootloader. The uefisecapp TA is an example of a preloaded TA loaded by UEFI. A static list is required since QTEE does not yet expose any way to dynamically query and enumerate the services exposed by it.
To facilitate object-IPC interactions from the kernel-space, this series also introduces a tee_client_object_invoke_func() to allow invocation of TEE objects similar to the existing tee_client_invoke_func() API exported by the TEE subsystem which allows invocation of TEE functions. Some suporting changes are also introduced to track and handle operations for TEE contexts opened from the kernel-space in the back-end QCOM-TEE driver along with associated changes in user-space libqcomtee via Pull Request [3].
Finally and as previously mentioned, access to the object-IPC based uefisecapp service is restricted on older QTEE firmware versions. A new QTEE firmware release must be picked up from QArtifactory [2] for all upstream supported Qualcomm SoCs to enable access to uefisecapp service via the TEE client driver.
This patch series has been validated on Kodiak RB3Gen2 platform with UFS storage by attempting to read/write EFI variables via the efivar tool [4] after mounting the efivarfs filesystem. See [5] for an example.
Merge Strategy:
This patch series could either be taken from the OP-TEE tree or the QCOM soc tree. I would prefer it to be picked by the OP-TEE tree since all except the uefisecapp TEE client driver patch in this series make changes relevant to the TEE subsystem. It would be great if the QCOM soc tree maintainers can Ack the uefisecapp driver patch.
[1] https://github.com/qualcomm/minkipc [2] https://shorturl.at/zQU07 [3] https://github.com/quic/quic-teec/pull/27 [4] https://github.com/rhboot/efivar [5] https://docs.qualcomm.com/doc/80-70020-27/topic/manage_uefi_environment_vari...
Signed-off-by: Harshal Dev harshal.dev@oss.qualcomm.com --- Amirreza Zarrabi (2): tee: Add kernel client object invoke helper tee: qcomtee: Allow object invokes from kernel clients
Harshal Dev (4): tee: qcomtee: Track the object invocation context tee: Export uuidv5 generation for TEE clients tee: qcomtee: Add support for registering QTEE services on TEE bus firmware: qcom: Add support for TEE based EFI-var client driver
MAINTAINERS | 7 + drivers/firmware/qcom/Kconfig | 24 ++ drivers/firmware/qcom/Makefile | 1 + drivers/firmware/qcom/qcom_tee_uefisecapp.c | 525 ++++++++++++++++++++++++++++ drivers/firmware/qcom/qcom_tee_uefisecapp.h | 120 +++++++ drivers/tee/qcomtee/call.c | 218 +++++++++++- drivers/tee/qcomtee/core.c | 9 +- drivers/tee/qcomtee/qcomtee.h | 18 + drivers/tee/qcomtee/qcomtee_msg.h | 1 + drivers/tee/qcomtee/qcomtee_object.h | 16 +- drivers/tee/tee_core.c | 26 +- include/linux/tee_core.h | 15 + include/linux/tee_drv.h | 18 +- 13 files changed, 968 insertions(+), 30 deletions(-) --- base-commit: f3e6330d7fe42b204af05a2dbc68b379e0ad179e change-id: 20260408-qcom_uefisecapp_migrate_qcomtee-13869d45e014
Best regards,
QCOMTEE needs to distinguish between object invocations arriving from kernel clients and user-space clients in order to correctly marshal UBUF parameters and decide whether certain operations should be permitted.
Add a kernel_ctx flag to struct qcomtee_object_invoke_context to track the context of object invocation. Objects invoked from the kernel-space are expected to have the MSB of their 64-bit object-id set to indicate a kernel context, whereas objects invoked from user-space should not set it. To ensure this, we restrict the object-id space of user-space invoked objects to 32-bits. This is in-line with QTEE expectation of 32-bit object ids.
Signed-off-by: Amirreza Zarrabi amirreza.zarrabi@oss.qualcomm.com Signed-off-by: Harshal Dev harshal.dev@oss.qualcomm.com --- drivers/tee/qcomtee/call.c | 24 ++++++++++++++++++++++-- drivers/tee/qcomtee/qcomtee.h | 6 ++++++ drivers/tee/qcomtee/qcomtee_object.h | 8 ++++++-- drivers/tee/tee_core.c | 4 ++++ 4 files changed, 38 insertions(+), 4 deletions(-)
diff --git a/drivers/tee/qcomtee/call.c b/drivers/tee/qcomtee/call.c index 0efc5646242a..a74a54d67b06 100644 --- a/drivers/tee/qcomtee/call.c +++ b/drivers/tee/qcomtee/call.c @@ -397,11 +397,31 @@ static int qcomtee_object_invoke(struct tee_context *ctx, { struct qcomtee_context_data *ctxdata = ctx->data; struct qcomtee_object *object; + bool kernel_ctx = false; int i, ret, result;
if (qcomtee_params_check(params, arg->num_params)) return -EINVAL;
+ /* Obtain the invocation context information from the MSB of the object + * `id` field. + */ + kernel_ctx = QCOMTEE_GET_CLIENT_CTX(arg->id); + /* User-space identifies a NULL object via a 32-bit TEE_OBJREF_NULL id, whereas + * the kernel uses as 64-bit object-id. Hence, we check for a NULL object by + * sign-extending the object-id to 64 bits. If user-space is indeed invoking a + * NULL object we must extend the object-id to 64-bits from here on so that + * QCOMTEE can recognize it. + */ + if (!kernel_ctx && ((s64)(s32)arg->id) == TEE_OBJREF_NULL) + arg->id = TEE_OBJREF_NULL; + + /* If the object being invoked is not NULL, drop the MSB from the `id` field to + * obtain the actual object-id. + */ + if (arg->id != TEE_OBJREF_NULL) + arg->id = QCOMTEE_SANITIZE_OBJ_ID(arg->id); + /* First, handle reserved operations: */ if (arg->op == QCOMTEE_MSG_OBJECT_OP_RELEASE) { del_qtee_object(arg->id, ctxdata); @@ -411,7 +431,7 @@ static int qcomtee_object_invoke(struct tee_context *ctx,
/* Otherwise, invoke a QTEE object: */ struct qcomtee_object_invoke_ctx *oic __free(kfree) = - qcomtee_object_invoke_ctx_alloc(ctx); + qcomtee_object_invoke_ctx_alloc(ctx, kernel_ctx); if (!oic) return -ENOMEM;
@@ -648,7 +668,7 @@ static void qcomtee_get_qtee_feature_list(struct tee_context *ctx, u32 id, int result;
struct qcomtee_object_invoke_ctx *oic __free(kfree) = - qcomtee_object_invoke_ctx_alloc(ctx); + qcomtee_object_invoke_ctx_alloc(ctx, true); if (!oic) return;
diff --git a/drivers/tee/qcomtee/qcomtee.h b/drivers/tee/qcomtee/qcomtee.h index f39bf63fd1c2..5d292a2ff83d 100644 --- a/drivers/tee/qcomtee/qcomtee.h +++ b/drivers/tee/qcomtee/qcomtee.h @@ -17,6 +17,12 @@ #define QCOMTEE_OBJREF_FLAG_USER BIT(1) #define QCOMTEE_OBJREF_FLAG_MEM BIT(2)
+/* The MSB of the object_id field indicates whether the client is invoking the + * object from user context or kernel context. + */ +#define QCOMTEE_GET_CLIENT_CTX(x) (((x) >> 63) & 1U) +#define QCOMTEE_SANITIZE_OBJ_ID(x) ((x) & (BIT(63) - 1)) + /** * struct qcomtee - Main service struct. * @teedev: client device. diff --git a/drivers/tee/qcomtee/qcomtee_object.h b/drivers/tee/qcomtee/qcomtee_object.h index 8b4401ecad48..2528d07e4576 100644 --- a/drivers/tee/qcomtee/qcomtee_object.h +++ b/drivers/tee/qcomtee/qcomtee_object.h @@ -146,6 +146,7 @@ static inline int qcomtee_args_len(struct qcomtee_arg *args) * struct qcomtee_object_invoke_ctx - QTEE context for object invocation. * @ctx: TEE context for this invocation. * @flags: flags for the invocation context. + * @kernel_ctx: flag that indicates this context is owned by a kernel client. * @errno: error code for the invocation. * @object: current object invoked in this callback context. * @u: array of arguments for the current invocation (+1 for ending arg). @@ -158,6 +159,7 @@ static inline int qcomtee_args_len(struct qcomtee_arg *args) struct qcomtee_object_invoke_ctx { struct tee_context *ctx; unsigned long flags; + bool kernel_ctx; int errno;
struct qcomtee_object *object; @@ -172,13 +174,15 @@ struct qcomtee_object_invoke_ctx { };
static inline struct qcomtee_object_invoke_ctx * -qcomtee_object_invoke_ctx_alloc(struct tee_context *ctx) +qcomtee_object_invoke_ctx_alloc(struct tee_context *ctx, bool kernel_ctx) { struct qcomtee_object_invoke_ctx *oic;
oic = kzalloc_obj(*oic); - if (oic) + if (oic) { oic->ctx = ctx; + oic->kernel_ctx = kernel_ctx; + } return oic; }
diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index ef9642d72672..7f986d7fb47f 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -706,6 +706,10 @@ static int tee_ioctl_object_invoke(struct tee_context *ctx, goto out; }
+ /* Userspace object-ids are restricted to 32-bits. */ + if (arg.id > U32_MAX) + return -EINVAL; + rc = ctx->teedev->desc->ops->object_invoke_func(ctx, &arg, params); if (rc) goto out;
Hi Harshal,
On 7/7/2026 4:11 PM, Harshal Dev wrote:
QCOMTEE needs to distinguish between object invocations arriving from kernel clients and user-space clients in order to correctly marshal UBUF parameters and decide whether certain operations should be permitted.
Add a kernel_ctx flag to struct qcomtee_object_invoke_context to track the context of object invocation. Objects invoked from the kernel-space are expected to have the MSB of their 64-bit object-id set to indicate a kernel context, whereas objects invoked from user-space should not set it. To ensure this, we restrict the object-id space of user-space invoked objects to 32-bits. This is in-line with QTEE expectation of 32-bit object ids.
Signed-off-by: Amirreza Zarrabi amirreza.zarrabi@oss.qualcomm.com Signed-off-by: Harshal Dev harshal.dev@oss.qualcomm.com
drivers/tee/qcomtee/call.c | 24 ++++++++++++++++++++++-- drivers/tee/qcomtee/qcomtee.h | 6 ++++++ drivers/tee/qcomtee/qcomtee_object.h | 8 ++++++-- drivers/tee/tee_core.c | 4 ++++ 4 files changed, 38 insertions(+), 4 deletions(-)
diff --git a/drivers/tee/qcomtee/call.c b/drivers/tee/qcomtee/call.c index 0efc5646242a..a74a54d67b06 100644 --- a/drivers/tee/qcomtee/call.c +++ b/drivers/tee/qcomtee/call.c @@ -397,11 +397,31 @@ static int qcomtee_object_invoke(struct tee_context *ctx, { struct qcomtee_context_data *ctxdata = ctx->data; struct qcomtee_object *object;
- bool kernel_ctx = false; int i, ret, result;
if (qcomtee_params_check(params, arg->num_params)) return -EINVAL;
- /* Obtain the invocation context information from the MSB of the object
* `id` field.*/- kernel_ctx = QCOMTEE_GET_CLIENT_CTX(arg->id);
- /* User-space identifies a NULL object via a 32-bit TEE_OBJREF_NULL id, whereas
* the kernel uses as 64-bit object-id. Hence, we check for a NULL object by* sign-extending the object-id to 64 bits. If user-space is indeed invoking a* NULL object we must extend the object-id to 64-bits from here on so that* QCOMTEE can recognize it.*/- if (!kernel_ctx && ((s64)(s32)arg->id) == TEE_OBJREF_NULL)
arg->id = TEE_OBJREF_NULL;
Does it need to be MSB -- why bit 63? the object ID supported by QTEE is 32-bit anyway. Let's mask the upper 32-bit and do something like kernel_ctx = !!upper_32_bits(id). What do you think?
- /* If the object being invoked is not NULL, drop the MSB from the `id` field to
* obtain the actual object-id.*/- if (arg->id != TEE_OBJREF_NULL)
arg->id = QCOMTEE_SANITIZE_OBJ_ID(arg->id);- /* First, handle reserved operations: */ if (arg->op == QCOMTEE_MSG_OBJECT_OP_RELEASE) { del_qtee_object(arg->id, ctxdata);
@@ -411,7 +431,7 @@ static int qcomtee_object_invoke(struct tee_context *ctx, /* Otherwise, invoke a QTEE object: */ struct qcomtee_object_invoke_ctx *oic __free(kfree) =
qcomtee_object_invoke_ctx_alloc(ctx);
if (!oic) return -ENOMEM;qcomtee_object_invoke_ctx_alloc(ctx, kernel_ctx);@@ -648,7 +668,7 @@ static void qcomtee_get_qtee_feature_list(struct tee_context *ctx, u32 id, int result; struct qcomtee_object_invoke_ctx *oic __free(kfree) =
qcomtee_object_invoke_ctx_alloc(ctx);
if (!oic) return;qcomtee_object_invoke_ctx_alloc(ctx, true);diff --git a/drivers/tee/qcomtee/qcomtee.h b/drivers/tee/qcomtee/qcomtee.h index f39bf63fd1c2..5d292a2ff83d 100644 --- a/drivers/tee/qcomtee/qcomtee.h +++ b/drivers/tee/qcomtee/qcomtee.h @@ -17,6 +17,12 @@ #define QCOMTEE_OBJREF_FLAG_USER BIT(1) #define QCOMTEE_OBJREF_FLAG_MEM BIT(2) +/* The MSB of the object_id field indicates whether the client is invoking the
- object from user context or kernel context.
- */
+#define QCOMTEE_GET_CLIENT_CTX(x) (((x) >> 63) & 1U) +#define QCOMTEE_SANITIZE_OBJ_ID(x) ((x) & (BIT(63) - 1))
/**
- struct qcomtee - Main service struct.
- @teedev: client device.
diff --git a/drivers/tee/qcomtee/qcomtee_object.h b/drivers/tee/qcomtee/qcomtee_object.h index 8b4401ecad48..2528d07e4576 100644 --- a/drivers/tee/qcomtee/qcomtee_object.h +++ b/drivers/tee/qcomtee/qcomtee_object.h @@ -146,6 +146,7 @@ static inline int qcomtee_args_len(struct qcomtee_arg *args)
- struct qcomtee_object_invoke_ctx - QTEE context for object invocation.
- @ctx: TEE context for this invocation.
- @flags: flags for the invocation context.
- @kernel_ctx: flag that indicates this context is owned by a kernel client.
- @errno: error code for the invocation.
- @object: current object invoked in this callback context.
- @u: array of arguments for the current invocation (+1 for ending arg).
@@ -158,6 +159,7 @@ static inline int qcomtee_args_len(struct qcomtee_arg *args) struct qcomtee_object_invoke_ctx { struct tee_context *ctx; unsigned long flags;
- bool kernel_ctx; int errno;
struct qcomtee_object *object; @@ -172,13 +174,15 @@ struct qcomtee_object_invoke_ctx { }; static inline struct qcomtee_object_invoke_ctx * -qcomtee_object_invoke_ctx_alloc(struct tee_context *ctx) +qcomtee_object_invoke_ctx_alloc(struct tee_context *ctx, bool kernel_ctx) { struct qcomtee_object_invoke_ctx *oic; oic = kzalloc_obj(*oic);
- if (oic)
- if (oic) { oic->ctx = ctx;
oic->kernel_ctx = kernel_ctx;- } return oic;
} diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index ef9642d72672..7f986d7fb47f 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -706,6 +706,10 @@ static int tee_ioctl_object_invoke(struct tee_context *ctx, goto out; }
- /* Userspace object-ids are restricted to 32-bits. */
- if (arg.id > U32_MAX)
return -EINVAL;
This change belongs to tee SS, move it to a separate commit with appropriate message.
rc = ctx->teedev->desc->ops->object_invoke_func(ctx, &arg, params); if (rc) goto out;
Regards, Amir
From: Amirreza Zarrabi amirreza.zarrabi@oss.qualcomm.com
Kernel clients can open a TEE context and invoke regular TA commands through tee_client_invoke_func(). However, there is currently no equivalent helper for invoking TEE objects.
Add tee_client_object_invoke_func() as a kernel client API for issuing object invocation requests. The helper checks that the backend provides object_invoke_func() before setting the MSB of the object-id and forwarding the request. The MSB of the object-id informs the TEE backend that the object is invoked from a kernel context.
This allows TEE backends that support privileged object-based calls from the kernel-space.
Signed-off-by: Amirreza Zarrabi amirreza.zarrabi@oss.qualcomm.com Signed-off-by: Harshal Dev harshal.dev@oss.qualcomm.com --- drivers/tee/tee_core.c | 13 +++++++++++++ include/linux/tee_drv.h | 13 +++++++++++++ 2 files changed, 26 insertions(+)
diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index 7f986d7fb47f..0783802fd010 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -1413,6 +1413,19 @@ int tee_client_invoke_func(struct tee_context *ctx, } EXPORT_SYMBOL_GPL(tee_client_invoke_func);
+int tee_client_object_invoke_func(struct tee_context *ctx, + struct tee_ioctl_object_invoke_arg *arg, + struct tee_param *param) +{ + if (!ctx->teedev->desc->ops->object_invoke_func) + return -EINVAL; + + /* Indicate that this object is being invoked from a kernel context. */ + arg->id = arg->id | BIT(63); + return ctx->teedev->desc->ops->object_invoke_func(ctx, arg, param); +} +EXPORT_SYMBOL_GPL(tee_client_object_invoke_func); + int tee_client_cancel_req(struct tee_context *ctx, struct tee_ioctl_cancel_arg *arg) { diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h index e561a26f537a..ca99c6b747a8 100644 --- a/include/linux/tee_drv.h +++ b/include/linux/tee_drv.h @@ -283,6 +283,19 @@ int tee_client_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, struct tee_param *param);
+/** + * tee_client_object_invoke_func() - Invoke a TEE object from kernel space + * @ctx: TEE Context + * @arg: Invoke arguments, see description of + * struct tee_ioctl_object_invoke_arg + * @param: Parameters for the object invocation + * + * Return: On success, returns 0; on failure, returns < 0. + */ +int tee_client_object_invoke_func(struct tee_context *ctx, + struct tee_ioctl_object_invoke_arg *arg, + struct tee_param *param); + /** * tee_client_cancel_req() - Request cancellation of the previous open-session * or invoke-command operations in a Trusted Application
Hi Harshal,
On 7/7/2026 4:11 PM, Harshal Dev wrote:
From: Amirreza Zarrabi amirreza.zarrabi@oss.qualcomm.com
Kernel clients can open a TEE context and invoke regular TA commands through tee_client_invoke_func(). However, there is currently no equivalent helper for invoking TEE objects.
Add tee_client_object_invoke_func() as a kernel client API for issuing object invocation requests. The helper checks that the backend provides object_invoke_func() before setting the MSB of the object-id and forwarding the request. The MSB of the object-id informs the TEE backend that the object is invoked from a kernel context.
This allows TEE backends that support privileged object-based calls from the kernel-space.
Signed-off-by: Amirreza Zarrabi amirreza.zarrabi@oss.qualcomm.com Signed-off-by: Harshal Dev harshal.dev@oss.qualcomm.com
drivers/tee/tee_core.c | 13 +++++++++++++ include/linux/tee_drv.h | 13 +++++++++++++ 2 files changed, 26 insertions(+)
diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index 7f986d7fb47f..0783802fd010 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -1413,6 +1413,19 @@ int tee_client_invoke_func(struct tee_context *ctx, } EXPORT_SYMBOL_GPL(tee_client_invoke_func); +int tee_client_object_invoke_func(struct tee_context *ctx,
struct tee_ioctl_object_invoke_arg *arg,struct tee_param *param)+{
- if (!ctx->teedev->desc->ops->object_invoke_func)
return -EINVAL;- /* Indicate that this object is being invoked from a kernel context. */
- arg->id = arg->id | BIT(63);
Setting this bit is a qcomtee backend requirement and should not be exposed to the TEE subsystem.
Kernel users of qcomtee_object_invoke() should use the normal object ID value. For example, access to the initial ROOT maybe represented as TEE_OBJREF_NULL | BIT_ULL(63), but the upper bit must be managed by the qcomtee backend for any return object, i.e. The backend should also set BIT_ULL(63) automatically on each returned object ID. Callers should treat the ID as opaque and must not manipulate the upper bit directly.
- return ctx->teedev->desc->ops->object_invoke_func(ctx, arg, param);
+} +EXPORT_SYMBOL_GPL(tee_client_object_invoke_func);
int tee_client_cancel_req(struct tee_context *ctx, struct tee_ioctl_cancel_arg *arg) { diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h index e561a26f537a..ca99c6b747a8 100644 --- a/include/linux/tee_drv.h +++ b/include/linux/tee_drv.h @@ -283,6 +283,19 @@ int tee_client_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, struct tee_param *param); +/**
- tee_client_object_invoke_func() - Invoke a TEE object from kernel space
- @ctx: TEE Context
- @arg: Invoke arguments, see description of
struct tee_ioctl_object_invoke_arg
- @param: Parameters for the object invocation
- Return: On success, returns 0; on failure, returns < 0.
- */
+int tee_client_object_invoke_func(struct tee_context *ctx,
struct tee_ioctl_object_invoke_arg *arg,struct tee_param *param);/**
- tee_client_cancel_req() - Request cancellation of the previous open-session
- or invoke-command operations in a Trusted Application
Regards, Amir
From: Amirreza Zarrabi amirreza.zarrabi@oss.qualcomm.com
QCOMTEE currently treats UBUF parameters as userspace addresses and applies userspace restrictions when invoking the root object. This is not suitable for object invocation requests issued by kernel clients.
Use the kernel_ctx flag to distinguish kernel client requests from userspace requests. For kernel contexts, do not mark UBUF parameters as user addresses, and allow permitted root-object operations to proceed without applying the userspace-only checks.
This allows in-kernel users of tee_client_object_invoke_func() to issue object invocation requests through the qcomtee backend.
Signed-off-by: Amirreza Zarrabi amirreza.zarrabi@oss.qualcomm.com Signed-off-by: Harshal Dev harshal.dev@oss.qualcomm.com --- drivers/tee/qcomtee/call.c | 34 +++++++++++++++++++++++----------- drivers/tee/qcomtee/qcomtee_object.h | 5 +++-- include/linux/tee_drv.h | 5 ++++- 3 files changed, 30 insertions(+), 14 deletions(-)
diff --git a/drivers/tee/qcomtee/call.c b/drivers/tee/qcomtee/call.c index a74a54d67b06..e12c1a6e72d8 100644 --- a/drivers/tee/qcomtee/call.c +++ b/drivers/tee/qcomtee/call.c @@ -202,7 +202,7 @@ int qcomtee_objref_from_arg(struct tee_param *param, struct qcomtee_arg *arg, */ static int qcomtee_params_to_args(struct qcomtee_arg *u, struct tee_param *params, int num_params, - struct tee_context *ctx) + struct qcomtee_object_invoke_ctx *oic) { int i;
@@ -210,8 +210,14 @@ static int qcomtee_params_to_args(struct qcomtee_arg *u, switch (params[i].attr) { case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_OUTPUT: - u[i].flags = QCOMTEE_ARG_FLAGS_UADDR; - u[i].b.uaddr = params[i].u.ubuf.uaddr; + u[i].flags = oic->kernel_ctx ? 0 : + QCOMTEE_ARG_FLAGS_UADDR; + + if (u[i].flags && QCOMTEE_ARG_FLAGS_UADDR) + u[i].b.uaddr = params[i].u.ubuf.uaddr; + else + u[i].b.addr = params[i].u.ubuf.addr; + u[i].b.size = params[i].u.ubuf.size;
if (params[i].attr == @@ -223,7 +229,7 @@ static int qcomtee_params_to_args(struct qcomtee_arg *u, break; case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT: u[i].type = QCOMTEE_ARG_TYPE_IO; - if (qcomtee_objref_to_arg(&u[i], ¶ms[i], ctx)) + if (qcomtee_objref_to_arg(&u[i], ¶ms[i], oic->ctx)) goto out_failed;
break; @@ -270,7 +276,7 @@ static int qcomtee_params_to_args(struct qcomtee_arg *u, */ static int qcomtee_params_from_args(struct tee_param *params, struct qcomtee_arg *u, int num_params, - struct tee_context *ctx) + struct qcomtee_object_invoke_ctx *oic) { int i, np;
@@ -288,7 +294,8 @@ static int qcomtee_params_from_args(struct tee_param *params, break; case QCOMTEE_ARG_TYPE_OO: /* TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT */ - if (qcomtee_objref_from_arg(¶ms[np], &u[np], ctx)) + if (qcomtee_objref_from_arg(¶ms[np], &u[np], + oic->ctx)) goto out_failed;
break; @@ -304,7 +311,7 @@ static int qcomtee_params_from_args(struct tee_param *params, /* Undo qcomtee_objref_from_arg(). */ for (i = 0; i < np; i++) { if (params[i].attr == TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT) - qcomtee_context_del_qtee_object(¶ms[i], ctx); + qcomtee_context_del_qtee_object(¶ms[i], oic->ctx); }
/* Release any IO and OO objects not processed. */ @@ -357,7 +364,8 @@ static int qcomtee_params_check(struct tee_param *params, int num_params) }
/* Check if an operation on ROOT_QCOMTEE_OBJECT from userspace is permitted. */ -static int qcomtee_root_object_check(u32 op, struct tee_param *params, +static int qcomtee_root_object_check(struct qcomtee_object_invoke_ctx *oic, + u32 op, struct tee_param *params, int num_params) { /* Some privileged operations recognized by QTEE. */ @@ -366,6 +374,9 @@ static int qcomtee_root_object_check(u32 op, struct tee_param *params, op == QCOMTEE_ROOT_OP_ADCI_SHUTDOWN) return -EINVAL;
+ if (oic->kernel_ctx) + return 0; + /* * QCOMTEE_ROOT_OP_REG_WITH_CREDENTIALS is to register with QTEE * by passing a credential object as input OBJREF. TEE_OBJREF_NULL as a @@ -444,7 +455,8 @@ static int qcomtee_object_invoke(struct tee_context *ctx, /* Get an object to invoke. */ if (arg->id == TEE_OBJREF_NULL) { /* Use ROOT if TEE_OBJREF_NULL is invoked. */ - if (qcomtee_root_object_check(arg->op, params, arg->num_params)) + if (qcomtee_root_object_check(oic, arg->op, params, + arg->num_params)) return -EINVAL;
object = ROOT_QCOMTEE_OBJECT; @@ -452,7 +464,7 @@ static int qcomtee_object_invoke(struct tee_context *ctx, return -EINVAL; }
- ret = qcomtee_params_to_args(u, params, arg->num_params, ctx); + ret = qcomtee_params_to_args(u, params, arg->num_params, oic); if (ret) goto out;
@@ -470,7 +482,7 @@ static int qcomtee_object_invoke(struct tee_context *ctx,
if (!result) { /* Assume service is UNAVAIL if unable to process the result. */ - if (qcomtee_params_from_args(params, u, arg->num_params, ctx)) + if (qcomtee_params_from_args(params, u, arg->num_params, oic)) result = QCOMTEE_MSG_ERROR_UNAVAIL; } else { /* diff --git a/drivers/tee/qcomtee/qcomtee_object.h b/drivers/tee/qcomtee/qcomtee_object.h index 2528d07e4576..7bd6e23b038c 100644 --- a/drivers/tee/qcomtee/qcomtee_object.h +++ b/drivers/tee/qcomtee/qcomtee_object.h @@ -112,8 +112,9 @@ struct qcomtee_buffer { * @b: address and size if the type of argument is a buffer. * @o: object instance if the type of argument is an object. * - * &qcomtee_arg.flags only accepts %QCOMTEE_ARG_FLAGS_UADDR for now, which - * states that &qcomtee_arg.b contains a userspace address in uaddr. ++ * If %QCOMTEE_ARG_FLAGS_UADDR is set in &qcomtee_arg.flags then it implies ++ * that &qcomtee_arg.b contains a userspace address in uaddr. ++ * Otherwise, &qcomtee_arg.b contains a kernel address in addr. */ struct qcomtee_arg { enum qcomtee_arg_type type; diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h index ca99c6b747a8..71d0536db60e 100644 --- a/include/linux/tee_drv.h +++ b/include/linux/tee_drv.h @@ -83,7 +83,10 @@ struct tee_param_memref { };
struct tee_param_ubuf { - void __user *uaddr; + union { + void *addr; + void __user *uaddr; + }; size_t size; };
Export the uuidv5() function defined in the TEE core to all TEE backends. This enables the TEE backend drivers to generate a UUID for identifying and registering their secure services on the TEE bus.
Signed-off-by: Harshal Dev harshal.dev@oss.qualcomm.com --- drivers/tee/tee_core.c | 9 +++++---- include/linux/tee_core.h | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 4 deletions(-)
diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index 0783802fd010..61dd99bbf5f6 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -135,7 +135,7 @@ static int tee_release(struct inode *inode, struct file *filp) }
/** - * uuid_v5() - Calculate UUIDv5 + * tee_generate_uuid_v5() - Calculate UUIDv5 * @uuid: Resulting UUID * @ns: Name space ID for UUIDv5 function * @name: Name for UUIDv5 function @@ -146,8 +146,8 @@ static int tee_release(struct inode *inode, struct file *filp) * This implements section (for SHA-1): * 4.3. Algorithm for Creating a Name-Based UUID */ -static void uuid_v5(uuid_t *uuid, const uuid_t *ns, const void *name, - size_t size) +void tee_generate_uuid_v5(uuid_t *uuid, const uuid_t *ns, const void *name, + size_t size) { unsigned char hash[SHA1_DIGEST_SIZE]; struct sha1_ctx ctx; @@ -163,6 +163,7 @@ static void uuid_v5(uuid_t *uuid, const uuid_t *ns, const void *name, uuid->b[6] = (hash[6] & 0x0F) | 0x50; uuid->b[8] = (hash[8] & 0x3F) | 0x80; } +EXPORT_SYMBOL_GPL(tee_generate_uuid_v5);
int tee_session_calc_client_uuid(uuid_t *uuid, u32 connection_method, const u8 connection_data[TEE_IOCTL_UUID_LEN]) @@ -228,7 +229,7 @@ int tee_session_calc_client_uuid(uuid_t *uuid, u32 connection_method, goto out_free_name; }
- uuid_v5(uuid, &tee_client_uuid_ns, name, name_len); + tee_generate_uuid_v5(uuid, &tee_client_uuid_ns, name, name_len); out_free_name: kfree(name);
diff --git a/include/linux/tee_core.h b/include/linux/tee_core.h index f993d5118edd..13807e795880 100644 --- a/include/linux/tee_core.h +++ b/include/linux/tee_core.h @@ -266,6 +266,21 @@ void tee_device_set_dev_groups(struct tee_device *teedev, int tee_session_calc_client_uuid(uuid_t *uuid, u32 connection_method, const u8 connection_data[TEE_IOCTL_UUID_LEN]);
+/** + * tee_generate_uuid_v5() - Calculate UUIDv5 + * @uuid: Resulting UUID + * @ns: Name space ID for UUIDv5 function + * @name: Name for UUIDv5 function + * @size: Size of name + * + * UUIDv5 is specific in RFC 4122. + * + * This implements section (for SHA-1): + * 4.3. Algorithm for Creating a Name-Based UUID + */ +void tee_generate_uuid_v5(uuid_t *uuid, const uuid_t *ns, const void *name, + size_t size); + /** * struct tee_shm_pool - shared memory pool * @ops: operations
QTEE exposes certain secure services implemented either within the QTEE kernel or via pre-loaded Trusted Applications (TAs). Such always-available services can be readily accessed by TEE client drivers via QTEE's object-IPC protocol if the service is registered as a device on the TEE bus.
One such service is the EFI-variables service, implemented by the uefisecapp TA which enables kernel clients to access EFI variables at runtime.
Maintain a static list of such always-available secure services and add support for the QCOMTEE driver to register these services as devices on the TEE bus during probe.
Signed-off-by: Harshal Dev harshal.dev@oss.qualcomm.com --- drivers/tee/qcomtee/call.c | 160 ++++++++++++++++++++++++++++++++++- drivers/tee/qcomtee/core.c | 9 +- drivers/tee/qcomtee/qcomtee.h | 12 +++ drivers/tee/qcomtee/qcomtee_msg.h | 1 + drivers/tee/qcomtee/qcomtee_object.h | 3 +- 5 files changed, 177 insertions(+), 8 deletions(-)
diff --git a/drivers/tee/qcomtee/call.c b/drivers/tee/qcomtee/call.c index e12c1a6e72d8..4373fcac2f5f 100644 --- a/drivers/tee/qcomtee/call.c +++ b/drivers/tee/qcomtee/call.c @@ -677,7 +677,7 @@ static void qcomtee_get_qtee_feature_list(struct tee_context *ctx, u32 id, { struct qcomtee_object *client_env, *service; struct qcomtee_arg u[3] = { 0 }; - int result; + int result, error = 0;
struct qcomtee_object_invoke_ctx *oic __free(kfree) = qcomtee_object_invoke_ctx_alloc(ctx, true); @@ -690,9 +690,13 @@ static void qcomtee_get_qtee_feature_list(struct tee_context *ctx, u32 id,
/* Get ''FeatureVersions Service'' object. */ service = qcomtee_object_get_service(oic, client_env, - QCOMTEE_FEATURE_VER_UID); - if (service == NULL_QCOMTEE_OBJECT) + QCOMTEE_FEATURE_VER_UID, + &error); + if (service == NULL_QCOMTEE_OBJECT) { + if (error) + pr_err("Failed to get service! error: %d\n", error); goto out_failed; + }
/* IB: Feature to query. */ u[0].b.addr = &id; @@ -712,6 +716,153 @@ static void qcomtee_get_qtee_feature_list(struct tee_context *ctx, u32 id, qcomtee_object_put(client_env); }
+/** + * is_qcomtee_service_available() - Check if the QTEE service identified by the UID + * is available + * @ctx: TEE context. + * @uid: 32-bit UID of the service. + * + * Returns true if the service exists and is available. + * Returns false if a service is not exposed by QTEE. + */ +static bool is_qcomtee_service_available(struct tee_context *ctx, u32 uid) +{ + struct qcomtee_object *client_env; + struct qcomtee_object *service; + int error = 0; + bool ret = false; + + struct qcomtee_object_invoke_ctx *oic __free(kfree) = + qcomtee_object_invoke_ctx_alloc(ctx, true); + if (!oic) + return ret; + + client_env = qcomtee_object_get_client_env(oic); + if (client_env == NULL_QCOMTEE_OBJECT) + return ret; + + /* Get service object corresponding to the uid. */ + service = qcomtee_object_get_service(oic, client_env, uid, &error); + if (service != NULL_QCOMTEE_OBJECT) { + qcomtee_object_put(service); + ret = true; + } + + /* When we fail to get the service, QTEE provides the reason. */ + if (error) + pr_err("Failed to get service! error: %d\n", error); + + qcomtee_object_put(client_env); + return ret; +} + +/* + * QTEE Service UUID name space identifier + * + * A random UUID that is allocated as a name space identifier for forming UUID's + * representing secure services exposed by QTEE. + */ +static const uuid_t qtee_service_uuid_ns = UUID_INIT(0xe1b48857, 0x6154, 0x49f9, + 0x93, 0x4e, 0xa2, 0xf2, + 0x0a, 0xba, 0x98, 0x42); + +static const struct qtee_service qtee_services[] = { + { "qcom.tz.uefisecapp", + QCOMTEE_UEFI_SEC_UID } +}; + +static void qtee_release_service(struct device *dev) +{ + struct tee_client_device *qtee_service = to_tee_client_device(dev); + + kfree(qtee_service); +} + +/** + * qtee_enumerate_service() - Enumerate a given QTEE service and register + * it on the TEE bus as a TEE client device + * @ctx: TEE context. + * @service_uuid: UUID of the service to be registered on the TEE bus. + * @uid: 32-bit UID used by QTEE to identify the service. + * + * Returns 0 on success and < 0 on failure. + */ +static int qtee_enumerate_service(struct tee_context *ctx, const char *service_name, + const u32 uid) +{ + struct tee_client_device *qtee_service; + uuid_t service_uuid; + int rc; + + if (!is_qcomtee_service_available(ctx, uid)) + return -ENXIO; + + tee_generate_uuid_v5(&service_uuid, &qtee_service_uuid_ns, service_name, + strlen(service_name)); + + qtee_service = kzalloc_obj(*qtee_service); + if (!qtee_service) + return -ENOMEM; + + qtee_service->dev.bus = &tee_bus_type; + qtee_service->dev.release = qtee_release_service; + if (dev_set_name(&qtee_service->dev, "qtee-svc-%pUb", &service_uuid)) { + kfree(qtee_service); + return -ENOMEM; + } + uuid_copy(&qtee_service->id.uuid, &service_uuid); + + rc = device_register(&qtee_service->dev); + if (rc) { + pr_err("QTEE service registration failed, err: %d\n", rc); + put_device(&qtee_service->dev); + kfree(qtee_service); + return rc; + } + + return 0; +} + +/** + * qtee_enumerate_services() - Enumerate all the secure services exposed by QTEE + * from the static 'qtee_services' list and register them on the TEE bus as + * TEE client devices. + * + * Not all versions of QTEE support a given service. Hence, we try to + * enumerate as many services from the 'qtee_services' list as possible. + * Not being able to enumerate a service shouldn't cause the driver probe + * to fail since none of the services in the list are mandatory for + * establishing communication with QTEE. + * @ctx: TEE context. + */ +static void qtee_enumerate_services(struct tee_context *ctx) +{ + int rc; + u32 idx; + + for (idx = 0; idx < ARRAY_SIZE(qtee_services); idx++) { + rc = qtee_enumerate_service(ctx, qtee_services[idx].name, + qtee_services[idx].uid); + if (rc == -ENXIO) + pr_err("QTEE does not implement service %d.\n", + qtee_services[idx].uid); + } +} + +static int qtee_unregister_service(struct device *dev, void *data) +{ + if (!strncmp(dev_name(dev), "qtee-svc", strlen("qtee-svc"))) + device_unregister(dev); + + return 0; +} + +static void qtee_unregister_services(void) +{ + bus_for_each_dev(&tee_bus_type, NULL, NULL, + qtee_unregister_service); +} + static const struct tee_driver_ops qcomtee_ops = { .get_version = qcomtee_get_version, .open = qcomtee_open, @@ -793,6 +944,8 @@ static int qcomtee_probe(struct platform_device *pdev) QTEE_VERSION_GET_MINOR(qcomtee->qtee_version), QTEE_VERSION_GET_PATCH(qcomtee->qtee_version));
+ qtee_enumerate_services(qcomtee->ctx); + return 0;
err_dest_wq: @@ -822,6 +975,7 @@ static void qcomtee_remove(struct platform_device *pdev) { struct qcomtee *qcomtee = platform_get_drvdata(pdev);
+ qtee_unregister_services(); teedev_close_context(qcomtee->ctx); /* Wait for RELEASE operations to be processed for QTEE objects. */ tee_device_unregister(qcomtee->teedev); diff --git a/drivers/tee/qcomtee/core.c b/drivers/tee/qcomtee/core.c index b1cb50e434f0..4e39e867c3e9 100644 --- a/drivers/tee/qcomtee/core.c +++ b/drivers/tee/qcomtee/core.c @@ -896,19 +896,20 @@ qcomtee_object_get_client_env(struct qcomtee_object_invoke_ctx *oic)
struct qcomtee_object * qcomtee_object_get_service(struct qcomtee_object_invoke_ctx *oic, - struct qcomtee_object *client_env, u32 uid) + struct qcomtee_object *client_env, u32 uid, + int *result) { struct qcomtee_arg u[3] = { 0 }; - int ret, result; + int ret;
u[0].b.addr = &uid; u[0].b.size = sizeof(uid); u[0].type = QCOMTEE_ARG_TYPE_IB; u[1].type = QCOMTEE_ARG_TYPE_OO; ret = qcomtee_object_do_invoke(oic, client_env, QCOMTEE_CLIENT_ENV_OPEN, - u, &result); + u, result);
- if (ret || result) + if (ret || *result) return NULL_QCOMTEE_OBJECT;
return u[1].o; diff --git a/drivers/tee/qcomtee/qcomtee.h b/drivers/tee/qcomtee/qcomtee.h index 5d292a2ff83d..44fbed6868ca 100644 --- a/drivers/tee/qcomtee/qcomtee.h +++ b/drivers/tee/qcomtee/qcomtee.h @@ -17,6 +17,8 @@ #define QCOMTEE_OBJREF_FLAG_USER BIT(1) #define QCOMTEE_OBJREF_FLAG_MEM BIT(2)
+#define QTEE_UUID_NS_NAME_SIZE 128 + /* The MSB of the object_id field indicates whether the client is invoking the * object from user context or kernel context. */ @@ -45,6 +47,16 @@ struct qcomtee { u32 qtee_version; };
+/** + * struct qtee_service - A secure service exposed by QTEE identified by a 32-bit UID. + * @name: Name of the QTEE service. + * @uid: 32-bit UID used by QTEE to identify the service. + */ +struct qtee_service { + const char *name; + const u32 uid; +}; + void qcomtee_fetch_async_reqs(struct qcomtee_object_invoke_ctx *oic); struct qcomtee_object *qcomtee_idx_erase(struct qcomtee_object_invoke_ctx *oic, u32 idx); diff --git a/drivers/tee/qcomtee/qcomtee_msg.h b/drivers/tee/qcomtee/qcomtee_msg.h index 878f70178a5b..ecaf8db67d45 100644 --- a/drivers/tee/qcomtee/qcomtee_msg.h +++ b/drivers/tee/qcomtee/qcomtee_msg.h @@ -105,6 +105,7 @@ union qcomtee_msg_arg { #define QTEE_VERSION_GET_MINOR(x) (((x) >> 12) & 0xffU) #define QTEE_VERSION_GET_PATCH(x) ((x) >> 0 & 0xfffU)
+#define QCOMTEE_UEFI_SEC_UID 413 /* Response types as returned from qcomtee_object_invoke_ctx_invoke(). */
/* The message contains a callback request. */ diff --git a/drivers/tee/qcomtee/qcomtee_object.h b/drivers/tee/qcomtee/qcomtee_object.h index 7bd6e23b038c..f4cb9b8fcbd4 100644 --- a/drivers/tee/qcomtee/qcomtee_object.h +++ b/drivers/tee/qcomtee/qcomtee_object.h @@ -316,6 +316,7 @@ qcomtee_object_get_client_env(struct qcomtee_object_invoke_ctx *oic);
struct qcomtee_object * qcomtee_object_get_service(struct qcomtee_object_invoke_ctx *oic, - struct qcomtee_object *client_env, u32 uid); + struct qcomtee_object *client_env, u32 uid, + int *result);
#endif /* QCOMTEE_OBJECT_H */
On Qualcomm SoC based platforms, UEFI stores EFI variables within the Replay Protected Memory Block (RPMB) which is only accessible by the Qualcomm Trusted Execution Environment (QTEE).
For Qualcomm platforms without emulated RPMB support, specifically platforms where RPMB is not located within SPI-NOR storage and instead located on UFS/EMMC storage, non-volatile EFI variables can only be set via a callback request from the UEFI Trusted Application (TA) to the RPMB service running in user-space (within the QTEE supplicant).
Unlike the QCOMTEE driver, the QSEECOM driver (used by the current uefisecapp client driver) does not support callback requests. And on certain Qualcomm platforms such as the RB3Gen2, attempts to access the QSEECOM interface fail due to lack of support within QTEE. On all such platforms, a TEE based uefisecapp client driver must be used to access cached/volatile EFI variables within the uefisecapp TA and ensure persistence of writes to non-volatile EFI variables through the RPMB service hosted in the QTEE supplicant.
Add support for a TEE based uefisecapp client driver which installs efivar operations after obtaining an object reference to the uefisecapp service. This enables the kernel/user-space to access/modify both volatile EFI vars stored by the Secure Application (in-memory) and non-volatile ones stored within RPMB.
Signed-off-by: Harshal Dev harshal.dev@oss.qualcomm.com --- MAINTAINERS | 7 + drivers/firmware/qcom/Kconfig | 24 ++ drivers/firmware/qcom/Makefile | 1 + drivers/firmware/qcom/qcom_tee_uefisecapp.c | 525 ++++++++++++++++++++++++++++ drivers/firmware/qcom/qcom_tee_uefisecapp.h | 120 +++++++ 5 files changed, 677 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS index 10d12b51b1f6..e8316007370f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22018,6 +22018,13 @@ L: linux-arm-msm@vger.kernel.org S: Maintained F: drivers/firmware/qcom/qcom_qseecom_uefisecapp.c
+QUALCOMM TEE UEFISECAPP DRIVER +M: Harshal Dev harshal.dev@oss.qualcomm.com +L: linux-arm-msm@vger.kernel.org +S: Maintained +F: drivers/firmware/qcom/qcom_tee_uefisecapp.c +F: drivers/firmware/qcom/qcom_tee_uefisecapp.h + QUALCOMM RMNET DRIVER M: Subash Abhinov Kasiviswanathan subash.a.kasiviswanathan@oss.qualcomm.com M: Sean Tranchetti sean.tranchetti@oss.qualcomm.com diff --git a/drivers/firmware/qcom/Kconfig b/drivers/firmware/qcom/Kconfig index b477d54b495a..20ce8b58e490 100644 --- a/drivers/firmware/qcom/Kconfig +++ b/drivers/firmware/qcom/Kconfig @@ -74,4 +74,28 @@ config QCOM_QSEECOM_UEFISECAPP Select Y here to provide access to EFI variables on the aforementioned platforms.
+config QCOM_TEE_UEFISECAPP + tristate "Qualcomm TEE UEFI Secure App client driver" + depends on QCOMTEE + depends on EFI + depends on !QCOM_QSEECOM_UEFISECAPP + help + On Qualcomm SoC based platforms without emulated RPMB support, + specifically platforms where RPMB is not present within SPI-NOR storage + and instead located on UFS/EMMC storage, non-volatile EFI variables can + only be set via a callback request from the UEFI Secure Application to + the RPMB service running in user-space (within the QTEE supplicant: + github.com/qualcomm/minkipc). Unlike the QCOMTEE driver, the QSEECOM + driver used by the QSEECOM based uefisecapp does not support callback + requests. And so on these platforms, the TEE based uefisecapp client + driver must be used to ensure persistence of non-volatile EFI variables + via writes through the RPMB service hosted in the QTEE supplicant. + + This module provides a TEE client driver for uefisecapp, installing efivar + operations to allow the kernel and user-space access to EFI variables. + + Select m here to provide access to EFI variables on the aforementioned + platforms if your Linux distribution has QTEE supplicant installed and + running. + endmenu diff --git a/drivers/firmware/qcom/Makefile b/drivers/firmware/qcom/Makefile index 0be40a1abc13..d780490b2865 100644 --- a/drivers/firmware/qcom/Makefile +++ b/drivers/firmware/qcom/Makefile @@ -8,3 +8,4 @@ qcom-scm-objs += qcom_scm.o qcom_scm-smc.o qcom_scm-legacy.o obj-$(CONFIG_QCOM_TZMEM) += qcom_tzmem.o obj-$(CONFIG_QCOM_QSEECOM) += qcom_qseecom.o obj-$(CONFIG_QCOM_QSEECOM_UEFISECAPP) += qcom_qseecom_uefisecapp.o +obj-$(CONFIG_QCOM_TEE_UEFISECAPP) += qcom_tee_uefisecapp.o diff --git a/drivers/firmware/qcom/qcom_tee_uefisecapp.c b/drivers/firmware/qcom/qcom_tee_uefisecapp.c new file mode 100644 index 000000000000..9a5a6f145a9f --- /dev/null +++ b/drivers/firmware/qcom/qcom_tee_uefisecapp.c @@ -0,0 +1,525 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include <linux/efi.h> +#include <linux/tee.h> +#include <linux/tee_drv.h> +#include <linux/ucs2_string.h> +#include "qcom_tee_uefisecapp.h" + +static struct qcomtee_uefisec_app uefisec_app; + +static int qcuefi_get_variable(struct tee_param_ubuf in_variable, efi_guid_t *guid, + struct tee_param_ubuf in_attributes, + struct tee_param_ubuf *data, + u32 *out_attributes, u32 *out_errno) +{ + int ret; + struct tee_ioctl_object_invoke_arg inv_arg; + u64 obj_id = uefisec_app.uefisec_svc_obj.id; + u32 nparams = 5; + struct tee_param param[nparams]; + + struct { + efi_guid_t guid; + u32 in_data_size; + } in_cong = { 0 }; + + struct { + u32 out_data_size; + u32 attributes; + u32 errno; + } out_cong = { 0 }; + + in_cong.guid = *guid; + in_cong.in_data_size = data->size; + + memset(&inv_arg, 0, sizeof(inv_arg)); + memset(¶m, 0, sizeof(param)); + + SET_INVOKE_ARG(inv_arg, obj_id, QCOMTEE_UEFI_SEC_OP_GET_VAR, nparams); + SET_TEE_PARAM_UBUF(param[0], UBUF_INPUT, TEE_PARAM_UBUF(in_cong)); + SET_TEE_PARAM_UBUF(param[1], UBUF_INPUT, in_variable); + SET_TEE_PARAM_UBUF(param[2], UBUF_INPUT, in_attributes); + SET_TEE_PARAM_UBUF(param[3], UBUF_OUTPUT, TEE_PARAM_UBUF(out_cong)); + SET_TEE_PARAM_UBUF(param[4], UBUF_OUTPUT, *data); + + ret = tee_client_object_invoke_func(uefisec_app.ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) { + dev_err(uefisec_app.dev, "QCOMTEE_UEFI_SEC_OP_GET_VAR invoke ret: %d, err: 0x%x\n", + ret, inv_arg.ret); + return ret ?: inv_arg.ret; + } + + data->size = out_cong.out_data_size; + *out_attributes = out_cong.attributes; + *out_errno = out_cong.errno; + + return ret; +} + +static efi_status_t qcomtee_uefi_get_variable(efi_char16_t *name, efi_guid_t *guid, + u32 *attr, unsigned long *data_size, + void *data) +{ + int ret; + u32 in_attr, out_attributes, out_errno; + struct tee_param_ubuf in_data, in_var, in_attributes; + + if (!name || !guid) + return EFI_INVALID_PARAMETER; + + /* 'attr' can be NULL, however an input attribute is always expected + * by UefiSecApp TA + */ + in_attr = 0; + if (attr) + in_attr = *attr; + + in_data = (struct tee_param_ubuf){ .addr = data, *data_size }; + in_var = (struct tee_param_ubuf){ .addr = name, + (ucs2_strlen(name) + 1) * sizeof(*name) }; + in_attributes = (struct tee_param_ubuf){ .addr = &in_attr, sizeof(u32) }; + + /* On SUCCESS, 'data' member of 'in_data' has already been updated. */ + ret = qcuefi_get_variable(in_var, guid, in_attributes, &in_data, + &out_attributes, &out_errno); + + if (ret) + return EFI_DEVICE_ERROR; + + if (!out_errno || out_errno == QCOMTEE_UEFI_SEC_ERROR_SIZE_OUT) { + /* If 'attr' is NULL 'out_attributes' is not updated. */ + if (attr) + *attr = out_attributes; + + *data_size = in_data.size; + } + + return uefisecapp_err_to_efi_status(out_errno); +} + +static int qcuefi_set_variable(struct tee_param_ubuf in_variable, efi_guid_t *guid, + u32 attributes, struct tee_param_ubuf data, + u32 *out_errno) +{ + int ret; + struct tee_ioctl_object_invoke_arg inv_arg; + u64 obj_id = uefisec_app.uefisec_svc_obj.id; + u32 nparams = 4; + struct tee_param param[nparams]; + + struct { + efi_guid_t guid; + u32 attributes; + u32 in_data_size; + } in_cong = { 0 }; + + in_cong.guid = *guid; + in_cong.attributes = attributes; + in_cong.in_data_size = data.size; + + memset(&inv_arg, 0, sizeof(inv_arg)); + memset(¶m, 0, sizeof(param)); + + SET_INVOKE_ARG(inv_arg, obj_id, QCOMTEE_UEFI_SEC_OP_SET_VAR, nparams); + SET_TEE_PARAM_UBUF(param[0], UBUF_INPUT, TEE_PARAM_UBUF(in_cong)); + SET_TEE_PARAM_UBUF(param[1], UBUF_INPUT, in_variable); + SET_TEE_PARAM_UBUF(param[2], UBUF_INPUT, data); + SET_TEE_PARAM_UBUF(param[3], UBUF_OUTPUT, TEE_PARAM_UBUF(*out_errno)); + + ret = tee_client_object_invoke_func(uefisec_app.ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) { + dev_err(uefisec_app.dev, "QCOMTEE_UEFI_SEC_OP_SET_VAR invoke ret: %d, err: 0x%x\n", + ret, inv_arg.ret); + return ret ?: inv_arg.ret; + } + + return ret; +} + +static efi_status_t qcomtee_uefi_set_variable(efi_char16_t *name, efi_guid_t *guid, + u32 attr, unsigned long data_size, + void *data) +{ + int ret; + u32 out_errno; + struct tee_param_ubuf in_data, in_var; + + if (!name || !guid) + return EFI_INVALID_PARAMETER; + + in_data = (struct tee_param_ubuf){ .addr = data, data_size }; + in_var = (struct tee_param_ubuf){ .addr = name, + (ucs2_strlen(name) + 1) * sizeof(*name) }; + + ret = qcuefi_set_variable(in_var, guid, attr, in_data, &out_errno); + if (ret) + return EFI_DEVICE_ERROR; + + return uefisecapp_err_to_efi_status(out_errno); +} + +static int qcuefi_get_next_variable(struct tee_param_ubuf in_variable, efi_guid_t *guid, + struct tee_param_ubuf *out_variable, + efi_guid_t *out_vendor_guid, u32 *out_errno) +{ + int ret; + struct tee_ioctl_object_invoke_arg inv_arg; + u64 obj_id = uefisec_app.uefisec_svc_obj.id; + u32 nparams = 4; + struct tee_param param[nparams]; + + struct { + efi_guid_t guid; + u32 in_data_size; + } in_cong = { 0 }; + + struct { + efi_guid_t guid; + u32 out_data_size; + u32 errno; + } out_cong = { 0 }; + + /* Pass size of available buffer */ + in_cong.in_data_size = out_variable->size; + in_cong.guid = *guid; + + memset(&inv_arg, 0, sizeof(inv_arg)); + memset(¶m, 0, sizeof(param)); + + SET_INVOKE_ARG(inv_arg, obj_id, QCOMTEE_UEFI_SEC_OP_GET_NEXT_VAR_NAME, nparams); + SET_TEE_PARAM_UBUF(param[0], UBUF_INPUT, TEE_PARAM_UBUF(in_cong)); + SET_TEE_PARAM_UBUF(param[1], UBUF_INPUT, in_variable); + SET_TEE_PARAM_UBUF(param[2], UBUF_OUTPUT, TEE_PARAM_UBUF(out_cong)); + SET_TEE_PARAM_UBUF(param[3], UBUF_OUTPUT, *out_variable); + + ret = tee_client_object_invoke_func(uefisec_app.ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) { + dev_err(uefisec_app.dev, "QCOMTEE_UEFI_SEC_OP_GET_NEXT_VAR_NAME invoke ret: %d, err: 0x%x\n", + ret, inv_arg.ret); + return ret ?: inv_arg.ret; + } + + /* UefiSecApp TA does not touch 'out_variable.size'. Update it here. + * On SUCCESS (!out_errno), 'out_data_size' is length of name in 'out_variable.addr'. + * On failure (out_errno == QCOMTEE_UEFI_SEC_ERROR_SIZE_OUT), 'out_data_size' is + * actual name length. + * Otherwise, it's undefined. + */ + out_variable->size = out_cong.out_data_size; + *out_vendor_guid = out_cong.guid; + *out_errno = out_cong.errno; + + return ret; +} + +static efi_status_t qcomtee_uefi_get_next_variable(unsigned long *name_size, + efi_char16_t *name, + efi_guid_t *guid) +{ + int ret; + u32 out_errno; + efi_guid_t out_guid; + struct tee_param_ubuf in_var, out_var; + + if (!name_size || !name || !guid) + return EFI_INVALID_PARAMETER; + + if (*name_size == 0) + return EFI_INVALID_PARAMETER; + + /* For 'in_var', 'name_size' is not necessarily size of 'name'; + * could be size of buffer where 'name' has been stored. TA expects a + * NULL-terminated string in 'name' and ignores the size. + * For 'out_var', 'name_size' is size of buffer pointed by 'name'. + */ + in_var = (struct tee_param_ubuf){ .addr = name, *name_size }; + out_var = (struct tee_param_ubuf){ .addr = name, *name_size }; + + ret = qcuefi_get_next_variable(in_var, guid, &out_var, &out_guid, + &out_errno); + if (ret) + return EFI_DEVICE_ERROR; + + if (!out_errno) + *guid = out_guid; + + if (!out_errno || out_errno == QCOMTEE_UEFI_SEC_ERROR_SIZE_OUT) + *name_size = out_var.size; + + /* On SUCCESS, 'name' stores the next variable name. */ + return uefisecapp_err_to_efi_status(out_errno); +} + +static int qcuefi_query_variable_info(u32 attributes, + u64 *maximum_variable_storage_size, + u64 *remaining_variable_storage_size, + u64 *maximum_variable_size, u32 *out_errno) +{ + int ret; + struct tee_ioctl_object_invoke_arg inv_arg; + u64 obj_id = uefisec_app.uefisec_svc_obj.id; + u32 nparams = 2; + struct tee_param param[nparams]; + + struct { + u64 max_var_storage_size; + u64 remaining_var_storage_size; + u64 maximum_var_size; + u32 errno; + } out_cong = { 0 }; + + memset(&inv_arg, 0, sizeof(inv_arg)); + memset(¶m, 0, sizeof(param)); + + SET_INVOKE_ARG(inv_arg, obj_id, QCOMTEE_UEFI_SEC_OP_QUERY_VAR_INFO, nparams); + SET_TEE_PARAM_UBUF(param[0], UBUF_INPUT, TEE_PARAM_UBUF(attributes)); + SET_TEE_PARAM_UBUF(param[1], UBUF_OUTPUT, TEE_PARAM_UBUF(out_cong)); + + ret = tee_client_object_invoke_func(uefisec_app.ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) { + dev_err(uefisec_app.dev, "QCOMTEE_UEFI_SEC_OP_QUERY_VAR_INFO invoke ret: %d, err: 0x%x\n", + ret, inv_arg.ret); + return ret ?: inv_arg.ret; + } + + *maximum_variable_storage_size = out_cong.max_var_storage_size; + *remaining_variable_storage_size = out_cong.remaining_var_storage_size; + *maximum_variable_size = out_cong.maximum_var_size; + *out_errno = out_cong.errno; + + return ret; +} + +static efi_status_t qcomtee_uefi_query_variable_info(u32 attr, u64 *storage_space, + u64 *remaining_space, + u64 *max_variable_size) +{ + int ret; + u32 out_errno; + u64 maximum_variable_storage_size; + u64 remaining_variable_storage_size; + u64 maximum_variable_size; + + if (!storage_space || !remaining_space || !max_variable_size) + return EFI_INVALID_PARAMETER; + + ret = qcuefi_query_variable_info(attr, + &maximum_variable_storage_size, + &remaining_variable_storage_size, + &maximum_variable_size, + &out_errno); + + if (ret) + return EFI_DEVICE_ERROR; + + if (!out_errno) { + *storage_space = maximum_variable_storage_size; + *remaining_space = remaining_variable_storage_size; + *max_variable_size = maximum_variable_size; + } + + return uefisecapp_err_to_efi_status(out_errno); +} + +/** + * qcomtee_release_object() - Release an object returned by QTEE. + * + * Each object returned by QTEE repesents a secure service exposed to the + * client. Whenever an secure service is opened, QTEE may allocate resources + * on the client's behalf. Therefore, once the client is done accessing the + * secure service, the object representing it should be explicitly released + * so that QTEE can release the associated resources as well. + * + * @ctx: TEE context. + * @object: The object to release. + */ +static void qcomtee_release_object(struct tee_context *ctx, + struct tee_param_objref object) +{ + struct tee_ioctl_object_invoke_arg inv_arg; + + memset(&inv_arg, 0, sizeof(inv_arg)); + SET_INVOKE_ARG(inv_arg, object.id, QCOMTEE_MSG_OBJECT_OP_RELEASE, 0); + tee_client_object_invoke_func(ctx, &inv_arg, NULL); +} + +/** + * qcomtee_get_uefisec_svc_obj() - Get a UEFI Secure App service object to + * begin communication with the service. + * @ctx: TEE context. + * @client_env_obj: The client environment object returned earlier by QTEE. + * @uefisec_svc_obj: The UEFI Secure App service object. + * + * Returns 0 on success. + * Returns < 0 if client environment object invocation failed. + * Returns > 0 if client environment invocation was success but UEFI Secure App + * service object could not be returned for some other reason (represented by the + * returned value) + */ +static int qcomtee_get_uefisec_svc_obj(struct tee_context *ctx, + struct tee_param_objref client_env_obj, + struct tee_param_objref *uefisec_svc_obj) +{ + int ret; + struct tee_ioctl_object_invoke_arg inv_arg; + u64 obj_id = client_env_obj.id; + u32 nparams = 2; + struct tee_param param[nparams]; + u32 uefisec_uid = QCOMTEE_UEFI_SEC_UID; + + memset(&inv_arg, 0, sizeof(inv_arg)); + memset(¶m, 0, sizeof(param)); + + SET_INVOKE_ARG(inv_arg, obj_id, QCOMTEE_OP_CLIENT_ENV_OPEN, nparams); + SET_TEE_PARAM_UBUF(param[0], UBUF_INPUT, TEE_PARAM_UBUF(uefisec_uid)); + SET_TEE_PARAM_OBJREF(param[1], OBJREF_OUTPUT, 0, 0); + + ret = tee_client_object_invoke_func(ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) { + dev_err(uefisec_app.dev, "QCOMTEE_CLIENT_ENV_OPEN invoke ret: %d, err: 0x%x\n", + ret, inv_arg.ret); + return ret ?: inv_arg.ret; + } + + *uefisec_svc_obj = param[1].u.objref; + return ret; +} + +/** + * qcomtee_get_client_env_obj() - Get a client environment object to begin + * object exchange with QTEE. + * @ctx: TEE context. + * @client_env_obj: The client environment object returned by QTEE. + * + * Returns 0 on success. + * Returns < 0 if root object invocation failed. + * Returns > 0 if root object invocation was success but client environment + * object could not be returned for some other reason (represented by the + * returned value) + */ +static int qcomtee_get_client_env_obj(struct tee_context *ctx, + struct tee_param_objref *client_env_obj) +{ + int ret; + struct tee_ioctl_object_invoke_arg inv_arg; + u32 nparams = 2; + struct tee_param param[nparams]; + + memset(&inv_arg, 0, sizeof(inv_arg)); + memset(¶m, 0, sizeof(param)); + + SET_INVOKE_ARG(inv_arg, TEE_OBJREF_NULL, + QCOMTEE_ROOT_OP_REG_WITH_CREDENTIALS, nparams); + SET_TEE_PARAM_OBJREF(param[0], OBJREF_INPUT, TEE_OBJREF_NULL, 0); + SET_TEE_PARAM_OBJREF(param[1], OBJREF_OUTPUT, 0, 0); + + ret = tee_client_object_invoke_func(ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) { + dev_err(uefisec_app.dev, "QCOMTEE_ROOT_OP_REG_WITH_CREDENTIALS invoke ret: %d, err: 0x%x\n", + ret, inv_arg.ret); + return ret ?: inv_arg.ret; + } + + *client_env_obj = param[1].u.objref; + return ret; +} + +static const struct efivar_operations qcom_efivar_ops = { + .get_variable = qcomtee_uefi_get_variable, + .set_variable = qcomtee_uefi_set_variable, + .get_next_variable = qcomtee_uefi_get_next_variable, + .query_variable_info = qcomtee_uefi_query_variable_info, +}; + +static int qcomtee_ctx_match(struct tee_ioctl_version_data *ver, + const void *data) +{ + return (ver->impl_id == TEE_IMPL_ID_QTEE); +} + +static int qcomtee_uefisecapp_probe(struct tee_client_device *tee_dev) +{ + int ret, err; + struct tee_param_objref client_env_obj; + struct tee_param_objref uefisec_svc_obj; + + uefisec_app.dev = &tee_dev->dev; + /* Open context with QCOMTEE driver */ + uefisec_app.ctx = tee_client_open_context(NULL, qcomtee_ctx_match, NULL, + NULL); + if (IS_ERR(uefisec_app.ctx)) + return -ENODEV; + + /* Obtain a reference to client_env object to begin object exchange + * with QTEE + */ + ret = qcomtee_get_client_env_obj(uefisec_app.ctx, &client_env_obj); + if (ret) { + err = -EINVAL; + goto err_get_client_env; + } + + /* Obtain a reference to the uefisec_svc object which provides access to + * the EFI var storage. + */ + ret = qcomtee_get_uefisec_svc_obj(uefisec_app.ctx, client_env_obj, + &uefisec_svc_obj); + if (ret) { + err = -EINVAL; + goto err_get_uefisec_svc; + } + uefisec_app.uefisec_svc_obj = uefisec_svc_obj; + + ret = efivars_register(&uefisec_app.efivars, &qcom_efivar_ops); + if (ret) { + err = ret; + goto err_efi_vars_reg; + } + + /* We don't need to keep a reference to this object anymore, we only + * needed it to obtain the uefisec_svc object. + */ + qcomtee_release_object(uefisec_app.ctx, client_env_obj); + return 0; + +err_efi_vars_reg: + qcomtee_release_object(uefisec_app.ctx, uefisec_svc_obj); +err_get_uefisec_svc: + qcomtee_release_object(uefisec_app.ctx, client_env_obj); +err_get_client_env: + tee_client_close_context(uefisec_app.ctx); + + return err; +} + +static void qcomtee_uefisecapp_remove(struct tee_client_device *tee_dev) +{ + efivars_unregister(&uefisec_app.efivars); + qcomtee_release_object(uefisec_app.ctx, uefisec_app.uefisec_svc_obj); + tee_client_close_context(uefisec_app.ctx); +} + +static const struct tee_client_device_id qcomtee_uefisecapp_id_table[] = { + {UEFISECAPP_UUID}, + {} +}; +MODULE_DEVICE_TABLE(tee, qcomtee_uefisecapp_id_table); + +static struct tee_client_driver qcomtee_uefisecapp_driver = { + .id_table = qcomtee_uefisecapp_id_table, + .probe = qcomtee_uefisecapp_probe, + .remove = qcomtee_uefisecapp_remove, + .driver = { + .name = "qcom-tee-uefisecapp", + }, +}; + +module_tee_client_driver(qcomtee_uefisecapp_driver); + +MODULE_AUTHOR("Qualcomm"); +MODULE_DESCRIPTION("TEE client driver for Qualcomm TEE UEFI Secure App"); +MODULE_LICENSE("GPL"); diff --git a/drivers/firmware/qcom/qcom_tee_uefisecapp.h b/drivers/firmware/qcom/qcom_tee_uefisecapp.h new file mode 100644 index 000000000000..a014c18cfed0 --- /dev/null +++ b/drivers/firmware/qcom/qcom_tee_uefisecapp.h @@ -0,0 +1,120 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef QCOM_TEE_UEFISECAPP_H +#define QCOM_TEE_UEFISECAPP_H + +#define QCOMTEE_OP_CLIENT_ENV_OPEN 0 +#define QCOMTEE_ROOT_OP_REG_WITH_CREDENTIALS 5 + +/* Each service exposed by QTEE is identified by a 32-bit UID */ +#define QCOMTEE_UEFI_SEC_UID 413 + +/* Operations supported by the UEFI Sec App service */ +#define QCOMTEE_UEFI_SEC_OP_GET_VAR 0 +#define QCOMTEE_UEFI_SEC_OP_SET_VAR 1 +#define QCOMTEE_UEFI_SEC_OP_QUERY_VAR_INFO 2 +#define QCOMTEE_UEFI_SEC_OP_GET_NEXT_VAR_NAME 3 + +/* Error codes returned by the UEFI Sec App service */ +#define QCOMTEE_UEFI_SEC_SUCCESS 0 +#define QCOMTEE_UEFI_SEC_ERROR_INVALID_PARAMETER 10 +#define QCOMTEE_UEFI_SEC_ERROR_UNSUPPORTED 11 +#define QCOMTEE_UEFI_SEC_ERROR_WRITE_PROTECTED 12 +#define QCOMTEE_UEFI_SEC_ERROR_SECURITY_VIOLATION 13 +#define QCOMTEE_UEFI_SEC_ERROR_DEVICE_ERROR 14 +#define QCOMTEE_UEFI_SEC_ERROR_OUT_OF_RESOURCES 15 +#define QCOMTEE_UEFI_SEC_ERROR_VOLUME_CORRUPTED 16 +#define QCOMTEE_UEFI_SEC_ERROR_SIZE_OUT 17 +#define QCOMTEE_UEFI_SEC_ERROR_NOT_FOUND 18 +#define QCOMTEE_UEFI_SEC_ERROR_ALREADY_STARTED 19 + +/* Operations for objects are 32-bit. QCOMTEE transport uses the upper 16 bits. */ +#define QCOMTEE_MSG_OBJECT_OP_MASK GENMASK(15, 0) +#define QCOMTEE_MSG_OBJECT_OP_RELEASE (QCOMTEE_MSG_OBJECT_OP_MASK - 0) + +/** + * struct qcomtee_uefisec_app - An instance of UEFI Secure Application. + * @dev: TEE client device on the TEE bus which represents uefisecapp. + * @ctx: The context opened with the TEE subsystem by the uefisecapp client. + * @uefisec_svc_obj: A TEE object representing the uefisecapp service. + * @efivars: EFI variables registered with the EFI subsystem. + */ +struct qcomtee_uefisec_app { + struct device *dev; + struct tee_context *ctx; + struct tee_param_objref uefisec_svc_obj; + struct efivars efivars; +}; + +#define UEFISECAPP_UUID \ + UUID_INIT(0x01f95dcd, 0x2d7e, 0x58be, \ + 0xa1, 0x43, 0x81, 0x32, 0xa1, 0x72, 0xdb, 0x7d) + +/* Short-hands for these long attribute names */ +#define UBUF_INPUT TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT +#define UBUF_OUTPUT TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_OUTPUT +#define OBJREF_INPUT TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT +#define OBJREF_OUTPUT TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT + +/* Init instance of 'struct tee_param_objref'. */ +#define SET_TEE_PARAM_OBJREF(param, attri, obj_id, obj_flag) do { \ + (param).attr = (attri); \ + (param).u.objref.id = (obj_id); \ + (param).u.objref.flags = (obj_flag); \ + } while (0) + +/* Init instance of 'struct tee_param_ubuf'. */ +#define SET_TEE_PARAM_UBUF(param, attri, ubuff) do { \ + (param).attr = (attri); \ + (param).u.ubuf = (ubuff); \ + } while (0) + +#define TEE_PARAM_UBUF(x) ((struct tee_param_ubuf){ .addr = &(x), sizeof(x) }) + +#define SET_INVOKE_ARG(arg, object_id, opp, nparam) do { \ + (arg).id = (object_id); \ + (arg).op = (opp); \ + (arg).num_params = (nparam); \ + } while (0) + +static inline efi_status_t uefisecapp_err_to_efi_status(u32 err) +{ + switch (err) { + case QCOMTEE_UEFI_SEC_SUCCESS: + return EFI_SUCCESS; + + case QCOMTEE_UEFI_SEC_ERROR_INVALID_PARAMETER: + return EFI_INVALID_PARAMETER; + + case QCOMTEE_UEFI_SEC_ERROR_UNSUPPORTED: + return EFI_UNSUPPORTED; + + case QCOMTEE_UEFI_SEC_ERROR_WRITE_PROTECTED: + return EFI_WRITE_PROTECTED; + + case QCOMTEE_UEFI_SEC_ERROR_SECURITY_VIOLATION: + return EFI_SECURITY_VIOLATION; + + case QCOMTEE_UEFI_SEC_ERROR_DEVICE_ERROR: + return EFI_DEVICE_ERROR; + + case QCOMTEE_UEFI_SEC_ERROR_OUT_OF_RESOURCES: + return EFI_OUT_OF_RESOURCES; + + case QCOMTEE_UEFI_SEC_ERROR_SIZE_OUT: + return EFI_BUFFER_TOO_SMALL; + + case QCOMTEE_UEFI_SEC_ERROR_NOT_FOUND: + return EFI_NOT_FOUND; + + /* No matching on EFI_* list. */ + case QCOMTEE_UEFI_SEC_ERROR_ALREADY_STARTED: /* EFI_ALREADY_STARTED. */ + case QCOMTEE_UEFI_SEC_ERROR_VOLUME_CORRUPTED: /* EFI_VOLUME_CORRUPTED. */ + default: + return EFI_DEVICE_ERROR; + } +} +#endif /* QCOM_TEE_UEFISECAPP_H */
op-tee@lists.trustedfirmware.org