u-boot/drivers/tee/optee/rpmb.c
Jorge Ramirez-Ortiz 2464b229b5 drivers: optee: rpmb: fix returning CID to TEE
The mmc CID value is one of the input parameters used to provision the
RPMB key. The trusted execution environment expects this value to be
specified in big endian format.

Before this fix, on little endian systems, the value returned by the
linux kernel mmc driver differed from the one returned by u-boot.
This meant that if linux provisioned the RPMB key, u-boot would not
have access to the partition (and the other way around).

Signed-off-by: Jorge Ramirez-Ortiz <jorge@foundries.io>
Reviewed-by: Jens Wiklander <jens.wiklander@linaro.org>
2019-12-05 10:28:38 -05:00

184 lines
4.3 KiB
C

// 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);
int i;
if (!mmc)
return TEE_ERROR_ITEM_NOT_FOUND;
if (!mmc->ext_csd)
return TEE_ERROR_GENERIC;
for (i = 0; i < ARRAY_SIZE(mmc->cid); i++)
((u32 *) info->cid)[i] = cpu_to_be32(mmc->cid[i]);
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));
}