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
ccp5_debugfs_info_read(struct file * filp,char __user * ubuf,size_t count,loff_t * offp)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(ubuf, count, offp, obuf, oboff);
89 kfree(obuf);
90
91 return ret;
92 }
93
94 /* Return a formatted buffer containing the current
95 * statistics across all queues for a CCP.
96 */
ccp5_debugfs_stats_read(struct file * filp,char __user * ubuf,size_t count,loff_t * offp)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(ubuf, count, offp, obuf, oboff);
151 kfree(obuf);
152
153 return ret;
154 }
155
156 /* Reset the counters in a queue
157 */
ccp5_debugfs_reset_queue_stats(struct ccp_cmd_queue * cmd_q)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 */
ccp5_debugfs_stats_write(struct file * filp,const char __user * ubuf,size_t count,loff_t * offp)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(&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 */
ccp5_debugfs_queue_read(struct file * filp,char __user * ubuf,size_t count,loff_t * offp)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(ubuf, count, offp, obuf, oboff);
237 kfree(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 */
ccp5_debugfs_queue_write(struct file * filp,const char __user * ubuf,size_t count,loff_t * offp)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
ccp5_debugfs_setup(struct ccp_device * ccp)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(&ccp_debugfs_lock);
296
297 ccp->debugfs_instance = debugfs_create_dir(ccp->name, ccp_debugfs_dir);
298
299 debugfs_create_file("info", 0400, ccp->debugfs_instance, ccp,
300 &ccp_debugfs_info_ops);
301
302 debugfs_create_file("stats", 0600, ccp->debugfs_instance, ccp,
303 &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(name, MAX_NAME_LEN - 1, "q%d", cmd_q->id);
309
310 debugfs_q_instance =
311 debugfs_create_dir(name, ccp->debugfs_instance);
312
313 debugfs_create_file("stats", 0600, debugfs_q_instance, cmd_q,
314 &ccp_debugfs_queue_ops);
315 }
316
317 return;
318 }
319
ccp5_debugfs_destroy(void)320 void ccp5_debugfs_destroy(void)
321 {
322 debugfs_remove_recursive(ccp_debugfs_dir);
323 }
324