blob: cb7b4e869d97695f5e969c2059fbc17a9e1f6d84 [file] [log] [blame]
// 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;
/*
* Reverse each 4 bytes of the 16 bytes CID array, as expected over at
* the OP-TEE side in order to derive the correct RPMB key.
*
* As it turns out, the swaped array also matches the definition of JEDEC
* spec, at least for the eMMC part used on newman, which means the original
* bytes order from mmc driver is off.
*
* This is all admittedly very confusing and there is no reliable way to
* verify which ordering is correct for a given part. The best we can do
* is probably to manually dump out the CID and compare against the spec.
*/
u8 *next = info->cid;
for (int i = 0; i < ARRAY_SIZE(mmc->cid); i++) {
*(next++) = mmc->cid[i] >> 24;
*(next++) = mmc->cid[i] >> 16;
*(next++) = mmc->cid[i] >> 8;
*(next++) = 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)
{
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_TMEM_INPUT ||
arg->params[1].attr != OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT) {
arg->ret = TEE_ERROR_BAD_PARAMETERS;
return;
}
req_buf = (u8 *)arg->params[0].u.tmem.buf_ptr;
req_size = arg->params[0].u.tmem.size;
rsp_buf = (u8 *)arg->params[1].u.tmem.buf_ptr;
rsp_size = arg->params[1].u.tmem.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));
}