diff options
Diffstat (limited to 'drivers/s390/crypto/zcrypt_api.c')
| -rw-r--r-- | drivers/s390/crypto/zcrypt_api.c | 315 | 
1 files changed, 293 insertions, 22 deletions
diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c index f5221749d18..0e18c5dcd91 100644 --- a/drivers/s390/crypto/zcrypt_api.c +++ b/drivers/s390/crypto/zcrypt_api.c @@ -1,9 +1,7 @@  /* - *  linux/drivers/s390/crypto/zcrypt_api.c - *   *  zcrypt 2.1.0   * - *  Copyright (C)  2001, 2006 IBM Corporation + *  Copyright IBM Corp. 2001, 2012   *  Author(s): Robert Burroughs   *	       Eric Rossman (edrossma@us.ibm.com)   *	       Cornelia Huck <cornelia.huck@de.ibm.com> @@ -11,6 +9,7 @@   *  Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)   *  Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>   *				  Ralph Wuerthner <rwuerthn@de.ibm.com> + *  MSGTYPE restruct:		  Holger Dengler <hd@linux.vnet.ibm.com>   *   * This program is free software; you can redistribute it and/or modify   * it under the terms of the GNU General Public License as published by @@ -35,30 +34,45 @@  #include <linux/proc_fs.h>  #include <linux/seq_file.h>  #include <linux/compat.h> -#include <linux/smp_lock.h>  #include <linux/slab.h> -#include <asm/atomic.h> +#include <linux/atomic.h>  #include <asm/uaccess.h>  #include <linux/hw_random.h> +#include <linux/debugfs.h> +#include <asm/debug.h> +#include "zcrypt_debug.h"  #include "zcrypt_api.h" +#include "zcrypt_msgtype6.h" +  /*   * Module description.   */  MODULE_AUTHOR("IBM Corporation"); -MODULE_DESCRIPTION("Cryptographic Coprocessor interface, " -		   "Copyright 2001, 2006 IBM Corporation"); +MODULE_DESCRIPTION("Cryptographic Coprocessor interface, " \ +		   "Copyright IBM Corp. 2001, 2012");  MODULE_LICENSE("GPL");  static DEFINE_SPINLOCK(zcrypt_device_lock);  static LIST_HEAD(zcrypt_device_list);  static int zcrypt_device_count = 0;  static atomic_t zcrypt_open_count = ATOMIC_INIT(0); +static atomic_t zcrypt_rescan_count = ATOMIC_INIT(0); + +atomic_t zcrypt_rescan_req = ATOMIC_INIT(0); +EXPORT_SYMBOL(zcrypt_rescan_req);  static int zcrypt_rng_device_add(void);  static void zcrypt_rng_device_remove(void); +static DEFINE_SPINLOCK(zcrypt_ops_list_lock); +static LIST_HEAD(zcrypt_ops_list); + +static debug_info_t *zcrypt_dbf_common; +static debug_info_t *zcrypt_dbf_devices; +static struct dentry *debugfs_root; +  /*   * Device attributes common for all crypto devices.   */ @@ -88,6 +102,8 @@ static ssize_t zcrypt_online_store(struct device *dev,  	if (sscanf(buf, "%d\n", &online) != 1 || online < 0 || online > 1)  		return -EINVAL;  	zdev->online = online; +	ZCRYPT_DBF_DEV(DBF_INFO, zdev, "dev%04xo%dman", zdev->ap_dev->qid, +		       zdev->online);  	if (!online)  		ap_flush_queue(zdev->ap_dev);  	return count; @@ -106,6 +122,24 @@ static struct attribute_group zcrypt_device_attr_group = {  };  /** + * Process a rescan of the transport layer. + * + * Returns 1, if the rescan has been processed, otherwise 0. + */ +static inline int zcrypt_process_rescan(void) +{ +	if (atomic_read(&zcrypt_rescan_req)) { +		atomic_set(&zcrypt_rescan_req, 0); +		atomic_inc(&zcrypt_rescan_count); +		ap_bus_force_rescan(); +		ZCRYPT_DBF_COMMON(DBF_INFO, "rescan%07d", +				  atomic_inc_return(&zcrypt_rescan_count)); +		return 1; +	} +	return 0; +} + +/**   * __zcrypt_increase_preference(): Increase preference of a crypto device.   * @zdev: Pointer the crypto device   * @@ -193,6 +227,7 @@ struct zcrypt_device *zcrypt_device_alloc(size_t max_response_size)  	zdev->reply.length = max_response_size;  	spin_lock_init(&zdev->lock);  	INIT_LIST_HEAD(&zdev->list); +	zdev->dbf_area = zcrypt_dbf_devices;  	return zdev;  out_free: @@ -218,6 +253,8 @@ int zcrypt_device_register(struct zcrypt_device *zdev)  {  	int rc; +	if (!zdev->ops) +		return -ENODEV;  	rc = sysfs_create_group(&zdev->ap_dev->device.kobj,  				&zcrypt_device_attr_group);  	if (rc) @@ -226,6 +263,8 @@ int zcrypt_device_register(struct zcrypt_device *zdev)  	kref_init(&zdev->refcount);  	spin_lock_bh(&zcrypt_device_lock);  	zdev->online = 1;	/* New devices are online by default. */ +	ZCRYPT_DBF_DEV(DBF_INFO, zdev, "dev%04xo%dreg", zdev->ap_dev->qid, +		       zdev->online);  	list_add_tail(&zdev->list, &zcrypt_device_list);  	__zcrypt_increase_preference(zdev);  	zcrypt_device_count++; @@ -272,6 +311,67 @@ void zcrypt_device_unregister(struct zcrypt_device *zdev)  }  EXPORT_SYMBOL(zcrypt_device_unregister); +void zcrypt_msgtype_register(struct zcrypt_ops *zops) +{ +	if (zops->owner) { +		spin_lock_bh(&zcrypt_ops_list_lock); +		list_add_tail(&zops->list, &zcrypt_ops_list); +		spin_unlock_bh(&zcrypt_ops_list_lock); +	} +} +EXPORT_SYMBOL(zcrypt_msgtype_register); + +void zcrypt_msgtype_unregister(struct zcrypt_ops *zops) +{ +	spin_lock_bh(&zcrypt_ops_list_lock); +	list_del_init(&zops->list); +	spin_unlock_bh(&zcrypt_ops_list_lock); +} +EXPORT_SYMBOL(zcrypt_msgtype_unregister); + +static inline +struct zcrypt_ops *__ops_lookup(unsigned char *name, int variant) +{ +	struct zcrypt_ops *zops; +	int found = 0; + +	spin_lock_bh(&zcrypt_ops_list_lock); +	list_for_each_entry(zops, &zcrypt_ops_list, list) { +		if ((zops->variant == variant) && +		    (!strncmp(zops->owner->name, name, MODULE_NAME_LEN))) { +			found = 1; +			break; +		} +	} +	spin_unlock_bh(&zcrypt_ops_list_lock); + +	if (!found) +		return NULL; +	return zops; +} + +struct zcrypt_ops *zcrypt_msgtype_request(unsigned char *name, int variant) +{ +	struct zcrypt_ops *zops = NULL; + +	zops = __ops_lookup(name, variant); +	if (!zops) { +		request_module("%s", name); +		zops = __ops_lookup(name, variant); +	} +	if ((!zops) || (!try_module_get(zops->owner))) +		return NULL; +	return zops; +} +EXPORT_SYMBOL(zcrypt_msgtype_request); + +void zcrypt_msgtype_release(struct zcrypt_ops *zops) +{ +	if (zops) +		module_put(zops->owner); +} +EXPORT_SYMBOL(zcrypt_msgtype_release); +  /**   * zcrypt_read (): Not supported beyond zcrypt 1.3.1.   * @@ -397,8 +497,15 @@ static long zcrypt_rsa_crt(struct ica_rsa_modexpo_crt *crt)  			if (copied == 0) {  				unsigned int len;  				spin_unlock_bh(&zcrypt_device_lock); -				/* len is max 256 / 2 - 120 = 8 */ -				len = crt->inputdatalength / 2 - 120; +				/* len is max 256 / 2 - 120 = 8 +				 * For bigger device just assume len of leading +				 * 0s is 8 as stated in the requirements for +				 * ica_rsa_modexpo_crt struct in zcrypt.h. +				 */ +				if (crt->inputdatalength <= 256) +					len = crt->inputdatalength / 2 - 120; +				else +					len = 8;  				if (len > sizeof(z1))  					return -EFAULT;  				z1 = z2 = z3 = 0; @@ -406,6 +513,7 @@ static long zcrypt_rsa_crt(struct ica_rsa_modexpo_crt *crt)  				    copy_from_user(&z2, crt->bp_key, len) ||  				    copy_from_user(&z3, crt->u_mult_inv, len))  					return -EFAULT; +				z1 = z2 = z3 = 0;  				copied = 1;  				/*  				 * We have to restart device lookup - @@ -448,9 +556,9 @@ static long zcrypt_send_cprb(struct ica_xcRB *xcRB)  	spin_lock_bh(&zcrypt_device_lock);  	list_for_each_entry(zdev, &zcrypt_device_list, list) {  		if (!zdev->online || !zdev->ops->send_cprb || -		    (xcRB->user_defined != AUTOSELECT && -			AP_QID_DEVICE(zdev->ap_dev->qid) != xcRB->user_defined) -		    ) +		   (zdev->ops->variant == MSGTYPE06_VARIANT_EP11) || +		   (xcRB->user_defined != AUTOSELECT && +		    AP_QID_DEVICE(zdev->ap_dev->qid) != xcRB->user_defined))  			continue;  		zcrypt_device_get(zdev);  		get_device(&zdev->ap_dev->device); @@ -475,6 +583,90 @@ static long zcrypt_send_cprb(struct ica_xcRB *xcRB)  	return -ENODEV;  } +struct ep11_target_dev_list { +	unsigned short		targets_num; +	struct ep11_target_dev	*targets; +}; + +static bool is_desired_ep11dev(unsigned int dev_qid, +			       struct ep11_target_dev_list dev_list) +{ +	int n; + +	for (n = 0; n < dev_list.targets_num; n++, dev_list.targets++) { +		if ((AP_QID_DEVICE(dev_qid) == dev_list.targets->ap_id) && +		    (AP_QID_QUEUE(dev_qid) == dev_list.targets->dom_id)) { +			return true; +		} +	} +	return false; +} + +static long zcrypt_send_ep11_cprb(struct ep11_urb *xcrb) +{ +	struct zcrypt_device *zdev; +	bool autoselect = false; +	int rc; +	struct ep11_target_dev_list ep11_dev_list = { +		.targets_num	=  0x00, +		.targets	=  NULL, +	}; + +	ep11_dev_list.targets_num = (unsigned short) xcrb->targets_num; + +	/* empty list indicates autoselect (all available targets) */ +	if (ep11_dev_list.targets_num == 0) +		autoselect = true; +	else { +		ep11_dev_list.targets = kcalloc((unsigned short) +						xcrb->targets_num, +						sizeof(struct ep11_target_dev), +						GFP_KERNEL); +		if (!ep11_dev_list.targets) +			return -ENOMEM; + +		if (copy_from_user(ep11_dev_list.targets, +				   (struct ep11_target_dev __force __user *) +				   xcrb->targets, xcrb->targets_num * +				   sizeof(struct ep11_target_dev))) +			return -EFAULT; +	} + +	spin_lock_bh(&zcrypt_device_lock); +	list_for_each_entry(zdev, &zcrypt_device_list, list) { +		/* check if device is eligible */ +		if (!zdev->online || +		    zdev->ops->variant != MSGTYPE06_VARIANT_EP11) +			continue; + +		/* check if device is selected as valid target */ +		if (!is_desired_ep11dev(zdev->ap_dev->qid, ep11_dev_list) && +		    !autoselect) +			continue; + +		zcrypt_device_get(zdev); +		get_device(&zdev->ap_dev->device); +		zdev->request_count++; +		__zcrypt_decrease_preference(zdev); +		if (try_module_get(zdev->ap_dev->drv->driver.owner)) { +			spin_unlock_bh(&zcrypt_device_lock); +			rc = zdev->ops->send_ep11_cprb(zdev, xcrb); +			spin_lock_bh(&zcrypt_device_lock); +			module_put(zdev->ap_dev->drv->driver.owner); +		} else { +			rc = -EAGAIN; +		  } +		zdev->request_count--; +		__zcrypt_increase_preference(zdev); +		put_device(&zdev->ap_dev->device); +		zcrypt_device_put(zdev); +		spin_unlock_bh(&zcrypt_device_lock); +		return rc; +	} +	spin_unlock_bh(&zcrypt_device_lock); +	return -ENODEV; +} +  static long zcrypt_rng(char *buffer)  {  	struct zcrypt_device *zdev; @@ -635,6 +827,11 @@ static long zcrypt_unlocked_ioctl(struct file *filp, unsigned int cmd,  		do {  			rc = zcrypt_rsa_modexpo(&mex);  		} while (rc == -EAGAIN); +		/* on failure: retry once again after a requested rescan */ +		if ((rc == -ENODEV) && (zcrypt_process_rescan())) +			do { +				rc = zcrypt_rsa_modexpo(&mex); +			} while (rc == -EAGAIN);  		if (rc)  			return rc;  		return put_user(mex.outputdatalength, &umex->outputdatalength); @@ -647,6 +844,11 @@ static long zcrypt_unlocked_ioctl(struct file *filp, unsigned int cmd,  		do {  			rc = zcrypt_rsa_crt(&crt);  		} while (rc == -EAGAIN); +		/* on failure: retry once again after a requested rescan */ +		if ((rc == -ENODEV) && (zcrypt_process_rescan())) +			do { +				rc = zcrypt_rsa_crt(&crt); +			} while (rc == -EAGAIN);  		if (rc)  			return rc;  		return put_user(crt.outputdatalength, &ucrt->outputdatalength); @@ -659,10 +861,32 @@ static long zcrypt_unlocked_ioctl(struct file *filp, unsigned int cmd,  		do {  			rc = zcrypt_send_cprb(&xcRB);  		} while (rc == -EAGAIN); +		/* on failure: retry once again after a requested rescan */ +		if ((rc == -ENODEV) && (zcrypt_process_rescan())) +			do { +				rc = zcrypt_send_cprb(&xcRB); +			} while (rc == -EAGAIN);  		if (copy_to_user(uxcRB, &xcRB, sizeof(xcRB)))  			return -EFAULT;  		return rc;  	} +	case ZSENDEP11CPRB: { +		struct ep11_urb __user *uxcrb = (void __user *)arg; +		struct ep11_urb xcrb; +		if (copy_from_user(&xcrb, uxcrb, sizeof(xcrb))) +			return -EFAULT; +		do { +			rc = zcrypt_send_ep11_cprb(&xcrb); +		} while (rc == -EAGAIN); +		/* on failure: retry once again after a requested rescan */ +		if ((rc == -ENODEV) && (zcrypt_process_rescan())) +			do { +				rc = zcrypt_send_ep11_cprb(&xcrb); +			} while (rc == -EAGAIN); +		if (copy_to_user(uxcrb, &xcrb, sizeof(xcrb))) +			return -EFAULT; +		return rc; +	}  	case Z90STAT_STATUS_MASK: {  		char status[AP_DEVICES];  		zcrypt_status_mask(status); @@ -765,10 +989,15 @@ static long trans_modexpo32(struct file *filp, unsigned int cmd,  	do {  		rc = zcrypt_rsa_modexpo(&mex64);  	} while (rc == -EAGAIN); -	if (!rc) -		rc = put_user(mex64.outputdatalength, -			      &umex32->outputdatalength); -	return rc; +	/* on failure: retry once again after a requested rescan */ +	if ((rc == -ENODEV) && (zcrypt_process_rescan())) +		do { +			rc = zcrypt_rsa_modexpo(&mex64); +		} while (rc == -EAGAIN); +	if (rc) +		return rc; +	return put_user(mex64.outputdatalength, +			&umex32->outputdatalength);  }  struct compat_ica_rsa_modexpo_crt { @@ -805,10 +1034,15 @@ static long trans_modexpo_crt32(struct file *filp, unsigned int cmd,  	do {  		rc = zcrypt_rsa_crt(&crt64);  	} while (rc == -EAGAIN); -	if (!rc) -		rc = put_user(crt64.outputdatalength, -			      &ucrt32->outputdatalength); -	return rc; +	/* on failure: retry once again after a requested rescan */ +	if ((rc == -ENODEV) && (zcrypt_process_rescan())) +		do { +			rc = zcrypt_rsa_crt(&crt64); +		} while (rc == -EAGAIN); +	if (rc) +		return rc; +	return put_user(crt64.outputdatalength, +			&ucrt32->outputdatalength);  }  struct compat_ica_xcRB { @@ -864,6 +1098,11 @@ static long trans_xcRB32(struct file *filp, unsigned int cmd,  	do {  		rc = zcrypt_send_cprb(&xcRB64);  	} while (rc == -EAGAIN); +	/* on failure: retry once again after a requested rescan */ +	if ((rc == -ENODEV) && (zcrypt_process_rescan())) +		do { +			rc = zcrypt_send_cprb(&xcRB64); +		} while (rc == -EAGAIN);  	xcRB32.reply_control_blk_length = xcRB64.reply_control_blk_length;  	xcRB32.reply_data_length = xcRB64.reply_data_length;  	xcRB32.status = xcRB64.status; @@ -1121,6 +1360,9 @@ static int zcrypt_rng_data_read(struct hwrng *rng, u32 *data)  	 */  	if (zcrypt_rng_buffer_index == 0) {  		rc = zcrypt_rng((char *) zcrypt_rng_buffer); +		/* on failure: retry once again after a requested rescan */ +		if ((rc == -ENODEV) && (zcrypt_process_rescan())) +			rc = zcrypt_rng((char *) zcrypt_rng_buffer);  		if (rc < 0)  			return -EIO;  		zcrypt_rng_buffer_index = rc / sizeof *data; @@ -1173,6 +1415,30 @@ static void zcrypt_rng_device_remove(void)  	mutex_unlock(&zcrypt_rng_mutex);  } +int __init zcrypt_debug_init(void) +{ +	debugfs_root = debugfs_create_dir("zcrypt", NULL); + +	zcrypt_dbf_common = debug_register("zcrypt_common", 1, 1, 16); +	debug_register_view(zcrypt_dbf_common, &debug_hex_ascii_view); +	debug_set_level(zcrypt_dbf_common, DBF_ERR); + +	zcrypt_dbf_devices = debug_register("zcrypt_devices", 1, 1, 16); +	debug_register_view(zcrypt_dbf_devices, &debug_hex_ascii_view); +	debug_set_level(zcrypt_dbf_devices, DBF_ERR); + +	return 0; +} + +void zcrypt_debug_exit(void) +{ +	debugfs_remove(debugfs_root); +	if (zcrypt_dbf_common) +		debug_unregister(zcrypt_dbf_common); +	if (zcrypt_dbf_devices) +		debug_unregister(zcrypt_dbf_devices); +} +  /**   * zcrypt_api_init(): Module initialization.   * @@ -1182,6 +1448,12 @@ int __init zcrypt_api_init(void)  {  	int rc; +	rc = zcrypt_debug_init(); +	if (rc) +		goto out; + +	atomic_set(&zcrypt_rescan_req, 0); +  	/* Register the request sprayer. */  	rc = misc_register(&zcrypt_misc_device);  	if (rc < 0) @@ -1211,9 +1483,8 @@ void zcrypt_api_exit(void)  {  	remove_proc_entry("driver/z90crypt", NULL);  	misc_deregister(&zcrypt_misc_device); +	zcrypt_debug_exit();  } -#ifndef CONFIG_ZCRYPT_MONOLITHIC  module_init(zcrypt_api_init);  module_exit(zcrypt_api_exit); -#endif  | 
