File Index
Files and directories
Sign in
FILE

loongson-rng.c

File preview with syntax highlighting.

linux-6.19.14/drivers/crypto/loongson/loongson-rng.c C
  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
 21struct loongson_rng_list {
 22	struct mutex lock;
 23	struct list_head list;
 24	int registered;
 25};
 26
 27struct loongson_rng {
 28	u32 used;
 29	struct loongson_se_engine *engine;
 30	struct list_head list;
 31	struct mutex lock;
 32};
 33
 34struct loongson_rng_ctx {
 35	struct loongson_rng *rng;
 36};
 37
 38struct 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
 49static struct loongson_rng_list rng_devices = {
 50	.lock = __MUTEX_INITIALIZER(rng_devices.lock),
 51	.list = LIST_HEAD_INIT(rng_devices.list),
 52};
 53
 54static 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
 87static 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
106static 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
115static 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;
140out:
141	mutex_unlock(&rng->lock);
142
143	return err;
144}
145
146static 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
161static 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);
191out:
192	mutex_unlock(&rng_devices.lock);
193
194	return ret;
195}
196
197static struct platform_driver loongson_rng_driver = {
198	.probe		= loongson_rng_probe,
199	.driver		= {
200		.name	= "loongson-rng",
201	},
202};
203module_platform_driver(loongson_rng_driver);
204
205MODULE_ALIAS("platform:loongson-rng");
206MODULE_LICENSE("GPL");
207MODULE_AUTHOR("Yinggang Gu <guyinggang@loongson.cn>");
208MODULE_AUTHOR("Qunqin Zhao <zhaoqunqin@loongson.cn>");
209MODULE_DESCRIPTION("Loongson Random Number Generator driver");
~/ Read-only file preview.