diff options
Diffstat (limited to 'drivers/tee/optee/smc_abi.c')
-rw-r--r-- | drivers/tee/optee/smc_abi.c | 141 |
1 files changed, 139 insertions, 2 deletions
diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c index 26f8f7bbbe56..0be663fcd52b 100644 --- a/drivers/tee/optee/smc_abi.c +++ b/drivers/tee/optee/smc_abi.c @@ -965,6 +965,70 @@ static int optee_smc_do_call_with_arg(struct tee_context *ctx, return rc; } +static int optee_smc_lend_protmem(struct optee *optee, struct tee_shm *protmem, + u32 *mem_attrs, unsigned int ma_count, + u32 use_case) +{ + struct optee_shm_arg_entry *entry; + struct optee_msg_arg *msg_arg; + struct tee_shm *shm; + u_int offs; + int rc; + + msg_arg = optee_get_msg_arg(optee->ctx, 2, &entry, &shm, &offs); + if (IS_ERR(msg_arg)) + return PTR_ERR(msg_arg); + + msg_arg->cmd = OPTEE_MSG_CMD_LEND_PROTMEM; + msg_arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT; + msg_arg->params[0].u.value.a = use_case; + msg_arg->params[1].attr = OPTEE_MSG_ATTR_TYPE_TMEM_INPUT; + msg_arg->params[1].u.tmem.buf_ptr = protmem->paddr; + msg_arg->params[1].u.tmem.size = protmem->size; + msg_arg->params[1].u.tmem.shm_ref = (u_long)protmem; + + rc = optee->ops->do_call_with_arg(optee->ctx, shm, offs, false); + if (rc) + goto out; + if (msg_arg->ret != TEEC_SUCCESS) { + rc = -EINVAL; + goto out; + } + protmem->sec_world_id = (u_long)protmem; + +out: + optee_free_msg_arg(optee->ctx, entry, offs); + return rc; +} + +static int optee_smc_reclaim_protmem(struct optee *optee, + struct tee_shm *protmem) +{ + struct optee_shm_arg_entry *entry; + struct optee_msg_arg *msg_arg; + struct tee_shm *shm; + u_int offs; + int rc; + + msg_arg = optee_get_msg_arg(optee->ctx, 1, &entry, &shm, &offs); + if (IS_ERR(msg_arg)) + return PTR_ERR(msg_arg); + + msg_arg->cmd = OPTEE_MSG_CMD_RECLAIM_PROTMEM; + msg_arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT; + msg_arg->params[0].u.rmem.shm_ref = (u_long)protmem; + + rc = optee->ops->do_call_with_arg(optee->ctx, shm, offs, false); + if (rc) + goto out; + if (msg_arg->ret != TEEC_SUCCESS) + rc = -EINVAL; + +out: + optee_free_msg_arg(optee->ctx, entry, offs); + return rc; +} + /* * 5. Asynchronous notification */ @@ -1216,6 +1280,8 @@ static const struct optee_ops optee_ops = { .do_call_with_arg = optee_smc_do_call_with_arg, .to_msg_param = optee_to_msg_param, .from_msg_param = optee_from_msg_param, + .lend_protmem = optee_smc_lend_protmem, + .reclaim_protmem = optee_smc_reclaim_protmem, }; static int enable_async_notif(optee_invoke_fn *invoke_fn) @@ -1583,6 +1649,74 @@ static inline int optee_load_fw(struct platform_device *pdev, } #endif +static struct tee_protmem_pool *static_protmem_pool_init(struct optee *optee) +{ +#if IS_ENABLED(CONFIG_OPTEE_STATIC_PROTMEM_POOL) + union { + struct arm_smccc_res smccc; + struct optee_smc_get_protmem_config_result result; + } res; + struct tee_protmem_pool *pool; + void *p; + int rc; + + optee->smc.invoke_fn(OPTEE_SMC_GET_PROTMEM_CONFIG, 0, 0, 0, 0, + 0, 0, 0, &res.smccc); + if (res.result.status != OPTEE_SMC_RETURN_OK) + return ERR_PTR(-EINVAL); + + rc = optee_set_dma_mask(optee, res.result.pa_width); + if (rc) + return ERR_PTR(rc); + + /* + * Map the memory as uncached to make sure the kernel can work with + * __pfn_to_page() and friends since that's needed when passing the + * protected DMA-buf to a device. The memory should otherwise not + * be touched by the kernel since it's likely to cause an external + * abort due to the protection status. + */ + p = devm_memremap(&optee->teedev->dev, res.result.start, + res.result.size, MEMREMAP_WC); + if (IS_ERR(p)) + return p; + + pool = tee_protmem_static_pool_alloc(res.result.start, res.result.size); + if (IS_ERR(pool)) + devm_memunmap(&optee->teedev->dev, p); + + return pool; +#else + return ERR_PTR(-EINVAL); +#endif +} + +static int optee_protmem_pool_init(struct optee *optee) +{ + bool protm = optee->smc.sec_caps & OPTEE_SMC_SEC_CAP_PROTMEM; + bool dyn_protm = optee->smc.sec_caps & + OPTEE_SMC_SEC_CAP_DYNAMIC_PROTMEM; + enum tee_dma_heap_id heap_id = TEE_DMA_HEAP_SECURE_VIDEO_PLAY; + struct tee_protmem_pool *pool = ERR_PTR(-EINVAL); + int rc = -EINVAL; + + if (!protm && !dyn_protm) + return 0; + + if (protm) + pool = static_protmem_pool_init(optee); + if (dyn_protm && IS_ERR(pool)) + pool = optee_protmem_alloc_dyn_pool(optee, heap_id); + if (IS_ERR(pool)) + return PTR_ERR(pool); + + rc = tee_device_register_dma_heap(optee->teedev, heap_id, pool); + if (rc) + pool->ops->destroy_pool(pool); + + return rc; +} + static int optee_probe(struct platform_device *pdev) { optee_invoke_fn *invoke_fn; @@ -1678,7 +1812,7 @@ static int optee_probe(struct platform_device *pdev) optee = kzalloc(sizeof(*optee), GFP_KERNEL); if (!optee) { rc = -ENOMEM; - goto err_free_pool; + goto err_free_shm_pool; } optee->ops = &optee_ops; @@ -1751,6 +1885,9 @@ static int optee_probe(struct platform_device *pdev) pr_info("Asynchronous notifications enabled\n"); } + if (optee_protmem_pool_init(optee)) + pr_info("Protected memory service not available\n"); + /* * Ensure that there are no pre-existing shm objects before enabling * the shm cache so that there's no chance of receiving an invalid @@ -1802,7 +1939,7 @@ err_unreg_teedev: tee_device_unregister(optee->teedev); err_free_optee: kfree(optee); -err_free_pool: +err_free_shm_pool: tee_shm_pool_free(pool); if (memremaped_shm) memunmap(memremaped_shm); |