The supplicant switched the eMMC to the RPMB hardware partition when the device was first used and only switched back when the OP-TEE session was released. Between two RPMB requests other users of the same device switch it back to the user area: libavb does so for every partition read while verifying a chain, and the Android bootmeth reads boot/vendor_boot right after verification.
With RPMB left selected those reads hit the RPMB partition, and the next RPMB frames were routed to whatever partition the other user left selected, which ends in status timeouts and I/O errors (seen with the AVB TA on QEMU's emulated eMMC). Select the RPMB partition around every data request and restore the previously selected partition afterwards. Signed-off-by: Igor Opaniuk <[email protected]> --- drivers/tee/optee/rpmb.c | 61 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/drivers/tee/optee/rpmb.c b/drivers/tee/optee/rpmb.c index 7ae6045511b..ea5507af086 100644 --- a/drivers/tee/optee/rpmb.c +++ b/drivers/tee/optee/rpmb.c @@ -42,24 +42,15 @@ struct rpmb_dev_info { static void release_mmc(struct optee_private *priv) { - int rc; - if (!priv->rpmb_mmc) return; - rc = blk_select_hwpart_devnum(UCLASS_MMC, priv->rpmb_dev_id, - priv->rpmb_original_part); - if (rc) - debug("%s: blk_select_hwpart_devnum() failed: %d\n", - __func__, rc); - priv->rpmb_mmc = NULL; } static struct mmc *get_mmc(struct optee_private *priv, int dev_id) { struct mmc *mmc; - int rc; if (priv->rpmb_mmc && priv->rpmb_dev_id == dev_id) return priv->rpmb_mmc; @@ -86,19 +77,44 @@ static struct mmc *get_mmc(struct optee_private *priv, int dev_id) } priv->rpmb_original_part = mmc_get_blk_desc(mmc)->hwpart; - - rc = blk_select_hwpart_devnum(UCLASS_MMC, dev_id, MMC_PART_RPMB); - if (rc) { - debug("Device id %d: cannot select RPMB partition: %d\n", - dev_id, rc); - return NULL; - } - priv->rpmb_mmc = mmc; priv->rpmb_dev_id = dev_id; return mmc; } +/* + * The RPMB hardware partition is selected for the duration of a single + * request only. Between two requests other users of the same eMMC, for + * instance libavb reading the user area while verifying a chain of + * partitions, switch the device back to the user area. Keeping RPMB + * selected across requests would make those reads, and the following RPMB + * frames, hit the wrong partition. + */ +static int select_rpmb_part(struct optee_private *priv) +{ + int rc; + + priv->rpmb_original_part = mmc_get_blk_desc(priv->rpmb_mmc)->hwpart; + rc = blk_select_hwpart_devnum(UCLASS_MMC, priv->rpmb_dev_id, + MMC_PART_RPMB); + if (rc) + debug("Device id %d: cannot select RPMB partition: %d\n", + priv->rpmb_dev_id, rc); + + return rc; +} + +static void restore_part(struct optee_private *priv) +{ + int rc; + + rc = blk_select_hwpart_devnum(UCLASS_MMC, priv->rpmb_dev_id, + priv->rpmb_original_part); + if (rc) + debug("%s: blk_select_hwpart_devnum() failed: %d\n", + __func__, rc); +} + static u32 rpmb_get_dev_info(u16 dev_id, struct rpmb_dev_info *info) { struct mmc *mmc = find_mmc_device(dev_id); @@ -130,6 +146,7 @@ static u32 rpmb_process_request(struct optee_private *priv, void *req, { struct rpmb_req *sreq = req; struct mmc *mmc; + int rc; if (req_size < sizeof(*sreq)) return TEE_ERROR_BAD_PARAMETERS; @@ -139,9 +156,13 @@ static u32 rpmb_process_request(struct optee_private *priv, void *req, mmc = get_mmc(priv, sreq->dev_id); if (!mmc) return TEE_ERROR_ITEM_NOT_FOUND; - if (mmc_rpmb_route_frames(mmc, RPMB_REQ_DATA(req), - req_size - sizeof(struct rpmb_req), - rsp, rsp_size)) + if (select_rpmb_part(priv)) + return TEE_ERROR_ITEM_NOT_FOUND; + rc = mmc_rpmb_route_frames(mmc, RPMB_REQ_DATA(req), + req_size - sizeof(struct rpmb_req), + rsp, rsp_size); + restore_part(priv); + if (rc) return TEE_ERROR_BAD_PARAMETERS; return TEE_SUCCESS; -- 2.53.0
