[S390] zcrypt: special command support for cex3 exploitation

Support for special command is implemented in the AP Bus in the NQAP
function __ap_send. This is extended for a further parameter special.
When set, the special bit, in GR0 will be set. Therefor the ap_message
struct is extended for a further bit. Thus calling functions of
__ap_send can use the special parameter in ap_message to give to
__ap_send. Affected is in the first place ap_queue_message, which is
called by the actual card driver. The second part of this support is
that the card driver for the CEX3C needs to set this special bit, when
an according CPRB is sent to the driver.

Signed-off-by: Felix Beck <felix.beck@de.ibm.com>
Signed-off-by: Ralph Wuerthner <ralph.wuerthner@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
This commit is contained in:
Felix Beck 2009-12-07 12:51:55 +01:00 committed by Martin Schwidefsky
parent 468ffddf19
commit a6a5d73a56
3 changed files with 23 additions and 5 deletions

View File

@ -282,6 +282,7 @@ static int ap_queue_enable_interruption(ap_qid_t qid, void *ind)
* @psmid: The program supplied message identifier * @psmid: The program supplied message identifier
* @msg: The message text * @msg: The message text
* @length: The message length * @length: The message length
* @special: Special Bit
* *
* Returns AP queue status structure. * Returns AP queue status structure.
* Condition code 1 on NQAP can't happen because the L bit is 1. * Condition code 1 on NQAP can't happen because the L bit is 1.
@ -289,7 +290,8 @@ static int ap_queue_enable_interruption(ap_qid_t qid, void *ind)
* because a segment boundary was reached. The NQAP is repeated. * because a segment boundary was reached. The NQAP is repeated.
*/ */
static inline struct ap_queue_status static inline struct ap_queue_status
__ap_send(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length) __ap_send(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length,
unsigned int special)
{ {
typedef struct { char _[length]; } msgblock; typedef struct { char _[length]; } msgblock;
register unsigned long reg0 asm ("0") = qid | 0x40000000UL; register unsigned long reg0 asm ("0") = qid | 0x40000000UL;
@ -299,6 +301,9 @@ __ap_send(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length)
register unsigned long reg4 asm ("4") = (unsigned int) (psmid >> 32); register unsigned long reg4 asm ("4") = (unsigned int) (psmid >> 32);
register unsigned long reg5 asm ("5") = (unsigned int) psmid; register unsigned long reg5 asm ("5") = (unsigned int) psmid;
if (special == 1)
reg0 |= 0x400000UL;
asm volatile ( asm volatile (
"0: .long 0xb2ad0042\n" /* DQAP */ "0: .long 0xb2ad0042\n" /* DQAP */
" brc 2,0b" " brc 2,0b"
@ -312,13 +317,15 @@ int ap_send(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length)
{ {
struct ap_queue_status status; struct ap_queue_status status;
status = __ap_send(qid, psmid, msg, length); status = __ap_send(qid, psmid, msg, length, 0);
switch (status.response_code) { switch (status.response_code) {
case AP_RESPONSE_NORMAL: case AP_RESPONSE_NORMAL:
return 0; return 0;
case AP_RESPONSE_Q_FULL: case AP_RESPONSE_Q_FULL:
case AP_RESPONSE_RESET_IN_PROGRESS: case AP_RESPONSE_RESET_IN_PROGRESS:
return -EBUSY; return -EBUSY;
case AP_RESPONSE_REQ_FAC_NOT_INST:
return -EINVAL;
default: /* Device is gone. */ default: /* Device is gone. */
return -ENODEV; return -ENODEV;
} }
@ -1008,7 +1015,7 @@ static int ap_probe_device_type(struct ap_device *ap_dev)
} }
status = __ap_send(ap_dev->qid, 0x0102030405060708ULL, status = __ap_send(ap_dev->qid, 0x0102030405060708ULL,
msg, sizeof(msg)); msg, sizeof(msg), 0);
if (status.response_code != AP_RESPONSE_NORMAL) { if (status.response_code != AP_RESPONSE_NORMAL) {
rc = -ENODEV; rc = -ENODEV;
goto out_free; goto out_free;
@ -1243,7 +1250,7 @@ static int ap_poll_write(struct ap_device *ap_dev, unsigned long *flags)
/* Start the next request on the queue. */ /* Start the next request on the queue. */
ap_msg = list_entry(ap_dev->requestq.next, struct ap_message, list); ap_msg = list_entry(ap_dev->requestq.next, struct ap_message, list);
status = __ap_send(ap_dev->qid, ap_msg->psmid, status = __ap_send(ap_dev->qid, ap_msg->psmid,
ap_msg->message, ap_msg->length); ap_msg->message, ap_msg->length, ap_msg->special);
switch (status.response_code) { switch (status.response_code) {
case AP_RESPONSE_NORMAL: case AP_RESPONSE_NORMAL:
atomic_inc(&ap_poll_requests); atomic_inc(&ap_poll_requests);
@ -1261,6 +1268,7 @@ static int ap_poll_write(struct ap_device *ap_dev, unsigned long *flags)
*flags |= 2; *flags |= 2;
break; break;
case AP_RESPONSE_MESSAGE_TOO_BIG: case AP_RESPONSE_MESSAGE_TOO_BIG:
case AP_RESPONSE_REQ_FAC_NOT_INST:
return -EINVAL; return -EINVAL;
default: default:
return -ENODEV; return -ENODEV;
@ -1302,7 +1310,8 @@ static int __ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_ms
if (list_empty(&ap_dev->requestq) && if (list_empty(&ap_dev->requestq) &&
ap_dev->queue_count < ap_dev->queue_depth) { ap_dev->queue_count < ap_dev->queue_depth) {
status = __ap_send(ap_dev->qid, ap_msg->psmid, status = __ap_send(ap_dev->qid, ap_msg->psmid,
ap_msg->message, ap_msg->length); ap_msg->message, ap_msg->length,
ap_msg->special);
switch (status.response_code) { switch (status.response_code) {
case AP_RESPONSE_NORMAL: case AP_RESPONSE_NORMAL:
list_add_tail(&ap_msg->list, &ap_dev->pendingq); list_add_tail(&ap_msg->list, &ap_dev->pendingq);
@ -1317,6 +1326,7 @@ static int __ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_ms
ap_dev->requestq_count++; ap_dev->requestq_count++;
ap_dev->total_request_count++; ap_dev->total_request_count++;
return -EBUSY; return -EBUSY;
case AP_RESPONSE_REQ_FAC_NOT_INST:
case AP_RESPONSE_MESSAGE_TOO_BIG: case AP_RESPONSE_MESSAGE_TOO_BIG:
ap_dev->drv->receive(ap_dev, ap_msg, ERR_PTR(-EINVAL)); ap_dev->drv->receive(ap_dev, ap_msg, ERR_PTR(-EINVAL));
return -EINVAL; return -EINVAL;

View File

@ -87,6 +87,7 @@ struct ap_queue_status {
#define AP_RESPONSE_INDEX_TOO_BIG 0x11 #define AP_RESPONSE_INDEX_TOO_BIG 0x11
#define AP_RESPONSE_NO_FIRST_PART 0x13 #define AP_RESPONSE_NO_FIRST_PART 0x13
#define AP_RESPONSE_MESSAGE_TOO_BIG 0x15 #define AP_RESPONSE_MESSAGE_TOO_BIG 0x15
#define AP_RESPONSE_REQ_FAC_NOT_INST 0x16
/* /*
* Known device types * Known device types
@ -161,6 +162,7 @@ struct ap_message {
size_t length; /* Message length. */ size_t length; /* Message length. */
void *private; /* ap driver private pointer. */ void *private; /* ap driver private pointer. */
unsigned int special:1; /* Used for special commands. */
}; };
#define AP_DEVICE(dt) \ #define AP_DEVICE(dt) \
@ -176,6 +178,7 @@ static inline void ap_init_message(struct ap_message *ap_msg)
{ {
ap_msg->psmid = 0; ap_msg->psmid = 0;
ap_msg->length = 0; ap_msg->length = 0;
ap_msg->special = 0;
} }
/* /*

View File

@ -326,6 +326,11 @@ static int XCRB_msg_to_type6CPRB_msgX(struct zcrypt_device *zdev,
function_code = ((unsigned char *)&msg->cprbx) + msg->cprbx.cprb_len; function_code = ((unsigned char *)&msg->cprbx) + msg->cprbx.cprb_len;
memcpy(msg->hdr.function_code, function_code, sizeof(msg->hdr.function_code)); 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 */ /* copy data block */
if (xcRB->request_data_length && if (xcRB->request_data_length &&
copy_from_user(req_data, xcRB->request_data_address, copy_from_user(req_data, xcRB->request_data_address,