1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * AMD Cryptographic Coprocessor (CCP) driver |
4 | * |
5 | * Copyright (C) 2017 Advanced Micro Devices, Inc. |
6 | * |
7 | * Author: Gary R Hook <gary.hook@amd.com> |
8 | */ |
9 | |
10 | #include <linux/debugfs.h> |
11 | #include <linux/ccp.h> |
12 | |
13 | #include "ccp-dev.h" |
14 | |
15 | /* DebugFS helpers */ |
16 | #define OBUFP (obuf + oboff) |
17 | #define OBUFLEN 512 |
18 | #define OBUFSPC (OBUFLEN - oboff) |
19 | #define OSCNPRINTF(fmt, ...) \ |
20 | scnprintf(OBUFP, OBUFSPC, fmt, ## __VA_ARGS__) |
21 | |
22 | #define BUFLEN 63 |
23 | |
24 | #define RI_VERSION_NUM 0x0000003F |
25 | #define RI_AES_PRESENT 0x00000040 |
26 | #define RI_3DES_PRESENT 0x00000080 |
27 | #define RI_SHA_PRESENT 0x00000100 |
28 | #define RI_RSA_PRESENT 0x00000200 |
29 | #define RI_ECC_PRESENT 0x00000400 |
30 | #define RI_ZDE_PRESENT 0x00000800 |
31 | #define RI_ZCE_PRESENT 0x00001000 |
32 | #define RI_TRNG_PRESENT 0x00002000 |
33 | #define RI_ELFC_PRESENT 0x00004000 |
34 | #define RI_ELFC_SHIFT 14 |
35 | #define RI_NUM_VQM 0x00078000 |
36 | #define RI_NVQM_SHIFT 15 |
37 | #define RI_NVQM(r) (((r) * RI_NUM_VQM) >> RI_NVQM_SHIFT) |
38 | #define RI_LSB_ENTRIES 0x0FF80000 |
39 | #define RI_NLSB_SHIFT 19 |
40 | #define RI_NLSB(r) (((r) * RI_LSB_ENTRIES) >> RI_NLSB_SHIFT) |
41 | |
42 | static ssize_t ccp5_debugfs_info_read(struct file *filp, char __user *ubuf, |
43 | size_t count, loff_t *offp) |
44 | { |
45 | struct ccp_device *ccp = filp->private_data; |
46 | unsigned int oboff = 0; |
47 | unsigned int regval; |
48 | ssize_t ret; |
49 | char *obuf; |
50 | |
51 | if (!ccp) |
52 | return 0; |
53 | |
54 | obuf = kmalloc(OBUFLEN, GFP_KERNEL); |
55 | if (!obuf) |
56 | return -ENOMEM; |
57 | |
58 | oboff += OSCNPRINTF("Device name: %s\n" , ccp->name); |
59 | oboff += OSCNPRINTF(" RNG name: %s\n" , ccp->rngname); |
60 | oboff += OSCNPRINTF(" # Queues: %d\n" , ccp->cmd_q_count); |
61 | oboff += OSCNPRINTF(" # Cmds: %d\n" , ccp->cmd_count); |
62 | |
63 | regval = ioread32(ccp->io_regs + CMD5_PSP_CCP_VERSION); |
64 | oboff += OSCNPRINTF(" Version: %d\n" , regval & RI_VERSION_NUM); |
65 | oboff += OSCNPRINTF(" Engines:" ); |
66 | if (regval & RI_AES_PRESENT) |
67 | oboff += OSCNPRINTF(" AES" ); |
68 | if (regval & RI_3DES_PRESENT) |
69 | oboff += OSCNPRINTF(" 3DES" ); |
70 | if (regval & RI_SHA_PRESENT) |
71 | oboff += OSCNPRINTF(" SHA" ); |
72 | if (regval & RI_RSA_PRESENT) |
73 | oboff += OSCNPRINTF(" RSA" ); |
74 | if (regval & RI_ECC_PRESENT) |
75 | oboff += OSCNPRINTF(" ECC" ); |
76 | if (regval & RI_ZDE_PRESENT) |
77 | oboff += OSCNPRINTF(" ZDE" ); |
78 | if (regval & RI_ZCE_PRESENT) |
79 | oboff += OSCNPRINTF(" ZCE" ); |
80 | if (regval & RI_TRNG_PRESENT) |
81 | oboff += OSCNPRINTF(" TRNG" ); |
82 | oboff += OSCNPRINTF("\n" ); |
83 | oboff += OSCNPRINTF(" Queues: %d\n" , |
84 | (regval & RI_NUM_VQM) >> RI_NVQM_SHIFT); |
85 | oboff += OSCNPRINTF("LSB Entries: %d\n" , |
86 | (regval & RI_LSB_ENTRIES) >> RI_NLSB_SHIFT); |
87 | |
88 | ret = simple_read_from_buffer(to: ubuf, count, ppos: offp, from: obuf, available: oboff); |
89 | kfree(objp: obuf); |
90 | |
91 | return ret; |
92 | } |
93 | |
94 | /* Return a formatted buffer containing the current |
95 | * statistics across all queues for a CCP. |
96 | */ |
97 | static ssize_t ccp5_debugfs_stats_read(struct file *filp, char __user *ubuf, |
98 | size_t count, loff_t *offp) |
99 | { |
100 | struct ccp_device *ccp = filp->private_data; |
101 | unsigned long total_xts_aes_ops = 0; |
102 | unsigned long total_3des_ops = 0; |
103 | unsigned long total_aes_ops = 0; |
104 | unsigned long total_sha_ops = 0; |
105 | unsigned long total_rsa_ops = 0; |
106 | unsigned long total_ecc_ops = 0; |
107 | unsigned long total_pt_ops = 0; |
108 | unsigned long total_ops = 0; |
109 | unsigned int oboff = 0; |
110 | ssize_t ret = 0; |
111 | unsigned int i; |
112 | char *obuf; |
113 | |
114 | for (i = 0; i < ccp->cmd_q_count; i++) { |
115 | struct ccp_cmd_queue *cmd_q = &ccp->cmd_q[i]; |
116 | |
117 | total_ops += cmd_q->total_ops; |
118 | total_aes_ops += cmd_q->total_aes_ops; |
119 | total_xts_aes_ops += cmd_q->total_xts_aes_ops; |
120 | total_3des_ops += cmd_q->total_3des_ops; |
121 | total_sha_ops += cmd_q->total_sha_ops; |
122 | total_rsa_ops += cmd_q->total_rsa_ops; |
123 | total_pt_ops += cmd_q->total_pt_ops; |
124 | total_ecc_ops += cmd_q->total_ecc_ops; |
125 | } |
126 | |
127 | obuf = kmalloc(OBUFLEN, GFP_KERNEL); |
128 | if (!obuf) |
129 | return -ENOMEM; |
130 | |
131 | oboff += OSCNPRINTF("Total Interrupts Handled: %ld\n" , |
132 | ccp->total_interrupts); |
133 | oboff += OSCNPRINTF(" Total Operations: %ld\n" , |
134 | total_ops); |
135 | oboff += OSCNPRINTF(" AES: %ld\n" , |
136 | total_aes_ops); |
137 | oboff += OSCNPRINTF(" XTS AES: %ld\n" , |
138 | total_xts_aes_ops); |
139 | oboff += OSCNPRINTF(" SHA: %ld\n" , |
140 | total_3des_ops); |
141 | oboff += OSCNPRINTF(" SHA: %ld\n" , |
142 | total_sha_ops); |
143 | oboff += OSCNPRINTF(" RSA: %ld\n" , |
144 | total_rsa_ops); |
145 | oboff += OSCNPRINTF(" Pass-Thru: %ld\n" , |
146 | total_pt_ops); |
147 | oboff += OSCNPRINTF(" ECC: %ld\n" , |
148 | total_ecc_ops); |
149 | |
150 | ret = simple_read_from_buffer(to: ubuf, count, ppos: offp, from: obuf, available: oboff); |
151 | kfree(objp: obuf); |
152 | |
153 | return ret; |
154 | } |
155 | |
156 | /* Reset the counters in a queue |
157 | */ |
158 | static void ccp5_debugfs_reset_queue_stats(struct ccp_cmd_queue *cmd_q) |
159 | { |
160 | cmd_q->total_ops = 0L; |
161 | cmd_q->total_aes_ops = 0L; |
162 | cmd_q->total_xts_aes_ops = 0L; |
163 | cmd_q->total_3des_ops = 0L; |
164 | cmd_q->total_sha_ops = 0L; |
165 | cmd_q->total_rsa_ops = 0L; |
166 | cmd_q->total_pt_ops = 0L; |
167 | cmd_q->total_ecc_ops = 0L; |
168 | } |
169 | |
170 | /* A value was written to the stats variable, which |
171 | * should be used to reset the queue counters across |
172 | * that device. |
173 | */ |
174 | static ssize_t ccp5_debugfs_stats_write(struct file *filp, |
175 | const char __user *ubuf, |
176 | size_t count, loff_t *offp) |
177 | { |
178 | struct ccp_device *ccp = filp->private_data; |
179 | int i; |
180 | |
181 | for (i = 0; i < ccp->cmd_q_count; i++) |
182 | ccp5_debugfs_reset_queue_stats(cmd_q: &ccp->cmd_q[i]); |
183 | ccp->total_interrupts = 0L; |
184 | |
185 | return count; |
186 | } |
187 | |
188 | /* Return a formatted buffer containing the current information |
189 | * for that queue |
190 | */ |
191 | static ssize_t ccp5_debugfs_queue_read(struct file *filp, char __user *ubuf, |
192 | size_t count, loff_t *offp) |
193 | { |
194 | struct ccp_cmd_queue *cmd_q = filp->private_data; |
195 | unsigned int oboff = 0; |
196 | unsigned int regval; |
197 | ssize_t ret; |
198 | char *obuf; |
199 | |
200 | if (!cmd_q) |
201 | return 0; |
202 | |
203 | obuf = kmalloc(OBUFLEN, GFP_KERNEL); |
204 | if (!obuf) |
205 | return -ENOMEM; |
206 | |
207 | oboff += OSCNPRINTF(" Total Queue Operations: %ld\n" , |
208 | cmd_q->total_ops); |
209 | oboff += OSCNPRINTF(" AES: %ld\n" , |
210 | cmd_q->total_aes_ops); |
211 | oboff += OSCNPRINTF(" XTS AES: %ld\n" , |
212 | cmd_q->total_xts_aes_ops); |
213 | oboff += OSCNPRINTF(" SHA: %ld\n" , |
214 | cmd_q->total_3des_ops); |
215 | oboff += OSCNPRINTF(" SHA: %ld\n" , |
216 | cmd_q->total_sha_ops); |
217 | oboff += OSCNPRINTF(" RSA: %ld\n" , |
218 | cmd_q->total_rsa_ops); |
219 | oboff += OSCNPRINTF(" Pass-Thru: %ld\n" , |
220 | cmd_q->total_pt_ops); |
221 | oboff += OSCNPRINTF(" ECC: %ld\n" , |
222 | cmd_q->total_ecc_ops); |
223 | |
224 | regval = ioread32(cmd_q->reg_int_enable); |
225 | oboff += OSCNPRINTF(" Enabled Interrupts:" ); |
226 | if (regval & INT_EMPTY_QUEUE) |
227 | oboff += OSCNPRINTF(" EMPTY" ); |
228 | if (regval & INT_QUEUE_STOPPED) |
229 | oboff += OSCNPRINTF(" STOPPED" ); |
230 | if (regval & INT_ERROR) |
231 | oboff += OSCNPRINTF(" ERROR" ); |
232 | if (regval & INT_COMPLETION) |
233 | oboff += OSCNPRINTF(" COMPLETION" ); |
234 | oboff += OSCNPRINTF("\n" ); |
235 | |
236 | ret = simple_read_from_buffer(to: ubuf, count, ppos: offp, from: obuf, available: oboff); |
237 | kfree(objp: obuf); |
238 | |
239 | return ret; |
240 | } |
241 | |
242 | /* A value was written to the stats variable for a |
243 | * queue. Reset the queue counters to this value. |
244 | */ |
245 | static ssize_t ccp5_debugfs_queue_write(struct file *filp, |
246 | const char __user *ubuf, |
247 | size_t count, loff_t *offp) |
248 | { |
249 | struct ccp_cmd_queue *cmd_q = filp->private_data; |
250 | |
251 | ccp5_debugfs_reset_queue_stats(cmd_q); |
252 | |
253 | return count; |
254 | } |
255 | |
256 | static const struct file_operations ccp_debugfs_info_ops = { |
257 | .owner = THIS_MODULE, |
258 | .open = simple_open, |
259 | .read = ccp5_debugfs_info_read, |
260 | .write = NULL, |
261 | }; |
262 | |
263 | static const struct file_operations ccp_debugfs_queue_ops = { |
264 | .owner = THIS_MODULE, |
265 | .open = simple_open, |
266 | .read = ccp5_debugfs_queue_read, |
267 | .write = ccp5_debugfs_queue_write, |
268 | }; |
269 | |
270 | static const struct file_operations ccp_debugfs_stats_ops = { |
271 | .owner = THIS_MODULE, |
272 | .open = simple_open, |
273 | .read = ccp5_debugfs_stats_read, |
274 | .write = ccp5_debugfs_stats_write, |
275 | }; |
276 | |
277 | static struct dentry *ccp_debugfs_dir; |
278 | static DEFINE_MUTEX(ccp_debugfs_lock); |
279 | |
280 | #define MAX_NAME_LEN 20 |
281 | |
282 | void ccp5_debugfs_setup(struct ccp_device *ccp) |
283 | { |
284 | struct ccp_cmd_queue *cmd_q; |
285 | char name[MAX_NAME_LEN + 1]; |
286 | struct dentry *debugfs_q_instance; |
287 | int i; |
288 | |
289 | if (!debugfs_initialized()) |
290 | return; |
291 | |
292 | mutex_lock(&ccp_debugfs_lock); |
293 | if (!ccp_debugfs_dir) |
294 | ccp_debugfs_dir = debugfs_create_dir(KBUILD_MODNAME, NULL); |
295 | mutex_unlock(lock: &ccp_debugfs_lock); |
296 | |
297 | ccp->debugfs_instance = debugfs_create_dir(name: ccp->name, parent: ccp_debugfs_dir); |
298 | |
299 | debugfs_create_file(name: "info" , mode: 0400, parent: ccp->debugfs_instance, data: ccp, |
300 | fops: &ccp_debugfs_info_ops); |
301 | |
302 | debugfs_create_file(name: "stats" , mode: 0600, parent: ccp->debugfs_instance, data: ccp, |
303 | fops: &ccp_debugfs_stats_ops); |
304 | |
305 | for (i = 0; i < ccp->cmd_q_count; i++) { |
306 | cmd_q = &ccp->cmd_q[i]; |
307 | |
308 | snprintf(buf: name, MAX_NAME_LEN - 1, fmt: "q%d" , cmd_q->id); |
309 | |
310 | debugfs_q_instance = |
311 | debugfs_create_dir(name, parent: ccp->debugfs_instance); |
312 | |
313 | debugfs_create_file(name: "stats" , mode: 0600, parent: debugfs_q_instance, data: cmd_q, |
314 | fops: &ccp_debugfs_queue_ops); |
315 | } |
316 | |
317 | return; |
318 | } |
319 | |
320 | void ccp5_debugfs_destroy(void) |
321 | { |
322 | debugfs_remove_recursive(dentry: ccp_debugfs_dir); |
323 | } |
324 | |