mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Harshal Dev <harshal.dev@oss.qualcomm.com>
To: Amirreza Zarrabi <amirreza.zarrabi@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: Tue, 3 Nov 2026 14:05:12 +0530	[thread overview]
Message-ID: <eddd891e-c23f-4977-9f40-9a41222f5bf2@oss.qualcomm.com> (raw)
In-Reply-To: <4fed24d1-72a4-4314-a6e0-89ee7546240b@oss.qualcomm.com>

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.

>> +	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
 
> 
> - Amir
>

  reply	other threads:[~2026-11-03  8:35 UTC|newest]

Thread overview: 21+ 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 [this message]
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=eddd891e-c23f-4977-9f40-9a41222f5bf2@oss.qualcomm.com \
    --to=harshal.dev@oss.qualcomm.com \
    --cc=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=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®