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(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 */
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(ubuf, count, offp, obuf, oboff);
151	kfree(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(&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(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 */
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(&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
320void ccp5_debugfs_destroy(void)
321{
322	debugfs_remove_recursive(ccp_debugfs_dir);
323}
324