Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/crypto/loongson/loongson-rng.c
29268 views
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 <[email protected]>");
208
MODULE_AUTHOR("Qunqin Zhao <[email protected]>");
209
MODULE_DESCRIPTION("Loongson Random Number Generator driver");
210
211