diff options
Diffstat (limited to 'drivers/s390/crypto/ap_queue.c')
| -rw-r--r-- | drivers/s390/crypto/ap_queue.c | 717 |
1 files changed, 586 insertions, 131 deletions
diff --git a/drivers/s390/crypto/ap_queue.c b/drivers/s390/crypto/ap_queue.c index a32457b4cbb8..4a32c1e19a1e 100644 --- a/drivers/s390/crypto/ap_queue.c +++ b/drivers/s390/crypto/ap_queue.c @@ -1,23 +1,53 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright IBM Corp. 2016 + * Copyright IBM Corp. 2016, 2023 * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com> * * Adjunct processor bus, queue related code. */ -#define KMSG_COMPONENT "ap" -#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt +#define pr_fmt(fmt) "ap: " fmt +#include <linux/export.h> #include <linux/init.h> #include <linux/slab.h> #include <asm/facility.h> +#define CREATE_TRACE_POINTS +#include <asm/trace/ap.h> + #include "ap_bus.h" #include "ap_debug.h" +EXPORT_TRACEPOINT_SYMBOL(s390_ap_nqap); +EXPORT_TRACEPOINT_SYMBOL(s390_ap_dqap); + static void __ap_flush_queue(struct ap_queue *aq); +/* + * some AP queue helper functions + */ + +static inline bool ap_q_supported_in_se(struct ap_queue *aq) +{ + return aq->card->hwinfo.ep11 || aq->card->hwinfo.accel; +} + +static inline bool ap_q_supports_bind(struct ap_queue *aq) +{ + return aq->card->hwinfo.ep11 || aq->card->hwinfo.accel; +} + +static inline bool ap_q_supports_assoc(struct ap_queue *aq) +{ + return aq->card->hwinfo.ep11; +} + +static inline bool ap_q_needs_bind(struct ap_queue *aq) +{ + return ap_q_supports_bind(aq) && ap_sb_available(); +} + /** * ap_queue_enable_irq(): Enable interrupt support on this AP queue. * @aq: The AP queue @@ -29,12 +59,14 @@ static void __ap_flush_queue(struct ap_queue *aq); */ static int ap_queue_enable_irq(struct ap_queue *aq, void *ind) { + union ap_qirq_ctrl qirqctrl = { .value = 0 }; struct ap_queue_status status; - struct ap_qirq_ctrl qirqctrl = { 0 }; qirqctrl.ir = 1; qirqctrl.isc = AP_ISC; status = ap_aqic(aq->qid, qirqctrl, virt_to_phys(ind)); + if (status.async) + return -EPERM; switch (status.response_code) { case AP_RESPONSE_NORMAL: case AP_RESPONSE_OTHERWISE_CHANGED: @@ -59,7 +91,7 @@ static int ap_queue_enable_irq(struct ap_queue *aq, void *ind) * @qid: The AP queue number * @psmid: The program supplied message identifier * @msg: The message text - * @length: The message length + * @msglen: The message length * @special: Special Bit * * Returns AP queue status structure. @@ -68,54 +100,21 @@ static int ap_queue_enable_irq(struct ap_queue *aq, void *ind) * because a segment boundary was reached. The NQAP is repeated. */ 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 psmid, void *msg, size_t msglen, int special) { + struct ap_queue_status status; + if (special) qid |= 0x400000UL; - return ap_nqap(qid, psmid, msg, length); -} - -int ap_send(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length) -{ - struct ap_queue_status status; - status = __ap_send(qid, psmid, msg, length, 0); - switch (status.response_code) { - case AP_RESPONSE_NORMAL: - return 0; - case AP_RESPONSE_Q_FULL: - case AP_RESPONSE_RESET_IN_PROGRESS: - return -EBUSY; - case AP_RESPONSE_REQ_FAC_NOT_INST: - return -EINVAL; - default: /* Device is gone. */ - return -ENODEV; - } -} -EXPORT_SYMBOL(ap_send); + status = ap_nqap(qid, psmid, msg, msglen); -int ap_recv(ap_qid_t qid, unsigned long long *psmid, void *msg, size_t length) -{ - struct ap_queue_status status; + trace_s390_ap_nqap(AP_QID_CARD(qid), AP_QID_QUEUE(qid), + status.value, psmid); - if (!msg) - return -EINVAL; - status = ap_dqap(qid, psmid, msg, length, NULL, NULL); - switch (status.response_code) { - case AP_RESPONSE_NORMAL: - return 0; - case AP_RESPONSE_NO_PENDING_REPLY: - if (status.queue_empty) - return -ENOENT; - return -EBUSY; - case AP_RESPONSE_RESET_IN_PROGRESS: - return -EBUSY; - default: - return -ENODEV; - } + return status; } -EXPORT_SYMBOL(ap_recv); /* State machine definitions and helpers */ @@ -150,12 +149,17 @@ static struct ap_queue_status ap_sm_recv(struct ap_queue *aq) do { status = ap_dqap(aq->qid, &aq->reply->psmid, aq->reply->msg, aq->reply->bufsize, - &reslen, &resgr0); + &aq->reply->len, &reslen, &resgr0); parts++; } while (status.response_code == 0xFF && resgr0 != 0); + trace_s390_ap_dqap(AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid), + status.value, aq->reply->psmid); + switch (status.response_code) { case AP_RESPONSE_NORMAL: + print_hex_dump_debug("aprpl: ", DUMP_PREFIX_ADDRESS, 16, 1, + aq->reply->msg, aq->reply->len, false); aq->queue_count = max_t(int, 0, aq->queue_count - 1); if (!status.queue_empty && !aq->queue_count) aq->queue_count++; @@ -177,7 +181,7 @@ static struct ap_queue_status ap_sm_recv(struct ap_queue *aq) break; } if (!found) { - AP_DBF_WARN("%s unassociated reply psmid=0x%016llx on 0x%02x.%04x\n", + AP_DBF_WARN("%s unassociated reply psmid=0x%016lx on 0x%02x.%04x\n", __func__, aq->reply->psmid, AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid)); } @@ -189,6 +193,9 @@ static struct ap_queue_status ap_sm_recv(struct ap_queue *aq) aq->queue_count = 0; list_splice_init(&aq->pendingq, &aq->requestq); aq->requestq_count += aq->pendingq_count; + pr_debug("queue 0x%02x.%04x rescheduled %d reqs (new req %d)\n", + AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid), + aq->pendingq_count, aq->requestq_count); aq->pendingq_count = 0; break; default: @@ -210,6 +217,8 @@ static enum ap_sm_wait ap_sm_read(struct ap_queue *aq) if (!aq->reply) return AP_SM_WAIT_NONE; status = ap_sm_recv(aq); + if (status.async) + return AP_SM_WAIT_NONE; switch (status.response_code) { case AP_RESPONSE_NORMAL: if (aq->queue_count > 0) { @@ -217,13 +226,13 @@ static enum ap_sm_wait ap_sm_read(struct ap_queue *aq) return AP_SM_WAIT_AGAIN; } aq->sm_state = AP_SM_STATE_IDLE; - return AP_SM_WAIT_NONE; + break; case AP_RESPONSE_NO_PENDING_REPLY: if (aq->queue_count > 0) - return aq->interrupt ? - AP_SM_WAIT_INTERRUPT : AP_SM_WAIT_TIMEOUT; + return status.irq_enabled ? + AP_SM_WAIT_INTERRUPT : AP_SM_WAIT_HIGH_TIMEOUT; aq->sm_state = AP_SM_STATE_IDLE; - return AP_SM_WAIT_NONE; + break; default: aq->dev_state = AP_DEV_STATE_ERROR; aq->last_err_rc = status.response_code; @@ -232,6 +241,16 @@ static enum ap_sm_wait ap_sm_read(struct ap_queue *aq) AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid)); return AP_SM_WAIT_NONE; } + /* Check and maybe enable irq support (again) on this queue */ + if (!status.irq_enabled && status.queue_empty) { + void *lsi_ptr = ap_airq_ptr(); + + if (lsi_ptr && ap_queue_enable_irq(aq, lsi_ptr) == 0) { + aq->sm_state = AP_SM_STATE_SETIRQ_WAIT; + return AP_SM_WAIT_AGAIN; + } + } + return AP_SM_WAIT_NONE; } /** @@ -251,16 +270,13 @@ static enum ap_sm_wait ap_sm_write(struct ap_queue *aq) /* Start the next request on the queue. */ ap_msg = list_entry(aq->requestq.next, struct ap_message, list); -#ifdef CONFIG_ZCRYPT_DEBUG - if (ap_msg->fi.action == AP_FI_ACTION_NQAP_QID_INVAL) { - AP_DBF_WARN("%s fi cmd 0x%04x: forcing invalid qid 0xFF00\n", - __func__, ap_msg->fi.cmd); - qid = 0xFF00; - } -#endif + print_hex_dump_debug("apreq: ", DUMP_PREFIX_ADDRESS, 16, 1, + ap_msg->msg, ap_msg->len, false); status = __ap_send(qid, ap_msg->psmid, ap_msg->msg, ap_msg->len, ap_msg->flags & AP_MSG_FLAG_SPECIAL); + if (status.async) + return AP_SM_WAIT_NONE; switch (status.response_code) { case AP_RESPONSE_NORMAL: aq->queue_count = max_t(int, 1, aq->queue_count + 1); @@ -269,18 +285,18 @@ static enum ap_sm_wait ap_sm_write(struct ap_queue *aq) list_move_tail(&ap_msg->list, &aq->pendingq); aq->requestq_count--; aq->pendingq_count++; - if (aq->queue_count < aq->card->queue_depth) { + if (aq->queue_count < aq->card->hwinfo.qd) { aq->sm_state = AP_SM_STATE_WORKING; return AP_SM_WAIT_AGAIN; } fallthrough; case AP_RESPONSE_Q_FULL: aq->sm_state = AP_SM_STATE_QUEUE_FULL; - return aq->interrupt ? - AP_SM_WAIT_INTERRUPT : AP_SM_WAIT_TIMEOUT; + return status.irq_enabled ? + AP_SM_WAIT_INTERRUPT : AP_SM_WAIT_HIGH_TIMEOUT; case AP_RESPONSE_RESET_IN_PROGRESS: aq->sm_state = AP_SM_STATE_RESET_WAIT; - return AP_SM_WAIT_TIMEOUT; + return AP_SM_WAIT_LOW_TIMEOUT; case AP_RESPONSE_INVALID_DOMAIN: AP_DBF_WARN("%s RESPONSE_INVALID_DOMAIN on NQAP\n", __func__); fallthrough; @@ -322,13 +338,15 @@ static enum ap_sm_wait ap_sm_reset(struct ap_queue *aq) { struct ap_queue_status status; - status = ap_rapq(aq->qid); + status = ap_rapq(aq->qid, aq->rapq_fbit); + if (status.async) + return AP_SM_WAIT_NONE; switch (status.response_code) { case AP_RESPONSE_NORMAL: case AP_RESPONSE_RESET_IN_PROGRESS: aq->sm_state = AP_SM_STATE_RESET_WAIT; - aq->interrupt = false; - return AP_SM_WAIT_TIMEOUT; + aq->rapq_fbit = 0; + return AP_SM_WAIT_LOW_TIMEOUT; default: aq->dev_state = AP_DEV_STATE_ERROR; aq->last_err_rc = status.response_code; @@ -348,17 +366,15 @@ static enum ap_sm_wait ap_sm_reset(struct ap_queue *aq) static enum ap_sm_wait ap_sm_reset_wait(struct ap_queue *aq) { struct ap_queue_status status; + struct ap_tapq_hwinfo hwinfo; void *lsi_ptr; - if (aq->queue_count > 0 && aq->reply) - /* Try to read a completed message and get the status */ - status = ap_sm_recv(aq); - else - /* Get the status with TAPQ */ - status = ap_tapq(aq->qid, NULL); + /* Get the status with TAPQ */ + status = ap_test_queue(aq->qid, 1, &hwinfo); switch (status.response_code) { case AP_RESPONSE_NORMAL: + aq->se_bstate = hwinfo.bs; lsi_ptr = ap_airq_ptr(); if (lsi_ptr && ap_queue_enable_irq(aq, lsi_ptr) == 0) aq->sm_state = AP_SM_STATE_SETIRQ_WAIT; @@ -368,7 +384,7 @@ static enum ap_sm_wait ap_sm_reset_wait(struct ap_queue *aq) return AP_SM_WAIT_AGAIN; case AP_RESPONSE_BUSY: case AP_RESPONSE_RESET_IN_PROGRESS: - return AP_SM_WAIT_TIMEOUT; + return AP_SM_WAIT_LOW_TIMEOUT; case AP_RESPONSE_Q_NOT_AVAIL: case AP_RESPONSE_DECONFIGURED: case AP_RESPONSE_CHECKSTOPPED: @@ -401,7 +417,6 @@ static enum ap_sm_wait ap_sm_setirq_wait(struct ap_queue *aq) if (status.irq_enabled == 1) { /* Irqs are now enabled */ - aq->interrupt = true; aq->sm_state = (aq->queue_count > 0) ? AP_SM_STATE_WORKING : AP_SM_STATE_IDLE; } @@ -412,7 +427,7 @@ static enum ap_sm_wait ap_sm_setirq_wait(struct ap_queue *aq) return AP_SM_WAIT_AGAIN; fallthrough; case AP_RESPONSE_NO_PENDING_REPLY: - return AP_SM_WAIT_TIMEOUT; + return AP_SM_WAIT_LOW_TIMEOUT; default: aq->dev_state = AP_DEV_STATE_ERROR; aq->last_err_rc = status.response_code; @@ -423,6 +438,62 @@ static enum ap_sm_wait ap_sm_setirq_wait(struct ap_queue *aq) } } +/** + * ap_sm_assoc_wait(): Test queue for completion of a pending + * association request. + * @aq: pointer to the AP queue + */ +static enum ap_sm_wait ap_sm_assoc_wait(struct ap_queue *aq) +{ + struct ap_queue_status status; + struct ap_tapq_hwinfo hwinfo; + + status = ap_test_queue(aq->qid, 1, &hwinfo); + /* handle asynchronous error on this queue */ + if (status.async && status.response_code) { + aq->dev_state = AP_DEV_STATE_ERROR; + aq->last_err_rc = status.response_code; + AP_DBF_WARN("%s asynch RC 0x%02x on 0x%02x.%04x -> AP_DEV_STATE_ERROR\n", + __func__, status.response_code, + AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid)); + return AP_SM_WAIT_NONE; + } + if (status.response_code > AP_RESPONSE_BUSY) { + aq->dev_state = AP_DEV_STATE_ERROR; + aq->last_err_rc = status.response_code; + AP_DBF_WARN("%s RC 0x%02x on 0x%02x.%04x -> AP_DEV_STATE_ERROR\n", + __func__, status.response_code, + AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid)); + return AP_SM_WAIT_NONE; + } + + /* update queue's SE bind state */ + aq->se_bstate = hwinfo.bs; + + /* check bs bits */ + switch (hwinfo.bs) { + case AP_BS_Q_USABLE: + /* association is through */ + aq->sm_state = AP_SM_STATE_IDLE; + pr_debug("queue 0x%02x.%04x associated with %u\n", + AP_QID_CARD(aq->qid), + AP_QID_QUEUE(aq->qid), aq->assoc_idx); + return AP_SM_WAIT_NONE; + case AP_BS_Q_USABLE_NO_SECURE_KEY: + /* association still pending */ + return AP_SM_WAIT_LOW_TIMEOUT; + default: + /* reset from 'outside' happened or no idea at all */ + aq->assoc_idx = ASSOC_IDX_INVALID; + aq->dev_state = AP_DEV_STATE_ERROR; + aq->last_err_rc = status.response_code; + AP_DBF_WARN("%s bs 0x%02x on 0x%02x.%04x -> AP_DEV_STATE_ERROR\n", + __func__, hwinfo.bs, + AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid)); + return AP_SM_WAIT_NONE; + } +} + /* * AP state machine jump table */ @@ -451,6 +522,10 @@ static ap_func_t *ap_jumptable[NR_AP_SM_STATES][NR_AP_SM_EVENTS] = { [AP_SM_EVENT_POLL] = ap_sm_read, [AP_SM_EVENT_TIMEOUT] = ap_sm_reset, }, + [AP_SM_STATE_ASSOC_WAIT] = { + [AP_SM_EVENT_POLL] = ap_sm_assoc_wait, + [AP_SM_EVENT_TIMEOUT] = ap_sm_reset, + }, }; enum ap_sm_wait ap_sm_event(struct ap_queue *aq, enum ap_sm_event event) @@ -490,9 +565,9 @@ static ssize_t request_count_show(struct device *dev, spin_unlock_bh(&aq->lock); if (valid) - return scnprintf(buf, PAGE_SIZE, "%llu\n", req_cnt); + return sysfs_emit(buf, "%llu\n", req_cnt); else - return scnprintf(buf, PAGE_SIZE, "-\n"); + return sysfs_emit(buf, "-\n"); } static ssize_t request_count_store(struct device *dev, @@ -520,7 +595,7 @@ static ssize_t requestq_count_show(struct device *dev, if (aq->dev_state > AP_DEV_STATE_UNINITIATED) reqq_cnt = aq->requestq_count; spin_unlock_bh(&aq->lock); - return scnprintf(buf, PAGE_SIZE, "%d\n", reqq_cnt); + return sysfs_emit(buf, "%d\n", reqq_cnt); } static DEVICE_ATTR_RO(requestq_count); @@ -535,7 +610,7 @@ static ssize_t pendingq_count_show(struct device *dev, if (aq->dev_state > AP_DEV_STATE_UNINITIATED) penq_cnt = aq->pendingq_count; spin_unlock_bh(&aq->lock); - return scnprintf(buf, PAGE_SIZE, "%d\n", penq_cnt); + return sysfs_emit(buf, "%d\n", penq_cnt); } static DEVICE_ATTR_RO(pendingq_count); @@ -550,14 +625,14 @@ static ssize_t reset_show(struct device *dev, switch (aq->sm_state) { case AP_SM_STATE_RESET_START: case AP_SM_STATE_RESET_WAIT: - rc = scnprintf(buf, PAGE_SIZE, "Reset in progress.\n"); + rc = sysfs_emit(buf, "Reset in progress.\n"); break; case AP_SM_STATE_WORKING: case AP_SM_STATE_QUEUE_FULL: - rc = scnprintf(buf, PAGE_SIZE, "Reset Timer armed.\n"); + rc = sysfs_emit(buf, "Reset Timer armed.\n"); break; default: - rc = scnprintf(buf, PAGE_SIZE, "No Reset Timer set.\n"); + rc = sysfs_emit(buf, "No Reset Timer set.\n"); } spin_unlock_bh(&aq->lock); return rc; @@ -587,16 +662,21 @@ static ssize_t interrupt_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ap_queue *aq = to_ap_queue(dev); + struct ap_queue_status status; int rc = 0; spin_lock_bh(&aq->lock); - if (aq->sm_state == AP_SM_STATE_SETIRQ_WAIT) - rc = scnprintf(buf, PAGE_SIZE, "Enable Interrupt pending.\n"); - else if (aq->interrupt) - rc = scnprintf(buf, PAGE_SIZE, "Interrupts enabled.\n"); - else - rc = scnprintf(buf, PAGE_SIZE, "Interrupts disabled.\n"); + if (aq->sm_state == AP_SM_STATE_SETIRQ_WAIT) { + rc = sysfs_emit(buf, "Enable Interrupt pending.\n"); + } else { + status = ap_tapq(aq->qid, NULL); + if (status.irq_enabled) + rc = sysfs_emit(buf, "Interrupts enabled.\n"); + else + rc = sysfs_emit(buf, "Interrupts disabled.\n"); + } spin_unlock_bh(&aq->lock); + return rc; } @@ -609,7 +689,7 @@ static ssize_t config_show(struct device *dev, int rc; spin_lock_bh(&aq->lock); - rc = scnprintf(buf, PAGE_SIZE, "%d\n", aq->config ? 1 : 0); + rc = sysfs_emit(buf, "%d\n", aq->config ? 1 : 0); spin_unlock_bh(&aq->lock); return rc; } @@ -623,14 +703,86 @@ static ssize_t chkstop_show(struct device *dev, int rc; spin_lock_bh(&aq->lock); - rc = scnprintf(buf, PAGE_SIZE, "%d\n", aq->chkstop ? 1 : 0); + rc = sysfs_emit(buf, "%d\n", aq->chkstop ? 1 : 0); spin_unlock_bh(&aq->lock); return rc; } static DEVICE_ATTR_RO(chkstop); -#ifdef CONFIG_ZCRYPT_DEBUG +static ssize_t ap_functions_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ap_queue *aq = to_ap_queue(dev); + struct ap_queue_status status; + struct ap_tapq_hwinfo hwinfo; + + status = ap_test_queue(aq->qid, 1, &hwinfo); + if (status.response_code > AP_RESPONSE_BUSY) { + pr_debug("RC 0x%02x on tapq(0x%02x.%04x)\n", + status.response_code, + AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid)); + return -EIO; + } + + return sysfs_emit(buf, "0x%08X\n", hwinfo.fac); +} + +static DEVICE_ATTR_RO(ap_functions); + +static ssize_t driver_override_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ap_queue *aq = to_ap_queue(dev); + struct ap_device *ap_dev = &aq->ap_dev; + int rc; + + device_lock(dev); + if (ap_dev->driver_override) + rc = sysfs_emit(buf, "%s\n", ap_dev->driver_override); + else + rc = sysfs_emit(buf, "\n"); + device_unlock(dev); + + return rc; +} + +static ssize_t driver_override_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ap_queue *aq = to_ap_queue(dev); + struct ap_device *ap_dev = &aq->ap_dev; + int rc = -EINVAL; + bool old_value; + + if (mutex_lock_interruptible(&ap_attr_mutex)) + return -ERESTARTSYS; + + /* Do not allow driver override if apmask/aqmask is in use */ + if (ap_apmask_aqmask_in_use) + goto out; + + old_value = ap_dev->driver_override ? true : false; + rc = driver_set_override(dev, &ap_dev->driver_override, buf, count); + if (rc) + goto out; + if (old_value && !ap_dev->driver_override) + --ap_driver_override_ctr; + else if (!old_value && ap_dev->driver_override) + ++ap_driver_override_ctr; + + rc = count; + +out: + mutex_unlock(&ap_attr_mutex); + return rc; +} + +static DEVICE_ATTR_RW(driver_override); + +#ifdef CONFIG_AP_DEBUG static ssize_t states_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -641,50 +793,46 @@ static ssize_t states_show(struct device *dev, /* queue device state */ switch (aq->dev_state) { case AP_DEV_STATE_UNINITIATED: - rc = scnprintf(buf, PAGE_SIZE, "UNINITIATED\n"); + rc = sysfs_emit(buf, "UNINITIATED\n"); break; case AP_DEV_STATE_OPERATING: - rc = scnprintf(buf, PAGE_SIZE, "OPERATING"); + rc = sysfs_emit(buf, "OPERATING"); break; case AP_DEV_STATE_SHUTDOWN: - rc = scnprintf(buf, PAGE_SIZE, "SHUTDOWN"); + rc = sysfs_emit(buf, "SHUTDOWN"); break; case AP_DEV_STATE_ERROR: - rc = scnprintf(buf, PAGE_SIZE, "ERROR"); + rc = sysfs_emit(buf, "ERROR"); break; default: - rc = scnprintf(buf, PAGE_SIZE, "UNKNOWN"); + rc = sysfs_emit(buf, "UNKNOWN"); } /* state machine state */ if (aq->dev_state) { switch (aq->sm_state) { case AP_SM_STATE_RESET_START: - rc += scnprintf(buf + rc, PAGE_SIZE - rc, - " [RESET_START]\n"); + rc += sysfs_emit_at(buf, rc, " [RESET_START]\n"); break; case AP_SM_STATE_RESET_WAIT: - rc += scnprintf(buf + rc, PAGE_SIZE - rc, - " [RESET_WAIT]\n"); + rc += sysfs_emit_at(buf, rc, " [RESET_WAIT]\n"); break; case AP_SM_STATE_SETIRQ_WAIT: - rc += scnprintf(buf + rc, PAGE_SIZE - rc, - " [SETIRQ_WAIT]\n"); + rc += sysfs_emit_at(buf, rc, " [SETIRQ_WAIT]\n"); break; case AP_SM_STATE_IDLE: - rc += scnprintf(buf + rc, PAGE_SIZE - rc, - " [IDLE]\n"); + rc += sysfs_emit_at(buf, rc, " [IDLE]\n"); break; case AP_SM_STATE_WORKING: - rc += scnprintf(buf + rc, PAGE_SIZE - rc, - " [WORKING]\n"); + rc += sysfs_emit_at(buf, rc, " [WORKING]\n"); break; case AP_SM_STATE_QUEUE_FULL: - rc += scnprintf(buf + rc, PAGE_SIZE - rc, - " [FULL]\n"); + rc += sysfs_emit_at(buf, rc, " [FULL]\n"); + break; + case AP_SM_STATE_ASSOC_WAIT: + rc += sysfs_emit_at(buf, rc, " [ASSOC_WAIT]\n"); break; default: - rc += scnprintf(buf + rc, PAGE_SIZE - rc, - " [UNKNOWN]\n"); + rc += sysfs_emit_at(buf, rc, " [UNKNOWN]\n"); } } spin_unlock_bh(&aq->lock); @@ -705,33 +853,33 @@ static ssize_t last_err_rc_show(struct device *dev, switch (rc) { case AP_RESPONSE_NORMAL: - return scnprintf(buf, PAGE_SIZE, "NORMAL\n"); + return sysfs_emit(buf, "NORMAL\n"); case AP_RESPONSE_Q_NOT_AVAIL: - return scnprintf(buf, PAGE_SIZE, "Q_NOT_AVAIL\n"); + return sysfs_emit(buf, "Q_NOT_AVAIL\n"); case AP_RESPONSE_RESET_IN_PROGRESS: - return scnprintf(buf, PAGE_SIZE, "RESET_IN_PROGRESS\n"); + return sysfs_emit(buf, "RESET_IN_PROGRESS\n"); case AP_RESPONSE_DECONFIGURED: - return scnprintf(buf, PAGE_SIZE, "DECONFIGURED\n"); + return sysfs_emit(buf, "DECONFIGURED\n"); case AP_RESPONSE_CHECKSTOPPED: - return scnprintf(buf, PAGE_SIZE, "CHECKSTOPPED\n"); + return sysfs_emit(buf, "CHECKSTOPPED\n"); case AP_RESPONSE_BUSY: - return scnprintf(buf, PAGE_SIZE, "BUSY\n"); + return sysfs_emit(buf, "BUSY\n"); case AP_RESPONSE_INVALID_ADDRESS: - return scnprintf(buf, PAGE_SIZE, "INVALID_ADDRESS\n"); + return sysfs_emit(buf, "INVALID_ADDRESS\n"); case AP_RESPONSE_OTHERWISE_CHANGED: - return scnprintf(buf, PAGE_SIZE, "OTHERWISE_CHANGED\n"); + return sysfs_emit(buf, "OTHERWISE_CHANGED\n"); case AP_RESPONSE_Q_FULL: - return scnprintf(buf, PAGE_SIZE, "Q_FULL/NO_PENDING_REPLY\n"); + return sysfs_emit(buf, "Q_FULL/NO_PENDING_REPLY\n"); case AP_RESPONSE_INDEX_TOO_BIG: - return scnprintf(buf, PAGE_SIZE, "INDEX_TOO_BIG\n"); + return sysfs_emit(buf, "INDEX_TOO_BIG\n"); case AP_RESPONSE_NO_FIRST_PART: - return scnprintf(buf, PAGE_SIZE, "NO_FIRST_PART\n"); + return sysfs_emit(buf, "NO_FIRST_PART\n"); case AP_RESPONSE_MESSAGE_TOO_BIG: - return scnprintf(buf, PAGE_SIZE, "MESSAGE_TOO_BIG\n"); + return sysfs_emit(buf, "MESSAGE_TOO_BIG\n"); case AP_RESPONSE_REQ_FAC_NOT_INST: - return scnprintf(buf, PAGE_SIZE, "REQ_FAC_NOT_INST\n"); + return sysfs_emit(buf, "REQ_FAC_NOT_INST\n"); default: - return scnprintf(buf, PAGE_SIZE, "response code %d\n", rc); + return sysfs_emit(buf, "response code %d\n", rc); } } static DEVICE_ATTR_RO(last_err_rc); @@ -745,7 +893,9 @@ static struct attribute *ap_queue_dev_attrs[] = { &dev_attr_interrupt.attr, &dev_attr_config.attr, &dev_attr_chkstop.attr, -#ifdef CONFIG_ZCRYPT_DEBUG + &dev_attr_ap_functions.attr, + &dev_attr_driver_override.attr, +#ifdef CONFIG_AP_DEBUG &dev_attr_states.attr, &dev_attr_last_err_rc.attr, #endif @@ -766,6 +916,258 @@ static struct device_type ap_queue_type = { .groups = ap_queue_dev_attr_groups, }; +static ssize_t se_bind_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ap_queue *aq = to_ap_queue(dev); + struct ap_queue_status status; + struct ap_tapq_hwinfo hwinfo; + + if (!ap_q_supports_bind(aq)) + return sysfs_emit(buf, "-\n"); + + status = ap_test_queue(aq->qid, 1, &hwinfo); + if (status.response_code > AP_RESPONSE_BUSY) { + pr_debug("RC 0x%02x on tapq(0x%02x.%04x)\n", + status.response_code, + AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid)); + return -EIO; + } + + /* update queue's SE bind state */ + spin_lock_bh(&aq->lock); + aq->se_bstate = hwinfo.bs; + spin_unlock_bh(&aq->lock); + + switch (hwinfo.bs) { + case AP_BS_Q_USABLE: + case AP_BS_Q_USABLE_NO_SECURE_KEY: + return sysfs_emit(buf, "bound\n"); + default: + return sysfs_emit(buf, "unbound\n"); + } +} + +static ssize_t se_bind_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ap_queue *aq = to_ap_queue(dev); + struct ap_queue_status status; + struct ap_tapq_hwinfo hwinfo; + bool value; + int rc; + + if (!ap_q_supports_bind(aq)) + return -EINVAL; + + /* only 0 (unbind) and 1 (bind) allowed */ + rc = kstrtobool(buf, &value); + if (rc) + return rc; + + if (!value) { + /* Unbind. Set F bit arg and trigger RAPQ */ + spin_lock_bh(&aq->lock); + __ap_flush_queue(aq); + aq->rapq_fbit = 1; + _ap_queue_init_state(aq); + rc = count; + goto out; + } + + /* Bind. Check current SE bind state */ + status = ap_test_queue(aq->qid, 1, &hwinfo); + if (status.response_code) { + AP_DBF_WARN("%s RC 0x%02x on tapq(0x%02x.%04x)\n", + __func__, status.response_code, + AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid)); + return -EIO; + } + + /* Update BS state */ + spin_lock_bh(&aq->lock); + aq->se_bstate = hwinfo.bs; + if (hwinfo.bs != AP_BS_Q_AVAIL_FOR_BINDING) { + AP_DBF_WARN("%s bind attempt with bs %d on queue 0x%02x.%04x\n", + __func__, hwinfo.bs, + AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid)); + rc = -EINVAL; + goto out; + } + + /* Check SM state */ + if (aq->sm_state < AP_SM_STATE_IDLE) { + rc = -EBUSY; + goto out; + } + + /* invoke BAPQ */ + status = ap_bapq(aq->qid); + if (status.response_code) { + AP_DBF_WARN("%s RC 0x%02x on bapq(0x%02x.%04x)\n", + __func__, status.response_code, + AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid)); + rc = -EIO; + goto out; + } + aq->assoc_idx = ASSOC_IDX_INVALID; + + /* verify SE bind state */ + status = ap_test_queue(aq->qid, 1, &hwinfo); + if (status.response_code) { + AP_DBF_WARN("%s RC 0x%02x on tapq(0x%02x.%04x)\n", + __func__, status.response_code, + AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid)); + rc = -EIO; + goto out; + } + aq->se_bstate = hwinfo.bs; + if (!(hwinfo.bs == AP_BS_Q_USABLE || + hwinfo.bs == AP_BS_Q_USABLE_NO_SECURE_KEY)) { + AP_DBF_WARN("%s BAPQ success, but bs shows %d on queue 0x%02x.%04x\n", + __func__, hwinfo.bs, + AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid)); + rc = -EIO; + goto out; + } + + /* SE bind was successful */ + AP_DBF_INFO("%s bapq(0x%02x.%04x) success\n", __func__, + AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid)); + rc = count; + +out: + spin_unlock_bh(&aq->lock); + return rc; +} + +static DEVICE_ATTR_RW(se_bind); + +static ssize_t se_associate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ap_queue *aq = to_ap_queue(dev); + struct ap_queue_status status; + struct ap_tapq_hwinfo hwinfo; + + if (!ap_q_supports_assoc(aq)) + return sysfs_emit(buf, "-\n"); + + status = ap_test_queue(aq->qid, 1, &hwinfo); + if (status.response_code > AP_RESPONSE_BUSY) { + pr_debug("RC 0x%02x on tapq(0x%02x.%04x)\n", + status.response_code, + AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid)); + return -EIO; + } + + /* update queue's SE bind state */ + spin_lock_bh(&aq->lock); + aq->se_bstate = hwinfo.bs; + spin_unlock_bh(&aq->lock); + + switch (hwinfo.bs) { + case AP_BS_Q_USABLE: + if (aq->assoc_idx == ASSOC_IDX_INVALID) { + AP_DBF_WARN("%s AP_BS_Q_USABLE but invalid assoc_idx\n", __func__); + return -EIO; + } + return sysfs_emit(buf, "associated %u\n", aq->assoc_idx); + case AP_BS_Q_USABLE_NO_SECURE_KEY: + if (aq->assoc_idx != ASSOC_IDX_INVALID) + return sysfs_emit(buf, "association pending\n"); + fallthrough; + default: + return sysfs_emit(buf, "unassociated\n"); + } +} + +static ssize_t se_associate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ap_queue *aq = to_ap_queue(dev); + struct ap_queue_status status; + struct ap_tapq_hwinfo hwinfo; + unsigned int value; + int rc; + + if (!ap_q_supports_assoc(aq)) + return -EINVAL; + + /* association index needs to be >= 0 */ + rc = kstrtouint(buf, 0, &value); + if (rc) + return rc; + if (value >= ASSOC_IDX_INVALID) + return -EINVAL; + + /* check current SE bind state */ + status = ap_test_queue(aq->qid, 1, &hwinfo); + if (status.response_code) { + AP_DBF_WARN("%s RC 0x%02x on tapq(0x%02x.%04x)\n", + __func__, status.response_code, + AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid)); + return -EIO; + } + spin_lock_bh(&aq->lock); + aq->se_bstate = hwinfo.bs; + if (hwinfo.bs != AP_BS_Q_USABLE_NO_SECURE_KEY) { + AP_DBF_WARN("%s association attempt with bs %d on queue 0x%02x.%04x\n", + __func__, hwinfo.bs, + AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid)); + rc = -EINVAL; + goto out; + } + + /* check SM state */ + if (aq->sm_state != AP_SM_STATE_IDLE) { + rc = -EBUSY; + goto out; + } + + /* trigger the asynchronous association request */ + status = ap_aapq(aq->qid, value); + switch (status.response_code) { + case AP_RESPONSE_NORMAL: + case AP_RESPONSE_STATE_CHANGE_IN_PROGRESS: + aq->sm_state = AP_SM_STATE_ASSOC_WAIT; + aq->assoc_idx = value; + ap_wait(ap_sm_event(aq, AP_SM_EVENT_POLL)); + break; + default: + AP_DBF_WARN("%s RC 0x%02x on aapq(0x%02x.%04x)\n", + __func__, status.response_code, + AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid)); + rc = -EIO; + goto out; + } + + rc = count; + +out: + spin_unlock_bh(&aq->lock); + return rc; +} + +static DEVICE_ATTR_RW(se_associate); + +static struct attribute *ap_queue_dev_sb_attrs[] = { + &dev_attr_se_bind.attr, + &dev_attr_se_associate.attr, + NULL +}; + +static struct attribute_group ap_queue_dev_sb_attr_group = { + .attrs = ap_queue_dev_sb_attrs +}; + +static const struct attribute_group *ap_queue_dev_sb_attr_groups[] = { + &ap_queue_dev_sb_attr_group, + NULL +}; + static void ap_queue_device_release(struct device *dev) { struct ap_queue *aq = to_ap_queue(dev); @@ -777,18 +1179,21 @@ static void ap_queue_device_release(struct device *dev) kfree(aq); } -struct ap_queue *ap_queue_create(ap_qid_t qid, int device_type) +struct ap_queue *ap_queue_create(ap_qid_t qid, struct ap_card *ac) { struct ap_queue *aq; aq = kzalloc(sizeof(*aq), GFP_KERNEL); if (!aq) return NULL; + aq->card = ac; aq->ap_dev.device.release = ap_queue_device_release; aq->ap_dev.device.type = &ap_queue_type; - aq->ap_dev.device_type = device_type; + aq->ap_dev.device_type = ac->ap_dev.device_type; + /* in SE environment add bind/associate attributes group */ + if (ap_is_se_guest() && ap_q_supported_in_se(aq)) + aq->ap_dev.device.groups = ap_queue_dev_sb_attr_groups; aq->qid = qid; - aq->interrupt = false; spin_lock_init(&aq->lock); INIT_LIST_HEAD(&aq->pendingq); INIT_LIST_HEAD(&aq->requestq); @@ -841,6 +1246,50 @@ int ap_queue_message(struct ap_queue *aq, struct ap_message *ap_msg) EXPORT_SYMBOL(ap_queue_message); /** + * ap_queue_usable(): Check if queue is usable just now. + * @aq: The AP queue device to test for usability. + * This function is intended for the scheduler to query if it makes + * sense to enqueue a message into this AP queue device by calling + * ap_queue_message(). The perspective is very short-term as the + * state machine and device state(s) may change at any time. + */ +bool ap_queue_usable(struct ap_queue *aq) +{ + bool rc = true; + + spin_lock_bh(&aq->lock); + + /* check for not configured or checkstopped */ + if (!aq->config || aq->chkstop) { + rc = false; + goto unlock_and_out; + } + + /* device state needs to be ok */ + if (aq->dev_state != AP_DEV_STATE_OPERATING) { + rc = false; + goto unlock_and_out; + } + + /* SE guest's queues additionally need to be bound */ + if (ap_is_se_guest()) { + if (!ap_q_supported_in_se(aq)) { + rc = false; + goto unlock_and_out; + } + if (ap_q_needs_bind(aq) && + !(aq->se_bstate == AP_BS_Q_USABLE || + aq->se_bstate == AP_BS_Q_USABLE_NO_SECURE_KEY)) + rc = false; + } + +unlock_and_out: + spin_unlock_bh(&aq->lock); + return rc; +} +EXPORT_SYMBOL(ap_queue_usable); + +/** * ap_cancel_message(): Cancel a crypto request. * @aq: The AP device that has the message queued * @ap_msg: The message that is to be removed @@ -910,7 +1359,7 @@ void ap_queue_prepare_remove(struct ap_queue *aq) /* move queue device state to SHUTDOWN in progress */ aq->dev_state = AP_DEV_STATE_SHUTDOWN; spin_unlock_bh(&aq->lock); - del_timer_sync(&aq->timeout); + timer_delete_sync(&aq->timeout); } void ap_queue_remove(struct ap_queue *aq) @@ -922,18 +1371,24 @@ void ap_queue_remove(struct ap_queue *aq) * to the initial value AP_DEV_STATE_UNINITIATED. */ spin_lock_bh(&aq->lock); - ap_zapq(aq->qid); + ap_zapq(aq->qid, 0); aq->dev_state = AP_DEV_STATE_UNINITIATED; spin_unlock_bh(&aq->lock); } -void ap_queue_init_state(struct ap_queue *aq) +void _ap_queue_init_state(struct ap_queue *aq) { - spin_lock_bh(&aq->lock); aq->dev_state = AP_DEV_STATE_OPERATING; aq->sm_state = AP_SM_STATE_RESET_START; aq->last_err_rc = 0; + aq->assoc_idx = ASSOC_IDX_INVALID; ap_wait(ap_sm_event(aq, AP_SM_EVENT_POLL)); +} + +void ap_queue_init_state(struct ap_queue *aq) +{ + spin_lock_bh(&aq->lock); + _ap_queue_init_state(aq); spin_unlock_bh(&aq->lock); } EXPORT_SYMBOL(ap_queue_init_state); |
