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