Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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