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

Reply via email to