diff options
author | Ralph Wuerthner <rwuerthn@de.ibm.com> | 2006-09-20 09:58:36 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2006-09-20 09:58:36 -0400 |
commit | 5432114baf0300286a6ca1b0aea549492a379432 (patch) | |
tree | 6a9cf64c86e4ee4d439f91e9bd4485688d28ddd7 /drivers/s390/crypto/zcrypt_pcixcc.c | |
parent | fe3a1be59c851aba2330387596c6134bc5ec8397 (diff) |
[S390] zcrypt secure key cryptography extension.
Allow the user space to send extended cprb messages directly to the
PCIXCC / CEX2C cards. This allows the CCA library to construct special
crypto requests that use "secure" keys that are stored on the card.
Signed-off-by: Ralph Wuerthner <rwuerthn@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390/crypto/zcrypt_pcixcc.c')
-rw-r--r-- | drivers/s390/crypto/zcrypt_pcixcc.c | 263 |
1 files changed, 250 insertions, 13 deletions
diff --git a/drivers/s390/crypto/zcrypt_pcixcc.c b/drivers/s390/crypto/zcrypt_pcixcc.c index 6064cf58be43..2da8b9381407 100644 --- a/drivers/s390/crypto/zcrypt_pcixcc.c +++ b/drivers/s390/crypto/zcrypt_pcixcc.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * linux/drivers/s390/crypto/zcrypt_pcixcc.c | 2 | * linux/drivers/s390/crypto/zcrypt_pcixcc.c |
3 | * | 3 | * |
4 | * zcrypt 2.0.0 | 4 | * zcrypt 2.1.0 |
5 | * | 5 | * |
6 | * Copyright (C) 2001, 2006 IBM Corporation | 6 | * Copyright (C) 2001, 2006 IBM Corporation |
7 | * Author(s): Robert Burroughs | 7 | * Author(s): Robert Burroughs |
@@ -60,6 +60,15 @@ | |||
60 | 60 | ||
61 | #define PCIXCC_CLEANUP_TIME (15*HZ) | 61 | #define PCIXCC_CLEANUP_TIME (15*HZ) |
62 | 62 | ||
63 | #define CEIL4(x) ((((x)+3)/4)*4) | ||
64 | |||
65 | struct response_type { | ||
66 | struct completion work; | ||
67 | int type; | ||
68 | }; | ||
69 | #define PCIXCC_RESPONSE_TYPE_ICA 0 | ||
70 | #define PCIXCC_RESPONSE_TYPE_XCRB 1 | ||
71 | |||
63 | static struct ap_device_id zcrypt_pcixcc_ids[] = { | 72 | static struct ap_device_id zcrypt_pcixcc_ids[] = { |
64 | { AP_DEVICE(AP_DEVICE_TYPE_PCIXCC) }, | 73 | { AP_DEVICE(AP_DEVICE_TYPE_PCIXCC) }, |
65 | { AP_DEVICE(AP_DEVICE_TYPE_CEX2C) }, | 74 | { AP_DEVICE(AP_DEVICE_TYPE_CEX2C) }, |
@@ -244,6 +253,108 @@ static int ICACRT_msg_to_type6CRT_msgX(struct zcrypt_device *zdev, | |||
244 | } | 253 | } |
245 | 254 | ||
246 | /** | 255 | /** |
256 | * Convert a XCRB message to a type6 CPRB message. | ||
257 | * | ||
258 | * @zdev: crypto device pointer | ||
259 | * @ap_msg: pointer to AP message | ||
260 | * @xcRB: pointer to user input data | ||
261 | * | ||
262 | * Returns 0 on success or -EFAULT. | ||
263 | */ | ||
264 | struct type86_fmt2_msg { | ||
265 | struct type86_hdr hdr; | ||
266 | struct type86_fmt2_ext fmt2; | ||
267 | } __attribute__((packed)); | ||
268 | |||
269 | static int XCRB_msg_to_type6CPRB_msgX(struct zcrypt_device *zdev, | ||
270 | struct ap_message *ap_msg, | ||
271 | struct ica_xcRB *xcRB) | ||
272 | { | ||
273 | static struct type6_hdr static_type6_hdrX = { | ||
274 | .type = 0x06, | ||
275 | .offset1 = 0x00000058, | ||
276 | }; | ||
277 | struct { | ||
278 | struct type6_hdr hdr; | ||
279 | struct ica_CPRBX cprbx; | ||
280 | } __attribute__((packed)) *msg = ap_msg->message; | ||
281 | |||
282 | int rcblen = CEIL4(xcRB->request_control_blk_length); | ||
283 | int replylen; | ||
284 | char *req_data = ap_msg->message + sizeof(struct type6_hdr) + rcblen; | ||
285 | char *function_code; | ||
286 | |||
287 | /* length checks */ | ||
288 | ap_msg->length = sizeof(struct type6_hdr) + | ||
289 | CEIL4(xcRB->request_control_blk_length) + | ||
290 | xcRB->request_data_length; | ||
291 | if (ap_msg->length > PCIXCC_MAX_XCRB_MESSAGE_SIZE) { | ||
292 | PRINTK("Combined message is too large (%ld/%d/%d).\n", | ||
293 | sizeof(struct type6_hdr), | ||
294 | xcRB->request_control_blk_length, | ||
295 | xcRB->request_data_length); | ||
296 | return -EFAULT; | ||
297 | } | ||
298 | if (CEIL4(xcRB->reply_control_blk_length) > | ||
299 | PCIXCC_MAX_XCRB_REPLY_SIZE) { | ||
300 | PDEBUG("Reply CPRB length is too large (%d).\n", | ||
301 | xcRB->request_control_blk_length); | ||
302 | return -EFAULT; | ||
303 | } | ||
304 | if (CEIL4(xcRB->reply_data_length) > PCIXCC_MAX_XCRB_DATA_SIZE) { | ||
305 | PDEBUG("Reply data block length is too large (%d).\n", | ||
306 | xcRB->reply_data_length); | ||
307 | return -EFAULT; | ||
308 | } | ||
309 | replylen = CEIL4(xcRB->reply_control_blk_length) + | ||
310 | CEIL4(xcRB->reply_data_length) + | ||
311 | sizeof(struct type86_fmt2_msg); | ||
312 | if (replylen > PCIXCC_MAX_XCRB_RESPONSE_SIZE) { | ||
313 | PDEBUG("Reply CPRB + data block > PCIXCC_MAX_XCRB_RESPONSE_SIZE" | ||
314 | " (%d/%d/%d).\n", | ||
315 | sizeof(struct type86_fmt2_msg), | ||
316 | xcRB->reply_control_blk_length, | ||
317 | xcRB->reply_data_length); | ||
318 | xcRB->reply_control_blk_length = PCIXCC_MAX_XCRB_RESPONSE_SIZE - | ||
319 | (sizeof(struct type86_fmt2_msg) + | ||
320 | CEIL4(xcRB->reply_data_length)); | ||
321 | PDEBUG("Capping Reply CPRB length at %d\n", | ||
322 | xcRB->reply_control_blk_length); | ||
323 | } | ||
324 | |||
325 | /* prepare type6 header */ | ||
326 | msg->hdr = static_type6_hdrX; | ||
327 | memcpy(msg->hdr.agent_id , &(xcRB->agent_ID), sizeof(xcRB->agent_ID)); | ||
328 | msg->hdr.ToCardLen1 = xcRB->request_control_blk_length; | ||
329 | if (xcRB->request_data_length) { | ||
330 | msg->hdr.offset2 = msg->hdr.offset1 + rcblen; | ||
331 | msg->hdr.ToCardLen2 = xcRB->request_data_length; | ||
332 | } | ||
333 | msg->hdr.FromCardLen1 = xcRB->reply_control_blk_length; | ||
334 | msg->hdr.FromCardLen2 = xcRB->reply_data_length; | ||
335 | |||
336 | /* prepare CPRB */ | ||
337 | if (copy_from_user(&(msg->cprbx), xcRB->request_control_blk_addr, | ||
338 | xcRB->request_control_blk_length)) | ||
339 | return -EFAULT; | ||
340 | if (msg->cprbx.cprb_len + sizeof(msg->hdr.function_code) > | ||
341 | xcRB->request_control_blk_length) { | ||
342 | PDEBUG("cprb_len too large (%d/%d)\n", msg->cprbx.cprb_len, | ||
343 | xcRB->request_control_blk_length); | ||
344 | return -EFAULT; | ||
345 | } | ||
346 | function_code = ((unsigned char *)&msg->cprbx) + msg->cprbx.cprb_len; | ||
347 | memcpy(msg->hdr.function_code, function_code, sizeof(msg->hdr.function_code)); | ||
348 | |||
349 | /* copy data block */ | ||
350 | if (xcRB->request_data_length && | ||
351 | copy_from_user(req_data, xcRB->request_data_address, | ||
352 | xcRB->request_data_length)) | ||
353 | return -EFAULT; | ||
354 | return 0; | ||
355 | } | ||
356 | |||
357 | /** | ||
247 | * Copy results from a type 86 ICA reply message back to user space. | 358 | * Copy results from a type 86 ICA reply message back to user space. |
248 | * | 359 | * |
249 | * @zdev: crypto device pointer | 360 | * @zdev: crypto device pointer |
@@ -363,6 +474,37 @@ static int convert_type86_ica(struct zcrypt_device *zdev, | |||
363 | return 0; | 474 | return 0; |
364 | } | 475 | } |
365 | 476 | ||
477 | /** | ||
478 | * Copy results from a type 86 XCRB reply message back to user space. | ||
479 | * | ||
480 | * @zdev: crypto device pointer | ||
481 | * @reply: reply AP message. | ||
482 | * @xcRB: pointer to XCRB | ||
483 | * | ||
484 | * Returns 0 on success or -EINVAL, -EFAULT, -EAGAIN in case of an error. | ||
485 | */ | ||
486 | static int convert_type86_xcrb(struct zcrypt_device *zdev, | ||
487 | struct ap_message *reply, | ||
488 | struct ica_xcRB *xcRB) | ||
489 | { | ||
490 | struct type86_fmt2_msg *msg = reply->message; | ||
491 | char *data = reply->message; | ||
492 | |||
493 | /* Copy CPRB to user */ | ||
494 | if (copy_to_user(xcRB->reply_control_blk_addr, | ||
495 | data + msg->fmt2.offset1, msg->fmt2.count1)) | ||
496 | return -EFAULT; | ||
497 | xcRB->reply_control_blk_length = msg->fmt2.count1; | ||
498 | |||
499 | /* Copy data buffer to user */ | ||
500 | if (msg->fmt2.count2) | ||
501 | if (copy_to_user(xcRB->reply_data_addr, | ||
502 | data + msg->fmt2.offset2, msg->fmt2.count2)) | ||
503 | return -EFAULT; | ||
504 | xcRB->reply_data_length = msg->fmt2.count2; | ||
505 | return 0; | ||
506 | } | ||
507 | |||
366 | static int convert_response_ica(struct zcrypt_device *zdev, | 508 | static int convert_response_ica(struct zcrypt_device *zdev, |
367 | struct ap_message *reply, | 509 | struct ap_message *reply, |
368 | char __user *outputdata, | 510 | char __user *outputdata, |
@@ -391,6 +533,36 @@ static int convert_response_ica(struct zcrypt_device *zdev, | |||
391 | } | 533 | } |
392 | } | 534 | } |
393 | 535 | ||
536 | static int convert_response_xcrb(struct zcrypt_device *zdev, | ||
537 | struct ap_message *reply, | ||
538 | struct ica_xcRB *xcRB) | ||
539 | { | ||
540 | struct type86x_reply *msg = reply->message; | ||
541 | |||
542 | /* Response type byte is the second byte in the response. */ | ||
543 | switch (((unsigned char *) reply->message)[1]) { | ||
544 | case TYPE82_RSP_CODE: | ||
545 | case TYPE88_RSP_CODE: | ||
546 | xcRB->status = 0x0008044DL; /* HDD_InvalidParm */ | ||
547 | return convert_error(zdev, reply); | ||
548 | case TYPE86_RSP_CODE: | ||
549 | if (msg->hdr.reply_code) { | ||
550 | memcpy(&(xcRB->status), msg->fmt2.apfs, sizeof(u32)); | ||
551 | return convert_error(zdev, reply); | ||
552 | } | ||
553 | if (msg->cprbx.cprb_ver_id == 0x02) | ||
554 | return convert_type86_xcrb(zdev, reply, xcRB); | ||
555 | /* no break, incorrect cprb version is an unknown response */ | ||
556 | default: /* Unknown response type, this should NEVER EVER happen */ | ||
557 | PRINTK("Unrecognized Message Header: %08x%08x\n", | ||
558 | *(unsigned int *) reply->message, | ||
559 | *(unsigned int *) (reply->message+4)); | ||
560 | xcRB->status = 0x0008044DL; /* HDD_InvalidParm */ | ||
561 | zdev->online = 0; | ||
562 | return -EAGAIN; /* repeat the request on a different device. */ | ||
563 | } | ||
564 | } | ||
565 | |||
394 | /** | 566 | /** |
395 | * This function is called from the AP bus code after a crypto request | 567 | * This function is called from the AP bus code after a crypto request |
396 | * "msg" has finished with the reply message "reply". | 568 | * "msg" has finished with the reply message "reply". |
@@ -407,6 +579,8 @@ static void zcrypt_pcixcc_receive(struct ap_device *ap_dev, | |||
407 | .type = TYPE82_RSP_CODE, | 579 | .type = TYPE82_RSP_CODE, |
408 | .reply_code = REP82_ERROR_MACHINE_FAILURE, | 580 | .reply_code = REP82_ERROR_MACHINE_FAILURE, |
409 | }; | 581 | }; |
582 | struct response_type *resp_type = | ||
583 | (struct response_type *) msg->private; | ||
410 | struct type86x_reply *t86r = reply->message; | 584 | struct type86x_reply *t86r = reply->message; |
411 | int length; | 585 | int length; |
412 | 586 | ||
@@ -415,12 +589,27 @@ static void zcrypt_pcixcc_receive(struct ap_device *ap_dev, | |||
415 | memcpy(msg->message, &error_reply, sizeof(error_reply)); | 589 | memcpy(msg->message, &error_reply, sizeof(error_reply)); |
416 | else if (t86r->hdr.type == TYPE86_RSP_CODE && | 590 | else if (t86r->hdr.type == TYPE86_RSP_CODE && |
417 | t86r->cprbx.cprb_ver_id == 0x02) { | 591 | t86r->cprbx.cprb_ver_id == 0x02) { |
418 | length = sizeof(struct type86x_reply) + t86r->length - 2; | 592 | switch (resp_type->type) { |
419 | length = min(PCIXCC_MAX_ICA_RESPONSE_SIZE, length); | 593 | case PCIXCC_RESPONSE_TYPE_ICA: |
420 | memcpy(msg->message, reply->message, length); | 594 | length = sizeof(struct type86x_reply) |
595 | + t86r->length - 2; | ||
596 | length = min(PCIXCC_MAX_ICA_RESPONSE_SIZE, length); | ||
597 | memcpy(msg->message, reply->message, length); | ||
598 | break; | ||
599 | case PCIXCC_RESPONSE_TYPE_XCRB: | ||
600 | length = t86r->fmt2.offset2 + t86r->fmt2.count2; | ||
601 | length = min(PCIXCC_MAX_XCRB_RESPONSE_SIZE, length); | ||
602 | memcpy(msg->message, reply->message, length); | ||
603 | break; | ||
604 | default: | ||
605 | PRINTK("Invalid internal response type: %i\n", | ||
606 | resp_type->type); | ||
607 | memcpy(msg->message, &error_reply, | ||
608 | sizeof error_reply); | ||
609 | } | ||
421 | } else | 610 | } else |
422 | memcpy(msg->message, reply->message, sizeof error_reply); | 611 | memcpy(msg->message, reply->message, sizeof error_reply); |
423 | complete((struct completion *) msg->private); | 612 | complete(&(resp_type->work)); |
424 | } | 613 | } |
425 | 614 | ||
426 | static atomic_t zcrypt_step = ATOMIC_INIT(0); | 615 | static atomic_t zcrypt_step = ATOMIC_INIT(0); |
@@ -436,7 +625,9 @@ static long zcrypt_pcixcc_modexpo(struct zcrypt_device *zdev, | |||
436 | struct ica_rsa_modexpo *mex) | 625 | struct ica_rsa_modexpo *mex) |
437 | { | 626 | { |
438 | struct ap_message ap_msg; | 627 | struct ap_message ap_msg; |
439 | struct completion work; | 628 | struct response_type resp_type = { |
629 | .type = PCIXCC_RESPONSE_TYPE_ICA, | ||
630 | }; | ||
440 | int rc; | 631 | int rc; |
441 | 632 | ||
442 | ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL); | 633 | ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL); |
@@ -444,14 +635,14 @@ static long zcrypt_pcixcc_modexpo(struct zcrypt_device *zdev, | |||
444 | return -ENOMEM; | 635 | return -ENOMEM; |
445 | ap_msg.psmid = (((unsigned long long) current->pid) << 32) + | 636 | ap_msg.psmid = (((unsigned long long) current->pid) << 32) + |
446 | atomic_inc_return(&zcrypt_step); | 637 | atomic_inc_return(&zcrypt_step); |
447 | ap_msg.private = &work; | 638 | ap_msg.private = &resp_type; |
448 | rc = ICAMEX_msg_to_type6MEX_msgX(zdev, &ap_msg, mex); | 639 | rc = ICAMEX_msg_to_type6MEX_msgX(zdev, &ap_msg, mex); |
449 | if (rc) | 640 | if (rc) |
450 | goto out_free; | 641 | goto out_free; |
451 | init_completion(&work); | 642 | init_completion(&resp_type.work); |
452 | ap_queue_message(zdev->ap_dev, &ap_msg); | 643 | ap_queue_message(zdev->ap_dev, &ap_msg); |
453 | rc = wait_for_completion_interruptible_timeout( | 644 | rc = wait_for_completion_interruptible_timeout( |
454 | &work, PCIXCC_CLEANUP_TIME); | 645 | &resp_type.work, PCIXCC_CLEANUP_TIME); |
455 | if (rc > 0) | 646 | if (rc > 0) |
456 | rc = convert_response_ica(zdev, &ap_msg, mex->outputdata, | 647 | rc = convert_response_ica(zdev, &ap_msg, mex->outputdata, |
457 | mex->outputdatalength); | 648 | mex->outputdatalength); |
@@ -478,7 +669,9 @@ static long zcrypt_pcixcc_modexpo_crt(struct zcrypt_device *zdev, | |||
478 | struct ica_rsa_modexpo_crt *crt) | 669 | struct ica_rsa_modexpo_crt *crt) |
479 | { | 670 | { |
480 | struct ap_message ap_msg; | 671 | struct ap_message ap_msg; |
481 | struct completion work; | 672 | struct response_type resp_type = { |
673 | .type = PCIXCC_RESPONSE_TYPE_ICA, | ||
674 | }; | ||
482 | int rc; | 675 | int rc; |
483 | 676 | ||
484 | ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL); | 677 | ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL); |
@@ -486,14 +679,14 @@ static long zcrypt_pcixcc_modexpo_crt(struct zcrypt_device *zdev, | |||
486 | return -ENOMEM; | 679 | return -ENOMEM; |
487 | ap_msg.psmid = (((unsigned long long) current->pid) << 32) + | 680 | ap_msg.psmid = (((unsigned long long) current->pid) << 32) + |
488 | atomic_inc_return(&zcrypt_step); | 681 | atomic_inc_return(&zcrypt_step); |
489 | ap_msg.private = &work; | 682 | ap_msg.private = &resp_type; |
490 | rc = ICACRT_msg_to_type6CRT_msgX(zdev, &ap_msg, crt); | 683 | rc = ICACRT_msg_to_type6CRT_msgX(zdev, &ap_msg, crt); |
491 | if (rc) | 684 | if (rc) |
492 | goto out_free; | 685 | goto out_free; |
493 | init_completion(&work); | 686 | init_completion(&resp_type.work); |
494 | ap_queue_message(zdev->ap_dev, &ap_msg); | 687 | ap_queue_message(zdev->ap_dev, &ap_msg); |
495 | rc = wait_for_completion_interruptible_timeout( | 688 | rc = wait_for_completion_interruptible_timeout( |
496 | &work, PCIXCC_CLEANUP_TIME); | 689 | &resp_type.work, PCIXCC_CLEANUP_TIME); |
497 | if (rc > 0) | 690 | if (rc > 0) |
498 | rc = convert_response_ica(zdev, &ap_msg, crt->outputdata, | 691 | rc = convert_response_ica(zdev, &ap_msg, crt->outputdata, |
499 | crt->outputdatalength); | 692 | crt->outputdatalength); |
@@ -510,11 +703,55 @@ out_free: | |||
510 | } | 703 | } |
511 | 704 | ||
512 | /** | 705 | /** |
706 | * The request distributor calls this function if it picked the PCIXCC/CEX2C | ||
707 | * device to handle a send_cprb request. | ||
708 | * @zdev: pointer to zcrypt_device structure that identifies the | ||
709 | * PCIXCC/CEX2C device to the request distributor | ||
710 | * @xcRB: pointer to the send_cprb request buffer | ||
711 | */ | ||
712 | long zcrypt_pcixcc_send_cprb(struct zcrypt_device *zdev, struct ica_xcRB *xcRB) | ||
713 | { | ||
714 | struct ap_message ap_msg; | ||
715 | struct response_type resp_type = { | ||
716 | .type = PCIXCC_RESPONSE_TYPE_XCRB, | ||
717 | }; | ||
718 | int rc; | ||
719 | |||
720 | ap_msg.message = (void *) kmalloc(PCIXCC_MAX_XCRB_MESSAGE_SIZE, GFP_KERNEL); | ||
721 | if (!ap_msg.message) | ||
722 | return -ENOMEM; | ||
723 | ap_msg.psmid = (((unsigned long long) current->pid) << 32) + | ||
724 | atomic_inc_return(&zcrypt_step); | ||
725 | ap_msg.private = &resp_type; | ||
726 | rc = XCRB_msg_to_type6CPRB_msgX(zdev, &ap_msg, xcRB); | ||
727 | if (rc) | ||
728 | goto out_free; | ||
729 | init_completion(&resp_type.work); | ||
730 | ap_queue_message(zdev->ap_dev, &ap_msg); | ||
731 | rc = wait_for_completion_interruptible_timeout( | ||
732 | &resp_type.work, PCIXCC_CLEANUP_TIME); | ||
733 | if (rc > 0) | ||
734 | rc = convert_response_xcrb(zdev, &ap_msg, xcRB); | ||
735 | else { | ||
736 | /* Signal pending or message timed out. */ | ||
737 | ap_cancel_message(zdev->ap_dev, &ap_msg); | ||
738 | if (rc == 0) | ||
739 | /* Message timed out. */ | ||
740 | rc = -ETIME; | ||
741 | } | ||
742 | out_free: | ||
743 | memset(ap_msg.message, 0x0, ap_msg.length); | ||
744 | kfree(ap_msg.message); | ||
745 | return rc; | ||
746 | } | ||
747 | |||
748 | /** | ||
513 | * The crypto operations for a PCIXCC/CEX2C card. | 749 | * The crypto operations for a PCIXCC/CEX2C card. |
514 | */ | 750 | */ |
515 | static struct zcrypt_ops zcrypt_pcixcc_ops = { | 751 | static struct zcrypt_ops zcrypt_pcixcc_ops = { |
516 | .rsa_modexpo = zcrypt_pcixcc_modexpo, | 752 | .rsa_modexpo = zcrypt_pcixcc_modexpo, |
517 | .rsa_modexpo_crt = zcrypt_pcixcc_modexpo_crt, | 753 | .rsa_modexpo_crt = zcrypt_pcixcc_modexpo_crt, |
754 | .send_cprb = zcrypt_pcixcc_send_cprb, | ||
518 | }; | 755 | }; |
519 | 756 | ||
520 | /** | 757 | /** |