linux/drivers/crypto/hisilicon/trng/trng.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/* Copyright (c) 2019 HiSilicon Limited. */
   3
   4#include <linux/acpi.h>
   5#include <linux/crypto.h>
   6#include <linux/err.h>
   7#include <linux/hw_random.h>
   8#include <linux/io.h>
   9#include <linux/iopoll.h>
  10#include <linux/kernel.h>
  11#include <linux/list.h>
  12#include <linux/module.h>
  13#include <linux/mutex.h>
  14#include <linux/platform_device.h>
  15#include <linux/random.h>
  16#include <crypto/internal/rng.h>
  17
  18#define HISI_TRNG_REG           0x00F0
  19#define HISI_TRNG_BYTES         4
  20#define HISI_TRNG_QUALITY       512
  21#define HISI_TRNG_VERSION       0x01B8
  22#define HISI_TRNG_VER_V1        GENMASK(31, 0)
  23#define SLEEP_US                10
  24#define TIMEOUT_US              10000
  25#define SW_DRBG_NUM_SHIFT       2
  26#define SW_DRBG_KEY_BASE        0x082C
  27#define SW_DRBG_SEED(n)         (SW_DRBG_KEY_BASE - ((n) << SW_DRBG_NUM_SHIFT))
  28#define SW_DRBG_SEED_REGS_NUM   12
  29#define SW_DRBG_SEED_SIZE       48
  30#define SW_DRBG_BLOCKS          0x0830
  31#define SW_DRBG_INIT            0x0834
  32#define SW_DRBG_GEN             0x083c
  33#define SW_DRBG_STATUS          0x0840
  34#define SW_DRBG_BLOCKS_NUM      4095
  35#define SW_DRBG_DATA_BASE       0x0850
  36#define SW_DRBG_DATA_NUM        4
  37#define SW_DRBG_DATA(n)         (SW_DRBG_DATA_BASE - ((n) << SW_DRBG_NUM_SHIFT))
  38#define SW_DRBG_BYTES           16
  39#define SW_DRBG_ENABLE_SHIFT    12
  40#define SEED_SHIFT_24           24
  41#define SEED_SHIFT_16           16
  42#define SEED_SHIFT_8            8
  43
  44struct hisi_trng_list {
  45        struct mutex lock;
  46        struct list_head list;
  47        bool is_init;
  48};
  49
  50struct hisi_trng {
  51        void __iomem *base;
  52        struct hisi_trng_list *trng_list;
  53        struct list_head list;
  54        struct hwrng rng;
  55        u32 ver;
  56        bool is_used;
  57        struct mutex mutex;
  58};
  59
  60struct hisi_trng_ctx {
  61        struct hisi_trng *trng;
  62};
  63
  64static atomic_t trng_active_devs;
  65static struct hisi_trng_list trng_devices;
  66
  67static void hisi_trng_set_seed(struct hisi_trng *trng, const u8 *seed)
  68{
  69        u32 val, seed_reg, i;
  70
  71        for (i = 0; i < SW_DRBG_SEED_SIZE;
  72             i += SW_DRBG_SEED_SIZE / SW_DRBG_SEED_REGS_NUM) {
  73                val = seed[i] << SEED_SHIFT_24;
  74                val |= seed[i + 1UL] << SEED_SHIFT_16;
  75                val |= seed[i + 2UL] << SEED_SHIFT_8;
  76                val |= seed[i + 3UL];
  77
  78                seed_reg = (i >> SW_DRBG_NUM_SHIFT) % SW_DRBG_SEED_REGS_NUM;
  79                writel(val, trng->base + SW_DRBG_SEED(seed_reg));
  80        }
  81}
  82
  83static int hisi_trng_seed(struct crypto_rng *tfm, const u8 *seed,
  84                          unsigned int slen)
  85{
  86        struct hisi_trng_ctx *ctx = crypto_rng_ctx(tfm);
  87        struct hisi_trng *trng = ctx->trng;
  88        u32 val = 0;
  89        int ret = 0;
  90
  91        if (slen < SW_DRBG_SEED_SIZE) {
  92                pr_err("slen(%u) is not matched with trng(%d)\n", slen,
  93                        SW_DRBG_SEED_SIZE);
  94                return -EINVAL;
  95        }
  96
  97        writel(0x0, trng->base + SW_DRBG_BLOCKS);
  98        hisi_trng_set_seed(trng, seed);
  99
 100        writel(SW_DRBG_BLOCKS_NUM | (0x1 << SW_DRBG_ENABLE_SHIFT),
 101               trng->base + SW_DRBG_BLOCKS);
 102        writel(0x1, trng->base + SW_DRBG_INIT);
 103
 104        ret = readl_relaxed_poll_timeout(trng->base + SW_DRBG_STATUS,
 105                                        val, val & BIT(0), SLEEP_US, TIMEOUT_US);
 106        if (ret)
 107                pr_err("fail to init trng(%d)\n", ret);
 108
 109        return ret;
 110}
 111
 112static int hisi_trng_generate(struct crypto_rng *tfm, const u8 *src,
 113                              unsigned int slen, u8 *dstn, unsigned int dlen)
 114{
 115        struct hisi_trng_ctx *ctx = crypto_rng_ctx(tfm);
 116        struct hisi_trng *trng = ctx->trng;
 117        u32 data[SW_DRBG_DATA_NUM];
 118        u32 currsize = 0;
 119        u32 val = 0;
 120        int ret;
 121        u32 i;
 122
 123        if (dlen > SW_DRBG_BLOCKS_NUM * SW_DRBG_BYTES || dlen == 0) {
 124                pr_err("dlen(%d) exceeds limit(%d)!\n", dlen,
 125                        SW_DRBG_BLOCKS_NUM * SW_DRBG_BYTES);
 126                return -EINVAL;
 127        }
 128
 129        do {
 130                ret = readl_relaxed_poll_timeout(trng->base + SW_DRBG_STATUS,
 131                     val, val & BIT(1), SLEEP_US, TIMEOUT_US);
 132                if (ret) {
 133                        pr_err("fail to generate random number(%d)!\n", ret);
 134                        break;
 135                }
 136
 137                for (i = 0; i < SW_DRBG_DATA_NUM; i++)
 138                        data[i] = readl(trng->base + SW_DRBG_DATA(i));
 139
 140                if (dlen - currsize >= SW_DRBG_BYTES) {
 141                        memcpy(dstn + currsize, data, SW_DRBG_BYTES);
 142                        currsize += SW_DRBG_BYTES;
 143                } else {
 144                        memcpy(dstn + currsize, data, dlen - currsize);
 145                        currsize = dlen;
 146                }
 147
 148                writel(0x1, trng->base + SW_DRBG_GEN);
 149        } while (currsize < dlen);
 150
 151        return ret;
 152}
 153
 154static int hisi_trng_init(struct crypto_tfm *tfm)
 155{
 156        struct hisi_trng_ctx *ctx = crypto_tfm_ctx(tfm);
 157        struct hisi_trng *trng;
 158        int ret = -EBUSY;
 159
 160        mutex_lock(&trng_devices.lock);
 161        list_for_each_entry(trng, &trng_devices.list, list) {
 162                if (!trng->is_used) {
 163                        trng->is_used = true;
 164                        ctx->trng = trng;
 165                        ret = 0;
 166                        break;
 167                }
 168        }
 169        mutex_unlock(&trng_devices.lock);
 170
 171        return ret;
 172}
 173
 174static void hisi_trng_exit(struct crypto_tfm *tfm)
 175{
 176        struct hisi_trng_ctx *ctx = crypto_tfm_ctx(tfm);
 177
 178        mutex_lock(&trng_devices.lock);
 179        ctx->trng->is_used = false;
 180        mutex_unlock(&trng_devices.lock);
 181}
 182
 183static int hisi_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
 184{
 185        struct hisi_trng *trng;
 186        int currsize = 0;
 187        u32 val = 0;
 188        u32 ret;
 189
 190        trng = container_of(rng, struct hisi_trng, rng);
 191
 192        do {
 193                ret = readl_poll_timeout(trng->base + HISI_TRNG_REG, val,
 194                                         val, SLEEP_US, TIMEOUT_US);
 195                if (ret)
 196                        return currsize;
 197
 198                if (max - currsize >= HISI_TRNG_BYTES) {
 199                        memcpy(buf + currsize, &val, HISI_TRNG_BYTES);
 200                        currsize += HISI_TRNG_BYTES;
 201                        if (currsize == max)
 202                                return currsize;
 203                        continue;
 204                }
 205
 206                /* copy remaining bytes */
 207                memcpy(buf + currsize, &val, max - currsize);
 208                currsize = max;
 209        } while (currsize < max);
 210
 211        return currsize;
 212}
 213
 214static struct rng_alg hisi_trng_alg = {
 215        .generate = hisi_trng_generate,
 216        .seed = hisi_trng_seed,
 217        .seedsize = SW_DRBG_SEED_SIZE,
 218        .base = {
 219                .cra_name = "stdrng",
 220                .cra_driver_name = "hisi_stdrng",
 221                .cra_priority = 300,
 222                .cra_ctxsize = sizeof(struct hisi_trng_ctx),
 223                .cra_module = THIS_MODULE,
 224                .cra_init = hisi_trng_init,
 225                .cra_exit = hisi_trng_exit,
 226        },
 227};
 228
 229static void hisi_trng_add_to_list(struct hisi_trng *trng)
 230{
 231        mutex_lock(&trng_devices.lock);
 232        list_add_tail(&trng->list, &trng_devices.list);
 233        mutex_unlock(&trng_devices.lock);
 234}
 235
 236static int hisi_trng_del_from_list(struct hisi_trng *trng)
 237{
 238        int ret = -EBUSY;
 239
 240        mutex_lock(&trng_devices.lock);
 241        if (!trng->is_used) {
 242                list_del(&trng->list);
 243                ret = 0;
 244        }
 245        mutex_unlock(&trng_devices.lock);
 246
 247        return ret;
 248}
 249
 250static int hisi_trng_probe(struct platform_device *pdev)
 251{
 252        struct hisi_trng *trng;
 253        int ret;
 254
 255        trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL);
 256        if (!trng)
 257                return -ENOMEM;
 258
 259        platform_set_drvdata(pdev, trng);
 260
 261        trng->base = devm_platform_ioremap_resource(pdev, 0);
 262        if (IS_ERR(trng->base))
 263                return PTR_ERR(trng->base);
 264
 265        trng->is_used = false;
 266        trng->ver = readl(trng->base + HISI_TRNG_VERSION);
 267        if (!trng_devices.is_init) {
 268                INIT_LIST_HEAD(&trng_devices.list);
 269                mutex_init(&trng_devices.lock);
 270                trng_devices.is_init = true;
 271        }
 272
 273        hisi_trng_add_to_list(trng);
 274        if (trng->ver != HISI_TRNG_VER_V1 &&
 275            atomic_inc_return(&trng_active_devs) == 1) {
 276                ret = crypto_register_rng(&hisi_trng_alg);
 277                if (ret) {
 278                        dev_err(&pdev->dev,
 279                                "failed to register crypto(%d)\n", ret);
 280                        atomic_dec_return(&trng_active_devs);
 281                        goto err_remove_from_list;
 282                }
 283        }
 284
 285        trng->rng.name = pdev->name;
 286        trng->rng.read = hisi_trng_read;
 287        trng->rng.quality = HISI_TRNG_QUALITY;
 288        ret = devm_hwrng_register(&pdev->dev, &trng->rng);
 289        if (ret) {
 290                dev_err(&pdev->dev, "failed to register hwrng: %d!\n", ret);
 291                goto err_crypto_unregister;
 292        }
 293
 294        return ret;
 295
 296err_crypto_unregister:
 297        if (trng->ver != HISI_TRNG_VER_V1 &&
 298            atomic_dec_return(&trng_active_devs) == 0)
 299                crypto_unregister_rng(&hisi_trng_alg);
 300
 301err_remove_from_list:
 302        hisi_trng_del_from_list(trng);
 303        return ret;
 304}
 305
 306static int hisi_trng_remove(struct platform_device *pdev)
 307{
 308        struct hisi_trng *trng = platform_get_drvdata(pdev);
 309
 310        /* Wait until the task is finished */
 311        while (hisi_trng_del_from_list(trng))
 312                ;
 313
 314        if (trng->ver != HISI_TRNG_VER_V1 &&
 315            atomic_dec_return(&trng_active_devs) == 0)
 316                crypto_unregister_rng(&hisi_trng_alg);
 317
 318        return 0;
 319}
 320
 321static const struct acpi_device_id hisi_trng_acpi_match[] = {
 322        { "HISI02B3", 0 },
 323        { }
 324};
 325MODULE_DEVICE_TABLE(acpi, hisi_trng_acpi_match);
 326
 327static struct platform_driver hisi_trng_driver = {
 328        .probe          = hisi_trng_probe,
 329        .remove         = hisi_trng_remove,
 330        .driver         = {
 331                .name   = "hisi-trng-v2",
 332                .acpi_match_table = ACPI_PTR(hisi_trng_acpi_match),
 333        },
 334};
 335
 336module_platform_driver(hisi_trng_driver);
 337
 338MODULE_LICENSE("GPL v2");
 339MODULE_AUTHOR("Weili Qian <qianweili@huawei.com>");
 340MODULE_AUTHOR("Zaibo Xu <xuzaibo@huawei.com>");
 341MODULE_DESCRIPTION("HiSilicon true random number generator V2 driver");
 342