1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * s390 TRNG device driver |
4 | * |
5 | * Driver for the TRNG (true random number generation) command |
6 | * available via CPACF extension MSA 7 on the s390 arch. |
7 | |
8 | * Copyright IBM Corp. 2017 |
9 | * Author(s): Harald Freudenberger <freude@de.ibm.com> |
10 | */ |
11 | |
12 | #define KMSG_COMPONENT "trng" |
13 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt |
14 | |
15 | #include <linux/hw_random.h> |
16 | #include <linux/kernel.h> |
17 | #include <linux/module.h> |
18 | #include <linux/cpufeature.h> |
19 | #include <linux/miscdevice.h> |
20 | #include <linux/debugfs.h> |
21 | #include <linux/atomic.h> |
22 | #include <linux/random.h> |
23 | #include <linux/sched/signal.h> |
24 | #include <asm/debug.h> |
25 | #include <asm/cpacf.h> |
26 | #include <asm/archrandom.h> |
27 | |
28 | MODULE_LICENSE("GPL v2" ); |
29 | MODULE_AUTHOR("IBM Corporation" ); |
30 | MODULE_DESCRIPTION("s390 CPACF TRNG device driver" ); |
31 | |
32 | |
33 | /* trng related debug feature things */ |
34 | |
35 | static debug_info_t *debug_info; |
36 | |
37 | #define DEBUG_DBG(...) debug_sprintf_event(debug_info, 6, ##__VA_ARGS__) |
38 | #define DEBUG_INFO(...) debug_sprintf_event(debug_info, 5, ##__VA_ARGS__) |
39 | #define DEBUG_WARN(...) debug_sprintf_event(debug_info, 4, ##__VA_ARGS__) |
40 | #define DEBUG_ERR(...) debug_sprintf_event(debug_info, 3, ##__VA_ARGS__) |
41 | |
42 | |
43 | /* trng helpers */ |
44 | |
45 | static atomic64_t trng_dev_counter = ATOMIC64_INIT(0); |
46 | static atomic64_t trng_hwrng_counter = ATOMIC64_INIT(0); |
47 | |
48 | |
49 | /* file io functions */ |
50 | |
51 | static int trng_open(struct inode *inode, struct file *file) |
52 | { |
53 | return nonseekable_open(inode, filp: file); |
54 | } |
55 | |
56 | static ssize_t trng_read(struct file *file, char __user *ubuf, |
57 | size_t nbytes, loff_t *ppos) |
58 | { |
59 | u8 buf[32]; |
60 | u8 *p = buf; |
61 | unsigned int n; |
62 | ssize_t ret = 0; |
63 | |
64 | /* |
65 | * use buf for requests <= sizeof(buf), |
66 | * otherwise allocate one page and fetch |
67 | * pagewise. |
68 | */ |
69 | |
70 | if (nbytes > sizeof(buf)) { |
71 | p = (u8 *) __get_free_page(GFP_KERNEL); |
72 | if (!p) |
73 | return -ENOMEM; |
74 | } |
75 | |
76 | while (nbytes) { |
77 | if (need_resched()) { |
78 | if (signal_pending(current)) { |
79 | if (ret == 0) |
80 | ret = -ERESTARTSYS; |
81 | break; |
82 | } |
83 | schedule(); |
84 | } |
85 | n = nbytes > PAGE_SIZE ? PAGE_SIZE : nbytes; |
86 | cpacf_trng(NULL, 0, p, n); |
87 | atomic64_add(i: n, v: &trng_dev_counter); |
88 | if (copy_to_user(to: ubuf, from: p, n)) { |
89 | ret = -EFAULT; |
90 | break; |
91 | } |
92 | nbytes -= n; |
93 | ubuf += n; |
94 | ret += n; |
95 | } |
96 | |
97 | if (p != buf) |
98 | free_page((unsigned long) p); |
99 | |
100 | DEBUG_DBG("trng_read()=%zd\n" , ret); |
101 | return ret; |
102 | } |
103 | |
104 | |
105 | /* sysfs */ |
106 | |
107 | static ssize_t trng_counter_show(struct device *dev, |
108 | struct device_attribute *attr, char *buf) |
109 | { |
110 | u64 dev_counter = atomic64_read(v: &trng_dev_counter); |
111 | u64 hwrng_counter = atomic64_read(v: &trng_hwrng_counter); |
112 | u64 arch_counter = atomic64_read(v: &s390_arch_random_counter); |
113 | |
114 | return sysfs_emit(buf, |
115 | fmt: "trng: %llu\n" |
116 | "hwrng: %llu\n" |
117 | "arch: %llu\n" |
118 | "total: %llu\n" , |
119 | dev_counter, hwrng_counter, arch_counter, |
120 | dev_counter + hwrng_counter + arch_counter); |
121 | } |
122 | static DEVICE_ATTR(byte_counter, 0444, trng_counter_show, NULL); |
123 | |
124 | static struct attribute *trng_dev_attrs[] = { |
125 | &dev_attr_byte_counter.attr, |
126 | NULL |
127 | }; |
128 | |
129 | static const struct attribute_group trng_dev_attr_group = { |
130 | .attrs = trng_dev_attrs |
131 | }; |
132 | |
133 | static const struct attribute_group *trng_dev_attr_groups[] = { |
134 | &trng_dev_attr_group, |
135 | NULL |
136 | }; |
137 | |
138 | static const struct file_operations trng_fops = { |
139 | .owner = THIS_MODULE, |
140 | .open = &trng_open, |
141 | .release = NULL, |
142 | .read = &trng_read, |
143 | .llseek = noop_llseek, |
144 | }; |
145 | |
146 | static struct miscdevice trng_dev = { |
147 | .name = "trng" , |
148 | .minor = MISC_DYNAMIC_MINOR, |
149 | .mode = 0444, |
150 | .fops = &trng_fops, |
151 | .groups = trng_dev_attr_groups, |
152 | }; |
153 | |
154 | |
155 | /* hwrng_register */ |
156 | |
157 | static inline void _trng_hwrng_read(u8 *buf, size_t len) |
158 | { |
159 | cpacf_trng(NULL, 0, buf, len); |
160 | atomic64_add(i: len, v: &trng_hwrng_counter); |
161 | } |
162 | |
163 | static int trng_hwrng_data_read(struct hwrng *rng, u32 *data) |
164 | { |
165 | size_t len = sizeof(*data); |
166 | |
167 | _trng_hwrng_read(buf: (u8 *) data, len); |
168 | |
169 | DEBUG_DBG("trng_hwrng_data_read()=%zu\n" , len); |
170 | |
171 | return len; |
172 | } |
173 | |
174 | static int trng_hwrng_read(struct hwrng *rng, void *data, size_t max, bool wait) |
175 | { |
176 | size_t len = max <= PAGE_SIZE ? max : PAGE_SIZE; |
177 | |
178 | _trng_hwrng_read(buf: (u8 *) data, len); |
179 | |
180 | DEBUG_DBG("trng_hwrng_read()=%zu\n" , len); |
181 | |
182 | return len; |
183 | } |
184 | |
185 | /* |
186 | * hwrng register struct |
187 | * The trng is supposed to have 100% entropy, and thus we register with a very |
188 | * high quality value. If we ever have a better driver in the future, we should |
189 | * change this value again when we merge this driver. |
190 | */ |
191 | static struct hwrng trng_hwrng_dev = { |
192 | .name = "s390-trng" , |
193 | .data_read = trng_hwrng_data_read, |
194 | .read = trng_hwrng_read, |
195 | }; |
196 | |
197 | |
198 | /* init and exit */ |
199 | |
200 | static void __init trng_debug_init(void) |
201 | { |
202 | debug_info = debug_register("trng" , 1, 1, 4 * sizeof(long)); |
203 | debug_register_view(debug_info, &debug_sprintf_view); |
204 | debug_set_level(debug_info, 3); |
205 | } |
206 | |
207 | static void trng_debug_exit(void) |
208 | { |
209 | debug_unregister(debug_info); |
210 | } |
211 | |
212 | static int __init trng_init(void) |
213 | { |
214 | int ret; |
215 | |
216 | trng_debug_init(); |
217 | |
218 | /* check if subfunction CPACF_PRNO_TRNG is available */ |
219 | if (!cpacf_query_func(CPACF_PRNO, CPACF_PRNO_TRNG)) { |
220 | DEBUG_INFO("trng_init CPACF_PRNO_TRNG not available\n" ); |
221 | ret = -ENODEV; |
222 | goto out_dbg; |
223 | } |
224 | |
225 | ret = misc_register(misc: &trng_dev); |
226 | if (ret) { |
227 | DEBUG_WARN("trng_init misc_register() failed rc=%d\n" , ret); |
228 | goto out_dbg; |
229 | } |
230 | |
231 | ret = hwrng_register(rng: &trng_hwrng_dev); |
232 | if (ret) { |
233 | DEBUG_WARN("trng_init hwrng_register() failed rc=%d\n" , ret); |
234 | goto out_misc; |
235 | } |
236 | |
237 | DEBUG_DBG("trng_init successful\n" ); |
238 | |
239 | return 0; |
240 | |
241 | out_misc: |
242 | misc_deregister(misc: &trng_dev); |
243 | out_dbg: |
244 | trng_debug_exit(); |
245 | return ret; |
246 | } |
247 | |
248 | static void __exit trng_exit(void) |
249 | { |
250 | hwrng_unregister(rng: &trng_hwrng_dev); |
251 | misc_deregister(misc: &trng_dev); |
252 | trng_debug_exit(); |
253 | } |
254 | |
255 | module_cpu_feature_match(S390_CPU_FEATURE_MSA, trng_init); |
256 | module_exit(trng_exit); |
257 | |