162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2018-2019 Linaro Ltd. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/delay.h> 762306a36Sopenharmony_ci#include <linux/of.h> 862306a36Sopenharmony_ci#include <linux/hw_random.h> 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/tee_drv.h> 1362306a36Sopenharmony_ci#include <linux/uuid.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define DRIVER_NAME "optee-rng" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define TEE_ERROR_HEALTH_TEST_FAIL 0x00000001 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* 2062306a36Sopenharmony_ci * TA_CMD_GET_ENTROPY - Get Entropy from RNG 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci * param[0] (inout memref) - Entropy buffer memory reference 2362306a36Sopenharmony_ci * param[1] unused 2462306a36Sopenharmony_ci * param[2] unused 2562306a36Sopenharmony_ci * param[3] unused 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * Result: 2862306a36Sopenharmony_ci * TEE_SUCCESS - Invoke command success 2962306a36Sopenharmony_ci * TEE_ERROR_BAD_PARAMETERS - Incorrect input param 3062306a36Sopenharmony_ci * TEE_ERROR_NOT_SUPPORTED - Requested entropy size greater than size of pool 3162306a36Sopenharmony_ci * TEE_ERROR_HEALTH_TEST_FAIL - Continuous health testing failed 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_ci#define TA_CMD_GET_ENTROPY 0x0 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* 3662306a36Sopenharmony_ci * TA_CMD_GET_RNG_INFO - Get RNG information 3762306a36Sopenharmony_ci * 3862306a36Sopenharmony_ci * param[0] (out value) - value.a: RNG data-rate in bytes per second 3962306a36Sopenharmony_ci * value.b: Quality/Entropy per 1024 bit of data 4062306a36Sopenharmony_ci * param[1] unused 4162306a36Sopenharmony_ci * param[2] unused 4262306a36Sopenharmony_ci * param[3] unused 4362306a36Sopenharmony_ci * 4462306a36Sopenharmony_ci * Result: 4562306a36Sopenharmony_ci * TEE_SUCCESS - Invoke command success 4662306a36Sopenharmony_ci * TEE_ERROR_BAD_PARAMETERS - Incorrect input param 4762306a36Sopenharmony_ci */ 4862306a36Sopenharmony_ci#define TA_CMD_GET_RNG_INFO 0x1 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#define MAX_ENTROPY_REQ_SZ (4 * 1024) 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/** 5362306a36Sopenharmony_ci * struct optee_rng_private - OP-TEE Random Number Generator private data 5462306a36Sopenharmony_ci * @dev: OP-TEE based RNG device. 5562306a36Sopenharmony_ci * @ctx: OP-TEE context handler. 5662306a36Sopenharmony_ci * @session_id: RNG TA session identifier. 5762306a36Sopenharmony_ci * @data_rate: RNG data rate. 5862306a36Sopenharmony_ci * @entropy_shm_pool: Memory pool shared with RNG device. 5962306a36Sopenharmony_ci * @optee_rng: OP-TEE RNG driver structure. 6062306a36Sopenharmony_ci */ 6162306a36Sopenharmony_cistruct optee_rng_private { 6262306a36Sopenharmony_ci struct device *dev; 6362306a36Sopenharmony_ci struct tee_context *ctx; 6462306a36Sopenharmony_ci u32 session_id; 6562306a36Sopenharmony_ci u32 data_rate; 6662306a36Sopenharmony_ci struct tee_shm *entropy_shm_pool; 6762306a36Sopenharmony_ci struct hwrng optee_rng; 6862306a36Sopenharmony_ci}; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci#define to_optee_rng_private(r) \ 7162306a36Sopenharmony_ci container_of(r, struct optee_rng_private, optee_rng) 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic size_t get_optee_rng_data(struct optee_rng_private *pvt_data, 7462306a36Sopenharmony_ci void *buf, size_t req_size) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci int ret = 0; 7762306a36Sopenharmony_ci u8 *rng_data = NULL; 7862306a36Sopenharmony_ci size_t rng_size = 0; 7962306a36Sopenharmony_ci struct tee_ioctl_invoke_arg inv_arg; 8062306a36Sopenharmony_ci struct tee_param param[4]; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci memset(&inv_arg, 0, sizeof(inv_arg)); 8362306a36Sopenharmony_ci memset(¶m, 0, sizeof(param)); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci /* Invoke TA_CMD_GET_ENTROPY function of Trusted App */ 8662306a36Sopenharmony_ci inv_arg.func = TA_CMD_GET_ENTROPY; 8762306a36Sopenharmony_ci inv_arg.session = pvt_data->session_id; 8862306a36Sopenharmony_ci inv_arg.num_params = 4; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci /* Fill invoke cmd params */ 9162306a36Sopenharmony_ci param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT; 9262306a36Sopenharmony_ci param[0].u.memref.shm = pvt_data->entropy_shm_pool; 9362306a36Sopenharmony_ci param[0].u.memref.size = req_size; 9462306a36Sopenharmony_ci param[0].u.memref.shm_offs = 0; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci ret = tee_client_invoke_func(pvt_data->ctx, &inv_arg, param); 9762306a36Sopenharmony_ci if ((ret < 0) || (inv_arg.ret != 0)) { 9862306a36Sopenharmony_ci dev_err(pvt_data->dev, "TA_CMD_GET_ENTROPY invoke err: %x\n", 9962306a36Sopenharmony_ci inv_arg.ret); 10062306a36Sopenharmony_ci return 0; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci rng_data = tee_shm_get_va(pvt_data->entropy_shm_pool, 0); 10462306a36Sopenharmony_ci if (IS_ERR(rng_data)) { 10562306a36Sopenharmony_ci dev_err(pvt_data->dev, "tee_shm_get_va failed\n"); 10662306a36Sopenharmony_ci return 0; 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci rng_size = param[0].u.memref.size; 11062306a36Sopenharmony_ci memcpy(buf, rng_data, rng_size); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci return rng_size; 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic int optee_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct optee_rng_private *pvt_data = to_optee_rng_private(rng); 11862306a36Sopenharmony_ci size_t read = 0, rng_size; 11962306a36Sopenharmony_ci int timeout = 1; 12062306a36Sopenharmony_ci u8 *data = buf; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (max > MAX_ENTROPY_REQ_SZ) 12362306a36Sopenharmony_ci max = MAX_ENTROPY_REQ_SZ; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci while (read < max) { 12662306a36Sopenharmony_ci rng_size = get_optee_rng_data(pvt_data, data, (max - read)); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci data += rng_size; 12962306a36Sopenharmony_ci read += rng_size; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci if (wait && pvt_data->data_rate) { 13262306a36Sopenharmony_ci if ((timeout-- == 0) || (read == max)) 13362306a36Sopenharmony_ci return read; 13462306a36Sopenharmony_ci msleep((1000 * (max - read)) / pvt_data->data_rate); 13562306a36Sopenharmony_ci } else { 13662306a36Sopenharmony_ci return read; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci return read; 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic int optee_rng_init(struct hwrng *rng) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci struct optee_rng_private *pvt_data = to_optee_rng_private(rng); 14662306a36Sopenharmony_ci struct tee_shm *entropy_shm_pool = NULL; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci entropy_shm_pool = tee_shm_alloc_kernel_buf(pvt_data->ctx, 14962306a36Sopenharmony_ci MAX_ENTROPY_REQ_SZ); 15062306a36Sopenharmony_ci if (IS_ERR(entropy_shm_pool)) { 15162306a36Sopenharmony_ci dev_err(pvt_data->dev, "tee_shm_alloc_kernel_buf failed\n"); 15262306a36Sopenharmony_ci return PTR_ERR(entropy_shm_pool); 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci pvt_data->entropy_shm_pool = entropy_shm_pool; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci return 0; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic void optee_rng_cleanup(struct hwrng *rng) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct optee_rng_private *pvt_data = to_optee_rng_private(rng); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci tee_shm_free(pvt_data->entropy_shm_pool); 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic struct optee_rng_private pvt_data = { 16862306a36Sopenharmony_ci .optee_rng = { 16962306a36Sopenharmony_ci .name = DRIVER_NAME, 17062306a36Sopenharmony_ci .init = optee_rng_init, 17162306a36Sopenharmony_ci .cleanup = optee_rng_cleanup, 17262306a36Sopenharmony_ci .read = optee_rng_read, 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci}; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic int get_optee_rng_info(struct device *dev) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci int ret = 0; 17962306a36Sopenharmony_ci struct tee_ioctl_invoke_arg inv_arg; 18062306a36Sopenharmony_ci struct tee_param param[4]; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci memset(&inv_arg, 0, sizeof(inv_arg)); 18362306a36Sopenharmony_ci memset(¶m, 0, sizeof(param)); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci /* Invoke TA_CMD_GET_RNG_INFO function of Trusted App */ 18662306a36Sopenharmony_ci inv_arg.func = TA_CMD_GET_RNG_INFO; 18762306a36Sopenharmony_ci inv_arg.session = pvt_data.session_id; 18862306a36Sopenharmony_ci inv_arg.num_params = 4; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci /* Fill invoke cmd params */ 19162306a36Sopenharmony_ci param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci ret = tee_client_invoke_func(pvt_data.ctx, &inv_arg, param); 19462306a36Sopenharmony_ci if ((ret < 0) || (inv_arg.ret != 0)) { 19562306a36Sopenharmony_ci dev_err(dev, "TA_CMD_GET_RNG_INFO invoke err: %x\n", 19662306a36Sopenharmony_ci inv_arg.ret); 19762306a36Sopenharmony_ci return -EINVAL; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci pvt_data.data_rate = param[0].u.value.a; 20162306a36Sopenharmony_ci pvt_data.optee_rng.quality = param[0].u.value.b; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci return 0; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci if (ver->impl_id == TEE_IMPL_ID_OPTEE) 20962306a36Sopenharmony_ci return 1; 21062306a36Sopenharmony_ci else 21162306a36Sopenharmony_ci return 0; 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistatic int optee_rng_probe(struct device *dev) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci struct tee_client_device *rng_device = to_tee_client_device(dev); 21762306a36Sopenharmony_ci int ret = 0, err = -ENODEV; 21862306a36Sopenharmony_ci struct tee_ioctl_open_session_arg sess_arg; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci memset(&sess_arg, 0, sizeof(sess_arg)); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci /* Open context with TEE driver */ 22362306a36Sopenharmony_ci pvt_data.ctx = tee_client_open_context(NULL, optee_ctx_match, NULL, 22462306a36Sopenharmony_ci NULL); 22562306a36Sopenharmony_ci if (IS_ERR(pvt_data.ctx)) 22662306a36Sopenharmony_ci return -ENODEV; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci /* Open session with hwrng Trusted App */ 22962306a36Sopenharmony_ci export_uuid(sess_arg.uuid, &rng_device->id.uuid); 23062306a36Sopenharmony_ci sess_arg.clnt_login = TEE_IOCTL_LOGIN_PUBLIC; 23162306a36Sopenharmony_ci sess_arg.num_params = 0; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci ret = tee_client_open_session(pvt_data.ctx, &sess_arg, NULL); 23462306a36Sopenharmony_ci if ((ret < 0) || (sess_arg.ret != 0)) { 23562306a36Sopenharmony_ci dev_err(dev, "tee_client_open_session failed, err: %x\n", 23662306a36Sopenharmony_ci sess_arg.ret); 23762306a36Sopenharmony_ci err = -EINVAL; 23862306a36Sopenharmony_ci goto out_ctx; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci pvt_data.session_id = sess_arg.session; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci err = get_optee_rng_info(dev); 24362306a36Sopenharmony_ci if (err) 24462306a36Sopenharmony_ci goto out_sess; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci err = devm_hwrng_register(dev, &pvt_data.optee_rng); 24762306a36Sopenharmony_ci if (err) { 24862306a36Sopenharmony_ci dev_err(dev, "hwrng registration failed (%d)\n", err); 24962306a36Sopenharmony_ci goto out_sess; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci pvt_data.dev = dev; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci return 0; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ciout_sess: 25762306a36Sopenharmony_ci tee_client_close_session(pvt_data.ctx, pvt_data.session_id); 25862306a36Sopenharmony_ciout_ctx: 25962306a36Sopenharmony_ci tee_client_close_context(pvt_data.ctx); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci return err; 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic int optee_rng_remove(struct device *dev) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci tee_client_close_session(pvt_data.ctx, pvt_data.session_id); 26762306a36Sopenharmony_ci tee_client_close_context(pvt_data.ctx); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci return 0; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic const struct tee_client_device_id optee_rng_id_table[] = { 27362306a36Sopenharmony_ci {UUID_INIT(0xab7a617c, 0xb8e7, 0x4d8f, 27462306a36Sopenharmony_ci 0x83, 0x01, 0xd0, 0x9b, 0x61, 0x03, 0x6b, 0x64)}, 27562306a36Sopenharmony_ci {} 27662306a36Sopenharmony_ci}; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(tee, optee_rng_id_table); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic struct tee_client_driver optee_rng_driver = { 28162306a36Sopenharmony_ci .id_table = optee_rng_id_table, 28262306a36Sopenharmony_ci .driver = { 28362306a36Sopenharmony_ci .name = DRIVER_NAME, 28462306a36Sopenharmony_ci .bus = &tee_bus_type, 28562306a36Sopenharmony_ci .probe = optee_rng_probe, 28662306a36Sopenharmony_ci .remove = optee_rng_remove, 28762306a36Sopenharmony_ci }, 28862306a36Sopenharmony_ci}; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cistatic int __init optee_rng_mod_init(void) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci return driver_register(&optee_rng_driver.driver); 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic void __exit optee_rng_mod_exit(void) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci driver_unregister(&optee_rng_driver.driver); 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cimodule_init(optee_rng_mod_init); 30162306a36Sopenharmony_cimodule_exit(optee_rng_mod_exit); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 30462306a36Sopenharmony_ciMODULE_AUTHOR("Sumit Garg <sumit.garg@linaro.org>"); 30562306a36Sopenharmony_ciMODULE_DESCRIPTION("OP-TEE based random number generator driver"); 306