mmc_init() calls optee_rpmb_available() once the card has been set up.
When the first initialisation of the eMMC is triggered by the RPMB
supplicant, that is while OP-TEE is blocked in an RPC and holds its RPMB
mutex, the announcement opens a nested session to device.pta, whose RPMB
enumeration takes the same mutex. OP-TEE's second thread then tries to
sleep through the notification interface, which U-Boot does not
implement, and spins forever. This happens whenever a TA touches its
RPMB secure storage before anything else in U-Boot has initialised the
eMMC, for example a TA call issued from the shell right after reset
("optee_rpmb read_pvalue" with the AVB TA, "tpm2 init" with the fTPM).Linux never runs into this: the RPMB device is registered by the MMC core long before any TEE client, the OP-TEE driver announces it from a workqueue rather than from an RPC, and the kernel implements the notification wait. U-Boot serves every RPC synchronously on the one thread it has, so make the supplicant record that it is servicing a request and skip the announcement in that case: OP-TEE is using the RPMB already and will find the device when it probes. Signed-off-by: Igor Opaniuk <[email protected]> --- drivers/tee/optee/optee_private.h | 2 ++ drivers/tee/optee/rpmb.c | 11 +++++++++++ drivers/tee/optee/supplicant.c | 4 ++++ 3 files changed, 17 insertions(+) diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h index daff3e04493..92e3b3bea10 100644 --- a/drivers/tee/optee/optee_private.h +++ b/drivers/tee/optee/optee_private.h @@ -42,6 +42,8 @@ struct optee_private { struct mmc *rpmb_mmc; int rpmb_dev_id; int rpmb_original_part; + /* Non-zero while a supplicant RPC from OP-TEE is being serviced */ + int in_supplicant; }; struct optee_msg_arg; diff --git a/drivers/tee/optee/rpmb.c b/drivers/tee/optee/rpmb.c index ea5507af086..83dc0cc11aa 100644 --- a/drivers/tee/optee/rpmb.c +++ b/drivers/tee/optee/rpmb.c @@ -215,6 +215,7 @@ void optee_suppl_rpmb_release(struct udevice *dev) void optee_rpmb_available(void) { + struct optee_private *priv; struct udevice *dev; struct uclass *uc; u32 tee_sess; @@ -224,6 +225,16 @@ void optee_rpmb_available(void) !device_active(dev)) continue; + /* + * mmc_init() may run from the RPMB supplicant itself, while + * OP-TEE is blocked in that RPC and holds its RPMB lock. A + * nested call would then deadlock, and OP-TEE is about to use + * the device anyway. + */ + priv = dev_get_priv(dev); + if (priv->in_supplicant) + continue; + if (optee_open_enum_session(dev, &tee_sess) != 0) continue; diff --git a/drivers/tee/optee/supplicant.c b/drivers/tee/optee/supplicant.c index 8a426f53ba8..c0eeaed7003 100644 --- a/drivers/tee/optee/supplicant.c +++ b/drivers/tee/optee/supplicant.c @@ -3,6 +3,7 @@ * Copyright (c) 2018, Linaro Limited */ +#include <dm.h> #include <log.h> #include <malloc.h> #include <tee.h> @@ -73,8 +74,10 @@ static void cmd_shm_free(struct optee_msg_arg *arg) void optee_suppl_cmd(struct udevice *dev, struct tee_shm *shm_arg, void **page_list) { + struct optee_private *priv = dev_get_priv(dev); struct optee_msg_arg *arg = shm_arg->addr; + priv->in_supplicant++; switch (arg->cmd) { case OPTEE_MSG_RPC_CMD_SHM_ALLOC: cmd_shm_alloc(dev, arg, page_list); @@ -95,6 +98,7 @@ void optee_suppl_cmd(struct udevice *dev, struct tee_shm *shm_arg, default: arg->ret = TEE_ERROR_NOT_IMPLEMENTED; } + priv->in_supplicant--; arg->ret_origin = TEE_ORIGIN_COMMS; } -- 2.53.0
