root/drivers/crypto/exynos-rng.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. exynos_rng_readl
  2. exynos_rng_writel
  3. exynos_rng_set_seed
  4. exynos_rng_get_random
  5. exynos_rng_reseed
  6. exynos_rng_generate
  7. exynos_rng_seed
  8. exynos_rng_kcapi_init
  9. exynos_rng_probe
  10. exynos_rng_remove
  11. exynos_rng_suspend
  12. exynos_rng_resume

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * exynos-rng.c - Random Number Generator driver for the Exynos
   4  *
   5  * Copyright (c) 2017 Krzysztof Kozlowski <krzk@kernel.org>
   6  *
   7  * Loosely based on old driver from drivers/char/hw_random/exynos-rng.c:
   8  * Copyright (C) 2012 Samsung Electronics
   9  * Jonghwa Lee <jonghwa3.lee@samsung.com>
  10  */
  11 
  12 #include <linux/clk.h>
  13 #include <linux/crypto.h>
  14 #include <linux/err.h>
  15 #include <linux/io.h>
  16 #include <linux/module.h>
  17 #include <linux/mutex.h>
  18 #include <linux/of_device.h>
  19 #include <linux/platform_device.h>
  20 
  21 #include <crypto/internal/rng.h>
  22 
  23 #define EXYNOS_RNG_CONTROL              0x0
  24 #define EXYNOS_RNG_STATUS               0x10
  25 
  26 #define EXYNOS_RNG_SEED_CONF            0x14
  27 #define EXYNOS_RNG_GEN_PRNG             BIT(1)
  28 
  29 #define EXYNOS_RNG_SEED_BASE            0x140
  30 #define EXYNOS_RNG_SEED(n)              (EXYNOS_RNG_SEED_BASE + (n * 0x4))
  31 #define EXYNOS_RNG_OUT_BASE             0x160
  32 #define EXYNOS_RNG_OUT(n)               (EXYNOS_RNG_OUT_BASE + (n * 0x4))
  33 
  34 /* EXYNOS_RNG_CONTROL bit fields */
  35 #define EXYNOS_RNG_CONTROL_START        0x18
  36 /* EXYNOS_RNG_STATUS bit fields */
  37 #define EXYNOS_RNG_STATUS_SEED_SETTING_DONE     BIT(1)
  38 #define EXYNOS_RNG_STATUS_RNG_DONE              BIT(5)
  39 
  40 /* Five seed and output registers, each 4 bytes */
  41 #define EXYNOS_RNG_SEED_REGS            5
  42 #define EXYNOS_RNG_SEED_SIZE            (EXYNOS_RNG_SEED_REGS * 4)
  43 
  44 enum exynos_prng_type {
  45         EXYNOS_PRNG_UNKNOWN = 0,
  46         EXYNOS_PRNG_EXYNOS4,
  47         EXYNOS_PRNG_EXYNOS5,
  48 };
  49 
  50 /*
  51  * Driver re-seeds itself with generated random numbers to hinder
  52  * backtracking of the original seed.
  53  *
  54  * Time for next re-seed in ms.
  55  */
  56 #define EXYNOS_RNG_RESEED_TIME          1000
  57 #define EXYNOS_RNG_RESEED_BYTES         65536
  58 
  59 /*
  60  * In polling mode, do not wait infinitely for the engine to finish the work.
  61  */
  62 #define EXYNOS_RNG_WAIT_RETRIES         100
  63 
  64 /* Context for crypto */
  65 struct exynos_rng_ctx {
  66         struct exynos_rng_dev           *rng;
  67 };
  68 
  69 /* Device associated memory */
  70 struct exynos_rng_dev {
  71         struct device                   *dev;
  72         enum exynos_prng_type           type;
  73         void __iomem                    *mem;
  74         struct clk                      *clk;
  75         struct mutex                    lock;
  76         /* Generated numbers stored for seeding during resume */
  77         u8                              seed_save[EXYNOS_RNG_SEED_SIZE];
  78         unsigned int                    seed_save_len;
  79         /* Time of last seeding in jiffies */
  80         unsigned long                   last_seeding;
  81         /* Bytes generated since last seeding */
  82         unsigned long                   bytes_seeding;
  83 };
  84 
  85 static struct exynos_rng_dev *exynos_rng_dev;
  86 
  87 static u32 exynos_rng_readl(struct exynos_rng_dev *rng, u32 offset)
  88 {
  89         return readl_relaxed(rng->mem + offset);
  90 }
  91 
  92 static void exynos_rng_writel(struct exynos_rng_dev *rng, u32 val, u32 offset)
  93 {
  94         writel_relaxed(val, rng->mem + offset);
  95 }
  96 
  97 static int exynos_rng_set_seed(struct exynos_rng_dev *rng,
  98                                const u8 *seed, unsigned int slen)
  99 {
 100         u32 val;
 101         int i;
 102 
 103         /* Round seed length because loop iterates over full register size */
 104         slen = ALIGN_DOWN(slen, 4);
 105 
 106         if (slen < EXYNOS_RNG_SEED_SIZE)
 107                 return -EINVAL;
 108 
 109         for (i = 0; i < slen ; i += 4) {
 110                 unsigned int seed_reg = (i / 4) % EXYNOS_RNG_SEED_REGS;
 111 
 112                 val = seed[i] << 24;
 113                 val |= seed[i + 1] << 16;
 114                 val |= seed[i + 2] << 8;
 115                 val |= seed[i + 3] << 0;
 116 
 117                 exynos_rng_writel(rng, val, EXYNOS_RNG_SEED(seed_reg));
 118         }
 119 
 120         val = exynos_rng_readl(rng, EXYNOS_RNG_STATUS);
 121         if (!(val & EXYNOS_RNG_STATUS_SEED_SETTING_DONE)) {
 122                 dev_warn(rng->dev, "Seed setting not finished\n");
 123                 return -EIO;
 124         }
 125 
 126         rng->last_seeding = jiffies;
 127         rng->bytes_seeding = 0;
 128 
 129         return 0;
 130 }
 131 
 132 /*
 133  * Start the engine and poll for finish.  Then read from output registers
 134  * filling the 'dst' buffer up to 'dlen' bytes or up to size of generated
 135  * random data (EXYNOS_RNG_SEED_SIZE).
 136  *
 137  * On success: return 0 and store number of read bytes under 'read' address.
 138  * On error: return -ERRNO.
 139  */
 140 static int exynos_rng_get_random(struct exynos_rng_dev *rng,
 141                                  u8 *dst, unsigned int dlen,
 142                                  unsigned int *read)
 143 {
 144         int retry = EXYNOS_RNG_WAIT_RETRIES;
 145 
 146         if (rng->type == EXYNOS_PRNG_EXYNOS4) {
 147                 exynos_rng_writel(rng, EXYNOS_RNG_CONTROL_START,
 148                                   EXYNOS_RNG_CONTROL);
 149         } else if (rng->type == EXYNOS_PRNG_EXYNOS5) {
 150                 exynos_rng_writel(rng, EXYNOS_RNG_GEN_PRNG,
 151                                   EXYNOS_RNG_SEED_CONF);
 152         }
 153 
 154         while (!(exynos_rng_readl(rng,
 155                         EXYNOS_RNG_STATUS) & EXYNOS_RNG_STATUS_RNG_DONE) && --retry)
 156                 cpu_relax();
 157 
 158         if (!retry)
 159                 return -ETIMEDOUT;
 160 
 161         /* Clear status bit */
 162         exynos_rng_writel(rng, EXYNOS_RNG_STATUS_RNG_DONE,
 163                           EXYNOS_RNG_STATUS);
 164         *read = min_t(size_t, dlen, EXYNOS_RNG_SEED_SIZE);
 165         memcpy_fromio(dst, rng->mem + EXYNOS_RNG_OUT_BASE, *read);
 166         rng->bytes_seeding += *read;
 167 
 168         return 0;
 169 }
 170 
 171 /* Re-seed itself from time to time */
 172 static void exynos_rng_reseed(struct exynos_rng_dev *rng)
 173 {
 174         unsigned long next_seeding = rng->last_seeding + \
 175                                      msecs_to_jiffies(EXYNOS_RNG_RESEED_TIME);
 176         unsigned long now = jiffies;
 177         unsigned int read = 0;
 178         u8 seed[EXYNOS_RNG_SEED_SIZE];
 179 
 180         if (time_before(now, next_seeding) &&
 181             rng->bytes_seeding < EXYNOS_RNG_RESEED_BYTES)
 182                 return;
 183 
 184         if (exynos_rng_get_random(rng, seed, sizeof(seed), &read))
 185                 return;
 186 
 187         exynos_rng_set_seed(rng, seed, read);
 188 
 189         /* Let others do some of their job. */
 190         mutex_unlock(&rng->lock);
 191         mutex_lock(&rng->lock);
 192 }
 193 
 194 static int exynos_rng_generate(struct crypto_rng *tfm,
 195                                const u8 *src, unsigned int slen,
 196                                u8 *dst, unsigned int dlen)
 197 {
 198         struct exynos_rng_ctx *ctx = crypto_rng_ctx(tfm);
 199         struct exynos_rng_dev *rng = ctx->rng;
 200         unsigned int read = 0;
 201         int ret;
 202 
 203         ret = clk_prepare_enable(rng->clk);
 204         if (ret)
 205                 return ret;
 206 
 207         mutex_lock(&rng->lock);
 208         do {
 209                 ret = exynos_rng_get_random(rng, dst, dlen, &read);
 210                 if (ret)
 211                         break;
 212 
 213                 dlen -= read;
 214                 dst += read;
 215 
 216                 exynos_rng_reseed(rng);
 217         } while (dlen > 0);
 218         mutex_unlock(&rng->lock);
 219 
 220         clk_disable_unprepare(rng->clk);
 221 
 222         return ret;
 223 }
 224 
 225 static int exynos_rng_seed(struct crypto_rng *tfm, const u8 *seed,
 226                            unsigned int slen)
 227 {
 228         struct exynos_rng_ctx *ctx = crypto_rng_ctx(tfm);
 229         struct exynos_rng_dev *rng = ctx->rng;
 230         int ret;
 231 
 232         ret = clk_prepare_enable(rng->clk);
 233         if (ret)
 234                 return ret;
 235 
 236         mutex_lock(&rng->lock);
 237         ret = exynos_rng_set_seed(ctx->rng, seed, slen);
 238         mutex_unlock(&rng->lock);
 239 
 240         clk_disable_unprepare(rng->clk);
 241 
 242         return ret;
 243 }
 244 
 245 static int exynos_rng_kcapi_init(struct crypto_tfm *tfm)
 246 {
 247         struct exynos_rng_ctx *ctx = crypto_tfm_ctx(tfm);
 248 
 249         ctx->rng = exynos_rng_dev;
 250 
 251         return 0;
 252 }
 253 
 254 static struct rng_alg exynos_rng_alg = {
 255         .generate               = exynos_rng_generate,
 256         .seed                   = exynos_rng_seed,
 257         .seedsize               = EXYNOS_RNG_SEED_SIZE,
 258         .base                   = {
 259                 .cra_name               = "stdrng",
 260                 .cra_driver_name        = "exynos_rng",
 261                 .cra_priority           = 300,
 262                 .cra_ctxsize            = sizeof(struct exynos_rng_ctx),
 263                 .cra_module             = THIS_MODULE,
 264                 .cra_init               = exynos_rng_kcapi_init,
 265         }
 266 };
 267 
 268 static int exynos_rng_probe(struct platform_device *pdev)
 269 {
 270         struct exynos_rng_dev *rng;
 271         int ret;
 272 
 273         if (exynos_rng_dev)
 274                 return -EEXIST;
 275 
 276         rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL);
 277         if (!rng)
 278                 return -ENOMEM;
 279 
 280         rng->type = (enum exynos_prng_type)of_device_get_match_data(&pdev->dev);
 281 
 282         mutex_init(&rng->lock);
 283 
 284         rng->dev = &pdev->dev;
 285         rng->clk = devm_clk_get(&pdev->dev, "secss");
 286         if (IS_ERR(rng->clk)) {
 287                 dev_err(&pdev->dev, "Couldn't get clock.\n");
 288                 return PTR_ERR(rng->clk);
 289         }
 290 
 291         rng->mem = devm_platform_ioremap_resource(pdev, 0);
 292         if (IS_ERR(rng->mem))
 293                 return PTR_ERR(rng->mem);
 294 
 295         platform_set_drvdata(pdev, rng);
 296 
 297         exynos_rng_dev = rng;
 298 
 299         ret = crypto_register_rng(&exynos_rng_alg);
 300         if (ret) {
 301                 dev_err(&pdev->dev,
 302                         "Couldn't register rng crypto alg: %d\n", ret);
 303                 exynos_rng_dev = NULL;
 304         }
 305 
 306         return ret;
 307 }
 308 
 309 static int exynos_rng_remove(struct platform_device *pdev)
 310 {
 311         crypto_unregister_rng(&exynos_rng_alg);
 312 
 313         exynos_rng_dev = NULL;
 314 
 315         return 0;
 316 }
 317 
 318 static int __maybe_unused exynos_rng_suspend(struct device *dev)
 319 {
 320         struct exynos_rng_dev *rng = dev_get_drvdata(dev);
 321         int ret;
 322 
 323         /* If we were never seeded then after resume it will be the same */
 324         if (!rng->last_seeding)
 325                 return 0;
 326 
 327         rng->seed_save_len = 0;
 328         ret = clk_prepare_enable(rng->clk);
 329         if (ret)
 330                 return ret;
 331 
 332         mutex_lock(&rng->lock);
 333 
 334         /* Get new random numbers and store them for seeding on resume. */
 335         exynos_rng_get_random(rng, rng->seed_save, sizeof(rng->seed_save),
 336                               &(rng->seed_save_len));
 337 
 338         mutex_unlock(&rng->lock);
 339 
 340         dev_dbg(rng->dev, "Stored %u bytes for seeding on system resume\n",
 341                 rng->seed_save_len);
 342 
 343         clk_disable_unprepare(rng->clk);
 344 
 345         return 0;
 346 }
 347 
 348 static int __maybe_unused exynos_rng_resume(struct device *dev)
 349 {
 350         struct exynos_rng_dev *rng = dev_get_drvdata(dev);
 351         int ret;
 352 
 353         /* Never seeded so nothing to do */
 354         if (!rng->last_seeding)
 355                 return 0;
 356 
 357         ret = clk_prepare_enable(rng->clk);
 358         if (ret)
 359                 return ret;
 360 
 361         mutex_lock(&rng->lock);
 362 
 363         ret = exynos_rng_set_seed(rng, rng->seed_save, rng->seed_save_len);
 364 
 365         mutex_unlock(&rng->lock);
 366 
 367         clk_disable_unprepare(rng->clk);
 368 
 369         return ret;
 370 }
 371 
 372 static SIMPLE_DEV_PM_OPS(exynos_rng_pm_ops, exynos_rng_suspend,
 373                          exynos_rng_resume);
 374 
 375 static const struct of_device_id exynos_rng_dt_match[] = {
 376         {
 377                 .compatible = "samsung,exynos4-rng",
 378                 .data = (const void *)EXYNOS_PRNG_EXYNOS4,
 379         }, {
 380                 .compatible = "samsung,exynos5250-prng",
 381                 .data = (const void *)EXYNOS_PRNG_EXYNOS5,
 382         },
 383         { },
 384 };
 385 MODULE_DEVICE_TABLE(of, exynos_rng_dt_match);
 386 
 387 static struct platform_driver exynos_rng_driver = {
 388         .driver         = {
 389                 .name   = "exynos-rng",
 390                 .pm     = &exynos_rng_pm_ops,
 391                 .of_match_table = exynos_rng_dt_match,
 392         },
 393         .probe          = exynos_rng_probe,
 394         .remove         = exynos_rng_remove,
 395 };
 396 
 397 module_platform_driver(exynos_rng_driver);
 398 
 399 MODULE_DESCRIPTION("Exynos H/W Random Number Generator driver");
 400 MODULE_AUTHOR("Krzysztof Kozlowski <krzk@kernel.org>");
 401 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */