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 45 static 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 */ 100 static 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 */ 161 static 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 */ 177 static 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 */ 194 static 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 */ 248 static 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 259 static 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 266 static 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 273 static 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 280 static struct dentry *ccp_debugfs_dir; 281 static DEFINE_RWLOCK(ccp_debugfs_lock); 282 283 #define MAX_NAME_LEN 20 284 285 void 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_info; 290 struct dentry *debugfs_stats; 291 struct dentry *debugfs_q_instance; 292 struct dentry *debugfs_q_stats; 293 unsigned long flags; 294 int i; 295 296 if (!debugfs_initialized()) 297 return; 298 299 write_lock_irqsave(&ccp_debugfs_lock, flags); 300 if (!ccp_debugfs_dir) 301 ccp_debugfs_dir = debugfs_create_dir(KBUILD_MODNAME, NULL); 302 write_unlock_irqrestore(&ccp_debugfs_lock, flags); 303 if (!ccp_debugfs_dir) 304 return; 305 306 ccp->debugfs_instance = debugfs_create_dir(ccp->name, ccp_debugfs_dir); 307 if (!ccp->debugfs_instance) 308 goto err; 309 310 debugfs_info = debugfs_create_file("info", 0400, 311 ccp->debugfs_instance, ccp, 312 &ccp_debugfs_info_ops); 313 if (!debugfs_info) 314 goto err; 315 316 debugfs_stats = debugfs_create_file("stats", 0600, 317 ccp->debugfs_instance, ccp, 318 &ccp_debugfs_stats_ops); 319 if (!debugfs_stats) 320 goto err; 321 322 for (i = 0; i < ccp->cmd_q_count; i++) { 323 cmd_q = &ccp->cmd_q[i]; 324 325 snprintf(name, MAX_NAME_LEN - 1, "q%d", cmd_q->id); 326 327 debugfs_q_instance = 328 debugfs_create_dir(name, ccp->debugfs_instance); 329 if (!debugfs_q_instance) 330 goto err; 331 332 debugfs_q_stats = 333 debugfs_create_file("stats", 0600, 334 debugfs_q_instance, cmd_q, 335 &ccp_debugfs_queue_ops); 336 if (!debugfs_q_stats) 337 goto err; 338 } 339 340 return; 341 342 err: 343 debugfs_remove_recursive(ccp->debugfs_instance); 344 } 345 346 void ccp5_debugfs_destroy(void) 347 { 348 debugfs_remove_recursive(ccp_debugfs_dir); 349 } 350