linux/drivers/char/hw_random/optee-rng.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   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      0x00000001
  18
  19/*
  20 * TA_CMD_GET_ENTROPY - Get Entropy from RNG
  21 *
  22 * param[0] (inout memref) - Entropy buffer memory reference
  23 * param[1] unused
  24 * param[2] unused
  25 * param[3] unused
  26 *
  27 * Result:
  28 * TEE_SUCCESS - Invoke command success
  29 * TEE_ERROR_BAD_PARAMETERS - Incorrect input param
  30 * TEE_ERROR_NOT_SUPPORTED - Requested entropy size greater than size of pool
  31 * TEE_ERROR_HEALTH_TEST_FAIL - Continuous health testing failed
  32 */
  33#define TA_CMD_GET_ENTROPY              0x0
  34
  35/*
  36 * TA_CMD_GET_RNG_INFO - Get RNG information
  37 *
  38 * param[0] (out value) - value.a: RNG data-rate in bytes per second
  39 *                        value.b: Quality/Entropy per 1024 bit of data
  40 * param[1] unused
  41 * param[2] unused
  42 * param[3] unused
  43 *
  44 * Result:
  45 * TEE_SUCCESS - Invoke command success
  46 * TEE_ERROR_BAD_PARAMETERS - Incorrect input param
  47 */
  48#define TA_CMD_GET_RNG_INFO             0x1
  49
  50#define MAX_ENTROPY_REQ_SZ              (4 * 1024)
  51
  52/**
  53 * struct optee_rng_private - OP-TEE Random Number Generator private data
  54 * @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(&param, 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 = 0;
 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(pvt_data->ctx, MAX_ENTROPY_REQ_SZ,
 149                                         TEE_SHM_MAPPED | TEE_SHM_DMA_BUF);
 150        if (IS_ERR(entropy_shm_pool)) {
 151                dev_err(pvt_data->dev, "tee_shm_alloc 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(&param, 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        else
 211                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