diff options
Diffstat (limited to 'drivers/s390/crypto/zcrypt_api.c')
-rw-r--r-- | drivers/s390/crypto/zcrypt_api.c | 936 |
1 files changed, 662 insertions, 274 deletions
diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c index 56a405dce8bc..f94b43ce9a65 100644 --- a/drivers/s390/crypto/zcrypt_api.c +++ b/drivers/s390/crypto/zcrypt_api.c @@ -25,6 +25,7 @@ #include <linux/debugfs.h> #include <linux/cdev.h> #include <linux/ctype.h> +#include <linux/capability.h> #include <asm/debug.h> #define CREATE_TRACE_POINTS @@ -58,7 +59,6 @@ MODULE_PARM_DESC(hwrng_seed, "Turn on/off hwrng auto seed, default is 1 (on)."); DEFINE_SPINLOCK(zcrypt_list_lock); LIST_HEAD(zcrypt_card_list); -int zcrypt_device_count; static atomic_t zcrypt_open_count = ATOMIC_INIT(0); static atomic_t zcrypt_rescan_count = ATOMIC_INIT(0); @@ -71,7 +71,7 @@ static LIST_HEAD(zcrypt_ops_list); /* Zcrypt related debug feature stuff. */ debug_info_t *zcrypt_dbf_info; -/** +/* * Process a rescan of the transport layer. * * Returns 1, if the rescan has been processed, otherwise 0. @@ -82,8 +82,8 @@ static inline int zcrypt_process_rescan(void) atomic_set(&zcrypt_rescan_req, 0); atomic_inc(&zcrypt_rescan_count); ap_bus_force_rescan(); - ZCRYPT_DBF(DBF_INFO, "rescan count=%07d\n", - atomic_inc_return(&zcrypt_rescan_count)); + ZCRYPT_DBF_INFO("%s rescan count=%07d\n", __func__, + atomic_inc_return(&zcrypt_rescan_count)); return 1; } return 0; @@ -104,7 +104,7 @@ struct zcrypt_ops *zcrypt_msgtype(unsigned char *name, int variant) struct zcrypt_ops *zops; list_for_each_entry(zops, &zcrypt_ops_list, list) - if ((zops->variant == variant) && + if (zops->variant == variant && (!strncmp(zops->name, name, sizeof(zops->name)))) return zops; return NULL; @@ -285,10 +285,53 @@ static ssize_t aqmask_store(struct device *dev, static DEVICE_ATTR_RW(aqmask); +static ssize_t admask_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int i, rc; + struct zcdn_device *zcdndev = to_zcdn_dev(dev); + + if (mutex_lock_interruptible(&ap_perms_mutex)) + return -ERESTARTSYS; + + buf[0] = '0'; + buf[1] = 'x'; + for (i = 0; i < sizeof(zcdndev->perms.adm) / sizeof(long); i++) + snprintf(buf + 2 + 2 * i * sizeof(long), + PAGE_SIZE - 2 - 2 * i * sizeof(long), + "%016lx", zcdndev->perms.adm[i]); + buf[2 + 2 * i * sizeof(long)] = '\n'; + buf[2 + 2 * i * sizeof(long) + 1] = '\0'; + rc = 2 + 2 * i * sizeof(long) + 1; + + mutex_unlock(&ap_perms_mutex); + + return rc; +} + +static ssize_t admask_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int rc; + struct zcdn_device *zcdndev = to_zcdn_dev(dev); + + rc = ap_parse_mask_str(buf, zcdndev->perms.adm, + AP_DOMAINS, &ap_perms_mutex); + if (rc) + return rc; + + return count; +} + +static DEVICE_ATTR_RW(admask); + static struct attribute *zcdn_dev_attrs[] = { &dev_attr_ioctlmask.attr, &dev_attr_apmask.attr, &dev_attr_aqmask.attr, + &dev_attr_admask.attr, NULL }; @@ -341,8 +384,8 @@ static void zcdn_device_release(struct device *dev) { struct zcdn_device *zcdndev = to_zcdn_dev(dev); - ZCRYPT_DBF(DBF_INFO, "releasing zcdn device %d:%d\n", - MAJOR(dev->devt), MINOR(dev->devt)); + ZCRYPT_DBF_INFO("%s releasing zcdn device %d:%d\n", + __func__, MAJOR(dev->devt), MINOR(dev->devt)); kfree(zcdndev); } @@ -395,8 +438,8 @@ static int zcdn_create(const char *name) strncpy(nodename, name, sizeof(nodename)); else snprintf(nodename, sizeof(nodename), - ZCRYPT_NAME "_%d", (int) MINOR(devt)); - nodename[sizeof(nodename)-1] = '\0'; + ZCRYPT_NAME "_%d", (int)MINOR(devt)); + nodename[sizeof(nodename) - 1] = '\0'; if (dev_set_name(&zcdndev->device, nodename)) { rc = -EINVAL; goto unlockout; @@ -407,8 +450,8 @@ static int zcdn_create(const char *name) goto unlockout; } - ZCRYPT_DBF(DBF_INFO, "created zcdn device %d:%d\n", - MAJOR(devt), MINOR(devt)); + ZCRYPT_DBF_INFO("%s created zcdn device %d:%d\n", + __func__, MAJOR(devt), MINOR(devt)); unlockout: mutex_unlock(&ap_perms_mutex); @@ -462,7 +505,7 @@ static void zcdn_destroy_all(void) #endif -/** +/* * zcrypt_read (): Not supported beyond zcrypt 1.3.1. * * This function is not supported beyond zcrypt 1.3.1. @@ -473,10 +516,10 @@ static ssize_t zcrypt_read(struct file *filp, char __user *buf, return -EPERM; } -/** +/* * zcrypt_write(): Not allowed. * - * Write is is not allowed + * Write is not allowed */ static ssize_t zcrypt_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) @@ -484,7 +527,7 @@ static ssize_t zcrypt_write(struct file *filp, const char __user *buf, return -EPERM; } -/** +/* * zcrypt_open(): Count number of users. * * Device open function to count number of users. @@ -506,13 +549,13 @@ static int zcrypt_open(struct inode *inode, struct file *filp) perms = &zcdndev->perms; } #endif - filp->private_data = (void *) perms; + filp->private_data = (void *)perms; atomic_inc(&zcrypt_open_count); return stream_open(inode, filp); } -/** +/* * zcrypt_release(): Count number of users. * * Device close function to count number of users. @@ -550,9 +593,8 @@ static inline int zcrypt_check_ioctl(struct ap_perms *perms, } if (rc) - ZCRYPT_DBF(DBF_WARN, - "ioctl check failed: ioctlnr=0x%04x rc=%d\n", - ioctlnr, rc); + ZCRYPT_DBF_WARN("%s ioctl check failed: ioctlnr=0x%04x rc=%d\n", + __func__, ioctlnr, rc); return rc; } @@ -572,14 +614,14 @@ static inline struct zcrypt_queue *zcrypt_pick_queue(struct zcrypt_card *zc, struct module **pmod, unsigned int weight) { - if (!zq || !try_module_get(zq->queue->ap_dev.drv->driver.owner)) + if (!zq || !try_module_get(zq->queue->ap_dev.device.driver->owner)) return NULL; zcrypt_queue_get(zq); get_device(&zq->queue->ap_dev.device); atomic_add(weight, &zc->load); atomic_add(weight, &zq->load); zq->request_count++; - *pmod = zq->queue->ap_dev.drv->driver.owner; + *pmod = zq->queue->ap_dev.device.driver->owner; return zq; } @@ -602,13 +644,13 @@ static inline bool zcrypt_card_compare(struct zcrypt_card *zc, unsigned int pref_weight) { if (!pref_zc) - return false; + return true; weight += atomic_read(&zc->load); pref_weight += atomic_read(&pref_zc->load); if (weight == pref_weight) - return atomic64_read(&zc->card->total_request_count) > + return atomic64_read(&zc->card->total_request_count) < atomic64_read(&pref_zc->card->total_request_count); - return weight > pref_weight; + return weight < pref_weight; } static inline bool zcrypt_queue_compare(struct zcrypt_queue *zq, @@ -617,30 +659,39 @@ static inline bool zcrypt_queue_compare(struct zcrypt_queue *zq, unsigned int pref_weight) { if (!pref_zq) - return false; + return true; weight += atomic_read(&zq->load); pref_weight += atomic_read(&pref_zq->load); if (weight == pref_weight) - return zq->queue->total_request_count > + return zq->queue->total_request_count < pref_zq->queue->total_request_count; - return weight > pref_weight; + return weight < pref_weight; } /* * zcrypt ioctls. */ static long zcrypt_rsa_modexpo(struct ap_perms *perms, + struct zcrypt_track *tr, struct ica_rsa_modexpo *mex) { struct zcrypt_card *zc, *pref_zc; struct zcrypt_queue *zq, *pref_zq; - unsigned int weight, pref_weight; + struct ap_message ap_msg; + unsigned int wgt = 0, pref_wgt = 0; unsigned int func_code; - int qid = 0, rc = -ENODEV; + int cpen, qpen, qid = 0, rc = -ENODEV; struct module *mod; trace_s390_zcrypt_req(mex, TP_ICARSAMODEXPO); + ap_init_message(&ap_msg); + +#ifdef CONFIG_ZCRYPT_DEBUG + if (tr && tr->fi.cmd) + ap_msg.fi.cmd = tr->fi.cmd; +#endif + if (mex->outputdatalength < mex->inputdatalength) { func_code = 0; rc = -EINVAL; @@ -662,8 +713,9 @@ static long zcrypt_rsa_modexpo(struct ap_perms *perms, pref_zq = NULL; spin_lock(&zcrypt_list_lock); for_each_zcrypt_card(zc) { - /* Check for online accelarator and CCA cards */ - if (!zc->online || !(zc->card->functions & 0x18000000)) + /* Check for usable accelarator or CCA card */ + if (!zc->online || !zc->card->config || zc->card->chkstop || + !(zc->card->functions & 0x18000000)) continue; /* Check for size limits */ if (zc->min_mod_size > mex->inputdatalength || @@ -673,58 +725,83 @@ static long zcrypt_rsa_modexpo(struct ap_perms *perms, if (!zcrypt_check_card(perms, zc->card->id)) continue; /* get weight index of the card device */ - weight = zc->speed_rating[func_code]; - if (zcrypt_card_compare(zc, pref_zc, weight, pref_weight)) + wgt = zc->speed_rating[func_code]; + /* penalty if this msg was previously sent via this card */ + cpen = (tr && tr->again_counter && tr->last_qid && + AP_QID_CARD(tr->last_qid) == zc->card->id) ? + TRACK_AGAIN_CARD_WEIGHT_PENALTY : 0; + if (!zcrypt_card_compare(zc, pref_zc, wgt + cpen, pref_wgt)) continue; for_each_zcrypt_queue(zq, zc) { - /* check if device is online and eligible */ - if (!zq->online || !zq->ops->rsa_modexpo) + /* check if device is usable and eligible */ + if (!zq->online || !zq->ops->rsa_modexpo || + !zq->queue->config || zq->queue->chkstop) continue; /* check if device node has admission for this queue */ if (!zcrypt_check_queue(perms, AP_QID_QUEUE(zq->queue->qid))) continue; - if (zcrypt_queue_compare(zq, pref_zq, - weight, pref_weight)) + /* penalty if the msg was previously sent at this qid */ + qpen = (tr && tr->again_counter && tr->last_qid && + tr->last_qid == zq->queue->qid) ? + TRACK_AGAIN_QUEUE_WEIGHT_PENALTY : 0; + if (!zcrypt_queue_compare(zq, pref_zq, + wgt + cpen + qpen, pref_wgt)) continue; pref_zc = zc; pref_zq = zq; - pref_weight = weight; + pref_wgt = wgt + cpen + qpen; } } - pref_zq = zcrypt_pick_queue(pref_zc, pref_zq, &mod, weight); + pref_zq = zcrypt_pick_queue(pref_zc, pref_zq, &mod, wgt); spin_unlock(&zcrypt_list_lock); if (!pref_zq) { + ZCRYPT_DBF_DBG("%s no matching queue found => ENODEV\n", + __func__); rc = -ENODEV; goto out; } qid = pref_zq->queue->qid; - rc = pref_zq->ops->rsa_modexpo(pref_zq, mex); + rc = pref_zq->ops->rsa_modexpo(pref_zq, mex, &ap_msg); spin_lock(&zcrypt_list_lock); - zcrypt_drop_queue(pref_zc, pref_zq, mod, weight); + zcrypt_drop_queue(pref_zc, pref_zq, mod, wgt); spin_unlock(&zcrypt_list_lock); out: + ap_release_message(&ap_msg); + if (tr) { + tr->last_rc = rc; + tr->last_qid = qid; + } trace_s390_zcrypt_rep(mex, func_code, rc, AP_QID_CARD(qid), AP_QID_QUEUE(qid)); return rc; } static long zcrypt_rsa_crt(struct ap_perms *perms, + struct zcrypt_track *tr, struct ica_rsa_modexpo_crt *crt) { struct zcrypt_card *zc, *pref_zc; struct zcrypt_queue *zq, *pref_zq; - unsigned int weight, pref_weight; + struct ap_message ap_msg; + unsigned int wgt = 0, pref_wgt = 0; unsigned int func_code; - int qid = 0, rc = -ENODEV; + int cpen, qpen, qid = 0, rc = -ENODEV; struct module *mod; trace_s390_zcrypt_req(crt, TP_ICARSACRT); + ap_init_message(&ap_msg); + +#ifdef CONFIG_ZCRYPT_DEBUG + if (tr && tr->fi.cmd) + ap_msg.fi.cmd = tr->fi.cmd; +#endif + if (crt->outputdatalength < crt->inputdatalength) { func_code = 0; rc = -EINVAL; @@ -746,8 +823,9 @@ static long zcrypt_rsa_crt(struct ap_perms *perms, pref_zq = NULL; spin_lock(&zcrypt_list_lock); for_each_zcrypt_card(zc) { - /* Check for online accelarator and CCA cards */ - if (!zc->online || !(zc->card->functions & 0x18000000)) + /* Check for usable accelarator or CCA card */ + if (!zc->online || !zc->card->config || zc->card->chkstop || + !(zc->card->functions & 0x18000000)) continue; /* Check for size limits */ if (zc->min_mod_size > crt->inputdatalength || @@ -757,99 +835,145 @@ static long zcrypt_rsa_crt(struct ap_perms *perms, if (!zcrypt_check_card(perms, zc->card->id)) continue; /* get weight index of the card device */ - weight = zc->speed_rating[func_code]; - if (zcrypt_card_compare(zc, pref_zc, weight, pref_weight)) + wgt = zc->speed_rating[func_code]; + /* penalty if this msg was previously sent via this card */ + cpen = (tr && tr->again_counter && tr->last_qid && + AP_QID_CARD(tr->last_qid) == zc->card->id) ? + TRACK_AGAIN_CARD_WEIGHT_PENALTY : 0; + if (!zcrypt_card_compare(zc, pref_zc, wgt + cpen, pref_wgt)) continue; for_each_zcrypt_queue(zq, zc) { - /* check if device is online and eligible */ - if (!zq->online || !zq->ops->rsa_modexpo_crt) + /* check if device is usable and eligible */ + if (!zq->online || !zq->ops->rsa_modexpo_crt || + !zq->queue->config || zq->queue->chkstop) continue; /* check if device node has admission for this queue */ if (!zcrypt_check_queue(perms, AP_QID_QUEUE(zq->queue->qid))) continue; - if (zcrypt_queue_compare(zq, pref_zq, - weight, pref_weight)) + /* penalty if the msg was previously sent at this qid */ + qpen = (tr && tr->again_counter && tr->last_qid && + tr->last_qid == zq->queue->qid) ? + TRACK_AGAIN_QUEUE_WEIGHT_PENALTY : 0; + if (!zcrypt_queue_compare(zq, pref_zq, + wgt + cpen + qpen, pref_wgt)) continue; pref_zc = zc; pref_zq = zq; - pref_weight = weight; + pref_wgt = wgt + cpen + qpen; } } - pref_zq = zcrypt_pick_queue(pref_zc, pref_zq, &mod, weight); + pref_zq = zcrypt_pick_queue(pref_zc, pref_zq, &mod, wgt); spin_unlock(&zcrypt_list_lock); if (!pref_zq) { + ZCRYPT_DBF_DBG("%s no matching queue found => ENODEV\n", + __func__); rc = -ENODEV; goto out; } qid = pref_zq->queue->qid; - rc = pref_zq->ops->rsa_modexpo_crt(pref_zq, crt); + rc = pref_zq->ops->rsa_modexpo_crt(pref_zq, crt, &ap_msg); spin_lock(&zcrypt_list_lock); - zcrypt_drop_queue(pref_zc, pref_zq, mod, weight); + zcrypt_drop_queue(pref_zc, pref_zq, mod, wgt); spin_unlock(&zcrypt_list_lock); out: + ap_release_message(&ap_msg); + if (tr) { + tr->last_rc = rc; + tr->last_qid = qid; + } trace_s390_zcrypt_rep(crt, func_code, rc, AP_QID_CARD(qid), AP_QID_QUEUE(qid)); return rc; } -static long _zcrypt_send_cprb(struct ap_perms *perms, - struct ica_xcRB *xcRB) +static long _zcrypt_send_cprb(bool userspace, struct ap_perms *perms, + struct zcrypt_track *tr, + struct ica_xcRB *xcrb) { struct zcrypt_card *zc, *pref_zc; struct zcrypt_queue *zq, *pref_zq; struct ap_message ap_msg; - unsigned int weight, pref_weight; + unsigned int wgt = 0, pref_wgt = 0; unsigned int func_code; unsigned short *domain, tdom; - int qid = 0, rc = -ENODEV; + int cpen, qpen, qid = 0, rc = -ENODEV; struct module *mod; - trace_s390_zcrypt_req(xcRB, TB_ZSECSENDCPRB); + trace_s390_zcrypt_req(xcrb, TB_ZSECSENDCPRB); - xcRB->status = 0; + xcrb->status = 0; ap_init_message(&ap_msg); - rc = get_cprb_fc(xcRB, &ap_msg, &func_code, &domain); + +#ifdef CONFIG_ZCRYPT_DEBUG + if (tr && tr->fi.cmd) + ap_msg.fi.cmd = tr->fi.cmd; + if (tr && tr->fi.action == AP_FI_ACTION_CCA_AGENT_FF) { + ZCRYPT_DBF_WARN("%s fi cmd 0x%04x: forcing invalid agent_ID 'FF'\n", + __func__, tr->fi.cmd); + xcrb->agent_ID = 0x4646; + } +#endif + + rc = prep_cca_ap_msg(userspace, xcrb, &ap_msg, &func_code, &domain); if (rc) goto out; + tdom = *domain; + if (perms != &ap_perms && tdom < AP_DOMAINS) { + if (ap_msg.flags & AP_MSG_FLAG_ADMIN) { + if (!test_bit_inv(tdom, perms->adm)) { + rc = -ENODEV; + goto out; + } + } else if ((ap_msg.flags & AP_MSG_FLAG_USAGE) == 0) { + rc = -EOPNOTSUPP; + goto out; + } + } /* * If a valid target domain is set and this domain is NOT a usage - * domain but a control only domain, use the default domain as target. + * domain but a control only domain, autoselect target domain. */ - tdom = *domain; - if (tdom >= 0 && tdom < AP_DOMAINS && + if (tdom < AP_DOMAINS && !ap_test_config_usage_domain(tdom) && - ap_test_config_ctrl_domain(tdom) && - ap_domain_index >= 0) - tdom = ap_domain_index; + ap_test_config_ctrl_domain(tdom)) + tdom = AUTOSEL_DOM; pref_zc = NULL; pref_zq = NULL; spin_lock(&zcrypt_list_lock); for_each_zcrypt_card(zc) { - /* Check for online CCA cards */ - if (!zc->online || !(zc->card->functions & 0x10000000)) + /* Check for usable CCA card */ + if (!zc->online || !zc->card->config || zc->card->chkstop || + !(zc->card->functions & 0x10000000)) continue; /* Check for user selected CCA card */ - if (xcRB->user_defined != AUTOSELECT && - xcRB->user_defined != zc->card->id) + if (xcrb->user_defined != AUTOSELECT && + xcrb->user_defined != zc->card->id) + continue; + /* check if request size exceeds card max msg size */ + if (ap_msg.len > zc->card->maxmsgsize) continue; /* check if device node has admission for this card */ if (!zcrypt_check_card(perms, zc->card->id)) continue; /* get weight index of the card device */ - weight = speed_idx_cca(func_code) * zc->speed_rating[SECKEY]; - if (zcrypt_card_compare(zc, pref_zc, weight, pref_weight)) + wgt = speed_idx_cca(func_code) * zc->speed_rating[SECKEY]; + /* penalty if this msg was previously sent via this card */ + cpen = (tr && tr->again_counter && tr->last_qid && + AP_QID_CARD(tr->last_qid) == zc->card->id) ? + TRACK_AGAIN_CARD_WEIGHT_PENALTY : 0; + if (!zcrypt_card_compare(zc, pref_zc, wgt + cpen, pref_wgt)) continue; for_each_zcrypt_queue(zq, zc) { - /* check if device is online and eligible */ - if (!zq->online || - !zq->ops->send_cprb || + /* check for device usable and eligible */ + if (!zq->online || !zq->ops->send_cprb || + !zq->queue->config || zq->queue->chkstop || (tdom != AUTOSEL_DOM && tdom != AP_QID_QUEUE(zq->queue->qid))) continue; @@ -857,18 +981,24 @@ static long _zcrypt_send_cprb(struct ap_perms *perms, if (!zcrypt_check_queue(perms, AP_QID_QUEUE(zq->queue->qid))) continue; - if (zcrypt_queue_compare(zq, pref_zq, - weight, pref_weight)) + /* penalty if the msg was previously sent at this qid */ + qpen = (tr && tr->again_counter && tr->last_qid && + tr->last_qid == zq->queue->qid) ? + TRACK_AGAIN_QUEUE_WEIGHT_PENALTY : 0; + if (!zcrypt_queue_compare(zq, pref_zq, + wgt + cpen + qpen, pref_wgt)) continue; pref_zc = zc; pref_zq = zq; - pref_weight = weight; + pref_wgt = wgt + cpen + qpen; } } - pref_zq = zcrypt_pick_queue(pref_zc, pref_zq, &mod, weight); + pref_zq = zcrypt_pick_queue(pref_zc, pref_zq, &mod, wgt); spin_unlock(&zcrypt_list_lock); if (!pref_zq) { + ZCRYPT_DBF_DBG("%s no match for address %02x.%04x => ENODEV\n", + __func__, xcrb->user_defined, *domain); rc = -ENODEV; goto out; } @@ -878,22 +1008,34 @@ static long _zcrypt_send_cprb(struct ap_perms *perms, if (*domain == AUTOSEL_DOM) *domain = AP_QID_QUEUE(qid); - rc = pref_zq->ops->send_cprb(pref_zq, xcRB, &ap_msg); +#ifdef CONFIG_ZCRYPT_DEBUG + if (tr && tr->fi.action == AP_FI_ACTION_CCA_DOM_INVAL) { + ZCRYPT_DBF_WARN("%s fi cmd 0x%04x: forcing invalid domain\n", + __func__, tr->fi.cmd); + *domain = 99; + } +#endif + + rc = pref_zq->ops->send_cprb(userspace, pref_zq, xcrb, &ap_msg); spin_lock(&zcrypt_list_lock); - zcrypt_drop_queue(pref_zc, pref_zq, mod, weight); + zcrypt_drop_queue(pref_zc, pref_zq, mod, wgt); spin_unlock(&zcrypt_list_lock); out: ap_release_message(&ap_msg); - trace_s390_zcrypt_rep(xcRB, func_code, rc, + if (tr) { + tr->last_rc = rc; + tr->last_qid = qid; + } + trace_s390_zcrypt_rep(xcrb, func_code, rc, AP_QID_CARD(qid), AP_QID_QUEUE(qid)); return rc; } -long zcrypt_send_cprb(struct ica_xcRB *xcRB) +long zcrypt_send_cprb(struct ica_xcRB *xcrb) { - return _zcrypt_send_cprb(&ap_perms, xcRB); + return _zcrypt_send_cprb(false, &ap_perms, NULL, xcrb); } EXPORT_SYMBOL(zcrypt_send_cprb); @@ -924,24 +1066,30 @@ static bool is_desired_ep11_queue(unsigned int dev_qid, return false; } -static long _zcrypt_send_ep11_cprb(struct ap_perms *perms, +static long _zcrypt_send_ep11_cprb(bool userspace, struct ap_perms *perms, + struct zcrypt_track *tr, struct ep11_urb *xcrb) { struct zcrypt_card *zc, *pref_zc; struct zcrypt_queue *zq, *pref_zq; struct ep11_target_dev *targets; unsigned short target_num; - unsigned int weight, pref_weight; - unsigned int func_code; + unsigned int wgt = 0, pref_wgt = 0; + unsigned int func_code, domain; struct ap_message ap_msg; - int qid = 0, rc = -ENODEV; + int cpen, qpen, qid = 0, rc = -ENODEV; struct module *mod; trace_s390_zcrypt_req(xcrb, TP_ZSENDEP11CPRB); ap_init_message(&ap_msg); - target_num = (unsigned short) xcrb->targets_num; +#ifdef CONFIG_ZCRYPT_DEBUG + if (tr && tr->fi.cmd) + ap_msg.fi.cmd = tr->fi.cmd; +#endif + + target_num = (unsigned short)xcrb->targets_num; /* empty list indicates autoselect (all available targets) */ targets = NULL; @@ -955,41 +1103,61 @@ static long _zcrypt_send_ep11_cprb(struct ap_perms *perms, goto out; } - uptr = (struct ep11_target_dev __force __user *) xcrb->targets; - if (copy_from_user(targets, uptr, - target_num * sizeof(*targets))) { + uptr = (struct ep11_target_dev __force __user *)xcrb->targets; + if (z_copy_from_user(userspace, targets, uptr, + target_num * sizeof(*targets))) { func_code = 0; rc = -EFAULT; goto out_free; } } - rc = get_ep11cprb_fc(xcrb, &ap_msg, &func_code); + rc = prep_ep11_ap_msg(userspace, xcrb, &ap_msg, &func_code, &domain); if (rc) goto out_free; + if (perms != &ap_perms && domain < AUTOSEL_DOM) { + if (ap_msg.flags & AP_MSG_FLAG_ADMIN) { + if (!test_bit_inv(domain, perms->adm)) { + rc = -ENODEV; + goto out_free; + } + } else if ((ap_msg.flags & AP_MSG_FLAG_USAGE) == 0) { + rc = -EOPNOTSUPP; + goto out_free; + } + } + pref_zc = NULL; pref_zq = NULL; spin_lock(&zcrypt_list_lock); for_each_zcrypt_card(zc) { - /* Check for online EP11 cards */ - if (!zc->online || !(zc->card->functions & 0x04000000)) + /* Check for usable EP11 card */ + if (!zc->online || !zc->card->config || zc->card->chkstop || + !(zc->card->functions & 0x04000000)) continue; /* Check for user selected EP11 card */ if (targets && !is_desired_ep11_card(zc->card->id, target_num, targets)) continue; + /* check if request size exceeds card max msg size */ + if (ap_msg.len > zc->card->maxmsgsize) + continue; /* check if device node has admission for this card */ if (!zcrypt_check_card(perms, zc->card->id)) continue; /* get weight index of the card device */ - weight = speed_idx_ep11(func_code) * zc->speed_rating[SECKEY]; - if (zcrypt_card_compare(zc, pref_zc, weight, pref_weight)) + wgt = speed_idx_ep11(func_code) * zc->speed_rating[SECKEY]; + /* penalty if this msg was previously sent via this card */ + cpen = (tr && tr->again_counter && tr->last_qid && + AP_QID_CARD(tr->last_qid) == zc->card->id) ? + TRACK_AGAIN_CARD_WEIGHT_PENALTY : 0; + if (!zcrypt_card_compare(zc, pref_zc, wgt + cpen, pref_wgt)) continue; for_each_zcrypt_queue(zq, zc) { - /* check if device is online and eligible */ - if (!zq->online || - !zq->ops->send_ep11_cprb || + /* check if device is usable and eligible */ + if (!zq->online || !zq->ops->send_ep11_cprb || + !zq->queue->config || zq->queue->chkstop || (targets && !is_desired_ep11_queue(zq->queue->qid, target_num, targets))) @@ -998,33 +1166,52 @@ static long _zcrypt_send_ep11_cprb(struct ap_perms *perms, if (!zcrypt_check_queue(perms, AP_QID_QUEUE(zq->queue->qid))) continue; - if (zcrypt_queue_compare(zq, pref_zq, - weight, pref_weight)) + /* penalty if the msg was previously sent at this qid */ + qpen = (tr && tr->again_counter && tr->last_qid && + tr->last_qid == zq->queue->qid) ? + TRACK_AGAIN_QUEUE_WEIGHT_PENALTY : 0; + if (!zcrypt_queue_compare(zq, pref_zq, + wgt + cpen + qpen, pref_wgt)) continue; pref_zc = zc; pref_zq = zq; - pref_weight = weight; + pref_wgt = wgt + cpen + qpen; } } - pref_zq = zcrypt_pick_queue(pref_zc, pref_zq, &mod, weight); + pref_zq = zcrypt_pick_queue(pref_zc, pref_zq, &mod, wgt); spin_unlock(&zcrypt_list_lock); if (!pref_zq) { + if (targets && target_num == 1) { + ZCRYPT_DBF_DBG("%s no match for address %02x.%04x => ENODEV\n", + __func__, (int)targets->ap_id, + (int)targets->dom_id); + } else if (targets) { + ZCRYPT_DBF_DBG("%s no match for %d target addrs => ENODEV\n", + __func__, (int)target_num); + } else { + ZCRYPT_DBF_DBG("%s no match for address ff.ffff => ENODEV\n", + __func__); + } rc = -ENODEV; goto out_free; } qid = pref_zq->queue->qid; - rc = pref_zq->ops->send_ep11_cprb(pref_zq, xcrb, &ap_msg); + rc = pref_zq->ops->send_ep11_cprb(userspace, pref_zq, xcrb, &ap_msg); spin_lock(&zcrypt_list_lock); - zcrypt_drop_queue(pref_zc, pref_zq, mod, weight); + zcrypt_drop_queue(pref_zc, pref_zq, mod, wgt); spin_unlock(&zcrypt_list_lock); out_free: kfree(targets); out: ap_release_message(&ap_msg); + if (tr) { + tr->last_rc = rc; + tr->last_qid = qid; + } trace_s390_zcrypt_rep(xcrb, func_code, rc, AP_QID_CARD(qid), AP_QID_QUEUE(qid)); return rc; @@ -1032,7 +1219,7 @@ out: long zcrypt_send_ep11_cprb(struct ep11_urb *xcrb) { - return _zcrypt_send_ep11_cprb(&ap_perms, xcrb); + return _zcrypt_send_ep11_cprb(false, &ap_perms, NULL, xcrb); } EXPORT_SYMBOL(zcrypt_send_ep11_cprb); @@ -1040,7 +1227,7 @@ static long zcrypt_rng(char *buffer) { struct zcrypt_card *zc, *pref_zc; struct zcrypt_queue *zq, *pref_zq; - unsigned int weight, pref_weight; + unsigned int wgt = 0, pref_wgt = 0; unsigned int func_code; struct ap_message ap_msg; unsigned int domain; @@ -1050,7 +1237,7 @@ static long zcrypt_rng(char *buffer) trace_s390_zcrypt_req(buffer, TP_HWRNGCPRB); ap_init_message(&ap_msg); - rc = get_rng_fc(&ap_msg, &func_code, &domain); + rc = prep_rng_ap_msg(&ap_msg, &func_code, &domain); if (rc) goto out; @@ -1058,29 +1245,32 @@ static long zcrypt_rng(char *buffer) pref_zq = NULL; spin_lock(&zcrypt_list_lock); for_each_zcrypt_card(zc) { - /* Check for online CCA cards */ - if (!zc->online || !(zc->card->functions & 0x10000000)) + /* Check for usable CCA card */ + if (!zc->online || !zc->card->config || zc->card->chkstop || + !(zc->card->functions & 0x10000000)) continue; /* get weight index of the card device */ - weight = zc->speed_rating[func_code]; - if (zcrypt_card_compare(zc, pref_zc, weight, pref_weight)) + wgt = zc->speed_rating[func_code]; + if (!zcrypt_card_compare(zc, pref_zc, wgt, pref_wgt)) continue; for_each_zcrypt_queue(zq, zc) { - /* check if device is online and eligible */ - if (!zq->online || !zq->ops->rng) + /* check if device is usable and eligible */ + if (!zq->online || !zq->ops->rng || + !zq->queue->config || zq->queue->chkstop) continue; - if (zcrypt_queue_compare(zq, pref_zq, - weight, pref_weight)) + if (!zcrypt_queue_compare(zq, pref_zq, wgt, pref_wgt)) continue; pref_zc = zc; pref_zq = zq; - pref_weight = weight; + pref_wgt = wgt; } } - pref_zq = zcrypt_pick_queue(pref_zc, pref_zq, &mod, weight); + pref_zq = zcrypt_pick_queue(pref_zc, pref_zq, &mod, wgt); spin_unlock(&zcrypt_list_lock); if (!pref_zq) { + ZCRYPT_DBF_DBG("%s no matching queue found => ENODEV\n", + __func__); rc = -ENODEV; goto out; } @@ -1089,7 +1279,7 @@ static long zcrypt_rng(char *buffer) rc = pref_zq->ops->rng(pref_zq, buffer, &ap_msg); spin_lock(&zcrypt_list_lock); - zcrypt_drop_queue(pref_zc, pref_zq, mod, weight); + zcrypt_drop_queue(pref_zc, pref_zq, mod, wgt); spin_unlock(&zcrypt_list_lock); out: @@ -1191,8 +1381,8 @@ static void zcrypt_status_mask(char status[], size_t max_adapters) for_each_zcrypt_card(zc) { for_each_zcrypt_queue(zq, zc) { card = AP_QID_CARD(zq->queue->qid); - if (AP_QID_QUEUE(zq->queue->qid) != ap_domain_index - || card >= max_adapters) + if (AP_QID_QUEUE(zq->queue->qid) != ap_domain_index || + card >= max_adapters) continue; status[card] = zc->online ? zc->user_space_type : 0x0d; } @@ -1212,8 +1402,8 @@ static void zcrypt_qdepth_mask(char qdepth[], size_t max_adapters) for_each_zcrypt_card(zc) { for_each_zcrypt_queue(zq, zc) { card = AP_QID_CARD(zq->queue->qid); - if (AP_QID_QUEUE(zq->queue->qid) != ap_domain_index - || card >= max_adapters) + if (AP_QID_QUEUE(zq->queue->qid) != ap_domain_index || + card >= max_adapters) continue; spin_lock(&zq->queue->lock); qdepth[card] = @@ -1239,13 +1429,13 @@ static void zcrypt_perdev_reqcnt(u32 reqcnt[], size_t max_adapters) for_each_zcrypt_card(zc) { for_each_zcrypt_queue(zq, zc) { card = AP_QID_CARD(zq->queue->qid); - if (AP_QID_QUEUE(zq->queue->qid) != ap_domain_index - || card >= max_adapters) + if (AP_QID_QUEUE(zq->queue->qid) != ap_domain_index || + card >= max_adapters) continue; spin_lock(&zq->queue->lock); cnt = zq->queue->total_request_count; spin_unlock(&zq->queue->lock); - reqcnt[card] = (cnt < UINT_MAX) ? (u32) cnt : UINT_MAX; + reqcnt[card] = (cnt < UINT_MAX) ? (u32)cnt : UINT_MAX; } } local_bh_enable(); @@ -1298,99 +1488,207 @@ static int zcrypt_requestq_count(void) return requestq_count; } -static long zcrypt_unlocked_ioctl(struct file *filp, unsigned int cmd, - unsigned long arg) +static int icarsamodexpo_ioctl(struct ap_perms *perms, unsigned long arg) { int rc; - struct ap_perms *perms = - (struct ap_perms *) filp->private_data; + struct zcrypt_track tr; + struct ica_rsa_modexpo mex; + struct ica_rsa_modexpo __user *umex = (void __user *)arg; - rc = zcrypt_check_ioctl(perms, cmd); - if (rc) - return rc; + memset(&tr, 0, sizeof(tr)); + if (copy_from_user(&mex, umex, sizeof(mex))) + return -EFAULT; - switch (cmd) { - case ICARSAMODEXPO: { - struct ica_rsa_modexpo __user *umex = (void __user *) arg; - struct ica_rsa_modexpo mex; +#ifdef CONFIG_ZCRYPT_DEBUG + if (mex.inputdatalength & (1U << 31)) { + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + tr.fi.cmd = (u16)(mex.inputdatalength >> 16); + } + mex.inputdatalength &= 0x0000FFFF; +#endif - if (copy_from_user(&mex, umex, sizeof(mex))) - return -EFAULT; + do { + rc = zcrypt_rsa_modexpo(perms, &tr, &mex); + if (rc == -EAGAIN) + tr.again_counter++; +#ifdef CONFIG_ZCRYPT_DEBUG + if (rc == -EAGAIN && (tr.fi.flags & AP_FI_FLAG_NO_RETRY)) + break; +#endif + } while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX); + /* on failure: retry once again after a requested rescan */ + if ((rc == -ENODEV) && (zcrypt_process_rescan())) do { - rc = zcrypt_rsa_modexpo(perms, &mex); - } while (rc == -EAGAIN); - /* on failure: retry once again after a requested rescan */ - if ((rc == -ENODEV) && (zcrypt_process_rescan())) - do { - rc = zcrypt_rsa_modexpo(perms, &mex); - } while (rc == -EAGAIN); - if (rc) { - ZCRYPT_DBF(DBF_DEBUG, "ioctl ICARSAMODEXPO rc=%d\n", rc); - return rc; - } - return put_user(mex.outputdatalength, &umex->outputdatalength); + rc = zcrypt_rsa_modexpo(perms, &tr, &mex); + if (rc == -EAGAIN) + tr.again_counter++; + } while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX); + if (rc == -EAGAIN && tr.again_counter >= TRACK_AGAIN_MAX) + rc = -EIO; + if (rc) { + ZCRYPT_DBF_DBG("ioctl ICARSAMODEXPO rc=%d\n", rc); + return rc; } - case ICARSACRT: { - struct ica_rsa_modexpo_crt __user *ucrt = (void __user *) arg; - struct ica_rsa_modexpo_crt crt; + return put_user(mex.outputdatalength, &umex->outputdatalength); +} - if (copy_from_user(&crt, ucrt, sizeof(crt))) - return -EFAULT; - do { - rc = zcrypt_rsa_crt(perms, &crt); - } while (rc == -EAGAIN); - /* on failure: retry once again after a requested rescan */ - if ((rc == -ENODEV) && (zcrypt_process_rescan())) - do { - rc = zcrypt_rsa_crt(perms, &crt); - } while (rc == -EAGAIN); - if (rc) { - ZCRYPT_DBF(DBF_DEBUG, "ioctl ICARSACRT rc=%d\n", rc); - return rc; - } - return put_user(crt.outputdatalength, &ucrt->outputdatalength); +static int icarsacrt_ioctl(struct ap_perms *perms, unsigned long arg) +{ + int rc; + struct zcrypt_track tr; + struct ica_rsa_modexpo_crt crt; + struct ica_rsa_modexpo_crt __user *ucrt = (void __user *)arg; + + memset(&tr, 0, sizeof(tr)); + if (copy_from_user(&crt, ucrt, sizeof(crt))) + return -EFAULT; + +#ifdef CONFIG_ZCRYPT_DEBUG + if (crt.inputdatalength & (1U << 31)) { + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + tr.fi.cmd = (u16)(crt.inputdatalength >> 16); } - case ZSECSENDCPRB: { - struct ica_xcRB __user *uxcRB = (void __user *) arg; - struct ica_xcRB xcRB; + crt.inputdatalength &= 0x0000FFFF; +#endif - if (copy_from_user(&xcRB, uxcRB, sizeof(xcRB))) - return -EFAULT; + do { + rc = zcrypt_rsa_crt(perms, &tr, &crt); + if (rc == -EAGAIN) + tr.again_counter++; +#ifdef CONFIG_ZCRYPT_DEBUG + if (rc == -EAGAIN && (tr.fi.flags & AP_FI_FLAG_NO_RETRY)) + break; +#endif + } while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX); + /* on failure: retry once again after a requested rescan */ + if ((rc == -ENODEV) && (zcrypt_process_rescan())) do { - rc = _zcrypt_send_cprb(perms, &xcRB); - } while (rc == -EAGAIN); - /* on failure: retry once again after a requested rescan */ - if ((rc == -ENODEV) && (zcrypt_process_rescan())) - do { - rc = _zcrypt_send_cprb(perms, &xcRB); - } while (rc == -EAGAIN); - if (rc) - ZCRYPT_DBF(DBF_DEBUG, "ioctl ZSENDCPRB rc=%d status=0x%x\n", - rc, xcRB.status); - if (copy_to_user(uxcRB, &xcRB, sizeof(xcRB))) - return -EFAULT; + rc = zcrypt_rsa_crt(perms, &tr, &crt); + if (rc == -EAGAIN) + tr.again_counter++; + } while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX); + if (rc == -EAGAIN && tr.again_counter >= TRACK_AGAIN_MAX) + rc = -EIO; + if (rc) { + ZCRYPT_DBF_DBG("ioctl ICARSACRT rc=%d\n", rc); return rc; } - case ZSENDEP11CPRB: { - struct ep11_urb __user *uxcrb = (void __user *)arg; - struct ep11_urb xcrb; + return put_user(crt.outputdatalength, &ucrt->outputdatalength); +} - if (copy_from_user(&xcrb, uxcrb, sizeof(xcrb))) - return -EFAULT; +static int zsecsendcprb_ioctl(struct ap_perms *perms, unsigned long arg) +{ + int rc; + struct ica_xcRB xcrb; + struct zcrypt_track tr; + struct ica_xcRB __user *uxcrb = (void __user *)arg; + + memset(&tr, 0, sizeof(tr)); + if (copy_from_user(&xcrb, uxcrb, sizeof(xcrb))) + return -EFAULT; + +#ifdef CONFIG_ZCRYPT_DEBUG + if ((xcrb.status & 0x8000FFFF) == 0x80004649 /* 'FI' */) { + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + tr.fi.cmd = (u16)(xcrb.status >> 16); + } + xcrb.status = 0; +#endif + + do { + rc = _zcrypt_send_cprb(true, perms, &tr, &xcrb); + if (rc == -EAGAIN) + tr.again_counter++; +#ifdef CONFIG_ZCRYPT_DEBUG + if (rc == -EAGAIN && (tr.fi.flags & AP_FI_FLAG_NO_RETRY)) + break; +#endif + } while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX); + /* on failure: retry once again after a requested rescan */ + if ((rc == -ENODEV) && (zcrypt_process_rescan())) do { - rc = _zcrypt_send_ep11_cprb(perms, &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(perms, &xcrb); - } while (rc == -EAGAIN); - if (rc) - ZCRYPT_DBF(DBF_DEBUG, "ioctl ZSENDEP11CPRB rc=%d\n", rc); - if (copy_to_user(uxcrb, &xcrb, sizeof(xcrb))) - return -EFAULT; - return rc; + rc = _zcrypt_send_cprb(true, perms, &tr, &xcrb); + if (rc == -EAGAIN) + tr.again_counter++; + } while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX); + if (rc == -EAGAIN && tr.again_counter >= TRACK_AGAIN_MAX) + rc = -EIO; + if (rc) + ZCRYPT_DBF_DBG("ioctl ZSENDCPRB rc=%d status=0x%x\n", + rc, xcrb.status); + if (copy_to_user(uxcrb, &xcrb, sizeof(xcrb))) + return -EFAULT; + return rc; +} + +static int zsendep11cprb_ioctl(struct ap_perms *perms, unsigned long arg) +{ + int rc; + struct ep11_urb xcrb; + struct zcrypt_track tr; + struct ep11_urb __user *uxcrb = (void __user *)arg; + + memset(&tr, 0, sizeof(tr)); + if (copy_from_user(&xcrb, uxcrb, sizeof(xcrb))) + return -EFAULT; + +#ifdef CONFIG_ZCRYPT_DEBUG + if (xcrb.req_len & (1ULL << 63)) { + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + tr.fi.cmd = (u16)(xcrb.req_len >> 48); } + xcrb.req_len &= 0x0000FFFFFFFFFFFFULL; +#endif + + do { + rc = _zcrypt_send_ep11_cprb(true, perms, &tr, &xcrb); + if (rc == -EAGAIN) + tr.again_counter++; +#ifdef CONFIG_ZCRYPT_DEBUG + if (rc == -EAGAIN && (tr.fi.flags & AP_FI_FLAG_NO_RETRY)) + break; +#endif + } while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX); + /* on failure: retry once again after a requested rescan */ + if ((rc == -ENODEV) && (zcrypt_process_rescan())) + do { + rc = _zcrypt_send_ep11_cprb(true, perms, &tr, &xcrb); + if (rc == -EAGAIN) + tr.again_counter++; + } while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX); + if (rc == -EAGAIN && tr.again_counter >= TRACK_AGAIN_MAX) + rc = -EIO; + if (rc) + ZCRYPT_DBF_DBG("ioctl ZSENDEP11CPRB rc=%d\n", rc); + if (copy_to_user(uxcrb, &xcrb, sizeof(xcrb))) + return -EFAULT; + return rc; +} + +static long zcrypt_unlocked_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + int rc; + struct ap_perms *perms = + (struct ap_perms *)filp->private_data; + + rc = zcrypt_check_ioctl(perms, cmd); + if (rc) + return rc; + + switch (cmd) { + case ICARSAMODEXPO: + return icarsamodexpo_ioctl(perms, arg); + case ICARSACRT: + return icarsacrt_ioctl(perms, arg); + case ZSECSENDCPRB: + return zsecsendcprb_ioctl(perms, arg); + case ZSENDEP11CPRB: + return zsendep11cprb_ioctl(perms, arg); case ZCRYPT_DEVICE_STATUS: { struct zcrypt_device_status_ext *device_status; size_t total_size = MAX_ZDEV_ENTRIES_EXT @@ -1400,7 +1698,7 @@ static long zcrypt_unlocked_ioctl(struct file *filp, unsigned int cmd, if (!device_status) return -ENOMEM; zcrypt_device_status_mask_ext(device_status); - if (copy_to_user((char __user *) arg, device_status, + if (copy_to_user((char __user *)arg, device_status, total_size)) rc = -EFAULT; kfree(device_status); @@ -1410,7 +1708,7 @@ static long zcrypt_unlocked_ioctl(struct file *filp, unsigned int cmd, char status[AP_DEVICES]; zcrypt_status_mask(status, AP_DEVICES); - if (copy_to_user((char __user *) arg, status, sizeof(status))) + if (copy_to_user((char __user *)arg, status, sizeof(status))) return -EFAULT; return 0; } @@ -1418,7 +1716,7 @@ static long zcrypt_unlocked_ioctl(struct file *filp, unsigned int cmd, char qdepth[AP_DEVICES]; zcrypt_qdepth_mask(qdepth, AP_DEVICES); - if (copy_to_user((char __user *) arg, qdepth, sizeof(qdepth))) + if (copy_to_user((char __user *)arg, qdepth, sizeof(qdepth))) return -EFAULT; return 0; } @@ -1429,20 +1727,21 @@ static long zcrypt_unlocked_ioctl(struct file *filp, unsigned int cmd, if (!reqcnt) return -ENOMEM; zcrypt_perdev_reqcnt(reqcnt, AP_DEVICES); - if (copy_to_user((int __user *) arg, reqcnt, sizeof(reqcnt))) + if (copy_to_user((int __user *)arg, reqcnt, + sizeof(u32) * AP_DEVICES)) rc = -EFAULT; kfree(reqcnt); return rc; } case Z90STAT_REQUESTQ_COUNT: - return put_user(zcrypt_requestq_count(), (int __user *) arg); + return put_user(zcrypt_requestq_count(), (int __user *)arg); case Z90STAT_PENDINGQ_COUNT: - return put_user(zcrypt_pendingq_count(), (int __user *) arg); + return put_user(zcrypt_pendingq_count(), (int __user *)arg); case Z90STAT_TOTALOPEN_COUNT: return put_user(atomic_read(&zcrypt_open_count), - (int __user *) arg); + (int __user *)arg); case Z90STAT_DOMAIN_INDEX: - return put_user(ap_domain_index, (int __user *) arg); + return put_user(ap_domain_index, (int __user *)arg); /* * Deprecated ioctls */ @@ -1456,7 +1755,7 @@ static long zcrypt_unlocked_ioctl(struct file *filp, unsigned int cmd, if (!device_status) return -ENOMEM; zcrypt_device_status_mask(device_status); - if (copy_to_user((char __user *) arg, device_status, + if (copy_to_user((char __user *)arg, device_status, total_size)) rc = -EFAULT; kfree(device_status); @@ -1467,7 +1766,7 @@ static long zcrypt_unlocked_ioctl(struct file *filp, unsigned int cmd, char status[MAX_ZDEV_CARDIDS]; zcrypt_status_mask(status, MAX_ZDEV_CARDIDS); - if (copy_to_user((char __user *) arg, status, sizeof(status))) + if (copy_to_user((char __user *)arg, status, sizeof(status))) return -EFAULT; return 0; } @@ -1476,7 +1775,7 @@ static long zcrypt_unlocked_ioctl(struct file *filp, unsigned int cmd, char qdepth[MAX_ZDEV_CARDIDS]; zcrypt_qdepth_mask(qdepth, MAX_ZDEV_CARDIDS); - if (copy_to_user((char __user *) arg, qdepth, sizeof(qdepth))) + if (copy_to_user((char __user *)arg, qdepth, sizeof(qdepth))) return -EFAULT; return 0; } @@ -1485,13 +1784,13 @@ static long zcrypt_unlocked_ioctl(struct file *filp, unsigned int cmd, u32 reqcnt[MAX_ZDEV_CARDIDS]; zcrypt_perdev_reqcnt(reqcnt, MAX_ZDEV_CARDIDS); - if (copy_to_user((int __user *) arg, reqcnt, sizeof(reqcnt))) + if (copy_to_user((int __user *)arg, reqcnt, sizeof(reqcnt))) return -EFAULT; return 0; } /* unknown ioctl number */ default: - ZCRYPT_DBF(DBF_DEBUG, "unknown ioctl 0x%08x\n", cmd); + ZCRYPT_DBF_DBG("unknown ioctl 0x%08x\n", cmd); return -ENOIOCTLCMD; } } @@ -1515,8 +1814,10 @@ static long trans_modexpo32(struct ap_perms *perms, struct file *filp, struct compat_ica_rsa_modexpo __user *umex32 = compat_ptr(arg); struct compat_ica_rsa_modexpo mex32; struct ica_rsa_modexpo mex64; + struct zcrypt_track tr; long rc; + memset(&tr, 0, sizeof(tr)); if (copy_from_user(&mex32, umex32, sizeof(mex32))) return -EFAULT; mex64.inputdata = compat_ptr(mex32.inputdata); @@ -1526,13 +1827,19 @@ static long trans_modexpo32(struct ap_perms *perms, struct file *filp, mex64.b_key = compat_ptr(mex32.b_key); mex64.n_modulus = compat_ptr(mex32.n_modulus); do { - rc = zcrypt_rsa_modexpo(perms, &mex64); - } while (rc == -EAGAIN); + rc = zcrypt_rsa_modexpo(perms, &tr, &mex64); + if (rc == -EAGAIN) + tr.again_counter++; + } while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX); /* on failure: retry once again after a requested rescan */ if ((rc == -ENODEV) && (zcrypt_process_rescan())) do { - rc = zcrypt_rsa_modexpo(perms, &mex64); - } while (rc == -EAGAIN); + rc = zcrypt_rsa_modexpo(perms, &tr, &mex64); + if (rc == -EAGAIN) + tr.again_counter++; + } while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX); + if (rc == -EAGAIN && tr.again_counter >= TRACK_AGAIN_MAX) + rc = -EIO; if (rc) return rc; return put_user(mex64.outputdatalength, @@ -1557,8 +1864,10 @@ static long trans_modexpo_crt32(struct ap_perms *perms, struct file *filp, struct compat_ica_rsa_modexpo_crt __user *ucrt32 = compat_ptr(arg); struct compat_ica_rsa_modexpo_crt crt32; struct ica_rsa_modexpo_crt crt64; + struct zcrypt_track tr; long rc; + memset(&tr, 0, sizeof(tr)); if (copy_from_user(&crt32, ucrt32, sizeof(crt32))) return -EFAULT; crt64.inputdata = compat_ptr(crt32.inputdata); @@ -1571,20 +1880,26 @@ static long trans_modexpo_crt32(struct ap_perms *perms, struct file *filp, crt64.nq_prime = compat_ptr(crt32.nq_prime); crt64.u_mult_inv = compat_ptr(crt32.u_mult_inv); do { - rc = zcrypt_rsa_crt(perms, &crt64); - } while (rc == -EAGAIN); + rc = zcrypt_rsa_crt(perms, &tr, &crt64); + if (rc == -EAGAIN) + tr.again_counter++; + } while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX); /* on failure: retry once again after a requested rescan */ if ((rc == -ENODEV) && (zcrypt_process_rescan())) do { - rc = zcrypt_rsa_crt(perms, &crt64); - } while (rc == -EAGAIN); + rc = zcrypt_rsa_crt(perms, &tr, &crt64); + if (rc == -EAGAIN) + tr.again_counter++; + } while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX); + if (rc == -EAGAIN && tr.again_counter >= TRACK_AGAIN_MAX) + rc = -EIO; if (rc) return rc; return put_user(crt64.outputdatalength, &ucrt32->outputdatalength); } -struct compat_ica_xcRB { +struct compat_ica_xcrb { unsigned short agent_ID; unsigned int user_defined; unsigned short request_ID; @@ -1604,58 +1919,66 @@ struct compat_ica_xcRB { unsigned int status; } __packed; -static long trans_xcRB32(struct ap_perms *perms, struct file *filp, +static long trans_xcrb32(struct ap_perms *perms, struct file *filp, unsigned int cmd, unsigned long arg) { - struct compat_ica_xcRB __user *uxcRB32 = compat_ptr(arg); - struct compat_ica_xcRB xcRB32; - struct ica_xcRB xcRB64; + struct compat_ica_xcrb __user *uxcrb32 = compat_ptr(arg); + struct compat_ica_xcrb xcrb32; + struct zcrypt_track tr; + struct ica_xcRB xcrb64; long rc; - if (copy_from_user(&xcRB32, uxcRB32, sizeof(xcRB32))) + memset(&tr, 0, sizeof(tr)); + if (copy_from_user(&xcrb32, uxcrb32, sizeof(xcrb32))) return -EFAULT; - xcRB64.agent_ID = xcRB32.agent_ID; - xcRB64.user_defined = xcRB32.user_defined; - xcRB64.request_ID = xcRB32.request_ID; - xcRB64.request_control_blk_length = - xcRB32.request_control_blk_length; - xcRB64.request_control_blk_addr = - compat_ptr(xcRB32.request_control_blk_addr); - xcRB64.request_data_length = - xcRB32.request_data_length; - xcRB64.request_data_address = - compat_ptr(xcRB32.request_data_address); - xcRB64.reply_control_blk_length = - xcRB32.reply_control_blk_length; - xcRB64.reply_control_blk_addr = - compat_ptr(xcRB32.reply_control_blk_addr); - xcRB64.reply_data_length = xcRB32.reply_data_length; - xcRB64.reply_data_addr = - compat_ptr(xcRB32.reply_data_addr); - xcRB64.priority_window = xcRB32.priority_window; - xcRB64.status = xcRB32.status; + xcrb64.agent_ID = xcrb32.agent_ID; + xcrb64.user_defined = xcrb32.user_defined; + xcrb64.request_ID = xcrb32.request_ID; + xcrb64.request_control_blk_length = + xcrb32.request_control_blk_length; + xcrb64.request_control_blk_addr = + compat_ptr(xcrb32.request_control_blk_addr); + xcrb64.request_data_length = + xcrb32.request_data_length; + xcrb64.request_data_address = + compat_ptr(xcrb32.request_data_address); + xcrb64.reply_control_blk_length = + xcrb32.reply_control_blk_length; + xcrb64.reply_control_blk_addr = + compat_ptr(xcrb32.reply_control_blk_addr); + xcrb64.reply_data_length = xcrb32.reply_data_length; + xcrb64.reply_data_addr = + compat_ptr(xcrb32.reply_data_addr); + xcrb64.priority_window = xcrb32.priority_window; + xcrb64.status = xcrb32.status; do { - rc = _zcrypt_send_cprb(perms, &xcRB64); - } while (rc == -EAGAIN); + rc = _zcrypt_send_cprb(true, perms, &tr, &xcrb64); + if (rc == -EAGAIN) + tr.again_counter++; + } while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX); /* on failure: retry once again after a requested rescan */ if ((rc == -ENODEV) && (zcrypt_process_rescan())) do { - rc = _zcrypt_send_cprb(perms, &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; - if (copy_to_user(uxcRB32, &xcRB32, sizeof(xcRB32))) + rc = _zcrypt_send_cprb(true, perms, &tr, &xcrb64); + if (rc == -EAGAIN) + tr.again_counter++; + } while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX); + if (rc == -EAGAIN && tr.again_counter >= TRACK_AGAIN_MAX) + rc = -EIO; + xcrb32.reply_control_blk_length = xcrb64.reply_control_blk_length; + xcrb32.reply_data_length = xcrb64.reply_data_length; + xcrb32.status = xcrb64.status; + if (copy_to_user(uxcrb32, &xcrb32, sizeof(xcrb32))) return -EFAULT; return rc; } static long zcrypt_compat_ioctl(struct file *filp, unsigned int cmd, - unsigned long arg) + unsigned long arg) { int rc; struct ap_perms *perms = - (struct ap_perms *) filp->private_data; + (struct ap_perms *)filp->private_data; rc = zcrypt_check_ioctl(perms, cmd); if (rc) @@ -1666,7 +1989,7 @@ static long zcrypt_compat_ioctl(struct file *filp, unsigned int cmd, if (cmd == ICARSACRT) return trans_modexpo_crt32(perms, filp, cmd, arg); if (cmd == ZSECSENDCPRB) - return trans_xcRB32(perms, filp, cmd, arg); + return trans_xcrb32(perms, filp, cmd, arg); return zcrypt_unlocked_ioctl(filp, cmd, arg); } #endif @@ -1710,10 +2033,10 @@ static int zcrypt_rng_data_read(struct hwrng *rng, u32 *data) * read method calls. */ if (zcrypt_rng_buffer_index == 0) { - rc = zcrypt_rng((char *) zcrypt_rng_buffer); + 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); + rc = zcrypt_rng((char *)zcrypt_rng_buffer); if (rc < 0) return -EIO; zcrypt_rng_buffer_index = rc / sizeof(*data); @@ -1734,7 +2057,7 @@ int zcrypt_rng_device_add(void) mutex_lock(&zcrypt_rng_mutex); if (zcrypt_rng_device_count == 0) { - zcrypt_rng_buffer = (u32 *) get_zeroed_page(GFP_KERNEL); + zcrypt_rng_buffer = (u32 *)get_zeroed_page(GFP_KERNEL); if (!zcrypt_rng_buffer) { rc = -ENOMEM; goto out; @@ -1746,13 +2069,14 @@ int zcrypt_rng_device_add(void) if (rc) goto out_free; zcrypt_rng_device_count = 1; - } else + } else { zcrypt_rng_device_count++; + } mutex_unlock(&zcrypt_rng_mutex); return 0; out_free: - free_page((unsigned long) zcrypt_rng_buffer); + free_page((unsigned long)zcrypt_rng_buffer); out: mutex_unlock(&zcrypt_rng_mutex); return rc; @@ -1764,14 +2088,78 @@ void zcrypt_rng_device_remove(void) zcrypt_rng_device_count--; if (zcrypt_rng_device_count == 0) { hwrng_unregister(&zcrypt_rng_dev); - free_page((unsigned long) zcrypt_rng_buffer); + free_page((unsigned long)zcrypt_rng_buffer); } mutex_unlock(&zcrypt_rng_mutex); } +/* + * Wait until the zcrypt api is operational. + * The AP bus scan and the binding of ap devices to device drivers is + * an asynchronous job. This function waits until these initial jobs + * are done and so the zcrypt api should be ready to serve crypto + * requests - if there are resources available. The function uses an + * internal timeout of 60s. The very first caller will either wait for + * ap bus bindings complete or the timeout happens. This state will be + * remembered for further callers which will only be blocked until a + * decision is made (timeout or bindings complete). + * On timeout -ETIME is returned, on success the return value is 0. + */ +int zcrypt_wait_api_operational(void) +{ + static DEFINE_MUTEX(zcrypt_wait_api_lock); + static int zcrypt_wait_api_state; + int rc; + + rc = mutex_lock_interruptible(&zcrypt_wait_api_lock); + if (rc) + return rc; + + switch (zcrypt_wait_api_state) { + case 0: + /* initial state, invoke wait for the ap bus complete */ + rc = ap_wait_init_apqn_bindings_complete( + msecs_to_jiffies(60 * 1000)); + switch (rc) { + case 0: + /* ap bus bindings are complete */ + zcrypt_wait_api_state = 1; + break; + case -EINTR: + /* interrupted, go back to caller */ + break; + case -ETIME: + /* timeout */ + ZCRYPT_DBF_WARN("%s ap_wait_init_apqn_bindings_complete()=ETIME\n", + __func__); + zcrypt_wait_api_state = -ETIME; + break; + default: + /* other failure */ + ZCRYPT_DBF_DBG("%s ap_wait_init_apqn_bindings_complete()=%d\n", + __func__, rc); + break; + } + break; + case 1: + /* a previous caller already found ap bus bindings complete */ + rc = 0; + break; + default: + /* a previous caller had timeout or other failure */ + rc = zcrypt_wait_api_state; + break; + } + + mutex_unlock(&zcrypt_wait_api_lock); + + return rc; +} +EXPORT_SYMBOL(zcrypt_wait_api_operational); + int __init zcrypt_debug_init(void) { - zcrypt_dbf_info = debug_register("zcrypt", 1, 1, + zcrypt_dbf_info = debug_register("zcrypt", 2, 1, DBF_MAX_SPRINTF_ARGS * sizeof(long)); debug_register_view(zcrypt_dbf_info, &debug_sprintf_view); debug_set_level(zcrypt_dbf_info, DBF_ERR); @@ -1845,7 +2233,7 @@ static void zcdn_exit(void) #endif -/** +/* * zcrypt_api_init(): Module initialization. * * The module initialization code. @@ -1883,7 +2271,7 @@ out: return rc; } -/** +/* * zcrypt_api_exit(): Module termination. * * The module termination code. |