Skip to content

Commit

Permalink
core: rpmb: probe for kernel RPMB driver
Browse files Browse the repository at this point in the history
Two RPC functions are added to support RPMB probing,
OPTEE_RPC_CMD_RPMB_PROBE_RESET and OPTEE_RPC_CMD_RPMB_PROBE_NEXT.

OPTEE_RPC_CMD_RPMB_PROBE_RESET resets probing to a well known state and
returns the shared memory type needed when allocating shared memory for
communication with later RPMB functions.

OPTEE_RPC_CMD_RPMB_PROBE_NEXT selects the next RPMB device and returns
its device information. Later calls to OPTEE_RPC_CMD_RPMB will use this
selected device.

Backwards compatibility is maintained by falling back to the old type of
initialization if OPTEE_RPC_CMD_RPMB_PROBE_RESET returns
TEE_ERROR_NOT_SUPPORTED.

Whether RPMB devices are probed by the kernel or tee-supplicant is
decided by the kernel driver where the shared memory type returned by
OPTEE_RPC_CMD_RPMB_PROBE_RESET plays a vital role.

Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
  • Loading branch information
jenswi-linaro committed Feb 27, 2024
1 parent 32865b9 commit 50ed732
Show file tree
Hide file tree
Showing 2 changed files with 192 additions and 62 deletions.
24 changes: 24 additions & 0 deletions core/include/optee_rpc_cmd.h
Expand Up @@ -178,6 +178,30 @@
/* I2C master control flags */
#define OPTEE_RPC_I2C_FLAGS_TEN_BIT BIT(0)

/*
* Reset RPMB probing
*
* Releases an eventually already used RPMB devices and starts over searching
* for RPMB devices. Returns the kind of shared memory to use in subsequent
* OPTEE_RPC_CMD_RPMB_PROBE_NEXT and OPTEE_RPC_CMD_RPMB calls.
*
* [out] value[0].a OPTEE_RPC_SHM_TYPE_*, the parameter for
* OPTEE_RPC_CMD_SHM_ALLOC
*/
#define OPTEE_RPC_CMD_RPMB_PROBE_RESET U(22)

/*
* Probe next RPMB device
*
* [out] value[0].a Must be OPTEE_RPC_RPMB_EMMC
* [out] value[0].b EXT CSD-slice 168 "RPMB Size"
* [out] value[0].c EXT CSD-slice 222 "Reliable Write Sector Count"
* [out] memref[1] Buffer with the raw CID
*/
#define OPTEE_RPC_CMD_RPMB_PROBE_NEXT U(23)

#define OPTEE_RPC_RPMB_EMMC U(0)

/*
* Definition of protocol for command OPTEE_RPC_CMD_FS
*/
Expand Down
230 changes: 168 additions & 62 deletions core/tee/tee_rpmb_fs.c
Expand Up @@ -455,6 +455,62 @@ static TEE_Result tee_rpmb_invoke(struct tee_rpmb_mem *mem)
return thread_rpc_cmd(OPTEE_RPC_CMD_RPMB, 2, params);
}

static TEE_Result rpmb_probe_reset(void)
{
struct thread_param params[1] = {
[0] = THREAD_PARAM_VALUE(OUT, 0, 0, 0),
};
TEE_Result res = TEE_SUCCESS;

res = thread_rpc_cmd(OPTEE_RPC_CMD_RPMB_PROBE_RESET, 1, params);
if (res)
return res;

switch (params[0].u.value.a) {
case OPTEE_RPC_SHM_TYPE_APPL:
rpmb_ctx->shm_type = THREAD_SHM_TYPE_APPLICATION;
return TEE_SUCCESS;
case OPTEE_RPC_SHM_TYPE_KERNEL:
rpmb_ctx->shm_type = THREAD_SHM_TYPE_KERNEL_PRIVATE;
return TEE_SUCCESS;
default:
return TEE_ERROR_GENERIC;
}
}

static TEE_Result rpmb_probe_next(struct rpmb_dev_info *dev_info)
{
struct thread_param params[2] = {
[0] = THREAD_PARAM_VALUE(OUT, 0, 0, 0),
};
TEE_Result res = TEE_SUCCESS;
struct mobj *mobj = NULL;
void *va = NULL;

va = thread_rpc_shm_cache_alloc(THREAD_SHM_CACHE_USER_RPMB,
THREAD_SHM_TYPE_KERNEL_PRIVATE,
sizeof(dev_info->cid), &mobj);
if (!va)
return TEE_ERROR_OUT_OF_MEMORY;

params[1] = THREAD_PARAM_MEMREF(OUT, mobj, 0, sizeof(dev_info->cid));
res = thread_rpc_cmd(OPTEE_RPC_CMD_RPMB_PROBE_NEXT, 2, params);
if (res)
return res;

if (params[0].u.value.a != OPTEE_RPC_RPMB_EMMC)
return TEE_ERROR_NOT_SUPPORTED;

*dev_info = (struct rpmb_dev_info ){
.rpmb_size_mult = params[0].u.value.b,
.rel_wr_sec_c = params[0].u.value.c,
.ret_code = RPMB_CMD_GET_DEV_INFO_RET_OK,
};
memcpy(dev_info->cid, va, sizeof(dev_info->cid));

return TEE_SUCCESS;
}

