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