goto matrix_alloc_err;
        }
 
+       /* Fill in config info via PQAP(QCI), if available */
+       if (test_facility(12)) {
+               ret = ap_qci(&matrix_dev->info);
+               if (ret)
+                       goto matrix_alloc_err;
+       }
+
+       mutex_init(&matrix_dev->lock);
+       INIT_LIST_HEAD(&matrix_dev->mdev_list);
+
        matrix_dev->device.type = &vfio_ap_dev_type;
        dev_set_name(&matrix_dev->device, "%s", VFIO_AP_DEV_NAME);
        matrix_dev->device.parent = root_device;
                return ret;
        }
 
+       ret = vfio_ap_mdev_register();
+       if (ret) {
+               ap_driver_unregister(&vfio_ap_drv);
+               vfio_ap_matrix_dev_destroy();
+
+               return ret;
+       }
+
        return 0;
 }
 
 void __exit vfio_ap_exit(void)
 {
+       vfio_ap_mdev_unregister();
        ap_driver_unregister(&vfio_ap_drv);
        vfio_ap_matrix_dev_destroy();
 }
 
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Adjunct processor matrix VFIO device driver callbacks.
+ *
+ * Copyright IBM Corp. 2018
+ *
+ * Author(s): Tony Krowiak <akrowiak@linux.ibm.com>
+ *           Halil Pasic <pasic@linux.ibm.com>
+ *           Pierre Morel <pmorel@linux.ibm.com>
+ */
+#include <linux/string.h>
+#include <linux/vfio.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/ctype.h>
+#include <asm/zcrypt.h>
+
+#include "vfio_ap_private.h"
+
+#define VFIO_AP_MDEV_TYPE_HWVIRT "passthrough"
+#define VFIO_AP_MDEV_NAME_HWVIRT "VFIO AP Passthrough Device"
+
+static void vfio_ap_matrix_init(struct ap_config_info *info,
+                               struct ap_matrix *matrix)
+{
+       matrix->apm_max = info->apxa ? info->Na : 63;
+       matrix->aqm_max = info->apxa ? info->Nd : 15;
+       matrix->adm_max = info->apxa ? info->Nd : 15;
+}
+
+static int vfio_ap_mdev_create(struct kobject *kobj, struct mdev_device *mdev)
+{
+       struct ap_matrix_mdev *matrix_mdev;
+
+       if ((atomic_dec_if_positive(&matrix_dev->available_instances) < 0))
+               return -EPERM;
+
+       matrix_mdev = kzalloc(sizeof(*matrix_mdev), GFP_KERNEL);
+       if (!matrix_mdev) {
+               atomic_inc(&matrix_dev->available_instances);
+               return -ENOMEM;
+       }
+
+       vfio_ap_matrix_init(&matrix_dev->info, &matrix_mdev->matrix);
+       mdev_set_drvdata(mdev, matrix_mdev);
+       mutex_lock(&matrix_dev->lock);
+       list_add(&matrix_mdev->node, &matrix_dev->mdev_list);
+       mutex_unlock(&matrix_dev->lock);
+
+       return 0;
+}
+
+static int vfio_ap_mdev_remove(struct mdev_device *mdev)
+{
+       struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
+
+       mutex_lock(&matrix_dev->lock);
+       list_del(&matrix_mdev->node);
+       mutex_unlock(&matrix_dev->lock);
+
+       kfree(matrix_mdev);
+       mdev_set_drvdata(mdev, NULL);
+       atomic_inc(&matrix_dev->available_instances);
+
+       return 0;
+}
+
+static ssize_t name_show(struct kobject *kobj, struct device *dev, char *buf)
+{
+       return sprintf(buf, "%s\n", VFIO_AP_MDEV_NAME_HWVIRT);
+}
+
+MDEV_TYPE_ATTR_RO(name);
+
+static ssize_t available_instances_show(struct kobject *kobj,
+                                       struct device *dev, char *buf)
+{
+       return sprintf(buf, "%d\n",
+                      atomic_read(&matrix_dev->available_instances));
+}
+
+MDEV_TYPE_ATTR_RO(available_instances);
+
+static ssize_t device_api_show(struct kobject *kobj, struct device *dev,
+                              char *buf)
+{
+       return sprintf(buf, "%s\n", VFIO_DEVICE_API_AP_STRING);
+}
+
+MDEV_TYPE_ATTR_RO(device_api);
+
+static struct attribute *vfio_ap_mdev_type_attrs[] = {
+       &mdev_type_attr_name.attr,
+       &mdev_type_attr_device_api.attr,
+       &mdev_type_attr_available_instances.attr,
+       NULL,
+};
+
+static struct attribute_group vfio_ap_mdev_hwvirt_type_group = {
+       .name = VFIO_AP_MDEV_TYPE_HWVIRT,
+       .attrs = vfio_ap_mdev_type_attrs,
+};
+
+static struct attribute_group *vfio_ap_mdev_type_groups[] = {
+       &vfio_ap_mdev_hwvirt_type_group,
+       NULL,
+};
+
+static const struct mdev_parent_ops vfio_ap_matrix_ops = {
+       .owner                  = THIS_MODULE,
+       .supported_type_groups  = vfio_ap_mdev_type_groups,
+       .create                 = vfio_ap_mdev_create,
+       .remove                 = vfio_ap_mdev_remove,
+};
+
+int vfio_ap_mdev_register(void)
+{
+       atomic_set(&matrix_dev->available_instances, MAX_ZDEV_ENTRIES_EXT);
+
+       return mdev_register_device(&matrix_dev->device, &vfio_ap_matrix_ops);
+}
+
+void vfio_ap_mdev_unregister(void)
+{
+       mdev_unregister_device(&matrix_dev->device);
+}
 
  * Private data and functions for adjunct processor VFIO matrix driver.
  *
  * Author(s): Tony Krowiak <akrowiak@linux.ibm.com>
