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
