diff options
author | Gerd Hoffmann <kraxel@redhat.com> | 2018-05-11 11:05:03 -0400 |
---|---|---|
committer | Alex Williamson <alex.williamson@redhat.com> | 2018-06-08 12:24:09 -0400 |
commit | d61fc96f47fdac1f031ed4eafa9106fe10cdaa37 (patch) | |
tree | 08b7ef5d11978851ada2d64d7e68d87a85de59a6 | |
parent | 48d8476b41eed63567dd2f0ad125c895b9ac648a (diff) |
sample: vfio mdev display - host device
Simple framebuffer display, demo-ing the vfio region display interface
(VFIO_GFX_PLANE_TYPE_REGION).
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
-rw-r--r-- | samples/Kconfig | 8 | ||||
-rw-r--r-- | samples/vfio-mdev/Makefile | 1 | ||||
-rw-r--r-- | samples/vfio-mdev/mdpy-defs.h | 22 | ||||
-rw-r--r-- | samples/vfio-mdev/mdpy.c | 807 |
4 files changed, 838 insertions, 0 deletions
diff --git a/samples/Kconfig b/samples/Kconfig index 3db002b9e1d3..960f19b24df0 100644 --- a/samples/Kconfig +++ b/samples/Kconfig | |||
@@ -115,6 +115,14 @@ config SAMPLE_VFIO_MDEV_MTTY | |||
115 | Build a virtual tty sample driver for use as a VFIO | 115 | Build a virtual tty sample driver for use as a VFIO |
116 | mediated device | 116 | mediated device |
117 | 117 | ||
118 | config SAMPLE_VFIO_MDEV_MDPY | ||
119 | tristate "Build VFIO mdpy example mediated device sample code -- loadable modules only" | ||
120 | depends on VFIO_MDEV_DEVICE && m | ||
121 | help | ||
122 | Build a virtual display sample driver for use as a VFIO | ||
123 | mediated device. It is a simple framebuffer and supports | ||
124 | the region display interface (VFIO_GFX_PLANE_TYPE_REGION). | ||
125 | |||
118 | config SAMPLE_STATX | 126 | config SAMPLE_STATX |
119 | bool "Build example extended-stat using code" | 127 | bool "Build example extended-stat using code" |
120 | depends on BROKEN | 128 | depends on BROKEN |
diff --git a/samples/vfio-mdev/Makefile b/samples/vfio-mdev/Makefile index cbbd868a50a8..031d6b88e9e9 100644 --- a/samples/vfio-mdev/Makefile +++ b/samples/vfio-mdev/Makefile | |||
@@ -1 +1,2 @@ | |||
1 | obj-$(CONFIG_SAMPLE_VFIO_MDEV_MTTY) += mtty.o | 1 | obj-$(CONFIG_SAMPLE_VFIO_MDEV_MTTY) += mtty.o |
2 | obj-$(CONFIG_SAMPLE_VFIO_MDEV_MDPY) += mdpy.o | ||
diff --git a/samples/vfio-mdev/mdpy-defs.h b/samples/vfio-mdev/mdpy-defs.h new file mode 100644 index 000000000000..96b3b1b49d34 --- /dev/null +++ b/samples/vfio-mdev/mdpy-defs.h | |||
@@ -0,0 +1,22 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ | ||
2 | /* | ||
3 | * Simple pci display device. | ||
4 | * | ||
5 | * Framebuffer memory is pci bar 0. | ||
6 | * Configuration (read-only) is in pci config space. | ||
7 | * Format field uses drm fourcc codes. | ||
8 | * ATM only DRM_FORMAT_XRGB8888 is supported. | ||
9 | */ | ||
10 | |||
11 | /* pci ids */ | ||
12 | #define MDPY_PCI_VENDOR_ID 0x1b36 /* redhat */ | ||
13 | #define MDPY_PCI_DEVICE_ID 0x000f | ||
14 | #define MDPY_PCI_SUBVENDOR_ID PCI_SUBVENDOR_ID_REDHAT_QUMRANET | ||
15 | #define MDPY_PCI_SUBDEVICE_ID PCI_SUBDEVICE_ID_QEMU | ||
16 | |||
17 | /* pci cfg space offsets for fb config (dword) */ | ||
18 | #define MDPY_VENDORCAP_OFFSET 0x40 | ||
19 | #define MDPY_VENDORCAP_SIZE 0x10 | ||
20 | #define MDPY_FORMAT_OFFSET (MDPY_VENDORCAP_OFFSET + 0x04) | ||
21 | #define MDPY_WIDTH_OFFSET (MDPY_VENDORCAP_OFFSET + 0x08) | ||
22 | #define MDPY_HEIGHT_OFFSET (MDPY_VENDORCAP_OFFSET + 0x0c) | ||
diff --git a/samples/vfio-mdev/mdpy.c b/samples/vfio-mdev/mdpy.c new file mode 100644 index 000000000000..96e7969c473a --- /dev/null +++ b/samples/vfio-mdev/mdpy.c | |||
@@ -0,0 +1,807 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Mediated virtual PCI display host device driver | ||
4 | * | ||
5 | * See mdpy-defs.h for device specs | ||
6 | * | ||
7 | * (c) Gerd Hoffmann <kraxel@redhat.com> | ||
8 | * | ||
9 | * based on mtty driver which is: | ||
10 | * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. | ||
11 | * Author: Neo Jia <cjia@nvidia.com> | ||
12 | * Kirti Wankhede <kwankhede@nvidia.com> | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or modify | ||
15 | * it under the terms of the GNU General Public License version 2 as | ||
16 | * published by the Free Software Foundation. | ||
17 | */ | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/device.h> | ||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/slab.h> | ||
23 | #include <linux/vmalloc.h> | ||
24 | #include <linux/cdev.h> | ||
25 | #include <linux/vfio.h> | ||
26 | #include <linux/iommu.h> | ||
27 | #include <linux/sysfs.h> | ||
28 | #include <linux/mdev.h> | ||
29 | #include <linux/pci.h> | ||
30 | #include <drm/drm_fourcc.h> | ||
31 | #include "mdpy-defs.h" | ||
32 | |||
33 | #define MDPY_NAME "mdpy" | ||
34 | #define MDPY_CLASS_NAME "mdpy" | ||
35 | |||
36 | #define MDPY_CONFIG_SPACE_SIZE 0xff | ||
37 | #define MDPY_MEMORY_BAR_OFFSET PAGE_SIZE | ||
38 | #define MDPY_DISPLAY_REGION 16 | ||
39 | |||
40 | #define STORE_LE16(addr, val) (*(u16 *)addr = val) | ||
41 | #define STORE_LE32(addr, val) (*(u32 *)addr = val) | ||
42 | |||
43 | |||
44 | MODULE_LICENSE("GPL v2"); | ||
45 | |||
46 | static int max_devices = 4; | ||
47 | module_param_named(count, max_devices, int, 0444); | ||
48 | MODULE_PARM_DESC(count, "number of " MDPY_NAME " devices"); | ||
49 | |||
50 | |||
51 | #define MDPY_TYPE_1 "vga" | ||
52 | #define MDPY_TYPE_2 "xga" | ||
53 | #define MDPY_TYPE_3 "hd" | ||
54 | |||
55 | static const struct mdpy_type { | ||
56 | const char *name; | ||
57 | u32 format; | ||
58 | u32 bytepp; | ||
59 | u32 width; | ||
60 | u32 height; | ||
61 | } mdpy_types[] = { | ||
62 | { | ||
63 | .name = MDPY_CLASS_NAME "-" MDPY_TYPE_1, | ||
64 | .format = DRM_FORMAT_XRGB8888, | ||
65 | .bytepp = 4, | ||
66 | .width = 640, | ||
67 | .height = 480, | ||
68 | }, { | ||
69 | .name = MDPY_CLASS_NAME "-" MDPY_TYPE_2, | ||
70 | .format = DRM_FORMAT_XRGB8888, | ||
71 | .bytepp = 4, | ||
72 | .width = 1024, | ||
73 | .height = 768, | ||
74 | }, { | ||
75 | .name = MDPY_CLASS_NAME "-" MDPY_TYPE_3, | ||
76 | .format = DRM_FORMAT_XRGB8888, | ||
77 | .bytepp = 4, | ||
78 | .width = 1920, | ||
79 | .height = 1080, | ||
80 | }, | ||
81 | }; | ||
82 | |||
83 | static dev_t mdpy_devt; | ||
84 | static struct class *mdpy_class; | ||
85 | static struct cdev mdpy_cdev; | ||
86 | static struct device mdpy_dev; | ||
87 | static u32 mdpy_count; | ||
88 | |||
89 | /* State of each mdev device */ | ||
90 | struct mdev_state { | ||
91 | u8 *vconfig; | ||
92 | u32 bar_mask; | ||
93 | struct mutex ops_lock; | ||
94 | struct mdev_device *mdev; | ||
95 | struct vfio_device_info dev_info; | ||
96 | |||
97 | const struct mdpy_type *type; | ||
98 | u32 memsize; | ||
99 | void *memblk; | ||
100 | }; | ||
101 | |||
102 | static const struct mdpy_type *mdpy_find_type(struct kobject *kobj) | ||
103 | { | ||
104 | int i; | ||
105 | |||
106 | for (i = 0; i < ARRAY_SIZE(mdpy_types); i++) | ||
107 | if (strcmp(mdpy_types[i].name, kobj->name) == 0) | ||
108 | return mdpy_types + i; | ||
109 | return NULL; | ||
110 | } | ||
111 | |||
112 | static void mdpy_create_config_space(struct mdev_state *mdev_state) | ||
113 | { | ||
114 | STORE_LE16((u16 *) &mdev_state->vconfig[PCI_VENDOR_ID], | ||
115 | MDPY_PCI_VENDOR_ID); | ||
116 | STORE_LE16((u16 *) &mdev_state->vconfig[PCI_DEVICE_ID], | ||
117 | MDPY_PCI_DEVICE_ID); | ||
118 | STORE_LE16((u16 *) &mdev_state->vconfig[PCI_SUBSYSTEM_VENDOR_ID], | ||
119 | MDPY_PCI_SUBVENDOR_ID); | ||
120 | STORE_LE16((u16 *) &mdev_state->vconfig[PCI_SUBSYSTEM_ID], | ||
121 | MDPY_PCI_SUBDEVICE_ID); | ||
122 | |||
123 | STORE_LE16((u16 *) &mdev_state->vconfig[PCI_COMMAND], | ||
124 | PCI_COMMAND_IO | PCI_COMMAND_MEMORY); | ||
125 | STORE_LE16((u16 *) &mdev_state->vconfig[PCI_STATUS], | ||
126 | PCI_STATUS_CAP_LIST); | ||
127 | STORE_LE16((u16 *) &mdev_state->vconfig[PCI_CLASS_DEVICE], | ||
128 | PCI_CLASS_DISPLAY_OTHER); | ||
129 | mdev_state->vconfig[PCI_CLASS_REVISION] = 0x01; | ||
130 | |||
131 | STORE_LE32((u32 *) &mdev_state->vconfig[PCI_BASE_ADDRESS_0], | ||
132 | PCI_BASE_ADDRESS_SPACE_MEMORY | | ||
133 | PCI_BASE_ADDRESS_MEM_TYPE_32 | | ||
134 | PCI_BASE_ADDRESS_MEM_PREFETCH); | ||
135 | mdev_state->bar_mask = ~(mdev_state->memsize) + 1; | ||
136 | |||
137 | /* vendor specific capability for the config registers */ | ||
138 | mdev_state->vconfig[PCI_CAPABILITY_LIST] = MDPY_VENDORCAP_OFFSET; | ||
139 | mdev_state->vconfig[MDPY_VENDORCAP_OFFSET + 0] = 0x09; /* vendor cap */ | ||
140 | mdev_state->vconfig[MDPY_VENDORCAP_OFFSET + 1] = 0x00; /* next ptr */ | ||
141 | mdev_state->vconfig[MDPY_VENDORCAP_OFFSET + 2] = MDPY_VENDORCAP_SIZE; | ||
142 | STORE_LE32((u32 *) &mdev_state->vconfig[MDPY_FORMAT_OFFSET], | ||
143 | mdev_state->type->format); | ||
144 | STORE_LE32((u32 *) &mdev_state->vconfig[MDPY_WIDTH_OFFSET], | ||
145 | mdev_state->type->width); | ||
146 | STORE_LE32((u32 *) &mdev_state->vconfig[MDPY_HEIGHT_OFFSET], | ||
147 | mdev_state->type->height); | ||
148 | } | ||
149 | |||
150 | static void handle_pci_cfg_write(struct mdev_state *mdev_state, u16 offset, | ||
151 | char *buf, u32 count) | ||
152 | { | ||
153 | struct device *dev = mdev_dev(mdev_state->mdev); | ||
154 | u32 cfg_addr; | ||
155 | |||
156 | switch (offset) { | ||
157 | case PCI_BASE_ADDRESS_0: | ||
158 | cfg_addr = *(u32 *)buf; | ||
159 | |||
160 | if (cfg_addr == 0xffffffff) { | ||
161 | cfg_addr = (cfg_addr & mdev_state->bar_mask); | ||
162 | } else { | ||
163 | cfg_addr &= PCI_BASE_ADDRESS_MEM_MASK; | ||
164 | if (cfg_addr) | ||
165 | dev_info(dev, "BAR0 @ 0x%x\n", cfg_addr); | ||
166 | } | ||
167 | |||
168 | cfg_addr |= (mdev_state->vconfig[offset] & | ||
169 | ~PCI_BASE_ADDRESS_MEM_MASK); | ||
170 | STORE_LE32(&mdev_state->vconfig[offset], cfg_addr); | ||
171 | break; | ||
172 | } | ||
173 | } | ||
174 | |||
175 | static ssize_t mdev_access(struct mdev_device *mdev, char *buf, size_t count, | ||
176 | loff_t pos, bool is_write) | ||
177 | { | ||
178 | struct mdev_state *mdev_state = mdev_get_drvdata(mdev); | ||
179 | struct device *dev = mdev_dev(mdev); | ||
180 | int ret = 0; | ||
181 | |||
182 | mutex_lock(&mdev_state->ops_lock); | ||
183 | |||
184 | if (pos < MDPY_CONFIG_SPACE_SIZE) { | ||
185 | if (is_write) | ||
186 | handle_pci_cfg_write(mdev_state, pos, buf, count); | ||
187 | else | ||
188 | memcpy(buf, (mdev_state->vconfig + pos), count); | ||
189 | |||
190 | } else if ((pos >= MDPY_MEMORY_BAR_OFFSET) && | ||
191 | (pos + count <= | ||
192 | MDPY_MEMORY_BAR_OFFSET + mdev_state->memsize)) { | ||
193 | pos -= MDPY_MEMORY_BAR_OFFSET; | ||
194 | if (is_write) | ||
195 | memcpy(mdev_state->memblk, buf, count); | ||
196 | else | ||
197 | memcpy(buf, mdev_state->memblk, count); | ||
198 | |||
199 | } else { | ||
200 | dev_info(dev, "%s: %s @0x%llx (unhandled)\n", | ||
201 | __func__, is_write ? "WR" : "RD", pos); | ||
202 | ret = -1; | ||
203 | goto accessfailed; | ||
204 | } | ||
205 | |||
206 | ret = count; | ||
207 | |||
208 | |||
209 | accessfailed: | ||
210 | mutex_unlock(&mdev_state->ops_lock); | ||
211 | |||
212 | return ret; | ||
213 | } | ||
214 | |||
215 | static int mdpy_reset(struct mdev_device *mdev) | ||
216 | { | ||
217 | struct mdev_state *mdev_state = mdev_get_drvdata(mdev); | ||
218 | u32 stride, i; | ||
219 | |||
220 | /* initialize with gray gradient */ | ||
221 | stride = mdev_state->type->width * mdev_state->type->bytepp; | ||
222 | for (i = 0; i < mdev_state->type->height; i++) | ||
223 | memset(mdev_state->memblk + i * stride, | ||
224 | i * 255 / mdev_state->type->height, | ||
225 | stride); | ||
226 | return 0; | ||
227 | } | ||
228 | |||
229 | static int mdpy_create(struct kobject *kobj, struct mdev_device *mdev) | ||
230 | { | ||
231 | const struct mdpy_type *type = mdpy_find_type(kobj); | ||
232 | struct device *dev = mdev_dev(mdev); | ||
233 | struct mdev_state *mdev_state; | ||
234 | u32 fbsize; | ||
235 | |||
236 | if (mdpy_count >= max_devices) | ||
237 | return -ENOMEM; | ||
238 | |||
239 | mdev_state = kzalloc(sizeof(struct mdev_state), GFP_KERNEL); | ||
240 | if (mdev_state == NULL) | ||
241 | return -ENOMEM; | ||
242 | |||
243 | mdev_state->vconfig = kzalloc(MDPY_CONFIG_SPACE_SIZE, GFP_KERNEL); | ||
244 | if (mdev_state->vconfig == NULL) { | ||
245 | kfree(mdev_state); | ||
246 | return -ENOMEM; | ||
247 | } | ||
248 | |||
249 | if (!type) | ||
250 | type = &mdpy_types[0]; | ||
251 | fbsize = roundup_pow_of_two(type->width * type->height * type->bytepp); | ||
252 | |||
253 | mdev_state->memblk = vmalloc_user(fbsize); | ||
254 | if (!mdev_state->memblk) { | ||
255 | kfree(mdev_state->vconfig); | ||
256 | kfree(mdev_state); | ||
257 | return -ENOMEM; | ||
258 | } | ||
259 | dev_info(dev, "%s: %s (%dx%d)\n", | ||
260 | __func__, kobj->name, type->width, type->height); | ||
261 | |||
262 | mutex_init(&mdev_state->ops_lock); | ||
263 | mdev_state->mdev = mdev; | ||
264 | mdev_set_drvdata(mdev, mdev_state); | ||
265 | |||
266 | mdev_state->type = type; | ||
267 | mdev_state->memsize = fbsize; | ||
268 | mdpy_create_config_space(mdev_state); | ||
269 | mdpy_reset(mdev); | ||
270 | |||
271 | mdpy_count++; | ||
272 | return 0; | ||
273 | } | ||
274 | |||
275 | static int mdpy_remove(struct mdev_device *mdev) | ||
276 | { | ||
277 | struct mdev_state *mdev_state = mdev_get_drvdata(mdev); | ||
278 | struct device *dev = mdev_dev(mdev); | ||
279 | |||
280 | dev_info(dev, "%s\n", __func__); | ||
281 | |||
282 | mdev_set_drvdata(mdev, NULL); | ||
283 | vfree(mdev_state->memblk); | ||
284 | kfree(mdev_state->vconfig); | ||
285 | kfree(mdev_state); | ||
286 | |||
287 | mdpy_count--; | ||
288 | return 0; | ||
289 | } | ||
290 | |||
291 | static ssize_t mdpy_read(struct mdev_device *mdev, char __user *buf, | ||
292 | size_t count, loff_t *ppos) | ||
293 | { | ||
294 | unsigned int done = 0; | ||
295 | int ret; | ||
296 | |||
297 | while (count) { | ||
298 | size_t filled; | ||
299 | |||
300 | if (count >= 4 && !(*ppos % 4)) { | ||
301 | u32 val; | ||
302 | |||
303 | ret = mdev_access(mdev, (char *)&val, sizeof(val), | ||
304 | *ppos, false); | ||
305 | if (ret <= 0) | ||
306 | goto read_err; | ||
307 | |||
308 | if (copy_to_user(buf, &val, sizeof(val))) | ||
309 | goto read_err; | ||
310 | |||
311 | filled = 4; | ||
312 | } else if (count >= 2 && !(*ppos % 2)) { | ||
313 | u16 val; | ||
314 | |||
315 | ret = mdev_access(mdev, (char *)&val, sizeof(val), | ||
316 | *ppos, false); | ||
317 | if (ret <= 0) | ||
318 | goto read_err; | ||
319 | |||
320 | if (copy_to_user(buf, &val, sizeof(val))) | ||
321 | goto read_err; | ||
322 | |||
323 | filled = 2; | ||
324 | } else { | ||
325 | u8 val; | ||
326 | |||
327 | ret = mdev_access(mdev, (char *)&val, sizeof(val), | ||
328 | *ppos, false); | ||
329 | if (ret <= 0) | ||
330 | goto read_err; | ||
331 | |||
332 | if (copy_to_user(buf, &val, sizeof(val))) | ||
333 | goto read_err; | ||
334 | |||
335 | filled = 1; | ||
336 | } | ||
337 | |||
338 | count -= filled; | ||
339 | done += filled; | ||
340 | *ppos += filled; | ||
341 | buf += filled; | ||
342 | } | ||
343 | |||
344 | return done; | ||
345 | |||
346 | read_err: | ||
347 | return -EFAULT; | ||
348 | } | ||
349 | |||
350 | static ssize_t mdpy_write(struct mdev_device *mdev, const char __user *buf, | ||
351 | size_t count, loff_t *ppos) | ||
352 | { | ||
353 | unsigned int done = 0; | ||
354 | int ret; | ||
355 | |||
356 | while (count) { | ||
357 | size_t filled; | ||
358 | |||
359 | if (count >= 4 && !(*ppos % 4)) { | ||
360 | u32 val; | ||
361 | |||
362 | if (copy_from_user(&val, buf, sizeof(val))) | ||
363 | goto write_err; | ||
364 | |||
365 | ret = mdev_access(mdev, (char *)&val, sizeof(val), | ||
366 | *ppos, true); | ||
367 | if (ret <= 0) | ||
368 | goto write_err; | ||
369 | |||
370 | filled = 4; | ||
371 | } else if (count >= 2 && !(*ppos % 2)) { | ||
372 | u16 val; | ||
373 | |||
374 | if (copy_from_user(&val, buf, sizeof(val))) | ||
375 | goto write_err; | ||
376 | |||
377 | ret = mdev_access(mdev, (char *)&val, sizeof(val), | ||
378 | *ppos, true); | ||
379 | if (ret <= 0) | ||
380 | goto write_err; | ||
381 | |||
382 | filled = 2; | ||
383 | } else { | ||
384 | u8 val; | ||
385 | |||
386 | if (copy_from_user(&val, buf, sizeof(val))) | ||
387 | goto write_err; | ||
388 | |||
389 | ret = mdev_access(mdev, (char *)&val, sizeof(val), | ||
390 | *ppos, true); | ||
391 | if (ret <= 0) | ||
392 | goto write_err; | ||
393 | |||
394 | filled = 1; | ||
395 | } | ||
396 | count -= filled; | ||
397 | done += filled; | ||
398 | *ppos += filled; | ||
399 | buf += filled; | ||
400 | } | ||
401 | |||
402 | return done; | ||
403 | write_err: | ||
404 | return -EFAULT; | ||
405 | } | ||
406 | |||
407 | static int mdpy_mmap(struct mdev_device *mdev, struct vm_area_struct *vma) | ||
408 | { | ||
409 | struct mdev_state *mdev_state = mdev_get_drvdata(mdev); | ||
410 | |||
411 | if (vma->vm_pgoff != MDPY_MEMORY_BAR_OFFSET >> PAGE_SHIFT) | ||
412 | return -EINVAL; | ||
413 | if (vma->vm_end < vma->vm_start) | ||
414 | return -EINVAL; | ||
415 | if (vma->vm_end - vma->vm_start > mdev_state->memsize) | ||
416 | return -EINVAL; | ||
417 | if ((vma->vm_flags & VM_SHARED) == 0) | ||
418 | return -EINVAL; | ||
419 | |||
420 | return remap_vmalloc_range_partial(vma, vma->vm_start, | ||
421 | mdev_state->memblk, | ||
422 | vma->vm_end - vma->vm_start); | ||
423 | } | ||
424 | |||
425 | static int mdpy_get_region_info(struct mdev_device *mdev, | ||
426 | struct vfio_region_info *region_info, | ||
427 | u16 *cap_type_id, void **cap_type) | ||
428 | { | ||
429 | struct mdev_state *mdev_state; | ||
430 | |||
431 | mdev_state = mdev_get_drvdata(mdev); | ||
432 | if (!mdev_state) | ||
433 | return -EINVAL; | ||
434 | |||
435 | if (region_info->index >= VFIO_PCI_NUM_REGIONS && | ||
436 | region_info->index != MDPY_DISPLAY_REGION) | ||
437 | return -EINVAL; | ||
438 | |||
439 | switch (region_info->index) { | ||
440 | case VFIO_PCI_CONFIG_REGION_INDEX: | ||
441 | region_info->offset = 0; | ||
442 | region_info->size = MDPY_CONFIG_SPACE_SIZE; | ||
443 | region_info->flags = (VFIO_REGION_INFO_FLAG_READ | | ||
444 | VFIO_REGION_INFO_FLAG_WRITE); | ||
445 | break; | ||
446 | case VFIO_PCI_BAR0_REGION_INDEX: | ||
447 | case MDPY_DISPLAY_REGION: | ||
448 | region_info->offset = MDPY_MEMORY_BAR_OFFSET; | ||
449 | region_info->size = mdev_state->memsize; | ||
450 | region_info->flags = (VFIO_REGION_INFO_FLAG_READ | | ||
451 | VFIO_REGION_INFO_FLAG_WRITE | | ||
452 | VFIO_REGION_INFO_FLAG_MMAP); | ||
453 | break; | ||
454 | default: | ||
455 | region_info->size = 0; | ||
456 | region_info->offset = 0; | ||
457 | region_info->flags = 0; | ||
458 | } | ||
459 | |||
460 | return 0; | ||
461 | } | ||
462 | |||
463 | static int mdpy_get_irq_info(struct mdev_device *mdev, | ||
464 | struct vfio_irq_info *irq_info) | ||
465 | { | ||
466 | irq_info->count = 0; | ||
467 | return 0; | ||
468 | } | ||
469 | |||
470 | static int mdpy_get_device_info(struct mdev_device *mdev, | ||
471 | struct vfio_device_info *dev_info) | ||
472 | { | ||
473 | dev_info->flags = VFIO_DEVICE_FLAGS_PCI; | ||
474 | dev_info->num_regions = VFIO_PCI_NUM_REGIONS; | ||
475 | dev_info->num_irqs = VFIO_PCI_NUM_IRQS; | ||
476 | return 0; | ||
477 | } | ||
478 | |||
479 | static int mdpy_query_gfx_plane(struct mdev_device *mdev, | ||
480 | struct vfio_device_gfx_plane_info *plane) | ||
481 | { | ||
482 | struct mdev_state *mdev_state = mdev_get_drvdata(mdev); | ||
483 | |||
484 | if (plane->flags & VFIO_GFX_PLANE_TYPE_PROBE) { | ||
485 | if (plane->flags == (VFIO_GFX_PLANE_TYPE_PROBE | | ||
486 | VFIO_GFX_PLANE_TYPE_REGION)) | ||
487 | return 0; | ||
488 | return -EINVAL; | ||
489 | } | ||
490 | |||
491 | if (plane->flags != VFIO_GFX_PLANE_TYPE_REGION) | ||
492 | return -EINVAL; | ||
493 | |||
494 | plane->drm_format = mdev_state->type->format; | ||
495 | plane->width = mdev_state->type->width; | ||
496 | plane->height = mdev_state->type->height; | ||
497 | plane->stride = (mdev_state->type->width * | ||
498 | mdev_state->type->bytepp); | ||
499 | plane->size = mdev_state->memsize; | ||
500 | plane->region_index = MDPY_DISPLAY_REGION; | ||
501 | |||
502 | /* unused */ | ||
503 | plane->drm_format_mod = 0; | ||
504 | plane->x_pos = 0; | ||
505 | plane->y_pos = 0; | ||
506 | plane->x_hot = 0; | ||
507 | plane->y_hot = 0; | ||
508 | |||
509 | return 0; | ||
510 | } | ||
511 | |||
512 | static long mdpy_ioctl(struct mdev_device *mdev, unsigned int cmd, | ||
513 | unsigned long arg) | ||
514 | { | ||
515 | int ret = 0; | ||
516 | unsigned long minsz; | ||
517 | struct mdev_state *mdev_state; | ||
518 | |||
519 | mdev_state = mdev_get_drvdata(mdev); | ||
520 | |||
521 | switch (cmd) { | ||
522 | case VFIO_DEVICE_GET_INFO: | ||
523 | { | ||
524 | struct vfio_device_info info; | ||
525 | |||
526 | minsz = offsetofend(struct vfio_device_info, num_irqs); | ||
527 | |||
528 | if (copy_from_user(&info, (void __user *)arg, minsz)) | ||
529 | return -EFAULT; | ||
530 | |||
531 | if (info.argsz < minsz) | ||
532 | return -EINVAL; | ||
533 | |||
534 | ret = mdpy_get_device_info(mdev, &info); | ||
535 | if (ret) | ||
536 | return ret; | ||
537 | |||
538 | memcpy(&mdev_state->dev_info, &info, sizeof(info)); | ||
539 | |||
540 | if (copy_to_user((void __user *)arg, &info, minsz)) | ||
541 | return -EFAULT; | ||
542 | |||
543 | return 0; | ||
544 | } | ||
545 | case VFIO_DEVICE_GET_REGION_INFO: | ||
546 | { | ||
547 | struct vfio_region_info info; | ||
548 | u16 cap_type_id = 0; | ||
549 | void *cap_type = NULL; | ||
550 | |||
551 | minsz = offsetofend(struct vfio_region_info, offset); | ||
552 | |||
553 | if (copy_from_user(&info, (void __user *)arg, minsz)) | ||
554 | return -EFAULT; | ||
555 | |||
556 | if (info.argsz < minsz) | ||
557 | return -EINVAL; | ||
558 | |||
559 | ret = mdpy_get_region_info(mdev, &info, &cap_type_id, | ||
560 | &cap_type); | ||
561 | if (ret) | ||
562 | return ret; | ||
563 | |||
564 | if (copy_to_user((void __user *)arg, &info, minsz)) | ||
565 | return -EFAULT; | ||
566 | |||
567 | return 0; | ||
568 | } | ||
569 | |||
570 | case VFIO_DEVICE_GET_IRQ_INFO: | ||
571 | { | ||
572 | struct vfio_irq_info info; | ||
573 | |||
574 | minsz = offsetofend(struct vfio_irq_info, count); | ||
575 | |||
576 | if (copy_from_user(&info, (void __user *)arg, minsz)) | ||
577 | return -EFAULT; | ||
578 | |||
579 | if ((info.argsz < minsz) || | ||
580 | (info.index >= mdev_state->dev_info.num_irqs)) | ||
581 | return -EINVAL; | ||
582 | |||
583 | ret = mdpy_get_irq_info(mdev, &info); | ||
584 | if (ret) | ||
585 | return ret; | ||
586 | |||
587 | if (copy_to_user((void __user *)arg, &info, minsz)) | ||
588 | return -EFAULT; | ||
589 | |||
590 | return 0; | ||
591 | } | ||
592 | |||
593 | case VFIO_DEVICE_QUERY_GFX_PLANE: | ||
594 | { | ||
595 | struct vfio_device_gfx_plane_info plane; | ||
596 | |||
597 | minsz = offsetofend(struct vfio_device_gfx_plane_info, | ||
598 | region_index); | ||
599 | |||
600 | if (copy_from_user(&plane, (void __user *)arg, minsz)) | ||
601 | return -EFAULT; | ||
602 | |||
603 | if (plane.argsz < minsz) | ||
604 | return -EINVAL; | ||
605 | |||
606 | ret = mdpy_query_gfx_plane(mdev, &plane); | ||
607 | if (ret) | ||
608 | return ret; | ||
609 | |||
610 | if (copy_to_user((void __user *)arg, &plane, minsz)) | ||
611 | return -EFAULT; | ||
612 | |||
613 | return 0; | ||
614 | } | ||
615 | |||
616 | case VFIO_DEVICE_SET_IRQS: | ||
617 | return -EINVAL; | ||
618 | |||
619 | case VFIO_DEVICE_RESET: | ||
620 | return mdpy_reset(mdev); | ||
621 | } | ||
622 | return -ENOTTY; | ||
623 | } | ||
624 | |||
625 | static int mdpy_open(struct mdev_device *mdev) | ||
626 | { | ||
627 | if (!try_module_get(THIS_MODULE)) | ||
628 | return -ENODEV; | ||
629 | |||
630 | return 0; | ||
631 | } | ||
632 | |||
633 | static void mdpy_close(struct mdev_device *mdev) | ||
634 | { | ||
635 | module_put(THIS_MODULE); | ||
636 | } | ||
637 | |||
638 | static ssize_t | ||
639 | resolution_show(struct device *dev, struct device_attribute *attr, | ||
640 | char *buf) | ||
641 | { | ||
642 | struct mdev_device *mdev = mdev_from_dev(dev); | ||
643 | struct mdev_state *mdev_state = mdev_get_drvdata(mdev); | ||
644 | |||
645 | return sprintf(buf, "%dx%d\n", | ||
646 | mdev_state->type->width, | ||
647 | mdev_state->type->height); | ||
648 | } | ||
649 | static DEVICE_ATTR_RO(resolution); | ||
650 | |||
651 | static struct attribute *mdev_dev_attrs[] = { | ||
652 | &dev_attr_resolution.attr, | ||
653 | NULL, | ||
654 | }; | ||
655 | |||
656 | static const struct attribute_group mdev_dev_group = { | ||
657 | .name = "vendor", | ||
658 | .attrs = mdev_dev_attrs, | ||
659 | }; | ||
660 | |||
661 | const struct attribute_group *mdev_dev_groups[] = { | ||
662 | &mdev_dev_group, | ||
663 | NULL, | ||
664 | }; | ||
665 | |||
666 | static ssize_t | ||
667 | name_show(struct kobject *kobj, struct device *dev, char *buf) | ||
668 | { | ||
669 | return sprintf(buf, "%s\n", kobj->name); | ||
670 | } | ||
671 | MDEV_TYPE_ATTR_RO(name); | ||
672 | |||
673 | static ssize_t | ||
674 | description_show(struct kobject *kobj, struct device *dev, char *buf) | ||
675 | { | ||
676 | const struct mdpy_type *type = mdpy_find_type(kobj); | ||
677 | |||
678 | return sprintf(buf, "virtual display, %dx%d framebuffer\n", | ||
679 | type ? type->width : 0, | ||
680 | type ? type->height : 0); | ||
681 | } | ||
682 | MDEV_TYPE_ATTR_RO(description); | ||
683 | |||
684 | static ssize_t | ||
685 | available_instances_show(struct kobject *kobj, struct device *dev, char *buf) | ||
686 | { | ||
687 | return sprintf(buf, "%d\n", max_devices - mdpy_count); | ||
688 | } | ||
689 | MDEV_TYPE_ATTR_RO(available_instances); | ||
690 | |||
691 | static ssize_t device_api_show(struct kobject *kobj, struct device *dev, | ||
692 | char *buf) | ||
693 | { | ||
694 | return sprintf(buf, "%s\n", VFIO_DEVICE_API_PCI_STRING); | ||
695 | } | ||
696 | MDEV_TYPE_ATTR_RO(device_api); | ||
697 | |||
698 | static struct attribute *mdev_types_attrs[] = { | ||
699 | &mdev_type_attr_name.attr, | ||
700 | &mdev_type_attr_description.attr, | ||
701 | &mdev_type_attr_device_api.attr, | ||
702 | &mdev_type_attr_available_instances.attr, | ||
703 | NULL, | ||
704 | }; | ||
705 | |||
706 | static struct attribute_group mdev_type_group1 = { | ||
707 | .name = MDPY_TYPE_1, | ||
708 | .attrs = mdev_types_attrs, | ||
709 | }; | ||
710 | |||
711 | static struct attribute_group mdev_type_group2 = { | ||
712 | .name = MDPY_TYPE_2, | ||
713 | .attrs = mdev_types_attrs, | ||
714 | }; | ||
715 | |||
716 | static struct attribute_group mdev_type_group3 = { | ||
717 | .name = MDPY_TYPE_3, | ||
718 | .attrs = mdev_types_attrs, | ||
719 | }; | ||
720 | |||
721 | static struct attribute_group *mdev_type_groups[] = { | ||
722 | &mdev_type_group1, | ||
723 | &mdev_type_group2, | ||
724 | &mdev_type_group3, | ||
725 | NULL, | ||
726 | }; | ||
727 | |||
728 | static const struct mdev_parent_ops mdev_fops = { | ||
729 | .owner = THIS_MODULE, | ||
730 | .mdev_attr_groups = mdev_dev_groups, | ||
731 | .supported_type_groups = mdev_type_groups, | ||
732 | .create = mdpy_create, | ||
733 | .remove = mdpy_remove, | ||
734 | .open = mdpy_open, | ||
735 | .release = mdpy_close, | ||
736 | .read = mdpy_read, | ||
737 | .write = mdpy_write, | ||
738 | .ioctl = mdpy_ioctl, | ||
739 | .mmap = mdpy_mmap, | ||
740 | }; | ||
741 | |||
742 | static const struct file_operations vd_fops = { | ||
743 | .owner = THIS_MODULE, | ||
744 | }; | ||
745 | |||
746 | static void mdpy_device_release(struct device *dev) | ||
747 | { | ||
748 | /* nothing */ | ||
749 | } | ||
750 | |||
751 | static int __init mdpy_dev_init(void) | ||
752 | { | ||
753 | int ret = 0; | ||
754 | |||
755 | ret = alloc_chrdev_region(&mdpy_devt, 0, MINORMASK, MDPY_NAME); | ||
756 | if (ret < 0) { | ||
757 | pr_err("Error: failed to register mdpy_dev, err: %d\n", ret); | ||
758 | return ret; | ||
759 | } | ||
760 | cdev_init(&mdpy_cdev, &vd_fops); | ||
761 | cdev_add(&mdpy_cdev, mdpy_devt, MINORMASK); | ||
762 | pr_info("%s: major %d\n", __func__, MAJOR(mdpy_devt)); | ||
763 | |||
764 | mdpy_class = class_create(THIS_MODULE, MDPY_CLASS_NAME); | ||
765 | if (IS_ERR(mdpy_class)) { | ||
766 | pr_err("Error: failed to register mdpy_dev class\n"); | ||
767 | ret = PTR_ERR(mdpy_class); | ||
768 | goto failed1; | ||
769 | } | ||
770 | mdpy_dev.class = mdpy_class; | ||
771 | mdpy_dev.release = mdpy_device_release; | ||
772 | dev_set_name(&mdpy_dev, "%s", MDPY_NAME); | ||
773 | |||
774 | ret = device_register(&mdpy_dev); | ||
775 | if (ret) | ||
776 | goto failed2; | ||
777 | |||
778 | ret = mdev_register_device(&mdpy_dev, &mdev_fops); | ||
779 | if (ret) | ||
780 | goto failed3; | ||
781 | |||
782 | return 0; | ||
783 | |||
784 | failed3: | ||
785 | device_unregister(&mdpy_dev); | ||
786 | failed2: | ||
787 | class_destroy(mdpy_class); | ||
788 | failed1: | ||
789 | cdev_del(&mdpy_cdev); | ||
790 | unregister_chrdev_region(mdpy_devt, MINORMASK); | ||
791 | return ret; | ||
792 | } | ||
793 | |||
794 | static void __exit mdpy_dev_exit(void) | ||
795 | { | ||
796 | mdpy_dev.bus = NULL; | ||
797 | mdev_unregister_device(&mdpy_dev); | ||
798 | |||
799 | device_unregister(&mdpy_dev); | ||
800 | cdev_del(&mdpy_cdev); | ||
801 | unregister_chrdev_region(mdpy_devt, MINORMASK); | ||
802 | class_destroy(mdpy_class); | ||
803 | mdpy_class = NULL; | ||
804 | } | ||
805 | |||
806 | module_init(mdpy_dev_init) | ||
807 | module_exit(mdpy_dev_exit) | ||