aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/crypto
diff options
context:
space:
mode:
authorHolger Dengler <hd@linux.vnet.ibm.com>2012-08-28 16:45:36 +0200
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2012-09-26 15:44:55 +0200
commit5e55a488c87aa6cc269b04bfec99e835f03b2c2d (patch)
tree59d848f6cf6a67a520a3bd482a853b2955cf97d2 /drivers/s390/crypto
parents390/ap: Add functiton facility information as AP device attribute. (diff)
downloadlinux-dev-5e55a488c87aa6cc269b04bfec99e835f03b2c2d.tar.xz
linux-dev-5e55a488c87aa6cc269b04bfec99e835f03b2c2d.zip
s390/zcrypt: Separate msgtype implementation from card modules.
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>
Diffstat (limited to 'drivers/s390/crypto')
-rw-r--r--drivers/s390/crypto/Makefile1
-rw-r--r--drivers/s390/crypto/zcrypt_api.c73
-rw-r--r--drivers/s390/crypto/zcrypt_api.h10
-rw-r--r--drivers/s390/crypto/zcrypt_cex2a.c368
-rw-r--r--drivers/s390/crypto/zcrypt_msgtype50.c531
-rw-r--r--drivers/s390/crypto/zcrypt_msgtype50.h39
-rw-r--r--drivers/s390/crypto/zcrypt_msgtype6.c856
-rw-r--r--drivers/s390/crypto/zcrypt_msgtype6.h169
-rw-r--r--drivers/s390/crypto/zcrypt_pcixcc.c781
-rw-r--r--drivers/s390/crypto/zcrypt_pcixcc.h3
10 files changed, 1707 insertions, 1124 deletions
diff --git a/drivers/s390/crypto/Makefile b/drivers/s390/crypto/Makefile
index af3c7f16ea88..eca0bfaae08a 100644
--- a/drivers/s390/crypto/Makefile
+++ b/drivers/s390/crypto/Makefile
@@ -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
diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c
index 2f94132246a1..f1f026e0b189 100644
--- a/drivers/s390/crypto/zcrypt_api.c
+++ b/drivers/s390/crypto/zcrypt_api.c
@@ -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.
*
diff --git a/drivers/s390/crypto/zcrypt_api.h b/drivers/s390/crypto/zcrypt_api.h
index 7a32c4bc8ef9..02b2d35de1a7 100644
--- a/drivers/s390/crypto/zcrypt_api.h
+++ b/drivers/s390/crypto/zcrypt_api.h
@@ -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);
diff --git a/drivers/s390/crypto/zcrypt_cex2a.c b/drivers/s390/crypto/zcrypt_cex2a.c
index 8f5f70b43b29..1e849d6e1dfe 100644
--- a/drivers/s390/crypto/zcrypt_cex2a.c
+++ b/drivers/s390/crypto/zcrypt_cex2a.c
@@ -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,
@@ -80,344 +80,6 @@ static struct ap_driver zcrypt_cex2a_driver = {
};
/**
- * 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.
* @ap_dev: pointer to the AP device.
@@ -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
index 000000000000..035b6dc31b71
--- /dev/null
+++ b/drivers/s390/crypto/zcrypt_msgtype50.c
@@ -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
index 000000000000..e56dc72c7733
--- /dev/null
+++ b/drivers/s390/crypto/zcrypt_msgtype50.h
@@ -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
index 000000000000..7d97fa5a26d0
--- /dev/null
+++ b/drivers/s390/crypto/zcrypt_msgtype6.c
@@ -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
index 000000000000..1e500d3c0735
--- /dev/null
+++ b/drivers/s390/crypto/zcrypt_msgtype6.h
@@ -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_ */
diff --git a/drivers/s390/crypto/zcrypt_pcixcc.c b/drivers/s390/crypto/zcrypt_pcixcc.c
index ccb4f8b60c75..c7275e303a0d 100644
--- a/drivers/s390/crypto/zcrypt_pcixcc.c
+++ b/drivers/s390/crypto/zcrypt_pcixcc.c
@@ -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,9 +36,10 @@
#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,
@@ -92,766 +92,6 @@ static struct ap_driver zcrypt_pcixcc_driver = {
};
/**
- * 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.
* @ap_dev: pointer to the AP device.
@@ -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)
diff --git a/drivers/s390/crypto/zcrypt_pcixcc.h b/drivers/s390/crypto/zcrypt_pcixcc.h
index c7cdf599e46b..eacafc8962f2 100644
--- a/drivers/s390/crypto/zcrypt_pcixcc.h
+++ b/drivers/s390/crypto/zcrypt_pcixcc.h
@@ -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