optee: support routing of rpmb data frames to mmc
authorJens Wiklander <jens.wiklander@linaro.org>
Tue, 25 Sep 2018 14:40:14 +0000 (16:40 +0200)
committerTom Rini <trini@konsulko.com>
Sun, 7 Oct 2018 14:47:38 +0000 (10:47 -0400)
Adds support in optee supplicant to route signed (MACed) RPMB frames
from OP-TEE Secure OS to MMC and vice versa to manipulate the RPMB
partition.

Tested-by: Igor Opaniuk <igor.opaniuk@linaro.org>
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
Reviewed-by: Simon Glass <sjg@chromium.org>
drivers/tee/optee/Makefile
drivers/tee/optee/core.c
drivers/tee/optee/optee_private.h
drivers/tee/optee/rpmb.c [new file with mode: 0644]
drivers/tee/optee/supplicant.c

index 6148feb474a5e8f9d21c7ba791c63ca19054ad6f..928d3f80027f077a18e545dc2f821f10ea640975 100644 (file)
@@ -2,3 +2,4 @@
 
 obj-y += core.o
 obj-y += supplicant.o
+obj-$(CONFIG_SUPPORT_EMMC_RPMB) += rpmb.o
index 726382da9bb844e0f079442039ec94c6265badd8..7f870f2f735d15975ba806c12c22cff8bb0d50e5 100644 (file)
@@ -315,6 +315,13 @@ static u32 do_call_with_arg(struct udevice *dev, struct optee_msg_arg *arg)
                        param.a3 = res.a3;
                        handle_rpc(dev, &param, &page_list);
                } else {
+                       /*
+                        * In case we've accessed RPMB to serve an RPC
+                        * request we need to restore the previously
+                        * selected partition as the caller may expect it
+                        * to remain unchanged.
+                        */
+                       optee_suppl_rpmb_release(dev);
                        return call_err_to_res(res.a0);
                }
        }
@@ -651,4 +658,5 @@ U_BOOT_DRIVER(optee) = {
        .probe = optee_probe,
        .ops = &optee_ops,
        .platdata_auto_alloc_size = sizeof(struct optee_pdata),
+       .priv_auto_alloc_size = sizeof(struct optee_private),
 };
index 35adb83afcc79be8091465a13f2a53b6ab92129c..9442d1c176bc9065e84216c7b44ea1f04923c34d 100644 (file)
@@ -6,7 +6,60 @@
 #ifndef __OPTEE_PRIVATE_H
 #define __OPTEE_PRIVATE_H
 
+#include <tee.h>
+#include <log.h>
+
+/**
+ * struct optee_private - OP-TEE driver private data
+ * @rpmb_mmc:          mmc device for the RPMB partition
+ * @rpmb_dev_id:       mmc device id matching @rpmb_mmc
+ * @rpmb_original_part:        the previosly active partition on the mmc device,
+ *                     used to restore active the partition when the RPMB
+ *                     accesses are finished
+ */
+struct optee_private {
+       struct mmc *rpmb_mmc;
+       int rpmb_dev_id;
+       int rpmb_original_part;
+};
+
+struct optee_msg_arg;
+
+void optee_suppl_cmd(struct udevice *dev, struct tee_shm *shm_arg,
+                    void **page_list);
+
+#ifdef CONFIG_SUPPORT_EMMC_RPMB
+/**
+ * optee_suppl_cmd_rpmb() - route RPMB frames to mmc
+ * @dev:       device with the selected RPMB partition
+ * @arg:       OP-TEE message holding the frames to transmit to the mmc
+ *             and space for the response frames.
+ *
+ * Routes signed (MACed) RPMB frames from OP-TEE Secure OS to MMC and vice
+ * versa to manipulate the RPMB partition.
+ */
+void optee_suppl_cmd_rpmb(struct udevice *dev, struct optee_msg_arg *arg);
+
+/**
+ * optee_suppl_rpmb_release() - release mmc device
+ * @dev:       mmc device
+ *
+ * Releases the mmc device and restores the previously selected partition.
+ */
+void optee_suppl_rpmb_release(struct udevice *dev);
+#else
+static inline void optee_suppl_cmd_rpmb(struct udevice *dev,
+                                       struct optee_msg_arg *arg)
+{
+       debug("OPTEE_MSG_RPC_CMD_RPMB not implemented\n");
+       arg->ret = TEE_ERROR_NOT_IMPLEMENTED;
+}
+
+static inline void optee_suppl_rpmb_release(struct udevice *dev)
+{
+}
+#endif
+
 void *optee_alloc_and_init_page_list(void *buf, ulong len, u64 *phys_buf_ptr);
-void optee_suppl_cmd(struct udevice *dev, void *shm, void **page_list);
 
 #endif /* __OPTEE_PRIVATE_H */
