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 record in handle_rpc() that a supplicant request is
being serviced and skip the announcement in that case: OP-TEE is using
the RPMB already and will find the device when it probes.

The counter lives in the OP-TEE driver's private data and is therefore
maintained in handle_rpc(), which only the OP-TEE driver reaches, rather
than in optee_suppl_cmd(): the sandbox TEE driver calls that function
too, with a device whose private data is its own sandbox_tee_state.

Signed-off-by: Igor Opaniuk <[email protected]>
---
 drivers/tee/optee/core.c          |  7 +++++++
 drivers/tee/optee/optee_private.h |  6 ++++++
 drivers/tee/optee/rpmb_emmc.c     | 11 +++++++++++
 3 files changed, 24 insertions(+)

diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
index a47c76811cd..f7670096b61 100644
--- a/drivers/tee/optee/core.c
+++ b/drivers/tee/optee/core.c
@@ -424,6 +424,7 @@ static int from_msg_param(struct tee_param *params, uint 
num_params,
 static void handle_rpc(struct udevice *dev, struct rpc_param *param,
                       void *page_list)
 {
+       struct optee_private *priv = dev_get_priv(dev);
        struct tee_shm *shm;
 
        switch (OPTEE_SMC_RETURN_GET_RPC_FUNC(param->a0)) {
@@ -450,7 +451,13 @@ static void handle_rpc(struct udevice *dev, struct 
rpc_param *param,
                break;
        case OPTEE_SMC_RPC_FUNC_CMD:
                shm = reg_pair_to_ptr(param->a1, param->a2);
+               /*
+                * Tracked here rather than in optee_suppl_cmd(), which the
+                * sandbox TEE driver shares with its own private data.
+                */
+               priv->in_supplicant++;
                optee_suppl_cmd(dev, shm, page_list);
+               priv->in_supplicant--;
                break;
        default:
                break;
diff --git a/drivers/tee/optee/optee_private.h 
b/drivers/tee/optee/optee_private.h
index 5c72b280058..416fa66db4f 100644
--- a/drivers/tee/optee/optee_private.h
+++ b/drivers/tee/optee/optee_private.h
@@ -35,10 +35,13 @@
  * struct optee_private - OP-TEE driver private data
  * @rpmb_next_region:  next UFS RPMB region to report on PROBE_NEXT
  * @rpmb_cur_region:   UFS RPMB region selected by the last PROBE_NEXT
+ * @in_supplicant:     non-zero while a supplicant RPC from OP-TEE is being
+ *                     serviced
  */
 struct optee_private {
        unsigned int rpmb_next_region;
        unsigned int rpmb_cur_region;
+       int in_supplicant;
 };
 #else
 /**
@@ -48,11 +51,14 @@ struct optee_private {
  * @rpmb_original_part:        the previosly active partition on the mmc 
device,
  *                     used to restore active the partition when the RPMB
  *                     accesses are finished
+ * @in_supplicant:     non-zero while a supplicant RPC from OP-TEE is being
+ *                     serviced
  */
 struct optee_private {
        struct mmc *rpmb_mmc;
        int rpmb_dev_id;
        int rpmb_original_part;
+       int in_supplicant;
 };
 #endif
 
diff --git a/drivers/tee/optee/rpmb_emmc.c b/drivers/tee/optee/rpmb_emmc.c
index d4abc48f764..a40573c9281 100644
--- a/drivers/tee/optee/rpmb_emmc.c
+++ b/drivers/tee/optee/rpmb_emmc.c
@@ -210,6 +210,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;
@@ -219,6 +220,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;
 

-- 
2.53.0

Reply via email to