+ *           Halil Pasic <pasic@linux.ibm.com>
  *
  * Copyright IBM Corp. 2018
  */
 /**
  * ap_matrix_dev - the AP matrix device structure
  * @device:    generic device structure associated with the AP matrix device
+ * @available_instances: number of mediated matrix devices that can be created
+ * @info:      the struct containing the output from the PQAP(QCI) instruction
+ * mdev_list:  the list of mediated matrix devices created
+ * lock:       mutex for locking the AP matrix device. This lock will be
+ *             taken every time we fiddle with state managed by the vfio_ap
+ *             driver, be it using @mdev_list or writing the state of a
+ *             single ap_matrix_mdev device. It's quite coarse but we don't
+ *             expect much contention.
  */
 struct ap_matrix_dev {
        struct device device;
+       atomic_t available_instances;
+       struct ap_config_info info;
+       struct list_head mdev_list;
+       struct mutex lock;
 };
 
 extern struct ap_matrix_dev *matrix_dev;
 
+/**
+ * The AP matrix is comprised of three bit masks identifying the adapters,
+ * queues (domains) and control domains that belong to an AP matrix. The bits i
+ * each mask, from least significant to most significant bit, correspond to IDs
+ * 0 to 255. When a bit is set, the corresponding ID belongs to the matrix.
+ *
+ * @apm_max: max adapter number in @apm
+ * @apm identifies the AP adapters in the matrix
+ * @aqm_max: max domain number in @aqm
+ * @aqm identifies the AP queues (domains) in the matrix
+ * @adm_max: max domain number in @adm
+ * @adm identifies the AP control domains in the matrix
+ */
+struct ap_matrix {
+       unsigned long apm_max;
+       DECLARE_BITMAP(apm, 256);
+       unsigned long aqm_max;
+       DECLARE_BITMAP(aqm, 256);
+       unsigned long adm_max;
+       DECLARE_BITMAP(adm, 256);
+};
+
+/**
+ * struct ap_matrix_mdev - the mediated matrix device structure
+ * @list:      allows the ap_matrix_mdev struct to be added to a list
+ * @matrix:    the adapters, usage domains and control domains assigned to the
+ *             mediated matrix device.
+ */
+struct ap_matrix_mdev {
+       struct list_head node;
+       struct ap_matrix matrix;
+};
+
+extern int vfio_ap_mdev_register(void);
+extern void vfio_ap_mdev_unregister(void);
+
 #endif /* _VFIO_AP_PRIVATE_H_ */