xref: /linux/drivers/crypto/loongson/loongson-rng.c (revision 55a42f78ffd386e01a5404419f8c5ded7db70a21)
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