/*
- * linux/drivers/s390/misc/z90hardware.c
+ * linux/drivers/s390/crypto/z90hardware.c
*
- * z90crypt 1.3.1
+ * z90crypt 1.3.2
*
* Copyright (C) 2001, 2004 IBM Corporation
* Author(s): Robert Burroughs (burrough@us.ibm.com)
- * Eric Rossman (edrossma@us.ibm.com)
+ * Eric Rossman (edrossma@us.ibm.com)
*
* Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
*
*
* 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
+ * 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
#include "z90crypt.h"
#include "z90common.h"
-#define VERSION_Z90HARDWARE_C "$Revision: 1.19 $"
+#define VERSION_Z90HARDWARE_C "$Revision: 1.33 $"
-char z90chardware_version[] __initdata =
+char z90hardware_version[] __initdata =
"z90hardware.o (" VERSION_Z90HARDWARE_C "/"
VERSION_Z90COMMON_H "/" VERSION_Z90CRYPT_H ")";
unsigned char right[4];
unsigned char reserved3[2];
unsigned char reserved4[2];
- unsigned char pfs[4];
+ unsigned char apfs[4];
unsigned int offset1;
unsigned int offset2;
unsigned int offset3;
unsigned char svr_name[8];
};
-struct CPRBX {
- unsigned short cprb_len;
- unsigned char cprb_ver_id;
- unsigned char pad_000[3];
- unsigned char func_id[2];
- unsigned char cprb_flags[4];
- unsigned int req_parml;
- unsigned int req_datal;
- unsigned int rpl_msgbl;
- unsigned int rpld_parml;
- unsigned int rpl_datal;
- unsigned int rpld_datal;
- unsigned int req_extbl;
- unsigned char pad_001[4];
- unsigned int rpld_extbl;
- unsigned char req_parmb[16];
- unsigned char req_datab[16];
- unsigned char rpl_parmb[16];
- unsigned char rpl_datab[16];
- unsigned char req_extb[16];
- unsigned char rpl_extb[16];
- unsigned short ccp_rtcode;
- unsigned short ccp_rscode;
- unsigned int mac_data_len;
- unsigned char logon_id[8];
- unsigned char mac_value[8];
- unsigned char mac_content_flgs;
- unsigned char pad_002;
- unsigned short domain;
- unsigned char pad_003[12];
- unsigned char pad_004[36];
-};
-
struct type6_msg {
struct type6_hdr header;
struct CPRB CPRB;
#define REPLY_ERROR_FORMAT_FIELD 0x29
#define REPLY_ERROR_INVALID_COMMAND 0x30
#define REPLY_ERROR_MALFORMED_MSG 0x40
-#define REPLY_ERROR_RESERVED_FIELD 0x50
+#define REPLY_ERROR_RESERVED_FIELDO 0x50
#define REPLY_ERROR_WORD_ALIGNMENT 0x60
#define REPLY_ERROR_MESSAGE_LENGTH 0x80
#define REPLY_ERROR_OPERAND_INVALID 0x82
#define REPLY_ERROR_OPERAND_SIZE 0x84
#define REPLY_ERROR_EVEN_MOD_IN_OPND 0x85
+#define REPLY_ERROR_RESERVED_FIELD 0x88
#define REPLY_ERROR_TRANSPORT_FAIL 0x90
#define REPLY_ERROR_PACKET_TRUNCATED 0xA0
#define REPLY_ERROR_ZERO_BUFFER_LEN 0xB0
unsigned int offset2;
unsigned int count3;
unsigned int offset3;
- unsigned int ount4;
+ unsigned int count4;
unsigned int offset4;
};
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}
};
-static struct function_and_rules_block static_pkd_function_and_rulesX = {
+static struct function_and_rules_block static_pkd_function_and_rulesX_MCL2 = {
{0x50,0x44},
{0x00,0x0A},
{'P','K','C','S','-','1','.','2'}
};
-static struct function_and_rules_block static_pke_function_and_rulesX = {
+static struct function_and_rules_block static_pke_function_and_rulesX_MCL2 = {
{0x50,0x4B},
{0x00,0x0A},
{'Z','E','R','O','-','P','A','D'}
};
+static struct function_and_rules_block static_pkd_function_and_rulesX = {
+ {0x50,0x44},
+ {0x00,0x0A},
+ {'Z','E','R','O','-','P','A','D'}
+};
+
+static struct function_and_rules_block static_pke_function_and_rulesX = {
+ {0x50,0x4B},
+ {0x00,0x0A},
+ {'M','R','P',' ',' ',' ',' ',' '}
+};
+
struct T6_keyBlock_hdrX {
unsigned short blen;
unsigned short ulen;
#define FIXED_TYPE6_CR_LENX 0x000001E3
-#ifndef MAX_RESPONSE_SIZE
#define MAX_RESPONSE_SIZE 0x00000710
#define MAX_RESPONSEX_SIZE 0x0000077C
-#endif
#define RESPONSE_CPRB_SIZE 0x000006B8
#define RESPONSE_CPRBX_SIZE 0x00000724
*q_depth = t_depth + 1;
switch (t_dev_type) {
case OTHER_HW:
- case OTHER2_HW:
stat = HD_NOT_THERE;
*dev_type = NILDEV;
break;
*dev_type = PCICC;
break;
case PCIXCC_HW:
- *dev_type = PCIXCC;
+ *dev_type = PCIXCC_UNK;
+ break;
+ case CEX2C_HW:
+ *dev_type = CEX2C;
break;
default:
*dev_type = NILDEV;
default:
stat = HD_NOT_THERE;
break_out = 1;
+ break;
}
if (break_out)
break;
switch (stat_word.response_code) {
case AP_RESPONSE_NORMAL:
stat = DEV_ONLINE;
- if (stat_word.q_stat_flags &
- AP_Q_STATUS_EMPTY)
+ if (stat_word.q_stat_flags & AP_Q_STATUS_EMPTY)
break_out = 1;
break;
case AP_RESPONSE_Q_NOT_AVAIL:
- stat = DEV_GONE;
- break_out = 1;
- break;
case AP_RESPONSE_DECONFIGURED:
- stat = DEV_GONE;
- break_out = 1;
- break;
case AP_RESPONSE_CHECKSTOPPED:
stat = DEV_GONE;
break_out = 1;
default:
stat = DEV_GONE;
break_out = 1;
+ break;
}
if (break_out == 1)
break;
msg_ext[0], msg_ext[1], msg_ext[2], msg_ext[3],
msg_ext[4], msg_ext[5], msg_ext[6], msg_ext[7],
msg_ext[8], msg_ext[9], msg_ext[10], msg_ext[11]);
- print_buffer(msg_ext+12, msg_len);
+ print_buffer(msg_ext+CALLER_HEADER, msg_len);
#endif
ccode = sen(msg_len, msg_ext, &stat_word);
break;
default:
stat = DEV_GONE;
+ break;
}
return stat;
}
enum devstat
-receive_from_AP(int dev_nr, int cdx, int resplen,
- unsigned char *resp, unsigned char *psmid)
+receive_from_AP(int dev_nr, int cdx, int resplen, unsigned char *resp,
+ unsigned char *psmid)
{
int ccode;
struct ap_status_word stat_word;
struct type6_hdr *tp6Hdr_p;
struct CPRB *cprb_p;
struct cca_private_ext_ME *key_p;
+ static int deprecated_msg_count = 0;
mod_len = icaMsg_p->inputdatalength;
tmp_size = FIXED_TYPE6_ME_LEN + mod_len;
return SEN_USER_ERROR;
if (is_common_public_key(temp, mod_len)) {
- PRINTK("Common public key used for modex decrypt\n");
+ if (deprecated_msg_count < 20) {
+ PRINTK("Common public key used for modex decrypt\n");
+ deprecated_msg_count++;
+ if (deprecated_msg_count == 20)
+ PRINTK("No longer issuing messages about common"
+ " public key for modex decrypt.\n");
+ }
return SEN_NOT_AVAIL;
}
temp = key_p->pvtMESec.modulus + sizeof(key_p->pvtMESec.modulus)
- mod_len;
- if (copy_from_user(temp, icaMsg_p->n_modulus, mod_len) != 0)
+ if (copy_from_user(temp, icaMsg_p->n_modulus, mod_len))
return SEN_RELEASED;
if (is_empty(temp, mod_len))
return SEN_USER_ERROR;
{
int mod_len, vud_len, exp_len, key_len;
int pad_len, tmp_size, total_CPRB_len, parmBlock_l, i;
- unsigned char temp_exp[256], *exp_p, *temp;
+ unsigned char *temp_exp, *exp_p, *temp;
struct type6_hdr *tp6Hdr_p;
struct CPRB *cprb_p;
struct cca_public_key *key_p;
struct T6_keyBlock_hdr *keyb_p;
+ temp_exp = kmalloc(256, GFP_KERNEL);
+ if (!temp_exp)
+ return EGETBUFF;
mod_len = icaMsg_p->inputdatalength;
- if (copy_from_user(temp_exp, icaMsg_p->b_key, mod_len))
+ if (copy_from_user(temp_exp, icaMsg_p->b_key, mod_len)) {
+ kfree(temp_exp);
return SEN_RELEASED;
- if (is_empty(temp_exp, mod_len))
+ }
+ if (is_empty(temp_exp, mod_len)) {
+ kfree(temp_exp);
return SEN_USER_ERROR;
+ }
exp_p = temp_exp;
for (i = 0; i < mod_len; i++)
if (exp_p[i])
break;
- if (i >= mod_len)
+ if (i >= mod_len) {
+ kfree(temp_exp);
return SEN_USER_ERROR;
+ }
exp_len = mod_len - i;
exp_p += i;
sizeof(struct function_and_rules_block));
temp += sizeof(struct function_and_rules_block);
temp += 2;
- if (copy_from_user(temp, icaMsg_p->inputdata, mod_len))
+ if (copy_from_user(temp, icaMsg_p->inputdata, mod_len)) {
+ kfree(temp_exp);
return SEN_RELEASED;
- if (is_empty(temp, mod_len))
+ }
+ if (is_empty(temp, mod_len)) {
+ kfree(temp_exp);
return SEN_USER_ERROR;
- if (temp[0] != 0x00 || temp[1] != 0x02)
+ }
+ if ((temp[0] != 0x00) || (temp[1] != 0x02)) {
+ kfree(temp_exp);
return SEN_NOT_AVAIL;
+ }
for (i = 2; i < mod_len; i++)
if (temp[i] == 0x00)
break;
- if ((i < 9) || (i > (mod_len - 2)))
+ if ((i < 9) || (i > (mod_len - 2))) {
+ kfree(temp_exp);
return SEN_NOT_AVAIL;
+ }
pad_len = i + 1;
vud_len = mod_len - pad_len;
memmove(temp, temp+pad_len, vud_len);
key_p = (struct cca_public_key *)temp;
temp = key_p->pubSec.exponent;
memcpy(temp, exp_p, exp_len);
+ kfree(temp_exp);
temp += exp_len;
if (copy_from_user(temp, icaMsg_p->n_modulus, mod_len))
return SEN_RELEASED;
key_p->pubSec.modulus_bit_len = 8 * mod_len;
key_p->pubSec.modulus_byte_len = mod_len;
key_p->pubSec.exponent_len = exp_len;
- key_p->pubSec.section_length = 12 + mod_len + exp_len;
+ key_p->pubSec.section_length = CALLER_HEADER + mod_len + exp_len;
key_len = key_p->pubSec.section_length + sizeof(struct cca_token_hdr);
key_p->pubHdr.token_length = key_len;
key_len += 4;
static int
ICAMEX_msg_to_type6MEX_msgX(struct ica_rsa_modexpo *icaMsg_p, int cdx,
- int *z90cMsg_l_p, struct type6_msg *z90cMsg_p)
+ int *z90cMsg_l_p, struct type6_msg *z90cMsg_p,
+ int dev_type)
{
int mod_len, exp_len, vud_len, tmp_size, total_CPRB_len, parmBlock_l;
int key_len, i;
- unsigned char temp_exp[256], *tgt_p, *temp, *exp_p;
+ unsigned char *temp_exp, *tgt_p, *temp, *exp_p;
struct type6_hdr *tp6Hdr_p;
struct CPRBX *cprbx_p;
struct cca_public_key *key_p;
struct T6_keyBlock_hdrX *keyb_p;
+ temp_exp = kmalloc(256, GFP_KERNEL);
+ if (!temp_exp)
+ return EGETBUFF;
mod_len = icaMsg_p->inputdatalength;
- if (copy_from_user(temp_exp, icaMsg_p->b_key, mod_len))
+ if (copy_from_user(temp_exp, icaMsg_p->b_key, mod_len)) {
+ kfree(temp_exp);
return SEN_RELEASED;
- if (is_empty(temp_exp, mod_len))
+ }
+ if (is_empty(temp_exp, mod_len)) {
+ kfree(temp_exp);
return SEN_USER_ERROR;
+ }
exp_p = temp_exp;
for (i = 0; i < mod_len; i++)
if (exp_p[i])
break;
- if (i >= mod_len)
+ if (i >= mod_len) {
+ kfree(temp_exp);
return SEN_USER_ERROR;
+ }
exp_len = mod_len - i;
exp_p += i;
PDEBUG("exp_len after computation: %08x\n", exp_len);
cprbx_p->domain = (unsigned short)cdx;
cprbx_p->rpl_msgbl = RESPONSE_CPRBX_SIZE;
tgt_p += sizeof(struct CPRBX);
- memcpy(tgt_p, &static_pke_function_and_rulesX,
- sizeof(struct function_and_rules_block));
+ if (dev_type == PCIXCC_MCL2)
+ memcpy(tgt_p, &static_pke_function_and_rulesX_MCL2,
+ sizeof(struct function_and_rules_block));
+ else
+ memcpy(tgt_p, &static_pke_function_and_rulesX,
+ sizeof(struct function_and_rules_block));
tgt_p += sizeof(struct function_and_rules_block);
tgt_p += 2;
- if (copy_from_user(tgt_p, icaMsg_p->inputdata, mod_len))
- return SEN_RELEASED;
- if (is_empty(tgt_p, mod_len))
- return SEN_USER_ERROR;
+ if (copy_from_user(tgt_p, icaMsg_p->inputdata, mod_len)) {
+ kfree(temp_exp);
+ return SEN_RELEASED;
+ }
+ if (is_empty(tgt_p, mod_len)) {
+ kfree(temp_exp);
+ return SEN_USER_ERROR;
+ }
tgt_p -= 2;
*((short *)tgt_p) = (short) vud_len;
tgt_p += vud_len;
key_p = (struct cca_public_key *)tgt_p;
temp = key_p->pubSec.exponent;
memcpy(temp, exp_p, exp_len);
+ kfree(temp_exp);
temp += exp_len;
if (copy_from_user(temp, icaMsg_p->n_modulus, mod_len))
- return SEN_RELEASED;
+ return SEN_RELEASED;
if (is_empty(temp, mod_len))
- return SEN_USER_ERROR;
+ return SEN_USER_ERROR;
key_p->pubSec.modulus_bit_len = 8 * mod_len;
key_p->pubSec.modulus_byte_len = mod_len;
key_p->pubSec.exponent_len = exp_len;
- key_p->pubSec.section_length = 12 + mod_len + exp_len;
+ key_p->pubSec.section_length = CALLER_HEADER + mod_len + exp_len;
key_len = key_p->pubSec.section_length + sizeof(struct cca_token_hdr);
key_p->pubHdr.token_length = key_len;
key_len += 4;
static int
ICACRT_msg_to_type6CRT_msgX(struct ica_rsa_modexpo_crt *icaMsg_p, int cdx,
- int *z90cMsg_l_p, struct type6_msg *z90cMsg_p)
+ int *z90cMsg_l_p, struct type6_msg *z90cMsg_p,
+ int dev_type)
{
int mod_len, vud_len, tmp_size, total_CPRB_len, parmBlock_l, short_len;
int long_len, pad_len, keyPartsLen, tmp_l;
cprbx_p->req_parml = parmBlock_l;
cprbx_p->rpl_msgbl = parmBlock_l;
tgt_p += sizeof(struct CPRBX);
- memcpy(tgt_p, &static_pkd_function_and_rulesX,
- sizeof(struct function_and_rules_block));
+ if (dev_type == PCIXCC_MCL2)
+ memcpy(tgt_p, &static_pkd_function_and_rulesX_MCL2,
+ sizeof(struct function_and_rules_block));
+ else
+ memcpy(tgt_p, &static_pkd_function_and_rulesX,
+ sizeof(struct function_and_rules_block));
tgt_p += sizeof(struct function_and_rules_block);
*((short *)tgt_p) = (short) vud_len;
tgt_p += 2;
(struct ica_rsa_modexpo *) buffer,
cdx, msg_l_p, (struct type6_msg *) msg_p);
}
- if (dev_type == PCIXCC) {
+ if ((dev_type == PCIXCC_MCL2) ||
+ (dev_type == PCIXCC_MCL3) ||
+ (dev_type == CEX2C)) {
if (func == ICARSACRT)
return ICACRT_msg_to_type6CRT_msgX(
(struct ica_rsa_modexpo_crt *) buffer,
- cdx, msg_l_p, (struct type6_msg *) msg_p);
+ cdx, msg_l_p, (struct type6_msg *) msg_p,
+ dev_type);
else
return ICAMEX_msg_to_type6MEX_msgX(
(struct ica_rsa_modexpo *) buffer,
- cdx, msg_l_p, (struct type6_msg *) msg_p);
+ cdx, msg_l_p, (struct type6_msg *) msg_p,
+ dev_type);
}
return 0;
}
+int ext_bitlens_msg_count = 0;
+static inline void
+unset_ext_bitlens(void)
+{
+ if (!ext_bitlens_msg_count) {
+ PRINTK("Unable to use coprocessors for extended bitlengths. "
+ "Using PCICAs (if present) for extended bitlengths. "
+ "This is not an error.\n");
+ ext_bitlens_msg_count++;
+ }
+ ext_bitlens = 0;
+}
+
int
convert_response(unsigned char *response, unsigned char *buffer,
int *respbufflen_p, unsigned char *resp_buff)
struct ica_rsa_modexpo *icaMsg_p = (struct ica_rsa_modexpo *) buffer;
struct type82_hdr *t82h_p = (struct type82_hdr *) response;
struct type84_hdr *t84h_p = (struct type84_hdr *) response;
- struct type86_hdr *t86h_p = (struct type86_hdr *) response;
- int rv, reply_code, service_rc, service_rs, src_l;
+ struct type86_fmt2_msg *t86m_p = (struct type86_fmt2_msg *) response;
+ int reply_code, service_rc, service_rs, src_l;
unsigned char *src_p, *tgt_p;
struct CPRB *cprb_p;
struct CPRBX *cprbx_p;
service_rc = 0;
service_rs = 0;
src_l = 0;
- rv = 0;
switch (t82h_p->type) {
case TYPE82_RSP_CODE:
reply_code = t82h_p->reply_code;
- rv = 4;
src_p = (unsigned char *)t82h_p;
PRINTK("Hardware error: Type 82 Message Header: "
"%02x%02x%02x%02x%02x%02x%02x%02x\n",
src_p = response + (int)t84h_p->len - src_l;
break;
case TYPE86_RSP_CODE:
- reply_code = t86h_p->reply_code;
- if (t86h_p->format != TYPE86_FMT2) {
- rv = 4;
- break;
- }
- if (reply_code != 0) {
- rv = 4;
+ reply_code = t86m_p->hdr.reply_code;
+ if (reply_code != 0)
break;
- }
cprb_p = (struct CPRB *)
(response + sizeof(struct type86_fmt2_msg));
cprbx_p = (struct CPRBX *) cprb_p;
if (service_rc != 0) {
le2toI(cprb_p->ccp_rscode, &service_rs);
if ((service_rc == 8) && (service_rs == 66))
- PDEBUG("8/66 on PCICC\n");
+ PDEBUG("Bad block format on PCICC\n");
+ else if ((service_rc == 8) && (service_rs == 770)) {
+ PDEBUG("Invalid key length on PCICC\n");
+ unset_ext_bitlens();
+ return REC_USE_PCICA;
+ }
+ else if ((service_rc == 8) && (service_rs == 783)) {
+ PDEBUG("Extended bitlengths not enabled"
+ "on PCICC\n");
+ unset_ext_bitlens();
+ return REC_USE_PCICA;
+ }
else
PRINTK("service rc/rs: %d/%d\n",
service_rc, service_rs);
- rv = 8;
+ return REC_OPERAND_INV;
}
src_p = (unsigned char *)cprb_p + sizeof(struct CPRB);
src_p += 4;
if (service_rc != 0) {
service_rs = (int) cprbx_p->ccp_rscode;
if ((service_rc == 8) && (service_rs == 66))
- PDEBUG("8/66 on PCIXCC\n");
+ PDEBUG("Bad block format on PCXICC\n");
+ else if ((service_rc == 8) && (service_rs == 770)) {
+ PDEBUG("Invalid key length on PCIXCC\n");
+ unset_ext_bitlens();
+ return REC_USE_PCICA;
+ }
+ else if ((service_rc == 8) && (service_rs == 783)) {
+ PDEBUG("Extended bitlengths not enabled"
+ "on PCIXCC\n");
+ unset_ext_bitlens();
+ return REC_USE_PCICA;
+ }
else
PRINTK("service rc/rs: %d/%d\n",
service_rc, service_rs);
- rv = 8;
+ return REC_OPERAND_INV;
}
src_p = (unsigned char *)
cprbx_p + sizeof(struct CPRBX);
}
break;
default:
- break;
+ return REC_BAD_MESSAGE;
}
- if (rv == 8)
- return 8;
- if (rv == 4)
+ if (reply_code)
switch (reply_code) {
case REPLY_ERROR_OPERAND_INVALID:
return REC_OPERAND_INV;
return REC_EVEN_MOD;
case REPLY_ERROR_MESSAGE_TYPE:
return WRONG_DEVICE_TYPE;
+ case REPLY_ERROR_TRANSPORT_FAIL:
+ PRINTKW("Transport failed (APFS = %02X%02X%02X%02X)\n",
+ t86m_p->apfs[0], t86m_p->apfs[1],
+ t86m_p->apfs[2], t86m_p->apfs[3]);
+ return REC_HARDWAR_ERR;
default:
- return 12;
+ PRINTKW("reply code = %d\n", reply_code);
+ return REC_HARDWAR_ERR;
}
if (service_rc != 0)
memcpy(tgt_p, src_p, src_l);
if ((t82h_p->type == TYPE86_RSP_CODE) && (resp_buff < tgt_p)) {
memset(resp_buff, 0, icaMsg_p->outputdatalength - src_l);
- rv = pad_msg(resp_buff, icaMsg_p->outputdatalength, src_l);
- if (rv != 0)
- return rv;
+ if (pad_msg(resp_buff, icaMsg_p->outputdatalength, src_l))
+ return REC_INVALID_PAD;
}
*respbufflen_p = icaMsg_p->outputdatalength;
if (*respbufflen_p == 0)
PRINTK("Zero *respbufflen_p\n");
- return rv;
+ return 0;
}