210 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * VFIO based AP device driver4 *5 * Copyright IBM Corp. 20186 *7 * Author(s): Tony Krowiak <akrowiak@linux.ibm.com>8 * Pierre Morel <pmorel@linux.ibm.com>9 */10 11#include <linux/module.h>12#include <linux/mod_devicetable.h>13#include <linux/slab.h>14#include <linux/string.h>15#include <asm/facility.h>16#include "vfio_ap_private.h"17#include "vfio_ap_debug.h"18 19#define VFIO_AP_ROOT_NAME "vfio_ap"20#define VFIO_AP_DEV_NAME "matrix"21 22MODULE_AUTHOR("IBM Corporation");23MODULE_DESCRIPTION("VFIO AP device driver, Copyright IBM Corp. 2018");24MODULE_LICENSE("GPL v2");25 26struct ap_matrix_dev *matrix_dev;27debug_info_t *vfio_ap_dbf_info;28 29static ssize_t features_show(struct device *dev, struct device_attribute *attr, char *buf)30{31 return sysfs_emit(buf, "guest_matrix hotplug ap_config\n");32}33static DEVICE_ATTR_RO(features);34 35static struct attribute *matrix_dev_attrs[] = {36 &dev_attr_features.attr,37 NULL,38};39ATTRIBUTE_GROUPS(matrix_dev);40 41/* Only type 10 adapters (CEX4 and later) are supported42 * by the AP matrix device driver43 */44static struct ap_device_id ap_queue_ids[] = {45 { .dev_type = AP_DEVICE_TYPE_CEX4,46 .match_flags = AP_DEVICE_ID_MATCH_QUEUE_TYPE },47 { .dev_type = AP_DEVICE_TYPE_CEX5,48 .match_flags = AP_DEVICE_ID_MATCH_QUEUE_TYPE },49 { .dev_type = AP_DEVICE_TYPE_CEX6,50 .match_flags = AP_DEVICE_ID_MATCH_QUEUE_TYPE },51 { .dev_type = AP_DEVICE_TYPE_CEX7,52 .match_flags = AP_DEVICE_ID_MATCH_QUEUE_TYPE },53 { .dev_type = AP_DEVICE_TYPE_CEX8,54 .match_flags = AP_DEVICE_ID_MATCH_QUEUE_TYPE },55 { /* end of sibling */ },56};57 58static struct ap_driver vfio_ap_drv = {59 .probe = vfio_ap_mdev_probe_queue,60 .remove = vfio_ap_mdev_remove_queue,61 .in_use = vfio_ap_mdev_resource_in_use,62 .on_config_changed = vfio_ap_on_cfg_changed,63 .on_scan_complete = vfio_ap_on_scan_complete,64 .ids = ap_queue_ids,65};66 67static void vfio_ap_matrix_dev_release(struct device *dev)68{69 struct ap_matrix_dev *matrix_dev;70 71 matrix_dev = container_of(dev, struct ap_matrix_dev, device);72 kfree(matrix_dev);73}74 75static const struct bus_type matrix_bus = {76 .name = "matrix",77};78 79static struct device_driver matrix_driver = {80 .name = "vfio_ap",81 .bus = &matrix_bus,82 .suppress_bind_attrs = true,83 .dev_groups = matrix_dev_groups,84};85 86static int vfio_ap_matrix_dev_create(void)87{88 int ret;89 struct device *root_device;90 91 root_device = root_device_register(VFIO_AP_ROOT_NAME);92 if (IS_ERR(root_device))93 return PTR_ERR(root_device);94 95 ret = bus_register(&matrix_bus);96 if (ret)97 goto bus_register_err;98 99 matrix_dev = kzalloc(sizeof(*matrix_dev), GFP_KERNEL);100 if (!matrix_dev) {101 ret = -ENOMEM;102 goto matrix_alloc_err;103 }104 105 /* Fill in config info via PQAP(QCI), if available */106 if (test_facility(12)) {107 ret = ap_qci(&matrix_dev->info);108 if (ret)109 goto matrix_alloc_err;110 }111 112 mutex_init(&matrix_dev->mdevs_lock);113 INIT_LIST_HEAD(&matrix_dev->mdev_list);114 mutex_init(&matrix_dev->guests_lock);115 116 dev_set_name(&matrix_dev->device, "%s", VFIO_AP_DEV_NAME);117 matrix_dev->device.parent = root_device;118 matrix_dev->device.bus = &matrix_bus;119 matrix_dev->device.release = vfio_ap_matrix_dev_release;120 matrix_dev->vfio_ap_drv = &vfio_ap_drv;121 122 ret = device_register(&matrix_dev->device);123 if (ret)124 goto matrix_reg_err;125 126 ret = driver_register(&matrix_driver);127 if (ret)128 goto matrix_drv_err;129 130 return 0;131 132matrix_drv_err:133 device_del(&matrix_dev->device);134matrix_reg_err:135 put_device(&matrix_dev->device);136matrix_alloc_err:137 bus_unregister(&matrix_bus);138bus_register_err:139 root_device_unregister(root_device);140 return ret;141}142 143static void vfio_ap_matrix_dev_destroy(void)144{145 struct device *root_device = matrix_dev->device.parent;146 147 driver_unregister(&matrix_driver);148 device_unregister(&matrix_dev->device);149 bus_unregister(&matrix_bus);150 root_device_unregister(root_device);151}152 153static int __init vfio_ap_dbf_info_init(void)154{155 vfio_ap_dbf_info = debug_register("vfio_ap", 1, 1,156 DBF_MAX_SPRINTF_ARGS * sizeof(long));157 158 if (!vfio_ap_dbf_info)159 return -ENOENT;160 161 debug_register_view(vfio_ap_dbf_info, &debug_sprintf_view);162 debug_set_level(vfio_ap_dbf_info, DBF_WARN);163 164 return 0;165}166 167static int __init vfio_ap_init(void)168{169 int ret;170 171 ret = vfio_ap_dbf_info_init();172 if (ret)173 return ret;174 175 /* If there are no AP instructions, there is nothing to pass through. */176 if (!ap_instructions_available())177 return -ENODEV;178 179 ret = vfio_ap_matrix_dev_create();180 if (ret)181 return ret;182 183 ret = ap_driver_register(&vfio_ap_drv, THIS_MODULE, VFIO_AP_DRV_NAME);184 if (ret) {185 vfio_ap_matrix_dev_destroy();186 return ret;187 }188 189 ret = vfio_ap_mdev_register();190 if (ret) {191 ap_driver_unregister(&vfio_ap_drv);192 vfio_ap_matrix_dev_destroy();193 194 return ret;195 }196 197 return 0;198}199 200static void __exit vfio_ap_exit(void)201{202 vfio_ap_mdev_unregister();203 ap_driver_unregister(&vfio_ap_drv);204 vfio_ap_matrix_dev_destroy();205 debug_unregister(vfio_ap_dbf_info);206}207 208module_init(vfio_ap_init);209module_exit(vfio_ap_exit);210