static bool is_zero(const uint8_t *buf, size_t size)
{
size_t i;
Expand Down Expand Up @@ -909,8 +965,7 @@ static TEE_Result tee_rpmb_get_dev_info(uint16_t dev_id,
return TEE_SUCCESS;
}

static TEE_Result tee_rpmb_init_read_wr_cnt(uint32_t *wr_cnt,
uint16_t *op_result)
static TEE_Result tee_rpmb_init_read_wr_cnt(uint32_t *wr_cnt)
{
TEE_Result res = TEE_ERROR_GENERIC;
struct tee_rpmb_mem mem;
Expand All @@ -922,6 +977,7 @@ static TEE_Result tee_rpmb_init_read_wr_cnt(uint32_t *wr_cnt,
struct rpmb_raw_data rawdata;
uint32_t req_size;
uint32_t resp_size;
uint16_t op_result = 0;

if (!wr_cnt)
return TEE_ERROR_BAD_PARAMETERS;
Expand Down Expand Up @@ -955,29 +1011,14 @@ static TEE_Result tee_rpmb_init_read_wr_cnt(uint32_t *wr_cnt,

memset(&rawdata, 0x00, sizeof(struct rpmb_raw_data));
rawdata.msg_type = msg_type;
rawdata.op_result = op_result;
rawdata.op_result = &op_result;
rawdata.write_counter = wr_cnt;
rawdata.nonce = nonce;
rawdata.key_mac = hmac;

return tee_rpmb_resp_unpack_verify(resp, &rawdata, 1, NULL, NULL);
}

static TEE_Result tee_rpmb_verify_key_sync_counter(void)
{
uint16_t op_result = 0;
TEE_Result res = TEE_ERROR_GENERIC;

res = tee_rpmb_init_read_wr_cnt(&rpmb_ctx->wr_cnt, &op_result);

if (res == TEE_SUCCESS) {
rpmb_ctx->key_verified = true;
rpmb_ctx->wr_cnt_synced = true;
} else
EMSG("Verify key returning 0x%x", res);
return res;
}

#ifdef CFG_RPMB_WRITE_KEY
static TEE_Result tee_rpmb_write_key(void)
{
Expand Down Expand Up @@ -1034,7 +1075,7 @@ static TEE_Result tee_rpmb_write_and_verify_key(void)
res = tee_rpmb_write_key();
if (res == TEE_SUCCESS) {
DMSG("RPMB INIT: Verifying Key");
res = tee_rpmb_verify_key_sync_counter();
res = tee_rpmb_init_read_wr_cnt(&rpmb_ctx->wr_cnt);
}
return res;
}
Expand All @@ -1046,57 +1087,52 @@ static TEE_Result tee_rpmb_write_and_verify_key(void)
}
#endif

/* This function must never return TEE_SUCCESS if rpmb_ctx == NULL */
static TEE_Result tee_rpmb_init(void)
static TEE_Result rpmb_set_dev_info(const struct rpmb_dev_info *dev_info)
{
TEE_Result res = TEE_SUCCESS;
struct rpmb_dev_info dev_info = { };
uint32_t nblocks = 0;

if (rpmb_dead)
return TEE_ERROR_COMMUNICATION;
DMSG("RPMB: Syncing device information");

if (!rpmb_ctx) {
rpmb_ctx = calloc(1, sizeof(struct tee_rpmb_ctx));
if (!rpmb_ctx)
return TEE_ERROR_OUT_OF_MEMORY;
rpmb_ctx->dev_id = CFG_RPMB_FS_DEV_ID;
rpmb_ctx->shm_type = THREAD_SHM_TYPE_APPLICATION;
}
DMSG("RPMB: RPMB size is %d*128 KB", dev_info->rpmb_size_mult);
DMSG("RPMB: Reliable Write Sector Count is %d", dev_info->rel_wr_sec_c);
DMSG("RPMB: CID");
DHEXDUMP(dev_info->cid, sizeof(dev_info->cid));

if (!dev_info->rpmb_size_mult)
return TEE_ERROR_GENERIC;

if (!rpmb_ctx->dev_info_synced) {
DMSG("RPMB: Syncing device information");
if (MUL_OVERFLOW(dev_info->rpmb_size_mult,
RPMB_SIZE_SINGLE / RPMB_DATA_SIZE, &nblocks) ||
SUB_OVERFLOW(nblocks, 1, &rpmb_ctx->max_blk_idx))
return TEE_ERROR_BAD_PARAMETERS;

memcpy(rpmb_ctx->cid, dev_info->cid, RPMB_EMMC_CID_SIZE);

#ifdef RPMB_DRIVER_MULTIPLE_WRITE_FIXED
rpmb_ctx->rel_wr_blkcnt = dev_info->rel_wr_sec_c * 2;
#else
rpmb_ctx->rel_wr_blkcnt = 1;
#endif

return TEE_SUCCESS;
}

static TEE_Result legacy_rpmb_init(void)
{
TEE_Result res = TEE_SUCCESS;
struct rpmb_dev_info dev_info = { };

DMSG("Trying legacy RPMB init");
if (!rpmb_ctx->dev_info_synced) {
dev_info.rpmb_size_mult = 0;
dev_info.rel_wr_sec_c = 0;
res = tee_rpmb_get_dev_info(rpmb_ctx->dev_id, &dev_info);
if (res != TEE_SUCCESS)
goto func_exit;

DMSG("RPMB: RPMB size is %d*128 KB", dev_info.rpmb_size_mult);
DMSG("RPMB: Reliable Write Sector Count is %d",
dev_info.rel_wr_sec_c);

if (dev_info.rpmb_size_mult == 0) {
res = TEE_ERROR_GENERIC;
goto func_exit;
}

if (MUL_OVERFLOW(dev_info.rpmb_size_mult,
RPMB_SIZE_SINGLE / RPMB_DATA_SIZE, &nblocks) ||
SUB_OVERFLOW(nblocks, 1, &rpmb_ctx->max_blk_idx)) {
res = TEE_ERROR_BAD_PARAMETERS;
goto func_exit;
}

memcpy(rpmb_ctx->cid, dev_info.cid, RPMB_EMMC_CID_SIZE);

#ifdef RPMB_DRIVER_MULTIPLE_WRITE_FIXED
rpmb_ctx->rel_wr_blkcnt = dev_info.rel_wr_sec_c * 2;
#else
rpmb_ctx->rel_wr_blkcnt = 1;
#endif
res = rpmb_set_dev_info(&dev_info);
if (res)
return res;

rpmb_ctx->dev_info_synced = true;
}
Expand All @@ -1118,16 +1154,20 @@ static TEE_Result tee_rpmb_init(void)
if (!rpmb_ctx->wr_cnt_synced || !rpmb_ctx->key_verified) {
DMSG("RPMB INIT: Verifying Key");

res = tee_rpmb_verify_key_sync_counter();
if (res == TEE_ERROR_ITEM_NOT_FOUND &&
!rpmb_ctx->key_verified) {
res = tee_rpmb_init_read_wr_cnt(&rpmb_ctx->wr_cnt);
if (res == TEE_SUCCESS) {
DMSG("Found working RPMB device");
rpmb_ctx->key_verified = true;
rpmb_ctx->wr_cnt_synced = true;
} else if (res == TEE_ERROR_ITEM_NOT_FOUND &&
!rpmb_ctx->key_verified) {
/*
* Need to write the key here and verify it.
*/
DMSG("RPMB INIT: Auth key not yet written");
res = tee_rpmb_write_and_verify_key();
} else if (res != TEE_SUCCESS) {
EMSG("Verify key failed!");
} else {
EMSG("Verify key failed! %#"PRIx32, res);
EMSG("Make sure key here matches device key");
}
}
Expand All @@ -1136,6 +1176,72 @@ static TEE_Result tee_rpmb_init(void)
return res;
}

/* This function must never return TEE_SUCCESS if rpmb_ctx == NULL */
static TEE_Result tee_rpmb_init(void)
{
TEE_Result res = TEE_SUCCESS;
struct rpmb_dev_info dev_info = { };

if (rpmb_dead)
return TEE_ERROR_COMMUNICATION;

if (!rpmb_ctx) {
rpmb_ctx = calloc(1, sizeof(struct tee_rpmb_ctx));
if (!rpmb_ctx)
return TEE_ERROR_OUT_OF_MEMORY;
rpmb_ctx->dev_id = CFG_RPMB_FS_DEV_ID;
rpmb_ctx->shm_type = THREAD_SHM_TYPE_APPLICATION;
}

if (rpmb_ctx->key_verified)
return TEE_SUCCESS;

if (IS_ENABLED(CFG_RPMB_WRITE_KEY))
return legacy_rpmb_init();

res = rpmb_probe_reset();
if (res) {
if (res != TEE_ERROR_NOT_SUPPORTED)
return res;
return legacy_rpmb_init();
}

while (true) {
res = rpmb_probe_next(&dev_info);
if (res) {
/*
* The ID buffer is too small, perhaps it's a new
* kind of RPMB device that we don't know how to
* communicate with, let's ignore it for now.
*/
if (res == TEE_ERROR_SHORT_BUFFER)
continue;
DMSG("rpmb_probe_next error %#"PRIx32, res);
return res;
}
res = rpmb_set_dev_info(&dev_info);
if (res) {
DMSG("Invalid device info, looking for another device");
continue;
}

res = tee_rpmb_key_gen(rpmb_ctx->key, RPMB_KEY_MAC_SIZE);
if (res)
return res;

res = tee_rpmb_init_read_wr_cnt(&rpmb_ctx->wr_cnt);
if (res)
continue;
break;
}

DMSG("Found working RPMB device");
rpmb_ctx->key_verified = true;
rpmb_ctx->wr_cnt_synced = true;

return TEE_SUCCESS;
}

/*
* Read RPMB data in bytes.
*
Expand Down

0 comments on commit 50ed732

Please sign in to comment.