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/kernel.h> |
21 | #include <linux/slab.h> |
22 | #include <linux/vmalloc.h> |
23 | #include <linux/cdev.h> |
24 | #include <linux/vfio.h> |
25 | #include <linux/iommu.h> |
26 | #include <linux/sysfs.h> |
27 | #include <linux/mdev.h> |
28 | #include <linux/pci.h> |
29 | #include <drm/drm_fourcc.h> |
30 | #include "mdpy-defs.h" |
31 | |
32 | #define MDPY_NAME "mdpy" |
33 | #define MDPY_CLASS_NAME "mdpy" |
34 | |
35 | #define MDPY_CONFIG_SPACE_SIZE 0xff |
36 | #define MDPY_MEMORY_BAR_OFFSET PAGE_SIZE |
37 | #define MDPY_DISPLAY_REGION 16 |
38 | |
39 | #define STORE_LE16(addr, val) (*(u16 *)addr = val) |
40 | #define STORE_LE32(addr, val) (*(u32 *)addr = val) |
41 | |
42 | |
43 | MODULE_LICENSE("GPL v2" ); |
44 | |
45 | #define MDPY_TYPE_1 "vga" |
46 | #define MDPY_TYPE_2 "xga" |
47 | #define MDPY_TYPE_3 "hd" |
48 | |
49 | static struct mdpy_type { |
50 | struct mdev_type type; |
51 | u32 format; |
52 | u32 bytepp; |
53 | u32 width; |
54 | u32 height; |
55 | } mdpy_types[] = { |
56 | { |
57 | .type.sysfs_name = MDPY_TYPE_1, |
58 | .type.pretty_name = MDPY_CLASS_NAME "-" MDPY_TYPE_1, |
59 | .format = DRM_FORMAT_XRGB8888, |
60 | .bytepp = 4, |
61 | .width = 640, |
62 | .height = 480, |
63 | }, { |
64 | .type.sysfs_name = MDPY_TYPE_2, |
65 | .type.pretty_name = MDPY_CLASS_NAME "-" MDPY_TYPE_2, |
66 | .format = DRM_FORMAT_XRGB8888, |
67 | .bytepp = 4, |
68 | .width = 1024, |
69 | .height = 768, |
70 | }, { |
71 | .type.sysfs_name = MDPY_TYPE_3, |
72 | .type.pretty_name = MDPY_CLASS_NAME "-" MDPY_TYPE_3, |
73 | .format = DRM_FORMAT_XRGB8888, |
74 | .bytepp = 4, |
75 | .width = 1920, |
76 | .height = 1080, |
77 | }, |
78 | }; |
79 | |
80 | static struct mdev_type *mdpy_mdev_types[] = { |
81 | &mdpy_types[0].type, |
82 | &mdpy_types[1].type, |
83 | &mdpy_types[2].type, |
84 | }; |
85 | |
86 | static dev_t mdpy_devt; |
87 | static const struct class mdpy_class = { |
88 | .name = MDPY_CLASS_NAME, |
89 | }; |
90 | static struct cdev mdpy_cdev; |
91 | static struct device mdpy_dev; |
92 | static struct mdev_parent mdpy_parent; |
93 | static const struct vfio_device_ops mdpy_dev_ops; |
94 | |
95 | /* State of each mdev device */ |
96 | struct mdev_state { |
97 | struct vfio_device vdev; |
98 | u8 *vconfig; |
99 | u32 bar_mask; |
100 | struct mutex ops_lock; |
101 | struct mdev_device *mdev; |
102 | struct vfio_device_info dev_info; |
103 | |
104 | const struct mdpy_type *type; |
105 | u32 memsize; |
106 | void *memblk; |
107 | }; |
108 | |
109 | static void mdpy_create_config_space(struct mdev_state *mdev_state) |
110 | { |
111 | STORE_LE16((u16 *) &mdev_state->vconfig[PCI_VENDOR_ID], |
112 | MDPY_PCI_VENDOR_ID); |
113 | STORE_LE16((u16 *) &mdev_state->vconfig[PCI_DEVICE_ID], |
114 | MDPY_PCI_DEVICE_ID); |
115 | STORE_LE16((u16 *) &mdev_state->vconfig[PCI_SUBSYSTEM_VENDOR_ID], |
116 | MDPY_PCI_SUBVENDOR_ID); |
117 | STORE_LE16((u16 *) &mdev_state->vconfig[PCI_SUBSYSTEM_ID], |
118 | MDPY_PCI_SUBDEVICE_ID); |
119 | |
120 | STORE_LE16((u16 *) &mdev_state->vconfig[PCI_COMMAND], |
121 | PCI_COMMAND_IO | PCI_COMMAND_MEMORY); |
122 | STORE_LE16((u16 *) &mdev_state->vconfig[PCI_STATUS], |
123 | PCI_STATUS_CAP_LIST); |
124 | STORE_LE16((u16 *) &mdev_state->vconfig[PCI_CLASS_DEVICE], |
125 | PCI_CLASS_DISPLAY_OTHER); |
126 | mdev_state->vconfig[PCI_CLASS_REVISION] = 0x01; |
127 | |
128 | STORE_LE32((u32 *) &mdev_state->vconfig[PCI_BASE_ADDRESS_0], |
129 | PCI_BASE_ADDRESS_SPACE_MEMORY | |
130 | PCI_BASE_ADDRESS_MEM_TYPE_32 | |
131 | PCI_BASE_ADDRESS_MEM_PREFETCH); |
132 | mdev_state->bar_mask = ~(mdev_state->memsize) + 1; |
133 | |
134 | /* vendor specific capability for the config registers */ |
135 | mdev_state->vconfig[PCI_CAPABILITY_LIST] = MDPY_VENDORCAP_OFFSET; |
136 | mdev_state->vconfig[MDPY_VENDORCAP_OFFSET + 0] = 0x09; /* vendor cap */ |
137 | mdev_state->vconfig[MDPY_VENDORCAP_OFFSET + 1] = 0x00; /* next ptr */ |
138 | mdev_state->vconfig[MDPY_VENDORCAP_OFFSET + 2] = MDPY_VENDORCAP_SIZE; |
139 | STORE_LE32((u32 *) &mdev_state->vconfig[MDPY_FORMAT_OFFSET], |
140 | mdev_state->type->format); |
141 | STORE_LE32((u32 *) &mdev_state->vconfig[MDPY_WIDTH_OFFSET], |
142 | mdev_state->type->width); |
143 | STORE_LE32((u32 *) &mdev_state->vconfig[MDPY_HEIGHT_OFFSET], |
144 | mdev_state->type->height); |
145 | } |
146 | |
147 | static void handle_pci_cfg_write(struct mdev_state *mdev_state, u16 offset, |
148 | char *buf, u32 count) |
149 | { |
150 | struct device *dev = mdev_dev(mdev: mdev_state->mdev); |
151 | u32 cfg_addr; |
152 | |
153 | switch (offset) { |
154 | case PCI_BASE_ADDRESS_0: |
155 | cfg_addr = *(u32 *)buf; |
156 | |
157 | if (cfg_addr == 0xffffffff) { |
158 | cfg_addr = (cfg_addr & mdev_state->bar_mask); |
159 | } else { |
160 | cfg_addr &= PCI_BASE_ADDRESS_MEM_MASK; |
161 | if (cfg_addr) |
162 | dev_info(dev, "BAR0 @ 0x%x\n" , cfg_addr); |
163 | } |
164 | |
165 | cfg_addr |= (mdev_state->vconfig[offset] & |
166 | ~PCI_BASE_ADDRESS_MEM_MASK); |
167 | STORE_LE32(&mdev_state->vconfig[offset], cfg_addr); |
168 | break; |
169 | } |
170 | } |
171 | |
172 | static ssize_t mdev_access(struct mdev_state *mdev_state, char *buf, |
173 | size_t count, loff_t pos, bool is_write) |
174 | { |
175 | int ret = 0; |
176 | |
177 | mutex_lock(&mdev_state->ops_lock); |
178 | |
179 | if (pos < MDPY_CONFIG_SPACE_SIZE) { |
180 | if (is_write) |
181 | handle_pci_cfg_write(mdev_state, offset: pos, buf, count); |
182 | else |
183 | memcpy(buf, (mdev_state->vconfig + pos), count); |
184 | |
185 | } else if ((pos >= MDPY_MEMORY_BAR_OFFSET) && |
186 | (pos + count <= |
187 | MDPY_MEMORY_BAR_OFFSET + mdev_state->memsize)) { |
188 | pos -= MDPY_MEMORY_BAR_OFFSET; |
189 | if (is_write) |
190 | memcpy(mdev_state->memblk, buf, count); |
191 | else |
192 | memcpy(buf, mdev_state->memblk, count); |
193 | |
194 | } else { |
195 | dev_info(mdev_state->vdev.dev, |
196 | "%s: %s @0x%llx (unhandled)\n" , __func__, |
197 | is_write ? "WR" : "RD" , pos); |
198 | ret = -1; |
199 | goto accessfailed; |
200 | } |
201 | |
202 | ret = count; |
203 | |
204 | |
205 | accessfailed: |
206 | mutex_unlock(lock: &mdev_state->ops_lock); |
207 | |
208 | return ret; |
209 | } |
210 | |
211 | static int mdpy_reset(struct mdev_state *mdev_state) |
212 | { |
213 | u32 stride, i; |
214 | |
215 | /* initialize with gray gradient */ |
216 | stride = mdev_state->type->width * mdev_state->type->bytepp; |
217 | for (i = 0; i < mdev_state->type->height; i++) |
218 | memset(mdev_state->memblk + i * stride, |
219 | i * 255 / mdev_state->type->height, |
220 | stride); |
221 | return 0; |
222 | } |
223 | |
224 | static int mdpy_init_dev(struct vfio_device *vdev) |
225 | { |
226 | struct mdev_state *mdev_state = |
227 | container_of(vdev, struct mdev_state, vdev); |
228 | struct mdev_device *mdev = to_mdev_device(dev: vdev->dev); |
229 | const struct mdpy_type *type = |
230 | container_of(mdev->type, struct mdpy_type, type); |
231 | u32 fbsize; |
232 | int ret = -ENOMEM; |
233 | |
234 | mdev_state->vconfig = kzalloc(MDPY_CONFIG_SPACE_SIZE, GFP_KERNEL); |
235 | if (!mdev_state->vconfig) |
236 | return ret; |
237 | |
238 | fbsize = roundup_pow_of_two(type->width * type->height * type->bytepp); |
239 | |
240 | mdev_state->memblk = vmalloc_user(size: fbsize); |
241 | if (!mdev_state->memblk) |
242 | goto out_vconfig; |
243 | |
244 | mutex_init(&mdev_state->ops_lock); |
245 | mdev_state->mdev = mdev; |
246 | mdev_state->type = type; |
247 | mdev_state->memsize = fbsize; |
248 | mdpy_create_config_space(mdev_state); |
249 | mdpy_reset(mdev_state); |
250 | |
251 | dev_info(vdev->dev, "%s: %s (%dx%d)\n" , __func__, type->type.pretty_name, |
252 | type->width, type->height); |
253 | return 0; |
254 | |
255 | out_vconfig: |
256 | kfree(objp: mdev_state->vconfig); |
257 | return ret; |
258 | } |
259 | |
260 | static int mdpy_probe(struct mdev_device *mdev) |
261 | { |
262 | struct mdev_state *mdev_state; |
263 | int ret; |
264 | |
265 | mdev_state = vfio_alloc_device(mdev_state, vdev, &mdev->dev, |
266 | &mdpy_dev_ops); |
267 | if (IS_ERR(ptr: mdev_state)) |
268 | return PTR_ERR(ptr: mdev_state); |
269 | |
270 | ret = vfio_register_emulated_iommu_dev(device: &mdev_state->vdev); |
271 | if (ret) |
272 | goto err_put_vdev; |
273 | dev_set_drvdata(dev: &mdev->dev, data: mdev_state); |
274 | return 0; |
275 | |
276 | err_put_vdev: |
277 | vfio_put_device(device: &mdev_state->vdev); |
278 | return ret; |
279 | } |
280 | |
281 | static void mdpy_release_dev(struct vfio_device *vdev) |
282 | { |
283 | struct mdev_state *mdev_state = |
284 | container_of(vdev, struct mdev_state, vdev); |
285 | |
286 | vfree(addr: mdev_state->memblk); |
287 | kfree(objp: mdev_state->vconfig); |
288 | } |
289 | |
290 | static void mdpy_remove(struct mdev_device *mdev) |
291 | { |
292 | struct mdev_state *mdev_state = dev_get_drvdata(dev: &mdev->dev); |
293 | |
294 | dev_info(&mdev->dev, "%s\n" , __func__); |
295 | |
296 | vfio_unregister_group_dev(device: &mdev_state->vdev); |
297 | vfio_put_device(device: &mdev_state->vdev); |
298 | } |
299 | |
300 | static ssize_t mdpy_read(struct vfio_device *vdev, char __user *buf, |
301 | size_t count, loff_t *ppos) |
302 | { |
303 | struct mdev_state *mdev_state = |
304 | container_of(vdev, struct mdev_state, vdev); |
305 | unsigned int done = 0; |
306 | int ret; |
307 | |
308 | while (count) { |
309 | size_t filled; |
310 | |
311 | if (count >= 4 && !(*ppos % 4)) { |
312 | u32 val; |
313 | |
314 | ret = mdev_access(mdev_state, buf: (char *)&val, count: sizeof(val), |
315 | pos: *ppos, is_write: false); |
316 | if (ret <= 0) |
317 | goto read_err; |
318 | |
319 | if (copy_to_user(to: buf, from: &val, n: sizeof(val))) |
320 | goto read_err; |
321 | |
322 | filled = 4; |
323 | } else if (count >= 2 && !(*ppos % 2)) { |
324 | u16 val; |
325 | |
326 | ret = mdev_access(mdev_state, buf: (char *)&val, count: sizeof(val), |
327 | pos: *ppos, is_write: false); |
328 | if (ret <= 0) |
329 | goto read_err; |
330 | |
331 | if (copy_to_user(to: buf, from: &val, n: sizeof(val))) |
332 | goto read_err; |
333 | |
334 | filled = 2; |
335 | } else { |
336 | u8 val; |
337 | |
338 | ret = mdev_access(mdev_state, buf: (char *)&val, count: sizeof(val), |
339 | pos: *ppos, is_write: false); |
340 | if (ret <= 0) |
341 | goto read_err; |
342 | |
343 | if (copy_to_user(to: buf, from: &val, n: sizeof(val))) |
344 | goto read_err; |
345 | |
346 | filled = 1; |
347 | } |
348 | |
349 | count -= filled; |
350 | done += filled; |
351 | *ppos += filled; |
352 | buf += filled; |
353 | } |
354 | |
355 | return done; |
356 | |
357 | read_err: |
358 | return -EFAULT; |
359 | } |
360 | |
361 | static ssize_t mdpy_write(struct vfio_device *vdev, const char __user *buf, |
362 | size_t count, loff_t *ppos) |
363 | { |
364 | struct mdev_state *mdev_state = |
365 | container_of(vdev, struct mdev_state, vdev); |
366 | unsigned int done = 0; |
367 | int ret; |
368 | |
369 | while (count) { |
370 | size_t filled; |
371 | |
372 | if (count >= 4 && !(*ppos % 4)) { |
373 | u32 val; |
374 | |
375 | if (copy_from_user(to: &val, from: buf, n: sizeof(val))) |
376 | goto write_err; |
377 | |
378 | ret = mdev_access(mdev_state, buf: (char *)&val, count: sizeof(val), |
379 | pos: *ppos, is_write: true); |
380 | if (ret <= 0) |
381 | goto write_err; |
382 | |
383 | filled = 4; |
384 | } else if (count >= 2 && !(*ppos % 2)) { |
385 | u16 val; |
386 | |
387 | if (copy_from_user(to: &val, from: buf, n: sizeof(val))) |
388 | goto write_err; |
389 | |
390 | ret = mdev_access(mdev_state, buf: (char *)&val, count: sizeof(val), |
391 | pos: *ppos, is_write: true); |
392 | if (ret <= 0) |
393 | goto write_err; |
394 | |
395 | filled = 2; |
396 | } else { |
397 | u8 val; |
398 | |
399 | if (copy_from_user(to: &val, from: buf, n: sizeof(val))) |
400 | goto write_err; |
401 | |
402 | ret = mdev_access(mdev_state, buf: (char *)&val, count: sizeof(val), |
403 | pos: *ppos, is_write: true); |
404 | if (ret <= 0) |
405 | goto write_err; |
406 | |
407 | filled = 1; |
408 | } |
409 | count -= filled; |
410 | done += filled; |
411 | *ppos += filled; |
412 | buf += filled; |
413 | } |
414 | |
415 | return done; |
416 | write_err: |
417 | return -EFAULT; |
418 | } |
419 | |
420 | static int mdpy_mmap(struct vfio_device *vdev, struct vm_area_struct *vma) |
421 | { |
422 | struct mdev_state *mdev_state = |
423 | container_of(vdev, struct mdev_state, vdev); |
424 | |
425 | if (vma->vm_pgoff != MDPY_MEMORY_BAR_OFFSET >> PAGE_SHIFT) |
426 | return -EINVAL; |
427 | if (vma->vm_end < vma->vm_start) |
428 | return -EINVAL; |
429 | if (vma->vm_end - vma->vm_start > mdev_state->memsize) |
430 | return -EINVAL; |
431 | if ((vma->vm_flags & VM_SHARED) == 0) |
432 | return -EINVAL; |
433 | |
434 | return remap_vmalloc_range(vma, addr: mdev_state->memblk, pgoff: 0); |
435 | } |
436 | |
437 | static int mdpy_get_region_info(struct mdev_state *mdev_state, |
438 | struct vfio_region_info *region_info, |
439 | u16 *cap_type_id, void **cap_type) |
440 | { |
441 | if (region_info->index >= VFIO_PCI_NUM_REGIONS && |
442 | region_info->index != MDPY_DISPLAY_REGION) |
443 | return -EINVAL; |
444 | |
445 | switch (region_info->index) { |
446 | case VFIO_PCI_CONFIG_REGION_INDEX: |
447 | region_info->offset = 0; |
448 | region_info->size = MDPY_CONFIG_SPACE_SIZE; |
449 | region_info->flags = (VFIO_REGION_INFO_FLAG_READ | |
450 | VFIO_REGION_INFO_FLAG_WRITE); |
451 | break; |
452 | case VFIO_PCI_BAR0_REGION_INDEX: |
453 | case MDPY_DISPLAY_REGION: |
454 | region_info->offset = MDPY_MEMORY_BAR_OFFSET; |
455 | region_info->size = mdev_state->memsize; |
456 | region_info->flags = (VFIO_REGION_INFO_FLAG_READ | |
457 | VFIO_REGION_INFO_FLAG_WRITE | |
458 | VFIO_REGION_INFO_FLAG_MMAP); |
459 | break; |
460 | default: |
461 | region_info->size = 0; |
462 | region_info->offset = 0; |
463 | region_info->flags = 0; |
464 | } |
465 | |
466 | return 0; |
467 | } |
468 | |
469 | static int mdpy_get_irq_info(struct vfio_irq_info *irq_info) |
470 | { |
471 | irq_info->count = 0; |
472 | return 0; |
473 | } |
474 | |
475 | static int mdpy_get_device_info(struct vfio_device_info *dev_info) |
476 | { |
477 | dev_info->flags = VFIO_DEVICE_FLAGS_PCI; |
478 | dev_info->num_regions = VFIO_PCI_NUM_REGIONS; |
479 | dev_info->num_irqs = VFIO_PCI_NUM_IRQS; |
480 | return 0; |
481 | } |
482 | |
483 | static int mdpy_query_gfx_plane(struct mdev_state *mdev_state, |
484 | struct vfio_device_gfx_plane_info *plane) |
485 | { |
486 | if (plane->flags & VFIO_GFX_PLANE_TYPE_PROBE) { |
487 | if (plane->flags == (VFIO_GFX_PLANE_TYPE_PROBE | |
488 | VFIO_GFX_PLANE_TYPE_REGION)) |
489 | return 0; |
490 | return -EINVAL; |
491 | } |
492 | |
493 | if (plane->flags != VFIO_GFX_PLANE_TYPE_REGION) |
494 | return -EINVAL; |
495 | |
496 | plane->drm_format = mdev_state->type->format; |
497 | plane->width = mdev_state->type->width; |
498 | plane->height = mdev_state->type->height; |
499 | plane->stride = (mdev_state->type->width * |
500 | mdev_state->type->bytepp); |
501 | plane->size = mdev_state->memsize; |
502 | plane->region_index = MDPY_DISPLAY_REGION; |
503 | |
504 | /* unused */ |
505 | plane->drm_format_mod = 0; |
506 | plane->x_pos = 0; |
507 | plane->y_pos = 0; |
508 | plane->x_hot = 0; |
509 | plane->y_hot = 0; |
510 | |
511 | return 0; |
512 | } |
513 | |
514 | static long mdpy_ioctl(struct vfio_device *vdev, unsigned int cmd, |
515 | unsigned long arg) |
516 | { |
517 | int ret = 0; |
518 | unsigned long minsz; |
519 | struct mdev_state *mdev_state = |
520 | container_of(vdev, struct mdev_state, vdev); |
521 | |
522 | switch (cmd) { |
523 | case VFIO_DEVICE_GET_INFO: |
524 | { |
525 | struct vfio_device_info info; |
526 | |
527 | minsz = offsetofend(struct vfio_device_info, num_irqs); |
528 | |
529 | if (copy_from_user(to: &info, from: (void __user *)arg, n: minsz)) |
530 | return -EFAULT; |
531 | |
532 | if (info.argsz < minsz) |
533 | return -EINVAL; |
534 | |
535 | ret = mdpy_get_device_info(dev_info: &info); |
536 | if (ret) |
537 | return ret; |
538 | |
539 | memcpy(&mdev_state->dev_info, &info, sizeof(info)); |
540 | |
541 | if (copy_to_user(to: (void __user *)arg, from: &info, n: minsz)) |
542 | return -EFAULT; |
543 | |
544 | return 0; |
545 | } |
546 | case VFIO_DEVICE_GET_REGION_INFO: |
547 | { |
548 | struct vfio_region_info info; |
549 | u16 cap_type_id = 0; |
550 | void *cap_type = NULL; |
551 | |
552 | minsz = offsetofend(struct vfio_region_info, offset); |
553 | |
554 | if (copy_from_user(to: &info, from: (void __user *)arg, n: minsz)) |
555 | return -EFAULT; |
556 | |
557 | if (info.argsz < minsz) |
558 | return -EINVAL; |
559 | |
560 | ret = mdpy_get_region_info(mdev_state, region_info: &info, cap_type_id: &cap_type_id, |
561 | cap_type: &cap_type); |
562 | if (ret) |
563 | return ret; |
564 | |
565 | if (copy_to_user(to: (void __user *)arg, from: &info, n: minsz)) |
566 | return -EFAULT; |
567 | |
568 | return 0; |
569 | } |
570 | |
571 | case VFIO_DEVICE_GET_IRQ_INFO: |
572 | { |
573 | struct vfio_irq_info info; |
574 | |
575 | minsz = offsetofend(struct vfio_irq_info, count); |
576 | |
577 | if (copy_from_user(to: &info, from: (void __user *)arg, n: minsz)) |
578 | return -EFAULT; |
579 | |
580 | if ((info.argsz < minsz) || |
581 | (info.index >= mdev_state->dev_info.num_irqs)) |
582 | return -EINVAL; |
583 | |
584 | ret = mdpy_get_irq_info(irq_info: &info); |
585 | if (ret) |
586 | return ret; |
587 | |
588 | if (copy_to_user(to: (void __user *)arg, from: &info, n: minsz)) |
589 | return -EFAULT; |
590 | |
591 | return 0; |
592 | } |
593 | |
594 | case VFIO_DEVICE_QUERY_GFX_PLANE: |
595 | { |
596 | struct vfio_device_gfx_plane_info plane = {}; |
597 | |
598 | minsz = offsetofend(struct vfio_device_gfx_plane_info, |
599 | region_index); |
600 | |
601 | if (copy_from_user(to: &plane, from: (void __user *)arg, n: minsz)) |
602 | return -EFAULT; |
603 | |
604 | if (plane.argsz < minsz) |
605 | return -EINVAL; |
606 | |
607 | ret = mdpy_query_gfx_plane(mdev_state, plane: &plane); |
608 | if (ret) |
609 | return ret; |
610 | |
611 | if (copy_to_user(to: (void __user *)arg, from: &plane, n: minsz)) |
612 | return -EFAULT; |
613 | |
614 | return 0; |
615 | } |
616 | |
617 | case VFIO_DEVICE_SET_IRQS: |
618 | return -EINVAL; |
619 | |
620 | case VFIO_DEVICE_RESET: |
621 | return mdpy_reset(mdev_state); |
622 | } |
623 | return -ENOTTY; |
624 | } |
625 | |
626 | static ssize_t |
627 | resolution_show(struct device *dev, struct device_attribute *attr, |
628 | char *buf) |
629 | { |
630 | struct mdev_state *mdev_state = dev_get_drvdata(dev); |
631 | |
632 | return sprintf(buf, fmt: "%dx%d\n" , |
633 | mdev_state->type->width, |
634 | mdev_state->type->height); |
635 | } |
636 | static DEVICE_ATTR_RO(resolution); |
637 | |
638 | static struct attribute *mdev_dev_attrs[] = { |
639 | &dev_attr_resolution.attr, |
640 | NULL, |
641 | }; |
642 | |
643 | static const struct attribute_group mdev_dev_group = { |
644 | .name = "vendor" , |
645 | .attrs = mdev_dev_attrs, |
646 | }; |
647 | |
648 | static const struct attribute_group *mdev_dev_groups[] = { |
649 | &mdev_dev_group, |
650 | NULL, |
651 | }; |
652 | |
653 | static ssize_t mdpy_show_description(struct mdev_type *mtype, char *buf) |
654 | { |
655 | struct mdpy_type *type = container_of(mtype, struct mdpy_type, type); |
656 | |
657 | return sprintf(buf, fmt: "virtual display, %dx%d framebuffer\n" , |
658 | type->width, type->height); |
659 | } |
660 | |
661 | static const struct vfio_device_ops mdpy_dev_ops = { |
662 | .init = mdpy_init_dev, |
663 | .release = mdpy_release_dev, |
664 | .read = mdpy_read, |
665 | .write = mdpy_write, |
666 | .ioctl = mdpy_ioctl, |
667 | .mmap = mdpy_mmap, |
668 | .bind_iommufd = vfio_iommufd_emulated_bind, |
669 | .unbind_iommufd = vfio_iommufd_emulated_unbind, |
670 | .attach_ioas = vfio_iommufd_emulated_attach_ioas, |
671 | .detach_ioas = vfio_iommufd_emulated_detach_ioas, |
672 | }; |
673 | |
674 | static struct mdev_driver mdpy_driver = { |
675 | .device_api = VFIO_DEVICE_API_PCI_STRING, |
676 | .max_instances = 4, |
677 | .driver = { |
678 | .name = "mdpy" , |
679 | .owner = THIS_MODULE, |
680 | .mod_name = KBUILD_MODNAME, |
681 | .dev_groups = mdev_dev_groups, |
682 | }, |
683 | .probe = mdpy_probe, |
684 | .remove = mdpy_remove, |
685 | .show_description = mdpy_show_description, |
686 | }; |
687 | |
688 | static const struct file_operations vd_fops = { |
689 | .owner = THIS_MODULE, |
690 | }; |
691 | |
692 | static void mdpy_device_release(struct device *dev) |
693 | { |
694 | /* nothing */ |
695 | } |
696 | |
697 | static int __init mdpy_dev_init(void) |
698 | { |
699 | int ret = 0; |
700 | |
701 | ret = alloc_chrdev_region(&mdpy_devt, 0, MINORMASK + 1, MDPY_NAME); |
702 | if (ret < 0) { |
703 | pr_err("Error: failed to register mdpy_dev, err: %d\n" , ret); |
704 | return ret; |
705 | } |
706 | cdev_init(&mdpy_cdev, &vd_fops); |
707 | cdev_add(&mdpy_cdev, mdpy_devt, MINORMASK + 1); |
708 | pr_info("%s: major %d\n" , __func__, MAJOR(mdpy_devt)); |
709 | |
710 | ret = mdev_register_driver(drv: &mdpy_driver); |
711 | if (ret) |
712 | goto err_cdev; |
713 | |
714 | ret = class_register(class: &mdpy_class); |
715 | if (ret) |
716 | goto err_driver; |
717 | mdpy_dev.class = &mdpy_class; |
718 | mdpy_dev.release = mdpy_device_release; |
719 | dev_set_name(dev: &mdpy_dev, name: "%s" , MDPY_NAME); |
720 | |
721 | ret = device_register(dev: &mdpy_dev); |
722 | if (ret) |
723 | goto err_put; |
724 | |
725 | ret = mdev_register_parent(parent: &mdpy_parent, dev: &mdpy_dev, mdev_driver: &mdpy_driver, |
726 | types: mdpy_mdev_types, |
727 | ARRAY_SIZE(mdpy_mdev_types)); |
728 | if (ret) |
729 | goto err_device; |
730 | |
731 | return 0; |
732 | |
733 | err_device: |
734 | device_del(dev: &mdpy_dev); |
735 | err_put: |
736 | put_device(dev: &mdpy_dev); |
737 | class_unregister(class: &mdpy_class); |
738 | err_driver: |
739 | mdev_unregister_driver(drv: &mdpy_driver); |
740 | err_cdev: |
741 | cdev_del(&mdpy_cdev); |
742 | unregister_chrdev_region(mdpy_devt, MINORMASK + 1); |
743 | return ret; |
744 | } |
745 | |
746 | static void __exit mdpy_dev_exit(void) |
747 | { |
748 | mdpy_dev.bus = NULL; |
749 | mdev_unregister_parent(parent: &mdpy_parent); |
750 | |
751 | device_unregister(dev: &mdpy_dev); |
752 | mdev_unregister_driver(drv: &mdpy_driver); |
753 | cdev_del(&mdpy_cdev); |
754 | unregister_chrdev_region(mdpy_devt, MINORMASK + 1); |
755 | class_unregister(class: &mdpy_class); |
756 | } |
757 | |
758 | module_param_named(count, mdpy_driver.max_instances, int, 0444); |
759 | MODULE_PARM_DESC(count, "number of " MDPY_NAME " devices" ); |
760 | |
761 | module_init(mdpy_dev_init) |
762 | module_exit(mdpy_dev_exit) |
763 | |