2019-06-04 10:11:33 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2017-05-02 17:33:40 -05:00
/*
* AMD Cryptographic Coprocessor ( CCP ) driver
*
* Copyright ( C ) 2017 Advanced Micro Devices , Inc .
*
* Author : Gary R Hook < gary . hook @ amd . com >
*/
# include <linux/debugfs.h>
# include <linux/ccp.h>
# include "ccp-dev.h"
/* DebugFS helpers */
# define OBUFP (obuf + oboff)
# define OBUFLEN 512
# define OBUFSPC (OBUFLEN - oboff)
# define OSCNPRINTF(fmt, ...) \
scnprintf ( OBUFP , OBUFSPC , fmt , # # __VA_ARGS__ )
# define BUFLEN 63
# define RI_VERSION_NUM 0x0000003F
# define RI_AES_PRESENT 0x00000040
# define RI_3DES_PRESENT 0x00000080
# define RI_SHA_PRESENT 0x00000100
# define RI_RSA_PRESENT 0x00000200
# define RI_ECC_PRESENT 0x00000400
# define RI_ZDE_PRESENT 0x00000800
# define RI_ZCE_PRESENT 0x00001000
# define RI_TRNG_PRESENT 0x00002000
# define RI_ELFC_PRESENT 0x00004000
# define RI_ELFC_SHIFT 14
# define RI_NUM_VQM 0x00078000
# define RI_NVQM_SHIFT 15
# define RI_NVQM(r) (((r) * RI_NUM_VQM) >> RI_NVQM_SHIFT)
# define RI_LSB_ENTRIES 0x0FF80000
# define RI_NLSB_SHIFT 19
# define RI_NLSB(r) (((r) * RI_LSB_ENTRIES) >> RI_NLSB_SHIFT)
static ssize_t ccp5_debugfs_info_read ( struct file * filp , char __user * ubuf ,
size_t count , loff_t * offp )
{
struct ccp_device * ccp = filp - > private_data ;
unsigned int oboff = 0 ;
unsigned int regval ;
ssize_t ret ;
char * obuf ;
if ( ! ccp )
return 0 ;
obuf = kmalloc ( OBUFLEN , GFP_KERNEL ) ;
if ( ! obuf )
return - ENOMEM ;
oboff + = OSCNPRINTF ( " Device name: %s \n " , ccp - > name ) ;
oboff + = OSCNPRINTF ( " RNG name: %s \n " , ccp - > rngname ) ;
oboff + = OSCNPRINTF ( " # Queues: %d \n " , ccp - > cmd_q_count ) ;
oboff + = OSCNPRINTF ( " # Cmds: %d \n " , ccp - > cmd_count ) ;
regval = ioread32 ( ccp - > io_regs + CMD5_PSP_CCP_VERSION ) ;
oboff + = OSCNPRINTF ( " Version: %d \n " , regval & RI_VERSION_NUM ) ;
oboff + = OSCNPRINTF ( " Engines: " ) ;
if ( regval & RI_AES_PRESENT )
oboff + = OSCNPRINTF ( " AES " ) ;
if ( regval & RI_3DES_PRESENT )
oboff + = OSCNPRINTF ( " 3DES " ) ;
if ( regval & RI_SHA_PRESENT )
oboff + = OSCNPRINTF ( " SHA " ) ;
if ( regval & RI_RSA_PRESENT )
oboff + = OSCNPRINTF ( " RSA " ) ;
if ( regval & RI_ECC_PRESENT )
oboff + = OSCNPRINTF ( " ECC " ) ;
if ( regval & RI_ZDE_PRESENT )
oboff + = OSCNPRINTF ( " ZDE " ) ;
if ( regval & RI_ZCE_PRESENT )
oboff + = OSCNPRINTF ( " ZCE " ) ;
if ( regval & RI_TRNG_PRESENT )
oboff + = OSCNPRINTF ( " TRNG " ) ;
oboff + = OSCNPRINTF ( " \n " ) ;
oboff + = OSCNPRINTF ( " Queues: %d \n " ,
( regval & RI_NUM_VQM ) > > RI_NVQM_SHIFT ) ;
oboff + = OSCNPRINTF ( " LSB Entries: %d \n " ,
( regval & RI_LSB_ENTRIES ) > > RI_NLSB_SHIFT ) ;
ret = simple_read_from_buffer ( ubuf , count , offp , obuf , oboff ) ;
kfree ( obuf ) ;
return ret ;
}
/* Return a formatted buffer containing the current
* statistics across all queues for a CCP .
*/
static ssize_t ccp5_debugfs_stats_read ( struct file * filp , char __user * ubuf ,
size_t count , loff_t * offp )
{
struct ccp_device * ccp = filp - > private_data ;
unsigned long total_xts_aes_ops = 0 ;
unsigned long total_3des_ops = 0 ;
unsigned long total_aes_ops = 0 ;
unsigned long total_sha_ops = 0 ;
unsigned long total_rsa_ops = 0 ;
unsigned long total_ecc_ops = 0 ;
unsigned long total_pt_ops = 0 ;
unsigned long total_ops = 0 ;
unsigned int oboff = 0 ;
ssize_t ret = 0 ;
unsigned int i ;
char * obuf ;
for ( i = 0 ; i < ccp - > cmd_q_count ; i + + ) {
struct ccp_cmd_queue * cmd_q = & ccp - > cmd_q [ i ] ;
total_ops + = cmd_q - > total_ops ;
total_aes_ops + = cmd_q - > total_aes_ops ;
total_xts_aes_ops + = cmd_q - > total_xts_aes_ops ;
total_3des_ops + = cmd_q - > total_3des_ops ;
total_sha_ops + = cmd_q - > total_sha_ops ;
total_rsa_ops + = cmd_q - > total_rsa_ops ;
total_pt_ops + = cmd_q - > total_pt_ops ;
total_ecc_ops + = cmd_q - > total_ecc_ops ;
}
obuf = kmalloc ( OBUFLEN , GFP_KERNEL ) ;
if ( ! obuf )
return - ENOMEM ;
oboff + = OSCNPRINTF ( " Total Interrupts Handled: %ld \n " ,
ccp - > total_interrupts ) ;
oboff + = OSCNPRINTF ( " Total Operations: %ld \n " ,
total_ops ) ;
oboff + = OSCNPRINTF ( " AES: %ld \n " ,
total_aes_ops ) ;
oboff + = OSCNPRINTF ( " XTS AES: %ld \n " ,
total_xts_aes_ops ) ;
oboff + = OSCNPRINTF ( " SHA: %ld \n " ,
total_3des_ops ) ;
oboff + = OSCNPRINTF ( " SHA: %ld \n " ,
total_sha_ops ) ;
oboff + = OSCNPRINTF ( " RSA: %ld \n " ,
total_rsa_ops ) ;
oboff + = OSCNPRINTF ( " Pass-Thru: %ld \n " ,
total_pt_ops ) ;
oboff + = OSCNPRINTF ( " ECC: %ld \n " ,
total_ecc_ops ) ;
ret = simple_read_from_buffer ( ubuf , count , offp , obuf , oboff ) ;
kfree ( obuf ) ;
return ret ;
}
/* Reset the counters in a queue
*/
static void ccp5_debugfs_reset_queue_stats ( struct ccp_cmd_queue * cmd_q )
{
cmd_q - > total_ops = 0L ;
cmd_q - > total_aes_ops = 0L ;
cmd_q - > total_xts_aes_ops = 0L ;
cmd_q - > total_3des_ops = 0L ;
cmd_q - > total_sha_ops = 0L ;
cmd_q - > total_rsa_ops = 0L ;
cmd_q - > total_pt_ops = 0L ;
cmd_q - > total_ecc_ops = 0L ;
}
/* A value was written to the stats variable, which
* should be used to reset the queue counters across
* that device .
*/
static ssize_t ccp5_debugfs_stats_write ( struct file * filp ,
const char __user * ubuf ,
size_t count , loff_t * offp )
{
struct ccp_device * ccp = filp - > private_data ;
int i ;
for ( i = 0 ; i < ccp - > cmd_q_count ; i + + )
ccp5_debugfs_reset_queue_stats ( & ccp - > cmd_q [ i ] ) ;
ccp - > total_interrupts = 0L ;
return count ;
}
/* Return a formatted buffer containing the current information
* for that queue
*/
static ssize_t ccp5_debugfs_queue_read ( struct file * filp , char __user * ubuf ,
size_t count , loff_t * offp )
{
struct ccp_cmd_queue * cmd_q = filp - > private_data ;
unsigned int oboff = 0 ;
unsigned int regval ;
ssize_t ret ;
char * obuf ;
if ( ! cmd_q )
return 0 ;
obuf = kmalloc ( OBUFLEN , GFP_KERNEL ) ;
if ( ! obuf )
return - ENOMEM ;
oboff + = OSCNPRINTF ( " Total Queue Operations: %ld \n " ,
cmd_q - > total_ops ) ;
oboff + = OSCNPRINTF ( " AES: %ld \n " ,
cmd_q - > total_aes_ops ) ;
oboff + = OSCNPRINTF ( " XTS AES: %ld \n " ,
cmd_q - > total_xts_aes_ops ) ;
oboff + = OSCNPRINTF ( " SHA: %ld \n " ,
cmd_q - > total_3des_ops ) ;
oboff + = OSCNPRINTF ( " SHA: %ld \n " ,
cmd_q - > total_sha_ops ) ;
oboff + = OSCNPRINTF ( " RSA: %ld \n " ,
cmd_q - > total_rsa_ops ) ;
oboff + = OSCNPRINTF ( " Pass-Thru: %ld \n " ,
cmd_q - > total_pt_ops ) ;
oboff + = OSCNPRINTF ( " ECC: %ld \n " ,
cmd_q - > total_ecc_ops ) ;
regval = ioread32 ( cmd_q - > reg_int_enable ) ;
oboff + = OSCNPRINTF ( " Enabled Interrupts: " ) ;
if ( regval & INT_EMPTY_QUEUE )
oboff + = OSCNPRINTF ( " EMPTY " ) ;
if ( regval & INT_QUEUE_STOPPED )
oboff + = OSCNPRINTF ( " STOPPED " ) ;
if ( regval & INT_ERROR )
oboff + = OSCNPRINTF ( " ERROR " ) ;
if ( regval & INT_COMPLETION )
oboff + = OSCNPRINTF ( " COMPLETION " ) ;
oboff + = OSCNPRINTF ( " \n " ) ;
ret = simple_read_from_buffer ( ubuf , count , offp , obuf , oboff ) ;
kfree ( obuf ) ;
return ret ;
}
/* A value was written to the stats variable for a
* queue . Reset the queue counters to this value .
*/
static ssize_t ccp5_debugfs_queue_write ( struct file * filp ,
const char __user * ubuf ,
size_t count , loff_t * offp )
{
struct ccp_cmd_queue * cmd_q = filp - > private_data ;
ccp5_debugfs_reset_queue_stats ( cmd_q ) ;
return count ;
}
static const struct file_operations ccp_debugfs_info_ops = {
. owner = THIS_MODULE ,
. open = simple_open ,
. read = ccp5_debugfs_info_read ,
. write = NULL ,
} ;
static const struct file_operations ccp_debugfs_queue_ops = {
. owner = THIS_MODULE ,
. open = simple_open ,
. read = ccp5_debugfs_queue_read ,
. write = ccp5_debugfs_queue_write ,
} ;
static const struct file_operations ccp_debugfs_stats_ops = {
. owner = THIS_MODULE ,
. open = simple_open ,
. read = ccp5_debugfs_stats_read ,
. write = ccp5_debugfs_stats_write ,
} ;
static struct dentry * ccp_debugfs_dir ;
2018-02-23 23:33:07 +01:00
static DEFINE_MUTEX ( ccp_debugfs_lock ) ;
2017-05-02 17:33:40 -05:00
# define MAX_NAME_LEN 20
void ccp5_debugfs_setup ( struct ccp_device * ccp )
{
struct ccp_cmd_queue * cmd_q ;
char name [ MAX_NAME_LEN + 1 ] ;
struct dentry * debugfs_q_instance ;
int i ;
if ( ! debugfs_initialized ( ) )
return ;
2018-02-23 23:33:07 +01:00
mutex_lock ( & ccp_debugfs_lock ) ;
2017-06-19 12:31:17 -05:00
if ( ! ccp_debugfs_dir )
2017-05-02 17:33:40 -05:00
ccp_debugfs_dir = debugfs_create_dir ( KBUILD_MODNAME , NULL ) ;
2018-02-23 23:33:07 +01:00
mutex_unlock ( & ccp_debugfs_lock ) ;
2017-05-02 17:33:40 -05:00
ccp - > debugfs_instance = debugfs_create_dir ( ccp - > name , ccp_debugfs_dir ) ;
2019-01-22 16:14:21 +01:00
debugfs_create_file ( " info " , 0400 , ccp - > debugfs_instance , ccp ,
& ccp_debugfs_info_ops ) ;
2017-05-02 17:33:40 -05:00
2019-01-22 16:14:21 +01:00
debugfs_create_file ( " stats " , 0600 , ccp - > debugfs_instance , ccp ,
& ccp_debugfs_stats_ops ) ;
2017-05-02 17:33:40 -05:00
for ( i = 0 ; i < ccp - > cmd_q_count ; i + + ) {
cmd_q = & ccp - > cmd_q [ i ] ;
snprintf ( name , MAX_NAME_LEN - 1 , " q%d " , cmd_q - > id ) ;
debugfs_q_instance =
debugfs_create_dir ( name , ccp - > debugfs_instance ) ;
2019-01-22 16:14:21 +01:00
debugfs_create_file ( " stats " , 0600 , debugfs_q_instance , cmd_q ,
& ccp_debugfs_queue_ops ) ;
2017-05-02 17:33:40 -05:00
}
2017-06-28 11:56:47 -05:00
return ;
2017-05-02 17:33:40 -05:00
}
void ccp5_debugfs_destroy ( void )
{
debugfs_remove_recursive ( ccp_debugfs_dir ) ;
}