diff --git a/drivers/tee/optee/rpmb.c b/drivers/tee/optee/rpmb.c
new file mode 100644 (file)
index 0000000..955155b
--- /dev/null
@@ -0,0 +1,181 @@
+// SPDX-License-Identifier: BSD-2-Clause
+/*
+ * Copyright (c) 2018 Linaro Limited
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <log.h>
+#include <tee.h>
+#include <mmc.h>
+
+#include "optee_msg.h"
+#include "optee_private.h"
+
+/*
+ * Request and response definitions must be in sync with the secure side of
+ * OP-TEE.
+ */
+
+/* Request */
+struct rpmb_req {
+       u16 cmd;
+#define RPMB_CMD_DATA_REQ      0x00
+#define RPMB_CMD_GET_DEV_INFO  0x01
+       u16 dev_id;
+       u16 block_count;
+       /* Optional data frames (rpmb_data_frame) follow */
+};
+
+#define RPMB_REQ_DATA(req) ((void *)((struct rpmb_req *)(req) + 1))
+
+/* Response to device info request */
+struct rpmb_dev_info {
+       u8 cid[16];
+       u8 rpmb_size_mult;      /* EXT CSD-slice 168: RPMB Size */
+       u8 rel_wr_sec_c;        /* EXT CSD-slice 222: Reliable Write Sector */
+                               /*                    Count */
+       u8 ret_code;
+#define RPMB_CMD_GET_DEV_INFO_RET_OK     0x00
+#define RPMB_CMD_GET_DEV_INFO_RET_ERROR  0x01
+};
+
+static void release_mmc(struct optee_private *priv)
+{
+       int rc;
+
+       if (!priv->rpmb_mmc)
+               return;
+
+       rc = blk_select_hwpart_devnum(IF_TYPE_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;
+
+       release_mmc(priv);
+
+       mmc = find_mmc_device(dev_id);
+       if (!mmc) {
+               debug("Cannot find RPMB device\n");
+               return NULL;
+       }
+       if (!(mmc->version & MMC_VERSION_MMC)) {
+               debug("Device id %d is not an eMMC device\n", dev_id);
+               return NULL;
+       }
+       if (mmc->version < MMC_VERSION_4_41) {
+               debug("Device id %d: RPMB not supported before version 4.41\n",
+                     dev_id);
+               return NULL;
+       }
+
+       priv->rpmb_original_part = mmc_get_blk_desc(mmc)->hwpart;
+
+       rc = blk_select_hwpart_devnum(IF_TYPE_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;
+}
+
+static u32 rpmb_get_dev_info(u16 dev_id, struct rpmb_dev_info *info)
+{
+       struct mmc *mmc = find_mmc_device(dev_id);
+
+       if (!mmc)
+               return TEE_ERROR_ITEM_NOT_FOUND;
+
+       if (!mmc->ext_csd)
+               return TEE_ERROR_GENERIC;
+
+       memcpy(info->cid, mmc->cid, sizeof(info->cid));
+       info->rel_wr_sec_c = mmc->ext_csd[222];
+       info->rpmb_size_mult = mmc->ext_csd[168];
+       info->ret_code = RPMB_CMD_GET_DEV_INFO_RET_OK;
+
+       return TEE_SUCCESS;
+}
+
+static u32 rpmb_process_request(struct optee_private *priv, void *req,
+                               ulong req_size, void *rsp, ulong rsp_size)
+{
+       struct rpmb_req *sreq = req;
+       struct mmc *mmc;
+
+       if (req_size < sizeof(*sreq))
+               return TEE_ERROR_BAD_PARAMETERS;
+
+       switch (sreq->cmd) {
+       case RPMB_CMD_DATA_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))
+                       return TEE_ERROR_BAD_PARAMETERS;
+               return TEE_SUCCESS;
+
+       case RPMB_CMD_GET_DEV_INFO:
+               if (req_size != sizeof(struct rpmb_req) ||
+                   rsp_size != sizeof(struct rpmb_dev_info)) {
+                       debug("Invalid req/rsp size\n");
+                       return TEE_ERROR_BAD_PARAMETERS;
+               }
+               return rpmb_get_dev_info(sreq->dev_id, rsp);
+
+       default:
+               debug("Unsupported RPMB command: %d\n", sreq->cmd);
+               return TEE_ERROR_BAD_PARAMETERS;
+       }
+}
+
+void optee_suppl_cmd_rpmb(struct udevice *dev, struct optee_msg_arg *arg)
+{
+       struct tee_shm *req_shm;
+       struct tee_shm *rsp_shm;
+       void *req_buf;
+       void *rsp_buf;
+       ulong req_size;
+       ulong rsp_size;
+
+       if (arg->num_params != 2 ||
+           arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_RMEM_INPUT ||
+           arg->params[1].attr != OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT) {
+               arg->ret = TEE_ERROR_BAD_PARAMETERS;
+               return;
+       }
+
+       req_shm = (struct tee_shm *)(ulong)arg->params[0].u.rmem.shm_ref;
+       req_buf = (u8 *)req_shm->addr + arg->params[0].u.rmem.offs;
+       req_size = arg->params[0].u.rmem.size;
+
+       rsp_shm = (struct tee_shm *)(ulong)arg->params[1].u.rmem.shm_ref;
+       rsp_buf = (u8 *)rsp_shm->addr + arg->params[1].u.rmem.offs;
+       rsp_size = arg->params[1].u.rmem.size;
+
+       arg->ret = rpmb_process_request(dev_get_priv(dev), req_buf, req_size,
+                                       rsp_buf, rsp_size);
+}
+
+void optee_suppl_rpmb_release(struct udevice *dev)
+{
+       release_mmc(dev_get_priv(dev));
+}
index 2239b1bf7b37a7e07d3a8a8ea1d6597443ece581..b1ea65bdb2e3297a197ce9d6ec90b7e65cabb8ae 100644 (file)
@@ -85,6 +85,9 @@ void optee_suppl_cmd(struct udevice *dev, struct tee_shm *shm_arg,
                debug("OPTEE_MSG_RPC_CMD_FS not implemented\n");
                arg->ret = TEE_ERROR_NOT_IMPLEMENTED;
                break;
+       case OPTEE_MSG_RPC_CMD_RPMB:
+               optee_suppl_cmd_rpmb(dev, arg);
+               break;
        default:
                arg->ret = TEE_ERROR_NOT_IMPLEMENTED;
        }