Commit 258287c9 authored by Tony Krowiak's avatar Tony Krowiak Committed by Christian Borntraeger

s390: vfio-ap: implement mediated device open callback

Implements the open callback on the mediated matrix device.
The function registers a group notifier to receive notification
of the VFIO_GROUP_NOTIFY_SET_KVM event. When notified,
the vfio_ap device driver will get access to the guest's
kvm structure. The open callback must ensure that only one
mediated device shall be opened per guest.
Signed-off-by: default avatarTony Krowiak <akrowiak@linux.ibm.com>
Acked-by: default avatarHalil Pasic <pasic@linux.ibm.com>
Tested-by: default avatarMichael Mueller <mimu@linux.ibm.com>
Tested-by: default avatarFarhan Ali <alifm@linux.ibm.com>
Tested-by: default avatarPierre Morel <pmorel@linux.ibm.com>
Acked-by: default avatarPierre Morel <pmorel@linux.ibm.com>
Reviewed-by: default avatarCornelia Huck <cohuck@redhat.com>
Message-Id: <20180925231641.4954-12-akrowiak@linux.vnet.ibm.com>
Signed-off-by: default avatarChristian Borntraeger <borntraeger@de.ibm.com>
parent 42104598
...@@ -258,6 +258,7 @@ struct kvm_s390_sie_block { ...@@ -258,6 +258,7 @@ struct kvm_s390_sie_block {
__u64 tecmc; /* 0x00e8 */ __u64 tecmc; /* 0x00e8 */
__u8 reservedf0[12]; /* 0x00f0 */ __u8 reservedf0[12]; /* 0x00f0 */
#define CRYCB_FORMAT_MASK 0x00000003 #define CRYCB_FORMAT_MASK 0x00000003
#define CRYCB_FORMAT0 0x00000000
#define CRYCB_FORMAT1 0x00000001 #define CRYCB_FORMAT1 0x00000001
#define CRYCB_FORMAT2 0x00000003 #define CRYCB_FORMAT2 0x00000003
__u32 crycbd; /* 0x00fc */ __u32 crycbd; /* 0x00fc */
......
...@@ -13,6 +13,10 @@ ...@@ -13,6 +13,10 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/bitops.h>
#include <linux/kvm_host.h>
#include <linux/module.h>
#include <asm/kvm.h>
#include <asm/zcrypt.h> #include <asm/zcrypt.h>
#include "vfio_ap_private.h" #include "vfio_ap_private.h"
...@@ -54,6 +58,9 @@ static int vfio_ap_mdev_remove(struct mdev_device *mdev) ...@@ -54,6 +58,9 @@ static int vfio_ap_mdev_remove(struct mdev_device *mdev)
{ {
struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev); struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
if (matrix_mdev->kvm)
return -EBUSY;
mutex_lock(&matrix_dev->lock); mutex_lock(&matrix_dev->lock);
list_del(&matrix_mdev->node); list_del(&matrix_mdev->node);
mutex_unlock(&matrix_dev->lock); mutex_unlock(&matrix_dev->lock);
...@@ -305,6 +312,10 @@ static ssize_t assign_adapter_store(struct device *dev, ...@@ -305,6 +312,10 @@ static ssize_t assign_adapter_store(struct device *dev,
struct mdev_device *mdev = mdev_from_dev(dev); struct mdev_device *mdev = mdev_from_dev(dev);
struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev); struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
/* If the guest is running, disallow assignment of adapter */
if (matrix_mdev->kvm)
return -EBUSY;
ret = kstrtoul(buf, 0, &apid); ret = kstrtoul(buf, 0, &apid);
if (ret) if (ret)
return ret; return ret;
...@@ -367,6 +378,10 @@ static ssize_t unassign_adapter_store(struct device *dev, ...@@ -367,6 +378,10 @@ static ssize_t unassign_adapter_store(struct device *dev,
struct mdev_device *mdev = mdev_from_dev(dev); struct mdev_device *mdev = mdev_from_dev(dev);
struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev); struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
/* If the guest is running, disallow un-assignment of adapter */
if (matrix_mdev->kvm)
return -EBUSY;
ret = kstrtoul(buf, 0, &apid); ret = kstrtoul(buf, 0, &apid);
if (ret) if (ret)
return ret; return ret;
...@@ -444,6 +459,10 @@ static ssize_t assign_domain_store(struct device *dev, ...@@ -444,6 +459,10 @@ static ssize_t assign_domain_store(struct device *dev,
struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev); struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
unsigned long max_apqi = matrix_mdev->matrix.aqm_max; unsigned long max_apqi = matrix_mdev->matrix.aqm_max;
/* If the guest is running, disallow assignment of domain */
if (matrix_mdev->kvm)
return -EBUSY;
ret = kstrtoul(buf, 0, &apqi); ret = kstrtoul(buf, 0, &apqi);
if (ret) if (ret)
return ret; return ret;
...@@ -501,6 +520,10 @@ static ssize_t unassign_domain_store(struct device *dev, ...@@ -501,6 +520,10 @@ static ssize_t unassign_domain_store(struct device *dev,
struct mdev_device *mdev = mdev_from_dev(dev); struct mdev_device *mdev = mdev_from_dev(dev);
struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev); struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
/* If the guest is running, disallow un-assignment of domain */
if (matrix_mdev->kvm)
return -EBUSY;
ret = kstrtoul(buf, 0, &apqi); ret = kstrtoul(buf, 0, &apqi);
if (ret) if (ret)
return ret; return ret;
...@@ -541,6 +564,10 @@ static ssize_t assign_control_domain_store(struct device *dev, ...@@ -541,6 +564,10 @@ static ssize_t assign_control_domain_store(struct device *dev,
struct mdev_device *mdev = mdev_from_dev(dev); struct mdev_device *mdev = mdev_from_dev(dev);
struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev); struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
/* If the guest is running, disallow assignment of control domain */
if (matrix_mdev->kvm)
return -EBUSY;
ret = kstrtoul(buf, 0, &id); ret = kstrtoul(buf, 0, &id);
if (ret) if (ret)
return ret; return ret;
...@@ -587,6 +614,10 @@ static ssize_t unassign_control_domain_store(struct device *dev, ...@@ -587,6 +614,10 @@ static ssize_t unassign_control_domain_store(struct device *dev,
struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev); struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
unsigned long max_domid = matrix_mdev->matrix.adm_max; unsigned long max_domid = matrix_mdev->matrix.adm_max;
/* If the guest is running, disallow un-assignment of control domain */
if (matrix_mdev->kvm)
return -EBUSY;
ret = kstrtoul(buf, 0, &domid); ret = kstrtoul(buf, 0, &domid);
if (ret) if (ret)
return ret; return ret;
...@@ -696,12 +727,142 @@ static const struct attribute_group *vfio_ap_mdev_attr_groups[] = { ...@@ -696,12 +727,142 @@ static const struct attribute_group *vfio_ap_mdev_attr_groups[] = {
NULL NULL
}; };
static void vfio_ap_mdev_copy_masks(struct ap_matrix_mdev *matrix_mdev)
{
int nbytes;
unsigned long *apm, *aqm, *adm;
struct kvm_s390_crypto_cb *crycb = matrix_mdev->kvm->arch.crypto.crycb;
switch (matrix_mdev->kvm->arch.crypto.crycbd & CRYCB_FORMAT_MASK) {
case CRYCB_FORMAT2:
apm = (unsigned long *)crycb->apcb1.apm;
aqm = (unsigned long *)crycb->apcb1.aqm;
adm = (unsigned long *)crycb->apcb1.adm;
break;
case CRYCB_FORMAT1:
case CRYCB_FORMAT0:
apm = (unsigned long *)crycb->apcb0.apm;
aqm = (unsigned long *)crycb->apcb0.aqm;
adm = (unsigned long *)crycb->apcb0.adm;
break;
default:
/* cannot happen */
return;
}
nbytes = DIV_ROUND_UP(matrix_mdev->matrix.apm_max + 1, BITS_PER_BYTE);
memcpy(apm, matrix_mdev->matrix.apm, nbytes);
nbytes = DIV_ROUND_UP(matrix_mdev->matrix.aqm_max + 1, BITS_PER_BYTE);
memcpy(aqm, matrix_mdev->matrix.aqm, nbytes);
nbytes = DIV_ROUND_UP(matrix_mdev->matrix.adm_max + 1, BITS_PER_BYTE);
memcpy(adm, matrix_mdev->matrix.adm, nbytes);
}
/**
* vfio_ap_mdev_set_kvm
*
* @matrix_mdev: a mediated matrix device
* @kvm: reference to KVM instance
*
* Verifies no other mediated matrix device has @kvm and sets a reference to
* it in @matrix_mdev->kvm.
*
* Return 0 if no other mediated matrix device has a reference to @kvm;
* otherwise, returns an -EPERM.
*/
static int vfio_ap_mdev_set_kvm(struct ap_matrix_mdev *matrix_mdev,
struct kvm *kvm)
{
struct ap_matrix_mdev *m;
mutex_lock(&matrix_dev->lock);
list_for_each_entry(m, &matrix_dev->mdev_list, node) {
if ((m != matrix_mdev) && (m->kvm == kvm)) {
mutex_unlock(&matrix_dev->lock);
return -EPERM;
}
}
matrix_mdev->kvm = kvm;
mutex_unlock(&matrix_dev->lock);
return 0;
}
static int vfio_ap_mdev_group_notifier(struct notifier_block *nb,
unsigned long action, void *data)
{
int ret;
struct ap_matrix_mdev *matrix_mdev;
if (action != VFIO_GROUP_NOTIFY_SET_KVM)
return NOTIFY_OK;
matrix_mdev = container_of(nb, struct ap_matrix_mdev, group_notifier);
if (!data) {
matrix_mdev->kvm = NULL;
return NOTIFY_OK;
}
ret = vfio_ap_mdev_set_kvm(matrix_mdev, data);
if (ret)
return NOTIFY_DONE;
/* If there is no CRYCB pointer, then we can't copy the masks */
if (!matrix_mdev->kvm->arch.crypto.crycbd)
return NOTIFY_DONE;
vfio_ap_mdev_copy_masks(matrix_mdev);
return NOTIFY_OK;
}
static int vfio_ap_mdev_open(struct mdev_device *mdev)
{
struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
unsigned long events;
int ret;
if (!try_module_get(THIS_MODULE))
return -ENODEV;
matrix_mdev->group_notifier.notifier_call = vfio_ap_mdev_group_notifier;
events = VFIO_GROUP_NOTIFY_SET_KVM;
ret = vfio_register_notifier(mdev_dev(mdev), VFIO_GROUP_NOTIFY,
&events, &matrix_mdev->group_notifier);
if (ret) {
module_put(THIS_MODULE);
return ret;
}
return 0;
}
static void vfio_ap_mdev_release(struct mdev_device *mdev)
{
struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
if (matrix_mdev->kvm)
kvm_arch_crypto_clear_masks(matrix_mdev->kvm);
vfio_unregister_notifier(mdev_dev(mdev), VFIO_GROUP_NOTIFY,
&matrix_mdev->group_notifier);
matrix_mdev->kvm = NULL;
module_put(THIS_MODULE);
}
static const struct mdev_parent_ops vfio_ap_matrix_ops = { static const struct mdev_parent_ops vfio_ap_matrix_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.supported_type_groups = vfio_ap_mdev_type_groups, .supported_type_groups = vfio_ap_mdev_type_groups,
.mdev_attr_groups = vfio_ap_mdev_attr_groups, .mdev_attr_groups = vfio_ap_mdev_attr_groups,
.create = vfio_ap_mdev_create, .create = vfio_ap_mdev_create,
.remove = vfio_ap_mdev_remove, .remove = vfio_ap_mdev_remove,
.open = vfio_ap_mdev_open,
.release = vfio_ap_mdev_release,
}; };
int vfio_ap_mdev_register(void) int vfio_ap_mdev_register(void)
......
...@@ -71,10 +71,15 @@ struct ap_matrix { ...@@ -71,10 +71,15 @@ struct ap_matrix {
* @list: allows the ap_matrix_mdev struct to be added to a list * @list: allows the ap_matrix_mdev struct to be added to a list
* @matrix: the adapters, usage domains and control domains assigned to the * @matrix: the adapters, usage domains and control domains assigned to the
* mediated matrix device. * mediated matrix device.
* @group_notifier: notifier block used for specifying callback function for
* handling the VFIO_GROUP_NOTIFY_SET_KVM event
* @kvm: the struct holding guest's state
*/ */
struct ap_matrix_mdev { struct ap_matrix_mdev {
struct list_head node; struct list_head node;
struct ap_matrix matrix; struct ap_matrix matrix;
struct notifier_block group_notifier;
struct kvm *kvm;
}; };
extern int vfio_ap_mdev_register(void); extern int vfio_ap_mdev_register(void);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment