Update Linux to v5.4.2

Change-Id: Idf6911045d9d382da2cfe01b1edff026404ac8fd
diff --git a/drivers/s390/crypto/Makefile b/drivers/s390/crypto/Makefile
index b59af54..52aa95c 100644
--- a/drivers/s390/crypto/Makefile
+++ b/drivers/s390/crypto/Makefile
@@ -7,11 +7,15 @@
 obj-$(subst m,y,$(CONFIG_ZCRYPT)) += ap.o
 # zcrypt_api.o and zcrypt_msgtype*.o depend on ap.o
 zcrypt-objs := zcrypt_api.o zcrypt_card.o zcrypt_queue.o
-zcrypt-objs += zcrypt_msgtype6.o zcrypt_msgtype50.o
+zcrypt-objs += zcrypt_msgtype6.o zcrypt_msgtype50.o zcrypt_ccamisc.o
 obj-$(CONFIG_ZCRYPT) += zcrypt.o
 # adapter drivers depend on ap.o and zcrypt.o
-obj-$(CONFIG_ZCRYPT) += zcrypt_pcixcc.o zcrypt_cex2a.o zcrypt_cex4.o
+obj-$(CONFIG_ZCRYPT) += zcrypt_cex2c.o zcrypt_cex2a.o zcrypt_cex4.o
 
 # pkey kernel module
 pkey-objs := pkey_api.o
 obj-$(CONFIG_PKEY) += pkey.o
+
+# adjunct processor matrix
+vfio_ap-objs := vfio_ap_drv.o vfio_ap_ops.o
+obj-$(CONFIG_VFIO_AP) += vfio_ap.o
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c
index f039266..a191506 100644
--- a/drivers/s390/crypto/ap_bus.c
+++ b/drivers/s390/crypto/ap_bus.c
@@ -65,12 +65,11 @@
 DEFINE_SPINLOCK(ap_list_lock);
 LIST_HEAD(ap_card_list);
 
-/* Default permissions (card and domain masking) */
-static struct ap_perms {
-	DECLARE_BITMAP(apm, AP_DEVICES);
-	DECLARE_BITMAP(aqm, AP_DOMAINS);
-} ap_perms;
-static DEFINE_MUTEX(ap_perms_mutex);
+/* Default permissions (ioctl, card and domain masking) */
+struct ap_perms ap_perms;
+EXPORT_SYMBOL(ap_perms);
+DEFINE_MUTEX(ap_perms_mutex);
+EXPORT_SYMBOL(ap_perms_mutex);
 
 static struct ap_config_info *ap_configuration;
 static bool initialised;
@@ -117,7 +116,7 @@
 static struct bus_type ap_bus_type;
 
 /* Adapter interrupt definitions */
-static void ap_interrupt_handler(struct airq_struct *airq);
+static void ap_interrupt_handler(struct airq_struct *airq, bool floating);
 
 static int ap_airq_flag;
 
@@ -209,7 +208,6 @@
 		return -EINVAL;
 	return ap_qci(info);
 }
-EXPORT_SYMBOL(ap_query_configuration);
 
 /**
  * ap_init_configuration(): Allocate and query configuration array.
@@ -249,24 +247,43 @@
 static inline int ap_test_config_card_id(unsigned int id)
 {
 	if (!ap_configuration)	/* QCI not supported */
-		return 1;
+		/* only ids 0...3F may be probed */
+		return id < 0x40 ? 1 : 0;
 	return ap_test_config(ap_configuration->apm, id);
 }
 
 /*
- * ap_test_config_domain(): Test, whether an AP usage domain is configured.
+ * ap_test_config_usage_domain(): Test, whether an AP usage domain
+ * is configured.
  * @domain AP usage domain ID
  *
  * Returns 0 if the usage domain is not configured
  *	   1 if the usage domain is configured or
  *	     if the configuration information is not available
  */
-static inline int ap_test_config_domain(unsigned int domain)
+int ap_test_config_usage_domain(unsigned int domain)
 {
 	if (!ap_configuration)	/* QCI not supported */
 		return domain < 16;
 	return ap_test_config(ap_configuration->aqm, domain);
 }
+EXPORT_SYMBOL(ap_test_config_usage_domain);
+
+/*
+ * ap_test_config_ctrl_domain(): Test, whether an AP control domain
+ * is configured.
+ * @domain AP control domain ID
+ *
+ * Returns 1 if the control domain is configured
+ *	   0 in all other cases
+ */
+int ap_test_config_ctrl_domain(unsigned int domain)
+{
+	if (!ap_configuration)	/* QCI not supported */
+		return 0;
+	return ap_test_config(ap_configuration->adm, domain);
+}
+EXPORT_SYMBOL(ap_test_config_ctrl_domain);
 
 /**
  * ap_query_queue(): Check if an AP queue is available.
@@ -300,7 +317,7 @@
 			ap_max_domain_id = 15;
 		switch (*device_type) {
 			/* For CEX2 and CEX3 the available functions
-			 * are not refrected by the facilities bits.
+			 * are not reflected by the facilities bits.
 			 * Instead it is coded into the type. So here
 			 * modify the function bits based on the type.
 			 */
@@ -393,7 +410,7 @@
  * ap_interrupt_handler() - Schedule ap_tasklet on interrupt
  * @airq: pointer to adapter interrupt descriptor
  */
-static void ap_interrupt_handler(struct airq_struct *airq)
+static void ap_interrupt_handler(struct airq_struct *airq, bool floating)
 {
 	inc_irq_stat(IRQIO_APB);
 	if (!ap_suspend_flag)
@@ -776,6 +793,8 @@
 		drvres = ap_drv->flags & AP_DRIVER_FLAG_DEFAULT;
 		if (!!devres != !!drvres)
 			return -ENODEV;
+		/* (re-)init queue's state machine */
+		ap_queue_reinit_state(to_ap_queue(dev));
 	}
 
 	/* Add queue/card to list of active queues/cards */
@@ -808,9 +827,18 @@
 	struct ap_device *ap_dev = to_ap_dev(dev);
 	struct ap_driver *ap_drv = ap_dev->drv;
 
+	/* prepare ap queue device removal */
+	if (is_queue_dev(dev))
+		ap_queue_prepare_remove(to_ap_queue(dev));
+
+	/* driver's chance to clean up gracefully */
 	if (ap_drv->remove)
 		ap_drv->remove(ap_dev);
 
+	/* now do the ap queue device remove */
+	if (is_queue_dev(dev))
+		ap_queue_remove(to_ap_queue(dev));
+
 	/* Remove queue/card from list of active queues/cards */
 	spin_lock_bh(&ap_list_lock);
 	if (is_card_dev(dev))
@@ -857,6 +885,16 @@
 EXPORT_SYMBOL(ap_bus_force_rescan);
 
 /*
+* A config change has happened, force an ap bus rescan.
+*/
+void ap_bus_cfg_chg(void)
+{
+	AP_DBF(DBF_INFO, "%s config change, forcing bus rescan\n", __func__);
+
+	ap_bus_force_rescan();
+}
+
+/*
  * hex2bitmap() - parse hex mask string and set bitmap.
  * Valid strings are "0x012345678" with at least one valid hex number.
  * Rest of the bitmap to the right is padded with 0. No spaces allowed
@@ -944,21 +982,9 @@
 	return 0;
 }
 
-/*
- * process_mask_arg() - parse a bitmap string and clear/set the
- * bits in the bitmap accordingly. The string may be given as
- * absolute value, a hex string like 0x1F2E3D4C5B6A" simple over-
- * writing the current content of the bitmap. Or as relative string
- * like "+1-16,-32,-0x40,+128" where only single bits or ranges of
- * bits are cleared or set. Distinction is done based on the very
- * first character which may be '+' or '-' for the relative string
- * and othewise assume to be an absolute value string. If parsing fails
- * a negative errno value is returned. All arguments and bitmaps are
- * big endian order.
- */
-static int process_mask_arg(const char *str,
-			    unsigned long *bitmap, int bits,
-			    struct mutex *lock)
+int ap_parse_mask_str(const char *str,
+		      unsigned long *bitmap, int bits,
+		      struct mutex *lock)
 {
 	unsigned long *newmap, size;
 	int rc;
@@ -989,6 +1015,7 @@
 	kfree(newmap);
 	return rc;
 }
+EXPORT_SYMBOL(ap_parse_mask_str);
 
 /*
  * AP bus attributes.
@@ -1049,6 +1076,21 @@
 
 static BUS_ATTR_RO(ap_usage_domain_mask);
 
+static ssize_t ap_adapter_mask_show(struct bus_type *bus, char *buf)
+{
+	if (!ap_configuration)	/* QCI not supported */
+		return snprintf(buf, PAGE_SIZE, "not supported\n");
+
+	return snprintf(buf, PAGE_SIZE,
+			"0x%08x%08x%08x%08x%08x%08x%08x%08x\n",
+			ap_configuration->apm[0], ap_configuration->apm[1],
+			ap_configuration->apm[2], ap_configuration->apm[3],
+			ap_configuration->apm[4], ap_configuration->apm[5],
+			ap_configuration->apm[6], ap_configuration->apm[7]);
+}
+
+static BUS_ATTR_RO(ap_adapter_mask);
+
 static ssize_t ap_interrupts_show(struct bus_type *bus, char *buf)
 {
 	return snprintf(buf, PAGE_SIZE, "%d\n",
@@ -1161,7 +1203,7 @@
 {
 	int rc;
 
-	rc = process_mask_arg(buf, ap_perms.apm, AP_DEVICES, &ap_perms_mutex);
+	rc = ap_parse_mask_str(buf, ap_perms.apm, AP_DEVICES, &ap_perms_mutex);
 	if (rc)
 		return rc;
 
@@ -1192,7 +1234,7 @@
 {
 	int rc;
 
-	rc = process_mask_arg(buf, ap_perms.aqm, AP_DOMAINS, &ap_perms_mutex);
+	rc = ap_parse_mask_str(buf, ap_perms.aqm, AP_DOMAINS, &ap_perms_mutex);
 	if (rc)
 		return rc;
 
@@ -1207,6 +1249,7 @@
 	&bus_attr_ap_domain,
 	&bus_attr_ap_control_domain_mask,
 	&bus_attr_ap_usage_domain_mask,
+	&bus_attr_ap_adapter_mask,
 	&bus_attr_config_time,
 	&bus_attr_poll_thread,
 	&bus_attr_ap_interrupts,
@@ -1218,11 +1261,10 @@
 };
 
 /**
- * ap_select_domain(): Select an AP domain.
- *
- * Pick one of the 16 AP domains.
+ * ap_select_domain(): Select an AP domain if possible and we haven't
+ * already done so before.
  */
-static int ap_select_domain(void)
+static void ap_select_domain(void)
 {
 	int count, max_count, best_domain;
 	struct ap_queue_status status;
@@ -1237,12 +1279,12 @@
 	if (ap_domain_index >= 0) {
 		/* Domain has already been selected. */
 		spin_unlock_bh(&ap_domain_lock);
-		return 0;
+		return;
 	}
 	best_domain = -1;
 	max_count = 0;
 	for (i = 0; i < AP_DOMAINS; i++) {
-		if (!ap_test_config_domain(i) ||
+		if (!ap_test_config_usage_domain(i) ||
 		    !test_bit_inv(i, ap_perms.aqm))
 			continue;
 		count = 0;
@@ -1264,11 +1306,8 @@
 	if (best_domain >= 0) {
 		ap_domain_index = best_domain;
 		AP_DBF(DBF_DEBUG, "new ap_domain_index=%d\n", ap_domain_index);
-		spin_unlock_bh(&ap_domain_lock);
-		return 0;
 	}
 	spin_unlock_bh(&ap_domain_lock);
-	return -ENODEV;
 }
 
 /*
@@ -1283,24 +1322,24 @@
 	/* < CEX2A is not supported */
 	if (rawtype < AP_DEVICE_TYPE_CEX2A)
 		return 0;
-	/* up to CEX6 known and fully supported */
-	if (rawtype <= AP_DEVICE_TYPE_CEX6)
+	/* up to CEX7 known and fully supported */
+	if (rawtype <= AP_DEVICE_TYPE_CEX7)
 		return rawtype;
 	/*
-	 * unknown new type > CEX6, check for compatibility
+	 * unknown new type > CEX7, check for compatibility
 	 * to the highest known and supported type which is
-	 * currently CEX6 with the help of the QACT function.
+	 * currently CEX7 with the help of the QACT function.
 	 */
 	if (ap_qact_available()) {
 		struct ap_queue_status status;
 		union ap_qact_ap_info apinfo = {0};
 
 		apinfo.mode = (func >> 26) & 0x07;
-		apinfo.cat = AP_DEVICE_TYPE_CEX6;
+		apinfo.cat = AP_DEVICE_TYPE_CEX7;
 		status = ap_qact(qid, 0, &apinfo);
 		if (status.response_code == AP_RESPONSE_NORMAL
 		    && apinfo.cat >= AP_DEVICE_TYPE_CEX2A
-		    && apinfo.cat <= AP_DEVICE_TYPE_CEX6)
+		    && apinfo.cat <= AP_DEVICE_TYPE_CEX7)
 			comp_type = apinfo.cat;
 	}
 	if (!comp_type)
@@ -1313,166 +1352,218 @@
 }
 
 /*
- * helper function to be used with bus_find_dev
+ * Helper function to be used with bus_find_dev
  * matches for the card device with the given id
  */
-static int __match_card_device_with_id(struct device *dev, void *data)
+static int __match_card_device_with_id(struct device *dev, const void *data)
 {
-	return is_card_dev(dev) && to_ap_card(dev)->id == (int)(long) data;
+	return is_card_dev(dev) && to_ap_card(dev)->id == (int)(long)(void *) data;
 }
 
-/* helper function to be used with bus_find_dev
+/*
+ * Helper function to be used with bus_find_dev
  * matches for the queue device with a given qid
  */
-static int __match_queue_device_with_qid(struct device *dev, void *data)
+static int __match_queue_device_with_qid(struct device *dev, const void *data)
 {
 	return is_queue_dev(dev) && to_ap_queue(dev)->qid == (int)(long) data;
 }
 
+/*
+ * Helper function to be used with bus_find_dev
+ * matches any queue device with given queue id
+ */
+static int __match_queue_device_with_queue_id(struct device *dev, const void *data)
+{
+	return is_queue_dev(dev)
+		&& AP_QID_QUEUE(to_ap_queue(dev)->qid) == (int)(long) data;
+}
+
+/*
+ * Helper function for ap_scan_bus().
+ * Does the scan bus job for the given adapter id.
+ */
+static void _ap_scan_bus_adapter(int id)
+{
+	ap_qid_t qid;
+	unsigned int func;
+	struct ap_card *ac;
+	struct device *dev;
+	struct ap_queue *aq;
+	int rc, dom, depth, type, comp_type, borked;
+
+	/* check if there is a card device registered with this id */
+	dev = bus_find_device(&ap_bus_type, NULL,
+			      (void *)(long) id,
+			      __match_card_device_with_id);
+	ac = dev ? to_ap_card(dev) : NULL;
+	if (!ap_test_config_card_id(id)) {
+		if (dev) {
+			/* Card device has been removed from configuration */
+			bus_for_each_dev(&ap_bus_type, NULL,
+					 (void *)(long) id,
+					 __ap_queue_devices_with_id_unregister);
+			device_unregister(dev);
+			put_device(dev);
+		}
+		return;
+	}
+
+	/*
+	 * This card id is enabled in the configuration. If we already have
+	 * a card device with this id, check if type and functions are still
+	 * the very same. Also verify that at least one queue is available.
+	 */
+	if (ac) {
+		/* find the first valid queue */
+		for (dom = 0; dom < AP_DOMAINS; dom++) {
+			qid = AP_MKQID(id, dom);
+			if (ap_query_queue(qid, &depth, &type, &func) == 0)
+				break;
+		}
+		borked = 0;
+		if (dom >= AP_DOMAINS) {
+			/* no accessible queue on this card */
+			borked = 1;
+		} else if (ac->raw_hwtype != type) {
+			/* card type has changed */
+			AP_DBF(DBF_INFO, "card=%02x type changed.\n", id);
+			borked = 1;
+		} else if (ac->functions != func) {
+			/* card functions have changed */
+			AP_DBF(DBF_INFO, "card=%02x functions changed.\n", id);
+			borked = 1;
+		}
+		if (borked) {
+			/* unregister card device and associated queues */
+			bus_for_each_dev(&ap_bus_type, NULL,
+					 (void *)(long) id,
+					 __ap_queue_devices_with_id_unregister);
+			device_unregister(dev);
+			put_device(dev);
+			/* go back if there is no valid queue on this card */
+			if (dom >= AP_DOMAINS)
+				return;
+			ac = NULL;
+		}
+	}
+
+	/*
+	 * Go through all possible queue ids. Check and maybe create or release
+	 * queue devices for this card. If there exists no card device yet,
+	 * create a card device also.
+	 */
+	for (dom = 0; dom < AP_DOMAINS; dom++) {
+		qid = AP_MKQID(id, dom);
+		dev = bus_find_device(&ap_bus_type, NULL,
+				      (void *)(long) qid,
+				      __match_queue_device_with_qid);
+		aq = dev ? to_ap_queue(dev) : NULL;
+		if (!ap_test_config_usage_domain(dom)) {
+			if (dev) {
+				/* Queue device exists but has been
+				 * removed from configuration.
+				 */
+				device_unregister(dev);
+				put_device(dev);
+			}
+			continue;
+		}
+		/* try to fetch infos about this queue */
+		rc = ap_query_queue(qid, &depth, &type, &func);
+		if (dev) {
+			if (rc == -ENODEV)
+				borked = 1;
+			else {
+				spin_lock_bh(&aq->lock);
+				borked = aq->state == AP_STATE_BORKED;
+				spin_unlock_bh(&aq->lock);
+			}
+			if (borked) {
+				/* Remove broken device */
+				AP_DBF(DBF_DEBUG,
+				       "removing broken queue=%02x.%04x\n",
+				       id, dom);
+				device_unregister(dev);
+			}
+			put_device(dev);
+			continue;
+		}
+		if (rc)
+			continue;
+		/* a new queue device is needed, check out comp type */
+		comp_type = ap_get_compatible_type(qid, type, func);
+		if (!comp_type)
+			continue;
+		/* maybe a card device needs to be created first */
+		if (!ac) {
+			ac = ap_card_create(id, depth, type, comp_type, func);
+			if (!ac)
+				continue;
+			ac->ap_dev.device.bus = &ap_bus_type;
+			ac->ap_dev.device.parent = ap_root_device;
+			dev_set_name(&ac->ap_dev.device, "card%02x", id);
+			/* Register card device with AP bus */
+			rc = device_register(&ac->ap_dev.device);
+			if (rc) {
+				put_device(&ac->ap_dev.device);
+				ac = NULL;
+				break;
+			}
+			/* get it and thus adjust reference counter */
+			get_device(&ac->ap_dev.device);
+		}
+		/* now create the new queue device */
+		aq = ap_queue_create(qid, comp_type);
+		if (!aq)
+			continue;
+		aq->card = ac;
+		aq->ap_dev.device.bus = &ap_bus_type;
+		aq->ap_dev.device.parent = &ac->ap_dev.device;
+		dev_set_name(&aq->ap_dev.device, "%02x.%04x", id, dom);
+		/* Register queue device */
+		rc = device_register(&aq->ap_dev.device);
+		if (rc) {
+			put_device(&aq->ap_dev.device);
+			continue;
+		}
+	} /* end domain loop */
+
+	if (ac)
+		put_device(&ac->ap_dev.device);
+}
+
 /**
  * ap_scan_bus(): Scan the AP bus for new devices
  * Runs periodically, workqueue timer (ap_config_time)
  */
 static void ap_scan_bus(struct work_struct *unused)
 {
-	struct ap_queue *aq;
-	struct ap_card *ac;
-	struct device *dev;
-	ap_qid_t qid;
-	int comp_type, depth = 0, type = 0;
-	unsigned int func = 0;
-	int rc, id, dom, borked, domains, defdomdevs = 0;
+	int id;
 
 	AP_DBF(DBF_DEBUG, "%s running\n", __func__);
 
 	ap_query_configuration(ap_configuration);
-	if (ap_select_domain() != 0)
-		goto out;
+	ap_select_domain();
 
-	for (id = 0; id < AP_DEVICES; id++) {
-		/* check if device is registered */
-		dev = bus_find_device(&ap_bus_type, NULL,
-				      (void *)(long) id,
-				      __match_card_device_with_id);
-		ac = dev ? to_ap_card(dev) : NULL;
-		if (!ap_test_config_card_id(id)) {
-			if (dev) {
-				/* Card device has been removed from
-				 * configuration, remove the belonging
-				 * queue devices.
-				 */
-				bus_for_each_dev(&ap_bus_type, NULL,
-					(void *)(long) id,
-					__ap_queue_devices_with_id_unregister);
-				/* now remove the card device */
-				device_unregister(dev);
-				put_device(dev);
-			}
-			continue;
-		}
-		/* According to the configuration there should be a card
-		 * device, so check if there is at least one valid queue
-		 * and maybe create queue devices and the card device.
-		 */
-		domains = 0;
-		for (dom = 0; dom < AP_DOMAINS; dom++) {
-			qid = AP_MKQID(id, dom);
-			dev = bus_find_device(&ap_bus_type, NULL,
-					      (void *)(long) qid,
-					      __match_queue_device_with_qid);
-			aq = dev ? to_ap_queue(dev) : NULL;
-			if (!ap_test_config_domain(dom)) {
-				if (dev) {
-					/* Queue device exists but has been
-					 * removed from configuration.
-					 */
-					device_unregister(dev);
-					put_device(dev);
-				}
-				continue;
-			}
-			rc = ap_query_queue(qid, &depth, &type, &func);
-			if (dev) {
-				spin_lock_bh(&aq->lock);
-				if (rc == -ENODEV ||
-				    /* adapter reconfiguration */
-				    (ac && ac->functions != func))
-					aq->state = AP_STATE_BORKED;
-				borked = aq->state == AP_STATE_BORKED;
-				spin_unlock_bh(&aq->lock);
-				if (borked)	/* Remove broken device */
-					device_unregister(dev);
-				put_device(dev);
-				if (!borked) {
-					domains++;
-					if (dom == ap_domain_index)
-						defdomdevs++;
-					continue;
-				}
-			}
-			if (rc)
-				continue;
-			/* a new queue device is needed, check out comp type */
-			comp_type = ap_get_compatible_type(qid, type, func);
-			if (!comp_type)
-				continue;
-			/* maybe a card device needs to be created first */
-			if (!ac) {
-				ac = ap_card_create(id, depth, type,
-						    comp_type, func);
-				if (!ac)
-					continue;
-				ac->ap_dev.device.bus = &ap_bus_type;
-				ac->ap_dev.device.parent = ap_root_device;
-				dev_set_name(&ac->ap_dev.device,
-					     "card%02x", id);
-				/* Register card with AP bus */
-				rc = device_register(&ac->ap_dev.device);
-				if (rc) {
-					put_device(&ac->ap_dev.device);
-					ac = NULL;
-					break;
-				}
-				/* get it and thus adjust reference counter */
-				get_device(&ac->ap_dev.device);
-			}
-			/* now create the new queue device */
-			aq = ap_queue_create(qid, comp_type);
-			if (!aq)
-				continue;
-			aq->card = ac;
-			aq->ap_dev.device.bus = &ap_bus_type;
-			aq->ap_dev.device.parent = &ac->ap_dev.device;
-			dev_set_name(&aq->ap_dev.device,
-				     "%02x.%04x", id, dom);
-			/* Start with a device reset */
-			spin_lock_bh(&aq->lock);
-			ap_wait(ap_sm_event(aq, AP_EVENT_POLL));
-			spin_unlock_bh(&aq->lock);
-			/* Register device */
-			rc = device_register(&aq->ap_dev.device);
-			if (rc) {
-				put_device(&aq->ap_dev.device);
-				continue;
-			}
-			domains++;
-			if (dom == ap_domain_index)
-				defdomdevs++;
-		} /* end domain loop */
-		if (ac) {
-			/* remove card dev if there are no queue devices */
-			if (!domains)
-				device_unregister(&ac->ap_dev.device);
-			put_device(&ac->ap_dev.device);
-		}
-	} /* end device loop */
+	/* loop over all possible adapters */
+	for (id = 0; id < AP_DEVICES; id++)
+		_ap_scan_bus_adapter(id);
 
-	if (defdomdevs < 1)
-		AP_DBF(DBF_INFO,
-		       "no queue device with default domain %d available\n",
-		       ap_domain_index);
+	/* check if there is at least one queue available with default domain */
+	if (ap_domain_index >= 0) {
+		struct device *dev =
+			bus_find_device(&ap_bus_type, NULL,
+					(void *)(long) ap_domain_index,
+					__match_queue_device_with_queue_id);
+		if (dev)
+			put_device(dev);
+		else
+			AP_DBF(DBF_INFO,
+			       "no queue device with default domain %d available\n",
+			       ap_domain_index);
+	}
 
-out:
 	mod_timer(&ap_config_timer, jiffies + ap_config_time * HZ);
 }
 
@@ -1496,21 +1587,22 @@
 static void __init ap_perms_init(void)
 {
 	/* all resources useable if no kernel parameter string given */
+	memset(&ap_perms.ioctlm, 0xFF, sizeof(ap_perms.ioctlm));
 	memset(&ap_perms.apm, 0xFF, sizeof(ap_perms.apm));
 	memset(&ap_perms.aqm, 0xFF, sizeof(ap_perms.aqm));
 
 	/* apm kernel parameter string */
 	if (apm_str) {
 		memset(&ap_perms.apm, 0, sizeof(ap_perms.apm));
-		process_mask_arg(apm_str, ap_perms.apm, AP_DEVICES,
-				 &ap_perms_mutex);
+		ap_parse_mask_str(apm_str, ap_perms.apm, AP_DEVICES,
+				  &ap_perms_mutex);
 	}
 
 	/* aqm kernel parameter string */
 	if (aqm_str) {
 		memset(&ap_perms.aqm, 0, sizeof(ap_perms.aqm));
-		process_mask_arg(aqm_str, ap_perms.aqm, AP_DOMAINS,
-				 &ap_perms_mutex);
+		ap_parse_mask_str(aqm_str, ap_perms.aqm, AP_DOMAINS,
+				  &ap_perms_mutex);
 	}
 }
 
@@ -1533,7 +1625,7 @@
 		return -ENODEV;
 	}
 
-	/* set up the AP permissions (ap and aq masks) */
+	/* set up the AP permissions (ioctls, ap and aq masks) */
 	ap_perms_init();
 
 	/* Get AP configuration data if available */
diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h
index 5246cd8..433b7b6 100644
--- a/drivers/s390/crypto/ap_bus.h
+++ b/drivers/s390/crypto/ap_bus.h
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0+ */
 /*
- * Copyright IBM Corp. 2006, 2012
+ * Copyright IBM Corp. 2006, 2019
  * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
  *	      Martin Schwidefsky <schwidefsky@de.ibm.com>
  *	      Ralph Wuerthner <rwuerthn@de.ibm.com>
@@ -20,6 +20,7 @@
 
 #define AP_DEVICES 256		/* Number of AP devices. */
 #define AP_DOMAINS 256		/* Number of AP domains. */
+#define AP_IOCTLS  256		/* Number of ioctls. */
 #define AP_RESET_TIMEOUT (HZ*0.7)	/* Time in ticks for reset timeouts. */
 #define AP_CONFIG_TIME 30	/* Time in seconds between AP bus rescans. */
 #define AP_POLL_TIME 1		/* Time in ticks between receive polls. */
@@ -62,6 +63,7 @@
 #define AP_DEVICE_TYPE_CEX4	10
 #define AP_DEVICE_TYPE_CEX5	11
 #define AP_DEVICE_TYPE_CEX6	12
+#define AP_DEVICE_TYPE_CEX7	13
 
 /*
  * Known function facilities
@@ -90,7 +92,9 @@
 	AP_STATE_WORKING,
 	AP_STATE_QUEUE_FULL,
 	AP_STATE_SUSPEND_WAIT,
-	AP_STATE_BORKED,
+	AP_STATE_REMOVE,	/* about to be removed from driver */
+	AP_STATE_UNBOUND,	/* momentary not bound to a driver */
+	AP_STATE_BORKED,	/* broken */
 	NR_AP_STATES
 };
 
@@ -248,15 +252,28 @@
 void ap_request_timeout(struct timer_list *t);
 void ap_bus_force_rescan(void);
 
+int ap_test_config_usage_domain(unsigned int domain);
+int ap_test_config_ctrl_domain(unsigned int domain);
+
 void ap_queue_init_reply(struct ap_queue *aq, struct ap_message *ap_msg);
 struct ap_queue *ap_queue_create(ap_qid_t qid, int device_type);
+void ap_queue_prepare_remove(struct ap_queue *aq);
 void ap_queue_remove(struct ap_queue *aq);
 void ap_queue_suspend(struct ap_device *ap_dev);
 void ap_queue_resume(struct ap_device *ap_dev);
+void ap_queue_reinit_state(struct ap_queue *aq);
 
 struct ap_card *ap_card_create(int id, int queue_depth, int raw_device_type,
 			       int comp_device_type, unsigned int functions);
 
+struct ap_perms {
+	unsigned long ioctlm[BITS_TO_LONGS(AP_IOCTLS)];
+	unsigned long apm[BITS_TO_LONGS(AP_DEVICES)];
+	unsigned long aqm[BITS_TO_LONGS(AP_DOMAINS)];
+};
+extern struct ap_perms ap_perms;
+extern struct mutex ap_perms_mutex;
+
 /*
  * check APQN for owned/reserved by ap bus and default driver(s).
  * Checks if this APQN is or will be in use by the ap bus
@@ -280,4 +297,20 @@
 int ap_apqn_in_matrix_owned_by_def_drv(unsigned long *apm,
 				       unsigned long *aqm);
 
+/*
+ * ap_parse_mask_str() - helper function to parse a bitmap string
+ * and clear/set the bits in the bitmap accordingly. The string may be
+ * given as absolute value, a hex string like 0x1F2E3D4C5B6A" simple
+ * overwriting the current content of the bitmap. Or as relative string
+ * like "+1-16,-32,-0x40,+128" where only single bits or ranges of
+ * bits are cleared or set. Distinction is done based on the very
+ * first character which may be '+' or '-' for the relative string
+ * and othewise assume to be an absolute value string. If parsing fails
+ * a negative errno value is returned. All arguments and bitmaps are
+ * big endian order.
+ */
+int ap_parse_mask_str(const char *str,
+		      unsigned long *bitmap, int bits,
+		      struct mutex *lock);
+
 #endif /* _AP_BUS_H_ */
diff --git a/drivers/s390/crypto/ap_queue.c b/drivers/s390/crypto/ap_queue.c
index 66f7334..dad2be3 100644
--- a/drivers/s390/crypto/ap_queue.c
+++ b/drivers/s390/crypto/ap_queue.c
@@ -14,6 +14,9 @@
 #include <asm/facility.h>
 
 #include "ap_bus.h"
+#include "ap_debug.h"
+
+static void __ap_flush_queue(struct ap_queue *aq);
 
 /**
  * ap_queue_enable_interruption(): Enable interruption on an AP queue.
@@ -149,6 +152,7 @@
 			ap_msg->receive(aq, ap_msg, aq->reply);
 			break;
 		}
+		/* fall through */
 	case AP_RESPONSE_NO_PENDING_REPLY:
 		if (!status.queue_empty || aq->queue_count <= 0)
 			break;
@@ -417,6 +421,14 @@
 		[AP_EVENT_POLL] = ap_sm_suspend_read,
 		[AP_EVENT_TIMEOUT] = ap_sm_nop,
 	},
+	[AP_STATE_REMOVE] = {
+		[AP_EVENT_POLL] = ap_sm_nop,
+		[AP_EVENT_TIMEOUT] = ap_sm_nop,
+	},
+	[AP_STATE_UNBOUND] = {
+		[AP_EVENT_POLL] = ap_sm_nop,
+		[AP_EVENT_TIMEOUT] = ap_sm_nop,
+	},
 	[AP_STATE_BORKED] = {
 		[AP_EVENT_POLL] = ap_sm_nop,
 		[AP_EVENT_TIMEOUT] = ap_sm_nop,
@@ -541,7 +553,25 @@
 	return rc;
 }
 
-static DEVICE_ATTR_RO(reset);
+static ssize_t reset_store(struct device *dev,
+			   struct device_attribute *attr,
+			   const char *buf, size_t count)
+{
+	struct ap_queue *aq = to_ap_queue(dev);
+
+	spin_lock_bh(&aq->lock);
+	__ap_flush_queue(aq);
+	aq->state = AP_STATE_RESET_START;
+	ap_wait(ap_sm_event(aq, AP_EVENT_POLL));
+	spin_unlock_bh(&aq->lock);
+
+	AP_DBF(DBF_INFO, "reset queue=%02x.%04x triggered by user\n",
+	       AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid));
+
+	return count;
+}
+
+static DEVICE_ATTR_RW(reset);
 
 static ssize_t interrupt_show(struct device *dev,
 			      struct device_attribute *attr, char *buf)
@@ -704,6 +734,7 @@
 		ap_msg->rc = -EAGAIN;
 		ap_msg->receive(aq, ap_msg, NULL);
 	}
+	aq->queue_count = 0;
 }
 
 void ap_flush_queue(struct ap_queue *aq)
@@ -714,9 +745,36 @@
 }
 EXPORT_SYMBOL(ap_flush_queue);
 
-void ap_queue_remove(struct ap_queue *aq)
+void ap_queue_prepare_remove(struct ap_queue *aq)
 {
-	ap_flush_queue(aq);
+	spin_lock_bh(&aq->lock);
+	/* flush queue */
+	__ap_flush_queue(aq);
+	/* set REMOVE state to prevent new messages are queued in */
+	aq->state = AP_STATE_REMOVE;
+	spin_unlock_bh(&aq->lock);
 	del_timer_sync(&aq->timeout);
 }
-EXPORT_SYMBOL(ap_queue_remove);
+
+void ap_queue_remove(struct ap_queue *aq)
+{
+	/*
+	 * all messages have been flushed and the state is
+	 * AP_STATE_REMOVE. Now reset with zero which also
+	 * clears the irq registration and move the state
+	 * to AP_STATE_UNBOUND to signal that this queue
+	 * is not used by any driver currently.
+	 */
+	spin_lock_bh(&aq->lock);
+	ap_zapq(aq->qid);
+	aq->state = AP_STATE_UNBOUND;
+	spin_unlock_bh(&aq->lock);
+}
+
+void ap_queue_reinit_state(struct ap_queue *aq)
+{
+	spin_lock_bh(&aq->lock);
+	aq->state = AP_STATE_RESET_START;
+	ap_wait(ap_sm_event(aq, AP_EVENT_POLL));
+	spin_unlock_bh(&aq->lock);
+}
diff --git a/drivers/s390/crypto/pkey_api.c b/drivers/s390/crypto/pkey_api.c
index 1b4001e..9de3d46 100644
--- a/drivers/s390/crypto/pkey_api.c
+++ b/drivers/s390/crypto/pkey_api.c
@@ -2,7 +2,7 @@
 /*
  *  pkey device driver
  *
- *  Copyright IBM Corp. 2017
+ *  Copyright IBM Corp. 2017,2019
  *  Author(s): Harald Freudenberger
  */
 
@@ -16,21 +16,25 @@
 #include <linux/slab.h>
 #include <linux/kallsyms.h>
 #include <linux/debugfs.h>
+#include <linux/random.h>
+#include <linux/cpufeature.h>
 #include <asm/zcrypt.h>
 #include <asm/cpacf.h>
 #include <asm/pkey.h>
+#include <crypto/aes.h>
 
 #include "zcrypt_api.h"
+#include "zcrypt_ccamisc.h"
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("IBM Corporation");
 MODULE_DESCRIPTION("s390 protected key interface");
 
-/* Size of parameter block used for all cca requests/replies */
-#define PARMBSIZE 512
+#define KEYBLOBBUFSIZE 8192  /* key buffer size used for internal processing */
+#define MAXAPQNSINLIST 64    /* max 64 apqns within a apqn list */
 
-/* Size of vardata block used for some of the cca requests/replies */
-#define VARDATASIZE 4096
+/* mask of available pckmo subfunctions, fetched once at module init */
+static cpacf_mask_t pckmo_functions;
 
 /*
  * debug feature data and functions
@@ -45,7 +49,8 @@
 
 static void __init pkey_debug_init(void)
 {
-	debug_info = debug_register("pkey", 1, 1, 4 * sizeof(long));
+	/* 5 arguments per dbf entry (including the format string ptr) */
+	debug_info = debug_register("pkey", 1, 1, 5 * sizeof(long));
 	debug_register_view(debug_info, &debug_sprintf_view);
 	debug_set_level(debug_info, 3);
 }
@@ -55,574 +60,23 @@
 	debug_unregister(debug_info);
 }
 
-/* inside view of a secure key token (only type 0x01 version 0x04) */
-struct secaeskeytoken {
-	u8  type;     /* 0x01 for internal key token */
+/* inside view of a protected key token (only type 0x00 version 0x01) */
+struct protaeskeytoken {
+	u8  type;     /* 0x00 for PAES specific key tokens */
 	u8  res0[3];
-	u8  version;  /* should be 0x04 */
-	u8  res1[1];
-	u8  flag;     /* key flags */
-	u8  res2[1];
-	u64 mkvp;     /* master key verification pattern */
-	u8  key[32];  /* key value (encrypted) */
-	u8  cv[8];    /* control vector */
-	u16 bitsize;  /* key bit size */
-	u16 keysize;  /* key byte size */
-	u8  tvv[4];   /* token validation value */
+	u8  version;  /* should be 0x01 for protected AES key token */
+	u8  res1[3];
+	u32 keytype;  /* key type, one of the PKEY_KEYTYPE values */
+	u32 len;      /* bytes actually stored in protkey[] */
+	u8  protkey[MAXPROTKEYSIZE]; /* the protected key blob */
 } __packed;
 
 /*
- * Simple check if the token is a valid CCA secure AES key
- * token. If keybitsize is given, the bitsize of the key is
- * also checked. Returns 0 on success or errno value on failure.
- */
-static int check_secaeskeytoken(const u8 *token, int keybitsize)
-{
-	struct secaeskeytoken *t = (struct secaeskeytoken *) token;
-
-	if (t->type != 0x01) {
-		DEBUG_ERR(
-			"%s secure token check failed, type mismatch 0x%02x != 0x01\n",
-			__func__, (int) t->type);
-		return -EINVAL;
-	}
-	if (t->version != 0x04) {
-		DEBUG_ERR(
-			"%s secure token check failed, version mismatch 0x%02x != 0x04\n",
-			__func__, (int) t->version);
-		return -EINVAL;
-	}
-	if (keybitsize > 0 && t->bitsize != keybitsize) {
-		DEBUG_ERR(
-			"%s secure token check failed, bitsize mismatch %d != %d\n",
-			__func__, (int) t->bitsize, keybitsize);
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
-/*
- * Allocate consecutive memory for request CPRB, request param
- * block, reply CPRB and reply param block and fill in values
- * for the common fields. Returns 0 on success or errno value
- * on failure.
- */
-static int alloc_and_prep_cprbmem(size_t paramblen,
-				  u8 **pcprbmem,
-				  struct CPRBX **preqCPRB,
-				  struct CPRBX **prepCPRB)
-{
-	u8 *cprbmem;
-	size_t cprbplusparamblen = sizeof(struct CPRBX) + paramblen;
-	struct CPRBX *preqcblk, *prepcblk;
-
-	/*
-	 * allocate consecutive memory for request CPRB, request param
-	 * block, reply CPRB and reply param block
-	 */
-	cprbmem = kcalloc(2, cprbplusparamblen, GFP_KERNEL);
-	if (!cprbmem)
-		return -ENOMEM;
-
-	preqcblk = (struct CPRBX *) cprbmem;
-	prepcblk = (struct CPRBX *) (cprbmem + cprbplusparamblen);
-
-	/* fill request cprb struct */
-	preqcblk->cprb_len = sizeof(struct CPRBX);
-	preqcblk->cprb_ver_id = 0x02;
-	memcpy(preqcblk->func_id, "T2", 2);
-	preqcblk->rpl_msgbl = cprbplusparamblen;
-	if (paramblen) {
-		preqcblk->req_parmb =
-			((u8 *) preqcblk) + sizeof(struct CPRBX);
-		preqcblk->rpl_parmb =
-			((u8 *) prepcblk) + sizeof(struct CPRBX);
-	}
-
-	*pcprbmem = cprbmem;
-	*preqCPRB = preqcblk;
-	*prepCPRB = prepcblk;
-
-	return 0;
-}
-
-/*
- * Free the cprb memory allocated with the function above.
- * If the scrub value is not zero, the memory is filled
- * with zeros before freeing (useful if there was some
- * clear key material in there).
- */
-static void free_cprbmem(void *mem, size_t paramblen, int scrub)
-{
-	if (scrub)
-		memzero_explicit(mem, 2 * (sizeof(struct CPRBX) + paramblen));
-	kfree(mem);
-}
-
-/*
- * Helper function to prepare the xcrb struct
- */
-static inline void prep_xcrb(struct ica_xcRB *pxcrb,
-			     u16 cardnr,
-			     struct CPRBX *preqcblk,
-			     struct CPRBX *prepcblk)
-{
-	memset(pxcrb, 0, sizeof(*pxcrb));
-	pxcrb->agent_ID = 0x4341; /* 'CA' */
-	pxcrb->user_defined = (cardnr == 0xFFFF ? AUTOSELECT : cardnr);
-	pxcrb->request_control_blk_length =
-		preqcblk->cprb_len + preqcblk->req_parml;
-	pxcrb->request_control_blk_addr = (void __user *) preqcblk;
-	pxcrb->reply_control_blk_length = preqcblk->rpl_msgbl;
-	pxcrb->reply_control_blk_addr = (void __user *) prepcblk;
-}
-
-/*
- * Helper function which calls zcrypt_send_cprb with
- * memory management segment adjusted to kernel space
- * so that the copy_from_user called within this
- * function do in fact copy from kernel space.
- */
-static inline int _zcrypt_send_cprb(struct ica_xcRB *xcrb)
-{
-	int rc;
-	mm_segment_t old_fs = get_fs();
-
-	set_fs(KERNEL_DS);
-	rc = zcrypt_send_cprb(xcrb);
-	set_fs(old_fs);
-
-	return rc;
-}
-
-/*
- * Generate (random) AES secure key.
- */
-int pkey_genseckey(u16 cardnr, u16 domain,
-		   u32 keytype, struct pkey_seckey *seckey)
-{
-	int i, rc, keysize;
-	int seckeysize;
-	u8 *mem;
-	struct CPRBX *preqcblk, *prepcblk;
-	struct ica_xcRB xcrb;
-	struct kgreqparm {
-		u8  subfunc_code[2];
-		u16 rule_array_len;
-		struct lv1 {
-			u16 len;
-			char  key_form[8];
-			char  key_length[8];
-			char  key_type1[8];
-			char  key_type2[8];
-		} lv1;
-		struct lv2 {
-			u16 len;
-			struct keyid {
-				u16 len;
-				u16 attr;
-				u8  data[SECKEYBLOBSIZE];
-			} keyid[6];
-		} lv2;
-	} *preqparm;
-	struct kgrepparm {
-		u8  subfunc_code[2];
-		u16 rule_array_len;
-		struct lv3 {
-			u16 len;
-			u16 keyblocklen;
-			struct {
-				u16 toklen;
-				u16 tokattr;
-				u8  tok[0];
-				/* ... some more data ... */
-			} keyblock;
-		} lv3;
-	} *prepparm;
-
-	/* get already prepared memory for 2 cprbs with param block each */
-	rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem, &preqcblk, &prepcblk);
-	if (rc)
-		return rc;
-
-	/* fill request cprb struct */
-	preqcblk->domain = domain;
-
-	/* fill request cprb param block with KG request */
-	preqparm = (struct kgreqparm *) preqcblk->req_parmb;
-	memcpy(preqparm->subfunc_code, "KG", 2);
-	preqparm->rule_array_len = sizeof(preqparm->rule_array_len);
-	preqparm->lv1.len = sizeof(struct lv1);
-	memcpy(preqparm->lv1.key_form,	 "OP      ", 8);
-	switch (keytype) {
-	case PKEY_KEYTYPE_AES_128:
-		keysize = 16;
-		memcpy(preqparm->lv1.key_length, "KEYLN16 ", 8);
-		break;
-	case PKEY_KEYTYPE_AES_192:
-		keysize = 24;
-		memcpy(preqparm->lv1.key_length, "KEYLN24 ", 8);
-		break;
-	case PKEY_KEYTYPE_AES_256:
-		keysize = 32;
-		memcpy(preqparm->lv1.key_length, "KEYLN32 ", 8);
-		break;
-	default:
-		DEBUG_ERR(
-			"%s unknown/unsupported keytype %d\n",
-			__func__, keytype);
-		rc = -EINVAL;
-		goto out;
-	}
-	memcpy(preqparm->lv1.key_type1,  "AESDATA ", 8);
-	preqparm->lv2.len = sizeof(struct lv2);
-	for (i = 0; i < 6; i++) {
-		preqparm->lv2.keyid[i].len = sizeof(struct keyid);
-		preqparm->lv2.keyid[i].attr = (i == 2 ? 0x30 : 0x10);
-	}
-	preqcblk->req_parml = sizeof(struct kgreqparm);
-
-	/* fill xcrb struct */
-	prep_xcrb(&xcrb, cardnr, preqcblk, prepcblk);
-
-	/* forward xcrb with request CPRB and reply CPRB to zcrypt dd */
-	rc = _zcrypt_send_cprb(&xcrb);
-	if (rc) {
-		DEBUG_ERR(
-			"%s zcrypt_send_cprb (cardnr=%d domain=%d) failed with errno %d\n",
-			__func__, (int) cardnr, (int) domain, rc);
-		goto out;
-	}
-
-	/* check response returncode and reasoncode */
-	if (prepcblk->ccp_rtcode != 0) {
-		DEBUG_ERR(
-			"%s secure key generate failure, card response %d/%d\n",
-			__func__,
-			(int) prepcblk->ccp_rtcode,
-			(int) prepcblk->ccp_rscode);
-		rc = -EIO;
-		goto out;
-	}
-
-	/* process response cprb param block */
-	prepcblk->rpl_parmb = ((u8 *) prepcblk) + sizeof(struct CPRBX);
-	prepparm = (struct kgrepparm *) prepcblk->rpl_parmb;
-
-	/* check length of the returned secure key token */
-	seckeysize = prepparm->lv3.keyblock.toklen
-		- sizeof(prepparm->lv3.keyblock.toklen)
-		- sizeof(prepparm->lv3.keyblock.tokattr);
-	if (seckeysize != SECKEYBLOBSIZE) {
-		DEBUG_ERR(
-			"%s secure token size mismatch %d != %d bytes\n",
-			__func__, seckeysize, SECKEYBLOBSIZE);
-		rc = -EIO;
-		goto out;
-	}
-
-	/* check secure key token */
-	rc = check_secaeskeytoken(prepparm->lv3.keyblock.tok, 8*keysize);
-	if (rc) {
-		rc = -EIO;
-		goto out;
-	}
-
-	/* copy the generated secure key token */
-	memcpy(seckey->seckey, prepparm->lv3.keyblock.tok, SECKEYBLOBSIZE);
-
-out:
-	free_cprbmem(mem, PARMBSIZE, 0);
-	return rc;
-}
-EXPORT_SYMBOL(pkey_genseckey);
-
-/*
- * Generate an AES secure key with given key value.
- */
-int pkey_clr2seckey(u16 cardnr, u16 domain, u32 keytype,
-		    const struct pkey_clrkey *clrkey,
-		    struct pkey_seckey *seckey)
-{
-	int rc, keysize, seckeysize;
-	u8 *mem;
-	struct CPRBX *preqcblk, *prepcblk;
-	struct ica_xcRB xcrb;
-	struct cmreqparm {
-		u8  subfunc_code[2];
-		u16 rule_array_len;
-		char  rule_array[8];
-		struct lv1 {
-			u16 len;
-			u8  clrkey[0];
-		} lv1;
-		struct lv2 {
-			u16 len;
-			struct keyid {
-				u16 len;
-				u16 attr;
-				u8  data[SECKEYBLOBSIZE];
-			} keyid;
-		} lv2;
-	} *preqparm;
-	struct lv2 *plv2;
-	struct cmrepparm {
-		u8  subfunc_code[2];
-		u16 rule_array_len;
-		struct lv3 {
-			u16 len;
-			u16 keyblocklen;
-			struct {
-				u16 toklen;
-				u16 tokattr;
-				u8  tok[0];
-				/* ... some more data ... */
-			} keyblock;
-		} lv3;
-	} *prepparm;
-
-	/* get already prepared memory for 2 cprbs with param block each */
-	rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem, &preqcblk, &prepcblk);
-	if (rc)
-		return rc;
-
-	/* fill request cprb struct */
-	preqcblk->domain = domain;
-
-	/* fill request cprb param block with CM request */
-	preqparm = (struct cmreqparm *) preqcblk->req_parmb;
-	memcpy(preqparm->subfunc_code, "CM", 2);
-	memcpy(preqparm->rule_array, "AES     ", 8);
-	preqparm->rule_array_len =
-		sizeof(preqparm->rule_array_len) + sizeof(preqparm->rule_array);
-	switch (keytype) {
-	case PKEY_KEYTYPE_AES_128:
-		keysize = 16;
-		break;
-	case PKEY_KEYTYPE_AES_192:
-		keysize = 24;
-		break;
-	case PKEY_KEYTYPE_AES_256:
-		keysize = 32;
-		break;
-	default:
-		DEBUG_ERR(
-			"%s unknown/unsupported keytype %d\n",
-			__func__, keytype);
-		rc = -EINVAL;
-		goto out;
-	}
-	preqparm->lv1.len = sizeof(struct lv1) + keysize;
-	memcpy(preqparm->lv1.clrkey, clrkey->clrkey, keysize);
-	plv2 = (struct lv2 *) (((u8 *) &preqparm->lv2) + keysize);
-	plv2->len = sizeof(struct lv2);
-	plv2->keyid.len = sizeof(struct keyid);
-	plv2->keyid.attr = 0x30;
-	preqcblk->req_parml = sizeof(struct cmreqparm) + keysize;
-
-	/* fill xcrb struct */
-	prep_xcrb(&xcrb, cardnr, preqcblk, prepcblk);
-
-	/* forward xcrb with request CPRB and reply CPRB to zcrypt dd */
-	rc = _zcrypt_send_cprb(&xcrb);
-	if (rc) {
-		DEBUG_ERR(
-			"%s zcrypt_send_cprb (cardnr=%d domain=%d) failed with errno %d\n",
-			__func__, (int) cardnr, (int) domain, rc);
-		goto out;
-	}
-
-	/* check response returncode and reasoncode */
-	if (prepcblk->ccp_rtcode != 0) {
-		DEBUG_ERR(
-			"%s clear key import failure, card response %d/%d\n",
-			__func__,
-			(int) prepcblk->ccp_rtcode,
-			(int) prepcblk->ccp_rscode);
-		rc = -EIO;
-		goto out;
-	}
-
-	/* process response cprb param block */
-	prepcblk->rpl_parmb = ((u8 *) prepcblk) + sizeof(struct CPRBX);
-	prepparm = (struct cmrepparm *) prepcblk->rpl_parmb;
-
-	/* check length of the returned secure key token */
-	seckeysize = prepparm->lv3.keyblock.toklen
-		- sizeof(prepparm->lv3.keyblock.toklen)
-		- sizeof(prepparm->lv3.keyblock.tokattr);
-	if (seckeysize != SECKEYBLOBSIZE) {
-		DEBUG_ERR(
-			"%s secure token size mismatch %d != %d bytes\n",
-			__func__, seckeysize, SECKEYBLOBSIZE);
-		rc = -EIO;
-		goto out;
-	}
-
-	/* check secure key token */
-	rc = check_secaeskeytoken(prepparm->lv3.keyblock.tok, 8*keysize);
-	if (rc) {
-		rc = -EIO;
-		goto out;
-	}
-
-	/* copy the generated secure key token */
-	memcpy(seckey->seckey, prepparm->lv3.keyblock.tok, SECKEYBLOBSIZE);
-
-out:
-	free_cprbmem(mem, PARMBSIZE, 1);
-	return rc;
-}
-EXPORT_SYMBOL(pkey_clr2seckey);
-
-/*
- * Derive a proteced key from the secure key blob.
- */
-int pkey_sec2protkey(u16 cardnr, u16 domain,
-		     const struct pkey_seckey *seckey,
-		     struct pkey_protkey *protkey)
-{
-	int rc;
-	u8 *mem;
-	struct CPRBX *preqcblk, *prepcblk;
-	struct ica_xcRB xcrb;
-	struct uskreqparm {
-		u8  subfunc_code[2];
-		u16 rule_array_len;
-		struct lv1 {
-			u16 len;
-			u16 attr_len;
-			u16 attr_flags;
-		} lv1;
-		struct lv2 {
-			u16 len;
-			u16 attr_len;
-			u16 attr_flags;
-			u8  token[0];	      /* cca secure key token */
-		} lv2 __packed;
-	} *preqparm;
-	struct uskrepparm {
-		u8  subfunc_code[2];
-		u16 rule_array_len;
-		struct lv3 {
-			u16 len;
-			u16 attr_len;
-			u16 attr_flags;
-			struct cpacfkeyblock {
-				u8  version;  /* version of this struct */
-				u8  flags[2];
-				u8  algo;
-				u8  form;
-				u8  pad1[3];
-				u16 keylen;
-				u8  key[64];  /* the key (keylen bytes) */
-				u16 keyattrlen;
-				u8  keyattr[32];
-				u8  pad2[1];
-				u8  vptype;
-				u8  vp[32];  /* verification pattern */
-			} keyblock;
-		} lv3 __packed;
-	} *prepparm;
-
-	/* get already prepared memory for 2 cprbs with param block each */
-	rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem, &preqcblk, &prepcblk);
-	if (rc)
-		return rc;
-
-	/* fill request cprb struct */
-	preqcblk->domain = domain;
-
-	/* fill request cprb param block with USK request */
-	preqparm = (struct uskreqparm *) preqcblk->req_parmb;
-	memcpy(preqparm->subfunc_code, "US", 2);
-	preqparm->rule_array_len = sizeof(preqparm->rule_array_len);
-	preqparm->lv1.len = sizeof(struct lv1);
-	preqparm->lv1.attr_len = sizeof(struct lv1) - sizeof(preqparm->lv1.len);
-	preqparm->lv1.attr_flags = 0x0001;
-	preqparm->lv2.len = sizeof(struct lv2) + SECKEYBLOBSIZE;
-	preqparm->lv2.attr_len = sizeof(struct lv2)
-		- sizeof(preqparm->lv2.len) + SECKEYBLOBSIZE;
-	preqparm->lv2.attr_flags = 0x0000;
-	memcpy(preqparm->lv2.token, seckey->seckey, SECKEYBLOBSIZE);
-	preqcblk->req_parml = sizeof(struct uskreqparm) + SECKEYBLOBSIZE;
-
-	/* fill xcrb struct */
-	prep_xcrb(&xcrb, cardnr, preqcblk, prepcblk);
-
-	/* forward xcrb with request CPRB and reply CPRB to zcrypt dd */
-	rc = _zcrypt_send_cprb(&xcrb);
-	if (rc) {
-		DEBUG_ERR(
-			"%s zcrypt_send_cprb (cardnr=%d domain=%d) failed with errno %d\n",
-			__func__, (int) cardnr, (int) domain, rc);
-		goto out;
-	}
-
-	/* check response returncode and reasoncode */
-	if (prepcblk->ccp_rtcode != 0) {
-		DEBUG_ERR(
-			"%s unwrap secure key failure, card response %d/%d\n",
-			__func__,
-			(int) prepcblk->ccp_rtcode,
-			(int) prepcblk->ccp_rscode);
-		rc = -EIO;
-		goto out;
-	}
-	if (prepcblk->ccp_rscode != 0) {
-		DEBUG_WARN(
-			"%s unwrap secure key warning, card response %d/%d\n",
-			__func__,
-			(int) prepcblk->ccp_rtcode,
-			(int) prepcblk->ccp_rscode);
-	}
-
-	/* process response cprb param block */
-	prepcblk->rpl_parmb = ((u8 *) prepcblk) + sizeof(struct CPRBX);
-	prepparm = (struct uskrepparm *) prepcblk->rpl_parmb;
-
-	/* check the returned keyblock */
-	if (prepparm->lv3.keyblock.version != 0x01) {
-		DEBUG_ERR(
-			"%s reply param keyblock version mismatch 0x%02x != 0x01\n",
-			__func__, (int) prepparm->lv3.keyblock.version);
-		rc = -EIO;
-		goto out;
-	}
-
-	/* copy the tanslated protected key */
-	switch (prepparm->lv3.keyblock.keylen) {
-	case 16+32:
-		protkey->type = PKEY_KEYTYPE_AES_128;
-		break;
-	case 24+32:
-		protkey->type = PKEY_KEYTYPE_AES_192;
-		break;
-	case 32+32:
-		protkey->type = PKEY_KEYTYPE_AES_256;
-		break;
-	default:
-		DEBUG_ERR("%s unknown/unsupported keytype %d\n",
-			  __func__, prepparm->lv3.keyblock.keylen);
-		rc = -EIO;
-		goto out;
-	}
-	protkey->len = prepparm->lv3.keyblock.keylen;
-	memcpy(protkey->protkey, prepparm->lv3.keyblock.key, protkey->len);
-
-out:
-	free_cprbmem(mem, PARMBSIZE, 0);
-	return rc;
-}
-EXPORT_SYMBOL(pkey_sec2protkey);
-
-/*
  * Create a protected key from a clear key value.
  */
-int pkey_clr2protkey(u32 keytype,
-		     const struct pkey_clrkey *clrkey,
-		     struct pkey_protkey *protkey)
+static int pkey_clr2protkey(u32 keytype,
+			    const struct pkey_clrkey *clrkey,
+			    struct pkey_protkey *protkey)
 {
 	long fc;
 	int keysize;
@@ -647,6 +101,16 @@
 		return -EINVAL;
 	}
 
+	/*
+	 * Check if the needed pckmo subfunction is available.
+	 * These subfunctions can be enabled/disabled by customers
+	 * in the LPAR profile or may even change on the fly.
+	 */
+	if (!cpacf_test_func(&pckmo_functions, fc)) {
+		DEBUG_ERR("%s pckmo functions not available\n", __func__);
+		return -ENODEV;
+	}
+
 	/* prepare param block */
 	memset(paramblock, 0, sizeof(paramblock));
 	memcpy(paramblock, clrkey->clrkey, keysize);
@@ -661,338 +125,43 @@
 
 	return 0;
 }
-EXPORT_SYMBOL(pkey_clr2protkey);
-
-/*
- * query cryptographic facility from adapter
- */
-static int query_crypto_facility(u16 cardnr, u16 domain,
-				 const char *keyword,
-				 u8 *rarray, size_t *rarraylen,
-				 u8 *varray, size_t *varraylen)
-{
-	int rc;
-	u16 len;
-	u8 *mem, *ptr;
-	struct CPRBX *preqcblk, *prepcblk;
-	struct ica_xcRB xcrb;
-	struct fqreqparm {
-		u8  subfunc_code[2];
-		u16 rule_array_len;
-		char  rule_array[8];
-		struct lv1 {
-			u16 len;
-			u8  data[VARDATASIZE];
-		} lv1;
-		u16 dummylen;
-	} *preqparm;
-	size_t parmbsize = sizeof(struct fqreqparm);
-	struct fqrepparm {
-		u8  subfunc_code[2];
-		u8  lvdata[0];
-	} *prepparm;
-
-	/* get already prepared memory for 2 cprbs with param block each */
-	rc = alloc_and_prep_cprbmem(parmbsize, &mem, &preqcblk, &prepcblk);
-	if (rc)
-		return rc;
-
-	/* fill request cprb struct */
-	preqcblk->domain = domain;
-
-	/* fill request cprb param block with FQ request */
-	preqparm = (struct fqreqparm *) preqcblk->req_parmb;
-	memcpy(preqparm->subfunc_code, "FQ", 2);
-	memcpy(preqparm->rule_array, keyword, sizeof(preqparm->rule_array));
-	preqparm->rule_array_len =
-		sizeof(preqparm->rule_array_len) + sizeof(preqparm->rule_array);
-	preqparm->lv1.len = sizeof(preqparm->lv1);
-	preqparm->dummylen = sizeof(preqparm->dummylen);
-	preqcblk->req_parml = parmbsize;
-
-	/* fill xcrb struct */
-	prep_xcrb(&xcrb, cardnr, preqcblk, prepcblk);
-
-	/* forward xcrb with request CPRB and reply CPRB to zcrypt dd */
-	rc = _zcrypt_send_cprb(&xcrb);
-	if (rc) {
-		DEBUG_ERR(
-			"%s zcrypt_send_cprb (cardnr=%d domain=%d) failed with errno %d\n",
-			__func__, (int) cardnr, (int) domain, rc);
-		goto out;
-	}
-
-	/* check response returncode and reasoncode */
-	if (prepcblk->ccp_rtcode != 0) {
-		DEBUG_ERR(
-			"%s unwrap secure key failure, card response %d/%d\n",
-			__func__,
-			(int) prepcblk->ccp_rtcode,
-			(int) prepcblk->ccp_rscode);
-		rc = -EIO;
-		goto out;
-	}
-
-	/* process response cprb param block */
-	prepcblk->rpl_parmb = ((u8 *) prepcblk) + sizeof(struct CPRBX);
-	prepparm = (struct fqrepparm *) prepcblk->rpl_parmb;
-	ptr = prepparm->lvdata;
-
-	/* check and possibly copy reply rule array */
-	len = *((u16 *) ptr);
-	if (len > sizeof(u16)) {
-		ptr += sizeof(u16);
-		len -= sizeof(u16);
-		if (rarray && rarraylen && *rarraylen > 0) {
-			*rarraylen = (len > *rarraylen ? *rarraylen : len);
-			memcpy(rarray, ptr, *rarraylen);
-		}
-		ptr += len;
-	}
-	/* check and possible copy reply var array */
-	len = *((u16 *) ptr);
-	if (len > sizeof(u16)) {
-		ptr += sizeof(u16);
-		len -= sizeof(u16);
-		if (varray && varraylen && *varraylen > 0) {
-			*varraylen = (len > *varraylen ? *varraylen : len);
-			memcpy(varray, ptr, *varraylen);
-		}
-		ptr += len;
-	}
-
-out:
-	free_cprbmem(mem, parmbsize, 0);
-	return rc;
-}
-
-/*
- * Fetch the current and old mkvp values via
- * query_crypto_facility from adapter.
- */
-static int fetch_mkvp(u16 cardnr, u16 domain, u64 mkvp[2])
-{
-	int rc, found = 0;
-	size_t rlen, vlen;
-	u8 *rarray, *varray, *pg;
-
-	pg = (u8 *) __get_free_page(GFP_KERNEL);
-	if (!pg)
-		return -ENOMEM;
-	rarray = pg;
-	varray = pg + PAGE_SIZE/2;
-	rlen = vlen = PAGE_SIZE/2;
-
-	rc = query_crypto_facility(cardnr, domain, "STATICSA",
-				   rarray, &rlen, varray, &vlen);
-	if (rc == 0 && rlen > 8*8 && vlen > 184+8) {
-		if (rarray[8*8] == '2') {
-			/* current master key state is valid */
-			mkvp[0] = *((u64 *)(varray + 184));
-			mkvp[1] = *((u64 *)(varray + 172));
-			found = 1;
-		}
-	}
-
-	free_page((unsigned long) pg);
-
-	return found ? 0 : -ENOENT;
-}
-
-/* struct to hold cached mkvp info for each card/domain */
-struct mkvp_info {
-	struct list_head list;
-	u16 cardnr;
-	u16 domain;
-	u64 mkvp[2];
-};
-
-/* a list with mkvp_info entries */
-static LIST_HEAD(mkvp_list);
-static DEFINE_SPINLOCK(mkvp_list_lock);
-
-static int mkvp_cache_fetch(u16 cardnr, u16 domain, u64 mkvp[2])
-{
-	int rc = -ENOENT;
-	struct mkvp_info *ptr;
-
-	spin_lock_bh(&mkvp_list_lock);
-	list_for_each_entry(ptr, &mkvp_list, list) {
-		if (ptr->cardnr == cardnr &&
-		    ptr->domain == domain) {
-			memcpy(mkvp, ptr->mkvp, 2 * sizeof(u64));
-			rc = 0;
-			break;
-		}
-	}
-	spin_unlock_bh(&mkvp_list_lock);
-
-	return rc;
-}
-
-static void mkvp_cache_update(u16 cardnr, u16 domain, u64 mkvp[2])
-{
-	int found = 0;
-	struct mkvp_info *ptr;
-
-	spin_lock_bh(&mkvp_list_lock);
-	list_for_each_entry(ptr, &mkvp_list, list) {
-		if (ptr->cardnr == cardnr &&
-		    ptr->domain == domain) {
-			memcpy(ptr->mkvp, mkvp, 2 * sizeof(u64));
-			found = 1;
-			break;
-		}
-	}
-	if (!found) {
-		ptr = kmalloc(sizeof(*ptr), GFP_ATOMIC);
-		if (!ptr) {
-			spin_unlock_bh(&mkvp_list_lock);
-			return;
-		}
-		ptr->cardnr = cardnr;
-		ptr->domain = domain;
-		memcpy(ptr->mkvp, mkvp, 2 * sizeof(u64));
-		list_add(&ptr->list, &mkvp_list);
-	}
-	spin_unlock_bh(&mkvp_list_lock);
-}
-
-static void mkvp_cache_scrub(u16 cardnr, u16 domain)
-{
-	struct mkvp_info *ptr;
-
-	spin_lock_bh(&mkvp_list_lock);
-	list_for_each_entry(ptr, &mkvp_list, list) {
-		if (ptr->cardnr == cardnr &&
-		    ptr->domain == domain) {
-			list_del(&ptr->list);
-			kfree(ptr);
-			break;
-		}
-	}
-	spin_unlock_bh(&mkvp_list_lock);
-}
-
-static void __exit mkvp_cache_free(void)
-{
-	struct mkvp_info *ptr, *pnext;
-
-	spin_lock_bh(&mkvp_list_lock);
-	list_for_each_entry_safe(ptr, pnext, &mkvp_list, list) {
-		list_del(&ptr->list);
-		kfree(ptr);
-	}
-	spin_unlock_bh(&mkvp_list_lock);
-}
-
-/*
- * Search for a matching crypto card based on the Master Key
- * Verification Pattern provided inside a secure key.
- */
-int pkey_findcard(const struct pkey_seckey *seckey,
-		  u16 *pcardnr, u16 *pdomain, int verify)
-{
-	struct secaeskeytoken *t = (struct secaeskeytoken *) seckey;
-	struct zcrypt_device_status_ext *device_status;
-	u16 card, dom;
-	u64 mkvp[2];
-	int i, rc, oi = -1;
-
-	/* mkvp must not be zero */
-	if (t->mkvp == 0)
-		return -EINVAL;
-
-	/* fetch status of all crypto cards */
-	device_status = kmalloc_array(MAX_ZDEV_ENTRIES_EXT,
-				      sizeof(struct zcrypt_device_status_ext),
-				      GFP_KERNEL);
-	if (!device_status)
-		return -ENOMEM;
-	zcrypt_device_status_mask_ext(device_status);
-
-	/* walk through all crypto cards */
-	for (i = 0; i < MAX_ZDEV_ENTRIES_EXT; i++) {
-		card = AP_QID_CARD(device_status[i].qid);
-		dom = AP_QID_QUEUE(device_status[i].qid);
-		if (device_status[i].online &&
-		    device_status[i].functions & 0x04) {
-			/* an enabled CCA Coprocessor card */
-			/* try cached mkvp */
-			if (mkvp_cache_fetch(card, dom, mkvp) == 0 &&
-			    t->mkvp == mkvp[0]) {
-				if (!verify)
-					break;
-				/* verify: fetch mkvp from adapter */
-				if (fetch_mkvp(card, dom, mkvp) == 0) {
-					mkvp_cache_update(card, dom, mkvp);
-					if (t->mkvp == mkvp[0])
-						break;
-				}
-			}
-		} else {
-			/* Card is offline and/or not a CCA card. */
-			/* del mkvp entry from cache if it exists */
-			mkvp_cache_scrub(card, dom);
-		}
-	}
-	if (i >= MAX_ZDEV_ENTRIES_EXT) {
-		/* nothing found, so this time without cache */
-		for (i = 0; i < MAX_ZDEV_ENTRIES_EXT; i++) {
-			if (!(device_status[i].online &&
-			      device_status[i].functions & 0x04))
-				continue;
-			card = AP_QID_CARD(device_status[i].qid);
-			dom = AP_QID_QUEUE(device_status[i].qid);
-			/* fresh fetch mkvp from adapter */
-			if (fetch_mkvp(card, dom, mkvp) == 0) {
-				mkvp_cache_update(card, dom, mkvp);
-				if (t->mkvp == mkvp[0])
-					break;
-				if (t->mkvp == mkvp[1] && oi < 0)
-					oi = i;
-			}
-		}
-		if (i >= MAX_ZDEV_ENTRIES_EXT && oi >= 0) {
-			/* old mkvp matched, use this card then */
-			card = AP_QID_CARD(device_status[oi].qid);
-			dom = AP_QID_QUEUE(device_status[oi].qid);
-		}
-	}
-	if (i < MAX_ZDEV_ENTRIES_EXT || oi >= 0) {
-		if (pcardnr)
-			*pcardnr = card;
-		if (pdomain)
-			*pdomain = dom;
-		rc = 0;
-	} else
-		rc = -ENODEV;
-
-	kfree(device_status);
-	return rc;
-}
-EXPORT_SYMBOL(pkey_findcard);
 
 /*
  * Find card and transform secure key into protected key.
  */
-int pkey_skey2pkey(const struct pkey_seckey *seckey,
-		   struct pkey_protkey *protkey)
+static int pkey_skey2pkey(const u8 *key, struct pkey_protkey *pkey)
 {
-	u16 cardnr, domain;
 	int rc, verify;
+	u16 cardnr, domain;
+	struct keytoken_header *hdr = (struct keytoken_header *)key;
 
 	/*
-	 * The pkey_sec2protkey call may fail when a card has been
+	 * The cca_xxx2protkey call may fail when a card has been
 	 * addressed where the master key was changed after last fetch
-	 * of the mkvp into the cache. So first try without verify then
-	 * with verify enabled (thus refreshing the mkvp for each card).
+	 * of the mkvp into the cache. Try 3 times: First witout verify
+	 * then with verify and last round with verify and old master
+	 * key verification pattern match not ignored.
 	 */
-	for (verify = 0; verify < 2; verify++) {
-		rc = pkey_findcard(seckey, &cardnr, &domain, verify);
-		if (rc)
+	for (verify = 0; verify < 3; verify++) {
+		rc = cca_findcard(key, &cardnr, &domain, verify);
+		if (rc < 0)
 			continue;
-		rc = pkey_sec2protkey(cardnr, domain, seckey, protkey);
+		if (rc > 0 && verify < 2)
+			continue;
+		switch (hdr->version) {
+		case TOKVER_CCA_AES:
+			rc = cca_sec2protkey(cardnr, domain,
+					     key, pkey->protkey,
+					     &pkey->len, &pkey->type);
+			break;
+		case TOKVER_CCA_VLSC:
+			rc = cca_cipher2protkey(cardnr, domain,
+						key, pkey->protkey,
+						&pkey->len, &pkey->type);
+			break;
+		default:
+			return -EINVAL;
+		}
 		if (rc == 0)
 			break;
 	}
@@ -1002,22 +171,20 @@
 
 	return rc;
 }
-EXPORT_SYMBOL(pkey_skey2pkey);
 
 /*
  * Verify key and give back some info about the key.
  */
-int pkey_verifykey(const struct pkey_seckey *seckey,
-		   u16 *pcardnr, u16 *pdomain,
-		   u16 *pkeysize, u32 *pattributes)
+static int pkey_verifykey(const struct pkey_seckey *seckey,
+			  u16 *pcardnr, u16 *pdomain,
+			  u16 *pkeysize, u32 *pattributes)
 {
 	struct secaeskeytoken *t = (struct secaeskeytoken *) seckey;
 	u16 cardnr, domain;
-	u64 mkvp[2];
 	int rc;
 
 	/* check the secure key for valid AES secure key */
-	rc = check_secaeskeytoken((u8 *) seckey, 0);
+	rc = cca_check_secaeskeytoken(debug_info, 3, (u8 *) seckey, 0);
 	if (rc)
 		goto out;
 	if (pattributes)
@@ -1026,18 +193,16 @@
 		*pkeysize = t->bitsize;
 
 	/* try to find a card which can handle this key */
-	rc = pkey_findcard(seckey, &cardnr, &domain, 1);
-	if (rc)
+	rc = cca_findcard(seckey->seckey, &cardnr, &domain, 1);
+	if (rc < 0)
 		goto out;
 
-	/* check mkvp for old mkvp match */
-	rc = mkvp_cache_fetch(cardnr, domain, mkvp);
-	if (rc)
-		goto out;
-	if (t->mkvp == mkvp[1]) {
+	if (rc > 0) {
+		/* key mkvp matches to old master key mkvp */
 		DEBUG_DBG("%s secure key has old mkvp\n", __func__);
 		if (pattributes)
 			*pattributes |= PKEY_VERIFY_ATTR_OLD_MKVP;
+		rc = 0;
 	}
 
 	if (pcardnr)
@@ -1049,12 +214,539 @@
 	DEBUG_DBG("%s rc=%d\n", __func__, rc);
 	return rc;
 }
-EXPORT_SYMBOL(pkey_verifykey);
+
+/*
+ * Generate a random protected key
+ */
+static int pkey_genprotkey(u32 keytype, struct pkey_protkey *protkey)
+{
+	struct pkey_clrkey clrkey;
+	int keysize;
+	int rc;
+
+	switch (keytype) {
+	case PKEY_KEYTYPE_AES_128:
+		keysize = 16;
+		break;
+	case PKEY_KEYTYPE_AES_192:
+		keysize = 24;
+		break;
+	case PKEY_KEYTYPE_AES_256:
+		keysize = 32;
+		break;
+	default:
+		DEBUG_ERR("%s unknown/unsupported keytype %d\n", __func__,
+			  keytype);
+		return -EINVAL;
+	}
+
+	/* generate a dummy random clear key */
+	get_random_bytes(clrkey.clrkey, keysize);
+
+	/* convert it to a dummy protected key */
+	rc = pkey_clr2protkey(keytype, &clrkey, protkey);
+	if (rc)
+		return rc;
+
+	/* replace the key part of the protected key with random bytes */
+	get_random_bytes(protkey->protkey, keysize);
+
+	return 0;
+}
+
+/*
+ * Verify if a protected key is still valid
+ */
+static int pkey_verifyprotkey(const struct pkey_protkey *protkey)
+{
+	unsigned long fc;
+	struct {
+		u8 iv[AES_BLOCK_SIZE];
+		u8 key[MAXPROTKEYSIZE];
+	} param;
+	u8 null_msg[AES_BLOCK_SIZE];
+	u8 dest_buf[AES_BLOCK_SIZE];
+	unsigned int k;
+
+	switch (protkey->type) {
+	case PKEY_KEYTYPE_AES_128:
+		fc = CPACF_KMC_PAES_128;
+		break;
+	case PKEY_KEYTYPE_AES_192:
+		fc = CPACF_KMC_PAES_192;
+		break;
+	case PKEY_KEYTYPE_AES_256:
+		fc = CPACF_KMC_PAES_256;
+		break;
+	default:
+		DEBUG_ERR("%s unknown/unsupported keytype %d\n", __func__,
+			  protkey->type);
+		return -EINVAL;
+	}
+
+	memset(null_msg, 0, sizeof(null_msg));
+
+	memset(param.iv, 0, sizeof(param.iv));
+	memcpy(param.key, protkey->protkey, sizeof(param.key));
+
+	k = cpacf_kmc(fc | CPACF_ENCRYPT, &param, null_msg, dest_buf,
+		      sizeof(null_msg));
+	if (k != sizeof(null_msg)) {
+		DEBUG_ERR("%s protected key is not valid\n", __func__);
+		return -EKEYREJECTED;
+	}
+
+	return 0;
+}
+
+/*
+ * Transform a non-CCA key token into a protected key
+ */
+static int pkey_nonccatok2pkey(const u8 *key, u32 keylen,
+			       struct pkey_protkey *protkey)
+{
+	struct keytoken_header *hdr = (struct keytoken_header *)key;
+	struct protaeskeytoken *t;
+
+	switch (hdr->version) {
+	case TOKVER_PROTECTED_KEY:
+		if (keylen != sizeof(struct protaeskeytoken))
+			return -EINVAL;
+
+		t = (struct protaeskeytoken *)key;
+		protkey->len = t->len;
+		protkey->type = t->keytype;
+		memcpy(protkey->protkey, t->protkey,
+		       sizeof(protkey->protkey));
+
+		return pkey_verifyprotkey(protkey);
+	default:
+		DEBUG_ERR("%s unknown/unsupported non-CCA token version %d\n",
+			  __func__, hdr->version);
+		return -EINVAL;
+	}
+}
+
+/*
+ * Transform a CCA internal key token into a protected key
+ */
+static int pkey_ccainttok2pkey(const u8 *key, u32 keylen,
+			       struct pkey_protkey *protkey)
+{
+	struct keytoken_header *hdr = (struct keytoken_header *)key;
+
+	switch (hdr->version) {
+	case TOKVER_CCA_AES:
+		if (keylen != sizeof(struct secaeskeytoken))
+			return -EINVAL;
+		break;
+	case TOKVER_CCA_VLSC:
+		if (keylen < hdr->len || keylen > MAXCCAVLSCTOKENSIZE)
+			return -EINVAL;
+		break;
+	default:
+		DEBUG_ERR("%s unknown/unsupported CCA internal token version %d\n",
+			  __func__, hdr->version);
+		return -EINVAL;
+	}
+
+	return pkey_skey2pkey(key, protkey);
+}
+
+/*
+ * Transform a key blob (of any type) into a protected key
+ */
+int pkey_keyblob2pkey(const u8 *key, u32 keylen,
+		      struct pkey_protkey *protkey)
+{
+	int rc;
+	struct keytoken_header *hdr = (struct keytoken_header *)key;
+
+	if (keylen < sizeof(struct keytoken_header)) {
+		DEBUG_ERR("%s invalid keylen %d\n", __func__, keylen);
+		return -EINVAL;
+	}
+
+	switch (hdr->type) {
+	case TOKTYPE_NON_CCA:
+		rc = pkey_nonccatok2pkey(key, keylen, protkey);
+		break;
+	case TOKTYPE_CCA_INTERNAL:
+		rc = pkey_ccainttok2pkey(key, keylen, protkey);
+		break;
+	default:
+		DEBUG_ERR("%s unknown/unsupported blob type %d\n",
+			  __func__, hdr->type);
+		return -EINVAL;
+	}
+
+	DEBUG_DBG("%s rc=%d\n", __func__, rc);
+	return rc;
+
+}
+EXPORT_SYMBOL(pkey_keyblob2pkey);
+
+static int pkey_genseckey2(const struct pkey_apqn *apqns, size_t nr_apqns,
+			   enum pkey_key_type ktype, enum pkey_key_size ksize,
+			   u32 kflags, u8 *keybuf, size_t *keybufsize)
+{
+	int i, card, dom, rc;
+
+	/* check for at least one apqn given */
+	if (!apqns || !nr_apqns)
+		return -EINVAL;
+
+	/* check key type and size */
+	switch (ktype) {
+	case PKEY_TYPE_CCA_DATA:
+	case PKEY_TYPE_CCA_CIPHER:
+		if (*keybufsize < SECKEYBLOBSIZE)
+			return -EINVAL;
+		break;
+	default:
+		return -EINVAL;
+	}
+	switch (ksize) {
+	case PKEY_SIZE_AES_128:
+	case PKEY_SIZE_AES_192:
+	case PKEY_SIZE_AES_256:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* simple try all apqns from the list */
+	for (i = 0, rc = -ENODEV; i < nr_apqns; i++) {
+		card = apqns[i].card;
+		dom = apqns[i].domain;
+		if (ktype == PKEY_TYPE_CCA_DATA) {
+			rc = cca_genseckey(card, dom, ksize, keybuf);
+			*keybufsize = (rc ? 0 : SECKEYBLOBSIZE);
+		} else /* TOKVER_CCA_VLSC */
+			rc = cca_gencipherkey(card, dom, ksize, kflags,
+					      keybuf, keybufsize);
+		if (rc == 0)
+			break;
+	}
+
+	return rc;
+}
+
+static int pkey_clr2seckey2(const struct pkey_apqn *apqns, size_t nr_apqns,
+			    enum pkey_key_type ktype, enum pkey_key_size ksize,
+			    u32 kflags, const u8 *clrkey,
+			    u8 *keybuf, size_t *keybufsize)
+{
+	int i, card, dom, rc;
+
+	/* check for at least one apqn given */
+	if (!apqns || !nr_apqns)
+		return -EINVAL;
+
+	/* check key type and size */
+	switch (ktype) {
+	case PKEY_TYPE_CCA_DATA:
+	case PKEY_TYPE_CCA_CIPHER:
+		if (*keybufsize < SECKEYBLOBSIZE)
+			return -EINVAL;
+		break;
+	default:
+		return -EINVAL;
+	}
+	switch (ksize) {
+	case PKEY_SIZE_AES_128:
+	case PKEY_SIZE_AES_192:
+	case PKEY_SIZE_AES_256:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* simple try all apqns from the list */
+	for (i = 0, rc = -ENODEV; i < nr_apqns; i++) {
+		card = apqns[i].card;
+		dom = apqns[i].domain;
+		if (ktype == PKEY_TYPE_CCA_DATA) {
+			rc = cca_clr2seckey(card, dom, ksize,
+					    clrkey, keybuf);
+			*keybufsize = (rc ? 0 : SECKEYBLOBSIZE);
+		} else /* TOKVER_CCA_VLSC */
+			rc = cca_clr2cipherkey(card, dom, ksize, kflags,
+					       clrkey, keybuf, keybufsize);
+		if (rc == 0)
+			break;
+	}
+
+	return rc;
+}
+
+static int pkey_verifykey2(const u8 *key, size_t keylen,
+			   u16 *cardnr, u16 *domain,
+			   enum pkey_key_type *ktype,
+			   enum pkey_key_size *ksize, u32 *flags)
+{
+	int rc;
+	u32 _nr_apqns, *_apqns = NULL;
+	struct keytoken_header *hdr = (struct keytoken_header *)key;
+
+	if (keylen < sizeof(struct keytoken_header) ||
+	    hdr->type != TOKTYPE_CCA_INTERNAL)
+		return -EINVAL;
+
+	if (hdr->version == TOKVER_CCA_AES) {
+		struct secaeskeytoken *t = (struct secaeskeytoken *)key;
+
+		rc = cca_check_secaeskeytoken(debug_info, 3, key, 0);
+		if (rc)
+			goto out;
+		if (ktype)
+			*ktype = PKEY_TYPE_CCA_DATA;
+		if (ksize)
+			*ksize = (enum pkey_key_size) t->bitsize;
+
+		rc = cca_findcard2(&_apqns, &_nr_apqns, *cardnr, *domain,
+				   ZCRYPT_CEX3C, t->mkvp, 0, 1);
+		if (rc == 0 && flags)
+			*flags = PKEY_FLAGS_MATCH_CUR_MKVP;
+		if (rc == -ENODEV) {
+			rc = cca_findcard2(&_apqns, &_nr_apqns,
+					   *cardnr, *domain,
+					   ZCRYPT_CEX3C, 0, t->mkvp, 1);
+			if (rc == 0 && flags)
+				*flags = PKEY_FLAGS_MATCH_ALT_MKVP;
+		}
+		if (rc)
+			goto out;
+
+		*cardnr = ((struct pkey_apqn *)_apqns)->card;
+		*domain = ((struct pkey_apqn *)_apqns)->domain;
+
+	} else if (hdr->version == TOKVER_CCA_VLSC) {
+		struct cipherkeytoken *t = (struct cipherkeytoken *)key;
+
+		rc = cca_check_secaescipherkey(debug_info, 3, key, 0, 1);
+		if (rc)
+			goto out;
+		if (ktype)
+			*ktype = PKEY_TYPE_CCA_CIPHER;
+		if (ksize) {
+			*ksize = PKEY_SIZE_UNKNOWN;
+			if (!t->plfver && t->wpllen == 512)
+				*ksize = PKEY_SIZE_AES_128;
+			else if (!t->plfver && t->wpllen == 576)
+				*ksize = PKEY_SIZE_AES_192;
+			else if (!t->plfver && t->wpllen == 640)
+				*ksize = PKEY_SIZE_AES_256;
+		}
+
+		rc = cca_findcard2(&_apqns, &_nr_apqns, *cardnr, *domain,
+				   ZCRYPT_CEX6, t->mkvp0, 0, 1);
+		if (rc == 0 && flags)
+			*flags = PKEY_FLAGS_MATCH_CUR_MKVP;
+		if (rc == -ENODEV) {
+			rc = cca_findcard2(&_apqns, &_nr_apqns,
+					   *cardnr, *domain,
+					   ZCRYPT_CEX6, 0, t->mkvp0, 1);
+			if (rc == 0 && flags)
+				*flags = PKEY_FLAGS_MATCH_ALT_MKVP;
+		}
+		if (rc)
+			goto out;
+
+		*cardnr = ((struct pkey_apqn *)_apqns)->card;
+		*domain = ((struct pkey_apqn *)_apqns)->domain;
+
+	} else
+		rc = -EINVAL;
+
+out:
+	kfree(_apqns);
+	return rc;
+}
+
+static int pkey_keyblob2pkey2(const struct pkey_apqn *apqns, size_t nr_apqns,
+			      const u8 *key, size_t keylen,
+			      struct pkey_protkey *pkey)
+{
+	int i, card, dom, rc;
+	struct keytoken_header *hdr = (struct keytoken_header *)key;
+
+	/* check for at least one apqn given */
+	if (!apqns || !nr_apqns)
+		return -EINVAL;
+
+	if (keylen < sizeof(struct keytoken_header))
+		return -EINVAL;
+
+	switch (hdr->type) {
+	case TOKTYPE_NON_CCA:
+		return pkey_nonccatok2pkey(key, keylen, pkey);
+	case TOKTYPE_CCA_INTERNAL:
+		switch (hdr->version) {
+		case TOKVER_CCA_AES:
+			if (keylen != sizeof(struct secaeskeytoken))
+				return -EINVAL;
+			if (cca_check_secaeskeytoken(debug_info, 3, key, 0))
+				return -EINVAL;
+			break;
+		case TOKVER_CCA_VLSC:
+			if (keylen < hdr->len || keylen > MAXCCAVLSCTOKENSIZE)
+				return -EINVAL;
+			if (cca_check_secaescipherkey(debug_info, 3, key, 0, 1))
+				return -EINVAL;
+			break;
+		default:
+			DEBUG_ERR("%s unknown CCA internal token version %d\n",
+				  __func__, hdr->version);
+			return -EINVAL;
+		}
+		break;
+	default:
+		DEBUG_ERR("%s unknown/unsupported blob type %d\n",
+			  __func__, hdr->type);
+		return -EINVAL;
+	}
+
+	/* simple try all apqns from the list */
+	for (i = 0, rc = -ENODEV; i < nr_apqns; i++) {
+		card = apqns[i].card;
+		dom = apqns[i].domain;
+		if (hdr->version == TOKVER_CCA_AES)
+			rc = cca_sec2protkey(card, dom, key, pkey->protkey,
+					     &pkey->len, &pkey->type);
+		else /* TOKVER_CCA_VLSC */
+			rc = cca_cipher2protkey(card, dom, key, pkey->protkey,
+						&pkey->len, &pkey->type);
+		if (rc == 0)
+			break;
+	}
+
+	return rc;
+}
+
+static int pkey_apqns4key(const u8 *key, size_t keylen, u32 flags,
+			  struct pkey_apqn *apqns, size_t *nr_apqns)
+{
+	int rc = EINVAL;
+	u32 _nr_apqns, *_apqns = NULL;
+	struct keytoken_header *hdr = (struct keytoken_header *)key;
+
+	if (keylen < sizeof(struct keytoken_header) ||
+	    hdr->type != TOKTYPE_CCA_INTERNAL ||
+	    flags == 0)
+		return -EINVAL;
+
+	if (hdr->version == TOKVER_CCA_AES || hdr->version == TOKVER_CCA_VLSC) {
+		int minhwtype = ZCRYPT_CEX3C;
+		u64 cur_mkvp = 0, old_mkvp = 0;
+
+		if (hdr->version == TOKVER_CCA_AES) {
+			struct secaeskeytoken *t = (struct secaeskeytoken *)key;
+
+			if (flags & PKEY_FLAGS_MATCH_CUR_MKVP)
+				cur_mkvp = t->mkvp;
+			if (flags & PKEY_FLAGS_MATCH_ALT_MKVP)
+				old_mkvp = t->mkvp;
+		} else {
+			struct cipherkeytoken *t = (struct cipherkeytoken *)key;
+
+			minhwtype = ZCRYPT_CEX6;
+			if (flags & PKEY_FLAGS_MATCH_CUR_MKVP)
+				cur_mkvp = t->mkvp0;
+			if (flags & PKEY_FLAGS_MATCH_ALT_MKVP)
+				old_mkvp = t->mkvp0;
+		}
+		rc = cca_findcard2(&_apqns, &_nr_apqns, 0xFFFF, 0xFFFF,
+				   minhwtype, cur_mkvp, old_mkvp, 1);
+		if (rc)
+			goto out;
+		if (apqns) {
+			if (*nr_apqns < _nr_apqns)
+				rc = -ENOSPC;
+			else
+				memcpy(apqns, _apqns, _nr_apqns * sizeof(u32));
+		}
+		*nr_apqns = _nr_apqns;
+	}
+
+out:
+	kfree(_apqns);
+	return rc;
+}
+
+static int pkey_apqns4keytype(enum pkey_key_type ktype,
+			      u8 cur_mkvp[32], u8 alt_mkvp[32], u32 flags,
+			      struct pkey_apqn *apqns, size_t *nr_apqns)
+{
+	int rc = -EINVAL;
+	u32 _nr_apqns, *_apqns = NULL;
+
+	if (ktype == PKEY_TYPE_CCA_DATA || ktype == PKEY_TYPE_CCA_CIPHER) {
+		u64 cur_mkvp = 0, old_mkvp = 0;
+		int minhwtype = ZCRYPT_CEX3C;
+
+		if (flags & PKEY_FLAGS_MATCH_CUR_MKVP)
+			cur_mkvp = *((u64 *) cur_mkvp);
+		if (flags & PKEY_FLAGS_MATCH_ALT_MKVP)
+			old_mkvp = *((u64 *) alt_mkvp);
+		if (ktype == PKEY_TYPE_CCA_CIPHER)
+			minhwtype = ZCRYPT_CEX6;
+		rc = cca_findcard2(&_apqns, &_nr_apqns, 0xFFFF, 0xFFFF,
+				   minhwtype, cur_mkvp, old_mkvp, 1);
+		if (rc)
+			goto out;
+		if (apqns) {
+			if (*nr_apqns < _nr_apqns)
+				rc = -ENOSPC;
+			else
+				memcpy(apqns, _apqns, _nr_apqns * sizeof(u32));
+		}
+		*nr_apqns = _nr_apqns;
+	}
+
+out:
+	kfree(_apqns);
+	return rc;
+}
 
 /*
  * File io functions
  */
 
+static void *_copy_key_from_user(void __user *ukey, size_t keylen)
+{
+	void *kkey;
+
+	if (!ukey || keylen < MINKEYBLOBSIZE || keylen > KEYBLOBBUFSIZE)
+		return ERR_PTR(-EINVAL);
+	kkey = kmalloc(keylen, GFP_KERNEL);
+	if (!kkey)
+		return ERR_PTR(-ENOMEM);
+	if (copy_from_user(kkey, ukey, keylen)) {
+		kfree(kkey);
+		return ERR_PTR(-EFAULT);
+	}
+
+	return kkey;
+}
+
+static void *_copy_apqns_from_user(void __user *uapqns, size_t nr_apqns)
+{
+	void *kapqns = NULL;
+	size_t nbytes;
+
+	if (uapqns && nr_apqns > 0) {
+		nbytes = nr_apqns * sizeof(struct pkey_apqn);
+		kapqns = kmalloc(nbytes, GFP_KERNEL);
+		if (!kapqns)
+			return ERR_PTR(-ENOMEM);
+		if (copy_from_user(kapqns, uapqns, nbytes))
+			return ERR_PTR(-EFAULT);
+	}
+
+	return kapqns;
+}
+
 static long pkey_unlocked_ioctl(struct file *filp, unsigned int cmd,
 				unsigned long arg)
 {
@@ -1067,9 +759,9 @@
 
 		if (copy_from_user(&kgs, ugs, sizeof(kgs)))
 			return -EFAULT;
-		rc = pkey_genseckey(kgs.cardnr, kgs.domain,
-				    kgs.keytype, &kgs.seckey);
-		DEBUG_DBG("%s pkey_genseckey()=%d\n", __func__, rc);
+		rc = cca_genseckey(kgs.cardnr, kgs.domain,
+				   kgs.keytype, kgs.seckey.seckey);
+		DEBUG_DBG("%s cca_genseckey()=%d\n", __func__, rc);
 		if (rc)
 			break;
 		if (copy_to_user(ugs, &kgs, sizeof(kgs)))
@@ -1082,9 +774,9 @@
 
 		if (copy_from_user(&kcs, ucs, sizeof(kcs)))
 			return -EFAULT;
-		rc = pkey_clr2seckey(kcs.cardnr, kcs.domain, kcs.keytype,
-				     &kcs.clrkey, &kcs.seckey);
-		DEBUG_DBG("%s pkey_clr2seckey()=%d\n", __func__, rc);
+		rc = cca_clr2seckey(kcs.cardnr, kcs.domain, kcs.keytype,
+				    kcs.clrkey.clrkey, kcs.seckey.seckey);
+		DEBUG_DBG("%s cca_clr2seckey()=%d\n", __func__, rc);
 		if (rc)
 			break;
 		if (copy_to_user(ucs, &kcs, sizeof(kcs)))
@@ -1098,9 +790,10 @@
 
 		if (copy_from_user(&ksp, usp, sizeof(ksp)))
 			return -EFAULT;
-		rc = pkey_sec2protkey(ksp.cardnr, ksp.domain,
-				      &ksp.seckey, &ksp.protkey);
-		DEBUG_DBG("%s pkey_sec2protkey()=%d\n", __func__, rc);
+		rc = cca_sec2protkey(ksp.cardnr, ksp.domain,
+				     ksp.seckey.seckey, ksp.protkey.protkey,
+				     NULL, &ksp.protkey.type);
+		DEBUG_DBG("%s cca_sec2protkey()=%d\n", __func__, rc);
 		if (rc)
 			break;
 		if (copy_to_user(usp, &ksp, sizeof(ksp)))
@@ -1129,10 +822,10 @@
 
 		if (copy_from_user(&kfc, ufc, sizeof(kfc)))
 			return -EFAULT;
-		rc = pkey_findcard(&kfc.seckey,
-				   &kfc.cardnr, &kfc.domain, 1);
-		DEBUG_DBG("%s pkey_findcard()=%d\n", __func__, rc);
-		if (rc)
+		rc = cca_findcard(kfc.seckey.seckey,
+				  &kfc.cardnr, &kfc.domain, 1);
+		DEBUG_DBG("%s cca_findcard()=%d\n", __func__, rc);
+		if (rc < 0)
 			break;
 		if (copy_to_user(ufc, &kfc, sizeof(kfc)))
 			return -EFAULT;
@@ -1144,7 +837,7 @@
 
 		if (copy_from_user(&ksp, usp, sizeof(ksp)))
 			return -EFAULT;
-		rc = pkey_skey2pkey(&ksp.seckey, &ksp.protkey);
+		rc = pkey_skey2pkey(ksp.seckey.seckey, &ksp.protkey);
 		DEBUG_DBG("%s pkey_skey2pkey()=%d\n", __func__, rc);
 		if (rc)
 			break;
@@ -1167,6 +860,273 @@
 			return -EFAULT;
 		break;
 	}
+	case PKEY_GENPROTK: {
+		struct pkey_genprotk __user *ugp = (void __user *) arg;
+		struct pkey_genprotk kgp;
+
+		if (copy_from_user(&kgp, ugp, sizeof(kgp)))
+			return -EFAULT;
+		rc = pkey_genprotkey(kgp.keytype, &kgp.protkey);
+		DEBUG_DBG("%s pkey_genprotkey()=%d\n", __func__, rc);
+		if (rc)
+			break;
+		if (copy_to_user(ugp, &kgp, sizeof(kgp)))
+			return -EFAULT;
+		break;
+	}
+	case PKEY_VERIFYPROTK: {
+		struct pkey_verifyprotk __user *uvp = (void __user *) arg;
+		struct pkey_verifyprotk kvp;
+
+		if (copy_from_user(&kvp, uvp, sizeof(kvp)))
+			return -EFAULT;
+		rc = pkey_verifyprotkey(&kvp.protkey);
+		DEBUG_DBG("%s pkey_verifyprotkey()=%d\n", __func__, rc);
+		break;
+	}
+	case PKEY_KBLOB2PROTK: {
+		struct pkey_kblob2pkey __user *utp = (void __user *) arg;
+		struct pkey_kblob2pkey ktp;
+		u8 *kkey;
+
+		if (copy_from_user(&ktp, utp, sizeof(ktp)))
+			return -EFAULT;
+		kkey = _copy_key_from_user(ktp.key, ktp.keylen);
+		if (IS_ERR(kkey))
+			return PTR_ERR(kkey);
+		rc = pkey_keyblob2pkey(kkey, ktp.keylen, &ktp.protkey);
+		DEBUG_DBG("%s pkey_keyblob2pkey()=%d\n", __func__, rc);
+		kfree(kkey);
+		if (rc)
+			break;
+		if (copy_to_user(utp, &ktp, sizeof(ktp)))
+			return -EFAULT;
+		break;
+	}
+	case PKEY_GENSECK2: {
+		struct pkey_genseck2 __user *ugs = (void __user *) arg;
+		struct pkey_genseck2 kgs;
+		struct pkey_apqn *apqns;
+		size_t klen = KEYBLOBBUFSIZE;
+		u8 *kkey;
+
+		if (copy_from_user(&kgs, ugs, sizeof(kgs)))
+			return -EFAULT;
+		apqns = _copy_apqns_from_user(kgs.apqns, kgs.apqn_entries);
+		if (IS_ERR(apqns))
+			return PTR_ERR(apqns);
+		kkey = kmalloc(klen, GFP_KERNEL);
+		if (!kkey) {
+			kfree(apqns);
+			return -ENOMEM;
+		}
+		rc = pkey_genseckey2(apqns, kgs.apqn_entries,
+				     kgs.type, kgs.size, kgs.keygenflags,
+				     kkey, &klen);
+		DEBUG_DBG("%s pkey_genseckey2()=%d\n", __func__, rc);
+		kfree(apqns);
+		if (rc) {
+			kfree(kkey);
+			break;
+		}
+		if (kgs.key) {
+			if (kgs.keylen < klen) {
+				kfree(kkey);
+				return -EINVAL;
+			}
+			if (copy_to_user(kgs.key, kkey, klen)) {
+				kfree(kkey);
+				return -EFAULT;
+			}
+		}
+		kgs.keylen = klen;
+		if (copy_to_user(ugs, &kgs, sizeof(kgs)))
+			rc = -EFAULT;
+		kfree(kkey);
+		break;
+	}
+	case PKEY_CLR2SECK2: {
+		struct pkey_clr2seck2 __user *ucs = (void __user *) arg;
+		struct pkey_clr2seck2 kcs;
+		struct pkey_apqn *apqns;
+		size_t klen = KEYBLOBBUFSIZE;
+		u8 *kkey;
+
+		if (copy_from_user(&kcs, ucs, sizeof(kcs)))
+			return -EFAULT;
+		apqns = _copy_apqns_from_user(kcs.apqns, kcs.apqn_entries);
+		if (IS_ERR(apqns))
+			return PTR_ERR(apqns);
+		kkey = kmalloc(klen, GFP_KERNEL);
+		if (!kkey) {
+			kfree(apqns);
+			return -ENOMEM;
+		}
+		rc = pkey_clr2seckey2(apqns, kcs.apqn_entries,
+				      kcs.type, kcs.size, kcs.keygenflags,
+				      kcs.clrkey.clrkey, kkey, &klen);
+		DEBUG_DBG("%s pkey_clr2seckey2()=%d\n", __func__, rc);
+		kfree(apqns);
+		if (rc) {
+			kfree(kkey);
+			break;
+		}
+		if (kcs.key) {
+			if (kcs.keylen < klen) {
+				kfree(kkey);
+				return -EINVAL;
+			}
+			if (copy_to_user(kcs.key, kkey, klen)) {
+				kfree(kkey);
+				return -EFAULT;
+			}
+		}
+		kcs.keylen = klen;
+		if (copy_to_user(ucs, &kcs, sizeof(kcs)))
+			rc = -EFAULT;
+		memzero_explicit(&kcs, sizeof(kcs));
+		kfree(kkey);
+		break;
+	}
+	case PKEY_VERIFYKEY2: {
+		struct pkey_verifykey2 __user *uvk = (void __user *) arg;
+		struct pkey_verifykey2 kvk;
+		u8 *kkey;
+
+		if (copy_from_user(&kvk, uvk, sizeof(kvk)))
+			return -EFAULT;
+		kkey = _copy_key_from_user(kvk.key, kvk.keylen);
+		if (IS_ERR(kkey))
+			return PTR_ERR(kkey);
+		rc = pkey_verifykey2(kkey, kvk.keylen,
+				     &kvk.cardnr, &kvk.domain,
+				     &kvk.type, &kvk.size, &kvk.flags);
+		DEBUG_DBG("%s pkey_verifykey2()=%d\n", __func__, rc);
+		kfree(kkey);
+		if (rc)
+			break;
+		if (copy_to_user(uvk, &kvk, sizeof(kvk)))
+			return -EFAULT;
+		break;
+	}
+	case PKEY_KBLOB2PROTK2: {
+		struct pkey_kblob2pkey2 __user *utp = (void __user *) arg;
+		struct pkey_kblob2pkey2 ktp;
+		struct pkey_apqn *apqns = NULL;
+		u8 *kkey;
+
+		if (copy_from_user(&ktp, utp, sizeof(ktp)))
+			return -EFAULT;
+		apqns = _copy_apqns_from_user(ktp.apqns, ktp.apqn_entries);
+		if (IS_ERR(apqns))
+			return PTR_ERR(apqns);
+		kkey = _copy_key_from_user(ktp.key, ktp.keylen);
+		if (IS_ERR(kkey)) {
+			kfree(apqns);
+			return PTR_ERR(kkey);
+		}
+		rc = pkey_keyblob2pkey2(apqns, ktp.apqn_entries,
+					kkey, ktp.keylen, &ktp.protkey);
+		DEBUG_DBG("%s pkey_keyblob2pkey2()=%d\n", __func__, rc);
+		kfree(apqns);
+		kfree(kkey);
+		if (rc)
+			break;
+		if (copy_to_user(utp, &ktp, sizeof(ktp)))
+			return -EFAULT;
+		break;
+	}
+	case PKEY_APQNS4K: {
+		struct pkey_apqns4key __user *uak = (void __user *) arg;
+		struct pkey_apqns4key kak;
+		struct pkey_apqn *apqns = NULL;
+		size_t nr_apqns, len;
+		u8 *kkey;
+
+		if (copy_from_user(&kak, uak, sizeof(kak)))
+			return -EFAULT;
+		nr_apqns = kak.apqn_entries;
+		if (nr_apqns) {
+			apqns = kmalloc_array(nr_apqns,
+					      sizeof(struct pkey_apqn),
+					      GFP_KERNEL);
+			if (!apqns)
+				return -ENOMEM;
+		}
+		kkey = _copy_key_from_user(kak.key, kak.keylen);
+		if (IS_ERR(kkey)) {
+			kfree(apqns);
+			return PTR_ERR(kkey);
+		}
+		rc = pkey_apqns4key(kkey, kak.keylen, kak.flags,
+				    apqns, &nr_apqns);
+		DEBUG_DBG("%s pkey_apqns4key()=%d\n", __func__, rc);
+		kfree(kkey);
+		if (rc && rc != -ENOSPC) {
+			kfree(apqns);
+			break;
+		}
+		if (!rc && kak.apqns) {
+			if (nr_apqns > kak.apqn_entries) {
+				kfree(apqns);
+				return -EINVAL;
+			}
+			len = nr_apqns * sizeof(struct pkey_apqn);
+			if (len) {
+				if (copy_to_user(kak.apqns, apqns, len)) {
+					kfree(apqns);
+					return -EFAULT;
+				}
+			}
+		}
+		kak.apqn_entries = nr_apqns;
+		if (copy_to_user(uak, &kak, sizeof(kak)))
+			rc = -EFAULT;
+		kfree(apqns);
+		break;
+	}
+	case PKEY_APQNS4KT: {
+		struct pkey_apqns4keytype __user *uat = (void __user *) arg;
+		struct pkey_apqns4keytype kat;
+		struct pkey_apqn *apqns = NULL;
+		size_t nr_apqns, len;
+
+		if (copy_from_user(&kat, uat, sizeof(kat)))
+			return -EFAULT;
+		nr_apqns = kat.apqn_entries;
+		if (nr_apqns) {
+			apqns = kmalloc_array(nr_apqns,
+					      sizeof(struct pkey_apqn),
+					      GFP_KERNEL);
+			if (!apqns)
+				return -ENOMEM;
+		}
+		rc = pkey_apqns4keytype(kat.type, kat.cur_mkvp, kat.alt_mkvp,
+					kat.flags, apqns, &nr_apqns);
+		DEBUG_DBG("%s pkey_apqns4keytype()=%d\n", __func__, rc);
+		if (rc && rc != -ENOSPC) {
+			kfree(apqns);
+			break;
+		}
+		if (!rc && kat.apqns) {
+			if (nr_apqns > kat.apqn_entries) {
+				kfree(apqns);
+				return -EINVAL;
+			}
+			len = nr_apqns * sizeof(struct pkey_apqn);
+			if (len) {
+				if (copy_to_user(kat.apqns, apqns, len)) {
+					kfree(apqns);
+					return -EFAULT;
+				}
+			}
+		}
+		kat.apqn_entries = nr_apqns;
+		if (copy_to_user(uat, &kat, sizeof(kat)))
+			rc = -EFAULT;
+		kfree(apqns);
+		break;
+	}
 	default:
 		/* unknown/unsupported ioctl cmd */
 		return -ENOTTY;
@@ -1178,6 +1138,350 @@
 /*
  * Sysfs and file io operations
  */
+
+/*
+ * Sysfs attribute read function for all protected key binary attributes.
+ * The implementation can not deal with partial reads, because a new random
+ * protected key blob is generated with each read. In case of partial reads
+ * (i.e. off != 0 or count < key blob size) -EINVAL is returned.
+ */
+static ssize_t pkey_protkey_aes_attr_read(u32 keytype, bool is_xts, char *buf,
+					  loff_t off, size_t count)
+{
+	struct protaeskeytoken protkeytoken;
+	struct pkey_protkey protkey;
+	int rc;
+
+	if (off != 0 || count < sizeof(protkeytoken))
+		return -EINVAL;
+	if (is_xts)
+		if (count < 2 * sizeof(protkeytoken))
+			return -EINVAL;
+
+	memset(&protkeytoken, 0, sizeof(protkeytoken));
+	protkeytoken.type = TOKTYPE_NON_CCA;
+	protkeytoken.version = TOKVER_PROTECTED_KEY;
+	protkeytoken.keytype = keytype;
+
+	rc = pkey_genprotkey(protkeytoken.keytype, &protkey);
+	if (rc)
+		return rc;
+
+	protkeytoken.len = protkey.len;
+	memcpy(&protkeytoken.protkey, &protkey.protkey, protkey.len);
+
+	memcpy(buf, &protkeytoken, sizeof(protkeytoken));
+
+	if (is_xts) {
+		rc = pkey_genprotkey(protkeytoken.keytype, &protkey);
+		if (rc)
+			return rc;
+
+		protkeytoken.len = protkey.len;
+		memcpy(&protkeytoken.protkey, &protkey.protkey, protkey.len);
+
+		memcpy(buf + sizeof(protkeytoken), &protkeytoken,
+		       sizeof(protkeytoken));
+
+		return 2 * sizeof(protkeytoken);
+	}
+
+	return sizeof(protkeytoken);
+}
+
+static ssize_t protkey_aes_128_read(struct file *filp,
+				    struct kobject *kobj,
+				    struct bin_attribute *attr,
+				    char *buf, loff_t off,
+				    size_t count)
+{
+	return pkey_protkey_aes_attr_read(PKEY_KEYTYPE_AES_128, false, buf,
+					  off, count);
+}
+
+static ssize_t protkey_aes_192_read(struct file *filp,
+				    struct kobject *kobj,
+				    struct bin_attribute *attr,
+				    char *buf, loff_t off,
+				    size_t count)
+{
+	return pkey_protkey_aes_attr_read(PKEY_KEYTYPE_AES_192, false, buf,
+					  off, count);
+}
+
+static ssize_t protkey_aes_256_read(struct file *filp,
+				    struct kobject *kobj,
+				    struct bin_attribute *attr,
+				    char *buf, loff_t off,
+				    size_t count)
+{
+	return pkey_protkey_aes_attr_read(PKEY_KEYTYPE_AES_256, false, buf,
+					  off, count);
+}
+
+static ssize_t protkey_aes_128_xts_read(struct file *filp,
+					struct kobject *kobj,
+					struct bin_attribute *attr,
+					char *buf, loff_t off,
+					size_t count)
+{
+	return pkey_protkey_aes_attr_read(PKEY_KEYTYPE_AES_128, true, buf,
+					  off, count);
+}
+
+static ssize_t protkey_aes_256_xts_read(struct file *filp,
+					struct kobject *kobj,
+					struct bin_attribute *attr,
+					char *buf, loff_t off,
+					size_t count)
+{
+	return pkey_protkey_aes_attr_read(PKEY_KEYTYPE_AES_256, true, buf,
+					  off, count);
+}
+
+static BIN_ATTR_RO(protkey_aes_128, sizeof(struct protaeskeytoken));
+static BIN_ATTR_RO(protkey_aes_192, sizeof(struct protaeskeytoken));
+static BIN_ATTR_RO(protkey_aes_256, sizeof(struct protaeskeytoken));
+static BIN_ATTR_RO(protkey_aes_128_xts, 2 * sizeof(struct protaeskeytoken));
+static BIN_ATTR_RO(protkey_aes_256_xts, 2 * sizeof(struct protaeskeytoken));
+
+static struct bin_attribute *protkey_attrs[] = {
+	&bin_attr_protkey_aes_128,
+	&bin_attr_protkey_aes_192,
+	&bin_attr_protkey_aes_256,
+	&bin_attr_protkey_aes_128_xts,
+	&bin_attr_protkey_aes_256_xts,
+	NULL
+};
+
+static struct attribute_group protkey_attr_group = {
+	.name	   = "protkey",
+	.bin_attrs = protkey_attrs,
+};
+
+/*
+ * Sysfs attribute read function for all secure key ccadata binary attributes.
+ * The implementation can not deal with partial reads, because a new random
+ * protected key blob is generated with each read. In case of partial reads
+ * (i.e. off != 0 or count < key blob size) -EINVAL is returned.
+ */
+static ssize_t pkey_ccadata_aes_attr_read(u32 keytype, bool is_xts, char *buf,
+					  loff_t off, size_t count)
+{
+	int rc;
+	struct pkey_seckey *seckey = (struct pkey_seckey *) buf;
+
+	if (off != 0 || count < sizeof(struct secaeskeytoken))
+		return -EINVAL;
+	if (is_xts)
+		if (count < 2 * sizeof(struct secaeskeytoken))
+			return -EINVAL;
+
+	rc = cca_genseckey(-1, -1, keytype, seckey->seckey);
+	if (rc)
+		return rc;
+
+	if (is_xts) {
+		seckey++;
+		rc = cca_genseckey(-1, -1, keytype, seckey->seckey);
+		if (rc)
+			return rc;
+
+		return 2 * sizeof(struct secaeskeytoken);
+	}
+
+	return sizeof(struct secaeskeytoken);
+}
+
+static ssize_t ccadata_aes_128_read(struct file *filp,
+				    struct kobject *kobj,
+				    struct bin_attribute *attr,
+				    char *buf, loff_t off,
+				    size_t count)
+{
+	return pkey_ccadata_aes_attr_read(PKEY_KEYTYPE_AES_128, false, buf,
+					  off, count);
+}
+
+static ssize_t ccadata_aes_192_read(struct file *filp,
+				    struct kobject *kobj,
+				    struct bin_attribute *attr,
+				    char *buf, loff_t off,
+				    size_t count)
+{
+	return pkey_ccadata_aes_attr_read(PKEY_KEYTYPE_AES_192, false, buf,
+					  off, count);
+}
+
+static ssize_t ccadata_aes_256_read(struct file *filp,
+				    struct kobject *kobj,
+				    struct bin_attribute *attr,
+				    char *buf, loff_t off,
+				    size_t count)
+{
+	return pkey_ccadata_aes_attr_read(PKEY_KEYTYPE_AES_256, false, buf,
+					  off, count);
+}
+
+static ssize_t ccadata_aes_128_xts_read(struct file *filp,
+					struct kobject *kobj,
+					struct bin_attribute *attr,
+					char *buf, loff_t off,
+					size_t count)
+{
+	return pkey_ccadata_aes_attr_read(PKEY_KEYTYPE_AES_128, true, buf,
+					  off, count);
+}
+
+static ssize_t ccadata_aes_256_xts_read(struct file *filp,
+					struct kobject *kobj,
+					struct bin_attribute *attr,
+					char *buf, loff_t off,
+					size_t count)
+{
+	return pkey_ccadata_aes_attr_read(PKEY_KEYTYPE_AES_256, true, buf,
+					  off, count);
+}
+
+static BIN_ATTR_RO(ccadata_aes_128, sizeof(struct secaeskeytoken));
+static BIN_ATTR_RO(ccadata_aes_192, sizeof(struct secaeskeytoken));
+static BIN_ATTR_RO(ccadata_aes_256, sizeof(struct secaeskeytoken));
+static BIN_ATTR_RO(ccadata_aes_128_xts, 2 * sizeof(struct secaeskeytoken));
+static BIN_ATTR_RO(ccadata_aes_256_xts, 2 * sizeof(struct secaeskeytoken));
+
+static struct bin_attribute *ccadata_attrs[] = {
+	&bin_attr_ccadata_aes_128,
+	&bin_attr_ccadata_aes_192,
+	&bin_attr_ccadata_aes_256,
+	&bin_attr_ccadata_aes_128_xts,
+	&bin_attr_ccadata_aes_256_xts,
+	NULL
+};
+
+static struct attribute_group ccadata_attr_group = {
+	.name	   = "ccadata",
+	.bin_attrs = ccadata_attrs,
+};
+
+#define CCACIPHERTOKENSIZE	(sizeof(struct cipherkeytoken) + 80)
+
+/*
+ * Sysfs attribute read function for all secure key ccacipher binary attributes.
+ * The implementation can not deal with partial reads, because a new random
+ * secure key blob is generated with each read. In case of partial reads
+ * (i.e. off != 0 or count < key blob size) -EINVAL is returned.
+ */
+static ssize_t pkey_ccacipher_aes_attr_read(enum pkey_key_size keybits,
+					    bool is_xts, char *buf, loff_t off,
+					    size_t count)
+{
+	size_t keysize;
+	int rc;
+
+	if (off != 0 || count < CCACIPHERTOKENSIZE)
+		return -EINVAL;
+	if (is_xts)
+		if (count < 2 * CCACIPHERTOKENSIZE)
+			return -EINVAL;
+
+	keysize = CCACIPHERTOKENSIZE;
+	rc = cca_gencipherkey(-1, -1, keybits, 0, buf, &keysize);
+	if (rc)
+		return rc;
+	memset(buf + keysize, 0, CCACIPHERTOKENSIZE - keysize);
+
+	if (is_xts) {
+		keysize = CCACIPHERTOKENSIZE;
+		rc = cca_gencipherkey(-1, -1, keybits, 0,
+				      buf + CCACIPHERTOKENSIZE, &keysize);
+		if (rc)
+			return rc;
+		memset(buf + CCACIPHERTOKENSIZE + keysize, 0,
+		       CCACIPHERTOKENSIZE - keysize);
+
+		return 2 * CCACIPHERTOKENSIZE;
+	}
+
+	return CCACIPHERTOKENSIZE;
+}
+
+static ssize_t ccacipher_aes_128_read(struct file *filp,
+				      struct kobject *kobj,
+				      struct bin_attribute *attr,
+				      char *buf, loff_t off,
+				      size_t count)
+{
+	return pkey_ccacipher_aes_attr_read(PKEY_SIZE_AES_128, false, buf,
+					    off, count);
+}
+
+static ssize_t ccacipher_aes_192_read(struct file *filp,
+				      struct kobject *kobj,
+				      struct bin_attribute *attr,
+				      char *buf, loff_t off,
+				      size_t count)
+{
+	return pkey_ccacipher_aes_attr_read(PKEY_SIZE_AES_192, false, buf,
+					    off, count);
+}
+
+static ssize_t ccacipher_aes_256_read(struct file *filp,
+				      struct kobject *kobj,
+				      struct bin_attribute *attr,
+				      char *buf, loff_t off,
+				      size_t count)
+{
+	return pkey_ccacipher_aes_attr_read(PKEY_SIZE_AES_256, false, buf,
+					    off, count);
+}
+
+static ssize_t ccacipher_aes_128_xts_read(struct file *filp,
+					  struct kobject *kobj,
+					  struct bin_attribute *attr,
+					  char *buf, loff_t off,
+					  size_t count)
+{
+	return pkey_ccacipher_aes_attr_read(PKEY_SIZE_AES_128, true, buf,
+					    off, count);
+}
+
+static ssize_t ccacipher_aes_256_xts_read(struct file *filp,
+					  struct kobject *kobj,
+					  struct bin_attribute *attr,
+					  char *buf, loff_t off,
+					  size_t count)
+{
+	return pkey_ccacipher_aes_attr_read(PKEY_SIZE_AES_256, true, buf,
+					    off, count);
+}
+
+static BIN_ATTR_RO(ccacipher_aes_128, CCACIPHERTOKENSIZE);
+static BIN_ATTR_RO(ccacipher_aes_192, CCACIPHERTOKENSIZE);
+static BIN_ATTR_RO(ccacipher_aes_256, CCACIPHERTOKENSIZE);
+static BIN_ATTR_RO(ccacipher_aes_128_xts, 2 * CCACIPHERTOKENSIZE);
+static BIN_ATTR_RO(ccacipher_aes_256_xts, 2 * CCACIPHERTOKENSIZE);
+
+static struct bin_attribute *ccacipher_attrs[] = {
+	&bin_attr_ccacipher_aes_128,
+	&bin_attr_ccacipher_aes_192,
+	&bin_attr_ccacipher_aes_256,
+	&bin_attr_ccacipher_aes_128_xts,
+	&bin_attr_ccacipher_aes_256_xts,
+	NULL
+};
+
+static struct attribute_group ccacipher_attr_group = {
+	.name	   = "ccacipher",
+	.bin_attrs = ccacipher_attrs,
+};
+
+static const struct attribute_group *pkey_attr_groups[] = {
+	&protkey_attr_group,
+	&ccadata_attr_group,
+	&ccacipher_attr_group,
+	NULL,
+};
+
 static const struct file_operations pkey_fops = {
 	.owner		= THIS_MODULE,
 	.open		= nonseekable_open,
@@ -1190,6 +1494,7 @@
 	.minor	= MISC_DYNAMIC_MINOR,
 	.mode	= 0666,
 	.fops	= &pkey_fops,
+	.groups = pkey_attr_groups,
 };
 
 /*
@@ -1197,15 +1502,24 @@
  */
 static int __init pkey_init(void)
 {
-	cpacf_mask_t pckmo_functions;
+	cpacf_mask_t kmc_functions;
 
-	/* check for pckmo instructions available */
+	/*
+	 * The pckmo instruction should be available - even if we don't
+	 * actually invoke it. This instruction comes with MSA 3 which
+	 * is also the minimum level for the kmc instructions which
+	 * are able to work with protected keys.
+	 */
 	if (!cpacf_query(CPACF_PCKMO, &pckmo_functions))
-		return -EOPNOTSUPP;
-	if (!cpacf_test_func(&pckmo_functions, CPACF_PCKMO_ENC_AES_128_KEY) ||
-	    !cpacf_test_func(&pckmo_functions, CPACF_PCKMO_ENC_AES_192_KEY) ||
-	    !cpacf_test_func(&pckmo_functions, CPACF_PCKMO_ENC_AES_256_KEY))
-		return -EOPNOTSUPP;
+		return -ENODEV;
+
+	/* check for kmc instructions available */
+	if (!cpacf_query(CPACF_KMC, &kmc_functions))
+		return -ENODEV;
+	if (!cpacf_test_func(&kmc_functions, CPACF_KMC_PAES_128) ||
+	    !cpacf_test_func(&kmc_functions, CPACF_KMC_PAES_192) ||
+	    !cpacf_test_func(&kmc_functions, CPACF_KMC_PAES_256))
+		return -ENODEV;
 
 	pkey_debug_init();
 
@@ -1218,9 +1532,8 @@
 static void __exit pkey_exit(void)
 {
 	misc_deregister(&pkey_dev);
-	mkvp_cache_free();
 	pkey_debug_exit();
 }
 
-module_init(pkey_init);
+module_cpu_feature_match(MSA, pkey_init);
 module_exit(pkey_exit);
diff --git a/drivers/s390/crypto/vfio_ap_drv.c b/drivers/s390/crypto/vfio_ap_drv.c
new file mode 100644
index 0000000..be2520c
--- /dev/null
+++ b/drivers/s390/crypto/vfio_ap_drv.c
@@ -0,0 +1,218 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * VFIO based AP device driver
+ *
+ * Copyright IBM Corp. 2018
+ *
+ * Author(s): Tony Krowiak <akrowiak@linux.ibm.com>
+ *	      Pierre Morel <pmorel@linux.ibm.com>
+ */
+
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <asm/facility.h>
+#include "vfio_ap_private.h"
+
+#define VFIO_AP_ROOT_NAME "vfio_ap"
+#define VFIO_AP_DEV_NAME "matrix"
+
+MODULE_AUTHOR("IBM Corporation");
+MODULE_DESCRIPTION("VFIO AP device driver, Copyright IBM Corp. 2018");
+MODULE_LICENSE("GPL v2");
+
+static struct ap_driver vfio_ap_drv;
+
+struct ap_matrix_dev *matrix_dev;
+
+/* Only type 10 adapters (CEX4 and later) are supported
+ * by the AP matrix device driver
+ */
+static struct ap_device_id ap_queue_ids[] = {
+	{ .dev_type = AP_DEVICE_TYPE_CEX4,
+	  .match_flags = AP_DEVICE_ID_MATCH_QUEUE_TYPE },
+	{ .dev_type = AP_DEVICE_TYPE_CEX5,
+	  .match_flags = AP_DEVICE_ID_MATCH_QUEUE_TYPE },
+	{ .dev_type = AP_DEVICE_TYPE_CEX6,
+	  .match_flags = AP_DEVICE_ID_MATCH_QUEUE_TYPE },
+	{ .dev_type = AP_DEVICE_TYPE_CEX7,
+	  .match_flags = AP_DEVICE_ID_MATCH_QUEUE_TYPE },
+	{ /* end of sibling */ },
+};
+
+MODULE_DEVICE_TABLE(vfio_ap, ap_queue_ids);
+
+/**
+ * vfio_ap_queue_dev_probe:
+ *
+ * Allocate a vfio_ap_queue structure and associate it
+ * with the device as driver_data.
+ */
+static int vfio_ap_queue_dev_probe(struct ap_device *apdev)
+{
+	struct vfio_ap_queue *q;
+
+	q = kzalloc(sizeof(*q), GFP_KERNEL);
+	if (!q)
+		return -ENOMEM;
+	dev_set_drvdata(&apdev->device, q);
+	q->apqn = to_ap_queue(&apdev->device)->qid;
+	q->saved_isc = VFIO_AP_ISC_INVALID;
+	return 0;
+}
+
+/**
+ * vfio_ap_queue_dev_remove:
+ *
+ * Takes the matrix lock to avoid actions on this device while removing
+ * Free the associated vfio_ap_queue structure
+ */
+static void vfio_ap_queue_dev_remove(struct ap_device *apdev)
+{
+	struct vfio_ap_queue *q;
+	int apid, apqi;
+
+	mutex_lock(&matrix_dev->lock);
+	q = dev_get_drvdata(&apdev->device);
+	dev_set_drvdata(&apdev->device, NULL);
+	apid = AP_QID_CARD(q->apqn);
+	apqi = AP_QID_QUEUE(q->apqn);
+	vfio_ap_mdev_reset_queue(apid, apqi, 1);
+	vfio_ap_irq_disable(q);
+	kfree(q);
+	mutex_unlock(&matrix_dev->lock);
+}
+
+static void vfio_ap_matrix_dev_release(struct device *dev)
+{
+	struct ap_matrix_dev *matrix_dev = dev_get_drvdata(dev);
+
+	kfree(matrix_dev);
+}
+
+static int matrix_bus_match(struct device *dev, struct device_driver *drv)
+{
+	return 1;
+}
+
+static struct bus_type matrix_bus = {
+	.name = "matrix",
+	.match = &matrix_bus_match,
+};
+
+static struct device_driver matrix_driver = {
+	.name = "vfio_ap",
+	.bus = &matrix_bus,
+	.suppress_bind_attrs = true,
+};
+
+static int vfio_ap_matrix_dev_create(void)
+{
+	int ret;
+	struct device *root_device;
+
+	root_device = root_device_register(VFIO_AP_ROOT_NAME);
+	if (IS_ERR(root_device))
+		return PTR_ERR(root_device);
+
+	ret = bus_register(&matrix_bus);
+	if (ret)
+		goto bus_register_err;
+
+	matrix_dev = kzalloc(sizeof(*matrix_dev), GFP_KERNEL);
+	if (!matrix_dev) {
+		ret = -ENOMEM;
+		goto matrix_alloc_err;
+	}
+
+	/* Fill in config info via PQAP(QCI), if available */
+	if (test_facility(12)) {
+		ret = ap_qci(&matrix_dev->info);
+		if (ret)
+			goto matrix_alloc_err;
+	}
+
+	mutex_init(&matrix_dev->lock);
+	INIT_LIST_HEAD(&matrix_dev->mdev_list);
+
+	dev_set_name(&matrix_dev->device, "%s", VFIO_AP_DEV_NAME);
+	matrix_dev->device.parent = root_device;
+	matrix_dev->device.bus = &matrix_bus;
+	matrix_dev->device.release = vfio_ap_matrix_dev_release;
+	matrix_dev->vfio_ap_drv = &vfio_ap_drv;
+
+	ret = device_register(&matrix_dev->device);
+	if (ret)
+		goto matrix_reg_err;
+
+	ret = driver_register(&matrix_driver);
+	if (ret)
+		goto matrix_drv_err;
+
+	return 0;
+
+matrix_drv_err:
+	device_unregister(&matrix_dev->device);
+matrix_reg_err:
+	put_device(&matrix_dev->device);
+matrix_alloc_err:
+	bus_unregister(&matrix_bus);
+bus_register_err:
+	root_device_unregister(root_device);
+	return ret;
+}
+
+static void vfio_ap_matrix_dev_destroy(void)
+{
+	struct device *root_device = matrix_dev->device.parent;
+
+	driver_unregister(&matrix_driver);
+	device_unregister(&matrix_dev->device);
+	bus_unregister(&matrix_bus);
+	root_device_unregister(root_device);
+}
+
+static int __init vfio_ap_init(void)
+{
+	int ret;
+
+	/* If there are no AP instructions, there is nothing to pass through. */
+	if (!ap_instructions_available())
+		return -ENODEV;
+
+	ret = vfio_ap_matrix_dev_create();
+	if (ret)
+		return ret;
+
+	memset(&vfio_ap_drv, 0, sizeof(vfio_ap_drv));
+	vfio_ap_drv.probe = vfio_ap_queue_dev_probe;
+	vfio_ap_drv.remove = vfio_ap_queue_dev_remove;
+	vfio_ap_drv.ids = ap_queue_ids;
+
+	ret = ap_driver_register(&vfio_ap_drv, THIS_MODULE, VFIO_AP_DRV_NAME);
+	if (ret) {
+		vfio_ap_matrix_dev_destroy();
+		return ret;
+	}
+
+	ret = vfio_ap_mdev_register();
+	if (ret) {
+		ap_driver_unregister(&vfio_ap_drv);
+		vfio_ap_matrix_dev_destroy();
+
+		return ret;
+	}
+
+	return 0;
+}
+
+static void __exit vfio_ap_exit(void)
+{
+	vfio_ap_mdev_unregister();
+	ap_driver_unregister(&vfio_ap_drv);
+	vfio_ap_matrix_dev_destroy();
+}
+
+module_init(vfio_ap_init);
+module_exit(vfio_ap_exit);
diff --git a/drivers/s390/crypto/vfio_ap_ops.c b/drivers/s390/crypto/vfio_ap_ops.c
new file mode 100644
index 0000000..5c0f53c
--- /dev/null
+++ b/drivers/s390/crypto/vfio_ap_ops.c
@@ -0,0 +1,1304 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Adjunct processor matrix VFIO device driver callbacks.
+ *
+ * Copyright IBM Corp. 2018
+ *
+ * Author(s): Tony Krowiak <akrowiak@linux.ibm.com>
+ *	      Halil Pasic <pasic@linux.ibm.com>
+ *	      Pierre Morel <pmorel@linux.ibm.com>
+ */
+#include <linux/string.h>
+#include <linux/vfio.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/ctype.h>
+#include <linux/bitops.h>
+#include <linux/kvm_host.h>
+#include <linux/module.h>
+#include <asm/kvm.h>
+#include <asm/zcrypt.h>
+
+#include "vfio_ap_private.h"
+
+#define VFIO_AP_MDEV_TYPE_HWVIRT "passthrough"
+#define VFIO_AP_MDEV_NAME_HWVIRT "VFIO AP Passthrough Device"
+
+static int vfio_ap_mdev_reset_queues(struct mdev_device *mdev);
+
+static int match_apqn(struct device *dev, const void *data)
+{
+	struct vfio_ap_queue *q = dev_get_drvdata(dev);
+
+	return (q->apqn == *(int *)(data)) ? 1 : 0;
+}
+
+/**
+ * vfio_ap_get_queue: Retrieve a queue with a specific APQN from a list
+ * @matrix_mdev: the associated mediated matrix
+ * @apqn: The queue APQN
+ *
+ * Retrieve a queue with a specific APQN from the list of the
+ * devices of the vfio_ap_drv.
+ * Verify that the APID and the APQI are set in the matrix.
+ *
+ * Returns the pointer to the associated vfio_ap_queue
+ */
+static struct vfio_ap_queue *vfio_ap_get_queue(
+					struct ap_matrix_mdev *matrix_mdev,
+					int apqn)
+{
+	struct vfio_ap_queue *q;
+	struct device *dev;
+
+	if (!test_bit_inv(AP_QID_CARD(apqn), matrix_mdev->matrix.apm))
+		return NULL;
+	if (!test_bit_inv(AP_QID_QUEUE(apqn), matrix_mdev->matrix.aqm))
+		return NULL;
+
+	dev = driver_find_device(&matrix_dev->vfio_ap_drv->driver, NULL,
+				 &apqn, match_apqn);
+	if (!dev)
+		return NULL;
+	q = dev_get_drvdata(dev);
+	q->matrix_mdev = matrix_mdev;
+	put_device(dev);
+
+	return q;
+}
+
+/**
+ * vfio_ap_wait_for_irqclear
+ * @apqn: The AP Queue number
+ *
+ * Checks the IRQ bit for the status of this APQN using ap_tapq.
+ * Returns if the ap_tapq function succeeded and the bit is clear.
+ * Returns if ap_tapq function failed with invalid, deconfigured or
+ * checkstopped AP.
+ * Otherwise retries up to 5 times after waiting 20ms.
+ *
+ */
+static void vfio_ap_wait_for_irqclear(int apqn)
+{
+	struct ap_queue_status status;
+	int retry = 5;
+
+	do {
+		status = ap_tapq(apqn, NULL);
+		switch (status.response_code) {
+		case AP_RESPONSE_NORMAL:
+		case AP_RESPONSE_RESET_IN_PROGRESS:
+			if (!status.irq_enabled)
+				return;
+			/* Fall through */
+		case AP_RESPONSE_BUSY:
+			msleep(20);
+			break;
+		case AP_RESPONSE_Q_NOT_AVAIL:
+		case AP_RESPONSE_DECONFIGURED:
+		case AP_RESPONSE_CHECKSTOPPED:
+		default:
+			WARN_ONCE(1, "%s: tapq rc %02x: %04x\n", __func__,
+				  status.response_code, apqn);
+			return;
+		}
+	} while (--retry);
+
+	WARN_ONCE(1, "%s: tapq rc %02x: %04x could not clear IR bit\n",
+		  __func__, status.response_code, apqn);
+}
+
+/**
+ * vfio_ap_free_aqic_resources
+ * @q: The vfio_ap_queue
+ *
+ * Unregisters the ISC in the GIB when the saved ISC not invalid.
+ * Unpin the guest's page holding the NIB when it exist.
+ * Reset the saved_pfn and saved_isc to invalid values.
+ *
+ */
+static void vfio_ap_free_aqic_resources(struct vfio_ap_queue *q)
+{
+	if (q->saved_isc != VFIO_AP_ISC_INVALID && q->matrix_mdev)
+		kvm_s390_gisc_unregister(q->matrix_mdev->kvm, q->saved_isc);
+	if (q->saved_pfn && q->matrix_mdev)
+		vfio_unpin_pages(mdev_dev(q->matrix_mdev->mdev),
+				 &q->saved_pfn, 1);
+	q->saved_pfn = 0;
+	q->saved_isc = VFIO_AP_ISC_INVALID;
+}
+
+/**
+ * vfio_ap_irq_disable
+ * @q: The vfio_ap_queue
+ *
+ * Uses ap_aqic to disable the interruption and in case of success, reset
+ * in progress or IRQ disable command already proceeded: calls
+ * vfio_ap_wait_for_irqclear() to check for the IRQ bit to be clear
+ * and calls vfio_ap_free_aqic_resources() to free the resources associated
+ * with the AP interrupt handling.
+ *
+ * In the case the AP is busy, or a reset is in progress,
+ * retries after 20ms, up to 5 times.
+ *
+ * Returns if ap_aqic function failed with invalid, deconfigured or
+ * checkstopped AP.
+ */
+struct ap_queue_status vfio_ap_irq_disable(struct vfio_ap_queue *q)
+{
+	struct ap_qirq_ctrl aqic_gisa = {};
+	struct ap_queue_status status;
+	int retries = 5;
+
+	do {
+		status = ap_aqic(q->apqn, aqic_gisa, NULL);
+		switch (status.response_code) {
+		case AP_RESPONSE_OTHERWISE_CHANGED:
+		case AP_RESPONSE_NORMAL:
+			vfio_ap_wait_for_irqclear(q->apqn);
+			goto end_free;
+		case AP_RESPONSE_RESET_IN_PROGRESS:
+		case AP_RESPONSE_BUSY:
+			msleep(20);
+			break;
+		case AP_RESPONSE_Q_NOT_AVAIL:
+		case AP_RESPONSE_DECONFIGURED:
+		case AP_RESPONSE_CHECKSTOPPED:
+		case AP_RESPONSE_INVALID_ADDRESS:
+		default:
+			/* All cases in default means AP not operational */
+			WARN_ONCE(1, "%s: ap_aqic status %d\n", __func__,
+				  status.response_code);
+			goto end_free;
+		}
+	} while (retries--);
+
+	WARN_ONCE(1, "%s: ap_aqic status %d\n", __func__,
+		  status.response_code);
+end_free:
+	vfio_ap_free_aqic_resources(q);
+	q->matrix_mdev = NULL;
+	return status;
+}
+
+/**
+ * vfio_ap_setirq: Enable Interruption for a APQN
+ *
+ * @dev: the device associated with the ap_queue
+ * @q:	 the vfio_ap_queue holding AQIC parameters
+ *
+ * Pin the NIB saved in *q
+ * Register the guest ISC to GIB interface and retrieve the
+ * host ISC to issue the host side PQAP/AQIC
+ *
+ * Response.status may be set to AP_RESPONSE_INVALID_ADDRESS in case the
+ * vfio_pin_pages failed.
+ *
+ * Otherwise return the ap_queue_status returned by the ap_aqic(),
+ * all retry handling will be done by the guest.
+ */
+static struct ap_queue_status vfio_ap_irq_enable(struct vfio_ap_queue *q,
+						 int isc,
+						 unsigned long nib)
+{
+	struct ap_qirq_ctrl aqic_gisa = {};
+	struct ap_queue_status status = {};
+	struct kvm_s390_gisa *gisa;
+	struct kvm *kvm;
+	unsigned long h_nib, g_pfn, h_pfn;
+	int ret;
+
+	g_pfn = nib >> PAGE_SHIFT;
+	ret = vfio_pin_pages(mdev_dev(q->matrix_mdev->mdev), &g_pfn, 1,
+			     IOMMU_READ | IOMMU_WRITE, &h_pfn);
+	switch (ret) {
+	case 1:
+		break;
+	default:
+		status.response_code = AP_RESPONSE_INVALID_ADDRESS;
+		return status;
+	}
+
+	kvm = q->matrix_mdev->kvm;
+	gisa = kvm->arch.gisa_int.origin;
+
+	h_nib = (h_pfn << PAGE_SHIFT) | (nib & ~PAGE_MASK);
+	aqic_gisa.gisc = isc;
+	aqic_gisa.isc = kvm_s390_gisc_register(kvm, isc);
+	aqic_gisa.ir = 1;
+	aqic_gisa.gisa = (uint64_t)gisa >> 4;
+
+	status = ap_aqic(q->apqn, aqic_gisa, (void *)h_nib);
+	switch (status.response_code) {
+	case AP_RESPONSE_NORMAL:
+		/* See if we did clear older IRQ configuration */
+		vfio_ap_free_aqic_resources(q);
+		q->saved_pfn = g_pfn;
+		q->saved_isc = isc;
+		break;
+	case AP_RESPONSE_OTHERWISE_CHANGED:
+		/* We could not modify IRQ setings: clear new configuration */
+		vfio_unpin_pages(mdev_dev(q->matrix_mdev->mdev), &g_pfn, 1);
+		kvm_s390_gisc_unregister(kvm, isc);
+		break;
+	default:
+		pr_warn("%s: apqn %04x: response: %02x\n", __func__, q->apqn,
+			status.response_code);
+		vfio_ap_irq_disable(q);
+		break;
+	}
+
+	return status;
+}
+
+/**
+ * handle_pqap: PQAP instruction callback
+ *
+ * @vcpu: The vcpu on which we received the PQAP instruction
+ *
+ * Get the general register contents to initialize internal variables.
+ * REG[0]: APQN
+ * REG[1]: IR and ISC
+ * REG[2]: NIB
+ *
+ * Response.status may be set to following Response Code:
+ * - AP_RESPONSE_Q_NOT_AVAIL: if the queue is not available
+ * - AP_RESPONSE_DECONFIGURED: if the queue is not configured
+ * - AP_RESPONSE_NORMAL (0) : in case of successs
+ *   Check vfio_ap_setirq() and vfio_ap_clrirq() for other possible RC.
+ * We take the matrix_dev lock to ensure serialization on queues and
+ * mediated device access.
+ *
+ * Return 0 if we could handle the request inside KVM.
+ * otherwise, returns -EOPNOTSUPP to let QEMU handle the fault.
+ */
+static int handle_pqap(struct kvm_vcpu *vcpu)
+{
+	uint64_t status;
+	uint16_t apqn;
+	struct vfio_ap_queue *q;
+	struct ap_queue_status qstatus = {
+			       .response_code = AP_RESPONSE_Q_NOT_AVAIL, };
+	struct ap_matrix_mdev *matrix_mdev;
+
+	/* If we do not use the AIV facility just go to userland */
+	if (!(vcpu->arch.sie_block->eca & ECA_AIV))
+		return -EOPNOTSUPP;
+
+	apqn = vcpu->run->s.regs.gprs[0] & 0xffff;
+	mutex_lock(&matrix_dev->lock);
+
+	if (!vcpu->kvm->arch.crypto.pqap_hook)
+		goto out_unlock;
+	matrix_mdev = container_of(vcpu->kvm->arch.crypto.pqap_hook,
+				   struct ap_matrix_mdev, pqap_hook);
+
+	q = vfio_ap_get_queue(matrix_mdev, apqn);
+	if (!q)
+		goto out_unlock;
+
+	status = vcpu->run->s.regs.gprs[1];
+
+	/* If IR bit(16) is set we enable the interrupt */
+	if ((status >> (63 - 16)) & 0x01)
+		qstatus = vfio_ap_irq_enable(q, status & 0x07,
+					     vcpu->run->s.regs.gprs[2]);
+	else
+		qstatus = vfio_ap_irq_disable(q);
+
+out_unlock:
+	memcpy(&vcpu->run->s.regs.gprs[1], &qstatus, sizeof(qstatus));
+	vcpu->run->s.regs.gprs[1] >>= 32;
+	mutex_unlock(&matrix_dev->lock);
+	return 0;
+}
+
+static void vfio_ap_matrix_init(struct ap_config_info *info,
+				struct ap_matrix *matrix)
+{
+	matrix->apm_max = info->apxa ? info->Na : 63;
+	matrix->aqm_max = info->apxa ? info->Nd : 15;
+	matrix->adm_max = info->apxa ? info->Nd : 15;
+}
+
+static int vfio_ap_mdev_create(struct kobject *kobj, struct mdev_device *mdev)
+{
+	struct ap_matrix_mdev *matrix_mdev;
+
+	if ((atomic_dec_if_positive(&matrix_dev->available_instances) < 0))
+		return -EPERM;
+
+	matrix_mdev = kzalloc(sizeof(*matrix_mdev), GFP_KERNEL);
+	if (!matrix_mdev) {
+		atomic_inc(&matrix_dev->available_instances);
+		return -ENOMEM;
+	}
+
+	matrix_mdev->mdev = mdev;
+	vfio_ap_matrix_init(&matrix_dev->info, &matrix_mdev->matrix);
+	mdev_set_drvdata(mdev, matrix_mdev);
+	matrix_mdev->pqap_hook.hook = handle_pqap;
+	matrix_mdev->pqap_hook.owner = THIS_MODULE;
+	mutex_lock(&matrix_dev->lock);
+	list_add(&matrix_mdev->node, &matrix_dev->mdev_list);
+	mutex_unlock(&matrix_dev->lock);
+
+	return 0;
+}
+
+static int vfio_ap_mdev_remove(struct mdev_device *mdev)
+{
+	struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
+
+	if (matrix_mdev->kvm)
+		return -EBUSY;
+
+	mutex_lock(&matrix_dev->lock);
+	vfio_ap_mdev_reset_queues(mdev);
+	list_del(&matrix_mdev->node);
+	mutex_unlock(&matrix_dev->lock);
+
+	kfree(matrix_mdev);
+	mdev_set_drvdata(mdev, NULL);
+	atomic_inc(&matrix_dev->available_instances);
+
+	return 0;
+}
+
+static ssize_t name_show(struct kobject *kobj, struct device *dev, char *buf)
+{
+	return sprintf(buf, "%s\n", VFIO_AP_MDEV_NAME_HWVIRT);
+}
+
+static MDEV_TYPE_ATTR_RO(name);
+
+static ssize_t available_instances_show(struct kobject *kobj,
+					struct device *dev, char *buf)
+{
+	return sprintf(buf, "%d\n",
+		       atomic_read(&matrix_dev->available_instances));
+}
+
+static MDEV_TYPE_ATTR_RO(available_instances);
+
+static ssize_t device_api_show(struct kobject *kobj, struct device *dev,
+			       char *buf)
+{
+	return sprintf(buf, "%s\n", VFIO_DEVICE_API_AP_STRING);
+}
+
+static MDEV_TYPE_ATTR_RO(device_api);
+
+static struct attribute *vfio_ap_mdev_type_attrs[] = {
+	&mdev_type_attr_name.attr,
+	&mdev_type_attr_device_api.attr,
+	&mdev_type_attr_available_instances.attr,
+	NULL,
+};
+
+static struct attribute_group vfio_ap_mdev_hwvirt_type_group = {
+	.name = VFIO_AP_MDEV_TYPE_HWVIRT,
+	.attrs = vfio_ap_mdev_type_attrs,
+};
+
+static struct attribute_group *vfio_ap_mdev_type_groups[] = {
+	&vfio_ap_mdev_hwvirt_type_group,
+	NULL,
+};
+
+struct vfio_ap_queue_reserved {
+	unsigned long *apid;
+	unsigned long *apqi;
+	bool reserved;
+};
+
+/**
+ * vfio_ap_has_queue
+ *
+ * @dev: an AP queue device
+ * @data: a struct vfio_ap_queue_reserved reference
+ *
+ * Flags whether the AP queue device (@dev) has a queue ID containing the APQN,
+ * apid or apqi specified in @data:
+ *
+ * - If @data contains both an apid and apqi value, then @data will be flagged
+ *   as reserved if the APID and APQI fields for the AP queue device matches
+ *
+ * - If @data contains only an apid value, @data will be flagged as
+ *   reserved if the APID field in the AP queue device matches
+ *
+ * - If @data contains only an apqi value, @data will be flagged as
+ *   reserved if the APQI field in the AP queue device matches
+ *
+ * Returns 0 to indicate the input to function succeeded. Returns -EINVAL if
+ * @data does not contain either an apid or apqi.
+ */
+static int vfio_ap_has_queue(struct device *dev, void *data)
+{
+	struct vfio_ap_queue_reserved *qres = data;
+	struct ap_queue *ap_queue = to_ap_queue(dev);
+	ap_qid_t qid;
+	unsigned long id;
+
+	if (qres->apid && qres->apqi) {
+		qid = AP_MKQID(*qres->apid, *qres->apqi);
+		if (qid == ap_queue->qid)
+			qres->reserved = true;
+	} else if (qres->apid && !qres->apqi) {
+		id = AP_QID_CARD(ap_queue->qid);
+		if (id == *qres->apid)
+			qres->reserved = true;
+	} else if (!qres->apid && qres->apqi) {
+		id = AP_QID_QUEUE(ap_queue->qid);
+		if (id == *qres->apqi)
+			qres->reserved = true;
+	} else {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * vfio_ap_verify_queue_reserved
+ *
+ * @matrix_dev: a mediated matrix device
+ * @apid: an AP adapter ID
+ * @apqi: an AP queue index
+ *
+ * Verifies that the AP queue with @apid/@apqi is reserved by the VFIO AP device
+ * driver according to the following rules:
+ *
+ * - If both @apid and @apqi are not NULL, then there must be an AP queue
+ *   device bound to the vfio_ap driver with the APQN identified by @apid and
+ *   @apqi
+ *
+ * - If only @apid is not NULL, then there must be an AP queue device bound
+ *   to the vfio_ap driver with an APQN containing @apid
+ *
+ * - If only @apqi is not NULL, then there must be an AP queue device bound
+ *   to the vfio_ap driver with an APQN containing @apqi
+ *
+ * Returns 0 if the AP queue is reserved; otherwise, returns -EADDRNOTAVAIL.
+ */
+static int vfio_ap_verify_queue_reserved(unsigned long *apid,
+					 unsigned long *apqi)
+{
+	int ret;
+	struct vfio_ap_queue_reserved qres;
+
+	qres.apid = apid;
+	qres.apqi = apqi;
+	qres.reserved = false;
+
+	ret = driver_for_each_device(&matrix_dev->vfio_ap_drv->driver, NULL,
+				     &qres, vfio_ap_has_queue);
+	if (ret)
+		return ret;
+
+	if (qres.reserved)
+		return 0;
+
+	return -EADDRNOTAVAIL;
+}
+
+static int
+vfio_ap_mdev_verify_queues_reserved_for_apid(struct ap_matrix_mdev *matrix_mdev,
+					     unsigned long apid)
+{
+	int ret;
+	unsigned long apqi;
+	unsigned long nbits = matrix_mdev->matrix.aqm_max + 1;
+
+	if (find_first_bit_inv(matrix_mdev->matrix.aqm, nbits) >= nbits)
+		return vfio_ap_verify_queue_reserved(&apid, NULL);
+
+	for_each_set_bit_inv(apqi, matrix_mdev->matrix.aqm, nbits) {
+		ret = vfio_ap_verify_queue_reserved(&apid, &apqi);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * vfio_ap_mdev_verify_no_sharing
+ *
+ * Verifies that the APQNs derived from the cross product of the AP adapter IDs
+ * and AP queue indexes comprising the AP matrix are not configured for another
+ * mediated device. AP queue sharing is not allowed.
+ *
+ * @matrix_mdev: the mediated matrix device
+ *
+ * Returns 0 if the APQNs are not shared, otherwise; returns -EADDRINUSE.
+ */
+static int vfio_ap_mdev_verify_no_sharing(struct ap_matrix_mdev *matrix_mdev)
+{
+	struct ap_matrix_mdev *lstdev;
+	DECLARE_BITMAP(apm, AP_DEVICES);
+	DECLARE_BITMAP(aqm, AP_DOMAINS);
+
+	list_for_each_entry(lstdev, &matrix_dev->mdev_list, node) {
+		if (matrix_mdev == lstdev)
+			continue;
+
+		memset(apm, 0, sizeof(apm));
+		memset(aqm, 0, sizeof(aqm));
+
+		/*
+		 * We work on full longs, as we can only exclude the leftover
+		 * bits in non-inverse order. The leftover is all zeros.
+		 */
+		if (!bitmap_and(apm, matrix_mdev->matrix.apm,
+				lstdev->matrix.apm, AP_DEVICES))
+			continue;
+
+		if (!bitmap_and(aqm, matrix_mdev->matrix.aqm,
+				lstdev->matrix.aqm, AP_DOMAINS))
+			continue;
+
+		return -EADDRINUSE;
+	}
+
+	return 0;
+}
+
+/**
+ * assign_adapter_store
+ *
+ * @dev:	the matrix device
+ * @attr:	the mediated matrix device's assign_adapter attribute
+ * @buf:	a buffer containing the AP adapter number (APID) to
+ *		be assigned
+ * @count:	the number of bytes in @buf
+ *
+ * Parses the APID from @buf and sets the corresponding bit in the mediated
+ * matrix device's APM.
+ *
+ * Returns the number of bytes processed if the APID is valid; otherwise,
+ * returns one of the following errors:
+ *
+ *	1. -EINVAL
+ *	   The APID is not a valid number
+ *
+ *	2. -ENODEV
+ *	   The APID exceeds the maximum value configured for the system
+ *
+ *	3. -EADDRNOTAVAIL
+ *	   An APQN derived from the cross product of the APID being assigned
+ *	   and the APQIs previously assigned is not bound to the vfio_ap device
+ *	   driver; or, if no APQIs have yet been assigned, the APID is not
+ *	   contained in an APQN bound to the vfio_ap device driver.
+ *
+ *	4. -EADDRINUSE
+ *	   An APQN derived from the cross product of the APID being assigned
+ *	   and the APQIs previously assigned is being used by another mediated
+ *	   matrix device
+ */
+static ssize_t assign_adapter_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t count)
+{
+	int ret;
+	unsigned long apid;
+	struct mdev_device *mdev = mdev_from_dev(dev);
+	struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
+
+	/* If the guest is running, disallow assignment of adapter */
+	if (matrix_mdev->kvm)
+		return -EBUSY;
+
+	ret = kstrtoul(buf, 0, &apid);
+	if (ret)
+		return ret;
+
+	if (apid > matrix_mdev->matrix.apm_max)
+		return -ENODEV;
+
+	/*
+	 * Set the bit in the AP mask (APM) corresponding to the AP adapter
+	 * number (APID). The bits in the mask, from most significant to least
+	 * significant bit, correspond to APIDs 0-255.
+	 */
+	mutex_lock(&matrix_dev->lock);
+
+	ret = vfio_ap_mdev_verify_queues_reserved_for_apid(matrix_mdev, apid);
+	if (ret)
+		goto done;
+
+	set_bit_inv(apid, matrix_mdev->matrix.apm);
+
+	ret = vfio_ap_mdev_verify_no_sharing(matrix_mdev);
+	if (ret)
+		goto share_err;
+
+	ret = count;
+	goto done;
+
+share_err:
+	clear_bit_inv(apid, matrix_mdev->matrix.apm);
+done:
+	mutex_unlock(&matrix_dev->lock);
+
+	return ret;
+}
+static DEVICE_ATTR_WO(assign_adapter);
+
+/**
+ * unassign_adapter_store
+ *
+ * @dev:	the matrix device
+ * @attr:	the mediated matrix device's unassign_adapter attribute
+ * @buf:	a buffer containing the adapter number (APID) to be unassigned
+ * @count:	the number of bytes in @buf
+ *
+ * Parses the APID from @buf and clears the corresponding bit in the mediated
+ * matrix device's APM.
+ *
+ * Returns the number of bytes processed if the APID is valid; otherwise,
+ * returns one of the following errors:
+ *	-EINVAL if the APID is not a number
+ *	-ENODEV if the APID it exceeds the maximum value configured for the
+ *		system
+ */
+static ssize_t unassign_adapter_store(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf, size_t count)
+{
+	int ret;
+	unsigned long apid;
+	struct mdev_device *mdev = mdev_from_dev(dev);
+	struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
+
+	/* If the guest is running, disallow un-assignment of adapter */
+	if (matrix_mdev->kvm)
+		return -EBUSY;
+
+	ret = kstrtoul(buf, 0, &apid);
+	if (ret)
+		return ret;
+
+	if (apid > matrix_mdev->matrix.apm_max)
+		return -ENODEV;
+
+	mutex_lock(&matrix_dev->lock);
+	clear_bit_inv((unsigned long)apid, matrix_mdev->matrix.apm);
+	mutex_unlock(&matrix_dev->lock);
+
+	return count;
+}
+static DEVICE_ATTR_WO(unassign_adapter);
+
+static int
+vfio_ap_mdev_verify_queues_reserved_for_apqi(struct ap_matrix_mdev *matrix_mdev,
+					     unsigned long apqi)
+{
+	int ret;
+	unsigned long apid;
+	unsigned long nbits = matrix_mdev->matrix.apm_max + 1;
+
+	if (find_first_bit_inv(matrix_mdev->matrix.apm, nbits) >= nbits)
+		return vfio_ap_verify_queue_reserved(NULL, &apqi);
+
+	for_each_set_bit_inv(apid, matrix_mdev->matrix.apm, nbits) {
+		ret = vfio_ap_verify_queue_reserved(&apid, &apqi);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * assign_domain_store
+ *
+ * @dev:	the matrix device
+ * @attr:	the mediated matrix device's assign_domain attribute
+ * @buf:	a buffer containing the AP queue index (APQI) of the domain to
+ *		be assigned
+ * @count:	the number of bytes in @buf
+ *
+ * Parses the APQI from @buf and sets the corresponding bit in the mediated
+ * matrix device's AQM.
+ *
+ * Returns the number of bytes processed if the APQI is valid; otherwise returns
+ * one of the following errors:
+ *
+ *	1. -EINVAL
+ *	   The APQI is not a valid number
+ *
+ *	2. -ENODEV
+ *	   The APQI exceeds the maximum value configured for the system
+ *
+ *	3. -EADDRNOTAVAIL
+ *	   An APQN derived from the cross product of the APQI being assigned
+ *	   and the APIDs previously assigned is not bound to the vfio_ap device
+ *	   driver; or, if no APIDs have yet been assigned, the APQI is not
+ *	   contained in an APQN bound to the vfio_ap device driver.
+ *
+ *	4. -EADDRINUSE
+ *	   An APQN derived from the cross product of the APQI being assigned
+ *	   and the APIDs previously assigned is being used by another mediated
+ *	   matrix device
+ */
+static ssize_t assign_domain_store(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t count)
+{
+	int ret;
+	unsigned long apqi;
+	struct mdev_device *mdev = mdev_from_dev(dev);
+	struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
+	unsigned long max_apqi = matrix_mdev->matrix.aqm_max;
+
+	/* If the guest is running, disallow assignment of domain */
+	if (matrix_mdev->kvm)
+		return -EBUSY;
+
+	ret = kstrtoul(buf, 0, &apqi);
+	if (ret)
+		return ret;
+	if (apqi > max_apqi)
+		return -ENODEV;
+
+	mutex_lock(&matrix_dev->lock);
+
+	ret = vfio_ap_mdev_verify_queues_reserved_for_apqi(matrix_mdev, apqi);
+	if (ret)
+		goto done;
+
+	set_bit_inv(apqi, matrix_mdev->matrix.aqm);
+
+	ret = vfio_ap_mdev_verify_no_sharing(matrix_mdev);
+	if (ret)
+		goto share_err;
+
+	ret = count;
+	goto done;
+
+share_err:
+	clear_bit_inv(apqi, matrix_mdev->matrix.aqm);
+done:
+	mutex_unlock(&matrix_dev->lock);
+
+	return ret;
+}
+static DEVICE_ATTR_WO(assign_domain);
+
+
+/**
+ * unassign_domain_store
+ *
+ * @dev:	the matrix device
+ * @attr:	the mediated matrix device's unassign_domain attribute
+ * @buf:	a buffer containing the AP queue index (APQI) of the domain to
+ *		be unassigned
+ * @count:	the number of bytes in @buf
+ *
+ * Parses the APQI from @buf and clears the corresponding bit in the
+ * mediated matrix device's AQM.
+ *
+ * Returns the number of bytes processed if the APQI is valid; otherwise,
+ * returns one of the following errors:
+ *	-EINVAL if the APQI is not a number
+ *	-ENODEV if the APQI exceeds the maximum value configured for the system
+ */
+static ssize_t unassign_domain_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	int ret;
+	unsigned long apqi;
+	struct mdev_device *mdev = mdev_from_dev(dev);
+	struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
+
+	/* If the guest is running, disallow un-assignment of domain */
+	if (matrix_mdev->kvm)
+		return -EBUSY;
+
+	ret = kstrtoul(buf, 0, &apqi);
+	if (ret)
+		return ret;
+
+	if (apqi > matrix_mdev->matrix.aqm_max)
+		return -ENODEV;
+
+	mutex_lock(&matrix_dev->lock);
+	clear_bit_inv((unsigned long)apqi, matrix_mdev->matrix.aqm);
+	mutex_unlock(&matrix_dev->lock);
+
+	return count;
+}
+static DEVICE_ATTR_WO(unassign_domain);
+
+/**
+ * assign_control_domain_store
+ *
+ * @dev:	the matrix device
+ * @attr:	the mediated matrix device's assign_control_domain attribute
+ * @buf:	a buffer containing the domain ID to be assigned
+ * @count:	the number of bytes in @buf
+ *
+ * Parses the domain ID from @buf and sets the corresponding bit in the mediated
+ * matrix device's ADM.
+ *
+ * Returns the number of bytes processed if the domain ID is valid; otherwise,
+ * returns one of the following errors:
+ *	-EINVAL if the ID is not a number
+ *	-ENODEV if the ID exceeds the maximum value configured for the system
+ */
+static ssize_t assign_control_domain_store(struct device *dev,
+					   struct device_attribute *attr,
+					   const char *buf, size_t count)
+{
+	int ret;
+	unsigned long id;
+	struct mdev_device *mdev = mdev_from_dev(dev);
+	struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
+
+	/* If the guest is running, disallow assignment of control domain */
+	if (matrix_mdev->kvm)
+		return -EBUSY;
+
+	ret = kstrtoul(buf, 0, &id);
+	if (ret)
+		return ret;
+
+	if (id > matrix_mdev->matrix.adm_max)
+		return -ENODEV;
+
+	/* Set the bit in the ADM (bitmask) corresponding to the AP control
+	 * domain number (id). The bits in the mask, from most significant to
+	 * least significant, correspond to IDs 0 up to the one less than the
+	 * number of control domains that can be assigned.
+	 */
+	mutex_lock(&matrix_dev->lock);
+	set_bit_inv(id, matrix_mdev->matrix.adm);
+	mutex_unlock(&matrix_dev->lock);
+
+	return count;
+}
+static DEVICE_ATTR_WO(assign_control_domain);
+
+/**
+ * unassign_control_domain_store
+ *
+ * @dev:	the matrix device
+ * @attr:	the mediated matrix device's unassign_control_domain attribute
+ * @buf:	a buffer containing the domain ID to be unassigned
+ * @count:	the number of bytes in @buf
+ *
+ * Parses the domain ID from @buf and clears the corresponding bit in the
+ * mediated matrix device's ADM.
+ *
+ * Returns the number of bytes processed if the domain ID is valid; otherwise,
+ * returns one of the following errors:
+ *	-EINVAL if the ID is not a number
+ *	-ENODEV if the ID exceeds the maximum value configured for the system
+ */
+static ssize_t unassign_control_domain_store(struct device *dev,
+					     struct device_attribute *attr,
+					     const char *buf, size_t count)
+{
+	int ret;
+	unsigned long domid;
+	struct mdev_device *mdev = mdev_from_dev(dev);
+	struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
+	unsigned long max_domid =  matrix_mdev->matrix.adm_max;
+
+	/* If the guest is running, disallow un-assignment of control domain */
+	if (matrix_mdev->kvm)
+		return -EBUSY;
+
+	ret = kstrtoul(buf, 0, &domid);
+	if (ret)
+		return ret;
+	if (domid > max_domid)
+		return -ENODEV;
+
+	mutex_lock(&matrix_dev->lock);
+	clear_bit_inv(domid, matrix_mdev->matrix.adm);
+	mutex_unlock(&matrix_dev->lock);
+
+	return count;
+}
+static DEVICE_ATTR_WO(unassign_control_domain);
+
+static ssize_t control_domains_show(struct device *dev,
+				    struct device_attribute *dev_attr,
+				    char *buf)
+{
+	unsigned long id;
+	int nchars = 0;
+	int n;
+	char *bufpos = buf;
+	struct mdev_device *mdev = mdev_from_dev(dev);
+	struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
+	unsigned long max_domid = matrix_mdev->matrix.adm_max;
+
+	mutex_lock(&matrix_dev->lock);
+	for_each_set_bit_inv(id, matrix_mdev->matrix.adm, max_domid + 1) {
+		n = sprintf(bufpos, "%04lx\n", id);
+		bufpos += n;
+		nchars += n;
+	}
+	mutex_unlock(&matrix_dev->lock);
+
+	return nchars;
+}
+static DEVICE_ATTR_RO(control_domains);
+
+static ssize_t matrix_show(struct device *dev, struct device_attribute *attr,
+			   char *buf)
+{
+	struct mdev_device *mdev = mdev_from_dev(dev);
+	struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
+	char *bufpos = buf;
+	unsigned long apid;
+	unsigned long apqi;
+	unsigned long apid1;
+	unsigned long apqi1;
+	unsigned long napm_bits = matrix_mdev->matrix.apm_max + 1;
+	unsigned long naqm_bits = matrix_mdev->matrix.aqm_max + 1;
+	int nchars = 0;
+	int n;
+
+	apid1 = find_first_bit_inv(matrix_mdev->matrix.apm, napm_bits);
+	apqi1 = find_first_bit_inv(matrix_mdev->matrix.aqm, naqm_bits);
+
+	mutex_lock(&matrix_dev->lock);
+
+	if ((apid1 < napm_bits) && (apqi1 < naqm_bits)) {
+		for_each_set_bit_inv(apid, matrix_mdev->matrix.apm, napm_bits) {
+			for_each_set_bit_inv(apqi, matrix_mdev->matrix.aqm,
+					     naqm_bits) {
+				n = sprintf(bufpos, "%02lx.%04lx\n", apid,
+					    apqi);
+				bufpos += n;
+				nchars += n;
+			}
+		}
+	} else if (apid1 < napm_bits) {
+		for_each_set_bit_inv(apid, matrix_mdev->matrix.apm, napm_bits) {
+			n = sprintf(bufpos, "%02lx.\n", apid);
+			bufpos += n;
+			nchars += n;
+		}
+	} else if (apqi1 < naqm_bits) {
+		for_each_set_bit_inv(apqi, matrix_mdev->matrix.aqm, naqm_bits) {
+			n = sprintf(bufpos, ".%04lx\n", apqi);
+			bufpos += n;
+			nchars += n;
+		}
+	}
+
+	mutex_unlock(&matrix_dev->lock);
+
+	return nchars;
+}
+static DEVICE_ATTR_RO(matrix);
+
+static struct attribute *vfio_ap_mdev_attrs[] = {
+	&dev_attr_assign_adapter.attr,
+	&dev_attr_unassign_adapter.attr,
+	&dev_attr_assign_domain.attr,
+	&dev_attr_unassign_domain.attr,
+	&dev_attr_assign_control_domain.attr,
+	&dev_attr_unassign_control_domain.attr,
+	&dev_attr_control_domains.attr,
+	&dev_attr_matrix.attr,
+	NULL,
+};
+
+static struct attribute_group vfio_ap_mdev_attr_group = {
+	.attrs = vfio_ap_mdev_attrs
+};
+
+static const struct attribute_group *vfio_ap_mdev_attr_groups[] = {
+	&vfio_ap_mdev_attr_group,
+	NULL
+};
+
+/**
+ * vfio_ap_mdev_set_kvm
+ *
+ * @matrix_mdev: a mediated matrix device
+ * @kvm: reference to KVM instance
+ *
+ * Verifies no other mediated matrix device has @kvm and sets a reference to
+ * it in @matrix_mdev->kvm.
+ *
+ * Return 0 if no other mediated matrix device has a reference to @kvm;
+ * otherwise, returns an -EPERM.
+ */
+static int vfio_ap_mdev_set_kvm(struct ap_matrix_mdev *matrix_mdev,
+				struct kvm *kvm)
+{
+	struct ap_matrix_mdev *m;
+
+	mutex_lock(&matrix_dev->lock);
+
+	list_for_each_entry(m, &matrix_dev->mdev_list, node) {
+		if ((m != matrix_mdev) && (m->kvm == kvm)) {
+			mutex_unlock(&matrix_dev->lock);
+			return -EPERM;
+		}
+	}
+
+	matrix_mdev->kvm = kvm;
+	kvm_get_kvm(kvm);
+	kvm->arch.crypto.pqap_hook = &matrix_mdev->pqap_hook;
+	mutex_unlock(&matrix_dev->lock);
+
+	return 0;
+}
+
+/*
+ * vfio_ap_mdev_iommu_notifier: IOMMU notifier callback
+ *
+ * @nb: The notifier block
+ * @action: Action to be taken
+ * @data: data associated with the request
+ *
+ * For an UNMAP request, unpin the guest IOVA (the NIB guest address we
+ * pinned before). Other requests are ignored.
+ *
+ */
+static int vfio_ap_mdev_iommu_notifier(struct notifier_block *nb,
+				       unsigned long action, void *data)
+{
+	struct ap_matrix_mdev *matrix_mdev;
+
+	matrix_mdev = container_of(nb, struct ap_matrix_mdev, iommu_notifier);
+
+	if (action == VFIO_IOMMU_NOTIFY_DMA_UNMAP) {
+		struct vfio_iommu_type1_dma_unmap *unmap = data;
+		unsigned long g_pfn = unmap->iova >> PAGE_SHIFT;
+
+		vfio_unpin_pages(mdev_dev(matrix_mdev->mdev), &g_pfn, 1);
+		return NOTIFY_OK;
+	}
+
+	return NOTIFY_DONE;
+}
+
+static int vfio_ap_mdev_group_notifier(struct notifier_block *nb,
+				       unsigned long action, void *data)
+{
+	int ret;
+	struct ap_matrix_mdev *matrix_mdev;
+
+	if (action != VFIO_GROUP_NOTIFY_SET_KVM)
+		return NOTIFY_OK;
+
+	matrix_mdev = container_of(nb, struct ap_matrix_mdev, group_notifier);
+
+	if (!data) {
+		matrix_mdev->kvm = NULL;
+		return NOTIFY_OK;
+	}
+
+	ret = vfio_ap_mdev_set_kvm(matrix_mdev, data);
+	if (ret)
+		return NOTIFY_DONE;
+
+	/* If there is no CRYCB pointer, then we can't copy the masks */
+	if (!matrix_mdev->kvm->arch.crypto.crycbd)
+		return NOTIFY_DONE;
+
+	kvm_arch_crypto_set_masks(matrix_mdev->kvm, matrix_mdev->matrix.apm,
+				  matrix_mdev->matrix.aqm,
+				  matrix_mdev->matrix.adm);
+
+	return NOTIFY_OK;
+}
+
+static void vfio_ap_irq_disable_apqn(int apqn)
+{
+	struct device *dev;
+	struct vfio_ap_queue *q;
+
+	dev = driver_find_device(&matrix_dev->vfio_ap_drv->driver, NULL,
+				 &apqn, match_apqn);
+	if (dev) {
+		q = dev_get_drvdata(dev);
+		vfio_ap_irq_disable(q);
+		put_device(dev);
+	}
+}
+
+int vfio_ap_mdev_reset_queue(unsigned int apid, unsigned int apqi,
+			     unsigned int retry)
+{
+	struct ap_queue_status status;
+	int retry2 = 2;
+	int apqn = AP_MKQID(apid, apqi);
+
+	do {
+		status = ap_zapq(apqn);
+		switch (status.response_code) {
+		case AP_RESPONSE_NORMAL:
+			while (!status.queue_empty && retry2--) {
+				msleep(20);
+				status = ap_tapq(apqn, NULL);
+			}
+			WARN_ON_ONCE(retry2 <= 0);
+			return 0;
+		case AP_RESPONSE_RESET_IN_PROGRESS:
+		case AP_RESPONSE_BUSY:
+			msleep(20);
+			break;
+		default:
+			/* things are really broken, give up */
+			return -EIO;
+		}
+	} while (retry--);
+
+	return -EBUSY;
+}
+
+static int vfio_ap_mdev_reset_queues(struct mdev_device *mdev)
+{
+	int ret;
+	int rc = 0;
+	unsigned long apid, apqi;
+	struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
+
+	for_each_set_bit_inv(apid, matrix_mdev->matrix.apm,
+			     matrix_mdev->matrix.apm_max + 1) {
+		for_each_set_bit_inv(apqi, matrix_mdev->matrix.aqm,
+				     matrix_mdev->matrix.aqm_max + 1) {
+			ret = vfio_ap_mdev_reset_queue(apid, apqi, 1);
+			/*
+			 * Regardless whether a queue turns out to be busy, or
+			 * is not operational, we need to continue resetting
+			 * the remaining queues.
+			 */
+			if (ret)
+				rc = ret;
+			vfio_ap_irq_disable_apqn(AP_MKQID(apid, apqi));
+		}
+	}
+
+	return rc;
+}
+
+static int vfio_ap_mdev_open(struct mdev_device *mdev)
+{
+	struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
+	unsigned long events;
+	int ret;
+
+
+	if (!try_module_get(THIS_MODULE))
+		return -ENODEV;
+
+	matrix_mdev->group_notifier.notifier_call = vfio_ap_mdev_group_notifier;
+	events = VFIO_GROUP_NOTIFY_SET_KVM;
+
+	ret = vfio_register_notifier(mdev_dev(mdev), VFIO_GROUP_NOTIFY,
+				     &events, &matrix_mdev->group_notifier);
+	if (ret) {
+		module_put(THIS_MODULE);
+		return ret;
+	}
+
+	matrix_mdev->iommu_notifier.notifier_call = vfio_ap_mdev_iommu_notifier;
+	events = VFIO_IOMMU_NOTIFY_DMA_UNMAP;
+	ret = vfio_register_notifier(mdev_dev(mdev), VFIO_IOMMU_NOTIFY,
+				     &events, &matrix_mdev->iommu_notifier);
+	if (!ret)
+		return ret;
+
+	vfio_unregister_notifier(mdev_dev(mdev), VFIO_GROUP_NOTIFY,
+				 &matrix_mdev->group_notifier);
+	module_put(THIS_MODULE);
+	return ret;
+}
+
+static void vfio_ap_mdev_release(struct mdev_device *mdev)
+{
+	struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
+
+	mutex_lock(&matrix_dev->lock);
+	if (matrix_mdev->kvm) {
+		kvm_arch_crypto_clear_masks(matrix_mdev->kvm);
+		matrix_mdev->kvm->arch.crypto.pqap_hook = NULL;
+		vfio_ap_mdev_reset_queues(mdev);
+		kvm_put_kvm(matrix_mdev->kvm);
+		matrix_mdev->kvm = NULL;
+	}
+	mutex_unlock(&matrix_dev->lock);
+
+	vfio_unregister_notifier(mdev_dev(mdev), VFIO_IOMMU_NOTIFY,
+				 &matrix_mdev->iommu_notifier);
+	vfio_unregister_notifier(mdev_dev(mdev), VFIO_GROUP_NOTIFY,
+				 &matrix_mdev->group_notifier);
+	module_put(THIS_MODULE);
+}
+
+static int vfio_ap_mdev_get_device_info(unsigned long arg)
+{
+	unsigned long minsz;
+	struct vfio_device_info info;
+
+	minsz = offsetofend(struct vfio_device_info, num_irqs);
+
+	if (copy_from_user(&info, (void __user *)arg, minsz))
+		return -EFAULT;
+
+	if (info.argsz < minsz)
+		return -EINVAL;
+
+	info.flags = VFIO_DEVICE_FLAGS_AP | VFIO_DEVICE_FLAGS_RESET;
+	info.num_regions = 0;
+	info.num_irqs = 0;
+
+	return copy_to_user((void __user *)arg, &info, minsz);
+}
+
+static ssize_t vfio_ap_mdev_ioctl(struct mdev_device *mdev,
+				    unsigned int cmd, unsigned long arg)
+{
+	int ret;
+
+	mutex_lock(&matrix_dev->lock);
+	switch (cmd) {
+	case VFIO_DEVICE_GET_INFO:
+		ret = vfio_ap_mdev_get_device_info(arg);
+		break;
+	case VFIO_DEVICE_RESET:
+		ret = vfio_ap_mdev_reset_queues(mdev);
+		break;
+	default:
+		ret = -EOPNOTSUPP;
+		break;
+	}
+	mutex_unlock(&matrix_dev->lock);
+
+	return ret;
+}
+
+static const struct mdev_parent_ops vfio_ap_matrix_ops = {
+	.owner			= THIS_MODULE,
+	.supported_type_groups	= vfio_ap_mdev_type_groups,
+	.mdev_attr_groups	= vfio_ap_mdev_attr_groups,
+	.create			= vfio_ap_mdev_create,
+	.remove			= vfio_ap_mdev_remove,
+	.open			= vfio_ap_mdev_open,
+	.release		= vfio_ap_mdev_release,
+	.ioctl			= vfio_ap_mdev_ioctl,
+};
+
+int vfio_ap_mdev_register(void)
+{
+	atomic_set(&matrix_dev->available_instances, MAX_ZDEV_ENTRIES_EXT);
+
+	return mdev_register_device(&matrix_dev->device, &vfio_ap_matrix_ops);
+}
+
+void vfio_ap_mdev_unregister(void)
+{
+	mdev_unregister_device(&matrix_dev->device);
+}
diff --git a/drivers/s390/crypto/vfio_ap_private.h b/drivers/s390/crypto/vfio_ap_private.h
new file mode 100644
index 0000000..f46dde5
--- /dev/null
+++ b/drivers/s390/crypto/vfio_ap_private.h
@@ -0,0 +1,104 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Private data and functions for adjunct processor VFIO matrix driver.
+ *
+ * Author(s): Tony Krowiak <akrowiak@linux.ibm.com>
+ *	      Halil Pasic <pasic@linux.ibm.com>
+ *	      Pierre Morel <pmorel@linux.ibm.com>
+ *
+ * Copyright IBM Corp. 2018
+ */
+
+#ifndef _VFIO_AP_PRIVATE_H_
+#define _VFIO_AP_PRIVATE_H_
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/mdev.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/kvm_host.h>
+
+#include "ap_bus.h"
+
+#define VFIO_AP_MODULE_NAME "vfio_ap"
+#define VFIO_AP_DRV_NAME "vfio_ap"
+
+/**
+ * ap_matrix_dev - the AP matrix device structure
+ * @device:	generic device structure associated with the AP matrix device
+ * @available_instances: number of mediated matrix devices that can be created
+ * @info:	the struct containing the output from the PQAP(QCI) instruction
+ * mdev_list:	the list of mediated matrix devices created
+ * lock:	mutex for locking the AP matrix device. This lock will be
+ *		taken every time we fiddle with state managed by the vfio_ap
+ *		driver, be it using @mdev_list or writing the state of a
+ *		single ap_matrix_mdev device. It's quite coarse but we don't
+ *		expect much contention.
+ */
+struct ap_matrix_dev {
+	struct device device;
+	atomic_t available_instances;
+	struct ap_config_info info;
+	struct list_head mdev_list;
+	struct mutex lock;
+	struct ap_driver  *vfio_ap_drv;
+};
+
+extern struct ap_matrix_dev *matrix_dev;
+
+/**
+ * The AP matrix is comprised of three bit masks identifying the adapters,
+ * queues (domains) and control domains that belong to an AP matrix. The bits i
+ * each mask, from least significant to most significant bit, correspond to IDs
+ * 0 to 255. When a bit is set, the corresponding ID belongs to the matrix.
+ *
+ * @apm_max: max adapter number in @apm
+ * @apm identifies the AP adapters in the matrix
+ * @aqm_max: max domain number in @aqm
+ * @aqm identifies the AP queues (domains) in the matrix
+ * @adm_max: max domain number in @adm
+ * @adm identifies the AP control domains in the matrix
+ */
+struct ap_matrix {
+	unsigned long apm_max;
+	DECLARE_BITMAP(apm, 256);
+	unsigned long aqm_max;
+	DECLARE_BITMAP(aqm, 256);
+	unsigned long adm_max;
+	DECLARE_BITMAP(adm, 256);
+};
+
+/**
+ * struct ap_matrix_mdev - the mediated matrix device structure
+ * @list:	allows the ap_matrix_mdev struct to be added to a list
+ * @matrix:	the adapters, usage domains and control domains assigned to the
+ *		mediated matrix device.
+ * @group_notifier: notifier block used for specifying callback function for
+ *		    handling the VFIO_GROUP_NOTIFY_SET_KVM event
+ * @kvm:	the struct holding guest's state
+ */
+struct ap_matrix_mdev {
+	struct list_head node;
+	struct ap_matrix matrix;
+	struct notifier_block group_notifier;
+	struct notifier_block iommu_notifier;
+	struct kvm *kvm;
+	struct kvm_s390_module_hook pqap_hook;
+	struct mdev_device *mdev;
+};
+
+extern int vfio_ap_mdev_register(void);
+extern void vfio_ap_mdev_unregister(void);
+int vfio_ap_mdev_reset_queue(unsigned int apid, unsigned int apqi,
+			     unsigned int retry);
+
+struct vfio_ap_queue {
+	struct ap_matrix_mdev *matrix_mdev;
+	unsigned long saved_pfn;
+	int	apqn;
+#define VFIO_AP_ISC_INVALID 0xff
+	unsigned char saved_isc;
+};
+struct ap_queue_status vfio_ap_irq_disable(struct vfio_ap_queue *q);
+#endif /* _VFIO_AP_PRIVATE_H_ */
diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c
index e685412..9157e72 100644
--- a/drivers/s390/crypto/zcrypt_api.c
+++ b/drivers/s390/crypto/zcrypt_api.c
@@ -1,8 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- *  zcrypt 2.1.0
- *
- *  Copyright IBM Corp. 2001, 2012
+ *  Copyright IBM Corp. 2001, 2018
  *  Author(s): Robert Burroughs
  *	       Eric Rossman (edrossma@us.ibm.com)
  *	       Cornelia Huck <cornelia.huck@de.ibm.com>
@@ -11,6 +9,7 @@
  *  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>
+ *  Multiple device nodes: Harald Freudenberger <freude@linux.ibm.com>
  */
 
 #include <linux/module.h>
@@ -24,6 +23,8 @@
 #include <linux/uaccess.h>
 #include <linux/hw_random.h>
 #include <linux/debugfs.h>
+#include <linux/cdev.h>
+#include <linux/ctype.h>
 #include <asm/debug.h>
 
 #define CREATE_TRACE_POINTS
@@ -34,6 +35,7 @@
 
 #include "zcrypt_msgtype6.h"
 #include "zcrypt_msgtype50.h"
+#include "zcrypt_ccamisc.h"
 
 /*
  * Module description.
@@ -108,6 +110,357 @@
 }
 EXPORT_SYMBOL(zcrypt_msgtype);
 
+/*
+ * Multi device nodes extension functions.
+ */
+
+#ifdef CONFIG_ZCRYPT_MULTIDEVNODES
+
+struct zcdn_device;
+
+static struct class *zcrypt_class;
+static dev_t zcrypt_devt;
+static struct cdev zcrypt_cdev;
+
+struct zcdn_device {
+	struct device device;
+	struct ap_perms perms;
+};
+
+#define to_zcdn_dev(x) container_of((x), struct zcdn_device, device)
+
+#define ZCDN_MAX_NAME 32
+
+static int zcdn_create(const char *name);
+static int zcdn_destroy(const char *name);
+
+/*
+ * Find zcdn device by name.
+ * Returns reference to the zcdn device which needs to be released
+ * with put_device() after use.
+ */
+static inline struct zcdn_device *find_zcdndev_by_name(const char *name)
+{
+	struct device *dev = class_find_device_by_name(zcrypt_class, name);
+
+	return dev ? to_zcdn_dev(dev) : NULL;
+}
+
+/*
+ * Find zcdn device by devt value.
+ * Returns reference to the zcdn device which needs to be released
+ * with put_device() after use.
+ */
+static inline struct zcdn_device *find_zcdndev_by_devt(dev_t devt)
+{
+	struct device *dev = class_find_device_by_devt(zcrypt_class, devt);
+
+	return dev ? to_zcdn_dev(dev) : NULL;
+}
+
+static ssize_t ioctlmask_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.ioctlm) / sizeof(long); i++)
+		snprintf(buf + 2 + 2 * i * sizeof(long),
+			 PAGE_SIZE - 2 - 2 * i * sizeof(long),
+			 "%016lx", zcdndev->perms.ioctlm[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 ioctlmask_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.ioctlm,
+			       AP_IOCTLS, &ap_perms_mutex);
+	if (rc)
+		return rc;
+
+	return count;
+}
+
+static DEVICE_ATTR_RW(ioctlmask);
+
+static ssize_t apmask_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.apm) / sizeof(long); i++)
+		snprintf(buf + 2 + 2 * i * sizeof(long),
+			 PAGE_SIZE - 2 - 2 * i * sizeof(long),
+			 "%016lx", zcdndev->perms.apm[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 apmask_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.apm,
+			       AP_DEVICES, &ap_perms_mutex);
+	if (rc)
+		return rc;
+
+	return count;
+}
+
+static DEVICE_ATTR_RW(apmask);
+
+static ssize_t aqmask_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.aqm) / sizeof(long); i++)
+		snprintf(buf + 2 + 2 * i * sizeof(long),
+			 PAGE_SIZE - 2 - 2 * i * sizeof(long),
+			 "%016lx", zcdndev->perms.aqm[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 aqmask_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.aqm,
+			       AP_DOMAINS, &ap_perms_mutex);
+	if (rc)
+		return rc;
+
+	return count;
+}
+
+static DEVICE_ATTR_RW(aqmask);
+
+static struct attribute *zcdn_dev_attrs[] = {
+	&dev_attr_ioctlmask.attr,
+	&dev_attr_apmask.attr,
+	&dev_attr_aqmask.attr,
+	NULL
+};
+
+static struct attribute_group zcdn_dev_attr_group = {
+	.attrs = zcdn_dev_attrs
+};
+
+static const struct attribute_group *zcdn_dev_attr_groups[] = {
+	&zcdn_dev_attr_group,
+	NULL
+};
+
+static ssize_t zcdn_create_store(struct class *class,
+				 struct class_attribute *attr,
+				 const char *buf, size_t count)
+{
+	int rc;
+	char name[ZCDN_MAX_NAME];
+
+	strncpy(name, skip_spaces(buf), sizeof(name));
+	name[sizeof(name) - 1] = '\0';
+
+	rc = zcdn_create(strim(name));
+
+	return rc ? rc : count;
+}
+
+static const struct class_attribute class_attr_zcdn_create =
+	__ATTR(create, 0600, NULL, zcdn_create_store);
+
+static ssize_t zcdn_destroy_store(struct class *class,
+				  struct class_attribute *attr,
+				  const char *buf, size_t count)
+{
+	int rc;
+	char name[ZCDN_MAX_NAME];
+
+	strncpy(name, skip_spaces(buf), sizeof(name));
+	name[sizeof(name) - 1] = '\0';
+
+	rc = zcdn_destroy(strim(name));
+
+	return rc ? rc : count;
+}
+
+static const struct class_attribute class_attr_zcdn_destroy =
+	__ATTR(destroy, 0600, NULL, zcdn_destroy_store);
+
+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));
+
+	kfree(zcdndev);
+}
+
+static int zcdn_create(const char *name)
+{
+	dev_t devt;
+	int i, rc = 0;
+	char nodename[ZCDN_MAX_NAME];
+	struct zcdn_device *zcdndev;
+
+	if (mutex_lock_interruptible(&ap_perms_mutex))
+		return -ERESTARTSYS;
+
+	/* check if device node with this name already exists */
+	if (name[0]) {
+		zcdndev = find_zcdndev_by_name(name);
+		if (zcdndev) {
+			put_device(&zcdndev->device);
+			rc = -EEXIST;
+			goto unlockout;
+		}
+	}
+
+	/* find an unused minor number */
+	for (i = 0; i < ZCRYPT_MAX_MINOR_NODES; i++) {
+		devt = MKDEV(MAJOR(zcrypt_devt), MINOR(zcrypt_devt) + i);
+		zcdndev = find_zcdndev_by_devt(devt);
+		if (zcdndev)
+			put_device(&zcdndev->device);
+		else
+			break;
+	}
+	if (i == ZCRYPT_MAX_MINOR_NODES) {
+		rc = -ENOSPC;
+		goto unlockout;
+	}
+
+	/* alloc and prepare a new zcdn device */
+	zcdndev = kzalloc(sizeof(*zcdndev), GFP_KERNEL);
+	if (!zcdndev) {
+		rc = -ENOMEM;
+		goto unlockout;
+	}
+	zcdndev->device.release = zcdn_device_release;
+	zcdndev->device.class = zcrypt_class;
+	zcdndev->device.devt = devt;
+	zcdndev->device.groups = zcdn_dev_attr_groups;
+	if (name[0])
+		strncpy(nodename, name, sizeof(nodename));
+	else
+		snprintf(nodename, sizeof(nodename),
+			 ZCRYPT_NAME "_%d", (int) MINOR(devt));
+	nodename[sizeof(nodename)-1] = '\0';
+	if (dev_set_name(&zcdndev->device, nodename)) {
+		rc = -EINVAL;
+		goto unlockout;
+	}
+	rc = device_register(&zcdndev->device);
+	if (rc) {
+		put_device(&zcdndev->device);
+		goto unlockout;
+	}
+
+	ZCRYPT_DBF(DBF_INFO, "created zcdn device %d:%d\n",
+		   MAJOR(devt), MINOR(devt));
+
+unlockout:
+	mutex_unlock(&ap_perms_mutex);
+	return rc;
+}
+
+static int zcdn_destroy(const char *name)
+{
+	int rc = 0;
+	struct zcdn_device *zcdndev;
+
+	if (mutex_lock_interruptible(&ap_perms_mutex))
+		return -ERESTARTSYS;
+
+	/* try to find this zcdn device */
+	zcdndev = find_zcdndev_by_name(name);
+	if (!zcdndev) {
+		rc = -ENOENT;
+		goto unlockout;
+	}
+
+	/*
+	 * The zcdn device is not hard destroyed. It is subject to
+	 * reference counting and thus just needs to be unregistered.
+	 */
+	put_device(&zcdndev->device);
+	device_unregister(&zcdndev->device);
+
+unlockout:
+	mutex_unlock(&ap_perms_mutex);
+	return rc;
+}
+
+static void zcdn_destroy_all(void)
+{
+	int i;
+	dev_t devt;
+	struct zcdn_device *zcdndev;
+
+	mutex_lock(&ap_perms_mutex);
+	for (i = 0; i < ZCRYPT_MAX_MINOR_NODES; i++) {
+		devt = MKDEV(MAJOR(zcrypt_devt), MINOR(zcrypt_devt) + i);
+		zcdndev = find_zcdndev_by_devt(devt);
+		if (zcdndev) {
+			put_device(&zcdndev->device);
+			device_unregister(&zcdndev->device);
+		}
+	}
+	mutex_unlock(&ap_perms_mutex);
+}
+
+#endif
+
 /**
  * zcrypt_read (): Not supported beyond zcrypt 1.3.1.
  *
@@ -137,8 +490,25 @@
  */
 static int zcrypt_open(struct inode *inode, struct file *filp)
 {
+	struct ap_perms *perms = &ap_perms;
+
+#ifdef CONFIG_ZCRYPT_MULTIDEVNODES
+	if (filp->f_inode->i_cdev == &zcrypt_cdev) {
+		struct zcdn_device *zcdndev;
+
+		if (mutex_lock_interruptible(&ap_perms_mutex))
+			return -ERESTARTSYS;
+		zcdndev = find_zcdndev_by_devt(filp->f_inode->i_rdev);
+		/* find returns a reference, no get_device() needed */
+		mutex_unlock(&ap_perms_mutex);
+		if (zcdndev)
+			perms = &zcdndev->perms;
+	}
+#endif
+	filp->private_data = (void *) perms;
+
 	atomic_inc(&zcrypt_open_count);
-	return nonseekable_open(inode, filp);
+	return stream_open(inode, filp);
 }
 
 /**
@@ -148,12 +518,57 @@
  */
 static int zcrypt_release(struct inode *inode, struct file *filp)
 {
+#ifdef CONFIG_ZCRYPT_MULTIDEVNODES
+	if (filp->f_inode->i_cdev == &zcrypt_cdev) {
+		struct zcdn_device *zcdndev;
+
+		mutex_lock(&ap_perms_mutex);
+		zcdndev = find_zcdndev_by_devt(filp->f_inode->i_rdev);
+		mutex_unlock(&ap_perms_mutex);
+		if (zcdndev) {
+			/* 2 puts here: one for find, one for open */
+			put_device(&zcdndev->device);
+			put_device(&zcdndev->device);
+		}
+	}
+#endif
+
 	atomic_dec(&zcrypt_open_count);
 	return 0;
 }
 
+static inline int zcrypt_check_ioctl(struct ap_perms *perms,
+				     unsigned int cmd)
+{
+	int rc = -EPERM;
+	int ioctlnr = (cmd & _IOC_NRMASK) >> _IOC_NRSHIFT;
+
+	if (ioctlnr > 0 && ioctlnr < AP_IOCTLS) {
+		if (test_bit_inv(ioctlnr, perms->ioctlm))
+			rc = 0;
+	}
+
+	if (rc)
+		ZCRYPT_DBF(DBF_WARN,
+			   "ioctl check failed: ioctlnr=0x%04x rc=%d\n",
+			   ioctlnr, rc);
+
+	return rc;
+}
+
+static inline bool zcrypt_check_card(struct ap_perms *perms, int card)
+{
+	return test_bit_inv(card, perms->apm) ? true : false;
+}
+
+static inline bool zcrypt_check_queue(struct ap_perms *perms, int queue)
+{
+	return test_bit_inv(queue, perms->aqm) ? true : false;
+}
+
 static inline struct zcrypt_queue *zcrypt_pick_queue(struct zcrypt_card *zc,
 						     struct zcrypt_queue *zq,
+						     struct module **pmod,
 						     unsigned int weight)
 {
 	if (!zq || !try_module_get(zq->queue->ap_dev.drv->driver.owner))
@@ -163,15 +578,15 @@
 	atomic_add(weight, &zc->load);
 	atomic_add(weight, &zq->load);
 	zq->request_count++;
+	*pmod = zq->queue->ap_dev.drv->driver.owner;
 	return zq;
 }
 
 static inline void zcrypt_drop_queue(struct zcrypt_card *zc,
 				     struct zcrypt_queue *zq,
+				     struct module *mod,
 				     unsigned int weight)
 {
-	struct module *mod = zq->queue->ap_dev.drv->driver.owner;
-
 	zq->request_count--;
 	atomic_sub(weight, &zc->load);
 	atomic_sub(weight, &zq->load);
@@ -213,17 +628,20 @@
 /*
  * zcrypt ioctls.
  */
-static long zcrypt_rsa_modexpo(struct ica_rsa_modexpo *mex)
+static long zcrypt_rsa_modexpo(struct ap_perms *perms,
+			       struct ica_rsa_modexpo *mex)
 {
 	struct zcrypt_card *zc, *pref_zc;
 	struct zcrypt_queue *zq, *pref_zq;
 	unsigned int weight, pref_weight;
 	unsigned int func_code;
 	int qid = 0, rc = -ENODEV;
+	struct module *mod;
 
 	trace_s390_zcrypt_req(mex, TP_ICARSAMODEXPO);
 
 	if (mex->outputdatalength < mex->inputdatalength) {
+		func_code = 0;
 		rc = -EINVAL;
 		goto out;
 	}
@@ -250,6 +668,9 @@
 		if (zc->min_mod_size > mex->inputdatalength ||
 		    zc->max_mod_size < mex->inputdatalength)
 			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 = zc->speed_rating[func_code];
 		if (zcrypt_card_compare(zc, pref_zc, weight, pref_weight))
@@ -258,6 +679,10 @@
 			/* check if device is online and eligible */
 			if (!zq->online || !zq->ops->rsa_modexpo)
 				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))
 				continue;
@@ -266,7 +691,7 @@
 			pref_weight = weight;
 		}
 	}
-	pref_zq = zcrypt_pick_queue(pref_zc, pref_zq, weight);
+	pref_zq = zcrypt_pick_queue(pref_zc, pref_zq, &mod, weight);
 	spin_unlock(&zcrypt_list_lock);
 
 	if (!pref_zq) {
@@ -278,7 +703,7 @@
 	rc = pref_zq->ops->rsa_modexpo(pref_zq, mex);
 
 	spin_lock(&zcrypt_list_lock);
-	zcrypt_drop_queue(pref_zc, pref_zq, weight);
+	zcrypt_drop_queue(pref_zc, pref_zq, mod, weight);
 	spin_unlock(&zcrypt_list_lock);
 
 out:
@@ -287,17 +712,20 @@
 	return rc;
 }
 
-static long zcrypt_rsa_crt(struct ica_rsa_modexpo_crt *crt)
+static long zcrypt_rsa_crt(struct ap_perms *perms,
+			   struct ica_rsa_modexpo_crt *crt)
 {
 	struct zcrypt_card *zc, *pref_zc;
 	struct zcrypt_queue *zq, *pref_zq;
 	unsigned int weight, pref_weight;
 	unsigned int func_code;
 	int qid = 0, rc = -ENODEV;
+	struct module *mod;
 
 	trace_s390_zcrypt_req(crt, TP_ICARSACRT);
 
 	if (crt->outputdatalength < crt->inputdatalength) {
+		func_code = 0;
 		rc = -EINVAL;
 		goto out;
 	}
@@ -324,6 +752,9 @@
 		if (zc->min_mod_size > crt->inputdatalength ||
 		    zc->max_mod_size < crt->inputdatalength)
 			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 = zc->speed_rating[func_code];
 		if (zcrypt_card_compare(zc, pref_zc, weight, pref_weight))
@@ -332,6 +763,10 @@
 			/* check if device is online and eligible */
 			if (!zq->online || !zq->ops->rsa_modexpo_crt)
 				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))
 				continue;
@@ -340,7 +775,7 @@
 			pref_weight = weight;
 		}
 	}
-	pref_zq = zcrypt_pick_queue(pref_zc, pref_zq, weight);
+	pref_zq = zcrypt_pick_queue(pref_zc, pref_zq, &mod, weight);
 	spin_unlock(&zcrypt_list_lock);
 
 	if (!pref_zq) {
@@ -352,7 +787,7 @@
 	rc = pref_zq->ops->rsa_modexpo_crt(pref_zq, crt);
 
 	spin_lock(&zcrypt_list_lock);
-	zcrypt_drop_queue(pref_zc, pref_zq, weight);
+	zcrypt_drop_queue(pref_zc, pref_zq, mod, weight);
 	spin_unlock(&zcrypt_list_lock);
 
 out:
@@ -361,23 +796,37 @@
 	return rc;
 }
 
-long zcrypt_send_cprb(struct ica_xcRB *xcRB)
+static long _zcrypt_send_cprb(struct ap_perms *perms,
+			      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 func_code;
-	unsigned short *domain;
+	unsigned short *domain, tdom;
 	int qid = 0, rc = -ENODEV;
+	struct module *mod;
 
 	trace_s390_zcrypt_req(xcRB, TB_ZSECSENDCPRB);
 
+	xcRB->status = 0;
 	ap_init_message(&ap_msg);
 	rc = get_cprb_fc(xcRB, &ap_msg, &func_code, &domain);
 	if (rc)
 		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.
+	 */
+	tdom = *domain;
+	if (tdom >= 0 && tdom < AP_DOMAINS &&
+	    !ap_test_config_usage_domain(tdom) &&
+	    ap_test_config_ctrl_domain(tdom) &&
+	    ap_domain_index >= 0)
+		tdom = ap_domain_index;
+
 	pref_zc = NULL;
 	pref_zq = NULL;
 	spin_lock(&zcrypt_list_lock);
@@ -389,6 +838,9 @@
 		if (xcRB->user_defined != AUTOSELECT &&
 		    xcRB->user_defined != zc->card->id)
 			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))
@@ -397,8 +849,12 @@
 			/* check if device is online and eligible */
 			if (!zq->online ||
 			    !zq->ops->send_cprb ||
-			    ((*domain != (unsigned short) AUTOSELECT) &&
-			     (*domain != AP_QID_QUEUE(zq->queue->qid))))
+			    (tdom != (unsigned short) AUTOSELECT &&
+			     tdom != AP_QID_QUEUE(zq->queue->qid)))
+				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))
@@ -408,7 +864,7 @@
 			pref_weight = weight;
 		}
 	}
-	pref_zq = zcrypt_pick_queue(pref_zc, pref_zq, weight);
+	pref_zq = zcrypt_pick_queue(pref_zc, pref_zq, &mod, weight);
 	spin_unlock(&zcrypt_list_lock);
 
 	if (!pref_zq) {
@@ -424,7 +880,7 @@
 	rc = pref_zq->ops->send_cprb(pref_zq, xcRB, &ap_msg);
 
 	spin_lock(&zcrypt_list_lock);
-	zcrypt_drop_queue(pref_zc, pref_zq, weight);
+	zcrypt_drop_queue(pref_zc, pref_zq, mod, weight);
 	spin_unlock(&zcrypt_list_lock);
 
 out:
@@ -433,6 +889,11 @@
 			      AP_QID_CARD(qid), AP_QID_QUEUE(qid));
 	return rc;
 }
+
+long zcrypt_send_cprb(struct ica_xcRB *xcRB)
+{
+	return _zcrypt_send_cprb(&ap_perms, xcRB);
+}
 EXPORT_SYMBOL(zcrypt_send_cprb);
 
 static bool is_desired_ep11_card(unsigned int dev_id,
@@ -459,7 +920,8 @@
 	return false;
 }
 
-static long zcrypt_send_ep11_cprb(struct ep11_urb *xcrb)
+static long zcrypt_send_ep11_cprb(struct ap_perms *perms,
+				  struct ep11_urb *xcrb)
 {
 	struct zcrypt_card *zc, *pref_zc;
 	struct zcrypt_queue *zq, *pref_zq;
@@ -469,6 +931,7 @@
 	unsigned int func_code;
 	struct ap_message ap_msg;
 	int qid = 0, rc = -ENODEV;
+	struct module *mod;
 
 	trace_s390_zcrypt_req(xcrb, TP_ZSENDEP11CPRB);
 
@@ -483,6 +946,7 @@
 
 		targets = kcalloc(target_num, sizeof(*targets), GFP_KERNEL);
 		if (!targets) {
+			func_code = 0;
 			rc = -ENOMEM;
 			goto out;
 		}
@@ -490,6 +954,7 @@
 		uptr = (struct ep11_target_dev __force __user *) xcrb->targets;
 		if (copy_from_user(targets, uptr,
 				   target_num * sizeof(*targets))) {
+			func_code = 0;
 			rc = -EFAULT;
 			goto out_free;
 		}
@@ -510,6 +975,9 @@
 		if (targets &&
 		    !is_desired_ep11_card(zc->card->id, target_num, targets))
 			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))
@@ -522,6 +990,10 @@
 			     !is_desired_ep11_queue(zq->queue->qid,
 						    target_num, targets)))
 				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))
 				continue;
@@ -530,7 +1002,7 @@
 			pref_weight = weight;
 		}
 	}
-	pref_zq = zcrypt_pick_queue(pref_zc, pref_zq, weight);
+	pref_zq = zcrypt_pick_queue(pref_zc, pref_zq, &mod, weight);
 	spin_unlock(&zcrypt_list_lock);
 
 	if (!pref_zq) {
@@ -542,7 +1014,7 @@
 	rc = pref_zq->ops->send_ep11_cprb(pref_zq, xcrb, &ap_msg);
 
 	spin_lock(&zcrypt_list_lock);
-	zcrypt_drop_queue(pref_zc, pref_zq, weight);
+	zcrypt_drop_queue(pref_zc, pref_zq, mod, weight);
 	spin_unlock(&zcrypt_list_lock);
 
 out_free:
@@ -563,6 +1035,7 @@
 	struct ap_message ap_msg;
 	unsigned int domain;
 	int qid = 0, rc = -ENODEV;
+	struct module *mod;
 
 	trace_s390_zcrypt_req(buffer, TP_HWRNGCPRB);
 
@@ -594,7 +1067,7 @@
 			pref_weight = weight;
 		}
 	}
-	pref_zq = zcrypt_pick_queue(pref_zc, pref_zq, weight);
+	pref_zq = zcrypt_pick_queue(pref_zc, pref_zq, &mod, weight);
 	spin_unlock(&zcrypt_list_lock);
 
 	if (!pref_zq) {
@@ -606,7 +1079,7 @@
 	rc = pref_zq->ops->rng(pref_zq, buffer, &ap_msg);
 
 	spin_lock(&zcrypt_list_lock);
-	zcrypt_drop_queue(pref_zc, pref_zq, weight);
+	zcrypt_drop_queue(pref_zc, pref_zq, mod, weight);
 	spin_unlock(&zcrypt_list_lock);
 
 out:
@@ -669,6 +1142,34 @@
 }
 EXPORT_SYMBOL(zcrypt_device_status_mask_ext);
 
+int zcrypt_device_status_ext(int card, int queue,
+			     struct zcrypt_device_status_ext *devstat)
+{
+	struct zcrypt_card *zc;
+	struct zcrypt_queue *zq;
+
+	memset(devstat, 0, sizeof(*devstat));
+
+	spin_lock(&zcrypt_list_lock);
+	for_each_zcrypt_card(zc) {
+		for_each_zcrypt_queue(zq, zc) {
+			if (card == AP_QID_CARD(zq->queue->qid) &&
+			    queue == AP_QID_QUEUE(zq->queue->qid)) {
+				devstat->hwtype = zc->card->ap_dev.device_type;
+				devstat->functions = zc->card->functions >> 26;
+				devstat->qid = zq->queue->qid;
+				devstat->online = zq->online ? 0x01 : 0x00;
+				spin_unlock(&zcrypt_list_lock);
+				return 0;
+			}
+		}
+	}
+	spin_unlock(&zcrypt_list_lock);
+
+	return -ENODEV;
+}
+EXPORT_SYMBOL(zcrypt_device_status_ext);
+
 static void zcrypt_status_mask(char status[], size_t max_adapters)
 {
 	struct zcrypt_card *zc;
@@ -788,7 +1289,13 @@
 static long zcrypt_unlocked_ioctl(struct file *filp, unsigned int cmd,
 				  unsigned long arg)
 {
-	int rc = 0;
+	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: {
@@ -798,12 +1305,12 @@
 		if (copy_from_user(&mex, umex, sizeof(mex)))
 			return -EFAULT;
 		do {
-			rc = zcrypt_rsa_modexpo(&mex);
+			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(&mex);
+				rc = zcrypt_rsa_modexpo(perms, &mex);
 			} while (rc == -EAGAIN);
 		if (rc) {
 			ZCRYPT_DBF(DBF_DEBUG, "ioctl ICARSAMODEXPO rc=%d\n", rc);
@@ -818,12 +1325,12 @@
 		if (copy_from_user(&crt, ucrt, sizeof(crt)))
 			return -EFAULT;
 		do {
-			rc = zcrypt_rsa_crt(&crt);
+			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(&crt);
+				rc = zcrypt_rsa_crt(perms, &crt);
 			} while (rc == -EAGAIN);
 		if (rc) {
 			ZCRYPT_DBF(DBF_DEBUG, "ioctl ICARSACRT rc=%d\n", rc);
@@ -838,15 +1345,16 @@
 		if (copy_from_user(&xcRB, uxcRB, sizeof(xcRB)))
 			return -EFAULT;
 		do {
-			rc = zcrypt_send_cprb(&xcRB);
+			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(&xcRB);
+				rc = _zcrypt_send_cprb(perms, &xcRB);
 			} while (rc == -EAGAIN);
 		if (rc)
-			ZCRYPT_DBF(DBF_DEBUG, "ioctl ZSENDCPRB rc=%d\n", 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;
 		return rc;
@@ -858,12 +1366,12 @@
 		if (copy_from_user(&xcrb, uxcrb, sizeof(xcrb)))
 			return -EFAULT;
 		do {
-			rc = zcrypt_send_ep11_cprb(&xcrb);
+			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(&xcrb);
+				rc = zcrypt_send_ep11_cprb(perms, &xcrb);
 			} while (rc == -EAGAIN);
 		if (rc)
 			ZCRYPT_DBF(DBF_DEBUG, "ioctl ZSENDEP11CPRB rc=%d\n", rc);
@@ -989,8 +1497,8 @@
 	compat_uptr_t	n_modulus;
 };
 
-static long trans_modexpo32(struct file *filp, unsigned int cmd,
-			    unsigned long arg)
+static long trans_modexpo32(struct ap_perms *perms, struct file *filp,
+			    unsigned int cmd, unsigned long arg)
 {
 	struct compat_ica_rsa_modexpo __user *umex32 = compat_ptr(arg);
 	struct compat_ica_rsa_modexpo mex32;
@@ -1006,12 +1514,12 @@
 	mex64.b_key = compat_ptr(mex32.b_key);
 	mex64.n_modulus = compat_ptr(mex32.n_modulus);
 	do {
-		rc = zcrypt_rsa_modexpo(&mex64);
+		rc = zcrypt_rsa_modexpo(perms, &mex64);
 	} while (rc == -EAGAIN);
 	/* on failure: retry once again after a requested rescan */
 	if ((rc == -ENODEV) && (zcrypt_process_rescan()))
 		do {
-			rc = zcrypt_rsa_modexpo(&mex64);
+			rc = zcrypt_rsa_modexpo(perms, &mex64);
 		} while (rc == -EAGAIN);
 	if (rc)
 		return rc;
@@ -1031,8 +1539,8 @@
 	compat_uptr_t	u_mult_inv;
 };
 
-static long trans_modexpo_crt32(struct file *filp, unsigned int cmd,
-				unsigned long arg)
+static long trans_modexpo_crt32(struct ap_perms *perms, struct file *filp,
+				unsigned int cmd, unsigned long arg)
 {
 	struct compat_ica_rsa_modexpo_crt __user *ucrt32 = compat_ptr(arg);
 	struct compat_ica_rsa_modexpo_crt crt32;
@@ -1051,12 +1559,12 @@
 	crt64.nq_prime = compat_ptr(crt32.nq_prime);
 	crt64.u_mult_inv = compat_ptr(crt32.u_mult_inv);
 	do {
-		rc = zcrypt_rsa_crt(&crt64);
+		rc = zcrypt_rsa_crt(perms, &crt64);
 	} while (rc == -EAGAIN);
 	/* on failure: retry once again after a requested rescan */
 	if ((rc == -ENODEV) && (zcrypt_process_rescan()))
 		do {
-			rc = zcrypt_rsa_crt(&crt64);
+			rc = zcrypt_rsa_crt(perms, &crt64);
 		} while (rc == -EAGAIN);
 	if (rc)
 		return rc;
@@ -1084,8 +1592,8 @@
 	unsigned int	status;
 } __packed;
 
-static long trans_xcRB32(struct file *filp, unsigned int cmd,
-			 unsigned long arg)
+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;
@@ -1115,12 +1623,12 @@
 	xcRB64.priority_window = xcRB32.priority_window;
 	xcRB64.status = xcRB32.status;
 	do {
-		rc = zcrypt_send_cprb(&xcRB64);
+		rc = _zcrypt_send_cprb(perms, &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);
+			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;
@@ -1133,12 +1641,20 @@
 static long zcrypt_compat_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;
+
 	if (cmd == ICARSAMODEXPO)
-		return trans_modexpo32(filp, cmd, arg);
+		return trans_modexpo32(perms, filp, cmd, arg);
 	if (cmd == ICARSACRT)
-		return trans_modexpo_crt32(filp, cmd, arg);
+		return trans_modexpo_crt32(perms, filp, cmd, arg);
 	if (cmd == ZSECSENDCPRB)
-		return trans_xcRB32(filp, cmd, arg);
+		return trans_xcRB32(perms, filp, cmd, arg);
 	return zcrypt_unlocked_ioctl(filp, cmd, arg);
 }
 #endif
@@ -1256,6 +1772,67 @@
 	debug_unregister(zcrypt_dbf_info);
 }
 
+#ifdef CONFIG_ZCRYPT_MULTIDEVNODES
+
+static int __init zcdn_init(void)
+{
+	int rc;
+
+	/* create a new class 'zcrypt' */
+	zcrypt_class = class_create(THIS_MODULE, ZCRYPT_NAME);
+	if (IS_ERR(zcrypt_class)) {
+		rc = PTR_ERR(zcrypt_class);
+		goto out_class_create_failed;
+	}
+	zcrypt_class->dev_release = zcdn_device_release;
+
+	/* alloc device minor range */
+	rc = alloc_chrdev_region(&zcrypt_devt,
+				 0, ZCRYPT_MAX_MINOR_NODES,
+				 ZCRYPT_NAME);
+	if (rc)
+		goto out_alloc_chrdev_failed;
+
+	cdev_init(&zcrypt_cdev, &zcrypt_fops);
+	zcrypt_cdev.owner = THIS_MODULE;
+	rc = cdev_add(&zcrypt_cdev, zcrypt_devt, ZCRYPT_MAX_MINOR_NODES);
+	if (rc)
+		goto out_cdev_add_failed;
+
+	/* need some class specific sysfs attributes */
+	rc = class_create_file(zcrypt_class, &class_attr_zcdn_create);
+	if (rc)
+		goto out_class_create_file_1_failed;
+	rc = class_create_file(zcrypt_class, &class_attr_zcdn_destroy);
+	if (rc)
+		goto out_class_create_file_2_failed;
+
+	return 0;
+
+out_class_create_file_2_failed:
+	class_remove_file(zcrypt_class, &class_attr_zcdn_create);
+out_class_create_file_1_failed:
+	cdev_del(&zcrypt_cdev);
+out_cdev_add_failed:
+	unregister_chrdev_region(zcrypt_devt, ZCRYPT_MAX_MINOR_NODES);
+out_alloc_chrdev_failed:
+	class_destroy(zcrypt_class);
+out_class_create_failed:
+	return rc;
+}
+
+static void zcdn_exit(void)
+{
+	class_remove_file(zcrypt_class, &class_attr_zcdn_create);
+	class_remove_file(zcrypt_class, &class_attr_zcdn_destroy);
+	zcdn_destroy_all();
+	cdev_del(&zcrypt_cdev);
+	unregister_chrdev_region(zcrypt_devt, ZCRYPT_MAX_MINOR_NODES);
+	class_destroy(zcrypt_class);
+}
+
+#endif
+
 /**
  * zcrypt_api_init(): Module initialization.
  *
@@ -1269,15 +1846,27 @@
 	if (rc)
 		goto out;
 
+#ifdef CONFIG_ZCRYPT_MULTIDEVNODES
+	rc = zcdn_init();
+	if (rc)
+		goto out;
+#endif
+
 	/* Register the request sprayer. */
 	rc = misc_register(&zcrypt_misc_device);
 	if (rc < 0)
-		goto out;
+		goto out_misc_register_failed;
 
 	zcrypt_msgtype6_init();
 	zcrypt_msgtype50_init();
+
 	return 0;
 
+out_misc_register_failed:
+#ifdef CONFIG_ZCRYPT_MULTIDEVNODES
+	zcdn_exit();
+#endif
+	zcrypt_debug_exit();
 out:
 	return rc;
 }
@@ -1289,9 +1878,13 @@
  */
 void __exit zcrypt_api_exit(void)
 {
+#ifdef CONFIG_ZCRYPT_MULTIDEVNODES
+	zcdn_exit();
+#endif
 	misc_deregister(&zcrypt_misc_device);
 	zcrypt_msgtype6_exit();
 	zcrypt_msgtype50_exit();
+	zcrypt_ccamisc_exit();
 	zcrypt_debug_exit();
 }
 
diff --git a/drivers/s390/crypto/zcrypt_api.h b/drivers/s390/crypto/zcrypt_api.h
index a848625..d464618 100644
--- a/drivers/s390/crypto/zcrypt_api.h
+++ b/drivers/s390/crypto/zcrypt_api.h
@@ -1,8 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0+ */
 /*
- *  zcrypt 2.1.0
- *
- *  Copyright IBM Corp. 2001, 2012
+ *  Copyright IBM Corp. 2001, 2019
  *  Author(s): Robert Burroughs
  *	       Eric Rossman (edrossma@us.ibm.com)
  *	       Cornelia Huck <cornelia.huck@de.ibm.com>
@@ -22,17 +20,8 @@
 #include "ap_bus.h"
 
 /**
- * device type for an actual device is either PCICA, PCICC, PCIXCC_MCL2,
- * PCIXCC_MCL3, CEX2C, or CEX2A
- *
- * NOTE: PCIXCC_MCL3 refers to a PCIXCC with May 2004 version of Licensed
- *	 Internal Code (LIC) (EC J12220 level 29).
- *	 PCIXCC_MCL2 refers to any LIC before this level.
+ * Supported device types
  */
-#define ZCRYPT_PCICA		1
-#define ZCRYPT_PCICC		2
-#define ZCRYPT_PCIXCC_MCL2	3
-#define ZCRYPT_PCIXCC_MCL3	4
 #define ZCRYPT_CEX2C		5
 #define ZCRYPT_CEX2A		6
 #define ZCRYPT_CEX3C		7
@@ -40,6 +29,7 @@
 #define ZCRYPT_CEX4	       10
 #define ZCRYPT_CEX5	       11
 #define ZCRYPT_CEX6	       12
+#define ZCRYPT_CEX7	       13
 
 /**
  * Large random numbers are pulled in 4096 byte chunks from the crypto cards
@@ -132,9 +122,6 @@
 int zcrypt_card_put(struct zcrypt_card *);
 int zcrypt_card_register(struct zcrypt_card *);
 void zcrypt_card_unregister(struct zcrypt_card *);
-struct zcrypt_card *zcrypt_card_get_best(unsigned int *,
-					 unsigned int, unsigned int);
-void zcrypt_card_put_best(struct zcrypt_card *, unsigned int);
 
 struct zcrypt_queue *zcrypt_queue_alloc(size_t);
 void zcrypt_queue_free(struct zcrypt_queue *);
@@ -143,8 +130,6 @@
 int zcrypt_queue_register(struct zcrypt_queue *);
 void zcrypt_queue_unregister(struct zcrypt_queue *);
 void zcrypt_queue_force_online(struct zcrypt_queue *, int);
-struct zcrypt_queue *zcrypt_queue_get_best(unsigned int, unsigned int);
-void  zcrypt_queue_put_best(struct zcrypt_queue *, unsigned int);
 
 int zcrypt_rng_device_add(void);
 void zcrypt_rng_device_remove(void);
@@ -156,5 +141,7 @@
 void zcrypt_api_exit(void);
 long zcrypt_send_cprb(struct ica_xcRB *xcRB);
 void zcrypt_device_status_mask_ext(struct zcrypt_device_status_ext *devstatus);
+int zcrypt_device_status_ext(int card, int queue,
+			     struct zcrypt_device_status_ext *devstatus);
 
 #endif /* _ZCRYPT_API_H_ */
diff --git a/drivers/s390/crypto/zcrypt_card.c b/drivers/s390/crypto/zcrypt_card.c
index 40cd4c1..d4f35a1 100644
--- a/drivers/s390/crypto/zcrypt_card.c
+++ b/drivers/s390/crypto/zcrypt_card.c
@@ -1,7 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- *  zcrypt 2.1.0
- *
  *  Copyright IBM Corp. 2001, 2012
  *  Author(s): Robert Burroughs
  *	       Eric Rossman (edrossma@us.ibm.com)
diff --git a/drivers/s390/crypto/zcrypt_cca_key.h b/drivers/s390/crypto/zcrypt_cca_key.h
index e5b5c02..f09bb85 100644
--- a/drivers/s390/crypto/zcrypt_cca_key.h
+++ b/drivers/s390/crypto/zcrypt_cca_key.h
@@ -1,7 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0+ */
 /*
- *  zcrypt 2.1.0
- *
  *  Copyright IBM Corp. 2001, 2006
  *  Author(s): Robert Burroughs
  *	       Eric Rossman (edrossma@us.ibm.com)
diff --git a/drivers/s390/crypto/zcrypt_ccamisc.c b/drivers/s390/crypto/zcrypt_ccamisc.c
new file mode 100644
index 0000000..c1db64a
--- /dev/null
+++ b/drivers/s390/crypto/zcrypt_ccamisc.c
@@ -0,0 +1,1765 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ *  Copyright IBM Corp. 2019
+ *  Author(s): Harald Freudenberger <freude@linux.ibm.com>
+ *	       Ingo Franzki <ifranzki@linux.ibm.com>
+ *
+ *  Collection of CCA misc functions used by zcrypt and pkey
+ */
+
+#define KMSG_COMPONENT "zcrypt"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <asm/zcrypt.h>
+#include <asm/pkey.h>
+
+#include "ap_bus.h"
+#include "zcrypt_api.h"
+#include "zcrypt_debug.h"
+#include "zcrypt_msgtype6.h"
+#include "zcrypt_ccamisc.h"
+
+#define DEBUG_DBG(...)	ZCRYPT_DBF(DBF_DEBUG, ##__VA_ARGS__)
+#define DEBUG_INFO(...) ZCRYPT_DBF(DBF_INFO, ##__VA_ARGS__)
+#define DEBUG_WARN(...) ZCRYPT_DBF(DBF_WARN, ##__VA_ARGS__)
+#define DEBUG_ERR(...)	ZCRYPT_DBF(DBF_ERR, ##__VA_ARGS__)
+
+/* Size of parameter block used for all cca requests/replies */
+#define PARMBSIZE 512
+
+/* Size of vardata block used for some of the cca requests/replies */
+#define VARDATASIZE 4096
+
+struct cca_info_list_entry {
+	struct list_head list;
+	u16 cardnr;
+	u16 domain;
+	struct cca_info info;
+};
+
+/* a list with cca_info_list_entry entries */
+static LIST_HEAD(cca_info_list);
+static DEFINE_SPINLOCK(cca_info_list_lock);
+
+/*
+ * Simple check if the token is a valid CCA secure AES data key
+ * token. If keybitsize is given, the bitsize of the key is
+ * also checked. Returns 0 on success or errno value on failure.
+ */
+int cca_check_secaeskeytoken(debug_info_t *dbg, int dbflvl,
+			     const u8 *token, int keybitsize)
+{
+	struct secaeskeytoken *t = (struct secaeskeytoken *) token;
+
+#define DBF(...) debug_sprintf_event(dbg, dbflvl, ##__VA_ARGS__)
+
+	if (t->type != TOKTYPE_CCA_INTERNAL) {
+		if (dbg)
+			DBF("%s token check failed, type 0x%02x != 0x%02x\n",
+			    __func__, (int) t->type, TOKTYPE_CCA_INTERNAL);
+		return -EINVAL;
+	}
+	if (t->version != TOKVER_CCA_AES) {
+		if (dbg)
+			DBF("%s token check failed, version 0x%02x != 0x%02x\n",
+			    __func__, (int) t->version, TOKVER_CCA_AES);
+		return -EINVAL;
+	}
+	if (keybitsize > 0 && t->bitsize != keybitsize) {
+		if (dbg)
+			DBF("%s token check failed, bitsize %d != %d\n",
+			    __func__, (int) t->bitsize, keybitsize);
+		return -EINVAL;
+	}
+
+#undef DBF
+
+	return 0;
+}
+EXPORT_SYMBOL(cca_check_secaeskeytoken);
+
+/*
+ * Simple check if the token is a valid CCA secure AES cipher key
+ * token. If keybitsize is given, the bitsize of the key is
+ * also checked. If checkcpacfexport is enabled, the key is also
+ * checked for the export flag to allow CPACF export.
+ * Returns 0 on success or errno value on failure.
+ */
+int cca_check_secaescipherkey(debug_info_t *dbg, int dbflvl,
+			      const u8 *token, int keybitsize,
+			      int checkcpacfexport)
+{
+	struct cipherkeytoken *t = (struct cipherkeytoken *) token;
+	bool keybitsizeok = true;
+
+#define DBF(...) debug_sprintf_event(dbg, dbflvl, ##__VA_ARGS__)
+
+	if (t->type != TOKTYPE_CCA_INTERNAL) {
+		if (dbg)
+			DBF("%s token check failed, type 0x%02x != 0x%02x\n",
+			    __func__, (int) t->type, TOKTYPE_CCA_INTERNAL);
+		return -EINVAL;
+	}
+	if (t->version != TOKVER_CCA_VLSC) {
+		if (dbg)
+			DBF("%s token check failed, version 0x%02x != 0x%02x\n",
+			    __func__, (int) t->version, TOKVER_CCA_VLSC);
+		return -EINVAL;
+	}
+	if (t->algtype != 0x02) {
+		if (dbg)
+			DBF("%s token check failed, algtype 0x%02x != 0x02\n",
+			    __func__, (int) t->algtype);
+		return -EINVAL;
+	}
+	if (t->keytype != 0x0001) {
+		if (dbg)
+			DBF("%s token check failed, keytype 0x%04x != 0x0001\n",
+			    __func__, (int) t->keytype);
+		return -EINVAL;
+	}
+	if (t->plfver != 0x00 && t->plfver != 0x01) {
+		if (dbg)
+			DBF("%s token check failed, unknown plfver 0x%02x\n",
+			    __func__, (int) t->plfver);
+		return -EINVAL;
+	}
+	if (t->wpllen != 512 && t->wpllen != 576 && t->wpllen != 640) {
+		if (dbg)
+			DBF("%s token check failed, unknown wpllen %d\n",
+			    __func__, (int) t->wpllen);
+		return -EINVAL;
+	}
+	if (keybitsize > 0) {
+		switch (keybitsize) {
+		case 128:
+			if (t->wpllen != (t->plfver ? 640 : 512))
+				keybitsizeok = false;
+			break;
+		case 192:
+			if (t->wpllen != (t->plfver ? 640 : 576))
+				keybitsizeok = false;
+			break;
+		case 256:
+			if (t->wpllen != 640)
+				keybitsizeok = false;
+			break;
+		default:
+			keybitsizeok = false;
+			break;
+		}
+		if (!keybitsizeok) {
+			if (dbg)
+				DBF("%s token check failed, bitsize %d\n",
+				    __func__, keybitsize);
+			return -EINVAL;
+		}
+	}
+	if (checkcpacfexport && !(t->kmf1 & KMF1_XPRT_CPAC)) {
+		if (dbg)
+			DBF("%s token check failed, XPRT_CPAC bit is 0\n",
+			    __func__);
+		return -EINVAL;
+	}
+
+#undef DBF
+
+	return 0;
+}
+EXPORT_SYMBOL(cca_check_secaescipherkey);
+
+/*
+ * Allocate consecutive memory for request CPRB, request param
+ * block, reply CPRB and reply param block and fill in values
+ * for the common fields. Returns 0 on success or errno value
+ * on failure.
+ */
+static int alloc_and_prep_cprbmem(size_t paramblen,
+				  u8 **pcprbmem,
+				  struct CPRBX **preqCPRB,
+				  struct CPRBX **prepCPRB)
+{
+	u8 *cprbmem;
+	size_t cprbplusparamblen = sizeof(struct CPRBX) + paramblen;
+	struct CPRBX *preqcblk, *prepcblk;
+
+	/*
+	 * allocate consecutive memory for request CPRB, request param
+	 * block, reply CPRB and reply param block
+	 */
+	cprbmem = kcalloc(2, cprbplusparamblen, GFP_KERNEL);
+	if (!cprbmem)
+		return -ENOMEM;
+
+	preqcblk = (struct CPRBX *) cprbmem;
+	prepcblk = (struct CPRBX *) (cprbmem + cprbplusparamblen);
+
+	/* fill request cprb struct */
+	preqcblk->cprb_len = sizeof(struct CPRBX);
+	preqcblk->cprb_ver_id = 0x02;
+	memcpy(preqcblk->func_id, "T2", 2);
+	preqcblk->rpl_msgbl = cprbplusparamblen;
+	if (paramblen) {
+		preqcblk->req_parmb =
+			((u8 *) preqcblk) + sizeof(struct CPRBX);
+		preqcblk->rpl_parmb =
+			((u8 *) prepcblk) + sizeof(struct CPRBX);
+	}
+
+	*pcprbmem = cprbmem;
+	*preqCPRB = preqcblk;
+	*prepCPRB = prepcblk;
+
+	return 0;
+}
+
+/*
+ * Free the cprb memory allocated with the function above.
+ * If the scrub value is not zero, the memory is filled
+ * with zeros before freeing (useful if there was some
+ * clear key material in there).
+ */
+static void free_cprbmem(void *mem, size_t paramblen, int scrub)
+{
+	if (scrub)
+		memzero_explicit(mem, 2 * (sizeof(struct CPRBX) + paramblen));
+	kfree(mem);
+}
+
+/*
+ * Helper function to prepare the xcrb struct
+ */
+static inline void prep_xcrb(struct ica_xcRB *pxcrb,
+			     u16 cardnr,
+			     struct CPRBX *preqcblk,
+			     struct CPRBX *prepcblk)
+{
+	memset(pxcrb, 0, sizeof(*pxcrb));
+	pxcrb->agent_ID = 0x4341; /* 'CA' */
+	pxcrb->user_defined = (cardnr == 0xFFFF ? AUTOSELECT : cardnr);
+	pxcrb->request_control_blk_length =
+		preqcblk->cprb_len + preqcblk->req_parml;
+	pxcrb->request_control_blk_addr = (void __user *) preqcblk;
+	pxcrb->reply_control_blk_length = preqcblk->rpl_msgbl;
+	pxcrb->reply_control_blk_addr = (void __user *) prepcblk;
+}
+
+/*
+ * Helper function which calls zcrypt_send_cprb with
+ * memory management segment adjusted to kernel space
+ * so that the copy_from_user called within this
+ * function do in fact copy from kernel space.
+ */
+static inline int _zcrypt_send_cprb(struct ica_xcRB *xcrb)
+{
+	int rc;
+	mm_segment_t old_fs = get_fs();
+
+	set_fs(KERNEL_DS);
+	rc = zcrypt_send_cprb(xcrb);
+	set_fs(old_fs);
+
+	return rc;
+}
+
+/*
+ * Generate (random) CCA AES DATA secure key.
+ */
+int cca_genseckey(u16 cardnr, u16 domain,
+		  u32 keybitsize, u8 seckey[SECKEYBLOBSIZE])
+{
+	int i, rc, keysize;
+	int seckeysize;
+	u8 *mem;
+	struct CPRBX *preqcblk, *prepcblk;
+	struct ica_xcRB xcrb;
+	struct kgreqparm {
+		u8  subfunc_code[2];
+		u16 rule_array_len;
+		struct lv1 {
+			u16 len;
+			char  key_form[8];
+			char  key_length[8];
+			char  key_type1[8];
+			char  key_type2[8];
+		} lv1;
+		struct lv2 {
+			u16 len;
+			struct keyid {
+				u16 len;
+				u16 attr;
+				u8  data[SECKEYBLOBSIZE];
+			} keyid[6];
+		} lv2;
+	} __packed * preqparm;
+	struct kgrepparm {
+		u8  subfunc_code[2];
+		u16 rule_array_len;
+		struct lv3 {
+			u16 len;
+			u16 keyblocklen;
+			struct {
+				u16 toklen;
+				u16 tokattr;
+				u8  tok[0];
+				/* ... some more data ... */
+			} keyblock;
+		} lv3;
+	} __packed * prepparm;
+
+	/* get already prepared memory for 2 cprbs with param block each */
+	rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem, &preqcblk, &prepcblk);
+	if (rc)
+		return rc;
+
+	/* fill request cprb struct */
+	preqcblk->domain = domain;
+
+	/* fill request cprb param block with KG request */
+	preqparm = (struct kgreqparm *) preqcblk->req_parmb;
+	memcpy(preqparm->subfunc_code, "KG", 2);
+	preqparm->rule_array_len = sizeof(preqparm->rule_array_len);
+	preqparm->lv1.len = sizeof(struct lv1);
+	memcpy(preqparm->lv1.key_form,	 "OP      ", 8);
+	switch (keybitsize) {
+	case PKEY_SIZE_AES_128:
+	case PKEY_KEYTYPE_AES_128: /* older ioctls used this */
+		keysize = 16;
+		memcpy(preqparm->lv1.key_length, "KEYLN16 ", 8);
+		break;
+	case PKEY_SIZE_AES_192:
+	case PKEY_KEYTYPE_AES_192: /* older ioctls used this */
+		keysize = 24;
+		memcpy(preqparm->lv1.key_length, "KEYLN24 ", 8);
+		break;
+	case PKEY_SIZE_AES_256:
+	case PKEY_KEYTYPE_AES_256: /* older ioctls used this */
+		keysize = 32;
+		memcpy(preqparm->lv1.key_length, "KEYLN32 ", 8);
+		break;
+	default:
+		DEBUG_ERR("%s unknown/unsupported keybitsize %d\n",
+			  __func__, keybitsize);
+		rc = -EINVAL;
+		goto out;
+	}
+	memcpy(preqparm->lv1.key_type1,  "AESDATA ", 8);
+	preqparm->lv2.len = sizeof(struct lv2);
+	for (i = 0; i < 6; i++) {
+		preqparm->lv2.keyid[i].len = sizeof(struct keyid);
+		preqparm->lv2.keyid[i].attr = (i == 2 ? 0x30 : 0x10);
+	}
+	preqcblk->req_parml = sizeof(struct kgreqparm);
+
+	/* fill xcrb struct */
+	prep_xcrb(&xcrb, cardnr, preqcblk, prepcblk);
+
+	/* forward xcrb with request CPRB and reply CPRB to zcrypt dd */
+	rc = _zcrypt_send_cprb(&xcrb);
+	if (rc) {
+		DEBUG_ERR("%s zcrypt_send_cprb (cardnr=%d domain=%d) failed, errno %d\n",
+			  __func__, (int) cardnr, (int) domain, rc);
+		goto out;
+	}
+
+	/* check response returncode and reasoncode */
+	if (prepcblk->ccp_rtcode != 0) {
+		DEBUG_ERR("%s secure key generate failure, card response %d/%d\n",
+			  __func__,
+			  (int) prepcblk->ccp_rtcode,
+			  (int) prepcblk->ccp_rscode);
+		rc = -EIO;
+		goto out;
+	}
+
+	/* process response cprb param block */
+	prepcblk->rpl_parmb = ((u8 *) prepcblk) + sizeof(struct CPRBX);
+	prepparm = (struct kgrepparm *) prepcblk->rpl_parmb;
+
+	/* check length of the returned secure key token */
+	seckeysize = prepparm->lv3.keyblock.toklen
+		- sizeof(prepparm->lv3.keyblock.toklen)
+		- sizeof(prepparm->lv3.keyblock.tokattr);
+	if (seckeysize != SECKEYBLOBSIZE) {
+		DEBUG_ERR("%s secure token size mismatch %d != %d bytes\n",
+			  __func__, seckeysize, SECKEYBLOBSIZE);
+		rc = -EIO;
+		goto out;
+	}
+
+	/* check secure key token */
+	rc = cca_check_secaeskeytoken(zcrypt_dbf_info, DBF_ERR,
+				      prepparm->lv3.keyblock.tok, 8*keysize);
+	if (rc) {
+		rc = -EIO;
+		goto out;
+	}
+
+	/* copy the generated secure key token */
+	memcpy(seckey, prepparm->lv3.keyblock.tok, SECKEYBLOBSIZE);
+
+out:
+	free_cprbmem(mem, PARMBSIZE, 0);
+	return rc;
+}
+EXPORT_SYMBOL(cca_genseckey);
+
+/*
+ * Generate an CCA AES DATA secure key with given key value.
+ */
+int cca_clr2seckey(u16 cardnr, u16 domain, u32 keybitsize,
+		   const u8 *clrkey, u8 seckey[SECKEYBLOBSIZE])
+{
+	int rc, keysize, seckeysize;
+	u8 *mem;
+	struct CPRBX *preqcblk, *prepcblk;
+	struct ica_xcRB xcrb;
+	struct cmreqparm {
+		u8  subfunc_code[2];
+		u16 rule_array_len;
+		char  rule_array[8];
+		struct lv1 {
+			u16 len;
+			u8  clrkey[0];
+		} lv1;
+		struct lv2 {
+			u16 len;
+			struct keyid {
+				u16 len;
+				u16 attr;
+				u8  data[SECKEYBLOBSIZE];
+			} keyid;
+		} lv2;
+	} __packed * preqparm;
+	struct lv2 *plv2;
+	struct cmrepparm {
+		u8  subfunc_code[2];
+		u16 rule_array_len;
+		struct lv3 {
+			u16 len;
+			u16 keyblocklen;
+			struct {
+				u16 toklen;
+				u16 tokattr;
+				u8  tok[0];
+				/* ... some more data ... */
+			} keyblock;
+		} lv3;
+	} __packed * prepparm;
+
+	/* get already prepared memory for 2 cprbs with param block each */
+	rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem, &preqcblk, &prepcblk);
+	if (rc)
+		return rc;
+
+	/* fill request cprb struct */
+	preqcblk->domain = domain;
+
+	/* fill request cprb param block with CM request */
+	preqparm = (struct cmreqparm *) preqcblk->req_parmb;
+	memcpy(preqparm->subfunc_code, "CM", 2);
+	memcpy(preqparm->rule_array, "AES     ", 8);
+	preqparm->rule_array_len =
+		sizeof(preqparm->rule_array_len) + sizeof(preqparm->rule_array);
+	switch (keybitsize) {
+	case PKEY_SIZE_AES_128:
+	case PKEY_KEYTYPE_AES_128: /* older ioctls used this */
+		keysize = 16;
+		break;
+	case PKEY_SIZE_AES_192:
+	case PKEY_KEYTYPE_AES_192: /* older ioctls used this */
+		keysize = 24;
+		break;
+	case PKEY_SIZE_AES_256:
+	case PKEY_KEYTYPE_AES_256: /* older ioctls used this */
+		keysize = 32;
+		break;
+	default:
+		DEBUG_ERR("%s unknown/unsupported keybitsize %d\n",
+			  __func__, keybitsize);
+		rc = -EINVAL;
+		goto out;
+	}
+	preqparm->lv1.len = sizeof(struct lv1) + keysize;
+	memcpy(preqparm->lv1.clrkey, clrkey, keysize);
+	plv2 = (struct lv2 *) (((u8 *) &preqparm->lv2) + keysize);
+	plv2->len = sizeof(struct lv2);
+	plv2->keyid.len = sizeof(struct keyid);
+	plv2->keyid.attr = 0x30;
+	preqcblk->req_parml = sizeof(struct cmreqparm) + keysize;
+
+	/* fill xcrb struct */
+	prep_xcrb(&xcrb, cardnr, preqcblk, prepcblk);
+
+	/* forward xcrb with request CPRB and reply CPRB to zcrypt dd */
+	rc = _zcrypt_send_cprb(&xcrb);
+	if (rc) {
+		DEBUG_ERR("%s zcrypt_send_cprb (cardnr=%d domain=%d) failed, rc=%d\n",
+			  __func__, (int) cardnr, (int) domain, rc);
+		goto out;
+	}
+
+	/* check response returncode and reasoncode */
+	if (prepcblk->ccp_rtcode != 0) {
+		DEBUG_ERR("%s clear key import failure, card response %d/%d\n",
+			  __func__,
+			  (int) prepcblk->ccp_rtcode,
+			  (int) prepcblk->ccp_rscode);
+		rc = -EIO;
+		goto out;
+	}
+
+	/* process response cprb param block */
+	prepcblk->rpl_parmb = ((u8 *) prepcblk) + sizeof(struct CPRBX);
+	prepparm = (struct cmrepparm *) prepcblk->rpl_parmb;
+
+	/* check length of the returned secure key token */
+	seckeysize = prepparm->lv3.keyblock.toklen
+		- sizeof(prepparm->lv3.keyblock.toklen)
+		- sizeof(prepparm->lv3.keyblock.tokattr);
+	if (seckeysize != SECKEYBLOBSIZE) {
+		DEBUG_ERR("%s secure token size mismatch %d != %d bytes\n",
+			  __func__, seckeysize, SECKEYBLOBSIZE);
+		rc = -EIO;
+		goto out;
+	}
+
+	/* check secure key token */
+	rc = cca_check_secaeskeytoken(zcrypt_dbf_info, DBF_ERR,
+				      prepparm->lv3.keyblock.tok, 8*keysize);
+	if (rc) {
+		rc = -EIO;
+		goto out;
+	}
+
+	/* copy the generated secure key token */
+	if (seckey)
+		memcpy(seckey, prepparm->lv3.keyblock.tok, SECKEYBLOBSIZE);
+
+out:
+	free_cprbmem(mem, PARMBSIZE, 1);
+	return rc;
+}
+EXPORT_SYMBOL(cca_clr2seckey);
+
+/*
+ * Derive proteced key from an CCA AES DATA secure key.
+ */
+int cca_sec2protkey(u16 cardnr, u16 domain,
+		    const u8 seckey[SECKEYBLOBSIZE],
+		    u8 *protkey, u32 *protkeylen, u32 *protkeytype)
+{
+	int rc;
+	u8 *mem;
+	struct CPRBX *preqcblk, *prepcblk;
+	struct ica_xcRB xcrb;
+	struct uskreqparm {
+		u8  subfunc_code[2];
+		u16 rule_array_len;
+		struct lv1 {
+			u16 len;
+			u16 attr_len;
+			u16 attr_flags;
+		} lv1;
+		struct lv2 {
+			u16 len;
+			u16 attr_len;
+			u16 attr_flags;
+			u8  token[0];	      /* cca secure key token */
+		} lv2;
+	} __packed * preqparm;
+	struct uskrepparm {
+		u8  subfunc_code[2];
+		u16 rule_array_len;
+		struct lv3 {
+			u16 len;
+			u16 attr_len;
+			u16 attr_flags;
+			struct cpacfkeyblock {
+				u8  version;  /* version of this struct */
+				u8  flags[2];
+				u8  algo;
+				u8  form;
+				u8  pad1[3];
+				u16 len;
+				u8  key[64];  /* the key (len bytes) */
+				u16 keyattrlen;
+				u8  keyattr[32];
+				u8  pad2[1];
+				u8  vptype;
+				u8  vp[32];  /* verification pattern */
+			} keyblock;
+		} lv3;
+	} __packed * prepparm;
+
+	/* get already prepared memory for 2 cprbs with param block each */
+	rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem, &preqcblk, &prepcblk);
+	if (rc)
+		return rc;
+
+	/* fill request cprb struct */
+	preqcblk->domain = domain;
+
+	/* fill request cprb param block with USK request */
+	preqparm = (struct uskreqparm *) preqcblk->req_parmb;
+	memcpy(preqparm->subfunc_code, "US", 2);
+	preqparm->rule_array_len = sizeof(preqparm->rule_array_len);
+	preqparm->lv1.len = sizeof(struct lv1);
+	preqparm->lv1.attr_len = sizeof(struct lv1) - sizeof(preqparm->lv1.len);
+	preqparm->lv1.attr_flags = 0x0001;
+	preqparm->lv2.len = sizeof(struct lv2) + SECKEYBLOBSIZE;
+	preqparm->lv2.attr_len = sizeof(struct lv2)
+		- sizeof(preqparm->lv2.len) + SECKEYBLOBSIZE;
+	preqparm->lv2.attr_flags = 0x0000;
+	memcpy(preqparm->lv2.token, seckey, SECKEYBLOBSIZE);
+	preqcblk->req_parml = sizeof(struct uskreqparm) + SECKEYBLOBSIZE;
+
+	/* fill xcrb struct */
+	prep_xcrb(&xcrb, cardnr, preqcblk, prepcblk);
+
+	/* forward xcrb with request CPRB and reply CPRB to zcrypt dd */
+	rc = _zcrypt_send_cprb(&xcrb);
+	if (rc) {
+		DEBUG_ERR("%s zcrypt_send_cprb (cardnr=%d domain=%d) failed, rc=%d\n",
+			  __func__, (int) cardnr, (int) domain, rc);
+		goto out;
+	}
+
+	/* check response returncode and reasoncode */
+	if (prepcblk->ccp_rtcode != 0) {
+		DEBUG_ERR("%s unwrap secure key failure, card response %d/%d\n",
+			  __func__,
+			  (int) prepcblk->ccp_rtcode,
+			  (int) prepcblk->ccp_rscode);
+		rc = -EIO;
+		goto out;
+	}
+	if (prepcblk->ccp_rscode != 0) {
+		DEBUG_WARN("%s unwrap secure key warning, card response %d/%d\n",
+			   __func__,
+			   (int) prepcblk->ccp_rtcode,
+			   (int) prepcblk->ccp_rscode);
+	}
+
+	/* process response cprb param block */
+	prepcblk->rpl_parmb = ((u8 *) prepcblk) + sizeof(struct CPRBX);
+	prepparm = (struct uskrepparm *) prepcblk->rpl_parmb;
+
+	/* check the returned keyblock */
+	if (prepparm->lv3.keyblock.version != 0x01) {
+		DEBUG_ERR("%s reply param keyblock version mismatch 0x%02x != 0x01\n",
+			  __func__, (int) prepparm->lv3.keyblock.version);
+		rc = -EIO;
+		goto out;
+	}
+
+	/* copy the tanslated protected key */
+	switch (prepparm->lv3.keyblock.len) {
+	case 16+32:
+		/* AES 128 protected key */
+		if (protkeytype)
+			*protkeytype = PKEY_KEYTYPE_AES_128;
+		break;
+	case 24+32:
+		/* AES 192 protected key */
+		if (protkeytype)
+			*protkeytype = PKEY_KEYTYPE_AES_192;
+		break;
+	case 32+32:
+		/* AES 256 protected key */
+		if (protkeytype)
+			*protkeytype = PKEY_KEYTYPE_AES_256;
+		break;
+	default:
+		DEBUG_ERR("%s unknown/unsupported keylen %d\n",
+			  __func__, prepparm->lv3.keyblock.len);
+		rc = -EIO;
+		goto out;
+	}
+	memcpy(protkey, prepparm->lv3.keyblock.key, prepparm->lv3.keyblock.len);
+	if (protkeylen)
+		*protkeylen = prepparm->lv3.keyblock.len;
+
+out:
+	free_cprbmem(mem, PARMBSIZE, 0);
+	return rc;
+}
+EXPORT_SYMBOL(cca_sec2protkey);
+
+/*
+ * AES cipher key skeleton created with CSNBKTB2 with these flags:
+ * INTERNAL, NO-KEY, AES, CIPHER, ANY-MODE, NOEX-SYM, NOEXAASY,
+ * NOEXUASY, XPRTCPAC, NOEX-RAW, NOEX-DES, NOEX-AES, NOEX-RSA
+ * used by cca_gencipherkey() and cca_clr2cipherkey().
+ */
+static const u8 aes_cipher_key_skeleton[] = {
+	0x01, 0x00, 0x00, 0x38, 0x05, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+	0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x02, 0x00, 0x01, 0x02, 0xc0, 0x00, 0xff,
+	0x00, 0x03, 0x08, 0xc8, 0x00, 0x00, 0x00, 0x00 };
+#define SIZEOF_SKELETON (sizeof(aes_cipher_key_skeleton))
+
+/*
+ * Generate (random) CCA AES CIPHER secure key.
+ */
+int cca_gencipherkey(u16 cardnr, u16 domain, u32 keybitsize, u32 keygenflags,
+		     u8 *keybuf, size_t *keybufsize)
+{
+	int rc;
+	u8 *mem;
+	struct CPRBX *preqcblk, *prepcblk;
+	struct ica_xcRB xcrb;
+	struct gkreqparm {
+		u8  subfunc_code[2];
+		u16 rule_array_len;
+		char rule_array[2*8];
+		struct {
+			u16 len;
+			u8  key_type_1[8];
+			u8  key_type_2[8];
+			u16 clear_key_bit_len;
+			u16 key_name_1_len;
+			u16 key_name_2_len;
+			u16 user_data_1_len;
+			u16 user_data_2_len;
+			u8  key_name_1[0];
+			u8  key_name_2[0];
+			u8  user_data_1[0];
+			u8  user_data_2[0];
+		} vud;
+		struct {
+			u16 len;
+			struct {
+				u16 len;
+				u16 flag;
+				u8  kek_id_1[0];
+			} tlv1;
+			struct {
+				u16 len;
+				u16 flag;
+				u8  kek_id_2[0];
+			} tlv2;
+			struct {
+				u16 len;
+				u16 flag;
+				u8  gen_key_id_1[SIZEOF_SKELETON];
+			} tlv3;
+			struct {
+				u16 len;
+				u16 flag;
+				u8  gen_key_id_1_label[0];
+			} tlv4;
+			struct {
+				u16 len;
+				u16 flag;
+				u8  gen_key_id_2[0];
+			} tlv5;
+			struct {
+				u16 len;
+				u16 flag;
+				u8  gen_key_id_2_label[0];
+			} tlv6;
+		} kb;
+	} __packed * preqparm;
+	struct gkrepparm {
+		u8  subfunc_code[2];
+		u16 rule_array_len;
+		struct {
+			u16 len;
+		} vud;
+		struct {
+			u16 len;
+			struct {
+				u16 len;
+				u16 flag;
+				u8  gen_key[0]; /* 120-136 bytes */
+			} tlv1;
+		} kb;
+	} __packed * prepparm;
+	struct cipherkeytoken *t;
+
+	/* get already prepared memory for 2 cprbs with param block each */
+	rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem, &preqcblk, &prepcblk);
+	if (rc)
+		return rc;
+
+	/* fill request cprb struct */
+	preqcblk->domain = domain;
+	preqcblk->req_parml = sizeof(struct gkreqparm);
+
+	/* prepare request param block with GK request */
+	preqparm = (struct gkreqparm *) preqcblk->req_parmb;
+	memcpy(preqparm->subfunc_code, "GK", 2);
+	preqparm->rule_array_len =  sizeof(uint16_t) + 2 * 8;
+	memcpy(preqparm->rule_array, "AES     OP      ", 2*8);
+
+	/* prepare vud block */
+	preqparm->vud.len = sizeof(preqparm->vud);
+	switch (keybitsize) {
+	case 128:
+	case 192:
+	case 256:
+		break;
+	default:
+		DEBUG_ERR(
+			"%s unknown/unsupported keybitsize %d\n",
+			__func__, keybitsize);
+		rc = -EINVAL;
+		goto out;
+	}
+	preqparm->vud.clear_key_bit_len = keybitsize;
+	memcpy(preqparm->vud.key_type_1, "TOKEN   ", 8);
+	memset(preqparm->vud.key_type_2, ' ', sizeof(preqparm->vud.key_type_2));
+
+	/* prepare kb block */
+	preqparm->kb.len = sizeof(preqparm->kb);
+	preqparm->kb.tlv1.len = sizeof(preqparm->kb.tlv1);
+	preqparm->kb.tlv1.flag = 0x0030;
+	preqparm->kb.tlv2.len = sizeof(preqparm->kb.tlv2);
+	preqparm->kb.tlv2.flag = 0x0030;
+	preqparm->kb.tlv3.len = sizeof(preqparm->kb.tlv3);
+	preqparm->kb.tlv3.flag = 0x0030;
+	memcpy(preqparm->kb.tlv3.gen_key_id_1,
+	       aes_cipher_key_skeleton, SIZEOF_SKELETON);
+	preqparm->kb.tlv4.len = sizeof(preqparm->kb.tlv4);
+	preqparm->kb.tlv4.flag = 0x0030;
+	preqparm->kb.tlv5.len = sizeof(preqparm->kb.tlv5);
+	preqparm->kb.tlv5.flag = 0x0030;
+	preqparm->kb.tlv6.len = sizeof(preqparm->kb.tlv6);
+	preqparm->kb.tlv6.flag = 0x0030;
+
+	/* patch the skeleton key token export flags inside the kb block */
+	if (keygenflags) {
+		t = (struct cipherkeytoken *) preqparm->kb.tlv3.gen_key_id_1;
+		t->kmf1 |= (u16) (keygenflags & 0x0000FF00);
+		t->kmf1 &= (u16) ~(keygenflags & 0x000000FF);
+	}
+
+	/* prepare xcrb struct */
+	prep_xcrb(&xcrb, cardnr, preqcblk, prepcblk);
+
+	/* forward xcrb with request CPRB and reply CPRB to zcrypt dd */
+	rc = _zcrypt_send_cprb(&xcrb);
+	if (rc) {
+		DEBUG_ERR(
+			"%s zcrypt_send_cprb (cardnr=%d domain=%d) failed, rc=%d\n",
+			__func__, (int) cardnr, (int) domain, rc);
+		goto out;
+	}
+
+	/* check response returncode and reasoncode */
+	if (prepcblk->ccp_rtcode != 0) {
+		DEBUG_ERR(
+			"%s cipher key generate failure, card response %d/%d\n",
+			__func__,
+			(int) prepcblk->ccp_rtcode,
+			(int) prepcblk->ccp_rscode);
+		rc = -EIO;
+		goto out;
+	}
+
+	/* process response cprb param block */
+	prepcblk->rpl_parmb = ((u8 *) prepcblk) + sizeof(struct CPRBX);
+	prepparm = (struct gkrepparm *) prepcblk->rpl_parmb;
+
+	/* do some plausibility checks on the key block */
+	if (prepparm->kb.len < 120 + 5 * sizeof(uint16_t) ||
+	    prepparm->kb.len > 136 + 5 * sizeof(uint16_t)) {
+		DEBUG_ERR("%s reply with invalid or unknown key block\n",
+			  __func__);
+		rc = -EIO;
+		goto out;
+	}
+
+	/* and some checks on the generated key */
+	rc = cca_check_secaescipherkey(zcrypt_dbf_info, DBF_ERR,
+				       prepparm->kb.tlv1.gen_key,
+				       keybitsize, 1);
+	if (rc) {
+		rc = -EIO;
+		goto out;
+	}
+
+	/* copy the generated vlsc key token */
+	t = (struct cipherkeytoken *) prepparm->kb.tlv1.gen_key;
+	if (keybuf) {
+		if (*keybufsize >= t->len)
+			memcpy(keybuf, t, t->len);
+		else
+			rc = -EINVAL;
+	}
+	*keybufsize = t->len;
+
+out:
+	free_cprbmem(mem, PARMBSIZE, 0);
+	return rc;
+}
+EXPORT_SYMBOL(cca_gencipherkey);
+
+/*
+ * Helper function, does a the CSNBKPI2 CPRB.
+ */
+static int _ip_cprb_helper(u16 cardnr, u16 domain,
+			   const char *rule_array_1,
+			   const char *rule_array_2,
+			   const char *rule_array_3,
+			   const u8 *clr_key_value,
+			   int clr_key_bit_size,
+			   u8 *key_token,
+			   int *key_token_size)
+{
+	int rc, n;
+	u8 *mem;
+	struct CPRBX *preqcblk, *prepcblk;
+	struct ica_xcRB xcrb;
+	struct rule_array_block {
+		u8  subfunc_code[2];
+		u16 rule_array_len;
+		char rule_array[0];
+	} __packed * preq_ra_block;
+	struct vud_block {
+		u16 len;
+		struct {
+			u16 len;
+			u16 flag;	     /* 0x0064 */
+			u16 clr_key_bit_len;
+		} tlv1;
+		struct {
+			u16 len;
+			u16 flag;	/* 0x0063 */
+			u8  clr_key[0]; /* clear key value bytes */
+		} tlv2;
+	} __packed * preq_vud_block;
+	struct key_block {
+		u16 len;
+		struct {
+			u16 len;
+			u16 flag;	  /* 0x0030 */
+			u8  key_token[0]; /* key skeleton */
+		} tlv1;
+	} __packed * preq_key_block;
+	struct iprepparm {
+		u8  subfunc_code[2];
+		u16 rule_array_len;
+		struct {
+			u16 len;
+		} vud;
+		struct {
+			u16 len;
+			struct {
+				u16 len;
+				u16 flag;	  /* 0x0030 */
+				u8  key_token[0]; /* key token */
+			} tlv1;
+		} kb;
+	} __packed * prepparm;
+	struct cipherkeytoken *t;
+	int complete = strncmp(rule_array_2, "COMPLETE", 8) ? 0 : 1;
+
+	/* get already prepared memory for 2 cprbs with param block each */
+	rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem, &preqcblk, &prepcblk);
+	if (rc)
+		return rc;
+
+	/* fill request cprb struct */
+	preqcblk->domain = domain;
+	preqcblk->req_parml = 0;
+
+	/* prepare request param block with IP request */
+	preq_ra_block = (struct rule_array_block *) preqcblk->req_parmb;
+	memcpy(preq_ra_block->subfunc_code, "IP", 2);
+	preq_ra_block->rule_array_len =  sizeof(uint16_t) + 2 * 8;
+	memcpy(preq_ra_block->rule_array, rule_array_1, 8);
+	memcpy(preq_ra_block->rule_array + 8, rule_array_2, 8);
+	preqcblk->req_parml = sizeof(struct rule_array_block) + 2 * 8;
+	if (rule_array_3) {
+		preq_ra_block->rule_array_len += 8;
+		memcpy(preq_ra_block->rule_array + 16, rule_array_3, 8);
+		preqcblk->req_parml += 8;
+	}
+
+	/* prepare vud block */
+	preq_vud_block = (struct vud_block *)
+		(preqcblk->req_parmb + preqcblk->req_parml);
+	n = complete ? 0 : (clr_key_bit_size + 7) / 8;
+	preq_vud_block->len = sizeof(struct vud_block) + n;
+	preq_vud_block->tlv1.len = sizeof(preq_vud_block->tlv1);
+	preq_vud_block->tlv1.flag = 0x0064;
+	preq_vud_block->tlv1.clr_key_bit_len = complete ? 0 : clr_key_bit_size;
+	preq_vud_block->tlv2.len = sizeof(preq_vud_block->tlv2) + n;
+	preq_vud_block->tlv2.flag = 0x0063;
+	if (!complete)
+		memcpy(preq_vud_block->tlv2.clr_key, clr_key_value, n);
+	preqcblk->req_parml += preq_vud_block->len;
+
+	/* prepare key block */
+	preq_key_block = (struct key_block *)
+		(preqcblk->req_parmb + preqcblk->req_parml);
+	n = *key_token_size;
+	preq_key_block->len = sizeof(struct key_block) + n;
+	preq_key_block->tlv1.len = sizeof(preq_key_block->tlv1) + n;
+	preq_key_block->tlv1.flag = 0x0030;
+	memcpy(preq_key_block->tlv1.key_token, key_token, *key_token_size);
+	preqcblk->req_parml += preq_key_block->len;
+
+	/* prepare xcrb struct */
+	prep_xcrb(&xcrb, cardnr, preqcblk, prepcblk);
+
+	/* forward xcrb with request CPRB and reply CPRB to zcrypt dd */
+	rc = _zcrypt_send_cprb(&xcrb);
+	if (rc) {
+		DEBUG_ERR(
+			"%s zcrypt_send_cprb (cardnr=%d domain=%d) failed, rc=%d\n",
+			__func__, (int) cardnr, (int) domain, rc);
+		goto out;
+	}
+
+	/* check response returncode and reasoncode */
+	if (prepcblk->ccp_rtcode != 0) {
+		DEBUG_ERR(
+			"%s CSNBKPI2 failure, card response %d/%d\n",
+			__func__,
+			(int) prepcblk->ccp_rtcode,
+			(int) prepcblk->ccp_rscode);
+		rc = -EIO;
+		goto out;
+	}
+
+	/* process response cprb param block */
+	prepcblk->rpl_parmb = ((u8 *) prepcblk) + sizeof(struct CPRBX);
+	prepparm = (struct iprepparm *) prepcblk->rpl_parmb;
+
+	/* do some plausibility checks on the key block */
+	if (prepparm->kb.len < 120 + 5 * sizeof(uint16_t) ||
+	    prepparm->kb.len > 136 + 5 * sizeof(uint16_t)) {
+		DEBUG_ERR("%s reply with invalid or unknown key block\n",
+			  __func__);
+		rc = -EIO;
+		goto out;
+	}
+
+	/* do not check the key here, it may be incomplete */
+
+	/* copy the vlsc key token back */
+	t = (struct cipherkeytoken *) prepparm->kb.tlv1.key_token;
+	memcpy(key_token, t, t->len);
+	*key_token_size = t->len;
+
+out:
+	free_cprbmem(mem, PARMBSIZE, 0);
+	return rc;
+}
+
+/*
+ * Build CCA AES CIPHER secure key with a given clear key value.
+ */
+int cca_clr2cipherkey(u16 card, u16 dom, u32 keybitsize, u32 keygenflags,
+		      const u8 *clrkey, u8 *keybuf, size_t *keybufsize)
+{
+	int rc;
+	u8 *token;
+	int tokensize;
+	u8 exorbuf[32];
+	struct cipherkeytoken *t;
+
+	/* fill exorbuf with random data */
+	get_random_bytes(exorbuf, sizeof(exorbuf));
+
+	/* allocate space for the key token to build */
+	token = kmalloc(MAXCCAVLSCTOKENSIZE, GFP_KERNEL);
+	if (!token)
+		return -ENOMEM;
+
+	/* prepare the token with the key skeleton */
+	tokensize = SIZEOF_SKELETON;
+	memcpy(token, aes_cipher_key_skeleton, tokensize);
+
+	/* patch the skeleton key token export flags */
+	if (keygenflags) {
+		t = (struct cipherkeytoken *) token;
+		t->kmf1 |= (u16) (keygenflags & 0x0000FF00);
+		t->kmf1 &= (u16) ~(keygenflags & 0x000000FF);
+	}
+
+	/*
+	 * Do the key import with the clear key value in 4 steps:
+	 * 1/4 FIRST import with only random data
+	 * 2/4 EXOR the clear key
+	 * 3/4 EXOR the very same random data again
+	 * 4/4 COMPLETE the secure cipher key import
+	 */
+	rc = _ip_cprb_helper(card, dom, "AES     ", "FIRST   ", "MIN3PART",
+			     exorbuf, keybitsize, token, &tokensize);
+	if (rc) {
+		DEBUG_ERR(
+			"%s clear key import 1/4 with CSNBKPI2 failed, rc=%d\n",
+			__func__, rc);
+		goto out;
+	}
+	rc = _ip_cprb_helper(card, dom, "AES     ", "ADD-PART", NULL,
+			     clrkey, keybitsize, token, &tokensize);
+	if (rc) {
+		DEBUG_ERR(
+			"%s clear key import 2/4 with CSNBKPI2 failed, rc=%d\n",
+			__func__, rc);
+		goto out;
+	}
+	rc = _ip_cprb_helper(card, dom, "AES     ", "ADD-PART", NULL,
+			     exorbuf, keybitsize, token, &tokensize);
+	if (rc) {
+		DEBUG_ERR(
+			"%s clear key import 3/4 with CSNBKPI2 failed, rc=%d\n",
+			__func__, rc);
+		goto out;
+	}
+	rc = _ip_cprb_helper(card, dom, "AES     ", "COMPLETE", NULL,
+			     NULL, keybitsize, token, &tokensize);
+	if (rc) {
+		DEBUG_ERR(
+			"%s clear key import 4/4 with CSNBKPI2 failed, rc=%d\n",
+			__func__, rc);
+		goto out;
+	}
+
+	/* copy the generated key token */
+	if (keybuf) {
+		if (tokensize > *keybufsize)
+			rc = -EINVAL;
+		else
+			memcpy(keybuf, token, tokensize);
+	}
+	*keybufsize = tokensize;
+
+out:
+	kfree(token);
+	return rc;
+}
+EXPORT_SYMBOL(cca_clr2cipherkey);
+
+/*
+ * Derive proteced key from CCA AES cipher secure key.
+ */
+int cca_cipher2protkey(u16 cardnr, u16 domain, const u8 *ckey,
+		       u8 *protkey, u32 *protkeylen, u32 *protkeytype)
+{
+	int rc;
+	u8 *mem;
+	struct CPRBX *preqcblk, *prepcblk;
+	struct ica_xcRB xcrb;
+	struct aureqparm {
+		u8  subfunc_code[2];
+		u16 rule_array_len;
+		u8  rule_array[8];
+		struct {
+			u16 len;
+			u16 tk_blob_len;
+			u16 tk_blob_tag;
+			u8  tk_blob[66];
+		} vud;
+		struct {
+			u16 len;
+			u16 cca_key_token_len;
+			u16 cca_key_token_flags;
+			u8  cca_key_token[0]; // 64 or more
+		} kb;
+	} __packed * preqparm;
+	struct aurepparm {
+		u8  subfunc_code[2];
+		u16 rule_array_len;
+		struct {
+			u16 len;
+			u16 sublen;
+			u16 tag;
+			struct cpacfkeyblock {
+				u8  version;  /* version of this struct */
+				u8  flags[2];
+				u8  algo;
+				u8  form;
+				u8  pad1[3];
+				u16 keylen;
+				u8  key[64];  /* the key (keylen bytes) */
+				u16 keyattrlen;
+				u8  keyattr[32];
+				u8  pad2[1];
+				u8  vptype;
+				u8  vp[32];  /* verification pattern */
+			} ckb;
+		} vud;
+		struct {
+			u16 len;
+		} kb;
+	} __packed * prepparm;
+	int keytoklen = ((struct cipherkeytoken *)ckey)->len;
+
+	/* get already prepared memory for 2 cprbs with param block each */
+	rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem, &preqcblk, &prepcblk);
+	if (rc)
+		return rc;
+
+	/* fill request cprb struct */
+	preqcblk->domain = domain;
+
+	/* fill request cprb param block with AU request */
+	preqparm = (struct aureqparm *) preqcblk->req_parmb;
+	memcpy(preqparm->subfunc_code, "AU", 2);
+	preqparm->rule_array_len =
+		sizeof(preqparm->rule_array_len)
+		+ sizeof(preqparm->rule_array);
+	memcpy(preqparm->rule_array, "EXPT-SK ", 8);
+	/* vud, tk blob */
+	preqparm->vud.len = sizeof(preqparm->vud);
+	preqparm->vud.tk_blob_len = sizeof(preqparm->vud.tk_blob)
+		+ 2 * sizeof(uint16_t);
+	preqparm->vud.tk_blob_tag = 0x00C2;
+	/* kb, cca token */
+	preqparm->kb.len = keytoklen + 3 * sizeof(uint16_t);
+	preqparm->kb.cca_key_token_len = keytoklen + 2 * sizeof(uint16_t);
+	memcpy(preqparm->kb.cca_key_token, ckey, keytoklen);
+	/* now fill length of param block into cprb */
+	preqcblk->req_parml = sizeof(struct aureqparm) + keytoklen;
+
+	/* fill xcrb struct */
+	prep_xcrb(&xcrb, cardnr, preqcblk, prepcblk);
+
+	/* forward xcrb with request CPRB and reply CPRB to zcrypt dd */
+	rc = _zcrypt_send_cprb(&xcrb);
+	if (rc) {
+		DEBUG_ERR(
+			"%s zcrypt_send_cprb (cardnr=%d domain=%d) failed, rc=%d\n",
+			__func__, (int) cardnr, (int) domain, rc);
+		goto out;
+	}
+
+	/* check response returncode and reasoncode */
+	if (prepcblk->ccp_rtcode != 0) {
+		DEBUG_ERR(
+			"%s unwrap secure key failure, card response %d/%d\n",
+			__func__,
+			(int) prepcblk->ccp_rtcode,
+			(int) prepcblk->ccp_rscode);
+		rc = -EIO;
+		goto out;
+	}
+	if (prepcblk->ccp_rscode != 0) {
+		DEBUG_WARN(
+			"%s unwrap secure key warning, card response %d/%d\n",
+			__func__,
+			(int) prepcblk->ccp_rtcode,
+			(int) prepcblk->ccp_rscode);
+	}
+
+	/* process response cprb param block */
+	prepcblk->rpl_parmb = ((u8 *) prepcblk) + sizeof(struct CPRBX);
+	prepparm = (struct aurepparm *) prepcblk->rpl_parmb;
+
+	/* check the returned keyblock */
+	if (prepparm->vud.ckb.version != 0x01) {
+		DEBUG_ERR(
+			"%s reply param keyblock version mismatch 0x%02x != 0x01\n",
+			__func__, (int) prepparm->vud.ckb.version);
+		rc = -EIO;
+		goto out;
+	}
+	if (prepparm->vud.ckb.algo != 0x02) {
+		DEBUG_ERR(
+			"%s reply param keyblock algo mismatch 0x%02x != 0x02\n",
+			__func__, (int) prepparm->vud.ckb.algo);
+		rc = -EIO;
+		goto out;
+	}
+
+	/* copy the translated protected key */
+	switch (prepparm->vud.ckb.keylen) {
+	case 16+32:
+		/* AES 128 protected key */
+		if (protkeytype)
+			*protkeytype = PKEY_KEYTYPE_AES_128;
+		break;
+	case 24+32:
+		/* AES 192 protected key */
+		if (protkeytype)
+			*protkeytype = PKEY_KEYTYPE_AES_192;
+		break;
+	case 32+32:
+		/* AES 256 protected key */
+		if (protkeytype)
+			*protkeytype = PKEY_KEYTYPE_AES_256;
+		break;
+	default:
+		DEBUG_ERR("%s unknown/unsupported keylen %d\n",
+			  __func__, prepparm->vud.ckb.keylen);
+		rc = -EIO;
+		goto out;
+	}
+	memcpy(protkey, prepparm->vud.ckb.key, prepparm->vud.ckb.keylen);
+	if (protkeylen)
+		*protkeylen = prepparm->vud.ckb.keylen;
+
+out:
+	free_cprbmem(mem, PARMBSIZE, 0);
+	return rc;
+}
+EXPORT_SYMBOL(cca_cipher2protkey);
+
+/*
+ * query cryptographic facility from CCA adapter
+ */
+int cca_query_crypto_facility(u16 cardnr, u16 domain,
+			      const char *keyword,
+			      u8 *rarray, size_t *rarraylen,
+			      u8 *varray, size_t *varraylen)
+{
+	int rc;
+	u16 len;
+	u8 *mem, *ptr;
+	struct CPRBX *preqcblk, *prepcblk;
+	struct ica_xcRB xcrb;
+	struct fqreqparm {
+		u8  subfunc_code[2];
+		u16 rule_array_len;
+		char  rule_array[8];
+		struct lv1 {
+			u16 len;
+			u8  data[VARDATASIZE];
+		} lv1;
+		u16 dummylen;
+	} __packed * preqparm;
+	size_t parmbsize = sizeof(struct fqreqparm);
+	struct fqrepparm {
+		u8  subfunc_code[2];
+		u8  lvdata[0];
+	} __packed * prepparm;
+
+	/* get already prepared memory for 2 cprbs with param block each */
+	rc = alloc_and_prep_cprbmem(parmbsize, &mem, &preqcblk, &prepcblk);
+	if (rc)
+		return rc;
+
+	/* fill request cprb struct */
+	preqcblk->domain = domain;
+
+	/* fill request cprb param block with FQ request */
+	preqparm = (struct fqreqparm *) preqcblk->req_parmb;
+	memcpy(preqparm->subfunc_code, "FQ", 2);
+	memcpy(preqparm->rule_array, keyword, sizeof(preqparm->rule_array));
+	preqparm->rule_array_len =
+		sizeof(preqparm->rule_array_len) + sizeof(preqparm->rule_array);
+	preqparm->lv1.len = sizeof(preqparm->lv1);
+	preqparm->dummylen = sizeof(preqparm->dummylen);
+	preqcblk->req_parml = parmbsize;
+
+	/* fill xcrb struct */
+	prep_xcrb(&xcrb, cardnr, preqcblk, prepcblk);
+
+	/* forward xcrb with request CPRB and reply CPRB to zcrypt dd */
+	rc = _zcrypt_send_cprb(&xcrb);
+	if (rc) {
+		DEBUG_ERR("%s zcrypt_send_cprb (cardnr=%d domain=%d) failed, rc=%d\n",
+			  __func__, (int) cardnr, (int) domain, rc);
+		goto out;
+	}
+
+	/* check response returncode and reasoncode */
+	if (prepcblk->ccp_rtcode != 0) {
+		DEBUG_ERR("%s unwrap secure key failure, card response %d/%d\n",
+			  __func__,
+			  (int) prepcblk->ccp_rtcode,
+			  (int) prepcblk->ccp_rscode);
+		rc = -EIO;
+		goto out;
+	}
+
+	/* process response cprb param block */
+	prepcblk->rpl_parmb = ((u8 *) prepcblk) + sizeof(struct CPRBX);
+	prepparm = (struct fqrepparm *) prepcblk->rpl_parmb;
+	ptr = prepparm->lvdata;
+
+	/* check and possibly copy reply rule array */
+	len = *((u16 *) ptr);
+	if (len > sizeof(u16)) {
+		ptr += sizeof(u16);
+		len -= sizeof(u16);
+		if (rarray && rarraylen && *rarraylen > 0) {
+			*rarraylen = (len > *rarraylen ? *rarraylen : len);
+			memcpy(rarray, ptr, *rarraylen);
+		}
+		ptr += len;
+	}
+	/* check and possible copy reply var array */
+	len = *((u16 *) ptr);
+	if (len > sizeof(u16)) {
+		ptr += sizeof(u16);
+		len -= sizeof(u16);
+		if (varray && varraylen && *varraylen > 0) {
+			*varraylen = (len > *varraylen ? *varraylen : len);
+			memcpy(varray, ptr, *varraylen);
+		}
+		ptr += len;
+	}
+
+out:
+	free_cprbmem(mem, parmbsize, 0);
+	return rc;
+}
+EXPORT_SYMBOL(cca_query_crypto_facility);
+
+static int cca_info_cache_fetch(u16 cardnr, u16 domain, struct cca_info *ci)
+{
+	int rc = -ENOENT;
+	struct cca_info_list_entry *ptr;
+
+	spin_lock_bh(&cca_info_list_lock);
+	list_for_each_entry(ptr, &cca_info_list, list) {
+		if (ptr->cardnr == cardnr && ptr->domain == domain) {
+			memcpy(ci, &ptr->info, sizeof(*ci));
+			rc = 0;
+			break;
+		}
+	}
+	spin_unlock_bh(&cca_info_list_lock);
+
+	return rc;
+}
+
+static void cca_info_cache_update(u16 cardnr, u16 domain,
+				  const struct cca_info *ci)
+{
+	int found = 0;
+	struct cca_info_list_entry *ptr;
+
+	spin_lock_bh(&cca_info_list_lock);
+	list_for_each_entry(ptr, &cca_info_list, list) {
+		if (ptr->cardnr == cardnr &&
+		    ptr->domain == domain) {
+			memcpy(&ptr->info, ci, sizeof(*ci));
+			found = 1;
+			break;
+		}
+	}
+	if (!found) {
+		ptr = kmalloc(sizeof(*ptr), GFP_ATOMIC);
+		if (!ptr) {
+			spin_unlock_bh(&cca_info_list_lock);
+			return;
+		}
+		ptr->cardnr = cardnr;
+		ptr->domain = domain;
+		memcpy(&ptr->info, ci, sizeof(*ci));
+		list_add(&ptr->list, &cca_info_list);
+	}
+	spin_unlock_bh(&cca_info_list_lock);
+}
+
+static void cca_info_cache_scrub(u16 cardnr, u16 domain)
+{
+	struct cca_info_list_entry *ptr;
+
+	spin_lock_bh(&cca_info_list_lock);
+	list_for_each_entry(ptr, &cca_info_list, list) {
+		if (ptr->cardnr == cardnr &&
+		    ptr->domain == domain) {
+			list_del(&ptr->list);
+			kfree(ptr);
+			break;
+		}
+	}
+	spin_unlock_bh(&cca_info_list_lock);
+}
+
+static void __exit mkvp_cache_free(void)
+{
+	struct cca_info_list_entry *ptr, *pnext;
+
+	spin_lock_bh(&cca_info_list_lock);
+	list_for_each_entry_safe(ptr, pnext, &cca_info_list, list) {
+		list_del(&ptr->list);
+		kfree(ptr);
+	}
+	spin_unlock_bh(&cca_info_list_lock);
+}
+
+/*
+ * Fetch cca_info values via query_crypto_facility from adapter.
+ */
+static int fetch_cca_info(u16 cardnr, u16 domain, struct cca_info *ci)
+{
+	int rc, found = 0;
+	size_t rlen, vlen;
+	u8 *rarray, *varray, *pg;
+	struct zcrypt_device_status_ext devstat;
+
+	memset(ci, 0, sizeof(*ci));
+
+	/* get first info from zcrypt device driver about this apqn */
+	rc = zcrypt_device_status_ext(cardnr, domain, &devstat);
+	if (rc)
+		return rc;
+	ci->hwtype = devstat.hwtype;
+
+	/* prep page for rule array and var array use */
+	pg = (u8 *) __get_free_page(GFP_KERNEL);
+	if (!pg)
+		return -ENOMEM;
+	rarray = pg;
+	varray = pg + PAGE_SIZE/2;
+	rlen = vlen = PAGE_SIZE/2;
+
+	/* QF for this card/domain */
+	rc = cca_query_crypto_facility(cardnr, domain, "STATICSA",
+				       rarray, &rlen, varray, &vlen);
+	if (rc == 0 && rlen >= 10*8 && vlen >= 204) {
+		memcpy(ci->serial, rarray, 8);
+		ci->new_mk_state = (char) rarray[7*8];
+		ci->cur_mk_state = (char) rarray[8*8];
+		ci->old_mk_state = (char) rarray[9*8];
+		if (ci->old_mk_state == '2')
+			memcpy(&ci->old_mkvp, varray + 172, 8);
+		if (ci->cur_mk_state == '2')
+			memcpy(&ci->cur_mkvp, varray + 184, 8);
+		if (ci->new_mk_state == '3')
+			memcpy(&ci->new_mkvp, varray + 196, 8);
+		found = 1;
+	}
+
+	free_page((unsigned long) pg);
+
+	return found ? 0 : -ENOENT;
+}
+
+/*
+ * Fetch cca information about a CCA queue.
+ */
+int cca_get_info(u16 card, u16 dom, struct cca_info *ci, int verify)
+{
+	int rc;
+
+	rc = cca_info_cache_fetch(card, dom, ci);
+	if (rc || verify) {
+		rc = fetch_cca_info(card, dom, ci);
+		if (rc == 0)
+			cca_info_cache_update(card, dom, ci);
+	}
+
+	return rc;
+}
+EXPORT_SYMBOL(cca_get_info);
+
+/*
+ * Search for a matching crypto card based on the
+ * Master Key Verification Pattern given.
+ */
+static int findcard(u64 mkvp, u16 *pcardnr, u16 *pdomain,
+		    int verify, int minhwtype)
+{
+	struct zcrypt_device_status_ext *device_status;
+	u16 card, dom;
+	struct cca_info ci;
+	int i, rc, oi = -1;
+
+	/* mkvp must not be zero, minhwtype needs to be >= 0 */
+	if (mkvp == 0 || minhwtype < 0)
+		return -EINVAL;
+
+	/* fetch status of all crypto cards */
+	device_status = kmalloc_array(MAX_ZDEV_ENTRIES_EXT,
+				      sizeof(struct zcrypt_device_status_ext),
+				      GFP_KERNEL);
+	if (!device_status)
+		return -ENOMEM;
+	zcrypt_device_status_mask_ext(device_status);
+
+	/* walk through all crypto cards */
+	for (i = 0; i < MAX_ZDEV_ENTRIES_EXT; i++) {
+		card = AP_QID_CARD(device_status[i].qid);
+		dom = AP_QID_QUEUE(device_status[i].qid);
+		if (device_status[i].online &&
+		    device_status[i].functions & 0x04) {
+			/* enabled CCA card, check current mkvp from cache */
+			if (cca_info_cache_fetch(card, dom, &ci) == 0 &&
+			    ci.hwtype >= minhwtype &&
+			    ci.cur_mk_state == '2' &&
+			    ci.cur_mkvp == mkvp) {
+				if (!verify)
+					break;
+				/* verify: refresh card info */
+				if (fetch_cca_info(card, dom, &ci) == 0) {
+					cca_info_cache_update(card, dom, &ci);
+					if (ci.hwtype >= minhwtype &&
+					    ci.cur_mk_state == '2' &&
+					    ci.cur_mkvp == mkvp)
+						break;
+				}
+			}
+		} else {
+			/* Card is offline and/or not a CCA card. */
+			/* del mkvp entry from cache if it exists */
+			cca_info_cache_scrub(card, dom);
+		}
+	}
+	if (i >= MAX_ZDEV_ENTRIES_EXT) {
+		/* nothing found, so this time without cache */
+		for (i = 0; i < MAX_ZDEV_ENTRIES_EXT; i++) {
+			if (!(device_status[i].online &&
+			      device_status[i].functions & 0x04))
+				continue;
+			card = AP_QID_CARD(device_status[i].qid);
+			dom = AP_QID_QUEUE(device_status[i].qid);
+			/* fresh fetch mkvp from adapter */
+			if (fetch_cca_info(card, dom, &ci) == 0) {
+				cca_info_cache_update(card, dom, &ci);
+				if (ci.hwtype >= minhwtype &&
+				    ci.cur_mk_state == '2' &&
+				    ci.cur_mkvp == mkvp)
+					break;
+				if (ci.hwtype >= minhwtype &&
+				    ci.old_mk_state == '2' &&
+				    ci.old_mkvp == mkvp &&
+				    oi < 0)
+					oi = i;
+			}
+		}
+		if (i >= MAX_ZDEV_ENTRIES_EXT && oi >= 0) {
+			/* old mkvp matched, use this card then */
+			card = AP_QID_CARD(device_status[oi].qid);
+			dom = AP_QID_QUEUE(device_status[oi].qid);
+		}
+	}
+	if (i < MAX_ZDEV_ENTRIES_EXT || oi >= 0) {
+		if (pcardnr)
+			*pcardnr = card;
+		if (pdomain)
+			*pdomain = dom;
+		rc = (i < MAX_ZDEV_ENTRIES_EXT ? 0 : 1);
+	} else
+		rc = -ENODEV;
+
+	kfree(device_status);
+	return rc;
+}
+
+/*
+ * Search for a matching crypto card based on the Master Key
+ * Verification Pattern provided inside a secure key token.
+ */
+int cca_findcard(const u8 *key, u16 *pcardnr, u16 *pdomain, int verify)
+{
+	u64 mkvp;
+	int minhwtype = 0;
+	const struct keytoken_header *hdr = (struct keytoken_header *) key;
+
+	if (hdr->type != TOKTYPE_CCA_INTERNAL)
+		return -EINVAL;
+
+	switch (hdr->version) {
+	case TOKVER_CCA_AES:
+		mkvp = ((struct secaeskeytoken *)key)->mkvp;
+		break;
+	case TOKVER_CCA_VLSC:
+		mkvp = ((struct cipherkeytoken *)key)->mkvp0;
+		minhwtype = AP_DEVICE_TYPE_CEX6;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return findcard(mkvp, pcardnr, pdomain, verify, minhwtype);
+}
+EXPORT_SYMBOL(cca_findcard);
+
+int cca_findcard2(u32 **apqns, u32 *nr_apqns, u16 cardnr, u16 domain,
+		  int minhwtype, u64 cur_mkvp, u64 old_mkvp, int verify)
+{
+	struct zcrypt_device_status_ext *device_status;
+	int i, n, card, dom, curmatch, oldmatch, rc = 0;
+	struct cca_info ci;
+
+	*apqns = NULL;
+	*nr_apqns = 0;
+
+	/* fetch status of all crypto cards */
+	device_status = kmalloc_array(MAX_ZDEV_ENTRIES_EXT,
+				      sizeof(struct zcrypt_device_status_ext),
+				      GFP_KERNEL);
+	if (!device_status)
+		return -ENOMEM;
+	zcrypt_device_status_mask_ext(device_status);
+
+	/* loop two times: first gather eligible apqns, then store them */
+	while (1) {
+		n = 0;
+		/* walk through all the crypto cards */
+		for (i = 0; i < MAX_ZDEV_ENTRIES_EXT; i++) {
+			card = AP_QID_CARD(device_status[i].qid);
+			dom = AP_QID_QUEUE(device_status[i].qid);
+			/* check online state */
+			if (!device_status[i].online)
+				continue;
+			/* check for cca functions */
+			if (!(device_status[i].functions & 0x04))
+				continue;
+			/* check cardnr */
+			if (cardnr != 0xFFFF && card != cardnr)
+				continue;
+			/* check domain */
+			if (domain != 0xFFFF && dom != domain)
+				continue;
+			/* get cca info on this apqn */
+			if (cca_get_info(card, dom, &ci, verify))
+				continue;
+			/* current master key needs to be valid */
+			if (ci.cur_mk_state != '2')
+				continue;
+			/* check min hardware type */
+			if (minhwtype > 0 && minhwtype > ci.hwtype)
+				continue;
+			if (cur_mkvp || old_mkvp) {
+				/* check mkvps */
+				curmatch = oldmatch = 0;
+				if (cur_mkvp && cur_mkvp == ci.cur_mkvp)
+					curmatch = 1;
+				if (old_mkvp && ci.old_mk_state == '2' &&
+				    old_mkvp == ci.old_mkvp)
+					oldmatch = 1;
+				if ((cur_mkvp || old_mkvp) &&
+				    (curmatch + oldmatch < 1))
+					continue;
+			}
+			/* apqn passed all filtering criterons */
+			if (*apqns && n < *nr_apqns)
+				(*apqns)[n] = (((u16)card) << 16) | ((u16) dom);
+			n++;
+		}
+		/* loop 2nd time: array has been filled */
+		if (*apqns)
+			break;
+		/* loop 1st time: have # of eligible apqns in n */
+		if (!n) {
+			rc = -ENODEV; /* no eligible apqns found */
+			break;
+		}
+		*nr_apqns = n;
+		/* allocate array to store n apqns into */
+		*apqns = kmalloc_array(n, sizeof(u32), GFP_KERNEL);
+		if (!*apqns) {
+			rc = -ENOMEM;
+			break;
+		}
+		verify = 0;
+	}
+
+	kfree(device_status);
+	return rc;
+}
+EXPORT_SYMBOL(cca_findcard2);
+
+void __exit zcrypt_ccamisc_exit(void)
+{
+	mkvp_cache_free();
+}
diff --git a/drivers/s390/crypto/zcrypt_ccamisc.h b/drivers/s390/crypto/zcrypt_ccamisc.h
new file mode 100644
index 0000000..77b6cc7
--- /dev/null
+++ b/drivers/s390/crypto/zcrypt_ccamisc.h
@@ -0,0 +1,217 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ *  Copyright IBM Corp. 2019
+ *  Author(s): Harald Freudenberger <freude@linux.ibm.com>
+ *	       Ingo Franzki <ifranzki@linux.ibm.com>
+ *
+ *  Collection of CCA misc functions used by zcrypt and pkey
+ */
+
+#ifndef _ZCRYPT_CCAMISC_H_
+#define _ZCRYPT_CCAMISC_H_
+
+#include <asm/zcrypt.h>
+#include <asm/pkey.h>
+
+/* Key token types */
+#define TOKTYPE_NON_CCA		0x00 /* Non-CCA key token */
+#define TOKTYPE_CCA_INTERNAL	0x01 /* CCA internal key token */
+
+/* For TOKTYPE_NON_CCA: */
+#define TOKVER_PROTECTED_KEY	0x01 /* Protected key token */
+
+/* For TOKTYPE_CCA_INTERNAL: */
+#define TOKVER_CCA_AES		0x04 /* CCA AES key token */
+#define TOKVER_CCA_VLSC		0x05 /* var length sym cipher key token */
+
+/* Max size of a cca variable length cipher key token */
+#define MAXCCAVLSCTOKENSIZE 725
+
+/* header part of a CCA key token */
+struct keytoken_header {
+	u8  type;     /* one of the TOKTYPE values */
+	u8  res0[1];
+	u16 len;      /* vlsc token: total length in bytes */
+	u8  version;  /* one of the TOKVER values */
+	u8  res1[3];
+} __packed;
+
+/* inside view of a CCA secure key token (only type 0x01 version 0x04) */
+struct secaeskeytoken {
+	u8  type;     /* 0x01 for internal key token */
+	u8  res0[3];
+	u8  version;  /* should be 0x04 */
+	u8  res1[1];
+	u8  flag;     /* key flags */
+	u8  res2[1];
+	u64 mkvp;     /* master key verification pattern */
+	u8  key[32];  /* key value (encrypted) */
+	u8  cv[8];    /* control vector */
+	u16 bitsize;  /* key bit size */
+	u16 keysize;  /* key byte size */
+	u8  tvv[4];   /* token validation value */
+} __packed;
+
+/* inside view of a variable length symmetric cipher AES key token */
+struct cipherkeytoken {
+	u8  type;     /* 0x01 for internal key token */
+	u8  res0[1];
+	u16 len;      /* total key token length in bytes */
+	u8  version;  /* should be 0x05 */
+	u8  res1[3];
+	u8  kms;      /* key material state, 0x03 means wrapped with MK */
+	u8  kvpt;     /* key verification pattern type, should be 0x01 */
+	u64 mkvp0;    /* master key verification pattern, lo part */
+	u64 mkvp1;    /* master key verification pattern, hi part (unused) */
+	u8  eskwm;    /* encrypted section key wrapping method */
+	u8  hashalg;  /* hash algorithmus used for wrapping key */
+	u8  plfver;   /* pay load format version */
+	u8  res2[1];
+	u8  adsver;   /* associated data section version */
+	u8  res3[1];
+	u16 adslen;   /* associated data section length */
+	u8  kllen;    /* optional key label length */
+	u8  ieaslen;  /* optional extended associated data length */
+	u8  uadlen;   /* optional user definable associated data length */
+	u8  res4[1];
+	u16 wpllen;   /* wrapped payload length in bits: */
+		      /*   plfver  0x00 0x01		 */
+		      /*   AES-128  512  640		 */
+		      /*   AES-192  576  640		 */
+		      /*   AES-256  640  640		 */
+	u8  res5[1];
+	u8  algtype;  /* 0x02 for AES cipher */
+	u16 keytype;  /* 0x0001 for 'cipher' */
+	u8  kufc;     /* key usage field count */
+	u16 kuf1;     /* key usage field 1 */
+	u16 kuf2;     /* key usage field 2 */
+	u8  kmfc;     /* key management field count */
+	u16 kmf1;     /* key management field 1 */
+	u16 kmf2;     /* key management field 2 */
+	u16 kmf3;     /* key management field 3 */
+	u8  vdata[0]; /* variable part data follows */
+} __packed;
+
+/* Some defines for the CCA AES cipherkeytoken kmf1 field */
+#define KMF1_XPRT_SYM  0x8000
+#define KMF1_XPRT_UASY 0x4000
+#define KMF1_XPRT_AASY 0x2000
+#define KMF1_XPRT_RAW  0x1000
+#define KMF1_XPRT_CPAC 0x0800
+#define KMF1_XPRT_DES  0x0080
+#define KMF1_XPRT_AES  0x0040
+#define KMF1_XPRT_RSA  0x0008
+
+/*
+ * Simple check if the token is a valid CCA secure AES data key
+ * token. If keybitsize is given, the bitsize of the key is
+ * also checked. Returns 0 on success or errno value on failure.
+ */
+int cca_check_secaeskeytoken(debug_info_t *dbg, int dbflvl,
+			     const u8 *token, int keybitsize);
+
+/*
+ * Simple check if the token is a valid CCA secure AES cipher key
+ * token. If keybitsize is given, the bitsize of the key is
+ * also checked. If checkcpacfexport is enabled, the key is also
+ * checked for the export flag to allow CPACF export.
+ * Returns 0 on success or errno value on failure.
+ */
+int cca_check_secaescipherkey(debug_info_t *dbg, int dbflvl,
+			      const u8 *token, int keybitsize,
+			      int checkcpacfexport);
+
+/*
+ * Generate (random) CCA AES DATA secure key.
+ */
+int cca_genseckey(u16 cardnr, u16 domain, u32 keybitsize, u8 *seckey);
+
+/*
+ * Generate CCA AES DATA secure key with given clear key value.
+ */
+int cca_clr2seckey(u16 cardnr, u16 domain, u32 keybitsize,
+		   const u8 *clrkey, u8 *seckey);
+
+/*
+ * Derive proteced key from an CCA AES DATA secure key.
+ */
+int cca_sec2protkey(u16 cardnr, u16 domain,
+		    const u8 seckey[SECKEYBLOBSIZE],
+		    u8 *protkey, u32 *protkeylen, u32 *protkeytype);
+
+/*
+ * Generate (random) CCA AES CIPHER secure key.
+ */
+int cca_gencipherkey(u16 cardnr, u16 domain, u32 keybitsize, u32 keygenflags,
+		     u8 *keybuf, size_t *keybufsize);
+
+/*
+ * Derive proteced key from CCA AES cipher secure key.
+ */
+int cca_cipher2protkey(u16 cardnr, u16 domain, const u8 *ckey,
+		       u8 *protkey, u32 *protkeylen, u32 *protkeytype);
+
+/*
+ * Build CCA AES CIPHER secure key with a given clear key value.
+ */
+int cca_clr2cipherkey(u16 cardnr, u16 domain, u32 keybitsize, u32 keygenflags,
+		      const u8 *clrkey, u8 *keybuf, size_t *keybufsize);
+
+/*
+ * Query cryptographic facility from CCA adapter
+ */
+int cca_query_crypto_facility(u16 cardnr, u16 domain,
+			      const char *keyword,
+			      u8 *rarray, size_t *rarraylen,
+			      u8 *varray, size_t *varraylen);
+
+/*
+ * Search for a matching crypto card based on the Master Key
+ * Verification Pattern provided inside a secure key.
+ * Works with CCA AES data and cipher keys.
+ * Returns < 0 on failure, 0 if CURRENT MKVP matches and
+ * 1 if OLD MKVP matches.
+ */
+int cca_findcard(const u8 *key, u16 *pcardnr, u16 *pdomain, int verify);
+
+/*
+ * Build a list of cca apqns meeting the following constrains:
+ * - apqn is online and is in fact a CCA apqn
+ * - if cardnr is not FFFF only apqns with this cardnr
+ * - if domain is not FFFF only apqns with this domainnr
+ * - if minhwtype > 0 only apqns with hwtype >= minhwtype
+ * - if cur_mkvp != 0 only apqns where cur_mkvp == mkvp
+ * - if old_mkvp != 0 only apqns where old_mkvp == mkvp
+ * - if verify is enabled and a cur_mkvp and/or old_mkvp
+ *   value is given, then refetch the cca_info and make sure the current
+ *   cur_mkvp or old_mkvp values of the apqn are used.
+ * The array of apqn entries is allocated with kmalloc and returned in *apqns;
+ * the number of apqns stored into the list is returned in *nr_apqns. One apqn
+ * entry is simple a 32 bit value with 16 bit cardnr and 16 bit domain nr and
+ * may be casted to struct pkey_apqn. The return value is either 0 for success
+ * or a negative errno value. If no apqn meeting the criterias is found,
+ * -ENODEV is returned.
+ */
+int cca_findcard2(u32 **apqns, u32 *nr_apqns, u16 cardnr, u16 domain,
+		  int minhwtype, u64 cur_mkvp, u64 old_mkvp, int verify);
+
+/* struct to hold info for each CCA queue */
+struct cca_info {
+	int  hwtype;	    /* one of the defined AP_DEVICE_TYPE_* */
+	char new_mk_state;  /* '1' empty, '2' partially full, '3' full */
+	char cur_mk_state;  /* '1' invalid, '2' valid */
+	char old_mk_state;  /* '1' invalid, '2' valid */
+	u64  new_mkvp;	    /* truncated sha256 hash of new master key */
+	u64  cur_mkvp;	    /* truncated sha256 hash of current master key */
+	u64  old_mkvp;	    /* truncated sha256 hash of old master key */
+	char serial[9];     /* serial number string (8 ascii numbers + 0x00) */
+};
+
+/*
+ * Fetch cca information about an CCA queue.
+ */
+int cca_get_info(u16 card, u16 dom, struct cca_info *ci, int verify);
+
+void zcrypt_ccamisc_exit(void);
+
+#endif /* _ZCRYPT_CCAMISC_H_ */
diff --git a/drivers/s390/crypto/zcrypt_cex2a.c b/drivers/s390/crypto/zcrypt_cex2a.c
index f4ae5fa..c50f3e8 100644
--- a/drivers/s390/crypto/zcrypt_cex2a.c
+++ b/drivers/s390/crypto/zcrypt_cex2a.c
@@ -1,7 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- *  zcrypt 2.1.0
- *
  *  Copyright IBM Corp. 2001, 2012
  *  Author(s): Robert Burroughs
  *	       Eric Rossman (edrossma@us.ibm.com)
@@ -43,8 +41,8 @@
 #define CEX3A_CLEANUP_TIME	CEX2A_CLEANUP_TIME
 
 MODULE_AUTHOR("IBM Corporation");
-MODULE_DESCRIPTION("CEX2A Cryptographic Coprocessor device driver, " \
-		   "Copyright IBM Corp. 2001, 2012");
+MODULE_DESCRIPTION("CEX2A/CEX3A Cryptographic Coprocessor device driver, " \
+		   "Copyright IBM Corp. 2001, 2018");
 MODULE_LICENSE("GPL");
 
 static struct ap_device_id zcrypt_cex2a_card_ids[] = {
@@ -198,7 +196,6 @@
 	struct ap_queue *aq = to_ap_queue(&ap_dev->device);
 	struct zcrypt_queue *zq = aq->private;
 
-	ap_queue_remove(aq);
 	if (zq)
 		zcrypt_queue_unregister(zq);
 }
diff --git a/drivers/s390/crypto/zcrypt_cex2a.h b/drivers/s390/crypto/zcrypt_cex2a.h
index 66d58bc..7842214 100644
--- a/drivers/s390/crypto/zcrypt_cex2a.h
+++ b/drivers/s390/crypto/zcrypt_cex2a.h
@@ -1,7 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0+ */
 /*
- *  zcrypt 2.1.0
- *
  *  Copyright IBM Corp. 2001, 2006
  *  Author(s): Robert Burroughs
  *	       Eric Rossman (edrossma@us.ibm.com)
@@ -14,7 +12,7 @@
 #define _ZCRYPT_CEX2A_H_
 
 /**
- * The type 50 message family is associated with a CEX2A card.
+ * The type 50 message family is associated with CEXxA cards.
  *
  * The four members of the family are described below.
  *
@@ -111,7 +109,7 @@
 } __packed;
 
 /**
- * The type 80 response family is associated with a CEX2A card.
+ * The type 80 response family is associated with a CEXxA cards.
  *
  * Note that all unsigned char arrays are right-justified and left-padded
  * with zeroes.
diff --git a/drivers/s390/crypto/zcrypt_pcixcc.c b/drivers/s390/crypto/zcrypt_cex2c.c
similarity index 62%
rename from drivers/s390/crypto/zcrypt_pcixcc.c
rename to drivers/s390/crypto/zcrypt_cex2c.c
index 94d9f72..35c7c66 100644
--- a/drivers/s390/crypto/zcrypt_pcixcc.c
+++ b/drivers/s390/crypto/zcrypt_cex2c.c
@@ -1,8 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- *  zcrypt 2.1.0
- *
- *  Copyright IBM Corp. 2001, 2012
+ *  Copyright IBM Corp. 2001, 2018
  *  Author(s): Robert Burroughs
  *	       Eric Rossman (edrossma@us.ibm.com)
  *
@@ -25,39 +23,22 @@
 #include "zcrypt_api.h"
 #include "zcrypt_error.h"
 #include "zcrypt_msgtype6.h"
-#include "zcrypt_pcixcc.h"
+#include "zcrypt_cex2c.h"
 #include "zcrypt_cca_key.h"
 
-#define PCIXCC_MIN_MOD_SIZE	 16	/*  128 bits	*/
-#define PCIXCC_MIN_MOD_SIZE_OLD	 64	/*  512 bits	*/
-#define PCIXCC_MAX_MOD_SIZE	256	/* 2048 bits	*/
-#define CEX3C_MIN_MOD_SIZE	PCIXCC_MIN_MOD_SIZE
+#define CEX2C_MIN_MOD_SIZE	 16	/*  128 bits	*/
+#define CEX2C_MAX_MOD_SIZE	256	/* 2048 bits	*/
+#define CEX3C_MIN_MOD_SIZE	 16	/*  128 bits	*/
 #define CEX3C_MAX_MOD_SIZE	512	/* 4096 bits	*/
-
-#define PCIXCC_MAX_ICA_MESSAGE_SIZE 0x77c  /* max size type6 v2 crt message */
-#define PCIXCC_MAX_ICA_RESPONSE_SIZE 0x77c /* max size type86 v2 reply	    */
-
-#define PCIXCC_MAX_XCRB_MESSAGE_SIZE (12*1024)
-
-#define PCIXCC_CLEANUP_TIME	(15*HZ)
-
-#define CEIL4(x) ((((x)+3)/4)*4)
-
-struct response_type {
-	struct completion work;
-	int type;
-};
-#define PCIXCC_RESPONSE_TYPE_ICA  0
-#define PCIXCC_RESPONSE_TYPE_XCRB 1
+#define CEX2C_MAX_XCRB_MESSAGE_SIZE (12*1024)
+#define CEX2C_CLEANUP_TIME	(15*HZ)
 
 MODULE_AUTHOR("IBM Corporation");
-MODULE_DESCRIPTION("PCIXCC Cryptographic Coprocessor device driver, " \
-		   "Copyright IBM Corp. 2001, 2012");
+MODULE_DESCRIPTION("CEX2C/CEX3C Cryptographic Coprocessor device driver, " \
+		   "Copyright IBM Corp. 2001, 2018");
 MODULE_LICENSE("GPL");
 
-static struct ap_device_id zcrypt_pcixcc_card_ids[] = {
-	{ .dev_type = AP_DEVICE_TYPE_PCIXCC,
-	  .match_flags = AP_DEVICE_ID_MATCH_CARD_TYPE },
+static struct ap_device_id zcrypt_cex2c_card_ids[] = {
 	{ .dev_type = AP_DEVICE_TYPE_CEX2C,
 	  .match_flags = AP_DEVICE_ID_MATCH_CARD_TYPE },
 	{ .dev_type = AP_DEVICE_TYPE_CEX3C,
@@ -65,11 +46,9 @@
 	{ /* end of list */ },
 };
 
-MODULE_DEVICE_TABLE(ap, zcrypt_pcixcc_card_ids);
+MODULE_DEVICE_TABLE(ap, zcrypt_cex2c_card_ids);
 
-static struct ap_device_id zcrypt_pcixcc_queue_ids[] = {
-	{ .dev_type = AP_DEVICE_TYPE_PCIXCC,
-	  .match_flags = AP_DEVICE_ID_MATCH_QUEUE_TYPE },
+static struct ap_device_id zcrypt_cex2c_queue_ids[] = {
 	{ .dev_type = AP_DEVICE_TYPE_CEX2C,
 	  .match_flags = AP_DEVICE_ID_MATCH_QUEUE_TYPE },
 	{ .dev_type = AP_DEVICE_TYPE_CEX3C,
@@ -77,16 +56,16 @@
 	{ /* end of list */ },
 };
 
-MODULE_DEVICE_TABLE(ap, zcrypt_pcixcc_queue_ids);
+MODULE_DEVICE_TABLE(ap, zcrypt_cex2c_queue_ids);
 
 /**
- * Large random number detection function. Its sends a message to a pcixcc
+ * Large random number detection function. Its sends a message to a CEX2C/CEX3C
  * card to find out if large random numbers are supported.
  * @ap_dev: pointer to the AP device.
  *
  * Returns 1 if large random numbers are supported, 0 if not and < 0 on error.
  */
-static int zcrypt_pcixcc_rng_supported(struct ap_queue *aq)
+static int zcrypt_cex2c_rng_supported(struct ap_queue *aq)
 {
 	struct ap_message ap_msg;
 	unsigned long long psmid;
@@ -147,13 +126,11 @@
 }
 
 /**
- * Probe function for PCIXCC/CEX2C card devices. It always accepts the
- * AP device since the bus_match already checked the hardware type. The
- * PCIXCC cards come in two flavours: micro code level 2 and micro code
- * level 3. This is checked by sending a test message to the device.
+ * Probe function for CEX2C/CEX3C card devices. It always accepts the
+ * AP device since the bus_match already checked the hardware type.
  * @ap_dev: pointer to the AP card device.
  */
-static int zcrypt_pcixcc_card_probe(struct ap_device *ap_dev)
+static int zcrypt_cex2c_card_probe(struct ap_device *ap_dev)
 {
 	/*
 	 * Normalized speed ratings per crypto adapter
@@ -179,9 +156,9 @@
 		zc->type_string = "CEX2C";
 		memcpy(zc->speed_rating, CEX2C_SPEED_IDX,
 		       sizeof(CEX2C_SPEED_IDX));
-		zc->min_mod_size = PCIXCC_MIN_MOD_SIZE;
-		zc->max_mod_size = PCIXCC_MAX_MOD_SIZE;
-		zc->max_exp_bit_length = PCIXCC_MAX_MOD_SIZE;
+		zc->min_mod_size = CEX2C_MIN_MOD_SIZE;
+		zc->max_mod_size = CEX2C_MAX_MOD_SIZE;
+		zc->max_exp_bit_length = CEX2C_MAX_MOD_SIZE;
 		break;
 	case AP_DEVICE_TYPE_CEX3C:
 		zc->user_space_type = ZCRYPT_CEX3C;
@@ -208,10 +185,10 @@
 }
 
 /**
- * This is called to remove the PCIXCC/CEX2C card driver information
+ * This is called to remove the CEX2C/CEX3C card driver information
  * if an AP card device is removed.
  */
-static void zcrypt_pcixcc_card_remove(struct ap_device *ap_dev)
+static void zcrypt_cex2c_card_remove(struct ap_device *ap_dev)
 {
 	struct zcrypt_card *zc = to_ap_card(&ap_dev->device)->private;
 
@@ -219,33 +196,31 @@
 		zcrypt_card_unregister(zc);
 }
 
-static struct ap_driver zcrypt_pcixcc_card_driver = {
-	.probe = zcrypt_pcixcc_card_probe,
-	.remove = zcrypt_pcixcc_card_remove,
-	.ids = zcrypt_pcixcc_card_ids,
+static struct ap_driver zcrypt_cex2c_card_driver = {
+	.probe = zcrypt_cex2c_card_probe,
+	.remove = zcrypt_cex2c_card_remove,
+	.ids = zcrypt_cex2c_card_ids,
 	.flags = AP_DRIVER_FLAG_DEFAULT,
 };
 
 /**
- * Probe function for PCIXCC/CEX2C queue devices. It always accepts the
- * AP device since the bus_match already checked the hardware type. The
- * PCIXCC cards come in two flavours: micro code level 2 and micro code
- * level 3. This is checked by sending a test message to the device.
+ * Probe function for CEX2C/CEX3C queue devices. It always accepts the
+ * AP device since the bus_match already checked the hardware type.
  * @ap_dev: pointer to the AP card device.
  */
-static int zcrypt_pcixcc_queue_probe(struct ap_device *ap_dev)
+static int zcrypt_cex2c_queue_probe(struct ap_device *ap_dev)
 {
 	struct ap_queue *aq = to_ap_queue(&ap_dev->device);
 	struct zcrypt_queue *zq;
 	int rc;
 
-	zq = zcrypt_queue_alloc(PCIXCC_MAX_XCRB_MESSAGE_SIZE);
+	zq = zcrypt_queue_alloc(CEX2C_MAX_XCRB_MESSAGE_SIZE);
 	if (!zq)
 		return -ENOMEM;
 	zq->queue = aq;
 	zq->online = 1;
 	atomic_set(&zq->load, 0);
-	rc = zcrypt_pcixcc_rng_supported(aq);
+	rc = zcrypt_cex2c_rng_supported(aq);
 	if (rc < 0) {
 		zcrypt_queue_free(zq);
 		return rc;
@@ -257,7 +232,7 @@
 		zq->ops = zcrypt_msgtype(MSGTYPE06_NAME,
 					 MSGTYPE06_VARIANT_NORNG);
 	ap_queue_init_reply(aq, &zq->reply);
-	aq->request_timeout = PCIXCC_CLEANUP_TIME,
+	aq->request_timeout = CEX2C_CLEANUP_TIME;
 	aq->private = zq;
 	rc = zcrypt_queue_register(zq);
 	if (rc) {
@@ -268,50 +243,49 @@
 }
 
 /**
- * This is called to remove the PCIXCC/CEX2C queue driver information
+ * This is called to remove the CEX2C/CEX3C queue driver information
  * if an AP queue device is removed.
  */
-static void zcrypt_pcixcc_queue_remove(struct ap_device *ap_dev)
+static void zcrypt_cex2c_queue_remove(struct ap_device *ap_dev)
 {
 	struct ap_queue *aq = to_ap_queue(&ap_dev->device);
 	struct zcrypt_queue *zq = aq->private;
 
-	ap_queue_remove(aq);
 	if (zq)
 		zcrypt_queue_unregister(zq);
 }
 
-static struct ap_driver zcrypt_pcixcc_queue_driver = {
-	.probe = zcrypt_pcixcc_queue_probe,
-	.remove = zcrypt_pcixcc_queue_remove,
+static struct ap_driver zcrypt_cex2c_queue_driver = {
+	.probe = zcrypt_cex2c_queue_probe,
+	.remove = zcrypt_cex2c_queue_remove,
 	.suspend = ap_queue_suspend,
 	.resume = ap_queue_resume,
-	.ids = zcrypt_pcixcc_queue_ids,
+	.ids = zcrypt_cex2c_queue_ids,
 	.flags = AP_DRIVER_FLAG_DEFAULT,
 };
 
-int __init zcrypt_pcixcc_init(void)
+int __init zcrypt_cex2c_init(void)
 {
 	int rc;
 
-	rc = ap_driver_register(&zcrypt_pcixcc_card_driver,
-				THIS_MODULE, "pcixcccard");
+	rc = ap_driver_register(&zcrypt_cex2c_card_driver,
+				THIS_MODULE, "cex2card");
 	if (rc)
 		return rc;
 
-	rc = ap_driver_register(&zcrypt_pcixcc_queue_driver,
-				THIS_MODULE, "pcixccqueue");
+	rc = ap_driver_register(&zcrypt_cex2c_queue_driver,
+				THIS_MODULE, "cex2cqueue");
 	if (rc)
-		ap_driver_unregister(&zcrypt_pcixcc_card_driver);
+		ap_driver_unregister(&zcrypt_cex2c_card_driver);
 
 	return rc;
 }
 
-void zcrypt_pcixcc_exit(void)
+void zcrypt_cex2c_exit(void)
 {
-	ap_driver_unregister(&zcrypt_pcixcc_queue_driver);
-	ap_driver_unregister(&zcrypt_pcixcc_card_driver);
+	ap_driver_unregister(&zcrypt_cex2c_queue_driver);
+	ap_driver_unregister(&zcrypt_cex2c_card_driver);
 }
 
-module_init(zcrypt_pcixcc_init);
-module_exit(zcrypt_pcixcc_exit);
+module_init(zcrypt_cex2c_init);
+module_exit(zcrypt_cex2c_exit);
diff --git a/drivers/s390/crypto/zcrypt_pcixcc.h b/drivers/s390/crypto/zcrypt_cex2c.h
similarity index 63%
rename from drivers/s390/crypto/zcrypt_pcixcc.h
rename to drivers/s390/crypto/zcrypt_cex2c.h
index cf73a0f..6ec405c 100644
--- a/drivers/s390/crypto/zcrypt_pcixcc.h
+++ b/drivers/s390/crypto/zcrypt_cex2c.h
@@ -1,8 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0+ */
 /*
- *  zcrypt 2.1.0
- *
- *  Copyright IBM Corp. 2001, 2012
+ *  Copyright IBM Corp. 2001, 2018
  *  Author(s): Robert Burroughs
  *	       Eric Rossman (edrossma@us.ibm.com)
  *
@@ -11,10 +9,10 @@
  *  MSGTYPE restruct:		  Holger Dengler <hd@linux.vnet.ibm.com>
  */
 
-#ifndef _ZCRYPT_PCIXCC_H_
-#define _ZCRYPT_PCIXCC_H_
+#ifndef _ZCRYPT_CEX2C_H_
+#define _ZCRYPT_CEX2C_H_
 
-int zcrypt_pcixcc_init(void);
-void zcrypt_pcixcc_exit(void);
+int zcrypt_cex2c_init(void);
+void zcrypt_cex2c_exit(void);
 
-#endif /* _ZCRYPT_PCIXCC_H_ */
+#endif /* _ZCRYPT_CEX2C_H_ */
diff --git a/drivers/s390/crypto/zcrypt_cex4.c b/drivers/s390/crypto/zcrypt_cex4.c
index 35d58db..442e3d6 100644
--- a/drivers/s390/crypto/zcrypt_cex4.c
+++ b/drivers/s390/crypto/zcrypt_cex4.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- *  Copyright IBM Corp. 2012
+ *  Copyright IBM Corp. 2012, 2019
  *  Author(s): Holger Dengler <hd@linux.vnet.ibm.com>
  */
 
@@ -18,6 +18,7 @@
 #include "zcrypt_msgtype50.h"
 #include "zcrypt_error.h"
 #include "zcrypt_cex4.h"
+#include "zcrypt_ccamisc.h"
 
 #define CEX4A_MIN_MOD_SIZE	  1	/*    8 bits	*/
 #define CEX4A_MAX_MOD_SIZE_2K	256	/* 2048 bits	*/
@@ -37,8 +38,8 @@
 #define CEX4_CLEANUP_TIME	(900*HZ)
 
 MODULE_AUTHOR("IBM Corporation");
-MODULE_DESCRIPTION("CEX4 Cryptographic Card device driver, " \
-		   "Copyright IBM Corp. 2012");
+MODULE_DESCRIPTION("CEX4/CEX5/CEX6/CEX7 Cryptographic Card device driver, " \
+		   "Copyright IBM Corp. 2019");
 MODULE_LICENSE("GPL");
 
 static struct ap_device_id zcrypt_cex4_card_ids[] = {
@@ -48,6 +49,8 @@
 	  .match_flags = AP_DEVICE_ID_MATCH_CARD_TYPE },
 	{ .dev_type = AP_DEVICE_TYPE_CEX6,
 	  .match_flags = AP_DEVICE_ID_MATCH_CARD_TYPE },
+	{ .dev_type = AP_DEVICE_TYPE_CEX7,
+	  .match_flags = AP_DEVICE_ID_MATCH_CARD_TYPE },
 	{ /* end of list */ },
 };
 
@@ -60,14 +63,96 @@
 	  .match_flags = AP_DEVICE_ID_MATCH_QUEUE_TYPE },
 	{ .dev_type = AP_DEVICE_TYPE_CEX6,
 	  .match_flags = AP_DEVICE_ID_MATCH_QUEUE_TYPE },
+	{ .dev_type = AP_DEVICE_TYPE_CEX7,
+	  .match_flags = AP_DEVICE_ID_MATCH_QUEUE_TYPE },
 	{ /* end of list */ },
 };
 
 MODULE_DEVICE_TABLE(ap, zcrypt_cex4_queue_ids);
 
+/*
+ * CCA card addditional device attributes
+ */
+static ssize_t serialnr_show(struct device *dev,
+			     struct device_attribute *attr,
+			     char *buf)
+{
+	struct cca_info ci;
+	struct ap_card *ac = to_ap_card(dev);
+	struct zcrypt_card *zc = ac->private;
+
+	memset(&ci, 0, sizeof(ci));
+
+	if (ap_domain_index >= 0)
+		cca_get_info(ac->id, ap_domain_index, &ci, zc->online);
+
+	return snprintf(buf, PAGE_SIZE, "%s\n", ci.serial);
+}
+static DEVICE_ATTR_RO(serialnr);
+
+static struct attribute *cca_card_attrs[] = {
+	&dev_attr_serialnr.attr,
+	NULL,
+};
+
+static const struct attribute_group cca_card_attr_group = {
+	.attrs = cca_card_attrs,
+};
+
+/*
+ * CCA queue addditional device attributes
+ */
+static ssize_t mkvps_show(struct device *dev,
+			  struct device_attribute *attr,
+			  char *buf)
+{
+	int n = 0;
+	struct cca_info ci;
+	struct zcrypt_queue *zq = to_ap_queue(dev)->private;
+	static const char * const cao_state[] = { "invalid", "valid" };
+	static const char * const new_state[] = { "empty", "partial", "full" };
+
+	memset(&ci, 0, sizeof(ci));
+
+	cca_get_info(AP_QID_CARD(zq->queue->qid),
+		     AP_QID_QUEUE(zq->queue->qid),
+		     &ci, zq->online);
+
+	if (ci.new_mk_state >= '1' && ci.new_mk_state <= '3')
+		n = snprintf(buf, PAGE_SIZE, "AES NEW: %s 0x%016llx\n",
+			     new_state[ci.new_mk_state - '1'], ci.new_mkvp);
+	else
+		n = snprintf(buf, PAGE_SIZE, "AES NEW: - -\n");
+
+	if (ci.cur_mk_state >= '1' && ci.cur_mk_state <= '2')
+		n += snprintf(buf + n, PAGE_SIZE - n, "AES CUR: %s 0x%016llx\n",
+			      cao_state[ci.cur_mk_state - '1'], ci.cur_mkvp);
+	else
+		n += snprintf(buf + n, PAGE_SIZE - n, "AES CUR: - -\n");
+
+	if (ci.old_mk_state >= '1' && ci.old_mk_state <= '2')
+		n += snprintf(buf + n, PAGE_SIZE - n, "AES OLD: %s 0x%016llx\n",
+			      cao_state[ci.old_mk_state - '1'], ci.old_mkvp);
+	else
+		n += snprintf(buf + n, PAGE_SIZE - n, "AES OLD: - -\n");
+
+	return n;
+}
+static DEVICE_ATTR_RO(mkvps);
+
+static struct attribute *cca_queue_attrs[] = {
+	&dev_attr_mkvps.attr,
+	NULL,
+};
+
+static const struct attribute_group cca_queue_attr_group = {
+	.attrs = cca_queue_attrs,
+};
+
 /**
- * Probe function for CEX4 card device. It always accepts the AP device
- * since the bus_match already checked the hardware type.
+ * Probe function for CEX4/CEX5/CEX6/CEX7 card device. It always
+ * accepts the AP device since the bus_match already checked
+ * the hardware type.
  * @ap_dev: pointer to the AP device.
  */
 static int zcrypt_cex4_card_probe(struct ap_device *ap_dev)
@@ -77,25 +162,31 @@
 	 * MEX_1k, MEX_2k, MEX_4k, CRT_1k, CRT_2k, CRT_4k, RNG, SECKEY
 	 */
 	static const int CEX4A_SPEED_IDX[] = {
-		 14, 19, 249, 42, 228, 1458, 0, 0};
+		 14,  19, 249, 42, 228, 1458, 0, 0};
 	static const int CEX5A_SPEED_IDX[] = {
-		  8,  9,  20, 18,  66,	458, 0, 0};
+		  8,   9,  20, 18,  66,	 458, 0, 0};
 	static const int CEX6A_SPEED_IDX[] = {
-		  6,  9,  20, 17,  65,	438, 0, 0};
+		  6,   9,  20, 17,  65,	 438, 0, 0};
+	static const int CEX7A_SPEED_IDX[] = {
+		  6,   8,  17, 15,  54,	 362, 0, 0};
 
 	static const int CEX4C_SPEED_IDX[] = {
 		 59,  69, 308, 83, 278, 2204, 209, 40};
 	static const int CEX5C_SPEED_IDX[] = {
-		 24,  31,  50, 37,  90,  479,  27, 10};
+		 24,  31,  50, 37,  90,	 479,  27, 10};
 	static const int CEX6C_SPEED_IDX[] = {
-		 16,  20,  32, 27,  77,  455,  23,  9};
+		 16,  20,  32, 27,  77,	 455,  24,  9};
+	static const int CEX7C_SPEED_IDX[] = {
+		 14,  16,  26, 23,  64,	 376,  23,  8};
 
 	static const int CEX4P_SPEED_IDX[] = {
-		224, 313, 3560, 359, 605, 2827, 0, 50};
+		  0,   0,   0,	 0,   0,   0,	0,  50};
 	static const int CEX5P_SPEED_IDX[] = {
-		 63,  84,  156,  83, 142,  533, 0, 10};
+		  0,   0,   0,	 0,   0,   0,	0,  10};
 	static const int CEX6P_SPEED_IDX[] = {
-		 55,  70,  121,  73, 129,  522, 0,  9};
+		  0,   0,   0,	 0,   0,   0,	0,   9};
+	static const int CEX7P_SPEED_IDX[] = {
+		  0,   0,   0,	 0,   0,   0,	0,   8};
 
 	struct ap_card *ac = to_ap_card(&ap_dev->device);
 	struct zcrypt_card *zc;
@@ -117,11 +208,19 @@
 			zc->user_space_type = ZCRYPT_CEX5;
 			memcpy(zc->speed_rating, CEX5A_SPEED_IDX,
 			       sizeof(CEX5A_SPEED_IDX));
-		} else {
+		} else if (ac->ap_dev.device_type == AP_DEVICE_TYPE_CEX6) {
 			zc->type_string = "CEX6A";
 			zc->user_space_type = ZCRYPT_CEX6;
 			memcpy(zc->speed_rating, CEX6A_SPEED_IDX,
 			       sizeof(CEX6A_SPEED_IDX));
+		} else {
+			zc->type_string = "CEX7A";
+			/* wrong user space type, just for compatibility
+			 * with the ZCRYPT_STATUS_MASK ioctl.
+			 */
+			zc->user_space_type = ZCRYPT_CEX6;
+			memcpy(zc->speed_rating, CEX7A_SPEED_IDX,
+			       sizeof(CEX7A_SPEED_IDX));
 		}
 		zc->min_mod_size = CEX4A_MIN_MOD_SIZE;
 		if (ap_test_bit(&ac->functions, AP_FUNC_MEX4K) &&
@@ -151,7 +250,7 @@
 			zc->user_space_type = ZCRYPT_CEX3C;
 			memcpy(zc->speed_rating, CEX5C_SPEED_IDX,
 			       sizeof(CEX5C_SPEED_IDX));
-		} else {
+		} else if (ac->ap_dev.device_type == AP_DEVICE_TYPE_CEX6) {
 			zc->type_string = "CEX6C";
 			/* wrong user space type, must be CEX6
 			 * just keep it for cca compatibility
@@ -159,6 +258,14 @@
 			zc->user_space_type = ZCRYPT_CEX3C;
 			memcpy(zc->speed_rating, CEX6C_SPEED_IDX,
 			       sizeof(CEX6C_SPEED_IDX));
+		} else {
+			zc->type_string = "CEX7C";
+			/* wrong user space type, must be CEX7
+			 * just keep it for cca compatibility
+			 */
+			zc->user_space_type = ZCRYPT_CEX3C;
+			memcpy(zc->speed_rating, CEX7C_SPEED_IDX,
+			       sizeof(CEX7C_SPEED_IDX));
 		}
 		zc->min_mod_size = CEX4C_MIN_MOD_SIZE;
 		zc->max_mod_size = CEX4C_MAX_MOD_SIZE;
@@ -174,11 +281,19 @@
 			zc->user_space_type = ZCRYPT_CEX5;
 			memcpy(zc->speed_rating, CEX5P_SPEED_IDX,
 			       sizeof(CEX5P_SPEED_IDX));
-		} else {
+		} else if (ac->ap_dev.device_type == AP_DEVICE_TYPE_CEX6) {
 			zc->type_string = "CEX6P";
 			zc->user_space_type = ZCRYPT_CEX6;
 			memcpy(zc->speed_rating, CEX6P_SPEED_IDX,
 			       sizeof(CEX6P_SPEED_IDX));
+		} else {
+			zc->type_string = "CEX7P";
+			/* wrong user space type, just for compatibility
+			 * with the ZCRYPT_STATUS_MASK ioctl.
+			 */
+			zc->user_space_type = ZCRYPT_CEX6;
+			memcpy(zc->speed_rating, CEX7P_SPEED_IDX,
+			       sizeof(CEX7P_SPEED_IDX));
 		}
 		zc->min_mod_size = CEX4C_MIN_MOD_SIZE;
 		zc->max_mod_size = CEX4C_MAX_MOD_SIZE;
@@ -193,19 +308,31 @@
 	if (rc) {
 		ac->private = NULL;
 		zcrypt_card_free(zc);
+		goto out;
 	}
 
+	if (ap_test_bit(&ac->functions, AP_FUNC_COPRO)) {
+		rc = sysfs_create_group(&ap_dev->device.kobj,
+					&cca_card_attr_group);
+		if (rc)
+			zcrypt_card_unregister(zc);
+	}
+
+out:
 	return rc;
 }
 
 /**
- * This is called to remove the CEX4 card driver information
- * if an AP card device is removed.
+ * This is called to remove the CEX4/CEX5/CEX6/CEX7 card driver
+ * information if an AP card device is removed.
  */
 static void zcrypt_cex4_card_remove(struct ap_device *ap_dev)
 {
-	struct zcrypt_card *zc = to_ap_card(&ap_dev->device)->private;
+	struct ap_card *ac = to_ap_card(&ap_dev->device);
+	struct zcrypt_card *zc = ac->private;
 
+	if (ap_test_bit(&ac->functions, AP_FUNC_COPRO))
+		sysfs_remove_group(&ap_dev->device.kobj, &cca_card_attr_group);
 	if (zc)
 		zcrypt_card_unregister(zc);
 }
@@ -218,8 +345,9 @@
 };
 
 /**
- * Probe function for CEX4 queue device. It always accepts the AP device
- * since the bus_match already checked the hardware type.
+ * Probe function for CEX4/CEX5/CEX6/CEX7 queue device. It always
+ * accepts the AP device since the bus_match already checked
+ * the hardware type.
  * @ap_dev: pointer to the AP device.
  */
 static int zcrypt_cex4_queue_probe(struct ap_device *ap_dev)
@@ -249,6 +377,7 @@
 	} else {
 		return -ENODEV;
 	}
+
 	zq->queue = aq;
 	zq->online = 1;
 	atomic_set(&zq->load, 0);
@@ -259,21 +388,31 @@
 	if (rc) {
 		aq->private = NULL;
 		zcrypt_queue_free(zq);
+		goto out;
 	}
 
+	if (ap_test_bit(&aq->card->functions, AP_FUNC_COPRO)) {
+		rc = sysfs_create_group(&ap_dev->device.kobj,
+					&cca_queue_attr_group);
+		if (rc)
+			zcrypt_queue_unregister(zq);
+	}
+
+out:
 	return rc;
 }
 
 /**
- * This is called to remove the CEX4 queue driver information
- * if an AP queue device is removed.
+ * This is called to remove the CEX4/CEX5/CEX6/CEX7 queue driver
+ * information if an AP queue device is removed.
  */
 static void zcrypt_cex4_queue_remove(struct ap_device *ap_dev)
 {
 	struct ap_queue *aq = to_ap_queue(&ap_dev->device);
 	struct zcrypt_queue *zq = aq->private;
 
-	ap_queue_remove(aq);
+	if (ap_test_bit(&aq->card->functions, AP_FUNC_COPRO))
+		sysfs_remove_group(&ap_dev->device.kobj, &cca_queue_attr_group);
 	if (zq)
 		zcrypt_queue_unregister(zq);
 }
diff --git a/drivers/s390/crypto/zcrypt_error.h b/drivers/s390/crypto/zcrypt_error.h
index 6f7ebc1..f34ee41 100644
--- a/drivers/s390/crypto/zcrypt_error.h
+++ b/drivers/s390/crypto/zcrypt_error.h
@@ -1,7 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0+ */
 /*
- *  zcrypt 2.1.0
- *
  *  Copyright IBM Corp. 2001, 2006
  *  Author(s): Robert Burroughs
  *	       Eric Rossman (edrossma@us.ibm.com)
@@ -16,6 +14,7 @@
 #include <linux/atomic.h>
 #include "zcrypt_debug.h"
 #include "zcrypt_api.h"
+#include "zcrypt_msgtype6.h"
 
 /**
  * Reply Messages
@@ -52,6 +51,7 @@
 #define REP82_ERROR_FORMAT_FIELD	    0x29
 #define REP82_ERROR_INVALID_COMMAND	    0x30
 #define REP82_ERROR_MALFORMED_MSG	    0x40
+#define REP82_ERROR_INVALID_SPECIAL_CMD	    0x41
 #define REP82_ERROR_INVALID_DOMAIN_PRECHECK 0x42
 #define REP82_ERROR_RESERVED_FIELDO	    0x50 /* old value	*/
 #define REP82_ERROR_WORD_ALIGNMENT	    0x60
@@ -90,6 +90,7 @@
 	case REP88_ERROR_MESSAGE_MALFORMD:
 	case REP82_ERROR_INVALID_DOMAIN_PRECHECK:
 	case REP82_ERROR_INVALID_DOMAIN_PENDING:
+	case REP82_ERROR_INVALID_SPECIAL_CMD:
 	//   REP88_ERROR_INVALID_KEY		// '82' CEX2A
 	//   REP88_ERROR_OPERAND		// '84' CEX2A
 	//   REP88_ERROR_OPERAND_EVEN_MOD	// '85' CEX2A
@@ -114,6 +115,27 @@
 			   card, queue, ehdr->reply_code);
 		return -EAGAIN;
 	case REP82_ERROR_TRANSPORT_FAIL:
+		/* Card or infrastructure failure, disable card */
+		atomic_set(&zcrypt_rescan_req, 1);
+		zq->online = 0;
+		pr_err("Cryptographic device %02x.%04x failed and was set offline\n",
+		       card, queue);
+		/* For type 86 response show the apfs value (failure reason) */
+		if (ehdr->type == TYPE86_RSP_CODE) {
+			struct {
+				struct type86_hdr hdr;
+				struct type86_fmt2_ext fmt2;
+			} __packed * head = reply->message;
+			unsigned int apfs = *((u32 *)head->fmt2.apfs);
+
+			ZCRYPT_DBF(DBF_ERR,
+				   "device=%02x.%04x reply=0x%02x apfs=0x%x => online=0 rc=EAGAIN\n",
+				   card, queue, apfs, ehdr->reply_code);
+		} else
+			ZCRYPT_DBF(DBF_ERR,
+				   "device=%02x.%04x reply=0x%02x => online=0 rc=EAGAIN\n",
+				   card, queue, ehdr->reply_code);
+		return -EAGAIN;
 	case REP82_ERROR_MACHINE_FAILURE:
 	//   REP88_ERROR_MODULE_FAILURE		// '10' CEX2A
 		/* If a card fails disable it and repeat the request. */
diff --git a/drivers/s390/crypto/zcrypt_msgtype50.c b/drivers/s390/crypto/zcrypt_msgtype50.c
index f159662..fc4295b 100644
--- a/drivers/s390/crypto/zcrypt_msgtype50.c
+++ b/drivers/s390/crypto/zcrypt_msgtype50.c
@@ -1,7 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- *  zcrypt 2.1.0
- *
  *  Copyright IBM Corp. 2001, 2012
  *  Author(s): Robert Burroughs
  *	       Eric Rossman (edrossma@us.ibm.com)
@@ -27,13 +25,13 @@
 #include "zcrypt_error.h"
 #include "zcrypt_msgtype50.h"
 
-/* 4096 bits */
+/* >= CEX3A: 4096 bits */
 #define CEX3A_MAX_MOD_SIZE 512
 
-/* max outputdatalength + type80_hdr */
+/* CEX2A: max outputdatalength + type80_hdr */
 #define CEX2A_MAX_RESPONSE_SIZE 0x110
 
-/* 512 bit modulus, (max outputdatalength) + type80_hdr */
+/* >= CEX3A: 512 bit modulus, (max outputdatalength) + type80_hdr */
 #define CEX3A_MAX_RESPONSE_SIZE 0x210
 
 MODULE_AUTHOR("IBM Corporation");
@@ -42,7 +40,7 @@
 MODULE_LICENSE("GPL");
 
 /**
- * The type 50 message family is associated with a CEX2A card.
+ * The type 50 message family is associated with a CEXxA cards.
  *
  * The four members of the family are described below.
  *
@@ -139,7 +137,7 @@
 } __packed;
 
 /**
- * The type 80 response family is associated with a CEX2A card.
+ * The type 80 response family is associated with a CEXxA cards.
  *
  * Note that all unsigned char arrays are right-justified and left-padded
  * with zeroes.
@@ -273,7 +271,7 @@
 	/*
 	 * CEX2A and CEX3A w/o FW update can handle requests up to
 	 * 256 byte modulus (2k keys).
-	 * CEX3A with FW update and CEX4A cards are able to handle
+	 * CEX3A with FW update and newer CEXxA cards are able to handle
 	 * 512 byte modulus (4k keys).
 	 */
 	if (mod_len <= 128) {		/* up to 1024 bit key size */
@@ -356,7 +354,7 @@
 	unsigned char *data;
 
 	if (t80h->len < sizeof(*t80h) + outputdatalength) {
-		/* The result is too short, the CEX2A card may not do that.. */
+		/* The result is too short, the CEXxA card may not do that.. */
 		zq->online = 0;
 		pr_err("Cryptographic device %02x.%04x failed and was set offline\n",
 		       AP_QID_CARD(zq->queue->qid),
@@ -447,10 +445,10 @@
 static atomic_t zcrypt_step = ATOMIC_INIT(0);
 
 /**
- * The request distributor calls this function if it picked the CEX2A
+ * The request distributor calls this function if it picked the CEXxA
  * device to handle a modexpo request.
  * @zq: pointer to zcrypt_queue structure that identifies the
- *	  CEX2A device to the request distributor
+ *	CEXxA device to the request distributor
  * @mex: pointer to the modexpo request buffer
  */
 static long zcrypt_cex2a_modexpo(struct zcrypt_queue *zq,
@@ -493,10 +491,10 @@
 }
 
 /**
- * The request distributor calls this function if it picked the CEX2A
+ * The request distributor calls this function if it picked the CEXxA
  * device to handle a modexpo_crt request.
  * @zq: pointer to zcrypt_queue structure that identifies the
- *	  CEX2A device to the request distributor
+ *	CEXxA device to the request distributor
  * @crt: pointer to the modexpoc_crt request buffer
  */
 static long zcrypt_cex2a_modexpo_crt(struct zcrypt_queue *zq,
diff --git a/drivers/s390/crypto/zcrypt_msgtype50.h b/drivers/s390/crypto/zcrypt_msgtype50.h
index 8530f65..66bec4f 100644
--- a/drivers/s390/crypto/zcrypt_msgtype50.h
+++ b/drivers/s390/crypto/zcrypt_msgtype50.h
@@ -1,7 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0+ */
 /*
- *  zcrypt 2.1.0
- *
  *  Copyright IBM Corp. 2001, 2012
  *  Author(s): Robert Burroughs
  *	       Eric Rossman (edrossma@us.ibm.com)
diff --git a/drivers/s390/crypto/zcrypt_msgtype6.c b/drivers/s390/crypto/zcrypt_msgtype6.c
index 2101776..a36251d 100644
--- a/drivers/s390/crypto/zcrypt_msgtype6.c
+++ b/drivers/s390/crypto/zcrypt_msgtype6.c
@@ -1,7 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- *  zcrypt 2.1.0
- *
  *  Copyright IBM Corp. 2001, 2012
  *  Author(s): Robert Burroughs
  *	       Eric Rossman (edrossma@us.ibm.com)
@@ -29,8 +27,7 @@
 #include "zcrypt_msgtype6.h"
 #include "zcrypt_cca_key.h"
 
-#define PCIXCC_MIN_MOD_SIZE_OLD	 64	/*  512 bits	*/
-#define PCIXCC_MAX_ICA_RESPONSE_SIZE 0x77c /* max size type86 v2 reply	    */
+#define CEXXC_MAX_ICA_RESPONSE_SIZE 0x77c /* max size type86 v2 reply	    */
 
 #define CEIL4(x) ((((x)+3)/4)*4)
 
@@ -38,9 +35,9 @@
 	struct completion work;
 	int type;
 };
-#define PCIXCC_RESPONSE_TYPE_ICA  0
-#define PCIXCC_RESPONSE_TYPE_XCRB 1
-#define PCIXCC_RESPONSE_TYPE_EP11 2
+#define CEXXC_RESPONSE_TYPE_ICA  0
+#define CEXXC_RESPONSE_TYPE_XCRB 1
+#define CEXXC_RESPONSE_TYPE_EP11 2
 
 MODULE_AUTHOR("IBM Corporation");
 MODULE_DESCRIPTION("Cryptographic Coprocessor (message type 6), " \
@@ -111,7 +108,7 @@
 } __packed;
 
 /**
- * The following is used to initialize the CPRBX passed to the PCIXCC/CEX2C
+ * The following is used to initialize the CPRBX passed to the CEXxC/CEXxP
  * card in a type6 message. The 3 fields that must be filled in at execution
  * time are  req_parml, rpl_parml and usage_domain.
  * Everything about this interface is ascii/big-endian, since the
@@ -294,7 +291,7 @@
 	/* message header, cprbx and f&r */
 	msg->hdr = static_type6_hdrX;
 	msg->hdr.ToCardLen1 = size - sizeof(msg->hdr);
-	msg->hdr.FromCardLen1 = PCIXCC_MAX_ICA_RESPONSE_SIZE - sizeof(msg->hdr);
+	msg->hdr.FromCardLen1 = CEXXC_MAX_ICA_RESPONSE_SIZE - sizeof(msg->hdr);
 
 	msg->cprbx = static_cprbx;
 	msg->cprbx.domain = AP_QID_QUEUE(zq->queue->qid);
@@ -364,7 +361,7 @@
 	/* message header, cprbx and f&r */
 	msg->hdr = static_type6_hdrX;
 	msg->hdr.ToCardLen1 = size -  sizeof(msg->hdr);
-	msg->hdr.FromCardLen1 = PCIXCC_MAX_ICA_RESPONSE_SIZE - sizeof(msg->hdr);
+	msg->hdr.FromCardLen1 = CEXXC_MAX_ICA_RESPONSE_SIZE - sizeof(msg->hdr);
 
 	msg->cprbx = static_cprbx;
 	msg->cprbx.domain = AP_QID_QUEUE(zq->queue->qid);
@@ -570,6 +567,10 @@
 	payload_hdr = (struct pld_hdr *)((&(msg->pld_lenfmt))+lfmt);
 	*fcode = payload_hdr->func_val & 0xFFFF;
 
+	/* enable special processing based on the cprbs flags special bit */
+	if (msg->cprbx.flags & 0x20)
+		ap_msg->special = 1;
+
 	return 0;
 }
 
@@ -658,16 +659,6 @@
 				   (int) service_rc, (int) service_rs);
 			return -EINVAL;
 		}
-		if (service_rc == 8 && service_rs == 783) {
-			zq->zcard->min_mod_size =
-				PCIXCC_MIN_MOD_SIZE_OLD;
-			ZCRYPT_DBF(DBF_DEBUG,
-				   "device=%02x.%04x rc/rs=%d/%d => rc=EAGAIN\n",
-				   AP_QID_CARD(zq->queue->qid),
-				   AP_QID_QUEUE(zq->queue->qid),
-				   (int) service_rc, (int) service_rs);
-			return -EAGAIN;
-		}
 		zq->online = 0;
 		pr_err("Cryptographic device %02x.%04x failed and was set offline\n",
 		       AP_QID_CARD(zq->queue->qid),
@@ -697,7 +688,7 @@
 	if (pad_len > 0) {
 		if (pad_len < 10)
 			return -EINVAL;
-		/* 'restore' padding left in the PCICC/PCIXCC card. */
+		/* 'restore' padding left in the CEXXC card. */
 		if (copy_to_user(outputdata, static_pad, pad_len - 1))
 			return -EFAULT;
 		if (put_user(0, outputdata + pad_len - 1))
@@ -810,10 +801,7 @@
 		if (msg->cprbx.cprb_ver_id == 0x02)
 			return convert_type86_ica(zq, reply,
 						  outputdata, outputdatalength);
-		/*
-		 * Fall through, no break, incorrect cprb version is an unknown
-		 * response
-		 */
+		/* fall through - wrong cprb version is an unknown response */
 	default: /* Unknown response type, this should NEVER EVER happen */
 		zq->online = 0;
 		pr_err("Cryptographic device %02x.%04x failed and was set offline\n",
@@ -846,10 +834,7 @@
 		}
 		if (msg->cprbx.cprb_ver_id == 0x02)
 			return convert_type86_xcrb(zq, reply, xcRB);
-		/*
-		 * Fall through, no break, incorrect cprb version is an unknown
-		 * response
-		 */
+		/* fall through - wrong cprb version is an unknown response */
 	default: /* Unknown response type, this should NEVER EVER happen */
 		xcRB->status = 0x0008044DL; /* HDD_InvalidParm */
 		zq->online = 0;
@@ -879,7 +864,7 @@
 			return convert_error(zq, reply);
 		if (msg->cprbx.cprb_ver_id == 0x04)
 			return convert_type86_ep11_xcrb(zq, reply, xcRB);
-	/* Fall through, no break, incorrect cprb version is an unknown resp.*/
+		/* fall through - wrong cprb version is an unknown resp */
 	default: /* Unknown response type, this should NEVER EVER happen */
 		zq->online = 0;
 		pr_err("Cryptographic device %02x.%04x failed and was set offline\n",
@@ -909,10 +894,7 @@
 			return -EINVAL;
 		if (msg->cprbx.cprb_ver_id == 0x02)
 			return convert_type86_rng(zq, reply, data);
-		/*
-		 * Fall through, no break, incorrect cprb version is an unknown
-		 * response
-		 */
+		/* fall through - wrong cprb version is an unknown response */
 	default: /* Unknown response type, this should NEVER EVER happen */
 		zq->online = 0;
 		pr_err("Cryptographic device %02x.%04x failed and was set offline\n",
@@ -955,13 +937,13 @@
 	if (t86r->hdr.type == TYPE86_RSP_CODE &&
 		 t86r->cprbx.cprb_ver_id == 0x02) {
 		switch (resp_type->type) {
-		case PCIXCC_RESPONSE_TYPE_ICA:
+		case CEXXC_RESPONSE_TYPE_ICA:
 			length = sizeof(struct type86x_reply)
 				+ t86r->length - 2;
-			length = min(PCIXCC_MAX_ICA_RESPONSE_SIZE, length);
+			length = min(CEXXC_MAX_ICA_RESPONSE_SIZE, length);
 			memcpy(msg->message, reply->message, length);
 			break;
-		case PCIXCC_RESPONSE_TYPE_XCRB:
+		case CEXXC_RESPONSE_TYPE_XCRB:
 			length = t86r->fmt2.offset2 + t86r->fmt2.count2;
 			length = min(MSGTYPE06_MAX_MSG_SIZE, length);
 			memcpy(msg->message, reply->message, length);
@@ -1004,7 +986,7 @@
 	if (t86r->hdr.type == TYPE86_RSP_CODE &&
 	    t86r->cprbx.cprb_ver_id == 0x04) {
 		switch (resp_type->type) {
-		case PCIXCC_RESPONSE_TYPE_EP11:
+		case CEXXC_RESPONSE_TYPE_EP11:
 			length = t86r->fmt2.offset1 + t86r->fmt2.count1;
 			length = min(MSGTYPE06_MAX_MSG_SIZE, length);
 			memcpy(msg->message, reply->message, length);
@@ -1022,10 +1004,10 @@
 static atomic_t zcrypt_step = ATOMIC_INIT(0);
 
 /**
- * The request distributor calls this function if it picked the PCIXCC/CEX2C
+ * The request distributor calls this function if it picked the CEXxC
  * device to handle a modexpo request.
  * @zq: pointer to zcrypt_queue structure that identifies the
- *	  PCIXCC/CEX2C device to the request distributor
+ *	CEXxC device to the request distributor
  * @mex: pointer to the modexpo request buffer
  */
 static long zcrypt_msgtype6_modexpo(struct zcrypt_queue *zq,
@@ -1033,7 +1015,7 @@
 {
 	struct ap_message ap_msg;
 	struct response_type resp_type = {
-		.type = PCIXCC_RESPONSE_TYPE_ICA,
+		.type = CEXXC_RESPONSE_TYPE_ICA,
 	};
 	int rc;
 
@@ -1066,10 +1048,10 @@
 }
 
 /**
- * The request distributor calls this function if it picked the PCIXCC/CEX2C
+ * The request distributor calls this function if it picked the CEXxC
  * device to handle a modexpo_crt request.
  * @zq: pointer to zcrypt_queue structure that identifies the
- *	  PCIXCC/CEX2C device to the request distributor
+ *	CEXxC device to the request distributor
  * @crt: pointer to the modexpoc_crt request buffer
  */
 static long zcrypt_msgtype6_modexpo_crt(struct zcrypt_queue *zq,
@@ -1077,7 +1059,7 @@
 {
 	struct ap_message ap_msg;
 	struct response_type resp_type = {
-		.type = PCIXCC_RESPONSE_TYPE_ICA,
+		.type = CEXXC_RESPONSE_TYPE_ICA,
 	};
 	int rc;
 
@@ -1122,7 +1104,7 @@
 				unsigned int *func_code, unsigned short **dom)
 {
 	struct response_type resp_type = {
-		.type = PCIXCC_RESPONSE_TYPE_XCRB,
+		.type = CEXXC_RESPONSE_TYPE_XCRB,
 	};
 
 	ap_msg->message = kmalloc(MSGTYPE06_MAX_MSG_SIZE, GFP_KERNEL);
@@ -1131,18 +1113,17 @@
 	ap_msg->receive = zcrypt_msgtype6_receive;
 	ap_msg->psmid = (((unsigned long long) current->pid) << 32) +
 				atomic_inc_return(&zcrypt_step);
-	ap_msg->private = kmalloc(sizeof(resp_type), GFP_KERNEL);
+	ap_msg->private = kmemdup(&resp_type, sizeof(resp_type), GFP_KERNEL);
 	if (!ap_msg->private)
 		return -ENOMEM;
-	memcpy(ap_msg->private, &resp_type, sizeof(resp_type));
 	return XCRB_msg_to_type6CPRB_msgX(ap_msg, xcRB, func_code, dom);
 }
 
 /**
- * The request distributor calls this function if it picked the PCIXCC/CEX2C
+ * The request distributor calls this function if it picked the CEXxC
  * device to handle a send_cprb request.
  * @zq: pointer to zcrypt_queue structure that identifies the
- *	  PCIXCC/CEX2C device to the request distributor
+ *	CEXxC device to the request distributor
  * @xcRB: pointer to the send_cprb request buffer
  */
 static long zcrypt_msgtype6_send_cprb(struct zcrypt_queue *zq,
@@ -1178,7 +1159,7 @@
 				    unsigned int *func_code)
 {
 	struct response_type resp_type = {
-		.type = PCIXCC_RESPONSE_TYPE_EP11,
+		.type = CEXXC_RESPONSE_TYPE_EP11,
 	};
 
 	ap_msg->message = kmalloc(MSGTYPE06_MAX_MSG_SIZE, GFP_KERNEL);
@@ -1187,10 +1168,9 @@
 	ap_msg->receive = zcrypt_msgtype6_receive_ep11;
 	ap_msg->psmid = (((unsigned long long) current->pid) << 32) +
 				atomic_inc_return(&zcrypt_step);
-	ap_msg->private = kmalloc(sizeof(resp_type), GFP_KERNEL);
+	ap_msg->private = kmemdup(&resp_type, sizeof(resp_type), GFP_KERNEL);
 	if (!ap_msg->private)
 		return -ENOMEM;
-	memcpy(ap_msg->private, &resp_type, sizeof(resp_type));
 	return xcrb_msg_to_type6_ep11cprb_msgx(ap_msg, xcrb, func_code);
 }
 
@@ -1273,7 +1253,7 @@
 						   unsigned int *domain)
 {
 	struct response_type resp_type = {
-		.type = PCIXCC_RESPONSE_TYPE_XCRB,
+		.type = CEXXC_RESPONSE_TYPE_XCRB,
 	};
 
 	ap_msg->message = kmalloc(MSGTYPE06_MAX_MSG_SIZE, GFP_KERNEL);
@@ -1282,10 +1262,9 @@
 	ap_msg->receive = zcrypt_msgtype6_receive;
 	ap_msg->psmid = (((unsigned long long) current->pid) << 32) +
 				atomic_inc_return(&zcrypt_step);
-	ap_msg->private = kmalloc(sizeof(resp_type), GFP_KERNEL);
+	ap_msg->private = kmemdup(&resp_type, sizeof(resp_type), GFP_KERNEL);
 	if (!ap_msg->private)
 		return -ENOMEM;
-	memcpy(ap_msg->private, &resp_type, sizeof(resp_type));
 
 	rng_type6CPRB_msgX(ap_msg, ZCRYPT_RNG_BUFFER_SIZE, domain);
 
@@ -1294,10 +1273,10 @@
 }
 
 /**
- * The request distributor calls this function if it picked the PCIXCC/CEX2C
+ * The request distributor calls this function if it picked the CEXxC
  * device to generate random data.
  * @zq: pointer to zcrypt_queue structure that identifies the
- *	  PCIXCC/CEX2C device to the request distributor
+ *	CEXxC device to the request distributor
  * @buffer: pointer to a memory page to return random data
  */
 static long zcrypt_msgtype6_rng(struct zcrypt_queue *zq,
@@ -1332,7 +1311,7 @@
 }
 
 /**
- * The crypto operations for a PCIXCC/CEX2C card.
+ * The crypto operations for a CEXxC card.
  */
 static struct zcrypt_ops zcrypt_msgtype6_norng_ops = {
 	.owner = THIS_MODULE,
diff --git a/drivers/s390/crypto/zcrypt_msgtype6.h b/drivers/s390/crypto/zcrypt_msgtype6.h
index e4c2f37..41a0df5 100644
--- a/drivers/s390/crypto/zcrypt_msgtype6.h
+++ b/drivers/s390/crypto/zcrypt_msgtype6.h
@@ -1,7 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0+ */
 /*
- *  zcrypt 2.1.0
- *
  *  Copyright IBM Corp. 2001, 2012
  *  Author(s): Robert Burroughs
  *	       Eric Rossman (edrossma@us.ibm.com)
@@ -24,7 +22,7 @@
 #define MSGTYPE06_MAX_MSG_SIZE		(12*1024)
 
 /**
- * The type 6 message family is associated with PCICC or PCIXCC cards.
+ * The type 6 message family is associated with CEXxC/CEXxP cards.
  *
  * It contains a message header followed by a CPRB, both of which
  * are described below.
@@ -43,13 +41,8 @@
 	unsigned int  offset2;		/* 0x00000000			*/
 	unsigned int  offset3;		/* 0x00000000			*/
 	unsigned int  offset4;		/* 0x00000000			*/
-	unsigned char agent_id[16];	/* PCICC:			*/
-					/*    0x0100			*/
-					/*    0x4343412d4150504c202020	*/
-					/*    0x010101			*/
-					/* PCIXCC:			*/
-					/*    0x4341000000000000	*/
-					/*    0x0000000000000000	*/
+	unsigned char agent_id[16];	/* 0x4341000000000000		*/
+					/* 0x0000000000000000		*/
 	unsigned char rqid[2];		/* rqid.  internal to 603	*/
 	unsigned char reserved5[2];	/* 0x0000			*/
 	unsigned char function_code[2];	/* for PKD, 0x5044 (ascii 'PD')	*/
@@ -65,7 +58,7 @@
 } __packed;
 
 /**
- * The type 86 message family is associated with PCICC and PCIXCC cards.
+ * The type 86 message family is associated with CEXxC/CEXxP cards.
  *
  * It contains a message header followed by a CPRB.  The CPRB is
  * the same as the request CPRB, which is described above.
diff --git a/drivers/s390/crypto/zcrypt_queue.c b/drivers/s390/crypto/zcrypt_queue.c
index 8df82c6..522c4bc 100644
--- a/drivers/s390/crypto/zcrypt_queue.c
+++ b/drivers/s390/crypto/zcrypt_queue.c
@@ -1,7 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- *  zcrypt 2.1.0
- *
  *  Copyright IBM Corp. 2001, 2012
  *  Author(s): Robert Burroughs
  *	       Eric Rossman (edrossma@us.ibm.com)