diff options
Diffstat (limited to 'drivers/tee/optee/core.c')
-rw-r--r-- | drivers/tee/optee/core.c | 180 |
1 files changed, 102 insertions, 78 deletions
diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index b4fe7713b2fb..58169e519422 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -11,6 +11,10 @@ * GNU General Public License for more details. * */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/arm-smccc.h> #include <linux/errno.h> #include <linux/io.h> #include <linux/module.h> @@ -326,9 +330,8 @@ static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn, * point of view) or not, is_smp() returns the the information * needed, but can't be called directly from here. */ -#ifndef CONFIG_SMP - a1 |= OPTEE_SMC_NSEC_CAP_UNIPROCESSOR; -#endif + if (!IS_ENABLED(CONFIG_SMP) || nr_cpu_ids == 1) + a1 |= OPTEE_SMC_NSEC_CAP_UNIPROCESSOR; invoke_fn(OPTEE_SMC_EXCHANGE_CAPABILITIES, a1, 0, 0, 0, 0, 0, 0, &res.smccc); @@ -341,8 +344,7 @@ static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn, } static struct tee_shm_pool * -optee_config_shm_ioremap(struct device *dev, optee_invoke_fn *invoke_fn, - void __iomem **ioremaped_shm) +optee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm) { union { struct arm_smccc_res smccc; @@ -354,18 +356,18 @@ optee_config_shm_ioremap(struct device *dev, optee_invoke_fn *invoke_fn, size_t size; phys_addr_t begin; phys_addr_t end; - void __iomem *va; + void *va; struct tee_shm_pool_mem_info priv_info; struct tee_shm_pool_mem_info dmabuf_info; invoke_fn(OPTEE_SMC_GET_SHM_CONFIG, 0, 0, 0, 0, 0, 0, 0, &res.smccc); if (res.result.status != OPTEE_SMC_RETURN_OK) { - dev_info(dev, "shm service not available\n"); + pr_info("shm service not available\n"); return ERR_PTR(-ENOENT); } if (res.result.settings != OPTEE_SMC_SHM_CACHED) { - dev_err(dev, "only normal cached shared memory supported\n"); + pr_err("only normal cached shared memory supported\n"); return ERR_PTR(-EINVAL); } @@ -375,13 +377,13 @@ optee_config_shm_ioremap(struct device *dev, optee_invoke_fn *invoke_fn, size = end - begin; if (size < 2 * OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE) { - dev_err(dev, "too small shared memory area\n"); + pr_err("too small shared memory area\n"); return ERR_PTR(-EINVAL); } - va = ioremap_cache(paddr, size); + va = memremap(paddr, size, MEMREMAP_WB); if (!va) { - dev_err(dev, "shared memory ioremap failed\n"); + pr_err("shared memory ioremap failed\n"); return ERR_PTR(-EINVAL); } vaddr = (unsigned long)va; @@ -393,97 +395,112 @@ optee_config_shm_ioremap(struct device *dev, optee_invoke_fn *invoke_fn, dmabuf_info.paddr = paddr + OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE; dmabuf_info.size = size - OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE; - pool = tee_shm_pool_alloc_res_mem(dev, &priv_info, &dmabuf_info); + pool = tee_shm_pool_alloc_res_mem(&priv_info, &dmabuf_info); if (IS_ERR(pool)) { - iounmap(va); + memunmap(va); goto out; } - *ioremaped_shm = va; + *memremaped_shm = va; out: return pool; } -static int get_invoke_func(struct device *dev, optee_invoke_fn **invoke_fn) +/* Simple wrapper functions to be able to use a function pointer */ +static void optee_smccc_smc(unsigned long a0, unsigned long a1, + unsigned long a2, unsigned long a3, + unsigned long a4, unsigned long a5, + unsigned long a6, unsigned long a7, + struct arm_smccc_res *res) +{ + arm_smccc_smc(a0, a1, a2, a3, a4, a5, a6, a7, res); +} + +static void optee_smccc_hvc(unsigned long a0, unsigned long a1, + unsigned long a2, unsigned long a3, + unsigned long a4, unsigned long a5, + unsigned long a6, unsigned long a7, + struct arm_smccc_res *res) +{ + arm_smccc_hvc(a0, a1, a2, a3, a4, a5, a6, a7, res); +} + +static optee_invoke_fn *get_invoke_func(struct device_node *np) { - struct device_node *np = dev->of_node; const char *method; - dev_info(dev, "probing for conduit method from DT.\n"); + pr_info("probing for conduit method from DT.\n"); if (of_property_read_string(np, "method", &method)) { - dev_warn(dev, "missing \"method\" property\n"); - return -ENXIO; + pr_warn("missing \"method\" property\n"); + return ERR_PTR(-ENXIO); } - if (!strcmp("hvc", method)) { - *invoke_fn = arm_smccc_hvc; - } else if (!strcmp("smc", method)) { - *invoke_fn = arm_smccc_smc; - } else { - dev_warn(dev, "invalid \"method\" property: %s\n", method); - return -EINVAL; - } - return 0; + if (!strcmp("hvc", method)) + return optee_smccc_hvc; + else if (!strcmp("smc", method)) + return optee_smccc_smc; + + pr_warn("invalid \"method\" property: %s\n", method); + return ERR_PTR(-EINVAL); } -static int optee_probe(struct platform_device *pdev) +static struct optee *optee_probe(struct device_node *np) { optee_invoke_fn *invoke_fn; struct tee_shm_pool *pool; struct optee *optee = NULL; - void __iomem *ioremaped_shm = NULL; + void *memremaped_shm = NULL; struct tee_device *teedev; u32 sec_caps; int rc; - rc = get_invoke_func(&pdev->dev, &invoke_fn); - if (rc) - return rc; + invoke_fn = get_invoke_func(np); + if (IS_ERR(invoke_fn)) + return (void *)invoke_fn; if (!optee_msg_api_uid_is_optee_api(invoke_fn)) { - dev_warn(&pdev->dev, "api uid mismatch\n"); - return -EINVAL; + pr_warn("api uid mismatch\n"); + return ERR_PTR(-EINVAL); } if (!optee_msg_api_revision_is_compatible(invoke_fn)) { - dev_warn(&pdev->dev, "api revision mismatch\n"); - return -EINVAL; + pr_warn("api revision mismatch\n"); + return ERR_PTR(-EINVAL); } if (!optee_msg_exchange_capabilities(invoke_fn, &sec_caps)) { - dev_warn(&pdev->dev, "capabilities mismatch\n"); - return -EINVAL; + pr_warn("capabilities mismatch\n"); + return ERR_PTR(-EINVAL); } /* * We have no other option for shared memory, if secure world * doesn't have any reserved memory we can use we can't continue. */ - if (!(sec_caps & OPTEE_SMC_SEC_CAP_HAVE_RESERVERED_SHM)) - return -EINVAL; + if (!(sec_caps & OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM)) + return ERR_PTR(-EINVAL); - pool = optee_config_shm_ioremap(&pdev->dev, invoke_fn, &ioremaped_shm); + pool = optee_config_shm_memremap(invoke_fn, &memremaped_shm); if (IS_ERR(pool)) - return PTR_ERR(pool); + return (void *)pool; - optee = devm_kzalloc(&pdev->dev, sizeof(*optee), GFP_KERNEL); + optee = kzalloc(sizeof(*optee), GFP_KERNEL); if (!optee) { rc = -ENOMEM; goto err; } - optee->dev = &pdev->dev; optee->invoke_fn = invoke_fn; - teedev = tee_device_alloc(&optee_desc, &pdev->dev, pool, optee); + teedev = tee_device_alloc(&optee_desc, NULL, pool, optee); if (IS_ERR(teedev)) { rc = PTR_ERR(teedev); goto err; } optee->teedev = teedev; - teedev = tee_device_alloc(&optee_supp_desc, &pdev->dev, pool, optee); + teedev = tee_device_alloc(&optee_supp_desc, NULL, pool, optee); if (IS_ERR(teedev)) { rc = PTR_ERR(teedev); goto err; @@ -502,15 +519,13 @@ static int optee_probe(struct platform_device *pdev) INIT_LIST_HEAD(&optee->call_queue.waiters); optee_wait_queue_init(&optee->wait_queue); optee_supp_init(&optee->supp); - optee->ioremaped_shm = ioremaped_shm; + optee->memremaped_shm = memremaped_shm; optee->pool = pool; - platform_set_drvdata(pdev, optee); - optee_enable_shm_cache(optee); - dev_info(&pdev->dev, "initialized driver\n"); - return 0; + pr_info("initialized driver\n"); + return optee; err: if (optee) { /* @@ -520,18 +535,17 @@ err: */ tee_device_unregister(optee->supp_teedev); tee_device_unregister(optee->teedev); + kfree(optee); } if (pool) tee_shm_pool_free(pool); - if (ioremaped_shm) - iounmap(ioremaped_shm); - return rc; + if (memremaped_shm) + memunmap(memremaped_shm); + return ERR_PTR(rc); } -static int optee_remove(struct platform_device *pdev) +static void optee_remove(struct optee *optee) { - struct optee *optee = platform_get_drvdata(pdev); - /* * Ask OP-TEE to free all cached shared memory objects to decrease * reference counters and also avoid wild pointers in secure world @@ -547,13 +561,13 @@ static int optee_remove(struct platform_device *pdev) tee_device_unregister(optee->teedev); tee_shm_pool_free(optee->pool); - if (optee->ioremaped_shm) - iounmap(optee->ioremaped_shm); + if (optee->memremaped_shm) + memunmap(optee->memremaped_shm); optee_wait_queue_exit(&optee->wait_queue); optee_supp_uninit(&optee->supp); mutex_destroy(&optee->call_queue.mutex); - return 0; + kfree(optee); } static const struct of_device_id optee_match[] = { @@ -561,33 +575,43 @@ static const struct of_device_id optee_match[] = { {}, }; -static struct platform_driver optee_driver = { - .driver = { - .name = DRIVER_NAME, - .of_match_table = optee_match, - }, - .probe = optee_probe, - .remove = optee_remove, -}; +static struct optee *optee_svc; static int __init optee_driver_init(void) { - struct device_node *node; + struct device_node *fw_np; + struct device_node *np; + struct optee *optee; - /* - * Preferred path is /firmware/optee, but it's the matching that - * matters. - */ - for_each_matching_node(node, optee_match) - of_platform_device_create(node, NULL, NULL); + /* Node is supposed to be below /firmware */ + fw_np = of_find_node_by_name(NULL, "firmware"); + if (!fw_np) + return -ENODEV; + + np = of_find_matching_node(fw_np, optee_match); + of_node_put(fw_np); + if (!np) + return -ENODEV; + + optee = optee_probe(np); + of_node_put(np); + + if (IS_ERR(optee)) + return PTR_ERR(optee); - return platform_driver_register(&optee_driver); + optee_svc = optee; + + return 0; } module_init(optee_driver_init); static void __exit optee_driver_exit(void) { - platform_driver_unregister(&optee_driver); + struct optee *optee = optee_svc; + + optee_svc = NULL; + if (optee) + optee_remove(optee); } module_exit(optee_driver_exit); |