]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
s390/zcrypt: Separate msgtype implementation from card modules.
authorHolger Dengler <hd@linux.vnet.ibm.com>
Tue, 28 Aug 2012 14:45:36 +0000 (16:45 +0200)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Wed, 26 Sep 2012 13:44:55 +0000 (15:44 +0200)
Msgtype implementations are now separated from card specific modules
and can be dynamically registered. Existing msgtype implementations
are restructured in modules.

Signed-off-by: Holger Dengler <hd@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
drivers/s390/crypto/Makefile
drivers/s390/crypto/zcrypt_api.c
drivers/s390/crypto/zcrypt_api.h
drivers/s390/crypto/zcrypt_cex2a.c
drivers/s390/crypto/zcrypt_msgtype50.c [new file with mode: 0644]
drivers/s390/crypto/zcrypt_msgtype50.h [new file with mode: 0644]
drivers/s390/crypto/zcrypt_msgtype6.c [new file with mode: 0644]
drivers/s390/crypto/zcrypt_msgtype6.h [new file with mode: 0644]
drivers/s390/crypto/zcrypt_pcixcc.c
drivers/s390/crypto/zcrypt_pcixcc.h

index af3c7f16ea88a2e35b4a295666de4c7a8497ee02..eca0bfaae08a8ef8561426a76e9c8ab8159a5778 100644 (file)
@@ -5,3 +5,4 @@
 ap-objs := ap_bus.o
 obj-$(CONFIG_ZCRYPT) += ap.o zcrypt_api.o zcrypt_pcicc.o zcrypt_pcixcc.o
 obj-$(CONFIG_ZCRYPT) += zcrypt_pcica.o zcrypt_cex2a.o
+obj-$(CONFIG_ZCRYPT) += zcrypt_msgtype6.o zcrypt_msgtype50.o
index 2f94132246a1b1f766feecb8bc3c6986d56e9564..f1f026e0b1890bd3198797ca21124ae559a719c9 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  zcrypt 2.1.0
  *
- *  Copyright IBM Corp. 2001, 2006
+ *  Copyright IBM Corp. 2001, 2012
  *  Author(s): Robert Burroughs
  *            Eric Rossman (edrossma@us.ibm.com)
  *            Cornelia Huck <cornelia.huck@de.ibm.com>
@@ -9,6 +9,7 @@
  *  Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
  *  Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
  *                               Ralph Wuerthner <rwuerthn@de.ibm.com>
+ *  MSGTYPE restruct:            Holger Dengler <hd@linux.vnet.ibm.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -44,8 +45,8 @@
  * Module description.
  */
 MODULE_AUTHOR("IBM Corporation");
-MODULE_DESCRIPTION("Cryptographic Coprocessor interface, "
-                  "Copyright IBM Corp. 2001, 2006");
+MODULE_DESCRIPTION("Cryptographic Coprocessor interface, " \
+                  "Copyright IBM Corp. 2001, 2012");
 MODULE_LICENSE("GPL");
 
 static DEFINE_SPINLOCK(zcrypt_device_lock);
@@ -56,6 +57,9 @@ static atomic_t zcrypt_open_count = ATOMIC_INIT(0);
 static int zcrypt_rng_device_add(void);
 static void zcrypt_rng_device_remove(void);
 
+static DEFINE_SPINLOCK(zcrypt_ops_list_lock);
+static LIST_HEAD(zcrypt_ops_list);
+
 /*
  * Device attributes common for all crypto devices.
  */
@@ -215,6 +219,8 @@ int zcrypt_device_register(struct zcrypt_device *zdev)
 {
        int rc;
 
+       if (!zdev->ops)
+               return -ENODEV;
        rc = sysfs_create_group(&zdev->ap_dev->device.kobj,
                                &zcrypt_device_attr_group);
        if (rc)
@@ -269,6 +275,67 @@ void zcrypt_device_unregister(struct zcrypt_device *zdev)
 }
 EXPORT_SYMBOL(zcrypt_device_unregister);
 
