diff options
Diffstat (limited to 'drivers/s390/crypto/zcrypt_api.c')
| -rw-r--r-- | drivers/s390/crypto/zcrypt_api.c | 109 | 
1 files changed, 106 insertions, 3 deletions
diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c index 31cfaa556072..4b824b15194f 100644 --- a/drivers/s390/crypto/zcrypt_api.c +++ b/drivers/s390/crypto/zcrypt_api.c @@ -44,6 +44,8 @@  #include "zcrypt_debug.h"  #include "zcrypt_api.h" +#include "zcrypt_msgtype6.h" +  /*   * Module description.   */ @@ -554,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); @@ -581,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 *)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; @@ -784,6 +870,23 @@ static long zcrypt_unlocked_ioctl(struct file *filp, unsigned int cmd,  			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);  |