diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:27:49 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:27:49 +0000 |
commit | ace9429bb58fd418f0c81d4c2835699bddf6bde6 (patch) | |
tree | b2d64bc10158fdd5497876388cd68142ca374ed3 /drivers/s390/crypto/zcrypt_msgtype6.c | |
parent | Initial commit. (diff) | |
download | linux-ace9429bb58fd418f0c81d4c2835699bddf6bde6.tar.xz linux-ace9429bb58fd418f0c81d4c2835699bddf6bde6.zip |
Adding upstream version 6.6.15.upstream/6.6.15
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/s390/crypto/zcrypt_msgtype6.c')
-rw-r--r-- | drivers/s390/crypto/zcrypt_msgtype6.c | 1381 |
1 files changed, 1381 insertions, 0 deletions
diff --git a/drivers/s390/crypto/zcrypt_msgtype6.c b/drivers/s390/crypto/zcrypt_msgtype6.c new file mode 100644 index 0000000000..3c53abbdc3 --- /dev/null +++ b/drivers/s390/crypto/zcrypt_msgtype6.c @@ -0,0 +1,1381 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright IBM Corp. 2001, 2023 + * 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> + */ + +#define KMSG_COMPONENT "zcrypt" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + +#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 CEXXC_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 CEXXC_RESPONSE_TYPE_ICA 0 +#define CEXXC_RESPONSE_TYPE_XCRB 1 +#define CEXXC_RESPONSE_TYPE_EP11 2 + +MODULE_AUTHOR("IBM Corporation"); +MODULE_DESCRIPTION("Cryptographic Coprocessor (message type 6), " \ + "Copyright IBM Corp. 2001, 2023"); +MODULE_LICENSE("GPL"); + +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 CEXxC/CEXxP + * card in a type6 message. The 3 fields that must be filled in at execution + * time are req_parml, rpl_parml and usage_domain. + * Everything about this interface is ascii/big-endian, since the + * 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 const struct CPRBX static_cprbx = { + .cprb_len = 0x00DC, + .cprb_ver_id = 0x02, + .func_id = {0x54, 0x32}, +}; + +int speed_idx_cca(int req_type) +{ + switch (req_type) { + case 0x4142: + case 0x4149: + case 0x414D: + case 0x4341: + case 0x4344: + case 0x4354: + case 0x4358: + case 0x444B: + case 0x4558: + case 0x4643: + case 0x4651: + case 0x4C47: + case 0x4C4B: + case 0x4C51: + case 0x4F48: + case 0x504F: + case 0x5053: + case 0x5058: + case 0x5343: + case 0x5344: + case 0x5345: + case 0x5350: + return LOW; + case 0x414B: + case 0x4345: + case 0x4349: + case 0x434D: + case 0x4847: + case 0x4849: + case 0x484D: + case 0x4850: + case 0x4851: + case 0x4954: + case 0x4958: + case 0x4B43: + case 0x4B44: + case 0x4B45: + case 0x4B47: + case 0x4B48: + case 0x4B49: + case 0x4B4E: + case 0x4B50: + case 0x4B52: + case 0x4B54: + case 0x4B58: + case 0x4D50: + case 0x4D53: + case 0x4D56: + case 0x4D58: + case 0x5044: + case 0x5045: + case 0x5046: + case 0x5047: + case 0x5049: + case 0x504B: + case 0x504D: + case 0x5254: + case 0x5347: + case 0x5349: + case 0x534B: + case 0x534D: + case 0x5356: + case 0x5358: + case 0x5443: + case 0x544B: + case 0x5647: + return HIGH; + default: + return MEDIUM; + } +} + +int speed_idx_ep11(int req_type) +{ + switch (req_type) { + case 1: + case 2: + case 36: + case 37: + case 38: + case 39: + case 40: + return LOW; + case 17: + case 18: + case 19: + case 20: + case 21: + case 22: + case 26: + case 30: + case 31: + case 32: + case 33: + case 34: + case 35: + return HIGH; + default: + return MEDIUM; + } +} + +/* + * Convert a ICAMEX message to a type6 MEX message. + * + * @zq: crypto device pointer + * @ap_msg: pointer to AP message + * @mex: pointer to user input data + * + * Returns 0 on success or negative errno value. + */ +static int icamex_msg_to_type6mex_msgx(struct zcrypt_queue *zq, + 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', ' ', ' ', ' ', ' ', ' '} + }; + struct { + struct type6_hdr hdr; + struct CPRBX cprbx; + struct function_and_rules_block fr; + unsigned short length; + char text[]; + } __packed * msg = ap_msg->msg; + int size; + + /* + * The inputdatalength was a selection criteria in the dispatching + * function zcrypt_rsa_modexpo(). However, make sure the following + * copy_from_user() never exceeds the allocated buffer space. + */ + if (WARN_ON_ONCE(mex->inputdatalength > PAGE_SIZE)) + return -EINVAL; + + /* 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); + 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 = CEXXC_MAX_ICA_RESPONSE_SIZE - sizeof(msg->hdr); + + msg->cprbx = static_cprbx; + msg->cprbx.domain = AP_QID_QUEUE(zq->queue->qid); + msg->cprbx.rpl_msgbl = msg->hdr.fromcardlen1; + + msg->fr = static_pke_fnr; + + msg->cprbx.req_parml = size - sizeof(msg->hdr) - sizeof(msg->cprbx); + + ap_msg->len = size; + return 0; +} + +/* + * Convert a ICACRT message to a type6 CRT message. + * + * @zq: crypto device pointer + * @ap_msg: pointer to AP message + * @crt: pointer to user input data + * + * Returns 0 on success or negative errno value. + */ +static int icacrt_msg_to_type6crt_msgx(struct zcrypt_queue *zq, + 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'} + }; + + struct { + struct type6_hdr hdr; + struct CPRBX cprbx; + struct function_and_rules_block fr; + unsigned short length; + char text[]; + } __packed * msg = ap_msg->msg; + int size; + + /* + * The inputdatalength was a selection criteria in the dispatching + * function zcrypt_rsa_crt(). However, make sure the following + * copy_from_user() never exceeds the allocated buffer space. + */ + if (WARN_ON_ONCE(crt->inputdatalength > PAGE_SIZE)) + return -EINVAL; + + /* 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); + 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 = CEXXC_MAX_ICA_RESPONSE_SIZE - sizeof(msg->hdr); + + msg->cprbx = static_cprbx; + msg->cprbx.domain = AP_QID_QUEUE(zq->queue->qid); + msg->cprbx.req_parml = msg->cprbx.rpl_msgbl = + size - sizeof(msg->hdr) - sizeof(msg->cprbx); + + msg->fr = static_pkd_fnr; + + ap_msg->len = size; + return 0; +} + +/* + * Convert a XCRB message to a type6 CPRB message. + * + * @zq: 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(bool userspace, struct ap_message *ap_msg, + struct ica_xcRB *xcrb, + unsigned int *fcode, + unsigned short **dom) +{ + static struct type6_hdr static_type6_hdrX = { + .type = 0x06, + .offset1 = 0x00000058, + }; + struct { + struct type6_hdr hdr; + union { + struct CPRBX cprbx; + DECLARE_FLEX_ARRAY(u8, userdata); + }; + } __packed * msg = ap_msg->msg; + + int rcblen = CEIL4(xcrb->request_control_blk_length); + int req_sumlen, resp_sumlen; + char *req_data = ap_msg->msg + sizeof(struct type6_hdr) + rcblen; + char *function_code; + + if (CEIL4(xcrb->request_control_blk_length) < + xcrb->request_control_blk_length) + return -EINVAL; /* overflow after alignment*/ + + /* length checks */ + ap_msg->len = sizeof(struct type6_hdr) + + CEIL4(xcrb->request_control_blk_length) + + xcrb->request_data_length; + if (ap_msg->len > ap_msg->bufsize) + return -EINVAL; + + /* + * Overflow check + * sum must be greater (or equal) than the largest operand + */ + req_sumlen = CEIL4(xcrb->request_control_blk_length) + + xcrb->request_data_length; + if ((CEIL4(xcrb->request_control_blk_length) <= + xcrb->request_data_length) ? + req_sumlen < xcrb->request_data_length : + req_sumlen < CEIL4(xcrb->request_control_blk_length)) { + return -EINVAL; + } + + if (CEIL4(xcrb->reply_control_blk_length) < + xcrb->reply_control_blk_length) + return -EINVAL; /* overflow after alignment*/ + + /* + * Overflow check + * sum must be greater (or equal) than the largest operand + */ + resp_sumlen = CEIL4(xcrb->reply_control_blk_length) + + xcrb->reply_data_length; + if ((CEIL4(xcrb->reply_control_blk_length) <= + xcrb->reply_data_length) ? + resp_sumlen < xcrb->reply_data_length : + resp_sumlen < CEIL4(xcrb->reply_control_blk_length)) { + 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 (z_copy_from_user(userspace, msg->userdata, + 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)); + + *fcode = (msg->hdr.function_code[0] << 8) | msg->hdr.function_code[1]; + *dom = (unsigned short *)&msg->cprbx.domain; + + /* check subfunction, US and AU need special flag with NQAP */ + if (memcmp(function_code, "US", 2) == 0 || + memcmp(function_code, "AU", 2) == 0) + ap_msg->flags |= AP_MSG_FLAG_SPECIAL; + + /* check CPRB minor version, set info bits in ap_message flag field */ + switch (*(unsigned short *)(&msg->cprbx.func_id[0])) { + case 0x5432: /* "T2" */ + ap_msg->flags |= AP_MSG_FLAG_USAGE; + break; + case 0x5433: /* "T3" */ + case 0x5435: /* "T5" */ + case 0x5436: /* "T6" */ + case 0x5437: /* "T7" */ + ap_msg->flags |= AP_MSG_FLAG_ADMIN; + break; + default: + ZCRYPT_DBF_DBG("%s unknown CPRB minor version '%c%c'\n", + __func__, msg->cprbx.func_id[0], + msg->cprbx.func_id[1]); + } + + /* copy data block */ + if (xcrb->request_data_length && + z_copy_from_user(userspace, req_data, xcrb->request_data_address, + xcrb->request_data_length)) + return -EFAULT; + + return 0; +} + +static int xcrb_msg_to_type6_ep11cprb_msgx(bool userspace, struct ap_message *ap_msg, + struct ep11_urb *xcrb, + unsigned int *fcode, + unsigned int *domain) +{ + unsigned int lfmt; + static struct type6_hdr static_type6_ep11_hdr = { + .type = 0x06, + .rqid = {0x00, 0x01}, + .function_code = {0x00, 0x00}, + .agent_id[0] = 0x58, /* {'X'} */ + .agent_id[1] = 0x43, /* {'C'} */ + .offset1 = 0x00000058, + }; + + struct { + struct type6_hdr hdr; + union { + struct { + struct ep11_cprb cprbx; + unsigned char pld_tag; /* fixed value 0x30 */ + unsigned char pld_lenfmt; /* length format */ + } __packed; + DECLARE_FLEX_ARRAY(u8, userdata); + }; + } __packed * msg = ap_msg->msg; + + struct pld_hdr { + unsigned char func_tag; /* fixed value 0x4 */ + unsigned char func_len; /* fixed value 0x4 */ + unsigned int func_val; /* function ID */ + unsigned char dom_tag; /* fixed value 0x4 */ + unsigned char dom_len; /* fixed value 0x4 */ + unsigned int dom_val; /* domain id */ + } __packed * payload_hdr = NULL; + + if (CEIL4(xcrb->req_len) < xcrb->req_len) + return -EINVAL; /* overflow after alignment*/ + + /* length checks */ + ap_msg->len = sizeof(struct type6_hdr) + CEIL4(xcrb->req_len); + if (ap_msg->len > ap_msg->bufsize) + return -EINVAL; + + if (CEIL4(xcrb->resp_len) < xcrb->resp_len) + return -EINVAL; /* overflow after alignment*/ + + /* prepare type6 header */ + msg->hdr = static_type6_ep11_hdr; + msg->hdr.tocardlen1 = xcrb->req_len; + msg->hdr.fromcardlen1 = xcrb->resp_len; + + /* Import CPRB data from the ioctl input parameter */ + if (z_copy_from_user(userspace, msg->userdata, + (char __force __user *)xcrb->req, xcrb->req_len)) { + return -EFAULT; + } + + if ((msg->pld_lenfmt & 0x80) == 0x80) { /*ext.len.fmt 2 or 3*/ + switch (msg->pld_lenfmt & 0x03) { + case 1: + lfmt = 2; + break; + case 2: + lfmt = 3; + break; + default: + return -EINVAL; + } + } else { + lfmt = 1; /* length format #1 */ + } + payload_hdr = (struct pld_hdr *)((&msg->pld_lenfmt) + lfmt); + *fcode = payload_hdr->func_val & 0xFFFF; + + /* enable special processing based on the cprbs flags special bit */ + if (msg->cprbx.flags & 0x20) + ap_msg->flags |= AP_MSG_FLAG_SPECIAL; + + /* set info bits in ap_message flag field */ + if (msg->cprbx.flags & 0x80) + ap_msg->flags |= AP_MSG_FLAG_ADMIN; + else + ap_msg->flags |= AP_MSG_FLAG_USAGE; + + *domain = msg->cprbx.target_id; + + return 0; +} + +/* + * Copy results from a type 86 ICA reply message back to user space. + * + * @zq: 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; /* length of data including length field size */ + char data[]; +} __packed; + +struct type86_ep11_reply { + struct type86_hdr hdr; + struct type86_fmt2_ext fmt2; + struct ep11_cprb cprbx; +} __packed; + +static int convert_type86_ica(struct zcrypt_queue *zq, + struct ap_message *reply, + char __user *outputdata, + unsigned int outputdatalength) +{ + struct type86x_reply *msg = reply->msg; + unsigned short service_rc, service_rs; + unsigned int data_len; + + service_rc = msg->cprbx.ccp_rtcode; + if (unlikely(service_rc != 0)) { + service_rs = msg->cprbx.ccp_rscode; + if ((service_rc == 8 && service_rs == 66) || + (service_rc == 8 && service_rs == 65) || + (service_rc == 8 && service_rs == 72) || + (service_rc == 8 && service_rs == 770) || + (service_rc == 12 && service_rs == 769)) { + ZCRYPT_DBF_WARN("%s dev=%02x.%04x rc/rs=%d/%d => rc=EINVAL\n", + __func__, AP_QID_CARD(zq->queue->qid), + AP_QID_QUEUE(zq->queue->qid), + (int)service_rc, (int)service_rs); + return -EINVAL; + } + zq->online = 0; + pr_err("Crypto dev=%02x.%04x rc/rs=%d/%d online=0 rc=EAGAIN\n", + AP_QID_CARD(zq->queue->qid), + AP_QID_QUEUE(zq->queue->qid), + (int)service_rc, (int)service_rs); + ZCRYPT_DBF_ERR("%s dev=%02x.%04x rc/rs=%d/%d => online=0 rc=EAGAIN\n", + __func__, AP_QID_CARD(zq->queue->qid), + AP_QID_QUEUE(zq->queue->qid), + (int)service_rc, (int)service_rs); + ap_send_online_uevent(&zq->queue->ap_dev, zq->online); + return -EAGAIN; + } + data_len = msg->length - sizeof(msg->length); + if (data_len > outputdatalength) + return -EMSGSIZE; + + /* Copy the crypto response to user space. */ + if (copy_to_user(outputdata, msg->data, data_len)) + return -EFAULT; + return 0; +} + +/* + * Copy results from a type 86 XCRB reply message back to user space. + * + * @zq: 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(bool userspace, struct zcrypt_queue *zq, + struct ap_message *reply, + struct ica_xcRB *xcrb) +{ + struct type86_fmt2_msg *msg = reply->msg; + char *data = reply->msg; + + /* Copy CPRB to user */ + if (xcrb->reply_control_blk_length < msg->fmt2.count1) { + ZCRYPT_DBF_DBG("%s reply_control_blk_length %u < required %u => EMSGSIZE\n", + __func__, xcrb->reply_control_blk_length, + msg->fmt2.count1); + return -EMSGSIZE; + } + if (z_copy_to_user(userspace, 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 (xcrb->reply_data_length < msg->fmt2.count2) { + ZCRYPT_DBF_DBG("%s reply_data_length %u < required %u => EMSGSIZE\n", + __func__, xcrb->reply_data_length, + msg->fmt2.count2); + return -EMSGSIZE; + } + if (z_copy_to_user(userspace, xcrb->reply_data_addr, + data + msg->fmt2.offset2, msg->fmt2.count2)) + return -EFAULT; + } + xcrb->reply_data_length = msg->fmt2.count2; + + return 0; +} + +/* + * Copy results from a type 86 EP11 XCRB reply message back to user space. + * + * @zq: crypto device pointer + * @reply: reply AP message. + * @xcrb: pointer to EP11 user request block + * + * Returns 0 on success or -EINVAL, -EFAULT, -EAGAIN in case of an error. + */ +static int convert_type86_ep11_xcrb(bool userspace, struct zcrypt_queue *zq, + struct ap_message *reply, + struct ep11_urb *xcrb) +{ + struct type86_fmt2_msg *msg = reply->msg; + char *data = reply->msg; + + if (xcrb->resp_len < msg->fmt2.count1) { + ZCRYPT_DBF_DBG("%s resp_len %u < required %u => EMSGSIZE\n", + __func__, (unsigned int)xcrb->resp_len, + msg->fmt2.count1); + return -EMSGSIZE; + } + + /* Copy response CPRB to user */ + if (z_copy_to_user(userspace, (char __force __user *)xcrb->resp, + data + msg->fmt2.offset1, msg->fmt2.count1)) + return -EFAULT; + xcrb->resp_len = msg->fmt2.count1; + return 0; +} + +static int convert_type86_rng(struct zcrypt_queue *zq, + struct ap_message *reply, + char *buffer) +{ + struct { + struct type86_hdr hdr; + struct type86_fmt2_ext fmt2; + struct CPRBX cprbx; + } __packed * msg = reply->msg; + char *data = reply->msg; + + 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_queue *zq, + struct ap_message *reply, + char __user *outputdata, + unsigned int outputdatalength) +{ + struct type86x_reply *msg = reply->msg; + + switch (msg->hdr.type) { + case TYPE82_RSP_CODE: + case TYPE88_RSP_CODE: + return convert_error(zq, reply); + case TYPE86_RSP_CODE: + if (msg->cprbx.ccp_rtcode && + msg->cprbx.ccp_rscode == 0x14f && + outputdatalength > 256) { + if (zq->zcard->max_exp_bit_length <= 17) { + zq->zcard->max_exp_bit_length = 17; + return -EAGAIN; + } else { + return -EINVAL; + } + } + if (msg->hdr.reply_code) + return convert_error(zq, reply); + if (msg->cprbx.cprb_ver_id == 0x02) + return convert_type86_ica(zq, reply, + outputdata, outputdatalength); + fallthrough; /* wrong cprb version is an unknown response */ + default: + /* Unknown response type, this should NEVER EVER happen */ + zq->online = 0; + pr_err("Crypto dev=%02x.%04x unknown response type 0x%02x => online=0 rc=EAGAIN\n", + AP_QID_CARD(zq->queue->qid), + AP_QID_QUEUE(zq->queue->qid), + (int)msg->hdr.type); + ZCRYPT_DBF_ERR( + "%s dev=%02x.%04x unknown response type 0x%02x => online=0 rc=EAGAIN\n", + __func__, AP_QID_CARD(zq->queue->qid), + AP_QID_QUEUE(zq->queue->qid), (int)msg->hdr.type); + ap_send_online_uevent(&zq->queue->ap_dev, zq->online); + return -EAGAIN; + } +} + +static int convert_response_xcrb(bool userspace, struct zcrypt_queue *zq, + struct ap_message *reply, + struct ica_xcRB *xcrb) +{ + struct type86x_reply *msg = reply->msg; + + switch (msg->hdr.type) { + case TYPE82_RSP_CODE: + case TYPE88_RSP_CODE: + xcrb->status = 0x0008044DL; /* HDD_InvalidParm */ + return convert_error(zq, reply); + case TYPE86_RSP_CODE: + if (msg->hdr.reply_code) { + memcpy(&xcrb->status, msg->fmt2.apfs, sizeof(u32)); + return convert_error(zq, reply); + } + if (msg->cprbx.cprb_ver_id == 0x02) + return convert_type86_xcrb(userspace, zq, reply, xcrb); + fallthrough; /* wrong cprb version is an unknown response */ + default: /* Unknown response type, this should NEVER EVER happen */ + xcrb->status = 0x0008044DL; /* HDD_InvalidParm */ + zq->online = 0; + pr_err("Crypto dev=%02x.%04x unknown response type 0x%02x => online=0 rc=EAGAIN\n", + AP_QID_CARD(zq->queue->qid), + AP_QID_QUEUE(zq->queue->qid), + (int)msg->hdr.type); + ZCRYPT_DBF_ERR( + "%s dev=%02x.%04x unknown response type 0x%02x => online=0 rc=EAGAIN\n", + __func__, AP_QID_CARD(zq->queue->qid), + AP_QID_QUEUE(zq->queue->qid), (int)msg->hdr.type); + ap_send_online_uevent(&zq->queue->ap_dev, zq->online); + return -EAGAIN; + } +} + +static int convert_response_ep11_xcrb(bool userspace, struct zcrypt_queue *zq, + struct ap_message *reply, struct ep11_urb *xcrb) +{ + struct type86_ep11_reply *msg = reply->msg; + + switch (msg->hdr.type) { + case TYPE82_RSP_CODE: + case TYPE87_RSP_CODE: + return convert_error(zq, reply); + case TYPE86_RSP_CODE: + if (msg->hdr.reply_code) + return convert_error(zq, reply); + if (msg->cprbx.cprb_ver_id == 0x04) + return convert_type86_ep11_xcrb(userspace, zq, reply, xcrb); + fallthrough; /* wrong cprb version is an unknown resp */ + default: /* Unknown response type, this should NEVER EVER happen */ + zq->online = 0; + pr_err("Crypto dev=%02x.%04x unknown response type 0x%02x => online=0 rc=EAGAIN\n", + AP_QID_CARD(zq->queue->qid), + AP_QID_QUEUE(zq->queue->qid), + (int)msg->hdr.type); + ZCRYPT_DBF_ERR( + "%s dev=%02x.%04x unknown response type 0x%02x => online=0 rc=EAGAIN\n", + __func__, AP_QID_CARD(zq->queue->qid), + AP_QID_QUEUE(zq->queue->qid), (int)msg->hdr.type); + ap_send_online_uevent(&zq->queue->ap_dev, zq->online); + return -EAGAIN; + } +} + +static int convert_response_rng(struct zcrypt_queue *zq, + struct ap_message *reply, + char *data) +{ + struct type86x_reply *msg = reply->msg; + + 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(zq, reply, data); + fallthrough; /* wrong cprb version is an unknown response */ + default: /* Unknown response type, this should NEVER EVER happen */ + zq->online = 0; + pr_err("Crypto dev=%02x.%04x unknown response type 0x%02x => online=0 rc=EAGAIN\n", + AP_QID_CARD(zq->queue->qid), + AP_QID_QUEUE(zq->queue->qid), + (int)msg->hdr.type); + ZCRYPT_DBF_ERR( + "%s dev=%02x.%04x unknown response type 0x%02x => online=0 rc=EAGAIN\n", + __func__, AP_QID_CARD(zq->queue->qid), + AP_QID_QUEUE(zq->queue->qid), (int)msg->hdr.type); + ap_send_online_uevent(&zq->queue->ap_dev, zq->online); + return -EAGAIN; + } +} + +/* + * 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. + * @aq: pointer to the AP queue + * @msg: pointer to the AP message + * @reply: pointer to the AP reply message + */ +static void zcrypt_msgtype6_receive(struct ap_queue *aq, + 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 = msg->private; + struct type86x_reply *t86r; + int len; + + /* Copy the reply message to the request message buffer. */ + if (!reply) + goto out; /* ap_msg->rc indicates the error */ + t86r = reply->msg; + if (t86r->hdr.type == TYPE86_RSP_CODE && + t86r->cprbx.cprb_ver_id == 0x02) { + switch (resp_type->type) { + case CEXXC_RESPONSE_TYPE_ICA: + len = sizeof(struct type86x_reply) + t86r->length; + if (len > reply->bufsize || len > msg->bufsize || + len != reply->len) { + ZCRYPT_DBF_DBG("%s len mismatch => EMSGSIZE\n", __func__); + msg->rc = -EMSGSIZE; + goto out; + } + memcpy(msg->msg, reply->msg, len); + msg->len = len; + break; + case CEXXC_RESPONSE_TYPE_XCRB: + if (t86r->fmt2.count2) + len = t86r->fmt2.offset2 + t86r->fmt2.count2; + else + len = t86r->fmt2.offset1 + t86r->fmt2.count1; + if (len > reply->bufsize || len > msg->bufsize || + len != reply->len) { + ZCRYPT_DBF_DBG("%s len mismatch => EMSGSIZE\n", __func__); + msg->rc = -EMSGSIZE; + goto out; + } + memcpy(msg->msg, reply->msg, len); + msg->len = len; + break; + default: + memcpy(msg->msg, &error_reply, sizeof(error_reply)); + msg->len = sizeof(error_reply); + } + } else { + memcpy(msg->msg, reply->msg, sizeof(error_reply)); + msg->len = sizeof(error_reply); + } +out: + complete(&resp_type->work); +} + +/* + * 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. + * @aq: pointer to the AP queue + * @msg: pointer to the AP message + * @reply: pointer to the AP reply message + */ +static void zcrypt_msgtype6_receive_ep11(struct ap_queue *aq, + 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 = msg->private; + struct type86_ep11_reply *t86r; + int len; + + /* Copy the reply message to the request message buffer. */ + if (!reply) + goto out; /* ap_msg->rc indicates the error */ + t86r = reply->msg; + if (t86r->hdr.type == TYPE86_RSP_CODE && + t86r->cprbx.cprb_ver_id == 0x04) { + switch (resp_type->type) { + case CEXXC_RESPONSE_TYPE_EP11: + len = t86r->fmt2.offset1 + t86r->fmt2.count1; + if (len > reply->bufsize || len > msg->bufsize || + len != reply->len) { + ZCRYPT_DBF_DBG("%s len mismatch => EMSGSIZE\n", __func__); + msg->rc = -EMSGSIZE; + goto out; + } + memcpy(msg->msg, reply->msg, len); + msg->len = len; + break; + default: + memcpy(msg->msg, &error_reply, sizeof(error_reply)); + msg->len = sizeof(error_reply); + } + } else { + memcpy(msg->msg, reply->msg, sizeof(error_reply)); + msg->len = 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 CEXxC + * device to handle a modexpo request. + * @zq: pointer to zcrypt_queue structure that identifies the + * CEXxC device to the request distributor + * @mex: pointer to the modexpo request buffer + */ +static long zcrypt_msgtype6_modexpo(struct zcrypt_queue *zq, + struct ica_rsa_modexpo *mex, + struct ap_message *ap_msg) +{ + struct response_type resp_type = { + .type = CEXXC_RESPONSE_TYPE_ICA, + }; + int rc; + + ap_msg->msg = (void *)get_zeroed_page(GFP_KERNEL); + if (!ap_msg->msg) + return -ENOMEM; + ap_msg->bufsize = PAGE_SIZE; + ap_msg->receive = zcrypt_msgtype6_receive; + ap_msg->psmid = (((unsigned long)current->pid) << 32) + + atomic_inc_return(&zcrypt_step); + ap_msg->private = &resp_type; + rc = icamex_msg_to_type6mex_msgx(zq, ap_msg, mex); + if (rc) + goto out_free; + init_completion(&resp_type.work); + rc = ap_queue_message(zq->queue, ap_msg); + if (rc) + goto out_free; + rc = wait_for_completion_interruptible(&resp_type.work); + if (rc == 0) { + rc = ap_msg->rc; + if (rc == 0) + rc = convert_response_ica(zq, ap_msg, + mex->outputdata, + mex->outputdatalength); + } else { + /* Signal pending. */ + ap_cancel_message(zq->queue, ap_msg); + } + +out_free: + free_page((unsigned long)ap_msg->msg); + ap_msg->private = NULL; + ap_msg->msg = NULL; + return rc; +} + +/* + * The request distributor calls this function if it picked the CEXxC + * device to handle a modexpo_crt request. + * @zq: pointer to zcrypt_queue structure that identifies the + * CEXxC device to the request distributor + * @crt: pointer to the modexpoc_crt request buffer + */ +static long zcrypt_msgtype6_modexpo_crt(struct zcrypt_queue *zq, + struct ica_rsa_modexpo_crt *crt, + struct ap_message *ap_msg) +{ + struct response_type resp_type = { + .type = CEXXC_RESPONSE_TYPE_ICA, + }; + int rc; + + ap_msg->msg = (void *)get_zeroed_page(GFP_KERNEL); + if (!ap_msg->msg) + return -ENOMEM; + ap_msg->bufsize = PAGE_SIZE; + ap_msg->receive = zcrypt_msgtype6_receive; + ap_msg->psmid = (((unsigned long)current->pid) << 32) + + atomic_inc_return(&zcrypt_step); + ap_msg->private = &resp_type; + rc = icacrt_msg_to_type6crt_msgx(zq, ap_msg, crt); + if (rc) + goto out_free; + init_completion(&resp_type.work); + rc = ap_queue_message(zq->queue, ap_msg); + if (rc) + goto out_free; + rc = wait_for_completion_interruptible(&resp_type.work); + if (rc == 0) { + rc = ap_msg->rc; + if (rc == 0) + rc = convert_response_ica(zq, ap_msg, + crt->outputdata, + crt->outputdatalength); + } else { + /* Signal pending. */ + ap_cancel_message(zq->queue, ap_msg); + } + +out_free: + free_page((unsigned long)ap_msg->msg); + ap_msg->private = NULL; + ap_msg->msg = NULL; + return rc; +} + +/* + * Prepare a CCA AP msg request. + * Prepare a CCA AP msg: fetch the required data from userspace, + * prepare the AP msg, fill some info into the ap_message struct, + * extract some data from the CPRB and give back to the caller. + * This function allocates memory and needs an ap_msg prepared + * by the caller with ap_init_message(). Also the caller has to + * make sure ap_release_message() is always called even on failure. + */ +int prep_cca_ap_msg(bool userspace, struct ica_xcRB *xcrb, + struct ap_message *ap_msg, + unsigned int *func_code, unsigned short **dom) +{ + struct response_type resp_type = { + .type = CEXXC_RESPONSE_TYPE_XCRB, + }; + + ap_msg->bufsize = atomic_read(&ap_max_msg_size); + ap_msg->msg = kmalloc(ap_msg->bufsize, GFP_KERNEL); + if (!ap_msg->msg) + return -ENOMEM; + ap_msg->receive = zcrypt_msgtype6_receive; + ap_msg->psmid = (((unsigned long)current->pid) << 32) + + atomic_inc_return(&zcrypt_step); + ap_msg->private = kmemdup(&resp_type, sizeof(resp_type), GFP_KERNEL); + if (!ap_msg->private) + return -ENOMEM; + return xcrb_msg_to_type6cprb_msgx(userspace, ap_msg, xcrb, func_code, dom); +} + +/* + * The request distributor calls this function if it picked the CEXxC + * device to handle a send_cprb request. + * @zq: pointer to zcrypt_queue structure that identifies the + * CEXxC device to the request distributor + * @xcrb: pointer to the send_cprb request buffer + */ +static long zcrypt_msgtype6_send_cprb(bool userspace, struct zcrypt_queue *zq, + struct ica_xcRB *xcrb, + struct ap_message *ap_msg) +{ + struct response_type *rtype = ap_msg->private; + struct { + struct type6_hdr hdr; + struct CPRBX cprbx; + /* ... more data blocks ... */ + } __packed * msg = ap_msg->msg; + unsigned int max_payload_size; + int rc, delta; + + /* calculate maximum payload for this card and msg type */ + max_payload_size = zq->reply.bufsize - sizeof(struct type86_fmt2_msg); + + /* limit each of the two from fields to the maximum payload size */ + msg->hdr.fromcardlen1 = min(msg->hdr.fromcardlen1, max_payload_size); + msg->hdr.fromcardlen2 = min(msg->hdr.fromcardlen2, max_payload_size); + + /* calculate delta if the sum of both exceeds max payload size */ + delta = msg->hdr.fromcardlen1 + msg->hdr.fromcardlen2 + - max_payload_size; + if (delta > 0) { + /* + * Sum exceeds maximum payload size, prune fromcardlen1 + * (always trust fromcardlen2) + */ + if (delta > msg->hdr.fromcardlen1) { + rc = -EINVAL; + goto out; + } + msg->hdr.fromcardlen1 -= delta; + } + + init_completion(&rtype->work); + rc = ap_queue_message(zq->queue, ap_msg); + if (rc) + goto out; + rc = wait_for_completion_interruptible(&rtype->work); + if (rc == 0) { + rc = ap_msg->rc; + if (rc == 0) + rc = convert_response_xcrb(userspace, zq, ap_msg, xcrb); + } else { + /* Signal pending. */ + ap_cancel_message(zq->queue, ap_msg); + } + + if (rc == -EAGAIN && ap_msg->flags & AP_MSG_FLAG_ADMIN) + rc = -EIO; /* do not retry administrative requests */ + +out: + if (rc) + ZCRYPT_DBF_DBG("%s send cprb at dev=%02x.%04x rc=%d\n", + __func__, AP_QID_CARD(zq->queue->qid), + AP_QID_QUEUE(zq->queue->qid), rc); + return rc; +} + +/* + * Prepare an EP11 AP msg request. + * Prepare an EP11 AP msg: fetch the required data from userspace, + * prepare the AP msg, fill some info into the ap_message struct, + * extract some data from the CPRB and give back to the caller. + * This function allocates memory and needs an ap_msg prepared + * by the caller with ap_init_message(). Also the caller has to + * make sure ap_release_message() is always called even on failure. + */ +int prep_ep11_ap_msg(bool userspace, struct ep11_urb *xcrb, + struct ap_message *ap_msg, + unsigned int *func_code, unsigned int *domain) +{ + struct response_type resp_type = { + .type = CEXXC_RESPONSE_TYPE_EP11, + }; + + ap_msg->bufsize = atomic_read(&ap_max_msg_size); + ap_msg->msg = kmalloc(ap_msg->bufsize, GFP_KERNEL); + if (!ap_msg->msg) + return -ENOMEM; + ap_msg->receive = zcrypt_msgtype6_receive_ep11; + ap_msg->psmid = (((unsigned long)current->pid) << 32) + + atomic_inc_return(&zcrypt_step); + ap_msg->private = kmemdup(&resp_type, sizeof(resp_type), GFP_KERNEL); + if (!ap_msg->private) + return -ENOMEM; + return xcrb_msg_to_type6_ep11cprb_msgx(userspace, ap_msg, xcrb, + func_code, domain); +} + +/* + * The request distributor calls this function if it picked the CEX4P + * device to handle a send_ep11_cprb request. + * @zq: pointer to zcrypt_queue structure that identifies the + * CEX4P device to the request distributor + * @xcrb: pointer to the ep11 user request block + */ +static long zcrypt_msgtype6_send_ep11_cprb(bool userspace, struct zcrypt_queue *zq, + struct ep11_urb *xcrb, + struct ap_message *ap_msg) +{ + int rc; + unsigned int lfmt; + struct response_type *rtype = ap_msg->private; + struct { + struct type6_hdr hdr; + struct ep11_cprb cprbx; + unsigned char pld_tag; /* fixed value 0x30 */ + unsigned char pld_lenfmt; /* payload length format */ + } __packed * msg = ap_msg->msg; + struct pld_hdr { + unsigned char func_tag; /* fixed value 0x4 */ + unsigned char func_len; /* fixed value 0x4 */ + unsigned int func_val; /* function ID */ + unsigned char dom_tag; /* fixed value 0x4 */ + unsigned char dom_len; /* fixed value 0x4 */ + unsigned int dom_val; /* domain id */ + } __packed * payload_hdr = NULL; + + /* + * The target domain field within the cprb body/payload block will be + * replaced by the usage domain for non-management commands only. + * Therefore we check the first bit of the 'flags' parameter for + * management command indication. + * 0 - non management command + * 1 - management command + */ + if (!((msg->cprbx.flags & 0x80) == 0x80)) { + msg->cprbx.target_id = (unsigned int) + AP_QID_QUEUE(zq->queue->qid); + + if ((msg->pld_lenfmt & 0x80) == 0x80) { /*ext.len.fmt 2 or 3*/ + switch (msg->pld_lenfmt & 0x03) { + case 1: + lfmt = 2; + break; + case 2: + lfmt = 3; + break; + default: + return -EINVAL; + } + } else { + lfmt = 1; /* length format #1 */ + } + payload_hdr = (struct pld_hdr *)((&msg->pld_lenfmt) + lfmt); + payload_hdr->dom_val = (unsigned int) + AP_QID_QUEUE(zq->queue->qid); + } + + /* + * Set the queue's reply buffer length minus the two prepend headers + * as reply limit for the card firmware. + */ + msg->hdr.fromcardlen1 = zq->reply.bufsize - + sizeof(struct type86_hdr) - sizeof(struct type86_fmt2_ext); + + init_completion(&rtype->work); + rc = ap_queue_message(zq->queue, ap_msg); + if (rc) + goto out; + rc = wait_for_completion_interruptible(&rtype->work); + if (rc == 0) { + rc = ap_msg->rc; + if (rc == 0) + rc = convert_response_ep11_xcrb(userspace, zq, ap_msg, xcrb); + } else { + /* Signal pending. */ + ap_cancel_message(zq->queue, ap_msg); + } + + if (rc == -EAGAIN && ap_msg->flags & AP_MSG_FLAG_ADMIN) + rc = -EIO; /* do not retry administrative requests */ + +out: + if (rc) + ZCRYPT_DBF_DBG("%s send cprb at dev=%02x.%04x rc=%d\n", + __func__, AP_QID_CARD(zq->queue->qid), + AP_QID_QUEUE(zq->queue->qid), rc); + return rc; +} + +int prep_rng_ap_msg(struct ap_message *ap_msg, int *func_code, + unsigned int *domain) +{ + struct response_type resp_type = { + .type = CEXXC_RESPONSE_TYPE_XCRB, + }; + + ap_msg->bufsize = AP_DEFAULT_MAX_MSG_SIZE; + ap_msg->msg = kmalloc(ap_msg->bufsize, GFP_KERNEL); + if (!ap_msg->msg) + return -ENOMEM; + ap_msg->receive = zcrypt_msgtype6_receive; + ap_msg->psmid = (((unsigned long)current->pid) << 32) + + atomic_inc_return(&zcrypt_step); + ap_msg->private = kmemdup(&resp_type, sizeof(resp_type), GFP_KERNEL); + if (!ap_msg->private) + return -ENOMEM; + + rng_type6cprb_msgx(ap_msg, ZCRYPT_RNG_BUFFER_SIZE, domain); + + *func_code = HWRNG; + return 0; +} + +/* + * The request distributor calls this function if it picked the CEXxC + * device to generate random data. + * @zq: pointer to zcrypt_queue structure that identifies the + * CEXxC device to the request distributor + * @buffer: pointer to a memory page to return random data + */ +static long zcrypt_msgtype6_rng(struct zcrypt_queue *zq, + char *buffer, struct ap_message *ap_msg) +{ + 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->msg; + struct response_type *rtype = ap_msg->private; + int rc; + + msg->cprbx.domain = AP_QID_QUEUE(zq->queue->qid); + + init_completion(&rtype->work); + rc = ap_queue_message(zq->queue, ap_msg); + if (rc) + goto out; + rc = wait_for_completion_interruptible(&rtype->work); + if (rc == 0) { + rc = ap_msg->rc; + if (rc == 0) + rc = convert_response_rng(zq, ap_msg, buffer); + } else { + /* Signal pending. */ + ap_cancel_message(zq->queue, ap_msg); + } +out: + return rc; +} + +/* + * The crypto operations for a CEXxC card. + */ + +static struct zcrypt_ops zcrypt_msgtype6_ops = { + .owner = THIS_MODULE, + .name = MSGTYPE06_NAME, + .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, +}; + +static struct zcrypt_ops zcrypt_msgtype6_ep11_ops = { + .owner = THIS_MODULE, + .name = MSGTYPE06_NAME, + .variant = MSGTYPE06_VARIANT_EP11, + .rsa_modexpo = NULL, + .rsa_modexpo_crt = NULL, + .send_ep11_cprb = zcrypt_msgtype6_send_ep11_cprb, +}; + +void __init zcrypt_msgtype6_init(void) +{ + zcrypt_msgtype_register(&zcrypt_msgtype6_ops); + zcrypt_msgtype_register(&zcrypt_msgtype6_ep11_ops); +} + +void __exit zcrypt_msgtype6_exit(void) +{ + zcrypt_msgtype_unregister(&zcrypt_msgtype6_ops); + zcrypt_msgtype_unregister(&zcrypt_msgtype6_ep11_ops); +} |