diff --git a/core/include/optee_rpc_cmd.h b/core/include/optee_rpc_cmd.h index 816f0a24fbe..c1aaaa5ca6b 100644 --- a/core/include/optee_rpc_cmd.h +++ b/core/include/optee_rpc_cmd.h @@ -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 */ diff --git a/core/tee/tee_rpmb_fs.c b/core/tee/tee_rpmb_fs.c index 6525c8ca8d7..30220b3e77e 100644 --- a/core/tee/tee_rpmb_fs.c +++ b/core/tee/tee_rpmb_fs.c @@ -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; @@ -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; @@ -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; @@ -955,7 +1011,7 @@ 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; @@ -963,21 +1019,6 @@ static TEE_Result tee_rpmb_init_read_wr_cnt(uint32_t *wr_cnt, 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) { @@ -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; } @@ -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; } @@ -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"); } } @@ -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. *