306 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) 2018-2019 Linaro Ltd.4 */5 6#include <linux/delay.h>7#include <linux/of.h>8#include <linux/hw_random.h>9#include <linux/kernel.h>10#include <linux/module.h>11#include <linux/slab.h>12#include <linux/tee_drv.h>13#include <linux/uuid.h>14 15#define DRIVER_NAME "optee-rng"16 17#define TEE_ERROR_HEALTH_TEST_FAIL 0x0000000118 19/*20 * TA_CMD_GET_ENTROPY - Get Entropy from RNG21 *22 * param[0] (inout memref) - Entropy buffer memory reference23 * param[1] unused24 * param[2] unused25 * param[3] unused26 *27 * Result:28 * TEE_SUCCESS - Invoke command success29 * TEE_ERROR_BAD_PARAMETERS - Incorrect input param30 * TEE_ERROR_NOT_SUPPORTED - Requested entropy size greater than size of pool31 * TEE_ERROR_HEALTH_TEST_FAIL - Continuous health testing failed32 */33#define TA_CMD_GET_ENTROPY 0x034 35/*36 * TA_CMD_GET_RNG_INFO - Get RNG information37 *38 * param[0] (out value) - value.a: RNG data-rate in bytes per second39 * value.b: Quality/Entropy per 1024 bit of data40 * param[1] unused41 * param[2] unused42 * param[3] unused43 *44 * Result:45 * TEE_SUCCESS - Invoke command success46 * TEE_ERROR_BAD_PARAMETERS - Incorrect input param47 */48#define TA_CMD_GET_RNG_INFO 0x149 50#define MAX_ENTROPY_REQ_SZ (4 * 1024)51 52/**53 * struct optee_rng_private - OP-TEE Random Number Generator private data54 * @dev: OP-TEE based RNG device.55 * @ctx: OP-TEE context handler.56 * @session_id: RNG TA session identifier.57 * @data_rate: RNG data rate.58 * @entropy_shm_pool: Memory pool shared with RNG device.59 * @optee_rng: OP-TEE RNG driver structure.60 */61struct optee_rng_private {62 struct device *dev;63 struct tee_context *ctx;64 u32 session_id;65 u32 data_rate;66 struct tee_shm *entropy_shm_pool;67 struct hwrng optee_rng;68};69 70#define to_optee_rng_private(r) \71 container_of(r, struct optee_rng_private, optee_rng)72 73static size_t get_optee_rng_data(struct optee_rng_private *pvt_data,74 void *buf, size_t req_size)75{76 int ret = 0;77 u8 *rng_data = NULL;78 size_t rng_size = 0;79 struct tee_ioctl_invoke_arg inv_arg;80 struct tee_param param[4];81 82 memset(&inv_arg, 0, sizeof(inv_arg));83 memset(¶m, 0, sizeof(param));84 85 /* Invoke TA_CMD_GET_ENTROPY function of Trusted App */86 inv_arg.func = TA_CMD_GET_ENTROPY;87 inv_arg.session = pvt_data->session_id;88 inv_arg.num_params = 4;89 90 /* Fill invoke cmd params */91 param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT;92 param[0].u.memref.shm = pvt_data->entropy_shm_pool;93 param[0].u.memref.size = req_size;94 param[0].u.memref.shm_offs = 0;95 96 ret = tee_client_invoke_func(pvt_data->ctx, &inv_arg, param);97 if ((ret < 0) || (inv_arg.ret != 0)) {98 dev_err(pvt_data->dev, "TA_CMD_GET_ENTROPY invoke err: %x\n",99 inv_arg.ret);100 return 0;101 }102 103 rng_data = tee_shm_get_va(pvt_data->entropy_shm_pool, 0);104 if (IS_ERR(rng_data)) {105 dev_err(pvt_data->dev, "tee_shm_get_va failed\n");106 return 0;107 }108 109 rng_size = param[0].u.memref.size;110 memcpy(buf, rng_data, rng_size);111 112 return rng_size;113}114 115static int optee_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait)116{117 struct optee_rng_private *pvt_data = to_optee_rng_private(rng);118 size_t read = 0, rng_size;119 int timeout = 1;120 u8 *data = buf;121 122 if (max > MAX_ENTROPY_REQ_SZ)123 max = MAX_ENTROPY_REQ_SZ;124 125 while (read < max) {126 rng_size = get_optee_rng_data(pvt_data, data, (max - read));127 128 data += rng_size;129 read += rng_size;130 131 if (wait && pvt_data->data_rate) {132 if ((timeout-- == 0) || (read == max))133 return read;134 msleep((1000 * (max - read)) / pvt_data->data_rate);135 } else {136 return read;137 }138 }139 140 return read;141}142 143static int optee_rng_init(struct hwrng *rng)144{145 struct optee_rng_private *pvt_data = to_optee_rng_private(rng);146 struct tee_shm *entropy_shm_pool = NULL;147 148 entropy_shm_pool = tee_shm_alloc_kernel_buf(pvt_data->ctx,149 MAX_ENTROPY_REQ_SZ);150 if (IS_ERR(entropy_shm_pool)) {151 dev_err(pvt_data->dev, "tee_shm_alloc_kernel_buf failed\n");152 return PTR_ERR(entropy_shm_pool);153 }154 155 pvt_data->entropy_shm_pool = entropy_shm_pool;156 157 return 0;158}159 160static void optee_rng_cleanup(struct hwrng *rng)161{162 struct optee_rng_private *pvt_data = to_optee_rng_private(rng);163 164 tee_shm_free(pvt_data->entropy_shm_pool);165}166 167static struct optee_rng_private pvt_data = {168 .optee_rng = {169 .name = DRIVER_NAME,170 .init = optee_rng_init,171 .cleanup = optee_rng_cleanup,172 .read = optee_rng_read,173 }174};175 176static int get_optee_rng_info(struct device *dev)177{178 int ret = 0;179 struct tee_ioctl_invoke_arg inv_arg;180 struct tee_param param[4];181 182 memset(&inv_arg, 0, sizeof(inv_arg));183 memset(¶m, 0, sizeof(param));184 185 /* Invoke TA_CMD_GET_RNG_INFO function of Trusted App */186 inv_arg.func = TA_CMD_GET_RNG_INFO;187 inv_arg.session = pvt_data.session_id;188 inv_arg.num_params = 4;189 190 /* Fill invoke cmd params */191 param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT;192 193 ret = tee_client_invoke_func(pvt_data.ctx, &inv_arg, param);194 if ((ret < 0) || (inv_arg.ret != 0)) {195 dev_err(dev, "TA_CMD_GET_RNG_INFO invoke err: %x\n",196 inv_arg.ret);197 return -EINVAL;198 }199 200 pvt_data.data_rate = param[0].u.value.a;201 pvt_data.optee_rng.quality = param[0].u.value.b;202 203 return 0;204}205 206static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data)207{208 if (ver->impl_id == TEE_IMPL_ID_OPTEE)209 return 1;210 else211 return 0;212}213 214static int optee_rng_probe(struct device *dev)215{216 struct tee_client_device *rng_device = to_tee_client_device(dev);217 int ret = 0, err = -ENODEV;218 struct tee_ioctl_open_session_arg sess_arg;219 220 memset(&sess_arg, 0, sizeof(sess_arg));221 222 /* Open context with TEE driver */223 pvt_data.ctx = tee_client_open_context(NULL, optee_ctx_match, NULL,224 NULL);225 if (IS_ERR(pvt_data.ctx))226 return -ENODEV;227 228 /* Open session with hwrng Trusted App */229 export_uuid(sess_arg.uuid, &rng_device->id.uuid);230 sess_arg.clnt_login = TEE_IOCTL_LOGIN_PUBLIC;231 sess_arg.num_params = 0;232 233 ret = tee_client_open_session(pvt_data.ctx, &sess_arg, NULL);234 if ((ret < 0) || (sess_arg.ret != 0)) {235 dev_err(dev, "tee_client_open_session failed, err: %x\n",236 sess_arg.ret);237 err = -EINVAL;238 goto out_ctx;239 }240 pvt_data.session_id = sess_arg.session;241 242 err = get_optee_rng_info(dev);243 if (err)244 goto out_sess;245 246 err = devm_hwrng_register(dev, &pvt_data.optee_rng);247 if (err) {248 dev_err(dev, "hwrng registration failed (%d)\n", err);249 goto out_sess;250 }251 252 pvt_data.dev = dev;253 254 return 0;255 256out_sess:257 tee_client_close_session(pvt_data.ctx, pvt_data.session_id);258out_ctx:259 tee_client_close_context(pvt_data.ctx);260 261 return err;262}263 264static int optee_rng_remove(struct device *dev)265{266 tee_client_close_session(pvt_data.ctx, pvt_data.session_id);267 tee_client_close_context(pvt_data.ctx);268 269 return 0;270}271 272static const struct tee_client_device_id optee_rng_id_table[] = {273 {UUID_INIT(0xab7a617c, 0xb8e7, 0x4d8f,274 0x83, 0x01, 0xd0, 0x9b, 0x61, 0x03, 0x6b, 0x64)},275 {}276};277 278MODULE_DEVICE_TABLE(tee, optee_rng_id_table);279 280static struct tee_client_driver optee_rng_driver = {281 .id_table = optee_rng_id_table,282 .driver = {283 .name = DRIVER_NAME,284 .bus = &tee_bus_type,285 .probe = optee_rng_probe,286 .remove = optee_rng_remove,287 },288};289 290static int __init optee_rng_mod_init(void)291{292 return driver_register(&optee_rng_driver.driver);293}294 295static void __exit optee_rng_mod_exit(void)296{297 driver_unregister(&optee_rng_driver.driver);298}299 300module_init(optee_rng_mod_init);301module_exit(optee_rng_mod_exit);302 303MODULE_LICENSE("GPL v2");304MODULE_AUTHOR("Sumit Garg <sumit.garg@linaro.org>");305MODULE_DESCRIPTION("OP-TEE based random number generator driver");306