1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * VFIO based AP device driver |
4 | * |
5 | * Copyright IBM Corp. 2018 |
6 | * |
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 | |
22 | MODULE_AUTHOR("IBM Corporation" ); |
23 | MODULE_DESCRIPTION("VFIO AP device driver, Copyright IBM Corp. 2018" ); |
24 | MODULE_LICENSE("GPL v2" ); |
25 | |
26 | struct ap_matrix_dev *matrix_dev; |
27 | debug_info_t *vfio_ap_dbf_info; |
28 | |
29 | /* Only type 10 adapters (CEX4 and later) are supported |
30 | * by the AP matrix device driver |
31 | */ |
32 | static struct ap_device_id ap_queue_ids[] = { |
33 | { .dev_type = AP_DEVICE_TYPE_CEX4, |
34 | .match_flags = AP_DEVICE_ID_MATCH_QUEUE_TYPE }, |
35 | { .dev_type = AP_DEVICE_TYPE_CEX5, |
36 | .match_flags = AP_DEVICE_ID_MATCH_QUEUE_TYPE }, |
37 | { .dev_type = AP_DEVICE_TYPE_CEX6, |
38 | .match_flags = AP_DEVICE_ID_MATCH_QUEUE_TYPE }, |
39 | { .dev_type = AP_DEVICE_TYPE_CEX7, |
40 | .match_flags = AP_DEVICE_ID_MATCH_QUEUE_TYPE }, |
41 | { .dev_type = AP_DEVICE_TYPE_CEX8, |
42 | .match_flags = AP_DEVICE_ID_MATCH_QUEUE_TYPE }, |
43 | { /* end of sibling */ }, |
44 | }; |
45 | |
46 | static struct ap_driver vfio_ap_drv = { |
47 | .probe = vfio_ap_mdev_probe_queue, |
48 | .remove = vfio_ap_mdev_remove_queue, |
49 | .in_use = vfio_ap_mdev_resource_in_use, |
50 | .on_config_changed = vfio_ap_on_cfg_changed, |
51 | .on_scan_complete = vfio_ap_on_scan_complete, |
52 | .ids = ap_queue_ids, |
53 | }; |
54 | |
55 | static void vfio_ap_matrix_dev_release(struct device *dev) |
56 | { |
57 | struct ap_matrix_dev *matrix_dev; |
58 | |
59 | matrix_dev = container_of(dev, struct ap_matrix_dev, device); |
60 | kfree(objp: matrix_dev); |
61 | } |
62 | |
63 | static struct bus_type matrix_bus = { |
64 | .name = "matrix" , |
65 | }; |
66 | |
67 | static struct device_driver matrix_driver = { |
68 | .name = "vfio_ap" , |
69 | .bus = &matrix_bus, |
70 | .suppress_bind_attrs = true, |
71 | }; |
72 | |
73 | static int vfio_ap_matrix_dev_create(void) |
74 | { |
75 | int ret; |
76 | struct device *root_device; |
77 | |
78 | root_device = root_device_register(VFIO_AP_ROOT_NAME); |
79 | if (IS_ERR(ptr: root_device)) |
80 | return PTR_ERR(ptr: root_device); |
81 | |
82 | ret = bus_register(bus: &matrix_bus); |
83 | if (ret) |
84 | goto bus_register_err; |
85 | |
86 | matrix_dev = kzalloc(size: sizeof(*matrix_dev), GFP_KERNEL); |
87 | if (!matrix_dev) { |
88 | ret = -ENOMEM; |
89 | goto matrix_alloc_err; |
90 | } |
91 | |
92 | /* Fill in config info via PQAP(QCI), if available */ |
93 | if (test_facility(12)) { |
94 | ret = ap_qci(&matrix_dev->info); |
95 | if (ret) |
96 | goto matrix_alloc_err; |
97 | } |
98 | |
99 | mutex_init(&matrix_dev->mdevs_lock); |
100 | INIT_LIST_HEAD(list: &matrix_dev->mdev_list); |
101 | mutex_init(&matrix_dev->guests_lock); |
102 | |
103 | dev_set_name(dev: &matrix_dev->device, name: "%s" , VFIO_AP_DEV_NAME); |
104 | matrix_dev->device.parent = root_device; |
105 | matrix_dev->device.bus = &matrix_bus; |
106 | matrix_dev->device.release = vfio_ap_matrix_dev_release; |
107 | matrix_dev->vfio_ap_drv = &vfio_ap_drv; |
108 | |
109 | ret = device_register(dev: &matrix_dev->device); |
110 | if (ret) |
111 | goto matrix_reg_err; |
112 | |
113 | ret = driver_register(drv: &matrix_driver); |
114 | if (ret) |
115 | goto matrix_drv_err; |
116 | |
117 | return 0; |
118 | |
119 | matrix_drv_err: |
120 | device_del(dev: &matrix_dev->device); |
121 | matrix_reg_err: |
122 | put_device(dev: &matrix_dev->device); |
123 | matrix_alloc_err: |
124 | bus_unregister(bus: &matrix_bus); |
125 | bus_register_err: |
126 | root_device_unregister(root: root_device); |
127 | return ret; |
128 | } |
129 | |
130 | static void vfio_ap_matrix_dev_destroy(void) |
131 | { |
132 | struct device *root_device = matrix_dev->device.parent; |
133 | |
134 | driver_unregister(drv: &matrix_driver); |
135 | device_unregister(dev: &matrix_dev->device); |
136 | bus_unregister(bus: &matrix_bus); |
137 | root_device_unregister(root: root_device); |
138 | } |
139 | |
140 | static int __init vfio_ap_dbf_info_init(void) |
141 | { |
142 | vfio_ap_dbf_info = debug_register("vfio_ap" , 1, 1, |
143 | DBF_MAX_SPRINTF_ARGS * sizeof(long)); |
144 | |
145 | if (!vfio_ap_dbf_info) |
146 | return -ENOENT; |
147 | |
148 | debug_register_view(vfio_ap_dbf_info, &debug_sprintf_view); |
149 | debug_set_level(vfio_ap_dbf_info, DBF_WARN); |
150 | |
151 | return 0; |
152 | } |
153 | |
154 | static int __init vfio_ap_init(void) |
155 | { |
156 | int ret; |
157 | |
158 | ret = vfio_ap_dbf_info_init(); |
159 | if (ret) |
160 | return ret; |
161 | |
162 | /* If there are no AP instructions, there is nothing to pass through. */ |
163 | if (!ap_instructions_available()) |
164 | return -ENODEV; |
165 | |
166 | ret = vfio_ap_matrix_dev_create(); |
167 | if (ret) |
168 | return ret; |
169 | |
170 | ret = ap_driver_register(&vfio_ap_drv, THIS_MODULE, VFIO_AP_DRV_NAME); |
171 | if (ret) { |
172 | vfio_ap_matrix_dev_destroy(); |
173 | return ret; |
174 | } |
175 | |
176 | ret = vfio_ap_mdev_register(); |
177 | if (ret) { |
178 | ap_driver_unregister(&vfio_ap_drv); |
179 | vfio_ap_matrix_dev_destroy(); |
180 | |
181 | return ret; |
182 | } |
183 | |
184 | return 0; |
185 | } |
186 | |
187 | static void __exit vfio_ap_exit(void) |
188 | { |
189 | vfio_ap_mdev_unregister(); |
190 | ap_driver_unregister(&vfio_ap_drv); |
191 | vfio_ap_matrix_dev_destroy(); |
192 | debug_unregister(vfio_ap_dbf_info); |
193 | } |
194 | |
195 | module_init(vfio_ap_init); |
196 | module_exit(vfio_ap_exit); |
197 | |