+void zcrypt_msgtype_register(struct zcrypt_ops *zops)
+{
+       if (zops->owner) {
+               spin_lock_bh(&zcrypt_ops_list_lock);
+               list_add_tail(&zops->list, &zcrypt_ops_list);
+               spin_unlock_bh(&zcrypt_ops_list_lock);
+       }
+}
+EXPORT_SYMBOL(zcrypt_msgtype_register);
+
+void zcrypt_msgtype_unregister(struct zcrypt_ops *zops)
+{
+       spin_lock_bh(&zcrypt_ops_list_lock);
+       list_del_init(&zops->list);
+       spin_unlock_bh(&zcrypt_ops_list_lock);
+}
+EXPORT_SYMBOL(zcrypt_msgtype_unregister);
+
+static inline
+struct zcrypt_ops *__ops_lookup(unsigned char *name, int variant)
+{
+       struct zcrypt_ops *zops;
+       int found = 0;
+
+       spin_lock_bh(&zcrypt_ops_list_lock);
+       list_for_each_entry(zops, &zcrypt_ops_list, list) {
+               if ((zops->variant == variant) &&
+                   (!strncmp(zops->owner->name, name, MODULE_NAME_LEN))) {
+                       found = 1;
+                       break;
+               }
+       }
+       spin_unlock_bh(&zcrypt_ops_list_lock);
+
+       if (!found)
+               return NULL;
+       return zops;
+}
+
+struct zcrypt_ops *zcrypt_msgtype_request(unsigned char *name, int variant)
+{
+       struct zcrypt_ops *zops = NULL;
+
+       zops = __ops_lookup(name, variant);
+       if (!zops) {
+               request_module(name);
+               zops = __ops_lookup(name, variant);
+       }
+       if ((!zops) || (!try_module_get(zops->owner)))
+               return NULL;
+       return zops;
+}
+EXPORT_SYMBOL(zcrypt_msgtype_request);
+
+void zcrypt_msgtype_release(struct zcrypt_ops *zops)
+{
+       if (zops)
+               module_put(zops->owner);
+}
+EXPORT_SYMBOL(zcrypt_msgtype_release);
+
 /**
  * zcrypt_read (): Not supported beyond zcrypt 1.3.1.
  *
index 7a32c4bc8ef914c9d175c4dd5823b6a07495eb52..02b2d35de1a7dd3c3aa3a4ae1aabd739b737a2c0 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  zcrypt 2.1.0
  *
- *  Copyright IBM Corp. 2001, 2006
+ *  Copyright IBM Corp. 2001, 2012
  *  Author(s): Robert Burroughs
  *            Eric Rossman (edrossma@us.ibm.com)
  *            Cornelia Huck <cornelia.huck@de.ibm.com>
@@ -9,6 +9,7 @@
  *  Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
  *  Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
  *                               Ralph Wuerthner <rwuerthn@de.ibm.com>
+ *  MSGTYPE restruct:            Holger Dengler <hd@linux.vnet.ibm.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -87,6 +88,9 @@ struct zcrypt_ops {
                                struct ica_rsa_modexpo_crt *);
        long (*send_cprb)(struct zcrypt_device *, struct ica_xcRB *);
        long (*rng)(struct zcrypt_device *, char *);
+       struct list_head list;          /* zcrypt ops list. */
+       struct module *owner;
+       int variant;
 };
 
 struct zcrypt_device {
@@ -116,6 +120,10 @@ void zcrypt_device_get(struct zcrypt_device *);
 int zcrypt_device_put(struct zcrypt_device *);
 int zcrypt_device_register(struct zcrypt_device *);
 void zcrypt_device_unregister(struct zcrypt_device *);
+void zcrypt_msgtype_register(struct zcrypt_ops *);
+void zcrypt_msgtype_unregister(struct zcrypt_ops *);
+struct zcrypt_ops *zcrypt_msgtype_request(unsigned char *, int);
+void zcrypt_msgtype_release(struct zcrypt_ops *);
 int zcrypt_api_init(void);
 void zcrypt_api_exit(void);
 
index 8f5f70b43b296ffa3265d3cbb84b269f0972513c..1e849d6e1dfe11edee6ff72c79115ac8a38fccee 100644 (file)
@@ -1,13 +1,14 @@
 /*
  *  zcrypt 2.1.0
  *
- *  Copyright IBM Corp. 2001, 2006
+ *  Copyright IBM Corp. 2001, 2012
  *  Author(s): Robert Burroughs
  *            Eric Rossman (edrossma@us.ibm.com)
  *
  *  Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
  *  Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
  *                               Ralph Wuerthner <rwuerthn@de.ibm.com>
+ *  MSGTYPE restruct:            Holger Dengler <hd@linux.vnet.ibm.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -35,6 +36,7 @@
 #include "zcrypt_api.h"
 #include "zcrypt_error.h"
 #include "zcrypt_cex2a.h"
+#include "zcrypt_msgtype50.h"
 
 #define CEX2A_MIN_MOD_SIZE       1     /*    8 bits    */
 #define CEX2A_MAX_MOD_SIZE     256     /* 2048 bits    */
@@ -63,14 +65,12 @@ static struct ap_device_id zcrypt_cex2a_ids[] = {
 
 MODULE_DEVICE_TABLE(ap, zcrypt_cex2a_ids);
 MODULE_AUTHOR("IBM Corporation");
-MODULE_DESCRIPTION("CEX2A Cryptographic Coprocessor device driver, "
-                  "Copyright IBM Corp. 2001, 2006");
+MODULE_DESCRIPTION("CEX2A Cryptographic Coprocessor device driver, " \
+                  "Copyright IBM Corp. 2001, 2012");
 MODULE_LICENSE("GPL");
 
 static int zcrypt_cex2a_probe(struct ap_device *ap_dev);
 static void zcrypt_cex2a_remove(struct ap_device *ap_dev);
-static void zcrypt_cex2a_receive(struct ap_device *, struct ap_message *,
-                                struct ap_message *);
 
 static struct ap_driver zcrypt_cex2a_driver = {
        .probe = zcrypt_cex2a_probe,
@@ -79,344 +79,6 @@ static struct ap_driver zcrypt_cex2a_driver = {
        .request_timeout = CEX2A_CLEANUP_TIME,
 };
 
-/**
- * Convert a ICAMEX message to a type50 MEX message.
- *
- * @zdev: crypto device pointer
- * @zreq: crypto request pointer
- * @mex: pointer to user input data
- *
- * Returns 0 on success or -EFAULT.
- */
-static int ICAMEX_msg_to_type50MEX_msg(struct zcrypt_device *zdev,
-                                      struct ap_message *ap_msg,
-                                      struct ica_rsa_modexpo *mex)
-{
-       unsigned char *mod, *exp, *inp;
-       int mod_len;
-
-       mod_len = mex->inputdatalength;
-
-       if (mod_len <= 128) {
-               struct type50_meb1_msg *meb1 = ap_msg->message;
-               memset(meb1, 0, sizeof(*meb1));
-               ap_msg->length = sizeof(*meb1);
-               meb1->header.msg_type_code = TYPE50_TYPE_CODE;
-               meb1->header.msg_len = sizeof(*meb1);
-               meb1->keyblock_type = TYPE50_MEB1_FMT;
-               mod = meb1->modulus + sizeof(meb1->modulus) - mod_len;
-               exp = meb1->exponent + sizeof(meb1->exponent) - mod_len;
-               inp = meb1->message + sizeof(meb1->message) - mod_len;
-       } else if (mod_len <= 256) {
-               struct type50_meb2_msg *meb2 = ap_msg->message;
-               memset(meb2, 0, sizeof(*meb2));
-               ap_msg->length = sizeof(*meb2);
-               meb2->header.msg_type_code = TYPE50_TYPE_CODE;
-               meb2->header.msg_len = sizeof(*meb2);
-               meb2->keyblock_type = TYPE50_MEB2_FMT;
-               mod = meb2->modulus + sizeof(meb2->modulus) - mod_len;
-               exp = meb2->exponent + sizeof(meb2->exponent) - mod_len;
-               inp = meb2->message + sizeof(meb2->message) - mod_len;
-       } else {
-               /* mod_len > 256 = 4096 bit RSA Key */
-               struct type50_meb3_msg *meb3 = ap_msg->message;
-               memset(meb3, 0, sizeof(*meb3));
-               ap_msg->length = sizeof(*meb3);
-               meb3->header.msg_type_code = TYPE50_TYPE_CODE;
-               meb3->header.msg_len = sizeof(*meb3);
-               meb3->keyblock_type = TYPE50_MEB3_FMT;
-               mod = meb3->modulus + sizeof(meb3->modulus) - mod_len;
-               exp = meb3->exponent + sizeof(meb3->exponent) - mod_len;
-               inp = meb3->message + sizeof(meb3->message) - mod_len;
-       }
-
-       if (copy_from_user(mod, mex->n_modulus, mod_len) ||
-           copy_from_user(exp, mex->b_key, mod_len) ||
-           copy_from_user(inp, mex->inputdata, mod_len))
-               return -EFAULT;
-       return 0;
-}
-
-/**
- * Convert a ICACRT message to a type50 CRT message.
- *
- * @zdev: crypto device pointer
- * @zreq: crypto request pointer
- * @crt: pointer to user input data
- *
- * Returns 0 on success or -EFAULT.
- */
-static int ICACRT_msg_to_type50CRT_msg(struct zcrypt_device *zdev,
-                                      struct ap_message *ap_msg,
-                                      struct ica_rsa_modexpo_crt *crt)
-{
-       int mod_len, short_len, long_len, long_offset, limit;
-       unsigned char *p, *q, *dp, *dq, *u, *inp;
-
-       mod_len = crt->inputdatalength;
-       short_len = mod_len / 2;
-       long_len = mod_len / 2 + 8;
-
-       /*
-        * CEX2A cannot handle p, dp, or U > 128 bytes.
-        * If we have one of these, we need to do extra checking.
-        * For CEX3A the limit is 256 bytes.
-        */
-       if (zdev->max_mod_size == CEX3A_MAX_MOD_SIZE)
-               limit = 256;
-       else
-               limit = 128;
-
-       if (long_len > limit) {
-               /*
-                * zcrypt_rsa_crt already checked for the leading
-                * zeroes of np_prime, bp_key and u_mult_inc.
-                */
-               long_offset = long_len - limit;
-               long_len = limit;
-       } else
-               long_offset = 0;
-
-       /*
-        * Instead of doing extra work for p, dp, U > 64 bytes, we'll just use
-        * the larger message structure.
-        */
-       if (long_len <= 64) {
-               struct type50_crb1_msg *crb1 = ap_msg->message;
-               memset(crb1, 0, sizeof(*crb1));
-               ap_msg->length = sizeof(*crb1);
-               crb1->header.msg_type_code = TYPE50_TYPE_CODE;
-               crb1->header.msg_len = sizeof(*crb1);
-               crb1->keyblock_type = TYPE50_CRB1_FMT;
-               p = crb1->p + sizeof(crb1->p) - long_len;
-               q = crb1->q + sizeof(crb1->q) - short_len;
-               dp = crb1->dp + sizeof(crb1->dp) - long_len;
-               dq = crb1->dq + sizeof(crb1->dq) - short_len;
-               u = crb1->u + sizeof(crb1->u) - long_len;
-               inp = crb1->message + sizeof(crb1->message) - mod_len;
-       } else if (long_len <= 128) {
-               struct type50_crb2_msg *crb2 = ap_msg->message;
-               memset(crb2, 0, sizeof(*crb2));
-               ap_msg->length = sizeof(*crb2);
-               crb2->header.msg_type_code = TYPE50_TYPE_CODE;
-               crb2->header.msg_len = sizeof(*crb2);
-               crb2->keyblock_type = TYPE50_CRB2_FMT;
-               p = crb2->p + sizeof(crb2->p) - long_len;
-               q = crb2->q + sizeof(crb2->q) - short_len;
-               dp = crb2->dp + sizeof(crb2->dp) - long_len;
-               dq = crb2->dq + sizeof(crb2->dq) - short_len;
-               u = crb2->u + sizeof(crb2->u) - long_len;
-               inp = crb2->message + sizeof(crb2->message) - mod_len;
-       } else {
-               /* long_len >= 256 */
-               struct type50_crb3_msg *crb3 = ap_msg->message;
-               memset(crb3, 0, sizeof(*crb3));
-               ap_msg->length = sizeof(*crb3);
-               crb3->header.msg_type_code = TYPE50_TYPE_CODE;
-               crb3->header.msg_len = sizeof(*crb3);
-               crb3->keyblock_type = TYPE50_CRB3_FMT;
-               p = crb3->p + sizeof(crb3->p) - long_len;
-               q = crb3->q + sizeof(crb3->q) - short_len;
-               dp = crb3->dp + sizeof(crb3->dp) - long_len;
-               dq = crb3->dq + sizeof(crb3->dq) - short_len;
-               u = crb3->u + sizeof(crb3->u) - long_len;
-               inp = crb3->message + sizeof(crb3->message) - mod_len;
-       }
-
-       if (copy_from_user(p, crt->np_prime + long_offset, long_len) ||
-           copy_from_user(q, crt->nq_prime, short_len) ||
-           copy_from_user(dp, crt->bp_key + long_offset, long_len) ||
-           copy_from_user(dq, crt->bq_key, short_len) ||
-           copy_from_user(u, crt->u_mult_inv + long_offset, long_len) ||
-           copy_from_user(inp, crt->inputdata, mod_len))
-               return -EFAULT;
-
-       return 0;
-}
-
-/**
- * Copy results from a type 80 reply message back to user space.
- *
- * @zdev: crypto device pointer
- * @reply: reply AP message.
- * @data: pointer to user output data
- * @length: size of user output data
- *
- * Returns 0 on success or -EFAULT.
- */
-static int convert_type80(struct zcrypt_device *zdev,
-                         struct ap_message *reply,
-                         char __user *outputdata,
-                         unsigned int outputdatalength)
-{
-       struct type80_hdr *t80h = reply->message;
-       unsigned char *data;
-
-       if (t80h->len < sizeof(*t80h) + outputdatalength) {
-               /* The result is too short, the CEX2A card may not do that.. */
-               zdev->online = 0;
-               return -EAGAIN; /* repeat the request on a different device. */
-       }
-       if (zdev->user_space_type == ZCRYPT_CEX2A)
-               BUG_ON(t80h->len > CEX2A_MAX_RESPONSE_SIZE);
-       else
-               BUG_ON(t80h->len > CEX3A_MAX_RESPONSE_SIZE);
-       data = reply->message + t80h->len - outputdatalength;
-       if (copy_to_user(outputdata, data, outputdatalength))
-               return -EFAULT;
-       return 0;
-}
-
-static int convert_response(struct zcrypt_device *zdev,
-                           struct ap_message *reply,
-                           char __user *outputdata,
-                           unsigned int outputdatalength)
-{
-       /* Response type byte is the second byte in the response. */
-       switch (((unsigned char *) reply->message)[1]) {
-       case TYPE82_RSP_CODE:
-       case TYPE88_RSP_CODE:
-               return convert_error(zdev, reply);
-       case TYPE80_RSP_CODE:
-               return convert_type80(zdev, reply,
-                                     outputdata, outputdatalength);
-       default: /* Unknown response type, this should NEVER EVER happen */
-               zdev->online = 0;
-               return -EAGAIN; /* repeat the request on a different device. */
-       }
-}
-
-/**
- * This function is called from the AP bus code after a crypto request
- * "msg" has finished with the reply message "reply".
- * It is called from tasklet context.
- * @ap_dev: pointer to the AP device
- * @msg: pointer to the AP message
- * @reply: pointer to the AP reply message
- */
-static void zcrypt_cex2a_receive(struct ap_device *ap_dev,
-                                struct ap_message *msg,
-                                struct ap_message *reply)
-{
-       static struct error_hdr error_reply = {
-               .type = TYPE82_RSP_CODE,
-               .reply_code = REP82_ERROR_MACHINE_FAILURE,
-       };
-       struct type80_hdr *t80h;
-       int length;
-
-       /* Copy the reply message to the request message buffer. */
-       if (IS_ERR(reply)) {
-               memcpy(msg->message, &error_reply, sizeof(error_reply));
-               goto out;
-       }
-       t80h = reply->message;
-       if (t80h->type == TYPE80_RSP_CODE) {
-               if (ap_dev->device_type == AP_DEVICE_TYPE_CEX2A)
-                       length = min(CEX2A_MAX_RESPONSE_SIZE, (int) t80h->len);
-               else
-                       length = min(CEX3A_MAX_RESPONSE_SIZE, (int) t80h->len);
-               memcpy(msg->message, reply->message, length);
-       } else
-               memcpy(msg->message, reply->message, sizeof error_reply);
-out:
-       complete((struct completion *) msg->private);
-}
-
-static atomic_t zcrypt_step = ATOMIC_INIT(0);
-
-/**
- * The request distributor calls this function if it picked the CEX2A
- * device to handle a modexpo request.
- * @zdev: pointer to zcrypt_device structure that identifies the
- *       CEX2A device to the request distributor
- * @mex: pointer to the modexpo request buffer
- */
-static long zcrypt_cex2a_modexpo(struct zcrypt_device *zdev,
-                                struct ica_rsa_modexpo *mex)
-{
-       struct ap_message ap_msg;
-       struct completion work;
-       int rc;
-
-       ap_init_message(&ap_msg);
-       if (zdev->user_space_type == ZCRYPT_CEX2A)
-               ap_msg.message = kmalloc(CEX2A_MAX_MESSAGE_SIZE, GFP_KERNEL);
-       else
-               ap_msg.message = kmalloc(CEX3A_MAX_MESSAGE_SIZE, GFP_KERNEL);
-       if (!ap_msg.message)
-               return -ENOMEM;
-       ap_msg.receive = zcrypt_cex2a_receive;
-       ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
-                               atomic_inc_return(&zcrypt_step);
-       ap_msg.private = &work;
-       rc = ICAMEX_msg_to_type50MEX_msg(zdev, &ap_msg, mex);
-       if (rc)
-               goto out_free;
-       init_completion(&work);
-       ap_queue_message(zdev->ap_dev, &ap_msg);
-       rc = wait_for_completion_interruptible(&work);
-       if (rc == 0)
-               rc = convert_response(zdev, &ap_msg, mex->outputdata,
-                                     mex->outputdatalength);
-       else
-               /* Signal pending. */
-               ap_cancel_message(zdev->ap_dev, &ap_msg);
-out_free:
-       kfree(ap_msg.message);
-       return rc;
-}
-
-/**
- * The request distributor calls this function if it picked the CEX2A
- * device to handle a modexpo_crt request.
- * @zdev: pointer to zcrypt_device structure that identifies the
- *       CEX2A device to the request distributor
- * @crt: pointer to the modexpoc_crt request buffer
- */
-static long zcrypt_cex2a_modexpo_crt(struct zcrypt_device *zdev,
-                                    struct ica_rsa_modexpo_crt *crt)
-{
-       struct ap_message ap_msg;
-       struct completion work;
-       int rc;
-
-       ap_init_message(&ap_msg);
-       if (zdev->user_space_type == ZCRYPT_CEX2A)
-               ap_msg.message = kmalloc(CEX2A_MAX_MESSAGE_SIZE, GFP_KERNEL);
-       else
-               ap_msg.message = kmalloc(CEX3A_MAX_MESSAGE_SIZE, GFP_KERNEL);
-       if (!ap_msg.message)
-               return -ENOMEM;
-       ap_msg.receive = zcrypt_cex2a_receive;
-       ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
-                               atomic_inc_return(&zcrypt_step);
-       ap_msg.private = &work;
-       rc = ICACRT_msg_to_type50CRT_msg(zdev, &ap_msg, crt);
-       if (rc)
-               goto out_free;
-       init_completion(&work);
-       ap_queue_message(zdev->ap_dev, &ap_msg);
-       rc = wait_for_completion_interruptible(&work);
-       if (rc == 0)
-               rc = convert_response(zdev, &ap_msg, crt->outputdata,
-                                     crt->outputdatalength);
-       else
-               /* Signal pending. */
-               ap_cancel_message(zdev->ap_dev, &ap_msg);
-out_free:
-       kfree(ap_msg.message);
-       return rc;
-}
-
-/**
- * The crypto operations for a CEX2A card.
- */
-static struct zcrypt_ops zcrypt_cex2a_ops = {
-       .rsa_modexpo = zcrypt_cex2a_modexpo,
-       .rsa_modexpo_crt = zcrypt_cex2a_modexpo_crt,
-};
-
 /**
  * Probe function for CEX2A cards. It always accepts the AP device
  * since the bus_match already checked the hardware type.
@@ -458,16 +120,18 @@ static int zcrypt_cex2a_probe(struct ap_device *ap_dev)
                zdev->speed_rating = CEX3A_SPEED_RATING;
                break;
        }
-       if (zdev != NULL) {
-               zdev->ap_dev = ap_dev;
-               zdev->ops = &zcrypt_cex2a_ops;
-               zdev->online = 1;
-               ap_dev->reply = &zdev->reply;
-               ap_dev->private = zdev;
-               rc = zcrypt_device_register(zdev);
-       }
+       if (!zdev)
+               return -ENODEV;
+       zdev->ops = zcrypt_msgtype_request(MSGTYPE50_NAME,
+                                          MSGTYPE50_VARIANT_DEFAULT);
+       zdev->ap_dev = ap_dev;
+       zdev->online = 1;
+       ap_dev->reply = &zdev->reply;
+       ap_dev->private = zdev;
+       rc = zcrypt_device_register(zdev);
        if (rc) {
                ap_dev->private = NULL;
+               zcrypt_msgtype_release(zdev->ops);
                zcrypt_device_free(zdev);
        }
        return rc;
@@ -480,8 +144,10 @@ static int zcrypt_cex2a_probe(struct ap_device *ap_dev)
 static void zcrypt_cex2a_remove(struct ap_device *ap_dev)
 {
        struct zcrypt_device *zdev = ap_dev->private;
+       struct zcrypt_ops *zops = zdev->ops;
 
        zcrypt_device_unregister(zdev);
+       zcrypt_msgtype_release(zops);
 }
 
 int __init zcrypt_cex2a_init(void)
diff --git a/drivers/s390/crypto/zcrypt_msgtype50.c b/drivers/s390/crypto/zcrypt_msgtype50.c
new file mode 100644 (file)
index 0000000..035b6dc
--- /dev/null
@@ -0,0 +1,531 @@
+/*
+ *  zcrypt 2.1.0
+ *
+ *  Copyright IBM Corp. 2001, 2012
+ *  Author(s): Robert Burroughs
+ *            Eric Rossman (edrossma@us.ibm.com)
+ *
+ *  Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
+ *  Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
+ *                               Ralph Wuerthner <rwuerthn@de.ibm.com>
+ *  MSGTYPE restruct:            Holger Dengler <hd@linux.vnet.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/atomic.h>
+#include <linux/uaccess.h>
+
+#include "ap_bus.h"
+#include "zcrypt_api.h"
+#include "zcrypt_error.h"
+#include "zcrypt_msgtype50.h"
+
+#define CEX3A_MAX_MOD_SIZE     512     /* 4096 bits    */
+
+#define CEX2A_MAX_RESPONSE_SIZE 0x110  /* max outputdatalength + type80_hdr */
+
+#define CEX3A_MAX_RESPONSE_SIZE        0x210   /* 512 bit modulus
+                                        * (max outputdatalength) +
+                                        * type80_hdr*/
+
+MODULE_AUTHOR("IBM Corporation");
+MODULE_DESCRIPTION("Cryptographic Accelerator (message type 50), " \
+                  "Copyright IBM Corp. 2001, 2012");
+MODULE_LICENSE("GPL");
+
+static void zcrypt_cex2a_receive(struct ap_device *, struct ap_message *,
+                                struct ap_message *);
+
+/**
+ * The type 50 message family is associated with a CEX2A card.
+ *
+ * The four members of the family are described below.
+ *
+ * Note that all unsigned char arrays are right-justified and left-padded
+ * with zeroes.
+ *
+ * Note that all reserved fields must be zeroes.
+ */
+struct type50_hdr {
+       unsigned char   reserved1;
+       unsigned char   msg_type_code;  /* 0x50 */
+       unsigned short  msg_len;
+       unsigned char   reserved2;
+       unsigned char   ignored;
+       unsigned short  reserved3;
+} __packed;
+
+#define TYPE50_TYPE_CODE       0x50
+
+#define TYPE50_MEB1_FMT                0x0001
+#define TYPE50_MEB2_FMT                0x0002
+#define TYPE50_MEB3_FMT                0x0003
+#define TYPE50_CRB1_FMT                0x0011
+#define TYPE50_CRB2_FMT                0x0012
+#define TYPE50_CRB3_FMT                0x0013
+
+/* Mod-Exp, with a small modulus */
+struct type50_meb1_msg {
+       struct type50_hdr header;
+       unsigned short  keyblock_type;  /* 0x0001 */
+       unsigned char   reserved[6];
+       unsigned char   exponent[128];
+       unsigned char   modulus[128];
+       unsigned char   message[128];
+} __packed;
+
+/* Mod-Exp, with a large modulus */
+struct type50_meb2_msg {
+       struct type50_hdr header;
+       unsigned short  keyblock_type;  /* 0x0002 */
+       unsigned char   reserved[6];
+       unsigned char   exponent[256];
+       unsigned char   modulus[256];
+       unsigned char   message[256];
+} __packed;
+
+/* Mod-Exp, with a larger modulus */
+struct type50_meb3_msg {
+       struct type50_hdr header;
+       unsigned short  keyblock_type;  /* 0x0003 */
+       unsigned char   reserved[6];
+       unsigned char   exponent[512];
+       unsigned char   modulus[512];
+       unsigned char   message[512];
+} __packed;
+
+/* CRT, with a small modulus */
+struct type50_crb1_msg {
+       struct type50_hdr header;
+       unsigned short  keyblock_type;  /* 0x0011 */
+       unsigned char   reserved[6];
+       unsigned char   p[64];
+       unsigned char   q[64];
+       unsigned char   dp[64];
+       unsigned char   dq[64];
+       unsigned char   u[64];
+       unsigned char   message[128];
+} __packed;
+
+/* CRT, with a large modulus */
+struct type50_crb2_msg {
+       struct type50_hdr header;
+       unsigned short  keyblock_type;  /* 0x0012 */
+       unsigned char   reserved[6];
+       unsigned char   p[128];
+       unsigned char   q[128];
+       unsigned char   dp[128];
+       unsigned char   dq[128];
+       unsigned char   u[128];
+       unsigned char   message[256];
+} __packed;
+
+/* CRT, with a larger modulus */
+struct type50_crb3_msg {
+       struct type50_hdr header;
+       unsigned short  keyblock_type;  /* 0x0013 */
+       unsigned char   reserved[6];
+       unsigned char   p[256];
+       unsigned char   q[256];
+       unsigned char   dp[256];
+       unsigned char   dq[256];
+       unsigned char   u[256];
+       unsigned char   message[512];
+} __packed;
+
+/**
+ * The type 80 response family is associated with a CEX2A card.
+ *
+ * Note that all unsigned char arrays are right-justified and left-padded
+ * with zeroes.
+ *
+ * Note that all reserved fields must be zeroes.
+ */
+
+#define TYPE80_RSP_CODE 0x80
+
+struct type80_hdr {
+       unsigned char   reserved1;
+       unsigned char   type;           /* 0x80 */
+       unsigned short  len;
+       unsigned char   code;           /* 0x00 */
+       unsigned char   reserved2[3];
+       unsigned char   reserved3[8];
+} __packed;
+
+/**
+ * Convert a ICAMEX message to a type50 MEX message.
+ *
+ * @zdev: crypto device pointer
+ * @zreq: crypto request pointer
+ * @mex: pointer to user input data
+ *
+ * Returns 0 on success or -EFAULT.
+ */
+static int ICAMEX_msg_to_type50MEX_msg(struct zcrypt_device *zdev,
+                                      struct ap_message *ap_msg,
+                                      struct ica_rsa_modexpo *mex)
+{
+       unsigned char *mod, *exp, *inp;
+       int mod_len;
+
+       mod_len = mex->inputdatalength;
+
+       if (mod_len <= 128) {
+               struct type50_meb1_msg *meb1 = ap_msg->message;
+               memset(meb1, 0, sizeof(*meb1));
+               ap_msg->length = sizeof(*meb1);
+               meb1->header.msg_type_code = TYPE50_TYPE_CODE;
+               meb1->header.msg_len = sizeof(*meb1);
+               meb1->keyblock_type = TYPE50_MEB1_FMT;
+               mod = meb1->modulus + sizeof(meb1->modulus) - mod_len;
+               exp = meb1->exponent + sizeof(meb1->exponent) - mod_len;
+               inp = meb1->message + sizeof(meb1->message) - mod_len;
+       } else if (mod_len <= 256) {
+               struct type50_meb2_msg *meb2 = ap_msg->message;
+               memset(meb2, 0, sizeof(*meb2));
+               ap_msg->length = sizeof(*meb2);
+               meb2->header.msg_type_code = TYPE50_TYPE_CODE;
+               meb2->header.msg_len = sizeof(*meb2);
+               meb2->keyblock_type = TYPE50_MEB2_FMT;
+               mod = meb2->modulus + sizeof(meb2->modulus) - mod_len;
+               exp = meb2->exponent + sizeof(meb2->exponent) - mod_len;
+               inp = meb2->message + sizeof(meb2->message) - mod_len;
+       } else {
+               /* mod_len > 256 = 4096 bit RSA Key */
+               struct type50_meb3_msg *meb3 = ap_msg->message;
+               memset(meb3, 0, sizeof(*meb3));
+               ap_msg->length = sizeof(*meb3);
+               meb3->header.msg_type_code = TYPE50_TYPE_CODE;
+               meb3->header.msg_len = sizeof(*meb3);
+               meb3->keyblock_type = TYPE50_MEB3_FMT;
+               mod = meb3->modulus + sizeof(meb3->modulus) - mod_len;
+               exp = meb3->exponent + sizeof(meb3->exponent) - mod_len;
+               inp = meb3->message + sizeof(meb3->message) - mod_len;
+       }
+
+       if (copy_from_user(mod, mex->n_modulus, mod_len) ||
+           copy_from_user(exp, mex->b_key, mod_len) ||
+           copy_from_user(inp, mex->inputdata, mod_len))
+               return -EFAULT;
+       return 0;
+}
+
+/**
+ * Convert a ICACRT message to a type50 CRT message.
+ *
+ * @zdev: crypto device pointer
+ * @zreq: crypto request pointer
+ * @crt: pointer to user input data
+ *
+ * Returns 0 on success or -EFAULT.
+ */
+static int ICACRT_msg_to_type50CRT_msg(struct zcrypt_device *zdev,
+                                      struct ap_message *ap_msg,
+                                      struct ica_rsa_modexpo_crt *crt)
+{
+       int mod_len, short_len, long_len, long_offset, limit;
+       unsigned char *p, *q, *dp, *dq, *u, *inp;
+
+       mod_len = crt->inputdatalength;
+       short_len = mod_len / 2;
+       long_len = mod_len / 2 + 8;
+
+       /*
+        * CEX2A cannot handle p, dp, or U > 128 bytes.
+        * If we have one of these, we need to do extra checking.
+        * For CEX3A the limit is 256 bytes.
+        */
+       if (zdev->max_mod_size == CEX3A_MAX_MOD_SIZE)
+               limit = 256;
+       else
+               limit = 128;
+
+       if (long_len > limit) {
+               /*
+                * zcrypt_rsa_crt already checked for the leading
+                * zeroes of np_prime, bp_key and u_mult_inc.
+                */
+               long_offset = long_len - limit;
+               long_len = limit;
+       } else
+               long_offset = 0;
+
+       /*
+        * Instead of doing extra work for p, dp, U > 64 bytes, we'll just use
+        * the larger message structure.
+        */
+       if (long_len <= 64) {
+               struct type50_crb1_msg *crb1 = ap_msg->message;
+               memset(crb1, 0, sizeof(*crb1));
+               ap_msg->length = sizeof(*crb1);
+               crb1->header.msg_type_code = TYPE50_TYPE_CODE;
+               crb1->header.msg_len = sizeof(*crb1);
+               crb1->keyblock_type = TYPE50_CRB1_FMT;
+               p = crb1->p + sizeof(crb1->p) - long_len;
+               q = crb1->q + sizeof(crb1->q) - short_len;
+               dp = crb1->dp + sizeof(crb1->dp) - long_len;
+               dq = crb1->dq + sizeof(crb1->dq) - short_len;
+               u = crb1->u + sizeof(crb1->u) - long_len;
+               inp = crb1->message + sizeof(crb1->message) - mod_len;
+       } else if (long_len <= 128) {
+               struct type50_crb2_msg *crb2 = ap_msg->message;
+               memset(crb2, 0, sizeof(*crb2));
+               ap_msg->length = sizeof(*crb2);
+               crb2->header.msg_type_code = TYPE50_TYPE_CODE;
+               crb2->header.msg_len = sizeof(*crb2);
+               crb2->keyblock_type = TYPE50_CRB2_FMT;
+               p = crb2->p + sizeof(crb2->p) - long_len;
+               q = crb2->q + sizeof(crb2->q) - short_len;
+               dp = crb2->dp + sizeof(crb2->dp) - long_len;
+               dq = crb2->dq + sizeof(crb2->dq) - short_len;
+               u = crb2->u + sizeof(crb2->u) - long_len;
+               inp = crb2->message + sizeof(crb2->message) - mod_len;
+       } else {
+               /* long_len >= 256 */
+               struct type50_crb3_msg *crb3 = ap_msg->message;
+               memset(crb3, 0, sizeof(*crb3));
+               ap_msg->length = sizeof(*crb3);
+               crb3->header.msg_type_code = TYPE50_TYPE_CODE;
+               crb3->header.msg_len = sizeof(*crb3);
+               crb3->keyblock_type = TYPE50_CRB3_FMT;
+               p = crb3->p + sizeof(crb3->p) - long_len;
+               q = crb3->q + sizeof(crb3->q) - short_len;
+               dp = crb3->dp + sizeof(crb3->dp) - long_len;
+               dq = crb3->dq + sizeof(crb3->dq) - short_len;
+               u = crb3->u + sizeof(crb3->u) - long_len;
+               inp = crb3->message + sizeof(crb3->message) - mod_len;
+       }
+
+       if (copy_from_user(p, crt->np_prime + long_offset, long_len) ||
+           copy_from_user(q, crt->nq_prime, short_len) ||
+           copy_from_user(dp, crt->bp_key + long_offset, long_len) ||
+           copy_from_user(dq, crt->bq_key, short_len) ||
+           copy_from_user(u, crt->u_mult_inv + long_offset, long_len) ||
+           copy_from_user(inp, crt->inputdata, mod_len))
+               return -EFAULT;
+
+       return 0;
+}
+
+/**
+ * Copy results from a type 80 reply message back to user space.
+ *
+ * @zdev: crypto device pointer
+ * @reply: reply AP message.
+ * @data: pointer to user output data
+ * @length: size of user output data
+ *
+ * Returns 0 on success or -EFAULT.
+ */
+static int convert_type80(struct zcrypt_device *zdev,
+                         struct ap_message *reply,
+                         char __user *outputdata,
+                         unsigned int outputdatalength)
+{
+       struct type80_hdr *t80h = reply->message;
+       unsigned char *data;
+
+       if (t80h->len < sizeof(*t80h) + outputdatalength) {
+               /* The result is too short, the CEX2A card may not do that.. */
+               zdev->online = 0;
+               return -EAGAIN; /* repeat the request on a different device. */
+       }
+       if (zdev->user_space_type == ZCRYPT_CEX2A)
+               BUG_ON(t80h->len > CEX2A_MAX_RESPONSE_SIZE);
+       else
+               BUG_ON(t80h->len > CEX3A_MAX_RESPONSE_SIZE);
+       data = reply->message + t80h->len - outputdatalength;
+       if (copy_to_user(outputdata, data, outputdatalength))
+               return -EFAULT;
+       return 0;
+}
+
+static int convert_response(struct zcrypt_device *zdev,
+                           struct ap_message *reply,
+                           char __user *outputdata,
+                           unsigned int outputdatalength)
+{
+       /* Response type byte is the second byte in the response. */
+       switch (((unsigned char *) reply->message)[1]) {
+       case TYPE82_RSP_CODE:
+       case TYPE88_RSP_CODE:
+               return convert_error(zdev, reply);
+       case TYPE80_RSP_CODE:
+               return convert_type80(zdev, reply,
+                                     outputdata, outputdatalength);
+       default: /* Unknown response type, this should NEVER EVER happen */
+               zdev->online = 0;
+               return -EAGAIN; /* repeat the request on a different device. */
+       }
+}
+
+/**
+ * This function is called from the AP bus code after a crypto request
+ * "msg" has finished with the reply message "reply".
+ * It is called from tasklet context.
+ * @ap_dev: pointer to the AP device
+ * @msg: pointer to the AP message
+ * @reply: pointer to the AP reply message
+ */
+static void zcrypt_cex2a_receive(struct ap_device *ap_dev,
+                                struct ap_message *msg,
+                                struct ap_message *reply)
+{
+       static struct error_hdr error_reply = {
+               .type = TYPE82_RSP_CODE,
+               .reply_code = REP82_ERROR_MACHINE_FAILURE,
+       };
+       struct type80_hdr *t80h;
+       int length;
+
+       /* Copy the reply message to the request message buffer. */
+       if (IS_ERR(reply)) {
+               memcpy(msg->message, &error_reply, sizeof(error_reply));
+               goto out;
+       }
+       t80h = reply->message;
+       if (t80h->type == TYPE80_RSP_CODE) {
+               if (ap_dev->device_type == AP_DEVICE_TYPE_CEX2A)
+                       length = min_t(int,
+                                      CEX2A_MAX_RESPONSE_SIZE, t80h->len);
+               else
+                       length = min_t(int,
+                                      CEX3A_MAX_RESPONSE_SIZE, t80h->len);
+               memcpy(msg->message, reply->message, length);
+       } else
+               memcpy(msg->message, reply->message, sizeof(error_reply));
+out:
+       complete((struct completion *) msg->private);
+}
+
+static atomic_t zcrypt_step = ATOMIC_INIT(0);
+
+/**
+ * The request distributor calls this function if it picked the CEX2A
+ * device to handle a modexpo request.
+ * @zdev: pointer to zcrypt_device structure that identifies the
+ *       CEX2A device to the request distributor
+ * @mex: pointer to the modexpo request buffer
+ */
+static long zcrypt_cex2a_modexpo(struct zcrypt_device *zdev,
+                                struct ica_rsa_modexpo *mex)
+{
+       struct ap_message ap_msg;
+       struct completion work;
+       int rc;
+
+       ap_init_message(&ap_msg);
+       if (zdev->user_space_type == ZCRYPT_CEX2A)
+               ap_msg.message = kmalloc(MSGTYPE50_CRB2_MAX_MSG_SIZE,
+                                        GFP_KERNEL);
+       else
+               ap_msg.message = kmalloc(MSGTYPE50_CRB3_MAX_MSG_SIZE,
+                                        GFP_KERNEL);
+       if (!ap_msg.message)
+               return -ENOMEM;
+       ap_msg.receive = zcrypt_cex2a_receive;
+       ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
+                               atomic_inc_return(&zcrypt_step);
+       ap_msg.private = &work;
+       rc = ICAMEX_msg_to_type50MEX_msg(zdev, &ap_msg, mex);
+       if (rc)
+               goto out_free;
+       init_completion(&work);
+       ap_queue_message(zdev->ap_dev, &ap_msg);
+       rc = wait_for_completion_interruptible(&work);
+       if (rc == 0)
+               rc = convert_response(zdev, &ap_msg, mex->outputdata,
+                                     mex->outputdatalength);
+       else
+               /* Signal pending. */
+               ap_cancel_message(zdev->ap_dev, &ap_msg);
+out_free:
+       kfree(ap_msg.message);
+       return rc;
+}
+
+/**
+ * The request distributor calls this function if it picked the CEX2A
+ * device to handle a modexpo_crt request.
+ * @zdev: pointer to zcrypt_device structure that identifies the
+ *       CEX2A device to the request distributor
+ * @crt: pointer to the modexpoc_crt request buffer
+ */
+static long zcrypt_cex2a_modexpo_crt(struct zcrypt_device *zdev,
+                                    struct ica_rsa_modexpo_crt *crt)
+{
+       struct ap_message ap_msg;
+       struct completion work;
+       int rc;
+
+       ap_init_message(&ap_msg);
+       if (zdev->user_space_type == ZCRYPT_CEX2A)
+               ap_msg.message = kmalloc(MSGTYPE50_CRB2_MAX_MSG_SIZE,
+                                        GFP_KERNEL);
+       else
+               ap_msg.message = kmalloc(MSGTYPE50_CRB3_MAX_MSG_SIZE,
+                                        GFP_KERNEL);
+       if (!ap_msg.message)
+               return -ENOMEM;
+       ap_msg.receive = zcrypt_cex2a_receive;
+       ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
+                               atomic_inc_return(&zcrypt_step);
+       ap_msg.private = &work;
+       rc = ICACRT_msg_to_type50CRT_msg(zdev, &ap_msg, crt);
+       if (rc)
+               goto out_free;
+       init_completion(&work);
+       ap_queue_message(zdev->ap_dev, &ap_msg);
+       rc = wait_for_completion_interruptible(&work);
+       if (rc == 0)
+               rc = convert_response(zdev, &ap_msg, crt->outputdata,
+                                     crt->outputdatalength);
+       else
+               /* Signal pending. */
+               ap_cancel_message(zdev->ap_dev, &ap_msg);
+out_free:
+       kfree(ap_msg.message);
+       return rc;
+}
+
+/**
+ * The crypto operations for message type 50.
+ */
+static struct zcrypt_ops zcrypt_msgtype50_ops = {
+       .rsa_modexpo = zcrypt_cex2a_modexpo,
+       .rsa_modexpo_crt = zcrypt_cex2a_modexpo_crt,
+       .owner = THIS_MODULE,
+       .variant = MSGTYPE50_VARIANT_DEFAULT,
+};
+
+int __init zcrypt_msgtype50_init(void)
+{
+       zcrypt_msgtype_register(&zcrypt_msgtype50_ops);
+       return 0;
+}
+
+void __exit zcrypt_msgtype50_exit(void)
+{
+       zcrypt_msgtype_unregister(&zcrypt_msgtype50_ops);
+}
+
+module_init(zcrypt_msgtype50_init);
+module_exit(zcrypt_msgtype50_exit);
diff --git a/drivers/s390/crypto/zcrypt_msgtype50.h b/drivers/s390/crypto/zcrypt_msgtype50.h
new file mode 100644 (file)
index 0000000..e56dc72
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ *  zcrypt 2.1.0
+ *
+ *  Copyright IBM Corp. 2001, 2012
+ *  Author(s): Robert Burroughs
+ *            Eric Rossman (edrossma@us.ibm.com)
+ *
+ *  Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
+ *  Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
+ *  MSGTYPE restruct:            Holger Dengler <hd@linux.vnet.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _ZCRYPT_MSGTYPE50_H_
+#define _ZCRYPT_MSGTYPE50_H_
+
+#define MSGTYPE50_NAME                 "zcrypt_msgtype50"
+#define MSGTYPE50_VARIANT_DEFAULT      0
+
+#define MSGTYPE50_CRB2_MAX_MSG_SIZE    0x390 /*sizeof(struct type50_crb2_msg)*/
+#define MSGTYPE50_CRB3_MAX_MSG_SIZE    0x710 /*sizeof(struct type50_crb3_msg)*/
+
+int zcrypt_msgtype50_init(void);
+void zcrypt_msgtype50_exit(void);
+
+#endif /* _ZCRYPT_MSGTYPE50_H_ */
diff --git a/drivers/s390/crypto/zcrypt_msgtype6.c b/drivers/s390/crypto/zcrypt_msgtype6.c
new file mode 100644 (file)
index 0000000..7d97fa5
--- /dev/null
@@ -0,0 +1,856 @@
+/*
+ *  zcrypt 2.1.0
+ *
+ *  Copyright IBM Corp. 2001, 2012
+ *  Author(s): Robert Burroughs
+ *            Eric Rossman (edrossma@us.ibm.com)
+ *
+ *  Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
+ *  Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
+ *                               Ralph Wuerthner <rwuerthn@de.ibm.com>
+ *  MSGTYPE restruct:            Holger Dengler <hd@linux.vnet.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/atomic.h>
+#include <linux/uaccess.h>
+
+#include "ap_bus.h"
+#include "zcrypt_api.h"
+#include "zcrypt_error.h"
+#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 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
+
+MODULE_AUTHOR("IBM Corporation");
+MODULE_DESCRIPTION("Cryptographic Coprocessor (message type 6), " \
+                  "Copyright IBM Corp. 2001, 2012");
+MODULE_LICENSE("GPL");
+
+static void zcrypt_msgtype6_receive(struct ap_device *, struct ap_message *,
+                                struct ap_message *);
+
+/**
+ * CPRB
+ *       Note that all shorts, ints and longs are little-endian.
+ *       All pointer fields are 32-bits long, and mean nothing
+ *
+ *       A request CPRB is followed by a request_parameter_block.
+ *
+ *       The request (or reply) parameter block is organized thus:
+ *         function code
+ *         VUD block
+ *         key block
+ */
+struct CPRB {
+       unsigned short cprb_len;        /* CPRB length                   */
+       unsigned char cprb_ver_id;      /* CPRB version id.              */
+       unsigned char pad_000;          /* Alignment pad byte.           */
+       unsigned char srpi_rtcode[4];   /* SRPI return code LELONG       */
+       unsigned char srpi_verb;        /* SRPI verb type                */
+       unsigned char flags;            /* flags                         */
+       unsigned char func_id[2];       /* function id                   */
+       unsigned char checkpoint_flag;  /*                               */
+       unsigned char resv2;            /* reserved                      */
+       unsigned short req_parml;       /* request parameter buffer      */
+                                       /* length 16-bit little endian   */
+       unsigned char req_parmp[4];     /* request parameter buffer      *
+                                        * pointer (means nothing: the   *
+                                        * parameter buffer follows      *
+                                        * the CPRB).                    */
+       unsigned char req_datal[4];     /* request data buffer           */
+                                       /* length         ULELONG        */
+       unsigned char req_datap[4];     /* request data buffer           */
+                                       /* pointer                       */
+       unsigned short rpl_parml;       /* reply  parameter buffer       */
+                                       /* length 16-bit little endian   */
+       unsigned char pad_001[2];       /* Alignment pad bytes. ULESHORT */
+       unsigned char rpl_parmp[4];     /* reply parameter buffer        *
+                                        * pointer (means nothing: the   *
+                                        * parameter buffer follows      *
+                                        * the CPRB).                    */
+       unsigned char rpl_datal[4];     /* reply data buffer len ULELONG */
+       unsigned char rpl_datap[4];     /* reply data buffer             */
+                                       /* pointer                       */
+       unsigned short ccp_rscode;      /* server reason code   ULESHORT */
+       unsigned short ccp_rtcode;      /* server return code   ULESHORT */
+       unsigned char repd_parml[2];    /* replied parameter len ULESHORT*/
+       unsigned char mac_data_len[2];  /* Mac Data Length      ULESHORT */
+       unsigned char repd_datal[4];    /* replied data length  ULELONG  */
+       unsigned char req_pc[2];        /* PC identifier                 */
+       unsigned char res_origin[8];    /* resource origin               */
+       unsigned char mac_value[8];     /* Mac Value                     */
+       unsigned char logon_id[8];      /* Logon Identifier              */
+       unsigned char usage_domain[2];  /* cdx                           */
+       unsigned char resv3[18];        /* reserved for requestor        */
+       unsigned short svr_namel;       /* server name length  ULESHORT  */
+       unsigned char svr_name[8];      /* server name                   */
+} __packed;
+
+struct function_and_rules_block {
+       unsigned char function_code[2];
+       unsigned short ulen;
+       unsigned char only_rule[8];
+} __packed;
+
+/**
+ * The following is used to initialize the CPRBX passed to the PCIXCC/CEX2C
+ * 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
+ * device does *not* have 'Intel inside'.
+ *
+ * The CPRBX is followed immediately by the parm block.
+ * The parm block contains:
+ * - function code ('PD' 0x5044 or 'PK' 0x504B)
+ * - rule block (one of:)
+ *   + 0x000A 'PKCS-1.2' (MCL2 'PD')
+ *   + 0x000A 'ZERO-PAD' (MCL2 'PK')
+ *   + 0x000A 'ZERO-PAD' (MCL3 'PD' or CEX2C 'PD')
+ *   + 0x000A 'MRP     ' (MCL3 'PK' or CEX2C 'PK')
+ * - VUD block
+ */
+static struct CPRBX static_cprbx = {
+       .cprb_len       =  0x00DC,
+       .cprb_ver_id    =  0x02,
+       .func_id        = {0x54, 0x32},
+};
+
+/**
+ * Convert a ICAMEX message to a type6 MEX message.
+ *
+ * @zdev: crypto device pointer
+ * @ap_msg: pointer to AP message
+ * @mex: pointer to user input data
+ *
+ * Returns 0 on success or -EFAULT.
+ */
+static int ICAMEX_msg_to_type6MEX_msgX(struct zcrypt_device *zdev,
+                                      struct ap_message *ap_msg,
+                                      struct ica_rsa_modexpo *mex)
+{
+       static struct type6_hdr static_type6_hdrX = {
+               .type           =  0x06,
+               .offset1        =  0x00000058,
+               .agent_id       = {'C', 'A',},
+               .function_code  = {'P', 'K'},
+       };
+       static struct function_and_rules_block static_pke_fnr = {
+               .function_code  = {'P', 'K'},
+               .ulen           = 10,
+               .only_rule      = {'M', 'R', 'P', ' ', ' ', ' ', ' ', ' '}
+       };
+       static struct function_and_rules_block static_pke_fnr_MCL2 = {
+               .function_code  = {'P', 'K'},
+               .ulen           = 10,
+               .only_rule      = {'Z', 'E', 'R', 'O', '-', 'P', 'A', 'D'}
+       };
+       struct {
+               struct type6_hdr hdr;
+               struct CPRBX cprbx;
+               struct function_and_rules_block fr;
+               unsigned short length;
+               char text[0];
+       } __packed * msg = ap_msg->message;
+       int size;
+
+       /* VUD.ciphertext */
+       msg->length = mex->inputdatalength + 2;
+       if (copy_from_user(msg->text, mex->inputdata, mex->inputdatalength))
+               return -EFAULT;
+
+       /* Set up key which is located after the variable length text. */
+       size = zcrypt_type6_mex_key_en(mex, msg->text+mex->inputdatalength, 1);
+       if (size < 0)
+               return size;
+       size += sizeof(*msg) + mex->inputdatalength;
+
+       /* 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->cprbx = static_cprbx;
+       msg->cprbx.domain = AP_QID_QUEUE(zdev->ap_dev->qid);
+       msg->cprbx.rpl_msgbl = msg->hdr.FromCardLen1;
+
+       msg->fr = (zdev->user_space_type == ZCRYPT_PCIXCC_MCL2) ?
+               static_pke_fnr_MCL2 : static_pke_fnr;
+
+       msg->cprbx.req_parml = size - sizeof(msg->hdr) - sizeof(msg->cprbx);
+
+       ap_msg->length = size;
+       return 0;
+}
+
+/**
+ * Convert a ICACRT message to a type6 CRT message.
+ *
+ * @zdev: crypto device pointer
+ * @ap_msg: pointer to AP message
+ * @crt: pointer to user input data
+ *
+ * Returns 0 on success or -EFAULT.
+ */
+static int ICACRT_msg_to_type6CRT_msgX(struct zcrypt_device *zdev,
+                                      struct ap_message *ap_msg,
+                                      struct ica_rsa_modexpo_crt *crt)
+{
+       static struct type6_hdr static_type6_hdrX = {
+               .type           =  0x06,
+               .offset1        =  0x00000058,
+               .agent_id       = {'C', 'A',},
+               .function_code  = {'P', 'D'},
+       };
+       static struct function_and_rules_block static_pkd_fnr = {
+               .function_code  = {'P', 'D'},
+               .ulen           = 10,
+               .only_rule      = {'Z', 'E', 'R', 'O', '-', 'P', 'A', 'D'}
+       };
+
+       static struct function_and_rules_block static_pkd_fnr_MCL2 = {
+               .function_code  = {'P', 'D'},
+               .ulen           = 10,
+               .only_rule      = {'P', 'K', 'C', 'S', '-', '1', '.', '2'}
+       };
+       struct {
+               struct type6_hdr hdr;
+               struct CPRBX cprbx;
+               struct function_and_rules_block fr;
+               unsigned short length;
+               char text[0];
+       } __packed * msg = ap_msg->message;
+       int size;
+
+       /* VUD.ciphertext */
+       msg->length = crt->inputdatalength + 2;
+       if (copy_from_user(msg->text, crt->inputdata, crt->inputdatalength))
+               return -EFAULT;
+
+       /* Set up key which is located after the variable length text. */
+       size = zcrypt_type6_crt_key(crt, msg->text + crt->inputdatalength, 1);
+       if (size < 0)
+               return size;
+       size += sizeof(*msg) + crt->inputdatalength;    /* total size of msg */
+
+       /* 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->cprbx = static_cprbx;
+       msg->cprbx.domain = AP_QID_QUEUE(zdev->ap_dev->qid);
+       msg->cprbx.req_parml = msg->cprbx.rpl_msgbl =
+               size - sizeof(msg->hdr) - sizeof(msg->cprbx);
+
+       msg->fr = (zdev->user_space_type == ZCRYPT_PCIXCC_MCL2) ?
+               static_pkd_fnr_MCL2 : static_pkd_fnr;
+
+       ap_msg->length = size;
+       return 0;
+}
+
+/**
+ * Convert a XCRB message to a type6 CPRB message.
+ *
+ * @zdev: crypto device pointer
+ * @ap_msg: pointer to AP message
+ * @xcRB: pointer to user input data
+ *
+ * Returns 0 on success or -EFAULT, -EINVAL.
+ */
+struct type86_fmt2_msg {
+       struct type86_hdr hdr;
+       struct type86_fmt2_ext fmt2;
+} __packed;
+
+static int XCRB_msg_to_type6CPRB_msgX(struct zcrypt_device *zdev,
+                                      struct ap_message *ap_msg,
+                                      struct ica_xcRB *xcRB)
+{
+       static struct type6_hdr static_type6_hdrX = {
+               .type           =  0x06,
+               .offset1        =  0x00000058,
+       };
+       struct {
+               struct type6_hdr hdr;
+               struct CPRBX cprbx;
+       } __packed * msg = ap_msg->message;
+
+       int rcblen = CEIL4(xcRB->request_control_blk_length);
+       int replylen;
+       char *req_data = ap_msg->message + sizeof(struct type6_hdr) + rcblen;
+       char *function_code;
+
+       /* length checks */
+       ap_msg->length = sizeof(struct type6_hdr) +
+               CEIL4(xcRB->request_control_blk_length) +
+               xcRB->request_data_length;
+       if (ap_msg->length > MSGTYPE06_MAX_MSG_SIZE)
+               return -EINVAL;
+       replylen = sizeof(struct type86_fmt2_msg) +
+               CEIL4(xcRB->reply_control_blk_length) +
+               xcRB->reply_data_length;
+       if (replylen > MSGTYPE06_MAX_MSG_SIZE)
+               return -EINVAL;
+
+       /* prepare type6 header */
+       msg->hdr = static_type6_hdrX;
+       memcpy(msg->hdr.agent_id , &(xcRB->agent_ID), sizeof(xcRB->agent_ID));
+       msg->hdr.ToCardLen1 = xcRB->request_control_blk_length;
+       if (xcRB->request_data_length) {
+               msg->hdr.offset2 = msg->hdr.offset1 + rcblen;
+               msg->hdr.ToCardLen2 = xcRB->request_data_length;
+       }
+       msg->hdr.FromCardLen1 = xcRB->reply_control_blk_length;
+       msg->hdr.FromCardLen2 = xcRB->reply_data_length;
+
+       /* prepare CPRB */
+       if (copy_from_user(&(msg->cprbx), xcRB->request_control_blk_addr,
+                   xcRB->request_control_blk_length))
+               return -EFAULT;
+       if (msg->cprbx.cprb_len + sizeof(msg->hdr.function_code) >
+           xcRB->request_control_blk_length)
+               return -EINVAL;
+       function_code = ((unsigned char *)&msg->cprbx) + msg->cprbx.cprb_len;
+       memcpy(msg->hdr.function_code, function_code,
+              sizeof(msg->hdr.function_code));
+
+       if (memcmp(function_code, "US", 2) == 0)
+               ap_msg->special = 1;
+       else
+               ap_msg->special = 0;
+
+       /* copy data block */
+       if (xcRB->request_data_length &&
+           copy_from_user(req_data, xcRB->request_data_address,
+               xcRB->request_data_length))
+               return -EFAULT;
+       return 0;
+}
+
+/**
+ * Copy results from a type 86 ICA reply message back to user space.
+ *
+ * @zdev: crypto device pointer
+ * @reply: reply AP message.
+ * @data: pointer to user output data
+ * @length: size of user output data
+ *
+ * Returns 0 on success or -EINVAL, -EFAULT, -EAGAIN in case of an error.
+ */
+struct type86x_reply {
+       struct type86_hdr hdr;
+       struct type86_fmt2_ext fmt2;
+       struct CPRBX cprbx;
+       unsigned char pad[4];   /* 4 byte function code/rules block ? */
+       unsigned short length;
+       char text[0];
+} __packed;
+
+static int convert_type86_ica(struct zcrypt_device *zdev,
+                         struct ap_message *reply,
+                         char __user *outputdata,
+                         unsigned int outputdatalength)
+{
+       static unsigned char static_pad[] = {
+               0x00, 0x02,
+               0x1B, 0x7B, 0x5D, 0xB5, 0x75, 0x01, 0x3D, 0xFD,
+               0x8D, 0xD1, 0xC7, 0x03, 0x2D, 0x09, 0x23, 0x57,
+               0x89, 0x49, 0xB9, 0x3F, 0xBB, 0x99, 0x41, 0x5B,
+               0x75, 0x21, 0x7B, 0x9D, 0x3B, 0x6B, 0x51, 0x39,
+               0xBB, 0x0D, 0x35, 0xB9, 0x89, 0x0F, 0x93, 0xA5,
+               0x0B, 0x47, 0xF1, 0xD3, 0xBB, 0xCB, 0xF1, 0x9D,
+               0x23, 0x73, 0x71, 0xFF, 0xF3, 0xF5, 0x45, 0xFB,
+               0x61, 0x29, 0x23, 0xFD, 0xF1, 0x29, 0x3F, 0x7F,
+               0x17, 0xB7, 0x1B, 0xA9, 0x19, 0xBD, 0x57, 0xA9,
+               0xD7, 0x95, 0xA3, 0xCB, 0xED, 0x1D, 0xDB, 0x45,
+               0x7D, 0x11, 0xD1, 0x51, 0x1B, 0xED, 0x71, 0xE9,
+               0xB1, 0xD1, 0xAB, 0xAB, 0x21, 0x2B, 0x1B, 0x9F,
+               0x3B, 0x9F, 0xF7, 0xF7, 0xBD, 0x63, 0xEB, 0xAD,
+               0xDF, 0xB3, 0x6F, 0x5B, 0xDB, 0x8D, 0xA9, 0x5D,
+               0xE3, 0x7D, 0x77, 0x49, 0x47, 0xF5, 0xA7, 0xFD,
+               0xAB, 0x2F, 0x27, 0x35, 0x77, 0xD3, 0x49, 0xC9,
+               0x09, 0xEB, 0xB1, 0xF9, 0xBF, 0x4B, 0xCB, 0x2B,
+               0xEB, 0xEB, 0x05, 0xFF, 0x7D, 0xC7, 0x91, 0x8B,
+               0x09, 0x83, 0xB9, 0xB9, 0x69, 0x33, 0x39, 0x6B,
+               0x79, 0x75, 0x19, 0xBF, 0xBB, 0x07, 0x1D, 0xBD,
+               0x29, 0xBF, 0x39, 0x95, 0x93, 0x1D, 0x35, 0xC7,
+               0xC9, 0x4D, 0xE5, 0x97, 0x0B, 0x43, 0x9B, 0xF1,
+               0x16, 0x93, 0x03, 0x1F, 0xA5, 0xFB, 0xDB, 0xF3,
+               0x27, 0x4F, 0x27, 0x61, 0x05, 0x1F, 0xB9, 0x23,
+               0x2F, 0xC3, 0x81, 0xA9, 0x23, 0x71, 0x55, 0x55,
+               0xEB, 0xED, 0x41, 0xE5, 0xF3, 0x11, 0xF1, 0x43,
+               0x69, 0x03, 0xBD, 0x0B, 0x37, 0x0F, 0x51, 0x8F,
+               0x0B, 0xB5, 0x89, 0x5B, 0x67, 0xA9, 0xD9, 0x4F,
+               0x01, 0xF9, 0x21, 0x77, 0x37, 0x73, 0x79, 0xC5,
+               0x7F, 0x51, 0xC1, 0xCF, 0x97, 0xA1, 0x75, 0xAD,
+               0x35, 0x9D, 0xD3, 0xD3, 0xA7, 0x9D, 0x5D, 0x41,
+               0x6F, 0x65, 0x1B, 0xCF, 0xA9, 0x87, 0x91, 0x09
+       };
+       struct type86x_reply *msg = reply->message;
+       unsigned short service_rc, service_rs;
+       unsigned int reply_len, pad_len;
+       char *data;
+
+       service_rc = msg->cprbx.ccp_rtcode;
+       if (unlikely(service_rc != 0)) {
+               service_rs = msg->cprbx.ccp_rscode;
+               if (service_rc == 8 && service_rs == 66)
+                       return -EINVAL;
+               if (service_rc == 8 && service_rs == 65)
+                       return -EINVAL;
+               if (service_rc == 8 && service_rs == 770)
+                       return -EINVAL;
+               if (service_rc == 8 && service_rs == 783) {
+                       zdev->min_mod_size = PCIXCC_MIN_MOD_SIZE_OLD;
+                       return -EAGAIN;
+               }
+               if (service_rc == 12 && service_rs == 769)
+                       return -EINVAL;
+               if (service_rc == 8 && service_rs == 72)
+                       return -EINVAL;
+               zdev->online = 0;
+               return -EAGAIN; /* repeat the request on a different device. */
+       }
+       data = msg->text;
+       reply_len = msg->length - 2;
+       if (reply_len > outputdatalength)
+               return -EINVAL;
+       /*
+        * For all encipher requests, the length of the ciphertext (reply_len)
+        * will always equal the modulus length. For MEX decipher requests
+        * the output needs to get padded. Minimum pad size is 10.
+        *
+        * Currently, the cases where padding will be added is for:
+        * - PCIXCC_MCL2 using a CRT form token (since PKD didn't support
+        *   ZERO-PAD and CRT is only supported for PKD requests)
+        * - PCICC, always
+        */
+       pad_len = outputdatalength - reply_len;
+       if (pad_len > 0) {
+               if (pad_len < 10)
+                       return -EINVAL;
+               /* 'restore' padding left in the PCICC/PCIXCC card. */
+               if (copy_to_user(outputdata, static_pad, pad_len - 1))
+                       return -EFAULT;
+               if (put_user(0, outputdata + pad_len - 1))
+                       return -EFAULT;
+       }
+       /* Copy the crypto response to user space. */
+       if (copy_to_user(outputdata + pad_len, data, reply_len))
+               return -EFAULT;
+       return 0;
+}
+
+/**
+ * Copy results from a type 86 XCRB reply message back to user space.
+ *
+ * @zdev: crypto device pointer
+ * @reply: reply AP message.
+ * @xcRB: pointer to XCRB
+ *
+ * Returns 0 on success or -EINVAL, -EFAULT, -EAGAIN in case of an error.
+ */
+static int convert_type86_xcrb(struct zcrypt_device *zdev,
+                              struct ap_message *reply,
+                              struct ica_xcRB *xcRB)
+{
+       struct type86_fmt2_msg *msg = reply->message;
+       char *data = reply->message;
+
+       /* Copy CPRB to user */
+       if (copy_to_user(xcRB->reply_control_blk_addr,
+               data + msg->fmt2.offset1, msg->fmt2.count1))
+               return -EFAULT;
+       xcRB->reply_control_blk_length = msg->fmt2.count1;
+
+       /* Copy data buffer to user */
+       if (msg->fmt2.count2)
+               if (copy_to_user(xcRB->reply_data_addr,
+                       data + msg->fmt2.offset2, msg->fmt2.count2))
+                       return -EFAULT;
+       xcRB->reply_data_length = msg->fmt2.count2;
+       return 0;
+}
+
+static int convert_type86_rng(struct zcrypt_device *zdev,
+                         struct ap_message *reply,
+                         char *buffer)
+{
+       struct {
+               struct type86_hdr hdr;
+               struct type86_fmt2_ext fmt2;
+               struct CPRBX cprbx;
+       } __packed * msg = reply->message;
+       char *data = reply->message;
+
+       if (msg->cprbx.ccp_rtcode != 0 || msg->cprbx.ccp_rscode != 0)
+               return -EINVAL;
+       memcpy(buffer, data + msg->fmt2.offset2, msg->fmt2.count2);
+       return msg->fmt2.count2;
+}
+
+static int convert_response_ica(struct zcrypt_device *zdev,
+                           struct ap_message *reply,
+                           char __user *outputdata,
+                           unsigned int outputdatalength)
+{
+       struct type86x_reply *msg = reply->message;
+
+       /* Response type byte is the second byte in the response. */
+       switch (((unsigned char *) reply->message)[1]) {
+       case TYPE82_RSP_CODE:
+       case TYPE88_RSP_CODE:
+               return convert_error(zdev, reply);
+       case TYPE86_RSP_CODE:
+               if (msg->cprbx.ccp_rtcode &&
+                  (msg->cprbx.ccp_rscode == 0x14f) &&
+                  (outputdatalength > 256)) {
+                       if (zdev->max_exp_bit_length <= 17) {
+                               zdev->max_exp_bit_length = 17;
+                               return -EAGAIN;
+                       } else
+                               return -EINVAL;
+               }
+               if (msg->hdr.reply_code)
+                       return convert_error(zdev, reply);
+               if (msg->cprbx.cprb_ver_id == 0x02)
+                       return convert_type86_ica(zdev, reply,
+                                                 outputdata, outputdatalength);
+               /* Fall through, no break, incorrect cprb version is an unknown
+                * response */
+       default: /* Unknown response type, this should NEVER EVER happen */
+               zdev->online = 0;
+               return -EAGAIN; /* repeat the request on a different device. */
+       }
+}
+
+static int convert_response_xcrb(struct zcrypt_device *zdev,
+                           struct ap_message *reply,
+                           struct ica_xcRB *xcRB)
+{
+       struct type86x_reply *msg = reply->message;
+
+       /* Response type byte is the second byte in the response. */
+       switch (((unsigned char *) reply->message)[1]) {
+       case TYPE82_RSP_CODE:
+       case TYPE88_RSP_CODE:
+               xcRB->status = 0x0008044DL; /* HDD_InvalidParm */
+               return convert_error(zdev, reply);
+       case TYPE86_RSP_CODE:
+               if (msg->hdr.reply_code) {
+                       memcpy(&(xcRB->status), msg->fmt2.apfs, sizeof(u32));
+                       return convert_error(zdev, reply);
+               }
+               if (msg->cprbx.cprb_ver_id == 0x02)
+                       return convert_type86_xcrb(zdev, reply, xcRB);
+               /* Fall through, no break, incorrect cprb version is an unknown
+                * response */
+       default: /* Unknown response type, this should NEVER EVER happen */
+               xcRB->status = 0x0008044DL; /* HDD_InvalidParm */
+               zdev->online = 0;
+               return -EAGAIN; /* repeat the request on a different device. */
+       }
+}
+
+static int convert_response_rng(struct zcrypt_device *zdev,
+                                struct ap_message *reply,
+                                char *data)
+{
+       struct type86x_reply *msg = reply->message;
+
+       switch (msg->hdr.type) {
+       case TYPE82_RSP_CODE:
+       case TYPE88_RSP_CODE:
+               return -EINVAL;
+       case TYPE86_RSP_CODE:
+               if (msg->hdr.reply_code)
+                       return -EINVAL;
+               if (msg->cprbx.cprb_ver_id == 0x02)
+                       return convert_type86_rng(zdev, reply, data);
+               /* Fall through, no break, incorrect cprb version is an unknown
+                * response */
+       default: /* Unknown response type, this should NEVER EVER happen */
+               zdev->online = 0;
+               return -EAGAIN; /* repeat the request on a different device. */
+       }
+}
+
+/**
+ * This function is called from the AP bus code after a crypto request
+ * "msg" has finished with the reply message "reply".
+ * It is called from tasklet context.
+ * @ap_dev: pointer to the AP device
+ * @msg: pointer to the AP message
+ * @reply: pointer to the AP reply message
+ */
+static void zcrypt_msgtype6_receive(struct ap_device *ap_dev,
+                                 struct ap_message *msg,
+                                 struct ap_message *reply)
+{
+       static struct error_hdr error_reply = {
+               .type = TYPE82_RSP_CODE,
+               .reply_code = REP82_ERROR_MACHINE_FAILURE,
+       };
+       struct response_type *resp_type =
+               (struct response_type *) msg->private;
+       struct type86x_reply *t86r;
+       int length;
+
+       /* Copy the reply message to the request message buffer. */
+       if (IS_ERR(reply)) {
+               memcpy(msg->message, &error_reply, sizeof(error_reply));
+               goto out;
+       }
+       t86r = reply->message;
+       if (t86r->hdr.type == TYPE86_RSP_CODE &&
+                t86r->cprbx.cprb_ver_id == 0x02) {
+               switch (resp_type->type) {
+               case PCIXCC_RESPONSE_TYPE_ICA:
+                       length = sizeof(struct type86x_reply)
+                               + t86r->length - 2;
+                       length = min(PCIXCC_MAX_ICA_RESPONSE_SIZE, length);
+                       memcpy(msg->message, reply->message, length);
+                       break;
+               case PCIXCC_RESPONSE_TYPE_XCRB:
+                       length = t86r->fmt2.offset2 + t86r->fmt2.count2;
+                       length = min(MSGTYPE06_MAX_MSG_SIZE, length);
+                       memcpy(msg->message, reply->message, length);
+                       break;
+               default:
+                       memcpy(msg->message, &error_reply,
+                              sizeof(error_reply));
+               }
+       } else
+               memcpy(msg->message, reply->message, sizeof(error_reply));
+out:
+       complete(&(resp_type->work));
+}
+
+static atomic_t zcrypt_step = ATOMIC_INIT(0);
+
+/**
+ * The request distributor calls this function if it picked the PCIXCC/CEX2C
+ * device to handle a modexpo request.
+ * @zdev: pointer to zcrypt_device structure that identifies the
+ *       PCIXCC/CEX2C device to the request distributor
+ * @mex: pointer to the modexpo request buffer
+ */
+static long zcrypt_msgtype6_modexpo(struct zcrypt_device *zdev,
+                                 struct ica_rsa_modexpo *mex)
+{
+       struct ap_message ap_msg;
+       struct response_type resp_type = {
+               .type = PCIXCC_RESPONSE_TYPE_ICA,
+       };
+       int rc;
+
+       ap_init_message(&ap_msg);
+       ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL);
+       if (!ap_msg.message)
+               return -ENOMEM;
+       ap_msg.receive = zcrypt_msgtype6_receive;
+       ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
+                               atomic_inc_return(&zcrypt_step);
+       ap_msg.private = &resp_type;
+       rc = ICAMEX_msg_to_type6MEX_msgX(zdev, &ap_msg, mex);
+       if (rc)
+               goto out_free;
+       init_completion(&resp_type.work);
+       ap_queue_message(zdev->ap_dev, &ap_msg);
+       rc = wait_for_completion_interruptible(&resp_type.work);
+       if (rc == 0)
+               rc = convert_response_ica(zdev, &ap_msg, mex->outputdata,
+                                         mex->outputdatalength);
+       else
+               /* Signal pending. */
+               ap_cancel_message(zdev->ap_dev, &ap_msg);
+out_free:
+       free_page((unsigned long) ap_msg.message);
+       return rc;
+}
+
+/**
+ * The request distributor calls this function if it picked the PCIXCC/CEX2C
+ * device to handle a modexpo_crt request.
+ * @zdev: pointer to zcrypt_device structure that identifies the
+ *       PCIXCC/CEX2C device to the request distributor
+ * @crt: pointer to the modexpoc_crt request buffer
+ */
+static long zcrypt_msgtype6_modexpo_crt(struct zcrypt_device *zdev,
+                                     struct ica_rsa_modexpo_crt *crt)
+{
+       struct ap_message ap_msg;
+       struct response_type resp_type = {
+               .type = PCIXCC_RESPONSE_TYPE_ICA,
+       };
+       int rc;
+
+       ap_init_message(&ap_msg);
+       ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL);
+       if (!ap_msg.message)
+               return -ENOMEM;
+       ap_msg.receive = zcrypt_msgtype6_receive;
+       ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
+                               atomic_inc_return(&zcrypt_step);
+       ap_msg.private = &resp_type;
+       rc = ICACRT_msg_to_type6CRT_msgX(zdev, &ap_msg, crt);
+       if (rc)
+               goto out_free;
+       init_completion(&resp_type.work);
+       ap_queue_message(zdev->ap_dev, &ap_msg);
+       rc = wait_for_completion_interruptible(&resp_type.work);
+       if (rc == 0)
+               rc = convert_response_ica(zdev, &ap_msg, crt->outputdata,
+                                         crt->outputdatalength);
+       else
+               /* Signal pending. */
+               ap_cancel_message(zdev->ap_dev, &ap_msg);
+out_free:
+       free_page((unsigned long) ap_msg.message);
+       return rc;
+}
+
+/**
+ * The request distributor calls this function if it picked the PCIXCC/CEX2C
+ * device to handle a send_cprb request.
+ * @zdev: pointer to zcrypt_device structure that identifies the
+ *       PCIXCC/CEX2C device to the request distributor
+ * @xcRB: pointer to the send_cprb request buffer
+ */
+static long zcrypt_msgtype6_send_cprb(struct zcrypt_device *zdev,
+                                   struct ica_xcRB *xcRB)
+{
+       struct ap_message ap_msg;
+       struct response_type resp_type = {
+               .type = PCIXCC_RESPONSE_TYPE_XCRB,
+       };
+       int rc;
+
+       ap_init_message(&ap_msg);
+       ap_msg.message = kmalloc(MSGTYPE06_MAX_MSG_SIZE, GFP_KERNEL);
+       if (!ap_msg.message)
+               return -ENOMEM;
+       ap_msg.receive = zcrypt_msgtype6_receive;
+       ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
+                               atomic_inc_return(&zcrypt_step);
+       ap_msg.private = &resp_type;
+       rc = XCRB_msg_to_type6CPRB_msgX(zdev, &ap_msg, xcRB);
+       if (rc)
+               goto out_free;
+       init_completion(&resp_type.work);
+       ap_queue_message(zdev->ap_dev, &ap_msg);
+       rc = wait_for_completion_interruptible(&resp_type.work);
+       if (rc == 0)
+               rc = convert_response_xcrb(zdev, &ap_msg, xcRB);
+       else
+               /* Signal pending. */
+               ap_cancel_message(zdev->ap_dev, &ap_msg);
+out_free:
+       kzfree(ap_msg.message);
+       return rc;
+}
+
+/**
+ * The request distributor calls this function if it picked the PCIXCC/CEX2C
+ * device to generate random data.
+ * @zdev: pointer to zcrypt_device structure that identifies the
+ *       PCIXCC/CEX2C device to the request distributor
+ * @buffer: pointer to a memory page to return random data
+ */
+
+static long zcrypt_msgtype6_rng(struct zcrypt_device *zdev,
+                                   char *buffer)
+{
+       struct ap_message ap_msg;
+       struct response_type resp_type = {
+               .type = PCIXCC_RESPONSE_TYPE_XCRB,
+       };
+       int rc;
+
+       ap_init_message(&ap_msg);
+       ap_msg.message = kmalloc(MSGTYPE06_MAX_MSG_SIZE, GFP_KERNEL);
+       if (!ap_msg.message)
+               return -ENOMEM;
+       ap_msg.receive = zcrypt_msgtype6_receive;
+       ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
+                               atomic_inc_return(&zcrypt_step);
+       ap_msg.private = &resp_type;
+       rng_type6CPRB_msgX(zdev->ap_dev, &ap_msg, ZCRYPT_RNG_BUFFER_SIZE);
+       init_completion(&resp_type.work);
+       ap_queue_message(zdev->ap_dev, &ap_msg);
+       rc = wait_for_completion_interruptible(&resp_type.work);
+       if (rc == 0)
+               rc = convert_response_rng(zdev, &ap_msg, buffer);
+       else
+               /* Signal pending. */
+               ap_cancel_message(zdev->ap_dev, &ap_msg);
+       kfree(ap_msg.message);
+       return rc;
+}
+
+/**
+ * The crypto operations for a PCIXCC/CEX2C card.
+ */
+static struct zcrypt_ops zcrypt_msgtype6_norng_ops = {
+       .owner = THIS_MODULE,
+       .variant = MSGTYPE06_VARIANT_NORNG,
+       .rsa_modexpo = zcrypt_msgtype6_modexpo,
+       .rsa_modexpo_crt = zcrypt_msgtype6_modexpo_crt,
+       .send_cprb = zcrypt_msgtype6_send_cprb,
+};
+
+static struct zcrypt_ops zcrypt_msgtype6_ops = {
+       .owner = THIS_MODULE,
+       .variant = MSGTYPE06_VARIANT_DEFAULT,
+       .rsa_modexpo = zcrypt_msgtype6_modexpo,
+       .rsa_modexpo_crt = zcrypt_msgtype6_modexpo_crt,
+       .send_cprb = zcrypt_msgtype6_send_cprb,
+       .rng = zcrypt_msgtype6_rng,
+};
+
+int __init zcrypt_msgtype6_init(void)
+{
+       zcrypt_msgtype_register(&zcrypt_msgtype6_norng_ops);
+       zcrypt_msgtype_register(&zcrypt_msgtype6_ops);
+       return 0;
+}
+
+void __exit zcrypt_msgtype6_exit(void)
+{
+       zcrypt_msgtype_unregister(&zcrypt_msgtype6_norng_ops);
+       zcrypt_msgtype_unregister(&zcrypt_msgtype6_ops);
+}
+
+module_init(zcrypt_msgtype6_init);
+module_exit(zcrypt_msgtype6_exit);
diff --git a/drivers/s390/crypto/zcrypt_msgtype6.h b/drivers/s390/crypto/zcrypt_msgtype6.h
new file mode 100644 (file)
index 0000000..1e500d3
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ *  zcrypt 2.1.0
+ *
+ *  Copyright IBM Corp. 2001, 2012
+ *  Author(s): Robert Burroughs
+ *            Eric Rossman (edrossma@us.ibm.com)
+ *
+ *  Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
+ *  Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
+ *  MSGTYPE restruct:            Holger Dengler <hd@linux.vnet.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _ZCRYPT_MSGTYPE6_H_
+#define _ZCRYPT_MSGTYPE6_H_
+
+#include <asm/zcrypt.h>
+
+#define MSGTYPE06_NAME                 "zcrypt_msgtype6"
+#define MSGTYPE06_VARIANT_DEFAULT      0
+#define MSGTYPE06_VARIANT_NORNG                1
+
+#define MSGTYPE06_MAX_MSG_SIZE         (12*1024)
+
+/**
+ * The type 6 message family is associated with PCICC or PCIXCC cards.
+ *
+ * It contains a message header followed by a CPRB, both of which
+ * are described below.
+ *
+ * Note that all reserved fields must be zeroes.
+ */
+struct type6_hdr {
+       unsigned char reserved1;        /* 0x00                         */
+       unsigned char type;             /* 0x06                         */
+       unsigned char reserved2[2];     /* 0x0000                       */
+       unsigned char right[4];         /* 0x00000000                   */
+       unsigned char reserved3[2];     /* 0x0000                       */
+       unsigned char reserved4[2];     /* 0x0000                       */
+       unsigned char apfs[4];          /* 0x00000000                   */
+       unsigned int  offset1;          /* 0x00000058 (offset to CPRB)  */
+       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 rqid[2];          /* rqid.  internal to 603       */
+       unsigned char reserved5[2];     /* 0x0000                       */
+       unsigned char function_code[2]; /* for PKD, 0x5044 (ascii 'PD') */
+       unsigned char reserved6[2];     /* 0x0000                       */
+       unsigned int  ToCardLen1;       /* (request CPRB len + 3) & -4  */
+       unsigned int  ToCardLen2;       /* db len 0x00000000 for PKD    */
+       unsigned int  ToCardLen3;       /* 0x00000000                   */
+       unsigned int  ToCardLen4;       /* 0x00000000                   */
+       unsigned int  FromCardLen1;     /* response buffer length       */
+       unsigned int  FromCardLen2;     /* db len 0x00000000 for PKD    */
+       unsigned int  FromCardLen3;     /* 0x00000000                   */
+       unsigned int  FromCardLen4;     /* 0x00000000                   */
+} __packed;
+
+/**
+ * The type 86 message family is associated with PCICC and PCIXCC cards.
+ *
+ * It contains a message header followed by a CPRB.  The CPRB is
+ * the same as the request CPRB, which is described above.
+ *
+ * If format is 1, an error condition exists and no data beyond
+ * the 8-byte message header is of interest.
+ *
+ * The non-error message is shown below.
+ *
+ * Note that all reserved fields must be zeroes.
+ */
+struct type86_hdr {
+       unsigned char reserved1;        /* 0x00                         */
+       unsigned char type;             /* 0x86                         */
+       unsigned char format;           /* 0x01 (error) or 0x02 (ok)    */
+       unsigned char reserved2;        /* 0x00                         */
+       unsigned char reply_code;       /* reply code (see above)       */
+       unsigned char reserved3[3];     /* 0x000000                     */
+} __packed;
+
+#define TYPE86_RSP_CODE 0x86
+#define TYPE86_FMT2    0x02
+
+struct type86_fmt2_ext {
+       unsigned char     reserved[4];  /* 0x00000000                   */
+       unsigned char     apfs[4];      /* final status                 */
+       unsigned int      count1;       /* length of CPRB + parameters  */
+       unsigned int      offset1;      /* offset to CPRB               */
+       unsigned int      count2;       /* 0x00000000                   */
+       unsigned int      offset2;      /* db offset 0x00000000 for PKD */
+       unsigned int      count3;       /* 0x00000000                   */
+       unsigned int      offset3;      /* 0x00000000                   */
+       unsigned int      count4;       /* 0x00000000                   */
+       unsigned int      offset4;      /* 0x00000000                   */
+} __packed;
+
+/**
+ * Prepare a type6 CPRB message for random number generation
+ *
+ * @ap_dev: AP device pointer
+ * @ap_msg: pointer to AP message
+ */
+static inline void rng_type6CPRB_msgX(struct ap_device *ap_dev,
+                              struct ap_message *ap_msg,
+                              unsigned random_number_length)
+{
+       struct {
+               struct type6_hdr hdr;
+               struct CPRBX cprbx;
+               char function_code[2];
+               short int rule_length;
+               char rule[8];
+               short int verb_length;
+               short int key_length;
+       } __packed * msg = ap_msg->message;
+       static struct type6_hdr static_type6_hdrX = {
+               .type           = 0x06,
+               .offset1        = 0x00000058,
+               .agent_id       = {'C', 'A'},
+               .function_code  = {'R', 'L'},
+               .ToCardLen1     = sizeof(*msg) - sizeof(msg->hdr),
+               .FromCardLen1   = sizeof(*msg) - sizeof(msg->hdr),
+       };
+       static struct CPRBX local_cprbx = {
+               .cprb_len       = 0x00dc,
+               .cprb_ver_id    = 0x02,
+               .func_id        = {0x54, 0x32},
+               .req_parml      = sizeof(*msg) - sizeof(msg->hdr) -
+                                 sizeof(msg->cprbx),
+               .rpl_msgbl      = sizeof(*msg) - sizeof(msg->hdr),
+       };
+
+       msg->hdr = static_type6_hdrX;
+       msg->hdr.FromCardLen2 = random_number_length,
+       msg->cprbx = local_cprbx;
+       msg->cprbx.rpl_datal = random_number_length,
+       msg->cprbx.domain = AP_QID_QUEUE(ap_dev->qid);
+       memcpy(msg->function_code, msg->hdr.function_code, 0x02);
+       msg->rule_length = 0x0a;
+       memcpy(msg->rule, "RANDOM  ", 8);
+       msg->verb_length = 0x02;
+       msg->key_length = 0x02;
+       ap_msg->length = sizeof(*msg);
+}
+
+int zcrypt_msgtype6_init(void);
+void zcrypt_msgtype6_exit(void);
+
+#endif /* _ZCRYPT_MSGTYPE6_H_ */
index ccb4f8b60c750f9e7b636c4c5e5f900d523b6321..c7275e303a0d95074eefab828142c97406417c2b 100644 (file)
@@ -1,13 +1,14 @@
 /*
  *  zcrypt 2.1.0
  *
- *  Copyright IBM Corp. 2001, 2006
+ *  Copyright IBM Corp. 2001, 2012
  *  Author(s): Robert Burroughs
  *            Eric Rossman (edrossma@us.ibm.com)
  *
  *  Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
  *  Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
  *                               Ralph Wuerthner <rwuerthn@de.ibm.com>
+ *  MSGTYPE restruct:            Holger Dengler <hd@linux.vnet.ibm.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 #include "ap_bus.h"
 #include "zcrypt_api.h"
 #include "zcrypt_error.h"
-#include "zcrypt_pcicc.h"
+#include "zcrypt_msgtype6.h"
 #include "zcrypt_pcixcc.h"
 #include "zcrypt_cca_key.h"
+#include "zcrypt_msgtype6.h"
 
 #define PCIXCC_MIN_MOD_SIZE     16     /*  128 bits    */
 #define PCIXCC_MIN_MOD_SIZE_OLD         64     /*  512 bits    */
@@ -75,14 +77,12 @@ static struct ap_device_id zcrypt_pcixcc_ids[] = {
 
 MODULE_DEVICE_TABLE(ap, zcrypt_pcixcc_ids);
 MODULE_AUTHOR("IBM Corporation");
-MODULE_DESCRIPTION("PCIXCC Cryptographic Coprocessor device driver, "
-                  "Copyright IBM Corp. 2001, 2006");
+MODULE_DESCRIPTION("PCIXCC Cryptographic Coprocessor device driver, " \
+                  "Copyright IBM Corp. 2001, 2012");
 MODULE_LICENSE("GPL");
 
 static int zcrypt_pcixcc_probe(struct ap_device *ap_dev);
 static void zcrypt_pcixcc_remove(struct ap_device *ap_dev);
-static void zcrypt_pcixcc_receive(struct ap_device *, struct ap_message *,
-                                struct ap_message *);
 
 static struct ap_driver zcrypt_pcixcc_driver = {
        .probe = zcrypt_pcixcc_probe,
@@ -91,766 +91,6 @@ static struct ap_driver zcrypt_pcixcc_driver = {
        .request_timeout = PCIXCC_CLEANUP_TIME,
 };
 
-/**
- * The following is used to initialize the CPRBX passed to the PCIXCC/CEX2C
- * 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
- * device does *not* have 'Intel inside'.
- *
- * The CPRBX is followed immediately by the parm block.
- * The parm block contains:
- * - function code ('PD' 0x5044 or 'PK' 0x504B)
- * - rule block (one of:)
- *   + 0x000A 'PKCS-1.2' (MCL2 'PD')
- *   + 0x000A 'ZERO-PAD' (MCL2 'PK')
- *   + 0x000A 'ZERO-PAD' (MCL3 'PD' or CEX2C 'PD')
- *   + 0x000A 'MRP     ' (MCL3 'PK' or CEX2C 'PK')
- * - VUD block
- */
-static struct CPRBX static_cprbx = {
-       .cprb_len       =  0x00DC,
-       .cprb_ver_id    =  0x02,
-       .func_id        = {0x54,0x32},
-};
-
-/**
- * Convert a ICAMEX message to a type6 MEX message.
- *
- * @zdev: crypto device pointer
- * @ap_msg: pointer to AP message
- * @mex: pointer to user input data
- *
- * Returns 0 on success or -EFAULT.
- */
-static int ICAMEX_msg_to_type6MEX_msgX(struct zcrypt_device *zdev,
-                                      struct ap_message *ap_msg,
-                                      struct ica_rsa_modexpo *mex)
-{
-       static struct type6_hdr static_type6_hdrX = {
-               .type           =  0x06,
-               .offset1        =  0x00000058,
-               .agent_id       = {'C','A',},
-               .function_code  = {'P','K'},
-       };
-       static struct function_and_rules_block static_pke_fnr = {
-               .function_code  = {'P','K'},
-               .ulen           = 10,
-               .only_rule      = {'M','R','P',' ',' ',' ',' ',' '}
-       };
-       static struct function_and_rules_block static_pke_fnr_MCL2 = {
-               .function_code  = {'P','K'},
-               .ulen           = 10,
-               .only_rule      = {'Z','E','R','O','-','P','A','D'}
-       };
-       struct {
-               struct type6_hdr hdr;
-               struct CPRBX cprbx;
-               struct function_and_rules_block fr;
-               unsigned short length;
-               char text[0];
-       } __attribute__((packed)) *msg = ap_msg->message;
-       int size;
-
-       /* VUD.ciphertext */
-       msg->length = mex->inputdatalength + 2;
-       if (copy_from_user(msg->text, mex->inputdata, mex->inputdatalength))
-               return -EFAULT;
-
-       /* Set up key which is located after the variable length text. */
-       size = zcrypt_type6_mex_key_en(mex, msg->text+mex->inputdatalength, 1);
-       if (size < 0)
-               return size;
-       size += sizeof(*msg) + mex->inputdatalength;
-
-       /* 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->cprbx = static_cprbx;
-       msg->cprbx.domain = AP_QID_QUEUE(zdev->ap_dev->qid);
-       msg->cprbx.rpl_msgbl = msg->hdr.FromCardLen1;
-
-       msg->fr = (zdev->user_space_type == ZCRYPT_PCIXCC_MCL2) ?
-               static_pke_fnr_MCL2 : static_pke_fnr;
-
-       msg->cprbx.req_parml = size - sizeof(msg->hdr) - sizeof(msg->cprbx);
-
-       ap_msg->length = size;
-       return 0;
-}
-
-/**
- * Convert a ICACRT message to a type6 CRT message.
- *
- * @zdev: crypto device pointer
- * @ap_msg: pointer to AP message
- * @crt: pointer to user input data
- *
- * Returns 0 on success or -EFAULT.
- */
-static int ICACRT_msg_to_type6CRT_msgX(struct zcrypt_device *zdev,
-                                      struct ap_message *ap_msg,
-                                      struct ica_rsa_modexpo_crt *crt)
-{
-       static struct type6_hdr static_type6_hdrX = {
-               .type           =  0x06,
-               .offset1        =  0x00000058,
-               .agent_id       = {'C','A',},
-               .function_code  = {'P','D'},
-       };
-       static struct function_and_rules_block static_pkd_fnr = {
-               .function_code  = {'P','D'},
-               .ulen           = 10,
-               .only_rule      = {'Z','E','R','O','-','P','A','D'}
-       };
-
-       static struct function_and_rules_block static_pkd_fnr_MCL2 = {
-               .function_code  = {'P','D'},
-               .ulen           = 10,
-               .only_rule      = {'P','K','C','S','-','1','.','2'}
-       };
-       struct {
-               struct type6_hdr hdr;
-               struct CPRBX cprbx;
-               struct function_and_rules_block fr;
-               unsigned short length;
-               char text[0];
-       } __attribute__((packed)) *msg = ap_msg->message;
-       int size;
-
-       /* VUD.ciphertext */
-       msg->length = crt->inputdatalength + 2;
-       if (copy_from_user(msg->text, crt->inputdata, crt->inputdatalength))
-               return -EFAULT;
-
-       /* Set up key which is located after the variable length text. */
-       size = zcrypt_type6_crt_key(crt, msg->text + crt->inputdatalength, 1);
-       if (size < 0)
-               return size;
-       size += sizeof(*msg) + crt->inputdatalength;    /* total size of msg */
-
-       /* 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->cprbx = static_cprbx;
-       msg->cprbx.domain = AP_QID_QUEUE(zdev->ap_dev->qid);
-       msg->cprbx.req_parml = msg->cprbx.rpl_msgbl =
-               size - sizeof(msg->hdr) - sizeof(msg->cprbx);
-
-       msg->fr = (zdev->user_space_type == ZCRYPT_PCIXCC_MCL2) ?
-               static_pkd_fnr_MCL2 : static_pkd_fnr;
-
-       ap_msg->length = size;
-       return 0;
-}
-
-/**
- * Convert a XCRB message to a type6 CPRB message.
- *
- * @zdev: crypto device pointer
- * @ap_msg: pointer to AP message
- * @xcRB: pointer to user input data
- *
- * Returns 0 on success or -EFAULT, -EINVAL.
- */
-struct type86_fmt2_msg {
-       struct type86_hdr hdr;
-       struct type86_fmt2_ext fmt2;
-} __attribute__((packed));
-
-static int XCRB_msg_to_type6CPRB_msgX(struct zcrypt_device *zdev,
-                                      struct ap_message *ap_msg,
-                                      struct ica_xcRB *xcRB)
-{
-       static struct type6_hdr static_type6_hdrX = {
-               .type           =  0x06,
-               .offset1        =  0x00000058,
-       };
-       struct {
-               struct type6_hdr hdr;
-               struct CPRBX cprbx;
-       } __attribute__((packed)) *msg = ap_msg->message;
-
-       int rcblen = CEIL4(xcRB->request_control_blk_length);
-       int replylen;
-       char *req_data = ap_msg->message + sizeof(struct type6_hdr) + rcblen;
-       char *function_code;
-
-       /* length checks */
-       ap_msg->length = sizeof(struct type6_hdr) +
-               CEIL4(xcRB->request_control_blk_length) +
-               xcRB->request_data_length;
-       if (ap_msg->length > PCIXCC_MAX_XCRB_MESSAGE_SIZE)
-               return -EINVAL;
-       replylen = sizeof(struct type86_fmt2_msg) +
-               CEIL4(xcRB->reply_control_blk_length) +
-               xcRB->reply_data_length;
-       if (replylen > PCIXCC_MAX_XCRB_MESSAGE_SIZE)
-               return -EINVAL;
-
-       /* prepare type6 header */
-       msg->hdr = static_type6_hdrX;
-       memcpy(msg->hdr.agent_id , &(xcRB->agent_ID), sizeof(xcRB->agent_ID));
-       msg->hdr.ToCardLen1 = xcRB->request_control_blk_length;
-       if (xcRB->request_data_length) {
-               msg->hdr.offset2 = msg->hdr.offset1 + rcblen;
-               msg->hdr.ToCardLen2 = xcRB->request_data_length;
-       }
-       msg->hdr.FromCardLen1 = xcRB->reply_control_blk_length;
-       msg->hdr.FromCardLen2 = xcRB->reply_data_length;
-
-       /* prepare CPRB */
-       if (copy_from_user(&(msg->cprbx), xcRB->request_control_blk_addr,
-                   xcRB->request_control_blk_length))
-               return -EFAULT;
-       if (msg->cprbx.cprb_len + sizeof(msg->hdr.function_code) >
-           xcRB->request_control_blk_length)
-               return -EINVAL;
-       function_code = ((unsigned char *)&msg->cprbx) + msg->cprbx.cprb_len;
-       memcpy(msg->hdr.function_code, function_code, sizeof(msg->hdr.function_code));
-
-       if (memcmp(function_code, "US", 2) == 0)
-               ap_msg->special = 1;
-       else
-               ap_msg->special = 0;
-
-       /* copy data block */
-       if (xcRB->request_data_length &&
-           copy_from_user(req_data, xcRB->request_data_address,
-               xcRB->request_data_length))
-               return -EFAULT;
-       return 0;
-}
-
-/**
- * Prepare a type6 CPRB message for random number generation
- *
- * @ap_dev: AP device pointer
- * @ap_msg: pointer to AP message
- */
-static void rng_type6CPRB_msgX(struct ap_device *ap_dev,
-                              struct ap_message *ap_msg,
-                              unsigned random_number_length)
-{
-       struct {
-               struct type6_hdr hdr;
-               struct CPRBX cprbx;
-               char function_code[2];
-               short int rule_length;
-               char rule[8];
-               short int verb_length;
-               short int key_length;
-       } __attribute__((packed)) *msg = ap_msg->message;
-       static struct type6_hdr static_type6_hdrX = {
-               .type           = 0x06,
-               .offset1        = 0x00000058,
-               .agent_id       = {'C', 'A'},
-               .function_code  = {'R', 'L'},
-               .ToCardLen1     = sizeof *msg - sizeof(msg->hdr),
-               .FromCardLen1   = sizeof *msg - sizeof(msg->hdr),
-       };
-       static struct CPRBX local_cprbx = {
-               .cprb_len       = 0x00dc,
-               .cprb_ver_id    = 0x02,
-               .func_id        = {0x54, 0x32},
-               .req_parml      = sizeof *msg - sizeof(msg->hdr) -
-                                 sizeof(msg->cprbx),
-               .rpl_msgbl      = sizeof *msg - sizeof(msg->hdr),
-       };
-
-       msg->hdr = static_type6_hdrX;
-       msg->hdr.FromCardLen2 = random_number_length,
-       msg->cprbx = local_cprbx;
-       msg->cprbx.rpl_datal = random_number_length,
-       msg->cprbx.domain = AP_QID_QUEUE(ap_dev->qid);
-       memcpy(msg->function_code, msg->hdr.function_code, 0x02);
-       msg->rule_length = 0x0a;
-       memcpy(msg->rule, "RANDOM  ", 8);
-       msg->verb_length = 0x02;
-       msg->key_length = 0x02;
-       ap_msg->length = sizeof *msg;
-}
-
-/**
- * Copy results from a type 86 ICA reply message back to user space.
- *
- * @zdev: crypto device pointer
- * @reply: reply AP message.
- * @data: pointer to user output data
- * @length: size of user output data
- *
- * Returns 0 on success or -EINVAL, -EFAULT, -EAGAIN in case of an error.
- */
-struct type86x_reply {
-       struct type86_hdr hdr;
-       struct type86_fmt2_ext fmt2;
-       struct CPRBX cprbx;
-       unsigned char pad[4];   /* 4 byte function code/rules block ? */
-       unsigned short length;
-       char text[0];
-} __attribute__((packed));
-
-static int convert_type86_ica(struct zcrypt_device *zdev,
-                         struct ap_message *reply,
-                         char __user *outputdata,
-                         unsigned int outputdatalength)
-{
-       static unsigned char static_pad[] = {
-               0x00,0x02,
-               0x1B,0x7B,0x5D,0xB5,0x75,0x01,0x3D,0xFD,
-               0x8D,0xD1,0xC7,0x03,0x2D,0x09,0x23,0x57,
-               0x89,0x49,0xB9,0x3F,0xBB,0x99,0x41,0x5B,
-               0x75,0x21,0x7B,0x9D,0x3B,0x6B,0x51,0x39,
-               0xBB,0x0D,0x35,0xB9,0x89,0x0F,0x93,0xA5,
-               0x0B,0x47,0xF1,0xD3,0xBB,0xCB,0xF1,0x9D,
-               0x23,0x73,0x71,0xFF,0xF3,0xF5,0x45,0xFB,
-               0x61,0x29,0x23,0xFD,0xF1,0x29,0x3F,0x7F,
-               0x17,0xB7,0x1B,0xA9,0x19,0xBD,0x57,0xA9,
-               0xD7,0x95,0xA3,0xCB,0xED,0x1D,0xDB,0x45,
-               0x7D,0x11,0xD1,0x51,0x1B,0xED,0x71,0xE9,
-               0xB1,0xD1,0xAB,0xAB,0x21,0x2B,0x1B,0x9F,
-               0x3B,0x9F,0xF7,0xF7,0xBD,0x63,0xEB,0xAD,
-               0xDF,0xB3,0x6F,0x5B,0xDB,0x8D,0xA9,0x5D,
-               0xE3,0x7D,0x77,0x49,0x47,0xF5,0xA7,0xFD,
-               0xAB,0x2F,0x27,0x35,0x77,0xD3,0x49,0xC9,
-               0x09,0xEB,0xB1,0xF9,0xBF,0x4B,0xCB,0x2B,
-               0xEB,0xEB,0x05,0xFF,0x7D,0xC7,0x91,0x8B,
-               0x09,0x83,0xB9,0xB9,0x69,0x33,0x39,0x6B,
-               0x79,0x75,0x19,0xBF,0xBB,0x07,0x1D,0xBD,
-               0x29,0xBF,0x39,0x95,0x93,0x1D,0x35,0xC7,
-               0xC9,0x4D,0xE5,0x97,0x0B,0x43,0x9B,0xF1,
-               0x16,0x93,0x03,0x1F,0xA5,0xFB,0xDB,0xF3,
-               0x27,0x4F,0x27,0x61,0x05,0x1F,0xB9,0x23,
-               0x2F,0xC3,0x81,0xA9,0x23,0x71,0x55,0x55,
-               0xEB,0xED,0x41,0xE5,0xF3,0x11,0xF1,0x43,
-               0x69,0x03,0xBD,0x0B,0x37,0x0F,0x51,0x8F,
-               0x0B,0xB5,0x89,0x5B,0x67,0xA9,0xD9,0x4F,
-               0x01,0xF9,0x21,0x77,0x37,0x73,0x79,0xC5,
-               0x7F,0x51,0xC1,0xCF,0x97,0xA1,0x75,0xAD,
-               0x35,0x9D,0xD3,0xD3,0xA7,0x9D,0x5D,0x41,
-               0x6F,0x65,0x1B,0xCF,0xA9,0x87,0x91,0x09
-       };
-       struct type86x_reply *msg = reply->message;
-       unsigned short service_rc, service_rs;
-       unsigned int reply_len, pad_len;
-       char *data;
-
-       service_rc = msg->cprbx.ccp_rtcode;
-       if (unlikely(service_rc != 0)) {
-               service_rs = msg->cprbx.ccp_rscode;
-               if (service_rc == 8 && service_rs == 66)
-                       return -EINVAL;
-               if (service_rc == 8 && service_rs == 65)
-                       return -EINVAL;
-               if (service_rc == 8 && service_rs == 770)
-                       return -EINVAL;
-               if (service_rc == 8 && service_rs == 783) {
-                       zdev->min_mod_size = PCIXCC_MIN_MOD_SIZE_OLD;
-                       return -EAGAIN;
-               }
-               if (service_rc == 12 && service_rs == 769)
-                       return -EINVAL;
-               if (service_rc == 8 && service_rs == 72)
-                       return -EINVAL;
-               zdev->online = 0;
-               return -EAGAIN; /* repeat the request on a different device. */
-       }
-       data = msg->text;
-       reply_len = msg->length - 2;
-       if (reply_len > outputdatalength)
-               return -EINVAL;
-       /*
-        * For all encipher requests, the length of the ciphertext (reply_len)
-        * will always equal the modulus length. For MEX decipher requests
-        * the output needs to get padded. Minimum pad size is 10.
-        *
-        * Currently, the cases where padding will be added is for:
-        * - PCIXCC_MCL2 using a CRT form token (since PKD didn't support
-        *   ZERO-PAD and CRT is only supported for PKD requests)
-        * - PCICC, always
-        */
-       pad_len = outputdatalength - reply_len;
-       if (pad_len > 0) {
-               if (pad_len < 10)
-                       return -EINVAL;
-               /* 'restore' padding left in the PCICC/PCIXCC card. */
-               if (copy_to_user(outputdata, static_pad, pad_len - 1))
-                       return -EFAULT;
-               if (put_user(0, outputdata + pad_len - 1))
-                       return -EFAULT;
-       }
-       /* Copy the crypto response to user space. */
-       if (copy_to_user(outputdata + pad_len, data, reply_len))
-               return -EFAULT;
-       return 0;
-}
-
-/**
- * Copy results from a type 86 XCRB reply message back to user space.
- *
- * @zdev: crypto device pointer
- * @reply: reply AP message.
- * @xcRB: pointer to XCRB
- *
- * Returns 0 on success or -EINVAL, -EFAULT, -EAGAIN in case of an error.
- */
-static int convert_type86_xcrb(struct zcrypt_device *zdev,
-                              struct ap_message *reply,
-                              struct ica_xcRB *xcRB)
-{
-       struct type86_fmt2_msg *msg = reply->message;
-       char *data = reply->message;
-
-       /* Copy CPRB to user */
-       if (copy_to_user(xcRB->reply_control_blk_addr,
-               data + msg->fmt2.offset1, msg->fmt2.count1))
-               return -EFAULT;
-       xcRB->reply_control_blk_length = msg->fmt2.count1;
-
-       /* Copy data buffer to user */
-       if (msg->fmt2.count2)
-               if (copy_to_user(xcRB->reply_data_addr,
-                       data + msg->fmt2.offset2, msg->fmt2.count2))
-                       return -EFAULT;
-       xcRB->reply_data_length = msg->fmt2.count2;
-       return 0;
-}
-
-static int convert_type86_rng(struct zcrypt_device *zdev,
-                         struct ap_message *reply,
-                         char *buffer)
-{
-       struct {
-               struct type86_hdr hdr;
-               struct type86_fmt2_ext fmt2;
-               struct CPRBX cprbx;
-       } __attribute__((packed)) *msg = reply->message;
-       char *data = reply->message;
-
-       if (msg->cprbx.ccp_rtcode != 0 || msg->cprbx.ccp_rscode != 0)
-               return -EINVAL;
-       memcpy(buffer, data + msg->fmt2.offset2, msg->fmt2.count2);
-       return msg->fmt2.count2;
-}
-
-static int convert_response_ica(struct zcrypt_device *zdev,
-                           struct ap_message *reply,
-                           char __user *outputdata,
-                           unsigned int outputdatalength)
-{
-       struct type86x_reply *msg = reply->message;
-
-       /* Response type byte is the second byte in the response. */
-       switch (((unsigned char *) reply->message)[1]) {
-       case TYPE82_RSP_CODE:
-       case TYPE88_RSP_CODE:
-               return convert_error(zdev, reply);
-       case TYPE86_RSP_CODE:
-               if (msg->cprbx.ccp_rtcode &&
-                  (msg->cprbx.ccp_rscode == 0x14f) &&
-                  (outputdatalength > 256)) {
-                       if (zdev->max_exp_bit_length <= 17) {
-                               zdev->max_exp_bit_length = 17;
-                               return -EAGAIN;
-                       } else
-                               return -EINVAL;
-               }
-               if (msg->hdr.reply_code)
-                       return convert_error(zdev, reply);
-               if (msg->cprbx.cprb_ver_id == 0x02)
-                       return convert_type86_ica(zdev, reply,
-                                                 outputdata, outputdatalength);
-               /* Fall through, no break, incorrect cprb version is an unknown
-                * response */
-       default: /* Unknown response type, this should NEVER EVER happen */
-               zdev->online = 0;
-               return -EAGAIN; /* repeat the request on a different device. */
-       }
-}
-
-static int convert_response_xcrb(struct zcrypt_device *zdev,
-                           struct ap_message *reply,
-                           struct ica_xcRB *xcRB)
-{
-       struct type86x_reply *msg = reply->message;
-
-       /* Response type byte is the second byte in the response. */
-       switch (((unsigned char *) reply->message)[1]) {
-       case TYPE82_RSP_CODE:
-       case TYPE88_RSP_CODE:
-               xcRB->status = 0x0008044DL; /* HDD_InvalidParm */
-               return convert_error(zdev, reply);
-       case TYPE86_RSP_CODE:
-               if (msg->hdr.reply_code) {
-                       memcpy(&(xcRB->status), msg->fmt2.apfs, sizeof(u32));
-                       return convert_error(zdev, reply);
-               }
-               if (msg->cprbx.cprb_ver_id == 0x02)
-                       return convert_type86_xcrb(zdev, reply, xcRB);
-               /* Fall through, no break, incorrect cprb version is an unknown
-                * response */
-       default: /* Unknown response type, this should NEVER EVER happen */
-               xcRB->status = 0x0008044DL; /* HDD_InvalidParm */
-               zdev->online = 0;
-               return -EAGAIN; /* repeat the request on a different device. */
-       }
-}
-
-static int convert_response_rng(struct zcrypt_device *zdev,
-                                struct ap_message *reply,
-                                char *data)
-{
-       struct type86x_reply *msg = reply->message;
-
-       switch (msg->hdr.type) {
-       case TYPE82_RSP_CODE:
-       case TYPE88_RSP_CODE:
-               return -EINVAL;
-       case TYPE86_RSP_CODE:
-               if (msg->hdr.reply_code)
-                       return -EINVAL;
-               if (msg->cprbx.cprb_ver_id == 0x02)
-                       return convert_type86_rng(zdev, reply, data);
-               /* Fall through, no break, incorrect cprb version is an unknown
-                * response */
-       default: /* Unknown response type, this should NEVER EVER happen */
-               zdev->online = 0;
-               return -EAGAIN; /* repeat the request on a different device. */
-       }
-}
-
-/**
- * This function is called from the AP bus code after a crypto request
- * "msg" has finished with the reply message "reply".
- * It is called from tasklet context.
- * @ap_dev: pointer to the AP device
- * @msg: pointer to the AP message
- * @reply: pointer to the AP reply message
- */
-static void zcrypt_pcixcc_receive(struct ap_device *ap_dev,
-                                 struct ap_message *msg,
-                                 struct ap_message *reply)
-{
-       static struct error_hdr error_reply = {
-               .type = TYPE82_RSP_CODE,
-               .reply_code = REP82_ERROR_MACHINE_FAILURE,
-       };
-       struct response_type *resp_type =
-               (struct response_type *) msg->private;
-       struct type86x_reply *t86r;
-       int length;
-
-       /* Copy the reply message to the request message buffer. */
-       if (IS_ERR(reply)) {
-               memcpy(msg->message, &error_reply, sizeof(error_reply));
-               goto out;
-       }
-       t86r = reply->message;
-       if (t86r->hdr.type == TYPE86_RSP_CODE &&
-                t86r->cprbx.cprb_ver_id == 0x02) {
-               switch (resp_type->type) {
-               case PCIXCC_RESPONSE_TYPE_ICA:
-                       length = sizeof(struct type86x_reply)
-                               + t86r->length - 2;
-                       length = min(PCIXCC_MAX_ICA_RESPONSE_SIZE, length);
-                       memcpy(msg->message, reply->message, length);
-                       break;
-               case PCIXCC_RESPONSE_TYPE_XCRB:
-                       length = t86r->fmt2.offset2 + t86r->fmt2.count2;
-                       length = min(PCIXCC_MAX_XCRB_MESSAGE_SIZE, length);
-                       memcpy(msg->message, reply->message, length);
-                       break;
-               default:
-                       memcpy(msg->message, &error_reply, sizeof error_reply);
-               }
-       } else
-               memcpy(msg->message, reply->message, sizeof error_reply);
-out:
-       complete(&(resp_type->work));
-}
-
-static atomic_t zcrypt_step = ATOMIC_INIT(0);
-
-/**
- * The request distributor calls this function if it picked the PCIXCC/CEX2C
- * device to handle a modexpo request.
- * @zdev: pointer to zcrypt_device structure that identifies the
- *       PCIXCC/CEX2C device to the request distributor
- * @mex: pointer to the modexpo request buffer
- */
-static long zcrypt_pcixcc_modexpo(struct zcrypt_device *zdev,
-                                 struct ica_rsa_modexpo *mex)
-{
-       struct ap_message ap_msg;
-       struct response_type resp_type = {
-               .type = PCIXCC_RESPONSE_TYPE_ICA,
-       };
-       int rc;
-
-       ap_init_message(&ap_msg);
-       ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL);
-       if (!ap_msg.message)
-               return -ENOMEM;
-       ap_msg.receive = zcrypt_pcixcc_receive;
-       ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
-                               atomic_inc_return(&zcrypt_step);
-       ap_msg.private = &resp_type;
-       rc = ICAMEX_msg_to_type6MEX_msgX(zdev, &ap_msg, mex);
-       if (rc)
-               goto out_free;
-       init_completion(&resp_type.work);
-       ap_queue_message(zdev->ap_dev, &ap_msg);
-       rc = wait_for_completion_interruptible(&resp_type.work);
-       if (rc == 0)
-               rc = convert_response_ica(zdev, &ap_msg, mex->outputdata,
-                                         mex->outputdatalength);
-       else
-               /* Signal pending. */
-               ap_cancel_message(zdev->ap_dev, &ap_msg);
-out_free:
-       free_page((unsigned long) ap_msg.message);
-       return rc;
-}
-
-/**
- * The request distributor calls this function if it picked the PCIXCC/CEX2C
- * device to handle a modexpo_crt request.
- * @zdev: pointer to zcrypt_device structure that identifies the
- *       PCIXCC/CEX2C device to the request distributor
- * @crt: pointer to the modexpoc_crt request buffer
- */
-static long zcrypt_pcixcc_modexpo_crt(struct zcrypt_device *zdev,
-                                     struct ica_rsa_modexpo_crt *crt)
-{
-       struct ap_message ap_msg;
-       struct response_type resp_type = {
-               .type = PCIXCC_RESPONSE_TYPE_ICA,
-       };
-       int rc;
-
-       ap_init_message(&ap_msg);
-       ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL);
-       if (!ap_msg.message)
-               return -ENOMEM;
-       ap_msg.receive = zcrypt_pcixcc_receive;
-       ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
-                               atomic_inc_return(&zcrypt_step);
-       ap_msg.private = &resp_type;
-       rc = ICACRT_msg_to_type6CRT_msgX(zdev, &ap_msg, crt);
-       if (rc)
-               goto out_free;
-       init_completion(&resp_type.work);
-       ap_queue_message(zdev->ap_dev, &ap_msg);
-       rc = wait_for_completion_interruptible(&resp_type.work);
-       if (rc == 0)
-               rc = convert_response_ica(zdev, &ap_msg, crt->outputdata,
-                                         crt->outputdatalength);
-       else
-               /* Signal pending. */
-               ap_cancel_message(zdev->ap_dev, &ap_msg);
-out_free:
-       free_page((unsigned long) ap_msg.message);
-       return rc;
-}
-
-/**
- * The request distributor calls this function if it picked the PCIXCC/CEX2C
- * device to handle a send_cprb request.
- * @zdev: pointer to zcrypt_device structure that identifies the
- *       PCIXCC/CEX2C device to the request distributor
- * @xcRB: pointer to the send_cprb request buffer
- */
-static long zcrypt_pcixcc_send_cprb(struct zcrypt_device *zdev,
-                                   struct ica_xcRB *xcRB)
-{
-       struct ap_message ap_msg;
-       struct response_type resp_type = {
-               .type = PCIXCC_RESPONSE_TYPE_XCRB,
-       };
-       int rc;
-
-       ap_init_message(&ap_msg);
-       ap_msg.message = kmalloc(PCIXCC_MAX_XCRB_MESSAGE_SIZE, GFP_KERNEL);
-       if (!ap_msg.message)
-               return -ENOMEM;
-       ap_msg.receive = zcrypt_pcixcc_receive;
-       ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
-                               atomic_inc_return(&zcrypt_step);
-       ap_msg.private = &resp_type;
-       rc = XCRB_msg_to_type6CPRB_msgX(zdev, &ap_msg, xcRB);
-       if (rc)
-               goto out_free;
-       init_completion(&resp_type.work);
-       ap_queue_message(zdev->ap_dev, &ap_msg);
-       rc = wait_for_completion_interruptible(&resp_type.work);
-       if (rc == 0)
-               rc = convert_response_xcrb(zdev, &ap_msg, xcRB);
-       else
-               /* Signal pending. */
-               ap_cancel_message(zdev->ap_dev, &ap_msg);
-out_free:
-       kzfree(ap_msg.message);
-       return rc;
-}
-
-/**
- * The request distributor calls this function if it picked the PCIXCC/CEX2C
- * device to generate random data.
- * @zdev: pointer to zcrypt_device structure that identifies the
- *       PCIXCC/CEX2C device to the request distributor
- * @buffer: pointer to a memory page to return random data
- */
-
-static long zcrypt_pcixcc_rng(struct zcrypt_device *zdev,
-                                   char *buffer)
-{
-       struct ap_message ap_msg;
-       struct response_type resp_type = {
-               .type = PCIXCC_RESPONSE_TYPE_XCRB,
-       };
-       int rc;
-
-       ap_init_message(&ap_msg);
-       ap_msg.message = kmalloc(PCIXCC_MAX_XCRB_MESSAGE_SIZE, GFP_KERNEL);
-       if (!ap_msg.message)
-               return -ENOMEM;
-       ap_msg.receive = zcrypt_pcixcc_receive;
-       ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
-                               atomic_inc_return(&zcrypt_step);
-       ap_msg.private = &resp_type;
-       rng_type6CPRB_msgX(zdev->ap_dev, &ap_msg, ZCRYPT_RNG_BUFFER_SIZE);
-       init_completion(&resp_type.work);
-       ap_queue_message(zdev->ap_dev, &ap_msg);
-       rc = wait_for_completion_interruptible(&resp_type.work);
-       if (rc == 0)
-               rc = convert_response_rng(zdev, &ap_msg, buffer);
-       else
-               /* Signal pending. */
-               ap_cancel_message(zdev->ap_dev, &ap_msg);
-       kfree(ap_msg.message);
-       return rc;
-}
-
-/**
- * The crypto operations for a PCIXCC/CEX2C card.
- */
-static struct zcrypt_ops zcrypt_pcixcc_ops = {
-       .rsa_modexpo = zcrypt_pcixcc_modexpo,
-       .rsa_modexpo_crt = zcrypt_pcixcc_modexpo_crt,
-       .send_cprb = zcrypt_pcixcc_send_cprb,
-};
-
-static struct zcrypt_ops zcrypt_pcixcc_with_rng_ops = {
-       .rsa_modexpo = zcrypt_pcixcc_modexpo,
-       .rsa_modexpo_crt = zcrypt_pcixcc_modexpo_crt,
-       .send_cprb = zcrypt_pcixcc_send_cprb,
-       .rng = zcrypt_pcixcc_rng,
-};
-
 /**
  * Micro-code detection function. Its sends a message to a pcixcc card
  * to find out the microcode level.
@@ -1083,9 +323,11 @@ static int zcrypt_pcixcc_probe(struct ap_device *ap_dev)
                return rc;
        }
        if (rc)
-               zdev->ops = &zcrypt_pcixcc_with_rng_ops;
+               zdev->ops = zcrypt_msgtype_request(MSGTYPE06_NAME,
+                                                  MSGTYPE06_VARIANT_DEFAULT);
        else
-               zdev->ops = &zcrypt_pcixcc_ops;
+               zdev->ops = zcrypt_msgtype_request(MSGTYPE06_NAME,
+                                                  MSGTYPE06_VARIANT_NORNG);
        ap_dev->reply = &zdev->reply;
        ap_dev->private = zdev;
        rc = zcrypt_device_register(zdev);
@@ -1095,6 +337,7 @@ static int zcrypt_pcixcc_probe(struct ap_device *ap_dev)
 
  out_free:
        ap_dev->private = NULL;
+       zcrypt_msgtype_release(zdev->ops);
        zcrypt_device_free(zdev);
        return rc;
 }
@@ -1106,8 +349,10 @@ static int zcrypt_pcixcc_probe(struct ap_device *ap_dev)
 static void zcrypt_pcixcc_remove(struct ap_device *ap_dev)
 {
        struct zcrypt_device *zdev = ap_dev->private;
+       struct zcrypt_ops *zops = zdev->ops;
 
        zcrypt_device_unregister(zdev);
+       zcrypt_msgtype_release(zops);
 }
 
 int __init zcrypt_pcixcc_init(void)
index c7cdf599e46b1a67230e489ff10bb3dc9a2da776..eacafc8962f204377d2c95f8d9a5bfd032d16eb2 100644 (file)
@@ -1,12 +1,13 @@
 /*
  *  zcrypt 2.1.0
  *
- *  Copyright IBM Corp. 2001, 2006
+ *  Copyright IBM Corp. 2001, 2012
  *  Author(s): Robert Burroughs
  *            Eric Rossman (edrossma@us.ibm.com)
  *
  *  Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
  *  Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
+ *  MSGTYPE restruct:            Holger Dengler <hd@linux.vnet.ibm.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by