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
42static 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 */
97static 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 */
158static 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 */
174static 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 */
191static 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 */
245static 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
256static 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
263static 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
270static 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
277static struct dentry *ccp_debugfs_dir;
278static DEFINE_MUTEX(ccp_debugfs_lock);
279
280#define MAX_NAME_LEN 20
281
282void 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
320void ccp5_debugfs_destroy(void)
321{
322 debugfs_remove_recursive(dentry: ccp_debugfs_dir);
323}
324

source code of linux/drivers/crypto/ccp/ccp-debugfs.c