1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2019 HiSilicon Limited. */ 3 /* Copyright (c) 2025 Loongson Technology Corporation Limited. */ 4 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/mfd/loongson-se.h> 13 #include <linux/module.h> 14 #include <linux/mutex.h> 15 #include <linux/platform_device.h> 16 #include <linux/random.h> 17 #include <crypto/internal/rng.h> 18 19 #define SE_SEED_SIZE 32 20 21 struct loongson_rng_list { 22 struct mutex lock; 23 struct list_head list; 24 int registered; 25 }; 26 27 struct loongson_rng { 28 u32 used; 29 struct loongson_se_engine *engine; 30 struct list_head list; 31 struct mutex lock; 32 }; 33 34 struct loongson_rng_ctx { 35 struct loongson_rng *rng; 36 }; 37 38 struct loongson_rng_cmd { 39 u32 cmd_id; 40 union { 41 u32 len; 42 u32 ret; 43 } u; 44 u32 seed_off; 45 u32 out_off; 46 u32 pad[4]; 47 }; 48 49 static struct loongson_rng_list rng_devices = { 50 .lock = __MUTEX_INITIALIZER(rng_devices.lock), 51 .list = LIST_HEAD_INIT(rng_devices.list), 52 }; 53 54 static int loongson_rng_generate(struct crypto_rng *tfm, const u8 *src, 55 unsigned int slen, u8 *dstn, unsigned int dlen) 56 { 57 struct loongson_rng_ctx *ctx = crypto_rng_ctx(tfm); 58 struct loongson_rng *rng = ctx->rng; 59 struct loongson_rng_cmd *cmd = rng->engine->command; 60 int err, len; 61 62 mutex_lock(&rng->lock); 63 cmd->seed_off = 0; 64 do { 65 len = min(dlen, rng->engine->buffer_size); 66 cmd = rng->engine->command; 67 cmd->u.len = len; 68 err = loongson_se_send_engine_cmd(rng->engine); 69 if (err) 70 break; 71 72 cmd = rng->engine->command_ret; 73 if (cmd->u.ret) { 74 err = -EIO; 75 break; 76 } 77 78 memcpy(dstn, rng->engine->data_buffer, len); 79 dlen -= len; 80 dstn += len; 81 } while (dlen > 0); 82 mutex_unlock(&rng->lock); 83 84 return err; 85 } 86 87 static int loongson_rng_init(struct crypto_tfm *tfm) 88 { 89 struct loongson_rng_ctx *ctx = crypto_tfm_ctx(tfm); 90 struct loongson_rng *rng; 91 u32 min_used = U32_MAX; 92 93 mutex_lock(&rng_devices.lock); 94 list_for_each_entry(rng, &rng_devices.list, list) { 95 if (rng->used < min_used) { 96 ctx->rng = rng; 97 min_used = rng->used; 98 } 99 } 100 ctx->rng->used++; 101 mutex_unlock(&rng_devices.lock); 102 103 return 0; 104 } 105 106 static void loongson_rng_exit(struct crypto_tfm *tfm) 107 { 108 struct loongson_rng_ctx *ctx = crypto_tfm_ctx(tfm); 109 110 mutex_lock(&rng_devices.lock); 111 ctx->rng->used--; 112 mutex_unlock(&rng_devices.lock); 113 } 114 115 static int loongson_rng_seed(struct crypto_rng *tfm, const u8 *seed, 116 unsigned int slen) 117 { 118 struct loongson_rng_ctx *ctx = crypto_rng_ctx(tfm); 119 struct loongson_rng *rng = ctx->rng; 120 struct loongson_rng_cmd *cmd; 121 int err; 122 123 if (slen < SE_SEED_SIZE) 124 return -EINVAL; 125 126 slen = min(slen, rng->engine->buffer_size); 127 128 mutex_lock(&rng->lock); 129 cmd = rng->engine->command; 130 cmd->u.len = slen; 131 cmd->seed_off = rng->engine->buffer_off; 132 memcpy(rng->engine->data_buffer, seed, slen); 133 err = loongson_se_send_engine_cmd(rng->engine); 134 if (err) 135 goto out; 136 137 cmd = rng->engine->command_ret; 138 if (cmd->u.ret) 139 err = -EIO; 140 out: 141 mutex_unlock(&rng->lock); 142 143 return err; 144 } 145 146 static struct rng_alg loongson_rng_alg = { 147 .generate = loongson_rng_generate, 148 .seed = loongson_rng_seed, 149 .seedsize = SE_SEED_SIZE, 150 .base = { 151 .cra_name = "stdrng", 152 .cra_driver_name = "loongson_stdrng", 153 .cra_priority = 300, 154 .cra_ctxsize = sizeof(struct loongson_rng_ctx), 155 .cra_module = THIS_MODULE, 156 .cra_init = loongson_rng_init, 157 .cra_exit = loongson_rng_exit, 158 }, 159 }; 160 161 static int loongson_rng_probe(struct platform_device *pdev) 162 { 163 struct loongson_rng_cmd *cmd; 164 struct loongson_rng *rng; 165 int ret = 0; 166 167 rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL); 168 if (!rng) 169 return -ENOMEM; 170 171 rng->engine = loongson_se_init_engine(pdev->dev.parent, SE_ENGINE_RNG); 172 if (!rng->engine) 173 return -ENODEV; 174 cmd = rng->engine->command; 175 cmd->cmd_id = SE_CMD_RNG; 176 cmd->out_off = rng->engine->buffer_off; 177 mutex_init(&rng->lock); 178 179 mutex_lock(&rng_devices.lock); 180 181 if (!rng_devices.registered) { 182 ret = crypto_register_rng(&loongson_rng_alg); 183 if (ret) { 184 dev_err(&pdev->dev, "failed to register crypto(%d)\n", ret); 185 goto out; 186 } 187 rng_devices.registered = 1; 188 } 189 190 list_add_tail(&rng->list, &rng_devices.list); 191 out: 192 mutex_unlock(&rng_devices.lock); 193 194 return ret; 195 } 196 197 static struct platform_driver loongson_rng_driver = { 198 .probe = loongson_rng_probe, 199 .driver = { 200 .name = "loongson-rng", 201 }, 202 }; 203 module_platform_driver(loongson_rng_driver); 204 205 MODULE_ALIAS("platform:loongson-rng"); 206 MODULE_LICENSE("GPL"); 207 MODULE_AUTHOR("Yinggang Gu <guyinggang@loongson.cn>"); 208 MODULE_AUTHOR("Qunqin Zhao <zhaoqunqin@loongson.cn>"); 209 MODULE_DESCRIPTION("Loongson Random Number Generator driver"); 210