From: Amirreza Zarrabi <amirreza.zarrabi@oss.qualcomm.com>
To: Harshal Dev <harshal.dev@oss.qualcomm.com>
Cc: Kuldeep Singh <kuldeep.singh@oss.qualcomm.com>,
Basant Kumar <basantk@qti.qualcomm.com>,
Apurupa Pattapu <apurupa@qti.qualcomm.com>,
Arun Kumar Neelakantam <aneelaka@qti.qualcomm.com>,
op-tee@lists.trustedfirmware.org, linux-kernel@vger.kernel.org,
linux-arm-msm@vger.kernel.org,
Jens Wiklander <jens.wiklander@oss.qualcomm.com>,
Sumit Garg <sumit.garg@kernel.org>,
Bjorn Andersson <andersson@kernel.org>,
Konrad Dybcio <konradybcio@kernel.org>,
Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>,
Krzysztof Kozlowski <krzk@kernel.org>
Subject: Re: [PATCH v5 6/7] firmware: qcom: Add support for TEE based EFI-var client driver
Date: Thu, 5 Nov 2026 16:54:05 +1100 [thread overview]
Message-ID: <af92dbae-701c-480c-b7c7-7bc7bf2e6fc9@oss.qualcomm.com> (raw)
In-Reply-To: <eddd891e-c23f-4977-9f40-9a41222f5bf2@oss.qualcomm.com>
Hi Harshal,
On 11/3/2026 7:35 PM, Harshal Dev wrote:
> Hi Amir,
>
> On 26-10-2026 10:14 am, Amirreza Zarrabi wrote:
>> Hi Harshal,
>>
>> On 10/15/2026 10:39 PM, Harshal Dev wrote:
>>> On Qualcomm SoC based platforms, UEFI stores EFI variables within the
>>> Replay Protected Memory Block (RPMB) located within either the UFS,
>>> eMMC or SPI-NOR storage. The RPMB key which is one-time programmed into
>>> the storage controller to allow authentication of the RPMB frames is
>>> generated by and only available to the Qualcomm Trusted Execution
>>> Environment (QTEE).
>>>
>>> The legacy QSEECOM protocol used for communicating with the QTEE is
>>> deprecated and replaced with the use-case agnostic SMCInvoke protocol
>>> starting with the Qualcomm SM8x50 series. On platforms where the QSEECOM
>>> driver still probes, it does not support a listener interface with QTEE
>>> to enable writing of non-volatile EFI variables to the RPMB for UFS and
>>> eMMC storage.
>>> Therefore on such platforms, a TEE client driver which communicates with
>>> QTEE via the SMCInvoke protocol implemented by the QCOMTEE driver (and
>>> registered with the TEE subsystem) must be used to update such EFI
>>> variables.
>>>
>>> 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 or modify both volatile EFI
>>> variables 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 | 6 +
>>> drivers/firmware/qcom/Kconfig | 31 ++
>>> drivers/firmware/qcom/Makefile | 1 +
>>> drivers/firmware/qcom/qcom_tee_uefisecapp.c | 644 ++++++++++++++++++++++++++++
>>> 4 files changed, 682 insertions(+)
>>>
>
> [...]
>
>>> diff --git a/drivers/firmware/qcom/qcom_tee_uefisecapp.c b/drivers/firmware/qcom/qcom_tee_uefisecapp.c
>>> new file mode 100644
>>> index 000000000000..bcd025f929fc
>>> --- /dev/null
>>> +++ b/drivers/firmware/qcom/qcom_tee_uefisecapp.c
>>> @@ -0,0 +1,644 @@
>>> +// 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>
>
> [...]
>
>>> +
>>> +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
>>> + */
>>
>> You are enforceing a poliy on the arguments of a cb which is called publicly, are you
>> sure all callers understand that this output value is optional? it is not specified
>> anywhere as far as I can see. This seesm to be a TA thing.
>>
>
> I think the way this comment is written is a bit misleading. The comment is not about
> the output value of attr, it is about the input value passed to this callback.
>
> The callers of this callback either pass a NULL value for attribute or a valid value.
> When they pass NULL, they ignore the output since it's expected to not be set by the
> implementation: https://elixir.bootlin.com/linux/v7.3/source/fs/efivarfs/vars.c#L505
>
> When they pass a valid value, they expect a valid output in return and operate on it:
> https://elixir.bootlin.com/linux/v7.3/source/fs/efivarfs/file.c#L108
> https://elixir.bootlin.com/linux/v7.3/source/fs/efivarfs/vars.c#L531
> So the value is not optional, it is set or not set as per the expectation of the
> caller.
>
>>> + in_attr = 0;
>
> However, the SMCInvoke transport here needs this to not be NULL, and so we force
> set a 0 here which is interpreted corrected by the TA as attribute not set.
>
> I will re-phrase this comment to avoid ambiguity.
>
I do not believe it is comment issue. I understand attr is an optional output value
which could be NULL. However, this implementation also uses attr as input.
In your provided link:
https://elixir.bootlin.com/linux/v7.3/source/fs/efivarfs/file.c#L108
attr that passed to this function is from stack and uninitialized, so the
TA will get garbage in in_attr based on bellow code. It this acceptable?
>>> + 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 const struct efivar_operations qcom_efivar_ops = {
>>> + .get_variable = qcomtee_uefi_get_variable,
>>> + .set_variable = qcomtee_uefi_set_variable,
>>
>> Not sure how much it is relevant, but I was looking into the efi and in
>> efivar_set_variable_locked, saw this comment "If no _nonblocking variant exists,
>> the ordinary one is assumed to be non-blocking." which in not the case fo us.
>>
>> Does it make sense to introduce something like
>> qcomtee_uefi_set_variable_nonblocking and return UNSUPPORTED!?
>>
>
> Ack, this ensures that if a caller expects the set_variable() to land in a
> non-blocking call, they get returned -ENOTSUPPORTED instead of being misled.
> As far as I can see only the x86 pstore implementation explicitly requests
> a call into a non-blocking variant:
> https://elixir.bootlin.com/linux/v7.3/source/drivers/firmware/efi/efi-pstore.c#L227
>
>>> + .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;
>>> +
>>> + ret = qcomtee_get_client_env_obj(uefisec_app.ctx, &client_env_obj);
>>> + if (ret) {
>>> + err = -EINVAL;
>>> + goto err_get_client_env;
>>> + }
>>> +
>>> + 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);
>>
>> I still have concerns about the removal path. efivars_unregister() acquires the
>> semaphore interruptibly and can return -EINTR, for example when a signal arrives while
>> it waits for an active EFI operation. Its return value is ignored here, so removal can
>> release the service object and close the TEE context while the EFI provider remains registered.
>>
>
> This is a valid point. But it seems like no easy solution exists here. The driver core
> doesn't check the value being returned from remove(), so it will always detach the driver from
> the device.
>
> Currently when -EINTR is returned, the driver is removed but __efivar->ops remain set, so a
> subsequent call to __efivars->ops would cause a kernel panic/crash. If we check for error here
> and return without releasing the service object/TEE context, we still have the same issues but
> now also a leak; QTEE's object table still has the service object and the TEE context remains
> allocated (while the driver is removed and __efivars->ops remain set to a random pointers).
>
> Everyone else is also suffering the same race:
> https://elixir.bootlin.com/linux/v7.3/source/drivers/firmware/efi/stmm/tee_stmm_efi.c#L518
> https://elixir.bootlin.com/linux/v7.3/source/drivers/firmware/qcom/qcom_qseecom_uefisecapp.c#L842
> https://elixir.bootlin.com/linux/v7.3/source/drivers/firmware/google/gsmi.c#L1075
>
> This is a framework level issue, where a fail-safe fix is to use down() instead of down_interruptible()
> in efivar_unregister() and pray that whoever is invoking the callback comes out soon. But at
> least it replaces a sure-shot kernel panic with a low probability deadlock which can be
> diagnosed.
>
> I thought about other ways, like using try_module_get() from either the callbacks or the
> efivar_register/unregister(), but neither solve this.
> I can try initiating a discussion on this separately on the efivar mailing list, unless there
> are any other innovative suggestions for solving it from within the driver. :)
>
> Let me know your thoughts.
>
>> Separately, efivar_lock() appears to have an existing bug: it checks __efivars->ops
>> without first checking whether __efivars is NULL. Successful unregister sets that pointer to NULL,
>> so subsequent access through an existing efivarfs can dereference NULL.
>> I Feel, that needs an EFI-core fix as well. what do you think?
>>
>
> Ack, this is true and a fix is straightforward. I will send a patch separately if someone hasn't
> already sent one.
>
> Regards,
> Harshal
>
Yeah, you are right. Let's ignore it for now.
Best Regards,
Amir
>>
>> - Amir
>>
next prev parent reply other threads:[~2026-11-05 5:54 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-15 11:39 [PATCH v5 0/7] Add TEE based client driver for UEFI Secure Application Harshal Dev
2026-10-15 11:39 ` [PATCH v5 1/7] tee: qcomtee: Track the object invocation context Harshal Dev
2026-10-16 12:53 ` Kuldeep Singh
2026-10-15 11:39 ` [PATCH v5 2/7] tee: Add kernel client object invoke helper Harshal Dev
2026-10-16 12:54 ` Kuldeep Singh
2026-10-26 3:09 ` Amirreza Zarrabi
2026-11-02 13:40 ` Harshal Dev
2026-10-15 11:39 ` [PATCH v5 3/7] tee: qcomtee: Allow object invokes from kernel clients Harshal Dev
2026-10-16 12:55 ` Kuldeep Singh
2026-10-26 3:15 ` Amirreza Zarrabi
2026-11-02 13:41 ` Harshal Dev
2026-10-15 11:39 ` [PATCH v5 4/7] tee: Export uuidv5 generation for TEE backends Harshal Dev
2026-10-16 12:55 ` Kuldeep Singh
2026-10-15 11:39 ` [PATCH v5 5/7] tee: qcomtee: Add support for registering QTEE services on TEE bus Harshal Dev
2026-10-16 12:56 ` Kuldeep Singh
2026-10-26 3:51 ` Amirreza Zarrabi
2026-11-02 13:44 ` Harshal Dev
2026-10-15 11:39 ` [PATCH v5 6/7] firmware: qcom: Add support for TEE based EFI-var client driver Harshal Dev
2026-10-26 4:44 ` Amirreza Zarrabi
2026-11-03 8:35 ` Harshal Dev
2026-11-05 5:54 ` Amirreza Zarrabi [this message]
2026-11-05 6:57 ` Harshal Dev
2026-10-15 11:39 ` [PATCH v5 7/7] arm64: defconfig: Enable UefiSecApp TEE client driver for Qualcomm SoCs Harshal Dev
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=af92dbae-701c-480c-b7c7-7bc7bf2e6fc9@oss.qualcomm.com \
--to=amirreza.zarrabi@oss.qualcomm.com \
--cc=andersson@kernel.org \
--cc=aneelaka@qti.qualcomm.com \
--cc=apurupa@qti.qualcomm.com \
--cc=basantk@qti.qualcomm.com \
--cc=dmitry.baryshkov@oss.qualcomm.com \
--cc=harshal.dev@oss.qualcomm.com \
--cc=jens.wiklander@oss.qualcomm.com \
--cc=konradybcio@kernel.org \
--cc=krzk@kernel.org \
--cc=kuldeep.singh@oss.qualcomm.com \
--cc=linux-arm-msm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=op-tee@lists.trustedfirmware.org \
--cc=sumit.garg@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®