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