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

Reply via email to