18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2018-2019 Linaro Ltd. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/delay.h> 78c2ecf20Sopenharmony_ci#include <linux/of.h> 88c2ecf20Sopenharmony_ci#include <linux/hw_random.h> 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <linux/tee_drv.h> 138c2ecf20Sopenharmony_ci#include <linux/uuid.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#define DRIVER_NAME "optee-rng" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define TEE_ERROR_HEALTH_TEST_FAIL 0x00000001 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* 208c2ecf20Sopenharmony_ci * TA_CMD_GET_ENTROPY - Get Entropy from RNG 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * param[0] (inout memref) - Entropy buffer memory reference 238c2ecf20Sopenharmony_ci * param[1] unused 248c2ecf20Sopenharmony_ci * param[2] unused 258c2ecf20Sopenharmony_ci * param[3] unused 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * Result: 288c2ecf20Sopenharmony_ci * TEE_SUCCESS - Invoke command success 298c2ecf20Sopenharmony_ci * TEE_ERROR_BAD_PARAMETERS - Incorrect input param 308c2ecf20Sopenharmony_ci * TEE_ERROR_NOT_SUPPORTED - Requested entropy size greater than size of pool 318c2ecf20Sopenharmony_ci * TEE_ERROR_HEALTH_TEST_FAIL - Continuous health testing failed 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ci#define TA_CMD_GET_ENTROPY 0x0 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* 368c2ecf20Sopenharmony_ci * TA_CMD_GET_RNG_INFO - Get RNG information 378c2ecf20Sopenharmony_ci * 388c2ecf20Sopenharmony_ci * param[0] (out value) - value.a: RNG data-rate in bytes per second 398c2ecf20Sopenharmony_ci * value.b: Quality/Entropy per 1024 bit of data 408c2ecf20Sopenharmony_ci * param[1] unused 418c2ecf20Sopenharmony_ci * param[2] unused 428c2ecf20Sopenharmony_ci * param[3] unused 438c2ecf20Sopenharmony_ci * 448c2ecf20Sopenharmony_ci * Result: 458c2ecf20Sopenharmony_ci * TEE_SUCCESS - Invoke command success 468c2ecf20Sopenharmony_ci * TEE_ERROR_BAD_PARAMETERS - Incorrect input param 478c2ecf20Sopenharmony_ci */ 488c2ecf20Sopenharmony_ci#define TA_CMD_GET_RNG_INFO 0x1 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#define MAX_ENTROPY_REQ_SZ (4 * 1024) 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/** 538c2ecf20Sopenharmony_ci * struct optee_rng_private - OP-TEE Random Number Generator private data 548c2ecf20Sopenharmony_ci * @dev: OP-TEE based RNG device. 558c2ecf20Sopenharmony_ci * @ctx: OP-TEE context handler. 568c2ecf20Sopenharmony_ci * @session_id: RNG TA session identifier. 578c2ecf20Sopenharmony_ci * @data_rate: RNG data rate. 588c2ecf20Sopenharmony_ci * @entropy_shm_pool: Memory pool shared with RNG device. 598c2ecf20Sopenharmony_ci * @optee_rng: OP-TEE RNG driver structure. 608c2ecf20Sopenharmony_ci */ 618c2ecf20Sopenharmony_cistruct optee_rng_private { 628c2ecf20Sopenharmony_ci struct device *dev; 638c2ecf20Sopenharmony_ci struct tee_context *ctx; 648c2ecf20Sopenharmony_ci u32 session_id; 658c2ecf20Sopenharmony_ci u32 data_rate; 668c2ecf20Sopenharmony_ci struct tee_shm *entropy_shm_pool; 678c2ecf20Sopenharmony_ci struct hwrng optee_rng; 688c2ecf20Sopenharmony_ci}; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci#define to_optee_rng_private(r) \ 718c2ecf20Sopenharmony_ci container_of(r, struct optee_rng_private, optee_rng) 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic size_t get_optee_rng_data(struct optee_rng_private *pvt_data, 748c2ecf20Sopenharmony_ci void *buf, size_t req_size) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci int ret = 0; 778c2ecf20Sopenharmony_ci u8 *rng_data = NULL; 788c2ecf20Sopenharmony_ci size_t rng_size = 0; 798c2ecf20Sopenharmony_ci struct tee_ioctl_invoke_arg inv_arg; 808c2ecf20Sopenharmony_ci struct tee_param param[4]; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci memset(&inv_arg, 0, sizeof(inv_arg)); 838c2ecf20Sopenharmony_ci memset(¶m, 0, sizeof(param)); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci /* Invoke TA_CMD_GET_ENTROPY function of Trusted App */ 868c2ecf20Sopenharmony_ci inv_arg.func = TA_CMD_GET_ENTROPY; 878c2ecf20Sopenharmony_ci inv_arg.session = pvt_data->session_id; 888c2ecf20Sopenharmony_ci inv_arg.num_params = 4; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* Fill invoke cmd params */ 918c2ecf20Sopenharmony_ci param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT; 928c2ecf20Sopenharmony_ci param[0].u.memref.shm = pvt_data->entropy_shm_pool; 938c2ecf20Sopenharmony_ci param[0].u.memref.size = req_size; 948c2ecf20Sopenharmony_ci param[0].u.memref.shm_offs = 0; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci ret = tee_client_invoke_func(pvt_data->ctx, &inv_arg, param); 978c2ecf20Sopenharmony_ci if ((ret < 0) || (inv_arg.ret != 0)) { 988c2ecf20Sopenharmony_ci dev_err(pvt_data->dev, "TA_CMD_GET_ENTROPY invoke err: %x\n", 998c2ecf20Sopenharmony_ci inv_arg.ret); 1008c2ecf20Sopenharmony_ci return 0; 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci rng_data = tee_shm_get_va(pvt_data->entropy_shm_pool, 0); 1048c2ecf20Sopenharmony_ci if (IS_ERR(rng_data)) { 1058c2ecf20Sopenharmony_ci dev_err(pvt_data->dev, "tee_shm_get_va failed\n"); 1068c2ecf20Sopenharmony_ci return 0; 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci rng_size = param[0].u.memref.size; 1108c2ecf20Sopenharmony_ci memcpy(buf, rng_data, rng_size); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci return rng_size; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic int optee_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci struct optee_rng_private *pvt_data = to_optee_rng_private(rng); 1188c2ecf20Sopenharmony_ci size_t read = 0, rng_size = 0; 1198c2ecf20Sopenharmony_ci int timeout = 1; 1208c2ecf20Sopenharmony_ci u8 *data = buf; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci if (max > MAX_ENTROPY_REQ_SZ) 1238c2ecf20Sopenharmony_ci max = MAX_ENTROPY_REQ_SZ; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci while (read < max) { 1268c2ecf20Sopenharmony_ci rng_size = get_optee_rng_data(pvt_data, data, (max - read)); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci data += rng_size; 1298c2ecf20Sopenharmony_ci read += rng_size; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (wait && pvt_data->data_rate) { 1328c2ecf20Sopenharmony_ci if ((timeout-- == 0) || (read == max)) 1338c2ecf20Sopenharmony_ci return read; 1348c2ecf20Sopenharmony_ci msleep((1000 * (max - read)) / pvt_data->data_rate); 1358c2ecf20Sopenharmony_ci } else { 1368c2ecf20Sopenharmony_ci return read; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci return read; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic int optee_rng_init(struct hwrng *rng) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci struct optee_rng_private *pvt_data = to_optee_rng_private(rng); 1468c2ecf20Sopenharmony_ci struct tee_shm *entropy_shm_pool = NULL; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci entropy_shm_pool = tee_shm_alloc(pvt_data->ctx, MAX_ENTROPY_REQ_SZ, 1498c2ecf20Sopenharmony_ci TEE_SHM_MAPPED | TEE_SHM_DMA_BUF); 1508c2ecf20Sopenharmony_ci if (IS_ERR(entropy_shm_pool)) { 1518c2ecf20Sopenharmony_ci dev_err(pvt_data->dev, "tee_shm_alloc failed\n"); 1528c2ecf20Sopenharmony_ci return PTR_ERR(entropy_shm_pool); 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci pvt_data->entropy_shm_pool = entropy_shm_pool; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci return 0; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic void optee_rng_cleanup(struct hwrng *rng) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct optee_rng_private *pvt_data = to_optee_rng_private(rng); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci tee_shm_free(pvt_data->entropy_shm_pool); 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic struct optee_rng_private pvt_data = { 1688c2ecf20Sopenharmony_ci .optee_rng = { 1698c2ecf20Sopenharmony_ci .name = DRIVER_NAME, 1708c2ecf20Sopenharmony_ci .init = optee_rng_init, 1718c2ecf20Sopenharmony_ci .cleanup = optee_rng_cleanup, 1728c2ecf20Sopenharmony_ci .read = optee_rng_read, 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci}; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic int get_optee_rng_info(struct device *dev) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci int ret = 0; 1798c2ecf20Sopenharmony_ci struct tee_ioctl_invoke_arg inv_arg; 1808c2ecf20Sopenharmony_ci struct tee_param param[4]; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci memset(&inv_arg, 0, sizeof(inv_arg)); 1838c2ecf20Sopenharmony_ci memset(¶m, 0, sizeof(param)); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci /* Invoke TA_CMD_GET_RNG_INFO function of Trusted App */ 1868c2ecf20Sopenharmony_ci inv_arg.func = TA_CMD_GET_RNG_INFO; 1878c2ecf20Sopenharmony_ci inv_arg.session = pvt_data.session_id; 1888c2ecf20Sopenharmony_ci inv_arg.num_params = 4; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci /* Fill invoke cmd params */ 1918c2ecf20Sopenharmony_ci param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci ret = tee_client_invoke_func(pvt_data.ctx, &inv_arg, param); 1948c2ecf20Sopenharmony_ci if ((ret < 0) || (inv_arg.ret != 0)) { 1958c2ecf20Sopenharmony_ci dev_err(dev, "TA_CMD_GET_RNG_INFO invoke err: %x\n", 1968c2ecf20Sopenharmony_ci inv_arg.ret); 1978c2ecf20Sopenharmony_ci return -EINVAL; 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci pvt_data.data_rate = param[0].u.value.a; 2018c2ecf20Sopenharmony_ci pvt_data.optee_rng.quality = param[0].u.value.b; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci return 0; 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci if (ver->impl_id == TEE_IMPL_ID_OPTEE) 2098c2ecf20Sopenharmony_ci return 1; 2108c2ecf20Sopenharmony_ci else 2118c2ecf20Sopenharmony_ci return 0; 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic int optee_rng_probe(struct device *dev) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci struct tee_client_device *rng_device = to_tee_client_device(dev); 2178c2ecf20Sopenharmony_ci int ret = 0, err = -ENODEV; 2188c2ecf20Sopenharmony_ci struct tee_ioctl_open_session_arg sess_arg; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci memset(&sess_arg, 0, sizeof(sess_arg)); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci /* Open context with TEE driver */ 2238c2ecf20Sopenharmony_ci pvt_data.ctx = tee_client_open_context(NULL, optee_ctx_match, NULL, 2248c2ecf20Sopenharmony_ci NULL); 2258c2ecf20Sopenharmony_ci if (IS_ERR(pvt_data.ctx)) 2268c2ecf20Sopenharmony_ci return -ENODEV; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci /* Open session with hwrng Trusted App */ 2298c2ecf20Sopenharmony_ci export_uuid(sess_arg.uuid, &rng_device->id.uuid); 2308c2ecf20Sopenharmony_ci sess_arg.clnt_login = TEE_IOCTL_LOGIN_PUBLIC; 2318c2ecf20Sopenharmony_ci sess_arg.num_params = 0; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci ret = tee_client_open_session(pvt_data.ctx, &sess_arg, NULL); 2348c2ecf20Sopenharmony_ci if ((ret < 0) || (sess_arg.ret != 0)) { 2358c2ecf20Sopenharmony_ci dev_err(dev, "tee_client_open_session failed, err: %x\n", 2368c2ecf20Sopenharmony_ci sess_arg.ret); 2378c2ecf20Sopenharmony_ci err = -EINVAL; 2388c2ecf20Sopenharmony_ci goto out_ctx; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci pvt_data.session_id = sess_arg.session; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci err = get_optee_rng_info(dev); 2438c2ecf20Sopenharmony_ci if (err) 2448c2ecf20Sopenharmony_ci goto out_sess; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci err = hwrng_register(&pvt_data.optee_rng); 2478c2ecf20Sopenharmony_ci if (err) { 2488c2ecf20Sopenharmony_ci dev_err(dev, "hwrng registration failed (%d)\n", err); 2498c2ecf20Sopenharmony_ci goto out_sess; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci pvt_data.dev = dev; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci return 0; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ciout_sess: 2578c2ecf20Sopenharmony_ci tee_client_close_session(pvt_data.ctx, pvt_data.session_id); 2588c2ecf20Sopenharmony_ciout_ctx: 2598c2ecf20Sopenharmony_ci tee_client_close_context(pvt_data.ctx); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci return err; 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic int optee_rng_remove(struct device *dev) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci hwrng_unregister(&pvt_data.optee_rng); 2678c2ecf20Sopenharmony_ci tee_client_close_session(pvt_data.ctx, pvt_data.session_id); 2688c2ecf20Sopenharmony_ci tee_client_close_context(pvt_data.ctx); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci return 0; 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic const struct tee_client_device_id optee_rng_id_table[] = { 2748c2ecf20Sopenharmony_ci {UUID_INIT(0xab7a617c, 0xb8e7, 0x4d8f, 2758c2ecf20Sopenharmony_ci 0x83, 0x01, 0xd0, 0x9b, 0x61, 0x03, 0x6b, 0x64)}, 2768c2ecf20Sopenharmony_ci {} 2778c2ecf20Sopenharmony_ci}; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(tee, optee_rng_id_table); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic struct tee_client_driver optee_rng_driver = { 2828c2ecf20Sopenharmony_ci .id_table = optee_rng_id_table, 2838c2ecf20Sopenharmony_ci .driver = { 2848c2ecf20Sopenharmony_ci .name = DRIVER_NAME, 2858c2ecf20Sopenharmony_ci .bus = &tee_bus_type, 2868c2ecf20Sopenharmony_ci .probe = optee_rng_probe, 2878c2ecf20Sopenharmony_ci .remove = optee_rng_remove, 2888c2ecf20Sopenharmony_ci }, 2898c2ecf20Sopenharmony_ci}; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic int __init optee_rng_mod_init(void) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci return driver_register(&optee_rng_driver.driver); 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_cistatic void __exit optee_rng_mod_exit(void) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci driver_unregister(&optee_rng_driver.driver); 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cimodule_init(optee_rng_mod_init); 3028c2ecf20Sopenharmony_cimodule_exit(optee_rng_mod_exit); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 3058c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sumit Garg <sumit.garg@linaro.org>"); 3068c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("OP-TEE based random number generator driver"); 307