summaryrefslogtreecommitdiffstats
path: root/drivers/s390/crypto/vfio_ap_ops.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390/crypto/vfio_ap_ops.c')
-rw-r--r--drivers/s390/crypto/vfio_ap_ops.c151
1 files changed, 139 insertions, 12 deletions
diff --git a/drivers/s390/crypto/vfio_ap_ops.c b/drivers/s390/crypto/vfio_ap_ops.c
index abc0b9b88386..7dc26365e29a 100644
--- a/drivers/s390/crypto/vfio_ap_ops.c
+++ b/drivers/s390/crypto/vfio_ap_ops.c
@@ -16,10 +16,12 @@
#include <linux/bitops.h>
#include <linux/kvm_host.h>
#include <linux/module.h>
+#include <linux/uuid.h>
#include <asm/kvm.h>
#include <asm/zcrypt.h>
#include "vfio_ap_private.h"
+#include "vfio_ap_debug.h"
#define VFIO_AP_MDEV_TYPE_HWVIRT "passthrough"
#define VFIO_AP_MDEV_NAME_HWVIRT "VFIO AP Passthrough Device"
@@ -184,11 +186,43 @@ end_free:
}
/**
+ * vfio_ap_validate_nib - validate a notification indicator byte (nib) address.
+ *
+ * @vcpu: the object representing the vcpu executing the PQAP(AQIC) instruction.
+ * @nib: the location for storing the nib address.
+ * @g_pfn: the location for storing the page frame number of the page containing
+ * the nib.
+ *
+ * When the PQAP(AQIC) instruction is executed, general register 2 contains the
+ * address of the notification indicator byte (nib) used for IRQ notification.
+ * This function parses the nib from gr2 and calculates the page frame
+ * number for the guest of the page containing the nib. The values are
+ * stored in @nib and @g_pfn respectively.
+ *
+ * The g_pfn of the nib is then validated to ensure the nib address is valid.
+ *
+ * Return: returns zero if the nib address is a valid; otherwise, returns
+ * -EINVAL.
+ */
+static int vfio_ap_validate_nib(struct kvm_vcpu *vcpu, unsigned long *nib,
+ unsigned long *g_pfn)
+{
+ *nib = vcpu->run->s.regs.gprs[2];
+ *g_pfn = *nib >> PAGE_SHIFT;
+
+ if (kvm_is_error_hva(gfn_to_hva(vcpu->kvm, *g_pfn)))
+ return -EINVAL;
+
+ return 0;
+}
+
+/**
* vfio_ap_irq_enable - Enable Interruption for a APQN
*
* @q: the vfio_ap_queue holding AQIC parameters
* @isc: the guest ISC to register with the GIB interface
- * @nib: the notification indicator byte to pin.
+ * @vcpu: the vcpu object containing the registers specifying the parameters
+ * passed to the PQAP(AQIC) instruction.
*
* Pin the NIB saved in *q
* Register the guest ISC to GIB interface and retrieve the
@@ -204,22 +238,36 @@ end_free:
*/
static struct ap_queue_status vfio_ap_irq_enable(struct vfio_ap_queue *q,
int isc,
- unsigned long nib)
+ struct kvm_vcpu *vcpu)
{
+ unsigned long nib;
struct ap_qirq_ctrl aqic_gisa = {};
struct ap_queue_status status = {};
struct kvm_s390_gisa *gisa;
+ int nisc;
struct kvm *kvm;
unsigned long h_nib, g_pfn, h_pfn;
int ret;
- g_pfn = nib >> PAGE_SHIFT;
+ /* Verify that the notification indicator byte address is valid */
+ if (vfio_ap_validate_nib(vcpu, &nib, &g_pfn)) {
+ VFIO_AP_DBF_WARN("%s: invalid NIB address: nib=%#lx, g_pfn=%#lx, apqn=%#04x\n",
+ __func__, nib, g_pfn, q->apqn);
+
+ status.response_code = AP_RESPONSE_INVALID_ADDRESS;
+ return status;
+ }
+
ret = vfio_pin_pages(mdev_dev(q->matrix_mdev->mdev), &g_pfn, 1,
IOMMU_READ | IOMMU_WRITE, &h_pfn);
switch (ret) {
case 1:
break;
default:
+ VFIO_AP_DBF_WARN("%s: vfio_pin_pages failed: rc=%d,"
+ "nib=%#lx, g_pfn=%#lx, apqn=%#04x\n",
+ __func__, ret, nib, g_pfn, q->apqn);
+
status.response_code = AP_RESPONSE_INVALID_ADDRESS;
return status;
}
@@ -229,7 +277,17 @@ static struct ap_queue_status vfio_ap_irq_enable(struct vfio_ap_queue *q,
h_nib = (h_pfn << PAGE_SHIFT) | (nib & ~PAGE_MASK);
aqic_gisa.gisc = isc;
- aqic_gisa.isc = kvm_s390_gisc_register(kvm, isc);
+
+ nisc = kvm_s390_gisc_register(kvm, isc);
+ if (nisc < 0) {
+ VFIO_AP_DBF_WARN("%s: gisc registration failed: nisc=%d, isc=%d, apqn=%#04x\n",
+ __func__, nisc, isc, q->apqn);
+
+ status.response_code = AP_RESPONSE_INVALID_GISA;
+ return status;
+ }
+
+ aqic_gisa.isc = nisc;
aqic_gisa.ir = 1;
aqic_gisa.gisa = (uint64_t)gisa >> 4;
@@ -253,10 +311,62 @@ static struct ap_queue_status vfio_ap_irq_enable(struct vfio_ap_queue *q,
break;
}
+ if (status.response_code != AP_RESPONSE_NORMAL) {
+ VFIO_AP_DBF_WARN("%s: PQAP(AQIC) failed with status=%#02x: "
+ "zone=%#x, ir=%#x, gisc=%#x, f=%#x,"
+ "gisa=%#x, isc=%#x, apqn=%#04x\n",
+ __func__, status.response_code,
+ aqic_gisa.zone, aqic_gisa.ir, aqic_gisa.gisc,
+ aqic_gisa.gf, aqic_gisa.gisa, aqic_gisa.isc,
+ q->apqn);
+ }
+
return status;
}
/**
+ * vfio_ap_le_guid_to_be_uuid - convert a little endian guid array into an array
+ * of big endian elements that can be passed by
+ * value to an s390dbf sprintf event function to
+ * format a UUID string.
+ *
+ * @guid: the object containing the little endian guid
+ * @uuid: a six-element array of long values that can be passed by value as
+ * arguments for a formatting string specifying a UUID.
+ *
+ * The S390 Debug Feature (s390dbf) allows the use of "%s" in the sprintf
+ * event functions if the memory for the passed string is available as long as
+ * the debug feature exists. Since a mediated device can be removed at any
+ * time, it's name can not be used because %s passes the reference to the string
+ * in memory and the reference will go stale once the device is removed .
+ *
+ * The s390dbf string formatting function allows a maximum of 9 arguments for a
+ * message to be displayed in the 'sprintf' view. In order to use the bytes
+ * comprising the mediated device's UUID to display the mediated device name,
+ * they will have to be converted into an array whose elements can be passed by
+ * value to sprintf. For example:
+ *
+ * guid array: { 83, 78, 17, 62, bb, f1, f0, 47, 91, 4d, 32, a2, 2e, 3a, 88, 04 }
+ * mdev name: 62177883-f1bb-47f0-914d-32a22e3a8804
+ * array returned: { 62177883, f1bb, 47f0, 914d, 32a2, 2e3a8804 }
+ * formatting string: "%08lx-%04lx-%04lx-%04lx-%02lx%04lx"
+ */
+static void vfio_ap_le_guid_to_be_uuid(guid_t *guid, unsigned long *uuid)
+{
+ /*
+ * The input guid is ordered in little endian, so it needs to be
+ * reordered for displaying a UUID as a string. This specifies the
+ * guid indices in proper order.
+ */
+ uuid[0] = le32_to_cpup((__le32 *)guid);
+ uuid[1] = le16_to_cpup((__le16 *)&guid->b[4]);
+ uuid[2] = le16_to_cpup((__le16 *)&guid->b[6]);
+ uuid[3] = *((__u16 *)&guid->b[8]);
+ uuid[4] = *((__u16 *)&guid->b[10]);
+ uuid[5] = *((__u32 *)&guid->b[12]);
+}
+
+/**
* handle_pqap - PQAP instruction callback
*
* @vcpu: The vcpu on which we received the PQAP instruction
@@ -281,37 +391,54 @@ static int handle_pqap(struct kvm_vcpu *vcpu)
{
uint64_t status;
uint16_t apqn;
+ unsigned long uuid[6];
struct vfio_ap_queue *q;
struct ap_queue_status qstatus = {
.response_code = AP_RESPONSE_Q_NOT_AVAIL, };
struct ap_matrix_mdev *matrix_mdev;
+ apqn = vcpu->run->s.regs.gprs[0] & 0xffff;
+
/* If we do not use the AIV facility just go to userland */
- if (!(vcpu->arch.sie_block->eca & ECA_AIV))
+ if (!(vcpu->arch.sie_block->eca & ECA_AIV)) {
+ VFIO_AP_DBF_WARN("%s: AIV facility not installed: apqn=0x%04x, eca=0x%04x\n",
+ __func__, apqn, vcpu->arch.sie_block->eca);
+
return -EOPNOTSUPP;
+ }
- apqn = vcpu->run->s.regs.gprs[0] & 0xffff;
mutex_lock(&matrix_dev->lock);
-
- if (!vcpu->kvm->arch.crypto.pqap_hook)
+ if (!vcpu->kvm->arch.crypto.pqap_hook) {
+ VFIO_AP_DBF_WARN("%s: PQAP(AQIC) hook not registered with the vfio_ap driver: apqn=0x%04x\n",
+ __func__, apqn);
goto out_unlock;
+ }
+
matrix_mdev = container_of(vcpu->kvm->arch.crypto.pqap_hook,
struct ap_matrix_mdev, pqap_hook);
/* If the there is no guest using the mdev, there is nothing to do */
- if (!matrix_mdev->kvm)
+ if (!matrix_mdev->kvm) {
+ vfio_ap_le_guid_to_be_uuid(&matrix_mdev->mdev->uuid, uuid);
+ VFIO_AP_DBF_WARN("%s: mdev %08lx-%04lx-%04lx-%04lx-%04lx%08lx not in use: apqn=0x%04x\n",
+ __func__, uuid[0], uuid[1], uuid[2],
+ uuid[3], uuid[4], uuid[5], apqn);
goto out_unlock;
+ }
q = vfio_ap_get_queue(matrix_mdev, apqn);
- if (!q)
+ if (!q) {
+ VFIO_AP_DBF_WARN("%s: Queue %02x.%04x not bound to the vfio_ap driver\n",
+ __func__, AP_QID_CARD(apqn),
+ AP_QID_QUEUE(apqn));
goto out_unlock;
+ }
status = vcpu->run->s.regs.gprs[1];
/* If IR bit(16) is set we enable the interrupt */
if ((status >> (63 - 16)) & 0x01)
- qstatus = vfio_ap_irq_enable(q, status & 0x07,
- vcpu->run->s.regs.gprs[2]);
+ qstatus = vfio_ap_irq_enable(q, status & 0x07, vcpu);
else
qstatus = vfio_ap_irq_disable(q);